From e63dd25a54e466dd250f38a424899a487304ba3d Mon Sep 17 00:00:00 2001 From: Pat Manning Date: Tue, 2 Aug 2022 16:15:29 +0100 Subject: [PATCH 1/6] Use full screen width for drop target buttons to support higher screen densities from truncating text. Scale oversized text down if after all computations it will still be truncated at higher densities. Fix: 239401464 Test: manual. To follow up with screenshot test: b/241386128 Change-Id: Ie088d0631b0d13beb2d9f9d5396a56f7b971dee1 --- res/values/dimens.xml | 1 + .../android/launcher3/ButtonDropTarget.java | 62 +++++++++++++++++-- src/com/android/launcher3/DeviceProfile.java | 2 +- src/com/android/launcher3/DropTargetBar.java | 31 ++++++---- 4 files changed, 78 insertions(+), 18 deletions(-) diff --git a/res/values/dimens.xml b/res/values/dimens.xml index a9d1127184..2e886dbd9d 100644 --- a/res/values/dimens.xml +++ b/res/values/dimens.xml @@ -226,6 +226,7 @@ 16sp 2dp 4dp + 20dp 8dp 16dp 8dp diff --git a/src/com/android/launcher3/ButtonDropTarget.java b/src/com/android/launcher3/ButtonDropTarget.java index 3b24df2f18..5abe3d3af6 100644 --- a/src/com/android/launcher3/ButtonDropTarget.java +++ b/src/com/android/launcher3/ButtonDropTarget.java @@ -91,7 +91,7 @@ public abstract class ButtonDropTarget extends TextView Resources resources = getResources(); mDragDistanceThreshold = resources.getDimensionPixelSize(R.dimen.drag_distanceThreshold); - mDrawableSize = resources.getDimensionPixelSize(R.dimen.drop_target_text_size); + mDrawableSize = resources.getDimensionPixelSize(R.dimen.drop_target_button_drawable_size); mDrawablePadding = resources.getDimensionPixelSize( R.dimen.drop_target_button_drawable_padding); } @@ -374,11 +374,63 @@ public abstract class ButtonDropTarget extends TextView hideTooltip(); } + /** + * Returns if the text will be truncated within the provided availableWidth. + */ public boolean isTextTruncated(int availableWidth) { - availableWidth -= (getPaddingLeft() + getPaddingRight() + mDrawable.getIntrinsicWidth() - + getCompoundDrawablePadding()); - CharSequence displayedText = TextUtils.ellipsize(mText, getPaint(), availableWidth, + availableWidth -= getPaddingLeft() + getPaddingRight(); + if (mIconVisible) { + availableWidth -= mDrawable.getIntrinsicWidth() + getCompoundDrawablePadding(); + } + if (availableWidth <= 0) { + return true; + } + CharSequence firstLine = TextUtils.ellipsize(mText, getPaint(), availableWidth, TextUtils.TruncateAt.END); - return !mText.equals(displayedText); + if (!mTextMultiLine) { + return !TextUtils.equals(mText, firstLine); + } + if (TextUtils.equals(mText, firstLine)) { + // When multi-line is active, if it can display as one line, then text is not truncated. + return false; + } + CharSequence secondLine = + TextUtils.ellipsize(mText.subSequence(firstLine.length(), mText.length()), + getPaint(), availableWidth, TextUtils.TruncateAt.END); + return !(TextUtils.equals(mText.subSequence(0, firstLine.length()), firstLine) + && TextUtils.equals(mText.subSequence(firstLine.length(), secondLine.length()), + secondLine)); + } + + /** + * Reduce the size of the text until it fits the measured width or reaches a minimum. + * + * The minimum size is defined by {@code R.dimen.button_drop_target_min_text_size} and + * it diminishes by intervals defined by + * {@code R.dimen.button_drop_target_resize_text_increment} + * This functionality is very similar to the option + * {@link TextView#setAutoSizeTextTypeWithDefaults(int)} but can't be used in this view because + * the layout width is {@code WRAP_CONTENT}. + * + * @return The biggest text size in SP that makes the text fit or if the text can't fit returns + * the min available value + */ + public float resizeTextToFit() { + float minSize = Utilities.pxToSp(getResources() + .getDimensionPixelSize(R.dimen.button_drop_target_min_text_size)); + float step = Utilities.pxToSp(getResources() + .getDimensionPixelSize(R.dimen.button_drop_target_resize_text_increment)); + float textSize = Utilities.pxToSp(getTextSize()); + + int availableWidth = getMeasuredWidth(); + while (textSize > minSize) { + if (isTextTruncated(availableWidth)) { + textSize -= step; + setTextSize(textSize); + } else { + return textSize; + } + } + return minSize; } } diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java index 14a467ad65..55a8d370dd 100644 --- a/src/com/android/launcher3/DeviceProfile.java +++ b/src/com/android/launcher3/DeviceProfile.java @@ -1030,7 +1030,7 @@ public class DeviceProfile { / getCellLayoutHeight(); scale = Math.min(scale, 1f); - // Reduce scale if next pages would not be visible after scaling the workspace + // Reduce scale if next pages would not be visible after scaling the workspace. int workspaceWidth = availableWidthPx; float scaledWorkspaceWidth = workspaceWidth * scale; float maxAvailableWidth = workspaceWidth - (2 * workspaceSpringLoadedMinNextPageVisiblePx); diff --git a/src/com/android/launcher3/DropTargetBar.java b/src/com/android/launcher3/DropTargetBar.java index d64cb26ff4..98ecf3a7e6 100644 --- a/src/com/android/launcher3/DropTargetBar.java +++ b/src/com/android/launcher3/DropTargetBar.java @@ -151,6 +151,8 @@ public class DropTargetBar extends FrameLayout int widthSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.AT_MOST); ButtonDropTarget firstButton = mTempTargets[0]; + firstButton.setTextSize(TypedValue.COMPLEX_UNIT_PX, + mLauncher.getDeviceProfile().dropTargetTextSizePx); firstButton.setTextVisible(true); firstButton.setIconVisible(true); firstButton.measure(widthSpec, heightSpec); @@ -160,14 +162,16 @@ public class DropTargetBar extends FrameLayout int horizontalPadding = dp.dropTargetHorizontalPaddingPx; ButtonDropTarget firstButton = mTempTargets[0]; + firstButton.setTextSize(TypedValue.COMPLEX_UNIT_PX, dp.dropTargetTextSizePx); firstButton.setTextVisible(true); firstButton.setIconVisible(true); firstButton.setTextMultiLine(false); - // Reset second button padding in case it was previously changed to multi-line text. + // Reset first button padding in case it was previously changed to multi-line text. firstButton.setPadding(horizontalPadding, verticalPadding, horizontalPadding, verticalPadding); ButtonDropTarget secondButton = mTempTargets[1]; + secondButton.setTextSize(TypedValue.COMPLEX_UNIT_PX, dp.dropTargetTextSizePx); secondButton.setTextVisible(true); secondButton.setIconVisible(true); secondButton.setTextMultiLine(false); @@ -175,20 +179,14 @@ public class DropTargetBar extends FrameLayout secondButton.setPadding(horizontalPadding, verticalPadding, horizontalPadding, verticalPadding); - float scale = dp.getWorkspaceSpringLoadScale(mLauncher); - int scaledPanelWidth = (int) (dp.getCellLayoutWidth() * scale); - int availableWidth; if (dp.isTwoPanels) { - // Both buttons for two panel fit to the width of one Cell Layout (less - // half of the center gap between the buttons). - int halfButtonGap = dp.dropTargetGapPx / 2; - availableWidth = scaledPanelWidth - halfButtonGap / 2; + // Each button for two panel fits to half the width of the screen excluding the + // center gap between the buttons. + availableWidth = (dp.availableWidthPx - dp.dropTargetGapPx) / 2; } else { - // Both buttons plus the button gap do not display past the edge of the scaled - // workspace, less a pre-defined gap from the edge of the workspace. - availableWidth = scaledPanelWidth - dp.dropTargetGapPx - - 2 * dp.dropTargetButtonWorkspaceEdgeGapPx; + // Both buttons plus the button gap do not display past the edge of the screen. + availableWidth = dp.availableWidthPx - dp.dropTargetGapPx; } int widthSpec = MeasureSpec.makeMeasureSpec(availableWidth, MeasureSpec.AT_MOST); @@ -219,6 +217,15 @@ public class DropTargetBar extends FrameLayout horizontalPadding, verticalPadding / 2); } } + + // If text is still truncated, shrink to fit in measured width and resize both targets. + float minTextSize = + Math.min(firstButton.resizeTextToFit(), secondButton.resizeTextToFit()); + if (firstButton.getTextSize() != minTextSize + || secondButton.getTextSize() != minTextSize) { + firstButton.setTextSize(minTextSize); + secondButton.setTextSize(minTextSize); + } } setMeasuredDimension(width, height); } From 82da9fc5be5813d85e0b5ebecb94b8318877f74d Mon Sep 17 00:00:00 2001 From: Sukesh Ram Date: Fri, 15 Jul 2022 17:24:24 -0700 Subject: [PATCH 2/6] Made getOnBoardingPrefs @Nullable & avoid NPE Bug: 236679197 Test: Manual Change-Id: I3cbb0891cbfcf7cab956a4e05a28dbbcc2b33e19 (cherry picked from commit c0650e77257d464e7474b288bbf52e728b5cf87a) --- .../launcher3/appprediction/AppsDividerView.java | 11 +++++++---- src/com/android/launcher3/views/ActivityContext.java | 1 + 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/quickstep/src/com/android/launcher3/appprediction/AppsDividerView.java b/quickstep/src/com/android/launcher3/appprediction/AppsDividerView.java index f42b39fb2d..e8374b813c 100644 --- a/quickstep/src/com/android/launcher3/appprediction/AppsDividerView.java +++ b/quickstep/src/com/android/launcher3/appprediction/AppsDividerView.java @@ -35,6 +35,7 @@ import androidx.core.content.ContextCompat; import com.android.launcher3.R; import com.android.launcher3.allapps.FloatingHeaderRow; import com.android.launcher3.allapps.FloatingHeaderView; +import com.android.launcher3.util.OnboardingPrefs; import com.android.launcher3.util.Themes; import com.android.launcher3.views.ActivityContext; @@ -92,8 +93,10 @@ public class AppsDividerView extends View implements FloatingHeaderRow { ? R.color.all_apps_label_text_dark : R.color.all_apps_label_text); - mShowAllAppsLabel = !ActivityContext.lookupContext( - getContext()).getOnboardingPrefs().hasReachedMaxCount(ALL_APPS_VISITED_COUNT); + OnboardingPrefs onboardingPrefs = ActivityContext.lookupContext( + getContext()).getOnboardingPrefs(); + mShowAllAppsLabel = onboardingPrefs == null || !onboardingPrefs.hasReachedMaxCount( + ALL_APPS_VISITED_COUNT); } public void setup(FloatingHeaderView parent, FloatingHeaderRow[] rows, boolean tabsHidden) { @@ -216,8 +219,8 @@ public class AppsDividerView extends View implements FloatingHeaderRow { CharSequence allAppsLabelText = getResources().getText(R.string.all_apps_label); mAllAppsLabelLayout = StaticLayout.Builder.obtain( - allAppsLabelText, 0, allAppsLabelText.length(), mPaint, - Math.round(mPaint.measureText(allAppsLabelText.toString()))) + allAppsLabelText, 0, allAppsLabelText.length(), mPaint, + Math.round(mPaint.measureText(allAppsLabelText.toString()))) .setAlignment(Layout.Alignment.ALIGN_CENTER) .setMaxLines(1) .setIncludePad(true) diff --git a/src/com/android/launcher3/views/ActivityContext.java b/src/com/android/launcher3/views/ActivityContext.java index 93078e4cf6..e2dc34fb67 100644 --- a/src/com/android/launcher3/views/ActivityContext.java +++ b/src/com/android/launcher3/views/ActivityContext.java @@ -141,6 +141,7 @@ public interface ActivityContext { default void applyOverwritesToLogItem(LauncherAtom.ItemInfo.Builder itemInfoBuilder) { } /** Onboarding preferences for any onboarding data within this context. */ + @Nullable default OnboardingPrefs getOnboardingPrefs() { return null; } From 8d3d944d5dc188157e7d762552918a56fb34eada Mon Sep 17 00:00:00 2001 From: Sukesh Ram Date: Thu, 14 Jul 2022 16:02:25 -0700 Subject: [PATCH 3/6] Fix SecondaryDisplayLauncher crash by initializing OnBoardingPrefs after initializing UI Bug: 238325716 Test: Manual Change-Id: I66a85eb79cda89a63c3116a56a52b51526720158 Merged-In: I66a85eb79cda89a63c3116a56a52b51526720158 --- .../launcher3/secondarydisplay/SecondaryDisplayLauncher.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java b/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java index 33b2f55c12..a55f7e3b69 100644 --- a/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java +++ b/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java @@ -73,11 +73,11 @@ public class SecondaryDisplayLauncher extends BaseDraggingActivity protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mModel = LauncherAppState.getInstance(this).getModel(); + mOnboardingPrefs = new OnboardingPrefs<>(this, Utilities.getPrefs(this)); + mSecondaryDisplayPredictions = SecondaryDisplayPredictions.newInstance(this); if (getWindow().getDecorView().isAttachedToWindow()) { initUi(); } - mOnboardingPrefs = new OnboardingPrefs<>(this, Utilities.getPrefs(this)); - mSecondaryDisplayPredictions = SecondaryDisplayPredictions.newInstance(this); } @Override From 7ad70b6066bf2e74b4b418091bddd03b0a883089 Mon Sep 17 00:00:00 2001 From: Schneider Victor-tulias Date: Wed, 31 Aug 2022 15:38:32 -0700 Subject: [PATCH 4/6] Expand on gesture navigation error detection. - Added some missing error detection: 1. screenshot capture errors 2. recents scrolling errors 3. end-of-gesture callbacks - Added some more explanation for OverviewInputConsumer selection reason - Added logging the current task's package name to help identify gestures Bug: 227514916 Bug: 243471493 Test: Ran launcher, performed multiple gestures and checked logs Change-Id: I8b10cc75f8640a674c6fed6b06efa4763c9635a2 --- .../android/quickstep/AbsSwipeUpHandler.java | 11 +++ .../com/android/quickstep/GestureState.java | 2 + .../quickstep/RecentsAnimationController.java | 9 +- .../quickstep/TaskAnimationManager.java | 4 + .../quickstep/TouchInteractionService.java | 33 +++++-- .../ProgressDelegateInputConsumer.java | 14 ++- .../util/ActiveGestureErrorDetector.java | 89 ++++++++++++++++++- .../quickstep/util/ActiveGestureLog.java | 12 ++- 8 files changed, 153 insertions(+), 21 deletions(-) diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java index 6e616f388b..5a59f8589d 100644 --- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java +++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java @@ -333,6 +333,12 @@ public abstract class AbsSwipeUpHandler, return ActiveGestureErrorDetector.GestureEvent.STATE_GESTURE_COMPLETED; } else if (stateFlag == STATE_GESTURE_CANCELLED) { return ActiveGestureErrorDetector.GestureEvent.STATE_GESTURE_CANCELLED; + } else if (stateFlag == STATE_SCREENSHOT_CAPTURED) { + return ActiveGestureErrorDetector.GestureEvent.STATE_SCREENSHOT_CAPTURED; + } else if (stateFlag == STATE_CAPTURE_SCREENSHOT) { + return ActiveGestureErrorDetector.GestureEvent.STATE_CAPTURE_SCREENSHOT; + } else if (stateFlag == STATE_HANDLER_INVALIDATED) { + return ActiveGestureErrorDetector.GestureEvent.STATE_HANDLER_INVALIDATED; } return null; } @@ -1221,6 +1227,8 @@ public abstract class AbsSwipeUpHandler, // Let RecentsView handle the scrolling to the task, which we launch in startNewTask() // or resumeLastTask(). if (mRecentsView != null) { + ActiveGestureLog.INSTANCE.trackEvent(ActiveGestureErrorDetector.GestureEvent + .SET_ON_PAGE_TRANSITION_END_CALLBACK); mRecentsView.setOnPageTransitionEndCallback( () -> mGestureState.setState(STATE_RECENTS_SCROLLING_FINISHED)); } else { @@ -1668,6 +1676,9 @@ public abstract class AbsSwipeUpHandler, * handler (in case of quick switch). */ private void cancelCurrentAnimation() { + ActiveGestureLog.INSTANCE.addLog( + "AbsSwipeUpHandler.cancelCurrentAnimation", + ActiveGestureErrorDetector.GestureEvent.CANCEL_CURRENT_ANIMATION); mCanceled = true; mCurrentShift.cancelAnimation(); diff --git a/quickstep/src/com/android/quickstep/GestureState.java b/quickstep/src/com/android/quickstep/GestureState.java index bc2f55137b..38bf1fd6d8 100644 --- a/quickstep/src/com/android/quickstep/GestureState.java +++ b/quickstep/src/com/android/quickstep/GestureState.java @@ -189,6 +189,8 @@ public class GestureState implements RecentsAnimationCallbacks.RecentsAnimationL return ActiveGestureErrorDetector.GestureEvent.STATE_END_TARGET_ANIMATION_FINISHED; } else if (stateFlag == STATE_RECENTS_SCROLLING_FINISHED) { return ActiveGestureErrorDetector.GestureEvent.STATE_RECENTS_SCROLLING_FINISHED; + } else if (stateFlag == STATE_RECENTS_ANIMATION_CANCELED) { + return ActiveGestureErrorDetector.GestureEvent.STATE_RECENTS_ANIMATION_CANCELED; } return null; } diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationController.java b/quickstep/src/com/android/quickstep/RecentsAnimationController.java index fefef2f45e..542c0d4b21 100644 --- a/quickstep/src/com/android/quickstep/RecentsAnimationController.java +++ b/quickstep/src/com/android/quickstep/RecentsAnimationController.java @@ -32,6 +32,8 @@ import androidx.annotation.UiThread; import com.android.launcher3.util.Preconditions; import com.android.launcher3.util.RunnableList; +import com.android.quickstep.util.ActiveGestureErrorDetector; +import com.android.quickstep.util.ActiveGestureLog; import com.android.systemui.shared.recents.model.ThumbnailData; import com.android.systemui.shared.system.InteractionJankMonitorWrapper; import com.android.systemui.shared.system.RecentsAnimationControllerCompat; @@ -172,7 +174,12 @@ public class RecentsAnimationController { */ @UiThread public void cleanupScreenshot() { - UI_HELPER_EXECUTOR.execute(() -> mController.cleanupScreenshot()); + UI_HELPER_EXECUTOR.execute(() -> { + ActiveGestureLog.INSTANCE.addLog( + "cleanupScreenshot", + ActiveGestureErrorDetector.GestureEvent.CLEANUP_SCREENSHOT); + mController.cleanupScreenshot(); + }); } /** diff --git a/quickstep/src/com/android/quickstep/TaskAnimationManager.java b/quickstep/src/com/android/quickstep/TaskAnimationManager.java index 03147619e5..5fb806dacf 100644 --- a/quickstep/src/com/android/quickstep/TaskAnimationManager.java +++ b/quickstep/src/com/android/quickstep/TaskAnimationManager.java @@ -36,6 +36,8 @@ import androidx.annotation.UiThread; import com.android.launcher3.Utilities; import com.android.launcher3.config.FeatureFlags; import com.android.quickstep.TopTaskTracker.CachedTaskInfo; +import com.android.quickstep.util.ActiveGestureErrorDetector; +import com.android.quickstep.util.ActiveGestureLog; import com.android.quickstep.views.RecentsView; import com.android.systemui.shared.recents.model.ThumbnailData; import com.android.systemui.shared.system.ActivityManagerWrapper; @@ -136,6 +138,8 @@ public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAn // handling this call entirely return; } + ActiveGestureLog.INSTANCE.addLog("TaskAnimationManager.startRecentsAnimation", + ActiveGestureErrorDetector.GestureEvent.START_RECENTS_ANIMATION); mController = controller; mTargets = targets; mLastAppearedTaskTarget = mTargets.findTask(mLastGestureState.getRunningTaskId()); diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java index 82be3ec811..ba4f549bbb 100644 --- a/quickstep/src/com/android/quickstep/TouchInteractionService.java +++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java @@ -721,8 +721,10 @@ public class TouchInteractionService extends Service gestureState.updateRunningTask(taskInfo); } // Log initial state for the gesture. - ActiveGestureLog.INSTANCE.addLog( - "Current SystemUi state flags= " + mDeviceState.getSystemUiStateString()); + ActiveGestureLog.INSTANCE.addLog(new CompoundString("Current running task package name=") + .append(taskInfo == null ? "no running task" : taskInfo.getPackageName())); + ActiveGestureLog.INSTANCE.addLog(new CompoundString("Current SystemUi state flags=") + .append(mDeviceState.getSystemUiStateString())); return gestureState; } @@ -1024,12 +1026,27 @@ public class TouchInteractionService extends Service .append("activity == null, trying to use default input consumer")); } - if (activity.getRootView().hasWindowFocus() - || previousGestureState.isRunningAnimationToLauncher() - || (ASSISTANT_GIVES_LAUNCHER_FOCUS.get() - && forceOverviewInputConsumer) - || (ENABLE_QUICKSTEP_LIVE_TILE.get() - && gestureState.getActivityInterface().isInLiveTileMode())) { + boolean hasWindowFocus = activity.getRootView().hasWindowFocus(); + boolean isPreviousGestureAnimatingToLauncher = + previousGestureState.isRunningAnimationToLauncher(); + boolean forcingOverviewInputConsumer = + ASSISTANT_GIVES_LAUNCHER_FOCUS.get() && forceOverviewInputConsumer; + boolean isInLiveTileMode = ENABLE_QUICKSTEP_LIVE_TILE.get() + && gestureState.getActivityInterface().isInLiveTileMode(); + reasonString.append(SUBSTRING_PREFIX) + .append(hasWindowFocus + ? "activity has window focus" + : (isPreviousGestureAnimatingToLauncher + ? "previous gesture is still animating to launcher" + : (forcingOverviewInputConsumer + ? "assistant gives launcher focus and forcing focus" + : (isInLiveTileMode + ? "device is in live mode" + : "all overview focus conditions failed")))); + if (hasWindowFocus + || isPreviousGestureAnimatingToLauncher + || forcingOverviewInputConsumer + || isInLiveTileMode) { reasonString.append(SUBSTRING_PREFIX) .append("overview should have focus, using OverviewInputConsumer"); return new OverviewInputConsumer(gestureState, activity, mInputMonitorCompat, diff --git a/quickstep/src/com/android/quickstep/inputconsumers/ProgressDelegateInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/ProgressDelegateInputConsumer.java index 246239449f..b9b5e7c2b9 100644 --- a/quickstep/src/com/android/quickstep/inputconsumers/ProgressDelegateInputConsumer.java +++ b/quickstep/src/com/android/quickstep/inputconsumers/ProgressDelegateInputConsumer.java @@ -28,6 +28,8 @@ import android.content.Intent; import android.graphics.Point; import android.view.MotionEvent; +import androidx.annotation.Nullable; + import com.android.launcher3.anim.AnimatorListeners; import com.android.launcher3.testing.TestLogging; import com.android.launcher3.testing.shared.TestProtocol; @@ -41,6 +43,7 @@ import com.android.quickstep.RecentsAnimationCallbacks; import com.android.quickstep.RecentsAnimationController; import com.android.quickstep.RecentsAnimationTargets; import com.android.quickstep.TaskAnimationManager; +import com.android.quickstep.util.ActiveGestureErrorDetector; import com.android.systemui.shared.recents.model.ThumbnailData; import com.android.systemui.shared.system.InputMonitorCompat; @@ -99,7 +102,8 @@ public class ProgressDelegateInputConsumer implements InputConsumer, mDisplaySize = DisplayController.INSTANCE.get(context).getInfo().currentSize; // Init states - mStateCallback = new MultiStateCallback(STATE_NAMES); + mStateCallback = new MultiStateCallback( + STATE_NAMES, ProgressDelegateInputConsumer::getTrackedEventForState); mStateCallback.runOnceAtState(STATE_TARGET_RECEIVED | STATE_HANDLER_INVALIDATED, this::endRemoteAnimation); mStateCallback.runOnceAtState(STATE_TARGET_RECEIVED | STATE_FLING_FINISHED, @@ -109,6 +113,14 @@ public class ProgressDelegateInputConsumer implements InputConsumer, mSwipeDetector.setDetectableScrollConditions(DIRECTION_POSITIVE, false); } + @Nullable + private static ActiveGestureErrorDetector.GestureEvent getTrackedEventForState(int stateFlag) { + if (stateFlag == STATE_HANDLER_INVALIDATED) { + return ActiveGestureErrorDetector.GestureEvent.STATE_HANDLER_INVALIDATED; + } + return null; + } + @Override public int getType() { return TYPE_PROGRESS_DELEGATE; diff --git a/quickstep/src/com/android/quickstep/util/ActiveGestureErrorDetector.java b/quickstep/src/com/android/quickstep/util/ActiveGestureErrorDetector.java index 78075def8f..54f632afdb 100644 --- a/quickstep/src/com/android/quickstep/util/ActiveGestureErrorDetector.java +++ b/quickstep/src/com/android/quickstep/util/ActiveGestureErrorDetector.java @@ -29,11 +29,24 @@ import java.util.Set; */ public class ActiveGestureErrorDetector { + /** + * Enums associated to gesture navigation events. + */ public enum GestureEvent { MOTION_DOWN, MOTION_UP, SET_END_TARGET, ON_SETTLED_ON_END_TARGET, START_RECENTS_ANIMATION, - FINISH_RECENTS_ANIMATION, CANCEL_RECENTS_ANIMATION, STATE_GESTURE_STARTED, - STATE_GESTURE_COMPLETED, STATE_GESTURE_CANCELLED, STATE_END_TARGET_ANIMATION_FINISHED, - STATE_RECENTS_SCROLLING_FINISHED + FINISH_RECENTS_ANIMATION, CANCEL_RECENTS_ANIMATION, SET_ON_PAGE_TRANSITION_END_CALLBACK, + CANCEL_CURRENT_ANIMATION, CLEANUP_SCREENSHOT, + + /** + * These GestureEvents are specifically associated to state flags that get set in + * {@link com.android.quickstep.MultiStateCallback}. If a state flag needs to be tracked + * for error detection, an enum should be added here and that state flag-enum pair should + * be added to the state flag's container class' {@code getTrackedEventForState} method. + */ + STATE_GESTURE_STARTED, STATE_GESTURE_COMPLETED, STATE_GESTURE_CANCELLED, + STATE_END_TARGET_ANIMATION_FINISHED, STATE_RECENTS_SCROLLING_FINISHED, + STATE_CAPTURE_SCREENSHOT, STATE_SCREENSHOT_CAPTURED, STATE_HANDLER_INVALIDATED, + STATE_RECENTS_ANIMATION_CANCELED } private ActiveGestureErrorDetector() {} @@ -90,6 +103,14 @@ public class ActiveGestureErrorDetector { + "before/without startRecentsAnimation.", writer); break; + case CLEANUP_SCREENSHOT: + errorDetected |= printErrorIfTrue( + !encounteredEvents.contains(GestureEvent.STATE_SCREENSHOT_CAPTURED), + /* errorMessage= */ prefix + "\t\trecents activity screenshot was " + + "cleaned up before/without STATE_SCREENSHOT_CAPTURED " + + "being set.", + writer); + break; case STATE_GESTURE_COMPLETED: errorDetected |= printErrorIfTrue( !encounteredEvents.contains(GestureEvent.MOTION_UP), @@ -114,12 +135,39 @@ public class ActiveGestureErrorDetector { + "before/without STATE_GESTURE_STARTED.", writer); break; + case STATE_SCREENSHOT_CAPTURED: + errorDetected |= printErrorIfTrue( + !encounteredEvents.contains(GestureEvent.STATE_CAPTURE_SCREENSHOT), + /* errorMessage= */ prefix + "\t\tSTATE_SCREENSHOT_CAPTURED set " + + "before/without STATE_CAPTURE_SCREENSHOT.", + writer); + break; + case STATE_RECENTS_SCROLLING_FINISHED: + errorDetected |= printErrorIfTrue( + !encounteredEvents.contains( + GestureEvent.SET_ON_PAGE_TRANSITION_END_CALLBACK), + /* errorMessage= */ prefix + "\t\tSTATE_RECENTS_SCROLLING_FINISHED " + + "set before/without calling " + + "setOnPageTransitionEndCallback.", + writer); + break; + case STATE_RECENTS_ANIMATION_CANCELED: + errorDetected |= printErrorIfTrue( + !encounteredEvents.contains( + GestureEvent.START_RECENTS_ANIMATION), + /* errorMessage= */ prefix + "\t\tSTATE_RECENTS_ANIMATION_CANCELED " + + "set before/without startRecentsAnimation.", + writer); + break; case MOTION_DOWN: case SET_END_TARGET: case START_RECENTS_ANIMATION: + case SET_ON_PAGE_TRANSITION_END_CALLBACK: + case CANCEL_CURRENT_ANIMATION: case STATE_GESTURE_STARTED: case STATE_END_TARGET_ANIMATION_FINISHED: - case STATE_RECENTS_SCROLLING_FINISHED: + case STATE_CAPTURE_SCREENSHOT: + case STATE_HANDLER_INVALIDATED: default: // No-Op } @@ -183,6 +231,39 @@ public class ActiveGestureErrorDetector { + "STATE_GESTURE_COMPLETED and STATE_GESTURE_CANCELLED weren't.", writer); + errorDetected |= printErrorIfTrue( + /* condition= */ encounteredEvents.contains( + GestureEvent.STATE_CAPTURE_SCREENSHOT) + && !encounteredEvents.contains(GestureEvent.STATE_SCREENSHOT_CAPTURED), + /* errorMessage= */ prefix + "\t\tSTATE_CAPTURE_SCREENSHOT was set, but " + + "STATE_SCREENSHOT_CAPTURED wasn't.", + writer); + + errorDetected |= printErrorIfTrue( + /* condition= */ encounteredEvents.contains( + GestureEvent.SET_ON_PAGE_TRANSITION_END_CALLBACK) + && !encounteredEvents.contains( + GestureEvent.STATE_RECENTS_SCROLLING_FINISHED), + /* errorMessage= */ prefix + "\t\tsetOnPageTransitionEndCallback called, but " + + "STATE_RECENTS_SCROLLING_FINISHED wasn't set.", + writer); + + errorDetected |= printErrorIfTrue( + /* condition= */ !encounteredEvents.contains( + GestureEvent.CANCEL_CURRENT_ANIMATION) + && !encounteredEvents.contains(GestureEvent.STATE_HANDLER_INVALIDATED), + /* errorMessage= */ prefix + "\t\tAbsSwipeUpHandler.cancelCurrentAnimation " + + "wasn't called and STATE_HANDLER_INVALIDATED wasn't set.", + writer); + + errorDetected |= printErrorIfTrue( + /* condition= */ encounteredEvents.contains( + GestureEvent.STATE_RECENTS_ANIMATION_CANCELED) + && !encounteredEvents.contains(GestureEvent.CLEANUP_SCREENSHOT), + /* errorMessage= */ prefix + "\t\tSTATE_RECENTS_ANIMATION_CANCELED was set but " + + "the task screenshot wasn't cleaned up.", + writer); + if (!errorDetected) { writer.println(prefix + "\t\tNo errors detected."); } diff --git a/quickstep/src/com/android/quickstep/util/ActiveGestureLog.java b/quickstep/src/com/android/quickstep/util/ActiveGestureLog.java index 9f08010b2b..40eb31b727 100644 --- a/quickstep/src/com/android/quickstep/util/ActiveGestureLog.java +++ b/quickstep/src/com/android/quickstep/util/ActiveGestureLog.java @@ -138,14 +138,10 @@ public class ActiveGestureLog { List lastEventEntries = lastEventLog.eventEntries; EventEntry lastEntry = lastEventEntries.size() > 0 ? lastEventEntries.get(lastEventEntries.size() - 1) : null; - EventEntry secondLastEntry = lastEventEntries.size() > 1 - ? lastEventEntries.get(lastEventEntries.size() - 2) : null; // Update the last EventEntry if it's a duplicate - if (isEntrySame(lastEntry, type, event, compoundString, gestureEvent) - && isEntrySame(secondLastEntry, type, event, compoundString, gestureEvent)) { - lastEntry.update(type, event, extras, compoundString, gestureEvent); - secondLastEntry.duplicateCount++; + if (isEntrySame(lastEntry, type, event, extras, compoundString, gestureEvent)) { + lastEntry.duplicateCount++; return; } EventEntry eventEntry = new EventEntry(); @@ -223,11 +219,13 @@ public class ActiveGestureLog { EventEntry entry, int type, String event, + float extras, CompoundString compoundString, ActiveGestureErrorDetector.GestureEvent gestureEvent) { return entry != null && entry.type == type && entry.event.equals(event) + && Float.compare(entry.extras, extras) == 0 && entry.mCompoundString.equals(compoundString) && entry.gestureEvent == gestureEvent; } @@ -342,7 +340,7 @@ public class ActiveGestureLog { return false; } CompoundString other = (CompoundString) obj; - return mIsNoOp && other.mIsNoOp && Objects.equals(mSubstrings, other.mSubstrings); + return (mIsNoOp == other.mIsNoOp) && Objects.equals(mSubstrings, other.mSubstrings); } } } From 26db3bb62a66e3a999aa58cb15e1837b9fcb9fec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Ciche=C5=84ski?= Date: Sat, 3 Sep 2022 04:30:36 +0000 Subject: [PATCH 5/6] The shelf height was mistakenly provided as width, making it equal to 0. Bug: 244797561 Test: manually Change-Id: I28e9c34bb0feb643c5d7b652c055d5fdceaec878 --- quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java index 1f7b7de0e1..eff6fed030 100644 --- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java +++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java @@ -1535,7 +1535,7 @@ public abstract class AbsSwipeUpHandler, Rect keepClearArea; if (!ENABLE_PIP_KEEP_CLEAR_ALGORITHM) { // make the height equal to hotseatBarSizePx only - keepClearArea = new Rect(0, 0, mDp.hotseatBarSizePx, 0); + keepClearArea = new Rect(0, 0, 0, mDp.hotseatBarSizePx); return keepClearArea; } // the keep clear area in global screen coordinates, in pixels From 4a206837c93349898be4aa9c715b1af3ed0e0435 Mon Sep 17 00:00:00 2001 From: Alex Chau Date: Fri, 19 Aug 2022 15:34:30 -0400 Subject: [PATCH 6/6] Enable adjacent task animation for grid - Enable adjacent task animation for grid tasks - Only parallax for focused task and only when it's fully visible Bug: 236963497 Test: manual Change-Id: I6c681e112f3eb2c7075bc98fab405d978f5057d1 --- .../com/android/quickstep/TaskViewUtils.java | 10 ++-------- .../android/quickstep/views/RecentsView.java | 20 ++++++++++--------- 2 files changed, 13 insertions(+), 17 deletions(-) diff --git a/quickstep/src/com/android/quickstep/TaskViewUtils.java b/quickstep/src/com/android/quickstep/TaskViewUtils.java index 93170cb79b..556b99ec16 100644 --- a/quickstep/src/com/android/quickstep/TaskViewUtils.java +++ b/quickstep/src/com/android/quickstep/TaskViewUtils.java @@ -43,7 +43,6 @@ import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MOD import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; -import android.animation.ObjectAnimator; import android.animation.ValueAnimator; import android.annotation.TargetApi; import android.app.PendingIntent; @@ -198,8 +197,7 @@ public final class TaskViewUtils { BaseActivity baseActivity = BaseActivity.fromContext(context); DeviceProfile dp = baseActivity.getDeviceProfile(); boolean showAsGrid = dp.isTablet; - boolean parallaxCenterAndAdjacentTask = - taskIndex != recentsView.getCurrentPage() && !showAsGrid; + boolean parallaxCenterAndAdjacentTask = taskIndex != recentsView.getCurrentPage(); int taskRectTranslationPrimary = recentsView.getScrollOffset(taskIndex); int taskRectTranslationSecondary = showAsGrid ? (int) v.getGridTranslationY() : 0; @@ -603,11 +601,7 @@ public final class TaskViewUtils { if (raController != null) { raController.setWillFinishToHome(false); } - Context context = v.getContext(); - DeviceProfile dp = BaseActivity.fromContext(context).getDeviceProfile(); - launcherAnim = dp.isTablet - ? ObjectAnimator.ofFloat(recentsView, RecentsView.CONTENT_ALPHA, 0) - : recentsView.createAdjacentPageAnimForTaskLaunch(taskView); + launcherAnim = recentsView.createAdjacentPageAnimForTaskLaunch(taskView); launcherAnim.setInterpolator(Interpolators.TOUCH_RESPONSE_INTERPOLATOR); launcherAnim.setDuration(RECENTS_LAUNCH_DURATION); diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java index efaa644502..f00e858b04 100644 --- a/quickstep/src/com/android/quickstep/views/RecentsView.java +++ b/quickstep/src/com/android/quickstep/views/RecentsView.java @@ -4317,25 +4317,27 @@ public abstract class RecentsView