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

Commit adf522b2 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Compatible behavior for non-resizable activity (1/N)"

parents e5260cf2 0a343c34
Loading
Loading
Loading
Loading
+9 −1
Original line number Diff line number Diff line
@@ -1028,11 +1028,19 @@ public class ActivityInfo extends ComponentInfo implements Parcelable {
        }
    }

    /**
     * Returns true if the activity has maximum or minimum aspect ratio.
     * @hide
     */
    public boolean hasFixedAspectRatio() {
        return maxAspectRatio != 0 || minAspectRatio != 0;
    }

    /**
     * Returns true if the activity's orientation is fixed.
     * @hide
     */
    boolean isFixedOrientation() {
    public boolean isFixedOrientation() {
        return isFixedOrientationLandscape() || isFixedOrientationPortrait()
                || screenOrientation == SCREEN_ORIENTATION_LOCKED;
    }
+160 −33
Original line number Diff line number Diff line
@@ -75,12 +75,12 @@ import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZEABLE;
import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE;
import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION;
import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
import static android.content.pm.ActivityInfo.isFixedOrientationLandscape;
import static android.content.pm.ActivityInfo.isFixedOrientationPortrait;
import static android.content.res.Configuration.EMPTY;
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
import static android.content.res.Configuration.ORIENTATION_UNDEFINED;
import static android.content.res.Configuration.UI_MODE_TYPE_MASK;
import static android.content.res.Configuration.UI_MODE_TYPE_VR_HEADSET;
import static android.os.Build.VERSION_CODES.HONEYCOMB;
@@ -2610,10 +2610,6 @@ final class ActivityRecord extends ConfigurationContainer {
        }
    }

    int getRequestedOrientation() {
        return getOrientation();
    }

    void setRequestedOrientation(int requestedOrientation) {
        setOrientation(requestedOrientation, mayFreezeScreenLocked(app));
        mAtmService.getTaskChangeNotificationController().notifyActivityRequestedOrientationChanged(
@@ -2641,7 +2637,7 @@ final class ActivityRecord extends ConfigurationContainer {

    int getOrientation() {
        if (mAppWindowToken == null) {
            return SCREEN_ORIENTATION_UNSPECIFIED;
            return info.screenOrientation;
        }

        return mAppWindowToken.getOrientationIgnoreVisibility();
@@ -2677,25 +2673,92 @@ final class ActivityRecord extends ConfigurationContainer {
        mLastReportedConfiguration.setConfiguration(global, override);
    }

    /**
     * Get the configuration orientation by the requested screen orientation
     * ({@link ActivityInfo.ScreenOrientation}) of this activity.
     *
     * @return orientation in ({@link #ORIENTATION_LANDSCAPE}, {@link #ORIENTATION_PORTRAIT},
     *         {@link #ORIENTATION_UNDEFINED}).
     */
    int getRequestedConfigurationOrientation() {
        final int screenOrientation = getOrientation();
        if (screenOrientation == ActivityInfo.SCREEN_ORIENTATION_NOSENSOR) {
            // NOSENSOR means the display's "natural" orientation, so return that.
            final ActivityDisplay display = getDisplay();
            if (display != null && display.mDisplayContent != null) {
                return display.mDisplayContent.getNaturalOrientation();
            }
        } else if (screenOrientation == ActivityInfo.SCREEN_ORIENTATION_LOCKED) {
            // LOCKED means the activity's orientation remains unchanged, so return existing value.
            return getConfiguration().orientation;
        } else if (isFixedOrientationLandscape(screenOrientation)) {
            return ORIENTATION_LANDSCAPE;
        } else if (isFixedOrientationPortrait(screenOrientation)) {
            return ORIENTATION_PORTRAIT;
        }
        return ORIENTATION_UNDEFINED;
    }

    /**
     * Indicates the activity will keep the bounds and screen configuration when it was first
     * launched, no matter how its parent changes.
     *
     * @return {@code true} if this activity is declared as non-resizable and fixed orientation or
     *         aspect ratio.
     */
    private boolean inSizeCompatMode() {
        return !isResizeable() && (info.isFixedOrientation() || info.hasFixedAspectRatio())
                // The configuration of non-standard type should be enforced by system.
                && isActivityTypeStandard()
                && !mAtmService.mForceResizableActivities;
    }

    // TODO(b/36505427): Consider moving this method and similar ones to ConfigurationContainer.
    private void updateOverrideConfiguration() {
        final boolean inSizeCompatMode = inSizeCompatMode();
        if (inSizeCompatMode) {
            if (!matchParentBounds()) {
                // The override configuration is set only once in size compatible mode.
                return;
            }
            if (!hasProcess() && !isConfigurationCompatible(task.getConfiguration())) {
                // Don't compute when launching in fullscreen and the fixed orientation is not the
                // current orientation. It is more accurately to compute the override bounds from
                // the updated configuration after the fixed orientation is applied.
                return;
            }
        }

        computeBounds(mTmpBounds);

        if (inSizeCompatMode && mTmpBounds.isEmpty()) {
            mTmpBounds.set(task.getWindowConfiguration().getBounds());
        }
        if (mTmpBounds.equals(getRequestedOverrideBounds())) {
            // The bounds is not changed or the activity is resizable (both the 2 bounds are empty).
            return;
        }

        setBounds(mTmpBounds);

        // Bounds changed...update configuration to match.
        if (!matchParentBounds()) {
            mTmpConfig.setTo(getRequestedOverrideConfiguration());
            task.computeConfigResourceOverrides(mTmpConfig, task.getParent().getConfiguration());
        } else {
            mTmpConfig.unset();
        final Configuration overrideConfig = mTmpConfig;
        overrideConfig.unset();
        if (!mTmpBounds.isEmpty()) {
            overrideConfig.windowConfiguration.setBounds(mTmpBounds);
            if (inSizeCompatMode) {
                // Ensure the screen related fields are set. It is used to prevent activity relaunch
                // when moving between displays. For screenWidthDp and screenWidthDp, because they
                // are relative to bounds and density, they will be calculated in
                // {@link TaskRecord#computeConfigResourceOverrides} and the result will also be
                // relatively fixed.
                final Configuration srcConfig = task.getConfiguration();
                overrideConfig.colorMode = srcConfig.colorMode;
                overrideConfig.densityDpi = srcConfig.densityDpi;
                overrideConfig.screenLayout = srcConfig.screenLayout;
                // The smallest screen width is the short side of screen bounds. Because the bounds
                // and density won't be changed, smallestScreenWidthDp is also fixed.
                overrideConfig.smallestScreenWidthDp = srcConfig.smallestScreenWidthDp;
            }

        onRequestedOverrideConfigurationChanged(mTmpConfig);
        }
        onRequestedOverrideConfigurationChanged(overrideConfig);
    }

    @Override
@@ -2707,6 +2770,86 @@ final class ActivityRecord extends ConfigurationContainer {
        // layout traversals.
        mConfigurationSeq = Math.max(++mConfigurationSeq, 1);
        getResolvedOverrideConfiguration().seq = mConfigurationSeq;

        if (matchParentBounds()) {
            return;
        }

        final Configuration resolvedConfig = getResolvedOverrideConfiguration();
        if (!inSizeCompatMode()) {
            computeConfigResourceOverrides(resolvedConfig, newParentConfiguration,
                    ORIENTATION_UNDEFINED, true /* insideParentBounds */);
            return;
        }

        final Configuration displayConfig = getDisplay().getConfiguration();
        int orientation = getConfiguration().orientation;
        if (orientation != displayConfig.orientation && isConfigurationCompatible(displayConfig)) {
            // The activity is compatible to apply the orientation change or it requests different
            // fixed orientation.
            orientation = displayConfig.orientation;
        } else {
            if (resolvedConfig.windowConfiguration.getAppBounds() != null) {
                // Keep the computed resolved override configuration.
                return;
            }
            final int requestedOrientation = getRequestedConfigurationOrientation();
            if (requestedOrientation != ORIENTATION_UNDEFINED) {
                orientation = requestedOrientation;
            }
        }

        // Adjust the bounds to match the current orientation.
        if (orientation != ORIENTATION_UNDEFINED) {
            final Rect resolvedBounds = resolvedConfig.windowConfiguration.getBounds();
            final int longSide = Math.max(resolvedBounds.height(), resolvedBounds.width());
            final int shortSide = Math.min(resolvedBounds.height(), resolvedBounds.width());
            final boolean toBeLandscape = orientation == ORIENTATION_LANDSCAPE;
            final int width = toBeLandscape ? longSide : shortSide;
            final int height = toBeLandscape ? shortSide : longSide;
            // Assume the bounds is always started from zero because the size may be bigger than its
            // parent (task ~ display). The actual letterboxing will be done by surface offset.
            resolvedBounds.set(0, 0, width, height);
        }

        // In size compatible mode, activity is allowed to have larger bounds than its parent.
        computeConfigResourceOverrides(resolvedConfig, newParentConfiguration, orientation,
                false /* insideParentBounds */);
    }

    private void computeConfigResourceOverrides(Configuration inOutConfig,
            Configuration parentConfig, int orientation, boolean insideParentBounds) {
        // Set the real orientation or undefined value to ensure the output orientation won't be the
        // old value. Also reset app bounds so it will be updated according to bounds.
        inOutConfig.orientation = orientation;
        final Rect outAppBounds = inOutConfig.windowConfiguration.getAppBounds();
        if (outAppBounds != null) {
            outAppBounds.setEmpty();
        }

        // TODO(b/112288258): Remove below calculation because the position information in bounds
        // will be replaced by the offset of surface.
        final Rect appBounds = parentConfig.windowConfiguration.getAppBounds();
        if (appBounds != null) {
            final Rect outBounds = inOutConfig.windowConfiguration.getBounds();
            final int activityWidth = outBounds.width();
            final int navBarPosition = mAtmService.mWindowManager.getNavBarPosition(getDisplayId());
            if (navBarPosition == NAV_BAR_LEFT) {
                // Position the activity frame on the opposite side of the nav bar.
                outBounds.left = appBounds.right - activityWidth;
                outBounds.right = appBounds.right;
            } else if (navBarPosition == NAV_BAR_RIGHT) {
                // Position the activity frame on the opposite side of the nav bar.
                outBounds.left = 0;
                outBounds.right = activityWidth + appBounds.left;
            } else if (appBounds.width() > activityWidth) {
                // Horizontally center the frame.
                outBounds.left = appBounds.left + (appBounds.width() - activityWidth) / 2;
                outBounds.right = outBounds.left + activityWidth;
            }
        }

        task.computeConfigResourceOverrides(inOutConfig, parentConfig, insideParentBounds);
    }

    @Override
@@ -2739,8 +2882,7 @@ final class ActivityRecord extends ConfigurationContainer {

    /** Returns true if the configuration is compatible with this activity. */
    boolean isConfigurationCompatible(Configuration config) {
        final int orientation = mAppWindowToken != null
                ? getOrientation() : info.screenOrientation;
        final int orientation = getOrientation();
        if (isFixedOrientationPortrait(orientation)
                && config.orientation != ORIENTATION_PORTRAIT) {
            return false;
@@ -2822,21 +2964,6 @@ final class ActivityRecord extends ConfigurationContainer {
        // away later in StackWindowController.adjustConfigurationForBounds(). Otherwise, the app
        // bounds would end up too small.
        outBounds.set(0, 0, activityWidth + appBounds.left, activityHeight + appBounds.top);

        final int navBarPosition = mAtmService.mWindowManager.getNavBarPosition(getDisplayId());
        if (navBarPosition == NAV_BAR_LEFT) {
            // Position the activity frame on the opposite side of the nav bar.
            outBounds.left = appBounds.right - activityWidth;
            outBounds.right = appBounds.right;
        } else if (navBarPosition == NAV_BAR_RIGHT) {
            // Position the activity frame on the opposite side of the nav bar.
            outBounds.left = 0;
            outBounds.right = activityWidth + appBounds.left;
        } else {
            // Horizontally center the frame.
            outBounds.left = appBounds.left + (containingAppWidth - activityWidth) / 2;
            outBounds.right = outBounds.left + activityWidth;
        }
    }

    /**
+1 −1
Original line number Diff line number Diff line
@@ -1720,7 +1720,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
            if (r == null) {
                return ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
            }
            return r.getRequestedOrientation();
            return r.getOrientation();
        }
    }

+18 −24
Original line number Diff line number Diff line
@@ -1289,22 +1289,7 @@ class TaskRecord extends ConfigurationContainer {
                || top == null) {
            return getRequestedOverrideConfiguration().orientation;
        }
        int screenOrientation = top.getOrientation();
        if (screenOrientation == ActivityInfo.SCREEN_ORIENTATION_NOSENSOR) {
            // NOSENSOR means the display's "natural" orientation, so return that.
            ActivityDisplay display = mStack != null ? mStack.getDisplay() : null;
            if (display != null && display.mDisplayContent != null) {
                return mStack.getDisplay().mDisplayContent.getNaturalOrientation();
            }
        } else if (screenOrientation == ActivityInfo.SCREEN_ORIENTATION_LOCKED) {
            // LOCKED means the activity's orientation remains unchanged, so return existing value.
            return top.getConfiguration().orientation;
        } else if (ActivityInfo.isFixedOrientationLandscape(screenOrientation)) {
            return ORIENTATION_LANDSCAPE;
        } else if (ActivityInfo.isFixedOrientationPortrait(screenOrientation)) {
            return ORIENTATION_PORTRAIT;
        }
        return ORIENTATION_UNDEFINED;
        return top.getRequestedConfigurationOrientation();
    }

    /**
@@ -2085,6 +2070,11 @@ class TaskRecord extends ConfigurationContainer {
        return Configuration.SMALLEST_SCREEN_WIDTH_DP_UNDEFINED;
    }

    void computeConfigResourceOverrides(@NonNull Configuration inOutConfig,
            @NonNull Configuration parentConfig) {
        computeConfigResourceOverrides(inOutConfig, parentConfig, true /* insideParentBounds */);
    }

    /**
     * Calculates configuration values used by the client to get resources. This should be run
     * using app-facing bounds (bounds unmodified by animations or transient interactions).
@@ -2094,7 +2084,7 @@ class TaskRecord extends ConfigurationContainer {
     * just be inherited from the parent configuration.
     **/
    void computeConfigResourceOverrides(@NonNull Configuration inOutConfig,
            @NonNull Configuration parentConfig) {
            @NonNull Configuration parentConfig, boolean insideParentBounds) {
        int windowingMode = inOutConfig.windowConfiguration.getWindowingMode();
        if (windowingMode == WINDOWING_MODE_UNDEFINED) {
            windowingMode = parentConfig.windowConfiguration.getWindowingMode();
@@ -2112,7 +2102,7 @@ class TaskRecord extends ConfigurationContainer {
            inOutConfig.windowConfiguration.setAppBounds(bounds);
            outAppBounds = inOutConfig.windowConfiguration.getAppBounds();
        }
        if (windowingMode != WINDOWING_MODE_FREEFORM) {
        if (insideParentBounds && windowingMode != WINDOWING_MODE_FREEFORM) {
            final Rect parentAppBounds = parentConfig.windowConfiguration.getAppBounds();
            if (parentAppBounds != null && !parentAppBounds.isEmpty()) {
                outAppBounds.intersect(parentAppBounds);
@@ -2121,7 +2111,7 @@ class TaskRecord extends ConfigurationContainer {

        if (inOutConfig.screenWidthDp == Configuration.SCREEN_WIDTH_DP_UNDEFINED
                || inOutConfig.screenHeightDp == Configuration.SCREEN_HEIGHT_DP_UNDEFINED) {
            if (mStack != null) {
            if (insideParentBounds && mStack != null) {
                final DisplayInfo di = new DisplayInfo();
                mStack.getDisplay().mDisplay.getDisplayInfo(di);

@@ -2136,12 +2126,16 @@ class TaskRecord extends ConfigurationContainer {
            }

            if (inOutConfig.screenWidthDp == Configuration.SCREEN_WIDTH_DP_UNDEFINED) {
                inOutConfig.screenWidthDp = Math.min((int) (mTmpStableBounds.width() / density),
                        parentConfig.screenWidthDp);
                final int overrideScreenWidthDp = (int) (mTmpStableBounds.width() / density);
                inOutConfig.screenWidthDp = insideParentBounds
                        ? Math.min(overrideScreenWidthDp, parentConfig.screenWidthDp)
                        : overrideScreenWidthDp;
            }
            if (inOutConfig.screenHeightDp == Configuration.SCREEN_HEIGHT_DP_UNDEFINED) {
                inOutConfig.screenHeightDp = Math.min((int) (mTmpStableBounds.height() / density),
                        parentConfig.screenHeightDp);
                final int overrideScreenHeightDp = (int) (mTmpStableBounds.height() / density);
                inOutConfig.screenHeightDp = insideParentBounds
                        ? Math.min(overrideScreenHeightDp, parentConfig.screenWidthDp)
                        : overrideScreenHeightDp;
            }

            if (inOutConfig.smallestScreenWidthDp
@@ -2163,7 +2157,7 @@ class TaskRecord extends ConfigurationContainer {

        if (inOutConfig.orientation == ORIENTATION_UNDEFINED) {
            inOutConfig.orientation = (inOutConfig.screenWidthDp <= inOutConfig.screenHeightDp)
                    ? Configuration.ORIENTATION_PORTRAIT : Configuration.ORIENTATION_LANDSCAPE;
                    ? ORIENTATION_PORTRAIT : ORIENTATION_LANDSCAPE;
        }
        if (inOutConfig.screenLayout == Configuration.SCREENLAYOUT_UNDEFINED) {
            // For calculating screen layout, we need to use the non-decor inset screen area for the
+50 −5
Original line number Diff line number Diff line
@@ -183,11 +183,14 @@ public class ActivityRecordTests extends ActivityTestsBase {
                .thenReturn(navBarPosition);
        mTask.getConfiguration().windowConfiguration.setAppBounds(taskBounds);
        mActivity.info.maxAspectRatio = aspectRatio;
        mActivity.ensureActivityConfiguration(
                0 /* globalChanges */, false /* preserveWindow */);
        ensureActivityConfiguration();
        assertEquals(expectedActivityBounds, mActivity.getBounds());
    }

    private void ensureActivityConfiguration() {
        mActivity.ensureActivityConfiguration(0 /* globalChanges */, false /* preserveWindow */);
    }

    @Test
    public void testCanBeLaunchedOnDisplay() {
        mService.mSupportsMultiWindow = true;
@@ -281,7 +284,7 @@ public class ActivityRecordTests extends ActivityTestsBase {

        mActivity.mRelaunchReason = ActivityTaskManagerService.RELAUNCH_REASON_NONE;

        mActivity.ensureActivityConfiguration(0, false, false);
        ensureActivityConfiguration();

        assertEquals(ActivityTaskManagerService.RELAUNCH_REASON_WINDOWING_MODE_RESIZE,
                mActivity.mRelaunchReason);
@@ -305,7 +308,7 @@ public class ActivityRecordTests extends ActivityTestsBase {

        mActivity.mRelaunchReason = ActivityTaskManagerService.RELAUNCH_REASON_NONE;

        mActivity.ensureActivityConfiguration(0, false, false);
        ensureActivityConfiguration();

        assertEquals(ActivityTaskManagerService.RELAUNCH_REASON_FREE_RESIZE,
                mActivity.mRelaunchReason);
@@ -327,7 +330,7 @@ public class ActivityRecordTests extends ActivityTestsBase {
        mActivity.mRelaunchReason =
                ActivityTaskManagerService.RELAUNCH_REASON_WINDOWING_MODE_RESIZE;

        mActivity.ensureActivityConfiguration(0, false, false);
        ensureActivityConfiguration();

        assertEquals(ActivityTaskManagerService.RELAUNCH_REASON_NONE,
                mActivity.mRelaunchReason);
@@ -433,4 +436,46 @@ public class ActivityRecordTests extends ActivityTestsBase {
            stack.getDisplay().removeChild(stack);
        }
    }

    @Test
    public void testFixedScreenConfigurationWhenMovingToDisplay() {
        // Initialize different bounds on a new display.
        final ActivityDisplay newDisplay = addNewActivityDisplayAt(ActivityDisplay.POSITION_TOP);
        newDisplay.setBounds(0, 0, 1000, 2000);
        newDisplay.getConfiguration().densityDpi = 300;

        mTask.getWindowConfiguration().setAppBounds(mStack.getDisplay().getBounds());
        mTask.getConfiguration().densityDpi = 200;
        when(mActivity.mAppWindowToken.getOrientationIgnoreVisibility()).thenReturn(
                ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
        mActivity.info.resizeMode = ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
        mActivity.info.maxAspectRatio = 1.5f;
        ensureActivityConfiguration();
        final Rect originalBounds = new Rect(mActivity.getBounds());
        final int originalDpi = mActivity.getConfiguration().densityDpi;

        // Move the non-resizable activity to the new display.
        mStack.reparent(newDisplay, true /* onTop */, false /* displayRemoved */);
        ensureActivityConfiguration();

        assertEquals(originalBounds, mActivity.getBounds());
        assertEquals(originalDpi, mActivity.getConfiguration().densityDpi);
    }

    @Test
    public void testFixedScreenBoundsWhenDisplaySizeChanged() {
        when(mActivity.mAppWindowToken.getOrientationIgnoreVisibility()).thenReturn(
                ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
        mTask.getWindowConfiguration().setAppBounds(mStack.getDisplay().getBounds());
        mActivity.info.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
        mActivity.info.resizeMode = ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
        ensureActivityConfiguration();
        final Rect originalBounds = new Rect(mActivity.getBounds());

        // Change the size of current display.
        mStack.getDisplay().setBounds(0, 0, 1000, 2000);
        ensureActivityConfiguration();

        assertEquals(originalBounds, mActivity.getBounds());
    }
}