diff --git a/packages/SystemUI/res/drawable/tv_pip_play_button.xml b/packages/SystemUI/res/drawable/tv_pip_play_button.xml
new file mode 100644
index 0000000000000..fecdc095dac47
--- /dev/null
+++ b/packages/SystemUI/res/drawable/tv_pip_play_button.xml
@@ -0,0 +1,26 @@
+
+
+
+
+ -
+
+
+
+
+
+
+
diff --git a/packages/SystemUI/res/layout/tv_pip_menu.xml b/packages/SystemUI/res/layout/tv_pip_menu.xml
index 0b98d0ea71f51..1fec49e65b512 100644
--- a/packages/SystemUI/res/layout/tv_pip_menu.xml
+++ b/packages/SystemUI/res/layout/tv_pip_menu.xml
@@ -34,7 +34,7 @@
android:gravity="center"
android:clipChildren="false">
-
-
-
- mListeners = new ArrayList<>();
@@ -79,6 +83,8 @@ public class PipManager {
private Rect mRecentsFocusedPipBounds;
private boolean mInitialized;
private int mPipTaskId = TASK_ID_NO_PIP;
+ private ComponentName mPipComponentName;
+ private MediaController mPipMediaController;
private boolean mOnboardingShown;
private boolean mIsRecentsShown;
@@ -100,10 +106,15 @@ public class PipManager {
}
if (DEBUG) Log.d(TAG, "PINNED_STACK:" + stackInfo);
mPipTaskId = stackInfo.taskIds[stackInfo.taskIds.length - 1];
+ mPipComponentName = ComponentName.unflattenFromString(
+ stackInfo.taskNames[stackInfo.taskNames.length - 1]);
// Set state to overlay so we show it when the pinned stack animation ends.
mState = STATE_PIP_OVERLAY;
mCurrentPipBounds = mPipBounds;
launchPipOnboardingActivityIfNeeded();
+ mMediaSessionManager.addOnActiveSessionsChangedListener(
+ mActiveMediaSessionListener, null);
+ updateMediaController(mMediaSessionManager.getActiveSessions(null));
}
};
private final Runnable mOnTaskStackChanged = new Runnable() {
@@ -176,6 +187,13 @@ public class PipManager {
}
};
+ private final MediaSessionManager.OnActiveSessionsChangedListener mActiveMediaSessionListener =
+ new MediaSessionManager.OnActiveSessionsChangedListener() {
+ @Override
+ public void onActiveSessionsChanged(List controllers) {
+ updateMediaController(controllers);
+ }
+ };
private PipManager() { }
@@ -215,6 +233,9 @@ public class PipManager {
mContext.registerReceiver(mBroadcastReceiver, intentFilter);
mOnboardingShown = Prefs.getBoolean(
mContext, TV_PICTURE_IN_PICTURE_ONBOARDING_SHOWN, false);
+
+ mMediaSessionManager =
+ (MediaSessionManager) mContext.getSystemService(Context.MEDIA_SESSION_SERVICE);
}
/**
@@ -248,6 +269,8 @@ public class PipManager {
private void closePipInternal(boolean removePipStack) {
mState = STATE_NO_PIP;
mPipTaskId = TASK_ID_NO_PIP;
+ mPipMediaController = null;
+ mMediaSessionManager.removeOnActiveSessionsChangedListener(mActiveMediaSessionListener);
if (removePipStack) {
try {
mActivityManager.removeStack(PINNED_STACK_ID);
@@ -502,6 +525,34 @@ public class PipManager {
}
}
+ private void updateMediaController(List controllers) {
+ MediaController mediaController = null;
+ if (controllers != null && mState != STATE_NO_PIP && mPipComponentName != null) {
+ for (int i = controllers.size() - 1; i >= 0; i--) {
+ MediaController controller = controllers.get(i);
+ // We assumes that an app with PIPable activity
+ // keeps the single instance of media controller especially when PIP is on.
+ if (controller.getPackageName().equals(mPipComponentName.getPackageName())) {
+ mediaController = controller;
+ break;
+ }
+ }
+ }
+ if (mPipMediaController != mediaController) {
+ mPipMediaController = mediaController;
+ for (int i = mListeners.size() - 1; i >= 0; i--) {
+ mListeners.get(i).onMediaControllerChanged();
+ }
+ }
+ }
+
+ /**
+ * Gets the {@link android.media.session.MediaController} for the PIPed activity.
+ */
+ MediaController getMediaController() {
+ return mPipMediaController;
+ }
+
private class TaskStackListener extends ITaskStackListener.Stub {
@Override
public void onTaskStackChanged() throws RemoteException {
@@ -542,6 +593,8 @@ public class PipManager {
void onMoveToFullscreen();
/** Invoked when we are above to start resizing the Pip. */
void onPipResizeAboutToStart();
+ /** Invoked when the MediaController on PIPed activity is changed. */
+ void onMediaControllerChanged();
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/tv/pip/PipMenuActivity.java b/packages/SystemUI/src/com/android/systemui/tv/pip/PipMenuActivity.java
index a392becbcf981..fb7fa4de088f4 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/pip/PipMenuActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/tv/pip/PipMenuActivity.java
@@ -18,34 +18,49 @@ package com.android.systemui.tv.pip;
import android.app.Activity;
import android.media.session.MediaController;
+import android.media.session.PlaybackState;
import android.os.Bundle;
import android.view.View;
+import android.widget.ImageView;
+import android.widget.TextView;
import com.android.systemui.R;
+import static android.content.pm.PackageManager.FEATURE_LEANBACK;
+import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE;
+import static android.media.session.PlaybackState.ACTION_PAUSE;
+import static android.media.session.PlaybackState.ACTION_PLAY;
+
/**
* Activity to show the PIP menu to control PIP.
*/
public class PipMenuActivity extends Activity implements PipManager.Listener {
private static final String TAG = "PipMenuActivity";
- private static final boolean DEBUG = false;
private final PipManager mPipManager = PipManager.getInstance();
private MediaController mMediaController;
private View mFullButtonView;
private View mFullDescriptionView;
- private View mPlayPauseButtonView;
- private View mPlayPauseDescriptionView;
+ private View mPlayPauseView;
+ private ImageView mPlayPauseButtonImageView;
+ private TextView mPlayPauseDescriptionTextView;
private View mCloseButtonView;
private View mCloseDescriptionView;
+ private MediaController.Callback mMediaControllerCallback = new MediaController.Callback() {
+ @Override
+ public void onPlaybackStateChanged(PlaybackState state) {
+ updatePlayPauseView(state);
+ }
+ };
+
@Override
protected void onCreate(Bundle bundle) {
super.onCreate(bundle);
setContentView(R.layout.tv_pip_menu);
mPipManager.addListener(this);
- mFullButtonView = findViewById(R.id.full);
+ mFullButtonView = findViewById(R.id.full_button);
mFullDescriptionView = findViewById(R.id.full_desc);
mFullButtonView.setOnClickListener(new View.OnClickListener() {
@Override
@@ -61,22 +76,33 @@ public class PipMenuActivity extends Activity implements PipManager.Listener {
}
});
- mPlayPauseButtonView = findViewById(R.id.play_pause);
- mPlayPauseDescriptionView = findViewById(R.id.play_pause_desc);
- mPlayPauseButtonView.setOnClickListener(new View.OnClickListener() {
+ mPlayPauseView = findViewById(R.id.play_pause);
+ mPlayPauseButtonImageView = (ImageView) findViewById(R.id.play_pause_button);
+ mPlayPauseDescriptionTextView = (TextView) findViewById(R.id.play_pause_desc);
+ mPlayPauseButtonImageView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
- // TODO: Implement play/pause.
+ if (mMediaController == null || mMediaController.getPlaybackState() == null) {
+ return;
+ }
+ long actions = mMediaController.getPlaybackState().getActions();
+ int state = mMediaController.getPlaybackState().getState();
+ if (((actions & ACTION_PLAY) != 0) && !isPlaying(state)) {
+ mMediaController.getTransportControls().play();
+ } else if ((actions & ACTION_PAUSE) != 0 && isPlaying(state)) {
+ mMediaController.getTransportControls().pause();
+ }
+ // View will be updated later in {@link mMediaControllerCallback}
}
});
- mPlayPauseButtonView.setOnFocusChangeListener(new View.OnFocusChangeListener() {
+ mPlayPauseButtonImageView.setOnFocusChangeListener(new View.OnFocusChangeListener() {
@Override
public void onFocusChange(View v, boolean hasFocus) {
- mPlayPauseDescriptionView.setVisibility(hasFocus ? View.VISIBLE : View.INVISIBLE);
+ mPlayPauseDescriptionTextView.setVisibility(hasFocus ? View.VISIBLE : View.INVISIBLE);
}
});
- mCloseButtonView = findViewById(R.id.close);
+ mCloseButtonView = findViewById(R.id.close_button);
mCloseDescriptionView = findViewById(R.id.close_desc);
mCloseButtonView.setOnClickListener(new View.OnClickListener() {
@Override
@@ -91,6 +117,50 @@ public class PipMenuActivity extends Activity implements PipManager.Listener {
mCloseDescriptionView.setVisibility(hasFocus ? View.VISIBLE : View.INVISIBLE);
}
});
+ updateMediaController();
+ }
+
+ private void updateMediaController() {
+ MediaController newController = mPipManager.getMediaController();
+ if (mMediaController == newController) {
+ return;
+ }
+ if (mMediaController != null) {
+ mMediaController.unregisterCallback(mMediaControllerCallback);
+ }
+ mMediaController = newController;
+ if (mMediaController != null) {
+ mMediaController.registerCallback(mMediaControllerCallback);
+ updatePlayPauseView(mMediaController.getPlaybackState());
+ } else {
+ updatePlayPauseView(null);
+ }
+ }
+
+ private void updatePlayPauseView(PlaybackState playbackState) {
+ if (playbackState != null
+ && (playbackState.getActions() & (ACTION_PLAY | ACTION_PAUSE)) != 0) {
+ mPlayPauseView.setVisibility(View.VISIBLE);
+ if (isPlaying(playbackState.getState())) {
+ mPlayPauseButtonImageView.setImageResource(R.drawable.tv_pip_pause_button);
+ mPlayPauseDescriptionTextView.setText(R.string.pip_pause);
+ } else {
+ mPlayPauseButtonImageView.setImageResource(R.drawable.tv_pip_play_button);
+ mPlayPauseDescriptionTextView.setText(R.string.pip_play);
+ }
+ } else {
+ mPlayPauseView.setVisibility(View.GONE);
+ }
+ }
+
+ private boolean isPlaying(int state) {
+ return state == PlaybackState.STATE_BUFFERING
+ || state == PlaybackState.STATE_CONNECTING
+ || state == PlaybackState.STATE_PLAYING
+ || state == PlaybackState.STATE_FAST_FORWARDING
+ || state == PlaybackState.STATE_REWINDING
+ || state == PlaybackState.STATE_SKIPPING_TO_PREVIOUS
+ || state == PlaybackState.STATE_SKIPPING_TO_NEXT;
}
private void restorePipAndFinish() {
@@ -107,6 +177,9 @@ public class PipMenuActivity extends Activity implements PipManager.Listener {
@Override
protected void onDestroy() {
super.onDestroy();
+ if (mMediaController != null) {
+ mMediaController.unregisterCallback(mMediaControllerCallback);
+ }
mPipManager.removeListener(this);
mPipManager.resumePipResizing(
PipManager.SUSPEND_PIP_RESIZE_REASON_WAITING_FOR_MENU_ACTIVITY_FINISH);
@@ -130,6 +203,11 @@ public class PipMenuActivity extends Activity implements PipManager.Listener {
finish();
}
+ @Override
+ public void onMediaControllerChanged() {
+ updateMediaController();
+ }
+
@Override
public void onPipResizeAboutToStart() {
finish();
diff --git a/packages/SystemUI/src/com/android/systemui/tv/pip/PipOnboardingActivity.java b/packages/SystemUI/src/com/android/systemui/tv/pip/PipOnboardingActivity.java
index e5c07d2367c5d..ad45625b0f9cf 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/pip/PipOnboardingActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/tv/pip/PipOnboardingActivity.java
@@ -82,6 +82,8 @@ public class PipOnboardingActivity extends Activity implements PipManager.Listen
}
@Override
- public void onPipResizeAboutToStart() {
- }
+ public void onPipResizeAboutToStart() { }
+
+ @Override
+ public void onMediaControllerChanged() { }
}
diff --git a/packages/SystemUI/src/com/android/systemui/tv/pip/PipOverlayActivity.java b/packages/SystemUI/src/com/android/systemui/tv/pip/PipOverlayActivity.java
index cfeab6d24a95e..95d655c2f3200 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/pip/PipOverlayActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/tv/pip/PipOverlayActivity.java
@@ -103,4 +103,8 @@ public class PipOverlayActivity extends Activity implements PipManager.Listener
mPipManager.suspendPipResizing(
PipManager.SUSPEND_PIP_RESIZE_REASON_WAITING_FOR_OVERLAY_ACTIVITY_FINISH);
}
+
+ @Override
+ public void onMediaControllerChanged() {
+ }
}