diff --git a/core/res/res/values-television/config.xml b/core/res/res/values-television/config.xml
index f24f67deee4e5..a3a311295ca15 100644
--- a/core/res/res/values-television/config.xml
+++ b/core/res/res/values-television/config.xml
@@ -31,4 +31,8 @@
is located in center. -->
"596 280 1324 690"
+
+ "1480 123 1760 303"
+
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 54f9093f07da8..9552820714d3f 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2444,6 +2444,10 @@
is located in center. -->
"0 0 300 300"
+
+ "0 0 100 100"
+
+
+
+
+
+
diff --git a/packages/SystemUI/res/drawable/ic_pause_white_24dp.xml b/packages/SystemUI/res/drawable/ic_pause_white_24dp.xml
new file mode 100644
index 0000000000000..d9a4f7bd91372
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_pause_white_24dp.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
+
diff --git a/packages/SystemUI/res/drawable/ic_play_arrow_white_24dp.xml b/packages/SystemUI/res/drawable/ic_play_arrow_white_24dp.xml
new file mode 100644
index 0000000000000..b8fa99e1dbae1
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_play_arrow_white_24dp.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
+
diff --git a/packages/SystemUI/res/layout/recents_on_tv.xml b/packages/SystemUI/res/layout/recents_on_tv.xml
index b4543bdeb57f8..94b099e5f4460 100644
--- a/packages/SystemUI/res/layout/recents_on_tv.xml
+++ b/packages/SystemUI/res/layout/recents_on_tv.xml
@@ -28,10 +28,26 @@
android:clipChildren="false"
android:clipToPadding="false"
android:descendantFocusability="beforeDescendants"
+ android:layout_gravity="center"
android:gravity="center"
android:paddingStart="@dimen/recents_tv_grid_row_padding"
android:paddingEnd="@dimen/recents_tv_grid_row_padding"
- android:focusable="true"/>
+ android:focusable="true" />
+
+
+
+
-
diff --git a/packages/SystemUI/res/layout/tv_pip_overlay.xml b/packages/SystemUI/res/layout/tv_pip_overlay.xml
index 6d9c48d44dc6c..ebd362ca29fb2 100644
--- a/packages/SystemUI/res/layout/tv_pip_overlay.xml
+++ b/packages/SystemUI/res/layout/tv_pip_overlay.xml
@@ -17,13 +17,38 @@
*/
-->
-
+ android:layout_height="match_parent">
+
+
+
+
+
+
+
+
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
index 8b4474f1cea59..4b29c2986e4d5 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
@@ -998,20 +998,4 @@ public class SystemServicesProxy {
e.printStackTrace();
}
}
-
- public void focusPinnedStack() {
- try {
- mIam.setFocusedStack(PINNED_STACK_ID);
- } catch (RemoteException e) {
- e.printStackTrace();
- }
- }
-
- public void focusHomeStack() {
- try {
- mIam.setFocusedStack(HOME_STACK_ID);
- } catch (RemoteException e) {
- e.printStackTrace();
- }
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvActivity.java b/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvActivity.java
index f3201d0346b42..9450287760ce5 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvActivity.java
@@ -18,6 +18,7 @@ package com.android.systemui.recents.tv;
import android.app.Activity;
import android.app.ActivityOptions;
import android.content.Intent;
+import android.graphics.Rect;
import android.os.Bundle;
import android.os.UserHandle;
import android.util.Log;
@@ -25,6 +26,7 @@ import android.view.KeyEvent;
import android.view.View;
import android.view.ViewTreeObserver.OnPreDrawListener;
import android.view.WindowManager;
+import android.widget.FrameLayout.LayoutParams;
import com.android.systemui.R;
import com.android.systemui.recents.Recents;
@@ -58,6 +60,7 @@ import com.android.systemui.statusbar.BaseStatusBar;
import com.android.systemui.tv.pip.PipManager;
import java.util.ArrayList;
+
/**
* The main TV recents activity started by the RecentsImpl.
*/
@@ -73,9 +76,28 @@ public class RecentsTvActivity extends Activity implements OnPreDrawListener {
private boolean mIgnoreAltTabRelease;
private RecentsTvView mRecentsView;
+ private View mPipView;
+ private View mPipShadeView;
private TaskStackHorizontalViewAdapter mTaskStackViewAdapter;
private FinishRecentsRunnable mFinishLaunchHomeRunnable;
+ private PipManager mPipManager;
+ private PipManager.Listener mPipListener = new PipManager.Listener() {
+ @Override
+ public void onPipActivityClosed() {
+ mPipView.setVisibility(View.GONE);
+ mPipShadeView.setVisibility(View.GONE);
+ }
+
+ @Override
+ public void onShowPipMenu() { }
+
+ @Override
+ public void onMoveToFullscreen() { }
+
+ @Override
+ public void onPipResizeAboutToStart() { }
+ };
/**
* A common Runnable to finish Recents by launching Home with an animation depending on the
@@ -212,6 +234,7 @@ public class RecentsTvActivity extends Activity implements OnPreDrawListener {
finish();
return;
}
+ mPipManager = PipManager.getInstance();
// Register this activity with the event bus
EventBus.getDefault().register(this, EVENT_BUS_PRIORITY);
@@ -226,7 +249,8 @@ public class RecentsTvActivity extends Activity implements OnPreDrawListener {
mRecentsView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN |
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
-
+ mPipView = findViewById(R.id.pip);
+ mPipShadeView = findViewById(R.id.pip_shade);
getWindow().getAttributes().privateFlags |=
WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY;
@@ -265,6 +289,38 @@ public class RecentsTvActivity extends Activity implements OnPreDrawListener {
// Notify that recents is now visible
SystemServicesProxy ssp = Recents.getSystemServices();
EventBus.getDefault().send(new RecentsVisibilityChangedEvent(this, ssp, true));
+
+ if (mPipManager.isPipShown()) {
+ // Place mPipView at the PIP bounds for fine tuned focus handling.
+ Rect pipBounds = mPipManager.getPipBounds();
+ LayoutParams lp = (LayoutParams) mPipView.getLayoutParams();
+ lp.width = pipBounds.width();
+ lp.height = pipBounds.height();
+ lp.leftMargin = pipBounds.left;
+ lp.topMargin = pipBounds.top;
+ mPipView.setLayoutParams(lp);
+
+ mPipView.setVisibility(View.VISIBLE);
+ mPipView.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ mPipManager.resizePinnedStack(PipManager.STATE_PIP_MENU);
+ }
+ });
+ mPipView.setOnFocusChangeListener(new View.OnFocusChangeListener() {
+ @Override
+ public void onFocusChange(View v, boolean hasFocus) {
+ mPipManager.onPipViewFocusChangedInRecents(hasFocus);
+ mPipShadeView.setVisibility(hasFocus ? View.VISIBLE : View.INVISIBLE);
+ }
+ });
+ mPipManager.addListener(mPipListener);
+ } else {
+ mPipView.setVisibility(View.GONE);
+ }
+ mPipManager.onRecentsStarted();
+ // Give focus to the recents row whenever its visible to an user.
+ mRecentsView.requestFocus();
}
@Override
@@ -277,6 +333,8 @@ public class RecentsTvActivity extends Activity implements OnPreDrawListener {
protected void onStop() {
super.onStop();
+ mPipManager.onRecentsStopped();
+ mPipManager.removeListener(mPipListener);
mIgnoreAltTabRelease = false;
// Notify that recents is now hidden
SystemServicesProxy ssp = Recents.getSystemServices();
@@ -316,18 +374,6 @@ public class RecentsTvActivity extends Activity implements OnPreDrawListener {
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
switch (keyCode) {
- case KeyEvent.KEYCODE_DPAD_UP: {
- SystemServicesProxy ssp = Recents.getSystemServices();
- PipManager.getInstance().resizePinnedStack(PipManager.STATE_PIP_MENU);
- ssp.focusPinnedStack();
- return true;
- }
- case KeyEvent.KEYCODE_DPAD_DOWN: {
- SystemServicesProxy ssp = Recents.getSystemServices();
- PipManager.getInstance().resizePinnedStack(PipManager.STATE_PIP_OVERLAY);
- ssp.focusHomeStack();
- return true;
- }
case KeyEvent.KEYCODE_DEL:
case KeyEvent.KEYCODE_FORWARD_DEL: {
EventBus.getDefault().send(new DismissFocusedTaskViewEvent());
diff --git a/packages/SystemUI/src/com/android/systemui/recents/tv/views/RecentsTvView.java b/packages/SystemUI/src/com/android/systemui/recents/tv/views/RecentsTvView.java
index b175855c0d3d4..8e768a27f7d88 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/tv/views/RecentsTvView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/tv/views/RecentsTvView.java
@@ -200,19 +200,6 @@ public class RecentsTvView extends FrameLayout {
EventBus.getDefault().unregister(this);
}
- /**
- * This is called with the full size of the window since we are handling our own insets.
- */
- @Override
- protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
- if (mTaskStackHorizontalView != null && mTaskStackHorizontalView.getVisibility() != GONE) {
- mTaskStackHorizontalView.layout(left, top, left + getMeasuredWidth(), top + getMeasuredHeight());
- }
-
- // Layout the empty view
- mEmptyView.layout(left, top, right, bottom);
- }
-
@Override
public WindowInsets onApplyWindowInsets(WindowInsets insets) {
mSystemInsets.set(insets.getSystemWindowInsets());
diff --git a/packages/SystemUI/src/com/android/systemui/tv/pip/PipManager.java b/packages/SystemUI/src/com/android/systemui/tv/pip/PipManager.java
index 0089fa9cc4522..ec49256abb237 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/pip/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/tv/pip/PipManager.java
@@ -65,17 +65,25 @@ public class PipManager {
public static final int SUSPEND_PIP_RESIZE_REASON_WAITING_FOR_OVERLAY_ACTIVITY_FINISH = 0x2;
private int mSuspendPipResizingReason;
+ private static final float SCALE_FACTOR = 1.1f;
+
private Context mContext;
private IActivityManager mActivityManager;
private int mState = STATE_NO_PIP;
private final Handler mHandler = new Handler();
private List mListeners = new ArrayList<>();
- private Rect mPipBound;
- private Rect mMenuModePipBound;
+ private Rect mCurrentPipBounds;
+ private Rect mPipBounds;
+ private Rect mMenuModePipBounds;
+ private Rect mRecentsPipBounds;
+ private Rect mRecentsFocusedPipBounds;
private boolean mInitialized;
private int mPipTaskId = TASK_ID_NO_PIP;
private boolean mOnboardingShown;
+ private boolean mIsRecentsShown;
+ private boolean mIsPipFocusedInRecent;
+
private final Runnable mOnActivityPinnedRunnable = new Runnable() {
@Override
public void run() {
@@ -83,7 +91,7 @@ public class PipManager {
try {
stackInfo = mActivityManager.getStackInfo(PINNED_STACK_ID);
if (stackInfo == null) {
- Log.w(TAG, "There is no pinned stack");
+ Log.w(TAG, "Cannot find pinned stack");
return;
}
} catch (RemoteException e) {
@@ -94,6 +102,7 @@ public class PipManager {
mPipTaskId = stackInfo.taskIds[stackInfo.taskIds.length - 1];
// Set state to overlay so we show it when the pinned stack animation ends.
mState = STATE_PIP_OVERLAY;
+ mCurrentPipBounds = mPipBounds;
launchPipOnboardingActivityIfNeeded();
}
};
@@ -133,10 +142,13 @@ public class PipManager {
private final Runnable mOnPinnedStackAnimationEnded = new Runnable() {
@Override
public void run() {
- if (mState == STATE_PIP_OVERLAY) {
- showPipOverlay();
- } else if (mState == STATE_PIP_MENU) {
- showPipMenu();
+ switch (mState) {
+ case STATE_PIP_OVERLAY:
+ showPipOverlay();
+ break;
+ case STATE_PIP_MENU:
+ showPipMenu();
+ break;
}
}
};
@@ -177,10 +189,18 @@ public class PipManager {
mInitialized = true;
mContext = context;
Resources res = context.getResources();
- mPipBound = Rect.unflattenFromString(res.getString(
+ mPipBounds = Rect.unflattenFromString(res.getString(
com.android.internal.R.string.config_defaultPictureInPictureBounds));
- mMenuModePipBound = Rect.unflattenFromString(res.getString(
+ mMenuModePipBounds = Rect.unflattenFromString(res.getString(
com.android.internal.R.string.config_centeredPictureInPictureBounds));
+ mRecentsPipBounds = Rect.unflattenFromString(res.getString(
+ com.android.internal.R.string.config_pictureInPictureBoundsInRecents));
+ float scaleBy = (SCALE_FACTOR - 1.0f) / 2;
+ mRecentsFocusedPipBounds = new Rect(
+ (int) (mRecentsPipBounds.left - scaleBy * mRecentsPipBounds.width()),
+ (int) (mRecentsPipBounds.top - scaleBy * mRecentsPipBounds.height()),
+ (int) (mRecentsPipBounds.right + scaleBy * mRecentsPipBounds.width()),
+ (int) (mRecentsPipBounds.bottom + scaleBy * mRecentsPipBounds.height()));
mActivityManager = ActivityManagerNative.getDefault();
TaskStackListener taskStackListener = new TaskStackListener();
@@ -203,7 +223,7 @@ public class PipManager {
*/
public void requestTvPictureInPicture() {
if (DEBUG) Log.d(TAG, "requestTvPictureInPicture()");
- if (!hasPipTasks()) {
+ if (!isPipShown()) {
startPip();
} else if (mState == STATE_PIP_OVERLAY) {
resizePinnedStack(STATE_PIP_MENU);
@@ -212,7 +232,7 @@ public class PipManager {
private void startPip() {
try {
- mActivityManager.moveTopActivityToPinnedStack(FULLSCREEN_WORKSPACE_STACK_ID, mPipBound);
+ mActivityManager.moveTopActivityToPinnedStack(FULLSCREEN_WORKSPACE_STACK_ID, mPipBounds);
} catch (RemoteException|IllegalArgumentException e) {
Log.e(TAG, "moveTopActivityToPinnedStack failed", e);
}
@@ -235,6 +255,9 @@ public class PipManager {
Log.e(TAG, "removeStack failed", e);
}
}
+ for (int i = mListeners.size() - 1; i >= 0; --i) {
+ mListeners.get(i).onPipActivityClosed();
+ }
}
/**
@@ -295,36 +318,99 @@ public class PipManager {
public void resizePinnedStack(int state) {
if (DEBUG) Log.d(TAG, "resizePinnedStack() state=" + state);
mState = state;
- Rect bounds;
for (int i = mListeners.size() - 1; i >= 0; --i) {
mListeners.get(i).onPipResizeAboutToStart();
}
- switch (mState) {
- case STATE_PIP_MENU:
- bounds = mMenuModePipBound;
- break;
- case STATE_NO_PIP:
- bounds = null;
- break;
- default:
- bounds = mPipBound;
- break;
- }
-
if (mSuspendPipResizingReason != 0) {
if (DEBUG) Log.d(TAG,
"resizePinnedStack() deferring mSuspendPipResizingReason=" +
mSuspendPipResizingReason);
return;
}
-
+ switch (mState) {
+ case STATE_NO_PIP:
+ mCurrentPipBounds = null;
+ break;
+ case STATE_PIP_MENU:
+ mCurrentPipBounds = mMenuModePipBounds;
+ break;
+ case STATE_PIP_OVERLAY:
+ if (mIsRecentsShown) {
+ if (mIsPipFocusedInRecent) {
+ mCurrentPipBounds = mRecentsFocusedPipBounds;
+ } else {
+ mCurrentPipBounds = mRecentsPipBounds;
+ }
+ } else {
+ mCurrentPipBounds = mPipBounds;
+ }
+ break;
+ default:
+ mCurrentPipBounds = mPipBounds;
+ break;
+ }
try {
- mActivityManager.resizeStack(PINNED_STACK_ID, bounds, true, true, true);
+ mActivityManager.resizeStack(PINNED_STACK_ID, mCurrentPipBounds, true, true, true);
} catch (RemoteException e) {
Log.e(TAG, "showPipMenu failed", e);
}
}
+ /**
+ * Returns the current PIP bound for activities to sync their UI with PIP.
+ */
+ public Rect getPipBounds() {
+ return mCurrentPipBounds;
+ }
+
+ /**
+ * Called when Recents is started.
+ * PIPed activity will be resized accordingly and overlay will show available buttons.
+ */
+ public void onRecentsStarted() {
+ mIsRecentsShown = true;
+ mIsPipFocusedInRecent = false;
+ if (mState == STATE_NO_PIP) {
+ return;
+ }
+ resizePinnedStack(STATE_PIP_OVERLAY);
+ }
+
+ /**
+ * Called when Recents is stopped.
+ * PIPed activity will be resized accordingly and overlay will hide available buttons.
+ */
+ public void onRecentsStopped() {
+ mIsRecentsShown = false;
+ mIsPipFocusedInRecent = false;
+ if (mState == STATE_NO_PIP) {
+ return;
+ }
+ resizePinnedStack(STATE_PIP_OVERLAY);
+ }
+
+ /**
+ * Returns {@code true} if recents is shown.
+ */
+ boolean isRecentsShown() {
+ return mIsRecentsShown;
+ }
+
+ /**
+ * Called when the PIP view in {@link com.android.systemui.recents.tv.RecentsTvActivity}
+ * is focused.
+ * This only resizes pinned stack so it looks like it's in Recents.
+ * This should be called only by {@link com.android.systemui.recents.tv.RecentsTvActivity}.
+ */
+ public void onPipViewFocusChangedInRecents(boolean hasFocus) {
+ mIsPipFocusedInRecent = hasFocus;
+ if (mState != STATE_PIP_OVERLAY) {
+ Log.w(TAG, "There is no pinned stack to handle focus change.");
+ return;
+ }
+ resizePinnedStack(STATE_PIP_OVERLAY);
+ }
+
/**
* Shows PIP menu UI by launching {@link PipMenuActivity}. It also locates the pinned
* stack to the centered PIP bound {@link com.android.internal.R.string
@@ -362,6 +448,13 @@ public class PipManager {
}
}
+ /**
+ * Returns {@code true} if PIP is shown.
+ */
+ public boolean isPipShown() {
+ return hasPipTasks();
+ }
+
private boolean hasPipTasks() {
try {
StackInfo stackInfo = mActivityManager.getStackInfo(PINNED_STACK_ID);
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 7b1764f8d4bdd..6f246918fde00 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/pip/PipOverlayActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/tv/pip/PipOverlayActivity.java
@@ -35,6 +35,7 @@ public class PipOverlayActivity extends Activity implements PipManager.Listener
private final PipManager mPipManager = PipManager.getInstance();
private final Handler mHandler = new Handler();
private View mGuideOverlayView;
+ private View mGuideButtonsView;
private final Runnable mHideGuideOverlayRunnable = new Runnable() {
public void run() {
mGuideOverlayView.setVisibility(View.INVISIBLE);
@@ -46,13 +47,21 @@ public class PipOverlayActivity extends Activity implements PipManager.Listener
super.onCreate(bundle);
setContentView(R.layout.tv_pip_overlay);
mGuideOverlayView = findViewById(R.id.guide_overlay);
+ mGuideButtonsView = findViewById(R.id.guide_buttons);
mPipManager.addListener(this);
}
@Override
protected void onResume() {
super.onResume();
- mGuideOverlayView.setVisibility(View.VISIBLE);
+ // TODO: Implement animation for this
+ if (!mPipManager.isRecentsShown()) {
+ mGuideOverlayView.setVisibility(View.VISIBLE);
+ mGuideButtonsView.setVisibility(View.INVISIBLE);
+ } else {
+ mGuideOverlayView.setVisibility(View.INVISIBLE);
+ mGuideButtonsView.setVisibility(View.VISIBLE);
+ }
mHandler.removeCallbacks(mHideGuideOverlayRunnable);
mHandler.postDelayed(mHideGuideOverlayRunnable, SHOW_GUIDE_OVERLAY_VIEW_DURATION_MS);
}