Merge "Fix crash from folding/unfolding" into main
diff --git a/quickstep/res/values-es/strings.xml b/quickstep/res/values-es/strings.xml
index bb2f533..893b1c8 100644
--- a/quickstep/res/values-es/strings.xml
+++ b/quickstep/res/values-es/strings.xml
@@ -86,7 +86,7 @@
<string name="gesture_tutorial_nice" msgid="2936275692616928280">"¡Muy bien!"</string>
<string name="gesture_tutorial_step" msgid="1279786122817620968">"Tutorial <xliff:g id="CURRENT">%1$d</xliff:g>/<xliff:g id="TOTAL">%2$d</xliff:g>"</string>
<string name="allset_title" msgid="5021126669778966707">"¡Ya está!"</string>
- <string name="allset_hint" msgid="459504134589971527">"Desliza el dedo hacia arriba para ir a la pantalla de inicio"</string>
+ <string name="allset_hint" msgid="459504134589971527">"Desliza hacia arriba para ir a la pantalla de inicio"</string>
<string name="allset_button_hint" msgid="2395219947744706291">"Toca el botón de inicio para ir a la pantalla de inicio"</string>
<string name="allset_description_generic" msgid="5385500062202019855">"Ya puedes empezar a usar tu <xliff:g id="DEVICE">%1$s</xliff:g>"</string>
<string name="default_device_name" msgid="6660656727127422487">"dispositivo"</string>
diff --git a/quickstep/res/values-fa/strings.xml b/quickstep/res/values-fa/strings.xml
index c8da620..91e4ad5 100644
--- a/quickstep/res/values-fa/strings.xml
+++ b/quickstep/res/values-fa/strings.xml
@@ -109,7 +109,7 @@
<string name="taskbar_edu_splitscreen" msgid="5605512479258053350">"برای استفاده همزمان از ۲ برنامه، یک برنامه را به کناری بکشید"</string>
<string name="taskbar_edu_stashing" msgid="5645461372669217294">"برای نمایش «نوار وظیفه»، انگشتتان را آهسته بهبالا بکشید"</string>
<string name="taskbar_edu_suggestions" msgid="8215044496435527982">"براساس روالهایتان، پیشنهاد برنامه دریافت کنید"</string>
- <string name="taskbar_edu_pinning" msgid="6708550858580071558">"برای سنجاق کردن «نوار وظیفه»، جداکننده را چند ثانیه فشار دهید"</string>
+ <string name="taskbar_edu_pinning" msgid="6708550858580071558">"جداکننده را چند ثانیه فشار دهید تا «نوار وظیفه» سنجاق شود"</string>
<string name="taskbar_edu_features" msgid="3320337287472848162">"با «نوار وظیفه» میتوانید کارهای بیشتر انجام دهید"</string>
<string name="taskbar_edu_close" msgid="887022990168191073">"بستن"</string>
<string name="taskbar_edu_done" msgid="6880178093977704569">"تمام"</string>
diff --git a/quickstep/res/values-fr/strings.xml b/quickstep/res/values-fr/strings.xml
index df6cad4..789ef13 100644
--- a/quickstep/res/values-fr/strings.xml
+++ b/quickstep/res/values-fr/strings.xml
@@ -109,7 +109,7 @@
<string name="taskbar_edu_splitscreen" msgid="5605512479258053350">"Faites glisser une appli sur le côté pour en utiliser 2 à la fois"</string>
<string name="taskbar_edu_stashing" msgid="5645461372669217294">"Balayez lentement vers haut pour afficher barre des tâches"</string>
<string name="taskbar_edu_suggestions" msgid="8215044496435527982">"Obtenez des suggestions d\'applis basées sur vos habitudes"</string>
- <string name="taskbar_edu_pinning" msgid="6708550858580071558">"Appui de manière prolongée sur le séparateur pour épingler la barre des tâches"</string>
+ <string name="taskbar_edu_pinning" msgid="6708550858580071558">"Appui prolongé sur le séparateur pour épingler la barre des tâches"</string>
<string name="taskbar_edu_features" msgid="3320337287472848162">"Faites-en plus avec la barre des tâches"</string>
<string name="taskbar_edu_close" msgid="887022990168191073">"Fermer"</string>
<string name="taskbar_edu_done" msgid="6880178093977704569">"OK"</string>
diff --git a/quickstep/res/values/override.xml b/quickstep/res/values/override.xml
index df32626..29779a7 100644
--- a/quickstep/res/values/override.xml
+++ b/quickstep/res/values/override.xml
@@ -35,4 +35,6 @@
<string name="assist_state_manager_class" translatable="false"></string>
+ <string name="launcher_restore_event_logger_class" translatable="false">com.android.quickstep.LauncherRestoreEventLoggerImpl</string>
+
</resources>
diff --git a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
index 4898761..f25b652 100644
--- a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
+++ b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
@@ -64,8 +64,6 @@
import static com.android.launcher3.views.FloatingIconView.getFloatingIconView;
import static com.android.quickstep.TaskAnimationManager.ENABLE_SHELL_TRANSITIONS;
import static com.android.quickstep.TaskViewUtils.findTaskViewToLaunch;
-import static com.android.systemui.shared.system.InteractionJankMonitorWrapper.CUJ_APP_CLOSE_TO_HOME;
-import static com.android.systemui.shared.system.InteractionJankMonitorWrapper.CUJ_APP_CLOSE_TO_HOME_FALLBACK;
import static com.android.systemui.shared.system.QuickStepContract.getWindowCornerRadius;
import static com.android.systemui.shared.system.QuickStepContract.supportsRoundedCornersOnWindows;
@@ -117,6 +115,7 @@
import androidx.annotation.Nullable;
import androidx.core.graphics.ColorUtils;
+import com.android.internal.jank.Cuj;
import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener;
import com.android.launcher3.LauncherAnimationRunner.RemoteAnimationFactory;
import com.android.launcher3.anim.AnimationSuccessListener;
@@ -1652,7 +1651,8 @@
// is initialized.
if (launcherIsForceInvisibleOrOpening) {
addCujInstrumentation(anim, playFallBackAnimation
- ? CUJ_APP_CLOSE_TO_HOME_FALLBACK : CUJ_APP_CLOSE_TO_HOME);
+ ? Cuj.CUJ_LAUNCHER_APP_CLOSE_TO_HOME_FALLBACK
+ : Cuj.CUJ_LAUNCHER_APP_CLOSE_TO_HOME);
anim.addListener(new AnimatorListenerAdapter() {
@Override
@@ -1768,19 +1768,18 @@
if (launchingFromWidget) {
composeWidgetLaunchAnimator(anim, (LauncherAppWidgetHostView) mV, appTargets,
wallpaperTargets, nonAppTargets, launcherClosing);
- addCujInstrumentation(
- anim, InteractionJankMonitorWrapper.CUJ_APP_LAUNCH_FROM_WIDGET);
+ addCujInstrumentation(anim, Cuj.CUJ_LAUNCHER_APP_LAUNCH_FROM_WIDGET);
skipFirstFrame = true;
} else if (launchingFromRecents) {
composeRecentsLaunchAnimator(anim, mV, appTargets, wallpaperTargets, nonAppTargets,
launcherClosing);
addCujInstrumentation(
- anim, InteractionJankMonitorWrapper.CUJ_APP_LAUNCH_FROM_RECENTS);
+ anim, Cuj.CUJ_LAUNCHER_APP_LAUNCH_FROM_RECENTS);
skipFirstFrame = true;
} else {
composeIconLaunchAnimator(anim, mV, appTargets, wallpaperTargets, nonAppTargets,
launcherClosing);
- addCujInstrumentation(anim, InteractionJankMonitorWrapper.CUJ_APP_LAUNCH_FROM_ICON);
+ addCujInstrumentation(anim, Cuj.CUJ_LAUNCHER_APP_LAUNCH_FROM_ICON);
skipFirstFrame = false;
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java
index 968faf0..6d3b60a 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java
@@ -16,6 +16,7 @@
package com.android.launcher3.uioverrides.touchcontrollers;
import static android.view.MotionEvent.ACTION_DOWN;
+
import static com.android.app.animation.Interpolators.ACCELERATE_0_75;
import static com.android.app.animation.Interpolators.DECELERATE_3;
import static com.android.app.animation.Interpolators.LINEAR;
@@ -65,6 +66,7 @@
import android.view.MotionEvent;
import android.view.animation.Interpolator;
+import com.android.internal.jank.Cuj;
import com.android.launcher3.LauncherState;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
@@ -190,8 +192,7 @@
public void onDragStart(boolean start) {
mMotionPauseDetector.clear();
if (start) {
- InteractionJankMonitorWrapper.begin(mRecentsView,
- InteractionJankMonitorWrapper.CUJ_QUICK_SWITCH);
+ InteractionJankMonitorWrapper.begin(mRecentsView, Cuj.CUJ_LAUNCHER_QUICK_SWITCH);
mStartState = mLauncher.getStateManager().getState();
@@ -327,7 +328,7 @@
if (mMotionPauseDetector.isPaused() && noFling) {
// Going to Overview.
cancelAnimations();
- InteractionJankMonitorWrapper.cancel(InteractionJankMonitorWrapper.CUJ_QUICK_SWITCH);
+ InteractionJankMonitorWrapper.cancel(Cuj.CUJ_LAUNCHER_QUICK_SWITCH);
StateAnimationConfig config = new StateAnimationConfig();
config.duration = ATOMIC_DURATION_FROM_PAUSED_TO_OVERVIEW;
@@ -445,7 +446,7 @@
RecentsView.SCROLL_VIBRATION_PRIMITIVE_SCALE,
RecentsView.SCROLL_VIBRATION_FALLBACK);
} else {
- InteractionJankMonitorWrapper.cancel(InteractionJankMonitorWrapper.CUJ_QUICK_SWITCH);
+ InteractionJankMonitorWrapper.cancel(Cuj.CUJ_LAUNCHER_QUICK_SWITCH);
}
nonOverviewAnim.setDuration(Math.max(xDuration, yDuration));
@@ -469,7 +470,7 @@
: LAUNCHER_UNKNOWN_SWIPEDOWN));
if (targetState == QUICK_SWITCH_FROM_HOME) {
- InteractionJankMonitorWrapper.end(InteractionJankMonitorWrapper.CUJ_QUICK_SWITCH);
+ InteractionJankMonitorWrapper.end(Cuj.CUJ_LAUNCHER_QUICK_SWITCH);
}
mLauncher.getStateManager().goToState(targetState, false, forEndCallback(this::clearState));
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java
index 2c937b0..d94cd89 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java
@@ -24,6 +24,7 @@
import android.view.MotionEvent;
import com.android.app.animation.Interpolators;
+import com.android.internal.jank.Cuj;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
@@ -185,18 +186,15 @@
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
InteractionJankMonitorWrapper.begin(
- mLauncher.getRootView(), InteractionJankMonitorWrapper.CUJ_OPEN_ALL_APPS);
+ mLauncher.getRootView(), Cuj.CUJ_LAUNCHER_OPEN_ALL_APPS);
InteractionJankMonitorWrapper.begin(
- mLauncher.getRootView(),
- InteractionJankMonitorWrapper.CUJ_CLOSE_ALL_APPS_SWIPE);
+ mLauncher.getRootView(), Cuj.CUJ_LAUNCHER_CLOSE_ALL_APPS_SWIPE);
break;
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
- InteractionJankMonitorWrapper.cancel(
- InteractionJankMonitorWrapper.CUJ_OPEN_ALL_APPS);
- InteractionJankMonitorWrapper.cancel(
- InteractionJankMonitorWrapper.CUJ_CLOSE_ALL_APPS_SWIPE);
+ InteractionJankMonitorWrapper.cancel(Cuj.CUJ_LAUNCHER_OPEN_ALL_APPS);
+ InteractionJankMonitorWrapper.cancel(Cuj.CUJ_LAUNCHER_CLOSE_ALL_APPS_SWIPE);
break;
}
return super.onControllerInterceptTouchEvent(ev);
@@ -207,11 +205,10 @@
protected void onReinitToState(LauncherState newToState) {
super.onReinitToState(newToState);
if (newToState != ALL_APPS) {
- InteractionJankMonitorWrapper.cancel(InteractionJankMonitorWrapper.CUJ_OPEN_ALL_APPS);
+ InteractionJankMonitorWrapper.cancel(Cuj.CUJ_LAUNCHER_OPEN_ALL_APPS);
}
if (newToState != NORMAL) {
- InteractionJankMonitorWrapper.cancel(
- InteractionJankMonitorWrapper.CUJ_CLOSE_ALL_APPS_SWIPE);
+ InteractionJankMonitorWrapper.cancel(Cuj.CUJ_LAUNCHER_CLOSE_ALL_APPS_SWIPE);
}
}
@@ -219,18 +216,16 @@
protected void onReachedFinalState(LauncherState toState) {
super.onReachedFinalState(toState);
if (toState == ALL_APPS) {
- InteractionJankMonitorWrapper.end(InteractionJankMonitorWrapper.CUJ_OPEN_ALL_APPS);
+ InteractionJankMonitorWrapper.end(Cuj.CUJ_LAUNCHER_OPEN_ALL_APPS);
} else if (toState == NORMAL) {
- InteractionJankMonitorWrapper.end(
- InteractionJankMonitorWrapper.CUJ_CLOSE_ALL_APPS_SWIPE);
+ InteractionJankMonitorWrapper.end(Cuj.CUJ_LAUNCHER_CLOSE_ALL_APPS_SWIPE);
}
}
@Override
protected void clearState() {
super.clearState();
- InteractionJankMonitorWrapper.cancel(InteractionJankMonitorWrapper.CUJ_OPEN_ALL_APPS);
- InteractionJankMonitorWrapper.cancel(
- InteractionJankMonitorWrapper.CUJ_CLOSE_ALL_APPS_SWIPE);
+ InteractionJankMonitorWrapper.cancel(Cuj.CUJ_LAUNCHER_OPEN_ALL_APPS);
+ InteractionJankMonitorWrapper.cancel(Cuj.CUJ_LAUNCHER_CLOSE_ALL_APPS_SWIPE);
}
}
diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
index d7ff59e..42b18bd 100644
--- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
+++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
@@ -96,6 +96,7 @@
import androidx.annotation.Nullable;
import androidx.annotation.UiThread;
+import com.android.internal.jank.Cuj;
import com.android.internal.util.LatencyTracker;
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.DeviceProfile;
@@ -1024,12 +1025,12 @@
}
mHandled = true;
+ InteractionJankMonitorWrapper.begin(mRecentsView, Cuj.CUJ_LAUNCHER_QUICK_SWITCH,
+ 2000 /* ms timeout */);
InteractionJankMonitorWrapper.begin(mRecentsView,
- InteractionJankMonitorWrapper.CUJ_QUICK_SWITCH, 2000 /* ms timeout */);
+ Cuj.CUJ_LAUNCHER_APP_CLOSE_TO_HOME);
InteractionJankMonitorWrapper.begin(mRecentsView,
- InteractionJankMonitorWrapper.CUJ_APP_CLOSE_TO_HOME);
- InteractionJankMonitorWrapper.begin(mRecentsView,
- InteractionJankMonitorWrapper.CUJ_APP_SWIPE_TO_RECENTS);
+ Cuj.CUJ_LAUNCHER_APP_SWIPE_TO_RECENTS);
rv.post(() -> rv.getViewTreeObserver().removeOnDrawListener(this));
}
@@ -1145,16 +1146,13 @@
View postResumeLastTask = mActivityInterface.onSettledOnEndTarget(endTarget);
if (endTarget != NEW_TASK) {
- InteractionJankMonitorWrapper.cancel(
- InteractionJankMonitorWrapper.CUJ_QUICK_SWITCH);
+ InteractionJankMonitorWrapper.cancel(Cuj.CUJ_LAUNCHER_QUICK_SWITCH);
}
if (endTarget != HOME) {
- InteractionJankMonitorWrapper.cancel(
- InteractionJankMonitorWrapper.CUJ_APP_CLOSE_TO_HOME);
+ InteractionJankMonitorWrapper.cancel(Cuj.CUJ_LAUNCHER_APP_CLOSE_TO_HOME);
}
if (endTarget != RECENTS) {
- InteractionJankMonitorWrapper.cancel(
- InteractionJankMonitorWrapper.CUJ_APP_SWIPE_TO_RECENTS);
+ InteractionJankMonitorWrapper.cancel(Cuj.CUJ_LAUNCHER_APP_SWIPE_TO_RECENTS);
}
switch (endTarget) {
diff --git a/quickstep/src/com/android/quickstep/LauncherRestoreEventLoggerImpl.kt b/quickstep/src/com/android/quickstep/LauncherRestoreEventLoggerImpl.kt
new file mode 100644
index 0000000..645ecf4
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/LauncherRestoreEventLoggerImpl.kt
@@ -0,0 +1,136 @@
+package com.android.quickstep
+
+import android.app.backup.BackupManager
+import android.app.backup.BackupRestoreEventLogger
+import android.app.backup.BackupRestoreEventLogger.BackupRestoreDataType
+import android.app.backup.BackupRestoreEventLogger.BackupRestoreError
+import android.content.Context
+import com.android.launcher3.Flags
+import com.android.launcher3.LauncherSettings.Favorites
+import com.android.launcher3.backuprestore.LauncherRestoreEventLogger
+
+/**
+ * Concrete implementation for wrapper to log Restore event metrics for both success and failure to
+ * restore Launcher workspace from a backup. This implementation accesses SystemApis so is only
+ * available to QuickStep/NexusLauncher.
+ */
+class LauncherRestoreEventLoggerImpl(val context: Context) : LauncherRestoreEventLogger() {
+ companion object {
+ const val TAG = "LauncherRestoreEventLoggerImpl"
+
+ // Generic type for any possible workspace items, when specific type is not known.
+ @BackupRestoreDataType private const val DATA_TYPE_LAUNCHER_ITEM = "launcher_item"
+ // Specific workspace item types, based off of Favorites Table.
+ @BackupRestoreDataType private const val DATA_TYPE_APPLICATION = "application"
+ @BackupRestoreDataType private const val DATA_TYPE_FOLDER = "folder"
+ @BackupRestoreDataType private const val DATA_TYPE_APPWIDGET = "widget"
+ @BackupRestoreDataType private const val DATA_TYPE_CUSTOM_APPWIDGET = "custom_widget"
+ @BackupRestoreDataType private const val DATA_TYPE_DEEP_SHORTCUT = "deep_shortcut"
+ @BackupRestoreDataType private const val DATA_TYPE_APP_PAIR = "app_pair"
+ }
+
+ private val backupManager: BackupManager = BackupManager(context)
+ private val restoreEventLogger: BackupRestoreEventLogger = backupManager.delayedRestoreLogger
+
+ /**
+ * For logging when multiple items of a given data type failed to restore.
+ *
+ * @param dataType The data type that was not restored.
+ * @param count the number of data items that were not restored.
+ * @param error error type for why the data was not restored.
+ */
+ override fun logLauncherItemsRestoreFailed(
+ @BackupRestoreDataType dataType: String,
+ count: Int,
+ @BackupRestoreError error: String?
+ ) {
+ if (Flags.enableLauncherBrMetrics()) {
+ restoreEventLogger.logItemsRestoreFailed(dataType, count, error)
+ }
+ }
+
+ /**
+ * For logging when multiple items of a given data type were successfully restored.
+ *
+ * @param dataType The data type that was restored.
+ * @param count the number of data items restored.
+ */
+ override fun logLauncherItemsRestored(@BackupRestoreDataType dataType: String, count: Int) {
+ if (Flags.enableLauncherBrMetrics()) {
+ restoreEventLogger.logItemsRestored(dataType, count)
+ }
+ }
+
+ /**
+ * Helper to log successfully restoring a single item from the Favorites table.
+ *
+ * @param favoritesId The id of the item type from [Favorites] that was restored.
+ */
+ override fun logSingleFavoritesItemRestored(favoritesId: Int) {
+ if (Flags.enableLauncherBrMetrics()) {
+ restoreEventLogger.logItemsRestored(favoritesIdToDataType(favoritesId), 1)
+ }
+ }
+
+ /**
+ * Helper to log a failure to restore a single item from the Favorites table.
+ *
+ * @param favoritesId The id of the item type from [Favorites] that was not restored.
+ * @param error error type for why the data was not restored.
+ */
+ override fun logSingleFavoritesItemRestoreFailed(
+ favoritesId: Int,
+ @BackupRestoreError error: String?
+ ) {
+ if (Flags.enableLauncherBrMetrics()) {
+ restoreEventLogger.logItemsRestoreFailed(favoritesIdToDataType(favoritesId), 1, error)
+ }
+ }
+
+ /**
+ * Helper to log a failure to restore items from the Favorites table.
+ *
+ * @param favoritesId The id of the item type from [Favorites] that was not restored.
+ * @param count number of items that failed to restore.
+ * @param error error type for why the data was not restored.
+ */
+ override fun logFavoritesItemsRestoreFailed(
+ favoritesId: Int,
+ count: Int,
+ @BackupRestoreError error: String?
+ ) {
+ if (Flags.enableLauncherBrMetrics()) {
+ restoreEventLogger.logItemsRestoreFailed(
+ favoritesIdToDataType(favoritesId),
+ count,
+ error
+ )
+ }
+ }
+
+ /**
+ * Uses the current [restoreEventLogger] to report its results to the [backupManager]. Use when
+ * done restoring items for Launcher.
+ */
+ override fun reportLauncherRestoreResults() {
+ if (Flags.enableLauncherBrMetrics()) {
+ backupManager.reportDelayedRestoreResult(restoreEventLogger)
+ }
+ }
+
+ /**
+ * Helper method to convert item types from [Favorites] to B&R data types for logging. Also to
+ * avoid direct usage of @BackupRestoreDataType which is protected under @SystemApi.
+ */
+ @BackupRestoreDataType
+ private fun favoritesIdToDataType(favoritesId: Int): String =
+ when (favoritesId) {
+ Favorites.ITEM_TYPE_APPLICATION -> DATA_TYPE_APPLICATION
+ Favorites.ITEM_TYPE_FOLDER -> DATA_TYPE_FOLDER
+ Favorites.ITEM_TYPE_APPWIDGET -> DATA_TYPE_APPWIDGET
+ Favorites.ITEM_TYPE_CUSTOM_APPWIDGET -> DATA_TYPE_CUSTOM_APPWIDGET
+ Favorites.ITEM_TYPE_DEEP_SHORTCUT -> DATA_TYPE_DEEP_SHORTCUT
+ Favorites.ITEM_TYPE_APP_PAIR -> DATA_TYPE_APP_PAIR
+ else -> DATA_TYPE_LAUNCHER_ITEM
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/OverviewCommandHelper.java b/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
index 31fe791..b2429ad 100644
--- a/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
+++ b/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
@@ -31,6 +31,7 @@
import androidx.annotation.Nullable;
import androidx.annotation.UiThread;
+import com.android.internal.jank.Cuj;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.statemanager.StatefulActivity;
@@ -262,7 +263,7 @@
if (activity != null) {
InteractionJankMonitorWrapper.begin(
activity.getRootView(),
- InteractionJankMonitorWrapper.CUJ_QUICK_SWITCH);
+ Cuj.CUJ_LAUNCHER_QUICK_SWITCH);
}
GestureState gestureState = mService.createGestureState(GestureState.DEFAULT_STATE,
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationController.java b/quickstep/src/com/android/quickstep/RecentsAnimationController.java
index 341e18c..06a442b 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationController.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationController.java
@@ -25,14 +25,13 @@
import android.os.RemoteException;
import android.util.Log;
import android.view.IRecentsAnimationController;
-import android.view.RemoteAnimationTarget;
import android.view.SurfaceControl;
import android.view.WindowManagerGlobal;
import android.window.PictureInPictureSurfaceTransaction;
-import androidx.annotation.NonNull;
import androidx.annotation.UiThread;
+import com.android.internal.jank.Cuj;
import com.android.internal.os.IResultReceiver;
import com.android.launcher3.util.Preconditions;
import com.android.launcher3.util.RunnableList;
@@ -183,10 +182,9 @@
});
}
});
- InteractionJankMonitorWrapper.end(InteractionJankMonitorWrapper.CUJ_QUICK_SWITCH);
- InteractionJankMonitorWrapper.end(InteractionJankMonitorWrapper.CUJ_APP_CLOSE_TO_HOME);
- InteractionJankMonitorWrapper.end(
- InteractionJankMonitorWrapper.CUJ_APP_SWIPE_TO_RECENTS);
+ InteractionJankMonitorWrapper.end(Cuj.CUJ_LAUNCHER_QUICK_SWITCH);
+ InteractionJankMonitorWrapper.end(Cuj.CUJ_LAUNCHER_APP_CLOSE_TO_HOME);
+ InteractionJankMonitorWrapper.end(Cuj.CUJ_LAUNCHER_APP_SWIPE_TO_RECENTS);
};
if (forceFinish) {
finishCb.run();
diff --git a/quickstep/src/com/android/quickstep/TaskViewUtils.java b/quickstep/src/com/android/quickstep/TaskViewUtils.java
index 11c5ab4..4e84f4a 100644
--- a/quickstep/src/com/android/quickstep/TaskViewUtils.java
+++ b/quickstep/src/com/android/quickstep/TaskViewUtils.java
@@ -60,6 +60,7 @@
import androidx.annotation.Nullable;
import com.android.app.animation.Interpolators;
+import com.android.internal.jank.Cuj;
import com.android.launcher3.BaseActivity;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.anim.AnimatedFloat;
@@ -395,8 +396,7 @@
@Override
public void onAnimationSuccess(Animator animator) {
if (isQuickSwitch) {
- InteractionJankMonitorWrapper.end(
- InteractionJankMonitorWrapper.CUJ_QUICK_SWITCH);
+ InteractionJankMonitorWrapper.end(Cuj.CUJ_LAUNCHER_QUICK_SWITCH);
}
}
diff --git a/quickstep/src/com/android/quickstep/interaction/TutorialFragment.java b/quickstep/src/com/android/quickstep/interaction/TutorialFragment.java
index 69c15a5..c91ee81 100644
--- a/quickstep/src/com/android/quickstep/interaction/TutorialFragment.java
+++ b/quickstep/src/com/android/quickstep/interaction/TutorialFragment.java
@@ -25,6 +25,7 @@
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.app.Activity;
+import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.graphics.Insets;
@@ -86,6 +87,8 @@
private boolean mIsFoldable;
private boolean mOnAttachedToWindowPendingCreate;
+ @Nullable private Runnable mOnAttachedOnGlobalLayoutCallback = null;
+
public static TutorialFragment newInstance(
TutorialType tutorialType, boolean gestureComplete, boolean fromTutorialMenu) {
TutorialFragment fragment = getFragmentForTutorialType(tutorialType, fromTutorialMenu);
@@ -349,13 +352,27 @@
new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
- changeController(mTutorialType);
+ runOnAttached(() -> changeController(mTutorialType));
mRootView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
}
});
}
}
+ private void runOnAttached(Runnable callback) {
+ mOnAttachedOnGlobalLayoutCallback = callback;
+ if (getContext() != null) {
+ onAttached();
+ }
+ }
+
+ private void onAttached() {
+ if (mOnAttachedOnGlobalLayoutCallback != null) {
+ mOnAttachedOnGlobalLayoutCallback.run();
+ mOnAttachedOnGlobalLayoutCallback = null;
+ }
+ }
+
@Override
public boolean onTouch(View view, MotionEvent motionEvent) {
if (mTutorialController != null && !isGestureComplete()) {
@@ -378,6 +395,12 @@
}
@Override
+ public void onAttach(@NonNull Context context) {
+ super.onAttach(context);
+ onAttached();
+ }
+
+ @Override
void onAttachedToWindow() {
if (mEdgeBackGestureHandler == null) {
mOnAttachedToWindowPendingCreate = true;
diff --git a/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java b/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
index bce2e82..cf9fc74 100644
--- a/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
+++ b/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
@@ -42,6 +42,7 @@
import androidx.annotation.WorkerThread;
import androidx.slice.SliceItem;
+import com.android.internal.jank.Cuj;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.Utilities;
import com.android.launcher3.logger.LauncherAtom;
@@ -401,11 +402,10 @@
case LAUNCHER_ALLAPPS_VERTICAL_SWIPE_BEGIN:
InteractionJankMonitorWrapper.begin(
view,
- InteractionJankMonitorWrapper.CUJ_ALL_APPS_SCROLL);
+ Cuj.CUJ_LAUNCHER_ALL_APPS_SCROLL);
break;
case LAUNCHER_ALLAPPS_VERTICAL_SWIPE_END:
- InteractionJankMonitorWrapper.end(
- InteractionJankMonitorWrapper.CUJ_ALL_APPS_SCROLL);
+ InteractionJankMonitorWrapper.end(Cuj.CUJ_LAUNCHER_ALL_APPS_SCROLL);
break;
default:
break;
diff --git a/quickstep/src/com/android/quickstep/util/SplitToWorkspaceController.java b/quickstep/src/com/android/quickstep/util/SplitToWorkspaceController.java
index e705285..28efc97 100644
--- a/quickstep/src/com/android/quickstep/util/SplitToWorkspaceController.java
+++ b/quickstep/src/com/android/quickstep/util/SplitToWorkspaceController.java
@@ -34,6 +34,7 @@
import android.os.UserHandle;
import android.view.View;
+import com.android.internal.jank.Cuj;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAppState;
@@ -174,8 +175,7 @@
public void onAnimationEnd(Animator animation) {
if (!mIsCancelled) {
mController.launchSplitTasks(aBoolean -> cleanUp());
- InteractionJankMonitorWrapper.end(
- InteractionJankMonitorWrapper.CUJ_SPLIT_SCREEN_ENTER);
+ InteractionJankMonitorWrapper.end(Cuj.CUJ_SPLIT_SCREEN_ENTER);
}
}
diff --git a/quickstep/src/com/android/quickstep/util/SwipePipToHomeAnimator.java b/quickstep/src/com/android/quickstep/util/SwipePipToHomeAnimator.java
index fe6ce46..a36b32c 100644
--- a/quickstep/src/com/android/quickstep/util/SwipePipToHomeAnimator.java
+++ b/quickstep/src/com/android/quickstep/util/SwipePipToHomeAnimator.java
@@ -16,8 +16,6 @@
package com.android.quickstep.util;
-import static com.android.systemui.shared.system.InteractionJankMonitorWrapper.CUJ_APP_CLOSE_TO_PIP;
-
import android.animation.Animator;
import android.animation.RectEvaluator;
import android.content.ComponentName;
@@ -35,6 +33,7 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import com.android.internal.jank.Cuj;
import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.icons.IconProvider;
import com.android.quickstep.TaskAnimationManager;
@@ -174,19 +173,19 @@
addAnimatorListener(new AnimationSuccessListener() {
@Override
public void onAnimationStart(Animator animation) {
- InteractionJankMonitorWrapper.begin(view, CUJ_APP_CLOSE_TO_PIP);
+ InteractionJankMonitorWrapper.begin(view, Cuj.CUJ_LAUNCHER_APP_CLOSE_TO_PIP);
super.onAnimationStart(animation);
}
@Override
public void onAnimationCancel(Animator animation) {
super.onAnimationCancel(animation);
- InteractionJankMonitorWrapper.cancel(CUJ_APP_CLOSE_TO_PIP);
+ InteractionJankMonitorWrapper.cancel(Cuj.CUJ_LAUNCHER_APP_CLOSE_TO_PIP);
}
@Override
public void onAnimationSuccess(Animator animator) {
- InteractionJankMonitorWrapper.end(CUJ_APP_CLOSE_TO_PIP);
+ InteractionJankMonitorWrapper.end(Cuj.CUJ_LAUNCHER_APP_CLOSE_TO_PIP);
}
@Override
diff --git a/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java b/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
index 5d8e53e..baaa062 100644
--- a/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
+++ b/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
@@ -427,9 +427,16 @@
// conflict with layers that WM core positions (ie. the input consumers). For shell
// transitions, the animation leashes are reparented to an animation container so we
// can bump layers as needed.
- builder.setLayer(mDrawsBelowRecents
- ? Integer.MIN_VALUE + app.prefixOrderIndex
- : ENABLE_SHELL_TRANSITIONS ? Integer.MAX_VALUE : 0);
+ if (ENABLE_SHELL_TRANSITIONS) {
+ builder.setLayer(mDrawsBelowRecents
+ ? Integer.MIN_VALUE + app.prefixOrderIndex
+ // 1000 is an arbitrary number to give room for multiple layers.
+ : Integer.MAX_VALUE - 1000 + app.prefixOrderIndex);
+ } else {
+ builder.setLayer(mDrawsBelowRecents
+ ? Integer.MIN_VALUE + app.prefixOrderIndex
+ : 0);
+ }
}
}
diff --git a/quickstep/src/com/android/quickstep/util/unfold/LauncherJankMonitorTransitionProgressListener.kt b/quickstep/src/com/android/quickstep/util/unfold/LauncherJankMonitorTransitionProgressListener.kt
index 4f89c7e..b4ca35e 100644
--- a/quickstep/src/com/android/quickstep/util/unfold/LauncherJankMonitorTransitionProgressListener.kt
+++ b/quickstep/src/com/android/quickstep/util/unfold/LauncherJankMonitorTransitionProgressListener.kt
@@ -16,6 +16,7 @@
package com.android.quickstep.util.unfold
import android.view.View
+import com.android.internal.jank.Cuj
import com.android.systemui.shared.system.InteractionJankMonitorWrapper
import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
import java.util.function.Supplier
@@ -28,11 +29,11 @@
override fun onTransitionStarted() {
InteractionJankMonitorWrapper.begin(
attachedViewProvider.get(),
- InteractionJankMonitorWrapper.CUJ_LAUNCHER_UNFOLD_ANIM
+ Cuj.CUJ_LAUNCHER_UNFOLD_ANIM
)
}
override fun onTransitionFinished() {
- InteractionJankMonitorWrapper.end(InteractionJankMonitorWrapper.CUJ_LAUNCHER_UNFOLD_ANIM)
+ InteractionJankMonitorWrapper.end(Cuj.CUJ_LAUNCHER_UNFOLD_ANIM)
}
}
diff --git a/quickstep/src/com/android/quickstep/views/GroupedTaskView.java b/quickstep/src/com/android/quickstep/views/GroupedTaskView.java
index dd201af..2ae64ff 100644
--- a/quickstep/src/com/android/quickstep/views/GroupedTaskView.java
+++ b/quickstep/src/com/android/quickstep/views/GroupedTaskView.java
@@ -17,6 +17,7 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import com.android.internal.jank.Cuj;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
@@ -246,12 +247,11 @@
RunnableList endCallback = new RunnableList();
RecentsView recentsView = getRecentsView();
// Callbacks run from remote animation when recents animation not currently running
- InteractionJankMonitorWrapper.begin(this,
- InteractionJankMonitorWrapper.CUJ_SPLIT_SCREEN_ENTER, "Enter form GroupedTaskView");
+ InteractionJankMonitorWrapper.begin(this, Cuj.CUJ_SPLIT_SCREEN_ENTER,
+ "Enter form GroupedTaskView");
launchTaskInternal(success -> {
endCallback.executeAllAndDestroy();
- InteractionJankMonitorWrapper.end(
- InteractionJankMonitorWrapper.CUJ_SPLIT_SCREEN_ENTER);
+ InteractionJankMonitorWrapper.end(Cuj.CUJ_SPLIT_SCREEN_ENTER);
}, false /* freezeTaskList */, true /*launchingExistingTaskview*/);
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index 87cee63..ca9d13e 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -133,6 +133,7 @@
import androidx.annotation.UiThread;
import androidx.core.graphics.ColorUtils;
+import com.android.internal.jank.Cuj;
import com.android.launcher3.BaseActivity;
import com.android.launcher3.BaseActivity.MultiWindowModeChangedListener;
import com.android.launcher3.DeviceProfile;
@@ -1444,8 +1445,7 @@
mActionsView.updateDisabledFlags(OverviewActionsView.DISABLED_SCROLLING, true);
}
if (mOverviewStateEnabled) { // only when in overview
- InteractionJankMonitorWrapper.begin(/* view= */ this,
- InteractionJankMonitorWrapper.CUJ_RECENTS_SCROLLING);
+ InteractionJankMonitorWrapper.begin(/* view= */ this, Cuj.CUJ_RECENTS_SCROLLING);
}
}
@@ -1460,7 +1460,7 @@
if (getNextPage() > 0) {
setSwipeDownShouldLaunchApp(true);
}
- InteractionJankMonitorWrapper.end(InteractionJankMonitorWrapper.CUJ_RECENTS_SCROLLING);
+ InteractionJankMonitorWrapper.end(Cuj.CUJ_RECENTS_SCROLLING);
}
@Override
@@ -3313,8 +3313,8 @@
timings.getInstructionsUnfoldEndOffset()));
mSplitSelectStateController.setSplitInstructionsView(splitInstructionsView);
- InteractionJankMonitorWrapper.begin(this,
- InteractionJankMonitorWrapper.CUJ_SPLIT_SCREEN_ENTER, "First tile selected");
+ InteractionJankMonitorWrapper.begin(this, Cuj.CUJ_SPLIT_SCREEN_ENTER,
+ "First tile selected");
anim.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
@@ -3330,8 +3330,7 @@
});
anim.addEndListener(success -> {
if (success) {
- InteractionJankMonitorWrapper.end(
- InteractionJankMonitorWrapper.CUJ_SPLIT_SCREEN_ENTER);
+ InteractionJankMonitorWrapper.end(Cuj.CUJ_SPLIT_SCREEN_ENTER);
} else {
// If transition to split select was interrupted, clean up to prevent glitches
if (FeatureFlags.enableSplitContextually()) {
@@ -3339,8 +3338,7 @@
} else {
resetFromSplitSelectionState();
}
- InteractionJankMonitorWrapper.cancel(
- InteractionJankMonitorWrapper.CUJ_SPLIT_SCREEN_ENTER);
+ InteractionJankMonitorWrapper.cancel(Cuj.CUJ_SPLIT_SCREEN_ENTER);
}
updateCurrentTaskActionsVisibility();
@@ -4836,8 +4834,7 @@
} else {
resetFromSplitSelectionState();
}
- InteractionJankMonitorWrapper.end(
- InteractionJankMonitorWrapper.CUJ_SPLIT_SCREEN_ENTER);
+ InteractionJankMonitorWrapper.end(Cuj.CUJ_SPLIT_SCREEN_ENTER);
});
});
@@ -4847,8 +4844,8 @@
mSplitSelectStateController.getSecondTaskId());
}
- InteractionJankMonitorWrapper.begin(this,
- InteractionJankMonitorWrapper.CUJ_SPLIT_SCREEN_ENTER, "Second tile selected");
+ InteractionJankMonitorWrapper.begin(this, Cuj.CUJ_SPLIT_SCREEN_ENTER,
+ "Second tile selected");
// Fade out all other views underneath placeholders
ObjectAnimator tvFade = ObjectAnimator.ofFloat(this, RecentsView.CONTENT_ALPHA,1, 0);
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java
index b42f055..66a880b 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskView.java
@@ -1035,9 +1035,6 @@
anim.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
- if (!recentsView.showAsGrid()) {
- return;
- }
recentsView.runActionOnRemoteHandles(
(Consumer<RemoteTargetHandle>) remoteTargetHandle ->
remoteTargetHandle
diff --git a/res/values-da/strings.xml b/res/values-da/strings.xml
index 9d5a55f..415d1f3 100644
--- a/res/values-da/strings.xml
+++ b/res/values-da/strings.xml
@@ -173,10 +173,10 @@
<string name="work_apps_enable_btn_text" msgid="1736198302467317371">"Genoptag"</string>
<string name="developer_options_filter_hint" msgid="5896817443635989056">"Filter"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Mislykket: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
- <string name="private_space_label" msgid="2359721649407947001">"Privat rum"</string>
+ <string name="private_space_label" msgid="2359721649407947001">"Privat område"</string>
<string name="ps_container_title" msgid="4391796149519594205">"Privat"</string>
<string name="ps_container_settings" msgid="6059734123353320479">"Indstillinger for privat rum"</string>
- <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Lås/oplås det private rum"</string>
- <string name="ps_container_transition" msgid="8667331812048014412">"Ændringer af tilstanden for det private rum"</string>
+ <string name="ps_container_lock_unlock_button" msgid="7605602332253423755">"Lås/oplås det private område"</string>
+ <string name="ps_container_transition" msgid="8667331812048014412">"Ændringer af tilstanden for det private område"</string>
<string name="bubble_bar_overflow_description" msgid="7410995531938041192">"Overløb"</string>
</resources>
diff --git a/res/values/config.xml b/res/values/config.xml
index 154312a..2980635 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -86,6 +86,7 @@
<string name="widget_holder_factory_class" translatable="false"></string>
<string name="taskbar_search_session_controller_class" translatable="false"></string>
<string name="taskbar_model_callbacks_factory_class" translatable="false"></string>
+ <string name="launcher_restore_event_logger_class" translatable="false"></string>
<!-- View ID to use for QSB widget -->
<item type="id" name="qsb_widget" />
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index e2e528c..91da7e6 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -175,6 +175,7 @@
@ViewDebug.ExportedProperty(category = "launcher")
private DotInfo mDotInfo;
private DotRenderer mDotRenderer;
+ private Locale mCurrentLocale;
@ViewDebug.ExportedProperty(category = "launcher", deepExport = true)
protected DotRenderer.DrawParams mDotParams;
private Animator mDotScaleAnim;
@@ -250,6 +251,7 @@
mDotParams = new DotRenderer.DrawParams();
+ mCurrentLocale = context.getResources().getConfiguration().locale;
setEllipsize(TruncateAt.END);
setAccessibilityDelegate(mActivity.getAccessibilityDelegate());
setTextAlpha(1f);
@@ -411,10 +413,12 @@
* Only if actual text can be displayed in two line, the {@code true} value will be effective.
*/
protected boolean shouldUseTwoLine() {
- return ((FeatureFlags.enableTwolineAllapps())
- && (mDisplay == DISPLAY_ALL_APPS || mDisplay == DISPLAY_PREDICTION_ROW))
- || (FeatureFlags.ENABLE_TWOLINE_DEVICESEARCH.get()
- && mDisplay == DISPLAY_SEARCH_RESULT);
+ return (FeatureFlags.enableTwolineAllapps() && isCurrentLanguageEnglish())
+ && (mDisplay == DISPLAY_ALL_APPS || mDisplay == DISPLAY_PREDICTION_ROW);
+ }
+
+ protected boolean isCurrentLanguageEnglish() {
+ return mCurrentLocale.equals(Locale.US);
}
@UiThread
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index b6e8ec3..1ca7da9 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -356,7 +356,7 @@
final Resources res = context.getResources();
mMetrics = res.getDisplayMetrics();
- mIconSizeSteps = mIsResponsiveGrid ? new IconSizeSteps(res) : null;
+ mIconSizeSteps = new IconSizeSteps(res);
// Determine sizes.
widthPx = windowBounds.bounds.width();
@@ -1486,6 +1486,17 @@
folderCellWidthPx = roundPxValueFromFloat(folderCellWidthPx * scale);
folderCellHeightPx = roundPxValueFromFloat(folderCellHeightPx * scale);
}
+ // Recalculating padding and cell height
+ folderChildDrawablePaddingPx = getNormalizedFolderChildDrawablePaddingPx(textHeight);
+
+ CellContentDimensions cellContentDimensions = new CellContentDimensions(
+ folderChildIconSizePx,
+ folderChildDrawablePaddingPx,
+ folderChildTextSizePx);
+ cellContentDimensions.resizeToFitCellHeight(folderCellHeightPx, mIconSizeSteps);
+ folderChildIconSizePx = cellContentDimensions.getIconSizePx();
+ folderChildDrawablePaddingPx = cellContentDimensions.getIconDrawablePaddingPx();
+ folderChildTextSizePx = cellContentDimensions.getIconTextSizePx();
folderContentPaddingTop = roundPxValueFromFloat(folderContentPaddingTop * scale);
folderCellLayoutBorderSpacePx = new Point(
@@ -1493,10 +1504,7 @@
roundPxValueFromFloat(folderCellLayoutBorderSpacePx.y * scale)
);
folderFooterHeightPx = roundPxValueFromFloat(folderFooterHeightPx * scale);
-
folderContentPaddingLeftRight = folderCellLayoutBorderSpacePx.x;
-
- folderChildDrawablePaddingPx = getNormalizedFolderChildDrawablePaddingPx(textHeight);
} else {
int cellPaddingX = (int) (res.getDimensionPixelSize(R.dimen.folder_cell_x_padding)
* scale);
diff --git a/src/com/android/launcher3/backuprestore/LauncherRestoreEventLogger.kt b/src/com/android/launcher3/backuprestore/LauncherRestoreEventLogger.kt
new file mode 100644
index 0000000..16b1854
--- /dev/null
+++ b/src/com/android/launcher3/backuprestore/LauncherRestoreEventLogger.kt
@@ -0,0 +1,84 @@
+package com.android.launcher3.backuprestore
+
+import android.content.Context
+import com.android.launcher3.LauncherSettings.Favorites
+import com.android.launcher3.R
+import com.android.launcher3.util.ResourceBasedOverride
+
+/**
+ * Wrapper for logging Restore event metrics for both success and failure to restore the Launcher
+ * workspace from a backup.
+ */
+open class LauncherRestoreEventLogger : ResourceBasedOverride {
+
+ companion object {
+ const val TAG = "LauncherRestoreEventLogger"
+
+ fun newInstance(context: Context?): LauncherRestoreEventLogger {
+ return ResourceBasedOverride.Overrides.getObject(
+ LauncherRestoreEventLogger::class.java,
+ context,
+ R.string.launcher_restore_event_logger_class
+ )
+ }
+ }
+
+ /**
+ * For logging when multiple items of a given data type failed to restore.
+ *
+ * @param dataType The data type that was not restored.
+ * @param count the number of data items that were not restored.
+ * @param error error type for why the data was not restored.
+ */
+ open fun logLauncherItemsRestoreFailed(dataType: String, count: Int, error: String?) {
+ // no-op
+ }
+
+ /**
+ * For logging when multiple items of a given data type were successfully restored.
+ *
+ * @param dataType The data type that was restored.
+ * @param count the number of data items restored.
+ */
+ open fun logLauncherItemsRestored(dataType: String, count: Int) {
+ // no-op
+ }
+
+ /**
+ * Helper to log successfully restoring a single item from the Favorites table.
+ *
+ * @param favoritesId The id of the item type from [Favorites] that was restored.
+ */
+ open fun logSingleFavoritesItemRestored(favoritesId: Int) {
+ // no-op
+ }
+
+ /**
+ * Helper to log a failure to restore a single item from the Favorites table.
+ *
+ * @param favoritesId The id of the item type from [Favorites] that was not restored.
+ * @param error error type for why the data was not restored.
+ */
+ open fun logSingleFavoritesItemRestoreFailed(favoritesId: Int, error: String?) {
+ // no-op
+ }
+
+ /**
+ * Helper to log a failure to restore items from the Favorites table.
+ *
+ * @param favoritesId The id of the item type from [Favorites] that was not restored.
+ * @param count number of items that failed to restore.
+ * @param error error type for why the data was not restored.
+ */
+ open fun logFavoritesItemsRestoreFailed(favoritesId: Int, count: Int, error: String?) {
+ // no-op
+ }
+
+ /**
+ * Uses the current [restoreEventLogger] to report its results to the [backupManager]. Use when
+ * done restoring items for Launcher.
+ */
+ open fun reportLauncherRestoreResults() {
+ // no-op
+ }
+}
diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index 2f62840..7dfce56 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -201,10 +201,6 @@
"ENABLE_PARAMETRIZE_REORDER", DISABLED,
"Enables generating the reorder using a set of parameters");
- public static final BooleanFlag ENABLE_NO_LONG_PRESS_DRAG = getDebugFlag(299748096,
- "ENABLE_NO_LONG_PRESS_DRAG", ENABLED,
- "Don't trigger the drag if we are still under long press");
-
// TODO(Block 12): Clean up flags
public static final BooleanFlag ENABLE_MULTI_INSTANCE = getDebugFlag(270396680,
"ENABLE_MULTI_INSTANCE", DISABLED,
@@ -308,7 +304,7 @@
"Long press of nav handle is instantly triggered if deep press is detected.");
public static final IntFlag LPNH_HAPTIC_HINT_DELAY =
- getIntFlag(309972570, "LPNH_HAPTIC_HINT_ITERATIONS", 0,
+ getIntFlag(309972570, "LPNH_HAPTIC_HINT_DELAY", 0,
"Delay before haptic hint starts.");
// TODO(Block 17): Clean up flags
diff --git a/src/com/android/launcher3/dragndrop/DragController.java b/src/com/android/launcher3/dragndrop/DragController.java
index 777f4d5..aa5329b 100644
--- a/src/com/android/launcher3/dragndrop/DragController.java
+++ b/src/com/android/launcher3/dragndrop/DragController.java
@@ -17,7 +17,6 @@
package com.android.launcher3.dragndrop;
import static com.android.launcher3.Utilities.ATLEAST_Q;
-import static com.android.launcher3.config.FeatureFlags.ENABLE_NO_LONG_PRESS_DRAG;
import android.graphics.Point;
import android.graphics.Rect;
@@ -464,7 +463,7 @@
private DropTarget checkTouchMove(final int x, final int y) {
// If we are in predrag, don't trigger any other event until we get out of it
- if (ENABLE_NO_LONG_PRESS_DRAG.get() && mIsInPreDrag) {
+ if (mIsInPreDrag) {
return mLastDropTarget;
}
DropTarget dropTarget = findDropTarget(x, y);
diff --git a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
index 3330448..7dcc8a8 100644
--- a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
+++ b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
@@ -35,6 +35,7 @@
import android.appwidget.AppWidgetProviderInfo;
import android.content.Context;
import android.content.ContextWrapper;
+import android.content.res.Configuration;
import android.content.res.TypedArray;
import android.graphics.PointF;
import android.graphics.Rect;
@@ -197,7 +198,7 @@
mUiHandler = new Handler(Looper.getMainLooper());
mContext = context;
mIdp = idp;
- mDp = idp.getDeviceProfile(context).toBuilder(context).setViewScaleProvider(
+ mDp = getDeviceProfileForPreview(context).toBuilder(context).setViewScaleProvider(
this::getAppWidgetScale).build();
if (context instanceof PreviewContext) {
Context tempContext = ((PreviewContext) context).getBaseContext();
@@ -260,6 +261,21 @@
}
/**
+ * Returns the device profile based on resource configuration for previewing various display
+ * sizes
+ */
+ private DeviceProfile getDeviceProfileForPreview(Context context) {
+ float density = context.getResources().getDisplayMetrics().density;
+ Configuration config = context.getResources().getConfiguration();
+
+ return mIdp.getBestMatch(
+ config.screenWidthDp * density,
+ config.screenHeightDp * density,
+ WindowManagerProxy.INSTANCE.get(context).getRotation(context)
+ );
+ }
+
+ /**
* Returns the insets of the screen closest to the display given by the context
*/
private Rect getInsets(Context context) {
diff --git a/src/com/android/launcher3/recyclerview/AllAppsRecyclerViewPool.kt b/src/com/android/launcher3/recyclerview/AllAppsRecyclerViewPool.kt
index 45174a7..cbc6f44 100644
--- a/src/com/android/launcher3/recyclerview/AllAppsRecyclerViewPool.kt
+++ b/src/com/android/launcher3/recyclerview/AllAppsRecyclerViewPool.kt
@@ -23,10 +23,10 @@
import com.android.launcher3.BubbleTextView
import com.android.launcher3.allapps.BaseAllAppsAdapter
import com.android.launcher3.config.FeatureFlags
+import com.android.launcher3.util.ExecutorRunnable
import com.android.launcher3.util.Executors.MAIN_EXECUTOR
import com.android.launcher3.util.Executors.VIEW_PREINFLATION_EXECUTOR
import com.android.launcher3.views.ActivityContext
-import java.util.concurrent.Future
const val PREINFLATE_ICONS_ROW_COUNT = 4
const val EXTRA_ICONS_COUNT = 2
@@ -38,9 +38,8 @@
*/
class AllAppsRecyclerViewPool<T> : RecycledViewPool() {
- private var future: Future<Void>? = null
-
var hasWorkProfile = false
+ var executorRunnable: ExecutorRunnable<List<ViewHolder>>? = null
/**
* Preinflate app icons. If all apps RV cannot be scrolled down, we don't need to preinflate.
@@ -63,21 +62,38 @@
override fun getLayoutManager(): RecyclerView.LayoutManager? = null
}
- // Inflate view holders on background thread, and added to view pool on main thread.
- future?.cancel(true)
- future =
- VIEW_PREINFLATION_EXECUTOR.submit<Void> {
- val viewHolders =
- Array(preInflateCount) {
- adapter.createViewHolder(activeRv, BaseAllAppsAdapter.VIEW_TYPE_ICON)
+ executorRunnable?.cancel(/* interrupt= */ true)
+ executorRunnable =
+ ExecutorRunnable.createAndExecute(
+ VIEW_PREINFLATION_EXECUTOR,
+ {
+ val list: ArrayList<ViewHolder> = ArrayList()
+ for (i in 0 until preInflateCount) {
+ if (Thread.interrupted()) {
+ break
+ }
+ list.add(
+ adapter.createViewHolder(activeRv, BaseAllAppsAdapter.VIEW_TYPE_ICON)
+ )
}
- MAIN_EXECUTOR.execute {
+ list
+ },
+ MAIN_EXECUTOR,
+ { viewHolders ->
for (i in 0 until minOf(viewHolders.size, getPreinflateCount(context))) {
putRecycledView(viewHolders[i])
}
}
- null
- }
+ )
+ }
+
+ /**
+ * When clearing [RecycledViewPool], we should also abort pre-inflation tasks. This will make
+ * sure we don't inflate app icons after DeviceProfile has changed.
+ */
+ override fun clear() {
+ super.clear()
+ executorRunnable?.cancel(/* interrupt= */ true)
}
/**
diff --git a/src/com/android/launcher3/util/ExecutorRunnable.kt b/src/com/android/launcher3/util/ExecutorRunnable.kt
new file mode 100644
index 0000000..49cf592
--- /dev/null
+++ b/src/com/android/launcher3/util/ExecutorRunnable.kt
@@ -0,0 +1,78 @@
+/*
+ * 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.launcher3.util
+
+import java.util.concurrent.Executor
+import java.util.concurrent.ExecutorService
+import java.util.concurrent.Future
+import java.util.function.Consumer
+import java.util.function.Supplier
+
+/** A [Runnable] that can be posted to a [Executor] that can be cancelled. */
+class ExecutorRunnable<T>
+private constructor(
+ private val task: Supplier<T>,
+ // Executor where consumer needs to be executed on. Typically UI executor.
+ private val callbackExecutor: Executor,
+ // Consumer that needs to be accepted upon completion of the task. Typically work that needs to
+ // be done in UI thread after task completes.
+ private val callback: Consumer<T>
+) : Runnable {
+
+ // future of this runnable that will used for cancellation.
+ lateinit var future: Future<*>
+
+ // flag to cancel the callback
+ var canceled = false
+
+ override fun run() {
+ val value: T = task.get()
+ callbackExecutor.execute {
+ if (!canceled) {
+ callback.accept(value)
+ }
+ }
+ }
+
+ /**
+ * Cancel the [ExecutorRunnable] if not scheduled. If [ExecutorRunnable] has started execution
+ * at this time, we will try to cancel the callback if not executed yet.
+ */
+ fun cancel(interrupt: Boolean) {
+ future.cancel(interrupt)
+ canceled = true
+ }
+
+ companion object {
+ /**
+ * Create [ExecutorRunnable] and execute it on task [Executor]. It will also save the
+ * [Future] into this [ExecutorRunnable] to be used for cancellation.
+ */
+ fun <T> createAndExecute(
+ // Executor where task will be executed, typically an Executor running on background
+ // thread.
+ taskExecutor: ExecutorService,
+ task: Supplier<T>,
+ callbackExecutor: Executor,
+ callback: Consumer<T>
+ ): ExecutorRunnable<T> {
+ val executorRunnable = ExecutorRunnable(task, callbackExecutor, callback)
+ executorRunnable.future = taskExecutor.submit(executorRunnable)
+ return executorRunnable
+ }
+ }
+}
diff --git a/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java b/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java
index 1aa49c7..5d069ed 100644
--- a/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java
+++ b/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java
@@ -19,7 +19,6 @@
import android.annotation.TargetApi;
import android.appwidget.AppWidgetProviderInfo;
import android.content.Context;
-import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.Build;
import android.os.Handler;
@@ -32,7 +31,6 @@
import android.util.SparseIntArray;
import android.view.MotionEvent;
import android.view.View;
-import android.view.ViewDebug;
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.AdapterView;
@@ -79,9 +77,6 @@
private final CheckLongPressHelper mLongPressHelper;
protected final Launcher mLauncher;
- @ViewDebug.ExportedProperty(category = "launcher")
- private boolean mReinflateOnConfigChange;
-
// Maintain the color manager.
private final LocalColorExtractor mColorExtractor;
@@ -176,17 +171,6 @@
// The provider info or the views might have changed.
checkIfAutoAdvance();
-
- // It is possible that widgets can receive updates while launcher is not in the foreground.
- // Consequently, the widgets will be inflated for the orientation of the foreground activity
- // (framework issue). On resuming, we ensure that any widgets are inflated for the current
- // orientation.
- mReinflateOnConfigChange = !isSameOrientation();
- }
-
- private boolean isSameOrientation() {
- return mLauncher.getResources().getConfiguration().orientation ==
- mLauncher.getOrientation();
}
private boolean checkScrollableRecursively(ViewGroup viewGroup) {
@@ -450,17 +434,6 @@
scheduleNextAdvance();
}
- @Override
- protected void onConfigurationChanged(Configuration newConfig) {
- super.onConfigurationChanged(newConfig);
-
- // Only reinflate when the final configuration is same as the required configuration
- if (mReinflateOnConfigChange && isSameOrientation()) {
- mReinflateOnConfigChange = false;
- reInflate();
- }
- }
-
public void reInflate() {
if (!isAttachedToWindow()) {
return;
diff --git a/tests/src/com/android/launcher3/dragging/TaplUninstallRemove.java b/tests/src/com/android/launcher3/dragging/TaplUninstallRemove.java
index ed34307..568fc9f 100644
--- a/tests/src/com/android/launcher3/dragging/TaplUninstallRemove.java
+++ b/tests/src/com/android/launcher3/dragging/TaplUninstallRemove.java
@@ -166,9 +166,11 @@
mLauncher.getWorkspace().verifyWorkspaceAppIconIsGone(
DUMMY_APP_NAME + " was expected to disappear after uninstall.", DUMMY_APP_NAME);
- Map<String, Point> finalPositions =
- mLauncher.getWorkspace().getWorkspaceIconsPositions();
- assertThat(finalPositions).doesNotContainKey(DUMMY_APP_NAME);
+ if (!TestStabilityRule.isPresubmit()) { // b/315847371
+ Map<String, Point> finalPositions =
+ mLauncher.getWorkspace().getWorkspaceIconsPositions();
+ assertThat(finalPositions).doesNotContainKey(DUMMY_APP_NAME);
+ }
} finally {
TestUtil.uninstallDummyApp();
}
diff --git a/tests/src/com/android/launcher3/util/ExecutorRunnableTest.kt b/tests/src/com/android/launcher3/util/ExecutorRunnableTest.kt
new file mode 100644
index 0000000..b4591ba
--- /dev/null
+++ b/tests/src/com/android/launcher3/util/ExecutorRunnableTest.kt
@@ -0,0 +1,101 @@
+/*
+ * 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.launcher3.util
+
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import java.util.concurrent.ExecutorService
+import junit.framework.Assert.assertEquals
+import junit.framework.Assert.assertFalse
+import junit.framework.Assert.assertTrue
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/** Unit test for [ExecutorRunnable] */
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class ExecutorRunnableTest {
+
+ private lateinit var underTest: ExecutorRunnable<Int>
+
+ private var result: Int = -1
+ private var isTaskExecuted = false
+ private var isCallbackExecuted = false
+
+ @Before
+ fun setup() {
+ reset()
+ underTest =
+ ExecutorRunnable.createAndExecute(
+ Executors.UI_HELPER_EXECUTOR,
+ {
+ isTaskExecuted = true
+ 1
+ },
+ Executors.VIEW_PREINFLATION_EXECUTOR,
+ {
+ isCallbackExecuted = true
+ result = it + 1
+ }
+ )
+ }
+
+ @Test
+ fun run_and_complete() {
+ awaitAllExecutorCompleted()
+
+ assertTrue(isTaskExecuted)
+ assertTrue(isCallbackExecuted)
+ assertEquals(2, result)
+ }
+
+ @Test
+ fun run_and_cancel_cancelCallback() {
+ underTest.cancel(true)
+ awaitAllExecutorCompleted()
+
+ assertFalse(isCallbackExecuted)
+ assertEquals(0, result)
+ }
+
+ @Test
+ fun run_and_cancelAfterCompletion_executeAll() {
+ awaitAllExecutorCompleted()
+
+ underTest.cancel(true)
+
+ assertTrue(isTaskExecuted)
+ assertTrue(isCallbackExecuted)
+ assertEquals(2, result)
+ }
+
+ private fun awaitExecutorCompleted(executor: ExecutorService) {
+ executor.submit<Any> { null }.get()
+ }
+
+ private fun awaitAllExecutorCompleted() {
+ awaitExecutorCompleted(Executors.UI_HELPER_EXECUTOR)
+ awaitExecutorCompleted(Executors.VIEW_PREINFLATION_EXECUTOR)
+ }
+
+ private fun reset() {
+ result = 0
+ isTaskExecuted = false
+ isCallbackExecuted = false
+ }
+}