Merge "Filter media controls by user" into rvc-dev
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
index cd27fdf..749b537 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
@@ -290,6 +290,12 @@
/** Whether we're in the middle of dragging the stack around by touch. */
private boolean mIsDraggingStack = false;
+ /**
+ * The pointer index of the ACTION_DOWN event we received prior to an ACTION_UP. We'll ignore
+ * touches from other pointer indices.
+ */
+ private int mPointerIndexDown = -1;
+
/** Description of current animation controller state. */
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.println("Stack view state:");
@@ -2220,6 +2226,18 @@
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
+ if (ev.getAction() != MotionEvent.ACTION_DOWN && ev.getActionIndex() != mPointerIndexDown) {
+ // Ignore touches from additional pointer indices.
+ return false;
+ }
+
+ if (ev.getAction() == MotionEvent.ACTION_DOWN) {
+ mPointerIndexDown = ev.getActionIndex();
+ } else if (ev.getAction() == MotionEvent.ACTION_UP
+ || ev.getAction() == MotionEvent.ACTION_CANCEL) {
+ mPointerIndexDown = -1;
+ }
+
boolean dispatched = super.dispatchTouchEvent(ev);
// If a new bubble arrives while the collapsed stack is being dragged, it will be positioned
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
index 043b368..127c5dd 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
@@ -248,6 +248,7 @@
val lp = LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT)
existingPlayer.view?.player?.setLayoutParams(lp)
+ existingPlayer.bind(data)
existingPlayer.setListening(currentlyExpanded)
updatePlayerToState(existingPlayer, noAnimation = true)
if (existingPlayer.isPlaying) {
@@ -255,16 +256,18 @@
} else {
mediaContent.addView(existingPlayer.view?.player)
}
- } else if (existingPlayer.isPlaying &&
- mediaContent.indexOfChild(existingPlayer.view?.player) != 0) {
- if (visualStabilityManager.isReorderingAllowed) {
- mediaContent.removeView(existingPlayer.view?.player)
- mediaContent.addView(existingPlayer.view?.player, 0)
- } else {
- needsReordering = true
+ } else {
+ existingPlayer.bind(data)
+ if (existingPlayer.isPlaying &&
+ mediaContent.indexOfChild(existingPlayer.view?.player) != 0) {
+ if (visualStabilityManager.isReorderingAllowed) {
+ mediaContent.removeView(existingPlayer.view?.player)
+ mediaContent.addView(existingPlayer.view?.player, 0)
+ } else {
+ needsReordering = true
+ }
}
}
- existingPlayer?.bind(data)
updatePageIndicator()
mediaCarouselScrollHandler.onPlayersChanged()
mediaCarousel.requiresRemeasuring = true
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaTimeoutListener.kt b/packages/SystemUI/src/com/android/systemui/media/MediaTimeoutListener.kt
index 9a134db..8662aac 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaTimeoutListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaTimeoutListener.kt
@@ -54,7 +54,32 @@
if (mediaListeners.containsKey(key)) {
return
}
+ // Having an old key means that we're migrating from/to resumption. We should invalidate
+ // the old listener and create a new one.
+ val migrating = oldKey != null && key != oldKey
+ var wasPlaying = false
+ if (migrating) {
+ if (mediaListeners.containsKey(oldKey)) {
+ val oldListener = mediaListeners.remove(oldKey)
+ wasPlaying = oldListener?.playing ?: false
+ oldListener?.destroy()
+ if (DEBUG) Log.d(TAG, "migrating key $oldKey to $key, for resumption")
+ } else {
+ Log.w(TAG, "Old key $oldKey for player $key doesn't exist. Continuing...")
+ }
+ }
mediaListeners[key] = PlaybackStateListener(key, data)
+
+ // If a player becomes active because of a migration, we'll need to broadcast its state.
+ // Doing it now would lead to reentrant callbacks, so let's wait until we're done.
+ if (migrating && mediaListeners[key]?.playing != wasPlaying) {
+ mainExecutor.execute {
+ if (mediaListeners[key]?.playing == true) {
+ if (DEBUG) Log.d(TAG, "deliver delayed playback state for $key")
+ timeoutCallback.invoke(key, false /* timedOut */)
+ }
+ }
+ }
}
override fun onMediaDataRemoved(key: String) {
@@ -71,7 +96,7 @@
) : MediaController.Callback() {
var timedOut = false
- private var playing: Boolean? = null
+ var playing: Boolean? = null
// Resume controls may have null token
private val mediaController = if (data.token != null) {
@@ -83,7 +108,9 @@
init {
mediaController?.registerCallback(this)
- onPlaybackStateChanged(mediaController?.playbackState)
+ // Let's register the cancellations, but not dispatch events now.
+ // Timeouts didn't happen yet and reentrant events are troublesome.
+ processState(mediaController?.playbackState, dispatchEvents = false)
}
fun destroy() {
@@ -91,8 +118,12 @@
}
override fun onPlaybackStateChanged(state: PlaybackState?) {
+ processState(state, dispatchEvents = true)
+ }
+
+ private fun processState(state: PlaybackState?, dispatchEvents: Boolean) {
if (DEBUG) {
- Log.v(TAG, "onPlaybackStateChanged: $state")
+ Log.v(TAG, "processState: $state")
}
val isPlaying = state != null && isPlayingState(state.state)
@@ -116,12 +147,16 @@
Log.v(TAG, "Execute timeout for $key")
}
timedOut = true
- timeoutCallback(key, timedOut)
+ if (dispatchEvents) {
+ timeoutCallback(key, timedOut)
+ }
}, PAUSED_MEDIA_TIMEOUT)
} else {
expireMediaTimeout(key, "playback started - $state, $key")
timedOut = false
- timeoutCallback(key, timedOut)
+ if (dispatchEvents) {
+ timeoutCallback(key, timedOut)
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaViewController.kt b/packages/SystemUI/src/com/android/systemui/media/MediaViewController.kt
index 83cfdd5..38817d7 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaViewController.kt
@@ -268,7 +268,6 @@
fun attach(transitionLayout: TransitionLayout) {
this.transitionLayout = transitionLayout
layoutController.attach(transitionLayout)
- ensureAllMeasurements()
if (currentEndLocation == -1) {
return
}
@@ -414,13 +413,16 @@
* Clear all existing measurements and refresh the state to match the view.
*/
fun refreshState() {
- if (!firstRefresh) {
- // Let's clear all of our measurements and recreate them!
- viewStates.clear()
- setCurrentState(currentStartLocation, currentEndLocation, currentTransitionProgress,
- applyImmediately = true)
+ // Let's clear all of our measurements and recreate them!
+ viewStates.clear()
+ if (firstRefresh) {
+ // This is the first bind, let's ensure we pre-cache all measurements. Otherwise
+ // We'll just load these on demand.
+ ensureAllMeasurements()
+ firstRefresh = false
}
- firstRefresh = false
+ setCurrentState(currentStartLocation, currentEndLocation, currentTransitionProgress,
+ applyImmediately = true)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
index 75d3d04..7d35416 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
@@ -36,7 +36,6 @@
import android.util.Pair;
import android.view.DisplayInfo;
import android.view.IPinnedStackController;
-import android.view.SurfaceControl;
import android.window.WindowContainerTransaction;
import com.android.systemui.Dependency;
@@ -176,7 +175,7 @@
@Override
public void onActivityRestartAttempt(ActivityManager.RunningTaskInfo task,
boolean homeTaskVisible, boolean clearedTask, boolean wasVisible) {
- if (!wasVisible || task.configuration.windowConfiguration.getWindowingMode()
+ if (task.configuration.windowConfiguration.getWindowingMode()
!= WINDOWING_MODE_PINNED) {
return;
}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
index 6282236..4b2c273 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
@@ -716,7 +716,7 @@
@Override
public void onActivityRestartAttempt(RunningTaskInfo task, boolean homeTaskVisible,
boolean clearedTask, boolean wasVisible) {
- if (!wasVisible || task.configuration.windowConfiguration.getWindowingMode()
+ if (task.configuration.windowConfiguration.getWindowingMode()
!= WINDOWING_MODE_PINNED) {
return;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
index 5628a24..739d30c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
@@ -100,12 +100,7 @@
PAUSED_MEDIA_STATES.add(PlaybackState.STATE_STOPPED);
PAUSED_MEDIA_STATES.add(PlaybackState.STATE_PAUSED);
PAUSED_MEDIA_STATES.add(PlaybackState.STATE_ERROR);
- }
- private static final HashSet<Integer> INACTIVE_MEDIA_STATES = new HashSet<>();
- static {
- INACTIVE_MEDIA_STATES.add(PlaybackState.STATE_NONE);
- INACTIVE_MEDIA_STATES.add(PlaybackState.STATE_STOPPED);
- INACTIVE_MEDIA_STATES.add(PlaybackState.STATE_ERROR);
+ PAUSED_MEDIA_STATES.add(PlaybackState.STATE_CONNECTING);
}
private final NotificationEntryManager mEntryManager;
@@ -262,15 +257,6 @@
return !PAUSED_MEDIA_STATES.contains(state);
}
- /**
- * Check if a state should be considered active (playing or paused)
- * @param state a PlaybackState
- * @return true if active
- */
- public static boolean isActiveState(int state) {
- return !INACTIVE_MEDIA_STATES.contains(state);
- }
-
public void setUpWithPresenter(NotificationPresenter presenter) {
mPresenter = presenter;
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaTimeoutListenerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaTimeoutListenerTest.kt
index bc14362..7a8e4f7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaTimeoutListenerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaTimeoutListenerTest.kt
@@ -32,7 +32,9 @@
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
import org.mockito.ArgumentMatchers.any
+import org.mockito.ArgumentMatchers.anyBoolean
import org.mockito.ArgumentMatchers.anyLong
+import org.mockito.ArgumentMatchers.anyString
import org.mockito.Captor
import org.mockito.Mock
import org.mockito.Mockito
@@ -119,6 +121,7 @@
mediaTimeoutListener.onMediaDataLoaded(KEY, null, mediaData)
verify(mediaController).registerCallback(capture(mediaCallbackCaptor))
verify(executor).executeDelayed(capture(timeoutCaptor), anyLong())
+ verify(timeoutCallback, never()).invoke(anyString(), anyBoolean())
}
@Test
@@ -134,6 +137,24 @@
}
@Test
+ fun testOnMediaDataLoaded_migratesKeys() {
+ // From not playing
+ mediaTimeoutListener.onMediaDataLoaded(KEY, null, mediaData)
+ clearInvocations(mediaController)
+
+ // To playing
+ val playingState = mock(android.media.session.PlaybackState::class.java)
+ `when`(playingState.state).thenReturn(PlaybackState.STATE_PLAYING)
+ `when`(mediaController.playbackState).thenReturn(playingState)
+ mediaTimeoutListener.onMediaDataLoaded("NEWKEY", KEY, mediaData)
+ verify(mediaController).unregisterCallback(anyObject())
+ verify(mediaController).registerCallback(anyObject())
+
+ // Enqueues callback
+ verify(executor).execute(anyObject())
+ }
+
+ @Test
fun testOnPlaybackStateChanged_schedulesTimeout_whenPaused() {
// Assuming we're registered
testOnMediaDataLoaded_registersPlaybackListener()
diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
index 407b9fc..edb3a9c 100644
--- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
@@ -2177,7 +2177,7 @@
// split-screen in split-screen.
mService.getTaskChangeNotificationController()
.notifyActivityDismissingDockedStack();
- taskDisplayArea.onSplitScreenModeDismissed(task.getStack());
+ taskDisplayArea.onSplitScreenModeDismissed((ActivityStack) task);
taskDisplayArea.mDisplayContent.ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS,
true /* notifyClients */);
}