From f3aefb28029f5eb05835a61e86bf660cc72da432 Mon Sep 17 00:00:00 2001 From: Charles Chen Date: Wed, 2 Sep 2020 09:06:05 +0800 Subject: [PATCH] Migrate Presentation to WindowContext After [1], display context had not been regarded as an UI context even if it is derived from an UI context. Thus, we need to migrate Presentation to use WindowContext to obtain UI components accesses and also receive configuration changes from the server side. Also remove isConfigurationStillValid() related logic since now the configuration gets updated if the property of the display changes. [1]: 3c0078e80f393c2daffec5ef1a34a9c89abccd87 Test: atest PresentationTest VirtualDisplayTest KeyguardPresentationTest Bug: 167462327 Change-Id: Ib5c4a33eeba0e0a6213960300dd4c0a18047686b --- core/java/android/app/Presentation.java | 140 ++++++++---------- .../hardware/display/VirtualDisplayTest.java | 5 +- .../keyguard/KeyguardDisplayManager.java | 30 ++-- .../keyguard/KeyguardPresentationTest.java | 9 +- 4 files changed, 80 insertions(+), 104 deletions(-) diff --git a/core/java/android/app/Presentation.java b/core/java/android/app/Presentation.java index 7a18b8120d7ec..ad903642c908f 100644 --- a/core/java/android/app/Presentation.java +++ b/core/java/android/app/Presentation.java @@ -16,30 +16,28 @@ package android.app; -import static android.content.Context.DISPLAY_SERVICE; -import static android.content.Context.WINDOW_SERVICE; +import static android.view.WindowManager.LayoutParams.INVALID_WINDOW_TYPE; import static android.view.WindowManager.LayoutParams.TYPE_PRESENTATION; import static android.view.WindowManager.LayoutParams.TYPE_PRIVATE_PRESENTATION; +import android.annotation.NonNull; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.content.res.Resources; import android.hardware.display.DisplayManager; import android.hardware.display.DisplayManager.DisplayListener; -import android.os.Binder; +import android.os.Build; import android.os.Handler; -import android.os.IBinder; -import android.os.Message; -import android.util.DisplayMetrics; -import android.util.Log; +import android.os.Looper; import android.util.TypedValue; import android.view.ContextThemeWrapper; import android.view.Display; import android.view.Gravity; import android.view.Window; import android.view.WindowManager; -import android.view.WindowManagerImpl; +import android.view.WindowManager.LayoutParams.WindowType; +import com.android.internal.util.Preconditions; /** * Base class for presentations. *

@@ -153,11 +151,10 @@ import android.view.WindowManagerImpl; public class Presentation extends Dialog { private static final String TAG = "Presentation"; - private static final int MSG_CANCEL = 1; - private final Display mDisplay; private final DisplayManager mDisplayManager; - private final IBinder mToken = new Binder(); + private final Handler mHandler = new Handler(Preconditions.checkNotNull(Looper.myLooper(), + "Presentation must be constructed on a looper thread.")); /** * Creates a new presentation that is attached to the specified display @@ -179,6 +176,11 @@ public class Presentation extends Dialog { * @param outerContext The context of the application that is showing the presentation. * The presentation will create its own context (see {@link #getContext()}) based * on this context and information about the associated display. + * From {@link android.os.Build.VERSION_CODES#S}, the presentation will create its own window + * context based on this context, information about the associated display. Customizing window + * type by {@link Window#setType(int) #getWindow#setType(int)} causes the mismatch of the window + * and the created window context, which leads to + * {@link android.view.WindowManager.InvalidDisplayException} when invoking {@link #show()}. * @param display The display to which the presentation should be attached. * @param theme A style resource describing the theme to use for the window. * See @@ -187,24 +189,53 @@ public class Presentation extends Dialog { * outerContext. If 0, the default presentation theme will be used. */ public Presentation(Context outerContext, Display display, int theme) { - super(createPresentationContext(outerContext, display, theme), theme, false); + this(outerContext, display, theme, INVALID_WINDOW_TYPE); + } + + /** + * Creates a new presentation that is attached to the specified display + * using the optionally specified theme, and override the default window type for the + * presentation. + * @param outerContext The context of the application that is showing the presentation. + * The presentation will create its own context (see {@link #getContext()}) based + * on this context and information about the associated display. + * From {@link android.os.Build.VERSION_CODES#S}, the presentation will create its own window + * context based on this context, information about the associated display and the window type. + * If the window type is not specified, the presentation will choose the default type for the + * presentation. + * @param display The display to which the presentation should be attached. + * @param theme A style resource describing the theme to use for the window. + * See + * Style and Theme Resources for more information about defining and using + * styles. This theme is applied on top of the current theme in + * outerContext. If 0, the default presentation theme will be used. + * @param type Window type. + * + * @hide + */ + public Presentation(@NonNull Context outerContext, @NonNull Display display, int theme, + @WindowType int type) { + super(createPresentationContext(outerContext, display, theme, type), theme, false); mDisplay = display; - mDisplayManager = (DisplayManager)getContext().getSystemService(DISPLAY_SERVICE); - - final int windowType = - (display.getFlags() & Display.FLAG_PRIVATE) != 0 ? TYPE_PRIVATE_PRESENTATION - : TYPE_PRESENTATION; + mDisplayManager = getContext().getSystemService(DisplayManager.class); final Window w = getWindow(); final WindowManager.LayoutParams attr = w.getAttributes(); - attr.token = mToken; w.setAttributes(attr); w.setGravity(Gravity.FILL); - w.setType(windowType); + w.setType(getWindowType(type, display)); setCanceledOnTouchOutside(false); } + private static @WindowType int getWindowType(@WindowType int type, @NonNull Display display) { + if (type != INVALID_WINDOW_TYPE) { + return type; + } + return (display.getFlags() & Display.FLAG_PRIVATE) != 0 ? TYPE_PRIVATE_PRESENTATION + : TYPE_PRESENTATION; + } + /** * Gets the {@link Display} that this presentation appears on. * @@ -229,16 +260,6 @@ public class Presentation extends Dialog { protected void onStart() { super.onStart(); mDisplayManager.registerDisplayListener(mDisplayListener, mHandler); - - // Since we were not watching for display changes until just now, there is a - // chance that the display metrics have changed. If so, we will need to - // dismiss the presentation immediately. This case is expected - // to be rare but surprising, so we'll write a log message about it. - if (!isConfigurationStillValid()) { - Log.i(TAG, "Presentation is being dismissed because the " - + "display metrics have changed since it was created."); - mHandler.sendEmptyMessage(MSG_CANCEL); - } } @Override @@ -273,10 +294,6 @@ public class Presentation extends Dialog { * Called by the system when the properties of the {@link Display} to which * the presentation is attached have changed. * - * If the display metrics have changed (for example, if the display has been - * resized or rotated), then the system automatically calls - * {@link #cancel} to dismiss the presentation. - * * @see #getDisplay */ public void onDisplayChanged() { @@ -289,28 +306,16 @@ public class Presentation extends Dialog { private void handleDisplayChanged() { onDisplayChanged(); - - // We currently do not support configuration changes for presentations - // (although we could add that feature with a bit more work). - // If the display metrics have changed in any way then the current configuration - // is invalid and the application must recreate the presentation to get - // a new context. - if (!isConfigurationStillValid()) { - Log.i(TAG, "Presentation is being dismissed because the " - + "display metrics have changed since it was created."); - cancel(); - } } - private boolean isConfigurationStillValid() { - DisplayMetrics dm = new DisplayMetrics(); - mDisplay.getMetrics(dm); - return dm.equalsPhysical(getResources().getDisplayMetrics()); + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, publicAlternatives = "{@code N/A}") + private static Context createPresentationContext(Context outerContext, Display display, + int theme) { + return createPresentationContext(outerContext, display, theme, INVALID_WINDOW_TYPE); } - @UnsupportedAppUsage private static Context createPresentationContext( - Context outerContext, Display display, int theme) { + Context outerContext, Display display, int theme, @WindowType int type) { if (outerContext == null) { throw new IllegalArgumentException("outerContext must not be null"); } @@ -318,31 +323,15 @@ public class Presentation extends Dialog { throw new IllegalArgumentException("display must not be null"); } - Context displayContext = outerContext.createDisplayContext(display); + Context windowContext = outerContext.createDisplayContext(display) + .createWindowContext(getWindowType(type, display), null /* options */); if (theme == 0) { TypedValue outValue = new TypedValue(); - displayContext.getTheme().resolveAttribute( + windowContext.getTheme().resolveAttribute( com.android.internal.R.attr.presentationTheme, outValue, true); theme = outValue.resourceId; } - - // Derive the display's window manager from the outer window manager. - // We do this because the outer window manager have some extra information - // such as the parent window, which is important if the presentation uses - // an application window type. - final WindowManagerImpl outerWindowManager = - (WindowManagerImpl)outerContext.getSystemService(WINDOW_SERVICE); - final WindowManagerImpl displayWindowManager = - outerWindowManager.createPresentationWindowManager(displayContext); - return new ContextThemeWrapper(displayContext, theme) { - @Override - public Object getSystemService(String name) { - if (WINDOW_SERVICE.equals(name)) { - return displayWindowManager; - } - return super.getSystemService(name); - } - }; + return new ContextThemeWrapper(windowContext, theme); } private final DisplayListener mDisplayListener = new DisplayListener() { @@ -364,15 +353,4 @@ public class Presentation extends Dialog { } } }; - - private final Handler mHandler = new Handler() { - @Override - public void handleMessage(Message msg) { - switch (msg.what) { - case MSG_CANCEL: - cancel(); - break; - } - } - }; } diff --git a/core/tests/coretests/src/android/hardware/display/VirtualDisplayTest.java b/core/tests/coretests/src/android/hardware/display/VirtualDisplayTest.java index 0f6284d22d106..01cf311f63b1a 100644 --- a/core/tests/coretests/src/android/hardware/display/VirtualDisplayTest.java +++ b/core/tests/coretests/src/android/hardware/display/VirtualDisplayTest.java @@ -362,14 +362,12 @@ public class VirtualDisplayTest extends AndroidTestCase { private final class TestPresentation extends Presentation { private final int mColor; - private final int mWindowType; private final int mWindowFlags; public TestPresentation(Context context, Display display, int color, int windowType, int windowFlags) { - super(context, display); + super(context, display, 0 /* theme */, windowType); mColor = color; - mWindowType = windowType; mWindowFlags = windowFlags; } @@ -378,7 +376,6 @@ public class VirtualDisplayTest extends AndroidTestCase { super.onCreate(savedInstanceState); setTitle(TAG); - getWindow().setType(mWindowType); getWindow().addFlags(mWindowFlags); // Create a solid color image to use as the content of the presentation. diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java index 36d5543f1c01c..901a7360f311d 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java @@ -20,7 +20,7 @@ import static android.view.Display.DEFAULT_DISPLAY; import android.app.Presentation; import android.content.Context; import android.graphics.Color; -import android.graphics.Point; +import android.graphics.Rect; import android.hardware.display.DisplayManager; import android.media.MediaRouter; import android.media.MediaRouter.RouteInfo; @@ -127,7 +127,7 @@ public class KeyguardDisplayManager { Presentation presentation = mPresentations.get(displayId); if (presentation == null) { final Presentation newPresentation = new KeyguardPresentation(mContext, display, - mKeyguardStatusViewComponentFactory, LayoutInflater.from(mContext)); + mKeyguardStatusViewComponentFactory); newPresentation.setOnDismissListener(dialog -> { if (newPresentation.equals(mPresentations.get(displayId))) { mPresentations.remove(displayId); @@ -245,7 +245,6 @@ public class KeyguardDisplayManager { private static final int VIDEO_SAFE_REGION = 80; // Percentage of display width & height private static final int MOVE_CLOCK_TIMEOUT = 10000; // 10s private final KeyguardStatusViewComponent.Factory mKeyguardStatusViewComponentFactory; - private final LayoutInflater mLayoutInflater; private KeyguardClockSwitchController mKeyguardClockSwitchController; private View mClock; private int mUsableWidth; @@ -264,18 +263,16 @@ public class KeyguardDisplayManager { }; KeyguardPresentation(Context context, Display display, - KeyguardStatusViewComponent.Factory keyguardStatusViewComponentFactory, - LayoutInflater layoutInflater) { - super(context, display, R.style.Theme_SystemUI_KeyguardPresentation); + KeyguardStatusViewComponent.Factory keyguardStatusViewComponentFactory) { + super(context, display, R.style.Theme_SystemUI_KeyguardPresentation, + WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG); mKeyguardStatusViewComponentFactory = keyguardStatusViewComponentFactory; - mLayoutInflater = layoutInflater; - getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG); setCancelable(false); } @Override public void cancel() { - // Do not allow anything to cancel KeyguardPresetation except KeyguardDisplayManager. + // Do not allow anything to cancel KeyguardPresentation except KeyguardDisplayManager. } @Override @@ -287,14 +284,15 @@ public class KeyguardDisplayManager { protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - Point p = new Point(); - getDisplay().getSize(p); - mUsableWidth = VIDEO_SAFE_REGION * p.x/100; - mUsableHeight = VIDEO_SAFE_REGION * p.y/100; - mMarginLeft = (100 - VIDEO_SAFE_REGION) * p.x / 200; - mMarginTop = (100 - VIDEO_SAFE_REGION) * p.y / 200; + final Rect bounds = getWindow().getWindowManager().getMaximumWindowMetrics() + .getBounds(); + mUsableWidth = VIDEO_SAFE_REGION * bounds.width() / 100; + mUsableHeight = VIDEO_SAFE_REGION * bounds.height() / 100; + mMarginLeft = (100 - VIDEO_SAFE_REGION) * bounds.width() / 200; + mMarginTop = (100 - VIDEO_SAFE_REGION) * bounds.height() / 200; - setContentView(mLayoutInflater.inflate(R.layout.keyguard_presentation, null)); + setContentView(LayoutInflater.from(getContext()) + .inflate(R.layout.keyguard_presentation, null)); // Logic to make the lock screen fullscreen getWindow().getDecorView().setSystemUiVisibility( diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPresentationTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPresentationTest.java index ae159c73b99fd..62906f3656c7f 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPresentationTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPresentationTest.java @@ -20,9 +20,11 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.when; import android.content.Context; +import android.hardware.display.DisplayManager; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.util.AttributeSet; +import android.view.Display; import android.view.LayoutInflater; import android.view.View; @@ -104,9 +106,10 @@ public class KeyguardPresentationTest extends SysuiTestCase { @Test public void testInflation_doesntCrash() { - KeyguardPresentation keyguardPresentation = new KeyguardPresentation(mContext, - mContext.getDisplayNoVerify(), mKeyguardStatusViewComponentFactory, - mLayoutInflater); + final Display display = mContext.getSystemService(DisplayManager.class).getDisplay( + Display.DEFAULT_DISPLAY); + KeyguardPresentation keyguardPresentation = new KeyguardPresentation(mContext, display, + mKeyguardStatusViewComponentFactory); keyguardPresentation.onCreate(null /*savedInstanceState */); } }