Merge "Close LS media player when session is destroyed" into rvc-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
0dcd72e1de
@@ -24,6 +24,8 @@ import android.graphics.Bitmap;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.graphics.drawable.Icon;
|
||||
import android.media.MediaMetadata;
|
||||
import android.media.session.MediaController;
|
||||
import android.media.session.MediaSession;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.widget.ImageButton;
|
||||
@@ -40,6 +42,7 @@ import androidx.palette.graphics.Palette;
|
||||
import com.android.internal.util.ContrastColorUtil;
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.dagger.qualifiers.Background;
|
||||
import com.android.systemui.media.MediaControllerFactory;
|
||||
import com.android.systemui.statusbar.notification.MediaNotificationProcessor;
|
||||
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
|
||||
import com.android.systemui.statusbar.notification.stack.MediaHeaderView;
|
||||
@@ -71,10 +74,11 @@ public class KeyguardMediaPlayer {
|
||||
private KeyguardMediaObserver mObserver;
|
||||
|
||||
@Inject
|
||||
public KeyguardMediaPlayer(Context context, @Background Executor backgroundExecutor) {
|
||||
public KeyguardMediaPlayer(Context context, MediaControllerFactory factory,
|
||||
@Background Executor backgroundExecutor) {
|
||||
mContext = context;
|
||||
mBackgroundExecutor = backgroundExecutor;
|
||||
mViewModel = new KeyguardMediaViewModel(context);
|
||||
mViewModel = new KeyguardMediaViewModel(context, factory);
|
||||
}
|
||||
|
||||
/** Binds media controls to a view hierarchy. */
|
||||
@@ -139,14 +143,16 @@ public class KeyguardMediaPlayer {
|
||||
private static final class KeyguardMediaViewModel {
|
||||
|
||||
private final Context mContext;
|
||||
private final MediaControllerFactory mMediaControllerFactory;
|
||||
private final MutableLiveData<KeyguardMedia> mMedia = new MutableLiveData<>();
|
||||
private final Object mActionsLock = new Object();
|
||||
private List<PendingIntent> mActions;
|
||||
private float mAlbumArtRadius;
|
||||
private int mAlbumArtSize;
|
||||
|
||||
KeyguardMediaViewModel(Context context) {
|
||||
KeyguardMediaViewModel(Context context, MediaControllerFactory factory) {
|
||||
mContext = context;
|
||||
mMediaControllerFactory = factory;
|
||||
loadDimens();
|
||||
}
|
||||
|
||||
@@ -162,6 +168,17 @@ public class KeyguardMediaPlayer {
|
||||
public void updateControls(NotificationEntry entry, Icon appIcon,
|
||||
MediaMetadata mediaMetadata) {
|
||||
|
||||
// Check the playback state of the media controller. If it is null, then the session was
|
||||
// probably destroyed. Don't update in this case.
|
||||
final MediaSession.Token token = entry.getSbn().getNotification().extras
|
||||
.getParcelable(Notification.EXTRA_MEDIA_SESSION);
|
||||
final MediaController controller = token != null
|
||||
? mMediaControllerFactory.create(token) : null;
|
||||
if (controller != null && controller.getPlaybackState() == null) {
|
||||
clearControls();
|
||||
return;
|
||||
}
|
||||
|
||||
// Foreground and Background colors computed from album art
|
||||
Notification notif = entry.getSbn().getNotification();
|
||||
int fgColor = notif.color;
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright (C) 2020 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.systemui.media;
|
||||
|
||||
import android.content.Context;
|
||||
import android.media.session.MediaController;
|
||||
import android.media.session.MediaSession;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
/**
|
||||
* Testable wrapper around {@link MediaController} constructor.
|
||||
*/
|
||||
public class MediaControllerFactory {
|
||||
|
||||
private final Context mContext;
|
||||
|
||||
@Inject
|
||||
public MediaControllerFactory(Context context) {
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new MediaController from a session's token.
|
||||
*
|
||||
* @param token The token for the session. This value must never be null.
|
||||
*/
|
||||
public MediaController create(MediaSession.Token token) {
|
||||
return new MediaController(mContext, token);
|
||||
}
|
||||
}
|
||||
@@ -16,8 +16,12 @@
|
||||
|
||||
package com.android.keyguard
|
||||
|
||||
import android.app.Notification
|
||||
import android.graphics.drawable.Icon
|
||||
import android.media.MediaMetadata
|
||||
import android.media.session.MediaController
|
||||
import android.media.session.MediaSession
|
||||
import android.media.session.PlaybackState
|
||||
import android.testing.AndroidTestingRunner
|
||||
import android.testing.TestableLooper
|
||||
import android.view.View
|
||||
@@ -28,7 +32,9 @@ import androidx.test.filters.SmallTest
|
||||
|
||||
import com.android.systemui.R
|
||||
import com.android.systemui.SysuiTestCase
|
||||
import com.android.systemui.statusbar.notification.collection.NotificationEntry
|
||||
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
|
||||
import com.android.systemui.media.MediaControllerFactory
|
||||
import com.android.systemui.util.concurrency.FakeExecutor
|
||||
import com.android.systemui.util.time.FakeSystemClock
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
@@ -38,6 +44,7 @@ import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mockito.Mock
|
||||
import org.mockito.Mockito.any
|
||||
import org.mockito.Mockito.mock
|
||||
import org.mockito.Mockito.verify
|
||||
import org.mockito.Mockito.`when` as whenever
|
||||
@@ -48,9 +55,12 @@ import org.mockito.Mockito.`when` as whenever
|
||||
public class KeyguardMediaPlayerTest : SysuiTestCase() {
|
||||
|
||||
private lateinit var keyguardMediaPlayer: KeyguardMediaPlayer
|
||||
@Mock private lateinit var mockMediaFactory: MediaControllerFactory
|
||||
@Mock private lateinit var mockMediaController: MediaController
|
||||
private lateinit var playbackState: PlaybackState
|
||||
private lateinit var fakeExecutor: FakeExecutor
|
||||
private lateinit var mediaMetadata: MediaMetadata.Builder
|
||||
private lateinit var entry: NotificationEntryBuilder
|
||||
private lateinit var entry: NotificationEntry
|
||||
@Mock private lateinit var mockView: View
|
||||
private lateinit var songView: TextView
|
||||
private lateinit var artistView: TextView
|
||||
@@ -70,8 +80,16 @@ public class KeyguardMediaPlayerTest : SysuiTestCase() {
|
||||
|
||||
@Before
|
||||
public fun setup() {
|
||||
playbackState = PlaybackState.Builder().run {
|
||||
build()
|
||||
}
|
||||
mockMediaController = mock(MediaController::class.java)
|
||||
whenever(mockMediaController.getPlaybackState()).thenReturn(playbackState)
|
||||
mockMediaFactory = mock(MediaControllerFactory::class.java)
|
||||
whenever(mockMediaFactory.create(any())).thenReturn(mockMediaController)
|
||||
|
||||
fakeExecutor = FakeExecutor(FakeSystemClock())
|
||||
keyguardMediaPlayer = KeyguardMediaPlayer(context, fakeExecutor)
|
||||
keyguardMediaPlayer = KeyguardMediaPlayer(context, mockMediaFactory, fakeExecutor)
|
||||
mockIcon = mock(Icon::class.java)
|
||||
|
||||
mockView = mock(View::class.java)
|
||||
@@ -81,7 +99,9 @@ public class KeyguardMediaPlayerTest : SysuiTestCase() {
|
||||
whenever<TextView>(mockView.findViewById(R.id.header_artist)).thenReturn(artistView)
|
||||
|
||||
mediaMetadata = MediaMetadata.Builder()
|
||||
entry = NotificationEntryBuilder()
|
||||
entry = NotificationEntryBuilder().build()
|
||||
entry.getSbn().getNotification().extras.putParcelable(Notification.EXTRA_MEDIA_SESSION,
|
||||
MediaSession.Token(1, null))
|
||||
|
||||
ArchTaskExecutor.getInstance().setDelegate(taskExecutor)
|
||||
|
||||
@@ -109,7 +129,7 @@ public class KeyguardMediaPlayerTest : SysuiTestCase() {
|
||||
|
||||
@Test
|
||||
public fun testUpdateControls() {
|
||||
keyguardMediaPlayer.updateControls(entry.build(), mockIcon, mediaMetadata.build())
|
||||
keyguardMediaPlayer.updateControls(entry, mockIcon, mediaMetadata.build())
|
||||
FakeExecutor.exhaustExecutors(fakeExecutor)
|
||||
verify(mockView).setVisibility(View.VISIBLE)
|
||||
}
|
||||
@@ -121,12 +141,23 @@ public class KeyguardMediaPlayerTest : SysuiTestCase() {
|
||||
verify(mockView).setVisibility(View.GONE)
|
||||
}
|
||||
|
||||
@Test
|
||||
public fun testUpdateControlsNullPlaybackState() {
|
||||
// GIVEN that the playback state is null (ie. the media session was destroyed)
|
||||
whenever(mockMediaController.getPlaybackState()).thenReturn(null)
|
||||
// WHEN updated
|
||||
keyguardMediaPlayer.updateControls(entry, mockIcon, mediaMetadata.build())
|
||||
FakeExecutor.exhaustExecutors(fakeExecutor)
|
||||
// THEN the controls are cleared (ie. visibility is set to GONE)
|
||||
verify(mockView).setVisibility(View.GONE)
|
||||
}
|
||||
|
||||
@Test
|
||||
public fun testSongName() {
|
||||
val song: String = "Song"
|
||||
mediaMetadata.putText(MediaMetadata.METADATA_KEY_TITLE, song)
|
||||
|
||||
keyguardMediaPlayer.updateControls(entry.build(), mockIcon, mediaMetadata.build())
|
||||
keyguardMediaPlayer.updateControls(entry, mockIcon, mediaMetadata.build())
|
||||
|
||||
assertThat(fakeExecutor.runAllReady()).isEqualTo(1)
|
||||
assertThat(songView.getText()).isEqualTo(song)
|
||||
@@ -137,7 +168,7 @@ public class KeyguardMediaPlayerTest : SysuiTestCase() {
|
||||
val artist: String = "Artist"
|
||||
mediaMetadata.putText(MediaMetadata.METADATA_KEY_ARTIST, artist)
|
||||
|
||||
keyguardMediaPlayer.updateControls(entry.build(), mockIcon, mediaMetadata.build())
|
||||
keyguardMediaPlayer.updateControls(entry, mockIcon, mediaMetadata.build())
|
||||
|
||||
assertThat(fakeExecutor.runAllReady()).isEqualTo(1)
|
||||
assertThat(artistView.getText()).isEqualTo(artist)
|
||||
|
||||
Reference in New Issue
Block a user