From b290e94020c22852ce821d6e7f8ad973f0549b6e Mon Sep 17 00:00:00 2001 From: Alex Chau Date: Fri, 29 Oct 2021 19:23:06 +0100 Subject: [PATCH 1/9] Crop letterbox insets when thumbnail is from different aspect ratio - Extracted isAspectLargelyDifferent and use that to determine if letterbox inset should be cropped - Always attempt to fit thumbnail width to avoid drawing area outside the thumbnail Bug: 199743725 Test: manual Change-Id: I2a7b20730055858b2376788e3a1fbbc66c0967e2 --- .../quickstep/views/TaskThumbnailView.java | 52 ++++++++++++------- 1 file changed, 34 insertions(+), 18 deletions(-) diff --git a/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java b/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java index a9db400df2..05ff881426 100644 --- a/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java +++ b/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java @@ -444,15 +444,36 @@ public class TaskThumbnailView extends View { float availableHeight = surfaceHeight - (thumbnailClipHint.top + thumbnailClipHint.bottom); - if (isRotated) { - float canvasAspect = canvasWidth / (float) canvasHeight; - float availableAspect = availableHeight / availableWidth; + float canvasAspect = canvasWidth / (float) canvasHeight; + float availableAspect = isRotated + ? availableHeight / availableWidth + : availableWidth / availableHeight; + boolean isAspectLargelyDifferent = Utilities.isRelativePercentDifferenceGreaterThan( + canvasAspect, availableAspect, 0.1f); + if (isRotated && isAspectLargelyDifferent) { // Do not rotate thumbnail if it would not improve fit - if (Utilities.isRelativePercentDifferenceGreaterThan(canvasAspect, - availableAspect, 0.1f)) { - isRotated = false; - isOrientationDifferent = false; + isRotated = false; + isOrientationDifferent = false; + } + + if (isAspectLargelyDifferent) { + // Crop letterbox insets if insets isn't already clipped + if (!TaskView.clipLeft(dp)) { + thumbnailClipHint.left = thumbnailData.letterboxInsets.left; } + if (!TaskView.clipRight(dp)) { + thumbnailClipHint.right = thumbnailData.letterboxInsets.right; + } + if (!TaskView.clipTop(dp)) { + thumbnailClipHint.top = thumbnailData.letterboxInsets.top; + } + if (!TaskView.clipBottom(dp)) { + thumbnailClipHint.bottom = thumbnailData.letterboxInsets.bottom; + } + availableWidth = surfaceWidth + - (thumbnailClipHint.left + thumbnailClipHint.right); + availableHeight = surfaceHeight + - (thumbnailClipHint.top + thumbnailClipHint.bottom); } final float targetW, targetH; @@ -463,30 +484,25 @@ public class TaskThumbnailView extends View { targetW = canvasWidth; targetH = canvasHeight; } - float canvasAspect = targetW / targetH; + float targetAspect = targetW / targetH; // Update the clipHint such that // > the final clipped position has same aspect ratio as requested by canvas - // > the clipped region is within the task insets if possible - // > the clipped region is not scaled up when drawing. If that is not possible - // while staying within the taskInsets, move outside the insets. + // > first fit the width and crop the extra height + // > if that will leave empty space, fit the height and crop the width instead float croppedWidth = availableWidth; - if (croppedWidth < targetW) { - croppedWidth = Math.min(targetW, surfaceWidth); - } - - float croppedHeight = croppedWidth / canvasAspect; + float croppedHeight = croppedWidth / targetAspect; if (croppedHeight > availableHeight) { croppedHeight = availableHeight; if (croppedHeight < targetH) { croppedHeight = Math.min(targetH, surfaceHeight); } - croppedWidth = croppedHeight * canvasAspect; + croppedWidth = croppedHeight * targetAspect; // One last check in case the task aspect radio messed up something if (croppedWidth > surfaceWidth) { croppedWidth = surfaceWidth; - croppedHeight = croppedWidth / canvasAspect; + croppedHeight = croppedWidth / targetAspect; } } From f6e31b1d219374fdf8e036988075cd0c7ec4daaa Mon Sep 17 00:00:00 2001 From: Lais Andrade Date: Wed, 27 Oct 2021 12:04:28 +0100 Subject: [PATCH 2/9] Fix overview scroll triggering haptics on swipe up gesture Add an extra check to the PagedView, before triggering the haptic callback, to avoid triggering this when the scroll happens outside an actual scroll action (e.g. some setter methods that update the scroll state). This was triggered on some calls to setCurrentPage, that was snapping the current scrolled page and triggering the View.onScrollChanged method. Fix: 201237536 Test: manual Change-Id: I9b29981dba408493c78873aea42d8615ea7573a0 Merged-In: I9b29981dba408493c78873aea42d8615ea7573a0 (cherry picked from commit bff03e2d0fa1186c0ef58f6cf643fffb5054e53e) --- src/com/android/launcher3/PagedView.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java index 523ac726f9..cefadf7941 100644 --- a/src/com/android/launcher3/PagedView.java +++ b/src/com/android/launcher3/PagedView.java @@ -1721,6 +1721,10 @@ public abstract class PagedView extends ViewGrou @Override protected void onScrollChanged(int l, int t, int oldl, int oldt) { + if (mScroller.isFinished()) { + // This was not caused by the scroller, skip it. + return; + } int newDestinationPage = getDestinationPage(); if (newDestinationPage >= 0 && newDestinationPage != mCurrentScrollOverPage) { mCurrentScrollOverPage = newDestinationPage; From e89a83b65d54d89da1a323c3b69fa2ee12677fbf Mon Sep 17 00:00:00 2001 From: Tracy Zhou Date: Mon, 8 Nov 2021 00:48:53 -0800 Subject: [PATCH 3/9] Track LauncherState, RecentsAnimation, resumed state for task bar in one place TODO: - Consider delaying animating task bar to stashed towards all apps state until user releasing their finger (tho in this change heuristic is applied for stashing and unstashing respectively) - Further consolidate some animation logic Bug: 204220602 Test: manual Change-Id: I58b4d035fcf65a9f5c68e69c129eae95b89b1c4a --- .../taskbar/LauncherTaskbarUIController.java | 251 +---------- .../TaskbarLauncherStateController.java | 395 ++++++++++++++++++ .../launcher3/util/MultiValueAlpha.java | 13 +- 3 files changed, 417 insertions(+), 242 deletions(-) create mode 100644 quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java diff --git a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java index aa312619bb..1c0c77328f 100644 --- a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java +++ b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java @@ -15,18 +15,10 @@ */ package com.android.launcher3.taskbar; -import static com.android.launcher3.LauncherState.HOTSEAT_ICONS; -import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN; -import static com.android.launcher3.taskbar.TaskbarStashController.FLAG_IN_APP; -import static com.android.launcher3.taskbar.TaskbarStashController.FLAG_IN_STASHED_LAUNCHER_STATE; -import static com.android.launcher3.taskbar.TaskbarStashController.TASKBAR_STASH_DURATION; -import static com.android.launcher3.taskbar.TaskbarViewController.ALPHA_INDEX_HOME; +import static com.android.launcher3.taskbar.TaskbarLauncherStateController.FLAG_RESUMED; import static com.android.systemui.shared.system.WindowManagerWrapper.ITYPE_EXTRA_NAVIGATION_BAR; import android.animation.Animator; -import android.animation.AnimatorListenerAdapter; -import android.animation.AnimatorSet; -import android.animation.ObjectAnimator; import android.annotation.ColorInt; import android.graphics.Rect; import android.os.RemoteException; @@ -44,28 +36,17 @@ import com.android.launcher3.LauncherState; import com.android.launcher3.QuickstepTransitionManager; import com.android.launcher3.R; import com.android.launcher3.Utilities; -import com.android.launcher3.anim.AnimatorListeners; -import com.android.launcher3.anim.PendingAnimation; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.logging.InstanceId; import com.android.launcher3.logging.InstanceIdSequence; import com.android.launcher3.model.data.ItemInfoWithIcon; import com.android.launcher3.model.data.WorkspaceItemInfo; -import com.android.launcher3.statemanager.StateManager; -import com.android.launcher3.util.MultiValueAlpha; -import com.android.launcher3.util.MultiValueAlpha.AlphaProperty; import com.android.launcher3.util.OnboardingPrefs; import com.android.quickstep.AnimatedFloat; import com.android.quickstep.RecentsAnimationCallbacks; -import com.android.quickstep.RecentsAnimationCallbacks.RecentsAnimationListener; -import com.android.quickstep.RecentsAnimationController; -import com.android.quickstep.views.RecentsView; -import com.android.systemui.shared.recents.model.ThumbnailData; import java.util.Arrays; -import java.util.HashMap; import java.util.Set; -import java.util.function.Supplier; import java.util.stream.Stream; /** @@ -77,82 +58,15 @@ public class LauncherTaskbarUIController extends TaskbarUIController { private final BaseQuickstepLauncher mLauncher; - private final AnimatedFloat mIconAlignmentForResumedState = - new AnimatedFloat(this::onIconAlignmentRatioChanged); - private final AnimatedFloat mIconAlignmentForGestureState = - new AnimatedFloat(this::onIconAlignmentRatioChanged); - private final AnimatedFloat mIconAlignmentForLauncherState = - new AnimatedFloat(this::onIconAlignmentRatioChangedForStateTransition); - private final DeviceProfile.OnDeviceProfileChangeListener mOnDeviceProfileChangeListener = this::onStashedInAppChanged; - private final StateManager.StateListener mStateListener = - new StateManager.StateListener() { - private Animator mAnimator; - - @Override - public void onStateTransitionStart(LauncherState toState) { - // Stash animation from going to launcher should be already handled in - // createAnimToLauncher. - TaskbarStashController controller = mControllers.taskbarStashController; - long duration = TASKBAR_STASH_DURATION; - controller.updateStateForFlag(FLAG_IN_STASHED_LAUNCHER_STATE, - toState.isTaskbarStashed()); - Animator stashAnimator = controller.applyStateWithoutStart(duration); - if (stashAnimator != null) { - if (mAnimator != null) { - mAnimator.cancel(); - } - PendingAnimation pendingAnimation = new PendingAnimation(duration); - pendingAnimation.add(stashAnimator); - pendingAnimation.setFloat(mIconAlignmentForLauncherState, - AnimatedFloat.VALUE, toState.isTaskbarStashed() ? 0 : 1, - FAST_OUT_SLOW_IN); - pendingAnimation.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationStart(Animator animator) { - mTargetStateOverrideForStateTransition = toState; - // Copy hotseat alpha over to taskbar icons - mIconAlphaForHome.setValue(mLauncher.getHotseat().getIconsAlpha()); - mLauncher.getHotseat().setIconsAlpha(0); - } - - @Override - public void onAnimationEnd(Animator animator) { - if (toState.isTaskbarStashed()) { - // Reset hotseat alpha to default - mLauncher.getHotseat().setIconsAlpha(1); - } - mTargetStateOverrideForStateTransition = null; - mAnimator = null; - } - }); - mAnimator = pendingAnimation.buildAnim(); - mAnimator.start(); - } - } - - @Override - public void onStateTransitionComplete(LauncherState finalState) { - TaskbarStashController controller = mControllers.taskbarStashController; - controller.updateStateForFlag(FLAG_IN_STASHED_LAUNCHER_STATE, - finalState.isTaskbarStashed()); - controller.applyState(); - } - }; - // Initialized in init. private TaskbarControllers mControllers; - private AnimatedFloat mTaskbarBackgroundAlpha; private AnimatedFloat mTaskbarOverrideBackgroundAlpha; - private AlphaProperty mIconAlphaForHome; - private boolean mIsAnimatingToLauncherViaResume; - private boolean mIsAnimatingToLauncherViaGesture; private TaskbarKeyguardController mKeyguardController; - - private LauncherState mTargetStateOverride = null; - private LauncherState mTargetStateOverrideForStateTransition = null; + private final TaskbarLauncherStateController + mTaskbarLauncherStateController = new TaskbarLauncherStateController(); private final DeviceProfile.OnDeviceProfileChangeListener mProfileChangeListener = new DeviceProfile.OnDeviceProfileChangeListener() { @@ -171,37 +85,26 @@ public class LauncherTaskbarUIController extends TaskbarUIController { protected void init(TaskbarControllers taskbarControllers) { mControllers = taskbarControllers; - mTaskbarBackgroundAlpha = mControllers.taskbarDragLayerController - .getTaskbarBackgroundAlpha(); + mTaskbarLauncherStateController.init(mControllers, mLauncher); mTaskbarOverrideBackgroundAlpha = mControllers.taskbarDragLayerController .getOverrideBackgroundAlpha(); - MultiValueAlpha taskbarIconAlpha = mControllers.taskbarViewController.getTaskbarIconAlpha(); - mIconAlphaForHome = taskbarIconAlpha.getProperty(ALPHA_INDEX_HOME); - mLauncher.setTaskbarUIController(this); mKeyguardController = taskbarControllers.taskbarKeyguardController; onLauncherResumedOrPaused(mLauncher.hasBeenResumed(), true /* fromInit */); - mIconAlignmentForResumedState.finishAnimation(); - onIconAlignmentRatioChanged(); onStashedInAppChanged(mLauncher.getDeviceProfile()); mLauncher.addOnDeviceProfileChangeListener(mOnDeviceProfileChangeListener); - mLauncher.getStateManager().addStateListener(mStateListener); mLauncher.addOnDeviceProfileChangeListener(mProfileChangeListener); } @Override protected void onDestroy() { onLauncherResumedOrPaused(false); - mIconAlignmentForResumedState.finishAnimation(); - mIconAlignmentForGestureState.finishAnimation(); - mIconAlignmentForLauncherState.finishAnimation(); + mTaskbarLauncherStateController.onDestroy(); mLauncher.removeOnDeviceProfileChangeListener(mOnDeviceProfileChangeListener); - mLauncher.getStateManager().removeStateListener(mStateListener); - mLauncher.getHotseat().setIconsAlpha(1f); mLauncher.setTaskbarUIController(null); mLauncher.removeOnDeviceProfileChangeListener(mProfileChangeListener); updateTaskTransitionSpec(true); @@ -209,11 +112,7 @@ public class LauncherTaskbarUIController extends TaskbarUIController { @Override protected boolean isTaskbarTouchable() { - return !isAnimatingToLauncher(); - } - - private boolean isAnimatingToLauncher() { - return mIsAnimatingToLauncherViaResume || mIsAnimatingToLauncherViaGesture; + return !mTaskbarLauncherStateController.isAnimatingToLauncher(); } @Override @@ -240,24 +139,9 @@ public class LauncherTaskbarUIController extends TaskbarUIController { } } - long duration = QuickstepTransitionManager.CONTENT_ALPHA_DURATION; - if (fromInit) { - // Since we are creating the starting state, we don't have a state to animate from, so - // set our state immediately. - duration = 0; - } - ObjectAnimator anim = mIconAlignmentForResumedState.animateToValue( - getCurrentIconAlignmentRatio(), isResumed ? 1 : 0) - .setDuration(duration); - - anim.addListener(AnimatorListeners.forEndCallback( - () -> mIsAnimatingToLauncherViaResume = false)); - anim.start(); - mIsAnimatingToLauncherViaResume = isResumed; - - TaskbarStashController stashController = mControllers.taskbarStashController; - stashController.updateStateForFlag(FLAG_IN_APP, !isResumed); - stashController.applyState(duration); + mTaskbarLauncherStateController.updateStateForFlag(FLAG_RESUMED, isResumed); + mTaskbarLauncherStateController.applyState( + fromInit ? 0 : QuickstepTransitionManager.CONTENT_ALPHA_DURATION); } /** @@ -268,77 +152,7 @@ public class LauncherTaskbarUIController extends TaskbarUIController { */ public Animator createAnimToLauncher(@NonNull LauncherState toState, @NonNull RecentsAnimationCallbacks callbacks, long duration) { - AnimatorSet animatorSet = new AnimatorSet(); - TaskbarStashController stashController = mControllers.taskbarStashController; - stashController.updateStateForFlag(FLAG_IN_STASHED_LAUNCHER_STATE, - toState.isTaskbarStashed()); - if (toState.isTaskbarStashed()) { - animatorSet.play(stashController.applyStateWithoutStart(duration)); - } else { - animatorSet.play(mIconAlignmentForGestureState - .animateToValue(1) - .setDuration(duration)); - } - animatorSet.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animator) { - mTargetStateOverride = null; - animator.removeListener(this); - } - - @Override - public void onAnimationStart(Animator animator) { - mTargetStateOverride = toState; - mIsAnimatingToLauncherViaGesture = true; - stashController.updateStateForFlag(FLAG_IN_APP, false); - stashController.applyState(duration); - } - }); - - TaskBarRecentsAnimationListener listener = new TaskBarRecentsAnimationListener(callbacks); - callbacks.addListener(listener); - RecentsView recentsView = mLauncher.getOverviewPanel(); - recentsView.setTaskLaunchListener(() -> { - listener.endGestureStateOverride(true); - callbacks.removeListener(listener); - }); - - return animatorSet; - } - - private float getCurrentIconAlignmentRatio() { - return Math.max(mIconAlignmentForResumedState.value, mIconAlignmentForGestureState.value); - } - - private float getCurrentIconAlignmentRatioForLauncherState() { - return mIconAlignmentForLauncherState.value; - } - - private void onIconAlignmentRatioChangedForStateTransition() { - onIconAlignmentRatioChanged( - mTargetStateOverrideForStateTransition != null - ? mTargetStateOverrideForStateTransition - : mLauncher.getStateManager().getState(), - this::getCurrentIconAlignmentRatioForLauncherState); - } - - private void onIconAlignmentRatioChanged() { - onIconAlignmentRatioChanged(mTargetStateOverride != null ? mTargetStateOverride - : mLauncher.getStateManager().getState(), this::getCurrentIconAlignmentRatio); - } - - private void onIconAlignmentRatioChanged(LauncherState state, - Supplier alignmentSupplier) { - if (mControllers == null) { - return; - } - float alignment = alignmentSupplier.get(); - mControllers.taskbarViewController.setLauncherIconAlignment( - alignment, mLauncher.getDeviceProfile()); - - mTaskbarBackgroundAlpha.updateValue(1 - alignment); - - setIconAlpha(state, alignment); + return mTaskbarLauncherStateController.createAnimToLauncher(toState, callbacks, duration); } /** @@ -358,20 +172,6 @@ public class LauncherTaskbarUIController extends TaskbarUIController { return mControllers.taskbarActivityContext.getDragLayer(); } - private void setIconAlpha(LauncherState state, float progress) { - if ((state.getVisibleElements(mLauncher) & HOTSEAT_ICONS) != 0) { - // If the hotseat icons are visible, then switch taskbar in last frame - setTaskbarViewVisible(progress < 1); - } else { - mIconAlphaForHome.setValue(1 - progress); - } - } - - private void setTaskbarViewVisible(boolean isVisible) { - mIconAlphaForHome.setValue(isVisible ? 1 : 0); - mLauncher.getHotseat().setIconsAlpha(isVisible ? 0f : 1f); - } - @Override protected void onStashedInAppChanged() { onStashedInAppChanged(mLauncher.getDeviceProfile()); @@ -451,35 +251,4 @@ public class LauncherTaskbarUIController extends TaskbarUIController { mLauncher.logAppLaunch(mControllers.taskbarActivityContext.getStatsLogManager(), item, instanceId); } - - private final class TaskBarRecentsAnimationListener implements RecentsAnimationListener { - private final RecentsAnimationCallbacks mCallbacks; - - TaskBarRecentsAnimationListener(RecentsAnimationCallbacks callbacks) { - mCallbacks = callbacks; - } - - @Override - public void onRecentsAnimationCanceled(HashMap thumbnailDatas) { - endGestureStateOverride(true); - } - - @Override - public void onRecentsAnimationFinished(RecentsAnimationController controller) { - endGestureStateOverride(!controller.getFinishTargetIsLauncher()); - } - - private void endGestureStateOverride(boolean finishedToApp) { - mCallbacks.removeListener(this); - mIsAnimatingToLauncherViaGesture = false; - - mIconAlignmentForGestureState - .animateToValue(0) - .start(); - - TaskbarStashController controller = mControllers.taskbarStashController; - controller.updateStateForFlag(FLAG_IN_APP, finishedToApp); - controller.applyState(); - } - } } diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java new file mode 100644 index 0000000000..2693bc343d --- /dev/null +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java @@ -0,0 +1,395 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.launcher3.taskbar; + +import static com.android.launcher3.LauncherState.HOTSEAT_ICONS; +import static com.android.launcher3.taskbar.TaskbarStashController.FLAG_IN_APP; +import static com.android.launcher3.taskbar.TaskbarStashController.FLAG_IN_STASHED_LAUNCHER_STATE; +import static com.android.launcher3.taskbar.TaskbarStashController.TASKBAR_STASH_DURATION; +import static com.android.launcher3.taskbar.TaskbarViewController.ALPHA_INDEX_HOME; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.AnimatorSet; +import android.animation.ObjectAnimator; + +import androidx.annotation.NonNull; + +import com.android.launcher3.BaseQuickstepLauncher; +import com.android.launcher3.LauncherState; +import com.android.launcher3.statemanager.StateManager; +import com.android.launcher3.util.MultiValueAlpha; +import com.android.quickstep.AnimatedFloat; +import com.android.quickstep.RecentsAnimationCallbacks; +import com.android.quickstep.RecentsAnimationController; +import com.android.quickstep.views.RecentsView; +import com.android.systemui.shared.recents.model.ThumbnailData; + +import java.util.HashMap; +import java.util.function.Consumer; +import java.util.function.Supplier; + +/** + * Track LauncherState, RecentsAnimation, resumed state for task bar in one place here and animate + * the task bar accordingly. + */ + public class TaskbarLauncherStateController { + + public static final int FLAG_RESUMED = 1 << 0; + public static final int FLAG_RECENTS_ANIMATION_RUNNING = 1 << 1; + public static final int FLAG_TRANSITION_STATE_START_STASHED = 1 << 2; + public static final int FLAG_TRANSITION_STATE_COMMITTED_STASHED = 1 << 3; + + private final AnimatedFloat mIconAlignmentForResumedState = + new AnimatedFloat(this::onIconAlignmentRatioChanged); + private final AnimatedFloat mIconAlignmentForGestureState = + new AnimatedFloat(this::onIconAlignmentRatioChanged); + private final AnimatedFloat mIconAlignmentForLauncherState = + new AnimatedFloat(this::onIconAlignmentRatioChangedForStateTransition); + + private TaskbarControllers mControllers; + private AnimatedFloat mTaskbarBackgroundAlpha; + private MultiValueAlpha.AlphaProperty mIconAlphaForHome; + private BaseQuickstepLauncher mLauncher; + + private int mPrevState; + private int mState; + + private LauncherState mTargetStateOverride = null; + private LauncherState mTargetStateOverrideForStateTransition = null; + + private boolean mIsAnimatingToLauncherViaGesture; + private boolean mIsAnimatingToLauncherViaResume; + + private final StateManager.StateListener mStateListener = + new StateManager.StateListener() { + + @Override + public void onStateTransitionStart(LauncherState toState) { + mTargetStateOverrideForStateTransition = toState; + updateStateForFlag(FLAG_TRANSITION_STATE_START_STASHED, + toState.isTaskbarStashed()); + applyState(); + } + + @Override + public void onStateTransitionComplete(LauncherState finalState) { + updateStateForFlag(FLAG_TRANSITION_STATE_COMMITTED_STASHED, + finalState.isTaskbarStashed()); + applyState(); + } + }; + + public void init(TaskbarControllers controllers, BaseQuickstepLauncher launcher) { + mControllers = controllers; + mLauncher = launcher; + + mTaskbarBackgroundAlpha = mControllers.taskbarDragLayerController + .getTaskbarBackgroundAlpha(); + MultiValueAlpha taskbarIconAlpha = mControllers.taskbarViewController.getTaskbarIconAlpha(); + mIconAlphaForHome = taskbarIconAlpha.getProperty(ALPHA_INDEX_HOME); + mIconAlphaForHome.setConsumer( + (Consumer) alpha -> mLauncher.getHotseat().setIconsAlpha(alpha > 0 ? 0 : 1)); + + mIconAlignmentForResumedState.finishAnimation(); + onIconAlignmentRatioChanged(); + + mLauncher.getStateManager().addStateListener(mStateListener); + } + + public void onDestroy() { + mIconAlignmentForResumedState.finishAnimation(); + mIconAlignmentForGestureState.finishAnimation(); + mIconAlignmentForLauncherState.finishAnimation(); + + mLauncher.getHotseat().setIconsAlpha(1f); + mLauncher.getStateManager().removeStateListener(mStateListener); + } + + public Animator createAnimToLauncher(@NonNull LauncherState toState, + @NonNull RecentsAnimationCallbacks callbacks, long duration) { + // If going to overview, stash the task bar + // If going home, align the icons to hotseat + AnimatorSet animatorSet = new AnimatorSet(); + + TaskbarStashController stashController = mControllers.taskbarStashController; + stashController.updateStateForFlag(FLAG_IN_STASHED_LAUNCHER_STATE, + toState.isTaskbarStashed()); + stashController.updateStateForFlag(FLAG_IN_APP, false); + + updateStateForFlag(FLAG_RECENTS_ANIMATION_RUNNING, true); + animatorSet.play(stashController.applyStateWithoutStart(duration)); + animatorSet.play(applyState(duration, false)); + animatorSet.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animator) { + mTargetStateOverride = null; + animator.removeListener(this); + } + + @Override + public void onAnimationStart(Animator animator) { + mTargetStateOverride = toState; + } + }); + + TaskBarRecentsAnimationListener listener = new TaskBarRecentsAnimationListener(callbacks); + callbacks.addListener(listener); + RecentsView recentsView = mLauncher.getOverviewPanel(); + recentsView.setTaskLaunchListener(() -> { + listener.endGestureStateOverride(true); + callbacks.removeListener(listener); + }); + return animatorSet; + } + + public boolean isAnimatingToLauncher() { + return mIsAnimatingToLauncherViaResume || mIsAnimatingToLauncherViaGesture; + } + + /** + * Updates the proper flag to change the state of the task bar. + * + * Note that this only updates the flag. {@link #applyState()} needs to be called separately. + * + * @param flag The flag to update. + * @param enabled Whether to enable the flag + */ + public void updateStateForFlag(int flag, boolean enabled) { + if (enabled) { + mState |= flag; + } else { + mState &= ~flag; + } + } + + private boolean hasAnyFlag(int flagMask) { + return hasAnyFlag(mState, flagMask); + } + + private boolean hasAnyFlag(int flags, int flagMask) { + return (flags & flagMask) != 0; + } + + public void applyState() { + applyState(TASKBAR_STASH_DURATION); + } + + public void applyState(long duration) { + applyState(duration, true); + } + + public Animator applyState(boolean start) { + return applyState(TASKBAR_STASH_DURATION, start); + } + + public Animator applyState(long duration, boolean start) { + Animator animator = null; + if (mPrevState != mState) { + int changedFlags = mPrevState ^ mState; + animator = onStateChangeApplied(changedFlags, duration, start); + mPrevState = mState; + } + return animator; + } + + private Animator onStateChangeApplied(int changedFlags, long duration, boolean start) { + AnimatorSet animatorSet = new AnimatorSet(); + if (hasAnyFlag(changedFlags, FLAG_RESUMED)) { + boolean isResumed = isResumed(); + ObjectAnimator anim = mIconAlignmentForResumedState + .animateToValue(getCurrentIconAlignmentRatio(), isResumed ? 1 : 0) + .setDuration(duration); + + anim.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + mIsAnimatingToLauncherViaResume = false; + } + + @Override + public void onAnimationStart(Animator animation) { + mIsAnimatingToLauncherViaResume = isResumed; + + TaskbarStashController stashController = mControllers.taskbarStashController; + stashController.updateStateForFlag(FLAG_IN_APP, !isResumed); + stashController.applyState(duration); + } + }); + animatorSet.play(anim); + } + + if (hasAnyFlag(changedFlags, FLAG_RECENTS_ANIMATION_RUNNING)) { + boolean isRecentsAnimationRunning = isRecentsAnimationRunning(); + Animator animator = mIconAlignmentForGestureState + .animateToValue(isRecentsAnimationRunning ? 1 : 0); + if (isRecentsAnimationRunning) { + animator.setDuration(duration); + } + animator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + mIsAnimatingToLauncherViaGesture = false; + } + + @Override + public void onAnimationStart(Animator animation) { + mIsAnimatingToLauncherViaGesture = isRecentsAnimationRunning(); + } + }); + animatorSet.play(animator); + } + + if (hasAnyFlag(changedFlags, FLAG_TRANSITION_STATE_START_STASHED)) { + playStateTransitionAnim(isTransitionStateStartStashed(), animatorSet, duration, + false /* committed */); + } + + if (hasAnyFlag(changedFlags, FLAG_TRANSITION_STATE_COMMITTED_STASHED)) { + playStateTransitionAnim(isTransitionStateCommittedStashed(), animatorSet, duration, + true /* committed */); + } + + if (start) { + animatorSet.start(); + } + return animatorSet; + } + + private void playStateTransitionAnim(boolean isTransitionStateStashed, + AnimatorSet animatorSet, long duration, boolean committed) { + TaskbarStashController controller = mControllers.taskbarStashController; + controller.updateStateForFlag(FLAG_IN_STASHED_LAUNCHER_STATE, + isTransitionStateStashed); + Animator stashAnimator = controller.applyStateWithoutStart(duration); + if (stashAnimator != null) { + stashAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + if (isTransitionStateStashed && committed) { + // Reset hotseat alpha to default + mLauncher.getHotseat().setIconsAlpha(1); + } + mTargetStateOverrideForStateTransition = null; + } + + @Override + public void onAnimationStart(Animator animation) { + mIconAlphaForHome.setValue(mLauncher.getHotseat().getIconsAlpha()); + } + }); + animatorSet.play(stashAnimator); + animatorSet.play(mIconAlignmentForLauncherState.animateToValue( + getCurrentIconAlignmentRatioForLauncherState(), + isTransitionStateStashed ? 0 : 1)); + } else { + mTargetStateOverrideForStateTransition = null; + } + } + + private boolean isResumed() { + return (mState & FLAG_RESUMED) != 0; + } + + private boolean isRecentsAnimationRunning() { + return (mState & FLAG_RECENTS_ANIMATION_RUNNING) != 0; + } + + private boolean isTransitionStateStartStashed() { + return (mState & FLAG_TRANSITION_STATE_START_STASHED) != 0; + } + + private boolean isTransitionStateCommittedStashed() { + return (mState & FLAG_TRANSITION_STATE_COMMITTED_STASHED) != 0; + } + + private void onIconAlignmentRatioChangedForStateTransition() { + onIconAlignmentRatioChanged( + mTargetStateOverrideForStateTransition != null + ? mTargetStateOverrideForStateTransition + : mLauncher.getStateManager().getState(), + this::getCurrentIconAlignmentRatioForLauncherState); + } + + private void onIconAlignmentRatioChanged() { + onIconAlignmentRatioChanged(mTargetStateOverride != null ? mTargetStateOverride + : mLauncher.getStateManager().getState(), this::getCurrentIconAlignmentRatio); + } + + private void onIconAlignmentRatioChanged(LauncherState state, + Supplier alignmentSupplier) { + if (mControllers == null) { + return; + } + float alignment = alignmentSupplier.get(); + mControllers.taskbarViewController.setLauncherIconAlignment( + alignment, mLauncher.getDeviceProfile()); + + mTaskbarBackgroundAlpha.updateValue(1 - alignment); + + setIconAlpha(state, alignment); + } + + private float getCurrentIconAlignmentRatio() { + return Math.max(mIconAlignmentForResumedState.value, mIconAlignmentForGestureState.value); + } + + private float getCurrentIconAlignmentRatioForLauncherState() { + return mIconAlignmentForLauncherState.value; + } + + private void setIconAlpha(LauncherState state, float progress) { + if ((state.getVisibleElements(mLauncher) & HOTSEAT_ICONS) != 0) { + // If the hotseat icons are visible, then switch taskbar in last frame + setTaskbarViewVisible(progress < 1); + } else { + mIconAlphaForHome.setValue(1 - progress); + } + } + + private void setTaskbarViewVisible(boolean isVisible) { + mIconAlphaForHome.setValue(isVisible ? 1 : 0); + } + + private final class TaskBarRecentsAnimationListener implements + RecentsAnimationCallbacks.RecentsAnimationListener { + private final RecentsAnimationCallbacks mCallbacks; + + TaskBarRecentsAnimationListener(RecentsAnimationCallbacks callbacks) { + mCallbacks = callbacks; + } + + @Override + public void onRecentsAnimationCanceled(HashMap thumbnailDatas) { + endGestureStateOverride(true); + } + + @Override + public void onRecentsAnimationFinished(RecentsAnimationController controller) { + endGestureStateOverride(!controller.getFinishTargetIsLauncher()); + } + + private void endGestureStateOverride(boolean finishedToApp) { + mCallbacks.removeListener(this); + updateStateForFlag(FLAG_RECENTS_ANIMATION_RUNNING, false); + applyState(); + + TaskbarStashController controller = mControllers.taskbarStashController; + controller.updateStateForFlag(FLAG_IN_APP, finishedToApp); + controller.applyState(); + } + } +} diff --git a/src/com/android/launcher3/util/MultiValueAlpha.java b/src/com/android/launcher3/util/MultiValueAlpha.java index bd39391f44..326141d66e 100644 --- a/src/com/android/launcher3/util/MultiValueAlpha.java +++ b/src/com/android/launcher3/util/MultiValueAlpha.java @@ -24,6 +24,7 @@ import android.view.View; import com.android.launcher3.anim.AlphaUpdateListener; import java.util.Arrays; +import java.util.function.Consumer; /** * Utility class to handle separating a single value as a factor of multiple values @@ -85,6 +86,8 @@ public class MultiValueAlpha { // Factor of all other alpha channels, only valid if mMyMask is present in mValidMask. private float mOthers = 1; + private Consumer mConsumer; + AlphaProperty(int myMask) { mMyMask = myMask; } @@ -109,16 +112,24 @@ public class MultiValueAlpha { mValidMask = mMyMask; mValue = value; - mView.setAlpha(mOthers * mValue); + final float alpha = mOthers * mValue; + mView.setAlpha(alpha); if (mUpdateVisibility) { AlphaUpdateListener.updateVisibility(mView); } + if (mConsumer != null) { + mConsumer.accept(mValue); + } } public float getValue() { return mValue; } + public void setConsumer(Consumer consumer) { + mConsumer = consumer; + } + @Override public String toString() { return Float.toString(mValue); From 953eb8041e18d12ecd9d93eb37bb1f18e8374979 Mon Sep 17 00:00:00 2001 From: Andras Kloczl Date: Fri, 15 Oct 2021 21:16:48 +0100 Subject: [PATCH 4/9] Fix LauncherProvider newScreenId issue Remove maxScreenId from LauncherProvider and whenever we need a new screenId, query the database to calculate a new screenId. Also converted and refactored AddWorkspaceItemsTaskTest and added some extra test cases. Test: manual & AddWorkspaceItemsTaskTest.kt Bug: 199160559 Change-Id: I185f6823fed171d778af0130497f5ffaf89c0a70 --- .../android/launcher3/LauncherProvider.java | 37 ++++--------------- src/com/android/launcher3/Workspace.java | 10 ++--- .../model/AddWorkspaceItemsTask.java | 6 --- .../android/launcher3/model/ModelWriter.java | 2 +- .../model/AddWorkspaceItemsTaskTest.java | 20 ---------- 5 files changed, 13 insertions(+), 62 deletions(-) diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java index 9f3d445562..68e19cb85d 100644 --- a/src/com/android/launcher3/LauncherProvider.java +++ b/src/com/android/launcher3/LauncherProvider.java @@ -383,7 +383,7 @@ public class LauncherProvider extends ContentProvider { case LauncherSettings.Settings.METHOD_NEW_SCREEN_ID: { Bundle result = new Bundle(); result.putInt(LauncherSettings.Settings.EXTRA_VALUE, - mOpenHelper.generateNewScreenId()); + mOpenHelper.getNewScreenId()); return result; } case LauncherSettings.Settings.METHOD_CREATE_EMPTY_DB: { @@ -628,7 +628,6 @@ public class LauncherProvider extends ContentProvider { private final Context mContext; private final boolean mForMigration; private int mMaxItemId = -1; - private int mMaxScreenId = -1; private boolean mBackupTableExists; private boolean mHotseatRestoreTableExists; @@ -672,9 +671,6 @@ public class LauncherProvider extends ContentProvider { if (mMaxItemId == -1) { mMaxItemId = initializeMaxItemId(getWritableDatabase()); } - if (mMaxScreenId == -1) { - mMaxScreenId = initializeMaxScreenId(getWritableDatabase()); - } } @Override @@ -682,7 +678,6 @@ public class LauncherProvider extends ContentProvider { if (LOGD) Log.d(TAG, "creating new launcher database"); mMaxItemId = 1; - mMaxScreenId = 0; addFavoritesTable(db, false); @@ -1043,36 +1038,19 @@ public class LauncherProvider extends ContentProvider { public void checkId(ContentValues values) { int id = values.getAsInteger(Favorites._ID); mMaxItemId = Math.max(id, mMaxItemId); - - Integer screen = values.getAsInteger(Favorites.SCREEN); - Integer container = values.getAsInteger(Favorites.CONTAINER); - if (screen != null && container != null - && container.intValue() == Favorites.CONTAINER_DESKTOP) { - mMaxScreenId = Math.max(screen, mMaxScreenId); - } } private int initializeMaxItemId(SQLiteDatabase db) { return getMaxId(db, "SELECT MAX(%1$s) FROM %2$s", Favorites._ID, Favorites.TABLE_NAME); } - // Generates a new ID to use for an workspace screen in your database. This method - // should be only called from the main UI thread. As an exception, we do call it when we - // call the constructor from the worker thread; however, this doesn't extend until after the - // constructor is called, and we only pass a reference to LauncherProvider to LauncherApp - // after that point - public int generateNewScreenId() { - if (mMaxScreenId < 0) { - throw new RuntimeException("Error: max screen id was not initialized"); - } - mMaxScreenId += 1; - return mMaxScreenId; - } - - private int initializeMaxScreenId(SQLiteDatabase db) { - return getMaxId(db, "SELECT MAX(%1$s) FROM %2$s WHERE %3$s = %4$d AND %1$s >= 0", + // Returns a new ID to use for an workspace screen in your database that is greater than all + // existing screen IDs. + private int getNewScreenId() { + return getMaxId(getWritableDatabase(), + "SELECT MAX(%1$s) FROM %2$s WHERE %3$s = %4$d AND %1$s >= 0", Favorites.SCREEN, Favorites.TABLE_NAME, Favorites.CONTAINER, - Favorites.CONTAINER_DESKTOP); + Favorites.CONTAINER_DESKTOP) + 1; } @Thunk int loadFavorites(SQLiteDatabase db, AutoInstallsLayout loader) { @@ -1081,7 +1059,6 @@ public class LauncherProvider extends ContentProvider { // Ensure that the max ids are initialized mMaxItemId = initializeMaxItemId(db); - mMaxScreenId = initializeMaxScreenId(db); return count; } } diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index ce06c6e1fd..131fbfb071 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -869,13 +869,13 @@ public class Workspace extends PagedView mWorkspaceScreens.remove(emptyScreenId); mScreenOrder.removeValue(emptyScreenId); - int newScreenId = -1; + int newScreenId = LauncherSettings.Settings.call(getContext().getContentResolver(), + LauncherSettings.Settings.METHOD_NEW_SCREEN_ID) + .getInt(LauncherSettings.Settings.EXTRA_VALUE); // Launcher database isn't aware of empty pages that are already bound, so we need to // skip those IDs manually. - while (newScreenId == -1 || mWorkspaceScreens.containsKey(newScreenId)) { - newScreenId = LauncherSettings.Settings.call(getContext().getContentResolver(), - LauncherSettings.Settings.METHOD_NEW_SCREEN_ID) - .getInt(LauncherSettings.Settings.EXTRA_VALUE); + while (mWorkspaceScreens.containsKey(newScreenId)) { + newScreenId++; } mWorkspaceScreens.put(newScreenId, cl); diff --git a/src/com/android/launcher3/model/AddWorkspaceItemsTask.java b/src/com/android/launcher3/model/AddWorkspaceItemsTask.java index fea15c41f8..a13fa55dff 100644 --- a/src/com/android/launcher3/model/AddWorkspaceItemsTask.java +++ b/src/com/android/launcher3/model/AddWorkspaceItemsTask.java @@ -16,7 +16,6 @@ package com.android.launcher3.model; import static com.android.launcher3.WorkspaceLayoutManager.FIRST_SCREEN_ID; -import static com.android.launcher3.WorkspaceLayoutManager.SECOND_SCREEN_ID; import android.content.Intent; import android.content.pm.LauncherActivityInfo; @@ -300,11 +299,6 @@ public class AddWorkspaceItemsTask extends BaseModelUpdateTask { IntSet screensToExclude = new IntSet(); if (FeatureFlags.QSB_ON_FIRST_SCREEN) { screensToExclude.add(FIRST_SCREEN_ID); - - // On split display we don't want to add the new items onto the second screen. - if (app.getInvariantDeviceProfile().isSplitDisplay) { - screensToExclude.add(SECOND_SCREEN_ID); - } } for (int screen = 0; screen < screenCount; screen++) { diff --git a/src/com/android/launcher3/model/ModelWriter.java b/src/com/android/launcher3/model/ModelWriter.java index 0439e75843..94e06d16ce 100644 --- a/src/com/android/launcher3/model/ModelWriter.java +++ b/src/com/android/launcher3/model/ModelWriter.java @@ -292,7 +292,7 @@ public class ModelWriter { FileLog.d(TAG, "removing items from db " + items.stream().map( (item) -> item.getTargetComponent() == null ? "" : item.getTargetComponent().getPackageName()).collect( - Collectors.joining(",")), new Exception()); + Collectors.joining(","))); notifyDelete(items); enqueueDeleteRunnable(() -> { for (ItemInfo item : items) { diff --git a/tests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.java b/tests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.java index 16f024e421..8a4590a388 100644 --- a/tests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.java +++ b/tests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.java @@ -86,8 +86,6 @@ public class AddWorkspaceItemsTaskTest { @Test public void testFindSpaceForItem_prefers_second() throws Exception { - mIdp.isSplitDisplay = false; - // First screen has only one hole of size 1 int nextId = setupWorkspaceWithHoles(1, 1, new Rect(2, 2, 3, 3)); @@ -108,24 +106,6 @@ public class AddWorkspaceItemsTaskTest { .isRegionVacant(spaceFound[1], spaceFound[2], 2, 3)); } - @Test - public void testFindSpaceForItem_prefers_third_on_split_display() throws Exception { - mIdp.isSplitDisplay = true; - // First screen has only one hole of size 1 - int nextId = setupWorkspaceWithHoles(1, 1, new Rect(2, 2, 3, 3)); - - // Second screen has 2 holes of sizes 3x2 and 2x3 - setupWorkspaceWithHoles(nextId, 2, new Rect(2, 0, 5, 2), new Rect(0, 2, 2, 5)); - - int[] spaceFound = newTask().findSpaceForItem( - mAppState, mModelHelper.getBgDataModel(), mExistingScreens, mNewScreens, 1, 1); - // For split display, it picks the next screen, even if there is enough space - // on previous screen - assertEquals(2, spaceFound[0]); - assertTrue(mScreenOccupancy.get(spaceFound[0]) - .isRegionVacant(spaceFound[1], spaceFound[2], 1, 1)); - } - @Test public void testFindSpaceForItem_adds_new_screen() throws Exception { // First screen has 2 holes of sizes 3x2 and 2x3 From ea6ee1c82461d7fe56d570d69f50d8d798adaa8d Mon Sep 17 00:00:00 2001 From: Alex Chau Date: Mon, 15 Nov 2021 15:48:54 +0000 Subject: [PATCH 5/9] Add ScreenRecord to methods affected by a flaky problem Bug: 204807156 Test: none Change-Id: I554c032b2ea43b8ee813f694d9c995f4ebc4a90f --- tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java | 2 ++ tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java | 2 ++ 2 files changed, 4 insertions(+) diff --git a/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java index 194ee4f1c9..36af8f0436 100644 --- a/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java +++ b/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java @@ -28,6 +28,7 @@ import com.android.launcher3.tapl.Widget; import com.android.launcher3.tapl.WidgetResizeFrame; import com.android.launcher3.ui.AbstractLauncherUiTest; import com.android.launcher3.ui.TestViewHelpers; +import com.android.launcher3.util.rule.ScreenRecordRule.ScreenRecord; import com.android.launcher3.util.rule.ShellCommandRule; import com.android.launcher3.widget.LauncherAppWidgetProviderInfo; @@ -47,6 +48,7 @@ public class AddWidgetTest extends AbstractLauncherUiTest { @Test @PortraitLandscape + @ScreenRecord // b/204807156 public void testDragIcon() throws Throwable { clearHomescreen(); mDevice.pressHome(); diff --git a/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java index fa39ce0604..758c426940 100644 --- a/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java +++ b/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java @@ -45,6 +45,7 @@ import com.android.launcher3.tapl.Widget; import com.android.launcher3.tapl.Workspace; import com.android.launcher3.ui.AbstractLauncherUiTest; import com.android.launcher3.ui.TestViewHelpers; +import com.android.launcher3.util.rule.ScreenRecordRule.ScreenRecord; import com.android.launcher3.util.rule.ShellCommandRule; import com.android.launcher3.widget.LauncherAppWidgetProviderInfo; import com.android.launcher3.widget.WidgetManagerHelper; @@ -138,6 +139,7 @@ public class BindWidgetTest extends AbstractLauncherUiTest { } @Test + @ScreenRecord // b/204807156 public void testPendingWidget_autoRestored() { // A non-restored widget with no config screen gets restored automatically. LauncherAppWidgetProviderInfo info = TestViewHelpers.findWidgetProvider(this, false); From 9d6fb64f454072ea4d7cb1325f380522dc154a51 Mon Sep 17 00:00:00 2001 From: Fedor Kudasov Date: Thu, 11 Nov 2021 16:55:14 +0000 Subject: [PATCH 6/9] Simplify getTaskViewAt call RecentsView getTaskViewAt method is nullable and is safe to call for an out of bound index. Bug: 205828770 Test: m LauncherGoResLib Change-Id: I7709d63ad4490fd756a50caaf42ba70c4fad4d06 (cherry picked from commit 41edede1eed666fe9ffdfcd0d379d65252302e1e) --- .../src/com/android/quickstep/OverviewCommandHelper.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/quickstep/src/com/android/quickstep/OverviewCommandHelper.java b/quickstep/src/com/android/quickstep/OverviewCommandHelper.java index 26d935d501..75e8dd1f7a 100644 --- a/quickstep/src/com/android/quickstep/OverviewCommandHelper.java +++ b/quickstep/src/com/android/quickstep/OverviewCommandHelper.java @@ -117,11 +117,12 @@ public class OverviewCommandHelper { mPendingCommands.clear(); } + @Nullable private TaskView getNextTask(RecentsView view) { final TaskView runningTaskView = view.getRunningTaskView(); if (runningTaskView == null) { - return view.getTaskViewCount() > 0 ? view.getTaskViewAt(0) : null; + return view.getTaskViewAt(0); } else { final TaskView nextTask = view.getNextTaskView(); return nextTask != null ? nextTask : runningTaskView; @@ -256,8 +257,8 @@ public class OverviewCommandHelper { // Ensure that recents view has focus so that it receives the followup key inputs TaskView taskView = rv.getNextTaskView(); if (taskView == null) { - if (rv.getTaskViewCount() > 0) { - taskView = rv.getTaskViewAt(0); + taskView = rv.getTaskViewAt(0); + if (taskView != null) { taskView.requestFocus(); } else { rv.requestFocus(); From 3a0f8a90bd569c454580f088960b8aaeff32adc6 Mon Sep 17 00:00:00 2001 From: Fedor Kudasov Date: Fri, 12 Nov 2021 17:11:40 +0000 Subject: [PATCH 7/9] Annotate RecentsView with @Nullable Bug: 205828770 Test: m LauncherGoResLib Change-Id: I44a4f7ce4258e54d43ca2c5a3937c81a4a686f80 (cherry picked from commit ac9cee5a7cc8b471f276307389c0bbccbbaa61c7) --- .../android/quickstep/views/RecentsView.java | 41 +++++++++++++++++-- 1 file changed, 37 insertions(+), 4 deletions(-) diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java index 223ce1c556..92f1a67aa6 100644 --- a/quickstep/src/com/android/quickstep/views/RecentsView.java +++ b/quickstep/src/com/android/quickstep/views/RecentsView.java @@ -383,7 +383,9 @@ public abstract class RecentsView mSizeStrategy; + @Nullable protected RecentsAnimationController mRecentsAnimationController; + @Nullable protected SurfaceTransactionApplier mSyncTransactionApplier; protected int mTaskWidth; protected int mTaskHeight; @@ -394,12 +396,15 @@ public abstract class RecentsView extends IPipAnimationListener.Stub { + @Nullable private T mActivity; + @Nullable private RecentsView mRecentsView; - public void setActivityAndRecentsView(T activity, RecentsView recentsView) { + public void setActivityAndRecentsView(@Nullable T activity, + @Nullable RecentsView recentsView) { mActivity = activity; mRecentsView = recentsView; } From 033f2afe38738b8ca4b56f0220f1cd10838729ef Mon Sep 17 00:00:00 2001 From: Alex Chau Date: Tue, 16 Nov 2021 11:19:09 +0000 Subject: [PATCH 8/9] Revert "Add ScreenRecord to methods affected by a flaky problem" This reverts commit ea6ee1c82461d7fe56d570d69f50d8d798adaa8d. Reason for revert: Seems to increase the severity of the flaky problem Bug: 204807156 Change-Id: I7b87f2abf45c9da21981f9361e320163bd7834bd --- tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java | 2 -- tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java | 2 -- 2 files changed, 4 deletions(-) diff --git a/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java index 36af8f0436..194ee4f1c9 100644 --- a/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java +++ b/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java @@ -28,7 +28,6 @@ import com.android.launcher3.tapl.Widget; import com.android.launcher3.tapl.WidgetResizeFrame; import com.android.launcher3.ui.AbstractLauncherUiTest; import com.android.launcher3.ui.TestViewHelpers; -import com.android.launcher3.util.rule.ScreenRecordRule.ScreenRecord; import com.android.launcher3.util.rule.ShellCommandRule; import com.android.launcher3.widget.LauncherAppWidgetProviderInfo; @@ -48,7 +47,6 @@ public class AddWidgetTest extends AbstractLauncherUiTest { @Test @PortraitLandscape - @ScreenRecord // b/204807156 public void testDragIcon() throws Throwable { clearHomescreen(); mDevice.pressHome(); diff --git a/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java index 758c426940..fa39ce0604 100644 --- a/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java +++ b/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java @@ -45,7 +45,6 @@ import com.android.launcher3.tapl.Widget; import com.android.launcher3.tapl.Workspace; import com.android.launcher3.ui.AbstractLauncherUiTest; import com.android.launcher3.ui.TestViewHelpers; -import com.android.launcher3.util.rule.ScreenRecordRule.ScreenRecord; import com.android.launcher3.util.rule.ShellCommandRule; import com.android.launcher3.widget.LauncherAppWidgetProviderInfo; import com.android.launcher3.widget.WidgetManagerHelper; @@ -139,7 +138,6 @@ public class BindWidgetTest extends AbstractLauncherUiTest { } @Test - @ScreenRecord // b/204807156 public void testPendingWidget_autoRestored() { // A non-restored widget with no config screen gets restored automatically. LauncherAppWidgetProviderInfo info = TestViewHelpers.findWidgetProvider(this, false); From 04309940c09f04dcf9b60348d6bc94cd85fbc0f7 Mon Sep 17 00:00:00 2001 From: Alex Chau Date: Tue, 16 Nov 2021 18:45:39 +0000 Subject: [PATCH 9/9] If hometask is last task to be dismissed, show empty recents instead of home screen - http://ag/16221737 ensure homescren stub is always added if tasks hasn't been loaded in RecentsView. This introduced an edge case that after dismissing home task view, no task remains in overview and it get dismissed - The fix is to show empty recents instead of home screen if last task dismissed is a home task (showing empty recents isn't always correct, as there might be unloaded tasks, but it's better than dismissing overview) - Also tuned the timing of when mLoadPlanEverApplied is set Fix: 206462357 Test: FallbackRecentsTest.goToOverviewFromHome Change-Id: I0f395639f57a1a57fddf750623a8a74b8ede8555 --- .../src/com/android/quickstep/views/RecentsView.java | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java index 92f1a67aa6..0188962da9 100644 --- a/quickstep/src/com/android/quickstep/views/RecentsView.java +++ b/quickstep/src/com/android/quickstep/views/RecentsView.java @@ -1335,6 +1335,7 @@ public abstract class RecentsView