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

Commit 7b64adf8 authored by Kazuki Takise's avatar Kazuki Takise
Browse files

Integrate ActivityTransitionAnimator in CompatUI

This change integrates ActivityTransitionAnimator into CompatUI
so the user aspect ratio settings can be launched with the
expand animation.

- Animation invocation
The main logic is implemented in
`CompatUIController#launchUserAspectRatioSettings()`. (The no
animation version is now split out to
`launchUserAspectRatioSettings()`).

- Threading problem (`ActivityTransitionAnimator`)
Just removed the looper check. `mainExecutor` is supposed to
check if resposting to the main thead is needed, so this check
shouldn't be done by the caller. (At least conceptually. We need
to monitor performance regressions in reality.)

- View hierarchy change
Now the settings button has a fullscreen parent which is
requirement for the new animation to work.
`UserAspectRatioSettingsWindowManager` has `mLayoutParent` and
takes care of this new structure. We also need to not let the
fullscreen parent view steal input events by adding only the
button bounds to the touchable region.

- Layout lifecycle change
The layout stays alive until the end of the animation, and needs
to be cleaned up properly. To ensure this, `mIsAnimatingToHide`
is introduced to `UserAspectRatioSettingsWindowManager`, which
blocks all the calls to `release()` during the animation.
Clean-up will be handled in
`DelegateTransitionAnimatorController#onDispose()`.

- Layout bounds calculation change
Layout bounds are calculated in `getWindowLayoutParams()` and
`updateLayoutBounds()`, and this needs to be for the fullscreen
parent view of the button. Also bottom padding needs to be set
to the button.

