diff --git a/app/src/main/java/com/cappielloantonio/tempo/widget/WidgetUpdateManager.java b/app/src/main/java/com/cappielloantonio/tempo/widget/WidgetUpdateManager.java
index 6de08905..e9515c50 100644
--- a/app/src/main/java/com/cappielloantonio/tempo/widget/WidgetUpdateManager.java
+++ b/app/src/main/java/com/cappielloantonio/tempo/widget/WidgetUpdateManager.java
@@ -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;
+ }
+ }
+
}
diff --git a/app/src/main/java/com/cappielloantonio/tempo/widget/WidgetViewsFactory.java b/app/src/main/java/com/cappielloantonio/tempo/widget/WidgetViewsFactory.java
index 9dec3d60..05346eb3 100644
--- a/app/src/main/java/com/cappielloantonio/tempo/widget/WidgetViewsFactory.java
+++ b/app/src/main/java/com/cappielloantonio/tempo/widget/WidgetViewsFactory.java
@@ -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;
}
}
diff --git a/app/src/main/res/layout/widget_layout_compact.xml b/app/src/main/res/layout/widget_layout_compact.xml
index 8f139917..5e0108dc 100644
--- a/app/src/main/res/layout/widget_layout_compact.xml
+++ b/app/src/main/res/layout/widget_layout_compact.xml
@@ -43,6 +43,45 @@
android:textColor="@color/widget_subtitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Tempo Widget
Şu an oynatılmıyor
Tempo’yu aç
+ 0:00
+ 0:00
Albüm kapağı
Çal/Duraklat
Sonraki parça
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 483b4958..6a4851cd 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -442,6 +442,8 @@
Tempo Widget
Not playing
Open Tempo
+ 0:00
+ 0:00
Album artwork
Play or pause
Next track
diff --git a/app/src/notquitemy/java/com/cappielloantonio/tempo/service/MediaService.kt b/app/src/notquitemy/java/com/cappielloantonio/tempo/service/MediaService.kt
index de6a6f06..8ed4ecdf 100644
--- a/app/src/notquitemy/java/com/cappielloantonio/tempo/service/MediaService.kt
+++ b/app/src/notquitemy/java/com/cappielloantonio/tempo/service/MediaService.kt
@@ -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
)
}
diff --git a/app/src/play/java/com/cappielloantonio/tempo/service/MediaService.kt b/app/src/play/java/com/cappielloantonio/tempo/service/MediaService.kt
index dadf0d7d..69123de4 100644
--- a/app/src/play/java/com/cappielloantonio/tempo/service/MediaService.kt
+++ b/app/src/play/java/com/cappielloantonio/tempo/service/MediaService.kt
@@ -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
)
}
diff --git a/app/src/tempo/java/com/cappielloantonio/tempo/service/MediaService.kt b/app/src/tempo/java/com/cappielloantonio/tempo/service/MediaService.kt
index 4c9c77aa..b7d507b6 100644
--- a/app/src/tempo/java/com/cappielloantonio/tempo/service/MediaService.kt
+++ b/app/src/tempo/java/com/cappielloantonio/tempo/service/MediaService.kt
@@ -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
)
}