diff options
author | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2023-12-13 00:23:08 +0000 |
---|---|---|
committer | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2023-12-13 00:23:08 +0000 |
commit | 6fdc2fda19b1398b4affbd0c59701f77cfe190f9 (patch) | |
tree | 4634dcdf8970cec4554e42aa0eb08b9a62bdce92 | |
parent | 24ddc4ec44ebdd8821cfa874f020b076d8bb1d2f (diff) | |
parent | d48279bf1dfb4a6866cbdabc12a84703c438796d (diff) | |
download | Launcher3-6fdc2fda19b1398b4affbd0c59701f77cfe190f9.tar.gz |
Snap for 11211173 from d48279bf1dfb4a6866cbdabc12a84703c438796d to 24Q1-release
Change-Id: Ib0f855c521d9356b3b003c4945bfd1ef71ebcd59
23 files changed, 501 insertions, 197 deletions
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java index fc04d6c6b7..38ee4ac9ba 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java @@ -74,6 +74,7 @@ import androidx.annotation.VisibleForTesting; import com.android.launcher3.AbstractFloatingView; import com.android.launcher3.BubbleTextView; import com.android.launcher3.DeviceProfile; +import com.android.launcher3.LauncherPrefs; import com.android.launcher3.LauncherSettings.Favorites; import com.android.launcher3.R; import com.android.launcher3.anim.AnimatorPlaybackController; @@ -183,6 +184,8 @@ public class TaskbarActivityContext extends BaseTaskbarContext { private DeviceProfile mPersistentTaskbarDeviceProfile; + private final LauncherPrefs mLauncherPrefs; + public TaskbarActivityContext(Context windowContext, @Nullable Context navigationBarPanelContext, DeviceProfile launcherDp, TaskbarNavButtonController buttonController, ScopedUnfoldTransitionProgressProvider @@ -294,6 +297,8 @@ public class TaskbarActivityContext extends BaseTaskbarContext { new KeyboardQuickSwitchController(), new TaskbarPinningController(this), bubbleControllersOptional); + + mLauncherPrefs = LauncherPrefs.get(this); } /** Updates {@link DeviceProfile} instances for any Taskbar windows. */ @@ -411,6 +416,11 @@ public class TaskbarActivityContext extends BaseTaskbarContext { getDeviceProfile().toSmallString()); } + @NonNull + public LauncherPrefs getLauncherPrefs() { + return mLauncherPrefs; + } + /** * Returns the View bounds of transient taskbar. */ diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarPinningController.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarPinningController.kt index cbfa0247e8..6cb28eee36 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarPinningController.kt +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarPinningController.kt @@ -16,7 +16,9 @@ package com.android.launcher3.taskbar import android.animation.AnimatorSet +import android.annotation.SuppressLint import android.view.View +import androidx.annotation.VisibleForTesting import androidx.core.animation.doOnEnd import com.android.launcher3.LauncherPrefs import com.android.launcher3.LauncherPrefs.Companion.TASKBAR_PINNING @@ -31,46 +33,68 @@ class TaskbarPinningController(private val context: TaskbarActivityContext) : private lateinit var controllers: TaskbarControllers private lateinit var taskbarSharedState: TaskbarSharedState - private val launcherPrefs = LauncherPrefs.get(context) + private lateinit var launcherPrefs: LauncherPrefs private val statsLogManager = context.statsLogManager - private var isAnimatingTaskbarPinning = false + @VisibleForTesting var isAnimatingTaskbarPinning = false + @VisibleForTesting lateinit var onCloseCallback: (preferenceChanged: Boolean) -> Unit + @SuppressLint("VisibleForTests") fun init(taskbarControllers: TaskbarControllers, sharedState: TaskbarSharedState) { controllers = taskbarControllers taskbarSharedState = sharedState + launcherPrefs = context.launcherPrefs + onCloseCallback = + fun(didPreferenceChange: Boolean) { + statsLogManager.logger().log(LAUNCHER_TASKBAR_DIVIDER_MENU_CLOSE) + context.dragLayer.post { context.onPopupVisibilityChanged(false) } + + if (!didPreferenceChange) { + return + } + val animateToValue = + if (!launcherPrefs.get(TASKBAR_PINNING)) { + PINNING_PERSISTENT + } else { + PINNING_TRANSIENT + } + taskbarSharedState.taskbarWasPinned = animateToValue == PINNING_TRANSIENT + animateTaskbarPinning(animateToValue) + } } fun showPinningView(view: View) { context.isTaskbarWindowFullscreen = true - view.post { - val popupView = createAndPopulate(view, context) + val popupView = getPopupView(view) popupView.requestFocus() - - popupView.onCloseCallback = - callback@{ didPreferenceChange -> - statsLogManager.logger().log(LAUNCHER_TASKBAR_DIVIDER_MENU_CLOSE) - context.dragLayer.post { context.onPopupVisibilityChanged(false) } - - if (!didPreferenceChange) { - return@callback - } - val animateToValue = - if (!launcherPrefs.get(TASKBAR_PINNING)) { - PINNING_PERSISTENT - } else { - PINNING_TRANSIENT - } - taskbarSharedState.taskbarWasPinned = animateToValue == PINNING_TRANSIENT - animateTaskbarPinning(animateToValue) - } + popupView.onCloseCallback = onCloseCallback context.onPopupVisibilityChanged(true) popupView.show() statsLogManager.logger().log(LAUNCHER_TASKBAR_DIVIDER_MENU_OPEN) } } - private fun animateTaskbarPinning(animateToValue: Float) { + @VisibleForTesting + fun getPopupView(view: View): TaskbarDividerPopupView<*> { + return createAndPopulate(view, context) + } + + @VisibleForTesting + fun animateTaskbarPinning(animateToValue: Float) { + val taskbarViewController = controllers.taskbarViewController + val animatorSet = + getAnimatorSetForTaskbarPinningAnimation(animateToValue).apply { + doOnEnd { recreateTaskbarAndUpdatePinningValue() } + duration = PINNING_ANIMATION_DURATION + } + controllers.taskbarOverlayController.hideWindow() + updateIsAnimatingTaskbarPinningAndNotifyTaskbarDragLayer(true) + taskbarViewController.animateAwayNotificationDotsDuringTaskbarPinningAnimation() + animatorSet.start() + } + + @VisibleForTesting + fun getAnimatorSetForTaskbarPinningAnimation(animateToValue: Float): AnimatorSet { val animatorSet = AnimatorSet() val taskbarViewController = controllers.taskbarViewController val dragLayerController = controllers.taskbarDragLayerController @@ -82,13 +106,7 @@ class TaskbarPinningController(private val context: TaskbarActivityContext) : taskbarViewController.taskbarIconTranslationXForPinning.animateToValue(animateToValue) ) - controllers.taskbarOverlayController.hideWindow() - - animatorSet.doOnEnd { recreateTaskbarAndUpdatePinningValue() } - animatorSet.duration = PINNING_ANIMATION_DURATION - updateIsAnimatingTaskbarPinningAndNotifyTaskbarDragLayer(true) - taskbarViewController.animateAwayNotificationDotsDuringTaskbarPinningAnimation() - animatorSet.start() + return animatorSet } private fun updateIsAnimatingTaskbarPinningAndNotifyTaskbarDragLayer(isAnimating: Boolean) { @@ -96,7 +114,8 @@ class TaskbarPinningController(private val context: TaskbarActivityContext) : context.dragLayer.setAnimatingTaskbarPinning(isAnimating) } - private fun recreateTaskbarAndUpdatePinningValue() { + @VisibleForTesting + fun recreateTaskbarAndUpdatePinningValue() { updateIsAnimatingTaskbarPinningAndNotifyTaskbarDragLayer(false) launcherPrefs.put(TASKBAR_PINNING, !launcherPrefs.get(TASKBAR_PINNING)) } diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarSharedState.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarSharedState.java index 176a8c5e51..1224b3f064 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarSharedState.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarSharedState.java @@ -73,9 +73,17 @@ public class TaskbarSharedState { }; // Allows us to shift translation logic when doing taskbar pinning animation. - public Boolean startTaskbarVariantIsTransient = true; + public boolean startTaskbarVariantIsTransient = true; // To track if taskbar was pinned using taskbar pinning feature at the time of recreate, // so we can unstash transient taskbar when we un-pinning taskbar. - public Boolean taskbarWasPinned = false; + private boolean mTaskbarWasPinned = false; + + public boolean getTaskbarWasPinned() { + return mTaskbarWasPinned; + } + + public void setTaskbarWasPinned(boolean taskbarWasPinned) { + mTaskbarWasPinned = taskbarWasPinned; + } } diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java index 9c532ec4f8..c74ddcbd21 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java @@ -307,7 +307,7 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba boolean isTransientTaskbar = DisplayController.isTransientTaskbar(mActivity); boolean isInSetup = !mActivity.isUserSetupComplete() || setupUIVisible; updateStateForFlag(FLAG_STASHED_IN_APP_AUTO, - isTransientTaskbar && !mTaskbarSharedState.taskbarWasPinned); + isTransientTaskbar && !mTaskbarSharedState.getTaskbarWasPinned()); updateStateForFlag(FLAG_STASHED_IN_APP_SETUP, isInSetup); updateStateForFlag(FLAG_IN_SETUP, isInSetup); updateStateForFlag(FLAG_STASHED_SMALL_SCREEN, isPhoneMode() @@ -316,7 +316,7 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba // us that we're paused until a bit later. This avoids flickering upon recreating taskbar. updateStateForFlag(FLAG_IN_APP, true); applyState(/* duration = */ 0); - if (mTaskbarSharedState.taskbarWasPinned) { + if (mTaskbarSharedState.getTaskbarWasPinned()) { tryStartTaskbarTimeout(); } notifyStashChange(/* visible */ false, /* stashed */ isStashedInApp()); diff --git a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsSlideInView.java b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsSlideInView.java index 5ce2a7a246..964d329066 100644 --- a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsSlideInView.java +++ b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsSlideInView.java @@ -222,7 +222,7 @@ public class TaskbarAllAppsSlideInView extends AbstractSlideInView<TaskbarOverla if (ev.getAction() == MotionEvent.ACTION_DOWN) { mNoIntercept = !mAppsView.shouldContainerScroll(ev) || getTopOpenViewWithType( - mActivityContext, TYPE_ACCESSIBLE & ~TYPE_TASKBAR_OVERLAYS) != null; + mActivityContext, TYPE_TOUCH_CONTROLLER_NO_INTERCEPT) != null; } return super.onControllerInterceptTouchEvent(ev); } diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java index 8cbf2394ae..2c937b008e 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java +++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java @@ -15,8 +15,7 @@ */ package com.android.launcher3.uioverrides.touchcontrollers; -import static com.android.launcher3.AbstractFloatingView.TYPE_ACCESSIBLE; -import static com.android.launcher3.AbstractFloatingView.TYPE_ALL_APPS_EDU; +import static com.android.launcher3.AbstractFloatingView.TYPE_TOUCH_CONTROLLER_NO_INTERCEPT; import static com.android.launcher3.AbstractFloatingView.getTopOpenViewWithType; import static com.android.launcher3.LauncherState.ALL_APPS; import static com.android.launcher3.LauncherState.NORMAL; @@ -84,7 +83,7 @@ public class PortraitStatesTouchController extends AbstractStateChangeTouchContr return false; } } - if (getTopOpenViewWithType(mLauncher, TYPE_ACCESSIBLE | TYPE_ALL_APPS_EDU) != null) { + if (getTopOpenViewWithType(mLauncher, TYPE_TOUCH_CONTROLLER_NO_INTERCEPT) != null) { return false; } return true; diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java index 3d94857848..19bfe069c8 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java +++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java @@ -15,7 +15,7 @@ */ package com.android.launcher3.uioverrides.touchcontrollers; -import static com.android.launcher3.AbstractFloatingView.TYPE_ACCESSIBLE; +import static com.android.launcher3.AbstractFloatingView.TYPE_TOUCH_CONTROLLER_NO_INTERCEPT; import static com.android.launcher3.LauncherAnimUtils.SUCCESS_TRANSITION_PROGRESS; import static com.android.launcher3.touch.SingleAxisSwipeDetector.DIRECTION_BOTH; @@ -112,7 +112,8 @@ public abstract class TaskViewTouchController<T extends BaseDraggingActivity> // If we are already animating from a previous state, we can intercept. return true; } - if (AbstractFloatingView.getTopOpenViewWithType(mActivity, TYPE_ACCESSIBLE) != null) { + if (AbstractFloatingView.getTopOpenViewWithType( + mActivity, TYPE_TOUCH_CONTROLLER_NO_INTERCEPT) != null) { return false; } return isRecentsInteractive(); diff --git a/quickstep/src/com/android/quickstep/SystemUiProxy.java b/quickstep/src/com/android/quickstep/SystemUiProxy.java index 27de20cec4..94ed5b9cb0 100644 --- a/quickstep/src/com/android/quickstep/SystemUiProxy.java +++ b/quickstep/src/com/android/quickstep/SystemUiProxy.java @@ -17,7 +17,6 @@ package com.android.quickstep; import static android.app.ActivityManager.RECENT_IGNORE_UNAVAILABLE; -import static com.android.launcher3.testing.shared.TestProtocol.testLogD; import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR; import static com.android.launcher3.util.SplitConfigurationOptions.StagePosition; @@ -102,6 +101,7 @@ import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; import java.util.LinkedHashMap; +import java.util.List; /** * Holds the reference to SystemUI. @@ -147,6 +147,9 @@ public class SystemUiProxy implements ISystemUiProxy { private IDesktopTaskListener mDesktopTaskListener; private final LinkedHashMap<RemoteTransition, TransitionFilter> mRemoteTransitions = new LinkedHashMap<>(); + + private final List<Runnable> mStateChangeCallbacks = new ArrayList<>(); + private IBinder mOriginalTransactionToken = null; private IOnBackInvokedCallback mBackToLauncherCallback; private IRemoteAnimationRunner mBackToLauncherRunner; @@ -268,6 +271,7 @@ public class SystemUiProxy implements ISystemUiProxy { setDesktopTaskListener(mDesktopTaskListener); setAssistantOverridesRequested( AssistUtils.newInstance(mContext).getSysUiAssistOverrideInvocationTypes()); + mStateChangeCallbacks.forEach(Runnable::run); } /** @@ -278,6 +282,20 @@ public class SystemUiProxy implements ISystemUiProxy { setProxy(null, null, null, null, null, null, null, null, null, null, null, null, null); } + /** + * Adds a callback to be notified whenever the active state changes + */ + public void addOnStateChangeListener(Runnable callback) { + mStateChangeCallbacks.add(callback); + } + + /** + * Removes a previously added state change callback + */ + public void removeOnStateChangeListener(Runnable callback) { + mStateChangeCallbacks.remove(callback); + } + // TODO(141886704): Find a way to remove this public void setLastSystemUiStateFlags(int stateFlags) { mLastSystemUiStateFlags = stateFlags; @@ -1082,6 +1100,25 @@ public class SystemUiProxy implements ISystemUiProxy { } /** + * Returns a surface which can be used to attach overlays to home task or null if + * the task doesn't exist or sysui is not connected + */ + @Nullable + public SurfaceControl getHomeTaskOverlayContainer() { + // Use a local reference as this method can be called on a worker thread, which can lead + // to NullPointer exceptions if mShellTransitions is modified on the main thread. + IShellTransitions shellTransitions = mShellTransitions; + if (shellTransitions != null) { + try { + return mShellTransitions.getHomeTaskOverlayContainer(); + } catch (RemoteException e) { + Log.w(TAG, "Failed call getOverlayContainerForTask", e); + } + } + return null; + } + + /** * Use SystemUI's transaction-queue instead of Launcher's independent one. This is necessary * if Launcher and SystemUI need to coordinate transactions (eg. for shell transitions). */ diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java index 7e1034b16a..87cee63f9e 100644 --- a/quickstep/src/com/android/quickstep/views/RecentsView.java +++ b/quickstep/src/com/android/quickstep/views/RecentsView.java @@ -2124,8 +2124,7 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T for (int i = 0; i < taskCount; i++) { TaskView taskView = requireTaskViewAt(i); taskView.updateTaskSize(); - taskView.getPrimaryNonGridTranslationProperty().set(taskView, accumulatedTranslationX); - taskView.getSecondaryNonGridTranslationProperty().set(taskView, 0f); + taskView.setNonGridTranslationX(accumulatedTranslationX); taskView.setNonGridPivotTranslationX(translateXToMiddle); // Compensate space caused by TaskView scaling. float widthDiff = @@ -2642,23 +2641,25 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T if (endState.displayOverviewTasksAsGrid(mActivity.getDeviceProfile())) { TaskView runningTaskView = getRunningTaskView(); float runningTaskPrimaryGridTranslation = 0; + float runningTaskSecondaryGridTranslation = 0; if (runningTaskView != null) { // Apply the grid translation to running task unless it's being snapped to // and removes the current translation applied to the running task. - runningTaskPrimaryGridTranslation = mOrientationHandler.getPrimaryValue( - runningTaskView.getGridTranslationX(), - runningTaskView.getGridTranslationY()) - - runningTaskView.getPrimaryNonGridTranslationProperty().get( - runningTaskView); + runningTaskPrimaryGridTranslation = runningTaskView.getGridTranslationX() + - runningTaskView.getNonGridTranslationX(); + runningTaskSecondaryGridTranslation = runningTaskView.getGridTranslationY(); } for (TaskViewSimulator tvs : taskViewSimulators) { if (animatorSet == null) { setGridProgress(1); tvs.taskPrimaryTranslation.value = runningTaskPrimaryGridTranslation; + tvs.taskSecondaryTranslation.value = runningTaskSecondaryGridTranslation; } else { animatorSet.play(ObjectAnimator.ofFloat(this, RECENTS_GRID_PROGRESS, 1)); animatorSet.play(tvs.taskPrimaryTranslation.animateToValue( runningTaskPrimaryGridTranslation)); + animatorSet.play(tvs.taskSecondaryTranslation.animateToValue( + runningTaskSecondaryGridTranslation)); } } } @@ -3123,6 +3124,14 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T + snappedTaskNonGridScrollAdjustment); } + final TaskView runningTask = getRunningTaskView(); + if (showAsGrid() && enableGridOnlyOverview() && runningTask != null) { + runActionOnRemoteHandles( + remoteTargetHandle -> remoteTargetHandle.getTaskViewSimulator() + .taskSecondaryTranslation.value = runningTask.getGridTranslationY() + ); + } + mClearAllButton.setGridTranslationPrimary( clearAllTotalTranslationX - snappedTaskGridTranslationX); mClearAllButton.setGridScrollOffset( diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java index af4f402f07..b42f0552cd 100644 --- a/quickstep/src/com/android/quickstep/views/TaskView.java +++ b/quickstep/src/com/android/quickstep/views/TaskView.java @@ -119,6 +119,8 @@ import com.android.systemui.shared.recents.model.ThumbnailData; import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.shared.system.QuickStepContract; +import kotlin.Unit; + import java.lang.annotation.Retention; import java.util.Arrays; import java.util.Collections; @@ -127,8 +129,6 @@ import java.util.List; import java.util.function.Consumer; import java.util.stream.Stream; -import kotlin.Unit; - /** * A task in the Recents view. */ @@ -304,32 +304,6 @@ public class TaskView extends FrameLayout implements Reusable { } }; - private static final FloatProperty<TaskView> NON_GRID_TRANSLATION_X = - new FloatProperty<TaskView>("nonGridTranslationX") { - @Override - public void setValue(TaskView taskView, float v) { - taskView.setNonGridTranslationX(v); - } - - @Override - public Float get(TaskView taskView) { - return taskView.mNonGridTranslationX; - } - }; - - private static final FloatProperty<TaskView> NON_GRID_TRANSLATION_Y = - new FloatProperty<TaskView>("nonGridTranslationY") { - @Override - public void setValue(TaskView taskView, float v) { - taskView.setNonGridTranslationY(v); - } - - @Override - public Float get(TaskView taskView) { - return taskView.mNonGridTranslationY; - } - }; - public static final FloatProperty<TaskView> GRID_END_TRANSLATION_X = new FloatProperty<TaskView>("gridEndTranslationX") { @Override @@ -386,7 +360,6 @@ public class TaskView extends FrameLayout implements Reusable { // Applied as a complement to gridTranslation, for adjusting the carousel overview and quick // switch. private float mNonGridTranslationX; - private float mNonGridTranslationY; private float mNonGridPivotTranslationX; // Used when in SplitScreenSelectState private float mSplitSelectTranslationY; @@ -1323,7 +1296,7 @@ public class TaskView extends FrameLayout implements Reusable { } protected void resetPersistentViewTransforms() { - mNonGridTranslationX = mNonGridTranslationY = mGridTranslationX = + mNonGridTranslationX = mGridTranslationX = mGridTranslationY = mBoxTranslationY = mNonGridPivotTranslationX = 0f; resetViewTransforms(); } @@ -1494,14 +1467,16 @@ public class TaskView extends FrameLayout implements Reusable { applyTranslationY(); } - private void setNonGridTranslationX(float nonGridTranslationX) { - mNonGridTranslationX = nonGridTranslationX; - applyTranslationX(); + public float getNonGridTranslationX() { + return mNonGridTranslationX; } - private void setNonGridTranslationY(float nonGridTranslationY) { - mNonGridTranslationY = nonGridTranslationY; - applyTranslationY(); + /** + * Updates X coordinate of non-grid translation. + */ + public void setNonGridTranslationX(float nonGridTranslationX) { + mNonGridTranslationX = nonGridTranslationX; + applyTranslationX(); } public void setGridTranslationX(float gridTranslationX) { @@ -1540,7 +1515,7 @@ public class TaskView extends FrameLayout implements Reusable { if (gridEnabled) { scrollAdjustment += mGridTranslationX; } else { - scrollAdjustment += getPrimaryNonGridTranslationProperty().get(this); + scrollAdjustment += getNonGridTranslationX(); } return scrollAdjustment; } @@ -1586,9 +1561,7 @@ public class TaskView extends FrameLayout implements Reusable { * change according to a temporary state (e.g. task offset). */ public float getPersistentTranslationY() { - return mBoxTranslationY - + getNonGridTrans(mNonGridTranslationY) - + getGridTrans(mGridTranslationY); + return mBoxTranslationY + getGridTrans(mGridTranslationY); } public FloatProperty<TaskView> getPrimarySplitTranslationProperty() { @@ -1626,16 +1599,6 @@ public class TaskView extends FrameLayout implements Reusable { TASK_RESISTANCE_TRANSLATION_X, TASK_RESISTANCE_TRANSLATION_Y); } - public FloatProperty<TaskView> getPrimaryNonGridTranslationProperty() { - return getPagedOrientationHandler().getPrimaryValue( - NON_GRID_TRANSLATION_X, NON_GRID_TRANSLATION_Y); - } - - public FloatProperty<TaskView> getSecondaryNonGridTranslationProperty() { - return getPagedOrientationHandler().getSecondaryValue( - NON_GRID_TRANSLATION_X, NON_GRID_TRANSLATION_Y); - } - @Override public boolean hasOverlappingRendering() { // TODO: Clip-out the icon region from the thumbnail, since they are overlapping. diff --git a/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java b/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java index eced5a9a41..8d54dce8ef 100644 --- a/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java +++ b/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java @@ -144,7 +144,7 @@ public class FallbackRecentsTest { .around(new TestStabilityRule()) .around(new NavigationModeSwitchRule(mLauncher)) .around(new FailureWatcher(mLauncher, viewCaptureRule::getViewCaptureData)) - .around(viewCaptureRule) + // .around(viewCaptureRule) b/315482167 .around(new TestIsolationRule(mLauncher, false)) .around(setLauncherCommand); diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsTrackpad.java b/quickstep/tests/src/com/android/quickstep/TaplTestsTrackpad.java index 0eec8b7a37..3465f23476 100644 --- a/quickstep/tests/src/com/android/quickstep/TaplTestsTrackpad.java +++ b/quickstep/tests/src/com/android/quickstep/TaplTestsTrackpad.java @@ -86,7 +86,7 @@ public class TaplTestsTrackpad extends AbstractQuickStepTest { mLauncher.setTrackpadGestureType(TrackpadGestureType.THREE_FINGER); startTestActivity(2); - mLauncher.pressBack(); + mLauncher.getLaunchedAppState().pressBackToWorkspace(); } finally { instrumentation.getUiAutomation().dropShellPermissionIdentity(); } diff --git a/quickstep/tests/src/com/android/quickstep/taskbar/controllers/TaskbarPinningControllerTest.kt b/quickstep/tests/src/com/android/quickstep/taskbar/controllers/TaskbarPinningControllerTest.kt new file mode 100644 index 0000000000..dbe4624e45 --- /dev/null +++ b/quickstep/tests/src/com/android/quickstep/taskbar/controllers/TaskbarPinningControllerTest.kt @@ -0,0 +1,202 @@ +/* + * Copyright (C) 2023 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.quickstep.taskbar.controllers + +import android.animation.AnimatorSet +import android.animation.ObjectAnimator +import android.view.View +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.launcher3.LauncherPrefs +import com.android.launcher3.LauncherPrefs.Companion.TASKBAR_PINNING +import com.android.launcher3.logging.StatsLogManager +import com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_DIVIDER_MENU_CLOSE +import com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_DIVIDER_MENU_OPEN +import com.android.launcher3.taskbar.TaskbarActivityContext +import com.android.launcher3.taskbar.TaskbarBaseTestCase +import com.android.launcher3.taskbar.TaskbarDividerPopupView +import com.android.launcher3.taskbar.TaskbarDragLayer +import com.android.launcher3.taskbar.TaskbarPinningController +import com.android.launcher3.taskbar.TaskbarPinningController.Companion.PINNING_PERSISTENT +import com.android.launcher3.taskbar.TaskbarPinningController.Companion.PINNING_TRANSIENT +import com.android.launcher3.taskbar.TaskbarSharedState +import com.google.common.truth.Truth.assertThat +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.kotlin.any +import org.mockito.kotlin.argumentCaptor +import org.mockito.kotlin.doNothing +import org.mockito.kotlin.doReturn +import org.mockito.kotlin.mock +import org.mockito.kotlin.never +import org.mockito.kotlin.spy +import org.mockito.kotlin.times +import org.mockito.kotlin.verify +import org.mockito.kotlin.whenever + +@SmallTest +@RunWith(AndroidJUnit4::class) +class TaskbarPinningControllerTest : TaskbarBaseTestCase() { + private val taskbarDragLayer = mock<TaskbarDragLayer>() + private val taskbarSharedState = mock<TaskbarSharedState>() + private val launcherPrefs = mock<LauncherPrefs> { on { get(TASKBAR_PINNING) } doReturn false } + private val statsLogger = mock<StatsLogManager.StatsLogger>() + private val statsLogManager = mock<StatsLogManager> { on { logger() } doReturn statsLogger } + private lateinit var pinningController: TaskbarPinningController + + @Before + override fun setup() { + super.setup() + whenever(taskbarActivityContext.launcherPrefs).thenReturn(launcherPrefs) + whenever(taskbarActivityContext.dragLayer).thenReturn(taskbarDragLayer) + whenever(taskbarActivityContext.statsLogManager).thenReturn(statsLogManager) + pinningController = spy(TaskbarPinningController(taskbarActivityContext)) + pinningController.init(taskbarControllers, taskbarSharedState) + } + + @Test + fun testOnCloseCallback_whenClosingPopupView_shouldLogStatsForClosingPopupMenu() { + pinningController.onCloseCallback(false) + verify(statsLogger, times(1)).log(LAUNCHER_TASKBAR_DIVIDER_MENU_CLOSE) + } + + @Test + fun testOnCloseCallback_whenClosingPopupView_shouldPostVisibilityChangedToDragLayer() { + val argumentCaptor = argumentCaptor<Runnable>() + pinningController.onCloseCallback(false) + verify(taskbarDragLayer, times(1)).post(argumentCaptor.capture()) + + val runnable = argumentCaptor.lastValue + assertThat(runnable).isNotNull() + + runnable.run() + verify(taskbarActivityContext, times(1)).onPopupVisibilityChanged(false) + } + + @Test + fun testOnCloseCallback_whenPreferenceUnchanged_shouldNotAnimateTaskbarPinning() { + pinningController.onCloseCallback(false) + verify(taskbarSharedState, never()).taskbarWasPinned = true + verify(pinningController, never()).animateTaskbarPinning(any()) + } + + @Test + fun testOnCloseCallback_whenPreferenceChanged_shouldAnimateToPinnedTaskbar() { + whenever(launcherPrefs.get(TASKBAR_PINNING)).thenReturn(false) + doNothing().whenever(pinningController).animateTaskbarPinning(any()) + + pinningController.onCloseCallback(true) + + verify(taskbarSharedState, times(1)).taskbarWasPinned = false + verify(pinningController, times(1)).animateTaskbarPinning(PINNING_PERSISTENT) + } + + @Test + fun testOnCloseCallback_whenPreferenceChanged_shouldAnimateToTransientTaskbar() { + whenever(launcherPrefs.get(TASKBAR_PINNING)).thenReturn(true) + doNothing().whenever(pinningController).animateTaskbarPinning(any()) + + pinningController.onCloseCallback(true) + + verify(taskbarSharedState, times(1)).taskbarWasPinned = true + verify(pinningController, times(1)).animateTaskbarPinning(PINNING_TRANSIENT) + } + + @Test + fun testShowPinningView_whenShowingPinningView_shouldSetTaskbarWindowFullscreenAndPostRunnableToView() { + val popupView = + mock<TaskbarDividerPopupView<TaskbarActivityContext>> { + on { requestFocus() } doReturn true + } + val view = mock<View>() + val argumentCaptor = argumentCaptor<Runnable>() + doReturn(popupView).whenever(pinningController).getPopupView(view) + + pinningController.showPinningView(view) + + verify(view, times(1)).post(argumentCaptor.capture()) + + val runnable = argumentCaptor.lastValue + assertThat(runnable).isNotNull() + runnable.run() + + verify(pinningController, times(1)).getPopupView(view) + verify(popupView, times(1)).requestFocus() + verify(popupView, times(1)).onCloseCallback = any() + verify(taskbarActivityContext, times(1)).onPopupVisibilityChanged(true) + verify(popupView, times(1)).show() + verify(statsLogger, times(1)).log(LAUNCHER_TASKBAR_DIVIDER_MENU_OPEN) + } + + @Test + fun testAnimateTaskbarPinning_whenAnimationEnds_shouldInvokeCallbackDoOnEnd() { + val animatorSet = spy(AnimatorSet()) + doReturn(animatorSet) + .whenever(pinningController) + .getAnimatorSetForTaskbarPinningAnimation(PINNING_PERSISTENT) + doNothing().whenever(animatorSet).start() + pinningController.animateTaskbarPinning(PINNING_PERSISTENT) + animatorSet.listeners[0].onAnimationEnd(ObjectAnimator()) + verify(pinningController, times(1)).recreateTaskbarAndUpdatePinningValue() + } + + @Test + fun testAnimateTaskbarPinning_whenAnimatingToPersistentTaskbar_shouldAnimateToPinnedTaskbar() { + val animatorSet = spy(AnimatorSet()) + doReturn(animatorSet) + .whenever(pinningController) + .getAnimatorSetForTaskbarPinningAnimation(PINNING_PERSISTENT) + doNothing().whenever(animatorSet).start() + pinningController.animateTaskbarPinning(PINNING_PERSISTENT) + + verify(taskbarOverlayController, times(1)).hideWindow() + verify(pinningController, times(1)) + .getAnimatorSetForTaskbarPinningAnimation(PINNING_PERSISTENT) + verify(taskbarViewController, times(1)) + .animateAwayNotificationDotsDuringTaskbarPinningAnimation() + verify(taskbarDragLayer, times(1)).setAnimatingTaskbarPinning(true) + assertThat(pinningController.isAnimatingTaskbarPinning).isTrue() + assertThat(animatorSet.listeners).isNotNull() + } + + @Test + fun testAnimateTaskbarPinning_whenAnimatingToTransientTaskbar_shouldAnimateToTransientTaskbar() { + val animatorSet = spy(AnimatorSet()) + doReturn(animatorSet) + .whenever(pinningController) + .getAnimatorSetForTaskbarPinningAnimation(PINNING_TRANSIENT) + doNothing().whenever(animatorSet).start() + pinningController.animateTaskbarPinning(PINNING_TRANSIENT) + + verify(taskbarOverlayController, times(1)).hideWindow() + verify(pinningController, times(1)) + .getAnimatorSetForTaskbarPinningAnimation(PINNING_TRANSIENT) + verify(taskbarDragLayer, times(1)).setAnimatingTaskbarPinning(true) + assertThat(pinningController.isAnimatingTaskbarPinning).isTrue() + verify(taskbarViewController, times(1)) + .animateAwayNotificationDotsDuringTaskbarPinningAnimation() + assertThat(animatorSet.listeners).isNotNull() + } + + @Test + fun testRecreateTaskbarAndUpdatePinningValue_whenAnimationEnds_shouldUpdateTaskbarPinningLauncherPref() { + pinningController.recreateTaskbarAndUpdatePinningValue() + verify(taskbarDragLayer, times(1)).setAnimatingTaskbarPinning(false) + assertThat(pinningController.isAnimatingTaskbarPinning).isFalse() + verify(launcherPrefs, times(1)).put(TASKBAR_PINNING, true) + } +} diff --git a/src/com/android/launcher3/AbstractFloatingView.java b/src/com/android/launcher3/AbstractFloatingView.java index d78afd35d6..f72c55615c 100644 --- a/src/com/android/launcher3/AbstractFloatingView.java +++ b/src/com/android/launcher3/AbstractFloatingView.java @@ -135,6 +135,10 @@ public abstract class AbstractFloatingView extends LinearLayout implements Touch public static final int TYPE_TASKBAR_OVERLAYS = TYPE_TASKBAR_ALL_APPS | TYPE_TASKBAR_EDUCATION_DIALOG; + // Floating views that a TouchController should not try to intercept touches from. + public static final int TYPE_TOUCH_CONTROLLER_NO_INTERCEPT = TYPE_ALL & ~TYPE_DISCOVERY_BOUNCE + & ~TYPE_LISTENER & ~TYPE_TASKBAR_OVERLAYS; + public static final int TYPE_ALL_EXCEPT_ON_BOARD_POPUP = TYPE_ALL & ~TYPE_ON_BOARD_POPUP & ~TYPE_PIN_IME_POPUP; diff --git a/src/com/android/launcher3/AppWidgetsRestoredReceiver.java b/src/com/android/launcher3/AppWidgetsRestoredReceiver.java index 641fd834e2..429978eeb4 100644 --- a/src/com/android/launcher3/AppWidgetsRestoredReceiver.java +++ b/src/com/android/launcher3/AppWidgetsRestoredReceiver.java @@ -9,9 +9,13 @@ import android.content.Context; import android.content.Intent; import android.util.Log; +import com.android.launcher3.logging.FileLog; +import com.android.launcher3.provider.RestoreDbTask; import com.android.launcher3.util.IntArray; import com.android.launcher3.widget.LauncherWidgetHolder; +import java.util.Arrays; + public class AppWidgetsRestoredReceiver extends BroadcastReceiver { private static final String TAG = "AppWidgetsRestoredReceiver"; @@ -20,8 +24,11 @@ public class AppWidgetsRestoredReceiver extends BroadcastReceiver { public void onReceive(final Context context, Intent intent) { if (AppWidgetManager.ACTION_APPWIDGET_HOST_RESTORED.equals(intent.getAction())) { int hostId = intent.getIntExtra(AppWidgetManager.EXTRA_HOST_ID, 0); - Log.d(TAG, "Widget ID map received for host:" + hostId); + Log.d(TAG, "onReceive: Widget ID map received for host:" + hostId); if (hostId != LauncherWidgetHolder.APPWIDGET_HOST_ID) { + Log.w(TAG, "onReceive: hostId does not match Launcher." + + " Expected: " + LauncherWidgetHolder.APPWIDGET_HOST_ID + + ", Actual: " + hostId); return; } @@ -31,8 +38,18 @@ public class AppWidgetsRestoredReceiver extends BroadcastReceiver { LauncherPrefs.get(context).putSync( OLD_APP_WIDGET_IDS.to(IntArray.wrap(oldIds).toConcatString()), APP_WIDGET_IDS.to(IntArray.wrap(newIds).toConcatString())); + FileLog.d(TAG, "onReceive: Valid Widget IDs received." + + " old IDs=" + Arrays.toString(oldIds) + + ", new IDs=" + Arrays.toString(newIds)); + if (!RestoreDbTask.isPending(context)) { + FileLog.w(TAG, "onReceive: Restored App Widget Ids received but Launcher" + + " restore is not pending. New widget Ids might not get restored."); + } } else { - Log.e(TAG, "Invalid host restored received"); + Log.e(TAG, "onReceive: Invalid widget ids received for Launcher" + + ", skipping restore of widget ids." + + " newIds=" + Arrays.toString(newIds) + + ", oldIds=" + Arrays.toString(oldIds)); } } } diff --git a/src/com/android/launcher3/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java index dfbbcaabb4..5721ed3493 100644 --- a/src/com/android/launcher3/InvariantDeviceProfile.java +++ b/src/com/android/launcher3/InvariantDeviceProfile.java @@ -298,11 +298,15 @@ public class InvariantDeviceProfile { * Reinitialize the current grid after a restore, where some grids might now be disabled. */ public void reinitializeAfterRestore(Context context) { - FileLog.d(TAG, "Reinitializing grid after restore"); String currentGridName = getCurrentGridName(context); String currentDbFile = dbFile; String newGridName = initGrid(context, currentGridName); String newDbFile = dbFile; + FileLog.d(TAG, "Reinitializing grid after restore." + + " currentGridName=" + currentGridName + + ", currentDbFile=" + currentDbFile + + ", newGridName=" + newGridName + + ", newDbFile=" + newDbFile); if (!newDbFile.equals(currentDbFile)) { FileLog.d(TAG, "Restored grid is disabled : " + currentGridName + ", migrating to: " + newGridName diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java index 6ea3e8ae5e..a98ec6484a 100644 --- a/src/com/android/launcher3/model/LoaderTask.java +++ b/src/com/android/launcher3/model/LoaderTask.java @@ -411,7 +411,7 @@ public class LoaderTask implements Runnable { mSessionHelper.getActiveSessions(); installingPkgs.forEach(mApp.getIconCache()::updateSessionCache); FileLog.d(TAG, "loadWorkspace: Packages with active install sessions: " - + installingPkgs.values()); + + installingPkgs.keySet().stream().map(info -> info.mPackageName).toList()); final PackageUserKey tempPackageKey = new PackageUserKey(null, null); mFirstScreenBroadcast = new FirstScreenBroadcast(installingPkgs); @@ -440,10 +440,15 @@ public class LoaderTask implements Runnable { mShortcutKeyToPinnedShortcuts.put(ShortcutKey.fromInfo(shortcut), shortcut); } + if (pinnedShortcuts.isEmpty()) { + FileLog.d(TAG, "No pinned shortcuts found for user " + user); + } } else { // Shortcut manager can fail due to some race condition when the // lock state changes too frequently. For the purpose of the loading // shortcuts, consider the user is still locked. + FileLog.d(TAG, "Shortcut request failed for user " + + user + ", user may still be locked."); userUnlocked = false; } } @@ -590,17 +595,17 @@ public class LoaderTask implements Runnable { // Package is not yet available but might be // installed later. FileLog.d(TAG, "package not yet restored: " + targetPkg); - tempPackageKey.update(targetPkg, c.user); if (c.hasRestoreFlag(WorkspaceItemInfo.FLAG_RESTORE_STARTED)) { // Restore has started once. } else if (installingPkgs.containsKey(tempPackageKey)) { // App restore has started. Update the flag c.restoreFlag |= WorkspaceItemInfo.FLAG_RESTORE_STARTED; - c.updater().put(Favorites.RESTORED, - c.restoreFlag).commit(); + FileLog.d(TAG, "restore started for installing app: " + targetPkg); + c.updater().put(Favorites.RESTORED, c.restoreFlag).commit(); } else { - c.markDeleted("Unrestored app removed: " + targetPkg); + c.markDeleted("removing app that is not restored and not " + + "installing. package: " + targetPkg); return; } } else if (pmHelper.isAppOnSdcard(targetPkg, c.user)) { @@ -611,7 +616,7 @@ public class LoaderTask implements Runnable { } else if (!isSdCardReady) { // SdCard is not ready yet. Package might get available, // once it is ready. - Log.d(TAG, "Missing pkg, will check later: " + targetPkg); + Log.d(TAG, "Missing package, will check later: " + targetPkg); mPendingPackages.add(new PackageUserKey(targetPkg, c.user)); // Add the icon on the workspace anyway. allowMissingTarget = true; @@ -647,7 +652,8 @@ public class LoaderTask implements Runnable { ShortcutInfo pinnedShortcut = mShortcutKeyToPinnedShortcuts.get(key); if (pinnedShortcut == null) { // The shortcut is no longer valid. - c.markDeleted("Pinned shortcut not found"); + c.markDeleted("Pinned shortcut not found for package: " + + key.getPackageName()); return; } info = new WorkspaceItemInfo(pinnedShortcut, mApp.getContext()); @@ -817,7 +823,7 @@ public class LoaderTask implements Runnable { } else { Log.v(TAG, "Widget restore pending id=" + c.id + " appWidgetId=" + appWidgetId - + " status =" + c.restoreFlag); + + " status=" + c.restoreFlag); appWidgetInfo = new LauncherAppWidgetInfo(appWidgetId, component); appWidgetInfo.restoreStatus = c.restoreFlag; diff --git a/src/com/android/launcher3/model/ModelDbController.java b/src/com/android/launcher3/model/ModelDbController.java index 139efc3d99..d2b7161697 100644 --- a/src/com/android/launcher3/model/ModelDbController.java +++ b/src/com/android/launcher3/model/ModelDbController.java @@ -62,6 +62,7 @@ import com.android.launcher3.LauncherPrefs; import com.android.launcher3.LauncherSettings; import com.android.launcher3.LauncherSettings.Favorites; import com.android.launcher3.Utilities; +import com.android.launcher3.logging.FileLog; import com.android.launcher3.pm.UserCache; import com.android.launcher3.provider.LauncherDbUtils; import com.android.launcher3.provider.LauncherDbUtils.SQLiteTransaction; @@ -262,7 +263,7 @@ public class ModelDbController { */ public void tryMigrateDB() { if (!migrateGridIfNeeded()) { - Log.d(TAG, "Migration failed: resetting launcher database"); + FileLog.d(TAG, "Migration failed: resetting launcher database"); createEmptyDB(); LauncherPrefs.get(mContext).putSync( getEmptyDbCreatedKey(mOpenHelper.getDatabaseName()).to(true)); @@ -282,15 +283,17 @@ public class ModelDbController { createDbIfNotExists(); if (LauncherPrefs.get(mContext).get(getEmptyDbCreatedKey())) { // If we have already create a new DB, ignore migration + Log.d(TAG, "migrateGridIfNeeded: new DB already created, skipping migration"); return false; } InvariantDeviceProfile idp = LauncherAppState.getIDP(mContext); if (!GridSizeMigrationUtil.needsToMigrate(mContext, idp)) { + Log.d(TAG, "migrateGridIfNeeded: no grid migration needed"); return true; } String targetDbName = new DeviceGridState(idp).getDbFile(); if (TextUtils.equals(targetDbName, mOpenHelper.getDatabaseName())) { - Log.e(TAG, "migrateGridIfNeeded - target db is same as current: " + targetDbName); + Log.e(TAG, "migrateGridIfNeeded: target db is same as current: " + targetDbName); return false; } DatabaseHelper oldHelper = mOpenHelper; @@ -299,6 +302,9 @@ public class ModelDbController { try { return GridSizeMigrationUtil.migrateGridIfNeeded(mContext, idp, mOpenHelper, oldHelper.getWritableDatabase()); + } catch (Exception e) { + FileLog.e(TAG, "Failed to migrate grid", e); + return false; } finally { if (mOpenHelper != oldHelper) { oldHelper.close(); diff --git a/src/com/android/launcher3/provider/RestoreDbTask.java b/src/com/android/launcher3/provider/RestoreDbTask.java index dbd13b38ad..dc8cd3af65 100644 --- a/src/com/android/launcher3/provider/RestoreDbTask.java +++ b/src/com/android/launcher3/provider/RestoreDbTask.java @@ -89,9 +89,9 @@ public class RestoreDbTask { public static final String APPWIDGET_OLD_IDS = "appwidget_old_ids"; public static final String APPWIDGET_IDS = "appwidget_ids"; - private static final String[] DB_COLUMNS_TO_LOG = {"profileId", "title", "itemType", "screen", - "container", "cellX", "cellY", "spanX", "spanY", "intent"}; + "container", "cellX", "cellY", "spanX", "spanY", "intent", "appWidgetProvider", + "appWidgetId", "restored"}; /** * Tries to restore the backup DB if needed @@ -141,16 +141,17 @@ public class RestoreDbTask { * 4. If restored from a single display backup, remove gaps between screenIds * 5. Override shortcuts that need to be replaced. * - * @return number of items deleted. + * @return number of items deleted */ @VisibleForTesting protected int sanitizeDB(Context context, ModelDbController controller, SQLiteDatabase db, BackupManager backupManager) throws Exception { - FileLog.d(TAG, "Old Launcher Database before sanitizing:"); + logFavoritesTable(db, "Old Launcher Database before sanitizing:", null, null); // Primary user ids long myProfileId = controller.getSerialNumberForUser(myUserHandle()); long oldProfileId = getDefaultProfileId(db); - FileLog.d(TAG, "sanitizeDB: myProfileId=" + myProfileId + " oldProfileId=" + oldProfileId); + FileLog.d(TAG, "sanitizeDB: myProfileId= " + myProfileId + + ", oldProfileId= " + oldProfileId); LongSparseArray<Long> oldManagedProfileIds = getManagedProfileIds(db, oldProfileId); LongSparseArray<Long> profileMapping = new LongSparseArray<>(oldManagedProfileIds.size() + 1); @@ -182,7 +183,7 @@ public class RestoreDbTask { final String[] args = new String[profileIds.length]; Arrays.fill(args, "?"); final String where = "profileId NOT IN (" + TextUtils.join(", ", Arrays.asList(args)) + ")"; - logUnrestoredItems(db, where, profileIds); + logFavoritesTable(db, "items to delete from unrestored profiles:", where, profileIds); int itemsDeletedCount = db.delete(Favorites.TABLE_NAME, where, profileIds); FileLog.d(TAG, itemsDeletedCount + " total items from unrestored user(s) were deleted"); @@ -242,47 +243,6 @@ public class RestoreDbTask { } /** - * Queries and logs the items we will delete from unrestored profiles in the launcher db. - * This is to understand why items might be missing during the restore process for Launcher. - * @param database the Launcher db to query from. - * @param where the SELECT statement to query items that will be deleted. - * @param profileIds the profile ID's the user will be migrating to. - */ - private void logUnrestoredItems(SQLiteDatabase database, String where, String[] profileIds) { - try (Cursor itemsToDelete = database.query( - /* table */ Favorites.TABLE_NAME, - /* columns */ DB_COLUMNS_TO_LOG, - /* selection */ where, - /* selection args */ profileIds, - /* groupBy */ null, - /* having */ null, - /* orderBy */ null - )) { - if (itemsToDelete.moveToFirst()) { - String[] columnNames = itemsToDelete.getColumnNames(); - StringBuilder stringBuilder = new StringBuilder( - "items to be deleted from the Favorites Table during restore:\n" - ); - do { - for (String columnName : columnNames) { - stringBuilder.append(columnName) - .append("=") - .append(itemsToDelete.getString( - itemsToDelete.getColumnIndex(columnName))) - .append(" "); - } - stringBuilder.append("\n"); - } while (itemsToDelete.moveToNext()); - FileLog.d(TAG, stringBuilder.toString()); - } else { - FileLog.d(TAG, "logDeletedItems: No items found to delete"); - } - } catch (Exception e) { - FileLog.e(TAG, "logDeletedItems: Error reading from database", e); - } - } - - /** * Remove gaps between screenIds to make sure no empty pages are left in between. * * e.g. [0, 3, 4, 6, 7] -> [0, 1, 2, 3, 4] @@ -396,7 +356,7 @@ public class RestoreDbTask { IntArray.fromConcatString(lp.get(APP_WIDGET_IDS)).toArray(), host); } else { - FileLog.d(TAG, "No app widget ids to restore."); + FileLog.d(TAG, "No app widget ids were received from backup to restore."); } lp.remove(APP_WIDGET_IDS, OLD_APP_WIDGET_IDS); @@ -409,16 +369,16 @@ public class RestoreDbTask { private void restoreAppWidgetIds(Context context, ModelDbController controller, int[] oldWidgetIds, int[] newWidgetIds, @NonNull AppWidgetHost host) { if (WidgetsModel.GO_DISABLE_WIDGETS) { - Log.e(TAG, "Skipping widget ID remap as widgets not supported"); + FileLog.e(TAG, "Skipping widget ID remap as widgets not supported"); host.deleteHost(); return; } if (!RestoreDbTask.isPending(context)) { // Someone has already gone through our DB once, probably LoaderTask. Skip any further // modifications of the DB. - Log.e(TAG, "Skipping widget ID remap as DB already in use"); + FileLog.e(TAG, "Skipping widget ID remap as DB already in use"); for (int widgetId : newWidgetIds) { - Log.d(TAG, "Deleting widgetId: " + widgetId); + FileLog.d(TAG, "Deleting widgetId: " + widgetId); host.deleteAppWidgetId(widgetId); } return; @@ -426,7 +386,7 @@ public class RestoreDbTask { final AppWidgetManager widgets = AppWidgetManager.getInstance(context); - Log.d(TAG, "restoreAppWidgetIds: " + FileLog.d(TAG, "restoreAppWidgetIds: " + "oldWidgetIds=" + IntArray.wrap(oldWidgetIds).toConcatString() + ", newWidgetIds=" + IntArray.wrap(newWidgetIds).toConcatString()); @@ -434,7 +394,7 @@ public class RestoreDbTask { logDatabaseWidgetInfo(controller); for (int i = 0; i < oldWidgetIds.length; i++) { - Log.i(TAG, "Widget state restore id " + oldWidgetIds[i] + " => " + newWidgetIds[i]); + FileLog.i(TAG, "Widget state restore id " + oldWidgetIds[i] + " => " + newWidgetIds[i]); final AppWidgetProviderInfo provider = widgets.getAppWidgetInfo(newWidgetIds[i]); final int state; @@ -454,7 +414,7 @@ public class RestoreDbTask { final String where = "appWidgetId=? and (restored & 1) = 1 and profileId=?"; String profileId = Long.toString(mainProfileId); final String[] args = new String[] { oldWidgetId, profileId }; - Log.d(TAG, "restoreAppWidgetIds: querying profile id=" + profileId + FileLog.d(TAG, "restoreAppWidgetIds: querying profile id=" + profileId + " with controller profile ID=" + controllerProfileId); int result = new ContentWriter(context, new ContentWriter.CommitParams(controller, where, args)) @@ -463,7 +423,7 @@ public class RestoreDbTask { .commit(); if (result == 0) { // TODO(b/234700507): Remove the logs after the bug is fixed - Log.e(TAG, "restoreAppWidgetIds: remapping failed since the widget is not in" + FileLog.e(TAG, "restoreAppWidgetIds: remapping failed since the widget is not in" + " the database anymore"); try (Cursor cursor = controller.getDb().query( Favorites.TABLE_NAME, @@ -471,7 +431,7 @@ public class RestoreDbTask { "appWidgetId=?", new String[]{oldWidgetId}, null, null, null)) { if (!cursor.moveToFirst()) { // The widget no long exists. - Log.d(TAG, "Deleting widgetId: " + newWidgetIds[i] + " with old id: " + FileLog.d(TAG, "Deleting widgetId: " + newWidgetIds[i] + " with old id: " + oldWidgetId); host.deleteAppWidgetId(newWidgetIds[i]); } @@ -523,7 +483,7 @@ public class RestoreDbTask { } builder.append("]"); Log.d(TAG, "restoreAppWidgetIds: all widget ids in database: " - + builder.toString()); + + builder); } catch (Exception ex) { Log.e(TAG, "Getting widget ids from the database failed", ex); } @@ -572,4 +532,45 @@ public class RestoreDbTask { Collectors.joining(" OR ")); } + /** + * Queries and logs the items from the Favorites table in the launcher db. + * This is to understand why items might be missing during the restore process for Launcher. + * @param database The Launcher db to query from. + * @param logHeader First line in log statement, used to explain what is being logged. + * @param where The SELECT statement to query items. + * @param profileIds The profile ID's for each user profile. + */ + public static void logFavoritesTable(SQLiteDatabase database, @NonNull String logHeader, + String where, String[] profileIds) { + try (Cursor itemsToDelete = database.query( + /* table */ Favorites.TABLE_NAME, + /* columns */ DB_COLUMNS_TO_LOG, + /* selection */ where, + /* selection args */ profileIds, + /* groupBy */ null, + /* having */ null, + /* orderBy */ null + )) { + if (itemsToDelete.moveToFirst()) { + String[] columnNames = itemsToDelete.getColumnNames(); + StringBuilder stringBuilder = new StringBuilder(logHeader + "\n"); + do { + for (String columnName : columnNames) { + stringBuilder.append(columnName) + .append("=") + .append(itemsToDelete.getString( + itemsToDelete.getColumnIndex(columnName))) + .append(" "); + } + stringBuilder.append("\n"); + } while (itemsToDelete.moveToNext()); + FileLog.d(TAG, stringBuilder.toString()); + } else { + FileLog.d(TAG, "logFavoritesTable: No items found from query for" + + "\"" + logHeader + "\""); + } + } catch (Exception e) { + FileLog.e(TAG, "logFavoritesTable: Error reading from database", e); + } + } } diff --git a/src/com/android/launcher3/shortcuts/ShortcutRequest.java b/src/com/android/launcher3/shortcuts/ShortcutRequest.java index 5291ce4620..21efceb0fd 100644 --- a/src/com/android/launcher3/shortcuts/ShortcutRequest.java +++ b/src/com/android/launcher3/shortcuts/ShortcutRequest.java @@ -24,10 +24,11 @@ import android.content.pm.LauncherApps; import android.content.pm.LauncherApps.ShortcutQuery; import android.content.pm.ShortcutInfo; import android.os.UserHandle; -import android.util.Log; import androidx.annotation.Nullable; +import com.android.launcher3.logging.FileLog; + import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -101,7 +102,7 @@ public class ShortcutRequest { return new QueryResult(mContext.getSystemService(LauncherApps.class) .getShortcuts(mQuery, mUserHandle)); } catch (SecurityException | IllegalStateException e) { - Log.e(TAG, "Failed to query for shortcuts", e); + FileLog.e(TAG, "Failed to query for shortcuts", e); return QueryResult.DEFAULT; } } diff --git a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java index 95ed401636..79d8c60049 100644 --- a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java +++ b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java @@ -257,7 +257,7 @@ public abstract class AbstractLauncherUiTest { final RuleChain inner = RuleChain .outerRule(new PortraitLandscapeRunner(this)) .around(new FailureWatcher(mLauncher, viewCaptureRule::getViewCaptureData)) - .around(viewCaptureRule) + // .around(viewCaptureRule) // b/315482167 .around(new TestIsolationRule(mLauncher, true)); return TestHelpers.isInLauncherProcess() diff --git a/tests/tapl/com/android/launcher3/tapl/LaunchedAppState.java b/tests/tapl/com/android/launcher3/tapl/LaunchedAppState.java index 6d58a3562f..184ece74f5 100644 --- a/tests/tapl/com/android/launcher3/tapl/LaunchedAppState.java +++ b/tests/tapl/com/android/launcher3/tapl/LaunchedAppState.java @@ -340,4 +340,17 @@ public final class LaunchedAppState extends Background { } } } + + /** Send the "back" gesture to go to workspace. */ + public Workspace pressBackToWorkspace() { + try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck(); + LauncherInstrumentation.Closable c = mLauncher.addContextLayer( + "want to press back from launched app to workspace")) { + mLauncher.executeAndWaitForWallpaperAnimation( + () -> mLauncher.pressBackImpl(), + "pressing back" + ); + return new Workspace(mLauncher); + } + } } diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java index 0db6eb7412..91ef472ee4 100644 --- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java +++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java @@ -1121,30 +1121,34 @@ public final class LauncherInstrumentation { */ public void pressBack() { try (Closable e = eventsCheck(); Closable c = addContextLayer("want to press back")) { - waitForLauncherInitialized(); - final boolean launcherVisible = - isTablet() ? isLauncherContainerVisible() : isLauncherVisible(); - boolean isThreeFingerTrackpadGesture = - mTrackpadGestureType == TrackpadGestureType.THREE_FINGER; - if (getNavigationModel() == NavigationModel.ZERO_BUTTON - || isThreeFingerTrackpadGesture) { - final Point displaySize = getRealDisplaySize(); - // TODO(b/225505986): change startY and endY back to displaySize.y / 2 once the - // issue is solved. - int startX = isThreeFingerTrackpadGesture ? displaySize.x / 4 : 0; - int endX = isThreeFingerTrackpadGesture ? displaySize.x * 3 / 4 : displaySize.x / 2; - linearGesture(startX, displaySize.y / 4, endX, displaySize.y / 4, - 10, false, GestureScope.DONT_EXPECT_PILFER); + pressBackImpl(); + } + } + + void pressBackImpl() { + waitForLauncherInitialized(); + final boolean launcherVisible = + isTablet() ? isLauncherContainerVisible() : isLauncherVisible(); + boolean isThreeFingerTrackpadGesture = + mTrackpadGestureType == TrackpadGestureType.THREE_FINGER; + if (getNavigationModel() == NavigationModel.ZERO_BUTTON + || isThreeFingerTrackpadGesture) { + final Point displaySize = getRealDisplaySize(); + // TODO(b/225505986): change startY and endY back to displaySize.y / 2 once the + // issue is solved. + int startX = isThreeFingerTrackpadGesture ? displaySize.x / 4 : 0; + int endX = isThreeFingerTrackpadGesture ? displaySize.x * 3 / 4 : displaySize.x / 2; + linearGesture(startX, displaySize.y / 4, endX, displaySize.y / 4, + 10, false, GestureScope.DONT_EXPECT_PILFER); + } else { + waitForNavigationUiObject("back").click(); + } + if (launcherVisible) { + if (getContext().getApplicationInfo().isOnBackInvokedCallbackEnabled()) { + expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_ON_BACK_INVOKED); } else { - waitForNavigationUiObject("back").click(); - } - if (launcherVisible) { - if (getContext().getApplicationInfo().isOnBackInvokedCallbackEnabled()) { - expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_ON_BACK_INVOKED); - } else { - expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_KEY_BACK_DOWN); - expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_KEY_BACK_UP); - } + expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_KEY_BACK_DOWN); + expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_KEY_BACK_UP); } } } |