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

Commit fdcb4c9f authored by Mariia Sandrikova's avatar Mariia Sandrikova
Browse files

Add orientation per-app controls.

Overrides for device manufacturers:
- OVERRIDE_UNDEFINED_ORIENTATION_TO_PORTRAIT override orientation to SCREEN_ORIENTATION_PORTRAIT
- OVERRIDE_UNDEFINED_ORIENTATION_TO_NOSENSOR override orientation to SCREEN_ORIENTATION_NOSENSOR
- OVERRIDE_LANDSCAPE_ORIENTATION_TO_REVERSE_LANDSCAPE override orientation to SCREEN_ORIENTATION_REVERSE_LANDSCAPE
- OVERRIDE_ANY_ORIENTATION allows OVERRIDE_*_ORIENTATION_TO_* to override any orientation.

PROPERTY_COMPAT_ALLOW_ORIENTATION_OVERRIDE allows app developers to opt-out from per-app overrides above.

Test: atest WmTests:LetterboxUiControllerTest
Fix: 255940284, 265452344, 265451093, 265464455, 266124927
Merged-In: I6d83170632a04e3d60461f37f13acc1de5b6a6cb
Change-Id: I6d83170632a04e3d60461f37f13acc1de5b6a6cb
parent 6f20d51b
Loading
Loading
Loading
Loading
+63 −2
Original line number Original line Diff line number Diff line
@@ -1187,6 +1187,56 @@ public class ActivityInfo extends ComponentInfo implements Parcelable {
    @Overridable
    @Overridable
    public static final long OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS = 263259275L;
    public static final long OVERRIDE_ENABLE_COMPAT_FAKE_FOCUS = 263259275L;


    // Compat framework that per-app overrides rely on only supports booleans. That's why we have
    // multiple OVERRIDE_*_ORIENTATION_* change ids below instead of just one override with
    // the integer value.

    /**
     * Enables {@link #SCREEN_ORIENTATION_PORTRAIT}. Unless OVERRIDE_ANY_ORIENTATION
     * is enabled, this override is used only when no other fixed orientation was specified by the
     * activity.
     * @hide
     */
    @ChangeId
    @Disabled
    @Overridable
    public static final long OVERRIDE_UNDEFINED_ORIENTATION_TO_PORTRAIT = 265452344L;

    /**
     * Enables {@link #SCREEN_ORIENTATION_NOSENSOR}. Unless OVERRIDE_ANY_ORIENTATION
     * is enabled, this override is used only when no other fixed orientation was specified by the
     * activity.
     * @hide
     */
    @ChangeId
    @Disabled
    @Overridable
    public static final long OVERRIDE_UNDEFINED_ORIENTATION_TO_NOSENSOR = 265451093L;

    /**
     * Enables {@link #SCREEN_ORIENTATION_REVERSE_LANDSCAPE}. Unless OVERRIDE_ANY_ORIENTATION
     * is enabled, this override is used only when activity specify landscape orientation.
     * This can help apps that assume that landscape display orientation corresponds to {@link
     * android.view.Surface#ROTATION_90}, while on some devices it can be {@link
     * android.view.Surface#ROTATION_270}.
     * @hide
     */
    @ChangeId
    @Disabled
    @Overridable
    public static final long OVERRIDE_LANDSCAPE_ORIENTATION_TO_REVERSE_LANDSCAPE = 266124927L;

    /**
     * When enabled, allows OVERRIDE_LANDSCAPE_ORIENTATION_TO_REVERSE_LANDSCAPE,
     * OVERRIDE_UNDEFINED_ORIENTATION_TO_NOSENSOR and OVERRIDE_UNDEFINED_ORIENTATION_TO_PORTRAIT
     * to override any orientation requested by the activity.
     * @hide
     */
    @ChangeId
    @Disabled
    @Overridable
    public static final long OVERRIDE_ANY_ORIENTATION = 265464455L;

    /**
    /**
     * Compares activity window layout min width/height with require space for multi window to
     * Compares activity window layout min width/height with require space for multi window to
     * determine if it can be put into multi window mode.
     * determine if it can be put into multi window mode.
@@ -1405,8 +1455,19 @@ public class ActivityInfo extends ComponentInfo implements Parcelable {
     * @hide
     * @hide
     */
     */
    public boolean isFixedOrientation() {
    public boolean isFixedOrientation() {
        return isFixedOrientationLandscape() || isFixedOrientationPortrait()
        return isFixedOrientation(screenOrientation);
                || screenOrientation == SCREEN_ORIENTATION_LOCKED;
    }

    /**
     * Returns true if the passed activity's orientation is fixed.
     * @hide
     */
    public static boolean isFixedOrientation(@ScreenOrientation int orientation) {
        return orientation == SCREEN_ORIENTATION_LOCKED
                // Orientation is fixed to natural display orientation
                || orientation == SCREEN_ORIENTATION_NOSENSOR
                || isFixedOrientationLandscape(orientation)
                || isFixedOrientationPortrait(orientation);
    }
    }


    /**
    /**
+26 −0
Original line number Original line Diff line number Diff line
@@ -989,6 +989,32 @@ public interface WindowManager extends ViewManager {
    String PROPERTY_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE =
    String PROPERTY_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE =
            "android.window.PROPERTY_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE";
            "android.window.PROPERTY_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE";


    /**
     * Activity level {@link android.content.pm.PackageManager.Property PackageManager
     * .Property} for an app to inform the system that the activity should be excluded from the
     * compatibility override for orientation set by the device manufacturer.
     *
     * <p>With this property set to {@code true} or unset, device manufacturers can override
     * orientation for the activity using their discretion to improve display compatibility.
     *
     * <p>With this property set to {@code false}, device manufactured per-app override for
     * orientation won't be applied.
     *
     * <p><b>Syntax:</b>
     * <pre>
     * &lt;activity&gt;
     *   &lt;property
     *     android:name="android.window.PROPERTY_COMPAT_ALLOW_ORIENTATION_OVERRIDE"
     *     android:value="true|false"/&gt;
     * &lt;/activity&gt;
     * </pre>
     *
     * @hide
     */
    // TODO(b/263984287): Make this public API.
    String PROPERTY_COMPAT_ALLOW_ORIENTATION_OVERRIDE =
            "android.window.PROPERTY_COMPAT_ALLOW_ORIENTATION_OVERRIDE";

    /**
    /**
     * @hide
     * @hide
     */
     */
+2 −1
Original line number Original line Diff line number Diff line
@@ -709,7 +709,8 @@ class ActivityClientController extends IActivityClientController.Stub {
        synchronized (mGlobalLock) {
        synchronized (mGlobalLock) {
            final ActivityRecord r = ActivityRecord.isInRootTaskLocked(token);
            final ActivityRecord r = ActivityRecord.isInRootTaskLocked(token);
            return r != null
            return r != null
                    ? r.getRequestedOrientation() : ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
                    ? r.getOverrideOrientation()
                    : ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
        }
        }
    }
    }


