mirror of
https://github.com/antebudimir/tempus.git
synced 2025-12-31 09:33:33 +00:00
fix: Include song position and duration in widget
Co-authored-by: Mücahit Kaya <kaya-mucahit@outlook.com> Co-authored-by: The Firehawk <firehawk@opayq.net>
This commit is contained in:
parent
cc0e264a17
commit
e81e1a5356
9 changed files with 210 additions and 20 deletions
|
|
@ -10,9 +10,11 @@ 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.common.C;
|
||||
import androidx.media3.session.MediaController;
|
||||
import androidx.media3.session.SessionToken;
|
||||
import com.cappielloantonio.tempo.service.MediaService;
|
||||
import com.cappielloantonio.tempo.util.MusicUtil;
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
import com.google.common.util.concurrent.MoreExecutors;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
|
@ -23,14 +25,19 @@ public final class WidgetUpdateManager {
|
|||
String title,
|
||||
String artist,
|
||||
Bitmap art,
|
||||
boolean playing) {
|
||||
boolean playing,
|
||||
long positionMs,
|
||||
long durationMs) {
|
||||
if (TextUtils.isEmpty(title)) title = ctx.getString(R.string.widget_not_playing);
|
||||
if (TextUtils.isEmpty(artist)) artist = ctx.getString(R.string.widget_placeholder_subtitle);
|
||||
|
||||
final TimingInfo timing = createTimingInfo(positionMs, durationMs);
|
||||
|
||||
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);
|
||||
android.widget.RemoteViews rv = choosePopulate(ctx, title, artist, art, playing,
|
||||
timing.elapsedText, timing.totalText, timing.progress, id);
|
||||
WidgetProvider.attachIntents(ctx, rv, id);
|
||||
mgr.updateAppWidget(id, rv);
|
||||
}
|
||||
|
|
@ -50,11 +57,14 @@ public final class WidgetUpdateManager {
|
|||
String title,
|
||||
String artist,
|
||||
String coverArtId,
|
||||
boolean playing) {
|
||||
boolean playing,
|
||||
long positionMs,
|
||||
long durationMs) {
|
||||
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;
|
||||
final TimingInfo timing = createTimingInfo(positionMs, durationMs);
|
||||
|
||||
if (!TextUtils.isEmpty(coverArtId)) {
|
||||
CustomGlideRequest.loadAlbumArtBitmap(
|
||||
|
|
@ -66,7 +76,8 @@ public final class WidgetUpdateManager {
|
|||
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);
|
||||
android.widget.RemoteViews rv = choosePopulate(appCtx, t, a, resource, p,
|
||||
timing.elapsedText, timing.totalText, timing.progress, id);
|
||||
WidgetProvider.attachIntents(appCtx, rv, id);
|
||||
mgr.updateAppWidget(id, rv);
|
||||
}
|
||||
|
|
@ -76,7 +87,8 @@ public final class WidgetUpdateManager {
|
|||
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);
|
||||
android.widget.RemoteViews rv = choosePopulate(appCtx, t, a, null, p,
|
||||
timing.elapsedText, timing.totalText, timing.progress, id);
|
||||
WidgetProvider.attachIntents(appCtx, rv, id);
|
||||
mgr.updateAppWidget(id, rv);
|
||||
}
|
||||
|
|
@ -87,7 +99,8 @@ public final class WidgetUpdateManager {
|
|||
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);
|
||||
android.widget.RemoteViews rv = choosePopulate(appCtx, t, a, null, p,
|
||||
timing.elapsedText, timing.totalText, timing.progress, id);
|
||||
WidgetProvider.attachIntents(appCtx, rv, id);
|
||||
mgr.updateAppWidget(id, rv);
|
||||
}
|
||||
|
|
@ -113,25 +126,71 @@ public final class WidgetUpdateManager {
|
|||
coverId = mi.mediaMetadata.extras.getString("coverArtId");
|
||||
}
|
||||
}
|
||||
long position = c.getCurrentPosition();
|
||||
long duration = c.getDuration();
|
||||
if (position == C.TIME_UNSET) position = 0;
|
||||
if (duration == C.TIME_UNSET) duration = 0;
|
||||
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.isPlaying(),
|
||||
position,
|
||||
duration);
|
||||
c.release();
|
||||
} catch (ExecutionException | InterruptedException ignored) {
|
||||
}
|
||||
}, MoreExecutors.directExecutor());
|
||||
}
|
||||
|
||||
private static TimingInfo createTimingInfo(long positionMs, long durationMs) {
|
||||
long safePosition = Math.max(0L, positionMs);
|
||||
long safeDuration = durationMs > 0 ? durationMs : 0L;
|
||||
if (safeDuration > 0 && safePosition > safeDuration) {
|
||||
safePosition = safeDuration;
|
||||
}
|
||||
|
||||
String elapsed = (safeDuration > 0 || safePosition > 0)
|
||||
? MusicUtil.getReadableDurationString(safePosition, true)
|
||||
: null;
|
||||
String total = safeDuration > 0
|
||||
? MusicUtil.getReadableDurationString(safeDuration, true)
|
||||
: null;
|
||||
|
||||
int progress = 0;
|
||||
if (safeDuration > 0) {
|
||||
long scaled = safePosition * WidgetViewsFactory.PROGRESS_MAX;
|
||||
long progressLong = scaled / safeDuration;
|
||||
if (progressLong < 0) {
|
||||
progress = 0;
|
||||
} else if (progressLong > WidgetViewsFactory.PROGRESS_MAX) {
|
||||
progress = WidgetViewsFactory.PROGRESS_MAX;
|
||||
} else {
|
||||
progress = (int) progressLong;
|
||||
}
|
||||
}
|
||||
|
||||
return new TimingInfo(elapsed, total, progress);
|
||||
}
|
||||
|
||||
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 android.widget.RemoteViews choosePopulate(Context ctx,
|
||||
String title,
|
||||
String artist,
|
||||
Bitmap art,
|
||||
boolean playing,
|
||||
String elapsedText,
|
||||
String totalText,
|
||||
int progress,
|
||||
int appWidgetId) {
|
||||
if (isLarge(ctx, appWidgetId)) {
|
||||
return WidgetViewsFactory.populateLarge(ctx, title, artist, art, playing, elapsedText, totalText, progress);
|
||||
}
|
||||
return WidgetViewsFactory.populate(ctx, title, artist, art, playing, elapsedText, totalText, progress);
|
||||
}
|
||||
|
||||
private static boolean isLarge(Context ctx, int appWidgetId) {
|
||||
|
|
@ -142,5 +201,16 @@ public final class WidgetUpdateManager {
|
|||
return minH >= threshold; // dp threshold for 2-row height
|
||||
}
|
||||
|
||||
private static final class TimingInfo {
|
||||
final String elapsedText;
|
||||
final String totalText;
|
||||
final int progress;
|
||||
|
||||
TimingInfo(String elapsedText, String totalText, int progress) {
|
||||
this.elapsedText = elapsedText;
|
||||
this.totalText = totalText;
|
||||
this.progress = progress;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,11 +2,14 @@ package com.cappielloantonio.tempo.widget;
|
|||
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.text.TextUtils;
|
||||
import android.widget.RemoteViews;
|
||||
import com.cappielloantonio.tempo.R;
|
||||
|
||||
public final class WidgetViewsFactory {
|
||||
|
||||
static final int PROGRESS_MAX = 1000;
|
||||
|
||||
public static RemoteViews buildCompact(Context ctx) {
|
||||
return build(ctx, R.layout.widget_layout_compact);
|
||||
}
|
||||
|
|
@ -20,6 +23,9 @@ public final class WidgetViewsFactory {
|
|||
RemoteViews rv = new RemoteViews(ctx.getPackageName(), layoutRes);
|
||||
rv.setTextViewText(R.id.title, ctx.getString(R.string.widget_not_playing));
|
||||
rv.setTextViewText(R.id.subtitle, ctx.getString(R.string.widget_placeholder_subtitle));
|
||||
rv.setTextViewText(R.id.time_elapsed, ctx.getString(R.string.widget_time_elapsed_placeholder));
|
||||
rv.setTextViewText(R.id.time_total, ctx.getString(R.string.widget_time_duration_placeholder));
|
||||
rv.setProgressBar(R.id.progress, PROGRESS_MAX, 0, false);
|
||||
rv.setImageViewResource(R.id.btn_play_pause, R.drawable.ic_play);
|
||||
// Show Tempo logo when nothing is playing
|
||||
rv.setImageViewResource(R.id.album_art, R.drawable.ic_splash_logo);
|
||||
|
|
@ -30,16 +36,22 @@ public final class WidgetViewsFactory {
|
|||
String title,
|
||||
String subtitle,
|
||||
Bitmap art,
|
||||
boolean playing) {
|
||||
return populateWithLayout(ctx, title, subtitle, art, playing, R.layout.widget_layout_compact);
|
||||
boolean playing,
|
||||
String elapsedText,
|
||||
String totalText,
|
||||
int progress) {
|
||||
return populateWithLayout(ctx, title, subtitle, art, playing, elapsedText, totalText, progress, R.layout.widget_layout_compact);
|
||||
}
|
||||
|
||||
public static RemoteViews populateLarge(Context ctx,
|
||||
String title,
|
||||
String subtitle,
|
||||
Bitmap art,
|
||||
boolean playing) {
|
||||
return populateWithLayout(ctx, title, subtitle, art, playing, R.layout.widget_layout_large);
|
||||
boolean playing,
|
||||
String elapsedText,
|
||||
String totalText,
|
||||
int progress) {
|
||||
return populateWithLayout(ctx, title, subtitle, art, playing, elapsedText, totalText, progress, R.layout.widget_layout_large);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -48,6 +60,9 @@ public final class WidgetViewsFactory {
|
|||
String subtitle,
|
||||
Bitmap art,
|
||||
boolean playing,
|
||||
String elapsedText,
|
||||
String totalText,
|
||||
int progress,
|
||||
int layoutRes) {
|
||||
RemoteViews rv = new RemoteViews(ctx.getPackageName(), layoutRes);
|
||||
rv.setTextViewText(R.id.title, title);
|
||||
|
|
@ -63,6 +78,21 @@ public final class WidgetViewsFactory {
|
|||
rv.setImageViewResource(R.id.btn_play_pause,
|
||||
playing ? R.drawable.ic_pause : R.drawable.ic_play);
|
||||
|
||||
String elapsed = !TextUtils.isEmpty(elapsedText)
|
||||
? elapsedText
|
||||
: ctx.getString(R.string.widget_time_elapsed_placeholder);
|
||||
String total = !TextUtils.isEmpty(totalText)
|
||||
? totalText
|
||||
: ctx.getString(R.string.widget_time_duration_placeholder);
|
||||
|
||||
int safeProgress = progress;
|
||||
if (safeProgress < 0) safeProgress = 0;
|
||||
if (safeProgress > PROGRESS_MAX) safeProgress = PROGRESS_MAX;
|
||||
|
||||
rv.setTextViewText(R.id.time_elapsed, elapsed);
|
||||
rv.setTextViewText(R.id.time_total, total);
|
||||
rv.setProgressBar(R.id.progress, PROGRESS_MAX, safeProgress, false);
|
||||
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,6 +43,45 @@
|
|||
android:textColor="@color/widget_subtitle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"/>
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progress"
|
||||
style="?android:attr/progressBarStyleHorizontal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="4dp"
|
||||
android:layout_marginTop="4dp"
|
||||
android:indeterminate="false"
|
||||
android:max="1000"
|
||||
android:progress="0"
|
||||
android:progressBackgroundTint="@color/widget_subtitle"
|
||||
android:progressTint="@color/widget_icon_tint"/>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/timing"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="2dp"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/time_elapsed"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="@string/widget_time_elapsed_placeholder"
|
||||
android:textColor="@color/widget_subtitle"
|
||||
android:textSize="11sp"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/time_total"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:gravity="end"
|
||||
android:text="@string/widget_time_duration_placeholder"
|
||||
android:textColor="@color/widget_subtitle"
|
||||
android:textSize="11sp"/>
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
|
|
|
|||
|
|
@ -43,6 +43,45 @@
|
|||
android:textColor="@color/widget_subtitle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"/>
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progress"
|
||||
style="?android:attr/progressBarStyleHorizontal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="4dp"
|
||||
android:layout_marginTop="6dp"
|
||||
android:indeterminate="false"
|
||||
android:max="1000"
|
||||
android:progress="0"
|
||||
android:progressBackgroundTint="@color/widget_subtitle"
|
||||
android:progressTint="@color/widget_icon_tint"/>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/timing"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/time_elapsed"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="@string/widget_time_elapsed_placeholder"
|
||||
android:textColor="@color/widget_subtitle"
|
||||
android:textSize="12sp"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/time_total"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:gravity="end"
|
||||
android:text="@string/widget_time_duration_placeholder"
|
||||
android:textColor="@color/widget_subtitle"
|
||||
android:textSize="12sp"/>
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
|
|
|
|||
|
|
@ -445,6 +445,8 @@
|
|||
<string name="widget_label">Tempo Widget</string>
|
||||
<string name="widget_not_playing">Şu an oynatılmıyor</string>
|
||||
<string name="widget_placeholder_subtitle">Tempo’yu aç</string>
|
||||
<string name="widget_time_elapsed_placeholder">0:00</string>
|
||||
<string name="widget_time_duration_placeholder">0:00</string>
|
||||
<string name="widget_content_desc_album_art">Albüm kapağı</string>
|
||||
<string name="widget_content_desc_play_pause">Çal/Duraklat</string>
|
||||
<string name="widget_content_desc_next">Sonraki parça</string>
|
||||
|
|
|
|||
|
|
@ -442,6 +442,8 @@
|
|||
<string name="widget_label">Tempo Widget</string>
|
||||
<string name="widget_not_playing">Not playing</string>
|
||||
<string name="widget_placeholder_subtitle">Open Tempo</string>
|
||||
<string name="widget_time_elapsed_placeholder">0:00</string>
|
||||
<string name="widget_time_duration_placeholder">0:00</string>
|
||||
<string name="widget_content_desc_album_art">Album artwork</string>
|
||||
<string name="widget_content_desc_play_pause">Play or pause</string>
|
||||
<string name="widget_content_desc_next">Next track</string>
|
||||
|
|
|
|||
|
|
@ -394,12 +394,16 @@ class MediaService : MediaLibraryService() {
|
|||
val artist = mi?.mediaMetadata?.artist?.toString()
|
||||
?: mi?.mediaMetadata?.extras?.getString("artist")
|
||||
val coverId = mi?.mediaMetadata?.extras?.getString("coverArtId")
|
||||
val position = player.currentPosition.takeIf { it != C.TIME_UNSET } ?: 0L
|
||||
val duration = player.duration.takeIf { it != C.TIME_UNSET } ?: 0L
|
||||
WidgetUpdateManager.updateFromState(
|
||||
this,
|
||||
title ?: "",
|
||||
artist ?: "",
|
||||
coverId,
|
||||
player.isPlaying
|
||||
player.isPlaying,
|
||||
position,
|
||||
duration
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -241,14 +241,16 @@ class MediaService : MediaLibraryService(), SessionAvailabilityListener {
|
|||
?: mi?.mediaMetadata?.extras?.getString("artist")
|
||||
val coverId = mi?.mediaMetadata?.extras?.getString("coverArtId")
|
||||
|
||||
val position = player.currentPosition.takeIf { it != C.TIME_UNSET } ?: 0L
|
||||
val duration = player.duration.takeIf { it != C.TIME_UNSET } ?: 0L
|
||||
WidgetUpdateManager.updateFromState(
|
||||
this,
|
||||
title ?: "",
|
||||
artist ?: "",
|
||||
coverId,
|
||||
player.isPlaying,
|
||||
player.currentPosition,
|
||||
player.duration
|
||||
position,
|
||||
duration
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -240,14 +240,16 @@ class MediaService : MediaLibraryService(), SessionAvailabilityListener {
|
|||
val artist = mi?.mediaMetadata?.artist?.toString()
|
||||
?: mi?.mediaMetadata?.extras?.getString("artist")
|
||||
val coverId = mi?.mediaMetadata?.extras?.getString("coverArtId")
|
||||
val position = player.currentPosition.takeIf { it != C.TIME_UNSET } ?: 0L
|
||||
val duration = player.duration.takeIf { it != C.TIME_UNSET } ?: 0L
|
||||
WidgetUpdateManager.updateFromState(
|
||||
this,
|
||||
title ?: "",
|
||||
artist ?: "",
|
||||
coverId,
|
||||
player.isPlaying,
|
||||
player.currentPosition,
|
||||
player.duration
|
||||
position,
|
||||
duration
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue