@@ -69,6 +69,7 @@ class OnGoingActionProgressController(
6969 private val inFlightIconLoads = ConcurrentHashMap <String , Job >()
7070
7171 private var showMediaProgress = true
72+ private var pauseStale = false
7273 private var isTrackingProgress = false
7374 private var isForceHidden = false
7475 private var headsUpPinned = false
@@ -97,6 +98,7 @@ class OnGoingActionProgressController(
9798 private var staleCheckerJob: Job ? = null
9899 private var compactCollapseJob: Job ? = null
99100 private var menuCollapseJob: Job ? = null
101+ private var pausedStaleJob: Job ? = null
100102
101103 private val _state = MutableStateFlow (ProgressState ())
102104 val state: StateFlow <ProgressState > = _state .asStateFlow()
@@ -145,11 +147,22 @@ class OnGoingActionProgressController(
145147 private val mediaMetadataListener = object : MediaSessionManagerHelper .MediaMetadataListener {
146148 override fun onMediaMetadataChanged () {
147149 needsFullUiUpdate = true
150+ pauseStale = false
148151 requestUiUpdate()
149152 }
150153
151154 override fun onPlaybackStateChanged () {
152155 needsFullUiUpdate = true
156+ pauseStale = false
157+ pausedStaleJob?.cancel()
158+ if (mediaSessionHelper.isMediaSessionActive() &&
159+ ! mediaSessionHelper.isMediaPlaying()) {
160+ pausedStaleJob = mainScope.launch {
161+ delay(PAUSED_STALE_GRACE_MS )
162+ pauseStale = true
163+ requestUiUpdate()
164+ }
165+ }
153166 requestUiUpdate()
154167 }
155168 }
@@ -180,16 +193,17 @@ class OnGoingActionProgressController(
180193 }
181194
182195 fun expandCompactView () {
196+ val wasExpanded = isExpanded
183197 isExpanded = true
184198 compactCollapseJob?.cancel()
185199 compactCollapseJob = mainScope.launch {
186- delay(5000L )
200+ delay(COMPACT_COLLAPSE_TIMEOUT_MS )
187201 if (isCompactModeEnabled && isExpanded) {
188202 isExpanded = false
189203 requestUiUpdate()
190204 }
191205 }
192- updateProgressState ()
206+ if (wasExpanded != isExpanded) requestUiUpdate ()
193207 }
194208
195209 private fun requestUiUpdate () {
@@ -208,22 +222,24 @@ class OnGoingActionProgressController(
208222
209223 private fun updateProgressState () {
210224 var isVisible = ! isForceHidden && ! headsUpPinned && ! isSystemChipVisible
211- val isMediaPlaying = showMediaProgress && mediaSessionHelper.isMediaPlaying ()
225+ val hasMediaSession = isMediaSessionActiveForChip ()
212226 val hasNotificationProgress = isEnabled && isTrackingProgress
213227
214- isVisible = isVisible && (isMediaPlaying || hasNotificationProgress)
228+ isVisible = isVisible && (hasMediaSession || hasNotificationProgress)
215229
216230 if (! isVisible) {
217- val update = ProgressState (
218- isVisible = false ,
219- progress = 0 ,
220- maxProgress = 0 ,
221- iconBitmap = null ,
222- packageName = null ,
223- isCompactMode = false ,
224- showMediaControls = false
231+ publish(
232+ ProgressState (
233+ isVisible = false ,
234+ progress = 0 ,
235+ maxProgress = 0 ,
236+ iconBitmap = null ,
237+ packageName = null ,
238+ isCompactMode = false ,
239+ showMediaControls = false ,
240+ isMediaPlaying = false
241+ )
225242 )
226- publish(update)
227243 return
228244 }
229245
@@ -248,16 +264,20 @@ class OnGoingActionProgressController(
248264 null
249265 }
250266
251- val update = ProgressState (
252- isVisible = true ,
253- progress = currentProgress,
254- maxProgress = currentProgressMax,
255- iconBitmap = currentIconBitmap,
256- packageName = trackedPackageName,
257- isCompactMode = isCompact,
258- showMediaControls = isMenuVisible
267+ val isMediaPlaying = showMediaProgress && mediaSessionHelper.isMediaPlaying()
268+
269+ publish(
270+ ProgressState (
271+ isVisible = true ,
272+ progress = currentProgress,
273+ maxProgress = currentProgressMax,
274+ iconBitmap = currentIconBitmap,
275+ packageName = trackedPackageName,
276+ isCompactMode = isCompact,
277+ showMediaControls = isMenuVisible,
278+ isMediaPlaying = isMediaPlaying
279+ )
259280 )
260- publish(update)
261281 }
262282
263283 private fun updateViews () {
@@ -271,23 +291,41 @@ class OnGoingActionProgressController(
271291 return
272292 }
273293
274- val isMediaPlaying = showMediaProgress && mediaSessionHelper.isMediaPlaying ()
294+ val hasMediaSession = isMediaSessionActiveForChip ()
275295
276296 if (isCompactModeEnabled && ! isExpanded) {
277- if (! isEnabled && ! isMediaPlaying ) {
297+ if (! isEnabled && ! hasMediaSession ) {
278298 stopMediaLoop()
279299 updateProgressState()
280300 return
281301 }
282- if (isMediaPlaying) updateMediaProgressCompact() else updateNotificationProgressCompact()
302+ if (hasMediaSession) {
303+ updateMediaProgressCompact()
304+ } else {
305+ updateNotificationProgress()
306+ }
283307 } else {
284- if (isMediaPlaying) {
308+ val isMediaPlaying = showMediaProgress && mediaSessionHelper.isMediaPlaying()
309+ if (isTrackingProgress && ! isMediaPlaying) {
310+ stopMediaLoop()
311+ updateNotificationProgress()
312+ if (hasMediaSession) {
313+ pauseStale = true
314+ needsFullUiUpdate = true
315+ }
316+ } else if (hasMediaSession) {
285317 if (needsFullUiUpdate) {
286318 updateMediaProgressFull()
287319 needsFullUiUpdate = false
288320 } else {
289321 updateMediaProgressOnly()
290322 }
323+
324+ if (isMediaPlaying) {
325+ ensureMediaLoopRunning()
326+ } else {
327+ stopMediaLoop()
328+ }
291329 } else {
292330 stopMediaLoop()
293331 updateNotificationProgress()
@@ -324,7 +362,7 @@ class OnGoingActionProgressController(
324362 }
325363
326364 private fun updateMediaProgressFull () {
327- ensureMediaLoopRunning()
365+ if (mediaSessionHelper.isMediaPlaying()) ensureMediaLoopRunning() else stopMediaLoop ()
328366
329367 val mediaAppIcon = mediaSessionHelper.getMediaAppIcon()
330368 if (mediaAppIcon != null ) {
@@ -355,7 +393,7 @@ class OnGoingActionProgressController(
355393 }
356394
357395 private fun updateMediaProgressCompact () {
358- ensureMediaLoopRunning()
396+ if (mediaSessionHelper.isMediaPlaying()) ensureMediaLoopRunning() else stopMediaLoop ()
359397
360398 val totalDuration = mediaSessionHelper.getTotalDuration()
361399 val playbackState = mediaSessionHelper.getMediaControllerPlaybackState()
@@ -412,10 +450,6 @@ class OnGoingActionProgressController(
412450 }
413451 }
414452
415- private fun updateNotificationProgressCompact () {
416- updateNotificationProgress()
417- }
418-
419453 private fun fetchPackageIcon (packageName : String ): Drawable {
420454 val pm = context.packageManager
421455 return try {
@@ -529,33 +563,37 @@ class OnGoingActionProgressController(
529563 ! indeterminate && maxValid
530564 }
531565
566+ private fun isMediaSessionActiveForChip (): Boolean {
567+ if (! showMediaProgress) return false
568+ if (! mediaSessionHelper.isMediaSessionActive()) return false
569+ if (mediaSessionHelper.isMediaPlaying()) return true
570+ if (isMenuVisible) return true
571+ return ! pauseStale
572+ }
573+
532574 fun onInteraction () {
533- if (showMediaProgress && mediaSessionHelper.isMediaPlaying()) {
575+ vibrator.vibrate(HAPTIC_CLICK )
576+ if (isCompactModeEnabled) {
577+ expandCompactView()
578+ }
579+ if (isMediaSessionActiveForChip()) {
534580 isMenuVisible = ! isMenuVisible
535- updateProgressState()
536- if (isMenuVisible) {
537- menuCollapseJob?.cancel()
538- menuCollapseJob = mainScope.launch {
539- delay(5000L )
540- isMenuVisible = false
541- updateProgressState()
542- }
543- }
581+ collapseMediaControlsWithDelay()
544582 } else {
545583 openTrackedApp()
546584 }
547- vibrator.vibrate( VIBRATION_EFFECT )
585+ updateProgressState( )
548586 }
549587
550588 fun onLongPress () {
551- if (showMediaProgress && mediaSessionHelper.isMediaPlaying()) openMediaApp() else openTrackedApp( )
552- vibrator.vibrate( VIBRATION_EFFECT )
589+ vibrator.vibrate( HAPTIC_LONG )
590+ if (isMediaSessionActiveForChip()) openMediaApp() else openTrackedApp( )
553591 }
554592
555593 fun onDoubleTap () {
556- if (showMediaProgress && mediaSessionHelper.isMediaPlaying()) {
594+ if (isMediaSessionActiveForChip()) {
595+ vibrator.vibrate(HAPTIC_DOUBLE )
557596 toggleMediaPlaybackState()
558- vibrator.vibrate(VIBRATION_EFFECT )
559597 }
560598 }
561599
@@ -564,17 +602,22 @@ class OnGoingActionProgressController(
564602 }
565603
566604 fun onMediaAction (action : Int ) {
605+ vibrator.vibrate(HAPTIC_CLICK )
606+
567607 when (action) {
568608 0 -> skipToPreviousTrack()
569609 1 -> toggleMediaPlaybackState()
570610 2 -> skipToNextTrack()
571611 }
612+ collapseMediaControlsWithDelay()
613+ }
572614
615+ fun collapseMediaControlsWithDelay () {
616+ if (! isMenuVisible) return
573617 menuCollapseJob?.cancel()
574618 menuCollapseJob = mainScope.launch {
575- delay(5000L )
576- isMenuVisible = false
577- updateProgressState()
619+ delay(MENU_COLLAPSE_TIMEOUT_MS )
620+ onMediaMenuDismiss()
578621 }
579622 }
580623
@@ -586,7 +629,6 @@ class OnGoingActionProgressController(
586629 fun setSystemChipVisible (visible : Boolean ) {
587630 if (isSystemChipVisible == visible) return
588631 isSystemChipVisible = visible
589- updateProgressState()
590632 requestUiUpdate()
591633 }
592634
@@ -752,6 +794,7 @@ class OnGoingActionProgressController(
752794 staleCheckerJob?.cancel()
753795 compactCollapseJob?.cancel()
754796 menuCollapseJob?.cancel()
797+ pausedStaleJob?.cancel()
755798
756799 iconCache.clear()
757800 inFlightIconLoads.values.forEach { it.cancel() }
@@ -772,9 +815,16 @@ class OnGoingActionProgressController(
772815 private const val DEBOUNCE_DELAY_MS = 150L
773816 private const val STALE_PROGRESS_CHECK_INTERVAL_MS = 5000L
774817 private const val PROGRESS_TIMEOUT_MS = 30000L
818+ private const val COMPACT_COLLAPSE_TIMEOUT_MS = 10000L
819+ private const val MENU_COLLAPSE_TIMEOUT_MS = 5000L
820+ private const val PAUSED_STALE_GRACE_MS = 20000L
775821
776- private val VIBRATION_EFFECT : VibrationEffect =
777- VibrationEffect .get(VibrationEffect .EFFECT_CLICK )
822+ private val HAPTIC_CLICK =
823+ VibrationEffect .createPredefined(VibrationEffect .EFFECT_CLICK )
824+ private val HAPTIC_DOUBLE =
825+ VibrationEffect .createPredefined(VibrationEffect .EFFECT_DOUBLE_CLICK )
826+ private val HAPTIC_LONG =
827+ VibrationEffect .createPredefined(VibrationEffect .EFFECT_HEAVY_CLICK )
778828 }
779829}
780830
@@ -787,4 +837,5 @@ data class ProgressState(
787837 val packageName : String? = null ,
788838 val isCompactMode : Boolean = false ,
789839 val showMediaControls : Boolean = false ,
840+ val isMediaPlaying : Boolean = false ,
790841)
0 commit comments