Flag: com.android.window.flags.enable_compatui_sysui_launcher_fix
Bug: 300357441
Test: atest WMShellUnitTests:CompatUIControllerTest
Test: atest WMShellUnitTests:UserAspectRatioSettingsLayoutTest
Test: atest WMShellUnitTests:UserAspectRatioSettingsWindowManagerTest
Change-Id: I2c1eecf61bad51e293be3f6977fcc337475ce46c
parent 3142705c
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -159,6 +159,7 @@ android_library {
        "androidx.recyclerview_recyclerview",
        "com_android_launcher3_flags_lib",
        "com_android_wm_shell_flags_lib",
        "PlatformAnimationLib",
        "dagger2",
        "jsr330",
        "kotlinx-coroutines-android",
+29 −23
Original line number Diff line number Diff line
<?xml version="1.0" encoding="utf-8"?>
<!--
<?xml version="1.0" encoding="utf-8"?><!--
  ~ Copyright (C) 2023 The Android Open Source Project
  ~
  ~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,14 +13,20 @@
  ~ See the License for the specific language governing permissions and
  ~ limitations under the License.
  -->
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <com.android.wm.shell.compatui.UserAspectRatioSettingsLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/user_aspect_ratio_layout"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="vertical"
    android:gravity="bottom|end">
        android:gravity="bottom|end"
        android:layout_gravity="bottom|end">

    <include android:id="@+id/user_aspect_ratio_settings_hint"
        <include
            android:id="@+id/user_aspect_ratio_settings_hint"
            android:visibility="gone"
            android:layout_width="@dimen/compat_hint_width"
            android:layout_height="wrap_content"
@@ -39,3 +44,4 @@
            android:contentDescription="@string/user_aspect_ratio_settings_button_description" />

    </com.android.wm.shell.compatui.UserAspectRatioSettingsLayout>
</FrameLayout>
 No newline at end of file
+130 −12
Original line number Diff line number Diff line
@@ -20,10 +20,12 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.window.DesktopExperienceFlags.ENABLE_COMPAT_UI_DESKTOP_MODE_SYNCHRONIZATION_BUGFIX;

import static com.android.window.flags.Flags.enableCompatuiSysuiLauncherFix;
import static com.android.wm.shell.compatui.impl.CompatUIRequestsKt.DISPLAY_COMPAT_SHOW_RESTART_DIALOG;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityOptions;
import android.app.TaskInfo;
import android.content.ComponentName;
import android.content.Context;
@@ -40,10 +42,16 @@ import android.util.SparseArray;
import android.view.Display;
import android.view.InsetsSourceControl;
import android.view.InsetsState;
import android.view.View;
import android.view.accessibility.AccessibilityManager;
import android.window.DesktopModeFlags;
import android.window.RemoteTransition;

import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.function.TriConsumer;
import com.android.systemui.animation.ActivityTransitionAnimator;
import com.android.systemui.animation.DelegateTransitionAnimatorController;
import com.android.systemui.animation.RemoteAnimationRunnerCompat;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayController.OnDisplaysChangedListener;
@@ -62,6 +70,8 @@ import com.android.wm.shell.compatui.impl.CompatUIEvents.SizeCompatRestartButton
import com.android.wm.shell.compatui.impl.CompatUIRequests;
import com.android.wm.shell.desktopmode.DesktopUserRepositories;
import com.android.wm.shell.shared.desktopmode.DesktopState;
import com.android.wm.shell.startingsurface.SplashscreenContentDrawer;
import com.android.wm.shell.startingsurface.StartingWindowController;
import com.android.wm.shell.sysui.KeyguardChangeListener;
import com.android.wm.shell.sysui.ShellController;
import com.android.wm.shell.sysui.ShellInit;
@@ -213,6 +223,13 @@ public class CompatUIController implements OnDisplaysChangedListener,
    @NonNull
    private final DesktopState mDesktopState;

    @NonNull
    private Lazy<ActivityTransitionAnimator> mActivityTransitionAnimatorLazy;

    @NonNull
    private Lazy<StartingWindowController> mStartingWindowController;


    public CompatUIController(@NonNull Context context,
            @NonNull ShellInit shellInit,
            @NonNull ShellController shellController,
@@ -228,7 +245,9 @@ public class CompatUIController implements OnDisplaysChangedListener,
            @NonNull AccessibilityManager accessibilityManager,
            @NonNull CompatUIStatusManager compatUIStatusManager,
            @NonNull Optional<DesktopUserRepositories> desktopUserRepositories,
            @NonNull DesktopState desktopState) {
            @NonNull DesktopState desktopState,
            @NonNull Lazy<ActivityTransitionAnimator> activityTransitionAnimator,
            @NonNull Lazy<StartingWindowController> startingWindowController) {
        mContext = context;
        mShellController = shellController;
        mDisplayController = displayController;
@@ -246,6 +265,8 @@ public class CompatUIController implements OnDisplaysChangedListener,
        mCompatUIStatusManager = compatUIStatusManager;
        mDesktopUserRepositories = desktopUserRepositories;
        mDesktopState = desktopState;
        mActivityTransitionAnimatorLazy = activityTransitionAnimator;
        mStartingWindowController = startingWindowController;
        shellInit.addInitCallback(this::onInit, this);
    }

@@ -254,6 +275,26 @@ public class CompatUIController implements OnDisplaysChangedListener,
        mDisplayController.addDisplayWindowListener(this);
        mImeController.addPositionProcessor(this);
        mCompatUIShellCommandHandler.onInit();
        initActivityTransitionAnimator();
    }

    private void initActivityTransitionAnimator() {
        if (!enableCompatuiSysuiLauncherFix()) {
            return;
        }

        mActivityTransitionAnimatorLazy.get().setCallback(
                new ActivityTransitionAnimator.Callback() {
                    @Override
                    public int getBackgroundColor(@androidx.annotation.NonNull TaskInfo task) {
                        if (mStartingWindowController.get() == null) {
                            return SplashscreenContentDrawer.getSystemBGColor();
                        }

                        return mStartingWindowController.get().asStartingSurface()
                                .getBackgroundColor(task);
                    }
                });
    }

    /** Sets the callback for UI interactions. */
