Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f3aefb28 authored by Charles Chen's avatar Charles Chen
Browse files

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]: 3c0078e8

Test: atest PresentationTest VirtualDisplayTest KeyguardPresentationTest
Bug: 167462327

Change-Id: Ib5c4a33eeba0e0a6213960300dd4c0a18047686b
parent 41a7b5ae
Loading
Loading
Loading
Loading
+59 −81
Original line number Diff line number Diff line
@@ -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.
 * <p>
@@ -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 <a href="{@docRoot}guide/topics/resources/available-resources.html#stylesandthemes">
@@ -187,24 +189,53 @@ public class Presentation extends Dialog {
     * <var>outerContext</var>.  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);
    }

        mDisplay = display;
        mDisplayManager = (DisplayManager)getContext().getSystemService(DISPLAY_SERVICE);
    /**
     * 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 <a href="{@docRoot}guide/topics/resources/available-resources.html#stylesandthemes">
     * Style and Theme Resources</a> for more information about defining and using
     * styles.  This theme is applied on top of the current theme in
     * <var>outerContext</var>.  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);

        final int windowType =
                (display.getFlags() & Display.FLAG_PRIVATE) != 0 ? TYPE_PRIVATE_PRESENTATION
                        : TYPE_PRESENTATION;
        mDisplay = display;
        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;
            }
        }
    };
}
+1 −4
Original line number Diff line number Diff line
@@ -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.
+14 −16
Original line number Diff line number Diff line
@@ -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(
+6 −3
Original line number Diff line number Diff line
@@ -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 */);
    }
}