callback) {
diff --git a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
index 50e8d0edc7..1e861d2fb5 100644
--- a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
@@ -52,6 +52,7 @@ import com.android.launcher3.util.OnboardingPrefs;
import com.android.quickstep.LauncherActivityInterface;
import com.android.quickstep.RecentsAnimationCallbacks;
import com.android.quickstep.util.GroupTask;
+import com.android.quickstep.util.TISBindHelper;
import com.android.quickstep.views.RecentsView;
import java.io.PrintWriter;
@@ -428,6 +429,12 @@ public class LauncherTaskbarUIController extends TaskbarUIController {
mTaskbarLauncherStateController.resetIconAlignment();
}
+ @Nullable
+ @Override
+ protected TISBindHelper getTISBindHelper() {
+ return mLauncher.getTISBindHelper();
+ }
+
@Override
public void dumpLogs(String prefix, PrintWriter pw) {
super.dumpLogs(prefix, pw);
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
index 9006df8a1a..d12e187fd6 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
@@ -505,52 +505,26 @@ public class TaskbarActivityContext extends BaseTaskbarContext {
/**
* Creates {@link WindowManager.LayoutParams} for Taskbar, and also sets LP.paramsForRotation
- * for taskbar showing as navigation bar
+ * for taskbar
*/
private WindowManager.LayoutParams createAllWindowParams() {
final int windowType =
ENABLE_TASKBAR_NAVBAR_UNIFICATION ? TYPE_NAVIGATION_BAR : TYPE_NAVIGATION_BAR_PANEL;
WindowManager.LayoutParams windowLayoutParams =
createDefaultWindowLayoutParams(windowType, TaskbarActivityContext.WINDOW_TITLE);
- if (!isPhoneButtonNavMode()) {
- return windowLayoutParams;
- }
- // Provide WM layout params for all rotations to cache, see NavigationBar#getBarLayoutParams
- int width = WindowManager.LayoutParams.MATCH_PARENT;
- int height = WindowManager.LayoutParams.MATCH_PARENT;
- int gravity = Gravity.BOTTOM;
windowLayoutParams.paramsForRotation = new WindowManager.LayoutParams[4];
for (int rot = Surface.ROTATION_0; rot <= Surface.ROTATION_270; rot++) {
WindowManager.LayoutParams lp =
createDefaultWindowLayoutParams(windowType,
TaskbarActivityContext.WINDOW_TITLE);
- switch (rot) {
- case Surface.ROTATION_0, Surface.ROTATION_180 -> {
- // Defaults are fine
- width = WindowManager.LayoutParams.MATCH_PARENT;
- height = mLastRequestedNonFullscreenSize;
- gravity = Gravity.BOTTOM;
- }
- case Surface.ROTATION_90 -> {
- width = mLastRequestedNonFullscreenSize;
- height = WindowManager.LayoutParams.MATCH_PARENT;
- gravity = Gravity.END;
- }
- case Surface.ROTATION_270 -> {
- width = mLastRequestedNonFullscreenSize;
- height = WindowManager.LayoutParams.MATCH_PARENT;
- gravity = Gravity.START;
- }
-
+ if (isPhoneButtonNavMode()) {
+ populatePhoneButtonNavModeWindowLayoutParams(rot, lp);
}
- lp.width = width;
- lp.height = height;
- lp.gravity = gravity;
windowLayoutParams.paramsForRotation[rot] = lp;
}
- // Override current layout params
+ // Override with current layout params
WindowManager.LayoutParams currentParams =
windowLayoutParams.paramsForRotation[getDisplay().getRotation()];
windowLayoutParams.width = currentParams.width;
@@ -560,6 +534,32 @@ public class TaskbarActivityContext extends BaseTaskbarContext {
return windowLayoutParams;
}
+ /**
+ * Update {@link WindowManager.LayoutParams} with values specific to phone and 3 button
+ * navigation users
+ */
+ private void populatePhoneButtonNavModeWindowLayoutParams(int rot,
+ WindowManager.LayoutParams lp) {
+ lp.width = WindowManager.LayoutParams.MATCH_PARENT;
+ lp.height = WindowManager.LayoutParams.MATCH_PARENT;
+ lp.gravity = Gravity.BOTTOM;
+
+ // Override with per-rotation specific values
+ switch (rot) {
+ case Surface.ROTATION_0, Surface.ROTATION_180 -> {
+ lp.height = mLastRequestedNonFullscreenSize;
+ }
+ case Surface.ROTATION_90 -> {
+ lp.width = mLastRequestedNonFullscreenSize;
+ lp.gravity = Gravity.END;
+ }
+ case Surface.ROTATION_270 -> {
+ lp.width = mLastRequestedNonFullscreenSize;
+ lp.gravity = Gravity.START;
+ }
+ }
+ }
+
public void onConfigurationChanged(@Config int configChanges) {
mControllers.onConfigurationChanged(configChanges);
if (!mIsUserSetupComplete) {
@@ -920,8 +920,10 @@ public class TaskbarActivityContext extends BaseTaskbarContext {
}
if (landscapePhoneButtonNav) {
mWindowLayoutParams.width = size;
+ mWindowLayoutParams.paramsForRotation[getDisplay().getRotation()].width = size;
} else {
mWindowLayoutParams.height = size;
+ mWindowLayoutParams.paramsForRotation[getDisplay().getRotation()].height = size;
}
mControllers.taskbarInsetsController.onTaskbarOrBubblebarWindowHeightOrInsetsChanged();
notifyUpdateLayoutParams();
@@ -1485,6 +1487,10 @@ public class TaskbarActivityContext extends BaseTaskbarContext {
btv.post(() -> mControllers.taskbarPopupController.showForIcon(btv));
}
+ public void launchKeyboardFocusedTask() {
+ mControllers.uiController.launchKeyboardFocusedTask();
+ }
+
public boolean isInApp() {
return mControllers.taskbarStashController.isInApp();
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt
index aa457ca4f1..567fad02ac 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt
@@ -118,11 +118,9 @@ class TaskbarInsetsController(val context: TaskbarActivityContext) : LoggableTas
getProvidedInsets(insetsRoundedCornerFlag)
}
- if (!context.isGestureNav) {
- if (windowLayoutParams.paramsForRotation != null) {
- for (layoutParams in windowLayoutParams.paramsForRotation) {
- layoutParams.providedInsets = getProvidedInsets(insetsRoundedCornerFlag)
- }
+ if (windowLayoutParams.paramsForRotation != null) {
+ for (layoutParams in windowLayoutParams.paramsForRotation) {
+ layoutParams.providedInsets = getProvidedInsets(insetsRoundedCornerFlag)
}
}
@@ -156,19 +154,12 @@ class TaskbarInsetsController(val context: TaskbarActivityContext) : LoggableTas
)
}
- val gravity = windowLayoutParams.gravity
-
// Pre-calculate insets for different providers across different rotations for this gravity
for (rotation in Surface.ROTATION_0..Surface.ROTATION_270) {
// Add insets for navbar rotated params
- if (windowLayoutParams.paramsForRotation != null) {
- val layoutParams = windowLayoutParams.paramsForRotation[rotation]
- for (provider in layoutParams.providedInsets) {
- setProviderInsets(provider, layoutParams.gravity, rotation)
- }
- }
- for (provider in windowLayoutParams.providedInsets) {
- setProviderInsets(provider, gravity, rotation)
+ val layoutParams = windowLayoutParams.paramsForRotation[rotation]
+ for (provider in layoutParams.providedInsets) {
+ setProviderInsets(provider, layoutParams.gravity, rotation)
}
}
context.notifyUpdateLayoutParams()
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
index 8db343fa3f..c4be85fcb8 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
@@ -549,7 +549,8 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba
createAnimToIsStashed(
/* isStashed= */ false,
placeholderDuration,
- TRANSITION_UNSTASH_SUW_MANUAL);
+ TRANSITION_UNSTASH_SUW_MANUAL,
+ /* jankTag= */ "SUW_MANUAL");
animation.addListener(AnimatorListeners.forEndCallback(
() -> mControllers.taskbarViewController.setDeferUpdatesForSUW(false)));
animation.play(mAnimator);
@@ -560,9 +561,10 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba
* @param isStashed whether it's a stash animation or an unstash animation
* @param duration duration of the animation
* @param animationType what transition type to play.
+ * @param jankTag tag to be used in jank monitor trace.
*/
private void createAnimToIsStashed(boolean isStashed, long duration,
- @StashAnimation int animationType) {
+ @StashAnimation int animationType, String jankTag) {
if (animationType == TRANSITION_UNSTASH_SUW_MANUAL && isStashed) {
// The STASH_ANIMATION_SUW_MANUAL must only be used during an unstash animation.
Log.e(TAG, "Illegal arguments:Using TRANSITION_UNSTASH_SUW_MANUAL to stash taskbar");
@@ -573,7 +575,7 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba
}
mAnimator = new AnimatorSet();
addJankMonitorListener(
- mAnimator, /* expanding= */ !mIsStashed, /* animationType= */ animationType);
+ mAnimator, /* expanding= */ !isStashed, /* tag= */ jankTag);
boolean isTransientTaskbar = DisplayController.isTransientTaskbar(mActivity);
final float stashTranslation = mActivity.isPhoneMode() || isTransientTaskbar
? 0
@@ -800,7 +802,7 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba
}
private void addJankMonitorListener(
- AnimatorSet animator, boolean expanding, @StashAnimation int animationType) {
+ AnimatorSet animator, boolean expanding, String tag) {
View v = mControllers.taskbarActivityContext.getDragLayer();
if (!v.isAttachedToWindow()) {
// If the task bar drag layer is not attached to window, we don't need to monitor jank
@@ -814,8 +816,8 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba
public void onAnimationStart(@NonNull Animator animation) {
final Configuration.Builder builder =
Configuration.Builder.withView(action, v);
- if (animationType == TRANSITION_HOME_TO_APP) {
- builder.setTag("HOME_TO_APP");
+ if (tag != null) {
+ builder.setTag(tag);
}
InteractionJankMonitor.getInstance().begin(builder);
}
@@ -1215,12 +1217,34 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba
mLastStartedTransitionType = animationType;
// This sets mAnimator.
- createAnimToIsStashed(mIsStashed, duration, animationType);
+ createAnimToIsStashed(mIsStashed, duration, animationType,
+ computeTaskbarJankMonitorTag(changedFlags));
return mAnimator;
}
return null;
}
+ /** Calculates the tag for CUJ_TASKBAR_EXPAND and CUJ_TASKBAR_COLLAPSE jank traces.*/
+ private String computeTaskbarJankMonitorTag(int changedFlags) {
+ if (hasAnyFlag(changedFlags, FLAG_IN_APP)) {
+ // moving in or out of the app
+ if (hasAnyFlag(FLAG_IN_APP)) {
+ return "Home to App";
+ } else {
+ return "App to Home";
+ }
+ }
+ if (hasAnyFlag(changedFlags, FLAG_STASHED_IN_APP_AUTO)) {
+ // stash and unstash with-in the app
+ if (hasAnyFlag(FLAG_STASHED_IN_APP_AUTO)) {
+ return "Stashed in app";
+ } else {
+ return "Manually unstashed";
+ }
+ }
+ return "";
+ }
+
private @StashAnimation int computeTransitionType(int changedFlags) {
boolean hotseatHiddenDuringAppLaunch =
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
index af1bd2a19f..efe1e39f97 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
@@ -20,6 +20,7 @@ import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT;
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION;
import static com.android.launcher3.taskbar.TaskbarStashController.FLAG_IN_APP;
+import static com.android.quickstep.OverviewCommandHelper.TYPE_HIDE;
import android.content.Intent;
import android.graphics.drawable.BitmapDrawable;
@@ -38,7 +39,9 @@ import com.android.launcher3.model.data.ItemInfoWithIcon;
import com.android.launcher3.popup.SystemShortcut;
import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.SplitConfigurationOptions;
+import com.android.quickstep.OverviewCommandHelper;
import com.android.quickstep.util.GroupTask;
+import com.android.quickstep.util.TISBindHelper;
import com.android.quickstep.views.RecentsView;
import com.android.quickstep.views.TaskView;
import com.android.quickstep.views.TaskView.TaskIdAttributeContainer;
@@ -361,6 +364,28 @@ public class TaskbarUIController {
/** Adjusts the hotseat for the bubble bar. */
public void adjustHotseatForBubbleBar(boolean isBubbleBarVisible) {}
+ @Nullable
+ protected TISBindHelper getTISBindHelper() {
+ return null;
+ }
+
+ /**
+ * Launches the focused task in the Keyboard Quick Switch view through the OverviewCommandHelper
+ *
+ * Use this helper method when the focused task may be the overview task.
+ */
+ public void launchKeyboardFocusedTask() {
+ TISBindHelper tisBindHelper = getTISBindHelper();
+ if (tisBindHelper == null) {
+ return;
+ }
+ OverviewCommandHelper overviewCommandHelper = tisBindHelper.getOverviewCommandHelper();
+ if (overviewCommandHelper == null) {
+ return;
+ }
+ overviewCommandHelper.addCommand(TYPE_HIDE);
+ }
+
/**
* Adjusts the taskbar based on the visibility of the launcher.
* @param isVisible True if launcher is visible, false otherwise.
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
index 722676a19c..93ee76e0eb 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
@@ -245,6 +245,8 @@ public class QuickstepLauncher extends Launcher {
private boolean mEnableWidgetDepth;
+ private boolean mIsPredictiveBackToHomeInProgress;
+
private HomeTransitionController mHomeTransitionController;
@Override
@@ -514,6 +516,7 @@ public class QuickstepLauncher extends Launcher {
mAppTransitionManager.onActivityDestroyed();
}
mAppTransitionManager = null;
+ mIsPredictiveBackToHomeInProgress = false;
if (mUnfoldTransitionProgressProvider != null) {
SystemUiProxy.INSTANCE.get(this).setUnfoldAnimationListener(null);
@@ -974,6 +977,7 @@ public class QuickstepLauncher extends Launcher {
if (taskbarManager != null) {
taskbarManager.setActivity(this);
}
+ mTISBindHelper.setPredictiveBackToHomeInProgress(mIsPredictiveBackToHomeInProgress);
}
@Override
@@ -1278,6 +1282,14 @@ public class QuickstepLauncher extends Launcher {
mPendingSplitSelectInfo = null;
}
+ /**
+ * Sets flag whether a predictive back-to-home animation is in progress
+ */
+ public void setPredictiveBackToHomeInProgress(boolean isInProgress) {
+ mIsPredictiveBackToHomeInProgress = isInProgress;
+ mTISBindHelper.setPredictiveBackToHomeInProgress(isInProgress);
+ }
+
@Override
public boolean areFreeformTasksVisible() {
if (mDesktopVisibilityController != null) {
@@ -1348,6 +1360,11 @@ public class QuickstepLauncher extends Launcher {
return (mTaskbarUIController != null && mTaskbarUIController.hasBubbles());
}
+ @NonNull
+ public TISBindHelper getTISBindHelper() {
+ return mTISBindHelper;
+ }
+
@Override
public boolean handleIncorrectSplitTargetSelection() {
if (!enableSplitContextually() || !mSplitSelectStateController.isSplitSelectActive()) {
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepWidgetHolder.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepWidgetHolder.java
index 0fb2b17eef..e3ff281e4d 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepWidgetHolder.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepWidgetHolder.java
@@ -219,6 +219,20 @@ public final class QuickstepWidgetHolder extends LauncherWidgetHolder {
return () -> holderListener.mListeningHolders.remove(handler);
}
+ /**
+ * Recycling logic:
+ * The holder doesn't maintain any states associated with the view, so if the view was
+ * initially initialized by this holder, all its state are already set in the view. We just
+ * update the RemoteViews for this view again, in case the widget sent an update during the
+ * time between inflation and recycle.
+ */
+ @Override
+ protected LauncherAppWidgetHostView recycleExistingView(LauncherAppWidgetHostView view) {
+ RemoteViews views = getHolderListener(view.getAppWidgetId()).addHolder(mUpdateHandler);
+ view.updateAppWidget(views);
+ return view;
+ }
+
@NonNull
@Override
protected LauncherAppWidgetHostView createViewInternal(
diff --git a/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java b/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java
index 5772450b62..cc5a923e5a 100644
--- a/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java
+++ b/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java
@@ -52,6 +52,7 @@ import android.window.BackMotionEvent;
import android.window.BackProgressAnimator;
import android.window.IOnBackInvokedCallback;
+import com.android.app.animation.Interpolators;
import com.android.internal.view.AppearanceRegion;
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.BubbleTextView;
@@ -103,7 +104,8 @@ public class LauncherBackAnimationController {
private float mWindowScaleEndCornerRadius;
private float mWindowScaleStartCornerRadius;
private final Interpolator mCancelInterpolator;
- private final Interpolator mProgressInterpolator = new DecelerateInterpolator();
+ private final Interpolator mProgressInterpolator = Interpolators.STANDARD_DECELERATE;
+ private final Interpolator mVerticalMoveInterpolator = new DecelerateInterpolator();
private final PointF mInitialTouchPos = new PointF();
private RemoteAnimationTarget mBackTarget;
@@ -376,7 +378,7 @@ public class LauncherBackAnimationController {
float yDirection = rawYDelta < 0 ? -1 : 1;
// limit yDelta interpretation to 1/2 of screen height in either direction
float deltaYRatio = Math.min(screenHeight / 2f, Math.abs(rawYDelta)) / (screenHeight / 2f);
- float interpolatedYRatio = mProgressInterpolator.getInterpolation(deltaYRatio);
+ float interpolatedYRatio = mVerticalMoveInterpolator.getInterpolation(deltaYRatio);
// limit y-shift so surface never passes 8dp screen margin
float deltaY = yDirection * interpolatedYRatio * Math.max(0f, (screenHeight - height)
/ 2f - mWindowScaleMarginX);
@@ -437,6 +439,7 @@ public class LauncherBackAnimationController {
if (mLauncher.isDestroyed()) {
return;
}
+ mLauncher.setPredictiveBackToHomeInProgress(true);
LauncherTaskbarUIController taskbarUIController = mLauncher.getTaskbarUIController();
if (taskbarUIController != null) {
taskbarUIController.onLauncherVisibilityChanged(true);
@@ -475,6 +478,7 @@ public class LauncherBackAnimationController {
}
private void finishAnimation() {
+ mLauncher.setPredictiveBackToHomeInProgress(false);
mBackTarget = null;
mLauncherTarget = null;
mBackInProgress = false;
diff --git a/quickstep/src/com/android/quickstep/OverviewCommandHelper.java b/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
index febfc3ad73..65b5397fd1 100644
--- a/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
+++ b/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
@@ -209,27 +209,38 @@ public class OverviewCommandHelper {
&& dp != null
&& (dp.isTablet || dp.isTwoPanels);
- if (cmd.type == TYPE_HIDE) {
- if (!allowQuickSwitch) {
+ switch (cmd.type) {
+ case TYPE_HIDE:
+ if (!allowQuickSwitch) {
+ return true;
+ }
+ mKeyboardTaskFocusIndex = uiController.launchFocusedTask();
+ if (mKeyboardTaskFocusIndex == -1) {
+ return true;
+ }
+ break;
+ case TYPE_KEYBOARD_INPUT:
+ if (allowQuickSwitch) {
+ uiController.openQuickSwitchView();
+ return true;
+ } else {
+ mKeyboardTaskFocusIndex = 0;
+ break;
+ }
+ case TYPE_HOME:
+ ActiveGestureLog.INSTANCE.addLog(
+ "OverviewCommandHelper.executeCommand(TYPE_HOME)");
+ mService.startActivity(mOverviewComponentObserver.getHomeIntent());
return true;
- }
- mKeyboardTaskFocusIndex = uiController.launchFocusedTask();
- if (mKeyboardTaskFocusIndex == -1) {
- return true;
- }
- }
- if (cmd.type == TYPE_KEYBOARD_INPUT) {
- if (allowQuickSwitch) {
- uiController.openQuickSwitchView();
- return true;
- } else {
+ case TYPE_SHOW:
+ // When Recents is not currently visible, the command's type is TYPE_SHOW
+ // when overview is triggered via the keyboard overview button or Action+Tab
+ // keys (Not Alt+Tab which is KQS). The overview button on-screen in 3-button
+ // nav is TYPE_TOGGLE.
mKeyboardTaskFocusIndex = 0;
- }
- }
- if (cmd.type == TYPE_HOME) {
- ActiveGestureLog.INSTANCE.addLog("OverviewCommandHelper.executeCommand(TYPE_HOME)");
- mService.startActivity(mOverviewComponentObserver.getHomeIntent());
- return true;
+ break;
+ default:
+ // continue below to handle displaying Recents.
}
} else {
createdRecentsView = visibleRecentsView;
@@ -351,7 +362,8 @@ public class OverviewCommandHelper {
private void updateRecentsViewFocus(CommandInfo cmd) {
RecentsView recentsView =
mOverviewComponentObserver.getActivityInterface().getVisibleRecentsView();
- if (recentsView == null || (cmd.type != TYPE_KEYBOARD_INPUT && cmd.type != TYPE_HIDE)) {
+ if (recentsView == null || (cmd.type != TYPE_KEYBOARD_INPUT && cmd.type != TYPE_HIDE
+ && cmd.type != TYPE_SHOW)) {
return;
}
// When the overview is launched via alt tab (cmd type is TYPE_KEYBOARD_INPUT),
diff --git a/quickstep/src/com/android/quickstep/RecentsActivity.java b/quickstep/src/com/android/quickstep/RecentsActivity.java
index 22163b9614..02f9a6962e 100644
--- a/quickstep/src/com/android/quickstep/RecentsActivity.java
+++ b/quickstep/src/com/android/quickstep/RecentsActivity.java
@@ -498,4 +498,9 @@ public final class RecentsActivity extends StatefulActivity {
OverviewCommandHelper overviewCommandHelper = mTISBindHelper.getOverviewCommandHelper();
return overviewCommandHelper == null || overviewCommandHelper.canStartHomeSafely();
}
+
+ @NonNull
+ public TISBindHelper getTISBindHelper() {
+ return mTISBindHelper;
+ }
}
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
index cb0aa8f69f..c56a6219b2 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
@@ -119,6 +119,7 @@ public class RecentsAnimationDeviceState implements DisplayInfoChangeListener, E
private boolean mIsSwipeToNotificationEnabled;
private final boolean mIsOneHandedModeSupported;
private boolean mPipIsActive;
+ private boolean mIsPredictiveBackToHomeInProgress;
private int mGestureBlockingTaskId = -1;
private @NonNull Region mExclusionRegion = GestureExclusionManager.EMPTY_REGION;
@@ -361,6 +362,20 @@ public class RecentsAnimationDeviceState implements DisplayInfoChangeListener, E
return mSystemUiStateFlags;
}
+ /**
+ * Sets the flag that indicates whether a predictive back-to-home animation is in progress
+ */
+ public void setPredictiveBackToHomeInProgress(boolean isInProgress) {
+ mIsPredictiveBackToHomeInProgress = isInProgress;
+ }
+
+ /**
+ * @return whether a predictive back-to-home animation is currently in progress
+ */
+ public boolean isPredictiveBackToHomeInProgress() {
+ return mIsPredictiveBackToHomeInProgress;
+ }
+
/**
* @return whether SystemUI is in a state where we can start a system gesture.
*/
@@ -609,6 +624,7 @@ public class RecentsAnimationDeviceState implements DisplayInfoChangeListener, E
pw.println(" deferredGestureRegion=" + mDeferredGestureRegion.getBounds());
pw.println(" exclusionRegion=" + mExclusionRegion.getBounds());
pw.println(" pipIsActive=" + mPipIsActive);
+ pw.println(" predictiveBackToHomeInProgress=" + mIsPredictiveBackToHomeInProgress);
mRotationTouchHelper.dump(pw);
}
}
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index f9486bd6a8..415f73f1aa 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -397,6 +397,14 @@ public class TouchInteractionService extends Service {
return tis.mTaskbarManager;
}
+ /**
+ * Sets whether a predictive back-to-home animation is in progress in the device state
+ */
+ public void setPredictiveBackToHomeInProgress(boolean isInProgress) {
+ executeForTouchInteractionService(tis ->
+ tis.mDeviceState.setPredictiveBackToHomeInProgress(isInProgress));
+ }
+
/**
* Returns the {@link OverviewCommandHelper}.
*
@@ -1170,7 +1178,8 @@ public class TouchInteractionService extends Service {
}
boolean previousGestureAnimatedToLauncher =
- previousGestureState.isRunningAnimationToLauncher();
+ previousGestureState.isRunningAnimationToLauncher()
+ || mDeviceState.isPredictiveBackToHomeInProgress();
// with shell-transitions, home is resumed during recents animation, so
// explicitly check against recents animation too.
boolean launcherResumedThroughShellTransition =
@@ -1275,7 +1284,8 @@ public class TouchInteractionService extends Service {
boolean hasWindowFocus = activity.getRootView().hasWindowFocus();
boolean isPreviousGestureAnimatingToLauncher =
- previousGestureState.isRunningAnimationToLauncher();
+ previousGestureState.isRunningAnimationToLauncher()
+ || mDeviceState.isPredictiveBackToHomeInProgress();
boolean isInLiveTileMode = gestureState.getActivityInterface().isInLiveTileMode();
reasonString.append(SUBSTRING_PREFIX)
.append(hasWindowFocus
diff --git a/quickstep/src/com/android/quickstep/util/AppPairsController.java b/quickstep/src/com/android/quickstep/util/AppPairsController.java
index b8463230fc..ce8df9bdff 100644
--- a/quickstep/src/com/android/quickstep/util/AppPairsController.java
+++ b/quickstep/src/com/android/quickstep/util/AppPairsController.java
@@ -20,6 +20,7 @@ package com.android.quickstep.util;
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_APP_PAIR_LAUNCH;
+import static com.android.launcher3.model.data.AppInfo.PACKAGE_KEY_COMPARATOR;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT;
@@ -30,6 +31,7 @@ import static com.android.wm.shell.common.split.SplitScreenConstants.isPersisten
import android.content.Context;
import android.content.Intent;
+import android.content.pm.PackageManager;
import android.content.pm.LauncherApps;
import android.util.Log;
import android.util.Pair;
@@ -42,10 +44,12 @@ import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.R;
import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
+import com.android.launcher3.allapps.AllAppsStore;
import com.android.launcher3.apppairs.AppPairIcon;
import com.android.launcher3.icons.IconCache;
import com.android.launcher3.logging.InstanceId;
import com.android.launcher3.logging.StatsLogManager;
+import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.model.data.FolderInfo;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
@@ -93,14 +97,38 @@ public class AppPairsController {
}
/**
- * Creates a new app pair ItemInfo and adds it to the workspace
+ * Creates a new app pair ItemInfo and adds it to the workspace.
+ *
+ * We create WorkspaceItemInfos to save onto the app pair in the following way:
+ *
1. We verify that the ComponentKey from our Recents tile corresponds to a real
+ * launchable app in the app store.
+ *
2. If it doesn't, we search for the underlying launchable app via package name, and use
+ * that instead.
+ *
3. If that fails, we re-use the existing WorkspaceItemInfo by cloning it and replacing
+ * its intent with one from PackageManager.
+ *
4. If everything fails, we just use the WorkspaceItemInfo as is, with its existing
+ * intent. This is not preferred, but will still work in most cases (notably it will not work
+ * well on trampoline apps).
*/
public void saveAppPair(GroupedTaskView gtv) {
TaskView.TaskIdAttributeContainer[] attributes = gtv.getTaskIdAttributeContainers();
- WorkspaceItemInfo app1 = attributes[0].getItemInfo().clone();
- WorkspaceItemInfo app2 = attributes[1].getItemInfo().clone();
- app1.itemType = LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
- app2.itemType = LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
+ WorkspaceItemInfo recentsInfo1 = attributes[0].getItemInfo();
+ WorkspaceItemInfo recentsInfo2 = attributes[0].getItemInfo();
+ WorkspaceItemInfo app1 = lookupLaunchableItem(recentsInfo1.getComponentKey());
+ WorkspaceItemInfo app2 = lookupLaunchableItem(recentsInfo2.getComponentKey());
+
+ // If app lookup fails, use the WorkspaceItemInfo that we have, but try to override default
+ // intent with one from PackageManager.
+ if (app1 == null) {
+ Log.w(TAG, "Creating an app pair, but app lookup for " + recentsInfo1.title
+ + " failed. Falling back to the WorkspaceItemInfo from Recents.");
+ app1 = convertRecentsItemToAppItem(recentsInfo1);
+ }
+ if (app2 == null) {
+ Log.w(TAG, "Creating an app pair, but app lookup for " + recentsInfo2.title
+ + " failed. Falling back to the WorkspaceItemInfo from Recents.");
+ app2 = convertRecentsItemToAppItem(recentsInfo2);
+ }
@PersistentSnapPosition int snapPosition = gtv.getSnapPosition();
if (!isPersistentSnapPosition(snapPosition)) {
@@ -188,6 +216,52 @@ public class AppPairsController {
);
}
+ /**
+ * Creates a new launchable WorkspaceItemInfo of itemType=ITEM_TYPE_APPLICATION by looking the
+ * ComponentKey up in the AllAppsStore. If no app is found, attempts a lookup by package
+ * instead. If that lookup fails, returns null.
+ */
+ @Nullable
+ private WorkspaceItemInfo lookupLaunchableItem(@Nullable ComponentKey key) {
+ if (key == null) {
+ return null;
+ }
+
+ AllAppsStore appsStore = Launcher.getLauncher(mContext).getAppsView().getAppsStore();
+
+ // Lookup by ComponentKey
+ AppInfo appInfo = appsStore.getApp(key);
+ if (appInfo == null) {
+ // Lookup by package
+ appInfo = appsStore.getApp(key, PACKAGE_KEY_COMPARATOR);
+ }
+
+ return appInfo != null ? appInfo.makeWorkspaceItem(mContext) : null;
+ }
+
+ /**
+ * Converts a WorkspaceItemInfo of itemType=ITEM_TYPE_TASK (from a Recents task) to a new
+ * WorkspaceItemInfo of itemType=ITEM_TYPE_APPLICATION.
+ */
+ private WorkspaceItemInfo convertRecentsItemToAppItem(WorkspaceItemInfo recentsItem) {
+ if (recentsItem.itemType != LauncherSettings.Favorites.ITEM_TYPE_TASK) {
+ Log.w(TAG, "Expected ItemInfo of type ITEM_TYPE_TASK, but received "
+ + recentsItem.itemType);
+ }
+
+ WorkspaceItemInfo launchableItem = recentsItem.clone();
+ PackageManager p = mContext.getPackageManager();
+ Intent launchIntent = p.getLaunchIntentForPackage(recentsItem.getTargetPackage());
+ Log.w(TAG, "Initial intent from Recents: " + launchableItem.intent + "\n"
+ + "Intent from PackageManager: " + launchIntent);
+ if (launchIntent != null) {
+ // If lookup from PackageManager fails, just use the existing intent
+ launchableItem.intent = launchIntent;
+ }
+ launchableItem.itemType = LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
+ return launchableItem;
+ }
+
/**
* Handles the complicated logic for how to animate an app pair entrance when already inside an
* app or app pair.
diff --git a/quickstep/src/com/android/quickstep/util/TISBindHelper.java b/quickstep/src/com/android/quickstep/util/TISBindHelper.java
index ddc796fc33..9a010429d3 100644
--- a/quickstep/src/com/android/quickstep/util/TISBindHelper.java
+++ b/quickstep/src/com/android/quickstep/util/TISBindHelper.java
@@ -108,6 +108,15 @@ public class TISBindHelper implements ServiceConnection {
return mBinder == null ? null : mBinder.getTaskbarManager();
}
+ /**
+ * Sets flag whether a predictive back-to-home animation is in progress
+ */
+ public void setPredictiveBackToHomeInProgress(boolean isInProgress) {
+ if (mBinder != null) {
+ mBinder.setPredictiveBackToHomeInProgress(isInProgress);
+ }
+ }
+
@Nullable
public OverviewCommandHelper getOverviewCommandHelper() {
return mBinder == null ? null : mBinder.getOverviewCommandHelper();
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index 99c42a7682..7bfa5eddd1 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -2093,22 +2093,20 @@ public abstract class RecentsView= 0; i--) {
TaskView child = requireTaskViewAt(i);
int[] childTaskIds = child.getTaskIds();
- if (!mRunningTaskTileHidden ||
- (childTaskIds[0] != runningTaskId && childTaskIds[1] != runningTaskId)) {
- child.setStableAlpha(alpha);
+ if (runningTaskId != INVALID_TASK_ID
+ && mRunningTaskTileHidden
+ && (childTaskIds[0] == runningTaskId || childTaskIds[1] == runningTaskId)) {
+ continue;
}
+ child.setStableAlpha(alpha);
}
mClearAllButton.setContentAlpha(mContentAlpha);
int alphaInt = Math.round(alpha * 255);
@@ -4407,9 +4406,6 @@ public abstract class RecentsView LauncherState.NORMAL));
}
+ @Test
+ public void testOpenOverviewWithActionPlusTabKeys() throws Exception {
+ startTestAppsWithCheck();
+ startAppFast(CALCULATOR_APP_PACKAGE); // Ensure Calculator is last opened app.
+ Workspace home = mLauncher.goHome();
+ assertTrue("Launcher state is not Home", isInState(() -> LauncherState.NORMAL));
+
+ Overview overview = home.openOverviewFromActionPlusTabKeyboardShortcut();
+
+ assertTrue("Launcher state is not Overview", isInState(() -> LauncherState.OVERVIEW));
+ overview.launchFocusedTaskByEnterKey(CALCULATOR_APP_PACKAGE); // Assert app is focused.
+ }
+
+ @Test
+ public void testOpenOverviewWithRecentsKey() throws Exception {
+ startTestAppsWithCheck();
+ startAppFast(CALCULATOR_APP_PACKAGE); // Ensure Calculator is last opened app.
+ Workspace home = mLauncher.goHome();
+ assertTrue("Launcher state is not Home", isInState(() -> LauncherState.NORMAL));
+
+ Overview overview = home.openOverviewFromRecentsKeyboardShortcut();
+
+ assertTrue("Launcher state is not Overview", isInState(() -> LauncherState.OVERVIEW));
+ overview.launchFocusedTaskByEnterKey(CALCULATOR_APP_PACKAGE); // Assert app is focused.
+ }
+
private int getCurrentOverviewPage(Launcher launcher) {
return launcher.getOverviewPanel().getCurrentPage();
}
diff --git a/res/values-af/strings.xml b/res/values-af/strings.xml
index 835c201700..8dd419cf57 100644
--- a/res/values-af/strings.xml
+++ b/res/values-af/strings.xml
@@ -40,14 +40,14 @@
"Voeg by tuisskerm"
"%1$s-legstuk by tuisskerm gevoeg"
"Voorstelle"
- "Gee jou dag \'n hupstoot"
+
+
"Nuus vir jou"
"Jou ontspansone"
"Bereik jou fiksheiddoelwitte"
"Spring die weer voor"
"Jy hou dalk ook van"
-
-
+ "-legstukke aan die regterkant, soektog en opsies aan die linkerkant"
"{count,plural, =1{# legstuk}other{# legstukke}}"
"{count,plural, =1{# kortpad}other{# kortpaaie}}"
"%1$s, %2$s"
@@ -185,6 +185,8 @@
"Privaat"
"Privaat Ruimte-instellings"
"Sluit/ontsluit Privaat Ruimte"
+
+
"Privaat Ruimte-oorgang"
"Installeer apps"
"Installeer apps in privaat ruimte"
diff --git a/res/values-am/strings.xml b/res/values-am/strings.xml
index 98c41de7e4..e2834182dd 100644
--- a/res/values-am/strings.xml
+++ b/res/values-am/strings.xml
@@ -40,14 +40,14 @@
"ወደ መነሻ ማያ ገፅ አክል"
"%1$s ምግብር ወደ መነሻ ማያ ገፅ ታክሏል"
"የአስተያየት ጥቆማዎች"
- "ቀንዎን ያሳምሩ"
+
+
"ዜና ለእርስዎ"
"የሚያርፉበት ቦታዎ"
"የአካል ብቃት ግቦችዎን ያሳኩ"
"ለአየር ሁኔታው አስቀድመው ያቅዱ"
"ይህንንም ሊወዱት ይችላሉ"
-
-
+ " ምግብሮች በቀኝ በኩል፣ ፍለጋ እና አማራጮች በግራ በኩል"
"{count,plural, =1{# ምግብር}one{# ምግብሮች}other{# ምግብሮች}}"
"{count,plural, =1{# አቋራጭ}one{# አቋራጭ}other{# አቋራጮች}}"
"%1$s፣ %2$s"
@@ -185,6 +185,8 @@
"የግል"
"የግል ቦታ ቅንብሮች"
"የግል ቦታን ቆልፍ/ክፈት"
+
+
"የግል ቦታ ሽግግር"
"መተግበሪያዎችን ይጫኑ"
"መተግበሪያዎችን ወደ የግል ቦታ ይጫኑ"
diff --git a/res/values-ar/strings.xml b/res/values-ar/strings.xml
index 91e23376a0..86a9a8748a 100644
--- a/res/values-ar/strings.xml
+++ b/res/values-ar/strings.xml
@@ -40,14 +40,14 @@
"إضافة إلى الشاشة الرئيسية"
"تمت إضافة الأداة %1$s إلى الشاشة الرئيسية."
"اقتراحات"
- "تعزيز إنتاجية يومك"
+
+
"أخبار مقترَحة لك"
"محتوى ترفيهي مقترَح"
"تحقيق أهداف اللياقة البدنية"
"معرفة حالة الطقس أولاً بأول"
"محتوى قد يعجبك أيضًا"
-
-
+ "تطبيقات \"\" المصغّرة على اليسار، والبحث والخيارات على اليمين"
"{count,plural, =1{تطبيق مصغّر واحد}zero{# تطبيق مصغّر}two{تطبيقان مصغّران}few{# تطبيقات مصغّرة}many{# تطبيقًا مصغّرًا}other{# تطبيق مصغّر}}"
"{count,plural, =1{اختصار واحد}zero{# اختصار}two{اختصاران}few{# اختصارات}many{# اختصارًا}other{# اختصار}}"
"%1$s، %2$s"
@@ -185,6 +185,8 @@
"المساحة الخاصة"
"إعدادات المساحة الخاصة"
"قفل المساحة الخاصة أو فتح قفلها"
+
+
"النقل إلى المساحة الخاصة"
"تثبيت التطبيقات"
"تثبيت التطبيقات في المساحة الخاصّة"
diff --git a/res/values-as/strings.xml b/res/values-as/strings.xml
index 3fa40d5549..53de80b84a 100644
--- a/res/values-as/strings.xml
+++ b/res/values-as/strings.xml
@@ -40,14 +40,14 @@
"গৃহ স্ক্ৰীনত যোগ কৰক"
"%1$s ৱিজেটটো গৃহ স্ক্ৰীনত যোগ দিয়া হৈছে"
"পৰামৰ্শ"
- "আপোনাৰ দিনটো কাৰ্যকৰী কৰি তোলক"
+
+
"আপোনাৰ বাবে বাতৰি"
"আপোনাৰ পচন্দৰ স্থান"
"আপোনাৰ সুস্থতাৰ লক্ষ্যত উপনীত হওক"
"বতৰৰ বিষয়ে আগতীয়াকৈ জানক"
"আপুনি হয়তো এইটোও পচন্দ কৰিব"
-
-
+ " ৱিজেট সোঁফালে, সন্ধান আৰু বিকল্পসমূহ বাওঁফালে"
"{count,plural, =1{# টা ৱিজেট}one{# টা ৱিজেট}other{# টা ৱিজেট}}"
"{count,plural, =1{# টা শ্বৰ্টকাট}one{# টা শ্বৰ্টকাট}other{# টা শ্বৰ্টকাট}}"
"%1$s, %2$s"
@@ -185,6 +185,8 @@
"ব্যক্তিগত"
"ব্যক্তিগত স্পে’চৰ ছেটিং"
"ব্যক্তিগত স্পে’চ লক/আনলক কৰক"
+
+
"ব্যক্তিগত স্পে’চৰ স্থানান্তৰণ"
"এপ্ ইনষ্টল কৰক"
"এপ্সমূহ প্ৰাইভেট স্পেচত ইনষ্টল কৰক"
diff --git a/res/values-az/strings.xml b/res/values-az/strings.xml
index 2848606c61..3dce943cdb 100644
--- a/res/values-az/strings.xml
+++ b/res/values-az/strings.xml
@@ -40,14 +40,14 @@
"Əsas ekrana əlavə edin"
"%1$s vidceti əsas ekrana əlavə edildi"
"Təkliflər"
- "Günü canlandırın"
+
+
"Sizin üçün xəbərlər"
"İstirahət zonası"
"Fitnes hədəflərinə nail olun"
"Hava barədə məlumatlı olun"
"Tövsiyələr"
-
-
+ " vidcetləri sağda, axtarış və seçimlər solda"
"{count,plural, =1{# vidcet}other{# vidcet}}"
"{count,plural, =1{# qısayol}other{# qısayol}}"
"%1$s, %2$s"
@@ -185,6 +185,8 @@
"Şəxsi"
"Şəxsi məkan ayarları"
"Şəxsi məkanı kilidləyin/kiliddən çıxarın"
+
+
"Şəxsi məkana keçid"
"Tətbiqlər quraşdırın"
"Tətbiqləri şəxsi sahədə quraşdırın"
diff --git a/res/values-b+sr+Latn/strings.xml b/res/values-b+sr+Latn/strings.xml
index f75dd60682..ef21114a57 100644
--- a/res/values-b+sr+Latn/strings.xml
+++ b/res/values-b+sr+Latn/strings.xml
@@ -40,14 +40,14 @@
"Dodaj na početni ekran"
"Dodali ste vidžet %1$s na početni ekran"
"Predlozi"
- "Poboljšajte dan"
+
+
"Vesti za vas"
"Zona za opuštanje"
"Ostvarite fitnes ciljeve"
"Budite u toku sa vremenskim prilikama"
"Možda će vam se dopasti i"
-
-
+ "Vidžeti sa desne strane, pretraga i opcije sa leve strane"
"{count,plural, =1{# vidžet}one{# vidžet}few{# vidžeta}other{# vidžeta}}"
"{count,plural, =1{# prečica}one{# prečica}few{# prečice}other{# prečica}}"
"%1$s, %2$s"
@@ -185,6 +185,8 @@
"Privatno"
"Podešavanja privatnog prostora"
"Zaključaj/otključaj privatni prostor"
+
+
"Prenos privatnog prostora"
"Instalirajte aplikacije"
"Instaliraj aplikacije u privatan prostor"
diff --git a/res/values-be/strings.xml b/res/values-be/strings.xml
index 4b3780b50c..760cd2c40f 100644
--- a/res/values-be/strings.xml
+++ b/res/values-be/strings.xml
@@ -40,14 +40,14 @@
"Дадаць на галоўны экран"
"Віджэт \"%1$s\" дададзены на галоўны экран"
"Прапановы"
- "Прадукцыйны дзень"
+
+
"Навіны для вас"
"Зона адпачынку"
"Вашы фітнэс-мэты"
"Прагноз надвор\'я"
"Іншыя рэкамендацыі"
-
-
+ "Віджэты праграмы \"\" справа, пошук і параметры злева"
"{count,plural, =1{# віджэт}one{# віджэт}few{# віджэты}many{# віджэтаў}other{# віджэта}}"
"{count,plural, =1{# ярлык}one{# ярлык}few{# ярлыкі}many{# ярлыкоў}other{# ярлыка}}"
"%1$s, %2$s"
@@ -185,6 +185,8 @@
"Прыватная"
"Налады прыватнай вобласці"
"Заблакіраваць (разблакіраваць) прыватную вобласць"
+
+
"Пераход у прыватную вобласць"
"Усталяваць праграмы"
"Усталяваць праграмы ў прыватнай прасторы"
diff --git a/res/values-bg/strings.xml b/res/values-bg/strings.xml
index 40c9c6a370..9d69b65cae 100644
--- a/res/values-bg/strings.xml
+++ b/res/values-bg/strings.xml
@@ -40,14 +40,14 @@
"Добавяне към началния екран"
"Приспособлението %1$s е добавено към началния екран"
"Предложения"
- "Подобрете ежедневието си"
+
+
"Новини за вас"
"Зоната ви за разпускане"
"Постигнете фитнес целите си"
"Бъдете една крачка напред с прогнозата за времето"
"Може също да харесате"
-
-
+ "Приспособленията за са отдясно, търсенето и опциите – отляво"
"{count,plural, =1{# приспособление}other{# приспособления}}"
"{count,plural, =1{# пряк път}other{# преки пътя}}"
"%1$s, %2$s"
@@ -185,6 +185,8 @@
"Лично"
"Настройки за личното пространство"
"Заключване/отключване на личното пространство"
+
+
"Преминаване към личното пространство"
"Инсталиране на приложения"
"Инсталиране на приложения в частно пространство"
diff --git a/res/values-bn/strings.xml b/res/values-bn/strings.xml
index c34b64131a..2b0c6380ad 100644
--- a/res/values-bn/strings.xml
+++ b/res/values-bn/strings.xml
@@ -40,14 +40,14 @@
"হোম স্ক্রিনে যোগ করুন"
"%1$s উইজেট হোম স্ক্রিনে যোগ করা হয়েছে"
"সাজেশন"
- "আপনার দিন আরও ভালো করুন"
+
+
"আপনার জন্য খবর"
"আপনার চিল জোন"
"আপনার ফিটনেস সংক্রান্ত লক্ষ্যে পৌঁছান"
"আবহাওয়া সম্পর্কে আগেই খবর পান"
"আপনার এগুলিও পছন্দ হতে পারে"
-
-
+ " উইজেট ডানদিকে, সার্চ ও বিকল্প বাঁদিকে রয়েছে"
"{count,plural, =1{#টি উইজেট}one{#টি উইজেট}other{#টি উইজেট}}"
"{count,plural, =1{#টি শর্টকাট}one{#টি শর্টকাট}other{#টি শর্টকাট}}"
"%1$s, %2$s"
@@ -185,6 +185,8 @@
"ব্যক্তিগত"
"ব্যক্তিগত স্পেসের সেটিংস"
"ব্যক্তিগত স্পেস লক/আনলক করুন"
+
+
"ব্যক্তিগত স্পেস ট্রানজিট করা"
"অ্যাপ ইনস্টল করুন"
"প্রাইভেট স্পেসে অ্যাপ ইনস্টল করুন"
diff --git a/res/values-bs/strings.xml b/res/values-bs/strings.xml
index e2955c3741..e1d2da5dd7 100644
--- a/res/values-bs/strings.xml
+++ b/res/values-bs/strings.xml
@@ -40,14 +40,14 @@
"Dodaj na početni ekran"
"Vidžet %1$s je dodan na početni ekran"
"Prijedlozi"
- "Podignite raspoloženje danas"
+
+
"Vijesti za vas"
"Vaša zona opuštanja"
"Postignite svoje ciljeve fitnesa"
"Ne dajte da vas uhvati oluja"
"Možda vam se svidi i ovo"
-
-
+ "Vidžeti aplikacije su na desnoj, a pretraživanje i opcije na lijevoj strani"
"{count,plural, =1{# vidžet}one{# vidžet}few{# vidžeta}other{# vidžeta}}"
"{count,plural, =1{# prečica}one{# prečica}few{# prečice}other{# prečica}}"
"%1$s, %2$s"
@@ -185,6 +185,8 @@
"Privatno"
"Postavke privatnog prostora"
"Zaključavanje/otključavanje privatnog prostora"
+
+
"Prelazak u privatan prostor"
"Instaliranje aplikacija"
"Instaliranje aplikacija u privatni prostor"
diff --git a/res/values-ca/strings.xml b/res/values-ca/strings.xml
index a8bae8b216..dffa857943 100644
--- a/res/values-ca/strings.xml
+++ b/res/values-ca/strings.xml
@@ -40,14 +40,14 @@
"Afegeix a la pantalla d\'inici"
"El widget %1$s s\'ha afegit a la pantalla d\'inici"
"Suggeriments"
- "Millora el teu dia"
+
+
"Notícies per a tu"
"La teva zona de relax"
"Assoleix els teus objectius de fitnes"
"Que no et sorprengui el temps"
"També et pot agradar"
-
-
+ "Widgets de a la dreta, cerca i opcions a l\'esquerra"
"{count,plural, =1{# widget}other{# widgets}}"
"{count,plural, =1{# drecera}other{# dreceres}}"
"%1$s, %2$s"
@@ -185,6 +185,8 @@
"Privat"
"Configuració d\'Espai privat"
"Bloqueja o desbloqueja Espai privat"
+
+
"Canvia a Espai privat"
"Instal·la aplicacions"
"Instal·la les aplicacions a Espai privat"
diff --git a/res/values-cs/strings.xml b/res/values-cs/strings.xml
index 312b57b047..12e8a9f92f 100644
--- a/res/values-cs/strings.xml
+++ b/res/values-cs/strings.xml
@@ -40,14 +40,14 @@
"Přidat na plochu"
"Widget %1$s byl přidán na plochu"
"Návrhy"
- "Buďte produktivnější"
+
+
"Zprávy pro vás"
"Vaše klidová zóna"
"Dosažení kondičních cílů"
"Mějte přehled o počasí"
"Také by se vám mohlo líbit"
-
-
+ "Widgety vpravo, vyhledávání a možnosti vlevo"
"{count,plural, =1{ # widget}few{# widgety}many{# widgetu}other{# widgetů}}"
"{count,plural, =1{# zkratka}few{# zkratky}many{# zkratky}other{# zkratek}}"
"%1$s, %2$s"
@@ -185,6 +185,8 @@
"Soukromé"
"Nastavení soukromého prostoru"
"Zamknout/odemknout soukromý prostor"
+
+
"Převádění soukromého prostoru"
"Instalace aplikací"
"Instalovat aplikace do soukromého prostoru"
diff --git a/res/values-da/strings.xml b/res/values-da/strings.xml
index 967a720fac..fee0ddb4d4 100644
--- a/res/values-da/strings.xml
+++ b/res/values-da/strings.xml
@@ -40,14 +40,14 @@
"Føj til startskærm"
"Widgetten %1$s blev føjet til startskærmen"
"Forslag"
- "Boost din dag"
+
+
"Nyheder til dig"
"Dit afslapningshjørne"
"Nå dine fitnessmål"
"Vær på forkant med vejret"
"Du kan måske også lide"
-
-
+ "-widgets til højre, søgning og valgmuligheder til venstre"
"{count,plural, =1{# widget}one{# widget}other{# widgets}}"
"{count,plural, =1{# genvej}one{# genvej}other{# genveje}}"
"%1$s, %2$s"
@@ -185,6 +185,8 @@
"Privat"
"Indstillinger for privat rum"
"Lås/oplås det private område"
+
+
"Ændringer af tilstanden for det private område"
"Installer apps"
"Installer apps i privat område"
diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml
index 478aabc3fa..1d263c8928 100644
--- a/res/values-de/strings.xml
+++ b/res/values-de/strings.xml
@@ -40,14 +40,14 @@
"Zum Startbildschirm hinzufügen"
"%1$s-Widget zum Startbildschirm hinzugefügt"
"Vorschläge"
- "Deine Alltagshelfer"
+
+
"Neuigkeiten für dich"
"Zum Entspannen"
"Erreiche deine Fitnessziele"
"Dem Wetter einen Schritt voraus"
"Das könnte dir auch gefallen"
-
-
+ "-Widgets rechts, Suche und Optionen links"
"{count,plural, =1{# Widget}other{# Widgets}}"
"{count,plural, =1{# Verknüpfung}other{# Verknüpfungen}}"
"%1$s, %2$s"
@@ -185,6 +185,8 @@
"Privat"
"Einstellungen für privaten Bereich"
"Privaten Bereich sperren/entsperren"
+
+
"Sperrzustand des privaten Bereichs wird gerade geändert"
"Apps installieren"
"Apps im privaten Bereich installieren"
diff --git a/res/values-el/strings.xml b/res/values-el/strings.xml
index 092f8d81aa..e67bbc6060 100644
--- a/res/values-el/strings.xml
+++ b/res/values-el/strings.xml
@@ -40,14 +40,14 @@
"Προσθήκη στην αρχική οθόνη"
"Το γραφικό στοιχείο %1$s προστέθηκε στην αρχική οθόνη."
"Προτάσεις"
- "Ενισχύστε την απόδοσή σας σήμερα"
+
+
"Ειδήσεις για εσάς"
"Ο δικός σας τρόπος χαλάρωσης"
"Επιτύχετε τους στόχους που έχετε θέσει για τη φυσική σας κατάσταση"
"Ετοιμαστείτε για κάθε καιρό"
"Μπορεί να σας αρέσουν επίσης"
-
-
+ "Γραφικά στοιχεία στα δεξιά, αναζήτηση και επιλογές στα αριστερά"
"{count,plural, =1{# γραφικό στοιχείο}other{# γραφικά στοιχεία}}"
"{count,plural, =1{# συντόμευση}other{# συντομεύσεις}}"
"%1$s, %2$s"
@@ -185,6 +185,8 @@
"Ιδιωτικό"
"Ρυθμίσεις Ιδιωτικού χώρου"
"Κλείδωμα/Ξεκλείδωμα Ιδιωτικού χώρου"
+
+
"Μετάβαση στον Ιδιωτικό χώρο"
"Εγκατάσταση εφαρμογών"
"Εγκατάσταση εφαρμογών στον απόρρητο χώρο"
diff --git a/res/values-en-rAU/strings.xml b/res/values-en-rAU/strings.xml
index c88d5e6ed5..e9339c7506 100644
--- a/res/values-en-rAU/strings.xml
+++ b/res/values-en-rAU/strings.xml
@@ -40,14 +40,14 @@
"Add to home screen"
"%1$s widget added to home screen"
"Suggestions"
- "Boost your day"
+
+
"News for you"
"Your chill zone"
"Reach your fitness goals"
"Stay ahead of the weather"
"You might also like"
-
-
+ " widgets on right, search and options on left"
"{count,plural, =1{# widget}other{# widgets}}"
"{count,plural, =1{# shortcut}other{# shortcuts}}"
"%1$s, %2$s"
@@ -185,6 +185,8 @@
"Private"
"Private Space Settings"
"Lock/Unlock Private Space"
+
+
"Private Space transitioning"
"Install apps"
"Install apps to private space"
diff --git a/res/values-en-rCA/strings.xml b/res/values-en-rCA/strings.xml
index fc611f3c78..14be657733 100644
--- a/res/values-en-rCA/strings.xml
+++ b/res/values-en-rCA/strings.xml
@@ -40,7 +40,7 @@
"Add to home screen"
"%1$s widget added to home screen"
"Suggestions"
- "Boost your day"
+ "Your Daily Essentials"
"News For You"
"Your Chill Zone"
"Reach Your Fitness Goals"
@@ -184,6 +184,7 @@
"Private"
"Private Space Settings"
"Lock/Unlock Private Space"
+ "Lock"
"Private Space Transitioning"
"Install apps"
"Install apps to Private Space"
diff --git a/res/values-en-rGB/strings.xml b/res/values-en-rGB/strings.xml
index c88d5e6ed5..e9339c7506 100644
--- a/res/values-en-rGB/strings.xml
+++ b/res/values-en-rGB/strings.xml
@@ -40,14 +40,14 @@
"Add to home screen"
"%1$s widget added to home screen"
"Suggestions"
- "Boost your day"
+
+
"News for you"
"Your chill zone"
"Reach your fitness goals"
"Stay ahead of the weather"
"You might also like"
-
-
+ " widgets on right, search and options on left"
"{count,plural, =1{# widget}other{# widgets}}"
"{count,plural, =1{# shortcut}other{# shortcuts}}"
"%1$s, %2$s"
@@ -185,6 +185,8 @@
"Private"
"Private Space Settings"
"Lock/Unlock Private Space"
+
+
"Private Space transitioning"
"Install apps"
"Install apps to private space"
diff --git a/res/values-en-rIN/strings.xml b/res/values-en-rIN/strings.xml
index c88d5e6ed5..e9339c7506 100644
--- a/res/values-en-rIN/strings.xml
+++ b/res/values-en-rIN/strings.xml
@@ -40,14 +40,14 @@
"Add to home screen"
"%1$s widget added to home screen"
"Suggestions"
- "Boost your day"
+
+
"News for you"
"Your chill zone"
"Reach your fitness goals"
"Stay ahead of the weather"
"You might also like"
-
-
+ " widgets on right, search and options on left"
"{count,plural, =1{# widget}other{# widgets}}"
"{count,plural, =1{# shortcut}other{# shortcuts}}"
"%1$s, %2$s"
@@ -185,6 +185,8 @@
"Private"
"Private Space Settings"
"Lock/Unlock Private Space"
+
+
"Private Space transitioning"
"Install apps"
"Install apps to private space"
diff --git a/res/values-en-rXC/strings.xml b/res/values-en-rXC/strings.xml
index 5ce58581f8..985174a808 100644
--- a/res/values-en-rXC/strings.xml
+++ b/res/values-en-rXC/strings.xml
@@ -40,7 +40,7 @@
"Add to home screen"
"%1$s widget added to home screen"
"Suggestions"
- "Boost your day"
+ "Your Daily Essentials"
"News For You"
"Your Chill Zone"
"Reach Your Fitness Goals"
@@ -184,6 +184,7 @@
"Private"
"Private Space Settings"
"Lock/Unlock Private Space"
+ "Lock"
"Private Space Transitioning"
"Install apps"
"Install apps to Private Space"
diff --git a/res/values-es-rUS/strings.xml b/res/values-es-rUS/strings.xml
index dc087f22c7..90b27cc715 100644
--- a/res/values-es-rUS/strings.xml
+++ b/res/values-es-rUS/strings.xml
@@ -40,14 +40,14 @@
"Agregar a pantalla principal"
"Se agregó el widget de %1$s a la pantalla principal"
"Sugerencias"
- "Aprovecha mejor tu día"
+
+
"Noticias para ti"
"Zona de descanso"
"Logra tus objetivos de fitness"
"Mantente al tanto del clima"
"Puede que también te guste"
-
-
+ "Widgets de a la derecha, búsqueda y opciones a la izquierda"
"{count,plural, =1{# widget}other{# widgets}}"
"{count,plural, =1{# acceso directo}other{# accesos directos}}"
"%1$s, %2$s"
@@ -185,6 +185,8 @@
"Privado"
"Configuración de Espacio privado"
"Bloquear o desbloquear Espacio privado"
+
+
"Pasar a Espacio privado"
"Instala apps"
"Instala las apps en el espacio privado"
diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml
index 94d83bd439..4bf5516b90 100644
--- a/res/values-es/strings.xml
+++ b/res/values-es/strings.xml
@@ -40,14 +40,14 @@
"Añadir a pantalla de inicio"
"Widget %1$s añadido a la pantalla de inicio"
"Sugerencias"
- "Mejora tu día"
+
+
"Noticias para ti"
"Tu zona de descanso"
"Logra tus objetivos de actividad física"
"Infórmate sobre el tiempo"
"También te puede interesar"
-
-
+ "Widgets de a la derecha, búsqueda y opciones a la izquierda"
"{count,plural, =1{# widget}other{# widgets}}"
"{count,plural, =1{# acceso directo}other{# accesos directos}}"
"%1$s, %2$s"
@@ -185,6 +185,8 @@
"Privado"
"Ajustes del espacio privado"
"Bloquear/Desbloquear espacio privado"
+
+
"Cambiar a espacio privado"
"Descargar aplicaciones"
"Descargar aplicaciones en el espacio privado"
diff --git a/res/values-et/strings.xml b/res/values-et/strings.xml
index 4b30dce8e5..26277af4d5 100644
--- a/res/values-et/strings.xml
+++ b/res/values-et/strings.xml
@@ -40,14 +40,14 @@
"Lisa avakuvale"
"Vidin %1$s lisati avakuvale"
"Soovitused"
- "Saavutage päeva jooksul rohkem"
+
+
"Uudised teile"
"Teie lõõgastumiskoht"
"Saavutage oma treeningueesmärgid"
"Olge ilmateatega kursis"
"Teile võivad meeldida ka need"
-
-
+ "Teenuse vidinad paremal, otsing ja valikud vasakul"
"{count,plural, =1{# vidin}other{# vidinat}}"
"{count,plural, =1{# otsetee}other{# otseteed}}"
"%1$s, %2$s"
@@ -185,6 +185,8 @@
"Privaatne"
"Privaatse ruumi seaded"
"Privaatse ruumi lukustamine/avamine"
+
+
"Privaatse ruumi üleviimine"
"Rakenduste installimine"
"Rakenduste installimine privaatses ruumis"
diff --git a/res/values-eu/strings.xml b/res/values-eu/strings.xml
index 3c708f8f9b..435d057943 100644
--- a/res/values-eu/strings.xml
+++ b/res/values-eu/strings.xml
@@ -40,14 +40,14 @@
"Gehitu hasierako pantailan"
"%1$s widgeta hasierako pantailan gehitu da"
"Iradokizunak"
- "Atera etekin handiagoa egunari"
+
+
"Zuretzako albisteak"
"Lasaitzeko gunea"
"Erdietsi zure fitness-helburuak"
"Hartu aurrea eguraldiari"
"Gustatuko zaizkizulakoan"
-
-
+ " zerbitzuaren widgetak eskuinean, bilaketa eta aukerak ezkerrean"
"{count,plural, =1{# widget}other{# widget}}"
"{count,plural, =1{# lasterbide}other{# lasterbide}}"
"%1$s, %2$s"
@@ -185,6 +185,8 @@
"Pribatua"
"Eremu pribatuaren ezarpenak"
"Blokeatu/Desblokeatu eremu pribatua"
+
+
"Eremu pribaturako trantsizioa"
"Aplikazioak instalatu"
"Instalatu aplikazioak eremu pribatuan"
diff --git a/res/values-fa/strings.xml b/res/values-fa/strings.xml
index f9ed4a0729..6c71677a1c 100644
--- a/res/values-fa/strings.xml
+++ b/res/values-fa/strings.xml
@@ -40,14 +40,14 @@
"افزودن به صفحه اصلی"
"ابزارک %1$s به صفحه اصلی اضافه شد"
"پیشنهادها"
- "روزتان را پربار کنید"
+
+
"اخبار برای شما"
"منطقه آرامش شما"
"دستیابی به اهداف تناسب اندام"
"آبوهوا را پیشبینی کنید"
"شاید این را هم بپسندید"
-
-
+ "ابزارکهای در سمت چپ، جستجو و گزینهها در سمت راست"
"{count,plural, =1{# ابزارک}one{# ابزارک}other{# ابزارک}}"
"{count,plural, =1{# میانبر}one{# میانبر}other{# میانبر}}"
"%1$s،%2$s"
@@ -185,6 +185,8 @@
"خصوصی"
"تنظیمات «فضای خصوصی»"
"قفل/ باز کردن «فضای خصوصی»"
+
+
"انتقال «فضای خصوصی»"
"نصب برنامهها"
"نصب برنامهها در «فضای خصوصی»"
diff --git a/res/values-fi/strings.xml b/res/values-fi/strings.xml
index 6d9cf77e8c..1109611828 100644
--- a/res/values-fi/strings.xml
+++ b/res/values-fi/strings.xml
@@ -40,14 +40,14 @@
"Lisää aloitusnäytölle"
"Widget lisätty aloitusnäytölle: %1$s"
"Ehdotukset"
- "Tehosta päivääsi"
+
+
"Uutisia sinulle"
"Ota rennosti"
"Saavuta kuntoilutavoitteet"
"Pysy ajan tasalla säästä"
"Saatat pitää myös näistä"
-
-
+ " widgetit oikealla, haku ja vaihtoehdot vasemmalla"
"{count,plural, =1{# widget}other{# widgetiä}}"
"{count,plural, =1{# pikakuvake}other{# pikakuvaketta}}"
"%1$s, %2$s"
@@ -185,6 +185,8 @@
"Yksityinen"
"Yksityisen tilan asetukset"
"Lukitse yksityinen tila / avaa sen lukitus"
+
+
"Yksityisen tilan siirtäminen"
"Asenna sovelluksia"
"Asenna sovelluksia yksityiseen tilaan"
diff --git a/res/values-fr-rCA/strings.xml b/res/values-fr-rCA/strings.xml
index a3ed04c11f..bb7055128b 100644
--- a/res/values-fr-rCA/strings.xml
+++ b/res/values-fr-rCA/strings.xml
@@ -40,14 +40,14 @@
"Ajouter à l\'écran d\'accueil"
"Le widget %1$s a été ajouté à l\'écran d\'accueil"
"Suggestions"
- "Productivité assurée"
+
+
"Actualités personnalisées"
"Zone de divertissement"
"Objectifs de mise en forme"
"À l\'affût de la météo"
"Autres recommandations"
-
-
+ "Widgets à droite, recherche et options à gauche"
"{count,plural, =1{# widget}one{# widget}other{# widgets}}"
"{count,plural, =1{# raccourci}one{# raccourci}other{# raccourcis}}"
"%1$s, %2$s"
@@ -185,6 +185,8 @@
"Privé"
"Paramètres de l\'Espace privé"
"Verrouiller/Déverrouiller l\'Espace privé"
+
+
"Transition vers l\'Espace privé"
"Installer des applications"
"Installer des applications dans l\'Espace privé"
diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml
index 80e3059a79..62844b09ed 100644
--- a/res/values-fr/strings.xml
+++ b/res/values-fr/strings.xml
@@ -40,14 +40,14 @@
"Ajouter à l\'écran d\'accueil"
"Widget %1$s ajouté à l\'écran d\'accueil"
"Suggestions"
- "Boostez votre journée"
+
+
"Actualités personnalisées"
"Votre espace détente"
"Atteignez vos objectifs forme"
"Soyez au fait de la météo"
"Découvrez également"
-
-
+ "Widgets à droite, recherche et options à gauche"
"{count,plural, =1{# widget}one{# widget}other{# widgets}}"
"{count,plural, =1{# raccourci}one{# raccourci}other{# raccourcis}}"
"%1$s, %2$s"
@@ -185,6 +185,8 @@
"Privé"
"Paramètres d\'Espace privé"
"Verrouiller/Déverrouiller Espace privé"
+
+
"Transition vers Espace privé"
"Installer des applis"
"Installer des applis dans l\'espace privé"
diff --git a/res/values-gl/strings.xml b/res/values-gl/strings.xml
index 677cfe300c..82cdf3ab0b 100644
--- a/res/values-gl/strings.xml
+++ b/res/values-gl/strings.xml
@@ -40,14 +40,14 @@
"Engadir á pantalla de inicio"
"Engadiuse o widget %1$s á pantalla de inicio"
"Suxestións"
- "Mellora o teu día"
+
+
"Novidades para ti"
"Reláxate"
"Acada os teus obxectivos para estar en forma"
"Adiántate á meteoroloxía"
"Tamén che pode interesar…"
-
-
+ "Widgets de á dereita, busca e opcións á esquerda"
"{count,plural, =1{# widget}other{# widgets}}"
"{count,plural, =1{# atallo}other{# atallos}}"
"%1$s, %2$s"
@@ -185,6 +185,8 @@
"Privado"
"Configuración do espazo privado"
"Bloquear ou desbloquear o espazo privado"
+
+
"Transición ao espazo privado"
"Instalar as aplicacións"
"Instalar as aplicacións no espazo privado"
diff --git a/res/values-gu/strings.xml b/res/values-gu/strings.xml
index a77cbfd2c2..4a5ac831ef 100644
--- a/res/values-gu/strings.xml
+++ b/res/values-gu/strings.xml
@@ -40,14 +40,14 @@
"હોમ સ્ક્રીનમાં ઉમેરો"
"હોમ સ્ક્રીન પર %1$s વિજેટ ઉમેર્યુ"
"સૂચનો"
- "તમારા દિવસને બૂસ્ટ કરો"
+
+
"તમારા માટે સમાચાર"
"તમારો આરામદાયક ઝોન"
"તમારા ફિટનેસ લક્ષ્યો પૂરા કરો"
"હવામાન વિશે અપ ટૂ ડેટ રહો"
"કદાચ તમને આ પણ પસંદ હોય"
-
-
+ "ની વિજેટ જમણે, શોધ અને વિકલ્પો ડાબે"
"{count,plural, =1{# વિજેટ}one{# વિજેટ}other{# વિજેટ}}"
"{count,plural, =1{# શૉર્ટકટ}one{# શૉર્ટકટ}other{# શૉર્ટકટ}}"
"%1$s, %2$s"
@@ -185,6 +185,8 @@
"ખાનગી"
"ખાનગી સ્પેસના સેટિંગ"
"ખાનગી સ્પેસને લૉક/અનલૉક કરો"
+
+
"ખાનગી સ્પેસ પર સ્થાનાંતરણ"
"ઍપ ઇન્સ્ટૉલ કરો"
"ખાનગી સ્પેસમાં ઍપ ઇન્સ્ટૉલ કરો"
diff --git a/res/values-hi/strings.xml b/res/values-hi/strings.xml
index b08230e110..319939afde 100644
--- a/res/values-hi/strings.xml
+++ b/res/values-hi/strings.xml
@@ -40,14 +40,14 @@
"होम स्क्रीन पर जोड़ें"
"%1$s विजेट को होम स्क्रीन पर जोड़ा गया"
"सुझाव"
- "अपना दिन बेहतर बनाएं"
+
+
"आपके लिए खबरें"
"आपके मनोरंजन के लिए"
"फ़िटनेस के लक्ष्य हासिल करें"
"मौसम की अप-टू-डेट जानकारी पाएं"
"शायद आपको ये भी पसंद आएं"
-
-
+ " के विजेट दाईं ओर, खोज का विजेट और अन्य विकल्प बाईं ओर"
"{count,plural, =1{# विजेट}one{# विजेट}other{# विजेट}}"
"{count,plural, =1{# शॉर्टकट}one{# शॉर्टकट}other{# शॉर्टकट}}"
"%1$s, %2$s"
@@ -185,6 +185,8 @@
"निजी"
"प्राइवेट स्पेस सेटिंग"
"प्राइवेट स्पेस को लॉक करें/अनलॉक करें"
+
+
"प्राइवेट स्पेस की सेटिंग में बदलाव किया जा रहा है"
"ऐप्लिकेशन इंस्टॉल करें"
"प्राइवेट स्पेस में ऐप्लिकेशन इंस्टॉल करें"
diff --git a/res/values-hr/strings.xml b/res/values-hr/strings.xml
index 1d85b4c59f..46b0abf757 100644
--- a/res/values-hr/strings.xml
+++ b/res/values-hr/strings.xml
@@ -40,14 +40,14 @@
"Dodaj na početni zaslon"
"Widget %1$s dodan je na početni zaslon"
"Prijedlozi"
- "Unaprijedite svoj dan"
+
+
"Vijesti za vas"
"Vaša zona za opuštanje"
"Postignite svoje ciljeve u fitnesu"
"Budite korak ispred vremenskih prilika"
"Možda će vam se svidjeti i ovo"
-
-
+ " –widgeti zdesna, pretraživanje i opcije slijeva"
"{count,plural, =1{# widget}one{# widget}few{# widgeta}other{# widgeta}}"
"{count,plural, =1{# prečac}one{# prečac}few{# prečaca}other{# prečaca}}"
"%1$s, %2$s"
@@ -185,6 +185,8 @@
"Privatno"
"Postavke privatnog prostora"
"Zaključavanje/otključavanje privatnog prostora"
+
+
"Prelazak na privatni prostor"
"Instaliranje aplikacija"
"Instaliranje aplikacija u privatni prostor"
diff --git a/res/values-hu/strings.xml b/res/values-hu/strings.xml
index 3ee1143a8c..06d17f5f61 100644
--- a/res/values-hu/strings.xml
+++ b/res/values-hu/strings.xml
@@ -40,14 +40,14 @@
"Hozzáadás a kezdőképernyőhöz"
"%1$s modul hozzáadva a kezdőképernyőhöz"
"Javaslatok"
- "Felturbózhatja a napját"
+
+
"Hírek Önnek"
"Az Ön relaxáló zónája"
"Elérheti kitűzött erőnléti céljait"
"Mindig friss időjárás-információk"
"Lehet, hogy ez is tetszeni fog"
-
-
+ "A -modulok a jobb, a kereső és a beállítások pedig a bal oldalon találhatók"
"{count,plural, =1{# modul}other{# modul}}"
"{count,plural, =1{# gyorsparancs}other{# gyorsparancs}}"
"%1$s, %2$s"
@@ -185,6 +185,8 @@
"Privát"
"Privát terület beállításai"
"Privát terület zárolása/zárolásának feloldása"
+
+
"Átállás privát területre…"
"Alkalmazástelepítés"
"Alkalmazások telepítése magánterületre"
diff --git a/res/values-hy/strings.xml b/res/values-hy/strings.xml
index 4f69c17b76..0e4a120713 100644
--- a/res/values-hy/strings.xml
+++ b/res/values-hy/strings.xml
@@ -40,14 +40,14 @@
"Ավելացնել հիմնական էկրանին"
"%1$s վիջեթն ավելացվել է հիմնական էկրանին"
"Առաջարկներ"
- "Ակտիվացրեք ձեր օրը"
+
+
"Նորություններ ձեզ համար"
"Ձեր հանգստի գոտին"
"Հասեք ձեր ֆիթնես նպատակներին"
"Եղեք տեղեկացված եղանակի մասին"
"Ձեզ կարող է դուր գալ"
-
-
+ "«» հավելվածի վիջեթներն աջ կողմում են, իսկ որոնման դաշտը և կարգավորումները՝ ձախ կողմում"
"{count,plural, =1{# վիջեթ}one{# վիջեթ}other{# վիջեթ}}"
"{count,plural, =1{# դյուրանցում}one{# դյուրանցում}other{# դյուրանցում}}"
"%1$s, %2$s"
@@ -185,6 +185,8 @@
"Անձնական"
"Անձնական տարածքի կարգավորումներ"
"Կողպել/ապակողպել անձնական տարածքը"
+
+
"Անցում անձնական տարածք"
"Հավելվածների տեղադրում"
"Հավելվածների տեղադրում անձնական տարածքում"
diff --git a/res/values-in/strings.xml b/res/values-in/strings.xml
index a1bd81ea3a..3e325b320d 100644
--- a/res/values-in/strings.xml
+++ b/res/values-in/strings.xml
@@ -40,14 +40,14 @@
"Tambahkan ke layar utama"
"Widget %1$s ditambahkan ke layar utama"
"Saran"
- "Tingkatkan hari Anda"
+
+
"Berita untuk Anda"
"Zona Nyaman Anda"
"Capai Target Kebugaran Anda"
"Tetap Waspada Menghadapi Cuaca"
"Anda Mungkin Juga Suka"
-
-
+ "Widget di bagian kanan, penelusuran dan opsi di bagian kiri"
"{count,plural, =1{# widget}other{# widget}}"
"{count,plural, =1{# pintasan}other{# pintasan}}"
"%1$s, %2$s"
@@ -185,6 +185,8 @@
"Pribadi"
"Setelan Ruang Pribadi"
"Kunci/Buka Kunci Ruang Pribadi"
+
+
"Ruang Pribadi Bertransisi"
"Menginstal aplikasi"
"Instal aplikasi ke Ruang Pribadi"
diff --git a/res/values-is/strings.xml b/res/values-is/strings.xml
index 034362eddb..b2e91d47d2 100644
--- a/res/values-is/strings.xml
+++ b/res/values-is/strings.xml
@@ -40,14 +40,14 @@
"Bæta á heimaskjá"
"%1$s græju bætt við heimaskjá"
"Tillögur"
- "Gerðu daginn betri"
+
+
"Fréttir fyrir þig"
"Slakaðu á"
"Náðu hreyfingarmarkmiðunum þínum"
"Vertu einu skrefi á undan veðrinu"
"Þú gætir einnig haft áhuga á"
-
-
+ "-græjur til hægri, leit og valkostir til vinstri"
"{count,plural, =1{# græja}one{# græja}other{# græjur}}"
"{count,plural, =1{# flýtileið}one{# flýtileið}other{# flýtileiðir}}"
"%1$s, %2$s"
@@ -185,6 +185,8 @@
"Lokað"
"Stillingar einkarýmis"
"Læsaeinkarými/taka einkarými úr lás"
+
+
"Einkarými að breytast"
"Setja upp forrit"
"Setja upp forrit í einkarými"
diff --git a/res/values-it/strings.xml b/res/values-it/strings.xml
index 808ee5bc78..f259595162 100644
--- a/res/values-it/strings.xml
+++ b/res/values-it/strings.xml
@@ -40,14 +40,14 @@
"Aggiungi alla schermata Home"
"Widget %1$s aggiunto alla schermata Home"
"Suggerimenti"
- "Dai la carica alla tua giornata"
+
+
"Notizie per te"
"Il tuo angolo di tranquillità"
"Raggiungi i tuoi obiettivi di fitness"
"Non perderti le previsioni meteo"
"Ti potrebbero anche piacere"
-
-
+ "Widget di a destra, ricerca e opzioni a sinistra"
"{count,plural, =1{# widget}other{# widget}}"
"{count,plural, =1{# scorciatoia}other{# scorciatoie}}"
"%1$s, %2$s"
@@ -185,6 +185,8 @@
"Privato"
"Impostazioni dello Spazio privato"
"Blocca/sblocca Spazio privato"
+
+
"Transizione dello Spazio privato in corso…"
"Installa app"
"Installa le app su spazi privati"
diff --git a/res/values-iw/strings.xml b/res/values-iw/strings.xml
index b0e4fc6d68..d22e937f02 100644
--- a/res/values-iw/strings.xml
+++ b/res/values-iw/strings.xml
@@ -40,14 +40,14 @@
"הוספה למסך הבית"
"הווידג\'ט %1$s נוסף למסך הבית"
"הצעות"
- "משפרים את היום"
+
+
"חדשות בשבילך"
"המקום שלך לרגיעה"
"השגת יעדי הכושר שלך"
"התעדכנות במזג האוויר"
"אולי יעניין אותך גם"
-
-
+ " ווידג\'טים מימין, חיפוש ואפשרויות משמאל"
"{count,plural, =1{ווידג\'ט אחד}one{# ווידג\'טים}two{# ווידג\'טים}other{# ווידג\'טים}}"
"{count,plural, =1{קיצור דרך אחד}one{# קיצורי דרך}two{# קיצורי דרך}other{# קיצורי דרך}}"
"%1$s, %2$s"
@@ -185,6 +185,8 @@
"פרטי"
"הגדרות המרחב הפרטי"
"נעילה או ביטול הנעילה של המרחב הפרטי"
+
+
"מעבר למרחב הפרטי"
"התקנת אפליקציות"
"התקנת אפליקציות במרחב הפרטי"
diff --git a/res/values-ja/strings.xml b/res/values-ja/strings.xml
index 9cb3fd57ae..769af4b698 100644
--- a/res/values-ja/strings.xml
+++ b/res/values-ja/strings.xml
@@ -40,14 +40,14 @@
"ホーム画面に追加"
"「%1$s」ウィジェットをホーム画面に追加しました"
"候補"
- "生産性を向上"
+
+
"あなたへのおすすめニュース"
"休憩エリア"
"フィットネスの目標を達成"
"天気予報"
"あなたへのおすすめ"
-
-
+ " のウィジェットは右側に、検索とオプションは左側にあります"
"{count,plural, =1{# 件のウィジェット}other{# 件のウィジェット}}"
"{count,plural, =1{# 件のショートカット}other{# 件のショートカット}}"
"%1$s、%2$s"
@@ -185,6 +185,8 @@
"プライベート"
"プライベート スペースの設定"
"プライベート スペースをロック / ロック解除する"
+
+
"プライベート スペース移行中"
"アプリをインストールする"
"プライベート スペースにアプリをインストールします"
diff --git a/res/values-ka/strings.xml b/res/values-ka/strings.xml
index d765680b84..43249d835e 100644
--- a/res/values-ka/strings.xml
+++ b/res/values-ka/strings.xml
@@ -40,14 +40,14 @@
"მთავარ ეკრანზე დამატება"
"%1$s ვიჯეტი დამატებულია მთავარ ეკრანზე"
"შეთავაზებები"
- "გაიუმჯობესეთ დღე"
+
+
"News თქვენთვის"
"განტვირთვის ადგილი"
"მიაღწიეთ ფიტნეს-მიზნებს"
"მიიღეთ ინფორმაცია წინასწარ ამინდის შესახებ"
"ასევე შეიძლება მოგეწონოთ"
-
-
+ " ვიჯეტები მდებარეობს მარჯვნივ, ძებნა და პარამეტრები — მარცხნივ"
"{count,plural, =1{# ვიჯეტი}other{# ვიჯეტი}}"
"{count,plural, =1{# მალსახმობი}other{# მალსახმობი}}"
"%1$s, %2$s"
@@ -185,6 +185,8 @@
"პირადი"
"პირადი სივრცის პარამეტრები"
"პირადი სივრცის ჩაკეტვა/განბლოკვა"
+
+
"პირად სივრცეზე გადასვლა"
"აპების ინსტალაცია"
"კერძო სივრცეში აპების ინსტალაცია"
diff --git a/res/values-kk/strings.xml b/res/values-kk/strings.xml
index e0f14ba0ee..005f86079e 100644
--- a/res/values-kk/strings.xml
+++ b/res/values-kk/strings.xml
@@ -40,14 +40,14 @@
"Негізгі экранға қосу"
"%1$s виджеті негізгі экранға енгізілді."
"Ұсыныстар"
- "Күні бойы қуатты болыңыз"
+
+
"Сізге арналған жаңалықтар"
"Жанға жайлы жер"
"Денені шынықтыру бойынша қойған мақсаттарыңызға жетіңіз"
"Ауа райын алдын ала біліп отырыңыз"
"Сізге мыналар да ұнауы мүмкін"
-
-
+ " виджеттері оң жақта, іздеу мен опциялар сол жақта"
"{count,plural, =1{# виджет}other{# виджет}}"
"{count,plural, =1{# таңбаша}other{# таңбаша}}"
"%1$s, %2$s"
@@ -185,6 +185,8 @@
"Жеке"
"Жеке бөлме параметрлері"
"Жеке бөлмені құлыптау/оның құлпын ашу"
+
+
"Жеке бөлмеге өту"
"Қолданбалар орнату"
"Қолданбаларды \"Құпия кеңістікке\" орнатыңыз."
diff --git a/res/values-km/strings.xml b/res/values-km/strings.xml
index c68194afa5..abce94a805 100644
--- a/res/values-km/strings.xml
+++ b/res/values-km/strings.xml
@@ -40,14 +40,14 @@
"បញ្ចូលទៅក្នុងអេក្រង់ដើម"
"បានបញ្ចូលធាតុក្រាហ្វិក %1$s ទៅអេក្រង់ដើម"
"ការណែនាំ"
- "ពង្រឹងសុខុមាលភាពប្រចាំថ្ងៃរបស់អ្នក"
+
+
"ព័ត៌មានសម្រាប់អ្នក"
"តំបន់បន្ធូរអារម្មណ៍របស់អ្នក"
"សម្រេចគោលដៅហាត់ប្រាណរបស់អ្នក"
"ទទួលបានដំណឹងជាមុនអំពីអាកាសធាតុ"
"អ្នកក៏អាចនឹងចូលចិត្ត"
-
-
+ "ធាតុក្រាហ្វិក នៅខាងស្ដាំ ការស្វែងរក និងជម្រើសនៅខាងឆ្វេង"
"{count,plural, =1{ធាតុក្រាហ្វិក #}other{ធាតុក្រាហ្វិក #}}"
"{count,plural, =1{ផ្លូវកាត់ #}other{ផ្លូវកាត់ #}}"
"%1$s, %2$s"
@@ -185,6 +185,8 @@
"ឯកជន"
"ការកំណត់ Private Space"
"ចាក់សោ/ដោះសោ Private Space"
+
+
"ការផ្លាស់ប្ដូរ Private Space"
"ដំឡើងកម្មវិធី"
"ដំឡើងកម្មវិធីទៅលំហឯកជន"
diff --git a/res/values-kn/strings.xml b/res/values-kn/strings.xml
index c82288fb75..4815371e08 100644
--- a/res/values-kn/strings.xml
+++ b/res/values-kn/strings.xml
@@ -40,14 +40,14 @@
"ಹೋಮ್ ಸ್ಕ್ರೀನ್ಗೆ ಸೇರಿಸಿ"
"ಹೋಮ್ಸ್ಕ್ರೀನ್ಗೆ %1$s ವಿಜೆಟ್ ಅನ್ನು ಸೇರಿಸಲಾಗಿದೆ"
"ಸಲಹೆಗಳು"
- "ನಿಮ್ಮ ದಿನವನ್ನು ಇನ್ನಷ್ಟು ಉತ್ಸಾಹಗೊಳಿಸಿ"
+
+
"ನಿಮಗಾಗಿ ಸುದ್ದಿ"
"ನೀವು ವಿಶ್ರಾಂತಿ ಪಡೆಯುವ ಸ್ಥಳ"
"ನಿಮ್ಮ ಫಿಟ್ನೆಸ್ ಗುರಿಗಳನ್ನು ಸಾಧಿಸಿ"
"ಹವಾಮಾನದ ಕುರಿತು ಮುಂಚೆಯೇ ಅಪ್ಡೇಟ್ ಆಗಿರಿ"
"ನಿಮಗೆ ಇವು ಕೂಡ ಇಷ್ಟವಾಗಬಹುದು"
-
-
+ "ಬಲಭಾಗದಲ್ಲಿ ವಿಜೆಟ್ಗಳು, ಎಡಭಾಗದಲ್ಲಿ ಹುಡುಕಾಟ ಮತ್ತು ಆಯ್ಕೆಗಳು"
"{count,plural, =1{# ವಿಜೆಟ್}one{# ವಿಜೆಟ್ಗಳು}other{# ವಿಜೆಟ್ಗಳು}}"
"{count,plural, =1{# ಶಾರ್ಟ್ಕಟ್}one{# ಶಾರ್ಟ್ಕಟ್ಗಳು}other{# ಶಾರ್ಟ್ಕಟ್ಗಳು}}"
"%1$s, %2$s"
@@ -185,6 +185,8 @@
"ಖಾಸಗಿ"
"ಖಾಸಗಿ ಸ್ಪೇಸ್ ಸೆಟ್ಟಿಂಗ್ಗಳು"
"ಖಾಸಗಿ ಸ್ಪೇಸ್ ಅನ್ನು ಲಾಕ್/ಅನ್ಲಾಕ್ ಮಾಡಿ"
+
+
"ಖಾಸಗಿ ಸ್ಪೇಸ್ ಪರಿವರ್ತನೆಯಾಗುತ್ತಿದೆ"
"ಆ್ಯಪ್ಗಳನ್ನು ಇನ್ಸ್ಟಾಲ್ ಮಾಡಿ"
"ಆ್ಯಪ್ಗಳನ್ನು ಪ್ರೈವೇಟ್ ಸ್ಪೇಸ್ನಲ್ಲಿ ಇನ್ಸ್ಟಾಲ್ ಮಾಡಿ"
diff --git a/res/values-ko/strings.xml b/res/values-ko/strings.xml
index d8e64d6b3c..fc0d8bb93d 100644
--- a/res/values-ko/strings.xml
+++ b/res/values-ko/strings.xml
@@ -40,14 +40,14 @@
"홈 화면에 추가"
"%1$s 위젯이 홈 화면에 추가됨"
"추천"
- "생산성 향상"
+
+
"추천 뉴스"
"휴식 공간"
"피트니스 목표 달성"
"사전에 날씨 확인"
"좋아할 만한 항목"
-
-
+ "오른쪽에 위젯, 왼쪽에 검색 및 옵션"
"{count,plural, =1{위젯 #개}other{위젯 #개}}"
"{count,plural, =1{바로가기 #개}other{바로가기 #개}}"
"%1$s, %2$s"
@@ -185,6 +185,8 @@
"비공개"
"비공개 스페이스 설정"
"비공개 스페이스 잠금/잠금 해제"
+
+
"비공개 스페이스 전환"
"앱 설치"
"비공개 스페이스에 앱 설치"
diff --git a/res/values-ky/strings.xml b/res/values-ky/strings.xml
index 8b2fef4bdd..dff3f00e94 100644
--- a/res/values-ky/strings.xml
+++ b/res/values-ky/strings.xml
@@ -40,14 +40,14 @@
"Башкы экранга кошуу"
"%1$s виджети башкы экранга кошулду"
"Сунуштар"
- "Майнаптуу күн"
+
+
"Сиз үчүн жаңылыктар"
"Чер жазуу"
"Фитнес максаттарыңызга жетиңиз"
"Аба ырайы тууралуу маалымат"
"Төмөнкүлөр да жагышы мүмкүн"
-
-
+ " виджеттери оң, ал эми издөө жана параметрлер сол жакта"
"{count,plural, =1{# виджет}other{# виджет}}"
"{count,plural, =1{# ыкчам баскыч}other{# ыкчам баскыч}}"
"%1$s, %2$s"
@@ -185,6 +185,8 @@
"Жеке"
"Жеке чөйрөнүн параметрлери"
"Жеке чөйрөнү кулпулоо/кулпусун ачуу"
+
+
"Жеке чөйрөгө өтүү"
"Колдонмолорду орнотуу"
"Колдонмолорду Жеке мейкиндикке орнотуe"
diff --git a/res/values-lo/strings.xml b/res/values-lo/strings.xml
index 99f30424a1..328d108697 100644
--- a/res/values-lo/strings.xml
+++ b/res/values-lo/strings.xml
@@ -40,14 +40,14 @@
"ເພີ່ມໃສ່ໂຮມສະກຣີນ"
"ເພີ່ມວິດເຈັດ %1$s ໃສ່ໂຮມສະກຣີນແລ້ວ"
"ການແນະນຳ"
- "ເຮັດໃຫ້ເປັນມື້ທີ່ມີປະສິດທິພາບ"
+
+
"ຂ່າວສຳລັບທ່ານ"
"ພື້ນທີ່ພັກຜ່ອນຂອງທ່ານ"
"ບັນລຸເປົ້າໝາຍການອອກກຳລັງກາຍຂອງທ່ານ"
"ຮູ້ສະພາບອາກາດລ່ວງໜ້າ"
"ທ່ານອາດຈະມັກ"
-
-
+ "ວິດເຈັດ ຢູ່ທາງຂວາ, ການຊອກຫາ ແລະ ຕົວເລືອກຢູ່ທາງຊ້າຍ"
"{count,plural, =1{# ວິດເຈັດ}other{# ວິດເຈັດ}}"
"{count,plural, =1{# ທາງລັດ}other{# ທາງລັດ}}"
"%1$s, %2$s"
@@ -185,6 +185,8 @@
"ສ່ວນຕົວ"
"ການຕັ້ງຄ່າພື້ນທີ່ສ່ວນຕົວ"
"ລັອກ/ປົດລັອກພື້ນທີ່ສ່ວນຕົວ"
+
+
"ການປ່ຽນແປງພື້ນທີ່ສ່ວນຕົວ"
"ຕິດຕັ້ງແອັບ"
"ຕິດຕັ້ງແອັບໄປໃສ່ພື້ນທີ່ສ່ວນບຸກຄົນ"
diff --git a/res/values-lt/strings.xml b/res/values-lt/strings.xml
index 6579674d3b..db18d7bf39 100644
--- a/res/values-lt/strings.xml
+++ b/res/values-lt/strings.xml
@@ -40,14 +40,14 @@
"Pridėti prie pagrindinio ekrano"
"Valdiklis „%1$s“ pridėtas prie pagrindinio ekrano"
"Pasiūlymai"
- "Padidinkite dienos produktyvumą"
+
+
"Naujienos jums"
"Jūsų atsipalaidavimo zona"
"Pasiekite mankštos tikslus"
"Visada žinokite, kokie bus orai"
"Jums taip pat gali patikti"
-
-
+ " valdikliai dešinėje, paieška ir parinktys kairėje"
"{count,plural, =1{# valdiklis}one{# valdiklis}few{# valdikliai}many{# valdiklio}other{# valdiklių}}"
"{count,plural, =1{# spartusis klavišas}one{# spartusis klavišas}few{# spartieji klavišai}many{# sparčiojo klavišo}other{# sparčiųjų klavišų}}"
"%1$s, %2$s"
@@ -185,6 +185,8 @@
"Privatus"
"Privačios erdvės nustatymai"
"Užrakinti ir (arba) atrakinti privačią erdvę"
+
+
"Privačios erdvės perkėlimas"
"Programų diegimas"
"Įdiegti programas privačioje erdvėje"
diff --git a/res/values-lv/strings.xml b/res/values-lv/strings.xml
index 7df06a9232..f0e58df749 100644
--- a/res/values-lv/strings.xml
+++ b/res/values-lv/strings.xml
@@ -40,14 +40,14 @@
"Pievienot sākuma ekrānam"
"Logrīks “%1$s” ir pievienots sākuma ekrānam"
"Ieteikumi"
- "Produktīvākai dienai"
+
+
"Ziņas jums"
"Jūsu atpūtas stūrītis"
"Sasniedziet fitnesa mērķus"
"Neļaujiet laikapstākļiem jūs pārsteigt"
"Jums varētu patikt arī…"
-
-
+ "Pa labi logrīki , pa kreisi meklēšana un iespējas"
"{count,plural, =1{# logrīks}zero{# logrīku}one{# logrīks}other{# logrīki}}"
"{count,plural, =1{# saīsne}zero{# saīšņu}one{# saīsne}other{# saīsnes}}"
"%1$s, %2$s"
@@ -185,6 +185,8 @@
"Privātā mape"
"Privātās mapes iestatījumi"
"Bloķēt/atbloķēt privāto mapi"
+
+
"Pāriet uz privāto mapi"
"Lietotņu instalēšana"
"Instalējiet lietotnes privātajā telpā."
diff --git a/res/values-mk/strings.xml b/res/values-mk/strings.xml
index 7dd8926c58..51b2b96bc6 100644
--- a/res/values-mk/strings.xml
+++ b/res/values-mk/strings.xml
@@ -40,14 +40,14 @@
"Додај на почетниот екран"
"Виџетот %1$s е додаден на почетниот екран"
"Предлози"
- "Подобрете го денот"
+
+
"Вести за вас"
"Вашата зона за релаксација"
"Достигнете ги целите за фитнес"
"Бидете во тек со временската прогноза"
"Можеби ќе ви се допадне и"
-
-
+ " виџети оддесно, „Пребарување“ и „Опции“ одлево"
"{count,plural, =1{# виџет}one{# виџет}other{# виџети}}"
"{count,plural, =1{# кратенка}one{# кратенка}other{# кратенки}}"
"%1$s, %2$s"
@@ -185,6 +185,8 @@
"Приватен"
"Поставки за „Приватен простор“"
"Заклучување/отклучување на „Приватен простор“"
+
+
"Префрлање на „Приватен простор“"
"Инсталирање апликации"
"Инсталирање апликации во „Приватен простор“"
diff --git a/res/values-ml/strings.xml b/res/values-ml/strings.xml
index 2abab01a74..3f515b8630 100644
--- a/res/values-ml/strings.xml
+++ b/res/values-ml/strings.xml
@@ -40,14 +40,14 @@
"ഹോം സ്ക്രീനിലേക്ക് ചേർക്കുക"
"%1$s വിജറ്റ് ഹോം സ്ക്രീനിലേക്ക് ചേർത്തു"
"നിർദ്ദേശങ്ങൾ"
- "കൂടുതൽ കാര്യക്ഷമമാകൂ"
+
+
"നിങ്ങൾക്കായുള്ള വാർത്ത"
"നിങ്ങൾക്ക് സുഖപ്രദമായ സ്ഥലം"
"ശാരീരികക്ഷമതയുമായി ബന്ധപ്പെട്ട ലക്ഷ്യങ്ങൾ കൈവരിക്കൂ"
"കാലാവസ്ഥ മുൻകൂട്ടി മനസ്സിലാക്കുക"
"നിങ്ങൾക്ക് ഇനിപ്പറയുന്നവ ഇഷ്ടമായേക്കാം"
-
-
+ "വലതുവശത്ത് വിജറ്റുകളും ഇടതുവശത്ത് തിരയൽ, ഓപ്ഷനുകൾ എന്നിവയും"
"{count,plural, =1{# വിജറ്റ്}other{# വിജറ്റുകൾ}}"
"{count,plural, =1{# കുറുക്കുവഴി}other{# കുറുക്കുവഴികൾ}}"
"%1$s, %2$s"
@@ -185,6 +185,8 @@
"സ്വകാര്യം"
"സ്വകാര്യ സ്പേസ് ക്രമീകരണം"
"സ്വകാര്യ സ്പേസ് ലോക്ക് ചെയ്യുക/അൺലോക്ക് ചെയ്യുക"
+
+
"പ്രൈവറ്റ് സ്പേസ് ട്രാൻസിഷനിംഗ്"
"ആപ്പുകൾ ഇൻസ്റ്റാൾ ചെയ്യുക"
"സ്വകാര്യ സ്പേസിലേക്ക് ആപ്പുകൾ ഇൻസ്റ്റാൾ ചെയ്യുക"
diff --git a/res/values-mn/strings.xml b/res/values-mn/strings.xml
index 71b7d4f08d..0dd648aecf 100644
--- a/res/values-mn/strings.xml
+++ b/res/values-mn/strings.xml
@@ -40,14 +40,14 @@
"Үндсэн нүүрэнд нэмэх"
"%1$s виджетийг үндсэн нүүрэнд нэмсэн"
"Зөвлөмжүүд"
- "Өдрөө сайхан болгоорой"
+
+
"Танд зориулсан мэдээ"
"Таны амралтын бүс"
"Фитнесийн зорилгодоо хүрээрэй"
"Цаг агаарын урьдчилсан мэдээлэлтэй байгаарай"
"Танд таалагдаж магадгүй"
-
-
+ "Баруун талд -н виджет, зүүн талд хайлт болон сонгуултууд байна"
"{count,plural, =1{# виджет}other{# виджет}}"
"{count,plural, =1{# товчлол}other{# товчлол}}"
"%1$s, %2$s"
@@ -185,6 +185,8 @@
"Хувийн"
"Private Space-н тохиргоо"
"Private Space-г түгжих/түгжээг тайлах"
+
+
"Private Space-н шилжилт"
"Аппуудыг суулгах"
"Хувийн орон зайд аппууд суулгана уу"
diff --git a/res/values-mr/strings.xml b/res/values-mr/strings.xml
index 7b1ceb975a..9d0f2bf510 100644
--- a/res/values-mr/strings.xml
+++ b/res/values-mr/strings.xml
@@ -40,14 +40,14 @@
"होम स्क्रीनवर जोडा"
"%1$s हे विजेट तुमच्या होम स्क्रीनवर जोडले आहे"
"सूचना"
- "तुमचा दिवस आणखी उत्साहवर्धक करा"
+
+
"तुमच्यासाठी बातम्या"
"तुमचा आरामदायक झोन"
"तुमची फिटनेस ध्येये गाठा"
"हवामानासंबंधित बातम्या आगामी मिळवा"
"तुम्हाला हेदेखील आवडू शकते"
-
-
+ "उजवीकडे विजेट, डावीकडे शोध आणि पर्याय"
"{count,plural, =1{# विजेट}other{# विजेट}}"
"{count,plural, =1{# शॉर्टकट}other{# शॉर्टकट}}"
"%1$s, %2$s"
@@ -185,6 +185,8 @@
"खाजगी"
"खाजगी स्पेस ची सेटिंग्ज"
"खाजगी स्पेस लॉक/अनलॉक करा"
+
+
"खाजगी स्पेस वर स्विच करणे"
"अॅप्स इंस्टॉल करा"
"अॅप्स खाजगी स्पेस मध्ये इंस्टॉल करा"
diff --git a/res/values-ms/strings.xml b/res/values-ms/strings.xml
index 94e89e0e50..83fd3eea6d 100644
--- a/res/values-ms/strings.xml
+++ b/res/values-ms/strings.xml
@@ -40,14 +40,14 @@
"Tambahkan pada skrin utama"
"Widget %1$s ditambahkan pada skrin utama"
"Cadangan"
- "Tingkatkan hari anda"
+
+
"Berita Untuk Anda"
"Zon Santai Anda"
"Capai Matlamat Kecergasan Anda"
"Ketahui Perkembangan Terkini Cuaca"
"Anda Mungkin Turut Menyukai"
-
-
+ "Widget pada sebelah kanan, carian dan pilihan pada sebelah kiri"
"{count,plural, =1{# widget}other{# widget}}"
"{count,plural, =1{# pintasan}other{# pintasan}}"
"%1$s, %2$s"
@@ -185,6 +185,8 @@
"Peribadi"
"Tetapan Ruang Peribadi"
"Kunci/Buka kunci Ruang Peribadi"
+
+
"Peralihan Ruang Peribadi"
"Pasang apl"
"Pasang apl pada Ruang Peribadi"
diff --git a/res/values-my/strings.xml b/res/values-my/strings.xml
index c10ddcd10b..c832434ad3 100644
--- a/res/values-my/strings.xml
+++ b/res/values-my/strings.xml
@@ -40,14 +40,14 @@
"ပင်မစာမျက်နှာတွင် ထည့်ရန်"
"%1$s ဝိဂျက်ကို ပင်မစာမျက်နှာတွင် ထည့်လိုက်ပြီ"
"အကြံပြုချက်"
- "သင့်နေ့ကို မွမ်းမံရန်"
+
+
"သင့်အတွက် သတင်းများ"
"သင်အနားယူသောနေရာ"
"သင့်ကြံ့ခိုင်ရေးပန်းတိုင်ဆီ သွားရန်"
"မိုးလေဝသကို ကြိုတင်ကာကွယ်ရန်"
"သင်နှစ်သက်နိုင်သောအရာများ"
-
-
+ " ဝိဂျက်များသည် ညာဘက်တွင်ရှိပြီး ရှာဖွေမှုနှင့် ရွေးစရာများသည် ဘယ်ဘက်တွင်ရှိသည်"
"{count,plural, =1{ဝိဂျက် # ခု}other{ဝိဂျက် # ခု}}"
"{count,plural, =1{ဖြတ်လမ်းလင့်ခ် # ခု}other{ဖြတ်လမ်းလင့်ခ် # ခု}}"
"%1$s၊ %2$s"
@@ -185,6 +185,8 @@
"သီးသန့်"
"သီးသန့်ချတ်ခန်း ဆက်တင်များ"
"သီးသန့်ချတ်ခန်း လော့ခ်ချ/ဖွင့်ရန်"
+
+
"သီးသန့်ချတ်ခန်း အပြောင်းအလဲ"
"အက်ပ်များ ထည့်သွင်းခြင်း"
"‘သီးသန့်နေရာ’ တွင် အက်ပ်များ ထည့်သွင်းနိုင်သည်"
diff --git a/res/values-nb/strings.xml b/res/values-nb/strings.xml
index ee73d51343..69697dc252 100644
--- a/res/values-nb/strings.xml
+++ b/res/values-nb/strings.xml
@@ -40,14 +40,14 @@
"Legg til på startskjermen"
"%1$s-modulen er lagt til på startskjermen"
"Forslag"
- "Få en bedre dag"
+
+
"Nyheter for deg"
"Avslappingssonen din"
"Nå treningsmålene dine"
"Hold deg i forkant av været"
"Kanskje du også liker"
-
-
+ " moduler til høyre, søk og alternativer til venstre"
"{count,plural, =1{# modul}other{# moduler}}"
"{count,plural, =1{# snarvei}other{# snarveier}}"
"%1$s, %2$s"
@@ -185,6 +185,8 @@
"Privat"
"Innstillinger for Private Space"
"Lås / lås opp Private Space"
+
+
"Private Space-overgang"
"Installer apper"
"Installer apper i privat område"
diff --git a/res/values-ne/strings.xml b/res/values-ne/strings.xml
index f061a821e5..c6d2e48928 100644
--- a/res/values-ne/strings.xml
+++ b/res/values-ne/strings.xml
@@ -40,14 +40,14 @@
"होम स्क्रिनमा राख्नुहोस्"
"होम स्क्रिनमा %1$s विजेट हालियो"
"सुझावहरू"
- "आफ्नो आजको पर्फर्मेन्स बढाउनुहोस्"
+
+
"तपाईंका निम्ति सिफारिस गरिएका समाचार"
"तपाईंको Chill Zone"
"आफूले तय गरेको तन्दुरुस्तीको लक्ष्यमा पुग्नुहोस्"
"मौसमको पूर्वानुमान प्राप्त गर्नुहोस्"
"तपाईंलाई निम्न कुराहरू पनि मन पर्न सक्छन्"
-
-
+ "दायाँ भागमा विजेटहरू, बायाँ भागमा खोज र विकल्पहरू"
"{count,plural, =1{# विजेट}other{# वटा विजेट}}"
"{count,plural, =1{# सर्टकट}other{# वटा सर्टकट}}"
"%1$s, %2$s"
@@ -185,6 +185,8 @@
"निजी"
"निजी स्पेससम्बन्धी सेटिङ"
"निजी स्पेस लक/अनलक गर्नुहोस्"
+
+
"निजी स्पेस ट्रान्जिसन गरिँदै छ"
"एपहरू इन्स्टल गर्नुहोस्"
"निजी स्पेसमा एपहरू इन्स्टल गर्नुहोस्"
diff --git a/res/values-nl/strings.xml b/res/values-nl/strings.xml
index 557fab3b57..c9d1e519cd 100644
--- a/res/values-nl/strings.xml
+++ b/res/values-nl/strings.xml
@@ -40,14 +40,14 @@
"Toevoegen aan startscherm"
"Widget %1$s toegevoegd aan startscherm"
"Suggesties"
- "Geef je dag een boost"
+
+
"Nieuws voor jou"
"Je chillzone"
"Behaal je fitnessdoelen"
"Blijf het weer een stap voor"
"Misschien ook interessant"
-
-
+ "-widgets aan de rechterkant, zoeken en opties aan de linkerkant"
"{count,plural, =1{# widget}other{# widgets}}"
"{count,plural, =1{# snelkoppeling}other{# snelkoppelingen}}"
"%1$s, %2$s"
@@ -185,6 +185,8 @@
"Privé"
"Instellingen voor privéruimte"
"Privéruimte vergrendelen/ontgrendelen"
+
+
"Overschakelen naar privéruimte"
"Apps installeren"
"Apps installeren in privégedeelte"
diff --git a/res/values-or/strings.xml b/res/values-or/strings.xml
index ed62a3576d..16cadba0a3 100644
--- a/res/values-or/strings.xml
+++ b/res/values-or/strings.xml
@@ -40,14 +40,14 @@
"ହୋମ ସ୍କ୍ରିନରେ ଯୋଗ କରନ୍ତୁ"
"%1$sର ୱିଜେଟ ହୋମ ସ୍କ୍ରିନରେ ଯୋଡ଼ାଗଲା"
"ପରାମର୍ଶଗୁଡ଼ିକ"
- "ଆପଣଙ୍କ ଦିନକୁ ବୁଷ୍ଟ କରନ୍ତୁ"
+
+
"ଆପଣଙ୍କ ପାଇଁ ନ୍ୟୁଜ"
"ଆପଣଙ୍କ ଚିଲ ଜୋନ"
"ଆପଣଙ୍କ ଫିଟନେସ ଲକ୍ଷ୍ୟରେ ପହଞ୍ଚନ୍ତୁ"
"ପାଣିପାଗ ବିଷୟରେ ଆଗୁଆ ସୂଚନା ପାଆନ୍ତୁ"
"ଆପଣ ମଧ୍ୟ ପସନ୍ଦ କରିପାରନ୍ତି"
-
-
+ "ଡାହାଣରେ ୱିଜେଟଗୁଡ଼ିକ ଅଛି, ବାମରେ ସର୍ଚ୍ଚ ଓ ବିକଳ୍ପଗୁଡ଼ିକ ଅଛି"
"{count,plural, =1{# ୱିଜେଟ}other{# ୱିଜେଟ}}"
"{count,plural, =1{#ଟି ସର୍ଟକଟ୍}other{#ଟି ସର୍ଟକଟ୍}}"
"%1$s, %2$s"
@@ -185,6 +185,8 @@
"ପ୍ରାଇଭେଟ"
"ପ୍ରାଇଭେଟ ସ୍ପେସ ସେଟିଂସ"
"ପ୍ରାଇଭେଟ ସ୍ପେସକୁ ଲକ/ଅନଲକ କରନ୍ତୁ"
+
+
"ପ୍ରାଇଭେଟ ସ୍ପେସ ଟ୍ରାଞ୍ଜିସନିଂ"
"ଆପ୍ ଇନଷ୍ଟଲ୍ କରନ୍ତୁ"
"ଆପ୍ସକୁ ପ୍ରାଇଭେଟ ସ୍ପେସରେ ଇନଷ୍ଟଲ କରନ୍ତୁ"
diff --git a/res/values-pa/strings.xml b/res/values-pa/strings.xml
index 661f035672..c83186d47b 100644
--- a/res/values-pa/strings.xml
+++ b/res/values-pa/strings.xml
@@ -40,14 +40,14 @@
"ਹੋਮ ਸਕ੍ਰੀਨ \'ਤੇ ਸ਼ਾਮਲ ਕਰੋ"
"%1$s ਵਿਜੇਟ ਨੂੰ ਹੋਮ ਸਕ੍ਰੀਨ \'ਤੇ ਸ਼ਾਮਲ ਕੀਤਾ ਗਿਆ"
"ਸੁਝਾਅ"
- "ਆਪਣੇ ਦਿਨ ਨੂੰ ਬੂਸਟ ਕਰੋ"
+
+
"ਤੁਹਾਡੇ ਲਈ ਖਬਰਾਂ"
"ਤੁਹਾਡੇ ਲਈ ਸਕੂਨਮਈ ਖੇਤਰ"
"ਆਪਣੇ ਫਿੱਟਨੈੱਸ ਸੰਬੰਧੀ ਟੀਚੇ ਹਾਸਲ ਕਰੋ"
"ਮੌਸਮ ਬਾਰੇ ਅੱਪ-ਟੂ-ਡੇਟ ਰਹੋ"
"ਸ਼ਾਇਦ ਤੁਸੀਂ ਇਹ ਵੀ ਪਸੰਦ ਕਰੋ"
-
-
+ " ਵਿਜੇਟ ਸੱਜੇ ਪਾਸੇ ਹਨ, ਖੋਜ ਵਿਜੇਟ ਅਤੇ ਹੋਰ ਵਿਕਲਪ ਖੱਬੇ ਪਾਸੇ ਹਨ"
"{count,plural, =1{# ਵਿਜੇਟ}one{# ਵਿਜੇਟ}other{# ਵਿਜੇਟ}}"
"{count,plural, =1{# ਸ਼ਾਰਟਕੱਟ}one{# ਸ਼ਾਰਟਕੱਟ}other{# ਸ਼ਾਰਟਕੱਟ}}"
"%1$s, %2$s"
@@ -185,6 +185,8 @@
"ਨਿੱਜੀ"
"ਨਿੱਜੀ ਸਪੇਸ ਸੰਬੰਧੀ ਸੈਟਿੰਗਾਂ"
"ਨਿੱਜੀ ਸਪੇਸ ਨੂੰ ਲਾਕ/ਅਣਲਾਕ ਕਰੋ"
+
+
"ਨਿੱਜੀ ਸਪੇਸ ਨੂੰ ਤਬਦੀਲ ਕਰਨਾ"
"ਐਪਾਂ ਸਥਾਪਤ ਕਰੋ"
"ਪ੍ਰਾਈਵੇਟ ਸਪੇਸ ਵਿੱਚ ਐਪਾਂ ਸਥਾਪਤ ਕਰੋ"
diff --git a/res/values-pl/strings.xml b/res/values-pl/strings.xml
index a2627ff659..4cf4beb502 100644
--- a/res/values-pl/strings.xml
+++ b/res/values-pl/strings.xml
@@ -40,14 +40,14 @@
"Dodaj do ekranu głównego"
"Widżet %1$s został dodany do ekranu głównego"
"Sugestie"
- "To Ci się przyda na co dzień"
+
+
"Wiadomości dla Ciebie"
"Strefa relaksu"
"Zadbaj o swoją formę"
"Nie daj się zaskoczyć pogodzie"
"To też może Cię zainteresować"
-
-
+ "Widżety () po prawej, wyszukiwanie i opcje po lewej"
"{count,plural, =1{# widżet}few{# widżety}many{# widżetów}other{# widżetu}}"
"{count,plural, =1{# skrót}few{# skróty}many{# skrótów}other{# skrótu}}"
"%1$s, %2$s"
@@ -185,6 +185,8 @@
"Prywatne"
"Ustawienia obszaru prywatnego"
"Zablokuj/odblokuj obszar prywatny"
+
+
"Przenoszenie obszaru prywatnego"
"Instalowanie aplikacji"
"Zainstaluj aplikacje w przestrzeni prywatnej"
diff --git a/res/values-pt-rPT/strings.xml b/res/values-pt-rPT/strings.xml
index 46ace14c4e..4304a5ab01 100644
--- a/res/values-pt-rPT/strings.xml
+++ b/res/values-pt-rPT/strings.xml
@@ -40,14 +40,14 @@
"Adicionar ao ecrã principal"
"Widget %1$s adicionado ao ecrã principal"
"Sugestões"
- "Melhorar o seu dia"
+
+
"Notícias para si"
"A sua zona de relaxamento"
"Atingir os seus objetivos de fitness"
"Ficar a par da meteorologia"
"Também poderá gostar de"
-
-
+ "Widgets de à direita, pesquisa e opções à esquerda"
"{count,plural, =1{# widget}other{# widgets}}"
"{count,plural, =1{# atalho}other{# atalhos}}"
"%1$s, %2$s"
@@ -185,6 +185,8 @@
"Privado"
"Definições do espaço privado"
"Bloquear/desbloquear espaço privado"
+
+
"Transição do espaço privado"
"Instalar apps"
"Instale apps no espaço privado"
diff --git a/res/values-pt/strings.xml b/res/values-pt/strings.xml
index f12bd4fed3..6256567783 100644
--- a/res/values-pt/strings.xml
+++ b/res/values-pt/strings.xml
@@ -23,7 +23,7 @@
"Trabalho"
"O app não está instalado."
"O app não está disponível"
- "App transferido por download desativado no modo de segurança"
+ "App baixado desativado no modo de segurança"
"Widgets desativados no modo de segurança"
"O atalho não está disponível"
"Início"
@@ -40,14 +40,14 @@
"Adicionar à tela inicial"
"Widget %1$s adicionado à tela inicial"
"Sugestões"
- "Melhore seu dia"
+
+
"Notícias para você"
"Sua zona de relaxamento"
"Alcance seus objetivos fitness"
"Fique por dentro da previsão do tempo"
"Você também pode gostar de"
-
-
+ "Widgets da à direita, pesquisa e opções à esquerda"
"{count,plural, =1{# widget}one{# widget}other{# widgets}}"
"{count,plural, =1{# atalho}one{# atalho}other{# atalhos}}"
"%1$s, %2$s"
@@ -185,6 +185,8 @@
"Particular"
"Configurações do Espaço particular"
"Bloquear/desbloquear o Espaço particular"
+
+
"Espaço particular em transição"
"Instalar apps"
"Instalar apps no espaço privado"
diff --git a/res/values-ro/strings.xml b/res/values-ro/strings.xml
index 93fb1b176d..3bc7d353f6 100644
--- a/res/values-ro/strings.xml
+++ b/res/values-ro/strings.xml
@@ -40,14 +40,14 @@
"Adaugă pe ecranul de pornire"
"Widgetul %1$s a fost adăugat pe ecranul de pornire"
"Sugestii"
- "Lucrează mai productiv"
+
+
"Știri pentru tine"
"Zona de relaxare"
"Atinge-ți obiectivele de fitness"
"Fii la curent cu prognoza meteo"
"S-ar putea să îți placă și"
-
-
+ "Widgeturi pentru în dreapta, căutare și opțiuni în stânga"
"{count,plural, =1{# widget}few{# widgeturi}other{# de widgeturi}}"
"{count,plural, =1{# comandă rapidă}few{# comenzi rapide}other{# de comenzi rapide}}"
"%1$s %2$s"
@@ -185,6 +185,8 @@
"Privat"
"Setări spațiu privat"
"Blochează / deblochează spațiul privat"
+
+
"Tranziție pentru spațiul privat"
"Instalează aplicații"
"Instalează aplicații în Spațiul privat"
diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml
index 4942eeb203..63c7f767f1 100644
--- a/res/values-ru/strings.xml
+++ b/res/values-ru/strings.xml
@@ -40,14 +40,14 @@
"Добавить на главный экран"
"Виджет \"%1$s\" добавлен на главный экран"
"Подсказки"
- "Эффективная работа"
+
+
"Новости для вас"
"Развлечение и общение"
"Ваши фитнес-цели"
"Прогноз погоды"
"Другие рекомендации"
-
-
+ "Виджеты приложения \"\" находятся справа, а панель поиска и настройки – слева"
"{count,plural, =1{# виджет}one{# виджет}few{# виджета}many{# виджетов}other{# виджета}}"
"{count,plural, =1{# ярлык}one{# ярлык}few{# ярлыка}many{# ярлыков}other{# ярлыка}}"
"%1$s, %2$s"
@@ -185,6 +185,8 @@
"Доступно только вам"
"Настройки личного пространства"
"Блокировка и разблокировка личного пространства"
+
+
"Переход к личному пространству"
"Установить приложения"
"Установить приложения в личном пространстве"
diff --git a/res/values-si/strings.xml b/res/values-si/strings.xml
index a0b54ebfc0..78d3249e32 100644
--- a/res/values-si/strings.xml
+++ b/res/values-si/strings.xml
@@ -40,14 +40,14 @@
"මුල් තිරය වෙත එක් කරන්න"
"%1$s විජට්ටුව මුල් පිටු තිරය වෙත එක් කරන ලදි"
"යෝජනා"
- "ඔබේ දවස වැඩි කරන්න"
+
+
"ඔබ වෙනුවෙන් පුවත්"
"ඔබේ නිවුණු කලාපය"
"ඔබේ යෝග්යතා ඉලක්ක ළඟා කර ගන්න"
"කාලගුණයට ඉදිරියෙන් සිටින්න"
"ඔබ මේවාට ද කැමති විය හැක"
-
-
+ "දකුණේ විජට්, වමේ සෙවීම සහ විකල්ප"
"{count,plural, =1{විජට් #}one{විජට් #}other{විජට් #}}"
"{count,plural, =1{කෙටි මං #}one{කෙටි මං #}other{කෙටි මං #}}"
"%1$s, %2$s"
@@ -185,6 +185,8 @@
"පෞද්ගලික"
"පෞද්ගලික අවකාශ සැකසීම්"
"පෞද්ගලික අවකාශය අගුළු දමන්න/අගුළු හරින්න"
+
+
"පෞද්ගලික අවකාශ සංක්රමණය"
"යෙදුම් ස්ථාපනය කරන්න"
"පෞද්ගලික අවකාශයට යෙදුම් ස්ථාපනය කරන්න"
diff --git a/res/values-sk/strings.xml b/res/values-sk/strings.xml
index 7853de169b..4cf5f7bc98 100644
--- a/res/values-sk/strings.xml
+++ b/res/values-sk/strings.xml
@@ -40,14 +40,14 @@
"Pridať na plochu"
"Na plochu bola pridaná miniaplikácia %1$s"
"Návrhy"
- "Zvýšte dnes svoju produktivitu"
+
+
"Vaše správy"
"Vaša komfortná zóna"
"Dosiahnite svoje kondičné ciele"
"Získavajte informácie o počasí v predstihu"
"Mohlo by sa vám páčiť"
-
-
+ "Miniaplikácie vpravo, vyhľadávanie a možnosti vľavo"
"{count,plural, =1{# miniaplikácia}few{# miniaplikácie}many{# widgets}other{# miniaplikácií}}"
"{count,plural, =1{# odkaz}few{# odkazy}many{# shortcuts}other{# odkazov}}"
"%1$s, %2$s"
@@ -185,6 +185,8 @@
"Súkromné"
"Nastavenia súkromného priestoru"
"Súkromný priestor zamykania a odomykania"
+
+
"Prechod súkromného priestoru"
"Inštalovať aplikácie"
"Inštalácia aplikácií v súkromnom priestore"
diff --git a/res/values-sl/strings.xml b/res/values-sl/strings.xml
index c1f6599b93..6fa10ba710 100644
--- a/res/values-sl/strings.xml
+++ b/res/values-sl/strings.xml
@@ -40,14 +40,14 @@
"Dodaj na začetni zaslon"
"Pripomoček »%1$s« je dodan na začetni zaslon."
"Predlogi"
- "Dajte pospešek svojemu dnevu"
+
+
"Novice za vas"
"Vaš kotiček za sprostitev"
"Dosegajte cilje glede telesne pripravljenosti"
"Bodite na tekočem z vremenom"
"Morda vam bo všeč tudi"
-
-
+ "Pripomočki na desni, iskanje in možnosti na levi"
"{count,plural, =1{# pripomoček}one{# pripomoček}two{# pripomočka}few{# pripomočki}other{# pripomočkov}}"
"{count,plural, =1{# bližnjica}one{# bližnjica}two{# bližnjici}few{# bližnjice}other{# bližnjic}}"
"%1$s, %2$s"
@@ -185,6 +185,8 @@
"Zasebno"
"Nastavitve zasebnega prostora"
"Zaklepanje/odklepanje zasebnega prostora"
+
+
"Preklapljanje zasebnega prostora"
"Nameščanje aplikacij"
"Nameščanje aplikacij v zasebni prostor"
diff --git a/res/values-sq/strings.xml b/res/values-sq/strings.xml
index 9dab5420da..edc1aad1de 100644
--- a/res/values-sq/strings.xml
+++ b/res/values-sq/strings.xml
@@ -40,14 +40,14 @@
"Shto në ekranin bazë"
"Miniaplikacioni %1$s u shtua në ekranin bazë"
"Sugjerime"
- "Përmirëso ditën tënde"
+
+
"Lajme për ty"
"Zona jote e qetësisë"
"Realizo objektivat e stërvitjes"
"Qëndro i informuar për motin"
"Gjithashtu mund të të pëlqejë"
-
-
+ "Miniaplikacionet e në të djathtë, kërkimi dhe opsionet në të majtë"
"{count,plural, =1{# miniaplikacion}other{# miniaplikacione}}"
"{count,plural, =1{# shkurtore}other{# shkurtore}}"
"%1$s, %2$s"
@@ -185,6 +185,8 @@
"Private"
"Cilësimet e \"Hapësirës private\""
"Kyç/Shkyç \"Hapësirën private\""
+
+
"Kalimi te \"Hapësira private\""
"Instalo aplikacionet"
"Instalo aplikacionet në hapësirën private"
diff --git a/res/values-sr/strings.xml b/res/values-sr/strings.xml
index 8553e58bb6..8550fa32b8 100644
--- a/res/values-sr/strings.xml
+++ b/res/values-sr/strings.xml
@@ -40,14 +40,14 @@
"Додај на почетни екран"
"Додали сте виџет %1$s на почетни екран"
"Предлози"
- "Побољшајте дан"
+
+
"Вести за вас"
"Зона за опуштање"
"Остварите фитнес циљеве"
"Будите у току са временским приликама"
"Можда ће вам се допасти и"
-
-
+ "Виџети са десне стране, претрага и опције са леве стране"
"{count,plural, =1{# виџет}one{# виџет}few{# виџета}other{# виџета}}"
"{count,plural, =1{# пречица}one{# пречица}few{# пречице}other{# пречица}}"
"%1$s, %2$s"
@@ -185,6 +185,8 @@
"Приватно"
"Подешавања приватног простора"
"Закључај/откључај приватни простор"
+
+
"Пренос приватног простора"
"Инсталирајте апликације"
"Инсталирај апликације у приватан простор"
diff --git a/res/values-sv/strings.xml b/res/values-sv/strings.xml
index 24cf747143..fae79c7260 100644
--- a/res/values-sv/strings.xml
+++ b/res/values-sv/strings.xml
@@ -40,14 +40,14 @@
"Lägg till på startskärmen"
"Widget för %1$s har lagts till på startskärmen"
"Förslag"
- "Få ut mer av din dag"
+
+
"Nyheter för dig"
"Koppla av"
"Nå dina träningsmål"
"Håll koll på vädret"
"Andra appar du kanske gillar"
-
-
+ "Widgetar för till höger, sökning och alternativ till vänster"
"{count,plural, =1{# widget}other{# widgetar}}"
"{count,plural, =1{# genväg}other{# genvägar}}"
"%1$s, %2$s"
@@ -185,6 +185,8 @@
"Privat"
"Inställningar för privat rum"
"Lås eller lås upp ditt privata rum"
+
+
"Överföring av privat rum"
"Installera appar"
"Installera appar i privat rum"
diff --git a/res/values-sw/strings.xml b/res/values-sw/strings.xml
index 4537d82551..693beaeaae 100644
--- a/res/values-sw/strings.xml
+++ b/res/values-sw/strings.xml
@@ -40,14 +40,14 @@
"Weka kwenye skrini ya kwanza"
"Umeongeza wijeti ya %1$s kwenye skrini ya kwanza"
"Mapendekezo"
- "Boresha siku yako"
+
+
"Habari Kwa Ajili Yako"
"Mahali Pako pa Kupumzika"
"Fikia Malengo Yako ya Siha"
"Pata Taarifa kuhusu Hali ya Hewa"
"Huenda Pia Ukapenda"
-
-
+ "Wijeti za ziko upande wa kulia, utafutaji na chaguo ziko upande wa kushoto"
"{count,plural, =1{Wijeti #}other{Wijeti #}}"
"{count,plural, =1{Njia # ya mkato}other{Njia # za mkato}}"
"%1$s, %2$s"
@@ -185,6 +185,8 @@
"Ya Faragha"
"Mipangilio ya Nafasi ya Faragha"
"Funga/Fungua Nafasi ya Faragha"
+
+
"Mabadiliko ya Nafasi ya Faragha"
"Sakinisha programu"
"Sakinisha programu kwenye Sehemu ya Faragha"
diff --git a/res/values-ta/strings.xml b/res/values-ta/strings.xml
index fede476192..a7c323e44b 100644
--- a/res/values-ta/strings.xml
+++ b/res/values-ta/strings.xml
@@ -40,14 +40,14 @@
"முகப்புத் திரையில் சேர்"
"%1$s விட்ஜெட் முகப்புத் திரையில் சேர்க்கப்பட்டது"
"பரிந்துரைகள்"
- "உங்கள் நாளை உற்சாகமாக்குங்கள்"
+
+
"உங்களுக்கான செய்திகள்"
"உங்கள் மனதுக்கு இதமானவை"
"உடற்பயிற்சி இலக்குகளை அடையுங்கள்"
"வானிலை குறித்து முன்கூட்டியே அறிந்திருங்கள்"
"நீங்கள் இவற்றையும் விரும்பக்கூடும்"
-
-
+ " விட்ஜெட்கள் வலதுபுறத்தில் உள்ளன, தேடல் மற்றும் விருப்பங்கள் இடதுபுறத்தில் உள்ளன"
"{count,plural, =1{# விட்ஜெட்}other{# விட்ஜெட்டுகள்}}"
"{count,plural, =1{# ஷார்ட்கட்}other{# ஷார்ட்கட்கள்}}"
"%1$s, %2$s"
@@ -185,6 +185,8 @@
"தனிப்பட்டது"
"தனிப்பட்ட சேமிப்பிட அமைப்புகள்"
"தனிப்பட்ட சேமிப்பிடத்தை லாக்/அன்லாக் செய்யும்"
+
+
"தனிப்பட்ட சேமிப்பிடத்திற்கு மாற்றுகிறது"
"ஆப்ஸை நிறுவுதல்"
"தனிப்பட்ட சேமிப்பிடத்தில் ஆப்ஸை நிறுவும்"
diff --git a/res/values-te/strings.xml b/res/values-te/strings.xml
index 9fcc88f101..f54a9f74f7 100644
--- a/res/values-te/strings.xml
+++ b/res/values-te/strings.xml
@@ -40,14 +40,14 @@
"మొదటి స్క్రీన్కు జోడించండి"
"మొదటి స్క్రీన్కు %1$s విడ్జెట్ జోడించబడింది"
"సూచనలు"
- "మీ రోజును బూస్ట్ చేయండి"
+
+
"మీ కోసం వార్తలు"
"మీరు ప్రశాంతంగా ఉండే ప్రదేశం"
"ఫిట్నెస్ లక్ష్యాలను చేరుకోండి"
"వాతావరణాన్ని ముందుగానే తెలుసుకోండి"
"మీరు వీటిని కూడా ఇష్టపడవచ్చు"
-
-
+ "కుడి వైపున విడ్జెట్లు, ఎడమ వైపున సెర్చ్, ఇతర ఆప్షన్లు"
"{count,plural, =1{# విడ్జెట్}other{# విడ్జెట్లు}}"
"{count,plural, =1{# షార్ట్కట్}other{# షార్ట్కట్లు}}"
"%1$s, %2$s"
@@ -185,6 +185,8 @@
"ప్రైవేట్"
"ప్రైవేట్ స్పేస్ సెట్టింగ్లు"
"ప్రైవేట్ స్పేస్ను లాక్/అన్లాక్ చేయండి"
+
+
"ప్రైవేట్ స్పేస్ కేటాయించడం జరుగుతుంది"
"యాప్లను ఇన్స్టాల్ చేయండి"
"ప్రైవేట్ స్పేస్కు యాప్లను ఇన్స్టాల్ చేయండి"
diff --git a/res/values-th/strings.xml b/res/values-th/strings.xml
index d189ed486b..db07dc2116 100644
--- a/res/values-th/strings.xml
+++ b/res/values-th/strings.xml
@@ -40,14 +40,14 @@
"เพิ่มลงในหน้าจอหลัก"
"เพิ่มวิดเจ็ต %1$s ลงในหน้าจอหลักแล้ว"
"คำแนะนำ"
- "เสริมสร้างวันของคุณ"
+
+
"ข่าวสารสำหรับคุณ"
"พื้นที่สบายๆ ของคุณ"
"บรรลุเป้าหมายการออกกำลังกาย"
"รู้สภาพอากาศล่วงหน้า"
"คุณอาจชอบ"
-
-
+ "วิดเจ็ตทางด้านขวา การค้นหาและตัวเลือกทางด้านซ้าย"
"{count,plural, =1{วิดเจ็ต # รายการ}other{วิดเจ็ต # รายการ}}"
"{count,plural, =1{ทางลัด # รายการ}other{ทางลัด # รายการ}}"
"%1$s, %2$s"
@@ -185,6 +185,8 @@
"ส่วนตัว"
"การตั้งค่าพื้นที่ส่วนตัว"
"ล็อก/ปลดล็อกพื้นที่ส่วนตัว"
+
+
"การเปลี่ยนไปใช้พื้นที่ส่วนตัว"
"ติดตั้งแอป"
"ติดตั้งแอปไปยังพื้นที่ส่วนตัว"
diff --git a/res/values-tl/strings.xml b/res/values-tl/strings.xml
index 3dedd43763..c1d51b5147 100644
--- a/res/values-tl/strings.xml
+++ b/res/values-tl/strings.xml
@@ -40,14 +40,14 @@
"Idagdag sa home screen"
"Idinagdag sa home screen ang widget na %1$s"
"Mga Suhestyon"
- "I-boost ang iyong araw"
+
+
"Balita para sa Iyo"
"Ang Iyong Chill Zone"
"Makamit ang Iyong Mga Layunin sa Fitness"
"Manatiling Handa sa Lagay ng Panahon"
"Baka Magustuhan Mo Rin"
-
-
+ "Mga widget ng sa kanan, paghahanap at mga opsyon sa kaliwa"
"{count,plural, =1{# widget}one{# widget}other{# na widget}}"
"{count,plural, =1{# shortcut}one{# shortcut}other{# na shortcut}}"
"%1$s, %2$s"
@@ -185,6 +185,8 @@
"Pribado"
"Mga Setting ng Pribadong Space"
"I-lock/I-unlock ang Pribadong Space"
+
+
"Pag-transition ng Pribadong Space"
"Mag-install ng mga app"
"Mag-install ng mga app sa Pribadong Space"
diff --git a/res/values-tr/strings.xml b/res/values-tr/strings.xml
index 561f82ed2d..b5807f5eaf 100644
--- a/res/values-tr/strings.xml
+++ b/res/values-tr/strings.xml
@@ -40,14 +40,14 @@
"Ana ekrana ekle"
"%1$s widget\'ı ana ekrana eklendi"
"Öneriler"
- "Gününüzü canlandırın"
+
+
"Size özel haberler"
"Huzur alanınız"
"Fitness hedeflerinize ulaşın"
"Havanın durumu sizi şaşırtmasın"
"Şunları da beğenebilirsiniz"
-
-
+ " widget\'ları sağda, arama ve seçenekler solda"
"{count,plural, =1{# widget}other{# widget}}"
"{count,plural, =1{# kısayol}other{# kısayol}}"
"%1$s, %2$s"
@@ -185,6 +185,8 @@
"Gizli"
"Gizli Alan Ayarları"
"Gizli Alanı Kilitleyin/Kilidini Açın"
+
+
"Gizli Alana Geçiş"
"Uygulamaları yükleme"
"Uygulamaları özel alana yükleyin"
diff --git a/res/values-uk/strings.xml b/res/values-uk/strings.xml
index 83727f84fb..3c9852bf35 100644
--- a/res/values-uk/strings.xml
+++ b/res/values-uk/strings.xml
@@ -40,14 +40,14 @@
"Додати на головний екран"
"Віджет %1$s додано на головний екран"
"Пропозиції"
- "Підвищуйте свою продуктивність"
+
+
"Новини для вас"
"Ваша зона розваг"
"Досягайте своїх фітнес-цілей"
"Завчасно дізнавайтеся про зміни погоди"
"Вам також може сподобатися"
-
-
+ ": віджети праворуч, пошук і опції ліворуч"
"{count,plural, =1{# віджет}one{# віджет}few{# віджети}many{# віджетів}other{# віджета}}"
"{count,plural, =1{# ярлик}one{# ярлик}few{# ярлики}many{# ярликів}other{# ярлика}}"
"%1$s, %2$s"
@@ -185,6 +185,8 @@
"Приватні"
"Налаштування приватного простору"
"Заблокувати/розблокувати приватний простір"
+
+
"Перехід у приватний простір"
"Установити додатки"
"Установити додатки в особистому просторі"
diff --git a/res/values-ur/strings.xml b/res/values-ur/strings.xml
index c38fde347b..2690a4cfb2 100644
--- a/res/values-ur/strings.xml
+++ b/res/values-ur/strings.xml
@@ -40,14 +40,14 @@
"ہوم اسکرین میں شامل کریں"
"%1$s ویجیٹ کو ہوم اسکرین میں شامل کیا گیا"
"تجاویز"
- "اپنی آج کی کارکردگی کو بوسٹ کریں"
+
+
"آپ کے لیے خبریں"
"آپ کا آرام دہ زون"
"اپنی تندرستی کے مقاصد حاصل کریں"
"موسم سے باخبر رہیں"
"آپ کو یہ بھی پسند آ سکتا ہے"
-
-
+ " دائیں طرف وجیٹس، بائیں طرف تلاش اور اختیارات"
"{count,plural, =1{# ویجیٹ}other{# ویجیٹس}}"
"{count,plural, =1{# شارٹ کٹ}other{# شارٹ کٹس}}"
"%1$s، %2$s"
@@ -185,6 +185,8 @@
"نجی"
"نجی اسپیس کی ترتیبات"
"نجی اسپیس کو مقفل کریں/غیر مقفل کریں"
+
+
"نجی اسپیس کی منتقلی"
"ایپس انسٹال کریں"
"پرائیویٹ اسپیس میں ایپس انسٹال کریں"
diff --git a/res/values-uz/strings.xml b/res/values-uz/strings.xml
index 5c885cdf15..31bdd3cff1 100644
--- a/res/values-uz/strings.xml
+++ b/res/values-uz/strings.xml
@@ -40,14 +40,14 @@
"Bosh ekranga chiqarish"
"%1$s vidjeti bosh ekranga qoʻshildi"
"Takliflar"
- "Kuningizni yaxshilang"
+
+
"Siz uchun yangiliklar"
"Sokin hududingiz"
"Fitness maqsadlaringizga erishing"
"Doim ob-havodan oldinda yuring"
"Sizga yoqishi mumkin"
-
-
+ " vidjetlari oʻngda, qidiruv va sozlamalar chapda"
"{count,plural, =1{# ta vidjet}other{# ta vidjet}}"
"{count,plural, =1{# ta yorliq}other{# ta yorliq}}"
"%1$s, %2$s"
@@ -185,6 +185,8 @@
"Yopiq"
"Shaxsiy xona sozlamalari"
"Shaxsiy xonani ochish/qulflash"
+
+
"Maxfiy joyga almashtirish"
"Ilovalar oʻrnatish"
"Ilovalarni Maxfiy makonga oʻrnatish"
diff --git a/res/values-vi/strings.xml b/res/values-vi/strings.xml
index cf667296db..24b2cce2ac 100644
--- a/res/values-vi/strings.xml
+++ b/res/values-vi/strings.xml
@@ -40,14 +40,14 @@
"Thêm vào màn hình chính"
"Đã thêm tiện ích %1$s vào màn hình chính"
"Nội dung đề xuất"
- "Thúc đẩy hiệu quả cho ngày của bạn"
+
+
"Tin tức cho bạn"
"Giai điệu thư giãn của bạn"
"Đạt được mục tiêu tập thể dục"
"Luôn nắm bắt tình hình thời tiết"
"Có thể bạn cũng thích"
-
-
+ "Tiện ích ở bên phải, công cụ tìm kiếm và tuỳ chọn ở bên trái"
"{count,plural, =1{# tiện ích}other{# tiện ích}}"
"{count,plural, =1{# lối tắt}other{# lối tắt}}"
"%1$s, %2$s"
@@ -185,6 +185,8 @@
"Riêng tư"
"Cài đặt không gian riêng tư"
"Khoá/mở khoá không gian riêng tư"
+
+
"Chuyển đổi sang không gian riêng tư"
"Cài đặt ứng dụng"
"Cài đặt ứng dụng vào Không gian riêng tư"
diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml
index 5c9f7ac9bc..994c976755 100644
--- a/res/values-zh-rCN/strings.xml
+++ b/res/values-zh-rCN/strings.xml
@@ -40,14 +40,14 @@
"添加到主屏幕"
"已将“%1$s”微件添加到主屏幕"
"建议"
- "您的日常必备"
+
+
"更多相关新闻"
"您的休闲区"
"达成您的健身目标"
"天气早知道"
"您可能还会喜欢"
-
-
+ "右边是微件,左边是搜索功能和选项"
"{count,plural, =1{# 个微件}other{# 个微件}}"
"{count,plural, =1{# 个快捷方式}other{# 个快捷方式}}"
"%1$s,%2$s"
@@ -185,6 +185,8 @@
"私密"
"私密空间设置"
"锁定/解锁私密空间"
+
+
"私密空间转换"
"安装应用"
"将应用安装到私密空间"
diff --git a/res/values-zh-rHK/strings.xml b/res/values-zh-rHK/strings.xml
index e0e07c1d1a..2a27de30d6 100644
--- a/res/values-zh-rHK/strings.xml
+++ b/res/values-zh-rHK/strings.xml
@@ -40,14 +40,14 @@
"加去主畫面"
"已經將「%1$s」小工具加咗去主畫面"
"建議"
- "日常必備"
+
+
"你的專屬新聞"
"放鬆專區"
"向健身目標邁進"
"隨時掌握天氣資料"
"相關推薦"
-
-
+ "右邊係「」小工具,左邊係搜尋功能同選項"
"{count,plural, =1{# 個小工具}other{# 個小工具}}"
"{count,plural, =1{# 個捷徑}other{# 個捷徑}}"
"%1$s、%2$s"
@@ -185,6 +185,8 @@
"私人"
"「私人空間」設定"
"鎖定/解鎖「私人空間」"
+
+
"轉為「私人空間」"
"安裝應用程式"
"將應用程式安裝在「私人空間」中"
diff --git a/res/values-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml
index bd46e263f8..e802d9ac14 100644
--- a/res/values-zh-rTW/strings.xml
+++ b/res/values-zh-rTW/strings.xml
@@ -40,14 +40,14 @@
"新增至主畫面"
"已將「%1$s」小工具新增到主畫面"
"建議"
- "日常必備"
+
+
"你的專屬新聞"
"放鬆專區"
"達成健身目標"
"隨時掌握天氣資訊"
"你可能也會喜歡的內容"
-
-
+ "右邊是「」小工具,左邊是搜尋功能和選項"
"{count,plural, =1{# 項小工具}other{# 項小工具}}"
"{count,plural, =1{# 個捷徑}other{# 個捷徑}}"
"%1$s、%2$s"
@@ -185,6 +185,8 @@
"私人"
"私人空間設定"
"鎖定/取消鎖定私人空間"
+
+
"轉換私人空間狀態"
"安裝應用程式"
"將應用程式安裝在私人空間中"
diff --git a/res/values-zu/strings.xml b/res/values-zu/strings.xml
index e5c19248d5..b93953595d 100644
--- a/res/values-zu/strings.xml
+++ b/res/values-zu/strings.xml
@@ -40,14 +40,14 @@
"Faka kusikrini sasekhaya"
"Iwijethi ye-%1$s yengezwe kusikrini sasekhaya"
"Iziphakamiso"
- "Thuthukisa usuku lwakho"
+
+
"Izindaba Zakho"
"Indawo Ozipholela Kuyo"
"Finyelela Imigomo Yakho Yokufaneleka"
"Hlale Wazi Ngesimo Sezulu"
"Ungase Futhi Uthande"
-
-
+ "Amawijethi okuthi kwesokudla, ukusesha nokukhethwayo kwesobunxele"
"{count,plural, =1{iwijethi #}one{amawijethi #}other{amawijethi #}}"
"{count,plural, =1{isinqamuleli #}one{izinqamuleli #}other{izinqamuleli #}}"
"%1$s, %2$s"
@@ -185,6 +185,8 @@
"Okuyimfihlo"
"Amasethingi Esikhala Esiyimfihlo"
"Khiya/Vula Isikhala Esiyimfihlo"
+
+
"Ukuguqulwa Kwendawo Yangasese"
"Faka ama-app"
"Faka ama-app Endaweni Engasese"
diff --git a/src/com/android/launcher3/Alarm.java b/src/com/android/launcher3/Alarm.java
index e4aebf606d..fb8088c13b 100644
--- a/src/com/android/launcher3/Alarm.java
+++ b/src/com/android/launcher3/Alarm.java
@@ -17,6 +17,7 @@
package com.android.launcher3;
import android.os.Handler;
+import android.os.Looper;
import android.os.SystemClock;
public class Alarm implements Runnable{
@@ -33,7 +34,11 @@ public class Alarm implements Runnable{
private long mLastSetTimeout;
public Alarm() {
- mHandler = new Handler();
+ this(Looper.myLooper());
+ }
+
+ public Alarm(Looper looper) {
+ mHandler = new Handler(looper);
}
public void setOnAlarmListener(OnAlarmListener alarmListener) {
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index edfef5eaf2..1ab6222c98 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -28,6 +28,7 @@ import static com.android.launcher3.AbstractFloatingView.TYPE_ICON_SURFACE;
import static com.android.launcher3.AbstractFloatingView.TYPE_REBIND_SAFE;
import static com.android.launcher3.AbstractFloatingView.getTopOpenViewWithType;
import static com.android.launcher3.Flags.enableAddAppWidgetViaConfigActivityV2;
+import static com.android.launcher3.Flags.enableWorkspaceInflation;
import static com.android.launcher3.LauncherAnimUtils.HOTSEAT_SCALE_PROPERTY_FACTORY;
import static com.android.launcher3.LauncherAnimUtils.SCALE_INDEX_WIDGET_TRANSITION;
import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_EXIT_DELAY;
@@ -1485,8 +1486,8 @@ public class Launcher extends StatefulActivity
CellPos presenterPos = getCellPosMapper().mapModelToPresenter(itemInfo);
if (showPendingWidget) {
launcherInfo.restoreStatus = LauncherAppWidgetInfo.FLAG_UI_NOT_READY;
- PendingAppWidgetHostView pendingAppWidgetHostView =
- new PendingAppWidgetHostView(this, launcherInfo, appWidgetInfo);
+ PendingAppWidgetHostView pendingAppWidgetHostView = new PendingAppWidgetHostView(
+ this, mAppWidgetHolder, launcherInfo, appWidgetInfo);
pendingAppWidgetHostView.setPreviewBitmap(widgetPreviewBitmap);
hostView = pendingAppWidgetHostView;
} else if (hostView instanceof PendingAppWidgetHostView) {
@@ -2187,17 +2188,23 @@ public class Launcher extends StatefulActivity
*/
@Override
public void bindItems(final List items, final boolean forceAnimateIcons) {
- bindItems(items.stream().map(i -> Pair.create(
+ bindInflatedItems(items.stream().map(i -> Pair.create(
i, getItemInflater().inflateItem(i, getModelWriter()))).toList(),
forceAnimateIcons ? new AnimatorSet() : null);
}
+ @Override
+ public void bindInflatedItems(List> items) {
+ bindInflatedItems(items, null);
+ }
+
/**
* Bind all the items in the map, ignoring any null views
*
* @param boundAnim if non-null, uses it to create and play the bounce animation for added views
*/
- public void bindItems(List> shortcuts, @Nullable AnimatorSet boundAnim) {
+ public void bindInflatedItems(
+ List> shortcuts, @Nullable AnimatorSet boundAnim) {
// Get the list of added items and intersect them with the set of items here
Workspace> workspace = mWorkspace;
int newItemsScreenId = -1;
@@ -2222,10 +2229,13 @@ public class Launcher extends StatefulActivity
}
}
- final View view = e.second;
+ View view = e.second;
if (view == null) {
continue;
}
+ if (enableWorkspaceInflation() && view instanceof LauncherAppWidgetHostView lv) {
+ view = getAppWidgetHolder().attachViewToHostAndGetAttachedView(lv);
+ }
workspace.addInScreenFromBind(view, item);
if (boundAnim != null) {
// Animate all the applications up now
@@ -2324,9 +2334,9 @@ public class Launcher extends StatefulActivity
@Override
public void onInitialBindComplete(IntSet boundPages, RunnableList pendingTasks,
- int workspaceItemCount, boolean isBindSync) {
- mModelCallbacks.onInitialBindComplete(boundPages, pendingTasks, workspaceItemCount,
- isBindSync);
+ RunnableList onCompleteSignal, int workspaceItemCount, boolean isBindSync) {
+ mModelCallbacks.onInitialBindComplete(boundPages, pendingTasks, onCompleteSignal,
+ workspaceItemCount, isBindSync);
}
/**
@@ -3057,6 +3067,7 @@ public class Launcher extends StatefulActivity
return super.getStatsLogManager().withDefaultInstanceId(mAllAppsSessionLogId);
}
+ @Override
public ItemInflater getItemInflater() {
return mItemInflater;
}
diff --git a/src/com/android/launcher3/ModelCallbacks.kt b/src/com/android/launcher3/ModelCallbacks.kt
index 9867556136..9b65a310eb 100644
--- a/src/com/android/launcher3/ModelCallbacks.kt
+++ b/src/com/android/launcher3/ModelCallbacks.kt
@@ -72,6 +72,7 @@ class ModelCallbacks(private var launcher: Launcher) : BgDataModel.Callbacks {
override fun onInitialBindComplete(
boundPages: LIntSet,
pendingTasks: RunnableList,
+ onCompleteSignal: RunnableList,
workspaceItemCount: Int,
isBindSync: Boolean
) {
@@ -99,7 +100,14 @@ class ModelCallbacks(private var launcher: Launcher) : BgDataModel.Callbacks {
}
}
pendingExecutor = executor
- executor.attachTo(launcher)
+
+ if (Flags.enableWorkspaceInflation()) {
+ // Finish the executor as soon as the pending inflation is completed
+ onCompleteSignal.add(executor::markCompleted)
+ } else {
+ // Pending executor is already completed, wait until first draw to run the tasks
+ executor.attachTo(launcher)
+ }
launcher.bindComplete(workspaceItemCount, isBindSync)
}
@@ -409,4 +417,6 @@ class ModelCallbacks(private var launcher: Launcher) : BgDataModel.Callbacks {
}
fun getIsFirstPagePinnedItemEnabled(): Boolean = isFirstPagePinnedItemEnabled
+
+ override fun getItemInflater() = launcher.itemInflater
}
diff --git a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
index a846e683ee..e861d38733 100644
--- a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
+++ b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
@@ -440,7 +440,7 @@ public class LauncherAccessibilityDelegate extends BaseAccessibilityDelegate view.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED)));
}
- mContext.bindItems(Collections.singletonList(Pair.create(item, view)), anim);
+ mContext.bindInflatedItems(Collections.singletonList(Pair.create(item, view)), anim);
}
/**
diff --git a/src/com/android/launcher3/allapps/AllAppsStore.java b/src/com/android/launcher3/allapps/AllAppsStore.java
index 009a2aa6b7..9623709348 100644
--- a/src/com/android/launcher3/allapps/AllAppsStore.java
+++ b/src/com/android/launcher3/allapps/AllAppsStore.java
@@ -40,6 +40,7 @@ import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
+import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArrayList;
@@ -138,12 +139,22 @@ public class AllAppsStore {
/**
* Returns {@link AppInfo} if any apps matches with provided {@link ComponentKey}, otherwise
* null.
+ *
+ * Uses {@link AppInfo#COMPONENT_KEY_COMPARATOR} as a default comparator.
*/
@Nullable
public AppInfo getApp(ComponentKey key) {
+ return getApp(key, COMPONENT_KEY_COMPARATOR);
+ }
+
+ /**
+ * Generic version of {@link #getApp(ComponentKey)} that allows comparator to be specified.
+ */
+ @Nullable
+ public AppInfo getApp(ComponentKey key, Comparator comparator) {
mTempInfo.componentName = key.componentName;
mTempInfo.user = key.user;
- int index = Arrays.binarySearch(mApps, mTempInfo, COMPONENT_KEY_COMPARATOR);
+ int index = Arrays.binarySearch(mApps, mTempInfo, comparator);
return index < 0 ? null : mApps[index];
}
diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java
index f013126409..ec9c27dfab 100644
--- a/src/com/android/launcher3/folder/Folder.java
+++ b/src/com/android/launcher3/folder/Folder.java
@@ -40,6 +40,7 @@ import android.graphics.Path;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.GradientDrawable;
+import android.os.Looper;
import android.text.InputType;
import android.text.Selection;
import android.text.TextUtils;
@@ -165,10 +166,10 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo
private static final Rect sTempRect = new Rect();
private static final int MIN_FOLDERS_FOR_HARDWARE_OPTIMIZATION = 10;
- private final Alarm mReorderAlarm = new Alarm();
- private final Alarm mOnExitAlarm = new Alarm();
- private final Alarm mOnScrollHintAlarm = new Alarm();
- final Alarm mScrollPauseAlarm = new Alarm();
+ private final Alarm mReorderAlarm = new Alarm(Looper.getMainLooper());
+ private final Alarm mOnExitAlarm = new Alarm(Looper.getMainLooper());
+ private final Alarm mOnScrollHintAlarm = new Alarm(Looper.getMainLooper());
+ final Alarm mScrollPauseAlarm = new Alarm(Looper.getMainLooper());
final ArrayList mItemsInReadingOrder = new ArrayList();
diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java
index 284b31e971..ee0d5fce24 100644
--- a/src/com/android/launcher3/folder/FolderIcon.java
+++ b/src/com/android/launcher3/folder/FolderIcon.java
@@ -32,6 +32,7 @@ import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
+import android.os.Looper;
import android.util.AttributeSet;
import android.util.Property;
import android.view.LayoutInflater;
@@ -121,7 +122,7 @@ public class FolderIcon extends FrameLayout implements FolderListener, IconLabel
boolean mAnimating = false;
- private Alarm mOpenAlarm = new Alarm();
+ private Alarm mOpenAlarm = new Alarm(Looper.getMainLooper());
private boolean mForceHideDot;
@ViewDebug.ExportedProperty(category = "launcher", deepExport = true)
diff --git a/src/com/android/launcher3/icons/IconCache.java b/src/com/android/launcher3/icons/IconCache.java
index ee66a60c03..8e73660ae0 100644
--- a/src/com/android/launcher3/icons/IconCache.java
+++ b/src/com/android/launcher3/icons/IconCache.java
@@ -37,6 +37,7 @@ import android.content.pm.ShortcutInfo;
import android.database.Cursor;
import android.database.sqlite.SQLiteException;
import android.graphics.drawable.Drawable;
+import android.os.Looper;
import android.os.Process;
import android.os.Trace;
import android.os.UserHandle;
@@ -44,6 +45,7 @@ import android.text.TextUtils;
import android.util.Log;
import android.util.SparseArray;
+import androidx.annotation.AnyThread;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
@@ -65,7 +67,6 @@ import com.android.launcher3.shortcuts.ShortcutKey;
import com.android.launcher3.util.CancellableTask;
import com.android.launcher3.util.InstantAppResolver;
import com.android.launcher3.util.PackageUserKey;
-import com.android.launcher3.util.Preconditions;
import com.android.launcher3.widget.WidgetSections;
import com.android.launcher3.widget.WidgetSections.WidgetSection;
@@ -173,9 +174,9 @@ public class IconCache extends BaseIconCache {
*
* @return a request ID that can be used to cancel the request.
*/
+ @AnyThread
public CancellableTask updateIconInBackground(final ItemInfoUpdateReceiver caller,
final ItemInfoWithIcon info) {
- Preconditions.assertUIThread();
Supplier task;
if (info instanceof AppInfo || info instanceof WorkspaceItemInfo) {
task = () -> {
@@ -193,13 +194,19 @@ public class IconCache extends BaseIconCache {
return mCancelledTask;
}
- if (mPendingIconRequestCount <= 0) {
- MODEL_EXECUTOR.setThreadPriority(Process.THREAD_PRIORITY_FOREGROUND);
+ Runnable endRunnable;
+ if (Looper.myLooper() == Looper.getMainLooper()) {
+ if (mPendingIconRequestCount <= 0) {
+ MODEL_EXECUTOR.setThreadPriority(Process.THREAD_PRIORITY_FOREGROUND);
+ }
+ mPendingIconRequestCount++;
+ endRunnable = this::onIconRequestEnd;
+ } else {
+ endRunnable = () -> { };
}
- mPendingIconRequestCount++;
CancellableTask request = new CancellableTask<>(
- task, MAIN_EXECUTOR, caller::reapplyItemInfo, this::onIconRequestEnd);
+ task, MAIN_EXECUTOR, caller::reapplyItemInfo, endRunnable);
Utilities.postAsyncCallback(mWorkerHandler, request);
return request;
}
diff --git a/src/com/android/launcher3/model/BaseLauncherBinder.java b/src/com/android/launcher3/model/BaseLauncherBinder.java
index 9b2344d2ad..fa2a1b01c7 100644
--- a/src/com/android/launcher3/model/BaseLauncherBinder.java
+++ b/src/com/android/launcher3/model/BaseLauncherBinder.java
@@ -16,20 +16,25 @@
package com.android.launcher3.model;
+import static com.android.launcher3.Flags.enableWorkspaceInflation;
import static com.android.launcher3.config.FeatureFlags.ENABLE_SMARTSPACE_REMOVAL;
import static com.android.launcher3.model.ItemInstallQueue.FLAG_LOADER_RUNNING;
import static com.android.launcher3.model.ModelUtils.filterCurrentWorkspaceItems;
+import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
import android.os.Process;
import android.os.Trace;
import android.util.Log;
+import android.util.Pair;
+import android.view.View;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherModel.CallbackTask;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.Workspace;
+import com.android.launcher3.celllayout.CellPosMapper;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.model.BgDataModel.Callbacks;
import com.android.launcher3.model.BgDataModel.FixedContainerItems;
@@ -38,6 +43,7 @@ import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.LauncherAppWidgetInfo;
import com.android.launcher3.util.IntArray;
import com.android.launcher3.util.IntSet;
+import com.android.launcher3.util.ItemInflater;
import com.android.launcher3.util.LooperExecutor;
import com.android.launcher3.util.LooperIdleLock;
import com.android.launcher3.util.PackageUserKey;
@@ -279,8 +285,8 @@ public abstract class BaseLauncherBinder {
// Separate the items that are on the current screen, and all the other remaining items
ArrayList currentWorkspaceItems = new ArrayList<>();
ArrayList otherWorkspaceItems = new ArrayList<>();
- ArrayList currentAppWidgets = new ArrayList<>();
- ArrayList otherAppWidgets = new ArrayList<>();
+ ArrayList currentAppWidgets = new ArrayList<>();
+ ArrayList otherAppWidgets = new ArrayList<>();
filterCurrentWorkspaceItems(currentScreenIds, mWorkspaceItems, currentWorkspaceItems,
otherWorkspaceItems);
@@ -304,8 +310,8 @@ public abstract class BaseLauncherBinder {
executeCallbacksTask(c -> c.bindScreens(mOrderedScreenIds), mUiExecutor);
// Load items on the current page.
- bindWorkspaceItems(currentWorkspaceItems, mUiExecutor);
- bindAppWidgets(currentAppWidgets, mUiExecutor);
+ bindItemsInChunks(currentWorkspaceItems, ITEMS_CHUNK, mUiExecutor);
+ bindItemsInChunks(currentAppWidgets, 1, mUiExecutor);
if (!FeatureFlags.CHANGE_MODEL_DELEGATE_LOADING_ORDER.get()) {
mExtraItems.forEach(item ->
executeCallbacksTask(c -> c.bindExtraContainerItems(item), mUiExecutor));
@@ -313,8 +319,41 @@ public abstract class BaseLauncherBinder {
RunnableList pendingTasks = new RunnableList();
Executor pendingExecutor = pendingTasks::add;
- bindWorkspaceItems(otherWorkspaceItems, pendingExecutor);
- bindAppWidgets(otherAppWidgets, pendingExecutor);
+
+ RunnableList onCompleteSignal = new RunnableList();
+
+ if (enableWorkspaceInflation()) {
+ MODEL_EXECUTOR.execute(() -> {
+ setupPendingBind(otherWorkspaceItems, otherAppWidgets, currentScreenIds,
+ pendingExecutor);
+
+ // Wait for the async inflation to complete and then notify the completion
+ // signal on UI thread.
+ MAIN_EXECUTOR.execute(onCompleteSignal::executeAllAndDestroy);
+ });
+ } else {
+ setupPendingBind(
+ otherWorkspaceItems, otherAppWidgets, currentScreenIds, pendingExecutor);
+ onCompleteSignal.executeAllAndDestroy();
+ }
+
+ executeCallbacksTask(
+ c -> {
+ if (!enableWorkspaceInflation()) {
+ MODEL_EXECUTOR.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
+ }
+ c.onInitialBindComplete(currentScreenIds, pendingTasks, onCompleteSignal,
+ workspaceItemCount, isBindSync);
+ }, mUiExecutor);
+ }
+
+ private void setupPendingBind(
+ List otherWorkspaceItems,
+ List otherAppWidgets,
+ IntSet currentScreenIds,
+ Executor pendingExecutor) {
+ bindItemsInChunks(otherWorkspaceItems, ITEMS_CHUNK, pendingExecutor);
+ bindItemsInChunks(otherAppWidgets, 1, pendingExecutor);
StringCache cacheClone = mBgDataModel.stringCache.clone();
executeCallbacksTask(c -> c.bindStringCache(cacheClone), pendingExecutor);
@@ -326,38 +365,51 @@ public abstract class BaseLauncherBinder {
ItemInstallQueue.INSTANCE.get(mApp.getContext())
.resumeModelPush(FLAG_LOADER_RUNNING);
});
-
- executeCallbacksTask(
- c -> {
- MODEL_EXECUTOR.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
- c.onInitialBindComplete(
- currentScreenIds, pendingTasks, workspaceItemCount, isBindSync);
- }, mUiExecutor);
}
- private void bindWorkspaceItems(
- final ArrayList workspaceItems, final Executor executor) {
+ /**
+ * Tries to inflate the items asynchronously and bind. Returns true on success or false if
+ * async-binding is not supported in this case.
+ */
+ private boolean inflateAsyncAndBind(List items, Executor executor) {
+ if (!enableWorkspaceInflation()) {
+ return false;
+ }
+ ItemInflater inflater = mCallbacks.getItemInflater();
+ if (inflater == null) {
+ return false;
+ }
+
+ if (mMyBindingId != mBgDataModel.lastBindId) {
+ Log.d(TAG, "Too many consecutive reloads, skipping obsolete view inflation");
+ return true;
+ }
+
+ ModelWriter writer = mApp.getModel()
+ .getWriter(false /* verifyChanges */, CellPosMapper.DEFAULT, null);
+ List> bindItems = items.stream().map(i ->
+ Pair.create(i, inflater.inflateItem(i, writer, null))).toList();
+ executeCallbacksTask(c -> c.bindInflatedItems(bindItems), executor);
+ return true;
+ }
+
+ private void bindItemsInChunks(List workspaceItems, int chunkCount,
+ Executor executor) {
+ if (inflateAsyncAndBind(workspaceItems, executor)) {
+ return;
+ }
+
// Bind the workspace items
int count = workspaceItems.size();
- for (int i = 0; i < count; i += ITEMS_CHUNK) {
+ for (int i = 0; i < count; i += chunkCount) {
final int start = i;
- final int chunkSize = (i + ITEMS_CHUNK <= count) ? ITEMS_CHUNK : (count - i);
+ final int chunkSize = (i + chunkCount <= count) ? chunkCount : (count - i);
executeCallbacksTask(
c -> c.bindItems(workspaceItems.subList(start, start + chunkSize), false),
executor);
}
}
- private void bindAppWidgets(List appWidgets, Executor executor) {
- // Bind the widgets, one at a time
- int count = appWidgets.size();
- for (int i = 0; i < count; i++) {
- final ItemInfo widget = appWidgets.get(i);
- executeCallbacksTask(
- c -> c.bindItems(Collections.singletonList(widget), false), executor);
- }
- }
-
protected void executeCallbacksTask(CallbackTask task, Executor executor) {
executor.execute(() -> {
if (mMyBindingId != mBgDataModel.lastBindId) {
@@ -430,8 +482,11 @@ public abstract class BaseLauncherBinder {
bindAppWidgets(appWidgets);
executeCallbacksTask(c -> {
MODEL_EXECUTOR.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
- c.onInitialBindComplete(
- mCurrentScreenIds, new RunnableList(), workspaceItemCount, isBindSync);
+
+ RunnableList onCompleteSignal = new RunnableList();
+ onCompleteSignal.executeAllAndDestroy();
+ c.onInitialBindComplete(mCurrentScreenIds, new RunnableList(), onCompleteSignal,
+ workspaceItemCount, isBindSync);
}, mUiExecutor);
}
diff --git a/src/com/android/launcher3/model/BgDataModel.java b/src/com/android/launcher3/model/BgDataModel.java
index 7f0f683091..8579d1d682 100644
--- a/src/com/android/launcher3/model/BgDataModel.java
+++ b/src/com/android/launcher3/model/BgDataModel.java
@@ -33,6 +33,8 @@ import android.os.UserHandle;
import android.text.TextUtils;
import android.util.ArraySet;
import android.util.Log;
+import android.util.Pair;
+import android.view.View;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -54,6 +56,7 @@ import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.IntArray;
import com.android.launcher3.util.IntSet;
import com.android.launcher3.util.IntSparseArrayMap;
+import com.android.launcher3.util.ItemInflater;
import com.android.launcher3.util.PackageUserKey;
import com.android.launcher3.util.RunnableList;
import com.android.launcher3.widget.model.WidgetsListBaseEntry;
@@ -495,7 +498,15 @@ public class BgDataModel {
default void clearPendingBinds() { }
default void startBinding() { }
- default void bindItems(List shortcuts, boolean forceAnimateIcons) { }
+ @Nullable
+ default ItemInflater getItemInflater() {
+ return null;
+ }
+
+ default void bindItems(@NonNull List shortcuts, boolean forceAnimateIcons) { }
+ /** Alternate method to bind preinflated views */
+ default void bindInflatedItems(@NonNull List> items) { }
+
default void bindScreens(IntArray orderedScreenIds) { }
default void setIsFirstPagePinnedItemEnabled(boolean isFirstPagePinnedItemEnabled) { }
default void finishBindingItems(IntSet pagesBoundFirst) { }
@@ -520,7 +531,9 @@ public class BgDataModel {
default void bindSmartspaceWidget() { }
/** Called when workspace has been bound. */
- default void onInitialBindComplete(IntSet boundPages, RunnableList pendingTasks,
+ default void onInitialBindComplete(@NonNull IntSet boundPages,
+ @NonNull RunnableList pendingTasks,
+ @NonNull RunnableList onCompleteSignal,
int workspaceItemCount, boolean isBindSync) {
pendingTasks.executeAllAndDestroy();
}
diff --git a/src/com/android/launcher3/model/ModelUtils.java b/src/com/android/launcher3/model/ModelUtils.java
index bc51c9bfad..9e72e2823e 100644
--- a/src/com/android/launcher3/model/ModelUtils.java
+++ b/src/com/android/launcher3/model/ModelUtils.java
@@ -20,7 +20,6 @@ import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.util.IntArray;
import com.android.launcher3.util.IntSet;
-import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
@@ -37,9 +36,9 @@ public class ModelUtils {
*/
public static void filterCurrentWorkspaceItems(
final IntSet currentScreenIds,
- ArrayList allWorkspaceItems,
- ArrayList currentScreenItems,
- ArrayList otherScreenItems) {
+ List extends T> allWorkspaceItems,
+ List currentScreenItems,
+ List otherScreenItems) {
// Purge any null ItemInfos
allWorkspaceItems.removeIf(Objects::isNull);
// Order the set of items by their containers first, this allows use to walk through the
diff --git a/src/com/android/launcher3/model/data/AppInfo.java b/src/com/android/launcher3/model/data/AppInfo.java
index b213fe306e..210d720bca 100644
--- a/src/com/android/launcher3/model/data/AppInfo.java
+++ b/src/com/android/launcher3/model/data/AppInfo.java
@@ -52,6 +52,9 @@ public class AppInfo extends ItemInfoWithIcon implements WorkspaceItemFactory {
return uc != 0 ? uc : a.componentName.compareTo(b.componentName);
};
+ public static final Comparator PACKAGE_KEY_COMPARATOR = Comparator.comparingInt(
+ (AppInfo a) -> a.user.hashCode()).thenComparing(ItemInfo::getTargetPackage);
+
/**
* The intent used to start the application.
*/
diff --git a/src/com/android/launcher3/util/ItemInflater.kt b/src/com/android/launcher3/util/ItemInflater.kt
index 79091caaab..cc66af1189 100644
--- a/src/com/android/launcher3/util/ItemInflater.kt
+++ b/src/com/android/launcher3/util/ItemInflater.kt
@@ -121,7 +121,7 @@ class ItemInflater(
}
val view =
if (type == WidgetInflater.TYPE_PENDING || widgetInfo == null)
- PendingAppWidgetHostView(context, item, widgetInfo)
+ PendingAppWidgetHostView(context, widgetHolder, item, widgetInfo)
else widgetHolder.createView(item.appWidgetId, widgetInfo)
prepareAppWidget(view, item)
return view
diff --git a/src/com/android/launcher3/util/RunnableList.java b/src/com/android/launcher3/util/RunnableList.java
index f6e0c57fb2..2b8bf56a3b 100644
--- a/src/com/android/launcher3/util/RunnableList.java
+++ b/src/com/android/launcher3/util/RunnableList.java
@@ -69,4 +69,11 @@ public class RunnableList {
}
}
}
+
+ /**
+ * Returns true if the list has been destroyed
+ */
+ public boolean isDestroyed() {
+ return mDestroyed;
+ }
}
diff --git a/src/com/android/launcher3/util/window/WindowManagerProxy.java b/src/com/android/launcher3/util/window/WindowManagerProxy.java
index 6a0090c60b..4a906d33e1 100644
--- a/src/com/android/launcher3/util/window/WindowManagerProxy.java
+++ b/src/com/android/launcher3/util/window/WindowManagerProxy.java
@@ -145,7 +145,18 @@ public class WindowManagerProxy implements ResourceBasedOverride {
: (isGesture
? getDimenByName(systemRes, NAVBAR_HEIGHT_LANDSCAPE)
: 0));
- Insets newNavInsets = Insets.of(navInsets.left, navInsets.top, navInsets.right, bottomNav);
+ int leftNav = navInsets.left;
+ int rightNav = navInsets.right;
+ if (!isLargeScreen && !isGesture && !isPortrait) {
+ // In 3-button landscape/seascape, Launcher should always have nav insets regardless if
+ // it's initiated from fullscreen apps.
+ int navBarWidth = getDimenByName(systemRes, NAVBAR_LANDSCAPE_LEFT_RIGHT_SIZE);
+ switch (getRotation(context)) {
+ case Surface.ROTATION_90 -> rightNav = navBarWidth;
+ case Surface.ROTATION_270 -> leftNav = navBarWidth;
+ }
+ }
+ Insets newNavInsets = Insets.of(leftNav, navInsets.top, rightNav, bottomNav);
insetsBuilder.setInsets(WindowInsets.Type.navigationBars(), newNavInsets);
insetsBuilder.setInsetsIgnoringVisibility(WindowInsets.Type.navigationBars(), newNavInsets);
diff --git a/src/com/android/launcher3/widget/BaseLauncherAppWidgetHostView.java b/src/com/android/launcher3/widget/BaseLauncherAppWidgetHostView.java
index 580b4f11ea..104209ef53 100644
--- a/src/com/android/launcher3/widget/BaseLauncherAppWidgetHostView.java
+++ b/src/com/android/launcher3/widget/BaseLauncherAppWidgetHostView.java
@@ -34,6 +34,17 @@ import com.android.launcher3.util.Executors;
*/
public abstract class BaseLauncherAppWidgetHostView extends NavigableAppWidgetHostView {
+ private static final ViewOutlineProvider VIEW_OUTLINE_PROVIDER = new ViewOutlineProvider() {
+ @Override
+ public void getOutline(View view, Outline outline) {
+ // Since ShortcutAndWidgetContainer sets clipChildren to false, we should restrict the
+ // outline to be the view bounds, otherwise widgets might draw themselves outside of
+ // the launcher view. Setting alpha to 0 to match the previous behavior.
+ outline.setRect(0, 0, view.getWidth(), view.getHeight());
+ outline.setAlpha(.0f);
+ }
+ };
+
protected final LayoutInflater mInflater;
private final Rect mEnforcedRectangle = new Rect();
@@ -49,10 +60,13 @@ public abstract class BaseLauncherAppWidgetHostView extends NavigableAppWidgetHo
}
};
+ private boolean mIsCornerRadiusEnforced;
+
public BaseLauncherAppWidgetHostView(Context context) {
super(context);
setExecutor(Executors.THREAD_POOL_EXECUTOR);
+ setClipToOutline(true);
mInflater = LayoutInflater.from(context);
mEnforcedCornerRadius = RoundedCornerEnforcement.computeEnforcedRadius(getContext());
@@ -84,8 +98,8 @@ public abstract class BaseLauncherAppWidgetHostView extends NavigableAppWidgetHo
@UiThread
private void resetRoundedCorners() {
- setOutlineProvider(ViewOutlineProvider.BACKGROUND);
- setClipToOutline(false);
+ setOutlineProvider(VIEW_OUTLINE_PROVIDER);
+ mIsCornerRadiusEnforced = false;
}
@UiThread
@@ -104,7 +118,7 @@ public abstract class BaseLauncherAppWidgetHostView extends NavigableAppWidgetHo
background,
mEnforcedRectangle);
setOutlineProvider(mCornerRadiusEnforcementOutline);
- setClipToOutline(true);
+ mIsCornerRadiusEnforced = true;
invalidateOutline();
}
@@ -115,6 +129,6 @@ public abstract class BaseLauncherAppWidgetHostView extends NavigableAppWidgetHo
/** Returns true if the corner radius are enforced for this App Widget. */
public boolean hasEnforcedCornerRadius() {
- return getClipToOutline();
+ return mIsCornerRadiusEnforced;
}
}
diff --git a/src/com/android/launcher3/widget/LauncherAppWidgetHost.java b/src/com/android/launcher3/widget/LauncherAppWidgetHost.java
index b1c477cbda..40c39840d6 100644
--- a/src/com/android/launcher3/widget/LauncherAppWidgetHost.java
+++ b/src/com/android/launcher3/widget/LauncherAppWidgetHost.java
@@ -54,6 +54,9 @@ class LauncherAppWidgetHost extends AppWidgetHost {
@Nullable
private final IntConsumer mAppWidgetRemovedCallback;
+ @Nullable
+ private ListenableHostView mViewToRecycle;
+
public LauncherAppWidgetHost(@NonNull Context context,
@Nullable IntConsumer appWidgetRemovedCallback,
List providerChangeListeners) {
@@ -73,11 +76,21 @@ class LauncherAppWidgetHost extends AppWidgetHost {
}
}
+ /**
+ * Sets the view to be recycled for the next widget creation.
+ */
+ public void recycleViewForNextCreation(ListenableHostView viewToRecycle) {
+ mViewToRecycle = viewToRecycle;
+ }
+
@Override
@NonNull
public LauncherAppWidgetHostView onCreateView(Context context, int appWidgetId,
AppWidgetProviderInfo appWidget) {
- return new ListenableHostView(context);
+ ListenableHostView result =
+ mViewToRecycle != null ? mViewToRecycle : new ListenableHostView(context);
+ mViewToRecycle = null;
+ return result;
}
/**
diff --git a/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java b/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java
index e77ec12e18..2259e3c0cd 100644
--- a/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java
+++ b/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java
@@ -40,12 +40,12 @@ import androidx.annotation.Nullable;
import com.android.launcher3.CheckLongPressHelper;
import com.android.launcher3.Flags;
-import com.android.launcher3.Launcher;
import com.android.launcher3.R;
-import com.android.launcher3.dragndrop.DragLayer;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.LauncherAppWidgetInfo;
import com.android.launcher3.util.Themes;
+import com.android.launcher3.views.ActivityContext;
+import com.android.launcher3.views.BaseDragLayer;
import com.android.launcher3.views.BaseDragLayer.TouchCompleteListener;
/**
@@ -72,7 +72,7 @@ public class LauncherAppWidgetHostView extends BaseLauncherAppWidgetHostView
private final Rect mTempRect = new Rect();
private final CheckLongPressHelper mLongPressHelper;
- protected final Launcher mLauncher;
+ protected final ActivityContext mActivityContext;
// Maintain the color manager.
private final LocalColorExtractor mColorExtractor;
@@ -94,15 +94,15 @@ public class LauncherAppWidgetHostView extends BaseLauncherAppWidgetHostView
public LauncherAppWidgetHostView(Context context) {
super(context);
- mLauncher = Launcher.getLauncher(context);
+ mActivityContext = ActivityContext.lookupContext(context);
mLongPressHelper = new CheckLongPressHelper(this, this);
- setAccessibilityDelegate(mLauncher.getAccessibilityDelegate());
+ setAccessibilityDelegate(mActivityContext.getAccessibilityDelegate());
setBackgroundResource(R.drawable.widget_internal_focus_bg);
if (Flags.enableFocusOutline()) {
setDefaultFocusHighlightEnabled(false);
}
- if (Themes.getAttrBoolean(mLauncher, R.attr.isWorkspaceDarkText)) {
+ if (Themes.getAttrBoolean(context, R.attr.isWorkspaceDarkText)) {
setOnLightBackground(true);
}
mColorExtractor = new LocalColorExtractor(); // no-op
@@ -120,8 +120,7 @@ public class LauncherAppWidgetHostView extends BaseLauncherAppWidgetHostView
@Override
public boolean onLongClick(View view) {
if (mIsScrollable) {
- DragLayer dragLayer = mLauncher.getDragLayer();
- dragLayer.requestDisallowInterceptTouchEvent(false);
+ mActivityContext.getDragLayer().requestDisallowInterceptTouchEvent(false);
}
view.performLongClick();
return true;
@@ -218,7 +217,7 @@ public class LauncherAppWidgetHostView extends BaseLauncherAppWidgetHostView
public boolean onInterceptTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
- DragLayer dragLayer = mLauncher.getDragLayer();
+ BaseDragLayer dragLayer = mActivityContext.getDragLayer();
if (mIsScrollable) {
dragLayer.requestDisallowInterceptTouchEvent(true);
}
diff --git a/src/com/android/launcher3/widget/LauncherWidgetHolder.java b/src/com/android/launcher3/widget/LauncherWidgetHolder.java
index 23127b3e4d..15bd6ed1f2 100644
--- a/src/com/android/launcher3/widget/LauncherWidgetHolder.java
+++ b/src/com/android/launcher3/widget/LauncherWidgetHolder.java
@@ -17,7 +17,9 @@ package com.android.launcher3.widget;
import static android.app.Activity.RESULT_CANCELED;
+import static com.android.launcher3.Flags.enableWorkspaceInflation;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+import static com.android.launcher3.widget.LauncherAppWidgetProviderInfo.fromProviderInfo;
import android.appwidget.AppWidgetHost;
import android.appwidget.AppWidgetHostView;
@@ -27,6 +29,7 @@ import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
+import android.os.Looper;
import android.util.SparseArray;
import android.widget.Toast;
@@ -310,7 +313,9 @@ public class LauncherWidgetHolder {
}
/**
- * Create a view for the specified app widget
+ * Create a view for the specified app widget. When calling this method from a background
+ * thread, the returned view will not receive ongoing updates. The caller needs to reattach
+ * the view using {@link #attachViewToHostAndGetAttachedView} on UIThread
*
* @param appWidgetId The ID of the widget
* @param appWidget The {@link LauncherAppWidgetProviderInfo} of the widget
@@ -327,7 +332,55 @@ public class LauncherWidgetHolder {
}
LauncherAppWidgetHostView view = createViewInternal(appWidgetId, appWidget);
- mViews.put(appWidgetId, view);
+ // Do not update mViews on a background thread call, as the holder is not thread safe.
+ if (!enableWorkspaceInflation() || Looper.myLooper() == Looper.getMainLooper()) {
+ mViews.put(appWidgetId, view);
+ }
+ return view;
+ }
+
+ /**
+ * Attaches an already inflated view to the host. If the view can't be attached, creates
+ * and attaches a new view.
+ * @return the final attached view
+ */
+ @NonNull
+ public final AppWidgetHostView attachViewToHostAndGetAttachedView(
+ @NonNull LauncherAppWidgetHostView view) {
+ if (mViews.get(view.getAppWidgetId()) != view) {
+ view = recycleExistingView(view);
+ mViews.put(view.getAppWidgetId(), view);
+ }
+ return view;
+ }
+
+ /**
+ * Recycling logic:
+ * 1) If the final view should be a pendingView
+ * if the provided view is also a pendingView, return itself
+ * otherwise discard provided view and return a new pending view
+ * 2) If the recycled view is a pendingView, discard it and return a new view
+ * 3) Use the same for as creating a new view, but used the provided view in the host instead
+ * of creating a new view. This ensures that all the host callbacks are properly attached
+ * as a result of using the same flow.
+ */
+ protected LauncherAppWidgetHostView recycleExistingView(LauncherAppWidgetHostView view) {
+ if ((mFlags & FLAG_LISTENING) == 0) {
+ if (view instanceof PendingAppWidgetHostView pv && pv.isDeferredWidget()) {
+ return view;
+ } else {
+ return new PendingAppWidgetHostView(mContext, this, view.getAppWidgetId(),
+ fromProviderInfo(mContext, view.getAppWidgetInfo()));
+ }
+ }
+ LauncherAppWidgetHost host = (LauncherAppWidgetHost) mWidgetHost;
+ if (view instanceof ListenableHostView lhv) {
+ host.recycleViewForNextCreation(lhv);
+ }
+
+ view = createViewInternal(
+ view.getAppWidgetId(), fromProviderInfo(mContext, view.getAppWidgetInfo()));
+ host.recycleViewForNextCreation(null);
return view;
}
@@ -338,8 +391,15 @@ public class LauncherWidgetHolder {
// Since the launcher hasn't started listening to widget updates, we can't simply call
// host.createView here because the later will make a binder call to retrieve
// RemoteViews from system process.
- return new PendingAppWidgetHostView(mContext, appWidgetId, appWidget);
+ return new PendingAppWidgetHostView(mContext, this, appWidgetId, appWidget);
} else {
+ if (enableWorkspaceInflation() && Looper.myLooper() != Looper.getMainLooper()) {
+ // Widget is being inflated a background thread, just create and
+ // return a placeholder view
+ ListenableHostView hostView = new ListenableHostView(mContext);
+ hostView.setAppWidget(appWidgetId, appWidget);
+ return hostView;
+ }
try {
return (LauncherAppWidgetHostView) mWidgetHost.createView(
mContext, appWidgetId, appWidget);
diff --git a/src/com/android/launcher3/widget/PendingAppWidgetHostView.java b/src/com/android/launcher3/widget/PendingAppWidgetHostView.java
index adf85c70e0..86400baaf2 100644
--- a/src/com/android/launcher3/widget/PendingAppWidgetHostView.java
+++ b/src/com/android/launcher3/widget/PendingAppWidgetHostView.java
@@ -50,6 +50,7 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.R;
import com.android.launcher3.icons.FastBitmapDrawable;
@@ -65,7 +66,7 @@ import java.util.List;
public class PendingAppWidgetHostView extends LauncherAppWidgetHostView
implements OnClickListener, ItemInfoUpdateReceiver {
private static final float SETUP_ICON_SIZE_FACTOR = 2f / 5;
- private static final float MIN_SATUNATION = 0.7f;
+ private static final float MIN_SATURATION = 0.7f;
private static final int FLAG_DRAW_SETTINGS = 1;
private static final int FLAG_DRAW_ICON = 2;
@@ -75,6 +76,7 @@ public class PendingAppWidgetHostView extends LauncherAppWidgetHostView
private final Rect mRect = new Rect();
+ private final LauncherWidgetHolder mWidgetHolder;
private final LauncherAppWidgetProviderInfo mAppwidget;
private final LauncherAppWidgetInfo mInfo;
private final int mStartState;
@@ -90,6 +92,7 @@ public class PendingAppWidgetHostView extends LauncherAppWidgetHostView
private Drawable mSettingIconDrawable;
private boolean mDrawableSizeChanged;
+ private boolean mIsDeferredWidget;
private final TextPaint mPaint;
@@ -98,13 +101,13 @@ public class PendingAppWidgetHostView extends LauncherAppWidgetHostView
@Nullable private Bitmap mPreviewBitmap;
- public PendingAppWidgetHostView(Context context, LauncherAppWidgetInfo info,
- @Nullable LauncherAppWidgetProviderInfo appWidget) {
- this(context, info, appWidget,
+ public PendingAppWidgetHostView(Context context, LauncherWidgetHolder widgetHolder,
+ LauncherAppWidgetInfo info, @Nullable LauncherAppWidgetProviderInfo appWidget) {
+ this(context, widgetHolder, info, appWidget,
context.getResources().getText(R.string.gadget_complete_setup_text));
super.updateAppWidget(null);
- setOnClickListener(mLauncher.getItemOnClickListener());
+ setOnClickListener(mActivityContext.getItemOnClickListener());
if (info.pendingItemInfo == null) {
info.pendingItemInfo = new PackageItemInfo(info.providerName.getPackageName(),
@@ -117,14 +120,16 @@ public class PendingAppWidgetHostView extends LauncherAppWidgetHostView
}
public PendingAppWidgetHostView(
- Context context, int appWidgetId, @NonNull LauncherAppWidgetProviderInfo appWidget) {
- this(context, new LauncherAppWidgetInfo(appWidgetId, appWidget.provider),
+ Context context, LauncherWidgetHolder widgetHolder,
+ int appWidgetId, @NonNull LauncherAppWidgetProviderInfo appWidget) {
+ this(context, widgetHolder, new LauncherAppWidgetInfo(appWidgetId, appWidget.provider),
appWidget, appWidget.label);
getBackground().mutate().setAlpha(DEFERRED_ALPHA);
mCenterDrawable = new ColorDrawable(Color.TRANSPARENT);
mDragFlags = FLAG_DRAW_LABEL;
mDrawableSizeChanged = true;
+ mIsDeferredWidget = true;
}
/** Set {@link Bitmap} of widget preview. */
@@ -136,10 +141,11 @@ public class PendingAppWidgetHostView extends LauncherAppWidgetHostView
invalidate();
}
- private PendingAppWidgetHostView(Context context, LauncherAppWidgetInfo info,
+ private PendingAppWidgetHostView(Context context,
+ LauncherWidgetHolder widgetHolder, LauncherAppWidgetInfo info,
LauncherAppWidgetProviderInfo appwidget, CharSequence label) {
super(new ContextThemeWrapper(context, R.style.WidgetContainerTheme));
-
+ mWidgetHolder = widgetHolder;
mAppwidget = appwidget;
mInfo = info;
mStartState = info.restoreStatus;
@@ -148,9 +154,12 @@ public class PendingAppWidgetHostView extends LauncherAppWidgetHostView
mPaint = new TextPaint();
mPaint.setColor(Themes.getAttrColor(getContext(), android.R.attr.textColorPrimary));
- mPaint.setTextSize(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX,
- mLauncher.getDeviceProfile().iconTextSizePx, getResources().getDisplayMetrics()));
+ mPaint.setTextSize(TypedValue.applyDimension(
+ TypedValue.COMPLEX_UNIT_PX,
+ mActivityContext.getDeviceProfile().iconTextSizePx,
+ getResources().getDisplayMetrics()));
mPreviewPaint = new Paint(ANTI_ALIAS_FLAG | DITHER_FLAG | FILTER_BITMAP_FLAG);
+
setWillNotDraw(false);
setBackgroundResource(R.drawable.pending_widget_bg);
}
@@ -160,6 +169,11 @@ public class PendingAppWidgetHostView extends LauncherAppWidgetHostView
return mAppwidget;
}
+ @Override
+ public int getAppWidgetId() {
+ return mInfo.appWidgetId;
+ }
+
@Override
public void updateAppWidget(RemoteViews remoteViews) {
checkIfRestored();
@@ -172,6 +186,10 @@ public class PendingAppWidgetHostView extends LauncherAppWidgetHostView
}
}
+ public boolean isDeferredWidget() {
+ return mIsDeferredWidget;
+ }
+
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
@@ -184,8 +202,8 @@ public class PendingAppWidgetHostView extends LauncherAppWidgetHostView
if (mOnDetachCleanup != null) {
mOnDetachCleanup.close();
}
- mOnDetachCleanup = mLauncher.getAppWidgetHolder()
- .addOnUpdateListener(mInfo.appWidgetId, mAppwidget, this::checkIfRestored);
+ mOnDetachCleanup = mWidgetHolder.addOnUpdateListener(
+ mInfo.appWidgetId, mAppwidget, this::checkIfRestored);
checkIfRestored();
}
}
@@ -211,11 +229,13 @@ public class PendingAppWidgetHostView extends LauncherAppWidgetHostView
// This occurs when LauncherAppWidgetHostView is used to render a preview layout.
return;
}
- // Remove and rebind the current widget (which was inflated in the wrong
- // orientation), but don't delete it from the database
- mLauncher.removeItem(this, info, false /* deleteFromDb */,
- "widget removed because of configuration change");
- mLauncher.bindAppWidget(info);
+ if (mActivityContext instanceof Launcher launcher) {
+ // Remove and rebind the current widget (which was inflated in the wrong
+ // orientation), but don't delete it from the database
+ launcher.removeItem(this, info, false /* deleteFromDb */,
+ "widget removed because of configuration change");
+ launcher.bindAppWidget(info);
+ }
}
@Override
@@ -303,7 +323,7 @@ public class PendingAppWidgetHostView extends LauncherAppWidgetHostView
// Make the dominant color bright.
float[] hsv = new float[3];
Color.colorToHSV(dominantColor, hsv);
- hsv[1] = Math.min(hsv[1], MIN_SATUNATION);
+ hsv[1] = Math.min(hsv[1], MIN_SATURATION);
hsv[2] = 1;
mSettingIconDrawable.setColorFilter(Color.HSVToColor(hsv), PorterDuff.Mode.SRC_IN);
}
@@ -344,7 +364,7 @@ public class PendingAppWidgetHostView extends LauncherAppWidgetHostView
}
private void updateDrawableBounds() {
- DeviceProfile grid = mLauncher.getDeviceProfile();
+ DeviceProfile grid = mActivityContext.getDeviceProfile();
int paddingTop = getPaddingTop();
int paddingBottom = getPaddingBottom();
int paddingLeft = getPaddingLeft();
diff --git a/tests/multivalentTests/tapl/com/android/launcher3/tapl/Folder.java b/tests/multivalentTests/tapl/com/android/launcher3/tapl/Folder.java
index 1352cc07c1..b8adfe6656 100644
--- a/tests/multivalentTests/tapl/com/android/launcher3/tapl/Folder.java
+++ b/tests/multivalentTests/tapl/com/android/launcher3/tapl/Folder.java
@@ -16,6 +16,8 @@
package com.android.launcher3.tapl;
+import android.graphics.Rect;
+
import androidx.annotation.NonNull;
import androidx.test.uiautomator.UiObject2;
@@ -58,4 +60,7 @@ public class Folder {
return mLauncher.getWorkspace();
}
}
+ Rect getDropLocationBounds() {
+ return mLauncher.getVisibleBounds(mContainer);
+ }
}
diff --git a/tests/multivalentTests/tapl/com/android/launcher3/tapl/FolderIcon.java b/tests/multivalentTests/tapl/com/android/launcher3/tapl/FolderIcon.java
index 0c453bd5b5..080e52c337 100644
--- a/tests/multivalentTests/tapl/com/android/launcher3/tapl/FolderIcon.java
+++ b/tests/multivalentTests/tapl/com/android/launcher3/tapl/FolderIcon.java
@@ -26,7 +26,7 @@ import com.android.launcher3.testing.shared.TestProtocol;
/**
* Folder Icon, an app folder in workspace.
*/
-public class FolderIcon implements FolderDragTarget {
+public class FolderIcon implements IconDragTarget {
protected final UiObject2 mObject;
protected final LauncherInstrumentation mLauncher;
@@ -60,7 +60,7 @@ public class FolderIcon implements FolderDragTarget {
/** This method requires public access, however should not be called in tests. */
@Override
- public FolderIcon getTargetFolder(Rect bounds) {
+ public FolderIcon getTargetIcon(Rect bounds) {
return this;
}
}
diff --git a/tests/multivalentTests/tapl/com/android/launcher3/tapl/HomeAppIcon.java b/tests/multivalentTests/tapl/com/android/launcher3/tapl/HomeAppIcon.java
index 693baa02c5..ca85b290c1 100644
--- a/tests/multivalentTests/tapl/com/android/launcher3/tapl/HomeAppIcon.java
+++ b/tests/multivalentTests/tapl/com/android/launcher3/tapl/HomeAppIcon.java
@@ -27,7 +27,7 @@ import java.util.function.Supplier;
/**
* App icon on the workspace or all apps.
*/
-public abstract class HomeAppIcon extends AppIcon implements FolderDragTarget, WorkspaceDragSource {
+public abstract class HomeAppIcon extends AppIcon implements IconDragTarget, WorkspaceDragSource {
private final String mAppName;
@@ -42,7 +42,7 @@ public abstract class HomeAppIcon extends AppIcon implements FolderDragTarget, W
* @param target the destination icon.
*/
@NonNull
- public FolderIcon dragToIcon(FolderDragTarget target) {
+ public FolderIcon dragToIcon(IconDragTarget target) {
try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck();
LauncherInstrumentation.Closable c = mLauncher.addContextLayer("want to drag icon")) {
final Rect dropBounds = target.getDropLocationBounds();
@@ -51,13 +51,33 @@ public abstract class HomeAppIcon extends AppIcon implements FolderDragTarget, W
() -> {
final Rect bounds = target.getDropLocationBounds();
return new Point(bounds.centerX(), bounds.centerY());
- });
- FolderIcon result = target.getTargetFolder(dropBounds);
+ }, false);
+ FolderIcon result = target.getTargetIcon(dropBounds);
mLauncher.assertTrue("Can't find the target folder.", result != null);
return result;
}
}
+ /**
+ * Drag the AppIcon to the given position of a folder icon, and then inside that folder.
+ *
+ * @param target the destination folder.
+ */
+ @NonNull
+ public Folder dragToFolder(IconDragTarget target) {
+ try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck();
+ LauncherInstrumentation.Closable c = mLauncher.addContextLayer("want to drag icon")) {
+ Workspace.dragIconToWorkspace(
+ mLauncher, this,
+ () -> {
+ final Rect bounds = target.getDropLocationBounds();
+ return new Point(bounds.centerX(), bounds.centerY());
+ }, /* isDraggingToFolder */ true);
+ }
+ return new Folder(mLauncher);
+ }
+
+
/** This method requires public access, however should not be called in tests. */
@Override
public Rect getDropLocationBounds() {
@@ -66,7 +86,7 @@ public abstract class HomeAppIcon extends AppIcon implements FolderDragTarget, W
/** This method requires public access, however should not be called in tests. */
@Override
- public FolderIcon getTargetFolder(Rect bounds) {
+ public FolderIcon getTargetIcon(Rect bounds) {
for (FolderIcon folderIcon : mLauncher.getWorkspace().getFolderIcons()) {
final Rect folderIconBounds = folderIcon.getDropLocationBounds();
if (bounds.contains(folderIconBounds.centerX(), folderIconBounds.centerY())) {
diff --git a/tests/multivalentTests/tapl/com/android/launcher3/tapl/FolderDragTarget.java b/tests/multivalentTests/tapl/com/android/launcher3/tapl/IconDragTarget.java
similarity index 91%
rename from tests/multivalentTests/tapl/com/android/launcher3/tapl/FolderDragTarget.java
rename to tests/multivalentTests/tapl/com/android/launcher3/tapl/IconDragTarget.java
index 2c60668ba8..2f86703d9b 100644
--- a/tests/multivalentTests/tapl/com/android/launcher3/tapl/FolderDragTarget.java
+++ b/tests/multivalentTests/tapl/com/android/launcher3/tapl/IconDragTarget.java
@@ -18,11 +18,11 @@ package com.android.launcher3.tapl;
import android.graphics.Rect;
-public interface FolderDragTarget {
+public interface IconDragTarget {
/** This method requires public access, however should not be called in tests. */
Rect getDropLocationBounds();
/** This method requires public access, however should not be called in tests. */
- FolderIcon getTargetFolder(Rect bounds);
+ FolderIcon getTargetIcon(Rect bounds);
}
diff --git a/tests/multivalentTests/tapl/com/android/launcher3/tapl/Workspace.java b/tests/multivalentTests/tapl/com/android/launcher3/tapl/Workspace.java
index 1029b78c21..506e563777 100644
--- a/tests/multivalentTests/tapl/com/android/launcher3/tapl/Workspace.java
+++ b/tests/multivalentTests/tapl/com/android/launcher3/tapl/Workspace.java
@@ -17,10 +17,14 @@
package com.android.launcher3.tapl;
import static android.view.KeyEvent.KEYCODE_META_RIGHT;
+import static android.view.KeyEvent.KEYCODE_RECENT_APPS;
+import static android.view.KeyEvent.KEYCODE_TAB;
+import static android.view.KeyEvent.META_META_ON;
import static android.view.accessibility.AccessibilityEvent.TYPE_VIEW_SCROLLED;
import static com.android.launcher3.testing.shared.TestProtocol.ALL_APPS_STATE_ORDINAL;
import static com.android.launcher3.testing.shared.TestProtocol.NORMAL_STATE_ORDINAL;
+import static com.android.launcher3.testing.shared.TestProtocol.OVERVIEW_STATE_ORDINAL;
import static com.android.launcher3.testing.shared.TestProtocol.UIOBJECT_STALE_ELEMENT;
import static junit.framework.TestCase.assertNotNull;
@@ -134,6 +138,40 @@ public final class Workspace extends Home {
}
}
+ /** Opens the Launcher Overview page with the action+tab keyboard shortcut. */
+ public Overview openOverviewFromActionPlusTabKeyboardShortcut() {
+ try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck();
+ LauncherInstrumentation.Closable c =
+ mLauncher.addContextLayer("want to open overview")) {
+ verifyActiveContainer();
+ mLauncher.runToState(
+ () -> mLauncher.getDevice().pressKeyCode(KEYCODE_TAB, META_META_ON),
+ OVERVIEW_STATE_ORDINAL,
+ "pressing keyboard shortcut");
+ try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer(
+ "pressed meta+tab key")) {
+ return new Overview(mLauncher);
+ }
+ }
+ }
+
+ /** Opens the Launcher Overview page with the Recents keyboard shortcut. */
+ public Overview openOverviewFromRecentsKeyboardShortcut() {
+ try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck();
+ LauncherInstrumentation.Closable c =
+ mLauncher.addContextLayer("want to open overview")) {
+ verifyActiveContainer();
+ mLauncher.runToState(
+ () -> mLauncher.getDevice().pressKeyCode(KEYCODE_RECENT_APPS),
+ OVERVIEW_STATE_ORDINAL,
+ "pressing keyboard shortcut");
+ try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer(
+ "pressed recents apps key")) {
+ return new Overview(mLauncher);
+ }
+ }
+ }
+
/**
* Returns the home qsb.
*
@@ -551,7 +589,8 @@ public final class Workspace extends Home {
* This function expects the launchable is inside the workspace and there is no drop event.
*/
static void dragIconToWorkspace(
- LauncherInstrumentation launcher, Launchable launchable, Supplier destSupplier) {
+ LauncherInstrumentation launcher, Launchable launchable, Supplier destSupplier,
+ boolean isDraggingToFolder) {
dragIconToWorkspace(
launcher,
launchable,
@@ -559,7 +598,8 @@ public final class Workspace extends Home {
/* isDecelerating= */ false,
() -> launcher.expectEvent(TestProtocol.SEQUENCE_MAIN, LONG_CLICK_EVENT),
/* expectDropEvents= */ null,
- /* startsActivity = */ false);
+ /* startsActivity = */ false,
+ isDraggingToFolder);
}
static void dragIconToWorkspace(
@@ -570,7 +610,8 @@ public final class Workspace extends Home {
@Nullable Runnable expectDropEvents,
boolean startsActivity) {
dragIconToWorkspace(launcher, launchable, dest, /* isDecelerating */ true,
- expectLongClickEvents, expectDropEvents, startsActivity);
+ expectLongClickEvents, expectDropEvents, startsActivity,
+ /* isDraggingToFolder */ false);
}
static void dragIconToWorkspace(
@@ -580,7 +621,8 @@ public final class Workspace extends Home {
boolean isDecelerating,
Runnable expectLongClickEvents,
@Nullable Runnable expectDropEvents,
- boolean startsActivity) {
+ boolean startsActivity,
+ boolean isDraggingToFolder) {
try (LauncherInstrumentation.Closable ignored = launcher.addContextLayer(
"want to drag icon to workspace")) {
final long downTime = SystemClock.uptimeMillis();
@@ -607,11 +649,27 @@ public final class Workspace extends Home {
dragStart = screenEdge;
}
- // targetDest.x is now between 0 and displayX so we found the target page,
- // we just have to put move the icon to the destination and drop it
- launcher.movePointer(dragStart, targetDest, DEFAULT_DRAG_STEPS, isDecelerating,
- downTime, SystemClock.uptimeMillis(), false,
- LauncherInstrumentation.GestureScope.DONT_EXPECT_PILFER);
+ // targetDest.x is now between 0 and displayX so we found the target page.
+ // If not a folder, we just have to put move the icon to the destination and drop it.
+ // If it's a folder we want to drag to the folder icon and then drag to the center of
+ // that folder when it opens.
+ if (isDraggingToFolder) {
+ Point finalDragStart = dragStart;
+ Point finalTargetDest = targetDest;
+ Folder folder = executeAndWaitForFolderOpen(launcher, () -> launcher.movePointer(
+ finalDragStart, finalTargetDest, DEFAULT_DRAG_STEPS, isDecelerating,
+ downTime, SystemClock.uptimeMillis(), false,
+ LauncherInstrumentation.GestureScope.DONT_EXPECT_PILFER));
+
+ Rect dropBounds = folder.getDropLocationBounds();
+ dragStart = targetDest;
+ targetDest = new Point(dropBounds.centerX(), dropBounds.centerY());
+ }
+
+ launcher.movePointer(dragStart, targetDest,
+ DEFAULT_DRAG_STEPS, isDecelerating, downTime, SystemClock.uptimeMillis(),
+ false, LauncherInstrumentation.GestureScope.DONT_EXPECT_PILFER);
+
dropDraggedIcon(launcher, targetDest, downTime, expectDropEvents, startsActivity);
}
}
@@ -696,6 +754,16 @@ public final class Workspace extends Home {
() -> "Page scroll didn't happen", "Scrolling page");
}
+ private static Folder executeAndWaitForFolderOpen(LauncherInstrumentation launcher,
+ Runnable command) {
+ launcher.executeAndWaitForEvent(command,
+ event -> TestProtocol.FOLDER_OPENED_MESSAGE.equals(
+ event.getClassName().toString()),
+ () -> "Fail to open folder.",
+ "open folder");
+ return new Folder(launcher);
+ }
+
static void dragIconToHotseat(
LauncherInstrumentation launcher,
Launchable launchable,
diff --git a/tests/src/com/android/launcher3/dragging/TaplDragTest.java b/tests/src/com/android/launcher3/dragging/TaplDragTest.java
index d57461f539..615f679154 100644
--- a/tests/src/com/android/launcher3/dragging/TaplDragTest.java
+++ b/tests/src/com/android/launcher3/dragging/TaplDragTest.java
@@ -87,8 +87,7 @@ public class TaplDragTest extends AbstractLauncherUiTest {
PHOTOS_APP_NAME);
final HomeAppIcon mapIcon = createShortcutInCenterIfNotExist(MAPS_APP_NAME);
- folderIcon = mapIcon.dragToIcon(folderIcon);
- folder = folderIcon.open();
+ folder = mapIcon.dragToFolder(folderIcon);
folder.getAppIcon(MAPS_APP_NAME);
workspace = folder.close();
diff --git a/tests/src/com/android/launcher3/model/AsyncBindingTest.kt b/tests/src/com/android/launcher3/model/AsyncBindingTest.kt
new file mode 100644
index 0000000000..af367a814a
--- /dev/null
+++ b/tests/src/com/android/launcher3/model/AsyncBindingTest.kt
@@ -0,0 +1,212 @@
+/*
+ * 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.model
+
+import android.os.Looper
+import android.platform.test.flag.junit.SetFlagsRule
+import android.util.Pair
+import android.util.SparseArray
+import android.view.View
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.launcher3.Flags
+import com.android.launcher3.model.BgDataModel.Callbacks
+import com.android.launcher3.model.data.ItemInfo
+import com.android.launcher3.util.Executors.MAIN_EXECUTOR
+import com.android.launcher3.util.Executors.MODEL_EXECUTOR
+import com.android.launcher3.util.IntArray
+import com.android.launcher3.util.IntSet
+import com.android.launcher3.util.ItemInflater
+import com.android.launcher3.util.LauncherLayoutBuilder
+import com.android.launcher3.util.LauncherModelHelper
+import com.android.launcher3.util.LauncherModelHelper.TEST_PACKAGE
+import com.android.launcher3.util.RunnableList
+import org.junit.After
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertTrue
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+import org.mockito.Spy
+import org.mockito.kotlin.any
+import org.mockito.kotlin.argThat
+import org.mockito.kotlin.atLeastOnce
+import org.mockito.kotlin.doAnswer
+import org.mockito.kotlin.isNull
+import org.mockito.kotlin.never
+import org.mockito.kotlin.reset
+import org.mockito.kotlin.times
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
+
+/** Tests to verify async binding of model views */
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class AsyncBindingTest {
+
+ @get:Rule val setFlagsRule = SetFlagsRule()
+
+ @Spy private var callbacks = MyCallbacks()
+ @Mock private lateinit var itemInflater: ItemInflater<*>
+
+ private val inflationLooper = SparseArray()
+
+ private lateinit var modelHelper: LauncherModelHelper
+
+ @Before
+ fun setUp() {
+ setFlagsRule.enableFlags(Flags.FLAG_ENABLE_WORKSPACE_INFLATION)
+ MockitoAnnotations.initMocks(this)
+ modelHelper = LauncherModelHelper()
+
+ doAnswer { i ->
+ inflationLooper[(i.arguments[0] as ItemInfo).id] = Looper.myLooper()
+ View(modelHelper.sandboxContext)
+ }
+ .whenever(itemInflater)
+ .inflateItem(any(), any(), isNull())
+
+ // Set up the workspace with 3 pages of apps
+ modelHelper.setupDefaultLayoutProvider(
+ LauncherLayoutBuilder()
+ .atWorkspace(0, 1, 0)
+ .putApp(TEST_PACKAGE, TEST_PACKAGE)
+ .atWorkspace(1, 1, 0)
+ .putApp(TEST_PACKAGE, TEST_PACKAGE)
+ .atWorkspace(0, 1, 1)
+ .putApp(TEST_PACKAGE, TEST_PACKAGE)
+ .atWorkspace(1, 1, 1)
+ .putApp(TEST_PACKAGE, TEST_PACKAGE)
+ .atWorkspace(0, 1, 2)
+ .putApp(TEST_PACKAGE, TEST_PACKAGE)
+ )
+ }
+
+ @After
+ fun tearDown() {
+ modelHelper.destroy()
+ }
+
+ @Test
+ fun test_bind_normally_without_itemInflater() {
+ MAIN_EXECUTOR.execute { modelHelper.model.addCallbacksAndLoad(callbacks) }
+ waitForLoaderAndTempMainThread()
+
+ verify(callbacks, never()).bindInflatedItems(any())
+ verify(callbacks, atLeastOnce()).bindItems(any(), any())
+ }
+
+ @Test
+ fun test_bind_inflates_item_on_background() {
+ callbacks.inflater = itemInflater
+ MAIN_EXECUTOR.execute { modelHelper.model.addCallbacksAndLoad(callbacks) }
+ waitForLoaderAndTempMainThread()
+
+ verify(callbacks, never()).bindItems(any(), any())
+ verify(callbacks, times(1)).bindInflatedItems(argThat { t -> t.size == 2 })
+
+ // Verify remaining items are bound using pendingTasks
+ reset(callbacks)
+ MAIN_EXECUTOR.submit(callbacks.pendingTasks!!::executeAllAndDestroy).get()
+ verify(callbacks, times(1)).bindInflatedItems(argThat { t -> t.size == 3 })
+
+ // Verify that all items were inflated on the background thread
+ assertEquals(5, inflationLooper.size())
+ for (i in 0..4) assertEquals(MODEL_EXECUTOR.looper, inflationLooper.valueAt(i))
+ }
+
+ @Test
+ fun test_bind_sync_partially_inflates_on_background() {
+ modelHelper.loadModelSync()
+ assertTrue(modelHelper.model.isModelLoaded)
+ callbacks.inflater = itemInflater
+
+ val firstPageBindIds = IntSet()
+
+ MAIN_EXECUTOR.submit {
+ modelHelper.model.addCallbacksAndLoad(callbacks)
+ verify(callbacks, never()).bindItems(any(), any())
+ verify(callbacks, times(1))
+ .bindInflatedItems(
+ argThat { t ->
+ t.forEach { firstPageBindIds.add(it.first.id) }
+ t.size == 2
+ }
+ )
+
+ // Verify that onInitialBindComplete is called and the binding is not yet complete
+ assertFalse(callbacks.onCompleteSignal!!.isDestroyed)
+ }
+ .get()
+
+ waitForLoaderAndTempMainThread()
+ assertTrue(callbacks.onCompleteSignal!!.isDestroyed)
+
+ // Verify that firstPageBindIds are loaded on the main thread and remaining
+ // on the background thread.
+ assertEquals(5, inflationLooper.size())
+ for (i in 0..4) {
+ if (firstPageBindIds.contains(inflationLooper.keyAt(i)))
+ assertEquals(MAIN_EXECUTOR.looper, inflationLooper.valueAt(i))
+ else assertEquals(MODEL_EXECUTOR.looper, inflationLooper.valueAt(i))
+ }
+
+ MAIN_EXECUTOR.submit {
+ reset(callbacks)
+ callbacks.pendingTasks!!.executeAllAndDestroy()
+ // Verify remaining 3 times are bound using pending tasks
+ verify(callbacks, times(1)).bindInflatedItems(argThat { t -> t.size == 3 })
+ }
+ .get()
+ }
+
+ private fun waitForLoaderAndTempMainThread() {
+ MAIN_EXECUTOR.submit {}.get()
+ MODEL_EXECUTOR.submit {}.get()
+ MAIN_EXECUTOR.submit {}.get()
+ }
+
+ class MyCallbacks : Callbacks {
+
+ var inflater: ItemInflater<*>? = null
+ var pendingTasks: RunnableList? = null
+ var onCompleteSignal: RunnableList? = null
+
+ override fun bindItems(shortcuts: MutableList, forceAnimateIcons: Boolean) {}
+
+ override fun bindInflatedItems(items: MutableList>) {}
+
+ override fun getPagesToBindSynchronously(orderedScreenIds: IntArray?) = IntSet.wrap(0)
+
+ override fun onInitialBindComplete(
+ boundPages: IntSet,
+ pendingTasks: RunnableList,
+ onCompleteSignal: RunnableList,
+ workspaceItemCount: Int,
+ isBindSync: Boolean
+ ) {
+ this.pendingTasks = pendingTasks
+ this.onCompleteSignal = onCompleteSignal
+ }
+
+ override fun getItemInflater() = inflater
+ }
+}
diff --git a/tests/src/com/android/launcher3/model/ModelMultiCallbacksTest.java b/tests/src/com/android/launcher3/model/ModelMultiCallbacksTest.java
index 25a4c4e8b8..b140f2efc7 100644
--- a/tests/src/com/android/launcher3/model/ModelMultiCallbacksTest.java
+++ b/tests/src/com/android/launcher3/model/ModelMultiCallbacksTest.java
@@ -187,7 +187,7 @@ public class ModelMultiCallbacksTest {
@Override
public void onInitialBindComplete(IntSet boundPages, RunnableList pendingTasks,
- int workspaceItemCount, boolean isBindSync) {
+ RunnableList onCompleteSignal, int workspaceItemCount, boolean isBindSync) {
mPageBoundSync = boundPages;
mPendingTasks = pendingTasks;
}
diff --git a/tests/src/com/android/launcher3/util/ItemInflaterTest.kt b/tests/src/com/android/launcher3/util/ItemInflaterTest.kt
new file mode 100644
index 0000000000..efad899e35
--- /dev/null
+++ b/tests/src/com/android/launcher3/util/ItemInflaterTest.kt
@@ -0,0 +1,313 @@
+/*
+ * 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.content.ComponentName
+import android.content.Context
+import android.content.pm.LauncherApps
+import android.os.Bundle
+import android.os.Process
+import android.platform.test.flag.junit.SetFlagsRule
+import android.view.View.OnClickListener
+import android.view.View.OnFocusChangeListener
+import android.widget.FrameLayout
+import androidx.test.core.app.ApplicationProvider.getApplicationContext
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.launcher3.BubbleTextView
+import com.android.launcher3.Flags
+import com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APP_PAIR
+import com.android.launcher3.apppairs.AppPairIcon
+import com.android.launcher3.folder.FolderIcon
+import com.android.launcher3.model.ModelWriter
+import com.android.launcher3.model.data.AppInfo
+import com.android.launcher3.model.data.FolderInfo
+import com.android.launcher3.model.data.LauncherAppWidgetInfo
+import com.android.launcher3.model.data.LauncherAppWidgetInfo.FLAG_ID_NOT_VALID
+import com.android.launcher3.model.data.LauncherAppWidgetInfo.FLAG_UI_NOT_READY
+import com.android.launcher3.model.data.LauncherAppWidgetInfo.RESTORE_COMPLETED
+import com.android.launcher3.ui.TestViewHelpers
+import com.android.launcher3.util.Executors.MAIN_EXECUTOR
+import com.android.launcher3.util.Executors.VIEW_PREINFLATION_EXECUTOR
+import com.android.launcher3.util.rule.ShellCommandRule
+import com.android.launcher3.widget.LauncherAppWidgetHostView
+import com.android.launcher3.widget.LauncherWidgetHolder
+import com.android.launcher3.widget.PendingAppWidgetHostView
+import com.android.launcher3.widget.WidgetManagerHelper
+import java.util.concurrent.Callable
+import org.junit.After
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertNull
+import org.junit.Assert.assertTrue
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.any
+import org.mockito.kotlin.same
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.verifyNoMoreInteractions
+
+/** Tests for ItemInflater */
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class ItemInflaterTest {
+
+ @get:Rule val setFlagsRule = SetFlagsRule()
+ @get:Rule val grantWidgetRule = ShellCommandRule.grantWidgetBind()
+
+ private val clickListener = OnClickListener {}
+ private val focusListener = OnFocusChangeListener { _, _ -> }
+
+ @Mock private lateinit var modelWriter: ModelWriter
+
+ private lateinit var testContext: Context
+ private lateinit var uiContext: ActivityContextWrapper
+
+ private lateinit var widgetHolder: LauncherWidgetHolder
+ private lateinit var underTest: ItemInflater<*>
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ testContext = InstrumentationRegistry.getInstrumentation().context
+
+ uiContext = ActivityContextWrapper(getApplicationContext())
+ uiContext.setTheme(Themes.getActivityThemeRes(uiContext, 0))
+
+ widgetHolder = LauncherWidgetHolder.newInstance(uiContext)
+ widgetHolder.startListening()
+ underTest =
+ ItemInflater(
+ uiContext,
+ widgetHolder,
+ clickListener,
+ focusListener,
+ FrameLayout(uiContext)
+ )
+ }
+
+ @After
+ fun tearDown() {
+ widgetHolder.destroy()
+ }
+
+ @Test
+ fun test_workspace_item_inflated_on_UI() {
+ val itemInfo = workspaceItemInfo()
+ val view =
+ MAIN_EXECUTOR.submit(Callable { underTest.inflateItem(itemInfo, modelWriter) }).get()
+
+ assertTrue(view is BubbleTextView)
+ assertEquals(itemInfo, view!!.tag)
+ }
+
+ @Test
+ fun test_workspace_item_inflated_on_BG() {
+ setFlagsRule.enableFlags(Flags.FLAG_ENABLE_WORKSPACE_INFLATION)
+
+ val itemInfo = workspaceItemInfo()
+ val view =
+ VIEW_PREINFLATION_EXECUTOR.submit(
+ Callable { underTest.inflateItem(itemInfo, modelWriter) }
+ )
+ .get()
+
+ assertTrue(view is BubbleTextView)
+ assertEquals(itemInfo, view!!.tag)
+ }
+
+ @Test
+ fun test_folder_inflated_on_UI() {
+ val itemInfo = FolderInfo()
+ itemInfo.contents.add(workspaceItemInfo())
+ itemInfo.contents.add(workspaceItemInfo())
+ itemInfo.contents.add(workspaceItemInfo())
+
+ val view =
+ MAIN_EXECUTOR.submit(Callable { underTest.inflateItem(itemInfo, modelWriter) }).get()
+
+ assertTrue(view is FolderIcon)
+ assertEquals(itemInfo, view!!.tag)
+ }
+
+ @Test
+ fun test_folder_inflated_on_BG() {
+ setFlagsRule.enableFlags(Flags.FLAG_ENABLE_WORKSPACE_INFLATION)
+
+ val itemInfo = FolderInfo()
+ itemInfo.contents.add(workspaceItemInfo())
+ itemInfo.contents.add(workspaceItemInfo())
+ itemInfo.contents.add(workspaceItemInfo())
+
+ val view =
+ VIEW_PREINFLATION_EXECUTOR.submit(
+ Callable { underTest.inflateItem(itemInfo, modelWriter) }
+ )
+ .get()
+
+ assertTrue(view is FolderIcon)
+ assertEquals(itemInfo, view!!.tag)
+ }
+
+ @Test
+ fun test_app_pair_inflated_on_UI() {
+ val itemInfo = FolderInfo()
+ itemInfo.itemType = ITEM_TYPE_APP_PAIR
+ itemInfo.contents.add(workspaceItemInfo())
+ itemInfo.contents.add(workspaceItemInfo())
+
+ val view =
+ MAIN_EXECUTOR.submit(Callable { underTest.inflateItem(itemInfo, modelWriter) }).get()
+
+ assertTrue(view is AppPairIcon)
+ assertEquals(itemInfo, view!!.tag)
+ }
+
+ @Test
+ fun test_app_pair_inflated_on_BG() {
+ setFlagsRule.enableFlags(Flags.FLAG_ENABLE_WORKSPACE_INFLATION)
+
+ val itemInfo = FolderInfo()
+ itemInfo.itemType = ITEM_TYPE_APP_PAIR
+ itemInfo.contents.add(workspaceItemInfo())
+ itemInfo.contents.add(workspaceItemInfo())
+
+ val view =
+ VIEW_PREINFLATION_EXECUTOR.submit(
+ Callable { underTest.inflateItem(itemInfo, modelWriter) }
+ )
+ .get()
+
+ assertTrue(view is AppPairIcon)
+ assertEquals(itemInfo, view!!.tag)
+ }
+
+ @Test
+ fun test_pending_widget_inflated_on_UI() {
+ val itemInfo = widgetItemInfo(true)
+
+ val view =
+ MAIN_EXECUTOR.submit(Callable { underTest.inflateItem(itemInfo, modelWriter) }).get()
+
+ assertTrue(view is PendingAppWidgetHostView)
+ assertEquals(itemInfo, view!!.tag)
+ }
+
+ @Test
+ fun test_pending_widget_inflated_on_BG() {
+ setFlagsRule.enableFlags(Flags.FLAG_ENABLE_WORKSPACE_INFLATION)
+
+ val itemInfo = widgetItemInfo(true)
+ val view =
+ VIEW_PREINFLATION_EXECUTOR.submit(
+ Callable { underTest.inflateItem(itemInfo, modelWriter) }
+ )
+ .get()
+
+ assertTrue(view is PendingAppWidgetHostView)
+ assertEquals(itemInfo, view!!.tag)
+ }
+
+ @Test
+ fun test_widget_restored_and_inflated_on_UI() {
+ val itemInfo = widgetItemInfo(false)
+
+ val view =
+ MAIN_EXECUTOR.submit(Callable { underTest.inflateItem(itemInfo, modelWriter) }).get()
+
+ // Verify that the widget is automatically restored and a final widget is returned
+ assertTrue(view is LauncherAppWidgetHostView)
+ assertFalse(view is PendingAppWidgetHostView)
+ assertEquals(itemInfo, view!!.tag)
+ assertEquals(RESTORE_COMPLETED, itemInfo.restoreStatus)
+ verify(modelWriter).updateItemInDatabase(same(itemInfo))
+ }
+
+ @Test
+ fun test_widget_restored_and_inflated_on_BG() {
+ setFlagsRule.enableFlags(Flags.FLAG_ENABLE_WORKSPACE_INFLATION)
+ val itemInfo = widgetItemInfo(false)
+
+ val view =
+ VIEW_PREINFLATION_EXECUTOR.submit(
+ Callable { underTest.inflateItem(itemInfo, modelWriter) }
+ )
+ .get()
+
+ // Verify that the widget is automatically restored and a final widget is returned
+ assertTrue(view is LauncherAppWidgetHostView)
+ assertFalse(view is PendingAppWidgetHostView)
+ assertEquals(itemInfo, view!!.tag)
+ assertEquals(RESTORE_COMPLETED, itemInfo.restoreStatus)
+ verify(modelWriter).updateItemInDatabase(same(itemInfo))
+ }
+
+ @Test
+ fun test_invalid_widget_deleted() {
+ val itemInfo =
+ widgetItemInfo(false).apply {
+ providerName = ComponentName(providerName.packageName, "invalid_provider_name")
+ }
+ val view =
+ MAIN_EXECUTOR.submit(Callable { underTest.inflateItem(itemInfo, modelWriter) }).get()
+ assertNull(view)
+ verify(modelWriter).deleteItemFromDatabase(same(itemInfo), any())
+ }
+
+ @Test
+ fun test_normal_widget_inflated_UI() {
+ val providerInfo = TestViewHelpers.findWidgetProvider(false)
+ val id = widgetHolder.allocateAppWidgetId()
+ assertTrue(
+ WidgetManagerHelper(uiContext).bindAppWidgetIdIfAllowed(id, providerInfo, Bundle())
+ )
+ val itemInfo = LauncherAppWidgetInfo(id, providerInfo.provider)
+ itemInfo.spanX = 2
+ itemInfo.spanY = 2
+
+ val view =
+ MAIN_EXECUTOR.submit(Callable { underTest.inflateItem(itemInfo, modelWriter) }).get()
+
+ // Verify that the widget is automatically restored and a final widget is returned
+ assertTrue(view is LauncherAppWidgetHostView)
+ assertFalse(view is PendingAppWidgetHostView)
+ assertEquals(itemInfo, view!!.tag)
+ verifyNoMoreInteractions(modelWriter)
+ }
+
+ private fun workspaceItemInfo() =
+ AppInfo(
+ uiContext,
+ uiContext
+ .getSystemService(LauncherApps::class.java)!!
+ .getActivityList(testContext.packageName, Process.myUserHandle())[0],
+ Process.myUserHandle()
+ )
+ .makeWorkspaceItem(uiContext)
+
+ private fun widgetItemInfo(hasConfig: Boolean) =
+ LauncherAppWidgetInfo(0, TestViewHelpers.findWidgetProvider(hasConfig).component).apply {
+ spanX = 2
+ spanY = 2
+ restoreStatus = FLAG_ID_NOT_VALID or FLAG_UI_NOT_READY
+ }
+}