diff options
Diffstat (limited to 'libs/WindowManager/Shell')
39 files changed, 749 insertions, 274 deletions
diff --git a/libs/WindowManager/Shell/res/values-fa/strings.xml b/libs/WindowManager/Shell/res/values-fa/strings.xml index ca7d077e0ef0..f646039df1f8 100644 --- a/libs/WindowManager/Shell/res/values-fa/strings.xml +++ b/libs/WindowManager/Shell/res/values-fa/strings.xml @@ -45,7 +45,7 @@ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"٪۵۰ بالا"</string> <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"٪۳۰ بالا"</string> <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"تمامصفحه پایین"</string> - <string name="one_handed_tutorial_title" msgid="4583241688067426350">"استفاده از «حالت تک حرکت»"</string> + <string name="one_handed_tutorial_title" msgid="4583241688067426350">"استفاده از حالت یکدستی"</string> <string name="one_handed_tutorial_description" msgid="3486582858591353067">"برای خارج شدن، از پایین صفحهنمایش تند بهطرف بالا بکشید یا در هر جایی از بالای برنامه که میخواهید ضربه بزنید"</string> <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"آغاز «حالت تک حرکت»"</string> <string name="accessibility_action_stop_one_handed" msgid="1369940261782179442">"خروج از «حالت تک حرکت»"</string> diff --git a/libs/WindowManager/Shell/res/values-nl/strings.xml b/libs/WindowManager/Shell/res/values-nl/strings.xml index 69fc25a378cf..06aaad7d65ca 100644 --- a/libs/WindowManager/Shell/res/values-nl/strings.xml +++ b/libs/WindowManager/Shell/res/values-nl/strings.xml @@ -45,10 +45,10 @@ <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"Bovenste scherm 50%"</string> <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"Bovenste scherm 30%"</string> <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Onderste scherm op volledig scherm"</string> - <string name="one_handed_tutorial_title" msgid="4583241688067426350">"Bediening met één hand gebruiken"</string> + <string name="one_handed_tutorial_title" msgid="4583241688067426350">"Bediening met 1 hand gebruiken"</string> <string name="one_handed_tutorial_description" msgid="3486582858591353067">"Als je wilt afsluiten, swipe je omhoog vanaf de onderkant van het scherm of tik je ergens boven de app"</string> - <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Bediening met één hand starten"</string> - <string name="accessibility_action_stop_one_handed" msgid="1369940261782179442">"Bediening met één hand afsluiten"</string> + <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Bediening met 1 hand starten"</string> + <string name="accessibility_action_stop_one_handed" msgid="1369940261782179442">"Bediening met 1 hand afsluiten"</string> <string name="bubbles_settings_button_description" msgid="1301286017420516912">"Instellingen voor <xliff:g id="APP_NAME">%1$s</xliff:g>-bubbels"</string> <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"Overloop"</string> <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"Weer toevoegen aan stack"</string> diff --git a/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml b/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml index 0af8d24a1783..aa666531996d 100644 --- a/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml +++ b/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml @@ -46,7 +46,7 @@ <string name="accessibility_action_divider_top_30" msgid="3572788224908570257">"以 30% 的螢幕空間顯示頂端畫面"</string> <string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"以全螢幕顯示底部畫面"</string> <string name="one_handed_tutorial_title" msgid="4583241688067426350">"使用單手模式"</string> - <string name="one_handed_tutorial_description" msgid="3486582858591353067">"如要退出,請從螢幕底部向上滑動,或輕觸應用程式上的任何位置"</string> + <string name="one_handed_tutorial_description" msgid="3486582858591353067">"如要退出,請從螢幕底部向上滑動,或輕觸應用程式上方的任何位置"</string> <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"啟動單手模式"</string> <string name="accessibility_action_stop_one_handed" msgid="1369940261782179442">"結束單手模式"</string> <string name="bubbles_settings_button_description" msgid="1301286017420516912">"「<xliff:g id="APP_NAME">%1$s</xliff:g>」對話框的設定"</string> diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java index 4b1955e56a6c..8cdb434d4f74 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java @@ -62,7 +62,8 @@ import java.util.function.Consumer; * Unified task organizer for all components in the shell. * TODO(b/167582004): may consider consolidating this class and TaskOrganizer */ -public class ShellTaskOrganizer extends TaskOrganizer { +public class ShellTaskOrganizer extends TaskOrganizer implements + SizeCompatUIController.SizeCompatUICallback { // Intentionally using negative numbers here so the positive numbers can be used // for task id specific listeners that will be added later. @@ -158,6 +159,9 @@ public class ShellTaskOrganizer extends TaskOrganizer { Context context, @Nullable SizeCompatUIController sizeCompatUI) { super(taskOrganizerController, mainExecutor); mSizeCompatUI = sizeCompatUI; + if (sizeCompatUI != null) { + sizeCompatUI.setSizeCompatUICallback(this); + } } @Override @@ -481,6 +485,17 @@ public class ShellTaskOrganizer extends TaskOrganizer { } } + @Override + public void onSizeCompatRestartButtonClicked(int taskId) { + final TaskAppearedInfo info; + synchronized (mLock) { + info = mTasks.get(taskId); + } + if (info != null) { + restartTaskTopActivityProcessIfVisible(info.getTaskInfo().token); + } + } + /** * Notifies {@link SizeCompatUIController} about the size compat info changed on the give Task * to update the UI accordingly. @@ -499,13 +514,12 @@ public class ShellTaskOrganizer extends TaskOrganizer { if (taskListener == null || !taskListener.supportSizeCompatUI() || !taskInfo.topActivityInSizeCompat) { mSizeCompatUI.onSizeCompatInfoChanged(taskInfo.displayId, taskInfo.taskId, - null /* taskConfig */, null /* sizeCompatActivity*/, - null /* taskListener */); + null /* taskConfig */, null /* taskListener */); return; } mSizeCompatUI.onSizeCompatInfoChanged(taskInfo.displayId, taskInfo.taskId, - taskInfo.configuration, taskInfo.topActivityToken, taskListener); + taskInfo.configuration, taskListener); } private TaskListener getTaskListener(RunningTaskInfo runningTaskInfo) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java index b2ac61cf3f6e..a7996f056785 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java @@ -42,8 +42,6 @@ import android.view.animation.PathInterpolator; import androidx.annotation.BinderThread; import androidx.annotation.VisibleForTesting; -import com.android.internal.inputmethod.Completable; -import com.android.internal.inputmethod.ResultCallbacks; import com.android.internal.view.IInputMethodManager; import java.util.ArrayList; @@ -159,6 +157,14 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged } } + private void dispatchImeControlTargetChanged(int displayId, boolean controlling) { + synchronized (mPositionProcessors) { + for (ImePositionProcessor pp : mPositionProcessors) { + pp.onImeControlTargetChanged(displayId, controlling); + } + } + } + private void dispatchVisibilityChanged(int displayId, boolean isShowing) { synchronized (mPositionProcessors) { for (ImePositionProcessor pp : mPositionProcessors) { @@ -237,38 +243,52 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged protected void insetsControlChanged(InsetsState insetsState, InsetsSourceControl[] activeControls) { insetsChanged(insetsState); + InsetsSourceControl imeSourceControl = null; if (activeControls != null) { for (InsetsSourceControl activeControl : activeControls) { if (activeControl == null) { continue; } if (activeControl.getType() == InsetsState.ITYPE_IME) { - final Point lastSurfacePosition = mImeSourceControl != null - ? mImeSourceControl.getSurfacePosition() : null; - final boolean positionChanged = - !activeControl.getSurfacePosition().equals(lastSurfacePosition); - final boolean leashChanged = - !haveSameLeash(mImeSourceControl, activeControl); - mImeSourceControl = activeControl; - if (mAnimation != null) { - if (positionChanged) { - startAnimation(mImeShowing, true /* forceRestart */); - } - } else { - if (leashChanged) { - applyVisibilityToLeash(); - } - if (!mImeShowing) { - removeImeSurface(); - } - } + imeSourceControl = activeControl; + } + } + } + + final boolean hadImeSourceControl = mImeSourceControl != null; + final boolean hasImeSourceControl = imeSourceControl != null; + if (hadImeSourceControl != hasImeSourceControl) { + dispatchImeControlTargetChanged(mDisplayId, hasImeSourceControl); + } + + if (hasImeSourceControl) { + final Point lastSurfacePosition = mImeSourceControl != null + ? mImeSourceControl.getSurfacePosition() : null; + final boolean positionChanged = + !imeSourceControl.getSurfacePosition().equals(lastSurfacePosition); + final boolean leashChanged = + !haveSameLeash(mImeSourceControl, imeSourceControl); + if (mAnimation != null) { + if (positionChanged) { + startAnimation(mImeShowing, true /* forceRestart */); + } + } else { + if (leashChanged) { + applyVisibilityToLeash(imeSourceControl); } + if (!mImeShowing) { + removeImeSurface(); + } + } + if (mImeSourceControl != null) { + mImeSourceControl.release(SurfaceControl::release); } + mImeSourceControl = imeSourceControl; } } - private void applyVisibilityToLeash() { - SurfaceControl leash = mImeSourceControl.getLeash(); + private void applyVisibilityToLeash(InsetsSourceControl imeSourceControl) { + SurfaceControl leash = imeSourceControl.getLeash(); if (leash != null) { SurfaceControl.Transaction t = mTransactionPool.acquire(); if (mImeShowing) { @@ -518,9 +538,7 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged try { // Remove the IME surface to make the insets invisible for // non-client controlled insets. - final Completable.Void value = Completable.createVoid(); - imms.removeImeSurface(ResultCallbacks.of(value)); - Completable.getResult(value); + imms.removeImeSurface(); } catch (RemoteException e) { Slog.e(TAG, "Failed to remove IME surface.", e); } @@ -580,6 +598,15 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged } /** + * Called when the IME control target changed. So that the processor can restore its + * adjusted layout when the IME insets is not controlling by the current controller anymore. + * + * @param controlling indicates whether the current controller is controlling IME insets. + */ + default void onImeControlTargetChanged(int displayId, boolean controlling) { + } + + /** * Called when the IME visibility changed. * * @param isShowing {@code true} if the IME is shown. diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java index ef113dc5e10a..97c89d042be0 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java @@ -134,6 +134,18 @@ public class SystemWindows { } /** + * Sets the accessibility window for the given {@param shellRootLayer}. + */ + public void setShellRootAccessibilityWindow(int displayId, + @WindowManager.ShellRootLayer int shellRootLayer, View view) { + PerDisplay pd = mPerDisplay.get(displayId); + if (pd == null) { + return; + } + pd.setShellRootAccessibilityWindow(shellRootLayer, view); + } + + /** * Sets the touchable region of a view's window. This will be cropped to the window size. * @param view * @param region @@ -202,15 +214,9 @@ public class SystemWindows { attrs.flags |= FLAG_HARDWARE_ACCELERATED; viewRoot.setView(view, attrs); mViewRoots.put(view, viewRoot); - - try { - mWmService.setShellRootAccessibilityWindow(mDisplayId, shellRootLayer, - viewRoot.getWindowToken()); - } catch (RemoteException e) { - Slog.e(TAG, "Error setting accessibility window for " + mDisplayId + ":" - + shellRootLayer, e); - } + setShellRootAccessibilityWindow(shellRootLayer, view); } + SysUiWindowManager addRoot(@WindowManager.ShellRootLayer int shellRootLayer) { SysUiWindowManager wwm = mWwms.get(shellRootLayer); if (wwm != null) { @@ -240,6 +246,21 @@ public class SystemWindows { return wwm.mContainerWindow; } + void setShellRootAccessibilityWindow(@WindowManager.ShellRootLayer int shellRootLayer, + View view) { + SysUiWindowManager wwm = mWwms.get(shellRootLayer); + if (wwm == null) { + return; + } + try { + mWmService.setShellRootAccessibilityWindow(mDisplayId, shellRootLayer, + view != null ? mViewRoots.get(view).getWindowToken() : null); + } catch (RemoteException e) { + Slog.e(TAG, "Error setting accessibility window for " + mDisplayId + ":" + + shellRootLayer, e); + } + } + void updateConfiguration(Configuration configuration) { for (int i = 0; i < mWwms.size(); ++i) { mWwms.valueAt(i).updateConfiguration(configuration); 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 e42f511eb391..5b158d2063ba 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 @@ -366,6 +366,7 @@ public final class SplitLayout { private final int mDisplayId; + private boolean mImeShown; private int mYOffsetForIme; private float mDimValue1; private float mDimValue2; @@ -392,6 +393,7 @@ public final class SplitLayout { if (!mInitialized || imeTargetPosition == SPLIT_POSITION_UNDEFINED) return 0; mStartImeTop = showing ? hiddenTop : shownTop; mEndImeTop = showing ? shownTop : hiddenTop; + mImeShown = showing; // Update target dim values mLastDim1 = mDimValue1; @@ -414,7 +416,7 @@ public final class SplitLayout { mSplitWindowManager.setInteractive( !showing || imeTargetPosition == SPLIT_POSITION_UNDEFINED); - return 0; + return needOffset ? IME_ANIMATION_NO_ALPHA : 0; } @Override @@ -432,6 +434,17 @@ public final class SplitLayout { mSplitLayoutHandler.onBoundsChanging(SplitLayout.this); } + @Override + public void onImeControlTargetChanged(int displayId, boolean controlling) { + if (displayId != mDisplayId) return; + // Restore the split layout when wm-shell is not controlling IME insets anymore. + if (!controlling && mImeShown) { + reset(); + mSplitWindowManager.setInteractive(true); + mSplitLayoutHandler.onBoundsChanging(SplitLayout.this); + } + } + private int getTargetYOffset() { final int desireOffset = Math.abs(mEndImeTop - mStartImeTop); // Make sure to keep at least 30% visible for the top split. @@ -461,8 +474,10 @@ public final class SplitLayout { } private void reset() { - mYOffsetForIme = 0; - mDimValue1 = mDimValue2 = 0.0f; + mImeShown = false; + mYOffsetForIme = mLastYOffset = mTargetYOffset = 0; + mDimValue1 = mLastDim1 = mTargetDim1 = 0.0f; + mDimValue2 = mLastDim2 = mTargetDim2 = 0.0f; } /* Adjust bounds with IME offset. */ diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutOrganizer.java index 53dd391a01af..75a1ddeccb22 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutOrganizer.java @@ -19,8 +19,8 @@ package com.android.wm.shell.hidedisplaycutout; import static android.view.Display.DEFAULT_DISPLAY; import android.content.Context; +import android.content.res.Configuration; import android.graphics.Insets; -import android.graphics.Point; import android.graphics.Rect; import android.util.ArrayMap; import android.util.Log; @@ -40,13 +40,12 @@ import androidx.annotation.NonNull; import androidx.annotation.VisibleForTesting; import com.android.internal.R; -import com.android.wm.shell.common.DisplayChangeController; import com.android.wm.shell.common.DisplayController; +import com.android.wm.shell.common.DisplayLayout; import com.android.wm.shell.common.ShellExecutor; import java.io.PrintWriter; import java.util.List; -import java.util.concurrent.Executor; /** * Manages the display areas of hide display cutout feature. @@ -76,19 +75,29 @@ class HideDisplayCutoutOrganizer extends DisplayAreaOrganizer { @VisibleForTesting int mRotation; - /** - * Handles rotation based on OnDisplayChangingListener callback. - */ - private final DisplayChangeController.OnDisplayChangingListener mRotationController = - (display, fromRotation, toRotation, wct) -> { - mRotation = toRotation; - updateBoundsAndOffsets(true /* enable */); - final SurfaceControl.Transaction t = new SurfaceControl.Transaction(); - applyAllBoundsAndOffsets(wct, t); - // Only apply t here since the server will do the wct.apply when the method - // finishes. - t.apply(); - }; + private final DisplayController.OnDisplaysChangedListener mListener = + new DisplayController.OnDisplaysChangedListener() { + @Override + public void onDisplayConfigurationChanged(int displayId, Configuration newConfig) { + if (displayId != DEFAULT_DISPLAY) { + return; + } + DisplayLayout displayLayout = + mDisplayController.getDisplayLayout(DEFAULT_DISPLAY); + if (displayLayout == null) { + return; + } + final boolean rotationChanged = mRotation != displayLayout.rotation(); + mRotation = displayLayout.rotation(); + if (rotationChanged || isDisplayBoundsChanged()) { + updateBoundsAndOffsets(true /* enabled */); + final WindowContainerTransaction wct = new WindowContainerTransaction(); + final SurfaceControl.Transaction t = new SurfaceControl.Transaction(); + applyAllBoundsAndOffsets(wct, t); + applyTransaction(wct, t); + } + } + }; HideDisplayCutoutOrganizer(Context context, DisplayController displayController, ShellExecutor mainExecutor) { @@ -154,10 +163,10 @@ class HideDisplayCutoutOrganizer extends DisplayAreaOrganizer { * Enables hide display cutout. */ void enableHideDisplayCutout() { - mDisplayController.addDisplayChangingController(mRotationController); - final Display display = mDisplayController.getDisplay(DEFAULT_DISPLAY); - if (display != null) { - mRotation = display.getRotation(); + mDisplayController.addDisplayWindowListener(mListener); + final DisplayLayout displayLayout = mDisplayController.getDisplayLayout(DEFAULT_DISPLAY); + if (displayLayout != null) { + mRotation = displayLayout.rotation(); } final List<DisplayAreaAppearedInfo> displayAreaInfos = registerOrganizer(DisplayAreaOrganizer.FEATURE_HIDE_DISPLAY_CUTOUT); @@ -174,7 +183,7 @@ class HideDisplayCutoutOrganizer extends DisplayAreaOrganizer { */ void disableHideDisplayCutout() { updateBoundsAndOffsets(false /* enabled */); - mDisplayController.removeDisplayChangingController(mRotationController); + mDisplayController.removeDisplayWindowListener(mListener); unregisterOrganizer(); } @@ -193,23 +202,35 @@ class HideDisplayCutoutOrganizer extends DisplayAreaOrganizer { @VisibleForTesting Rect getDisplayBoundsOfNaturalOrientation() { - Point realSize = new Point(0, 0); - final Display display = mDisplayController.getDisplay(DEFAULT_DISPLAY); - if (display != null) { - display.getRealSize(realSize); + final DisplayLayout displayLayout = mDisplayController.getDisplayLayout(DEFAULT_DISPLAY); + if (displayLayout == null) { + return new Rect(); } final boolean isDisplaySizeFlipped = isDisplaySizeFlipped(); return new Rect( 0, 0, - isDisplaySizeFlipped ? realSize.y : realSize.x, - isDisplaySizeFlipped ? realSize.x : realSize.y); + isDisplaySizeFlipped ? displayLayout.height() : displayLayout.width(), + isDisplaySizeFlipped ? displayLayout.width() : displayLayout.height()); } private boolean isDisplaySizeFlipped() { return mRotation == Surface.ROTATION_90 || mRotation == Surface.ROTATION_270; } + private boolean isDisplayBoundsChanged() { + final DisplayLayout displayLayout = mDisplayController.getDisplayLayout(DEFAULT_DISPLAY); + if (displayLayout == null) { + return false; + } + final boolean isDisplaySizeFlipped = isDisplaySizeFlipped(); + final int width = isDisplaySizeFlipped ? displayLayout.height() : displayLayout.width(); + final int height = isDisplaySizeFlipped ? displayLayout.width() : displayLayout.height(); + return mDefaultDisplayBounds.isEmpty() + || mDefaultDisplayBounds.width() != width + || mDefaultDisplayBounds.height() != height; + } + /** * Updates bounds and offsets according to current state. * @@ -237,7 +258,6 @@ class HideDisplayCutoutOrganizer extends DisplayAreaOrganizer { mCurrentDisplayBounds.right); } mCurrentDisplayBounds.inset(mCurrentCutoutInsets); - // Replace the top bound with the max(status bar height, cutout height) if there is // cutout on the top side. mStatusBarHeight = getStatusBarHeight(); @@ -256,7 +276,7 @@ class HideDisplayCutoutOrganizer extends DisplayAreaOrganizer { } private void initDefaultValuesIfNeeded() { - if (!mDefaultDisplayBounds.isEmpty()) { + if (!isDisplayBoundsChanged()) { return; } mDefaultDisplayBounds.set(getDisplayBoundsOfNaturalOrientation()); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/DividerImeController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/DividerImeController.java index 23171bb9575c..aced072c8c71 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/DividerImeController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/DividerImeController.java @@ -91,7 +91,6 @@ class DividerImeController implements DisplayImeController.ImePositionProcessor private boolean mPaused = true; private boolean mPausedTargetAdjusted = false; - private boolean mAdjustedWhileHidden = false; DividerImeController(LegacySplitScreenTaskListener splits, TransactionPool pool, ShellExecutor mainExecutor, TaskOrganizer taskOrganizer) { @@ -123,7 +122,6 @@ class DividerImeController implements DisplayImeController.ImePositionProcessor void reset() { mPaused = true; mPausedTargetAdjusted = false; - mAdjustedWhileHidden = false; mAnimation = null; mAdjusted = mTargetAdjusted = false; mImeWasShown = mTargetShown = false; @@ -140,6 +138,19 @@ class DividerImeController implements DisplayImeController.ImePositionProcessor ? ADJUSTED_NONFOCUS_DIM : 0.f; } + + @Override + public void onImeControlTargetChanged(int displayId, boolean controlling) { + // Restore the split layout when wm-shell is not controlling IME insets anymore. + if (!controlling && mTargetShown) { + mPaused = false; + mTargetAdjusted = mTargetShown = false; + mTargetPrimaryDim = mTargetSecondaryDim = 0.f; + updateImeAdjustState(true /* force */); + startAsyncAnimation(); + } + } + @Override @ImeAnimationFlags public int onImeStartPositioning(int displayId, int hiddenTop, int shownTop, @@ -151,8 +162,7 @@ class DividerImeController implements DisplayImeController.ImePositionProcessor mShownTop = shownTop; mTargetShown = imeShouldShow; mSecondaryHasFocus = getSecondaryHasFocus(displayId); - final boolean splitIsVisible = !getView().isHidden(); - final boolean targetAdjusted = splitIsVisible && imeShouldShow && mSecondaryHasFocus + final boolean targetAdjusted = imeShouldShow && mSecondaryHasFocus && !imeIsFloating && !getLayout().mDisplayLayout.isLandscape() && !mSplits.mSplitScreenController.isMinimized(); if (mLastAdjustTop < 0) { @@ -176,7 +186,7 @@ class DividerImeController implements DisplayImeController.ImePositionProcessor } mTargetAdjusted = targetAdjusted; updateDimTargets(); - if (DEBUG) Slog.d(TAG, " ime starting. vis:" + splitIsVisible + " " + dumpState()); + if (DEBUG) Slog.d(TAG, " ime starting. " + dumpState()); if (mAnimation != null || (mImeWasShown && imeShouldShow && mTargetAdjusted != mAdjusted)) { // We need to animate adjustment independently of the IME position, so @@ -184,13 +194,8 @@ class DividerImeController implements DisplayImeController.ImePositionProcessor // different split's editor has gained focus while the IME is still visible. startAsyncAnimation(); } - if (splitIsVisible) { - // If split is hidden, we don't want to trigger any relayouts that would cause the - // divider to show again. - updateImeAdjustState(); - } else { - mAdjustedWhileHidden = true; - } + updateImeAdjustState(); + return (mTargetAdjusted || mAdjusted) ? IME_ANIMATION_NO_ALPHA : 0; } @@ -256,11 +261,6 @@ class DividerImeController implements DisplayImeController.ImePositionProcessor mSplits.mSplitScreenController.setAdjustedForIme(mTargetShown && !mPaused); } - public void updateAdjustForIme() { - updateImeAdjustState(mAdjustedWhileHidden); - mAdjustedWhileHidden = false; - } - @Override public void onImePositionChanged(int displayId, int imeTop, SurfaceControl.Transaction t) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/DividerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/DividerView.java index 7e4010d5fa6f..362b40f33e89 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/DividerView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/DividerView.java @@ -408,7 +408,7 @@ public class DividerView extends FrameLayout implements OnTouchListener, } boolean isHidden() { - return mSurfaceHidden; + return getVisibility() != View.VISIBLE || mSurfaceHidden; } /** Starts dragging the divider bar. */ diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenController.java index 261ff2f8de83..80ab166d0649 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenController.java @@ -146,10 +146,8 @@ public class LegacySplitScreenController implements DisplayController.OnDisplays new LegacySplitDisplayLayout(mContext, displayLayout, mSplits); sdl.rotateTo(toRotation); mRotateSplitLayout = sdl; - final int position = isDividerVisible() - ? (mMinimized ? mView.mSnapTargetBeforeMinimized.position - : mView.getCurrentPosition()) - // snap resets to middle target when not in split-mode + // snap resets to middle target when not minimized and rotation changed. + final int position = mMinimized ? mView.mSnapTargetBeforeMinimized.position : sdl.getSnapAlgorithm().getMiddleTarget().position; DividerSnapAlgorithm snap = sdl.getSnapAlgorithm(); final DividerSnapAlgorithm.SnapTarget target = @@ -229,9 +227,6 @@ public class LegacySplitScreenController implements DisplayController.OnDisplays return; } mView.setHidden(showing); - if (!showing) { - mImePositionProcessor.updateAdjustForIme(); - } mIsKeyguardShowing = showing; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java index b5c54023c492..1cc7ed3afcf7 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java @@ -95,7 +95,6 @@ public class OneHandedController implements RemoteCallable<OneHandedController> private final OneHandedTouchHandler mTouchHandler; private final OneHandedState mState; private final OneHandedTutorialHandler mTutorialHandler; - private final OneHandedUiEventLogger mOneHandedUiEventLogger; private final TaskStackListenerImpl mTaskStackListener; private final IOverlayManager mOverlayManager; private final ShellExecutor mMainExecutor; @@ -105,6 +104,7 @@ public class OneHandedController implements RemoteCallable<OneHandedController> private OneHandedEventCallback mEventCallback; private OneHandedDisplayAreaOrganizer mDisplayAreaOrganizer; private OneHandedBackgroundPanelOrganizer mBackgroundPanelOrganizer; + private OneHandedUiEventLogger mOneHandedUiEventLogger; /** * Handle rotation based on OnDisplayChangingListener callback @@ -115,6 +115,8 @@ public class OneHandedController implements RemoteCallable<OneHandedController> return; } mDisplayAreaOrganizer.onRotateDisplay(mContext, toRotation, wct); + mOneHandedUiEventLogger.writeEvent( + OneHandedUiEventLogger.EVENT_ONE_HANDED_TRIGGER_ROTATION_OUT); }; private final DisplayController.OnDisplaysChangedListener mDisplaysChangedListener = @@ -498,6 +500,11 @@ public class OneHandedController implements RemoteCallable<OneHandedController> @VisibleForTesting void onActivatedActionChanged() { + if (!isShortcutEnabled()) { + Slog.w(TAG, "Shortcut not enabled, skip onActivatedActionChanged()"); + return; + } + if (!isOneHandedEnabled()) { final boolean success = mOneHandedSettingsUtil.setOneHandedModeEnabled( mContext.getContentResolver(), 1 /* Enabled for shortcut */, mUserId); @@ -586,6 +593,10 @@ public class OneHandedController implements RemoteCallable<OneHandedController> mContext.getContentResolver(), mUserId); setSwipeToNotificationEnabled(enabled); + mOneHandedUiEventLogger.writeEvent(enabled + ? OneHandedUiEventLogger.EVENT_ONE_HANDED_SETTINGS_SHOW_NOTIFICATION_ENABLED_ON + : OneHandedUiEventLogger.EVENT_ONE_HANDED_SETTINGS_SHOW_NOTIFICATION_ENABLED_OFF); + // Also checks one handed mode settings since they all need gesture overlay. setEnabledGesturalOverlay( enabled || mOneHandedSettingsUtil.getSettingsOneHandedModeEnabled( @@ -608,6 +619,11 @@ public class OneHandedController implements RemoteCallable<OneHandedController> } @VisibleForTesting + boolean isShortcutEnabled() { + return mOneHandedSettingsUtil.getShortcutEnabled(mContext.getContentResolver(), mUserId); + } + + @VisibleForTesting boolean isSwipeToNotificationEnabled() { return mIsSwipeToNotificationEnabled; } @@ -617,8 +633,11 @@ public class OneHandedController implements RemoteCallable<OneHandedController> mMainExecutor.execute(() -> stopOneHanded()); } - // Reset and align shortcut one_handed_mode_activated status with current mState - notifyShortcutState(mState.getState()); + // If setting is pull screen, notify shortcut one_handed_mode_activated to reset + // and align status with current mState when function enabled. + if (isOneHandedEnabled() && !isSwipeToNotificationEnabled()) { + notifyShortcutState(mState.getState()); + } mTouchHandler.onOneHandedEnabled(mIsOneHandedEnabled); @@ -717,6 +736,8 @@ public class OneHandedController implements RemoteCallable<OneHandedController> pw.println(mLockedDisabled); pw.print(innerPrefix + "mUserId="); pw.println(mUserId); + pw.print(innerPrefix + "isShortcutEnabled="); + pw.println(isShortcutEnabled()); if (mBackgroundPanelOrganizer != null) { mBackgroundPanelOrganizer.dump(pw); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedSettingsUtil.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedSettingsUtil.java index da53b359a304..3baa69f0033a 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedSettingsUtil.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedSettingsUtil.java @@ -16,6 +16,8 @@ package com.android.wm.shell.onehanded; +import static com.android.internal.accessibility.AccessibilityShortcutController.ONE_HANDED_COMPONENT_NAME; + import android.annotation.IntDef; import android.content.ContentResolver; import android.database.ContentObserver; @@ -34,6 +36,9 @@ import java.lang.annotation.RetentionPolicy; public final class OneHandedSettingsUtil { private static final String TAG = "OneHandedSettingsUtil"; + private static final String ONE_HANDED_MODE_TARGET_NAME = + ONE_HANDED_COMPONENT_NAME.getShortClassName(); + @IntDef(prefix = {"ONE_HANDED_TIMEOUT_"}, value = { ONE_HANDED_TIMEOUT_NEVER, ONE_HANDED_TIMEOUT_SHORT_IN_SECONDS, @@ -158,6 +163,17 @@ public final class OneHandedSettingsUtil { } /** + * Queries one-handed mode shortcut enabled in settings or not. + * + * @return true if user enabled one-handed shortcut in settings, false otherwise. + */ + public boolean getShortcutEnabled(ContentResolver resolver, int userId) { + final String targets = Settings.Secure.getStringForUser(resolver, + Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS, userId); + return targets != null ? targets.contains(ONE_HANDED_MODE_TARGET_NAME) : false; + } + + /** * Sets tutorial shown counts. * * @return true if the value was set, false on database errors. diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedUiEventLogger.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedUiEventLogger.java index 38ffb07aa5a4..4e610fad05ae 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedUiEventLogger.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedUiEventLogger.java @@ -50,6 +50,8 @@ public class OneHandedUiEventLogger { public static final int EVENT_ONE_HANDED_SETTINGS_TIMEOUT_SECONDS_4 = 15; public static final int EVENT_ONE_HANDED_SETTINGS_TIMEOUT_SECONDS_8 = 16; public static final int EVENT_ONE_HANDED_SETTINGS_TIMEOUT_SECONDS_12 = 17; + public static final int EVENT_ONE_HANDED_SETTINGS_SHOW_NOTIFICATION_ENABLED_ON = 18; + public static final int EVENT_ONE_HANDED_SETTINGS_SHOW_NOTIFICATION_ENABLED_OFF = 19; private static final String[] EVENT_TAGS = { "one_handed_trigger_gesture_in", @@ -69,7 +71,9 @@ public class OneHandedUiEventLogger { "one_handed_settings_timeout_seconds_never", "one_handed_settings_timeout_seconds_4", "one_handed_settings_timeout_seconds_8", - "one_handed_settings_timeout_seconds_12" + "one_handed_settings_timeout_seconds_12", + "one_handed_settings_show_notification_enabled_on", + "one_handed_settings_show_notification_enabled_off" }; public OneHandedUiEventLogger(UiEventLogger uiEventLogger) { @@ -152,7 +156,13 @@ public class OneHandedUiEventLogger { ONE_HANDED_SETTINGS_TOGGLES_TIMEOUT_SECONDS_8(364), @UiEvent(doc = "One-Handed mode timeout value changed to 12 seconds") - ONE_HANDED_SETTINGS_TOGGLES_TIMEOUT_SECONDS_12(365); + ONE_HANDED_SETTINGS_TOGGLES_TIMEOUT_SECONDS_12(365), + + @UiEvent(doc = "One-Handed mode show notification toggle on") + ONE_HANDED_SETTINGS_TOGGLES_SHOW_NOTIFICATION_ENABLED_ON(847), + + @UiEvent(doc = "One-Handed mode show notification toggle off") + ONE_HANDED_SETTINGS_TOGGLES_SHOW_NOTIFICATION_ENABLED_OFF(848); private final int mId; @@ -247,6 +257,14 @@ public class OneHandedUiEventLogger { mUiEventLogger.log(OneHandedSettingsTogglesEvent .ONE_HANDED_SETTINGS_TOGGLES_TIMEOUT_SECONDS_12); break; + case EVENT_ONE_HANDED_SETTINGS_SHOW_NOTIFICATION_ENABLED_ON: + mUiEventLogger.log(OneHandedSettingsTogglesEvent + .ONE_HANDED_SETTINGS_TOGGLES_SHOW_NOTIFICATION_ENABLED_ON); + break; + case EVENT_ONE_HANDED_SETTINGS_SHOW_NOTIFICATION_ENABLED_OFF: + mUiEventLogger.log(OneHandedSettingsTogglesEvent + .ONE_HANDED_SETTINGS_TOGGLES_SHOW_NOTIFICATION_ENABLED_OFF); + break; default: // Do nothing break; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java index 046c32071358..a4b866aa3f5e 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java @@ -19,6 +19,7 @@ package com.android.wm.shell.pip; import static android.util.TypedValue.COMPLEX_UNIT_DIP; import android.annotation.NonNull; +import android.annotation.Nullable; import android.app.PictureInPictureParams; import android.content.Context; import android.content.pm.ActivityInfo; @@ -454,6 +455,56 @@ public class PipBoundsAlgorithm { } /** + * @return the normal bounds adjusted so that they fit the menu actions. + */ + public Rect adjustNormalBoundsToFitMenu(@NonNull Rect normalBounds, + @Nullable Size minMenuSize) { + if (minMenuSize == null) { + return normalBounds; + } + if (normalBounds.width() >= minMenuSize.getWidth() + && normalBounds.height() >= minMenuSize.getHeight()) { + // The normal bounds can fit the menu as is, no need to adjust the bounds. + return normalBounds; + } + final Rect adjustedNormalBounds = new Rect(); + final boolean needsWidthAdj = minMenuSize.getWidth() > normalBounds.width(); + final boolean needsHeightAdj = minMenuSize.getHeight() > normalBounds.height(); + final int adjWidth; + final int adjHeight; + if (needsWidthAdj && needsHeightAdj) { + // Both the width and the height are too small - find the edge that needs the larger + // adjustment and scale that edge. The other edge will scale beyond the minMenuSize + // when the aspect ratio is applied. + final float widthScaleFactor = + ((float) (minMenuSize.getWidth())) / ((float) (normalBounds.width())); + final float heightScaleFactor = + ((float) (minMenuSize.getHeight())) / ((float) (normalBounds.height())); + if (widthScaleFactor > heightScaleFactor) { + adjWidth = minMenuSize.getWidth(); + adjHeight = Math.round(adjWidth / mPipBoundsState.getAspectRatio()); + } else { + adjHeight = minMenuSize.getHeight(); + adjWidth = Math.round(adjHeight * mPipBoundsState.getAspectRatio()); + } + } else if (needsWidthAdj) { + // Width is too small - use the min menu size width instead. + adjWidth = minMenuSize.getWidth(); + adjHeight = Math.round(adjWidth / mPipBoundsState.getAspectRatio()); + } else { + // Height is too small - use the min menu size height instead. + adjHeight = minMenuSize.getHeight(); + adjWidth = Math.round(adjHeight * mPipBoundsState.getAspectRatio()); + } + adjustedNormalBounds.set(0, 0, adjWidth, adjHeight); + // Make sure the bounds conform to the aspect ratio and min edge size. + transformBoundsToAspectRatio(adjustedNormalBounds, + mPipBoundsState.getAspectRatio(), true /* useCurrentMinEdgeSize */, + true /* useCurrentSize */); + return adjustedNormalBounds; + } + + /** * Dumps internal states. */ public void dump(PrintWriter pw, String prefix) { 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 324a6e27a242..f367cd608f37 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 @@ -722,6 +722,14 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, if (info.displayId != Display.DEFAULT_DISPLAY && mOnDisplayIdChangeCallback != null) { mOnDisplayIdChangeCallback.accept(Display.DEFAULT_DISPLAY); } + + final PipAnimationController.PipTransitionAnimator animator = + mPipAnimationController.getCurrentAnimator(); + if (animator != null) { + animator.removeAllUpdateListeners(); + animator.removeAllListeners(); + animator.cancel(); + } } @Override diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java index bc8e1e72b830..a646b07c49dc 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java @@ -524,6 +524,15 @@ public class PhonePipMenuController implements PipMenuController { mListeners.forEach(l -> l.onPipMenuStateChangeFinish(menuState)); } mMenuState = menuState; + switch (mMenuState) { + case MENU_STATE_NONE: + mSystemWindows.setShellRootAccessibilityWindow(0, SHELL_ROOT_LAYER_PIP, null); + break; + default: + mSystemWindows.setShellRootAccessibilityWindow(0, SHELL_ROOT_LAYER_PIP, + mPipMenuView); + break; + } } /** diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java index f0bd8a2846ed..c816f18c2fc2 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java @@ -246,10 +246,20 @@ public class PipResizeGestureHandler { } if (ev instanceof MotionEvent) { + MotionEvent mv = (MotionEvent) ev; + int action = mv.getActionMasked(); + final Rect pipBounds = mPipBoundsState.getBounds(); + if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) { + if (!pipBounds.contains((int) mv.getRawX(), (int) mv.getRawY()) + && mPhonePipMenuController.isMenuVisible()) { + mPhonePipMenuController.hideMenu(); + } + } + if (mEnablePinchResize && mOngoingPinchToResize) { - onPinchResize((MotionEvent) ev); + onPinchResize(mv); } else if (mEnableDragCornerResize) { - onDragCornerResize((MotionEvent) ev); + onDragCornerResize(mv); } } } @@ -450,7 +460,6 @@ public class PipResizeGestureHandler { float x = ev.getX(); float y = ev.getY() - mOhmOffset; if (action == MotionEvent.ACTION_DOWN) { - final Rect currentPipBounds = mPipBoundsState.getBounds(); mLastResizeBounds.setEmpty(); mAllowGesture = isInValidSysUiState() && isWithinDragResizeRegion((int) x, (int) y); if (mAllowGesture) { @@ -458,11 +467,6 @@ public class PipResizeGestureHandler { mDownPoint.set(x, y); mDownBounds.set(mPipBoundsState.getBounds()); } - if (!currentPipBounds.contains((int) x, (int) y) - && mPhonePipMenuController.isMenuVisible()) { - mPhonePipMenuController.hideMenu(); - } - } else if (mAllowGesture) { switch (action) { case MotionEvent.ACTION_POINTER_DOWN: diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java index b1086c575f49..0bcd1a363eb6 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java @@ -726,12 +726,17 @@ public class PipTouchHandler { } private void animateToNormalSize(Runnable callback) { + // Save the current bounds as the user-resize bounds. mPipResizeGestureHandler.setUserResizeBounds(mPipBoundsState.getBounds()); - final Rect normalBounds = new Rect(mPipBoundsState.getNormalBounds()); + + final Size minMenuSize = mMenuController.getEstimatedMinMenuSize(); + final Rect normalBounds = mPipBoundsState.getNormalBounds(); + final Rect destBounds = mPipBoundsAlgorithm.adjustNormalBoundsToFitMenu(normalBounds, + minMenuSize); Rect restoredMovementBounds = new Rect(); - mPipBoundsAlgorithm.getMovementBounds(normalBounds, + mPipBoundsAlgorithm.getMovementBounds(destBounds, mInsetBounds, restoredMovementBounds, mIsImeShowing ? mImeHeight : 0); - mSavedSnapFraction = mMotionHelper.animateToExpandedState(normalBounds, + mSavedSnapFraction = mMotionHelper.animateToExpandedState(destBounds, mPipBoundsState.getMovementBounds(), restoredMovementBounds, callback); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java index 70980191f103..a2e9b64046fd 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java @@ -279,7 +279,7 @@ public class TvPipController implements PipTransitionController.PipTransitionCal private void checkIfPinnedTaskAppeared() { final TaskInfo pinnedTask = getPinnedTaskInfo(); if (DEBUG) Log.d(TAG, "checkIfPinnedTaskAppeared(), task=" + pinnedTask); - if (pinnedTask == null) return; + if (pinnedTask == null || pinnedTask.topActivity == null) return; mPinnedTaskId = pinnedTask.taskId; setState(STATE_PIP); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUIController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUIController.java index c981adee9b5c..1fc4d12def1f 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUIController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUIController.java @@ -20,7 +20,6 @@ import android.annotation.Nullable; import android.content.Context; import android.content.res.Configuration; import android.hardware.display.DisplayManager; -import android.os.IBinder; import android.util.ArraySet; import android.util.Log; import android.util.SparseArray; @@ -45,6 +44,13 @@ import java.util.function.Consumer; */ public class SizeCompatUIController implements DisplayController.OnDisplaysChangedListener, DisplayImeController.ImePositionProcessor { + + /** Callback for size compat UI interaction. */ + public interface SizeCompatUICallback { + /** Called when the size compat restart button is clicked. */ + void onSizeCompatRestartButtonClicked(int taskId); + } + private static final String TAG = "SizeCompatUIController"; /** Whether the IME is shown on display id. */ @@ -61,6 +67,8 @@ public class SizeCompatUIController implements DisplayController.OnDisplaysChang private final DisplayImeController mImeController; private final SyncTransactionQueue mSyncQueue; + private SizeCompatUICallback mCallback; + /** Only show once automatically in the process life. */ private boolean mHasShownHint; @@ -76,29 +84,31 @@ public class SizeCompatUIController implements DisplayController.OnDisplaysChang mImeController.addPositionProcessor(this); } + /** Sets the callback for UI interactions. */ + public void setSizeCompatUICallback(SizeCompatUICallback callback) { + mCallback = callback; + } + /** * Called when the Task info changed. Creates and updates the size compat UI if there is an * activity in size compat, or removes the UI if there is no size compat activity. - * * @param displayId display the task and activity are in. * @param taskId task the activity is in. * @param taskConfig task config to place the size compat UI with. - * @param sizeCompatActivity the size compat activity in the task. Can be {@code null} if the - * top activity in this Task is not in size compat. * @param taskListener listener to handle the Task Surface placement. */ public void onSizeCompatInfoChanged(int displayId, int taskId, - @Nullable Configuration taskConfig, @Nullable IBinder sizeCompatActivity, + @Nullable Configuration taskConfig, @Nullable ShellTaskOrganizer.TaskListener taskListener) { - if (taskConfig == null || sizeCompatActivity == null || taskListener == null) { + if (taskConfig == null || taskListener == null) { // Null token means the current foreground activity is not in size compatibility mode. removeLayout(taskId); } else if (mActiveLayouts.contains(taskId)) { // UI already exists, update the UI layout. - updateLayout(taskId, taskConfig, sizeCompatActivity, taskListener); + updateLayout(taskId, taskConfig, taskListener); } else { // Create a new size compat UI. - createLayout(displayId, taskId, taskConfig, sizeCompatActivity, taskListener); + createLayout(displayId, taskId, taskConfig, taskListener); } } @@ -137,7 +147,7 @@ public class SizeCompatUIController implements DisplayController.OnDisplaysChang } private void createLayout(int displayId, int taskId, Configuration taskConfig, - IBinder activityToken, ShellTaskOrganizer.TaskListener taskListener) { + ShellTaskOrganizer.TaskListener taskListener) { final Context context = getOrCreateDisplayContext(displayId); if (context == null) { Log.e(TAG, "Cannot get context for display " + displayId); @@ -145,17 +155,16 @@ public class SizeCompatUIController implements DisplayController.OnDisplaysChang } final SizeCompatUILayout layout = createLayout(context, displayId, taskId, taskConfig, - activityToken, taskListener); + taskListener); mActiveLayouts.put(taskId, layout); layout.createSizeCompatButton(isImeShowingOnDisplay(displayId)); } @VisibleForTesting SizeCompatUILayout createLayout(Context context, int displayId, int taskId, - Configuration taskConfig, IBinder activityToken, - ShellTaskOrganizer.TaskListener taskListener) { - final SizeCompatUILayout layout = new SizeCompatUILayout(mSyncQueue, context, taskConfig, - taskId, activityToken, taskListener, mDisplayController.getDisplayLayout(displayId), + Configuration taskConfig, ShellTaskOrganizer.TaskListener taskListener) { + final SizeCompatUILayout layout = new SizeCompatUILayout(mSyncQueue, mCallback, context, + taskConfig, taskId, taskListener, mDisplayController.getDisplayLayout(displayId), mHasShownHint); // Only show hint for the first time. mHasShownHint = true; @@ -163,13 +172,12 @@ public class SizeCompatUIController implements DisplayController.OnDisplaysChang } private void updateLayout(int taskId, Configuration taskConfig, - IBinder sizeCompatActivity, ShellTaskOrganizer.TaskListener taskListener) { final SizeCompatUILayout layout = mActiveLayouts.get(taskId); if (layout == null) { return; } - layout.updateSizeCompatInfo(taskConfig, sizeCompatActivity, taskListener, + layout.updateSizeCompatInfo(taskConfig, taskListener, isImeShowingOnDisplay(layout.getDisplayId())); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUILayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUILayout.java index c6d994ecde8d..a5e96d14dde6 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUILayout.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUILayout.java @@ -23,13 +23,11 @@ import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERL import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; import android.annotation.Nullable; -import android.app.ActivityClient; import android.content.Context; import android.content.res.Configuration; import android.graphics.PixelFormat; import android.graphics.Rect; import android.os.Binder; -import android.os.IBinder; import android.view.SurfaceControl; import android.view.View; import android.view.WindowManager; @@ -48,11 +46,11 @@ class SizeCompatUILayout { private static final String TAG = "SizeCompatUILayout"; private final SyncTransactionQueue mSyncQueue; + private final SizeCompatUIController.SizeCompatUICallback mCallback; private Context mContext; private Configuration mTaskConfig; private final int mDisplayId; private final int mTaskId; - private IBinder mActivityToken; private ShellTaskOrganizer.TaskListener mTaskListener; private DisplayLayout mDisplayLayout; @@ -72,15 +70,16 @@ class SizeCompatUILayout { final int mPopupOffsetY; boolean mShouldShowHint; - SizeCompatUILayout(SyncTransactionQueue syncQueue, Context context, Configuration taskConfig, - int taskId, IBinder activityToken, ShellTaskOrganizer.TaskListener taskListener, + SizeCompatUILayout(SyncTransactionQueue syncQueue, + SizeCompatUIController.SizeCompatUICallback callback, Context context, + Configuration taskConfig, int taskId, ShellTaskOrganizer.TaskListener taskListener, DisplayLayout displayLayout, boolean hasShownHint) { mSyncQueue = syncQueue; + mCallback = callback; mContext = context.createConfigurationContext(taskConfig); mTaskConfig = taskConfig; mDisplayId = mContext.getDisplayId(); mTaskId = taskId; - mActivityToken = activityToken; mTaskListener = taskListener; mDisplayLayout = displayLayout; mShouldShowHint = !hasShownHint; @@ -141,12 +140,11 @@ class SizeCompatUILayout { } /** Called when size compat info changed. */ - void updateSizeCompatInfo(Configuration taskConfig, IBinder activityToken, + void updateSizeCompatInfo(Configuration taskConfig, ShellTaskOrganizer.TaskListener taskListener, boolean isImeShowing) { final Configuration prevTaskConfig = mTaskConfig; final ShellTaskOrganizer.TaskListener prevTaskListener = mTaskListener; mTaskConfig = taskConfig; - mActivityToken = activityToken; mTaskListener = taskListener; // Update configuration. @@ -253,7 +251,7 @@ class SizeCompatUILayout { /** Called when the restart button is clicked. */ void onRestartButtonClicked() { - ActivityClient.getInstance().restartActivityProcessIfVisible(mActivityToken); + mCallback.onSizeCompatRestartButtonClicked(mTaskId); } /** Called when the restart button is long clicked. */ diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashScreenExitAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashScreenExitAnimation.java index 9986154b051d..4e477ca104dd 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashScreenExitAnimation.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashScreenExitAnimation.java @@ -85,7 +85,10 @@ public class SplashScreenExitAnimation implements Animator.AnimatorListener { } View iconView = view.getIconView(); - if (iconView == null || iconView.getBackground() == null) { + + // If the icon and the background are invisible, don't animate it + if (iconView == null || iconView.getLayoutParams().width == 0 + || iconView.getLayoutParams().height == 0) { mIconFadeOutDuration = 0; mIconStartAlpha = 0; mAppRevealDelay = 0; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java index df3fee043419..75dd561ffc61 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java @@ -18,6 +18,9 @@ package com.android.wm.shell.startingsurface; import static android.os.Process.THREAD_PRIORITY_TOP_APP_BOOST; import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; +import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_EMPTY_SPLASH_SCREEN; +import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_LEGACY_SPLASH_SCREEN; +import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_SPLASH_SCREEN; import android.annotation.ColorInt; import android.annotation.NonNull; @@ -47,6 +50,7 @@ import android.util.Slog; import android.view.SurfaceControl; import android.view.View; import android.window.SplashScreenView; +import android.window.StartingWindowInfo.StartingWindowType; import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; @@ -77,6 +81,13 @@ public class SplashscreenContentDrawer { // For example, an icon with the foreground 108*108 opaque pixels and it's background // also 108*108 pixels, then do not enlarge this icon if only need to show foreground icon. private static final float ENLARGE_FOREGROUND_ICON_THRESHOLD = (72f * 72f) / (108f * 108f); + + /** + * If the developer doesn't specify a background for the icon, we slightly scale it up. + * + * The background is either manually specified in the theme or the Adaptive Icon + * background is used if it's different from the window background. + */ private static final float NO_BACKGROUND_SCALE = 192f / 160; private final Context mContext; private final IconProvider mIconProvider; @@ -115,17 +126,17 @@ public class SplashscreenContentDrawer { * view on background thread so the view and the drawable can be create and pre-draw in * parallel. * - * @param emptyView Create a splash screen view without icon on it. + * @param suggestType Suggest type to create the splash screen view. * @param consumer Receiving the SplashScreenView object, which will also be executed * on splash screen thread. Note that the view can be null if failed. */ - void createContentView(Context context, boolean emptyView, ActivityInfo info, int taskId, - Consumer<SplashScreenView> consumer) { + void createContentView(Context context, @StartingWindowType int suggestType, ActivityInfo info, + int taskId, Consumer<SplashScreenView> consumer) { mSplashscreenWorkerHandler.post(() -> { SplashScreenView contentView; try { Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "makeSplashScreenContentView"); - contentView = makeSplashScreenContentView(context, info, emptyView); + contentView = makeSplashScreenContentView(context, info, suggestType); Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); } catch (RuntimeException e) { Slog.w(TAG, "failed creating starting window content at taskId: " @@ -192,22 +203,45 @@ public class SplashscreenContentDrawer { } } + private static Drawable peekLegacySplashscreenContent(Context context, + SplashScreenWindowAttrs attrs) { + final TypedArray a = context.obtainStyledAttributes(R.styleable.Window); + final int resId = safeReturnAttrDefault((def) -> + a.getResourceId(R.styleable.Window_windowSplashscreenContent, def), 0); + a.recycle(); + if (resId != 0) { + return context.getDrawable(resId); + } + if (attrs.mWindowBgResId != 0) { + return context.getDrawable(attrs.mWindowBgResId); + } + return null; + } + private SplashScreenView makeSplashScreenContentView(Context context, ActivityInfo ai, - boolean emptyView) { + @StartingWindowType int suggestType) { updateDensity(); getWindowAttrs(context, mTmpAttrs); mLastPackageContextConfigHash = context.getResources().getConfiguration().hashCode(); - final int themeBGColor = mColorCache.getWindowColor(ai.packageName, - mLastPackageContextConfigHash, mTmpAttrs.mWindowBgColor, mTmpAttrs.mWindowBgResId, - () -> peekWindowBGColor(context, mTmpAttrs)).mBgColor; - // TODO (b/173975965) Tracking the performance on improved splash screen. + + final Drawable legacyDrawable = suggestType == STARTING_WINDOW_TYPE_LEGACY_SPLASH_SCREEN + ? peekLegacySplashscreenContent(context, mTmpAttrs) : null; + final int themeBGColor = legacyDrawable != null + ? getBGColorFromCache(ai, () -> estimateWindowBGColor(legacyDrawable)) + : getBGColorFromCache(ai, () -> peekWindowBGColor(context, mTmpAttrs)); return new StartingWindowViewBuilder(context, ai) .setWindowBGColor(themeBGColor) - .makeEmptyView(emptyView) + .overlayDrawable(legacyDrawable) + .chooseStyle(suggestType) .build(); } + private int getBGColorFromCache(ActivityInfo ai, IntSupplier windowBgColorSupplier) { + return mColorCache.getWindowColor(ai.packageName, mLastPackageContextConfigHash, + mTmpAttrs.mWindowBgColor, mTmpAttrs.mWindowBgResId, windowBgColorSupplier).mBgColor; + } + private static <T> T safeReturnAttrDefault(UnaryOperator<T> getMethod, T def) { try { return getMethod.apply(def); @@ -228,7 +262,7 @@ public class SplashscreenContentDrawer { attrs.mWindowBgColor = safeReturnAttrDefault((def) -> typedArray.getColor( R.styleable.Window_windowSplashScreenBackground, def), Color.TRANSPARENT); - attrs.mReplaceIcon = safeReturnAttrDefault((def) -> typedArray.getDrawable( + attrs.mSplashScreenIcon = safeReturnAttrDefault((def) -> typedArray.getDrawable( R.styleable.Window_windowSplashScreenAnimatedIcon), null); attrs.mAnimationDuration = safeReturnAttrDefault((def) -> typedArray.getInt( R.styleable.Window_windowSplashScreenAnimationDuration, def), 0); @@ -241,7 +275,7 @@ public class SplashscreenContentDrawer { if (DEBUG) { Slog.d(TAG, "window attributes color: " + Integer.toHexString(attrs.mWindowBgColor) - + " icon " + attrs.mReplaceIcon + " duration " + attrs.mAnimationDuration + + " icon " + attrs.mSplashScreenIcon + " duration " + attrs.mAnimationDuration + " brandImage " + attrs.mBrandingImage); } } @@ -250,7 +284,7 @@ public class SplashscreenContentDrawer { public static class SplashScreenWindowAttrs { private int mWindowBgResId = 0; private int mWindowBgColor = Color.TRANSPARENT; - private Drawable mReplaceIcon = null; + private Drawable mSplashScreenIcon = null; private Drawable mBrandingImage = null; private int mIconBgColor = Color.TRANSPARENT; private int mAnimationDuration = 0; @@ -260,7 +294,8 @@ public class SplashscreenContentDrawer { private final Context mContext; private final ActivityInfo mActivityInfo; - private boolean mEmptyView; + private Drawable mOverlayDrawable; + private int mSuggestType; private int mThemeColor; private Drawable mFinalIconDrawable; private int mFinalIconSize = mIconSize; @@ -275,22 +310,33 @@ public class SplashscreenContentDrawer { return this; } - StartingWindowViewBuilder makeEmptyView(boolean empty) { - mEmptyView = empty; + StartingWindowViewBuilder overlayDrawable(Drawable overlay) { + mOverlayDrawable = overlay; + return this; + } + + StartingWindowViewBuilder chooseStyle(int suggestType) { + mSuggestType = suggestType; return this; } SplashScreenView build() { Drawable iconDrawable; final int animationDuration; - if (mEmptyView) { - // empty splash screen case + if (mSuggestType == STARTING_WINDOW_TYPE_EMPTY_SPLASH_SCREEN + || mSuggestType == STARTING_WINDOW_TYPE_LEGACY_SPLASH_SCREEN) { + // empty or legacy splash screen case animationDuration = 0; mFinalIconSize = 0; - } else if (mTmpAttrs.mReplaceIcon != null) { + } else if (mTmpAttrs.mSplashScreenIcon != null) { // replaced icon, don't process - iconDrawable = mTmpAttrs.mReplaceIcon; + iconDrawable = mTmpAttrs.mSplashScreenIcon; animationDuration = mTmpAttrs.mAnimationDuration; + + // There is no background below the icon, so scale the icon up + if (mTmpAttrs.mIconBgColor == Color.TRANSPARENT) { + mFinalIconSize *= NO_BACKGROUND_SCALE; + } createIconDrawable(iconDrawable, false); } else { final float iconScale = (float) mIconSize / (float) mDefaultIconSize; @@ -391,13 +437,15 @@ public class SplashscreenContentDrawer { Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "fillViewWithIcon"); final SplashScreenView.Builder builder = new SplashScreenView.Builder(mContext); builder.setBackgroundColor(mThemeColor); + builder.setOverlayDrawable(mOverlayDrawable); if (iconDrawable != null) { builder.setIconSize(iconSize) .setIconBackground(mTmpAttrs.mIconBgColor) .setCenterViewDrawable(iconDrawable) .setAnimationDurationMillis(animationDuration); } - if (mTmpAttrs.mBrandingImage != null) { + if (mSuggestType == STARTING_WINDOW_TYPE_SPLASH_SCREEN + && mTmpAttrs.mBrandingImage != null) { builder.setBrandingDrawable(mTmpAttrs.mBrandingImage, mBrandingImageWidth, mBrandingImageHeight); } @@ -405,20 +453,22 @@ public class SplashscreenContentDrawer { if (DEBUG) { Slog.d(TAG, "fillViewWithIcon surfaceWindowView " + splashScreenView); } - if (mEmptyView) { - splashScreenView.setNotCopyable(); + if (mSuggestType != STARTING_WINDOW_TYPE_LEGACY_SPLASH_SCREEN) { + splashScreenView.addOnAttachStateChangeListener( + new View.OnAttachStateChangeListener() { + @Override + public void onViewAttachedToWindow(View v) { + SplashScreenView.applySystemBarsContrastColor( + v.getWindowInsetsController(), + splashScreenView.getInitBackgroundColor()); + } + + @Override + public void onViewDetachedFromWindow(View v) { + } + }); } - splashScreenView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() { - @Override - public void onViewAttachedToWindow(View v) { - SplashScreenView.applySystemBarsContrastColor(v.getWindowInsetsController(), - splashScreenView.getInitBackgroundColor()); - } - @Override - public void onViewDetachedFromWindow(View v) { - } - }); Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); return splashScreenView; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenIconDrawableFactory.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenIconDrawableFactory.java index dae7055ede53..211941f44c8e 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenIconDrawableFactory.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenIconDrawableFactory.java @@ -162,6 +162,7 @@ public class SplashscreenIconDrawableFactory { @Override public void draw(Canvas canvas) { + canvas.clipPath(mMaskScaleOnly); if (mMaskScaleOnly != null) { canvas.drawPath(mMaskScaleOnly, mPaint); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java index 46db35a6e29f..4dc5447cbe65 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java @@ -20,6 +20,8 @@ import static android.content.Context.CONTEXT_RESTRICTED; import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; import static android.view.Choreographer.CALLBACK_INSETS_ANIMATION; import static android.view.Display.DEFAULT_DISPLAY; +import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_LEGACY_SPLASH_SCREEN; +import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_SNAPSHOT; import android.annotation.Nullable; import android.app.ActivityManager.RunningTaskInfo; @@ -32,7 +34,6 @@ import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.PixelFormat; import android.graphics.Rect; -import android.graphics.drawable.ColorDrawable; import android.hardware.display.DisplayManager; import android.os.IBinder; import android.os.RemoteCallback; @@ -50,6 +51,7 @@ import android.widget.FrameLayout; import android.window.SplashScreenView; import android.window.SplashScreenView.SplashScreenViewParcelable; import android.window.StartingWindowInfo; +import android.window.StartingWindowInfo.StartingWindowType; import android.window.TaskSnapshot; import com.android.internal.R; @@ -149,10 +151,11 @@ public class StartingSurfaceDrawer { /** * Called when a task need a splash screen starting window. - * @param emptyView Whether drawing an empty frame without anything on it. + * + * @param suggestType The suggestion type to draw the splash screen. */ void addSplashScreenStartingWindow(StartingWindowInfo windowInfo, IBinder appToken, - boolean emptyView) { + @StartingWindowType int suggestType) { final RunningTaskInfo taskInfo = windowInfo.taskInfo; final ActivityInfo activityInfo = taskInfo.topActivityInfo; if (activityInfo == null) { @@ -173,7 +176,8 @@ public class StartingSurfaceDrawer { : com.android.internal.R.style.Theme_DeviceDefault_DayNight; if (DEBUG_SPLASH_SCREEN) { Slog.d(TAG, "addSplashScreen " + activityInfo.packageName - + " theme=" + Integer.toHexString(theme) + " task= " + taskInfo.taskId); + + " theme=" + Integer.toHexString(theme) + " task=" + taskInfo.taskId + + " suggestType=" + suggestType); } // Obtain proper context to launch on the right display. @@ -231,13 +235,19 @@ public class StartingSurfaceDrawer { params.setFitInsetsTypes(0); params.format = PixelFormat.TRANSLUCENT; int windowFlags = WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED - | WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR; final TypedArray a = context.obtainStyledAttributes(R.styleable.Window); if (a.getBoolean(R.styleable.Window_windowShowWallpaper, false)) { windowFlags |= WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER; } + if (suggestType == STARTING_WINDOW_TYPE_LEGACY_SPLASH_SCREEN) { + if (a.getBoolean(R.styleable.Window_windowDrawsSystemBarBackgrounds, false)) { + windowFlags |= WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; + } + } else { + windowFlags |= WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; + } params.layoutInDisplayCutoutMode = a.getInt( R.styleable.Window_windowLayoutInDisplayCutoutMode, params.layoutInDisplayCutoutMode); @@ -288,6 +298,8 @@ public class StartingSurfaceDrawer { // create splash screen view finished. final SplashScreenViewSupplier viewSupplier = new SplashScreenViewSupplier(); final FrameLayout rootLayout = new FrameLayout(context); + rootLayout.setPadding(0, 0, 0, 0); + rootLayout.setFitsSystemWindows(false); final Runnable setViewSynchronized = () -> { Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "addSplashScreenView"); // waiting for setContentView before relayoutWindow @@ -310,12 +322,12 @@ public class StartingSurfaceDrawer { } Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); }; - mSplashscreenContentDrawer.createContentView(context, emptyView, activityInfo, taskId, + mSplashscreenContentDrawer.createContentView(context, suggestType, activityInfo, taskId, viewSupplier::setView); try { final WindowManager wm = context.getSystemService(WindowManager.class); - if (addWindow(taskId, appToken, rootLayout, wm, params)) { + if (addWindow(taskId, appToken, rootLayout, wm, params, suggestType)) { // We use the splash screen worker thread to create SplashScreenView while adding // the window, as otherwise Choreographer#doFrame might be delayed on this thread. // And since Choreographer#doFrame won't happen immediately after adding the window, @@ -335,8 +347,10 @@ public class StartingSurfaceDrawer { int getStartingWindowBackgroundColorForTask(int taskId) { StartingWindowRecord startingWindowRecord = mStartingWindowRecords.get(taskId); - if (startingWindowRecord == null || startingWindowRecord.mContentView == null) return 0; - return ((ColorDrawable) startingWindowRecord.mContentView.getBackground()).getColor(); + if (startingWindowRecord == null || startingWindowRecord.mContentView == null) { + return 0; + } + return startingWindowRecord.mContentView.getInitBackgroundColor(); } private static class SplashScreenViewSupplier implements Supplier<SplashScreenView> { @@ -378,7 +392,7 @@ public class StartingSurfaceDrawer { return; } final StartingWindowRecord tView = new StartingWindowRecord(appToken, - null/* decorView */, surface); + null/* decorView */, surface, STARTING_WINDOW_TYPE_SNAPSHOT); mStartingWindowRecords.put(taskId, tView); } @@ -448,7 +462,7 @@ public class StartingSurfaceDrawer { } protected boolean addWindow(int taskId, IBinder appToken, View view, WindowManager wm, - WindowManager.LayoutParams params) { + WindowManager.LayoutParams params, @StartingWindowType int suggestType) { boolean shouldSaveView = true; try { Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "addRootView"); @@ -468,14 +482,15 @@ public class StartingSurfaceDrawer { } if (shouldSaveView) { removeWindowNoAnimate(taskId); - saveSplashScreenRecord(appToken, taskId, view); + saveSplashScreenRecord(appToken, taskId, view, suggestType); } return shouldSaveView; } - private void saveSplashScreenRecord(IBinder appToken, int taskId, View view) { + private void saveSplashScreenRecord(IBinder appToken, int taskId, View view, + @StartingWindowType int suggestType) { final StartingWindowRecord tView = new StartingWindowRecord(appToken, view, - null/* TaskSnapshotWindow */); + null/* TaskSnapshotWindow */, suggestType); mStartingWindowRecords.put(taskId, tView); } @@ -492,14 +507,18 @@ public class StartingSurfaceDrawer { Slog.v(TAG, "Removing splash screen window for task: " + taskId); } if (record.mContentView != null) { - if (playRevealAnimation) { - mSplashscreenContentDrawer.applyExitAnimation(record.mContentView, - leash, frame, - () -> removeWindowInner(record.mDecorView, true)); + if (record.mSuggestType == STARTING_WINDOW_TYPE_LEGACY_SPLASH_SCREEN) { + removeWindowInner(record.mDecorView, false); } else { - // the SplashScreenView has been copied to client, hide the view to skip - // default exit animation - removeWindowInner(record.mDecorView, true); + if (playRevealAnimation) { + mSplashscreenContentDrawer.applyExitAnimation(record.mContentView, + leash, frame, + () -> removeWindowInner(record.mDecorView, true)); + } else { + // the SplashScreenView has been copied to client, hide the view to skip + // default exit animation + removeWindowInner(record.mDecorView, true); + } } } else { // shouldn't happen @@ -536,6 +555,7 @@ public class StartingSurfaceDrawer { private final TaskSnapshotWindow mTaskSnapshotWindow; private SplashScreenView mContentView; private boolean mSetSplashScreen; + private @StartingWindowType int mSuggestType; StartingWindowRecord(IBinder appToken, View decorView, TaskSnapshotWindow taskSnapshotWindow) { @@ -544,6 +564,14 @@ public class StartingSurfaceDrawer { mTaskSnapshotWindow = taskSnapshotWindow; } + StartingWindowRecord(IBinder appToken, View decorView, + TaskSnapshotWindow taskSnapshotWindow, @StartingWindowType int suggestType) { + mAppToken = appToken; + mDecorView = decorView; + mTaskSnapshotWindow = taskSnapshotWindow; + mSuggestType = suggestType; + } + private void setSplashScreenView(SplashScreenView splashScreenView) { if (mSetSplashScreen) { return; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java index 9c1dde925762..eaa89d8e3ab5 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java @@ -17,6 +17,7 @@ package com.android.wm.shell.startingsurface; import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_EMPTY_SPLASH_SCREEN; +import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_LEGACY_SPLASH_SCREEN; import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_SNAPSHOT; import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_SPLASH_SCREEN; @@ -31,6 +32,7 @@ import android.os.Trace; import android.util.Slog; import android.view.SurfaceControl; import android.window.StartingWindowInfo; +import android.window.StartingWindowInfo.StartingWindowType; import android.window.TaskOrganizer; import android.window.TaskSnapshot; @@ -106,10 +108,6 @@ public class StartingWindowController implements RemoteCallable<StartingWindowCo mTaskLaunchingCallback = listener; } - private boolean shouldSendToListener(int suggestionType) { - return suggestionType != STARTING_WINDOW_TYPE_EMPTY_SPLASH_SCREEN; - } - /** * Called when a task need a starting window. */ @@ -120,12 +118,9 @@ public class StartingWindowController implements RemoteCallable<StartingWindowCo final int suggestionType = mStartingWindowTypeAlgorithm.getSuggestedWindowType( windowInfo); final RunningTaskInfo runningTaskInfo = windowInfo.taskInfo; - if (suggestionType == STARTING_WINDOW_TYPE_SPLASH_SCREEN) { - mStartingSurfaceDrawer.addSplashScreenStartingWindow(windowInfo, appToken, - false /* emptyView */); - } else if (suggestionType == STARTING_WINDOW_TYPE_EMPTY_SPLASH_SCREEN) { + if (isSplashScreenType(suggestionType)) { mStartingSurfaceDrawer.addSplashScreenStartingWindow(windowInfo, appToken, - true /* emptyView */); + suggestionType); } else if (suggestionType == STARTING_WINDOW_TYPE_SNAPSHOT) { final TaskSnapshot snapshot = windowInfo.mTaskSnapshot; mStartingSurfaceDrawer.makeTaskSnapshotWindow(windowInfo, appToken, @@ -133,7 +128,7 @@ public class StartingWindowController implements RemoteCallable<StartingWindowCo } else /* suggestionType == STARTING_WINDOW_TYPE_NONE */ { // Don't add a staring window. } - if (mTaskLaunchingCallback != null && shouldSendToListener(suggestionType)) { + if (mTaskLaunchingCallback != null && isSplashScreenType(suggestionType)) { int taskId = runningTaskInfo.taskId; int color = mStartingSurfaceDrawer.getStartingWindowBackgroundColorForTask(taskId); mTaskLaunchingCallback.accept(taskId, suggestionType, color); @@ -143,6 +138,12 @@ public class StartingWindowController implements RemoteCallable<StartingWindowCo }); } + private static boolean isSplashScreenType(@StartingWindowType int suggestionType) { + return suggestionType == STARTING_WINDOW_TYPE_SPLASH_SCREEN + || suggestionType == STARTING_WINDOW_TYPE_EMPTY_SPLASH_SCREEN + || suggestionType == STARTING_WINDOW_TYPE_LEGACY_SPLASH_SCREEN; + } + public void copySplashScreenView(int taskId) { mSplashScreenExecutor.execute(() -> { mStartingSurfaceDrawer.copySplashScreenView(taskId); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java index acf7f3367d71..382d5806e3c2 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java @@ -16,6 +16,7 @@ package com.android.wm.shell.startingsurface; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; import static android.graphics.Color.WHITE; import static android.graphics.Color.alpha; import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; @@ -63,6 +64,7 @@ import android.graphics.RectF; import android.hardware.HardwareBuffer; import android.os.IBinder; import android.os.RemoteException; +import android.os.SystemClock; import android.os.Trace; import android.util.MergedConfiguration; import android.util.Slog; @@ -116,11 +118,15 @@ public class TaskSnapshotWindow { private static final boolean DEBUG = StartingSurfaceDrawer.DEBUG_TASK_SNAPSHOT; private static final String TITLE_FORMAT = "SnapshotStartingWindow for taskId=%s"; + private static final long DELAY_REMOVAL_TIME_GENERAL = 100; + private static final long DELAY_REMOVAL_TIME_IME_VISIBLE = 350; + //tmp vars for unused relayout params private static final Point TMP_SURFACE_SIZE = new Point(); private final Window mWindow; private final Runnable mClearWindowHandler; + private final long mDelayRemovalTime; private final ShellExecutor mSplashScreenExecutor; private final SurfaceControl mSurfaceControl; private final IWindowSession mSession; @@ -132,8 +138,10 @@ public class TaskSnapshotWindow { private final RectF mTmpDstFrame = new RectF(); private final CharSequence mTitle; private boolean mHasDrawn; + private long mShownTime; private boolean mSizeMismatch; private final Paint mBackgroundPaint = new Paint(); + private final int mActivityType; private final int mStatusBarColor; private final SystemBarBackgroundPainter mSystemBarBackgroundPainter; private final int mOrientationOnCreation; @@ -190,6 +198,7 @@ public class TaskSnapshotWindow { final Point taskSize = snapshot.getTaskSize(); final Rect taskBounds = new Rect(0, 0, taskSize.x, taskSize.y); final int orientation = snapshot.getOrientation(); + final int activityType = runningTaskInfo.topActivityType; final int displayId = runningTaskInfo.displayId; final IWindowSession session = WindowManagerGlobal.getWindowSession(); @@ -207,10 +216,13 @@ public class TaskSnapshotWindow { taskDescription.setBackgroundColor(WHITE); } + final long delayRemovalTime = snapshot.hasImeSurface() ? DELAY_REMOVAL_TIME_IME_VISIBLE + : DELAY_REMOVAL_TIME_GENERAL; + final TaskSnapshotWindow snapshotSurface = new TaskSnapshotWindow( surfaceControl, snapshot, layoutParams.getTitle(), taskDescription, appearance, - windowFlags, windowPrivateFlags, taskBounds, orientation, - topWindowInsetsState, clearWindowHandler, splashScreenExecutor); + windowFlags, windowPrivateFlags, taskBounds, orientation, activityType, + delayRemovalTime, topWindowInsetsState, clearWindowHandler, splashScreenExecutor); final Window window = snapshotSurface.mWindow; final InsetsState mTmpInsetsState = new InsetsState(); @@ -248,7 +260,8 @@ public class TaskSnapshotWindow { public TaskSnapshotWindow(SurfaceControl surfaceControl, TaskSnapshot snapshot, CharSequence title, TaskDescription taskDescription, int appearance, int windowFlags, int windowPrivateFlags, Rect taskBounds, - int currentOrientation, InsetsState topWindowInsetsState, Runnable clearWindowHandler, + int currentOrientation, int activityType, long delayRemovalTime, + InsetsState topWindowInsetsState, Runnable clearWindowHandler, ShellExecutor splashScreenExecutor) { mSplashScreenExecutor = splashScreenExecutor; mSession = WindowManagerGlobal.getWindowSession(); @@ -264,6 +277,8 @@ public class TaskSnapshotWindow { windowPrivateFlags, appearance, taskDescription, 1f, topWindowInsetsState); mStatusBarColor = taskDescription.getStatusBarColor(); mOrientationOnCreation = currentOrientation; + mActivityType = activityType; + mDelayRemovalTime = delayRemovalTime; mTransaction = new SurfaceControl.Transaction(); mClearWindowHandler = clearWindowHandler; } @@ -286,6 +301,17 @@ public class TaskSnapshotWindow { } void remove() { + final long now = SystemClock.uptimeMillis(); + if ((now - mShownTime < mDelayRemovalTime) + // Show the latest content as soon as possible for unlocking to home. + && mActivityType != ACTIVITY_TYPE_HOME) { + final long delayTime = mShownTime + mDelayRemovalTime - now; + mSplashScreenExecutor.executeDelayed(() -> remove(), delayTime); + if (DEBUG) { + Slog.d(TAG, "Defer removing snapshot surface in " + delayTime); + } + return; + } try { if (DEBUG) { Slog.d(TAG, "Removing snapshot surface, mHasDrawn: " + mHasDrawn); @@ -326,6 +352,7 @@ public class TaskSnapshotWindow { } else { drawSizeMatchSnapshot(); } + mShownTime = SystemClock.uptimeMillis(); mHasDrawn = true; reportDrawn(); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/phone/PhoneStartingWindowTypeAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/phone/PhoneStartingWindowTypeAlgorithm.java index 5a134b806745..848eff4b56f3 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/phone/PhoneStartingWindowTypeAlgorithm.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/phone/PhoneStartingWindowTypeAlgorithm.java @@ -18,11 +18,13 @@ package com.android.wm.shell.startingsurface.phone; import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_EMPTY_SPLASH_SCREEN; +import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_LEGACY_SPLASH_SCREEN; import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_NONE; import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_SNAPSHOT; import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_SPLASH_SCREEN; import static android.window.StartingWindowInfo.TYPE_PARAMETER_ACTIVITY_CREATED; import static android.window.StartingWindowInfo.TYPE_PARAMETER_ALLOW_TASK_SNAPSHOT; +import static android.window.StartingWindowInfo.TYPE_PARAMETER_LEGACY_SPLASH_SCREEN; import static android.window.StartingWindowInfo.TYPE_PARAMETER_NEW_TASK; import static android.window.StartingWindowInfo.TYPE_PARAMETER_PROCESS_RUNNING; import static android.window.StartingWindowInfo.TYPE_PARAMETER_TASK_SWITCH; @@ -54,30 +56,38 @@ public class PhoneStartingWindowTypeAlgorithm implements StartingWindowTypeAlgor final boolean activityCreated = (parameter & TYPE_PARAMETER_ACTIVITY_CREATED) != 0; final boolean useEmptySplashScreen = (parameter & TYPE_PARAMETER_USE_EMPTY_SPLASH_SCREEN) != 0; + final boolean legacySplashScreen = + ((parameter & TYPE_PARAMETER_LEGACY_SPLASH_SCREEN) != 0); final boolean topIsHome = windowInfo.taskInfo.topActivityType == ACTIVITY_TYPE_HOME; if (DEBUG_SPLASH_SCREEN || DEBUG_TASK_SNAPSHOT) { - Slog.d(TAG, "preferredStartingWindowType newTask " + newTask - + " taskSwitch " + taskSwitch - + " processRunning " + processRunning - + " allowTaskSnapshot " + allowTaskSnapshot - + " activityCreated " + activityCreated - + " useEmptySplashScreen " + useEmptySplashScreen - + " topIsHome " + topIsHome); + Slog.d(TAG, "preferredStartingWindowType newTask:" + newTask + + " taskSwitch:" + taskSwitch + + " processRunning:" + processRunning + + " allowTaskSnapshot:" + allowTaskSnapshot + + " activityCreated:" + activityCreated + + " useEmptySplashScreen:" + useEmptySplashScreen + + " legacySplashScreen:" + legacySplashScreen + + " topIsHome:" + topIsHome); } + + final int visibleSplashScreenType = legacySplashScreen + ? STARTING_WINDOW_TYPE_LEGACY_SPLASH_SCREEN + : STARTING_WINDOW_TYPE_SPLASH_SCREEN; + if (!topIsHome) { if (!processRunning) { return useEmptySplashScreen ? STARTING_WINDOW_TYPE_EMPTY_SPLASH_SCREEN - : STARTING_WINDOW_TYPE_SPLASH_SCREEN; + : visibleSplashScreenType; } if (newTask) { return useEmptySplashScreen ? STARTING_WINDOW_TYPE_EMPTY_SPLASH_SCREEN - : STARTING_WINDOW_TYPE_SPLASH_SCREEN; + : visibleSplashScreenType; } if (taskSwitch && !activityCreated) { - return STARTING_WINDOW_TYPE_SPLASH_SCREEN; + return visibleSplashScreenType; } } if (taskSwitch && allowTaskSnapshot) { diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java index df0a856db73c..cf5bd3ace806 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java @@ -48,6 +48,7 @@ import android.view.SurfaceControl; import android.window.ITaskOrganizer; import android.window.ITaskOrganizerController; import android.window.TaskAppearedInfo; +import android.window.WindowContainerToken; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; @@ -289,7 +290,6 @@ public class ShellTaskOrganizerTests { public void testOnSizeCompatActivityChanged() { final RunningTaskInfo taskInfo1 = createTaskInfo(12, WINDOWING_MODE_FULLSCREEN); taskInfo1.displayId = DEFAULT_DISPLAY; - taskInfo1.topActivityToken = mock(IBinder.class); taskInfo1.topActivityInSizeCompat = false; final TrackingTaskListener taskListener = new TrackingTaskListener(); mOrganizer.addListenerForType(taskListener, TASK_LISTENER_TYPE_FULLSCREEN); @@ -297,23 +297,22 @@ public class ShellTaskOrganizerTests { // sizeCompatActivity is null if top activity is not in size compat. verify(mSizeCompatUI).onSizeCompatInfoChanged(taskInfo1.displayId, taskInfo1.taskId, - null /* taskConfig */, null /* sizeCompatActivity*/, null /* taskListener */); + null /* taskConfig */, null /* taskListener */); // sizeCompatActivity is non-null if top activity is in size compat. clearInvocations(mSizeCompatUI); final RunningTaskInfo taskInfo2 = createTaskInfo(taskInfo1.taskId, taskInfo1.getWindowingMode()); taskInfo2.displayId = taskInfo1.displayId; - taskInfo2.topActivityToken = taskInfo1.topActivityToken; taskInfo2.topActivityInSizeCompat = true; mOrganizer.onTaskInfoChanged(taskInfo2); verify(mSizeCompatUI).onSizeCompatInfoChanged(taskInfo1.displayId, taskInfo1.taskId, - taskInfo1.configuration, taskInfo1.topActivityToken, taskListener); + taskInfo1.configuration, taskListener); clearInvocations(mSizeCompatUI); mOrganizer.onTaskVanished(taskInfo1); verify(mSizeCompatUI).onSizeCompatInfoChanged(taskInfo1.displayId, taskInfo1.taskId, - null /* taskConfig */, null /* sizeCompatActivity*/, null /* taskListener */); + null /* taskConfig */, null /* taskListener */); } @Test @@ -433,6 +432,18 @@ public class ShellTaskOrganizerTests { assertEquals(listener.invisibleLocusTasks.size(), 0); } + @Test + public void testOnSizeCompatRestartButtonClicked() throws RemoteException { + RunningTaskInfo task1 = createTaskInfo(1, WINDOWING_MODE_MULTI_WINDOW); + task1.token = mock(WindowContainerToken.class); + + mOrganizer.onTaskAppeared(task1, null); + + mOrganizer.onSizeCompatRestartButtonClicked(task1.taskId); + + verify(mTaskOrganizerController).restartTaskTopActivityProcessIfVisible(task1.token); + } + private static RunningTaskInfo createTaskInfo(int taskId, int windowingMode) { RunningTaskInfo taskInfo = new RunningTaskInfo(); taskInfo.taskId = taskId; diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutOrganizerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutOrganizerTest.java index 963757045453..3c124bafc18a 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutOrganizerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutOrganizerTest.java @@ -50,6 +50,7 @@ import androidx.test.filters.SmallTest; import com.android.internal.R; import com.android.wm.shell.common.DisplayController; +import com.android.wm.shell.common.DisplayLayout; import com.android.wm.shell.common.ShellExecutor; import org.junit.Before; @@ -82,6 +83,8 @@ public class HideDisplayCutoutOrganizerTest { @Mock private Display mDisplay; @Mock + private DisplayLayout mDisplayLayout; + @Mock private IWindowContainerToken mMockRealToken; private WindowContainerToken mToken; @@ -95,6 +98,7 @@ public class HideDisplayCutoutOrganizerTest { MockitoAnnotations.initMocks(this); when(mMockDisplayController.getDisplay(anyInt())).thenReturn(mDisplay); + when(mMockDisplayController.getDisplayLayout(anyInt())).thenReturn(mDisplayLayout); HideDisplayCutoutOrganizer organizer = new HideDisplayCutoutOrganizer( mContext, mMockDisplayController, mMockMainExecutor); @@ -152,7 +156,7 @@ public class HideDisplayCutoutOrganizerTest { .getDisplayCutoutInsetsOfNaturalOrientation(); mContext.getOrCreateTestableResources().addOverride( R.dimen.status_bar_height_portrait, mFakeStatusBarHeightPortrait); - doReturn(Surface.ROTATION_0).when(mDisplay).getRotation(); + doReturn(Surface.ROTATION_0).when(mDisplayLayout).rotation(); mOrganizer.enableHideDisplayCutout(); verify(mOrganizer).registerOrganizer(DisplayAreaOrganizer.FEATURE_HIDE_DISPLAY_CUTOUT); @@ -171,7 +175,7 @@ public class HideDisplayCutoutOrganizerTest { .getDisplayCutoutInsetsOfNaturalOrientation(); mContext.getOrCreateTestableResources().addOverride( R.dimen.status_bar_height_landscape, mFakeStatusBarHeightLandscape); - doReturn(Surface.ROTATION_90).when(mDisplay).getRotation(); + doReturn(Surface.ROTATION_90).when(mDisplayLayout).rotation(); mOrganizer.enableHideDisplayCutout(); verify(mOrganizer).registerOrganizer(DisplayAreaOrganizer.FEATURE_HIDE_DISPLAY_CUTOUT); @@ -190,7 +194,7 @@ public class HideDisplayCutoutOrganizerTest { .getDisplayCutoutInsetsOfNaturalOrientation(); mContext.getOrCreateTestableResources().addOverride( R.dimen.status_bar_height_landscape, mFakeStatusBarHeightLandscape); - doReturn(Surface.ROTATION_270).when(mDisplay).getRotation(); + doReturn(Surface.ROTATION_270).when(mDisplayLayout).rotation(); mOrganizer.enableHideDisplayCutout(); verify(mOrganizer).registerOrganizer(DisplayAreaOrganizer.FEATURE_HIDE_DISPLAY_CUTOUT); @@ -219,4 +223,22 @@ public class HideDisplayCutoutOrganizerTest { assertThat(mOrganizer.mOffsetX).isEqualTo(0); assertThat(mOrganizer.mOffsetY).isEqualTo(0); } + + @Test + public void testDisplaySizeChange() { + doReturn(100).when(mDisplayLayout).width(); + doReturn(200).when(mDisplayLayout).height(); + doReturn(mFakeDefaultCutoutInsets).when(mOrganizer) + .getDisplayCutoutInsetsOfNaturalOrientation(); + mContext.getOrCreateTestableResources().addOverride( + R.dimen.status_bar_height_portrait, mFakeStatusBarHeightPortrait); + doReturn(Surface.ROTATION_0).when(mDisplayLayout).rotation(); + mOrganizer.enableHideDisplayCutout(); + assertThat(mOrganizer.mCurrentDisplayBounds).isEqualTo(new Rect(0, 15, 100, 200)); + + doReturn(200).when(mDisplayLayout).width(); + doReturn(400).when(mDisplayLayout).height(); + mOrganizer.updateBoundsAndOffsets(true); + assertThat(mOrganizer.mCurrentDisplayBounds).isEqualTo(new Rect(0, 15, 200, 400)); + } } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java index 950900337918..be786fb55b30 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java @@ -118,6 +118,7 @@ public class OneHandedControllerTest extends OneHandedTestCase { mDefaultTapAppToExitEnabled); when(mMockSettingsUitl.getSettingsSwipeToNotificationEnabled(any(), anyInt())).thenReturn( mDefaultSwipeToNotificationEnabled); + when(mMockSettingsUitl.getShortcutEnabled(any(), anyInt())).thenReturn(false); when(mMockDisplayAreaOrganizer.getLastDisplayBounds()).thenReturn( new Rect(0, 0, mDisplayLayout.width(), mDisplayLayout.height())); @@ -341,6 +342,7 @@ public class OneHandedControllerTest extends OneHandedTestCase { when(mSpiedTransitionState.getState()).thenReturn(STATE_ACTIVE); when(mSpiedTransitionState.isTransitioning()).thenReturn(false); when(mMockSettingsUitl.getOneHandedModeActivated(any(), anyInt())).thenReturn(true); + when(mSpiedOneHandedController.isShortcutEnabled()).thenReturn(true); mSpiedOneHandedController.onActivatedActionChanged(); verify(mSpiedOneHandedController, never()).startOneHanded(); @@ -352,6 +354,7 @@ public class OneHandedControllerTest extends OneHandedTestCase { when(mSpiedTransitionState.getState()).thenReturn(STATE_NONE); when(mSpiedTransitionState.isTransitioning()).thenReturn(false); when(mMockSettingsUitl.getOneHandedModeActivated(any(), anyInt())).thenReturn(false); + when(mSpiedOneHandedController.isShortcutEnabled()).thenReturn(true); mSpiedOneHandedController.onActivatedActionChanged(); verify(mSpiedOneHandedController, never()).startOneHanded(); @@ -363,6 +366,7 @@ public class OneHandedControllerTest extends OneHandedTestCase { when(mSpiedTransitionState.getState()).thenReturn(STATE_NONE); when(mSpiedTransitionState.isTransitioning()).thenReturn(false); when(mMockSettingsUitl.getOneHandedModeActivated(any(), anyInt())).thenReturn(true); + when(mSpiedOneHandedController.isShortcutEnabled()).thenReturn(true); mSpiedOneHandedController.onActivatedActionChanged(); verify(mSpiedOneHandedController).startOneHanded(); @@ -374,6 +378,7 @@ public class OneHandedControllerTest extends OneHandedTestCase { when(mSpiedTransitionState.getState()).thenReturn(STATE_ENTERING); when(mSpiedTransitionState.isTransitioning()).thenReturn(true); when(mMockSettingsUitl.getOneHandedModeActivated(any(), anyInt())).thenReturn(true); + when(mSpiedOneHandedController.isShortcutEnabled()).thenReturn(true); mSpiedOneHandedController.onActivatedActionChanged(); verify(mSpiedTransitionState, never()).setState(STATE_EXITING); @@ -384,6 +389,7 @@ public class OneHandedControllerTest extends OneHandedTestCase { when(mSpiedTransitionState.getState()).thenReturn(STATE_EXITING); when(mSpiedTransitionState.isTransitioning()).thenReturn(true); when(mMockSettingsUitl.getOneHandedModeActivated(any(), anyInt())).thenReturn(true); + when(mSpiedOneHandedController.isShortcutEnabled()).thenReturn(true); mSpiedOneHandedController.onActivatedActionChanged(); verify(mSpiedTransitionState, never()).setState(STATE_ENTERING); @@ -392,6 +398,7 @@ public class OneHandedControllerTest extends OneHandedTestCase { @Test public void testOneHandedDisabled_shortcutTrigger_thenAutoEnabled() { when(mSpiedOneHandedController.isOneHandedEnabled()).thenReturn(false); + when(mSpiedOneHandedController.isShortcutEnabled()).thenReturn(true); when(mSpiedTransitionState.getState()).thenReturn(STATE_NONE); when(mSpiedTransitionState.isTransitioning()).thenReturn(false); when(mMockSettingsUitl.getOneHandedModeActivated(any(), anyInt())).thenReturn(false); @@ -417,6 +424,7 @@ public class OneHandedControllerTest extends OneHandedTestCase { when(mSpiedOneHandedController.isOneHandedEnabled()).thenReturn(true); when(mSpiedTransitionState.getState()).thenReturn(STATE_NONE); when(mSpiedTransitionState.isTransitioning()).thenReturn(false); + when(mSpiedOneHandedController.isShortcutEnabled()).thenReturn(true); when(mSpiedOneHandedController.isSwipeToNotificationEnabled()).thenReturn(true); mSpiedOneHandedController.registerEventCallback(mMockEventCallback); mSpiedOneHandedController.onActivatedActionChanged(); @@ -425,11 +433,11 @@ public class OneHandedControllerTest extends OneHandedTestCase { } @Test - public void testNotifyShortcutState_whenUpdateOneHandedEnabled() { - when(mSpiedOneHandedController.isOneHandedEnabled()).thenReturn(false); + public void testNotifyShortcutState_whenSetOneHandedEnabled() { + when(mSpiedOneHandedController.isOneHandedEnabled()).thenReturn(true); when(mSpiedTransitionState.getState()).thenReturn(STATE_NONE); when(mSpiedTransitionState.isTransitioning()).thenReturn(false); - when(mSpiedOneHandedController.isSwipeToNotificationEnabled()).thenReturn(true); + when(mSpiedOneHandedController.isSwipeToNotificationEnabled()).thenReturn(false); mSpiedOneHandedController.registerEventCallback(mMockEventCallback); mSpiedOneHandedController.setOneHandedEnabled(true); @@ -448,4 +456,31 @@ public class OneHandedControllerTest extends OneHandedTestCase { // Verify no NPE crash and mMockShellMainExecutor never be execute. verify(mMockShellMainExecutor, never()).execute(any()); } + + @Test + public void testShortcutEnable_ableToAutoEnableOneHandedMode() { + when(mSpiedOneHandedController.isOneHandedEnabled()).thenReturn(false); + when(mSpiedTransitionState.getState()).thenReturn(STATE_NONE); + when(mSpiedTransitionState.isTransitioning()).thenReturn(false); + when(mSpiedOneHandedController.isShortcutEnabled()).thenReturn(true); + when(mSpiedOneHandedController.isSwipeToNotificationEnabled()).thenReturn(false); + when(mMockSettingsUitl.setOneHandedModeEnabled(any(), anyInt(), anyInt())).thenReturn( + false /* To avoid test runner create Toast */); + mSpiedOneHandedController.onActivatedActionChanged(); + + verify(mSpiedOneHandedController).notifyUserConfigChanged(anyBoolean()); + } + + @Test + public void testShortcutDisable_shouldNotAutoEnableOneHandedMode() { + when(mSpiedOneHandedController.isOneHandedEnabled()).thenReturn(false); + when(mSpiedTransitionState.getState()).thenReturn(STATE_NONE); + when(mSpiedTransitionState.isTransitioning()).thenReturn(false); + when(mSpiedOneHandedController.isSwipeToNotificationEnabled()).thenReturn(false); + when(mMockSettingsUitl.setOneHandedModeEnabled(any(), anyInt(), anyInt())).thenReturn(true); + mSpiedOneHandedController.onActivatedActionChanged(); + + verify(mMockSettingsUitl, never()).setOneHandedModeEnabled(any(), anyInt(), anyInt()); + verify(mSpiedOneHandedController, never()).notifyUserConfigChanged(anyBoolean()); + } } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsAlgorithmTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsAlgorithmTest.java index a0c6d1138698..90f898aa09da 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsAlgorithmTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsAlgorithmTest.java @@ -402,6 +402,64 @@ public class PipBoundsAlgorithmTest extends ShellTestCase { assertBoundsInclusionWithMargin("useDefaultBounds", defaultBounds, actualBounds); } + @Test + public void adjustNormalBoundsToFitMenu_alreadyFits() { + final Rect normalBounds = new Rect(0, 0, 400, 711); + final Size minMenuSize = new Size(396, 292); + mPipBoundsState.setAspectRatio( + ((float) normalBounds.width()) / ((float) normalBounds.height())); + + final Rect bounds = + mPipBoundsAlgorithm.adjustNormalBoundsToFitMenu(normalBounds, minMenuSize); + + assertEquals(normalBounds, bounds); + } + + @Test + public void adjustNormalBoundsToFitMenu_widthTooSmall() { + final Rect normalBounds = new Rect(0, 0, 297, 528); + final Size minMenuSize = new Size(396, 292); + mPipBoundsState.setAspectRatio( + ((float) normalBounds.width()) / ((float) normalBounds.height())); + + final Rect bounds = + mPipBoundsAlgorithm.adjustNormalBoundsToFitMenu(normalBounds, minMenuSize); + + assertEquals(minMenuSize.getWidth(), bounds.width()); + assertEquals(minMenuSize.getWidth() / mPipBoundsState.getAspectRatio(), + bounds.height(), 0.3f); + } + + @Test + public void adjustNormalBoundsToFitMenu_heightTooSmall() { + final Rect normalBounds = new Rect(0, 0, 400, 280); + final Size minMenuSize = new Size(396, 292); + mPipBoundsState.setAspectRatio( + ((float) normalBounds.width()) / ((float) normalBounds.height())); + + final Rect bounds = + mPipBoundsAlgorithm.adjustNormalBoundsToFitMenu(normalBounds, minMenuSize); + + assertEquals(minMenuSize.getHeight(), bounds.height()); + assertEquals(minMenuSize.getHeight() * mPipBoundsState.getAspectRatio(), + bounds.width(), 0.3f); + } + + @Test + public void adjustNormalBoundsToFitMenu_widthAndHeightTooSmall() { + final Rect normalBounds = new Rect(0, 0, 350, 280); + final Size minMenuSize = new Size(396, 292); + mPipBoundsState.setAspectRatio( + ((float) normalBounds.width()) / ((float) normalBounds.height())); + + final Rect bounds = + mPipBoundsAlgorithm.adjustNormalBoundsToFitMenu(normalBounds, minMenuSize); + + assertEquals(minMenuSize.getWidth(), bounds.width()); + assertEquals(minMenuSize.getWidth() / mPipBoundsState.getAspectRatio(), + bounds.height(), 0.3f); + } + private void overrideDefaultAspectRatio(float aspectRatio) { final TestableResources res = mContext.getOrCreateTestableResources(); res.addOverride( diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatHintPopupTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatHintPopupTest.java index 9845d4650d20..10fd7d705967 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatHintPopupTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatHintPopupTest.java @@ -22,7 +22,6 @@ import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.verify; import android.content.res.Configuration; -import android.os.IBinder; import android.testing.AndroidTestingRunner; import android.view.LayoutInflater; import android.widget.Button; @@ -52,7 +51,7 @@ import org.mockito.MockitoAnnotations; public class SizeCompatHintPopupTest extends ShellTestCase { @Mock private SyncTransactionQueue mSyncTransactionQueue; - @Mock private IBinder mActivityToken; + @Mock private SizeCompatUIController.SizeCompatUICallback mCallback; @Mock private ShellTaskOrganizer.TaskListener mTaskListener; @Mock private DisplayLayout mDisplayLayout; @@ -64,8 +63,9 @@ public class SizeCompatHintPopupTest extends ShellTestCase { MockitoAnnotations.initMocks(this); final int taskId = 1; - mLayout = new SizeCompatUILayout(mSyncTransactionQueue, mContext, new Configuration(), - taskId, mActivityToken, mTaskListener, mDisplayLayout, false /* hasShownHint*/); + mLayout = new SizeCompatUILayout(mSyncTransactionQueue, mCallback, mContext, + new Configuration(), taskId, mTaskListener, mDisplayLayout, + false /* hasShownHint */); mHint = (SizeCompatHintPopup) LayoutInflater.from(mContext).inflate(R.layout.size_compat_mode_hint, null); mHint.inject(mLayout); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatRestartButtonTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatRestartButtonTest.java index 5a43925a5677..a20a5e9e8d91 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatRestartButtonTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatRestartButtonTest.java @@ -22,7 +22,6 @@ import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.verify; import android.content.res.Configuration; -import android.os.IBinder; import android.testing.AndroidTestingRunner; import android.view.LayoutInflater; import android.widget.ImageButton; @@ -51,8 +50,10 @@ import org.mockito.MockitoAnnotations; @SmallTest public class SizeCompatRestartButtonTest extends ShellTestCase { + private static final int TASK_ID = 1; + @Mock private SyncTransactionQueue mSyncTransactionQueue; - @Mock private IBinder mActivityToken; + @Mock private SizeCompatUIController.SizeCompatUICallback mCallback; @Mock private ShellTaskOrganizer.TaskListener mTaskListener; @Mock private DisplayLayout mDisplayLayout; @@ -63,9 +64,9 @@ public class SizeCompatRestartButtonTest extends ShellTestCase { public void setUp() { MockitoAnnotations.initMocks(this); - final int taskId = 1; - mLayout = new SizeCompatUILayout(mSyncTransactionQueue, mContext, new Configuration(), - taskId, mActivityToken, mTaskListener, mDisplayLayout, false /* hasShownHint*/); + mLayout = new SizeCompatUILayout(mSyncTransactionQueue, mCallback, mContext, + new Configuration(), TASK_ID, mTaskListener, mDisplayLayout, + false /* hasShownHint */); mButton = (SizeCompatRestartButton) LayoutInflater.from(mContext).inflate(R.layout.size_compat_ui, null); mButton.inject(mLayout); @@ -75,12 +76,11 @@ public class SizeCompatRestartButtonTest extends ShellTestCase { @Test public void testOnClick() { - doNothing().when(mLayout).onRestartButtonClicked(); - final ImageButton button = mButton.findViewById(R.id.size_compat_restart_button); button.performClick(); verify(mLayout).onRestartButtonClicked(); + verify(mCallback).onSizeCompatRestartButtonClicked(TASK_ID); } @Test diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatUIControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatUIControllerTest.java index 806a90b7832a..8839f58ea889 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatUIControllerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatUIControllerTest.java @@ -27,7 +27,6 @@ import static org.mockito.Mockito.verify; import android.content.Context; import android.content.res.Configuration; -import android.os.IBinder; import android.testing.AndroidTestingRunner; import androidx.test.filters.SmallTest; @@ -61,7 +60,6 @@ public class SizeCompatUIControllerTest extends ShellTestCase { private @Mock DisplayController mMockDisplayController; private @Mock DisplayLayout mMockDisplayLayout; private @Mock DisplayImeController mMockImeController; - private @Mock IBinder mMockActivityToken; private @Mock ShellTaskOrganizer.TaskListener mMockTaskListener; private @Mock SyncTransactionQueue mMockSyncQueue; private @Mock SizeCompatUILayout mMockLayout; @@ -77,8 +75,7 @@ public class SizeCompatUIControllerTest extends ShellTestCase { mMockImeController, mMockSyncQueue) { @Override SizeCompatUILayout createLayout(Context context, int displayId, int taskId, - Configuration taskConfig, IBinder activityToken, - ShellTaskOrganizer.TaskListener taskListener) { + Configuration taskConfig, ShellTaskOrganizer.TaskListener taskListener) { return mMockLayout; } }; @@ -97,21 +94,21 @@ public class SizeCompatUIControllerTest extends ShellTestCase { // Verify that the restart button is added with non-null size compat info. mController.onSizeCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig, - mMockActivityToken, mMockTaskListener); + mMockTaskListener); verify(mController).createLayout(any(), eq(DISPLAY_ID), eq(TASK_ID), eq(taskConfig), - eq(mMockActivityToken), eq(mMockTaskListener)); + eq(mMockTaskListener)); // Verify that the restart button is updated with non-null new size compat info. final Configuration newTaskConfig = new Configuration(); mController.onSizeCompatInfoChanged(DISPLAY_ID, TASK_ID, newTaskConfig, - mMockActivityToken, mMockTaskListener); + mMockTaskListener); - verify(mMockLayout).updateSizeCompatInfo(taskConfig, mMockActivityToken, mMockTaskListener, + verify(mMockLayout).updateSizeCompatInfo(taskConfig, mMockTaskListener, false /* isImeShowing */); // Verify that the restart button is removed with null size compat info. - mController.onSizeCompatInfoChanged(DISPLAY_ID, TASK_ID, null, null, mMockTaskListener); + mController.onSizeCompatInfoChanged(DISPLAY_ID, TASK_ID, null, mMockTaskListener); verify(mMockLayout).release(); } @@ -120,7 +117,7 @@ public class SizeCompatUIControllerTest extends ShellTestCase { public void testOnDisplayRemoved() { final Configuration taskConfig = new Configuration(); mController.onSizeCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig, - mMockActivityToken, mMockTaskListener); + mMockTaskListener); mController.onDisplayRemoved(DISPLAY_ID + 1); @@ -135,7 +132,7 @@ public class SizeCompatUIControllerTest extends ShellTestCase { public void testOnDisplayConfigurationChanged() { final Configuration taskConfig = new Configuration(); mController.onSizeCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig, - mMockActivityToken, mMockTaskListener); + mMockTaskListener); final Configuration newTaskConfig = new Configuration(); mController.onDisplayConfigurationChanged(DISPLAY_ID + 1, newTaskConfig); @@ -151,7 +148,7 @@ public class SizeCompatUIControllerTest extends ShellTestCase { public void testChangeButtonVisibilityOnImeShowHide() { final Configuration taskConfig = new Configuration(); mController.onSizeCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig, - mMockActivityToken, mMockTaskListener); + mMockTaskListener); mController.onImeVisibilityChanged(DISPLAY_ID, true /* isShowing */); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatUILayoutTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatUILayoutTest.java index f33cfe86224f..ee4c81547bbd 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatUILayoutTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatUILayoutTest.java @@ -21,20 +21,16 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; -import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.Mockito.clearInvocations; -import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; -import android.app.ActivityClient; import android.content.res.Configuration; import android.graphics.Rect; -import android.os.IBinder; import android.testing.AndroidTestingRunner; import android.view.DisplayInfo; import android.view.SurfaceControl; @@ -66,7 +62,7 @@ public class SizeCompatUILayoutTest extends ShellTestCase { private static final int TASK_ID = 1; @Mock private SyncTransactionQueue mSyncTransactionQueue; - @Mock private IBinder mActivityToken; + @Mock private SizeCompatUIController.SizeCompatUICallback mCallback; @Mock private ShellTaskOrganizer.TaskListener mTaskListener; @Mock private DisplayLayout mDisplayLayout; @Mock private SizeCompatRestartButton mButton; @@ -80,8 +76,9 @@ public class SizeCompatUILayoutTest extends ShellTestCase { MockitoAnnotations.initMocks(this); mTaskConfig = new Configuration(); - mLayout = new SizeCompatUILayout(mSyncTransactionQueue, mContext, new Configuration(), - TASK_ID, mActivityToken, mTaskListener, mDisplayLayout, false /* hasShownHint*/); + mLayout = new SizeCompatUILayout(mSyncTransactionQueue, mCallback, mContext, + new Configuration(), TASK_ID, mTaskListener, mDisplayLayout, + false /* hasShownHint */); spyOn(mLayout); spyOn(mLayout.mButtonWindowManager); @@ -145,7 +142,7 @@ public class SizeCompatUILayoutTest extends ShellTestCase { // No diff clearInvocations(mLayout); - mLayout.updateSizeCompatInfo(mTaskConfig, mActivityToken, mTaskListener, + mLayout.updateSizeCompatInfo(mTaskConfig, mTaskListener, false /* isImeShowing */); verify(mLayout, never()).updateButtonSurfacePosition(); @@ -156,7 +153,7 @@ public class SizeCompatUILayoutTest extends ShellTestCase { clearInvocations(mLayout); final ShellTaskOrganizer.TaskListener newTaskListener = mock( ShellTaskOrganizer.TaskListener.class); - mLayout.updateSizeCompatInfo(mTaskConfig, mActivityToken, newTaskListener, + mLayout.updateSizeCompatInfo(mTaskConfig, newTaskListener, false /* isImeShowing */); verify(mLayout).release(); @@ -166,7 +163,7 @@ public class SizeCompatUILayoutTest extends ShellTestCase { clearInvocations(mLayout); final Configuration newTaskConfiguration = new Configuration(); newTaskConfiguration.windowConfiguration.setBounds(new Rect(0, 1000, 0, 2000)); - mLayout.updateSizeCompatInfo(newTaskConfiguration, mActivityToken, newTaskListener, + mLayout.updateSizeCompatInfo(newTaskConfiguration, newTaskListener, false /* isImeShowing */); verify(mLayout).updateButtonSurfacePosition(); @@ -228,12 +225,9 @@ public class SizeCompatUILayoutTest extends ShellTestCase { @Test public void testOnRestartButtonClicked() { - spyOn(ActivityClient.getInstance()); - doNothing().when(ActivityClient.getInstance()).restartActivityProcessIfVisible(any()); - mLayout.onRestartButtonClicked(); - verify(ActivityClient.getInstance()).restartActivityProcessIfVisible(mActivityToken); + verify(mCallback).onSizeCompatRestartButtonClicked(TASK_ID); } @Test diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java index 903e63ad6554..5061b2369bb2 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java @@ -15,6 +15,8 @@ */ package com.android.wm.shell.startingsurface; +import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_SPLASH_SCREEN; + import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy; @@ -91,7 +93,7 @@ public class StartingSurfaceDrawerTests { @Override protected boolean addWindow(int taskId, IBinder appToken, - View view, WindowManager wm, WindowManager.LayoutParams params) { + View view, WindowManager wm, WindowManager.LayoutParams params, int suggestType) { // listen for addView mAddWindowForTask = taskId; mViewThemeResId = view.getContext().getThemeResId(); @@ -145,9 +147,11 @@ public class StartingSurfaceDrawerTests { final int taskId = 1; final StartingWindowInfo windowInfo = createWindowInfo(taskId, android.R.style.Theme); - mStartingSurfaceDrawer.addSplashScreenStartingWindow(windowInfo, mBinder, false); + mStartingSurfaceDrawer.addSplashScreenStartingWindow(windowInfo, mBinder, + STARTING_WINDOW_TYPE_SPLASH_SCREEN); waitHandlerIdle(mTestHandler); - verify(mStartingSurfaceDrawer).addWindow(eq(taskId), eq(mBinder), any(), any(), any()); + verify(mStartingSurfaceDrawer).addWindow(eq(taskId), eq(mBinder), any(), any(), any(), + eq(STARTING_WINDOW_TYPE_SPLASH_SCREEN)); assertEquals(mStartingSurfaceDrawer.mAddWindowForTask, taskId); mStartingSurfaceDrawer.removeStartingWindow(windowInfo.taskInfo.taskId, null, null, false); @@ -161,9 +165,11 @@ public class StartingSurfaceDrawerTests { final int taskId = 1; final StartingWindowInfo windowInfo = createWindowInfo(taskId, 0); - mStartingSurfaceDrawer.addSplashScreenStartingWindow(windowInfo, mBinder, false); + mStartingSurfaceDrawer.addSplashScreenStartingWindow(windowInfo, mBinder, + STARTING_WINDOW_TYPE_SPLASH_SCREEN); waitHandlerIdle(mTestHandler); - verify(mStartingSurfaceDrawer).addWindow(eq(taskId), eq(mBinder), any(), any(), any()); + verify(mStartingSurfaceDrawer).addWindow(eq(taskId), eq(mBinder), any(), any(), any(), + eq(STARTING_WINDOW_TYPE_SPLASH_SCREEN)); assertNotEquals(mStartingSurfaceDrawer.mViewThemeResId, 0); } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/TaskSnapshotWindowTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/TaskSnapshotWindowTest.java index 5945840a8fa2..a098a6863493 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/TaskSnapshotWindowTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/TaskSnapshotWindowTest.java @@ -16,6 +16,7 @@ package com.android.wm.shell.startingsurface; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.content.res.Configuration.ORIENTATION_PORTRAIT; import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; @@ -81,7 +82,8 @@ public class TaskSnapshotWindowTest { mWindow = new TaskSnapshotWindow(new SurfaceControl(), snapshot, "Test", createTaskDescription(Color.WHITE, Color.RED, Color.BLUE), 0 /* appearance */, windowFlags /* windowFlags */, 0 /* privateWindowFlags */, - taskBounds, ORIENTATION_PORTRAIT, new InsetsState(), + taskBounds, ORIENTATION_PORTRAIT, ACTIVITY_TYPE_STANDARD, + 100 /* delayRemovalTime */, new InsetsState(), null /* clearWindow */, new TestShellExecutor()); } |