Merge "Update AllSetActivity animations for certain device types" into main
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index 3d15e77..ff97b22 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -1,6 +1,13 @@
+[Builtin Hooks]
+ktfmt = true
+
+[Builtin Hooks Options]
+ktfmt = --kotlinlang-style
+
+[Tool Paths]
+ktfmt = ${REPO_ROOT}/prebuilts/build-tools/common/framework/ktfmt.jar
+
[Hook Scripts]
checkstyle_hook = ${REPO_ROOT}/prebuilts/checkstyle/checkstyle.py --config_xml tools/checkstyle.xml --sha ${PREUPLOAD_COMMIT}
-ktfmt_hook = ${REPO_ROOT}/external/ktfmt/ktfmt.py --check ${PREUPLOAD_FILES}
-
flag_hook = ${REPO_ROOT}/frameworks/base/packages/SystemUI/flag_check.py --msg=${PREUPLOAD_COMMIT_MESSAGE} --files=${PREUPLOAD_FILES} --project=${REPO_PATH}
diff --git a/aconfig/launcher.aconfig b/aconfig/launcher.aconfig
index 8682e5d..9147e4c 100644
--- a/aconfig/launcher.aconfig
+++ b/aconfig/launcher.aconfig
@@ -284,10 +284,3 @@
description: "Enables folders in all apps"
bug: "341582436"
}
-
-flag {
- name: "enable_tiny_taskbar"
- namespace: "launcher"
- description: "Enables Taskbar on phones"
- bug: "341784466"
-}
diff --git a/aconfig/launcher_search.aconfig b/aconfig/launcher_search.aconfig
index b243922..b98eee6 100644
--- a/aconfig/launcher_search.aconfig
+++ b/aconfig/launcher_search.aconfig
@@ -45,8 +45,11 @@
flag {
- name: "private_space_floating_mask_view"
+ name: "private_space_add_floating_mask_view"
namespace: "launcher_search"
description: "This flag enables the floating mask view as part of the Private Space animation. "
bug: "339850589"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
}
diff --git a/go/quickstep/res/layout/overview_actions_container.xml b/go/quickstep/res/layout/overview_actions_container.xml
index b1a6202..e31f462 100644
--- a/go/quickstep/res/layout/overview_actions_container.xml
+++ b/go/quickstep/res/layout/overview_actions_container.xml
@@ -124,23 +124,15 @@
</LinearLayout>
<!-- Unused. Included only for compatibility with parent class. -->
- <LinearLayout
- android:id="@+id/group_action_buttons"
- android:layout_width="match_parent"
- android:layout_height="@dimen/overview_actions_height"
+ <Button
+ android:id="@+id/action_save_app_pair"
+ style="@style/GoOverviewActionButton"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
android:layout_gravity="top|center_horizontal"
- android:orientation="horizontal"
- android:visibility="gone">
-
- <Button
- android:id="@+id/action_save_app_pair"
- style="@style/GoOverviewActionButton"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:drawableStart="@drawable/ic_save_app_pair_up_down"
- android:text="@string/action_save_app_pair"
- android:theme="@style/ThemeControlHighlightWorkspaceColor" />
-
- </LinearLayout>
+ android:drawableStart="@drawable/ic_save_app_pair_up_down"
+ android:text="@string/action_save_app_pair"
+ android:theme="@style/ThemeControlHighlightWorkspaceColor"
+ android:visibility="gone" />
</com.android.quickstep.views.GoOverviewActionsView>
\ No newline at end of file
diff --git a/quickstep/res/layout/digital_wellbeing_toast.xml b/quickstep/res/layout/digital_wellbeing_toast.xml
index 42cddbf..9144c7f 100644
--- a/quickstep/res/layout/digital_wellbeing_toast.xml
+++ b/quickstep/res/layout/digital_wellbeing_toast.xml
@@ -19,7 +19,7 @@
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
style="@style/TextTitle"
android:layout_width="match_parent"
- android:layout_height="48dp"
+ android:layout_height="@dimen/digital_wellbeing_toast_height"
android:background="@drawable/bg_wellbeing_toast"
android:forceHasOverlappingRendering="false"
android:gravity="center"
diff --git a/quickstep/res/layout/overview_actions_container.xml b/quickstep/res/layout/overview_actions_container.xml
index 7aaf744..fcd2e54 100644
--- a/quickstep/res/layout/overview_actions_container.xml
+++ b/quickstep/res/layout/overview_actions_container.xml
@@ -47,22 +47,16 @@
</LinearLayout>
- <LinearLayout
- android:id="@+id/group_action_buttons"
+ <!-- Currently, the only "group action button" is this save app pair button. If more are added,
+ a new LinearLayout may be needed to contain them, but beware of increased memory usage. -->
+ <Button
+ android:id="@+id/action_save_app_pair"
+ style="@style/OverviewActionButton"
android:layout_width="wrap_content"
- android:layout_height="@dimen/overview_actions_height"
+ android:layout_height="wrap_content"
+ android:text="@string/action_save_app_pair"
+ android:theme="@style/ThemeControlHighlightWorkspaceColor"
android:layout_gravity="bottom|center_horizontal"
- android:orientation="horizontal"
- android:visibility="gone">
-
- <Button
- android:id="@+id/action_save_app_pair"
- style="@style/OverviewActionButton"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/action_save_app_pair"
- android:theme="@style/ThemeControlHighlightWorkspaceColor" />
-
- </LinearLayout>
+ android:visibility="gone" />
</com.android.quickstep.views.OverviewActionsView>
\ No newline at end of file
diff --git a/quickstep/res/layout/taskbar_divider_popup_menu.xml b/quickstep/res/layout/taskbar_divider_popup_menu.xml
index 7f4f76c..b184191 100644
--- a/quickstep/res/layout/taskbar_divider_popup_menu.xml
+++ b/quickstep/res/layout/taskbar_divider_popup_menu.xml
@@ -16,8 +16,9 @@
-->
<com.android.launcher3.taskbar.TaskbarDividerPopupView
xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="@dimen/taskbar_pinning_popup_menu_width"
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ android:minWidth="@dimen/taskbar_pinning_popup_menu_width"
android:focusable="true"
android:background="@drawable/popup_background"
android:orientation="vertical">
@@ -51,14 +52,13 @@
android:layout_height="wrap_content"
android:id="@+id/taskbar_pinning_switch"
android:background="@null"
- android:clickable="false"
android:gravity="start|center_vertical"
android:textAlignment="viewStart"
android:paddingStart="12dp"
+ android:switchPadding="12dp"
android:layout_weight="1"
android:fontFamily="@*android:string/config_bodyFontFamilyMedium"
android:lines="1"
- android:ellipsize="end"
android:textSize="14sp"
android:textColor="?android:attr/textColorPrimary"
android:text="@string/always_show_taskbar" />
diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml
index 2021a0b..6bbf7f6 100644
--- a/quickstep/res/values/dimens.xml
+++ b/quickstep/res/values/dimens.xml
@@ -358,6 +358,9 @@
<dimen name="taskbar_running_app_indicator_height">4dp</dimen>
<dimen name="taskbar_running_app_indicator_width">14dp</dimen>
<dimen name="taskbar_running_app_indicator_top_margin">2dp</dimen>
+ <dimen name="taskbar_minimized_app_indicator_height">2dp</dimen>
+ <dimen name="taskbar_minimized_app_indicator_width">12dp</dimen>
+ <dimen name="taskbar_minimized_app_indicator_top_margin">2dp</dimen>
<!-- Transient taskbar -->
<dimen name="transient_taskbar_padding">12dp</dimen>
@@ -484,4 +487,7 @@
<dimen name="keyboard_quick_switch_no_recent_items_icon_size">24dp</dimen>
<dimen name="keyboard_quick_switch_no_recent_items_icon_margin">8dp</dimen>
+ <!-- Digital Wellbeing -->
+ <dimen name="digital_wellbeing_toast_height">48dp</dimen>
+
</resources>
diff --git a/quickstep/res/values/strings.xml b/quickstep/res/values/strings.xml
index cf987c3..340d25b 100644
--- a/quickstep/res/values/strings.xml
+++ b/quickstep/res/values/strings.xml
@@ -332,4 +332,14 @@
<!-- Accessibility label for quick switch tiles showing split tasks [CHAR LIMIT=NONE] -->
<string name="quick_switch_split_task"><xliff:g id="app_name_1" example="Chrome">%1$s</xliff:g> and <xliff:g id="app_name_2" example="Gmail">%2$s</xliff:g></string>
+
+ <!-- Strings for bubble bar -->
+ <!-- Fallback name for a bubble if it does have a title [CHAR_LIMIT=none] -->
+ <string name="bubble_bar_bubble_fallback_description">Bubble</string>
+ <!-- content description for the overflow bubble [CHAR_LIMIT=none] -->
+ <string name="bubble_bar_overflow_description">Overflow</string>
+ <!-- Content description for a bubble bar bubble. [CHAR_LIMIT=none] -->
+ <string name="bubble_bar_bubble_description"><xliff:g id="notification_title" example="some title">%1$s</xliff:g> from <xliff:g id="app_name" example="YouTube">%2$s</xliff:g></string>
+ <!-- Content description for bubble bar when it has multiple bubbles. [CHAR_LIMIT=NONE] -->
+ <string name="bubble_bar_description_multiple_bubbles"><xliff:g id="bubble_bar_bubble_description" example="some title from YouTube">%1$s</xliff:g> and <xliff:g id="bubble_count" example="4">%2$d</xliff:g> more</string>
</resources>
diff --git a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
index 0c499b8..fae281a 100644
--- a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
+++ b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
@@ -151,6 +151,7 @@
import com.android.quickstep.util.RectFSpringAnim;
import com.android.quickstep.util.RectFSpringAnim.DefaultSpringConfig;
import com.android.quickstep.util.RectFSpringAnim.TaskbarHotseatSpringConfig;
+import com.android.quickstep.util.ScalingWorkspaceRevealAnim;
import com.android.quickstep.util.StaggeredWorkspaceAnim;
import com.android.quickstep.util.SurfaceTransaction;
import com.android.quickstep.util.SurfaceTransaction.SurfaceProperties;
@@ -174,7 +175,6 @@
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
-import java.util.Map.Entry;
/**
* Manages the opening and closing app transitions from Launcher
@@ -1630,10 +1630,15 @@
anim.play(getUnlockWindowAnimator(appTargets, wallpaperTargets));
} else if (ENABLE_BACK_SWIPE_HOME_ANIMATION.get()
&& !playFallBackAnimation) {
- // Use a fixed velocity to start the animation.
- float velocityPxPerS = DynamicResource.provider(mLauncher)
- .getDimension(R.dimen.unlock_staggered_velocity_dp_per_s);
- PointF velocity = new PointF(0, -velocityPxPerS);
+ PointF velocity;
+ if (enableScalingRevealHomeAnimation()) {
+ velocity = new PointF();
+ } else {
+ // Use a fixed velocity to start the animation.
+ float velocityPxPerS = DynamicResource.provider(mLauncher)
+ .getDimension(R.dimen.unlock_staggered_velocity_dp_per_s);
+ velocity = new PointF(0, -velocityPxPerS);
+ }
rectFSpringAnim = getClosingWindowAnimators(
anim, appTargets, launcherView, velocity, startRect,
startWindowCornerRadius);
@@ -1642,8 +1647,15 @@
// layout bounds.
skipAllAppsScale = true;
} else if (!fromPredictiveBack) {
- anim.play(new StaggeredWorkspaceAnim(mLauncher, velocity.y,
- true /* animateOverviewScrim */, launcherView).getAnimators());
+ if (enableScalingRevealHomeAnimation()) {
+ anim.play(
+ new ScalingWorkspaceRevealAnim(
+ mLauncher, rectFSpringAnim,
+ rectFSpringAnim.getTargetRect()).getAnimators());
+ } else {
+ anim.play(new StaggeredWorkspaceAnim(mLauncher, velocity.y,
+ true /* animateOverviewScrim */, launcherView).getAnimators());
+ }
if (!areAllTargetsTranslucent(appTargets)) {
anim.play(ObjectAnimator.ofFloat(mLauncher.getDepthController().stateDepth,
diff --git a/quickstep/src/com/android/launcher3/desktop/DesktopRecentsTransitionController.kt b/quickstep/src/com/android/launcher3/desktop/DesktopRecentsTransitionController.kt
index 3ef8e54..0000c0d 100644
--- a/quickstep/src/com/android/launcher3/desktop/DesktopRecentsTransitionController.kt
+++ b/quickstep/src/com/android/launcher3/desktop/DesktopRecentsTransitionController.kt
@@ -30,6 +30,7 @@
import com.android.quickstep.SystemUiProxy
import com.android.quickstep.TaskViewUtils
import com.android.quickstep.views.DesktopTaskView
+import com.android.wm.shell.common.desktopmode.DesktopModeTransitionSource
import java.util.function.Consumer
/** Manage recents related operations with desktop tasks */
@@ -57,8 +58,8 @@
}
/** Launch desktop tasks from recents view */
- fun moveToDesktop(taskId: Int) {
- systemUiProxy.moveToDesktop(taskId)
+ fun moveToDesktop(taskId: Int, transitionSource: DesktopModeTransitionSource) {
+ systemUiProxy.moveToDesktop(taskId, transitionSource)
}
private class RemoteDesktopLaunchTransitionRunner(
diff --git a/quickstep/src/com/android/launcher3/taskbar/DesktopTaskbarRunningAppsController.kt b/quickstep/src/com/android/launcher3/taskbar/DesktopTaskbarRunningAppsController.kt
index 3649c4e..d4bef28 100644
--- a/quickstep/src/com/android/launcher3/taskbar/DesktopTaskbarRunningAppsController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/DesktopTaskbarRunningAppsController.kt
@@ -47,6 +47,7 @@
private var apps: Array<AppInfo>? = null
private var allRunningDesktopAppInfos: List<AppInfo>? = null
+ private var allMinimizedDesktopAppInfos: List<AppInfo>? = null
private val desktopVisibilityController: DesktopVisibilityController?
get() = desktopVisibilityControllerProvider()
@@ -95,6 +96,13 @@
return allRunningDesktopAppInfos?.mapNotNull { it.targetPackage }?.toSet() ?: emptySet()
}
+ override fun getMinimizedApps(): Set<String> {
+ if (!isInDesktopMode) {
+ return emptySet()
+ }
+ return allMinimizedDesktopAppInfos?.mapNotNull { it.targetPackage }?.toSet() ?: emptySet()
+ }
+
@VisibleForTesting
public override fun updateRunningApps() {
if (!isInDesktopMode) {
@@ -102,10 +110,34 @@
mControllers.taskbarViewController.commitRunningAppsToUI()
return
}
- allRunningDesktopAppInfos = getRunningDesktopAppInfos()
+ val runningTasks = getDesktopRunningTasks()
+ val runningAppInfo = getAppInfosFromRunningTasks(runningTasks)
+ allRunningDesktopAppInfos = runningAppInfo
+ updateMinimizedApps(runningTasks, runningAppInfo)
mControllers.taskbarViewController.commitRunningAppsToUI()
}
+ private fun updateMinimizedApps(
+ runningTasks: List<RunningTaskInfo>,
+ runningAppInfo: List<AppInfo>,
+ ) {
+ val allRunningAppTasks =
+ runningAppInfo
+ .mapNotNull { appInfo -> appInfo.targetPackage?.let { appInfo to it } }
+ .associate { (appInfo, targetPackage) ->
+ appInfo to
+ runningTasks
+ .filter { it.realActivity?.packageName == targetPackage }
+ .map { it.taskId }
+ }
+ val minimizedTaskIds = runningTasks.associate { it.taskId to !it.isVisible }
+ allMinimizedDesktopAppInfos =
+ allRunningAppTasks
+ .filterValues { taskIds -> taskIds.all { minimizedTaskIds[it] ?: false } }
+ .keys
+ .toList()
+ }
+
private fun getRunningDesktopAppInfosExceptHotseatApps(
allRunningDesktopAppInfos: List<AppInfo>,
hotseatItems: List<ItemInfo>
@@ -116,15 +148,10 @@
.map { WorkspaceItemInfo(it) }
}
- private fun getRunningDesktopAppInfos(): List<AppInfo> {
- return getAppInfosFromRunningTasks(
- recentsModel.runningTasks
- .filter { taskInfo: RunningTaskInfo ->
- taskInfo.windowingMode == WindowConfiguration.WINDOWING_MODE_FREEFORM
- }
- .toList()
- )
- }
+ private fun getDesktopRunningTasks(): List<RunningTaskInfo> =
+ recentsModel.runningTasks.filter { taskInfo: RunningTaskInfo ->
+ taskInfo.windowingMode == WindowConfiguration.WINDOWING_MODE_FREEFORM
+ }
// TODO(b/335398876) fetch app icons from Tasks instead of AppInfos
private fun getAppInfosFromRunningTasks(tasks: List<RunningTaskInfo>): List<AppInfo> {
@@ -138,9 +165,10 @@
.filterNotNull()
}
- private fun <E> SparseArray<E>.toList(): List<E> {
- return valueIterator().asSequence().toList()
- }
+ private fun getAppInfosFromRunningTask(task: RunningTaskInfo): AppInfo? =
+ apps?.firstOrNull { it.targetPackage == task.realActivity?.packageName }
+
+ private fun <E> SparseArray<E>.toList(): List<E> = valueIterator().asSequence().toList()
companion object {
private const val TAG = "TabletDesktopTaskbarRunningAppsController"
diff --git a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchController.java b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchController.java
index b213203..358d703 100644
--- a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchController.java
@@ -148,7 +148,7 @@
});
}
- private void processLoadedTasks(ArrayList<GroupTask> tasks) {
+ private void processLoadedTasks(List<GroupTask> tasks) {
// Only store MAX_TASK tasks, from most to least recent
Collections.reverse(tasks);
mTasks = tasks.stream()
@@ -157,7 +157,7 @@
mNumHiddenTasks = Math.max(0, tasks.size() - MAX_TASKS);
}
- private void processLoadedTasksOnDesktop(ArrayList<GroupTask> tasks) {
+ private void processLoadedTasksOnDesktop(List<GroupTask> tasks) {
// Find the single desktop task that contains a grouping of desktop tasks
DesktopTask desktopTask = findDesktopTask(tasks);
@@ -173,7 +173,7 @@
}
@Nullable
- private DesktopTask findDesktopTask(ArrayList<GroupTask> tasks) {
+ private DesktopTask findDesktopTask(List<GroupTask> tasks) {
return (DesktopTask) tasks.stream()
.filter(t -> t instanceof DesktopTask)
.findFirst()
diff --git a/quickstep/src/com/android/launcher3/taskbar/StashedHandleViewController.java b/quickstep/src/com/android/launcher3/taskbar/StashedHandleViewController.java
index 8d4c34d..252f2a8 100644
--- a/quickstep/src/com/android/launcher3/taskbar/StashedHandleViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/StashedHandleViewController.java
@@ -118,7 +118,7 @@
mControllers = controllers;
DeviceProfile deviceProfile = mActivity.getDeviceProfile();
Resources resources = mActivity.getResources();
- if (mActivity.isPhoneGestureNavMode()) {
+ if (mActivity.isPhoneGestureNavMode() || mActivity.isTinyTaskbar()) {
mTaskbarSize = resources.getDimensionPixelSize(R.dimen.taskbar_phone_size);
mStashedHandleWidth =
resources.getDimensionPixelSize(R.dimen.taskbar_stashed_small_screen);
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
index 1571ac0..0de0550 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
@@ -43,6 +43,7 @@
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_VOICE_INTERACTION_WINDOW_SHOWING;
import static com.android.window.flags.Flags.enableDesktopWindowingMode;
import static com.android.window.flags.Flags.enableDesktopWindowingTaskbarRunningApps;
+import static com.android.wm.shell.Flags.enableTinyTaskbar;
import android.animation.AnimatorSet;
import android.animation.ValueAnimator;
@@ -416,7 +417,9 @@
* single window for taskbar and navbar.
*/
public boolean isPhoneMode() {
- return ENABLE_TASKBAR_NAVBAR_UNIFICATION && mDeviceProfile.isPhone;
+ return ENABLE_TASKBAR_NAVBAR_UNIFICATION
+ && mDeviceProfile.isPhone
+ && !mDeviceProfile.isTaskbarPresent;
}
/**
@@ -433,6 +436,11 @@
return isPhoneMode() && !isThreeButtonNav();
}
+ /** Returns {@code true} iff a tiny version of taskbar is shown on phone. */
+ public boolean isTinyTaskbar() {
+ return enableTinyTaskbar() && mDeviceProfile.isPhone && mDeviceProfile.isTaskbarPresent;
+ }
+
/**
* Returns if software keyboard is docked or input toolbar is placed at the taskbar area
*/
@@ -981,7 +989,7 @@
public int getDefaultTaskbarWindowSize() {
Resources resources = getResources();
- if (ENABLE_TASKBAR_NAVBAR_UNIFICATION && mDeviceProfile.isPhone) {
+ if (isPhoneMode()) {
return isThreeButtonNav() ?
resources.getDimensionPixelSize(R.dimen.taskbar_phone_size) :
resources.getDimensionPixelSize(R.dimen.taskbar_stashed_size);
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarBackgroundRenderer.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarBackgroundRenderer.kt
index e290c3f..bafd059 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarBackgroundRenderer.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarBackgroundRenderer.kt
@@ -97,8 +97,11 @@
fun updateStashedHandleWidth(context: TaskbarActivityContext, res: Resources) {
stashedHandleWidth =
res.getDimensionPixelSize(
- if (context.isPhoneMode) R.dimen.taskbar_stashed_small_screen
- else R.dimen.taskbar_stashed_handle_width
+ if (context.isPhoneMode || context.isTinyTaskbar) {
+ R.dimen.taskbar_stashed_small_screen
+ } else {
+ R.dimen.taskbar_stashed_handle_width
+ }
)
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDividerPopupView.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarDividerPopupView.kt
index 12f1e63..a635537 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDividerPopupView.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDividerPopupView.kt
@@ -69,8 +69,6 @@
private lateinit var dividerView: View
- private val menuWidth =
- resources.getDimensionPixelSize(R.dimen.taskbar_pinning_popup_menu_width)
private val popupCornerRadius = Themes.getDialogCornerRadius(context)
private val arrowWidth = resources.getDimension(R.dimen.popup_arrow_width)
private val arrowHeight = resources.getDimension(R.dimen.popup_arrow_height)
@@ -98,16 +96,21 @@
popupContainer.getDescendantRectRelativeToSelf(dividerView, outPos)
}
- @SuppressLint("UseSwitchCompatOrMaterialCode")
+ @SuppressLint("UseSwitchCompatOrMaterialCode", "ClickableViewAccessibility")
override fun onFinishInflate() {
super.onFinishInflate()
val taskbarSwitchOption = requireViewById<LinearLayout>(R.id.taskbar_switch_option)
val alwaysShowTaskbarSwitch = requireViewById<Switch>(R.id.taskbar_pinning_switch)
val taskbarVisibilityIcon = requireViewById<View>(R.id.taskbar_pinning_visibility_icon)
+
alwaysShowTaskbarSwitch.isChecked = alwaysShowTaskbarOn
+ alwaysShowTaskbarSwitch.setOnTouchListener { view, event ->
+ (view.parent as View).onTouchEvent(event)
+ }
+ alwaysShowTaskbarSwitch.setOnClickListener { view -> (view.parent as View).performClick() }
+
if (ActivityContext.lookupContext<TaskbarActivityContext>(context).isGestureNav) {
taskbarSwitchOption.setOnClickListener {
- alwaysShowTaskbarSwitch.isClickable = true
alwaysShowTaskbarSwitch.isChecked = !alwaysShowTaskbarOn
onClickAlwaysShowTaskbarSwitchOption()
}
@@ -125,7 +128,7 @@
/** Orient object as usual and then center object horizontally. */
override fun orientAboutObject() {
super.orientAboutObject()
- x = mTempRect.centerX() - menuWidth / 2f
+ x = mTempRect.centerX() - measuredWidth / 2f
}
override fun onControllerInterceptTouchEvent(ev: MotionEvent?): Boolean {
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarEduTooltipController.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarEduTooltipController.kt
index d43055d..5cbd5c9 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarEduTooltipController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarEduTooltipController.kt
@@ -81,7 +81,11 @@
protected val activityContext: TaskbarActivityContext = ActivityContext.lookupContext(context)
open val shouldShowSearchEdu = false
private val isTooltipEnabled: Boolean
- get() = !Utilities.isRunningInTestHarness() && !activityContext.isPhoneMode
+ get() {
+ return !Utilities.isRunningInTestHarness() &&
+ !activityContext.isPhoneMode &&
+ !activityContext.isTinyTaskbar
+ }
private val isOpen: Boolean
get() = tooltip?.isOpen ?: false
val isBeforeTooltipFeaturesStep: Boolean
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt
index 4a8ed87..e1ddb6a 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt
@@ -51,6 +51,7 @@
import com.android.launcher3.config.FeatureFlags.ENABLE_TASKBAR_NAVBAR_UNIFICATION
import com.android.launcher3.config.FeatureFlags.enableTaskbarNoRecreate
import com.android.launcher3.taskbar.TaskbarControllers.LoggableTaskbarController
+import com.android.launcher3.testing.shared.ResourceUtils
import com.android.launcher3.util.DisplayController
import java.io.PrintWriter
import kotlin.jvm.optionals.getOrNull
@@ -231,8 +232,24 @@
val contentHeight = controllers.taskbarStashController.contentHeightToReportToApps
val tappableHeight = controllers.taskbarStashController.tappableHeightToReportToApps
val res = context.resources
- if (provider.type == navigationBars() || provider.type == mandatorySystemGestures()) {
+ if (provider.type == navigationBars()) {
provider.insetsSize = getInsetsForGravityWithCutout(contentHeight, gravity, endRotation)
+ } else if (provider.type == mandatorySystemGestures()) {
+ if (context.isThreeButtonNav) {
+ provider.insetsSize = Insets.of(0, 0, 0, 0)
+ } else {
+ val gestureHeight =
+ ResourceUtils.getNavbarSize(
+ ResourceUtils.NAVBAR_BOTTOM_GESTURE_SIZE,
+ context.resources)
+ val isPinnedTaskbar = context.deviceProfile.isTaskbarPresent
+ && !context.deviceProfile.isTransientTaskbar
+ val mandatoryGestureHeight =
+ if (isPinnedTaskbar) contentHeight
+ else gestureHeight
+ provider.insetsSize = getInsetsForGravityWithCutout(mandatoryGestureHeight, gravity,
+ endRotation)
+ }
} else if (provider.type == tappableElement()) {
provider.insetsSize = getInsetsForGravity(tappableHeight, gravity)
} else if (provider.type == systemGestures() && provider.index == INDEX_LEFT) {
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
index e8dc177..ec2cee2 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
@@ -519,7 +519,7 @@
}
}
- private static boolean isTaskbarEnabled(DeviceProfile deviceProfile) {
+ private boolean isTaskbarEnabled(DeviceProfile deviceProfile) {
return ENABLE_TASKBAR_NAVBAR_UNIFICATION || deviceProfile.isTaskbarPresent;
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarModelCallbacks.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarModelCallbacks.java
index 9f24d38..35e1c7b 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarModelCallbacks.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarModelCallbacks.java
@@ -68,7 +68,7 @@
// Used to defer any UI updates during the SUW unstash animation.
private boolean mDeferUpdatesForSUW;
private Runnable mDeferredUpdates;
- private DesktopVisibilityController.DesktopVisibilityListener mDesktopVisibilityListener =
+ private final DesktopVisibilityController.DesktopVisibilityListener mDesktopVisibilityListener =
visible -> updateRunningApps();
public TaskbarModelCallbacks(
@@ -235,20 +235,23 @@
hotseatItemInfos = mControllers.taskbarRecentAppsController
.updateHotseatItemInfos(hotseatItemInfos);
Set<String> runningPackages = mControllers.taskbarRecentAppsController.getRunningApps();
+ Set<String> minimizedPackages = mControllers.taskbarRecentAppsController.getMinimizedApps();
if (mDeferUpdatesForSUW) {
ItemInfo[] finalHotseatItemInfos = hotseatItemInfos;
mDeferredUpdates = () ->
- commitHotseatItemUpdates(finalHotseatItemInfos, runningPackages);
+ commitHotseatItemUpdates(finalHotseatItemInfos, runningPackages,
+ minimizedPackages);
} else {
- commitHotseatItemUpdates(hotseatItemInfos, runningPackages);
+ commitHotseatItemUpdates(hotseatItemInfos, runningPackages, minimizedPackages);
}
}
- private void commitHotseatItemUpdates(
- ItemInfo[] hotseatItemInfos, Set<String> runningPackages) {
+ private void commitHotseatItemUpdates(ItemInfo[] hotseatItemInfos, Set<String> runningPackages,
+ Set<String> minimizedPackages) {
mContainer.updateHotseatItems(hotseatItemInfos);
- mControllers.taskbarViewController.updateIconViewsRunningStates(runningPackages);
+ mControllers.taskbarViewController.updateIconViewsRunningStates(runningPackages,
+ minimizedPackages);
}
/**
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarRecentAppsController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarRecentAppsController.java
index a29c74b..606ba5b 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarRecentAppsController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarRecentAppsController.java
@@ -69,4 +69,9 @@
public Set<String> getRunningApps() {
return emptySet();
}
+
+ /** Returns the set of apps whose tasks are all minimized. */
+ public Set<String> getMinimizedApps() {
+ return emptySet();
+ }
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
index 74d2d60..8fdb460 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
@@ -100,7 +100,10 @@
// If we're in an app and any of these flags are enabled, taskbar should be stashed.
private static final int FLAGS_STASHED_IN_APP = FLAG_STASHED_IN_APP_SYSUI
| FLAG_STASHED_IN_APP_SETUP | FLAG_STASHED_IN_TASKBAR_ALL_APPS
- | FLAG_STASHED_SMALL_SCREEN | FLAG_STASHED_IN_APP_AUTO;
+ | FLAG_STASHED_SMALL_SCREEN | FLAG_STASHED_IN_APP_AUTO | FLAG_STASHED_IME;
+
+ // If we're in overview and any of these flags are enabled, taskbar should be stashed.
+ private static final int FLAGS_STASHED_IN_OVERVIEW = FLAG_STASHED_IME;
// If any of these flags are enabled, inset apps by our stashed height instead of our unstashed
// height. This way the reported insets are consistent even during transitions out of the app.
@@ -111,7 +114,7 @@
// If any of these flags are enabled, the taskbar must be stashed.
private static final int FLAGS_FORCE_STASHED = FLAG_STASHED_SYSUI | FLAG_STASHED_DEVICE_LOCKED
- | FLAG_STASHED_IN_TASKBAR_ALL_APPS | FLAG_STASHED_SMALL_SCREEN | FLAG_STASHED_IME;
+ | FLAG_STASHED_IN_TASKBAR_ALL_APPS | FLAG_STASHED_SMALL_SCREEN;
/**
* How long to stash/unstash when manually invoked via long press.
@@ -244,8 +247,13 @@
boolean inApp = hasAnyFlag(flags, FLAGS_IN_APP);
boolean stashedInApp = hasAnyFlag(flags, FLAGS_STASHED_IN_APP);
boolean stashedLauncherState = hasAnyFlag(flags, FLAG_IN_STASHED_LAUNCHER_STATE);
+ boolean inOverview = hasAnyFlag(flags, FLAG_IN_OVERVIEW);
+ boolean stashedInOverview = hasAnyFlag(flags, FLAGS_STASHED_IN_OVERVIEW);
boolean forceStashed = hasAnyFlag(flags, FLAGS_FORCE_STASHED);
- return (inApp && stashedInApp) || (!inApp && stashedLauncherState) || forceStashed;
+ return (inApp && stashedInApp)
+ || (!inApp && stashedLauncherState)
+ || (inOverview && stashedInOverview)
+ || forceStashed;
};
private final StatePropertyHolder mStatePropertyHolder = new StatePropertyHolder(
mIsStashedPredicate);
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
index e4ccc58..570221c 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
@@ -25,7 +25,6 @@
import static com.android.launcher3.config.FeatureFlags.enableTaskbarPinning;
import static com.android.launcher3.icons.IconNormalizer.ICON_VISIBLE_AREA_FACTOR;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
-import static com.android.quickstep.RecentsAnimationDeviceState.QUICKSTEP_TOUCH_SLOP_RATIO_TWO_BUTTON;
import android.content.Context;
import android.content.res.Resources;
@@ -33,7 +32,6 @@
import android.graphics.Rect;
import android.os.Bundle;
import android.util.AttributeSet;
-import android.util.Log;
import android.view.DisplayCutout;
import android.view.InputDevice;
import android.view.LayoutInflater;
@@ -68,7 +66,6 @@
import com.android.launcher3.views.ActivityContext;
import com.android.launcher3.views.IconButtonView;
import com.android.quickstep.DeviceConfigWrapper;
-import com.android.quickstep.RecentsAnimationDeviceState;
import com.android.quickstep.util.AssistStateManager;
import java.util.function.Predicate;
@@ -78,8 +75,6 @@
*/
public class TaskbarView extends FrameLayout implements FolderIcon.FolderIconParent, Insettable,
DeviceProfile.OnDeviceProfileChangeListener {
- private static final String TAG = "TaskbarView";
-
private static final Rect sTmpRect = new Rect();
private final int[] mTempOutLocation = new int[2];
@@ -89,10 +84,8 @@
private final int mItemPadding;
private final int mFolderLeaveBehindColor;
private final boolean mIsRtl;
- private final boolean mIsTransientTaskbar;
private final TaskbarActivityContext mActivityContext;
- private final RecentsAnimationDeviceState mDeviceState;
// Initialized in init.
private TaskbarViewCallbacks mControllerCallbacks;
@@ -107,8 +100,6 @@
private Runnable mAllAppsTouchRunnable;
private long mAllAppsButtonTouchDelayMs;
private boolean mAllAppsTouchTriggered;
- private MotionEvent mCurrentDownEvent;
- private float mTouchSlopSquared;
// Only non-null when device supports having an All Apps button.
private @Nullable IconButtonView mTaskbarDivider;
@@ -138,7 +129,7 @@
mActivityContext = ActivityContext.lookupContext(context);
mIconLayoutBounds = mActivityContext.getTransientTaskbarBounds();
Resources resources = getResources();
- mIsTransientTaskbar = DisplayController.isTransientTaskbar(mActivityContext)
+ boolean isTransientTaskbar = DisplayController.isTransientTaskbar(mActivityContext)
&& !mActivityContext.isPhoneMode();
mIsRtl = Utilities.isRtl(resources);
mTransientTaskbarMinWidth = resources.getDimension(R.dimen.transient_taskbar_min_width);
@@ -172,7 +163,7 @@
mAllAppsButton = (IconButtonView) LayoutInflater.from(context)
.inflate(R.layout.taskbar_all_apps_button, this, false);
mAllAppsButton.setIconDrawable(resources.getDrawable(
- getAllAppsButton(mIsTransientTaskbar)));
+ getAllAppsButton(isTransientTaskbar)));
mAllAppsButton.setPadding(mItemPadding, mItemPadding, mItemPadding, mItemPadding);
mAllAppsButton.setForegroundTint(
mActivityContext.getColor(R.color.all_apps_button_color));
@@ -191,10 +182,6 @@
// Default long press (touch) delay = 400ms
mAllAppsButtonTouchDelayMs = ViewConfiguration.getLongPressTimeout();
- // Default touch slop
- mDeviceState = new RecentsAnimationDeviceState(mContext);
- mTouchSlopSquared = mDeviceState.getSquaredTouchSlop(
- QUICKSTEP_TOUCH_SLOP_RATIO_TWO_BUTTON, 1);
}
@DrawableRes
@@ -299,12 +286,6 @@
&& assistStateManager.getLPNHDurationMillis().isPresent()) {
mAllAppsButtonTouchDelayMs = assistStateManager.getLPNHDurationMillis().get();
}
- if (DeviceConfigWrapper.get().getCustomLpaaThresholds()
- && assistStateManager.getLPNHCustomSlopMultiplier().isPresent()) {
- mTouchSlopSquared = mDeviceState.getSquaredTouchSlop(
- QUICKSTEP_TOUCH_SLOP_RATIO_TWO_BUTTON,
- assistStateManager.getLPNHCustomSlopMultiplier().get());
- }
}
if (mTaskbarDivider != null && !mActivityContext.isThreeButtonNav()) {
mTaskbarDivider.setOnLongClickListener(
@@ -727,27 +708,10 @@
private boolean onAllAppsButtonTouch(View view, MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
- if (mCurrentDownEvent != null) {
- mCurrentDownEvent.recycle();
- }
- mCurrentDownEvent = MotionEvent.obtain(ev);
mAllAppsTouchTriggered = false;
MAIN_EXECUTOR.getHandler().postDelayed(
mAllAppsTouchRunnable, mAllAppsButtonTouchDelayMs);
break;
- case MotionEvent.ACTION_MOVE:
- if (!MAIN_EXECUTOR.getHandler().hasCallbacks(mAllAppsTouchRunnable)
- || mIsTransientTaskbar) {
- break;
- }
- float dx = ev.getX() - mCurrentDownEvent.getX();
- float dy = ev.getY() - mCurrentDownEvent.getY();
- double distanceSquared = (dx * dx) + (dy * dy);
- if (distanceSquared > mTouchSlopSquared) {
- Log.d(TAG, "Touch slop out");
- cancelAllAppsButtonTouch();
- }
- break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
cancelAllAppsButtonTouch();
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
index 93814b7..23495ad 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
@@ -510,14 +510,30 @@
}
/** Updates which icons are marked as running given the Set of currently running packages. */
- public void updateIconViewsRunningStates(Set<String> runningPackages) {
+ public void updateIconViewsRunningStates(Set<String> runningPackages,
+ Set<String> minimizedPackages) {
for (View iconView : getIconViews()) {
if (iconView instanceof BubbleTextView btv) {
- btv.updateRunningState(runningPackages.contains(btv.getTargetPackageName()));
+ btv.updateRunningState(
+ getRunningAppState(btv.getTargetPackageName(), runningPackages,
+ minimizedPackages));
}
}
}
+ private BubbleTextView.RunningAppState getRunningAppState(
+ String packageName,
+ Set<String> runningPackages,
+ Set<String> minimizedPackages) {
+ if (minimizedPackages.contains(packageName)) {
+ return BubbleTextView.RunningAppState.MINIMIZED;
+ }
+ if (runningPackages.contains(packageName)) {
+ return BubbleTextView.RunningAppState.RUNNING;
+ }
+ return BubbleTextView.RunningAppState.NOT_RUNNING;
+ }
+
/**
* Defers any updates to the UI for the setup wizard animation.
*/
diff --git a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsSlideInView.java b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsSlideInView.java
index 8e05686..90ac872 100644
--- a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsSlideInView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsSlideInView.java
@@ -17,6 +17,8 @@
import static com.android.app.animation.Interpolators.EMPHASIZED;
import static com.android.launcher3.Flags.enablePredictiveBackGesture;
+import static com.android.launcher3.touch.AllAppsSwipeController.ALL_APPS_FADE_MANUAL;
+import static com.android.launcher3.touch.AllAppsSwipeController.SCRIM_FADE_MANUAL;
import android.animation.Animator;
import android.content.Context;
@@ -32,6 +34,7 @@
import androidx.annotation.Nullable;
+import com.android.app.animation.Interpolators;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Insettable;
import com.android.launcher3.R;
@@ -40,6 +43,7 @@
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.taskbar.allapps.TaskbarAllAppsViewController.TaskbarAllAppsCallbacks;
import com.android.launcher3.taskbar.overlay.TaskbarOverlayContext;
+import com.android.launcher3.util.Themes;
import com.android.launcher3.views.AbstractSlideInView;
/** Wrapper for taskbar all apps with slide-in behavior. */
@@ -113,8 +117,25 @@
@Override
protected void onOpenCloseAnimationPending(PendingAnimation animation) {
- mAllAppsCallbacks.onAllAppsAnimationPending(
- animation, mToTranslationShift == TRANSLATION_SHIFT_OPENED);
+ final boolean isOpening = mToTranslationShift == TRANSLATION_SHIFT_OPENED;
+
+ if (mActivityContext.getDeviceProfile().isPhone) {
+ final Interpolator allAppsFadeInterpolator =
+ isOpening ? ALL_APPS_FADE_MANUAL : Interpolators.reverse(ALL_APPS_FADE_MANUAL);
+ animation.setViewAlpha(mAppsView, 1 - mToTranslationShift, allAppsFadeInterpolator);
+ }
+
+ mAllAppsCallbacks.onAllAppsAnimationPending(animation, isOpening);
+ }
+
+ @Override
+ protected Interpolator getScrimInterpolator() {
+ if (mActivityContext.getDeviceProfile().isTablet) {
+ return super.getScrimInterpolator();
+ }
+ return mToTranslationShift == TRANSLATION_SHIFT_OPENED
+ ? SCRIM_FADE_MANUAL
+ : Interpolators.reverse(SCRIM_FADE_MANUAL);
}
/** The apps container inside this view. */
@@ -154,6 +175,9 @@
protected void onFinishInflate() {
super.onFinishInflate();
mAppsView = findViewById(R.id.apps_view);
+ if (mActivityContext.getDeviceProfile().isPhone) {
+ mAppsView.setAlpha(0);
+ }
mContent = mAppsView;
// Setup header protection for search bar, if enabled.
@@ -214,7 +238,9 @@
@Override
protected int getScrimColor(Context context) {
- return context.getColor(R.color.widgets_picker_scrim);
+ return mActivityContext.getDeviceProfile().isPhone
+ ? Themes.getAttrColor(context, R.attr.allAppsScrimColor)
+ : context.getColor(R.color.widgets_picker_scrim);
}
@Override
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java
index 23e52e6..7eeea84 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java
@@ -623,6 +623,8 @@
}
super.addView(child, index, params);
updateWidth();
+ updateBubbleAccessibilityStates();
+ updateContentDescription();
}
// TODO: (b/283309949) animate it
@@ -634,6 +636,8 @@
mBubbleBarBackground.showArrow(false);
}
updateWidth();
+ updateBubbleAccessibilityStates();
+ updateContentDescription();
}
private void updateWidth() {
@@ -799,6 +803,7 @@
}
}
updateChildrenRenderNodeProperties(mBubbleBarLocation);
+ updateContentDescription();
}
}
@@ -927,6 +932,7 @@
} else {
mWidthAnimator.reverse();
}
+ updateBubbleAccessibilityStates();
}
}
@@ -998,6 +1004,47 @@
return mIsAnimatingNewBubble;
}
+ private boolean hasOverview() {
+ // Overview is always the last bubble
+ View lastChild = getChildAt(getChildCount() - 1);
+ if (lastChild instanceof BubbleView bubbleView) {
+ return bubbleView.getBubble() instanceof BubbleBarOverflow;
+ }
+ return false;
+ }
+
+ private void updateBubbleAccessibilityStates() {
+ final int childA11y;
+ if (mIsBarExpanded) {
+ // Bar is expanded, focus on the bubbles
+ setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
+ childA11y = View.IMPORTANT_FOR_ACCESSIBILITY_YES;
+ } else {
+ // Bar is collapsed, only focus on the bar
+ setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
+ childA11y = View.IMPORTANT_FOR_ACCESSIBILITY_NO;
+ }
+ for (int i = 0; i < getChildCount(); i++) {
+ getChildAt(i).setImportantForAccessibility(childA11y);
+ // Only allowing focusing on bubbles when bar is expanded. Otherwise, in talkback mode,
+ // bubbles can be navigates to in collapsed mode.
+ getChildAt(i).setFocusable(mIsBarExpanded);
+ }
+ }
+
+ private void updateContentDescription() {
+ View firstChild = getChildAt(0);
+ CharSequence contentDesc = firstChild != null ? firstChild.getContentDescription() : "";
+
+ // Don't count overflow if it exists
+ int bubbleCount = getChildCount() - (hasOverview() ? 1 : 0);
+ if (bubbleCount > 1) {
+ contentDesc = getResources().getString(R.string.bubble_bar_description_multiple_bubbles,
+ contentDesc, bubbleCount - 1);
+ }
+ setContentDescription(contentDesc);
+ }
+
/** Interface for BubbleBarView to communicate with its controller. */
interface Controller {
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java
index f614dc6..45a9fa1 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java
@@ -98,7 +98,6 @@
mBarView = barView;
mSystemUiProxy = SystemUiProxy.INSTANCE.get(mActivity);
mBubbleBarAlpha = new MultiValueAlpha(mBarView, 1 /* num alpha channels */);
- mBubbleBarAlpha.setUpdateVisibility(true);
mIconSize = activity.getResources().getDimensionPixelSize(
R.dimen.bubblebar_icon_size);
}
@@ -408,7 +407,19 @@
b.getView().setOnClickListener(mBubbleClickListener);
mBubbleDragController.setupBubbleView(b.getView());
+ if (b instanceof BubbleBarOverflow) {
+ return;
+ }
+
if (suppressAnimation || !(b instanceof BubbleBarBubble bubble)) {
+ // the bubble bar and handle are initialized as part of the first bubble animation.
+ // if the animation is suppressed, immediately stash or show the bubble bar to
+ // ensure they've been initialized.
+ if (mTaskbarStashController.isInApp()) {
+ mBubbleStashController.stashBubbleBarImmediate();
+ } else {
+ mBubbleStashController.showBubbleBarImmediate();
+ }
return;
}
animateBubbleNotification(bubble, isExpanding);
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDismissController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDismissController.java
index 0e6fa3c..a6096e2 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDismissController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDismissController.java
@@ -169,7 +169,8 @@
private void setupMagnetizedObject(@NonNull View magnetizedView) {
mMagnetizedObject = new MagnetizedObject<>(mActivity.getApplicationContext(),
- magnetizedView, DynamicAnimation.TRANSLATION_X, DynamicAnimation.TRANSLATION_Y) {
+ magnetizedView, BubbleDragController.DRAG_TRANSLATION_X,
+ DynamicAnimation.TRANSLATION_Y) {
@Override
public float getWidth(@NonNull View underlyingObject) {
return underlyingObject.getWidth() * underlyingObject.getScaleX();
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDragAnimator.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDragAnimator.java
index 287e906..7aed2d2 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDragAnimator.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDragAnimator.java
@@ -60,7 +60,6 @@
private final float mBubbleFocusedScale;
private final float mBubbleCapturedScale;
private final float mDismissCapturedScale;
- private final FloatPropertyCompat<View> mTranslationXProperty;
/**
* Should be initialised for each dragged view
@@ -82,28 +81,9 @@
if (view instanceof BubbleBarView) {
mBubbleFocusedScale = SCALE_BUBBLE_BAR_FOCUSED;
mBubbleCapturedScale = mDismissCapturedScale;
- mTranslationXProperty = DynamicAnimation.TRANSLATION_X;
} else {
mBubbleFocusedScale = SCALE_BUBBLE_FOCUSED;
mBubbleCapturedScale = SCALE_BUBBLE_CAPTURED;
- // Wrap BubbleView.DRAG_TRANSLATION_X as it can't be cast to FloatPropertyCompat<View>
- mTranslationXProperty = new FloatPropertyCompat<>(
- BubbleView.DRAG_TRANSLATION_X.getName()) {
- @Override
- public float getValue(View object) {
- if (object instanceof BubbleView bubbleView) {
- return BubbleView.DRAG_TRANSLATION_X.get(bubbleView);
- }
- return 0;
- }
-
- @Override
- public void setValue(View object, float value) {
- if (object instanceof BubbleView bubbleView) {
- BubbleView.DRAG_TRANSLATION_X.setValue(bubbleView, value);
- }
- }
- };
}
}
@@ -140,7 +120,7 @@
mBubbleAnimator
.spring(DynamicAnimation.SCALE_X, 1f)
.spring(DynamicAnimation.SCALE_Y, 1f)
- .spring(mTranslationXProperty, restingPosition.x, velocity.x,
+ .spring(BubbleDragController.DRAG_TRANSLATION_X, restingPosition.x, velocity.x,
mTranslationConfig)
.spring(DynamicAnimation.TRANSLATION_Y, restingPosition.y, velocity.y,
mTranslationConfig)
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDragController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDragController.java
index 2ebc3e8..fbd1b88 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDragController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDragController.java
@@ -24,6 +24,7 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.dynamicanimation.animation.FloatPropertyCompat;
import com.android.launcher3.taskbar.TaskbarActivityContext;
import com.android.wm.shell.common.bubbles.BaseBubblePinController.LocationChangeListener;
@@ -38,6 +39,37 @@
* Restores initial position of dragged view if released outside of the dismiss target.
*/
public class BubbleDragController {
+
+ /**
+ * Property to update dragged bubble x-translation value.
+ * <p>
+ * When applied to {@link BubbleView}, will use set the translation through
+ * {@link BubbleView#getDragTranslationX()} and {@link BubbleView#setDragTranslationX(float)}
+ * methods.
+ * <p>
+ * When applied to {@link BubbleBarView}, will use {@link View#getTranslationX()} and
+ * {@link View#setTranslationX(float)}.
+ */
+ public static final FloatPropertyCompat<View> DRAG_TRANSLATION_X = new FloatPropertyCompat<>(
+ "dragTranslationX") {
+ @Override
+ public float getValue(View view) {
+ if (view instanceof BubbleView bubbleView) {
+ return bubbleView.getDragTranslationX();
+ }
+ return view.getTranslationX();
+ }
+
+ @Override
+ public void setValue(View view, float value) {
+ if (view instanceof BubbleView bubbleView) {
+ bubbleView.setDragTranslationX(value);
+ } else {
+ view.setTranslationX(value);
+ }
+ }
+ };
+
private final TaskbarActivityContext mActivity;
private BubbleBarController mBubbleBarController;
private BubbleBarViewController mBubbleBarViewController;
@@ -62,8 +94,10 @@
mBubblePinController = bubbleControllers.bubblePinController;
mBubbleDismissController.setListener(
stuck -> {
- mBubbleBarPinController.setDropTargetHidden(stuck);
- mBubblePinController.setDropTargetHidden(stuck);
+ if (stuck) {
+ mBubbleBarPinController.onStuckToDismissTarget();
+ mBubblePinController.onStuckToDismissTarget();
+ }
});
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashController.java
index 5d01b9b..74ddf90 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashController.java
@@ -418,6 +418,7 @@
/** Stashes the bubble bar immediately without animation. */
public void stashBubbleBarImmediate() {
mHandleViewController.setTranslationYForSwipe(0);
+ mBubbleStashedHandleAlpha.setValue(1);
mIconAlphaForStash.setValue(0);
mIconTranslationYForStash.updateValue(getStashTranslation());
mIconScaleForStash.updateValue(STASHED_BAR_SCALE);
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleView.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleView.java
index 61a6bce..2f92fbb 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleView.java
@@ -21,14 +21,13 @@
import android.graphics.Canvas;
import android.graphics.Outline;
import android.graphics.Rect;
+import android.text.TextUtils;
import android.util.AttributeSet;
-import android.util.FloatProperty;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewOutlineProvider;
import android.widget.ImageView;
-import androidx.annotation.NonNull;
import androidx.constraintlayout.widget.ConstraintLayout;
import com.android.launcher3.R;
@@ -49,25 +48,6 @@
public static final int DEFAULT_PATH_SIZE = 100;
/**
- * Property to update drag translation value.
- *
- * @see BubbleView#getDragTranslationX()
- * @see BubbleView#setDragTranslationX(float)
- */
- public static final FloatProperty<BubbleView> DRAG_TRANSLATION_X = new FloatProperty<>(
- "dragTranslationX") {
- @Override
- public void setValue(@NonNull BubbleView bubbleView, float value) {
- bubbleView.setDragTranslationX(value);
- }
-
- @Override
- public Float get(BubbleView bubbleView) {
- return bubbleView.getDragTranslationX();
- }
- };
-
- /**
* Flags that suppress the visibility of the 'new' dot or the app badge, for one reason or
* another. If any of these flags are set, the dot will not be shown.
* If {@link SuppressionFlag#BEHIND_STACK} then the app badge will not be shown.
@@ -208,6 +188,16 @@
mAppIcon.setImageBitmap(bubble.getBadge());
mDotColor = bubble.getDotColor();
mDotRenderer = new DotRenderer(mBubbleSize, bubble.getDotPath(), DEFAULT_PATH_SIZE);
+ String contentDesc = bubble.getInfo().getTitle();
+ if (TextUtils.isEmpty(contentDesc)) {
+ contentDesc = getResources().getString(R.string.bubble_bar_bubble_fallback_description);
+ }
+ String appName = bubble.getInfo().getAppName();
+ if (!TextUtils.isEmpty(appName)) {
+ contentDesc = getResources().getString(R.string.bubble_bar_bubble_description,
+ contentDesc, appName);
+ }
+ setContentDescription(contentDesc);
}
/**
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
index c33e4cc..e3a2bab 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
@@ -542,18 +542,16 @@
if (mDesktopVisibilityController != null) {
mDesktopVisibilityController.unregisterSystemUiListener();
}
- mDesktopVisibilityController = null;
if (mSplitSelectStateController != null) {
- removeBackAnimationCallback(mSplitSelectStateController.getSplitBackHandler());
mSplitSelectStateController.onDestroy();
}
- mSplitSelectStateController = null;
super.onDestroy();
mHotseatPredictionController.destroy();
mSplitWithKeyboardShortcutController.onDestroy();
if (mViewCapture != null) mViewCapture.close();
+ removeBackAnimationCallback(mSplitSelectStateController.getSplitBackHandler());
}
@Override
@@ -758,6 +756,9 @@
@Override
public boolean isSplitSelectionActive() {
+ if (mSplitSelectStateController == null) {
+ return false;
+ }
return mSplitSelectStateController.isSplitSelectActive();
}
@@ -1275,7 +1276,7 @@
if ((flags & CHANGE_NAVIGATION_MODE) != 0) {
getDragLayer().recreateControllers();
if (mActionsView != null) {
- mActionsView.updateVerticalMargin(info.navigationMode);
+ mActionsView.updateVerticalMargin(info.getNavigationMode());
}
}
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java b/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java
index 2625919..fa80dc2 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java
@@ -102,6 +102,11 @@
}
@Override
+ public int getTitle() {
+ return R.string.all_apps_label;
+ }
+
+ @Override
public float getVerticalProgress(Launcher launcher) {
return 0f;
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java b/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java
index 7173298..6822f1b 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java
@@ -182,6 +182,11 @@
return launcher.getString(R.string.accessibility_recent_apps);
}
+ @Override
+ public int getTitle() {
+ return R.string.accessibility_recent_apps;
+ }
+
public static float getDefaultSwipeHeight(Launcher launcher) {
return LayoutUtils.getDefaultSwipeHeight(launcher, launcher.getDeviceProfile());
}
diff --git a/quickstep/src/com/android/quickstep/DesktopSystemShortcut.kt b/quickstep/src/com/android/quickstep/DesktopSystemShortcut.kt
index 50a06fc..9c188f3 100644
--- a/quickstep/src/com/android/quickstep/DesktopSystemShortcut.kt
+++ b/quickstep/src/com/android/quickstep/DesktopSystemShortcut.kt
@@ -24,6 +24,7 @@
import com.android.quickstep.views.RecentsView
import com.android.quickstep.views.RecentsViewContainer
import com.android.quickstep.views.TaskView.TaskContainer
+import com.android.wm.shell.common.desktopmode.DesktopModeTransitionSource
import com.android.wm.shell.shared.DesktopModeStatus
/** A menu item, "Desktop", that allows the user to bring the current app into Desktop Windowing. */
@@ -43,7 +44,10 @@
override fun onClick(view: View) {
dismissTaskMenuView()
val recentsView = mTarget.getOverviewPanel<RecentsView<*, *>>()
- recentsView.moveTaskToDesktop(taskContainer) {
+ recentsView.moveTaskToDesktop(
+ taskContainer,
+ DesktopModeTransitionSource.APP_FROM_OVERVIEW
+ ) {
mTarget.statsLogManager
.logger()
.withItemInfo(taskContainer.itemInfo)
diff --git a/quickstep/src/com/android/quickstep/LauncherActivityInterface.java b/quickstep/src/com/android/quickstep/LauncherActivityInterface.java
index 811b9fd..08c2e1c 100644
--- a/quickstep/src/com/android/quickstep/LauncherActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/LauncherActivityInterface.java
@@ -308,7 +308,8 @@
return;
}
LauncherOverlayManager om = launcher.getOverlayManager();
- if (!launcher.isStarted() || launcher.isForceInvisible()) {
+ if (!SystemUiProxy.INSTANCE.get(launcher).getHomeVisibilityState().isHomeVisible()
+ || launcher.isForceInvisible()) {
om.hideOverlay(false /* animate */);
} else {
om.hideOverlay(150);
diff --git a/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java b/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java
index 080e03a..3c66590 100644
--- a/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java
+++ b/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java
@@ -173,14 +173,10 @@
}
@Override
- public void playAtomicAnimation(float velocity) {
- if (enableScalingRevealHomeAnimation()) {
- if (mContainer != null) {
- new ScalingWorkspaceRevealAnim(
- mContainer, mSiblingAnimation, getWindowTargetRect()).start();
- }
- } else {
- super.playAtomicAnimation(velocity);
+ protected void playScalingRevealAnimation() {
+ if (mContainer != null) {
+ new ScalingWorkspaceRevealAnim(mContainer, mSiblingAnimation,
+ getWindowTargetRect()).start();
}
}
@@ -370,9 +366,25 @@
@Override
public void playAtomicAnimation(float velocity) {
- new StaggeredWorkspaceAnim(mContainer, velocity, true /* animateOverviewScrim */,
- getViewIgnoredInWorkspaceRevealAnimation())
- .start();
+ if (enableScalingRevealHomeAnimation()) {
+ playScalingRevealAnimation();
+ } else {
+ new StaggeredWorkspaceAnim(mContainer, velocity, true /* animateOverviewScrim */,
+ getViewIgnoredInWorkspaceRevealAnimation())
+ .start();
+ }
+ }
+
+ /**
+ * Extracted in a different method so subclasses that have a custom window animation with a
+ * target (icons, widgets) can pass the optional parameters.
+ */
+ protected void playScalingRevealAnimation() {
+ if (mContainer != null) {
+ new ScalingWorkspaceRevealAnim(
+ mContainer, null /* siblingAnimation */,
+ null /* windowTargetRect */).start();
+ }
}
}
}
diff --git a/quickstep/src/com/android/quickstep/OverviewCommandHelper.java b/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
index 3091e3d..26a7c2f 100644
--- a/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
+++ b/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
@@ -28,9 +28,11 @@
import android.graphics.PointF;
import android.os.SystemClock;
import android.os.Trace;
+import android.util.Log;
import android.view.View;
import androidx.annotation.BinderThread;
+import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.UiThread;
@@ -58,6 +60,7 @@
* Helper class to handle various atomic commands for switching between Overview.
*/
public class OverviewCommandHelper {
+ private static final String TAG = "OverviewCommandHelper";
public static final int TYPE_SHOW = 1;
public static final int TYPE_KEYBOARD_INPUT = 2;
@@ -105,10 +108,19 @@
* Called when the command finishes execution.
*/
private void scheduleNextTask(CommandInfo command) {
- if (!mPendingCommands.isEmpty() && mPendingCommands.get(0) == command) {
- mPendingCommands.remove(0);
- executeNext();
+ if (mPendingCommands.isEmpty()) {
+ Log.d(TAG, "no pending commands to schedule");
+ return;
}
+ if (mPendingCommands.get(0) != command) {
+ Log.d(TAG, "next task not scheduled."
+ + " mPendingCommands[0] type is " + mPendingCommands.get(0)
+ + " - command type is: " + command);
+ return;
+ }
+ Log.d(TAG, "scheduleNextTask called: " + command);
+ mPendingCommands.remove(0);
+ executeNext();
}
/**
@@ -119,10 +131,14 @@
@UiThread
private void executeNext() {
if (mPendingCommands.isEmpty()) {
+ Log.d(TAG, "executeNext - mPendingCommands is empty");
return;
}
CommandInfo cmd = mPendingCommands.get(0);
- if (executeCommand(cmd)) {
+
+ boolean result = executeCommand(cmd);
+ Log.d(TAG, "executeNext cmd type: " + cmd + ", result: " + result);
+ if (result) {
scheduleNextTask(cmd);
}
}
@@ -144,14 +160,18 @@
@BinderThread
public void addCommand(int type) {
if (mPendingCommands.size() >= MAX_QUEUE_SIZE) {
+ Log.d(TAG, "the pending command queue is full (" + mPendingCommands.size() + "). "
+ + "command not added: " + type);
return;
}
+ Log.d(TAG, "adding command type: " + type);
CommandInfo cmd = new CommandInfo(type);
MAIN_EXECUTOR.execute(() -> addCommand(cmd));
}
@UiThread
public void clearPendingCommands() {
+ Log.d(TAG, "clearing pending commands - size: " + mPendingCommands.size());
mPendingCommands.clear();
}
@@ -182,9 +202,11 @@
if (callbackList != null) {
callbackList.add(() -> {
+ Log.d(TAG, "launching task callback: " + cmd);
scheduleNextTask(cmd);
mWaitForToggleCommandComplete = false;
});
+ Log.d(TAG, "launching task - waiting for callback: " + cmd);
return false;
} else {
recents.startHome();
@@ -200,12 +222,17 @@
private <T extends StatefulActivity<?> & RecentsViewContainer> boolean executeCommand(
CommandInfo cmd) {
if (mWaitForToggleCommandComplete && cmd.type == TYPE_TOGGLE) {
+ Log.d(TAG, "executeCommand: " + cmd
+ + " - waiting for toggle command complete");
return true;
}
BaseActivityInterface<?, T> activityInterface =
mOverviewComponentObserver.getActivityInterface();
RecentsView visibleRecentsView = activityInterface.getVisibleRecentsView();
RecentsView createdRecentsView;
+
+ Log.d(TAG, "executeCommand: " + cmd
+ + " - visibleRecentsView: " + visibleRecentsView);
if (visibleRecentsView == null) {
T activity = activityInterface.getCreatedContainer();
createdRecentsView = activity == null ? null : activity.getOverviewPanel();
@@ -292,14 +319,17 @@
updateRecentsViewFocus(cmd);
logShowOverviewFrom(cmd.type);
}
+
@Override
public void onAnimationEnd(Animator animation) {
+ Log.d(TAG, "switching to Overview state - onAnimationEnd: " + cmd);
super.onAnimationEnd(animation);
onRecentsViewFocusUpdated(cmd);
scheduleNextTask(cmd);
}
};
if (activityInterface.switchToRecentsIfVisible(animatorListener)) {
+ Log.d(TAG, "switching to Overview state - waiting: " + cmd);
// If successfully switched, wait until animation finishes
return false;
}
@@ -366,10 +396,12 @@
cmd.mActiveCallbacks.addListener(recentAnimListener);
}
Trace.beginAsyncSection(TRANSITION_NAME, 0);
+ Log.d(TAG, "switching via recents animation - onGestureStarted: " + cmd);
return false;
}
private void onTransitionComplete(CommandInfo cmd, AbsSwipeUpHandler handler) {
+ Log.d(TAG, "switching via recents animation - onTransitionComplete: " + cmd);
cmd.removeListener(handler);
Trace.endAsyncSection(TRANSITION_NAME, 0);
onRecentsViewFocusUpdated(cmd);
@@ -478,5 +510,15 @@
mActiveCallbacks.removeListener(listener);
}
}
+
+ @NonNull
+ @Override
+ public String toString() {
+ return "CommandInfo("
+ + "type=" + type + ", "
+ + "createTime=" + createTime + ", "
+ + "mActiveCallbacks=" + mActiveCallbacks
+ + ")";
+ }
}
}
diff --git a/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java b/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java
index dfbae65..b4b8c5b 100644
--- a/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java
+++ b/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java
@@ -198,6 +198,12 @@
.unstashBubbleBarIfStashed();
});
return response;
+ case TestProtocol.REQUEST_INJECT_FAKE_TRACKPAD:
+ runOnTISBinder(tisBinder -> tisBinder.injectFakeTrackpadForTesting());
+ return response;
+ case TestProtocol.REQUEST_EJECT_FAKE_TRACKPAD:
+ runOnTISBinder(tisBinder -> tisBinder.ejectFakeTrackpadForTesting());
+ return response;
}
return super.call(method, arg, extras);
diff --git a/quickstep/src/com/android/quickstep/RecentTasksList.java b/quickstep/src/com/android/quickstep/RecentTasksList.java
index 711882c..37b4dca 100644
--- a/quickstep/src/com/android/quickstep/RecentTasksList.java
+++ b/quickstep/src/com/android/quickstep/RecentTasksList.java
@@ -31,6 +31,7 @@
import android.os.RemoteException;
import android.util.SparseBooleanArray;
+import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import com.android.launcher3.util.LooperExecutor;
@@ -44,6 +45,7 @@
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collections;
+import java.util.List;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.stream.Collectors;
@@ -137,7 +139,7 @@
* @return The change id of the current task list
*/
public synchronized int getTasks(boolean loadKeysOnly,
- Consumer<ArrayList<GroupTask>> callback, Predicate<GroupTask> filter) {
+ @Nullable Consumer<List<GroupTask>> callback, Predicate<GroupTask> filter) {
final int requestLoadId = mChangeId;
if (mResultsUi.isValidForRequest(requestLoadId, loadKeysOnly)) {
// The list is up to date, send the callback on the next frame,
diff --git a/quickstep/src/com/android/quickstep/RecentsActivity.java b/quickstep/src/com/android/quickstep/RecentsActivity.java
index 97a0b3f..3df62da 100644
--- a/quickstep/src/com/android/quickstep/RecentsActivity.java
+++ b/quickstep/src/com/android/quickstep/RecentsActivity.java
@@ -371,6 +371,9 @@
getSystemUiController().updateUiState(SystemUiController.UI_STATE_BASE_WINDOW,
Themes.getAttrBoolean(this, R.attr.isWorkspaceDarkText));
ACTIVITY_TRACKER.handleCreate(this);
+
+ // Set screen title for Talkback
+ setTitle(R.string.accessibility_recent_apps);
}
@Override
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
index 34b50ca..8bcdaa3 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
@@ -91,7 +91,7 @@
static final String SUPPORT_ONE_HANDED_MODE = "ro.support_one_handed_mode";
// TODO: Move to quickstep contract
- public static final float QUICKSTEP_TOUCH_SLOP_RATIO_TWO_BUTTON = 3f;
+ private static final float QUICKSTEP_TOUCH_SLOP_RATIO_TWO_BUTTON = 3f;
private static final float QUICKSTEP_TOUCH_SLOP_RATIO_GESTURAL = 1.414f;
private final Context mContext;
@@ -610,16 +610,6 @@
return touchSlop * touchSlop;
}
- /**
- * Returns the squared touch slop using the given base slop multiplier {@code slopMultiplier}
- * and custom slop multiplier {@code customSlopMultiplier}.
- */
- public float getSquaredTouchSlop(float slopMultiplier, float customSlopMultiplier) {
- float systemTouchSlop = ViewConfiguration.get(mContext).getScaledTouchSlop();
- float touchSlop = customSlopMultiplier * slopMultiplier * systemTouchSlop;
- return touchSlop * touchSlop;
- }
-
public String getSystemUiStateString() {
return QuickStepContract.getSystemUiStateString(mSystemUiStateFlags);
}
diff --git a/quickstep/src/com/android/quickstep/RecentsModel.java b/quickstep/src/com/android/quickstep/RecentsModel.java
index 89351aa..98c1eb4 100644
--- a/quickstep/src/com/android/quickstep/RecentsModel.java
+++ b/quickstep/src/com/android/quickstep/RecentsModel.java
@@ -33,6 +33,7 @@
import android.os.Process;
import android.os.UserHandle;
+import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import com.android.launcher3.icons.IconProvider;
@@ -40,6 +41,7 @@
import com.android.launcher3.util.Executors.SimpleThreadFactory;
import com.android.launcher3.util.MainThreadInitializedObject;
import com.android.launcher3.util.SafeCloseable;
+import com.android.quickstep.recents.data.RecentTasksDataSource;
import com.android.quickstep.util.GroupTask;
import com.android.quickstep.util.TaskVisualsChangeListener;
import com.android.systemui.shared.recents.model.Task;
@@ -60,8 +62,8 @@
* Singleton class to load and manage recents model.
*/
@TargetApi(Build.VERSION_CODES.O)
-public class RecentsModel implements IconChangeListener, TaskStackChangeListener,
- TaskVisualsChangeListener, SafeCloseable {
+public class RecentsModel implements RecentTasksDataSource, IconChangeListener,
+ TaskStackChangeListener, TaskVisualsChangeListener, SafeCloseable {
// We do not need any synchronization for this variable as its only written on UI thread.
public static final MainThreadInitializedObject<RecentsModel> INSTANCE =
@@ -141,7 +143,8 @@
* always called on the UI thread.
* @return the request id associated with this call.
*/
- public int getTasks(Consumer<ArrayList<GroupTask>> callback) {
+ @Override
+ public int getTasks(@Nullable Consumer<List<GroupTask>> callback) {
return mTaskList.getTasks(false /* loadKeysOnly */, callback,
RecentsFilterState.DEFAULT_FILTER);
}
@@ -155,7 +158,7 @@
* callback.
* @return the request id associated with this call.
*/
- public int getTasks(Consumer<ArrayList<GroupTask>> callback, Predicate<GroupTask> filter) {
+ public int getTasks(@Nullable Consumer<List<GroupTask>> callback, Predicate<GroupTask> filter) {
return mTaskList.getTasks(false /* loadKeysOnly */, callback, filter);
}
diff --git a/quickstep/src/com/android/quickstep/RotationTouchHelper.java b/quickstep/src/com/android/quickstep/RotationTouchHelper.java
index 3380291..6f1ab7d 100644
--- a/quickstep/src/com/android/quickstep/RotationTouchHelper.java
+++ b/quickstep/src/com/android/quickstep/RotationTouchHelper.java
@@ -157,7 +157,7 @@
// Register for navigation mode changes
mDisplayController.addChangeListener(this);
DisplayController.Info info = mDisplayController.getInfo();
- onDisplayInfoChangedInternal(info, CHANGE_ALL, info.navigationMode.hasGestures);
+ onDisplayInfoChangedInternal(info, CHANGE_ALL, info.getNavigationMode().hasGestures);
runOnDestroy(() -> mDisplayController.removeChangeListener(this));
mOrientationListener = new OrientationEventListener(mContext) {
@@ -291,7 +291,7 @@
}
if ((flags & CHANGE_NAVIGATION_MODE) != 0) {
- NavigationMode newMode = info.navigationMode;
+ NavigationMode newMode = info.getNavigationMode();
mOrientationTouchTransformer.setNavigationMode(newMode, mDisplayController.getInfo(),
mContext.getResources());
diff --git a/quickstep/src/com/android/quickstep/SystemUiProxy.java b/quickstep/src/com/android/quickstep/SystemUiProxy.java
index 0ac3ec7..9c3fbaa 100644
--- a/quickstep/src/com/android/quickstep/SystemUiProxy.java
+++ b/quickstep/src/com/android/quickstep/SystemUiProxy.java
@@ -85,6 +85,7 @@
import com.android.wm.shell.bubbles.IBubbles;
import com.android.wm.shell.bubbles.IBubblesListener;
import com.android.wm.shell.common.bubbles.BubbleBarLocation;
+import com.android.wm.shell.common.desktopmode.DesktopModeTransitionSource;
import com.android.wm.shell.common.pip.IPip;
import com.android.wm.shell.common.pip.IPipAnimationListener;
import com.android.wm.shell.common.split.SplitScreenConstants.PersistentSnapPosition;
@@ -1494,10 +1495,10 @@
}
/** Call shell to move a task with given `taskId` to desktop */
- public void moveToDesktop(int taskId) {
+ public void moveToDesktop(int taskId, DesktopModeTransitionSource transitionSource) {
if (mDesktopMode != null) {
try {
- mDesktopMode.moveToDesktop(taskId);
+ mDesktopMode.moveToDesktop(taskId, transitionSource);
} catch (RemoteException e) {
Log.w(TAG, "Failed call moveToDesktop", e);
}
diff --git a/quickstep/src/com/android/quickstep/TaskShortcutFactory.java b/quickstep/src/com/android/quickstep/TaskShortcutFactory.java
index 4b5c826..fd141c3 100644
--- a/quickstep/src/com/android/quickstep/TaskShortcutFactory.java
+++ b/quickstep/src/com/android/quickstep/TaskShortcutFactory.java
@@ -21,6 +21,7 @@
import static android.view.Surface.ROTATION_0;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SYSTEM_SHORTCUT_FREE_FORM_TAP;
+import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT;
import static com.android.window.flags.Flags.enableDesktopWindowingMode;
import android.app.ActivityOptions;
@@ -107,10 +108,14 @@
public List<SystemShortcut> getShortcuts(RecentsViewContainer container,
TaskContainer taskContainer) {
TaskView taskView = taskContainer.getTaskView();
+ int actionId = taskContainer.getStagePosition() == STAGE_POSITION_BOTTOM_OR_RIGHT
+ ? R.id.action_app_info_bottom_right
+ : R.id.action_app_info_top_left;
+
AppInfo.SplitAccessibilityInfo accessibilityInfo =
new AppInfo.SplitAccessibilityInfo(taskView.containsMultipleTasks(),
TaskUtils.getTitle(taskView.getContext(), taskContainer.getTask()),
- taskContainer.getA11yNodeId()
+ actionId
);
return Collections.singletonList(new AppInfo(container, taskContainer.getItemInfo(),
taskView, accessibilityInfo));
diff --git a/quickstep/src/com/android/quickstep/TaskThumbnailCache.java b/quickstep/src/com/android/quickstep/TaskThumbnailCache.java
index 7ebb767..38e927f 100644
--- a/quickstep/src/com/android/quickstep/TaskThumbnailCache.java
+++ b/quickstep/src/com/android/quickstep/TaskThumbnailCache.java
@@ -21,11 +21,13 @@
import android.content.Context;
import android.content.res.Resources;
+import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
import com.android.launcher3.R;
import com.android.launcher3.util.CancellableTask;
import com.android.launcher3.util.Preconditions;
+import com.android.quickstep.task.thumbnail.data.TaskThumbnailDataSource;
import com.android.quickstep.util.TaskKeyByLastActiveTimeCache;
import com.android.quickstep.util.TaskKeyCache;
import com.android.quickstep.util.TaskKeyLruCache;
@@ -38,7 +40,7 @@
import java.util.concurrent.Executor;
import java.util.function.Consumer;
-public class TaskThumbnailCache {
+public class TaskThumbnailCache implements TaskThumbnailDataSource {
private final Executor mBgExecutor;
private final TaskKeyCache<ThumbnailData> mCache;
@@ -148,8 +150,9 @@
* @param callback The callback to receive the task after its data has been populated.
* @return A cancelable handle to the request
*/
+ @Override
public CancellableTask<ThumbnailData> updateThumbnailInBackground(
- Task task, Consumer<ThumbnailData> callback) {
+ Task task, @NonNull Consumer<ThumbnailData> callback) {
Preconditions.assertUIThread();
boolean lowResolution = !mHighResLoadingState.isEnabled();
diff --git a/quickstep/src/com/android/quickstep/TopTaskTracker.java b/quickstep/src/com/android/quickstep/TopTaskTracker.java
index 3a6b804..29c80fd 100644
--- a/quickstep/src/com/android/quickstep/TopTaskTracker.java
+++ b/quickstep/src/com/android/quickstep/TopTaskTracker.java
@@ -28,6 +28,7 @@
import android.annotation.UserIdInt;
import android.app.ActivityManager.RunningTaskInfo;
import android.content.Context;
+import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -47,6 +48,7 @@
import com.android.systemui.shared.system.TaskStackChangeListeners;
import com.android.wm.shell.splitscreen.ISplitScreenListener;
+import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -61,6 +63,10 @@
public class TopTaskTracker extends ISplitScreenListener.Stub
implements TaskStackChangeListener, SafeCloseable {
+ private static final String TAG = "TopTaskTracker";
+
+ private static final boolean DEBUG = true;
+
public static MainThreadInitializedObject<TopTaskTracker> INSTANCE =
new MainThreadInitializedObject<>(TopTaskTracker::new);
@@ -92,10 +98,19 @@
@Override
public void onTaskRemoved(int taskId) {
mOrderedTaskList.removeIf(rto -> rto.taskId == taskId);
+ if (DEBUG) {
+ Log.i(TAG, "onTaskRemoved: taskId=" + taskId);
+ }
}
@Override
public void onTaskMovedToFront(RunningTaskInfo taskInfo) {
+ if (!mOrderedTaskList.isEmpty()
+ && mOrderedTaskList.getFirst().taskId != taskInfo.taskId
+ && DEBUG) {
+ Log.i(TAG, "onTaskMovedToFront: (moved taskInfo to front) taskId=" + taskInfo.taskId
+ + ", baseIntent=" + taskInfo.baseIntent);
+ }
mOrderedTaskList.removeIf(rto -> rto.taskId == taskInfo.taskId);
mOrderedTaskList.addFirst(taskInfo);
@@ -106,6 +121,11 @@
final RunningTaskInfo topTaskOnHomeDisplay = mOrderedTaskList.stream()
.filter(rto -> rto.displayId == DEFAULT_DISPLAY).findFirst().orElse(null);
if (topTaskOnHomeDisplay != null) {
+ if (DEBUG) {
+ Log.i(TAG, "onTaskMovedToFront: (removing top task on home display) taskId="
+ + topTaskOnHomeDisplay.taskId
+ + ", baseIntent=" + topTaskOnHomeDisplay.baseIntent);
+ }
mOrderedTaskList.removeIf(rto -> rto.taskId == topTaskOnHomeDisplay.taskId);
mOrderedTaskList.addFirst(topTaskOnHomeDisplay);
}
@@ -119,6 +139,10 @@
if (info.taskId != taskInfo.taskId
&& info.taskId != mMainStagePosition.taskId
&& info.taskId != mSideStagePosition.taskId) {
+ if (DEBUG) {
+ Log.i(TAG, "onTaskMovedToFront: (removing task list overflow) taskId="
+ + taskInfo.taskId + ", baseIntent=" + taskInfo.baseIntent);
+ }
itr.remove();
return;
}
@@ -128,6 +152,9 @@
@Override
public void onStagePositionChanged(@StageType int stage, @StagePosition int position) {
+ if (DEBUG) {
+ Log.i(TAG, "onStagePositionChanged: stage=" + stage + ", position=" + position);
+ }
if (stage == SplitConfigurationOptions.STAGE_TYPE_MAIN) {
mMainStagePosition.stagePosition = position;
} else {
@@ -137,6 +164,10 @@
@Override
public void onTaskStageChanged(int taskId, @StageType int stage, boolean visible) {
+ if (DEBUG) {
+ Log.i(TAG, "onTaskStageChanged: taskId=" + taskId
+ + ", stage=" + stage + ", visible=" + visible);
+ }
// If a task is not visible anymore or has been moved to undefined, stop tracking it.
if (!visible || stage == SplitConfigurationOptions.STAGE_TYPE_UNDEFINED) {
if (mMainStagePosition.taskId == taskId) {
@@ -156,11 +187,18 @@
@Override
public void onActivityPinned(String packageName, int userId, int taskId, int stackId) {
+ if (DEBUG) {
+ Log.i(TAG, "onActivityPinned: packageName=" + packageName
+ + ", userId=" + userId + ", stackId=" + stackId);
+ }
mPinnedTaskId = taskId;
}
@Override
public void onActivityUnpinned() {
+ if (DEBUG) {
+ Log.i(TAG, "onActivityUnpinned");
+ }
mPinnedTaskId = INVALID_TASK_ID;
}
@@ -212,6 +250,21 @@
return new CachedTaskInfo(tasks);
}
+ public void dump(String prefix, PrintWriter writer) {
+ writer.println(prefix + "TopTaskTracker:");
+
+ writer.println(prefix + "\tmOrderedTaskList=[");
+ for (RunningTaskInfo taskInfo : mOrderedTaskList) {
+ writer.println(prefix + "\t\t(taskId=" + taskInfo.taskId
+ + "; baseIntent=" + taskInfo.baseIntent
+ + "; isRunning=" + taskInfo.isRunning + ")");
+ }
+ writer.println(prefix + "\t]");
+ writer.println(prefix + "\tmMainStagePosition=" + mMainStagePosition);
+ writer.println(prefix + "\tmSideStagePosition=" + mSideStagePosition);
+ writer.println(prefix + "\tmPinnedTaskId=" + mPinnedTaskId);
+ }
+
/**
* Class to provide information about a task which can be safely cached and do not change
* during the lifecycle of the task.
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index 4599f18..7aa99d9 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -68,11 +68,13 @@
import android.content.Intent;
import android.content.res.Configuration;
import android.graphics.Region;
+import android.hardware.input.InputManager;
import android.os.Bundle;
import android.os.IBinder;
import android.os.Looper;
import android.os.SystemClock;
import android.os.Trace;
+import android.util.ArraySet;
import android.util.Log;
import android.view.Choreographer;
import android.view.InputDevice;
@@ -83,6 +85,7 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.UiThread;
+import androidx.annotation.VisibleForTesting;
import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.ConstantItem;
@@ -146,6 +149,7 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.lang.ref.WeakReference;
+import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Function;
@@ -396,6 +400,25 @@
return tis.mTaskbarManager;
}
+ @VisibleForTesting
+ public void injectFakeTrackpadForTesting() {
+ TouchInteractionService tis = mTis.get();
+ if (tis == null) return;
+ tis.mTrackpadsConnected.add(1000);
+ tis.initInputMonitor("tapl testing");
+ }
+
+ @VisibleForTesting
+ public void ejectFakeTrackpadForTesting() {
+ TouchInteractionService tis = mTis.get();
+ if (tis == null) return;
+ tis.mTrackpadsConnected.clear();
+ // This method destroys the current input monitor if set up, and only init a new one
+ // in 3-button mode if {@code mTrackpadsConnected} is not empty. So in other words,
+ // it will destroy the input monitor.
+ tis.initInputMonitor("tapl testing");
+ }
+
/**
* Sets whether a predictive back-to-home animation is in progress in the device state
*/
@@ -453,6 +476,47 @@
}
}
+ private final InputManager.InputDeviceListener mInputDeviceListener =
+ new InputManager.InputDeviceListener() {
+ @Override
+ public void onInputDeviceAdded(int deviceId) {
+ if (isTrackpadDevice(deviceId)) {
+ boolean wasEmpty = mTrackpadsConnected.isEmpty();
+ mTrackpadsConnected.add(deviceId);
+ if (wasEmpty) {
+ update();
+ }
+ }
+ }
+
+ @Override
+ public void onInputDeviceChanged(int deviceId) {
+ }
+
+ @Override
+ public void onInputDeviceRemoved(int deviceId) {
+ mTrackpadsConnected.remove(deviceId);
+ if (mTrackpadsConnected.isEmpty()) {
+ update();
+ }
+ }
+
+ private void update() {
+ if (mInputMonitorCompat != null && !mTrackpadsConnected.isEmpty()) {
+ // Don't destroy and reinitialize input monitor due to trackpad
+ // connecting when it's already set up.
+ return;
+ }
+ initInputMonitor("onTrackpadConnected()");
+ }
+
+ private boolean isTrackpadDevice(int deviceId) {
+ InputDevice inputDevice = mInputManager.getInputDevice(deviceId);
+ return inputDevice.getSources() == (InputDevice.SOURCE_MOUSE
+ | InputDevice.SOURCE_TOUCHPAD);
+ }
+ };
+
private static boolean sConnected = false;
private static boolean sIsInitialized = false;
private RotationTouchHelper mRotationTouchHelper;
@@ -503,6 +567,8 @@
private TaskbarManager mTaskbarManager;
private Function<GestureState, AnimatedFloat> mSwipeUpProxyProvider = i -> null;
private AllAppsActionManager mAllAppsActionManager;
+ private InputManager mInputManager;
+ private final Set<Integer> mTrackpadsConnected = new ArraySet<>();
@Override
public void onCreate() {
@@ -514,6 +580,15 @@
mDeviceState = new RecentsAnimationDeviceState(this, true);
mAllAppsActionManager = new AllAppsActionManager(
this, UI_HELPER_EXECUTOR, this::createAllAppsPendingIntent);
+ mInputManager = getSystemService(InputManager.class);
+ if (ENABLE_TRACKPAD_GESTURE.get()) {
+ mInputManager.registerInputDeviceListener(mInputDeviceListener,
+ UI_HELPER_EXECUTOR.getHandler());
+ int [] inputDevices = mInputManager.getInputDeviceIds();
+ for (int inputDeviceId : inputDevices) {
+ mInputDeviceListener.onInputDeviceAdded(inputDeviceId);
+ }
+ }
mTaskbarManager = new TaskbarManager(this, mAllAppsActionManager, mNavCallbacks);
mRotationTouchHelper = mDeviceState.getRotationTouchHelper();
mInputConsumer = InputConsumerController.getRecentsAnimationInputConsumer();
@@ -542,7 +617,8 @@
private void initInputMonitor(String reason) {
disposeEventHandlers("Initializing input monitor due to: " + reason);
- if (mDeviceState.isButtonNavMode() && !ENABLE_TRACKPAD_GESTURE.get()) {
+ if (mDeviceState.isButtonNavMode() && (!ENABLE_TRACKPAD_GESTURE.get()
+ || mTrackpadsConnected.isEmpty())) {
return;
}
@@ -678,6 +754,9 @@
mAllAppsActionManager.onDestroy();
+ mInputManager.unregisterInputDeviceListener(mInputDeviceListener);
+ mTrackpadsConnected.clear();
+
mTaskbarManager.destroy();
sConnected = false;
@@ -1467,6 +1546,7 @@
pw.println("\tmConsumer=" + mConsumer.getName());
ActiveGestureLog.INSTANCE.dump("", pw);
RecentsModel.INSTANCE.get(this).dump("", pw);
+ TopTaskTracker.INSTANCE.get(this).dump("", pw);
if (mTaskAnimationManager != null) {
mTaskAnimationManager.dump("", pw);
}
diff --git a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
index 096ed2c..485d6c4 100644
--- a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
+++ b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
@@ -54,6 +54,7 @@
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.List;
public class FallbackRecentsView extends RecentsView<RecentsActivity, RecentsState>
implements StateListener<RecentsState> {
@@ -179,7 +180,7 @@
}
@Override
- protected void applyLoadPlan(ArrayList<GroupTask> taskGroups) {
+ protected void applyLoadPlan(List<GroupTask> taskGroups) {
// When quick-switching on 3p-launcher, we add a "stub" tile corresponding to Launcher
// as well. This tile is never shown as we have setCurrentTaskHidden, but allows use to
// track the index of the next task appropriately, as if we are switching on any other app.
diff --git a/quickstep/src/com/android/quickstep/logging/SettingsChangeLogger.java b/quickstep/src/com/android/quickstep/logging/SettingsChangeLogger.java
index f8d695c..5ac04da 100644
--- a/quickstep/src/com/android/quickstep/logging/SettingsChangeLogger.java
+++ b/quickstep/src/com/android/quickstep/logging/SettingsChangeLogger.java
@@ -148,7 +148,7 @@
@Override
public void onDisplayInfoChanged(Context context, Info info, int flags) {
if ((flags & CHANGE_NAVIGATION_MODE) != 0) {
- mNavMode = info.navigationMode;
+ mNavMode = info.getNavigationMode();
mStatsLogManager.logger().log(mNavMode.launcherEvent);
}
}
diff --git a/quickstep/src/com/android/quickstep/recents/data/RecentTasksDataSource.kt b/quickstep/src/com/android/quickstep/recents/data/RecentTasksDataSource.kt
new file mode 100644
index 0000000..6719099
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/recents/data/RecentTasksDataSource.kt
@@ -0,0 +1,24 @@
+/*
+ * 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.quickstep.recents.data
+
+import com.android.quickstep.util.GroupTask
+import java.util.function.Consumer
+
+interface RecentTasksDataSource {
+ fun getTasks(callback: Consumer<List<GroupTask>>?): Int
+}
diff --git a/quickstep/src/com/android/quickstep/recents/data/RecentTasksRepository.kt b/quickstep/src/com/android/quickstep/recents/data/RecentTasksRepository.kt
new file mode 100644
index 0000000..c1eef0b
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/recents/data/RecentTasksRepository.kt
@@ -0,0 +1,37 @@
+/*
+ * 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.quickstep.recents.data
+
+import com.android.systemui.shared.recents.model.Task
+import kotlinx.coroutines.flow.Flow
+
+interface RecentTasksRepository {
+ /** Gets all the recent tasks, refreshing from data sources if [forceRefresh] is true. */
+ fun getAllTaskData(forceRefresh: Boolean = false): Flow<List<Task>>
+
+ /**
+ * Gets the data associated with a task that has id [taskId]. Flow will settle on null if the
+ * task was not found.
+ */
+ fun getTaskDataById(taskId: Int): Flow<Task?>
+
+ /**
+ * Sets the tasks that are visible, indicating that properties relating to visuals need to be
+ * populated e.g. icons/thumbnails etc.
+ */
+ fun setVisibleTasks(visibleTaskIdList: List<Int>)
+}
diff --git a/quickstep/src/com/android/quickstep/recents/data/TasksRepository.kt b/quickstep/src/com/android/quickstep/recents/data/TasksRepository.kt
new file mode 100644
index 0000000..b21a1b4
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/recents/data/TasksRepository.kt
@@ -0,0 +1,114 @@
+/*
+ * 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.quickstep.recents.data
+
+import com.android.quickstep.TaskIconCache
+import com.android.quickstep.task.thumbnail.data.TaskThumbnailDataSource
+import com.android.quickstep.util.GroupTask
+import com.android.systemui.shared.recents.model.Task
+import com.android.systemui.shared.recents.model.ThumbnailData
+import kotlin.coroutines.resume
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.flow
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.suspendCancellableCoroutine
+
+@OptIn(ExperimentalCoroutinesApi::class)
+class TasksRepository(
+ private val recentsModel: RecentTasksDataSource,
+ private val taskThumbnailDataSource: TaskThumbnailDataSource,
+ private val taskIconCache: TaskIconCache,
+) : RecentTasksRepository {
+ private val groupedTaskData = MutableStateFlow(emptyList<GroupTask>())
+ private val _taskData =
+ groupedTaskData.map { groupTaskList -> groupTaskList.flatMap { it.tasks } }
+ private val visibleTaskIds = MutableStateFlow(emptySet<Int>())
+
+ private val taskData: Flow<List<Task>> =
+ combine(_taskData, getThumbnailQueryResults()) { tasks, results ->
+ tasks.forEach { task ->
+ // Add retrieved thumbnails + remove unnecessary thumbnails
+ task.thumbnail = results[task.key.id]
+ }
+ tasks
+ }
+
+ override fun getAllTaskData(forceRefresh: Boolean): Flow<List<Task>> {
+ if (forceRefresh) {
+ recentsModel.getTasks { groupedTaskData.value = it }
+ }
+ return taskData
+ }
+
+ override fun getTaskDataById(taskId: Int): Flow<Task?> =
+ taskData.map { taskList -> taskList.firstOrNull { it.key.id == taskId } }
+
+ override fun setVisibleTasks(visibleTaskIdList: List<Int>) {
+ this.visibleTaskIds.value = visibleTaskIdList.toSet()
+ }
+
+ /** Flow wrapper for [TaskThumbnailDataSource.updateThumbnailInBackground] api */
+ private fun getThumbnailDataRequest(task: Task): ThumbnailDataRequest =
+ flow {
+ emit(task.key.id to task.thumbnail)
+ val thumbnailDataResult: ThumbnailData? =
+ suspendCancellableCoroutine { continuation ->
+ val cancellableTask =
+ taskThumbnailDataSource.updateThumbnailInBackground(task) {
+ continuation.resume(it)
+ }
+ continuation.invokeOnCancellation { cancellableTask?.cancel() }
+ }
+ emit(task.key.id to thumbnailDataResult)
+ }
+ .distinctUntilChanged()
+
+ /**
+ * This is a Flow that makes a query for thumbnail data to the [taskThumbnailDataSource] for
+ * each visible task. It then collects the responses and returns them in a Map as soon as they
+ * are available.
+ */
+ private fun getThumbnailQueryResults(): Flow<Map<Int, ThumbnailData?>> {
+ val visibleTasks =
+ combine(_taskData, visibleTaskIds) { tasks, visibleIds ->
+ tasks.filter { it.key.id in visibleIds }
+ }
+ val visibleThumbnailDataRequests: Flow<List<ThumbnailDataRequest>> =
+ visibleTasks.map {
+ it.map { visibleTask ->
+ val taskCopy = Task(visibleTask).apply { thumbnail = visibleTask.thumbnail }
+ getThumbnailDataRequest(taskCopy)
+ }
+ }
+ return visibleThumbnailDataRequests.flatMapLatest {
+ thumbnailRequestFlows: List<ThumbnailDataRequest> ->
+ if (thumbnailRequestFlows.isEmpty()) {
+ flowOf(emptyMap())
+ } else {
+ combine(thumbnailRequestFlows) { it.toMap() }
+ }
+ }
+ }
+}
+
+typealias ThumbnailDataRequest = Flow<Pair<Int, ThumbnailData?>>
diff --git a/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailUiState.kt b/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailUiState.kt
index 0843ae3..40f9b28 100644
--- a/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailUiState.kt
+++ b/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailUiState.kt
@@ -16,11 +16,19 @@
package com.android.quickstep.task.thumbnail
-import com.android.systemui.shared.recents.model.Task
+import android.graphics.Bitmap
+import android.graphics.Rect
+import androidx.annotation.ColorInt
sealed class TaskThumbnailUiState {
data object Uninitialized : TaskThumbnailUiState()
data object LiveTile : TaskThumbnailUiState()
+ data class BackgroundOnly(@ColorInt val backgroundColor: Int) : TaskThumbnailUiState()
+ data class Snapshot(
+ val bitmap: Bitmap,
+ val drawnRect: Rect,
+ @ColorInt val backgroundColor: Int
+ ) : TaskThumbnailUiState()
}
-data class TaskThumbnail(val task: Task, val isRunning: Boolean)
+data class TaskThumbnail(val taskId: Int, val isRunning: Boolean)
diff --git a/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailView.kt b/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailView.kt
index 8762976..2836c89 100644
--- a/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailView.kt
+++ b/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailView.kt
@@ -19,15 +19,20 @@
import android.content.Context
import android.content.res.Configuration
import android.graphics.Canvas
+import android.graphics.Color
import android.graphics.Outline
import android.graphics.Paint
import android.graphics.PorterDuff
import android.graphics.PorterDuffXfermode
+import android.graphics.Rect
import android.util.AttributeSet
import android.view.View
import android.view.ViewOutlineProvider
+import androidx.annotation.ColorInt
import com.android.launcher3.Utilities
+import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.BackgroundOnly
import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.LiveTile
+import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.Snapshot
import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.Uninitialized
import com.android.quickstep.util.TaskCornerRadius
import com.android.quickstep.views.RecentsView
@@ -42,17 +47,26 @@
// to [TaskView], and also shared between [TaskView] and [TaskThumbnailView]
// This is using a lazy for now because the dependencies cannot be obtained without DI.
val viewModel by lazy {
- TaskThumbnailViewModel(
+ val recentsView =
RecentsViewContainer.containerFromContext<RecentsViewContainer>(context)
.getOverviewPanel<RecentsView<*, *>>()
- .mRecentsViewData,
- (parent as TaskView).taskViewData
+ TaskThumbnailViewModel(
+ recentsView.mRecentsViewData,
+ (parent as TaskView).taskViewData,
+ recentsView.mTasksRepository,
)
}
private var uiState: TaskThumbnailUiState = Uninitialized
private var inheritedScale: Float = 1f
+ private val backgroundPaint = Paint(Paint.ANTI_ALIAS_FLAG)
+ private val _measuredBounds = Rect()
+ private val measuredBounds: Rect
+ get() {
+ _measuredBounds.set(0, 0, measuredWidth, measuredHeight)
+ return _measuredBounds
+ }
private var cornerRadius: Float = TaskCornerRadius.get(context)
private var fullscreenCornerRadius: Float = QuickStepContract.getWindowCornerRadius(context)
@@ -85,24 +99,25 @@
outlineProvider =
object : ViewOutlineProvider() {
override fun getOutline(view: View, outline: Outline) {
- outline.setRoundRect(
- 0,
- 0,
- view.measuredWidth,
- view.measuredHeight,
- getCurrentCornerRadius()
- )
+ outline.setRoundRect(measuredBounds, getCurrentCornerRadius())
}
}
}
override fun onDraw(canvas: Canvas) {
- when (uiState) {
- is Uninitialized -> {}
+ when (val uiStateVal = uiState) {
+ is Uninitialized -> drawBackgroundOnly(canvas, Color.BLACK)
is LiveTile -> drawTransparentUiState(canvas)
+ is Snapshot -> drawSnapshotState(canvas, uiStateVal)
+ is BackgroundOnly -> drawBackgroundOnly(canvas, uiStateVal.backgroundColor)
}
}
+ private fun drawBackgroundOnly(canvas: Canvas, @ColorInt backgroundColor: Int) {
+ backgroundPaint.color = backgroundColor
+ canvas.drawRect(measuredBounds, backgroundPaint)
+ }
+
override fun onConfigurationChanged(newConfig: Configuration?) {
super.onConfigurationChanged(newConfig)
@@ -112,7 +127,12 @@
}
private fun drawTransparentUiState(canvas: Canvas) {
- canvas.drawRect(0f, 0f, measuredWidth.toFloat(), measuredHeight.toFloat(), CLEAR_PAINT)
+ canvas.drawRect(measuredBounds, CLEAR_PAINT)
+ }
+
+ private fun drawSnapshotState(canvas: Canvas, snapshot: Snapshot) {
+ drawBackgroundOnly(canvas, snapshot.backgroundColor)
+ canvas.drawBitmap(snapshot.bitmap, snapshot.drawnRect, measuredBounds, null)
}
private fun getCurrentCornerRadius() =
diff --git a/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailViewModel.kt b/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailViewModel.kt
index 71bc865..4511ea7 100644
--- a/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailViewModel.kt
+++ b/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailViewModel.kt
@@ -16,32 +16,76 @@
package com.android.quickstep.task.thumbnail
+import android.annotation.ColorInt
+import android.graphics.Rect
+import androidx.core.graphics.ColorUtils
+import com.android.quickstep.recents.data.RecentTasksRepository
import com.android.quickstep.recents.viewmodel.RecentsViewData
+import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.BackgroundOnly
import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.LiveTile
+import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.Snapshot
import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.Uninitialized
import com.android.quickstep.task.viewmodel.TaskViewData
+import com.android.systemui.shared.recents.model.Task
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
-class TaskThumbnailViewModel(recentsViewData: RecentsViewData, taskViewData: TaskViewData) {
- private val task = MutableStateFlow<TaskThumbnail?>(null)
+@OptIn(ExperimentalCoroutinesApi::class)
+class TaskThumbnailViewModel(
+ recentsViewData: RecentsViewData,
+ taskViewData: TaskViewData,
+ private val tasksRepository: RecentTasksRepository,
+) {
+ private val task = MutableStateFlow<Flow<Task?>>(flowOf(null))
+ private var boundTaskIsRunning = false
val recentsFullscreenProgress = recentsViewData.fullscreenProgress
val inheritedScale =
combine(recentsViewData.scale, taskViewData.scale) { recentsScale, taskScale ->
recentsScale * taskScale
}
- val uiState =
- task.map { taskVal ->
- when {
- taskVal == null -> Uninitialized
- taskVal.isRunning -> LiveTile
- else -> Uninitialized
+ val uiState: Flow<TaskThumbnailUiState> =
+ task
+ .flatMapLatest { taskFlow ->
+ taskFlow.map { taskVal ->
+ when {
+ taskVal == null -> Uninitialized
+ boundTaskIsRunning -> LiveTile
+ isBackgroundOnly(taskVal) ->
+ BackgroundOnly(taskVal.colorBackground.removeAlpha())
+ isSnapshotState(taskVal) -> {
+ val bitmap = taskVal.thumbnail?.thumbnail!!
+ Snapshot(
+ bitmap,
+ Rect(0, 0, bitmap.width, bitmap.height),
+ taskVal.colorBackground.removeAlpha()
+ )
+ }
+ else -> Uninitialized
+ }
+ }
}
- }
+ .distinctUntilChanged()
- fun bind(task: TaskThumbnail) {
- this.task.value = task
+ fun bind(taskThumbnail: TaskThumbnail) {
+ boundTaskIsRunning = taskThumbnail.isRunning
+ task.value = tasksRepository.getTaskDataById(taskThumbnail.taskId)
}
+
+ private fun isBackgroundOnly(task: Task): Boolean = task.isLocked || task.thumbnail == null
+
+ private fun isSnapshotState(task: Task): Boolean {
+ val thumbnailPresent = task.thumbnail?.thumbnail != null
+ val taskLocked = task.isLocked
+
+ return thumbnailPresent && !taskLocked
+ }
+
+ @ColorInt private fun Int.removeAlpha(): Int = ColorUtils.setAlphaComponent(this, 0xff)
}
diff --git a/quickstep/src/com/android/quickstep/task/thumbnail/data/TaskThumbnailDataSource.kt b/quickstep/src/com/android/quickstep/task/thumbnail/data/TaskThumbnailDataSource.kt
new file mode 100644
index 0000000..55598f0
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/task/thumbnail/data/TaskThumbnailDataSource.kt
@@ -0,0 +1,29 @@
+/*
+ * 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.quickstep.task.thumbnail.data
+
+import com.android.launcher3.util.CancellableTask
+import com.android.systemui.shared.recents.model.Task
+import com.android.systemui.shared.recents.model.ThumbnailData
+import java.util.function.Consumer
+
+interface TaskThumbnailDataSource {
+ fun updateThumbnailInBackground(
+ task: Task,
+ callback: Consumer<ThumbnailData>
+ ): CancellableTask<ThumbnailData>?
+}
diff --git a/quickstep/src/com/android/quickstep/util/AppPairsController.java b/quickstep/src/com/android/quickstep/util/AppPairsController.java
index e4e2eb2..6f9cbfd 100644
--- a/quickstep/src/com/android/quickstep/util/AppPairsController.java
+++ b/quickstep/src/com/android/quickstep/util/AppPairsController.java
@@ -27,6 +27,8 @@
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT;
import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT;
+import static com.android.wm.shell.common.split.SplitScreenConstants.SNAP_TO_50_50;
+import static com.android.wm.shell.common.split.SplitScreenConstants.SNAP_TO_NONE;
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT;
import static com.android.wm.shell.common.split.SplitScreenConstants.isPersistentSnapPosition;
@@ -186,6 +188,10 @@
}
@PersistentSnapPosition int snapPosition = gtv.getSnapPosition();
+ if (snapPosition == SNAP_TO_NONE) {
+ // Free snap mode is enabled, just save it as 50/50 split.
+ snapPosition = SNAP_TO_50_50;
+ }
if (!isPersistentSnapPosition(snapPosition)) {
// If we received an illegal snap position, log an error and do not create the app pair
Log.wtf(TAG, "Tried to save an app pair with illegal snapPosition "
diff --git a/quickstep/src/com/android/quickstep/util/DesktopTask.java b/quickstep/src/com/android/quickstep/util/DesktopTask.java
index 07f2d68..8d99069 100644
--- a/quickstep/src/com/android/quickstep/util/DesktopTask.java
+++ b/quickstep/src/com/android/quickstep/util/DesktopTask.java
@@ -21,7 +21,7 @@
import com.android.quickstep.views.TaskView;
import com.android.systemui.shared.recents.model.Task;
-import java.util.ArrayList;
+import java.util.List;
/**
* A {@link Task} container that can contain N number of tasks that are part of the desktop in
@@ -30,9 +30,9 @@
public class DesktopTask extends GroupTask {
@NonNull
- public final ArrayList<Task> tasks;
+ public final List<Task> tasks;
- public DesktopTask(@NonNull ArrayList<Task> tasks) {
+ public DesktopTask(@NonNull List<Task> tasks) {
super(tasks.get(0), null, null, TaskView.Type.DESKTOP);
this.tasks = tasks;
}
@@ -53,6 +53,12 @@
}
@Override
+ @NonNull
+ public List<Task> getTasks() {
+ return tasks;
+ }
+
+ @Override
public DesktopTask copy() {
return new DesktopTask(tasks);
}
diff --git a/quickstep/src/com/android/quickstep/util/GroupTask.java b/quickstep/src/com/android/quickstep/util/GroupTask.java
index 7dd6afc..945ffe3 100644
--- a/quickstep/src/com/android/quickstep/util/GroupTask.java
+++ b/quickstep/src/com/android/quickstep/util/GroupTask.java
@@ -23,6 +23,10 @@
import com.android.quickstep.views.TaskView;
import com.android.systemui.shared.recents.model.Task;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
/**
* A {@link Task} container that can contain one or two tasks, depending on if the two tasks
* are represented as an app-pair in the recents task list.
@@ -62,6 +66,17 @@
}
/**
+ * Returns a List of all the Tasks in this GroupTask
+ */
+ public List<Task> getTasks() {
+ if (task2 == null) {
+ return Collections.singletonList(task1);
+ } else {
+ return Arrays.asList(task1, task2);
+ }
+ }
+
+ /**
* Create a copy of this instance
*/
public GroupTask copy() {
diff --git a/quickstep/src/com/android/quickstep/util/NavBarPosition.java b/quickstep/src/com/android/quickstep/util/NavBarPosition.java
deleted file mode 100644
index 9418512..0000000
--- a/quickstep/src/com/android/quickstep/util/NavBarPosition.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright (C) 2019 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.quickstep.util;
-
-import static com.android.launcher3.util.NavigationMode.NO_BUTTON;
-
-import android.view.Surface;
-
-import com.android.launcher3.util.DisplayController.Info;
-import com.android.launcher3.util.NavigationMode;
-
-/**
- * Utility class to check nav bar position.
- */
-public class NavBarPosition {
-
- private final boolean mIsTablet;
- private final NavigationMode mMode;
- private final int mDisplayRotation;
-
- public NavBarPosition(NavigationMode mode, Info info) {
- mIsTablet = info.isTablet(info.realBounds);
- mMode = mode;
- mDisplayRotation = info.rotation;
- }
-
- public boolean isRightEdge() {
- return mMode != NO_BUTTON && mDisplayRotation == Surface.ROTATION_90 && !mIsTablet;
- }
-
- public boolean isLeftEdge() {
- return mMode != NO_BUTTON && mDisplayRotation == Surface.ROTATION_270 && !mIsTablet;
- }
-
- public float getRotation() {
- return isLeftEdge() ? 90 : (isRightEdge() ? -90 : 0);
- }
-}
diff --git a/quickstep/src/com/android/quickstep/util/NavBarPosition.kt b/quickstep/src/com/android/quickstep/util/NavBarPosition.kt
new file mode 100644
index 0000000..43cf540
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/NavBarPosition.kt
@@ -0,0 +1,41 @@
+/*
+ * 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.quickstep.util
+
+import android.view.Surface
+import com.android.launcher3.util.DisplayController.Info
+import com.android.launcher3.util.NavigationMode
+import com.android.launcher3.util.NavigationMode.NO_BUTTON
+
+/** Utility class to check nav bar position. */
+data class NavBarPosition(
+ val isTablet: Boolean,
+ val displayRotation: Int,
+ val mode: NavigationMode
+) {
+ constructor(
+ mode: NavigationMode,
+ info: Info
+ ) : this(info.isTablet(info.realBounds), info.rotation, mode)
+
+ val isRightEdge: Boolean
+ get() = mode != NO_BUTTON && displayRotation == Surface.ROTATION_90 && !isTablet
+ val isLeftEdge: Boolean
+ get() = mode != NO_BUTTON && displayRotation == Surface.ROTATION_270 && !isTablet
+
+ val rotation: Float
+ get() = if (isLeftEdge) 90f else if (isRightEdge) -90f else 0f
+}
diff --git a/quickstep/src/com/android/quickstep/util/RectFSpringAnim.java b/quickstep/src/com/android/quickstep/util/RectFSpringAnim.java
index 769ccc0..00b4011 100644
--- a/quickstep/src/com/android/quickstep/util/RectFSpringAnim.java
+++ b/quickstep/src/com/android/quickstep/util/RectFSpringAnim.java
@@ -157,6 +157,10 @@
mCurrentY = getTrackedYFromRect(mStartRect);
}
+ public RectF getTargetRect() {
+ return mTargetRect;
+ }
+
private float getTrackedYFromRect(RectF rect) {
switch (mTracking) {
case TRACKING_TOP:
diff --git a/quickstep/src/com/android/quickstep/util/ScalingWorkspaceRevealAnim.kt b/quickstep/src/com/android/quickstep/util/ScalingWorkspaceRevealAnim.kt
index 4513fa2..f547a7fb 100644
--- a/quickstep/src/com/android/quickstep/util/ScalingWorkspaceRevealAnim.kt
+++ b/quickstep/src/com/android/quickstep/util/ScalingWorkspaceRevealAnim.kt
@@ -16,6 +16,7 @@
package com.android.quickstep.util
+import android.animation.AnimatorSet
import android.graphics.Matrix
import android.graphics.Path
import android.graphics.RectF
@@ -187,7 +188,11 @@
)
}
+ fun getAnimators(): AnimatorSet {
+ return animation.buildAnim()
+ }
+
fun start() {
- animation.buildAnim().start()
+ getAnimators().start()
}
}
diff --git a/quickstep/src/com/android/quickstep/util/TriggerSwipeUpTouchTracker.java b/quickstep/src/com/android/quickstep/util/TriggerSwipeUpTouchTracker.java
index c63a58e..671b2ea 100644
--- a/quickstep/src/com/android/quickstep/util/TriggerSwipeUpTouchTracker.java
+++ b/quickstep/src/com/android/quickstep/util/TriggerSwipeUpTouchTracker.java
@@ -99,6 +99,7 @@
if (mDisableHorizontalSwipe
&& Math.abs(displacementX) > Math.abs(displacementY)) {
// Horizontal gesture is not allowed in this region
+ mOnSwipeUp.onSwipeUpCancelled();
endTouchTracking();
break;
}
@@ -111,6 +112,7 @@
}
case ACTION_CANCEL:
+ mOnSwipeUp.onSwipeUpCancelled();
endTouchTracking();
break;
diff --git a/quickstep/src/com/android/quickstep/views/DigitalWellBeingToast.java b/quickstep/src/com/android/quickstep/views/DigitalWellBeingToast.java
index a8ebe51..4a5b9e4 100644
--- a/quickstep/src/com/android/quickstep/views/DigitalWellBeingToast.java
+++ b/quickstep/src/com/android/quickstep/views/DigitalWellBeingToast.java
@@ -23,6 +23,7 @@
import android.app.ActivityOptions;
import android.content.ActivityNotFoundException;
+import android.content.Context;
import android.content.Intent;
import android.content.pm.LauncherApps;
import android.content.pm.LauncherApps.AppUsageLimit;
@@ -38,6 +39,7 @@
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewOutlineProvider;
+import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.FrameLayout;
import android.widget.TextView;
@@ -49,6 +51,7 @@
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.util.SplitConfigurationOptions.SplitBounds;
+import com.android.quickstep.TaskUtils;
import com.android.quickstep.orientation.RecentsPagedOrientationHandler;
import com.android.systemui.shared.recents.model.Task;
@@ -68,13 +71,15 @@
private static final int SPLIT_GRID_BANNER_LARGE = 1;
/** Used for grid task view, only showing icon */
private static final int SPLIT_GRID_BANNER_SMALL = 2;
+
@IntDef(value = {
SPLIT_BANNER_FULLSCREEN,
SPLIT_GRID_BANNER_LARGE,
SPLIT_GRID_BANNER_SMALL,
})
@Retention(RetentionPolicy.SOURCE)
- @interface SPLIT_BANNER_CONFIG{}
+ @interface SplitBannerConfig {
+ }
static final Intent OPEN_APP_USAGE_SETTINGS_TEMPLATE = new Intent(ACTION_APP_USAGE_SETTINGS);
static final int MINUTE_MS = 60000;
@@ -85,10 +90,11 @@
private final TaskView mTaskView;
private final LauncherApps mLauncherApps;
+ private final int mBannerHeight;
+
private Task mTask;
private boolean mHasLimit;
- private long mAppUsageLimitTimeMs;
private long mAppRemainingTimeMs;
@Nullable
private View mBanner;
@@ -96,26 +102,27 @@
private float mBannerOffsetPercentage;
@Nullable
private SplitBounds mSplitBounds;
- private int mSplitBannerConfig = SPLIT_BANNER_FULLSCREEN;
private float mSplitOffsetTranslationY;
private float mSplitOffsetTranslationX;
+ private boolean mIsDestroyed = false;
+
public DigitalWellBeingToast(RecentsViewContainer container, TaskView taskView) {
mContainer = container;
mTaskView = taskView;
mLauncherApps = container.asContext().getSystemService(LauncherApps.class);
+ mBannerHeight = container.asContext().getResources().getDimensionPixelSize(
+ R.dimen.digital_wellbeing_toast_height);
}
private void setNoLimit() {
mHasLimit = false;
mTaskView.setContentDescription(mTask.titleDescription);
replaceBanner(null);
- mAppUsageLimitTimeMs = -1;
mAppRemainingTimeMs = -1;
}
private void setLimit(long appUsageLimitTimeMs, long appRemainingTimeMs) {
- mAppUsageLimitTimeMs = appUsageLimitTimeMs;
mAppRemainingTimeMs = appRemainingTimeMs;
mHasLimit = true;
TextView toast = mContainer.getViewCache().getView(R.layout.digital_wellbeing_toast,
@@ -138,89 +145,95 @@
}
public void initialize(Task task) {
- mAppUsageLimitTimeMs = mAppRemainingTimeMs = -1;
+ if (mIsDestroyed) {
+ throw new IllegalStateException("Cannot re-initialize a destroyed toast");
+ }
mTask = task;
ORDERED_BG_EXECUTOR.execute(() -> {
- AppUsageLimit usageLimit = null;
- try {
- usageLimit = mLauncherApps.getAppUsageLimit(
- mTask.getTopComponent().getPackageName(),
- UserHandle.of(mTask.key.userId));
- } catch (Exception e) {
- Log.e(TAG, "Error initializing digital well being toast", e);
- }
- final long appUsageLimitTimeMs =
- usageLimit != null ? usageLimit.getTotalUsageLimit() : -1;
- final long appRemainingTimeMs =
- usageLimit != null ? usageLimit.getUsageRemaining() : -1;
+ AppUsageLimit usageLimit = null;
+ try {
+ usageLimit = mLauncherApps.getAppUsageLimit(
+ mTask.getTopComponent().getPackageName(),
+ UserHandle.of(mTask.key.userId));
+ } catch (Exception e) {
+ Log.e(TAG, "Error initializing digital well being toast", e);
+ }
+ final long appUsageLimitTimeMs =
+ usageLimit != null ? usageLimit.getTotalUsageLimit() : -1;
+ final long appRemainingTimeMs =
+ usageLimit != null ? usageLimit.getUsageRemaining() : -1;
- mTaskView.post(() -> {
- if (appUsageLimitTimeMs < 0 || appRemainingTimeMs < 0) {
- setNoLimit();
- } else {
- setLimit(appUsageLimitTimeMs, appRemainingTimeMs);
- }
- });
-
+ mTaskView.post(() -> {
+ if (mIsDestroyed) {
+ return;
}
- );
+ if (appUsageLimitTimeMs < 0 || appRemainingTimeMs < 0) {
+ setNoLimit();
+ } else {
+ setLimit(appUsageLimitTimeMs, appRemainingTimeMs);
+ }
+ });
+ });
}
- public void setSplitConfiguration(SplitBounds splitBounds) {
+ /**
+ * Mark the DWB toast as destroyed and remove banner from TaskView.
+ */
+ public void destroy() {
+ mIsDestroyed = true;
+ mTaskView.post(() -> replaceBanner(null));
+ }
+
+ public void setSplitBounds(@Nullable SplitBounds splitBounds) {
mSplitBounds = splitBounds;
+ }
+
+ private @SplitBannerConfig int getSplitBannerConfig() {
if (mSplitBounds == null
|| !mContainer.getDeviceProfile().isTablet
|| mTaskView.isFocusedTask()) {
- mSplitBannerConfig = SPLIT_BANNER_FULLSCREEN;
- return;
+ return SPLIT_BANNER_FULLSCREEN;
}
// For portrait grid only height of task changes, not width. So we keep the text the same
if (!mContainer.getDeviceProfile().isLeftRightSplit) {
- mSplitBannerConfig = SPLIT_GRID_BANNER_LARGE;
- return;
+ return SPLIT_GRID_BANNER_LARGE;
}
// For landscape grid, for 30% width we only show icon, otherwise show icon and time
if (mTask.key.id == mSplitBounds.leftTopTaskId) {
- mSplitBannerConfig = mSplitBounds.leftTaskPercent < THRESHOLD_LEFT_ICON_ONLY ?
- SPLIT_GRID_BANNER_SMALL : SPLIT_GRID_BANNER_LARGE;
+ return mSplitBounds.leftTaskPercent < THRESHOLD_LEFT_ICON_ONLY
+ ? SPLIT_GRID_BANNER_SMALL : SPLIT_GRID_BANNER_LARGE;
} else {
- mSplitBannerConfig = mSplitBounds.leftTaskPercent > THRESHOLD_RIGHT_ICON_ONLY ?
- SPLIT_GRID_BANNER_SMALL : SPLIT_GRID_BANNER_LARGE;
+ return mSplitBounds.leftTaskPercent > THRESHOLD_RIGHT_ICON_ONLY
+ ? SPLIT_GRID_BANNER_SMALL : SPLIT_GRID_BANNER_LARGE;
}
}
private String getReadableDuration(
Duration duration,
- FormatWidth formatWidthHourAndMinute,
- @StringRes int durationLessThanOneMinuteStringId,
- boolean forceFormatWidth) {
+ @StringRes int durationLessThanOneMinuteStringId) {
int hours = Math.toIntExact(duration.toHours());
int minutes = Math.toIntExact(duration.minusHours(hours).toMinutes());
- // Apply formatWidthHourAndMinute if both the hour part and the minute part are non-zero.
+ // Apply FormatWidth.WIDE if both the hour part and the minute part are non-zero.
if (hours > 0 && minutes > 0) {
- return MeasureFormat.getInstance(Locale.getDefault(), formatWidthHourAndMinute)
+ return MeasureFormat.getInstance(Locale.getDefault(), FormatWidth.NARROW)
.formatMeasures(
new Measure(hours, MeasureUnit.HOUR),
new Measure(minutes, MeasureUnit.MINUTE));
}
- // Apply formatWidthHourOrMinute if only the hour part is non-zero (unless forced).
+ // Apply FormatWidth.WIDE if only the hour part is non-zero (unless forced).
if (hours > 0) {
- return MeasureFormat.getInstance(
- Locale.getDefault(),
- forceFormatWidth ? formatWidthHourAndMinute : FormatWidth.WIDE)
- .formatMeasures(new Measure(hours, MeasureUnit.HOUR));
+ return MeasureFormat.getInstance(Locale.getDefault(), FormatWidth.WIDE).formatMeasures(
+ new Measure(hours, MeasureUnit.HOUR));
}
- // Apply formatWidthHourOrMinute if only the minute part is non-zero (unless forced).
+ // Apply FormatWidth.WIDE if only the minute part is non-zero (unless forced).
if (minutes > 0) {
- return MeasureFormat.getInstance(
- Locale.getDefault()
- , forceFormatWidth ? formatWidthHourAndMinute : FormatWidth.WIDE)
- .formatMeasures(new Measure(minutes, MeasureUnit.MINUTE));
+ return MeasureFormat.getInstance(Locale.getDefault(), FormatWidth.WIDE).formatMeasures(
+ new Measure(minutes, MeasureUnit.MINUTE));
}
// Use a specific string for usage less than one minute but non-zero.
@@ -229,13 +242,12 @@
}
// Otherwise, return 0-minute string.
- return MeasureFormat.getInstance(
- Locale.getDefault(), forceFormatWidth ? formatWidthHourAndMinute : FormatWidth.WIDE)
- .formatMeasures(new Measure(0, MeasureUnit.MINUTE));
+ return MeasureFormat.getInstance(Locale.getDefault(), FormatWidth.WIDE).formatMeasures(
+ new Measure(0, MeasureUnit.MINUTE));
}
/**
- * Returns text to show for the banner depending on {@link #mSplitBannerConfig}
+ * Returns text to show for the banner depending on {@link #getSplitBannerConfig()}
* If {@param forContentDesc} is {@code true}, this will always return the full
* string corresponding to {@link #SPLIT_BANNER_FULLSCREEN}
*/
@@ -245,16 +257,16 @@
(remainingTime + MINUTE_MS - 1) / MINUTE_MS * MINUTE_MS :
remainingTime);
String readableDuration = getReadableDuration(duration,
- FormatWidth.NARROW,
- R.string.shorter_duration_less_than_one_minute,
- false /* forceFormatWidth */);
- if (forContentDesc || mSplitBannerConfig == SPLIT_BANNER_FULLSCREEN) {
+ R.string.shorter_duration_less_than_one_minute
+ /* forceFormatWidth */);
+ @SplitBannerConfig int splitBannerConfig = getSplitBannerConfig();
+ if (forContentDesc || splitBannerConfig == SPLIT_BANNER_FULLSCREEN) {
return mContainer.asContext().getString(
R.string.time_left_for_app,
readableDuration);
}
- if (mSplitBannerConfig == SPLIT_GRID_BANNER_SMALL) {
+ if (splitBannerConfig == SPLIT_GRID_BANNER_SMALL) {
// show no text
return "";
} else { // SPLIT_GRID_BANNER_LARGE
@@ -309,7 +321,7 @@
private void setBanner(@Nullable View view) {
mBanner = view;
- if (view != null && mTaskView.getRecentsView() != null) {
+ if (mBanner != null && mTaskView.getRecentsView() != null) {
setupAndAddBanner();
setBannerOutline();
}
@@ -351,10 +363,12 @@
}
void updateBannerOffset(float offsetPercentage) {
- if (mBanner != null && mBannerOffsetPercentage != offsetPercentage) {
+ if (mBannerOffsetPercentage != offsetPercentage) {
mBannerOffsetPercentage = offsetPercentage;
- updateTranslationY();
- mBanner.invalidateOutline();
+ if (mBanner != null) {
+ updateTranslationY();
+ mBanner.invalidateOutline();
+ }
}
}
@@ -364,7 +378,7 @@
}
mBanner.setTranslationY(
- (mBannerOffsetPercentage * mBanner.getHeight()) + mSplitOffsetTranslationY);
+ (mBannerOffsetPercentage * mBannerHeight) + mSplitOffsetTranslationY);
}
private void updateTranslationX() {
@@ -395,4 +409,36 @@
mBanner.setVisibility(visibility);
}
+
+ private int getAccessibilityActionId() {
+ return (mSplitBounds != null
+ && mSplitBounds.rightBottomTaskId == mTask.key.id)
+ ? R.id.action_digital_wellbeing_bottom_right
+ : R.id.action_digital_wellbeing_top_left;
+ }
+
+ @Nullable
+ public AccessibilityNodeInfo.AccessibilityAction getDWBAccessibilityAction() {
+ if (!hasLimit()) {
+ return null;
+ }
+
+ Context context = mContainer.asContext();
+ String label =
+ (mTaskView.containsMultipleTasks())
+ ? context.getString(
+ R.string.split_app_usage_settings,
+ TaskUtils.getTitle(context, mTask)
+ ) : context.getString(R.string.accessibility_app_usage_settings);
+ return new AccessibilityNodeInfo.AccessibilityAction(getAccessibilityActionId(), label);
+ }
+
+ public boolean handleAccessibilityAction(int action) {
+ if (getAccessibilityActionId() == action) {
+ openAppUsageSettings(mTaskView);
+ return true;
+ } else {
+ return false;
+ }
+ }
}
diff --git a/quickstep/src/com/android/quickstep/views/GroupedTaskView.kt b/quickstep/src/com/android/quickstep/views/GroupedTaskView.kt
index efbfa09..d6a3376 100644
--- a/quickstep/src/com/android/quickstep/views/GroupedTaskView.kt
+++ b/quickstep/src/com/android/quickstep/views/GroupedTaskView.kt
@@ -162,6 +162,7 @@
PreviewPositionHelper.STAGE_POSITION_BOTTOM_OR_RIGHT
)
}
+ taskContainers.forEach { it.digitalWellBeingToast?.setSplitBounds(splitBoundsConfig) }
setOrientationState(orientedState)
}
@@ -240,6 +241,10 @@
fun updateSplitBoundsConfig(splitBounds: SplitConfigurationOptions.SplitBounds?) {
splitBoundsConfig = splitBounds
+ taskContainers.forEach {
+ it.digitalWellBeingToast?.setSplitBounds(splitBoundsConfig)
+ it.digitalWellBeingToast?.initialize(it.task)
+ }
invalidate()
}
diff --git a/quickstep/src/com/android/quickstep/views/OverviewActionsView.java b/quickstep/src/com/android/quickstep/views/OverviewActionsView.java
index 83a2ceb..604d072 100644
--- a/quickstep/src/com/android/quickstep/views/OverviewActionsView.java
+++ b/quickstep/src/com/android/quickstep/views/OverviewActionsView.java
@@ -20,6 +20,7 @@
import android.content.res.Configuration;
import android.graphics.Rect;
import android.util.AttributeSet;
+import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
@@ -49,6 +50,7 @@
*/
public class OverviewActionsView<T extends OverlayUICallbacks> extends FrameLayout
implements OnClickListener, Insettable {
+ public static final String TAG = "OverviewActionsView";
private final Rect mInsets = new Rect();
@IntDef(flag = true, value = {
@@ -110,9 +112,11 @@
/** Container for the action buttons below a focused, non-split Overview tile. */
protected LinearLayout mActionButtons;
- /** Container for the action buttons below a focused, split Overview tile. */
- protected LinearLayout mGroupActionButtons;
private Button mSplitButton;
+ /**
+ * The "save app pair" button. Currently this is the only button that is not contained in
+ * mActionButtons, since it is the sole button that appears for a grouped task.
+ */
private Button mSaveAppPairButton;
@ActionsHiddenFlags
@@ -150,15 +154,16 @@
super.onFinishInflate();
// Initialize 2 view containers: one for single tasks, one for grouped tasks.
// These will take up the same space on the screen and alternate visibility as needed.
+ // Currently, the only grouped task action is "save app pairs".
mActionButtons = findViewById(R.id.action_buttons);
- mGroupActionButtons = findViewById(R.id.group_action_buttons);
- // Initialize a list to hold alphas for mActionButtons and mGroupActionButtons.
+ mSaveAppPairButton = findViewById(R.id.action_save_app_pair);
+ // Initialize a list to hold alphas for mActionButtons and any group action buttons.
mMultiValueAlphas[ACTIONS_ALPHAS] = new MultiValueAlpha(mActionButtons, NUM_ALPHAS);
mMultiValueAlphas[GROUP_ACTIONS_ALPHAS] =
- new MultiValueAlpha(mGroupActionButtons, NUM_ALPHAS);
+ new MultiValueAlpha(mSaveAppPairButton, NUM_ALPHAS);
Arrays.stream(mMultiValueAlphas).forEach(a -> a.setUpdateVisibility(true));
- // To control alpha simultaneously on mActionButtons and mGroupActionButtons, we set up an
- // AnimatedFloat for each alpha property.
+ // To control alpha simultaneously on mActionButtons and any group action buttons, we set up
+ // an AnimatedFloat for each alpha property.
for (int i = 0; i < NUM_ALPHAS; i++) {
final int index = i;
mAlphaProperties[index] = new AnimatedFloat(() -> {
@@ -175,7 +180,6 @@
screenshotButton.setOnClickListener(this);
mSplitButton = findViewById(R.id.action_split);
mSplitButton.setOnClickListener(this);
- mSaveAppPairButton = findViewById(R.id.action_save_app_pair);
mSaveAppPairButton.setOnClickListener(this);
}
@@ -252,6 +256,8 @@
* pair.
*/
public void updateForGroupedTask(boolean isGroupedTask, boolean canSaveAppPair) {
+ Log.d(TAG, "updateForGroupedTask() called with: isGroupedTask = [" + isGroupedTask
+ + "], canSaveAppPair = [" + canSaveAppPair + "]");
mIsGroupedTask = isGroupedTask;
mCanSaveAppPair = canSaveAppPair;
updateActionButtonsVisibility();
@@ -271,6 +277,8 @@
assert mDp != null;
boolean showSingleTaskActions = !mIsGroupedTask;
boolean showGroupActions = mIsGroupedTask && mDp.isTablet && mCanSaveAppPair;
+ Log.d(TAG, "updateActionButtonsVisibility() called: showSingleTaskActions = ["
+ + showSingleTaskActions + ", showGroupActions = [" + showGroupActions + "]");
getActionsAlphas().get(INDEX_GROUPED_ALPHA).setValue(showSingleTaskActions ? 1 : 0);
getGroupActionsAlphas().get(INDEX_GROUPED_ALPHA).setValue(showGroupActions ? 1 : 0);
}
@@ -336,7 +344,7 @@
*/
public boolean areActionsButtonsVisible() {
return mActionButtons.getVisibility() == View.VISIBLE
- || mGroupActionButtons.getVisibility() == View.VISIBLE;
+ || mSaveAppPairButton.getVisibility() == View.VISIBLE;
}
/**
@@ -350,11 +358,11 @@
/** Updates vertical margins for different navigation mode or configuration changes. */
public void updateVerticalMargin(NavigationMode mode) {
updateActionBarPosition(mActionButtons);
- updateActionBarPosition(mGroupActionButtons);
+ updateActionBarPosition(mSaveAppPairButton);
}
/** Positions actions buttons according to device settings and insets. */
- private void updateActionBarPosition(LinearLayout actionBar) {
+ private void updateActionBarPosition(View actionBar) {
if (mDp == null) {
return;
}
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index 275734d..9096b75 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -188,6 +188,7 @@
import com.android.quickstep.TopTaskTracker;
import com.android.quickstep.ViewUtils;
import com.android.quickstep.orientation.RecentsPagedOrientationHandler;
+import com.android.quickstep.recents.data.TasksRepository;
import com.android.quickstep.recents.viewmodel.RecentsViewData;
import com.android.quickstep.util.ActiveGestureErrorDetector;
import com.android.quickstep.util.ActiveGestureLog;
@@ -216,6 +217,7 @@
import com.android.systemui.shared.system.PackageManagerWrapper;
import com.android.systemui.shared.system.TaskStackChangeListener;
import com.android.systemui.shared.system.TaskStackChangeListeners;
+import com.android.wm.shell.common.desktopmode.DesktopModeTransitionSource;
import com.android.wm.shell.common.pip.IPipAnimationListener;
import com.android.wm.shell.shared.DesktopModeStatus;
@@ -305,6 +307,7 @@
public static final float SCROLL_VIBRATION_PRIMITIVE_SCALE = 0.6f;
public static final VibrationEffect SCROLL_VIBRATION_FALLBACK =
VibrationConstants.EFFECT_TEXTURE_TICK;
+ public static final int UNBOUND_TASK_VIEW_ID = -1;
/**
* Can be used to tint the color of the RecentsView to simulate a scrim that can views
@@ -456,6 +459,7 @@
private static final float FOREGROUND_SCRIM_TINT = 0.32f;
public final RecentsViewData mRecentsViewData = new RecentsViewData();
+ public final TasksRepository mTasksRepository;
protected final RecentsOrientedState mOrientationState;
protected final BaseContainerInterface<STATE_TYPE, CONTAINER_TYPE> mSizeStrategy;
@@ -800,6 +804,12 @@
.getDimensionPixelSize(R.dimen.recents_fast_fling_velocity);
mModel = RecentsModel.INSTANCE.get(context);
mIdp = InvariantDeviceProfile.INSTANCE.get(context);
+ if (enableRefactorTaskThumbnail()) {
+ mTasksRepository = new TasksRepository(
+ mModel, mModel.getThumbnailCache(), mModel.getIconCache());
+ } else {
+ mTasksRepository = null;
+ }
mClearAllButton = (ClearAllButton) LayoutInflater.from(context)
.inflate(R.layout.overview_clear_all_button, this, false);
@@ -1165,7 +1175,6 @@
} else {
mTaskViewPool.recycle(taskView);
}
- taskView.setTaskViewId(-1);
mActionsView.updateHiddenFlags(HIDDEN_NO_TASKS, getTaskViewCount() == 0);
}
}
@@ -1716,7 +1725,7 @@
return super.isPageScrollsInitialized() && mLoadPlanEverApplied;
}
- protected void applyLoadPlan(ArrayList<GroupTask> taskGroups) {
+ protected void applyLoadPlan(List<GroupTask> taskGroups) {
if (mPendingAnimation != null) {
mPendingAnimation.addEndListener(success -> applyLoadPlan(taskGroups));
return;
@@ -2362,6 +2371,8 @@
upper = Math.min(centerPageIndex + 2, numChildren - 1);
}
+ List<Integer> visibleTaskIds = new ArrayList<>();
+
// Update the task data for the in/visible children
for (int i = 0; i < getTaskViewCount(); i++) {
TaskView taskView = requireTaskViewAt(i);
@@ -2381,6 +2392,10 @@
List<Task> tasksToUpdate = containers.stream()
.map(TaskContainer::getTask)
.collect(Collectors.toCollection(ArrayList::new));
+ if (enableRefactorTaskThumbnail()) {
+ visibleTaskIds.addAll(
+ tasksToUpdate.stream().map((task) -> task.key.id).toList());
+ }
if (mTmpRunningTasks != null) {
for (Task t : mTmpRunningTasks) {
// Skip loading if this is the task that we are animating into
@@ -2416,6 +2431,9 @@
}
}
}
+ if (enableRefactorTaskThumbnail()) {
+ mTasksRepository.setVisibleTasks(visibleTaskIds);
+ }
}
/**
@@ -2602,6 +2620,9 @@
if (!mModel.isTaskListValid(mTaskListChangeId)) {
mTaskListChangeId = mModel.getTasks(this::applyLoadPlan, RecentsFilterState
.getFilter(mFilterState.getPackageNameToFilter()));
+ if (enableRefactorTaskThumbnail()) {
+ mTasksRepository.getAllTaskData(/* forceRefresh = */ true);
+ }
}
}
@@ -2646,6 +2667,20 @@
// Let system take care of the rotation
return;
}
+
+ if (mRunningTaskShowScreenshot) {
+ animateRotation(newRotation);
+ } else {
+ // Animate the rotation and stops running task
+ switchToScreenshot(() -> {
+ animateRotation(newRotation);
+ finishRecentsAnimation(true /* toRecents */, false /* shouldPip */,
+ null /* onFinishComplete */);
+ });
+ }
+ }
+
+ private void animateRotation(int newRotation) {
AnimatorSet pa = setRecentsChangedOrientation(true);
pa.addListener(AnimatorListeners.forSuccessCallback(() -> {
setLayoutRotation(newRotation, mOrientationState.getDisplayRotation());
@@ -2655,17 +2690,9 @@
pa.start();
}
- public AnimatorSet setRecentsChangedOrientation(boolean fadeInChildren) {
- getRunningTaskIndex();
- int runningIndex = getCurrentPage();
+ public AnimatorSet setRecentsChangedOrientation(boolean fadeOut) {
AnimatorSet as = new AnimatorSet();
- for (int i = 0; i < getTaskViewCount(); i++) {
- View taskView = requireTaskViewAt(i);
- if (runningIndex == i && taskView.getAlpha() != 0) {
- continue;
- }
- as.play(ObjectAnimator.ofFloat(taskView, View.ALPHA, fadeInChildren ? 0 : 1));
- }
+ as.play(ObjectAnimator.ofFloat(this, View.ALPHA, fadeOut ? 0 : 1));
return as;
}
@@ -2935,7 +2962,6 @@
int taskCount = getTaskViewCount();
for (int i = 0; i < taskCount; i++) {
TaskView taskView = requireTaskViewAt(i);
- taskView.setIconScaleAnimStartProgress(0f);
taskView.animateIconScaleAndDimIntoView();
}
}
@@ -3768,7 +3794,7 @@
anim.setFloat(taskView, taskView.getSecondaryDismissTranslationProperty(),
secondaryTranslation, clampToProgress(LINEAR, animationStartProgress,
dismissTranslationInterpolationEnd));
- anim.setFloat(taskView, TaskView.FOCUS_TRANSITION, 0f,
+ anim.setFloat(taskView, TaskView.SCALE_AND_DIM_OUT, 0f,
clampToProgress(LINEAR, 0f, ANIMATION_DISMISS_PROGRESS_MIDPOINT));
} else {
float primaryTranslation =
@@ -5912,7 +5938,7 @@
return;
}
- taskView.setShouldShowScreenshot(true);
+ setRunningTaskViewShowScreenshot(true);
for (TaskContainer container : taskView.getTaskContainers()) {
if (container == null) {
continue;
@@ -6284,20 +6310,22 @@
* Moves the provided task into desktop mode, and invoke {@code successCallback} if succeeded.
*/
public void moveTaskToDesktop(TaskContainer taskContainer,
+ DesktopModeTransitionSource transitionSource,
Runnable successCallback) {
if (!DesktopModeStatus.canEnterDesktopMode(mContext)) {
return;
}
switchToScreenshot(() -> finishRecentsAnimation(/* toRecents= */true, /* shouldPip= */false,
- () -> moveTaskToDesktopInternal(taskContainer, successCallback)));
+ () -> moveTaskToDesktopInternal(taskContainer, successCallback, transitionSource)));
}
private void moveTaskToDesktopInternal(TaskContainer taskContainer,
- Runnable successCallback) {
+ Runnable successCallback, DesktopModeTransitionSource transitionSource) {
if (mDesktopRecentsTransitionController == null) {
return;
}
- mDesktopRecentsTransitionController.moveToDesktop(taskContainer.getTask().key.id);
+ mDesktopRecentsTransitionController.moveToDesktop(taskContainer.getTask().key.id,
+ transitionSource);
successCallback.run();
}
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.kt b/quickstep/src/com/android/quickstep/views/TaskView.kt
index 05b9d40..4fd686d 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.kt
+++ b/quickstep/src/com/android/quickstep/views/TaskView.kt
@@ -38,6 +38,7 @@
import android.view.ViewGroup
import android.view.ViewStub
import android.view.accessibility.AccessibilityNodeInfo
+import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction
import android.widget.FrameLayout
import android.widget.Toast
import androidx.annotation.IntDef
@@ -51,7 +52,6 @@
import com.android.launcher3.Flags.enableRefactorTaskThumbnail
import com.android.launcher3.Flags.privateSpaceRestrictAccessibilityDrag
import com.android.launcher3.LauncherSettings
-import com.android.launcher3.LauncherState
import com.android.launcher3.R
import com.android.launcher3.Utilities
import com.android.launcher3.config.FeatureFlags.ENABLE_KEYBOARD_QUICK_SWITCH
@@ -64,10 +64,11 @@
import com.android.launcher3.testing.shared.TestProtocol
import com.android.launcher3.util.CancellableTask
import com.android.launcher3.util.Executors
+import com.android.launcher3.util.MultiPropertyFactory
+import com.android.launcher3.util.MultiPropertyFactory.MULTI_PROPERTY_VALUE
import com.android.launcher3.util.RunnableList
import com.android.launcher3.util.SafeCloseable
import com.android.launcher3.util.SplitConfigurationOptions
-import com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT
import com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_UNDEFINED
import com.android.launcher3.util.SplitConfigurationOptions.SplitPositionOption
import com.android.launcher3.util.SplitConfigurationOptions.StagePosition
@@ -93,6 +94,7 @@
import com.android.quickstep.util.RecentsOrientedState
import com.android.quickstep.util.TaskCornerRadius
import com.android.quickstep.util.TaskRemovedDuringLaunchListener
+import com.android.quickstep.views.RecentsView.UNBOUND_TASK_VIEW_ID
import com.android.systemui.shared.recents.model.Task
import com.android.systemui.shared.recents.model.ThumbnailData
import com.android.systemui.shared.system.ActivityManagerWrapper
@@ -132,19 +134,26 @@
val taskIds: IntArray
/** Returns a copy of integer array containing taskIds of all tasks in the TaskView. */
get() = taskContainers.map { it.task.key.id }.toIntArray()
+
val thumbnailViews: Array<TaskThumbnailViewDeprecated>
get() = taskContainers.map { it.thumbnailViewDeprecated }.toTypedArray()
+
val isGridTask: Boolean
/** Returns whether the task is part of overview grid and not being focused. */
get() = container.deviceProfile.isTablet && !isFocusedTask
+
val isRunningTask: Boolean
get() = this === recentsView?.runningTaskView
+
val isFocusedTask: Boolean
get() = this === recentsView?.focusedTaskView
+
val taskCornerRadius: Float
get() = currentFullscreenParams.cornerRadius
+
val recentsView: RecentsView<*, *>?
get() = parent as? RecentsView<*, *>
+
val pagedOrientationHandler: RecentsPagedOrientationHandler
get() = orientedState.orientationHandler
@@ -152,10 +161,12 @@
val firstTask: Task
/** Returns the first task bound to this TaskView. */
get() = taskContainers[0].task
+
@get:Deprecated("Use [taskContainers] instead.")
val firstThumbnailViewDeprecated: TaskThumbnailViewDeprecated
/** Returns the first thumbnailView of the TaskView. */
get() = taskContainers[0].thumbnailViewDeprecated
+
@get:Deprecated("Use [taskContainers] instead.")
val firstItemInfo: ItemInfo
get() = taskContainers[0].itemInfo
@@ -172,6 +183,7 @@
* not change according to a temporary state.
*/
get() = Utilities.mapRange(gridProgress, nonGridScale, 1f)
+
protected val persistentTranslationX: Float
/**
* Returns addition of translationX that is persistent (e.g. fullscreen and grid), and does
@@ -181,42 +193,50 @@
(getNonGridTrans(nonGridTranslationX) +
getGridTrans(this.gridTranslationX) +
getNonGridTrans(nonGridPivotTranslationX))
+
protected val persistentTranslationY: Float
/**
* Returns addition of translationY that is persistent (e.g. fullscreen and grid), and does
* not change according to a temporary state (e.g. task offset).
*/
get() = boxTranslationY + getGridTrans(gridTranslationY)
+
protected val primarySplitTranslationProperty: FloatProperty<TaskView>
get() =
pagedOrientationHandler.getPrimaryValue(
SPLIT_SELECT_TRANSLATION_X,
SPLIT_SELECT_TRANSLATION_Y
)
+
protected val secondarySplitTranslationProperty: FloatProperty<TaskView>
get() =
pagedOrientationHandler.getSecondaryValue(
SPLIT_SELECT_TRANSLATION_X,
SPLIT_SELECT_TRANSLATION_Y
)
+
protected val primaryDismissTranslationProperty: FloatProperty<TaskView>
get() =
pagedOrientationHandler.getPrimaryValue(DISMISS_TRANSLATION_X, DISMISS_TRANSLATION_Y)
+
protected val secondaryDismissTranslationProperty: FloatProperty<TaskView>
get() =
pagedOrientationHandler.getSecondaryValue(DISMISS_TRANSLATION_X, DISMISS_TRANSLATION_Y)
+
protected val primaryTaskOffsetTranslationProperty: FloatProperty<TaskView>
get() =
pagedOrientationHandler.getPrimaryValue(
TASK_OFFSET_TRANSLATION_X,
TASK_OFFSET_TRANSLATION_Y
)
+
protected val secondaryTaskOffsetTranslationProperty: FloatProperty<TaskView>
get() =
pagedOrientationHandler.getSecondaryValue(
TASK_OFFSET_TRANSLATION_X,
TASK_OFFSET_TRANSLATION_Y
)
+
protected val taskResistanceTranslationProperty: FloatProperty<TaskView>
get() =
pagedOrientationHandler.getSecondaryValue(
@@ -233,9 +253,10 @@
/** Returns a list of all TaskContainers in the TaskView. */
lateinit var taskContainers: List<TaskContainer>
protected set
+
lateinit var orientedState: RecentsOrientedState
- var taskViewId = -1
+ var taskViewId = UNBOUND_TASK_VIEW_ID
var isEndQuickSwitchCuj = false
// Various animation progress variables.
@@ -245,12 +266,14 @@
field = Utilities.boundToRange(value, 0f, 1f)
onFullscreenProgressChanged(field)
}
+
// gridProgress 0 = carousel; 1 = 2 row grid.
protected var gridProgress = 0f
set(value) {
field = value
onGridProgressChanged()
}
+
/**
* The modalness of this view is how it should be displayed when it is shown on its own in the
* modal state of overview. 0 being in context with other tasks, 1 being shown on its own.
@@ -263,74 +286,88 @@
field = value
onModalnessUpdated(field)
}
+
protected var taskThumbnailSplashAlpha = 0f
set(value) {
field = value
applyThumbnailSplashAlpha()
}
+
protected var nonGridScale = 1f
set(value) {
field = value
applyScale()
}
+
private var dismissScale = 1f
set(value) {
field = value
applyScale()
}
+
private var dismissTranslationX = 0f
set(value) {
field = value
applyTranslationX()
}
+
private var dismissTranslationY = 0f
set(value) {
field = value
applyTranslationY()
}
+
private var taskOffsetTranslationX = 0f
set(value) {
field = value
applyTranslationX()
}
+
private var taskOffsetTranslationY = 0f
set(value) {
field = value
applyTranslationY()
}
+
private var taskResistanceTranslationX = 0f
set(value) {
field = value
applyTranslationX()
}
+
private var taskResistanceTranslationY = 0f
set(value) {
field = value
applyTranslationY()
}
+
// The following translation variables should only be used in the same orientation as Launcher.
private var boxTranslationY = 0f
set(value) {
field = value
applyTranslationY()
}
+
// The following grid translations scales with mGridProgress.
protected var gridTranslationX = 0f
set(value) {
field = value
applyTranslationX()
}
+
var gridTranslationY = 0f
protected set(value) {
field = value
applyTranslationY()
}
+
// The following grid translation is used to animate closing the gap between grid and clear all.
private var gridEndTranslationX = 0f
set(value) {
field = value
applyTranslationX()
}
+
// Applied as a complement to gridTranslation, for adjusting the carousel overview and quick
// switch.
protected var nonGridTranslationX = 0f
@@ -338,29 +375,35 @@
field = value
applyTranslationX()
}
+
protected var nonGridPivotTranslationX = 0f
set(value) {
field = value
applyTranslationX()
}
+
// Used when in SplitScreenSelectState
private var splitSelectTranslationY = 0f
set(value) {
field = value
applyTranslationY()
}
+
private var splitSelectTranslationX = 0f
set(value) {
field = value
applyTranslationX()
}
+
protected var stableAlpha = 1f
set(value) {
field = value
alpha = stableAlpha
}
+
protected var shouldShowScreenshot = false
get() = !isRunningTask || field
+
/** Enable or disable showing border on hover and focus change */
@VisibleForTesting(otherwise = VisibleForTesting.PROTECTED)
var borderEnabled = false
@@ -374,8 +417,36 @@
hoverBorderAnimator?.setBorderVisibility(visible = field && isHovered, animated = true)
focusBorderAnimator?.setBorderVisibility(visible = field && isFocused, animated = true)
}
- protected var iconScaleAnimStartProgress = 0f
+
private var focusTransitionProgress = 1f
+ set(value) {
+ field = value
+ onFocusTransitionProgressUpdated(field)
+ }
+
+ private val focusTransitionPropertyFactory =
+ MultiPropertyFactory(
+ this,
+ FOCUS_TRANSITION,
+ FOCUS_TRANSITION_INDEX_COUNT,
+ { x: Float, y: Float -> x * y },
+ 1f
+ )
+ private val focusTransitionFullscreen =
+ focusTransitionPropertyFactory.get(FOCUS_TRANSITION_INDEX_FULLSCREEN)
+ private val focusTransitionScaleAndDim =
+ focusTransitionPropertyFactory.get(FOCUS_TRANSITION_INDEX_SCALE_AND_DIM)
+ /**
+ * Variant of [focusTransitionScaleAndDim] that has a built-in interpolator, to be used with
+ * [com.android.launcher3.anim.PendingAnimation] via [SCALE_AND_DIM_OUT] only. PendingAnimation
+ * doesn't support interpolator per animation, so we'll have to interpolate inside the property.
+ */
+ private var focusTransitionScaleAndDimOut = focusTransitionScaleAndDim.value
+ set(value) {
+ field = value
+ focusTransitionScaleAndDim.value =
+ FOCUS_TRANSITION_FAST_OUT_INTERPOLATOR.getInterpolation(field)
+ }
private var iconAndDimAnimator: ObjectAnimator? = null
// The current background requests to load the task thumbnail and icon
@@ -502,7 +573,6 @@
resetPersistentViewTransforms()
// Clear any references to the thumbnail (it will be re-read either from the cache or the
// system on next bind)
- // TODO(b/334825222): Implement thumbnail/snapshot for the new [TaskThumbnailView].
if (enableRefactorTaskThumbnail()) {
notifyIsRunningTaskUpdated()
} else {
@@ -511,6 +581,8 @@
setOverlayEnabled(false)
onTaskListVisibilityChanged(false)
borderEnabled = false
+ taskViewId = UNBOUND_TASK_VIEW_ID
+ taskContainers.forEach { it.destroy() }
}
// TODO: Clip-out the icon region from the thumbnail, since they are overlapping.
@@ -520,11 +592,12 @@
super.onInitializeAccessibilityNodeInfo(info)
with(info) {
addAction(
- AccessibilityNodeInfo.AccessibilityAction(
- R.string.accessibility_close,
+ AccessibilityAction(
+ R.id.action_close,
context.getText(R.string.accessibility_close)
)
)
+
taskContainers.forEach {
TraceHelper.allowIpcs("TV.a11yInfo") {
TaskOverlayFactory.getEnabledShortcuts(this@TaskView, it).forEach { shortcut ->
@@ -532,15 +605,12 @@
}
}
}
- // TODO(b/341672022): handle multiple digitalWellBeingToast accessibility actions
- if (taskContainers[0].digitalWellBeingToast?.hasLimit() == true) {
- addAction(
- AccessibilityNodeInfo.AccessibilityAction(
- R.string.accessibility_app_usage_settings,
- context.getText(R.string.accessibility_app_usage_settings)
- )
- )
+
+ // Add DWB accessibility action at the end of the list
+ taskContainers.forEach {
+ it.digitalWellBeingToast?.getDWBAccessibilityAction()?.let(::addAction)
}
+
recentsView?.let {
collectionItemInfo =
AccessibilityNodeInfo.CollectionItemInfo.obtain(
@@ -555,16 +625,17 @@
}
override fun performAccessibilityAction(action: Int, arguments: Bundle?): Boolean {
- if (action == R.string.accessibility_close) {
+ // TODO(b/343708271): Add support for multiple tasks per action.
+ if (action == R.id.action_close) {
recentsView?.dismissTask(this, true /*animateTaskView*/, true /*removeTask*/)
return true
}
- if (action == R.string.accessibility_app_usage_settings) {
- // TODO(b/341672022): handle multiple digitalWellBeingToast accessibility actions
- taskContainers[0].digitalWellBeingToast?.openAppUsageSettings(this)
- return true
- }
+
taskContainers.forEach {
+ if (it.digitalWellBeingToast?.handleAccessibilityAction(action) == true) {
+ return true
+ }
+
TaskOverlayFactory.getEnabledShortcuts(this, it).forEach { shortcut ->
if (shortcut.hasHandlerForAction(action)) {
shortcut.onClick(this)
@@ -572,6 +643,7 @@
}
}
}
+
return super.performAccessibilityAction(action, arguments)
}
@@ -780,22 +852,19 @@
val recentsModel = RecentsModel.INSTANCE.get(context)
// These calls are no-ops if the data is already loaded, try and load the high
// resolution thumbnail if the state permits
- if (needsUpdate(changes, FLAG_UPDATE_THUMBNAIL)) {
- if (!enableRefactorTaskThumbnail()) {
- // TODO(b/334825222) add thumbnail state
- taskContainers.forEach {
- if (visible) {
- recentsModel.thumbnailCache
- .updateThumbnailInBackground(it.task) { thumbnailData ->
- it.thumbnailViewDeprecated.setThumbnail(it.task, thumbnailData)
- }
- ?.also { request -> pendingThumbnailLoadRequests.add(request) }
- } else {
- it.thumbnailViewDeprecated.setThumbnail(null, null)
- // Reset the task thumbnail reference as well (it will be fetched from the
- // cache or reloaded next time we need it)
- it.task.thumbnail = null
- }
+ if (needsUpdate(changes, FLAG_UPDATE_THUMBNAIL) && !enableRefactorTaskThumbnail()) {
+ taskContainers.forEach {
+ if (visible) {
+ recentsModel.thumbnailCache
+ .updateThumbnailInBackground(it.task) { thumbnailData ->
+ it.thumbnailViewDeprecated.setThumbnail(it.task, thumbnailData)
+ }
+ ?.also { request -> pendingThumbnailLoadRequests.add(request) }
+ } else {
+ it.thumbnailViewDeprecated.setThumbnail(null, null)
+ // Reset the task thumbnail reference as well (it will be fetched from the
+ // cache or reloaded next time we need it)
+ it.task.thumbnail = null
}
}
}
@@ -803,12 +872,12 @@
taskContainers.forEach {
if (visible) {
recentsModel.iconCache
- .updateIconInBackground(it.task) { thumbnailData ->
- setIcon(it.iconView, thumbnailData.icon)
+ .updateIconInBackground(it.task) { task ->
+ setIcon(it.iconView, task.icon)
if (enableOverviewIconMenu()) {
- setText(it.iconView, thumbnailData.title)
+ setText(it.iconView, task.title)
}
- it.digitalWellBeingToast?.initialize(thumbnailData)
+ it.digitalWellBeingToast?.initialize(task)
}
?.also { request -> pendingIconLoadRequests.add(request) }
} else {
@@ -861,7 +930,7 @@
open fun refreshThumbnails(thumbnailDatas: HashMap<Int, ThumbnailData?>?) {
if (enableRefactorTaskThumbnail()) {
- // TODO(b/334825222) add thumbnail logic
+ // TODO(b/342560598) add thumbnail logic
return
}
@@ -1268,43 +1337,23 @@
}
}
- protected open fun setIconsAndBannersFullscreenProgress(progress: Float) {
- // Animate icons and DWB banners in/out, except in QuickSwitch state, when tiles are
- // oversized and banner would look disproportionately large.
- if (recentsView?.stateManager?.state == LauncherState.BACKGROUND_APP) {
- return
- }
- setIconsAndBannersTransitionProgress(progress, invert = true)
- }
-
/**
* Called to animate a smooth transition when going directly from an app into Overview (and vice
* versa). Icons fade in, and DWB banners slide in with a "shift up" animation.
*/
- protected open fun setIconsAndBannersTransitionProgress(progress: Float, invert: Boolean) {
- focusTransitionProgress = if (invert) 1 - progress else progress
- getIconContentScale(invert).let { iconContentScale ->
- taskContainers.forEach {
- it.iconView.setContentAlpha(iconContentScale)
- it.digitalWellBeingToast?.updateBannerOffset(1f - iconContentScale)
- }
+ private fun onFocusTransitionProgressUpdated(focusTransitionProgress: Float) {
+ taskContainers.forEach {
+ it.iconView.setContentAlpha(focusTransitionProgress)
+ it.digitalWellBeingToast?.updateBannerOffset(1f - focusTransitionProgress)
}
}
- private fun getIconContentScale(invert: Boolean): Float {
- val iconScalePercentage = SCALE_ICON_DURATION.toFloat() / DIM_ANIM_DURATION
- val lowerClamp = if (invert) 1f - iconScalePercentage else 0f
- val upperClamp = if (invert) 1f else iconScalePercentage
- return Interpolators.clampToProgress(Interpolators.FAST_OUT_SLOW_IN, lowerClamp, upperClamp)
- .getInterpolation(focusTransitionProgress)
- }
-
fun animateIconScaleAndDimIntoView() {
iconAndDimAnimator?.cancel()
iconAndDimAnimator =
- ObjectAnimator.ofFloat(this, FOCUS_TRANSITION, 1f).apply {
- setCurrentFraction(iconScaleAnimStartProgress)
- setDuration(DIM_ANIM_DURATION).interpolator = Interpolators.LINEAR
+ ObjectAnimator.ofFloat(focusTransitionScaleAndDim, MULTI_PROPERTY_VALUE, 0f, 1f).apply {
+ duration = SCALE_ICON_DURATION
+ interpolator = Interpolators.LINEAR
addListener(
object : AnimatorListenerAdapter() {
override fun onAnimationEnd(animation: Animator) {
@@ -1318,7 +1367,7 @@
fun setIconScaleAndDim(iconScale: Float) {
iconAndDimAnimator?.cancel()
- setIconsAndBannersTransitionProgress(iconScale, invert = false)
+ focusTransitionScaleAndDim.value = iconScale
}
/** Set a color tint on the snapshot and supporting views. */
@@ -1421,7 +1470,8 @@
it.iconView.setVisibility(if (fullscreenProgress < 1) VISIBLE else INVISIBLE)
it.overlay.setFullscreenProgress(fullscreenProgress)
}
- setIconsAndBannersFullscreenProgress(fullscreenProgress)
+ focusTransitionFullscreen.value =
+ FOCUS_TRANSITION_FAST_OUT_INTERPOLATOR.getInterpolation(1 - fullscreenProgress)
updateSnapshotRadius()
}
@@ -1556,11 +1606,6 @@
) {
val overlay: TaskOverlay<*> = taskOverlayFactory.createOverlay(this)
- @IdRes
- val a11yNodeId: Int =
- if (stagePosition == STAGE_POSITION_BOTTOM_OR_RIGHT) R.id.split_bottomRight_appInfo
- else R.id.split_topLeft_appInfo
-
val snapshotView: View
get() = thumbnailView ?: thumbnailViewDeprecated
@@ -1588,10 +1633,17 @@
val taskView: TaskView
get() = this@TaskView
+ fun destroy() {
+ digitalWellBeingToast?.destroy()
+ thumbnailView?.let { taskView.removeView(it) }
+ }
+
// TODO(b/335649589): TaskView's VM will already have access to TaskThumbnailView's VM
// so there will be no need to access TaskThumbnailView's VM through the TaskThumbnailView
fun bindThumbnailView() {
- thumbnailView?.viewModel?.bind(TaskThumbnail(task, isRunningTask))
+ // TODO(b/343364498): Existing view has shouldShowScreenshot as an override as well but
+ // this should be decided inside TaskThumbnailViewModel.
+ thumbnailView?.viewModel?.bind(TaskThumbnail(task.key.id, isRunningTask))
}
}
@@ -1603,21 +1655,43 @@
const val FLAG_UPDATE_ALL =
(FLAG_UPDATE_ICON or FLAG_UPDATE_THUMBNAIL or FLAG_UPDATE_CORNER_RADIUS)
+ const val FOCUS_TRANSITION_INDEX_FULLSCREEN = 0
+ const val FOCUS_TRANSITION_INDEX_SCALE_AND_DIM = 1
+ const val FOCUS_TRANSITION_INDEX_COUNT = 2
+
/** The maximum amount that a task view can be scrimmed, dimmed or tinted. */
const val MAX_PAGE_SCRIM_ALPHA = 0.4f
const val SCALE_ICON_DURATION: Long = 120
private const val DIM_ANIM_DURATION: Long = 700
+ private const val FOCUS_TRANSITION_THRESHOLD =
+ SCALE_ICON_DURATION.toFloat() / DIM_ANIM_DURATION
+ val FOCUS_TRANSITION_FAST_OUT_INTERPOLATOR =
+ Interpolators.clampToProgress(
+ Interpolators.FAST_OUT_SLOW_IN,
+ 1f - FOCUS_TRANSITION_THRESHOLD,
+ 1f
+ )!!
private val SYSTEM_GESTURE_EXCLUSION_RECT = listOf(Rect())
- @JvmField
- val FOCUS_TRANSITION: FloatProperty<TaskView> =
+ private val FOCUS_TRANSITION: FloatProperty<TaskView> =
object : FloatProperty<TaskView>("focusTransition") {
override fun setValue(taskView: TaskView, v: Float) {
- taskView.setIconsAndBannersTransitionProgress(v, false /* invert */)
+ taskView.focusTransitionProgress = v
}
override fun get(taskView: TaskView) = taskView.focusTransitionProgress
}
+
+ @JvmField
+ val SCALE_AND_DIM_OUT: FloatProperty<TaskView> =
+ object : FloatProperty<TaskView>("scaleAndDimFastOut") {
+ override fun setValue(taskView: TaskView, v: Float) {
+ taskView.focusTransitionScaleAndDimOut = v
+ }
+
+ override fun get(taskView: TaskView) = taskView.focusTransitionScaleAndDimOut
+ }
+
private val SPLIT_SELECT_TRANSLATION_X: FloatProperty<TaskView> =
object : FloatProperty<TaskView>("splitSelectTranslationX") {
override fun setValue(taskView: TaskView, v: Float) {
@@ -1626,6 +1700,7 @@
override fun get(taskView: TaskView) = taskView.splitSelectTranslationX
}
+
private val SPLIT_SELECT_TRANSLATION_Y: FloatProperty<TaskView> =
object : FloatProperty<TaskView>("splitSelectTranslationY") {
override fun setValue(taskView: TaskView, v: Float) {
@@ -1634,6 +1709,7 @@
override fun get(taskView: TaskView) = taskView.splitSelectTranslationY
}
+
private val DISMISS_TRANSLATION_X: FloatProperty<TaskView> =
object : FloatProperty<TaskView>("dismissTranslationX") {
override fun setValue(taskView: TaskView, v: Float) {
@@ -1642,6 +1718,7 @@
override fun get(taskView: TaskView) = taskView.dismissTranslationX
}
+
private val DISMISS_TRANSLATION_Y: FloatProperty<TaskView> =
object : FloatProperty<TaskView>("dismissTranslationY") {
override fun setValue(taskView: TaskView, v: Float) {
@@ -1650,6 +1727,7 @@
override fun get(taskView: TaskView) = taskView.dismissTranslationY
}
+
private val TASK_OFFSET_TRANSLATION_X: FloatProperty<TaskView> =
object : FloatProperty<TaskView>("taskOffsetTranslationX") {
override fun setValue(taskView: TaskView, v: Float) {
@@ -1658,6 +1736,7 @@
override fun get(taskView: TaskView) = taskView.taskOffsetTranslationX
}
+
private val TASK_OFFSET_TRANSLATION_Y: FloatProperty<TaskView> =
object : FloatProperty<TaskView>("taskOffsetTranslationY") {
override fun setValue(taskView: TaskView, v: Float) {
@@ -1666,6 +1745,7 @@
override fun get(taskView: TaskView) = taskView.taskOffsetTranslationY
}
+
private val TASK_RESISTANCE_TRANSLATION_X: FloatProperty<TaskView> =
object : FloatProperty<TaskView>("taskResistanceTranslationX") {
override fun setValue(taskView: TaskView, v: Float) {
@@ -1674,6 +1754,7 @@
override fun get(taskView: TaskView) = taskView.taskResistanceTranslationX
}
+
private val TASK_RESISTANCE_TRANSLATION_Y: FloatProperty<TaskView> =
object : FloatProperty<TaskView>("taskResistanceTranslationY") {
override fun setValue(taskView: TaskView, v: Float) {
@@ -1682,6 +1763,7 @@
override fun get(taskView: TaskView) = taskView.taskResistanceTranslationY
}
+
@JvmField
val GRID_END_TRANSLATION_X: FloatProperty<TaskView> =
object : FloatProperty<TaskView>("gridEndTranslationX") {
@@ -1691,6 +1773,7 @@
override fun get(taskView: TaskView) = taskView.gridEndTranslationX
}
+
@JvmField
val DISMISS_SCALE: FloatProperty<TaskView> =
object : FloatProperty<TaskView>("dismissScale") {
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/animation/BubbleBarViewAnimatorTest.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/animation/BubbleBarViewAnimatorTest.kt
index 49e54fb..cc579ab 100644
--- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/animation/BubbleBarViewAnimatorTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/animation/BubbleBarViewAnimatorTest.kt
@@ -392,7 +392,8 @@
overflowView.setOverflow(BubbleBarOverflow(overflowView), bitmap)
bubbleBarView.addView(overflowView)
- val bubbleInfo = BubbleInfo("key", 0, null, null, 0, context.packageName, null, false)
+ val bubbleInfo =
+ BubbleInfo("key", 0, null, null, 0, context.packageName, null, null, false)
bubbleView =
inflater.inflate(R.layout.bubblebar_item_view, bubbleBarView, false) as BubbleView
bubble =
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/FakeRecentTasksDataSource.kt b/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/FakeRecentTasksDataSource.kt
new file mode 100644
index 0000000..eaeb513
--- /dev/null
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/FakeRecentTasksDataSource.kt
@@ -0,0 +1,33 @@
+/*
+ * 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.quickstep.recents.data
+
+import com.android.quickstep.util.GroupTask
+import java.util.function.Consumer
+
+class FakeRecentTasksDataSource : RecentTasksDataSource {
+ var taskList: List<GroupTask> = listOf()
+
+ override fun getTasks(callback: Consumer<List<GroupTask>>?): Int {
+ callback?.accept(taskList)
+ return 0
+ }
+
+ fun seedTasks(tasks: List<GroupTask>) {
+ taskList = tasks
+ }
+}
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/FakeTaskThumbnailDataSource.kt b/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/FakeTaskThumbnailDataSource.kt
new file mode 100644
index 0000000..b66b735
--- /dev/null
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/FakeTaskThumbnailDataSource.kt
@@ -0,0 +1,52 @@
+/*
+ * 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.quickstep.recents.data
+
+import android.graphics.Bitmap
+import com.android.launcher3.util.CancellableTask
+import com.android.quickstep.task.thumbnail.data.TaskThumbnailDataSource
+import com.android.systemui.shared.recents.model.Task
+import com.android.systemui.shared.recents.model.ThumbnailData
+import java.util.function.Consumer
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.whenever
+
+class FakeTaskThumbnailDataSource : TaskThumbnailDataSource {
+
+ val taskIdToBitmap: Map<Int, Bitmap> = (0..10).associateWith { mock() }
+ val taskIdToUpdatingTask: MutableMap<Int, () -> Unit> = mutableMapOf()
+ var shouldLoadSynchronously: Boolean = true
+
+ /** Retrieves and sets a thumbnail on [task] from [taskIdToBitmap]. */
+ override fun updateThumbnailInBackground(
+ task: Task,
+ callback: Consumer<ThumbnailData>
+ ): CancellableTask<ThumbnailData>? {
+ val thumbnailData = mock<ThumbnailData>()
+ whenever(thumbnailData.thumbnail).thenReturn(taskIdToBitmap[task.key.id])
+ val wrappedCallback = {
+ task.thumbnail = thumbnailData
+ callback.accept(thumbnailData)
+ }
+ if (shouldLoadSynchronously) {
+ wrappedCallback()
+ } else {
+ taskIdToUpdatingTask[task.key.id] = wrappedCallback
+ }
+ return null
+ }
+}
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/FakeTasksRepository.kt b/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/FakeTasksRepository.kt
new file mode 100644
index 0000000..e160627
--- /dev/null
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/FakeTasksRepository.kt
@@ -0,0 +1,47 @@
+/*
+ * 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.quickstep.recents.data
+
+import com.android.systemui.shared.recents.model.Task
+import com.android.systemui.shared.recents.model.ThumbnailData
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.map
+
+class FakeTasksRepository : RecentTasksRepository {
+ private var thumbnailDataMap: Map<Int, ThumbnailData> = emptyMap()
+ private var tasks: MutableStateFlow<List<Task>> = MutableStateFlow(emptyList())
+ private var visibleTasks: MutableStateFlow<List<Int>> = MutableStateFlow(emptyList())
+
+ override fun getAllTaskData(forceRefresh: Boolean): Flow<List<Task>> = tasks
+
+ override fun getTaskDataById(taskId: Int): Flow<Task?> =
+ getAllTaskData().map { taskList -> taskList.firstOrNull { it.key.id == taskId } }
+
+ override fun setVisibleTasks(visibleTaskIdList: List<Int>) {
+ visibleTasks.value = visibleTaskIdList
+ tasks.value = tasks.value.map { it.apply { thumbnail = thumbnailDataMap[it.key.id] } }
+ }
+
+ fun seedTasks(tasks: List<Task>) {
+ this.tasks.value = tasks
+ }
+
+ fun seedThumbnailData(thumbnailDataMap: Map<Int, ThumbnailData>) {
+ this.thumbnailDataMap = thumbnailDataMap
+ }
+}
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/TasksRepositoryTest.kt b/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/TasksRepositoryTest.kt
new file mode 100644
index 0000000..c28a85a
--- /dev/null
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/TasksRepositoryTest.kt
@@ -0,0 +1,150 @@
+/*
+ * 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.quickstep.recents.data
+
+import android.content.ComponentName
+import android.content.Intent
+import com.android.quickstep.TaskIconCache
+import com.android.quickstep.util.DesktopTask
+import com.android.quickstep.util.GroupTask
+import com.android.systemui.shared.recents.model.Task
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.drop
+import kotlinx.coroutines.flow.first
+import kotlinx.coroutines.flow.toList
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.mockito.kotlin.mock
+
+@OptIn(ExperimentalCoroutinesApi::class)
+class TasksRepositoryTest {
+ private val tasks = (0..5).map(::createTaskWithId)
+ private val defaultTaskList =
+ listOf(
+ GroupTask(tasks[0]),
+ GroupTask(tasks[1], tasks[2], null),
+ DesktopTask(tasks.subList(3, 6))
+ )
+ private val recentsModel = FakeRecentTasksDataSource()
+ private val taskThumbnailDataSource = FakeTaskThumbnailDataSource()
+ private val taskIconCache = mock<TaskIconCache>()
+
+ private val systemUnderTest =
+ TasksRepository(recentsModel, taskThumbnailDataSource, taskIconCache)
+
+ @Test
+ fun getAllTaskDataReturnsFlattenedListOfTasks() = runTest {
+ recentsModel.seedTasks(defaultTaskList)
+
+ assertThat(systemUnderTest.getAllTaskData(forceRefresh = true).first()).isEqualTo(tasks)
+ }
+
+ @Test
+ fun getTaskDataByIdReturnsSpecificTask() = runTest {
+ recentsModel.seedTasks(defaultTaskList)
+ systemUnderTest.getAllTaskData(forceRefresh = true)
+
+ assertThat(systemUnderTest.getTaskDataById(2).first()).isEqualTo(tasks[2])
+ }
+
+ @Test
+ fun setVisibleTasksPopulatesThumbnails() = runTest {
+ recentsModel.seedTasks(defaultTaskList)
+ val bitmap1 = taskThumbnailDataSource.taskIdToBitmap[1]
+ val bitmap2 = taskThumbnailDataSource.taskIdToBitmap[2]
+ systemUnderTest.getAllTaskData(forceRefresh = true)
+
+ systemUnderTest.setVisibleTasks(listOf(1, 2))
+
+ // .drop(1) to ignore initial null content before from thumbnail was loaded.
+ assertThat(systemUnderTest.getTaskDataById(1).drop(1).first()!!.thumbnail!!.thumbnail)
+ .isEqualTo(bitmap1)
+ assertThat(systemUnderTest.getTaskDataById(2).first()!!.thumbnail!!.thumbnail)
+ .isEqualTo(bitmap2)
+ }
+
+ @Test
+ fun changingVisibleTasksContainsAlreadyPopulatedThumbnails() = runTest {
+ recentsModel.seedTasks(defaultTaskList)
+ val bitmap2 = taskThumbnailDataSource.taskIdToBitmap[2]
+ systemUnderTest.getAllTaskData(forceRefresh = true)
+
+ systemUnderTest.setVisibleTasks(listOf(1, 2))
+
+ // .drop(1) to ignore initial null content before from thumbnail was loaded.
+ assertThat(systemUnderTest.getTaskDataById(2).drop(1).first()!!.thumbnail!!.thumbnail)
+ .isEqualTo(bitmap2)
+
+ // Prevent new loading of Bitmaps
+ taskThumbnailDataSource.shouldLoadSynchronously = false
+ systemUnderTest.setVisibleTasks(listOf(2, 3))
+
+ assertThat(systemUnderTest.getTaskDataById(2).first()!!.thumbnail!!.thumbnail)
+ .isEqualTo(bitmap2)
+ }
+
+ @Test
+ fun retrievedThumbnailsAreDiscardedWhenTaskBecomesInvisible() = runTest {
+ recentsModel.seedTasks(defaultTaskList)
+ val bitmap2 = taskThumbnailDataSource.taskIdToBitmap[2]
+ systemUnderTest.getAllTaskData(forceRefresh = true)
+
+ systemUnderTest.setVisibleTasks(listOf(1, 2))
+
+ // .drop(1) to ignore initial null content before from thumbnail was loaded.
+ assertThat(systemUnderTest.getTaskDataById(2).drop(1).first()!!.thumbnail!!.thumbnail)
+ .isEqualTo(bitmap2)
+
+ // Prevent new loading of Bitmaps
+ taskThumbnailDataSource.shouldLoadSynchronously = false
+ systemUnderTest.setVisibleTasks(listOf(0, 1))
+
+ assertThat(systemUnderTest.getTaskDataById(2).first()!!.thumbnail).isNull()
+ }
+
+ @Test
+ fun retrievedThumbnailsCauseEmissionOnTaskDataFlow() = runTest {
+ // Setup fakes
+ recentsModel.seedTasks(defaultTaskList)
+ val bitmap2 = taskThumbnailDataSource.taskIdToBitmap[2]
+ taskThumbnailDataSource.shouldLoadSynchronously = false
+
+ // Setup TasksRepository
+ systemUnderTest.getAllTaskData(forceRefresh = true)
+ systemUnderTest.setVisibleTasks(listOf(1, 2))
+
+ // Assert there is no bitmap in first emission
+ val taskFlow = systemUnderTest.getTaskDataById(2)
+ val taskFlowValuesList = mutableListOf<Task?>()
+ backgroundScope.launch(UnconfinedTestDispatcher(testScheduler)) {
+ taskFlow.toList(taskFlowValuesList)
+ }
+ assertThat(taskFlowValuesList[0]!!.thumbnail).isNull()
+
+ // Simulate bitmap loading after first emission
+ taskThumbnailDataSource.taskIdToUpdatingTask.getValue(2).invoke()
+
+ // Check for second emission
+ assertThat(taskFlowValuesList[1]!!.thumbnail!!.thumbnail).isEqualTo(bitmap2)
+ }
+
+ private fun createTaskWithId(taskId: Int) =
+ Task(Task.TaskKey(taskId, 0, Intent(), ComponentName("", ""), 0, 2000))
+}
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/task/thumbnail/TaskThumbnailViewModelTest.kt b/quickstep/tests/multivalentTests/src/com/android/quickstep/task/thumbnail/TaskThumbnailViewModelTest.kt
index efd7bec..3b8754c 100644
--- a/quickstep/tests/multivalentTests/src/com/android/quickstep/task/thumbnail/TaskThumbnailViewModelTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/task/thumbnail/TaskThumbnailViewModelTest.kt
@@ -16,33 +16,51 @@
package com.android.quickstep.task.thumbnail
+import android.content.ComponentName
+import android.content.Intent
+import android.graphics.Bitmap
+import android.graphics.Color
+import android.graphics.Rect
import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.quickstep.recents.data.FakeTasksRepository
import com.android.quickstep.recents.viewmodel.RecentsViewData
+import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.BackgroundOnly
+import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.LiveTile
+import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.Snapshot
+import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.Uninitialized
import com.android.quickstep.task.viewmodel.TaskViewData
import com.android.systemui.shared.recents.model.Task
+import com.android.systemui.shared.recents.model.ThumbnailData
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.whenever
@RunWith(AndroidJUnit4::class)
class TaskThumbnailViewModelTest {
private val recentsViewData = RecentsViewData()
private val taskViewData = TaskViewData()
- private val systemUnderTest = TaskThumbnailViewModel(recentsViewData, taskViewData)
+ private val tasksRepository = FakeTasksRepository()
+ private val systemUnderTest =
+ TaskThumbnailViewModel(recentsViewData, taskViewData, tasksRepository)
+
+ private val tasks = (0..5).map(::createTaskWithId)
@Test
fun initialStateIsUninitialized() = runTest {
- assertThat(systemUnderTest.uiState.first()).isEqualTo(TaskThumbnailUiState.Uninitialized)
+ assertThat(systemUnderTest.uiState.first()).isEqualTo(Uninitialized)
}
@Test
fun bindRunningTask_thenStateIs_LiveTile() = runTest {
- val taskThumbnail = TaskThumbnail(Task(), isRunning = true)
+ tasksRepository.seedTasks(tasks)
+ val taskThumbnail = TaskThumbnail(taskId = 1, isRunning = true)
systemUnderTest.bind(taskThumbnail)
- assertThat(systemUnderTest.uiState.first()).isEqualTo(TaskThumbnailUiState.LiveTile)
+ assertThat(systemUnderTest.uiState.first()).isEqualTo(LiveTile)
}
@Test
@@ -65,15 +83,96 @@
}
@Test
- fun bindRunningTaskThenStoppedTask_thenStateIs_Uninitialized() = runTest {
- // TODO(b/334825222): Change the expectation here when snapshot state is implemented
- val task = Task()
- val runningTask = TaskThumbnail(task, isRunning = true)
- val stoppedTask = TaskThumbnail(task, isRunning = false)
- systemUnderTest.bind(runningTask)
- assertThat(systemUnderTest.uiState.first()).isEqualTo(TaskThumbnailUiState.LiveTile)
+ fun bindRunningTaskThenStoppedTaskWithoutThumbnail_thenStateChangesToBackgroundOnly() =
+ runTest {
+ tasksRepository.seedTasks(tasks)
+ val runningTask = TaskThumbnail(taskId = 1, isRunning = true)
+ val stoppedTask = TaskThumbnail(taskId = 2, isRunning = false)
+ systemUnderTest.bind(runningTask)
+ assertThat(systemUnderTest.uiState.first()).isEqualTo(LiveTile)
+
+ systemUnderTest.bind(stoppedTask)
+ assertThat(systemUnderTest.uiState.first())
+ .isEqualTo(BackgroundOnly(backgroundColor = Color.rgb(2, 2, 2)))
+ }
+
+ @Test
+ fun bindStoppedTaskWithoutThumbnail_thenStateIs_BackgroundOnly_withAlphaRemoved() = runTest {
+ tasksRepository.seedTasks(tasks)
+ val stoppedTask = TaskThumbnail(taskId = 2, isRunning = false)
systemUnderTest.bind(stoppedTask)
- assertThat(systemUnderTest.uiState.first()).isEqualTo(TaskThumbnailUiState.Uninitialized)
+ assertThat(systemUnderTest.uiState.first())
+ .isEqualTo(BackgroundOnly(backgroundColor = Color.rgb(2, 2, 2)))
+ }
+
+ @Test
+ fun bindLockedTaskWithThumbnail_thenStateIs_BackgroundOnly() = runTest {
+ tasksRepository.seedThumbnailData(mapOf(2 to createThumbnailData()))
+ tasks[2].isLocked = true
+ tasksRepository.seedTasks(tasks)
+ val recentTask = TaskThumbnail(taskId = 2, isRunning = false)
+
+ systemUnderTest.bind(recentTask)
+ assertThat(systemUnderTest.uiState.first())
+ .isEqualTo(BackgroundOnly(backgroundColor = Color.rgb(2, 2, 2)))
+ }
+
+ @Test
+ fun bindStoppedTaskWithThumbnail_thenStateIs_Snapshot_withAlphaRemoved() = runTest {
+ val expectedThumbnailData = createThumbnailData()
+ tasksRepository.seedThumbnailData(mapOf(2 to expectedThumbnailData))
+ tasksRepository.seedTasks(tasks)
+ tasksRepository.setVisibleTasks(listOf(2))
+ val recentTask = TaskThumbnail(taskId = 2, isRunning = false)
+
+ systemUnderTest.bind(recentTask)
+ assertThat(systemUnderTest.uiState.first())
+ .isEqualTo(
+ Snapshot(
+ backgroundColor = Color.rgb(2, 2, 2),
+ bitmap = expectedThumbnailData.thumbnail!!,
+ drawnRect = Rect(0, 0, THUMBNAIL_WIDTH, THUMBNAIL_HEIGHT)
+ )
+ )
+ }
+
+ @Test
+ fun bindNonVisibleStoppedTask_whenMadeVisible_thenStateIsSnapshot() = runTest {
+ val expectedThumbnailData = createThumbnailData()
+ tasksRepository.seedThumbnailData(mapOf(2 to expectedThumbnailData))
+ tasksRepository.seedTasks(tasks)
+ val recentTask = TaskThumbnail(taskId = 2, isRunning = false)
+
+ systemUnderTest.bind(recentTask)
+ assertThat(systemUnderTest.uiState.first())
+ .isEqualTo(BackgroundOnly(backgroundColor = Color.rgb(2, 2, 2)))
+ tasksRepository.setVisibleTasks(listOf(2))
+ assertThat(systemUnderTest.uiState.first())
+ .isEqualTo(
+ Snapshot(
+ backgroundColor = Color.rgb(2, 2, 2),
+ bitmap = expectedThumbnailData.thumbnail!!,
+ drawnRect = Rect(0, 0, THUMBNAIL_WIDTH, THUMBNAIL_HEIGHT)
+ )
+ )
+ }
+
+ private fun createTaskWithId(taskId: Int) =
+ Task(Task.TaskKey(taskId, 0, Intent(), ComponentName("", ""), 0, 2000)).apply {
+ colorBackground = Color.argb(taskId, taskId, taskId, taskId)
+ }
+
+ private fun createThumbnailData(): ThumbnailData {
+ val bitmap = mock<Bitmap>()
+ whenever(bitmap.width).thenReturn(THUMBNAIL_WIDTH)
+ whenever(bitmap.height).thenReturn(THUMBNAIL_HEIGHT)
+
+ return ThumbnailData(thumbnail = bitmap)
+ }
+
+ companion object {
+ const val THUMBNAIL_WIDTH = 100
+ const val THUMBNAIL_HEIGHT = 200
}
}
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/util/SplitSelectStateControllerTest.kt b/quickstep/tests/multivalentTests/src/com/android/quickstep/util/SplitSelectStateControllerTest.kt
index 0de5f19..aa08ca4 100644
--- a/quickstep/tests/multivalentTests/src/com/android/quickstep/util/SplitSelectStateControllerTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/util/SplitSelectStateControllerTest.kt
@@ -31,7 +31,6 @@
import com.android.launcher3.model.data.ItemInfo
import com.android.launcher3.statehandlers.DepthController
import com.android.launcher3.statemanager.StateManager
-import com.android.launcher3.statemanager.StatefulActivity
import com.android.launcher3.util.ComponentKey
import com.android.launcher3.util.SplitConfigurationOptions
import com.android.quickstep.RecentsModel
@@ -121,7 +120,7 @@
// Capture callback from recentsModel#getTasks()
val consumer =
- argumentCaptor<Consumer<ArrayList<GroupTask>>> {
+ argumentCaptor<Consumer<List<GroupTask>>> {
splitSelectStateController.findLastActiveTasksAndRunCallback(
listOf(nonMatchingComponent),
false /* findExactPairMatch */,
@@ -174,7 +173,7 @@
// Capture callback from recentsModel#getTasks()
val consumer =
- argumentCaptor<Consumer<ArrayList<GroupTask>>> {
+ argumentCaptor<Consumer<List<GroupTask>>> {
splitSelectStateController.findLastActiveTasksAndRunCallback(
listOf(matchingComponent),
false /* findExactPairMatch */,
@@ -215,7 +214,7 @@
// Capture callback from recentsModel#getTasks()
val consumer =
- argumentCaptor<Consumer<ArrayList<GroupTask>>> {
+ argumentCaptor<Consumer<List<GroupTask>>> {
splitSelectStateController.findLastActiveTasksAndRunCallback(
listOf(nonPrimaryUserComponent),
false /* findExactPairMatch */,
@@ -271,7 +270,7 @@
// Capture callback from recentsModel#getTasks()
val consumer =
- argumentCaptor<Consumer<ArrayList<GroupTask>>> {
+ argumentCaptor<Consumer<List<GroupTask>>> {
splitSelectStateController.findLastActiveTasksAndRunCallback(
listOf(nonPrimaryUserComponent),
false /* findExactPairMatch */,
@@ -324,7 +323,7 @@
// Capture callback from recentsModel#getTasks()
val consumer =
- argumentCaptor<Consumer<ArrayList<GroupTask>>> {
+ argumentCaptor<Consumer<List<GroupTask>>> {
splitSelectStateController.findLastActiveTasksAndRunCallback(
listOf(matchingComponent),
false /* findExactPairMatch */,
@@ -378,7 +377,7 @@
// Capture callback from recentsModel#getTasks()
val consumer =
- argumentCaptor<Consumer<ArrayList<GroupTask>>> {
+ argumentCaptor<Consumer<List<GroupTask>>> {
splitSelectStateController.findLastActiveTasksAndRunCallback(
listOf(nonMatchingComponent, matchingComponent),
false /* findExactPairMatch */,
@@ -431,7 +430,7 @@
// Capture callback from recentsModel#getTasks()
val consumer =
- argumentCaptor<Consumer<ArrayList<GroupTask>>> {
+ argumentCaptor<Consumer<List<GroupTask>>> {
splitSelectStateController.findLastActiveTasksAndRunCallback(
listOf(matchingComponent, matchingComponent),
false /* findExactPairMatch */,
@@ -497,7 +496,7 @@
// Capture callback from recentsModel#getTasks()
val consumer =
- argumentCaptor<Consumer<ArrayList<GroupTask>>> {
+ argumentCaptor<Consumer<List<GroupTask>>> {
splitSelectStateController.findLastActiveTasksAndRunCallback(
listOf(matchingComponent, matchingComponent),
false /* findExactPairMatch */,
@@ -549,7 +548,7 @@
// Capture callback from recentsModel#getTasks()
val consumer =
- argumentCaptor<Consumer<ArrayList<GroupTask>>> {
+ argumentCaptor<Consumer<List<GroupTask>>> {
splitSelectStateController.findLastActiveTasksAndRunCallback(
listOf(matchingComponent2, matchingComponent),
true /* findExactPairMatch */,
diff --git a/quickstep/tests/multivalentTestsForDeviceless b/quickstep/tests/multivalentTestsForDeviceless
deleted file mode 120000
index fa0fabf..0000000
--- a/quickstep/tests/multivalentTestsForDeviceless
+++ /dev/null
@@ -1 +0,0 @@
-./multivalentTests
\ No newline at end of file
diff --git a/quickstep/tests/src/com/android/launcher3/taskbar/DesktopTaskbarRunningAppsControllerTest.kt b/quickstep/tests/src/com/android/launcher3/taskbar/DesktopTaskbarRunningAppsControllerTest.kt
index 4fafde8..5b56710 100644
--- a/quickstep/tests/src/com/android/launcher3/taskbar/DesktopTaskbarRunningAppsControllerTest.kt
+++ b/quickstep/tests/src/com/android/launcher3/taskbar/DesktopTaskbarRunningAppsControllerTest.kt
@@ -86,7 +86,8 @@
val newHotseatItems =
taskbarRunningAppsController.updateHotseatItemInfos(hotseatItems.toTypedArray())
- assertThat(newHotseatItems.map { it?.targetPackage }).isEqualTo(hotseatPackages)
+ assertThat(newHotseatItems.map { it?.targetPackage })
+ .containsExactlyElementsIn(hotseatPackages)
}
@Test
@@ -119,7 +120,8 @@
RUNNING_APP_PACKAGE_1,
RUNNING_APP_PACKAGE_2,
)
- assertThat(newHotseatItems.map { it?.targetPackage }).isEqualTo(expectedPackages)
+ assertThat(newHotseatItems.map { it?.targetPackage })
+ .containsExactlyElementsIn(expectedPackages)
}
@Test
@@ -144,7 +146,8 @@
RUNNING_APP_PACKAGE_1,
RUNNING_APP_PACKAGE_2,
)
- assertThat(newHotseatItems.map { it?.targetPackage }).isEqualTo(expectedPackages)
+ assertThat(newHotseatItems.map { it?.targetPackage })
+ .containsExactlyElementsIn(expectedPackages)
}
@Test
@@ -155,7 +158,8 @@
whenever(mockRecentsModel.runningTasks).thenReturn(runningTasks)
taskbarRunningAppsController.updateRunningApps()
- assertThat(taskbarRunningAppsController.runningApps).isEqualTo(emptySet<String>())
+ assertThat(taskbarRunningAppsController.runningApps).isEmpty()
+ assertThat(taskbarRunningAppsController.minimizedApps).isEmpty()
}
@Test
@@ -167,7 +171,28 @@
taskbarRunningAppsController.updateRunningApps()
assertThat(taskbarRunningAppsController.runningApps)
- .isEqualTo(setOf(RUNNING_APP_PACKAGE_1, RUNNING_APP_PACKAGE_2))
+ .containsExactly(RUNNING_APP_PACKAGE_1, RUNNING_APP_PACKAGE_2)
+ assertThat(taskbarRunningAppsController.minimizedApps).isEmpty()
+ }
+
+ @Test
+ fun getMinimizedApps_inDesktopMode_returnsAllAppsRunningAndInvisibleAppsMinimized() {
+ setInDesktopMode(true)
+ val runningTasks =
+ ArrayList(
+ listOf(
+ createDesktopTaskInfo(RUNNING_APP_PACKAGE_1) { isVisible = true },
+ createDesktopTaskInfo(RUNNING_APP_PACKAGE_2) { isVisible = true },
+ createDesktopTaskInfo(RUNNING_APP_PACKAGE_3) { isVisible = false },
+ )
+ )
+ whenever(mockRecentsModel.runningTasks).thenReturn(runningTasks)
+ taskbarRunningAppsController.updateRunningApps()
+
+ assertThat(taskbarRunningAppsController.runningApps)
+ .containsExactly(RUNNING_APP_PACKAGE_1, RUNNING_APP_PACKAGE_2, RUNNING_APP_PACKAGE_3)
+ assertThat(taskbarRunningAppsController.minimizedApps)
+ .containsExactly(RUNNING_APP_PACKAGE_3)
}
private fun createHotseatItemsFromPackageNames(packageNames: List<String>): List<ItemInfo> {
@@ -180,11 +205,15 @@
return ArrayList(packageNames.map { createDesktopTaskInfo(packageName = it) })
}
- private fun createDesktopTaskInfo(packageName: String): RunningTaskInfo {
+ private fun createDesktopTaskInfo(
+ packageName: String,
+ init: RunningTaskInfo.() -> Unit = { isVisible = true },
+ ): RunningTaskInfo {
return RunningTaskInfo().apply {
taskId = nextTaskId++
configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FREEFORM
realActivity = ComponentName(packageName, "TestActivity")
+ init()
}
}
diff --git a/quickstep/tests/src/com/android/quickstep/DesktopSystemShortcutTest.kt b/quickstep/tests/src/com/android/quickstep/DesktopSystemShortcutTest.kt
index 36f2ccf..50b5df1 100644
--- a/quickstep/tests/src/com/android/quickstep/DesktopSystemShortcutTest.kt
+++ b/quickstep/tests/src/com/android/quickstep/DesktopSystemShortcutTest.kt
@@ -38,6 +38,7 @@
import com.android.systemui.shared.recents.model.Task
import com.android.systemui.shared.recents.model.Task.TaskKey
import com.android.window.flags.Flags
+import com.android.wm.shell.common.desktopmode.DesktopModeTransitionSource
import com.android.wm.shell.shared.DesktopModeStatus
import com.google.common.truth.Truth.assertThat
import org.junit.After
@@ -152,8 +153,8 @@
whenever(launcher.statsLogManager).thenReturn(statsLogManager)
whenever(statsLogManager.logger()).thenReturn(statsLogger)
whenever(statsLogger.withItemInfo(any())).thenReturn(statsLogger)
- whenever(recentsView.moveTaskToDesktop(any(), any())).thenAnswer {
- val successCallback = it.getArgument<Runnable>(1)
+ whenever(recentsView.moveTaskToDesktop(any(), any(), any())).thenAnswer {
+ val successCallback = it.getArgument<Runnable>(2)
successCallback.run()
}
doReturn(workspaceItemInfo).whenever(taskContainer).itemInfo
@@ -169,7 +170,12 @@
val allTypesExceptRebindSafe =
AbstractFloatingView.TYPE_ALL and AbstractFloatingView.TYPE_REBIND_SAFE.inv()
verify(abstractFloatingViewHelper).closeOpenViews(launcher, true, allTypesExceptRebindSafe)
- verify(recentsView).moveTaskToDesktop(eq(taskContainer), any())
+ verify(recentsView)
+ .moveTaskToDesktop(
+ eq(taskContainer),
+ eq(DesktopModeTransitionSource.APP_FROM_OVERVIEW),
+ any()
+ )
verify(statsLogger).withItemInfo(workspaceItemInfo)
verify(statsLogger).log(LauncherEvent.LAUNCHER_SYSTEM_SHORTCUT_DESKTOP_TAP)
}
diff --git a/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java b/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java
index 094fd4c..4459ed6 100644
--- a/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java
+++ b/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java
@@ -162,8 +162,8 @@
final Context targetContext = getInstrumentation().getTargetContext();
final DisplayController.DisplayInfoChangeListener listener =
(context, info, flags) -> {
- if (LauncherInstrumentation.getNavigationModel(info.navigationMode.resValue)
- == expectedMode) {
+ if (LauncherInstrumentation.getNavigationModel(
+ info.getNavigationMode().resValue) == expectedMode) {
latch.countDown();
}
};
diff --git a/quickstep/tests/src/com/android/quickstep/RecentsAnimationDeviceStateTest.kt b/quickstep/tests/src/com/android/quickstep/RecentsAnimationDeviceStateTest.kt
index 2916952..5157c71 100644
--- a/quickstep/tests/src/com/android/quickstep/RecentsAnimationDeviceStateTest.kt
+++ b/quickstep/tests/src/com/android/quickstep/RecentsAnimationDeviceStateTest.kt
@@ -18,6 +18,7 @@
import org.mockito.Mockito.reset
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.doReturn
import org.mockito.kotlin.verifyZeroInteractions
import org.mockito.kotlin.whenever
@@ -79,7 +80,7 @@
@Test
fun onDisplayInfoChanged_noButton_registerExclusionListener() {
- whenever(windowManagerProxy.getNavigationMode(context)).thenReturn(NavigationMode.NO_BUTTON)
+ doReturn(NavigationMode.NO_BUTTON).whenever(info).getNavigationMode()
underTest.onDisplayInfoChanged(context, info, CHANGE_ROTATION or CHANGE_NAVIGATION_MODE)
diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsSplitscreen.java b/quickstep/tests/src/com/android/quickstep/TaplTestsSplitscreen.java
index bfd7bdb..6be082a 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplTestsSplitscreen.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplTestsSplitscreen.java
@@ -129,8 +129,6 @@
overview.getCurrentTask()
.tapMenu()
.hasMenuItem("Save app pair"));
- } else {
- overview.getOverviewGroupActions().assertHasAction("Save app pair");
}
}
diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsTrackpad.java b/quickstep/tests/src/com/android/quickstep/TaplTestsTrackpad.java
index e4f8b6c..2c23f86 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplTestsTrackpad.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplTestsTrackpad.java
@@ -37,6 +37,7 @@
import com.android.quickstep.NavigationModeSwitchRule.NavigationModeSwitch;
import org.junit.After;
+import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -47,8 +48,14 @@
private static final String READ_DEVICE_CONFIG_PERMISSION =
"android.permission.READ_DEVICE_CONFIG";
+ @Before
+ public void setup() {
+ mLauncher.injectFakeTrackpad();
+ }
+
@After
public void tearDown() {
+ mLauncher.ejectFakeTrackpad();
mLauncher.setTrackpadGestureType(TrackpadGestureType.NONE);
}
@@ -110,8 +117,8 @@
}
@Test
- @NavigationModeSwitch
@PortraitLandscape
+ @NavigationModeSwitch
public void testQuickSwitchFromHome() throws Exception {
assumeTrue(mLauncher.isTablet());
diff --git a/res/layout/private_space_header.xml b/res/layout/private_space_header.xml
index cefe394..9c0f129 100644
--- a/res/layout/private_space_header.xml
+++ b/res/layout/private_space_header.xml
@@ -37,7 +37,7 @@
android:gravity="center_vertical"
android:layout_alignParentEnd="true"
android:animateLayoutChanges="false">
- <ImageButton
+ <com.android.launcher3.allapps.PrivateSpaceSettingsButton
android:id="@+id/ps_settings_button"
android:layout_width="@dimen/ps_header_image_height"
android:layout_height="@dimen/ps_header_image_height"
diff --git a/res/values-af/strings.xml b/res/values-af/strings.xml
index b936ad0..e18d9cd 100644
--- a/res/values-af/strings.xml
+++ b/res/values-af/strings.xml
@@ -29,6 +29,8 @@
<string name="home_screen" msgid="5629429142036709174">"Tuis"</string>
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"Verdeelde skerm"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"Programinligting vir %1$s"</string>
+ <!-- no translation found for split_app_usage_settings (7214375263347964093) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"Stoor apppaar"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"Hierdie apppaar word nie op hierdie toestel gesteun nie"</string>
@@ -84,6 +86,7 @@
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Deïnstalleer"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"Appinligting"</string>
<string name="install_private_system_shortcut_label" msgid="1616889277073184841">"Installeer privaat"</string>
+ <string name="uninstall_private_system_shortcut_label" msgid="8423460530441627982">"Deïnstalleer app"</string>
<string name="install_drop_target_label" msgid="2539096853673231757">"Installeer"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Moenie voorstel nie"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Vasspeldvoorspelling"</string>
diff --git a/res/values-am/strings.xml b/res/values-am/strings.xml
index 19d4604..39dbfef 100644
--- a/res/values-am/strings.xml
+++ b/res/values-am/strings.xml
@@ -29,6 +29,8 @@
<string name="home_screen" msgid="5629429142036709174">"መነሻ"</string>
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"የተከፈለ ማያ ገፅ"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"የመተግበሪያ መረጃ ለ%1$s"</string>
+ <!-- no translation found for split_app_usage_settings (7214375263347964093) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"የመተግበሪያ ጥምረትን ያስቀምጡ"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"ይህ የመተግበሪያ ጥምረት በዚህ መሣሪያ ላይ አይደገፍም"</string>
@@ -84,6 +86,7 @@
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"አራግፍ"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"የመተግበሪያ መረጃ"</string>
<string name="install_private_system_shortcut_label" msgid="1616889277073184841">"በግል ይጫኑ"</string>
+ <string name="uninstall_private_system_shortcut_label" msgid="8423460530441627982">"መተግበሪያን አራግፍ"</string>
<string name="install_drop_target_label" msgid="2539096853673231757">"ጫን"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"መተግበሪያውን አይጠቁሙ"</string>
<string name="pin_prediction" msgid="4196423321649756498">"የፒን ግምት"</string>
diff --git a/res/values-ar/strings.xml b/res/values-ar/strings.xml
index 500fe61..1143042 100644
--- a/res/values-ar/strings.xml
+++ b/res/values-ar/strings.xml
@@ -29,6 +29,8 @@
<string name="home_screen" msgid="5629429142036709174">"الشاشة الرئيسية"</string>
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"تقسيم الشاشة"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"معلومات تطبيق %1$s"</string>
+ <!-- no translation found for split_app_usage_settings (7214375263347964093) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"حفظ استخدام التطبيقين معًا"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"لا يمكن استخدام هذين التطبيقَين في الوقت نفسه على هذا الجهاز"</string>
@@ -84,6 +86,7 @@
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"إلغاء التثبيت"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"معلومات عن التطبيق"</string>
<string name="install_private_system_shortcut_label" msgid="1616889277073184841">"تثبيت في مساحة خاصّة"</string>
+ <string name="uninstall_private_system_shortcut_label" msgid="8423460530441627982">"إلغاء تثبيت التطبيق"</string>
<string name="install_drop_target_label" msgid="2539096853673231757">"تثبيت"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"عدم اقتراح التطبيق"</string>
<string name="pin_prediction" msgid="4196423321649756498">"تثبيت التطبيق المتوقّع"</string>
@@ -183,7 +186,7 @@
<string name="developer_options_filter_hint" msgid="5896817443635989056">"فلتر"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"تعذَّر <xliff:g id="WHAT">%1$s</xliff:g>."</string>
<string name="private_space_label" msgid="2359721649407947001">"مساحة خاصة"</string>
- <string name="private_space_secondary_label" msgid="9203933341714508907">"النقر للإعداد أو الفتح"</string>
+ <string name="private_space_secondary_label" msgid="9203933341714508907">"انقر للإعداد أو الفتح"</string>
<string name="ps_container_title" msgid="4391796149519594205">"المساحة الخاصة"</string>
<string name="ps_container_settings" msgid="6059734123353320479">"إعدادات المساحة الخاصة"</string>
<string name="ps_container_unlock_button_content_description" msgid="9181551784092204234">"المساحة الخاصة غير مُقفلة."</string>
diff --git a/res/values-as/strings.xml b/res/values-as/strings.xml
index cb490b5..ed4bb97 100644
--- a/res/values-as/strings.xml
+++ b/res/values-as/strings.xml
@@ -29,6 +29,8 @@
<string name="home_screen" msgid="5629429142036709174">"গৃহ স্ক্ৰীন"</string>
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"বিভাজিত স্ক্ৰীন"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"%1$sৰ বাবে এপৰ তথ্য"</string>
+ <!-- no translation found for split_app_usage_settings (7214375263347964093) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"এপৰ পেয়াৰ ছেভ কৰক"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"এই ডিভাইচটোত এই এপ্ পেয়াৰ কৰাৰ সুবিধাটো সমৰ্থিত নহয়"</string>
@@ -84,6 +86,7 @@
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"আনইনষ্টল কৰক"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"এপ্ সম্পৰ্কীয় তথ্য"</string>
<string name="install_private_system_shortcut_label" msgid="1616889277073184841">"গোপনে ইনষ্টল কৰক"</string>
+ <string name="uninstall_private_system_shortcut_label" msgid="8423460530441627982">"এপ্ আনইনষ্টল কৰক"</string>
<string name="install_drop_target_label" msgid="2539096853673231757">"ইনষ্টল কৰক"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"পৰামৰ্শ নিদিব"</string>
<string name="pin_prediction" msgid="4196423321649756498">"পূৰ্বানুমান কৰা এপ্টো পিন কৰক"</string>
diff --git a/res/values-az/strings.xml b/res/values-az/strings.xml
index c6cc267..e827342 100644
--- a/res/values-az/strings.xml
+++ b/res/values-az/strings.xml
@@ -29,6 +29,8 @@
<string name="home_screen" msgid="5629429142036709174">"Əsas səhifə"</string>
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"Ekran bölünməsi"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"%1$s ilə bağlı tətbiq məlumatı"</string>
+ <!-- no translation found for split_app_usage_settings (7214375263347964093) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"Tətbiq cütünü saxlayın"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"Bu tətbiq cütü bu cihazda dəstəklənmir"</string>
@@ -84,6 +86,7 @@
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Sistemdən sil"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"Tətbiq haqqında"</string>
<string name="install_private_system_shortcut_label" msgid="1616889277073184841">"Məxfi quraşdırın"</string>
+ <string name="uninstall_private_system_shortcut_label" msgid="8423460530441627982">"Tətbiqi sistemdən silin"</string>
<string name="install_drop_target_label" msgid="2539096853673231757">"Quraşdırın"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Tətbiq təklif olunmasın"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Proqnozlaşdırılan tətbiqi bərkidin"</string>
diff --git a/res/values-b+sr+Latn/strings.xml b/res/values-b+sr+Latn/strings.xml
index 830de4e..30ba09c 100644
--- a/res/values-b+sr+Latn/strings.xml
+++ b/res/values-b+sr+Latn/strings.xml
@@ -29,6 +29,7 @@
<string name="home_screen" msgid="5629429142036709174">"Početni ekran"</string>
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"Podeljeni ekran"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"Informacije o aplikaciji za: %1$s"</string>
+ <string name="split_app_usage_settings" msgid="7214375263347964093">"Podešavanja potrošnje za %1$s"</string>
<string name="save_app_pair" msgid="5647523853662686243">"Sačuvaj par aplikacija"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"Ovaj par aplikacija nije podržan na ovom uređaju"</string>
@@ -84,6 +85,7 @@
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Deinstaliraj"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"Podaci o aplikaciji"</string>
<string name="install_private_system_shortcut_label" msgid="1616889277073184841">"Instaliraj na privatni"</string>
+ <string name="uninstall_private_system_shortcut_label" msgid="8423460530441627982">"Deinstalirajte aplikaciju"</string>
<string name="install_drop_target_label" msgid="2539096853673231757">"Instaliraj"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Ne predlaži aplikaciju"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Zakači predviđanje"</string>
diff --git a/res/values-be/strings.xml b/res/values-be/strings.xml
index 1a3a870..3ece170 100644
--- a/res/values-be/strings.xml
+++ b/res/values-be/strings.xml
@@ -29,6 +29,8 @@
<string name="home_screen" msgid="5629429142036709174">"Галоўны экран"</string>
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"Падзелены экран"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"Інфармацыя пра праграму для: %1$s"</string>
+ <!-- no translation found for split_app_usage_settings (7214375263347964093) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"Захаваць спалучэнне праграм"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"Дадзенае спалучэнне праграм не падтрымліваецца на гэтай прыладзе"</string>
@@ -84,6 +86,7 @@
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Дэінсталяваць"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"Звесткі аб праграме"</string>
<string name="install_private_system_shortcut_label" msgid="1616889277073184841">"Усталяваць прыватна"</string>
+ <string name="uninstall_private_system_shortcut_label" msgid="8423460530441627982">"Выдаліць праграму"</string>
<string name="install_drop_target_label" msgid="2539096853673231757">"Усталяваць"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Не прапаноўваць праграму"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Замацаваць прапанаваную праграму"</string>
diff --git a/res/values-bg/strings.xml b/res/values-bg/strings.xml
index 5030e7c..4d23773 100644
--- a/res/values-bg/strings.xml
+++ b/res/values-bg/strings.xml
@@ -29,6 +29,8 @@
<string name="home_screen" msgid="5629429142036709174">"Начален екран"</string>
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"Разделен екран"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"Информация за приложението за %1$s"</string>
+ <!-- no translation found for split_app_usage_settings (7214375263347964093) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"Запазване на двойката приложения"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"Тази двойка приложения не се поддържа на устройството"</string>
@@ -84,6 +86,7 @@
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Деинсталиране"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"Информация за прилож."</string>
<string name="install_private_system_shortcut_label" msgid="1616889277073184841">"Инстал. в частно простр."</string>
+ <string name="uninstall_private_system_shortcut_label" msgid="8423460530441627982">"Деинсталиране на приложението"</string>
<string name="install_drop_target_label" msgid="2539096853673231757">"Инсталиране"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Без предлагане на приложение"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Фиксиране на предвиждането"</string>
@@ -182,14 +185,14 @@
<string name="work_apps_enable_btn_text" msgid="1736198302467317371">"Отмяна на паузата"</string>
<string name="developer_options_filter_hint" msgid="5896817443635989056">"Филтър"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Неуспешно: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
- <string name="private_space_label" msgid="2359721649407947001">"Лично пространство"</string>
+ <string name="private_space_label" msgid="2359721649407947001">"Частно пространство"</string>
<string name="private_space_secondary_label" msgid="9203933341714508907">"Докоснете за настройване или отваряне"</string>
<string name="ps_container_title" msgid="4391796149519594205">"Лично"</string>
- <string name="ps_container_settings" msgid="6059734123353320479">"Настройки за личното пространство"</string>
+ <string name="ps_container_settings" msgid="6059734123353320479">"Настройки за частното пространство"</string>
<string name="ps_container_unlock_button_content_description" msgid="9181551784092204234">"Частно, отключено."</string>
<string name="ps_container_lock_button_content_description" msgid="5961993384382649530">"Частно, заключено."</string>
<string name="ps_container_lock_title" msgid="2640257399982364682">"Заключване"</string>
- <string name="ps_container_transition" msgid="8667331812048014412">"Преминаване към личното пространство"</string>
+ <string name="ps_container_transition" msgid="8667331812048014412">"Преминаване към частното пространство"</string>
<string name="ps_add_button_label" msgid="8127988716897128773">"Инсталиране"</string>
<string name="ps_add_button_content_description" msgid="3254274107740952556">"Инсталиране на приложения в частно пространство"</string>
<string name="bubble_bar_overflow_description" msgid="7410995531938041192">"Препълване"</string>
diff --git a/res/values-bn/strings.xml b/res/values-bn/strings.xml
index 34edae0..83f4ba1 100644
--- a/res/values-bn/strings.xml
+++ b/res/values-bn/strings.xml
@@ -29,6 +29,8 @@
<string name="home_screen" msgid="5629429142036709174">"হোম"</string>
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"স্প্লিট স্ক্রিন"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"%1$s-এর জন্য অ্যাপ সম্পর্কিত তথ্য"</string>
+ <!-- no translation found for split_app_usage_settings (7214375263347964093) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"অ্যাপ পেয়ার সেভ করুন"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"এই ডিভাইসে এই অ্যাপ পেয়ারটি কাজ করে না"</string>
@@ -84,6 +86,7 @@
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"আনইনস্টল করুন"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"অ্যাপের তথ্য"</string>
<string name="install_private_system_shortcut_label" msgid="1616889277073184841">"প্রাইভেট প্রোফাইলে ইনস্টল করুন"</string>
+ <string name="uninstall_private_system_shortcut_label" msgid="8423460530441627982">"অ্যাপ আনইনস্টল করুন"</string>
<string name="install_drop_target_label" msgid="2539096853673231757">"ইনস্টল করুন"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"অ্যাপ সাজেস্ট করবেন না"</string>
<string name="pin_prediction" msgid="4196423321649756498">"আপনার প্রয়োজন হতে পারে এমন অ্যাপ পিন করুন"</string>
diff --git a/res/values-bs/strings.xml b/res/values-bs/strings.xml
index 1ad9a5c..2c5e22a 100644
--- a/res/values-bs/strings.xml
+++ b/res/values-bs/strings.xml
@@ -29,6 +29,8 @@
<string name="home_screen" msgid="5629429142036709174">"Početni ekran"</string>
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"Podijeljeni ekran"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"Informacije o aplikaciji %1$s"</string>
+ <!-- no translation found for split_app_usage_settings (7214375263347964093) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"Sačuvaj par aplikacija"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"Par aplikacija nije podržan na uređaju"</string>
@@ -84,6 +86,7 @@
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Deinstaliraj"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"Inform. o aplikaciji"</string>
<string name="install_private_system_shortcut_label" msgid="1616889277073184841">"Instaliraj u Privatno"</string>
+ <string name="uninstall_private_system_shortcut_label" msgid="8423460530441627982">"Deinstalirajte aplikaciju"</string>
<string name="install_drop_target_label" msgid="2539096853673231757">"Instaliraj"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Ne predlaži aplikaciju"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Zakači predviđanje"</string>
diff --git a/res/values-ca/strings.xml b/res/values-ca/strings.xml
index 33f75b5..f275e49 100644
--- a/res/values-ca/strings.xml
+++ b/res/values-ca/strings.xml
@@ -29,6 +29,8 @@
<string name="home_screen" msgid="5629429142036709174">"Inici"</string>
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"Pantalla dividida"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"Informació de l\'aplicació %1$s"</string>
+ <!-- no translation found for split_app_usage_settings (7214375263347964093) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"Desa la parella d\'aplicacions"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"Aquesta parella d\'aplicacions no s\'admet en aquest dispositiu"</string>
@@ -84,6 +86,7 @@
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Desinstal·la"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"Informació de l\'aplicació"</string>
<string name="install_private_system_shortcut_label" msgid="1616889277073184841">"Instal·la en privat"</string>
+ <string name="uninstall_private_system_shortcut_label" msgid="8423460530441627982">"Desinstal·la l\'aplicació"</string>
<string name="install_drop_target_label" msgid="2539096853673231757">"Instal·la"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"No suggereixis l\'aplicació"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Fixa la predicció"</string>
diff --git a/res/values-cs/strings.xml b/res/values-cs/strings.xml
index 6ac1d57..c499a9a 100644
--- a/res/values-cs/strings.xml
+++ b/res/values-cs/strings.xml
@@ -29,6 +29,8 @@
<string name="home_screen" msgid="5629429142036709174">"Domů"</string>
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"Rozdělit obrazovku"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"Informace o aplikaci %1$s"</string>
+ <!-- no translation found for split_app_usage_settings (7214375263347964093) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"Uložit dvojici aplikací"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"Tento pár aplikací není na tomto zařízení podporován"</string>
@@ -84,6 +86,7 @@
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Odinstalovat"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"O aplikaci"</string>
<string name="install_private_system_shortcut_label" msgid="1616889277073184841">"Instalovat soukromě"</string>
+ <string name="uninstall_private_system_shortcut_label" msgid="8423460530441627982">"Odinstalovat aplikaci"</string>
<string name="install_drop_target_label" msgid="2539096853673231757">"Nainstalovat"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Nenavrhovat aplikaci"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Připnout předpověď"</string>
diff --git a/res/values-da/strings.xml b/res/values-da/strings.xml
index bf09aeb..f2c08fa 100644
--- a/res/values-da/strings.xml
+++ b/res/values-da/strings.xml
@@ -29,6 +29,8 @@
<string name="home_screen" msgid="5629429142036709174">"Startskærm"</string>
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"Opdel skærm"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"Appinfo for %1$s"</string>
+ <!-- no translation found for split_app_usage_settings (7214375263347964093) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"Gem appsammenknytning"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"Denne appsammenknytning understøttes ikke på enheden"</string>
@@ -84,6 +86,7 @@
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Afinstaller"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"Appinfo"</string>
<string name="install_private_system_shortcut_label" msgid="1616889277073184841">"Installer (privat)"</string>
+ <string name="uninstall_private_system_shortcut_label" msgid="8423460530441627982">"Afinstaller appen"</string>
<string name="install_drop_target_label" msgid="2539096853673231757">"Installer"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Foreslå ikke en app"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Fastgør forslaget"</string>
diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml
index 834100b..f5ecf59 100644
--- a/res/values-de/strings.xml
+++ b/res/values-de/strings.xml
@@ -29,6 +29,8 @@
<string name="home_screen" msgid="5629429142036709174">"Startbildschirm"</string>
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"Splitscreen"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"App-Info für %1$s"</string>
+ <!-- no translation found for split_app_usage_settings (7214375263347964093) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"App-Paar speichern"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"Dieses App-Paar wird auf diesem Gerät nicht unterstützt"</string>
@@ -84,6 +86,7 @@
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Deinstallieren"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"App-Info"</string>
<string name="install_private_system_shortcut_label" msgid="1616889277073184841">"Vertraul. installieren"</string>
+ <string name="uninstall_private_system_shortcut_label" msgid="8423460530441627982">"App deinstallieren"</string>
<string name="install_drop_target_label" msgid="2539096853673231757">"Installieren"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"App nicht vorschlagen"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Vorgeschlagene App fixieren"</string>
diff --git a/res/values-el/strings.xml b/res/values-el/strings.xml
index f0496b5..1c4700a 100644
--- a/res/values-el/strings.xml
+++ b/res/values-el/strings.xml
@@ -29,6 +29,8 @@
<string name="home_screen" msgid="5629429142036709174">"Αρχική οθόνη"</string>
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"Διαχωρισμός οθόνης"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"Πληροφορίες εφαρμογής για %1$s"</string>
+ <!-- no translation found for split_app_usage_settings (7214375263347964093) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"Αποθήκευση ζεύγους εφαρμογών"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"Αυτό το ζεύγος εφαρμογών δεν υποστηρίζεται σε αυτή τη συσκευή"</string>
@@ -84,6 +86,7 @@
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Απεγκατάσταση"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"Πληροφ. εφαρμογής"</string>
<string name="install_private_system_shortcut_label" msgid="1616889277073184841">"Εγκατ. στο απόρρητο"</string>
+ <string name="uninstall_private_system_shortcut_label" msgid="8423460530441627982">"Απεγκατάσταση εφαρμογής"</string>
<string name="install_drop_target_label" msgid="2539096853673231757">"Εγκατάσταση"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Να μην προτείνεται"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Καρφίτσωμα πρόβλεψης"</string>
diff --git a/res/values-en-rAU/strings.xml b/res/values-en-rAU/strings.xml
index 83f1977..27bcb65 100644
--- a/res/values-en-rAU/strings.xml
+++ b/res/values-en-rAU/strings.xml
@@ -29,6 +29,8 @@
<string name="home_screen" msgid="5629429142036709174">"Home"</string>
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"Split screen"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"App info for %1$s"</string>
+ <!-- no translation found for split_app_usage_settings (7214375263347964093) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"Save app pair"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"This app pair isn\'t supported on this device"</string>
@@ -84,6 +86,7 @@
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Uninstall"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"App info"</string>
<string name="install_private_system_shortcut_label" msgid="1616889277073184841">"Install in private"</string>
+ <string name="uninstall_private_system_shortcut_label" msgid="8423460530441627982">"Uninstall app"</string>
<string name="install_drop_target_label" msgid="2539096853673231757">"Install"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Don\'t suggest app"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Pin prediction"</string>
diff --git a/res/values-en-rCA/strings.xml b/res/values-en-rCA/strings.xml
index 4bf147c..f672a82 100644
--- a/res/values-en-rCA/strings.xml
+++ b/res/values-en-rCA/strings.xml
@@ -29,6 +29,7 @@
<string name="home_screen" msgid="5629429142036709174">"Home"</string>
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"Split screen"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"App info for %1$s"</string>
+ <string name="split_app_usage_settings" msgid="7214375263347964093">"Usage settings for %1$s"</string>
<string name="save_app_pair" msgid="5647523853662686243">"Save app pair"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"This app pair isn\'t supported on this device"</string>
@@ -84,6 +85,7 @@
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Uninstall"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"App info"</string>
<string name="install_private_system_shortcut_label" msgid="1616889277073184841">"Install in private"</string>
+ <string name="uninstall_private_system_shortcut_label" msgid="8423460530441627982">"Uninstall app"</string>
<string name="install_drop_target_label" msgid="2539096853673231757">"Install"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Don\'t suggest app"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Pin Prediction"</string>
diff --git a/res/values-en-rGB/strings.xml b/res/values-en-rGB/strings.xml
index 83f1977..27bcb65 100644
--- a/res/values-en-rGB/strings.xml
+++ b/res/values-en-rGB/strings.xml
@@ -29,6 +29,8 @@
<string name="home_screen" msgid="5629429142036709174">"Home"</string>
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"Split screen"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"App info for %1$s"</string>
+ <!-- no translation found for split_app_usage_settings (7214375263347964093) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"Save app pair"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"This app pair isn\'t supported on this device"</string>
@@ -84,6 +86,7 @@
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Uninstall"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"App info"</string>
<string name="install_private_system_shortcut_label" msgid="1616889277073184841">"Install in private"</string>
+ <string name="uninstall_private_system_shortcut_label" msgid="8423460530441627982">"Uninstall app"</string>
<string name="install_drop_target_label" msgid="2539096853673231757">"Install"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Don\'t suggest app"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Pin prediction"</string>
diff --git a/res/values-en-rIN/strings.xml b/res/values-en-rIN/strings.xml
index 83f1977..27bcb65 100644
--- a/res/values-en-rIN/strings.xml
+++ b/res/values-en-rIN/strings.xml
@@ -29,6 +29,8 @@
<string name="home_screen" msgid="5629429142036709174">"Home"</string>
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"Split screen"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"App info for %1$s"</string>
+ <!-- no translation found for split_app_usage_settings (7214375263347964093) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"Save app pair"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"This app pair isn\'t supported on this device"</string>
@@ -84,6 +86,7 @@
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Uninstall"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"App info"</string>
<string name="install_private_system_shortcut_label" msgid="1616889277073184841">"Install in private"</string>
+ <string name="uninstall_private_system_shortcut_label" msgid="8423460530441627982">"Uninstall app"</string>
<string name="install_drop_target_label" msgid="2539096853673231757">"Install"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Don\'t suggest app"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Pin prediction"</string>
diff --git a/res/values-en-rXC/strings.xml b/res/values-en-rXC/strings.xml
index 24cc5ac..706c65c 100644
--- a/res/values-en-rXC/strings.xml
+++ b/res/values-en-rXC/strings.xml
@@ -29,6 +29,7 @@
<string name="home_screen" msgid="5629429142036709174">"Home"</string>
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"Split screen"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"App info for %1$s"</string>
+ <string name="split_app_usage_settings" msgid="7214375263347964093">"Usage settings for %1$s"</string>
<string name="save_app_pair" msgid="5647523853662686243">"Save app pair"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"This app pair isn\'t supported on this device"</string>
@@ -84,6 +85,7 @@
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Uninstall"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"App info"</string>
<string name="install_private_system_shortcut_label" msgid="1616889277073184841">"Install in private"</string>
+ <string name="uninstall_private_system_shortcut_label" msgid="8423460530441627982">"Uninstall app"</string>
<string name="install_drop_target_label" msgid="2539096853673231757">"Install"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Don\'t suggest app"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Pin Prediction"</string>
diff --git a/res/values-es-rUS/strings.xml b/res/values-es-rUS/strings.xml
index 2e6d840..3ba7ece 100644
--- a/res/values-es-rUS/strings.xml
+++ b/res/values-es-rUS/strings.xml
@@ -29,6 +29,8 @@
<string name="home_screen" msgid="5629429142036709174">"Pantalla principal"</string>
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"Pantalla dividida"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"Información de la app de %1$s"</string>
+ <!-- no translation found for split_app_usage_settings (7214375263347964093) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"Guardar vinculación"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"No se admite esta vinculación de apps en este dispositivo"</string>
@@ -58,7 +60,7 @@
<string name="widgets_full_sheet_cancel_button_description" msgid="5766167035728653605">"Borra el texto del cuadro de búsqueda"</string>
<string name="no_widgets_available" msgid="4337693382501046170">"Los widgets y accesos directos no están disponibles"</string>
<string name="no_search_results" msgid="3787956167293097509">"No se encontraron widgets ni accesos directos"</string>
- <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"Personales"</string>
+ <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"Personal"</string>
<string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Trabajo"</string>
<string name="widget_category_conversations" msgid="8894438636213590446">"Conversaciones"</string>
<string name="widget_category_note_taking" msgid="3469689394504266039">"Tomar notas"</string>
@@ -84,6 +86,7 @@
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Desinstalar"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"Información de app"</string>
<string name="install_private_system_shortcut_label" msgid="1616889277073184841">"Instalar en privado"</string>
+ <string name="uninstall_private_system_shortcut_label" msgid="8423460530441627982">"Desinstalar app"</string>
<string name="install_drop_target_label" msgid="2539096853673231757">"Instalar"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"No sugerir app"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Fijar predicción"</string>
@@ -167,8 +170,8 @@
<string name="action_deep_shortcut" msgid="2864038805849372848">"Accesos directos"</string>
<string name="action_dismiss_notification" msgid="5909461085055959187">"Descartar"</string>
<string name="accessibility_close" msgid="2277148124685870734">"Cerrar"</string>
- <string name="all_apps_personal_tab" msgid="4190252696685155002">"Personales"</string>
- <string name="all_apps_work_tab" msgid="4884822796154055118">"De trabajo"</string>
+ <string name="all_apps_personal_tab" msgid="4190252696685155002">"Personal"</string>
+ <string name="all_apps_work_tab" msgid="4884822796154055118">"Trabajo"</string>
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Perfil de trabajo"</string>
<string name="work_profile_edu_work_apps" msgid="7895468576497746520">"Las apps de trabajo tienen una insignia y el administrador de TI las puede ver"</string>
<string name="work_profile_edu_accept" msgid="6069788082535149071">"Entendido"</string>
diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml
index 463509a..7309e58 100644
--- a/res/values-es/strings.xml
+++ b/res/values-es/strings.xml
@@ -29,6 +29,8 @@
<string name="home_screen" msgid="5629429142036709174">"Inicio"</string>
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"Pantalla dividida"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"Información de la aplicación %1$s"</string>
+ <!-- no translation found for split_app_usage_settings (7214375263347964093) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"Guardar apps emparejadas"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"El dispositivo no admite esta aplicación emparejada"</string>
@@ -84,6 +86,7 @@
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Desinstalar"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"Información de la aplicación"</string>
<string name="install_private_system_shortcut_label" msgid="1616889277073184841">"Descargar en privado"</string>
+ <string name="uninstall_private_system_shortcut_label" msgid="8423460530441627982">"Desinstalar aplicación"</string>
<string name="install_drop_target_label" msgid="2539096853673231757">"Instalar"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"No sugerir aplicación"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Fijar predicción"</string>
diff --git a/res/values-et/strings.xml b/res/values-et/strings.xml
index 291831a..13500b5 100644
--- a/res/values-et/strings.xml
+++ b/res/values-et/strings.xml
@@ -29,6 +29,7 @@
<string name="home_screen" msgid="5629429142036709174">"Avakuva"</string>
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"Jagatud ekraanikuva"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"Rakenduse teave: %1$s"</string>
+ <string name="split_app_usage_settings" msgid="7214375263347964093">"Kasutuse seaded: %1$s"</string>
<string name="save_app_pair" msgid="5647523853662686243">"Salvesta rakendusepaar"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"See rakendusepaar ei ole selles seadmes toetatud"</string>
@@ -84,6 +85,7 @@
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Desinstalli"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"Rakenduse teave"</string>
<string name="install_private_system_shortcut_label" msgid="1616889277073184841">"Privaatselt installimine"</string>
+ <string name="uninstall_private_system_shortcut_label" msgid="8423460530441627982">"Desinstalli rakendus"</string>
<string name="install_drop_target_label" msgid="2539096853673231757">"Installimine"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Ära soovita rakendust"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Kinnita ennustus"</string>
diff --git a/res/values-eu/strings.xml b/res/values-eu/strings.xml
index 1a28d70..d1ff434 100644
--- a/res/values-eu/strings.xml
+++ b/res/values-eu/strings.xml
@@ -29,6 +29,8 @@
<string name="home_screen" msgid="5629429142036709174">"Orri nagusia"</string>
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"Pantaila zatitzea"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"%1$s aplikazioari buruzko informazioa"</string>
+ <!-- no translation found for split_app_usage_settings (7214375263347964093) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"Gorde aplikazio parea"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"Aplikazio pare hori ez da onartzen gailu honetan"</string>
@@ -84,6 +86,7 @@
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Desinstalatu"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"Aplikazioaren informazioa"</string>
<string name="install_private_system_shortcut_label" msgid="1616889277073184841">"Instalatu pribatuan"</string>
+ <string name="uninstall_private_system_shortcut_label" msgid="8423460530441627982">"Desinstalatu aplikazioa"</string>
<string name="install_drop_target_label" msgid="2539096853673231757">"Instalatu"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Ez iradoki aplikazioa"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Ainguratu iragarpena"</string>
diff --git a/res/values-fa/strings.xml b/res/values-fa/strings.xml
index 1ac2e9a..367945b 100644
--- a/res/values-fa/strings.xml
+++ b/res/values-fa/strings.xml
@@ -29,6 +29,8 @@
<string name="home_screen" msgid="5629429142036709174">"صفحه اصلی"</string>
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"صفحهٔ دونیمه"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"اطلاعات برنامه %1$s"</string>
+ <!-- no translation found for split_app_usage_settings (7214375263347964093) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"ذخیره جفت برنامه"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"از این جفت برنامه در این دستگاه پشتیبانی نمیشود"</string>
@@ -84,6 +86,7 @@
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"حذف نصب"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"اطلاعات برنامه"</string>
<string name="install_private_system_shortcut_label" msgid="1616889277073184841">"نصب در نمایه خصوصی"</string>
+ <string name="uninstall_private_system_shortcut_label" msgid="8423460530441627982">"حذف نصب برنامه"</string>
<string name="install_drop_target_label" msgid="2539096853673231757">"نصب"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"برنامه پیشنهاد داده نشود"</string>
<string name="pin_prediction" msgid="4196423321649756498">"سنجاق کردن پیشنهاد"</string>
diff --git a/res/values-fi/strings.xml b/res/values-fi/strings.xml
index b4fa543..5f7df00 100644
--- a/res/values-fi/strings.xml
+++ b/res/values-fi/strings.xml
@@ -29,6 +29,8 @@
<string name="home_screen" msgid="5629429142036709174">"Etusivu"</string>
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"Jaettu näyttö"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"Sovellustiedot: %1$s"</string>
+ <!-- no translation found for split_app_usage_settings (7214375263347964093) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"Tallenna sovelluspari"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"Sovellusparia ei tueta tällä laitteella"</string>
@@ -84,6 +86,7 @@
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Poista asennus"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"Sovelluksen tiedot"</string>
<string name="install_private_system_shortcut_label" msgid="1616889277073184841">"Asenna yksityisesti"</string>
+ <string name="uninstall_private_system_shortcut_label" msgid="8423460530441627982">"Sovelluksen poistaminen"</string>
<string name="install_drop_target_label" msgid="2539096853673231757">"Asenna"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Älä ehdota sovellusta"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Kiinnitä sovellus"</string>
diff --git a/res/values-fr-rCA/strings.xml b/res/values-fr-rCA/strings.xml
index 9f07ffd..9775dbc 100644
--- a/res/values-fr-rCA/strings.xml
+++ b/res/values-fr-rCA/strings.xml
@@ -29,6 +29,8 @@
<string name="home_screen" msgid="5629429142036709174">"Accueil"</string>
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"Écran divisé"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"Renseignements sur l\'appli pour %1$s"</string>
+ <!-- no translation found for split_app_usage_settings (7214375263347964093) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"Enr. paire d\'applis"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"Cette paire d\'applications n\'est pas prise en charge sur cet appareil"</string>
@@ -84,6 +86,7 @@
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Désinstaller"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"Détails de l\'appli"</string>
<string name="install_private_system_shortcut_label" msgid="1616889277073184841">"Installer dans privé"</string>
+ <string name="uninstall_private_system_shortcut_label" msgid="8423460530441627982">"Désinstaller l\'appli"</string>
<string name="install_drop_target_label" msgid="2539096853673231757">"Installer"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Ne pas suggérer d\'appli"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Épingler la prédiction"</string>
diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml
index dd9ec40..53a30d3 100644
--- a/res/values-fr/strings.xml
+++ b/res/values-fr/strings.xml
@@ -29,6 +29,8 @@
<string name="home_screen" msgid="5629429142036709174">"Accueil"</string>
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"Écran partagé"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"Infos sur l\'appli pour %1$s"</string>
+ <!-- no translation found for split_app_usage_settings (7214375263347964093) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"Enregistrer la paire d\'applis"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"Cette paire d\'applications n\'est pas prise en charge sur cet appareil"</string>
@@ -84,6 +86,7 @@
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Désinstaller"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"Infos sur l\'appli"</string>
<string name="install_private_system_shortcut_label" msgid="1616889277073184841">"Installer en mode privé"</string>
+ <string name="uninstall_private_system_shortcut_label" msgid="8423460530441627982">"Désinstaller l\'application"</string>
<string name="install_drop_target_label" msgid="2539096853673231757">"Installer"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Ne pas suggérer d\'appli"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Épingler la prédiction"</string>
diff --git a/res/values-gl/strings.xml b/res/values-gl/strings.xml
index 8940daf..d651692 100644
--- a/res/values-gl/strings.xml
+++ b/res/values-gl/strings.xml
@@ -29,6 +29,8 @@
<string name="home_screen" msgid="5629429142036709174">"Inicio"</string>
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"Pantalla dividida"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"Información da aplicación para %1$s"</string>
+ <!-- no translation found for split_app_usage_settings (7214375263347964093) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"Gardar parella de apps"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"O dispositivo non admite este emparellamento de aplicacións"</string>
@@ -84,6 +86,7 @@
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Desinstalar"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"Información da app"</string>
<string name="install_private_system_shortcut_label" msgid="1616889277073184841">"Instalar en privado"</string>
+ <string name="uninstall_private_system_shortcut_label" msgid="8423460530441627982">"Desinstalar aplicación"</string>
<string name="install_drop_target_label" msgid="2539096853673231757">"Instalar"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Non suxerir app"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Fixar predición"</string>
diff --git a/res/values-gu/strings.xml b/res/values-gu/strings.xml
index e595588..89291bd 100644
--- a/res/values-gu/strings.xml
+++ b/res/values-gu/strings.xml
@@ -29,6 +29,8 @@
<string name="home_screen" msgid="5629429142036709174">"હોમ સ્ક્રીન"</string>
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"સ્ક્રીનને વિભાજિત કરો"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"%1$s માટે ઍપ માહિતી"</string>
+ <!-- no translation found for split_app_usage_settings (7214375263347964093) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"ઍપની જોડી સાચવો"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"આ ડિવાઇસ પર, આ ઍપની જોડીને સપોર્ટ આપવામાં આવતો નથી"</string>
@@ -84,6 +86,7 @@
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"અનઇન્સ્ટૉલ કરો"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"ઍપની માહિતી"</string>
<string name="install_private_system_shortcut_label" msgid="1616889277073184841">"ખાનગીમાં ઇન્સ્ટૉલ કરો"</string>
+ <string name="uninstall_private_system_shortcut_label" msgid="8423460530441627982">"ઍપ અનઇન્સ્ટૉલ કરો"</string>
<string name="install_drop_target_label" msgid="2539096853673231757">"ઇન્સ્ટૉલ કરો"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"ઍપ સૂચવશો નહીં"</string>
<string name="pin_prediction" msgid="4196423321649756498">"પૂર્વાનુમાનને પિન કરો"</string>
diff --git a/res/values-hi/strings.xml b/res/values-hi/strings.xml
index 0c71db8..d70bf08 100644
--- a/res/values-hi/strings.xml
+++ b/res/values-hi/strings.xml
@@ -29,6 +29,8 @@
<string name="home_screen" msgid="5629429142036709174">"होम स्क्रीन"</string>
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"स्प्लिट स्क्रीन"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"%1$s के लिए ऐप्लिकेशन की जानकारी"</string>
+ <!-- no translation found for split_app_usage_settings (7214375263347964093) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"ऐप पेयर सेव करें"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"साथ में इस्तेमाल किए जा सकने वाले ये ऐप्लिकेशन, इस डिवाइस पर काम नहीं कर सकते"</string>
@@ -84,6 +86,7 @@
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"अनइंस्टॉल करें"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"ऐप्लिकेशन की जानकारी"</string>
<string name="install_private_system_shortcut_label" msgid="1616889277073184841">"निजी तौर पर इंस्टॉल करें"</string>
+ <string name="uninstall_private_system_shortcut_label" msgid="8423460530441627982">"ऐप्लिकेशन अनइंस्टॉल करें"</string>
<string name="install_drop_target_label" msgid="2539096853673231757">"इंस्टॉल करें"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"ऐप्लिकेशन का सुझाव न दें"</string>
<string name="pin_prediction" msgid="4196423321649756498">"सुझाए गए ऐप्लिकेशन को पिन करें"</string>
@@ -123,7 +126,7 @@
<string name="title_missing_notification_access" msgid="7503287056163941064">"सूचना के ऐक्सेस की ज़रूरत है"</string>
<string name="msg_missing_notification_access" msgid="281113995110910548">"सूचना बिंदु दिखाने के लिए, <xliff:g id="NAME">%1$s</xliff:g> के ऐप्लिकेशन सूचना चालू करें"</string>
<string name="title_change_settings" msgid="1376365968844349552">"सेटिंग बदलें"</string>
- <string name="notification_dots_service_title" msgid="4284221181793592871">"नई सूचनाएं बताने वाला गोल निशान दिखाएं"</string>
+ <string name="notification_dots_service_title" msgid="4284221181793592871">"सूचनाएं बताने वाले डॉट दिखाएं"</string>
<string name="developer_options_title" msgid="700788437593726194">"डेवलपर के लिए सेटिंग और टूल"</string>
<string name="auto_add_shortcuts_label" msgid="4926805029653694105">"होम स्क्रीन पर ऐप्लिकेशन के आइकॉन जोड़ें"</string>
<string name="auto_add_shortcuts_description" msgid="7117251166066978730">"नए ऐप्लिकेशन के लिए"</string>
diff --git a/res/values-hr/strings.xml b/res/values-hr/strings.xml
index d07775c..19658f6 100644
--- a/res/values-hr/strings.xml
+++ b/res/values-hr/strings.xml
@@ -29,6 +29,8 @@
<string name="home_screen" msgid="5629429142036709174">"Početni zaslon"</string>
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"Podijeljeni zaslon"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"Informacije o aplikaciji %1$s"</string>
+ <!-- no translation found for split_app_usage_settings (7214375263347964093) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"Spremi par aplikacija"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"Taj par aplikacija nije podržan na ovom uređaju"</string>
@@ -84,6 +86,7 @@
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Deinstaliraj"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"Podaci o aplikaciji"</string>
<string name="install_private_system_shortcut_label" msgid="1616889277073184841">"Instaliraj u privatno"</string>
+ <string name="uninstall_private_system_shortcut_label" msgid="8423460530441627982">"Deinstaliraj aplikaciju"</string>
<string name="install_drop_target_label" msgid="2539096853673231757">"Instaliraj"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Ne predlaži aplikaciju"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Prikvači predviđenu apl."</string>
diff --git a/res/values-hu/strings.xml b/res/values-hu/strings.xml
index 86ec374..96e4981 100644
--- a/res/values-hu/strings.xml
+++ b/res/values-hu/strings.xml
@@ -29,6 +29,8 @@
<string name="home_screen" msgid="5629429142036709174">"Kezdőképernyő"</string>
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"Osztott képernyő"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"Alkalmazásinformáció a következőhöz: %1$s"</string>
+ <!-- no translation found for split_app_usage_settings (7214375263347964093) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"Alkalmazáspár mentése"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"Ezt az alkalmazáspárt nem támogatja az eszköz"</string>
@@ -84,6 +86,7 @@
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Eltávolítás"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"Alkalmazásinfó"</string>
<string name="install_private_system_shortcut_label" msgid="1616889277073184841">"Privát telepítés"</string>
+ <string name="uninstall_private_system_shortcut_label" msgid="8423460530441627982">"Alkalmazás eltávolítása"</string>
<string name="install_drop_target_label" msgid="2539096853673231757">"Telepítés"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Ne javasoljon appot"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Várható kitűzése"</string>
diff --git a/res/values-hy/strings.xml b/res/values-hy/strings.xml
index 4bc54ab..e2b6d34 100644
--- a/res/values-hy/strings.xml
+++ b/res/values-hy/strings.xml
@@ -29,6 +29,8 @@
<string name="home_screen" msgid="5629429142036709174">"Հիմնական էկրան"</string>
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"Տրոհել էկրանը"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"Տեղեկություններ %1$s հավելվածի մասին"</string>
+ <!-- no translation found for split_app_usage_settings (7214375263347964093) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"Պահել հավելվ. զույգը"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"Հավելվածների զույգը չի աջակցվում այս սարքում"</string>
@@ -84,6 +86,7 @@
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Ապատեղադրել"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"Հավելվածի մասին"</string>
<string name="install_private_system_shortcut_label" msgid="1616889277073184841">"Տեղադրել մասնավորում"</string>
+ <string name="uninstall_private_system_shortcut_label" msgid="8423460530441627982">"Ապատեղադրել հավելվածը"</string>
<string name="install_drop_target_label" msgid="2539096853673231757">"Տեղադրել"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Չառաջարկել"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Ամրացնել առաջարկվող հավելվածը"</string>
diff --git a/res/values-in/strings.xml b/res/values-in/strings.xml
index cd614d0..a86380e 100644
--- a/res/values-in/strings.xml
+++ b/res/values-in/strings.xml
@@ -29,6 +29,8 @@
<string name="home_screen" msgid="5629429142036709174">"Layar utama"</string>
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"Layar terpisah"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"Info aplikasi untuk %1$s"</string>
+ <!-- no translation found for split_app_usage_settings (7214375263347964093) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"Simpan pasangan aplikasi"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"Pasangan aplikasi ini tidak didukung di perangkat ini"</string>
@@ -84,6 +86,7 @@
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Uninstal"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"Info aplikasi"</string>
<string name="install_private_system_shortcut_label" msgid="1616889277073184841">"Instal di ruang privasi"</string>
+ <string name="uninstall_private_system_shortcut_label" msgid="8423460530441627982">"Uninstal aplikasi"</string>
<string name="install_drop_target_label" msgid="2539096853673231757">"Instal"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Jangan sarankan apl"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Pin Prediksi"</string>
diff --git a/res/values-is/strings.xml b/res/values-is/strings.xml
index 53b8e90..d903d1a 100644
--- a/res/values-is/strings.xml
+++ b/res/values-is/strings.xml
@@ -29,6 +29,8 @@
<string name="home_screen" msgid="5629429142036709174">"Heim"</string>
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"Skipta skjá"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"Upplýsingar um forrit fyrir %1$s"</string>
+ <!-- no translation found for split_app_usage_settings (7214375263347964093) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"Vista forritapar"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"Þetta forritapar er ekki stutt í þessu tæki"</string>
@@ -84,6 +86,7 @@
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Fjarlægja"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"Forritsupplýsingar"</string>
<string name="install_private_system_shortcut_label" msgid="1616889277073184841">"Setja upp á lokuðum prófíl"</string>
+ <string name="uninstall_private_system_shortcut_label" msgid="8423460530441627982">"Fjarlægja forrit"</string>
<string name="install_drop_target_label" msgid="2539096853673231757">"Setja upp"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Ekki fá tillögu að forriti"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Festa tillögu"</string>
@@ -182,14 +185,14 @@
<string name="work_apps_enable_btn_text" msgid="1736198302467317371">"Ljúka hléi"</string>
<string name="developer_options_filter_hint" msgid="5896817443635989056">"Sía"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Mistókst: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
- <string name="private_space_label" msgid="2359721649407947001">"Einkarými"</string>
+ <string name="private_space_label" msgid="2359721649407947001">"Leynirými"</string>
<string name="private_space_secondary_label" msgid="9203933341714508907">"Ýttu til að setja upp eða opna"</string>
<string name="ps_container_title" msgid="4391796149519594205">"Lokað"</string>
<string name="ps_container_settings" msgid="6059734123353320479">"Stillingar einkarýmis"</string>
<string name="ps_container_unlock_button_content_description" msgid="9181551784092204234">"Lokað, ólæst."</string>
<string name="ps_container_lock_button_content_description" msgid="5961993384382649530">"Lokað, læst."</string>
<string name="ps_container_lock_title" msgid="2640257399982364682">"Læsa"</string>
- <string name="ps_container_transition" msgid="8667331812048014412">"Einkarými að breytast"</string>
+ <string name="ps_container_transition" msgid="8667331812048014412">"Leynirými að breytast"</string>
<string name="ps_add_button_label" msgid="8127988716897128773">"Setja upp"</string>
<string name="ps_add_button_content_description" msgid="3254274107740952556">"Setja upp forrit í leynirými"</string>
<string name="bubble_bar_overflow_description" msgid="7410995531938041192">"Yfirflæði"</string>
diff --git a/res/values-it/strings.xml b/res/values-it/strings.xml
index af0e2b9..3895409 100644
--- a/res/values-it/strings.xml
+++ b/res/values-it/strings.xml
@@ -29,6 +29,8 @@
<string name="home_screen" msgid="5629429142036709174">"Home"</string>
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"Schermo diviso"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"Informazioni sull\'app %1$s"</string>
+ <!-- no translation found for split_app_usage_settings (7214375263347964093) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"Salva coppia di app"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"Questa coppia di app non è supportata su questo dispositivo"</string>
@@ -84,6 +86,7 @@
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Disinstalla"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"Informazioni app"</string>
<string name="install_private_system_shortcut_label" msgid="1616889277073184841">"Installa in privato"</string>
+ <string name="uninstall_private_system_shortcut_label" msgid="8423460530441627982">"Disinstalla app"</string>
<string name="install_drop_target_label" msgid="2539096853673231757">"Installa"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Non suggerire app"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Blocca previsione"</string>
diff --git a/res/values-iw/strings.xml b/res/values-iw/strings.xml
index 7075d59..aea1fa6 100644
--- a/res/values-iw/strings.xml
+++ b/res/values-iw/strings.xml
@@ -29,6 +29,8 @@
<string name="home_screen" msgid="5629429142036709174">"בית"</string>
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"מסך מפוצל"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"פרטים על האפליקציה %1$s"</string>
+ <!-- no translation found for split_app_usage_settings (7214375263347964093) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"שמירת צמד אפליקציות"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"צמד האפליקציות הזה לא נתמך במכשיר הזה"</string>
@@ -84,6 +86,7 @@
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"להסרת התקנה"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"פרטי אפליקציה"</string>
<string name="install_private_system_shortcut_label" msgid="1616889277073184841">"התקנה במרחב הפרטי"</string>
+ <string name="uninstall_private_system_shortcut_label" msgid="8423460530441627982">"הסרת האפליקציה"</string>
<string name="install_drop_target_label" msgid="2539096853673231757">"התקנה"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"בלי להציע את האפליקציה"</string>
<string name="pin_prediction" msgid="4196423321649756498">"הצמדת החיזוי"</string>
diff --git a/res/values-ja/strings.xml b/res/values-ja/strings.xml
index d957c5c..c349e54 100644
--- a/res/values-ja/strings.xml
+++ b/res/values-ja/strings.xml
@@ -29,6 +29,7 @@
<string name="home_screen" msgid="5629429142036709174">"ホーム"</string>
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"分割画面"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"%1$s のアプリ情報"</string>
+ <string name="split_app_usage_settings" msgid="7214375263347964093">"%1$s の使用設定"</string>
<string name="save_app_pair" msgid="5647523853662686243">"アプリのペア設定を保存"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"このデバイスは、このアプリのペア設定に対応していません"</string>
@@ -84,6 +85,7 @@
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"アンインストール"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"アプリ情報"</string>
<string name="install_private_system_shortcut_label" msgid="1616889277073184841">"非公開インストール"</string>
+ <string name="uninstall_private_system_shortcut_label" msgid="8423460530441627982">"アプリをアンインストール"</string>
<string name="install_drop_target_label" msgid="2539096853673231757">"インストール"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"アプリを表示しない"</string>
<string name="pin_prediction" msgid="4196423321649756498">"アプリの候補を固定"</string>
diff --git a/res/values-ka/strings.xml b/res/values-ka/strings.xml
index 8a1eb21..fecd50e 100644
--- a/res/values-ka/strings.xml
+++ b/res/values-ka/strings.xml
@@ -29,6 +29,7 @@
<string name="home_screen" msgid="5629429142036709174">"მთავარი გვერდი"</string>
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"ეკრანის გაყოფა"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"%1$s-ის აპის ინფო"</string>
+ <string name="split_app_usage_settings" msgid="7214375263347964093">"გამოყენების პარამეტრები %1$s-ისთვის"</string>
<string name="save_app_pair" msgid="5647523853662686243">"აპთა წყვილის შენახვა"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"ამ მოწყობილობაზე აღნიშნული აპთა წყვილი არ არის მხარდაჭერილი"</string>
@@ -84,6 +85,7 @@
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"დეინსტალაცია"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"აპის შესახებ"</string>
<string name="install_private_system_shortcut_label" msgid="1616889277073184841">"კერძოში ინსტალაცია"</string>
+ <string name="uninstall_private_system_shortcut_label" msgid="8423460530441627982">"აპის დეინსტალაცია"</string>
<string name="install_drop_target_label" msgid="2539096853673231757">"ინსტალაცია"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"არ შემომთავაზო აპი"</string>
<string name="pin_prediction" msgid="4196423321649756498">"ჩამაგრების პროგნოზირება"</string>
diff --git a/res/values-kk/strings.xml b/res/values-kk/strings.xml
index f70888a..f3aa5f6 100644
--- a/res/values-kk/strings.xml
+++ b/res/values-kk/strings.xml
@@ -29,6 +29,8 @@
<string name="home_screen" msgid="5629429142036709174">"Негізгі экран"</string>
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"Экранды бөлу"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"%1$s қолданбасы туралы ақпарат"</string>
+ <!-- no translation found for split_app_usage_settings (7214375263347964093) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"Қолданбаларды жұптау әрекетін сақтау"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"Бұл құрылғы қолданбаларды жұптау функциясын қолдамайды."</string>
@@ -84,6 +86,7 @@
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Жою"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"Қолданба ақпараты"</string>
<string name="install_private_system_shortcut_label" msgid="1616889277073184841">"Құпия профильге орнату"</string>
+ <string name="uninstall_private_system_shortcut_label" msgid="8423460530441627982">"Қолданбаны жою"</string>
<string name="install_drop_target_label" msgid="2539096853673231757">"Орнату"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Қолданба ұсынбау"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Болжанған қолданбаны бекіту"</string>
diff --git a/res/values-km/strings.xml b/res/values-km/strings.xml
index 14ff3c9..1f73d4c 100644
--- a/res/values-km/strings.xml
+++ b/res/values-km/strings.xml
@@ -29,6 +29,8 @@
<string name="home_screen" msgid="5629429142036709174">"អេក្រង់ដើម"</string>
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"មុខងារបំបែកអេក្រង់"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"ព័ត៌មានកម្មវិធីសម្រាប់ %1$s"</string>
+ <!-- no translation found for split_app_usage_settings (7214375263347964093) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"រក្សាទុកគូកម្មវិធី"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"មិនអាចប្រើគូកម្មវិធីនេះនៅលើឧបករណ៍នេះបានទេ"</string>
@@ -84,6 +86,7 @@
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"លុប"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"ព័ត៌មានកម្មវិធី"</string>
<string name="install_private_system_shortcut_label" msgid="1616889277073184841">"ដំឡើងជាលក្ខណៈឯកជន"</string>
+ <string name="uninstall_private_system_shortcut_label" msgid="8423460530441627982">"លុបកម្មវិធី"</string>
<string name="install_drop_target_label" msgid="2539096853673231757">"ដំឡើង"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"កុំណែនាំកម្មវិធី"</string>
<string name="pin_prediction" msgid="4196423321649756498">"ខ្ទាស់ការព្យាករ"</string>
diff --git a/res/values-kn/strings.xml b/res/values-kn/strings.xml
index f918d18..309a1fb 100644
--- a/res/values-kn/strings.xml
+++ b/res/values-kn/strings.xml
@@ -29,6 +29,8 @@
<string name="home_screen" msgid="5629429142036709174">"ಹೋಮ್"</string>
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"ಸ್ಪ್ಲಿಟ್ ಸ್ಕ್ರೀನ್"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"%1$s ಗಾಗಿ ಆ್ಯಪ್ ಮಾಹಿತಿ"</string>
+ <!-- no translation found for split_app_usage_settings (7214375263347964093) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"ಆ್ಯಪ್ ಪೇರ್ ಸೇವ್ ಮಾಡಿ"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"ಈ ಆ್ಯಪ್ ಜೋಡಿಯು ಈ ಸಾಧನದಲ್ಲಿ ಬೆಂಬಲಿತವಾಗಿಲ್ಲ"</string>
@@ -84,6 +86,7 @@
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"ಅನ್ಇನ್ಸ್ಟಾಲ್"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"ಆ್ಯಪ್ ಮಾಹಿತಿ"</string>
<string name="install_private_system_shortcut_label" msgid="1616889277073184841">"ಖಾಸಗಿಯಾಗಿ ಇನ್ಸ್ಟಾಲ್ ಮಾಡಿ"</string>
+ <string name="uninstall_private_system_shortcut_label" msgid="8423460530441627982">"ಆ್ಯಪ್ ಅನ್ಇನ್ಸ್ಟಾಲ್ ಮಾಡಿ"</string>
<string name="install_drop_target_label" msgid="2539096853673231757">"ಸ್ಥಾಪಿಸಿ"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"ಆ್ಯಪ್ ಅನ್ನು ಸೂಚಿಸಬೇಡಿ"</string>
<string name="pin_prediction" msgid="4196423321649756498">"ಮುನ್ನೋಟ ಪಿನ್ ಮಾಡಿ"</string>
diff --git a/res/values-ko/strings.xml b/res/values-ko/strings.xml
index b59ccd6..cfc04ef 100644
--- a/res/values-ko/strings.xml
+++ b/res/values-ko/strings.xml
@@ -29,6 +29,8 @@
<string name="home_screen" msgid="5629429142036709174">"홈"</string>
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"화면 분할"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"%1$s 앱 정보"</string>
+ <!-- no translation found for split_app_usage_settings (7214375263347964093) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"앱 페어링 저장"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"이 앱 페어링은 이 기기에서 지원되지 않습니다"</string>
@@ -84,6 +86,7 @@
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"제거"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"앱 정보"</string>
<string name="install_private_system_shortcut_label" msgid="1616889277073184841">"비공개 스페이스에 설치"</string>
+ <string name="uninstall_private_system_shortcut_label" msgid="8423460530441627982">"앱 제거"</string>
<string name="install_drop_target_label" msgid="2539096853673231757">"설치"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"앱 제안 받지 않음"</string>
<string name="pin_prediction" msgid="4196423321649756498">"예상 앱 고정"</string>
diff --git a/res/values-ky/strings.xml b/res/values-ky/strings.xml
index e4bbc01..96446e8 100644
--- a/res/values-ky/strings.xml
+++ b/res/values-ky/strings.xml
@@ -29,6 +29,8 @@
<string name="home_screen" msgid="5629429142036709174">"Башкы экран"</string>
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"Экранды бөлүү"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"%1$s колдонмосу жөнүндө маалымат"</string>
+ <!-- no translation found for split_app_usage_settings (7214375263347964093) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"Колдонмолорду сактап коюу"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"Бул эки колдонмону бул түзмөктө бир маалда пайдаланууга болбойт"</string>
@@ -84,6 +86,7 @@
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Чыгарып салуу"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"Колдонмо тууралуу"</string>
<string name="install_private_system_shortcut_label" msgid="1616889277073184841">"Жеке мейкиндикке орнотуу"</string>
+ <string name="uninstall_private_system_shortcut_label" msgid="8423460530441627982">"Колдонмону чыгарып салуу"</string>
<string name="install_drop_target_label" msgid="2539096853673231757">"Орнотуу"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Cунушталбасын"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Божомолдонгон колдонмону кадап коюу"</string>
diff --git a/res/values-lo/strings.xml b/res/values-lo/strings.xml
index 17f226e..708a2f3 100644
--- a/res/values-lo/strings.xml
+++ b/res/values-lo/strings.xml
@@ -29,6 +29,8 @@
<string name="home_screen" msgid="5629429142036709174">"ໂຮມສະກຣີນ"</string>
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"ແບ່ງໜ້າຈໍ"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"ຂໍ້ມູນແອັບສຳລັບ %1$s"</string>
+ <!-- no translation found for split_app_usage_settings (7214375263347964093) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"ບັນທຶກຈັບຄູ່ແອັບ"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"ການຈັບຄູ່ແອັບນີ້ບໍ່ຮອງຮັບຢູ່ອຸປະກອນນີ້"</string>
@@ -84,6 +86,7 @@
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"ຖອນການຕິດຕັ້ງ"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"ຂໍ້ມູນແອັບ"</string>
<string name="install_private_system_shortcut_label" msgid="1616889277073184841">"ຕິດຕັ້ງໃນສ່ວນຕົວ"</string>
+ <string name="uninstall_private_system_shortcut_label" msgid="8423460530441627982">"ຖອນການຕິດຕັ້ງແອັບ"</string>
<string name="install_drop_target_label" msgid="2539096853673231757">"ຕິດຕັ້ງ"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"ຢ່າແນະນຳແອັບ"</string>
<string name="pin_prediction" msgid="4196423321649756498">"ປັກໝຸດການຄາດເດົາ"</string>
diff --git a/res/values-lt/strings.xml b/res/values-lt/strings.xml
index 17cb3cf..bc33bd8 100644
--- a/res/values-lt/strings.xml
+++ b/res/values-lt/strings.xml
@@ -29,6 +29,7 @@
<string name="home_screen" msgid="5629429142036709174">"Pagrindinis"</string>
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"Išskaidyto ekrano režimas"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"Programos „%1$s“ informacija"</string>
+ <string name="split_app_usage_settings" msgid="7214375263347964093">"„%1$s“ naudojimo nustatymai"</string>
<string name="save_app_pair" msgid="5647523853662686243">"Išsaugoti programų porą"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"Ši programų pora šiame įrenginyje nepalaikoma"</string>
@@ -84,6 +85,7 @@
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Pašalinti"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"Programos inform."</string>
<string name="install_private_system_shortcut_label" msgid="1616889277073184841">"Įdiegti privačiai"</string>
+ <string name="uninstall_private_system_shortcut_label" msgid="8423460530441627982">"Pašalinti programą"</string>
<string name="install_drop_target_label" msgid="2539096853673231757">"Įdiegti"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Nesiūlyti programos"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Prisegti numatymą"</string>
diff --git a/res/values-lv/strings.xml b/res/values-lv/strings.xml
index e514afc..7dbe5fa 100644
--- a/res/values-lv/strings.xml
+++ b/res/values-lv/strings.xml
@@ -29,6 +29,8 @@
<string name="home_screen" msgid="5629429142036709174">"Sākums"</string>
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"Sadalīt ekrānu"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"%1$s: informācija par lietotni"</string>
+ <!-- no translation found for split_app_usage_settings (7214375263347964093) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"Saglabāt lietotņu pāri"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"Šis lietotņu pāris netiek atbalstīts šajā ierīcē"</string>
@@ -84,6 +86,7 @@
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Atinstalēt"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"Par lietotni"</string>
<string name="install_private_system_shortcut_label" msgid="1616889277073184841">"Instalēt privāti"</string>
+ <string name="uninstall_private_system_shortcut_label" msgid="8423460530441627982">"Atinstalēt lietotni"</string>
<string name="install_drop_target_label" msgid="2539096853673231757">"Instalēt"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Neieteikt lietotni"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Piespraust prognozēto lietotni"</string>
diff --git a/res/values-mk/strings.xml b/res/values-mk/strings.xml
index 3169f7d..18849e9 100644
--- a/res/values-mk/strings.xml
+++ b/res/values-mk/strings.xml
@@ -29,6 +29,8 @@
<string name="home_screen" msgid="5629429142036709174">"Почетен екран"</string>
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"Поделен екран"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"Податоци за апликација за %1$s"</string>
+ <!-- no translation found for split_app_usage_settings (7214375263347964093) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"Зачувај го парот апликации"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"Паров апликации не е поддржан на уредов"</string>
@@ -84,6 +86,7 @@
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Деинсталирај"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"Инф. за апликација"</string>
<string name="install_private_system_shortcut_label" msgid="1616889277073184841">"Инстал. во приватен"</string>
+ <string name="uninstall_private_system_shortcut_label" msgid="8423460530441627982">"Деинсталирај ја апликацијата"</string>
<string name="install_drop_target_label" msgid="2539096853673231757">"Инсталирај"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Не предлагај апл."</string>
<string name="pin_prediction" msgid="4196423321649756498">"Закачи го предвидувањето"</string>
diff --git a/res/values-ml/strings.xml b/res/values-ml/strings.xml
index 952e607..529cc8d 100644
--- a/res/values-ml/strings.xml
+++ b/res/values-ml/strings.xml
@@ -29,6 +29,8 @@
<string name="home_screen" msgid="5629429142036709174">"ഹോം"</string>
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"സ്ക്രീൻ വിഭജന മോഡ്"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"%1$s എന്നതിന്റെ ആപ്പ് വിവരങ്ങൾ"</string>
+ <!-- no translation found for split_app_usage_settings (7214375263347964093) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"ആപ്പ് ജോടി സംരക്ഷിക്കുക"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"ഈ ഉപകരണത്തിൽ ഈ ആപ്പ് ജോടിക്ക് പിന്തുണയില്ല"</string>
@@ -84,6 +86,7 @@
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"അൺഇൻസ്റ്റാൾ"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"ആപ്പ് വിവരങ്ങൾ"</string>
<string name="install_private_system_shortcut_label" msgid="1616889277073184841">"സ്വകാര്യമായി ഇൻസ്റ്റാൾ ചെയ്യൂ"</string>
+ <string name="uninstall_private_system_shortcut_label" msgid="8423460530441627982">"ആപ്പ് അൺഇൻസ്റ്റാൾ ചെയ്യുക"</string>
<string name="install_drop_target_label" msgid="2539096853673231757">"ഇൻസ്റ്റാൾ ചെയ്യുക"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"ആപ്പ് നിർദ്ദേശിക്കേണ്ട"</string>
<string name="pin_prediction" msgid="4196423321649756498">"പ്രവചനം പിൻ ചെയ്യുക"</string>
diff --git a/res/values-mn/strings.xml b/res/values-mn/strings.xml
index 7209c85..21c8ad1 100644
--- a/res/values-mn/strings.xml
+++ b/res/values-mn/strings.xml
@@ -29,6 +29,8 @@
<string name="home_screen" msgid="5629429142036709174">"Нүүр"</string>
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"Дэлгэцийг хуваах"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"%1$s-н аппын мэдээлэл"</string>
+ <!-- no translation found for split_app_usage_settings (7214375263347964093) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"Апп хослуулалтыг хадгалах"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"Энэ апп хослуулалтыг уг төхөөрөмж дээр дэмждэггүй"</string>
@@ -84,6 +86,7 @@
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Устгах"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"Аппын мэдээлэл"</string>
<string name="install_private_system_shortcut_label" msgid="1616889277073184841">"Хувийнхад суулгах"</string>
+ <string name="uninstall_private_system_shortcut_label" msgid="8423460530441627982">"Аппыг устгах"</string>
<string name="install_drop_target_label" msgid="2539096853673231757">"Суулгах"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Апп бүү санал болго"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Таамаглалыг бэхлэх"</string>
diff --git a/res/values-mr/strings.xml b/res/values-mr/strings.xml
index 5c08f2e..1badd21 100644
--- a/res/values-mr/strings.xml
+++ b/res/values-mr/strings.xml
@@ -29,6 +29,7 @@
<string name="home_screen" msgid="5629429142036709174">"होम"</string>
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"स्प्लिट स्क्रीन"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"%1$s साठी ॲपशी संबंधित माहिती"</string>
+ <string name="split_app_usage_settings" msgid="7214375263347964093">"%1$s साठी वापरासंबंधित सेटिंग्ज"</string>
<string name="save_app_pair" msgid="5647523853662686243">"ॲपची जोडी सेव्ह करा"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"या ॲपची जोडीला या डिव्हाइसवर सपोर्ट नाही"</string>
@@ -84,6 +85,7 @@
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"अनइंस्टॉल करा"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"अॅप माहिती"</string>
<string name="install_private_system_shortcut_label" msgid="1616889277073184841">"खाजगीत इंस्टॉल करा"</string>
+ <string name="uninstall_private_system_shortcut_label" msgid="8423460530441627982">"अॅप अनइंस्टॉल करा"</string>
<string name="install_drop_target_label" msgid="2539096853673231757">"इंस्टॉल करा"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"ॲप सुचवू नका"</string>
<string name="pin_prediction" msgid="4196423321649756498">"पूर्वानुमान पिन करा"</string>
diff --git a/res/values-ms/strings.xml b/res/values-ms/strings.xml
index 20627c8..270ebf0 100644
--- a/res/values-ms/strings.xml
+++ b/res/values-ms/strings.xml
@@ -29,6 +29,8 @@
<string name="home_screen" msgid="5629429142036709174">"Rumah"</string>
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"Skrin pisah"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"Maklumat apl untuk %1$s"</string>
+ <!-- no translation found for split_app_usage_settings (7214375263347964093) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"Simpan gandingan apl"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"Gandingan apl ini tidak disokong pada peranti ini"</string>
@@ -84,6 +86,7 @@
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Nyahpasang"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"Maklumat apl"</string>
<string name="install_private_system_shortcut_label" msgid="1616889277073184841">"Pasang dalam persendirian"</string>
+ <string name="uninstall_private_system_shortcut_label" msgid="8423460530441627982">"Nyahpasang apl"</string>
<string name="install_drop_target_label" msgid="2539096853673231757">"Pasang"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Jangan cadangkan apl"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Sematkan Ramalan"</string>
diff --git a/res/values-my/strings.xml b/res/values-my/strings.xml
index b77ee98..cec9b1b 100644
--- a/res/values-my/strings.xml
+++ b/res/values-my/strings.xml
@@ -29,6 +29,7 @@
<string name="home_screen" msgid="5629429142036709174">"ပင်မစာမျက်နှာ"</string>
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"မျက်နှာပြင် ခွဲ၍ပြသခြင်း"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"%1$s အတွက် အက်ပ်အချက်အလက်"</string>
+ <string name="split_app_usage_settings" msgid="7214375263347964093">"%1$s အတွက် အသုံးပြုမှုဆက်တင်များ"</string>
<string name="save_app_pair" msgid="5647523853662686243">"အက်ပ်တွဲချိတ်ခြင်း သိမ်းရန်"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"ဤအက်ပ်တွဲချိတ်ခြင်းကို ဤစက်တွင် ပံ့ပိုးမထားပါ"</string>
@@ -84,6 +85,7 @@
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"ဖယ်ရှားရန်"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"အက်ပ်အချက်အလက်"</string>
<string name="install_private_system_shortcut_label" msgid="1616889277073184841">"သီးသန့်တွင် ထည့်သွင်းရန်"</string>
+ <string name="uninstall_private_system_shortcut_label" msgid="8423460530441627982">"အက်ပ်ကို ဖယ်ရှားရန်"</string>
<string name="install_drop_target_label" msgid="2539096853673231757">"ထည့်သွင်းရန်"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"အက်ပ်အကြံမပြုပါနှင့်"</string>
<string name="pin_prediction" msgid="4196423321649756498">"ခန့်မှန်းချက်ကို ပင်ထိုးရန်"</string>
diff --git a/res/values-nb/strings.xml b/res/values-nb/strings.xml
index c262517..392cf45 100644
--- a/res/values-nb/strings.xml
+++ b/res/values-nb/strings.xml
@@ -29,6 +29,8 @@
<string name="home_screen" msgid="5629429142036709174">"Startskjerm"</string>
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"Delt skjerm"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"Appinformasjon for %1$s"</string>
+ <!-- no translation found for split_app_usage_settings (7214375263347964093) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"Lagre apptilkoblingen"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"Denne apptilkoblingen støttes ikke på denne enheten"</string>
@@ -84,6 +86,7 @@
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Avinstaller"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"Info om appen"</string>
<string name="install_private_system_shortcut_label" msgid="1616889277073184841">"Installer privat"</string>
+ <string name="uninstall_private_system_shortcut_label" msgid="8423460530441627982">"Avinstaller appen"</string>
<string name="install_drop_target_label" msgid="2539096853673231757">"Installer"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Ikke foreslå app"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Fest forslaget"</string>
diff --git a/res/values-ne/strings.xml b/res/values-ne/strings.xml
index c47d38e..f5a16ee 100644
--- a/res/values-ne/strings.xml
+++ b/res/values-ne/strings.xml
@@ -29,6 +29,8 @@
<string name="home_screen" msgid="5629429142036709174">"होम"</string>
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"स्प्लिट स्क्रिन"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"%1$s का हकमा एपसम्बन्धी जानकारी"</string>
+ <!-- no translation found for split_app_usage_settings (7214375263347964093) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"एपको पेयर सेभ गर्नुहोस्"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"यस डिभाइसमा यो एप पेयर प्रयोग गर्न मिल्दैन"</string>
@@ -84,6 +86,7 @@
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"अनइन्स्टल गर्नुहोस्"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"एपसम्बन्धी जानकारी"</string>
<string name="install_private_system_shortcut_label" msgid="1616889277073184841">"निजी प्रोफाइलमा इन्स्टल गर्नुहोस्"</string>
+ <string name="uninstall_private_system_shortcut_label" msgid="8423460530441627982">"एप अनइन्स्टल गर्नुहोस्"</string>
<string name="install_drop_target_label" msgid="2539096853673231757">"स्थापना गर्नुहोस्"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"एप सिफारिस नगर्नुहोस्"</string>
<string name="pin_prediction" msgid="4196423321649756498">"सिफारिस गरिएको एप पिन गर्नुहोस्"</string>
diff --git a/res/values-nl/strings.xml b/res/values-nl/strings.xml
index 09a4090..176fbc9 100644
--- a/res/values-nl/strings.xml
+++ b/res/values-nl/strings.xml
@@ -29,6 +29,8 @@
<string name="home_screen" msgid="5629429142036709174">"Startscherm"</string>
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"Gesplitst scherm"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"App-info voor %1$s"</string>
+ <!-- no translation found for split_app_usage_settings (7214375263347964093) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"App-paar opslaan"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"Dit app-paar wordt niet ondersteund op dit apparaat"</string>
@@ -84,6 +86,7 @@
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Deïnstalleren"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"App-info"</string>
<string name="install_private_system_shortcut_label" msgid="1616889277073184841">"Privé installeren"</string>
+ <string name="uninstall_private_system_shortcut_label" msgid="8423460530441627982">"App verwijderen"</string>
<string name="install_drop_target_label" msgid="2539096853673231757">"Installeren"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Geen app voorstellen"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Vastzetvoorspelling"</string>
diff --git a/res/values-or/strings.xml b/res/values-or/strings.xml
index ea68d8c..db656a6 100644
--- a/res/values-or/strings.xml
+++ b/res/values-or/strings.xml
@@ -29,6 +29,8 @@
<string name="home_screen" msgid="5629429142036709174">"ହୋମ"</string>
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"ସ୍କ୍ରିନକୁ ସ୍ପ୍ଲିଟ କରନ୍ତୁ"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"%1$s ପାଇଁ ଆପ ସୂଚନା"</string>
+ <!-- no translation found for split_app_usage_settings (7214375263347964093) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"ଆପ ପେୟାର ସେଭ କରନ୍ତୁ"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"ଏହି ଆପ ପେୟାର ଏ ଡିଭାଇସରେ ସମର୍ଥିତ ନୁହେଁ"</string>
@@ -84,6 +86,7 @@
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"ଅନଇନଷ୍ଟଲ କରନ୍ତୁ"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"ଆପ ସୂଚନା"</string>
<string name="install_private_system_shortcut_label" msgid="1616889277073184841">"ପ୍ରାଇଭେଟରେ ଇନଷ୍ଟଲ କର"</string>
+ <string name="uninstall_private_system_shortcut_label" msgid="8423460530441627982">"ଆପ ଅନଇନଷ୍ଟଲ କରନ୍ତୁ"</string>
<string name="install_drop_target_label" msgid="2539096853673231757">"ଇନଷ୍ଟଲ୍ କରନ୍ତୁ"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"ଆପ ପରାମର୍ଶ ଦିଅନ୍ତୁ ନାହିଁ"</string>
<string name="pin_prediction" msgid="4196423321649756498">"ପୂର୍ବାନୁମାନକୁ ପିନ୍ କରନ୍ତୁ"</string>
diff --git a/res/values-pa/strings.xml b/res/values-pa/strings.xml
index 073acfa..a496865 100644
--- a/res/values-pa/strings.xml
+++ b/res/values-pa/strings.xml
@@ -29,6 +29,8 @@
<string name="home_screen" msgid="5629429142036709174">"ਮੁੱਖ ਪੰਨਾ"</string>
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"ਸਪਲਿਟ ਸਕ੍ਰੀਨ"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"%1$s ਲਈ ਐਪ ਜਾਣਕਾਰੀ"</string>
+ <!-- no translation found for split_app_usage_settings (7214375263347964093) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"ਐਪ ਜੋੜਾਬੱਧ ਰੱਖਿਅਤ ਕਰੋ"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"ਇਸ ਐਪ ਜੋੜਾਬੱਧ ਦਾ ਇਸ ਡੀਵਾਈਸ \'ਤੇ ਸਮਰਥਨ ਨਹੀਂ ਕੀਤਾ ਜਾਂਦਾ"</string>
@@ -84,6 +86,7 @@
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"ਅਣਸਥਾਪਤ ਕਰੋ"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"ਐਪ ਜਾਣਕਾਰੀ"</string>
<string name="install_private_system_shortcut_label" msgid="1616889277073184841">"ਨਿੱਜੀ ਵਜੋਂ ਸਥਾਪਤ ਕਰੋ"</string>
+ <string name="uninstall_private_system_shortcut_label" msgid="8423460530441627982">"ਐਪ ਅਣਸਥਾਪਤ ਕਰੋ"</string>
<string name="install_drop_target_label" msgid="2539096853673231757">"ਸਥਾਪਤ ਕਰੋ"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"ਐਪ ਦਾ ਸੁਝਾਅ ਨਾ ਦਿਓ"</string>
<string name="pin_prediction" msgid="4196423321649756498">"ਪੂਰਵ-ਅਨੁਮਾਨ ਪਿੰਨ ਕਰੋ"</string>
@@ -124,7 +127,7 @@
<string name="msg_missing_notification_access" msgid="281113995110910548">"ਸੂਚਨਾ ਬਿੰਦੂਆਂ ਦਿਖਾਉਣ ਲਈ, <xliff:g id="NAME">%1$s</xliff:g> ਲਈ ਐਪ ਸੂਚਨਾਵਾਂ ਚਾਲੂ ਕਰੋ"</string>
<string name="title_change_settings" msgid="1376365968844349552">"ਸੈਟਿੰਗਾਂ ਬਦਲੋ"</string>
<string name="notification_dots_service_title" msgid="4284221181793592871">"ਸੂਚਨਾ ਬਿੰਦੂ ਦਿਖਾਓ"</string>
- <string name="developer_options_title" msgid="700788437593726194">"ਵਿਕਾਸਕਾਰ ਚੋਣਾਂ"</string>
+ <string name="developer_options_title" msgid="700788437593726194">"ਵਿਕਾਸਕਾਰ ਵਿਕਲਪ"</string>
<string name="auto_add_shortcuts_label" msgid="4926805029653694105">"ਹੋਮ ਸਕ੍ਰੀਨ \'ਤੇ ਐਪ ਪ੍ਰਤੀਕਾਂ ਨੂੰ ਸ਼ਾਮਲ ਕਰੋ"</string>
<string name="auto_add_shortcuts_description" msgid="7117251166066978730">"ਨਵੀਆਂ ਐਪਾਂ ਲਈ"</string>
<string name="package_state_unknown" msgid="7592128424511031410">"ਅਗਿਆਤ"</string>
diff --git a/res/values-pl/strings.xml b/res/values-pl/strings.xml
index 04a4419..86a43ab 100644
--- a/res/values-pl/strings.xml
+++ b/res/values-pl/strings.xml
@@ -29,6 +29,8 @@
<string name="home_screen" msgid="5629429142036709174">"Ekran główny"</string>
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"Podziel ekran"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"Informacje o aplikacji: %1$s"</string>
+ <!-- no translation found for split_app_usage_settings (7214375263347964093) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"Zapisz parę aplikacji"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"Ta para aplikacji nie jest obsługiwana na tym urządzeniu"</string>
@@ -84,6 +86,7 @@
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Odinstaluj"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"O aplikacji"</string>
<string name="install_private_system_shortcut_label" msgid="1616889277073184841">"Zainstaluj prywatnie"</string>
+ <string name="uninstall_private_system_shortcut_label" msgid="8423460530441627982">"Odinstaluj aplikację"</string>
<string name="install_drop_target_label" msgid="2539096853673231757">"Zainstaluj"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Nie proponuj aplikacji"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Przypnij podpowiedź"</string>
diff --git a/res/values-pt-rPT/strings.xml b/res/values-pt-rPT/strings.xml
index 07b32b1..a8eadfe 100644
--- a/res/values-pt-rPT/strings.xml
+++ b/res/values-pt-rPT/strings.xml
@@ -29,6 +29,7 @@
<string name="home_screen" msgid="5629429142036709174">"Página inicial"</string>
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"Ecrã dividido"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"Informações da app para %1$s"</string>
+ <string name="split_app_usage_settings" msgid="7214375263347964093">"Definições de utilização para %1$s"</string>
<string name="save_app_pair" msgid="5647523853662686243">"Guardar par de apps"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"Este par de apps não é suportado neste dispositivo"</string>
@@ -84,6 +85,7 @@
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Desinstalar"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"Info. da app"</string>
<string name="install_private_system_shortcut_label" msgid="1616889277073184841">"Instalar em privado"</string>
+ <string name="uninstall_private_system_shortcut_label" msgid="8423460530441627982">"Desinstalar app"</string>
<string name="install_drop_target_label" msgid="2539096853673231757">"Instalar"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Não sugerir app"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Fixar previsão"</string>
diff --git a/res/values-pt/strings.xml b/res/values-pt/strings.xml
index d06806d..2833a3f 100644
--- a/res/values-pt/strings.xml
+++ b/res/values-pt/strings.xml
@@ -29,6 +29,8 @@
<string name="home_screen" msgid="5629429142036709174">"Início"</string>
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"Tela dividida"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"Informações do app %1$s"</string>
+ <!-- no translation found for split_app_usage_settings (7214375263347964093) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"Salvar par de apps"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"Este Par de apps não está disponível no dispositivo"</string>
@@ -84,6 +86,7 @@
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Desinstalar"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"Informações do app"</string>
<string name="install_private_system_shortcut_label" msgid="1616889277073184841">"Instalar em particular"</string>
+ <string name="uninstall_private_system_shortcut_label" msgid="8423460530441627982">"Desinstalar app"</string>
<string name="install_drop_target_label" msgid="2539096853673231757">"Instalar"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Não sugerir esse app"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Fixar previsão"</string>
diff --git a/res/values-ro/strings.xml b/res/values-ro/strings.xml
index fdd1f27..a3ee56e 100644
--- a/res/values-ro/strings.xml
+++ b/res/values-ro/strings.xml
@@ -29,6 +29,8 @@
<string name="home_screen" msgid="5629429142036709174">"Pagina de pornire"</string>
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"Ecran împărțit"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"Informații despre aplicație pentru %1$s"</string>
+ <!-- no translation found for split_app_usage_settings (7214375263347964093) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"Salvează perechea de aplicații"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"Perechea de aplicații nu este acceptată pe acest dispozitiv"</string>
@@ -84,6 +86,7 @@
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Dezinstalează"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"Informații despre aplicații"</string>
<string name="install_private_system_shortcut_label" msgid="1616889277073184841">"Instalează în privat"</string>
+ <string name="uninstall_private_system_shortcut_label" msgid="8423460530441627982">"Dezinstalează aplicația"</string>
<string name="install_drop_target_label" msgid="2539096853673231757">"Instalează"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Nu sugera aplicația"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Fixează predicția"</string>
diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml
index b797b32..7b99297 100644
--- a/res/values-ru/strings.xml
+++ b/res/values-ru/strings.xml
@@ -29,6 +29,8 @@
<string name="home_screen" msgid="5629429142036709174">"Главный экран"</string>
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"Разделить экран"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"Сведения о приложении \"%1$s\""</string>
+ <!-- no translation found for split_app_usage_settings (7214375263347964093) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"Сохранить приложения"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"Одновременно использовать эти два приложения на устройстве нельзя."</string>
@@ -84,6 +86,7 @@
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Удалить"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"О приложении"</string>
<string name="install_private_system_shortcut_label" msgid="1616889277073184841">"Частная установка"</string>
+ <string name="uninstall_private_system_shortcut_label" msgid="8423460530441627982">"Удалить приложение"</string>
<string name="install_drop_target_label" msgid="2539096853673231757">"Установить"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Не рекомендовать"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Закрепить рекомендацию"</string>
@@ -184,7 +187,7 @@
<string name="remote_action_failed" msgid="1383965239183576790">"Не удалось выполнить действие (<xliff:g id="WHAT">%1$s</xliff:g>)."</string>
<string name="private_space_label" msgid="2359721649407947001">"Частное пространство"</string>
<string name="private_space_secondary_label" msgid="9203933341714508907">"Нажмите, чтобы настроить или открыть"</string>
- <string name="ps_container_title" msgid="4391796149519594205">"Доступно только вам"</string>
+ <string name="ps_container_title" msgid="4391796149519594205">"Частный профиль"</string>
<string name="ps_container_settings" msgid="6059734123353320479">"Настройки личного пространства"</string>
<string name="ps_container_unlock_button_content_description" msgid="9181551784092204234">"Личное, разблокировано."</string>
<string name="ps_container_lock_button_content_description" msgid="5961993384382649530">"Личное, заблокировано."</string>
diff --git a/res/values-si/strings.xml b/res/values-si/strings.xml
index 9d85606..511d593 100644
--- a/res/values-si/strings.xml
+++ b/res/values-si/strings.xml
@@ -29,6 +29,8 @@
<string name="home_screen" msgid="5629429142036709174">"මුල් පිටුව"</string>
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"බෙදුම් තිරය"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"%1$s සඳහා යෙදුම් තතු"</string>
+ <!-- no translation found for split_app_usage_settings (7214375263347964093) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"යෙදුම් යුගල සුරකින්න"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"මෙම යෙදුම් යුගලය මෙම උපාංගයෙහි සහාය නොදක්වයි"</string>
@@ -84,6 +86,7 @@
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"අස්ථාපනය කරන්න"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"යෙදුම් තොරතුරු"</string>
<string name="install_private_system_shortcut_label" msgid="1616889277073184841">"පෞද්ගලිකව ස්ථාපනය කරන්න"</string>
+ <string name="uninstall_private_system_shortcut_label" msgid="8423460530441627982">"යෙදුම අස්ථාපනය කරන්න"</string>
<string name="install_drop_target_label" msgid="2539096853673231757">"ස්ථාපනය කරන්න"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"යෙදුම යෝජනා නොකරන්න"</string>
<string name="pin_prediction" msgid="4196423321649756498">"පුරෝකථනය අමුණන්න"</string>
diff --git a/res/values-sk/strings.xml b/res/values-sk/strings.xml
index 0d9001a..9602594 100644
--- a/res/values-sk/strings.xml
+++ b/res/values-sk/strings.xml
@@ -29,6 +29,8 @@
<string name="home_screen" msgid="5629429142036709174">"Domov"</string>
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"Rozdeliť obrazovku"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"Informácie o aplikácii pre %1$s"</string>
+ <!-- no translation found for split_app_usage_settings (7214375263347964093) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"Uložiť pár aplikácií"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"Tento pár aplikácií nie je v tomto zariadení podporovaný"</string>
@@ -84,6 +86,7 @@
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Odinštalovať"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"Info o aplikácii"</string>
<string name="install_private_system_shortcut_label" msgid="1616889277073184841">"Inštalovať v súkromí"</string>
+ <string name="uninstall_private_system_shortcut_label" msgid="8423460530441627982">"Odinštalovať aplikáciu"</string>
<string name="install_drop_target_label" msgid="2539096853673231757">"Inštalovať"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Nenavrhovať aplikáciu"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Pripnúť predpoveď"</string>
diff --git a/res/values-sl/strings.xml b/res/values-sl/strings.xml
index e16ec59..2b334aa 100644
--- a/res/values-sl/strings.xml
+++ b/res/values-sl/strings.xml
@@ -29,6 +29,7 @@
<string name="home_screen" msgid="5629429142036709174">"Začetni zaslon"</string>
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"Razdeljen zaslon"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"Podatki o aplikaciji za: %1$s"</string>
+ <string name="split_app_usage_settings" msgid="7214375263347964093">"Nastavitve uporabe za »%1$s«"</string>
<string name="save_app_pair" msgid="5647523853662686243">"Shrani par aplikacij"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"Ta par aplikacij ni podprt v tej napravi"</string>
@@ -84,6 +85,7 @@
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Odmesti"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"Podatki o aplikaciji"</string>
<string name="install_private_system_shortcut_label" msgid="1616889277073184841">"Namesti v zasebno"</string>
+ <string name="uninstall_private_system_shortcut_label" msgid="8423460530441627982">"Odmesti aplikacijo"</string>
<string name="install_drop_target_label" msgid="2539096853673231757">"Namesti"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Ne predlagaj"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Predvidevanje pripenjanja"</string>
diff --git a/res/values-sq/strings.xml b/res/values-sq/strings.xml
index 1f2aa26..fbeb205 100644
--- a/res/values-sq/strings.xml
+++ b/res/values-sq/strings.xml
@@ -29,6 +29,8 @@
<string name="home_screen" msgid="5629429142036709174">"Ekrani bazë"</string>
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"Ekrani i ndarë"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"Informacioni i aplikacionit për %1$s"</string>
+ <!-- no translation found for split_app_usage_settings (7214375263347964093) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"Ruaj çiftin e aplikacioneve"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"Ky çift aplikacionesh nuk mbështetet në këtë pajisje"</string>
@@ -84,6 +86,7 @@
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Çinstalo"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"Info mbi aplikacionin"</string>
<string name="install_private_system_shortcut_label" msgid="1616889277073184841">"Instalo në private"</string>
+ <string name="uninstall_private_system_shortcut_label" msgid="8423460530441627982">"Çinstalo aplikacionin"</string>
<string name="install_drop_target_label" msgid="2539096853673231757">"Instalo"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Mos sugjero aplikacion"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Gozhdo parashikimin"</string>
diff --git a/res/values-sr/strings.xml b/res/values-sr/strings.xml
index 3d0ad3b..8ae58eb 100644
--- a/res/values-sr/strings.xml
+++ b/res/values-sr/strings.xml
@@ -29,6 +29,7 @@
<string name="home_screen" msgid="5629429142036709174">"Почетни екран"</string>
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"Подељени екран"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"Информације о апликацији за: %1$s"</string>
+ <string name="split_app_usage_settings" msgid="7214375263347964093">"Подешавања потрошње за %1$s"</string>
<string name="save_app_pair" msgid="5647523853662686243">"Сачувај пар апликација"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"Овај пар апликација није подржан на овом уређају"</string>
@@ -84,6 +85,7 @@
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Деинсталирај"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"Подаци о апликацији"</string>
<string name="install_private_system_shortcut_label" msgid="1616889277073184841">"Инсталирај на приватни"</string>
+ <string name="uninstall_private_system_shortcut_label" msgid="8423460530441627982">"Деинсталирајте апликацију"</string>
<string name="install_drop_target_label" msgid="2539096853673231757">"Инсталирај"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Не предлажи апликацију"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Закачи предвиђање"</string>
diff --git a/res/values-sv/strings.xml b/res/values-sv/strings.xml
index ee2b328..d99fd86 100644
--- a/res/values-sv/strings.xml
+++ b/res/values-sv/strings.xml
@@ -29,6 +29,8 @@
<string name="home_screen" msgid="5629429142036709174">"Startskärm"</string>
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"Delad skärm"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"Appinformation för %1$s"</string>
+ <!-- no translation found for split_app_usage_settings (7214375263347964093) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"Spara app-par"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"De här apparna som ska användas tillsammans stöds inte på den här enheten"</string>
@@ -84,6 +86,7 @@
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Avinstallera"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"Info om appen"</string>
<string name="install_private_system_shortcut_label" msgid="1616889277073184841">"Installera i privat"</string>
+ <string name="uninstall_private_system_shortcut_label" msgid="8423460530441627982">"Avinstallera appen"</string>
<string name="install_drop_target_label" msgid="2539096853673231757">"Installera"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Föreslå inte app"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Fäst förslag"</string>
diff --git a/res/values-sw/strings.xml b/res/values-sw/strings.xml
index a7f5fdd..8893a90 100644
--- a/res/values-sw/strings.xml
+++ b/res/values-sw/strings.xml
@@ -29,6 +29,8 @@
<string name="home_screen" msgid="5629429142036709174">"Skrini ya kwanza"</string>
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"Gawa skrini"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"Maelezo ya programu ya %1$s"</string>
+ <!-- no translation found for split_app_usage_settings (7214375263347964093) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"Hifadhi jozi ya programu"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"Jozi hii ya programu haitumiki kwenye kifaa hiki"</string>
@@ -84,6 +86,7 @@
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Ondoa"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"Maelezo ya programu"</string>
<string name="install_private_system_shortcut_label" msgid="1616889277073184841">"Sakinisha faraghani"</string>
+ <string name="uninstall_private_system_shortcut_label" msgid="8423460530441627982">"Ondoa programu"</string>
<string name="install_drop_target_label" msgid="2539096853673231757">"Sakinisha"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Isipendekeze programu"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Bandika Utabiri"</string>
diff --git a/res/values-ta/strings.xml b/res/values-ta/strings.xml
index 06eaa44..fbd7880 100644
--- a/res/values-ta/strings.xml
+++ b/res/values-ta/strings.xml
@@ -29,6 +29,8 @@
<string name="home_screen" msgid="5629429142036709174">"முகப்பு"</string>
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"திரைப் பிரிப்பு"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"%1$sக்கான ஆப்ஸ் தகவல்கள்"</string>
+ <!-- no translation found for split_app_usage_settings (7214375263347964093) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"ஆப்ஸ் ஜோடியைச் சேமி"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"இந்தச் சாதனத்தில் இந்த ஆப்ஸ் ஜோடி ஆதரிக்கப்படவில்லை"</string>
@@ -84,6 +86,7 @@
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"நிறுவல் நீக்கு"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"ஆப்ஸ் தகவல்"</string>
<string name="install_private_system_shortcut_label" msgid="1616889277073184841">"தனிப்பட்டதில் நிறுவு"</string>
+ <string name="uninstall_private_system_shortcut_label" msgid="8423460530441627982">"ஆப்ஸை நிறுவல் நீக்கு"</string>
<string name="install_drop_target_label" msgid="2539096853673231757">"நிறுவு"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"பரிந்துரைக்காதே"</string>
<string name="pin_prediction" msgid="4196423321649756498">"கணிக்கப்பட்ட ஆப்ஸைப் பின் செய்தல்"</string>
diff --git a/res/values-te/strings.xml b/res/values-te/strings.xml
index 55aae20..11c2755 100644
--- a/res/values-te/strings.xml
+++ b/res/values-te/strings.xml
@@ -29,6 +29,8 @@
<string name="home_screen" msgid="5629429142036709174">"మొదటి ట్యాబ్"</string>
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"స్ప్లిట్ స్క్రీన్"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"%1$s కోసం యాప్ సమాచారం"</string>
+ <!-- no translation found for split_app_usage_settings (7214375263347964093) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"యాప్ పెయిర్ను సేవ్ చేయండి"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"ఈ పరికరంలో ఈ యాప్ పెయిర్ సపోర్ట్ చేయదు"</string>
@@ -84,6 +86,7 @@
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"అన్ఇన్స్టాల్ చేయండి"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"యాప్ సమాచారం"</string>
<string name="install_private_system_shortcut_label" msgid="1616889277073184841">"ప్రైవేట్ ఇన్స్టాల్"</string>
+ <string name="uninstall_private_system_shortcut_label" msgid="8423460530441627982">"యాప్ను అన్ఇన్స్టాల్ చేయండి"</string>
<string name="install_drop_target_label" msgid="2539096853673231757">"ఇన్స్టాల్ చేయండి"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"యాప్ సూచించకు"</string>
<string name="pin_prediction" msgid="4196423321649756498">"సూచనను పిన్ చేయండి"</string>
@@ -123,7 +126,7 @@
<string name="title_missing_notification_access" msgid="7503287056163941064">"నోటిఫికేషన్ యాక్సెస్ అవసరం"</string>
<string name="msg_missing_notification_access" msgid="281113995110910548">"నోటిఫికేషన్ డాట్లను చూపించడానికి <xliff:g id="NAME">%1$s</xliff:g>కు యాప్ నోటిఫికేషన్లను ఆన్ చేయండి"</string>
<string name="title_change_settings" msgid="1376365968844349552">"సెట్టింగ్లను మార్చు"</string>
- <string name="notification_dots_service_title" msgid="4284221181793592871">"నోటిఫికేషన్ డాట్లను చూపు"</string>
+ <string name="notification_dots_service_title" msgid="4284221181793592871">"నోటిఫికేషన్ డాట్లను చూపండి"</string>
<string name="developer_options_title" msgid="700788437593726194">"డెవలపర్ ఆప్షన్లు"</string>
<string name="auto_add_shortcuts_label" msgid="4926805029653694105">"యాప్ చిహ్నాలను మొదటి స్క్రీన్కు జోడించండి"</string>
<string name="auto_add_shortcuts_description" msgid="7117251166066978730">"కొత్త యాప్ల కోసం"</string>
diff --git a/res/values-th/strings.xml b/res/values-th/strings.xml
index 56864a6..d006e5e 100644
--- a/res/values-th/strings.xml
+++ b/res/values-th/strings.xml
@@ -29,6 +29,8 @@
<string name="home_screen" msgid="5629429142036709174">"หน้าแรก"</string>
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"แยกหน้าจอ"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"ข้อมูลแอปสำหรับ %1$s"</string>
+ <!-- no translation found for split_app_usage_settings (7214375263347964093) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"บันทึกคู่แอป"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"ไม่รองรับคู่แอปนี้ในอุปกรณ์เครื่องนี้"</string>
@@ -84,6 +86,7 @@
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"ถอนการติดตั้ง"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"ข้อมูลแอป"</string>
<string name="install_private_system_shortcut_label" msgid="1616889277073184841">"ติดตั้งในแบบส่วนตัว"</string>
+ <string name="uninstall_private_system_shortcut_label" msgid="8423460530441627982">"ถอนการติดตั้งแอป"</string>
<string name="install_drop_target_label" msgid="2539096853673231757">"ติดตั้ง"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"ไม่ต้องแนะนำแอป"</string>
<string name="pin_prediction" msgid="4196423321649756498">"ปักหมุดแอปที่คาดการณ์ไว้"</string>
diff --git a/res/values-tl/strings.xml b/res/values-tl/strings.xml
index 1a3d719..a497d4f 100644
--- a/res/values-tl/strings.xml
+++ b/res/values-tl/strings.xml
@@ -29,6 +29,8 @@
<string name="home_screen" msgid="5629429142036709174">"Home"</string>
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"Split screen"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"Impormasyon ng app para sa %1$s"</string>
+ <!-- no translation found for split_app_usage_settings (7214375263347964093) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"I-save ang app pair"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"Hindi sinusuportahan sa device na ito ang pares ng app na ito"</string>
@@ -84,6 +86,7 @@
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"I-uninstall"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"Impormasyon ng app"</string>
<string name="install_private_system_shortcut_label" msgid="1616889277073184841">"Pribadong i-install"</string>
+ <string name="uninstall_private_system_shortcut_label" msgid="8423460530441627982">"I-uninstall ang app"</string>
<string name="install_drop_target_label" msgid="2539096853673231757">"I-install"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Huwag magmungkahi"</string>
<string name="pin_prediction" msgid="4196423321649756498">"I-pin ang Hula"</string>
diff --git a/res/values-tr/strings.xml b/res/values-tr/strings.xml
index 6d3fc6f..301a754 100644
--- a/res/values-tr/strings.xml
+++ b/res/values-tr/strings.xml
@@ -29,6 +29,8 @@
<string name="home_screen" msgid="5629429142036709174">"Ana ekran"</string>
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"Bölünmüş ekran"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"%1$s uygulama bilgileri"</string>
+ <!-- no translation found for split_app_usage_settings (7214375263347964093) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"Uygulama çiftini kaydedin"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"Bu uygulama çifti bu cihazda desteklenmiyor"</string>
@@ -84,6 +86,7 @@
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Kaldır"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"Uygulama bilgileri"</string>
<string name="install_private_system_shortcut_label" msgid="1616889277073184841">"Özel olarak yükle"</string>
+ <string name="uninstall_private_system_shortcut_label" msgid="8423460530441627982">"Uygulamanın yüklemesini kaldır"</string>
<string name="install_drop_target_label" msgid="2539096853673231757">"Yükle"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Uygulamayı önerme"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Tahmini Sabitle"</string>
diff --git a/res/values-uk/strings.xml b/res/values-uk/strings.xml
index a69f4b0..3ff70b8 100644
--- a/res/values-uk/strings.xml
+++ b/res/values-uk/strings.xml
@@ -29,6 +29,8 @@
<string name="home_screen" msgid="5629429142036709174">"Головний екран"</string>
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"Розділити екран"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"Інформація про додаток для %1$s"</string>
+ <!-- no translation found for split_app_usage_settings (7214375263347964093) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"Зберегти пару додатків"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"Ці два додатки не можна одночасно використовувати на цьому пристрої"</string>
@@ -84,6 +86,7 @@
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Видалити додаток"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"Про додаток"</string>
<string name="install_private_system_shortcut_label" msgid="1616889277073184841">"Установити приватно"</string>
+ <string name="uninstall_private_system_shortcut_label" msgid="8423460530441627982">"Видалити додаток"</string>
<string name="install_drop_target_label" msgid="2539096853673231757">"Установити"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Не пропонувати додаток"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Закріпити передбачений додаток"</string>
diff --git a/res/values-ur/strings.xml b/res/values-ur/strings.xml
index 99d3a7b..653959e 100644
--- a/res/values-ur/strings.xml
+++ b/res/values-ur/strings.xml
@@ -29,6 +29,8 @@
<string name="home_screen" msgid="5629429142036709174">"ہوم"</string>
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"اسپلٹ اسکرین"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"%1$s کے لیے ایپ کی معلومات"</string>
+ <!-- no translation found for split_app_usage_settings (7214375263347964093) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"ایپس کے جوڑے کو محفوظ کریں"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"ایپس کا یہ جوڑا اس آلے پر تعاون یافتہ نہیں ہے"</string>
@@ -84,6 +86,7 @@
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"اَن انسٹال کریں"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"ایپ کی معلومات"</string>
<string name="install_private_system_shortcut_label" msgid="1616889277073184841">"پرائیویٹ میں انسٹال کریں"</string>
+ <string name="uninstall_private_system_shortcut_label" msgid="8423460530441627982">"ایپ کو اَن انسٹال کریں"</string>
<string name="install_drop_target_label" msgid="2539096853673231757">"انسٹال کریں"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"ایپ تجویز نہ کریں"</string>
<string name="pin_prediction" msgid="4196423321649756498">"پیشگوئی پن کریں"</string>
diff --git a/res/values-uz/strings.xml b/res/values-uz/strings.xml
index 2334a61..194822e 100644
--- a/res/values-uz/strings.xml
+++ b/res/values-uz/strings.xml
@@ -29,6 +29,8 @@
<string name="home_screen" msgid="5629429142036709174">"Bosh ekran"</string>
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"Ekranni ikkiga ajratish"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"%1$s ilovasi axboroti"</string>
+ <!-- no translation found for split_app_usage_settings (7214375263347964093) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"Ilova juftini saqlash"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"Bu ilova jufti ushbu qurilmada ishlamaydi"</string>
@@ -84,6 +86,7 @@
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"O‘chirib tashlash"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"Ilova haqida"</string>
<string name="install_private_system_shortcut_label" msgid="1616889277073184841">"Maxfiy oʻrnatish"</string>
+ <string name="uninstall_private_system_shortcut_label" msgid="8423460530441627982">"Ilovani oʻchirish"</string>
<string name="install_drop_target_label" msgid="2539096853673231757">"O‘rnatish"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Tavsiya qilinmasin"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Tavsiyani mahkamlash"</string>
diff --git a/res/values-vi/strings.xml b/res/values-vi/strings.xml
index feaddcd..f50b8e8 100644
--- a/res/values-vi/strings.xml
+++ b/res/values-vi/strings.xml
@@ -29,6 +29,8 @@
<string name="home_screen" msgid="5629429142036709174">"Màn hình chính"</string>
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"Chia đôi màn hình"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"Thông tin ứng dụng cho %1$s"</string>
+ <!-- no translation found for split_app_usage_settings (7214375263347964093) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"Lưu cặp ứng dụng"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"Cặp ứng dụng này không hoạt động được trên thiết bị này"</string>
@@ -84,6 +86,7 @@
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Gỡ cài đặt"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"Thông tin ứng dụng"</string>
<string name="install_private_system_shortcut_label" msgid="1616889277073184841">"Cài đặt ở chế độ riêng tư"</string>
+ <string name="uninstall_private_system_shortcut_label" msgid="8423460530441627982">"Gỡ cài đặt ứng dụng"</string>
<string name="install_drop_target_label" msgid="2539096853673231757">"Cài đặt"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Không gợi ý ứng dụng"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Ghim ứng dụng dự đoán"</string>
diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml
index 58533c3..b4c432b 100644
--- a/res/values-zh-rCN/strings.xml
+++ b/res/values-zh-rCN/strings.xml
@@ -29,6 +29,8 @@
<string name="home_screen" msgid="5629429142036709174">"主屏幕"</string>
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"分屏"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"%1$s 的应用信息"</string>
+ <!-- no translation found for split_app_usage_settings (7214375263347964093) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"保存应用组合"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"在该设备上无法使用此应用对"</string>
@@ -84,6 +86,7 @@
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"卸载"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"应用信息"</string>
<string name="install_private_system_shortcut_label" msgid="1616889277073184841">"安装到私密个人资料中"</string>
+ <string name="uninstall_private_system_shortcut_label" msgid="8423460530441627982">"卸载应用"</string>
<string name="install_drop_target_label" msgid="2539096853673231757">"安装"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"不要提供应用建议"</string>
<string name="pin_prediction" msgid="4196423321649756498">"固定预测的应用"</string>
diff --git a/res/values-zh-rHK/strings.xml b/res/values-zh-rHK/strings.xml
index 6070b4e..94b3ae6 100644
--- a/res/values-zh-rHK/strings.xml
+++ b/res/values-zh-rHK/strings.xml
@@ -29,6 +29,8 @@
<string name="home_screen" msgid="5629429142036709174">"主畫面"</string>
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"分割螢幕"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"%1$s 的應用程式資料"</string>
+ <!-- no translation found for split_app_usage_settings (7214375263347964093) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"儲存應用程式配對"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"此裝置不支援此應用程式配對"</string>
@@ -84,6 +86,7 @@
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"解除安裝"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"應用程式資料"</string>
<string name="install_private_system_shortcut_label" msgid="1616889277073184841">"安裝在私人資料夾中"</string>
+ <string name="uninstall_private_system_shortcut_label" msgid="8423460530441627982">"解除安裝應用程式"</string>
<string name="install_drop_target_label" msgid="2539096853673231757">"安裝"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"不要提供應用程式建議"</string>
<string name="pin_prediction" msgid="4196423321649756498">"固定預測"</string>
diff --git a/res/values-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml
index 0b40dd0..e0b7ab2 100644
--- a/res/values-zh-rTW/strings.xml
+++ b/res/values-zh-rTW/strings.xml
@@ -29,6 +29,8 @@
<string name="home_screen" msgid="5629429142036709174">"主畫面"</string>
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"分割畫面"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"「%1$s」的應用程式資訊"</string>
+ <!-- no translation found for split_app_usage_settings (7214375263347964093) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"儲存應用程式配對"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"這部裝置不支援這組應用程式配對"</string>
@@ -84,6 +86,7 @@
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"解除安裝"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"應用程式資訊"</string>
<string name="install_private_system_shortcut_label" msgid="1616889277073184841">"安裝在私人空間中"</string>
+ <string name="uninstall_private_system_shortcut_label" msgid="8423460530441627982">"解除安裝應用程式"</string>
<string name="install_drop_target_label" msgid="2539096853673231757">"安裝"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"不要提供應用程式建議"</string>
<string name="pin_prediction" msgid="4196423321649756498">"固定預測的應用程式"</string>
diff --git a/res/values-zu/strings.xml b/res/values-zu/strings.xml
index 99abe05..16d71a6 100644
--- a/res/values-zu/strings.xml
+++ b/res/values-zu/strings.xml
@@ -29,6 +29,8 @@
<string name="home_screen" msgid="5629429142036709174">"Ikhaya"</string>
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"Hlukanisa isikrini"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"Ulwazi lwe-App ye-%1$s"</string>
+ <!-- no translation found for split_app_usage_settings (7214375263347964093) -->
+ <skip />
<string name="save_app_pair" msgid="5647523853662686243">"Londoloza i-app ebhangqiwe"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"Lokhu kubhanqwa kwe-app akusekelwa kule divayisi"</string>
@@ -84,6 +86,7 @@
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Khipha"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"Ulwazi nge-app"</string>
<string name="install_private_system_shortcut_label" msgid="1616889277073184841">"Faka ngokugodliwe"</string>
+ <string name="uninstall_private_system_shortcut_label" msgid="8423460530441627982">"Khipha i-app"</string>
<string name="install_drop_target_label" msgid="2539096853673231757">"Faka"</string>
<string name="dismiss_prediction_label" msgid="3357562989568808658">"Ungaphakamisi uhlelo lokusebenza"</string>
<string name="pin_prediction" msgid="4196423321649756498">"Ukubikezela Iphinikhodi"</string>
@@ -122,7 +125,7 @@
<string name="notification_dots_desc_off" msgid="1760796511504341095">"Valiwe"</string>
<string name="title_missing_notification_access" msgid="7503287056163941064">"Ukufinyelela izaziso kuyadingeka"</string>
<string name="msg_missing_notification_access" msgid="281113995110910548">"Ukuze ubonisa amcashazi esaziso, vula izaziso zohlelo lokusebenza ze-<xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="title_change_settings" msgid="1376365968844349552">"Shintsha izilungiselelo"</string>
+ <string name="title_change_settings" msgid="1376365968844349552">"Shintsha amasethingi"</string>
<string name="notification_dots_service_title" msgid="4284221181793592871">"Bonisa amacashazi esaziso"</string>
<string name="developer_options_title" msgid="700788437593726194">"Izinketho zonjiniyela"</string>
<string name="auto_add_shortcuts_label" msgid="4926805029653694105">"Engeza izithonjana ze-app kusikrini sasekhaya"</string>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index e31a35f..aa83c01 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -320,6 +320,8 @@
<dimen name="bg_popup_item_height">52dp</dimen>
<dimen name="bg_popup_item_vertical_padding">12dp</dimen>
<dimen name="pre_drag_view_scale">6dp</dimen>
+ <!-- Minimum size of the widget dragged view to keep it visible under the finger. -->
+ <dimen name="widget_drag_view_min_scale_down_size">70dp</dimen>
<!-- an icon with shortcuts must be dragged this far before the container is removed. -->
<dimen name="deep_shortcuts_start_drag_threshold">16dp</dimen>
<!-- Possibly related to b/235886078, icon needs to be scaled up to match expected visual size of 32 dp -->
@@ -416,6 +418,9 @@
<dimen name="taskbar_running_app_indicator_height">0dp</dimen>
<dimen name="taskbar_running_app_indicator_width">0dp</dimen>
<dimen name="taskbar_running_app_indicator_top_margin">0dp</dimen>
+ <dimen name="taskbar_minimized_app_indicator_height">0dp</dimen>
+ <dimen name="taskbar_minimized_app_indicator_width">0dp</dimen>
+ <dimen name="taskbar_minimized_app_indicator_top_margin">0dp</dimen>
<!-- Transient taskbar (placeholders to compile in Launcher3 without Quickstep) -->
<dimen name="transient_taskbar_padding">0dp</dimen>
diff --git a/res/values/id.xml b/res/values/id.xml
index 7bb9396..28496b5 100644
--- a/res/values/id.xml
+++ b/res/values/id.xml
@@ -19,9 +19,6 @@
<item type="id" name="view_type_widgets_space" />
<item type="id" name="view_type_widgets_list" />
<item type="id" name="view_type_widgets_header" />
- <!-- Used for A11y actions in staged split to identify each task uniquely -->
- <item type="id" name="split_topLeft_appInfo" />
- <item type="id" name="split_bottomRight_appInfo" />
<!-- Accessibility actions -->
<item type="id" name="action_remove" />
@@ -37,6 +34,12 @@
<item type="id" name="action_remote_action_shortcut" />
<item type="id" name="action_dismiss_prediction" />
<item type="id" name="action_pin_prediction"/>
+ <item type="id" name="action_close"/>
+ <!-- Used for A11y actions in staged split to identify each task uniquely -->
+ <item type="id" name="action_app_info_top_left" />
+ <item type="id" name="action_app_info_bottom_right" />
+ <item type="id" name="action_digital_wellbeing_top_left" />
+ <item type="id" name="action_digital_wellbeing_bottom_right" />
<!-- QSB IDs. DO not change -->
<item type="id" name="search_container_workspace" />
diff --git a/res/values/strings.xml b/res/values/strings.xml
index ef0f0d8..a9cca6d 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -41,6 +41,7 @@
<!-- Title for an option to enter split screen mode for a given app -->
<string name="recent_task_option_split_screen">Split screen</string>
<string name="split_app_info_accessibility">App info for %1$s</string>
+ <string name="split_app_usage_settings">Usage settings for %1$s</string>
<!-- App pairs -->
<string name="save_app_pair">Save app pair</string>
@@ -497,8 +498,4 @@
<string name="ps_add_button_label">Install</string>
<!-- Content description for install app icon -->
<string name="ps_add_button_content_description">Install apps to Private Space</string>
-
- <!-- Strings for bubble bar -->
- <!-- content description for the overflow bubble [CHAR_LIMIT=none] -->
- <string name="bubble_bar_overflow_description">Overflow</string>
</resources>
diff --git a/src/com/android/launcher3/BaseDraggingActivity.java b/src/com/android/launcher3/BaseDraggingActivity.java
index cf93a79..8585b66 100644
--- a/src/com/android/launcher3/BaseDraggingActivity.java
+++ b/src/com/android/launcher3/BaseDraggingActivity.java
@@ -147,7 +147,8 @@
@Override
public void onDisplayInfoChanged(Context context, Info info, int flags) {
- if ((flags & CHANGE_ROTATION) != 0 && mDeviceProfile.updateIsSeascape(this)) {
+ if ((flags & CHANGE_ROTATION) != 0 && mDeviceProfile.isVerticalBarLayout()) {
+ mDeviceProfile.updateIsSeascape(this);
reapplyUi();
}
}
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index 2a8298f..7d09164 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -186,9 +186,20 @@
// These fields, related to showing running apps, are only used for Taskbar.
private final Size mRunningAppIndicatorSize;
private final int mRunningAppIndicatorTopMargin;
+ private final Size mMinimizedAppIndicatorSize;
+ private final int mMinimizedAppIndicatorTopMargin;
private final Paint mRunningAppIndicatorPaint;
private final Rect mRunningAppIconBounds = new Rect();
- private boolean mIsRunning;
+ private RunningAppState mRunningAppState;
+
+ /**
+ * Various options for the running state of an app.
+ */
+ public enum RunningAppState {
+ NOT_RUNNING,
+ RUNNING,
+ MINIMIZED,
+ }
@ViewDebug.ExportedProperty(category = "launcher")
private boolean mStayPressed;
@@ -259,9 +270,16 @@
mRunningAppIndicatorSize = new Size(
getResources().getDimensionPixelSize(R.dimen.taskbar_running_app_indicator_width),
getResources().getDimensionPixelSize(R.dimen.taskbar_running_app_indicator_height));
+ mMinimizedAppIndicatorSize = new Size(
+ getResources().getDimensionPixelSize(R.dimen.taskbar_minimized_app_indicator_width),
+ getResources().getDimensionPixelSize(
+ R.dimen.taskbar_minimized_app_indicator_height));
mRunningAppIndicatorTopMargin =
getResources().getDimensionPixelSize(
R.dimen.taskbar_running_app_indicator_top_margin);
+ mMinimizedAppIndicatorTopMargin =
+ getResources().getDimensionPixelSize(
+ R.dimen.taskbar_minimized_app_indicator_top_margin);
mRunningAppIndicatorPaint = new Paint();
mRunningAppIndicatorPaint.setColor(getResources().getColor(
R.color.taskbar_running_app_indicator_color, context.getTheme()));
@@ -414,8 +432,8 @@
/** Updates whether the app this view represents is currently running. */
@UiThread
- public void updateRunningState(boolean isRunning) {
- mIsRunning = isRunning;
+ public void updateRunningState(RunningAppState runningAppState) {
+ mRunningAppState = runningAppState;
}
protected void setItemInfo(ItemInfoWithIcon itemInfo) {
@@ -667,18 +685,20 @@
/** Draws a line under the app icon if this is representing a running app in Desktop Mode. */
protected void drawRunningAppIndicatorIfNecessary(Canvas canvas) {
- if (!mIsRunning || mDisplay != DISPLAY_TASKBAR) {
+ if (mRunningAppState == RunningAppState.NOT_RUNNING || mDisplay != DISPLAY_TASKBAR) {
return;
}
getIconBounds(mRunningAppIconBounds);
// TODO(b/333872717): update color, shape, and size of indicator
- int indicatorTop = mRunningAppIconBounds.bottom + mRunningAppIndicatorTopMargin;
- canvas.drawRect(
- mRunningAppIconBounds.centerX() - mRunningAppIndicatorSize.getWidth() / 2,
- indicatorTop,
- mRunningAppIconBounds.centerX() + mRunningAppIndicatorSize.getWidth() / 2,
- indicatorTop + mRunningAppIndicatorSize.getHeight(),
- mRunningAppIndicatorPaint);
+ boolean isMinimized = mRunningAppState == RunningAppState.MINIMIZED;
+ int indicatorTop =
+ mRunningAppIconBounds.bottom + (isMinimized ? mMinimizedAppIndicatorTopMargin
+ : mRunningAppIndicatorTopMargin);
+ final Size indicatorSize =
+ isMinimized ? mMinimizedAppIndicatorSize : mRunningAppIndicatorSize;
+ canvas.drawRect(mRunningAppIconBounds.centerX() - indicatorSize.getWidth() / 2,
+ indicatorTop, mRunningAppIconBounds.centerX() + indicatorSize.getWidth() / 2,
+ indicatorTop + indicatorSize.getHeight(), mRunningAppIndicatorPaint);
}
@Override
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index a667c96..0daabb1 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -32,6 +32,7 @@
import static com.android.launcher3.testing.shared.ResourceUtils.INVALID_RESOURCE_HANDLE;
import static com.android.launcher3.testing.shared.ResourceUtils.pxFromDp;
import static com.android.launcher3.testing.shared.ResourceUtils.roundPxValueFromFloat;
+import static com.android.wm.shell.Flags.enableTinyTaskbar;
import android.annotation.SuppressLint;
import android.content.Context;
@@ -353,7 +354,7 @@
isTablet = info.isTablet(windowBounds);
isPhone = !isTablet;
isTwoPanels = isTablet && isMultiDisplay;
- isTaskbarPresent = isTablet
+ isTaskbarPresent = (isTablet || (enableTinyTaskbar() && isGestureMode))
&& WindowManagerProxy.INSTANCE.get(context).isTaskbarDrawnInProcess();
// Some more constants.
@@ -2402,7 +2403,7 @@
mTransposeLayoutWithOrientation = !mInfo.isTablet(mWindowBounds);
}
if (mIsGestureMode == null) {
- mIsGestureMode = mInfo.navigationMode.hasGestures;
+ mIsGestureMode = mInfo.getNavigationMode().hasGestures;
}
if (mDotRendererCache == null) {
mDotRendererCache = new SparseArray<>();
diff --git a/src/com/android/launcher3/Hotseat.java b/src/com/android/launcher3/Hotseat.java
index 37737d8..8546454 100644
--- a/src/com/android/launcher3/Hotseat.java
+++ b/src/com/android/launcher3/Hotseat.java
@@ -82,6 +82,10 @@
return mHasVerticalHotseat ? (getCountY() - (rank + 1)) : 0;
}
+ boolean isHasVerticalHotseat() {
+ return mHasVerticalHotseat;
+ }
+
public void resetLayout(boolean hasVerticalHotseat) {
ActivityContext activityContext = ActivityContext.lookupContext(getContext());
boolean bubbleBarEnabled = activityContext.isBubbleBarEnabled();
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index b89d05e..2b30dc4 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -96,9 +96,7 @@
import static com.android.launcher3.popup.SystemShortcut.WIDGETS;
import static com.android.launcher3.states.RotationHelper.REQUEST_LOCK;
import static com.android.launcher3.states.RotationHelper.REQUEST_NONE;
-import static com.android.launcher3.testing.shared.TestProtocol.CLOCK_ICON_DRAWABLE_LEAKING;
import static com.android.launcher3.testing.shared.TestProtocol.LAUNCHER_ACTIVITY_STOPPED_MESSAGE;
-import static com.android.launcher3.testing.shared.TestProtocol.testLogD;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.ItemInfoMatcher.forFolderMatch;
import static com.android.launcher3.util.SettingsCache.TOUCHPAD_NATURAL_SCROLLING;
@@ -163,6 +161,7 @@
import androidx.annotation.StringRes;
import androidx.annotation.UiThread;
import androidx.annotation.VisibleForTesting;
+import androidx.core.os.BuildCompat;
import androidx.window.embedding.RuleController;
import com.android.launcher3.DropTarget.DragObject;
@@ -423,7 +422,6 @@
@Override
@TargetApi(Build.VERSION_CODES.S)
protected void onCreate(Bundle savedInstanceState) {
- testLogD(CLOCK_ICON_DRAWABLE_LEAKING, "onCreate: instance=" + this);
mStartupLatencyLogger = createStartupLatencyLogger(
sIsNewProcess
? LockedUserState.get(this).isUserUnlockedAtLauncherStartup()
@@ -589,7 +587,8 @@
setTitle(R.string.home_screen);
mStartupLatencyLogger.logEnd(LAUNCHER_LATENCY_STARTUP_ACTIVITY_ON_CREATE);
- if (com.android.launcher3.Flags.enableTwoPaneLauncherSettings()) {
+ if (BuildCompat.isAtLeastV()
+ && com.android.launcher3.Flags.enableTwoPaneLauncherSettings()) {
RuleController.getInstance(this).setRules(
RuleController.parseRules(this, R.xml.split_configuration));
}
@@ -1082,7 +1081,6 @@
@Override
protected void onStart() {
- testLogD(CLOCK_ICON_DRAWABLE_LEAKING, "onStart: instance=" + this);
TraceHelper.INSTANCE.beginSection(ON_START_EVT);
super.onStart();
if (!mDeferOverlayCallbacks) {
@@ -1096,7 +1094,6 @@
@Override
@CallSuper
protected void onDeferredResumed() {
- testLogD(CLOCK_ICON_DRAWABLE_LEAKING, "onDeferredResumed: instance=" + this);
logStopAndResume(true /* isResume */);
// Process any items that were added while Launcher was away.
@@ -1268,11 +1265,7 @@
}
// Set screen title for Talkback
- if (state == ALL_APPS) {
- setTitle(R.string.all_apps_label);
- } else {
- setTitle(R.string.home_screen);
- }
+ setTitle(state.getTitle());
}
/**
@@ -1284,7 +1277,6 @@
@Override
protected void onResume() {
- testLogD(CLOCK_ICON_DRAWABLE_LEAKING, "onResume: instance=" + this);
TraceHelper.INSTANCE.beginSection(ON_RESUME_EVT);
super.onResume();
@@ -1300,7 +1292,6 @@
@Override
protected void onPause() {
- testLogD(CLOCK_ICON_DRAWABLE_LEAKING, "onPause: instance=" + this);
// Ensure that items added to Launcher are queued until Launcher returns
ItemInstallQueue.INSTANCE.get(this).pauseModelPush(FLAG_ACTIVITY_PAUSED);
@@ -1783,7 +1774,6 @@
@Override
public void onDestroy() {
- testLogD(CLOCK_ICON_DRAWABLE_LEAKING, "onDestroy: instance=" + this);
super.onDestroy();
ACTIVITY_TRACKER.onActivityDestroyed(this);
@@ -2793,7 +2783,7 @@
}
private void updateDisallowBack() {
- if (Flags.enableDesktopWindowingMode()) {
+ if (BuildCompat.isAtLeastV() && Flags.enableDesktopWindowingMode()) {
// TODO(b/330183377) disable back in launcher when when we productionize
return;
}
@@ -3150,4 +3140,4 @@
}
// End of Getters and Setters
-}
\ No newline at end of file
+}
diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java
index a4ae1c8..3b8ff62 100644
--- a/src/com/android/launcher3/LauncherAppState.java
+++ b/src/com/android/launcher3/LauncherAppState.java
@@ -39,6 +39,7 @@
import android.util.Log;
import androidx.annotation.Nullable;
+import androidx.core.os.BuildCompat;
import com.android.launcher3.graphics.IconShape;
import com.android.launcher3.icons.IconCache;
@@ -107,7 +108,7 @@
mOnTerminateCallback.add(() ->
mContext.getSystemService(LauncherApps.class).unregisterCallback(callbacks));
- if (Flags.enableSupportForArchiving()) {
+ if (BuildCompat.isAtLeastV() && Flags.enableSupportForArchiving()) {
ArchiveCompatibilityParams params = new ArchiveCompatibilityParams();
params.setEnableUnarchivalConfirmation(false);
launcherApps.setArchiveCompatibility(params);
diff --git a/src/com/android/launcher3/LauncherState.java b/src/com/android/launcher3/LauncherState.java
index 72a3c53..d2d56f2 100644
--- a/src/com/android/launcher3/LauncherState.java
+++ b/src/com/android/launcher3/LauncherState.java
@@ -38,6 +38,7 @@
import android.view.animation.Interpolator;
import androidx.annotation.FloatRange;
+import androidx.annotation.StringRes;
import com.android.launcher3.statemanager.BaseState;
import com.android.launcher3.statemanager.StateManager;
@@ -369,6 +370,10 @@
return launcher.getWorkspace().getCurrentPageDescription();
}
+ public @StringRes int getTitle() {
+ return R.string.home_screen;
+ }
+
public PageAlphaProvider getWorkspacePageAlphaProvider(Launcher launcher) {
if ((this != NORMAL && this != HINT_STATE)
|| !launcher.getDeviceProfile().shouldFadeAdjacentWorkspaceScreens()) {
diff --git a/src/com/android/launcher3/ModelCallbacks.kt b/src/com/android/launcher3/ModelCallbacks.kt
index 7d6d154..13062b6 100644
--- a/src/com/android/launcher3/ModelCallbacks.kt
+++ b/src/com/android/launcher3/ModelCallbacks.kt
@@ -3,6 +3,7 @@
import android.annotation.TargetApi
import android.os.Build
import android.os.Trace
+import android.util.Log
import androidx.annotation.UiThread
import com.android.launcher3.Flags.enableSmartspaceRemovalToggle
import com.android.launcher3.LauncherConstants.TraceEvents
@@ -29,6 +30,8 @@
import com.android.launcher3.widget.model.WidgetsListBaseEntry
import java.util.function.Predicate
+private const val TAG = "ModelCallbacks"
+
class ModelCallbacks(private var launcher: Launcher) : BgDataModel.Callbacks {
var synchronouslyBoundPages = LIntSet()
@@ -66,6 +69,13 @@
launcher.workspace.removeAllWorkspaceScreens()
// Avoid clearing the widget update listeners for staying up-to-date with widget info
launcher.appWidgetHolder.clearWidgetViews()
+ // TODO(b/335141365): Remove this log after the bug is fixed.
+ Log.d(
+ TAG,
+ "startBinding: " +
+ "hotseat layout was vertical: ${launcher.hotseat?.isHasVerticalHotseat}" +
+ " and is setting to ${launcher.deviceProfile.isVerticalBarLayout}"
+ )
launcher.hotseat?.resetLayout(launcher.deviceProfile.isVerticalBarLayout)
TraceHelper.INSTANCE.endSection()
}
@@ -142,7 +152,10 @@
launcher.viewCache.setCacheSize(R.layout.folder_page, 2)
TraceHelper.INSTANCE.endSection()
launcher.workspace.removeExtraEmptyScreen(/* stripEmptyScreens= */ true)
- launcher.workspace.pageIndicator.setPauseScroll(/*pause=*/ false, deviceProfile.isTwoPanels)
+ launcher.workspace.pageIndicator.setPauseScroll(
+ /*pause=*/ false,
+ deviceProfile.isTwoPanels
+ )
}
/**
@@ -290,8 +303,7 @@
val widgetsListBaseEntry: WidgetsListBaseEntry =
launcher.popupDataProvider.allWidgets.firstOrNull { item: WidgetsListBaseEntry ->
item.mPkgItem.packageName == BuildConfig.APPLICATION_ID
- }
- ?: return
+ } ?: return
val info =
PendingAddWidgetInfo(
@@ -315,16 +327,14 @@
)
val firstScreenPosition = 0
if (
- (isFirstPagePinnedItemEnabled &&
- !SHOULD_SHOW_FIRST_PAGE_WIDGET) &&
+ (isFirstPagePinnedItemEnabled && !SHOULD_SHOW_FIRST_PAGE_WIDGET) &&
orderedScreenIds.indexOf(FIRST_SCREEN_ID) != firstScreenPosition
) {
orderedScreenIds.removeValue(FIRST_SCREEN_ID)
orderedScreenIds.add(firstScreenPosition, FIRST_SCREEN_ID)
} else if (
- (!isFirstPagePinnedItemEnabled ||
- SHOULD_SHOW_FIRST_PAGE_WIDGET)
- && orderedScreenIds.isEmpty
+ (!isFirstPagePinnedItemEnabled || SHOULD_SHOW_FIRST_PAGE_WIDGET) &&
+ orderedScreenIds.isEmpty
) {
// If there are no screens, we need to have an empty screen
launcher.workspace.addExtraEmptyScreens()
@@ -380,7 +390,7 @@
}
orderedScreenIds
.filterNot { screenId ->
- isFirstPagePinnedItemEnabled &&
+ isFirstPagePinnedItemEnabled &&
!SHOULD_SHOW_FIRST_PAGE_WIDGET &&
screenId == WorkspaceLayoutManager.FIRST_SCREEN_ID
}
diff --git a/src/com/android/launcher3/WorkspaceLayoutManager.java b/src/com/android/launcher3/WorkspaceLayoutManager.java
index 4768773..f11a88f 100644
--- a/src/com/android/launcher3/WorkspaceLayoutManager.java
+++ b/src/com/android/launcher3/WorkspaceLayoutManager.java
@@ -58,6 +58,9 @@
int screenId = presenterPos.screenId;
x = getHotseat().getCellXFromOrder(screenId);
y = getHotseat().getCellYFromOrder(screenId);
+ // TODO(b/335141365): Remove this log after the bug is fixed.
+ Log.d(TAG, "addInScreenFromBind: hotseat inflation with x = " + x
+ + " and y = " + y);
}
addInScreen(child, info.container, presenterPos.screenId, x, y, info.spanX, info.spanY);
}
diff --git a/src/com/android/launcher3/allapps/BaseAllAppsAdapter.java b/src/com/android/launcher3/allapps/BaseAllAppsAdapter.java
index 16630967..98ca420 100644
--- a/src/com/android/launcher3/allapps/BaseAllAppsAdapter.java
+++ b/src/com/android/launcher3/allapps/BaseAllAppsAdapter.java
@@ -267,13 +267,15 @@
PrivateProfileManager privateProfileManager = mApps.getPrivateProfileManager();
if (privateProfileManager != null) {
// Set the alpha of the private space icon to 0 upon expanding the header so the
- // alpha can animate -> 1.
+ // alpha can animate -> 1. This should only be in effect when doing a
+ // transitioning between Locked/Unlocked state.
boolean isPrivateSpaceItem =
privateProfileManager.isPrivateSpaceItem(adapterItem);
if (icon.getAlpha() == 0 || icon.getAlpha() == 1) {
icon.setAlpha(isPrivateSpaceItem
- && (privateProfileManager.getAnimationScrolling() ||
- privateProfileManager.getAnimate())
+ && privateProfileManager.isStateTransitioning()
+ && (privateProfileManager.isScrolling() ||
+ privateProfileManager.getReadyToAnimate())
&& privateProfileManager.getCurrentState() == STATE_ENABLED
? 0 : 1);
}
@@ -298,7 +300,7 @@
case VIEW_TYPE_PRIVATE_SPACE_HEADER:
RelativeLayout psHeaderLayout = holder.itemView.findViewById(
R.id.ps_header_layout);
- mApps.getPrivateProfileManager().addPrivateSpaceHeaderViewElements(psHeaderLayout);
+ mApps.getPrivateProfileManager().bindPrivateSpaceHeaderViewElements(psHeaderLayout);
AdapterItem adapterItem = mApps.getAdapterItems().get(position);
int roundRegions = ROUND_TOP_LEFT | ROUND_TOP_RIGHT;
if (mApps.getPrivateProfileManager().getCurrentState() == STATE_DISABLED) {
diff --git a/src/com/android/launcher3/allapps/PrivateProfileManager.java b/src/com/android/launcher3/allapps/PrivateProfileManager.java
index 27340a3..5cacf60 100644
--- a/src/com/android/launcher3/allapps/PrivateProfileManager.java
+++ b/src/com/android/launcher3/allapps/PrivateProfileManager.java
@@ -20,7 +20,6 @@
import static android.view.View.INVISIBLE;
import static android.view.View.VISIBLE;
-import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_PRIVATESPACE;
import static com.android.launcher3.allapps.ActivityAllAppsContainerView.AdapterHolder.MAIN;
import static com.android.launcher3.allapps.BaseAllAppsAdapter.VIEW_TYPE_ICON;
import static com.android.launcher3.allapps.BaseAllAppsAdapter.VIEW_TYPE_PRIVATE_SPACE_HEADER;
@@ -30,7 +29,6 @@
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_PRIVATE_SPACE_LOCK_ANIMATION_BEGIN;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_PRIVATE_SPACE_LOCK_ANIMATION_END;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_PRIVATE_SPACE_LOCK_TAP;
-import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_PRIVATE_SPACE_SETTINGS_TAP;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_PRIVATE_SPACE_UNLOCK_ANIMATION_BEGIN;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_PRIVATE_SPACE_UNLOCK_ANIMATION_END;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_PRIVATE_SPACE_UNLOCK_TAP;
@@ -52,7 +50,6 @@
import android.os.UserManager;
import android.view.View;
import android.view.ViewGroup;
-import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.TextView;
@@ -104,6 +101,7 @@
private static final int LOCK_TEXT_OPACITY_DELAY = 500;
private static final int MASK_VIEW_DELAY = 400;
private static final int NO_DELAY = 0;
+ private static final int CONTAINER_OPACITY_DURATION = 150;
private final ActivityAllAppsContainerView<?> mAllApps;
private final Predicate<UserHandle> mPrivateProfileMatcher;
private final int mPsHeaderHeight;
@@ -114,16 +112,21 @@
public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
if (newState == RecyclerView.SCROLL_STATE_IDLE) {
- mAnimationScrolling = false;
+ mIsScrolling = false;
}
}
};
private Intent mAppInstallerIntent = new Intent();
private PrivateAppsSectionDecorator mPrivateAppsSectionDecorator;
private boolean mPrivateSpaceSettingsAvailable;
+ // Returns if the animation is currently running.
private boolean mIsAnimationRunning;
- private boolean mAnimate;
- private boolean mAnimationScrolling;
+ // mAnimate denotes if private space is ready to be animated.
+ private boolean mReadyToAnimate;
+ // Returns when the recyclerView is currently scrolling.
+ private boolean mIsScrolling;
+ // mIsStateTransitioning indicates that private space is transitioning between states.
+ private boolean mIsStateTransitioning;
private Runnable mOnPSHeaderAdded;
@Nullable
private RelativeLayout mPSHeader;
@@ -191,23 +194,6 @@
mAllApps.mAH.get(MAIN).mAdapter.notifyItemInserted(adapterItems.size() - 1);
}
- /**
- * Disables quiet mode for Private Space User Profile.
- * When called from search, a runnable is set and executed in the {@link #reset()} method, when
- * Launcher receives update about profile availability.
- * The runnable is only executed once, and reset after execution.
- * In case the method is called again, before the previously set runnable was executed,
- * the runnable will be updated.
- */
- public void unlockPrivateProfile() {
- setQuietMode(false);
- }
-
- /** Enables quiet mode for Private Space User Profile. */
- void lockPrivateProfile() {
- setQuietMode(true);
- }
-
/** Whether private profile should be hidden on Launcher. */
public boolean isPrivateSpaceHidden() {
return getCurrentState() == STATE_DISABLED && SettingsCache.INSTANCE
@@ -220,46 +206,25 @@
* when animation is not running.
*/
public void reset() {
+ // Ensure the state of the header views is what it should be before animating.
+ updateView();
getMainRecyclerView().setChildAttachedConsumer(null);
int previousState = getCurrentState();
boolean isEnabled = !mAllApps.getAppsStore()
.hasModelFlag(FLAG_PRIVATE_PROFILE_QUIET_MODE_ENABLED);
int updatedState = isEnabled ? STATE_ENABLED : STATE_DISABLED;
setCurrentState(updatedState);
- mFloatingMaskView = null;
- if (mPSHeader != null) {
- mPSHeader.setAlpha(1);
+ if (Flags.privateSpaceAddFloatingMaskView()) {
+ mFloatingMaskView = null;
}
- if (transitioningFromLockedToUnlocked(previousState, updatedState)) {
+ // It's possible that previousState is 0 when reset is first called.
+ mIsStateTransitioning = previousState != STATE_UNKNOWN && previousState != updatedState;
+ if (previousState == STATE_DISABLED && updatedState == STATE_ENABLED) {
postUnlock();
- } else if (transitioningFromUnlockedToLocked(previousState, updatedState)){
+ } else if (previousState == STATE_ENABLED && updatedState == STATE_DISABLED){
executeLock();
}
- resetPrivateSpaceDecorator(updatedState);
- }
-
- /**
- * Opens the Private Space Settings Page.
- *
- * @param view the view that was clicked to open the settings page and which will be the same
- * view to animate back. Otherwise if there is no view, simply start the activity.
- */
- public void openPrivateSpaceSettings(View view) {
- if (mPrivateSpaceSettingsAvailable) {
- Context context = mAllApps.getContext();
- Intent intent = ApiWrapper.INSTANCE.get(context).getPrivateSpaceSettingsIntent();
- if (view == null) {
- context.startActivity(intent);
- return;
- }
- ActivityContext activityContext = ActivityContext.lookupContext(context);
- AppInfo itemInfo = new AppInfo();
- itemInfo.id = CONTAINER_PRIVATESPACE;
- itemInfo.componentName = intent.getComponent();
- itemInfo.container = CONTAINER_PRIVATESPACE;
- view.setTag(itemInfo);
- activityContext.startActivitySafely(view, intent, itemInfo);
- }
+ addPrivateSpaceDecorator(updatedState);
}
/** Returns whether or not Private Space Settings Page is available. */
@@ -292,8 +257,9 @@
setPrivateSpaceSettingsAvailable(apiWrapper.getPrivateSpaceSettingsIntent() != null);
}
+ /** Adds a private space decorator only when STATE_ENABLED. */
@VisibleForTesting
- void resetPrivateSpaceDecorator(int updatedState) {
+ void addPrivateSpaceDecorator(int updatedState) {
ActivityAllAppsContainerView<?>.AdapterHolder mainAdapterHolder = mAllApps.mAH.get(MAIN);
if (updatedState == STATE_ENABLED) {
// Create a new decorator instance if not already available.
@@ -310,18 +276,13 @@
}
// Add Private Space Decorator to the Recycler view.
mainAdapterHolder.mRecyclerView.addItemDecoration(mPrivateAppsSectionDecorator);
- } else {
- // Remove Private Space Decorator from the Recycler view.
- if (mPrivateAppsSectionDecorator != null && !mIsAnimationRunning) {
- mainAdapterHolder.mRecyclerView.removeItemDecoration(mPrivateAppsSectionDecorator);
- }
}
}
@Override
public void setQuietMode(boolean enable) {
super.setQuietMode(enable);
- mAnimate = true;
+ mReadyToAnimate = true;
}
/**
@@ -343,7 +304,7 @@
void setAnimationRunning(boolean isAnimationRunning) {
if (!isAnimationRunning) {
- mAnimate = false;
+ mReadyToAnimate = false;
}
mIsAnimationRunning = isAnimationRunning;
}
@@ -352,14 +313,6 @@
return mIsAnimationRunning;
}
- private boolean transitioningFromLockedToUnlocked(int previousState, int updatedState) {
- return previousState == STATE_DISABLED && updatedState == STATE_ENABLED;
- }
-
- private boolean transitioningFromUnlockedToLocked(int previousState, int updatedState) {
- return previousState == STATE_ENABLED && updatedState == STATE_DISABLED;
- }
-
@Override
public Predicate<UserHandle> getUserMatcher() {
return mPrivateProfileMatcher;
@@ -378,7 +331,7 @@
}
/** Add Private Space Header view elements based upon {@link UserProfileState} */
- public void addPrivateSpaceHeaderViewElements(RelativeLayout parent) {
+ public void bindPrivateSpaceHeaderViewElements(RelativeLayout parent) {
mPSHeader = parent;
if (mOnPSHeaderAdded != null) {
MAIN_EXECUTOR.execute(mOnPSHeaderAdded);
@@ -386,100 +339,70 @@
}
// Set the transition duration for the settings and lock button to animate.
ViewGroup settingAndLockGroup = mPSHeader.findViewById(R.id.settingsAndLockGroup);
- if (mAnimate) {
+ if (mReadyToAnimate) {
enableLayoutTransition(settingAndLockGroup);
} else {
// Ensure any unwanted animations to not happen.
settingAndLockGroup.setLayoutTransition(null);
}
-
- //Add quietMode image and action for lock/unlock button
- ViewGroup lockButton = mPSHeader.findViewById(R.id.ps_lock_unlock_button);
- assert lockButton != null;
- addLockButton(lockButton);
-
- //Trigger lock/unlock action from header.
- addHeaderOnClickListener(mPSHeader);
-
- //Add image and action for private space settings button
- ImageButton settingsButton = mPSHeader.findViewById(R.id.ps_settings_button);
- assert settingsButton != null;
- addPrivateSpaceSettingsButton(settingsButton);
-
- //Add image for private space transitioning view
- ImageView transitionView = parent.findViewById(R.id.ps_transition_image);
- assert transitionView != null;
- addTransitionImage(transitionView);
+ updateView();
}
- /**
- * Adds the quietModeButton and attach onClickListener for the header to animate different
- * states when clicked.
- */
- private void addLockButton(ViewGroup lockButton) {
- TextView lockText = lockButton.findViewById(R.id.lock_text);
- switch (getCurrentState()) {
+ /** Update the states of the views that make up the header at the state it is called in. */
+ private void updateView() {
+ if (mPSHeader == null) {
+ return;
+ }
+ mPSHeader.setAlpha(1);
+ ViewGroup lockPill = mPSHeader.findViewById(R.id.ps_lock_unlock_button);
+ assert lockPill != null;
+ TextView lockText = lockPill.findViewById(R.id.lock_text);
+ PrivateSpaceSettingsButton settingsButton = mPSHeader.findViewById(R.id.ps_settings_button);
+ assert settingsButton != null;
+ //Add image for private space transitioning view
+ ImageView transitionView = mPSHeader.findViewById(R.id.ps_transition_image);
+ assert transitionView != null;
+ switch(getCurrentState()) {
case STATE_ENABLED -> {
+ mPSHeader.setOnClickListener(null);
+ mPSHeader.setClickable(false);
+ // Remove header from accessibility target when enabled.
+ mPSHeader.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
+
lockText.setVisibility(VISIBLE);
- lockButton.setVisibility(VISIBLE);
- lockButton.setOnClickListener(view -> lockingAction(/* lock */ true));
- lockButton.setContentDescription(mUnLockedStateContentDesc);
+ lockPill.setVisibility(VISIBLE);
+ lockPill.setOnClickListener(view -> lockingAction(/* lock */ true));
+ lockPill.setContentDescription(mUnLockedStateContentDesc);
+
+ settingsButton.setVisibility(isPrivateSpaceSettingsAvailable() ? VISIBLE : GONE);
+ transitionView.setVisibility(GONE);
}
case STATE_DISABLED -> {
- lockText.setVisibility(GONE);
- lockButton.setVisibility(VISIBLE);
- lockButton.setOnClickListener(view -> lockingAction(/* lock */ false));
- lockButton.setContentDescription(mLockedStateContentDesc);
- }
- default -> lockButton.setVisibility(GONE);
- }
- }
+ mPSHeader.setOnClickListener(view -> lockingAction(/* lock */ false));
+ mPSHeader.setClickable(true);
+ // Add header as accessibility target when disabled.
+ mPSHeader.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
+ mPSHeader.setContentDescription(mLockedStateContentDesc);
- private void addHeaderOnClickListener(RelativeLayout header) {
- if (getCurrentState() == STATE_DISABLED) {
- header.setOnClickListener(view -> lockingAction(/* lock */ false));
- header.setClickable(true);
- // Add header as accessibility target when disabled.
- header.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
- header.setContentDescription(mLockedStateContentDesc);
- } else {
- header.setOnClickListener(null);
- header.setClickable(false);
- // Remove header from accessibility target when enabled.
- header.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
+ lockText.setVisibility(GONE);
+ lockPill.setVisibility(VISIBLE);
+ lockPill.setOnClickListener(view -> lockingAction(/* lock */ false));
+ lockPill.setContentDescription(mLockedStateContentDesc);
+
+ settingsButton.setVisibility(GONE);
+ transitionView.setVisibility(GONE);
+ }
+ case STATE_TRANSITION -> {
+ transitionView.setVisibility(VISIBLE);
+ lockPill.setVisibility(GONE);
+ }
}
}
/** Sets the enablement of the profile when header or button is clicked. */
private void lockingAction(boolean lock) {
logEvents(lock ? LAUNCHER_PRIVATE_SPACE_LOCK_TAP : LAUNCHER_PRIVATE_SPACE_UNLOCK_TAP);
- if (lock) {
- lockPrivateProfile();
- } else {
- unlockPrivateProfile();
- }
- }
-
- private void addPrivateSpaceSettingsButton(ImageButton settingsButton) {
- if (getCurrentState() == STATE_ENABLED
- && isPrivateSpaceSettingsAvailable()) {
- settingsButton.setVisibility(VISIBLE);
- settingsButton.setOnClickListener(
- view -> {
- logEvents(LAUNCHER_PRIVATE_SPACE_SETTINGS_TAP);
- openPrivateSpaceSettings(view);
- });
- } else {
- settingsButton.setVisibility(GONE);
- }
- }
-
- private void addTransitionImage(ImageView transitionImage) {
- if (getCurrentState() == STATE_TRANSITION) {
- transitionImage.setVisibility(VISIBLE);
- } else {
- transitionImage.setVisibility(GONE);
- }
+ setQuietMode(lock);
}
/** Finds the private space header to scroll to and set the private space icons to GONE. */
@@ -498,7 +421,10 @@
return LinearSmoothScroller.SNAP_TO_END;
}
};
- smoothScroller.setTargetPosition(i);
+ // If privateSpaceHidden() then the entire container decorator will be invisible and
+ // we can directly move to an element above the header. There should always be one
+ // element, as PS is present in the bottom of All Apps.
+ smoothScroller.setTargetPosition(isPrivateSpaceHidden() ? i - 1 : i);
RecyclerView.LayoutManager layoutManager = allAppsRecyclerView.getLayoutManager();
if (layoutManager != null) {
startAnimationScroll(allAppsRecyclerView, layoutManager, smoothScroller);
@@ -620,8 +546,11 @@
float newAlpha = (float) valueAnimator.getAnimatedValue();
for (int i = 0; i < allAppsAdapterItems.size(); i++) {
BaseAllAppsAdapter.AdapterItem currentItem = allAppsAdapterItems.get(i);
+ // When not hidden: Fade all PS items except header.
+ // When hidden: Fade all items.
if (isPrivateSpaceItem(currentItem) &&
- currentItem.viewType != VIEW_TYPE_PRIVATE_SPACE_HEADER) {
+ (currentItem.viewType != VIEW_TYPE_PRIVATE_SPACE_HEADER
+ || isPrivateSpaceHidden())) {
RecyclerView.ViewHolder viewHolder =
allAppsRecyclerView.findViewHolderForAdapterPosition(i);
if (viewHolder != null) {
@@ -681,6 +610,7 @@
}
});
animatorSet.addListener(forEndCallback(() -> {
+ mIsStateTransitioning = false;
setAnimationRunning(false);
getMainRecyclerView().setChildAttachedConsumer(child -> child.setAlpha(1));
mStatsLogManager.logger().sendToInteractionJankMonitor(
@@ -689,6 +619,8 @@
: LAUNCHER_PRIVATE_SPACE_LOCK_ANIMATION_END,
mAllApps.getActiveRecyclerView());
if (!expand) {
+ mAllApps.mAH.get(MAIN).mRecyclerView.removeItemDecoration(
+ mPrivateAppsSectionDecorator);
// Call onAppsUpdated() because it may be canceled when this animation occurs.
mAllApps.getPersonalAppList().onAppsUpdated();
if (isPrivateSpaceHidden()) {
@@ -702,43 +634,42 @@
translateFloatingMaskView(false));
} else {
if (isPrivateSpaceHidden()) {
- animatorSet.playSequentially(translateFloatingMaskView(false),
- animateAlphaOfIcons(false),
- animateCollapseAnimation(),
- fadeOutHeaderAlpha());
+ animatorSet.playSequentially(animateAlphaOfIcons(false),
+ animateAlphaOfPrivateSpaceContainer(),
+ animateCollapseAnimation());
} else {
animatorSet.playSequentially(translateFloatingMaskView(true),
animateAlphaOfIcons(false),
animateCollapseAnimation());
}
}
- animatorSet.setDuration(EXPAND_COLLAPSE_DURATION);
animatorSet.start();
}
- /** Fades out the private space container. */
- private ValueAnimator fadeOutHeaderAlpha() {
- if (mPSHeader == null) {
- return new ValueAnimator();
- }
- float from = 1;
- float to = 0;
- ValueAnimator alphaAnim = ObjectAnimator.ofFloat(from, to);
- alphaAnim.setDuration(EXPAND_COLLAPSE_DURATION);
- alphaAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator valueAnimator) {
- if (mPSHeader != null) {
- mPSHeader.setAlpha((float) valueAnimator.getAnimatedValue());
+ /** Fades out the private space container (defined by its items' decorators). */
+ private ValueAnimator animateAlphaOfPrivateSpaceContainer() {
+ int from = 255; // 100% opacity.
+ int to = 0; // No opacity.
+ ValueAnimator alphaAnim = ObjectAnimator.ofInt(from, to);
+ AllAppsRecyclerView allAppsRecyclerView = mAllApps.getActiveRecyclerView();
+ List<BaseAllAppsAdapter.AdapterItem> allAppsAdapterItems =
+ allAppsRecyclerView.getApps().getAdapterItems();
+ alphaAnim.setDuration(CONTAINER_OPACITY_DURATION);
+ alphaAnim.addUpdateListener(valueAnimator -> {
+ for (BaseAllAppsAdapter.AdapterItem currentItem : allAppsAdapterItems) {
+ if (isPrivateSpaceItem(currentItem)) {
+ currentItem.setDecorationFillAlpha((int) valueAnimator.getAnimatedValue());
}
}
+ // Invalidate the parent view, to redraw the decorations with changed alpha.
+ allAppsRecyclerView.invalidate();
});
return alphaAnim;
}
/** Fades out the private space container. */
private ValueAnimator translateFloatingMaskView(boolean animateIn) {
- if (!Flags.privateSpaceFloatingMaskView() || mFloatingMaskView == null) {
+ if (!Flags.privateSpaceAddFloatingMaskView() || mFloatingMaskView == null) {
return new ValueAnimator();
}
// Translate base on the height amount. Translates out on expand and in on collapse.
@@ -773,7 +704,7 @@
public void endTransition(LayoutTransition transition, ViewGroup viewGroup,
View view, int i) {
settingsAndLockGroup.setLayoutTransition(null);
- mAnimate = false;
+ mReadyToAnimate = false;
}
});
settingsAndLockGroup.setLayoutTransition(settingsAndLockTransition);
@@ -849,7 +780,7 @@
}
private void attachFloatingMaskView(boolean expand) {
- if (!Flags.privateSpaceFloatingMaskView()) {
+ if (!Flags.privateSpaceAddFloatingMaskView()) {
return;
}
mFloatingMaskView = (FloatingMaskView) mAllApps.getLayoutInflater().inflate(
@@ -873,7 +804,7 @@
/** Starts the smooth scroll with the provided smoothScroller and add idle listener. */
private void startAnimationScroll(AllAppsRecyclerView allAppsRecyclerView,
RecyclerView.LayoutManager layoutManager, RecyclerView.SmoothScroller smoothScroller) {
- mAnimationScrolling = true;
+ mIsScrolling = true;
layoutManager.startSmoothScroll(smoothScroller);
allAppsRecyclerView.removeOnScrollListener(mOnIdleScrollListener);
allAppsRecyclerView.addOnScrollListener(mOnIdleScrollListener);
@@ -887,12 +818,24 @@
return mAllApps.mAH.get(ActivityAllAppsContainerView.AdapterHolder.MAIN).mRecyclerView;
}
- boolean getAnimate() {
- return mAnimate;
+ /** Returns if private space is readily available to be animated. */
+ boolean getReadyToAnimate() {
+ return mReadyToAnimate;
}
- boolean getAnimationScrolling() {
- return mAnimationScrolling;
+ /** Returns when a smooth scroll is happening. */
+ boolean isScrolling() {
+ return mIsScrolling;
+ }
+
+ /**
+ * Returns when private space is in the process of transitioning. This is different from
+ * getAnimate() since mStateTransitioning checks from the time transitioning starts happening
+ * in reset() as oppose to when private space is animating. This should be used to ensure
+ * Private Space state during onBind().
+ */
+ boolean isStateTransitioning() {
+ return mIsStateTransitioning;
}
int getPsHeaderHeight() {
diff --git a/src/com/android/launcher3/allapps/PrivateSpaceSettingsButton.java b/src/com/android/launcher3/allapps/PrivateSpaceSettingsButton.java
new file mode 100644
index 0000000..43e42ff
--- /dev/null
+++ b/src/com/android/launcher3/allapps/PrivateSpaceSettingsButton.java
@@ -0,0 +1,81 @@
+/*
+ * 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.allapps;
+
+import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_PRIVATESPACE;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_PRIVATE_SPACE_SETTINGS_TAP;
+
+import android.content.Context;
+import android.content.Intent;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.ImageButton;
+
+import com.android.launcher3.logging.StatsLogManager;
+import com.android.launcher3.model.data.AppInfo;
+import com.android.launcher3.util.ApiWrapper;
+import com.android.launcher3.views.ActivityContext;
+
+public class PrivateSpaceSettingsButton extends ImageButton implements View.OnClickListener {
+
+ private final ActivityContext mActivityContext;
+ private final StatsLogManager mStatsLogManager;
+ private final Intent mPrivateSpaceSettingsIntent;
+
+ public PrivateSpaceSettingsButton(Context context) {
+ this(context, null, 0);
+ }
+
+ public PrivateSpaceSettingsButton(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public PrivateSpaceSettingsButton(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ mActivityContext = ActivityContext.lookupContext(context);
+ mStatsLogManager = mActivityContext.getStatsLogManager();
+ mPrivateSpaceSettingsIntent =
+ ApiWrapper.INSTANCE.get(context).getPrivateSpaceSettingsIntent();
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ setOnClickListener(this);
+ }
+
+ @Override
+ public void onClick(View view) {
+ mStatsLogManager.logger().log(LAUNCHER_PRIVATE_SPACE_SETTINGS_TAP);
+ AppInfo privateSpaceSettingsItemInfo = createPrivateSpaceSettingsAppInfo();
+ view.setTag(privateSpaceSettingsItemInfo);
+ mActivityContext.startActivitySafely(
+ view,
+ mPrivateSpaceSettingsIntent,
+ privateSpaceSettingsItemInfo);
+ }
+
+ AppInfo createPrivateSpaceSettingsAppInfo() {
+ AppInfo itemInfo = new AppInfo();
+ itemInfo.id = CONTAINER_PRIVATESPACE;
+ if (mPrivateSpaceSettingsIntent != null) {
+ itemInfo.componentName = mPrivateSpaceSettingsIntent.getComponent();
+ }
+ itemInfo.container = CONTAINER_PRIVATESPACE;
+ return itemInfo;
+ }
+}
diff --git a/src/com/android/launcher3/allapps/UserProfileManager.java b/src/com/android/launcher3/allapps/UserProfileManager.java
index 3351ee3..eb74d20 100644
--- a/src/com/android/launcher3/allapps/UserProfileManager.java
+++ b/src/com/android/launcher3/allapps/UserProfileManager.java
@@ -40,11 +40,13 @@
* {@link PrivateProfileManager} which manages private profile state.
*/
public abstract class UserProfileManager {
+ public static final int STATE_UNKNOWN = 0;
public static final int STATE_ENABLED = 1;
public static final int STATE_DISABLED = 2;
public static final int STATE_TRANSITION = 3;
@IntDef(value = {
+ STATE_UNKNOWN,
STATE_ENABLED,
STATE_DISABLED,
STATE_TRANSITION
diff --git a/src/com/android/launcher3/dragndrop/AddItemActivity.java b/src/com/android/launcher3/dragndrop/AddItemActivity.java
index ec0a222..85eb39b 100644
--- a/src/com/android/launcher3/dragndrop/AddItemActivity.java
+++ b/src/com/android/launcher3/dragndrop/AddItemActivity.java
@@ -91,7 +91,8 @@
*/
@TargetApi(Build.VERSION_CODES.O)
public class AddItemActivity extends BaseActivity
- implements OnLongClickListener, OnTouchListener, AbstractSlideInView.OnCloseListener {
+ implements OnLongClickListener, OnTouchListener, AbstractSlideInView.OnCloseListener,
+ WidgetCell.PreviewReadyListener {
private static final int SHADOW_SIZE = 10;
@@ -142,6 +143,7 @@
mDragLayer = findViewById(R.id.add_item_drag_layer);
mDragLayer.recreateControllers();
mWidgetCell = findViewById(R.id.widget_cell);
+ mWidgetCell.addPreviewReadyListener(this);
mAccessibilityManager =
getApplicationContext().getSystemService(AccessibilityManager.class);
@@ -454,4 +456,11 @@
.withItemInfo((ItemInfo) mWidgetCell.getWidgetView().getTag())
.log(command);
}
+
+ @Override
+ public void onPreviewAvailable() {
+ // Set the preview height based on "the only" widget's preview.
+ mWidgetCell.setParentAlignedPreviewHeight(mWidgetCell.getPreviewContentHeight());
+ mWidgetCell.post(mWidgetCell::requestLayout);
+ }
}
diff --git a/src/com/android/launcher3/dragndrop/LauncherDragController.java b/src/com/android/launcher3/dragndrop/LauncherDragController.java
index f3708a2..29fc613 100644
--- a/src/com/android/launcher3/dragndrop/LauncherDragController.java
+++ b/src/com/android/launcher3/dragndrop/LauncherDragController.java
@@ -28,6 +28,7 @@
import android.view.View;
import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.DragSource;
@@ -36,6 +37,7 @@
import com.android.launcher3.R;
import com.android.launcher3.accessibility.DragViewStateAnnouncer;
import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.widget.util.WidgetDragScaleUtils;
/**
* Drag controller for Launcher activity
@@ -43,7 +45,6 @@
public class LauncherDragController extends DragController<Launcher> {
private static final boolean PROFILE_DRAWING_DURING_DRAG = false;
-
private final FlingToDeleteHelper mFlingToDeleteHelper;
public LauncherDragController(Launcher launcher) {
@@ -92,8 +93,13 @@
&& !mOptions.preDragCondition.shouldStartDrag(0);
final Resources res = mActivity.getResources();
- final float scaleDps = mIsInPreDrag
- ? res.getDimensionPixelSize(R.dimen.pre_drag_view_scale) : 0f;
+
+ final float scalePx;
+ if (originalView.getViewType() == DraggableView.DRAGGABLE_WIDGET) {
+ scalePx = mIsInPreDrag ? 0f : getWidgetDragScalePx(drawable, view, dragInfo);
+ } else {
+ scalePx = mIsInPreDrag ? res.getDimensionPixelSize(R.dimen.pre_drag_view_scale) : 0f;
+ }
final DragView dragView = mDragObject.dragView = drawable != null
? new LauncherDragView(
mActivity,
@@ -102,7 +108,7 @@
registrationY,
initialDragViewScale,
dragViewScaleOnDrop,
- scaleDps)
+ scalePx)
: new LauncherDragView(
mActivity,
view,
@@ -112,7 +118,7 @@
registrationY,
initialDragViewScale,
dragViewScaleOnDrop,
- scaleDps);
+ scalePx);
dragView.setItemInfo(dragInfo);
mDragObject.dragComplete = false;
@@ -157,6 +163,29 @@
return dragView;
}
+
+ /**
+ * Returns the scale in terms of pixels (to be applied on width) to scale the preview
+ * during drag and drop.
+ */
+ @VisibleForTesting
+ float getWidgetDragScalePx(@Nullable Drawable drawable, @Nullable View view,
+ ItemInfo dragInfo) {
+ float draggedViewWidthPx = 0;
+ float draggedViewHeightPx = 0;
+
+ if (view != null) {
+ draggedViewWidthPx = view.getMeasuredWidth();
+ draggedViewHeightPx = view.getMeasuredHeight();
+ } else if (drawable != null) {
+ draggedViewWidthPx = drawable.getIntrinsicWidth();
+ draggedViewHeightPx = drawable.getIntrinsicHeight();
+ }
+
+ return WidgetDragScaleUtils.getWidgetDragScalePx(mActivity, mActivity.getDeviceProfile(),
+ draggedViewWidthPx, draggedViewHeightPx, dragInfo);
+ }
+
@Override
protected void exitDrag() {
if (!mActivity.isInState(EDIT_MODE)) {
diff --git a/src/com/android/launcher3/icons/IconCache.java b/src/com/android/launcher3/icons/IconCache.java
index 04d8ac0..44e448e 100644
--- a/src/com/android/launcher3/icons/IconCache.java
+++ b/src/com/android/launcher3/icons/IconCache.java
@@ -57,6 +57,7 @@
import com.android.launcher3.icons.ComponentWithLabel.ComponentCachingLogic;
import com.android.launcher3.icons.cache.BaseIconCache;
import com.android.launcher3.icons.cache.CachingLogic;
+import com.android.launcher3.logging.FileLog;
import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.model.data.IconRequestInfo;
import com.android.launcher3.model.data.ItemInfoWithIcon;
@@ -641,4 +642,10 @@
void reapplyItemInfo(ItemInfoWithIcon info);
}
+
+ /** Log persistently to FileLog.d for debugging. */
+ @Override
+ protected void logdPersistently(String tag, String message, @Nullable Exception e) {
+ FileLog.d(tag, message, e);
+ }
}
diff --git a/src/com/android/launcher3/qsb/QsbContainerView.java b/src/com/android/launcher3/qsb/QsbContainerView.java
index 8e53aff..d6b41b0 100644
--- a/src/com/android/launcher3/qsb/QsbContainerView.java
+++ b/src/com/android/launcher3/qsb/QsbContainerView.java
@@ -61,7 +61,7 @@
*/
public class QsbContainerView extends FrameLayout {
- public static final String SEARCH_PROVIDER_SETTINGS_KEY = "SEARCH_PROVIDER_PACKAGE_NAME";
+ public static final String SEARCH_ENGINE_SETTINGS_KEY = "selected_search_engine";
/**
* Returns the package name for user configured search provider or from searchManager
@@ -71,8 +71,8 @@
@WorkerThread
@Nullable
public static String getSearchWidgetPackageName(@NonNull Context context) {
- String providerPkg = Settings.Global.getString(context.getContentResolver(),
- SEARCH_PROVIDER_SETTINGS_KEY);
+ String providerPkg = Settings.Secure.getString(context.getContentResolver(),
+ SEARCH_ENGINE_SETTINGS_KEY);
if (providerPkg == null) {
SearchManager searchManager = context.getSystemService(SearchManager.class);
ComponentName componentName = searchManager.getGlobalSearchActivity();
diff --git a/src/com/android/launcher3/util/DisplayController.java b/src/com/android/launcher3/util/DisplayController.java
index 92fc38f..21eee55 100644
--- a/src/com/android/launcher3/util/DisplayController.java
+++ b/src/com/android/launcher3/util/DisplayController.java
@@ -170,7 +170,7 @@
* Returns the current navigation mode
*/
public static NavigationMode getNavigationMode(Context context) {
- return INSTANCE.get(context).getInfo().navigationMode;
+ return INSTANCE.get(context).getInfo().getNavigationMode();
}
/**
@@ -302,7 +302,7 @@
Info newInfo = new Info(displayInfoContext, wmProxy, oldInfo.mPerDisplayBounds);
if (newInfo.densityDpi != oldInfo.densityDpi || newInfo.fontScale != oldInfo.fontScale
- || newInfo.navigationMode != oldInfo.navigationMode) {
+ || newInfo.getNavigationMode() != oldInfo.getNavigationMode()) {
// Cache may not be valid anymore, recreate without cache
newInfo = new Info(displayInfoContext, wmProxy,
wmProxy.estimateInternalDisplayBounds(displayInfoContext));
@@ -318,7 +318,7 @@
if (newInfo.densityDpi != oldInfo.densityDpi || newInfo.fontScale != oldInfo.fontScale) {
change |= CHANGE_DENSITY;
}
- if (newInfo.navigationMode != oldInfo.navigationMode) {
+ if (newInfo.getNavigationMode() != oldInfo.getNavigationMode()) {
change |= CHANGE_NAVIGATION_MODE;
}
if (!newInfo.supportedBounds.equals(oldInfo.supportedBounds)
@@ -369,7 +369,7 @@
// Configuration property
public final float fontScale;
private final int densityDpi;
- public final NavigationMode navigationMode;
+ private final NavigationMode navigationMode;
private final PortraitSize mScreenSizeDp;
// WindowBounds
@@ -553,7 +553,7 @@
pw.println(" rotation=" + info.rotation);
pw.println(" fontScale=" + info.fontScale);
pw.println(" densityDpi=" + info.densityDpi);
- pw.println(" navigationMode=" + info.navigationMode.name());
+ pw.println(" navigationMode=" + info.getNavigationMode().name());
pw.println(" isTaskbarPinned=" + info.mIsTaskbarPinned);
pw.println(" isTaskbarPinnedInDesktopMode=" + info.mIsTaskbarPinnedInDesktopMode);
pw.println(" isInDesktopMode=" + info.mIsInDesktopMode);
diff --git a/src/com/android/launcher3/util/SplitConfigurationOptions.java b/src/com/android/launcher3/util/SplitConfigurationOptions.java
index 837d7bc..95624b1 100644
--- a/src/com/android/launcher3/util/SplitConfigurationOptions.java
+++ b/src/com/android/launcher3/util/SplitConfigurationOptions.java
@@ -186,6 +186,12 @@
public int stagePosition = STAGE_POSITION_UNDEFINED;
@StageType
public int stageType = STAGE_TYPE_UNDEFINED;
+
+ @Override
+ public String toString() {
+ return "SplitStageInfo { taskId=" + taskId
+ + ", stagePosition=" + stagePosition + ", stageType=" + stageType + " }";
+ }
}
public static StatsLogManager.EventEnum getLogEventForPosition(@StagePosition int position) {
diff --git a/src/com/android/launcher3/widget/WidgetCell.java b/src/com/android/launcher3/widget/WidgetCell.java
index eac2ce7..2bb485a 100644
--- a/src/com/android/launcher3/widget/WidgetCell.java
+++ b/src/com/android/launcher3/widget/WidgetCell.java
@@ -103,6 +103,8 @@
private Size mWidgetSize;
private final DatabaseWidgetPreviewLoader mWidgetPreviewLoader;
+ @Nullable
+ private PreviewReadyListener mPreviewReadyListener = null;
protected CancellableTask mActiveRequest;
private boolean mAnimatePreview = true;
@@ -118,7 +120,8 @@
private CancellableTask mIconLoadRequest;
private boolean mIsShowingAddButton = false;
-
+ // Height enforced by the parent to align all widget cells displayed by it.
+ private int mParentAlignedPreviewHeight;
public WidgetCell(Context context) {
this(context, null);
}
@@ -190,6 +193,8 @@
mWidgetDims.setText(null);
mWidgetDescription.setText(null);
mWidgetDescription.setVisibility(GONE);
+ mPreviewReadyListener = null;
+ mParentAlignedPreviewHeight = 0;
showDescription(true);
showDimensions(true);
@@ -338,8 +343,8 @@
private void updateAppWidgetHostScale(NavigableAppWidgetHostView view) {
// Scale the content such that all of the content is visible
- int contentWidth = view.getWidth();
- int contentHeight = view.getHeight();
+ float contentWidth = view.getWidth();
+ float contentHeight = view.getHeight();
if (view.getChildCount() == 1) {
View content = view.getChildAt(0);
@@ -359,6 +364,12 @@
mAppWidgetHostViewScale = Math.min(pWidth / contentWidth, pHeight / contentHeight);
}
view.setScaleToFit(mAppWidgetHostViewScale);
+
+ // layout based previews maybe ready at this point to inspect their inner height.
+ if (mPreviewReadyListener != null) {
+ mPreviewReadyListener.onPreviewAvailable();
+ mPreviewReadyListener = null;
+ }
}
public WidgetImageView getWidgetView() {
@@ -384,6 +395,12 @@
removeView(mAppWidgetHostViewPreview);
mAppWidgetHostViewPreview = null;
}
+
+ // Drawables of the image previews are available at this point to measure.
+ if (mPreviewReadyListener != null) {
+ mPreviewReadyListener.onPreviewAvailable();
+ mPreviewReadyListener = null;
+ }
}
if (mAnimatePreview) {
@@ -489,14 +506,20 @@
// mPreviewContainerScale ensures the needed scaling with respect to original widget size.
mAppWidgetHostViewScale = mPreviewContainerScale;
containerLp.width = mPreviewContainerSize.getWidth();
- containerLp.height = mPreviewContainerSize.getHeight();
+ int height = mPreviewContainerSize.getHeight();
// If we don't have enough available width, scale the preview container to fit.
if (containerLp.width > maxWidth) {
containerLp.width = maxWidth;
mAppWidgetHostViewScale = (float) containerLp.width / mPreviewContainerSize.getWidth();
- containerLp.height = Math.round(
- mPreviewContainerSize.getHeight() * mAppWidgetHostViewScale);
+ height = Math.round(mPreviewContainerSize.getHeight() * mAppWidgetHostViewScale);
+ }
+
+ // Use parent aligned height in set.
+ if (mParentAlignedPreviewHeight > 0) {
+ containerLp.height = Math.min(height, mParentAlignedPreviewHeight);
+ } else {
+ containerLp.height = height;
}
// No need to call mWidgetImageContainer.setLayoutParams as we are in measure pass
@@ -513,6 +536,42 @@
}
/**
+ * Sets the height of the preview as adjusted by the parent to have this cell's content aligned
+ * with other cells displayed by the parent.
+ */
+ public void setParentAlignedPreviewHeight(int previewHeight) {
+ mParentAlignedPreviewHeight = previewHeight;
+ }
+
+ /**
+ * Returns the height of the preview without any empty space.
+ * In case of appwidget host views, it returns the height of first child. This way, if preview
+ * view provided by an app doesn't fill bounds, this will return actual height without white
+ * space.
+ */
+ public int getPreviewContentHeight() {
+ // By default assume scaled height.
+ int height = Math.round(mPreviewContainerScale * mWidgetSize.getHeight());
+
+ if (mWidgetImage != null && mWidgetImage.getDrawable() != null) {
+ // getBitmapBounds returns the scaled bounds.
+ Rect bitmapBounds = mWidgetImage.getBitmapBounds();
+ height = bitmapBounds.height();
+ } else if (mAppWidgetHostViewPreview != null
+ && mAppWidgetHostViewPreview.getChildCount() == 1) {
+ int contentHeight = Math.round(
+ mPreviewContainerScale * mWidgetSize.getHeight());
+ int previewInnerHeight = Math.round(
+ mAppWidgetHostViewScale * mAppWidgetHostViewPreview.getChildAt(
+ 0).getMeasuredHeight());
+ // Use either of the inner scaled height or the scaled widget height
+ height = Math.min(contentHeight, previewInnerHeight);
+ }
+
+ return height;
+ }
+
+ /**
* Loads a high resolution package icon to show next to the widget title.
*/
public void loadHighResPackageIcon() {
@@ -651,4 +710,19 @@
}
return false;
}
+
+ /**
+ * Listener to notify when previews are available.
+ */
+ public void addPreviewReadyListener(PreviewReadyListener previewReadyListener) {
+ mPreviewReadyListener = previewReadyListener;
+ }
+
+ /**
+ * Listener interface for subscribers to listen to preview's availability.
+ */
+ public interface PreviewReadyListener {
+ /** Handler on to invoke when previews are available. */
+ void onPreviewAvailable();
+ }
}
diff --git a/src/com/android/launcher3/widget/WidgetTableRow.java b/src/com/android/launcher3/widget/WidgetTableRow.java
new file mode 100644
index 0000000..a5312ce
--- /dev/null
+++ b/src/com/android/launcher3/widget/WidgetTableRow.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.widget;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.widget.TableRow;
+
+/**
+ * A row of {@link WidgetCell}s that can be displayed in a table.
+ */
+public class WidgetTableRow extends TableRow implements WidgetCell.PreviewReadyListener {
+ private int mNumOfReadyCells;
+ private int mNumOfCells;
+ private int mResizeDelay;
+
+ public WidgetTableRow(Context context) {
+ super(context);
+ }
+ public WidgetTableRow(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ public void onPreviewAvailable() {
+ mNumOfReadyCells++;
+
+ // Once all previews are loaded, find max visible height and adjust the preview containers.
+ if (mNumOfReadyCells == mNumOfCells) {
+ resize();
+ }
+ }
+
+ private void resize() {
+ int previewHeight = 0;
+ // get the maximum height of each widget preview
+ for (int i = 0; i < getChildCount(); i++) {
+ WidgetCell widgetCell = (WidgetCell) getChildAt(i);
+ previewHeight = Math.max(widgetCell.getPreviewContentHeight(), previewHeight);
+ }
+ if (mResizeDelay > 0) {
+ postDelayed(() -> setAlpha(1f), mResizeDelay);
+ }
+ if (previewHeight > 0) {
+ for (int i = 0; i < getChildCount(); i++) {
+ WidgetCell widgetCell = (WidgetCell) getChildAt(i);
+ widgetCell.setParentAlignedPreviewHeight(previewHeight);
+ widgetCell.postDelayed(widgetCell::requestLayout, mResizeDelay);
+ }
+ }
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ super.onLayout(changed, l, t, r, b);
+ }
+
+ /**
+ * Sets up the row to display the provided number of numOfCells.
+ *
+ * @param numOfCells number of numOfCells in the row
+ * @param resizeDelayMs time to wait in millis before making any layout size adjustments e.g. we
+ * want to wait for list expand collapse animation before resizing the
+ * cell previews.
+ */
+ public void setupRow(int numOfCells, int resizeDelayMs) {
+ mNumOfCells = numOfCells;
+ mNumOfReadyCells = 0;
+
+ mResizeDelay = resizeDelayMs;
+ // For delayed resize, reveal contents only after resize is done.
+ if (mResizeDelay > 0) {
+ setAlpha(0);
+ }
+ }
+}
diff --git a/src/com/android/launcher3/widget/WidgetsBottomSheet.java b/src/com/android/launcher3/widget/WidgetsBottomSheet.java
index 4ea2426..894099d 100644
--- a/src/com/android/launcher3/widget/WidgetsBottomSheet.java
+++ b/src/com/android/launcher3/widget/WidgetsBottomSheet.java
@@ -31,7 +31,6 @@
import android.view.animation.Interpolator;
import android.widget.ScrollView;
import android.widget.TableLayout;
-import android.widget.TableRow;
import android.widget.TextView;
import androidx.annotation.Px;
@@ -137,8 +136,9 @@
mActivityContext.getDeviceProfile(), mMaxHorizontalSpan,
mWidgetCellHorizontalPadding)
.forEach(row -> {
- TableRow tableRow = new TableRow(getContext());
+ WidgetTableRow tableRow = new WidgetTableRow(getContext());
tableRow.setGravity(Gravity.TOP);
+ tableRow.setupRow(row.size(), /*resizeDelayMs=*/ 0);
row.forEach(widgetItem -> {
WidgetCell widget = addItemCell(tableRow);
widget.applyFromCellItem(widgetItem);
@@ -163,9 +163,10 @@
return super.onControllerInterceptTouchEvent(ev);
}
- protected WidgetCell addItemCell(ViewGroup parent) {
+ protected WidgetCell addItemCell(WidgetTableRow parent) {
WidgetCell widget = (WidgetCell) LayoutInflater.from(getContext())
.inflate(R.layout.widget_cell, parent, false);
+ widget.addPreviewReadyListener(parent);
widget.setOnClickListener(this);
View previewContainer = widget.findViewById(R.id.widget_preview_container);
diff --git a/src/com/android/launcher3/widget/picker/WidgetRecommendationsView.java b/src/com/android/launcher3/widget/picker/WidgetRecommendationsView.java
index 4f73e66..9260af9 100644
--- a/src/com/android/launcher3/widget/picker/WidgetRecommendationsView.java
+++ b/src/com/android/launcher3/widget/picker/WidgetRecommendationsView.java
@@ -53,6 +53,7 @@
*/
public final class WidgetRecommendationsView extends PagedView<PageIndicatorDots> {
private @Px float mAvailableHeight = Float.MAX_VALUE;
+ private @Px float mAvailableWidth = 0;
private static final String INITIALLY_DISPLAYED_WIDGETS_STATE_KEY =
"widgetRecommendationsView:mDisplayedWidgets";
private static final int MAX_CATEGORIES = 3;
@@ -152,6 +153,7 @@
final @Px float availableHeight, final @Px int availableWidth,
final @Px int cellPadding) {
this.mAvailableHeight = availableHeight;
+ this.mAvailableWidth = availableWidth;
clear();
Set<ComponentName> displayedWidgets = maybeDisplayInTable(recommendedWidgets,
@@ -187,6 +189,7 @@
DeviceProfile deviceProfile, final @Px float availableHeight,
final @Px int availableWidth, final @Px int cellPadding, final int requestedPage) {
this.mAvailableHeight = availableHeight;
+ this.mAvailableWidth = availableWidth;
Context context = getContext();
// For purpose of recommendations section, we don't want paging dots to be halved in two
// pane display, so, we always provide isTwoPanels = "false".
@@ -280,17 +283,24 @@
boolean hasMultiplePages = getChildCount() > 0;
if (hasMultiplePages) {
- int finalWidth = MeasureSpec.getSize(widthMeasureSpec);
int desiredHeight = 0;
+ int desiredWidth = Math.round(mAvailableWidth);
for (int i = 0; i < getChildCount(); i++) {
View child = getChildAt(i);
- measureChild(child, widthMeasureSpec, heightMeasureSpec);
+ // Measure children based on available height and width.
+ measureChild(child,
+ MeasureSpec.makeMeasureSpec(desiredWidth, MeasureSpec.EXACTLY),
+ MeasureSpec.makeMeasureSpec(Math.round(mAvailableHeight),
+ MeasureSpec.AT_MOST));
// Use height of tallest child as we have limited height.
- desiredHeight = Math.max(desiredHeight, child.getMeasuredHeight());
+ int childHeight = child.getMeasuredHeight();
+ desiredHeight = Math.max(desiredHeight, childHeight);
}
int finalHeight = resolveSizeAndState(desiredHeight, heightMeasureSpec, 0);
+ int finalWidth = resolveSizeAndState(desiredWidth, widthMeasureSpec, 0);
+
setMeasuredDimension(finalWidth, finalHeight);
} else {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
diff --git a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
index 6aaa7d2..3be6faa 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
@@ -20,7 +20,6 @@
import static com.android.launcher3.allapps.ActivityAllAppsContainerView.AdapterHolder.SEARCH;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_WIDGETSTRAY_SEARCHED;
import static com.android.launcher3.testing.shared.TestProtocol.NORMAL_STATE_ORDINAL;
-import static com.android.launcher3.widget.picker.WidgetsListAdapter.VIEW_TYPE_WIDGETS_LIST;
import android.animation.Animator;
import android.content.Context;
@@ -1023,35 +1022,7 @@
default:
break;
}
- mWidgetsListItemAnimator = new DefaultItemAnimator() {
- @Override
- public boolean animateChange(RecyclerView.ViewHolder oldHolder,
- RecyclerView.ViewHolder newHolder, int fromLeft, int fromTop, int toLeft,
- int toTop) {
- // As we expand an item, the content / widgets list that appears (with add
- // event) also gets change events as its previews load asynchronously. The
- // super implementation of animateChange cancels the animations on it - breaking
- // the "add animation". Instead, here, we skip "change" animation for content
- // list - because we want it to either appear or disappear. And, the previews
- // themselves have their own animation when loaded, so, we don't need change
- // animations for them anyway. Below, we do-nothing.
- if (oldHolder.getItemViewType() == VIEW_TYPE_WIDGETS_LIST) {
- dispatchChangeStarting(oldHolder, true);
- dispatchChangeFinished(oldHolder, true);
- return true;
- }
- return super.animateChange(oldHolder, newHolder, fromLeft, fromTop, toLeft,
- toTop);
- }
- };
- // Disable change animations because it disrupts the item focus upon adapter item
- // change.
- mWidgetsListItemAnimator.setSupportsChangeAnimations(false);
- // Make the moves a bit faster, so that the amount of time for which user sees the
- // bottom-sheet background before "add" animation starts is less making it smoother.
- mWidgetsListItemAnimator.setChangeDuration(90);
- mWidgetsListItemAnimator.setMoveDuration(90);
- mWidgetsListItemAnimator.setAddDuration(300);
+ mWidgetsListItemAnimator = new WidgetsListItemAnimator();
}
private int getEmptySpaceHeight() {
@@ -1065,7 +1036,7 @@
mWidgetsRecyclerView.setClipChildren(false);
mWidgetsRecyclerView.setAdapter(mWidgetsListAdapter);
mWidgetsRecyclerView.bindFastScrollbar(mFastScroller);
- mWidgetsRecyclerView.setItemAnimator(mWidgetsListItemAnimator);
+ mWidgetsRecyclerView.setItemAnimator(isTwoPane() ? null : mWidgetsListItemAnimator);
mWidgetsRecyclerView.setHeaderViewDimensionsProvider(WidgetsFullSheet.this);
if (!isTwoPane()) {
mWidgetsRecyclerView.setEdgeEffectFactory(
diff --git a/src/com/android/launcher3/widget/picker/WidgetsListHeader.java b/src/com/android/launcher3/widget/picker/WidgetsListHeader.java
index d373a3b..d164dd0 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsListHeader.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsListHeader.java
@@ -15,6 +15,8 @@
*/
package com.android.launcher3.widget.picker;
+import static android.animation.ValueAnimator.areAnimatorsEnabled;
+
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
@@ -201,7 +203,7 @@
public void reapplyItemInfo(ItemInfoWithIcon info) {
if (getTag() == info) {
mIconLoadRequest = null;
- mEnableIconUpdateAnimation = true;
+ mEnableIconUpdateAnimation = areAnimatorsEnabled();
// Optimization: Starting in N, pre-uploads the bitmap to RenderThread.
info.bitmap.icon.prepareToDraw();
diff --git a/src/com/android/launcher3/widget/picker/WidgetsListItemAnimator.java b/src/com/android/launcher3/widget/picker/WidgetsListItemAnimator.java
new file mode 100644
index 0000000..854700f
--- /dev/null
+++ b/src/com/android/launcher3/widget/picker/WidgetsListItemAnimator.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.widget.picker;
+
+import static com.android.launcher3.widget.picker.WidgetsListAdapter.VIEW_TYPE_WIDGETS_LIST;
+
+import androidx.recyclerview.widget.DefaultItemAnimator;
+import androidx.recyclerview.widget.RecyclerView;
+
+public class WidgetsListItemAnimator extends DefaultItemAnimator {
+ public static final int CHANGE_DURATION_MS = 90;
+ public static final int MOVE_DURATION_MS = 90;
+ public static final int ADD_DURATION_MS = 120;
+
+ public WidgetsListItemAnimator() {
+ super();
+
+ // Disable change animations because it disrupts the item focus upon adapter item
+ // change.
+ setSupportsChangeAnimations(false);
+ // Make the moves a bit faster, so that the amount of time for which user sees the
+ // bottom-sheet background before "add" animation starts is less making it smoother.
+ setChangeDuration(CHANGE_DURATION_MS);
+ setMoveDuration(MOVE_DURATION_MS);
+ setAddDuration(ADD_DURATION_MS);
+ }
+ @Override
+ public boolean animateChange(RecyclerView.ViewHolder oldHolder,
+ RecyclerView.ViewHolder newHolder, int fromLeft, int fromTop, int toLeft,
+ int toTop) {
+ // As we expand an item, the content / widgets list that appears (with add
+ // event) also gets change events as its previews load asynchronously. The
+ // super implementation of animateChange cancels the animations on it - breaking
+ // the "add animation". Instead, here, we skip "change" animation for content
+ // list - because we want it to either appear or disappear. And, the previews
+ // themselves have their own animation when loaded, so, we don't need change
+ // animations for them anyway. Below, we do-nothing.
+ if (oldHolder.getItemViewType() == VIEW_TYPE_WIDGETS_LIST) {
+ dispatchChangeStarting(oldHolder, true);
+ dispatchChangeFinished(oldHolder, true);
+ return true;
+ }
+ return super.animateChange(oldHolder, newHolder, fromLeft, fromTop, toLeft,
+ toTop);
+ }
+}
diff --git a/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinder.java b/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinder.java
index 56352cc..45d733a 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinder.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinder.java
@@ -15,6 +15,11 @@
*/
package com.android.launcher3.widget.picker;
+import static com.android.launcher3.widget.picker.WidgetsListItemAnimator.CHANGE_DURATION_MS;
+import static com.android.launcher3.widget.picker.WidgetsListItemAnimator.MOVE_DURATION_MS;
+
+import static android.animation.ValueAnimator.areAnimatorsEnabled;
+
import android.content.Context;
import android.graphics.Bitmap;
import android.util.Log;
@@ -26,7 +31,6 @@
import android.view.View.OnLongClickListener;
import android.view.ViewGroup;
import android.widget.TableLayout;
-import android.widget.TableRow;
import androidx.annotation.NonNull;
import androidx.annotation.Px;
@@ -36,6 +40,7 @@
import com.android.launcher3.recyclerview.ViewHolderBinder;
import com.android.launcher3.views.ActivityContext;
import com.android.launcher3.widget.WidgetCell;
+import com.android.launcher3.widget.WidgetTableRow;
import com.android.launcher3.widget.model.WidgetsListContentEntry;
import com.android.launcher3.widget.util.WidgetsTableUtils;
@@ -111,17 +116,15 @@
for (int i = 0; i < widgetItemsTable.size(); i++) {
List<WidgetItem> widgetItemsPerRow = widgetItemsTable.get(i);
for (int j = 0; j < widgetItemsPerRow.size(); j++) {
- TableRow row = (TableRow) table.getChildAt(i);
+ WidgetTableRow row = (WidgetTableRow) table.getChildAt(i);
row.setVisibility(View.VISIBLE);
WidgetCell widget = (WidgetCell) row.getChildAt(j);
widget.clear();
+ widget.addPreviewReadyListener(row);
WidgetItem widgetItem = widgetItemsPerRow.get(j);
widget.setVisibility(View.VISIBLE);
- // When preview loads, notify adapter to rebind the item and possibly animate
- widget.applyFromCellItem(widgetItem,
- bitmap -> holder.onPreviewLoaded(Pair.create(widgetItem, bitmap)),
- holder.previewCache.get(widgetItem));
+ widget.applyFromCellItem(widgetItem);
widget.requestLayout();
}
}
@@ -143,14 +146,19 @@
for (int i = 0; i < widgetItemsTable.size(); i++) {
List<WidgetItem> widgetItems = widgetItemsTable.get(i);
- TableRow tableRow;
+ WidgetTableRow tableRow;
if (i < table.getChildCount()) {
- tableRow = (TableRow) table.getChildAt(i);
+ tableRow = (WidgetTableRow) table.getChildAt(i);
} else {
- tableRow = new TableRow(table.getContext());
+ tableRow = new WidgetTableRow(table.getContext());
tableRow.setGravity(Gravity.TOP);
table.addView(tableRow);
}
+ // Pass resize delay to let the "move" and "change" animations run before resizing the
+ // row.
+ tableRow.setupRow(widgetItems.size(),
+ /*resizeDelayMs=*/
+ areAnimatorsEnabled() ? (CHANGE_DURATION_MS + MOVE_DURATION_MS) : 0);
if (tableRow.getChildCount() > widgetItems.size()) {
for (int j = widgetItems.size(); j < tableRow.getChildCount(); j++) {
tableRow.getChildAt(j).setVisibility(View.GONE);
@@ -161,6 +169,7 @@
R.layout.widget_cell, tableRow, false);
// set up touch.
widget.setOnClickListener(mIconClickListener);
+ widget.addPreviewReadyListener(tableRow);
View preview = widget.findViewById(R.id.widget_preview_container);
preview.setOnClickListener(mIconClickListener);
preview.setOnLongClickListener(mIconLongClickListener);
@@ -176,7 +185,7 @@
int numOfRows = holder.tableContainer.getChildCount();
holder.previewCache.clear();
for (int i = 0; i < numOfRows; i++) {
- TableRow tableRow = (TableRow) holder.tableContainer.getChildAt(i);
+ WidgetTableRow tableRow = (WidgetTableRow) holder.tableContainer.getChildAt(i);
int numOfCols = tableRow.getChildCount();
for (int j = 0; j < numOfCols; j++) {
WidgetCell widget = (WidgetCell) tableRow.getChildAt(j);
diff --git a/src/com/android/launcher3/widget/picker/WidgetsRecommendationTableLayout.java b/src/com/android/launcher3/widget/picker/WidgetsRecommendationTableLayout.java
index 6dbad5c..1ed3d88 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsRecommendationTableLayout.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsRecommendationTableLayout.java
@@ -27,9 +27,7 @@
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
-import android.view.ViewGroup;
import android.widget.TableLayout;
-import android.widget.TableRow;
import androidx.annotation.Nullable;
import androidx.annotation.Px;
@@ -38,6 +36,7 @@
import com.android.launcher3.R;
import com.android.launcher3.model.WidgetItem;
import com.android.launcher3.widget.WidgetCell;
+import com.android.launcher3.widget.WidgetTableRow;
import com.android.launcher3.widget.picker.util.WidgetPreviewContainerSize;
import java.util.ArrayList;
@@ -105,7 +104,8 @@
for (int i = 0; i < recommendationTable.size(); i++) {
List<WidgetItem> widgetItems = recommendationTable.get(i);
- TableRow tableRow = new TableRow(getContext());
+ WidgetTableRow tableRow = new WidgetTableRow(getContext());
+ tableRow.setupRow(widgetItems.size(), /*resizeDelayMs=*/ 0);
tableRow.setGravity(Gravity.TOP);
for (WidgetItem widgetItem : widgetItems) {
WidgetCell widgetCell = addItemCell(tableRow);
@@ -121,9 +121,10 @@
setVisibility(VISIBLE);
}
- private WidgetCell addItemCell(ViewGroup parent) {
+ private WidgetCell addItemCell(WidgetTableRow parent) {
WidgetCell widget = (WidgetCell) LayoutInflater.from(
getContext()).inflate(R.layout.widget_cell, parent, false);
+ widget.addPreviewReadyListener(parent);
widget.setOnClickListener(mWidgetCellOnClickListener);
View previewContainer = widget.findViewById(R.id.widget_preview_container);
diff --git a/src/com/android/launcher3/widget/picker/WidgetsTwoPaneSheet.java b/src/com/android/launcher3/widget/picker/WidgetsTwoPaneSheet.java
index f835e18..5d71db6 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsTwoPaneSheet.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsTwoPaneSheet.java
@@ -67,7 +67,7 @@
// This ratio defines the max percentage of content area that the recommendations can display
// with respect to the bottom sheet's height.
- private static final float RECOMMENDATION_SECTION_HEIGHT_RATIO_TWO_PANE = 0.60f;
+ private static final float RECOMMENDATION_SECTION_HEIGHT_RATIO_TWO_PANE = 0.70f;
private FrameLayout mSuggestedWidgetsContainer;
private WidgetsListHeader mSuggestedWidgetsHeader;
private PackageUserKey mSuggestedWidgetsPackageUserKey;
diff --git a/src/com/android/launcher3/widget/util/WidgetDragScaleUtils.java b/src/com/android/launcher3/widget/util/WidgetDragScaleUtils.java
new file mode 100644
index 0000000..b8e7248
--- /dev/null
+++ b/src/com/android/launcher3/widget/util/WidgetDragScaleUtils.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.widget.util;
+
+import static com.android.launcher3.widget.util.WidgetSizes.getWidgetSizePx;
+
+import android.content.Context;
+import android.util.Size;
+
+import androidx.annotation.Px;
+
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.R;
+import com.android.launcher3.model.data.ItemInfo;
+
+/** Utility classes to evaluate widget scale during drag and drops. **/
+public final class WidgetDragScaleUtils {
+ // Widgets are 5% scaled down relative to their size to have shadow display well inside the
+ // drop target frame (if its possible to scale it down within visible area under the finger).
+ private static final float WIDGET_SCALE_DOWN = 0.05f;
+
+ /**
+ * Returns the scale to be applied to given dragged view to scale it down relative to the
+ * spring loaded workspace. Applies additional scale down offset to get it a little inside
+ * the drop target frame. If the relative scale is smaller than minimum size needed to keep the
+ * view visible under the finger, scale down is performed only until the minimum size.
+ */
+ @Px
+ public static float getWidgetDragScalePx(Context context, DeviceProfile deviceProfile,
+ @Px float draggedViewWidthPx, @Px float draggedViewHeightPx, ItemInfo itemInfo) {
+ int minSize = context.getResources().getDimensionPixelSize(
+ R.dimen.widget_drag_view_min_scale_down_size);
+ Size widgetSizesPx = getWidgetSizePx(deviceProfile, itemInfo.spanX, itemInfo.spanY);
+
+ // We add workspace spring load scale, since the widget's drop target is also scaled, so
+ // the widget size is essentially that smaller.
+ float desiredWidgetScale = deviceProfile.getWorkspaceSpringLoadScale(context)
+ - WIDGET_SCALE_DOWN;
+ float desiredWidgetWidthPx = Math.max(minSize,
+ (desiredWidgetScale * widgetSizesPx.getWidth()));
+ float desiredWidgetHeightPx = Math.max(minSize,
+ desiredWidgetScale * widgetSizesPx.getHeight());
+
+ final float bitmapAspectRatio = draggedViewWidthPx / draggedViewHeightPx;
+ final float containerAspectRatio = desiredWidgetWidthPx / desiredWidgetHeightPx;
+
+ // This downscales large views to fit inside drop target frame. Smaller drawable views may
+ // be up-scaled if they are smaller than the min size;
+ final float scale = bitmapAspectRatio >= containerAspectRatio ? desiredWidgetWidthPx
+ / draggedViewWidthPx : desiredWidgetHeightPx / draggedViewHeightPx;
+ // scale in terms of dp to be applied to the drag shadow during drag and drop
+ return (draggedViewWidthPx * scale) - draggedViewWidthPx;
+ }
+}
diff --git a/src_no_quickstep/com/android/launcher3/uioverrides/states/AllAppsState.java b/src_no_quickstep/com/android/launcher3/uioverrides/states/AllAppsState.java
index b62dbd1..9865516 100644
--- a/src_no_quickstep/com/android/launcher3/uioverrides/states/AllAppsState.java
+++ b/src_no_quickstep/com/android/launcher3/uioverrides/states/AllAppsState.java
@@ -53,6 +53,11 @@
}
@Override
+ public int getTitle() {
+ return R.string.all_apps_label;
+ }
+
+ @Override
public int getVisibleElements(Launcher launcher) {
return ALL_APPS_CONTENT;
}
diff --git a/tests/Android.bp b/tests/Android.bp
index 5794cce..1dcb2a6 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -198,6 +198,9 @@
"androidx.test.uiautomator_uiautomator",
"androidx.core_core-animation-testing",
"androidx.test.ext.junit",
+ "androidx.test.espresso.core",
+ "androidx.test.espresso.contrib",
+ "androidx.test.espresso.intents",
"androidx.test.rules",
"uiautomator-helpers",
"inline-mockito-robolectric-prebuilt",
@@ -217,4 +220,5 @@
],
instrumentation_for: "Launcher3",
upstream: true,
+ strict_mode: false,
}
diff --git a/tests/multivalentTests/shared/com/android/launcher3/testing/shared/TestProtocol.java b/tests/multivalentTests/shared/com/android/launcher3/testing/shared/TestProtocol.java
index c3b7a2a..59d0de6 100644
--- a/tests/multivalentTests/shared/com/android/launcher3/testing/shared/TestProtocol.java
+++ b/tests/multivalentTests/shared/com/android/launcher3/testing/shared/TestProtocol.java
@@ -169,13 +169,11 @@
public static final String REQUEST_MOCK_SENSOR_ROTATION = "mock-sensor-rotation";
public static final String PERMANENT_DIAG_TAG = "TaplTarget";
- public static final String TWO_NEXUS_LAUNCHER_ACTIVITY_WHILE_UNLOCKING = "b/273347463";
public static final String ICON_MISSING = "b/282963545";
public static final String UIOBJECT_STALE_ELEMENT = "b/319501259";
public static final String TEST_DRAG_APP_ICON_TO_MULTIPLE_WORKSPACES_FAILURE = "b/326908466";
public static final String WIDGET_CONFIG_NULL_EXTRA_INTENT = "b/324419890";
public static final String OVERVIEW_SELECT_TOOLTIP_MISALIGNED = "b/332485341";
- public static final String CLOCK_ICON_DRAWABLE_LEAKING = "b/319168409";
public static final String REQUEST_FLAG_ENABLE_GRID_ONLY_OVERVIEW = "enable-grid-only-overview";
public static final String REQUEST_FLAG_ENABLE_APP_PAIRS = "enable-app-pairs";
@@ -183,6 +181,9 @@
public static final String REQUEST_UNSTASH_BUBBLE_BAR_IF_STASHED =
"unstash-bubble-bar-if-stashed";
+ public static final String REQUEST_INJECT_FAKE_TRACKPAD = "inject-fake-trackpad";
+ public static final String REQUEST_EJECT_FAKE_TRACKPAD = "eject-fake-trackpad";
+
/** Logs {@link Log#d(String, String)} if {@link #sDebugTracing} is true. */
public static void testLogD(String tag, String message) {
if (!sDebugTracing) {
diff --git a/tests/multivalentTests/src/com/android/launcher3/AbstractDeviceProfileTest.kt b/tests/multivalentTests/src/com/android/launcher3/AbstractDeviceProfileTest.kt
index e378733..3405635 100644
--- a/tests/multivalentTests/src/com/android/launcher3/AbstractDeviceProfileTest.kt
+++ b/tests/multivalentTests/src/com/android/launcher3/AbstractDeviceProfileTest.kt
@@ -57,7 +57,7 @@
*
* For an implementation that mocks InvariantDeviceProfile, use [FakeInvariantDeviceProfileTest]
*/
-@AllowedDevices(allowed = [DeviceProduct.CF_PHONE])
+@AllowedDevices(allowed = [DeviceProduct.CF_PHONE, DeviceProduct.HOST_SIDE_X86_64])
@IgnoreLimit(ignoreLimit = BuildConfig.IS_STUDIO_BUILD)
abstract class AbstractDeviceProfileTest {
protected val testContext: Context = InstrumentationRegistry.getInstrumentation().context
diff --git a/tests/src/com/android/launcher3/AbstractFloatingViewHelperTest.kt b/tests/multivalentTests/src/com/android/launcher3/AbstractFloatingViewHelperTest.kt
similarity index 91%
rename from tests/src/com/android/launcher3/AbstractFloatingViewHelperTest.kt
rename to tests/multivalentTests/src/com/android/launcher3/AbstractFloatingViewHelperTest.kt
index 7ff544d..5344d5c 100644
--- a/tests/src/com/android/launcher3/AbstractFloatingViewHelperTest.kt
+++ b/tests/multivalentTests/src/com/android/launcher3/AbstractFloatingViewHelperTest.kt
@@ -25,7 +25,7 @@
import org.mockito.kotlin.mock
import org.mockito.kotlin.never
import org.mockito.kotlin.verify
-import org.mockito.kotlin.verifyZeroInteractions
+import org.mockito.kotlin.verifyNoMoreInteractions
import org.mockito.kotlin.whenever
/** Test for AbstractFloatingViewHelper */
@@ -60,7 +60,8 @@
AbstractFloatingView.TYPE_ALL
)
- verifyZeroInteractions(view)
+ // b/343530737
+ verifyNoMoreInteractions(view)
verify(folderView).close(true)
verify(taskMenuView).close(true)
}
@@ -73,7 +74,8 @@
AbstractFloatingView.TYPE_TASK_MENU
)
- verifyZeroInteractions(view)
+ // b/343530737
+ verifyNoMoreInteractions(view)
verify(folderView, never()).close(any())
verify(taskMenuView).close(true)
}
@@ -86,7 +88,8 @@
AbstractFloatingView.TYPE_PIN_IME_POPUP
)
- verifyZeroInteractions(view)
+ // b/343530737
+ verifyNoMoreInteractions(view)
verify(folderView, never()).close(any())
verify(taskMenuView, never()).close(any())
}
@@ -99,7 +102,8 @@
AbstractFloatingView.TYPE_FOLDER or AbstractFloatingView.TYPE_TASK_MENU
)
- verifyZeroInteractions(view)
+ // b/343530737
+ verifyNoMoreInteractions(view)
verify(folderView).close(false)
verify(taskMenuView).close(false)
}
diff --git a/tests/multivalentTests/src/com/android/launcher3/UtilitiesTest.kt b/tests/multivalentTests/src/com/android/launcher3/UtilitiesTest.kt
new file mode 100644
index 0000000..60a4197
--- /dev/null
+++ b/tests/multivalentTests/src/com/android/launcher3/UtilitiesTest.kt
@@ -0,0 +1,97 @@
+/*
+ * 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
+
+import android.content.Context
+import android.view.View
+import android.view.ViewGroup
+import androidx.test.core.app.ApplicationProvider.getApplicationContext
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.launcher3.util.ActivityContextWrapper
+import org.junit.Assert.*
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class UtilitiesTest {
+
+ private lateinit var mContext: Context
+
+ @Before
+ fun setUp() {
+ mContext = ActivityContextWrapper(getApplicationContext())
+ }
+
+ @Test
+ fun testIsPropertyEnabled() {
+ // This assumes the property "propertyName" is not enabled by default
+ assertFalse(Utilities.isPropertyEnabled("propertyName"))
+ }
+
+ @Test
+ fun testGetDescendantCoordRelativeToAncestor() {
+ val ancestor =
+ object : ViewGroup(mContext) {
+ override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {}
+ }
+ val descendant = View(mContext)
+
+ descendant.x = 50f
+ descendant.y = 30f
+ descendant.scaleX = 2f
+ descendant.scaleY = 2f
+
+ ancestor.addView(descendant)
+
+ val coord = floatArrayOf(10f, 15f)
+ val scale =
+ Utilities.getDescendantCoordRelativeToAncestor(descendant, ancestor, coord, false)
+
+ assertEquals(2f, scale) // Expecting scale to be 2f
+ assertEquals(70f, coord[0])
+ assertEquals(60f, coord[1])
+ }
+
+ @Test
+ fun testRoundArray() {
+ val floatArray = floatArrayOf(1.2f, 3.7f, 5.5f)
+ val intArray = IntArray(3)
+ Utilities.roundArray(floatArray, intArray)
+ assertArrayEquals(intArrayOf(1, 4, 6), intArray)
+ }
+
+ @Test
+ fun testOffsetPoints() {
+ val points = floatArrayOf(1f, 2f, 3f, 4f)
+ Utilities.offsetPoints(points, 5f, 6f)
+
+ val expected = listOf(6f, 8f, 8f, 10f)
+ assertEquals(expected, points.toList())
+ }
+
+ @Test
+ fun testPointInView() {
+ val view = View(mContext)
+ view.layout(0, 0, 100, 100)
+
+ assertTrue(Utilities.pointInView(view, 50f, 50f, 0f)) // Inside view
+ assertFalse(Utilities.pointInView(view, -10f, -10f, 0f)) // Outside view
+ assertTrue(Utilities.pointInView(view, -5f, -5f, 10f)) // Inside slop
+ assertFalse(Utilities.pointInView(view, 115f, 115f, 10f)) // Outside slop
+ }
+}
diff --git a/tests/src/com/android/launcher3/settings/SettingsActivityTest.java b/tests/multivalentTests/src/com/android/launcher3/settings/SettingsActivityTest.java
similarity index 100%
rename from tests/src/com/android/launcher3/settings/SettingsActivityTest.java
rename to tests/multivalentTests/src/com/android/launcher3/settings/SettingsActivityTest.java
diff --git a/tests/src/com/android/launcher3/util/DisplayControllerTest.kt b/tests/multivalentTests/src/com/android/launcher3/util/DisplayControllerTest.kt
similarity index 98%
rename from tests/src/com/android/launcher3/util/DisplayControllerTest.kt
rename to tests/multivalentTests/src/com/android/launcher3/util/DisplayControllerTest.kt
index 273f0c4..41effa2 100644
--- a/tests/src/com/android/launcher3/util/DisplayControllerTest.kt
+++ b/tests/multivalentTests/src/com/android/launcher3/util/DisplayControllerTest.kt
@@ -27,7 +27,6 @@
import android.view.Surface
import androidx.test.annotation.UiThreadTest
import androidx.test.core.app.ApplicationProvider
-import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.launcher3.LauncherPrefs
import com.android.launcher3.LauncherPrefs.Companion.TASKBAR_PINNING
@@ -55,7 +54,7 @@
/** Unit tests for {@link DisplayController} */
@SmallTest
-@RunWith(AndroidJUnit4::class)
+@RunWith(LauncherMultivalentJUnit::class)
class DisplayControllerTest {
private val appContext: Context = ApplicationProvider.getApplicationContext()
diff --git a/tests/src/com/android/launcher3/util/LockedUserStateTest.kt b/tests/multivalentTests/src/com/android/launcher3/util/LockedUserStateTest.kt
similarity index 95%
rename from tests/src/com/android/launcher3/util/LockedUserStateTest.kt
rename to tests/multivalentTests/src/com/android/launcher3/util/LockedUserStateTest.kt
index 2c4a54f..2711d7a 100644
--- a/tests/src/com/android/launcher3/util/LockedUserStateTest.kt
+++ b/tests/multivalentTests/src/com/android/launcher3/util/LockedUserStateTest.kt
@@ -28,7 +28,7 @@
import org.junit.runner.RunWith
import org.mockito.kotlin.mock
import org.mockito.kotlin.verify
-import org.mockito.kotlin.verifyZeroInteractions
+import org.mockito.kotlin.verifyNoMoreInteractions
import org.mockito.kotlin.whenever
/** Unit tests for {@link LockedUserState} */
@@ -58,7 +58,8 @@
val action: Runnable = mock()
val state = LockedUserState(context)
state.runOnUserUnlocked(action)
- verifyZeroInteractions(action)
+ // b/343530737
+ verifyNoMoreInteractions(action)
state.mUserUnlockedReceiver.onReceive(context, Intent(Intent.ACTION_USER_UNLOCKED))
verify(action).run()
}
diff --git a/tests/multivalentTests/src/com/android/launcher3/widget/util/WidgetDragScaleUtilsTest.kt b/tests/multivalentTests/src/com/android/launcher3/widget/util/WidgetDragScaleUtilsTest.kt
new file mode 100644
index 0000000..63833e4
--- /dev/null
+++ b/tests/multivalentTests/src/com/android/launcher3/widget/util/WidgetDragScaleUtilsTest.kt
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.widget.util
+
+import android.content.Context
+import android.graphics.Point
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.launcher3.DeviceProfile
+import com.android.launcher3.LauncherAppState
+import com.android.launcher3.R
+import com.android.launcher3.model.data.ItemInfo
+import com.android.launcher3.util.ActivityContextWrapper
+import com.google.common.truth.Truth.assertThat
+import org.junit.Assume.assumeTrue
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.any
+import org.mockito.Mockito
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.whenever
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class WidgetDragScaleUtilsTest {
+ private lateinit var context: Context
+ private lateinit var itemInfo: ItemInfo
+ private lateinit var deviceProfile: DeviceProfile
+
+ @Before
+ fun setup() {
+ context = ActivityContextWrapper(ApplicationProvider.getApplicationContext())
+
+ itemInfo = ItemInfo()
+
+ deviceProfile =
+ Mockito.spy(LauncherAppState.getIDP(context).getDeviceProfile(context).copy(context))
+
+ doReturn(0.8f)
+ .whenever(deviceProfile).getWorkspaceSpringLoadScale(any(Context::class.java))
+ deviceProfile.cellLayoutBorderSpacePx = Point(CELL_SPACING, CELL_SPACING)
+ deviceProfile.widgetPadding.setEmpty()
+ }
+
+ @Test
+ fun getWidgetDragScalePx_largeDraggedView_downScaled() {
+ val minSize = context.resources.getDimensionPixelSize(
+ R.dimen.widget_drag_view_min_scale_down_size)
+ whenever(deviceProfile.cellSize).thenReturn(Point(minSize * 2, minSize * 2))
+
+ itemInfo.spanX = 2
+ itemInfo.spanY = 2
+
+ val widgetSize = WidgetSizes.getWidgetSizePx(deviceProfile, itemInfo.spanX, itemInfo.spanY)
+ // Assume dragged view was a drawable which was larger than widget's size.
+ val draggedViewWidthPx = widgetSize.width + 0.5f * widgetSize.width
+ val draggedViewHeightPx = widgetSize.height + 0.5f * widgetSize.height
+ // Returns negative scale pixels - i.e. downscaled
+ assertThat(
+ WidgetDragScaleUtils.getWidgetDragScalePx(
+ context,
+ deviceProfile,
+ draggedViewWidthPx,
+ draggedViewHeightPx,
+ itemInfo
+ )
+ )
+ .isLessThan(0)
+ }
+
+ @Test
+ fun getWidgetDragScalePx_draggedViewSameAsWidgetSize_downScaled() {
+ val minSize = context.resources.getDimensionPixelSize(
+ R.dimen.widget_drag_view_min_scale_down_size)
+ whenever(deviceProfile.cellSize).thenReturn(Point(minSize * 2, minSize * 2))
+ itemInfo.spanX = 4
+ itemInfo.spanY = 2
+
+ val widgetSize = WidgetSizes.getWidgetSizePx(deviceProfile, itemInfo.spanX, itemInfo.spanY)
+ // Assume dragged view was a drawable which was larger than widget's size.
+ val draggedViewWidthPx = widgetSize.width.toFloat()
+ val draggedViewHeightPx = widgetSize.height.toFloat()
+ // Returns negative scale pixels - i.e. downscaled
+ // Even if dragged view was of same size as widget's drop target, to accommodate the spring
+ // load scaling of workspace and additionally getting the view inside of drop target frame,
+ // widget would be downscaled.
+ assertThat(
+ WidgetDragScaleUtils.getWidgetDragScalePx(
+ context,
+ deviceProfile,
+ draggedViewWidthPx,
+ draggedViewHeightPx,
+ itemInfo
+ )
+ )
+ .isLessThan(0)
+ }
+
+ @Test
+ fun getWidgetDragScalePx_draggedViewSmallerThanMinSize_scaledSizeIsAtLeastMinSize() {
+ val minSizePx =
+ context.resources.getDimensionPixelSize(R.dimen.widget_drag_view_min_scale_down_size)
+ // Assume min size is greater than cell size, so that, we know the upscale of dragged view
+ // is due to min size enforcement.
+ whenever(deviceProfile.cellSize).thenReturn(Point(minSizePx / 2, minSizePx / 2))
+ itemInfo.spanX = 1
+ itemInfo.spanY = 1
+
+ val draggedViewWidthPx = minSizePx - 15f
+ val draggedViewHeightPx = minSizePx - 15f
+
+ // Returns positive scale pixels - i.e. up-scaled
+ val finalScalePx =
+ WidgetDragScaleUtils.getWidgetDragScalePx(
+ context,
+ deviceProfile,
+ draggedViewWidthPx,
+ draggedViewHeightPx,
+ itemInfo
+ )
+
+ val effectiveWidthPx = draggedViewWidthPx + finalScalePx
+ val scaleFactor = (draggedViewWidthPx + finalScalePx) / draggedViewWidthPx
+ val effectiveHeightPx = scaleFactor * draggedViewHeightPx
+ // Both original height and width were smaller than min size, scaling them down below min
+ // size would have made them not visible under the finger. Here, as expected, widget is
+ // at least as large as min size.
+ assertThat(effectiveWidthPx).isAtLeast(minSizePx)
+ assertThat(effectiveHeightPx).isAtLeast(minSizePx)
+ }
+
+ companion object {
+ const val CELL_SPACING = 10
+ }
+}
diff --git a/tests/multivalentTestsForDeviceless b/tests/multivalentTestsForDeviceless
deleted file mode 120000
index 20ee34a..0000000
--- a/tests/multivalentTestsForDeviceless
+++ /dev/null
@@ -1 +0,0 @@
-multivalentTests
\ No newline at end of file
diff --git a/tests/src/com/android/launcher3/allapps/PrivateProfileManagerTest.java b/tests/src/com/android/launcher3/allapps/PrivateProfileManagerTest.java
index 4cd2a07..57117cb 100644
--- a/tests/src/com/android/launcher3/allapps/PrivateProfileManagerTest.java
+++ b/tests/src/com/android/launcher3/allapps/PrivateProfileManagerTest.java
@@ -130,7 +130,7 @@
public void lockPrivateProfile_requestsQuietModeAsTrue() throws Exception {
when(mAllAppsStore.hasModelFlag(FLAG_PRIVATE_PROFILE_QUIET_MODE_ENABLED)).thenReturn(false);
- mPrivateProfileManager.lockPrivateProfile();
+ mPrivateProfileManager.setQuietMode(true /* lock */);
awaitTasksCompleted();
Mockito.verify(mUserManager).requestQuietModeEnabled(true, PRIVATE_HANDLE);
@@ -140,7 +140,7 @@
public void unlockPrivateProfile_requestsQuietModeAsFalse() throws Exception {
when(mAllAppsStore.hasModelFlag(FLAG_PRIVATE_PROFILE_QUIET_MODE_ENABLED)).thenReturn(true);
- mPrivateProfileManager.unlockPrivateProfile();
+ mPrivateProfileManager.setQuietMode(false /* unlock */);
awaitTasksCompleted();
Mockito.verify(mUserManager).requestQuietModeEnabled(false, PRIVATE_HANDLE);
@@ -149,7 +149,7 @@
@Test
public void quietModeFlagPresent_privateSpaceIsResetToDisabled() {
PrivateProfileManager privateProfileManager = spy(mPrivateProfileManager);
- doNothing().when(privateProfileManager).resetPrivateSpaceDecorator(anyInt());
+ doNothing().when(privateProfileManager).addPrivateSpaceDecorator(anyInt());
doNothing().when(privateProfileManager).executeLock();
doReturn(mAllAppsRecyclerView).when(privateProfileManager).getMainRecyclerView();
when(mAllAppsStore.hasModelFlag(FLAG_PRIVATE_PROFILE_QUIET_MODE_ENABLED))
@@ -169,14 +169,14 @@
@Test
public void transitioningToUnlocked_resetCallsPostUnlock() throws Exception {
PrivateProfileManager privateProfileManager = spy(mPrivateProfileManager);
- doNothing().when(privateProfileManager).resetPrivateSpaceDecorator(anyInt());
+ doNothing().when(privateProfileManager).addPrivateSpaceDecorator(anyInt());
doReturn(mAllAppsRecyclerView).when(privateProfileManager).getMainRecyclerView();
when(mAllAppsStore.hasModelFlag(FLAG_PRIVATE_PROFILE_QUIET_MODE_ENABLED))
.thenReturn(false);
doNothing().when(privateProfileManager).expandPrivateSpace();
when(privateProfileManager.getCurrentState()).thenReturn(STATE_DISABLED);
- privateProfileManager.unlockPrivateProfile();
+ privateProfileManager.setQuietMode(false /* unlock */);
privateProfileManager.reset();
awaitTasksCompleted();
@@ -186,7 +186,7 @@
@Test
public void transitioningToLocked_resetCallsExecuteLock() throws Exception {
PrivateProfileManager privateProfileManager = spy(mPrivateProfileManager);
- doNothing().when(privateProfileManager).resetPrivateSpaceDecorator(anyInt());
+ doNothing().when(privateProfileManager).addPrivateSpaceDecorator(anyInt());
doNothing().when(privateProfileManager).executeLock();
doReturn(mAllAppsRecyclerView).when(privateProfileManager).getMainRecyclerView();
when(mAllAppsStore.hasModelFlag(FLAG_PRIVATE_PROFILE_QUIET_MODE_ENABLED))
@@ -194,7 +194,7 @@
doNothing().when(privateProfileManager).expandPrivateSpace();
when(privateProfileManager.getCurrentState()).thenReturn(STATE_ENABLED);
- privateProfileManager.lockPrivateProfile();
+ privateProfileManager.setQuietMode(true /* lock */);
privateProfileManager.reset();
awaitTasksCompleted();
@@ -208,7 +208,7 @@
ArgumentCaptor<Intent> acIntent = ArgumentCaptor.forClass(Intent.class);
mPrivateProfileManager.setPrivateSpaceSettingsAvailable(true);
- mPrivateProfileManager.openPrivateSpaceSettings(null);
+ mContext.startActivity(expectedIntent);
Mockito.verify(mContext).startActivity(acIntent.capture());
assertEquals("Intent Action is different",
diff --git a/tests/src/com/android/launcher3/allapps/PrivateSpaceHeaderViewTest.java b/tests/src/com/android/launcher3/allapps/PrivateSpaceHeaderViewTest.java
index 512b2ac..eac7f63 100644
--- a/tests/src/com/android/launcher3/allapps/PrivateSpaceHeaderViewTest.java
+++ b/tests/src/com/android/launcher3/allapps/PrivateSpaceHeaderViewTest.java
@@ -133,7 +133,7 @@
Bitmap unlockButton = getBitmap(mContext.getDrawable(R.drawable.ic_lock));
PrivateProfileManager privateProfileManager = spy(mPrivateProfileManager);
when(privateProfileManager.getCurrentState()).thenReturn(STATE_DISABLED);
- privateProfileManager.addPrivateSpaceHeaderViewElements(mPsHeaderLayout);
+ privateProfileManager.bindPrivateSpaceHeaderViewElements(mPsHeaderLayout);
awaitTasksCompleted();
int totalContainerHeaderView = 0;
@@ -168,7 +168,7 @@
PrivateProfileManager privateProfileManager = spy(mPrivateProfileManager);
when(privateProfileManager.getCurrentState()).thenReturn(STATE_ENABLED);
when(privateProfileManager.isPrivateSpaceSettingsAvailable()).thenReturn(true);
- privateProfileManager.addPrivateSpaceHeaderViewElements(mPsHeaderLayout);
+ privateProfileManager.bindPrivateSpaceHeaderViewElements(mPsHeaderLayout);
awaitTasksCompleted();
int totalContainerHeaderView = 0;
@@ -210,7 +210,7 @@
PrivateProfileManager privateProfileManager = spy(mPrivateProfileManager);
when(privateProfileManager.getCurrentState()).thenReturn(STATE_ENABLED);
when(privateProfileManager.isPrivateSpaceSettingsAvailable()).thenReturn(false);
- privateProfileManager.addPrivateSpaceHeaderViewElements(mPsHeaderLayout);
+ privateProfileManager.bindPrivateSpaceHeaderViewElements(mPsHeaderLayout);
awaitTasksCompleted();
int totalContainerHeaderView = 0;
@@ -248,7 +248,7 @@
Bitmap transitionImage = getBitmap(mContext.getDrawable(R.drawable.bg_ps_transition_image));
PrivateProfileManager privateProfileManager = spy(mPrivateProfileManager);
when(privateProfileManager.getCurrentState()).thenReturn(STATE_TRANSITION);
- privateProfileManager.addPrivateSpaceHeaderViewElements(mPsHeaderLayout);
+ privateProfileManager.bindPrivateSpaceHeaderViewElements(mPsHeaderLayout);
awaitTasksCompleted();
int totalContainerHeaderView = 0;
diff --git a/tests/src/com/android/launcher3/allapps/PrivateSpaceSettingsButtonTest.java b/tests/src/com/android/launcher3/allapps/PrivateSpaceSettingsButtonTest.java
new file mode 100644
index 0000000..9537e1c
--- /dev/null
+++ b/tests/src/com/android/launcher3/allapps/PrivateSpaceSettingsButtonTest.java
@@ -0,0 +1,56 @@
+/*
+ * 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.allapps;
+
+import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
+
+import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_PRIVATESPACE;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.launcher3.model.data.AppInfo;
+import com.android.launcher3.util.ActivityContextWrapper;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(AndroidJUnit4.class)
+public class PrivateSpaceSettingsButtonTest {
+
+ private PrivateSpaceSettingsButton mVut;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ Context context = new ActivityContextWrapper(getApplicationContext());
+ mVut = new PrivateSpaceSettingsButton(context);
+ }
+
+ @Test
+ public void privateSpaceSettingsAppInfo_hasCorrectIdAndContainer() {
+ AppInfo appInfo = mVut.createPrivateSpaceSettingsAppInfo();
+
+ assertThat(appInfo.id).isEqualTo(CONTAINER_PRIVATESPACE);
+ assertThat(appInfo.container).isEqualTo(CONTAINER_PRIVATESPACE);
+ }
+}
diff --git a/tests/src/com/android/launcher3/allapps/TaplOpenCloseAllAppsTest.java b/tests/src/com/android/launcher3/allapps/TaplOpenCloseAllAppsTest.java
index f3f6fa5..05a1224 100644
--- a/tests/src/com/android/launcher3/allapps/TaplOpenCloseAllAppsTest.java
+++ b/tests/src/com/android/launcher3/allapps/TaplOpenCloseAllAppsTest.java
@@ -120,7 +120,6 @@
@Test
@PortraitLandscape
@PlatinumTest(focusArea = "launcher")
- @ScreenRecordRule.ScreenRecord // b/322228038
public void testAllAppsFromHome() {
// Test opening all apps
assertNotNull("switchToAllApps() returned null",
diff --git a/tests/src/com/android/launcher3/dragging/TaplDragTest.java b/tests/src/com/android/launcher3/dragging/TaplDragTest.java
index 1c41ded..41abcf8 100644
--- a/tests/src/com/android/launcher3/dragging/TaplDragTest.java
+++ b/tests/src/com/android/launcher3/dragging/TaplDragTest.java
@@ -41,6 +41,7 @@
import com.android.launcher3.ui.AbstractLauncherUiTest;
import com.android.launcher3.ui.PortraitLandscapeRunner.PortraitLandscape;
import com.android.launcher3.util.TestUtil;
+import com.android.launcher3.util.rule.ScreenRecordRule;
import org.junit.Test;
@@ -194,6 +195,7 @@
@PlatinumTest(focusArea = "launcher")
@Test
@PortraitLandscape
+ @ScreenRecordRule.ScreenRecord // b/343953783
public void testDragAppIcon() {
final HomeAllApps allApps = mLauncher.getWorkspace().switchToAllApps();
@@ -221,24 +223,7 @@
public void testDragAppIconToMultipleWorkspaceCells() throws Exception {
long startTime, endTime, elapsedTime;
Point[] targets = TestUtil.getCornersAndCenterPositions(mLauncher);
-
- for (Point target : targets) {
- startTime = SystemClock.uptimeMillis();
- final HomeAllApps allApps = mLauncher.getWorkspace().switchToAllApps();
- allApps.freeze();
- try {
- allApps.getAppIcon(TEST_APP_NAME).dragToWorkspace(target.x, target.y);
- } finally {
- allApps.unfreeze();
- }
- // Reset the workspace for the next shortcut creation.
- reinitializeLauncherData(true);
- endTime = SystemClock.uptimeMillis();
- elapsedTime = endTime - startTime;
- Log.d("testDragAppIconToWorkspaceCellTime",
- "Milliseconds taken to drag app icon to workspace cell: " + elapsedTime);
- }
-
+ reinitializeLauncherData(true);
// test to move a shortcut to other cell.
final HomeAppIcon launcherTestAppIcon = createShortcutInCenterIfNotExist(TEST_APP_NAME);
for (Point target : targets) {
diff --git a/tests/multivalentTests/src/com/android/launcher3/folder/FolderNameProviderTest.java b/tests/src/com/android/launcher3/folder/FolderNameProviderTest.java
similarity index 100%
rename from tests/multivalentTests/src/com/android/launcher3/folder/FolderNameProviderTest.java
rename to tests/src/com/android/launcher3/folder/FolderNameProviderTest.java
diff --git a/tests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.kt b/tests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.kt
index 78c61d5..370af0c 100644
--- a/tests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.kt
+++ b/tests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.kt
@@ -35,7 +35,7 @@
import org.mockito.kotlin.mock
import org.mockito.kotlin.same
import org.mockito.kotlin.verify
-import org.mockito.kotlin.verifyZeroInteractions
+import org.mockito.kotlin.verifyNoMoreInteractions
import org.mockito.kotlin.whenever
/** Tests for [AddWorkspaceItemsTask] */
@@ -97,7 +97,8 @@
val addedItems = testAddItems(nonEmptyScreenIds, itemToAdd)
assertThat(addedItems.size).isEqualTo(0)
- verifyZeroInteractions(mWorkspaceItemSpaceFinder)
+ // b/343530737
+ verifyNoMoreInteractions(mWorkspaceItemSpaceFinder)
}
@Test
diff --git a/tests/src/com/android/launcher3/popup/SystemShortcutTest.java b/tests/src/com/android/launcher3/popup/SystemShortcutTest.java
index c663be0..98b6b4b 100644
--- a/tests/src/com/android/launcher3/popup/SystemShortcutTest.java
+++ b/tests/src/com/android/launcher3/popup/SystemShortcutTest.java
@@ -49,7 +49,6 @@
import android.view.View;
import androidx.test.annotation.UiThreadTest;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.launcher3.allapps.PrivateProfileManager;
@@ -60,6 +59,7 @@
import com.android.launcher3.util.ApiWrapper;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.LauncherModelHelper.SandboxModelContext;
+import com.android.launcher3.util.LauncherMultivalentJUnit;
import com.android.launcher3.util.TestSandboxModelContextWrapper;
import com.android.launcher3.util.UserIconInfo;
import com.android.launcher3.views.BaseDragLayer;
@@ -76,7 +76,7 @@
import java.util.ArrayList;
@SmallTest
-@RunWith(AndroidJUnit4.class)
+@RunWith(LauncherMultivalentJUnit.class)
public class SystemShortcutTest {
@Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(DEVICE_DEFAULT);
private static final UserHandle PRIVATE_HANDLE = new UserHandle(11);
diff --git a/tests/src/com/android/launcher3/provider/RestoreDbTaskTest.java b/tests/src/com/android/launcher3/provider/RestoreDbTaskTest.java
index 733f1e9..b3675a6 100644
--- a/tests/src/com/android/launcher3/provider/RestoreDbTaskTest.java
+++ b/tests/src/com/android/launcher3/provider/RestoreDbTaskTest.java
@@ -38,7 +38,7 @@
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import android.app.backup.BackupManager;
@@ -243,7 +243,8 @@
// Then
assertThat(expectedHost.getAppWidgetIds()).isEqualTo(expectedOldIds);
assertThat(mPrefs.has(OLD_APP_WIDGET_IDS, APP_WIDGET_IDS)).isFalse();
- verifyZeroInteractions(mMockController);
+ // b/343530737
+ verifyNoMoreInteractions(mMockController);
}
@Test
diff --git a/tests/src/com/android/launcher3/ui/workspace/TaplTwoPanelWorkspaceTest.java b/tests/src/com/android/launcher3/ui/workspace/TaplTwoPanelWorkspaceTest.java
index a672c01..e92d641 100644
--- a/tests/src/com/android/launcher3/ui/workspace/TaplTwoPanelWorkspaceTest.java
+++ b/tests/src/com/android/launcher3/ui/workspace/TaplTwoPanelWorkspaceTest.java
@@ -113,6 +113,8 @@
@Test
@PortraitLandscape
+ @ScreenRecordRule.ScreenRecord // b/329935119
+ @TestStabilityRule.Stability(flavors = LOCAL | PLATFORM_POSTSUBMIT) // b/329935119
public void testSinglePageDragIconWhenMultiplePageScrollingIsPossible() {
Workspace workspace = mLauncher.getWorkspace();
diff --git a/tests/tapl/com/android/launcher3/tapl/BaseOverview.java b/tests/tapl/com/android/launcher3/tapl/BaseOverview.java
index 2e3944d..e10893e 100644
--- a/tests/tapl/com/android/launcher3/tapl/BaseOverview.java
+++ b/tests/tapl/com/android/launcher3/tapl/BaseOverview.java
@@ -359,21 +359,6 @@
}
/**
- * Gets Overview Actions specific to grouped tasks.
- *
- * @return The Overview group actions bar
- */
- @NonNull
- public OverviewActions getOverviewGroupActions() {
- try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
- "want to get overview group actions")) {
- verifyActiveContainer();
- UiObject2 groupActions = mLauncher.waitForOverviewObject("group_action_buttons");
- return new OverviewActions(groupActions, mLauncher);
- }
- }
-
- /**
* Returns if clear all button is visible.
*/
public boolean isClearAllVisible() {
@@ -469,13 +454,13 @@
if (isActionsViewVisible()) {
if (task.isTaskSplit()) {
- mLauncher.waitForOverviewObject("group_action_buttons");
+ mLauncher.waitForOverviewObject("action_save_app_pair");
} else {
mLauncher.waitForOverviewObject("action_buttons");
}
} else {
mLauncher.waitUntilOverviewObjectGone("action_buttons");
- mLauncher.waitUntilOverviewObjectGone("group_action_buttons");
+ mLauncher.waitUntilOverviewObjectGone("action_save_app_pair");
}
}
}
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index 68b0a36..f02a0c2 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -711,7 +711,7 @@
final LogEventChecker eventChecker = mEventChecker;
mEventChecker = null;
if (checkEvents) {
- final String eventMismatch = eventChecker.verify(0, false);
+ final String eventMismatch = eventChecker.verify(0);
if (eventMismatch != null) {
message = message + ";\n" + eventMismatch;
}
@@ -2318,6 +2318,14 @@
getTestInfo(TestProtocol.REQUEST_UNSTASH_BUBBLE_BAR_IF_STASHED);
}
+ public void injectFakeTrackpad() {
+ getTestInfo(TestProtocol.REQUEST_INJECT_FAKE_TRACKPAD);
+ }
+
+ public void ejectFakeTrackpad() {
+ getTestInfo(TestProtocol.REQUEST_EJECT_FAKE_TRACKPAD);
+ }
+
/** Blocks the taskbar from automatically stashing based on time. */
public void enableBlockTimeout(boolean enable) {
getTestInfo(enable
@@ -2408,7 +2416,7 @@
if (mEventChecker != null) {
mEventChecker = null;
if (mCheckEventsForSuccessfulGestures) {
- final String message = eventChecker.verify(WAIT_TIME_MS, true);
+ final String message = eventChecker.verify(WAIT_TIME_MS);
if (message != null) {
dumpDiagnostics(message);
checkForAnomaly();
diff --git a/tests/tapl/com/android/launcher3/tapl/LogEventChecker.java b/tests/tapl/com/android/launcher3/tapl/LogEventChecker.java
index 672c6e0..055a357 100644
--- a/tests/tapl/com/android/launcher3/tapl/LogEventChecker.java
+++ b/tests/tapl/com/android/launcher3/tapl/LogEventChecker.java
@@ -15,10 +15,6 @@
*/
package com.android.launcher3.tapl;
-import static com.android.launcher3.testing.shared.TestProtocol.SEQUENCE_MAIN;
-import static com.android.launcher3.testing.shared.TestProtocol.SEQUENCE_PILFER;
-import static com.android.launcher3.testing.shared.TestProtocol.SEQUENCE_TIS;
-
import android.os.SystemClock;
import com.android.launcher3.testing.shared.TestProtocol;
@@ -87,25 +83,11 @@
mLauncher.getTestInfo(TestProtocol.REQUEST_STOP_EVENT_LOGGING);
}
- String verify(long waitForExpectedCountMs, boolean successfulGesture) {
+ String verify(long waitForExpectedCountMs) {
final ListMap<String> actualEvents = finishSync(waitForExpectedCountMs);
if (actualEvents == null) return "null event sequences because launcher likely died";
- final String lowLevelDiags = lowLevelMismatchDiagnostics(actualEvents);
- // If we have a sequence mismatch for a successful gesture, we want to provide all low-level
- // details.
- if (successfulGesture) {
- return lowLevelDiags;
- }
-
- final String sequenceMismatchInEnglish = highLevelMismatchDiagnostics(actualEvents);
-
- if (sequenceMismatchInEnglish != null) {
- LauncherInstrumentation.log(lowLevelDiags);
- return "Hint: " + sequenceMismatchInEnglish;
- } else {
- return lowLevelDiags;
- }
+ return lowLevelMismatchDiagnostics(actualEvents);
}
private String lowLevelMismatchDiagnostics(ListMap<String> actualEvents) {
@@ -140,42 +122,6 @@
return hasMismatches ? "Mismatching events: " + sb.toString() : null;
}
- private String highLevelMismatchDiagnostics(ListMap<String> actualEvents) {
- if (!mExpectedEvents.getNonNull(SEQUENCE_TIS).isEmpty()
- && actualEvents.getNonNull(SEQUENCE_TIS).isEmpty()) {
- return "TouchInteractionService didn't receive any of the touch events sent by the "
- + "test";
- }
- if (getMismatchPosition(mExpectedEvents.getNonNull(SEQUENCE_TIS),
- actualEvents.getNonNull(SEQUENCE_TIS)) != -1) {
- // If TIS has a mismatch that we can't convert to high-level diags, don't convert
- // other sequences either.
- return null;
- }
-
- if (mExpectedEvents.getNonNull(SEQUENCE_PILFER).size() == 1
- && actualEvents.getNonNull(SEQUENCE_PILFER).isEmpty()) {
- return "Launcher didn't detect the navigation gesture sent by the test";
- }
- if (mExpectedEvents.getNonNull(SEQUENCE_PILFER).isEmpty()
- && actualEvents.getNonNull(SEQUENCE_PILFER).size() == 1) {
- return "Launcher detected a navigation gesture, but the test didn't send one";
- }
- if (getMismatchPosition(mExpectedEvents.getNonNull(SEQUENCE_PILFER),
- actualEvents.getNonNull(SEQUENCE_PILFER)) != -1) {
- // If Pilfer has a mismatch that we can't convert to high-level diags, don't analyze
- // other sequences.
- return null;
- }
-
- if (!mExpectedEvents.getNonNull(SEQUENCE_MAIN).isEmpty()
- && actualEvents.getNonNull(SEQUENCE_MAIN).isEmpty()) {
- return "None of the touch or keyboard events sent by the test was received by "
- + "Launcher's main thread";
- }
- return null;
- }
-
// If the list of actual events matches the list of expected events, returns -1, otherwise
// the position of the mismatch.
private static int getMismatchPosition(List<Pattern> expected, List<String> actual) {
diff --git a/tests/tapl/com/android/launcher3/tapl/OverviewActions.java b/tests/tapl/com/android/launcher3/tapl/OverviewActions.java
index 486a63b..d7c40a0 100644
--- a/tests/tapl/com/android/launcher3/tapl/OverviewActions.java
+++ b/tests/tapl/com/android/launcher3/tapl/OverviewActions.java
@@ -17,7 +17,6 @@
package com.android.launcher3.tapl;
import androidx.annotation.NonNull;
-import androidx.test.uiautomator.By;
import androidx.test.uiautomator.UiObject2;
/**
@@ -111,12 +110,4 @@
}
}
}
-
- /** Asserts that an item matching the given string is present in the overview actions. */
- public void assertHasAction(String text) {
- try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
- "want to check if the action [" + text + "] is present")) {
- mLauncher.waitForObjectInContainer(mOverviewActions, By.text(text));
- }
- }
}