Merge "Fix bug with app pair 10:90 launch" into main
diff --git a/quickstep/res/values-ar/strings.xml b/quickstep/res/values-ar/strings.xml
index a866bb9..281125d 100644
--- a/quickstep/res/values-ar/strings.xml
+++ b/quickstep/res/values-ar/strings.xml
@@ -46,8 +46,7 @@
<string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"تم تفعيل ميزة \"التطبيقات المقترحة\"."</string>
<string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"ميزة \"التطبيقات المقترحة\" غير مفعّلة."</string>
<string name="hotseat_prediction_content_description" msgid="4582028296938078419">"التطبيق المتوقع: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
- <!-- no translation found for gesture_tutorial_title (2750751261768388354) -->
- <skip />
+ <string name="gesture_tutorial_title" msgid="2750751261768388354">"دليل توجيهي للتنقُّل بالإيماءات"</string>
<string name="gesture_tutorial_rotation_prompt_title" msgid="7537946781362766964">"يُرجى تدوير الجهاز"</string>
<string name="gesture_tutorial_rotation_prompt" msgid="1664493449851960691">"يُرجى تدوير جهازك لإكمال الدليل التوجيهي للتنقُّل بالإيماءات."</string>
<string name="back_gesture_feedback_swipe_too_far_from_edge" msgid="4175100312909721217">"تأكَّد من التمرير سريعًا من أقصى الحافة اليسرى أو اليمنى."</string>
diff --git a/quickstep/res/values-et/strings.xml b/quickstep/res/values-et/strings.xml
index bb56954..0af4d9c 100644
--- a/quickstep/res/values-et/strings.xml
+++ b/quickstep/res/values-et/strings.xml
@@ -89,7 +89,7 @@
<string name="allset_title" msgid="5021126669778966707">"Valmis!"</string>
<string name="allset_hint" msgid="459504134589971527">"Avalehele liikumiseks pühkige üles"</string>
<string name="allset_button_hint" msgid="2395219947744706291">"Avakuvale liikumiseks puudutage avakuva nuppu"</string>
- <string name="allset_description_generic" msgid="5385500062202019855">"<xliff:g id="DEVICE">%1$s</xliff:g> on nüüd kasutamiseks valmis"</string>
+ <string name="allset_description_generic" msgid="5385500062202019855">"Teie <xliff:g id="DEVICE">%1$s</xliff:g> on nüüd kasutamiseks valmis."</string>
<string name="default_device_name" msgid="6660656727127422487">"seade"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Süsteemi navigeerimisseaded"</annotation></string>
<string name="action_share" msgid="2648470652637092375">"Jaga"</string>
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
index 17fb959..36185b1 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
@@ -115,9 +115,8 @@
private static final Uri NAV_BAR_KIDS_MODE = Settings.Secure.getUriFor(
Settings.Secure.NAV_BAR_KIDS_MODE);
- private final Context mWindowContext;
+ private final Context mParentContext;
private final @Nullable Context mNavigationBarPanelContext;
- private WindowManager mWindowManager;
private final TaskbarNavButtonController mDefaultNavButtonController;
private final ComponentCallbacks mDefaultComponentCallbacks;
@@ -132,6 +131,8 @@
new NonDestroyableScopedUnfoldTransitionProgressProvider();
/** DisplayId - {@link TaskbarActivityContext} map for Connected Display. */
private final SparseArray<TaskbarActivityContext> mTaskbars = new SparseArray<>();
+ /** DisplayId - {@link Context} map for Connected Display. */
+ private final SparseArray<Context> mWindowContexts = new SparseArray<>();
/** DisplayId - {@link FrameLayout} map for Connected Display. */
private final SparseArray<FrameLayout> mRootLayouts = new SparseArray<>();
/** DisplayId - {@link Boolean} map indicating if RootLayout was added to window. */
@@ -243,36 +244,35 @@
Context context,
AllAppsActionManager allAppsActionManager,
TaskbarNavButtonCallbacks navCallbacks) {
- Display display =
- context.getSystemService(DisplayManager.class).getDisplay(context.getDisplayId());
- mWindowContext = context.createWindowContext(display,
- ENABLE_TASKBAR_NAVBAR_UNIFICATION ? TYPE_NAVIGATION_BAR : TYPE_NAVIGATION_BAR_PANEL,
- null);
+ mParentContext = context;
+ createWindowContext(context.getDisplayId());
mAllAppsActionManager = allAppsActionManager;
+ Display display = context.getSystemService(DisplayManager.class).getDisplay(
+ getDefaultDisplayId());
mNavigationBarPanelContext = ENABLE_TASKBAR_NAVBAR_UNIFICATION
? context.createWindowContext(display, TYPE_NAVIGATION_BAR_PANEL, null)
: null;
if (enableTaskbarNoRecreate()) {
- mWindowManager = mWindowContext.getSystemService(WindowManager.class);
createTaskbarRootLayout(getDefaultDisplayId());
}
mDefaultNavButtonController = createDefaultNavButtonController(context, navCallbacks);
mDefaultComponentCallbacks = createDefaultComponentCallbacks();
- SettingsCache.INSTANCE.get(mWindowContext)
+ SettingsCache.INSTANCE.get(getPrimaryWindowContext())
.register(USER_SETUP_COMPLETE_URI, mOnSettingsChangeListener);
- SettingsCache.INSTANCE.get(mWindowContext)
+ SettingsCache.INSTANCE.get(getPrimaryWindowContext())
.register(NAV_BAR_KIDS_MODE, mOnSettingsChangeListener);
Log.d(TASKBAR_NOT_DESTROYED_TAG, "registering component callbacks from constructor.");
- mWindowContext.registerComponentCallbacks(mDefaultComponentCallbacks);
- mShutdownReceiver.register(mWindowContext, Intent.ACTION_SHUTDOWN);
+ getPrimaryWindowContext().registerComponentCallbacks(mDefaultComponentCallbacks);
+ mShutdownReceiver.register(getPrimaryWindowContext(), Intent.ACTION_SHUTDOWN);
UI_HELPER_EXECUTOR.execute(() -> {
mSharedState.taskbarSystemActionPendingIntent = PendingIntent.getBroadcast(
- mWindowContext,
+ getPrimaryWindowContext(),
SYSTEM_ACTION_ID_TASKBAR,
- new Intent(ACTION_SHOW_TASKBAR).setPackage(mWindowContext.getPackageName()),
+ new Intent(ACTION_SHOW_TASKBAR).setPackage(
+ getPrimaryWindowContext().getPackageName()),
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
mTaskbarBroadcastReceiver.register(
- mWindowContext, RECEIVER_NOT_EXPORTED, ACTION_SHOW_TASKBAR);
+ getPrimaryWindowContext(), RECEIVER_NOT_EXPORTED, ACTION_SHOW_TASKBAR);
});
debugWhyTaskbarNotDestroyed("TaskbarManager created");
@@ -285,14 +285,15 @@
return new TaskbarNavButtonController(
context,
navCallbacks,
- SystemUiProxy.INSTANCE.get(mWindowContext),
+ SystemUiProxy.INSTANCE.get(getPrimaryWindowContext()),
new Handler(),
- new ContextualSearchInvoker(mWindowContext));
+ new ContextualSearchInvoker(getPrimaryWindowContext()));
}
private ComponentCallbacks createDefaultComponentCallbacks() {
return new ComponentCallbacks() {
- private Configuration mOldConfig = mWindowContext.getResources().getConfiguration();
+ private Configuration mOldConfig =
+ getPrimaryWindowContext().getResources().getConfiguration();
@Override
public void onConfigurationChanged(Configuration newConfig) {
@@ -302,7 +303,8 @@
"TaskbarManager#mComponentCallbacks.onConfigurationChanged: " + newConfig);
// TODO: adapt this logic to be specific to different displays.
DeviceProfile dp = mUserUnlocked
- ? LauncherAppState.getIDP(mWindowContext).getDeviceProfile(mWindowContext)
+ ? LauncherAppState.getIDP(getPrimaryWindowContext()).getDeviceProfile(
+ getPrimaryWindowContext())
: null;
int configDiff = mOldConfig.diff(newConfig) & ~SKIP_RECREATE_CONFIG_CHANGES;
@@ -354,6 +356,7 @@
int displayId = mTaskbars.keyAt(i);
destroyTaskbarForDisplay(displayId);
removeTaskbarRootViewFromWindow(displayId);
+ removeWindowContextFromMap(displayId);
}
}
@@ -372,7 +375,8 @@
}
// make this display-specific
DeviceProfile dp = mUserUnlocked ?
- LauncherAppState.getIDP(mWindowContext).getDeviceProfile(mWindowContext) : null;
+ LauncherAppState.getIDP(getWindowContext(displayId)).getDeviceProfile(
+ getWindowContext(displayId)) : null;
if (dp == null || !isTaskbarEnabled(dp)) {
removeTaskbarRootViewFromWindow(displayId);
}
@@ -419,7 +423,8 @@
*/
public void onUserUnlocked() {
mUserUnlocked = true;
- DisplayController.INSTANCE.get(mWindowContext).addChangeListener(mRecreationListener);
+ DisplayController.INSTANCE.get(getPrimaryWindowContext()).addChangeListener(
+ mRecreationListener);
recreateTaskbar();
addTaskbarRootViewToWindow(getDefaultDisplayId());
}
@@ -482,7 +487,8 @@
return ql.getUnfoldTransitionProgressProvider();
}
} else {
- return SystemUiProxy.INSTANCE.get(mWindowContext).getUnfoldTransitionProvider();
+ return SystemUiProxy.INSTANCE.get(
+ getPrimaryWindowContext()).getUnfoldTransitionProvider();
}
return null;
}
@@ -528,7 +534,8 @@
Log.d(ILLEGAL_ARGUMENT_WM_ADD_VIEW, "recreateTaskbarForDisplay: " + displayId);
// TODO: make this code display specific
DeviceProfile dp = mUserUnlocked ?
- LauncherAppState.getIDP(mWindowContext).getDeviceProfile(mWindowContext) : null;
+ LauncherAppState.getIDP(getWindowContext(displayId)).getDeviceProfile(
+ getWindowContext(displayId)) : null;
// All Apps action is unrelated to navbar unification, so we only need to check DP.
final boolean isLargeScreenTaskbar = dp != null && dp.isTaskbarPresent;
@@ -542,7 +549,7 @@
+ " FLAG_HIDE_NAVBAR_WINDOW=" + ENABLE_TASKBAR_NAVBAR_UNIFICATION
+ " dp.isTaskbarPresent=" + (dp == null ? "null" : dp.isTaskbarPresent));
if (!isTaskbarEnabled || !isLargeScreenTaskbar) {
- SystemUiProxy.INSTANCE.get(mWindowContext)
+ SystemUiProxy.INSTANCE.get(getPrimaryWindowContext())
.notifyTaskbarStatus(/* visible */ false, /* stashed */ false);
if (!isTaskbarEnabled) {
return;
@@ -754,23 +761,24 @@
mRecentsViewContainer = null;
debugWhyTaskbarNotDestroyed("TaskbarManager#destroy()");
removeActivityCallbacksAndListeners();
- mTaskbarBroadcastReceiver.unregisterReceiverSafely(mWindowContext);
- destroyAllTaskbars();
+ mTaskbarBroadcastReceiver.unregisterReceiverSafely(getPrimaryWindowContext());
+
if (mUserUnlocked) {
- DisplayController.INSTANCE.get(mWindowContext).removeChangeListener(
+ DisplayController.INSTANCE.get(getPrimaryWindowContext()).removeChangeListener(
mRecreationListener);
}
- SettingsCache.INSTANCE.get(mWindowContext)
+ SettingsCache.INSTANCE.get(getPrimaryWindowContext())
.unregister(USER_SETUP_COMPLETE_URI, mOnSettingsChangeListener);
- SettingsCache.INSTANCE.get(mWindowContext)
+ SettingsCache.INSTANCE.get(getPrimaryWindowContext())
.unregister(NAV_BAR_KIDS_MODE, mOnSettingsChangeListener);
Log.d(TASKBAR_NOT_DESTROYED_TAG, "unregistering component callbacks from destroy().");
- mWindowContext.unregisterComponentCallbacks(mDefaultComponentCallbacks);
- mShutdownReceiver.unregisterReceiverSafely(mWindowContext);
+ getPrimaryWindowContext().unregisterComponentCallbacks(mDefaultComponentCallbacks);
+ mShutdownReceiver.unregisterReceiverSafely(getPrimaryWindowContext());
+ destroyAllTaskbars();
}
public @Nullable TaskbarActivityContext getCurrentActivityContext() {
- return getTaskbarForDisplay(mWindowContext.getDisplayId());
+ return getTaskbarForDisplay(getDefaultDisplayId());
}
public void dumpLogs(String prefix, PrintWriter pw) {
@@ -786,7 +794,6 @@
taskbar.dumpLogs(prefix + "\t\t", pw);
}
}
-
}
private void addTaskbarRootViewToWindow(int displayId) {
@@ -800,8 +807,7 @@
if (!isTaskbarRootLayoutAddedForDisplay(displayId)) {
FrameLayout rootLayout = getTaskbarRootLayoutForDisplay(displayId);
if (rootLayout != null) {
- mWindowManager.addView(getTaskbarRootLayoutForDisplay(displayId),
- taskbar.getWindowLayoutParams());
+ getWindowManager(displayId).addView(rootLayout, taskbar.getWindowLayoutParams());
mAddedRootLayouts.put(displayId, true);
} else {
Log.d(ILLEGAL_ARGUMENT_WM_ADD_VIEW,
@@ -822,7 +828,7 @@
}
if (isTaskbarRootLayoutAddedForDisplay(displayId)) {
- mWindowManager.removeViewImmediate(rootLayout);
+ getWindowManager(displayId).removeViewImmediate(rootLayout);
mAddedRootLayouts.put(displayId, false);
removeTaskbarRootLayoutFromMap(displayId);
}
@@ -855,10 +861,10 @@
* Creates a {@link TaskbarActivityContext} for the given display and adds it to the map.
*/
private TaskbarActivityContext createTaskbarActivityContext(DeviceProfile dp, int displayId) {
- TaskbarActivityContext newTaskbar = new TaskbarActivityContext(mWindowContext,
+ TaskbarActivityContext newTaskbar = new TaskbarActivityContext(getWindowContext(displayId),
mNavigationBarPanelContext, dp, mDefaultNavButtonController,
mUnfoldProgressProvider, isDefaultDisplay(displayId),
- SystemUiProxy.INSTANCE.get(mWindowContext));
+ SystemUiProxy.INSTANCE.get(getPrimaryWindowContext()));
addTaskbarToMap(displayId, newTaskbar);
return newTaskbar;
@@ -892,7 +898,7 @@
*/
private void createTaskbarRootLayout(int displayId) {
Log.d(ILLEGAL_ARGUMENT_WM_ADD_VIEW, "createTaskbarRootLayout: " + displayId);
- FrameLayout newTaskbarRootLayout = new FrameLayout(mWindowContext) {
+ FrameLayout newTaskbarRootLayout = new FrameLayout(getWindowContext(displayId)) {
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
// The motion events can be outside the view bounds of task bar, and hence
@@ -958,8 +964,75 @@
Log.d(NULL_TASKBAR_ROOT_LAYOUT_TAG, "mRootLayouts.size()=" + mRootLayouts.size());
}
+ /**
+ * Creates {@link Context} for the taskbar on the specified display and›› adds it to map.
+ * @param displayId The ID of the display for which to create the window context.
+ */
+ private void createWindowContext(int displayId) {
+ DisplayManager displayManager = mParentContext.getSystemService(DisplayManager.class);
+ if (displayManager == null) {
+ return;
+ }
+
+ Display display = displayManager.getDisplay(displayId);
+ if (display != null) {
+ int windowType = (ENABLE_TASKBAR_NAVBAR_UNIFICATION && isDefaultDisplay(displayId))
+ ? TYPE_NAVIGATION_BAR : TYPE_NAVIGATION_BAR_PANEL;
+ Context newContext = mParentContext.createWindowContext(display, windowType, null);
+ addWindowContextToMap(displayId, newContext);
+ }
+ }
+
+ /**
+ * Retrieves the window context of the taskbar for the specified display.
+ *
+ * @param displayId The ID of the display for which to retrieve the window context.
+ * @return The Window Context {@link Context} for a given display or {@code null}.
+ */
+ private Context getWindowContext(int displayId) {
+ return mWindowContexts.get(displayId);
+ }
+
+ @VisibleForTesting
+ public Context getPrimaryWindowContext() {
+ return getWindowContext(getDefaultDisplayId());
+ }
+
+ /**
+ * Retrieves the window manager {@link WindowManager} of the taskbar for the specified display.
+ *
+ * @param displayId The ID of the display for which to retrieve the window manager.
+ * @return The window manager {@link WindowManager} for a given display or {@code null}.
+ */
+ private WindowManager getWindowManager(int displayId) {
+ return getWindowContext(displayId).getSystemService(WindowManager.class);
+ }
+
+ /**
+ * Adds the window context {@link Context} to taskbar map, mapped to display ID.
+ *
+ * @param displayId The ID of the display to associate with the taskbar root layout.
+ * @param windowContext The window context {@link Context} to add to the map.
+ */
+ private void addWindowContextToMap(int displayId, @NonNull Context windowContext) {
+ if (!mWindowContexts.contains(displayId)) {
+ mWindowContexts.put(displayId, windowContext);
+ }
+ }
+
+ /**
+ * Removes the window context {@link Context} for given display ID from the taskbar map.
+ *
+ * @param displayId The ID of the display for which to remove the taskbar root layout.
+ */
+ private void removeWindowContextFromMap(int displayId) {
+ if (mWindowContexts.contains(displayId)) {
+ mWindowContexts.delete(displayId);
+ }
+ }
+
private int getDefaultDisplayId() {
- return mWindowContext.getDisplayId();
+ return mParentContext.getDisplayId();
}
/** Temp logs for b/254119092. */
@@ -974,9 +1047,14 @@
boolean activityTaskbarPresent = mActivity != null
&& mActivity.getDeviceProfile().isTaskbarPresent;
- // TODO: make this display specific
- boolean contextTaskbarPresent = mUserUnlocked && LauncherAppState.getIDP(mWindowContext)
- .getDeviceProfile(mWindowContext).isTaskbarPresent;
+ Context windowContext = getWindowContext(displayId);
+ if (windowContext == null) {
+ log.add("window context for displayId" + displayId);
+ return;
+ }
+
+ boolean contextTaskbarPresent = mUserUnlocked && LauncherAppState.getIDP(windowContext)
+ .getDeviceProfile(windowContext).isTaskbarPresent;
if (activityTaskbarPresent == contextTaskbarPresent) {
log.add("mActivity and mWindowContext agree taskbarIsPresent=" + contextTaskbarPresent);
Log.d(TASKBAR_NOT_DESTROYED_TAG, log.toString());
@@ -993,12 +1071,12 @@
log.add("\t\tmActivity.getDeviceProfile().isTaskbarPresent="
+ activityTaskbarPresent);
}
- log.add("\tmWindowContext logs:");
- log.add("\t\tmWindowContext=" + mWindowContext);
- log.add("\t\tmWindowContext.getResources().getConfiguration()="
- + mWindowContext.getResources().getConfiguration());
+ log.add("\tWindowContext logs:");
+ log.add("\t\tWindowContext=" + windowContext);
+ log.add("\t\tWindowContext.getResources().getConfiguration()="
+ + windowContext.getResources().getConfiguration());
if (mUserUnlocked) {
- log.add("\t\tLauncherAppState.getIDP().getDeviceProfile(mWindowContext)"
+ log.add("\t\tLauncherAppState.getIDP().getDeviceProfile(getPrimaryWindowContext())"
+ ".isTaskbarPresent=" + contextTaskbarPresent);
} else {
log.add("\t\tCouldn't get DeviceProfile because !mUserUnlocked");
@@ -1010,8 +1088,4 @@
private final DeviceProfile.OnDeviceProfileChangeListener mDebugActivityDeviceProfileChanged =
dp -> debugWhyTaskbarNotDestroyed("mActivity onDeviceProfileChanged");
- @VisibleForTesting
- public Context getWindowContext() {
- return mWindowContext;
- }
}
diff --git a/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailView.kt b/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailView.kt
index 34b0206..28152ec 100644
--- a/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailView.kt
+++ b/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailView.kt
@@ -151,8 +151,8 @@
}
}
- fun setState(state: TaskThumbnailUiState) {
- Log.d(TAG, "viewModelUiState changed from: $uiState to: $state")
+ fun setState(state: TaskThumbnailUiState, taskId: Int? = null) {
+ logDebug("taskId: $taskId - uiState changed from: $uiState to: $state")
if (uiState == state) return
uiState = state
resetViews()
@@ -245,6 +245,10 @@
thumbnailView.imageMatrix = viewModel.getThumbnailPositionState(width, height, isLayoutRtl)
}
+ private fun logDebug(message: String) {
+ Log.d(TAG, "[TaskThumbnailView@${Integer.toHexString(hashCode())}] $message")
+ }
+
private fun maybeCreateHeader() {
if (enableDesktopExplodedView() && taskThumbnailViewHeader == null) {
taskThumbnailViewHeader =
diff --git a/quickstep/src/com/android/quickstep/util/ExternalDisplays.kt b/quickstep/src/com/android/quickstep/util/ExternalDisplays.kt
index e1a8578..455b312 100644
--- a/quickstep/src/com/android/quickstep/util/ExternalDisplays.kt
+++ b/quickstep/src/com/android/quickstep/util/ExternalDisplays.kt
@@ -17,6 +17,7 @@
package com.android.quickstep.util
import android.view.Display.DEFAULT_DISPLAY
+import android.view.Display.INVALID_DISPLAY
import com.android.systemui.shared.recents.model.Task
/** Whether this displayId belongs to an external display */
@@ -25,7 +26,14 @@
/** Returns displayId of this [Task], default to [DEFAULT_DISPLAY] */
val Task?.displayId
- get() = this?.key?.displayId ?: DEFAULT_DISPLAY
+ get() =
+ this?.key?.displayId.let { displayId ->
+ when (displayId) {
+ null -> DEFAULT_DISPLAY
+ INVALID_DISPLAY -> DEFAULT_DISPLAY
+ else -> displayId
+ }
+ }
/** Returns if this task belongs tto [DEFAULT_DISPLAY] */
val Task?.isExternalDisplay
diff --git a/quickstep/src/com/android/quickstep/views/TaskContainer.kt b/quickstep/src/com/android/quickstep/views/TaskContainer.kt
index a9e84ef..0350c78 100644
--- a/quickstep/src/com/android/quickstep/views/TaskContainer.kt
+++ b/quickstep/src/com/android/quickstep/views/TaskContainer.kt
@@ -153,7 +153,10 @@
}
fun setState(state: TaskData?, liveTile: Boolean, hasHeader: Boolean) {
- thumbnailView.setState(TaskUiStateMapper.toTaskThumbnailUiState(state, liveTile, hasHeader))
+ thumbnailView.setState(
+ TaskUiStateMapper.toTaskThumbnailUiState(state, liveTile, hasHeader),
+ state?.taskId,
+ )
splitAnimationThumbnail =
if (state is TaskData.Data) state.thumbnailData?.thumbnail else null
}
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/rules/TaskbarUnitTestRule.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/rules/TaskbarUnitTestRule.kt
index e150568..90c9553 100644
--- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/rules/TaskbarUnitTestRule.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/rules/TaskbarUnitTestRule.kt
@@ -125,7 +125,8 @@
// Needs to be set on window context instead of sandbox context, because it does
// does not propagate between them. However, this change will impact created
// TaskbarActivityContext instances, since they wrap the window context.
- taskbarManager.windowContext.resources.configuration.setLayoutDirection(
+ // TODO: iterate through all window contexts and do this.
+ taskbarManager.primaryWindowContext.resources.configuration.setLayoutDirection(
RTL_LOCALE
)
}
diff --git a/res/values-ar/strings.xml b/res/values-ar/strings.xml
index a925866..0b8e52a 100644
--- a/res/values-ar/strings.xml
+++ b/res/values-ar/strings.xml
@@ -38,13 +38,13 @@
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"لا يمكن استخدام هذين التطبيقَين في الوقت نفسه على هذا الجهاز"</string>
<string name="app_pair_needs_unfold" msgid="4588897528143807002">"افتح الجهاز لاستخدام هذين التطبيقَين في الوقت نفسه"</string>
<string name="app_pair_not_available" msgid="3556767440808032031">"ميزة \"استخدام تطبيقين في الوقت نفسه\" غير متوفّرة"</string>
- <string name="long_press_widget_to_add" msgid="3587712543577675817">"انقر مع الاستمرار لنقل أداة."</string>
- <string name="long_accessible_way_to_add" msgid="2733588281439571974">"انقر مرتين مع تثبيت إصبعك لنقل أداة أو استخدام الإجراءات المخصّصة."</string>
+ <string name="long_press_widget_to_add" msgid="3587712543577675817">"انقر مع الاستمرار لنقل تطبيق مصغَّر."</string>
+ <string name="long_accessible_way_to_add" msgid="2733588281439571974">"انقر مرتين مع تثبيت إصبعك لنقل تطبيق مصغَّر أو استخدام الإجراءات المخصّصة."</string>
<string name="widget_picker_widget_options_button_description" msgid="4770099264476852363">"خيارات إضافية"</string>
<string name="widget_picker_show_all_widgets_menu_item_title" msgid="9023638224586908119">"عرض كل التطبيقات المصغّرة"</string>
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"العرض %1$d الطول %2$d"</string>
- <string name="widget_preview_context_description" msgid="9045841361655787574">"أداة <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
+ <string name="widget_preview_context_description" msgid="9045841361655787574">"التطبيق المصغَّر <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
<string name="widget_preview_name_and_dims_content_description" msgid="8489038126122831595">"التطبيق المصغّرة \"<xliff:g id="WIDGET_NAME">%1$s</xliff:g>\"، بعرض %2$d وارتفاع %3$d"</string>
<string name="add_item_request_drag_hint" msgid="8730547755622776606">"يُرجى النقر مع الاستمرار على التطبيق المصغّر لنقله إلى الشاشة الرئيسية"</string>
<string name="add_to_home_screen" msgid="9168649446635919791">"إضافة إلى الشاشة الرئيسية"</string>
@@ -75,8 +75,8 @@
<string name="widgets_list_expand_button_label" msgid="7912016136574932622">"عرض الكل"</string>
<string name="widgets_list_expand_button_content_description" msgid="4600513860973450888">"عرض كل التطبيقات المصغّرة"</string>
<string name="widgets_list_expanded" msgid="7374857868788557730">"جارٍ عرض كل التطبيقات المصغّرة"</string>
- <string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"انقر لتغيير إعدادات الأداة"</string>
- <string name="widget_reconfigure_button_content_description" msgid="8811472721881205250">"تغيير إعدادات الأداة"</string>
+ <string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"انقر لتغيير إعدادات التطبيق المصغَّر"</string>
+ <string name="widget_reconfigure_button_content_description" msgid="8811472721881205250">"تغيير إعدادات التطبيق المصغَّر"</string>
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"بحث في التطبيقات"</string>
<string name="all_apps_loading_message" msgid="5813968043155271636">"جارٍ تحميل التطبيقات…"</string>
<string name="all_apps_no_search_results" msgid="3200346862396363786">"لم يتم العثور على أي تطبيقات تتطابق مع \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
@@ -108,7 +108,7 @@
<string name="permdesc_read_settings" msgid="4208061150510996676">"يسمح هذا الإذن للتطبيق بالاطلاع على الإعدادات والاختصارات على الشاشة الرئيسية."</string>
<string name="permlab_write_settings" msgid="4820028712156303762">"تعديل الإعدادات والاختصارات على الشاشة الرئيسية"</string>
<string name="permdesc_write_settings" msgid="726859348127868466">"يسمح هذا الإذن للتطبيق بتغيير الإعدادات والاختصارات على الشاشة الرئيسية."</string>
- <string name="gadget_error_text" msgid="740356548025791839">"يتعذّر تحميل الأداة."</string>
+ <string name="gadget_error_text" msgid="740356548025791839">"يتعذّر تحميل التطبيق المصغَّر."</string>
<string name="gadget_setup_text" msgid="8348374825537681407">"إعدادات التطبيق المصغّر"</string>
<string name="gadget_complete_setup_text" msgid="309040266978007925">"انقر لإكمال الإعداد."</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"هذا تطبيق نظام وتتعذر إزالته."</string>
@@ -181,7 +181,7 @@
<string name="action_increase_height" msgid="459390020612501122">"زيادة الارتفاع"</string>
<string name="action_decrease_width" msgid="1374549771083094654">"تقليل العرض"</string>
<string name="action_decrease_height" msgid="282377193880900022">"تقليل الارتفاع"</string>
- <string name="widget_resized" msgid="9130327887929620">"تم تغيير حجم الأداة إلى العرض <xliff:g id="NUMBER_0">%1$s</xliff:g> والارتفاع <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+ <string name="widget_resized" msgid="9130327887929620">"تم تغيير حجم التطبيق المصغَّر إلى العرض <xliff:g id="NUMBER_0">%1$s</xliff:g> والارتفاع <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
<string name="action_deep_shortcut" msgid="4766835855579976045">"قائمة الاختصارات"</string>
<string name="action_dismiss_notification" msgid="5909461085055959187">"تجاهل"</string>
<string name="accessibility_close" msgid="2277148124685870734">"إغلاق"</string>
diff --git a/res/values-pl/strings.xml b/res/values-pl/strings.xml
index 754bd76..1dad001 100644
--- a/res/values-pl/strings.xml
+++ b/res/values-pl/strings.xml
@@ -46,7 +46,7 @@
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"Szerokość %1$d, wysokość %2$d"</string>
<string name="widget_preview_context_description" msgid="9045841361655787574">"Widżet <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
<string name="widget_preview_name_and_dims_content_description" msgid="8489038126122831595">"Widżet <xliff:g id="WIDGET_NAME">%1$s</xliff:g>, %2$d (szerokość), %3$d (wysokość)"</string>
- <string name="add_item_request_drag_hint" msgid="8730547755622776606">"Aby poruszać widżetem po ekranie głównym, kliknij go i przytrzymaj"</string>
+ <string name="add_item_request_drag_hint" msgid="8730547755622776606">"Aby przesunąć widżet na ekranie głównym, kliknij go i przytrzymaj"</string>
<string name="add_to_home_screen" msgid="9168649446635919791">"Dodaj do ekranu głównego"</string>
<string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"Widżet <xliff:g id="WIDGET_NAME">%1$s</xliff:g> został dodany do ekranu głównego"</string>
<string name="suggested_widgets_header_title" msgid="1844314680798145222">"Sugestie"</string>
diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java
index b76e098..fb48a4d 100644
--- a/src/com/android/launcher3/folder/Folder.java
+++ b/src/com/android/launcher3/folder/Folder.java
@@ -1708,7 +1708,7 @@
@Override
public boolean canInterceptEventsInSystemGestureRegion() {
- return true;
+ return !mIsEditingName;
}
/**
diff --git a/src/com/android/launcher3/model/LoaderCursor.java b/src/com/android/launcher3/model/LoaderCursor.java
index 1623881..6a8d86b 100644
--- a/src/com/android/launcher3/model/LoaderCursor.java
+++ b/src/com/android/launcher3/model/LoaderCursor.java
@@ -42,6 +42,7 @@
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
+import com.android.launcher3.Flags;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherSettings.Favorites;
@@ -201,7 +202,7 @@
info.itemType = itemType;
info.title = getTitle();
// the fallback icon
- if (!loadIcon(info)) {
+ if (!loadIconFromDb(info)) {
info.bitmap = mIconCache.getDefaultIcon(info.user);
}
@@ -213,15 +214,15 @@
/**
* Loads the icon from the cursor and updates the {@param info} if the icon is an app resource.
*/
- protected boolean loadIcon(WorkspaceItemInfo info) {
- return createIconRequestInfo(info, false).loadWorkspaceIcon(mContext);
+ protected boolean loadIconFromDb(WorkspaceItemInfo info) {
+ return createIconRequestInfo(info, false).loadIconFromDbBlob(mContext);
}
public IconRequestInfo<WorkspaceItemInfo> createIconRequestInfo(
WorkspaceItemInfo wai, boolean useLowResIcon) {
byte[] iconBlob = itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT || restoreFlag != 0
+ || (wai.isInactiveArchive() && Flags.restoreArchivedAppIconsFromDb())
? getIconBlob() : null;
-
return new IconRequestInfo<>(wai, mActivityInfo, iconBlob, useLowResIcon);
}
@@ -312,7 +313,7 @@
info.intent = intent;
// the fallback icon
- if (!loadIcon(info)) {
+ if (!loadIconFromDb(info)) {
mIconCache.getTitleAndIcon(info, DEFAULT_LOOKUP_FLAG);
}
@@ -375,20 +376,11 @@
info.intent = newIntent;
UserCache userCache = UserCache.getInstance(mContext);
UserIconInfo userIconInfo = userCache.getUserInfo(user);
-
- if (loadIcon) {
- mIconCache.getTitleAndIcon(info, mActivityInfo,
- DEFAULT_LOOKUP_FLAG.withUseLowRes(useLowResIcon));
- if (mIconCache.isDefaultIcon(info.bitmap, user)) {
- loadIcon(info);
- }
- }
-
if (mActivityInfo != null) {
AppInfo.updateRuntimeFlagsForActivityTarget(info, mActivityInfo, userIconInfo,
ApiWrapper.INSTANCE.get(mContext), mPmHelper);
}
-
+ loadWorkspaceTitleAndIcon(useLowResIcon, loadIcon, info);
// from the db
if (TextUtils.isEmpty(info.title)) {
if (loadIcon) {
@@ -407,6 +399,32 @@
return info;
}
+ @VisibleForTesting
+ void loadWorkspaceTitleAndIcon(
+ boolean useLowResIcon,
+ boolean loadIconFromCache,
+ WorkspaceItemInfo info
+ ) {
+ boolean isPreArchived = Flags.enableSupportForArchiving()
+ && Flags.restoreArchivedAppIconsFromDb()
+ && info.isInactiveArchive();
+ boolean preArchivedIconNotFound = isPreArchived && !loadIconFromDb(info);
+ if (preArchivedIconNotFound) {
+ Log.d(TAG, "loadIconFromDb failed for pre-archived icon, loading from cache."
+ + " Component=" + info.getTargetComponent());
+ mIconCache.getTitleAndIcon(info, mActivityInfo,
+ DEFAULT_LOOKUP_FLAG.withUseLowRes(useLowResIcon));
+ } else if (loadIconFromCache && !info.isInactiveArchive()) {
+ mIconCache.getTitleAndIcon(info, mActivityInfo,
+ DEFAULT_LOOKUP_FLAG.withUseLowRes(useLowResIcon));
+ if (mIconCache.isDefaultIcon(info.bitmap, user)) {
+ Log.d(TAG, "Default Icon found in cache, trying DB instead. "
+ + " Component=" + info.getTargetComponent());
+ loadIconFromDb(info);
+ }
+ }
+ }
+
/**
* Returns a {@link ContentWriter} which can be used to update the current item.
*/
diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java
index fee9696..3ee029b 100644
--- a/src/com/android/launcher3/model/LoaderTask.java
+++ b/src/com/android/launcher3/model/LoaderTask.java
@@ -26,6 +26,7 @@
import static com.android.launcher3.LauncherSettings.Favorites.DESKTOP_ICON_FLAG;
import static com.android.launcher3.LauncherSettings.Favorites.TABLE_NAME;
import static com.android.launcher3.icons.CacheableShortcutInfo.convertShortcutsToCacheableShortcuts;
+import static com.android.launcher3.icons.cache.CacheLookupFlag.DEFAULT_LOOKUP_FLAG;
import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_HAS_SHORTCUT_PERMISSION;
import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_PRIVATE_PROFILE_QUIET_MODE_ENABLED;
import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_QUIET_MODE_CHANGE_PERMISSION;
@@ -106,11 +107,13 @@
import com.android.launcher3.widget.WidgetInflater;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
+import java.util.Optional;
import java.util.Set;
import java.util.concurrent.CancellationException;
@@ -154,6 +157,8 @@
private Map<ShortcutKey, ShortcutInfo> mShortcutKeyToPinnedShortcuts;
private HashMap<PackageUserKey, SessionInfo> mInstallingPkgsCached;
+ private List<IconRequestInfo<WorkspaceItemInfo>> mWorkspaceIconRequestInfos = new ArrayList<>();
+
private boolean mStopped;
private final Set<PackageUserKey> mPendingPackages = new HashSet<>();
@@ -410,7 +415,7 @@
protected void loadWorkspace(
List<CacheableShortcutInfo> allDeepShortcuts,
String selection,
- LoaderMemoryLogger memoryLogger,
+ @Nullable LoaderMemoryLogger memoryLogger,
@Nullable LauncherRestoreEventLogger restoreEventLogger
) {
Trace.beginSection("LoadWorkspace");
@@ -474,13 +479,12 @@
final LongSparseArray<Boolean> unlockedUsers = new LongSparseArray<>();
queryPinnedShortcutsForUnlockedUsers(context, unlockedUsers);
- List<IconRequestInfo<WorkspaceItemInfo>> iconRequestInfos = new ArrayList<>();
-
+ mWorkspaceIconRequestInfos = new ArrayList<>();
WorkspaceItemProcessor itemProcessor = new WorkspaceItemProcessor(c, memoryLogger,
mUserCache, mUserManagerState, mLauncherApps, mPendingPackages,
mShortcutKeyToPinnedShortcuts, mApp, mBgDataModel,
mWidgetProvidersMap, installingPkgs, isSdCardReady,
- widgetInflater, mPmHelper, iconRequestInfos, unlockedUsers,
+ widgetInflater, mPmHelper, mWorkspaceIconRequestInfos, unlockedUsers,
allDeepShortcuts);
if (mStopped) {
@@ -490,7 +494,7 @@
itemProcessor.processItem();
}
}
- tryLoadWorkspaceIconsInBulk(iconRequestInfos);
+ tryLoadWorkspaceIconsInBulk(mWorkspaceIconRequestInfos);
} finally {
IOUtils.closeSilently(c);
}
@@ -621,7 +625,9 @@
for (IconRequestInfo<WorkspaceItemInfo> iconRequestInfo : iconRequestInfos) {
WorkspaceItemInfo wai = iconRequestInfo.itemInfo;
if (mIconCache.isDefaultIcon(wai.bitmap, wai.user)) {
- iconRequestInfo.loadWorkspaceIcon(mApp.getContext());
+ logASplit("tryLoadWorkspaceIconsInBulk: default icon found for "
+ + wai.getTargetComponent() + ", will attempt to load from iconBlob");
+ iconRequestInfo.loadIconFromDbBlob(mApp.getContext());
}
}
} finally {
@@ -702,7 +708,7 @@
// Clear the list of apps
mBgAllAppsList.clear();
- List<IconRequestInfo<AppInfo>> iconRequestInfos = new ArrayList<>();
+ List<IconRequestInfo<AppInfo>> allAppsItemRequestInfos = new ArrayList<>();
boolean isWorkProfileQuiet = false;
boolean isPrivateProfileQuiet = false;
for (UserHandle user : profiles) {
@@ -742,15 +748,14 @@
}
}
- iconRequestInfos.add(new IconRequestInfo<>(
- appInfo, app, /* useLowResIcon= */ false));
- mBgAllAppsList.add(
- appInfo, app, false);
+ IconRequestInfo<AppInfo> iconRequestInfo = getAppInfoIconRequestInfo(
+ appInfo, app, mWorkspaceIconRequestInfos);
+ allAppsItemRequestInfos.add(iconRequestInfo);
+ mBgAllAppsList.add(appInfo, app, false);
}
allActivityList.addAll(apps);
}
-
if (FeatureFlags.PROMISE_APPS_IN_ALL_APPS.get()) {
// get all active sessions and add them to the all apps list
for (PackageInstaller.SessionInfo info :
@@ -761,7 +766,7 @@
false);
if (promiseAppInfo != null) {
- iconRequestInfos.add(new IconRequestInfo<>(
+ allAppsItemRequestInfos.add(new IconRequestInfo<>(
promiseAppInfo,
/* launcherActivityInfo= */ null,
promiseAppInfo.getMatchingLookupFlag().useLowRes()));
@@ -770,9 +775,22 @@
}
Trace.beginSection("LoadAllAppsIconsInBulk");
+
try {
- mIconCache.getTitlesAndIconsInBulk(iconRequestInfos);
- iconRequestInfos.forEach(iconRequestInfo ->
+ mIconCache.getTitlesAndIconsInBulk(allAppsItemRequestInfos);
+ if (Flags.restoreArchivedAppIconsFromDb()) {
+ for (IconRequestInfo<AppInfo> iconRequestInfo : allAppsItemRequestInfos) {
+ AppInfo appInfo = iconRequestInfo.itemInfo;
+ if (mIconCache.isDefaultIcon(appInfo.bitmap, appInfo.user)) {
+ logASplit("LoadAllAppsIconsInBulk: default icon found for "
+ + appInfo.getTargetComponent()
+ + ", will attempt to load from iconBlob: "
+ + Arrays.toString(iconRequestInfo.iconBlob));
+ iconRequestInfo.loadIconFromDbBlob(mApp.getContext());
+ }
+ }
+ }
+ allAppsItemRequestInfos.forEach(iconRequestInfo ->
mBgAllAppsList.updateSectionName(iconRequestInfo.itemInfo));
} finally {
Trace.endSection();
@@ -795,6 +813,51 @@
return allActivityList;
}
+ @NonNull
+ @VisibleForTesting
+ IconRequestInfo<AppInfo> getAppInfoIconRequestInfo(
+ AppInfo appInfo,
+ LauncherActivityInfo activityInfo,
+ List<IconRequestInfo<WorkspaceItemInfo>> workspaceRequestInfos
+ ) {
+ if (Flags.restoreArchivedAppIconsFromDb()) {
+ Optional<IconRequestInfo<WorkspaceItemInfo>> workspaceIconRequest =
+ workspaceRequestInfos.stream()
+ .filter(request -> appInfo.getTargetComponent().equals(
+ request.itemInfo.getTargetComponent()))
+ .findFirst();
+
+ if (workspaceIconRequest.isPresent() && activityInfo.getApplicationInfo().isArchived) {
+ logASplit("getAppInfoIconRequestInfo:"
+ + " matching archived info found, loading icon blob into icon request."
+ + " Component=" + appInfo.getTargetComponent());
+ IconRequestInfo<AppInfo> iconRequestInfo = new IconRequestInfo<>(
+ appInfo,
+ activityInfo,
+ workspaceIconRequest.get().iconBlob,
+ false /* useLowResIcon= */
+ );
+ if (!iconRequestInfo.loadIconFromDbBlob(mApp.getContext())) {
+ Log.d(TAG, "AppInfo Icon failed to load from blob, using cache.");
+ mIconCache.getTitleAndIcon(
+ appInfo,
+ iconRequestInfo.launcherActivityInfo,
+ DEFAULT_LOOKUP_FLAG
+ );
+ }
+ return iconRequestInfo;
+ } else {
+ Log.d(TAG, "App not archived or workspace info not found"
+ + ", creating IconRequestInfo without icon blob."
+ + " Component:" + appInfo.getTargetComponent()
+ + ", isArchived: " + activityInfo.getApplicationInfo().isArchived);
+ }
+ }
+ logASplit("Loading IconRequestInfo without iconBlob for AppInfo: "
+ + appInfo.getTargetComponent());
+ return new IconRequestInfo<>(appInfo, activityInfo, false /* useLowResIcon= */);
+ }
+
private List<ShortcutInfo> loadDeepShortcuts() {
List<ShortcutInfo> allShortcuts = new ArrayList<>();
mBgDataModel.deepShortcutMap.clear();
diff --git a/src/com/android/launcher3/model/WorkspaceItemProcessor.kt b/src/com/android/launcher3/model/WorkspaceItemProcessor.kt
index de1df2e..90f11a3 100644
--- a/src/com/android/launcher3/model/WorkspaceItemProcessor.kt
+++ b/src/com/android/launcher3/model/WorkspaceItemProcessor.kt
@@ -286,7 +286,7 @@
// If the pinned deep shortcut is no longer published,
// use the last saved icon instead of the default.
val csi = CacheableShortcutInfo(pinnedShortcut, appInfoWrapper)
- iconCache.getShortcutIcon(info, csi, c::loadIcon)
+ iconCache.getShortcutIcon(info, csi, c::loadIconFromDb)
if (appInfoWrapper.isSuspended()) {
info.runtimeStatusFlags =
info.runtimeStatusFlags or ItemInfoWithIcon.FLAG_DISABLED_SUSPENDED
diff --git a/src/com/android/launcher3/model/data/IconRequestInfo.java b/src/com/android/launcher3/model/data/IconRequestInfo.java
index e77e527..42af018 100644
--- a/src/com/android/launcher3/model/data/IconRequestInfo.java
+++ b/src/com/android/launcher3/model/data/IconRequestInfo.java
@@ -64,23 +64,25 @@
}
/**
- * Loads this request's item info's title. This method should only be used on IconRequestInfos
- * for WorkspaceItemInfos.
+ * Loads this request's item info's title and icon from given iconBlob from Launcher.db.
+ * This method should only be used on {@link IconRequestInfo} for {@link WorkspaceItemInfo}
+ * or {@link AppInfo}.
*/
- public boolean loadWorkspaceIcon(Context context) {
- if (!(itemInfo instanceof WorkspaceItemInfo)) {
+ public boolean loadIconFromDbBlob(Context context) {
+ if (!(itemInfo instanceof WorkspaceItemInfo) && !(itemInfo instanceof AppInfo)) {
throw new IllegalStateException(
- "loadWorkspaceIcon should only be use for a WorkspaceItemInfos: " + itemInfo);
+ "loadIconFromDb should only be used for either WorkspaceItemInfo or AppInfo: "
+ + itemInfo);
}
try (LauncherIcons li = LauncherIcons.obtain(context)) {
- WorkspaceItemInfo info = (WorkspaceItemInfo) itemInfo;
- // Failed to load from resource, try loading from DB.
+ ItemInfoWithIcon info = itemInfo;
if (iconBlob == null) {
+ Log.d(TAG, "loadIconFromDb: icon blob null, returning. Component="
+ + info.getTargetComponent());
return false;
}
- info.bitmap = li.createIconBitmap(decodeByteArray(
- iconBlob, 0, iconBlob.length));
+ info.bitmap = li.createIconBitmap(decodeByteArray(iconBlob, 0, iconBlob.length));
return true;
} catch (Exception e) {
Log.e(TAG, "Failed to decode byte array for info " + itemInfo, e);
diff --git a/src/com/android/launcher3/widget/WidgetCell.java b/src/com/android/launcher3/widget/WidgetCell.java
index 4811a17..7a27bf4 100644
--- a/src/com/android/launcher3/widget/WidgetCell.java
+++ b/src/com/android/launcher3/widget/WidgetCell.java
@@ -153,17 +153,19 @@
mWidgetAddButton = findViewById(R.id.widget_add_button);
if (enableWidgetTapToAdd()) {
-
setAccessibilityDelegate(new AccessibilityDelegate() {
@Override
public void onInitializeAccessibilityNodeInfo(View host,
AccessibilityNodeInfo info) {
super.onInitializeAccessibilityNodeInfo(host, info);
- String accessibilityLabel = getResources().getString(mWidgetAddButton.isShown()
- ? R.string.widget_cell_tap_to_hide_add_button_label
- : R.string.widget_cell_tap_to_show_add_button_label);
- info.addAction(new AccessibilityNodeInfo.AccessibilityAction(ACTION_CLICK,
- accessibilityLabel));
+ if (hasOnClickListeners()) {
+ String accessibilityLabel = getResources().getString(
+ mWidgetAddButton.isShown()
+ ? R.string.widget_cell_tap_to_hide_add_button_label
+ : R.string.widget_cell_tap_to_show_add_button_label);
+ info.addAction(new AccessibilityNodeInfo.AccessibilityAction(ACTION_CLICK,
+ accessibilityLabel));
+ }
}
});
mWidgetAddButton.setVisibility(INVISIBLE);
diff --git a/tests/multivalentTests/src/com/android/launcher3/model/LoaderCursorTest.java b/tests/multivalentTests/src/com/android/launcher3/model/LoaderCursorTest.java
index 63359ec..11047fb 100644
--- a/tests/multivalentTests/src/com/android/launcher3/model/LoaderCursorTest.java
+++ b/tests/multivalentTests/src/com/android/launcher3/model/LoaderCursorTest.java
@@ -16,6 +16,9 @@
package com.android.launcher3.model;
+import static android.graphics.BitmapFactory.decodeByteArray;
+import static android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT;
+
import static androidx.test.InstrumentationRegistry.getContext;
import static com.android.launcher3.LauncherSettings.Favorites.APPWIDGET_ID;
@@ -40,8 +43,11 @@
import static com.android.launcher3.LauncherSettings.Favorites.SPANY;
import static com.android.launcher3.LauncherSettings.Favorites.TITLE;
import static com.android.launcher3.LauncherSettings.Favorites._ID;
+import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_ARCHIVED;
import static com.android.launcher3.util.LauncherModelHelper.TEST_ACTIVITY;
+import static com.google.common.truth.Truth.assertThat;
+
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertNotNull;
@@ -52,13 +58,19 @@
import android.content.Context;
import android.content.Intent;
import android.database.MatrixCursor;
+import android.graphics.Bitmap;
import android.os.Process;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
+import com.android.launcher3.Flags;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.icons.LauncherIcons;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.util.Executors;
@@ -67,6 +79,7 @@
import org.junit.After;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -77,6 +90,9 @@
@RunWith(AndroidJUnit4.class)
public class LoaderCursorTest {
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(DEVICE_DEFAULT);
+
private LauncherModelHelper mModelHelper;
private LauncherAppState mApp;
private PackageManagerHelper mPmHelper;
@@ -87,6 +103,12 @@
private LoaderCursor mLoaderCursor;
+ private static byte[] sTestBlob = new byte[] {
+ -119, 80, 78, 71, 13, 10, 26, 10, 0, 0, 0, 13, 73, 72, 68, 82, 0, 0, 0, 1, 0, 0, 0, 1,
+ 8, 4, 0, 0, 0, -75, 28, 12, 2, 0, 0, 0, 11, 73, 68, 65, 84, 120, -38, 99, 100, 96, 0,
+ 0, 0, 6, 0, 2, 48, -127, -48, 47, 0, 0, 0, 0, 73, 69, 78, 68, -82, 66, 96, -126
+ };
+
@Before
public void setup() {
mModelHelper = new LauncherModelHelper();
@@ -119,7 +141,8 @@
.add(PROFILE_ID, 0)
.add(ITEM_TYPE, itemType)
.add(TITLE, title)
- .add(CONTAINER, CONTAINER_DESKTOP);
+ .add(CONTAINER, CONTAINER_DESKTOP)
+ .add(ICON, sTestBlob);
}
@Test
@@ -161,7 +184,12 @@
@Test
public void loadSimpleShortcut() {
- initCursor(ITEM_TYPE_DEEP_SHORTCUT, "my-shortcut");
+ mCursor.newRow()
+ .add(_ID, 1)
+ .add(PROFILE_ID, 0)
+ .add(ITEM_TYPE, ITEM_TYPE_DEEP_SHORTCUT)
+ .add(TITLE, "my-shortcut")
+ .add(CONTAINER, CONTAINER_DESKTOP);
assertTrue(mLoaderCursor.moveToNext());
WorkspaceItemInfo info = mLoaderCursor.loadSimpleWorkspaceItem();
@@ -223,6 +251,68 @@
newItemInfo(3, 3, 1, 1, CONTAINER_HOTSEAT, 3), true));
}
+ @Test
+ @EnableFlags(Flags.FLAG_RESTORE_ARCHIVED_APP_ICONS_FROM_DB)
+ public void ifArchivedWithFlag_whenloadWorkspaceTitleAndIcon_thenLoadIconFromDb() {
+ // Given
+ initCursor(ITEM_TYPE_APPLICATION, "title");
+ assertTrue(mLoaderCursor.moveToNext());
+ WorkspaceItemInfo itemInfo = new WorkspaceItemInfo();
+ itemInfo.bitmap = null;
+ itemInfo.runtimeStatusFlags |= FLAG_ARCHIVED;
+ Bitmap expectedBitmap = LauncherIcons.obtain(mContext)
+ .createIconBitmap(decodeByteArray(sTestBlob, 0, sTestBlob.length))
+ .icon;
+ // When
+ mLoaderCursor.loadWorkspaceTitleAndIcon(false, true, itemInfo);
+ // Then
+ assertThat(itemInfo.bitmap.icon).isNotNull();
+ assertThat(itemInfo.bitmap.icon.sameAs(expectedBitmap)).isTrue();
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_RESTORE_ARCHIVED_APP_ICONS_FROM_DB)
+ public void ifArchivedWithFlag_whenLoadIconFromDb_thenLoadIconFromBlob() {
+ // Given
+ initCursor(ITEM_TYPE_APPLICATION, "title");
+ assertTrue(mLoaderCursor.moveToNext());
+ WorkspaceItemInfo itemInfo = new WorkspaceItemInfo();
+ itemInfo.runtimeStatusFlags |= FLAG_ARCHIVED;
+ // Then
+ assertTrue(mLoaderCursor.loadIconFromDb(itemInfo));
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_RESTORE_ARCHIVED_APP_ICONS_FROM_DB)
+ public void ifArchivedWithoutFlag_whenLoadWorkspaceTitleAndIcon_thenDoNotLoadFromDb() {
+ // Given
+ initCursor(ITEM_TYPE_APPLICATION, "title");
+ assertTrue(mLoaderCursor.moveToNext());
+ WorkspaceItemInfo itemInfo = new WorkspaceItemInfo();
+ itemInfo.bitmap = null;
+ itemInfo.runtimeStatusFlags |= FLAG_ARCHIVED;
+ Intent intent = new Intent();
+ intent.setComponent(new ComponentName("package", "class"));
+ itemInfo.intent = intent;
+ // When
+ mLoaderCursor.loadWorkspaceTitleAndIcon(false, false, itemInfo);
+ // Then
+ assertThat(itemInfo.bitmap).isNull();
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_RESTORE_ARCHIVED_APP_ICONS_FROM_DB)
+ public void ifArchivedWithoutFlag_whenLoadIconFromDb_thenDoNotLoadFromBlob() {
+ // Given
+ initCursor(ITEM_TYPE_APPLICATION, "title");
+ assertTrue(mLoaderCursor.moveToNext());
+ WorkspaceItemInfo itemInfo = new WorkspaceItemInfo();
+ itemInfo.runtimeStatusFlags |= FLAG_ARCHIVED;
+ // Then
+ assertFalse(mLoaderCursor.loadIconFromDb(itemInfo));
+ }
+
+
private ItemInfo newItemInfo(int cellX, int cellY, int spanX, int spanY,
int container, int screenId) {
ItemInfo info = new ItemInfo();
diff --git a/tests/src/com/android/launcher3/model/LoaderTaskTest.kt b/tests/src/com/android/launcher3/model/LoaderTaskTest.kt
index dba7603..cdb45fc 100644
--- a/tests/src/com/android/launcher3/model/LoaderTaskTest.kt
+++ b/tests/src/com/android/launcher3/model/LoaderTaskTest.kt
@@ -1,7 +1,10 @@
package com.android.launcher3.model
import android.appwidget.AppWidgetManager
+import android.content.ComponentName
import android.content.Intent
+import android.content.pm.ApplicationInfo
+import android.content.pm.LauncherActivityInfo
import android.os.Process
import android.os.UserHandle
import android.platform.test.annotations.DisableFlags
@@ -28,6 +31,9 @@
import com.android.launcher3.icons.IconCache
import com.android.launcher3.icons.cache.CachingLogic
import com.android.launcher3.icons.cache.IconCacheUpdateHandler
+import com.android.launcher3.model.data.AppInfo
+import com.android.launcher3.model.data.IconRequestInfo
+import com.android.launcher3.model.data.WorkspaceItemInfo
import com.android.launcher3.pm.UserCache
import com.android.launcher3.provider.RestoreDbTask
import com.android.launcher3.ui.TestViewHelpers
@@ -38,8 +44,11 @@
import com.android.launcher3.util.TestUtil
import com.android.launcher3.util.UserIconInfo
import com.google.common.truth.Truth
+import com.google.common.truth.Truth.assertThat
import dagger.BindsInstance
import dagger.Component
+import java.util.concurrent.CountDownLatch
+import java.util.function.Predicate
import junit.framework.Assert.assertEquals
import org.junit.After
import org.junit.Before
@@ -58,12 +67,11 @@
import org.mockito.kotlin.any
import org.mockito.kotlin.anyOrNull
import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.mock
import org.mockito.kotlin.spy
import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever
import org.mockito.quality.Strictness
-import java.util.concurrent.CountDownLatch
-import java.util.function.Predicate
private const val INSERTION_STATEMENT_FILE = "databases/workspace_items.sql"
@@ -480,6 +488,157 @@
verify(spyContext, times(0)).sendBroadcast(any())
}
+ @Test
+ @EnableFlags(Flags.FLAG_RESTORE_ARCHIVED_APP_ICONS_FROM_DB)
+ fun `When flag on then archived AllApps icons found on Workspace loaded from db`() {
+ // Given
+ // Given
+ val activityInfo: LauncherActivityInfo = mock()
+ val applicationInfo: ApplicationInfo = mock<ApplicationInfo>().apply { isArchived = true }
+ whenever(activityInfo.applicationInfo).thenReturn(applicationInfo)
+ val expectedIconBlob = byteArrayOf(0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08)
+ val expectedComponent = ComponentName("package", "class")
+ val workspaceIconRequests =
+ listOf(
+ IconRequestInfo<WorkspaceItemInfo>(
+ WorkspaceItemInfo().apply {
+ intent = Intent().apply { component = expectedComponent }
+ },
+ activityInfo,
+ expectedIconBlob,
+ false, /* useLowResIcon */
+ )
+ )
+ val expectedAppInfo = AppInfo().apply { componentName = expectedComponent }
+ // When
+ val loader =
+ LoaderTask(
+ app,
+ bgAllAppsList,
+ BgDataModel(),
+ modelDelegate,
+ launcherBinder,
+ widgetsFilterDataProvider,
+ )
+ val actualIconRequest =
+ loader.getAppInfoIconRequestInfo(expectedAppInfo, activityInfo, workspaceIconRequests)
+ // Then
+ assertThat(actualIconRequest.iconBlob).isEqualTo(expectedIconBlob)
+ assertThat(actualIconRequest.itemInfo).isEqualTo(expectedAppInfo)
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_RESTORE_ARCHIVED_APP_ICONS_FROM_DB)
+ fun `When flag on then unarchived AllApps icons not loaded from db`() {
+ // Given
+ val activityInfo: LauncherActivityInfo = mock()
+ val applicationInfo: ApplicationInfo = mock<ApplicationInfo>().apply { isArchived = false }
+ whenever(activityInfo.applicationInfo).thenReturn(applicationInfo)
+ val expectedIconBlob = byteArrayOf(0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08)
+ val expectedComponent = ComponentName("package", "class")
+ val workspaceIconRequests =
+ listOf(
+ IconRequestInfo<WorkspaceItemInfo>(
+ WorkspaceItemInfo().apply {
+ intent = Intent().apply { component = expectedComponent }
+ },
+ activityInfo,
+ expectedIconBlob,
+ false, /* useLowResIcon */
+ )
+ )
+ val expectedAppInfo = AppInfo().apply { componentName = expectedComponent }
+ // When
+ val loader =
+ LoaderTask(
+ app,
+ bgAllAppsList,
+ BgDataModel(),
+ modelDelegate,
+ launcherBinder,
+ widgetsFilterDataProvider,
+ )
+ val actualIconRequest =
+ loader.getAppInfoIconRequestInfo(expectedAppInfo, activityInfo, workspaceIconRequests)
+ // Then
+ assertThat(actualIconRequest.iconBlob).isNull()
+ assertThat(actualIconRequest.itemInfo).isEqualTo(expectedAppInfo)
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_RESTORE_ARCHIVED_APP_ICONS_FROM_DB)
+ fun `When flag on then archived AllApps icon not found on Workspace not loaded from db`() {
+ // Given
+ val activityInfo: LauncherActivityInfo = mock()
+ val applicationInfo: ApplicationInfo = mock<ApplicationInfo>().apply { isArchived = true }
+ whenever(activityInfo.applicationInfo).thenReturn(applicationInfo)
+ val expectedIconBlob = byteArrayOf(0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08)
+ val expectedComponent = ComponentName("package", "class")
+ val workspaceIconRequests =
+ listOf(
+ IconRequestInfo<WorkspaceItemInfo>(
+ WorkspaceItemInfo().apply {
+ intent = Intent().apply { component = expectedComponent }
+ },
+ activityInfo,
+ expectedIconBlob,
+ false, /* useLowResIcon */
+ )
+ )
+ val expectedAppInfo =
+ AppInfo().apply { componentName = ComponentName("differentPkg", "differentClass") }
+ // When
+ val loader =
+ LoaderTask(
+ app,
+ bgAllAppsList,
+ BgDataModel(),
+ modelDelegate,
+ launcherBinder,
+ widgetsFilterDataProvider,
+ )
+ val actualIconRequest =
+ loader.getAppInfoIconRequestInfo(expectedAppInfo, activityInfo, workspaceIconRequests)
+ // Then
+ assertThat(actualIconRequest.iconBlob).isNull()
+ assertThat(actualIconRequest.itemInfo).isEqualTo(expectedAppInfo)
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_RESTORE_ARCHIVED_APP_ICONS_FROM_DB)
+ fun `When flag off then archived AllApps icons not loaded from db`() {
+ // Given
+ val activityInfo: LauncherActivityInfo = mock()
+ val applicationInfo: ApplicationInfo = mock<ApplicationInfo>().apply { isArchived = true }
+ whenever(activityInfo.applicationInfo).thenReturn(applicationInfo)
+ val expectedIconBlob = byteArrayOf(0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08)
+ val workspaceIconRequests =
+ listOf(
+ IconRequestInfo<WorkspaceItemInfo>(
+ WorkspaceItemInfo(),
+ activityInfo,
+ expectedIconBlob,
+ false, /* useLowResIcon */
+ )
+ )
+ val expectedAppInfo = AppInfo()
+ // When
+ val loader =
+ LoaderTask(
+ app,
+ bgAllAppsList,
+ BgDataModel(),
+ modelDelegate,
+ launcherBinder,
+ widgetsFilterDataProvider,
+ )
+ val actualIconRequest =
+ loader.getAppInfoIconRequestInfo(expectedAppInfo, activityInfo, workspaceIconRequests)
+ // Then
+ assertThat(actualIconRequest.iconBlob).isNull()
+ assertThat(actualIconRequest.itemInfo).isEqualTo(expectedAppInfo)
+ }
+
@LauncherAppSingleton
@Component(modules = [AllModulesForTest::class])
interface TestComponent : LauncherAppComponent {