diff options
Diffstat (limited to 'libs')
50 files changed, 717 insertions, 472 deletions
diff --git a/libs/WindowManager/Shell/res/drawable/pip_ic_move_down.xml b/libs/WindowManager/Shell/res/drawable/pip_ic_move_down.xml deleted file mode 100644 index d8f356164358..000000000000 --- a/libs/WindowManager/Shell/res/drawable/pip_ic_move_down.xml +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - ~ Copyright (C) 2021 The Android Open Source Project - ~ - ~ Licensed under the Apache License, Version 2.0 (the "License"); - ~ you may not use this file except in compliance with the License. - ~ You may obtain a copy of the License at - ~ - ~ http://www.apache.org/licenses/LICENSE-2.0 - ~ - ~ Unless required by applicable law or agreed to in writing, software - ~ distributed under the License is distributed on an "AS IS" BASIS, - ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - ~ See the License for the specific language governing permissions and - ~ limitations under the License. - --> -<vector xmlns:android="http://schemas.android.com/apk/res/android" - android:width="24dp" - android:height="24dp" - android:viewportWidth="24" - android:viewportHeight="24"> - <path - android:fillColor="@color/tv_pip_menu_focus_border" - android:pathData="M7,10l5,5 5,-5H7z"/> -</vector>
\ No newline at end of file diff --git a/libs/WindowManager/Shell/res/drawable/pip_ic_move_left.xml b/libs/WindowManager/Shell/res/drawable/pip_ic_move_left.xml deleted file mode 100644 index 3e0011c65942..000000000000 --- a/libs/WindowManager/Shell/res/drawable/pip_ic_move_left.xml +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - ~ Copyright (C) 2021 The Android Open Source Project - ~ - ~ Licensed under the Apache License, Version 2.0 (the "License"); - ~ you may not use this file except in compliance with the License. - ~ You may obtain a copy of the License at - ~ - ~ http://www.apache.org/licenses/LICENSE-2.0 - ~ - ~ Unless required by applicable law or agreed to in writing, software - ~ distributed under the License is distributed on an "AS IS" BASIS, - ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - ~ See the License for the specific language governing permissions and - ~ limitations under the License. - --> -<vector xmlns:android="http://schemas.android.com/apk/res/android" - android:width="24dp" - android:height="24dp" - android:viewportWidth="24" - android:viewportHeight="24"> - <path - android:fillColor="@color/tv_pip_menu_focus_border" - android:pathData="M14,7l-5,5 5,5V7z"/> -</vector>
\ No newline at end of file diff --git a/libs/WindowManager/Shell/res/drawable/pip_ic_move_right.xml b/libs/WindowManager/Shell/res/drawable/pip_ic_move_right.xml deleted file mode 100644 index f6b3c72e3cb5..000000000000 --- a/libs/WindowManager/Shell/res/drawable/pip_ic_move_right.xml +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - ~ Copyright (C) 2021 The Android Open Source Project - ~ - ~ Licensed under the Apache License, Version 2.0 (the "License"); - ~ you may not use this file except in compliance with the License. - ~ You may obtain a copy of the License at - ~ - ~ http://www.apache.org/licenses/LICENSE-2.0 - ~ - ~ Unless required by applicable law or agreed to in writing, software - ~ distributed under the License is distributed on an "AS IS" BASIS, - ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - ~ See the License for the specific language governing permissions and - ~ limitations under the License. - --> -<vector xmlns:android="http://schemas.android.com/apk/res/android" - android:width="24dp" - android:height="24dp" - android:viewportWidth="24" - android:viewportHeight="24"> - <path - android:fillColor="@color/tv_pip_menu_focus_border" - android:pathData="M10,17l5,-5 -5,-5v10z"/> -</vector>
\ No newline at end of file diff --git a/libs/WindowManager/Shell/res/drawable/pip_ic_move_up.xml b/libs/WindowManager/Shell/res/drawable/pip_ic_move_up.xml deleted file mode 100644 index 1a3446249573..000000000000 --- a/libs/WindowManager/Shell/res/drawable/pip_ic_move_up.xml +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - ~ Copyright (C) 2021 The Android Open Source Project - ~ - ~ Licensed under the Apache License, Version 2.0 (the "License"); - ~ you may not use this file except in compliance with the License. - ~ You may obtain a copy of the License at - ~ - ~ http://www.apache.org/licenses/LICENSE-2.0 - ~ - ~ Unless required by applicable law or agreed to in writing, software - ~ distributed under the License is distributed on an "AS IS" BASIS, - ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - ~ See the License for the specific language governing permissions and - ~ limitations under the License. - --> -<vector xmlns:android="http://schemas.android.com/apk/res/android" - android:width="24dp" - android:height="24dp" - android:viewportWidth="24" - android:viewportHeight="24"> - <path - android:fillColor="@color/tv_pip_menu_focus_border" - android:pathData="M7,14l5,-5 5,5H7z"/> -</vector>
\ No newline at end of file diff --git a/libs/WindowManager/Shell/res/layout/tv_pip_menu.xml b/libs/WindowManager/Shell/res/layout/tv_pip_menu.xml index ab64f9e359b0..82a358cf68d6 100644 --- a/libs/WindowManager/Shell/res/layout/tv_pip_menu.xml +++ b/libs/WindowManager/Shell/res/layout/tv_pip_menu.xml @@ -104,9 +104,7 @@ android:layout_centerHorizontal="true" android:layout_alignParentTop="true" android:alpha="0" - android:contentDescription="@string/a11y_action_pip_move_up" - android:elevation="@dimen/pip_menu_arrow_elevation" - android:src="@drawable/pip_ic_move_up" /> + android:contentDescription="@string/a11y_action_pip_move_up"/> <ImageView android:id="@+id/tv_pip_menu_arrow_right" @@ -115,9 +113,7 @@ android:layout_centerVertical="true" android:layout_alignParentRight="true" android:alpha="0" - android:contentDescription="@string/a11y_action_pip_move_right" - android:elevation="@dimen/pip_menu_arrow_elevation" - android:src="@drawable/pip_ic_move_right" /> + android:contentDescription="@string/a11y_action_pip_move_right"/> <ImageView android:id="@+id/tv_pip_menu_arrow_down" @@ -126,9 +122,7 @@ android:layout_centerHorizontal="true" android:layout_alignParentBottom="true" android:alpha="0" - android:contentDescription="@string/a11y_action_pip_move_down" - android:elevation="@dimen/pip_menu_arrow_elevation" - android:src="@drawable/pip_ic_move_down" /> + android:contentDescription="@string/a11y_action_pip_move_down"/> <ImageView android:id="@+id/tv_pip_menu_arrow_left" @@ -137,7 +131,5 @@ android:layout_centerVertical="true" android:layout_alignParentLeft="true" android:alpha="0" - android:contentDescription="@string/a11y_action_pip_move_left" - android:elevation="@dimen/pip_menu_arrow_elevation" - android:src="@drawable/pip_ic_move_left" /> + android:contentDescription="@string/a11y_action_pip_move_left"/> </RelativeLayout> diff --git a/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml b/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml index 7814b7d38fed..6d19e55217f6 100644 --- a/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml +++ b/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml @@ -79,7 +79,7 @@ <string name="notification_bubble_title" msgid="6082910224488253378">"Bulle"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"Gérer"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bulle ignorée."</string> - <string name="restart_button_description" msgid="6712141648865547958">"Touchez pour redémarrer cette application afin d\'obtenir un meilleur affichage."</string> + <string name="restart_button_description" msgid="6712141648865547958">"Pour obtenir un meilleur affichage, touchez pour redémarrer cette application."</string> <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Problèmes d\'appareil photo?\nTouchez pour réajuster"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Problème non résolu?\nTouchez pour rétablir"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Aucun problème d\'appareil photo? Touchez pour ignorer."</string> diff --git a/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml b/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml index ed0cdb61dacf..04ee54046439 100644 --- a/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml +++ b/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml @@ -79,7 +79,7 @@ <string name="notification_bubble_title" msgid="6082910224488253378">"Balão"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"Gerir"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Balão ignorado."</string> - <string name="restart_button_description" msgid="6712141648865547958">"Toque para reiniciar esta app e ficar com uma melhor visão."</string> + <string name="restart_button_description" msgid="6712141648865547958">"Toque para reiniciar esta app e ver melhor."</string> <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Problemas com a câmara?\nToque aqui para reajustar"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Não foi corrigido?\nToque para reverter"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nenhum problema com a câmara? Toque para ignorar."</string> diff --git a/libs/WindowManager/Shell/res/values-tvdpi/dimen.xml b/libs/WindowManager/Shell/res/values-tvdpi/dimen.xml index adbf65648dd1..fd825639f1e8 100644 --- a/libs/WindowManager/Shell/res/values-tvdpi/dimen.xml +++ b/libs/WindowManager/Shell/res/values-tvdpi/dimen.xml @@ -33,8 +33,8 @@ <!-- outer space minus border width --> <dimen name="pip_menu_outer_space_frame">20dp</dimen> - <dimen name="pip_menu_arrow_size">24dp</dimen> - <dimen name="pip_menu_arrow_elevation">5dp</dimen> + <dimen name="pip_menu_arrow_size">12dp</dimen> + <dimen name="pip_menu_arrow_elevation">1dp</dimen> <dimen name="pip_menu_elevation_no_menu">1dp</dimen> <dimen name="pip_menu_elevation_move_menu">7dp</dimen> diff --git a/libs/WindowManager/Shell/res/values/colors_tv.xml b/libs/WindowManager/Shell/res/values/colors_tv.xml index e6933ca3fce6..5f7fb12c3002 100644 --- a/libs/WindowManager/Shell/res/values/colors_tv.xml +++ b/libs/WindowManager/Shell/res/values/colors_tv.xml @@ -27,6 +27,10 @@ <color name="tv_pip_menu_focus_border">#E8EAED</color> <color name="tv_pip_menu_dim_layer">#990E0E0F</color> <color name="tv_pip_menu_background">#1E232C</color> + <!-- Normally, the arrow color would be the same as the focus border color. But due to + optical illusion that looks too dark on the screen. That's why we define a separate + (lighter) arrow color. --> + <color name="tv_pip_menu_arrow_color">#F1F3F4</color> <color name="tv_pip_edu_text">#99D2E3FC</color> <color name="tv_pip_edu_text_home_icon">#D2E3FC</color> diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimation.java index 53a438ec2fde..c98090638010 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimation.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimation.java @@ -54,10 +54,42 @@ public interface BackAnimation { void setTriggerBack(boolean triggerBack); /** - * Sets the threshold values that defining edge swipe behavior. - * @param progressThreshold the max threshold to keep linear progressing back animation. + * Sets the threshold values that define edge swipe behavior.<br> + * <br> + * <h1>How does {@code nonLinearFactor} work?</h1> + * <pre> + * screen screen screen + * width width width + * |——————| |————————————| |————————————————————| + * A B A B C A + * 1 +——————+—————+ 1 +————————————+ 1 +————————————+———————+ + * | / | | —/| | | —————/| + * | / | | —/ | | ——/ | + * | / | | —/ | | ——/ | | + * | / | | —/ | | ——/ | | + * | / | | —/ | | ——/ | | + * |/ | |—/ | |—/ | | + * 0 +————————————+ 0 +————————————+ 0 +————————————+———————+ + * B B B + * </pre> + * Three devices with different widths (smaller, equal, and wider) relative to the progress + * threshold are shown in the graphs.<br> + * - A is the width of the screen<br> + * - B is the progress threshold (horizontal swipe distance where progress is linear)<br> + * - C equals B + (A - B) * nonLinearFactor<br> + * <br> + * If A is less than or equal to B, {@code progress} for the swipe distance between:<br> + * - [0, A] will scale linearly between [0, 1].<br> + * If A is greater than B, {@code progress} for swipe distance between:<br> + * - [0, B] will scale linearly between [0, B / C]<br> + * - (B, A] will scale non-linearly and reach 1. + * + * @param linearDistance up to this distance progress continues linearly. B in the graph above. + * @param maxDistance distance at which the progress will be 1f. A in the graph above. + * @param nonLinearFactor This value is used to calculate the target if the screen is wider + * than the progress threshold. */ - void setSwipeThresholds(float progressThreshold); + void setSwipeThresholds(float linearDistance, float maxDistance, float nonLinearFactor); /** * Sets the system bar listener to control the system bar color. diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java index 6d879b830e2e..bb543f24a8ea 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java @@ -301,9 +301,12 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont } @Override - public void setSwipeThresholds(float progressThreshold) { + public void setSwipeThresholds( + float linearDistance, + float maxDistance, + float nonLinearFactor) { mShellExecutor.execute(() -> BackAnimationController.this.setSwipeThresholds( - progressThreshold)); + linearDistance, maxDistance, nonLinearFactor)); } @Override @@ -509,7 +512,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont // Constraints - absolute values float minVelocity = mFlingAnimationUtils.getMinVelocityPxPerSecond(); float maxVelocity = mFlingAnimationUtils.getHighVelocityPxPerSecond(); - float maxX = mTouchTracker.getMaxX(); // px + float maxX = mTouchTracker.getMaxDistance(); // px float maxFlingDistance = maxX * MAX_FLING_PROGRESS; // px // Current state @@ -605,8 +608,11 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont mTouchTracker.setTriggerBack(triggerBack); } - private void setSwipeThresholds(float progressThreshold) { - mTouchTracker.setProgressThreshold(progressThreshold); + private void setSwipeThresholds( + float linearDistance, + float maxDistance, + float nonLinearFactor) { + mTouchTracker.setProgressThresholds(linearDistance, maxDistance, nonLinearFactor); } private void invokeOrCancelBack() { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/TouchTracker.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/TouchTracker.java index 7a00f5b9bab4..a0ada39b459e 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/TouchTracker.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/TouchTracker.java @@ -28,11 +28,13 @@ import android.window.BackMotionEvent; * Helper class to record the touch location for gesture and generate back events. */ class TouchTracker { - private static final String PREDICTIVE_BACK_PROGRESS_THRESHOLD_PROP = - "persist.wm.debug.predictive_back_progress_threshold"; - private static final int PROGRESS_THRESHOLD = SystemProperties - .getInt(PREDICTIVE_BACK_PROGRESS_THRESHOLD_PROP, -1); - private float mProgressThreshold; + private static final String PREDICTIVE_BACK_LINEAR_DISTANCE_PROP = + "persist.wm.debug.predictive_back_linear_distance"; + private static final int LINEAR_DISTANCE = SystemProperties + .getInt(PREDICTIVE_BACK_LINEAR_DISTANCE_PROP, -1); + private float mLinearDistance = LINEAR_DISTANCE; + private float mMaxDistance; + private float mNonLinearFactor; /** * Location of the latest touch event */ @@ -125,17 +127,42 @@ class TouchTracker { // the location everytime back is restarted after being cancelled. float startX = mTriggerBack ? mInitTouchX : mStartThresholdX; float deltaX = Math.abs(startX - touchX); - float maxX = getMaxX(); - maxX = maxX == 0 ? 1 : maxX; - return MathUtils.constrain(deltaX / maxX, 0, 1); + float linearDistance = mLinearDistance; + float maxDistance = getMaxDistance(); + maxDistance = maxDistance == 0 ? 1 : maxDistance; + float progress; + if (linearDistance < maxDistance) { + // Up to linearDistance it behaves linearly, then slowly reaches 1f. + + // maxDistance is composed of linearDistance + nonLinearDistance + float nonLinearDistance = maxDistance - linearDistance; + float initialTarget = linearDistance + nonLinearDistance * mNonLinearFactor; + + boolean isLinear = deltaX <= linearDistance; + if (isLinear) { + progress = deltaX / initialTarget; + } else { + float nonLinearDeltaX = deltaX - linearDistance; + float nonLinearProgress = nonLinearDeltaX / nonLinearDistance; + float currentTarget = MathUtils.lerp( + /* start = */ initialTarget, + /* stop = */ maxDistance, + /* amount = */ nonLinearProgress); + progress = deltaX / currentTarget; + } + } else { + // Always linear behavior. + progress = deltaX / maxDistance; + } + return MathUtils.constrain(progress, 0, 1); } /** - * Maximum X value (in pixels). + * Maximum distance in pixels. * Progress is considered to be completed (1f) when this limit is exceeded. */ - float getMaxX() { - return PROGRESS_THRESHOLD >= 0 ? PROGRESS_THRESHOLD : mProgressThreshold; + float getMaxDistance() { + return mMaxDistance; } BackMotionEvent createProgressEvent(float progress) { @@ -149,7 +176,14 @@ class TouchTracker { /* departingAnimationTarget = */ null); } - public void setProgressThreshold(float progressThreshold) { - mProgressThreshold = progressThreshold; + public void setProgressThresholds(float linearDistance, float maxDistance, + float nonLinearFactor) { + if (LINEAR_DISTANCE >= 0) { + mLinearDistance = LINEAR_DISTANCE; + } else { + mLinearDistance = linearDistance; + } + mMaxDistance = maxDistance; + mNonLinearFactor = nonLinearFactor; } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java index 698681029595..3eb9fa2eef6b 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java @@ -17,18 +17,12 @@ package com.android.wm.shell.bubbles; import static android.app.ActivityTaskManager.INVALID_TASK_ID; -import static android.content.pm.ActivityInfo.CONFIG_ASSETS_PATHS; -import static android.content.pm.ActivityInfo.CONFIG_DENSITY; -import static android.content.pm.ActivityInfo.CONFIG_FONT_SCALE; -import static android.content.pm.ActivityInfo.CONFIG_LAYOUT_DIRECTION; -import static android.content.pm.ActivityInfo.CONFIG_UI_MODE; import static android.service.notification.NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_DELETED; import static android.service.notification.NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_UPDATED; import static android.service.notification.NotificationListenerService.REASON_CANCEL; import static android.view.View.INVISIBLE; import static android.view.View.VISIBLE; import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; -import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; import static com.android.wm.shell.bubbles.BubbleDebugConfig.DEBUG_BUBBLE_CONTROLLER; import static com.android.wm.shell.bubbles.BubbleDebugConfig.DEBUG_BUBBLE_GESTURE; @@ -53,7 +47,6 @@ import android.app.Notification; import android.app.NotificationChannel; import android.app.PendingIntent; import android.content.BroadcastReceiver; -import android.content.ComponentCallbacks2; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; @@ -81,6 +74,7 @@ import android.util.Pair; import android.util.SparseArray; import android.view.IWindowManager; import android.view.SurfaceControl; +import android.view.View; import android.view.ViewGroup; import android.view.ViewRootImpl; import android.view.WindowInsets; @@ -108,7 +102,6 @@ import com.android.wm.shell.common.SingleInstanceRemoteListener; import com.android.wm.shell.common.SyncTransactionQueue; import com.android.wm.shell.common.TaskStackListenerCallback; import com.android.wm.shell.common.TaskStackListenerImpl; -import com.android.wm.shell.common.annotations.ExternalMainThread; import com.android.wm.shell.common.annotations.ShellBackgroundThread; import com.android.wm.shell.common.annotations.ShellMainThread; import com.android.wm.shell.common.bubbles.BubbleBarUpdate; @@ -116,6 +109,7 @@ import com.android.wm.shell.draganddrop.DragAndDropController; import com.android.wm.shell.onehanded.OneHandedController; import com.android.wm.shell.onehanded.OneHandedTransitionCallback; import com.android.wm.shell.pip.PinnedStackListenerForwarder; +import com.android.wm.shell.sysui.ConfigurationChangeListener; import com.android.wm.shell.sysui.ShellCommandHandler; import com.android.wm.shell.sysui.ShellController; import com.android.wm.shell.sysui.ShellInit; @@ -141,7 +135,7 @@ import java.util.function.IntConsumer; * * The controller manages addition, removal, and visible state of bubbles on screen. */ -public class BubbleController implements ComponentCallbacks2, +public class BubbleController implements ConfigurationChangeListener, RemoteCallable<BubbleController> { private static final String TAG = TAG_WITH_CLASS_NAME ? "BubbleController" : TAG_BUBBLES; @@ -159,6 +153,7 @@ public class BubbleController implements ComponentCallbacks2, private static final boolean BUBBLE_BAR_ENABLED = SystemProperties.getBoolean("persist.wm.debug.bubble_bar", false); + /** * Common interface to send updates to bubble views. */ @@ -242,17 +237,17 @@ public class BubbleController implements ComponentCallbacks2, /** Whether or not the BubbleStackView has been added to the WindowManager. */ private boolean mAddedToWindowManager = false; - /** - * Saved configuration, used to detect changes in - * {@link #onConfigurationChanged(Configuration)} - */ - private final Configuration mLastConfiguration = new Configuration(); + /** Saved screen density, used to detect display size changes in {@link #onConfigChanged}. */ + private int mDensityDpi = Configuration.DENSITY_DPI_UNDEFINED; - /** - * Saved screen bounds, used to detect screen size changes in - * {@link #onConfigurationChanged(Configuration)}. - */ - private final Rect mScreenBounds = new Rect(); + /** Saved screen bounds, used to detect screen size changes in {@link #onConfigChanged}. **/ + private Rect mScreenBounds = new Rect(); + + /** Saved font scale, used to detect font size changes in {@link #onConfigChanged}. */ + private float mFontScale = 0; + + /** Saved direction, used to detect layout direction changes @link #onConfigChanged}. */ + private int mLayoutDirection = View.LAYOUT_DIRECTION_UNDEFINED; /** Saved insets, used to detect WindowInset changes. */ private WindowInsets mWindowInsets; @@ -298,8 +293,7 @@ public class BubbleController implements ComponentCallbacks2, TaskViewTransitions taskViewTransitions, SyncTransactionQueue syncQueue, IWindowManager wmService) { - mContext = context.createWindowContext(TYPE_APPLICATION_OVERLAY, null); - mLastConfiguration.setTo(mContext.getResources().getConfiguration()); + mContext = context; mShellCommandHandler = shellCommandHandler; mShellController = shellController; mLauncherApps = launcherApps; @@ -323,11 +317,11 @@ public class BubbleController implements ComponentCallbacks2, mBubblePositioner = positioner; mBubbleData = data; mSavedUserBubbleData = new SparseArray<>(); - mBubbleIconFactory = new BubbleIconFactory(mContext, - mContext.getResources().getDimensionPixelSize(R.dimen.bubble_size), - mContext.getResources().getDimensionPixelSize(R.dimen.bubble_badge_size), - mContext.getResources().getColor(R.color.important_conversation), - mContext.getResources().getDimensionPixelSize( + mBubbleIconFactory = new BubbleIconFactory(context, + context.getResources().getDimensionPixelSize(R.dimen.bubble_size), + context.getResources().getDimensionPixelSize(R.dimen.bubble_badge_size), + context.getResources().getColor(R.color.important_conversation), + context.getResources().getDimensionPixelSize( com.android.internal.R.dimen.importance_ring_stroke_width)); mDisplayController = displayController; mTaskViewTransitions = taskViewTransitions; @@ -488,6 +482,7 @@ public class BubbleController implements ComponentCallbacks2, } mCurrentProfiles = userProfiles; + mShellController.addConfigurationChangeListener(this); mShellController.addExternalInterface(KEY_EXTRA_SHELL_BUBBLES, this::createExternalInterface, this); mShellCommandHandler.addDumpCallback(this::dump, this); @@ -779,7 +774,6 @@ public class BubbleController implements ComponentCallbacks2, try { mAddedToWindowManager = true; registerBroadcastReceiver(); - mContext.registerComponentCallbacks(this); mBubbleData.getOverflow().initialize(this); // (TODO: b/273314541) some duplication in the inset listener if (isShowingAsBubbleBar()) { @@ -837,7 +831,6 @@ public class BubbleController implements ComponentCallbacks2, // Put on background for this binder call, was causing jank mBackgroundExecutor.execute(() -> { try { - mContext.unregisterComponentCallbacks(this); mContext.unregisterReceiver(mBroadcastReceiver); } catch (IllegalArgumentException e) { // Not sure if this happens in production, but was happening in tests @@ -937,7 +930,8 @@ public class BubbleController implements ComponentCallbacks2, mSavedUserBubbleData.remove(userId); } - private void onThemeChanged() { + @Override + public void onThemeChanged() { if (mStackView != null) { mStackView.onThemeChanged(); } @@ -969,60 +963,34 @@ public class BubbleController implements ComponentCallbacks2, } } - // Note: Component callbacks are always called on the main thread of the process - @ExternalMainThread @Override public void onConfigurationChanged(Configuration newConfig) { - mMainExecutor.execute(() -> { - final int diff = newConfig.diff(mLastConfiguration); - final boolean themeChanged = (diff & CONFIG_ASSETS_PATHS) != 0 - || (diff & CONFIG_UI_MODE) != 0; - if (themeChanged) { - onThemeChanged(); - } - if (mBubblePositioner != null) { - mBubblePositioner.update(); + if (mBubblePositioner != null) { + mBubblePositioner.update(); + } + if (mStackView != null && newConfig != null) { + if (newConfig.densityDpi != mDensityDpi + || !newConfig.windowConfiguration.getBounds().equals(mScreenBounds)) { + mDensityDpi = newConfig.densityDpi; + mScreenBounds.set(newConfig.windowConfiguration.getBounds()); + mBubbleData.onMaxBubblesChanged(); + mBubbleIconFactory = new BubbleIconFactory(mContext, + mContext.getResources().getDimensionPixelSize(R.dimen.bubble_size), + mContext.getResources().getDimensionPixelSize(R.dimen.bubble_badge_size), + mContext.getResources().getColor(R.color.important_conversation), + mContext.getResources().getDimensionPixelSize( + com.android.internal.R.dimen.importance_ring_stroke_width)); + mStackView.onDisplaySizeChanged(); + } + if (newConfig.fontScale != mFontScale) { + mFontScale = newConfig.fontScale; + mStackView.updateFontScale(); + } + if (newConfig.getLayoutDirection() != mLayoutDirection) { + mLayoutDirection = newConfig.getLayoutDirection(); + mStackView.onLayoutDirectionChanged(mLayoutDirection); } - if (mStackView != null) { - final boolean densityChanged = (diff & CONFIG_DENSITY) != 0; - final boolean fontScaleChanged = (diff & CONFIG_FONT_SCALE) != 0; - final boolean layoutDirectionChanged = (diff & CONFIG_LAYOUT_DIRECTION) != 0; - if (densityChanged - || !newConfig.windowConfiguration.getBounds().equals(mScreenBounds)) { - mScreenBounds.set(newConfig.windowConfiguration.getBounds()); - mBubbleData.onMaxBubblesChanged(); - mBubbleIconFactory = new BubbleIconFactory(mContext, - mContext.getResources().getDimensionPixelSize(R.dimen.bubble_size), - mContext.getResources().getDimensionPixelSize( - R.dimen.bubble_badge_size), - mContext.getResources().getColor(R.color.important_conversation), - mContext.getResources().getDimensionPixelSize( - com.android.internal.R.dimen.importance_ring_stroke_width)); - mStackView.onDisplaySizeChanged(); - } - if (fontScaleChanged) { - mStackView.updateFontScale(); - } - if (layoutDirectionChanged) { - mStackView.onLayoutDirectionChanged(newConfig.getLayoutDirection()); - } - } - mLastConfiguration.setTo(newConfig); - }); - } - - // Note: Component callbacks are always called on the main thread of the process - @ExternalMainThread - @Override - public void onTrimMemory(int level) { - // Do nothing - } - - // Note: Component callbacks are always called on the main thread of the process - @ExternalMainThread - @Override - public void onLowMemory() { - // Do nothing + } } private void onNotificationPanelExpandedChanged(boolean expanded) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java index adc0c9c4322a..9fcd207dc370 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java @@ -46,6 +46,7 @@ import android.graphics.Outline; import android.graphics.Paint; import android.graphics.Picture; import android.graphics.PointF; +import android.graphics.PorterDuff; import android.graphics.Rect; import android.graphics.drawable.ShapeDrawable; import android.os.RemoteException; @@ -479,13 +480,18 @@ public class BubbleExpandedView extends LinearLayout { void applyThemeAttrs() { final TypedArray ta = mContext.obtainStyledAttributes(new int[]{ android.R.attr.dialogCornerRadius, - com.android.internal.R.attr.materialColorSurfaceBright}); + com.android.internal.R.attr.materialColorSurfaceBright, + com.android.internal.R.attr.materialColorSurfaceContainerHigh}); boolean supportsRoundedCorners = ScreenDecorationsUtils.supportsRoundedCornersOnWindows( mContext.getResources()); mCornerRadius = supportsRoundedCorners ? ta.getDimensionPixelSize(0, 0) : 0; mBackgroundColorFloating = ta.getColor(1, Color.WHITE); mExpandedViewContainer.setBackgroundColor(mBackgroundColorFloating); + final int manageMenuBg = ta.getColor(2, Color.WHITE); ta.recycle(); + if (mManageButton != null) { + mManageButton.getBackground().setColorFilter(manageMenuBg, PorterDuff.Mode.SRC_IN); + } if (mTaskView != null) { mTaskView.setCornerRadius(mCornerRadius); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java index 8e9fc1131e4a..91c7cc0c6e89 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java @@ -38,8 +38,10 @@ import android.content.Context; import android.content.Intent; import android.content.res.Resources; import android.content.res.TypedArray; +import android.graphics.Color; import android.graphics.Outline; import android.graphics.PointF; +import android.graphics.PorterDuff; import android.graphics.Rect; import android.graphics.RectF; import android.graphics.drawable.ColorDrawable; @@ -1203,6 +1205,12 @@ public class BubbleStackView extends FrameLayout R.layout.bubble_manage_menu, this, false); mManageMenu.setVisibility(View.INVISIBLE); + final TypedArray ta = mContext.obtainStyledAttributes(new int[]{ + com.android.internal.R.attr.materialColorSurfaceBright}); + final int menuBackgroundColor = ta.getColor(0, Color.WHITE); + ta.recycle(); + mManageMenu.getBackground().setColorFilter(menuBackgroundColor, PorterDuff.Mode.SRC_IN); + PhysicsAnimator.getInstance(mManageMenu).setDefaultSpringConfig(mManageSpringConfig); mManageMenu.setOutlineProvider(new ViewOutlineProvider() { @@ -1338,7 +1346,7 @@ public class BubbleStackView extends FrameLayout // Recreates & shows the education views. Call when a theme/config change happens. private void updateUserEdu() { - if (isStackEduVisible()) { + if (isStackEduVisible() && !mStackEduView.isHiding()) { removeView(mStackEduView); mStackEduView = new StackEducationView(mContext, mPositioner, mBubbleController); addView(mStackEduView); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/StackEducationView.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/StackEducationView.kt index 627273f093f3..d0598cd28582 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/StackEducationView.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/StackEducationView.kt @@ -37,8 +37,7 @@ class StackEducationView constructor( context: Context, positioner: BubblePositioner, controller: BubbleController -) - : LinearLayout(context) { +) : LinearLayout(context) { private val TAG = if (BubbleDebugConfig.TAG_WITH_CLASS_NAME) "BubbleStackEducationView" else BubbleDebugConfig.TAG_BUBBLES @@ -53,7 +52,8 @@ class StackEducationView constructor( private val titleTextView by lazy { findViewById<TextView>(R.id.stack_education_title) } private val descTextView by lazy { findViewById<TextView>(R.id.stack_education_description) } - private var isHiding = false + var isHiding = false + private set init { LayoutInflater.from(context).inflate(R.layout.bubble_stack_user_education, this) diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java index 9a2ec15238a7..ab8e7e63ef7f 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java @@ -709,7 +709,8 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange return context.getSystemService(WindowManager.class) .getMaximumWindowMetrics() .getWindowInsets() - .getInsets(WindowInsets.Type.systemBars() | WindowInsets.Type.displayCutout()) + .getInsetsIgnoringVisibility(WindowInsets.Type.systemBars() + | WindowInsets.Type.displayCutout()) .toRect(); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java index 74ef57e4baae..5a9c25f7a710 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java @@ -732,10 +732,11 @@ public abstract class WMShellBaseModule { @WMSingleton @Provides - static ShellController provideShellController(ShellInit shellInit, + static ShellController provideShellController(Context context, + ShellInit shellInit, ShellCommandHandler shellCommandHandler, @ShellMainThread ShellExecutor mainExecutor) { - return new ShellController(shellInit, shellCommandHandler, mainExecutor); + return new ShellController(context, shellInit, shellCommandHandler, mainExecutor); } // diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/IPip.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/IPip.aidl index 78de5f3e7a1f..3906599b7581 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/IPip.aidl +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/IPip.aidl @@ -57,27 +57,35 @@ interface IPip { in Rect destinationBounds, in SurfaceControl overlay) = 2; /** + * Notifies the swiping Activity to PiP onto home transition is aborted + * + * @param taskId the Task id that the Activity and overlay are currently in. + * @param componentName ComponentName represents the Activity + */ + oneway void abortSwipePipToHome(int taskId, in ComponentName componentName) = 3; + + /** * Sets listener to get pinned stack animation callbacks. */ - oneway void setPipAnimationListener(IPipAnimationListener listener) = 3; + oneway void setPipAnimationListener(IPipAnimationListener listener) = 4; /** * Sets the shelf height and visibility. */ - oneway void setShelfHeight(boolean visible, int shelfHeight) = 4; + oneway void setShelfHeight(boolean visible, int shelfHeight) = 5; /** * Sets the next pip animation type to be the alpha animation. */ - oneway void setPipAnimationTypeToAlpha() = 5; + oneway void setPipAnimationTypeToAlpha() = 6; /** * Sets the height and visibility of the Launcher keep clear area. */ - oneway void setLauncherKeepClearAreaHeight(boolean visible, int height) = 6; + oneway void setLauncherKeepClearAreaHeight(boolean visible, int height) = 7; /** * Sets the app icon size in pixel used by Launcher */ - oneway void setLauncherAppIconSize(int iconSizePx) = 7; + oneway void setLauncherAppIconSize(int iconSizePx) = 8; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java index bbfeb90704db..57cc28d1dde5 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java @@ -328,6 +328,8 @@ public class PipAnimationController { private PipSurfaceTransactionHelper mSurfaceTransactionHelper; private @TransitionDirection int mTransitionDirection; protected PipContentOverlay mContentOverlay; + // Flag to avoid double-end + private boolean mHasRequestedEnd; private PipTransitionAnimator(TaskInfo taskInfo, SurfaceControl leash, @AnimationType int animationType, @@ -357,6 +359,7 @@ public class PipAnimationController { @Override public void onAnimationUpdate(ValueAnimator animation) { + if (mHasRequestedEnd) return; applySurfaceControlTransaction(mLeash, mSurfaceControlTransactionFactory.getTransaction(), animation.getAnimatedFraction()); @@ -364,6 +367,8 @@ public class PipAnimationController { @Override public void onAnimationEnd(Animator animation) { + if (mHasRequestedEnd) return; + mHasRequestedEnd = true; mCurrentValue = mEndValue; final SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction(); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipContentOverlay.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipContentOverlay.java index 9fa57cacb11f..c701b9581ca2 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipContentOverlay.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipContentOverlay.java @@ -206,6 +206,7 @@ public abstract class PipContentOverlay { tx.show(mLeash); tx.setLayer(mLeash, Integer.MAX_VALUE); tx.setBuffer(mLeash, mBitmap.getHardwareBuffer()); + tx.setAlpha(mLeash, 0f); tx.reparent(mLeash, parentLeash); tx.apply(); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java index 58bc81dd3007..db7c3fc9c9ec 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java @@ -462,13 +462,29 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, // to the actual Task surface now. // PipTransition is responsible to fade it out and cleanup when finishing the enter PIP // transition. - final SurfaceControl.Transaction t = new SurfaceControl.Transaction(); + final SurfaceControl.Transaction t = mSurfaceControlTransactionFactory.getTransaction(); mTaskOrganizer.reparentChildSurfaceToTask(taskId, overlay, t); t.setLayer(overlay, Integer.MAX_VALUE); t.apply(); } } + /** + * Callback when launcher aborts swipe-pip-to-home operation. + */ + public void abortSwipePipToHome(int taskId, ComponentName componentName) { + if (!mPipTransitionState.getInSwipePipToHomeTransition()) { + return; + } + ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, + "Abort swipe-pip-to-home for %s", componentName); + sendOnPipTransitionCancelled(TRANSITION_DIRECTION_TO_PIP); + // Cleanup internal states + mPipTransitionState.setInSwipePipToHomeTransition(false); + mPictureInPictureParams = null; + mPipTransitionState.setTransitionState(PipTransitionState.UNDEFINED); + } + public ActivityManager.RunningTaskInfo getTaskInfo() { return mTaskInfo; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java index 9677728d1d18..bfc1fb905ade 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java @@ -26,6 +26,7 @@ import static android.view.Surface.ROTATION_90; import static android.view.WindowManager.TRANSIT_CHANGE; import static android.view.WindowManager.TRANSIT_OPEN; import static android.view.WindowManager.TRANSIT_PIP; +import static android.view.WindowManager.TRANSIT_TO_BACK; import static android.view.WindowManager.transitTypeToString; import static android.window.TransitionInfo.FLAG_IS_DISPLAY; @@ -92,6 +93,8 @@ public class PipTransition extends PipTransitionController { private final Rect mExitDestinationBounds = new Rect(); @Nullable private IBinder mExitTransition; + @Nullable + private IBinder mMoveToBackTransition; private IBinder mRequestedEnterTransition; private WindowContainerToken mRequestedEnterTask; /** The Task window that is currently in PIP windowing mode. */ @@ -171,9 +174,10 @@ public class PipTransition extends PipTransitionController { // Exiting PIP. final int type = info.getType(); - if (transition.equals(mExitTransition)) { + if (transition.equals(mExitTransition) || transition.equals(mMoveToBackTransition)) { mExitDestinationBounds.setEmpty(); mExitTransition = null; + mMoveToBackTransition = null; mHasFadeOut = false; if (mFinishCallback != null) { callFinishCallback(null /* wct */); @@ -201,6 +205,8 @@ public class PipTransition extends PipTransitionController { startExitToSplitAnimation(info, startTransaction, finishTransaction, finishCallback, pipTaskInfo); break; + case TRANSIT_TO_BACK: + // pass through here is intended case TRANSIT_REMOVE_PIP: removePipImmediately(info, startTransaction, finishTransaction, finishCallback, pipTaskInfo); @@ -273,6 +279,15 @@ public class PipTransition extends PipTransitionController { WindowContainerTransaction wct = new WindowContainerTransaction(); augmentRequest(transition, request, wct); return wct; + } else if (request.getType() == TRANSIT_TO_BACK && request.getTriggerTask() != null + && request.getTriggerTask().getWindowingMode() == WINDOWING_MODE_PINNED) { + // if we receive a TRANSIT_TO_BACK type of request while in PiP + mMoveToBackTransition = transition; + // update the transition state to avoid {@link PipTaskOrganizer#onTaskVanished()} calls + mPipTransitionState.setTransitionState(PipTransitionState.EXITING_PIP); + + // return an empty WindowContainerTransaction so that we don't check other handlers + return new WindowContainerTransaction(); } else { return null; } @@ -1015,6 +1030,16 @@ public class PipTransition extends PipTransitionController { mPipOrganizer.onExitPipFinished(prevPipTaskChange.getTaskInfo()); } + @Override + public boolean syncPipSurfaceState(@NonNull TransitionInfo info, + @NonNull SurfaceControl.Transaction startTransaction, + @NonNull SurfaceControl.Transaction finishTransaction) { + final TransitionInfo.Change pipChange = findCurrentPipTaskChange(info); + if (pipChange == null) return false; + updatePipForUnhandledTransition(pipChange, startTransaction, finishTransaction); + return true; + } + private void updatePipForUnhandledTransition(@NonNull TransitionInfo.Change pipChange, @NonNull SurfaceControl.Transaction startTransaction, @NonNull SurfaceControl.Transaction finishTransaction) { @@ -1025,10 +1050,12 @@ public class PipTransition extends PipTransitionController { final boolean isInPip = mPipTransitionState.isInPip(); mSurfaceTransactionHelper .crop(startTransaction, leash, destBounds) - .round(startTransaction, leash, isInPip); + .round(startTransaction, leash, isInPip) + .shadow(startTransaction, leash, isInPip); mSurfaceTransactionHelper .crop(finishTransaction, leash, destBounds) - .round(finishTransaction, leash, isInPip); + .round(finishTransaction, leash, isInPip) + .shadow(finishTransaction, leash, isInPip); } /** Hides and shows the existing PIP during fixed rotation transition of other activities. */ diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java index 949d6f558c32..2fff0e469f3d 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java @@ -240,6 +240,18 @@ public abstract class PipTransitionController implements Transitions.TransitionH @NonNull final Transitions.TransitionFinishCallback finishCallback) { } + /** + * Applies the proper surface states (rounded corners/shadows) to pip surfaces in `info`. + * This is intended to be used when PiP is part of another animation but isn't, itself, + * animating (eg. unlocking). + * @return `true` if there was a pip in `info`. + */ + public boolean syncPipSurfaceState(@NonNull TransitionInfo info, + @NonNull SurfaceControl.Transaction startTransaction, + @NonNull SurfaceControl.Transaction finishTransaction) { + return false; + } + /** End the currently-playing PiP animation. */ public void end() { } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java index 9e6bd4760900..63181da46b02 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java @@ -1017,6 +1017,10 @@ public class PipController implements PipTransitionController.PipTransitionCallb mPipTaskOrganizer.stopSwipePipToHome(taskId, componentName, destinationBounds, overlay); } + private void abortSwipePipToHome(int taskId, ComponentName componentName) { + mPipTaskOrganizer.abortSwipePipToHome(taskId, componentName); + } + private String getTransitionTag(int direction) { switch (direction) { case TRANSITION_DIRECTION_TO_PIP: @@ -1313,6 +1317,12 @@ public class PipController implements PipTransitionController.PipTransitionCallb } @Override + public void abortSwipePipToHome(int taskId, ComponentName componentName) { + executeRemoteCallWithTaskPermission(mController, "abortSwipePipToHome", + (controller) -> controller.abortSwipePipToHome(taskId, componentName)); + } + + @Override public void setShelfHeight(boolean visible, int height) { executeRemoteCallWithTaskPermission(mController, "setShelfHeight", (controller) -> controller.setShelfHeight(visible, height)); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java index d07641892552..613791ccc062 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java @@ -31,12 +31,19 @@ import static com.android.wm.shell.pip.tv.TvPipMenuController.MODE_MOVE_MENU; import static com.android.wm.shell.pip.tv.TvPipMenuController.MODE_NO_MENU; import android.content.Context; +import android.content.res.Resources; +import android.graphics.Outline; +import android.graphics.Path; import android.graphics.Rect; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.ShapeDrawable; +import android.graphics.drawable.shapes.PathShape; import android.os.Handler; import android.view.Gravity; import android.view.KeyEvent; import android.view.View; import android.view.ViewGroup; +import android.view.ViewOutlineProvider; import android.view.accessibility.AccessibilityManager; import android.widget.FrameLayout; import android.widget.ImageView; @@ -91,6 +98,8 @@ public class TvPipMenuView extends FrameLayout implements TvPipActionsProvider.L private final ImageView mArrowLeft; private final TvWindowMenuActionButton mA11yDoneButton; + private final int mArrowElevation; + private @TvPipMenuController.TvPipMenuMode int mCurrentMenuMode = MODE_NO_MENU; private final Rect mCurrentPipBounds = new Rect(); private int mCurrentPipGravity; @@ -131,21 +140,70 @@ public class TvPipMenuView extends FrameLayout implements TvPipActionsProvider.L mArrowLeft = findViewById(R.id.tv_pip_menu_arrow_left); mA11yDoneButton = findViewById(R.id.tv_pip_menu_done_button); - mResizeAnimationDuration = context.getResources().getInteger( - R.integer.config_pipResizeAnimationDuration); - mPipMenuFadeAnimationDuration = context.getResources() - .getInteger(R.integer.tv_window_menu_fade_animation_duration); + final Resources res = context.getResources(); + mResizeAnimationDuration = res.getInteger(R.integer.config_pipResizeAnimationDuration); + mPipMenuFadeAnimationDuration = + res.getInteger(R.integer.tv_window_menu_fade_animation_duration); + mPipMenuOuterSpace = res.getDimensionPixelSize(R.dimen.pip_menu_outer_space); + mPipMenuBorderWidth = res.getDimensionPixelSize(R.dimen.pip_menu_border_width); + mArrowElevation = res.getDimensionPixelSize(R.dimen.pip_menu_arrow_elevation); - mPipMenuOuterSpace = context.getResources() - .getDimensionPixelSize(R.dimen.pip_menu_outer_space); - mPipMenuBorderWidth = context.getResources() - .getDimensionPixelSize(R.dimen.pip_menu_border_width); + initMoveArrows(); mEduTextDrawer = new TvPipMenuEduTextDrawer(mContext, mainHandler, this); mEduTextContainer = (ViewGroup) findViewById(R.id.tv_pip_menu_edu_text_container); mEduTextContainer.addView(mEduTextDrawer); } + private void initMoveArrows() { + final int arrowSize = + mContext.getResources().getDimensionPixelSize(R.dimen.pip_menu_arrow_size); + final Path arrowPath = createArrowPath(arrowSize); + + final ShapeDrawable arrowDrawable = new ShapeDrawable(); + arrowDrawable.setShape(new PathShape(arrowPath, arrowSize, arrowSize)); + arrowDrawable.setTint(mContext.getResources().getColor(R.color.tv_pip_menu_arrow_color)); + + final ViewOutlineProvider arrowOutlineProvider = new ViewOutlineProvider() { + @Override + public void getOutline(View view, Outline outline) { + outline.setPath(createArrowPath(view.getMeasuredHeight())); + } + }; + + initArrow(mArrowRight, arrowOutlineProvider, arrowDrawable, 0); + initArrow(mArrowDown, arrowOutlineProvider, arrowDrawable, 90); + initArrow(mArrowLeft, arrowOutlineProvider, arrowDrawable, 180); + initArrow(mArrowUp, arrowOutlineProvider, arrowDrawable, 270); + } + + /** + * Creates a Path for a movement arrow in the MODE_MOVE_MENU. The resulting Path is a simple + * right-pointing triangle with its tip in the center of a size x size square: + * _ _ _ _ _ + * |* | + * |* * | + * |* * | + * |* * | + * |* _ _ _ _| + * + */ + private Path createArrowPath(int size) { + final Path triangle = new Path(); + triangle.lineTo(0, size); + triangle.lineTo(size / 2, size / 2); + triangle.close(); + return triangle; + } + + private void initArrow(View v, ViewOutlineProvider arrowOutlineProvider, Drawable arrowDrawable, + int rotation) { + v.setOutlineProvider(arrowOutlineProvider); + v.setBackground(arrowDrawable); + v.setRotation(rotation); + v.setElevation(mArrowElevation); + } + void onPipTransitionToTargetBoundsStarted(Rect targetBounds) { if (targetBounds == null) { return; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java index 387d39056ffc..a9ad3c90949f 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java @@ -301,7 +301,7 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler { try { for (int i = 0; i < mPausingTasks.size(); ++i) { snapshots[i] = ActivityTaskManager.getService().takeTaskSnapshot( - mPausingTasks.get(0).mTaskInfo.taskId); + mPausingTasks.get(0).mTaskInfo.taskId, false /* updateCache */); } } catch (RemoteException e) { taskIds = null; @@ -671,7 +671,8 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler { try { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION, "[%d] RecentsController.screenshotTask: taskId=%d", mInstanceId, taskId); - return ActivityTaskManager.getService().takeTaskSnapshot(taskId); + return ActivityTaskManager.getService().takeTaskSnapshot(taskId, + true /* updateCache */); } catch (RemoteException e) { Slog.e(TAG, "Failed to screenshot task", e); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java index 38c420a15231..14ea86a8c0e9 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java @@ -20,8 +20,9 @@ import static android.view.WindowManager.TRANSIT_CHANGE; import static android.view.WindowManager.TRANSIT_CLOSE; import static android.view.WindowManager.TRANSIT_OPEN; import static android.view.WindowManager.TRANSIT_TO_BACK; -import static android.view.WindowManager.TRANSIT_TO_FRONT; +import static com.android.wm.shell.animation.Interpolators.ALPHA_IN; +import static com.android.wm.shell.animation.Interpolators.ALPHA_OUT; import static com.android.wm.shell.common.split.SplitScreenConstants.FADE_DURATION; import static com.android.wm.shell.common.split.SplitScreenConstants.FLAG_IS_DIVIDER_BAR; import static com.android.wm.shell.splitscreen.SplitScreen.stageTypeToString; @@ -86,6 +87,7 @@ class SplitScreenTransitions { mStageCoordinator = stageCoordinator; } + /** Play animation for enter transition or dismiss transition. */ void playAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction startTransaction, @NonNull SurfaceControl.Transaction finishTransaction, @@ -116,6 +118,7 @@ class SplitScreenTransitions { playInternalAnimation(transition, info, startTransaction, mainRoot, sideRoot, topRoot); } + /** Internal funcation of playAnimation. */ private void playInternalAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction t, @NonNull WindowContainerToken mainRoot, @NonNull WindowContainerToken sideRoot, @NonNull WindowContainerToken topRoot) { @@ -126,8 +129,8 @@ class SplitScreenTransitions { final SurfaceControl leash = change.getLeash(); final int mode = info.getChanges().get(i).getMode(); + final int rootIdx = TransitionUtil.rootIndexFor(change, info); if (mode == TRANSIT_CHANGE) { - final int rootIdx = TransitionUtil.rootIndexFor(change, info); if (change.getParent() != null) { // This is probably reparented, so we want the parent to be immediately visible final TransitionInfo.Change parentChange = info.getChange(change.getParent()); @@ -155,7 +158,7 @@ class SplitScreenTransitions { mFinishTransaction.setPosition(leash, change.getEndRelOffset().x, change.getEndRelOffset().y); mFinishTransaction.setCrop(leash, null); - } else if (isEnter && isTopRoot) { + } else if (isTopRoot) { // Ensure top root is visible at start. t.setAlpha(leash, 1.f); t.show(leash); @@ -168,40 +171,56 @@ class SplitScreenTransitions { t.setLayer(leash, Integer.MAX_VALUE); t.show(leash); } - // These container changes we don't want to animate them. - // We should only animate stage root, divider and child tasks are not under stage root. - if (isTopRoot || isMainChild || isSideChild || change.getTaskInfo() == null) { + + // We want to use child tasks to animate so ignore split root container and non task + // except divider change. + if (isTopRoot || isMainRoot || isSideRoot + || (change.getTaskInfo() == null && !isDivider)) { continue; } - if (isEnter && mPendingEnter.mResizeAnim) { // We will run animation in next transition so skip anim here continue; - } else if (isEnter && isMainRoot) { - // Main stage already on top so skip fade in animation to reduce flicker. + } else if (isPendingDismiss(transition) + && mPendingDismiss.mReason == EXIT_REASON_DRAG_DIVIDER) { + // TODO(b/280020345): need to refine animation for this but just skip anim now. continue; } + // Because cross fade might be looked more flicker during animation + // (surface become black in middle of animation), we only do fade-out + // and show opening surface directly. boolean isOpening = TransitionUtil.isOpeningType(info.getType()); - if (isOpening && (mode == TRANSIT_OPEN || mode == TRANSIT_TO_FRONT)) { - // fade in - startFadeAnimation(leash, true /* show */); - } else if (!isOpening && (mode == TRANSIT_CLOSE || mode == TRANSIT_TO_BACK)) { + if (!isOpening && (mode == TRANSIT_CLOSE || mode == TRANSIT_TO_BACK)) { // fade out - if (info.getType() == TRANSIT_SPLIT_DISMISS_SNAP) { - // Dismissing via snap-to-top/bottom means that the dismissed task is already - // not-visible (usually cropped to oblivion) so immediately set its alpha to 0 - // and don't animate it so it doesn't pop-in when reparented. - t.setAlpha(leash, 0.f); + if (change.getSnapshot() != null) { + // This case is happened if task is going to reparent to TDA, the origin leash + // doesn't rendor so we use snapshot to replace it animating. + t.reparent(change.getSnapshot(), info.getRoot(rootIdx).getLeash()); + // Use origin leash layer. + t.setLayer(change.getSnapshot(), info.getChanges().size() - i); + t.setPosition(change.getSnapshot(), change.getStartAbsBounds().left, + change.getStartAbsBounds().top); + t.show(change.getSnapshot()); + startFadeAnimation(change.getSnapshot(), false /* show */); } else { startFadeAnimation(leash, false /* show */); } + } else if (mode == TRANSIT_CHANGE && change.getSnapshot() != null) { + t.reparent(change.getSnapshot(), info.getRoot(rootIdx).getLeash()); + // Ensure snapshot it on the top of all transition surfaces + t.setLayer(change.getSnapshot(), info.getChanges().size() + 1); + t.setPosition(change.getSnapshot(), change.getStartAbsBounds().left, + change.getStartAbsBounds().top); + t.show(change.getSnapshot()); + startFadeAnimation(change.getSnapshot(), false /* show */); } } t.apply(); onFinish(null /* wct */, null /* wctCB */); } + /** Play animation for resize transition. */ void playResizeAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction startTransaction, @NonNull SurfaceControl.Transaction finishTransaction, @@ -437,6 +456,7 @@ class SplitScreenTransitions { final SurfaceControl.Transaction transaction = mTransactionPool.acquire(); final ValueAnimator va = ValueAnimator.ofFloat(start, end); va.setDuration(FADE_DURATION); + va.setInterpolator(show ? ALPHA_IN : ALPHA_OUT); va.addUpdateListener(animation -> { float fraction = animation.getAnimatedFraction(); transaction.setAlpha(leash, start * (1.f - fraction) + end * fraction); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java index 087e3a2384d6..bf20567834e0 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java @@ -582,7 +582,6 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, return; } - prepareEvictChildTasksIfSplitActive(wct); setSideStagePosition(splitPosition, wct); options1 = options1 != null ? options1 : new Bundle(); addActivityOptions(options1, mSideStage); @@ -604,7 +603,6 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, return; } - prepareEvictChildTasksIfSplitActive(wct); setSideStagePosition(splitPosition, wct); options1 = options1 != null ? options1 : new Bundle(); addActivityOptions(options1, mSideStage); @@ -625,7 +623,6 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, return; } - prepareEvictChildTasksIfSplitActive(wct); setSideStagePosition(splitPosition, wct); options1 = options1 != null ? options1 : new Bundle(); addActivityOptions(options1, mSideStage); @@ -691,7 +688,6 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, mMainStage.activate(wct, false /* reparent */); } - prepareEvictChildTasksIfSplitActive(wct); mSplitLayout.setDivideRatio(splitRatio); updateWindowBounds(mSplitLayout, wct); wct.reorder(mRootTaskInfo.token, true); @@ -1075,24 +1071,11 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, } final WindowContainerTransaction evictWct = new WindowContainerTransaction(); - prepareEvictNonOpeningChildTasks(SPLIT_POSITION_TOP_OR_LEFT, apps, evictWct); - prepareEvictNonOpeningChildTasks(SPLIT_POSITION_BOTTOM_OR_RIGHT, apps, evictWct); + mMainStage.evictNonOpeningChildren(apps, evictWct); + mSideStage.evictNonOpeningChildren(apps, evictWct); mSyncQueue.queue(evictWct); } - - /** - * Collects all the current child tasks of a specific split and prepares transaction to evict - * them to display. - */ - void prepareEvictChildTasks(@SplitPosition int position, WindowContainerTransaction wct) { - if (position == mSideStagePosition) { - mSideStage.evictAllChildren(wct); - } else { - mMainStage.evictAllChildren(wct); - } - } - void prepareEvictNonOpeningChildTasks(@SplitPosition int position, RemoteAnimationTarget[] apps, WindowContainerTransaction wct) { if (position == mSideStagePosition) { @@ -1107,13 +1090,6 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, mSideStage.evictInvisibleChildren(wct); } - void prepareEvictChildTasksIfSplitActive(WindowContainerTransaction wct) { - if (mMainStage.isActive()) { - mMainStage.evictAllChildren(wct); - mSideStage.evictAllChildren(wct); - } - } - Bundle resolveStartStage(@StageType int stage, @SplitPosition int position, @Nullable Bundle options, @Nullable WindowContainerTransaction wct) { switch (stage) { @@ -1500,10 +1476,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, wct.startTask(taskInfo.taskId, resolveStartStage(STAGE_TYPE_UNDEFINED, startPosition, null, wct)); } - // If running background, we need to reparent current top visible task to another stage - // and evict all tasks current under its. + // If running background, we need to reparent current top visible task to main stage. if (!isSplitScreenVisible()) { - // Recreate so we need to reset position rather than keep position of background split. mMainStage.reparentTopTask(wct); prepareSplitLayout(wct); } @@ -2061,18 +2035,16 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, // Reset this flag every time onLayoutSizeChanged. mShowDecorImmediately = false; - if (!ENABLE_SHELL_TRANSITIONS) { - // Only need screenshot for legacy case because shell transition should screenshot - // itself during transition. - final SurfaceControl.Transaction startT = mTransactionPool.acquire(); - mMainStage.screenshotIfNeeded(startT); - mSideStage.screenshotIfNeeded(startT); - mTransactionPool.release(startT); - } - final WindowContainerTransaction wct = new WindowContainerTransaction(); boolean sizeChanged = updateWindowBounds(layout, wct); - if (!sizeChanged) return; + if (!sizeChanged) { + // We still need to resize on decor for ensure all current status clear. + final SurfaceControl.Transaction t = mTransactionPool.acquire(); + mMainStage.onResized(t); + mSideStage.onResized(t); + mTransactionPool.release(t); + return; + } sendOnBoundsChanged(); if (ENABLE_SHELL_TRANSITIONS) { @@ -2080,6 +2052,13 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, mSplitTransitions.startResizeTransition(wct, this, (finishWct, t) -> mSplitLayout.setDividerInteractive(true, false, "onSplitResizeFinish")); } else { + // Only need screenshot for legacy case because shell transition should screenshot + // itself during transition. + final SurfaceControl.Transaction startT = mTransactionPool.acquire(); + mMainStage.screenshotIfNeeded(startT); + mSideStage.screenshotIfNeeded(startT); + mTransactionPool.release(startT); + mSyncQueue.queue(wct); mSyncQueue.runInSync(t -> { updateSurfaceBounds(layout, t, false /* applyResizingOffset */); @@ -2337,7 +2316,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, prepareEnterSplitScreen(out); mSplitTransitions.setEnterTransition(transition, request.getRemoteTransition(), null /* consumedCallback */, null /* finishedCallback */, - 0 /* extraTransitType */, !mIsDropEntering); + TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE, !mIsDropEntering); } } return out; @@ -2604,7 +2583,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, == TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE) { if (mainChild == null && sideChild == null) { Log.w(TAG, "Launched a task in split, but didn't receive any task in transition."); - mSplitTransitions.mPendingEnter.cancel(null /* finishedCb */); + mSplitTransitions.mPendingEnter.cancel((cancelWct, cancelT) + -> prepareExitSplitScreen(STAGE_TYPE_UNDEFINED, cancelWct)); return true; } } else { @@ -2614,10 +2594,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, final int dismissTop = mainChild != null ? STAGE_TYPE_MAIN : (sideChild != null ? STAGE_TYPE_SIDE : STAGE_TYPE_UNDEFINED); mSplitTransitions.mPendingEnter.cancel( - (cancelWct, cancelT) -> { - mSideStage.removeAllTasks(cancelWct, dismissTop == STAGE_TYPE_SIDE); - mMainStage.deactivate(cancelWct, dismissTop == STAGE_TYPE_MAIN); - }); + (cancelWct, cancelT) -> prepareExitSplitScreen(dismissTop, cancelWct)); return true; } } @@ -2626,14 +2603,19 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, // transitions locally, but remotes (like Launcher) may get confused if they were // depending on listener callbacks. This can happen because task-organizer callbacks // aren't serialized with transition callbacks. + // This usually occurred on app use trampoline launch new task and finish itself. // TODO(b/184679596): Find a way to either include task-org information in // the transition, or synchronize task-org callbacks. - if (mainChild != null && !mMainStage.containsTask(mainChild.getTaskInfo().taskId)) { + final boolean mainNotContainOpenTask = + mainChild != null && !mMainStage.containsTask(mainChild.getTaskInfo().taskId); + final boolean sideNotContainOpenTask = + sideChild != null && !mSideStage.containsTask(sideChild.getTaskInfo().taskId); + if (mainNotContainOpenTask) { Log.w(TAG, "Expected onTaskAppeared on " + mMainStage + " to have been called with " + mainChild.getTaskInfo().taskId + " before startAnimation()."); } - if (sideChild != null && !mSideStage.containsTask(sideChild.getTaskInfo().taskId)) { + if (sideNotContainOpenTask) { Log.w(TAG, "Expected onTaskAppeared on " + mSideStage + " to have been called with " + sideChild.getTaskInfo().taskId + " before startAnimation()."); @@ -2642,10 +2624,18 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, final TransitionInfo.Change finalSideChild = sideChild; enterTransition.setFinishedCallback((callbackWct, callbackT) -> { if (finalMainChild != null) { - mMainStage.evictOtherChildren(callbackWct, finalMainChild.getTaskInfo().taskId); + if (!mainNotContainOpenTask) { + mMainStage.evictOtherChildren(callbackWct, finalMainChild.getTaskInfo().taskId); + } else { + mMainStage.evictInvisibleChildren(callbackWct); + } } if (finalSideChild != null) { - mSideStage.evictOtherChildren(callbackWct, finalSideChild.getTaskInfo().taskId); + if (!sideNotContainOpenTask) { + mSideStage.evictOtherChildren(callbackWct, finalSideChild.getTaskInfo().taskId); + } else { + mSideStage.evictInvisibleChildren(callbackWct); + } } if (enterTransition.mResizeAnim) { mShowDecorImmediately = true; @@ -2904,9 +2894,10 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, if (!isSplitScreenVisible()) { mIsDropEntering = true; } - if (!isSplitScreenVisible()) { + if (!isSplitScreenVisible() && !ENABLE_SHELL_TRANSITIONS) { // If split running background, exit split first. - // TODO(b/280392203) : skip doing this on shell transition once this bug is fixed. + // Skip this on shell transition due to we could evict existing tasks on transition + // finished. exitSplitScreen(null /* childrenToTop */, EXIT_REASON_RECREATE_SPLIT); } mLogger.enterRequestedByDrag(position, dragSessionId); @@ -2916,9 +2907,10 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, * Sets info to be logged when splitscreen is next entered. */ public void onRequestToSplit(InstanceId sessionId, int enterReason) { - if (!isSplitScreenVisible()) { + if (!isSplitScreenVisible() && !ENABLE_SHELL_TRANSITIONS) { // If split running background, exit split first. - // TODO(b/280392203) : skip doing this on shell transition once this bug is fixed. + // Skip this on shell transition due to we could evict existing tasks on transition + // finished. exitSplitScreen(null /* childrenToTop */, EXIT_REASON_RECREATE_SPLIT); } mLogger.enterRequested(sessionId, enterReason); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellController.java index 3f944cb6d628..0eb7c2d98e0a 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellController.java @@ -32,6 +32,7 @@ import android.content.pm.UserInfo; import android.content.res.Configuration; import android.os.Bundle; import android.util.ArrayMap; +import android.view.SurfaceControlRegistry; import androidx.annotation.NonNull; import androidx.annotation.VisibleForTesting; @@ -52,6 +53,7 @@ import java.util.function.Supplier; public class ShellController { private static final String TAG = ShellController.class.getSimpleName(); + private final Context mContext; private final ShellInit mShellInit; private final ShellCommandHandler mShellCommandHandler; private final ShellExecutor mMainExecutor; @@ -72,8 +74,11 @@ public class ShellController { private Configuration mLastConfiguration; - public ShellController(ShellInit shellInit, ShellCommandHandler shellCommandHandler, + public ShellController(Context context, + ShellInit shellInit, + ShellCommandHandler shellCommandHandler, ShellExecutor mainExecutor) { + mContext = context; mShellInit = shellInit; mShellCommandHandler = shellCommandHandler; mMainExecutor = mainExecutor; @@ -254,6 +259,16 @@ public class ShellController { } } + private void handleInit() { + SurfaceControlRegistry.createProcessInstance(mContext); + mShellInit.init(); + } + + private void handleDump(PrintWriter pw) { + mShellCommandHandler.dump(pw); + SurfaceControlRegistry.dump(100 /* limit */, false /* runGc */, pw); + } + public void dump(@NonNull PrintWriter pw, String prefix) { final String innerPrefix = prefix + " "; pw.println(prefix + TAG); @@ -279,7 +294,7 @@ public class ShellController { @Override public void onInit() { try { - mMainExecutor.executeBlocking(() -> mShellInit.init()); + mMainExecutor.executeBlocking(() -> ShellController.this.handleInit()); } catch (InterruptedException e) { throw new RuntimeException("Failed to initialize the Shell in 2s", e); } @@ -344,7 +359,7 @@ public class ShellController { @Override public void dump(PrintWriter pw) { try { - mMainExecutor.executeBlocking(() -> mShellCommandHandler.dump(pw)); + mMainExecutor.executeBlocking(() -> ShellController.this.handleDump(pw)); } catch (InterruptedException e) { throw new RuntimeException("Failed to dump the Shell in 2s", e); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java index 42633b722649..863b5ab73a7d 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java @@ -304,8 +304,8 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, return animateRecentsDuringSplit(mixed, info, startTransaction, finishTransaction, finishCallback); } else if (mixed.mType == MixedTransition.TYPE_KEYGUARD) { - return mKeyguardHandler.startAnimation( - transition, info, startTransaction, finishTransaction, finishCallback); + return animateKeyguard(mixed, info, startTransaction, finishTransaction, + finishCallback); } else { mActiveTransitions.remove(mixed); throw new IllegalStateException("Starting mixed animation without a known mixed type? " @@ -557,6 +557,27 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, return handled; } + private boolean animateKeyguard(@NonNull final MixedTransition mixed, + @NonNull TransitionInfo info, + @NonNull SurfaceControl.Transaction startTransaction, + @NonNull SurfaceControl.Transaction finishTransaction, + @NonNull Transitions.TransitionFinishCallback finishCallback) { + boolean consumed = mKeyguardHandler.startAnimation( + mixed.mTransition, info, startTransaction, finishTransaction, finishCallback); + if (!consumed) { + return false; + } + // Sync pip state. + if (mPipHandler != null) { + // We don't know when to apply `startTransaction` so use a separate transaction here. + // This should be fine because these surface properties are independent. + final SurfaceControl.Transaction t = new SurfaceControl.Transaction(); + mPipHandler.syncPipSurfaceState(info, t, finishTransaction); + t.apply(); + } + return true; + } + @Override public void mergeAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction t, @NonNull IBinder mergeTarget, diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java index f33b0778a1b2..d16b497b5aca 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java @@ -492,6 +492,10 @@ public class Transitions implements RemoteCallable<Transitions>, finishT.show(leash); } else if (mode == TRANSIT_CLOSE || mode == TRANSIT_TO_BACK) { finishT.hide(leash); + } else if (isOpening && mode == TRANSIT_CHANGE) { + // Just in case there is a race with another animation (eg. recents finish()). + // Changes are visible->visible so it's a problem if it isn't visible. + t.show(leash); } } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldTransitionHandler.java index 5d7b62905d3b..bb0eba6a0fc7 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldTransitionHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldTransitionHandler.java @@ -18,6 +18,8 @@ package com.android.wm.shell.unfold; import static android.view.WindowManager.TRANSIT_CHANGE; +import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_TRANSITIONS; + import android.os.IBinder; import android.view.SurfaceControl; import android.window.TransitionInfo; @@ -27,6 +29,7 @@ import android.window.WindowContainerTransaction; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import com.android.internal.protolog.common.ProtoLog; import com.android.wm.shell.common.TransactionPool; import com.android.wm.shell.sysui.ShellInit; import com.android.wm.shell.transition.Transitions; @@ -36,6 +39,7 @@ import com.android.wm.shell.unfold.ShellUnfoldProgressProvider.UnfoldListener; import com.android.wm.shell.unfold.animation.FullscreenUnfoldTaskAnimator; import com.android.wm.shell.unfold.animation.SplitTaskUnfoldAnimator; import com.android.wm.shell.unfold.animation.UnfoldTaskAnimator; +import com.android.wm.shell.util.TransitionUtil; import java.util.ArrayList; import java.util.List; @@ -105,8 +109,14 @@ public class UnfoldTransitionHandler implements TransitionHandler, UnfoldListene animator.clearTasks(); info.getChanges().forEach(change -> { - if (change.getTaskInfo() != null - && change.getMode() == TRANSIT_CHANGE + if (change.getTaskInfo() != null) { + ProtoLog.v(WM_SHELL_TRANSITIONS, + "startAnimation, check taskInfo: %s, mode: %s, isApplicableTask: %s", + change.getTaskInfo(), TransitionInfo.modeToString(change.getMode()), + animator.isApplicableTask(change.getTaskInfo())); + } + if (change.getTaskInfo() != null && (change.getMode() == TRANSIT_CHANGE + || TransitionUtil.isOpeningType(change.getMode())) && animator.isApplicableTask(change.getTaskInfo())) { animator.onTaskAppeared(change.getTaskInfo(), change.getLeash()); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/animation/SplitTaskUnfoldAnimator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/animation/SplitTaskUnfoldAnimator.java index 123bf3bfca2e..a4cf149cc3b5 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/animation/SplitTaskUnfoldAnimator.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/animation/SplitTaskUnfoldAnimator.java @@ -213,7 +213,7 @@ public class SplitTaskUnfoldAnimator implements UnfoldTaskAnimator, @Override public boolean isApplicableTask(TaskInfo taskInfo) { return taskInfo.hasParentTask() - && taskInfo.isVisible + && taskInfo.isRunning && taskInfo.realActivity != null // to filter out parents created by organizer && taskInfo.getWindowingMode() == WINDOWING_MODE_MULTI_WINDOW; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/util/TransitionUtil.java b/libs/WindowManager/Shell/src/com/android/wm/shell/util/TransitionUtil.java index 6d37e58c59ad..143b42a1ec96 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/util/TransitionUtil.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/util/TransitionUtil.java @@ -24,9 +24,7 @@ import static android.view.WindowManager.LayoutParams.INVALID_WINDOW_TYPE; import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER; import static android.view.WindowManager.TRANSIT_CHANGE; import static android.view.WindowManager.TRANSIT_CLOSE; -import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY; import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY; -import static android.view.WindowManager.TRANSIT_KEYGUARD_UNOCCLUDE; import static android.view.WindowManager.TRANSIT_OPEN; import static android.view.WindowManager.TRANSIT_TO_BACK; import static android.view.WindowManager.TRANSIT_TO_FRONT; @@ -178,7 +176,14 @@ public class TransitionUtil { } // Put all the OPEN/SHOW on top - if (TransitionUtil.isOpeningType(mode)) { + if ((change.getFlags() & FLAG_IS_WALLPAPER) != 0) { + // Wallpaper is always at the bottom, opening wallpaper on top of closing one. + if (mode == WindowManager.TRANSIT_OPEN || mode == WindowManager.TRANSIT_TO_FRONT) { + t.setLayer(leash, -zSplitLine + info.getChanges().size() - layer); + } else { + t.setLayer(leash, -zSplitLine - layer); + } + } else if (TransitionUtil.isOpeningType(mode)) { if (isOpening) { t.setLayer(leash, zSplitLine + info.getChanges().size() - layer); if ((change.getFlags() & FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT) == 0) { @@ -312,6 +317,7 @@ public class TransitionUtil { target.setWillShowImeOnTarget( (change.getFlags() & TransitionInfo.FLAG_WILL_IME_SHOWN) != 0); target.setRotationChange(change.getEndRotation() - change.getStartRotation()); + target.backgroundColor = change.getBackgroundColor(); return target; } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/TouchTrackerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/TouchTrackerTest.java deleted file mode 100644 index d62e6601723a..000000000000 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/TouchTrackerTest.java +++ /dev/null @@ -1,141 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.wm.shell.back; - -import static org.junit.Assert.assertEquals; - -import android.window.BackEvent; -import android.window.BackMotionEvent; - -import org.junit.Before; -import org.junit.Test; - -public class TouchTrackerTest { - private static final float FAKE_THRESHOLD = 400; - private static final float INITIAL_X_LEFT_EDGE = 5; - private static final float INITIAL_X_RIGHT_EDGE = FAKE_THRESHOLD - INITIAL_X_LEFT_EDGE; - private TouchTracker mTouchTracker; - - @Before - public void setUp() throws Exception { - mTouchTracker = new TouchTracker(); - mTouchTracker.setProgressThreshold(FAKE_THRESHOLD); - } - - @Test - public void generatesProgress_onStart() { - mTouchTracker.setGestureStartLocation(INITIAL_X_LEFT_EDGE, 0, BackEvent.EDGE_LEFT); - BackMotionEvent event = mTouchTracker.createStartEvent(null); - assertEquals(event.getProgress(), 0f, 0f); - } - - @Test - public void generatesProgress_leftEdge() { - mTouchTracker.setGestureStartLocation(INITIAL_X_LEFT_EDGE, 0, BackEvent.EDGE_LEFT); - float touchX = 10; - float velocityX = 0; - float velocityY = 0; - - // Pre-commit - mTouchTracker.update(touchX, 0, velocityX, velocityY); - assertEquals(getProgress(), (touchX - INITIAL_X_LEFT_EDGE) / FAKE_THRESHOLD, 0f); - - // Post-commit - touchX += 100; - mTouchTracker.setTriggerBack(true); - mTouchTracker.update(touchX, 0, velocityX, velocityY); - assertEquals(getProgress(), (touchX - INITIAL_X_LEFT_EDGE) / FAKE_THRESHOLD, 0f); - - // Cancel - touchX -= 10; - mTouchTracker.setTriggerBack(false); - mTouchTracker.update(touchX, 0, velocityX, velocityY); - assertEquals(getProgress(), 0, 0f); - - // Cancel more - touchX -= 10; - mTouchTracker.update(touchX, 0, velocityX, velocityY); - assertEquals(getProgress(), 0, 0f); - - // Restart - touchX += 10; - mTouchTracker.update(touchX, 0, velocityX, velocityY); - assertEquals(getProgress(), 0, 0f); - - // Restarted, but pre-commit - float restartX = touchX; - touchX += 10; - mTouchTracker.update(touchX, 0, velocityX, velocityY); - assertEquals(getProgress(), (touchX - restartX) / FAKE_THRESHOLD, 0f); - - // Restarted, post-commit - touchX += 10; - mTouchTracker.setTriggerBack(true); - mTouchTracker.update(touchX, 0, velocityX, velocityY); - assertEquals(getProgress(), (touchX - INITIAL_X_LEFT_EDGE) / FAKE_THRESHOLD, 0f); - } - - @Test - public void generatesProgress_rightEdge() { - mTouchTracker.setGestureStartLocation(INITIAL_X_RIGHT_EDGE, 0, BackEvent.EDGE_RIGHT); - float touchX = INITIAL_X_RIGHT_EDGE - 10; // Fake right edge - float velocityX = 0f; - float velocityY = 0f; - - // Pre-commit - mTouchTracker.update(touchX, 0, velocityX, velocityY); - assertEquals(getProgress(), (INITIAL_X_RIGHT_EDGE - touchX) / FAKE_THRESHOLD, 0f); - - // Post-commit - touchX -= 100; - mTouchTracker.setTriggerBack(true); - mTouchTracker.update(touchX, 0, velocityX, velocityY); - assertEquals(getProgress(), (INITIAL_X_RIGHT_EDGE - touchX) / FAKE_THRESHOLD, 0f); - - // Cancel - touchX += 10; - mTouchTracker.setTriggerBack(false); - mTouchTracker.update(touchX, 0, velocityX, velocityY); - assertEquals(getProgress(), 0, 0f); - - // Cancel more - touchX += 10; - mTouchTracker.update(touchX, 0, velocityX, velocityY); - assertEquals(getProgress(), 0, 0f); - - // Restart - touchX -= 10; - mTouchTracker.update(touchX, 0, velocityX, velocityY); - assertEquals(getProgress(), 0, 0f); - - // Restarted, but pre-commit - float restartX = touchX; - touchX -= 10; - mTouchTracker.update(touchX, 0, velocityX, velocityY); - assertEquals(getProgress(), (restartX - touchX) / FAKE_THRESHOLD, 0f); - - // Restarted, post-commit - touchX -= 10; - mTouchTracker.setTriggerBack(true); - mTouchTracker.update(touchX, 0, velocityX, velocityY); - assertEquals(getProgress(), (INITIAL_X_RIGHT_EDGE - touchX) / FAKE_THRESHOLD, 0f); - } - - private float getProgress() { - return mTouchTracker.createProgressEvent().getProgress(); - } -} diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/TouchTrackerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/TouchTrackerTest.kt new file mode 100644 index 000000000000..9088e8997e79 --- /dev/null +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/TouchTrackerTest.kt @@ -0,0 +1,181 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.wm.shell.back + +import android.util.MathUtils +import android.window.BackEvent +import org.junit.Assert.assertEquals +import org.junit.Test + +class TouchTrackerTest { + private fun linearTouchTracker(): TouchTracker = TouchTracker().apply { + setProgressThresholds(MAX_DISTANCE, MAX_DISTANCE, NON_LINEAR_FACTOR) + } + + private fun nonLinearTouchTracker(): TouchTracker = TouchTracker().apply { + setProgressThresholds(LINEAR_DISTANCE, MAX_DISTANCE, NON_LINEAR_FACTOR) + } + + private fun TouchTracker.assertProgress(expected: Float) { + val actualProgress = createProgressEvent().progress + assertEquals(expected, actualProgress, /* delta = */ 0f) + } + + @Test + fun generatesProgress_onStart() { + val linearTracker = linearTouchTracker() + linearTracker.setGestureStartLocation(INITIAL_X_LEFT_EDGE, 0f, BackEvent.EDGE_LEFT) + val event = linearTracker.createStartEvent(null) + assertEquals(0f, event.progress, 0f) + } + + @Test + fun generatesProgress_leftEdge() { + val linearTracker = linearTouchTracker() + linearTracker.setGestureStartLocation(INITIAL_X_LEFT_EDGE, 0f, BackEvent.EDGE_LEFT) + var touchX = 10f + val velocityX = 0f + val velocityY = 0f + + // Pre-commit + linearTracker.update(touchX, 0f, velocityX, velocityY) + linearTracker.assertProgress((touchX - INITIAL_X_LEFT_EDGE) / MAX_DISTANCE) + + // Post-commit + touchX += 100f + linearTracker.setTriggerBack(true) + linearTracker.update(touchX, 0f, velocityX, velocityY) + linearTracker.assertProgress((touchX - INITIAL_X_LEFT_EDGE) / MAX_DISTANCE) + + // Cancel + touchX -= 10f + linearTracker.setTriggerBack(false) + linearTracker.update(touchX, 0f, velocityX, velocityY) + linearTracker.assertProgress(0f) + + // Cancel more + touchX -= 10f + linearTracker.update(touchX, 0f, velocityX, velocityY) + linearTracker.assertProgress(0f) + + // Restart + touchX += 10f + linearTracker.update(touchX, 0f, velocityX, velocityY) + linearTracker.assertProgress(0f) + + // Restarted, but pre-commit + val restartX = touchX + touchX += 10f + linearTracker.update(touchX, 0f, velocityX, velocityY) + linearTracker.assertProgress((touchX - restartX) / MAX_DISTANCE) + + // Restarted, post-commit + touchX += 10f + linearTracker.setTriggerBack(true) + linearTracker.update(touchX, 0f, velocityX, velocityY) + linearTracker.assertProgress((touchX - INITIAL_X_LEFT_EDGE) / MAX_DISTANCE) + } + + @Test + fun generatesProgress_rightEdge() { + val linearTracker = linearTouchTracker() + linearTracker.setGestureStartLocation(INITIAL_X_RIGHT_EDGE, 0f, BackEvent.EDGE_RIGHT) + var touchX = INITIAL_X_RIGHT_EDGE - 10 // Fake right edge + val velocityX = 0f + val velocityY = 0f + val target = MAX_DISTANCE + + // Pre-commit + linearTracker.update(touchX, 0f, velocityX, velocityY) + linearTracker.assertProgress((INITIAL_X_RIGHT_EDGE - touchX) / target) + + // Post-commit + touchX -= 100f + linearTracker.setTriggerBack(true) + linearTracker.update(touchX, 0f, velocityX, velocityY) + linearTracker.assertProgress((INITIAL_X_RIGHT_EDGE - touchX) / target) + + // Cancel + touchX += 10f + linearTracker.setTriggerBack(false) + linearTracker.update(touchX, 0f, velocityX, velocityY) + linearTracker.assertProgress(0f) + + // Cancel more + touchX += 10f + linearTracker.update(touchX, 0f, velocityX, velocityY) + linearTracker.assertProgress(0f) + + // Restart + touchX -= 10f + linearTracker.update(touchX, 0f, velocityX, velocityY) + linearTracker.assertProgress(0f) + + // Restarted, but pre-commit + val restartX = touchX + touchX -= 10f + linearTracker.update(touchX, 0f, velocityX, velocityY) + linearTracker.assertProgress((restartX - touchX) / target) + + // Restarted, post-commit + touchX -= 10f + linearTracker.setTriggerBack(true) + linearTracker.update(touchX, 0f, velocityX, velocityY) + linearTracker.assertProgress((INITIAL_X_RIGHT_EDGE - touchX) / target) + } + + @Test + fun generatesNonLinearProgress_leftEdge() { + val nonLinearTracker = nonLinearTouchTracker() + nonLinearTracker.setGestureStartLocation(INITIAL_X_LEFT_EDGE, 0f, BackEvent.EDGE_LEFT) + var touchX = 10f + val velocityX = 0f + val velocityY = 0f + val linearTarget = LINEAR_DISTANCE + (MAX_DISTANCE - LINEAR_DISTANCE) * NON_LINEAR_FACTOR + + // Pre-commit: linear progress + nonLinearTracker.update(touchX, 0f, velocityX, velocityY) + nonLinearTracker.assertProgress((touchX - INITIAL_X_LEFT_EDGE) / linearTarget) + + // Post-commit: still linear progress + touchX += 100f + nonLinearTracker.setTriggerBack(true) + nonLinearTracker.update(touchX, 0f, velocityX, velocityY) + nonLinearTracker.assertProgress((touchX - INITIAL_X_LEFT_EDGE) / linearTarget) + + // still linear progress + touchX = INITIAL_X_LEFT_EDGE + LINEAR_DISTANCE + nonLinearTracker.update(touchX, 0f, velocityX, velocityY) + nonLinearTracker.assertProgress((touchX - INITIAL_X_LEFT_EDGE) / linearTarget) + + // non linear progress + touchX += 10 + nonLinearTracker.update(touchX, 0f, velocityX, velocityY) + val nonLinearTouch = (touchX - INITIAL_X_LEFT_EDGE) - LINEAR_DISTANCE + val nonLinearProgress = nonLinearTouch / NON_LINEAR_DISTANCE + val nonLinearTarget = MathUtils.lerp(linearTarget, MAX_DISTANCE, nonLinearProgress) + nonLinearTracker.assertProgress((touchX - INITIAL_X_LEFT_EDGE) / nonLinearTarget) + } + + companion object { + private const val MAX_DISTANCE = 500f + private const val LINEAR_DISTANCE = 400f + private const val NON_LINEAR_DISTANCE = MAX_DISTANCE - LINEAR_DISTANCE + private const val NON_LINEAR_FACTOR = 0.2f + private const val INITIAL_X_LEFT_EDGE = 5f + private const val INITIAL_X_RIGHT_EDGE = MAX_DISTANCE - INITIAL_X_LEFT_EDGE + } +}
\ No newline at end of file diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java index 04f2c99783da..85167cb97501 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java @@ -129,7 +129,7 @@ public class PipControllerTest extends ShellTestCase { return null; }).when(mMockExecutor).execute(any()); mShellInit = spy(new ShellInit(mMockExecutor)); - mShellController = spy(new ShellController(mShellInit, mMockShellCommandHandler, + mShellController = spy(new ShellController(mContext, mShellInit, mMockShellCommandHandler, mMockExecutor)); mPipController = new PipController(mContext, mShellInit, mMockShellCommandHandler, mShellController, mMockDisplayController, mMockPipAnimationController, diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java index b542fae060d1..2c69522413d5 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java @@ -107,7 +107,7 @@ public class RecentTasksControllerTest extends ShellTestCase { mMainExecutor = new TestShellExecutor(); when(mContext.getPackageManager()).thenReturn(mock(PackageManager.class)); mShellInit = spy(new ShellInit(mMainExecutor)); - mShellController = spy(new ShellController(mShellInit, mShellCommandHandler, + mShellController = spy(new ShellController(mContext, mShellInit, mShellCommandHandler, mMainExecutor)); mRecentTasksControllerReal = new RecentTasksController(mContext, mShellInit, mShellController, mShellCommandHandler, mTaskStackListener, mActivityTaskManager, diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java index c37a497c5d40..9189d3dd0327 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java @@ -111,7 +111,7 @@ public class SplitScreenControllerTests extends ShellTestCase { public void setup() { assumeTrue(ActivityTaskManager.supportsSplitScreenMultiWindow(mContext)); MockitoAnnotations.initMocks(this); - mShellController = spy(new ShellController(mShellInit, mShellCommandHandler, + mShellController = spy(new ShellController(mContext, mShellInit, mShellCommandHandler, mMainExecutor)); mSplitScreenController = spy(new SplitScreenController(mContext, mShellInit, mShellCommandHandler, mShellController, mTaskOrganizer, mSyncQueue, diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingWindowControllerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingWindowControllerTests.java index 10dec9ef12f9..a9082a6e2585 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingWindowControllerTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingWindowControllerTests.java @@ -81,7 +81,7 @@ public class StartingWindowControllerTests extends ShellTestCase { doReturn(mock(Display.class)).when(mDisplayManager).getDisplay(anyInt()); doReturn(mDisplayManager).when(mContext).getSystemService(eq(DisplayManager.class)); mShellInit = spy(new ShellInit(mMainExecutor)); - mShellController = spy(new ShellController(mShellInit, mShellCommandHandler, + mShellController = spy(new ShellController(mContext, mShellInit, mShellCommandHandler, mMainExecutor)); mController = new StartingWindowController(mContext, mShellInit, mShellController, mTaskOrganizer, mMainExecutor, mTypeAlgorithm, mIconProvider, mTransactionPool); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sysui/ShellControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sysui/ShellControllerTest.java index 8d92d0864338..7c520c34b29d 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sysui/ShellControllerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sysui/ShellControllerTest.java @@ -78,7 +78,7 @@ public class ShellControllerTest extends ShellTestCase { mConfigChangeListener = new TestConfigurationChangeListener(); mUserChangeListener = new TestUserChangeListener(); mExecutor = new TestShellExecutor(); - mController = new ShellController(mShellInit, mShellCommandHandler, mExecutor); + mController = new ShellController(mContext, mShellInit, mShellCommandHandler, mExecutor); mController.onConfigurationChanged(getConfigurationCopy()); } diff --git a/libs/hwui/MemoryPolicy.h b/libs/hwui/MemoryPolicy.h index 139cdde5c0d2..347daf34f52a 100644 --- a/libs/hwui/MemoryPolicy.h +++ b/libs/hwui/MemoryPolicy.h @@ -31,6 +31,12 @@ enum class TrimLevel { RUNNING_MODERATE = 5, }; +enum class CacheTrimLevel { + ALL_CACHES = 0, + FONT_CACHE = 1, + RESOURCE_CACHE = 2, +}; + struct MemoryPolicy { // The initial scale factor applied to the display resolution. The default is 1, but // lower values may be used to start with a smaller initial cache size. The cache will diff --git a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp index 6a7411f5d859..d04de37f6961 100644 --- a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp +++ b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp @@ -362,6 +362,10 @@ static void android_view_ThreadedRenderer_trimMemory(JNIEnv* env, jobject clazz, RenderProxy::trimMemory(level); } +static void android_view_ThreadedRenderer_trimCaches(JNIEnv* env, jobject clazz, jint level) { + RenderProxy::trimCaches(level); +} + static void android_view_ThreadedRenderer_overrideProperty(JNIEnv* env, jobject clazz, jstring name, jstring value) { const char* nameCharArray = env->GetStringUTFChars(name, NULL); @@ -1018,6 +1022,7 @@ static const JNINativeMethod gMethods[] = { (void*)android_view_ThreadedRenderer_notifyCallbackPending}, {"nNotifyExpensiveFrame", "(J)V", (void*)android_view_ThreadedRenderer_notifyExpensiveFrame}, + {"nTrimCaches", "(I)V", (void*)android_view_ThreadedRenderer_trimCaches}, }; static JavaVM* mJvm = nullptr; diff --git a/libs/hwui/renderthread/CacheManager.cpp b/libs/hwui/renderthread/CacheManager.cpp index c00a2707e0a2..babce88b8e1e 100644 --- a/libs/hwui/renderthread/CacheManager.cpp +++ b/libs/hwui/renderthread/CacheManager.cpp @@ -139,6 +139,25 @@ void CacheManager::trimMemory(TrimLevel mode) { } } +void CacheManager::trimCaches(CacheTrimLevel mode) { + switch (mode) { + case CacheTrimLevel::FONT_CACHE: + SkGraphics::PurgeFontCache(); + break; + case CacheTrimLevel::RESOURCE_CACHE: + SkGraphics::PurgeResourceCache(); + break; + case CacheTrimLevel::ALL_CACHES: + SkGraphics::PurgeAllCaches(); + if (mGrContext) { + mGrContext->purgeUnlockedResources(false); + } + break; + default: + break; + } +} + void CacheManager::trimStaleResources() { if (!mGrContext) { return; diff --git a/libs/hwui/renderthread/CacheManager.h b/libs/hwui/renderthread/CacheManager.h index d21ac9badc43..5e43ac209696 100644 --- a/libs/hwui/renderthread/CacheManager.h +++ b/libs/hwui/renderthread/CacheManager.h @@ -48,6 +48,7 @@ public: void configureContext(GrContextOptions* context, const void* identity, ssize_t size); #endif void trimMemory(TrimLevel mode); + void trimCaches(CacheTrimLevel mode); void trimStaleResources(); void dumpMemoryUsage(String8& log, const RenderState* renderState = nullptr); void getMemoryUsage(size_t* cpuUsage, size_t* gpuUsage); diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp index 31b4b203c670..224c878bf43d 100644 --- a/libs/hwui/renderthread/RenderProxy.cpp +++ b/libs/hwui/renderthread/RenderProxy.cpp @@ -231,6 +231,15 @@ void RenderProxy::trimMemory(int level) { } } +void RenderProxy::trimCaches(int level) { + // Avoid creating a RenderThread to do a trimMemory. + if (RenderThread::hasInstance()) { + RenderThread& thread = RenderThread::getInstance(); + const auto trimLevel = static_cast<CacheTrimLevel>(level); + thread.queue().post([&thread, trimLevel]() { thread.trimCaches(trimLevel); }); + } +} + void RenderProxy::purgeCaches() { if (RenderThread::hasInstance()) { RenderThread& thread = RenderThread::getInstance(); diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h index 82072a6e2499..47c1b0cd28e5 100644 --- a/libs/hwui/renderthread/RenderProxy.h +++ b/libs/hwui/renderthread/RenderProxy.h @@ -105,6 +105,7 @@ public: void destroyHardwareResources(); static void trimMemory(int level); + static void trimCaches(int level); static void purgeCaches(); static void overrideProperty(const char* name, const char* value); diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp index 9ba67a2110c1..eb28c080c056 100644 --- a/libs/hwui/renderthread/RenderThread.cpp +++ b/libs/hwui/renderthread/RenderThread.cpp @@ -521,6 +521,11 @@ void RenderThread::trimMemory(TrimLevel level) { cacheManager().trimMemory(level); } +void RenderThread::trimCaches(CacheTrimLevel level) { + ATRACE_CALL(); + cacheManager().trimCaches(level); +} + } /* namespace renderthread */ } /* namespace uirenderer */ } /* namespace android */ diff --git a/libs/hwui/renderthread/RenderThread.h b/libs/hwui/renderthread/RenderThread.h index c77cd4134d1e..79e57de9d66f 100644 --- a/libs/hwui/renderthread/RenderThread.h +++ b/libs/hwui/renderthread/RenderThread.h @@ -174,6 +174,7 @@ public: } void trimMemory(TrimLevel level); + void trimCaches(CacheTrimLevel level); /** * isCurrent provides a way to query, if the caller is running on |