Merge "PIP: Implement play/pause button in menu" into nyc-dev

This commit is contained in:
Dongwon Kang
2016-02-29 19:00:01 +00:00
committed by Android (Google) Code Review
7 changed files with 183 additions and 18 deletions

View 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>

View File

@@ -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"

View File

@@ -97,6 +97,9 @@ public class RecentsTvActivity extends Activity implements OnPreDrawListener {
@Override
public void onPipResizeAboutToStart() { }
@Override
public void onMediaControllerChanged() { }
};
/**

View File

@@ -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();
}
/**

View File

@@ -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();

View File

@@ -82,6 +82,8 @@ public class PipOnboardingActivity extends Activity implements PipManager.Listen
}
@Override
public void onPipResizeAboutToStart() {
}
public void onPipResizeAboutToStart() { }
@Override
public void onMediaControllerChanged() { }
}

View File

@@ -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() {
}
}