Merge "PIP: Implement play/pause button in menu" into nyc-dev
This commit is contained in:
26
packages/SystemUI/res/drawable/tv_pip_play_button.xml
Normal file
26
packages/SystemUI/res/drawable/tv_pip_play_button.xml
Normal file
@@ -0,0 +1,26 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2016 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.
|
||||
-->
|
||||
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:constantSize="true">
|
||||
<item android:state_focused="true">
|
||||
<layer-list>
|
||||
<item android:drawable="@drawable/tv_pip_button_focused" />
|
||||
<item android:drawable="@drawable/ic_play_arrow_white_24dp" />
|
||||
</layer-list>
|
||||
</item>
|
||||
<item android:drawable="@drawable/ic_play_arrow_white_24dp" />
|
||||
</selector>
|
||||
@@ -34,7 +34,7 @@
|
||||
android:gravity="center"
|
||||
android:clipChildren="false">
|
||||
|
||||
<ImageView android:id="@+id/full"
|
||||
<ImageView android:id="@+id/full_button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:focusable="true"
|
||||
@@ -53,17 +53,16 @@
|
||||
android:clipChildren="false" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
<LinearLayout android:id="@+id/play_pause"
|
||||
android:layout_width="34dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="3dp"
|
||||
android:layout_marginEnd="3dp"
|
||||
android:orientation="vertical"
|
||||
android:gravity="center"
|
||||
android:visibility="gone"
|
||||
android:clipChildren="false">
|
||||
|
||||
<ImageView android:id="@+id/play_pause"
|
||||
<ImageView android:id="@+id/play_pause_button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:focusable="true"
|
||||
@@ -90,7 +89,7 @@
|
||||
android:gravity="center"
|
||||
android:clipChildren="false">
|
||||
|
||||
<ImageView android:id="@+id/close"
|
||||
<ImageView android:id="@+id/close_button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:focusable="true"
|
||||
|
||||
@@ -97,6 +97,9 @@ public class RecentsTvActivity extends Activity implements OnPreDrawListener {
|
||||
|
||||
@Override
|
||||
public void onPipResizeAboutToStart() { }
|
||||
|
||||
@Override
|
||||
public void onMediaControllerChanged() { }
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -23,11 +23,14 @@ import android.app.ActivityOptions;
|
||||
import android.app.IActivityManager;
|
||||
import android.app.ITaskStackListener;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Rect;
|
||||
import android.media.session.MediaController;
|
||||
import android.media.session.MediaSessionManager;
|
||||
import android.os.Debug;
|
||||
import android.os.Handler;
|
||||
import android.os.RemoteException;
|
||||
@@ -69,6 +72,7 @@ public class PipManager {
|
||||
|
||||
private Context mContext;
|
||||
private IActivityManager mActivityManager;
|
||||
private MediaSessionManager mMediaSessionManager;
|
||||
private int mState = STATE_NO_PIP;
|
||||
private final Handler mHandler = new Handler();
|
||||
private List<Listener> 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<MediaController> 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<MediaController> 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();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -82,6 +82,8 @@ public class PipOnboardingActivity extends Activity implements PipManager.Listen
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPipResizeAboutToStart() {
|
||||
}
|
||||
public void onPipResizeAboutToStart() { }
|
||||
|
||||
@Override
|
||||
public void onMediaControllerChanged() { }
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user