+57 −21
Original line number Original line Diff line number Diff line
@@ -1165,8 +1165,10 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
            pw.println(prefix + "mVoiceInteraction=true");
            pw.println(prefix + "mVoiceInteraction=true");
        }
        }
        pw.print(prefix); pw.print("mOccludesParent="); pw.println(mOccludesParent);
        pw.print(prefix); pw.print("mOccludesParent="); pw.println(mOccludesParent);
        pw.print(prefix); pw.print("mOrientation=");
        pw.print(prefix); pw.print("overrideOrientation=");
        pw.println(ActivityInfo.screenOrientationToString(mOrientation));
        pw.println(ActivityInfo.screenOrientationToString(getOverrideOrientation()));
        pw.print(prefix); pw.print("requestedOrientation=");
        pw.println(ActivityInfo.screenOrientationToString(super.getOverrideOrientation()));
        pw.println(prefix + "mVisibleRequested=" + mVisibleRequested
        pw.println(prefix + "mVisibleRequested=" + mVisibleRequested
                + " mVisible=" + mVisible + " mClientVisible=" + isClientVisible()
                + " mVisible=" + mVisible + " mClientVisible=" + isClientVisible()
                + ((mDeferHidingClient) ? " mDeferHidingClient=" + mDeferHidingClient : "")
                + ((mDeferHidingClient) ? " mDeferHidingClient=" + mDeferHidingClient : "")
@@ -1964,6 +1966,15 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
                    new ComponentName(info.packageName, info.targetActivity);
                    new ComponentName(info.packageName, info.targetActivity);
        }
        }


        // Don't move below setActivityType since it triggers onConfigurationChange ->
        // resolveOverrideConfiguration that requires having mLetterboxUiController initialised.
        // Don't move below setOrientation(info.screenOrientation) since it triggers
        // getOverrideOrientation that requires having mLetterboxUiController
        // initialised.
        mLetterboxUiController = new LetterboxUiController(mWmService, this);
        mCameraCompatControlEnabled = mWmService.mContext.getResources()
                .getBoolean(R.bool.config_isCameraCompatControlForStretchedIssuesEnabled);

        mTargetSdk = info.applicationInfo.targetSdkVersion;
        mTargetSdk = info.applicationInfo.targetSdkVersion;
        mShowForAllUsers = (info.flags & FLAG_SHOW_FOR_ALL_USERS) != 0;
        mShowForAllUsers = (info.flags & FLAG_SHOW_FOR_ALL_USERS) != 0;
        setOrientation(info.screenOrientation);
        setOrientation(info.screenOrientation);
