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