Skip to content

Commit f5cb5cb

Browse files
neobuddy89joeyhuab
authored andcommitted
SystemUI: Improve Ongoing Chip interactions
* Expand to full mode for 10 seconds when taping on compact mode. * Add different haptic effects for different elements/actions. * Keep chip visible when media is paused for grace period of 20 seconds. * Add play button for media popup; show when media is paused. Signed-off-by: Pranav Vashi <neobuddy89@gmail.com>
1 parent f1d1ab7 commit f5cb5cb

4 files changed

Lines changed: 163 additions & 74 deletions

File tree

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<!--
3+
SPDX-FileCopyrightText: crDroid Android Project
4+
SPDX-License-Identifier: Apache-2.0
5+
-->
6+
<vector xmlns:android="http://schemas.android.com/apk/res/android"
7+
android:width="24dp"
8+
android:height="24dp"
9+
android:viewportWidth="960"
10+
android:viewportHeight="960"
11+
android:tint="?attr/colorControlNormal">
12+
<path
13+
android:fillColor="@android:color/white"
14+
android:pathData="M380,660L660,480L380,300L380,660ZM480,880Q397,880 324,848.5Q251,817 197,763Q143,709 111.5,636Q80,563 80,480Q80,397 111.5,324Q143,251 197,197Q251,143 324,111.5Q397,80 480,80Q563,80 636,111.5Q709,143 763,197Q817,251 848.5,324Q880,397 880,480Q880,563 848.5,636Q817,709 763,763Q709,817 636,848.5Q563,880 480,880Z"/>
15+
</vector>

packages/SystemUI/src/com/android/systemui/statusbar/OnGoingActionProgressController.kt

Lines changed: 104 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -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
)

packages/SystemUI/src/com/android/systemui/statusbar/OngoingActionProgressCompose.kt

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -221,8 +221,12 @@ fun OngoingActionProgress(
221221
)
222222

223223
MediaControlButton(
224-
iconRes = R.drawable.ic_media_control_pause,
225-
contentDescription = "Pause",
224+
iconRes = if (state.isMediaPlaying) {
225+
R.drawable.ic_media_control_pause
226+
} else {
227+
R.drawable.ic_media_control_play
228+
},
229+
contentDescription = if (state.isMediaPlaying) "Pause" else "Play",
226230
onClick = { controller.onMediaAction(1) }
227231
)
228232

@@ -298,7 +302,8 @@ class OnGoingActionProgressComposeController(
298302
iconBitmap = state.iconBitmap,
299303
packageName = state.packageName,
300304
isCompactMode = state.isCompactMode,
301-
showMediaControls = state.showMediaControls
305+
showMediaControls = state.showMediaControls,
306+
isMediaPlaying = state.isMediaPlaying
302307
)
303308
}
304309
}

0 commit comments

Comments
 (0)