avoid full updates

This commit is contained in:
pca006132 2025-11-03 14:46:10 +08:00
parent b335ddec01
commit be9eec625a
3 changed files with 108 additions and 65 deletions

View file

@ -4,7 +4,10 @@ import android.annotation.SuppressLint
import android.app.PendingIntent.FLAG_IMMUTABLE import android.app.PendingIntent.FLAG_IMMUTABLE
import android.app.PendingIntent.FLAG_UPDATE_CURRENT import android.app.PendingIntent.FLAG_UPDATE_CURRENT
import android.app.TaskStackBuilder import android.app.TaskStackBuilder
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.IntentFilter
import android.graphics.Bitmap import android.graphics.Bitmap
import android.graphics.drawable.Drawable import android.graphics.drawable.Drawable
import android.net.ConnectivityManager import android.net.ConnectivityManager
@ -60,7 +63,7 @@ class MediaService : MediaLibraryService() {
private var widgetUpdateScheduled = false private var widgetUpdateScheduled = false
private val widgetUpdateRunnable = object : Runnable { private val widgetUpdateRunnable = object : Runnable {
override fun run() { override fun run() {
if (!player.isPlaying) { if (!player.isPlaying || !screenOn) {
widgetUpdateScheduled = false widgetUpdateScheduled = false
return return
} }
@ -68,7 +71,29 @@ class MediaService : MediaLibraryService() {
widgetUpdateHandler.postDelayed(this, WIDGET_UPDATE_INTERVAL_MS) widgetUpdateHandler.postDelayed(this, WIDGET_UPDATE_INTERVAL_MS)
} }
} }
@Volatile private var artCache : Optional<Optional<Bitmap>> = Optional.empty<Optional<Bitmap>>()
private var prevPlayerStates = Triple(false, false, -1)
@Volatile private var nowPlayingChanged = false
@Volatile private var artCacheUpdated = false
@Volatile private var artCache : Bitmap? = null
@Volatile private var screenOn = true
val broadCastReceiver = object : BroadcastReceiver() {
override fun onReceive(contxt: Context?, intent: Intent?) {
when (intent?.action) {
Intent.ACTION_SCREEN_ON -> {
Log.d("MediaService", "screenOn");
screenOn = true
widgetUpdateHandler.post(widgetUpdateRunnable)
}
Intent.ACTION_SCREEN_OFF -> {
Log.d("MediaService", "screenOff");
screenOn = false
}
}
}
}
inner class LocalBinder : Binder() { inner class LocalBinder : Binder() {
fun getEqualizerManager(): EqualizerManager { fun getEqualizerManager(): EqualizerManager {
@ -134,6 +159,7 @@ class MediaService : MediaLibraryService() {
initializePlayerListener() initializePlayerListener()
initializeEqualizerManager() initializeEqualizerManager()
initializeNetworkListener() initializeNetworkListener()
initializeScreenListener()
setPlayer(player) setPlayer(player)
} }
@ -143,6 +169,7 @@ class MediaService : MediaLibraryService() {
} }
override fun onDestroy() { override fun onDestroy() {
unregisterReceiver(broadCastReceiver)
releaseNetworkCallback() releaseNetworkCallback()
equalizerManager.release() equalizerManager.release()
stopWidgetUpdates() stopWidgetUpdates()
@ -288,6 +315,12 @@ class MediaService : MediaLibraryService() {
player.repeatMode = Preferences.getRepeatMode() player.repeatMode = Preferences.getRepeatMode()
} }
private fun initializeScreenListener() {
val filter = IntentFilter(Intent.ACTION_SCREEN_ON)
filter.addAction(Intent.ACTION_SCREEN_OFF)
registerReceiver(broadCastReceiver, filter)
}
private fun initializeEqualizerManager() { private fun initializeEqualizerManager() {
equalizerManager = EqualizerManager() equalizerManager = EqualizerManager()
val audioSessionId = player.audioSessionId val audioSessionId = player.audioSessionId
@ -376,7 +409,9 @@ class MediaService : MediaLibraryService() {
} }
override fun onIsPlayingChanged(isPlaying: Boolean) { override fun onIsPlayingChanged(isPlaying: Boolean) {
artCache = Optional.empty() nowPlayingChanged = true
artCacheUpdated = false
artCache = null
if (!isPlaying) { if (!isPlaying) {
MediaManager.setPlayingPausedTimestamp( MediaManager.setPlayingPausedTimestamp(
player.currentMediaItem, player.currentMediaItem,
@ -390,7 +425,8 @@ class MediaService : MediaLibraryService() {
} else { } else {
stopWidgetUpdates() stopWidgetUpdates()
} }
updateWidget() if (screenOn)
updateWidget()
} }
override fun onPlaybackStateChanged(playbackState: Int) { override fun onPlaybackStateChanged(playbackState: Int) {
@ -505,11 +541,11 @@ class MediaService : MediaLibraryService() {
private inner class CustomGlideTarget : CustomTarget<Bitmap>() { private inner class CustomGlideTarget : CustomTarget<Bitmap>() {
override fun onResourceReady(resource: Bitmap, transition: Transition<in Bitmap>?) { override fun onResourceReady(resource: Bitmap, transition: Transition<in Bitmap>?) {
artCache = Optional.of(Optional.of(resource)) artCache = resource
} }
override fun onLoadCleared(placeholder: Drawable?) { override fun onLoadCleared(placeholder: Drawable?) {
artCache = Optional.of(Optional.empty()) artCache = null
} }
} }
@ -532,7 +568,7 @@ class MediaService : MediaLibraryService() {
val position = player.currentPosition.takeIf { it != C.TIME_UNSET } ?: 0L val position = player.currentPosition.takeIf { it != C.TIME_UNSET } ?: 0L
val duration = player.duration.takeIf { it != C.TIME_UNSET } ?: 0L val duration = player.duration.takeIf { it != C.TIME_UNSET } ?: 0L
if (!TextUtils.isEmpty(coverId) && artCache.isEmpty) { if (!TextUtils.isEmpty(coverId) && nowPlayingChanged) {
CustomGlideRequest.loadAlbumArtBitmap( CustomGlideRequest.loadAlbumArtBitmap(
applicationContext, applicationContext,
coverId, coverId,
@ -540,21 +576,30 @@ class MediaService : MediaLibraryService() {
CustomGlideTarget()) CustomGlideTarget())
} }
WidgetUpdateManager.updateFromState( val newPlayerState = Triple(player.isPlaying, player.shuffleModeEnabled, player.repeatMode)
this, if (nowPlayingChanged || prevPlayerStates != newPlayerState) {
title ?: "", WidgetUpdateManager.updateFromState(
artist ?: "", this,
album ?: "", title ?: "",
artCache, artist ?: "",
player.isPlaying, album ?: "",
player.shuffleModeEnabled, Optional.ofNullable(artCache),
player.repeatMode, player.isPlaying,
position, player.shuffleModeEnabled,
duration, player.repeatMode,
songLink, position,
albumLink, duration,
artistLink songLink,
) albumLink,
artistLink
)
prevPlayerStates = newPlayerState
Log.d("MediaService", "fullUpdate");
} else {
WidgetUpdateManager.updateProgress(this, position, duration)
Log.d("MediaService", "updateProgress");
}
nowPlayingChanged = false
} }
private fun scheduleWidgetUpdates() { private fun scheduleWidgetUpdates() {

View file

@ -7,6 +7,7 @@ import android.graphics.Bitmap;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.os.Bundle; import android.os.Bundle;
import android.text.TextUtils; import android.text.TextUtils;
import android.widget.RemoteViews;
import com.bumptech.glide.request.target.CustomTarget; import com.bumptech.glide.request.target.CustomTarget;
import com.bumptech.glide.request.transition.Transition; import com.bumptech.glide.request.transition.Transition;
@ -36,7 +37,7 @@ public final class WidgetUpdateManager {
String title, String title,
String artist, String artist,
String album, String album,
Bitmap art, Optional<Bitmap> art,
boolean playing, boolean playing,
boolean shuffleEnabled, boolean shuffleEnabled,
int repeatMode, int repeatMode,
@ -54,13 +55,47 @@ public final class WidgetUpdateManager {
AppWidgetManager mgr = AppWidgetManager.getInstance(ctx); AppWidgetManager mgr = AppWidgetManager.getInstance(ctx);
int[] ids = mgr.getAppWidgetIds(new ComponentName(ctx, WidgetProvider4x1.class)); int[] ids = mgr.getAppWidgetIds(new ComponentName(ctx, WidgetProvider4x1.class));
for (int id : ids) { for (int id : ids) {
android.widget.RemoteViews rv = choosePopulate(ctx, title, artist, album, art, playing, android.widget.RemoteViews rv = choosePopulate(ctx, title, artist, album, art.orElse(null), playing,
timing.elapsedText, timing.totalText, timing.progress, shuffleEnabled, repeatMode, id); timing.elapsedText, timing.totalText, timing.progress, shuffleEnabled, repeatMode, id);
WidgetProvider.attachIntents(ctx, rv, id, songLink, albumLink, artistLink); WidgetProvider.attachIntents(ctx, rv, id, songLink, albumLink, artistLink);
mgr.updateAppWidget(id, rv); mgr.updateAppWidget(id, rv);
} }
} }
public static void updateProgress(Context ctx,
long positionMs,
long durationMs) {
final TimingInfo timing = createTimingInfo(positionMs, durationMs);
AppWidgetManager mgr = AppWidgetManager.getInstance(ctx);
int[] ids = mgr.getAppWidgetIds(new ComponentName(ctx, WidgetProvider4x1.class));
for (int id : ids) {
LayoutSize size = resolveLayoutSize(ctx, id);
int layoutRes = 0;
switch (size) {
case MEDIUM:
layoutRes = R.layout.widget_layout_medium;
break;
case LARGE:
layoutRes = R.layout.widget_layout_large_short;
break;
case EXPANDED:
layoutRes = R.layout.widget_layout_large;
break;
case COMPACT:
default:
layoutRes = R.layout.widget_layout_compact;
break;
}
RemoteViews rv = new RemoteViews(ctx.getPackageName(), layoutRes);
int safeProgress = Math.max(0, Math.min(timing.progress, WidgetViewsFactory.PROGRESS_MAX));
rv.setTextViewText(R.id.time_elapsed, timing.elapsedText);
rv.setTextViewText(R.id.time_total, timing.totalText);
rv.setProgressBar(R.id.progress, WidgetViewsFactory.PROGRESS_MAX, safeProgress, false);
mgr.updateAppWidget(id, rv);
}
}
public static void pushNow(Context ctx) { public static void pushNow(Context ctx) {
AppWidgetManager mgr = AppWidgetManager.getInstance(ctx); AppWidgetManager mgr = AppWidgetManager.getInstance(ctx);
int[] ids = mgr.getAppWidgetIds(new ComponentName(ctx, WidgetProvider4x1.class)); int[] ids = mgr.getAppWidgetIds(new ComponentName(ctx, WidgetProvider4x1.class));
@ -72,43 +107,6 @@ public final class WidgetUpdateManager {
} }
public static void updateFromState(Context ctx,
String title,
String artist,
String album,
Optional<Optional<Bitmap>> coverArt,
boolean playing,
boolean shuffleEnabled,
int repeatMode,
long positionMs,
long durationMs,
String songLink,
String albumLink,
String artistLink) {
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 String alb = !TextUtils.isEmpty(album) ? album : "";
final boolean p = playing;
final boolean sh = shuffleEnabled;
final int rep = repeatMode;
final TimingInfo timing = createTimingInfo(positionMs, durationMs);
final String songLinkFinal = songLink;
final String albumLinkFinal = albumLink;
final String artistLinkFinal = artistLink;
AppWidgetManager mgr = AppWidgetManager.getInstance(appCtx);
int[] ids = mgr.getAppWidgetIds(new ComponentName(appCtx, WidgetProvider4x1.class));
Bitmap resource = coverArt.filter(Optional::isPresent).map(Optional::get).orElse(null);
for (int id : ids) {
android.widget.RemoteViews rv = choosePopulate(appCtx, t, a, alb, resource, p,
timing.elapsedText, timing.totalText, timing.progress, sh, rep, id);
WidgetProvider.attachIntents(appCtx, rv, id, songLinkFinal, albumLinkFinal, artistLinkFinal);
mgr.updateAppWidget(id, rv);
}
}
public static void updateFromState(Context ctx, public static void updateFromState(Context ctx,
String title, String title,
String artist, String artist,
@ -131,19 +129,19 @@ public final class WidgetUpdateManager {
new CustomTarget<Bitmap>() { new CustomTarget<Bitmap>() {
@Override @Override
public void onResourceReady(Bitmap resource, Transition<? super Bitmap> transition) { public void onResourceReady(Bitmap resource, Transition<? super Bitmap> transition) {
updateFromState(ctx, title, artist, album, Optional.of(Optional.of(resource)), updateFromState(ctx, title, artist, album, Optional.of(resource),
playing, shuffleEnabled, repeatMode, positionMs, durationMs, songLink, albumLink, artistLink); playing, shuffleEnabled, repeatMode, positionMs, durationMs, songLink, albumLink, artistLink);
} }
@Override @Override
public void onLoadCleared(Drawable placeholder) { public void onLoadCleared(Drawable placeholder) {
updateFromState(ctx, title, artist, album, Optional.of(Optional.empty()), updateFromState(ctx, title, artist, album, Optional.empty(),
playing, shuffleEnabled, repeatMode, positionMs, durationMs, songLink, albumLink, artistLink); playing, shuffleEnabled, repeatMode, positionMs, durationMs, songLink, albumLink, artistLink);
} }
} }
); );
} else { } else {
updateFromState(ctx, title, artist, album, Optional.of(Optional.empty()), updateFromState(ctx, title, artist, album, Optional.empty(),
playing, shuffleEnabled, repeatMode, positionMs, durationMs, songLink, albumLink, artistLink); playing, shuffleEnabled, repeatMode, positionMs, durationMs, songLink, albumLink, artistLink);
} }
} }

View file

@ -19,7 +19,7 @@ import com.cappielloantonio.tempo.R;
public final class WidgetViewsFactory { public final class WidgetViewsFactory {
static final int PROGRESS_MAX = 1000; public static final int PROGRESS_MAX = 1000;
private static final float ALBUM_ART_CORNER_RADIUS_DP = 6f; private static final float ALBUM_ART_CORNER_RADIUS_DP = 6f;
private WidgetViewsFactory() { private WidgetViewsFactory() {