@@ -2084,12 +2095,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A


        launchMode = aInfo.launchMode;
        launchMode = aInfo.launchMode;


        // Don't move below setActivityType since it triggers onConfigurationChange ->
        // resolveOverrideConfiguration that requires having mLetterboxUiController initialised.
        mLetterboxUiController = new LetterboxUiController(mWmService, this);
        mCameraCompatControlEnabled = mWmService.mContext.getResources()
                .getBoolean(R.bool.config_isCameraCompatControlForStretchedIssuesEnabled);

        setActivityType(_componentSpecified, _launchedFromUid, _intent, options, sourceRecord);
        setActivityType(_componentSpecified, _launchedFromUid, _intent, options, sourceRecord);


        immersive = (aInfo.flags & FLAG_IMMERSIVE) != 0;
        immersive = (aInfo.flags & FLAG_IMMERSIVE) != 0;
@@ -2486,7 +2491,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
            if (topAttached != null) {
            if (topAttached != null) {
                if (topAttached.isSnapshotCompatible(snapshot)
                if (topAttached.isSnapshotCompatible(snapshot)
                        // This trampoline must be the same rotation.
                        // This trampoline must be the same rotation.
                        && mDisplayContent.getDisplayRotation().rotationForOrientation(mOrientation,
                        && mDisplayContent.getDisplayRotation().rotationForOrientation(
                                getOverrideOrientation(),
                                mDisplayContent.getRotation()) == snapshot.getRotation()) {
                                mDisplayContent.getRotation()) == snapshot.getRotation()) {
                    return STARTING_WINDOW_TYPE_SNAPSHOT;
                    return STARTING_WINDOW_TYPE_SNAPSHOT;
                }
                }
@@ -7704,13 +7710,13 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
                return mLetterboxUiController.getInheritedOrientation();
                return mLetterboxUiController.getInheritedOrientation();
            }
            }
        }
        }
        if (mOrientation == SCREEN_ORIENTATION_BEHIND && task != null) {
        if (task != null && getOverrideOrientation() == SCREEN_ORIENTATION_BEHIND) {
            // We use Task here because we want to be consistent with what happens in
            // We use Task here because we want to be consistent with what happens in
            // multi-window mode where other tasks orientations are ignored.
            // multi-window mode where other tasks orientations are ignored.
            final ActivityRecord belowCandidate = task.getActivity(
            final ActivityRecord belowCandidate = task.getActivity(
                    a -> a.mOrientation != SCREEN_ORIENTATION_UNSET && !a.finishing
                    a -> a.canDefineOrientationForActivitiesAbove() /* callback */,
                            && a.mOrientation != ActivityInfo.SCREEN_ORIENTATION_BEHIND, this,
                    this /* boundary */, false /* includeBoundary */,
                    false /* includeBoundary */, true /* traverseTopToBottom */);
                    true /* traverseTopToBottom */);
            if (belowCandidate != null) {
            if (belowCandidate != null) {
                return belowCandidate.getRequestedConfigurationOrientation(forDisplay);
                return belowCandidate.getRequestedConfigurationOrientation(forDisplay);
            }
            }
@@ -7718,6 +7724,19 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
        return super.getRequestedConfigurationOrientation(forDisplay);
        return super.getRequestedConfigurationOrientation(forDisplay);
    }
    }


    /**
     * Whether this activity can be used as an orientation source for activities above with
     * {@link SCREEN_ORIENTATION_BEHIND}.
     */
    boolean canDefineOrientationForActivitiesAbove() {
        if (finishing) {
            return false;
        }
        final int overrideOrientation = getOverrideOrientation();
        return overrideOrientation != SCREEN_ORIENTATION_UNSET
                && overrideOrientation != SCREEN_ORIENTATION_BEHIND;
    }

    @Override
    @Override
    void onCancelFixedRotationTransform(int originalDisplayRotation) {
    void onCancelFixedRotationTransform(int originalDisplayRotation) {
        if (this != mDisplayContent.getLastOrientationSource()) {
        if (this != mDisplayContent.getLastOrientationSource()) {
@@ -7744,7 +7763,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
        }
        }
    }
    }


    void setRequestedOrientation(int requestedOrientation) {
    void setRequestedOrientation(@ActivityInfo.ScreenOrientation int requestedOrientation) {
        if (mLetterboxUiController.shouldIgnoreRequestedOrientation(requestedOrientation)) {
        if (mLetterboxUiController.shouldIgnoreRequestedOrientation(requestedOrientation)) {
            return;
            return;
        }
        }
@@ -7789,7 +7808,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
            // Allow app to specify orientation regardless of its visibility state if the current
            // Allow app to specify orientation regardless of its visibility state if the current
            // candidate want us to use orientation behind. I.e. the visible app on-top of this one
            // candidate want us to use orientation behind. I.e. the visible app on-top of this one
            // wants us to use the orientation of the app behind it.
            // wants us to use the orientation of the app behind it.
            return mOrientation;
            return getOverrideOrientation();
        }
        }


        // The {@link ActivityRecord} should only specify an orientation when it is not closing.
        // The {@link ActivityRecord} should only specify an orientation when it is not closing.