@@ -362,7 +403,7 @@ public class CompatUIController implements OnDisplaysChangedListener,
            // The user aspect ratio button should not be handled when a new TaskInfo is
            // sent because of a double tap or when in multi-window mode.
            if (taskInfo.getWindowingMode() != WINDOWING_MODE_FULLSCREEN) {
                if (mUserAspectRatioSettingsLayout != null) {
                if (doesUserAspectRatioSettingsLayoutExist()) {
                    mUserAspectRatioSettingsLayout.release();
                    mUserAspectRatioSettingsLayout = null;
                }
@@ -714,10 +755,15 @@ public class CompatUIController implements OnDisplaysChangedListener,
        createOrUpdateUserAspectRatioSettingsLayout(taskInfo, taskListener);
    }

    private boolean doesUserAspectRatioSettingsLayoutExist() {
        return mUserAspectRatioSettingsLayout != null
                && !mUserAspectRatioSettingsLayout.isAnimatingToHide();
    }

    private void createOrUpdateUserAspectRatioSettingsLayout(@NonNull TaskInfo taskInfo,
            @Nullable ShellTaskOrganizer.TaskListener taskListener) {
        boolean overridesShowAppHandle = mDesktopState.overridesShowAppHandle();
        if (mUserAspectRatioSettingsLayout != null) {
        if (doesUserAspectRatioSettingsLayoutExist()) {
            if (mUserAspectRatioSettingsLayout.needsToBeRecreated(taskInfo, taskListener)
                    || mIsInDesktopMode || overridesShowAppHandle) {
                mUserAspectRatioSettingsLayout.release();
@@ -757,19 +803,87 @@ public class CompatUIController implements OnDisplaysChangedListener,
            @Nullable ShellTaskOrganizer.TaskListener taskListener) {
        return new UserAspectRatioSettingsWindowManager(context, taskInfo, mSyncQueue,
                taskListener, mDisplayController.getDisplayLayout(taskInfo.displayId),
                mCompatUIHintsState, this::launchUserAspectRatioSettings, mMainExecutor,
                mCompatUIHintsState,
                new TriConsumer<>() {
                    @Override
                    public void accept(TaskInfo taskInfo,
                            ShellTaskOrganizer.TaskListener taskListener, View view) {
                        launchUserAspectRatioSettings(mContext, taskInfo, view);
                    }
                }, mMainExecutor,
                mDisappearTimeSupplier, this::hasShownUserAspectRatioSettingsButton,
                this::setHasShownUserAspectRatioSettingsButton);
    }

    private void launchUserAspectRatioSettings(
            @NonNull TaskInfo taskInfo, @NonNull ShellTaskOrganizer.TaskListener taskListener) {
        launchUserAspectRatioSettings(mContext, taskInfo);
    /** Launch the user aspect ratio settings for the package of the given task. */
    void launchUserAspectRatioSettings(
            @NonNull Context context, @NonNull TaskInfo taskInfo, @Nullable View launchableView) {
        if (!enableCompatuiSysuiLauncherFix()) {
            launchUserAspectRatioSettingsNoAnimation(context, taskInfo);
            return;
        }

    /** Launch the user aspect ratio settings for the package of the given task. */
    public static void launchUserAspectRatioSettings(
            @NonNull Context context, @NonNull TaskInfo taskInfo) {
        final ActivityTransitionAnimator.Controller delegate =
                ActivityTransitionAnimator.Controller.fromView(
                        launchableView, /* cujType */ null);
        if (delegate == null) {
            launchUserAspectRatioSettingsNoAnimation(context, taskInfo);
            return;
        }
        final ActivityTransitionAnimator.Controller controller =
                new DelegateTransitionAnimatorController(delegate) {
                    @Override
                    public void onTransitionAnimationEnd(boolean isExpandingFullyAbove) {
                        if (mUserAspectRatioSettingsLayout != null) {
                            mUserAspectRatioSettingsLayout.setIsAnimatingToHide(false);
                            mUserAspectRatioSettingsLayout.release();
                            mUserAspectRatioSettingsLayout = null;
                        }
                    }

                    @Override
                    public void onTransitionAnimationCancelled(Boolean newKeyguardOccludedState) {
                        if (mUserAspectRatioSettingsLayout != null) {
                            mUserAspectRatioSettingsLayout.setIsAnimatingToHide(false);
                            mUserAspectRatioSettingsLayout.release();
                            mUserAspectRatioSettingsLayout = null;
                        }
                    }

                };

        mUserAspectRatioSettingsLayout.setIsAnimatingToHide(true);

        mActivityTransitionAnimatorLazy.get().startIntentWithAnimation(
                controller,
                true /* animate */,
                null /* packageName */,
                false /* showOverLockscreen */,
                (animationAdapter) -> {
                    ActivityOptions options = null;
                    if (animationAdapter != null) {
                        options = ActivityOptions.makeRemoteTransition(
                                new RemoteTransition(
                                        RemoteAnimationRunnerCompat
                                                .wrap(animationAdapter.getRunner()),
                                        animationAdapter.getCallingApplication(), "SysUILaunch"));
                    }
                    return launchUserAspectRatioSettingsNoAnimation(mContext, taskInfo, options);
                }
        );
    }

    /**
     * Launches the User Aspect Ratio Settings page from a source different from the button in
     * Compat UI.
     */
    public static int launchUserAspectRatioSettingsNoAnimation(@NonNull Context context,
            @NonNull TaskInfo taskInfo) {
        return launchUserAspectRatioSettingsNoAnimation(context, taskInfo, null /* options */);
    }

    private static int launchUserAspectRatioSettingsNoAnimation(@NonNull Context context,
            @NonNull TaskInfo taskInfo, @Nullable ActivityOptions options) {
        final Intent intent = new Intent(Settings.ACTION_MANAGE_USER_ASPECT_RATIO_SETTINGS);
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
@@ -779,7 +893,11 @@ public class CompatUIController implements OnDisplaysChangedListener,
            intent.setData(packageUri);
        }
        final UserHandle userHandle = UserHandle.of(taskInfo.userId);
        context.startActivityAsUser(intent, userHandle);
        if (options == null) {
            options = ActivityOptions.makeBasic();
        }
        final Intent[] intents = {intent};
        return context.startActivitiesAsUser(intents, options.toBundle(), userHandle);
    }

    @VisibleForTesting
@@ -805,7 +923,7 @@ public class CompatUIController implements OnDisplaysChangedListener,
            mActiveReachabilityEduLayout = null;
        }

        if (mUserAspectRatioSettingsLayout != null
        if (doesUserAspectRatioSettingsLayoutExist()
                && mUserAspectRatioSettingsLayout.getTaskId() == taskId) {
            mUserAspectRatioSettingsLayout.release();
            mUserAspectRatioSettingsLayout = null;
+2 −2
Original line number Diff line number Diff line
@@ -32,14 +32,14 @@ import android.widget.ImageButton;
import android.widget.LinearLayout;
import android.widget.TextView;

import com.android.window.flags.Flags;
import com.android.systemui.animation.view.LaunchableLinearLayout;
import com.android.wm.shell.R;

/**
 * Layout for the user aspect ratio button which opens the app list page in settings
 * and allows users to change apps aspect ratio.
 */
public class UserAspectRatioSettingsLayout extends LinearLayout {
public class UserAspectRatioSettingsLayout extends LaunchableLinearLayout {

    private static final Interpolator LINEAR_INTERPOLATOR = new LinearInterpolator();

+83 −9
Original line number Diff line number Diff line
@@ -18,6 +18,8 @@ package com.android.wm.shell.compatui;

import static android.window.TaskConstants.TASK_CHILD_LAYER_COMPAT_UI;

import static com.android.window.flags.Flags.enableCompatuiSysuiLauncherFix;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.AppCompatTaskInfo;
@@ -25,13 +27,19 @@ import android.app.TaskInfo;
import android.content.Context;
import android.content.Intent;
import android.graphics.Rect;
import android.graphics.Region;
import android.os.SystemClock;
import android.view.LayoutInflater;
import android.view.SurfaceControl;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityManager;
import android.widget.FrameLayout;

import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.function.TriConsumer;
import com.android.wm.shell.R;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.DisplayLayout;
@@ -39,7 +47,6 @@ import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.compatui.CompatUIController.CompatUIHintsState;

import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
@@ -54,7 +61,7 @@ class UserAspectRatioSettingsWindowManager extends CompatUIWindowManagerAbstract

    private long mNextButtonHideTimeMs = -1L;

    private final BiConsumer<TaskInfo, ShellTaskOrganizer.TaskListener> mOnButtonClicked;
    private final TriConsumer<TaskInfo, ShellTaskOrganizer.TaskListener, View> mOnButtonClicked;

    private final Function<Integer, Integer> mDisappearTimeSupplier;

@@ -76,15 +83,20 @@ class UserAspectRatioSettingsWindowManager extends CompatUIWindowManagerAbstract
    @Nullable
    private UserAspectRatioSettingsLayout mLayout;

    @Nullable
    private FrameLayout mLayoutParent;

    // Remember the last reported states in case visibility changes due to keyguard or IME updates.
    @VisibleForTesting
    boolean mHasUserAspectRatioSettingsButton;

    boolean mIsAnimatingToHide;

    UserAspectRatioSettingsWindowManager(@NonNull Context context, @NonNull TaskInfo taskInfo,
            @NonNull SyncTransactionQueue syncQueue,
            @Nullable ShellTaskOrganizer.TaskListener taskListener,
            @NonNull DisplayLayout displayLayout, @NonNull CompatUIHintsState compatUIHintsState,
            @NonNull BiConsumer<TaskInfo, ShellTaskOrganizer.TaskListener> onButtonClicked,
            @NonNull TriConsumer<TaskInfo, ShellTaskOrganizer.TaskListener, View> onButtonClicked,
            @NonNull ShellExecutor shellExecutor,
            @NonNull Function<Integer, Integer> disappearTimeSupplier,
            @NonNull Supplier<Boolean> userAspectRatioButtonStateChecker,
@@ -114,6 +126,7 @@ class UserAspectRatioSettingsWindowManager extends CompatUIWindowManagerAbstract
    protected void removeLayout() {
        mLayoutBounds.setEmpty();
        mLayout = null;
        mLayoutParent = null;
    }

    @Override
@@ -123,18 +136,40 @@ class UserAspectRatioSettingsWindowManager extends CompatUIWindowManagerAbstract

    @Override
    protected View createLayout() {
        mLayout = inflateLayout();
        mLayoutParent = inflateLayout();
        mLayout = mLayoutParent.findViewById(R.id.user_aspect_ratio_layout);
        mLayout.inject(this);

        updateVisibilityOfViews();

        if (enableCompatuiSysuiLauncherFix()) {
            // Don't let the fullscreen parent view steal input events by adding only the button
            // bounds to the touchable region.
            mLayoutParent.getViewTreeObserver().addOnComputeInternalInsetsListener(info -> {
                if (mLayout == null || mLayout.getVisibility() != View.VISIBLE) {
                    info.touchableRegion.setEmpty();
                } else {
                    final Region touchableRegion = new Region();
                    final Rect layoutBounds = new Rect(mLayout.getLeft(), mLayout.getTop(),
                            mLayout.getRight(), mLayout.getBottom());
                    touchableRegion.op(layoutBounds, Region.Op.UNION);
                    info.touchableRegion.set(touchableRegion);
                }
                info.setTouchableInsets(
                        ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
            });

            return mLayoutParent;
        } else {
            mLayoutParent.removeView(mLayout);
            return mLayout;
        }
    }

    @VisibleForTesting
    UserAspectRatioSettingsLayout inflateLayout() {
        return (UserAspectRatioSettingsLayout) LayoutInflater.from(mContext).inflate(
                R.layout.user_aspect_ratio_settings_layout, null);
    FrameLayout inflateLayout() {
        return (FrameLayout) LayoutInflater.from(mContext)
                .inflate(R.layout.user_aspect_ratio_settings_layout, null);
    }

    @Override
@@ -156,7 +191,7 @@ class UserAspectRatioSettingsWindowManager extends CompatUIWindowManagerAbstract

    /** Called when the user aspect ratio settings button is clicked. */
    void onUserAspectRatioSettingsButtonClicked() {
        mOnButtonClicked.accept(getLastTaskInfo(), getTaskListener());
        mOnButtonClicked.accept(getLastTaskInfo(), getTaskListener(), mLayout);
    }

    /** Called when the user aspect ratio settings button is long clicked. */
@@ -170,6 +205,25 @@ class UserAspectRatioSettingsWindowManager extends CompatUIWindowManagerAbstract
        mShellExecutor.executeDelayed(this::hideUserAspectRatioButton, disappearTimeMs);
    }

    boolean isAnimatingToHide() {
        return mIsAnimatingToHide;
    }

    void setIsAnimatingToHide(boolean isAnimatingToHide) {
        mIsAnimatingToHide = isAnimatingToHide;
        if (isAnimatingToHide) {
            mLayout.setUserAspectRatioSettingsHintVisibility(false);
        }
    }

    @Override
    public void release() {
        if (mIsAnimatingToHide) {
            return;
        }
        super.release();
    }

    @Override
    @VisibleForTesting
    public void updateSurfacePosition() {
@@ -180,6 +234,15 @@ class UserAspectRatioSettingsWindowManager extends CompatUIWindowManagerAbstract
        updateSurfacePosition(mLayoutBounds.left, mLayoutBounds.top);
    }

    @Override
    protected WindowManager.LayoutParams getWindowLayoutParams() {
        if (!enableCompatuiSysuiLauncherFix()) {
            return super.getWindowLayoutParams();
        }
        final Rect taskBounds = getTaskBounds();
        return getWindowLayoutParams(taskBounds.width(), taskBounds.height());
    }

    @Override
    @VisibleForTesting
    public void updateSurfacePosition(@NonNull SurfaceControl.Transaction tx) {
@@ -218,6 +281,17 @@ class UserAspectRatioSettingsWindowManager extends CompatUIWindowManagerAbstract
        // Position of the button in the container coordinate.
        final Rect taskBounds = getTaskBounds();
        final Rect taskStableBounds = getTaskStableBounds();
        if (enableCompatuiSysuiLauncherFix()) {
            mLayoutBounds.set(taskBounds);
            ViewGroup.LayoutParams params = mLayout.getLayoutParams();
            if (params instanceof ViewGroup.MarginLayoutParams) {
                ViewGroup.MarginLayoutParams marginParams = (ViewGroup.MarginLayoutParams) params;
                marginParams.setMargins(0, 0, 0, taskBounds.bottom - taskStableBounds.bottom);
                mLayout.setLayoutParams(marginParams);
            }
            return;
        }

        final int layoutWidth = mLayout.getMeasuredWidth();
        final int layoutHeight = mLayout.getMeasuredHeight();
        final int positionX = getLayoutDirection() == View.LAYOUT_DIRECTION_RTL
Loading