package com.cappielloantonio.tempo.widget; import android.appwidget.AppWidgetManager; import android.content.ComponentName; import android.content.Context; import android.graphics.Bitmap; import android.text.TextUtils; import android.graphics.drawable.Drawable; import com.bumptech.glide.request.target.CustomTarget; import com.bumptech.glide.request.transition.Transition; import com.cappielloantonio.tempo.glide.CustomGlideRequest; import com.cappielloantonio.tempo.R; import androidx.media3.session.MediaController; import androidx.media3.session.SessionToken; import com.cappielloantonio.tempo.service.MediaService; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.MoreExecutors; import java.util.concurrent.ExecutionException; public final class WidgetUpdateManager { public static void updateFromState(Context ctx, String title, String artist, Bitmap art, boolean playing) { if (TextUtils.isEmpty(title)) title = ctx.getString(R.string.widget_not_playing); if (TextUtils.isEmpty(artist)) artist = ctx.getString(R.string.widget_placeholder_subtitle); AppWidgetManager mgr = AppWidgetManager.getInstance(ctx); int[] ids = mgr.getAppWidgetIds(new ComponentName(ctx, WidgetProvider4x1.class)); for (int id : ids) { android.widget.RemoteViews rv = choosePopulate(ctx, title, artist, art, playing, id); WidgetProvider.attachIntents(ctx, rv, id); mgr.updateAppWidget(id, rv); } } public static void pushNow(Context ctx) { AppWidgetManager mgr = AppWidgetManager.getInstance(ctx); int[] ids = mgr.getAppWidgetIds(new ComponentName(ctx, WidgetProvider4x1.class)); for (int id : ids) { android.widget.RemoteViews rv = chooseBuild(ctx, id); WidgetProvider.attachIntents(ctx, rv, id); mgr.updateAppWidget(id, rv); } } public static void updateFromState(Context ctx, String title, String artist, String coverArtId, boolean playing) { final Context appCtx = ctx.getApplicationContext(); final String t = TextUtils.isEmpty(title) ? appCtx.getString(R.string.widget_not_playing) : title; final String a = TextUtils.isEmpty(artist) ? appCtx.getString(R.string.widget_placeholder_subtitle) : artist; final boolean p = playing; if (!TextUtils.isEmpty(coverArtId)) { CustomGlideRequest.loadAlbumArtBitmap( appCtx, coverArtId, com.cappielloantonio.tempo.util.Preferences.getImageSize(), new CustomTarget() { @Override public void onResourceReady(Bitmap resource, Transition transition) { AppWidgetManager mgr = AppWidgetManager.getInstance(appCtx); int[] ids = mgr.getAppWidgetIds(new ComponentName(appCtx, WidgetProvider4x1.class)); for (int id : ids) { android.widget.RemoteViews rv = choosePopulate(appCtx, t, a, resource, p, id); WidgetProvider.attachIntents(appCtx, rv, id); mgr.updateAppWidget(id, rv); } } @Override public void onLoadCleared(Drawable placeholder) { AppWidgetManager mgr = AppWidgetManager.getInstance(appCtx); int[] ids = mgr.getAppWidgetIds(new ComponentName(appCtx, WidgetProvider4x1.class)); for (int id : ids) { android.widget.RemoteViews rv = choosePopulate(appCtx, t, a, null, p, id); WidgetProvider.attachIntents(appCtx, rv, id); mgr.updateAppWidget(id, rv); } } } ); } else { AppWidgetManager mgr = AppWidgetManager.getInstance(appCtx); int[] ids = mgr.getAppWidgetIds(new ComponentName(appCtx, WidgetProvider4x1.class)); for (int id : ids) { android.widget.RemoteViews rv = choosePopulate(appCtx, t, a, null, p, id); WidgetProvider.attachIntents(appCtx, rv, id); mgr.updateAppWidget(id, rv); } } } public static void refreshFromController(Context ctx) { final Context appCtx = ctx.getApplicationContext(); SessionToken token = new SessionToken(appCtx, new ComponentName(appCtx, MediaService.class)); ListenableFuture future = new MediaController.Builder(appCtx, token).buildAsync(); future.addListener(() -> { try { if (!future.isDone()) return; MediaController c = future.get(); androidx.media3.common.MediaItem mi = c.getCurrentMediaItem(); String title = null, artist = null, coverId = null; if (mi != null && mi.mediaMetadata != null) { if (mi.mediaMetadata.title != null) title = mi.mediaMetadata.title.toString(); if (mi.mediaMetadata.artist != null) artist = mi.mediaMetadata.artist.toString(); if (mi.mediaMetadata.extras != null) { if (title == null) title = mi.mediaMetadata.extras.getString("title"); if (artist == null) artist = mi.mediaMetadata.extras.getString("artist"); coverId = mi.mediaMetadata.extras.getString("coverArtId"); } } updateFromState(appCtx, title != null ? title : appCtx.getString(R.string.widget_not_playing), artist != null ? artist : appCtx.getString(R.string.widget_placeholder_subtitle), coverId, c.isPlaying()); c.release(); } catch (ExecutionException | InterruptedException ignored) { } }, MoreExecutors.directExecutor()); } public static android.widget.RemoteViews chooseBuild(Context ctx, int appWidgetId) { if (isLarge(ctx, appWidgetId)) return WidgetViewsFactory.buildLarge(ctx); return WidgetViewsFactory.buildCompact(ctx); } private static android.widget.RemoteViews choosePopulate(Context ctx, String title, String artist, Bitmap art, boolean playing, int appWidgetId) { if (isLarge(ctx, appWidgetId)) return WidgetViewsFactory.populateLarge(ctx, title, artist, art, playing); return WidgetViewsFactory.populate(ctx, title, artist, art, playing); } private static boolean isLarge(Context ctx, int appWidgetId) { AppWidgetManager mgr = AppWidgetManager.getInstance(ctx); android.os.Bundle opts = mgr.getAppWidgetOptions(appWidgetId); int minH = opts != null ? opts.getInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT) : 0; int threshold = ctx.getResources().getInteger(com.cappielloantonio.tempo.R.integer.widget_large_min_height_dp); return minH >= threshold; // dp threshold for 2-row height } }