diff --git a/go/quickstep/res/values-my/strings.xml b/go/quickstep/res/values-my/strings.xml index cbb485a6d2..5fcb5bdc34 100644 --- a/go/quickstep/res/values-my/strings.xml +++ b/go/quickstep/res/values-my/strings.xml @@ -9,11 +9,11 @@ "မလုပ်တော့" "ဆက်တင်များ" "ဖန်သားပြင်ပေါ်ရှိ စာသားကို ဘာသာပြန်ပါ (သို့) နားထောင်ပါ" - "သင့်ဖန်သားပြင်ပေါ်ရှိ စာသား၊ ဝဘ်လိပ်စာနှင့် ဖန်သားပြင်ဓာတ်ပုံများကဲ့သို့ အချက်အလက်များကို Google နှင့် မျှဝေနိုင်သည်။\n\nသင်မျှဝေသည့် အချက်အလက်များကို ပြောင်းရန် ""ဆက်တင်များ > အက်ပ်များ > မူရင်းအက်ပ်များ > ဒစ်ဂျစ်တယ် Assistant အက်ပ်"" သို့ သွားပါ။" + "သင့်ဖန်သားပြင်ပေါ်ရှိ စာသား၊ ဝဘ်လိပ်စာနှင့် ဖန်သားပြင်ဓာတ်ပုံများကဲ့သို့ အချက်အလက်များကို Google နှင့် မျှဝေနိုင်သည်။\n\nသင်မျှဝေသည့် အချက်အလက်များကို ပြောင်းရန် ""ဆက်တင်များ > အက်ပ်များ > မူရင်းအက်ပ်များ > ဒစ်ဂျစ်တယ်အထောက်အကူ အက်ပ်"" သို့ သွားပါ။" "ဤဝန်ဆောင်မှုကို အသုံးပြုရန် assistant ရွေးပါ" - "ဖန်သားပြင်ပေါ်ရှိ စာသားကို နားထောင်ရန် (သို့) ဘာသာပြန်ဆိုရန် ‘ဆက်တင်များ’ တွင် ဒစ်ဂျစ်တယ် assistant အက်ပ် ရွေးပါ" + "ဖန်သားပြင်ပေါ်ရှိ စာသားကို နားထောင်ရန် (သို့) ဘာသာပြန်ဆိုရန် ‘ဆက်တင်များ’ တွင် ဒစ်ဂျစ်တယ်အထောက်အကူ အက်ပ် ရွေးပါ" "ဤဝန်ဆောင်မှုကို သုံးရန် assistant ကို ပြောင်းပါ" - "ဖန်သားပြင်ပေါ်ရှိ စာသားကို နားထောင်ရန် (သို့) ဘာသာပြန်ဆိုရန် ‘ဆက်တင်များ’ တွင် ဒစ်ဂျစ်တယ် assistant အက်ပ်ကို ပြောင်းပါ" + "ဖန်သားပြင်ပေါ်ရှိ စာသားကို နားထောင်ရန် (သို့) ဘာသာပြန်ဆိုရန် ‘ဆက်တင်များ’ တွင် ဒစ်ဂျစ်တယ်အထောက်အကူ အက်ပ်ကို ပြောင်းပါ" "ဤဖန်သားပြင်ပေါ်ရှိ စာသားကို နားထောင်ရန် ဤနေရာကို တို့ပါ" "ဤဖန်သားပြင်ပေါ်ရှိ စာသားကို ဘာသာပြန်ဆိုရန် ဤနေရာကို တို့ပါ" "ဤအက်ပ်ကို မျှဝေ၍မရပါ" diff --git a/quickstep/res/values-uk/strings.xml b/quickstep/res/values-uk/strings.xml index e18ddf5dcc..c20c213e00 100644 --- a/quickstep/res/values-uk/strings.xml +++ b/quickstep/res/values-uk/strings.xml @@ -95,7 +95,7 @@ "Поділитися" "Знімок екрана" "Розділити" - "Щоб розділити екран, виберіть ще один додаток" + "Щоб розділити екран, виберіть ще один додаток." "Щоб розділити екран, виберіть ще один додаток." "Скасувати" "Вийти з режиму розділення екрана" diff --git a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java index 851f2b3d73..2a6e37dc18 100644 --- a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java +++ b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java @@ -2049,7 +2049,7 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener private final RemoteAnimationTarget[] mAppTargets; private final Matrix mMatrix = new Matrix(); private final Point mTmpPos = new Point(); - private final Rect mCurrentRect = new Rect(); + private final RectF mCurrentRectF = new RectF(); private final float mStartRadius; private final float mEndRadius; private final SurfaceTransactionApplier mSurfaceApplier; @@ -2116,25 +2116,24 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener } if (target.mode == MODE_CLOSING) { - transferRectToTargetCoordinate(target, currentRectF, false, currentRectF); - currentRectF.round(mCurrentRect); + transferRectToTargetCoordinate(target, currentRectF, false, mCurrentRectF); // Scale the target window to match the currentRectF. final float scale; // We need to infer the crop (we crop the window to match the currentRectF). if (mWindowStartBounds.height() > mWindowStartBounds.width()) { - scale = Math.min(1f, currentRectF.width() / mWindowOriginalBounds.width()); + scale = Math.min(1f, mCurrentRectF.width() / mWindowOriginalBounds.width()); - int unscaledHeight = (int) (mCurrentRect.height() * (1f / scale)); + int unscaledHeight = (int) (mCurrentRectF.height() * (1f / scale)); int croppedHeight = mWindowStartBounds.height() - unscaledHeight; mTmpRect.set(0, 0, mWindowOriginalBounds.width(), mWindowStartBounds.height() - croppedHeight); } else { - scale = Math.min(1f, currentRectF.height() + scale = Math.min(1f, mCurrentRectF.height() / mWindowOriginalBounds.height()); - int unscaledWidth = (int) (mCurrentRect.width() * (1f / scale)); + int unscaledWidth = (int) (mCurrentRectF.width() * (1f / scale)); int croppedWidth = mWindowStartBounds.width() - unscaledWidth; mTmpRect.set(0, 0, mWindowStartBounds.width() - croppedWidth, mWindowOriginalBounds.height()); @@ -2142,7 +2141,7 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener // Match size and position of currentRect. mMatrix.setScale(scale, scale); - mMatrix.postTranslate(mCurrentRect.left, mCurrentRect.top); + mMatrix.postTranslate(mCurrentRectF.left, mCurrentRectF.top); builder.setMatrix(mMatrix) .setWindowCrop(mTmpRect) diff --git a/quickstep/src/com/android/launcher3/WidgetPickerActivity.java b/quickstep/src/com/android/launcher3/WidgetPickerActivity.java index 68e7824793..8c4db4a569 100644 --- a/quickstep/src/com/android/launcher3/WidgetPickerActivity.java +++ b/quickstep/src/com/android/launcher3/WidgetPickerActivity.java @@ -219,7 +219,9 @@ public class WidgetPickerActivity extends BaseActivity { final boolean isHorizontallyResizable = (info.resizeMode & AppWidgetProviderInfo.RESIZE_HORIZONTAL) != 0; if (mDesiredWidgetWidth > 0 && isHorizontallyResizable) { - if (info.maxResizeWidth > 0 && info.maxResizeWidth < mDesiredWidgetWidth) { + if (info.maxResizeWidth > 0 + && info.maxResizeWidth >= info.minWidth + && info.maxResizeWidth < mDesiredWidgetWidth) { return rejectWidget( widget, "maxResizeWidth[%d] < mDesiredWidgetWidth[%d]", @@ -227,12 +229,13 @@ public class WidgetPickerActivity extends BaseActivity { mDesiredWidgetWidth); } - final int minWidth = info.minResizeWidth > 0 ? info.minResizeWidth : info.minWidth; + final int minWidth = Math.min(info.minResizeWidth, info.minWidth); if (minWidth > mDesiredWidgetWidth) { return rejectWidget( widget, - "minWidth[%d] > mDesiredWidgetWidth[%d]", - minWidth, + "min(minWidth[%d], minResizeWidth[%d]) > mDesiredWidgetWidth[%d]", + info.minWidth, + info.minResizeWidth, mDesiredWidgetWidth); } } @@ -240,7 +243,9 @@ public class WidgetPickerActivity extends BaseActivity { final boolean isVerticallyResizable = (info.resizeMode & AppWidgetProviderInfo.RESIZE_VERTICAL) != 0; if (mDesiredWidgetHeight > 0 && isVerticallyResizable) { - if (info.maxResizeHeight > 0 && info.maxResizeHeight < mDesiredWidgetHeight) { + if (info.maxResizeHeight > 0 + && info.maxResizeHeight >= info.minHeight + && info.maxResizeHeight < mDesiredWidgetHeight) { return rejectWidget( widget, "maxResizeHeight[%d] < mDesiredWidgetHeight[%d]", @@ -248,20 +253,19 @@ public class WidgetPickerActivity extends BaseActivity { mDesiredWidgetHeight); } - final int minHeight = info.minResizeHeight > 0 ? info.minResizeHeight : info.minHeight; + final int minHeight = Math.min(info.minResizeHeight, info.minHeight); if (minHeight > mDesiredWidgetHeight) { return rejectWidget( widget, - "minHeight[%d] > mDesiredWidgetHeight[%d]", - minHeight, + "min(minHeight[%d], minResizeHeight[%d]) > mDesiredWidgetHeight[%d]", + info.minHeight, + info.minResizeHeight, mDesiredWidgetHeight); } } - if (!isHorizontallyResizable - && !isVerticallyResizable - && (info.minWidth < mDesiredWidgetWidth || info.minHeight < mDesiredWidgetHeight)) { - return rejectWidget(widget, "too small and not resizeable"); + if (!isHorizontallyResizable || !isVerticallyResizable) { + return rejectWidget(widget, "not resizeable"); } return acceptWidget(widget); @@ -271,12 +275,15 @@ public class WidgetPickerActivity extends BaseActivity { WidgetItem widget, String rejectionReason, Object... args) { return new WidgetAcceptabilityVerdict( false, - widget.label, + widget.widgetInfo != null + ? widget.widgetInfo.provider.flattenToShortString() + : widget.label, String.format(Locale.ENGLISH, rejectionReason, args)); } private static WidgetAcceptabilityVerdict acceptWidget(WidgetItem widget) { - return new WidgetAcceptabilityVerdict(true, widget.label, ""); + return new WidgetAcceptabilityVerdict( + true, widget.widgetInfo.provider.flattenToShortString(), ""); } private record WidgetAcceptabilityVerdict( diff --git a/quickstep/src/com/android/launcher3/taskbar/DesktopTaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/DesktopTaskbarUIController.java index 633325bf4b..2dd610c467 100644 --- a/quickstep/src/com/android/launcher3/taskbar/DesktopTaskbarUIController.java +++ b/quickstep/src/com/android/launcher3/taskbar/DesktopTaskbarUIController.java @@ -16,7 +16,10 @@ package com.android.launcher3.taskbar; +import androidx.annotation.Nullable; + import com.android.launcher3.uioverrides.QuickstepLauncher; +import com.android.quickstep.util.TISBindHelper; /** * A data source which integrates with a Launcher instance, used specifically for a @@ -50,4 +53,10 @@ public class DesktopTaskbarUIController extends TaskbarUIController { public boolean supportsVisualStashing() { return false; } + + @Nullable + @Override + protected TISBindHelper getTISBindHelper() { + return mLauncher.getTISBindHelper(); + } } diff --git a/quickstep/src/com/android/launcher3/taskbar/FallbackTaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/FallbackTaskbarUIController.java index f9816102d4..c0ecc6151a 100644 --- a/quickstep/src/com/android/launcher3/taskbar/FallbackTaskbarUIController.java +++ b/quickstep/src/com/android/launcher3/taskbar/FallbackTaskbarUIController.java @@ -21,11 +21,14 @@ import static com.android.launcher3.taskbar.TaskbarStashController.FLAG_IN_STASH import android.animation.Animator; +import androidx.annotation.Nullable; + import com.android.launcher3.popup.SystemShortcut; import com.android.launcher3.statemanager.StateManager; import com.android.quickstep.RecentsActivity; import com.android.quickstep.TopTaskTracker; import com.android.quickstep.fallback.RecentsState; +import com.android.quickstep.util.TISBindHelper; import com.android.quickstep.views.RecentsView; import java.util.stream.Stream; @@ -124,4 +127,10 @@ public class FallbackTaskbarUIController extends TaskbarUIController { .get(mControllers.taskbarActivityContext).getCachedTopTask(true); return topTask.isHomeTask() || topTask.isRecentsTask(); } + + @Nullable + @Override + protected TISBindHelper getTISBindHelper() { + return mRecentsActivity.getTISBindHelper(); + } } diff --git a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchView.java b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchView.java index 42c423ce83..2d9e23644d 100644 --- a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchView.java +++ b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchView.java @@ -147,7 +147,7 @@ public class KeyboardQuickSwitchView extends ConstraintLayout { KeyboardQuickSwitchTaskView taskView = (KeyboardQuickSwitchTaskView) layoutInflater.inflate( R.layout.keyboard_quick_switch_taskview, mContent, false); taskView.setId(View.generateViewId()); - taskView.setOnClickListener(v -> mViewCallbacks.launchTappedTask(index)); + taskView.setOnClickListener(v -> mViewCallbacks.launchTaskAt(index)); LayoutParams lp = new LayoutParams(width, mTaskViewHeight); // Create a left-to-right ordering of views (or right-to-left in RTL locales) @@ -186,7 +186,7 @@ public class KeyboardQuickSwitchView extends ConstraintLayout { KeyboardQuickSwitchTaskView overviewButton = (KeyboardQuickSwitchTaskView) layoutInflater.inflate( R.layout.keyboard_quick_switch_overview, this, false); - overviewButton.setOnClickListener(v -> mViewCallbacks.launchTappedTask(MAX_TASKS)); + overviewButton.setOnClickListener(v -> mViewCallbacks.launchTaskAt(MAX_TASKS)); overviewButton.findViewById(R.id.text).setText(overflowString); diff --git a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchViewController.java b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchViewController.java index 3e262e5f35..c830aa8640 100644 --- a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchViewController.java +++ b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchViewController.java @@ -22,7 +22,6 @@ import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR; import android.animation.Animator; import android.app.ActivityOptions; import android.view.KeyEvent; -import android.view.View; import android.view.animation.AnimationUtils; import android.window.RemoteTransition; @@ -137,7 +136,6 @@ public class KeyboardQuickSwitchViewController { } // Even with a valid index, this can be null if the user tries to quick switch before the // views have been added in the KeyboardQuickSwitchView. - View taskView = mKeyboardQuickSwitchView.getTaskAt(index); GroupTask task = mControllerCallbacks.getTaskAt(index); if (task == null) { return Math.max(0, index); @@ -198,13 +196,18 @@ public class KeyboardQuickSwitchViewController { && keyCode != KeyEvent.KEYCODE_DPAD_RIGHT && keyCode != KeyEvent.KEYCODE_DPAD_LEFT && keyCode != KeyEvent.KEYCODE_GRAVE - && keyCode != KeyEvent.KEYCODE_ESCAPE) { + && keyCode != KeyEvent.KEYCODE_ESCAPE + && keyCode != KeyEvent.KEYCODE_ENTER) { return false; } if (keyCode == KeyEvent.KEYCODE_GRAVE || keyCode == KeyEvent.KEYCODE_ESCAPE) { closeQuickSwitchView(true); return true; } + if (keyCode == KeyEvent.KEYCODE_ENTER) { + launchTaskAt(mCurrentFocusIndex); + return true; + } if (!allowTraversal) { return false; } @@ -234,9 +237,10 @@ public class KeyboardQuickSwitchViewController { mCurrentFocusIndex = index; } - void launchTappedTask(int index) { - KeyboardQuickSwitchViewController.this.launchTaskAt(index); - closeQuickSwitchView(true); + void launchTaskAt(int index) { + mCurrentFocusIndex = Utilities.boundToRange( + index, 0, mKeyboardQuickSwitchView.getChildCount() - 1); + mControllers.taskbarActivityContext.launchKeyboardFocusedTask(); } void updateThumbnailInBackground(Task task, Consumer 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" - - + "%1$s-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 ምግብር ወደ መነሻ ማያ ገፅ ታክሏል" "የአስተያየት ጥቆማዎች" - "ቀንዎን ያሳምሩ" + + "ዜና ለእርስዎ" "የሚያርፉበት ቦታዎ" "የአካል ብቃት ግቦችዎን ያሳኩ" "ለአየር ሁኔታው አስቀድመው ያቅዱ" "ይህንንም ሊወዱት ይችላሉ" - - + "%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 إلى الشاشة الرئيسية." "اقتراحات" - "تعزيز إنتاجية يومك" + + "أخبار مقترَحة لك" "محتوى ترفيهي مقترَح" "تحقيق أهداف اللياقة البدنية" "معرفة حالة الطقس أولاً بأول" "محتوى قد يعجبك أيضًا" - - + "تطبيقات \"%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 ৱিজেটটো গৃহ স্ক্ৰীনত যোগ দিয়া হৈছে" "পৰামৰ্শ" - "আপোনাৰ দিনটো কাৰ্যকৰী কৰি তোলক" + + "আপোনাৰ বাবে বাতৰি" "আপোনাৰ পচন্দৰ স্থান" "আপোনাৰ সুস্থতাৰ লক্ষ্যত উপনীত হওক" "বতৰৰ বিষয়ে আগতীয়াকৈ জানক" "আপুনি হয়তো এইটোও পচন্দ কৰিব" - - + "%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" - - + "%1$s 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 %1$s 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\" дададзены на галоўны экран" "Прапановы" - "Прадукцыйны дзень" + + "Навіны для вас" "Зона адпачынку" "Вашы фітнэс-мэты" "Прагноз надвор\'я" "Іншыя рэкамендацыі" - - + "Віджэты праграмы \"%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 е добавено към началния екран" "Предложения" - "Подобрете ежедневието си" + + "Новини за вас" "Зоната ви за разпускане" "Постигнете фитнес целите си" "Бъдете една крачка напред с прогнозата за времето" "Може също да харесате" - - + "Приспособленията за %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 উইজেট হোম স্ক্রিনে যোগ করা হয়েছে" "সাজেশন" - "আপনার দিন আরও ভালো করুন" + + "আপনার জন্য খবর" "আপনার চিল জোন" "আপনার ফিটনেস সংক্রান্ত লক্ষ্যে পৌঁছান" "আবহাওয়া সম্পর্কে আগেই খবর পান" "আপনার এগুলিও পছন্দ হতে পারে" - - + "%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 %1$s 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 %1$s 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 %1$s 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" - - + "%1$s-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" - - + "%1$s-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 προστέθηκε στην αρχική οθόνη." "Προτάσεις" - "Ενισχύστε την απόδοσή σας σήμερα" + + "Ειδήσεις για εσάς" "Ο δικός σας τρόπος χαλάρωσης" "Επιτύχετε τους στόχους που έχετε θέσει για τη φυσική σας κατάσταση" "Ετοιμαστείτε για κάθε καιρό" "Μπορεί να σας αρέσουν επίσης" - - + "Γραφικά στοιχεία %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" - - + "%1$s 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" - - + "%1$s 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" - - + "%1$s 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 %1$s 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 %1$s 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 %1$s 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" - - + "%1$s 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 به صفحه اصلی اضافه شد" "پیشنهادها" - "روزتان را پربار کنید" + + "اخبار برای شما" "منطقه آرامش شما" "دستیابی به اهداف تناسب اندام" "آب‌وهوا را پیش‌بینی کنید" "شاید این را هم بپسندید" - - + "ابزارک‌های %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ä" - - + "%1$s 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 %1$s à 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 %1$s à 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 %1$s á 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 વિજેટ ઉમેર્યુ" "સૂચનો" - "તમારા દિવસને બૂસ્ટ કરો" + + "તમારા માટે સમાચાર" "તમારો આરામદાયક ઝોન" "તમારા ફિટનેસ લક્ષ્યો પૂરા કરો" "હવામાન વિશે અપ ટૂ ડેટ રહો" "કદાચ તમને આ પણ પસંદ હોય" - - + "%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 विजेट को होम स्क्रीन पर जोड़ा गया" "सुझाव" - "अपना दिन बेहतर बनाएं" + + "आपके लिए खबरें" "आपके मनोरंजन के लिए" "फ़िटनेस के लक्ष्य हासिल करें" "मौसम की अप-टू-डेट जानकारी पाएं" "शायद आपको ये भी पसंद आएं" - - + "%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" - - + "%1$s –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 %1$s-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 վիջեթն ավելացվել է հիմնական էկրանին" "Առաջարկներ" - "Ակտիվացրեք ձեր օրը" + + "Նորություններ ձեզ համար" "Ձեր հանգստի գոտին" "Հասեք ձեր ֆիթնես նպատակներին" "Եղեք տեղեկացված եղանակի մասին" "Ձեզ կարող է դուր գալ" - - + %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 %1$s 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 á" - - + "%1$s-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 %1$s 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 נוסף למסך הבית" "הצעות" - "משפרים את היום" + + "חדשות בשבילך" "המקום שלך לרגיעה" "השגת יעדי הכושר שלך" "התעדכנות במזג האוויר" "אולי יעניין אותך גם" - - + "‫%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」ウィジェットをホーム画面に追加しました" "候補" - "生産性を向上" + + "あなたへのおすすめニュース" "休憩エリア" "フィットネスの目標を達成" "天気予報" "あなたへのおすすめ" - - + "%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 თქვენთვის" "განტვირთვის ადგილი" "მიაღწიეთ ფიტნეს-მიზნებს" "მიიღეთ ინფორმაცია წინასწარ ამინდის შესახებ" "ასევე შეიძლება მოგეწონოთ" - - + "%1$s ვიჯეტები მდებარეობს მარჯვნივ, ძებნა და პარამეტრები — მარცხნივ" "{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 виджеті негізгі экранға енгізілді." "Ұсыныстар" - "Күні бойы қуатты болыңыз" + + "Сізге арналған жаңалықтар" "Жанға жайлы жер" "Денені шынықтыру бойынша қойған мақсаттарыңызға жетіңіз" "Ауа райын алдын ала біліп отырыңыз" "Сізге мыналар да ұнауы мүмкін" - - + "%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 ទៅ​អេក្រង់ដើម" "ការណែនាំ" - "​ពង្រឹង​សុខុមាលភាពប្រចាំថ្ងៃរបស់អ្នក" + + "ព័ត៌មាន​សម្រាប់​អ្នក" "តំបន់បន្ធូរ​អារម្មណ៍របស់អ្នក" "សម្រេច​គោលដៅ​ហាត់ប្រាណ​របស់អ្នក" "ទទួលបានដំណឹងជាមុនអំពីអាកាសធាតុ" "អ្នក​ក៏​អាច​នឹង​ចូលចិត្ត" - - + "ធាតុក្រាហ្វិក %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 ವಿಜೆಟ್ ಅನ್ನು ಸೇರಿಸಲಾಗಿದೆ" "ಸಲಹೆಗಳು" - "ನಿಮ್ಮ ದಿನವನ್ನು ಇನ್ನಷ್ಟು ಉತ್ಸಾಹಗೊಳಿಸಿ" + + "ನಿಮಗಾಗಿ ಸುದ್ದಿ" "ನೀವು ವಿಶ್ರಾಂತಿ ಪಡೆಯುವ ಸ್ಥಳ" "ನಿಮ್ಮ ಫಿಟ್‌ನೆಸ್ ಗುರಿಗಳನ್ನು ಸಾಧಿಸಿ" "ಹವಾಮಾನದ ಕುರಿತು ಮುಂಚೆಯೇ ಅಪ್‌ಡೇಟ್‌ ಆಗಿರಿ" "ನಿಮಗೆ ಇವು ಕೂಡ ಇಷ್ಟವಾಗಬಹುದು" - - + "ಬಲಭಾಗದಲ್ಲಿ %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 위젯이 홈 화면에 추가됨" "추천" - "생산성 향상" + + "추천 뉴스" "휴식 공간" "피트니스 목표 달성" "사전에 날씨 확인" "좋아할 만한 항목" - - + "오른쪽에 %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 виджети башкы экранга кошулду" "Сунуштар" - "Майнаптуу күн" + + "Сиз үчүн жаңылыктар" "Чер жазуу" "Фитнес максаттарыңызга жетиңиз" "Аба ырайы тууралуу маалымат" "Төмөнкүлөр да жагышы мүмкүн" - - + "%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 ໃສ່ໂຮມສະກຣີນແລ້ວ" "ການແນະນຳ" - "ເຮັດໃຫ້ເປັນມື້ທີ່ມີປະສິດທິພາບ" + + "ຂ່າວສຳລັບທ່ານ" "ພື້ນທີ່ພັກຜ່ອນຂອງທ່ານ" "ບັນລຸເປົ້າໝາຍການອອກກຳລັງກາຍຂອງທ່ານ" "ຮູ້ສະພາບອາກາດລ່ວງໜ້າ" "ທ່ານອາດຈະມັກ" - - + "ວິດເຈັດ %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" - - + "%1$s 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 %1$s, 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 е додаден на почетниот екран" "Предлози" - "Подобрете го денот" + + "Вести за вас" "Вашата зона за релаксација" "Достигнете ги целите за фитнес" "Бидете во тек со временската прогноза" "Можеби ќе ви се допадне и" - - + "%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 വിജറ്റ് ഹോം സ്‌ക്രീനിലേക്ക് ചേർത്തു" "നിർദ്ദേശങ്ങൾ" - "കൂടുതൽ കാര്യക്ഷമമാകൂ" + + "നിങ്ങൾക്കായുള്ള വാർത്ത" "നിങ്ങൾക്ക് സുഖപ്രദമായ സ്ഥലം" "ശാരീരികക്ഷമതയുമായി ബന്ധപ്പെട്ട ലക്ഷ്യങ്ങൾ കൈവരിക്കൂ" "കാലാവസ്ഥ മുൻകൂട്ടി മനസ്സിലാക്കുക" "നിങ്ങൾക്ക് ഇനിപ്പറയുന്നവ ഇഷ്ടമായേക്കാം" - - + "വലതുവശത്ത് %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 виджетийг үндсэн нүүрэнд нэмсэн" "Зөвлөмжүүд" - "Өдрөө сайхан болгоорой" + + "Танд зориулсан мэдээ" "Таны амралтын бүс" "Фитнесийн зорилгодоо хүрээрэй" "Цаг агаарын урьдчилсан мэдээлэлтэй байгаарай" "Танд таалагдаж магадгүй" - - + "Баруун талд %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 हे विजेट तुमच्या होम स्क्रीनवर जोडले आहे" "सूचना" - "तुमचा दिवस आणखी उत्साहवर्धक करा" + + "तुमच्यासाठी बातम्या" "तुमचा आरामदायक झोन" "तुमची फिटनेस ध्येये गाठा" "हवामानासंबंधित बातम्या आगामी मिळवा" "तुम्हाला हेदेखील आवडू शकते" - - + "उजवीकडे %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 %1$s 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 ဝိဂျက်ကို ပင်မစာမျက်နှာတွင် ထည့်လိုက်ပြီ" "အကြံပြုချက်" - "သင့်နေ့ကို မွမ်းမံရန်" + + "သင့်အတွက် သတင်းများ" "သင်အနားယူသောနေရာ" "သင့်ကြံ့ခိုင်ရေးပန်းတိုင်ဆီ သွားရန်" "မိုးလေဝသကို ကြိုတင်ကာကွယ်ရန်" "သင်နှစ်သက်နိုင်သောအရာများ" - - + "%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" - - + "%1$s 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" "आफूले तय गरेको तन्दुरुस्तीको लक्ष्यमा पुग्नुहोस्" "मौसमको पूर्वानुमान प्राप्त गर्नुहोस्" "तपाईंलाई निम्न कुराहरू पनि मन पर्न सक्छन्" - - + "दायाँ भागमा %1$s विजेटहरू, बायाँ भागमा खोज र विकल्पहरू" "{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" - - + "%1$s-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ର ୱିଜେଟ ହୋମ ସ୍କ୍ରିନରେ ଯୋଡ଼ାଗଲା" "ପରାମର୍ଶଗୁଡ଼ିକ" - "ଆପଣଙ୍କ ଦିନକୁ ବୁଷ୍ଟ କରନ୍ତୁ" + + "ଆପଣଙ୍କ ପାଇଁ ନ୍ୟୁଜ" "ଆପଣଙ୍କ ଚିଲ ଜୋନ" "ଆପଣଙ୍କ ଫିଟନେସ ଲକ୍ଷ୍ୟରେ ପହଞ୍ଚନ୍ତୁ" "ପାଣିପାଗ ବିଷୟରେ ଆଗୁଆ ସୂଚନା ପାଆନ୍ତୁ" "ଆପଣ ମଧ୍ୟ ପସନ୍ଦ କରିପାରନ୍ତି" - - + "ଡାହାଣରେ %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 ਵਿਜੇਟ ਨੂੰ ਹੋਮ ਸਕ੍ਰੀਨ \'ਤੇ ਸ਼ਾਮਲ ਕੀਤਾ ਗਿਆ" "ਸੁਝਾਅ" - "ਆਪਣੇ ਦਿਨ ਨੂੰ ਬੂਸਟ ਕਰੋ" + + "ਤੁਹਾਡੇ ਲਈ ਖਬਰਾਂ" "ਤੁਹਾਡੇ ਲਈ ਸਕੂਨਮਈ ਖੇਤਰ" "ਆਪਣੇ ਫਿੱਟਨੈੱਸ ਸੰਬੰਧੀ ਟੀਚੇ ਹਾਸਲ ਕਰੋ" "ਮੌਸਮ ਬਾਰੇ ਅੱਪ-ਟੂ-ਡੇਟ ਰਹੋ" "ਸ਼ਾਇਦ ਤੁਸੀਂ ਇਹ ਵੀ ਪਸੰਦ ਕਰੋ" - - + "%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 (%1$s) 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 %1$s à 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 %1$s à 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 %1$s î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\" добавлен на главный экран" "Подсказки" - "Эффективная работа" + + "Новости для вас" "Развлечение и общение" "Ваши фитнес-цели" "Прогноз погоды" "Другие рекомендации" - - + "Виджеты приложения \"%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 විජට්ටුව මුල් පිටු තිරය වෙත එක් කරන ලදි" "යෝජනා" - "ඔබේ දවස වැඩි කරන්න" + + "ඔබ වෙනුවෙන් පුවත්" "ඔබේ නිවුණු කලාපය" "ඔබේ යෝග්‍යතා ඉලක්ක ළඟා කර ගන්න" "කාලගුණයට ඉදිරියෙන් සිටින්න" "ඔබ මේවාට ද කැමති විය හැක" - - + "දකුණේ %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 %1$s 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 %1$s 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 %1$s 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 на почетни екран" "Предлози" - "Побољшајте дан" + + "Вести за вас" "Зона за опуштање" "Остварите фитнес циљеве" "Будите у току са временским приликама" "Можда ће вам се допасти и" - - + "Виџети %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 %1$s 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 %1$s 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 விட்ஜெட் முகப்புத் திரையில் சேர்க்கப்பட்டது" "பரிந்துரைகள்" - "உங்கள் நாளை உற்சாகமாக்குங்கள்" + + "உங்களுக்கான செய்திகள்" "உங்கள் மனதுக்கு இதமானவை" "உடற்பயிற்சி இலக்குகளை அடையுங்கள்" "வானிலை குறித்து முன்கூட்டியே அறிந்திருங்கள்" "நீங்கள் இவற்றையும் விரும்பக்கூடும்" - - + "%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 విడ్జెట్ జోడించబడింది" "సూచనలు" - "మీ రోజును బూస్ట్ చేయండి" + + "మీ కోసం వార్తలు" "మీరు ప్రశాంతంగా ఉండే ప్రదేశం" "ఫిట్‌నెస్ లక్ష్యాలను చేరుకోండి" "వాతావరణాన్ని ముందుగానే తెలుసుకోండి" "మీరు వీటిని కూడా ఇష్టపడవచ్చు" - - + "కుడి వైపున %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 ลงในหน้าจอหลักแล้ว" "คำแนะนำ" - "เสริมสร้างวันของคุณ" + + "ข่าวสารสำหรับคุณ" "พื้นที่สบายๆ ของคุณ" "บรรลุเป้าหมายการออกกำลังกาย" "รู้สภาพอากาศล่วงหน้า" "คุณอาจชอบ" - - + "วิดเจ็ต%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 %1$s 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" - - + "%1$s 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 додано на головний екран" "Пропозиції" - "Підвищуйте свою продуктивність" + + "Новини для вас" "Ваша зона розваг" "Досягайте своїх фітнес-цілей" "Завчасно дізнавайтеся про зміни погоди" "Вам також може сподобатися" - - + "%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 ویجیٹ کو ہوم اسکرین میں شامل کیا گیا" "تجاویز" - "اپنی آج کی کارکردگی کو بوسٹ کریں" + + "آپ کے لیے خبریں" "آپ کا آرام دہ زون" "اپنی تندرستی کے مقاصد حاصل کریں" "موسم سے باخبر رہیں" "آپ کو یہ بھی پسند آ سکتا ہے" - - + "%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" - - + "%1$s 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 %1$s ở 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”微件添加到主屏幕" "建议" - "您的日常必备" + + "更多相关新闻" "您的休闲区" "达成您的健身目标" "天气早知道" "您可能还会喜欢" - - + "右边是%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」小工具加咗去主畫面" "建議" - "日常必備" + + "你的專屬新聞" "放鬆專區" "向健身目標邁進" "隨時掌握天氣資料" "相關推薦" - - + "右邊係「%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」小工具新增到主畫面" "建議" - "日常必備" + + "你的專屬新聞" "放鬆專區" "達成健身目標" "隨時掌握天氣資訊" "你可能也會喜歡的內容" - - + "右邊是「%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 %1$s 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 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 + } +}