Merge "Fixed quick search bar shrinking." into main
diff --git a/quickstep/res/values-az/strings.xml b/quickstep/res/values-az/strings.xml
index a87ed44..6c5748d 100644
--- a/quickstep/res/values-az/strings.xml
+++ b/quickstep/res/values-az/strings.xml
@@ -69,7 +69,7 @@
<string name="home_gesture_intro_title" msgid="836590312858441830">"Əsas səhifəyə keçmək üçün sürüşdürün"</string>
<string name="home_gesture_intro_subtitle" msgid="2632238748497975326">"Ekranın aşağısından yuxarısına sürüşdürün. Bu jest həmişə Əsas səhifəyə aparır."</string>
<string name="home_gesture_spoken_intro_subtitle" msgid="1030987707382031750">"2 barmaqla ekranın aşağısından yuxarısına sürüşdürün. Bu jest həmişə Əsas səhifəyə aparır."</string>
- <string name="home_gesture_tutorial_title" msgid="3126834347496917376">"Əsas səhifəyə qayıdın"</string>
+ <string name="home_gesture_tutorial_title" msgid="3126834347496917376">"Əsas səhifəyə keçin"</string>
<string name="home_gesture_tutorial_subtitle" msgid="7245995490408668778">"Ekranın aşağısından yuxarı sürüşdürün"</string>
<string name="home_gesture_tutorial_success" msgid="1736295017642244751">"Əla!"</string>
<string name="overview_gesture_feedback_swipe_too_far_from_edge" msgid="6402349235265407385">"Ekranın aşağı kənarından yuxarı sürüşdürün"</string>
diff --git a/quickstep/res/values-zh-rCN/strings.xml b/quickstep/res/values-zh-rCN/strings.xml
index eee46bb..dc16036 100644
--- a/quickstep/res/values-zh-rCN/strings.xml
+++ b/quickstep/res/values-zh-rCN/strings.xml
@@ -140,7 +140,7 @@
<string name="always_show_taskbar" msgid="3608801276107751229">"始终显示任务栏"</string>
<string name="change_navigation_mode" msgid="9088393078736808968">"更改导航模式"</string>
<string name="taskbar_divider_a11y_title" msgid="6608690309720242080">"任务栏分隔线"</string>
- <string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"溢出式任务栏"</string>
+ <string name="taskbar_overflow_a11y_title" msgid="7960342079198820179">"任务栏溢出图标"</string>
<string name="move_drop_target_top_or_left" msgid="2988702185049595807">"移到顶部/左侧"</string>
<string name="move_drop_target_bottom_or_right" msgid="5431393418797620162">"移到底部/右侧"</string>
<string name="quick_switch_overflow" msgid="3679780650881041632">"{count,plural, =1{多个应用}other{多个应用}}"</string>
diff --git a/quickstep/res/values/config.xml b/quickstep/res/values/config.xml
index 5c80575..f3c9467 100644
--- a/quickstep/res/values/config.xml
+++ b/quickstep/res/values/config.xml
@@ -52,6 +52,11 @@
<integer name="max_depth_blur_radius">23</integer>
+ <!-- If predicted widgets from prediction service are less than this number, additional
+ eligible widgets may be added locally by launcher. When set to 0, no widgets will be added
+ locally. -->
+ <integer name="widget_predictions_min_count">6</integer>
+
<!-- Accessibility actions -->
<item type="id" name="action_move_to_top_or_left" />
<item type="id" name="action_move_to_bottom_or_right" />
diff --git a/quickstep/src/com/android/launcher3/desktop/DesktopAppLaunchTransition.kt b/quickstep/src/com/android/launcher3/desktop/DesktopAppLaunchTransition.kt
index 6916a1d..e160f82 100644
--- a/quickstep/src/com/android/launcher3/desktop/DesktopAppLaunchTransition.kt
+++ b/quickstep/src/com/android/launcher3/desktop/DesktopAppLaunchTransition.kt
@@ -20,6 +20,7 @@
import android.animation.AnimatorSet
import android.animation.ValueAnimator
import android.content.Context
+import android.graphics.Rect
import android.os.IBinder
import android.view.SurfaceControl.Transaction
import android.view.WindowManager.TRANSIT_OPEN
@@ -31,6 +32,7 @@
import android.window.TransitionInfo.Change
import androidx.core.animation.addListener
import com.android.app.animation.Interpolators
+import com.android.internal.policy.ScreenDecorationsUtils
import com.android.quickstep.RemoteRunnable
import com.android.wm.shell.shared.animation.MinimizeAnimator
import com.android.wm.shell.shared.animation.WindowAnimator
@@ -43,8 +45,19 @@
* ([android.view.WindowManager.TRANSIT_TO_BACK]) this transition will apply a minimize animation to
* that window.
*/
-class DesktopAppLaunchTransition(private val context: Context, private val mainExecutor: Executor) :
- RemoteTransitionStub() {
+class DesktopAppLaunchTransition(
+ private val context: Context,
+ private val mainExecutor: Executor,
+ private val launchType: AppLaunchType,
+) : RemoteTransitionStub() {
+
+ enum class AppLaunchType(
+ val boundsAnimationParams: WindowAnimator.BoundsAnimationParams,
+ val alphaDurationMs: Long,
+ ) {
+ LAUNCH(launchBoundsAnimationDef, /* alphaDurationMs= */ 200L),
+ UNMINIMIZE(unminimizeBoundsAnimationDef, /* alphaDurationMs= */ 100L),
+ }
override fun startAnimation(
token: IBinder,
@@ -105,18 +118,24 @@
val boundsAnimator =
WindowAnimator.createBoundsAnimator(
context.resources.displayMetrics,
- launchBoundsAnimationDef,
+ launchType.boundsAnimationParams,
change,
transaction,
)
val alphaAnimator =
ValueAnimator.ofFloat(0f, 1f).apply {
- duration = LAUNCH_ANIM_ALPHA_DURATION_MS
+ duration = launchType.alphaDurationMs
interpolator = Interpolators.LINEAR
addUpdateListener { animation ->
transaction.setAlpha(change.leash, animation.animatedValue as Float).apply()
}
}
+ val clipRect = Rect(change.endAbsBounds).apply { offsetTo(0, 0) }
+ transaction.setCrop(change.leash, clipRect)
+ transaction.setCornerRadius(
+ change.leash,
+ ScreenDecorationsUtils.getWindowCornerRadius(context),
+ )
return AnimatorSet().apply {
playTogether(boundsAnimator, alphaAnimator)
addListener(onEnd = { animation -> onAnimFinish(animation) })
@@ -124,13 +143,18 @@
}
companion object {
- private val LAUNCH_CHANGE_MODES = intArrayOf(TRANSIT_OPEN, TRANSIT_TO_FRONT)
-
- private const val LAUNCH_ANIM_ALPHA_DURATION_MS = 100L
- private const val MINIMIZE_ANIM_ALPHA_DURATION_MS = 100L
+ val LAUNCH_CHANGE_MODES = intArrayOf(TRANSIT_OPEN, TRANSIT_TO_FRONT)
private val launchBoundsAnimationDef =
WindowAnimator.BoundsAnimationParams(
+ durationMs = 600,
+ startOffsetYDp = 36f,
+ startScale = 0.95f,
+ interpolator = Interpolators.STANDARD_DECELERATE,
+ )
+
+ private val unminimizeBoundsAnimationDef =
+ WindowAnimator.BoundsAnimationParams(
durationMs = 300,
startOffsetYDp = 12f,
startScale = 0.97f,
diff --git a/quickstep/src/com/android/launcher3/model/WidgetsPredictionUpdateTask.java b/quickstep/src/com/android/launcher3/model/WidgetsPredictionUpdateTask.java
index 0395d32..9d9054e 100644
--- a/quickstep/src/com/android/launcher3/model/WidgetsPredictionUpdateTask.java
+++ b/quickstep/src/com/android/launcher3/model/WidgetsPredictionUpdateTask.java
@@ -16,8 +16,12 @@
package com.android.launcher3.model;
import static com.android.launcher3.Flags.enableCategorizedWidgetSuggestions;
+import static com.android.launcher3.Flags.enableTieredWidgetsByDefaultInPicker;
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_WIDGETS_PREDICTION;
+import static java.util.stream.Collectors.groupingBy;
+import static java.util.stream.Collectors.toMap;
+
import android.app.prediction.AppTarget;
import android.content.ComponentName;
import android.content.Context;
@@ -26,6 +30,7 @@
import androidx.annotation.NonNull;
import com.android.launcher3.LauncherModel.ModelUpdateTask;
+import com.android.launcher3.R;
import com.android.launcher3.model.BgDataModel.FixedContainerItems;
import com.android.launcher3.model.QuickstepModelDelegate.PredictorState;
import com.android.launcher3.model.data.ItemInfo;
@@ -34,8 +39,10 @@
import com.android.launcher3.widget.picker.WidgetRecommendationCategoryProvider;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
import java.util.Map;
+import java.util.Random;
import java.util.Set;
import java.util.function.Predicate;
import java.util.stream.Collectors;
@@ -60,33 +67,72 @@
@Override
public void execute(@NonNull ModelTaskController taskController, @NonNull BgDataModel dataModel,
@NonNull AllAppsList apps) {
+ Predicate<WidgetItem> predictedWidgetsFilter = enableTieredWidgetsByDefaultInPicker()
+ ? dataModel.widgetsModel.getPredictedWidgetsFilter() : null;
Set<ComponentKey> widgetsInWorkspace = dataModel.appWidgets.stream().map(
widget -> new ComponentKey(widget.providerName, widget.user)).collect(
Collectors.toSet());
- Predicate<WidgetItem> notOnWorkspace = w -> !widgetsInWorkspace.contains(w);
- Map<ComponentKey, WidgetItem> allWidgets =
- dataModel.widgetsModel.getWidgetsByComponentKey();
+
+ // Widgets (excluding shortcuts & already added widgets) that belong to apps eligible for
+ // being in predictions.
+ Map<ComponentKey, WidgetItem> allEligibleWidgets =
+ dataModel.widgetsModel.getWidgetsByComponentKey()
+ .entrySet()
+ .stream()
+ .filter(entry -> entry.getValue().widgetInfo != null
+ && !widgetsInWorkspace.contains(entry.getValue())
+ && (predictedWidgetsFilter == null
+ || predictedWidgetsFilter.test(entry.getValue()))
+ ).collect(toMap(Map.Entry::getKey, Map.Entry::getValue));
+
+ Context context = taskController.getApp().getContext();
List<WidgetItem> servicePredictedItems = new ArrayList<>();
+ List<String> addedWidgetApps = new ArrayList<>();
for (AppTarget app : mTargets) {
ComponentKey componentKey = new ComponentKey(
new ComponentName(app.getPackageName(), app.getClassName()), app.getUser());
- WidgetItem widget = allWidgets.get(componentKey);
- if (widget == null) {
+ WidgetItem widget = allEligibleWidgets.get(componentKey);
+ if (widget == null) { // widget not eligible.
continue;
}
String className = app.getClassName();
if (!TextUtils.isEmpty(className)) {
- if (notOnWorkspace.test(widget)) {
- servicePredictedItems.add(widget);
- }
+ servicePredictedItems.add(widget);
+ addedWidgetApps.add(componentKey.componentName.getPackageName());
+ }
+ }
+
+ int minPredictionCount = context.getResources().getInteger(
+ R.integer.widget_predictions_min_count);
+ if (enableTieredWidgetsByDefaultInPicker()
+ && servicePredictedItems.size() < minPredictionCount) {
+ // Eligible apps that aren't already part of predictions.
+ Map<String, List<WidgetItem>> eligibleWidgetsByApp =
+ allEligibleWidgets.values().stream()
+ .filter(w -> !addedWidgetApps.contains(
+ w.componentName.getPackageName()))
+ .collect(groupingBy(w -> w.componentName.getPackageName()));
+
+ // Randomize available apps list
+ List<String> appPackages = new ArrayList<>(eligibleWidgetsByApp.keySet());
+ Collections.shuffle(appPackages);
+
+ int widgetsToAdd = minPredictionCount - servicePredictedItems.size();
+ for (String appPackage : appPackages) {
+ if (widgetsToAdd <= 0) break;
+
+ List<WidgetItem> widgetsForApp = eligibleWidgetsByApp.get(appPackage);
+ int index = new Random().nextInt(widgetsForApp.size());
+ // Add a random widget from the app.
+ servicePredictedItems.add(widgetsForApp.get(index));
+ widgetsToAdd--;
}
}
List<ItemInfo> items;
if (enableCategorizedWidgetSuggestions()) {
- Context context = taskController.getApp().getContext();
WidgetRecommendationCategoryProvider categoryProvider =
WidgetRecommendationCategoryProvider.newInstance(context);
items = servicePredictedItems.stream()
diff --git a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchViewController.java b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchViewController.java
index 7a63f74..390112e 100644
--- a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchViewController.java
@@ -36,6 +36,7 @@
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AnimatorListeners;
import com.android.launcher3.desktop.DesktopAppLaunchTransition;
+import com.android.launcher3.desktop.DesktopAppLaunchTransition.AppLaunchType;
import com.android.launcher3.taskbar.overlay.TaskbarOverlayContext;
import com.android.launcher3.taskbar.overlay.TaskbarOverlayDragLayer;
import com.android.launcher3.util.DisplayController;
@@ -47,7 +48,6 @@
import com.android.systemui.shared.recents.model.ThumbnailData;
import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
import com.android.systemui.shared.system.QuickStepContract;
-import com.android.window.flags.Flags;
import java.io.PrintWriter;
import java.util.List;
@@ -247,11 +247,13 @@
return -1;
}
RemoteTransition remoteTransition = slideInTransition;
- if (mOnDesktop && task.task1.isMinimized
- && Flags.enableDesktopAppLaunchAlttabTransitions()) {
+ if (mOnDesktop
+ && mControllers.taskbarActivityContext.canUnminimizeDesktopTask(task.task1.key.id)
+ ) {
// This app is being unminimized - use our own transition runner.
remoteTransition = new RemoteTransition(
- new DesktopAppLaunchTransition(context, MAIN_EXECUTOR));
+ new DesktopAppLaunchTransition(
+ context, MAIN_EXECUTOR, AppLaunchType.UNMINIMIZE));
}
mControllers.taskbarActivityContext.handleGroupTaskLaunch(
task,
diff --git a/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
index 7d8e93c..fbd1b6e 100644
--- a/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
@@ -49,6 +49,7 @@
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_PINNING;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SHORTCUT_HELPER_SHOWING;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_VOICE_INTERACTION_WINDOW_SHOWING;
+import static com.android.window.flags.Flags.predictiveBackThreeButtonNav;
import static com.android.wm.shell.Flags.enableBubbleBarInPersistentTaskBar;
import android.animation.Animator;
@@ -71,8 +72,10 @@
import android.graphics.drawable.RotateDrawable;
import android.inputmethodservice.InputMethodService;
import android.os.Handler;
+import android.os.SystemClock;
import android.util.Property;
import android.view.Gravity;
+import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnAttachStateChangeListener;
@@ -843,12 +846,43 @@
buttonView.setImageResource(drawableId);
buttonView.setContentDescription(parent.getContext().getString(
navButtonController.getButtonContentDescription(buttonType)));
- buttonView.setOnClickListener(view -> navButtonController.onButtonClick(buttonType, view));
- buttonView.setOnLongClickListener(view ->
- navButtonController.onButtonLongClick(buttonType, view));
+ if (predictiveBackThreeButtonNav() && buttonType == BUTTON_BACK) {
+ // set up special touch listener for back button to support predictive back
+ setBackButtonTouchListener(buttonView, navButtonController);
+ } else {
+ buttonView.setOnClickListener(view ->
+ navButtonController.onButtonClick(buttonType, view));
+ buttonView.setOnLongClickListener(view ->
+ navButtonController.onButtonLongClick(buttonType, view));
+ }
return buttonView;
}
+ private void setBackButtonTouchListener(View buttonView,
+ TaskbarNavButtonController navButtonController) {
+ buttonView.setOnTouchListener((v, event) -> {
+ if (event.getAction() == MotionEvent.ACTION_MOVE) return false;
+ long time = SystemClock.uptimeMillis();
+ int action = event.getAction();
+ KeyEvent keyEvent = new KeyEvent(time, time,
+ action == MotionEvent.ACTION_DOWN ? KeyEvent.ACTION_DOWN : KeyEvent.ACTION_UP,
+ KeyEvent.KEYCODE_BACK, 0);
+ if (event.getAction() == MotionEvent.ACTION_CANCEL) {
+ keyEvent.cancel();
+ }
+ navButtonController.executeBack(keyEvent);
+
+ if (action == MotionEvent.ACTION_UP) {
+ buttonView.performClick();
+ }
+ return false;
+ });
+ buttonView.setOnLongClickListener((view) -> {
+ navButtonController.onButtonLongClick(BUTTON_BACK, view);
+ return false;
+ });
+ }
+
private ImageView addButton(ViewGroup parent, @IdRes int id, @LayoutRes int layoutId) {
ImageView buttonView = (ImageView) mContext.getLayoutInflater()
.inflate(layoutId, parent, false);
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
index e22de06..82acc0c 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
@@ -59,6 +59,7 @@
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.hardware.display.DisplayManager;
+import android.os.Bundle;
import android.os.IRemoteCallback;
import android.os.Process;
import android.os.Trace;
@@ -72,6 +73,7 @@
import android.view.WindowManager;
import android.widget.FrameLayout;
import android.widget.Toast;
+import android.window.DesktopModeFlags;
import android.window.RemoteTransition;
import androidx.annotation.NonNull;
@@ -89,6 +91,8 @@
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.apppairs.AppPairIcon;
import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.desktop.DesktopAppLaunchTransition;
+import com.android.launcher3.desktop.DesktopAppLaunchTransition.AppLaunchType;
import com.android.launcher3.dot.DotInfo;
import com.android.launcher3.folder.Folder;
import com.android.launcher3.folder.FolderIcon;
@@ -282,6 +286,7 @@
BubbleBarController.onTaskbarRecreated();
if (BubbleBarController.isBubbleBarEnabled()
&& !mDeviceProfile.isPhone
+ && !mDeviceProfile.isVerticalBarLayout()
&& bubbleBarView != null
) {
Optional<BubbleStashedHandleViewController> bubbleHandleController = Optional.empty();
@@ -859,6 +864,33 @@
return makeDefaultActivityOptions(SPLASH_SCREEN_STYLE_UNDEFINED);
}
+ private ActivityOptionsWrapper getActivityLaunchDesktopOptions(ItemInfo info) {
+ if (!DesktopModeFlags.ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS.isTrue()) {
+ return null;
+ }
+ if (!areDesktopTasksVisible()) {
+ return null;
+ }
+ BubbleTextView.RunningAppState appState =
+ mControllers.taskbarRecentAppsController.getDesktopItemState(info);
+ AppLaunchType launchType = null;
+ switch (appState) {
+ case RUNNING:
+ return null;
+ case MINIMIZED:
+ launchType = AppLaunchType.UNMINIMIZE;
+ break;
+ case NOT_RUNNING:
+ launchType = AppLaunchType.LAUNCH;
+ break;
+ }
+ ActivityOptions options = ActivityOptions.makeRemoteTransition(
+ new RemoteTransition(
+ new DesktopAppLaunchTransition(
+ /* context= */ this, getMainExecutor(), launchType)));
+ return new ActivityOptionsWrapper(options, new RunnableList());
+ }
+
/**
* Sets a new data-source for this taskbar instance
*/
@@ -1219,10 +1251,10 @@
mControllers.keyboardQuickSwitchController.closeQuickSwitchView(false);
if (tag instanceof GroupTask groupTask) {
- handleGroupTaskLaunch(
- groupTask,
- /* remoteTransition= */ null,
- areDesktopTasksVisible());
+ RemoteTransition remoteTransition =
+ (areDesktopTasksVisible() && canUnminimizeDesktopTask(groupTask.task1.key.id))
+ ? createUnminimizeRemoteTransition() : null;
+ handleGroupTaskLaunch(groupTask, remoteTransition, areDesktopTasksVisible());
mControllers.taskbarStashController.updateAndAnimateTransientTaskbar(true);
} else if (tag instanceof FolderInfo) {
// Tapping an expandable folder icon on Taskbar
@@ -1240,9 +1272,11 @@
mControllers.taskbarStashController.updateAndAnimateTransientTaskbar(true);
}
} else if (tag instanceof TaskItemInfo info) {
+ RemoteTransition remoteTransition = canUnminimizeDesktopTask(info.getTaskId())
+ ? createUnminimizeRemoteTransition() : null;
UI_HELPER_EXECUTOR.execute(() ->
SystemUiProxy.INSTANCE.get(this).showDesktopApp(
- info.getTaskId(), /* remoteTransition= */ null));
+ info.getTaskId(), remoteTransition));
mControllers.taskbarStashController.updateAndAnimateTransientTaskbar(
/* stash= */ true);
} else if (tag instanceof WorkspaceItemInfo) {
@@ -1361,8 +1395,7 @@
return;
}
if (onDesktop) {
- boolean useRemoteTransition = task.task1.isMinimized
- && com.android.window.flags.Flags.enableDesktopAppLaunchAlttabTransitions();
+ boolean useRemoteTransition = canUnminimizeDesktopTask(task.task1.key.id);
UI_HELPER_EXECUTOR.execute(() -> {
if (onStartCallback != null) {
onStartCallback.run();
@@ -1389,6 +1422,20 @@
mControllers.uiController.launchSplitTasks(task, remoteTransition);
}
+ /** Returns whether the given task is minimized and can be unminimized. */
+ public boolean canUnminimizeDesktopTask(int taskId) {
+ BubbleTextView.RunningAppState runningAppState =
+ mControllers.taskbarRecentAppsController.getRunningAppState(taskId);
+ return runningAppState == BubbleTextView.RunningAppState.MINIMIZED
+ && DesktopModeFlags.ENABLE_DESKTOP_APP_LAUNCH_ALTTAB_TRANSITIONS.isTrue();
+ }
+
+ private RemoteTransition createUnminimizeRemoteTransition() {
+ return new RemoteTransition(
+ new DesktopAppLaunchTransition(
+ this, getMainExecutor(), AppLaunchType.UNMINIMIZE));
+ }
+
/**
* Runs when the user taps a Taskbar icon in TaskbarActivityContext (Overview or inside an app),
* and calls the appropriate method to animate and launch.
@@ -1487,25 +1534,31 @@
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
try {
TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "start: taskbarAppIcon");
- if (info.user.equals(Process.myUserHandle())) {
- // TODO(b/216683257): Use startActivityForResult for search results that require it.
- if (taskInRecents != null) {
- // Re launch instance from recents
- ActivityOptionsWrapper opts = getActivityLaunchOptions(null, info);
- opts.options.setLaunchDisplayId(
- getDisplay() == null ? DEFAULT_DISPLAY : getDisplay().getDisplayId());
- if (ActivityManagerWrapper.getInstance()
- .startActivityFromRecents(taskInRecents.key, opts.options)) {
- mControllers.uiController.getRecentsView()
- .addSideTaskLaunchCallback(opts.onEndCallback);
- return;
- }
- }
- startActivity(intent);
- } else {
+ if (!info.user.equals(Process.myUserHandle())) {
+ // TODO b/376819104: support Desktop launch animations for apps in managed profiles
getSystemService(LauncherApps.class).startMainActivity(
intent.getComponent(), info.user, intent.getSourceBounds(), null);
+ return;
}
+ // TODO(b/216683257): Use startActivityForResult for search results that require it.
+ if (taskInRecents != null) {
+ // Re launch instance from recents
+ ActivityOptionsWrapper opts = getActivityLaunchOptions(null, info);
+ opts.options.setLaunchDisplayId(
+ getDisplay() == null ? DEFAULT_DISPLAY : getDisplay().getDisplayId());
+ if (ActivityManagerWrapper.getInstance()
+ .startActivityFromRecents(taskInRecents.key, opts.options)) {
+ mControllers.uiController.getRecentsView()
+ .addSideTaskLaunchCallback(opts.onEndCallback);
+ return;
+ }
+ }
+ ActivityOptionsWrapper opts = null;
+ if (areDesktopTasksVisible()) {
+ opts = getActivityLaunchDesktopOptions(info);
+ }
+ Bundle optionsBundle = opts == null ? null : opts.options.toBundle();
+ startActivity(intent, optionsBundle);
} catch (NullPointerException | ActivityNotFoundException | SecurityException e) {
Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT)
.show();
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarModelCallbacks.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarModelCallbacks.java
index bdefea6..c20617d 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarModelCallbacks.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarModelCallbacks.java
@@ -42,7 +42,6 @@
import java.util.HashSet;
import java.util.List;
import java.util.Map;
-import java.util.Set;
import java.util.function.Predicate;
/**
@@ -196,26 +195,21 @@
final TaskbarRecentAppsController recentAppsController =
mControllers.taskbarRecentAppsController;
hotseatItemInfos = recentAppsController.updateHotseatItemInfos(hotseatItemInfos);
- Set<Integer> runningTaskIds = recentAppsController.getRunningTaskIds();
- Set<Integer> minimizedTaskIds = recentAppsController.getMinimizedTaskIds();
if (mDeferUpdatesForSUW) {
ItemInfo[] finalHotseatItemInfos = hotseatItemInfos;
mDeferredUpdates = () ->
commitHotseatItemUpdates(finalHotseatItemInfos,
- recentAppsController.getShownTasks(), runningTaskIds,
- minimizedTaskIds);
+ recentAppsController.getShownTasks());
} else {
- commitHotseatItemUpdates(hotseatItemInfos,
- recentAppsController.getShownTasks(), runningTaskIds, minimizedTaskIds);
+ commitHotseatItemUpdates(hotseatItemInfos, recentAppsController.getShownTasks());
}
}
- private void commitHotseatItemUpdates(ItemInfo[] hotseatItemInfos, List<GroupTask> recentTasks,
- Set<Integer> runningTaskIds, Set<Integer> minimizedTaskIds) {
+ private void commitHotseatItemUpdates(
+ ItemInfo[] hotseatItemInfos, List<GroupTask> recentTasks) {
mContainer.updateHotseatItems(hotseatItemInfos, recentTasks);
- mControllers.taskbarViewController.updateIconViewsRunningStates(
- runningTaskIds, minimizedTaskIds);
+ mControllers.taskbarViewController.updateIconViewsRunningStates();
}
/**
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java
index 8947914..0f9ede9 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java
@@ -16,6 +16,8 @@
package com.android.launcher3.taskbar;
+import static android.view.MotionEvent.ACTION_UP;
+
import static com.android.internal.app.AssistUtils.INVOCATION_TYPE_HOME_BUTTON_LONG_PRESS;
import static com.android.internal.app.AssistUtils.INVOCATION_TYPE_KEY;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_A11Y_BUTTON_LONGPRESS;
@@ -31,12 +33,14 @@
import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_HOME_KEY;
import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_PINNING;
+import static com.android.window.flags.Flags.predictiveBackThreeButtonNav;
import android.content.Context;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.view.HapticFeedbackConstants;
+import android.view.KeyEvent;
import android.view.View;
import android.view.inputmethod.Flags;
@@ -141,10 +145,7 @@
view.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
switch (buttonType) {
case BUTTON_BACK:
- logEvent(LAUNCHER_TASKBAR_BACK_BUTTON_TAP);
- mContextualEduStatsManager.updateEduStats(/* isTrackpadGesture= */ false,
- GestureType.BACK);
- executeBack();
+ executeBack(/* keyEvent */ null);
break;
case BUTTON_HOME:
logEvent(LAUNCHER_TASKBAR_HOME_BUTTON_TAP);
@@ -182,7 +183,9 @@
// Provide the same haptic feedback that the system offers for long press.
// The haptic feedback from long pressing on the home button is handled by circle to search.
- if (buttonType != BUTTON_HOME) {
+ // There are no haptics for long pressing the back button if predictive back is enabled
+ if (buttonType != BUTTON_HOME
+ && (!predictiveBackThreeButtonNav() || buttonType != BUTTON_BACK)) {
view.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
}
switch (buttonType) {
@@ -320,8 +323,13 @@
mCallbacks.onToggleOverview();
}
- private void executeBack() {
- mSystemUiProxy.onBackPressed();
+ void executeBack(@Nullable KeyEvent keyEvent) {
+ if (keyEvent == null || (keyEvent.getAction() == ACTION_UP && !keyEvent.isCanceled())) {
+ logEvent(LAUNCHER_TASKBAR_BACK_BUTTON_TAP);
+ mContextualEduStatsManager.updateEduStats(/* isTrackpadGesture= */ false,
+ GestureType.BACK);
+ }
+ mSystemUiProxy.onBackEvent(keyEvent);
}
private void onImeSwitcherPress() {
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarRecentAppsController.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarRecentAppsController.kt
index 3e8b615..3d57de4 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarRecentAppsController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarRecentAppsController.kt
@@ -18,6 +18,7 @@
import android.content.Context
import android.window.DesktopModeFlags
import androidx.annotation.VisibleForTesting
+import com.android.launcher3.BubbleTextView.RunningAppState
import com.android.launcher3.Flags.enableRecentsInTaskbar
import com.android.launcher3.model.data.ItemInfo
import com.android.launcher3.model.data.TaskItemInfo
@@ -72,6 +73,43 @@
var shownTasks: List<GroupTask> = emptyList()
private set
+ /**
+ * Returns the state of the most active Desktop task represented by the given [ItemInfo].
+ *
+ * If there are several tasks represented by the same [ItemInfo] we return the most active one,
+ * i.e. we return [DesktopAppState.RUNNING] over [DesktopAppState.MINIMIZED], and
+ * [DesktopAppState.MINIMIZED] over [DesktopAppState.NOT_RUNNING].
+ */
+ fun getDesktopItemState(itemInfo: ItemInfo?): RunningAppState {
+ val packageName = itemInfo?.getTargetPackage() ?: return RunningAppState.NOT_RUNNING
+ return getDesktopAppState(packageName, itemInfo.user.identifier)
+ }
+
+ private fun getDesktopAppState(packageName: String, userId: Int): RunningAppState {
+ val tasks = desktopTask?.tasks ?: return RunningAppState.NOT_RUNNING
+ val appTasks =
+ tasks.filter { task ->
+ packageName == task.key.packageName && task.key.userId == userId
+ }
+ if (appTasks.find { getRunningAppState(it.key.id) == RunningAppState.RUNNING } != null) {
+ return RunningAppState.RUNNING
+ }
+ if (appTasks.find { getRunningAppState(it.key.id) == RunningAppState.MINIMIZED } != null) {
+ return RunningAppState.MINIMIZED
+ }
+ return RunningAppState.NOT_RUNNING
+ }
+
+ /** Get the [RunningAppState] for the given task. */
+ fun getRunningAppState(taskId: Int): RunningAppState {
+ return when (taskId) {
+ in minimizedTaskIds -> RunningAppState.MINIMIZED
+ in runningTaskIds -> RunningAppState.RUNNING
+ else -> RunningAppState.NOT_RUNNING
+ }
+ }
+
+ @VisibleForTesting
val runningTaskIds: Set<Int>
/**
* Returns the task IDs of apps that should be indicated as "running" to the user.
@@ -88,6 +126,7 @@
return tasks.map { task -> task.key.id }.toSet()
}
+ @VisibleForTesting
val minimizedTaskIds: Set<Int>
/**
* Returns the task IDs for the tasks that should be indicated as "minimized" to the user.
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimViewController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimViewController.java
index bf086b4..4a7e4f0 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimViewController.java
@@ -155,7 +155,7 @@
}
private void onClick() {
- SystemUiProxy.INSTANCE.get(mActivity).onBackPressed();
+ SystemUiProxy.INSTANCE.get(mActivity).onBackEvent(null);
}
@Override
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
index 87e19be..494c472 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
@@ -623,12 +623,10 @@
* Updates which icons are marked as running or minimized given the Sets of currently running
* and minimized tasks.
*/
- public void updateIconViewsRunningStates(Set<Integer> runningTaskIds,
- Set<Integer> minimizedTaskIds) {
+ public void updateIconViewsRunningStates() {
for (View iconView : getIconViews()) {
if (iconView instanceof BubbleTextView btv) {
- btv.updateRunningState(
- getRunningAppState(btv, runningTaskIds, minimizedTaskIds));
+ btv.updateRunningState(getRunningAppState(btv));
}
}
}
@@ -651,26 +649,15 @@
return pinnedAppsWithTasks;
}
- private BubbleTextView.RunningAppState getRunningAppState(
- BubbleTextView btv,
- Set<Integer> runningTaskIds,
- Set<Integer> minimizedTaskIds) {
+ private BubbleTextView.RunningAppState getRunningAppState(BubbleTextView btv) {
Object tag = btv.getTag();
if (tag instanceof TaskItemInfo itemInfo) {
- if (minimizedTaskIds.contains(itemInfo.getTaskId())) {
- return BubbleTextView.RunningAppState.MINIMIZED;
- }
- if (runningTaskIds.contains(itemInfo.getTaskId())) {
- return BubbleTextView.RunningAppState.RUNNING;
- }
+ return mControllers.taskbarRecentAppsController.getRunningAppState(
+ itemInfo.getTaskId());
}
if (tag instanceof GroupTask groupTask && !groupTask.hasMultipleTasks()) {
- if (minimizedTaskIds.contains(groupTask.task1.key.id)) {
- return BubbleTextView.RunningAppState.MINIMIZED;
- }
- if (runningTaskIds.contains(groupTask.task1.key.id)) {
- return BubbleTextView.RunningAppState.RUNNING;
- }
+ return mControllers.taskbarRecentAppsController.getRunningAppState(
+ groupTask.task1.key.id);
}
return BubbleTextView.RunningAppState.NOT_RUNNING;
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java
index 30e4e47..3e2c4b9 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java
@@ -297,9 +297,7 @@
Log.e(TAG, "Could not instantiate BubbleBarBubble for " + bubbleInfos.get(i));
continue;
}
- addBubbleInternally(bubble, /* showAppBadge = */
- mBubbleBarViewController.isExpanded() || i == 0,
- /* isExpanding = */ false, /* suppressAnimation = */ true);
+ addBubbleInternally(bubble, /* isExpanding= */ false, /* suppressAnimation= */ true);
}
}
@@ -390,8 +388,7 @@
for (int i = update.currentBubbles.size() - 1; i >= 0; i--) {
BubbleBarBubble bubble = update.currentBubbles.get(i);
if (bubble != null) {
- addBubbleInternally(bubble, /* showAppBadge = */ !isCollapsed || i == 0,
- isExpanding, suppressAnimation);
+ addBubbleInternally(bubble, isExpanding, suppressAnimation);
if (isCollapsed) {
// If we're collapsed, the most recently added bubble will be selected.
bubbleToSelect = bubble;
@@ -420,8 +417,13 @@
// Updates mean the dot state may have changed; any other changes were updated in
// the populateBubble step.
BubbleBarBubble bb = mBubbles.get(update.updatedBubble.getKey());
- mBubbleBarViewController.animateBubbleNotification(
- bb, /* isExpanding= */ false, /* isUpdate= */ true);
+ if (suppressAnimation) {
+ // since we're not animating this update, we should update the dot visibility here.
+ bb.getView().updateDotVisibility(/* animate= */ false);
+ } else {
+ mBubbleBarViewController.animateBubbleNotification(
+ bb, /* isExpanding= */ false, /* isUpdate= */ true);
+ }
}
if (update.bubbleKeysInOrder != null && !update.bubbleKeysInOrder.isEmpty()) {
// Create the new list
@@ -563,10 +565,8 @@
}
}
- private void addBubbleInternally(BubbleBarBubble bubble, boolean showAppBadge,
- boolean isExpanding, boolean suppressAnimation) {
- //TODO(b/360652359): remove setting scale to the app badge once issue is fixed
- bubble.getView().setBadgeScale(showAppBadge ? 1 : 0);
+ private void addBubbleInternally(BubbleBarBubble bubble, boolean isExpanding,
+ boolean suppressAnimation) {
mBubbles.put(bubble.getKey(), bubble);
mBubbleBarViewController.addBubble(bubble, isExpanding, suppressAnimation);
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java
index d91d10a..c0a76a8 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java
@@ -791,6 +791,7 @@
updateLayoutParams();
updateBubbleAccessibilityStates();
updateContentDescription();
+ updateDotsAndBadgesIfCollapsed();
}
/** Removes the given bubble from the bubble bar. */
@@ -856,7 +857,7 @@
updateBubbleAccessibilityStates();
updateContentDescription();
mDismissedByDragBubbleView = null;
- updateNotificationDotsIfCollapsed();
+ updateDotsAndBadgesIfCollapsed();
}
/**
@@ -886,17 +887,23 @@
return childViews;
}
- private void updateNotificationDotsIfCollapsed() {
+ private void updateDotsAndBadgesIfCollapsed() {
if (isExpanded()) {
return;
}
for (int i = 0; i < getChildCount(); i++) {
BubbleView bubbleView = (BubbleView) getChildAt(i);
- // when we're collapsed, the first bubble should show the dot if it has it. the rest of
- // the bubbles should hide their dots.
- if (i == 0 && bubbleView.hasUnseenContent()) {
- bubbleView.showDotIfNeeded(/* animate= */ true);
+ // when we're collapsed, the first bubble should show the badge and the dot if it has
+ // it. the rest of the bubbles should hide their badges and dots.
+ if (i == 0) {
+ bubbleView.showBadge();
+ if (bubbleView.hasUnseenContent()) {
+ bubbleView.showDotIfNeeded(/* animate= */ true);
+ } else {
+ bubbleView.hideDot();
+ }
} else {
+ bubbleView.hideBadge();
bubbleView.hideDot();
}
}
@@ -1100,7 +1107,7 @@
}
updateBubblesLayoutProperties(mBubbleBarLocation);
updateContentDescription();
- updateNotificationDotsIfCollapsed();
+ updateDotsAndBadgesIfCollapsed();
}
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleView.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleView.java
index 114edf4..0ea4222 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleView.java
@@ -49,6 +49,8 @@
public class BubbleView extends ConstraintLayout {
public static final int DEFAULT_PATH_SIZE = 100;
+ /** Duration for animating the scale of the dot and badge. */
+ private static final int SCALE_ANIMATION_DURATION_MS = 200;
private final ImageView mBubbleIcon;
private final ImageView mAppIcon;
@@ -316,12 +318,37 @@
}
void setBadgeScale(float fraction) {
- if (mAppIcon.getVisibility() == VISIBLE) {
+ if (hasBadge()) {
mAppIcon.setScaleX(fraction);
mAppIcon.setScaleY(fraction);
}
}
+ void showBadge() {
+ animateBadgeScale(1);
+ }
+
+ void hideBadge() {
+ animateBadgeScale(0);
+ }
+
+ private boolean hasBadge() {
+ return mAppIcon.getVisibility() == VISIBLE;
+ }
+
+ private void animateBadgeScale(float scale) {
+ if (!hasBadge()) {
+ return;
+ }
+ mAppIcon.clearAnimation();
+ mAppIcon.animate()
+ .setDuration(SCALE_ANIMATION_DURATION_MS)
+ .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
+ .scaleX(scale)
+ .scaleY(scale)
+ .start();
+ }
+
/** Suppresses or un-suppresses drawing the dot due to an update for this bubble. */
public void suppressDotForBubbleUpdate(boolean suppress) {
mDotSuppressedForBubbleUpdate = suppress;
@@ -409,7 +436,7 @@
clearAnimation();
animate()
- .setDuration(200)
+ .setDuration(SCALE_ANIMATION_DURATION_MS)
.setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
.setUpdateListener((valueAnimator) -> {
float fraction = valueAnimator.getAnimatedFraction();
diff --git a/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayController.java b/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayController.java
index 7eb34a5..79cb748 100644
--- a/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayController.java
@@ -35,6 +35,7 @@
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.Flags;
import com.android.launcher3.taskbar.TaskbarActivityContext;
import com.android.launcher3.taskbar.TaskbarControllers;
import com.android.systemui.shared.system.TaskStackChangeListener;
@@ -216,6 +217,13 @@
@Override
protected void handleClose(boolean animate) {
if (!mIsOpen) return;
+ if (Flags.taskbarOverflow()) {
+ // Mark the view closed before attempting to remove it, so the drag layer does not
+ // schedule another call to close. Needed for taskbar overflow in case the KQS
+ // view shown for taskbar overflow needs to be reshown - delayed close call would
+ // would result in reshown KQS view getting hidden.
+ mIsOpen = false;
+ }
mTaskbarContext.getDragLayer().removeView(this);
Optional.ofNullable(mOverlayContext).ifPresent(c -> {
if (canCloseWindow()) {
diff --git a/quickstep/src/com/android/quickstep/SystemUiProxy.java b/quickstep/src/com/android/quickstep/SystemUiProxy.java
index 73e22bb..c618422 100644
--- a/quickstep/src/com/android/quickstep/SystemUiProxy.java
+++ b/quickstep/src/com/android/quickstep/SystemUiProxy.java
@@ -43,6 +43,7 @@
import android.os.UserHandle;
import android.util.Log;
import android.view.IRemoteAnimationRunner;
+import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.RemoteAnimationTarget;
import android.view.SurfaceControl;
@@ -210,10 +211,10 @@
}
@Override
- public void onBackPressed() {
+ public void onBackEvent(KeyEvent backEvent) {
if (mSystemUiProxy != null) {
try {
- mSystemUiProxy.onBackPressed();
+ mSystemUiProxy.onBackEvent(backEvent);
} catch (RemoteException e) {
Log.w(TAG, "Failed call onBackPressed", e);
}
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index 5b085d2..fcc5121 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -74,6 +74,7 @@
import android.view.View;
import androidx.annotation.BinderThread;
+import androidx.annotation.MainThread;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.UiThread;
@@ -550,11 +551,15 @@
@Override
public void onInputDeviceAdded(int deviceId) {
if (isTrackpadDevice(deviceId)) {
- boolean wasEmpty = mTrackpadsConnected.isEmpty();
- mTrackpadsConnected.add(deviceId);
- if (wasEmpty) {
- update();
- }
+ // This updates internal TIS state so it needs to also run on the main
+ // thread.
+ MAIN_EXECUTOR.execute(() -> {
+ boolean wasEmpty = mTrackpadsConnected.isEmpty();
+ mTrackpadsConnected.add(deviceId);
+ if (wasEmpty) {
+ update();
+ }
+ });
}
}
@@ -564,12 +569,17 @@
@Override
public void onInputDeviceRemoved(int deviceId) {
- mTrackpadsConnected.remove(deviceId);
- if (mTrackpadsConnected.isEmpty()) {
- update();
- }
+ // This updates internal TIS state so it needs to also run on the main
+ // thread.
+ MAIN_EXECUTOR.execute(() -> {
+ mTrackpadsConnected.remove(deviceId);
+ if (mTrackpadsConnected.isEmpty()) {
+ update();
+ }
+ });
}
+ @MainThread
private void update() {
if (mInputMonitorCompat != null && !mTrackpadsConnected.isEmpty()) {
// Don't destroy and reinitialize input monitor due to trackpad
@@ -580,6 +590,7 @@
}
private boolean isTrackpadDevice(int deviceId) {
+ // This is a blocking binder call that should run on a bg thread.
InputDevice inputDevice = mInputManager.getInputDevice(deviceId);
if (inputDevice == null) {
return false;
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarNavButtonControllerTest.java b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarNavButtonControllerTest.java
index 253d921..4b04dba 100644
--- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarNavButtonControllerTest.java
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarNavButtonControllerTest.java
@@ -115,7 +115,7 @@
@Test
public void testPressBack() {
mNavButtonController.onButtonClick(BUTTON_BACK, mockView);
- verify(mockSystemUiProxy, times(1)).onBackPressed();
+ verify(mockSystemUiProxy, times(1)).onBackEvent(null);
}
@Test
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarScrimViewControllerTest.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarScrimViewControllerTest.kt
index 3912051..436dfd3 100644
--- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarScrimViewControllerTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarScrimViewControllerTest.kt
@@ -17,6 +17,7 @@
package com.android.launcher3.taskbar
import android.animation.AnimatorTestRule
+import android.view.KeyEvent
import android.view.View.GONE
import android.view.View.VISIBLE
import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
@@ -47,8 +48,8 @@
TaskbarWindowSandboxContext.create { builder ->
builder.bindSystemUiProxy(
object : SystemUiProxy(this) {
- override fun onBackPressed() {
- super.onBackPressed()
+ override fun onBackEvent(backEvent: KeyEvent?) {
+ super.onBackEvent(backEvent)
backPressed = true
}
}
diff --git a/quickstep/tests/src/com/android/launcher3/model/WidgetsPredicationUpdateTaskTest.java b/quickstep/tests/src/com/android/launcher3/model/WidgetsPredicationUpdateTaskTest.java
index 7b57c81..c53c177 100644
--- a/quickstep/tests/src/com/android/launcher3/model/WidgetsPredicationUpdateTaskTest.java
+++ b/quickstep/tests/src/com/android/launcher3/model/WidgetsPredicationUpdateTaskTest.java
@@ -33,6 +33,7 @@
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
import android.app.prediction.AppTarget;
import android.app.prediction.AppTargetId;
@@ -42,6 +43,8 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.LauncherApps;
import android.os.UserHandle;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.SetFlagsRule;
import android.text.TextUtils;
@@ -62,9 +65,13 @@
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
import java.util.Arrays;
import java.util.List;
+import java.util.Set;
+import java.util.function.Predicate;
import java.util.stream.Collectors;
@SmallTest
@@ -72,6 +79,9 @@
public final class WidgetsPredicationUpdateTaskTest {
@Rule
+ public final MockitoRule mocks = MockitoJUnit.rule();
+
+ @Rule
public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
private AppWidgetProviderInfo mApp1Provider1;
@@ -145,6 +155,7 @@
}
@Test
+ @DisableFlags(Flags.FLAG_ENABLE_TIERED_WIDGETS_BY_DEFAULT_IN_PICKER) // Flag off
public void widgetsRecommendationRan_shouldOnlyReturnNotAddedWidgetsInAppPredictionOrder() {
// Run on model executor so that no other task runs in the middle.
runOnExecutorSync(MODEL_EXECUTOR, () -> {
@@ -184,6 +195,7 @@
}
@Test
+ @DisableFlags(Flags.FLAG_ENABLE_TIERED_WIDGETS_BY_DEFAULT_IN_PICKER) // Flag off
public void widgetsRecommendationRan_shouldReturnEmptyWidgetsWhenEmpty() {
runOnExecutorSync(MODEL_EXECUTOR, () -> {
@@ -213,6 +225,50 @@
});
}
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_TIERED_WIDGETS_BY_DEFAULT_IN_PICKER)
+ public void widgetsRecommendationRan_keepsWidgetsNotOnWorkspace_addsWidgetsFromEligibleApps() {
+ runOnExecutorSync(MODEL_EXECUTOR, () -> {
+ WidgetsFilterDataProvider spiedFilterProvider = spy(
+ mModelHelper.getModel().getWidgetsFilterDataProvider());
+ doAnswer(i -> new Predicate<WidgetItem>() {
+ @Override
+ public boolean test(WidgetItem widgetItem) {
+ // app5's widget is already on workspace, but, app2 is not.
+ // And app4's second widget is also not on workspace.
+ return Set.of("app5", "app2", "app4").contains(
+ widgetItem.componentName.getPackageName());
+ }
+ }).when(spiedFilterProvider).getPredictedWidgetsFilter();
+ mModelHelper.getBgDataModel().widgetsModel.updateWidgetFilters(spiedFilterProvider);
+ // App5's widget that's already on workspace.
+ AppTarget widget1 = new AppTarget(new AppTargetId("app5"), "app5", "provider1",
+ mUserHandle);
+ // App4's widget eligible and not on workspace.
+ AppTarget widget2 = new AppTarget(new AppTargetId("app4"), "app4", "provider2",
+ mUserHandle);
+
+ mCallback.mRecommendedWidgets = null;
+ mModelHelper.getModel().enqueueModelUpdateTask(
+ newWidgetsPredicationTask(List.of(widget1, widget2)));
+ runOnExecutorSync(MAIN_EXECUTOR, () -> {
+ });
+
+ List<PendingAddWidgetInfo> recommendedWidgets = mCallback.mRecommendedWidgets.items
+ .stream()
+ .map(itemInfo -> (PendingAddWidgetInfo) itemInfo)
+ .collect(Collectors.toList());
+ assertThat(recommendedWidgets).hasSize(2);
+ List<ComponentName> componentNames = recommendedWidgets.stream().map(
+ w -> w.componentName).toList();
+ assertThat(componentNames).containsExactly(
+ // Locally added, not on workspace, eligible app per filter
+ mApp2Provider1.provider,
+ // From prediction service, not on workspace, eligible app per filter
+ mApp4Provider2.provider);
+ });
+ }
+
private void assertWidgetInfo(
LauncherAppWidgetProviderInfo actual, AppWidgetProviderInfo expected) {
assertThat(actual.provider).isEqualTo(expected.provider);
diff --git a/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarRecentAppsControllerTest.kt b/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarRecentAppsControllerTest.kt
index b67bc5a..066ddc0 100644
--- a/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarRecentAppsControllerTest.kt
+++ b/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarRecentAppsControllerTest.kt
@@ -26,11 +26,13 @@
import android.platform.test.rule.TestWatcher
import android.testing.AndroidTestingRunner
import com.android.internal.R
+import com.android.launcher3.BubbleTextView.RunningAppState
import com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT
import com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION
import com.android.launcher3.model.data.AppInfo
import com.android.launcher3.model.data.ItemInfo
import com.android.launcher3.model.data.TaskItemInfo
+import com.android.launcher3.model.data.WorkspaceItemInfo
import com.android.quickstep.RecentsModel
import com.android.quickstep.RecentsModel.RecentTasksChangedListener
import com.android.quickstep.TaskIconCache
@@ -77,7 +79,9 @@
private var taskListChangeId: Int = 1
private lateinit var recentAppsController: TaskbarRecentAppsController
- private lateinit var userHandle: UserHandle
+ private lateinit var myUserHandle: UserHandle
+ private val USER_HANDLE_1 = UserHandle.of(1)
+ private val USER_HANDLE_2 = UserHandle.of(2)
private var canShowRunningAndRecentAppsAtInit = true
private var recentTasksChangedListener: RecentTasksChangedListener? = null
@@ -85,7 +89,7 @@
@Before
fun setUp() {
super.setup()
- userHandle = Process.myUserHandle()
+ myUserHandle = Process.myUserHandle()
// Set desktop mode supported
whenever(mockContext.getResources()).thenReturn(mockResources)
@@ -148,6 +152,115 @@
}
@Test
+ fun getDesktopItemState_nullItemInfo_returnsNotRunning() {
+ setInDesktopMode(true)
+ assertThat(recentAppsController.getDesktopItemState(/* itemInfo= */ null))
+ .isEqualTo(RunningAppState.NOT_RUNNING)
+ }
+
+ @Test
+ fun getDesktopItemState_noItemPackage_returnsNotRunning() {
+ setInDesktopMode(true)
+ assertThat(recentAppsController.getDesktopItemState(ItemInfo()))
+ .isEqualTo(RunningAppState.NOT_RUNNING)
+ }
+
+ @Test
+ fun getDesktopItemState_noMatchingTasks_returnsNotRunning() {
+ setInDesktopMode(true)
+ val itemInfo = createItemInfo("package")
+ assertThat(recentAppsController.getDesktopItemState(itemInfo))
+ .isEqualTo(RunningAppState.NOT_RUNNING)
+ }
+
+ @Test
+ fun getDesktopItemState_matchingVisibleTask_returnsVisible() {
+ setInDesktopMode(true)
+ val visibleTask = createTask(id = 1, "visiblePackage", isVisible = true)
+ updateRecentTasks(runningTasks = listOf(visibleTask), recentTaskPackages = emptyList())
+ val itemInfo = createItemInfo("visiblePackage")
+
+ assertThat(recentAppsController.getDesktopItemState(itemInfo))
+ .isEqualTo(RunningAppState.RUNNING)
+ }
+
+ @Test
+ fun getDesktopItemState_matchingMinimizedTask_returnsMinimized() {
+ setInDesktopMode(true)
+ val minimizedTask = createTask(id = 1, "minimizedPackage", isVisible = false)
+ updateRecentTasks(runningTasks = listOf(minimizedTask), recentTaskPackages = emptyList())
+ val itemInfo = createItemInfo("minimizedPackage")
+
+ assertThat(recentAppsController.getDesktopItemState(itemInfo))
+ .isEqualTo(RunningAppState.MINIMIZED)
+ }
+
+ @Test
+ fun getDesktopItemState_matchingMinimizedAndRunningTask_returnsVisible() {
+ setInDesktopMode(true)
+ updateRecentTasks(
+ runningTasks =
+ listOf(
+ createTask(id = 1, "package", isVisible = false),
+ createTask(id = 2, "package", isVisible = true),
+ ),
+ recentTaskPackages = emptyList(),
+ )
+ val itemInfo = createItemInfo("package")
+
+ assertThat(recentAppsController.getDesktopItemState(itemInfo))
+ .isEqualTo(RunningAppState.RUNNING)
+ }
+
+ @Test
+ fun getDesktopItemState_noMatchingUserId_returnsNotRunning() {
+ setInDesktopMode(true)
+ updateRecentTasks(
+ runningTasks =
+ listOf(
+ createTask(id = 1, "package", isVisible = false, USER_HANDLE_1),
+ createTask(id = 2, "package", isVisible = true, USER_HANDLE_1),
+ ),
+ recentTaskPackages = emptyList(),
+ )
+ val itemInfo = createItemInfo("package", USER_HANDLE_2)
+
+ assertThat(recentAppsController.getDesktopItemState(itemInfo))
+ .isEqualTo(RunningAppState.NOT_RUNNING)
+ }
+
+ @Test
+ fun getRunningAppState_taskNotRunningOrMinimized_returnsNotRunning() {
+ setInDesktopMode(true)
+ updateRecentTasks(runningTasks = emptyList(), recentTaskPackages = emptyList())
+
+ assertThat(recentAppsController.getRunningAppState(taskId = 1))
+ .isEqualTo(RunningAppState.NOT_RUNNING)
+ }
+
+ @Test
+ fun getRunningAppState_taskNotVisible_returnsMinimized() {
+ setInDesktopMode(true)
+ val task1 = createTask(id = 1, packageName = RUNNING_APP_PACKAGE_1, isVisible = false)
+ val task2 = createTask(id = 2, packageName = RUNNING_APP_PACKAGE_1, isVisible = true)
+ updateRecentTasks(runningTasks = listOf(task1, task2), recentTaskPackages = emptyList())
+
+ assertThat(recentAppsController.getRunningAppState(taskId = 1))
+ .isEqualTo(RunningAppState.MINIMIZED)
+ }
+
+ @Test
+ fun getRunningAppState_taskVisible_returnsRunning() {
+ setInDesktopMode(true)
+ val task1 = createTask(id = 1, packageName = RUNNING_APP_PACKAGE_1, isVisible = false)
+ val task2 = createTask(id = 2, packageName = RUNNING_APP_PACKAGE_1, isVisible = true)
+ updateRecentTasks(runningTasks = listOf(task1, task2), recentTaskPackages = emptyList())
+
+ assertThat(recentAppsController.getRunningAppState(taskId = 2))
+ .isEqualTo(RunningAppState.RUNNING)
+ }
+
+ @Test
fun updateHotseatItemInfos_cantShowRunning_inDesktopMode_returnsAllHotseatItems() {
recentAppsController.canShowRunningApps = false
setInDesktopMode(true)
@@ -782,7 +895,13 @@
private fun createTestAppInfo(
packageName: String = "testPackageName",
className: String = "testClassName",
- ) = AppInfo(ComponentName(packageName, className), className /* title */, userHandle, Intent())
+ ) =
+ AppInfo(
+ ComponentName(packageName, className),
+ className /* title */,
+ myUserHandle,
+ Intent(),
+ )
private fun createRecentTasksFromPackageNames(packageNames: List<String>): List<GroupTask> {
return packageNames.map { packageName ->
@@ -801,14 +920,19 @@
}
}
- private fun createTask(id: Int, packageName: String, isVisible: Boolean = true): Task {
+ private fun createTask(
+ id: Int,
+ packageName: String,
+ isVisible: Boolean = true,
+ localUserHandle: UserHandle? = null,
+ ): Task {
return Task(
Task.TaskKey(
id,
WINDOWING_MODE_FREEFORM,
Intent().apply { `package` = packageName },
ComponentName(packageName, "TestActivity"),
- userHandle.identifier,
+ localUserHandle?.identifier ?: myUserHandle.identifier,
0,
)
)
@@ -820,6 +944,16 @@
.thenReturn(inDesktopMode)
}
+ private fun createItemInfo(
+ packageName: String,
+ userHandle: UserHandle = myUserHandle,
+ ): ItemInfo {
+ val appInfo = AppInfo()
+ appInfo.intent = Intent().setComponent(ComponentName(packageName, "className"))
+ appInfo.user = userHandle
+ return WorkspaceItemInfo(appInfo)
+ }
+
private val GroupTask.packageNames: List<String>
get() = tasks.map { task -> task.key.packageName }
diff --git a/quickstep/tests/src/com/android/quickstep/AbstractQuickStepTest.java b/quickstep/tests/src/com/android/quickstep/AbstractQuickStepTest.java
index 44c23ba..6a7b6f8 100644
--- a/quickstep/tests/src/com/android/quickstep/AbstractQuickStepTest.java
+++ b/quickstep/tests/src/com/android/quickstep/AbstractQuickStepTest.java
@@ -26,6 +26,7 @@
import com.android.launcher3.tapl.LaunchedAppState;
import com.android.launcher3.ui.AbstractLauncherUiTest;
import com.android.launcher3.uioverrides.QuickstepLauncher;
+import com.android.launcher3.util.TestUtil;
import com.android.quickstep.views.RecentsView;
import org.junit.rules.RuleChain;
@@ -56,7 +57,7 @@
protected void assertTestActivityIsRunning(int activityNumber, String message) {
assertTrue(message, mDevice.wait(
Until.hasObject(By.pkg(getAppPackageName()).text("TestActivity" + activityNumber)),
- DEFAULT_UI_TIMEOUT));
+ TestUtil.DEFAULT_UI_TIMEOUT));
}
protected LaunchedAppState getAndAssertLaunchedApp() {
diff --git a/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java b/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
index 1f11c14..aa105f9 100644
--- a/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
+++ b/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
@@ -22,9 +22,7 @@
import static com.android.launcher3.tapl.LauncherInstrumentation.WAIT_TIME_MS;
import static com.android.launcher3.tapl.TestHelpers.getHomeIntentInPackage;
import static com.android.launcher3.tapl.TestHelpers.getLauncherInMyProcess;
-import static com.android.launcher3.ui.AbstractLauncherUiTest.DEFAULT_ACTIVITY_TIMEOUT;
import static com.android.launcher3.ui.AbstractLauncherUiTest.DEFAULT_BROADCAST_TIMEOUT_SECS;
-import static com.android.launcher3.ui.AbstractLauncherUiTest.DEFAULT_UI_TIMEOUT;
import static com.android.launcher3.ui.AbstractLauncherUiTest.resolveSystemApp;
import static com.android.launcher3.ui.AbstractLauncherUiTest.startAppFast;
import static com.android.launcher3.ui.AbstractLauncherUiTest.startTestActivity;
@@ -56,6 +54,7 @@
import com.android.launcher3.tapl.TestHelpers;
import com.android.launcher3.testcomponent.TestCommandReceiver;
import com.android.launcher3.ui.AbstractLauncherUiTest;
+import com.android.launcher3.util.TestUtil;
import com.android.launcher3.util.Wait;
import com.android.launcher3.util.rule.ExtendedLongPressTimeoutRule;
import com.android.launcher3.util.rule.FailureWatcher;
@@ -214,7 +213,7 @@
}
result[0] = f.apply(activity);
return true;
- }).get(), DEFAULT_UI_TIMEOUT, mLauncher);
+ }).get(), mLauncher);
return (T) result[0];
}
@@ -244,7 +243,7 @@
Wait.atMost("Recents activity didn't stop",
() -> getFromRecents(recents -> !recents.isStarted()),
- DEFAULT_UI_TIMEOUT, mLauncher);
+ mLauncher);
}
@Test
@@ -254,7 +253,8 @@
startTestActivity(2);
waitForRecentsActivityStop();
Wait.atMost("Expected three apps in the task list",
- () -> mLauncher.getRecentTasks().size() >= 3, DEFAULT_ACTIVITY_TIMEOUT, mLauncher);
+ () -> mLauncher.getRecentTasks().size() >= 3,
+ mLauncher);
checkTestLauncher();
BaseOverview overview = mLauncher.getLaunchedAppState().switchToOverview();
@@ -282,7 +282,7 @@
assertNotNull("OverviewTask.open returned null", task.open());
assertTrue("Test activity didn't open from Overview", TestHelpers.wait(Until.hasObject(
By.pkg(getAppPackageName()).text("TestActivity2")),
- DEFAULT_UI_TIMEOUT));
+ TestUtil.DEFAULT_UI_TIMEOUT));
// Test dismissing a task.
diff --git a/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java b/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java
index 4459ed6..77f4c05 100644
--- a/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java
+++ b/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java
@@ -57,8 +57,6 @@
static final String TAG = "QuickStepOnOffRule";
- public static final int WAIT_TIME_MS = 10000;
-
public enum Mode {
THREE_BUTTON, ZERO_BUTTON, ALL
}
@@ -179,12 +177,13 @@
}
Wait.atMost("Couldn't switch to " + overlayPackage,
- () -> launcher.getNavigationModel() == expectedMode, WAIT_TIME_MS, launcher);
+ () -> launcher.getNavigationModel() == expectedMode,
+ launcher);
Wait.atMost(() -> "Switching nav mode: "
+ launcher.getNavigationModeMismatchError(false),
() -> launcher.getNavigationModeMismatchError(false) == null,
- WAIT_TIME_MS, launcher);
+ launcher);
AbstractLauncherUiTest.checkDetectedLeaks(launcher, false);
return true;
}
diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsOverviewDesktop.kt b/quickstep/tests/src/com/android/quickstep/TaplTestsOverviewDesktop.kt
index 120a89b..f58c84e 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplTestsOverviewDesktop.kt
+++ b/quickstep/tests/src/com/android/quickstep/TaplTestsOverviewDesktop.kt
@@ -26,6 +26,7 @@
import com.android.launcher3.ui.AbstractLauncherUiTest
import com.android.launcher3.ui.PortraitLandscapeRunner.PortraitLandscape
import com.android.launcher3.uioverrides.QuickstepLauncher
+import com.android.launcher3.util.TestUtil
import com.google.common.truth.Truth.assertWithMessage
import org.junit.Before
import org.junit.Test
@@ -172,7 +173,7 @@
.that(
mDevice.wait(
Until.hasObject(By.pkg(getAppPackageName()).text("TestActivity$index")),
- DEFAULT_UI_TIMEOUT,
+ TestUtil.DEFAULT_UI_TIMEOUT,
)
)
.isTrue()
diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
index 5ff2af7..f1fe2d2 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
@@ -47,6 +47,7 @@
import com.android.launcher3.tapl.SelectModeButtons;
import com.android.launcher3.tapl.Workspace;
import com.android.launcher3.ui.PortraitLandscapeRunner.PortraitLandscape;
+import com.android.launcher3.util.TestUtil;
import com.android.launcher3.util.Wait;
import com.android.launcher3.util.rule.ScreenRecordRule.ScreenRecord;
import com.android.launcher3.util.rule.TestStabilityRule;
@@ -145,7 +146,7 @@
assertNotNull("OverviewTask.open returned null", task.open());
assertTrue("Test activity didn't open from Overview", mDevice.wait(Until.hasObject(
By.pkg(getAppPackageName()).text("TestActivity2")),
- DEFAULT_UI_TIMEOUT));
+ TestUtil.DEFAULT_UI_TIMEOUT));
executeOnLauncher(launcher -> assertTrue(
"Launcher activity is the top activity; expecting another activity to be the top "
+ "one",
@@ -448,7 +449,7 @@
mDevice.wait(Until.hasObject(By.pkg(getAppPackageName()).text(
mLauncher.isGridOnlyOverviewEnabled() ? "TestActivity12"
: "TestActivity13")),
- DEFAULT_UI_TIMEOUT));
+ TestUtil.DEFAULT_UI_TIMEOUT));
// Scroll the task offscreen as it is now first
overview = mLauncher.goHome().switchToOverview();
@@ -563,7 +564,7 @@
mLauncher.getDevice().setOrientationLeft();
startTestActivity(7);
Wait.atMost("Device should not be in natural orientation",
- () -> !mDevice.isNaturalOrientation(), DEFAULT_UI_TIMEOUT, mLauncher);
+ () -> !mDevice.isNaturalOrientation(), mLauncher);
mLauncher.goHome();
} finally {
mLauncher.setExpectedRotationCheckEnabled(true);
diff --git a/res/drawable/ic_more_horiz_24.xml b/res/drawable/ic_more_horiz_24.xml
new file mode 100644
index 0000000..d46827c
--- /dev/null
+++ b/res/drawable/ic_more_horiz_24.xml
@@ -0,0 +1,26 @@
+<!--
+ ~ Copyright (C) 2024 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.
+ -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:tint="#000000"
+ android:viewportHeight="24"
+ android:viewportWidth="24">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M6,10c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2zM18,10c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2zM12,10c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2z" />
+</vector>
diff --git a/res/drawable/widgets_list_expand_button_background.xml b/res/drawable/widgets_list_expand_button_background.xml
new file mode 100644
index 0000000..068b26d
--- /dev/null
+++ b/res/drawable/widgets_list_expand_button_background.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2024 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.
+ -->
+<inset xmlns:android="http://schemas.android.com/apk/res/android">
+ <ripple android:color="?android:attr/colorControlHighlight">
+ <item>
+ <shape android:shape="rectangle">
+ <corners android:radius="50dp" />
+ <solid android:color="?attr/widgetPickerExpandButtonBackgroundColor" />
+ </shape>
+ </item>
+ </ripple>
+</inset>
\ No newline at end of file
diff --git a/res/layout/widgets_list_expand_button.xml b/res/layout/widgets_list_expand_button.xml
new file mode 100644
index 0000000..17c19ac
--- /dev/null
+++ b/res/layout/widgets_list_expand_button.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2024 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.
+ -->
+
+<Button xmlns:android="http://schemas.android.com/apk/res/android"
+ style="@style/Button.Rounded.Colored"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="@dimen/widgets_list_expand_button_top_margin"
+ android:background="@drawable/widgets_list_expand_button_background"
+ android:drawablePadding="@dimen/widgets_list_expand_button_drawable_padding"
+ android:drawableStart="@drawable/ic_more_horiz_24"
+ android:drawableTint="?attr/widgetPickerExpandButtonTextColor"
+ android:maxLines="1"
+ android:minHeight="48dp"
+ android:paddingEnd="@dimen/widgets_list_expand_button_end_padding"
+ android:paddingStart="@dimen/widgets_list_expand_button_start_padding"
+ android:paddingVertical="@dimen/widgets_list_expand_button_vertical_padding"
+ android:text="@string/widgets_list_expand_button_label"
+ android:contentDescription="@string/widgets_list_expand_button_content_description"
+ android:textColor="?attr/widgetPickerExpandButtonTextColor" />
\ No newline at end of file
diff --git a/res/layout/widgets_two_pane_sheet.xml b/res/layout/widgets_two_pane_sheet.xml
index 8235875..5dc1b47 100644
--- a/res/layout/widgets_two_pane_sheet.xml
+++ b/res/layout/widgets_two_pane_sheet.xml
@@ -70,7 +70,7 @@
android:layout_height="match_parent"
android:clipChildren="false"
android:clipToPadding="false"
- android:paddingBottom="24dp"
+ android:paddingBottom="8dp"
android:layout_gravity="start"
android:layout_weight="0.33">
<TextView
@@ -84,14 +84,6 @@
android:layout_width="@dimen/fastscroll_width"
android:layout_height="match_parent"
android:layout_marginEnd="@dimen/fastscroll_end_margin" />
-
- <com.android.launcher3.widget.picker.WidgetsRecyclerView
- android:id="@+id/search_widgets_list_view"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:clipToPadding="false"
- android:paddingHorizontal="@dimen/widget_list_horizontal_margin_two_pane"
- android:visibility="gone" />
</FrameLayout>
<FrameLayout
diff --git a/res/layout/widgets_two_pane_sheet_paged_view.xml b/res/layout/widgets_two_pane_sheet_paged_view.xml
index 33a50b0..0528d3b 100644
--- a/res/layout/widgets_two_pane_sheet_paged_view.xml
+++ b/res/layout/widgets_two_pane_sheet_paged_view.xml
@@ -143,5 +143,13 @@
android:clipToPadding="false" />
</com.android.launcher3.widget.picker.WidgetPagedView>
+
+ <com.android.launcher3.widget.picker.WidgetsRecyclerView
+ android:id="@+id/search_widgets_list_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:clipToPadding="false"
+ android:paddingHorizontal="@dimen/widget_list_horizontal_margin_two_pane"
+ android:visibility="gone" />
</LinearLayout>
</merge>
diff --git a/res/layout/widgets_two_pane_sheet_recyclerview.xml b/res/layout/widgets_two_pane_sheet_recyclerview.xml
index 94f141b..45a9ac0 100644
--- a/res/layout/widgets_two_pane_sheet_recyclerview.xml
+++ b/res/layout/widgets_two_pane_sheet_recyclerview.xml
@@ -85,5 +85,13 @@
android:layout_height="match_parent"
android:layout_marginHorizontal="@dimen/widget_list_horizontal_margin_two_pane"
android:clipToPadding="false" />
+
+ <com.android.launcher3.widget.picker.WidgetsRecyclerView
+ android:id="@+id/search_widgets_list_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:clipToPadding="false"
+ android:paddingHorizontal="@dimen/widget_list_horizontal_margin_two_pane"
+ android:visibility="gone" />
</LinearLayout>
</merge>
\ No newline at end of file
diff --git a/res/values-af/strings.xml b/res/values-af/strings.xml
index e5c1b61..c956621 100644
--- a/res/values-af/strings.xml
+++ b/res/values-af/strings.xml
@@ -142,7 +142,8 @@
<string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> installeer tans; <xliff:g id="PROGRESS">%2$s</xliff:g> voltooi"</string>
<string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> laai tans af, <xliff:g id="PROGRESS">%2$s</xliff:g> voltooid"</string>
<string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> wag tans om te installeer"</string>
- <string name="app_archived_title" msgid="7717956158562544081">"<xliff:g id="NAME">%1$s</xliff:g> is geargiveer. Tik om af te laai en terug te stel."</string>
+ <string name="app_archived_title" msgid="4548283110222420708">"<xliff:g id="NAME">%1$s</xliff:g> is geargiveer."</string>
+ <string name="app_unarchiving_action" msgid="5736107006413929484">"laai af en stel terug"</string>
<string name="dialog_update_title" msgid="114234265740994042">"Programopdatering word vereis"</string>
<string name="dialog_update_message" msgid="4176784553982226114">"Die program vir hierdie ikoon is nie opgedateer nie. Jy kan dit handmatig opdateer om hierdie kortpad weer te aktiveer, of die ikoon verwyder."</string>
<string name="dialog_update" msgid="2178028071796141234">"Dateer op"</string>
diff --git a/res/values-am/strings.xml b/res/values-am/strings.xml
index 53dc4ba..cfb5585 100644
--- a/res/values-am/strings.xml
+++ b/res/values-am/strings.xml
@@ -142,7 +142,8 @@
<string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> በመጫን ላይ፣ <xliff:g id="PROGRESS">%2$s</xliff:g> ተጠናቅቋል"</string>
<string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> በመውረድ ላይ፣ <xliff:g id="PROGRESS">%2$s</xliff:g> ተጠናቋል"</string>
<string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> ለመጫን በመጠበቅ ላይ"</string>
- <string name="app_archived_title" msgid="7717956158562544081">"<xliff:g id="NAME">%1$s</xliff:g> በማህደር ተቀምጧል። ለማወረድ እና ወደነበረበት ለመመለስ መታ ያድርጉ።"</string>
+ <string name="app_archived_title" msgid="4548283110222420708">"<xliff:g id="NAME">%1$s</xliff:g> በማህደር ተቀምጧል።"</string>
+ <string name="app_unarchiving_action" msgid="5736107006413929484">"አውርድ እና ወደነበረበት መልስ"</string>
<string name="dialog_update_title" msgid="114234265740994042">"መተግበሪያ ማዘመን አስፈላጊ ነው"</string>
<string name="dialog_update_message" msgid="4176784553982226114">"የዚህ አዶ መተግበሪያ አልተዘመነም። ይህን አቋራጭ ዳግም ለማንቃት በራስዎ ማዘመን ወይም አዶውን ማስወገድ ይችላሉ።"</string>
<string name="dialog_update" msgid="2178028071796141234">"አዘምን"</string>
diff --git a/res/values-ar/strings.xml b/res/values-ar/strings.xml
index b15d525..6429b53 100644
--- a/res/values-ar/strings.xml
+++ b/res/values-ar/strings.xml
@@ -142,7 +142,8 @@
<string name="app_installing_title" msgid="5864044122733792085">"جارٍ تثبيت <xliff:g id="NAME">%1$s</xliff:g>، مستوى التقدم: <xliff:g id="PROGRESS">%2$s</xliff:g>"</string>
<string name="app_downloading_title" msgid="8336702962104482644">"جارٍ تنزيل <xliff:g id="NAME">%1$s</xliff:g>، اكتمل <xliff:g id="PROGRESS">%2$s</xliff:g>"</string>
<string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> في انتظار التثبيت"</string>
- <string name="app_archived_title" msgid="7717956158562544081">"تمت أرشفة تطبيق \"<xliff:g id="NAME">%1$s</xliff:g>\". انقر لتنزيله واستعادته."</string>
+ <string name="app_archived_title" msgid="4548283110222420708">"تمت أرشفة \"<xliff:g id="NAME">%1$s</xliff:g>\"."</string>
+ <string name="app_unarchiving_action" msgid="5736107006413929484">"تنزيل التطبيق واستعادته"</string>
<string name="dialog_update_title" msgid="114234265740994042">"مطلوب تحديث التطبيق"</string>
<string name="dialog_update_message" msgid="4176784553982226114">"لم يتمّ تحديث التطبيق الخاص بهذا الرمز. يمكنك تحديث التطبيق يدويًا لإعادة تفعيل هذا الاختصار أو إزالة الرمز."</string>
<string name="dialog_update" msgid="2178028071796141234">"تحديث"</string>
diff --git a/res/values-as/strings.xml b/res/values-as/strings.xml
index d8783a6..f1db033 100644
--- a/res/values-as/strings.xml
+++ b/res/values-as/strings.xml
@@ -142,7 +142,8 @@
<string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> ইনষ্টল কৰি থকা হৈছে, <xliff:g id="PROGRESS">%2$s</xliff:g> সম্পূৰ্ণ হৈছে"</string>
<string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> ডাউনল’ড কৰি থকা হৈছে, <xliff:g id="PROGRESS">%2$s</xliff:g> সম্পূৰ্ণ হ’ল"</string>
<string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> ইনষ্টল হোৱালৈ অপেক্ষা কৰি থকা হৈছে"</string>
- <string name="app_archived_title" msgid="7717956158562544081">"<xliff:g id="NAME">%1$s</xliff:g> আৰ্কাইভ কৰা হৈছে। ডাউনল’ড আৰু পুনঃস্থাপন কৰিবলৈ টিপক।"</string>
+ <string name="app_archived_title" msgid="4548283110222420708">"<xliff:g id="NAME">%1$s</xliff:g> আৰ্কাইভ কৰা হৈছে।"</string>
+ <string name="app_unarchiving_action" msgid="5736107006413929484">"ডাউনল’ড আৰু পুনঃস্থাপন কৰক"</string>
<string name="dialog_update_title" msgid="114234265740994042">"এপ্টো আপডে’ট কৰা প্ৰয়োজন"</string>
<string name="dialog_update_message" msgid="4176784553982226114">"এই চিহ্নটোৰ এপ্টো আপডে’ট কৰা হোৱা নাই। আপুনি এই শ্বৰ্টকাটটো পুনৰ সক্ষম কৰিবলৈ মেনুৱেলী আপডে’ট কৰিব পাৰে অথবা চিহ্নটো আঁতৰাব পাৰে।"</string>
<string name="dialog_update" msgid="2178028071796141234">"আপডে’ট কৰক"</string>
diff --git a/res/values-az/strings.xml b/res/values-az/strings.xml
index 5b86fac..f2129cc 100644
--- a/res/values-az/strings.xml
+++ b/res/values-az/strings.xml
@@ -142,7 +142,8 @@
<string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> quraşdırır, <xliff:g id="PROGRESS">%2$s</xliff:g> tamamlanıb"</string>
<string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> endirilir, <xliff:g id="PROGRESS">%2$s</xliff:g> tamamlandı"</string>
<string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> yüklənmək üçün gözləyir"</string>
- <string name="app_archived_title" msgid="7717956158562544081">"<xliff:g id="NAME">%1$s</xliff:g> arxivləndi. Toxunaraq endirin və bərpa edin."</string>
+ <string name="app_archived_title" msgid="4548283110222420708">"<xliff:g id="NAME">%1$s</xliff:g> arxivləndi."</string>
+ <string name="app_unarchiving_action" msgid="5736107006413929484">"endirin və bərpa edin"</string>
<string name="dialog_update_title" msgid="114234265740994042">"Tətbiqin güncəllənməsi tələb edilir"</string>
<string name="dialog_update_message" msgid="4176784553982226114">"Bu ikona üçün tətbiq güncəllənməyib. Bu qısayolu yenidən aktivləşdirmək üçün manual olaraq güncəlləyə və ya ikonanı silə bilərsiniz."</string>
<string name="dialog_update" msgid="2178028071796141234">"Güncəlləyin"</string>
diff --git a/res/values-b+sr+Latn/strings.xml b/res/values-b+sr+Latn/strings.xml
index 002c800..bdce3b7 100644
--- a/res/values-b+sr+Latn/strings.xml
+++ b/res/values-b+sr+Latn/strings.xml
@@ -142,7 +142,8 @@
<string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> se instalira, <xliff:g id="PROGRESS">%2$s</xliff:g> gotovo"</string>
<string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> se preuzima, završeno je <xliff:g id="PROGRESS">%2$s</xliff:g>"</string>
<string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> čeka na instaliranje"</string>
- <string name="app_archived_title" msgid="7717956158562544081">"Aplikacija <xliff:g id="NAME">%1$s</xliff:g> je arhivirana. Dodirnite da biste je preuzeli i vratili."</string>
+ <string name="app_archived_title" msgid="4548283110222420708">"Aplikacija <xliff:g id="NAME">%1$s</xliff:g> je arhivirana."</string>
+ <string name="app_unarchiving_action" msgid="5736107006413929484">"preuzmite i vratite"</string>
<string name="dialog_update_title" msgid="114234265740994042">"Treba da ažurirate aplikaciju"</string>
<string name="dialog_update_message" msgid="4176784553982226114">"Aplikacija za ovu ikonu nije ažurirana. Možete da je ručno ažurirate da biste ponovo omogućili ovu prečicu ili uklonite ikonu."</string>
<string name="dialog_update" msgid="2178028071796141234">"Ažuriraj"</string>
diff --git a/res/values-be/strings.xml b/res/values-be/strings.xml
index 0984f32..8f264e2 100644
--- a/res/values-be/strings.xml
+++ b/res/values-be/strings.xml
@@ -142,7 +142,8 @@
<string name="app_installing_title" msgid="5864044122733792085">"Усталёўваецца праграма \"<xliff:g id="NAME">%1$s</xliff:g>\", завершана <xliff:g id="PROGRESS">%2$s</xliff:g>"</string>
<string name="app_downloading_title" msgid="8336702962104482644">"Ідзе спампоўка <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="PROGRESS">%2$s</xliff:g> завершана"</string>
<string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> чакае ўсталёўкі"</string>
- <string name="app_archived_title" msgid="7717956158562544081">"Праграма \"<xliff:g id="NAME">%1$s</xliff:g>\" знаходзіцца ў архіве. Націсніце, каб спампаваць яе і аднавіць."</string>
+ <string name="app_archived_title" msgid="4548283110222420708">"Праграма \"<xliff:g id="NAME">%1$s</xliff:g>\" знаходзіцца ў архіве."</string>
+ <string name="app_unarchiving_action" msgid="5736107006413929484">"спампаваць і аднавіць"</string>
<string name="dialog_update_title" msgid="114234265740994042">"Неабходна абнавіць праграму"</string>
<string name="dialog_update_message" msgid="4176784553982226114">"Гэта версія праграмы састарэла. Абнавіце праграму ўручную, каб зноў карыстацца гэтым ярлыком, або выдаліце значок."</string>
<string name="dialog_update" msgid="2178028071796141234">"Абнавіць"</string>
diff --git a/res/values-bg/strings.xml b/res/values-bg/strings.xml
index b321b42..efa941f 100644
--- a/res/values-bg/strings.xml
+++ b/res/values-bg/strings.xml
@@ -142,7 +142,8 @@
<string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> се инсталира, <xliff:g id="PROGRESS">%2$s</xliff:g> завършено"</string>
<string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> се изтегля. Завършено: <xliff:g id="PROGRESS">%2$s</xliff:g>"</string>
<string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> изчаква инсталиране"</string>
- <string name="app_archived_title" msgid="7717956158562544081">"Приложението <xliff:g id="NAME">%1$s</xliff:g> е архивирано. Докоснете за изтегляне и възстановяване."</string>
+ <string name="app_archived_title" msgid="4548283110222420708">"Приложението <xliff:g id="NAME">%1$s</xliff:g> е архивирано."</string>
+ <string name="app_unarchiving_action" msgid="5736107006413929484">"изтегляне и възстановяване"</string>
<string name="dialog_update_title" msgid="114234265740994042">"Изисква се актуализация на приложението"</string>
<string name="dialog_update_message" msgid="4176784553982226114">"Приложението за тази икона не е актуализирано. Можете да го актуализирате ръчно, за да активирате отново този пряк път, или да премахнете иконата."</string>
<string name="dialog_update" msgid="2178028071796141234">"Актуализиране"</string>
diff --git a/res/values-bn/strings.xml b/res/values-bn/strings.xml
index 0722e84..0ccedd1 100644
--- a/res/values-bn/strings.xml
+++ b/res/values-bn/strings.xml
@@ -142,7 +142,8 @@
<string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> ইনস্টল করা হচ্ছে, <xliff:g id="PROGRESS">%2$s</xliff:g> সম্পূর্ণ হয়েছে"</string>
<string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> ডাউনলোড হচ্ছে <xliff:g id="PROGRESS">%2$s</xliff:g> সম্পন্ন হয়েছে"</string>
<string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> ইনস্টলের অপেক্ষায় রয়েছে"</string>
- <string name="app_archived_title" msgid="7717956158562544081">"<xliff:g id="NAME">%1$s</xliff:g> আর্কাইভ করা হয়েছে। ডাউনলোড করতে এবং ফিরিয়ে আনতে ট্যাপ করুন।"</string>
+ <string name="app_archived_title" msgid="4548283110222420708">"<xliff:g id="NAME">%1$s</xliff:g> আর্কাইভ করা হয়েছে।"</string>
+ <string name="app_unarchiving_action" msgid="5736107006413929484">"ডাউনলোড করুন ও ফিরিয়ে আনুন"</string>
<string name="dialog_update_title" msgid="114234265740994042">"অ্যাপটি আপডেট করা প্রয়োজন"</string>
<string name="dialog_update_message" msgid="4176784553982226114">"এই আইকনের জন্য অ্যাপটি আপডেট করা নেই। এই শর্টকার্ট আবার চালু করতে, আপনি ম্যানুয়ালি আপডেট করতে বা সরিয়ে দিতে পারবেন।"</string>
<string name="dialog_update" msgid="2178028071796141234">"আপডেট করুন"</string>
diff --git a/res/values-bs/strings.xml b/res/values-bs/strings.xml
index 2b168f6..a276431 100644
--- a/res/values-bs/strings.xml
+++ b/res/values-bs/strings.xml
@@ -142,7 +142,8 @@
<string name="app_installing_title" msgid="5864044122733792085">"Instaliranje aplikacije <xliff:g id="NAME">%1$s</xliff:g>, završeno je <xliff:g id="PROGRESS">%2$s</xliff:g>"</string>
<string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> se preuzima, završeno <xliff:g id="PROGRESS">%2$s</xliff:g>"</string>
<string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> čeka da se instalira"</string>
- <string name="app_archived_title" msgid="7717956158562544081">"Arhivirana je aplikacija <xliff:g id="NAME">%1$s</xliff:g>. Dodirnite da je preuzmete i vratite."</string>
+ <string name="app_archived_title" msgid="4548283110222420708">"Arhivirana je aplikacija <xliff:g id="NAME">%1$s</xliff:g>."</string>
+ <string name="app_unarchiving_action" msgid="5736107006413929484">"preuzimanje i vraćanje"</string>
<string name="dialog_update_title" msgid="114234265740994042">"Potrebno je ažurirati aplikaciju"</string>
<string name="dialog_update_message" msgid="4176784553982226114">"Aplikacija za ovu ikonu nije ažurirana. Možete je ažurirati ručno da ponovo omogućite ovu prečicu ili možete ukloniti ikonu."</string>
<string name="dialog_update" msgid="2178028071796141234">"Ažuriraj"</string>
diff --git a/res/values-ca/strings.xml b/res/values-ca/strings.xml
index 6d30ec1..8f7151e 100644
--- a/res/values-ca/strings.xml
+++ b/res/values-ca/strings.xml
@@ -142,7 +142,8 @@
<string name="app_installing_title" msgid="5864044122733792085">"S\'està instal·lant <xliff:g id="NAME">%1$s</xliff:g>; s\'ha completat un <xliff:g id="PROGRESS">%2$s</xliff:g>"</string>
<string name="app_downloading_title" msgid="8336702962104482644">"S\'està baixant <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="PROGRESS">%2$s</xliff:g> completat"</string>
<string name="app_waiting_download_title" msgid="7053938513995617849">"S\'està esperant per instal·lar <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="app_archived_title" msgid="7717956158562544081">"L\'aplicació <xliff:g id="NAME">%1$s</xliff:g> està arxivada. Toca per baixar-la i restaurar-la."</string>
+ <string name="app_archived_title" msgid="4548283110222420708">"L\'aplicació <xliff:g id="NAME">%1$s</xliff:g> està arxivada."</string>
+ <string name="app_unarchiving_action" msgid="5736107006413929484">"baixa i restaura"</string>
<string name="dialog_update_title" msgid="114234265740994042">"Cal actualitzar l\'aplicació"</string>
<string name="dialog_update_message" msgid="4176784553982226114">"L\'aplicació d\'aquesta icona no està actualitzada. Pots actualitzar-la manualment per tornar a activar aquesta drecera o pots suprimir la icona."</string>
<string name="dialog_update" msgid="2178028071796141234">"Actualitza"</string>
diff --git a/res/values-cs/strings.xml b/res/values-cs/strings.xml
index 732343d..1dead61 100644
--- a/res/values-cs/strings.xml
+++ b/res/values-cs/strings.xml
@@ -142,7 +142,8 @@
<string name="app_installing_title" msgid="5864044122733792085">"Instalace aplikace <xliff:g id="NAME">%1$s</xliff:g>, dokončeno <xliff:g id="PROGRESS">%2$s</xliff:g>"</string>
<string name="app_downloading_title" msgid="8336702962104482644">"Stahování aplikace <xliff:g id="NAME">%1$s</xliff:g> (dokončeno <xliff:g id="PROGRESS">%2$s</xliff:g>)"</string>
<string name="app_waiting_download_title" msgid="7053938513995617849">"Instalace aplikace <xliff:g id="NAME">%1$s</xliff:g> čeká na zahájení"</string>
- <string name="app_archived_title" msgid="7717956158562544081">"Aplikace <xliff:g id="NAME">%1$s</xliff:g> je archivována. Klepnutím ji můžete stáhnout a obnovit."</string>
+ <string name="app_archived_title" msgid="4548283110222420708">"Aplikace <xliff:g id="NAME">%1$s</xliff:g> je archivována."</string>
+ <string name="app_unarchiving_action" msgid="5736107006413929484">"stáhnout a obnovit"</string>
<string name="dialog_update_title" msgid="114234265740994042">"Je nutná aktualizace aplikace"</string>
<string name="dialog_update_message" msgid="4176784553982226114">"Aplikace pro tuto ikonu není nainstalována. Můžete ji ručně aktualizovat, aby zkratka znovu fungovala, případně můžete ikonu odstranit."</string>
<string name="dialog_update" msgid="2178028071796141234">"Aktualizovat"</string>
diff --git a/res/values-da/strings.xml b/res/values-da/strings.xml
index 0d78d90..4851c52 100644
--- a/res/values-da/strings.xml
+++ b/res/values-da/strings.xml
@@ -142,7 +142,8 @@
<string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> installeres. <xliff:g id="PROGRESS">%2$s</xliff:g> fuldført"</string>
<string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> downloades. <xliff:g id="PROGRESS">%2$s</xliff:g> er gennemført"</string>
<string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> venter på at installere"</string>
- <string name="app_archived_title" msgid="7717956158562544081">"<xliff:g id="NAME">%1$s</xliff:g> er arkiveret Tryk for at downloade og gendanne."</string>
+ <string name="app_archived_title" msgid="4548283110222420708">"<xliff:g id="NAME">%1$s</xliff:g> er arkiveret."</string>
+ <string name="app_unarchiving_action" msgid="5736107006413929484">"download og gendan"</string>
<string name="dialog_update_title" msgid="114234265740994042">"Appen skal opdateres"</string>
<string name="dialog_update_message" msgid="4176784553982226114">"Appen, der tilhører dette ikon, er ikke opdateret. Du kan opdatere appen manuelt for at genaktivere denne genvej, eller du kan fjerne ikonet."</string>
<string name="dialog_update" msgid="2178028071796141234">"Opdater"</string>
diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml
index c90cf84..38f943b 100644
--- a/res/values-de/strings.xml
+++ b/res/values-de/strings.xml
@@ -142,7 +142,8 @@
<string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> wird installiert, <xliff:g id="PROGRESS">%2$s</xliff:g> abgeschlossen"</string>
<string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> wird heruntergeladen, <xliff:g id="PROGRESS">%2$s</xliff:g> abgeschlossen"</string>
<string name="app_waiting_download_title" msgid="7053938513995617849">"Warten auf Installation von <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="app_archived_title" msgid="7717956158562544081">"<xliff:g id="NAME">%1$s</xliff:g> ist archiviert. Tippe, um die App herunterzuladen und wiederherzustellen."</string>
+ <string name="app_archived_title" msgid="4548283110222420708">"<xliff:g id="NAME">%1$s</xliff:g> ist archiviert."</string>
+ <string name="app_unarchiving_action" msgid="5736107006413929484">"herunterladen und wiederherstellen"</string>
<string name="dialog_update_title" msgid="114234265740994042">"App-Update erforderlich"</string>
<string name="dialog_update_message" msgid="4176784553982226114">"Die App für dieses Symbol wurde noch nicht aktualisiert. Du kannst sie manuell aktualisieren, um die Verknüpfung wieder zu aktivieren, oder das Symbol entfernen."</string>
<string name="dialog_update" msgid="2178028071796141234">"Aktualisieren"</string>
diff --git a/res/values-el/strings.xml b/res/values-el/strings.xml
index cd1a1e0..9860f21 100644
--- a/res/values-el/strings.xml
+++ b/res/values-el/strings.xml
@@ -142,7 +142,8 @@
<string name="app_installing_title" msgid="5864044122733792085">"Έχει ολοκληρωθεί το <xliff:g id="PROGRESS">%2$s</xliff:g> της εγκατάστασης της εφαρμογής <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="app_downloading_title" msgid="8336702962104482644">"Λήψη <xliff:g id="NAME">%1$s</xliff:g>, ολοκληρώθηκε <xliff:g id="PROGRESS">%2$s</xliff:g>"</string>
<string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> σε αναμονή για εγκατάσταση"</string>
- <string name="app_archived_title" msgid="7717956158562544081">"Η εφαρμογή <xliff:g id="NAME">%1$s</xliff:g> είναι αρχειοθετημένη. Πατήστε για λήψη και επαναφορά."</string>
+ <string name="app_archived_title" msgid="4548283110222420708">"Η εφαρμογή <xliff:g id="NAME">%1$s</xliff:g> είναι αρχειοθετημένη."</string>
+ <string name="app_unarchiving_action" msgid="5736107006413929484">"λήψη και επαναφορά"</string>
<string name="dialog_update_title" msgid="114234265740994042">"Απαιτείται ενημέρωση της εφαρμογής"</string>
<string name="dialog_update_message" msgid="4176784553982226114">"Η εφαρμογή για αυτό το εικονίδιο δεν έχει ενημερωθεί. Μπορείτε να την ενημερώσετε μη αυτόματα για να ενεργοποιήσετε ξανά τη συγκεκριμένη συντόμευση ή να καταργήσετε το εικονίδιο."</string>
<string name="dialog_update" msgid="2178028071796141234">"Ενημέρωση"</string>
diff --git a/res/values-en-rAU/strings.xml b/res/values-en-rAU/strings.xml
index f7b04a3..f9711f0 100644
--- a/res/values-en-rAU/strings.xml
+++ b/res/values-en-rAU/strings.xml
@@ -142,7 +142,8 @@
<string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> installing, <xliff:g id="PROGRESS">%2$s</xliff:g> complete"</string>
<string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> downloading, <xliff:g id="PROGRESS">%2$s</xliff:g> complete"</string>
<string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> waiting to install"</string>
- <string name="app_archived_title" msgid="7717956158562544081">"<xliff:g id="NAME">%1$s</xliff:g> is archived. Tap to download and restore."</string>
+ <string name="app_archived_title" msgid="4548283110222420708">"<xliff:g id="NAME">%1$s</xliff:g> is archived."</string>
+ <string name="app_unarchiving_action" msgid="5736107006413929484">"download and restore"</string>
<string name="dialog_update_title" msgid="114234265740994042">"App update required"</string>
<string name="dialog_update_message" msgid="4176784553982226114">"The app for this icon isn\'t updated. You can update manually to re-enable this shortcut or remove the icon."</string>
<string name="dialog_update" msgid="2178028071796141234">"Update"</string>
diff --git a/res/values-en-rCA/strings.xml b/res/values-en-rCA/strings.xml
index 8feccb0..f41ece6 100644
--- a/res/values-en-rCA/strings.xml
+++ b/res/values-en-rCA/strings.xml
@@ -142,7 +142,8 @@
<string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> installing, <xliff:g id="PROGRESS">%2$s</xliff:g> complete"</string>
<string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> downloading, <xliff:g id="PROGRESS">%2$s</xliff:g> complete"</string>
<string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> waiting to install"</string>
- <string name="app_archived_title" msgid="7717956158562544081">"<xliff:g id="NAME">%1$s</xliff:g> is archived. Tap to download and restore."</string>
+ <string name="app_archived_title" msgid="4548283110222420708">"<xliff:g id="NAME">%1$s</xliff:g> is archived."</string>
+ <string name="app_unarchiving_action" msgid="5736107006413929484">"download and restore"</string>
<string name="dialog_update_title" msgid="114234265740994042">"App update required"</string>
<string name="dialog_update_message" msgid="4176784553982226114">"The app for this icon isn\'t updated. You can update manually to re-enable this shortcut, or remove the icon."</string>
<string name="dialog_update" msgid="2178028071796141234">"Update"</string>
diff --git a/res/values-en-rGB/strings.xml b/res/values-en-rGB/strings.xml
index f7b04a3..f9711f0 100644
--- a/res/values-en-rGB/strings.xml
+++ b/res/values-en-rGB/strings.xml
@@ -142,7 +142,8 @@
<string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> installing, <xliff:g id="PROGRESS">%2$s</xliff:g> complete"</string>
<string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> downloading, <xliff:g id="PROGRESS">%2$s</xliff:g> complete"</string>
<string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> waiting to install"</string>
- <string name="app_archived_title" msgid="7717956158562544081">"<xliff:g id="NAME">%1$s</xliff:g> is archived. Tap to download and restore."</string>
+ <string name="app_archived_title" msgid="4548283110222420708">"<xliff:g id="NAME">%1$s</xliff:g> is archived."</string>
+ <string name="app_unarchiving_action" msgid="5736107006413929484">"download and restore"</string>
<string name="dialog_update_title" msgid="114234265740994042">"App update required"</string>
<string name="dialog_update_message" msgid="4176784553982226114">"The app for this icon isn\'t updated. You can update manually to re-enable this shortcut or remove the icon."</string>
<string name="dialog_update" msgid="2178028071796141234">"Update"</string>
diff --git a/res/values-en-rIN/strings.xml b/res/values-en-rIN/strings.xml
index f7b04a3..f9711f0 100644
--- a/res/values-en-rIN/strings.xml
+++ b/res/values-en-rIN/strings.xml
@@ -142,7 +142,8 @@
<string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> installing, <xliff:g id="PROGRESS">%2$s</xliff:g> complete"</string>
<string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> downloading, <xliff:g id="PROGRESS">%2$s</xliff:g> complete"</string>
<string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> waiting to install"</string>
- <string name="app_archived_title" msgid="7717956158562544081">"<xliff:g id="NAME">%1$s</xliff:g> is archived. Tap to download and restore."</string>
+ <string name="app_archived_title" msgid="4548283110222420708">"<xliff:g id="NAME">%1$s</xliff:g> is archived."</string>
+ <string name="app_unarchiving_action" msgid="5736107006413929484">"download and restore"</string>
<string name="dialog_update_title" msgid="114234265740994042">"App update required"</string>
<string name="dialog_update_message" msgid="4176784553982226114">"The app for this icon isn\'t updated. You can update manually to re-enable this shortcut or remove the icon."</string>
<string name="dialog_update" msgid="2178028071796141234">"Update"</string>
diff --git a/res/values-es-rUS/strings.xml b/res/values-es-rUS/strings.xml
index 0125ae5..eae2009 100644
--- a/res/values-es-rUS/strings.xml
+++ b/res/values-es-rUS/strings.xml
@@ -142,7 +142,8 @@
<string name="app_installing_title" msgid="5864044122733792085">"Se está instalando <xliff:g id="NAME">%1$s</xliff:g>; <xliff:g id="PROGRESS">%2$s</xliff:g> completado"</string>
<string name="app_downloading_title" msgid="8336702962104482644">"Se completó el <xliff:g id="PROGRESS">%2$s</xliff:g> de la descarga de <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="app_waiting_download_title" msgid="7053938513995617849">"Instalación de <xliff:g id="NAME">%1$s</xliff:g> en espera"</string>
- <string name="app_archived_title" msgid="7717956158562544081">"<xliff:g id="NAME">%1$s</xliff:g> está archivada. Presiona para descargar y restablecer."</string>
+ <string name="app_archived_title" msgid="4548283110222420708">"<xliff:g id="NAME">%1$s</xliff:g> está archivada."</string>
+ <string name="app_unarchiving_action" msgid="5736107006413929484">"descargar y restablecer"</string>
<string name="dialog_update_title" msgid="114234265740994042">"Es necesario actualizar la app"</string>
<string name="dialog_update_message" msgid="4176784553982226114">"No se actualizó la app de este ícono. Puedes actualizarla manualmente para rehabilitar el acceso directo, o bien quitar el ícono."</string>
<string name="dialog_update" msgid="2178028071796141234">"Actualizar"</string>
diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml
index ddcee65..756912b 100644
--- a/res/values-es/strings.xml
+++ b/res/values-es/strings.xml
@@ -142,7 +142,8 @@
<string name="app_installing_title" msgid="5864044122733792085">"Instalando <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="PROGRESS">%2$s</xliff:g> completado"</string>
<string name="app_downloading_title" msgid="8336702962104482644">"Descargando <xliff:g id="NAME">%1$s</xliff:g> (<xliff:g id="PROGRESS">%2$s</xliff:g> completado)"</string>
<string name="app_waiting_download_title" msgid="7053938513995617849">"Esperando para instalar <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="app_archived_title" msgid="7717956158562544081">"<xliff:g id="NAME">%1$s</xliff:g> está archivada. Toca para descargar y restaurar."</string>
+ <string name="app_archived_title" msgid="4548283110222420708">"<xliff:g id="NAME">%1$s</xliff:g> está archivada."</string>
+ <string name="app_unarchiving_action" msgid="5736107006413929484">"descargar y restaurar"</string>
<string name="dialog_update_title" msgid="114234265740994042">"Debes actualizar la aplicación"</string>
<string name="dialog_update_message" msgid="4176784553982226114">"La aplicación de este icono no está actualizada. Puedes actualizarla manualmente para volver a habilitar este acceso directo o puedes eliminar el icono."</string>
<string name="dialog_update" msgid="2178028071796141234">"Actualizar"</string>
diff --git a/res/values-et/strings.xml b/res/values-et/strings.xml
index 289b9d9..f44f45b 100644
--- a/res/values-et/strings.xml
+++ b/res/values-et/strings.xml
@@ -142,7 +142,8 @@
<string name="app_installing_title" msgid="5864044122733792085">"Üksust <xliff:g id="NAME">%1$s</xliff:g> installitakse, <xliff:g id="PROGRESS">%2$s</xliff:g> on valmis"</string>
<string name="app_downloading_title" msgid="8336702962104482644">"Rakenduse <xliff:g id="NAME">%1$s</xliff:g> allalaadimine, <xliff:g id="PROGRESS">%2$s</xliff:g> on valmis"</string>
<string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> on installimise ootel"</string>
- <string name="app_archived_title" msgid="7717956158562544081">"<xliff:g id="NAME">%1$s</xliff:g> on arhiivitud. Puudutage allalaadimiseks ja taastamiseks."</string>
+ <string name="app_archived_title" msgid="4548283110222420708">"<xliff:g id="NAME">%1$s</xliff:g> on arhiivitud."</string>
+ <string name="app_unarchiving_action" msgid="5736107006413929484">"laadi alla ja taasta"</string>
<string name="dialog_update_title" msgid="114234265740994042">"Rakendust tuleb värskendada"</string>
<string name="dialog_update_message" msgid="4176784553982226114">"Selle ikooni rakendust pole värskendatud. Otsetee uuesti lubamiseks võite rakendust käsitsi värskendada või ikooni eemaldada."</string>
<string name="dialog_update" msgid="2178028071796141234">"Värskenda"</string>
diff --git a/res/values-eu/strings.xml b/res/values-eu/strings.xml
index db99806..5f5f76f 100644
--- a/res/values-eu/strings.xml
+++ b/res/values-eu/strings.xml
@@ -142,7 +142,8 @@
<string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> instalatzen, <xliff:g id="PROGRESS">%2$s</xliff:g> osatuta"</string>
<string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> deskargatzen, <xliff:g id="PROGRESS">%2$s</xliff:g> osatuta"</string>
<string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> instalatzeko zain"</string>
- <string name="app_archived_title" msgid="7717956158562544081">"<xliff:g id="NAME">%1$s</xliff:g> artxibatuta dago. Sakatu deskargatzeko eta leheneratzeko."</string>
+ <string name="app_archived_title" msgid="4548283110222420708">"<xliff:g id="NAME">%1$s</xliff:g> artxibatuta dago."</string>
+ <string name="app_unarchiving_action" msgid="5736107006413929484">"deskargatu eta leheneratu"</string>
<string name="dialog_update_title" msgid="114234265740994042">"Aplikazioa eguneratu egin behar da"</string>
<string name="dialog_update_message" msgid="4176784553982226114">"Ikonoaren aplikazioa ez dago eguneratuta. Lasterbidea berriro gaitzeko, eskuz egunera dezakezu aplikazioa. Bestela, kendu ikonoa."</string>
<string name="dialog_update" msgid="2178028071796141234">"Eguneratu"</string>
diff --git a/res/values-fa/strings.xml b/res/values-fa/strings.xml
index 17dd851..c5adfd6 100644
--- a/res/values-fa/strings.xml
+++ b/res/values-fa/strings.xml
@@ -142,7 +142,8 @@
<string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> درحال نصب است، <xliff:g id="PROGRESS">%2$s</xliff:g> تکمیل شده است"</string>
<string name="app_downloading_title" msgid="8336702962104482644">"درحال بارگیری <xliff:g id="NAME">%1$s</xliff:g>، <xliff:g id="PROGRESS">%2$s</xliff:g> کامل شد"</string>
<string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> درانتظار نصب"</string>
- <string name="app_archived_title" msgid="7717956158562544081">"<xliff:g id="NAME">%1$s</xliff:g> بایگانی شده است. برای بارگیری و بازیابی تکضرب بزنید."</string>
+ <string name="app_archived_title" msgid="4548283110222420708">"<xliff:g id="NAME">%1$s</xliff:g> بایگانی شده است."</string>
+ <string name="app_unarchiving_action" msgid="5736107006413929484">"بارگیری و بازیابی کردن"</string>
<string name="dialog_update_title" msgid="114234265740994042">"برنامه باید بهروز شود"</string>
<string name="dialog_update_message" msgid="4176784553982226114">"برنامه برای این نماد بهروز نشده است. میتوانید آن را بهصورت دستی بهروز کنید تا میانبر دوباره فعال شود، یا نماد را بردارید."</string>
<string name="dialog_update" msgid="2178028071796141234">"بهروزرسانی"</string>
diff --git a/res/values-fi/strings.xml b/res/values-fi/strings.xml
index c61c85a..ea5883b 100644
--- a/res/values-fi/strings.xml
+++ b/res/values-fi/strings.xml
@@ -142,7 +142,8 @@
<string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> asennetaan, <xliff:g id="PROGRESS">%2$s</xliff:g> valmis"</string>
<string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> latautuu, valmiina <xliff:g id="PROGRESS">%2$s</xliff:g>"</string>
<string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> odottaa asennusta"</string>
- <string name="app_archived_title" msgid="7717956158562544081">"<xliff:g id="NAME">%1$s</xliff:g> on arkistoitu. Lataa ja palauta napauttamalla."</string>
+ <string name="app_archived_title" msgid="4548283110222420708">"<xliff:g id="NAME">%1$s</xliff:g> on arkistoitu."</string>
+ <string name="app_unarchiving_action" msgid="5736107006413929484">"lataa ja palauta"</string>
<string name="dialog_update_title" msgid="114234265740994042">"Sovelluspäivitys vaaditaan"</string>
<string name="dialog_update_message" msgid="4176784553982226114">"Kuvakkeen sovellusta ei ole päivitetty. Voit ottaa pikakuvakkeen uudelleen käyttöön päivittämällä sovelluksen tai poistaa kuvakkeen."</string>
<string name="dialog_update" msgid="2178028071796141234">"Päivitä"</string>
diff --git a/res/values-fr-rCA/strings.xml b/res/values-fr-rCA/strings.xml
index a941d88..f58b644 100644
--- a/res/values-fr-rCA/strings.xml
+++ b/res/values-fr-rCA/strings.xml
@@ -142,7 +142,8 @@
<string name="app_installing_title" msgid="5864044122733792085">"Installation de l\'appli <xliff:g id="NAME">%1$s</xliff:g> en cours, <xliff:g id="PROGRESS">%2$s</xliff:g> terminée"</string>
<string name="app_downloading_title" msgid="8336702962104482644">"Téléchargement de <xliff:g id="NAME">%1$s</xliff:g> : <xliff:g id="PROGRESS">%2$s</xliff:g>"</string>
<string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> en attente d\'installation"</string>
- <string name="app_archived_title" msgid="7717956158562544081">"L\'appli <xliff:g id="NAME">%1$s</xliff:g> est archivée. Touchez le bouton pour télécharger et restaurer l\'appli."</string>
+ <string name="app_archived_title" msgid="4548283110222420708">"L\'appli <xliff:g id="NAME">%1$s</xliff:g> est archivée."</string>
+ <string name="app_unarchiving_action" msgid="5736107006413929484">"télécharger et restaurer"</string>
<string name="dialog_update_title" msgid="114234265740994042">"Mise à jour de l\'appli requise"</string>
<string name="dialog_update_message" msgid="4176784553982226114">"L\'appli pour cette icône n\'est pas à jour. Vous pouvez soit la mettre à jour manuellement pour réactiver ce raccourci, soit retirer l\'icône."</string>
<string name="dialog_update" msgid="2178028071796141234">"Mettre à jour"</string>
diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml
index 6cbc921..feac922 100644
--- a/res/values-fr/strings.xml
+++ b/res/values-fr/strings.xml
@@ -142,7 +142,8 @@
<string name="app_installing_title" msgid="5864044122733792085">"Installation de <xliff:g id="NAME">%1$s</xliff:g>… (<xliff:g id="PROGRESS">%2$s</xliff:g> terminés)"</string>
<string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> en cours de téléchargement, <xliff:g id="PROGRESS">%2$s</xliff:g> effectué(s)"</string>
<string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> en attente d\'installation"</string>
- <string name="app_archived_title" msgid="7717956158562544081">"L\'application <xliff:g id="NAME">%1$s</xliff:g> est archivée. Appuyez pour la télécharger et la restaurer."</string>
+ <string name="app_archived_title" msgid="4548283110222420708">"L\'application <xliff:g id="NAME">%1$s</xliff:g> est archivée."</string>
+ <string name="app_unarchiving_action" msgid="5736107006413929484">"télécharger et restaurer"</string>
<string name="dialog_update_title" msgid="114234265740994042">"Mise à jour de l\'appli requise"</string>
<string name="dialog_update_message" msgid="4176784553982226114">"L\'appli correspondant à cette icône n\'est pas mise à jour. Vous pouvez la mettre à jour manuellement pour réactiver le raccourci ou supprimer l\'icône."</string>
<string name="dialog_update" msgid="2178028071796141234">"Modifier"</string>
diff --git a/res/values-gl/strings.xml b/res/values-gl/strings.xml
index 02389c4..c36bf18 100644
--- a/res/values-gl/strings.xml
+++ b/res/values-gl/strings.xml
@@ -142,7 +142,8 @@
<string name="app_installing_title" msgid="5864044122733792085">"Instalando <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="PROGRESS">%2$s</xliff:g> completado"</string>
<string name="app_downloading_title" msgid="8336702962104482644">"Descargando <xliff:g id="NAME">%1$s</xliff:g> (<xliff:g id="PROGRESS">%2$s</xliff:g> completado)"</string>
<string name="app_waiting_download_title" msgid="7053938513995617849">"Esperando para instalar <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="app_archived_title" msgid="7717956158562544081">"<xliff:g id="NAME">%1$s</xliff:g> está no arquivo. Toca para descargar e restaurar."</string>
+ <string name="app_archived_title" msgid="4548283110222420708">"<xliff:g id="NAME">%1$s</xliff:g> está arquivada."</string>
+ <string name="app_unarchiving_action" msgid="5736107006413929484">"descargar e restaurar"</string>
<string name="dialog_update_title" msgid="114234265740994042">"É necesario actualizar a aplicación"</string>
<string name="dialog_update_message" msgid="4176784553982226114">"A aplicación á que corresponde esta icona non está actualizada. Podes actualizala manualmente para activar de novo este atallo, ou ben quitar a icona."</string>
<string name="dialog_update" msgid="2178028071796141234">"Actualizar"</string>
diff --git a/res/values-gu/strings.xml b/res/values-gu/strings.xml
index aca3054..ee0dec7 100644
--- a/res/values-gu/strings.xml
+++ b/res/values-gu/strings.xml
@@ -142,7 +142,8 @@
<string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> ઇન્સ્ટૉલ કરી રહ્યાં છીએ, <xliff:g id="PROGRESS">%2$s</xliff:g> પૂર્ણ થયું"</string>
<string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> ડાઉનલોડ કરી રહ્યાં છે, <xliff:g id="PROGRESS">%2$s</xliff:g> પૂર્ણ"</string>
<string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g>, ઇન્સ્ટૉલ થવાની રાહ જોઈ રહ્યું છે"</string>
- <string name="app_archived_title" msgid="7717956158562544081">"<xliff:g id="NAME">%1$s</xliff:g>ને આર્કાઇવ કર્યું છે. ડાઉનલોડ અને રિસ્ટોર કરવા માટે ટૅપ કરો."</string>
+ <string name="app_archived_title" msgid="4548283110222420708">"<xliff:g id="NAME">%1$s</xliff:g>ને આર્કાઇવ કર્યું છે."</string>
+ <string name="app_unarchiving_action" msgid="5736107006413929484">"ડાઉનલોડ અને રિસ્ટોર કરો"</string>
<string name="dialog_update_title" msgid="114234265740994042">"ઍપને અપડેટ કરવી જરૂરી છે"</string>
<string name="dialog_update_message" msgid="4176784553982226114">"આ આઇકન માટે ઍપ અપડેટ કરવામાં આવી નથી. તમે આ શૉર્ટકટ ફરી ચાલુ કરવા અથવા આઇકન કાઢી નાખવા માટે ઍપને મેન્યુઅલી અપડેટ કરી શકો છો."</string>
<string name="dialog_update" msgid="2178028071796141234">"અપડેટ કરો"</string>
diff --git a/res/values-hi/strings.xml b/res/values-hi/strings.xml
index 00726ea..c57bad3 100644
--- a/res/values-hi/strings.xml
+++ b/res/values-hi/strings.xml
@@ -142,7 +142,8 @@
<string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> इंस्टॉल किया जा रहा है, <xliff:g id="PROGRESS">%2$s</xliff:g> पूरा हो गया"</string>
<string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> डाउनलोड हो रहा है, <xliff:g id="PROGRESS">%2$s</xliff:g> पूरी हुई"</string>
<string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> के इंस्टॉल होने की प्रतीक्षा की जा रही है"</string>
- <string name="app_archived_title" msgid="7717956158562544081">"<xliff:g id="NAME">%1$s</xliff:g> को संग्रहित किया गया. ऐप्लिकेशन को वापस लाने और डाउनलोड करने के लिए टैप करें."</string>
+ <string name="app_archived_title" msgid="4548283110222420708">"<xliff:g id="NAME">%1$s</xliff:g> को संग्रहित किया गया."</string>
+ <string name="app_unarchiving_action" msgid="5736107006413929484">"डाउनलोड करें और वापस लाएं"</string>
<string name="dialog_update_title" msgid="114234265740994042">"ऐप्लिकेशन को अपडेट करना ज़रूरी है"</string>
<string name="dialog_update_message" msgid="4176784553982226114">"इस आइकॉन का ऐप्लिकेशन अपडेट नहीं है. इस शॉर्टकट को फिर से चालू करने या आइकॉन को हटाने के लिए, ऐप्लिकेशन को मैन्युअल रूप से अपडेट किया जा सकता है."</string>
<string name="dialog_update" msgid="2178028071796141234">"अपडेट करें"</string>
diff --git a/res/values-hr/strings.xml b/res/values-hr/strings.xml
index a9fd14e..26c9155 100644
--- a/res/values-hr/strings.xml
+++ b/res/values-hr/strings.xml
@@ -142,7 +142,8 @@
<string name="app_installing_title" msgid="5864044122733792085">"Instaliranje aplikacije <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="PROGRESS">%2$s</xliff:g> dovršeno"</string>
<string name="app_downloading_title" msgid="8336702962104482644">"Preuzimanje aplikacije <xliff:g id="NAME">%1$s</xliff:g>, dovršeno <xliff:g id="PROGRESS">%2$s</xliff:g>"</string>
<string name="app_waiting_download_title" msgid="7053938513995617849">"Čekanje na instaliranje aplikacije <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="app_archived_title" msgid="7717956158562544081">"Aplikacija <xliff:g id="NAME">%1$s</xliff:g> je arhivirana. Dodirnite da biste je preuzeli i vratili."</string>
+ <string name="app_archived_title" msgid="4548283110222420708">"Aplikacija <xliff:g id="NAME">%1$s</xliff:g> je arhivirana."</string>
+ <string name="app_unarchiving_action" msgid="5736107006413929484">"preuzmi i vrati"</string>
<string name="dialog_update_title" msgid="114234265740994042">"Aplikacija se treba ažurirati"</string>
<string name="dialog_update_message" msgid="4176784553982226114">"Aplikacija ove ikone nije ažurirana. Možete ručno ažurirati da biste ponovo omogućili ovaj prečac ili uklonite ikonu."</string>
<string name="dialog_update" msgid="2178028071796141234">"Ažuriraj"</string>
diff --git a/res/values-hu/strings.xml b/res/values-hu/strings.xml
index a0c089e..dd50451 100644
--- a/res/values-hu/strings.xml
+++ b/res/values-hu/strings.xml
@@ -142,7 +142,8 @@
<string name="app_installing_title" msgid="5864044122733792085">"Folyamatban van a(z) <xliff:g id="NAME">%1$s</xliff:g> telepítése, <xliff:g id="PROGRESS">%2$s</xliff:g> kész"</string>
<string name="app_downloading_title" msgid="8336702962104482644">"A(z) <xliff:g id="NAME">%1$s</xliff:g> letöltése, <xliff:g id="PROGRESS">%2$s</xliff:g> kész"</string>
<string name="app_waiting_download_title" msgid="7053938513995617849">"A(z) <xliff:g id="NAME">%1$s</xliff:g> telepítésre vár"</string>
- <string name="app_archived_title" msgid="7717956158562544081">"<xliff:g id="NAME">%1$s</xliff:g> archiválva. Koppintson a letöltéshez és a visszaállításhoz."</string>
+ <string name="app_archived_title" msgid="4548283110222420708">"<xliff:g id="NAME">%1$s</xliff:g> archiválva."</string>
+ <string name="app_unarchiving_action" msgid="5736107006413929484">"letöltés és visszaállítás"</string>
<string name="dialog_update_title" msgid="114234265740994042">"Alkalmazásfrissítés szükséges"</string>
<string name="dialog_update_message" msgid="4176784553982226114">"Az ikonhoz tartozó alkalmazás nincs frissítve. A parancsikon újbóli engedélyezéséhez frissítse az alkalmazást, vagy távolítsa ez az ikont."</string>
<string name="dialog_update" msgid="2178028071796141234">"Frissítés"</string>
diff --git a/res/values-hy/strings.xml b/res/values-hy/strings.xml
index ba2edb1..74ea382 100644
--- a/res/values-hy/strings.xml
+++ b/res/values-hy/strings.xml
@@ -142,7 +142,8 @@
<string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> հավելվածը տեղադրվում է, կատարված է <xliff:g id="PROGRESS">%2$s</xliff:g>-ը"</string>
<string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g>–ի ներբեռնում (<xliff:g id="PROGRESS">%2$s</xliff:g>)"</string>
<string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g>-ի տեղադրման սպասում"</string>
- <string name="app_archived_title" msgid="7717956158562544081">"<xliff:g id="NAME">%1$s</xliff:g> հավելվածն արխիվացված է։ Հպեք՝ ներբեռնելու և վերականգնելու համար։"</string>
+ <string name="app_archived_title" msgid="4548283110222420708">"<xliff:g id="NAME">%1$s</xliff:g> հավելվածն արխիվացված է։"</string>
+ <string name="app_unarchiving_action" msgid="5736107006413929484">"ներբեռնել և վերականգնել"</string>
<string name="dialog_update_title" msgid="114234265740994042">"Պահանջվում է թարմացնել հավելվածը"</string>
<string name="dialog_update_message" msgid="4176784553982226114">"Հավելվածը հնացել է։ Թարմացրեք այն ձեռքով, որպեսզի շարունակեք օգտագործել դյուրանցումը, կամ հեռացրեք հավելվածի պատկերակը։"</string>
<string name="dialog_update" msgid="2178028071796141234">"Թարմացնել"</string>
diff --git a/res/values-in/strings.xml b/res/values-in/strings.xml
index 40f2e57..296b12e 100644
--- a/res/values-in/strings.xml
+++ b/res/values-in/strings.xml
@@ -142,7 +142,8 @@
<string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> sedang diinstal, <xliff:g id="PROGRESS">%2$s</xliff:g> selesai"</string>
<string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> sedang didownload, <xliff:g id="PROGRESS">%2$s</xliff:g> selesai"</string>
<string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> menunggu dipasang"</string>
- <string name="app_archived_title" msgid="7717956158562544081">"<xliff:g id="NAME">%1$s</xliff:g> diarsipkan. Ketuk untuk mendownload dan memulihkan."</string>
+ <string name="app_archived_title" msgid="4548283110222420708">"<xliff:g id="NAME">%1$s</xliff:g> diarsipkan."</string>
+ <string name="app_unarchiving_action" msgid="5736107006413929484">"download dan pulihkan"</string>
<string name="dialog_update_title" msgid="114234265740994042">"Aplikasi perlu diupdate"</string>
<string name="dialog_update_message" msgid="4176784553982226114">"Aplikasi untuk ikon ini belum diupdate. Anda dapat mengupdate secara manual untuk mengaktifkan kembali pintasan ini, atau hapus ikon."</string>
<string name="dialog_update" msgid="2178028071796141234">"Update"</string>
diff --git a/res/values-is/strings.xml b/res/values-is/strings.xml
index 0014317..ad1742d 100644
--- a/res/values-is/strings.xml
+++ b/res/values-is/strings.xml
@@ -142,7 +142,8 @@
<string name="app_installing_title" msgid="5864044122733792085">"Setur upp <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="PROGRESS">%2$s</xliff:g> lokið"</string>
<string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> í niðurhali, <xliff:g id="PROGRESS">%2$s</xliff:g> lokið"</string>
<string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> bíður uppsetningar"</string>
- <string name="app_archived_title" msgid="7717956158562544081">"<xliff:g id="NAME">%1$s</xliff:g> er í geymslu. Ýttu til að sækja og endurheimta."</string>
+ <string name="app_archived_title" msgid="4548283110222420708">"<xliff:g id="NAME">%1$s</xliff:g> er í geymslu."</string>
+ <string name="app_unarchiving_action" msgid="5736107006413929484">"sækja og endurheimta"</string>
<string name="dialog_update_title" msgid="114234265740994042">"Uppfæra þarf forritið"</string>
<string name="dialog_update_message" msgid="4176784553982226114">"Forritið fyrir þetta tákn er ekki uppfært. Þú getur uppfært það handvirkt til að kveikja aftur á þessari flýtileið eða fjarlægt táknið."</string>
<string name="dialog_update" msgid="2178028071796141234">"Uppfæra"</string>
diff --git a/res/values-it/strings.xml b/res/values-it/strings.xml
index 9cdb5aa..3177dba 100644
--- a/res/values-it/strings.xml
+++ b/res/values-it/strings.xml
@@ -142,7 +142,8 @@
<string name="app_installing_title" msgid="5864044122733792085">"Installazione di <xliff:g id="NAME">%1$s</xliff:g>, completamento: <xliff:g id="PROGRESS">%2$s</xliff:g>"</string>
<string name="app_downloading_title" msgid="8336702962104482644">"Download di <xliff:g id="NAME">%1$s</xliff:g> in corso, <xliff:g id="PROGRESS">%2$s</xliff:g> completato"</string>
<string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> in attesa di installazione"</string>
- <string name="app_archived_title" msgid="7717956158562544081">"App <xliff:g id="NAME">%1$s</xliff:g> archiviata. Tocca per scaricare e ripristinare."</string>
+ <string name="app_archived_title" msgid="4548283110222420708">"App <xliff:g id="NAME">%1$s</xliff:g> archiviata."</string>
+ <string name="app_unarchiving_action" msgid="5736107006413929484">"download e ripristino"</string>
<string name="dialog_update_title" msgid="114234265740994042">"È necessario aggiornare l\'app"</string>
<string name="dialog_update_message" msgid="4176784553982226114">"L\'app relativa a questa icona non è aggiornata. Puoi eseguire manualmente l\'aggiornamento per riattivare questa scorciatoia oppure rimuovere l\'icona."</string>
<string name="dialog_update" msgid="2178028071796141234">"Aggiorna"</string>
diff --git a/res/values-iw/strings.xml b/res/values-iw/strings.xml
index 82eb5f8..823e915 100644
--- a/res/values-iw/strings.xml
+++ b/res/values-iw/strings.xml
@@ -142,7 +142,8 @@
<string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> בתהליך התקנה, <xliff:g id="PROGRESS">%2$s</xliff:g> הושלמו"</string>
<string name="app_downloading_title" msgid="8336702962104482644">"הורדת <xliff:g id="NAME">%1$s</xliff:g> מתבצעת, <xliff:g id="PROGRESS">%2$s</xliff:g> הושלמו"</string>
<string name="app_waiting_download_title" msgid="7053938513995617849">"מחכה להתקנה של <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="app_archived_title" msgid="7717956158562544081">"אפליקציית <xliff:g id="NAME">%1$s</xliff:g> הועברה לארכיון. אפשר להקיש כדי להוריד ולשחזר אותה."</string>
+ <string name="app_archived_title" msgid="4548283110222420708">"אפליקציית <xliff:g id="NAME">%1$s</xliff:g> הועברה לארכיון."</string>
+ <string name="app_unarchiving_action" msgid="5736107006413929484">"הורדה ושחזור"</string>
<string name="dialog_update_title" msgid="114234265740994042">"נדרש עדכון לאפליקציה"</string>
<string name="dialog_update_message" msgid="4176784553982226114">"האפליקציה של הסמל הזה לא מעודכנת. אפשר לעדכן אותה ידנית כדי להפעיל מחדש את קיצור הדרך הזה, או להסיר את הסמל."</string>
<string name="dialog_update" msgid="2178028071796141234">"עדכון"</string>
diff --git a/res/values-ja/strings.xml b/res/values-ja/strings.xml
index fc89041..0ffd066 100644
--- a/res/values-ja/strings.xml
+++ b/res/values-ja/strings.xml
@@ -142,7 +142,8 @@
<string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> をインストールしています: <xliff:g id="PROGRESS">%2$s</xliff:g> 完了"</string>
<string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g>をダウンロード中、<xliff:g id="PROGRESS">%2$s</xliff:g>完了"</string>
<string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g>のインストール待ち"</string>
- <string name="app_archived_title" msgid="7717956158562544081">"<xliff:g id="NAME">%1$s</xliff:g>はアーカイブ済みです。ダウンロードして復元するには、タップしてください。"</string>
+ <string name="app_archived_title" msgid="4548283110222420708">"<xliff:g id="NAME">%1$s</xliff:g> はアーカイブ済みです。"</string>
+ <string name="app_unarchiving_action" msgid="5736107006413929484">"ダウンロードして復元"</string>
<string name="dialog_update_title" msgid="114234265740994042">"アプリの更新が必要"</string>
<string name="dialog_update_message" msgid="4176784553982226114">"このアイコンのアプリは更新されていません。手動で更新して、このショートカットを再度有効にできます。また、アイコンを削除することもできます。"</string>
<string name="dialog_update" msgid="2178028071796141234">"更新"</string>
diff --git a/res/values-ka/strings.xml b/res/values-ka/strings.xml
index f099bcd..a2b83df 100644
--- a/res/values-ka/strings.xml
+++ b/res/values-ka/strings.xml
@@ -142,7 +142,8 @@
<string name="app_installing_title" msgid="5864044122733792085">"ინსტალირდება <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="PROGRESS">%2$s</xliff:g> დასრულებულია"</string>
<string name="app_downloading_title" msgid="8336702962104482644">"მიმდინარეობს <xliff:g id="NAME">%1$s</xliff:g>-ის ჩამოტვირთვა, <xliff:g id="PROGRESS">%2$s</xliff:g> დასრულდა"</string>
<string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> ელოდება ინსტალაციას"</string>
- <string name="app_archived_title" msgid="7717956158562544081">"<xliff:g id="NAME">%1$s</xliff:g> დაარქივებულია. შეეხეთ გადმოსაწერად და აღსადგენად."</string>
+ <string name="app_archived_title" msgid="4548283110222420708">"<xliff:g id="NAME">%1$s</xliff:g> დაარქივებულია."</string>
+ <string name="app_unarchiving_action" msgid="5736107006413929484">"ჩამოტვირთვა და აღდგენა"</string>
<string name="dialog_update_title" msgid="114234265740994042">"საჭიროა აპის განახლება"</string>
<string name="dialog_update_message" msgid="4176784553982226114">"ამ ხატულის აპი განახლებული არ არის. შეგიძლიათ, ხელით განაახლოთ ამ მალსახმობის ხელახლა გასააქტიურებლად, ან ამოშალოთ ხატულა."</string>
<string name="dialog_update" msgid="2178028071796141234">"განახლება"</string>
diff --git a/res/values-kk/strings.xml b/res/values-kk/strings.xml
index ebaacc9..0e5472c 100644
--- a/res/values-kk/strings.xml
+++ b/res/values-kk/strings.xml
@@ -142,7 +142,8 @@
<string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> орнатылуда, <xliff:g id="PROGRESS">%2$s</xliff:g> аяқталды"</string>
<string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> жүктелуде, <xliff:g id="PROGRESS">%2$s</xliff:g> аяқталды"</string>
<string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> орнату күтілуде"</string>
- <string name="app_archived_title" msgid="7717956158562544081">"<xliff:g id="NAME">%1$s</xliff:g> мұрағатталды. Жүктеп алу және қалпына келтіру үшін түртіңіз."</string>
+ <string name="app_archived_title" msgid="4548283110222420708">"<xliff:g id="NAME">%1$s</xliff:g> мұрағатталды."</string>
+ <string name="app_unarchiving_action" msgid="5736107006413929484">"жүктеп алу және қалпына келтіру"</string>
<string name="dialog_update_title" msgid="114234265740994042">"Қолданбаны жаңарту қажет"</string>
<string name="dialog_update_message" msgid="4176784553982226114">"Осы белгіше үшін қолданба жаңартылмаған. Оны қолмен жаңартып, осы таңбашаны қайта іске қоса аласыз немесе белгішені өшіріп тастаңыз."</string>
<string name="dialog_update" msgid="2178028071796141234">"Жаңарту"</string>
diff --git a/res/values-km/strings.xml b/res/values-km/strings.xml
index b05eeb0..1f06e35 100644
--- a/res/values-km/strings.xml
+++ b/res/values-km/strings.xml
@@ -142,7 +142,8 @@
<string name="app_installing_title" msgid="5864044122733792085">"កំពុងដំឡើង <xliff:g id="NAME">%1$s</xliff:g>, បានបញ្ចប់ <xliff:g id="PROGRESS">%2$s</xliff:g>"</string>
<string name="app_downloading_title" msgid="8336702962104482644">"កំពុងដោនឡូត <xliff:g id="NAME">%1$s</xliff:g> បានបញ្ចប់ <xliff:g id="PROGRESS">%2$s</xliff:g>"</string>
<string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> កំពុងរង់ចាំការដំឡើង"</string>
- <string name="app_archived_title" msgid="7717956158562544081">"<xliff:g id="NAME">%1$s</xliff:g> ត្រូវបានទុកក្នុងបណ្ណសារ។ សូមចុចដើម្បីទាញយក និងស្ដារ។"</string>
+ <string name="app_archived_title" msgid="4548283110222420708">"<xliff:g id="NAME">%1$s</xliff:g> ត្រូវបានទុកក្នុងបណ្ណសារ។"</string>
+ <string name="app_unarchiving_action" msgid="5736107006413929484">"ទាញយក និងស្ដារ"</string>
<string name="dialog_update_title" msgid="114234265740994042">"តម្រូវឱ្យមានកំណែកម្មវិធីថ្មី"</string>
<string name="dialog_update_message" msgid="4176784553982226114">"កម្មវិធីសម្រាប់រូបតំណាងនេះមិនត្រូវបានដំឡើងកំណែទេ។ អ្នកអាចដំឡើងកំណែដោយផ្ទាល់ ដើម្បីបើកផ្លូវកាត់នេះឡើងវិញ ឬលុបរូបតំណាងនេះ។"</string>
<string name="dialog_update" msgid="2178028071796141234">"ដំឡើងកំណែ"</string>
diff --git a/res/values-kn/strings.xml b/res/values-kn/strings.xml
index 786cea1..08b4e47 100644
--- a/res/values-kn/strings.xml
+++ b/res/values-kn/strings.xml
@@ -142,7 +142,8 @@
<string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> ಇನ್ಸ್ಟಾಲ್ ಮಾಡಲಾಗುತ್ತಿದೆ, <xliff:g id="PROGRESS">%2$s</xliff:g> ಪೂರ್ಣಗೊಂಡಿದೆ"</string>
<string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> ಡೌನ್ಲೋಡ್ ಮಾಡಲಾಗುತ್ತಿದೆ, <xliff:g id="PROGRESS">%2$s</xliff:g> ಪೂರ್ಣಗೊಂಡಿದೆ"</string>
<string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> ಸ್ಥಾಪಿಸಲು ಕಾಯಲಾಗುತ್ತಿದೆ"</string>
- <string name="app_archived_title" msgid="7717956158562544081">"<xliff:g id="NAME">%1$s</xliff:g> ಅನ್ನು ಆರ್ಕೈವ್ ಮಾಡಲಾಗಿದೆ. ಡೌನ್ಲೋಡ್ ಮಾಡಲು ಮತ್ತು ಮರುಸ್ಥಾಪಿಸಲು ಟ್ಯಾಪ್ ಮಾಡಿ."</string>
+ <string name="app_archived_title" msgid="4548283110222420708">"<xliff:g id="NAME">%1$s</xliff:g> ಅನ್ನು ಆರ್ಕೈವ್ ಮಾಡಲಾಗಿದೆ."</string>
+ <string name="app_unarchiving_action" msgid="5736107006413929484">"ಡೌನ್ಲೋಡ್ ಮಾಡಿ ಮತ್ತು ಮರುಸ್ಥಾಪಿಸಿ"</string>
<string name="dialog_update_title" msgid="114234265740994042">"ಆ್ಯಪ್ ಅಪ್ಡೇಟ್ ಅಗತ್ಯವಿದೆ"</string>
<string name="dialog_update_message" msgid="4176784553982226114">"ಈ ಐಕಾನ್ಗಾಗಿ ಆ್ಯಪ್ ಅನ್ನು ಅಪ್ಡೇಟ್ ಮಾಡಲಾಗಿಲ್ಲ. ಈ ಶಾರ್ಟ್ಕಟ್ ಅನ್ನು ಮರು-ಸಕ್ರಿಯಗೊಳಿಸಲು ನೀವು ಹಸ್ತಚಾಲಿತವಾಗಿ ಅಪ್ಡೇಟ್ ಮಾಡಬಹುದು ಅಥವಾ ಐಕಾನ್ ಅನ್ನು ತೆಗೆದುಹಾಕಬಹುದು."</string>
<string name="dialog_update" msgid="2178028071796141234">"ಅಪ್ಡೇಟ್ ಮಾಡಿ"</string>
diff --git a/res/values-ko/strings.xml b/res/values-ko/strings.xml
index 0e7ef7b..2130382 100644
--- a/res/values-ko/strings.xml
+++ b/res/values-ko/strings.xml
@@ -142,7 +142,8 @@
<string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> 설치 중, <xliff:g id="PROGRESS">%2$s</xliff:g> 완료"</string>
<string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> 다운로드 중, <xliff:g id="PROGRESS">%2$s</xliff:g> 완료"</string>
<string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> 설치 대기 중"</string>
- <string name="app_archived_title" msgid="7717956158562544081">"<xliff:g id="NAME">%1$s</xliff:g> 앱이 보관처리되었습니다. 탭하여 다운로드하고 복원하세요"</string>
+ <string name="app_archived_title" msgid="4548283110222420708">"<xliff:g id="NAME">%1$s</xliff:g> 앱이 보관처리되었습니다"</string>
+ <string name="app_unarchiving_action" msgid="5736107006413929484">"다운로드 및 복원"</string>
<string name="dialog_update_title" msgid="114234265740994042">"앱 업데이트 필요"</string>
<string name="dialog_update_message" msgid="4176784553982226114">"바로가기 아이콘의 앱이 업데이트되지 않았습니다. 직접 업데이트하여 앱 바로가기를 다시 사용할 수 있도록 하거나 아이콘을 삭제하세요."</string>
<string name="dialog_update" msgid="2178028071796141234">"업데이트"</string>
diff --git a/res/values-ky/strings.xml b/res/values-ky/strings.xml
index 753d2dd..b803fa5 100644
--- a/res/values-ky/strings.xml
+++ b/res/values-ky/strings.xml
@@ -142,7 +142,8 @@
<string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> орнотулууда, <xliff:g id="PROGRESS">%2$s</xliff:g> аткарылды"</string>
<string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> жүктөлүп алынууда, <xliff:g id="PROGRESS">%2$s</xliff:g> аяктады"</string>
<string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> орнотулушу күтүлүүдө"</string>
- <string name="app_archived_title" msgid="7717956158562544081">"<xliff:g id="NAME">%1$s</xliff:g> архивделди. Жүктөп алуу жана калыбына келтирүү үчүн таптаңыз."</string>
+ <string name="app_archived_title" msgid="4548283110222420708">"<xliff:g id="NAME">%1$s</xliff:g> архивделди."</string>
+ <string name="app_unarchiving_action" msgid="5736107006413929484">"жүктөп алуу жана калыбына келтирүү"</string>
<string name="dialog_update_title" msgid="114234265740994042">"Колдонмону жаңыртыңыз"</string>
<string name="dialog_update_message" msgid="4176784553982226114">"Бул сүрөтчөнүн колдонмосу жаңыртылган эмес. Ыкчам баскычты кайра иштетүү үчүн аны кол менен жаңыртып же сүрөтчөнү өчүрүп койсоңуз болот."</string>
<string name="dialog_update" msgid="2178028071796141234">"Жаңыртуу"</string>
diff --git a/res/values-lo/strings.xml b/res/values-lo/strings.xml
index 9ddf0b3..d90f563 100644
--- a/res/values-lo/strings.xml
+++ b/res/values-lo/strings.xml
@@ -142,7 +142,8 @@
<string name="app_installing_title" msgid="5864044122733792085">"ກຳລັງຕິດຕັ້ງ <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="PROGRESS">%2$s</xliff:g> ສຳເລັດແລ້ວ"</string>
<string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> ກຳລັງດາວໂຫຼດ, <xliff:g id="PROGRESS">%2$s</xliff:g> ສຳເລັດ"</string>
<string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> ກຳລັງລໍຖ້າຕິດຕັ້ງ"</string>
- <string name="app_archived_title" msgid="7717956158562544081">"<xliff:g id="NAME">%1$s</xliff:g> ຖືກເກັບໄວ້ໃນແຟ້ມ. ແຕະເພື່ອດາວໂຫຼດ ແລະ ກູ້ຄືນ."</string>
+ <string name="app_archived_title" msgid="4548283110222420708">"<xliff:g id="NAME">%1$s</xliff:g> ຖືກເກັບໄວ້ໃນແຟ້ມ."</string>
+ <string name="app_unarchiving_action" msgid="5736107006413929484">"ດາວໂຫຼດ ແລະ ກູ້ຄືນ"</string>
<string name="dialog_update_title" msgid="114234265740994042">"ຈຳເປັນຕ້ອງອັບເດດແອັບ"</string>
<string name="dialog_update_message" msgid="4176784553982226114">"ບໍ່ໄດ້ອັບເດດແອັບສຳລັບໄອຄອນນີ້. ທ່ານສາມາດອັບເດດເອງໄດ້ເພື່ອເປີດການນຳໃຊ້ທາງລັດນີ້ຄືນໃໝ່ ຫຼື ລຶບໄອຄອນດັ່ງກ່າວອອກ."</string>
<string name="dialog_update" msgid="2178028071796141234">"ອັບເດດ"</string>
diff --git a/res/values-lt/strings.xml b/res/values-lt/strings.xml
index 6879862..b9495db 100644
--- a/res/values-lt/strings.xml
+++ b/res/values-lt/strings.xml
@@ -142,7 +142,8 @@
<string name="app_installing_title" msgid="5864044122733792085">"Įdiegiama: „<xliff:g id="NAME">%1$s</xliff:g>“; baigta: <xliff:g id="PROGRESS">%2$s</xliff:g>"</string>
<string name="app_downloading_title" msgid="8336702962104482644">"Atsisiunčiama programa „<xliff:g id="NAME">%1$s</xliff:g>“, <xliff:g id="PROGRESS">%2$s</xliff:g> baigta"</string>
<string name="app_waiting_download_title" msgid="7053938513995617849">"Laukiama, kol bus įdiegta programa „<xliff:g id="NAME">%1$s</xliff:g>“"</string>
- <string name="app_archived_title" msgid="7717956158562544081">"Programa „<xliff:g id="NAME">%1$s</xliff:g>“ suarchyvuota. Palieskite, jei norite atsisiųsti ir atkurti."</string>
+ <string name="app_archived_title" msgid="4548283110222420708">"Programa „<xliff:g id="NAME">%1$s</xliff:g>“ suarchyvuota."</string>
+ <string name="app_unarchiving_action" msgid="5736107006413929484">"atsisiųsti ir atkurti"</string>
<string name="dialog_update_title" msgid="114234265740994042">"Būtina atnaujinti programą"</string>
<string name="dialog_update_message" msgid="4176784553982226114">"Šios piktogramos programa neatnaujinta. Galite patys atnaujinti, kad iš naujo įgalintumėte šį spartųjį klavišą, arba pašalinkite piktogramą."</string>
<string name="dialog_update" msgid="2178028071796141234">"Atnaujinti"</string>
diff --git a/res/values-lv/strings.xml b/res/values-lv/strings.xml
index 8166bce..1c3faf4 100644
--- a/res/values-lv/strings.xml
+++ b/res/values-lv/strings.xml
@@ -142,7 +142,8 @@
<string name="app_installing_title" msgid="5864044122733792085">"Notiek lietotnes “<xliff:g id="NAME">%1$s</xliff:g>” instalēšana. Norise: <xliff:g id="PROGRESS">%2$s</xliff:g>."</string>
<string name="app_downloading_title" msgid="8336702962104482644">"Lietotnes <xliff:g id="NAME">%1$s</xliff:g> lejupielāde (<xliff:g id="PROGRESS">%2$s</xliff:g> pabeigti)"</string>
<string name="app_waiting_download_title" msgid="7053938513995617849">"Notiek <xliff:g id="NAME">%1$s</xliff:g> instalēšana"</string>
- <string name="app_archived_title" msgid="7717956158562544081">"Lietotne <xliff:g id="NAME">%1$s</xliff:g> ir arhivēta; lai lejupielādētu un atjaunotu, pieskarieties"</string>
+ <string name="app_archived_title" msgid="4548283110222420708">"Lietotne <xliff:g id="NAME">%1$s</xliff:g> ir arhivēta."</string>
+ <string name="app_unarchiving_action" msgid="5736107006413929484">"lejupielādēt un atjaunot"</string>
<string name="dialog_update_title" msgid="114234265740994042">"Lietotne ir jāatjaunina"</string>
<string name="dialog_update_message" msgid="4176784553982226114">"Šai ikonai paredzētā lietotne nav atjaunināta. Varat to atjaunināt manuāli, lai atkārtoti iespējotu šo saīsni, vai noņemt ikonu."</string>
<string name="dialog_update" msgid="2178028071796141234">"Atjaunināt"</string>
diff --git a/res/values-mk/strings.xml b/res/values-mk/strings.xml
index 0511c3f..cf3363e 100644
--- a/res/values-mk/strings.xml
+++ b/res/values-mk/strings.xml
@@ -142,7 +142,8 @@
<string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> се инсталира, <xliff:g id="PROGRESS">%2$s</xliff:g> завршено"</string>
<string name="app_downloading_title" msgid="8336702962104482644">"Се презема <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="PROGRESS">%2$s</xliff:g> завршено"</string>
<string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> чека да се инсталира"</string>
- <string name="app_archived_title" msgid="7717956158562544081">"Апликацијата <xliff:g id="NAME">%1$s</xliff:g> е архивирана. Допрете за да преземете и вратите."</string>
+ <string name="app_archived_title" msgid="4548283110222420708">"Апликацијата <xliff:g id="NAME">%1$s</xliff:g> е архивирана."</string>
+ <string name="app_unarchiving_action" msgid="5736107006413929484">"преземете и вратете"</string>
<string name="dialog_update_title" msgid="114234265740994042">"Потребно е ажурирање на апликацијата"</string>
<string name="dialog_update_message" msgid="4176784553982226114">"Апликацијата за оваа икона не е ажурирана. Може да ажурирате рачно за да повторно се овозможи кратенкава или отстранете ја иконата."</string>
<string name="dialog_update" msgid="2178028071796141234">"Ажурирај"</string>
diff --git a/res/values-ml/strings.xml b/res/values-ml/strings.xml
index b9e68e0..dd6aa71 100644
--- a/res/values-ml/strings.xml
+++ b/res/values-ml/strings.xml
@@ -142,7 +142,8 @@
<string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> ഇൻസ്റ്റാൾ ചെയ്യുന്നു, <xliff:g id="PROGRESS">%2$s</xliff:g> പൂർത്തിയായി"</string>
<string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> ഡൗൺലോഡ് ചെയ്യുന്നു, <xliff:g id="PROGRESS">%2$s</xliff:g> പൂർത്തിയായി"</string>
<string name="app_waiting_download_title" msgid="7053938513995617849">"ഇൻസ്റ്റാൾ ചെയ്യാൻ <xliff:g id="NAME">%1$s</xliff:g> കാക്കുന്നു"</string>
- <string name="app_archived_title" msgid="7717956158562544081">"<xliff:g id="NAME">%1$s</xliff:g> ആർക്കൈവ് ചെയ്തു. ഡൗൺലോഡ് ചെയ്യാനും പുനഃസ്ഥാപിക്കാനും ടാപ്പ് ചെയ്യുക."</string>
+ <string name="app_archived_title" msgid="4548283110222420708">"<xliff:g id="NAME">%1$s</xliff:g> ആർക്കൈവ് ചെയ്തു."</string>
+ <string name="app_unarchiving_action" msgid="5736107006413929484">"ഡൗൺലോഡ് ചെയ്ത് പുനഃസ്ഥാപിക്കുക"</string>
<string name="dialog_update_title" msgid="114234265740994042">"ആപ്പ് അപ്ഡേറ്റ് ചെയ്യേണ്ടതുണ്ട്"</string>
<string name="dialog_update_message" msgid="4176784553982226114">"ഈ ഐക്കണിനുള്ള ആപ്പ് അപ്ഡേറ്റ് ചെയ്തിട്ടില്ല. ഈ കുറുക്കുവഴി വീണ്ടും പ്രവർത്തനക്ഷമമാക്കാൻ നിങ്ങൾക്ക് നേരിട്ട് അപ്ഡേറ്റ് ചെയ്യാം അല്ലെങ്കിൽ ഐക്കൺ നീക്കം ചെയ്യാം."</string>
<string name="dialog_update" msgid="2178028071796141234">"അപ്ഡേറ്റ് ചെയ്യുക"</string>
diff --git a/res/values-mn/strings.xml b/res/values-mn/strings.xml
index 28a1438..659f82c 100644
--- a/res/values-mn/strings.xml
+++ b/res/values-mn/strings.xml
@@ -142,7 +142,8 @@
<string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g>-г суулгаж байна. <xliff:g id="PROGRESS">%2$s</xliff:g> дууссан"</string>
<string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g>-г татаж байна, <xliff:g id="PROGRESS">%2$s</xliff:g> татсан"</string>
<string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> нь суулгахыг хүлээж байна"</string>
- <string name="app_archived_title" msgid="7717956158562544081">"<xliff:g id="NAME">%1$s</xliff:g>-г архивласан. Татаж, сэргээхийн тулд товшино уу."</string>
+ <string name="app_archived_title" msgid="4548283110222420708">"<xliff:g id="NAME">%1$s</xliff:g>-г архивласан."</string>
+ <string name="app_unarchiving_action" msgid="5736107006413929484">"татах, сэргээх"</string>
<string name="dialog_update_title" msgid="114234265740994042">"Аппын шинэчлэлт шаардлагатай"</string>
<string name="dialog_update_message" msgid="4176784553982226114">"Энэ дүрс тэмдгийн аппыг шинэчлээгүй. Та энэ товчлолыг дахин идэвхжүүлэх эсвэл дүрсийг хасахын тулд гараар шинэчлэх боломжтой."</string>
<string name="dialog_update" msgid="2178028071796141234">"Шинэчлэх"</string>
diff --git a/res/values-mr/strings.xml b/res/values-mr/strings.xml
index 0190f1f..aa4b433 100644
--- a/res/values-mr/strings.xml
+++ b/res/values-mr/strings.xml
@@ -142,7 +142,8 @@
<string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> इंस्टॉल करत आहे, <xliff:g id="PROGRESS">%2$s</xliff:g> पूर्ण झाले"</string>
<string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> डाउनलोड होत आहे , <xliff:g id="PROGRESS">%2$s</xliff:g> पूर्ण झाले"</string>
<string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> इंस्टॉल करण्याची प्रतिक्षा करत आहे"</string>
- <string name="app_archived_title" msgid="7717956158562544081">"<xliff:g id="NAME">%1$s</xliff:g> संग्रहित केले आहे. डाउनलोड करून रिस्टोअर करण्यासाठी टॅप करा."</string>
+ <string name="app_archived_title" msgid="4548283110222420708">"<xliff:g id="NAME">%1$s</xliff:g> संग्रहित केले आहे."</string>
+ <string name="app_unarchiving_action" msgid="5736107006413929484">"डाउनलोड करून रिस्टोअर करा"</string>
<string name="dialog_update_title" msgid="114234265740994042">"अॅप अपडेट करणे आवश्यक आहे"</string>
<string name="dialog_update_message" msgid="4176784553982226114">"या आयकनसाठी अॅप अपडेट केलेले नाही. हा शॉटकर्ट पुन्हा सुरू करण्यासाठी तुम्ही मॅन्युअली अपडेट करू शकता किंवा आयकन काढून टाका."</string>
<string name="dialog_update" msgid="2178028071796141234">"अपडेट करा"</string>
diff --git a/res/values-ms/strings.xml b/res/values-ms/strings.xml
index 6941139..89345ce 100644
--- a/res/values-ms/strings.xml
+++ b/res/values-ms/strings.xml
@@ -142,7 +142,8 @@
<string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> dipasang, <xliff:g id="PROGRESS">%2$s</xliff:g> selesai"</string>
<string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> memuat turun, <xliff:g id="PROGRESS">%2$s</xliff:g> selesai"</string>
<string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> menunggu untuk dipasang"</string>
- <string name="app_archived_title" msgid="7717956158562544081">"<xliff:g id="NAME">%1$s</xliff:g> diarkibkan. Ketik untuk memuat turun dan memulihkan apl tersebut."</string>
+ <string name="app_archived_title" msgid="4548283110222420708">"<xliff:g id="NAME">%1$s</xliff:g> diarkibkan."</string>
+ <string name="app_unarchiving_action" msgid="5736107006413929484">"muat turun dan pulihkan"</string>
<string name="dialog_update_title" msgid="114234265740994042">"Kemas kini apl diperlukan"</string>
<string name="dialog_update_message" msgid="4176784553982226114">"Apl untuk ikon ini tidak dikemas kini. Anda boleh mengemas kini secara manual untuk mendayakan semula pintasan atau mengalih keluar ikon."</string>
<string name="dialog_update" msgid="2178028071796141234">"Kemas kini"</string>
diff --git a/res/values-my/strings.xml b/res/values-my/strings.xml
index 948f2eb..7d5d275 100644
--- a/res/values-my/strings.xml
+++ b/res/values-my/strings.xml
@@ -142,7 +142,8 @@
<string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> ကို ထည့်သွင်းနေသည်၊ <xliff:g id="PROGRESS">%2$s</xliff:g> ပြီးပါပြီ"</string>
<string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> ဒေါင်းလုဒ်လုပ်နေသည်၊ <xliff:g id="PROGRESS">%2$s</xliff:g> ပြီးပါပြီ"</string>
<string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> ကိုထည့်သွင်းရန်စောင့်နေသည်"</string>
- <string name="app_archived_title" msgid="7717956158562544081">"<xliff:g id="NAME">%1$s</xliff:g> ကို သိမ်းထားသည်။ ဒေါင်းလုဒ်လုပ်ပြီး ပြန်ယူရန် တို့ပါ။"</string>
+ <string name="app_archived_title" msgid="4548283110222420708">"<xliff:g id="NAME">%1$s</xliff:g> ကို သိမ်းထားသည်။"</string>
+ <string name="app_unarchiving_action" msgid="5736107006413929484">"ဒေါင်းလုဒ်လုပ်ပြီး ပြန်ယူရန်"</string>
<string name="dialog_update_title" msgid="114234265740994042">"အက်ပ်ကို အပ်ဒိတ်လုပ်ရန် လိုအပ်သည်"</string>
<string name="dialog_update_message" msgid="4176784553982226114">"ဤသင်္ကေတအတွက် အက်ပ်ကို အပ်ဒိတ်လုပ်မထားပါ။ ဤဖြတ်လမ်းလင့်ခ်ကို ပြန်ဖွင့်ရန် ကိုယ်တိုင်အပ်ဒိတ်လုပ်နိုင်သည် (သို့) သင်္ကေတကို ဖယ်ရှားနိုင်သည်။"</string>
<string name="dialog_update" msgid="2178028071796141234">"အပ်ဒိတ်လုပ်ရန်"</string>
diff --git a/res/values-nb/strings.xml b/res/values-nb/strings.xml
index 0dc6160..bb92d90 100644
--- a/res/values-nb/strings.xml
+++ b/res/values-nb/strings.xml
@@ -142,7 +142,8 @@
<string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> installerer, <xliff:g id="PROGRESS">%2$s</xliff:g> er fullført"</string>
<string name="app_downloading_title" msgid="8336702962104482644">"Laster ned <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="PROGRESS">%2$s</xliff:g> er fullført"</string>
<string name="app_waiting_download_title" msgid="7053938513995617849">"Venter på å installere <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="app_archived_title" msgid="7717956158562544081">"<xliff:g id="NAME">%1$s</xliff:g> er arkivert. Trykk for å laste ned og gjenopprette."</string>
+ <string name="app_archived_title" msgid="4548283110222420708">"<xliff:g id="NAME">%1$s</xliff:g> er arkivert."</string>
+ <string name="app_unarchiving_action" msgid="5736107006413929484">"last ned og gjenopprett"</string>
<string name="dialog_update_title" msgid="114234265740994042">"Appen må oppdateres"</string>
<string name="dialog_update_message" msgid="4176784553982226114">"Appen for dette ikonet er ikke oppdatert. Du kan oppdatere manuelt for å aktivere denne snarveien igjen, eller du kan fjerne ikonet."</string>
<string name="dialog_update" msgid="2178028071796141234">"Oppdater"</string>
diff --git a/res/values-ne/strings.xml b/res/values-ne/strings.xml
index 4fae68b..49efec9 100644
--- a/res/values-ne/strings.xml
+++ b/res/values-ne/strings.xml
@@ -142,7 +142,8 @@
<string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> इन्स्टल गरिँदै छ, <xliff:g id="PROGRESS">%2$s</xliff:g> पूरा भयो"</string>
<string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> डाउनलोड गर्दै, <xliff:g id="PROGRESS">%2$s</xliff:g> सम्पन्न"</string>
<string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> स्थापना गर्न प्रतीक्षा गर्दै"</string>
- <string name="app_archived_title" msgid="7717956158562544081">"<xliff:g id="NAME">%1$s</xliff:g> अभिलेखमा राखिएको छ। डाउनलोड गरी रिस्टोर गर्न ट्याप गर्नुहोस्।"</string>
+ <string name="app_archived_title" msgid="4548283110222420708">"<xliff:g id="NAME">%1$s</xliff:g> अभिलेखमा राखिएको छ।"</string>
+ <string name="app_unarchiving_action" msgid="5736107006413929484">"डाउनलोड र रिस्टोर गर्नुहोस्"</string>
<string name="dialog_update_title" msgid="114234265740994042">"एप अपडेट गरिनु पर्छ"</string>
<string name="dialog_update_message" msgid="4176784553982226114">"यो आइकनले जनाउने एप अपडेट गरिएको छैन। तपाईं यो सर्टकट फेरि अन गर्न म्यानुअल रूपमा अपडेट गर्न सक्नुहुन्छ वा आइकन नै हटाउनुहोस्।"</string>
<string name="dialog_update" msgid="2178028071796141234">"अपडेट गर्नुहोस्"</string>
diff --git a/res/values-nl/strings.xml b/res/values-nl/strings.xml
index 35f8d65..d898270 100644
--- a/res/values-nl/strings.xml
+++ b/res/values-nl/strings.xml
@@ -142,7 +142,8 @@
<string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> installeren, <xliff:g id="PROGRESS">%2$s</xliff:g> voltooid"</string>
<string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> wordt gedownload, <xliff:g id="PROGRESS">%2$s</xliff:g> voltooid"</string>
<string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> wacht op installatie"</string>
- <string name="app_archived_title" msgid="7717956158562544081">"<xliff:g id="NAME">%1$s</xliff:g> is gearchiveerd. Tik om te downloaden en te herstellen."</string>
+ <string name="app_archived_title" msgid="4548283110222420708">"<xliff:g id="NAME">%1$s</xliff:g> is gearchiveerd."</string>
+ <string name="app_unarchiving_action" msgid="5736107006413929484">"downloaden en herstellen"</string>
<string name="dialog_update_title" msgid="114234265740994042">"App-update vereist"</string>
<string name="dialog_update_message" msgid="4176784553982226114">"De app voor dit icoon is niet geüpdatet. Je kunt handmatig updaten om deze snelkoppeling weer aan te zetten of het icoon verwijderen."</string>
<string name="dialog_update" msgid="2178028071796141234">"Updaten"</string>
diff --git a/res/values-or/strings.xml b/res/values-or/strings.xml
index abc0787..788b168 100644
--- a/res/values-or/strings.xml
+++ b/res/values-or/strings.xml
@@ -142,7 +142,8 @@
<string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> ଇନଷ୍ଟଲ୍ କରାଯାଉଛି, <xliff:g id="PROGRESS">%2$s</xliff:g> ସମ୍ପୂର୍ଣ୍ଣ ହୋଇଛି"</string>
<string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> ଡାଉନଲୋଡ୍ ହେଉଛି, <xliff:g id="PROGRESS">%2$s</xliff:g> ସମ୍ପୂର୍ଣ୍ଣ"</string>
<string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> ଇନଷ୍ଟଲ୍ ହେବାକୁ ଅପେକ୍ଷା କରିଛି"</string>
- <string name="app_archived_title" msgid="7717956158562544081">"<xliff:g id="NAME">%1$s</xliff:g>କୁ ଆର୍କାଇଭ କରାଯାଇଛି। ଡାଉନଲୋଡ ଏବଂ ରିଷ୍ଟୋର କରିବା ପାଇଁ ଟାପ କରନ୍ତୁ।"</string>
+ <string name="app_archived_title" msgid="4548283110222420708">"<xliff:g id="NAME">%1$s</xliff:g>କୁ ଆର୍କାଇଭ କରାଯାଇଛି।"</string>
+ <string name="app_unarchiving_action" msgid="5736107006413929484">"ଡାଉନଲୋଡ ଏବଂ ରିଷ୍ଟୋର କରନ୍ତୁ"</string>
<string name="dialog_update_title" msgid="114234265740994042">"ଆପକୁ ଅପଡେଟ କରିବା ଆବଶ୍ୟକ"</string>
<string name="dialog_update_message" msgid="4176784553982226114">"ଏହି ଆଇକନ ପାଇଁ ଆପକୁ ଅପଡେଟ କରାଯାଇନାହିଁ। ଏହି ସର୍ଟକଟକୁ ପୁଣି-ସକ୍ଷମ କରିବା ପାଇଁ ଆପଣ ମାନୁଆଲୀ ଅପଡେଟ କରିପାରିବେ କିମ୍ବା ଆଇକନଟିକୁ କାଢ଼ି ଦେଇପାରିବେ।"</string>
<string name="dialog_update" msgid="2178028071796141234">"ଅପଡେଟ କରନ୍ତୁ"</string>
diff --git a/res/values-pa/strings.xml b/res/values-pa/strings.xml
index 715f61b..4dc26aa 100644
--- a/res/values-pa/strings.xml
+++ b/res/values-pa/strings.xml
@@ -142,7 +142,8 @@
<string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> ਨੂੰ ਸਥਾਪਤ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ, <xliff:g id="PROGRESS">%2$s</xliff:g> ਪੂਰਾ ਹੋਇਆ"</string>
<string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> ਡਾਉਨਲੋਡ ਹੋਰ ਰਿਹਾ ਹੈ, <xliff:g id="PROGRESS">%2$s</xliff:g> ਸੰਪੂਰਣ"</string>
<string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> ਸਥਾਪਤ ਕਰਨ ਦੀ ਉਡੀਕ ਕਰ ਰਿਹਾ ਹੈ"</string>
- <string name="app_archived_title" msgid="7717956158562544081">"<xliff:g id="NAME">%1$s</xliff:g> ਪੁਰਾਲੇਖਬੱਧ ਹੈ। ਡਾਊਨਲੋਡ ਅਤੇ ਮੁੜ-ਬਹਾਲ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ।"</string>
+ <string name="app_archived_title" msgid="4548283110222420708">"<xliff:g id="NAME">%1$s</xliff:g> ਪੁਰਾਲੇਖਬੱਧ ਹੈ।"</string>
+ <string name="app_unarchiving_action" msgid="5736107006413929484">"ਡਾਊਨਲੋਡ ਕਰ ਕੇ ਮੁੜ-ਬਹਾਲ ਕਰੋ"</string>
<string name="dialog_update_title" msgid="114234265740994042">"ਐਪ ਨੂੰ ਅੱਪਡੇਟ ਕਰਨ ਦੀ ਲੋੜ ਹੈ"</string>
<string name="dialog_update_message" msgid="4176784553982226114">"ਇਸ ਪ੍ਰਤੀਕ ਲਈ ਐਪ ਨੂੰ ਅੱਪਡੇਟ ਨਹੀਂ ਕੀਤਾ ਗਿਆ ਹੈ। ਇਸ ਸ਼ਾਰਟਕੱਟ ਨੂੰ ਮੁੜ-ਚਾਲੂ ਕਰਨ ਜਾਂ ਪ੍ਰਤੀਕ ਨੂੰ ਹਟਾਉਣ ਲਈ ਤੁਸੀਂ ਹੱਥੀਂ ਅੱਪਡੇਟ ਕਰ ਸਕਦੇ ਹੋ।"</string>
<string name="dialog_update" msgid="2178028071796141234">"ਅੱਪਡੇਟ ਕਰੋ"</string>
diff --git a/res/values-pl/strings.xml b/res/values-pl/strings.xml
index 47f8a51..1b0f841 100644
--- a/res/values-pl/strings.xml
+++ b/res/values-pl/strings.xml
@@ -142,7 +142,8 @@
<string name="app_installing_title" msgid="5864044122733792085">"Instaluję aplikację <xliff:g id="NAME">%1$s</xliff:g>, postęp: <xliff:g id="PROGRESS">%2$s</xliff:g>"</string>
<string name="app_downloading_title" msgid="8336702962104482644">"Pobieranie elementu <xliff:g id="NAME">%1$s</xliff:g>, ukończono: <xliff:g id="PROGRESS">%2$s</xliff:g>"</string>
<string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> oczekuje na instalację"</string>
- <string name="app_archived_title" msgid="7717956158562544081">"Aplikacja <xliff:g id="NAME">%1$s</xliff:g> jest zarchiwizowana. Kliknij, aby ją pobrać i przywrócić."</string>
+ <string name="app_archived_title" msgid="4548283110222420708">"Aplikacja <xliff:g id="NAME">%1$s</xliff:g> jest zarchiwizowana."</string>
+ <string name="app_unarchiving_action" msgid="5736107006413929484">"pobierz i przywróć"</string>
<string name="dialog_update_title" msgid="114234265740994042">"Wymagana aktualizacja aplikacji"</string>
<string name="dialog_update_message" msgid="4176784553982226114">"Aplikacja z tą ikoną nie jest aktualizowana. Możesz zaktualizować ją ręcznie, aby ponownie uruchomić ten skrót, lub usunąć ikonę."</string>
<string name="dialog_update" msgid="2178028071796141234">"Aktualizuj"</string>
diff --git a/res/values-pt-rPT/strings.xml b/res/values-pt-rPT/strings.xml
index d2b214e..2cb3c8f 100644
--- a/res/values-pt-rPT/strings.xml
+++ b/res/values-pt-rPT/strings.xml
@@ -142,7 +142,8 @@
<string name="app_installing_title" msgid="5864044122733792085">"A instalar <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="PROGRESS">%2$s</xliff:g> concluído"</string>
<string name="app_downloading_title" msgid="8336702962104482644">"A transferir o <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="PROGRESS">%2$s</xliff:g> concluído"</string>
<string name="app_waiting_download_title" msgid="7053938513995617849">"A aguardar a instalação do <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="app_archived_title" msgid="7717956158562544081">"A app <xliff:g id="NAME">%1$s</xliff:g> está arquivada. Toque para transferir e restaurar."</string>
+ <string name="app_archived_title" msgid="4548283110222420708">"A app <xliff:g id="NAME">%1$s</xliff:g> está arquivada."</string>
+ <string name="app_unarchiving_action" msgid="5736107006413929484">"transferir e restaurar"</string>
<string name="dialog_update_title" msgid="114234265740994042">"Atualização da app necessária"</string>
<string name="dialog_update_message" msgid="4176784553982226114">"A app deste ícone não está atualizada. Pode atualizar manualmente para reativar este atalho ou remover o ícone."</string>
<string name="dialog_update" msgid="2178028071796141234">"Atualizar"</string>
diff --git a/res/values-pt/strings.xml b/res/values-pt/strings.xml
index f864b3e..414f55d 100644
--- a/res/values-pt/strings.xml
+++ b/res/values-pt/strings.xml
@@ -142,7 +142,8 @@
<string name="app_installing_title" msgid="5864044122733792085">"Instalando <xliff:g id="NAME">%1$s</xliff:g>. <xliff:g id="PROGRESS">%2$s</xliff:g> concluído"</string>
<string name="app_downloading_title" msgid="8336702962104482644">"Fazendo download de <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="PROGRESS">%2$s</xliff:g> concluído"</string>
<string name="app_waiting_download_title" msgid="7053938513995617849">"Aguardando instalação de <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="app_archived_title" msgid="7717956158562544081">"O app <xliff:g id="NAME">%1$s</xliff:g> está arquivado. Toque para baixar e restaurar."</string>
+ <string name="app_archived_title" msgid="4548283110222420708">"O app <xliff:g id="NAME">%1$s</xliff:g> está arquivado."</string>
+ <string name="app_unarchiving_action" msgid="5736107006413929484">"baixar e restaurar"</string>
<string name="dialog_update_title" msgid="114234265740994042">"Atualização obrigatória do app"</string>
<string name="dialog_update_message" msgid="4176784553982226114">"O app desse ícone não está atualizado. Você pode remover o ícone ou atualizar o app manualmente para reativar esse atalho."</string>
<string name="dialog_update" msgid="2178028071796141234">"Atualizar"</string>
diff --git a/res/values-ro/strings.xml b/res/values-ro/strings.xml
index 30e33dc..24e6cde 100644
--- a/res/values-ro/strings.xml
+++ b/res/values-ro/strings.xml
@@ -142,7 +142,8 @@
<string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> se instalează, <xliff:g id="PROGRESS">%2$s</xliff:g> finalizat"</string>
<string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> se descarcă (finalizat <xliff:g id="PROGRESS">%2$s</xliff:g>)"</string>
<string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> așteaptă instalarea"</string>
- <string name="app_archived_title" msgid="7717956158562544081">"<xliff:g id="NAME">%1$s</xliff:g> s-a arhivat. Atinge pentru a descărca și restabili."</string>
+ <string name="app_archived_title" msgid="4548283110222420708">"<xliff:g id="NAME">%1$s</xliff:g> s-a arhivat."</string>
+ <string name="app_unarchiving_action" msgid="5736107006413929484">"descarcă și restabilește"</string>
<string name="dialog_update_title" msgid="114234265740994042">"Este necesară actualizarea aplicației"</string>
<string name="dialog_update_message" msgid="4176784553982226114">"Aplicația pentru această pictogramă nu este actualizată. Poți să actualizezi manual ca să reactivezi comanda rapidă sau să elimini pictograma."</string>
<string name="dialog_update" msgid="2178028071796141234">"Actualizează"</string>
diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml
index 126e3ad..f94fd59 100644
--- a/res/values-ru/strings.xml
+++ b/res/values-ru/strings.xml
@@ -142,7 +142,8 @@
<string name="app_installing_title" msgid="5864044122733792085">"Установка приложения \"<xliff:g id="NAME">%1$s</xliff:g>\" (выполнено <xliff:g id="PROGRESS">%2$s</xliff:g>)"</string>
<string name="app_downloading_title" msgid="8336702962104482644">"Скачивается \"<xliff:g id="NAME">%1$s</xliff:g>\" (<xliff:g id="PROGRESS">%2$s</xliff:g>)"</string>
<string name="app_waiting_download_title" msgid="7053938513995617849">"Ожидание установки \"<xliff:g id="NAME">%1$s</xliff:g>\""</string>
- <string name="app_archived_title" msgid="7717956158562544081">"Приложение \"<xliff:g id="NAME">%1$s</xliff:g>\" находится в архиве. Нажмите, чтобы скачать его и восстановить"</string>
+ <string name="app_archived_title" msgid="4548283110222420708">"Приложение \"<xliff:g id="NAME">%1$s</xliff:g>\" находится в архиве."</string>
+ <string name="app_unarchiving_action" msgid="5736107006413929484">"скачать и восстановить"</string>
<string name="dialog_update_title" msgid="114234265740994042">"Обновите приложение"</string>
<string name="dialog_update_message" msgid="4176784553982226114">"Эта версия приложения устарела. Обновите его вручную, чтобы снова пользоваться ярлыком, или удалите значок."</string>
<string name="dialog_update" msgid="2178028071796141234">"Обновить"</string>
diff --git a/res/values-si/strings.xml b/res/values-si/strings.xml
index 328f2f5..1938c54 100644
--- a/res/values-si/strings.xml
+++ b/res/values-si/strings.xml
@@ -142,7 +142,8 @@
<string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> ස්ථාපනය කරමින්, <xliff:g id="PROGRESS">%2$s</xliff:g> සම්පූර්ණයි"</string>
<string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> බාගත කරමින්, <xliff:g id="PROGRESS">%2$s</xliff:g> සම්පූර්ණයි"</string>
<string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> ස්ථාපනය කිරීමට බලා සිටිමින්"</string>
- <string name="app_archived_title" msgid="7717956158562544081">"<xliff:g id="NAME">%1$s</xliff:g> සංරක්ෂිතයි. බා ගෙන ප්රතිසාධන කිරීමට තට්ටු කරන්න."</string>
+ <string name="app_archived_title" msgid="4548283110222420708">"<xliff:g id="NAME">%1$s</xliff:g> ලේඛනාරක්ෂණය කර ඇත."</string>
+ <string name="app_unarchiving_action" msgid="5736107006413929484">"බාගත කර ප්රතිසාධනය කරන්න"</string>
<string name="dialog_update_title" msgid="114234265740994042">"යෙදුම් යාවත්කාලීනයක් අවශ්යයි"</string>
<string name="dialog_update_message" msgid="4176784553982226114">"මෙම නිරූපකය සඳහා යෙදුම යාවත්කාලීන කර නැත. ඔබට මෙම කෙටි මඟ යළි සබල කිරීමට හෝ නිරූපකය ඉවත් කිරීමට හස්තීයව යාවත්කාලීන කළ හැකිය."</string>
<string name="dialog_update" msgid="2178028071796141234">"යාවත්කාලීන කරන්න"</string>
diff --git a/res/values-sk/strings.xml b/res/values-sk/strings.xml
index 88cb1b1..528ba28 100644
--- a/res/values-sk/strings.xml
+++ b/res/values-sk/strings.xml
@@ -142,7 +142,8 @@
<string name="app_installing_title" msgid="5864044122733792085">"Inštaluje sa <xliff:g id="NAME">%1$s</xliff:g>. Dokončené: <xliff:g id="PROGRESS">%2$s</xliff:g>."</string>
<string name="app_downloading_title" msgid="8336702962104482644">"Sťahuje sa aplikácia <xliff:g id="NAME">%1$s</xliff:g>. Stiahnuté: <xliff:g id="PROGRESS">%2$s</xliff:g>"</string>
<string name="app_waiting_download_title" msgid="7053938513995617849">"Aplikácia <xliff:g id="NAME">%1$s</xliff:g> čaká na inštaláciu"</string>
- <string name="app_archived_title" msgid="7717956158562544081">"Aplikácia <xliff:g id="NAME">%1$s</xliff:g> je archivovaná. Klepnutím ju stiahnite a obnovte."</string>
+ <string name="app_archived_title" msgid="4548283110222420708">"Aplikácia <xliff:g id="NAME">%1$s</xliff:g> je archivovaná."</string>
+ <string name="app_unarchiving_action" msgid="5736107006413929484">"stiahnuť a obnoviť"</string>
<string name="dialog_update_title" msgid="114234265740994042">"Vyžaduje sa aktualizácia aplikácie"</string>
<string name="dialog_update_message" msgid="4176784553982226114">"Aplikácia, ktorú zastupuje táto ikona, nie je aktualizovaná. Môžete ju ručne aktualizovať, aby odkaz znova fungoval, prípadne môžete ikonu odstrániť."</string>
<string name="dialog_update" msgid="2178028071796141234">"Aktualizovať"</string>
diff --git a/res/values-sl/strings.xml b/res/values-sl/strings.xml
index 37a02e4..e0288c4 100644
--- a/res/values-sl/strings.xml
+++ b/res/values-sl/strings.xml
@@ -142,7 +142,8 @@
<string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> se namešča, dokončano: <xliff:g id="PROGRESS">%2$s</xliff:g>"</string>
<string name="app_downloading_title" msgid="8336702962104482644">"Prenašanje aplikacije <xliff:g id="NAME">%1$s</xliff:g>; preneseno <xliff:g id="PROGRESS">%2$s</xliff:g>"</string>
<string name="app_waiting_download_title" msgid="7053938513995617849">"Aplikacija <xliff:g id="NAME">%1$s</xliff:g> čaka na namestitev"</string>
- <string name="app_archived_title" msgid="7717956158562544081">"Aplikacija <xliff:g id="NAME">%1$s</xliff:g> je arhivirana. Dotaknite se za prenos in obnovitev."</string>
+ <string name="app_archived_title" msgid="4548283110222420708">"Aplikacija <xliff:g id="NAME">%1$s</xliff:g> je arhivirana."</string>
+ <string name="app_unarchiving_action" msgid="5736107006413929484">"prenos in obnovitev"</string>
<string name="dialog_update_title" msgid="114234265740994042">"Zahtevana je posodobitev aplikacije"</string>
<string name="dialog_update_message" msgid="4176784553982226114">"Aplikacija za to ikono ni posodobljena. Lahko jo ročno posodobite, da znova omogočite to bližnjico, ali pa odstranite ikono."</string>
<string name="dialog_update" msgid="2178028071796141234">"Posodobi"</string>
diff --git a/res/values-sq/strings.xml b/res/values-sq/strings.xml
index 5be991a..96b1bff 100644
--- a/res/values-sq/strings.xml
+++ b/res/values-sq/strings.xml
@@ -142,7 +142,8 @@
<string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> po instalohet, <xliff:g id="PROGRESS">%2$s</xliff:g> i përfunduar"</string>
<string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> po shkarkohet, <xliff:g id="PROGRESS">%2$s</xliff:g> të përfunduara"</string>
<string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> po pret të instalohet"</string>
- <string name="app_archived_title" msgid="7717956158562544081">"<xliff:g id="NAME">%1$s</xliff:g> është arkivuar. Trokit për ta shkarkuar dhe restauruar."</string>
+ <string name="app_archived_title" msgid="4548283110222420708">"<xliff:g id="NAME">%1$s</xliff:g> është arkivuar."</string>
+ <string name="app_unarchiving_action" msgid="5736107006413929484">"shkarko dhe restauro"</string>
<string name="dialog_update_title" msgid="114234265740994042">"Kërkohet përditësimi i aplikacionit"</string>
<string name="dialog_update_message" msgid="4176784553982226114">"Aplikacioni për këtë ikonë nuk është përditësuar. Mund ta përditësosh manualisht për të riaktivizuar këtë shkurtore ose hiq ikonën."</string>
<string name="dialog_update" msgid="2178028071796141234">"Përditëso"</string>
diff --git a/res/values-sr/strings.xml b/res/values-sr/strings.xml
index afaeb61..054ddff 100644
--- a/res/values-sr/strings.xml
+++ b/res/values-sr/strings.xml
@@ -142,7 +142,8 @@
<string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> се инсталира, <xliff:g id="PROGRESS">%2$s</xliff:g> готово"</string>
<string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> се преузима, завршено је <xliff:g id="PROGRESS">%2$s</xliff:g>"</string>
<string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> чека на инсталирање"</string>
- <string name="app_archived_title" msgid="7717956158562544081">"Апликација <xliff:g id="NAME">%1$s</xliff:g> је архивирана. Додирните да бисте је преузели и вратили."</string>
+ <string name="app_archived_title" msgid="4548283110222420708">"Апликација <xliff:g id="NAME">%1$s</xliff:g> је архивирана."</string>
+ <string name="app_unarchiving_action" msgid="5736107006413929484">"преузмите и вратите"</string>
<string name="dialog_update_title" msgid="114234265740994042">"Треба да ажурирате апликацију"</string>
<string name="dialog_update_message" msgid="4176784553982226114">"Апликација за ову икону није ажурирана. Можете да је ручно ажурирате да бисте поново омогућили ову пречицу или уклоните икону."</string>
<string name="dialog_update" msgid="2178028071796141234">"Ажурирај"</string>
diff --git a/res/values-sv/strings.xml b/res/values-sv/strings.xml
index 547f60e..482cd88 100644
--- a/res/values-sv/strings.xml
+++ b/res/values-sv/strings.xml
@@ -142,7 +142,8 @@
<string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> installeras. <xliff:g id="PROGRESS">%2$s</xliff:g> har slutförts"</string>
<string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> laddas ned, <xliff:g id="PROGRESS">%2$s</xliff:g> klart"</string>
<string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> väntar på installation"</string>
- <string name="app_archived_title" msgid="7717956158562544081">"<xliff:g id="NAME">%1$s</xliff:g> har arkiverats. Tryck för att ladda ner och återställa."</string>
+ <string name="app_archived_title" msgid="4548283110222420708">"<xliff:g id="NAME">%1$s</xliff:g> har arkiverats."</string>
+ <string name="app_unarchiving_action" msgid="5736107006413929484">"ladda ned och återställ"</string>
<string name="dialog_update_title" msgid="114234265740994042">"Du måste uppdatera appen"</string>
<string name="dialog_update_message" msgid="4176784553982226114">"Appen för den här ikonen har inte uppdaterats. Du kan uppdatera den manuellt för att återaktivera genvägen eller ta bort ikonen."</string>
<string name="dialog_update" msgid="2178028071796141234">"Uppdatera"</string>
diff --git a/res/values-sw/strings.xml b/res/values-sw/strings.xml
index 71f8d8b..015ff8d 100644
--- a/res/values-sw/strings.xml
+++ b/res/values-sw/strings.xml
@@ -142,7 +142,8 @@
<string name="app_installing_title" msgid="5864044122733792085">"Inasakinisha <xliff:g id="NAME">%1$s</xliff:g>, imekamilika <xliff:g id="PROGRESS">%2$s</xliff:g>"</string>
<string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> inapakuliwa, <xliff:g id="PROGRESS">%2$s</xliff:g> imekamilika"</string>
<string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> inasubiri kusakinisha"</string>
- <string name="app_archived_title" msgid="7717956158562544081">"<xliff:g id="NAME">%1$s</xliff:g> imewekwa kwenye kumbukumbu. Gusa ili upakue na urejeshe."</string>
+ <string name="app_archived_title" msgid="4548283110222420708">"<xliff:g id="NAME">%1$s</xliff:g> imewekwa kwenye kumbukumbu."</string>
+ <string name="app_unarchiving_action" msgid="5736107006413929484">"pakua na urejeshe"</string>
<string name="dialog_update_title" msgid="114234265740994042">"Unahitaji kusasisha programu"</string>
<string name="dialog_update_message" msgid="4176784553982226114">"Programu ya aikoni hii haijasasishwa. Unaweza kusasisha mwenyewe ili uruhusu upya njia hii ya mkato au uondoe aikoni."</string>
<string name="dialog_update" msgid="2178028071796141234">"Sasisha"</string>
diff --git a/res/values-ta/strings.xml b/res/values-ta/strings.xml
index e87e2eb..704361a 100644
--- a/res/values-ta/strings.xml
+++ b/res/values-ta/strings.xml
@@ -142,7 +142,8 @@
<string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> நிறுவப்படுகிறது, <xliff:g id="PROGRESS">%2$s</xliff:g> முடிந்தது"</string>
<string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g>ஐப் பதிவிறக்குகிறது, <xliff:g id="PROGRESS">%2$s</xliff:g> முடிந்தது"</string>
<string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g>ஐ நிறுவுவதற்காகக் காத்திருக்கிறது"</string>
- <string name="app_archived_title" msgid="7717956158562544081">"<xliff:g id="NAME">%1$s</xliff:g> காப்பிடப்பட்டுள்ளது. அதைப் பதிவிறக்கி மீட்டெடுக்க தட்டுங்கள்."</string>
+ <string name="app_archived_title" msgid="4548283110222420708">"<xliff:g id="NAME">%1$s</xliff:g> காப்பிடப்பட்டுள்ளது."</string>
+ <string name="app_unarchiving_action" msgid="5736107006413929484">"பதிவிறக்கி மீட்டெடுக்கும்"</string>
<string name="dialog_update_title" msgid="114234265740994042">"ஆப்ஸைப் புதுப்பியுங்கள்"</string>
<string name="dialog_update_message" msgid="4176784553982226114">"இந்த ஐகானுக்கான ஆப்ஸ் புதுப்பிக்கப்படவில்லை. இந்த ஷார்ட்கட்டை மீண்டும் இயக்கவோ ஐகானை அகற்றவோ நீங்களாகவே புதுப்பிக்கலாம்."</string>
<string name="dialog_update" msgid="2178028071796141234">"புதுப்பி"</string>
diff --git a/res/values-te/strings.xml b/res/values-te/strings.xml
index 75dc7f0..4550961 100644
--- a/res/values-te/strings.xml
+++ b/res/values-te/strings.xml
@@ -142,7 +142,8 @@
<string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g>ను ఇన్స్టాల్ చేయడం, <xliff:g id="PROGRESS">%2$s</xliff:g> పూర్తయింది"</string>
<string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> డౌన్లోడ్ అవుతోంది, <xliff:g id="PROGRESS">%2$s</xliff:g> పూర్తయింది"</string>
<string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> ఇన్స్టాల్ కావడానికి వేచి ఉంది"</string>
- <string name="app_archived_title" msgid="7717956158562544081">"<xliff:g id="NAME">%1$s</xliff:g> ఆర్కైవ్ చేయబడింది. డౌన్లోడ్ చేయడానికి, రీస్టోర్ చేయడానికి ట్యాప్ చేయండి."</string>
+ <string name="app_archived_title" msgid="4548283110222420708">"<xliff:g id="NAME">%1$s</xliff:g> ఆర్కైవ్ చేయబడింది."</string>
+ <string name="app_unarchiving_action" msgid="5736107006413929484">"డౌన్లోడ్ చేసి, రీస్టోర్ చేయండి"</string>
<string name="dialog_update_title" msgid="114234265740994042">"యాప్ను అప్డేట్ చేయడం అవసరం"</string>
<string name="dialog_update_message" msgid="4176784553982226114">"ఈ చిహ్నం కోసం యాప్ అప్డేట్ చేయబడలేదు. మీరు ఈ షార్ట్కట్ను మళ్లీ ఎనేబుల్ చేయడానికి మాన్యువల్గా అప్డేట్ చేయవచ్చు లేదా చిహ్నాన్ని తీసివేయవచ్చు."</string>
<string name="dialog_update" msgid="2178028071796141234">"అప్డేట్ చేయండి"</string>
diff --git a/res/values-th/strings.xml b/res/values-th/strings.xml
index 50a0e52..27cba7f 100644
--- a/res/values-th/strings.xml
+++ b/res/values-th/strings.xml
@@ -142,7 +142,8 @@
<string name="app_installing_title" msgid="5864044122733792085">"กำลังติดตั้ง <xliff:g id="NAME">%1$s</xliff:g> เสร็จแล้ว <xliff:g id="PROGRESS">%2$s</xliff:g>"</string>
<string name="app_downloading_title" msgid="8336702962104482644">"กำลังดาวน์โหลด <xliff:g id="NAME">%1$s</xliff:g> เสร็จแล้ว <xliff:g id="PROGRESS">%2$s</xliff:g>"</string>
<string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> กำลังรอติดตั้ง"</string>
- <string name="app_archived_title" msgid="7717956158562544081">"เก็บถาวร <xliff:g id="NAME">%1$s</xliff:g> แล้ว แตะเพื่อดาวน์โหลดและกู้คืน"</string>
+ <string name="app_archived_title" msgid="4548283110222420708">"เก็บถาวร <xliff:g id="NAME">%1$s</xliff:g> แล้ว"</string>
+ <string name="app_unarchiving_action" msgid="5736107006413929484">"ดาวน์โหลดและกู้คืน"</string>
<string name="dialog_update_title" msgid="114234265740994042">"ต้องอัปเดตแอป"</string>
<string name="dialog_update_message" msgid="4176784553982226114">"แอปสำหรับไอคอนนี้ยังไม่ได้อัปเดต คุณอัปเดตด้วยตนเองได้โดยเปิดใช้ทางลัดนี้อีกครั้งหรือนำไอคอนออก"</string>
<string name="dialog_update" msgid="2178028071796141234">"อัปเดต"</string>
diff --git a/res/values-tl/strings.xml b/res/values-tl/strings.xml
index 86ad330..5dea339 100644
--- a/res/values-tl/strings.xml
+++ b/res/values-tl/strings.xml
@@ -142,7 +142,8 @@
<string name="app_installing_title" msgid="5864044122733792085">"Ini-install ang <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="PROGRESS">%2$s</xliff:g> kumpleto"</string>
<string name="app_downloading_title" msgid="8336702962104482644">"Dina-download na ang <xliff:g id="NAME">%1$s</xliff:g>, tapos na ang <xliff:g id="PROGRESS">%2$s</xliff:g>"</string>
<string name="app_waiting_download_title" msgid="7053938513995617849">"Hinihintay nang mag-install ang <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="app_archived_title" msgid="7717956158562544081">"Naka-archive ang <xliff:g id="NAME">%1$s</xliff:g>. I-tap para i-download at i-restore."</string>
+ <string name="app_archived_title" msgid="4548283110222420708">"Naka-archive ang <xliff:g id="NAME">%1$s</xliff:g>."</string>
+ <string name="app_unarchiving_action" msgid="5736107006413929484">"i-download at i-restore"</string>
<string name="dialog_update_title" msgid="114234265740994042">"Kinakailangang i-update ang app"</string>
<string name="dialog_update_message" msgid="4176784553982226114">"Hindi updated ang app para sa icon na ito. Puwede kang manual na mag-update para ma-enable ulit ang shortcut na ito, o alisin ang icon."</string>
<string name="dialog_update" msgid="2178028071796141234">"I-update"</string>
diff --git a/res/values-tr/strings.xml b/res/values-tr/strings.xml
index 9955bbf..6b2cb60 100644
--- a/res/values-tr/strings.xml
+++ b/res/values-tr/strings.xml
@@ -142,7 +142,8 @@
<string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> yükleniyor, <xliff:g id="PROGRESS">%2$s</xliff:g> tamamlandı"</string>
<string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> indiriliyor, <xliff:g id="PROGRESS">%2$s</xliff:g> tamamlandı"</string>
<string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> uygulaması yüklenmek için bekliyor"</string>
- <string name="app_archived_title" msgid="7717956158562544081">"<xliff:g id="NAME">%1$s</xliff:g> arşivlendi. İndirip geri yüklemek için dokunun."</string>
+ <string name="app_archived_title" msgid="4548283110222420708">"<xliff:g id="NAME">%1$s</xliff:g> arşivlendi."</string>
+ <string name="app_unarchiving_action" msgid="5736107006413929484">"indir ve geri yükle"</string>
<string name="dialog_update_title" msgid="114234265740994042">"Uygulama güncellemesi gerekli"</string>
<string name="dialog_update_message" msgid="4176784553982226114">"Bu simgenin uygulaması güncellenmemiş. Simgeyi kaldırabilir ya da uygulamayı manuel olarak güncelleyerek bu kısayolu yeniden etkinleştirebilirsiniz."</string>
<string name="dialog_update" msgid="2178028071796141234">"Güncelle"</string>
diff --git a/res/values-uk/strings.xml b/res/values-uk/strings.xml
index e777262..3b35bc8 100644
--- a/res/values-uk/strings.xml
+++ b/res/values-uk/strings.xml
@@ -142,7 +142,8 @@
<string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> встановлюється, виконано <xliff:g id="PROGRESS">%2$s</xliff:g>"</string>
<string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> завантажується, <xliff:g id="PROGRESS">%2$s</xliff:g>"</string>
<string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> очікує на завантаження"</string>
- <string name="app_archived_title" msgid="7717956158562544081">"Додаток <xliff:g id="NAME">%1$s</xliff:g> заархівовано. Натисніть, щоб завантажити й відновити."</string>
+ <string name="app_archived_title" msgid="4548283110222420708">"Додаток <xliff:g id="NAME">%1$s</xliff:g> заархівовано."</string>
+ <string name="app_unarchiving_action" msgid="5736107006413929484">"завантажити й відновити"</string>
<string name="dialog_update_title" msgid="114234265740994042">"Потрібно оновити додаток"</string>
<string name="dialog_update_message" msgid="4176784553982226114">"Додаток для цього значка не оновлено. Ви можете оновити його вручну, щоб знову ввімкнути цю швидку команду, або можете вилучити значок."</string>
<string name="dialog_update" msgid="2178028071796141234">"Оновити"</string>
diff --git a/res/values-ur/strings.xml b/res/values-ur/strings.xml
index 9fb85b2..8d158ce 100644
--- a/res/values-ur/strings.xml
+++ b/res/values-ur/strings.xml
@@ -142,7 +142,8 @@
<string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> انسٹال کی جا رہی ہے، <xliff:g id="PROGRESS">%2$s</xliff:g> مکمل ہو گئی"</string>
<string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> ڈاؤن لوڈ ہو رہا ہے، <xliff:g id="PROGRESS">%2$s</xliff:g> مکمل ہو گیا"</string>
<string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> انسٹال ہونے کا انتظار کر رہی ہے"</string>
- <string name="app_archived_title" msgid="7717956158562544081">"<xliff:g id="NAME">%1$s</xliff:g> کو آرکائیو کر لیا گیا ہے۔ ڈاؤن لوڈ اور بحال کرنے کیلئے تھپتھپائیں۔"</string>
+ <string name="app_archived_title" msgid="4548283110222420708">"<xliff:g id="NAME">%1$s</xliff:g> کو آرکائیو کر لیا گیا ہے۔"</string>
+ <string name="app_unarchiving_action" msgid="5736107006413929484">"ڈاؤن لوڈ کریں اور بحال کریں"</string>
<string name="dialog_update_title" msgid="114234265740994042">"ایپ کی اپ ڈیٹ درکار ہے"</string>
<string name="dialog_update_message" msgid="4176784553982226114">"اس آئیکن کیلئے ایپ کو اپ ڈیٹ نہیں کیا گیا ہے۔ آپ اس شارٹ کٹ کو دوبارہ فعال کرنے کے لیے دستی طور پر اپ ڈیٹ کر سکتے ہیں، یا آئیکن کو ہٹا سکتے ہیں۔"</string>
<string name="dialog_update" msgid="2178028071796141234">"اپ ڈیٹ کریں"</string>
diff --git a/res/values-uz/strings.xml b/res/values-uz/strings.xml
index 83cabc9..511d44b 100644
--- a/res/values-uz/strings.xml
+++ b/res/values-uz/strings.xml
@@ -142,7 +142,8 @@
<string name="app_installing_title" msgid="5864044122733792085">"<xliff:g id="NAME">%1$s</xliff:g> oʻrnatlmoqda, <xliff:g id="PROGRESS">%2$s</xliff:g> yakunlandi"</string>
<string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> yuklab olinmoqda, <xliff:g id="PROGRESS">%2$s</xliff:g> bajarildi"</string>
<string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> ilovasi o‘rnatilishi kutilmoqda"</string>
- <string name="app_archived_title" msgid="7717956158562544081">"<xliff:g id="NAME">%1$s</xliff:g> arxivlangan. Yuklab olish va tiklash uchun bosing."</string>
+ <string name="app_archived_title" msgid="4548283110222420708">"<xliff:g id="NAME">%1$s</xliff:g> arxivlangan."</string>
+ <string name="app_unarchiving_action" msgid="5736107006413929484">"yuklab olish va tiklash"</string>
<string name="dialog_update_title" msgid="114234265740994042">"Ilovani yangilash zarur"</string>
<string name="dialog_update_message" msgid="4176784553982226114">"Bu belgi uchun ilova yangilanmagan. Ushbu yorliqni qayta yoqish uchun oddiy usulda yangilashingiz yoki belgini olib tashlashingiz mumkin."</string>
<string name="dialog_update" msgid="2178028071796141234">"Yangilash"</string>
diff --git a/res/values-vi/strings.xml b/res/values-vi/strings.xml
index d67f661..d1f2adc 100644
--- a/res/values-vi/strings.xml
+++ b/res/values-vi/strings.xml
@@ -142,7 +142,8 @@
<string name="app_installing_title" msgid="5864044122733792085">"Đang cài đặt <xliff:g id="NAME">%1$s</xliff:g>, hoàn tất <xliff:g id="PROGRESS">%2$s</xliff:g>"</string>
<string name="app_downloading_title" msgid="8336702962104482644">"Đang tải xuống <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="PROGRESS">%2$s</xliff:g> hoàn tất"</string>
<string name="app_waiting_download_title" msgid="7053938513995617849">"Đang chờ cài đặt <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="app_archived_title" msgid="7717956158562544081">"<xliff:g id="NAME">%1$s</xliff:g> đã được lưu trữ. Hãy nhấn để tải xuống và khôi phục."</string>
+ <string name="app_archived_title" msgid="4548283110222420708">"<xliff:g id="NAME">%1$s</xliff:g> đã được lưu trữ."</string>
+ <string name="app_unarchiving_action" msgid="5736107006413929484">"tải xuống và khôi phục"</string>
<string name="dialog_update_title" msgid="114234265740994042">"Cần cập nhật ứng dụng"</string>
<string name="dialog_update_message" msgid="4176784553982226114">"Ứng dụng cho biểu tượng này chưa được cập nhật. Bạn có thể cập nhật theo cách thủ công để bật lại phím tắt này hoặc xóa biểu tượng."</string>
<string name="dialog_update" msgid="2178028071796141234">"Cập nhật"</string>
diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml
index 1097e57..ed30c33 100644
--- a/res/values-zh-rCN/strings.xml
+++ b/res/values-zh-rCN/strings.xml
@@ -142,7 +142,8 @@
<string name="app_installing_title" msgid="5864044122733792085">"正在安装<xliff:g id="NAME">%1$s</xliff:g>,已完成 <xliff:g id="PROGRESS">%2$s</xliff:g>"</string>
<string name="app_downloading_title" msgid="8336702962104482644">"正在下载<xliff:g id="NAME">%1$s</xliff:g>,已完成 <xliff:g id="PROGRESS">%2$s</xliff:g>"</string>
<string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g>正在等待安装"</string>
- <string name="app_archived_title" msgid="7717956158562544081">"已归档“<xliff:g id="NAME">%1$s</xliff:g>”。点按即可进行下载并恢复。"</string>
+ <string name="app_archived_title" msgid="4548283110222420708">"已归档“<xliff:g id="NAME">%1$s</xliff:g>”。"</string>
+ <string name="app_unarchiving_action" msgid="5736107006413929484">"下载并恢复"</string>
<string name="dialog_update_title" msgid="114234265740994042">"需要更新应用"</string>
<string name="dialog_update_message" msgid="4176784553982226114">"此图标对应的应用未更新。您可以手动更新以重新启用该快捷方式,或者移除此图标。"</string>
<string name="dialog_update" msgid="2178028071796141234">"更新"</string>
diff --git a/res/values-zh-rHK/strings.xml b/res/values-zh-rHK/strings.xml
index 6471a9a..1ef3070 100644
--- a/res/values-zh-rHK/strings.xml
+++ b/res/values-zh-rHK/strings.xml
@@ -142,7 +142,8 @@
<string name="app_installing_title" msgid="5864044122733792085">"正在安裝「<xliff:g id="NAME">%1$s</xliff:g>」(已完成 <xliff:g id="PROGRESS">%2$s</xliff:g>)"</string>
<string name="app_downloading_title" msgid="8336702962104482644">"正在下載 <xliff:g id="NAME">%1$s</xliff:g>,已完成 <xliff:g id="PROGRESS">%2$s</xliff:g>"</string>
<string name="app_waiting_download_title" msgid="7053938513995617849">"正在等待安裝 <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="app_archived_title" msgid="7717956158562544081">"「<xliff:g id="NAME">%1$s</xliff:g>」已封存。輕按即可下載並還原。"</string>
+ <string name="app_archived_title" msgid="4548283110222420708">"「<xliff:g id="NAME">%1$s</xliff:g>」已封存。"</string>
+ <string name="app_unarchiving_action" msgid="5736107006413929484">"下載及還原"</string>
<string name="dialog_update_title" msgid="114234265740994042">"必須更新應用程式"</string>
<string name="dialog_update_message" msgid="4176784553982226114">"你尚未更新這個圖示代表的應用程式。你可以手動更新以重新啟用此快速鍵,或者移除圖示。"</string>
<string name="dialog_update" msgid="2178028071796141234">"更新"</string>
diff --git a/res/values-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml
index 0a9ffa2..fa1b347 100644
--- a/res/values-zh-rTW/strings.xml
+++ b/res/values-zh-rTW/strings.xml
@@ -142,7 +142,8 @@
<string name="app_installing_title" msgid="5864044122733792085">"正在安裝「<xliff:g id="NAME">%1$s</xliff:g>」(已完成 <xliff:g id="PROGRESS">%2$s</xliff:g>)"</string>
<string name="app_downloading_title" msgid="8336702962104482644">"正在下載「<xliff:g id="NAME">%1$s</xliff:g>」,已完成 <xliff:g id="PROGRESS">%2$s</xliff:g>"</string>
<string name="app_waiting_download_title" msgid="7053938513995617849">"正在等待安裝「<xliff:g id="NAME">%1$s</xliff:g>」"</string>
- <string name="app_archived_title" msgid="7717956158562544081">"「<xliff:g id="NAME">%1$s</xliff:g>」已封存。輕觸即可下載並還原。"</string>
+ <string name="app_archived_title" msgid="4548283110222420708">"「<xliff:g id="NAME">%1$s</xliff:g>」已封存。"</string>
+ <string name="app_unarchiving_action" msgid="5736107006413929484">"下載及還原"</string>
<string name="dialog_update_title" msgid="114234265740994042">"必須更新應用程式"</string>
<string name="dialog_update_message" msgid="4176784553982226114">"這個圖示代表的應用程式未更新。手動更新即可重新啟用這個捷徑,你也可以移除圖示。"</string>
<string name="dialog_update" msgid="2178028071796141234">"更新"</string>
diff --git a/res/values-zu/strings.xml b/res/values-zu/strings.xml
index 59c99c4..c6753fc 100644
--- a/res/values-zu/strings.xml
+++ b/res/values-zu/strings.xml
@@ -142,7 +142,8 @@
<string name="app_installing_title" msgid="5864044122733792085">"I-<xliff:g id="NAME">%1$s</xliff:g> iyafakwa, seyiqede <xliff:g id="PROGRESS">%2$s</xliff:g>"</string>
<string name="app_downloading_title" msgid="8336702962104482644">"I-<xliff:g id="NAME">%1$s</xliff:g> iyalandwa, <xliff:g id="PROGRESS">%2$s</xliff:g> kuqediwe"</string>
<string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> ilinde ukufakwa"</string>
- <string name="app_archived_title" msgid="7717956158562544081">"Okuthi <xliff:g id="NAME">%1$s</xliff:g> kufakwe kungobo yomlando. Thepha ukuze udawunilode futhi ubuyisele."</string>
+ <string name="app_archived_title" msgid="4548283110222420708">"Okuthi <xliff:g id="NAME">%1$s</xliff:g> kufakwe kungobo yomlando."</string>
+ <string name="app_unarchiving_action" msgid="5736107006413929484">"dawuniloda uphinde ubuyisele"</string>
<string name="dialog_update_title" msgid="114234265740994042">"Kudingeka isibuyekezo se-app"</string>
<string name="dialog_update_message" msgid="4176784553982226114">"I-app yalesi sithonjana ibuyekeziwe. Ungabuyekeza mathupha ukuze uphinde unike amandla lesi sinqamuleli, noma ususe isithonjana."</string>
<string name="dialog_update" msgid="2178028071796141234">"Vuselela"</string>
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index 4dddb9a..f24707f 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -126,6 +126,8 @@
<attr name="widgetPickerCollapseHandleColor" format="color"/>
<attr name="widgetPickerAddButtonBackgroundColor" format="color"/>
<attr name="widgetPickerAddButtonTextColor" format="color"/>
+ <attr name="widgetPickerExpandButtonBackgroundColor" format="color"/>
+ <attr name="widgetPickerExpandButtonTextColor" format="color"/>
<attr name="widgetCellTitleColor" format="color" />
<attr name="widgetCellSubtitleColor" format="color" />
@@ -309,6 +311,9 @@
<!-- defaults to allAppsCellSpecsId, if not specified -->
<attr name="allAppsCellSpecsTwoPanelId" format="reference" />
+ <!-- defaults to false, if not specified -->
+ <attr name="isFixedLandscape" format="boolean" />
+
<!-- By default all categories are enabled -->
<attr name="deviceCategory" format="integer">
<!-- Enable on phone only -->
diff --git a/res/values/colors.xml b/res/values/colors.xml
index fa1626e..4549b86 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -117,6 +117,12 @@
<color name="widget_picker_collapse_handle_color_light">#C4C7C5</color>
<color name="widget_picker_add_button_background_color_light">#0B57D0</color>
<color name="widget_picker_add_button_text_color_light">#0B57D0</color>
+ <color name="widget_picker_expand_button_background_color_light">
+ @color/widget_picker_secondary_surface_color_light
+ </color>
+ <color name="widget_picker_expand_button_text_color_light">
+ @color/widget_picker_header_app_title_color_light
+ </color>
<color name="widget_cell_title_color_light">@color/system_on_surface_light</color>
<color name="widget_cell_subtitle_color_light">@color/system_on_surface_variant_light</color>
@@ -138,6 +144,12 @@
<color name="widget_picker_collapse_handle_color_dark">#444746</color>
<color name="widget_picker_add_button_background_color_dark">#062E6F</color>
<color name="widget_picker_add_button_text_color_dark">#FFFFFF</color>
+ <color name="widget_picker_expand_button_background_color_dark">
+ @color/widget_picker_secondary_surface_color_dark
+ </color>
+ <color name="widget_picker_expand_button_text_color_dark">
+ @color/widget_picker_header_app_title_color_dark
+ </color>
<color name="widget_cell_title_color_dark">@color/system_on_surface_dark</color>
<color name="widget_cell_subtitle_color_dark">@color/system_on_surface_variant_dark</color>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index d4773c3..d1dde3f 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -230,6 +230,12 @@
<dimen name="widget_list_top_bottom_corner_radius">28dp</dimen>
<dimen name="widget_list_content_corner_radius">4dp</dimen>
+ <!-- Button that expands the widget apps list in the widget picker. -->
+ <dimen name="widgets_list_expand_button_drawable_padding">8dp</dimen>
+ <dimen name="widgets_list_expand_button_start_padding">16dp</dimen>
+ <dimen name="widgets_list_expand_button_end_padding">24dp</dimen>
+ <dimen name="widgets_list_expand_button_vertical_padding">16dp</dimen>
+ <dimen name="widgets_list_expand_button_top_margin">14dp</dimen>
<dimen name="widget_list_header_view_vertical_padding">20dp</dimen>
<dimen name="widget_list_entry_spacing">2dp</dimen>
diff --git a/res/values/id.xml b/res/values/id.xml
index 28496b5..67692d8 100644
--- a/res/values/id.xml
+++ b/res/values/id.xml
@@ -19,6 +19,7 @@
<item type="id" name="view_type_widgets_space" />
<item type="id" name="view_type_widgets_list" />
<item type="id" name="view_type_widgets_header" />
+ <item type="id" name="view_type_widgets_list_expand" />
<!-- Accessibility actions -->
<item type="id" name="action_remove" />
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 746bd7e..123e2b8 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -157,6 +157,12 @@
<!-- Accessibility content description for the button that adds a widget to the home screen. The
placeholder text is the widget name. [CHAR_LIMIT=none] -->
<string name="widget_add_button_content_description">Add <xliff:g id="widget_name" example="Calendar month view">%1$s</xliff:g> widget</string>
+ <!-- Text on the button that enables users to expand the widgets list to see all widget apps besides the default ones displayed. [CHAR_LIMIT=15] -->
+ <string name="widgets_list_expand_button_label">Show all</string>
+ <!-- Accessibility content description for the button that enables users to expand the widgets list to see all widget apps besides the default ones displayed. [CHAR_LIMIT=none] -->
+ <string name="widgets_list_expand_button_content_description">Show all widgets</string>
+ <!-- Accessibility announcement to indicate to the users that widgets list is now expanded -->
+ <string name="widgets_list_expanded">Showing all widgets</string>
<!-- Text on an educational tip on widget informing users that they can change widget settings.
[CHAR_LIMIT=NONE] -->
@@ -311,6 +317,12 @@
<string name="allow_rotation_title">Allow home screen rotation</string>
<!-- Text explaining when the home screen will get rotated. [CHAR LIMIT=100] -->
<string name="allow_rotation_desc">When phone is rotated</string>
+
+ <!-- Title for Landscape Mode setting. [CHAR LIMIT=50] -->
+ <string name="landscape_mode_title">Landscape mode</string>
+ <!-- [CHAR LIMIT=100] -->
+ <string name="landscape_mode_desc">Set phone into landscape mode</string>
+
<!-- Title for Notification dots setting. Tapping this will link to the system Notifications settings screen where the user can turn off notification dots globally. [CHAR LIMIT=50] -->
<string name="notification_dots_title">Notification dots</string>
<!-- Text to indicate that the system notification dots setting is on [CHAR LIMIT=100] -->
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 728c523..6d3579b 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -275,6 +275,12 @@
@color/widget_picker_add_button_background_color_light</item>
<item name="widgetPickerAddButtonTextColor">
@color/widget_picker_add_button_text_color_light</item>
+ <item name="widgetPickerExpandButtonBackgroundColor">
+ @color/widget_picker_expand_button_background_color_light
+ </item>
+ <item name="widgetPickerExpandButtonTextColor">
+ @color/widget_picker_expand_button_text_color_light
+ </item>
<item name="widgetCellTitleColor">
@color/widget_cell_title_color_light</item>
<item name="widgetCellSubtitleColor">
@@ -316,6 +322,12 @@
@color/widget_picker_add_button_background_color_dark</item>
<item name="widgetPickerAddButtonTextColor">
@color/widget_picker_add_button_text_color_dark</item>
+ <item name="widgetPickerExpandButtonBackgroundColor">
+ @color/widget_picker_expand_button_background_color_dark
+ </item>
+ <item name="widgetPickerExpandButtonTextColor">
+ @color/widget_picker_expand_button_text_color_dark
+ </item>
<item name="widgetCellTitleColor">
@color/widget_cell_title_color_dark</item>
<item name="widgetCellSubtitleColor">
diff --git a/res/xml/backupscheme.xml b/res/xml/backupscheme.xml
index 58916a8..34b80b1 100644
--- a/res/xml/backupscheme.xml
+++ b/res/xml/backupscheme.xml
@@ -10,6 +10,7 @@
<include domain="database" path="launcher_4_by_4.db" />
<include domain="database" path="launcher_3_by_3.db" />
<include domain="database" path="launcher_2_by_2.db" />
+ <include domain="database" path="launcher_7_by_3.db" />
<include domain="sharedpref" path="com.android.launcher3.prefs.xml" />
<include domain="file" path="downgrade_schema.json" />
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index c25e8fb..78535a1 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -617,7 +617,7 @@
|| inv.inlineQsb[INDEX_TWO_PANEL_LANDSCAPE]
: inv.inlineQsb[INDEX_DEFAULT] || inv.inlineQsb[INDEX_LANDSCAPE])
&& hotseatQsbHeight > 0;
- isQsbInline = mIsScalableGrid && inv.inlineQsb[mTypeIndex] && canQsbInline;
+ isQsbInline = isQsbInline(inv);
areNavButtonsInline = isTaskbarPresent && !isGestureMode;
numShownHotseatIcons =
@@ -850,6 +850,24 @@
mDotRendererAllApps = createDotRenderer(context, allAppsIconSizePx, dotRendererCache);
}
+ /**
+ * Takes care of the logic that determines if we show a the QSB inline or not.
+ */
+ private boolean isQsbInline(InvariantDeviceProfile inv) {
+ // For foldable (two panel), we inline the qsb if we have the screen open and we are in
+ // either Landscape or Portrait. This cal also be disabled in the device_profile.xml
+ boolean twoPanelCanInline = inv.inlineQsb[INDEX_TWO_PANEL_PORTRAIT]
+ || inv.inlineQsb[INDEX_TWO_PANEL_LANDSCAPE];
+
+ // In tablets we inline in both orientations but only if we have enough space in the QSB
+ boolean tabletInlineQsb = inv.inlineQsb[INDEX_DEFAULT] || inv.inlineQsb[INDEX_LANDSCAPE];
+ boolean canQsbInline = isTwoPanels ? twoPanelCanInline : tabletInlineQsb;
+ canQsbInline = canQsbInline && hotseatQsbHeight > 0;
+
+ return (mIsScalableGrid && inv.inlineQsb[mTypeIndex] && canQsbInline)
+ || inv.isFixedLandscapeMode;
+ }
+
private static DotRenderer createDotRenderer(
@NonNull Context context, int size, @NonNull SparseArray<DotRenderer> cache) {
DotRenderer renderer = cache.get(size);
@@ -1903,7 +1921,7 @@
hotseatBarPadding.set(mHotseatBarWorkspaceSpacePx, paddingTop,
mInsets.right + mHotseatBarEdgePaddingPx, paddingBottom);
}
- } else if (isTaskbarPresent) {
+ } else if (isTaskbarPresent || inv.isFixedLandscapeMode) {
// Center the QSB vertically with hotseat
int hotseatBarBottomPadding = getHotseatBarBottomPadding();
int hotseatBarTopPadding =
@@ -1922,6 +1940,13 @@
}
startSpacing += getAdditionalQsbSpace();
+ if (inv.isFixedLandscapeMode) {
+ endSpacing += workspacePadding.right + cellLayoutPaddingPx.right
+ + mInsets.right;
+ startSpacing += workspacePadding.left + cellLayoutPaddingPx.left
+ + mInsets.left;
+ }
+
hotseatBarPadding.top = hotseatBarTopPadding;
hotseatBarPadding.bottom = hotseatBarBottomPadding;
boolean isRtl = Utilities.isRtl(context.getResources());
@@ -2517,7 +2542,8 @@
throw new IllegalArgumentException("Window bounds not set");
}
if (mTransposeLayoutWithOrientation == null) {
- mTransposeLayoutWithOrientation = !mInfo.isTablet(mWindowBounds);
+ mTransposeLayoutWithOrientation =
+ !(mInfo.isTablet(mWindowBounds) || mInv.isFixedLandscapeMode);
}
if (mIsGestureMode == null) {
mIsGestureMode = mInfo.getNavigationMode().hasGestures;
diff --git a/src/com/android/launcher3/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java
index 7112a1b..090fe51 100644
--- a/src/com/android/launcher3/InvariantDeviceProfile.java
+++ b/src/com/android/launcher3/InvariantDeviceProfile.java
@@ -16,6 +16,7 @@
package com.android.launcher3;
+import static com.android.launcher3.LauncherPrefs.FIXED_LANDSCAPE_MODE;
import static com.android.launcher3.LauncherPrefs.GRID_NAME;
import static com.android.launcher3.Utilities.dpiFromPx;
import static com.android.launcher3.testing.shared.ResourceUtils.INVALID_RESOURCE_HANDLE;
@@ -86,7 +87,8 @@
@Retention(RetentionPolicy.SOURCE)
@IntDef({TYPE_PHONE, TYPE_MULTI_DISPLAY, TYPE_TABLET})
- public @interface DeviceType {}
+ public @interface DeviceType {
+ }
public static final int TYPE_PHONE = 0;
public static final int TYPE_MULTI_DISPLAY = 1;
@@ -132,6 +134,7 @@
public int iconBitmapSize;
public int fillResIconDpi;
public @DeviceType int deviceType;
+ public Info displayInfo;
public PointF[] minCellSize;
@@ -209,6 +212,13 @@
@XmlRes
public int allAppsCellSpecsTwoPanelId = INVALID_RESOURCE_HANDLE;
+
+ /**
+ * Fixed landscape mode is the landscape on the phones.
+ */
+ public boolean isFixedLandscapeMode = false;
+ private LauncherPrefChangeListener mLandscapeModePreferenceListener;
+
public String dbFile;
public int defaultLayoutId;
public int demoModeLayoutId;
@@ -224,7 +234,8 @@
private final ArrayList<OnIDPChangeListener> mChangeListeners = new ArrayList<>();
@VisibleForTesting
- public InvariantDeviceProfile() { }
+ public InvariantDeviceProfile() {
+ }
@TargetApi(23)
private InvariantDeviceProfile(Context context) {
@@ -242,6 +253,18 @@
onConfigChanged(displayContext);
}
});
+ if (Flags.oneGridSpecs()) {
+ mLandscapeModePreferenceListener = (String s) -> {
+ boolean newFixedLandscapeValue = FIXED_LANDSCAPE_MODE.get(context);
+ if (isFixedLandscapeMode != newFixedLandscapeValue) {
+ setFixedLandscape(context, newFixedLandscapeValue);
+ }
+ };
+ LauncherPrefs.INSTANCE.get(context).addListener(
+ mLandscapeModePreferenceListener,
+ FIXED_LANDSCAPE_MODE
+ );
+ }
}
/**
@@ -267,8 +290,13 @@
@DeviceType int defaultDeviceType = defaultInfo.getDeviceType();
DisplayOption defaultDisplayOption = invDistWeightedInterpolate(
defaultInfo,
- getPredefinedDeviceProfiles(context, gridName, defaultDeviceType,
- /*allowDisabledGrid=*/false),
+ getPredefinedDeviceProfiles(
+ context,
+ gridName,
+ defaultInfo,
+ /*allowDisabledGrid=*/false,
+ isFixedLandscapeMode
+ ),
defaultDeviceType);
Context displayContext = context.createDisplayContext(display);
@@ -276,8 +304,13 @@
@DeviceType int deviceType = myInfo.getDeviceType();
DisplayOption myDisplayOption = invDistWeightedInterpolate(
myInfo,
- getPredefinedDeviceProfiles(context, gridName, deviceType,
- /*allowDisabledGrid=*/false),
+ getPredefinedDeviceProfiles(
+ context,
+ gridName,
+ myInfo,
+ /*allowDisabledGrid=*/false,
+ isFixedLandscapeMode
+ ),
deviceType);
DisplayOption result = new DisplayOption(defaultDisplayOption.grid)
@@ -294,12 +327,17 @@
System.arraycopy(defaultDisplayOption.borderSpaces, 0, result.borderSpaces, 0,
COUNT_SIZES);
- initGrid(context, myInfo, result, deviceType);
+ initGrid(context, myInfo, result);
}
@Override
public void close() {
DisplayController.INSTANCE.executeIfCreated(dc -> dc.setPriorityListener(null));
+ if (mLandscapeModePreferenceListener != null) {
+ LauncherPrefs.INSTANCE.executeIfCreated(
+ lp -> lp.removeListener(mLandscapeModePreferenceListener, FIXED_LANDSCAPE_MODE)
+ );
+ }
}
public static String getCurrentGridName(Context context) {
@@ -308,11 +346,14 @@
private String initGrid(Context context, String gridName) {
Info displayInfo = DisplayController.INSTANCE.get(context).getInfo();
- @DeviceType int deviceType = displayInfo.getDeviceType();
- List<DisplayOption> allOptions =
- getPredefinedDeviceProfiles(context, gridName, deviceType,
- RestoreDbTask.isPending(context));
+ List<DisplayOption> allOptions = getPredefinedDeviceProfiles(
+ context,
+ gridName,
+ displayInfo,
+ RestoreDbTask.isPending(context),
+ FIXED_LANDSCAPE_MODE.get(context)
+ );
// Filter out options that don't have the same number of columns as the grid
DeviceGridState deviceGridState = new DeviceGridState(context);
@@ -321,9 +362,10 @@
DisplayOption displayOption =
invDistWeightedInterpolate(displayInfo, allOptionsFilteredByColCount.isEmpty()
- ? new ArrayList<>(allOptions)
- : new ArrayList<>(allOptionsFilteredByColCount), deviceType);
- initGrid(context, displayInfo, displayOption, deviceType);
+ ? new ArrayList<>(allOptions)
+ : new ArrayList<>(allOptionsFilteredByColCount),
+ displayInfo.getDeviceType());
+ initGrid(context, displayInfo, displayOption);
return displayOption.grid.name;
}
@@ -347,8 +389,7 @@
return new InvariantDeviceProfile().initGrid(context, null);
}
- private void initGrid(Context context, Info displayInfo, DisplayOption displayOption,
- @DeviceType int deviceType) {
+ private void initGrid(Context context, Info displayInfo, DisplayOption displayOption) {
DisplayMetrics metrics = context.getResources().getDisplayMetrics();
GridOption closestProfile = displayOption.grid;
numRows = closestProfile.numRows;
@@ -381,7 +422,8 @@
allAppsCellSpecsTwoPanelId = closestProfile.mAllAppsCellSpecsTwoPanelId;
numAllAppsRowsForCellHeightCalculation =
closestProfile.mNumAllAppsRowsForCellHeightCalculation;
- this.deviceType = deviceType;
+ this.deviceType = displayInfo.getDeviceType();
+ this.displayInfo = displayInfo;
inlineNavButtonsEndSpacing = closestProfile.inlineNavButtonsEndSpacing;
@@ -425,6 +467,9 @@
startAlignTaskbar = displayOption.startAlignTaskbar;
+ // Fixed Landscape mode
+ isFixedLandscapeMode = FIXED_LANDSCAPE_MODE.get(context) && Flags.oneGridSpecs();
+
// If the partner customization apk contains any grid overrides, apply them
// Supported overrides: numRows, numColumns, iconSize
applyPartnerDeviceProfileOverrides(context, metrics);
@@ -480,8 +525,12 @@
mChangeListeners.remove(listener);
}
- public void setCurrentGrid(Context context, String gridName) {
- LauncherPrefs.get(context).put(GRID_NAME, gridName);
+ /**
+ * Updates the current grid, this triggers a new IDP, reloads the database and triggers a grid
+ * migration.
+ */
+ public void setCurrentGrid(Context context, String newGridName) {
+ LauncherPrefs.get(context).put(GRID_NAME, newGridName);
MAIN_EXECUTOR.execute(() -> {
Trace.beginSection("InvariantDeviceProfile#setCurrentGrid");
onConfigChanged(context.getApplicationContext());
@@ -489,6 +538,24 @@
});
}
+ /**
+ * Updates the mounted mode, this triggers a new IDP, reloads the database and triggers a grid
+ * migration.
+ */
+ public void setFixedLandscape(Context context, boolean isFixedLandscape) {
+ this.isFixedLandscapeMode = isFixedLandscape;
+ if (isFixedLandscape) {
+ // When in isFixedLandscape there should only be one default grid to choose from
+ MAIN_EXECUTOR.execute(() -> {
+ Trace.beginSection("InvariantDeviceProfile#setFixedLandscape");
+ onConfigChanged(context.getApplicationContext());
+ Trace.endSection();
+ });
+ } else {
+ setCurrentGrid(context, LauncherPrefs.get(context).get(GRID_NAME));
+ }
+ }
+
private Object[] toModelState() {
return new Object[]{
numColumns, numRows, numSearchContainerColumns, numDatabaseHotseatIcons,
@@ -510,8 +577,20 @@
}
}
- private static List<DisplayOption> getPredefinedDeviceProfiles(Context context,
- String gridName, @DeviceType int deviceType, boolean allowDisabledGrid) {
+ private static boolean firstGridFilter(GridOption gridOption, int deviceType,
+ boolean allowDisabledGrid, boolean isFixedLandscapeMode) {
+ return (gridOption.isEnabled(deviceType) || allowDisabledGrid)
+ && ((gridOption.mIsFixedLandscape == isFixedLandscapeMode)
+ && gridOption.filterByFlag(deviceType));
+ }
+
+ private static List<DisplayOption> getPredefinedDeviceProfiles(
+ Context context,
+ String gridName,
+ Info displayInfo,
+ boolean allowDisabledGrid,
+ boolean isFixedLandscapeMode
+ ) {
ArrayList<DisplayOption> profiles = new ArrayList<>();
try (XmlResourceParser parser = context.getResources().getXml(R.xml.device_profiles)) {
@@ -521,10 +600,10 @@
parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {
if ((type == XmlPullParser.START_TAG)
&& GridOption.TAG_NAME.equals(parser.getName())) {
-
- GridOption gridOption = new GridOption(context, Xml.asAttributeSet(parser));
- if ((gridOption.isEnabled(deviceType) || allowDisabledGrid)
- && gridOption.filterByFlag(deviceType)) {
+ GridOption gridOption = new GridOption(
+ context, Xml.asAttributeSet(parser), displayInfo);
+ if (firstGridFilter(gridOption, displayInfo.getDeviceType(), allowDisabledGrid,
+ isFixedLandscapeMode)) {
final int displayDepth = parser.getDepth();
while (((type = parser.next()) != XmlPullParser.END_TAG
|| parser.getDepth() > displayDepth)
@@ -541,12 +620,11 @@
} catch (IOException | XmlPullParserException e) {
throw new RuntimeException(e);
}
-
ArrayList<DisplayOption> filteredProfiles = new ArrayList<>();
if (!TextUtils.isEmpty(gridName)) {
for (DisplayOption option : profiles) {
- if (gridName.equals(option.grid.name)
- && (option.grid.isEnabled(deviceType) || allowDisabledGrid)) {
+ if (gridName.equals(option.grid.name) && (option.grid.isEnabled(
+ displayInfo.getDeviceType()) || allowDisabledGrid)) {
filteredProfiles.add(option);
}
}
@@ -572,10 +650,10 @@
* Parses through the xml to find NumRows specs. Then calls findBestRowCount to get the correct
* row count for this GridOption.
*
- * @return the result of {@link #findBestRowCount(List, Context, int)}.
+ * @return the result of {@link #findBestRowCount(List, Info)}.
*/
public static NumRows getRowCount(ResourceHelper resourceHelper, Context context,
- int deviceType) {
+ Info displayInfo) {
ArrayList<NumRows> rowCounts = new ArrayList<>();
try (XmlResourceParser parser = resourceHelper.getXml()) {
@@ -592,21 +670,19 @@
throw new RuntimeException(e);
}
- return findBestRowCount(rowCounts, context, deviceType);
+ return findBestRowCount(rowCounts, displayInfo);
}
/**
* @return the biggest row count that fits the display dimensions spec using NumRows to
* determine that. If no best row count is found, return -1.
*/
- public static NumRows findBestRowCount(List<NumRows> list, Context context,
- @DeviceType int deviceType) {
- Info displayInfo = DisplayController.INSTANCE.get(context).getInfo();
+ public static NumRows findBestRowCount(List<NumRows> list, Info displayInfo) {
int minWidthPx = Integer.MAX_VALUE;
int minHeightPx = Integer.MAX_VALUE;
for (WindowBounds bounds : displayInfo.supportedBounds) {
boolean isTablet = displayInfo.isTablet(bounds);
- if (isTablet && deviceType == TYPE_MULTI_DISPLAY) {
+ if (isTablet && displayInfo.getDeviceType() == TYPE_MULTI_DISPLAY) {
// For split displays, take half width per page
minWidthPx = Math.min(minWidthPx, bounds.availableSize.x / 2);
minHeightPx = Math.min(minHeightPx, bounds.availableSize.y);
@@ -651,7 +727,6 @@
* supported. Ej. 4x4 -> normal, 5x4 -> practical, etc.
* (Note: the name of the grid can be different for the same grid size depending of
* the values of the InvariantDeviceProfile)
- *
*/
public String getGridNameFromSize(Context context, Point size) {
return parseAllGridOptions(context).stream()
@@ -677,9 +752,11 @@
* @return all the grid options that can be shown on the device
*/
public List<GridOption> parseAllGridOptions(Context context) {
- return parseAllDefinedGridOptions(context)
+ return parseAllDefinedGridOptions(context, displayInfo)
.stream()
.filter(go -> go.isEnabled(deviceType))
+ // if in fixedLandscape, then only show fixed landscape grids
+ .filter(go -> go.mIsFixedLandscape == isFixedLandscapeMode)
.filter(go -> go.filterByFlag(deviceType))
.collect(Collectors.toList());
}
@@ -687,9 +764,8 @@
/**
* @return all the grid options that can be shown on the device
*/
- public static List<GridOption> parseAllDefinedGridOptions(Context context) {
+ public static List<GridOption> parseAllDefinedGridOptions(Context context, Info displayInfo) {
List<GridOption> result = new ArrayList<>();
-
try (XmlResourceParser parser = context.getResources().getXml(R.xml.device_profiles)) {
final int depth = parser.getDepth();
int type;
@@ -697,7 +773,7 @@
|| parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {
if ((type == XmlPullParser.START_TAG)
&& GridOption.TAG_NAME.equals(parser.getName())) {
- result.add(new GridOption(context, Xml.asAttributeSet(parser)));
+ result.add(new GridOption(context, Xml.asAttributeSet(parser), displayInfo));
}
}
} catch (IOException | XmlPullParserException e) {
@@ -952,8 +1028,9 @@
private final int mAllAppsCellSpecsId;
private final int mAllAppsCellSpecsTwoPanelId;
private final int mRowCountSpecsId;
+ private final boolean mIsFixedLandscape;
- public GridOption(Context context, AttributeSet attrs) {
+ public GridOption(Context context, AttributeSet attrs, Info displayInfo) {
TypedArray a = context.obtainStyledAttributes(
attrs, R.styleable.GridDisplayOption);
name = a.getString(R.styleable.GridDisplayOption_name);
@@ -965,7 +1042,7 @@
mIsDualGrid = a.getBoolean(R.styleable.GridDisplayOption_isDualGrid, false);
if (mRowCountSpecsId != INVALID_RESOURCE_HANDLE) {
ResourceHelper resourceHelper = new ResourceHelper(context, mRowCountSpecsId);
- NumRows numR = getRowCount(resourceHelper, context, deviceCategory);
+ NumRows numR = getRowCount(resourceHelper, context, displayInfo);
numRows = numR.mNumRowsNew;
dbFile = numR.mDbFile;
} else {
@@ -1093,6 +1170,8 @@
mNumAllAppsRowsForCellHeightCalculation = numRows;
}
+ mIsFixedLandscape = a.getBoolean(R.styleable.GridDisplayOption_isFixedLandscape, false);
+
int inlineForRotation = a.getInt(R.styleable.GridDisplayOption_inlineQsb,
DONT_INLINE_QSB);
inlineQsb[INDEX_DEFAULT] =
@@ -1124,7 +1203,8 @@
}
public boolean isNewGridOption() {
- return mRowCountSpecsId != INVALID_RESOURCE_HANDLE;
+ Log.d("HHHH", "GRID = " + mIsFixedLandscape);
+ return mRowCountSpecsId != INVALID_RESOURCE_HANDLE || mIsFixedLandscape;
}
public boolean filterByFlag(int deviceType) {
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 6446f7b..be2ba57 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -96,6 +96,7 @@
import static com.android.launcher3.popup.SystemShortcut.INSTALL;
import static com.android.launcher3.popup.SystemShortcut.WIDGETS;
import static com.android.launcher3.states.RotationHelper.REQUEST_LOCK;
+import static com.android.launcher3.states.RotationHelper.REQUEST_FIXED_LANDSCAPE;
import static com.android.launcher3.states.RotationHelper.REQUEST_NONE;
import static com.android.launcher3.testing.shared.TestProtocol.LAUNCHER_ACTIVITY_STOPPED_MESSAGE;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
@@ -279,6 +280,7 @@
import java.util.HashSet;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.Optional;
import java.util.function.Predicate;
import java.util.function.Supplier;
@@ -761,7 +763,6 @@
if (!initDeviceProfile(mDeviceProfile.inv) && !mForceConfigUpdate) {
return;
}
-
dispatchDeviceProfileChanged();
reapplyUi();
mDragLayer.recreateControllers();
@@ -777,6 +778,18 @@
}
}
+ private void updateFixedLandscape() {
+ if (!com.android.launcher3.Flags.oneGridSpecs()) {
+ return;
+ }
+ if (Objects.requireNonNull(mDeviceProfile.inv).isFixedLandscapeMode) {
+ // Set rotation fixed
+ getRotationHelper().setStateHandlerRequest(REQUEST_FIXED_LANDSCAPE);
+ } else {
+ getRotationHelper().setStateHandlerRequest(REQUEST_NONE);
+ }
+ }
+
public void onAssistantVisibilityChanged(float visibility) {
mHotseat.getQsb().setAlpha(1f - visibility);
}
@@ -805,6 +818,7 @@
mDeviceProfile.numShownHotseatIcons);
}
mModelWriter = mModel.getWriter(true, mCellPosMapper, this);
+ updateFixedLandscape();
return true;
}
diff --git a/src/com/android/launcher3/LauncherFiles.java b/src/com/android/launcher3/LauncherFiles.java
index 95c0ee8..df75470 100644
--- a/src/com/android/launcher3/LauncherFiles.java
+++ b/src/com/android/launcher3/LauncherFiles.java
@@ -24,6 +24,7 @@
public static final String LAUNCHER_4_BY_4_DB = "launcher_4_by_4.db";
public static final String LAUNCHER_3_BY_3_DB = "launcher_3_by_3.db";
public static final String LAUNCHER_2_BY_2_DB = "launcher_2_by_2.db";
+ public static final String LAUNCHER_7_BY_3_DB = "launcher_7_by_3.db";
public static final String BACKUP_DB = "backup.db";
public static final String SHARED_PREFERENCES_KEY = "com.android.launcher3.prefs";
public static final String MANAGED_USER_PREFERENCES_KEY =
@@ -43,7 +44,8 @@
LAUNCHER_5_BY_6_DB,
LAUNCHER_4_BY_4_DB,
LAUNCHER_3_BY_3_DB,
- LAUNCHER_2_BY_2_DB));
+ LAUNCHER_2_BY_2_DB,
+ LAUNCHER_7_BY_3_DB));
public static final List<String> OTHER_FILES = Collections.unmodifiableList(Arrays.asList(
BACKUP_DB,
diff --git a/src/com/android/launcher3/LauncherPrefs.kt b/src/com/android/launcher3/LauncherPrefs.kt
index 5c03644..712c56c 100644
--- a/src/com/android/launcher3/LauncherPrefs.kt
+++ b/src/com/android/launcher3/LauncherPrefs.kt
@@ -26,6 +26,7 @@
import com.android.launcher3.pm.InstallSessionHelper
import com.android.launcher3.provider.RestoreDbTask
import com.android.launcher3.provider.RestoreDbTask.FIRST_LOAD_AFTER_RESTORE_KEY
+import com.android.launcher3.settings.SettingsActivity
import com.android.launcher3.states.RotationHelper
import com.android.launcher3.util.DisplayController
import com.android.launcher3.util.MainThreadInitializedObject
@@ -133,6 +134,7 @@
nonRestorableItem(FIRST_LOAD_AFTER_RESTORE_KEY, false, EncryptionType.ENCRYPTED)
@JvmField val APP_WIDGET_IDS = backedUpItem(RestoreDbTask.APPWIDGET_IDS, "")
@JvmField val OLD_APP_WIDGET_IDS = backedUpItem(RestoreDbTask.APPWIDGET_OLD_IDS, "")
+
@JvmField
val GRID_NAME =
ConstantItem(
@@ -148,6 +150,9 @@
RotationHelper.getAllowRotationDefaultValue(DisplayController.INSTANCE.get(it).info)
}
+ @JvmField
+ val FIXED_LANDSCAPE_MODE = backedUpItem(SettingsActivity.FIXED_LANDSCAPE_MODE, false)
+
// Preferences for widget configurations
@JvmField
val RECONFIGURABLE_WIDGET_EDUCATION_TIP_SEEN =
diff --git a/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java b/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
index fc8465d..1b58987 100644
--- a/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
@@ -95,6 +95,7 @@
import com.android.launcher3.views.ScrimView;
import com.android.launcher3.views.SpringRelativeLayout;
import com.android.launcher3.workprofile.PersonalWorkSlidingTabStrip;
+import com.android.systemui.plugins.AllAppsRow;
import java.util.ArrayList;
import java.util.Arrays;
@@ -152,6 +153,7 @@
private final RectF mTmpRectF = new RectF();
protected AllAppsPagedView mViewPager;
protected FloatingHeaderView mHeader;
+ protected final List<AllAppsRow> mAdditionalHeaderRows = new ArrayList<>();
protected View mBottomSheetBackground;
protected RecyclerViewFastScroller mFastScroller;
private ConstraintLayout mFastScrollLetterLayout;
@@ -262,6 +264,8 @@
getLayoutInflater().inflate(R.layout.all_apps_content, this);
mHeader = findViewById(R.id.all_apps_header);
+ mAdditionalHeaderRows.clear();
+ mAdditionalHeaderRows.addAll(getAdditionalHeaderRows());
mBottomSheetBackground = findViewById(R.id.bottom_sheet_background);
mBottomSheetHandleArea = findViewById(R.id.bottom_sheet_handle_area);
mSearchRecyclerView = findViewById(R.id.search_results_list_view);
@@ -288,6 +292,10 @@
mSearchUiManager = (SearchUiManager) mSearchContainer;
}
+ public List<AllAppsRow> getAdditionalHeaderRows() {
+ return List.of();
+ }
+
@Override
protected void onFinishInflate() {
super.onFinishInflate();
@@ -714,6 +722,8 @@
}
void setupHeader() {
+ mAdditionalHeaderRows.forEach(row -> mHeader.onPluginDisconnected(row));
+
mHeader.setVisibility(View.VISIBLE);
boolean tabsHidden = !mUsingTabs;
mHeader.setup(
@@ -731,6 +741,7 @@
adapterHolder.mRecyclerView.scrollToTop();
}
});
+ mAdditionalHeaderRows.forEach(row -> mHeader.onPluginConnected(row, mActivityContext));
removeCustomRules(mHeader);
if (isSearchBarFloating()) {
diff --git a/src/com/android/launcher3/allapps/FloatingHeaderView.java b/src/com/android/launcher3/allapps/FloatingHeaderView.java
index ac06ab4..8193511 100644
--- a/src/com/android/launcher3/allapps/FloatingHeaderView.java
+++ b/src/com/android/launcher3/allapps/FloatingHeaderView.java
@@ -15,6 +15,8 @@
*/
package com.android.launcher3.allapps;
+import static com.android.launcher3.allapps.FloatingHeaderRow.NO_ROWS;
+
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Point;
@@ -109,11 +111,11 @@
// This is initialized once during inflation and stays constant after that. Fixed views
// cannot be added or removed dynamically.
- private FloatingHeaderRow[] mFixedRows = FloatingHeaderRow.NO_ROWS;
+ private FloatingHeaderRow[] mFixedRows = NO_ROWS;
// Array of all fixed rows and plugin rows. This is initialized every time a plugin is
// enabled or disabled, and represent the current set of all rows.
- private FloatingHeaderRow[] mAllRows = FloatingHeaderRow.NO_ROWS;
+ private FloatingHeaderRow[] mAllRows = NO_ROWS;
public FloatingHeaderView(@NonNull Context context) {
this(context, null);
@@ -180,6 +182,10 @@
@Override
public void onPluginConnected(AllAppsRow allAppsRowPlugin, Context context) {
+ if (mPluginRows.containsKey(allAppsRowPlugin)) {
+ // Plugin has already been connected
+ return;
+ }
PluginHeaderRow headerRow = new PluginHeaderRow(allAppsRowPlugin, this);
addView(headerRow.mView, indexOfChild(mTabLayout));
mPluginRows.put(allAppsRowPlugin, headerRow);
@@ -211,6 +217,9 @@
@Override
public void onPluginDisconnected(AllAppsRow plugin) {
PluginHeaderRow row = mPluginRows.get(plugin);
+ if (row == null) {
+ return;
+ }
removeView(row.mView);
mPluginRows.remove(plugin);
recreateAllRowsArray();
diff --git a/src/com/android/launcher3/icons/CacheableShortcutInfo.kt b/src/com/android/launcher3/icons/CacheableShortcutInfo.kt
index f8a5552..a78da23 100644
--- a/src/com/android/launcher3/icons/CacheableShortcutInfo.kt
+++ b/src/com/android/launcher3/icons/CacheableShortcutInfo.kt
@@ -121,7 +121,10 @@
item: CacheableShortcutInfo,
provider: IconProvider,
): String? =
- item.shortcutInfo.lastChangedTimestamp.toString() +
+ // Manifest shortcuts get updated on every reboot. Don't include their change timestamp as
+ // it gets covered by the app's version
+ (if (item.shortcutInfo.isDeclaredInManifest) ""
+ else item.shortcutInfo.lastChangedTimestamp.toString()) +
"-" +
provider.getStateForApp(getApplicationInfo(item))
}
diff --git a/src/com/android/launcher3/logging/StatsLogManager.java b/src/com/android/launcher3/logging/StatsLogManager.java
index e5cd76a..2550ebb 100644
--- a/src/com/android/launcher3/logging/StatsLogManager.java
+++ b/src/com/android/launcher3/logging/StatsLogManager.java
@@ -175,6 +175,10 @@
@UiEvent(doc = "User searched for a widget in the widget picker.")
LAUNCHER_WIDGETSTRAY_SEARCHED(819),
+ @UiEvent(doc = "User clicked on view all button to expand the displayed list in the "
+ + "widget picker.")
+ LAUNCHER_WIDGETSTRAY_EXPAND_PRESS(1978),
+
@UiEvent(doc = "A dragged item is dropped on 'Remove' button in the target bar")
LAUNCHER_ITEM_DROPPED_ON_REMOVE(465),
diff --git a/src/com/android/launcher3/model/GridSizeMigrationDBController.java b/src/com/android/launcher3/model/GridSizeMigrationDBController.java
index 2d6be7e..617cac7 100644
--- a/src/com/android/launcher3/model/GridSizeMigrationDBController.java
+++ b/src/com/android/launcher3/model/GridSizeMigrationDBController.java
@@ -17,7 +17,6 @@
package com.android.launcher3.model;
import static com.android.launcher3.Flags.enableSmartspaceRemovalToggle;
-import static com.android.launcher3.LauncherPrefs.IS_FIRST_LOAD_AFTER_RESTORE;
import static com.android.launcher3.LauncherSettings.Favorites.TABLE_NAME;
import static com.android.launcher3.LauncherSettings.Favorites.TMP_TABLE;
import static com.android.launcher3.Utilities.SHOULD_SHOW_FIRST_PAGE_WIDGET;
@@ -118,13 +117,14 @@
@NonNull DeviceGridState srcDeviceState,
@NonNull DeviceGridState destDeviceState,
@NonNull DatabaseHelper target,
- @NonNull SQLiteDatabase source) {
+ @NonNull SQLiteDatabase source,
+ boolean isDestNewDb) {
if (!needsToMigrate(srcDeviceState, destDeviceState)) {
return true;
}
- if (LauncherPrefs.get(context).get(IS_FIRST_LOAD_AFTER_RESTORE)
+ if (isDestNewDb
&& srcDeviceState.getColumns().equals(destDeviceState.getColumns())
&& srcDeviceState.getRows() < destDeviceState.getRows()) {
// Only use this strategy when comparing the previous grid to the new grid and the
diff --git a/src/com/android/launcher3/model/GridSizeMigrationLogic.kt b/src/com/android/launcher3/model/GridSizeMigrationLogic.kt
index 07316ef..c856d4b 100644
--- a/src/com/android/launcher3/model/GridSizeMigrationLogic.kt
+++ b/src/com/android/launcher3/model/GridSizeMigrationLogic.kt
@@ -46,6 +46,7 @@
destDeviceState: DeviceGridState,
target: DatabaseHelper,
source: SQLiteDatabase,
+ isDestNewDb: Boolean,
) {
if (!GridSizeMigrationDBController.needsToMigrate(srcDeviceState, destDeviceState)) {
return
@@ -57,7 +58,7 @@
// This is a special case where if the grid is the same amount of columns but a larger
// amount of rows we simply copy over the source grid to the destination grid, rather
// than undergoing the general grid migration.
- if (shouldMigrateToStrictlyTallerGrid(isFirstLoad, srcDeviceState, destDeviceState)) {
+ if (shouldMigrateToStrictlyTallerGrid(isDestNewDb, srcDeviceState, destDeviceState)) {
GridSizeMigrationDBController.copyCurrentGridToNewGrid(
context,
destDeviceState,
@@ -335,11 +336,11 @@
/** Only migrate the grid in this manner if the target grid is taller and not wider. */
private fun shouldMigrateToStrictlyTallerGrid(
- isFirstLoad: Boolean,
+ isDestNewDb: Boolean,
srcDeviceState: DeviceGridState,
destDeviceState: DeviceGridState,
): Boolean {
- return isFirstLoad &&
+ return isDestNewDb &&
srcDeviceState.columns == destDeviceState.columns &&
srcDeviceState.rows < destDeviceState.rows
}
diff --git a/src/com/android/launcher3/model/ModelDbController.java b/src/com/android/launcher3/model/ModelDbController.java
index 094798b..6ff8547 100644
--- a/src/com/android/launcher3/model/ModelDbController.java
+++ b/src/com/android/launcher3/model/ModelDbController.java
@@ -89,6 +89,7 @@
import java.io.File;
import java.io.InputStream;
import java.io.StringReader;
+import java.util.List;
/**
* Utility class which maintains an instance of Launcher database and provides utility methods
@@ -368,6 +369,13 @@
InvariantDeviceProfile idp = LauncherAppState.getIDP(mContext);
DatabaseHelper oldHelper = mOpenHelper;
+
+ // We save the existing db's before creating the destination db helper so we know what logic
+ // to run in grid migration based on if that grid already existed before migration or not.
+ List<String> existingDBs = LauncherFiles.GRID_DB_FILES.stream()
+ .filter(dbName -> mContext.getDatabasePath(dbName).exists())
+ .toList();
+
mOpenHelper = (mContext instanceof SandboxContext) ? oldHelper
: createDatabaseHelper(true, new DeviceGridState(idp).getDbFile());
try {
@@ -376,9 +384,11 @@
// This is the state we want to migrate to that is given by the idp
DeviceGridState destDeviceState = new DeviceGridState(idp);
+ boolean isDestNewDb = !existingDBs.contains(destDeviceState.getDbFile());
+
GridSizeMigrationLogic gridSizeMigrationLogic = new GridSizeMigrationLogic();
gridSizeMigrationLogic.migrateGrid(mContext, srcDeviceState, destDeviceState,
- mOpenHelper, oldHelper.getWritableDatabase());
+ mOpenHelper, oldHelper.getWritableDatabase(), isDestNewDb);
} catch (Exception e) {
resetLauncherDb(restoreEventLogger);
throw new Exception("Failed to migrate grid", e);
@@ -441,6 +451,13 @@
return false;
}
DatabaseHelper oldHelper = mOpenHelper;
+
+ // We save the existing db's before creating the destination db helper so we know what logic
+ // to run in grid migration based on if that grid already existed before migration or not.
+ List<String> existingDBs = LauncherFiles.GRID_DB_FILES.stream()
+ .filter(dbName -> mContext.getDatabasePath(dbName).exists())
+ .toList();
+
mOpenHelper = (mContext instanceof SandboxContext) ? oldHelper
: createDatabaseHelper(true /* forMigration */, targetDbName);
try {
@@ -448,8 +465,11 @@
DeviceGridState srcDeviceState = new DeviceGridState(mContext);
// This is the state we want to migrate to that is given by the idp
DeviceGridState destDeviceState = new DeviceGridState(idp);
+
+ boolean isDestNewDb = !existingDBs.contains(destDeviceState.getDbFile());
+
return GridSizeMigrationDBController.migrateGridIfNeeded(mContext, srcDeviceState,
- destDeviceState, mOpenHelper, oldHelper.getWritableDatabase());
+ destDeviceState, mOpenHelper, oldHelper.getWritableDatabase(), isDestNewDb);
} catch (Exception e) {
FileLog.e(TAG, "Failed to migrate grid", e);
return false;
diff --git a/src/com/android/launcher3/settings/SettingsActivity.java b/src/com/android/launcher3/settings/SettingsActivity.java
index bd9298b..76f8dd3 100644
--- a/src/com/android/launcher3/settings/SettingsActivity.java
+++ b/src/com/android/launcher3/settings/SettingsActivity.java
@@ -26,6 +26,7 @@
import android.app.Activity;
import android.content.Intent;
+import android.content.pm.ActivityInfo;
import android.net.Uri;
import android.os.Bundle;
import android.provider.Settings;
@@ -66,6 +67,8 @@
@VisibleForTesting
static final String DEVELOPER_OPTIONS_KEY = "pref_developer_options";
+ public static final String FIXED_LANDSCAPE_MODE = "pref_fixed_landscape_mode";
+
private static final String NOTIFICATION_DOTS_PREFERENCE_KEY = "pref_icon_badging";
public static final String EXTRA_FRAGMENT_ARGS = ":settings:fragment_args";
@@ -236,7 +239,7 @@
/**
* Finds the parent preference screen for the given target key.
*
- * @param parent the parent preference screen
+ * @param parent the parent preference screen
* @param targetKey the key of the preference to find
* @return the parent preference screen that contains the target preference
*/
@@ -286,13 +289,11 @@
* will remove that preference from the list.
*/
protected boolean initPreference(Preference preference) {
+ DisplayController.Info info = DisplayController.INSTANCE.get(getContext()).getInfo();
switch (preference.getKey()) {
case NOTIFICATION_DOTS_PREFERENCE_KEY:
return BuildConfig.NOTIFICATION_DOTS_ENABLED;
-
case ALLOW_ROTATION_PREFERENCE_KEY:
- DisplayController.Info info =
- DisplayController.INSTANCE.get(getContext()).getInfo();
if (info.isTablet(info.realBounds)) {
// Launcher supports rotation by default. No need to show this setting.
return false;
@@ -300,14 +301,29 @@
// Initialize the UI once
preference.setDefaultValue(RotationHelper.getAllowRotationDefaultValue(info));
return true;
-
case DEVELOPER_OPTIONS_KEY:
if (IS_STUDIO_BUILD) {
preference.setOrder(0);
}
return mDeveloperOptionsEnabled;
+ case FIXED_LANDSCAPE_MODE:
+ if (!Flags.oneGridSpecs()) {
+ return false;
+ }
+ // When the setting changes rotate the screen accordingly to showcase the result
+ // of the setting
+ preference.setOnPreferenceChangeListener(
+ (pref, newValue) -> {
+ getActivity().setRequestedOrientation(
+ (boolean) newValue
+ ? ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
+ : ActivityInfo.SCREEN_ORIENTATION_USER
+ );
+ return true;
+ }
+ );
+ return !info.isTablet(info.realBounds);
}
-
return true;
}
diff --git a/src/com/android/launcher3/states/RotationHelper.java b/src/com/android/launcher3/states/RotationHelper.java
index 4c9da5d..1f397d1 100644
--- a/src/com/android/launcher3/states/RotationHelper.java
+++ b/src/com/android/launcher3/states/RotationHelper.java
@@ -18,6 +18,7 @@
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LOCKED;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_NOSENSOR;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE;
import static android.util.DisplayMetrics.DENSITY_DEVICE_STABLE;
import static com.android.launcher3.LauncherPrefs.ALLOW_ROTATION;
@@ -62,6 +63,7 @@
public static final int REQUEST_NONE = 0;
public static final int REQUEST_ROTATE = 1;
public static final int REQUEST_LOCK = 2;
+ public static final int REQUEST_FIXED_LANDSCAPE = 3;
@NonNull
private final BaseActivity mActivity;
@@ -195,7 +197,9 @@
}
final int activityFlags;
- if (mStateHandlerRequest != REQUEST_NONE) {
+ if (mStateHandlerRequest == REQUEST_FIXED_LANDSCAPE) {
+ activityFlags = SCREEN_ORIENTATION_USER_LANDSCAPE;
+ } else if (mStateHandlerRequest != REQUEST_NONE) {
activityFlags = mStateHandlerRequest == REQUEST_LOCK ?
SCREEN_ORIENTATION_LOCKED : SCREEN_ORIENTATION_UNSPECIFIED;
} else if (mCurrentTransitionRequest != REQUEST_NONE) {
diff --git a/src/com/android/launcher3/widget/model/WidgetsListExpandActionEntry.java b/src/com/android/launcher3/widget/model/WidgetsListExpandActionEntry.java
new file mode 100644
index 0000000..8c84030
--- /dev/null
+++ b/src/com/android/launcher3/widget/model/WidgetsListExpandActionEntry.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2024 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.widget.model;
+
+import android.os.Process;
+
+import com.android.launcher3.model.data.PackageItemInfo;
+
+import java.util.Collections;
+
+/**
+ * Binds the section to be displayed at the bottom of the widgets list that enables user to expand
+ * and view all the widget apps including non-default. Bound when
+ * {@link WidgetsListExpandActionEntry} exists in the list on adapter.
+ */
+public class WidgetsListExpandActionEntry extends WidgetsListBaseEntry {
+
+ public WidgetsListExpandActionEntry() {
+ super(/*pkgItem=*/ new PackageItemInfo(/* packageName= */ "", Process.myUserHandle()),
+ /*titleSectionName=*/ "",
+ /*items=*/ Collections.EMPTY_LIST);
+ mPkgItem.title = "";
+ }
+}
diff --git a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
index 1860977..2f64ab1 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
@@ -16,12 +16,16 @@
package com.android.launcher3.widget.picker;
import static com.android.launcher3.Flags.enableCategorizedWidgetSuggestions;
+import static com.android.launcher3.Flags.enableTieredWidgetsByDefaultInPicker;
import static com.android.launcher3.Flags.enableUnfoldedTwoPanePicker;
import static com.android.launcher3.allapps.ActivityAllAppsContainerView.AdapterHolder.SEARCH;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_WIDGETSTRAY_EXPAND_PRESS;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_WIDGETSTRAY_SEARCHED;
import static com.android.launcher3.testing.shared.TestProtocol.NORMAL_STATE_ORDINAL;
import static com.android.launcher3.views.RecyclerViewFastScroller.FastScrollerLocation.WIDGET_SCROLLER;
+import static java.util.Collections.emptyList;
+
import android.animation.Animator;
import android.content.Context;
import android.content.res.Resources;
@@ -68,6 +72,7 @@
import com.android.launcher3.widget.BaseWidgetSheet;
import com.android.launcher3.widget.WidgetCell;
import com.android.launcher3.widget.model.WidgetsListBaseEntry;
+import com.android.launcher3.widget.picker.model.data.WidgetPickerData;
import com.android.launcher3.widget.picker.search.SearchModeListener;
import com.android.launcher3.widget.picker.search.WidgetsSearchBar;
import com.android.launcher3.widget.picker.search.WidgetsSearchBar.WidgetsSearchDataProvider;
@@ -87,7 +92,8 @@
*/
public class WidgetsFullSheet extends BaseWidgetSheet
implements OnActivePageChangedListener,
- WidgetsRecyclerView.HeaderViewDimensionsProvider, SearchModeListener {
+ WidgetsRecyclerView.HeaderViewDimensionsProvider, SearchModeListener,
+ WidgetsListAdapter.ExpandButtonClickListener {
private static final long FADE_IN_DURATION = 150;
@@ -257,7 +263,13 @@
mSearchBar.initialize(new WidgetsSearchDataProvider() {
@Override
public List<WidgetsListBaseEntry> getWidgets() {
- return getWidgetsToDisplay();
+ if (enableTieredWidgetsByDefaultInPicker()) {
+ // search all
+ return mActivityContext.getWidgetPickerDataProvider().get().getAllWidgets();
+ } else {
+ // Can be removed when inlining enableTieredWidgetsByDefaultInPicker flag
+ return getWidgetsToDisplay();
+ }
}
}, /* searchModeListener= */ this);
}
@@ -482,6 +494,9 @@
/**
* Returns all displayable widgets.
*/
+ // Used by the two pane sheet to show 3-dot menu to toggle between default lists and all lists
+ // when enableTieredWidgetsByDefaultInPicker is OFF. This code path and the 3-dot menu can be
+ // safely deleted when it's alternative "enableTieredWidgetsByDefaultInPicker" flag is inlined.
protected List<WidgetsListBaseEntry> getWidgetsToDisplay() {
return mActivityContext.getWidgetPickerDataProvider().get().getAllWidgets();
}
@@ -491,16 +506,27 @@
if (mIsInSearchMode) {
return;
}
- List<WidgetsListBaseEntry> widgets = getWidgetsToDisplay();
+ List<WidgetsListBaseEntry> widgets;
+ List<WidgetsListBaseEntry> defaultWidgets = emptyList();
+
+ if (enableTieredWidgetsByDefaultInPicker()) {
+ WidgetPickerData dataProvider =
+ mActivityContext.getWidgetPickerDataProvider().get();
+ widgets = dataProvider.getAllWidgets();
+ defaultWidgets = dataProvider.getDefaultWidgets();
+ } else {
+ // This code path can be deleted once enableTieredWidgetsByDefaultInPicker is inlined.
+ widgets = getWidgetsToDisplay();
+ }
AdapterHolder primaryUserAdapterHolder = mAdapters.get(AdapterHolder.PRIMARY);
- primaryUserAdapterHolder.mWidgetsListAdapter.setWidgets(widgets);
+ primaryUserAdapterHolder.mWidgetsListAdapter.setWidgets(widgets, defaultWidgets);
if (mHasWorkProfile) {
mViewPager.setVisibility(VISIBLE);
mTabBar.setVisibility(VISIBLE);
AdapterHolder workUserAdapterHolder = mAdapters.get(AdapterHolder.WORK);
- workUserAdapterHolder.mWidgetsListAdapter.setWidgets(widgets);
+ workUserAdapterHolder.mWidgetsListAdapter.setWidgets(widgets, defaultWidgets);
onActivePageChanged(mViewPager.getCurrentPage());
} else {
onActivePageChanged(0);
@@ -518,6 +544,16 @@
}
@Override
+ public void onWidgetsListExpandButtonClick(View v) {
+ AdapterHolder currentAdapterHolder = mAdapters.get(getCurrentAdapterHolderType());
+ currentAdapterHolder.mWidgetsListAdapter.useExpandedList();
+ onWidgetsBound();
+ currentAdapterHolder.mWidgetsRecyclerView.announceForAccessibility(
+ mActivityContext.getString(R.string.widgets_list_expanded));
+ mActivityContext.getStatsLogManager().logger().log(LAUNCHER_WIDGETSTRAY_EXPAND_PRESS);
+ }
+
+ @Override
public void enterSearchMode(boolean shouldLog) {
if (mIsInSearchMode) return;
setViewVisibilityBasedOnSearch(/*isInSearchMode= */ true);
@@ -832,7 +868,7 @@
+ marginLayoutParams.topMargin;
}
- private int getCurrentAdapterHolderType() {
+ protected int getCurrentAdapterHolderType() {
if (mIsInSearchMode) {
return SEARCH;
} else if (mViewPager != null) {
@@ -861,6 +897,7 @@
WidgetsFullSheet sheet = show(BaseActivity.fromContext(getContext()), false);
sheet.restoreRecommendations(mRecommendedWidgets, mRecommendedWidgetsMap);
sheet.restoreHierarchyState(widgetsState);
+ sheet.restoreAdapterStates(mAdapters);
sheet.restorePreviousAdapterHolderType(getCurrentAdapterHolderType());
} else if (!isTwoPane()) {
reset();
@@ -876,6 +913,17 @@
mRecommendedWidgetsMap = recommendedWidgetsMap;
}
+ private void restoreAdapterStates(SparseArray<AdapterHolder> adapters) {
+ if (adapters.contains(AdapterHolder.WORK)) {
+ mAdapters.get(AdapterHolder.WORK).mWidgetsListAdapter.restoreState(
+ adapters.get(AdapterHolder.WORK).mWidgetsListAdapter);
+ }
+ mAdapters.get(AdapterHolder.PRIMARY).mWidgetsListAdapter.restoreState(
+ adapters.get(AdapterHolder.PRIMARY).mWidgetsListAdapter);
+ mAdapters.get(AdapterHolder.SEARCH).mWidgetsListAdapter.restoreState(
+ adapters.get(AdapterHolder.SEARCH).mWidgetsListAdapter);
+ }
+
/**
* Indicates if layout should be re-created on device profile change - so that a different
* layout can be displayed.
@@ -1045,6 +1093,7 @@
this::getEmptySpaceHeight,
/* iconClickListener= */ WidgetsFullSheet.this,
/* iconLongClickListener= */ WidgetsFullSheet.this,
+ /* expandButtonClickListener= */ WidgetsFullSheet.this,
isTwoPane());
mWidgetsListAdapter.setHasStableIds(true);
switch (mAdapterType) {
diff --git a/src/com/android/launcher3/widget/picker/WidgetsListAdapter.java b/src/com/android/launcher3/widget/picker/WidgetsListAdapter.java
index 3d3a669..3c67538 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsListAdapter.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsListAdapter.java
@@ -49,6 +49,7 @@
import com.android.launcher3.widget.model.WidgetListSpaceEntry;
import com.android.launcher3.widget.model.WidgetsListBaseEntry;
import com.android.launcher3.widget.model.WidgetsListContentEntry;
+import com.android.launcher3.widget.model.WidgetsListExpandActionEntry;
import com.android.launcher3.widget.model.WidgetsListHeaderEntry;
import com.android.launcher3.widget.util.WidgetSizes;
@@ -82,6 +83,7 @@
public static final int VIEW_TYPE_WIDGETS_SPACE = R.id.view_type_widgets_space;
public static final int VIEW_TYPE_WIDGETS_LIST = R.id.view_type_widgets_list;
public static final int VIEW_TYPE_WIDGETS_HEADER = R.id.view_type_widgets_header;
+ public static final int VIEW_TYPE_WIDGETS_EXPAND = R.id.view_type_widgets_list_expand;
private final Context mContext;
private final SparseArray<ViewHolderBinder> mViewHolderBinders = new SparseArray<>();
@@ -90,7 +92,9 @@
@Nullable private WidgetsTwoPaneSheet.HeaderChangeListener mHeaderChangeListener;
private final List<WidgetsListBaseEntry> mAllEntries = new ArrayList<>();
+ private final List<WidgetsListBaseEntry> mAllDefaultEntries = new ArrayList<>();
private ArrayList<WidgetsListBaseEntry> mVisibleEntries = new ArrayList<>();
+
@Nullable private PackageUserKey mWidgetsContentVisiblePackageUserKey = null;
private Predicate<WidgetsListBaseEntry> mHeaderAndSelectedContentFilter = entry ->
@@ -102,9 +106,12 @@
@Nullable private PackageUserKey mPendingClickHeader;
@Px private int mMaxHorizontalSpan;
+ private boolean mShowOnlyDefaultList = true;
+
public WidgetsListAdapter(Context context, LayoutInflater layoutInflater,
IntSupplier emptySpaceHeightProvider, OnClickListener iconClickListener,
OnLongClickListener iconLongClickListener,
+ ExpandButtonClickListener expandButtonClickListener,
boolean isTwoPane) {
mContext = context;
mMaxHorizontalSpan = WidgetSizes.getWidgetSizePx(
@@ -123,6 +130,16 @@
mViewHolderBinders.put(
VIEW_TYPE_WIDGETS_SPACE,
new WidgetsSpaceViewHolderBinder(emptySpaceHeightProvider));
+ mViewHolderBinders.put(VIEW_TYPE_WIDGETS_EXPAND,
+ new WidgetsListExpandActionViewHolderBinder(layoutInflater,
+ expandButtonClickListener::onWidgetsListExpandButtonClick));
+ }
+
+ /**
+ * Copies state info from another adapter.
+ */
+ public void restoreState(WidgetsListAdapter adapter) {
+ mShowOnlyDefaultList = adapter.mShowOnlyDefaultList;
}
public void setHeaderChangeListener(WidgetsTwoPaneSheet.HeaderChangeListener
@@ -168,10 +185,21 @@
}
/** Updates the widget list based on {@code tempEntries}. */
- public void setWidgets(List<WidgetsListBaseEntry> tempEntries) {
+ public void setWidgets(List<WidgetsListBaseEntry> tempEntries,
+ List<WidgetsListBaseEntry> tempDefaultEntries) {
mAllEntries.clear();
mAllEntries.add(new WidgetListSpaceEntry());
tempEntries.stream().sorted(mRowComparator).forEach(mAllEntries::add);
+
+ mAllDefaultEntries.clear();
+
+ if (mShowOnlyDefaultList && !tempDefaultEntries.isEmpty()) {
+ mAllDefaultEntries.add(new WidgetListSpaceEntry());
+ tempDefaultEntries.stream().sorted(mRowComparator).forEach(mAllDefaultEntries::add);
+ // Include view all action when default entries exist.
+ mAllDefaultEntries.add(new WidgetsListExpandActionEntry());
+ }
+
updateVisibleEntries();
}
@@ -179,7 +207,8 @@
public void setWidgetsOnSearch(List<WidgetsListBaseEntry> searchResults) {
// Forget the expanded package every time widget list is refreshed in search mode.
mWidgetsContentVisiblePackageUserKey = null;
- setWidgets(searchResults);
+ mShowOnlyDefaultList = false;
+ setWidgets(searchResults, /*tempDefaultEntries=*/ List.of());
}
private void updateVisibleEntries() {
@@ -190,10 +219,11 @@
OptionalInt topForPackageUserKey =
getOffsetForPosition(previousPositionForPackageUserKey);
- List<WidgetsListBaseEntry> newVisibleEntries = mAllEntries.stream()
+ List<WidgetsListBaseEntry> newVisibleEntries = getAllEntries().stream()
.filter(entry -> (((mFilter == null || mFilter.test(entry))
&& mHeaderAndSelectedContentFilter.test(entry))
- || entry instanceof WidgetListSpaceEntry)
+ || entry instanceof WidgetListSpaceEntry
+ || entry instanceof WidgetsListExpandActionEntry)
&& (mHeaderChangeListener == null
|| !(entry instanceof WidgetsListContentEntry)))
.map(entry -> {
@@ -227,6 +257,11 @@
}
}
+ private List<WidgetsListBaseEntry> getAllEntries() {
+ return (mShowOnlyDefaultList && !mAllDefaultEntries.isEmpty()) ? mAllDefaultEntries
+ : mAllEntries;
+ }
+
/** Returns whether {@code entry} matches {@code key}. */
private static boolean isHeaderForPackageUserKey(
@NonNull WidgetsListBaseEntry entry, @Nullable PackageUserKey key) {
@@ -262,7 +297,13 @@
// The first entry has an empty space, count from second entries.
int listPos = (pos > 1) ? POSITION_DEFAULT : POSITION_FIRST;
- if (pos == (getItemCount() - 1)) {
+ int lastIndex = getItemCount() - 1;
+ // Last index may be the view all entry
+ int actualLastItemIndex = (mVisibleEntries.get(
+ lastIndex) instanceof WidgetsListExpandActionEntry) ? getItemCount() - 2
+ : getItemCount() - 1;
+
+ if (pos == (actualLastItemIndex)) {
listPos |= POSITION_LAST;
}
viewHolderBinder.bindViewHolder(holder, mVisibleEntries.get(pos), listPos, payloads);
@@ -319,6 +360,8 @@
return VIEW_TYPE_WIDGETS_HEADER;
} else if (entry instanceof WidgetListSpaceEntry) {
return VIEW_TYPE_WIDGETS_SPACE;
+ } else if (entry instanceof WidgetsListExpandActionEntry) {
+ return VIEW_TYPE_WIDGETS_EXPAND;
}
throw new UnsupportedOperationException("ViewHolderBinder not found for " + entry);
}
@@ -412,6 +455,23 @@
updateVisibleEntries();
}
+ /**
+ * Returns the widget content {@link WidgetsListContentEntry} for a selected header.
+ */
+ public WidgetsListContentEntry getContentEntry(PackageUserKey selectedHeader) {
+ return getAllEntries().stream().filter(entry -> entry instanceof WidgetsListContentEntry)
+ .map(entry -> (WidgetsListContentEntry) entry)
+ .filter(entry -> PackageUserKey.fromPackageItemInfo(entry.mPkgItem).equals(
+ selectedHeader)).findFirst().orElse(null);
+ }
+
+ /**
+ * Sets adapter to use expanded list when updating widgets.
+ */
+ public void useExpandedList() {
+ mShowOnlyDefaultList = false;
+ }
+
/** Comparator for sorting WidgetListRowEntry based on package title. */
public static class WidgetListBaseRowEntryComparator implements
Comparator<WidgetsListBaseEntry> {
@@ -430,4 +490,10 @@
return 1;
}
}
+
+ /** Callback interface for the interaction with the expand button */
+ public interface ExpandButtonClickListener {
+ /** Called when user clicks the button at end of widget apps list to expand it. */
+ void onWidgetsListExpandButtonClick(View view);
+ }
}
diff --git a/src/com/android/launcher3/widget/picker/WidgetsListExpandActionViewHolderBinder.java b/src/com/android/launcher3/widget/picker/WidgetsListExpandActionViewHolderBinder.java
new file mode 100644
index 0000000..288c456
--- /dev/null
+++ b/src/com/android/launcher3/widget/picker/WidgetsListExpandActionViewHolderBinder.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2024 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.widget.picker;
+
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.android.launcher3.R;
+import com.android.launcher3.recyclerview.ViewHolderBinder;
+import com.android.launcher3.widget.model.WidgetsListExpandActionEntry;
+
+import java.util.List;
+
+/**
+ * Creates and populates views for the {@link WidgetsListExpandActionEntry}.
+ */
+public class WidgetsListExpandActionViewHolderBinder implements
+ ViewHolderBinder<WidgetsListExpandActionEntry, RecyclerView.ViewHolder> {
+ @NonNull
+ View.OnClickListener mExpandListClickListener;
+ private final LayoutInflater mLayoutInflater;
+
+ public WidgetsListExpandActionViewHolderBinder(
+ @NonNull LayoutInflater layoutInflater,
+ @NonNull View.OnClickListener expandListClickListener) {
+ mLayoutInflater = layoutInflater;
+ mExpandListClickListener = expandListClickListener;
+ }
+
+ @Override
+ public RecyclerView.ViewHolder newViewHolder(ViewGroup parent) {
+ return new RecyclerView.ViewHolder(mLayoutInflater.inflate(
+ R.layout.widgets_list_expand_button, parent, false)) {
+ };
+ }
+
+ @Override
+ public void bindViewHolder(RecyclerView.ViewHolder viewHolder,
+ WidgetsListExpandActionEntry data, int position, List<Object> payloads) {
+ viewHolder.itemView.setOnClickListener(mExpandListClickListener);
+ }
+}
diff --git a/src/com/android/launcher3/widget/picker/WidgetsTwoPaneSheet.java b/src/com/android/launcher3/widget/picker/WidgetsTwoPaneSheet.java
index f4b99a0..f9bd5f1 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsTwoPaneSheet.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsTwoPaneSheet.java
@@ -16,6 +16,7 @@
package com.android.launcher3.widget.picker;
import static com.android.launcher3.Flags.enableCategorizedWidgetSuggestions;
+import static com.android.launcher3.Flags.enableTieredWidgetsByDefaultInPicker;
import static com.android.launcher3.Flags.enableUnfoldedTwoPanePicker;
import static com.android.launcher3.UtilitiesKt.CLIP_CHILDREN_FALSE_MODIFIER;
import static com.android.launcher3.UtilitiesKt.CLIP_TO_PADDING_FALSE_MODIFIER;
@@ -147,7 +148,9 @@
mHeaderDescription = mContent.findViewById(R.id.widget_picker_description);
mWidgetOptionsMenu = mContent.findViewById(R.id.widget_picker_widget_options_menu);
- setupWidgetOptionsMenu();
+ if (!enableTieredWidgetsByDefaultInPicker()) {
+ setupWidgetOptionsMenu();
+ }
mRightPane = mContent.findViewById(R.id.right_pane);
mRightPaneScrollView = mContent.findViewById(R.id.right_pane_scroll_view);
@@ -286,6 +289,9 @@
}
}
+ // Used by the two pane sheet to show 3-dot menu to toggle between default lists and all lists
+ // when enableTieredWidgetsByDefaultInPicker is OFF. This code path and the 3-dot menu can be
+ // safely deleted when it's alternative "enableTieredWidgetsByDefaultInPicker" flag is inlined.
@Override
protected List<WidgetsListBaseEntry> getWidgetsToDisplay() {
List<WidgetsListBaseEntry> allWidgets =
@@ -319,6 +325,15 @@
}
@Override
+ public void onWidgetsListExpandButtonClick(View v) {
+ super.onWidgetsListExpandButtonClick(v);
+ // Refresh right pane with updated data for the selected header.
+ if (mSelectedHeader != null && mSelectedHeader != mSuggestedWidgetsPackageUserKey) {
+ getHeaderChangeListener().onHeaderChanged(mSelectedHeader);
+ }
+ }
+
+ @Override
public void onRecommendedWidgetsBound() {
super.onRecommendedWidgetsBound();
@@ -511,11 +526,20 @@
&& !mOpenCloseAnimation.getAnimationPlayer().isRunning()
&& !getAccessibilityInitialFocusView().isAccessibilityFocused();
mSelectedHeader = selectedHeader;
- final boolean showDefaultWidgets = mWidgetOptionsMenuState != null
- && !mWidgetOptionsMenuState.showAllWidgets;
- WidgetsListContentEntry contentEntry = findContentEntryForPackageUser(
- mActivityContext.getWidgetPickerDataProvider().get(),
- selectedHeader, showDefaultWidgets);
+
+ WidgetsListContentEntry contentEntry;
+ if (enableTieredWidgetsByDefaultInPicker()) {
+ contentEntry = mAdapters.get(
+ getCurrentAdapterHolderType()).mWidgetsListAdapter.getContentEntry(
+ selectedHeader);
+ } else { // Can be deleted when inlining the "enableTieredWidgetsByDefaultInPicker"
+ // flag
+ final boolean showDefaultWidgets = mWidgetOptionsMenuState != null
+ && !mWidgetOptionsMenuState.showAllWidgets;
+ contentEntry = findContentEntryForPackageUser(
+ mActivityContext.getWidgetPickerDataProvider().get(),
+ selectedHeader, showDefaultWidgets);
+ }
if (contentEntry == null || mRightPane == null) {
return;
diff --git a/tests/multivalentTests/src/com/android/launcher3/AbstractDeviceProfileTest.kt b/tests/multivalentTests/src/com/android/launcher3/AbstractDeviceProfileTest.kt
index 55a028b..62c8426 100644
--- a/tests/multivalentTests/src/com/android/launcher3/AbstractDeviceProfileTest.kt
+++ b/tests/multivalentTests/src/com/android/launcher3/AbstractDeviceProfileTest.kt
@@ -307,6 +307,7 @@
whenever(launcherPrefs.get(LauncherPrefs.TASKBAR_PINNING)).thenReturn(false)
whenever(launcherPrefs.get(LauncherPrefs.TASKBAR_PINNING_IN_DESKTOP_MODE)).thenReturn(true)
+ whenever(launcherPrefs.get(LauncherPrefs.FIXED_LANDSCAPE_MODE)).thenReturn(false)
whenever(launcherPrefs.get(LauncherPrefs.HOTSEAT_COUNT)).thenReturn(-1)
whenever(launcherPrefs.get(LauncherPrefs.DEVICE_TYPE)).thenReturn(-1)
whenever(launcherPrefs.get(LauncherPrefs.WORKSPACE_SIZE)).thenReturn("")
diff --git a/tests/multivalentTests/src/com/android/launcher3/icons/IconCacheUpdateHandlerTest.kt b/tests/multivalentTests/src/com/android/launcher3/icons/IconCacheUpdateHandlerTest.kt
index ed9a080..bae74c8 100644
--- a/tests/multivalentTests/src/com/android/launcher3/icons/IconCacheUpdateHandlerTest.kt
+++ b/tests/multivalentTests/src/com/android/launcher3/icons/IconCacheUpdateHandlerTest.kt
@@ -19,6 +19,7 @@
import android.content.ComponentName
import android.content.pm.ApplicationInfo
import android.database.MatrixCursor
+import android.os.Handler
import android.os.Process.myUserHandle
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
@@ -34,9 +35,18 @@
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.Captor
import org.mockito.Mock
import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.any
+import org.mockito.kotlin.anyOrNull
+import org.mockito.kotlin.doAnswer
import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.never
+import org.mockito.kotlin.times
+import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever
@SmallTest
@@ -45,14 +55,25 @@
@Mock private lateinit var iconProvider: IconProvider
@Mock private lateinit var baseIconCache: BaseIconCache
+ @Mock private lateinit var cacheDb: IconDB
+ @Mock private lateinit var workerHandler: Handler
- private var cursor: MatrixCursor? = null
- private var cachingLogic = CachedObjectCachingLogic
+ @Captor private lateinit var deleteCaptor: ArgumentCaptor<String>
+
+ private var cursor =
+ MatrixCursor(
+ arrayOf(IconDB.COLUMN_ROWID, IconDB.COLUMN_COMPONENT, IconDB.COLUMN_FRESHNESS_ID)
+ )
+
+ private lateinit var updateHandlerUnderTest: IconCacheUpdateHandler
@Before
fun setup() {
MockitoAnnotations.initMocks(this)
doReturn(iconProvider).whenever(baseIconCache).iconProvider
+ doReturn(cursor).whenever(cacheDb).query(any(), any(), any())
+
+ updateHandlerUnderTest = IconCacheUpdateHandler(baseIconCache, cacheDb, workerHandler)
}
@After
@@ -61,63 +82,136 @@
}
@Test
- fun `IconCacheUpdateHandler returns null if the component name is malformed`() {
- val updateHandlerUnderTest = IconCacheUpdateHandler(baseIconCache)
- val cn = ComponentName.unflattenFromString("com.android.fake/.FakeActivity")!!
+ fun `keeps correct icons irrespective of call order`() {
+ val obj1 = TestCachedObject(1).apply { addToCursor(cursor) }
+ val obj2 = TestCachedObject(2).apply { addToCursor(cursor) }
- val result =
- updateHandlerUnderTest.updateOrDeleteIcon(
- createCursor(1, cn.flattenToString() + "#", "freshId-old"),
- hashMapOf(cn to TestCachedObject(cn, "freshId")),
- setOf(),
- myUserHandle(),
- cachingLogic,
- )
- assertThat(result).isNull()
+ updateHandlerUnderTest.updateIcons(obj1)
+ updateHandlerUnderTest.updateIcons(obj2)
+ updateHandlerUnderTest.finish()
+
+ verify(cacheDb, never()).delete(any(), anyOrNull())
}
@Test
- fun `IconCacheUpdateHandler returns null if the freshId match`() {
- val updateHandlerUnderTest = IconCacheUpdateHandler(baseIconCache)
- val cn = ComponentName.unflattenFromString("com.android.fake/.FakeActivity")!!
+ fun `removes missing entries in single call`() {
+ TestCachedObject(1).addToCursor(cursor)
+ TestCachedObject(2).addToCursor(cursor)
+ TestCachedObject(3).addToCursor(cursor)
+ TestCachedObject(4).addToCursor(cursor)
+ TestCachedObject(5).addToCursor(cursor)
- val result =
- updateHandlerUnderTest.updateOrDeleteIcon(
- createCursor(1, cn.flattenToString(), "freshId"),
- hashMapOf(cn to TestCachedObject(cn, "freshId")),
- setOf(),
- myUserHandle(),
- cachingLogic,
- )
- assertThat(result).isNull()
+ updateHandlerUnderTest.updateIcons(TestCachedObject(1), TestCachedObject(4))
+ updateHandlerUnderTest.finish()
+
+ verifyItemsDeleted(2, 3, 5)
}
@Test
- fun `IconCacheUpdateHandler returns non-null if the freshId do not match`() {
- val updateHandlerUnderTest = IconCacheUpdateHandler(baseIconCache)
- val cn = ComponentName.unflattenFromString("com.android.fake/.FakeActivity")!!
- val testObj = TestCachedObject(cn, "freshId")
+ fun `removes missing entries in multiple calls`() {
+ TestCachedObject(1).addToCursor(cursor)
+ TestCachedObject(2).addToCursor(cursor)
+ TestCachedObject(3).addToCursor(cursor)
+ TestCachedObject(4).addToCursor(cursor)
+ TestCachedObject(5).addToCursor(cursor)
+ TestCachedObject(6).addToCursor(cursor)
- val result =
- updateHandlerUnderTest.updateOrDeleteIcon(
- createCursor(1, cn.flattenToString(), "freshId-old"),
- hashMapOf(cn to testObj),
- setOf(),
- myUserHandle(),
- cachingLogic,
- )
- assertThat(result).isEqualTo(testObj)
+ updateHandlerUnderTest.updateIcons(TestCachedObject(1), TestCachedObject(2))
+ updateHandlerUnderTest.updateIcons(TestCachedObject(4), TestCachedObject(5))
+ updateHandlerUnderTest.finish()
+
+ verifyItemsDeleted(3, 6)
}
- private fun createCursor(row: Long, component: String, appState: String) =
- MatrixCursor(
- arrayOf(IconDB.COLUMN_ROWID, IconDB.COLUMN_COMPONENT, IconDB.COLUMN_FRESHNESS_ID)
- )
- .apply { addRow(arrayOf(row, component, appState)) }
- .apply {
- cursor = this
- moveToNext()
+ @Test
+ fun `keeps valid app infos`() {
+ val appInfo = ApplicationInfo()
+ doReturn("app-fresh").whenever(iconProvider).getStateForApp(eq(appInfo))
+
+ TestCachedObject(1).addToCursor(cursor)
+ TestCachedObject(2).addToCursor(cursor)
+ cursor.addRow(arrayOf(33, TestCachedObject(1).getPackageKey(), "app-fresh"))
+
+ updateHandlerUnderTest.updateIcons(
+ TestCachedObject(1, appInfo = appInfo),
+ TestCachedObject(2),
+ )
+ updateHandlerUnderTest.finish()
+
+ verify(cacheDb, never()).delete(any(), anyOrNull())
+ }
+
+ @Test
+ fun `deletes stale app infos`() {
+ val appInfo1 = ApplicationInfo()
+ doReturn("app1-fresh").whenever(iconProvider).getStateForApp(eq(appInfo1))
+
+ val appInfo2 = ApplicationInfo()
+ doReturn("app2-fresh").whenever(iconProvider).getStateForApp(eq(appInfo2))
+
+ TestCachedObject(1).addToCursor(cursor)
+ TestCachedObject(2).addToCursor(cursor)
+ cursor.addRow(arrayOf(33, TestCachedObject(1).getPackageKey(), "app1-not-fresh"))
+ cursor.addRow(arrayOf(34, TestCachedObject(2).getPackageKey(), "app2-fresh"))
+
+ updateHandlerUnderTest.updateIcons(
+ TestCachedObject(1, appInfo = appInfo1),
+ TestCachedObject(2, appInfo = appInfo2),
+ )
+ updateHandlerUnderTest.finish()
+
+ verifyItemsDeleted(33)
+ }
+
+ @Test
+ fun `updates stale entries`() {
+ doAnswer { i ->
+ (i.arguments[0] as Runnable).run()
+ true
}
+ .whenever(workerHandler)
+ .postAtTime(any(), anyOrNull(), any())
+
+ TestCachedObject(1).addToCursor(cursor)
+ TestCachedObject(2).addToCursor(cursor)
+ TestCachedObject(3).addToCursor(cursor)
+
+ var updatedPackages = mutableSetOf<String>()
+ updateHandlerUnderTest.updateIcons(
+ listOf(
+ TestCachedObject(1, freshnessId = "not-fresh"),
+ TestCachedObject(2, freshnessId = "not-fresh"),
+ TestCachedObject(3),
+ ),
+ CachedObjectCachingLogic,
+ ) { apps, _ ->
+ updatedPackages.addAll(apps)
+ }
+ updateHandlerUnderTest.finish()
+
+ assertThat(updatedPackages)
+ .isEqualTo(
+ mutableSetOf(TestCachedObject(1).cn.packageName, TestCachedObject(2).cn.packageName)
+ )
+ }
+
+ private fun IconCacheUpdateHandler.updateIcons(vararg items: TestCachedObject) {
+ updateIcons(items.toList(), CachedObjectCachingLogic) { _, _ -> }
+ }
+
+ private fun verifyItemsDeleted(vararg rowIds: Long) {
+ verify(cacheDb, times(1)).delete(deleteCaptor.capture(), anyOrNull())
+ val actual =
+ deleteCaptor.value
+ .split('(')
+ ?.get(1)
+ ?.split(')')
+ ?.get(0)
+ ?.split(",")
+ ?.map { it.trim().toLong() }!!
+ .sorted()
+ assertThat(actual).isEqualTo(rowIds.toList().sorted())
+ }
}
/** Utility method to wait for the icon update handler to finish */
@@ -135,7 +229,13 @@
}
}
-class TestCachedObject(val cn: ComponentName, val freshnessId: String) : CachedObject {
+class TestCachedObject(
+ val rowId: Long,
+ val cn: ComponentName =
+ ComponentName.unflattenFromString("com.android.fake$rowId/.FakeActivity")!!,
+ val freshnessId: String = "fresh-$rowId",
+ val appInfo: ApplicationInfo? = null,
+) : CachedObject {
override fun getComponent() = cn
@@ -143,7 +243,13 @@
override fun getLabel(): CharSequence? = null
- override fun getApplicationInfo(): ApplicationInfo? = null
+ override fun getApplicationInfo(): ApplicationInfo? = appInfo
override fun getFreshnessIdentifier(iconProvider: IconProvider): String? = freshnessId
+
+ fun addToCursor(cursor: MatrixCursor) =
+ cursor.addRow(arrayOf(rowId, cn.flattenToString(), freshnessId))
+
+ fun getPackageKey() =
+ BaseIconCache.getPackageKey(cn.packageName, user).componentName.flattenToString()
}
diff --git a/tests/multivalentTests/src/com/android/launcher3/util/VibratorWrapperTest.kt b/tests/multivalentTests/src/com/android/launcher3/util/VibratorWrapperTest.kt
index dee98e7..0f212eb 100644
--- a/tests/multivalentTests/src/com/android/launcher3/util/VibratorWrapperTest.kt
+++ b/tests/multivalentTests/src/com/android/launcher3/util/VibratorWrapperTest.kt
@@ -66,6 +66,7 @@
@Test
fun init_register_onChangeListener() {
+ TestUtil.runOnExecutorSync(Executors.MAIN_EXECUTOR) {}
verify(settingsCache).register(HAPTIC_FEEDBACK_URI, underTest.mHapticChangeListener)
}
diff --git a/tests/src/com/android/launcher3/LauncherIntentTest.java b/tests/src/com/android/launcher3/LauncherIntentTest.java
index aeeb42a..3e16713 100644
--- a/tests/src/com/android/launcher3/LauncherIntentTest.java
+++ b/tests/src/com/android/launcher3/LauncherIntentTest.java
@@ -23,7 +23,7 @@
import android.platform.test.annotations.LargeTest;
import android.view.KeyEvent;
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.android.launcher3.allapps.ActivityAllAppsContainerView;
import com.android.launcher3.allapps.SearchRecyclerView;
diff --git a/tests/src/com/android/launcher3/dragging/TaplUninstallRemoveTest.java b/tests/src/com/android/launcher3/dragging/TaplUninstallRemoveTest.java
index 44b8ff8..1816030 100644
--- a/tests/src/com/android/launcher3/dragging/TaplUninstallRemoveTest.java
+++ b/tests/src/com/android/launcher3/dragging/TaplUninstallRemoveTest.java
@@ -69,8 +69,7 @@
private void verifyAppUninstalledFromAllApps(Workspace workspace, String appName) {
final HomeAllApps allApps = workspace.switchToAllApps();
Wait.atMost(appName + " app was found on all apps after being uninstalled",
- () -> allApps.tryGetAppIcon(appName) == null,
- DEFAULT_UI_TIMEOUT, mLauncher);
+ () -> allApps.tryGetAppIcon(appName) == null, mLauncher);
}
private void installDummyAppAndWaitForUIUpdate() throws IOException {
diff --git a/tests/src/com/android/launcher3/model/GridMigrationTest.kt b/tests/src/com/android/launcher3/model/GridMigrationTest.kt
index 666ec16..379e98d 100644
--- a/tests/src/com/android/launcher3/model/GridMigrationTest.kt
+++ b/tests/src/com/android/launcher3/model/GridMigrationTest.kt
@@ -101,6 +101,7 @@
dst.gridState,
dst.dbHelper,
src.dbHelper.readableDatabase,
+ false,
)
} else {
GridSizeMigrationDBController.migrateGridIfNeeded(
@@ -109,6 +110,7 @@
dst.gridState,
dst.dbHelper,
src.dbHelper.readableDatabase,
+ false,
)
}
}
diff --git a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
index 206647a..1fbdceb 100644
--- a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
+++ b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
@@ -32,8 +32,6 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ActivityInfo;
-import android.content.pm.LauncherActivityInfo;
-import android.content.pm.LauncherApps;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.graphics.Point;
@@ -97,10 +95,8 @@
*/
public abstract class AbstractLauncherUiTest<LAUNCHER_TYPE extends Launcher> {
- public static final long DEFAULT_ACTIVITY_TIMEOUT = TimeUnit.SECONDS.toMillis(10);
public static final long DEFAULT_BROADCAST_TIMEOUT_SECS = 10;
- public static final long DEFAULT_UI_TIMEOUT = TestUtil.DEFAULT_UI_TIMEOUT;
private static final String TAG = "AbstractLauncherUiTest";
private static final long BYTES_PER_MEGABYTE = 1 << 20;
@@ -151,7 +147,7 @@
launcher.forceGc();
return MAIN_EXECUTOR.submit(
() -> launcher.noLeakedActivities(requireOneActiveActivity)).get();
- }, DEFAULT_UI_TIMEOUT, launcher);
+ }, launcher);
}
public static String getAppPackageName() {
@@ -443,7 +439,7 @@
*/
protected <T> T getOnUiThread(final Callable<T> callback) {
try {
- return mMainThreadExecutor.submit(callback).get(DEFAULT_UI_TIMEOUT,
+ return mMainThreadExecutor.submit(callback).get(TestUtil.DEFAULT_UI_TIMEOUT,
TimeUnit.MILLISECONDS);
} catch (TimeoutException e) {
Log.e(TAG, "Timeout in getOnUiThread, sending SIGABRT", e);
@@ -498,13 +494,7 @@
// flakiness.
protected void waitForLauncherCondition(String
message, Function<LAUNCHER_TYPE, Boolean> condition) {
- waitForLauncherCondition(message, condition, DEFAULT_ACTIVITY_TIMEOUT);
- }
-
- // Cannot be used in TaplTests after injecting any gesture using Tapl because this can hide
- // flakiness.
- protected <O> O getOnceNotNull(String message, Function<LAUNCHER_TYPE, O> f) {
- return getOnceNotNull(message, f, DEFAULT_ACTIVITY_TIMEOUT);
+ waitForLauncherCondition(message, condition, TestUtil.DEFAULT_UI_TIMEOUT);
}
// Cannot be used in TaplTests after injecting any gesture using Tapl because this can hide
@@ -513,12 +503,12 @@
String message, Function<LAUNCHER_TYPE, Boolean> condition, long timeout) {
verifyKeyguardInvisible();
if (!TestHelpers.isInLauncherProcess()) return;
- Wait.atMost(message, () -> getFromLauncher(condition), timeout, mLauncher);
+ Wait.atMost(message, () -> getFromLauncher(condition), mLauncher, timeout);
}
// Cannot be used in TaplTests after injecting any gesture using Tapl because this can hide
// flakiness.
- protected <T> T getOnceNotNull(String message, Function<LAUNCHER_TYPE, T> f, long timeout) {
+ protected <T> T getOnceNotNull(String message, Function<LAUNCHER_TYPE, T> f) {
if (!TestHelpers.isInLauncherProcess()) return null;
final Object[] output = new Object[1];
@@ -526,7 +516,7 @@
final Object fromLauncher = getFromLauncher(f);
output[0] = fromLauncher;
return fromLauncher != null;
- }, timeout, mLauncher);
+ }, mLauncher);
return (T) output[0];
}
@@ -540,12 +530,7 @@
Wait.atMost(message, () -> {
testThreadAction.run();
return getFromLauncher(condition);
- }, timeout, mLauncher);
- }
-
- protected LauncherActivityInfo getSettingsApp() {
- return mTargetContext.getSystemService(LauncherApps.class)
- .getActivityList("com.android.settings", Process.myUserHandle()).get(0);
+ }, mLauncher, timeout);
}
/**
@@ -633,13 +618,13 @@
}
getInstrumentation().getTargetContext().startActivity(intent);
assertTrue("App didn't start: " + selector,
- TestHelpers.wait(Until.hasObject(selector), DEFAULT_UI_TIMEOUT));
+ TestHelpers.wait(Until.hasObject(selector), TestUtil.DEFAULT_UI_TIMEOUT));
// Wait for the Launcher to stop.
final LauncherInstrumentation launcherInstrumentation = new LauncherInstrumentation();
Wait.atMost("Launcher activity didn't stop",
() -> !launcherInstrumentation.isLauncherActivityStarted(),
- DEFAULT_ACTIVITY_TIMEOUT, launcherInstrumentation);
+ launcherInstrumentation);
}
public static ActivityInfo resolveSystemAppInfo(String category) {
@@ -662,8 +647,7 @@
launcher.finish();
}
});
- waitForLauncherCondition(
- "Launcher still active", launcher -> launcher == null, DEFAULT_UI_TIMEOUT);
+ waitForLauncherCondition("Launcher still active", launcher -> launcher == null);
}
protected boolean isInLaunchedApp(LAUNCHER_TYPE launcher) {
diff --git a/tests/src/com/android/launcher3/ui/widget/TaplAddConfigWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/TaplAddConfigWidgetTest.java
index 7ff4f22..7845222 100644
--- a/tests/src/com/android/launcher3/ui/widget/TaplAddConfigWidgetTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/TaplAddConfigWidgetTest.java
@@ -103,12 +103,12 @@
setResultAndWaitForAnimation(acceptConfig);
if (acceptConfig) {
- Wait.atMost("", new WidgetSearchCondition(), DEFAULT_ACTIVITY_TIMEOUT, mLauncher);
+ Wait.atMost("", new WidgetSearchCondition(), mLauncher);
assertNotNull(mAppWidgetManager.getAppWidgetInfo(mWidgetId));
} else {
// Verify that the widget id is deleted.
Wait.atMost("", () -> mAppWidgetManager.getAppWidgetInfo(mWidgetId) == null,
- DEFAULT_ACTIVITY_TIMEOUT, mLauncher);
+ mLauncher);
}
}
diff --git a/tests/src/com/android/launcher3/ui/widget/TaplAddWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/TaplAddWidgetTest.java
index 9a2147a..460ffc4 100644
--- a/tests/src/com/android/launcher3/ui/widget/TaplAddWidgetTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/TaplAddWidgetTest.java
@@ -29,6 +29,7 @@
import com.android.launcher3.ui.AbstractLauncherUiTest;
import com.android.launcher3.ui.PortraitLandscapeRunner.PortraitLandscape;
import com.android.launcher3.ui.TestViewHelpers;
+import com.android.launcher3.util.TestUtil;
import com.android.launcher3.util.rule.ShellCommandRule;
import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
@@ -68,7 +69,7 @@
resizeFrame.dismiss();
final Widget widget = mLauncher.getWorkspace().tryGetWidget(widgetInfo.label,
- DEFAULT_UI_TIMEOUT);
+ TestUtil.DEFAULT_UI_TIMEOUT);
assertNotNull("Widget not found on the workspace", widget);
widget.launch(getAppPackageName());
mLauncher.disableDebugTracing(); // b/289161193
diff --git a/tests/src/com/android/launcher3/ui/widget/TaplBindWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/TaplBindWidgetTest.java
index d40d3bc..4cdbd96 100644
--- a/tests/src/com/android/launcher3/ui/widget/TaplBindWidgetTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/TaplBindWidgetTest.java
@@ -53,6 +53,7 @@
import com.android.launcher3.tapl.Workspace;
import com.android.launcher3.ui.AbstractLauncherUiTest;
import com.android.launcher3.ui.TestViewHelpers;
+import com.android.launcher3.util.TestUtil;
import com.android.launcher3.util.rule.ShellCommandRule;
import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
import com.android.launcher3.widget.WidgetManagerHelper;
@@ -233,13 +234,15 @@
}
private void verifyWidgetPresent(LauncherAppWidgetProviderInfo info) {
- final Widget widget = mLauncher.getWorkspace().tryGetWidget(info.label, DEFAULT_UI_TIMEOUT);
+ final Widget widget = mLauncher.getWorkspace().tryGetWidget(info.label,
+ TestUtil.DEFAULT_UI_TIMEOUT);
assertTrue("Widget is not present",
widget != null);
}
private void verifyPendingWidgetPresent() {
- final Widget widget = mLauncher.getWorkspace().tryGetPendingWidget(DEFAULT_UI_TIMEOUT);
+ final Widget widget = mLauncher.getWorkspace().tryGetPendingWidget(
+ TestUtil.DEFAULT_UI_TIMEOUT);
assertTrue("Pending widget is not present",
widget != null);
}
diff --git a/tests/src/com/android/launcher3/ui/widget/TaplRequestPinItemTest.java b/tests/src/com/android/launcher3/ui/widget/TaplRequestPinItemTest.java
index 35c7cab..fe3b2ee 100644
--- a/tests/src/com/android/launcher3/ui/widget/TaplRequestPinItemTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/TaplRequestPinItemTest.java
@@ -169,8 +169,7 @@
// Go back to home
mLauncher.goHome();
- Wait.atMost("", new ItemSearchCondition(itemMatcher), DEFAULT_ACTIVITY_TIMEOUT,
- mLauncher);
+ Wait.atMost("", new ItemSearchCondition(itemMatcher), mLauncher);
}
/**
diff --git a/tests/src/com/android/launcher3/util/Wait.java b/tests/src/com/android/launcher3/util/Wait.java
deleted file mode 100644
index 50bc32e..0000000
--- a/tests/src/com/android/launcher3/util/Wait.java
+++ /dev/null
@@ -1,66 +0,0 @@
-package com.android.launcher3.util;
-
-import android.os.SystemClock;
-import android.util.Log;
-
-import com.android.launcher3.tapl.LauncherInstrumentation;
-
-import org.junit.Assert;
-
-import java.util.function.Supplier;
-
-/**
- * A utility class for waiting for a condition to be true.
- */
-public class Wait {
-
- private static final long DEFAULT_SLEEP_MS = 200;
-
- public static void atMost(String message, Condition condition, long timeout,
- LauncherInstrumentation launcher) {
- atMost(() -> message, condition, timeout, DEFAULT_SLEEP_MS, launcher);
- }
-
- public static void atMost(Supplier<String> message, Condition condition, long timeout,
- LauncherInstrumentation launcher) {
- atMost(message, condition, timeout, DEFAULT_SLEEP_MS, launcher);
- }
-
- public static void atMost(Supplier<String> message, Condition condition, long timeout,
- long sleepMillis,
- LauncherInstrumentation launcher) {
- final long startTime = SystemClock.uptimeMillis();
- long endTime = startTime + timeout;
- Log.d("Wait", "atMost: " + startTime + " - " + endTime);
- while (SystemClock.uptimeMillis() < endTime) {
- try {
- if (condition.isTrue()) {
- return;
- }
- } catch (Throwable t) {
- throw new RuntimeException(t);
- }
- SystemClock.sleep(sleepMillis);
- }
-
- // Check once more before returning false.
- try {
- if (condition.isTrue()) {
- return;
- }
- } catch (Throwable t) {
- throw new RuntimeException(t);
- }
- Log.d("Wait", "atMost: timed out: " + SystemClock.uptimeMillis());
- launcher.checkForAnomaly(false, false);
- Assert.fail(message.get());
- }
-
- /**
- * Interface representing a generic condition
- */
- public interface Condition {
-
- boolean isTrue() throws Throwable;
- }
-}
diff --git a/tests/src/com/android/launcher3/util/Wait.kt b/tests/src/com/android/launcher3/util/Wait.kt
new file mode 100644
index 0000000..1e5af54
--- /dev/null
+++ b/tests/src/com/android/launcher3/util/Wait.kt
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2024 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.os.SystemClock
+import android.util.Log
+import com.android.launcher3.tapl.LauncherInstrumentation
+import java.util.function.Supplier
+import org.junit.Assert
+
+/** A utility class for waiting for a condition to be true. */
+object Wait {
+ private const val DEFAULT_SLEEP_MS: Long = 200
+
+ @JvmStatic
+ @JvmOverloads
+ fun atMost(
+ message: String,
+ condition: Condition,
+ launcherInstrumentation: LauncherInstrumentation? = null,
+ timeout: Long = TestUtil.DEFAULT_UI_TIMEOUT,
+ ) {
+ atMost({ message }, condition, launcherInstrumentation, timeout)
+ }
+
+ @JvmStatic
+ @JvmOverloads
+ fun atMost(
+ message: Supplier<String>,
+ condition: Condition,
+ launcherInstrumentation: LauncherInstrumentation? = null,
+ timeout: Long = TestUtil.DEFAULT_UI_TIMEOUT,
+ ) {
+ val startTime = SystemClock.uptimeMillis()
+ val endTime = startTime + timeout
+ Log.d("Wait", "atMost: $startTime - $endTime")
+ while (SystemClock.uptimeMillis() < endTime) {
+ try {
+ if (condition.isTrue()) {
+ return
+ }
+ } catch (t: Throwable) {
+ throw RuntimeException(t)
+ }
+ SystemClock.sleep(DEFAULT_SLEEP_MS)
+ }
+
+ // Check once more before returning false.
+ try {
+ if (condition.isTrue()) {
+ return
+ }
+ } catch (t: Throwable) {
+ throw RuntimeException(t)
+ }
+ Log.d("Wait", "atMost: timed out: " + SystemClock.uptimeMillis())
+ launcherInstrumentation?.checkForAnomaly(false, false)
+ Assert.fail(message.get())
+ }
+
+ /** Interface representing a generic condition */
+ fun interface Condition {
+
+ @Throws(Throwable::class) fun isTrue(): Boolean
+ }
+}