@@ -7797,15 +7816,31 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
        // task being started in the wrong orientation during the transition.
        // task being started in the wrong orientation during the transition.
        if (!getDisplayContent().mClosingApps.contains(this)
        if (!getDisplayContent().mClosingApps.contains(this)
                && (isVisibleRequested() || getDisplayContent().mOpeningApps.contains(this))) {
                && (isVisibleRequested() || getDisplayContent().mOpeningApps.contains(this))) {
            return mOrientation;
            return getOverrideOrientation();
        }
        }


        return SCREEN_ORIENTATION_UNSET;
        return SCREEN_ORIENTATION_UNSET;
    }
    }


    /** Returns the app's preferred orientation regardless of its currently visibility state. */
    /**
     * Returns the app's preferred orientation regardless of its current visibility state taking
     * into account orientation per-app overrides applied by the device manufacturers.
     */
    @Override
    protected int getOverrideOrientation() {
        return mLetterboxUiController.overrideOrientationIfNeeded(super.getOverrideOrientation());
    }

    /**
     * Returns the app's preferred orientation regardless of its currently visibility state. This
     * is used to return a requested value to an app if they call {@link
     * android.app.Activity#getRequestedOrientation} since {@link #getOverrideOrientation} value
     * with override can confuse an app if it's different from what they requested with {@link
     * android.app.Activity#setRequestedOrientation}.
     */
    @ActivityInfo.ScreenOrientation
    int getRequestedOrientation() {
    int getRequestedOrientation() {
        return mOrientation;
        return super.getOverrideOrientation();
    }
    }


    /**
    /**
@@ -8357,8 +8392,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
        // If orientation is respected when insets are applied, then stableBounds will be empty.
        // If orientation is respected when insets are applied, then stableBounds will be empty.
        boolean orientationRespectedWithInsets =
        boolean orientationRespectedWithInsets =
                orientationRespectedWithInsets(parentBounds, stableBounds);
                orientationRespectedWithInsets(parentBounds, stableBounds);
        if (orientationRespectedWithInsets
        if (orientationRespectedWithInsets && handlesOrientationChangeFromDescendant(
                && handlesOrientationChangeFromDescendant(mOrientation)) {
                getOverrideOrientation())) {
            // No need to letterbox because of fixed orientation. Display will handle
            // No need to letterbox because of fixed orientation. Display will handle
            // fixed-orientation requests and a display rotation is enough to respect requested
            // fixed-orientation requests and a display rotation is enough to respect requested
            // orientation with insets applied.
            // orientation with insets applied.
@@ -9003,7 +9038,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
        }
        }


        if (info.isChangeEnabled(OVERRIDE_MIN_ASPECT_RATIO_PORTRAIT_ONLY)
        if (info.isChangeEnabled(OVERRIDE_MIN_ASPECT_RATIO_PORTRAIT_ONLY)
                && !ActivityInfo.isFixedOrientationPortrait(getRequestedOrientation())) {
                && !ActivityInfo.isFixedOrientationPortrait(
                        getOverrideOrientation())) {
            return info.getMinAspectRatio();
            return info.getMinAspectRatio();
        }
        }


+3 −2
Original line number Original line Diff line number Diff line
@@ -93,7 +93,7 @@ public class DisplayArea<T extends WindowContainer> extends WindowContainer<T> {
    DisplayArea(WindowManagerService wms, Type type, String name, int featureId) {
    DisplayArea(WindowManagerService wms, Type type, String name, int featureId) {
        super(wms);
        super(wms);
        // TODO(display-area): move this up to ConfigurationContainer
        // TODO(display-area): move this up to ConfigurationContainer
        mOrientation = SCREEN_ORIENTATION_UNSET;
        setOverrideOrientation(SCREEN_ORIENTATION_UNSET);
        mType = type;
        mType = type;
        mName = name;
        mName = name;
        mFeatureId = featureId;
        mFeatureId = featureId;
@@ -165,7 +165,8 @@ public class DisplayArea<T extends WindowContainer> extends WindowContainer<T> {
        // If this is set to ignore the orientation request, we don't propagate descendant
        // If this is set to ignore the orientation request, we don't propagate descendant
        // orientation request.
        // orientation request.
        final int orientation = requestingContainer != null
        final int orientation = requestingContainer != null
                ? requestingContainer.mOrientation : SCREEN_ORIENTATION_UNSET;
                ? requestingContainer.getOverrideOrientation()
                : SCREEN_ORIENTATION_UNSET;
        return !getIgnoreOrientationRequest(orientation)
        return !getIgnoreOrientationRequest(orientation)
                && super.onDescendantOrientationChanged(requestingContainer);
                && super.onDescendantOrientationChanged(requestingContainer);
    }
    }
Loading