From 4d8681ff6ae8f47566fdc97264fc6b3b4f3b9e9b Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Tue, 23 May 2017 16:22:08 -0700 Subject: [PATCH] Workaround to ensure that PIP activities have a visible shadow. - When the window for the activity enters PIP, update the outline provider to override the alpha of the shadow (to be opaque) to ensure that is is visible. Only applies to the task root activity. Bug: 36741700 Test: Launch YT, ensure that there is a shadow when after it enters PIP Test: go/wm-smoke Test: android.server.cts.ActivityManagerPinnedStackTests Change-Id: If089dae84e4916d3d0e7bbeb316215b46e522e05 --- core/java/android/app/Activity.java | 4 ++ core/java/android/view/Window.java | 9 ++++ .../android/internal/policy/DecorView.java | 53 +++++++++++++++++++ .../android/internal/policy/PhoneWindow.java | 7 +++ .../statusbar/phone/StatusBarWindowView.java | 4 ++ 5 files changed, 77 insertions(+) diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index b360c82374d05..3574f8d05055c 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -5790,6 +5790,7 @@ public class Activity extends ContextThemeWrapper * * @return True if this is the root activity, else false. */ + @Override public boolean isTaskRoot() { try { return ActivityManager.getService().getTaskForActivity(mToken, true) >= 0; @@ -7207,6 +7208,9 @@ public class Activity extends ContextThemeWrapper "dispatchPictureInPictureModeChanged " + this + ": " + isInPictureInPictureMode + " " + newConfig); mFragments.dispatchPictureInPictureModeChanged(isInPictureInPictureMode, newConfig); + if (mWindow != null) { + mWindow.onPictureInPictureModeChanged(isInPictureInPictureMode); + } onPictureInPictureModeChanged(isInPictureInPictureMode, newConfig); } diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java index 6dd8ecfa12e3d..0d5c0754dca77 100644 --- a/core/java/android/view/Window.java +++ b/core/java/android/view/Window.java @@ -624,6 +624,9 @@ public abstract class Window { /** Returns the current stack Id for the window. */ int getWindowStackId() throws RemoteException; + + /** Returns whether the window belongs to the task root. */ + boolean isTaskRoot(); } /** @@ -2270,6 +2273,12 @@ public abstract class Window { */ public abstract void onMultiWindowModeChanged(); + /** + * Called when the activity changes to/from picture-in-picture mode. + * @hide + */ + public abstract void onPictureInPictureModeChanged(boolean isInPictureInPictureMode); + /** * Called when the activity just relaunched. * @hide diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java index ba3aa36067d52..60fbbe9778e7e 100644 --- a/core/java/com/android/internal/policy/DecorView.java +++ b/core/java/com/android/internal/policy/DecorView.java @@ -16,6 +16,8 @@ package com.android.internal.policy; +import android.graphics.Outline; +import android.view.ViewOutlineProvider; import android.view.accessibility.AccessibilityNodeInfo; import com.android.internal.R; import com.android.internal.policy.PhoneWindow.PanelFeatureState; @@ -135,6 +137,16 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind com.android.internal.R.id.navigationBarBackground, 0 /* hideWindowFlag */); + // This is used to workaround an issue where the PiP shadow can be transparent if the window + // background is transparent + private static final ViewOutlineProvider PIP_OUTLINE_PROVIDER = new ViewOutlineProvider() { + @Override + public void getOutline(View view, Outline outline) { + outline.setRect(0, 0, view.getWidth(), view.getHeight()); + outline.setAlpha(1f); + } + }; + // Cludge to address b/22668382: Set the shadow size to the maximum so that the layer // size calculation takes the shadow size into account. We set the elevation currently // to max until the first layout command has been executed. @@ -142,6 +154,12 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind private boolean mElevationAdjustedForStack = false; + // Keeps track of the picture-in-picture mode for the view shadow + private boolean mIsInPictureInPictureMode; + + // Stores the previous outline provider prior to applying PIP_OUTLINE_PROVIDER + private ViewOutlineProvider mLastOutlineProvider; + int mDefaultOpacity = PixelFormat.OPAQUE; /** The feature ID of the panel, or -1 if this is the application's DecorView */ @@ -1404,6 +1422,41 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind } } + /** + * Overrides the view outline when the activity enters picture-in-picture to ensure that it has + * an opaque shadow even if the window background is completely transparent. This only applies + * to activities that are currently the task root. + */ + public void updatePictureInPictureOutlineProvider(boolean isInPictureInPictureMode) { + if (mIsInPictureInPictureMode == isInPictureInPictureMode) { + return; + } + + if (isInPictureInPictureMode) { + final Window.WindowControllerCallback callback = + mWindow.getWindowControllerCallback(); + if (callback != null && callback.isTaskRoot()) { + // Call super implementation directly as we don't want to save the PIP outline + // provider to be restored + super.setOutlineProvider(PIP_OUTLINE_PROVIDER); + } + } else { + // Restore the previous outline provider + if (getOutlineProvider() != mLastOutlineProvider) { + setOutlineProvider(mLastOutlineProvider); + } + } + mIsInPictureInPictureMode = isInPictureInPictureMode; + } + + @Override + public void setOutlineProvider(ViewOutlineProvider provider) { + super.setOutlineProvider(provider); + + // Save the outline provider set to ensure that we can restore when the activity leaves PiP + mLastOutlineProvider = provider; + } + private void drawableChanged() { if (mChanging) { return; diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java index 243916b4ade20..8fe9100d2011b 100644 --- a/core/java/com/android/internal/policy/PhoneWindow.java +++ b/core/java/com/android/internal/policy/PhoneWindow.java @@ -728,6 +728,13 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { } } + @Override + public void onPictureInPictureModeChanged(boolean isInPictureInPictureMode) { + if (mDecor != null) { + mDecor.updatePictureInPictureOutlineProvider(isInPictureInPictureMode); + } + } + @Override public void reportActivityRelaunched() { if (mDecor != null && mDecor.getViewRootImpl() != null) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java index 236e008d42968..f58a91b8b369b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java @@ -721,6 +721,10 @@ public class StatusBarWindowView extends FrameLayout { public void onMultiWindowModeChanged() { } + @Override + public void onPictureInPictureModeChanged(boolean isInPictureInPictureMode) { + } + @Override public void reportActivityRelaunched() { }