[automerger skipped] Simplify OverviewActionsView layout to avoid unexpected misalignemnt am: 30d9b190d5 -s ours
am skip reason: Merged-In Ice087dde7d3c36b4536d36ce82c382c31c124855 with SHA-1 2cf1a0c08d is already in history
Original change: https://googleplex-android-review.googlesource.com/c/platform/packages/apps/Launcher3/+/26685191
Change-Id: I83962fb620b0ed6dac799e12dac59c2c379dc7c0
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/Android.bp b/Android.bp
index 78db013..cdada0a 100644
--- a/Android.bp
+++ b/Android.bp
@@ -201,6 +201,8 @@
"Launcher3ResLib",
"launcher-testing-shared",
"animationlib",
+ "kotlinx_coroutines_android",
+ "kotlinx_coroutines",
"com_android_launcher3_flags_lib",
"com_android_wm_shell_flags_lib",
"android.appwidget.flags-aconfig-java",
diff --git a/aconfig/launcher.aconfig b/aconfig/launcher.aconfig
index 462d947..8274bd6 100644
--- a/aconfig/launcher.aconfig
+++ b/aconfig/launcher.aconfig
@@ -171,6 +171,13 @@
}
flag {
+ name: "force_monochrome_app_icons"
+ namespace: "launcher"
+ description: "Enable the ability to generate monochromatic icons, if it is not provided by the app"
+ bug: "270396209"
+}
+
+flag {
name: "enable_add_app_widget_via_config_activity_v2"
namespace: "launcher"
description: "When adding app widget through config activity, directly add it to workspace to reduce flicker"
@@ -188,21 +195,37 @@
}
flag {
- name: "grid_migration_fix"
+ name: "enable_grid_migration_fix"
namespace: "launcher"
description: "Keep items in place when migrating to a bigger grid"
bug: "325286145"
+ is_fixed_read_only: true
metadata {
purpose: PURPOSE_BUGFIX
}
}
flag {
- name: "narrow_grid_restore"
+ name: "enable_narrow_grid_restore"
namespace: "launcher"
description: "Using only the most recent workspace when restoring to avoid confusion."
+ is_fixed_read_only: true
bug: "325285743"
metadata {
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ name: "enable_scaling_reveal_home_animation"
+ namespace: "launcher"
+ description: "Enables the Home gesture animation"
+ bug: "308801666"
+}
+
+flag {
+ name: "enable_widget_tap_to_add"
+ namespace: "launcher"
+ description: "Enables an add button in the widget picker"
+ bug: "323886237"
+}
diff --git a/quickstep/Android.bp b/quickstep/Android.bp
index ec4f6fc..f14cebd 100644
--- a/quickstep/Android.bp
+++ b/quickstep/Android.bp
@@ -23,15 +23,23 @@
}
filegroup {
- name: "launcher3-quickstep-robolectric-src",
- path: "robolectric_tests",
- srcs: ["robolectric_tests/src/**/*.java"],
+ name: "launcher3-quickstep-robo-src",
+ path: "tests/multivalentTests",
+ srcs: [
+ "tests/multivalentTests/src/**/*.java",
+ "tests/multivalentTests/src/**/*.kt",
+ ],
}
filegroup {
name: "launcher3-quickstep-tests-src",
path: "tests",
- srcs: ["tests/src/**/*.java", "tests/src/**/*.kt"],
+ srcs: [
+ "tests/multivalentTests/src/**/*.java",
+ "tests/multivalentTests/src/**/*.kt",
+ "tests/src/**/*.java",
+ "tests/src/**/*.kt",
+ ],
}
filegroup {
@@ -44,5 +52,6 @@
"tests/src/com/android/quickstep/TaplOverviewIconTest.java",
"tests/src/com/android/quickstep/TaplTestsQuickstep.java",
"tests/src/com/android/quickstep/TaplTestsSplitscreen.java",
- ]
+ "tests/src/com/android/launcher3/testcomponent/ExcludeFromRecentsTestActivity.java"
+ ],
}
diff --git a/quickstep/res/layout-land/keyboard_quick_switch_taskview.xml b/quickstep/res/layout-land/keyboard_quick_switch_taskview.xml
index 69e1574..38df756 100644
--- a/quickstep/res/layout-land/keyboard_quick_switch_taskview.xml
+++ b/quickstep/res/layout-land/keyboard_quick_switch_taskview.xml
@@ -36,19 +36,19 @@
app:layout_constraintEnd_toEndOf="parent">
<include
- layout="@layout/keyboard_quick_switch_thumbnail"
- android:id="@+id/thumbnail1"
+ layout="@layout/keyboard_quick_switch_taskview_thumbnail"
+ android:id="@+id/thumbnail_1"
android:layout_width="0dp"
android:layout_height="match_parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintEnd_toStartOf="@id/thumbnail2"/>
+ app:layout_constraintEnd_toStartOf="@id/thumbnail_2"/>
<include
- layout="@layout/keyboard_quick_switch_thumbnail"
- android:id="@+id/thumbnail2"
+ layout="@layout/keyboard_quick_switch_taskview_thumbnail"
+ android:id="@+id/thumbnail_2"
android:layout_width="0dp"
android:layout_height="match_parent"
android:visibility="gone"
@@ -56,31 +56,33 @@
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintStart_toEndOf="@id/thumbnail1"
+ app:layout_constraintStart_toEndOf="@id/thumbnail_1"
app:layout_constraintEnd_toEndOf="parent"/>
<ImageView
- android:id="@+id/icon1"
+ android:id="@+id/icon_1"
android:layout_width="@dimen/keyboard_quick_switch_taskview_icon_size"
android:layout_height="@dimen/keyboard_quick_switch_taskview_icon_size"
- android:layout_marginTop="@dimen/keyboard_quick_switch_taskview_icon_margin"
- android:layout_marginStart="@dimen/keyboard_quick_switch_taskview_icon_margin"
android:importantForAccessibility="no"
+ android:scaleType="centerCrop"
- app:layout_constraintTop_toTopOf="@id/thumbnail1"
- app:layout_constraintStart_toStartOf="@id/thumbnail1"/>
+ app:layout_constraintTop_toTopOf="@id/thumbnail_1"
+ app:layout_constraintBottom_toBottomOf="@id/thumbnail_1"
+ app:layout_constraintStart_toStartOf="@id/thumbnail_1"
+ app:layout_constraintEnd_toEndOf="@id/thumbnail_1"/>
<ImageView
- android:id="@+id/icon2"
+ android:id="@+id/icon_2"
android:layout_width="@dimen/keyboard_quick_switch_taskview_icon_size"
android:layout_height="@dimen/keyboard_quick_switch_taskview_icon_size"
- android:layout_marginTop="@dimen/keyboard_quick_switch_taskview_icon_margin"
- android:layout_marginStart="@dimen/keyboard_quick_switch_taskview_icon_margin"
android:importantForAccessibility="no"
android:visibility="gone"
+ android:scaleType="centerCrop"
- app:layout_constraintTop_toTopOf="@id/thumbnail2"
- app:layout_constraintStart_toStartOf="@id/thumbnail2"/>
+ app:layout_constraintTop_toTopOf="@id/thumbnail_2"
+ app:layout_constraintBottom_toBottomOf="@id/thumbnail_2"
+ app:layout_constraintStart_toStartOf="@id/thumbnail_2"
+ app:layout_constraintEnd_toEndOf="@id/thumbnail_2"/>
</androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/quickstep/res/layout/keyboard_quick_switch_taskview.xml b/quickstep/res/layout/keyboard_quick_switch_taskview.xml
index 6ed3c6e..c0ace9a 100644
--- a/quickstep/res/layout/keyboard_quick_switch_taskview.xml
+++ b/quickstep/res/layout/keyboard_quick_switch_taskview.xml
@@ -36,51 +36,53 @@
app:layout_constraintEnd_toEndOf="parent">
<include
- layout="@layout/keyboard_quick_switch_thumbnail"
- android:id="@+id/thumbnail1"
+ layout="@layout/keyboard_quick_switch_taskview_thumbnail"
+ android:id="@+id/thumbnail_1"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintTop_toTopOf="parent"
- app:layout_constraintBottom_toTopOf="@id/thumbnail2"
+ app:layout_constraintBottom_toTopOf="@id/thumbnail_2"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"/>
<include
- layout="@layout/keyboard_quick_switch_thumbnail"
- android:id="@+id/thumbnail2"
+ layout="@layout/keyboard_quick_switch_taskview_thumbnail"
+ android:id="@+id/thumbnail_2"
android:layout_width="match_parent"
android:layout_height="0dp"
android:visibility="gone"
android:layout_marginTop="@dimen/keyboard_quick_switch_split_view_spacing"
- app:layout_constraintTop_toBottomOf="@id/thumbnail1"
+ app:layout_constraintTop_toBottomOf="@id/thumbnail_1"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"/>
<ImageView
- android:id="@+id/icon1"
+ android:id="@+id/icon_1"
android:layout_width="@dimen/keyboard_quick_switch_taskview_icon_size"
android:layout_height="@dimen/keyboard_quick_switch_taskview_icon_size"
- android:layout_marginTop="@dimen/keyboard_quick_switch_taskview_icon_margin"
- android:layout_marginStart="@dimen/keyboard_quick_switch_taskview_icon_margin"
android:importantForAccessibility="no"
+ android:scaleType="centerCrop"
- app:layout_constraintTop_toTopOf="@id/thumbnail1"
- app:layout_constraintStart_toStartOf="@id/thumbnail1"/>
+ app:layout_constraintTop_toTopOf="@id/thumbnail_1"
+ app:layout_constraintBottom_toBottomOf="@id/thumbnail_1"
+ app:layout_constraintStart_toStartOf="@id/thumbnail_1"
+ app:layout_constraintEnd_toEndOf="@id/thumbnail_1"/>
<ImageView
- android:id="@+id/icon2"
+ android:id="@+id/icon_2"
android:layout_width="@dimen/keyboard_quick_switch_taskview_icon_size"
android:layout_height="@dimen/keyboard_quick_switch_taskview_icon_size"
- android:layout_marginTop="@dimen/keyboard_quick_switch_taskview_icon_margin"
- android:layout_marginStart="@dimen/keyboard_quick_switch_taskview_icon_margin"
android:importantForAccessibility="no"
android:visibility="gone"
+ android:scaleType="centerCrop"
- app:layout_constraintTop_toTopOf="@id/thumbnail2"
- app:layout_constraintStart_toStartOf="@id/thumbnail2"/>
+ app:layout_constraintTop_toTopOf="@id/thumbnail_2"
+ app:layout_constraintBottom_toBottomOf="@id/thumbnail_2"
+ app:layout_constraintStart_toStartOf="@id/thumbnail_2"
+ app:layout_constraintEnd_toEndOf="@id/thumbnail_2"/>
</androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/quickstep/res/layout/keyboard_quick_switch_thumbnail.xml b/quickstep/res/layout/keyboard_quick_switch_taskview_thumbnail.xml
similarity index 100%
rename from quickstep/res/layout/keyboard_quick_switch_thumbnail.xml
rename to quickstep/res/layout/keyboard_quick_switch_taskview_thumbnail.xml
diff --git a/quickstep/res/layout/transient_taskbar.xml b/quickstep/res/layout/transient_taskbar.xml
index 6af7cf4..3c6878a 100644
--- a/quickstep/res/layout/transient_taskbar.xml
+++ b/quickstep/res/layout/transient_taskbar.xml
@@ -41,9 +41,10 @@
<com.android.launcher3.taskbar.bubbles.BubbleBarView
android:id="@+id/taskbar_bubbles"
android:layout_width="wrap_content"
- android:layout_height="@dimen/bubblebar_size"
+ android:layout_height="@dimen/bubblebar_size_with_pointer"
android:layout_gravity="bottom|end"
- android:layout_marginEnd="@dimen/transient_taskbar_bottom_margin"
+ android:layout_marginHorizontal="@dimen/transient_taskbar_bottom_margin"
+ android:paddingTop="@dimen/bubblebar_pointer_size"
android:paddingEnd="@dimen/taskbar_icon_spacing"
android:paddingStart="@dimen/taskbar_icon_spacing"
android:visibility="gone"
diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml
index 3331321..93ef735 100644
--- a/quickstep/res/values/dimens.xml
+++ b/quickstep/res/values/dimens.xml
@@ -326,6 +326,7 @@
<dimen name="taskbar_nav_buttons_size">44dp</dimen>
<dimen name="taskbar_split_instructions_margin">48dp</dimen>
<dimen name="taskbar_contextual_button_margin">120dp</dimen>
+ <dimen name="taskbar_ime_switcher_button_margin_start">40dp</dimen>
<dimen name="taskbar_suw_insets">48dp</dimen>
<dimen name="taskbar_suw_frame">48dp</dimen>
<dimen name="taskbar_hotseat_nav_spacing">24dp</dimen>
@@ -413,6 +414,8 @@
<dimen name="bubblebar_stashed_size">@dimen/transient_taskbar_stashed_height</dimen>
<dimen name="bubblebar_stashed_handle_height">@dimen/taskbar_stashed_handle_height</dimen>
<dimen name="bubblebar_pointer_size">8dp</dimen>
+ <!-- Container size with pointer included: bubblebar_size + bubblebar_pointer_size -->
+ <dimen name="bubblebar_size_with_pointer">80dp</dimen>
<dimen name="bubblebar_elevation">1dp</dimen>
<dimen name="bubblebar_hotseat_adjustment_threshold">90dp</dimen>
@@ -439,8 +442,7 @@
<dimen name="keyboard_quick_switch_border_width">4dp</dimen>
<dimen name="keyboard_quick_switch_taskview_width">104dp</dimen>
<dimen name="keyboard_quick_switch_taskview_height">134dp</dimen>
- <dimen name="keyboard_quick_switch_taskview_icon_size">28dp</dimen>
- <dimen name="keyboard_quick_switch_taskview_icon_margin">4dp</dimen>
+ <dimen name="keyboard_quick_switch_taskview_icon_size">52dp</dimen>
<dimen name="keyboard_quick_switch_recents_icon_size">20dp</dimen>
<dimen name="keyboard_quick_switch_margin_top">56dp</dimen>
<dimen name="keyboard_quick_switch_margin_ends">16dp</dimen>
diff --git a/quickstep/res/values/styles.xml b/quickstep/res/values/styles.xml
index 350c752..16fb6d2 100644
--- a/quickstep/res/values/styles.xml
+++ b/quickstep/res/values/styles.xml
@@ -315,5 +315,6 @@
<style name="WidgetPickerActivityTheme" parent="@android:style/Theme.Translucent.NoTitleBar">
<item name="widgetsTheme">@style/WidgetContainerTheme</item>
<item name="android:windowBackground">@android:color/transparent</item>
+ <item name="pageIndicatorDotColor">@color/page_indicator_dot_color_light</item>
</style>
</resources>
diff --git a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
index 569e95a..be532b4 100644
--- a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
+++ b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
@@ -43,6 +43,7 @@
import static com.android.launcher3.BaseActivity.INVISIBLE_BY_APP_TRANSITIONS;
import static com.android.launcher3.BaseActivity.INVISIBLE_BY_PENDING_FLAGS;
import static com.android.launcher3.BaseActivity.PENDING_INVISIBLE_BY_WALLPAPER_ANIMATION;
+import static com.android.launcher3.Flags.enableScalingRevealHomeAnimation;
import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
import static com.android.launcher3.LauncherAnimUtils.VIEW_BACKGROUND_COLOR;
import static com.android.launcher3.LauncherState.ALL_APPS;
@@ -215,7 +216,8 @@
public static final int TASKBAR_TO_APP_DURATION = 600;
// TODO(b/236145847): Tune TASKBAR_TO_HOME_DURATION to 383 after conflict with unlock animation
// is solved.
- public static final int TASKBAR_TO_HOME_DURATION = 300;
+ private static final int TASKBAR_TO_HOME_DURATION_FAST = 300;
+ private static final int TASKBAR_TO_HOME_DURATION_SLOW = 1000;
protected static final int CONTENT_SCALE_DURATION = 350;
protected static final int CONTENT_SCRIM_DURATION = 350;
@@ -1704,6 +1706,14 @@
return new Pair(rectFSpringAnim, anim);
}
+ public static int getTaskbarToHomeDuration() {
+ if (enableScalingRevealHomeAnimation()) {
+ return TASKBAR_TO_HOME_DURATION_SLOW;
+ } else {
+ return TASKBAR_TO_HOME_DURATION_FAST;
+ }
+ }
+
/**
* Remote animation runner for animation from the app to Launcher, including recents.
*/
diff --git a/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java b/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java
index e1443d0..b36fd66 100644
--- a/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java
+++ b/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java
@@ -138,9 +138,9 @@
int totalHeight = iconHeight + iconPadding + textHeight + mVerticalPadding * 2;
// Prediction row height will be 4dp bigger than the regular apps in A-Z list when two line
// is not enabled. Otherwise, the extra height will increase by just the textHeight.
- int extraHeight = (FeatureFlags.enableTwolineAllapps() && (!Flags.enableTwolineToggle()
- || (Flags.enableTwolineToggle() && LauncherPrefs.ENABLE_TWOLINE_ALLAPPS_TOGGLE.get(
- getContext())))) ? textHeight : mTopRowExtraHeight;
+ int extraHeight = (Flags.enableTwolineToggle() &&
+ LauncherPrefs.ENABLE_TWOLINE_ALLAPPS_TOGGLE.get(getContext()))
+ ? textHeight : mTopRowExtraHeight;
totalHeight += extraHeight;
return getVisibility() == GONE ? 0 : totalHeight + getPaddingTop() + getPaddingBottom();
}
diff --git a/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.java b/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.java
index 4741ddd..f9a8c99 100644
--- a/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.java
+++ b/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.java
@@ -19,7 +19,7 @@
import static com.android.launcher3.LauncherState.BACKGROUND_APP;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
-import static com.android.quickstep.views.DesktopTaskView.isDesktopModeSupported;
+import static com.android.window.flags.Flags.enableDesktopWindowingMode;
import android.os.Debug;
import android.os.SystemProperties;
@@ -136,7 +136,7 @@
Log.d(TAG, "setVisibleFreeformTasksCount: visibleTasksCount=" + visibleTasksCount
+ " currentValue=" + mVisibleFreeformTasksCount);
}
- if (!isDesktopModeSupported()) {
+ if (!enableDesktopWindowingMode()) {
return;
}
@@ -180,7 +180,7 @@
Log.d(TAG, "setOverviewStateEnabled: enabled=" + overviewStateEnabled
+ " currentValue=" + mInOverviewState);
}
- if (!isDesktopModeSupported()) {
+ if (!enableDesktopWindowingMode()) {
return;
}
if (overviewStateEnabled != mInOverviewState) {
@@ -202,7 +202,7 @@
Log.d(TAG, "setBackgroundStateEnabled: enabled=" + backgroundStateEnabled
+ " currentValue=" + mBackgroundStateEnabled);
}
- if (!isDesktopModeSupported()) {
+ if (!enableDesktopWindowingMode()) {
return;
}
if (backgroundStateEnabled != mBackgroundStateEnabled) {
@@ -229,7 +229,7 @@
* Notify controller that recents gesture has started.
*/
public void setRecentsGestureStart() {
- if (!isDesktopModeSupported()) {
+ if (!enableDesktopWindowingMode()) {
return;
}
if (DEBUG) {
@@ -243,7 +243,7 @@
* {@link com.android.quickstep.GestureState.GestureEndTarget}
*/
public void setRecentsGestureEnd(@Nullable GestureState.GestureEndTarget endTarget) {
- if (!isDesktopModeSupported()) {
+ if (!enableDesktopWindowingMode()) {
return;
}
if (DEBUG) {
diff --git a/quickstep/src/com/android/launcher3/taskbar/BlurredBitmapDrawable.kt b/quickstep/src/com/android/launcher3/taskbar/BlurredBitmapDrawable.kt
new file mode 100644
index 0000000..8aee1aa
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/BlurredBitmapDrawable.kt
@@ -0,0 +1,59 @@
+/*
+ * 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.taskbar
+
+import android.graphics.Bitmap
+import android.graphics.Canvas
+import android.graphics.PixelFormat
+import android.graphics.RenderEffect
+import android.graphics.RenderNode
+import android.graphics.Shader
+import android.graphics.drawable.BitmapDrawable
+import android.graphics.drawable.DrawableWrapper
+
+/* BitmapDrawable that can blur the given bitmap. */
+class BlurredBitmapDrawable(bitmap: Bitmap?, radiusX: Float, radiusY: Float) :
+ DrawableWrapper(BitmapDrawable(bitmap)) {
+ private val mBlurRenderNode: RenderNode = RenderNode("BlurredConstraintLayoutBlurNode")
+
+ constructor(bitmap: Bitmap?, radius: Float) : this(bitmap, radius, radius)
+
+ init {
+ mBlurRenderNode.setRenderEffect(
+ RenderEffect.createBlurEffect(radiusX, radiusY, Shader.TileMode.CLAMP)
+ )
+ }
+
+ override fun draw(canvas: Canvas) {
+ if (!canvas.isHardwareAccelerated) {
+ super.draw(canvas)
+ return
+ }
+ mBlurRenderNode.setPosition(bounds)
+ if (!mBlurRenderNode.hasDisplayList()) {
+ // Record render node if its display list is not recorded or discarded
+ // (which happens when it's no longer drawn by anything).
+ val recordingCanvas = mBlurRenderNode.beginRecording()
+ super.draw(recordingCanvas)
+ mBlurRenderNode.endRecording()
+ }
+ canvas.drawRenderNode(mBlurRenderNode)
+ }
+
+ override fun getOpacity(): Int {
+ return PixelFormat.OPAQUE
+ }
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchController.java b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchController.java
index f15d12b..0bcf2d1 100644
--- a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchController.java
@@ -15,7 +15,7 @@
*/
package com.android.launcher3.taskbar;
-import static com.android.quickstep.views.DesktopTaskView.isDesktopModeSupported;
+import static com.android.window.flags.Flags.enableDesktopWindowingMode;
import android.content.ComponentName;
import android.content.pm.ActivityInfo;
@@ -113,7 +113,7 @@
DesktopVisibilityController desktopController =
LauncherActivityInterface.INSTANCE.getDesktopVisibilityController();
final boolean onDesktop =
- isDesktopModeSupported()
+ enableDesktopWindowingMode()
&& desktopController != null
&& desktopController.areFreeformTasksVisible();
@@ -154,7 +154,7 @@
// Hide all desktop tasks and show them on the hidden tile
int hiddenDesktopTasks = 0;
- if (isDesktopModeSupported()) {
+ if (enableDesktopWindowingMode()) {
DesktopTask desktopTask = findDesktopTask(tasks);
if (desktopTask != null) {
hiddenDesktopTasks = desktopTask.tasks.size();
@@ -193,10 +193,14 @@
}
void closeQuickSwitchView() {
+ closeQuickSwitchView(true);
+ }
+
+ void closeQuickSwitchView(boolean animate) {
if (mQuickSwitchViewController == null) {
return;
}
- mQuickSwitchViewController.closeQuickSwitchView(true);
+ mQuickSwitchViewController.closeQuickSwitchView(animate);
}
/**
diff --git a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchTaskView.java b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchTaskView.java
index a9d50b9..5b407f0 100644
--- a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchTaskView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchTaskView.java
@@ -23,6 +23,7 @@
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
+import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.View;
import android.widget.ImageView;
@@ -47,6 +48,8 @@
*/
public class KeyboardQuickSwitchTaskView extends ConstraintLayout {
+ private static final float THUMBNAIL_BLUR_RADIUS = 1f;
+
@ColorInt private final int mBorderColor;
@Nullable private BorderAnimator mBorderAnimator;
@@ -89,10 +92,10 @@
@Override
protected void onFinishInflate() {
super.onFinishInflate();
- mThumbnailView1 = findViewById(R.id.thumbnail1);
- mThumbnailView2 = findViewById(R.id.thumbnail2);
- mIcon1 = findViewById(R.id.icon1);
- mIcon2 = findViewById(R.id.icon2);
+ mThumbnailView1 = findViewById(R.id.thumbnail_1);
+ mThumbnailView2 = findViewById(R.id.thumbnail_2);
+ mIcon1 = findViewById(R.id.icon_1);
+ mIcon2 = findViewById(R.id.icon_2);
mContent = findViewById(R.id.content);
Resources resources = mContext.getResources();
@@ -167,10 +170,7 @@
@Nullable ImageView thumbnailView,
@Nullable Task task,
@Nullable ThumbnailUpdateFunction updateFunction) {
- if (thumbnailView == null) {
- return;
- }
- if (task == null) {
+ if (thumbnailView == null || task == null) {
return;
}
if (updateFunction == null) {
@@ -182,19 +182,30 @@
}
private void applyThumbnail(
- @NonNull ImageView thumbnailView, ThumbnailData thumbnailData) {
+ @NonNull ImageView thumbnailView,
+ ThumbnailData thumbnailData) {
Bitmap bm = thumbnailData == null ? null : thumbnailData.thumbnail;
- thumbnailView.setVisibility(VISIBLE);
- thumbnailView.setImageBitmap(bm);
+ if (thumbnailView.getVisibility() != VISIBLE) {
+ thumbnailView.setVisibility(VISIBLE);
+ }
+ thumbnailView.setImageDrawable(new BlurredBitmapDrawable(bm, THUMBNAIL_BLUR_RADIUS));
}
private void applyIcon(@Nullable ImageView iconView, @Nullable Task task) {
- if (iconView == null || task == null) {
+ if (iconView == null || task == null || task.icon == null) {
return;
}
- iconView.setVisibility(VISIBLE);
- iconView.setImageDrawable(task.icon);
+ Drawable.ConstantState constantState = task.icon.getConstantState();
+ if (constantState == null) {
+ return;
+ }
+ if (iconView.getVisibility() != VISIBLE) {
+ iconView.setVisibility(VISIBLE);
+ }
+ // Use the bitmap directly since the drawable's scale can change
+ iconView.setImageDrawable(
+ constantState.newDrawable(getResources(), getContext().getTheme()));
}
protected interface ThumbnailUpdateFunction {
diff --git a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchViewController.java b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchViewController.java
index c830aa8..847088d 100644
--- a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchViewController.java
@@ -151,7 +151,8 @@
context.getDeviceProfile().overviewPageSpacing,
QuickStepContract.getWindowCornerRadius(context),
AnimationUtils.loadInterpolator(
- context, android.R.interpolator.fast_out_extra_slow_in)));
+ context, android.R.interpolator.fast_out_extra_slow_in)),
+ "SlideInTransition");
if (mOnDesktop) {
UI_HELPER_EXECUTOR.execute(() ->
SystemUiProxy.INSTANCE.get(mKeyboardQuickSwitchView.getContext())
@@ -238,8 +239,7 @@
}
void launchTaskAt(int index) {
- mCurrentFocusIndex = Utilities.boundToRange(
- index, 0, mKeyboardQuickSwitchView.getChildCount() - 1);
+ mCurrentFocusIndex = index;
mControllers.taskbarActivityContext.launchKeyboardFocusedTask();
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
index 1e861d2..a59aead 100644
--- a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
@@ -21,7 +21,7 @@
import static com.android.launcher3.taskbar.TaskbarEduTooltipControllerKt.TOOLTIP_STEP_FEATURES;
import static com.android.launcher3.taskbar.TaskbarLauncherStateController.FLAG_VISIBLE;
import static com.android.quickstep.TaskAnimationManager.ENABLE_SHELL_TRANSITIONS;
-import static com.android.quickstep.views.DesktopTaskView.isDesktopModeSupported;
+import static com.android.window.flags.Flags.enableDesktopWindowingMode;
import android.animation.Animator;
import android.animation.AnimatorSet;
@@ -191,7 +191,7 @@
? TRANSIENT_TASKBAR_TRANSITION_DURATION
: (!isVisible
? QuickstepTransitionManager.TASKBAR_TO_APP_DURATION
- : QuickstepTransitionManager.TASKBAR_TO_HOME_DURATION));
+ : QuickstepTransitionManager.getTaskbarToHomeDuration()));
}
@Nullable
@@ -209,7 +209,7 @@
DesktopVisibilityController desktopController =
LauncherActivityInterface.INSTANCE.getDesktopVisibilityController();
final boolean onDesktop =
- isDesktopModeSupported()
+ enableDesktopWindowingMode()
&& desktopController != null
&& desktopController.areFreeformTasksVisible();
if (onDesktop) {
@@ -414,6 +414,13 @@
}
@Override
+ protected boolean canToggleHomeAllApps() {
+ return mLauncher.isResumed()
+ && !mTaskbarLauncherStateController.isInOverview()
+ && !mLauncher.areFreeformTasksVisible();
+ }
+
+ @Override
public RecentsView getRecentsView() {
return mLauncher.getOverviewPanel();
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
index c543307..aedbe6c 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
@@ -17,6 +17,7 @@
import static android.content.pm.PackageManager.FEATURE_PC;
import static android.os.Trace.TRACE_TAG_APP;
+import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
@@ -1091,7 +1092,6 @@
} else if (tag instanceof FolderInfo fi && fi.itemType == Favorites.ITEM_TYPE_APP_PAIR) {
// Tapping an app pair icon on Taskbar
if (recents != null && recents.isSplitSelectionActive()) {
- // TODO (b/274835596): Implement "can't split with this" bounce animation
Toast.makeText(this, "Unable to split with an app pair. Select another app.",
Toast.LENGTH_SHORT).show();
} else {
@@ -1215,7 +1215,7 @@
.handleAppPairLaunchInApp((AppPairIcon) launchingIconView, itemInfos);
} else {
// Tapped a single app, nothing complicated here.
- startItemInfoActivity(itemInfos.get(0));
+ startItemInfoActivity(itemInfos.get(0), null /*foundTask*/);
}
}
@@ -1254,21 +1254,39 @@
if (findExactPairMatch) {
// We did not find the app pair we were looking for, so launch one.
recents.getSplitSelectController().getAppPairsController().launchAppPair(
- (AppPairIcon) launchingIconView);
+ (AppPairIcon) launchingIconView, -1 /*cuj*/);
} else {
- startItemInfoActivity(itemInfos.get(0));
+ startItemInfoActivity(itemInfos.get(0), foundTask);
}
}
);
}
- private void startItemInfoActivity(ItemInfo info) {
+ /**
+ * Starts an activity with the information provided by the "info" param. However, if
+ * taskInRecents is present, it will prioritize re-launching an existing instance via
+ * {@link ActivityManagerWrapper#startActivityFromRecents(int, ActivityOptions)}
+ */
+ private void startItemInfoActivity(ItemInfo info, @Nullable Task taskInRecents) {
Intent intent = new Intent(info.getIntent())
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
try {
TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "start: taskbarAppIcon");
if (info.user.equals(Process.myUserHandle())) {
// TODO(b/216683257): Use startActivityForResult for search results that require it.
+ if (taskInRecents != null) {
+ // Re launch instance from recents
+ ActivityOptionsWrapper opts = getActivityLaunchOptions(null, info);
+ opts.options.setLaunchDisplayId(
+ getDisplay() == null ? DEFAULT_DISPLAY : getDisplay().getDisplayId());
+ if (ActivityManagerWrapper.getInstance()
+ .startActivityFromRecents(taskInRecents.key, opts.options)) {
+ mControllers.uiController.getRecentsView()
+ .addSideTaskLaunchCallback(opts.onEndCallback);
+ return;
+ }
+ }
+
startActivity(intent);
} else {
getSystemService(LauncherApps.class).startMainActivity(
@@ -1327,8 +1345,12 @@
* Called when we want to unstash taskbar when user performs swipes up gesture.
*/
public void onSwipeToUnstashTaskbar() {
- VibratorWrapper.INSTANCE.get(this).vibrateForTaskbarUnstash();
+ boolean wasStashed = mControllers.taskbarStashController.isStashed();
mControllers.taskbarStashController.updateAndAnimateTransientTaskbar(/* stash= */ false);
+ boolean isStashed = mControllers.taskbarStashController.isStashed();
+ if (isStashed != wasStashed) {
+ VibratorWrapper.INSTANCE.get(this).vibrateForTaskbarUnstash();
+ }
mControllers.taskbarEduTooltipController.hide();
}
@@ -1558,4 +1580,13 @@
public float getStashedTaskbarScale() {
return mControllers.stashedHandleViewController.getStashedHandleHintScale().value;
}
+
+ /** Closes the KeyboardQuickSwitchView without an animation if open. */
+ public void closeKeyboardQuickSwitchView() {
+ mControllers.keyboardQuickSwitchController.closeQuickSwitchView(false);
+ }
+
+ boolean canToggleHomeAllApps() {
+ return mControllers.uiController.canToggleHomeAllApps();
+ }
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
index 259af1d..a14e3fd 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
@@ -193,7 +193,7 @@
updateStateForFlag(FLAG_LAUNCHER_IN_STATE_TRANSITION, true);
if (!mShouldDelayLauncherStateAnim) {
if (toState == LauncherState.NORMAL) {
- applyState(QuickstepTransitionManager.TASKBAR_TO_HOME_DURATION);
+ applyState(QuickstepTransitionManager.getTaskbarToHomeDuration());
} else {
applyState();
}
@@ -205,7 +205,13 @@
mLauncherState = finalState;
updateStateForFlag(FLAG_LAUNCHER_IN_STATE_TRANSITION, false);
applyState();
- updateOverviewDragState(finalState);
+ boolean disallowLongClick =
+ FeatureFlags.enableSplitContextually()
+ ? mLauncher.isSplitSelectionActive()
+ : finalState == LauncherState.OVERVIEW_SPLIT_SELECT;
+ com.android.launcher3.taskbar.Utilities.setOverviewDragState(
+ mControllers, finalState.disallowTaskbarGlobalDrag(),
+ disallowLongClick, finalState.allowTaskbarInitialSplitSelection());
}
};
@@ -250,7 +256,6 @@
mCanSyncViews = true;
mLauncher.addOnDeviceProfileChangeListener(mOnDeviceProfileChangeListener);
- updateOverviewDragState(mLauncherState);
}
public void onDestroy() {
@@ -323,7 +328,7 @@
updateStateForSysuiFlags(systemUiStateFlags, /* applyState */ true);
}
- private void updateStateForSysuiFlags(int systemUiStateFlags, boolean applyState) {
+ private void updateStateForSysuiFlags(int systemUiStateFlags, boolean applyState) {
final boolean prevIsAwake = hasAnyFlag(FLAG_AWAKE);
final boolean currIsAwake = hasAnyFlag(systemUiStateFlags, SYSUI_STATE_AWAKE);
@@ -353,21 +358,6 @@
}
/**
- * Updates overview drag state on various controllers based on {@link #mLauncherState}.
- *
- * @param launcherState The current state launcher is in
- */
- private void updateOverviewDragState(LauncherState launcherState) {
- boolean disallowLongClick =
- FeatureFlags.enableSplitContextually()
- ? mLauncher.isSplitSelectionActive()
- : launcherState == LauncherState.OVERVIEW_SPLIT_SELECT;
- com.android.launcher3.taskbar.Utilities.setOverviewDragState(
- mControllers, launcherState.disallowTaskbarGlobalDrag(),
- disallowLongClick, launcherState.allowTaskbarInitialSplitSelection());
- }
-
- /**
* Updates the proper flag to change the state of the task bar.
*
* Note that this only updates the flag. {@link #applyState()} needs to be called separately.
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
index ff33ca9..ecbc7e7 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
@@ -23,7 +23,6 @@
import static com.android.launcher3.BaseActivity.EVENT_DESTROYED;
import static com.android.launcher3.Flags.enableUnfoldStateAnimation;
-import static com.android.launcher3.LauncherState.OVERVIEW;
import static com.android.launcher3.config.FeatureFlags.ENABLE_TASKBAR_NAVBAR_UNIFICATION;
import static com.android.launcher3.config.FeatureFlags.enableTaskbarNoRecreate;
import static com.android.launcher3.util.DisplayController.CHANGE_DENSITY;
@@ -69,6 +68,7 @@
import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.SettingsCache;
import com.android.launcher3.util.SimpleBroadcastReceiver;
+import com.android.quickstep.AllAppsActionManager;
import com.android.quickstep.RecentsActivity;
import com.android.quickstep.SystemUiProxy;
import com.android.quickstep.TouchInteractionService;
@@ -158,6 +158,8 @@
private final SimpleBroadcastReceiver mTaskbarBroadcastReceiver =
new SimpleBroadcastReceiver(this::showTaskbarFromBroadcast);
+ private final AllAppsActionManager mAllAppsActionManager;
+
private final Runnable mActivityOnDestroyCallback = new Runnable() {
@Override
public void run() {
@@ -212,12 +214,14 @@
private Boolean mFolded;
@SuppressLint("WrongConstant")
- public TaskbarManager(TouchInteractionService service) {
+ public TaskbarManager(
+ TouchInteractionService service, AllAppsActionManager allAppsActionManager) {
Display display =
service.getSystemService(DisplayManager.class).getDisplay(DEFAULT_DISPLAY);
mContext = service.createWindowContext(display,
ENABLE_TASKBAR_NAVBAR_UNIFICATION ? TYPE_NAVIGATION_BAR : TYPE_NAVIGATION_BAR_PANEL,
null);
+ mAllAppsActionManager = allAppsActionManager;
mNavigationBarPanelContext = ENABLE_TASKBAR_NAVBAR_UNIFICATION
? service.createWindowContext(display, TYPE_NAVIGATION_BAR_PANEL, null)
: null;
@@ -291,10 +295,10 @@
recreateTaskbar();
} else {
// Config change might be handled without re-creating the taskbar
- if (dp != null && !isTaskbarPresent(dp)) {
+ if (dp != null && !isTaskbarEnabled(dp)) {
destroyExistingTaskbar();
} else {
- if (dp != null && isTaskbarPresent(dp)) {
+ if (dp != null && isTaskbarEnabled(dp)) {
if (ENABLE_TASKBAR_NAVBAR_UNIFICATION) {
// Re-initialize for screen size change? Should this be done
// by looking at screen-size change flag in configDiff in the
@@ -349,7 +353,7 @@
}
DeviceProfile dp = mUserUnlocked ?
LauncherAppState.getIDP(mContext).getDeviceProfile(mContext) : null;
- if (dp == null || !isTaskbarPresent(dp)) {
+ if (dp == null || !isTaskbarEnabled(dp)) {
removeTaskbarRootViewFromWindow();
}
}
@@ -369,20 +373,11 @@
* @param homeAllAppsIntent Intent used if Taskbar is not enabled or Launcher is resumed.
*/
public void toggleAllApps(Intent homeAllAppsIntent) {
- if (mTaskbarActivityContext == null) {
+ if (mTaskbarActivityContext == null || mTaskbarActivityContext.canToggleHomeAllApps()) {
mContext.startActivity(homeAllAppsIntent);
- return;
+ } else {
+ mTaskbarActivityContext.toggleAllAppsSearch();
}
-
- if (mActivity != null
- && mActivity.isResumed()
- && !mActivity.isInState(OVERVIEW)
- && !(mActivity instanceof QuickstepLauncher l && l.areFreeformTasksVisible())) {
- mContext.startActivity(homeAllAppsIntent);
- return;
- }
-
- mTaskbarActivityContext.toggleAllAppsSearch();
}
/**
@@ -477,9 +472,12 @@
DeviceProfile dp = mUserUnlocked ?
LauncherAppState.getIDP(mContext).getDeviceProfile(mContext) : null;
+ // All Apps action is unrelated to navbar unification, so we only need to check DP.
+ mAllAppsActionManager.setTaskbarPresent(dp != null && dp.isTaskbarPresent);
+
destroyExistingTaskbar();
- boolean isTaskbarEnabled = dp != null && isTaskbarPresent(dp);
+ boolean isTaskbarEnabled = dp != null && isTaskbarEnabled(dp);
debugWhyTaskbarNotDestroyed("recreateTaskbar: isTaskbarEnabled=" + isTaskbarEnabled
+ " [dp != null (i.e. mUserUnlocked)]=" + (dp != null)
+ " FLAG_HIDE_NAVBAR_WINDOW=" + ENABLE_TASKBAR_NAVBAR_UNIFICATION
@@ -544,7 +542,7 @@
}
}
- private static boolean isTaskbarPresent(DeviceProfile deviceProfile) {
+ private static boolean isTaskbarEnabled(DeviceProfile deviceProfile) {
return ENABLE_TASKBAR_NAVBAR_UNIFICATION || deviceProfile.isTaskbarPresent;
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java
index 19293b5..e293ad4 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java
@@ -27,7 +27,7 @@
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_IME_SWITCHER_BUTTON_TAP;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_OVERVIEW_BUTTON_LONGPRESS;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_OVERVIEW_BUTTON_TAP;
-import static com.android.quickstep.views.DesktopTaskView.isDesktopModeSupported;
+import static com.android.window.flags.Flags.enableDesktopWindowingMode;
import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_HOME_KEY;
import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_PINNING;
@@ -281,7 +281,7 @@
private void navigateHome() {
TaskUtils.closeSystemWindowsAsync(CLOSE_SYSTEM_WINDOWS_REASON_HOME_KEY);
- if (isDesktopModeSupported()) {
+ if (enableDesktopWindowingMode()) {
DesktopVisibilityController desktopVisibilityController =
LauncherActivityInterface.INSTANCE.getDesktopVisibilityController();
if (desktopVisibilityController != null) {
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
index efe1e39..109400e 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
@@ -197,6 +197,11 @@
return false;
}
+ /** Returns {@code true} if Home All Apps available instead of Taskbar All Apps. */
+ protected boolean canToggleHomeAllApps() {
+ return false;
+ }
+
@CallSuper
protected void dumpLogs(String prefix, PrintWriter pw) {
pw.println(String.format(
diff --git a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsSlideInView.java b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsSlideInView.java
index 5424fcf..e0e78f9 100644
--- a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsSlideInView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsSlideInView.java
@@ -35,6 +35,7 @@
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Insettable;
import com.android.launcher3.R;
+import com.android.launcher3.allapps.AllAppsRecyclerView;
import com.android.launcher3.anim.AnimatorListeners;
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.config.FeatureFlags;
@@ -196,7 +197,13 @@
@Override
protected void dispatchDraw(Canvas canvas) {
- mAppsView.drawOnScrimWithScale(canvas, mSlideInViewScale.value);
+ // We should call drawOnScrimWithBottomOffset() rather than drawOnScrimWithScale(). Because
+ // for taskbar all apps, the scrim view is a child view of AbstractSlideInView. Thus scaling
+ // down in AbstractSlideInView#onScaleProgressChanged() with SCALE_PROPERTY has already
+ // done the job - there is no need to re-apply scale effect here. But it also means we need
+ // to pass extra bottom offset to background scrim to fill the bottom gap during predictive
+ // back swipe.
+ mAppsView.drawOnScrimWithBottomOffset(canvas, getBottomOffsetPx());
super.dispatchDraw(canvas);
}
@@ -205,6 +212,11 @@
super.onScaleProgressChanged();
mAppsView.setClipChildren(!mIsBackProgressing);
mAppsView.getAppsRecyclerViewContainer().setClipChildren(!mIsBackProgressing);
+ AllAppsRecyclerView rv = mAppsView.getActiveRecyclerView();
+ if (rv != null && rv.getScrollbar() != null) {
+ rv.getScrollbar().setVisibility(
+ mIsBackProgressing ? INVISIBLE : VISIBLE);
+ }
}
@Override
@@ -255,7 +267,11 @@
@Override
public void onBackInvoked() {
- if (!mAllAppsCallbacks.handleSearchBackInvoked()) {
+ if (mAllAppsCallbacks.handleSearchBackInvoked()) {
+ // We need to scale back taskbar all apps if we navigate back within search inside all
+ // apps
+ animateSlideInViewToNoScale();
+ } else {
super.onBackInvoked();
}
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarBackground.kt b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarBackground.kt
index aa2b29d..79fdeda 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarBackground.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarBackground.kt
@@ -19,6 +19,7 @@
import android.graphics.Color
import android.graphics.ColorFilter
import android.graphics.Paint
+import android.graphics.PixelFormat
import android.graphics.drawable.Drawable
import android.graphics.drawable.ShapeDrawable
import com.android.app.animation.Interpolators
@@ -122,14 +123,24 @@
// Draw background.
val radius = backgroundHeight / 2f
- val left = if (anchorLeft) 0f else canvas.width.toFloat() - width
- val right = if (anchorLeft) width else canvas.width.toFloat()
- canvas.drawRoundRect(left, 0f, right, canvas.height.toFloat(), radius, radius, paint)
+ val left = if (anchorLeft) 0f else bounds.width().toFloat() - width
+ val right = if (anchorLeft) width else bounds.width().toFloat()
+ canvas.drawRoundRect(
+ left,
+ pointerSize,
+ right,
+ bounds.height().toFloat(),
+ radius,
+ radius,
+ paint
+ )
if (showingArrow) {
// Draw arrow.
val transX = arrowPositionX - pointerSize / 2f
- canvas.translate(transX, -pointerSize)
+ // Shift arrow down by 1 pixel. Rounded rect has a 1 pixel border which will show up
+ // between background and arrow otherwise.
+ canvas.translate(transX, 1f)
arrowDrawable.draw(canvas)
}
@@ -137,11 +148,20 @@
}
override fun getOpacity(): Int {
- return paint.alpha
+ return when (paint.alpha) {
+ 255 -> PixelFormat.OPAQUE
+ 0 -> PixelFormat.TRANSPARENT
+ else -> PixelFormat.TRANSLUCENT
+ }
}
override fun setAlpha(alpha: Int) {
paint.alpha = alpha
+ arrowDrawable.paint.alpha = alpha
+ }
+
+ override fun getAlpha(): Int {
+ return paint.alpha
}
override fun setColorFilter(colorFilter: ColorFilter?) {
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java
index 6dc7db7..1f3c483 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java
@@ -73,6 +73,7 @@
import com.android.quickstep.SystemUiProxy;
import com.android.wm.shell.Flags;
import com.android.wm.shell.bubbles.IBubblesListener;
+import com.android.wm.shell.common.bubbles.BubbleBarLocation;
import com.android.wm.shell.common.bubbles.BubbleBarUpdate;
import com.android.wm.shell.common.bubbles.BubbleInfo;
import com.android.wm.shell.common.bubbles.RemovedBubble;
@@ -155,12 +156,14 @@
* {@link BubbleBarBubble}s so that it can be used to update the views.
*/
private static class BubbleBarViewUpdate {
+ final boolean initialState;
boolean expandedChanged;
boolean expanded;
boolean shouldShowEducation;
String selectedBubbleKey;
String suppressedBubbleKey;
String unsuppressedBubbleKey;
+ BubbleBarLocation bubbleBarLocation;
List<RemovedBubble> removedBubbles;
List<String> bubbleKeysInOrder;
@@ -170,12 +173,14 @@
List<BubbleBarBubble> currentBubbles;
BubbleBarViewUpdate(BubbleBarUpdate update) {
+ initialState = update.initialState;
expandedChanged = update.expandedChanged;
expanded = update.expanded;
shouldShowEducation = update.shouldShowEducation;
selectedBubbleKey = update.selectedBubbleKey;
suppressedBubbleKey = update.suppressedBubbleKey;
unsuppressedBubbleKey = update.unsupressedBubbleKey;
+ bubbleBarLocation = update.bubbleBarLocation;
removedBubbles = update.removedBubbles;
bubbleKeysInOrder = update.bubbleKeysInOrder;
}
@@ -400,6 +405,14 @@
Log.w(TAG, "expansion was changed but is the same");
}
}
+ if (update.bubbleBarLocation != null) {
+ if (update.bubbleBarLocation != mBubbleBarViewController.getBubbleBarLocation()) {
+ // Animate when receiving updates. Skip it if we received the initial state.
+ boolean animate = !update.initialState;
+ mBubbleBarViewController.setBubbleBarLocation(update.bubbleBarLocation, animate);
+ mBubbleStashController.setBubbleBarLocation(update.bubbleBarLocation);
+ }
+ }
}
/** Tells WMShell to show the currently selected bubble. */
@@ -593,7 +606,7 @@
Rect location = new Rect();
// currentBarBounds is only useful for distance from left or right edge.
// It contains the current bounds, calculate the expanded bounds.
- if (mBarView.isOnLeft()) {
+ if (mBarView.getBubbleBarLocation().isOnLeft(mBarView.isLayoutRtl())) {
location.left = currentBarBounds.left;
location.right = (int) (currentBarBounds.left + mBarView.expandedWidth());
} else {
@@ -601,7 +614,7 @@
location.right = currentBarBounds.right;
}
final int translation = (int) abs(mBubbleStashController.getBubbleBarTranslationY());
- location.top = displaySize.y - mBarView.getHeight() - translation;
+ location.top = displaySize.y - currentBarBounds.height() - translation;
location.bottom = displaySize.y - translation;
return location;
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java
index 8f693a6..a5da65f 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java
@@ -15,21 +15,33 @@
*/
package com.android.launcher3.taskbar.bubbles;
+import static com.android.app.animation.Interpolators.EMPHASIZED_ACCELERATE;
+import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X;
+
import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.annotation.Nullable;
import android.content.Context;
import android.graphics.Rect;
import android.util.AttributeSet;
+import android.util.LayoutDirection;
import android.util.Log;
+import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
+import androidx.dynamicanimation.animation.SpringForce;
+
import com.android.launcher3.R;
+import com.android.launcher3.anim.SpringAnimationBuilder;
import com.android.launcher3.taskbar.TaskbarActivityContext;
import com.android.launcher3.views.ActivityContext;
+import com.android.wm.shell.common.bubbles.BubbleBarLocation;
import java.util.List;
import java.util.function.Consumer;
@@ -70,6 +82,18 @@
private static final int ARROW_POSITION_ANIMATION_DURATION_MS = 200;
private static final int WIDTH_ANIMATION_DURATION_MS = 200;
+ private static final long FADE_OUT_ANIM_ALPHA_DURATION_MS = 50L;
+ private static final long FADE_OUT_ANIM_ALPHA_DELAY_MS = 50L;
+ private static final long FADE_OUT_ANIM_POSITION_DURATION_MS = 100L;
+ // During fade out animation we shift the bubble bar 1/80th of the screen width
+ private static final float FADE_OUT_ANIM_POSITION_SHIFT = 1 / 80f;
+
+ private static final long FADE_IN_ANIM_ALPHA_DURATION_MS = 100L;
+ // Use STIFFNESS_MEDIUMLOW which is not defined in the API constants
+ private static final float FADE_IN_ANIM_POSITION_SPRING_STIFFNESS = 400f;
+ // During fade in animation we shift the bubble bar 1/60th of the screen width
+ private static final float FADE_IN_ANIM_POSITION_SHIFT = 1 / 60f;
+
private final BubbleBarBackground mBubbleBarBackground;
/**
@@ -86,11 +110,13 @@
private final float mIconSize;
// The elevation of the bubbles within the bar
private final float mBubbleElevation;
+ private final int mPointerSize;
// Whether the bar is expanded (i.e. the bubble activity is being displayed).
private boolean mIsBarExpanded = false;
// The currently selected bubble view.
private BubbleView mSelectedBubbleView;
+ private BubbleBarLocation mBubbleBarLocation = BubbleBarLocation.DEFAULT;
// The click listener when the bubble bar is collapsed.
private View.OnClickListener mOnClickListener;
@@ -102,6 +128,9 @@
// collapsed state and 1 to the fully expanded state.
private final ValueAnimator mWidthAnimator = ValueAnimator.ofFloat(0, 1);
+ @Nullable
+ private Animator mBubbleBarLocationAnimator = null;
+
// We don't reorder the bubbles when they are expanded as it could be jarring for the user
// this runnable will be populated with any reordering of the bubbles that should be applied
// once they are collapsed.
@@ -114,6 +143,8 @@
@Nullable
private BubbleView mDraggedBubbleView;
+ private int mPreviousLayoutDirection = LayoutDirection.UNDEFINED;
+
public BubbleBarView(Context context) {
this(context, null);
}
@@ -136,6 +167,8 @@
mIconSpacing = getResources().getDimensionPixelSize(R.dimen.bubblebar_icon_spacing);
mIconSize = getResources().getDimensionPixelSize(R.dimen.bubblebar_icon_size);
mBubbleElevation = getResources().getDimensionPixelSize(R.dimen.bubblebar_icon_elevation);
+ mPointerSize = getResources().getDimensionPixelSize(R.dimen.bubblebar_pointer_size);
+
setClipToPadding(false);
mBubbleBarBackground = new BubbleBarBackground(activityContext,
@@ -184,7 +217,7 @@
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
mBubbleBarBounds.left = left;
- mBubbleBarBounds.top = top;
+ mBubbleBarBounds.top = top + mPointerSize;
mBubbleBarBounds.right = right;
mBubbleBarBounds.bottom = bottom;
@@ -199,24 +232,123 @@
@Override
public void onRtlPropertiesChanged(int layoutDirection) {
- // TODO(b/313661121): set this based on bubble bar position and not LTR or RTL
- boolean onLeft = layoutDirection == LAYOUT_DIRECTION_RTL;
+ if (mBubbleBarLocation == BubbleBarLocation.DEFAULT
+ && mPreviousLayoutDirection != layoutDirection) {
+ Log.d(TAG, "BubbleBar RTL properties changed, new layoutDirection=" + layoutDirection
+ + " previous layoutDirection=" + mPreviousLayoutDirection);
+ mPreviousLayoutDirection = layoutDirection;
+ onBubbleBarLocationChanged();
+ }
+ }
+
+ private void onBubbleBarLocationChanged() {
+ final boolean onLeft = mBubbleBarLocation.isOnLeft(isLayoutRtl());
mBubbleBarBackground.setAnchorLeft(onLeft);
mRelativePivotX = onLeft ? 0f : 1f;
+ ViewGroup.LayoutParams layoutParams = getLayoutParams();
+ if (layoutParams instanceof LayoutParams lp) {
+ lp.gravity = Gravity.BOTTOM | (onLeft ? Gravity.LEFT : Gravity.RIGHT);
+ setLayoutParams(lp);
+ }
+ invalidate();
}
/**
- * @return <code>true</code> when bar is pinned to the left edge of the screen
+ * @return current {@link BubbleBarLocation}
*/
- public boolean isOnLeft() {
- return getLayoutDirection() == LAYOUT_DIRECTION_RTL;
+ public BubbleBarLocation getBubbleBarLocation() {
+ return mBubbleBarLocation;
+ }
+
+ /**
+ * Update {@link BubbleBarLocation}
+ */
+ public void setBubbleBarLocation(BubbleBarLocation bubbleBarLocation, boolean animate) {
+ if (animate) {
+ animateToBubbleBarLocation(bubbleBarLocation);
+ } else {
+ setBubbleBarLocationInternal(bubbleBarLocation);
+ }
+ }
+
+ private void setBubbleBarLocationInternal(BubbleBarLocation bubbleBarLocation) {
+ if (bubbleBarLocation != mBubbleBarLocation) {
+ mBubbleBarLocation = bubbleBarLocation;
+ onBubbleBarLocationChanged();
+ invalidate();
+ }
+ }
+
+ private void animateToBubbleBarLocation(BubbleBarLocation bubbleBarLocation) {
+ if (bubbleBarLocation == mBubbleBarLocation) {
+ // nothing to do, already at expected location
+ return;
+ }
+ if (mBubbleBarLocationAnimator != null && mBubbleBarLocationAnimator.isRunning()) {
+ mBubbleBarLocationAnimator.cancel();
+ }
+
+ // Location animation uses two separate animators.
+ // First animator hides the bar.
+ // After it completes, location update is sent to layout the bar in the new location.
+ // Second animator is started to show the bar.
+ mBubbleBarLocationAnimator = getLocationUpdateFadeOutAnimator();
+ mBubbleBarLocationAnimator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ // Bubble bar is not visible, update the location
+ setBubbleBarLocationInternal(bubbleBarLocation);
+ // Animate it in
+ mBubbleBarLocationAnimator = getLocationUpdateFadeInAnimator();
+ mBubbleBarLocationAnimator.start();
+ }
+ });
+ mBubbleBarLocationAnimator.start();
+ }
+
+ private AnimatorSet getLocationUpdateFadeOutAnimator() {
+ final float shift =
+ getResources().getDisplayMetrics().widthPixels * FADE_OUT_ANIM_POSITION_SHIFT;
+ final float tx = mBubbleBarLocation.isOnLeft(isLayoutRtl()) ? shift : -shift;
+
+ ObjectAnimator positionAnim = ObjectAnimator.ofFloat(this, TRANSLATION_X, tx)
+ .setDuration(FADE_OUT_ANIM_POSITION_DURATION_MS);
+ positionAnim.setInterpolator(EMPHASIZED_ACCELERATE);
+
+ ObjectAnimator alphaAnim = ObjectAnimator.ofFloat(this, ALPHA, 0f)
+ .setDuration(FADE_OUT_ANIM_ALPHA_DURATION_MS);
+ alphaAnim.setStartDelay(FADE_OUT_ANIM_ALPHA_DELAY_MS);
+
+ AnimatorSet animatorSet = new AnimatorSet();
+ animatorSet.playTogether(positionAnim, alphaAnim);
+ return animatorSet;
+ }
+
+ private Animator getLocationUpdateFadeInAnimator() {
+ final float shift =
+ getResources().getDisplayMetrics().widthPixels * FADE_IN_ANIM_POSITION_SHIFT;
+ final float startTx = mBubbleBarLocation.isOnLeft(isLayoutRtl()) ? shift : -shift;
+
+ ValueAnimator positionAnim = new SpringAnimationBuilder(getContext())
+ .setStartValue(startTx)
+ .setEndValue(0)
+ .setDampingRatio(SpringForce.DAMPING_RATIO_LOW_BOUNCY)
+ .setStiffness(FADE_IN_ANIM_POSITION_SPRING_STIFFNESS)
+ .build(this, VIEW_TRANSLATE_X);
+
+ ObjectAnimator alphaAnim = ObjectAnimator.ofFloat(this, ALPHA, 1f)
+ .setDuration(FADE_IN_ANIM_ALPHA_DURATION_MS);
+
+ AnimatorSet animatorSet = new AnimatorSet();
+ animatorSet.playTogether(positionAnim, alphaAnim);
+ return animatorSet;
}
/**
* Updates the bounds with translation that may have been applied and returns the result.
*/
public Rect getBubbleBarBounds() {
- mBubbleBarBounds.top = getTop() + (int) getTranslationY();
+ mBubbleBarBounds.top = getTop() + (int) getTranslationY() + mPointerSize;
mBubbleBarBounds.bottom = getBottom() + (int) getTranslationY();
return mBubbleBarBounds;
}
@@ -290,7 +422,7 @@
int bubbleCount = getChildCount();
final float ty = (mBubbleBarBounds.height() - mIconSize) / 2f;
final boolean animate = getVisibility() == VISIBLE;
- final boolean onLeft = isOnLeft();
+ final boolean onLeft = mBubbleBarLocation.isOnLeft(isLayoutRtl());
for (int i = 0; i < bubbleCount; i++) {
BubbleView bv = (BubbleView) getChildAt(i);
bv.setTranslationY(ty);
@@ -453,7 +585,7 @@
private float arrowPositionForSelectedWhenExpanded() {
final int index = indexOfChild(mSelectedBubbleView);
final int bubblePosition;
- if (isOnLeft()) {
+ if (mBubbleBarLocation.isOnLeft(isLayoutRtl())) {
// Bubble positions are reversed. First bubble is on the right.
bubblePosition = getChildCount() - index - 1;
} else {
@@ -465,7 +597,7 @@
private float arrowPositionForSelectedWhenCollapsed() {
final int index = indexOfChild(mSelectedBubbleView);
final int bubblePosition;
- if (isOnLeft()) {
+ if (mBubbleBarLocation.isOnLeft(isLayoutRtl())) {
// Bubble positions are reversed. First bubble may be shifted, if there are more
// bubbles than the current bubble and overflow.
bubblePosition = index == 0 && getChildCount() > 2 ? 1 : 0;
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java
index 6bb7b04..0f019a3 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java
@@ -37,6 +37,7 @@
import com.android.launcher3.util.MultiPropertyFactory;
import com.android.launcher3.util.MultiValueAlpha;
import com.android.quickstep.SystemUiProxy;
+import com.android.wm.shell.common.bubbles.BubbleBarLocation;
import java.util.List;
import java.util.Objects;
@@ -54,6 +55,7 @@
private final TaskbarActivityContext mActivity;
private final BubbleBarView mBarView;
private final int mIconSize;
+ private final int mPointerSize;
// Initialized in init.
private BubbleStashController mBubbleStashController;
@@ -86,6 +88,8 @@
mBubbleBarAlpha = new MultiValueAlpha(mBarView, 1 /* num alpha channels */);
mBubbleBarAlpha.setUpdateVisibility(true);
mIconSize = activity.getResources().getDimensionPixelSize(R.dimen.bubblebar_icon_size);
+ mPointerSize = activity.getResources().getDimensionPixelSize(
+ R.dimen.bubblebar_pointer_size);
}
public void init(TaskbarControllers controllers, BubbleControllers bubbleControllers) {
@@ -96,9 +100,11 @@
mTaskbarInsetsController = controllers.taskbarInsetsController;
mActivity.addOnDeviceProfileChangeListener(dp ->
- mBarView.getLayoutParams().height = mActivity.getDeviceProfile().taskbarHeight
+ mBarView.getLayoutParams().height =
+ mActivity.getDeviceProfile().taskbarHeight + mPointerSize
);
- mBarView.getLayoutParams().height = mActivity.getDeviceProfile().taskbarHeight;
+ mBarView.getLayoutParams().height =
+ mActivity.getDeviceProfile().taskbarHeight + mPointerSize;
mBubbleBarScale.updateValue(1f);
mBubbleClickListener = v -> onBubbleClicked(v);
mBubbleBarClickListener = v -> onBubbleBarClicked();
@@ -169,6 +175,20 @@
}
/**
+ * @return current {@link BubbleBarLocation}
+ */
+ public BubbleBarLocation getBubbleBarLocation() {
+ return mBarView.getBubbleBarLocation();
+ }
+
+ /**
+ * Update bar {@link BubbleBarLocation}
+ */
+ public void setBubbleBarLocation(BubbleBarLocation bubbleBarLocation, boolean animate) {
+ mBarView.setBubbleBarLocation(bubbleBarLocation, animate);
+ }
+
+ /**
* The bounds of the bubble bar.
*/
public Rect getBubbleBarBounds() {
@@ -213,6 +233,10 @@
if (mHiddenForNoBubbles != hidden) {
mHiddenForNoBubbles = hidden;
updateVisibilityForStateChange();
+ if (hidden) {
+ mBarView.setAlpha(0);
+ mBarView.setExpanded(false);
+ }
mActivity.bubbleBarVisibilityChanged(!hidden);
}
}
@@ -239,8 +263,6 @@
mBarView.setVisibility(VISIBLE);
} else {
mBarView.setVisibility(INVISIBLE);
- mBarView.setAlpha(0);
- mBarView.setExpanded(false);
}
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashController.java
index 09021ed..e25e586 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashController.java
@@ -31,6 +31,7 @@
import com.android.launcher3.taskbar.TaskbarInsetsController;
import com.android.launcher3.taskbar.TaskbarStashController;
import com.android.launcher3.util.MultiPropertyFactory;
+import com.android.wm.shell.common.bubbles.BubbleBarLocation;
/**
* Coordinates between controllers such as BubbleBarView and BubbleHandleViewController to
@@ -356,4 +357,9 @@
public boolean isEventOverStashHandle(MotionEvent ev) {
return mHandleViewController.isEventOverHandle(ev);
}
+
+ /** Set a bubble bar location */
+ public void setBubbleBarLocation(BubbleBarLocation bubbleBarLocation) {
+ mHandleViewController.setBubbleBarLocation(bubbleBarLocation);
+ }
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashedHandleViewController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashedHandleViewController.java
index f88460f..f64517a 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashedHandleViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashedHandleViewController.java
@@ -16,7 +16,6 @@
package com.android.launcher3.taskbar.bubbles;
import static android.view.View.INVISIBLE;
-import static android.view.View.LAYOUT_DIRECTION_RTL;
import static android.view.View.VISIBLE;
import android.animation.Animator;
@@ -39,6 +38,7 @@
import com.android.launcher3.util.MultiPropertyFactory;
import com.android.launcher3.util.MultiValueAlpha;
import com.android.systemui.shared.navigationbar.RegionSamplingHelper;
+import com.android.wm.shell.common.bubbles.BubbleBarLocation;
/**
* Handles properties/data collection, then passes the results to our stashed handle View to render.
@@ -119,14 +119,14 @@
}, Executors.UI_HELPER_EXECUTOR);
mStashedHandleView.addOnLayoutChangeListener((view, i, i1, i2, i3, i4, i5, i6, i7) ->
- updateBounds());
+ updateBounds(mBarViewController.getBubbleBarLocation()));
}
- private void updateBounds() {
+ private void updateBounds(BubbleBarLocation bubbleBarLocation) {
// As more bubbles get added, the icon bounds become larger. To ensure a consistent
// handle bar position, we pin it to the edge of the screen.
final int stashedCenterY = mStashedHandleView.getHeight() - mStashedTaskbarHeight / 2;
- if (isOnLeft()) {
+ if (bubbleBarLocation.isOnLeft(mStashedHandleView.isLayoutRtl())) {
final int left = mBarViewController.getHorizontalMargin();
mStashedHandleBounds.set(
left,
@@ -149,11 +149,6 @@
mStashedHandleView.setPivotY(mStashedHandleView.getHeight() - mStashedTaskbarHeight / 2f);
}
- private boolean isOnLeft() {
- // TODO(b/313661121): set this based on bubble bar position and not LTR or RTL
- return mStashedHandleView.getLayoutDirection() == LAYOUT_DIRECTION_RTL;
- }
-
public void onDestroy() {
mRegionSamplingHelper.stopAndDestroy();
mRegionSamplingHelper = null;
@@ -301,4 +296,9 @@
public boolean containsX(int x) {
return x >= mStashedHandleBounds.left && x <= mStashedHandleBounds.right;
}
+
+ /** Set a bubble bar location */
+ public void setBubbleBarLocation(BubbleBarLocation bubbleBarLocation) {
+ updateBounds(bubbleBarLocation);
+ }
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/navbutton/TaskbarNavLayoutter.kt b/quickstep/src/com/android/launcher3/taskbar/navbutton/TaskbarNavLayoutter.kt
index 5c57a01..9840791 100644
--- a/quickstep/src/com/android/launcher3/taskbar/navbutton/TaskbarNavLayoutter.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/TaskbarNavLayoutter.kt
@@ -105,8 +105,16 @@
contextualMargin, Gravity.START)
if (imeSwitcher != null) {
+ val imeStartMargin = resources.getDimensionPixelSize(
+ R.dimen.taskbar_ime_switcher_button_margin_start)
startContextualContainer.addView(imeSwitcher)
- imeSwitcher.layoutParams = getParamsToCenterView()
+ val imeSwitcherButtonParams = FrameLayout.LayoutParams(
+ FrameLayout.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)
+ imeSwitcherButtonParams.apply {
+ marginStart = imeStartMargin
+ gravity = Gravity.CENTER_VERTICAL
+ }
+ imeSwitcher.layoutParams = imeSwitcherButtonParams
}
if (a11yButton != null) {
endContextualContainer.addView(a11yButton)
diff --git a/quickstep/src/com/android/launcher3/uioverrides/ApiWrapper.java b/quickstep/src/com/android/launcher3/uioverrides/ApiWrapper.java
index dcc3b05..873dea8 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/ApiWrapper.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/ApiWrapper.java
@@ -21,6 +21,7 @@
import android.app.Person;
import android.content.Context;
import android.content.Intent;
+import android.content.IntentSender;
import android.content.pm.ActivityInfo;
import android.content.pm.LauncherActivityInfo;
import android.content.pm.LauncherApps;
@@ -158,6 +159,28 @@
}
/**
+ * Returns an intent which can be used to open Private Space Settings.
+ */
+ public static Intent getPrivateSpaceSettingsIntent(Context context) {
+ if (android.os.Flags.allowPrivateProfile() && Flags.enablePrivateSpace()) {
+ LauncherApps launcherApps = context.getSystemService(LauncherApps.class);
+ IntentSender intentSender = launcherApps.getPrivateSpaceSettingsIntent();
+ if (intentSender == null) {
+ return null;
+ }
+ StartActivityParams params = new StartActivityParams((PendingIntent) null, 0);
+ params.intentSender = intentSender;
+ ActivityOptions options = ActivityOptions.makeBasic()
+ .setPendingIntentBackgroundActivityStartMode(ActivityOptions
+ .MODE_BACKGROUND_ACTIVITY_START_ALLOWED);
+ params.options = options.toBundle();
+ params.requireActivityResult = false;
+ return ProxyActivityStarter.getLaunchIntent(context, params);
+ }
+ return null;
+ }
+
+ /**
* Checks if an activity is flagged as non-resizeable.
*/
public static boolean isNonResizeableActivity(LauncherActivityInfo lai) {
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
index 75dfe30..b49c752 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
@@ -21,6 +21,7 @@
import static android.view.accessibility.AccessibilityEvent.TYPE_VIEW_FOCUSED;
import static com.android.app.animation.Interpolators.EMPHASIZED;
+import static com.android.internal.jank.Cuj.CUJ_LAUNCHER_LAUNCH_APP_PAIR_FROM_WORKSPACE;
import static com.android.launcher3.Flags.enablePredictiveBackGesture;
import static com.android.launcher3.Flags.enableUnfoldStateAnimation;
import static com.android.launcher3.LauncherConstants.SavedInstanceKeys.PENDING_SPLIT_SELECT_INFO;
@@ -39,8 +40,8 @@
import static com.android.launcher3.compat.AccessibilityManagerCompat.sendCustomAccessibilityEvent;
import static com.android.launcher3.config.FeatureFlags.enableSplitContextually;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_APP_LAUNCH_TAP;
-import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SPLIT_SELECTION_EXIT_INTERRUPTED;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SPLIT_SELECTION_EXIT_HOME;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SPLIT_SELECTION_EXIT_INTERRUPTED;
import static com.android.launcher3.model.data.ItemInfo.NO_MATCHING_ID;
import static com.android.launcher3.popup.QuickstepSystemShortcut.getSplitSelectShortcutByPosition;
import static com.android.launcher3.popup.SystemShortcut.APP_INFO;
@@ -61,7 +62,7 @@
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
import static com.android.quickstep.util.AnimUtils.completeRunnableListCallback;
import static com.android.quickstep.util.SplitAnimationTimings.TABLET_HOME_TO_SPLIT;
-import static com.android.quickstep.views.DesktopTaskView.isDesktopModeSupported;
+import static com.android.window.flags.Flags.enableDesktopWindowingMode;
import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_HOME_KEY;
import static com.android.wm.shell.common.split.SplitScreenConstants.SNAP_TO_50_50;
@@ -105,6 +106,7 @@
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Flags;
import com.android.launcher3.HomeTransitionController;
+import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.LauncherState;
@@ -257,7 +259,7 @@
getDepthController(), getStatsLogManager(),
systemUiProxy, RecentsModel.INSTANCE.get(this),
() -> onStateBack());
- if (isDesktopModeSupported()) {
+ if (enableDesktopWindowingMode()) {
mDesktopRecentsTransitionController = new DesktopRecentsTransitionController(
getStateManager(), systemUiProxy, getIApplicationThread(),
getDepthController());
@@ -283,7 +285,7 @@
mTISBindHelper = new TISBindHelper(this, this::onTISConnected);
mDepthController = new DepthController(this);
mDesktopVisibilityController = new DesktopVisibilityController(this);
- if (isDesktopModeSupported()) {
+ if (enableDesktopWindowingMode()) {
mDesktopVisibilityController.registerSystemUiListener();
mSplitSelectStateController.initSplitFromDesktopController(this);
}
@@ -666,6 +668,14 @@
}
@Override
+ protected boolean initDeviceProfile(InvariantDeviceProfile idp) {
+ final boolean ret = super.initDeviceProfile(idp);
+ mDeviceProfile.isPredictiveBackSwipe =
+ getApplicationInfo().isOnBackInvokedCallbackEnabled();
+ return ret;
+ }
+
+ @Override
public void startSplitSelection(SplitSelectSource splitSelectSource) {
RecentsView recentsView = getOverviewPanel();
// Check if there is already an instance of this app running, if so, initiate the split
@@ -938,7 +948,7 @@
@Override
public void setResumed() {
- if (isDesktopModeSupported()) {
+ if (enableDesktopWindowingMode()) {
DesktopVisibilityController controller = mDesktopVisibilityController;
if (controller != null && controller.areFreeformTasksVisible()
&& !controller.isRecentsGestureInProgress()) {
@@ -1338,7 +1348,8 @@
* Launches two apps as an app pair.
*/
public void launchAppPair(AppPairIcon appPairIcon) {
- mSplitSelectStateController.getAppPairsController().launchAppPair(appPairIcon);
+ mSplitSelectStateController.getAppPairsController().launchAppPair(appPairIcon,
+ CUJ_LAUNCHER_LAUNCH_APP_PAIR_FROM_WORKSPACE);
}
public boolean canStartHomeSafely() {
diff --git a/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java b/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
index e6a115a..6c1d4b1 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
@@ -133,7 +133,7 @@
// Create transition animations to split select
RecentsPagedOrientationHandler orientationHandler =
((RecentsView) mLauncher.getOverviewPanel()).getPagedOrientationHandler();
- Pair<FloatProperty, FloatProperty> taskViewsFloat =
+ Pair<FloatProperty<RecentsView>, FloatProperty<RecentsView>> taskViewsFloat =
orientationHandler.getSplitSelectTaskOffset(
TASK_PRIMARY_SPLIT_TRANSLATION, TASK_SECONDARY_SPLIT_TRANSLATION,
mLauncher.getDeviceProfile());
diff --git a/quickstep/src/com/android/launcher3/uioverrides/flags/DeveloperOptionsUI.java b/quickstep/src/com/android/launcher3/uioverrides/flags/DeveloperOptionsUI.java
index 369ff14..6713964 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/flags/DeveloperOptionsUI.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/flags/DeveloperOptionsUI.java
@@ -22,6 +22,7 @@
import static com.android.launcher3.LauncherPrefs.ALL_APPS_OVERVIEW_THRESHOLD;
import static com.android.launcher3.LauncherPrefs.PRIVATE_SPACE_APPS;
+import static com.android.launcher3.config.FeatureFlags.LPNH_EXTRA_TOUCH_WIDTH_DP;
import static com.android.launcher3.config.FeatureFlags.LPNH_HAPTIC_HINT_DELAY;
import static com.android.launcher3.config.FeatureFlags.LPNH_HAPTIC_HINT_END_SCALE_PERCENT;
import static com.android.launcher3.config.FeatureFlags.LPNH_HAPTIC_HINT_ITERATIONS;
@@ -359,6 +360,10 @@
"Slop multiplier (applied to edge slop, "
+ "which is generally already 50% higher than touch slop)",
25, 200, 100, LPNH_SLOP_PERCENTAGE));
+ category.addPreference(createSeekBarPreference(
+ "Extra width DP (how far outside the sides of the nav bar to trigger)",
+ // Stashed taskbar is currently 220dp; -86 (x2) would result in 48dp touch area.
+ -86, 100, 1, LPNH_EXTRA_TOUCH_WIDTH_DP));
category.addPreference(createSeekBarPreference("LPNH timeout",
100, 500, 1, LPNH_TIMEOUT_MS));
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java b/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java
index 577eba6..b6002e8 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java
@@ -16,6 +16,7 @@
package com.android.launcher3.uioverrides.states;
import static com.android.app.animation.Interpolators.DECELERATE_2;
+import static com.android.launcher3.Flags.enableScalingRevealHomeAnimation;
import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_ALLAPPS;
import android.content.Context;
@@ -28,6 +29,7 @@
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.util.Themes;
import com.android.launcher3.views.ActivityContext;
+import com.android.quickstep.util.BaseDepthController;
import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
/**
@@ -105,8 +107,14 @@
return context.getDeviceProfile().bottomSheetDepth;
} else {
// The scrim fades in at approximately 50% of the swipe gesture.
- // This means that the depth should be greater than 1, in order to fully zoom out.
- return 2f;
+ if (enableScalingRevealHomeAnimation()) {
+ // This means that the depth should be twice of what we want, in order to fully zoom
+ // out during the visible portion of the animation.
+ return BaseDepthController.DEPTH_60_PERCENT;
+ } else {
+ // This means that the depth should be greater than 1, in order to fully zoom out.
+ return 2f;
+ }
}
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java b/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
index d11a08b..a443c00 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
@@ -15,9 +15,10 @@
*/
package com.android.launcher3.uioverrides.states;
+import static com.android.launcher3.Flags.enableScalingRevealHomeAnimation;
import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_BACKGROUND;
import static com.android.quickstep.TaskAnimationManager.ENABLE_SHELL_TRANSITIONS;
-import static com.android.quickstep.views.DesktopTaskView.isDesktopModeSupported;
+import static com.android.window.flags.Flags.enableDesktopWindowingMode;
import android.content.Context;
import android.graphics.Color;
@@ -26,6 +27,7 @@
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Launcher;
import com.android.launcher3.allapps.AllAppsTransitionController;
+import com.android.quickstep.util.BaseDepthController;
import com.android.quickstep.util.LayoutUtils;
import com.android.quickstep.views.RecentsView;
@@ -90,13 +92,15 @@
@Override
protected float getDepthUnchecked(Context context) {
- if (isDesktopModeSupported()) {
- if (Launcher.getLauncher(context).areFreeformTasksVisible()) {
- // Don't blur the background while freeform tasks are visible
- return 0;
- }
+ if (enableDesktopWindowingMode()
+ && Launcher.getLauncher(context).areFreeformTasksVisible()) {
+ // Don't blur the background while freeform tasks are visible
+ return BaseDepthController.DEPTH_0_PERCENT;
+ } else if (enableScalingRevealHomeAnimation()) {
+ return BaseDepthController.DEPTH_70_PERCENT;
+ } else {
+ return 1f;
}
- return 1;
}
@Override
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java b/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java
index 7650235..8c2efc2 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java
@@ -16,6 +16,7 @@
package com.android.launcher3.uioverrides.states;
import static com.android.app.animation.Interpolators.DECELERATE_2;
+import static com.android.launcher3.Flags.enableScalingRevealHomeAnimation;
import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_OVERVIEW;
import static com.android.wm.shell.Flags.enableSplitContextual;
@@ -29,6 +30,7 @@
import com.android.launcher3.R;
import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.Themes;
+import com.android.quickstep.util.BaseDepthController;
import com.android.quickstep.util.LayoutUtils;
import com.android.quickstep.views.RecentsView;
import com.android.quickstep.views.TaskView;
@@ -186,8 +188,14 @@
@Override
protected float getDepthUnchecked(Context context) {
- //TODO revert when b/178661709 is fixed
- return SystemProperties.getBoolean("ro.launcher.depth.overview", true) ? 1 : 0;
+ // TODO(178661709): revert to always scaled
+ if (enableScalingRevealHomeAnimation()) {
+ return SystemProperties.getBoolean("ro.launcher.depth.overview", true)
+ ? BaseDepthController.DEPTH_70_PERCENT
+ : BaseDepthController.DEPTH_0_PERCENT;
+ } else {
+ return SystemProperties.getBoolean("ro.launcher.depth.overview", true) ? 1 : 0;
+ }
}
@Override
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/QuickSwitchState.java b/quickstep/src/com/android/launcher3/uioverrides/states/QuickSwitchState.java
index ba44d6a..2587395 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/QuickSwitchState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/QuickSwitchState.java
@@ -16,7 +16,7 @@
package com.android.launcher3.uioverrides.states;
import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_BACKGROUND;
-import static com.android.quickstep.views.DesktopTaskView.isDesktopModeSupported;
+import static com.android.window.flags.Flags.enableDesktopWindowingMode;
import android.graphics.Color;
@@ -46,7 +46,7 @@
@Override
public int getWorkspaceScrimColor(Launcher launcher) {
- if (isDesktopModeSupported()) {
+ if (enableDesktopWindowingMode()) {
if (launcher.areFreeformTasksVisible()) {
// No scrim while freeform tasks are visible
return Color.TRANSPARENT;
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java b/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java
index 8c92c7d..b401868 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java
@@ -37,7 +37,6 @@
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.LauncherState.OVERVIEW;
import static com.android.launcher3.LauncherState.OVERVIEW_SPLIT_SELECT;
-import static com.android.launcher3.QuickstepTransitionManager.TASKBAR_TO_HOME_DURATION;
import static com.android.launcher3.WorkspaceStateTransitionAnimation.getWorkspaceSpringScaleAnimator;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_APPS_FADE;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_DEPTH;
@@ -59,6 +58,7 @@
import com.android.launcher3.CellLayout;
import com.android.launcher3.Hotseat;
import com.android.launcher3.LauncherState;
+import com.android.launcher3.QuickstepTransitionManager;
import com.android.launcher3.Workspace;
import com.android.launcher3.states.StateAnimationConfig;
import com.android.launcher3.touch.AllAppsSwipeController;
@@ -108,7 +108,8 @@
// We sync the scrim fade with the taskbar animation duration to avoid any flickers for
// taskbar icons disappearing before hotseat icons show up.
- float scrimUpperBoundFromSplit = TASKBAR_TO_HOME_DURATION / (float) config.duration;
+ float scrimUpperBoundFromSplit =
+ QuickstepTransitionManager.getTaskbarToHomeDuration() / (float) config.duration;
config.setInterpolator(ANIM_OVERVIEW_ACTIONS_FADE, clampToProgress(LINEAR, 0, 0.25f));
config.setInterpolator(ANIM_SCRIM_FADE,
fromState == OVERVIEW_SPLIT_SELECT
@@ -136,7 +137,8 @@
// Sync scroll so that it ends before or at the same time as the taskbar animation.
if (mActivity.getDeviceProfile().isTaskbarPresent) {
- config.duration = Math.min(config.duration, TASKBAR_TO_HOME_DURATION);
+ config.duration = Math.min(
+ config.duration, QuickstepTransitionManager.getTaskbarToHomeDuration());
}
overview.snapToPage(DEFAULT_PAGE, Math.toIntExact(config.duration));
} else {
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java
index 3a1c42d..8ef35c0 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java
@@ -49,7 +49,6 @@
import static com.android.launcher3.util.NavigationMode.THREE_BUTTONS;
import static com.android.launcher3.util.VibratorWrapper.OVERVIEW_HAPTIC;
import static com.android.launcher3.util.window.RefreshRateTracker.getSingleFrameMs;
-import static com.android.quickstep.views.DesktopTaskView.isDesktopModeSupported;
import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_HORIZONTAL_OFFSET;
import static com.android.quickstep.views.RecentsView.CONTENT_ALPHA;
import static com.android.quickstep.views.RecentsView.FULLSCREEN_PROGRESS;
@@ -178,10 +177,6 @@
if ((stateFlags & SYSUI_STATE_OVERVIEW_DISABLED) != 0) {
return false;
}
- if (isDesktopModeSupported()) {
- // TODO(b/268075592): add support for quickswitch to/from desktop
- return false;
- }
if (isTrackpadMultiFingerSwipe(ev)) {
return isTrackpadFourFingerSwipe(ev);
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java
index ff142fe..de73630 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java
@@ -30,7 +30,6 @@
import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_FADE;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_TRANSLATE;
import static com.android.launcher3.util.SystemUiController.UI_STATE_FULLSCREEN_TASK;
-import static com.android.quickstep.views.DesktopTaskView.isDesktopModeSupported;
import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_HORIZONTAL_OFFSET;
import static com.android.quickstep.views.RecentsView.RECENTS_SCALE_PROPERTY;
import static com.android.quickstep.views.RecentsView.UPDATE_SYSUI_FLAGS_THRESHOLD;
@@ -79,10 +78,6 @@
if ((ev.getEdgeFlags() & Utilities.EDGE_NAV_BAR) == 0) {
return false;
}
- if (isDesktopModeSupported()) {
- // TODO(b/268075592): add support for quickswitch to/from desktop
- return false;
- }
return true;
}
diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
index 4752225..0320f50 100644
--- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
+++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
@@ -59,9 +59,9 @@
import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.INVALID_VELOCITY_ON_SWIPE_UP;
import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.LAUNCHER_DESTROYED;
import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.ON_SETTLED_ON_END_TARGET;
-import static com.android.quickstep.views.DesktopTaskView.isDesktopModeSupported;
import static com.android.quickstep.views.RecentsView.UPDATE_SYSUI_FLAGS_THRESHOLD;
import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
+import static com.android.window.flags.Flags.enableDesktopWindowingMode;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -947,7 +947,7 @@
public void onRecentsAnimationStart(RecentsAnimationController controller,
RecentsAnimationTargets targets) {
super.onRecentsAnimationStart(controller, targets);
- if (isDesktopModeSupported() && targets.hasDesktopTasks()) {
+ if (enableDesktopWindowingMode() && targets.hasDesktopTasks()) {
mRemoteTargetHandles = mTargetGluer.assignTargetsForDesktop(targets);
} else {
int untrimmedAppCount = mRemoteTargetHandles.length;
@@ -1170,7 +1170,7 @@
mStateCallback.setState(STATE_SCALED_CONTROLLER_HOME | STATE_CAPTURE_SCREENSHOT);
// Notify the SysUI to use fade-in animation when entering PiP
SystemUiProxy.INSTANCE.get(mContext).setPipAnimationTypeToAlpha();
- if (isDesktopModeSupported()) {
+ if (enableDesktopWindowingMode()) {
// Notify the SysUI to stash desktop apps if they are visible
DesktopVisibilityController desktopVisibilityController =
mActivityInterface.getDesktopVisibilityController();
@@ -1255,7 +1255,11 @@
return LAST_TASK;
}
- if (isDesktopModeSupported() && endTarget == NEW_TASK) {
+ if (((mRecentsView.getNextPageTaskView() != null
+ && mRecentsView.getNextPageTaskView().isDesktopTask())
+ || (mRecentsView.getCurrentPageTaskView() != null
+ && mRecentsView.getCurrentPageTaskView().isDesktopTask()))
+ && endTarget == NEW_TASK) {
// TODO(b/268075592): add support for quickswitch to/from desktop
return LAST_TASK;
}
@@ -1416,9 +1420,11 @@
mGestureState.setState(STATE_RECENTS_SCROLLING_FINISHED);
setClampScrollOffset(false);
};
- if (mRecentsView != null) {
+ if (mRecentsView != null && (mRecentsView.getCurrentPageTaskView() != null
+ && !mRecentsView.getCurrentPageTaskView().isDesktopTask())) {
ActiveGestureLog.INSTANCE.trackEvent(ActiveGestureErrorDetector.GestureEvent
.SET_ON_PAGE_TRANSITION_END_CALLBACK);
+ // TODO(b/268075592): add support for quickswitch to/from desktop
mRecentsView.setOnPageTransitionEndCallback(onPageTransitionEnd);
} else {
onPageTransitionEnd.run();
@@ -2232,6 +2238,15 @@
mRecentsAnimationController, mRecentsAnimationTargets);
});
+ if ((mRecentsView.getNextPageTaskView() != null
+ && mRecentsView.getNextPageTaskView().isDesktopTask())
+ || (mRecentsView.getCurrentPageTaskView() != null
+ && mRecentsView.getCurrentPageTaskView().isDesktopTask())) {
+ // TODO(b/268075592): add support for quickswitch to/from desktop
+ mRecentsViewScrollLinked = false;
+ return;
+ }
+
// Disable scrolling in RecentsView for trackpad 3-finger swipe up gesture.
if (!mGestureState.isThreeFingerTrackpadGesture()) {
mRecentsViewScrollLinked = true;
diff --git a/quickstep/src/com/android/quickstep/AllAppsActionManager.kt b/quickstep/src/com/android/quickstep/AllAppsActionManager.kt
new file mode 100644
index 0000000..fd2ed3a
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/AllAppsActionManager.kt
@@ -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.quickstep
+
+import android.accessibilityservice.AccessibilityService.GLOBAL_ACTION_ACCESSIBILITY_ALL_APPS
+import android.app.PendingIntent
+import android.app.RemoteAction
+import android.content.Context
+import android.graphics.drawable.Icon
+import android.view.accessibility.AccessibilityManager
+import com.android.launcher3.R
+import java.util.concurrent.Executor
+
+/**
+ * Registers a [RemoteAction] for toggling All Apps if needed.
+ *
+ * We need this action when either [isHomeAndOverviewSame] or [isTaskbarPresent] is `true`. When
+ * home and overview are the same, we can control Launcher's or Taskbar's All Apps tray. If they are
+ * not the same, but Taskbar is present, we can only control Taskbar's tray.
+ */
+class AllAppsActionManager(
+ private val context: Context,
+ private val bgExecutor: Executor,
+ private val createAllAppsPendingIntent: () -> PendingIntent,
+) {
+
+ /** `true` if home and overview are the same Activity. */
+ var isHomeAndOverviewSame = false
+ set(value) {
+ field = value
+ updateSystemAction()
+ }
+
+ /** `true` if Taskbar is enabled. */
+ var isTaskbarPresent = false
+ set(value) {
+ field = value
+ updateSystemAction()
+ }
+
+ /** `true` if the action should be registered. */
+ var isActionRegistered = false
+ private set
+
+ private fun updateSystemAction() {
+ val shouldRegisterAction = isHomeAndOverviewSame || isTaskbarPresent
+ if (isActionRegistered == shouldRegisterAction) return
+ isActionRegistered = shouldRegisterAction
+
+ bgExecutor.execute {
+ val accessibilityManager =
+ context.getSystemService(AccessibilityManager::class.java) ?: return@execute
+ if (shouldRegisterAction) {
+ accessibilityManager.registerSystemAction(
+ RemoteAction(
+ Icon.createWithResource(context, R.drawable.ic_apps),
+ context.getString(R.string.all_apps_label),
+ context.getString(R.string.all_apps_label),
+ createAllAppsPendingIntent(),
+ ),
+ GLOBAL_ACTION_ACCESSIBILITY_ALL_APPS,
+ )
+ } else {
+ accessibilityManager.unregisterSystemAction(GLOBAL_ACTION_ACCESSIBILITY_ALL_APPS)
+ }
+ }
+ }
+
+ fun onDestroy() {
+ context
+ .getSystemService(AccessibilityManager::class.java)
+ ?.unregisterSystemAction(
+ GLOBAL_ACTION_ACCESSIBILITY_ALL_APPS,
+ )
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/BaseActivityInterface.java b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
index 2341e4c..24c99e3 100644
--- a/quickstep/src/com/android/quickstep/BaseActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
@@ -25,7 +25,7 @@
import static com.android.quickstep.GestureState.GestureEndTarget.RECENTS;
import static com.android.quickstep.util.RecentsAtomicAnimationFactory.INDEX_RECENTS_FADE_ANIM;
import static com.android.quickstep.util.RecentsAtomicAnimationFactory.INDEX_RECENTS_TRANSLATE_X_ANIM;
-import static com.android.quickstep.views.DesktopTaskView.isDesktopModeSupported;
+import static com.android.window.flags.Flags.enableDesktopWindowingMode;
import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_HORIZONTAL_OFFSET;
import static com.android.quickstep.views.RecentsView.FULLSCREEN_PROGRESS;
import static com.android.quickstep.views.RecentsView.RECENTS_SCALE_PROPERTY;
@@ -109,7 +109,7 @@
if (endTarget != null) {
// We were on our way to this state when we got canceled, end there instead.
startState = stateFromGestureEndTarget(endTarget);
- if (isDesktopModeSupported()) {
+ if (enableDesktopWindowingMode()) {
DesktopVisibilityController controller = getDesktopVisibilityController();
if (controller != null && controller.areFreeformTasksVisible()
&& endTarget == LAST_TASK) {
@@ -380,13 +380,6 @@
}
/**
- * Calculates the task size for the desktop task
- */
- public final void calculateDesktopTaskSize(Context context, DeviceProfile dp, Rect outRect) {
- calculateFocusTaskSize(context, dp, outRect);
- }
-
- /**
* Calculates the modal taskView size for the provided device configuration
*/
public final void calculateModalTaskSize(Context context, DeviceProfile dp, Rect outRect,
diff --git a/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java b/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java
index a9d8afc..f678bea 100644
--- a/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java
+++ b/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java
@@ -17,6 +17,7 @@
import static com.android.app.animation.Interpolators.EXAGGERATED_EASE;
import static com.android.app.animation.Interpolators.LINEAR;
+import static com.android.launcher3.Flags.enableScalingRevealHomeAnimation;
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.Utilities.mapBoundToRange;
import static com.android.launcher3.model.data.ItemInfo.NO_MATCHING_ID;
@@ -46,6 +47,7 @@
import com.android.launcher3.views.FloatingView;
import com.android.launcher3.widget.LauncherAppWidgetHostView;
import com.android.quickstep.util.RectFSpringAnim;
+import com.android.quickstep.util.ScalingWorkspaceRevealAnim;
import com.android.quickstep.util.StaggeredWorkspaceAnim;
import com.android.quickstep.util.TaskViewSimulator;
import com.android.quickstep.views.FloatingWidgetView;
@@ -296,9 +298,15 @@
@Override
public void playAtomicAnimation(float velocity) {
- new StaggeredWorkspaceAnim(mActivity, velocity, true /* animateOverviewScrim */,
- getViewIgnoredInWorkspaceRevealAnimation())
- .start();
+ if (enableScalingRevealHomeAnimation()) {
+ if (mActivity != null) {
+ new ScalingWorkspaceRevealAnim(mActivity).start();
+ }
+ } else {
+ new StaggeredWorkspaceAnim(mActivity, velocity, true /* animateOverviewScrim */,
+ getViewIgnoredInWorkspaceRevealAnimation())
+ .start();
+ }
}
}
}
diff --git a/quickstep/src/com/android/quickstep/OverviewCommandHelper.java b/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
index 65b5397..56c9a00 100644
--- a/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
+++ b/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
@@ -248,7 +248,15 @@
case TYPE_SHOW:
// already visible
return true;
+ case TYPE_KEYBOARD_INPUT: {
+ if (visibleRecentsView.isHandlingTouch()) {
+ return true;
+ }
+ }
case TYPE_HIDE: {
+ if (visibleRecentsView.isHandlingTouch()) {
+ return true;
+ }
mKeyboardTaskFocusIndex = INVALID_PAGE;
int currentPage = visibleRecentsView.getNextPage();
TaskView tv = (currentPage >= 0
diff --git a/quickstep/src/com/android/quickstep/RecentTasksList.java b/quickstep/src/com/android/quickstep/RecentTasksList.java
index f3704e09..4c1b1e0 100644
--- a/quickstep/src/com/android/quickstep/RecentTasksList.java
+++ b/quickstep/src/com/android/quickstep/RecentTasksList.java
@@ -20,7 +20,7 @@
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
import static com.android.quickstep.util.SplitScreenUtils.convertShellSplitBoundsToLauncher;
-import static com.android.quickstep.views.DesktopTaskView.isDesktopModeSupported;
+import static com.android.window.flags.Flags.enableDesktopWindowingMode;
import static com.android.wm.shell.util.GroupedRecentTaskInfo.TYPE_FREEFORM;
import android.app.ActivityManager;
@@ -270,7 +270,7 @@
int numVisibleTasks = 0;
for (GroupedRecentTaskInfo rawTask : rawTasks) {
- if (isDesktopModeSupported() && rawTask.getType() == TYPE_FREEFORM) {
+ if (enableDesktopWindowingMode() && rawTask.getType() == TYPE_FREEFORM) {
GroupTask desktopTask = createDesktopTask(rawTask);
allTasks.add(desktopTask);
continue;
diff --git a/quickstep/src/com/android/quickstep/RecentsActivity.java b/quickstep/src/com/android/quickstep/RecentsActivity.java
index 02f9a69..81ab6be 100644
--- a/quickstep/src/com/android/quickstep/RecentsActivity.java
+++ b/quickstep/src/com/android/quickstep/RecentsActivity.java
@@ -27,7 +27,7 @@
import static com.android.quickstep.OverviewComponentObserver.startHomeIntentSafely;
import static com.android.quickstep.TaskUtils.taskIsATargetWithMode;
import static com.android.quickstep.TaskViewUtils.createRecentsWindowAnimator;
-import static com.android.quickstep.views.DesktopTaskView.isDesktopModeSupported;
+import static com.android.window.flags.Flags.enableDesktopWindowingMode;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -144,7 +144,7 @@
systemUiProxy, RecentsModel.INSTANCE.get(this),
null /*activityBackCallback*/);
mDragLayer.recreateControllers();
- if (isDesktopModeSupported()) {
+ if (enableDesktopWindowingMode()) {
mDesktopRecentsTransitionController = new DesktopRecentsTransitionController(
getStateManager(), systemUiProxy, getIApplicationThread(),
null /* depthController */
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationTargets.java b/quickstep/src/com/android/quickstep/RecentsAnimationTargets.java
index 556dd7e..f936882 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationTargets.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationTargets.java
@@ -17,7 +17,8 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.view.RemoteAnimationTarget.MODE_CLOSING;
-import static com.android.quickstep.views.DesktopTaskView.isDesktopModeSupported;
+
+import static com.android.window.flags.Flags.enableDesktopWindowingMode;
import android.app.WindowConfiguration;
import android.graphics.Rect;
@@ -52,7 +53,7 @@
* @return {@code true} if at least one target app is a desktop task
*/
public boolean hasDesktopTasks() {
- if (!isDesktopModeSupported()) {
+ if (!enableDesktopWindowingMode()) {
return false;
}
for (RemoteAnimationTarget target : apps) {
diff --git a/quickstep/src/com/android/quickstep/RemoteTargetGluer.java b/quickstep/src/com/android/quickstep/RemoteTargetGluer.java
index 6a9caf7..ffbb064 100644
--- a/quickstep/src/com/android/quickstep/RemoteTargetGluer.java
+++ b/quickstep/src/com/android/quickstep/RemoteTargetGluer.java
@@ -17,7 +17,7 @@
package com.android.quickstep;
import static com.android.quickstep.util.SplitScreenUtils.convertShellSplitBoundsToLauncher;
-import static com.android.quickstep.views.DesktopTaskView.isDesktopModeSupported;
+import static com.android.window.flags.Flags.enableDesktopWindowingMode;
import static com.android.wm.shell.util.SplitBounds.KEY_EXTRA_SPLIT_BOUNDS;
import android.app.WindowConfiguration;
@@ -68,7 +68,7 @@
* running tasks
*/
public RemoteTargetGluer(Context context, BaseActivityInterface sizingStrategy) {
- if (isDesktopModeSupported()) {
+ if (enableDesktopWindowingMode()) {
DesktopVisibilityController desktopVisibilityController =
LauncherActivityInterface.INSTANCE.getDesktopVisibilityController();
if (desktopVisibilityController != null) {
diff --git a/quickstep/src/com/android/quickstep/TaskShortcutFactory.java b/quickstep/src/com/android/quickstep/TaskShortcutFactory.java
index 54bbd73..c97e62a 100644
--- a/quickstep/src/com/android/quickstep/TaskShortcutFactory.java
+++ b/quickstep/src/com/android/quickstep/TaskShortcutFactory.java
@@ -20,7 +20,7 @@
import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SYSTEM_SHORTCUT_FREE_FORM_TAP;
-import static com.android.quickstep.views.DesktopTaskView.isDesktopModeSupported;
+import static com.android.window.flags.Flags.enableDesktopWindowingMode;
import android.app.Activity;
import android.app.ActivityOptions;
@@ -45,6 +45,7 @@
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.logging.StatsLogManager.LauncherEvent;
import com.android.launcher3.model.WellbeingModel;
+import com.android.launcher3.model.data.ItemInfoWithIcon;
import com.android.launcher3.popup.SystemShortcut;
import com.android.launcher3.popup.SystemShortcut.AppInfo;
import com.android.launcher3.util.InstantAppResolver;
@@ -61,6 +62,7 @@
import com.android.systemui.shared.recents.view.RecentsTransition;
import com.android.systemui.shared.system.ActivityManagerWrapper;
+import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.function.Function;
@@ -319,15 +321,21 @@
recentsView.isTaskInExpectedScrollPosition(recentsView.indexOfChild(taskView));
boolean shouldShowActionsButtonInstead =
isLargeTileFocusedTask && isInExpectedScrollPosition;
+ boolean hasUnpinnableApp = Arrays.stream(taskView.getTaskIdAttributeContainers())
+ .anyMatch(att -> att != null && att.getItemInfo() != null
+ && ((att.getItemInfo().runtimeStatusFlags
+ & ItemInfoWithIcon.FLAG_NOT_PINNABLE) != 0));
// No "save app pair" menu item if:
// - app pairs feature is not enabled
// - we are in 3p launcher
// - the task in question is a single task
+ // - at least one app in app pair is unpinnable
// - the Overview Actions Button should be visible
if (!FeatureFlags.enableAppPairs()
|| !recentsView.supportsAppPairs()
|| !taskView.containsMultipleTasks()
+ || hasUnpinnableApp
|| shouldShowActionsButtonInstead) {
return null;
}
@@ -364,7 +372,7 @@
return Settings.Global.getInt(
activity.getContentResolver(),
Settings.Global.DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT, 0) != 0
- && !isDesktopModeSupported();
+ && !enableDesktopWindowingMode();
}
};
diff --git a/quickstep/src/com/android/quickstep/TaskViewUtils.java b/quickstep/src/com/android/quickstep/TaskViewUtils.java
index 8d4255c..450e960 100644
--- a/quickstep/src/com/android/quickstep/TaskViewUtils.java
+++ b/quickstep/src/com/android/quickstep/TaskViewUtils.java
@@ -38,7 +38,6 @@
import static com.android.launcher3.Utilities.getDescendantCoordRelativeToAncestor;
import static com.android.launcher3.util.MultiPropertyFactory.MULTI_PROPERTY_VALUE;
import static com.android.quickstep.util.AnimUtils.clampToDuration;
-import static com.android.quickstep.views.DesktopTaskView.isDesktopModeSupported;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -183,7 +182,7 @@
// Re-use existing handles
remoteTargetHandles = recentsViewHandles;
} else {
- boolean forDesktop = isDesktopModeSupported() && v instanceof DesktopTaskView;
+ boolean forDesktop = v instanceof DesktopTaskView;
RemoteTargetGluer gluer = new RemoteTargetGluer(v.getContext(),
recentsView.getSizeStrategy(), targets, forDesktop);
if (forDesktop) {
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index 7880124..b43c520 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -31,6 +31,7 @@
import static com.android.launcher3.MotionEventsUtils.isTrackpadMultiFingerSwipe;
import static com.android.launcher3.config.FeatureFlags.ENABLE_TRACKPAD_GESTURE;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
import static com.android.launcher3.util.OnboardingPrefs.HOME_BOUNCE_SEEN;
import static com.android.launcher3.util.window.WindowManagerProxy.MIN_TABLET_WIDTH;
import static com.android.quickstep.GestureState.DEFAULT_STATE;
@@ -59,14 +60,12 @@
import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_STARTING_WINDOW;
import android.app.PendingIntent;
-import android.app.RemoteAction;
import android.app.Service;
import android.content.IIntentReceiver;
import android.content.IIntentSender;
import android.content.Intent;
import android.content.res.Configuration;
import android.graphics.Region;
-import android.graphics.drawable.Icon;
import android.os.Bundle;
import android.os.IBinder;
import android.os.Looper;
@@ -77,7 +76,6 @@
import android.view.InputDevice;
import android.view.InputEvent;
import android.view.MotionEvent;
-import android.view.accessibility.AccessibilityManager;
import androidx.annotation.BinderThread;
import androidx.annotation.NonNull;
@@ -88,7 +86,6 @@
import com.android.launcher3.ConstantItem;
import com.android.launcher3.EncryptionType;
import com.android.launcher3.LauncherPrefs;
-import com.android.launcher3.R;
import com.android.launcher3.anim.AnimatedFloat;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.provider.RestoreDbTask;
@@ -101,7 +98,6 @@
import com.android.launcher3.uioverrides.flags.FlagsFactory;
import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
import com.android.launcher3.util.DisplayController;
-import com.android.launcher3.util.Executors;
import com.android.launcher3.util.LockedUserState;
import com.android.launcher3.util.SafeCloseable;
import com.android.launcher3.util.ScreenOnTracker;
@@ -488,6 +484,7 @@
private TaskbarManager mTaskbarManager;
private Function<GestureState, AnimatedFloat> mSwipeUpProxyProvider = i -> null;
+ private AllAppsActionManager mAllAppsActionManager;
@Override
public void onCreate() {
@@ -497,7 +494,9 @@
mMainChoreographer = Choreographer.getInstance();
mAM = ActivityManagerWrapper.getInstance();
mDeviceState = new RecentsAnimationDeviceState(this, true);
- mTaskbarManager = new TaskbarManager(this);
+ mAllAppsActionManager = new AllAppsActionManager(
+ this, UI_HELPER_EXECUTOR, this::createAllAppsPendingIntent);
+ mTaskbarManager = new TaskbarManager(this, mAllAppsActionManager);
mRotationTouchHelper = mDeviceState.getRotationTouchHelper();
mInputConsumer = InputConsumerController.getRecentsAnimationInputConsumer();
BootAwarePreloader.start(this);
@@ -590,16 +589,7 @@
}
private void onOverviewTargetChange(boolean isHomeAndOverviewSame) {
- Executors.UI_HELPER_EXECUTOR.execute(() -> {
- AccessibilityManager am = getSystemService(AccessibilityManager.class);
-
- if (isHomeAndOverviewSame) {
- am.registerSystemAction(
- createAllAppsAction(), GLOBAL_ACTION_ACCESSIBILITY_ALL_APPS);
- } else {
- am.unregisterSystemAction(GLOBAL_ACTION_ACCESSIBILITY_ALL_APPS);
- }
- });
+ mAllAppsActionManager.setHomeAndOverviewSame(isHomeAndOverviewSame);
StatefulActivity newOverviewActivity = mOverviewComponentObserver.getActivityInterface()
.getCreatedActivity();
@@ -609,13 +599,12 @@
mTISBinder.onOverviewTargetChange();
}
- private RemoteAction createAllAppsAction() {
+ private PendingIntent createAllAppsPendingIntent() {
final Intent homeIntent = new Intent(mOverviewComponentObserver.getHomeIntent())
.setAction(INTENT_ACTION_ALL_APPS_TOGGLE);
- final PendingIntent actionPendingIntent;
if (FeatureFlags.ENABLE_ALL_APPS_SEARCH_IN_TASKBAR.get()) {
- actionPendingIntent = new PendingIntent(new IIntentSender.Stub() {
+ return new PendingIntent(new IIntentSender.Stub() {
@Override
public void send(int code, Intent intent, String resolvedType,
IBinder allowlistToken, IIntentReceiver finishedReceiver,
@@ -624,18 +613,12 @@
}
});
} else {
- actionPendingIntent = PendingIntent.getActivity(
+ return PendingIntent.getActivity(
this,
GLOBAL_ACTION_ACCESSIBILITY_ALL_APPS,
homeIntent,
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
}
-
- return new RemoteAction(
- Icon.createWithResource(this, R.drawable.ic_apps),
- getString(R.string.all_apps_label),
- getString(R.string.all_apps_label),
- actionPendingIntent);
}
@UiThread
@@ -678,8 +661,7 @@
mDeviceState.destroy();
SystemUiProxy.INSTANCE.get(this).clearProxy();
- getSystemService(AccessibilityManager.class)
- .unregisterSystemAction(GLOBAL_ACTION_ACCESSIBILITY_ALL_APPS);
+ mAllAppsActionManager.onDestroy();
mTaskbarManager.destroy();
sConnected = false;
@@ -754,6 +736,10 @@
boolean isOneHandedModeActive = mDeviceState.isOneHandedModeActive();
boolean isInSwipeUpTouchRegion = mRotationTouchHelper.isInSwipeUpTouchRegion(event);
+ TaskbarActivityContext tac = mTaskbarManager.getCurrentActivityContext();
+ if (isInSwipeUpTouchRegion && tac != null) {
+ tac.closeKeyboardQuickSwitchView();
+ }
if ((!isOneHandedModeActive && isInSwipeUpTouchRegion)
|| isHoverActionWithoutConsumer) {
reasonString.append(!isOneHandedModeActive && isInSwipeUpTouchRegion
diff --git a/quickstep/src/com/android/quickstep/fallback/FallbackNavBarTouchController.java b/quickstep/src/com/android/quickstep/fallback/FallbackNavBarTouchController.java
index 8a87f63..69de3b0 100644
--- a/quickstep/src/com/android/quickstep/fallback/FallbackNavBarTouchController.java
+++ b/quickstep/src/com/android/quickstep/fallback/FallbackNavBarTouchController.java
@@ -45,8 +45,7 @@
NavBarPosition navBarPosition = new NavBarPosition(sysUINavigationMode,
DisplayController.INSTANCE.get(mActivity).getInfo());
mTriggerSwipeUpTracker = new TriggerSwipeUpTouchTracker(mActivity,
- true /* disableHorizontalSwipe */, navBarPosition,
- null /* onInterceptTouch */, this);
+ true /* disableHorizontalSwipe */, navBarPosition, this);
} else {
mTriggerSwipeUpTracker = null;
}
@@ -78,7 +77,4 @@
public void onSwipeUp(boolean wasFling, PointF finalVelocity) {
mActivity.<FallbackRecentsView>getOverviewPanel().startHome();
}
-
- @Override
- public void onSwipeUpCancelled() {}
}
diff --git a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsStateController.java b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsStateController.java
index 3e731e5..69eaf6a 100644
--- a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsStateController.java
+++ b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsStateController.java
@@ -49,6 +49,7 @@
import com.android.launcher3.util.MultiPropertyFactory;
import com.android.quickstep.RecentsActivity;
import com.android.quickstep.views.ClearAllButton;
+import com.android.quickstep.views.RecentsView;
/**
* State controller for fallback recents activity
@@ -125,7 +126,7 @@
setter.add(pa.buildAnim());
}
- Pair<FloatProperty, FloatProperty> taskViewsFloat =
+ Pair<FloatProperty<RecentsView>, FloatProperty<RecentsView>> taskViewsFloat =
mRecentsView.getPagedOrientationHandler().getSplitSelectTaskOffset(
TASK_PRIMARY_SPLIT_TRANSLATION, TASK_SECONDARY_SPLIT_TRANSLATION,
mActivity.getDeviceProfile());
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/NavHandleLongPressInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/NavHandleLongPressInputConsumer.java
index cf8750f..e4a8619 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/NavHandleLongPressInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/NavHandleLongPressInputConsumer.java
@@ -22,9 +22,11 @@
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import android.content.Context;
+import android.util.Log;
import android.view.MotionEvent;
import android.view.ViewConfiguration;
+import com.android.launcher3.Utilities;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.logging.StatsLogManager;
import com.android.launcher3.util.DisplayController;
@@ -39,6 +41,8 @@
*/
public class NavHandleLongPressInputConsumer extends DelegateInputConsumer {
+ private static final String TAG = "NavHandleLongPressIC";
+
private final NavHandleLongPressHandler mNavHandleLongPressHandler;
private final float mNavHandleWidth;
private final float mScreenWidth;
@@ -175,6 +179,14 @@
private boolean isInNavBarHorizontalArea(float x) {
float areaFromMiddle = mNavHandleWidth / 2.0f;
+ if (FeatureFlags.CUSTOM_LPNH_THRESHOLDS.get()) {
+ areaFromMiddle += Utilities.dpToPx(FeatureFlags.LPNH_EXTRA_TOUCH_WIDTH_DP.get());
+ }
+ int minAccessibleSize = Utilities.dpToPx(24); // Half of 48dp because this is per side.
+ if (areaFromMiddle < minAccessibleSize) {
+ Log.w(TAG, "Custom nav handle region is too small - resetting to 48dp");
+ areaFromMiddle = minAccessibleSize;
+ }
float distFromMiddle = Math.abs(mScreenWidth / 2.0f - x);
return distFromMiddle < areaFromMiddle;
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/OverviewWithoutFocusInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/OverviewWithoutFocusInputConsumer.java
index 41730bb..42e8694 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/OverviewWithoutFocusInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/OverviewWithoutFocusInputConsumer.java
@@ -51,7 +51,7 @@
mGestureState = gestureState;
mInputMonitor = inputMonitor;
mTriggerSwipeUpTracker = new TriggerSwipeUpTouchTracker(context, disableHorizontalSwipe,
- deviceState.getNavBarPosition(), this::onInterceptTouch, this);
+ deviceState.getNavBarPosition(), this);
}
@Override
@@ -69,7 +69,8 @@
mTriggerSwipeUpTracker.onMotionEvent(ev);
}
- private void onInterceptTouch() {
+ @Override
+ public void onSwipeUpTouchIntercepted() {
if (mInputMonitor != null) {
TestLogging.recordEvent(TestProtocol.SEQUENCE_PILFER, "pilferPointers");
mInputMonitor.pilferPointers();
@@ -93,7 +94,4 @@
.build())
.log(LAUNCHER_HOME_GESTURE);
}
-
- @Override
- public void onSwipeUpCancelled() {}
}
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/SysUiOverlayInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/SysUiOverlayInputConsumer.java
index 4806ac1..871d075 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/SysUiOverlayInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/SysUiOverlayInputConsumer.java
@@ -54,7 +54,7 @@
mContext = context;
mInputMonitor = inputMonitor;
mTriggerSwipeUpTracker = new TriggerSwipeUpTouchTracker(context, true,
- deviceState.getNavBarPosition(), this::onInterceptTouch, this);
+ deviceState.getNavBarPosition(), this);
}
@Override
@@ -72,7 +72,8 @@
mTriggerSwipeUpTracker.onMotionEvent(ev);
}
- private void onInterceptTouch() {
+ @Override
+ public void onSwipeUpTouchIntercepted() {
if (mInputMonitor != null) {
TestLogging.recordEvent(TestProtocol.SEQUENCE_PILFER, "pilferPointers");
mInputMonitor.pilferPointers();
@@ -88,9 +89,4 @@
Log.e(TAG, "Exception calling closeSystemDialogs " + e.getMessage());
}
}
-
- @Override
- public void onSwipeUpCancelled() {
-
- }
}
diff --git a/quickstep/src/com/android/quickstep/interaction/GestureSandboxActivity.java b/quickstep/src/com/android/quickstep/interaction/GestureSandboxActivity.java
index 4198e2d..4f1dbbe 100644
--- a/quickstep/src/com/android/quickstep/interaction/GestureSandboxActivity.java
+++ b/quickstep/src/com/android/quickstep/interaction/GestureSandboxActivity.java
@@ -59,7 +59,6 @@
@Nullable private TutorialType[] mTutorialSteps;
private GestureSandboxFragment mCurrentFragment;
- private GestureSandboxFragment mPendingFragment;
private int mCurrentStep;
private int mNumSteps;
@@ -177,22 +176,16 @@
&& getResources().getConfiguration().orientation
== ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
- GestureSandboxFragment recreatedFragment =
- showRotationPrompt || mPendingFragment == null
- ? null : mPendingFragment.recreateFragment();
showFragment(showRotationPrompt
? new RotationPromptFragment()
- : recreatedFragment == null
- ? mCurrentFragment : recreatedFragment);
+ : mCurrentFragment.canRecreateFragment()
+ ? mCurrentFragment.recreateFragment() : mCurrentFragment);
} else {
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
}
}
private void showFragment(@NonNull GestureSandboxFragment fragment) {
- if (mCurrentFragment.recreateFragment() != null) {
- mPendingFragment = mCurrentFragment;
- }
mCurrentFragment = fragment;
getSupportFragmentManager().beginTransaction()
.replace(R.id.gesture_tutorial_fragment_container, mCurrentFragment)
diff --git a/quickstep/src/com/android/quickstep/interaction/GestureSandboxFragment.java b/quickstep/src/com/android/quickstep/interaction/GestureSandboxFragment.java
index 08f2989..03bdd5d 100644
--- a/quickstep/src/com/android/quickstep/interaction/GestureSandboxFragment.java
+++ b/quickstep/src/com/android/quickstep/interaction/GestureSandboxFragment.java
@@ -28,6 +28,10 @@
void onDetachedFromWindow() {}
+ boolean canRecreateFragment() {
+ return false;
+ }
+
@Nullable
GestureSandboxFragment recreateFragment() {
return null;
diff --git a/quickstep/src/com/android/quickstep/interaction/MenuFragment.java b/quickstep/src/com/android/quickstep/interaction/MenuFragment.java
index dbf141b..8ead3dd 100644
--- a/quickstep/src/com/android/quickstep/interaction/MenuFragment.java
+++ b/quickstep/src/com/android/quickstep/interaction/MenuFragment.java
@@ -39,6 +39,11 @@
}
@Override
+ boolean canRecreateFragment() {
+ return true;
+ }
+
+ @Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
final View root = inflater.inflate(
diff --git a/quickstep/src/com/android/quickstep/interaction/NavBarGestureHandler.java b/quickstep/src/com/android/quickstep/interaction/NavBarGestureHandler.java
index c4a2216..c00f508 100644
--- a/quickstep/src/com/android/quickstep/interaction/NavBarGestureHandler.java
+++ b/quickstep/src/com/android/quickstep/interaction/NavBarGestureHandler.java
@@ -65,7 +65,7 @@
mSwipeUpTouchTracker =
new TriggerSwipeUpTouchTracker(context, true /*disableHorizontalSwipe*/,
new NavBarPosition(NavigationMode.NO_BUTTON, displayInfo),
- null /*onInterceptTouch*/, this);
+ this);
mMotionPauseDetector = new MotionPauseDetector(context);
final Resources resources = context.getResources();
diff --git a/quickstep/src/com/android/quickstep/interaction/TutorialController.java b/quickstep/src/com/android/quickstep/interaction/TutorialController.java
index 545a94d..f89888a 100644
--- a/quickstep/src/com/android/quickstep/interaction/TutorialController.java
+++ b/quickstep/src/com/android/quickstep/interaction/TutorialController.java
@@ -41,6 +41,7 @@
import android.view.ViewGroup;
import android.view.ViewOutlineProvider;
import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityManager;
import android.widget.Button;
import android.widget.FrameLayout;
import android.widget.ImageView;
@@ -87,7 +88,7 @@
private static final int FEEDBACK_ANIMATION_MS = 133;
private static final int RIPPLE_VISIBLE_MS = 300;
private static final int GESTURE_ANIMATION_DELAY_MS = 1500;
- private static final int ADVANCE_TUTORIAL_TIMEOUT_MS = 2000;
+ private static final int ADVANCE_TUTORIAL_TIMEOUT_MS = 3000;
private static final long GESTURE_ANIMATION_PAUSE_DURATION_MILLIS = 1000;
protected float mExitingAppEndingCornerRadius;
protected float mExitingAppStartingCornerRadius;
@@ -209,8 +210,12 @@
mFeedbackView.removeCallbacks(mFeedbackViewCallback);
}
mFeedbackViewCallback = mTutorialFragment::continueTutorial;
- mFeedbackView.postDelayed(mFeedbackViewCallback,
- ADVANCE_TUTORIAL_TIMEOUT_MS);
+ mFeedbackView.postDelayed(
+ mFeedbackViewCallback,
+ AccessibilityManager.getInstance(mContext)
+ .getRecommendedTimeoutMillis(
+ ADVANCE_TUTORIAL_TIMEOUT_MS,
+ AccessibilityManager.FLAG_CONTENT_TEXT));
}
})
.start();
diff --git a/quickstep/src/com/android/quickstep/interaction/TutorialFragment.java b/quickstep/src/com/android/quickstep/interaction/TutorialFragment.java
index c91ee81..0fafb94 100644
--- a/quickstep/src/com/android/quickstep/interaction/TutorialFragment.java
+++ b/quickstep/src/com/android/quickstep/interaction/TutorialFragment.java
@@ -114,6 +114,11 @@
return newInstance(tutorialType, isGestureComplete(), mFromTutorialMenu);
}
+ @Override
+ boolean canRecreateFragment() {
+ return true;
+ }
+
@NonNull
abstract TutorialType getDefaultTutorialType();
diff --git a/quickstep/src/com/android/quickstep/orientation/LandscapePagedViewHandler.java b/quickstep/src/com/android/quickstep/orientation/LandscapePagedViewHandler.java
deleted file mode 100644
index f345aeb..0000000
--- a/quickstep/src/com/android/quickstep/orientation/LandscapePagedViewHandler.java
+++ /dev/null
@@ -1,704 +0,0 @@
-/*
- * 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.orientation;
-
-import static android.view.Gravity.BOTTOM;
-import static android.view.Gravity.CENTER_VERTICAL;
-import static android.view.Gravity.END;
-import static android.view.Gravity.LEFT;
-import static android.view.Gravity.START;
-import static android.view.Gravity.TOP;
-import static android.view.View.LAYOUT_DIRECTION_RTL;
-import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
-import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
-
-import static com.android.launcher3.Flags.enableOverviewIconMenu;
-import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X;
-import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y;
-import static com.android.launcher3.touch.SingleAxisSwipeDetector.HORIZONTAL;
-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.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_UNDEFINED;
-import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_TYPE_MAIN;
-
-import android.content.res.Resources;
-import android.graphics.Point;
-import android.graphics.PointF;
-import android.graphics.Rect;
-import android.graphics.RectF;
-import android.graphics.drawable.ShapeDrawable;
-import android.util.FloatProperty;
-import android.util.Pair;
-import android.view.Gravity;
-import android.view.MotionEvent;
-import android.view.Surface;
-import android.view.VelocityTracker;
-import android.view.View;
-import android.view.accessibility.AccessibilityEvent;
-import android.widget.FrameLayout;
-import android.widget.LinearLayout;
-
-import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
-import com.android.launcher3.touch.SingleAxisSwipeDetector;
-import com.android.launcher3.util.SplitConfigurationOptions;
-import com.android.launcher3.util.SplitConfigurationOptions.SplitBounds;
-import com.android.launcher3.util.SplitConfigurationOptions.SplitPositionOption;
-import com.android.launcher3.util.SplitConfigurationOptions.StagePosition;
-import com.android.launcher3.views.BaseDragLayer;
-import com.android.quickstep.views.IconAppChipView;
-
-import java.util.Collections;
-import java.util.List;
-
-public class LandscapePagedViewHandler implements RecentsPagedOrientationHandler {
-
- @Override
- public <T> T getPrimaryValue(T x, T y) {
- return y;
- }
-
- @Override
- public <T> T getSecondaryValue(T x, T y) {
- return x;
- }
-
- @Override
- public int getPrimaryValue(int x, int y) {
- return y;
- }
-
- @Override
- public int getSecondaryValue(int x, int y) {
- return x;
- }
-
- @Override
- public float getPrimaryValue(float x, float y) {
- return y;
- }
-
- @Override
- public float getSecondaryValue(float x, float y) {
- return x;
- }
-
- @Override
- public boolean isLayoutNaturalToLauncher() {
- return false;
- }
-
- @Override
- public void adjustFloatingIconStartVelocity(PointF velocity) {
- float oldX = velocity.x;
- float oldY = velocity.y;
- velocity.set(-oldY, oldX);
- }
-
- @Override
- public void fixBoundsForHomeAnimStartRect(RectF outStartRect, DeviceProfile deviceProfile) {
- // We don't need to check the "top" value here because the startRect is in the orientation
- // of the app, not of the fixed portrait launcher.
- if (outStartRect.left > deviceProfile.heightPx) {
- outStartRect.offsetTo(0, outStartRect.top);
- } else if (outStartRect.left < -deviceProfile.heightPx) {
- outStartRect.offsetTo(0, outStartRect.top);
- }
- }
-
- @Override
- public <T> void setPrimary(T target, Int2DAction<T> action, int param) {
- action.call(target, 0, param);
- }
-
- @Override
- public <T> void setPrimary(T target, Float2DAction<T> action, float param) {
- action.call(target, 0, param);
- }
-
- @Override
- public <T> void setSecondary(T target, Float2DAction<T> action, float param) {
- action.call(target, param, 0);
- }
-
- @Override
- public <T> void set(T target, Int2DAction<T> action, int primaryParam,
- int secondaryParam) {
- action.call(target, secondaryParam, primaryParam);
- }
-
- @Override
- public float getPrimaryDirection(MotionEvent event, int pointerIndex) {
- return event.getY(pointerIndex);
- }
-
- @Override
- public float getPrimaryVelocity(VelocityTracker velocityTracker, int pointerId) {
- return velocityTracker.getYVelocity(pointerId);
- }
-
- @Override
- public int getMeasuredSize(View view) {
- return view.getMeasuredHeight();
- }
-
- @Override
- public int getPrimarySize(View view) {
- return view.getHeight();
- }
-
- @Override
- public float getPrimarySize(RectF rect) {
- return rect.height();
- }
-
- @Override
- public float getStart(RectF rect) {
- return rect.top;
- }
-
- @Override
- public float getEnd(RectF rect) {
- return rect.bottom;
- }
-
- @Override
- public int getClearAllSidePadding(View view, boolean isRtl) {
- return (isRtl ? view.getPaddingBottom() : - view.getPaddingTop()) / 2;
- }
-
- @Override
- public int getSecondaryDimension(View view) {
- return view.getWidth();
- }
-
- @Override
- public FloatProperty<View> getPrimaryViewTranslate() {
- return VIEW_TRANSLATE_Y;
- }
-
- @Override
- public FloatProperty<View> getSecondaryViewTranslate() {
- return VIEW_TRANSLATE_X;
- }
-
- @Override
- public int getPrimaryScroll(View view) {
- return view.getScrollY();
- }
-
- @Override
- public float getPrimaryScale(View view) {
- return view.getScaleY();
- }
-
- @Override
- public void setMaxScroll(AccessibilityEvent event, int maxScroll) {
- event.setMaxScrollY(maxScroll);
- }
-
- @Override
- public boolean getRecentsRtlSetting(Resources resources) {
- return !Utilities.isRtl(resources);
- }
-
- @Override
- public float getDegreesRotated() {
- return 90;
- }
-
- @Override
- public int getRotation() {
- return Surface.ROTATION_90;
- }
-
- @Override
- public void setPrimaryScale(View view, float scale) {
- view.setScaleY(scale);
- }
-
- @Override
- public void setSecondaryScale(View view, float scale) {
- view.setScaleX(scale);
- }
-
- @Override
- public int getChildStart(View view) {
- return view.getTop();
- }
-
- @Override
- public int getCenterForPage(View view, Rect insets) {
- return (view.getPaddingLeft() + view.getMeasuredWidth() + insets.left
- - insets.right - view.getPaddingRight()) / 2;
- }
-
- @Override
- public int getScrollOffsetStart(View view, Rect insets) {
- return insets.top + view.getPaddingTop();
- }
-
- @Override
- public int getScrollOffsetEnd(View view, Rect insets) {
- return view.getHeight() - view.getPaddingBottom() - insets.bottom;
- }
-
- public int getSecondaryTranslationDirectionFactor() {
- return 1;
- }
-
- @Override
- public int getSplitTranslationDirectionFactor(int stagePosition, DeviceProfile deviceProfile) {
- if (stagePosition == STAGE_POSITION_BOTTOM_OR_RIGHT) {
- return -1;
- } else {
- return 1;
- }
- }
-
- @Override
- public float getTaskMenuX(float x, View thumbnailView,
- DeviceProfile deviceProfile, float taskInsetMargin, View taskViewIcon) {
- return thumbnailView.getMeasuredWidth() + x - taskInsetMargin;
- }
-
- @Override
- public float getTaskMenuY(float y, View thumbnailView, int stagePosition,
- View taskMenuView, float taskInsetMargin, View taskViewIcon) {
- BaseDragLayer.LayoutParams lp = (BaseDragLayer.LayoutParams) taskMenuView.getLayoutParams();
- int taskMenuWidth = lp.width;
- if (stagePosition == STAGE_POSITION_UNDEFINED) {
- return y + taskInsetMargin
- + (thumbnailView.getMeasuredHeight() - taskMenuWidth) / 2f;
- } else {
- return y + taskInsetMargin;
- }
- }
-
- @Override
- public int getTaskMenuWidth(View thumbnailView, DeviceProfile deviceProfile,
- @StagePosition int stagePosition) {
- if (enableOverviewIconMenu()) {
- return thumbnailView.getResources().getDimensionPixelSize(
- R.dimen.task_thumbnail_icon_menu_expanded_width);
- }
- if (stagePosition == SplitConfigurationOptions.STAGE_POSITION_UNDEFINED) {
- return thumbnailView.getMeasuredWidth();
- } else {
- return thumbnailView.getMeasuredHeight();
- }
- }
-
- @Override
- public int getTaskMenuHeight(float taskInsetMargin, DeviceProfile deviceProfile,
- float taskMenuX, float taskMenuY) {
- return (int) (taskMenuX - taskInsetMargin);
- }
-
- @Override
- public void setTaskOptionsMenuLayoutOrientation(DeviceProfile deviceProfile,
- LinearLayout taskMenuLayout, int dividerSpacing,
- ShapeDrawable dividerDrawable) {
- taskMenuLayout.setOrientation(LinearLayout.VERTICAL);
- dividerDrawable.setIntrinsicHeight(dividerSpacing);
- taskMenuLayout.setDividerDrawable(dividerDrawable);
- }
-
- @Override
- public void setLayoutParamsForTaskMenuOptionItem(LinearLayout.LayoutParams lp,
- LinearLayout viewGroup, DeviceProfile deviceProfile) {
- // Phone fake landscape
- viewGroup.setOrientation(LinearLayout.HORIZONTAL);
- lp.width = MATCH_PARENT;
- lp.height = WRAP_CONTENT;
- }
-
- @Override
- public Pair<Float, Float> getDwbLayoutTranslations(int taskViewWidth,
- int taskViewHeight, SplitBounds splitBounds, DeviceProfile deviceProfile,
- View[] thumbnailViews, int desiredTaskId, View banner) {
- boolean isRtl = banner.getLayoutDirection() == LAYOUT_DIRECTION_RTL;
- float translationX = 0;
- float translationY = 0;
- FrameLayout.LayoutParams bannerParams = (FrameLayout.LayoutParams) banner.getLayoutParams();
- banner.setPivotX(0);
- banner.setPivotY(0);
- banner.setRotation(getDegreesRotated());
- translationX = banner.getHeight();
- FrameLayout.LayoutParams snapshotParams =
- (FrameLayout.LayoutParams) thumbnailViews[0]
- .getLayoutParams();
- bannerParams.gravity = TOP | (isRtl ? END : START);
- if (splitBounds == null) {
- // Single, fullscreen case
- bannerParams.width = taskViewHeight - snapshotParams.topMargin;
- return new Pair<>(translationX, Integer.valueOf(snapshotParams.topMargin).floatValue());
- }
-
- // Set correct width
- if (desiredTaskId == splitBounds.leftTopTaskId) {
- bannerParams.width = thumbnailViews[0].getMeasuredHeight();
- } else {
- bannerParams.width = thumbnailViews[1].getMeasuredHeight();
- }
-
- // Set translations
- if (desiredTaskId == splitBounds.rightBottomTaskId) {
- float topLeftTaskPlusDividerPercent = splitBounds.appsStackedVertically
- ? (splitBounds.topTaskPercent + splitBounds.dividerHeightPercent)
- : (splitBounds.leftTaskPercent + splitBounds.dividerWidthPercent);
- translationY = snapshotParams.topMargin
- + ((taskViewHeight - snapshotParams.topMargin) * topLeftTaskPlusDividerPercent);
- }
- if (desiredTaskId == splitBounds.leftTopTaskId) {
- translationY = snapshotParams.topMargin;
- }
- return new Pair<>(translationX, translationY);
- }
-
- /* ---------- The following are only used by TaskViewTouchHandler. ---------- */
-
- @Override
- public SingleAxisSwipeDetector.Direction getUpDownSwipeDirection() {
- return HORIZONTAL;
- }
-
- @Override
- public int getUpDirection(boolean isRtl) {
- return isRtl ? SingleAxisSwipeDetector.DIRECTION_NEGATIVE
- : SingleAxisSwipeDetector.DIRECTION_POSITIVE;
- }
-
- @Override
- public boolean isGoingUp(float displacement, boolean isRtl) {
- return isRtl ? displacement < 0 : displacement > 0;
- }
-
- @Override
- public int getTaskDragDisplacementFactor(boolean isRtl) {
- return isRtl ? 1 : -1;
- }
-
- /* -------------------- */
-
- @Override
- public ChildBounds getChildBounds(View child, int childStart, int pageCenter,
- boolean layoutChild) {
- final int childHeight = child.getMeasuredHeight();
- final int childBottom = childStart + childHeight;
- final int childWidth = child.getMeasuredWidth();
- final int childLeft = pageCenter - childWidth/ 2;
- if (layoutChild) {
- child.layout(childLeft, childStart, childLeft + childWidth, childBottom);
- }
- return new ChildBounds(childHeight, childWidth, childBottom, childLeft);
- }
-
- @SuppressWarnings("SuspiciousNameCombination")
- @Override
- public int getDistanceToBottomOfRect(DeviceProfile dp, Rect rect) {
- return rect.left;
- }
-
- @Override
- public List<SplitPositionOption> getSplitPositionOptions(DeviceProfile dp) {
- // Add "left" side of phone which is actually the top
- return Collections.singletonList(new SplitPositionOption(
- R.drawable.ic_split_horizontal, R.string.recent_task_option_split_screen,
- STAGE_POSITION_TOP_OR_LEFT, STAGE_TYPE_MAIN));
- }
-
- @Override
- public void getInitialSplitPlaceholderBounds(int placeholderHeight, int placeholderInset,
- DeviceProfile dp, @StagePosition int stagePosition, Rect out) {
- // In fake land/seascape, the placeholder always needs to go to the "top" of the device,
- // which is the same bounds as 0 rotation.
- int width = dp.widthPx;
- int insetSizeAdjustment = getPlaceholderSizeAdjustment(dp);
- out.set(0, 0, width, placeholderHeight + insetSizeAdjustment);
- out.inset(placeholderInset, 0);
-
- // Adjust the top to account for content off screen. This will help to animate the view in
- // with rounded corners.
- int screenWidth = dp.widthPx;
- int screenHeight = dp.heightPx;
- int totalHeight = (int) (1.0f * screenHeight / 2 * (screenWidth - 2 * placeholderInset)
- / screenWidth);
- out.top -= (totalHeight - placeholderHeight);
- }
-
- @Override
- public void updateSplitIconParams(View out, float onScreenRectCenterX,
- float onScreenRectCenterY, float fullscreenScaleX, float fullscreenScaleY,
- int drawableWidth, int drawableHeight, DeviceProfile dp,
- @StagePosition int stagePosition) {
- float insetAdjustment = getPlaceholderSizeAdjustment(dp) / 2f;
- out.setX(onScreenRectCenterX / fullscreenScaleX
- - 1.0f * drawableWidth / 2);
- out.setY((onScreenRectCenterY + insetAdjustment) / fullscreenScaleY
- - 1.0f * drawableHeight / 2);
- }
-
- /**
- * The split placeholder comes with a default inset to buffer the icon from the top of the
- * screen. But if the device already has a large inset (from cutouts etc), use that instead.
- */
- private int getPlaceholderSizeAdjustment(DeviceProfile dp) {
- return Math.max(dp.getInsets().top - dp.splitPlaceholderInset, 0);
- }
-
- @Override
- public void setSplitInstructionsParams(View out, DeviceProfile dp, int splitInstructionsHeight,
- int splitInstructionsWidth) {
- out.setPivotX(0);
- out.setPivotY(splitInstructionsHeight);
- out.setRotation(getDegreesRotated());
- int distanceToEdge = out.getResources().getDimensionPixelSize(
- R.dimen.split_instructions_bottom_margin_phone_landscape);
- // Adjust for any insets on the left edge
- int insetCorrectionX = dp.getInsets().left;
- // Center the view in case of unbalanced insets on top or bottom of screen
- int insetCorrectionY = (dp.getInsets().bottom - dp.getInsets().top) / 2;
- out.setTranslationX(distanceToEdge - insetCorrectionX);
- out.setTranslationY(((-splitInstructionsHeight - splitInstructionsWidth) / 2f)
- + insetCorrectionY);
- FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) out.getLayoutParams();
- // Setting gravity to LEFT instead of the lint-recommended START because we always want this
- // view to be screen-left when phone is in landscape, regardless of the RtL setting.
- lp.gravity = LEFT | CENTER_VERTICAL;
- out.setLayoutParams(lp);
- }
-
- @Override
- public void getFinalSplitPlaceholderBounds(int splitDividerSize, DeviceProfile dp,
- @StagePosition int stagePosition, Rect out1, Rect out2) {
- // In fake land/seascape, the window bounds are always top and bottom half
- int screenHeight = dp.heightPx;
- int screenWidth = dp.widthPx;
- out1.set(0, 0, screenWidth, screenHeight / 2 - splitDividerSize);
- out2.set(0, screenHeight / 2 + splitDividerSize, screenWidth, screenHeight);
- }
-
- @Override
- public void setSplitTaskSwipeRect(DeviceProfile dp, Rect outRect,
- SplitBounds splitInfo, int desiredStagePosition) {
- float topLeftTaskPercent = splitInfo.appsStackedVertically
- ? splitInfo.topTaskPercent
- : splitInfo.leftTaskPercent;
- float dividerBarPercent = splitInfo.appsStackedVertically
- ? splitInfo.dividerHeightPercent
- : splitInfo.dividerWidthPercent;
-
- if (desiredStagePosition == SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT) {
- outRect.bottom = outRect.top + (int) (outRect.height() * topLeftTaskPercent);
- } else {
- outRect.top += (int) (outRect.height() * (topLeftTaskPercent + dividerBarPercent));
- }
- }
-
- @Override
- public void measureGroupedTaskViewThumbnailBounds(View primarySnapshot, View secondarySnapshot,
- int parentWidth, int parentHeight, SplitBounds splitBoundsConfig, DeviceProfile dp,
- boolean isRtl) {
- FrameLayout.LayoutParams primaryParams =
- (FrameLayout.LayoutParams) primarySnapshot.getLayoutParams();
- FrameLayout.LayoutParams secondaryParams =
- (FrameLayout.LayoutParams) secondarySnapshot.getLayoutParams();
-
- // Swap the margins that are set in TaskView#setRecentsOrientedState()
- secondaryParams.topMargin = dp.overviewTaskThumbnailTopMarginPx;
- primaryParams.topMargin = 0;
-
- // Measure and layout the thumbnails bottom up, since the primary is on the visual left
- // (portrait bottom) and secondary is on the right (portrait top)
- int spaceAboveSnapshot = dp.overviewTaskThumbnailTopMarginPx;
- int totalThumbnailHeight = parentHeight - spaceAboveSnapshot;
- int dividerBar = Math.round(totalThumbnailHeight * (splitBoundsConfig.appsStackedVertically
- ? splitBoundsConfig.dividerHeightPercent
- : splitBoundsConfig.dividerWidthPercent));
-
- Pair<Point, Point> taskViewSizes =
- getGroupedTaskViewSizes(dp, splitBoundsConfig, parentWidth, parentHeight);
-
- int translationY = taskViewSizes.first.y + spaceAboveSnapshot + dividerBar;
- primarySnapshot.setTranslationY(spaceAboveSnapshot);
- secondarySnapshot.setTranslationY(translationY - spaceAboveSnapshot);
-
- primarySnapshot.measure(
- View.MeasureSpec.makeMeasureSpec(taskViewSizes.first.x, View.MeasureSpec.EXACTLY),
- View.MeasureSpec.makeMeasureSpec(taskViewSizes.first.y, View.MeasureSpec.EXACTLY)
- );
- secondarySnapshot.measure(
- View.MeasureSpec.makeMeasureSpec(taskViewSizes.second.x, View.MeasureSpec.EXACTLY),
- View.MeasureSpec.makeMeasureSpec(taskViewSizes.second.y, View.MeasureSpec.EXACTLY)
- );
- }
-
- @Override
- public Pair<Point, Point> getGroupedTaskViewSizes(
- DeviceProfile dp,
- SplitBounds splitBoundsConfig,
- int parentWidth,
- int parentHeight) {
- int spaceAboveSnapshot = dp.overviewTaskThumbnailTopMarginPx;
- int totalThumbnailHeight = parentHeight - spaceAboveSnapshot;
- int dividerBar = Math.round(totalThumbnailHeight * (splitBoundsConfig.appsStackedVertically
- ? splitBoundsConfig.dividerHeightPercent
- : splitBoundsConfig.dividerWidthPercent));
- float taskPercent = splitBoundsConfig.appsStackedVertically
- ? splitBoundsConfig.topTaskPercent
- : splitBoundsConfig.leftTaskPercent;
-
- Point firstTaskViewSize = new Point(
- parentWidth,
- (int) (totalThumbnailHeight * taskPercent)
- );
- Point secondTaskViewSize = new Point(
- parentWidth,
- totalThumbnailHeight - firstTaskViewSize.y - dividerBar
- );
-
- return new Pair<>(firstTaskViewSize, secondTaskViewSize);
- }
-
- @Override
- public void setTaskIconParams(FrameLayout.LayoutParams iconParams, int taskIconMargin,
- int taskIconHeight, int thumbnailTopMargin, boolean isRtl) {
- iconParams.gravity = (isRtl ? START : END) | CENTER_VERTICAL;
- iconParams.rightMargin = -taskIconHeight - taskIconMargin / 2;
- iconParams.leftMargin = 0;
- iconParams.topMargin = thumbnailTopMargin / 2;
- iconParams.bottomMargin = 0;
- }
-
- @Override
- public void setIconAppChipChildrenParams(FrameLayout.LayoutParams iconParams,
- int chipChildMarginStart) {
- iconParams.gravity = Gravity.START | Gravity.CENTER_VERTICAL;
- iconParams.setMarginStart(chipChildMarginStart);
- iconParams.topMargin = 0;
- }
-
- @Override
- public void setIconAppChipMenuParams(IconAppChipView iconAppChipView,
- FrameLayout.LayoutParams iconMenuParams, int iconMenuMargin, int thumbnailTopMargin) {
- boolean isRtl = iconAppChipView.getLayoutDirection() == LAYOUT_DIRECTION_RTL;
- iconMenuParams.gravity = (isRtl ? START : END) | (isRtl ? BOTTOM : TOP);
- iconMenuParams.setMarginStart(isRtl ? iconMenuMargin : 0);
- iconMenuParams.topMargin = iconMenuMargin;
- iconMenuParams.bottomMargin = isRtl ? iconMenuMargin : 0;
- iconMenuParams.setMarginEnd(iconMenuMargin);
-
- iconAppChipView.setPivotX(isRtl ? iconMenuParams.width - (iconMenuParams.height / 2f)
- : iconMenuParams.width / 2f);
- iconAppChipView.setPivotY(
- isRtl ? (iconMenuParams.height / 2f) : iconMenuParams.width / 2f);
- iconAppChipView.setSplitTranslationY(0);
- iconAppChipView.setRotation(getDegreesRotated());
- }
-
- @Override
- public void setSplitIconParams(View primaryIconView, View secondaryIconView,
- int taskIconHeight, int primarySnapshotWidth, int primarySnapshotHeight,
- int groupedTaskViewHeight, int groupedTaskViewWidth, boolean isRtl,
- DeviceProfile deviceProfile, SplitBounds splitConfig) {
- FrameLayout.LayoutParams primaryIconParams =
- (FrameLayout.LayoutParams) primaryIconView.getLayoutParams();
- FrameLayout.LayoutParams secondaryIconParams = enableOverviewIconMenu()
- ? (FrameLayout.LayoutParams) secondaryIconView.getLayoutParams()
- : new FrameLayout.LayoutParams(primaryIconParams);
-
- // We calculate the "midpoint" of the thumbnail area, and place the icons there.
- // This is the place where the thumbnail area splits by default, in a near-50/50 split.
- // It is usually not exactly 50/50, due to insets/screen cutouts.
- int fullscreenInsetThickness = deviceProfile.getInsets().top
- - deviceProfile.getInsets().bottom;
- int fullscreenMidpointFromBottom = ((deviceProfile.heightPx - fullscreenInsetThickness)
- / 2);
- float midpointFromBottomPct = (float) fullscreenMidpointFromBottom / deviceProfile.heightPx;
- float insetPct = (float) fullscreenInsetThickness / deviceProfile.heightPx;
- int spaceAboveSnapshots = deviceProfile.overviewTaskThumbnailTopMarginPx;
- int overviewThumbnailAreaThickness = groupedTaskViewHeight - spaceAboveSnapshots;
- int bottomToMidpointOffset = (int) (overviewThumbnailAreaThickness * midpointFromBottomPct);
- int insetOffset = (int) (overviewThumbnailAreaThickness * insetPct);
-
- if (enableOverviewIconMenu()) {
- primaryIconParams.gravity = isRtl ? BOTTOM | START : TOP | END;
- secondaryIconParams.gravity = isRtl ? BOTTOM | START : TOP | END;
- } else {
- primaryIconParams.gravity = BOTTOM | (isRtl ? START : END);
- secondaryIconParams.gravity = BOTTOM | (isRtl ? START : END);
- }
- primaryIconView.setTranslationX(0);
- secondaryIconView.setTranslationX(0);
- if (enableOverviewIconMenu()) {
- IconAppChipView primaryAppChipView = (IconAppChipView) primaryIconView;
- IconAppChipView secondaryAppChipView = (IconAppChipView) secondaryIconView;
- if (primaryIconView.getLayoutDirection() == LAYOUT_DIRECTION_RTL) {
- secondaryAppChipView.setSplitTranslationY(-primarySnapshotHeight);
- primaryAppChipView.setSplitTranslationY(0);
- } else {
- int secondarySnapshotHeight = groupedTaskViewHeight - primarySnapshotHeight;
- primaryAppChipView.setSplitTranslationY(secondarySnapshotHeight);
- }
- } else if (splitConfig.initiatedFromSeascape) {
- // if the split was initiated from seascape,
- // the task on the right (secondary) is slightly larger
- primaryIconView.setTranslationY(-bottomToMidpointOffset - insetOffset);
- secondaryIconView.setTranslationY(-bottomToMidpointOffset - insetOffset
- + taskIconHeight);
- } else {
- // if not,
- // the task on the left (primary) is slightly larger
- primaryIconView.setTranslationY(-bottomToMidpointOffset);
- secondaryIconView.setTranslationY(-bottomToMidpointOffset + taskIconHeight);
- }
-
- primaryIconView.setLayoutParams(primaryIconParams);
- secondaryIconView.setLayoutParams(secondaryIconParams);
- }
-
- @Override
- public int getDefaultSplitPosition(DeviceProfile deviceProfile) {
- throw new IllegalStateException("Default position not available in fake landscape");
- }
-
- @Override
- public Pair<FloatProperty, FloatProperty> getSplitSelectTaskOffset(FloatProperty primary,
- FloatProperty secondary, DeviceProfile deviceProfile) {
- return new Pair<>(primary, secondary);
- }
-
- @Override
- public float getFloatingTaskOffscreenTranslationTarget(View floatingTask, RectF onScreenRect,
- @StagePosition int stagePosition, DeviceProfile dp) {
- float currentTranslationY = floatingTask.getTranslationY();
- return currentTranslationY - onScreenRect.height();
- }
-
- @Override
- public void setFloatingTaskPrimaryTranslation(View floatingTask, float translation,
- DeviceProfile dp) {
- floatingTask.setTranslationY(translation);
- }
-
- @Override
- public Float getFloatingTaskPrimaryTranslation(View floatingTask, DeviceProfile dp) {
- return floatingTask.getTranslationY();
- }
-}
diff --git a/quickstep/src/com/android/quickstep/orientation/LandscapePagedViewHandler.kt b/quickstep/src/com/android/quickstep/orientation/LandscapePagedViewHandler.kt
new file mode 100644
index 0000000..1640104
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/orientation/LandscapePagedViewHandler.kt
@@ -0,0 +1,700 @@
+/*
+ * 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.orientation
+
+import android.annotation.SuppressLint
+import android.content.res.Resources
+import android.graphics.Point
+import android.graphics.PointF
+import android.graphics.Rect
+import android.graphics.RectF
+import android.graphics.drawable.ShapeDrawable
+import android.util.FloatProperty
+import android.util.Pair
+import android.view.Gravity
+import android.view.MotionEvent
+import android.view.Surface
+import android.view.VelocityTracker
+import android.view.View
+import android.view.View.MeasureSpec
+import android.view.ViewGroup
+import android.view.accessibility.AccessibilityEvent
+import android.widget.FrameLayout
+import android.widget.LinearLayout
+import androidx.annotation.VisibleForTesting
+import androidx.core.util.component1
+import androidx.core.util.component2
+import com.android.launcher3.DeviceProfile
+import com.android.launcher3.Flags
+import com.android.launcher3.LauncherAnimUtils
+import com.android.launcher3.R
+import com.android.launcher3.Utilities
+import com.android.launcher3.touch.PagedOrientationHandler.ChildBounds
+import com.android.launcher3.touch.PagedOrientationHandler.Float2DAction
+import com.android.launcher3.touch.PagedOrientationHandler.Int2DAction
+import com.android.launcher3.touch.SingleAxisSwipeDetector
+import com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT
+import com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT
+import com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_UNDEFINED
+import com.android.launcher3.util.SplitConfigurationOptions.STAGE_TYPE_MAIN
+import com.android.launcher3.util.SplitConfigurationOptions.SplitBounds
+import com.android.launcher3.util.SplitConfigurationOptions.SplitPositionOption
+import com.android.launcher3.util.SplitConfigurationOptions.StagePosition
+import com.android.launcher3.views.BaseDragLayer
+import com.android.quickstep.views.IconAppChipView
+import kotlin.math.max
+
+open class LandscapePagedViewHandler : RecentsPagedOrientationHandler {
+ override fun <T> getPrimaryValue(x: T, y: T): T = y
+
+ override fun <T> getSecondaryValue(x: T, y: T): T = x
+
+ override fun getPrimaryValue(x: Int, y: Int): Int = y
+
+ override fun getSecondaryValue(x: Int, y: Int): Int = x
+
+ override fun getPrimaryValue(x: Float, y: Float): Float = y
+
+ override fun getSecondaryValue(x: Float, y: Float): Float = x
+
+ override val isLayoutNaturalToLauncher: Boolean = false
+
+ override fun adjustFloatingIconStartVelocity(velocity: PointF) {
+ val oldX = velocity.x
+ val oldY = velocity.y
+ velocity.set(-oldY, oldX)
+ }
+
+ override fun fixBoundsForHomeAnimStartRect(outStartRect: RectF, deviceProfile: DeviceProfile) {
+ // We don't need to check the "top" value here because the startRect is in the orientation
+ // of the app, not of the fixed portrait launcher.
+ if (outStartRect.left > deviceProfile.heightPx) {
+ outStartRect.offsetTo(0f, outStartRect.top)
+ } else if (outStartRect.left < -deviceProfile.heightPx) {
+ outStartRect.offsetTo(0f, outStartRect.top)
+ }
+ }
+
+ override fun <T> setPrimary(target: T, action: Int2DAction<T>, param: Int) =
+ action.call(target, 0, param)
+
+ override fun <T> setPrimary(target: T, action: Float2DAction<T>, param: Float) =
+ action.call(target, 0f, param)
+
+ override fun <T> setSecondary(target: T, action: Float2DAction<T>, param: Float) =
+ action.call(target, param, 0f)
+
+ override fun <T> set(
+ target: T,
+ action: Int2DAction<T>,
+ primaryParam: Int,
+ secondaryParam: Int
+ ) = action.call(target, secondaryParam, primaryParam)
+
+ override fun getPrimaryDirection(event: MotionEvent, pointerIndex: Int): Float =
+ event.getY(pointerIndex)
+
+ override fun getPrimaryVelocity(velocityTracker: VelocityTracker, pointerId: Int): Float =
+ velocityTracker.getYVelocity(pointerId)
+
+ override fun getMeasuredSize(view: View): Int = view.measuredHeight
+
+ override fun getPrimarySize(view: View): Int = view.height
+
+ override fun getPrimarySize(rect: RectF): Float = rect.height()
+
+ override fun getStart(rect: RectF): Float = rect.top
+
+ override fun getEnd(rect: RectF): Float = rect.bottom
+
+ override fun getClearAllSidePadding(view: View, isRtl: Boolean): Int =
+ if (isRtl) view.paddingBottom / 2 else -view.paddingTop / 2
+
+ override fun getSecondaryDimension(view: View): Int = view.width
+
+ override val primaryViewTranslate: FloatProperty<View> = LauncherAnimUtils.VIEW_TRANSLATE_Y
+
+ override val secondaryViewTranslate: FloatProperty<View> = LauncherAnimUtils.VIEW_TRANSLATE_X
+
+ override fun getPrimaryScroll(view: View): Int = view.scrollY
+
+ override fun getPrimaryScale(view: View): Float = view.scaleY
+
+ override fun setMaxScroll(event: AccessibilityEvent, maxScroll: Int) {
+ event.maxScrollY = maxScroll
+ }
+
+ override fun getRecentsRtlSetting(resources: Resources): Boolean = !Utilities.isRtl(resources)
+
+ override val degreesRotated: Float = 90f
+
+ override val rotation: Int = Surface.ROTATION_90
+
+ override fun setPrimaryScale(view: View, scale: Float) {
+ view.scaleY = scale
+ }
+
+ override fun setSecondaryScale(view: View, scale: Float) {
+ view.scaleX = scale
+ }
+
+ override fun getChildStart(view: View): Int = view.top
+
+ override fun getCenterForPage(view: View, insets: Rect): Int =
+ (view.paddingLeft + view.measuredWidth + insets.left - insets.right - view.paddingRight) / 2
+
+ override fun getScrollOffsetStart(view: View, insets: Rect): Int = insets.top + view.paddingTop
+
+ override fun getScrollOffsetEnd(view: View, insets: Rect): Int =
+ view.height - view.paddingBottom - insets.bottom
+
+ override val secondaryTranslationDirectionFactor: Int = 1
+
+ override fun getSplitTranslationDirectionFactor(
+ stagePosition: Int,
+ deviceProfile: DeviceProfile
+ ): Int = if (stagePosition == STAGE_POSITION_BOTTOM_OR_RIGHT) -1 else 1
+
+ override fun getTaskMenuX(
+ x: Float,
+ thumbnailView: View,
+ deviceProfile: DeviceProfile,
+ taskInsetMargin: Float,
+ taskViewIcon: View
+ ): Float = thumbnailView.measuredWidth + x - taskInsetMargin
+
+ override fun getTaskMenuY(
+ y: Float,
+ thumbnailView: View,
+ stagePosition: Int,
+ taskMenuView: View,
+ taskInsetMargin: Float,
+ taskViewIcon: View
+ ): Float {
+ val layoutParams = taskMenuView.layoutParams as BaseDragLayer.LayoutParams
+ var taskMenuY = y + taskInsetMargin
+
+ if (stagePosition == STAGE_POSITION_UNDEFINED) {
+ taskMenuY += (thumbnailView.measuredHeight - layoutParams.width) / 2f
+ }
+
+ return taskMenuY
+ }
+
+ override fun getTaskMenuWidth(
+ thumbnailView: View,
+ deviceProfile: DeviceProfile,
+ @StagePosition stagePosition: Int
+ ): Int =
+ when {
+ Flags.enableOverviewIconMenu() ->
+ thumbnailView.resources.getDimensionPixelSize(
+ R.dimen.task_thumbnail_icon_menu_expanded_width
+ )
+ stagePosition == STAGE_POSITION_UNDEFINED -> thumbnailView.measuredWidth
+ else -> thumbnailView.measuredHeight
+ }
+
+ override fun getTaskMenuHeight(
+ taskInsetMargin: Float,
+ deviceProfile: DeviceProfile,
+ taskMenuX: Float,
+ taskMenuY: Float
+ ): Int = (taskMenuX - taskInsetMargin).toInt()
+
+ override fun setTaskOptionsMenuLayoutOrientation(
+ deviceProfile: DeviceProfile,
+ taskMenuLayout: LinearLayout,
+ dividerSpacing: Int,
+ dividerDrawable: ShapeDrawable
+ ) {
+ taskMenuLayout.orientation = LinearLayout.VERTICAL
+ dividerDrawable.intrinsicHeight = dividerSpacing
+ taskMenuLayout.dividerDrawable = dividerDrawable
+ }
+
+ override fun setLayoutParamsForTaskMenuOptionItem(
+ lp: LinearLayout.LayoutParams,
+ viewGroup: LinearLayout,
+ deviceProfile: DeviceProfile
+ ) {
+ // Phone fake landscape
+ viewGroup.orientation = LinearLayout.HORIZONTAL
+ lp.width = ViewGroup.LayoutParams.MATCH_PARENT
+ lp.height = ViewGroup.LayoutParams.WRAP_CONTENT
+ }
+
+ override fun getDwbLayoutTranslations(
+ taskViewWidth: Int,
+ taskViewHeight: Int,
+ splitBounds: SplitBounds?,
+ deviceProfile: DeviceProfile,
+ thumbnailViews: Array<View>,
+ desiredTaskId: Int,
+ banner: View
+ ): Pair<Float, Float> {
+ val snapshotParams = thumbnailViews[0].layoutParams as FrameLayout.LayoutParams
+ val isRtl = banner.layoutDirection == View.LAYOUT_DIRECTION_RTL
+ val translationX = banner.height.toFloat()
+
+ val bannerParams = banner.layoutParams as FrameLayout.LayoutParams
+ bannerParams.gravity = Gravity.TOP or if (isRtl) Gravity.END else Gravity.START
+ banner.pivotX = 0f
+ banner.pivotY = 0f
+ banner.rotation = degreesRotated
+
+ if (splitBounds == null) {
+ // Single, fullscreen case
+ bannerParams.width = taskViewHeight - snapshotParams.topMargin
+ return Pair(translationX, snapshotParams.topMargin.toFloat())
+ }
+
+ // Set correct width and translations
+ val translationY: Float
+ if (desiredTaskId == splitBounds.leftTopTaskId) {
+ bannerParams.width = thumbnailViews[0].measuredHeight
+ translationY = snapshotParams.topMargin.toFloat()
+ } else {
+ bannerParams.width = thumbnailViews[1].measuredHeight
+ val topLeftTaskPlusDividerPercent =
+ if (splitBounds.appsStackedVertically) {
+ splitBounds.topTaskPercent + splitBounds.dividerHeightPercent
+ } else {
+ splitBounds.leftTaskPercent + splitBounds.dividerWidthPercent
+ }
+ translationY =
+ snapshotParams.topMargin +
+ (taskViewHeight - snapshotParams.topMargin) * topLeftTaskPlusDividerPercent
+ }
+
+ return Pair(translationX, translationY)
+ }
+
+ /* ---------- The following are only used by TaskViewTouchHandler. ---------- */
+ override val upDownSwipeDirection: SingleAxisSwipeDetector.Direction =
+ SingleAxisSwipeDetector.HORIZONTAL
+
+ override fun getUpDirection(isRtl: Boolean): Int =
+ if (isRtl) SingleAxisSwipeDetector.DIRECTION_NEGATIVE
+ else SingleAxisSwipeDetector.DIRECTION_POSITIVE
+
+ override fun isGoingUp(displacement: Float, isRtl: Boolean): Boolean =
+ if (isRtl) displacement < 0 else displacement > 0
+
+ override fun getTaskDragDisplacementFactor(isRtl: Boolean): Int = if (isRtl) 1 else -1
+ /* -------------------- */
+
+ override fun getChildBounds(
+ child: View,
+ childStart: Int,
+ pageCenter: Int,
+ layoutChild: Boolean
+ ): ChildBounds {
+ val childHeight = child.measuredHeight
+ val childWidth = child.measuredWidth
+ val childBottom = childStart + childHeight
+ val childLeft = pageCenter - childWidth / 2
+ if (layoutChild) {
+ child.layout(childLeft, childStart, childLeft + childWidth, childBottom)
+ }
+ return ChildBounds(childHeight, childWidth, childBottom, childLeft)
+ }
+
+ override fun getDistanceToBottomOfRect(dp: DeviceProfile, rect: Rect): Int = rect.left
+
+ override fun getSplitPositionOptions(dp: DeviceProfile): List<SplitPositionOption> =
+ // Add "left" side of phone which is actually the top
+ listOf(
+ SplitPositionOption(
+ R.drawable.ic_split_horizontal,
+ R.string.recent_task_option_split_screen,
+ STAGE_POSITION_TOP_OR_LEFT,
+ STAGE_TYPE_MAIN
+ )
+ )
+
+ override fun getInitialSplitPlaceholderBounds(
+ placeholderHeight: Int,
+ placeholderInset: Int,
+ dp: DeviceProfile,
+ @StagePosition stagePosition: Int,
+ out: Rect
+ ) {
+ // In fake land/seascape, the placeholder always needs to go to the "top" of the device,
+ // which is the same bounds as 0 rotation.
+ val width = dp.widthPx
+ val insetSizeAdjustment = getPlaceholderSizeAdjustment(dp)
+ out.set(0, 0, width, placeholderHeight + insetSizeAdjustment)
+ out.inset(placeholderInset, 0)
+
+ // Adjust the top to account for content off screen. This will help to animate the view in
+ // with rounded corners.
+ val screenWidth = dp.widthPx
+ val screenHeight = dp.heightPx
+ val totalHeight =
+ (1.0f * screenHeight / 2 * (screenWidth - 2 * placeholderInset) / screenWidth).toInt()
+ out.top -= totalHeight - placeholderHeight
+ }
+
+ override fun updateSplitIconParams(
+ out: View,
+ onScreenRectCenterX: Float,
+ onScreenRectCenterY: Float,
+ fullscreenScaleX: Float,
+ fullscreenScaleY: Float,
+ drawableWidth: Int,
+ drawableHeight: Int,
+ dp: DeviceProfile,
+ @StagePosition stagePosition: Int
+ ) {
+ val insetAdjustment = getPlaceholderSizeAdjustment(dp) / 2f
+ out.x = (onScreenRectCenterX / fullscreenScaleX - 1.0f * drawableWidth / 2)
+ out.y =
+ ((onScreenRectCenterY + insetAdjustment) / fullscreenScaleY - 1.0f * drawableHeight / 2)
+ }
+
+ /**
+ * The split placeholder comes with a default inset to buffer the icon from the top of the
+ * screen. But if the device already has a large inset (from cutouts etc), use that instead.
+ */
+ private fun getPlaceholderSizeAdjustment(dp: DeviceProfile?): Int =
+ max((dp!!.insets.top - dp.splitPlaceholderInset).toDouble(), 0.0).toInt()
+
+ override fun setSplitInstructionsParams(
+ out: View,
+ dp: DeviceProfile,
+ splitInstructionsHeight: Int,
+ splitInstructionsWidth: Int
+ ) {
+ out.pivotX = 0f
+ out.pivotY = splitInstructionsHeight.toFloat()
+ out.rotation = degreesRotated
+ val distanceToEdge =
+ out.resources.getDimensionPixelSize(
+ R.dimen.split_instructions_bottom_margin_phone_landscape
+ )
+ // Adjust for any insets on the left edge
+ val insetCorrectionX = dp.insets.left
+ // Center the view in case of unbalanced insets on top or bottom of screen
+ val insetCorrectionY = (dp.insets.bottom - dp.insets.top) / 2
+ out.translationX = (distanceToEdge - insetCorrectionX).toFloat()
+ out.translationY =
+ (-splitInstructionsHeight - splitInstructionsWidth) / 2f + insetCorrectionY
+ // Setting gravity to LEFT instead of the lint-recommended START because we always want this
+ // view to be screen-left when phone is in landscape, regardless of the RtL setting.
+ val lp = out.layoutParams as FrameLayout.LayoutParams
+ lp.gravity = Gravity.LEFT or Gravity.CENTER_VERTICAL
+ out.layoutParams = lp
+ }
+
+ override fun getFinalSplitPlaceholderBounds(
+ splitDividerSize: Int,
+ dp: DeviceProfile,
+ @StagePosition stagePosition: Int,
+ out1: Rect,
+ out2: Rect
+ ) {
+ // In fake land/seascape, the window bounds are always top and bottom half
+ val screenHeight = dp.heightPx
+ val screenWidth = dp.widthPx
+ out1.set(0, 0, screenWidth, screenHeight / 2 - splitDividerSize)
+ out2.set(0, screenHeight / 2 + splitDividerSize, screenWidth, screenHeight)
+ }
+
+ override fun setSplitTaskSwipeRect(
+ dp: DeviceProfile,
+ outRect: Rect,
+ splitInfo: SplitBounds,
+ desiredStagePosition: Int
+ ) {
+ val topLeftTaskPercent: Float
+ val dividerBarPercent: Float
+ if (splitInfo.appsStackedVertically) {
+ topLeftTaskPercent = splitInfo.topTaskPercent
+ dividerBarPercent = splitInfo.dividerHeightPercent
+ } else {
+ topLeftTaskPercent = splitInfo.leftTaskPercent
+ dividerBarPercent = splitInfo.dividerWidthPercent
+ }
+
+ if (desiredStagePosition == STAGE_POSITION_TOP_OR_LEFT) {
+ outRect.bottom = outRect.top + (outRect.height() * topLeftTaskPercent).toInt()
+ } else {
+ outRect.top += (outRect.height() * (topLeftTaskPercent + dividerBarPercent)).toInt()
+ }
+ }
+
+ override fun measureGroupedTaskViewThumbnailBounds(
+ primarySnapshot: View,
+ secondarySnapshot: View,
+ parentWidth: Int,
+ parentHeight: Int,
+ splitBoundsConfig: SplitBounds,
+ dp: DeviceProfile,
+ isRtl: Boolean
+ ) {
+ val primaryParams = primarySnapshot.layoutParams as FrameLayout.LayoutParams
+ val secondaryParams = secondarySnapshot.layoutParams as FrameLayout.LayoutParams
+
+ // Swap the margins that are set in TaskView#setRecentsOrientedState()
+ secondaryParams.topMargin = dp.overviewTaskThumbnailTopMarginPx
+ primaryParams.topMargin = 0
+
+ // Measure and layout the thumbnails bottom up, since the primary is on the visual left
+ // (portrait bottom) and secondary is on the right (portrait top)
+ val spaceAboveSnapshot = dp.overviewTaskThumbnailTopMarginPx
+ val totalThumbnailHeight = parentHeight - spaceAboveSnapshot
+ val dividerBar = getDividerBarSize(totalThumbnailHeight, splitBoundsConfig)
+
+ val (taskViewFirst, taskViewSecond) =
+ getGroupedTaskViewSizes(dp, splitBoundsConfig, parentWidth, parentHeight)
+
+ primarySnapshot.translationY = spaceAboveSnapshot.toFloat()
+ primarySnapshot.measure(
+ MeasureSpec.makeMeasureSpec(taskViewFirst.x, MeasureSpec.EXACTLY),
+ MeasureSpec.makeMeasureSpec(taskViewFirst.y, MeasureSpec.EXACTLY)
+ )
+ val translationY = taskViewFirst.y + spaceAboveSnapshot + dividerBar
+ secondarySnapshot.translationY = (translationY - spaceAboveSnapshot).toFloat()
+ secondarySnapshot.measure(
+ MeasureSpec.makeMeasureSpec(taskViewSecond.x, MeasureSpec.EXACTLY),
+ MeasureSpec.makeMeasureSpec(taskViewSecond.y, MeasureSpec.EXACTLY)
+ )
+ }
+
+ override fun getGroupedTaskViewSizes(
+ dp: DeviceProfile,
+ splitBoundsConfig: SplitBounds,
+ parentWidth: Int,
+ parentHeight: Int
+ ): Pair<Point, Point> {
+ val spaceAboveSnapshot = dp.overviewTaskThumbnailTopMarginPx
+ val totalThumbnailHeight = parentHeight - spaceAboveSnapshot
+ val dividerBar = getDividerBarSize(totalThumbnailHeight, splitBoundsConfig)
+
+ val taskPercent =
+ if (splitBoundsConfig.appsStackedVertically) {
+ splitBoundsConfig.topTaskPercent
+ } else {
+ splitBoundsConfig.leftTaskPercent
+ }
+ val firstTaskViewSize = Point(parentWidth, (totalThumbnailHeight * taskPercent).toInt())
+ val secondTaskViewSize =
+ Point(parentWidth, totalThumbnailHeight - firstTaskViewSize.y - dividerBar)
+ return Pair(firstTaskViewSize, secondTaskViewSize)
+ }
+
+ override fun setTaskIconParams(
+ iconParams: FrameLayout.LayoutParams,
+ taskIconMargin: Int,
+ taskIconHeight: Int,
+ thumbnailTopMargin: Int,
+ isRtl: Boolean
+ ) {
+ iconParams.gravity =
+ if (isRtl) {
+ Gravity.START or Gravity.CENTER_VERTICAL
+ } else {
+ Gravity.END or Gravity.CENTER_VERTICAL
+ }
+ iconParams.rightMargin = -taskIconHeight - taskIconMargin / 2
+ iconParams.leftMargin = 0
+ iconParams.topMargin = thumbnailTopMargin / 2
+ iconParams.bottomMargin = 0
+ }
+
+ override fun setIconAppChipChildrenParams(
+ iconParams: FrameLayout.LayoutParams,
+ chipChildMarginStart: Int
+ ) {
+ iconParams.gravity = Gravity.START or Gravity.CENTER_VERTICAL
+ iconParams.marginStart = chipChildMarginStart
+ iconParams.topMargin = 0
+ }
+
+ override fun setIconAppChipMenuParams(
+ iconAppChipView: IconAppChipView,
+ iconMenuParams: FrameLayout.LayoutParams,
+ iconMenuMargin: Int,
+ thumbnailTopMargin: Int
+ ) {
+ val isRtl = iconAppChipView.layoutDirection == View.LAYOUT_DIRECTION_RTL
+
+ if (isRtl) {
+ iconMenuParams.gravity = Gravity.START or Gravity.BOTTOM
+ iconMenuParams.marginStart = iconMenuMargin
+ iconMenuParams.bottomMargin = iconMenuMargin
+ iconAppChipView.pivotX = iconMenuParams.width - iconMenuParams.height / 2f
+ iconAppChipView.pivotY = iconMenuParams.height / 2f
+ } else {
+ iconMenuParams.gravity = Gravity.END or Gravity.TOP
+ iconMenuParams.marginStart = 0
+ iconMenuParams.bottomMargin = 0
+ iconAppChipView.pivotX = iconMenuParams.width / 2f
+ iconAppChipView.pivotY = iconMenuParams.width / 2f
+ }
+
+ iconMenuParams.topMargin = iconMenuMargin
+ iconMenuParams.marginEnd = iconMenuMargin
+ iconAppChipView.setSplitTranslationY(0f)
+ iconAppChipView.setRotation(degreesRotated)
+ }
+
+ override fun setSplitIconParams(
+ primaryIconView: View,
+ secondaryIconView: View,
+ taskIconHeight: Int,
+ primarySnapshotWidth: Int,
+ primarySnapshotHeight: Int,
+ groupedTaskViewHeight: Int,
+ groupedTaskViewWidth: Int,
+ isRtl: Boolean,
+ deviceProfile: DeviceProfile,
+ splitConfig: SplitBounds
+ ) {
+ val spaceAboveSnapshot = deviceProfile.overviewTaskThumbnailTopMarginPx
+ val totalThumbnailHeight = groupedTaskViewHeight - spaceAboveSnapshot
+ val dividerBar: Int = getDividerBarSize(totalThumbnailHeight, splitConfig)
+
+ val (topLeftY, bottomRightY) =
+ getSplitIconsPosition(
+ taskIconHeight,
+ primarySnapshotHeight,
+ totalThumbnailHeight,
+ isRtl,
+ deviceProfile.overviewTaskMarginPx,
+ dividerBar
+ )
+
+ updateSplitIconsPosition(primaryIconView, topLeftY, isRtl)
+ updateSplitIconsPosition(secondaryIconView, bottomRightY, isRtl)
+ }
+
+ override fun getDefaultSplitPosition(deviceProfile: DeviceProfile): Int {
+ throw IllegalStateException("Default position not available in fake landscape")
+ }
+
+ override fun <T> getSplitSelectTaskOffset(
+ primary: FloatProperty<T>,
+ secondary: FloatProperty<T>,
+ deviceProfile: DeviceProfile
+ ): Pair<FloatProperty<T>, FloatProperty<T>> = Pair(primary, secondary)
+
+ override fun getFloatingTaskOffscreenTranslationTarget(
+ floatingTask: View,
+ onScreenRect: RectF,
+ @StagePosition stagePosition: Int,
+ dp: DeviceProfile
+ ): Float = floatingTask.translationY - onScreenRect.height()
+
+ override fun setFloatingTaskPrimaryTranslation(
+ floatingTask: View,
+ translation: Float,
+ dp: DeviceProfile
+ ) {
+ floatingTask.translationY = translation
+ }
+
+ override fun getFloatingTaskPrimaryTranslation(floatingTask: View, dp: DeviceProfile): Float =
+ floatingTask.translationY
+
+ /**
+ * Retrieves split icons position
+ *
+ * @param taskIconHeight The height of the task icon.
+ * @param primarySnapshotHeight The height for the primary snapshot (i.e., top-left snapshot).
+ * @param totalThumbnailHeight The total height for the group task view.
+ * @param isRtl Whether the layout direction is RTL (or false for LTR).
+ * @param overviewTaskMarginPx The space under the focused task icon provided by Device Profile.
+ * @param dividerSize The size of the divider for the group task view.
+ * @return The top-left and right-bottom positions for the icon views.
+ */
+ @VisibleForTesting
+ open fun getSplitIconsPosition(
+ taskIconHeight: Int,
+ primarySnapshotHeight: Int,
+ totalThumbnailHeight: Int,
+ isRtl: Boolean,
+ overviewTaskMarginPx: Int,
+ dividerSize: Int,
+ ): SplitIconPositions {
+ return if (Flags.enableOverviewIconMenu()) {
+ if (isRtl) {
+ SplitIconPositions(0, -(totalThumbnailHeight - primarySnapshotHeight))
+ } else {
+ SplitIconPositions(0, primarySnapshotHeight + dividerSize)
+ }
+ } else {
+ val topLeftY = primarySnapshotHeight + overviewTaskMarginPx
+ SplitIconPositions(
+ topLeftY = topLeftY,
+ bottomRightY = topLeftY + dividerSize + taskIconHeight
+ )
+ }
+ }
+
+ /**
+ * Updates icon view gravity and translation for split tasks
+ *
+ * @param iconView View to be updated
+ * @param translationY the translationY that should be applied
+ * @param isRtl Whether the layout direction is RTL (or false for LTR).
+ */
+ @SuppressLint("RtlHardcoded")
+ @VisibleForTesting
+ open fun updateSplitIconsPosition(iconView: View, translationY: Int, isRtl: Boolean) {
+ val layoutParams = iconView.layoutParams as FrameLayout.LayoutParams
+
+ if (Flags.enableOverviewIconMenu()) {
+ val appChipView = iconView as IconAppChipView
+ layoutParams.gravity =
+ if (isRtl) Gravity.BOTTOM or Gravity.START else Gravity.TOP or Gravity.END
+ appChipView.layoutParams = layoutParams
+ appChipView.setSplitTranslationX(0f)
+ appChipView.setSplitTranslationY(translationY.toFloat())
+ } else {
+ layoutParams.gravity = Gravity.TOP or Gravity.RIGHT
+ layoutParams.topMargin = translationY
+ iconView.translationX = 0f
+ iconView.translationY = 0f
+ iconView.layoutParams = layoutParams
+ }
+ }
+
+ /**
+ * It calculates the divider's size in the group task view.
+ *
+ * @param totalThumbnailHeight The total height for the group task view
+ * @param splitConfig Contains information about sizes and proportions for split task.
+ * @return The divider size for the group task view.
+ */
+ protected fun getDividerBarSize(totalThumbnailHeight: Int, splitConfig: SplitBounds): Int {
+ return Math.round(
+ totalThumbnailHeight *
+ if (splitConfig.appsStackedVertically) splitConfig.dividerHeightPercent
+ else splitConfig.dividerWidthPercent
+ )
+ }
+
+ /**
+ * Data structure to keep the y position to be used for the split task icon views translation.
+ *
+ * @param topLeftY The y-axis position for the task view position on the Top or Left side.
+ * @param bottomRightY The y-axis position for the task view position on the Bottom or Right
+ * side.
+ */
+ data class SplitIconPositions(val topLeftY: Int, val bottomRightY: Int)
+}
diff --git a/quickstep/src/com/android/quickstep/orientation/PortraitPagedViewHandler.java b/quickstep/src/com/android/quickstep/orientation/PortraitPagedViewHandler.java
index 5cd9776..0476fe8 100644
--- a/quickstep/src/com/android/quickstep/orientation/PortraitPagedViewHandler.java
+++ b/quickstep/src/com/android/quickstep/orientation/PortraitPagedViewHandler.java
@@ -75,6 +75,7 @@
public <T> T getSecondaryValue(T x, T y) {
return y;
}
+
@Override
public boolean isLayoutNaturalToLauncher() {
return true;
@@ -795,7 +796,7 @@
}
@Override
- public Float getFloatingTaskPrimaryTranslation(View floatingTask, DeviceProfile dp) {
+ public float getFloatingTaskPrimaryTranslation(View floatingTask, DeviceProfile dp) {
return dp.isLeftRightSplit
? floatingTask.getTranslationX()
: floatingTask.getTranslationY();
diff --git a/quickstep/src/com/android/quickstep/orientation/RecentsPagedOrientationHandler.java b/quickstep/src/com/android/quickstep/orientation/RecentsPagedOrientationHandler.java
deleted file mode 100644
index 4b65d53..0000000
--- a/quickstep/src/com/android/quickstep/orientation/RecentsPagedOrientationHandler.java
+++ /dev/null
@@ -1,261 +0,0 @@
-/*
- * 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.orientation;
-
-
-import android.graphics.Point;
-import android.graphics.PointF;
-import android.graphics.Rect;
-import android.graphics.RectF;
-import android.graphics.drawable.ShapeDrawable;
-import android.util.FloatProperty;
-import android.util.Pair;
-import android.view.View;
-import android.widget.FrameLayout;
-import android.widget.LinearLayout;
-
-import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.touch.PagedOrientationHandler;
-import com.android.launcher3.touch.SingleAxisSwipeDetector;
-import com.android.launcher3.util.SplitConfigurationOptions;
-import com.android.launcher3.util.SplitConfigurationOptions.SplitBounds;
-import com.android.launcher3.util.SplitConfigurationOptions.SplitPositionOption;
-import com.android.launcher3.util.SplitConfigurationOptions.StagePosition;
-import com.android.quickstep.views.IconAppChipView;
-
-import java.util.List;
-
-/**
- * Abstraction layer to separate horizontal and vertical specific implementations
- * for {@link com.android.quickstep.views.RecentsView}. Majority of these implementations are
- * (should be) as simple as choosing the correct X and Y analogous methods.
- */
-public interface RecentsPagedOrientationHandler extends PagedOrientationHandler {
-
- RecentsPagedOrientationHandler PORTRAIT = new PortraitPagedViewHandler();
- RecentsPagedOrientationHandler LANDSCAPE = new LandscapePagedViewHandler();
- RecentsPagedOrientationHandler SEASCAPE = new SeascapePagedViewHandler();
-
- <T> void setSecondary(T target, Float2DAction<T> action, float param);
- <T> void set(T target, Int2DAction<T> action, int primaryParam, int secondaryParam);
- int getPrimarySize(View view);
- float getPrimarySize(RectF rect);
- int getSecondaryTranslationDirectionFactor();
- float getDegreesRotated();
- int getRotation();
- boolean isLayoutNaturalToLauncher();
-
- <T> T getPrimaryValue(T x, T y);
- <T> T getSecondaryValue(T x, T y);
- void setPrimaryScale(View view, float scale);
- void setSecondaryScale(View view, float scale);
- float getStart(RectF rect);
- float getEnd(RectF rect);
- int getClearAllSidePadding(View view, boolean isRtl);
- int getSecondaryDimension(View view);
- FloatProperty<View> getPrimaryViewTranslate();
- FloatProperty<View> getSecondaryViewTranslate();
- int getSplitTranslationDirectionFactor(@StagePosition int stagePosition,
- DeviceProfile deviceProfile);
- Pair<FloatProperty, FloatProperty> getSplitSelectTaskOffset(FloatProperty primary,
- FloatProperty secondary, DeviceProfile deviceProfile);
- int getDistanceToBottomOfRect(DeviceProfile dp, Rect rect);
- List<SplitPositionOption> getSplitPositionOptions(DeviceProfile dp);
- /**
- * @param placeholderHeight height of placeholder view in portrait, width in landscape
- */
- void getInitialSplitPlaceholderBounds(int placeholderHeight, int placeholderInset,
- DeviceProfile dp, @StagePosition int stagePosition, Rect out);
-
- /**
- * Centers an icon in the split staging area, accounting for insets.
- * @param out The icon that needs to be centered.
- * @param onScreenRectCenterX The x-center of the on-screen staging area (most of the Rect is
- * offscreen).
- * @param onScreenRectCenterY The y-center of the on-screen staging area (most of the Rect is
- * offscreen).
- * @param fullscreenScaleX A x-scaling factor used to convert coordinates back into pixels.
- * @param fullscreenScaleY A y-scaling factor used to convert coordinates back into pixels.
- * @param drawableWidth The icon's drawable (final) width.
- * @param drawableHeight The icon's drawable (final) height.
- * @param dp The device profile, used to report rotation and hardware insets.
- * @param stagePosition 0 if the staging area is pinned to top/left, 1 for bottom/right.
- */
- void updateSplitIconParams(View out, float onScreenRectCenterX,
- float onScreenRectCenterY, float fullscreenScaleX, float fullscreenScaleY,
- int drawableWidth, int drawableHeight, DeviceProfile dp,
- @StagePosition int stagePosition);
-
- /**
- * Sets positioning and rotation for a SplitInstructionsView.
- * @param out The SplitInstructionsView that needs to be positioned.
- * @param dp The device profile, used to report rotation and device type.
- * @param splitInstructionsHeight The SplitInstructionView's height.
- * @param splitInstructionsWidth The SplitInstructionView's width.
- */
- void setSplitInstructionsParams(View out, DeviceProfile dp, int splitInstructionsHeight,
- int splitInstructionsWidth);
-
- /**
- * @param splitDividerSize height of split screen drag handle in portrait, width in landscape
- * @param stagePosition the split position option (top/left, bottom/right) of the first
- * task selected for entering split
- * @param out1 the bounds for where the first selected app will be
- * @param out2 the bounds for where the second selected app will be, complimentary to
- * {@param out1} based on {@param initialSplitOption}
- */
- void getFinalSplitPlaceholderBounds(int splitDividerSize, DeviceProfile dp,
- @StagePosition int stagePosition, Rect out1, Rect out2);
-
- int getDefaultSplitPosition(DeviceProfile deviceProfile);
-
- /**
- * @param outRect This is expected to be the rect that has the dimensions for a non-split,
- * fullscreen task in overview. This will directly be modified.
- * @param desiredStagePosition Which stage position (topLeft/rightBottom) we want to resize
- * outRect for
- */
- void setSplitTaskSwipeRect(DeviceProfile dp, Rect outRect, SplitBounds splitInfo,
- @SplitConfigurationOptions.StagePosition int desiredStagePosition);
-
- void measureGroupedTaskViewThumbnailBounds(View primarySnapshot, View secondarySnapshot,
- int parentWidth, int parentHeight,
- SplitBounds splitBoundsConfig, DeviceProfile dp, boolean isRtl);
-
- /**
- * Creates two Points representing the dimensions of the two tasks in a GroupedTaskView
- *
- * @return first -> primary task snapshot, second -> secondary task snapshot.
- * x -> width, y -> height
- */
- Pair<Point, Point> getGroupedTaskViewSizes(DeviceProfile dp, SplitBounds splitBoundsConfig,
- int parentWidth, int parentHeight);
-
- // Overview TaskMenuView methods
- /** Sets layout params on a task's app icon. Only use this when app chip is disabled. */
- void setTaskIconParams(FrameLayout.LayoutParams iconParams,
- int taskIconMargin, int taskIconHeight, int thumbnailTopMargin, boolean isRtl);
-
- /**
- * Sets layout params on the children of an app chip. Only use this when app chip is enabled.
- */
- void setIconAppChipChildrenParams(
- FrameLayout.LayoutParams iconParams, int chipChildMarginStart);
-
- void setIconAppChipMenuParams(IconAppChipView iconAppChipView,
- FrameLayout.LayoutParams iconMenuParams,
- int iconMenuMargin, int thumbnailTopMargin);
- void setSplitIconParams(View primaryIconView, View secondaryIconView,
- int taskIconHeight, int primarySnapshotWidth, int primarySnapshotHeight,
- int groupedTaskViewHeight, int groupedTaskViewWidth, boolean isRtl,
- DeviceProfile deviceProfile, SplitBounds splitConfig);
-
- /*
- * The following two methods try to center the TaskMenuView in landscape by finding the center
- * of the thumbnail view and then subtracting half of the taskMenu width. In this case, the
- * taskMenu width is the same size as the thumbnail width (what got set below in
- * getTaskMenuWidth()), so we directly use that in the calculations.
- */
- float getTaskMenuX(float x, View thumbnailView, DeviceProfile deviceProfile,
- float taskInsetMargin, View taskViewIcon);
- float getTaskMenuY(float y, View thumbnailView, int stagePosition,
- View taskMenuView, float taskInsetMargin, View taskViewIcon);
- int getTaskMenuWidth(View thumbnailView, DeviceProfile deviceProfile,
- @StagePosition int stagePosition);
-
- int getTaskMenuHeight(float taskInsetMargin, DeviceProfile deviceProfile, float taskMenuX,
- float taskMenuY);
- /**
- * Sets linear layout orientation for {@link com.android.launcher3.popup.SystemShortcut} items
- * inside task menu view.
- */
- void setTaskOptionsMenuLayoutOrientation(DeviceProfile deviceProfile,
- LinearLayout taskMenuLayout, int dividerSpacing,
- ShapeDrawable dividerDrawable);
- /**
- * Sets layout param attributes for {@link com.android.launcher3.popup.SystemShortcut} child
- * views inside task menu view.
- */
- void setLayoutParamsForTaskMenuOptionItem(LinearLayout.LayoutParams lp,
- LinearLayout viewGroup, DeviceProfile deviceProfile);
-
- /**
- * Calculates the position where a Digital Wellbeing Banner should be placed on its parent
- * TaskView.
- * @return A Pair of Floats representing the proper x and y translations.
- */
- Pair<Float, Float> getDwbLayoutTranslations(int taskViewWidth,
- int taskViewHeight, SplitBounds splitBounds, DeviceProfile deviceProfile,
- View[] thumbnailViews, int desiredTaskId, View banner);
-
- // The following are only used by TaskViewTouchHandler.
- /** @return Either VERTICAL or HORIZONTAL. */
- SingleAxisSwipeDetector.Direction getUpDownSwipeDirection();
- /** @return Given {@link #getUpDownSwipeDirection()}, whether POSITIVE or NEGATIVE is up. */
- int getUpDirection(boolean isRtl);
- /** @return Whether the displacement is going towards the top of the screen. */
- boolean isGoingUp(float displacement, boolean isRtl);
- /** @return Either 1 or -1, a factor to multiply by so the animation goes the correct way. */
- int getTaskDragDisplacementFactor(boolean isRtl);
-
- /**
- * Maps the velocity from the coordinate plane of the foreground app to that
- * of Launcher's (which now will always be portrait)
- */
- void adjustFloatingIconStartVelocity(PointF velocity);
-
- /**
- * Ensures that outStartRect left bound is within the DeviceProfile's visual boundaries
- * @param outStartRect The start rect that will directly be modified
- */
- void fixBoundsForHomeAnimStartRect(RectF outStartRect, DeviceProfile deviceProfile);
-
- /**
- * Determine the target translation for animating the FloatingTaskView out. This value could
- * either be an x-coordinate or a y-coordinate, depending on which way the FloatingTaskView was
- * docked.
- *
- * @param floatingTask The FloatingTaskView.
- * @param onScreenRect The current on-screen dimensions of the FloatingTaskView.
- * @param stagePosition STAGE_POSITION_TOP_OR_LEFT or STAGE_POSITION_BOTTOM_OR_RIGHT.
- * @param dp The device profile.
- * @return A float. When an animation translates the FloatingTaskView to this position, it will
- * appear to tuck away off the edge of the screen.
- */
- float getFloatingTaskOffscreenTranslationTarget(View floatingTask, RectF onScreenRect,
- @StagePosition int stagePosition, DeviceProfile dp);
-
- /**
- * Sets the translation of a FloatingTaskView along its "slide-in/slide-out" axis (could be
- * either x or y), depending on how the view is oriented.
- *
- * @param floatingTask The FloatingTaskView to be translated.
- * @param translation The target translation value.
- * @param dp The current device profile.
- */
- void setFloatingTaskPrimaryTranslation(View floatingTask, float translation, DeviceProfile dp);
-
- /**
- * Gets the translation of a FloatingTaskView along its "slide-in/slide-out" axis (could be
- * either x or y), depending on how the view is oriented.
- *
- * @param floatingTask The FloatingTaskView in question.
- * @param dp The current device profile.
- * @return The current translation value.
- */
- Float getFloatingTaskPrimaryTranslation(View floatingTask, DeviceProfile dp);
-}
diff --git a/quickstep/src/com/android/quickstep/orientation/RecentsPagedOrientationHandler.kt b/quickstep/src/com/android/quickstep/orientation/RecentsPagedOrientationHandler.kt
new file mode 100644
index 0000000..6c82890
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/orientation/RecentsPagedOrientationHandler.kt
@@ -0,0 +1,379 @@
+/*
+ * 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.orientation
+
+import android.graphics.Point
+import android.graphics.PointF
+import android.graphics.Rect
+import android.graphics.RectF
+import android.graphics.drawable.ShapeDrawable
+import android.util.FloatProperty
+import android.util.Pair
+import android.view.View
+import android.widget.FrameLayout
+import android.widget.LinearLayout
+import com.android.launcher3.DeviceProfile
+import com.android.launcher3.touch.PagedOrientationHandler
+import com.android.launcher3.touch.PagedOrientationHandler.Float2DAction
+import com.android.launcher3.touch.PagedOrientationHandler.Int2DAction
+import com.android.launcher3.touch.SingleAxisSwipeDetector
+import com.android.launcher3.util.SplitConfigurationOptions
+import com.android.launcher3.util.SplitConfigurationOptions.SplitPositionOption
+import com.android.launcher3.util.SplitConfigurationOptions.StagePosition
+import com.android.quickstep.views.IconAppChipView
+
+/**
+ * Abstraction layer to separate horizontal and vertical specific implementations for
+ * [com.android.quickstep.views.RecentsView]. Majority of these implementations are (should be) as
+ * simple as choosing the correct X and Y analogous methods.
+ */
+interface RecentsPagedOrientationHandler : PagedOrientationHandler {
+ fun <T> setSecondary(target: T, action: Float2DAction<T>, param: Float)
+
+ operator fun <T> set(target: T, action: Int2DAction<T>, primaryParam: Int, secondaryParam: Int)
+
+ fun getPrimarySize(view: View): Int
+
+ fun getPrimarySize(rect: RectF): Float
+
+ val secondaryTranslationDirectionFactor: Int
+
+ val degreesRotated: Float
+
+ val rotation: Int
+
+ val isLayoutNaturalToLauncher: Boolean
+
+ fun <T> getPrimaryValue(x: T, y: T): T
+
+ fun <T> getSecondaryValue(x: T, y: T): T
+
+ fun setPrimaryScale(view: View, scale: Float)
+
+ fun setSecondaryScale(view: View, scale: Float)
+
+ fun getStart(rect: RectF): Float
+
+ fun getEnd(rect: RectF): Float
+
+ fun getClearAllSidePadding(view: View, isRtl: Boolean): Int
+
+ fun getSecondaryDimension(view: View): Int
+
+ val primaryViewTranslate: FloatProperty<View>
+ val secondaryViewTranslate: FloatProperty<View>
+
+ fun getSplitTranslationDirectionFactor(
+ @StagePosition stagePosition: Int,
+ deviceProfile: DeviceProfile
+ ): Int
+
+ fun <T> getSplitSelectTaskOffset(
+ primary: FloatProperty<T>,
+ secondary: FloatProperty<T>,
+ deviceProfile: DeviceProfile
+ ): Pair<FloatProperty<T>, FloatProperty<T>>
+
+ fun getDistanceToBottomOfRect(dp: DeviceProfile, rect: Rect): Int
+
+ fun getSplitPositionOptions(dp: DeviceProfile): List<SplitPositionOption>
+
+ /** @param placeholderHeight height of placeholder view in portrait, width in landscape */
+ fun getInitialSplitPlaceholderBounds(
+ placeholderHeight: Int,
+ placeholderInset: Int,
+ dp: DeviceProfile,
+ @StagePosition stagePosition: Int,
+ out: Rect
+ )
+
+ /**
+ * Centers an icon in the split staging area, accounting for insets.
+ *
+ * @param out The icon that needs to be centered.
+ * @param onScreenRectCenterX The x-center of the on-screen staging area (most of the Rect is
+ * offscreen).
+ * @param onScreenRectCenterY The y-center of the on-screen staging area (most of the Rect is
+ * offscreen).
+ * @param fullscreenScaleX A x-scaling factor used to convert coordinates back into pixels.
+ * @param fullscreenScaleY A y-scaling factor used to convert coordinates back into pixels.
+ * @param drawableWidth The icon's drawable (final) width.
+ * @param drawableHeight The icon's drawable (final) height.
+ * @param dp The device profile, used to report rotation and hardware insets.
+ * @param stagePosition 0 if the staging area is pinned to top/left, 1 for bottom/right.
+ */
+ fun updateSplitIconParams(
+ out: View,
+ onScreenRectCenterX: Float,
+ onScreenRectCenterY: Float,
+ fullscreenScaleX: Float,
+ fullscreenScaleY: Float,
+ drawableWidth: Int,
+ drawableHeight: Int,
+ dp: DeviceProfile,
+ @StagePosition stagePosition: Int
+ )
+
+ /**
+ * Sets positioning and rotation for a SplitInstructionsView.
+ *
+ * @param out The SplitInstructionsView that needs to be positioned.
+ * @param dp The device profile, used to report rotation and device type.
+ * @param splitInstructionsHeight The SplitInstructionView's height.
+ * @param splitInstructionsWidth The SplitInstructionView's width.
+ */
+ fun setSplitInstructionsParams(
+ out: View,
+ dp: DeviceProfile,
+ splitInstructionsHeight: Int,
+ splitInstructionsWidth: Int
+ )
+
+ /**
+ * @param splitDividerSize height of split screen drag handle in portrait, width in landscape
+ * @param stagePosition the split position option (top/left, bottom/right) of the first task
+ * selected for entering split
+ * @param out1 the bounds for where the first selected app will be
+ * @param out2 the bounds for where the second selected app will be, complimentary to {@param
+ * out1} based on {@param initialSplitOption}
+ */
+ fun getFinalSplitPlaceholderBounds(
+ splitDividerSize: Int,
+ dp: DeviceProfile,
+ @StagePosition stagePosition: Int,
+ out1: Rect,
+ out2: Rect
+ )
+
+ fun getDefaultSplitPosition(deviceProfile: DeviceProfile): Int
+
+ /**
+ * @param outRect This is expected to be the rect that has the dimensions for a non-split,
+ * fullscreen task in overview. This will directly be modified.
+ * @param desiredStagePosition Which stage position (topLeft/rightBottom) we want to resize
+ * outRect for
+ */
+ fun setSplitTaskSwipeRect(
+ dp: DeviceProfile,
+ outRect: Rect,
+ splitInfo: SplitConfigurationOptions.SplitBounds,
+ @StagePosition desiredStagePosition: Int
+ )
+
+ fun measureGroupedTaskViewThumbnailBounds(
+ primarySnapshot: View,
+ secondarySnapshot: View,
+ parentWidth: Int,
+ parentHeight: Int,
+ splitBoundsConfig: SplitConfigurationOptions.SplitBounds,
+ dp: DeviceProfile,
+ isRtl: Boolean
+ )
+
+ /**
+ * Creates two Points representing the dimensions of the two tasks in a GroupedTaskView
+ *
+ * @return first -> primary task snapshot, second -> secondary task snapshot. x -> width, y ->
+ * height
+ */
+ fun getGroupedTaskViewSizes(
+ dp: DeviceProfile,
+ splitBoundsConfig: SplitConfigurationOptions.SplitBounds,
+ parentWidth: Int,
+ parentHeight: Int
+ ): Pair<Point, Point>
+ // Overview TaskMenuView methods
+ /** Sets layout params on a task's app icon. Only use this when app chip is disabled. */
+ fun setTaskIconParams(
+ iconParams: FrameLayout.LayoutParams,
+ taskIconMargin: Int,
+ taskIconHeight: Int,
+ thumbnailTopMargin: Int,
+ isRtl: Boolean
+ )
+
+ /**
+ * Sets layout params on the children of an app chip. Only use this when app chip is enabled.
+ */
+ fun setIconAppChipChildrenParams(
+ iconParams: FrameLayout.LayoutParams,
+ chipChildMarginStart: Int
+ )
+
+ fun setIconAppChipMenuParams(
+ iconAppChipView: IconAppChipView,
+ iconMenuParams: FrameLayout.LayoutParams,
+ iconMenuMargin: Int,
+ thumbnailTopMargin: Int
+ )
+
+ fun setSplitIconParams(
+ primaryIconView: View,
+ secondaryIconView: View,
+ taskIconHeight: Int,
+ primarySnapshotWidth: Int,
+ primarySnapshotHeight: Int,
+ groupedTaskViewHeight: Int,
+ groupedTaskViewWidth: Int,
+ isRtl: Boolean,
+ deviceProfile: DeviceProfile,
+ splitConfig: SplitConfigurationOptions.SplitBounds
+ )
+
+ /*
+ * The following two methods try to center the TaskMenuView in landscape by finding the center
+ * of the thumbnail view and then subtracting half of the taskMenu width. In this case, the
+ * taskMenu width is the same size as the thumbnail width (what got set below in
+ * getTaskMenuWidth()), so we directly use that in the calculations.
+ */
+ fun getTaskMenuX(
+ x: Float,
+ thumbnailView: View,
+ deviceProfile: DeviceProfile,
+ taskInsetMargin: Float,
+ taskViewIcon: View
+ ): Float
+
+ fun getTaskMenuY(
+ y: Float,
+ thumbnailView: View,
+ stagePosition: Int,
+ taskMenuView: View,
+ taskInsetMargin: Float,
+ taskViewIcon: View
+ ): Float
+
+ fun getTaskMenuWidth(
+ thumbnailView: View,
+ deviceProfile: DeviceProfile,
+ @StagePosition stagePosition: Int
+ ): Int
+
+ fun getTaskMenuHeight(
+ taskInsetMargin: Float,
+ deviceProfile: DeviceProfile,
+ taskMenuX: Float,
+ taskMenuY: Float
+ ): Int
+
+ /**
+ * Sets linear layout orientation for [com.android.launcher3.popup.SystemShortcut] items inside
+ * task menu view.
+ */
+ fun setTaskOptionsMenuLayoutOrientation(
+ deviceProfile: DeviceProfile,
+ taskMenuLayout: LinearLayout,
+ dividerSpacing: Int,
+ dividerDrawable: ShapeDrawable
+ )
+
+ /**
+ * Sets layout param attributes for [com.android.launcher3.popup.SystemShortcut] child views
+ * inside task menu view.
+ */
+ fun setLayoutParamsForTaskMenuOptionItem(
+ lp: LinearLayout.LayoutParams,
+ viewGroup: LinearLayout,
+ deviceProfile: DeviceProfile
+ )
+
+ /**
+ * Calculates the position where a Digital Wellbeing Banner should be placed on its parent
+ * TaskView.
+ *
+ * @return A Pair of Floats representing the proper x and y translations.
+ */
+ fun getDwbLayoutTranslations(
+ taskViewWidth: Int,
+ taskViewHeight: Int,
+ splitBounds: SplitConfigurationOptions.SplitBounds?,
+ deviceProfile: DeviceProfile,
+ thumbnailViews: Array<View>,
+ desiredTaskId: Int,
+ banner: View
+ ): Pair<Float, Float>
+ // The following are only used by TaskViewTouchHandler.
+
+ /** @return Either VERTICAL or HORIZONTAL. */
+ val upDownSwipeDirection: SingleAxisSwipeDetector.Direction
+
+ /** @return Given [.getUpDownSwipeDirection], whether POSITIVE or NEGATIVE is up. */
+ fun getUpDirection(isRtl: Boolean): Int
+
+ /** @return Whether the displacement is going towards the top of the screen. */
+ fun isGoingUp(displacement: Float, isRtl: Boolean): Boolean
+
+ /** @return Either 1 or -1, a factor to multiply by so the animation goes the correct way. */
+ fun getTaskDragDisplacementFactor(isRtl: Boolean): Int
+
+ /**
+ * Maps the velocity from the coordinate plane of the foreground app to that of Launcher's
+ * (which now will always be portrait)
+ */
+ fun adjustFloatingIconStartVelocity(velocity: PointF)
+
+ /**
+ * Ensures that outStartRect left bound is within the DeviceProfile's visual boundaries
+ *
+ * @param outStartRect The start rect that will directly be modified
+ */
+ fun fixBoundsForHomeAnimStartRect(outStartRect: RectF, deviceProfile: DeviceProfile)
+
+ /**
+ * Determine the target translation for animating the FloatingTaskView out. This value could
+ * either be an x-coordinate or a y-coordinate, depending on which way the FloatingTaskView was
+ * docked.
+ *
+ * @param floatingTask The FloatingTaskView.
+ * @param onScreenRect The current on-screen dimensions of the FloatingTaskView.
+ * @param stagePosition STAGE_POSITION_TOP_OR_LEFT or STAGE_POSITION_BOTTOM_OR_RIGHT.
+ * @param dp The device profile.
+ * @return A float. When an animation translates the FloatingTaskView to this position, it will
+ * appear to tuck away off the edge of the screen.
+ */
+ fun getFloatingTaskOffscreenTranslationTarget(
+ floatingTask: View,
+ onScreenRect: RectF,
+ @StagePosition stagePosition: Int,
+ dp: DeviceProfile
+ ): Float
+
+ /**
+ * Sets the translation of a FloatingTaskView along its "slide-in/slide-out" axis (could be
+ * either x or y), depending on how the view is oriented.
+ *
+ * @param floatingTask The FloatingTaskView to be translated.
+ * @param translation The target translation value.
+ * @param dp The current device profile.
+ */
+ fun setFloatingTaskPrimaryTranslation(floatingTask: View, translation: Float, dp: DeviceProfile)
+
+ /**
+ * Gets the translation of a FloatingTaskView along its "slide-in/slide-out" axis (could be
+ * either x or y), depending on how the view is oriented.
+ *
+ * @param floatingTask The FloatingTaskView in question.
+ * @param dp The current device profile.
+ * @return The current translation value.
+ */
+ fun getFloatingTaskPrimaryTranslation(floatingTask: View, dp: DeviceProfile): Float
+
+ companion object {
+ @JvmField val PORTRAIT: RecentsPagedOrientationHandler = PortraitPagedViewHandler()
+ @JvmField val LANDSCAPE: RecentsPagedOrientationHandler = LandscapePagedViewHandler()
+ @JvmField val SEASCAPE: RecentsPagedOrientationHandler = SeascapePagedViewHandler()
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/orientation/SeascapePagedViewHandler.java b/quickstep/src/com/android/quickstep/orientation/SeascapePagedViewHandler.java
deleted file mode 100644
index 89c678c..0000000
--- a/quickstep/src/com/android/quickstep/orientation/SeascapePagedViewHandler.java
+++ /dev/null
@@ -1,418 +0,0 @@
-/*
- * 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.orientation;
-
-import static android.view.Gravity.BOTTOM;
-import static android.view.Gravity.CENTER_VERTICAL;
-import static android.view.Gravity.END;
-import static android.view.Gravity.RIGHT;
-import static android.view.Gravity.START;
-import static android.view.Gravity.TOP;
-import static android.view.View.LAYOUT_DIRECTION_RTL;
-
-import static com.android.launcher3.Flags.enableOverviewIconMenu;
-import static com.android.launcher3.touch.SingleAxisSwipeDetector.HORIZONTAL;
-import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT;
-import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_UNDEFINED;
-import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_TYPE_MAIN;
-
-import android.content.res.Resources;
-import android.graphics.Point;
-import android.graphics.PointF;
-import android.graphics.Rect;
-import android.util.Pair;
-import android.view.Gravity;
-import android.view.Surface;
-import android.view.View;
-import android.widget.FrameLayout;
-
-import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
-import com.android.launcher3.touch.SingleAxisSwipeDetector;
-import com.android.launcher3.util.SplitConfigurationOptions;
-import com.android.launcher3.util.SplitConfigurationOptions.SplitBounds;
-import com.android.launcher3.util.SplitConfigurationOptions.SplitPositionOption;
-import com.android.launcher3.views.BaseDragLayer;
-import com.android.quickstep.views.IconAppChipView;
-
-import java.util.Collections;
-import java.util.List;
-
-public class SeascapePagedViewHandler extends LandscapePagedViewHandler {
-
- @Override
- public int getSecondaryTranslationDirectionFactor() {
- return -1;
- }
-
- @Override
- public int getSplitTranslationDirectionFactor(int stagePosition, DeviceProfile deviceProfile) {
- if (stagePosition == STAGE_POSITION_BOTTOM_OR_RIGHT) {
- return -1;
- } else {
- return 1;
- }
- }
-
- @Override
- public boolean getRecentsRtlSetting(Resources resources) {
- return Utilities.isRtl(resources);
- }
-
- @Override
- public float getDegreesRotated() {
- return 270;
- }
-
- @Override
- public int getRotation() {
- return Surface.ROTATION_270;
- }
-
- @Override
- public void adjustFloatingIconStartVelocity(PointF velocity) {
- float oldX = velocity.x;
- float oldY = velocity.y;
- velocity.set(oldY, -oldX);
- }
-
- @Override
- public float getTaskMenuX(float x, View thumbnailView,
- DeviceProfile deviceProfile, float taskInsetMargin, View taskViewIcon) {
- return x + taskInsetMargin;
- }
-
- @Override
- public float getTaskMenuY(float y, View thumbnailView, int stagePosition,
- View taskMenuView, float taskInsetMargin, View taskViewIcon) {
- if (enableOverviewIconMenu()) {
- return y;
- }
- BaseDragLayer.LayoutParams lp = (BaseDragLayer.LayoutParams) taskMenuView.getLayoutParams();
- int taskMenuWidth = lp.width;
- if (stagePosition == STAGE_POSITION_UNDEFINED) {
- return y + taskInsetMargin
- + (thumbnailView.getMeasuredHeight() + taskMenuWidth) / 2f;
- } else {
- return y + taskMenuWidth + taskInsetMargin;
- }
- }
-
- @Override
- public int getTaskMenuHeight(float taskInsetMargin, DeviceProfile deviceProfile,
- float taskMenuX, float taskMenuY) {
- return (int) (deviceProfile.availableWidthPx - taskInsetMargin - taskMenuX);
- }
-
- @Override
- public void setSplitTaskSwipeRect(DeviceProfile dp, Rect outRect, SplitBounds splitInfo,
- int desiredStagePosition) {
- float topLeftTaskPercent = splitInfo.appsStackedVertically
- ? splitInfo.topTaskPercent
- : splitInfo.leftTaskPercent;
- float dividerBarPercent = splitInfo.appsStackedVertically
- ? splitInfo.dividerHeightPercent
- : splitInfo.dividerWidthPercent;
-
- // In seascape, the primary thumbnail is counterintuitively placed at the physical bottom of
- // the screen. This is to preserve consistency when the user rotates: From the user's POV,
- // the primary should always be on the left.
- if (desiredStagePosition == SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT) {
- outRect.top += (int) (outRect.height() * ((1 - topLeftTaskPercent)));
- } else {
- outRect.bottom -= (int) (outRect.height() * (topLeftTaskPercent + dividerBarPercent));
- }
- }
-
- @Override
- public Pair<Float, Float> getDwbLayoutTranslations(int taskViewWidth,
- int taskViewHeight, SplitBounds splitBounds, DeviceProfile deviceProfile,
- View[] thumbnailViews, int desiredTaskId, View banner) {
- boolean isRtl = banner.getLayoutDirection() == LAYOUT_DIRECTION_RTL;
- float translationX = 0;
- float translationY = 0;
- FrameLayout.LayoutParams bannerParams = (FrameLayout.LayoutParams) banner.getLayoutParams();
- banner.setPivotX(0);
- banner.setPivotY(0);
- banner.setRotation(getDegreesRotated());
- translationX = taskViewWidth - banner.getHeight();
- FrameLayout.LayoutParams snapshotParams =
- (FrameLayout.LayoutParams) thumbnailViews[0]
- .getLayoutParams();
- bannerParams.gravity = BOTTOM | (isRtl ? END : START);
-
- if (splitBounds == null) {
- // Single, fullscreen case
- bannerParams.width = taskViewHeight - snapshotParams.topMargin;
- translationY = banner.getHeight();
- return new Pair<>(translationX, translationY);
- }
-
- // Set correct width
- if (desiredTaskId == splitBounds.leftTopTaskId) {
- bannerParams.width = thumbnailViews[0].getMeasuredHeight();
- } else {
- bannerParams.width = thumbnailViews[1].getMeasuredHeight();
- }
-
- // Set translations
- if (desiredTaskId == splitBounds.rightBottomTaskId) {
- translationY = banner.getHeight();
- }
- if (desiredTaskId == splitBounds.leftTopTaskId) {
- float bottomRightTaskPlusDividerPercent = splitBounds.appsStackedVertically
- ? (1f - splitBounds.topTaskPercent)
- : (1f - splitBounds.leftTaskPercent);
- translationY = banner.getHeight()
- - ((taskViewHeight - snapshotParams.topMargin)
- * bottomRightTaskPlusDividerPercent);
- }
- return new Pair<>(translationX, translationY);
- }
-
- @Override
- public int getDistanceToBottomOfRect(DeviceProfile dp, Rect rect) {
- return dp.widthPx - rect.right;
- }
-
- @Override
- public List<SplitPositionOption> getSplitPositionOptions(DeviceProfile dp) {
- // Add "right" option which is actually the top
- return Collections.singletonList(new SplitPositionOption(
- R.drawable.ic_split_horizontal, R.string.recent_task_option_split_screen,
- STAGE_POSITION_BOTTOM_OR_RIGHT, STAGE_TYPE_MAIN));
- }
-
- @Override
- public void setSplitInstructionsParams(View out, DeviceProfile dp, int splitInstructionsHeight,
- int splitInstructionsWidth) {
- out.setPivotX(0);
- out.setPivotY(splitInstructionsHeight);
- out.setRotation(getDegreesRotated());
- int distanceToEdge = out.getResources().getDimensionPixelSize(
- R.dimen.split_instructions_bottom_margin_phone_landscape);
- // Adjust for any insets on the right edge
- int insetCorrectionX = dp.getInsets().right;
- // Center the view in case of unbalanced insets on top or bottom of screen
- int insetCorrectionY = (dp.getInsets().bottom - dp.getInsets().top) / 2;
- out.setTranslationX(splitInstructionsWidth - distanceToEdge + insetCorrectionX);
- out.setTranslationY(((-splitInstructionsHeight + splitInstructionsWidth) / 2f)
- + insetCorrectionY);
- FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) out.getLayoutParams();
- // Setting gravity to RIGHT instead of the lint-recommended END because we always want this
- // view to be screen-right when phone is in seascape, regardless of the RtL setting.
- lp.gravity = RIGHT | CENTER_VERTICAL;
- out.setLayoutParams(lp);
- }
-
- @Override
- public void setTaskIconParams(FrameLayout.LayoutParams iconParams,
- int taskIconMargin, int taskIconHeight, int thumbnailTopMargin, boolean isRtl) {
- iconParams.gravity = (isRtl ? END : START) | CENTER_VERTICAL;
- iconParams.setMargins(-taskIconHeight - taskIconMargin / 2, thumbnailTopMargin / 2, 0, 0);
- }
-
- @Override
- public void setIconAppChipChildrenParams(FrameLayout.LayoutParams iconParams,
- int chipChildMarginStart) {
- iconParams.setMargins(0, 0, 0, 0);
- iconParams.setMarginStart(chipChildMarginStart);
- iconParams.gravity = Gravity.START | Gravity.CENTER_VERTICAL;
- }
-
- @Override
- public void setIconAppChipMenuParams(IconAppChipView iconAppChipView,
- FrameLayout.LayoutParams iconMenuParams, int iconMenuMargin, int thumbnailTopMargin) {
- boolean isRtl = iconAppChipView.getLayoutDirection() == LAYOUT_DIRECTION_RTL;
- iconMenuParams.gravity = (isRtl ? TOP : BOTTOM) | (isRtl ? END : START);
- iconMenuParams.setMarginStart(0);
- iconMenuParams.topMargin = isRtl ? iconMenuMargin : 0;
- iconMenuParams.bottomMargin = 0;
- iconMenuParams.setMarginEnd(isRtl ? thumbnailTopMargin : 0);
-
- // Use half menu height to place the pivot within the X/Y center of icon in the menu.
- float iconCenter = iconAppChipView.getHeight() / 2f;
- iconAppChipView.setPivotX(isRtl ? iconMenuParams.width / 2f : iconCenter);
- iconAppChipView.setPivotY(
- isRtl ? iconMenuParams.width / 2f : iconCenter - iconMenuMargin);
- iconAppChipView.setSplitTranslationY(0);
- iconAppChipView.setRotation(getDegreesRotated());
- }
-
- @Override
- public void setSplitIconParams(View primaryIconView, View secondaryIconView,
- int taskIconHeight, int primarySnapshotWidth, int primarySnapshotHeight,
- int groupedTaskViewHeight, int groupedTaskViewWidth, boolean isRtl,
- DeviceProfile deviceProfile, SplitBounds splitConfig) {
- super.setSplitIconParams(primaryIconView, secondaryIconView, taskIconHeight,
- primarySnapshotWidth, primarySnapshotHeight, groupedTaskViewHeight,
- groupedTaskViewWidth, isRtl, deviceProfile, splitConfig);
- FrameLayout.LayoutParams primaryIconParams =
- (FrameLayout.LayoutParams) primaryIconView.getLayoutParams();
- FrameLayout.LayoutParams secondaryIconParams =
- (FrameLayout.LayoutParams) secondaryIconView.getLayoutParams();
-
- // We calculate the "midpoint" of the thumbnail area, and place the icons there.
- // This is the place where the thumbnail area splits by default, in a near-50/50 split.
- // It is usually not exactly 50/50, due to insets/screen cutouts.
- int fullscreenInsetThickness = deviceProfile.getInsets().top
- - deviceProfile.getInsets().bottom;
- int fullscreenMidpointFromBottom = ((deviceProfile.heightPx
- - fullscreenInsetThickness) / 2);
- float midpointFromBottomPct = (float) fullscreenMidpointFromBottom / deviceProfile.heightPx;
- float insetPct = (float) fullscreenInsetThickness / deviceProfile.heightPx;
- int spaceAboveSnapshots = deviceProfile.overviewTaskThumbnailTopMarginPx;
- int overviewThumbnailAreaThickness = groupedTaskViewHeight - spaceAboveSnapshots;
- int bottomToMidpointOffset = (int) (overviewThumbnailAreaThickness * midpointFromBottomPct);
- int insetOffset = (int) (overviewThumbnailAreaThickness * insetPct);
-
- int gravity = (isRtl ? TOP : BOTTOM) | (isRtl ? END : START);
- primaryIconParams.gravity = gravity;
- secondaryIconParams.gravity = gravity;
- primaryIconView.setTranslationX(0);
- secondaryIconView.setTranslationX(0);
- if (enableOverviewIconMenu()) {
- IconAppChipView primaryAppChipView = (IconAppChipView) primaryIconView;
- IconAppChipView secondaryAppChipView = (IconAppChipView) secondaryIconView;
- if (isRtl) {
- primaryAppChipView.setSplitTranslationY(
- groupedTaskViewHeight - primarySnapshotHeight);
- secondaryAppChipView.setSplitTranslationY(0);
- } else {
- secondaryAppChipView.setSplitTranslationY(-primarySnapshotHeight);
- primaryAppChipView.setSplitTranslationY(0);
- }
- } else if (splitConfig.initiatedFromSeascape) {
- // if the split was initiated from seascape,
- // the task on the right (secondary) is slightly larger
- if (isRtl) {
- primaryIconView.setTranslationY(bottomToMidpointOffset - insetOffset
- + taskIconHeight);
- secondaryIconView.setTranslationY(bottomToMidpointOffset - insetOffset);
- } else {
- primaryIconView.setTranslationY(-bottomToMidpointOffset - insetOffset
- + taskIconHeight);
- secondaryIconView.setTranslationY(-bottomToMidpointOffset - insetOffset);
- }
- } else {
- // if not,
- // the task on the left (primary) is slightly larger
- if (isRtl) {
- primaryIconView.setTranslationY(bottomToMidpointOffset + taskIconHeight);
- secondaryIconView.setTranslationY(bottomToMidpointOffset);
- } else {
- primaryIconView.setTranslationY(-bottomToMidpointOffset + taskIconHeight);
- secondaryIconView.setTranslationY(-bottomToMidpointOffset);
- }
- }
-
- primaryIconView.setLayoutParams(primaryIconParams);
- secondaryIconView.setLayoutParams(secondaryIconParams);
- }
-
- @Override
- public void measureGroupedTaskViewThumbnailBounds(View primarySnapshot, View secondarySnapshot,
- int parentWidth, int parentHeight, SplitBounds splitBoundsConfig, DeviceProfile dp,
- boolean isRtl) {
- FrameLayout.LayoutParams primaryParams =
- (FrameLayout.LayoutParams) primarySnapshot.getLayoutParams();
- FrameLayout.LayoutParams secondaryParams =
- (FrameLayout.LayoutParams) secondarySnapshot.getLayoutParams();
-
- // Swap the margins that are set in TaskView#setRecentsOrientedState()
- secondaryParams.topMargin = dp.overviewTaskThumbnailTopMarginPx;
- primaryParams.topMargin = 0;
-
- // Measure and layout the thumbnails bottom up, since the primary is on the visual left
- // (portrait bottom) and secondary is on the right (portrait top)
- int spaceAboveSnapshot = dp.overviewTaskThumbnailTopMarginPx;
- int totalThumbnailHeight = parentHeight - spaceAboveSnapshot;
- int dividerBar = Math.round(totalThumbnailHeight * (splitBoundsConfig.appsStackedVertically
- ? splitBoundsConfig.dividerHeightPercent
- : splitBoundsConfig.dividerWidthPercent));
-
- Pair<Point, Point> taskViewSizes =
- getGroupedTaskViewSizes(dp, splitBoundsConfig, parentWidth, parentHeight);
-
- secondarySnapshot.setTranslationY(0);
- primarySnapshot.setTranslationY(taskViewSizes.second.y + spaceAboveSnapshot + dividerBar);
-
- primarySnapshot.measure(
- View.MeasureSpec.makeMeasureSpec(taskViewSizes.first.x, View.MeasureSpec.EXACTLY),
- View.MeasureSpec.makeMeasureSpec(taskViewSizes.first.y, View.MeasureSpec.EXACTLY)
- );
- secondarySnapshot.measure(
- View.MeasureSpec.makeMeasureSpec(taskViewSizes.second.x, View.MeasureSpec.EXACTLY),
- View.MeasureSpec.makeMeasureSpec(taskViewSizes.second.y, View.MeasureSpec.EXACTLY)
- );
- }
-
- @Override
- public Pair<Point, Point> getGroupedTaskViewSizes(
- DeviceProfile dp,
- SplitBounds splitBoundsConfig,
- int parentWidth,
- int parentHeight) {
- // Measure and layout the thumbnails bottom up, since the primary is on the visual left
- // (portrait bottom) and secondary is on the right (portrait top)
- int spaceAboveSnapshot = dp.overviewTaskThumbnailTopMarginPx;
- int totalThumbnailHeight = parentHeight - spaceAboveSnapshot;
- int dividerBar = Math.round(totalThumbnailHeight * (splitBoundsConfig.appsStackedVertically
- ? splitBoundsConfig.dividerHeightPercent
- : splitBoundsConfig.dividerWidthPercent));
- float taskPercent = splitBoundsConfig.appsStackedVertically
- ? splitBoundsConfig.topTaskPercent
- : splitBoundsConfig.leftTaskPercent;
-
- Point firstTaskViewSize = new Point(
- parentWidth,
- (int) (totalThumbnailHeight * taskPercent)
- );
- Point secondTaskViewSize = new Point(
- parentWidth,
- totalThumbnailHeight - firstTaskViewSize.y - dividerBar
- );
-
- return new Pair<>(firstTaskViewSize, secondTaskViewSize);
- }
-
- /* ---------- The following are only used by TaskViewTouchHandler. ---------- */
-
- @Override
- public SingleAxisSwipeDetector.Direction getUpDownSwipeDirection() {
- return HORIZONTAL;
- }
-
- @Override
- public int getUpDirection(boolean isRtl) {
- return isRtl ? SingleAxisSwipeDetector.DIRECTION_POSITIVE
- : SingleAxisSwipeDetector.DIRECTION_NEGATIVE;
- }
-
- @Override
- public boolean isGoingUp(float displacement, boolean isRtl) {
- return isRtl ? displacement > 0 : displacement < 0;
- }
-
- @Override
- public int getTaskDragDisplacementFactor(boolean isRtl) {
- return isRtl ? -1 : 1;
- }
-
- /* -------------------- */
-}
diff --git a/quickstep/src/com/android/quickstep/orientation/SeascapePagedViewHandler.kt b/quickstep/src/com/android/quickstep/orientation/SeascapePagedViewHandler.kt
new file mode 100644
index 0000000..5bebf8c
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/orientation/SeascapePagedViewHandler.kt
@@ -0,0 +1,398 @@
+/*
+ * 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.orientation
+
+import android.annotation.SuppressLint
+import android.content.res.Resources
+import android.graphics.Point
+import android.graphics.PointF
+import android.graphics.Rect
+import android.util.Pair
+import android.view.Gravity
+import android.view.Surface
+import android.view.View
+import android.view.View.MeasureSpec
+import android.widget.FrameLayout
+import androidx.core.util.component1
+import androidx.core.util.component2
+import com.android.launcher3.DeviceProfile
+import com.android.launcher3.Flags
+import com.android.launcher3.R
+import com.android.launcher3.Utilities
+import com.android.launcher3.touch.SingleAxisSwipeDetector
+import com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT
+import com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT
+import com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_UNDEFINED
+import com.android.launcher3.util.SplitConfigurationOptions.STAGE_TYPE_MAIN
+import com.android.launcher3.util.SplitConfigurationOptions.SplitBounds
+import com.android.launcher3.util.SplitConfigurationOptions.SplitPositionOption
+import com.android.launcher3.views.BaseDragLayer
+import com.android.quickstep.views.IconAppChipView
+
+class SeascapePagedViewHandler : LandscapePagedViewHandler() {
+ override val secondaryTranslationDirectionFactor: Int = -1
+
+ override fun getSplitTranslationDirectionFactor(
+ stagePosition: Int,
+ deviceProfile: DeviceProfile
+ ): Int = if (stagePosition == STAGE_POSITION_BOTTOM_OR_RIGHT) -1 else 1
+
+ override fun getRecentsRtlSetting(resources: Resources): Boolean = Utilities.isRtl(resources)
+
+ override val degreesRotated: Float = 270f
+
+ override val rotation: Int = Surface.ROTATION_270
+
+ override fun adjustFloatingIconStartVelocity(velocity: PointF) =
+ velocity.set(velocity.y, -velocity.x)
+
+ override fun getTaskMenuX(
+ x: Float,
+ thumbnailView: View,
+ deviceProfile: DeviceProfile,
+ taskInsetMargin: Float,
+ taskViewIcon: View
+ ): Float = x + taskInsetMargin
+
+ override fun getTaskMenuY(
+ y: Float,
+ thumbnailView: View,
+ stagePosition: Int,
+ taskMenuView: View,
+ taskInsetMargin: Float,
+ taskViewIcon: View
+ ): Float {
+ if (Flags.enableOverviewIconMenu()) {
+ return y
+ }
+ val lp = taskMenuView.layoutParams as BaseDragLayer.LayoutParams
+ val taskMenuWidth = lp.width
+ return if (stagePosition == STAGE_POSITION_UNDEFINED) {
+ y + taskInsetMargin + (thumbnailView.measuredHeight + taskMenuWidth) / 2f
+ } else {
+ y + taskMenuWidth + taskInsetMargin
+ }
+ }
+
+ override fun getTaskMenuHeight(
+ taskInsetMargin: Float,
+ deviceProfile: DeviceProfile,
+ taskMenuX: Float,
+ taskMenuY: Float
+ ): Int = (deviceProfile.availableWidthPx - taskInsetMargin - taskMenuX).toInt()
+
+ override fun setSplitTaskSwipeRect(
+ dp: DeviceProfile,
+ outRect: Rect,
+ splitInfo: SplitBounds,
+ desiredStagePosition: Int
+ ) {
+ val topLeftTaskPercent: Float
+ val dividerBarPercent: Float
+ if (splitInfo.appsStackedVertically) {
+ topLeftTaskPercent = splitInfo.topTaskPercent
+ dividerBarPercent = splitInfo.dividerHeightPercent
+ } else {
+ topLeftTaskPercent = splitInfo.leftTaskPercent
+ dividerBarPercent = splitInfo.dividerWidthPercent
+ }
+
+ // In seascape, the primary thumbnail is counterintuitively placed at the physical bottom of
+ // the screen. This is to preserve consistency when the user rotates: From the user's POV,
+ // the primary should always be on the left.
+ if (desiredStagePosition == STAGE_POSITION_TOP_OR_LEFT) {
+ outRect.top += (outRect.height() * (1 - topLeftTaskPercent)).toInt()
+ } else {
+ outRect.bottom -= (outRect.height() * (topLeftTaskPercent + dividerBarPercent)).toInt()
+ }
+ }
+
+ override fun getDwbLayoutTranslations(
+ taskViewWidth: Int,
+ taskViewHeight: Int,
+ splitBounds: SplitBounds?,
+ deviceProfile: DeviceProfile,
+ thumbnailViews: Array<View>,
+ desiredTaskId: Int,
+ banner: View
+ ): Pair<Float, Float> {
+ val snapshotParams = thumbnailViews[0].layoutParams as FrameLayout.LayoutParams
+ val isRtl = banner.layoutDirection == View.LAYOUT_DIRECTION_RTL
+
+ val bannerParams = banner.layoutParams as FrameLayout.LayoutParams
+ bannerParams.gravity = Gravity.BOTTOM or if (isRtl) Gravity.END else Gravity.START
+ banner.pivotX = 0f
+ banner.pivotY = 0f
+ banner.rotation = degreesRotated
+
+ val translationX: Float = (taskViewWidth - banner.height).toFloat()
+ if (splitBounds == null) {
+ // Single, fullscreen case
+ bannerParams.width = taskViewHeight - snapshotParams.topMargin
+ return Pair(translationX, banner.height.toFloat())
+ }
+
+ // Set correct width and translations
+ val translationY: Float
+ if (desiredTaskId == splitBounds.leftTopTaskId) {
+ bannerParams.width = thumbnailViews[0].measuredHeight
+ val bottomRightTaskPlusDividerPercent =
+ if (splitBounds.appsStackedVertically) {
+ 1f - splitBounds.topTaskPercent
+ } else {
+ 1f - splitBounds.leftTaskPercent
+ }
+ translationY =
+ banner.height -
+ (taskViewHeight - snapshotParams.topMargin) * bottomRightTaskPlusDividerPercent
+ } else {
+ bannerParams.width = thumbnailViews[1].measuredHeight
+ translationY = banner.height.toFloat()
+ }
+
+ return Pair(translationX, translationY)
+ }
+
+ override fun getDistanceToBottomOfRect(dp: DeviceProfile, rect: Rect): Int =
+ dp.widthPx - rect.right
+
+ override fun getSplitPositionOptions(dp: DeviceProfile): List<SplitPositionOption> =
+ // Add "right" option which is actually the top
+ listOf(
+ SplitPositionOption(
+ R.drawable.ic_split_horizontal,
+ R.string.recent_task_option_split_screen,
+ STAGE_POSITION_BOTTOM_OR_RIGHT,
+ STAGE_TYPE_MAIN
+ )
+ )
+
+ override fun setSplitInstructionsParams(
+ out: View,
+ dp: DeviceProfile,
+ splitInstructionsHeight: Int,
+ splitInstructionsWidth: Int
+ ) {
+ out.pivotX = 0f
+ out.pivotY = splitInstructionsHeight.toFloat()
+ out.rotation = degreesRotated
+ val distanceToEdge =
+ out.resources.getDimensionPixelSize(
+ R.dimen.split_instructions_bottom_margin_phone_landscape
+ )
+ // Adjust for any insets on the right edge
+ val insetCorrectionX = dp.insets.right
+ // Center the view in case of unbalanced insets on top or bottom of screen
+ val insetCorrectionY = (dp.insets.bottom - dp.insets.top) / 2
+ out.translationX = (splitInstructionsWidth - distanceToEdge + insetCorrectionX).toFloat()
+ out.translationY =
+ (-splitInstructionsHeight + splitInstructionsWidth) / 2f + insetCorrectionY
+ // Setting gravity to RIGHT instead of the lint-recommended END because we always want this
+ // view to be screen-right when phone is in seascape, regardless of the RtL setting.
+ val lp = out.layoutParams as FrameLayout.LayoutParams
+ lp.gravity = Gravity.RIGHT or Gravity.CENTER_VERTICAL
+ out.layoutParams = lp
+ }
+
+ override fun setTaskIconParams(
+ iconParams: FrameLayout.LayoutParams,
+ taskIconMargin: Int,
+ taskIconHeight: Int,
+ thumbnailTopMargin: Int,
+ isRtl: Boolean
+ ) {
+ iconParams.gravity =
+ if (isRtl) {
+ Gravity.END or Gravity.CENTER_VERTICAL
+ } else {
+ Gravity.START or Gravity.CENTER_VERTICAL
+ }
+ iconParams.setMargins(-taskIconHeight - taskIconMargin / 2, thumbnailTopMargin / 2, 0, 0)
+ }
+
+ override fun setIconAppChipChildrenParams(
+ iconParams: FrameLayout.LayoutParams,
+ chipChildMarginStart: Int
+ ) {
+ iconParams.setMargins(0, 0, 0, 0)
+ iconParams.marginStart = chipChildMarginStart
+ iconParams.gravity = Gravity.START or Gravity.CENTER_VERTICAL
+ }
+
+ override fun setIconAppChipMenuParams(
+ iconAppChipView: IconAppChipView,
+ iconMenuParams: FrameLayout.LayoutParams,
+ iconMenuMargin: Int,
+ thumbnailTopMargin: Int
+ ) {
+ val isRtl = iconAppChipView.layoutDirection == View.LAYOUT_DIRECTION_RTL
+ val iconCenter = iconAppChipView.getHeight() / 2f
+
+ if (isRtl) {
+ iconMenuParams.gravity = Gravity.TOP or Gravity.END
+ iconMenuParams.topMargin = iconMenuMargin
+ iconMenuParams.marginEnd = thumbnailTopMargin
+ // Use half menu height to place the pivot within the X/Y center of icon in the menu.
+ iconAppChipView.pivotX = iconMenuParams.width / 2f
+ iconAppChipView.pivotY = iconMenuParams.width / 2f
+ } else {
+ iconMenuParams.gravity = Gravity.BOTTOM or Gravity.START
+ iconMenuParams.topMargin = 0
+ iconMenuParams.marginEnd = 0
+ iconAppChipView.pivotX = iconCenter
+ iconAppChipView.pivotY = iconCenter - iconMenuMargin
+ }
+ iconMenuParams.marginStart = 0
+ iconMenuParams.bottomMargin = 0
+ iconAppChipView.setSplitTranslationY(0f)
+ iconAppChipView.setRotation(degreesRotated)
+ }
+
+ override fun measureGroupedTaskViewThumbnailBounds(
+ primarySnapshot: View,
+ secondarySnapshot: View,
+ parentWidth: Int,
+ parentHeight: Int,
+ splitBoundsConfig: SplitBounds,
+ dp: DeviceProfile,
+ isRtl: Boolean
+ ) {
+ val primaryParams = primarySnapshot.layoutParams as FrameLayout.LayoutParams
+ val secondaryParams = secondarySnapshot.layoutParams as FrameLayout.LayoutParams
+
+ // Swap the margins that are set in TaskView#setRecentsOrientedState()
+ secondaryParams.topMargin = dp.overviewTaskThumbnailTopMarginPx
+ primaryParams.topMargin = 0
+
+ // Measure and layout the thumbnails bottom up, since the primary is on the visual left
+ // (portrait bottom) and secondary is on the right (portrait top)
+ val spaceAboveSnapshot = dp.overviewTaskThumbnailTopMarginPx
+ val totalThumbnailHeight = parentHeight - spaceAboveSnapshot
+ val dividerBar = getDividerBarSize(totalThumbnailHeight, splitBoundsConfig)
+
+ val (taskViewFirst, taskViewSecond) =
+ getGroupedTaskViewSizes(dp, splitBoundsConfig, parentWidth, parentHeight)
+ secondarySnapshot.translationY = 0f
+ primarySnapshot.translationY =
+ (taskViewSecond.y + spaceAboveSnapshot + dividerBar).toFloat()
+ primarySnapshot.measure(
+ MeasureSpec.makeMeasureSpec(taskViewFirst.x, MeasureSpec.EXACTLY),
+ MeasureSpec.makeMeasureSpec(taskViewFirst.y, MeasureSpec.EXACTLY)
+ )
+ secondarySnapshot.measure(
+ MeasureSpec.makeMeasureSpec(taskViewSecond.x, MeasureSpec.EXACTLY),
+ MeasureSpec.makeMeasureSpec(taskViewSecond.y, MeasureSpec.EXACTLY)
+ )
+ }
+
+ override fun getGroupedTaskViewSizes(
+ dp: DeviceProfile,
+ splitBoundsConfig: SplitBounds,
+ parentWidth: Int,
+ parentHeight: Int
+ ): Pair<Point, Point> {
+ // Measure and layout the thumbnails bottom up, since the primary is on the visual left
+ // (portrait bottom) and secondary is on the right (portrait top)
+ val spaceAboveSnapshot = dp.overviewTaskThumbnailTopMarginPx
+ val totalThumbnailHeight = parentHeight - spaceAboveSnapshot
+ val dividerBar = getDividerBarSize(totalThumbnailHeight, splitBoundsConfig)
+
+ val taskPercent =
+ if (splitBoundsConfig.appsStackedVertically) {
+ splitBoundsConfig.topTaskPercent
+ } else {
+ splitBoundsConfig.leftTaskPercent
+ }
+ val firstTaskViewSize = Point(parentWidth, (totalThumbnailHeight * taskPercent).toInt())
+ val secondTaskViewSize =
+ Point(parentWidth, totalThumbnailHeight - firstTaskViewSize.y - dividerBar)
+ return Pair(firstTaskViewSize, secondTaskViewSize)
+ }
+
+ /* ---------- The following are only used by TaskViewTouchHandler. ---------- */
+ override val upDownSwipeDirection: SingleAxisSwipeDetector.Direction =
+ SingleAxisSwipeDetector.HORIZONTAL
+
+ override fun getUpDirection(isRtl: Boolean): Int =
+ if (isRtl) SingleAxisSwipeDetector.DIRECTION_POSITIVE
+ else SingleAxisSwipeDetector.DIRECTION_NEGATIVE
+
+ override fun isGoingUp(displacement: Float, isRtl: Boolean): Boolean =
+ if (isRtl) displacement > 0 else displacement < 0
+
+ override fun getTaskDragDisplacementFactor(isRtl: Boolean): Int = if (isRtl) -1 else 1
+ /* -------------------- */
+
+ override fun getSplitIconsPosition(
+ taskIconHeight: Int,
+ primarySnapshotHeight: Int,
+ totalThumbnailHeight: Int,
+ isRtl: Boolean,
+ overviewTaskMarginPx: Int,
+ dividerSize: Int,
+ ): SplitIconPositions {
+ return if (Flags.enableOverviewIconMenu()) {
+ if (isRtl) {
+ SplitIconPositions(
+ topLeftY = totalThumbnailHeight - primarySnapshotHeight,
+ bottomRightY = 0
+ )
+ } else {
+ SplitIconPositions(
+ topLeftY = 0,
+ bottomRightY = -(primarySnapshotHeight + dividerSize)
+ )
+ }
+ } else {
+ // In seascape, the icons are initially placed at the bottom start of the
+ // display (portrait locked). The values defined here are used to translate the icons
+ // from the bottom to the almost-center of the screen using the bottom margin.
+ // The primary snapshot is placed at the bottom, thus we translate the icons using
+ // the size of the primary snapshot minus the icon size for the top-left icon.
+ SplitIconPositions(
+ topLeftY = primarySnapshotHeight - taskIconHeight,
+ bottomRightY = primarySnapshotHeight + dividerSize
+ )
+ }
+ }
+
+ /**
+ * Updates icon view gravity and translation for split tasks
+ *
+ * @param iconView View to be updated
+ * @param translationY the translationY that should be applied
+ * @param isRtl Whether the layout direction is RTL (or false for LTR).
+ */
+ @SuppressLint("RtlHardcoded")
+ override fun updateSplitIconsPosition(iconView: View, translationY: Int, isRtl: Boolean) {
+ val layoutParams = iconView.layoutParams as FrameLayout.LayoutParams
+
+ if (Flags.enableOverviewIconMenu()) {
+ val appChipView = iconView as IconAppChipView
+ layoutParams.gravity =
+ if (isRtl) Gravity.TOP or Gravity.END else Gravity.BOTTOM or Gravity.START
+ appChipView.layoutParams = layoutParams
+ appChipView.setSplitTranslationX(0f)
+ appChipView.setSplitTranslationY(translationY.toFloat())
+ } else {
+ layoutParams.gravity = Gravity.BOTTOM or Gravity.LEFT
+ iconView.translationX = 0f
+ iconView.translationY = 0f
+ layoutParams.bottomMargin = translationY
+ iconView.layoutParams = layoutParams
+ }
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/util/AppPairsController.java b/quickstep/src/com/android/quickstep/util/AppPairsController.java
index 757f1f8..ecb6118 100644
--- a/quickstep/src/com/android/quickstep/util/AppPairsController.java
+++ b/quickstep/src/com/android/quickstep/util/AppPairsController.java
@@ -19,6 +19,7 @@
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
+import static com.android.internal.jank.Cuj.CUJ_LAUNCHER_LAUNCH_APP_PAIR_FROM_TASKBAR;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_APP_PAIR_LAUNCH;
import static com.android.launcher3.model.data.AppInfo.PACKAGE_KEY_COMPARATOR;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
@@ -40,6 +41,7 @@
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
+import com.android.internal.jank.Cuj;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherSettings;
@@ -62,6 +64,7 @@
import com.android.quickstep.views.GroupedTaskView;
import com.android.quickstep.views.TaskView;
import com.android.systemui.shared.recents.model.Task;
+import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
import com.android.wm.shell.common.split.SplitScreenConstants.PersistentSnapPosition;
import java.util.Arrays;
@@ -112,6 +115,7 @@
* well on trampoline apps).
*/
public void saveAppPair(GroupedTaskView gtv) {
+ InteractionJankMonitorWrapper.begin(gtv, Cuj.CUJ_LAUNCHER_SAVE_APP_PAIR);
TaskView.TaskIdAttributeContainer[] attributes = gtv.getTaskIdAttributeContainers();
WorkspaceItemInfo recentsInfo1 = attributes[0].getItemInfo();
WorkspaceItemInfo recentsInfo2 = attributes[1].getItemInfo();
@@ -168,7 +172,13 @@
LauncherAccessibilityDelegate delegate =
Launcher.getLauncher(mContext).getAccessibilityDelegate();
if (delegate != null) {
- delegate.addToWorkspace(newAppPair, true);
+ delegate.addToWorkspace(newAppPair, true, (success) -> {
+ if (success) {
+ InteractionJankMonitorWrapper.end(Cuj.CUJ_LAUNCHER_SAVE_APP_PAIR);
+ } else {
+ InteractionJankMonitorWrapper.cancel(Cuj.CUJ_LAUNCHER_SAVE_APP_PAIR);
+ }
+ });
mStatsLogManager.logger().withItemInfo(newAppPair)
.log(StatsLogManager.LauncherEvent.LAUNCHER_APP_PAIR_SAVE);
}
@@ -179,12 +189,18 @@
/**
* Launches an app pair by searching the RecentsModel for running instances of each app, and
* staging either those running instances or launching the apps as new Intents.
+ *
+ * @param cuj Should be an integer from {@link Cuj} or -1 if no CUJ needs to be logged for jank
+ * monitoring
*/
- public void launchAppPair(AppPairIcon appPairIcon) {
+ public void launchAppPair(AppPairIcon appPairIcon, int cuj) {
WorkspaceItemInfo app1 = appPairIcon.getInfo().contents.get(0);
WorkspaceItemInfo app2 = appPairIcon.getInfo().contents.get(1);
ComponentKey app1Key = new ComponentKey(app1.getTargetComponent(), app1.user);
ComponentKey app2Key = new ComponentKey(app2.getTargetComponent(), app2.user);
+ mSplitSelectStateController.setLaunchingCuj(cuj);
+ InteractionJankMonitorWrapper.begin(appPairIcon, cuj);
+
mSplitSelectStateController.findLastActiveTasksAndRunCallback(
Arrays.asList(app1Key, app2Key),
false /* findExactPairMatch */,
@@ -343,7 +359,8 @@
&& !lastActiveTasksOfAppPair.contains(runningTaskId2)) {
// Neither A nor B are on screen, so just launch a new app pair
// normally.
- launchAppPair(launchingIconView);
+ launchAppPair(launchingIconView,
+ CUJ_LAUNCHER_LAUNCH_APP_PAIR_FROM_TASKBAR);
} else {
// Exactly one app (A or B) is on-screen, so we have to launch the other
// on the appropriate side.
@@ -388,7 +405,8 @@
if (!task1IsOnScreen && !task2IsOnScreen) {
// Neither App A nor App B are on-screen, launch the app pair normally.
- launchAppPair(launchingIconView);
+ launchAppPair(launchingIconView,
+ CUJ_LAUNCHER_LAUNCH_APP_PAIR_FROM_TASKBAR);
} else {
// Either A or B is on-screen, so launch the other on the appropriate
// side.
diff --git a/quickstep/src/com/android/quickstep/util/AssistStateManager.java b/quickstep/src/com/android/quickstep/util/AssistStateManager.java
index e9a06f7..a854656 100644
--- a/quickstep/src/com/android/quickstep/util/AssistStateManager.java
+++ b/quickstep/src/com/android/quickstep/util/AssistStateManager.java
@@ -52,18 +52,8 @@
return Optional.empty();
}
- /** Whether search recovery is available. */
- public boolean isVisRecoveryEnabled() {
- return false;
- }
-
/** Return {@code true} if the Settings toggle is enabled. */
- public boolean isSettingsNavHandleEnabled() {
- return false;
- }
-
- /** Return {@code true} if the Settings toggle is enabled. */
- public boolean isSettingsHomeButtonEnabled() {
+ public boolean isSettingsAllEntrypointsEnabled() {
return false;
}
diff --git a/quickstep/src/com/android/quickstep/util/BaseDepthController.java b/quickstep/src/com/android/quickstep/util/BaseDepthController.java
index 99f564c..5d6bb1d 100644
--- a/quickstep/src/com/android/quickstep/util/BaseDepthController.java
+++ b/quickstep/src/com/android/quickstep/util/BaseDepthController.java
@@ -15,6 +15,8 @@
*/
package com.android.quickstep.util;
+import static com.android.launcher3.Flags.enableScalingRevealHomeAnimation;
+
import android.app.WallpaperManager;
import android.os.IBinder;
import android.util.FloatProperty;
@@ -33,6 +35,9 @@
* Utility class for applying depth effect
*/
public class BaseDepthController {
+ public static final float DEPTH_0_PERCENT = 0f;
+ public static final float DEPTH_60_PERCENT = 0.6f;
+ public static final float DEPTH_70_PERCENT = 0.7f;
private static final FloatProperty<BaseDepthController> DEPTH =
new FloatProperty<BaseDepthController>("depth") {
@@ -127,10 +132,14 @@
float depth = mDepth;
IBinder windowToken = mLauncher.getRootView().getWindowToken();
if (windowToken != null) {
- // The API's full zoom-out is three times larger than the zoom-out we apply to the
- // icons. To keep the two consistent throughout the animation while keeping Launcher's
- // concept of full depth unchanged, we divide the depth by 3 here.
- mWallpaperManager.setWallpaperZoomOut(windowToken, depth / 3);
+ if (enableScalingRevealHomeAnimation()) {
+ mWallpaperManager.setWallpaperZoomOut(windowToken, depth);
+ } else {
+ // The API's full zoom-out is three times larger than the zoom-out we apply to the
+ // icons. To keep the two consistent throughout the animation while keeping
+ // Launcher's concept of full depth unchanged, we divide the depth by 3 here.
+ mWallpaperManager.setWallpaperZoomOut(windowToken, depth / 3);
+ }
}
if (!BlurUtils.supportsBlursOnWindows()) {
@@ -150,8 +159,15 @@
boolean hasOpaqueBg = mLauncher.getScrimView().isFullyOpaque();
boolean isSurfaceOpaque = !mHasContentBehindLauncher && hasOpaqueBg && !mPauseBlurs;
+ float blurAmount;
+ if (enableScalingRevealHomeAnimation()) {
+ blurAmount = mapDepthToBlur(depth);
+ } else {
+ blurAmount = depth;
+ }
mCurrentBlur = !mCrossWindowBlursEnabled || hasOpaqueBg || mPauseBlurs
- ? 0 : (int) (depth * mMaxBlurRadius);
+ ? 0 : (int) (blurAmount * mMaxBlurRadius);
+
SurfaceControl.Transaction transaction = new SurfaceControl.Transaction()
.setBackgroundBlurRadius(mSurface, mCurrentBlur)
.setOpaque(mSurface, isSurfaceOpaque);
@@ -197,4 +213,12 @@
applyDepthAndBlur();
}
}
+
+ /**
+ * Maps depth values to blur amounts as a percentage of the max blur.
+ * The blur percentage grows linearly with depth, and maxes out at 30% depth.
+ */
+ private static float mapDepthToBlur(float depth) {
+ return Math.min(3 * depth, 1f);
+ }
}
diff --git a/quickstep/src/com/android/quickstep/util/FadeOutRemoteTransition.kt b/quickstep/src/com/android/quickstep/util/FadeOutRemoteTransition.kt
index 32a15a2..24e261f 100644
--- a/quickstep/src/com/android/quickstep/util/FadeOutRemoteTransition.kt
+++ b/quickstep/src/com/android/quickstep/util/FadeOutRemoteTransition.kt
@@ -37,12 +37,7 @@
mergeTarget: IBinder,
finishCB: IRemoteTransitionFinishedCallback
) {
-
- try {
- finishCB.onTransitionFinished(null, Transaction())
- } catch (e: RemoteException) {
- // Ignore
- }
+ // Do not report finish if we don't know how to handle this transition.
}
override fun startAnimation(
diff --git a/quickstep/src/com/android/quickstep/util/RectFSpringAnim.java b/quickstep/src/com/android/quickstep/util/RectFSpringAnim.java
index 245dde2..c39056d 100644
--- a/quickstep/src/com/android/quickstep/util/RectFSpringAnim.java
+++ b/quickstep/src/com/android/quickstep/util/RectFSpringAnim.java
@@ -15,6 +15,8 @@
*/
package com.android.quickstep.util;
+import static com.android.launcher3.Flags.enableScalingRevealHomeAnimation;
+
import static java.lang.annotation.RetentionPolicy.SOURCE;
import android.animation.Animator;
@@ -104,6 +106,8 @@
private float mCurrentScaleProgress;
private FlingSpringAnim mRectXAnim;
private FlingSpringAnim mRectYAnim;
+ private SpringAnimation mRectXSpring;
+ private SpringAnimation mRectYSpring;
private SpringAnimation mRectScaleAnim;
private boolean mAnimsStarted;
private boolean mRectXAnimEnded;
@@ -166,27 +170,47 @@
}
public void onTargetPositionChanged() {
- if (mRectXAnim != null && mRectXAnim.getTargetPosition() != mTargetRect.centerX()) {
- mRectXAnim.updatePosition(mCurrentCenterX, mTargetRect.centerX());
- }
+ if (enableScalingRevealHomeAnimation()) {
+ if (mRectXSpring != null) {
+ mRectXSpring.animateToFinalPosition(mTargetRect.centerX());
+ }
- if (mRectYAnim != null) {
- switch (mTracking) {
- case TRACKING_TOP:
- if (mRectYAnim.getTargetPosition() != mTargetRect.top) {
- mRectYAnim.updatePosition(mCurrentY, mTargetRect.top);
- }
- break;
- case TRACKING_BOTTOM:
- if (mRectYAnim.getTargetPosition() != mTargetRect.bottom) {
- mRectYAnim.updatePosition(mCurrentY, mTargetRect.bottom);
- }
- break;
- case TRACKING_CENTER:
- if (mRectYAnim.getTargetPosition() != mTargetRect.centerY()) {
- mRectYAnim.updatePosition(mCurrentY, mTargetRect.centerY());
- }
- break;
+ if (mRectYSpring != null) {
+ switch (mTracking) {
+ case TRACKING_TOP:
+ mRectYSpring.animateToFinalPosition(mTargetRect.top);
+ break;
+ case TRACKING_BOTTOM:
+ mRectYSpring.animateToFinalPosition(mTargetRect.bottom);
+ break;
+ case TRACKING_CENTER:
+ mRectYSpring.animateToFinalPosition(mTargetRect.centerY());
+ break;
+ }
+ }
+ } else {
+ if (mRectXAnim != null && mRectXAnim.getTargetPosition() != mTargetRect.centerX()) {
+ mRectXAnim.updatePosition(mCurrentCenterX, mTargetRect.centerX());
+ }
+
+ if (mRectYAnim != null) {
+ switch (mTracking) {
+ case TRACKING_TOP:
+ if (mRectYAnim.getTargetPosition() != mTargetRect.top) {
+ mRectYAnim.updatePosition(mCurrentY, mTargetRect.top);
+ }
+ break;
+ case TRACKING_BOTTOM:
+ if (mRectYAnim.getTargetPosition() != mTargetRect.bottom) {
+ mRectYAnim.updatePosition(mCurrentY, mTargetRect.bottom);
+ }
+ break;
+ case TRACKING_CENTER:
+ if (mRectYAnim.getTargetPosition() != mTargetRect.centerY()) {
+ mRectYAnim.updatePosition(mCurrentY, mTargetRect.centerY());
+ }
+ break;
+ }
}
}
}
@@ -215,59 +239,126 @@
maybeOnEnd();
});
- // We dampen the user velocity here to keep the natural feeling and to prevent the
- // rect from straying too from a linear path.
- final float xVelocityPxPerS = velocityPxPerMs.x * 1000;
- final float yVelocityPxPerS = velocityPxPerMs.y * 1000;
- final float dampedXVelocityPxPerS = OverScroll.dampedScroll(
- Math.abs(xVelocityPxPerS), mMaxVelocityPxPerS) * Math.signum(xVelocityPxPerS);
- final float dampedYVelocityPxPerS = OverScroll.dampedScroll(
- Math.abs(yVelocityPxPerS), mMaxVelocityPxPerS) * Math.signum(yVelocityPxPerS);
-
+ float xVelocityPxPerS = velocityPxPerMs.x * 1000;
+ float yVelocityPxPerS = velocityPxPerMs.y * 1000;
float startX = mCurrentCenterX;
float endX = mTargetRect.centerX();
- float minXValue = Math.min(startX, endX);
- float maxXValue = Math.max(startX, endX);
-
- mRectXAnim = new FlingSpringAnim(this, context, RECT_CENTER_X, startX, endX,
- dampedXVelocityPxPerS, mMinVisChange, minXValue, maxXValue, mDampingX, mStiffnessX,
- onXEndListener);
-
float startY = mCurrentY;
float endY = getTrackedYFromRect(mTargetRect);
- float minYValue = Math.min(startY, endY);
- float maxYValue = Math.max(startY, endY);
- mRectYAnim = new FlingSpringAnim(this, context, RECT_Y, startY, endY, dampedYVelocityPxPerS,
- mMinVisChange, minYValue, maxYValue, mDampingY, mStiffnessY, onYEndListener);
-
float minVisibleChange = Math.abs(1f / mStartRect.height());
- ResourceProvider rp = DynamicResource.provider(context);
- float damping = rp.getFloat(R.dimen.swipe_up_rect_scale_damping_ratio);
- // Increase the stiffness for devices where we want the window size to transform quicker.
- boolean shouldUseHigherStiffness = profile != null
- && (profile.isLandscape || profile.isTablet);
- float stiffness = shouldUseHigherStiffness
- ? rp.getFloat(R.dimen.swipe_up_rect_scale_higher_stiffness)
- : rp.getFloat(R.dimen.swipe_up_rect_scale_stiffness);
+ if (enableScalingRevealHomeAnimation()) {
+ ResourceProvider rp = DynamicResource.provider(context);
+ long minVelocityXPxPerS = rp.getInt(R.dimen.swipe_up_min_velocity_x_px_per_s);
+ long maxVelocityXPxPerS = rp.getInt(R.dimen.swipe_up_max_velocity_x_px_per_s);
+ long minVelocityYPxPerS = rp.getInt(R.dimen.swipe_up_min_velocity_y_px_per_s);
+ long maxVelocityYPxPerS = rp.getInt(R.dimen.swipe_up_max_velocity_y_px_per_s);
+ float fallOffFactor = rp.getFloat(R.dimen.swipe_up_max_velocity_fall_off_factor);
- mRectScaleAnim = new SpringAnimation(this, RECT_SCALE_PROGRESS)
- .setSpring(new SpringForce(1f)
- .setDampingRatio(damping)
- .setStiffness(stiffness))
- .setStartVelocity(velocityPxPerMs.y * minVisibleChange)
- .setMaxValue(1f)
- .setMinimumVisibleChange(minVisibleChange)
- .addEndListener((animation, canceled, value, velocity) -> {
- mRectScaleAnimEnded = true;
- maybeOnEnd();
- });
+ // We want the actual initial velocity to never dip below the minimum, and to taper off
+ // once it's above the soft cap so that we can prevent the window from flying off
+ // screen, while maintaining a natural feel.
+ xVelocityPxPerS = adjustVelocity(
+ xVelocityPxPerS, minVelocityXPxPerS, maxVelocityXPxPerS, fallOffFactor);
+ yVelocityPxPerS = adjustVelocity(
+ yVelocityPxPerS, minVelocityYPxPerS, maxVelocityYPxPerS, fallOffFactor);
- setCanRelease(false);
- mAnimsStarted = true;
+ float stiffnessX = rp.getFloat(R.dimen.swipe_up_rect_x_stiffness);
+ float dampingX = rp.getFloat(R.dimen.swipe_up_rect_x_damping_ratio);
+ mRectXSpring =
+ new SpringAnimation(this, RECT_CENTER_X)
+ .setSpring(
+ new SpringForce(endX)
+ .setStiffness(stiffnessX)
+ .setDampingRatio(dampingX)
+ ).setStartValue(startX)
+ .setStartVelocity(xVelocityPxPerS)
+ .addEndListener(onXEndListener);
- mRectXAnim.start();
- mRectYAnim.start();
+ float stiffnessY = rp.getFloat(R.dimen.swipe_up_rect_y_stiffness);
+ float dampingY = rp.getFloat(R.dimen.swipe_up_rect_y_damping_ratio);
+ mRectYSpring =
+ new SpringAnimation(this, RECT_Y)
+ .setSpring(
+ new SpringForce(endY)
+ .setStiffness(stiffnessY)
+ .setDampingRatio(dampingY)
+ )
+ .setStartValue(startY)
+ .setStartVelocity(yVelocityPxPerS)
+ .addEndListener(onYEndListener);
+
+ float stiffnessZ = rp.getFloat(R.dimen.swipe_up_rect_scale_stiffness_v2);
+ float dampingZ = rp.getFloat(R.dimen.swipe_up_rect_scale_damping_ratio_v2);
+ mRectScaleAnim =
+ new SpringAnimation(this, RECT_SCALE_PROGRESS)
+ .setSpring(
+ new SpringForce(1f)
+ .setStiffness(stiffnessZ)
+ .setDampingRatio(dampingZ))
+ .setStartVelocity(velocityPxPerMs.y * minVisibleChange)
+ .setMinimumVisibleChange(minVisibleChange)
+ .addEndListener((animation, canceled, value, velocity) -> {
+ mRectScaleAnimEnded = true;
+ maybeOnEnd();
+ });
+
+ setCanRelease(false);
+ mAnimsStarted = true;
+
+ mRectXSpring.start();
+ mRectYSpring.start();
+ } else {
+ // We dampen the user velocity here to keep the natural feeling and to prevent the
+ // rect from straying too from a linear path.
+ final float dampedXVelocityPxPerS = OverScroll.dampedScroll(
+ Math.abs(xVelocityPxPerS), mMaxVelocityPxPerS) * Math.signum(xVelocityPxPerS);
+ final float dampedYVelocityPxPerS = OverScroll.dampedScroll(
+ Math.abs(yVelocityPxPerS), mMaxVelocityPxPerS) * Math.signum(yVelocityPxPerS);
+
+ float minXValue = Math.min(startX, endX);
+ float maxXValue = Math.max(startX, endX);
+
+ mRectXAnim = new FlingSpringAnim(this, context, RECT_CENTER_X, startX, endX,
+ dampedXVelocityPxPerS, mMinVisChange, minXValue, maxXValue, mDampingX,
+ mStiffnessX, onXEndListener);
+
+ float minYValue = Math.min(startY, endY);
+ float maxYValue = Math.max(startY, endY);
+ mRectYAnim = new FlingSpringAnim(this, context, RECT_Y, startY, endY,
+ dampedYVelocityPxPerS, mMinVisChange, minYValue, maxYValue, mDampingY,
+ mStiffnessY, onYEndListener);
+
+ ResourceProvider rp = DynamicResource.provider(context);
+ float damping = rp.getFloat(R.dimen.swipe_up_rect_scale_damping_ratio);
+
+ // Increase the stiffness for devices where we want the window size to transform
+ // quicker.
+ boolean shouldUseHigherStiffness = profile != null
+ && (profile.isLandscape || profile.isTablet);
+ float stiffness = shouldUseHigherStiffness
+ ? rp.getFloat(R.dimen.swipe_up_rect_scale_higher_stiffness)
+ : rp.getFloat(R.dimen.swipe_up_rect_scale_stiffness);
+
+ mRectScaleAnim = new SpringAnimation(this, RECT_SCALE_PROGRESS)
+ .setSpring(new SpringForce(1f)
+ .setDampingRatio(damping)
+ .setStiffness(stiffness))
+ .setStartVelocity(velocityPxPerMs.y * minVisibleChange)
+ .setMaxValue(1f)
+ .setMinimumVisibleChange(minVisibleChange)
+ .addEndListener((animation, canceled, value, velocity) -> {
+ mRectScaleAnimEnded = true;
+ maybeOnEnd();
+ });
+
+ setCanRelease(false);
+ mAnimsStarted = true;
+
+ mRectXAnim.start();
+ mRectYAnim.start();
+ }
+
mRectScaleAnim.start();
for (Animator.AnimatorListener animatorListener : mAnimatorListeners) {
animatorListener.onAnimationStart(null);
@@ -276,8 +367,17 @@
public void end() {
if (mAnimsStarted) {
- mRectXAnim.end();
- mRectYAnim.end();
+ if (enableScalingRevealHomeAnimation()) {
+ if (mRectXSpring.canSkipToEnd()) {
+ mRectXSpring.skipToEnd();
+ }
+ if (mRectYSpring.canSkipToEnd()) {
+ mRectYSpring.skipToEnd();
+ }
+ } else {
+ mRectXAnim.end();
+ mRectYAnim.end();
+ }
if (mRectScaleAnim.canSkipToEnd()) {
mRectScaleAnim.skipToEnd();
}
@@ -357,6 +457,32 @@
end();
}
+ /**
+ * Modify the given velocity so that it's never below the minimum value, and falls off by the
+ * given factor once it goes above the maximum value.
+ * In order for the max soft cap to be enforced, the fall-off factor must be >1.
+ */
+ private static float adjustVelocity(float velocity, long min, long max, float factor) {
+ float sign = Math.signum(velocity);
+ float magnitude = Math.abs(velocity);
+
+ // If the absolute velocity is less than the min, bump it up.
+ if (magnitude < min) {
+ return min * sign;
+ }
+
+ // If the absolute velocity falls between min and max, or the fall-off factor is invalid,
+ // do nothing.
+ if (magnitude <= max || factor <= 1) {
+ return velocity;
+ }
+
+ // Scale the excess velocity by the fall-off factor.
+ float excess = magnitude - max;
+ float scaled = (float) Math.pow(excess, 1f / factor);
+ return (max + scaled) * sign;
+ }
+
public interface OnUpdateListener {
/**
* Called when an update is made to the animation.
diff --git a/quickstep/src/com/android/quickstep/util/ScalingWorkspaceRevealAnim.kt b/quickstep/src/com/android/quickstep/util/ScalingWorkspaceRevealAnim.kt
new file mode 100644
index 0000000..33736ad
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/ScalingWorkspaceRevealAnim.kt
@@ -0,0 +1,129 @@
+/*
+ * 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.View
+import com.android.app.animation.Interpolators
+import com.android.app.animation.Interpolators.EMPHASIZED
+import com.android.app.animation.Interpolators.LINEAR
+import com.android.launcher3.Launcher
+import com.android.launcher3.LauncherAnimUtils.HOTSEAT_SCALE_PROPERTY_FACTORY
+import com.android.launcher3.LauncherAnimUtils.SCALE_INDEX_WORKSPACE_STATE
+import com.android.launcher3.LauncherAnimUtils.WORKSPACE_SCALE_PROPERTY_FACTORY
+import com.android.launcher3.LauncherState
+import com.android.launcher3.anim.AnimatorListeners
+import com.android.launcher3.anim.PendingAnimation
+import com.android.launcher3.anim.PropertySetter
+import com.android.launcher3.states.StateAnimationConfig
+import com.android.launcher3.states.StateAnimationConfig.SKIP_DEPTH_CONTROLLER
+import com.android.launcher3.states.StateAnimationConfig.SKIP_OVERVIEW
+import com.android.launcher3.states.StateAnimationConfig.SKIP_SCRIM
+import com.android.launcher3.uioverrides.QuickstepLauncher
+import com.android.quickstep.views.RecentsView
+
+/**
+ * Creates an animation where the workspace and hotseat fade in while revealing from the center of
+ * the screen outwards radially. This is used in conjunction with the swipe up to home animation.
+ */
+class ScalingWorkspaceRevealAnim(launcher: Launcher) {
+ companion object {
+ private const val FADE_DURATION_MS = 200L
+ private const val SCALE_DURATION_MS = 1000L
+ private const val MAX_ALPHA = 1f
+ private const val MIN_ALPHA = 0f
+ private const val MAX_SIZE = 1f
+ private const val MIN_SIZE = 0.85f
+ }
+
+ private val animation = PendingAnimation(SCALE_DURATION_MS)
+
+ init {
+ // Make sure the starting state is right for the animation.
+ val config = StateAnimationConfig()
+ config.animFlags = SKIP_OVERVIEW.or(SKIP_DEPTH_CONTROLLER).or(SKIP_SCRIM)
+ config.duration = 0
+ launcher.stateManager
+ .createAtomicAnimation(LauncherState.BACKGROUND_APP, LauncherState.NORMAL, config)
+ .start()
+ launcher
+ .getOverviewPanel<RecentsView<QuickstepLauncher, LauncherState>>()
+ .forceFinishScroller()
+ launcher.workspace.stateTransitionAnimation.setScrim(
+ PropertySetter.NO_ANIM_PROPERTY_SETTER,
+ LauncherState.BACKGROUND_APP,
+ config
+ )
+
+ val workspace = launcher.workspace
+ val hotseat = launcher.hotseat
+
+ // Scale the Workspace and Hotseat around the same pivot.
+ animation.addFloat(
+ workspace,
+ WORKSPACE_SCALE_PROPERTY_FACTORY[SCALE_INDEX_WORKSPACE_STATE],
+ MIN_SIZE,
+ MAX_SIZE,
+ EMPHASIZED,
+ )
+ workspace.setPivotToScaleWithSelf(hotseat)
+ animation.addFloat(
+ hotseat,
+ HOTSEAT_SCALE_PROPERTY_FACTORY[SCALE_INDEX_WORKSPACE_STATE],
+ MIN_SIZE,
+ MAX_SIZE,
+ EMPHASIZED,
+ )
+
+ // Fade in quickly at the beginning of the animation, so the content doesn't look like it's
+ // popping into existence out of nowhere.
+ val fadeClamp = FADE_DURATION_MS.toFloat() / SCALE_DURATION_MS
+ workspace.alpha = MIN_ALPHA
+ animation.setViewAlpha(
+ workspace,
+ MAX_ALPHA,
+ Interpolators.clampToProgress(LINEAR, 0f, fadeClamp)
+ )
+ hotseat.alpha = MIN_ALPHA
+ animation.setViewAlpha(
+ hotseat,
+ MAX_ALPHA,
+ Interpolators.clampToProgress(LINEAR, 0f, fadeClamp)
+ )
+
+ // Match the Wallpaper animation to the rest of the content.
+ val depthController = (launcher as? QuickstepLauncher)?.depthController
+ val depthConfig = StateAnimationConfig()
+ depthConfig.setInterpolator(StateAnimationConfig.ANIM_DEPTH, EMPHASIZED)
+ depthController?.setStateWithAnimation(LauncherState.NORMAL, depthConfig, animation)
+
+ // Needed to avoid text artefacts during the scale animation.
+ workspace.setLayerType(View.LAYER_TYPE_HARDWARE, null)
+ hotseat.setLayerType(View.LAYER_TYPE_HARDWARE, null)
+ animation.addListener(
+ AnimatorListeners.forEndCallback(
+ Runnable {
+ workspace.setLayerType(View.LAYER_TYPE_NONE, null)
+ hotseat.setLayerType(View.LAYER_TYPE_NONE, null)
+ }
+ )
+ )
+ }
+
+ fun start() {
+ animation.buildAnim().start()
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/util/SlideInRemoteTransition.kt b/quickstep/src/com/android/quickstep/util/SlideInRemoteTransition.kt
index 9f7b46d..1347291 100644
--- a/quickstep/src/com/android/quickstep/util/SlideInRemoteTransition.kt
+++ b/quickstep/src/com/android/quickstep/util/SlideInRemoteTransition.kt
@@ -46,12 +46,7 @@
mergeTarget: IBinder,
finishCB: IRemoteTransitionFinishedCallback
) {
-
- try {
- finishCB.onTransitionFinished(null, Transaction())
- } catch (e: RemoteException) {
- // Ignore
- }
+ // Do not report finish if we don't know how to handle this transition.
}
override fun startAnimation(
diff --git a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
index 8e2520e..d8cbbf9 100644
--- a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
+++ b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
@@ -35,7 +35,7 @@
import static com.android.quickstep.util.SplitSelectDataHolder.SPLIT_TASK_PENDINGINTENT;
import static com.android.quickstep.util.SplitSelectDataHolder.SPLIT_TASK_SHORTCUT;
import static com.android.quickstep.util.SplitSelectDataHolder.SPLIT_TASK_TASK;
-import static com.android.quickstep.views.DesktopTaskView.isDesktopModeSupported;
+import static com.android.window.flags.Flags.enableDesktopWindowingMode;
import static com.android.wm.shell.common.split.SplitScreenConstants.KEY_EXTRA_WIDGET_INTENT;
import static com.android.wm.shell.common.split.SplitScreenConstants.SNAP_TO_50_50;
@@ -104,6 +104,7 @@
import com.android.systemui.animation.RemoteAnimationRunnerCompat;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.system.ActivityManagerWrapper;
+import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
import com.android.wm.shell.common.split.SplitScreenConstants.PersistentSnapPosition;
import com.android.wm.shell.splitscreen.ISplitSelectListener;
@@ -151,6 +152,12 @@
/** True when the first selected split app is being launched in fullscreen. */
private boolean mLaunchingFirstAppFullscreen;
+ /**
+ * Should be a constant from {@link com.android.internal.jank.Cuj} or -1, does not need to be
+ * set for all launches.
+ */
+ private int mLaunchCuj = -1;
+
private FloatingTaskView mFirstFloatingTaskView;
private SplitInstructionsView mSplitInstructionsView;
@@ -707,6 +714,10 @@
return mSplitAnimationController;
}
+ public void setLaunchingCuj(int launchCuj) {
+ mLaunchCuj = launchCuj;
+ }
+
/**
* Requires Shell Transitions
*/
@@ -761,7 +772,9 @@
@Override
public void mergeAnimation(IBinder transition, TransitionInfo info,
SurfaceControl.Transaction t, IBinder mergeTarget,
- IRemoteTransitionFinishedCallback finishedCallback) { }
+ IRemoteTransitionFinishedCallback finishedCallback) {
+ // Do not report finish if we don't know how to handle this transition.
+ }
@Override
public void onTransitionConsumed(IBinder transition, boolean aborted)
@@ -850,6 +863,11 @@
mSplitInstructionsView = null;
mLaunchingFirstAppFullscreen = false;
+ if (mLaunchCuj != -1) {
+ InteractionJankMonitorWrapper.end(mLaunchCuj);
+ }
+ mLaunchCuj = -1;
+
if (mSessionInstanceIds != null) {
mStatsLogManager.logger()
.withInstanceId(mSessionInstanceIds.second)
@@ -950,7 +968,7 @@
@Override
public boolean onRequestSplitSelect(ActivityManager.RunningTaskInfo taskInfo,
int splitPosition, Rect taskBounds) {
- if (!isDesktopModeSupported()) return false;
+ if (!enableDesktopWindowingMode()) return false;
MAIN_EXECUTOR.execute(() -> enterSplitSelect(taskInfo, splitPosition,
taskBounds));
return true;
diff --git a/quickstep/src/com/android/quickstep/util/SplitToWorkspaceController.java b/quickstep/src/com/android/quickstep/util/SplitToWorkspaceController.java
index 445a540..87be091 100644
--- a/quickstep/src/com/android/quickstep/util/SplitToWorkspaceController.java
+++ b/quickstep/src/com/android/quickstep/util/SplitToWorkspaceController.java
@@ -18,7 +18,7 @@
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APP_PAIR;
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
-import static com.android.quickstep.views.DesktopTaskView.isDesktopModeSupported;
+import static com.android.window.flags.Flags.enableDesktopWindowingMode;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -199,7 +199,7 @@
private boolean shouldIgnoreSecondSplitLaunch() {
return (!FeatureFlags.enableSplitContextually()
- && !isDesktopModeSupported())
+ && !enableDesktopWindowingMode())
|| !mController.isSplitSelectActive();
}
}
diff --git a/quickstep/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java b/quickstep/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java
index 6b3199f..d6d6a11 100644
--- a/quickstep/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java
+++ b/quickstep/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java
@@ -65,7 +65,7 @@
// Should be used for animations running alongside this StaggeredWorkspaceAnim.
public static final int DURATION_MS = 250;
public static final int DURATION_TASKBAR_MS =
- QuickstepTransitionManager.TASKBAR_TO_HOME_DURATION;
+ QuickstepTransitionManager.getTaskbarToHomeDuration();
private static final float MAX_VELOCITY_PX_PER_S = 22f;
diff --git a/quickstep/src/com/android/quickstep/util/TriggerSwipeUpTouchTracker.java b/quickstep/src/com/android/quickstep/util/TriggerSwipeUpTouchTracker.java
index 7bbde30..c63a58e 100644
--- a/quickstep/src/com/android/quickstep/util/TriggerSwipeUpTouchTracker.java
+++ b/quickstep/src/com/android/quickstep/util/TriggerSwipeUpTouchTracker.java
@@ -28,6 +28,8 @@
import android.view.MotionEvent;
import android.view.VelocityTracker;
+import androidx.annotation.NonNull;
+
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
@@ -41,21 +43,20 @@
private final float mMinFlingVelocity;
private final boolean mDisableHorizontalSwipe;
private final NavBarPosition mNavBarPosition;
- private final Runnable mOnInterceptTouch;
+
+ @NonNull
private final OnSwipeUpListener mOnSwipeUp;
private boolean mInterceptedTouch;
private VelocityTracker mVelocityTracker;
public TriggerSwipeUpTouchTracker(Context context, boolean disableHorizontalSwipe,
- NavBarPosition navBarPosition, Runnable onInterceptTouch,
- OnSwipeUpListener onSwipeUp) {
+ NavBarPosition navBarPosition, @NonNull OnSwipeUpListener onSwipeUp) {
mSquaredTouchSlop = Utilities.squaredTouchSlop(context);
mMinFlingVelocity = context.getResources().getDimension(
R.dimen.quickstep_fling_threshold_speed);
mNavBarPosition = navBarPosition;
mDisableHorizontalSwipe = disableHorizontalSwipe;
- mOnInterceptTouch = onInterceptTouch;
mOnSwipeUp = onSwipeUp;
init();
@@ -103,10 +104,7 @@
}
mInterceptedTouch = true;
-
- if (mOnInterceptTouch != null) {
- mOnInterceptTouch.run();
- }
+ mOnSwipeUp.onSwipeUpTouchIntercepted();
}
}
break;
@@ -124,7 +122,8 @@
}
}
- private void endTouchTracking() {
+ /** Finishes the tracking. All events after this call are ignored */
+ public void endTouchTracking() {
if (mVelocityTracker != null) {
mVelocityTracker.recycle();
mVelocityTracker = null;
@@ -151,12 +150,10 @@
isSwipeUp = squaredHypot(displacementX, displacementY) >= mSquaredTouchSlop;
}
- if (mOnSwipeUp != null) {
- if (isSwipeUp) {
- mOnSwipeUp.onSwipeUp(wasFling, new PointF(velocityX, velocityY));
- } else {
- mOnSwipeUp.onSwipeUpCancelled();
- }
+ if (isSwipeUp) {
+ mOnSwipeUp.onSwipeUp(wasFling, new PointF(velocityX, velocityY));
+ } else {
+ mOnSwipeUp.onSwipeUpCancelled();
}
}
@@ -172,6 +169,9 @@
void onSwipeUp(boolean wasFling, PointF finalVelocity);
/** Called on touch up if a swipe up was not detected. */
- void onSwipeUpCancelled();
+ default void onSwipeUpCancelled() { }
+
+ /** Called when the touch for swipe up is intercepted. */
+ default void onSwipeUpTouchIntercepted() { }
}
}
diff --git a/quickstep/src/com/android/quickstep/views/DesktopTaskView.java b/quickstep/src/com/android/quickstep/views/DesktopTaskView.java
index f2c9f27..10b4168 100644
--- a/quickstep/src/com/android/quickstep/views/DesktopTaskView.java
+++ b/quickstep/src/com/android/quickstep/views/DesktopTaskView.java
@@ -51,7 +51,6 @@
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.recents.model.ThumbnailData;
import com.android.systemui.shared.system.QuickStepContract;
-import com.android.window.flags.Flags;
import kotlin.Unit;
@@ -88,11 +87,6 @@
private int mChildCountAtInflation;
- /** Check whether desktop windowing is enabled */
- public static boolean isDesktopModeSupported() {
- return Flags.enableDesktopWindowingMode();
- }
-
public DesktopTaskView(Context context) {
this(context, null);
}
diff --git a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
index 97f3d81..cd4fab6 100644
--- a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
@@ -26,7 +26,7 @@
import static com.android.launcher3.LauncherState.OVERVIEW_SPLIT_SELECT;
import static com.android.launcher3.LauncherState.SPRING_LOADED;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SPLIT_SELECTION_EXIT_HOME;
-import static com.android.quickstep.views.DesktopTaskView.isDesktopModeSupported;
+import static com.android.window.flags.Flags.enableDesktopWindowingMode;
import android.annotation.TargetApi;
import android.content.Context;
@@ -268,7 +268,7 @@
DesktopVisibilityController desktopVisibilityController = null;
boolean showDesktopApps = false;
GestureState.GestureEndTarget endTarget = null;
- if (isDesktopModeSupported()) {
+ if (enableDesktopWindowingMode()) {
desktopVisibilityController = mActivity.getDesktopVisibilityController();
endTarget = mCurrentGestureEndTarget;
if (endTarget == GestureState.GestureEndTarget.LAST_TASK
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index 760d84b..2aa16a9 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -61,7 +61,7 @@
import static com.android.quickstep.util.TaskGridNavHelper.DIRECTION_TAB;
import static com.android.quickstep.util.TaskGridNavHelper.DIRECTION_UP;
import static com.android.quickstep.views.ClearAllButton.DISMISS_ALPHA;
-import static com.android.quickstep.views.DesktopTaskView.isDesktopModeSupported;
+import static com.android.window.flags.Flags.enableDesktopWindowingMode;
import static com.android.quickstep.views.OverviewActionsView.HIDDEN_ACTIONS_IN_MENU;
import static com.android.quickstep.views.OverviewActionsView.HIDDEN_DESKTOP;
import static com.android.quickstep.views.OverviewActionsView.HIDDEN_NON_ZERO_ROTATION;
@@ -463,7 +463,6 @@
protected final Rect mLastComputedTaskSize = new Rect();
protected final Rect mLastComputedGridSize = new Rect();
protected final Rect mLastComputedGridTaskSize = new Rect();
- protected final Rect mLastComputedDesktopTaskSize = new Rect();
private TaskView mSelectedTask = null;
// How much a task that is directly offscreen will be pushed out due to RecentsView scale/pivot.
@Nullable
@@ -1271,6 +1270,8 @@
final SurfaceTransaction showTransaction = new SurfaceTransaction();
for (int i = apps.length - 1; i >= 0; --i) {
showTransaction.getTransaction().show(apps[i].leash);
+ showTransaction.forSurface(apps[i].leash).setLayer(
+ Integer.MAX_VALUE - 1000 + apps[i].prefixOrderIndex);
}
surfaceApplier.scheduleApply(showTransaction);
}
@@ -1658,15 +1659,8 @@
removeView(runningTaskView);
mMovingTaskView = null;
runningTaskView.resetPersistentViewTransforms();
- int frontTaskIndex = 0;
- if (isDesktopModeSupported() && mDesktopTaskView != null
- && !runningTaskView.isDesktopTask()) {
- // If desktop mode is enabled, desktop task view is pinned at first position if present.
- // Move running task to position 1.
- frontTaskIndex = 1;
- }
- addView(runningTaskView, frontTaskIndex);
- setCurrentPage(frontTaskIndex);
+ addView(runningTaskView, 0);
+ setCurrentPage(0);
updateTaskSize();
}
@@ -1743,7 +1737,6 @@
// Clear out desktop view if it is set
mDesktopTaskView = null;
- DesktopTask desktopTask = null;
// Add views as children based on whether it's grouped or single task. Looping through
// taskGroups backwards populates the thumbnail grid from least recent to most recent.
@@ -1752,12 +1745,6 @@
boolean isRemovalNeeded = stagedTaskIdToBeRemovedFromGrid != INVALID_TASK_ID
&& groupTask.containsTask(stagedTaskIdToBeRemovedFromGrid);
- if (groupTask instanceof DesktopTask) {
- desktopTask = (DesktopTask) groupTask;
- // Desktop task will be added separately in the end
- continue;
- }
-
TaskView taskView;
if (isRemovalNeeded && groupTask.hasMultipleTasks()) {
// If we need to remove half of a pair of tasks, force a TaskView with Type.SINGLE
@@ -1788,6 +1775,10 @@
((GroupedTaskView) taskView).bind(leftTopTask, rightBottomTask, mOrientationState,
groupTask.mSplitBounds);
+ } else if (taskView instanceof DesktopTaskView) {
+ ((DesktopTaskView) taskView).bind(((DesktopTask) groupTask).tasks,
+ mOrientationState);
+ mDesktopTaskView = (DesktopTaskView) taskView;
} else {
taskView.bind(groupTask.task1, mOrientationState);
}
@@ -1800,19 +1791,6 @@
if (!taskGroups.isEmpty()) {
addView(mClearAllButton);
- if (isDesktopModeSupported()) {
- // Check if we have apps on the desktop
- if (desktopTask != null && !desktopTask.tasks.isEmpty()) {
- // If we are actively choosing apps for split, skip the desktop tile
- if (!getSplitSelectController().isSplitSelectActive()) {
- mDesktopTaskView = (DesktopTaskView) getTaskViewFromPool(
- TaskView.Type.DESKTOP);
- // Always add a desktop task to the first position
- addView(mDesktopTaskView, 0);
- mDesktopTaskView.bind(desktopTask.tasks, mOrientationState);
- }
- }
- }
}
// Keep same previous focused task
@@ -1820,12 +1798,6 @@
// If the list changed, maybe the focused task doesn't exist anymore
if (newFocusedTaskView == null && getTaskViewCount() > 0) {
newFocusedTaskView = getTaskViewAt(0);
- // Check if the first task is the desktop.
- // If first task is desktop, try to find another task to set as the focused task
- if (newFocusedTaskView != null && newFocusedTaskView.isDesktopTask()
- && getTaskViewCount() > 1) {
- newFocusedTaskView = getTaskViewAt(1);
- }
}
mFocusedTaskViewId = newFocusedTaskView != null && !enableGridOnlyOverview()
? newFocusedTaskView.getTaskViewId() : INVALID_TASK_ID;
@@ -1869,12 +1841,7 @@
if (hasAnyValidTaskIds(runningTaskId)) {
targetPage = indexOfChild(newRunningTaskView);
} else if (getTaskViewCount() > 0) {
- TaskView taskView = requireTaskViewAt(0);
- // If first task id desktop, try to find another task to set the target page
- if (taskView.isDesktopTask() && getTaskViewCount() > 1) {
- taskView = requireTaskViewAt(1);
- }
- targetPage = indexOfChild(taskView);
+ targetPage = indexOfChild(requireTaskViewAt(0));
}
}
if (targetPage != -1 && mCurrentPage != targetPage) {
@@ -2119,9 +2086,6 @@
mSizeStrategy.calculateGridSize(dp, mActivity, mLastComputedGridSize);
mSizeStrategy.calculateGridTaskSize(mActivity, dp, mLastComputedGridTaskSize,
getPagedOrientationHandler());
- if (isDesktopModeSupported()) {
- mSizeStrategy.calculateDesktopTaskSize(mActivity, dp, mLastComputedDesktopTaskSize);
- }
if (enableGridOnlyOverview()) {
mSizeStrategy.calculateCarouselTaskSize(mActivity, dp, mLastComputedCarouselTaskSize,
getPagedOrientationHandler());
@@ -2222,11 +2186,6 @@
return mLastComputedGridTaskSize;
}
- /** Gets the last computed desktop task size */
- public Rect getLastComputedDesktopTaskSize() {
- return mLastComputedDesktopTaskSize;
- }
-
public Rect getLastComputedCarouselTaskSize() {
return mLastComputedCarouselTaskSize;
}
@@ -2832,7 +2791,7 @@
}
private boolean hasDesktopTask(Task[] runningTasks) {
- if (!isDesktopModeSupported()) {
+ if (!enableDesktopWindowingMode()) {
return false;
}
for (Task task : runningTasks) {
@@ -2977,8 +2936,6 @@
TaskView homeTaskView = getHomeTaskView();
TaskView nextFocusedTaskView = null;
- int desktopTaskIndex = Integer.MAX_VALUE;
-
if (!isTaskDismissal) {
mTopRowIdSet.clear();
}
@@ -3005,21 +2962,6 @@
// If focused task is snapped, the row width is just task width and spacing.
snappedTaskRowWidth = taskWidthAndSpacing;
}
- } else if (taskView.isDesktopTask()) {
- // Desktop task was not focused. Pin it to the right of focused
- desktopTaskIndex = i;
- if (taskView.getVisibility() == View.GONE) {
- // Desktop task view is hidden, skip it from grid calculations
- continue;
- }
- if (!enableGridOnlyOverview()) {
- // Only apply x-translation when using legacy overview grid
- gridTranslations[i] += mIsRtl ? taskWidthAndSpacing : -taskWidthAndSpacing;
- }
-
- // Center view vertically in case it's from different orientation.
- taskView.setGridTranslationY((mLastComputedDesktopTaskSize.height() + taskTopMargin
- - taskView.getLayoutParams().height) / 2f);
} else {
if (i > focusedTaskIndex) {
// For tasks after the focused task, shift by focused task's width and spacing.
@@ -3060,7 +3002,7 @@
// Move horizontally into empty space.
float widthOffset = 0;
for (int j = i - 1; !topSet.contains(j) && j >= 0; j--) {
- if (j == focusedTaskIndex || j == desktopTaskIndex) {
+ if (j == focusedTaskIndex) {
continue;
}
widthOffset += requireTaskViewAt(j).getLayoutParams().width + mPageSpacing;
@@ -3079,7 +3021,7 @@
// Move horizontally into empty space.
float widthOffset = 0;
for (int j = i - 1; !bottomSet.contains(j) && j >= 0; j--) {
- if (j == focusedTaskIndex || j == desktopTaskIndex) {
+ if (j == focusedTaskIndex) {
continue;
}
widthOffset += requireTaskViewAt(j).getLayoutParams().width + mPageSpacing;
@@ -4025,7 +3967,7 @@
// Update flags for 1p/3p launchers
mActionsView.updateFor3pLauncher(!supportsAppPairs());
- if (isDesktopModeSupported()) {
+ if (enableDesktopWindowingMode()) {
boolean isCurrentDesktop = getCurrentPageTaskView() instanceof DesktopTaskView;
mActionsView.updateHiddenFlags(HIDDEN_DESKTOP, isCurrentDesktop);
}
@@ -4147,10 +4089,10 @@
private boolean snapToPageRelative(int delta, boolean cycle,
@TaskGridNavHelper.TASK_NAV_DIRECTION int direction) {
- // Ignore grid page snap events while scroll animations are running, otherwise the next
- // page gets set before the animation finishes and can cause jumps.
+ // Set next page if scroll animation is still running, otherwise cannot snap to the
+ // next page on successive key presses. Setting the current page aborts the scroll.
if (!mScroller.isFinished()) {
- return true;
+ setCurrentPage(getNextPage());
}
int pageCount = getPageCount();
if (pageCount == 0) {
@@ -4231,30 +4173,31 @@
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
- if (event.getAction() == KeyEvent.ACTION_DOWN) {
- switch (event.getKeyCode()) {
- case KeyEvent.KEYCODE_TAB:
- return snapToPageRelative(event.isShiftPressed() ? -1 : 1, true /* cycle */,
- DIRECTION_TAB);
- case KeyEvent.KEYCODE_DPAD_RIGHT:
- return snapToPageRelative(mIsRtl ? -1 : 1, true /* cycle */, DIRECTION_RIGHT);
- case KeyEvent.KEYCODE_DPAD_LEFT:
- return snapToPageRelative(mIsRtl ? 1 : -1, true /* cycle */, DIRECTION_LEFT);
- case KeyEvent.KEYCODE_DPAD_UP:
- return snapToPageRelative(1, false /* cycle */, DIRECTION_UP);
- case KeyEvent.KEYCODE_DPAD_DOWN:
- return snapToPageRelative(1, false /* cycle */, DIRECTION_DOWN);
- case KeyEvent.KEYCODE_DEL:
- case KeyEvent.KEYCODE_FORWARD_DEL:
+ if (isHandlingTouch() || event.getAction() != KeyEvent.ACTION_DOWN) {
+ return super.dispatchKeyEvent(event);
+ }
+ switch (event.getKeyCode()) {
+ case KeyEvent.KEYCODE_TAB:
+ return snapToPageRelative(event.isShiftPressed() ? -1 : 1, true /* cycle */,
+ DIRECTION_TAB);
+ case KeyEvent.KEYCODE_DPAD_RIGHT:
+ return snapToPageRelative(mIsRtl ? -1 : 1, true /* cycle */, DIRECTION_RIGHT);
+ case KeyEvent.KEYCODE_DPAD_LEFT:
+ return snapToPageRelative(mIsRtl ? 1 : -1, true /* cycle */, DIRECTION_LEFT);
+ case KeyEvent.KEYCODE_DPAD_UP:
+ return snapToPageRelative(1, false /* cycle */, DIRECTION_UP);
+ case KeyEvent.KEYCODE_DPAD_DOWN:
+ return snapToPageRelative(1, false /* cycle */, DIRECTION_DOWN);
+ case KeyEvent.KEYCODE_DEL:
+ case KeyEvent.KEYCODE_FORWARD_DEL:
+ dismissCurrentTask();
+ return true;
+ case KeyEvent.KEYCODE_NUMPAD_DOT:
+ if (event.isAltPressed()) {
+ // Numpad DEL pressed while holding Alt.
dismissCurrentTask();
return true;
- case KeyEvent.KEYCODE_NUMPAD_DOT:
- if (event.isAltPressed()) {
- // Numpad DEL pressed while holding Alt.
- dismissCurrentTask();
- return true;
- }
- }
+ }
}
return super.dispatchKeyEvent(event);
}
@@ -4749,7 +4692,7 @@
mSplitSelectStateController.setAnimateCurrentTaskDismissal(
true /*animateCurrentTaskDismissal*/);
mSplitHiddenTaskViewIndex = indexOfChild(taskView);
- if (isDesktopModeSupported()) {
+ if (enableDesktopWindowingMode()) {
updateDesktopTaskVisibility(false /* visible */);
}
}
@@ -4773,7 +4716,7 @@
mSplitSelectStateController.setInitialTaskSelect(splitSelectSource.intent,
splitSelectSource.position.stagePosition, splitSelectSource.itemInfo,
splitSelectSource.splitEvent, splitSelectSource.alreadyRunningTaskId);
- if (isDesktopModeSupported()) {
+ if (enableDesktopWindowingMode()) {
updateDesktopTaskVisibility(false /* visible */);
}
}
@@ -4979,7 +4922,7 @@
mSplitHiddenTaskView.setThumbnailVisibility(VISIBLE, INVALID_TASK_ID);
mSplitHiddenTaskView = null;
}
- if (isDesktopModeSupported()) {
+ if (enableDesktopWindowingMode()) {
updateDesktopTaskVisibility(true /* visible */);
}
}
@@ -5031,7 +4974,7 @@
firstFloatingTaskView.update(mTempRectF, /*progress=*/1f);
RecentsPagedOrientationHandler orientationHandler = getPagedOrientationHandler();
- Pair<FloatProperty, FloatProperty> taskViewsFloat =
+ Pair<FloatProperty<RecentsView>, FloatProperty<RecentsView>> taskViewsFloat =
orientationHandler.getSplitSelectTaskOffset(
TASK_PRIMARY_SPLIT_TRANSLATION, TASK_SECONDARY_SPLIT_TRANSLATION,
mActivity.getDeviceProfile());
@@ -5169,9 +5112,13 @@
public float getMaxScaleForFullScreen() {
if (enableGridOnlyOverview() && mActivity.getDeviceProfile().isTablet
&& !mOverviewGridEnabled) {
+ if (mLastComputedCarouselTaskSize.isEmpty()) {
+ mSizeStrategy.calculateCarouselTaskSize(mActivity, mActivity.getDeviceProfile(),
+ mLastComputedCarouselTaskSize, getPagedOrientationHandler());
+ }
mTempRect.set(mLastComputedCarouselTaskSize);
} else {
- if (mLastComputedTaskSize.height() == 0 || mLastComputedTaskSize.width() == 0) {
+ if (mLastComputedTaskSize.isEmpty()) {
getTaskSize(mLastComputedTaskSize);
}
mTempRect.set(mLastComputedTaskSize);
@@ -5365,7 +5312,7 @@
}
RemoteTargetGluer gluer;
- if (isDesktopModeSupported() && recentsAnimationTargets.hasDesktopTasks()) {
+ if (recentsAnimationTargets.hasDesktopTasks()) {
gluer = new RemoteTargetGluer(getContext(), getSizeStrategy(), recentsAnimationTargets,
true /* forDesktop */);
mRemoteTargetHandles = gluer.assignTargetsForDesktop(recentsAnimationTargets);
@@ -5540,10 +5487,6 @@
}
private int getFirstViewIndex() {
- if (isDesktopModeSupported() && mDesktopTaskView != null) {
- // Desktop task is at position 0, that is the first view
- return 0;
- }
TaskView focusedTaskView = mShowAsGridLastOnLayout ? getFocusedTaskView() : null;
return focusedTaskView != null ? indexOfChild(focusedTaskView) : 0;
}
diff --git a/quickstep/src/com/android/quickstep/views/TaskMenuView.java b/quickstep/src/com/android/quickstep/views/TaskMenuView.java
index c9aad1a..e86b7d8 100644
--- a/quickstep/src/com/android/quickstep/views/TaskMenuView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskMenuView.java
@@ -301,6 +301,7 @@
private void animateOpenOrClosed(boolean closing) {
if (mOpenCloseAnimator != null && mOpenCloseAnimator.isRunning()) {
+ testLogD(TEST_TAPL_OVERVIEW_ACTIONS_MENU_FAILURE, "getting canceled");
mOpenCloseAnimator.cancel();
}
mOpenCloseAnimator = new AnimatorSet();
@@ -357,11 +358,14 @@
iconAppChip.getMenuTranslationX(),
MULTI_PROPERTY_VALUE, closing ? 0 : -additionalTranslationX);
menuTranslationXAnim.setInterpolator(EMPHASIZED);
+ testLogD(TEST_TAPL_OVERVIEW_ACTIONS_MENU_FAILURE,
+ "TaskMenuView.java.animateOpenOrClosed: running translation animations");
mOpenCloseAnimator.playTogether(translationYAnim, translationXAnim,
menuTranslationXAnim, menuTranslationYAnim);
}
-
+ testLogD(TEST_TAPL_OVERVIEW_ACTIONS_MENU_FAILURE,
+ "TaskMenuView.java.animateOpenOrClosed: running animation 2");
mOpenCloseAnimator.playTogether(mRevealAnimator,
ObjectAnimator.ofFloat(
mTaskContainer.getThumbnailView(), DIM_ALPHA,
@@ -379,6 +383,13 @@
}
@Override
+ public void onAnimationCancel(Animator animation) {
+ super.onAnimationCancel(animation);
+ testLogD(TEST_TAPL_OVERVIEW_ACTIONS_MENU_FAILURE,
+ "TaskMenuView.java.animateOpenOrClosed: onAnimationCancel");
+ }
+
+ @Override
public void onAnimationSuccess(Animator animator) {
testLogD(TEST_TAPL_OVERVIEW_ACTIONS_MENU_FAILURE,
"TaskMenuView.java.animateOpenOrClosed: onAnimationSuccess");
@@ -392,6 +403,7 @@
}
private void closeComplete() {
+ testLogD(TEST_TAPL_OVERVIEW_ACTIONS_MENU_FAILURE, "TaskMenuView.java.closeComplete");
mIsOpen = false;
mActivity.getDragLayer().removeView(this);
mRevealAnimator = null;
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java
index 085c502..abd4ec4 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskView.java
@@ -29,6 +29,9 @@
import static com.android.launcher3.Utilities.getDescendantCoordRelativeToAncestor;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASK_ICON_TAP_OR_LONGPRESS;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASK_LAUNCH_TAP;
+import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_NOT_PINNABLE;
+import static com.android.launcher3.testing.shared.TestProtocol.SUCCESSFUL_GESTURE_MISMATCH_EVENTS;
+import static com.android.launcher3.testing.shared.TestProtocol.testLogD;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT;
@@ -37,7 +40,6 @@
import static com.android.quickstep.TaskOverlayFactory.getEnabledShortcuts;
import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.EXPECTING_TASK_APPEARED;
import static com.android.quickstep.util.BorderAnimator.DEFAULT_BORDER_COLOR;
-import static com.android.quickstep.views.DesktopTaskView.isDesktopModeSupported;
import static java.lang.annotation.RetentionPolicy.SOURCE;
@@ -83,6 +85,7 @@
import com.android.launcher3.Utilities;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.model.data.WorkspaceItemInfo;
+import com.android.launcher3.pm.UserCache;
import com.android.launcher3.popup.SystemShortcut;
import com.android.launcher3.statemanager.StatefulActivity;
import com.android.launcher3.testing.TestLogging;
@@ -422,8 +425,7 @@
mCurrentFullscreenParams = new FullscreenDrawParams(context);
mDigitalWellBeingToast = new DigitalWellBeingToast(mActivity, this);
- boolean keyboardFocusHighlightEnabled = FeatureFlags.ENABLE_KEYBOARD_QUICK_SWITCH.get()
- || isDesktopModeSupported();
+ boolean keyboardFocusHighlightEnabled = FeatureFlags.ENABLE_KEYBOARD_QUICK_SWITCH.get();
boolean cursorHoverStatesEnabled = enableCursorHoverStates();
setWillNotDraw(!keyboardFocusHighlightEnabled && !cursorHoverStatesEnabled);
@@ -501,6 +503,11 @@
if (getRecentsView() != null) {
stubInfo.screenId = getRecentsView().indexOfChild(this);
}
+ if (Flags.privateSpaceRestrictAccessibilityDrag()) {
+ if (UserCache.getInstance(getContext()).getUserInfo(componentKey.user).isPrivate()) {
+ stubInfo.runtimeStatusFlags |= FLAG_NOT_PINNABLE;
+ }
+ }
return stubInfo;
}
@@ -854,6 +861,8 @@
@Nullable
public RunnableList launchTaskAnimated() {
if (mTask != null) {
+ testLogD(SUCCESSFUL_GESTURE_MISMATCH_EVENTS,
+ "TaskView.launchTaskAnimated: startActivityFromRecentsAsync");
TestLogging.recordEvent(
TestProtocol.SEQUENCE_MAIN, "startActivityFromRecentsAsync", mTask);
ActivityOptionsWrapper opts = mActivity.getActivityLaunchOptions(this, null);
@@ -902,6 +911,8 @@
*/
public void launchTask(@NonNull Consumer<Boolean> callback, boolean isQuickswitch) {
if (mTask != null) {
+ testLogD(SUCCESSFUL_GESTURE_MISMATCH_EVENTS,
+ "TaskView.launchTask: startActivityFromRecentsAsync");
TestLogging.recordEvent(
TestProtocol.SEQUENCE_MAIN, "startActivityFromRecentsAsync", mTask);
@@ -995,6 +1006,8 @@
if (targets == null) {
// If the recents animation is cancelled somehow between the parent if block and
// here, try to launch the task as a non live tile task.
+ testLogD(SUCCESSFUL_GESTURE_MISMATCH_EVENTS,
+ "TaskView.java - launchTasks: recents animation is cancelled");
RunnableList runnableList = launchTaskAnimated();
if (runnableList == null) {
Log.e(TAG, "Recents animation cancelled and cannot launch task as non-live tile"
@@ -1015,6 +1028,8 @@
@Override
public void onAnimationEnd(Animator animator) {
if (mTask != null && mTask.key.displayId != getRootViewDisplayId()) {
+ testLogD(SUCCESSFUL_GESTURE_MISMATCH_EVENTS,
+ "TaskView.java - launchTasks: onAnimationEnd");
launchTaskAnimated();
}
mIsClickableAsLiveTile = true;
@@ -1034,6 +1049,9 @@
recentsView.onTaskLaunchedInLiveTileMode();
return runnableList;
} else {
+ testLogD(SUCCESSFUL_GESTURE_MISMATCH_EVENTS,
+ "TaskView.java - launchTasks: isRunningTask=" + isRunningTask() + "||"
+ + "remoteTargetHandles == null?" + (remoteTargetHandles == null));
return launchTaskAnimated();
}
}
@@ -1726,12 +1744,7 @@
int boxWidth;
int boxHeight;
boolean isFocusedTask = isFocusedTask();
- if (isDesktopTask()) {
- Rect lastComputedDesktopTaskSize =
- getRecentsView().getLastComputedDesktopTaskSize();
- boxWidth = lastComputedDesktopTaskSize.width();
- boxHeight = lastComputedDesktopTaskSize.height();
- } else if (isFocusedTask) {
+ if (isFocusedTask) {
// Task will be focused and should use focused task size. Use focusTaskRatio
// that is associated with the original orientation of the focused task.
boxWidth = taskWidth;
diff --git a/quickstep/tests/src/com/android/launcher3/model/AppEventProducerTest.java b/quickstep/tests/multivalentTests/src/com/android/launcher3/model/AppEventProducerTest.java
similarity index 100%
rename from quickstep/tests/src/com/android/launcher3/model/AppEventProducerTest.java
rename to quickstep/tests/multivalentTests/src/com/android/launcher3/model/AppEventProducerTest.java
diff --git a/quickstep/tests/src/com/android/launcher3/taskbar/RecentsHitboxExtenderTest.java b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/RecentsHitboxExtenderTest.java
similarity index 100%
rename from quickstep/tests/src/com/android/launcher3/taskbar/RecentsHitboxExtenderTest.java
rename to quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/RecentsHitboxExtenderTest.java
diff --git a/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarNavButtonControllerTest.java b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarNavButtonControllerTest.java
similarity index 100%
rename from quickstep/tests/src/com/android/launcher3/taskbar/TaskbarNavButtonControllerTest.java
rename to quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarNavButtonControllerTest.java
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/AllAppsActionManagerTest.kt b/quickstep/tests/multivalentTests/src/com/android/quickstep/AllAppsActionManagerTest.kt
new file mode 100644
index 0000000..73b35e8
--- /dev/null
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/AllAppsActionManagerTest.kt
@@ -0,0 +1,91 @@
+/*
+ * 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
+
+import android.app.PendingIntent
+import android.content.IIntentSender
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR
+import com.android.launcher3.util.TestUtil
+import com.google.common.truth.Truth.assertThat
+import java.util.concurrent.Semaphore
+import java.util.concurrent.TimeUnit.SECONDS
+import org.junit.Test
+import org.junit.runner.RunWith
+
+private const val TIMEOUT = 5L
+
+@RunWith(AndroidJUnit4::class)
+class AllAppsActionManagerTest {
+ private val callbackSemaphore = Semaphore(0)
+ private val bgExecutor = UI_HELPER_EXECUTOR
+
+ private val allAppsActionManager =
+ AllAppsActionManager(
+ InstrumentationRegistry.getInstrumentation().targetContext,
+ bgExecutor,
+ ) {
+ callbackSemaphore.release()
+ PendingIntent(IIntentSender.Default())
+ }
+
+ @Test
+ fun taskbarPresent_actionRegistered() {
+ allAppsActionManager.isTaskbarPresent = true
+ assertThat(callbackSemaphore.tryAcquire(TIMEOUT, SECONDS)).isTrue()
+ assertThat(allAppsActionManager.isActionRegistered).isTrue()
+ }
+
+ @Test
+ fun homeAndOverviewSame_actionRegistered() {
+ allAppsActionManager.isHomeAndOverviewSame = true
+ assertThat(callbackSemaphore.tryAcquire(TIMEOUT, SECONDS)).isTrue()
+ assertThat(allAppsActionManager.isActionRegistered).isTrue()
+ }
+
+ @Test
+ fun toggleTaskbar_destroyedAfterActionRegistered_actionUnregistered() {
+ allAppsActionManager.isTaskbarPresent = true
+ assertThat(callbackSemaphore.tryAcquire(TIMEOUT, SECONDS)).isTrue()
+
+ allAppsActionManager.isTaskbarPresent = false
+ TestUtil.runOnExecutorSync(bgExecutor) {} // Force system action to unregister.
+ assertThat(allAppsActionManager.isActionRegistered).isFalse()
+ }
+
+ @Test
+ fun toggleTaskbar_destroyedBeforeActionRegistered_pendingActionUnregistered() {
+ allAppsActionManager.isTaskbarPresent = true
+ allAppsActionManager.isTaskbarPresent = false
+
+ TestUtil.runOnExecutorSync(bgExecutor) {} // Force system action to unregister.
+ assertThat(callbackSemaphore.tryAcquire(TIMEOUT, SECONDS)).isTrue()
+ assertThat(allAppsActionManager.isActionRegistered).isFalse()
+ }
+
+ @Test
+ fun changeHome_sameAsOverviewBeforeActionUnregistered_actionRegisteredAgain() {
+ allAppsActionManager.isHomeAndOverviewSame = true // Initialize to same.
+ assertThat(callbackSemaphore.tryAcquire(TIMEOUT, SECONDS)).isTrue()
+
+ allAppsActionManager.isHomeAndOverviewSame = false
+ allAppsActionManager.isHomeAndOverviewSame = true
+ assertThat(callbackSemaphore.tryAcquire(TIMEOUT, SECONDS)).isTrue()
+ assertThat(allAppsActionManager.isActionRegistered).isTrue()
+ }
+}
diff --git a/quickstep/tests/src/com/android/quickstep/NavigationBarRotationContextTest.java b/quickstep/tests/multivalentTests/src/com/android/quickstep/NavigationBarRotationContextTest.java
similarity index 100%
rename from quickstep/tests/src/com/android/quickstep/NavigationBarRotationContextTest.java
rename to quickstep/tests/multivalentTests/src/com/android/quickstep/NavigationBarRotationContextTest.java
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/RobolectricTest.kt b/quickstep/tests/multivalentTests/src/com/android/quickstep/RobolectricTest.kt
new file mode 100644
index 0000000..0694aec
--- /dev/null
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/RobolectricTest.kt
@@ -0,0 +1,31 @@
+/*
+ * 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
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class RobolectricTest {
+ @Test
+ fun test1() {
+ val actual = 1 + 1
+ assertThat(actual).isEqualTo(2)
+ }
+}
diff --git a/quickstep/tests/src/com/android/quickstep/util/SplitSelectStateControllerTest.kt b/quickstep/tests/multivalentTests/src/com/android/quickstep/util/SplitSelectStateControllerTest.kt
similarity index 100%
rename from quickstep/tests/src/com/android/quickstep/util/SplitSelectStateControllerTest.kt
rename to quickstep/tests/multivalentTests/src/com/android/quickstep/util/SplitSelectStateControllerTest.kt
diff --git a/quickstep/tests/src/com/android/quickstep/util/TaskGridNavHelperTest.java b/quickstep/tests/multivalentTests/src/com/android/quickstep/util/TaskGridNavHelperTest.java
similarity index 100%
rename from quickstep/tests/src/com/android/quickstep/util/TaskGridNavHelperTest.java
rename to quickstep/tests/multivalentTests/src/com/android/quickstep/util/TaskGridNavHelperTest.java
diff --git a/quickstep/tests/src/com/android/quickstep/util/TaskKeyByLastActiveTimeCacheTest.java b/quickstep/tests/multivalentTests/src/com/android/quickstep/util/TaskKeyByLastActiveTimeCacheTest.java
similarity index 100%
rename from quickstep/tests/src/com/android/quickstep/util/TaskKeyByLastActiveTimeCacheTest.java
rename to quickstep/tests/multivalentTests/src/com/android/quickstep/util/TaskKeyByLastActiveTimeCacheTest.java
diff --git a/quickstep/tests/src/com/android/quickstep/util/TaskViewSimulatorTest.java b/quickstep/tests/multivalentTests/src/com/android/quickstep/util/TaskViewSimulatorTest.java
similarity index 100%
rename from quickstep/tests/src/com/android/quickstep/util/TaskViewSimulatorTest.java
rename to quickstep/tests/multivalentTests/src/com/android/quickstep/util/TaskViewSimulatorTest.java
diff --git a/quickstep/tests/multivalentTestsForDevice b/quickstep/tests/multivalentTestsForDevice
new file mode 120000
index 0000000..fa0fabf
--- /dev/null
+++ b/quickstep/tests/multivalentTestsForDevice
@@ -0,0 +1 @@
+./multivalentTests
\ No newline at end of file
diff --git a/quickstep/tests/multivalentTestsForDeviceless b/quickstep/tests/multivalentTestsForDeviceless
new file mode 120000
index 0000000..fa0fabf
--- /dev/null
+++ b/quickstep/tests/multivalentTestsForDeviceless
@@ -0,0 +1 @@
+./multivalentTests
\ No newline at end of file
diff --git a/quickstep/tests/src/com/android/launcher3/model/WidgetsPredicationUpdateTaskTest.java b/quickstep/tests/src/com/android/launcher3/model/WidgetsPredicationUpdateTaskTest.java
index 37dde10..8702f70 100644
--- a/quickstep/tests/src/com/android/launcher3/model/WidgetsPredicationUpdateTaskTest.java
+++ b/quickstep/tests/src/com/android/launcher3/model/WidgetsPredicationUpdateTaskTest.java
@@ -15,6 +15,8 @@
*/
package com.android.launcher3.model;
+import static android.content.pm.ApplicationInfo.CATEGORY_PRODUCTIVITY;
+import static android.content.pm.ApplicationInfo.FLAG_INSTALLED;
import static android.os.Process.myUserHandle;
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_WIDGETS_PREDICTION;
@@ -37,6 +39,8 @@
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProviderInfo;
import android.content.ComponentName;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.LauncherApps;
import android.os.UserHandle;
import android.platform.test.flag.junit.SetFlagsRule;
import android.text.TextUtils;
@@ -81,6 +85,8 @@
private FakeBgDataModelCallback mCallback = new FakeBgDataModelCallback();
private LauncherModelHelper mModelHelper;
private UserHandle mUserHandle;
+ private LauncherApps mLauncherApps;
+
@Before
public void setup() throws Exception {
@@ -103,12 +109,18 @@
allWidgets = Arrays.asList(mApp1Provider1, mApp1Provider2, mApp2Provider1,
mApp4Provider1, mApp4Provider2, mApp5Provider1);
+ mLauncherApps = mModelHelper.sandboxContext.spyService(LauncherApps.class);
doAnswer(i -> {
String pkg = i.getArgument(0);
- return ApplicationInfoBuilder.newBuilder().setPackageName(pkg).setName(
- "App " + pkg).build();
- }).when(mModelHelper.sandboxContext.getPackageManager())
- .getApplicationInfo(anyString(), anyInt());
+ ApplicationInfo applicationInfo = ApplicationInfoBuilder.newBuilder()
+ .setPackageName(pkg)
+ .setName("App " + pkg)
+ .build();
+ applicationInfo.category = CATEGORY_PRODUCTIVITY;
+ applicationInfo.flags = FLAG_INSTALLED;
+ return applicationInfo;
+ }).when(mLauncherApps).getApplicationInfo(anyString(), anyInt(), any());
+
AppWidgetManager manager = mModelHelper.sandboxContext.spyService(AppWidgetManager.class);
doReturn(allWidgets).when(manager).getInstalledProviders();
doReturn(allWidgets).when(manager).getInstalledProvidersForProfile(eq(myUserHandle()));
diff --git a/quickstep/tests/src/com/android/launcher3/testcomponent/ExcludeFromRecentsTestActivity.java b/quickstep/tests/src/com/android/launcher3/testcomponent/ExcludeFromRecentsTestActivity.java
new file mode 100644
index 0000000..68ac3d5
--- /dev/null
+++ b/quickstep/tests/src/com/android/launcher3/testcomponent/ExcludeFromRecentsTestActivity.java
@@ -0,0 +1,22 @@
+/*
+ * 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.testcomponent;
+
+/**
+ * Extension of BaseTestingActivity to help test excludeFromRecents="true".
+ */
+public class ExcludeFromRecentsTestActivity extends BaseTestingActivity {}
diff --git a/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java b/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
index 213f58f..077ca60 100644
--- a/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
+++ b/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
@@ -76,17 +76,21 @@
import org.junit.runner.RunWith;
import org.junit.runners.model.Statement;
+import java.io.IOException;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import java.util.function.Function;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
@LargeTest
@RunWith(AndroidJUnit4.class)
public class FallbackRecentsTest {
private static final String FALLBACK_LAUNCHER_TITLE = "Test launcher";
+ private static final Pattern COMPONENT_INFO_REGEX = Pattern.compile("ComponentInfo\\{(.*)\\}");
private final UiDevice mDevice;
private final LauncherInstrumentation mLauncher;
@@ -253,7 +257,7 @@
//@NavigationModeSwitch
@Test
@ScreenRecordRule.ScreenRecord // b/321775748
- public void testOverview() {
+ public void testOverview() throws IOException {
startAppFast(getAppPackageName());
startAppFast(resolveSystemApp(Intent.CATEGORY_APP_CALCULATOR));
startTestActivity(2);
@@ -261,7 +265,10 @@
Wait.atMost("Expected three apps in the task list",
() -> mLauncher.getRecentTasks().size() >= 3, DEFAULT_ACTIVITY_TIMEOUT, mLauncher);
+ checkTestLauncher();
BaseOverview overview = mLauncher.getLaunchedAppState().switchToOverview();
+ checkTestLauncher();
+
executeOnRecents(recents -> {
assertTrue("Don't have at least 3 tasks", getTaskCount(recents) >= 3);
});
@@ -303,6 +310,17 @@
mOtherLauncherActivity.packageName).text(FALLBACK_LAUNCHER_TITLE)), WAIT_TIME_MS));
}
+ private void checkTestLauncher() throws IOException {
+ final Matcher matcher = COMPONENT_INFO_REGEX.matcher(
+ mDevice.executeShellCommand("cmd shortcut get-default-launcher"));
+ assertTrue("Incorrect output from get-default-launcher", matcher.find());
+ assertEquals("Current Launcher activity is incorrect",
+ "com.google.android.apps.nexuslauncher.tests/com.android"
+ + ".launcher3.testcomponent.TestLauncherActivity",
+ matcher.group(1)
+ );
+ }
+
private int getCurrentOverviewPage(RecentsActivity recents) {
return recents.<RecentsView>getOverviewPanel().getCurrentPage();
}
diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
index 45a9527..81a2d54 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
@@ -21,6 +21,7 @@
import static com.android.quickstep.TaskbarModeSwitchRule.Mode.TRANSIENT;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeFalse;
@@ -35,9 +36,9 @@
import androidx.test.uiautomator.By;
import androidx.test.uiautomator.Until;
-import com.android.launcher3.Flags;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
+import com.android.launcher3.tapl.BaseOverview;
import com.android.launcher3.tapl.LaunchedAppState;
import com.android.launcher3.tapl.LauncherInstrumentation.NavigationModel;
import com.android.launcher3.tapl.Overview;
@@ -55,6 +56,7 @@
import org.junit.After;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -421,7 +423,6 @@
READ_DEVICE_CONFIG_PERMISSION);
// Debug if we need to goHome to prevent wrong previous state b/315525621
mLauncher.goHome();
- assumeFalse(Flags.enablePredictiveBackGesture());
mLauncher.getWorkspace().switchToAllApps().pressBackToWorkspace();
waitForState("Launcher internal state didn't switch to Home", () -> LauncherState.NORMAL);
@@ -434,6 +435,7 @@
@PortraitLandscape
@TaskbarModeSwitch()
@TestStabilityRule.Stability(flavors = LOCAL | PLATFORM_POSTSUBMIT) // b/309820115
+ @Ignore("b/315376057")
@ScreenRecord // b/309820115
public void testOverviewForTablet() throws Exception {
assumeTrue(mLauncher.isTablet());
@@ -503,7 +505,6 @@
@Test
@PortraitLandscape
- @ScreenRecord // b/326839375
public void testOverviewDeadzones() throws Exception {
startTestAppsWithCheck();
@@ -583,4 +584,25 @@
mLauncher.getDevice().setOrientationNatural();
}
}
+
+ @Test
+ public void testExcludeFromRecents() throws Exception {
+ startExcludeFromRecentsTestActivity();
+ OverviewTask currentTask = getAndAssertLaunchedApp().switchToOverview().getCurrentTask();
+ // TODO(b/326565120): the expected content description shouldn't be null but for now there
+ // is a bug that causes it to sometimes be for excludeForRecents tasks.
+ assertTrue("Can't find ExcludeFromRecentsTestActivity after entering Overview from it",
+ currentTask.containsContentDescription("ExcludeFromRecents")
+ || currentTask.containsContentDescription(null));
+ // Going home should clear out the excludeFromRecents task.
+ BaseOverview overview = mLauncher.goHome().switchToOverview();
+ if (overview.hasTasks()) {
+ currentTask = overview.getCurrentTask();
+ assertFalse("Found ExcludeFromRecentsTestActivity after entering Overview from Home",
+ currentTask.containsContentDescription("ExcludeFromRecents")
+ || currentTask.containsContentDescription(null));
+ } else {
+ // Presumably the test started with 0 tasks and remains that way after going home.
+ }
+ }
}
diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsTrackpad.java b/quickstep/tests/src/com/android/quickstep/TaplTestsTrackpad.java
index aa8c7b5..374722e 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplTestsTrackpad.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplTestsTrackpad.java
@@ -19,7 +19,6 @@
import static com.android.quickstep.NavigationModeSwitchRule.Mode.ZERO_BUTTON;
import static org.junit.Assert.assertNotNull;
-import static org.junit.Assume.assumeFalse;
import static org.junit.Assume.assumeTrue;
import android.app.Instrumentation;
@@ -28,7 +27,6 @@
import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
-import com.android.launcher3.Flags;
import com.android.launcher3.tapl.LauncherInstrumentation.TrackpadGestureType;
import com.android.launcher3.tapl.Workspace;
import com.android.launcher3.ui.PortraitLandscapeRunner.PortraitLandscape;
@@ -69,7 +67,6 @@
@NavigationModeSwitch(mode = ZERO_BUTTON)
public void pressBack() throws Exception {
assumeTrue(mLauncher.isTablet());
- assumeFalse(Flags.enablePredictiveBackGesture());
Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
try {
diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsTransientTaskbar.java b/quickstep/tests/src/com/android/quickstep/TaplTestsTransientTaskbar.java
index 38d6046..d04e389 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplTestsTransientTaskbar.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplTestsTransientTaskbar.java
@@ -17,6 +17,8 @@
import static com.android.launcher3.Flags.enableCursorHoverStates;
import static com.android.launcher3.util.TestConstants.AppNames.TEST_APP_NAME;
+import static com.android.launcher3.util.rule.TestStabilityRule.LOCAL;
+import static com.android.launcher3.util.rule.TestStabilityRule.PLATFORM_POSTSUBMIT;
import static com.android.quickstep.TaskbarModeSwitchRule.Mode.TRANSIENT;
import static org.junit.Assume.assumeTrue;
@@ -26,6 +28,7 @@
import com.android.launcher3.ui.PortraitLandscapeRunner.PortraitLandscape;
import com.android.launcher3.util.rule.ScreenRecordRule.ScreenRecord;
+import com.android.launcher3.util.rule.TestStabilityRule;
import com.android.quickstep.TaskbarModeSwitchRule.TaskbarModeSwitch;
import org.junit.Test;
@@ -71,6 +74,7 @@
@TaskbarModeSwitch(mode = TRANSIENT)
@PortraitLandscape
@ScreenRecord // b/317798731
+ @TestStabilityRule.Stability(flavors = LOCAL | PLATFORM_POSTSUBMIT) // b/321083190
public void testSwipeToStashAndUnstash() {
getTaskbar().swipeDownToStash();
mLauncher.getLaunchedAppState().swipeUpToUnstashTaskbar();
diff --git a/quickstep/tests/src/com/android/quickstep/TaplViewInflationDuringSwipeUp.java b/quickstep/tests/src/com/android/quickstep/TaplViewInflationDuringSwipeUp.java
index 6093816..208920a 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplViewInflationDuringSwipeUp.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplViewInflationDuringSwipeUp.java
@@ -190,16 +190,14 @@
info.spanX = 2;
info.spanY = 2;
AtomicInteger widgetId = new AtomicInteger();
- new FavoriteItemsTransaction(mTargetContext)
+
+ commitTransactionAndLoadHome(new FavoriteItemsTransaction(mTargetContext)
.addItem(() -> {
LauncherAppWidgetInfo item = createWidgetInfo(info, mTargetContext, true);
item.screenId = FIRST_SCREEN_ID;
widgetId.set(item.appWidgetId);
return item;
- })
- .commitAndLoadHome(mLauncher);
-
-
+ }));
assertTrue("Widget is not present",
mLauncher.goHome().tryGetWidget(info.label, DEFAULT_UI_TIMEOUT) != null);
diff --git a/quickstep/tests/src/com/android/quickstep/orientation/LandscapePagedViewHandlerTest.kt b/quickstep/tests/src/com/android/quickstep/orientation/LandscapePagedViewHandlerTest.kt
new file mode 100644
index 0000000..ea52842
--- /dev/null
+++ b/quickstep/tests/src/com/android/quickstep/orientation/LandscapePagedViewHandlerTest.kt
@@ -0,0 +1,195 @@
+/*
+ * 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.orientation
+
+import android.platform.test.flag.junit.SetFlagsRule
+import android.view.Gravity
+import android.view.View
+import android.widget.FrameLayout
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.launcher3.Flags
+import com.android.quickstep.orientation.LandscapePagedViewHandler.SplitIconPositions
+import com.android.quickstep.views.IconAppChipView
+import com.google.common.truth.Truth.assertThat
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito.`when`
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.verify
+
+@RunWith(AndroidJUnit4::class)
+class LandscapePagedViewHandlerTest {
+
+ @get:Rule val setFlagsRule = SetFlagsRule()
+
+ private val sut = LandscapePagedViewHandler()
+
+ private fun enableGridOnlyOverview(isEnabled: Boolean) {
+ if (isEnabled) {
+ setFlagsRule.enableFlags(
+ Flags.FLAG_ENABLE_GRID_ONLY_OVERVIEW,
+ Flags.FLAG_ENABLE_OVERVIEW_ICON_MENU
+ )
+ } else {
+ setFlagsRule.disableFlags(
+ Flags.FLAG_ENABLE_GRID_ONLY_OVERVIEW,
+ Flags.FLAG_ENABLE_OVERVIEW_ICON_MENU
+ )
+ }
+ }
+
+ /** [ Test getSplitIconsPosition ] */
+ private fun getSplitIconsPosition(isRTL: Boolean): SplitIconPositions {
+ return sut.getSplitIconsPosition(
+ TASK_ICON_HEIGHT_PX,
+ PRIMARY_SNAPSHOT,
+ TOTAL_THUMBNAIL_HEIGHT,
+ isRTL,
+ OVERVIEW_TASK_MARGIN_PX,
+ DIVIDER_SIZE_PX,
+ )
+ }
+
+ @Test
+ fun testIcon_getSplitIconsPositions() {
+ enableGridOnlyOverview(false)
+
+ val (topLeftY, bottomRightY) = getSplitIconsPosition(isRTL = false)
+
+ // Top-Left icon should be at the end of the primary snapshot height
+ assertThat(topLeftY).isEqualTo(250)
+ // Bottom-Right icon should be at the end of the primary height + divider + icon size
+ assertThat(bottomRightY).isEqualTo(374)
+ }
+
+ @Test
+ fun testIcon_getSplitIconsPositions_isRTL() {
+ enableGridOnlyOverview(false)
+
+ val (topLeftY, bottomRightY) = getSplitIconsPosition(isRTL = true)
+
+ // Top-Left icon should be at the end of the primary snapshot height
+ assertThat(topLeftY).isEqualTo(250)
+ // Bottom-Right icon should be at the end of the primary height + divider + icon size
+ assertThat(bottomRightY).isEqualTo(374)
+ }
+
+ @Test
+ fun testChip_getSplitIconsPositions() {
+ enableGridOnlyOverview(true)
+
+ val (topLeftY, bottomRightY) = getSplitIconsPosition(isRTL = false)
+
+ // Top-Left app chip should always be at the initial position of the first snapshot
+ assertThat(topLeftY).isEqualTo(0)
+ // Bottom-Right app chip should be at the end of the primary height + divider
+ assertThat(bottomRightY).isEqualTo(266)
+ }
+
+ @Test
+ fun testChip_getSplitIconsPositions_isRTL() {
+ enableGridOnlyOverview(true)
+
+ val (topLeftY, bottomRightY) = getSplitIconsPosition(isRTL = true)
+
+ // TODO(b/326377497): When started in fake seascape and rotated to landscape,
+ // the icon chips are in RTL and wrongly positioned at the right side of the snapshot.
+ // Top-Left app chip should be placed at the top left of the first snapshot, but because
+ // this issue, it's displayed at the top-right of the second snapshot.
+ // The Bottom-Right app chip is displayed at the top-right of the first snapshot because
+ // of this issue.
+ assertThat(topLeftY).isEqualTo(0)
+ assertThat(bottomRightY).isEqualTo(-316)
+ }
+
+ /** Test updateSplitIconsPosition */
+ @Test
+ fun testIcon_updateSplitIconsPosition() {
+ enableGridOnlyOverview(false)
+
+ val expectedTranslationY = 250
+ val expectedGravity = Gravity.TOP or Gravity.RIGHT
+
+ val iconView = mock<View>()
+ val frameLayout = FrameLayout.LayoutParams(100, 100)
+ `when`(iconView.layoutParams).thenReturn(frameLayout)
+
+ sut.updateSplitIconsPosition(iconView, expectedTranslationY, false)
+ assertThat(frameLayout.gravity).isEqualTo(expectedGravity)
+ assertThat(frameLayout.topMargin).isEqualTo(expectedTranslationY)
+ verify(iconView).translationX = 0f
+ verify(iconView).translationY = 0f
+ }
+
+ @Test
+ fun testIcon_updateSplitIconsPosition_isRTL() {
+ enableGridOnlyOverview(false)
+
+ val expectedTranslationY = 250
+ val expectedGravity = Gravity.TOP or Gravity.RIGHT
+
+ val iconView = mock<View>()
+ val frameLayout = FrameLayout.LayoutParams(100, 100)
+ `when`(iconView.layoutParams).thenReturn(frameLayout)
+
+ sut.updateSplitIconsPosition(iconView, expectedTranslationY, true)
+ assertThat(frameLayout.gravity).isEqualTo(expectedGravity)
+ assertThat(frameLayout.topMargin).isEqualTo(expectedTranslationY)
+ verify(iconView).translationX = 0f
+ verify(iconView).translationY = 0f
+ }
+
+ @Test
+ fun testChip_updateSplitIconsPosition() {
+ enableGridOnlyOverview(true)
+
+ val expectedTranslationY = 250
+ val frameLayout = FrameLayout.LayoutParams(100, 100)
+ val iconView = mock<IconAppChipView>()
+ `when`(iconView.layoutParams).thenReturn(frameLayout)
+
+ sut.updateSplitIconsPosition(iconView, expectedTranslationY, false)
+ assertThat(frameLayout.gravity).isEqualTo(Gravity.TOP or Gravity.END)
+ verify(iconView).setSplitTranslationX(0f)
+ verify(iconView).setSplitTranslationY(expectedTranslationY.toFloat())
+ }
+
+ @Test
+ fun testChip_updateSplitIconsPosition_isRTL() {
+ enableGridOnlyOverview(true)
+
+ val expectedTranslationY = 250
+ val frameLayout = FrameLayout.LayoutParams(100, 100)
+ val iconView = mock<IconAppChipView>()
+ `when`(iconView.layoutParams).thenReturn(frameLayout)
+
+ sut.updateSplitIconsPosition(iconView, expectedTranslationY, true)
+ assertThat(frameLayout.gravity).isEqualTo(Gravity.BOTTOM or Gravity.START)
+ verify(iconView).setSplitTranslationX(0f)
+ verify(iconView).setSplitTranslationY(expectedTranslationY.toFloat())
+ }
+
+ private companion object {
+ const val TASK_ICON_HEIGHT_PX = 108
+ const val OVERVIEW_TASK_MARGIN_PX = 0
+ const val DIVIDER_SIZE_PX = 16
+ const val PRIMARY_SNAPSHOT = 250
+ const val SECONDARY_SNAPSHOT = 300
+ const val TOTAL_THUMBNAIL_HEIGHT = PRIMARY_SNAPSHOT + SECONDARY_SNAPSHOT + DIVIDER_SIZE_PX
+ }
+}
diff --git a/quickstep/tests/src/com/android/quickstep/orientation/SeascapePagedViewHandlerTest.kt b/quickstep/tests/src/com/android/quickstep/orientation/SeascapePagedViewHandlerTest.kt
new file mode 100644
index 0000000..2bc182c
--- /dev/null
+++ b/quickstep/tests/src/com/android/quickstep/orientation/SeascapePagedViewHandlerTest.kt
@@ -0,0 +1,197 @@
+/*
+ * 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.orientation
+
+import android.platform.test.flag.junit.SetFlagsRule
+import android.view.Gravity
+import android.view.View
+import android.widget.FrameLayout
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.launcher3.Flags
+import com.android.quickstep.orientation.LandscapePagedViewHandler.SplitIconPositions
+import com.android.quickstep.views.IconAppChipView
+import com.google.common.truth.Truth.assertThat
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito.`when`
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.verify
+
+@RunWith(AndroidJUnit4::class)
+class SeascapePagedViewHandlerTest {
+
+ @get:Rule val setFlagsRule = SetFlagsRule()
+
+ private val sut = SeascapePagedViewHandler()
+
+ private fun enableGridOnlyOverview(isEnabled: Boolean) {
+ if (isEnabled) {
+ setFlagsRule.enableFlags(
+ Flags.FLAG_ENABLE_GRID_ONLY_OVERVIEW,
+ Flags.FLAG_ENABLE_OVERVIEW_ICON_MENU
+ )
+ } else {
+ setFlagsRule.disableFlags(
+ Flags.FLAG_ENABLE_GRID_ONLY_OVERVIEW,
+ Flags.FLAG_ENABLE_OVERVIEW_ICON_MENU
+ )
+ }
+ }
+
+ /** [ Test getSplitIconsPosition ] */
+ private fun getSplitIconsPosition(isRTL: Boolean): SplitIconPositions {
+ return sut.getSplitIconsPosition(
+ TASK_ICON_HEIGHT_PX,
+ PRIMARY_SNAPSHOT,
+ TOTAL_THUMBNAIL_HEIGHT,
+ isRTL,
+ OVERVIEW_TASK_MARGIN_PX,
+ DIVIDER_SIZE_PX,
+ )
+ }
+
+ @Test
+ fun testIcon_getSplitIconsPositions() {
+ enableGridOnlyOverview(false)
+
+ val (topLeftY, bottomRightY) = getSplitIconsPosition(isRTL = false)
+
+ // The top-left icon is translated from the bottom of the screen to the end of
+ // the primary snapshot minus the icon size.
+ assertThat(topLeftY).isEqualTo(142)
+ // The bottom-right icon is placed at the end of the primary snapshot plus the divider.
+ assertThat(bottomRightY).isEqualTo(266)
+ }
+
+ @Test
+ fun testIcon_getSplitIconsPositions_isRTL() {
+ enableGridOnlyOverview(false)
+
+ val (topLeftY, bottomRightY) = getSplitIconsPosition(isRTL = true)
+
+ // The top-left icon is translated from the bottom of the screen to the end of
+ // the primary snapshot minus the icon size.
+ assertThat(topLeftY).isEqualTo(142)
+ // The bottom-right icon is placed at the end of the primary snapshot plus the divider.
+ assertThat(bottomRightY).isEqualTo(266)
+ }
+
+ @Test
+ fun testChip_getSplitIconsPositions() {
+ enableGridOnlyOverview(true)
+
+ val (topLeftY, bottomRightY) = getSplitIconsPosition(isRTL = false)
+
+ // Top-Left app chip should always be at the initial position of the first snapshot
+ assertThat(topLeftY).isEqualTo(0)
+ // Bottom-Right app chip should be at the end of the primary height + divider
+ assertThat(bottomRightY).isEqualTo(-266)
+ }
+
+ @Test
+ fun testChip_getSplitIconsPositions_isRTL() {
+ enableGridOnlyOverview(true)
+
+ val (topLeftY, bottomRightY) = getSplitIconsPosition(isRTL = true)
+
+ // TODO(b/326377497): When started in fake seascape and rotated to landscape,
+ // the icon chips are in RTL and wrongly positioned at the right side of the snapshot.
+ // Top-Left app chip should be placed at the top left of the first snapshot, but because
+ // this issue, it's displayed at the top-right of the second snapshot.
+ // The Bottom-Right app chip is displayed at the top-right of the first snapshot because
+ // of this issue.
+ assertThat(topLeftY).isEqualTo(316)
+ assertThat(bottomRightY).isEqualTo(0)
+ }
+
+ /** Test updateSplitIconsPosition */
+ @Test
+ fun testIcon_updateSplitIconsPosition() {
+ enableGridOnlyOverview(false)
+
+ val expectedTranslationY = 250
+ val expectedGravity = Gravity.BOTTOM or Gravity.LEFT
+
+ val iconView = mock<View>()
+ val frameLayout = FrameLayout.LayoutParams(100, 100)
+ `when`(iconView.layoutParams).thenReturn(frameLayout)
+
+ sut.updateSplitIconsPosition(iconView, expectedTranslationY, false)
+ assertThat(frameLayout.gravity).isEqualTo(expectedGravity)
+ assertThat(frameLayout.bottomMargin).isEqualTo(expectedTranslationY)
+ verify(iconView).translationX = 0f
+ verify(iconView).translationY = 0f
+ }
+
+ @Test
+ fun testIcon_updateSplitIconsPosition_isRTL() {
+ enableGridOnlyOverview(false)
+
+ val expectedTranslationY = 250
+ val expectedGravity = Gravity.BOTTOM or Gravity.LEFT
+
+ val iconView = mock<View>()
+ val frameLayout = FrameLayout.LayoutParams(100, 100)
+ `when`(iconView.layoutParams).thenReturn(frameLayout)
+
+ sut.updateSplitIconsPosition(iconView, expectedTranslationY, true)
+ assertThat(frameLayout.gravity).isEqualTo(expectedGravity)
+ assertThat(frameLayout.bottomMargin).isEqualTo(expectedTranslationY)
+ verify(iconView).translationX = 0f
+ verify(iconView).translationY = 0f
+ }
+
+ @Test
+ fun testChip_updateSplitIconsPosition() {
+ enableGridOnlyOverview(true)
+
+ val expectedTranslationY = 250
+ val frameLayout = FrameLayout.LayoutParams(100, 100)
+ val iconView = mock<IconAppChipView>()
+ `when`(iconView.layoutParams).thenReturn(frameLayout)
+
+ sut.updateSplitIconsPosition(iconView, expectedTranslationY, false)
+ assertThat(frameLayout.gravity).isEqualTo(Gravity.BOTTOM or Gravity.START)
+ verify(iconView).setSplitTranslationX(0f)
+ verify(iconView).setSplitTranslationY(expectedTranslationY.toFloat())
+ }
+
+ @Test
+ fun testChip_updateSplitIconsPosition_isRTL() {
+ enableGridOnlyOverview(true)
+
+ val expectedTranslationY = 250
+ val frameLayout = FrameLayout.LayoutParams(100, 100)
+ val iconView = mock<IconAppChipView>()
+ `when`(iconView.layoutParams).thenReturn(frameLayout)
+
+ sut.updateSplitIconsPosition(iconView, expectedTranslationY, true)
+ assertThat(frameLayout.gravity).isEqualTo(Gravity.TOP or Gravity.END)
+ verify(iconView).setSplitTranslationX(0f)
+ verify(iconView).setSplitTranslationY(expectedTranslationY.toFloat())
+ }
+
+ private companion object {
+ const val TASK_ICON_HEIGHT_PX = 108
+ const val OVERVIEW_TASK_MARGIN_PX = 0
+ const val DIVIDER_SIZE_PX = 16
+ const val PRIMARY_SNAPSHOT = 250
+ const val SECONDARY_SNAPSHOT = 300
+ const val TOTAL_THUMBNAIL_HEIGHT = PRIMARY_SNAPSHOT + SECONDARY_SNAPSHOT + DIVIDER_SIZE_PX
+ }
+}
diff --git a/quickstep/tests/src/com/android/quickstep/util/AppPairsControllerTest.kt b/quickstep/tests/src/com/android/quickstep/util/AppPairsControllerTest.kt
index 510faf6..adaf7ff 100644
--- a/quickstep/tests/src/com/android/quickstep/util/AppPairsControllerTest.kt
+++ b/quickstep/tests/src/com/android/quickstep/util/AppPairsControllerTest.kt
@@ -105,7 +105,7 @@
whenever(mockTopTaskTracker.getCachedTopTask(any())).thenReturn(mockCachedTaskInfo)
whenever(mockTask1.getKey()).thenReturn(mockTaskKey1)
whenever(mockTask2.getKey()).thenReturn(mockTaskKey2)
- doNothing().whenever(spyAppPairsController).launchAppPair(any())
+ doNothing().whenever(spyAppPairsController).launchAppPair(any(), any())
doNothing()
.whenever(spyAppPairsController)
.launchToSide(anyOrNull(), anyOrNull(), anyOrNull(), anyOrNull())
@@ -210,7 +210,7 @@
callback.accept(arrayOf(mockTask1, mockTask2))
// Verify that launchAppPair and launchToSide were never called
- verify(spyAppPairsController, never()).launchAppPair(any())
+ verify(spyAppPairsController, never()).launchAppPair(any(), any())
verify(spyAppPairsController, never())
.launchToSide(anyOrNull(), anyOrNull(), anyOrNull(), anyOrNull())
}
@@ -234,7 +234,7 @@
callback.accept(arrayOf(mockTask1, mockTask2))
// Verify that launchToSide was called with the correct arguments
- verify(spyAppPairsController, never()).launchAppPair(any())
+ verify(spyAppPairsController, never()).launchAppPair(any(), any())
verify(spyAppPairsController, times(1))
.launchToSide(anyOrNull(), anyOrNull(), anyOrNull(), eq(STAGE_POSITION_BOTTOM_OR_RIGHT))
}
@@ -258,7 +258,7 @@
callback.accept(arrayOf(mockTask1, mockTask2))
// Verify that launchToSide was called with the correct arguments
- verify(spyAppPairsController, never()).launchAppPair(any())
+ verify(spyAppPairsController, never()).launchAppPair(any(), any())
verify(spyAppPairsController, times(1))
.launchToSide(anyOrNull(), anyOrNull(), anyOrNull(), eq(STAGE_POSITION_TOP_OR_LEFT))
}
@@ -282,7 +282,7 @@
callback.accept(arrayOf(mockTask1, mockTask2))
// Verify that launchToSide was called with the correct arguments
- verify(spyAppPairsController, never()).launchAppPair(any())
+ verify(spyAppPairsController, never()).launchAppPair(any(), any())
verify(spyAppPairsController, times(1))
.launchToSide(anyOrNull(), anyOrNull(), anyOrNull(), eq(STAGE_POSITION_BOTTOM_OR_RIGHT))
}
@@ -306,7 +306,7 @@
callback.accept(arrayOf(mockTask1, mockTask2))
// Verify that launchToSide was called with the correct arguments
- verify(spyAppPairsController, never()).launchAppPair(any())
+ verify(spyAppPairsController, never()).launchAppPair(any(), any())
verify(spyAppPairsController, times(1))
.launchToSide(anyOrNull(), anyOrNull(), anyOrNull(), eq(STAGE_POSITION_TOP_OR_LEFT))
}
@@ -330,7 +330,7 @@
callback.accept(arrayOf(mockTask1, mockTask2))
// Verify that launchAppPair was called
- verify(spyAppPairsController, times(1)).launchAppPair(any())
+ verify(spyAppPairsController, times(1)).launchAppPair(any(), any())
verify(spyAppPairsController, never())
.launchToSide(anyOrNull(), anyOrNull(), anyOrNull(), anyOrNull())
}
@@ -354,7 +354,7 @@
callback.accept(arrayOf(mockTask1, mockTask2))
// Verify that launchToSide was called with the correct arguments
- verify(spyAppPairsController, never()).launchAppPair(any())
+ verify(spyAppPairsController, never()).launchAppPair(any(), any())
verify(spyAppPairsController, times(1))
.launchToSide(anyOrNull(), anyOrNull(), anyOrNull(), eq(STAGE_POSITION_BOTTOM_OR_RIGHT))
}
@@ -378,7 +378,7 @@
callback.accept(arrayOf(mockTask1, mockTask2))
// Verify that launchToSide was called with the correct arguments
- verify(spyAppPairsController, never()).launchAppPair(any())
+ verify(spyAppPairsController, never()).launchAppPair(any(), any())
verify(spyAppPairsController, times(1))
.launchToSide(anyOrNull(), anyOrNull(), anyOrNull(), eq(STAGE_POSITION_TOP_OR_LEFT))
}
@@ -402,7 +402,7 @@
callback.accept(arrayOf(mockTask1, mockTask2))
// Verify that launchAppPair was called
- verify(spyAppPairsController, times(1)).launchAppPair(any())
+ verify(spyAppPairsController, times(1)).launchAppPair(any(), any())
verify(spyAppPairsController, never())
.launchToSide(anyOrNull(), anyOrNull(), anyOrNull(), anyOrNull())
}
diff --git a/res/drawable/ic_info_no_shadow.xml b/res/drawable/ic_info_no_shadow.xml
index 7c43779..29a81bd 100644
--- a/res/drawable/ic_info_no_shadow.xml
+++ b/res/drawable/ic_info_no_shadow.xml
@@ -18,7 +18,7 @@
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
- android:tint="?android:attr/textColorPrimary">
+ android:tint="?attr/materialColorOnSurface">
<path
android:fillColor="@android:color/white"
diff --git a/res/drawable/ic_install_no_shadow.xml b/res/drawable/ic_install_no_shadow.xml
index eaad0de..6e0125a 100644
--- a/res/drawable/ic_install_no_shadow.xml
+++ b/res/drawable/ic_install_no_shadow.xml
@@ -18,7 +18,7 @@
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
- android:tint="?android:attr/textColorPrimary">
+ android:tint="?attr/materialColorOnSurface">
<path
android:fillColor="@android:color/white"
diff --git a/res/drawable/ic_install_to_private.xml b/res/drawable/ic_install_to_private.xml
index 0e9833c..45723b0 100644
--- a/res/drawable/ic_install_to_private.xml
+++ b/res/drawable/ic_install_to_private.xml
@@ -20,7 +20,7 @@
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
- android:tint="?android:attr/textColorPrimary">
+ android:tint="?attr/materialColorOnSurface">
<path
android:fillColor="@android:color/white"
diff --git a/res/drawable/ic_plus.xml b/res/drawable/ic_plus.xml
new file mode 100644
index 0000000..3ab926a
--- /dev/null
+++ b/res/drawable/ic_plus.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2024 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="19dp"
+ android:height="18dp"
+ android:viewportWidth="19"
+ android:viewportHeight="18">
+ <path
+ android:pathData="M15.5,9.75H10.25V15H8.75V9.75H3.5V8.25H8.75V3H10.25V8.25H15.5V9.75Z"
+ android:fillColor="#ffffff"/>
+</vector>
diff --git a/res/drawable/ic_private_space_with_background.xml b/res/drawable/ic_private_space_with_background.xml
index 59a33dd..da199f0 100644
--- a/res/drawable/ic_private_space_with_background.xml
+++ b/res/drawable/ic_private_space_with_background.xml
@@ -12,14 +12,15 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<vector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:aapt="http://schemas.android.com/aapt"
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
android:viewportWidth="48"
android:viewportHeight="48"
android:width="48dp"
android:height="48dp">
<path
android:pathData="M48 24A24 24 0 0 1 0 24A24 24 0 0 1 48 24Z"
- android:fillColor="?attr/materialColorOutlineVariant" />
+ android:fillColor="?androidprv:attr/materialColorSurfaceContainerLowest" />
<path
android:pathData="M33.3333 14.6667V33.3333H14.6667V14.6667H33.3333ZM33.3333 12H14.6667C13.2 12 12 13.2 12 14.6667V33.3333C12 34.8 13.2 36 14.6667 36H33.3333C34.8 36 36 34.8 36 33.3333V14.6667C36 13.2 34.8 12 33.3333 12Z"
android:fillColor="?attr/materialColorOnSurface" />
diff --git a/res/drawable/ic_uninstall_no_shadow.xml b/res/drawable/ic_uninstall_no_shadow.xml
index fbabdd2..6200054 100644
--- a/res/drawable/ic_uninstall_no_shadow.xml
+++ b/res/drawable/ic_uninstall_no_shadow.xml
@@ -18,7 +18,7 @@
android:height="20dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0"
- android:tint="?android:attr/textColorPrimary" >
+ android:tint="?attr/materialColorOnSurface" >
<path
android:fillColor="@android:color/white"
android:pathData="M15,4V3H9v1H4v2h1v13c0,1.1,0.9,2,2,2h10c1.1,0,2-0.9,2-2V6h1V4H15z M17,19H7V6h10V19z" />
diff --git a/res/drawable/page_indicator.xml b/res/drawable/page_indicator.xml
index c0ccc49..d4cb13f 100644
--- a/res/drawable/page_indicator.xml
+++ b/res/drawable/page_indicator.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
- <solid android:color="?attr/folderPaginationColor"/>
+ <solid android:color="?attr/pageIndicatorDotColor"/>
<size android:width="@dimen/page_indicator_size" android:height="@dimen/page_indicator_size"/>
</shape>
\ No newline at end of file
diff --git a/res/drawable/widget_cell_add_button_background.xml b/res/drawable/widget_cell_add_button_background.xml
new file mode 100644
index 0000000..860d1cd
--- /dev/null
+++ b/res/drawable/widget_cell_add_button_background.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2024 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<inset
+ xmlns:android="http://schemas.android.com/apk/res/android">
+ <ripple
+ android:color="?android:attr/colorControlHighlight">
+ <item>
+ <shape android:shape="rectangle">
+ <corners
+ android:radius="50dp"/>
+ <solid android:color="?attr/widgetPickerAddButtonBackgroundColor" />
+ </shape>
+ </item>
+ </ripple>
+</inset>
\ No newline at end of file
diff --git a/res/layout/private_space_header.xml b/res/layout/private_space_header.xml
index 0b0af87..2b5db48 100644
--- a/res/layout/private_space_header.xml
+++ b/res/layout/private_space_header.xml
@@ -87,7 +87,8 @@
<TextView
android:id="@+id/ps_container_header"
android:layout_width="wrap_content"
- android:layout_height="@dimen/ps_header_text_height"
+ android:layout_height="wrap_content"
+ android:minHeight="@dimen/ps_header_text_height"
android:layout_alignParentStart="true"
android:layout_centerVertical="true"
android:layout_toStartOf="@+id/settingsAndLockGroup"
diff --git a/res/layout/user_folder_icon_normalized.xml b/res/layout/user_folder_icon_normalized.xml
index 31f4870..43a8aac 100644
--- a/res/layout/user_folder_icon_normalized.xml
+++ b/res/layout/user_folder_icon_normalized.xml
@@ -18,6 +18,7 @@
xmlns:launcher="http://schemas.android.com/apk/res-auto"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ android:defaultFocusHighlightEnabled="false"
android:orientation="vertical" >
<com.android.launcher3.folder.FolderPagedView
diff --git a/res/layout/widget_cell.xml b/res/layout/widget_cell.xml
index 55dd1de..4533873 100644
--- a/res/layout/widget_cell.xml
+++ b/res/layout/widget_cell.xml
@@ -17,7 +17,8 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="0dp"
android:layout_height="wrap_content"
- android:paddingHorizontal="@dimen/widget_cell_horizontal_padding"
+ android:layout_marginStart="@dimen/widget_cell_horizontal_padding"
+ android:layout_marginEnd="@dimen/widget_cell_horizontal_padding"
android:paddingVertical="@dimen/widget_cell_vertical_padding"
android:layout_weight="1"
android:orientation="vertical"
diff --git a/res/layout/widget_cell_content.xml b/res/layout/widget_cell_content.xml
index 0c606f6..106c5b7 100644
--- a/res/layout/widget_cell_content.xml
+++ b/res/layout/widget_cell_content.xml
@@ -45,40 +45,70 @@
android:layout_margin="@dimen/profile_badge_margin"/>
</com.android.launcher3.widget.WidgetCellPreview>
- <!-- The name of the widget. -->
- <TextView
- android:id="@+id/widget_name"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:ellipsize="end"
- android:fadingEdge="horizontal"
- android:gravity="center_horizontal|center_vertical"
- android:singleLine="true"
- android:maxLines="1"
- android:textColor="?android:attr/textColorPrimary"
- android:drawablePadding="@dimen/widget_cell_app_icon_padding"
- android:textSize="@dimen/widget_cell_font_size" />
-
- <!-- The original dimensions of the widget -->
- <TextView
- android:id="@+id/widget_dims"
+ <FrameLayout
android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:gravity="center_horizontal"
- android:textColor="?android:attr/textColorSecondary"
- android:textSize="@dimen/widget_cell_font_size"
- android:alpha="0.7" />
+ android:layout_height="wrap_content">
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:id="@+id/widget_text_container"
+ android:orientation="vertical">
+ <!-- The name of the widget. -->
+ <TextView
+ android:id="@+id/widget_name"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:ellipsize="end"
+ android:fadingEdge="horizontal"
+ android:layout_gravity="center_horizontal"
+ android:gravity="center_horizontal|center_vertical"
+ android:singleLine="true"
+ android:maxLines="1"
+ android:textColor="?android:attr/textColorPrimary"
+ android:drawablePadding="@dimen/widget_cell_app_icon_padding"
+ android:textSize="@dimen/widget_cell_font_size" />
- <TextView
- android:id="@+id/widget_description"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:gravity="center_horizontal"
- android:textSize="@dimen/widget_cell_font_size"
- android:textColor="?android:attr/textColorSecondary"
- android:maxLines="2"
- android:ellipsize="end"
- android:fadingEdge="horizontal"
- android:alpha="0.7" />
+ <!-- The original dimensions of the widget -->
+ <TextView
+ android:id="@+id/widget_dims"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_horizontal"
+ android:textColor="?android:attr/textColorSecondary"
+ android:textSize="@dimen/widget_cell_font_size"
+ android:alpha="0.7" />
-</merge>
\ No newline at end of file
+ <TextView
+ android:id="@+id/widget_description"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_horizontal"
+ android:textSize="@dimen/widget_cell_font_size"
+ android:textColor="?android:attr/textColorSecondary"
+ android:maxLines="2"
+ android:ellipsize="end"
+ android:fadingEdge="horizontal"
+ android:alpha="0.7" />
+ </LinearLayout>
+
+ <Button
+ android:id="@+id/widget_add_button"
+ android:layout_width="wrap_content"
+ android:layout_height="@dimen/widget_cell_add_button_height"
+ android:layout_gravity="center"
+ android:minWidth="0dp"
+ android:paddingTop="@dimen/widget_cell_add_button_vertical_padding"
+ android:paddingBottom="@dimen/widget_cell_add_button_vertical_padding"
+ android:paddingStart="@dimen/widget_cell_add_button_start_padding"
+ android:paddingEnd="@dimen/widget_cell_add_button_end_padding"
+ android:text="@string/widget_add_button_label"
+ android:textColor="?attr/widgetPickerAddButtonTextColor"
+ android:textSize="@dimen/widget_cell_font_size"
+ android:gravity="center"
+ android:visibility="gone"
+ android:drawableLeft="@drawable/ic_plus"
+ android:drawablePadding="8dp"
+ android:drawableTint="?attr/widgetPickerAddButtonTextColor"
+ android:background="@drawable/widget_cell_add_button_background" />
+ </FrameLayout>
+</merge>
diff --git a/res/layout/widget_recommendations.xml b/res/layout/widget_recommendations.xml
index 89821ac..531db2e 100644
--- a/res/layout/widget_recommendations.xml
+++ b/res/layout/widget_recommendations.xml
@@ -28,6 +28,7 @@
android:layout_marginTop="16dp"
android:accessibilityLiveRegion="polite"
android:gravity="center_horizontal"
+ android:layout_gravity="top"
android:lineHeight="20sp"
android:textColor="?attr/widgetPickerTitleColor"
android:textFontWeight="500"
@@ -38,7 +39,7 @@
android:id="@+id/widget_recommendations_page_indicator"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_gravity="center_horizontal"
+ android:layout_gravity="center_horizontal|top"
android:elevation="1dp"
android:visibility="gone" />
<!--
@@ -50,8 +51,9 @@
<com.android.launcher3.widget.picker.WidgetRecommendationsView
android:id="@+id/widget_recommendations_view"
android:layout_width="match_parent"
- android:layout_height="wrap_content"
+ android:layout_height="0dp"
android:layout_gravity="center"
+ android:layout_weight="1"
android:background="@drawable/widgets_surface_background"
android:importantForAccessibility="yes"
launcher:pageIndicator="@+id/widget_recommendations_page_indicator" />
diff --git a/res/layout/widget_recommendations_table.xml b/res/layout/widget_recommendations_table.xml
index e3f0562..b53d2d5 100644
--- a/res/layout/widget_recommendations_table.xml
+++ b/res/layout/widget_recommendations_table.xml
@@ -17,5 +17,4 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:paddingHorizontal="@dimen/widget_recommendations_table_horizontal_padding"
android:paddingVertical="@dimen/widget_recommendations_table_vertical_padding" />
diff --git a/res/layout/widgets_full_sheet_paged_view.xml b/res/layout/widgets_full_sheet_paged_view.xml
index 1d37043..8dc785a 100644
--- a/res/layout/widgets_full_sheet_paged_view.xml
+++ b/res/layout/widgets_full_sheet_paged_view.xml
@@ -79,6 +79,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
+ android:layout_marginBottom="8dp"
android:background="@drawable/widgets_surface_background"
android:orientation="vertical"
android:layout_marginHorizontal="@dimen/widget_list_horizontal_margin"
@@ -92,7 +93,7 @@
android:layout_height="64dp"
android:gravity="center_horizontal"
android:orientation="horizontal"
- android:paddingVertical="8dp"
+ android:paddingBottom="8dp"
android:paddingHorizontal="@dimen/widget_list_horizontal_margin"
android:background="?attr/widgetPickerPrimarySurfaceColor"
style="@style/TextHeadline"
diff --git a/res/layout/widgets_full_sheet_recyclerview.xml b/res/layout/widgets_full_sheet_recyclerview.xml
index dca08ff..5427732 100644
--- a/res/layout/widgets_full_sheet_recyclerview.xml
+++ b/res/layout/widgets_full_sheet_recyclerview.xml
@@ -29,7 +29,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/collapse_handle"
- android:paddingBottom="16dp"
+ android:paddingBottom="8dp"
android:layout_marginHorizontal="@dimen/widget_list_horizontal_margin"
android:clipToOutline="true"
android:orientation="vertical">
@@ -62,6 +62,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
+ android:layout_marginBottom="8dp"
android:background="@drawable/widgets_surface_background"
android:orientation="vertical"
android:visibility="gone">
diff --git a/res/layout/widgets_two_pane_sheet.xml b/res/layout/widgets_two_pane_sheet.xml
index 8e45740f..6c4810c 100644
--- a/res/layout/widgets_two_pane_sheet.xml
+++ b/res/layout/widgets_two_pane_sheet.xml
@@ -122,7 +122,7 @@
<LinearLayout
android:id="@+id/widget_recommendations_container"
android:layout_width="match_parent"
- android:layout_height="wrap_content"
+ android:layout_height="match_parent"
android:background="@drawable/widgets_surface_background"
android:orientation="vertical"
android:visibility="gone">
diff --git a/res/values-af/strings.xml b/res/values-af/strings.xml
index 1e1b3f3..8902033 100644
--- a/res/values-af/strings.xml
+++ b/res/values-af/strings.xml
@@ -38,16 +38,19 @@
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d breed by %2$d hoog"</string>
<string name="widget_preview_context_description" msgid="9045841361655787574">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g>-legstuk"</string>
+ <string name="widget_preview_name_and_dims_content_description" msgid="8489038126122831595">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g>-legstuk, %2$d wyd en %3$d hoog"</string>
<string name="add_item_request_drag_hint" msgid="8730547755622776606">"Raak en hou die legstuk om dit op die tuisskerm rond te beweeg"</string>
<string name="add_to_home_screen" msgid="9168649446635919791">"Voeg by tuisskerm"</string>
<string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g>-legstuk by tuisskerm gevoeg"</string>
<string name="suggested_widgets_header_title" msgid="1844314680798145222">"Voorstelle"</string>
- <string name="productivity_widget_recommendation_category_label" msgid="1722113555721820766">"Jou daaglikse noodsaaklikhede"</string>
- <string name="news_widget_recommendation_category_label" msgid="3908242346768119070">"Nuus vir jou"</string>
+ <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Noodsaaklikhede"</string>
+ <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"Nuus en tydskrifte"</string>
<string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"Jou ontspansone"</string>
- <string name="fitness_widget_recommendation_category_label" msgid="2657652999128882431">"Bereik jou fiksheiddoelwitte"</string>
- <string name="weather_widget_recommendation_category_label" msgid="6712678763480668598">"Spring die weer voor"</string>
- <string name="others_widget_recommendation_category_label" msgid="897876078077284733">"Jy hou dalk ook van"</string>
+ <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"Vermaak"</string>
+ <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"Sosiaal"</string>
+ <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"Gesondheid en fiksheid"</string>
+ <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"Weer"</string>
+ <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"Voorgestel vir jou"</string>
<string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"<xliff:g id="SELECTED_HEADER">%1$s</xliff:g>-legstukke aan die regterkant, soektog en opsies aan die linkerkant"</string>
<string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# legstuk}other{# legstukke}}"</string>
<string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# kortpad}other{# kortpaaie}}"</string>
@@ -61,6 +64,8 @@
<string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Werk"</string>
<string name="widget_category_conversations" msgid="8894438636213590446">"Gesprekke"</string>
<string name="widget_category_note_taking" msgid="3469689394504266039">"Neem notas"</string>
+ <string name="widget_add_button_label" msgid="2761267068711937179">"Voeg by"</string>
+ <string name="widget_add_button_content_description" msgid="1810530016360039643">"Voeg <xliff:g id="WIDGET_NAME">%1$s</xliff:g>-legstuk by"</string>
<string name="widget_education_header" msgid="4874760613775913787">"Nuttige inligting binne jou bereik"</string>
<string name="widget_education_content" msgid="1731667670753497052">"Jy kan legstukke by jou tuisskerm voeg om inligting te kry sonder om programme oop te maak"</string>
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Tik om legstukinstellings te verander"</string>
diff --git a/res/values-am/strings.xml b/res/values-am/strings.xml
index e404506..0b94a4c 100644
--- a/res/values-am/strings.xml
+++ b/res/values-am/strings.xml
@@ -38,16 +38,19 @@
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d ስፋት በ%2$d ከፍታ"</string>
<string name="widget_preview_context_description" msgid="9045841361655787574">"የ<xliff:g id="WIDGET_NAME">%1$s</xliff:g> ምግብር"</string>
+ <string name="widget_preview_name_and_dims_content_description" msgid="8489038126122831595">"የ<xliff:g id="WIDGET_NAME">%1$s</xliff:g> ምግብር፣ %2$d ስፋት በ%3$d ቁመት"</string>
<string name="add_item_request_drag_hint" msgid="8730547755622776606">"በመነሻ ማያ ገፅ አካባቢ ላይ ለማንቀሳቀስ ነክተው ይያዙት"</string>
<string name="add_to_home_screen" msgid="9168649446635919791">"ወደ መነሻ ማያ ገፅ አክል"</string>
<string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> ምግብር ወደ መነሻ ማያ ገፅ ታክሏል"</string>
<string name="suggested_widgets_header_title" msgid="1844314680798145222">"የአስተያየት ጥቆማዎች"</string>
- <string name="productivity_widget_recommendation_category_label" msgid="1722113555721820766">"የእርስዎ ዕለታዊ መሠረታዊ ነገሮች"</string>
- <string name="news_widget_recommendation_category_label" msgid="3908242346768119070">"ዜና ለእርስዎ"</string>
+ <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"ጠቃሚ ነገሮች"</string>
+ <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"ዜና እና መጽሔቶች"</string>
<string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"የሚያርፉበት ቦታዎ"</string>
- <string name="fitness_widget_recommendation_category_label" msgid="2657652999128882431">"የአካል ብቃት ግቦችዎን ያሳኩ"</string>
- <string name="weather_widget_recommendation_category_label" msgid="6712678763480668598">"ለአየር ሁኔታው አስቀድመው ያቅዱ"</string>
- <string name="others_widget_recommendation_category_label" msgid="897876078077284733">"ይህንንም ሊወዱት ይችላሉ"</string>
+ <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"መዝናኛ"</string>
+ <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"ማህበራዊ"</string>
+ <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"ጤና እና የአካል ብቃት"</string>
+ <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"የአየር ሁኔታ"</string>
+ <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"ለእርስዎ የተጠቆሙ"</string>
<string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"<xliff:g id="SELECTED_HEADER">%1$s</xliff:g> ምግብሮች በቀኝ በኩል፣ ፍለጋ እና አማራጮች በግራ በኩል"</string>
<string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# ምግብር}one{# ምግብሮች}other{# ምግብሮች}}"</string>
<string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# አቋራጭ}one{# አቋራጭ}other{# አቋራጮች}}"</string>
@@ -61,6 +64,8 @@
<string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"ሥራ"</string>
<string name="widget_category_conversations" msgid="8894438636213590446">"ውይይቶች"</string>
<string name="widget_category_note_taking" msgid="3469689394504266039">"የማስታወሻ አያያዝ"</string>
+ <string name="widget_add_button_label" msgid="2761267068711937179">"አክል"</string>
+ <string name="widget_add_button_content_description" msgid="1810530016360039643">"ምግብር <xliff:g id="WIDGET_NAME">%1$s</xliff:g>ን አክል"</string>
<string name="widget_education_header" msgid="4874760613775913787">"በጣቶችዎ ጫፎች ላይ ጠቃሚ መረጃ"</string>
<string name="widget_education_content" msgid="1731667670753497052">"መተግበሪያዎችን ሳይከፍቱ መረጃ ለማግኘት በመነሻ ማያ ገጽዎ ላይ ምግብሮችን ማከል ይችላሉ"</string>
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"የምግብር ቅንብሮችን ለመለወጥ መታ ያድርጉ"</string>
diff --git a/res/values-ar/strings.xml b/res/values-ar/strings.xml
index e1b98fb..7ae39e3 100644
--- a/res/values-ar/strings.xml
+++ b/res/values-ar/strings.xml
@@ -38,16 +38,19 @@
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"العرض %1$d الطول %2$d"</string>
<string name="widget_preview_context_description" msgid="9045841361655787574">"أداة <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
+ <string name="widget_preview_name_and_dims_content_description" msgid="8489038126122831595">"التطبيق المصغّرة \"<xliff:g id="WIDGET_NAME">%1$s</xliff:g>\"، بعرض %2$d وارتفاع %3$d"</string>
<string name="add_item_request_drag_hint" msgid="8730547755622776606">"انقر مع الاستمرار على التطبيق المصغّر لنقله إلى الشاشة الرئيسية."</string>
<string name="add_to_home_screen" msgid="9168649446635919791">"إضافة إلى الشاشة الرئيسية"</string>
<string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"تمت إضافة الأداة <xliff:g id="WIDGET_NAME">%1$s</xliff:g> إلى الشاشة الرئيسية."</string>
<string name="suggested_widgets_header_title" msgid="1844314680798145222">"اقتراحات"</string>
- <string name="productivity_widget_recommendation_category_label" msgid="1722113555721820766">"تطبيقات أساسية للحياة اليومية"</string>
- <string name="news_widget_recommendation_category_label" msgid="3908242346768119070">"أخبار مقترَحة لك"</string>
+ <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"الأساسيات"</string>
+ <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"الأخبار والمجلات"</string>
<string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"محتوى ترفيهي مقترَح"</string>
- <string name="fitness_widget_recommendation_category_label" msgid="2657652999128882431">"تحقيق أهداف اللياقة البدنية"</string>
- <string name="weather_widget_recommendation_category_label" msgid="6712678763480668598">"معرفة حالة الطقس أولاً بأول"</string>
- <string name="others_widget_recommendation_category_label" msgid="897876078077284733">"محتوى قد يعجبك أيضًا"</string>
+ <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"الترفيه"</string>
+ <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"التواصل الاجتماعي"</string>
+ <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"الصحة واللياقة البدنية"</string>
+ <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"الطقس"</string>
+ <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"محتوى مقترَح لك"</string>
<string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"تطبيقات \"<xliff:g id="SELECTED_HEADER">%1$s</xliff:g>\" المصغّرة على اليسار، والبحث والخيارات على اليمين"</string>
<string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{تطبيق مصغّر واحد}zero{# تطبيق مصغّر}two{تطبيقان مصغّران}few{# تطبيقات مصغّرة}many{# تطبيقًا مصغّرًا}other{# تطبيق مصغّر}}"</string>
<string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{اختصار واحد}zero{# اختصار}two{اختصاران}few{# اختصارات}many{# اختصارًا}other{# اختصار}}"</string>
@@ -61,6 +64,8 @@
<string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"تطبيقات العمل"</string>
<string name="widget_category_conversations" msgid="8894438636213590446">"المحادثات"</string>
<string name="widget_category_note_taking" msgid="3469689394504266039">"تدوين الملاحظات"</string>
+ <string name="widget_add_button_label" msgid="2761267068711937179">"إضافة"</string>
+ <string name="widget_add_button_content_description" msgid="1810530016360039643">"إضافة التطبيق المصغّر \"<xliff:g id="WIDGET_NAME">%1$s</xliff:g>\""</string>
<string name="widget_education_header" msgid="4874760613775913787">"معلومات مفيدة في متناول يديك"</string>
<string name="widget_education_content" msgid="1731667670753497052">"للحصول على معلومات بدون فتح التطبيقات، يمكنك إضافة التطبيقات المصغّرة إلى الشاشة الرئيسية."</string>
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"انقر لتغيير إعدادات الأداة"</string>
diff --git a/res/values-as/strings.xml b/res/values-as/strings.xml
index 940e2db..47586d9 100644
--- a/res/values-as/strings.xml
+++ b/res/values-as/strings.xml
@@ -38,16 +38,19 @@
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d বহল x %2$d ওখ"</string>
<string name="widget_preview_context_description" msgid="9045841361655787574">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> ৱিজেট"</string>
+ <string name="widget_preview_name_and_dims_content_description" msgid="8489038126122831595">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> ৱিজেট, %2$d বহল %3$d ওখ"</string>
<string name="add_item_request_drag_hint" msgid="8730547755622776606">"ৱিজেটটো গৃহ স্ক্ৰীনৰ আশে-পাশে নিবলৈ সেইটোত স্পৰ্শ কৰি ধৰি ৰাখক"</string>
<string name="add_to_home_screen" msgid="9168649446635919791">"গৃহ স্ক্ৰীনত যোগ কৰক"</string>
<string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> ৱিজেটটো গৃহ স্ক্ৰীনত যোগ দিয়া হৈছে"</string>
<string name="suggested_widgets_header_title" msgid="1844314680798145222">"পৰামৰ্শ"</string>
- <string name="productivity_widget_recommendation_category_label" msgid="1722113555721820766">"আপোনাৰ দৈনিক অত্যাৱশ্যকীয় সামগ্ৰী"</string>
- <string name="news_widget_recommendation_category_label" msgid="3908242346768119070">"আপোনাৰ বাবে বাতৰি"</string>
+ <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"অত্যাৱশ্যকীয়সমূহ"</string>
+ <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"বাতৰি আৰু আলোচনী"</string>
<string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"আপোনাৰ পচন্দৰ স্থান"</string>
- <string name="fitness_widget_recommendation_category_label" msgid="2657652999128882431">"আপোনাৰ সুস্থতাৰ লক্ষ্যত উপনীত হওক"</string>
- <string name="weather_widget_recommendation_category_label" msgid="6712678763480668598">"বতৰৰ বিষয়ে আগতীয়াকৈ জানক"</string>
- <string name="others_widget_recommendation_category_label" msgid="897876078077284733">"আপুনি হয়তো এইটোও পচন্দ কৰিব"</string>
+ <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"মনোৰঞ্জন"</string>
+ <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"সামাজিক"</string>
+ <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"স্বাস্থ্য আৰু সুস্থতা"</string>
+ <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"বতৰ"</string>
+ <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"আপোনাৰ বাবে পৰামৰ্শ হিচাপে আগবঢ়োৱা"</string>
<string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"<xliff:g id="SELECTED_HEADER">%1$s</xliff:g> ৱিজেট সোঁফালে, সন্ধান আৰু বিকল্পসমূহ বাওঁফালে"</string>
<string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# টা ৱিজেট}one{# টা ৱিজেট}other{# টা ৱিজেট}}"</string>
<string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# টা শ্বৰ্টকাট}one{# টা শ্বৰ্টকাট}other{# টা শ্বৰ্টকাট}}"</string>
@@ -61,6 +64,8 @@
<string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"কৰ্মস্থান"</string>
<string name="widget_category_conversations" msgid="8894438636213590446">"বাৰ্তালাপ"</string>
<string name="widget_category_note_taking" msgid="3469689394504266039">"টোকা গ্ৰহণ কৰা"</string>
+ <string name="widget_add_button_label" msgid="2761267068711937179">"যোগ দিয়ক"</string>
+ <string name="widget_add_button_content_description" msgid="1810530016360039643">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> ৱিজেট যোগ দিয়ক"</string>
<string name="widget_education_header" msgid="4874760613775913787">"আপোনাৰ আঙুলিৰে টিপতে উপযোগী তথ্য পাওক"</string>
<string name="widget_education_content" msgid="1731667670753497052">"এপ্ নোখোলাকৈ তথ্য পাবলৈ আপুনি নিজৰ গৃহ স্ক্ৰীনত ৱিজেট যোগ দিব পাৰে"</string>
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"ৱিজেটৰ ছেটিং সলনি কৰিবলৈ টিপক"</string>
diff --git a/res/values-az/strings.xml b/res/values-az/strings.xml
index d11c4a7..4631f61 100644
--- a/res/values-az/strings.xml
+++ b/res/values-az/strings.xml
@@ -38,16 +38,19 @@
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"%2$d hündürlük %1$d enində"</string>
<string name="widget_preview_context_description" msgid="9045841361655787574">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> vidceti"</string>
+ <string name="widget_preview_name_and_dims_content_description" msgid="8489038126122831595">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> vidceti, eni - %2$d, hündürlüyü - %3$d"</string>
<string name="add_item_request_drag_hint" msgid="8730547755622776606">"Əsas ekranda hərəkət etdirmək üçün vidcetə toxunub saxlayın"</string>
<string name="add_to_home_screen" msgid="9168649446635919791">"Əsas ekrana əlavə edin"</string>
<string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> vidceti əsas ekrana əlavə edildi"</string>
<string name="suggested_widgets_header_title" msgid="1844314680798145222">"Təkliflər"</string>
- <string name="productivity_widget_recommendation_category_label" msgid="1722113555721820766">"Gündəlik vacib vidcetlər"</string>
- <string name="news_widget_recommendation_category_label" msgid="3908242346768119070">"Sizin üçün xəbərlər"</string>
+ <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Əsaslar"</string>
+ <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"Xəbər və jurnallar"</string>
<string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"İstirahət zonası"</string>
- <string name="fitness_widget_recommendation_category_label" msgid="2657652999128882431">"Fitnes hədəflərinə nail olun"</string>
- <string name="weather_widget_recommendation_category_label" msgid="6712678763480668598">"Hava barədə məlumatlı olun"</string>
- <string name="others_widget_recommendation_category_label" msgid="897876078077284733">"Tövsiyələr"</string>
+ <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"Əyləncə"</string>
+ <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"Sosial"</string>
+ <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"Sağlamlıq və fitnes"</string>
+ <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"Hava"</string>
+ <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"Təklif edirik"</string>
<string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"<xliff:g id="SELECTED_HEADER">%1$s</xliff:g> vidcetləri sağda, axtarış və seçimlər solda"</string>
<string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# vidcet}other{# vidcet}}"</string>
<string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# qısayol}other{# qısayol}}"</string>
@@ -61,6 +64,8 @@
<string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"İş"</string>
<string name="widget_category_conversations" msgid="8894438636213590446">"Söhbətlər"</string>
<string name="widget_category_note_taking" msgid="3469689394504266039">"Qeydgötürmə"</string>
+ <string name="widget_add_button_label" msgid="2761267068711937179">"Əlavə edin"</string>
+ <string name="widget_add_button_content_description" msgid="1810530016360039643">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> vidcet əlavə edin"</string>
<string name="widget_education_header" msgid="4874760613775913787">"Faydalı məlumatlar barmaqlarınızın ucunda"</string>
<string name="widget_education_content" msgid="1731667670753497052">"Tətbiqləri açmadan məlumat almaq üçün Əsas ekrana vidcet əlavə edə bilərsiniz"</string>
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Vidcet ayarlarını dəyişmək üçün toxunun"</string>
diff --git a/res/values-b+sr+Latn/strings.xml b/res/values-b+sr+Latn/strings.xml
index b99ec65..d96d660 100644
--- a/res/values-b+sr+Latn/strings.xml
+++ b/res/values-b+sr+Latn/strings.xml
@@ -38,16 +38,19 @@
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d×%2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"širina od %1$d i visina od %2$d"</string>
<string name="widget_preview_context_description" msgid="9045841361655787574">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> vidžet"</string>
+ <string name="widget_preview_name_and_dims_content_description" msgid="8489038126122831595">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> vidžet, širina %2$d i visina %3$d"</string>
<string name="add_item_request_drag_hint" msgid="8730547755622776606">"Dodirnite i zadržite vidžet da biste ga pomerali po početnom ekranu"</string>
<string name="add_to_home_screen" msgid="9168649446635919791">"Dodaj na početni ekran"</string>
<string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"Dodali ste vidžet <xliff:g id="WIDGET_NAME">%1$s</xliff:g> na početni ekran"</string>
<string name="suggested_widgets_header_title" msgid="1844314680798145222">"Predlozi"</string>
- <string name="productivity_widget_recommendation_category_label" msgid="1722113555721820766">"Osnovni vidžeti za svaki dan"</string>
- <string name="news_widget_recommendation_category_label" msgid="3908242346768119070">"Vesti za vas"</string>
+ <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Osnovne aplikacije"</string>
+ <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"Novosti i časopisi"</string>
<string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"Zona za opuštanje"</string>
- <string name="fitness_widget_recommendation_category_label" msgid="2657652999128882431">"Ostvarite fitnes ciljeve"</string>
- <string name="weather_widget_recommendation_category_label" msgid="6712678763480668598">"Budite u toku sa vremenskim prilikama"</string>
- <string name="others_widget_recommendation_category_label" msgid="897876078077284733">"Možda će vam se dopasti i"</string>
+ <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"Zabava"</string>
+ <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"Društvene mreže"</string>
+ <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"Zdravlje i fitnes"</string>
+ <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"Vreme"</string>
+ <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"Predloženo za vas"</string>
<string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"Vidžeti <xliff:g id="SELECTED_HEADER">%1$s</xliff:g> sa desne strane, pretraga i opcije sa leve strane"</string>
<string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# vidžet}one{# vidžet}few{# vidžeta}other{# vidžeta}}"</string>
<string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# prečica}one{# prečica}few{# prečice}other{# prečica}}"</string>
@@ -61,6 +64,8 @@
<string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Posao"</string>
<string name="widget_category_conversations" msgid="8894438636213590446">"Konverzacije"</string>
<string name="widget_category_note_taking" msgid="3469689394504266039">"Pravljenje beležaka"</string>
+ <string name="widget_add_button_label" msgid="2761267068711937179">"Dodaj"</string>
+ <string name="widget_add_button_content_description" msgid="1810530016360039643">"Dodajte vidžet <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
<string name="widget_education_header" msgid="4874760613775913787">"Korisne informacije nadohvat ruke"</string>
<string name="widget_education_content" msgid="1731667670753497052">"Da biste pronašli informacije bez otvaranja aplikacija, možete da dodate vidžete na početni ekran"</string>
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Dodirnite da biste promenili podešavanja vidžeta"</string>
diff --git a/res/values-be/strings.xml b/res/values-be/strings.xml
index c978425..7c33ff6 100644
--- a/res/values-be/strings.xml
+++ b/res/values-be/strings.xml
@@ -38,16 +38,19 @@
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"Шырына: %1$d, вышыня: %2$d"</string>
<string name="widget_preview_context_description" msgid="9045841361655787574">"Віджэт \"<xliff:g id="WIDGET_NAME">%1$s</xliff:g>\""</string>
+ <string name="widget_preview_name_and_dims_content_description" msgid="8489038126122831595">"Віджэт \"<xliff:g id="WIDGET_NAME">%1$s</xliff:g>\": шырыня – %2$d, вышыня – %3$d"</string>
<string name="add_item_request_drag_hint" msgid="8730547755622776606">"Утрымліваючы віджэт націснутым, перамяшчайце яго па галоўным экране"</string>
<string name="add_to_home_screen" msgid="9168649446635919791">"Дадаць на галоўны экран"</string>
<string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"Віджэт \"<xliff:g id="WIDGET_NAME">%1$s</xliff:g>\" дададзены на галоўны экран"</string>
<string name="suggested_widgets_header_title" msgid="1844314680798145222">"Прапановы"</string>
- <string name="productivity_widget_recommendation_category_label" msgid="1722113555721820766">"Праграмы першай неабходнасці"</string>
- <string name="news_widget_recommendation_category_label" msgid="3908242346768119070">"Навіны для вас"</string>
+ <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Асноўнае"</string>
+ <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"Навіны і часопісы"</string>
<string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"Зона адпачынку"</string>
- <string name="fitness_widget_recommendation_category_label" msgid="2657652999128882431">"Вашы фітнэс-мэты"</string>
- <string name="weather_widget_recommendation_category_label" msgid="6712678763480668598">"Прагноз надвор\'я"</string>
- <string name="others_widget_recommendation_category_label" msgid="897876078077284733">"Іншыя рэкамендацыі"</string>
+ <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"Забавы"</string>
+ <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"Сацыяльныя сеткі"</string>
+ <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"Здароўе і фітнес"</string>
+ <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"Надвор\'е"</string>
+ <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"Рэкамендавана для вас"</string>
<string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"Віджэты праграмы \"<xliff:g id="SELECTED_HEADER">%1$s</xliff:g>\" справа, пошук і параметры злева"</string>
<string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# віджэт}one{# віджэт}few{# віджэты}many{# віджэтаў}other{# віджэта}}"</string>
<string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# ярлык}one{# ярлык}few{# ярлыкі}many{# ярлыкоў}other{# ярлыка}}"</string>
@@ -61,6 +64,8 @@
<string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Працоўныя"</string>
<string name="widget_category_conversations" msgid="8894438636213590446">"Размовы"</string>
<string name="widget_category_note_taking" msgid="3469689394504266039">"Стварэнне нататак"</string>
+ <string name="widget_add_button_label" msgid="2761267068711937179">"Дадаць"</string>
+ <string name="widget_add_button_content_description" msgid="1810530016360039643">"Дадаць віджэт \"<xliff:g id="WIDGET_NAME">%1$s</xliff:g>\""</string>
<string name="widget_education_header" msgid="4874760613775913787">"Карысная інфармацыя ў вас пад рукой"</string>
<string name="widget_education_content" msgid="1731667670753497052">"Каб не адкрываць праграмы для прагляду патрэбнай інфармацыі, дадайце віджэты на галоўны экран"</string>
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Націсніце, каб змяніць налады віджэта"</string>
diff --git a/res/values-bg/strings.xml b/res/values-bg/strings.xml
index 769c538..2a9ac1b 100644
--- a/res/values-bg/strings.xml
+++ b/res/values-bg/strings.xml
@@ -38,16 +38,19 @@
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"Ширина %1$d и височина %2$d"</string>
<string name="widget_preview_context_description" msgid="9045841361655787574">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> приспособление"</string>
+ <string name="widget_preview_name_and_dims_content_description" msgid="8489038126122831595">"Приспособлението „<xliff:g id="WIDGET_NAME">%1$s</xliff:g>“ е широко %2$d и високо %3$d"</string>
<string name="add_item_request_drag_hint" msgid="8730547755622776606">"Докоснете приспособлението и го задръжте, за да го местите на началния екран"</string>
<string name="add_to_home_screen" msgid="9168649446635919791">"Добавяне към началния екран"</string>
<string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"Приспособлението <xliff:g id="WIDGET_NAME">%1$s</xliff:g> е добавено към началния екран"</string>
<string name="suggested_widgets_header_title" msgid="1844314680798145222">"Предложения"</string>
- <string name="productivity_widget_recommendation_category_label" msgid="1722113555721820766">"Основните за деня ви"</string>
- <string name="news_widget_recommendation_category_label" msgid="3908242346768119070">"Новини за вас"</string>
+ <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Приспособления, които трябва да изпробвате"</string>
+ <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"Новини и списания"</string>
<string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"Зоната ви за разпускане"</string>
- <string name="fitness_widget_recommendation_category_label" msgid="2657652999128882431">"Постигнете фитнес целите си"</string>
- <string name="weather_widget_recommendation_category_label" msgid="6712678763480668598">"Бъдете една крачка напред с прогнозата за времето"</string>
- <string name="others_widget_recommendation_category_label" msgid="897876078077284733">"Може също да харесате"</string>
+ <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"Развлечения"</string>
+ <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"Социални мрежи"</string>
+ <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"Здраве и фитнес"</string>
+ <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"Времето"</string>
+ <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"Предложено за вас"</string>
<string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"Приспособленията за <xliff:g id="SELECTED_HEADER">%1$s</xliff:g> са отдясно, търсенето и опциите – отляво"</string>
<string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# приспособление}other{# приспособления}}"</string>
<string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# пряк път}other{# преки пътя}}"</string>
@@ -61,6 +64,8 @@
<string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Служебни"</string>
<string name="widget_category_conversations" msgid="8894438636213590446">"Разговори"</string>
<string name="widget_category_note_taking" msgid="3469689394504266039">"Водене на бележки"</string>
+ <string name="widget_add_button_label" msgid="2761267068711937179">"Добавяне"</string>
+ <string name="widget_add_button_content_description" msgid="1810530016360039643">"Добавяне на приспособлението „<xliff:g id="WIDGET_NAME">%1$s</xliff:g>“"</string>
<string name="widget_education_header" msgid="4874760613775913787">"Лесен достъп до полезна информация"</string>
<string name="widget_education_content" msgid="1731667670753497052">"За да получавате информация, без да отваряте приложенията, можете да добавите приспособления към началния екран"</string>
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Докоснете, за да промените настройките на приспособлението"</string>
diff --git a/res/values-bn/strings.xml b/res/values-bn/strings.xml
index cc6a8aa..ca6ee84 100644
--- a/res/values-bn/strings.xml
+++ b/res/values-bn/strings.xml
@@ -38,16 +38,19 @@
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"%2$d উচ্চতা অনুযায়ী %1$d প্রস্থ"</string>
<string name="widget_preview_context_description" msgid="9045841361655787574">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g>টি উইজেট"</string>
+ <string name="widget_preview_name_and_dims_content_description" msgid="8489038126122831595">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> উইজেট, %2$d প্রস্থ ও %3$d উচ্চতা"</string>
<string name="add_item_request_drag_hint" msgid="8730547755622776606">"হোম স্ক্রিনের যেকোনও জায়গায় নিয়ে যেতে, উইজেট টাচ করে ধরে থাকুন"</string>
<string name="add_to_home_screen" msgid="9168649446635919791">"হোম স্ক্রিনে যোগ করুন"</string>
<string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> উইজেট হোম স্ক্রিনে যোগ করা হয়েছে"</string>
<string name="suggested_widgets_header_title" msgid="1844314680798145222">"সাজেশন"</string>
- <string name="productivity_widget_recommendation_category_label" msgid="1722113555721820766">"আপনার নিত্য প্রয়োজনীয় জিনিস"</string>
- <string name="news_widget_recommendation_category_label" msgid="3908242346768119070">"আপনার জন্য খবর"</string>
+ <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"প্রয়োজনীয় জিনিস"</string>
+ <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"খবর ও ম্যাগাজিন"</string>
<string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"আপনার চিল জোন"</string>
- <string name="fitness_widget_recommendation_category_label" msgid="2657652999128882431">"আপনার ফিটনেস সংক্রান্ত লক্ষ্যে পৌঁছান"</string>
- <string name="weather_widget_recommendation_category_label" msgid="6712678763480668598">"আবহাওয়া সম্পর্কে আগেই খবর পান"</string>
- <string name="others_widget_recommendation_category_label" msgid="897876078077284733">"আপনার এগুলিও পছন্দ হতে পারে"</string>
+ <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"বিনোদন"</string>
+ <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"সোশ্যাল"</string>
+ <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"স্বাস্থ্য ও ফিটনেস"</string>
+ <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"আবহাওয়া"</string>
+ <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"আপনার জন্য সাজেস্ট করা হয়েছে"</string>
<string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"<xliff:g id="SELECTED_HEADER">%1$s</xliff:g> উইজেট ডানদিকে, সার্চ ও বিকল্প বাঁদিকে রয়েছে"</string>
<string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{#টি উইজেট}one{#টি উইজেট}other{#টি উইজেট}}"</string>
<string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{#টি শর্টকাট}one{#টি শর্টকাট}other{#টি শর্টকাট}}"</string>
@@ -61,6 +64,8 @@
<string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"অফিস"</string>
<string name="widget_category_conversations" msgid="8894438636213590446">"কথোপকথন"</string>
<string name="widget_category_note_taking" msgid="3469689394504266039">"নোট নেওয়া"</string>
+ <string name="widget_add_button_label" msgid="2761267068711937179">"যোগ করুন"</string>
+ <string name="widget_add_button_content_description" msgid="1810530016360039643">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> উইজেট যোগ করুন"</string>
<string name="widget_education_header" msgid="4874760613775913787">"সহজেই দরকারি তথ্য পান"</string>
<string name="widget_education_content" msgid="1731667670753497052">"অ্যাপ না খুলেই তথ্য পাওয়ার জন্য, হোম স্ক্রিনে উইজেট যোগ করতে পারবেন"</string>
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"উইজেট সেটিংস পরিবর্তন করতে ট্যাপ করুন"</string>
diff --git a/res/values-bs/strings.xml b/res/values-bs/strings.xml
index 0c43561..96f45c1 100644
--- a/res/values-bs/strings.xml
+++ b/res/values-bs/strings.xml
@@ -38,16 +38,19 @@
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"Širina %1$d, visina %2$d"</string>
<string name="widget_preview_context_description" msgid="9045841361655787574">"Vidžet <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
+ <string name="widget_preview_name_and_dims_content_description" msgid="8489038126122831595">"Vidžet <xliff:g id="WIDGET_NAME">%1$s</xliff:g>, širina %2$d, visina %3$d"</string>
<string name="add_item_request_drag_hint" msgid="8730547755622776606">"Dodirnite i držite vidžet da ga pomjerate po početnom ekranu"</string>
<string name="add_to_home_screen" msgid="9168649446635919791">"Dodaj na početni ekran"</string>
<string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"Vidžet <xliff:g id="WIDGET_NAME">%1$s</xliff:g> je dodan na početni ekran"</string>
<string name="suggested_widgets_header_title" msgid="1844314680798145222">"Prijedlozi"</string>
- <string name="productivity_widget_recommendation_category_label" msgid="1722113555721820766">"Svakodnevni osnovni vidžeti"</string>
- <string name="news_widget_recommendation_category_label" msgid="3908242346768119070">"Vijesti za vas"</string>
+ <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Osnovne aplikacije"</string>
+ <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"Vijesti i časopisi"</string>
<string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"Vaša zona opuštanja"</string>
- <string name="fitness_widget_recommendation_category_label" msgid="2657652999128882431">"Postignite svoje ciljeve fitnesa"</string>
- <string name="weather_widget_recommendation_category_label" msgid="6712678763480668598">"Ne dajte da vas uhvati oluja"</string>
- <string name="others_widget_recommendation_category_label" msgid="897876078077284733">"Možda vam se svidi i ovo"</string>
+ <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"Zabava"</string>
+ <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"Društvene mreže"</string>
+ <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"Zdravlje i fitnes"</string>
+ <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"Vrijeme"</string>
+ <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"Predloženo za vas"</string>
<string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"Vidžeti aplikacije <xliff:g id="SELECTED_HEADER">%1$s</xliff:g> su na desnoj, a pretraživanje i opcije na lijevoj strani"</string>
<string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# vidžet}one{# vidžet}few{# vidžeta}other{# vidžeta}}"</string>
<string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# prečica}one{# prečica}few{# prečice}other{# prečica}}"</string>
@@ -61,6 +64,8 @@
<string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Posao"</string>
<string name="widget_category_conversations" msgid="8894438636213590446">"Razgovori"</string>
<string name="widget_category_note_taking" msgid="3469689394504266039">"Pisanje bilješki"</string>
+ <string name="widget_add_button_label" msgid="2761267068711937179">"Dodaj"</string>
+ <string name="widget_add_button_content_description" msgid="1810530016360039643">"Dodavanje vidžeta <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
<string name="widget_education_header" msgid="4874760613775913787">"Korisne informacije nadohvat ruke"</string>
<string name="widget_education_content" msgid="1731667670753497052">"Da dobijete informacije bez otvaranja aplikacija, možete dodati vidžete na početni ekran"</string>
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Dodirnite da promijenite postavke vidžeta"</string>
diff --git a/res/values-ca/strings.xml b/res/values-ca/strings.xml
index 37d8626..666cb15 100644
--- a/res/values-ca/strings.xml
+++ b/res/values-ca/strings.xml
@@ -38,16 +38,19 @@
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d d\'amplada per %2$d d\'alçada"</string>
<string name="widget_preview_context_description" msgid="9045841361655787574">"Widget de <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
+ <string name="widget_preview_name_and_dims_content_description" msgid="8489038126122831595">"Widget <xliff:g id="WIDGET_NAME">%1$s</xliff:g>, %2$d d\'amplada per %3$d d\'alçària"</string>
<string name="add_item_request_drag_hint" msgid="8730547755622776606">"Mantén premut el widget per moure\'l per la pantalla d\'inici"</string>
<string name="add_to_home_screen" msgid="9168649446635919791">"Afegeix a la pantalla d\'inici"</string>
<string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"El widget <xliff:g id="WIDGET_NAME">%1$s</xliff:g> s\'ha afegit a la pantalla d\'inici"</string>
<string name="suggested_widgets_header_title" msgid="1844314680798145222">"Suggeriments"</string>
- <string name="productivity_widget_recommendation_category_label" msgid="1722113555721820766">"Els teus essencials per al dia a dia"</string>
- <string name="news_widget_recommendation_category_label" msgid="3908242346768119070">"Notícies per a tu"</string>
+ <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Essencials"</string>
+ <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"Notícies i revistes"</string>
<string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"La teva zona de relax"</string>
- <string name="fitness_widget_recommendation_category_label" msgid="2657652999128882431">"Assoleix els teus objectius de fitnes"</string>
- <string name="weather_widget_recommendation_category_label" msgid="6712678763480668598">"Que no et sorprengui el temps"</string>
- <string name="others_widget_recommendation_category_label" msgid="897876078077284733">"També et pot agradar"</string>
+ <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"Entreteniment"</string>
+ <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"Social"</string>
+ <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"Salut i fitnes"</string>
+ <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"Temps"</string>
+ <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"Suggeriments personalitzats"</string>
<string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"Widgets de <xliff:g id="SELECTED_HEADER">%1$s</xliff:g> a la dreta, cerca i opcions a l\'esquerra"</string>
<string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# widget}other{# widgets}}"</string>
<string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# drecera}other{# dreceres}}"</string>
@@ -61,6 +64,8 @@
<string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Treball"</string>
<string name="widget_category_conversations" msgid="8894438636213590446">"Converses"</string>
<string name="widget_category_note_taking" msgid="3469689394504266039">"Presa de notes"</string>
+ <string name="widget_add_button_label" msgid="2761267068711937179">"Afegeix"</string>
+ <string name="widget_add_button_content_description" msgid="1810530016360039643">"Afegeix el widget <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
<string name="widget_education_header" msgid="4874760613775913787">"Informació útil a l\'abast de la mà"</string>
<string name="widget_education_content" msgid="1731667670753497052">"Per obtenir informació sense obrir les aplicacions, pots afegir widgets a la pantalla d\'inici"</string>
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Toca per canviar la configuració del widget"</string>
diff --git a/res/values-cs/strings.xml b/res/values-cs/strings.xml
index 2def07c..1d02668 100644
--- a/res/values-cs/strings.xml
+++ b/res/values-cs/strings.xml
@@ -38,16 +38,19 @@
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"šířka %1$d, výška %2$d"</string>
<string name="widget_preview_context_description" msgid="9045841361655787574">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> widget"</string>
+ <string name="widget_preview_name_and_dims_content_description" msgid="8489038126122831595">"Widget <xliff:g id="WIDGET_NAME">%1$s</xliff:g>, šířka %2$d, výška %3$d"</string>
<string name="add_item_request_drag_hint" msgid="8730547755622776606">"Pokud chcete widgetem pohybovat po ploše, podržte ho"</string>
<string name="add_to_home_screen" msgid="9168649446635919791">"Přidat na plochu"</string>
<string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"Widget <xliff:g id="WIDGET_NAME">%1$s</xliff:g> byl přidán na plochu"</string>
<string name="suggested_widgets_header_title" msgid="1844314680798145222">"Návrhy"</string>
- <string name="productivity_widget_recommendation_category_label" msgid="1722113555721820766">"Vaše každodenní nezbytnosti"</string>
- <string name="news_widget_recommendation_category_label" msgid="3908242346768119070">"Zprávy pro vás"</string>
+ <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Nezbytnosti"</string>
+ <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"Zprávy a časopisy"</string>
<string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"Vaše klidová zóna"</string>
- <string name="fitness_widget_recommendation_category_label" msgid="2657652999128882431">"Dosažení kondičních cílů"</string>
- <string name="weather_widget_recommendation_category_label" msgid="6712678763480668598">"Mějte přehled o počasí"</string>
- <string name="others_widget_recommendation_category_label" msgid="897876078077284733">"Také by se vám mohlo líbit"</string>
+ <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"Zábava"</string>
+ <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"Sociální sítě"</string>
+ <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"Zdraví a fitness"</string>
+ <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"Počasí"</string>
+ <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"Návrhy pro vás"</string>
<string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"Widgety <xliff:g id="SELECTED_HEADER">%1$s</xliff:g> vpravo, vyhledávání a možnosti vlevo"</string>
<string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{ # widget}few{# widgety}many{# widgetu}other{# widgetů}}"</string>
<string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# zkratka}few{# zkratky}many{# zkratky}other{# zkratek}}"</string>
@@ -61,6 +64,8 @@
<string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Práce"</string>
<string name="widget_category_conversations" msgid="8894438636213590446">"Konverzace"</string>
<string name="widget_category_note_taking" msgid="3469689394504266039">"Psaní poznámek"</string>
+ <string name="widget_add_button_label" msgid="2761267068711937179">"Přidat"</string>
+ <string name="widget_add_button_content_description" msgid="1810530016360039643">"Přidat widget <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
<string name="widget_education_header" msgid="4874760613775913787">"Užitečné informace na dosah"</string>
<string name="widget_education_content" msgid="1731667670753497052">"Pokud chcete mít informace k dispozici bez otevírání aplikací, můžete si na plochu přidat widgety"</string>
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Klepnutím změníte nastavení widgetu"</string>
diff --git a/res/values-da/strings.xml b/res/values-da/strings.xml
index 57053e6..2f1aa65 100644
--- a/res/values-da/strings.xml
+++ b/res/values-da/strings.xml
@@ -38,16 +38,19 @@
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d i bredden og %2$d i højden"</string>
<string name="widget_preview_context_description" msgid="9045841361655787574">"Widgetten <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
+ <string name="widget_preview_name_and_dims_content_description" msgid="8489038126122831595">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g>-widget, %2$d i bredden og %3$d i højden"</string>
<string name="add_item_request_drag_hint" msgid="8730547755622776606">"Hold widgetten nede for at flytte den rundt på startskærmen"</string>
<string name="add_to_home_screen" msgid="9168649446635919791">"Føj til startskærm"</string>
<string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"Widgetten <xliff:g id="WIDGET_NAME">%1$s</xliff:g> blev føjet til startskærmen"</string>
<string name="suggested_widgets_header_title" msgid="1844314680798145222">"Forslag"</string>
- <string name="productivity_widget_recommendation_category_label" msgid="1722113555721820766">"Dine vigtige apps"</string>
- <string name="news_widget_recommendation_category_label" msgid="3908242346768119070">"Nyheder til dig"</string>
+ <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Vigtige ting"</string>
+ <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"Aviser og blade"</string>
<string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"Dit afslapningshjørne"</string>
- <string name="fitness_widget_recommendation_category_label" msgid="2657652999128882431">"Nå dine fitnessmål"</string>
- <string name="weather_widget_recommendation_category_label" msgid="6712678763480668598">"Vær på forkant med vejret"</string>
- <string name="others_widget_recommendation_category_label" msgid="897876078077284733">"Du kan måske også lide"</string>
+ <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"Underholdning"</string>
+ <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"Socialt"</string>
+ <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"Sundhed og fitness"</string>
+ <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"Vejr"</string>
+ <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"Forslag til dig"</string>
<string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"<xliff:g id="SELECTED_HEADER">%1$s</xliff:g>-widgets til højre, søgning og valgmuligheder til venstre"</string>
<string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# widget}one{# widget}other{# widgets}}"</string>
<string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# genvej}one{# genvej}other{# genveje}}"</string>
@@ -61,6 +64,8 @@
<string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Arbejde"</string>
<string name="widget_category_conversations" msgid="8894438636213590446">"Samtaler"</string>
<string name="widget_category_note_taking" msgid="3469689394504266039">"Notetagning"</string>
+ <string name="widget_add_button_label" msgid="2761267068711937179">"Tilføj"</string>
+ <string name="widget_add_button_content_description" msgid="1810530016360039643">"Tilføj <xliff:g id="WIDGET_NAME">%1$s</xliff:g>-widget"</string>
<string name="widget_education_header" msgid="4874760613775913787">"Nyttige oplysninger lige ved hånden"</string>
<string name="widget_education_content" msgid="1731667670753497052">"Hvis du vil have oplysninger uden at åbne apps, kan du føje widgets til din startskærm"</string>
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Tryk for at ændre widgetindstillinger"</string>
diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml
index 5eff0b0..e19a9b0 100644
--- a/res/values-de/strings.xml
+++ b/res/values-de/strings.xml
@@ -38,16 +38,19 @@
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d breit und %2$d hoch"</string>
<string name="widget_preview_context_description" msgid="9045841361655787574">"Widget „<xliff:g id="WIDGET_NAME">%1$s</xliff:g>“"</string>
+ <string name="widget_preview_name_and_dims_content_description" msgid="8489038126122831595">"Widget „<xliff:g id="WIDGET_NAME">%1$s</xliff:g>“, %2$d breit und %3$d hoch"</string>
<string name="add_item_request_drag_hint" msgid="8730547755622776606">"Wenn du das Widget auf dem Startbildschirm verschieben möchtest, halte es gedrückt"</string>
<string name="add_to_home_screen" msgid="9168649446635919791">"Zum Startbildschirm hinzufügen"</string>
<string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g>-Widget zum Startbildschirm hinzugefügt"</string>
<string name="suggested_widgets_header_title" msgid="1844314680798145222">"Vorschläge"</string>
- <string name="productivity_widget_recommendation_category_label" msgid="1722113555721820766">"Deine täglichen Essentials"</string>
- <string name="news_widget_recommendation_category_label" msgid="3908242346768119070">"Neuigkeiten für dich"</string>
+ <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Must-haves"</string>
+ <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"Nachrichten und Zeitschriften"</string>
<string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"Zum Entspannen"</string>
- <string name="fitness_widget_recommendation_category_label" msgid="2657652999128882431">"Erreiche deine Fitnessziele"</string>
- <string name="weather_widget_recommendation_category_label" msgid="6712678763480668598">"Dem Wetter einen Schritt voraus"</string>
- <string name="others_widget_recommendation_category_label" msgid="897876078077284733">"Das könnte dir auch gefallen"</string>
+ <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"Unterhaltung"</string>
+ <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"Soziale Netzwerke"</string>
+ <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"Gesundheit und Fitness"</string>
+ <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"Wetter"</string>
+ <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"Vorschläge für dich"</string>
<string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"<xliff:g id="SELECTED_HEADER">%1$s</xliff:g>-Widgets rechts, Suche und Optionen links"</string>
<string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# Widget}other{# Widgets}}"</string>
<string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# Verknüpfung}other{# Verknüpfungen}}"</string>
@@ -61,6 +64,8 @@
<string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Geschäftlich"</string>
<string name="widget_category_conversations" msgid="8894438636213590446">"Unterhaltungen"</string>
<string name="widget_category_note_taking" msgid="3469689394504266039">"Notizen"</string>
+ <string name="widget_add_button_label" msgid="2761267068711937179">"Hinzufügen"</string>
+ <string name="widget_add_button_content_description" msgid="1810530016360039643">"Widget „<xliff:g id="WIDGET_NAME">%1$s</xliff:g>“ hinzufügen"</string>
<string name="widget_education_header" msgid="4874760613775913787">"Praktische Informationen – immer zur Hand"</string>
<string name="widget_education_content" msgid="1731667670753497052">"Wenn du Informationen erhalten möchtest, ohne Apps zu öffnen, kannst du deinem Startbildschirm Widgets hinzufügen"</string>
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Tippen, um die Widget-Einstellungen zu ändern"</string>
diff --git a/res/values-el/strings.xml b/res/values-el/strings.xml
index d868040..55575e7 100644
--- a/res/values-el/strings.xml
+++ b/res/values-el/strings.xml
@@ -38,16 +38,19 @@
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"Πλάτος %1$d επί ύψος %2$d"</string>
<string name="widget_preview_context_description" msgid="9045841361655787574">"Γραφικό στοιχείο <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
+ <string name="widget_preview_name_and_dims_content_description" msgid="8489038126122831595">"Γραφικό στοιχείο <xliff:g id="WIDGET_NAME">%1$s</xliff:g>, %2$d πλάτος επί %3$d ύψος"</string>
<string name="add_item_request_drag_hint" msgid="8730547755622776606">"Αγγίξτε παρατεταμένα το γραφικό στοιχείο για να το μετακινήσετε στην αρχική οθόνη"</string>
<string name="add_to_home_screen" msgid="9168649446635919791">"Προσθήκη στην αρχική οθόνη"</string>
<string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"Το γραφικό στοιχείο <xliff:g id="WIDGET_NAME">%1$s</xliff:g> προστέθηκε στην αρχική οθόνη."</string>
<string name="suggested_widgets_header_title" msgid="1844314680798145222">"Προτάσεις"</string>
- <string name="productivity_widget_recommendation_category_label" msgid="1722113555721820766">"Τα καθημερινά απαραίτητα"</string>
- <string name="news_widget_recommendation_category_label" msgid="3908242346768119070">"Ειδήσεις για εσάς"</string>
+ <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Απαραίτητα"</string>
+ <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"Ειδήσεις και περιοδικά"</string>
<string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"Ο δικός σας τρόπος χαλάρωσης"</string>
- <string name="fitness_widget_recommendation_category_label" msgid="2657652999128882431">"Επιτύχετε τους στόχους που έχετε θέσει για τη φυσική σας κατάσταση"</string>
- <string name="weather_widget_recommendation_category_label" msgid="6712678763480668598">"Ετοιμαστείτε για κάθε καιρό"</string>
- <string name="others_widget_recommendation_category_label" msgid="897876078077284733">"Μπορεί να σας αρέσουν επίσης"</string>
+ <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"Ψυχαγωγία"</string>
+ <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"Κοινωνικά δίκτυα"</string>
+ <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"Υγεία και φυσική κατάσταση"</string>
+ <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"Καιρός"</string>
+ <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"Προτεινόμενα για εσάς"</string>
<string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"Γραφικά στοιχεία <xliff:g id="SELECTED_HEADER">%1$s</xliff:g> στα δεξιά, αναζήτηση και επιλογές στα αριστερά"</string>
<string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# γραφικό στοιχείο}other{# γραφικά στοιχεία}}"</string>
<string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# συντόμευση}other{# συντομεύσεις}}"</string>
@@ -61,6 +64,8 @@
<string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Εργασίας"</string>
<string name="widget_category_conversations" msgid="8894438636213590446">"Συζητήσεις"</string>
<string name="widget_category_note_taking" msgid="3469689394504266039">"Δημιουργία σημειώσεων"</string>
+ <string name="widget_add_button_label" msgid="2761267068711937179">"Προσθήκη"</string>
+ <string name="widget_add_button_content_description" msgid="1810530016360039643">"Προσθήκη του γραφικού στοιχείου <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
<string name="widget_education_header" msgid="4874760613775913787">"Χρήσιμες πληροφορίες στη διάθεσή σας"</string>
<string name="widget_education_content" msgid="1731667670753497052">"Για να λάβετε πληροφορίες χωρίς να ανοίξετε εφαρμογές, μπορείτε να προσθέσετε γραφικά στοιχεία στην αρχική οθόνη."</string>
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Πατήστε για αλλαγή των ρυθμίσεων του γραφικού στοιχείου"</string>
diff --git a/res/values-en-rAU/strings.xml b/res/values-en-rAU/strings.xml
index cee4d2a..d86770a 100644
--- a/res/values-en-rAU/strings.xml
+++ b/res/values-en-rAU/strings.xml
@@ -38,16 +38,19 @@
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d wide by %2$d high"</string>
<string name="widget_preview_context_description" msgid="9045841361655787574">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> widget"</string>
+ <string name="widget_preview_name_and_dims_content_description" msgid="8489038126122831595">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> widget, %2$d wide by %3$d high"</string>
<string name="add_item_request_drag_hint" msgid="8730547755622776606">"Touch and hold the widget to move it around the home screen"</string>
<string name="add_to_home_screen" msgid="9168649446635919791">"Add to home screen"</string>
<string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> widget added to home screen"</string>
<string name="suggested_widgets_header_title" msgid="1844314680798145222">"Suggestions"</string>
- <string name="productivity_widget_recommendation_category_label" msgid="1722113555721820766">"Your daily essentials"</string>
- <string name="news_widget_recommendation_category_label" msgid="3908242346768119070">"News for you"</string>
+ <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Essentials"</string>
+ <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"News and magazines"</string>
<string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"Your chill zone"</string>
- <string name="fitness_widget_recommendation_category_label" msgid="2657652999128882431">"Reach your fitness goals"</string>
- <string name="weather_widget_recommendation_category_label" msgid="6712678763480668598">"Stay ahead of the weather"</string>
- <string name="others_widget_recommendation_category_label" msgid="897876078077284733">"You might also like"</string>
+ <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"Entertainment"</string>
+ <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"Social"</string>
+ <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"Health and fitness"</string>
+ <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"Weather"</string>
+ <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"Suggested for you"</string>
<string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"<xliff:g id="SELECTED_HEADER">%1$s</xliff:g> widgets on right, search and options on left"</string>
<string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# widget}other{# widgets}}"</string>
<string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# shortcut}other{# shortcuts}}"</string>
@@ -61,6 +64,8 @@
<string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Work"</string>
<string name="widget_category_conversations" msgid="8894438636213590446">"Conversations"</string>
<string name="widget_category_note_taking" msgid="3469689394504266039">"Note-taking"</string>
+ <string name="widget_add_button_label" msgid="2761267068711937179">"Add"</string>
+ <string name="widget_add_button_content_description" msgid="1810530016360039643">"Add <xliff:g id="WIDGET_NAME">%1$s</xliff:g> widget"</string>
<string name="widget_education_header" msgid="4874760613775913787">"Useful info at your fingertips"</string>
<string name="widget_education_content" msgid="1731667670753497052">"To get info without opening apps, you can add widgets to your home screen"</string>
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Tap to change widget settings"</string>
diff --git a/res/values-en-rCA/strings.xml b/res/values-en-rCA/strings.xml
index 87cd9eb..2967941 100644
--- a/res/values-en-rCA/strings.xml
+++ b/res/values-en-rCA/strings.xml
@@ -38,16 +38,19 @@
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d wide by %2$d high"</string>
<string name="widget_preview_context_description" msgid="9045841361655787574">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> widget"</string>
+ <string name="widget_preview_name_and_dims_content_description" msgid="8489038126122831595">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> widget, %2$d wide by %3$d high"</string>
<string name="add_item_request_drag_hint" msgid="8730547755622776606">"Touch and hold the widget to move it around the home screen"</string>
<string name="add_to_home_screen" msgid="9168649446635919791">"Add to home screen"</string>
<string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> widget added to home screen"</string>
<string name="suggested_widgets_header_title" msgid="1844314680798145222">"Suggestions"</string>
- <string name="productivity_widget_recommendation_category_label" msgid="1722113555721820766">"Your Daily Essentials"</string>
- <string name="news_widget_recommendation_category_label" msgid="3908242346768119070">"News For You"</string>
+ <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Essentials"</string>
+ <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"News & magazines"</string>
<string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"Your Chill Zone"</string>
- <string name="fitness_widget_recommendation_category_label" msgid="2657652999128882431">"Reach Your Fitness Goals"</string>
- <string name="weather_widget_recommendation_category_label" msgid="6712678763480668598">"Stay Ahead of the Weather"</string>
- <string name="others_widget_recommendation_category_label" msgid="897876078077284733">"You Might Also Like"</string>
+ <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"Entertainment"</string>
+ <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"Social"</string>
+ <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"Health & fitness"</string>
+ <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"Weather"</string>
+ <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"Suggested for you"</string>
<string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"<xliff:g id="SELECTED_HEADER">%1$s</xliff:g> widgets on right, search and options on left"</string>
<string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# widget}other{# widgets}}"</string>
<string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# shortcut}other{# shortcuts}}"</string>
@@ -61,6 +64,8 @@
<string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Work"</string>
<string name="widget_category_conversations" msgid="8894438636213590446">"Conversations"</string>
<string name="widget_category_note_taking" msgid="3469689394504266039">"Note-taking"</string>
+ <string name="widget_add_button_label" msgid="2761267068711937179">"Add"</string>
+ <string name="widget_add_button_content_description" msgid="1810530016360039643">"Add <xliff:g id="WIDGET_NAME">%1$s</xliff:g> widget"</string>
<string name="widget_education_header" msgid="4874760613775913787">"Useful info at your fingertips"</string>
<string name="widget_education_content" msgid="1731667670753497052">"To get info without opening apps, you can add widgets to your home screen"</string>
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Tap to change widget settings"</string>
diff --git a/res/values-en-rGB/strings.xml b/res/values-en-rGB/strings.xml
index cee4d2a..d86770a 100644
--- a/res/values-en-rGB/strings.xml
+++ b/res/values-en-rGB/strings.xml
@@ -38,16 +38,19 @@
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d wide by %2$d high"</string>
<string name="widget_preview_context_description" msgid="9045841361655787574">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> widget"</string>
+ <string name="widget_preview_name_and_dims_content_description" msgid="8489038126122831595">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> widget, %2$d wide by %3$d high"</string>
<string name="add_item_request_drag_hint" msgid="8730547755622776606">"Touch and hold the widget to move it around the home screen"</string>
<string name="add_to_home_screen" msgid="9168649446635919791">"Add to home screen"</string>
<string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> widget added to home screen"</string>
<string name="suggested_widgets_header_title" msgid="1844314680798145222">"Suggestions"</string>
- <string name="productivity_widget_recommendation_category_label" msgid="1722113555721820766">"Your daily essentials"</string>
- <string name="news_widget_recommendation_category_label" msgid="3908242346768119070">"News for you"</string>
+ <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Essentials"</string>
+ <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"News and magazines"</string>
<string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"Your chill zone"</string>
- <string name="fitness_widget_recommendation_category_label" msgid="2657652999128882431">"Reach your fitness goals"</string>
- <string name="weather_widget_recommendation_category_label" msgid="6712678763480668598">"Stay ahead of the weather"</string>
- <string name="others_widget_recommendation_category_label" msgid="897876078077284733">"You might also like"</string>
+ <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"Entertainment"</string>
+ <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"Social"</string>
+ <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"Health and fitness"</string>
+ <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"Weather"</string>
+ <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"Suggested for you"</string>
<string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"<xliff:g id="SELECTED_HEADER">%1$s</xliff:g> widgets on right, search and options on left"</string>
<string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# widget}other{# widgets}}"</string>
<string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# shortcut}other{# shortcuts}}"</string>
@@ -61,6 +64,8 @@
<string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Work"</string>
<string name="widget_category_conversations" msgid="8894438636213590446">"Conversations"</string>
<string name="widget_category_note_taking" msgid="3469689394504266039">"Note-taking"</string>
+ <string name="widget_add_button_label" msgid="2761267068711937179">"Add"</string>
+ <string name="widget_add_button_content_description" msgid="1810530016360039643">"Add <xliff:g id="WIDGET_NAME">%1$s</xliff:g> widget"</string>
<string name="widget_education_header" msgid="4874760613775913787">"Useful info at your fingertips"</string>
<string name="widget_education_content" msgid="1731667670753497052">"To get info without opening apps, you can add widgets to your home screen"</string>
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Tap to change widget settings"</string>
diff --git a/res/values-en-rIN/strings.xml b/res/values-en-rIN/strings.xml
index cee4d2a..d86770a 100644
--- a/res/values-en-rIN/strings.xml
+++ b/res/values-en-rIN/strings.xml
@@ -38,16 +38,19 @@
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d wide by %2$d high"</string>
<string name="widget_preview_context_description" msgid="9045841361655787574">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> widget"</string>
+ <string name="widget_preview_name_and_dims_content_description" msgid="8489038126122831595">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> widget, %2$d wide by %3$d high"</string>
<string name="add_item_request_drag_hint" msgid="8730547755622776606">"Touch and hold the widget to move it around the home screen"</string>
<string name="add_to_home_screen" msgid="9168649446635919791">"Add to home screen"</string>
<string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> widget added to home screen"</string>
<string name="suggested_widgets_header_title" msgid="1844314680798145222">"Suggestions"</string>
- <string name="productivity_widget_recommendation_category_label" msgid="1722113555721820766">"Your daily essentials"</string>
- <string name="news_widget_recommendation_category_label" msgid="3908242346768119070">"News for you"</string>
+ <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Essentials"</string>
+ <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"News and magazines"</string>
<string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"Your chill zone"</string>
- <string name="fitness_widget_recommendation_category_label" msgid="2657652999128882431">"Reach your fitness goals"</string>
- <string name="weather_widget_recommendation_category_label" msgid="6712678763480668598">"Stay ahead of the weather"</string>
- <string name="others_widget_recommendation_category_label" msgid="897876078077284733">"You might also like"</string>
+ <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"Entertainment"</string>
+ <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"Social"</string>
+ <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"Health and fitness"</string>
+ <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"Weather"</string>
+ <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"Suggested for you"</string>
<string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"<xliff:g id="SELECTED_HEADER">%1$s</xliff:g> widgets on right, search and options on left"</string>
<string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# widget}other{# widgets}}"</string>
<string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# shortcut}other{# shortcuts}}"</string>
@@ -61,6 +64,8 @@
<string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Work"</string>
<string name="widget_category_conversations" msgid="8894438636213590446">"Conversations"</string>
<string name="widget_category_note_taking" msgid="3469689394504266039">"Note-taking"</string>
+ <string name="widget_add_button_label" msgid="2761267068711937179">"Add"</string>
+ <string name="widget_add_button_content_description" msgid="1810530016360039643">"Add <xliff:g id="WIDGET_NAME">%1$s</xliff:g> widget"</string>
<string name="widget_education_header" msgid="4874760613775913787">"Useful info at your fingertips"</string>
<string name="widget_education_content" msgid="1731667670753497052">"To get info without opening apps, you can add widgets to your home screen"</string>
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Tap to change widget settings"</string>
diff --git a/res/values-en-rXC/strings.xml b/res/values-en-rXC/strings.xml
index ae84841..dab6052 100644
--- a/res/values-en-rXC/strings.xml
+++ b/res/values-en-rXC/strings.xml
@@ -38,16 +38,19 @@
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d wide by %2$d high"</string>
<string name="widget_preview_context_description" msgid="9045841361655787574">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> widget"</string>
+ <string name="widget_preview_name_and_dims_content_description" msgid="8489038126122831595">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> widget, %2$d wide by %3$d high"</string>
<string name="add_item_request_drag_hint" msgid="8730547755622776606">"Touch & hold the widget to move it around the home screen"</string>
<string name="add_to_home_screen" msgid="9168649446635919791">"Add to home screen"</string>
<string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> widget added to home screen"</string>
<string name="suggested_widgets_header_title" msgid="1844314680798145222">"Suggestions"</string>
- <string name="productivity_widget_recommendation_category_label" msgid="1722113555721820766">"Your Daily Essentials"</string>
- <string name="news_widget_recommendation_category_label" msgid="3908242346768119070">"News For You"</string>
+ <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Essentials"</string>
+ <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"News & magazines"</string>
<string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"Your Chill Zone"</string>
- <string name="fitness_widget_recommendation_category_label" msgid="2657652999128882431">"Reach Your Fitness Goals"</string>
- <string name="weather_widget_recommendation_category_label" msgid="6712678763480668598">"Stay Ahead of the Weather"</string>
- <string name="others_widget_recommendation_category_label" msgid="897876078077284733">"You Might Also Like"</string>
+ <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"Entertainment"</string>
+ <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"Social"</string>
+ <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"Health & fitness"</string>
+ <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"Weather"</string>
+ <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"Suggested for you"</string>
<string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"<xliff:g id="SELECTED_HEADER">%1$s</xliff:g> widgets on right, search and options on left"</string>
<string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# widget}other{# widgets}}"</string>
<string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# shortcut}other{# shortcuts}}"</string>
@@ -61,6 +64,8 @@
<string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Work"</string>
<string name="widget_category_conversations" msgid="8894438636213590446">"Conversations"</string>
<string name="widget_category_note_taking" msgid="3469689394504266039">"Note-taking"</string>
+ <string name="widget_add_button_label" msgid="2761267068711937179">"Add"</string>
+ <string name="widget_add_button_content_description" msgid="1810530016360039643">"Add <xliff:g id="WIDGET_NAME">%1$s</xliff:g> widget"</string>
<string name="widget_education_header" msgid="4874760613775913787">"Useful info at your fingertips"</string>
<string name="widget_education_content" msgid="1731667670753497052">"To get info without opening apps, you can add widgets to your home screen"</string>
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Tap to change widget settings"</string>
diff --git a/res/values-es-rUS/strings.xml b/res/values-es-rUS/strings.xml
index a7259a2..21325b9 100644
--- a/res/values-es-rUS/strings.xml
+++ b/res/values-es-rUS/strings.xml
@@ -38,16 +38,19 @@
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d de ancho por %2$d de alto"</string>
<string name="widget_preview_context_description" msgid="9045841361655787574">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> widget"</string>
+ <string name="widget_preview_name_and_dims_content_description" msgid="8489038126122831595">"Widget <xliff:g id="WIDGET_NAME">%1$s</xliff:g>, %2$d de ancho por %3$d de alto"</string>
<string name="add_item_request_drag_hint" msgid="8730547755622776606">"Mantén presionado el widget para moverlo por la pantalla principal"</string>
<string name="add_to_home_screen" msgid="9168649446635919791">"Agregar a pantalla principal"</string>
<string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"Se agregó el widget de <xliff:g id="WIDGET_NAME">%1$s</xliff:g> a la pantalla principal"</string>
<string name="suggested_widgets_header_title" msgid="1844314680798145222">"Sugerencias"</string>
- <string name="productivity_widget_recommendation_category_label" msgid="1722113555721820766">"Tus esenciales diarios"</string>
- <string name="news_widget_recommendation_category_label" msgid="3908242346768119070">"Noticias para ti"</string>
+ <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Imprescindibles"</string>
+ <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"Noticias y revistas"</string>
<string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"Zona de descanso"</string>
- <string name="fitness_widget_recommendation_category_label" msgid="2657652999128882431">"Logra tus objetivos de fitness"</string>
- <string name="weather_widget_recommendation_category_label" msgid="6712678763480668598">"Mantente al tanto del clima"</string>
- <string name="others_widget_recommendation_category_label" msgid="897876078077284733">"Puede que también te guste"</string>
+ <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"Entretenimiento"</string>
+ <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"Social"</string>
+ <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"Salud y bienestar"</string>
+ <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"Clima"</string>
+ <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"Sugerencias para ti"</string>
<string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"Widgets de <xliff:g id="SELECTED_HEADER">%1$s</xliff:g> a la derecha, búsqueda y opciones a la izquierda"</string>
<string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# widget}other{# widgets}}"</string>
<string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# acceso directo}other{# accesos directos}}"</string>
@@ -61,6 +64,8 @@
<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>
+ <string name="widget_add_button_label" msgid="2761267068711937179">"Agregar"</string>
+ <string name="widget_add_button_content_description" msgid="1810530016360039643">"Agregar widget <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
<string name="widget_education_header" msgid="4874760613775913787">"Información útil a tu alcance"</string>
<string name="widget_education_content" msgid="1731667670753497052">"Para recibir información de apps sin abrirlas, puedes agregar widgets a la pantalla principal"</string>
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Presiona para cambiar la configuración del widget"</string>
diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml
index 176bfe3..1858290 100644
--- a/res/values-es/strings.xml
+++ b/res/values-es/strings.xml
@@ -38,16 +38,19 @@
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d de ancho por %2$d de alto"</string>
<string name="widget_preview_context_description" msgid="9045841361655787574">"Widget de <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
+ <string name="widget_preview_name_and_dims_content_description" msgid="8489038126122831595">"Widget <xliff:g id="WIDGET_NAME">%1$s</xliff:g>, %2$d de ancho por %3$d de alto"</string>
<string name="add_item_request_drag_hint" msgid="8730547755622776606">"Mantén pulsado el widget para moverlo por la pantalla de inicio"</string>
<string name="add_to_home_screen" msgid="9168649446635919791">"Añadir a pantalla de inicio"</string>
<string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"Widget <xliff:g id="WIDGET_NAME">%1$s</xliff:g> añadido a la pantalla de inicio"</string>
<string name="suggested_widgets_header_title" msgid="1844314680798145222">"Sugerencias"</string>
- <string name="productivity_widget_recommendation_category_label" msgid="1722113555721820766">"Lo esencial para el día a día"</string>
- <string name="news_widget_recommendation_category_label" msgid="3908242346768119070">"Noticias para ti"</string>
+ <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Imprescindibles"</string>
+ <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"Noticias y revistas"</string>
<string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"Tu zona de descanso"</string>
- <string name="fitness_widget_recommendation_category_label" msgid="2657652999128882431">"Logra tus objetivos de actividad física"</string>
- <string name="weather_widget_recommendation_category_label" msgid="6712678763480668598">"Infórmate sobre el tiempo"</string>
- <string name="others_widget_recommendation_category_label" msgid="897876078077284733">"También te puede interesar"</string>
+ <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"Entretenimiento"</string>
+ <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"Social"</string>
+ <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"Salud y actividad física"</string>
+ <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"El tiempo"</string>
+ <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"Sugerencias para ti"</string>
<string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"Widgets de <xliff:g id="SELECTED_HEADER">%1$s</xliff:g> a la derecha, búsqueda y opciones a la izquierda"</string>
<string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# widget}other{# widgets}}"</string>
<string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# acceso directo}other{# accesos directos}}"</string>
@@ -61,6 +64,8 @@
<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">"Toma de notas"</string>
+ <string name="widget_add_button_label" msgid="2761267068711937179">"Añadir"</string>
+ <string name="widget_add_button_content_description" msgid="1810530016360039643">"Añadir widget <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
<string name="widget_education_header" msgid="4874760613775913787">"Información útil al alcance de la mano"</string>
<string name="widget_education_content" msgid="1731667670753497052">"Para ver información sin abrir una aplicación, puedes añadir widgets a la pantalla de inicio"</string>
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Toca para cambiar los ajustes del widget"</string>
diff --git a/res/values-et/strings.xml b/res/values-et/strings.xml
index 830abf0..26110ae 100644
--- a/res/values-et/strings.xml
+++ b/res/values-et/strings.xml
@@ -38,16 +38,19 @@
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d lai ja %2$d kõrge"</string>
<string name="widget_preview_context_description" msgid="9045841361655787574">"Vidin <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
+ <string name="widget_preview_name_and_dims_content_description" msgid="8489038126122831595">"Vidin <xliff:g id="WIDGET_NAME">%1$s</xliff:g>, %2$d lai ja %3$d kõrge"</string>
<string name="add_item_request_drag_hint" msgid="8730547755622776606">"Vidina teisaldamiseks avakuval puudutage vidinat pikalt"</string>
<string name="add_to_home_screen" msgid="9168649446635919791">"Lisa avakuvale"</string>
<string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"Vidin <xliff:g id="WIDGET_NAME">%1$s</xliff:g> lisati avakuvale"</string>
<string name="suggested_widgets_header_title" msgid="1844314680798145222">"Soovitused"</string>
- <string name="productivity_widget_recommendation_category_label" msgid="1722113555721820766">"Kasulikud vidinad"</string>
- <string name="news_widget_recommendation_category_label" msgid="3908242346768119070">"Uudised teile"</string>
+ <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Alustalad"</string>
+ <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"Uudised ja ajakirjad"</string>
<string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"Teie lõõgastumiskoht"</string>
- <string name="fitness_widget_recommendation_category_label" msgid="2657652999128882431">"Saavutage oma treeningueesmärgid"</string>
- <string name="weather_widget_recommendation_category_label" msgid="6712678763480668598">"Olge ilmateatega kursis"</string>
- <string name="others_widget_recommendation_category_label" msgid="897876078077284733">"Teile võivad meeldida ka need"</string>
+ <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"Meelelahutus"</string>
+ <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"Suhtlus"</string>
+ <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"Tervis ja vormisolek"</string>
+ <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"Ilm"</string>
+ <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"Teile soovitatud"</string>
<string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"Teenuse <xliff:g id="SELECTED_HEADER">%1$s</xliff:g> vidinad paremal, otsing ja valikud vasakul"</string>
<string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# vidin}other{# vidinat}}"</string>
<string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# otsetee}other{# otseteed}}"</string>
@@ -61,6 +64,8 @@
<string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Töö"</string>
<string name="widget_category_conversations" msgid="8894438636213590446">"Vestlused"</string>
<string name="widget_category_note_taking" msgid="3469689394504266039">"Märkmete tegemine"</string>
+ <string name="widget_add_button_label" msgid="2761267068711937179">"Lisa"</string>
+ <string name="widget_add_button_content_description" msgid="1810530016360039643">"Lisa vidin <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
<string name="widget_education_header" msgid="4874760613775913787">"Kasulik teave on teie käeulatuses"</string>
<string name="widget_education_content" msgid="1731667670753497052">"Teabe saamiseks rakendusi avamata võite oma avakuvale lisada vidinaid"</string>
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Puudutage vidina seadete muutmiseks"</string>
diff --git a/res/values-eu/strings.xml b/res/values-eu/strings.xml
index f269c25..a722a58 100644
--- a/res/values-eu/strings.xml
+++ b/res/values-eu/strings.xml
@@ -38,16 +38,19 @@
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d zabal eta %2$d luze"</string>
<string name="widget_preview_context_description" msgid="9045841361655787574">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> widgeta"</string>
+ <string name="widget_preview_name_and_dims_content_description" msgid="8489038126122831595">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> widgeta, %2$d zabal eta %3$d altu"</string>
<string name="add_item_request_drag_hint" msgid="8730547755622776606">"Widgeta orri nagusian zehar mugitzeko, eduki ezazu sakatuta"</string>
<string name="add_to_home_screen" msgid="9168649446635919791">"Gehitu orri nagusian"</string>
<string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> widgeta orri nagusian gehitu da"</string>
<string name="suggested_widgets_header_title" msgid="1844314680798145222">"Iradokizunak"</string>
- <string name="productivity_widget_recommendation_category_label" msgid="1722113555721820766">"Eguneroko funtsezkoak"</string>
- <string name="news_widget_recommendation_category_label" msgid="3908242346768119070">"Zuretzako albisteak"</string>
+ <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Oinarrizkoak"</string>
+ <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"Albisteak eta aldizkariak"</string>
<string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"Lasaitzeko gunea"</string>
- <string name="fitness_widget_recommendation_category_label" msgid="2657652999128882431">"Erdietsi zure fitness-helburuak"</string>
- <string name="weather_widget_recommendation_category_label" msgid="6712678763480668598">"Hartu aurrea eguraldiari"</string>
- <string name="others_widget_recommendation_category_label" msgid="897876078077284733">"Gustatuko zaizkizulakoan"</string>
+ <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"Aisia"</string>
+ <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"Sare sozialak"</string>
+ <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"Osasuna eta ongizatea"</string>
+ <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"Eguraldia"</string>
+ <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"Zuri iradokiak"</string>
<string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"<xliff:g id="SELECTED_HEADER">%1$s</xliff:g> zerbitzuaren widgetak eskuinean, bilaketa eta aukerak ezkerrean"</string>
<string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# widget}other{# widget}}"</string>
<string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# lasterbide}other{# lasterbide}}"</string>
@@ -61,6 +64,8 @@
<string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Lanekoak"</string>
<string name="widget_category_conversations" msgid="8894438636213590446">"Elkarrizketak"</string>
<string name="widget_category_note_taking" msgid="3469689394504266039">"Oharrak idazteko"</string>
+ <string name="widget_add_button_label" msgid="2761267068711937179">"Gehitu"</string>
+ <string name="widget_add_button_content_description" msgid="1810530016360039643">"Gehitu <xliff:g id="WIDGET_NAME">%1$s</xliff:g> widgeta"</string>
<string name="widget_education_header" msgid="4874760613775913787">"Informazio erabilgarria beti eskura"</string>
<string name="widget_education_content" msgid="1731667670753497052">"Aplikaziorik ireki beharrik gabe informazioa zuzenean jasotzeko, gehitu widgetak orri nagusian"</string>
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Sakatu hau widgeten ezarpenak aldatzeko"</string>
diff --git a/res/values-fa/strings.xml b/res/values-fa/strings.xml
index feaf724..d69a590 100644
--- a/res/values-fa/strings.xml
+++ b/res/values-fa/strings.xml
@@ -38,16 +38,19 @@
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d عرض در %2$d طول"</string>
<string name="widget_preview_context_description" msgid="9045841361655787574">"ابزارک <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
+ <string name="widget_preview_name_and_dims_content_description" msgid="8489038126122831595">"ابزارک <xliff:g id="WIDGET_NAME">%1$s</xliff:g>، %2$d عرض در %3$d ارتفاع"</string>
<string name="add_item_request_drag_hint" msgid="8730547755622776606">"ابزارک را لمس کنید و نگه دارید تا بتوانید آن را در صفحه اصلی حرکت دهید"</string>
<string name="add_to_home_screen" msgid="9168649446635919791">"افزودن به صفحه اصلی"</string>
<string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"ابزارک <xliff:g id="WIDGET_NAME">%1$s</xliff:g> به صفحه اصلی اضافه شد"</string>
<string name="suggested_widgets_header_title" msgid="1844314680798145222">"پیشنهادها"</string>
- <string name="productivity_widget_recommendation_category_label" msgid="1722113555721820766">"ملزومات روزانه"</string>
- <string name="news_widget_recommendation_category_label" msgid="3908242346768119070">"اخبار برای شما"</string>
+ <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"ضروریات"</string>
+ <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"اخبار و مجله"</string>
<string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"منطقه آرامش شما"</string>
- <string name="fitness_widget_recommendation_category_label" msgid="2657652999128882431">"دستیابی به اهداف تناسب اندام"</string>
- <string name="weather_widget_recommendation_category_label" msgid="6712678763480668598">"آبوهوا را پیشبینی کنید"</string>
- <string name="others_widget_recommendation_category_label" msgid="897876078077284733">"شاید این را هم بپسندید"</string>
+ <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"سرگرمی"</string>
+ <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"اجتماعی"</string>
+ <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"سلامتی و تناسب اندام"</string>
+ <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"آبوهوا"</string>
+ <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"پیشنهادشده برای شما"</string>
<string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"ابزارکهای <xliff:g id="SELECTED_HEADER">%1$s</xliff:g> در سمت چپ، جستجو و گزینهها در سمت راست"</string>
<string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# ابزارک}one{# ابزارک}other{# ابزارک}}"</string>
<string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# میانبر}one{# میانبر}other{# میانبر}}"</string>
@@ -61,6 +64,8 @@
<string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"کار"</string>
<string name="widget_category_conversations" msgid="8894438636213590446">"مکالمهها"</string>
<string name="widget_category_note_taking" msgid="3469689394504266039">"یادداشتبرداری"</string>
+ <string name="widget_add_button_label" msgid="2761267068711937179">"افزودن"</string>
+ <string name="widget_add_button_content_description" msgid="1810530016360039643">"افزودن ابزارک <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
<string name="widget_education_header" msgid="4874760613775913787">"دسترسی آسان به اطلاعات سودمند"</string>
<string name="widget_education_content" msgid="1731667670753497052">"با افزودن ابزارکها به صفحه اصلی میتوانید اطلاعات را بدون باز کردن برنامهها دریافت کنید"</string>
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"برای تغییر تنظیمات ابزارک، ضربه بزنید"</string>
diff --git a/res/values-fi/strings.xml b/res/values-fi/strings.xml
index 3198a03..81790b9 100644
--- a/res/values-fi/strings.xml
+++ b/res/values-fi/strings.xml
@@ -38,16 +38,19 @@
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"Leveys: %1$d, korkeus: %2$d"</string>
<string name="widget_preview_context_description" msgid="9045841361655787574">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> widget"</string>
+ <string name="widget_preview_name_and_dims_content_description" msgid="8489038126122831595">"Widget: <xliff:g id="WIDGET_NAME">%1$s</xliff:g>, leveys %2$d ja korkeus %3$d"</string>
<string name="add_item_request_drag_hint" msgid="8730547755622776606">"Voit siirtää widgetiä aloitusnäytöllä koskettamalla sitä pitkään"</string>
<string name="add_to_home_screen" msgid="9168649446635919791">"Lisää aloitusnäytölle"</string>
<string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"Widget lisätty aloitusnäytölle: <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
<string name="suggested_widgets_header_title" msgid="1844314680798145222">"Ehdotukset"</string>
- <string name="productivity_widget_recommendation_category_label" msgid="1722113555721820766">"Tärkeät asiat päivään"</string>
- <string name="news_widget_recommendation_category_label" msgid="3908242346768119070">"Uutisia sinulle"</string>
+ <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Kaikki tarvittava"</string>
+ <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"Uutiset ja aikakauslehdet"</string>
<string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"Ota rennosti"</string>
- <string name="fitness_widget_recommendation_category_label" msgid="2657652999128882431">"Saavuta kuntoilutavoitteet"</string>
- <string name="weather_widget_recommendation_category_label" msgid="6712678763480668598">"Pysy ajan tasalla säästä"</string>
- <string name="others_widget_recommendation_category_label" msgid="897876078077284733">"Saatat pitää myös näistä"</string>
+ <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"Viihde"</string>
+ <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"Some"</string>
+ <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"Terveys ja kuntoilu"</string>
+ <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"Sää"</string>
+ <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"Sinulle ehdotetut"</string>
<string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"<xliff:g id="SELECTED_HEADER">%1$s</xliff:g> widgetit oikealla, haku ja vaihtoehdot vasemmalla"</string>
<string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# widget}other{# widgetiä}}"</string>
<string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# pikakuvake}other{# pikakuvaketta}}"</string>
@@ -61,6 +64,8 @@
<string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Työ"</string>
<string name="widget_category_conversations" msgid="8894438636213590446">"Keskustelut"</string>
<string name="widget_category_note_taking" msgid="3469689394504266039">"Muistiinpanojen tekeminen"</string>
+ <string name="widget_add_button_label" msgid="2761267068711937179">"Lisää"</string>
+ <string name="widget_add_button_content_description" msgid="1810530016360039643">"Lisää widget: <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
<string name="widget_education_header" msgid="4874760613775913787">"Hyödyllisiä tietoja käden ulottuvilla"</string>
<string name="widget_education_content" msgid="1731667670753497052">"Jos haluat nähdä tietoja avaamatta sovelluksia, voit lisätä aloitusnäytölle widgetejä"</string>
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Napauta, niin voit muuttaa widgetin asetuksia"</string>
diff --git a/res/values-fr-rCA/strings.xml b/res/values-fr-rCA/strings.xml
index 6ddeeef..d21fef4 100644
--- a/res/values-fr-rCA/strings.xml
+++ b/res/values-fr-rCA/strings.xml
@@ -38,16 +38,19 @@
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d de largeur sur %2$d de hauteur"</string>
<string name="widget_preview_context_description" msgid="9045841361655787574">"Widget <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
+ <string name="widget_preview_name_and_dims_content_description" msgid="8489038126122831595">"Widget <xliff:g id="WIDGET_NAME">%1$s</xliff:g>, %2$d de largeur sur %3$d de hauteur"</string>
<string name="add_item_request_drag_hint" msgid="8730547755622776606">"Maintenez le doigt sur le widget pour le déplacer sur l\'écran d\'accueil"</string>
<string name="add_to_home_screen" msgid="9168649446635919791">"Ajouter à l\'écran d\'accueil"</string>
<string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"Le widget <xliff:g id="WIDGET_NAME">%1$s</xliff:g> a été ajouté à l\'écran d\'accueil"</string>
<string name="suggested_widgets_header_title" msgid="1844314680798145222">"Suggestions"</string>
- <string name="productivity_widget_recommendation_category_label" msgid="1722113555721820766">"Vos indispensables au quotidien"</string>
- <string name="news_widget_recommendation_category_label" msgid="3908242346768119070">"Actualités personnalisées"</string>
+ <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Essentiels"</string>
+ <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"Actualités et magazines"</string>
<string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"Zone de divertissement"</string>
- <string name="fitness_widget_recommendation_category_label" msgid="2657652999128882431">"Objectifs de mise en forme"</string>
- <string name="weather_widget_recommendation_category_label" msgid="6712678763480668598">"À l\'affût de la météo"</string>
- <string name="others_widget_recommendation_category_label" msgid="897876078077284733">"Autres recommandations"</string>
+ <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"Divertissement"</string>
+ <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"Médias sociaux"</string>
+ <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"Santé et mise en forme"</string>
+ <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"Météo"</string>
+ <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"Suggestions personnalisées"</string>
<string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"Widgets <xliff:g id="SELECTED_HEADER">%1$s</xliff:g> à droite, recherche et options à gauche"</string>
<string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# widget}one{# widget}other{# widgets}}"</string>
<string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# raccourci}one{# raccourci}other{# raccourcis}}"</string>
@@ -61,6 +64,8 @@
<string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Professionnels"</string>
<string name="widget_category_conversations" msgid="8894438636213590446">"Conversations"</string>
<string name="widget_category_note_taking" msgid="3469689394504266039">"Prise de note"</string>
+ <string name="widget_add_button_label" msgid="2761267068711937179">"Ajouter"</string>
+ <string name="widget_add_button_content_description" msgid="1810530016360039643">"Ajoutez le widget <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
<string name="widget_education_header" msgid="4874760613775913787">"Renseignements utiles à portée de main"</string>
<string name="widget_education_content" msgid="1731667670753497052">"Pour obtenir des informations sans ouvrir d\'applications, vous pouvez ajouter des widgets à votre écran d\'accueil"</string>
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Touchez pour modifier les paramètres du widget"</string>
diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml
index fdeae1c..4244f23 100644
--- a/res/values-fr/strings.xml
+++ b/res/values-fr/strings.xml
@@ -38,16 +38,19 @@
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d x %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d de largeur et %2$d de hauteur"</string>
<string name="widget_preview_context_description" msgid="9045841361655787574">"Widget <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
+ <string name="widget_preview_name_and_dims_content_description" msgid="8489038126122831595">"Widget <xliff:g id="WIDGET_NAME">%1$s</xliff:g>, %2$d de large par %3$d de haut"</string>
<string name="add_item_request_drag_hint" msgid="8730547755622776606">"Appuyez de manière prolongée sur le widget pour le déplacer sur l\'écran d\'accueil"</string>
<string name="add_to_home_screen" msgid="9168649446635919791">"Ajouter à l\'écran d\'accueil"</string>
<string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"Widget <xliff:g id="WIDGET_NAME">%1$s</xliff:g> ajouté à l\'écran d\'accueil"</string>
<string name="suggested_widgets_header_title" msgid="1844314680798145222">"Suggestions"</string>
- <string name="productivity_widget_recommendation_category_label" msgid="1722113555721820766">"Vos indispensables du jour"</string>
- <string name="news_widget_recommendation_category_label" msgid="3908242346768119070">"Actualités personnalisées"</string>
+ <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Les bases"</string>
+ <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"Actualités et magazines"</string>
<string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"Votre espace détente"</string>
- <string name="fitness_widget_recommendation_category_label" msgid="2657652999128882431">"Atteignez vos objectifs forme"</string>
- <string name="weather_widget_recommendation_category_label" msgid="6712678763480668598">"Soyez au fait de la météo"</string>
- <string name="others_widget_recommendation_category_label" msgid="897876078077284733">"Découvrez également"</string>
+ <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"Divertissement"</string>
+ <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"Réseaux sociaux"</string>
+ <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"Santé et bien-être"</string>
+ <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"Météo"</string>
+ <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"Recommandations"</string>
<string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"Widgets <xliff:g id="SELECTED_HEADER">%1$s</xliff:g> à droite, recherche et options à gauche"</string>
<string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# widget}one{# widget}other{# widgets}}"</string>
<string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# raccourci}one{# raccourci}other{# raccourcis}}"</string>
@@ -61,6 +64,8 @@
<string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Professionnels"</string>
<string name="widget_category_conversations" msgid="8894438636213590446">"Conversations"</string>
<string name="widget_category_note_taking" msgid="3469689394504266039">"Prise de notes"</string>
+ <string name="widget_add_button_label" msgid="2761267068711937179">"Ajouter"</string>
+ <string name="widget_add_button_content_description" msgid="1810530016360039643">"Ajoutez un widget <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
<string name="widget_education_header" msgid="4874760613775913787">"Infos utiles à portée de main"</string>
<string name="widget_education_content" msgid="1731667670753497052">"Pour obtenir des infos sans ouvrir d\'applis, vous pouvez ajouter des widgets à votre écran d\'accueil"</string>
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Appuyez pour modifier les paramètres du widget"</string>
diff --git a/res/values-gl/strings.xml b/res/values-gl/strings.xml
index 9388948..1089ecf 100644
--- a/res/values-gl/strings.xml
+++ b/res/values-gl/strings.xml
@@ -38,16 +38,19 @@
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d de largo por %2$d de alto"</string>
<string name="widget_preview_context_description" msgid="9045841361655787574">"Widget <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
+ <string name="widget_preview_name_and_dims_content_description" msgid="8489038126122831595">"Widget <xliff:g id="WIDGET_NAME">%1$s</xliff:g>: %2$d de largo por %3$d de alto"</string>
<string name="add_item_request_drag_hint" msgid="8730547755622776606">"Mantén premido o widget para movelo pola pantalla de inicio"</string>
<string name="add_to_home_screen" msgid="9168649446635919791">"Engadir á pantalla de inicio"</string>
<string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"Engadiuse o widget <xliff:g id="WIDGET_NAME">%1$s</xliff:g> á pantalla de inicio"</string>
<string name="suggested_widgets_header_title" msgid="1844314680798145222">"Suxestións"</string>
- <string name="productivity_widget_recommendation_category_label" msgid="1722113555721820766">"Indispensables para o día a día"</string>
- <string name="news_widget_recommendation_category_label" msgid="3908242346768119070">"Novidades para ti"</string>
+ <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Esenciais"</string>
+ <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"Noticias e revistas"</string>
<string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"Reláxate"</string>
- <string name="fitness_widget_recommendation_category_label" msgid="2657652999128882431">"Acada os teus obxectivos para estar en forma"</string>
- <string name="weather_widget_recommendation_category_label" msgid="6712678763480668598">"Adiántate á meteoroloxía"</string>
- <string name="others_widget_recommendation_category_label" msgid="897876078077284733">"Tamén che pode interesar…"</string>
+ <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"Entretemento"</string>
+ <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"Redes sociais"</string>
+ <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"Saúde e forma física"</string>
+ <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"O tempo"</string>
+ <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"Suxestións personalizadas"</string>
<string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"Widgets de <xliff:g id="SELECTED_HEADER">%1$s</xliff:g> á dereita, busca e opcións á esquerda"</string>
<string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# widget}other{# widgets}}"</string>
<string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# atallo}other{# atallos}}"</string>
@@ -61,6 +64,8 @@
<string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Widgets do traballo"</string>
<string name="widget_category_conversations" msgid="8894438636213590446">"Conversas"</string>
<string name="widget_category_note_taking" msgid="3469689394504266039">"Toma de notas"</string>
+ <string name="widget_add_button_label" msgid="2761267068711937179">"Engadir"</string>
+ <string name="widget_add_button_content_description" msgid="1810530016360039643">"Engadir o widget <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
<string name="widget_education_header" msgid="4874760613775913787">"Información útil ao teu alcance"</string>
<string name="widget_education_content" msgid="1731667670753497052">"Se queres obter información sen abrir as aplicacións, podes engadir widgets á pantalla de inicio"</string>
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Toca para cambiar a configuración do widget"</string>
diff --git a/res/values-gu/strings.xml b/res/values-gu/strings.xml
index ec6d994..a30af7e 100644
--- a/res/values-gu/strings.xml
+++ b/res/values-gu/strings.xml
@@ -38,16 +38,19 @@
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d પહોળાઈ X %2$d ઊંચાઈ"</string>
<string name="widget_preview_context_description" msgid="9045841361655787574">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> વિજેટ"</string>
+ <string name="widget_preview_name_and_dims_content_description" msgid="8489038126122831595">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> વિજેટ, %2$d પહોળું x %3$d ઊંચું"</string>
<string name="add_item_request_drag_hint" msgid="8730547755622776606">"વિજેટને હોમ સ્ક્રીનની આજુબાજુ ખસેડવા માટે, તેને ટચ કરીને થોડીવાર દબાવી રાખો"</string>
<string name="add_to_home_screen" msgid="9168649446635919791">"હોમ સ્ક્રીનમાં ઉમેરો"</string>
<string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"હોમ સ્ક્રીન પર <xliff:g id="WIDGET_NAME">%1$s</xliff:g> વિજેટ ઉમેર્યુ"</string>
<string name="suggested_widgets_header_title" msgid="1844314680798145222">"સૂચનો"</string>
- <string name="productivity_widget_recommendation_category_label" msgid="1722113555721820766">"તમારી દૈનિક આવશ્યકતાઓ"</string>
- <string name="news_widget_recommendation_category_label" msgid="3908242346768119070">"તમારા માટે સમાચાર"</string>
+ <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"આવશ્યક"</string>
+ <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"ન્યૂઝ અને સામાયિકો"</string>
<string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"તમારો આરામદાયક ઝોન"</string>
- <string name="fitness_widget_recommendation_category_label" msgid="2657652999128882431">"તમારા ફિટનેસ લક્ષ્યો પૂરા કરો"</string>
- <string name="weather_widget_recommendation_category_label" msgid="6712678763480668598">"હવામાન વિશે અપ ટૂ ડેટ રહો"</string>
- <string name="others_widget_recommendation_category_label" msgid="897876078077284733">"કદાચ તમને આ પણ પસંદ હોય"</string>
+ <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"મનોરંજન"</string>
+ <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"સામાજિક"</string>
+ <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"આરોગ્ય અને ફિટનેસ"</string>
+ <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"હવામાન"</string>
+ <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"તમારા માટે સૂચવેલી સેવાઓ"</string>
<string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"<xliff:g id="SELECTED_HEADER">%1$s</xliff:g>ની વિજેટ જમણે, શોધ અને વિકલ્પો ડાબે"</string>
<string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# વિજેટ}one{# વિજેટ}other{# વિજેટ}}"</string>
<string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# શૉર્ટકટ}one{# શૉર્ટકટ}other{# શૉર્ટકટ}}"</string>
@@ -61,6 +64,8 @@
<string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"ઑફિસ"</string>
<string name="widget_category_conversations" msgid="8894438636213590446">"વાતચીતો"</string>
<string name="widget_category_note_taking" msgid="3469689394504266039">"નોંધ લેવી"</string>
+ <string name="widget_add_button_label" msgid="2761267068711937179">"ઉમેરો"</string>
+ <string name="widget_add_button_content_description" msgid="1810530016360039643">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> વિજેટ ઉમેરો"</string>
<string name="widget_education_header" msgid="4874760613775913787">"ઉપયોગી માહિતી તમારી આંગળીના ટેરવે"</string>
<string name="widget_education_content" msgid="1731667670753497052">"ઍપને ખોલ્યા વિના માહિતી મેળવવા માટે, તમે તમારી હોમ સ્ક્રીનમાં વિજેટ ઉમેરી શકો છો"</string>
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"વિજેટના સેટિંગ બદલવા માટે ટૅપ કરો"</string>
diff --git a/res/values-hi/strings.xml b/res/values-hi/strings.xml
index f043149..f65584b 100644
--- a/res/values-hi/strings.xml
+++ b/res/values-hi/strings.xml
@@ -38,16 +38,19 @@
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d चौड़ाई गुणा %2$d ऊंचाई"</string>
<string name="widget_preview_context_description" msgid="9045841361655787574">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> विजेट"</string>
+ <string name="widget_preview_name_and_dims_content_description" msgid="8489038126122831595">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> विजेट. यह %2$d चौड़ा और %3$d लंबा है"</string>
<string name="add_item_request_drag_hint" msgid="8730547755622776606">"विजेट को होम स्क्रीन पर इधर-उधर ले जाने के लिए, उसे दबाकर रखें"</string>
<string name="add_to_home_screen" msgid="9168649446635919791">"होम स्क्रीन पर जोड़ें"</string>
<string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> विजेट को होम स्क्रीन पर जोड़ा गया"</string>
<string name="suggested_widgets_header_title" msgid="1844314680798145222">"सुझाव"</string>
- <string name="productivity_widget_recommendation_category_label" msgid="1722113555721820766">"रोज़ाना इस्तेमाल होने वाले ज़रूरी ऐप्लिकेशन"</string>
- <string name="news_widget_recommendation_category_label" msgid="3908242346768119070">"आपके लिए खबरें"</string>
+ <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"ज़रूरी ऐप्लिकेशन"</string>
+ <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"खबरों और पत्रिकाओं वाले ऐप्लिकेशन"</string>
<string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"आपके मनोरंजन के लिए"</string>
- <string name="fitness_widget_recommendation_category_label" msgid="2657652999128882431">"फ़िटनेस के लक्ष्य हासिल करें"</string>
- <string name="weather_widget_recommendation_category_label" msgid="6712678763480668598">"मौसम की अप-टू-डेट जानकारी पाएं"</string>
- <string name="others_widget_recommendation_category_label" msgid="897876078077284733">"शायद आपको ये भी पसंद आएं"</string>
+ <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"मनोरंजन वाले ऐप्लिकेशन"</string>
+ <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"सोशल"</string>
+ <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"हेल्थ और फ़िटनेस वाले ऐप्लिकेशन"</string>
+ <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"मौसम"</string>
+ <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"आपके लिए सुझाए गए ऐप्लिकेशन"</string>
<string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"<xliff:g id="SELECTED_HEADER">%1$s</xliff:g> के विजेट दाईं ओर, खोज का विजेट और अन्य विकल्प बाईं ओर"</string>
<string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# विजेट}one{# विजेट}other{# विजेट}}"</string>
<string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# शॉर्टकट}one{# शॉर्टकट}other{# शॉर्टकट}}"</string>
@@ -61,6 +64,8 @@
<string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"वर्क विजेट"</string>
<string name="widget_category_conversations" msgid="8894438636213590446">"बातचीत"</string>
<string name="widget_category_note_taking" msgid="3469689394504266039">"नोट बनाने से जुड़े विजेट"</string>
+ <string name="widget_add_button_label" msgid="2761267068711937179">"जोड़ें"</string>
+ <string name="widget_add_button_content_description" msgid="1810530016360039643">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> विजेट जोड़ें"</string>
<string name="widget_education_header" msgid="4874760613775913787">"काम की जानकारी आसानी से पाएं"</string>
<string name="widget_education_content" msgid="1731667670753497052">"ऐप्लिकेशन को खोले बिना उनकी जानकारी पाने के लिए, होम स्क्रीन पर विजेट जोड़े जा सकते हैं"</string>
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"विजेट की सेटिंग में बदलाव करने के लिए टैप करें"</string>
diff --git a/res/values-hr/strings.xml b/res/values-hr/strings.xml
index f40252b..5ae78a3 100644
--- a/res/values-hr/strings.xml
+++ b/res/values-hr/strings.xml
@@ -38,16 +38,19 @@
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d širine i %2$d visine"</string>
<string name="widget_preview_context_description" msgid="9045841361655787574">"Widget <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
+ <string name="widget_preview_name_and_dims_content_description" msgid="8489038126122831595">"Widget <xliff:g id="WIDGET_NAME">%1$s</xliff:g>, širine %2$d, visine %3$d"</string>
<string name="add_item_request_drag_hint" msgid="8730547755622776606">"Dodirnite i zadržite widget da biste ga pomicali po početnom zaslonu"</string>
<string name="add_to_home_screen" msgid="9168649446635919791">"Dodaj na početni zaslon"</string>
<string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"Widget <xliff:g id="WIDGET_NAME">%1$s</xliff:g> dodan je na početni zaslon"</string>
<string name="suggested_widgets_header_title" msgid="1844314680798145222">"Prijedlozi"</string>
- <string name="productivity_widget_recommendation_category_label" msgid="1722113555721820766">"Vaše dnevne potrepštine"</string>
- <string name="news_widget_recommendation_category_label" msgid="3908242346768119070">"Vijesti za vas"</string>
+ <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Osnovno"</string>
+ <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"Vijesti i časopisi"</string>
<string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"Vaša zona za opuštanje"</string>
- <string name="fitness_widget_recommendation_category_label" msgid="2657652999128882431">"Postignite svoje ciljeve u fitnesu"</string>
- <string name="weather_widget_recommendation_category_label" msgid="6712678763480668598">"Budite korak ispred vremenskih prilika"</string>
- <string name="others_widget_recommendation_category_label" msgid="897876078077284733">"Možda će vam se svidjeti i ovo"</string>
+ <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"Zabava"</string>
+ <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"Društvene mreže"</string>
+ <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"Zdravlje i fitness"</string>
+ <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"Vrijeme"</string>
+ <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"Prijedlozi za vas"</string>
<string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"<xliff:g id="SELECTED_HEADER">%1$s</xliff:g> –widgeti zdesna, pretraživanje i opcije slijeva"</string>
<string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# widget}one{# widget}few{# widgeta}other{# widgeta}}"</string>
<string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# prečac}one{# prečac}few{# prečaca}other{# prečaca}}"</string>
@@ -61,6 +64,8 @@
<string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Posao"</string>
<string name="widget_category_conversations" msgid="8894438636213590446">"Razgovori"</string>
<string name="widget_category_note_taking" msgid="3469689394504266039">"Pisanje bilježaka"</string>
+ <string name="widget_add_button_label" msgid="2761267068711937179">"Dodaj"</string>
+ <string name="widget_add_button_content_description" msgid="1810530016360039643">"Dodaj widget <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
<string name="widget_education_header" msgid="4874760613775913787">"Korisne informacije nadohvat ruke"</string>
<string name="widget_education_content" msgid="1731667670753497052">"Da biste dobili informacije bez otvaranja aplikacija, možete dodati widgete na početni zaslon"</string>
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Dodirnite da biste promijenili postavke widgeta"</string>
diff --git a/res/values-hu/strings.xml b/res/values-hu/strings.xml
index 6fc5f28..0a05ce4 100644
--- a/res/values-hu/strings.xml
+++ b/res/values-hu/strings.xml
@@ -38,16 +38,19 @@
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d széles és %2$d magas"</string>
<string name="widget_preview_context_description" msgid="9045841361655787574">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> modul"</string>
+ <string name="widget_preview_name_and_dims_content_description" msgid="8489038126122831595">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> modul – %2$d széles, %3$d magas"</string>
<string name="add_item_request_drag_hint" msgid="8730547755622776606">"Tartsa lenyomva a modult a kezdőképernyőn való mozgatáshoz"</string>
<string name="add_to_home_screen" msgid="9168649446635919791">"Hozzáadás a kezdőképernyőhöz"</string>
<string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> modul hozzáadva a kezdőképernyőhöz"</string>
<string name="suggested_widgets_header_title" msgid="1844314680798145222">"Javaslatok"</string>
- <string name="productivity_widget_recommendation_category_label" msgid="1722113555721820766">"Legfontosabb napi dolgok"</string>
- <string name="news_widget_recommendation_category_label" msgid="3908242346768119070">"Hírek Önnek"</string>
+ <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Kihagyhatatlanok"</string>
+ <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"Hírlapok és folyóiratok"</string>
<string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"Az Ön relaxáló zónája"</string>
- <string name="fitness_widget_recommendation_category_label" msgid="2657652999128882431">"Elérheti kitűzött erőnléti céljait"</string>
- <string name="weather_widget_recommendation_category_label" msgid="6712678763480668598">"Mindig friss időjárás-információk"</string>
- <string name="others_widget_recommendation_category_label" msgid="897876078077284733">"Lehet, hogy ez is tetszeni fog"</string>
+ <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"Szórakozás"</string>
+ <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"Közösségi"</string>
+ <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"Egészség és fitnesz"</string>
+ <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"Időjárás"</string>
+ <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"Neked javasolt"</string>
<string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"A <xliff:g id="SELECTED_HEADER">%1$s</xliff:g>-modulok a jobb, a kereső és a beállítások pedig a bal oldalon találhatók"</string>
<string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# modul}other{# modul}}"</string>
<string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# gyorsparancs}other{# gyorsparancs}}"</string>
@@ -61,6 +64,8 @@
<string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Munka"</string>
<string name="widget_category_conversations" msgid="8894438636213590446">"Beszélgetések"</string>
<string name="widget_category_note_taking" msgid="3469689394504266039">"Jegyzetelés"</string>
+ <string name="widget_add_button_label" msgid="2761267068711937179">"Hozzáadás"</string>
+ <string name="widget_add_button_content_description" msgid="1810530016360039643">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> modul hozzáadása"</string>
<string name="widget_education_header" msgid="4874760613775913787">"Hasznos információk egy koppintásnyira"</string>
<string name="widget_education_content" msgid="1731667670753497052">"Ha az alkalmazások megnyitása nélkül szeretne információhoz jutni, felvehet modulokat a kezdőképernyőre"</string>
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Ide koppintva módosíthatja a modulbeállításokat"</string>
diff --git a/res/values-hy/strings.xml b/res/values-hy/strings.xml
index 83a4559..7bb79e5 100644
--- a/res/values-hy/strings.xml
+++ b/res/values-hy/strings.xml
@@ -38,16 +38,19 @@
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"Լայնությունը՝ %1$d, բարձրությունը՝ %2$d"</string>
<string name="widget_preview_context_description" msgid="9045841361655787574">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> վիջեթ"</string>
+ <string name="widget_preview_name_and_dims_content_description" msgid="8489038126122831595">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> վիջեթ, լայնությունը՝ %2$d, բարձրությունը՝ %3$d"</string>
<string name="add_item_request_drag_hint" msgid="8730547755622776606">"Հպեք վիջեթին և պահեք՝ հիմնական էկրան տեղափոխելու համար"</string>
<string name="add_to_home_screen" msgid="9168649446635919791">"Ավելացնել հիմնական էկրանին"</string>
<string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> վիջեթն ավելացվել է հիմնական էկրանին"</string>
<string name="suggested_widgets_header_title" msgid="1844314680798145222">"Առաջարկներ"</string>
- <string name="productivity_widget_recommendation_category_label" msgid="1722113555721820766">"Ամենաանհրաժեշտը յուրաքանչյուր օրվա համար"</string>
- <string name="news_widget_recommendation_category_label" msgid="3908242346768119070">"Նորություններ ձեզ համար"</string>
+ <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Հիմնական"</string>
+ <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"Նորություններ և ամսագրեր"</string>
<string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"Ձեր հանգստի գոտին"</string>
- <string name="fitness_widget_recommendation_category_label" msgid="2657652999128882431">"Հասեք ձեր ֆիթնես նպատակներին"</string>
- <string name="weather_widget_recommendation_category_label" msgid="6712678763480668598">"Եղեք տեղեկացված եղանակի մասին"</string>
- <string name="others_widget_recommendation_category_label" msgid="897876078077284733">"Ձեզ կարող է դուր գալ"</string>
+ <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"Զվարճանք"</string>
+ <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"Սոցցանցեր"</string>
+ <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"Առողջություն և ֆիթնես"</string>
+ <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"Եղանակ"</string>
+ <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"Առաջարկում ենք"</string>
<string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"«<xliff:g id="SELECTED_HEADER">%1$s</xliff:g>» հավելվածի վիջեթներն աջ կողմում են, իսկ որոնման դաշտը և կարգավորումները՝ ձախ կողմում"</string>
<string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# վիջեթ}one{# վիջեթ}other{# վիջեթ}}"</string>
<string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# դյուրանցում}one{# դյուրանցում}other{# դյուրանցում}}"</string>
@@ -61,6 +64,8 @@
<string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Աշխատանքային"</string>
<string name="widget_category_conversations" msgid="8894438636213590446">"Զրույցներ"</string>
<string name="widget_category_note_taking" msgid="3469689394504266039">"Նշումների ստեղծում"</string>
+ <string name="widget_add_button_label" msgid="2761267068711937179">"Ավելացնել"</string>
+ <string name="widget_add_button_content_description" msgid="1810530016360039643">"Ավելացնել <xliff:g id="WIDGET_NAME">%1$s</xliff:g> վիջեթը"</string>
<string name="widget_education_header" msgid="4874760613775913787">"Բոլոր կարևոր տեղեկությունները՝ ձեռքի տակ"</string>
<string name="widget_education_content" msgid="1731667670753497052">"Ավելացրեք վիջեթներ ձեր հիմնական էկրանին, որպեսզի տեղեկություններ ստանաք՝ առանց հավելվածները բացելու։"</string>
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Հպեք՝ վիջեթի կարգավորումները փոփոխելու համար"</string>
diff --git a/res/values-in/strings.xml b/res/values-in/strings.xml
index edf62a4..246367d 100644
--- a/res/values-in/strings.xml
+++ b/res/values-in/strings.xml
@@ -38,16 +38,19 @@
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"lebar %1$d x tinggi %2$d"</string>
<string name="widget_preview_context_description" msgid="9045841361655787574">"Widget <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
+ <string name="widget_preview_name_and_dims_content_description" msgid="8489038126122831595">"Widget <xliff:g id="WIDGET_NAME">%1$s</xliff:g>, lebar %2$d dan tinggi %3$d"</string>
<string name="add_item_request_drag_hint" msgid="8730547755622776606">"Sentuh lama widget untuk memindah-mindahkannya di layar utama"</string>
<string name="add_to_home_screen" msgid="9168649446635919791">"Tambahkan ke layar utama"</string>
<string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"Widget <xliff:g id="WIDGET_NAME">%1$s</xliff:g> ditambahkan ke layar utama"</string>
<string name="suggested_widgets_header_title" msgid="1844314680798145222">"Saran"</string>
- <string name="productivity_widget_recommendation_category_label" msgid="1722113555721820766">"Kebutuhan Harian Anda"</string>
- <string name="news_widget_recommendation_category_label" msgid="3908242346768119070">"Berita untuk Anda"</string>
+ <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Wajib Coba"</string>
+ <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"Berita & majalah"</string>
<string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"Zona Nyaman Anda"</string>
- <string name="fitness_widget_recommendation_category_label" msgid="2657652999128882431">"Capai Target Kebugaran Anda"</string>
- <string name="weather_widget_recommendation_category_label" msgid="6712678763480668598">"Tetap Waspada Menghadapi Cuaca"</string>
- <string name="others_widget_recommendation_category_label" msgid="897876078077284733">"Anda Mungkin Juga Suka"</string>
+ <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"Hiburan"</string>
+ <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"Sosial"</string>
+ <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"Kesehatan & kebugaran"</string>
+ <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"Cuaca"</string>
+ <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"Saran untuk Anda"</string>
<string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"Widget <xliff:g id="SELECTED_HEADER">%1$s</xliff:g> di bagian kanan, penelusuran dan opsi di bagian kiri"</string>
<string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# widget}other{# widget}}"</string>
<string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# pintasan}other{# pintasan}}"</string>
@@ -61,6 +64,8 @@
<string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Kerja"</string>
<string name="widget_category_conversations" msgid="8894438636213590446">"Percakapan"</string>
<string name="widget_category_note_taking" msgid="3469689394504266039">"Pembuatan catatan"</string>
+ <string name="widget_add_button_label" msgid="2761267068711937179">"Tambahkan"</string>
+ <string name="widget_add_button_content_description" msgid="1810530016360039643">"Tambahkan widget <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
<string name="widget_education_header" msgid="4874760613775913787">"Info bermanfaat mudah dilihat"</string>
<string name="widget_education_content" msgid="1731667670753497052">"Untuk mendapatkan info tanpa membuka aplikasi, Anda dapat menambahkan widget ke layar utama"</string>
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Ketuk untuk mengubah setelan widget"</string>
diff --git a/res/values-is/strings.xml b/res/values-is/strings.xml
index b75f61f..23899ad 100644
--- a/res/values-is/strings.xml
+++ b/res/values-is/strings.xml
@@ -38,16 +38,19 @@
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d á breidd og %2$d á hæð"</string>
<string name="widget_preview_context_description" msgid="9045841361655787574">"Græjan <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
+ <string name="widget_preview_name_and_dims_content_description" msgid="8489038126122831595">"Græjan <xliff:g id="WIDGET_NAME">%1$s</xliff:g>, %2$d breið og %3$d há"</string>
<string name="add_item_request_drag_hint" msgid="8730547755622776606">"Haltu fingri á græjunni til að hreyfa hana um heimaskjáinn"</string>
<string name="add_to_home_screen" msgid="9168649446635919791">"Bæta á heimaskjá"</string>
<string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> græju bætt við heimaskjá"</string>
<string name="suggested_widgets_header_title" msgid="1844314680798145222">"Tillögur"</string>
- <string name="productivity_widget_recommendation_category_label" msgid="1722113555721820766">"Daglegar nauðsynjar"</string>
- <string name="news_widget_recommendation_category_label" msgid="3908242346768119070">"Fréttir fyrir þig"</string>
+ <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Það nauðsynlegasta"</string>
+ <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"Fréttir og tímarit"</string>
<string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"Slakaðu á"</string>
- <string name="fitness_widget_recommendation_category_label" msgid="2657652999128882431">"Náðu hreyfingarmarkmiðunum þínum"</string>
- <string name="weather_widget_recommendation_category_label" msgid="6712678763480668598">"Vertu einu skrefi á undan veðrinu"</string>
- <string name="others_widget_recommendation_category_label" msgid="897876078077284733">"Þú gætir einnig haft áhuga á"</string>
+ <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"Afþreying"</string>
+ <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"Samfélag"</string>
+ <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"Heilsa og líkamsrækt"</string>
+ <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"Veður"</string>
+ <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"Tillögur fyrir þig"</string>
<string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"<xliff:g id="SELECTED_HEADER">%1$s</xliff:g>-græjur til hægri, leit og valkostir til vinstri"</string>
<string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# græja}one{# græja}other{# græjur}}"</string>
<string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# flýtileið}one{# flýtileið}other{# flýtileiðir}}"</string>
@@ -61,6 +64,8 @@
<string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Vinna"</string>
<string name="widget_category_conversations" msgid="8894438636213590446">"Samtöl"</string>
<string name="widget_category_note_taking" msgid="3469689394504266039">"Glósugerð"</string>
+ <string name="widget_add_button_label" msgid="2761267068711937179">"Bæta við"</string>
+ <string name="widget_add_button_content_description" msgid="1810530016360039643">"Bæta græjunni <xliff:g id="WIDGET_NAME">%1$s</xliff:g> við"</string>
<string name="widget_education_header" msgid="4874760613775913787">"Gagnlegar upplýsingar innan seilingar"</string>
<string name="widget_education_content" msgid="1731667670753497052">"Þú getur bætt við græjum á heimaskjáinn til að fá upplýsingar án þess að opna forrit"</string>
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Ýttu til að breyta græjustillingum"</string>
diff --git a/res/values-it/strings.xml b/res/values-it/strings.xml
index 0c76ba9..e45f9dd 100644
--- a/res/values-it/strings.xml
+++ b/res/values-it/strings.xml
@@ -38,16 +38,19 @@
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d di larghezza per %2$d di altezza"</string>
<string name="widget_preview_context_description" msgid="9045841361655787574">"Widget <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
+ <string name="widget_preview_name_and_dims_content_description" msgid="8489038126122831595">"Widget <xliff:g id="WIDGET_NAME">%1$s</xliff:g>, %2$d di larghezza per %3$d di altezza"</string>
<string name="add_item_request_drag_hint" msgid="8730547755622776606">"Tocca e tieni premuto il widget per spostarlo nella schermata Home"</string>
<string name="add_to_home_screen" msgid="9168649446635919791">"Aggiungi alla schermata Home"</string>
<string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"Widget <xliff:g id="WIDGET_NAME">%1$s</xliff:g> aggiunto alla schermata Home"</string>
<string name="suggested_widgets_header_title" msgid="1844314680798145222">"Suggerimenti"</string>
- <string name="productivity_widget_recommendation_category_label" msgid="1722113555721820766">"L\'essenziale ogni giorno"</string>
- <string name="news_widget_recommendation_category_label" msgid="3908242346768119070">"Notizie per te"</string>
+ <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Essenziali"</string>
+ <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"Notizie e riviste"</string>
<string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"Il tuo angolo di tranquillità"</string>
- <string name="fitness_widget_recommendation_category_label" msgid="2657652999128882431">"Raggiungi i tuoi obiettivi di fitness"</string>
- <string name="weather_widget_recommendation_category_label" msgid="6712678763480668598">"Non perderti le previsioni meteo"</string>
- <string name="others_widget_recommendation_category_label" msgid="897876078077284733">"Ti potrebbero anche piacere"</string>
+ <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"Intrattenimento"</string>
+ <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"Social"</string>
+ <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"Salute e fitness"</string>
+ <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"Meteo"</string>
+ <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"Consigliati per te"</string>
<string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"Widget di <xliff:g id="SELECTED_HEADER">%1$s</xliff:g> a destra, ricerca e opzioni a sinistra"</string>
<string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# widget}other{# widget}}"</string>
<string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# scorciatoia}other{# scorciatoie}}"</string>
@@ -61,6 +64,8 @@
<string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Lavoro"</string>
<string name="widget_category_conversations" msgid="8894438636213590446">"Conversazioni"</string>
<string name="widget_category_note_taking" msgid="3469689394504266039">"Aggiunta di note"</string>
+ <string name="widget_add_button_label" msgid="2761267068711937179">"Aggiungi"</string>
+ <string name="widget_add_button_content_description" msgid="1810530016360039643">"Aggiungi widget <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
<string name="widget_education_header" msgid="4874760613775913787">"Informazioni utili a portata di mano"</string>
<string name="widget_education_content" msgid="1731667670753497052">"Per ricevere informazioni senza aprire le app, puoi aggiungere dei widget alla schermata Home"</string>
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Tocca per modificare le impostazioni del widget"</string>
diff --git a/res/values-iw/strings.xml b/res/values-iw/strings.xml
index 3132e2d..41c08e2 100644
--- a/res/values-iw/strings.xml
+++ b/res/values-iw/strings.xml
@@ -38,16 +38,19 @@
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"רוחב %1$d על גובה %2$d"</string>
<string name="widget_preview_context_description" msgid="9045841361655787574">"ווידג\'ט <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
+ <string name="widget_preview_name_and_dims_content_description" msgid="8489038126122831595">"ווידג\'ט <xliff:g id="WIDGET_NAME">%1$s</xliff:g>, רוחב: %2$d, אורך: %3$d"</string>
<string name="add_item_request_drag_hint" msgid="8730547755622776606">"לוחצים לחיצה ארוכה על הווידג\'ט כדי להזיז אותו במסך הבית"</string>
<string name="add_to_home_screen" msgid="9168649446635919791">"הוספה למסך הבית"</string>
<string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"הווידג\'ט <xliff:g id="WIDGET_NAME">%1$s</xliff:g> נוסף למסך הבית"</string>
<string name="suggested_widgets_header_title" msgid="1844314680798145222">"הצעות"</string>
- <string name="productivity_widget_recommendation_category_label" msgid="1722113555721820766">"הפריטים היומיומיים שלך"</string>
- <string name="news_widget_recommendation_category_label" msgid="3908242346768119070">"חדשות בשבילך"</string>
+ <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"האפליקציות שחייבים להכיר"</string>
+ <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"חדשות וכתבי עת"</string>
<string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"המקום שלך לרגיעה"</string>
- <string name="fitness_widget_recommendation_category_label" msgid="2657652999128882431">"השגת יעדי הכושר שלך"</string>
- <string name="weather_widget_recommendation_category_label" msgid="6712678763480668598">"התעדכנות במזג האוויר"</string>
- <string name="others_widget_recommendation_category_label" msgid="897876078077284733">"אולי יעניין אותך גם"</string>
+ <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"בידור"</string>
+ <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"רשתות חברתיות"</string>
+ <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"בריאות וכושר"</string>
+ <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"מזג אוויר"</string>
+ <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"הצעות בשבילך"</string>
<string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"<xliff:g id="SELECTED_HEADER">%1$s</xliff:g> ווידג\'טים מימין, חיפוש ואפשרויות משמאל"</string>
<string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{ווידג\'ט אחד}one{# ווידג\'טים}two{# ווידג\'טים}other{# ווידג\'טים}}"</string>
<string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{קיצור דרך אחד}one{# קיצורי דרך}two{# קיצורי דרך}other{# קיצורי דרך}}"</string>
@@ -61,6 +64,8 @@
<string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"עבודה"</string>
<string name="widget_category_conversations" msgid="8894438636213590446">"שיחות"</string>
<string name="widget_category_note_taking" msgid="3469689394504266039">"כתיבת הערות"</string>
+ <string name="widget_add_button_label" msgid="2761267068711937179">"הוספה"</string>
+ <string name="widget_add_button_content_description" msgid="1810530016360039643">"הוספת הווידג\'ט <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
<string name="widget_education_header" msgid="4874760613775913787">"קבלת מידע שימושי בהקשה"</string>
<string name="widget_education_content" msgid="1731667670753497052">"רוצה לקבל מידע בלי לפתוח אפליקציות? אפשר להוסיף ווידג\'טים למסך הבית"</string>
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"אפשר לשנות את הגדרות הווידג\'ט בהקשה"</string>
diff --git a/res/values-ja/strings.xml b/res/values-ja/strings.xml
index 7f6846c..d467f2c 100644
--- a/res/values-ja/strings.xml
+++ b/res/values-ja/strings.xml
@@ -38,16 +38,19 @@
<string name="widget_dims_format" msgid="2370757736025621599">"%1$dx%2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"幅 %1$d、高さ %2$d"</string>
<string name="widget_preview_context_description" msgid="9045841361655787574">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> ウィジェット"</string>
+ <string name="widget_preview_name_and_dims_content_description" msgid="8489038126122831595">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g>ウィジェット、幅%2$d、高さ%3$d"</string>
<string name="add_item_request_drag_hint" msgid="8730547755622776606">"ウィジェットを押し続けると、ホーム画面上に移動できます"</string>
<string name="add_to_home_screen" msgid="9168649446635919791">"ホーム画面に追加"</string>
<string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"「<xliff:g id="WIDGET_NAME">%1$s</xliff:g>」ウィジェットをホーム画面に追加しました"</string>
<string name="suggested_widgets_header_title" msgid="1844314680798145222">"候補"</string>
- <string name="productivity_widget_recommendation_category_label" msgid="1722113555721820766">"生活必需品"</string>
- <string name="news_widget_recommendation_category_label" msgid="3908242346768119070">"あなたへのおすすめニュース"</string>
+ <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"基本"</string>
+ <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"ニュース&雑誌"</string>
<string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"休憩エリア"</string>
- <string name="fitness_widget_recommendation_category_label" msgid="2657652999128882431">"フィットネスの目標を達成"</string>
- <string name="weather_widget_recommendation_category_label" msgid="6712678763480668598">"天気予報"</string>
- <string name="others_widget_recommendation_category_label" msgid="897876078077284733">"あなたへのおすすめ"</string>
+ <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"エンタメ"</string>
+ <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"ソーシャル"</string>
+ <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"健康&フィットネス"</string>
+ <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"天気"</string>
+ <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"おすすめ"</string>
<string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"<xliff:g id="SELECTED_HEADER">%1$s</xliff:g> のウィジェットは右側に、検索とオプションは左側にあります"</string>
<string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# 件のウィジェット}other{# 件のウィジェット}}"</string>
<string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# 件のショートカット}other{# 件のショートカット}}"</string>
@@ -61,6 +64,8 @@
<string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"仕事用"</string>
<string name="widget_category_conversations" msgid="8894438636213590446">"会話"</string>
<string name="widget_category_note_taking" msgid="3469689394504266039">"メモ"</string>
+ <string name="widget_add_button_label" msgid="2761267068711937179">"追加"</string>
+ <string name="widget_add_button_content_description" msgid="1810530016360039643">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g>ウィジェットを追加"</string>
<string name="widget_education_header" msgid="4874760613775913787">"ウィジェットで情報を得る"</string>
<string name="widget_education_content" msgid="1731667670753497052">"ホーム画面にウィジェットを追加すると、アプリを開かずに情報を入手できます"</string>
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"タップしてウィジェットの設定を変更する"</string>
diff --git a/res/values-ka/strings.xml b/res/values-ka/strings.xml
index ae3b1e6..e473052 100644
--- a/res/values-ka/strings.xml
+++ b/res/values-ka/strings.xml
@@ -38,16 +38,19 @@
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"სიგრძე: %1$d, სიგანე: %2$d"</string>
<string name="widget_preview_context_description" msgid="9045841361655787574">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> ვიჯეტი"</string>
+ <string name="widget_preview_name_and_dims_content_description" msgid="8489038126122831595">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> ვიჯეტი, სიგანე: %2$d, სიმაღლე: %3$d"</string>
<string name="add_item_request_drag_hint" msgid="8730547755622776606">"ხანგრძლივად შეეხეთ ვიჯეტს მთავარ ეკრანზე მის გადასაადგილებლად"</string>
<string name="add_to_home_screen" msgid="9168649446635919791">"მთავარ ეკრანზე დამატება"</string>
<string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> ვიჯეტი დამატებულია მთავარ ეკრანზე"</string>
<string name="suggested_widgets_header_title" msgid="1844314680798145222">"შეთავაზებები"</string>
- <string name="productivity_widget_recommendation_category_label" msgid="1722113555721820766">"თქვენი ყოველდღიური საჭირო აპები"</string>
- <string name="news_widget_recommendation_category_label" msgid="3908242346768119070">"News თქვენთვის"</string>
+ <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"უმნიშვნელოვანესები"</string>
+ <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"ახალი ამბები და ჟურნალები"</string>
<string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"განტვირთვის ადგილი"</string>
- <string name="fitness_widget_recommendation_category_label" msgid="2657652999128882431">"მიაღწიეთ ფიტნეს-მიზნებს"</string>
- <string name="weather_widget_recommendation_category_label" msgid="6712678763480668598">"მიიღეთ ინფორმაცია წინასწარ ამინდის შესახებ"</string>
- <string name="others_widget_recommendation_category_label" msgid="897876078077284733">"ასევე შეიძლება მოგეწონოთ"</string>
+ <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"გართობა"</string>
+ <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"სოციალური"</string>
+ <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"ჯანმრთელობა და ფიტნესი"</string>
+ <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"ამინდი"</string>
+ <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"თქვენთვის შემოთავაზებული"</string>
<string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"<xliff:g id="SELECTED_HEADER">%1$s</xliff:g> ვიჯეტები მდებარეობს მარჯვნივ, ძებნა და პარამეტრები — მარცხნივ"</string>
<string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# ვიჯეტი}other{# ვიჯეტი}}"</string>
<string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# მალსახმობი}other{# მალსახმობი}}"</string>
@@ -61,6 +64,8 @@
<string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"სამსახური"</string>
<string name="widget_category_conversations" msgid="8894438636213590446">"მიმოწერები"</string>
<string name="widget_category_note_taking" msgid="3469689394504266039">"ჩანიშვნა"</string>
+ <string name="widget_add_button_label" msgid="2761267068711937179">"დამატება"</string>
+ <string name="widget_add_button_content_description" msgid="1810530016360039643">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> ვიჯეტის დამატება"</string>
<string name="widget_education_header" msgid="4874760613775913787">"ადვილად მისაწვდომი სასარგებლო ინფორმაცია"</string>
<string name="widget_education_content" msgid="1731667670753497052">"იმისთვის, რომ ინფორმაცია აპების გაუხსნელად მიიღოთ, შეგიძლიათ, მთავარ ეკრანზე ვიჯეტები დაამატოთ"</string>
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"შეეხეთ ვიჯეტის პარამეტრების შესაცვლელად"</string>
diff --git a/res/values-kk/strings.xml b/res/values-kk/strings.xml
index 7038483..fce1090 100644
--- a/res/values-kk/strings.xml
+++ b/res/values-kk/strings.xml
@@ -38,16 +38,19 @@
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"Ені: %1$d, биіктігі: %2$d"</string>
<string name="widget_preview_context_description" msgid="9045841361655787574">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> виджеті"</string>
+ <string name="widget_preview_name_and_dims_content_description" msgid="8489038126122831595">"Виджет: <xliff:g id="WIDGET_NAME">%1$s</xliff:g>. Ені %2$d, биіктігі %3$d."</string>
<string name="add_item_request_drag_hint" msgid="8730547755622776606">"Негізгі экран бойынша жылжыту үшін виджетті басып ұстаңыз."</string>
<string name="add_to_home_screen" msgid="9168649446635919791">"Негізгі экранға қосу"</string>
<string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> виджеті негізгі экранға енгізілді."</string>
<string name="suggested_widgets_header_title" msgid="1844314680798145222">"Ұсыныстар"</string>
- <string name="productivity_widget_recommendation_category_label" msgid="1722113555721820766">"Күнделікті маңызды виджеттеріңіз"</string>
- <string name="news_widget_recommendation_category_label" msgid="3908242346768119070">"Сізге арналған жаңалықтар"</string>
+ <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Ең қажетті"</string>
+ <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"Газеттер мен журналдар"</string>
<string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"Жанға жайлы жер"</string>
- <string name="fitness_widget_recommendation_category_label" msgid="2657652999128882431">"Денені шынықтыру бойынша қойған мақсаттарыңызға жетіңіз"</string>
- <string name="weather_widget_recommendation_category_label" msgid="6712678763480668598">"Ауа райын алдын ала біліп отырыңыз"</string>
- <string name="others_widget_recommendation_category_label" msgid="897876078077284733">"Сізге мыналар да ұнауы мүмкін"</string>
+ <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"Ойын-сауық"</string>
+ <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"Әлеуметтік"</string>
+ <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"Денсаулық және фитнес"</string>
+ <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"Ауа райы"</string>
+ <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"Сізге ұсынылғандар"</string>
<string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"<xliff:g id="SELECTED_HEADER">%1$s</xliff:g> виджеттері оң жақта, іздеу мен опциялар сол жақта"</string>
<string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# виджет}other{# виджет}}"</string>
<string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# таңбаша}other{# таңбаша}}"</string>
@@ -61,6 +64,8 @@
<string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Жұмыс виджеттері"</string>
<string name="widget_category_conversations" msgid="8894438636213590446">"Әңгімелер"</string>
<string name="widget_category_note_taking" msgid="3469689394504266039">"Ескертпе жазу"</string>
+ <string name="widget_add_button_label" msgid="2761267068711937179">"Қосу"</string>
+ <string name="widget_add_button_content_description" msgid="1810530016360039643">"Виджет (<xliff:g id="WIDGET_NAME">%1$s</xliff:g>) қосу"</string>
<string name="widget_education_header" msgid="4874760613775913787">"Саусақпен түртсеңіз болғаны – пайдалы ақпарат көз алдыңызда"</string>
<string name="widget_education_content" msgid="1731667670753497052">"Қолданбаларды ашпай-ақ ақпарат алу үшін негізгі экранға тиісті виджеттерді қосыңыз."</string>
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Виджет параметрлерін өзгерту үшін түртіңіз."</string>
diff --git a/res/values-km/strings.xml b/res/values-km/strings.xml
index 1297dee..817bb35 100644
--- a/res/values-km/strings.xml
+++ b/res/values-km/strings.xml
@@ -38,16 +38,19 @@
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"ទទឺង %1$d គុណនឹងកម្ពស់ %2$d"</string>
<string name="widget_preview_context_description" msgid="9045841361655787574">"ធាតុក្រាហ្វិក <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
+ <string name="widget_preview_name_and_dims_content_description" msgid="8489038126122831595">"ធាតុក្រាហ្វិក <xliff:g id="WIDGET_NAME">%1$s</xliff:g> ទទឹង %2$d គុណនឹងកម្ពស់ %3$d"</string>
<string name="add_item_request_drag_hint" msgid="8730547755622776606">"ចុចលើធាតុក្រាហ្វិកឱ្យជាប់ ដើម្បីផ្លាស់ទីវាជុំវិញអេក្រង់ដើម"</string>
<string name="add_to_home_screen" msgid="9168649446635919791">"បញ្ចូលទៅក្នុងអេក្រង់ដើម"</string>
<string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"បានបញ្ចូលធាតុក្រាហ្វិក <xliff:g id="WIDGET_NAME">%1$s</xliff:g> ទៅអេក្រង់ដើម"</string>
<string name="suggested_widgets_header_title" msgid="1844314680798145222">"ការណែនាំ"</string>
- <string name="productivity_widget_recommendation_category_label" msgid="1722113555721820766">"វត្ថុចាំបាច់ប្រចាំថ្ងៃរបស់អ្នក"</string>
- <string name="news_widget_recommendation_category_label" msgid="3908242346768119070">"ព័ត៌មានសម្រាប់អ្នក"</string>
+ <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"សំខាន់"</string>
+ <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"ព័ត៌មាន និងទស្សនាវដ្ដី"</string>
<string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"តំបន់បន្ធូរអារម្មណ៍របស់អ្នក"</string>
- <string name="fitness_widget_recommendation_category_label" msgid="2657652999128882431">"សម្រេចគោលដៅហាត់ប្រាណរបស់អ្នក"</string>
- <string name="weather_widget_recommendation_category_label" msgid="6712678763480668598">"ទទួលបានដំណឹងជាមុនអំពីអាកាសធាតុ"</string>
- <string name="others_widget_recommendation_category_label" msgid="897876078077284733">"អ្នកក៏អាចនឹងចូលចិត្ត"</string>
+ <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"កម្សាន្ត"</string>
+ <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"សង្គម"</string>
+ <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"សុខភាព និងសម្បទា"</string>
+ <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"អាកាសធាតុ"</string>
+ <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"ណែនាំជូនអ្នក"</string>
<string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"ធាតុក្រាហ្វិក <xliff:g id="SELECTED_HEADER">%1$s</xliff:g> នៅខាងស្ដាំ ការស្វែងរក និងជម្រើសនៅខាងឆ្វេង"</string>
<string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{ធាតុក្រាហ្វិក #}other{ធាតុក្រាហ្វិក #}}"</string>
<string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{ផ្លូវកាត់ #}other{ផ្លូវកាត់ #}}"</string>
@@ -61,6 +64,8 @@
<string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"ការងារ"</string>
<string name="widget_category_conversations" msgid="8894438636213590446">"ការសន្ទនា"</string>
<string name="widget_category_note_taking" msgid="3469689394504266039">"ការកត់ត្រា"</string>
+ <string name="widget_add_button_label" msgid="2761267068711937179">"បញ្ចូល"</string>
+ <string name="widget_add_button_content_description" msgid="1810530016360039643">"បញ្ចូលធាតុក្រាហ្វិក <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
<string name="widget_education_header" msgid="4874760613775913787">"ទទួលបានព័ត៌មានដែលមានប្រយោជន៍យ៉ាងងាយស្រួល"</string>
<string name="widget_education_content" msgid="1731667670753497052">"ដើម្បីទទួលបានព័ត៌មានដោយមិនចាំបាច់បើកកម្មវិធី អ្នកអាចបញ្ចូលធាតុក្រាហ្វិកទៅក្នុងអេក្រង់ដើមរបស់អ្នក"</string>
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"ចុចដើម្បីប្ដូរការកំណត់ធាតុក្រាហ្វិក"</string>
diff --git a/res/values-kn/strings.xml b/res/values-kn/strings.xml
index 2832941..f0e61cf 100644
--- a/res/values-kn/strings.xml
+++ b/res/values-kn/strings.xml
@@ -38,16 +38,19 @@
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d ಅಗಲ ಮತ್ತು %2$d ಎತ್ತರ"</string>
<string name="widget_preview_context_description" msgid="9045841361655787574">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> ವಿಜೆಟ್"</string>
+ <string name="widget_preview_name_and_dims_content_description" msgid="8489038126122831595">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> ವಿಜೆಟ್, %2$d ಅಗಲ ಮತ್ತು %3$d ಎತ್ತರ"</string>
<string name="add_item_request_drag_hint" msgid="8730547755622776606">"ಹೋಮ್ ಸ್ಕ್ರೀನ್ ಸುತ್ತ ವಿಜೆಟ್ ಅನ್ನು ಸರಿಸಲು, ಸ್ಪರ್ಶಿಸಿ ಮತ್ತು ಒತ್ತಿ ಹಿಡಿದುಕೊಳ್ಳಿ"</string>
<string name="add_to_home_screen" msgid="9168649446635919791">"ಹೋಮ್ ಸ್ಕ್ರೀನ್ಗೆ ಸೇರಿಸಿ"</string>
<string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"ಹೋಮ್ಸ್ಕ್ರೀನ್ಗೆ <xliff:g id="WIDGET_NAME">%1$s</xliff:g> ವಿಜೆಟ್ ಅನ್ನು ಸೇರಿಸಲಾಗಿದೆ"</string>
<string name="suggested_widgets_header_title" msgid="1844314680798145222">"ಸಲಹೆಗಳು"</string>
- <string name="productivity_widget_recommendation_category_label" msgid="1722113555721820766">"ನಿಮ್ಮ ದೈನಂದಿನ ಎಸೆನ್ಶಿಯಲ್ಗಳು"</string>
- <string name="news_widget_recommendation_category_label" msgid="3908242346768119070">"ನಿಮಗಾಗಿ ಸುದ್ದಿ"</string>
+ <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"ಅಗತ್ಯತೆಗಳು"</string>
+ <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"ಸುದ್ದಿ ಮತ್ತು ನಿಯತಕಾಲಿಕೆಗಳು"</string>
<string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"ನೀವು ವಿಶ್ರಾಂತಿ ಪಡೆಯುವ ಸ್ಥಳ"</string>
- <string name="fitness_widget_recommendation_category_label" msgid="2657652999128882431">"ನಿಮ್ಮ ಫಿಟ್ನೆಸ್ ಗುರಿಗಳನ್ನು ಸಾಧಿಸಿ"</string>
- <string name="weather_widget_recommendation_category_label" msgid="6712678763480668598">"ಹವಾಮಾನದ ಕುರಿತು ಮುಂಚೆಯೇ ಅಪ್ಡೇಟ್ ಆಗಿರಿ"</string>
- <string name="others_widget_recommendation_category_label" msgid="897876078077284733">"ನಿಮಗೆ ಇವು ಕೂಡ ಇಷ್ಟವಾಗಬಹುದು"</string>
+ <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"ಮನರಂಜನೆ"</string>
+ <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"ಸಾಮಾಜಿಕ"</string>
+ <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"ಆರೋಗ್ಯ ಮತ್ತು ಫಿಟ್ನೆಸ್"</string>
+ <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"ಹವಾಮಾನ"</string>
+ <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"ನಿಮಗಾಗಿ ಸೂಚಿಸಲಾಗಿರುವುದು"</string>
<string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"ಬಲಭಾಗದಲ್ಲಿ <xliff:g id="SELECTED_HEADER">%1$s</xliff:g> ವಿಜೆಟ್ಗಳು, ಎಡಭಾಗದಲ್ಲಿ ಹುಡುಕಾಟ ಮತ್ತು ಆಯ್ಕೆಗಳು"</string>
<string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# ವಿಜೆಟ್}one{# ವಿಜೆಟ್ಗಳು}other{# ವಿಜೆಟ್ಗಳು}}"</string>
<string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# ಶಾರ್ಟ್ಕಟ್}one{# ಶಾರ್ಟ್ಕಟ್ಗಳು}other{# ಶಾರ್ಟ್ಕಟ್ಗಳು}}"</string>
@@ -61,6 +64,8 @@
<string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"ಕೆಲಸ"</string>
<string name="widget_category_conversations" msgid="8894438636213590446">"ಸಂಭಾಷಣೆಗಳು"</string>
<string name="widget_category_note_taking" msgid="3469689394504266039">"ಟಿಪ್ಪಣಿ ತೆಗೆದುಕೊಳ್ಳುವುದು"</string>
+ <string name="widget_add_button_label" msgid="2761267068711937179">"ಸೇರಿಸಿ"</string>
+ <string name="widget_add_button_content_description" msgid="1810530016360039643">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> ವಿಜೆಟ್ ಸೇರಿಸಿ"</string>
<string name="widget_education_header" msgid="4874760613775913787">"ನಿಮ್ಮ ಬೆರಳ ತುದಿಯಲ್ಲಿ ಉಪಯುಕ್ತ ಮಾಹಿತಿ"</string>
<string name="widget_education_content" msgid="1731667670753497052">"ಆ್ಯಪ್ಗಳನ್ನು ತೆರೆಯದೆಯೇ ಮಾಹಿತಿಯನ್ನು ಪಡೆಯಲು, ನಿಮ್ಮ ಹೋಮ್ ಸ್ಕ್ರೀನ್ನಲ್ಲಿ ನೀವು ವಿಜೆಟ್ಗಳನ್ನು ಸೇರಿಸಬಹುದು"</string>
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"ವಿಜೆಟ್ ಸೆಟ್ಟಿಂಗ್ಗಳನ್ನು ಬದಲಾಯಿಸಲು ಟ್ಯಾಪ್ ಮಾಡಿ"</string>
diff --git a/res/values-ko/strings.xml b/res/values-ko/strings.xml
index 5c1e854..e916cef 100644
--- a/res/values-ko/strings.xml
+++ b/res/values-ko/strings.xml
@@ -38,16 +38,19 @@
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d×%2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"너비 %1$d, 높이 %2$d"</string>
<string name="widget_preview_context_description" msgid="9045841361655787574">"위젯 <xliff:g id="WIDGET_NAME">%1$s</xliff:g>개"</string>
+ <string name="widget_preview_name_and_dims_content_description" msgid="8489038126122831595">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> 위젯, 너비는 %2$d, 높이는 %3$d입니다"</string>
<string name="add_item_request_drag_hint" msgid="8730547755622776606">"홈 화면에서 위젯을 이동하려면 길게 터치하세요."</string>
<string name="add_to_home_screen" msgid="9168649446635919791">"홈 화면에 추가"</string>
<string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> 위젯이 홈 화면에 추가됨"</string>
<string name="suggested_widgets_header_title" msgid="1844314680798145222">"추천"</string>
- <string name="productivity_widget_recommendation_category_label" msgid="1722113555721820766">"매일 사용하는 항목"</string>
- <string name="news_widget_recommendation_category_label" msgid="3908242346768119070">"추천 뉴스"</string>
+ <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"필수"</string>
+ <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"뉴스 및 잡지"</string>
<string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"휴식 공간"</string>
- <string name="fitness_widget_recommendation_category_label" msgid="2657652999128882431">"피트니스 목표 달성"</string>
- <string name="weather_widget_recommendation_category_label" msgid="6712678763480668598">"사전에 날씨 확인"</string>
- <string name="others_widget_recommendation_category_label" msgid="897876078077284733">"좋아할 만한 항목"</string>
+ <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"엔터테인먼트"</string>
+ <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"소셜"</string>
+ <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"건강 및 피트니스"</string>
+ <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"날씨"</string>
+ <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"추천"</string>
<string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"오른쪽에 <xliff:g id="SELECTED_HEADER">%1$s</xliff:g> 위젯, 왼쪽에 검색 및 옵션"</string>
<string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{위젯 #개}other{위젯 #개}}"</string>
<string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{바로가기 #개}other{바로가기 #개}}"</string>
@@ -61,6 +64,8 @@
<string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"직장 위젯"</string>
<string name="widget_category_conversations" msgid="8894438636213590446">"대화"</string>
<string name="widget_category_note_taking" msgid="3469689394504266039">"메모"</string>
+ <string name="widget_add_button_label" msgid="2761267068711937179">"추가"</string>
+ <string name="widget_add_button_content_description" msgid="1810530016360039643">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> 위젯 추가"</string>
<string name="widget_education_header" msgid="4874760613775913787">"빠르게 유용한 정보 확인"</string>
<string name="widget_education_content" msgid="1731667670753497052">"앱을 열지 않고 정보를 확인하려면 홈 화면에 위젯을 추가하세요."</string>
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"탭하여 위젯 설정 변경"</string>
diff --git a/res/values-ky/strings.xml b/res/values-ky/strings.xml
index 7c32fe6..47eae5b 100644
--- a/res/values-ky/strings.xml
+++ b/res/values-ky/strings.xml
@@ -38,16 +38,19 @@
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"Туурасы: %1$d, бийиктиги: %2$d"</string>
<string name="widget_preview_context_description" msgid="9045841361655787574">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> виджети"</string>
+ <string name="widget_preview_name_and_dims_content_description" msgid="8489038126122831595">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> виджети, кеңдиги %2$d жана бийиктиги %3$d"</string>
<string name="add_item_request_drag_hint" msgid="8730547755622776606">"Башкы экранга жылдыруу үчүн виджетти коё бербей басып туруңуз"</string>
<string name="add_to_home_screen" msgid="9168649446635919791">"Башкы экранга кошуу"</string>
<string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> виджети башкы экранга кошулду"</string>
<string name="suggested_widgets_header_title" msgid="1844314680798145222">"Сунуштар"</string>
- <string name="productivity_widget_recommendation_category_label" msgid="1722113555721820766">"Күнүмдүк керектелүүчү нерселер"</string>
- <string name="news_widget_recommendation_category_label" msgid="3908242346768119070">"Сиз үчүн жаңылыктар"</string>
+ <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Эң зарыл параметрлер"</string>
+ <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"Жаңылыктар жана журналдар"</string>
<string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"Чер жазуу"</string>
- <string name="fitness_widget_recommendation_category_label" msgid="2657652999128882431">"Фитнес максаттарыңызга жетиңиз"</string>
- <string name="weather_widget_recommendation_category_label" msgid="6712678763480668598">"Аба ырайы тууралуу маалымат"</string>
- <string name="others_widget_recommendation_category_label" msgid="897876078077284733">"Төмөнкүлөр да жагышы мүмкүн"</string>
+ <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"Көңүл ачуу"</string>
+ <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"Коомдук тармактар"</string>
+ <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"Ден соолук жана дене-бойду чыңдоо"</string>
+ <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"Аба ырайы"</string>
+ <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"Сизге сунушталат"</string>
<string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"<xliff:g id="SELECTED_HEADER">%1$s</xliff:g> виджеттери оң, ал эми издөө жана параметрлер сол жакта"</string>
<string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# виджет}other{# виджет}}"</string>
<string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# ыкчам баскыч}other{# ыкчам баскыч}}"</string>
@@ -61,6 +64,8 @@
<string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Жумуш"</string>
<string name="widget_category_conversations" msgid="8894438636213590446">"Сүйлөшүүлөр"</string>
<string name="widget_category_note_taking" msgid="3469689394504266039">"Эскертме жазуу"</string>
+ <string name="widget_add_button_label" msgid="2761267068711937179">"Кошуу"</string>
+ <string name="widget_add_button_content_description" msgid="1810530016360039643">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> виджетин кошуу"</string>
<string name="widget_education_header" msgid="4874760613775913787">"Керектүү маалымат манжаңыздын учунда"</string>
<string name="widget_education_content" msgid="1731667670753497052">"Бир нерсе билүү үчүн колдонмолорду улам ачып убара болбостон, башкы экранга виджеттерди кошуп коюңуз."</string>
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Виджеттин параметрлерин өзгөртүү үчүн таптап коюңуз"</string>
diff --git a/res/values-lo/strings.xml b/res/values-lo/strings.xml
index 0ff2d4e..658e8e3 100644
--- a/res/values-lo/strings.xml
+++ b/res/values-lo/strings.xml
@@ -38,16 +38,19 @@
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"ກວ້າງ %1$d ຄູນສູງ %2$d"</string>
<string name="widget_preview_context_description" msgid="9045841361655787574">"ວິດເຈັດ <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
+ <string name="widget_preview_name_and_dims_content_description" msgid="8489038126122831595">"ວິດເຈັດ <xliff:g id="WIDGET_NAME">%1$s</xliff:g>, ກວ້າງ %2$d ສູງ %3$d"</string>
<string name="add_item_request_drag_hint" msgid="8730547755622776606">"ແຕະໃສ່ວິດເຈັດຄ້າງໄວ້ເພື່ອຍ້າຍມັນໄປມາຢູ່ໂຮມສະກຣີນ"</string>
<string name="add_to_home_screen" msgid="9168649446635919791">"ເພີ່ມໃສ່ໂຮມສະກຣີນ"</string>
<string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"ເພີ່ມວິດເຈັດ <xliff:g id="WIDGET_NAME">%1$s</xliff:g> ໃສ່ໂຮມສະກຣີນແລ້ວ"</string>
<string name="suggested_widgets_header_title" msgid="1844314680798145222">"ການແນະນຳ"</string>
- <string name="productivity_widget_recommendation_category_label" msgid="1722113555721820766">"ສິ່ງຈຳເປັນໃນຊີວິດປະຈຳວັນຂອງທ່ານ"</string>
- <string name="news_widget_recommendation_category_label" msgid="3908242346768119070">"ຂ່າວສຳລັບທ່ານ"</string>
+ <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"ສິ່ງຈຳເປັນ"</string>
+ <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"ຂ່າວ ແລະ ວາລະສານ"</string>
<string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"ພື້ນທີ່ພັກຜ່ອນຂອງທ່ານ"</string>
- <string name="fitness_widget_recommendation_category_label" msgid="2657652999128882431">"ບັນລຸເປົ້າໝາຍການອອກກຳລັງກາຍຂອງທ່ານ"</string>
- <string name="weather_widget_recommendation_category_label" msgid="6712678763480668598">"ຮູ້ສະພາບອາກາດລ່ວງໜ້າ"</string>
- <string name="others_widget_recommendation_category_label" msgid="897876078077284733">"ທ່ານອາດຈະມັກ"</string>
+ <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"ຄວາມບັນເທີງ"</string>
+ <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"ສັງຄົມ"</string>
+ <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"ສຸຂະພາບ ແລະ ການອອກກຳລັງກາຍ"</string>
+ <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"ສະພາບອາກາດ"</string>
+ <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"ແນະນຳສຳລັບທ່ານ"</string>
<string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"ວິດເຈັດ <xliff:g id="SELECTED_HEADER">%1$s</xliff:g> ຢູ່ທາງຂວາ, ການຊອກຫາ ແລະ ຕົວເລືອກຢູ່ທາງຊ້າຍ"</string>
<string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# ວິດເຈັດ}other{# ວິດເຈັດ}}"</string>
<string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# ທາງລັດ}other{# ທາງລັດ}}"</string>
@@ -61,6 +64,8 @@
<string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"ວຽກ"</string>
<string name="widget_category_conversations" msgid="8894438636213590446">"ການສົນທະນາ"</string>
<string name="widget_category_note_taking" msgid="3469689394504266039">"ການຈົດບັນທຶກ"</string>
+ <string name="widget_add_button_label" msgid="2761267068711937179">"ເພີ່ມ"</string>
+ <string name="widget_add_button_content_description" msgid="1810530016360039643">"ເພີ່ມວິດເຈັດ <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
<string name="widget_education_header" msgid="4874760613775913787">"ຂໍ້ມູນທີ່ເປັນປະໂຫຍດຢູ່ປາຍນິ້ວຂອງທ່ານ"</string>
<string name="widget_education_content" msgid="1731667670753497052">"ເພື່ອຮັບຂໍ້ມູນໂດຍບໍ່ຕ້ອງເປີດແອັບ, ທ່ານສາມາດເພີ່ມວິດເຈັດໃສ່ໂຮມສະກຣີນຂອງທ່ານໄດ້"</string>
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"ແຕະເພື່ອປ່ຽນການຕັ້ງຄ່າວິດເຈັດ"</string>
diff --git a/res/values-lt/strings.xml b/res/values-lt/strings.xml
index ef81b96..4e8c453 100644
--- a/res/values-lt/strings.xml
+++ b/res/values-lt/strings.xml
@@ -38,16 +38,19 @@
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d plotis ir %2$d aukštis"</string>
<string name="widget_preview_context_description" msgid="9045841361655787574">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> valdiklis"</string>
+ <string name="widget_preview_name_and_dims_content_description" msgid="8489038126122831595">"Valdiklis: <xliff:g id="WIDGET_NAME">%1$s</xliff:g>; %2$d pločio ir %3$d aukščio"</string>
<string name="add_item_request_drag_hint" msgid="8730547755622776606">"Paliesdami ir palaikydami valdiklį galite judėti pagrindiniame ekrane"</string>
<string name="add_to_home_screen" msgid="9168649446635919791">"Pridėti prie pagrindinio ekrano"</string>
<string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"Valdiklis „<xliff:g id="WIDGET_NAME">%1$s</xliff:g>“ pridėtas prie pagrindinio ekrano"</string>
<string name="suggested_widgets_header_title" msgid="1844314680798145222">"Pasiūlymai"</string>
- <string name="productivity_widget_recommendation_category_label" msgid="1722113555721820766">"Tai, ko jums reikia kasdien"</string>
- <string name="news_widget_recommendation_category_label" msgid="3908242346768119070">"Naujienos jums"</string>
+ <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Būtiniausi"</string>
+ <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"Naujienos ir žurnalai"</string>
<string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"Jūsų atsipalaidavimo zona"</string>
- <string name="fitness_widget_recommendation_category_label" msgid="2657652999128882431">"Pasiekite mankštos tikslus"</string>
- <string name="weather_widget_recommendation_category_label" msgid="6712678763480668598">"Visada žinokite, kokie bus orai"</string>
- <string name="others_widget_recommendation_category_label" msgid="897876078077284733">"Jums taip pat gali patikti"</string>
+ <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"Pramogos"</string>
+ <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"Socialiniai tinklai"</string>
+ <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"Sveikata ir kūno rengyba"</string>
+ <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"Orai"</string>
+ <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"Siūloma jums"</string>
<string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"<xliff:g id="SELECTED_HEADER">%1$s</xliff:g> valdikliai dešinėje, paieška ir parinktys kairėje"</string>
<string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# valdiklis}one{# valdiklis}few{# valdikliai}many{# valdiklio}other{# valdiklių}}"</string>
<string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# spartusis klavišas}one{# spartusis klavišas}few{# spartieji klavišai}many{# sparčiojo klavišo}other{# sparčiųjų klavišų}}"</string>
@@ -61,6 +64,8 @@
<string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Darbas"</string>
<string name="widget_category_conversations" msgid="8894438636213590446">"Pokalbiai"</string>
<string name="widget_category_note_taking" msgid="3469689394504266039">"Užrašų kūrimas"</string>
+ <string name="widget_add_button_label" msgid="2761267068711937179">"Pridėti"</string>
+ <string name="widget_add_button_content_description" msgid="1810530016360039643">"Pridėti valdiklį: <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
<string name="widget_education_header" msgid="4874760613775913787">"Lengvai pasiekiama naudinga informacija"</string>
<string name="widget_education_content" msgid="1731667670753497052">"Jei norite gauti informacijos neatidarę programų, galite pridėti valdiklių pagrindiniame ekrane"</string>
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Palieskite, kad pakeistumėte valdiklio nustatymus"</string>
diff --git a/res/values-lv/strings.xml b/res/values-lv/strings.xml
index d04afa1..3e1fcdb 100644
--- a/res/values-lv/strings.xml
+++ b/res/values-lv/strings.xml
@@ -38,16 +38,19 @@
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d plats un %2$d augsts"</string>
<string name="widget_preview_context_description" msgid="9045841361655787574">"Logrīks <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
+ <string name="widget_preview_name_and_dims_content_description" msgid="8489038126122831595">"Logrīks <xliff:g id="WIDGET_NAME">%1$s</xliff:g>, %2$d plats un %3$d augsts"</string>
<string name="add_item_request_drag_hint" msgid="8730547755622776606">"Pieskarieties logrīkam un turiet to, lai to pārvietotu pa sākuma ekrānu."</string>
<string name="add_to_home_screen" msgid="9168649446635919791">"Pievienot sākuma ekrānam"</string>
<string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"Logrīks “<xliff:g id="WIDGET_NAME">%1$s</xliff:g>” ir pievienots sākuma ekrānam"</string>
<string name="suggested_widgets_header_title" msgid="1844314680798145222">"Ieteikumi"</string>
- <string name="productivity_widget_recommendation_category_label" msgid="1722113555721820766">"Jums ikdienā vajadzīgais"</string>
- <string name="news_widget_recommendation_category_label" msgid="3908242346768119070">"Ziņas jums"</string>
+ <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Produktivitātei"</string>
+ <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"Ziņas un žurnāli"</string>
<string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"Jūsu atpūtas stūrītis"</string>
- <string name="fitness_widget_recommendation_category_label" msgid="2657652999128882431">"Sasniedziet fitnesa mērķus"</string>
- <string name="weather_widget_recommendation_category_label" msgid="6712678763480668598">"Neļaujiet laikapstākļiem jūs pārsteigt"</string>
- <string name="others_widget_recommendation_category_label" msgid="897876078077284733">"Jums varētu patikt arī…"</string>
+ <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"Izklaide"</string>
+ <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"Sociālie tīkli"</string>
+ <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"Veselība un fitness"</string>
+ <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"Laikapstākļi"</string>
+ <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"Ieteikumi jums"</string>
<string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"Pa labi logrīki <xliff:g id="SELECTED_HEADER">%1$s</xliff:g>, pa kreisi meklēšana un iespējas"</string>
<string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# logrīks}zero{# logrīku}one{# logrīks}other{# logrīki}}"</string>
<string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# saīsne}zero{# saīšņu}one{# saīsne}other{# saīsnes}}"</string>
@@ -61,6 +64,8 @@
<string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Darba"</string>
<string name="widget_category_conversations" msgid="8894438636213590446">"Sarunas"</string>
<string name="widget_category_note_taking" msgid="3469689394504266039">"Piezīmju pierakstīšana"</string>
+ <string name="widget_add_button_label" msgid="2761267068711937179">"Pievienot"</string>
+ <string name="widget_add_button_content_description" msgid="1810530016360039643">"Pievienot logrīku <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
<string name="widget_education_header" msgid="4874760613775913787">"Ērta piekļuve noderīgai informācijai"</string>
<string name="widget_education_content" msgid="1731667670753497052">"Lai iegūtu informāciju, neatverot lietotnes, varat pievienot sākuma ekrānam logrīkus"</string>
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Pieskarieties, lai mainītu logrīka iestatījumus."</string>
diff --git a/res/values-mk/strings.xml b/res/values-mk/strings.xml
index 184d7cc..cf9c954 100644
--- a/res/values-mk/strings.xml
+++ b/res/values-mk/strings.xml
@@ -38,16 +38,19 @@
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d широк на %2$d висок"</string>
<string name="widget_preview_context_description" msgid="9045841361655787574">"Виџет <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
+ <string name="widget_preview_name_and_dims_content_description" msgid="8489038126122831595">"Виџет <xliff:g id="WIDGET_NAME">%1$s</xliff:g>, ширина од %2$d со висина од %3$d"</string>
<string name="add_item_request_drag_hint" msgid="8730547755622776606">"Допрете го и задржете го виџетот за да го движите наоколу на почетниот екран"</string>
<string name="add_to_home_screen" msgid="9168649446635919791">"Додај на почетниот екран"</string>
<string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"Виџетот <xliff:g id="WIDGET_NAME">%1$s</xliff:g> е додаден на почетниот екран"</string>
<string name="suggested_widgets_header_title" msgid="1844314680798145222">"Предлози"</string>
- <string name="productivity_widget_recommendation_category_label" msgid="1722113555721820766">"Вашите секојдневни неопходности"</string>
- <string name="news_widget_recommendation_category_label" msgid="3908242346768119070">"Вести за вас"</string>
+ <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Неопходни"</string>
+ <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"Вести и списанија"</string>
<string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"Вашата зона за релаксација"</string>
- <string name="fitness_widget_recommendation_category_label" msgid="2657652999128882431">"Достигнете ги целите за фитнес"</string>
- <string name="weather_widget_recommendation_category_label" msgid="6712678763480668598">"Бидете во тек со временската прогноза"</string>
- <string name="others_widget_recommendation_category_label" msgid="897876078077284733">"Можеби ќе ви се допадне и"</string>
+ <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"Забава"</string>
+ <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"Друштвени"</string>
+ <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"Здравје и фитнес"</string>
+ <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"Време"</string>
+ <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"Препорачано за вас"</string>
<string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"<xliff:g id="SELECTED_HEADER">%1$s</xliff:g> виџети оддесно, „Пребарување“ и „Опции“ одлево"</string>
<string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# виџет}one{# виџет}other{# виџети}}"</string>
<string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# кратенка}one{# кратенка}other{# кратенки}}"</string>
@@ -61,6 +64,8 @@
<string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Работни"</string>
<string name="widget_category_conversations" msgid="8894438636213590446">"Разговори"</string>
<string name="widget_category_note_taking" msgid="3469689394504266039">"Фаќање белешки"</string>
+ <string name="widget_add_button_label" msgid="2761267068711937179">"Додај"</string>
+ <string name="widget_add_button_content_description" msgid="1810530016360039643">"Додај го виџетот <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
<string name="widget_education_header" msgid="4874760613775913787">"Корисни информации на дофат на прстите"</string>
<string name="widget_education_content" msgid="1731667670753497052">"За да добивате информации без да ги отворате апликациите, може да додадете виџети на почетниот екран"</string>
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Допрете за да ги промените поставките за виџетот"</string>
diff --git a/res/values-ml/strings.xml b/res/values-ml/strings.xml
index 3952b22..0b66a36 100644
--- a/res/values-ml/strings.xml
+++ b/res/values-ml/strings.xml
@@ -38,16 +38,19 @@
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d വീതിയും %2$d ഉയരവും"</string>
<string name="widget_preview_context_description" msgid="9045841361655787574">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> വിജറ്റ്"</string>
+ <string name="widget_preview_name_and_dims_content_description" msgid="8489038126122831595">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> വിജറ്റ്, %2$d വീതി %3$d ഉയരം"</string>
<string name="add_item_request_drag_hint" msgid="8730547755622776606">"ഹോം സ്ക്രീനിന് ചുറ്റും വിജറ്റ് നീക്കാൻ അതിൽ സ്പർശിച്ച് പിടിക്കുക"</string>
<string name="add_to_home_screen" msgid="9168649446635919791">"ഹോം സ്ക്രീനിലേക്ക് ചേർക്കുക"</string>
<string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> വിജറ്റ് ഹോം സ്ക്രീനിലേക്ക് ചേർത്തു"</string>
<string name="suggested_widgets_header_title" msgid="1844314680798145222">"നിർദ്ദേശങ്ങൾ"</string>
- <string name="productivity_widget_recommendation_category_label" msgid="1722113555721820766">"ഓരോ ദിവസവും ആവശ്യമായവ"</string>
- <string name="news_widget_recommendation_category_label" msgid="3908242346768119070">"നിങ്ങൾക്കായുള്ള വാർത്ത"</string>
+ <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"ആവശ്യമായവ"</string>
+ <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"വാർത്തകളും മാസികകളും"</string>
<string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"നിങ്ങൾക്ക് സുഖപ്രദമായ സ്ഥലം"</string>
- <string name="fitness_widget_recommendation_category_label" msgid="2657652999128882431">"ശാരീരികക്ഷമതയുമായി ബന്ധപ്പെട്ട ലക്ഷ്യങ്ങൾ കൈവരിക്കൂ"</string>
- <string name="weather_widget_recommendation_category_label" msgid="6712678763480668598">"കാലാവസ്ഥ മുൻകൂട്ടി മനസ്സിലാക്കുക"</string>
- <string name="others_widget_recommendation_category_label" msgid="897876078077284733">"നിങ്ങൾക്ക് ഇനിപ്പറയുന്നവ ഇഷ്ടമായേക്കാം"</string>
+ <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"വിനോദം"</string>
+ <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"സാമൂഹികം"</string>
+ <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"ആരോഗ്യവും ശാരീരികക്ഷമതയും"</string>
+ <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"കാലാവസ്ഥ"</string>
+ <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"നിങ്ങൾക്കായി നിർദ്ദേശിച്ചവ"</string>
<string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"വലതുവശത്ത് <xliff:g id="SELECTED_HEADER">%1$s</xliff:g> വിജറ്റുകളും ഇടതുവശത്ത് തിരയൽ, ഓപ്ഷനുകൾ എന്നിവയും"</string>
<string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# വിജറ്റ്}other{# വിജറ്റുകൾ}}"</string>
<string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# കുറുക്കുവഴി}other{# കുറുക്കുവഴികൾ}}"</string>
@@ -61,6 +64,8 @@
<string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"ജോലി"</string>
<string name="widget_category_conversations" msgid="8894438636213590446">"സംഭാഷണങ്ങൾ"</string>
<string name="widget_category_note_taking" msgid="3469689394504266039">"കുറിപ്പ് രേഖപ്പെടുത്തൽ"</string>
+ <string name="widget_add_button_label" msgid="2761267068711937179">"ചേർക്കുക"</string>
+ <string name="widget_add_button_content_description" msgid="1810530016360039643">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> വിജറ്റ് ചേർക്കുക"</string>
<string name="widget_education_header" msgid="4874760613775913787">"ഉപകാരപ്രദമായ വിവരങ്ങൾ നിങ്ങളുടെ വിരൽത്തുമ്പിൽ"</string>
<string name="widget_education_content" msgid="1731667670753497052">"ആപ്പുകൾ തുറക്കാതെ വിവരങ്ങൾ ലഭിക്കാൻ, നിങ്ങൾക്ക് ഹോം സ്ക്രീനിലേക്ക് വിജറ്റുകൾ ചേർക്കാം"</string>
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"വിജറ്റ് ക്രമീകരണം മാറ്റാൻ ടാപ്പ് ചെയ്യുക"</string>
diff --git a/res/values-mn/strings.xml b/res/values-mn/strings.xml
index 7a73041..0feeb10 100644
--- a/res/values-mn/strings.xml
+++ b/res/values-mn/strings.xml
@@ -38,16 +38,19 @@
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d өргөн %2$d өндөр"</string>
<string name="widget_preview_context_description" msgid="9045841361655787574">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> жижиг хэрэгсэл"</string>
+ <string name="widget_preview_name_and_dims_content_description" msgid="8489038126122831595">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> виджет, %3$d-н өндрийг харьцах нь %2$d-н өргөн"</string>
<string name="add_item_request_drag_hint" msgid="8730547755622776606">"Виджетийг үндсэн нүүрний эргэн тойронд зөөхийн тулд түүнд хүрээд, удаан дарна уу"</string>
<string name="add_to_home_screen" msgid="9168649446635919791">"Үндсэн нүүрэнд нэмэх"</string>
<string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> виджетийг үндсэн нүүрэнд нэмсэн"</string>
<string name="suggested_widgets_header_title" msgid="1844314680798145222">"Зөвлөмжүүд"</string>
- <string name="productivity_widget_recommendation_category_label" msgid="1722113555721820766">"Таны өдөр тутмын хэрэгцээт зүйлс"</string>
- <string name="news_widget_recommendation_category_label" msgid="3908242346768119070">"Танд зориулсан мэдээ"</string>
+ <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Зайлшгүй хэрэгтэй"</string>
+ <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"Мэдээ, сэтгүүлүүд"</string>
<string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"Таны амралтын бүс"</string>
- <string name="fitness_widget_recommendation_category_label" msgid="2657652999128882431">"Фитнесийн зорилгодоо хүрээрэй"</string>
- <string name="weather_widget_recommendation_category_label" msgid="6712678763480668598">"Цаг агаарын урьдчилсан мэдээлэлтэй байгаарай"</string>
- <string name="others_widget_recommendation_category_label" msgid="897876078077284733">"Танд таалагдаж магадгүй"</string>
+ <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"Энтертэйнмент"</string>
+ <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"Сошиал"</string>
+ <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"Эрүүл мэнд, фитнес"</string>
+ <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"Цаг агаар"</string>
+ <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"Танд санал болгосон"</string>
<string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"Баруун талд <xliff:g id="SELECTED_HEADER">%1$s</xliff:g>-н виджет, зүүн талд хайлт болон сонгуултууд байна"</string>
<string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# виджет}other{# виджет}}"</string>
<string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# товчлол}other{# товчлол}}"</string>
@@ -61,6 +64,8 @@
<string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Ажил"</string>
<string name="widget_category_conversations" msgid="8894438636213590446">"Харилцан яриа"</string>
<string name="widget_category_note_taking" msgid="3469689394504266039">"Тэмдэглэл хөтлөх"</string>
+ <string name="widget_add_button_label" msgid="2761267068711937179">"Нэмэх"</string>
+ <string name="widget_add_button_content_description" msgid="1810530016360039643">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> виджетийг нэмэх"</string>
<string name="widget_education_header" msgid="4874760613775913787">"Хэрэгтэй мэдээллээ хурууныхаа үзүүрээр аваарай"</string>
<string name="widget_education_content" msgid="1731667670753497052">"Аппуудыг нээлгүйгээр мэдээлэл авахын тулд та үндсэн нүүрэндээ виджетүүд нэмэх боломжтой"</string>
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Жижиг хэрэгслийн тохиргоог өөрчлөхийн тулд товшино уу"</string>
diff --git a/res/values-mr/strings.xml b/res/values-mr/strings.xml
index c54f614..58f4f0e 100644
--- a/res/values-mr/strings.xml
+++ b/res/values-mr/strings.xml
@@ -38,16 +38,19 @@
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d रूंद बाय %2$d उंच"</string>
<string name="widget_preview_context_description" msgid="9045841361655787574">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> विजेट"</string>
+ <string name="widget_preview_name_and_dims_content_description" msgid="8489038126122831595">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> विजेट, %2$d रुंदी आणि %3$d उंची"</string>
<string name="add_item_request_drag_hint" msgid="8730547755622776606">"होम स्क्रीनवर हलवण्यासाठी विजेटला स्पर्श करून धरून ठेवा"</string>
<string name="add_to_home_screen" msgid="9168649446635919791">"होम स्क्रीनवर जोडा"</string>
<string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> हे विजेट तुमच्या होम स्क्रीनवर जोडले आहे"</string>
<string name="suggested_widgets_header_title" msgid="1844314680798145222">"सूचना"</string>
- <string name="productivity_widget_recommendation_category_label" msgid="1722113555721820766">"तुमच्या दररोजच्या आवश्यक गोष्टी"</string>
- <string name="news_widget_recommendation_category_label" msgid="3908242346768119070">"तुमच्यासाठी बातम्या"</string>
+ <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"आवश्यक गोष्टी"</string>
+ <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"बातम्या आणि मासिके"</string>
<string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"तुमचा आरामदायक झोन"</string>
- <string name="fitness_widget_recommendation_category_label" msgid="2657652999128882431">"तुमची फिटनेस ध्येये गाठा"</string>
- <string name="weather_widget_recommendation_category_label" msgid="6712678763480668598">"हवामानासंबंधित बातम्या आगामी मिळवा"</string>
- <string name="others_widget_recommendation_category_label" msgid="897876078077284733">"तुम्हाला हेदेखील आवडू शकते"</string>
+ <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"मनोरंजन"</string>
+ <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"सोशल"</string>
+ <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"आरोग्य आणि फिटनेस"</string>
+ <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"हवामान"</string>
+ <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"तुमच्यासाठी सुचवलेले"</string>
<string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"उजवीकडे <xliff:g id="SELECTED_HEADER">%1$s</xliff:g> विजेट, डावीकडे शोध आणि पर्याय"</string>
<string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# विजेट}other{# विजेट}}"</string>
<string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# शॉर्टकट}other{# शॉर्टकट}}"</string>
@@ -61,6 +64,8 @@
<string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"ऑफिस"</string>
<string name="widget_category_conversations" msgid="8894438636213590446">"संभाषणे"</string>
<string name="widget_category_note_taking" msgid="3469689394504266039">"टिपा घेणे"</string>
+ <string name="widget_add_button_label" msgid="2761267068711937179">"जोडा"</string>
+ <string name="widget_add_button_content_description" msgid="1810530016360039643">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> विजेट जोडा"</string>
<string name="widget_education_header" msgid="4874760613775913787">"तुमच्यासाठी सहज उपलब्ध असलेली माहिती"</string>
<string name="widget_education_content" msgid="1731667670753497052">"ॲप्स न उघडता माहिती मिळवण्यासाठी, तुम्ही तुमच्या होम स्क्रीनवर विजेट जोडू शकता"</string>
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"विजेट सेटिंग्ज बदलण्यासाठी टॅप करा"</string>
diff --git a/res/values-ms/strings.xml b/res/values-ms/strings.xml
index ac7f8ed..7832705 100644
--- a/res/values-ms/strings.xml
+++ b/res/values-ms/strings.xml
@@ -38,16 +38,19 @@
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"Lebar %1$d kali tinggi %2$d"</string>
<string name="widget_preview_context_description" msgid="9045841361655787574">"Widget <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
+ <string name="widget_preview_name_and_dims_content_description" msgid="8489038126122831595">"widget <xliff:g id="WIDGET_NAME">%1$s</xliff:g>, lebar %2$d kali tinggi %3$d"</string>
<string name="add_item_request_drag_hint" msgid="8730547755622776606">"Sentuh & tahan widget untuk menggerakkan widget di sekitar skrin utama"</string>
<string name="add_to_home_screen" msgid="9168649446635919791">"Tambahkan pada skrin utama"</string>
<string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"Widget <xliff:g id="WIDGET_NAME">%1$s</xliff:g> ditambahkan pada skrin utama"</string>
<string name="suggested_widgets_header_title" msgid="1844314680798145222">"Cadangan"</string>
- <string name="productivity_widget_recommendation_category_label" msgid="1722113555721820766">"Keperluan Harian Anda"</string>
- <string name="news_widget_recommendation_category_label" msgid="3908242346768119070">"Berita Untuk Anda"</string>
+ <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Apl Asas"</string>
+ <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"Berita & majalah"</string>
<string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"Zon Santai Anda"</string>
- <string name="fitness_widget_recommendation_category_label" msgid="2657652999128882431">"Capai Matlamat Kecergasan Anda"</string>
- <string name="weather_widget_recommendation_category_label" msgid="6712678763480668598">"Ketahui Perkembangan Terkini Cuaca"</string>
- <string name="others_widget_recommendation_category_label" msgid="897876078077284733">"Anda Mungkin Turut Menyukai"</string>
+ <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"Hiburan"</string>
+ <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"Sosial"</string>
+ <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"Kesihatan & kecergasan"</string>
+ <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"Cuaca"</string>
+ <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"Dicadangkan untuk anda"</string>
<string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"Widget <xliff:g id="SELECTED_HEADER">%1$s</xliff:g> pada sebelah kanan, carian dan pilihan pada sebelah kiri"</string>
<string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# widget}other{# widget}}"</string>
<string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# pintasan}other{# pintasan}}"</string>
@@ -61,6 +64,8 @@
<string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Tempat kerja"</string>
<string name="widget_category_conversations" msgid="8894438636213590446">"Perbualan"</string>
<string name="widget_category_note_taking" msgid="3469689394504266039">"Pengambilan nota"</string>
+ <string name="widget_add_button_label" msgid="2761267068711937179">"Tambah"</string>
+ <string name="widget_add_button_content_description" msgid="1810530016360039643">"Tambahkan widget <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
<string name="widget_education_header" msgid="4874760613775913787">"Maklumat berguna di hujung jari anda"</string>
<string name="widget_education_content" msgid="1731667670753497052">"Untuk mendapatkan maklumat tanpa membuka apl, anda boleh menambahkan widget pada skrin utama anda"</string>
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Ketik untuk menukar tetapan widget"</string>
diff --git a/res/values-my/strings.xml b/res/values-my/strings.xml
index c3067bb..80fef4b 100644
--- a/res/values-my/strings.xml
+++ b/res/values-my/strings.xml
@@ -38,16 +38,19 @@
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"အလျား %1$d နှင့် အမြင့် %2$d"</string>
<string name="widget_preview_context_description" msgid="9045841361655787574">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> ဝိဂျက်"</string>
+ <string name="widget_preview_name_and_dims_content_description" msgid="8489038126122831595">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> ဝိဂျက်၊ အကျယ် %2$d နှင့် အမြင့် %3$d"</string>
<string name="add_item_request_drag_hint" msgid="8730547755622776606">"ပင်မစာမျက်နှာတွင်ရွှေ့ရန် ဝိဂျက်ကို တို့ထိ၍ ဖိထားပါ"</string>
<string name="add_to_home_screen" msgid="9168649446635919791">"ပင်မစာမျက်နှာတွင် ထည့်ရန်"</string>
<string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> ဝိဂျက်ကို ပင်မစာမျက်နှာတွင် ထည့်လိုက်ပြီ"</string>
<string name="suggested_widgets_header_title" msgid="1844314680798145222">"အကြံပြုချက်"</string>
- <string name="productivity_widget_recommendation_category_label" msgid="1722113555721820766">"သင်၏ နေ့စဉ်မရှိမဖြစ်များ"</string>
- <string name="news_widget_recommendation_category_label" msgid="3908242346768119070">"သင့်အတွက် သတင်းများ"</string>
+ <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"မရှိမဖြစ်များ"</string>
+ <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"သတင်းနှင့် မဂ္ဂဇင်းများ"</string>
<string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"သင်အနားယူသောနေရာ"</string>
- <string name="fitness_widget_recommendation_category_label" msgid="2657652999128882431">"သင့်ကြံ့ခိုင်ရေးပန်းတိုင်ဆီ သွားရန်"</string>
- <string name="weather_widget_recommendation_category_label" msgid="6712678763480668598">"မိုးလေဝသကို ကြိုတင်ကာကွယ်ရန်"</string>
- <string name="others_widget_recommendation_category_label" msgid="897876078077284733">"သင်နှစ်သက်နိုင်သောအရာများ"</string>
+ <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"ဖျော်ဖြေရေး"</string>
+ <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"လူမှုရေး"</string>
+ <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"ကျန်းမာကြံ့ခိုင်ရေး"</string>
+ <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"မိုးလေဝသ"</string>
+ <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"သင့်အတွက် အကြံပြုထားသည်များ"</string>
<string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"<xliff:g id="SELECTED_HEADER">%1$s</xliff:g> ဝိဂျက်များသည် ညာဘက်တွင်ရှိပြီး ရှာဖွေမှုနှင့် ရွေးစရာများသည် ဘယ်ဘက်တွင်ရှိသည်"</string>
<string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{ဝိဂျက် # ခု}other{ဝိဂျက် # ခု}}"</string>
<string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{ဖြတ်လမ်းလင့်ခ် # ခု}other{ဖြတ်လမ်းလင့်ခ် # ခု}}"</string>
@@ -61,6 +64,8 @@
<string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"အလုပ်"</string>
<string name="widget_category_conversations" msgid="8894438636213590446">"စကားဝိုင်းများ"</string>
<string name="widget_category_note_taking" msgid="3469689394504266039">"မှတ်စုလိုက်ခြင်း"</string>
+ <string name="widget_add_button_label" msgid="2761267068711937179">"ထည့်ရန်"</string>
+ <string name="widget_add_button_content_description" msgid="1810530016360039643">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> ဝိဂျက်ထည့်ရန်"</string>
<string name="widget_education_header" msgid="4874760613775913787">"အသုံးဝင်သော အချက်အလက်များကို အလွယ်တကူ ရယူလိုက်ပါ"</string>
<string name="widget_education_content" msgid="1731667670753497052">"အက်ပ်မဖွင့်ဘဲ အချက်အလက်များရယူရန် ပင်မစာမျက်နှာတွင် ဝိဂျက်များ ထည့်နိုင်သည်"</string>
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"ဝိဂျက် ဆက်တင်များကို ပြောင်းရန် တို့ပါ"</string>
diff --git a/res/values-nb/strings.xml b/res/values-nb/strings.xml
index 72ac454..f0c947b 100644
--- a/res/values-nb/strings.xml
+++ b/res/values-nb/strings.xml
@@ -38,16 +38,19 @@
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d bredde x %2$d høyde"</string>
<string name="widget_preview_context_description" msgid="9045841361655787574">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g>-modul"</string>
+ <string name="widget_preview_name_and_dims_content_description" msgid="8489038126122831595">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g>-modulen, %2$d bred og %3$d høy"</string>
<string name="add_item_request_drag_hint" msgid="8730547755622776606">"Trykk og hold på modulen for å bevege den rundt på startskjermen"</string>
<string name="add_to_home_screen" msgid="9168649446635919791">"Legg til på startskjermen"</string>
<string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g>-modulen er lagt til på startskjermen"</string>
<string name="suggested_widgets_header_title" msgid="1844314680798145222">"Forslag"</string>
- <string name="productivity_widget_recommendation_category_label" msgid="1722113555721820766">"Viktige apper for dagliglivet"</string>
- <string name="news_widget_recommendation_category_label" msgid="3908242346768119070">"Nyheter for deg"</string>
+ <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Uunnværlige spill"</string>
+ <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"Nyheter og tidsskrifter"</string>
<string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"Avslappingssonen din"</string>
- <string name="fitness_widget_recommendation_category_label" msgid="2657652999128882431">"Nå treningsmålene dine"</string>
- <string name="weather_widget_recommendation_category_label" msgid="6712678763480668598">"Hold deg i forkant av været"</string>
- <string name="others_widget_recommendation_category_label" msgid="897876078077284733">"Kanskje du også liker"</string>
+ <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"Underholdning"</string>
+ <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"Sosialt"</string>
+ <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"Kropp og helse"</string>
+ <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"Været"</string>
+ <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"Foreslått for deg"</string>
<string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"<xliff:g id="SELECTED_HEADER">%1$s</xliff:g> moduler til høyre, søk og alternativer til venstre"</string>
<string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# modul}other{# moduler}}"</string>
<string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# snarvei}other{# snarveier}}"</string>
@@ -61,6 +64,8 @@
<string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Jobb"</string>
<string name="widget_category_conversations" msgid="8894438636213590446">"Samtaler"</string>
<string name="widget_category_note_taking" msgid="3469689394504266039">"Notatskriving"</string>
+ <string name="widget_add_button_label" msgid="2761267068711937179">"Legg til"</string>
+ <string name="widget_add_button_content_description" msgid="1810530016360039643">"Legg til <xliff:g id="WIDGET_NAME">%1$s</xliff:g>-modulen"</string>
<string name="widget_education_header" msgid="4874760613775913787">"Lett tilgjengelig nyttig informasjon"</string>
<string name="widget_education_content" msgid="1731667670753497052">"For å se informasjon uten å åpne apper kan du legge til moduler på startskjermen"</string>
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Trykk for å endre modulinnstillinger"</string>
diff --git a/res/values-ne/strings.xml b/res/values-ne/strings.xml
index d9b4efd..e18dce4 100644
--- a/res/values-ne/strings.xml
+++ b/res/values-ne/strings.xml
@@ -38,16 +38,19 @@
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d चौडाइ गुणा %2$d उचाइ"</string>
<string name="widget_preview_context_description" msgid="9045841361655787574">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> विजेट"</string>
+ <string name="widget_preview_name_and_dims_content_description" msgid="8489038126122831595">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> विजेट, %2$d चौडाइ र %3$d उचाइ"</string>
<string name="add_item_request_drag_hint" msgid="8730547755622776606">"विजेटलाई होम स्क्रिनमा यताउता सार्न त्यसमा टच एन्ड होल्ड गर्नुहोस्"</string>
<string name="add_to_home_screen" msgid="9168649446635919791">"होम स्क्रिनमा राख्नुहोस्"</string>
<string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"होम स्क्रिनमा <xliff:g id="WIDGET_NAME">%1$s</xliff:g> विजेट हालियो"</string>
<string name="suggested_widgets_header_title" msgid="1844314680798145222">"सुझावहरू"</string>
- <string name="productivity_widget_recommendation_category_label" msgid="1722113555721820766">"तपाईंलाई दैनिक आवश्यक पर्ने एपहरू"</string>
- <string name="news_widget_recommendation_category_label" msgid="3908242346768119070">"तपाईंका निम्ति सिफारिस गरिएका समाचार"</string>
+ <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"अत्यावश्यक कुराहरू"</string>
+ <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"समाचार तथा पत्रपत्रिकाहरू"</string>
<string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"तपाईंको Chill Zone"</string>
- <string name="fitness_widget_recommendation_category_label" msgid="2657652999128882431">"आफूले तय गरेको तन्दुरुस्तीको लक्ष्यमा पुग्नुहोस्"</string>
- <string name="weather_widget_recommendation_category_label" msgid="6712678763480668598">"मौसमको पूर्वानुमान प्राप्त गर्नुहोस्"</string>
- <string name="others_widget_recommendation_category_label" msgid="897876078077284733">"तपाईंलाई निम्न कुराहरू पनि मन पर्न सक्छन्"</string>
+ <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"मनोरञ्जन"</string>
+ <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"सोसल मिडिया"</string>
+ <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"स्वास्थ्य तथा तन्दुरुस्ती"</string>
+ <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"मौसम"</string>
+ <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"तपाईंका लागि सिफारिस गरिएका"</string>
<string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"दायाँ भागमा <xliff:g id="SELECTED_HEADER">%1$s</xliff:g> विजेटहरू, बायाँ भागमा खोज र विकल्पहरू"</string>
<string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# विजेट}other{# वटा विजेट}}"</string>
<string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# सर्टकट}other{# वटा सर्टकट}}"</string>
@@ -61,6 +64,8 @@
<string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"कामसम्बन्धी"</string>
<string name="widget_category_conversations" msgid="8894438636213590446">"वार्तालापहरू"</string>
<string name="widget_category_note_taking" msgid="3469689394504266039">"नोट लेख्ने कार्य"</string>
+ <string name="widget_add_button_label" msgid="2761267068711937179">"हाल्नुहोस्"</string>
+ <string name="widget_add_button_content_description" msgid="1810530016360039643">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> विजेट हाल्नुहोस्"</string>
<string name="widget_education_header" msgid="4874760613775913787">"उपयोगी जानकारी सजिलै प्राप्त गर्नुहोस्"</string>
<string name="widget_education_content" msgid="1731667670753497052">"एपहरू नखोलिकनै जानकारी प्राप्त गर्न तपाईं आफ्नो होम स्क्रिनमा विजेटहरू हाल्न सक्नुहुन्छ"</string>
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"विजेटका सेटिङ बदल्न ट्याप गर्नुहोस्"</string>
diff --git a/res/values-night-v31/colors.xml b/res/values-night-v31/colors.xml
index e462ae0..d23f4d1 100644
--- a/res/values-night-v31/colors.xml
+++ b/res/values-night-v31/colors.xml
@@ -46,6 +46,10 @@
@android:color/system_neutral2_200</color>
<color name="widget_picker_collapse_handle_color_dark">
@android:color/system_neutral2_700</color>
+ <color name="widget_picker_add_button_background_color_dark">
+ @android:color/system_accent1_200</color>
+ <color name="widget_picker_add_button_text_color_dark">
+ @android:color/system_accent1_800</color>
<color name="work_fab_bg_color">
@android:color/system_accent1_200</color>
diff --git a/res/values-night/styles.xml b/res/values-night/styles.xml
index d41eb7e..613c2e9 100644
--- a/res/values-night/styles.xml
+++ b/res/values-night/styles.xml
@@ -22,4 +22,10 @@
<item name="widgetsTheme">@style/WidgetContainerTheme.Dark</item>
<item name="android:windowTranslucentStatus">true</item>
</style>
+
+ <style name="WidgetPickerActivityTheme" parent="@android:style/Theme.Translucent.NoTitleBar">
+ <item name="widgetsTheme">@style/WidgetContainerTheme.Dark</item>
+ <item name="android:windowBackground">@android:color/transparent</item>
+ <item name="pageIndicatorDotColor">@color/page_indicator_dot_color_dark</item>
+ </style>
</resources>
diff --git a/res/values-nl/strings.xml b/res/values-nl/strings.xml
index 0e521ef..c16fb3c 100644
--- a/res/values-nl/strings.xml
+++ b/res/values-nl/strings.xml
@@ -38,16 +38,19 @@
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d breed en %2$d hoog"</string>
<string name="widget_preview_context_description" msgid="9045841361655787574">"Widget <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
+ <string name="widget_preview_name_and_dims_content_description" msgid="8489038126122831595">"Widget <xliff:g id="WIDGET_NAME">%1$s</xliff:g>, %2$d breed bij %3$d hoog"</string>
<string name="add_item_request_drag_hint" msgid="8730547755622776606">"Tik op de widget en houd vast om deze te verplaatsen op het startscherm"</string>
<string name="add_to_home_screen" msgid="9168649446635919791">"Toevoegen aan startscherm"</string>
<string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"Widget <xliff:g id="WIDGET_NAME">%1$s</xliff:g> toegevoegd aan startscherm"</string>
<string name="suggested_widgets_header_title" msgid="1844314680798145222">"Suggesties"</string>
- <string name="productivity_widget_recommendation_category_label" msgid="1722113555721820766">"Je dagelijkse essentials"</string>
- <string name="news_widget_recommendation_category_label" msgid="3908242346768119070">"Nieuws voor jou"</string>
+ <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Essentials"</string>
+ <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"Nieuws en tijdschriften"</string>
<string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"Je chillzone"</string>
- <string name="fitness_widget_recommendation_category_label" msgid="2657652999128882431">"Behaal je fitnessdoelen"</string>
- <string name="weather_widget_recommendation_category_label" msgid="6712678763480668598">"Blijf het weer een stap voor"</string>
- <string name="others_widget_recommendation_category_label" msgid="897876078077284733">"Misschien ook interessant"</string>
+ <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"Entertainment"</string>
+ <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"Sociaal"</string>
+ <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"Gezondheid en fitness"</string>
+ <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"Weer"</string>
+ <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"Voorgesteld voor jou"</string>
<string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"<xliff:g id="SELECTED_HEADER">%1$s</xliff:g>-widgets aan de rechterkant, zoeken en opties aan de linkerkant"</string>
<string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# widget}other{# widgets}}"</string>
<string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# snelkoppeling}other{# snelkoppelingen}}"</string>
@@ -61,6 +64,8 @@
<string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Werk"</string>
<string name="widget_category_conversations" msgid="8894438636213590446">"Gesprekken"</string>
<string name="widget_category_note_taking" msgid="3469689394504266039">"Aantekeningen maken"</string>
+ <string name="widget_add_button_label" msgid="2761267068711937179">"Toevoegen"</string>
+ <string name="widget_add_button_content_description" msgid="1810530016360039643">"Widget <xliff:g id="WIDGET_NAME">%1$s</xliff:g> toevoegen"</string>
<string name="widget_education_header" msgid="4874760613775913787">"Nuttige informatie binnen handbereik"</string>
<string name="widget_education_content" msgid="1731667670753497052">"Als je informatie wilt krijgen zonder apps te openen, kun je widgets toevoegen aan je startscherm"</string>
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Tik om de widgetinstellingen te wijzigen"</string>
diff --git a/res/values-or/strings.xml b/res/values-or/strings.xml
index 9b71abb..21b5f03 100644
--- a/res/values-or/strings.xml
+++ b/res/values-or/strings.xml
@@ -38,16 +38,19 @@
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d ଓସାର ଓ %2$d ଉଚ୍ଚ"</string>
<string name="widget_preview_context_description" msgid="9045841361655787574">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> ୱିଜେଟ୍"</string>
+ <string name="widget_preview_name_and_dims_content_description" msgid="8489038126122831595">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> ୱିଜେଟ, %2$d ଓସାର %3$d ଉଚ୍ଚ"</string>
<string name="add_item_request_drag_hint" msgid="8730547755622776606">"ହୋମ ସ୍କ୍ରିନର ଆଖପାଖରେ ୱିଜେଟକୁ ମୁଭ କରିବା ପାଇଁ ଏହାକୁ ସ୍ପର୍ଶ କରି ଧରି ରଖନ୍ତୁ"</string>
<string name="add_to_home_screen" msgid="9168649446635919791">"ହୋମ ସ୍କ୍ରିନରେ ଯୋଗ କରନ୍ତୁ"</string>
<string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g>ର ୱିଜେଟ ହୋମ ସ୍କ୍ରିନରେ ଯୋଡ଼ାଗଲା"</string>
<string name="suggested_widgets_header_title" msgid="1844314680798145222">"ପରାମର୍ଶଗୁଡ଼ିକ"</string>
- <string name="productivity_widget_recommendation_category_label" msgid="1722113555721820766">"ଆପଣଙ୍କ ଦୈନନ୍ଦିନ ଅତ୍ୟାବଶ୍ୟକୀୟ"</string>
- <string name="news_widget_recommendation_category_label" msgid="3908242346768119070">"ଆପଣଙ୍କ ପାଇଁ ନ୍ୟୁଜ"</string>
+ <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"ଅତ୍ୟାବଶ୍ୟକୀୟ"</string>
+ <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"ନ୍ୟୁଜ ଓ ମେଗାଜିନ"</string>
<string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"ଆପଣଙ୍କ ଚିଲ ଜୋନ"</string>
- <string name="fitness_widget_recommendation_category_label" msgid="2657652999128882431">"ଆପଣଙ୍କ ଫିଟନେସ ଲକ୍ଷ୍ୟରେ ପହଞ୍ଚନ୍ତୁ"</string>
- <string name="weather_widget_recommendation_category_label" msgid="6712678763480668598">"ପାଣିପାଗ ବିଷୟରେ ଆଗୁଆ ସୂଚନା ପାଆନ୍ତୁ"</string>
- <string name="others_widget_recommendation_category_label" msgid="897876078077284733">"ଆପଣ ମଧ୍ୟ ପସନ୍ଦ କରିପାରନ୍ତି"</string>
+ <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"ମନୋରଞ୍ଜନ"</string>
+ <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"ସୋସିଆଲ"</string>
+ <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"ସ୍ୱାସ୍ଥ୍ୟ ଓ ଫିଟନେସ"</string>
+ <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"ପାଣିପାଗ"</string>
+ <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"ଆପଣଙ୍କ ପାଇଁ ପ୍ରସ୍ତାବିତ"</string>
<string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"ଡାହାଣରେ <xliff:g id="SELECTED_HEADER">%1$s</xliff:g> ୱିଜେଟଗୁଡ଼ିକ ଅଛି, ବାମରେ ସର୍ଚ୍ଚ ଓ ବିକଳ୍ପଗୁଡ଼ିକ ଅଛି"</string>
<string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# ୱିଜେଟ}other{# ୱିଜେଟ}}"</string>
<string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{#ଟି ସର୍ଟକଟ୍}other{#ଟି ସର୍ଟକଟ୍}}"</string>
@@ -61,6 +64,8 @@
<string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"ୱାର୍କ"</string>
<string name="widget_category_conversations" msgid="8894438636213590446">"ବାର୍ତ୍ତାଳାପଗୁଡ଼ିକ"</string>
<string name="widget_category_note_taking" msgid="3469689394504266039">"ନୋଟ-ଟେକିଂ"</string>
+ <string name="widget_add_button_label" msgid="2761267068711937179">"ଯୋଗ କରନ୍ତୁ"</string>
+ <string name="widget_add_button_content_description" msgid="1810530016360039643">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> ୱିଜେଟ ଯୋଗ କରନ୍ତୁ"</string>
<string name="widget_education_header" msgid="4874760613775913787">"ଉପଯୋଗୀ ସୂଚନା ଆପଣଙ୍କ ପାଖରେ ସହଜରେ ଉପଲବ୍ଧ"</string>
<string name="widget_education_content" msgid="1731667670753497052">"ଆପ୍ସକୁ ନଖୋଲି ସୂଚନା ପାଇବା ପାଇଁ, ଆପଣ ଆପଣଙ୍କ ହୋମ ସ୍କ୍ରିନରେ ୱିଜେଟଗୁଡ଼ିକୁ ଯୋଗ କରିପାରିବେ"</string>
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"ୱିଜେଟ ସେଟିଂସ ପରିବର୍ତ୍ତନ କରିବାକୁ ଟାପ କରନ୍ତୁ"</string>
diff --git a/res/values-pa/strings.xml b/res/values-pa/strings.xml
index 7ed539f..9ae784f 100644
--- a/res/values-pa/strings.xml
+++ b/res/values-pa/strings.xml
@@ -38,16 +38,19 @@
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d ਚੌੜਾਈ ਅਤੇ %2$d ਲੰਬਾਈ"</string>
<string name="widget_preview_context_description" msgid="9045841361655787574">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> ਵਿਜੇਟ"</string>
+ <string name="widget_preview_name_and_dims_content_description" msgid="8489038126122831595">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> ਵਿਜੇਟ, ਇਹ %2$d ਚੌੜਾ ਅਤੇ %3$d ਲੰਬਾ ਹੈ"</string>
<string name="add_item_request_drag_hint" msgid="8730547755622776606">"ਵਿਜੇਟ ਨੂੰ ਹੋਮ ਸਕ੍ਰੀਨ \'ਤੇ ਇੱਧਰ-ਉੱਧਰ ਲਿਜਾਉਣ ਲਈ ਸਪਰਸ਼ ਕਰ ਕੇ ਦਬਾਈ ਰੱਖੋ"</string>
<string name="add_to_home_screen" msgid="9168649446635919791">"ਹੋਮ ਸਕ੍ਰੀਨ \'ਤੇ ਸ਼ਾਮਲ ਕਰੋ"</string>
<string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> ਵਿਜੇਟ ਨੂੰ ਹੋਮ ਸਕ੍ਰੀਨ \'ਤੇ ਸ਼ਾਮਲ ਕੀਤਾ ਗਿਆ"</string>
<string name="suggested_widgets_header_title" msgid="1844314680798145222">"ਸੁਝਾਅ"</string>
- <string name="productivity_widget_recommendation_category_label" msgid="1722113555721820766">"ਤੁਹਾਡੇ ਰੋਜ਼ਾਨਾ ਦੀਆਂ ਲੋੜੀਂਦੀਆਂ ਚੀਜ਼ਾਂ"</string>
- <string name="news_widget_recommendation_category_label" msgid="3908242346768119070">"ਤੁਹਾਡੇ ਲਈ ਖਬਰਾਂ"</string>
+ <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"ਲੋੜੀਂਦੀਆਂ"</string>
+ <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"ਖਬਰਾਂ ਅਤੇ ਰਸਾਲੇ"</string>
<string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"ਤੁਹਾਡੇ ਲਈ ਸਕੂਨਮਈ ਖੇਤਰ"</string>
- <string name="fitness_widget_recommendation_category_label" msgid="2657652999128882431">"ਆਪਣੇ ਫਿੱਟਨੈੱਸ ਸੰਬੰਧੀ ਟੀਚੇ ਹਾਸਲ ਕਰੋ"</string>
- <string name="weather_widget_recommendation_category_label" msgid="6712678763480668598">"ਮੌਸਮ ਬਾਰੇ ਅੱਪ-ਟੂ-ਡੇਟ ਰਹੋ"</string>
- <string name="others_widget_recommendation_category_label" msgid="897876078077284733">"ਸ਼ਾਇਦ ਤੁਸੀਂ ਇਹ ਵੀ ਪਸੰਦ ਕਰੋ"</string>
+ <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"ਮਨੋਰੰਜਨ"</string>
+ <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"ਸੋਸ਼ਲ"</string>
+ <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"ਸਿਹਤ ਅਤੇ ਫਿੱਟਨੈੱਸ"</string>
+ <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"ਮੌਸਮ"</string>
+ <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"ਤੁਹਾਡੇ ਲਈ ਸੁਝਾਅ"</string>
<string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"<xliff:g id="SELECTED_HEADER">%1$s</xliff:g> ਵਿਜੇਟ ਸੱਜੇ ਪਾਸੇ ਹਨ, ਖੋਜ ਵਿਜੇਟ ਅਤੇ ਹੋਰ ਵਿਕਲਪ ਖੱਬੇ ਪਾਸੇ ਹਨ"</string>
<string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# ਵਿਜੇਟ}one{# ਵਿਜੇਟ}other{# ਵਿਜੇਟ}}"</string>
<string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# ਸ਼ਾਰਟਕੱਟ}one{# ਸ਼ਾਰਟਕੱਟ}other{# ਸ਼ਾਰਟਕੱਟ}}"</string>
@@ -61,6 +64,8 @@
<string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"ਕਾਰਜ-ਸਥਾਨ"</string>
<string name="widget_category_conversations" msgid="8894438636213590446">"ਗੱਲਾਂਬਾਤਾਂ"</string>
<string name="widget_category_note_taking" msgid="3469689394504266039">"ਨੋਟ ਬਣਾਉਣਾ"</string>
+ <string name="widget_add_button_label" msgid="2761267068711937179">"ਸ਼ਾਮਲ ਕਰੋ"</string>
+ <string name="widget_add_button_content_description" msgid="1810530016360039643">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> ਵਿਜੇਟ ਸ਼ਾਮਲ ਕਰੋ"</string>
<string name="widget_education_header" msgid="4874760613775913787">"ਮਹੱਤਵਪੂਰਨ ਜਾਣਕਾਰੀ ਤੁਰੰਤ ਪ੍ਰਾਪਤ ਕਰੋ"</string>
<string name="widget_education_content" msgid="1731667670753497052">"ਐਪਾਂ ਨੂੰ ਖੋਲ੍ਹੇ ਬਿਨਾਂ ਜਾਣਕਾਰੀ ਪ੍ਰਾਪਤ ਕਰਨ ਲਈ, ਤੁਸੀਂ ਆਪਣੀ ਹੋਮ ਸਕ੍ਰੀਨ \'ਤੇ ਵਿਜੇਟ ਸ਼ਾਮਲ ਕਰ ਸਕਦੇ ਹੋ"</string>
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"ਵਿਜੇਟ ਸੈਟਿੰਗਾਂ ਨੂੰ ਬਦਲਣ ਲਈ ਟੈਪ ਕਰੋ"</string>
diff --git a/res/values-pl/strings.xml b/res/values-pl/strings.xml
index e382ba9..a765377 100644
--- a/res/values-pl/strings.xml
+++ b/res/values-pl/strings.xml
@@ -38,16 +38,19 @@
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"Szerokość %1$d, wysokość %2$d"</string>
<string name="widget_preview_context_description" msgid="9045841361655787574">"Widżet <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
+ <string name="widget_preview_name_and_dims_content_description" msgid="8489038126122831595">"Widżet <xliff:g id="WIDGET_NAME">%1$s</xliff:g>, %2$d (szerokość), %3$d (wysokość)"</string>
<string name="add_item_request_drag_hint" msgid="8730547755622776606">"Aby poruszać widżetem po ekranie głównym, kliknij go i przytrzymaj"</string>
<string name="add_to_home_screen" msgid="9168649446635919791">"Dodaj do ekranu głównego"</string>
<string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"Widżet <xliff:g id="WIDGET_NAME">%1$s</xliff:g> został dodany do ekranu głównego"</string>
<string name="suggested_widgets_header_title" msgid="1844314680798145222">"Sugestie"</string>
- <string name="productivity_widget_recommendation_category_label" msgid="1722113555721820766">"Niezbędne na co dzień"</string>
- <string name="news_widget_recommendation_category_label" msgid="3908242346768119070">"Wiadomości dla Ciebie"</string>
+ <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Niezbędne"</string>
+ <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"Wiadomości i czasopisma"</string>
<string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"Strefa relaksu"</string>
- <string name="fitness_widget_recommendation_category_label" msgid="2657652999128882431">"Zadbaj o swoją formę"</string>
- <string name="weather_widget_recommendation_category_label" msgid="6712678763480668598">"Nie daj się zaskoczyć pogodzie"</string>
- <string name="others_widget_recommendation_category_label" msgid="897876078077284733">"To też może Cię zainteresować"</string>
+ <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"Rozrywka"</string>
+ <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"Społecznościowe"</string>
+ <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"Zdrowie i fitness"</string>
+ <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"Pogoda"</string>
+ <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"Proponowane dla Ciebie"</string>
<string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"Widżety (<xliff:g id="SELECTED_HEADER">%1$s</xliff:g>) po prawej, wyszukiwanie i opcje po lewej"</string>
<string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# widżet}few{# widżety}many{# widżetów}other{# widżetu}}"</string>
<string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# skrót}few{# skróty}many{# skrótów}other{# skrótu}}"</string>
@@ -61,6 +64,8 @@
<string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Służbowe"</string>
<string name="widget_category_conversations" msgid="8894438636213590446">"Rozmowy"</string>
<string name="widget_category_note_taking" msgid="3469689394504266039">"Notatki"</string>
+ <string name="widget_add_button_label" msgid="2761267068711937179">"Dodaj"</string>
+ <string name="widget_add_button_content_description" msgid="1810530016360039643">"Dodaj widżet <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
<string name="widget_education_header" msgid="4874760613775913787">"Użyteczne informacje w zasięgu ręki"</string>
<string name="widget_education_content" msgid="1731667670753497052">"Możesz dodać widżety do ekranu głównego, aby uzyskiwać informacje bez otwierania aplikacji"</string>
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Kliknij, aby zmienić ustawienia widżetu"</string>
diff --git a/res/values-pt-rPT/strings.xml b/res/values-pt-rPT/strings.xml
index 24f3b06..946ed1b 100644
--- a/res/values-pt-rPT/strings.xml
+++ b/res/values-pt-rPT/strings.xml
@@ -38,16 +38,19 @@
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d de largura por %2$d de altura"</string>
<string name="widget_preview_context_description" msgid="9045841361655787574">"Widget <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
+ <string name="widget_preview_name_and_dims_content_description" msgid="8489038126122831595">"Widget <xliff:g id="WIDGET_NAME">%1$s</xliff:g>, %2$d de largura por %3$d de altura"</string>
<string name="add_item_request_drag_hint" msgid="8730547755622776606">"Toque sem soltar no widget para o mover no ecrã principal"</string>
<string name="add_to_home_screen" msgid="9168649446635919791">"Adicionar ao ecrã principal"</string>
<string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"Widget <xliff:g id="WIDGET_NAME">%1$s</xliff:g> adicionado ao ecrã principal"</string>
<string name="suggested_widgets_header_title" msgid="1844314680798145222">"Sugestões"</string>
- <string name="productivity_widget_recommendation_category_label" msgid="1722113555721820766">"Os seus essenciais do dia a dia"</string>
- <string name="news_widget_recommendation_category_label" msgid="3908242346768119070">"Notícias para si"</string>
+ <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Essenciais"</string>
+ <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"Notícias e revistas"</string>
<string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"A sua zona de relaxamento"</string>
- <string name="fitness_widget_recommendation_category_label" msgid="2657652999128882431">"Atingir os seus objetivos de fitness"</string>
- <string name="weather_widget_recommendation_category_label" msgid="6712678763480668598">"Ficar a par da meteorologia"</string>
- <string name="others_widget_recommendation_category_label" msgid="897876078077284733">"Também poderá gostar de"</string>
+ <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"Entretenimento"</string>
+ <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"Redes sociais"</string>
+ <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"Saúde e fitness"</string>
+ <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"Meteorologia"</string>
+ <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"Sugerido para si"</string>
<string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"Widgets de <xliff:g id="SELECTED_HEADER">%1$s</xliff:g> à direita, pesquisa e opções à esquerda"</string>
<string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# widget}other{# widgets}}"</string>
<string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# atalho}other{# atalhos}}"</string>
@@ -61,6 +64,8 @@
<string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Trabalho"</string>
<string name="widget_category_conversations" msgid="8894438636213590446">"Conversas"</string>
<string name="widget_category_note_taking" msgid="3469689394504266039">"Tomar notas"</string>
+ <string name="widget_add_button_label" msgid="2761267068711937179">"Adicionar"</string>
+ <string name="widget_add_button_content_description" msgid="1810530016360039643">"Adicione o widget <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
<string name="widget_education_header" msgid="4874760613775913787">"Informações úteis à sua disposição"</string>
<string name="widget_education_content" msgid="1731667670753497052">"Para obter informações sem abrir apps, pode adicionar widgets ao seu ecrã principal"</string>
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Toque para alterar as definições do widget"</string>
diff --git a/res/values-pt/strings.xml b/res/values-pt/strings.xml
index 10111aa..04688d1 100644
--- a/res/values-pt/strings.xml
+++ b/res/values-pt/strings.xml
@@ -38,16 +38,19 @@
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d de largura por %2$d de altura"</string>
<string name="widget_preview_context_description" msgid="9045841361655787574">"Widget <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
+ <string name="widget_preview_name_and_dims_content_description" msgid="8489038126122831595">"Widget <xliff:g id="WIDGET_NAME">%1$s</xliff:g>: %2$d de largura por %3$d de altura"</string>
<string name="add_item_request_drag_hint" msgid="8730547755622776606">"Toque no widget e o pressione para definir a posição dele na tela inicial"</string>
<string name="add_to_home_screen" msgid="9168649446635919791">"Adicionar à tela inicial"</string>
<string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"Widget <xliff:g id="WIDGET_NAME">%1$s</xliff:g> adicionado à tela inicial"</string>
<string name="suggested_widgets_header_title" msgid="1844314680798145222">"Sugestões"</string>
- <string name="productivity_widget_recommendation_category_label" msgid="1722113555721820766">"Seus itens diários essenciais"</string>
- <string name="news_widget_recommendation_category_label" msgid="3908242346768119070">"Notícias para você"</string>
+ <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Essenciais"</string>
+ <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"Notícias e revistas"</string>
<string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"Sua zona de relaxamento"</string>
- <string name="fitness_widget_recommendation_category_label" msgid="2657652999128882431">"Alcance seus objetivos fitness"</string>
- <string name="weather_widget_recommendation_category_label" msgid="6712678763480668598">"Fique por dentro da previsão do tempo"</string>
- <string name="others_widget_recommendation_category_label" msgid="897876078077284733">"Você também pode gostar de"</string>
+ <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"Entretenimento"</string>
+ <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"Social"</string>
+ <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"Saúde e bem-estar"</string>
+ <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"Clima"</string>
+ <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"Sugestões para você"</string>
<string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"Widgets da <xliff:g id="SELECTED_HEADER">%1$s</xliff:g> à direita, pesquisa e opções à esquerda"</string>
<string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# widget}one{# widget}other{# widgets}}"</string>
<string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# atalho}one{# atalho}other{# atalhos}}"</string>
@@ -61,6 +64,8 @@
<string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Trabalho"</string>
<string name="widget_category_conversations" msgid="8894438636213590446">"Conversas"</string>
<string name="widget_category_note_taking" msgid="3469689394504266039">"Anotações"</string>
+ <string name="widget_add_button_label" msgid="2761267068711937179">"Adicionar"</string>
+ <string name="widget_add_button_content_description" msgid="1810530016360039643">"Adicionar o widget <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
<string name="widget_education_header" msgid="4874760613775913787">"Informações úteis ao seu alcance"</string>
<string name="widget_education_content" msgid="1731667670753497052">"Para acessar informações sem precisar abrir os apps, adicione widgets à sua tela inicial"</string>
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Toque para mudar as configurações do widget"</string>
diff --git a/res/values-ro/strings.xml b/res/values-ro/strings.xml
index 9eab793..4605871 100644
--- a/res/values-ro/strings.xml
+++ b/res/values-ro/strings.xml
@@ -38,16 +38,19 @@
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d lățime și %2$d înălțime"</string>
<string name="widget_preview_context_description" msgid="9045841361655787574">"Widgetul <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
+ <string name="widget_preview_name_and_dims_content_description" msgid="8489038126122831595">"Widgetul <xliff:g id="WIDGET_NAME">%1$s</xliff:g>, %2$d lățime x %3$d înălțime"</string>
<string name="add_item_request_drag_hint" msgid="8730547755622776606">"Atinge lung widgetul pentru a-l muta pe ecranul de pornire"</string>
<string name="add_to_home_screen" msgid="9168649446635919791">"Adaugă pe ecranul de pornire"</string>
<string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"Widgetul <xliff:g id="WIDGET_NAME">%1$s</xliff:g> a fost adăugat pe ecranul de pornire"</string>
<string name="suggested_widgets_header_title" msgid="1844314680798145222">"Sugestii"</string>
- <string name="productivity_widget_recommendation_category_label" msgid="1722113555721820766">"Instrumente esențiale de zi cu zi"</string>
- <string name="news_widget_recommendation_category_label" msgid="3908242346768119070">"Știri pentru tine"</string>
+ <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Esențiale"</string>
+ <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"Știri și reviste"</string>
<string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"Zona de relaxare"</string>
- <string name="fitness_widget_recommendation_category_label" msgid="2657652999128882431">"Atinge-ți obiectivele de fitness"</string>
- <string name="weather_widget_recommendation_category_label" msgid="6712678763480668598">"Fii la curent cu prognoza meteo"</string>
- <string name="others_widget_recommendation_category_label" msgid="897876078077284733">"S-ar putea să îți placă și"</string>
+ <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"Divertisment"</string>
+ <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"Rețele sociale"</string>
+ <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"Sănătate și fitness"</string>
+ <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"Meteo"</string>
+ <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"Sugerate pentru tine"</string>
<string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"Widgeturi pentru <xliff:g id="SELECTED_HEADER">%1$s</xliff:g> în dreapta, căutare și opțiuni în stânga"</string>
<string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# widget}few{# widgeturi}other{# de widgeturi}}"</string>
<string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# comandă rapidă}few{# comenzi rapide}other{# de comenzi rapide}}"</string>
@@ -61,6 +64,8 @@
<string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Serviciu"</string>
<string name="widget_category_conversations" msgid="8894438636213590446">"Conversații"</string>
<string name="widget_category_note_taking" msgid="3469689394504266039">"Luare de notițe"</string>
+ <string name="widget_add_button_label" msgid="2761267068711937179">"Adaugă"</string>
+ <string name="widget_add_button_content_description" msgid="1810530016360039643">"Adaugă widgetul <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
<string name="widget_education_header" msgid="4874760613775913787">"Informații utile la îndemâna ta"</string>
<string name="widget_education_content" msgid="1731667670753497052">"Pentru a primi informații fără să deschizi aplicațiile, poți adăuga widgeturi pe ecranul de pornire"</string>
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Atinge ca să schimbi setările pentru widgeturi"</string>
diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml
index 1e8407a..9a40b84 100644
--- a/res/values-ru/strings.xml
+++ b/res/values-ru/strings.xml
@@ -38,16 +38,19 @@
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d x %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"Ширина %1$d, высота %2$d"</string>
<string name="widget_preview_context_description" msgid="9045841361655787574">"Виджет \"<xliff:g id="WIDGET_NAME">%1$s</xliff:g>\""</string>
+ <string name="widget_preview_name_and_dims_content_description" msgid="8489038126122831595">"Виджет \"<xliff:g id="WIDGET_NAME">%1$s</xliff:g>\", ширина: %2$d, высота: %3$d"</string>
<string name="add_item_request_drag_hint" msgid="8730547755622776606">"Нажмите на виджет и удерживайте его, чтобы переместить в нужное место на главном экране."</string>
<string name="add_to_home_screen" msgid="9168649446635919791">"Добавить на главный экран"</string>
<string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"Виджет \"<xliff:g id="WIDGET_NAME">%1$s</xliff:g>\" добавлен на главный экран"</string>
<string name="suggested_widgets_header_title" msgid="1844314680798145222">"Подсказки"</string>
- <string name="productivity_widget_recommendation_category_label" msgid="1722113555721820766">"Самое важное"</string>
- <string name="news_widget_recommendation_category_label" msgid="3908242346768119070">"Новости для вас"</string>
+ <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Главное"</string>
+ <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"Новости и журналы"</string>
<string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"Развлечение и общение"</string>
- <string name="fitness_widget_recommendation_category_label" msgid="2657652999128882431">"Ваши фитнес-цели"</string>
- <string name="weather_widget_recommendation_category_label" msgid="6712678763480668598">"Прогноз погоды"</string>
- <string name="others_widget_recommendation_category_label" msgid="897876078077284733">"Другие рекомендации"</string>
+ <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"Развлечения"</string>
+ <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"Общение"</string>
+ <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"Здоровье и спорт"</string>
+ <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"Погода"</string>
+ <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"Рекомендации"</string>
<string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"Виджеты приложения \"<xliff:g id="SELECTED_HEADER">%1$s</xliff:g>\" находятся справа, а панель поиска и настройки – слева"</string>
<string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# виджет}one{# виджет}few{# виджета}many{# виджетов}other{# виджета}}"</string>
<string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# ярлык}one{# ярлык}few{# ярлыка}many{# ярлыков}other{# ярлыка}}"</string>
@@ -61,6 +64,8 @@
<string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Рабочие виджеты"</string>
<string name="widget_category_conversations" msgid="8894438636213590446">"Разговоры"</string>
<string name="widget_category_note_taking" msgid="3469689394504266039">"Создание заметок"</string>
+ <string name="widget_add_button_label" msgid="2761267068711937179">"Добавить"</string>
+ <string name="widget_add_button_content_description" msgid="1810530016360039643">"Добавить виджет \"<xliff:g id="WIDGET_NAME">%1$s</xliff:g>\""</string>
<string name="widget_education_header" msgid="4874760613775913787">"Вся нужная информация перед глазами"</string>
<string name="widget_education_content" msgid="1731667670753497052">"Чтобы не открывать приложения каждый раз, когда нужна информация, добавьте виджеты на главный экран."</string>
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Нажмите, чтобы изменить настройки виджета"</string>
diff --git a/res/values-si/strings.xml b/res/values-si/strings.xml
index ad5d158..8780527 100644
--- a/res/values-si/strings.xml
+++ b/res/values-si/strings.xml
@@ -38,16 +38,19 @@
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"පළල %1$d උස %2$d"</string>
<string name="widget_preview_context_description" msgid="9045841361655787574">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> විජට්ටුව"</string>
+ <string name="widget_preview_name_and_dims_content_description" msgid="8489038126122831595">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> විජට්ටුව, %2$d පළල සහ උස %3$d"</string>
<string name="add_item_request_drag_hint" msgid="8730547755622776606">"විජට් එක මුල් පිටු තිරය වටා ගෙන යාමට විජට් එක ස්පර්ශ කර අල්ලාගෙන සිටින්න"</string>
<string name="add_to_home_screen" msgid="9168649446635919791">"මුල් තිරය වෙත එක් කරන්න"</string>
<string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> විජට්ටුව මුල් පිටු තිරය වෙත එක් කරන ලදි"</string>
<string name="suggested_widgets_header_title" msgid="1844314680798145222">"යෝජනා"</string>
- <string name="productivity_widget_recommendation_category_label" msgid="1722113555721820766">"ඔබේ දෛනික අත්යවශ්යාංග"</string>
- <string name="news_widget_recommendation_category_label" msgid="3908242346768119070">"ඔබ වෙනුවෙන් පුවත්"</string>
+ <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"අත්යවශ්යාංග"</string>
+ <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"පුවත් සහ සඟරා"</string>
<string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"ඔබේ නිවුණු කලාපය"</string>
- <string name="fitness_widget_recommendation_category_label" msgid="2657652999128882431">"ඔබේ යෝග්යතා ඉලක්ක ළඟා කර ගන්න"</string>
- <string name="weather_widget_recommendation_category_label" msgid="6712678763480668598">"කාලගුණයට ඉදිරියෙන් සිටින්න"</string>
- <string name="others_widget_recommendation_category_label" msgid="897876078077284733">"ඔබ මේවාට ද කැමති විය හැක"</string>
+ <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"විනෝදාස්වාදය"</string>
+ <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"සමාජයීය"</string>
+ <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"සෞඛ්යය සහ යෝග්යතාව"</string>
+ <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"කාලගුණ"</string>
+ <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"ඔබ සඳහා යෝජිත"</string>
<string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"දකුණේ <xliff:g id="SELECTED_HEADER">%1$s</xliff:g> විජට්, වමේ සෙවීම සහ විකල්ප"</string>
<string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{විජට් #}one{විජට් #}other{විජට් #}}"</string>
<string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{කෙටි මං #}one{කෙටි මං #}other{කෙටි මං #}}"</string>
@@ -61,6 +64,8 @@
<string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"කාර්යාලය"</string>
<string name="widget_category_conversations" msgid="8894438636213590446">"සංවාද"</string>
<string name="widget_category_note_taking" msgid="3469689394504266039">"සටහන් කර ගැනීම"</string>
+ <string name="widget_add_button_label" msgid="2761267068711937179">"එක් කරන්න"</string>
+ <string name="widget_add_button_content_description" msgid="1810530016360039643">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> විජට්ටුව එක් කරන්න"</string>
<string name="widget_education_header" msgid="4874760613775913787">"ප්රයෝජනවත් තොරතුරු ඔබගේ ඇඟිලි තුඩු අග"</string>
<string name="widget_education_content" msgid="1731667670753497052">"යෙදුම් විවෘත නොකර තොරතුරු ලබා ගැනීම සඳහා, ඔබට ඔබගේ මුල් තිරයට විජට් එක් කළ හැකිය"</string>
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"විජට් සැකසීම් වෙනස් කිරීමට තට්ටු කරන්න"</string>
diff --git a/res/values-sk/strings.xml b/res/values-sk/strings.xml
index b4b67a1..e513497 100644
--- a/res/values-sk/strings.xml
+++ b/res/values-sk/strings.xml
@@ -38,16 +38,19 @@
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"šírka %1$d, výška %2$d"</string>
<string name="widget_preview_context_description" msgid="9045841361655787574">"Miniaplikácia <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
+ <string name="widget_preview_name_and_dims_content_description" msgid="8489038126122831595">"Miniaplikácia <xliff:g id="WIDGET_NAME">%1$s</xliff:g>, %2$d na šírku, %3$d na výšku"</string>
<string name="add_item_request_drag_hint" msgid="8730547755622776606">"Pridržaním môžete miniaplikáciu posúvať po ploche"</string>
<string name="add_to_home_screen" msgid="9168649446635919791">"Pridať na plochu"</string>
<string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"Na plochu bola pridaná miniaplikácia <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
<string name="suggested_widgets_header_title" msgid="1844314680798145222">"Návrhy"</string>
- <string name="productivity_widget_recommendation_category_label" msgid="1722113555721820766">"Vaše každodenné základné položky"</string>
- <string name="news_widget_recommendation_category_label" msgid="3908242346768119070">"Vaše správy"</string>
+ <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Všetko dôležité"</string>
+ <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"Noviny a časopisy"</string>
<string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"Vaša komfortná zóna"</string>
- <string name="fitness_widget_recommendation_category_label" msgid="2657652999128882431">"Dosiahnite svoje kondičné ciele"</string>
- <string name="weather_widget_recommendation_category_label" msgid="6712678763480668598">"Získavajte informácie o počasí v predstihu"</string>
- <string name="others_widget_recommendation_category_label" msgid="897876078077284733">"Mohlo by sa vám páčiť"</string>
+ <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"Zábava"</string>
+ <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"Sociálne siete"</string>
+ <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"Zdravie a kondícia"</string>
+ <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"Počasie"</string>
+ <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"Navrhnuté pre vás"</string>
<string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"Miniaplikácie <xliff:g id="SELECTED_HEADER">%1$s</xliff:g> vpravo, vyhľadávanie a možnosti vľavo"</string>
<string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# miniaplikácia}few{# miniaplikácie}many{# widgets}other{# miniaplikácií}}"</string>
<string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# odkaz}few{# odkazy}many{# shortcuts}other{# odkazov}}"</string>
@@ -61,6 +64,8 @@
<string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Práca"</string>
<string name="widget_category_conversations" msgid="8894438636213590446">"Konverzácie"</string>
<string name="widget_category_note_taking" msgid="3469689394504266039">"Zapisovanie poznámok"</string>
+ <string name="widget_add_button_label" msgid="2761267068711937179">"Pridať"</string>
+ <string name="widget_add_button_content_description" msgid="1810530016360039643">"Pridať miniaplikáciu <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
<string name="widget_education_header" msgid="4874760613775913787">"Užitočné informácie poruke"</string>
<string name="widget_education_content" msgid="1731667670753497052">"Ak chcete získavať informácie bez otvárania aplikácií, môžete si na plochu pridať miniaplikácie"</string>
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Klepnutím zmeňte nastavenia miniaplikácie"</string>
diff --git a/res/values-sl/strings.xml b/res/values-sl/strings.xml
index 9472e44..70a4c08 100644
--- a/res/values-sl/strings.xml
+++ b/res/values-sl/strings.xml
@@ -38,16 +38,19 @@
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"Širina %1$d, višina %2$d"</string>
<string name="widget_preview_context_description" msgid="9045841361655787574">"Pripomoček <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
+ <string name="widget_preview_name_and_dims_content_description" msgid="8489038126122831595">"Pripomoček »<xliff:g id="WIDGET_NAME">%1$s</xliff:g>«, širina: %2$d, višina: %3$d"</string>
<string name="add_item_request_drag_hint" msgid="8730547755622776606">"Dotaknite se pripomočka in ga pridržite, če ga želite premikati po začetnem zaslonu."</string>
<string name="add_to_home_screen" msgid="9168649446635919791">"Dodaj na začetni zaslon"</string>
<string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"Pripomoček »<xliff:g id="WIDGET_NAME">%1$s</xliff:g>« je dodan na začetni zaslon."</string>
<string name="suggested_widgets_header_title" msgid="1844314680798145222">"Predlogi"</string>
- <string name="productivity_widget_recommendation_category_label" msgid="1722113555721820766">"Vaše dnevne potrebščine"</string>
- <string name="news_widget_recommendation_category_label" msgid="3908242346768119070">"Novice za vas"</string>
+ <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Nepogrešljivo"</string>
+ <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"Novice in revije"</string>
<string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"Vaš kotiček za sprostitev"</string>
- <string name="fitness_widget_recommendation_category_label" msgid="2657652999128882431">"Dosegajte cilje glede telesne pripravljenosti"</string>
- <string name="weather_widget_recommendation_category_label" msgid="6712678763480668598">"Bodite na tekočem z vremenom"</string>
- <string name="others_widget_recommendation_category_label" msgid="897876078077284733">"Morda vam bo všeč tudi"</string>
+ <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"Razvedrilo"</string>
+ <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"Družbeno"</string>
+ <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"Zdravje in fitnes"</string>
+ <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"Vreme"</string>
+ <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"Predlagano za vas"</string>
<string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"Pripomočki <xliff:g id="SELECTED_HEADER">%1$s</xliff:g> na desni, iskanje in možnosti na levi"</string>
<string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# pripomoček}one{# pripomoček}two{# pripomočka}few{# pripomočki}other{# pripomočkov}}"</string>
<string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# bližnjica}one{# bližnjica}two{# bližnjici}few{# bližnjice}other{# bližnjic}}"</string>
@@ -61,6 +64,8 @@
<string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Služba"</string>
<string name="widget_category_conversations" msgid="8894438636213590446">"Pogovori"</string>
<string name="widget_category_note_taking" msgid="3469689394504266039">"Ustvarjanje zapiskov"</string>
+ <string name="widget_add_button_label" msgid="2761267068711937179">"Dodaj"</string>
+ <string name="widget_add_button_content_description" msgid="1810530016360039643">"Dodajanje pripomočka »<xliff:g id="WIDGET_NAME">%1$s</xliff:g>«"</string>
<string name="widget_education_header" msgid="4874760613775913787">"Koristne informacije na dosegu prstov"</string>
<string name="widget_education_content" msgid="1731667670753497052">"Če si želite podatke ogledati brez odpiranja aplikacij, lahko na začetni zaslon dodate pripomočke."</string>
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Dotaknite se, če želite spremeniti nastavitve pripomočka."</string>
diff --git a/res/values-sq/strings.xml b/res/values-sq/strings.xml
index bfc6fb3..51fcf13 100644
--- a/res/values-sq/strings.xml
+++ b/res/values-sq/strings.xml
@@ -38,16 +38,19 @@
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d i gjerë me %2$d i lartë"</string>
<string name="widget_preview_context_description" msgid="9045841361655787574">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> miniaplikacion"</string>
+ <string name="widget_preview_name_and_dims_content_description" msgid="8489038126122831595">"Miniaplikacioni <xliff:g id="WIDGET_NAME">%1$s</xliff:g>, me gjerësi %2$d dhe lartësi %3$d"</string>
<string name="add_item_request_drag_hint" msgid="8730547755622776606">"Prek dhe mbaj të shtypur miniaplikacionin për ta lëvizur atë nëpër ekranin bazë"</string>
<string name="add_to_home_screen" msgid="9168649446635919791">"Shto në ekranin bazë"</string>
<string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"Miniaplikacioni <xliff:g id="WIDGET_NAME">%1$s</xliff:g> u shtua në ekranin bazë"</string>
<string name="suggested_widgets_header_title" msgid="1844314680798145222">"Sugjerime"</string>
- <string name="productivity_widget_recommendation_category_label" msgid="1722113555721820766">"Aplikacionet thelbësore të përditshme"</string>
- <string name="news_widget_recommendation_category_label" msgid="3908242346768119070">"Lajme për ty"</string>
+ <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Thelbësoret"</string>
+ <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"Lajme dhe revista"</string>
<string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"Zona jote e qetësisë"</string>
- <string name="fitness_widget_recommendation_category_label" msgid="2657652999128882431">"Realizo objektivat e stërvitjes"</string>
- <string name="weather_widget_recommendation_category_label" msgid="6712678763480668598">"Qëndro i informuar për motin"</string>
- <string name="others_widget_recommendation_category_label" msgid="897876078077284733">"Gjithashtu mund të të pëlqejë"</string>
+ <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"Argëtim"</string>
+ <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"Rrjetet sociale"</string>
+ <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"Shëndet dhe fitnes"</string>
+ <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"Moti"</string>
+ <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"Sugjeruar për ty"</string>
<string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"Miniaplikacionet e <xliff:g id="SELECTED_HEADER">%1$s</xliff:g> në të djathtë, kërkimi dhe opsionet në të majtë"</string>
<string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# miniaplikacion}other{# miniaplikacione}}"</string>
<string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# shkurtore}other{# shkurtore}}"</string>
@@ -61,6 +64,8 @@
<string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Puna"</string>
<string name="widget_category_conversations" msgid="8894438636213590446">"Bisedat"</string>
<string name="widget_category_note_taking" msgid="3469689394504266039">"Mbajtja e shënimeve"</string>
+ <string name="widget_add_button_label" msgid="2761267068711937179">"Shto"</string>
+ <string name="widget_add_button_content_description" msgid="1810530016360039643">"Shto miniaplikacionin <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
<string name="widget_education_header" msgid="4874760613775913787">"Informacione të dobishme në majë të gishtave të tu"</string>
<string name="widget_education_content" msgid="1731667670753497052">"Për të marrë informacione pa i hapur aplikacionet, mund të shtosh miniaplikacione në ekranin bazë"</string>
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Trokit për të ndryshuar cilësimet e miniaplikacionit"</string>
diff --git a/res/values-sr/strings.xml b/res/values-sr/strings.xml
index 270654d..7a32a54 100644
--- a/res/values-sr/strings.xml
+++ b/res/values-sr/strings.xml
@@ -38,16 +38,19 @@
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d×%2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"ширина од %1$d и висина од %2$d"</string>
<string name="widget_preview_context_description" msgid="9045841361655787574">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> виџет"</string>
+ <string name="widget_preview_name_and_dims_content_description" msgid="8489038126122831595">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> виџет, ширина %2$d и висина %3$d"</string>
<string name="add_item_request_drag_hint" msgid="8730547755622776606">"Додирните и задржите виџет да бисте га померали по почетном екрану"</string>
<string name="add_to_home_screen" msgid="9168649446635919791">"Додај на почетни екран"</string>
<string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"Додали сте виџет <xliff:g id="WIDGET_NAME">%1$s</xliff:g> на почетни екран"</string>
<string name="suggested_widgets_header_title" msgid="1844314680798145222">"Предлози"</string>
- <string name="productivity_widget_recommendation_category_label" msgid="1722113555721820766">"Основни виџети за сваки дан"</string>
- <string name="news_widget_recommendation_category_label" msgid="3908242346768119070">"Вести за вас"</string>
+ <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Основне апликације"</string>
+ <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"Новости и часописи"</string>
<string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"Зона за опуштање"</string>
- <string name="fitness_widget_recommendation_category_label" msgid="2657652999128882431">"Остварите фитнес циљеве"</string>
- <string name="weather_widget_recommendation_category_label" msgid="6712678763480668598">"Будите у току са временским приликама"</string>
- <string name="others_widget_recommendation_category_label" msgid="897876078077284733">"Можда ће вам се допасти и"</string>
+ <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"Забава"</string>
+ <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"Друштвене мреже"</string>
+ <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"Здравље и фитнес"</string>
+ <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"Време"</string>
+ <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"Предложено за вас"</string>
<string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"Виџети <xliff:g id="SELECTED_HEADER">%1$s</xliff:g> са десне стране, претрага и опције са леве стране"</string>
<string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# виџет}one{# виџет}few{# виџета}other{# виџета}}"</string>
<string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# пречица}one{# пречица}few{# пречице}other{# пречица}}"</string>
@@ -61,6 +64,8 @@
<string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Посао"</string>
<string name="widget_category_conversations" msgid="8894438636213590446">"Конверзације"</string>
<string name="widget_category_note_taking" msgid="3469689394504266039">"Прављење бележака"</string>
+ <string name="widget_add_button_label" msgid="2761267068711937179">"Додај"</string>
+ <string name="widget_add_button_content_description" msgid="1810530016360039643">"Додајте виџет <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
<string name="widget_education_header" msgid="4874760613775913787">"Корисне информације надохват руке"</string>
<string name="widget_education_content" msgid="1731667670753497052">"Да бисте пронашли информације без отварања апликација, можете да додате виџете на почетни екран"</string>
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Додирните да бисте променили подешавања виџета"</string>
diff --git a/res/values-sv/strings.xml b/res/values-sv/strings.xml
index 32c52d8..df2539f 100644
--- a/res/values-sv/strings.xml
+++ b/res/values-sv/strings.xml
@@ -38,16 +38,19 @@
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d bred gånger %2$d hög"</string>
<string name="widget_preview_context_description" msgid="9045841361655787574">"Widget för <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
+ <string name="widget_preview_name_and_dims_content_description" msgid="8489038126122831595">"Widgeten <xliff:g id="WIDGET_NAME">%1$s</xliff:g>, %2$d i bredd och %3$d i höjd"</string>
<string name="add_item_request_drag_hint" msgid="8730547755622776606">"Tryck länge på widgeten om du vill flytta den på startskärmen"</string>
<string name="add_to_home_screen" msgid="9168649446635919791">"Lägg till på startskärmen"</string>
<string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"Widget för <xliff:g id="WIDGET_NAME">%1$s</xliff:g> har lagts till på startskärmen"</string>
<string name="suggested_widgets_header_title" msgid="1844314680798145222">"Förslag"</string>
- <string name="productivity_widget_recommendation_category_label" msgid="1722113555721820766">"Det viktigaste i vardagen"</string>
- <string name="news_widget_recommendation_category_label" msgid="3908242346768119070">"Nyheter för dig"</string>
+ <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Favoriter"</string>
+ <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"Nyheter och tidskrifter"</string>
<string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"Koppla av"</string>
- <string name="fitness_widget_recommendation_category_label" msgid="2657652999128882431">"Nå dina träningsmål"</string>
- <string name="weather_widget_recommendation_category_label" msgid="6712678763480668598">"Håll koll på vädret"</string>
- <string name="others_widget_recommendation_category_label" msgid="897876078077284733">"Andra appar du kanske gillar"</string>
+ <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"Underhållning"</string>
+ <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"Socialt"</string>
+ <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"Hälsa och träning"</string>
+ <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"Väder"</string>
+ <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"Våra förslag"</string>
<string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"Widgetar för <xliff:g id="SELECTED_HEADER">%1$s</xliff:g> till höger, sökning och alternativ till vänster"</string>
<string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# widget}other{# widgetar}}"</string>
<string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# genväg}other{# genvägar}}"</string>
@@ -61,6 +64,8 @@
<string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Arbete"</string>
<string name="widget_category_conversations" msgid="8894438636213590446">"Konversationer"</string>
<string name="widget_category_note_taking" msgid="3469689394504266039">"Anteckna"</string>
+ <string name="widget_add_button_label" msgid="2761267068711937179">"Lägg till"</string>
+ <string name="widget_add_button_content_description" msgid="1810530016360039643">"Lägg till widgeten <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
<string name="widget_education_header" msgid="4874760613775913787">"Användbar information nära till hands"</string>
<string name="widget_education_content" msgid="1731667670753497052">"Om du vill ha information utan att öppna appar kan du lägga till widgetar på startskärmen"</string>
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Tryck för att ändra inställningarna för widgeten"</string>
diff --git a/res/values-sw/strings.xml b/res/values-sw/strings.xml
index 193c92e..4d3b22f 100644
--- a/res/values-sw/strings.xml
+++ b/res/values-sw/strings.xml
@@ -38,16 +38,19 @@
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"Upana wa %1$d na kimo cha %2$d"</string>
<string name="widget_preview_context_description" msgid="9045841361655787574">"Wijeti ya <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
+ <string name="widget_preview_name_and_dims_content_description" msgid="8489038126122831595">"Wijeti ya <xliff:g id="WIDGET_NAME">%1$s</xliff:g>, upana wa %2$d kwa urefu wa %3$d"</string>
<string name="add_item_request_drag_hint" msgid="8730547755622776606">"Gusa na ushikilie wijeti ili uisogeze kwenye skrini ya kwanza"</string>
<string name="add_to_home_screen" msgid="9168649446635919791">"Weka kwenye skrini ya kwanza"</string>
<string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"Umeongeza wijeti ya <xliff:g id="WIDGET_NAME">%1$s</xliff:g> kwenye skrini ya kwanza"</string>
<string name="suggested_widgets_header_title" msgid="1844314680798145222">"Mapendekezo"</string>
- <string name="productivity_widget_recommendation_category_label" msgid="1722113555721820766">"Hati Zako Muhimu za Kila Siku"</string>
- <string name="news_widget_recommendation_category_label" msgid="3908242346768119070">"Habari Kwa Ajili Yako"</string>
+ <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Essentials"</string>
+ <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"Habari na magazeti"</string>
<string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"Mahali Pako pa Kupumzika"</string>
- <string name="fitness_widget_recommendation_category_label" msgid="2657652999128882431">"Fikia Malengo Yako ya Siha"</string>
- <string name="weather_widget_recommendation_category_label" msgid="6712678763480668598">"Pata Taarifa kuhusu Hali ya Hewa"</string>
- <string name="others_widget_recommendation_category_label" msgid="897876078077284733">"Huenda Pia Ukapenda"</string>
+ <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"Burudani"</string>
+ <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"Mitandao jamii"</string>
+ <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"Afya na siha"</string>
+ <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"Hali ya Hewa"</string>
+ <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"Unayopendekezewa"</string>
<string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"Wijeti za <xliff:g id="SELECTED_HEADER">%1$s</xliff:g> ziko upande wa kulia, utafutaji na chaguo ziko upande wa kushoto"</string>
<string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{Wijeti #}other{Wijeti #}}"</string>
<string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{Njia # ya mkato}other{Njia # za mkato}}"</string>
@@ -61,6 +64,8 @@
<string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Kazini"</string>
<string name="widget_category_conversations" msgid="8894438636213590446">"Mazungumzo"</string>
<string name="widget_category_note_taking" msgid="3469689394504266039">"Kuandika madokezo"</string>
+ <string name="widget_add_button_label" msgid="2761267068711937179">"Weka"</string>
+ <string name="widget_add_button_content_description" msgid="1810530016360039643">"Weka wijeti ya <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
<string name="widget_education_header" msgid="4874760613775913787">"Maelezo muhimu, popote ulipo"</string>
<string name="widget_education_content" msgid="1731667670753497052">"Ili upate maelezo bila kufungua programu, unaweza kuweka wijeti kwenye skrini yako ya kwanza"</string>
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Gusa ili ubadilishe mipangilio ya wijeti"</string>
diff --git a/res/values-sw720dp/dimens.xml b/res/values-sw720dp/dimens.xml
index 3c79588..27aba6b 100644
--- a/res/values-sw720dp/dimens.xml
+++ b/res/values-sw720dp/dimens.xml
@@ -37,6 +37,7 @@
<!-- Widget picker-->
<dimen name="widget_list_horizontal_margin">30dp</dimen>
+ <dimen name="widget_cell_horizontal_padding">16dp</dimen>
<!-- Folder spaces -->
<dimen name="folder_footer_horiz_padding">24dp</dimen>
diff --git a/res/values-ta/strings.xml b/res/values-ta/strings.xml
index 2f79f40..9e82389 100644
--- a/res/values-ta/strings.xml
+++ b/res/values-ta/strings.xml
@@ -38,16 +38,19 @@
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d அகலத்திற்கு %2$d உயரம்"</string>
<string name="widget_preview_context_description" msgid="9045841361655787574">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> விட்ஜெட்"</string>
+ <string name="widget_preview_name_and_dims_content_description" msgid="8489038126122831595">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> விட்ஜெட், %2$d அகலம் மற்றும் %3$d உயரம்"</string>
<string name="add_item_request_drag_hint" msgid="8730547755622776606">"முகப்புத் திரையைச் சுற்றி விட்ஜெட்டை நகர்த்த அதைத் தொட்டுப் பிடியுங்கள்"</string>
<string name="add_to_home_screen" msgid="9168649446635919791">"முகப்புத் திரையில் சேர்"</string>
<string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> விட்ஜெட் முகப்புத் திரையில் சேர்க்கப்பட்டது"</string>
<string name="suggested_widgets_header_title" msgid="1844314680798145222">"பரிந்துரைகள்"</string>
- <string name="productivity_widget_recommendation_category_label" msgid="1722113555721820766">"உங்கள் தினசரி அத்தியாவசியத் தேவைகள்"</string>
- <string name="news_widget_recommendation_category_label" msgid="3908242346768119070">"உங்களுக்கான செய்திகள்"</string>
+ <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"அத்தியாவசியமானவை"</string>
+ <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"செய்திகள் & பத்திரிக்கைகள்"</string>
<string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"உங்கள் மனதுக்கு இதமானவை"</string>
- <string name="fitness_widget_recommendation_category_label" msgid="2657652999128882431">"உடற்பயிற்சி இலக்குகளை அடையுங்கள்"</string>
- <string name="weather_widget_recommendation_category_label" msgid="6712678763480668598">"வானிலை குறித்து முன்கூட்டியே அறிந்திருங்கள்"</string>
- <string name="others_widget_recommendation_category_label" msgid="897876078077284733">"நீங்கள் இவற்றையும் விரும்பக்கூடும்"</string>
+ <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"பொழுதுபோக்கு"</string>
+ <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"சமூகம்"</string>
+ <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"ஆரோக்கியம் & உடற்பயிற்சி"</string>
+ <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"வானிலை"</string>
+ <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"உங்களுக்கான பரிந்துரைகள்"</string>
<string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"<xliff:g id="SELECTED_HEADER">%1$s</xliff:g> விட்ஜெட்கள் வலதுபுறத்தில் உள்ளன, தேடல் மற்றும் விருப்பங்கள் இடதுபுறத்தில் உள்ளன"</string>
<string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# விட்ஜெட்}other{# விட்ஜெட்டுகள்}}"</string>
<string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# ஷார்ட்கட்}other{# ஷார்ட்கட்கள்}}"</string>
@@ -61,6 +64,8 @@
<string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"பணி"</string>
<string name="widget_category_conversations" msgid="8894438636213590446">"உரையாடல்கள்"</string>
<string name="widget_category_note_taking" msgid="3469689394504266039">"குறிப்பெடுத்தல்"</string>
+ <string name="widget_add_button_label" msgid="2761267068711937179">"சேர்"</string>
+ <string name="widget_add_button_content_description" msgid="1810530016360039643">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> விட்ஜெட்டைச் சேர்க்கும்"</string>
<string name="widget_education_header" msgid="4874760613775913787">"விரல்நுனியில் பயனுள்ள தகவல்களைப் பெறுங்கள்"</string>
<string name="widget_education_content" msgid="1731667670753497052">"முகப்புத் திரையில் விட்ஜெட்டுகளைச் சேர்த்து ஆப்ஸைத் திறக்காமலேயே தகவல்களைப் பெறலாம்"</string>
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"விட்ஜெட் அமைப்புகளை மாற்றத் தட்டவும்"</string>
diff --git a/res/values-te/strings.xml b/res/values-te/strings.xml
index 5f104e2..9ad29c6 100644
--- a/res/values-te/strings.xml
+++ b/res/values-te/strings.xml
@@ -38,16 +38,19 @@
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d వెడల్పు X %2$d ఎత్తు"</string>
<string name="widget_preview_context_description" msgid="9045841361655787574">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> విడ్జెట్"</string>
+ <string name="widget_preview_name_and_dims_content_description" msgid="8489038126122831595">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> విడ్జెట్, %2$d వెడల్పు %3$d ఎత్తు ఉండాలి"</string>
<string name="add_item_request_drag_hint" msgid="8730547755622776606">"విడ్జెట్ను మొదటి స్క్రీన్లో తిప్పడానికి దాన్ని తాకి, & నొక్కి పట్టుకోండి"</string>
<string name="add_to_home_screen" msgid="9168649446635919791">"మొదటి స్క్రీన్కు జోడించండి"</string>
<string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"మొదటి స్క్రీన్కు <xliff:g id="WIDGET_NAME">%1$s</xliff:g> విడ్జెట్ జోడించబడింది"</string>
<string name="suggested_widgets_header_title" msgid="1844314680798145222">"సూచనలు"</string>
- <string name="productivity_widget_recommendation_category_label" msgid="1722113555721820766">"మీ రోజువారీ అవసరాలు"</string>
- <string name="news_widget_recommendation_category_label" msgid="3908242346768119070">"మీ కోసం వార్తలు"</string>
+ <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Essentials"</string>
+ <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"వార్తలు & మ్యాగజైన్లు"</string>
<string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"మీరు ప్రశాంతంగా ఉండే ప్రదేశం"</string>
- <string name="fitness_widget_recommendation_category_label" msgid="2657652999128882431">"ఫిట్నెస్ లక్ష్యాలను చేరుకోండి"</string>
- <string name="weather_widget_recommendation_category_label" msgid="6712678763480668598">"వాతావరణాన్ని ముందుగానే తెలుసుకోండి"</string>
- <string name="others_widget_recommendation_category_label" msgid="897876078077284733">"మీరు వీటిని కూడా ఇష్టపడవచ్చు"</string>
+ <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"వినోదం"</string>
+ <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"సామాజికం"</string>
+ <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"ఆరోగ్యం & ఫిట్నెస్"</string>
+ <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"వాతావరణం"</string>
+ <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"మీ కోసం సూచించినవి"</string>
<string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"కుడి వైపున <xliff:g id="SELECTED_HEADER">%1$s</xliff:g> విడ్జెట్లు, ఎడమ వైపున సెర్చ్, ఇతర ఆప్షన్లు"</string>
<string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# విడ్జెట్}other{# విడ్జెట్లు}}"</string>
<string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# షార్ట్కట్}other{# షార్ట్కట్లు}}"</string>
@@ -61,6 +64,8 @@
<string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"ఆఫీస్"</string>
<string name="widget_category_conversations" msgid="8894438636213590446">"సంభాషణలు"</string>
<string name="widget_category_note_taking" msgid="3469689394504266039">"నోట్-టేకింగ్"</string>
+ <string name="widget_add_button_label" msgid="2761267068711937179">"జోడించండి"</string>
+ <string name="widget_add_button_content_description" msgid="1810530016360039643">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> విడ్జెట్ను జోడించండి"</string>
<string name="widget_education_header" msgid="4874760613775913787">"మీ చేతివేళ్ల మీద ఉపయోగకరమైన సమాచారం"</string>
<string name="widget_education_content" msgid="1731667670753497052">"యాప్లను తెరవకుండా సమాచారాన్ని పొందడానికి, మీరు మీ మొదటి స్క్రీన్కు విడ్జెట్లను జోడించవచ్చు"</string>
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"విడ్జెట్ సెట్టింగ్లను మార్చడానికి ట్యాప్ చేయండి"</string>
diff --git a/res/values-th/strings.xml b/res/values-th/strings.xml
index c3c2c20..dc92980 100644
--- a/res/values-th/strings.xml
+++ b/res/values-th/strings.xml
@@ -38,16 +38,19 @@
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"กว้าง %1$d x สูง %2$d"</string>
<string name="widget_preview_context_description" msgid="9045841361655787574">"วิดเจ็ต <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
+ <string name="widget_preview_name_and_dims_content_description" msgid="8489038126122831595">"วิดเจ็ต <xliff:g id="WIDGET_NAME">%1$s</xliff:g>, กว้าง %2$d สูง %3$d"</string>
<string name="add_item_request_drag_hint" msgid="8730547755622776606">"แตะวิดเจ็ตค้างไว้เพื่อย้ายไปรอบๆ หน้าจอหลัก"</string>
<string name="add_to_home_screen" msgid="9168649446635919791">"เพิ่มลงในหน้าจอหลัก"</string>
<string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"เพิ่มวิดเจ็ต <xliff:g id="WIDGET_NAME">%1$s</xliff:g> ลงในหน้าจอหลักแล้ว"</string>
<string name="suggested_widgets_header_title" msgid="1844314680798145222">"คำแนะนำ"</string>
- <string name="productivity_widget_recommendation_category_label" msgid="1722113555721820766">"สิ่งจำเป็นในชีวิตประจำวันของคุณ"</string>
- <string name="news_widget_recommendation_category_label" msgid="3908242346768119070">"ข่าวสารสำหรับคุณ"</string>
+ <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"รายการที่ห้ามพลาด"</string>
+ <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"ข่าวสารและนิตยสาร"</string>
<string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"พื้นที่สบายๆ ของคุณ"</string>
- <string name="fitness_widget_recommendation_category_label" msgid="2657652999128882431">"บรรลุเป้าหมายการออกกำลังกาย"</string>
- <string name="weather_widget_recommendation_category_label" msgid="6712678763480668598">"รู้สภาพอากาศล่วงหน้า"</string>
- <string name="others_widget_recommendation_category_label" msgid="897876078077284733">"คุณอาจชอบ"</string>
+ <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"ความบันเทิง"</string>
+ <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"โซเชียล"</string>
+ <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"สุขภาพและการออกกำลังกาย"</string>
+ <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"สภาพอากาศ"</string>
+ <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"แนะนำให้คุณ"</string>
<string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"วิดเจ็ต<xliff:g id="SELECTED_HEADER">%1$s</xliff:g>ทางด้านขวา การค้นหาและตัวเลือกทางด้านซ้าย"</string>
<string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{วิดเจ็ต # รายการ}other{วิดเจ็ต # รายการ}}"</string>
<string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{ทางลัด # รายการ}other{ทางลัด # รายการ}}"</string>
@@ -61,6 +64,8 @@
<string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"งาน"</string>
<string name="widget_category_conversations" msgid="8894438636213590446">"การสนทนา"</string>
<string name="widget_category_note_taking" msgid="3469689394504266039">"การจดบันทึก"</string>
+ <string name="widget_add_button_label" msgid="2761267068711937179">"เพิ่ม"</string>
+ <string name="widget_add_button_content_description" msgid="1810530016360039643">"เพิ่มวิดเจ็ต <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
<string name="widget_education_header" msgid="4874760613775913787">"เข้าถึงข้อมูลที่เป็นประโยชน์ได้จากปลายนิ้ว"</string>
<string name="widget_education_content" msgid="1731667670753497052">"หากต้องการรับข้อมูลโดยไม่เปิดแอป ให้เพิ่มวิดเจ็ตลงในหน้าจอหลัก"</string>
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"แตะเพื่อเปลี่ยนการตั้งค่าวิดเจ็ต"</string>
diff --git a/res/values-tl/strings.xml b/res/values-tl/strings.xml
index 8e4dc1d..36a7b5c 100644
--- a/res/values-tl/strings.xml
+++ b/res/values-tl/strings.xml
@@ -38,16 +38,19 @@
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d ang lapad at %2$d ang taas"</string>
<string name="widget_preview_context_description" msgid="9045841361655787574">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> widget"</string>
+ <string name="widget_preview_name_and_dims_content_description" msgid="8489038126122831595">"Widget na <xliff:g id="WIDGET_NAME">%1$s</xliff:g>, %2$d ang lapad at %3$d ang taas"</string>
<string name="add_item_request_drag_hint" msgid="8730547755622776606">"Pindutin nang matagal ang widget para ilipat-lipat ito sa home screen"</string>
<string name="add_to_home_screen" msgid="9168649446635919791">"Idagdag sa home screen"</string>
<string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"Idinagdag sa home screen ang widget na <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
<string name="suggested_widgets_header_title" msgid="1844314680798145222">"Mga Suhestyon"</string>
- <string name="productivity_widget_recommendation_category_label" msgid="1722113555721820766">"Ang Pang-araw-araw Mong Mga Essential"</string>
- <string name="news_widget_recommendation_category_label" msgid="3908242346768119070">"Balita para sa Iyo"</string>
+ <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Mga essential"</string>
+ <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"Balita at mga magazine"</string>
<string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"Ang Iyong Chill Zone"</string>
- <string name="fitness_widget_recommendation_category_label" msgid="2657652999128882431">"Makamit ang Iyong Mga Layunin sa Fitness"</string>
- <string name="weather_widget_recommendation_category_label" msgid="6712678763480668598">"Manatiling Handa sa Lagay ng Panahon"</string>
- <string name="others_widget_recommendation_category_label" msgid="897876078077284733">"Baka Magustuhan Mo Rin"</string>
+ <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"Entertainment"</string>
+ <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"Social"</string>
+ <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"Kalusugan at fitness"</string>
+ <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"Lagay ng panahon"</string>
+ <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"Iminumungkahi para sa iyo"</string>
<string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"Mga widget ng <xliff:g id="SELECTED_HEADER">%1$s</xliff:g> sa kanan, paghahanap at mga opsyon sa kaliwa"</string>
<string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# widget}one{# widget}other{# na widget}}"</string>
<string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# shortcut}one{# shortcut}other{# na shortcut}}"</string>
@@ -61,6 +64,8 @@
<string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Trabaho"</string>
<string name="widget_category_conversations" msgid="8894438636213590446">"Mga Pag-uusap"</string>
<string name="widget_category_note_taking" msgid="3469689394504266039">"Pagtatala"</string>
+ <string name="widget_add_button_label" msgid="2761267068711937179">"Idagdag"</string>
+ <string name="widget_add_button_content_description" msgid="1810530016360039643">"Idagdag ang widget na <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
<string name="widget_education_header" msgid="4874760613775913787">"Abot-kamay na mahalagang impormasyon"</string>
<string name="widget_education_content" msgid="1731667670753497052">"Para makakuha ng impormasyon nang hindi nagbubukas ng mga app, puwede kang magdagdag ng mga widget sa iyong home screen"</string>
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"I-tap para baguhin ang mga setting ng widget"</string>
diff --git a/res/values-tr/strings.xml b/res/values-tr/strings.xml
index 7633940..8c00ec3 100644
--- a/res/values-tr/strings.xml
+++ b/res/values-tr/strings.xml
@@ -38,16 +38,19 @@
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"genişlik: %1$d, yükseklik: %2$d"</string>
<string name="widget_preview_context_description" msgid="9045841361655787574">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> widget\'ı"</string>
+ <string name="widget_preview_name_and_dims_content_description" msgid="8489038126122831595">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> widget\'ı, %2$d genişlik x %3$d yükseklik"</string>
<string name="add_item_request_drag_hint" msgid="8730547755622776606">"Ana ekranda taşımak için widget\'a dokunup basılı tutun"</string>
<string name="add_to_home_screen" msgid="9168649446635919791">"Ana ekrana ekle"</string>
<string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> widget\'ı ana ekrana eklendi"</string>
<string name="suggested_widgets_header_title" msgid="1844314680798145222">"Öneriler"</string>
- <string name="productivity_widget_recommendation_category_label" msgid="1722113555721820766">"Günlük Gerekenler"</string>
- <string name="news_widget_recommendation_category_label" msgid="3908242346768119070">"Size özel haberler"</string>
+ <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Kaçırmamanız gerekenler"</string>
+ <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"Haberler ve dergiler"</string>
<string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"Huzur alanınız"</string>
- <string name="fitness_widget_recommendation_category_label" msgid="2657652999128882431">"Fitness hedeflerinize ulaşın"</string>
- <string name="weather_widget_recommendation_category_label" msgid="6712678763480668598">"Havanın durumu sizi şaşırtmasın"</string>
- <string name="others_widget_recommendation_category_label" msgid="897876078077284733">"Şunları da beğenebilirsiniz"</string>
+ <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"Eğlence"</string>
+ <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"Sosyal"</string>
+ <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"Sağlık ve fitness"</string>
+ <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"Hava durumu"</string>
+ <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"Sizin için önerilenler"</string>
<string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"<xliff:g id="SELECTED_HEADER">%1$s</xliff:g> widget\'ları sağda, arama ve seçenekler solda"</string>
<string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# widget}other{# widget}}"</string>
<string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# kısayol}other{# kısayol}}"</string>
@@ -61,6 +64,8 @@
<string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"İş"</string>
<string name="widget_category_conversations" msgid="8894438636213590446">"Görüşmeler"</string>
<string name="widget_category_note_taking" msgid="3469689394504266039">"Not alma"</string>
+ <string name="widget_add_button_label" msgid="2761267068711937179">"Ekle"</string>
+ <string name="widget_add_button_content_description" msgid="1810530016360039643">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> widget\'ı ekle"</string>
<string name="widget_education_header" msgid="4874760613775913787">"Faydalı bilgiler parmaklarınızın ucunda"</string>
<string name="widget_education_content" msgid="1731667670753497052">"Uygulama açmadan bilgi almak için ana ekranınıza widget ekleyebilirsiniz"</string>
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Widget ayarlarını değiştirmek için dokunun"</string>
diff --git a/res/values-uk/strings.xml b/res/values-uk/strings.xml
index 7f56e1b..a8211f5 100644
--- a/res/values-uk/strings.xml
+++ b/res/values-uk/strings.xml
@@ -38,16 +38,19 @@
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"Ширина – %1$d, висота – %2$d"</string>
<string name="widget_preview_context_description" msgid="9045841361655787574">"Віджет <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
+ <string name="widget_preview_name_and_dims_content_description" msgid="8489038126122831595">"Віджет \"<xliff:g id="WIDGET_NAME">%1$s</xliff:g>\", ширина: %2$d, висота: %3$d"</string>
<string name="add_item_request_drag_hint" msgid="8730547755622776606">"Натисніть і втримуйте віджет, щоб перемістити його в потрібне місце на головному екрані"</string>
<string name="add_to_home_screen" msgid="9168649446635919791">"Додати на головний екран"</string>
<string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"Віджет <xliff:g id="WIDGET_NAME">%1$s</xliff:g> додано на головний екран"</string>
<string name="suggested_widgets_header_title" msgid="1844314680798145222">"Пропозиції"</string>
- <string name="productivity_widget_recommendation_category_label" msgid="1722113555721820766">"Найнеобхідніше на кожен день"</string>
- <string name="news_widget_recommendation_category_label" msgid="3908242346768119070">"Новини для вас"</string>
+ <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Основне для роботи"</string>
+ <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"Новини й журнали"</string>
<string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"Ваша зона розваг"</string>
- <string name="fitness_widget_recommendation_category_label" msgid="2657652999128882431">"Досягайте своїх фітнес-цілей"</string>
- <string name="weather_widget_recommendation_category_label" msgid="6712678763480668598">"Завчасно дізнавайтеся про зміни погоди"</string>
- <string name="others_widget_recommendation_category_label" msgid="897876078077284733">"Вам також може сподобатися"</string>
+ <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"Розваги"</string>
+ <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"Соціальні мережі"</string>
+ <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"Здоров’я і фітнес"</string>
+ <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"Погода"</string>
+ <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"Пропозиції для вас"</string>
<string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"<xliff:g id="SELECTED_HEADER">%1$s</xliff:g>: віджети праворуч, пошук і опції ліворуч"</string>
<string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# віджет}one{# віджет}few{# віджети}many{# віджетів}other{# віджета}}"</string>
<string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# ярлик}one{# ярлик}few{# ярлики}many{# ярликів}other{# ярлика}}"</string>
@@ -61,6 +64,8 @@
<string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Робочі"</string>
<string name="widget_category_conversations" msgid="8894438636213590446">"Розмови"</string>
<string name="widget_category_note_taking" msgid="3469689394504266039">"Створення нотаток"</string>
+ <string name="widget_add_button_label" msgid="2761267068711937179">"Додати"</string>
+ <string name="widget_add_button_content_description" msgid="1810530016360039643">"Додати віджет \"<xliff:g id="WIDGET_NAME">%1$s</xliff:g>\""</string>
<string name="widget_education_header" msgid="4874760613775913787">"Корисна інформація завжди під рукою"</string>
<string name="widget_education_content" msgid="1731667670753497052">"Щоб отримувати інформацію, не відкриваючи додатки, ви можете додати на головний екран віджети"</string>
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Натисніть, щоб змінити налаштування віджета"</string>
diff --git a/res/values-ur/strings.xml b/res/values-ur/strings.xml
index c945bab..430c835 100644
--- a/res/values-ur/strings.xml
+++ b/res/values-ur/strings.xml
@@ -38,16 +38,19 @@
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d چوڑا اور %2$d اونچا"</string>
<string name="widget_preview_context_description" msgid="9045841361655787574">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> ویجیٹ"</string>
+ <string name="widget_preview_name_and_dims_content_description" msgid="8489038126122831595">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> ویجیٹ، %2$d چوڑا اور %3$d اونچا ہے"</string>
<string name="add_item_request_drag_hint" msgid="8730547755622776606">"ویجیٹ کو ہوم اسکرین کے چاروں طرف منتقل کرنے کے لیے اسے ٹچ کریں اور دبائے رکھیں"</string>
<string name="add_to_home_screen" msgid="9168649446635919791">"ہوم اسکرین میں شامل کریں"</string>
<string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> ویجیٹ کو ہوم اسکرین میں شامل کیا گیا"</string>
<string name="suggested_widgets_header_title" msgid="1844314680798145222">"تجاویز"</string>
- <string name="productivity_widget_recommendation_category_label" msgid="1722113555721820766">"آپ کے روز مرہ کے لوازمات"</string>
- <string name="news_widget_recommendation_category_label" msgid="3908242346768119070">"آپ کے لیے خبریں"</string>
+ <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"لوازمات"</string>
+ <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"خبریں اور میگزینز"</string>
<string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"آپ کا آرام دہ زون"</string>
- <string name="fitness_widget_recommendation_category_label" msgid="2657652999128882431">"اپنی تندرستی کے مقاصد حاصل کریں"</string>
- <string name="weather_widget_recommendation_category_label" msgid="6712678763480668598">"موسم سے باخبر رہیں"</string>
- <string name="others_widget_recommendation_category_label" msgid="897876078077284733">"آپ کو یہ بھی پسند آ سکتا ہے"</string>
+ <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"تفریح"</string>
+ <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"سماجی"</string>
+ <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"صحت اور تندرستی"</string>
+ <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"موسم"</string>
+ <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"آپ کے لیے تجویز کردہ"</string>
<string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"<xliff:g id="SELECTED_HEADER">%1$s</xliff:g> دائیں طرف وجیٹس، بائیں طرف تلاش اور اختیارات"</string>
<string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# ویجیٹ}other{# ویجیٹس}}"</string>
<string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# شارٹ کٹ}other{# شارٹ کٹس}}"</string>
@@ -61,6 +64,8 @@
<string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"دفتری ویجیٹس"</string>
<string name="widget_category_conversations" msgid="8894438636213590446">"گفتگوئیں"</string>
<string name="widget_category_note_taking" msgid="3469689394504266039">"نوٹ لکھنا"</string>
+ <string name="widget_add_button_label" msgid="2761267068711937179">"شامل کریں"</string>
+ <string name="widget_add_button_content_description" msgid="1810530016360039643">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> ویجیٹ شامل کریں"</string>
<string name="widget_education_header" msgid="4874760613775913787">"مفید معلومات کو آسانی سے حاصل کریں"</string>
<string name="widget_education_content" msgid="1731667670753497052">"ایپس کو کھولے بغیر معلومات حاصل کرنے کے لیے آپ اپنی ہوم اسکرین پر ویجیٹس شامل کر سکتے ہیں"</string>
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"ویجیٹ ترتیبات تبدیل کرنے کے لیے تھپتھپائیں"</string>
diff --git a/res/values-uz/strings.xml b/res/values-uz/strings.xml
index 4fbacb7..8e03a13 100644
--- a/res/values-uz/strings.xml
+++ b/res/values-uz/strings.xml
@@ -38,16 +38,19 @@
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"Eni %1$d, bo‘yi %2$d"</string>
<string name="widget_preview_context_description" msgid="9045841361655787574">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> ta vidjet"</string>
+ <string name="widget_preview_name_and_dims_content_description" msgid="8489038126122831595">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> vidjeti, eniga: %2$d, boʻyiga: %3$d"</string>
<string name="add_item_request_drag_hint" msgid="8730547755622776606">"Bosh ekranda surish uchun vidjet ustiga bosib turing"</string>
<string name="add_to_home_screen" msgid="9168649446635919791">"Bosh ekranga chiqarish"</string>
<string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> vidjeti bosh ekranga qoʻshildi"</string>
<string name="suggested_widgets_header_title" msgid="1844314680798145222">"Takliflar"</string>
- <string name="productivity_widget_recommendation_category_label" msgid="1722113555721820766">"Kunlik muhim vazifalaringiz"</string>
- <string name="news_widget_recommendation_category_label" msgid="3908242346768119070">"Siz uchun yangiliklar"</string>
+ <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Asosiy ilovalar"</string>
+ <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"Gazeta va jurnallar"</string>
<string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"Sokin hududingiz"</string>
- <string name="fitness_widget_recommendation_category_label" msgid="2657652999128882431">"Fitness maqsadlaringizga erishing"</string>
- <string name="weather_widget_recommendation_category_label" msgid="6712678763480668598">"Doim ob-havodan oldinda yuring"</string>
- <string name="others_widget_recommendation_category_label" msgid="897876078077284733">"Sizga yoqishi mumkin"</string>
+ <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"Hordiq"</string>
+ <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"Ijtimoiy"</string>
+ <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"Salomatlik va sport"</string>
+ <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"Ob-havo"</string>
+ <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"Sizga tavsiya etiladi"</string>
<string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"<xliff:g id="SELECTED_HEADER">%1$s</xliff:g> vidjetlari oʻngda, qidiruv va sozlamalar chapda"</string>
<string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# ta vidjet}other{# ta vidjet}}"</string>
<string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# ta yorliq}other{# ta yorliq}}"</string>
@@ -61,6 +64,8 @@
<string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Ish"</string>
<string name="widget_category_conversations" msgid="8894438636213590446">"Suhbatlar"</string>
<string name="widget_category_note_taking" msgid="3469689394504266039">"Qayd olish"</string>
+ <string name="widget_add_button_label" msgid="2761267068711937179">"Chiqarish"</string>
+ <string name="widget_add_button_content_description" msgid="1810530016360039643">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g> vidjetini chiqarish"</string>
<string name="widget_education_header" msgid="4874760613775913787">"Barcha kerakli axborot doim yoningizda"</string>
<string name="widget_education_content" msgid="1731667670753497052">"Kerakli ilovalarni ochmasdan turib ulardan axborot olish uchun vidjetlarni bosh ekranga chiqaring"</string>
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Vidjet sozlamalarini oʻzgartirish uchun bosing"</string>
diff --git a/res/values-v31/colors.xml b/res/values-v31/colors.xml
index e2f610e..fa87221 100644
--- a/res/values-v31/colors.xml
+++ b/res/values-v31/colors.xml
@@ -47,8 +47,8 @@
<color name="wallpaper_popup_scrim">@android:color/system_neutral1_900</color>
- <color name="folder_pagination_color_light">@android:color/system_accent1_600</color>
- <color name="folder_pagination_color_dark">@android:color/system_accent1_200</color>
+ <color name="page_indicator_dot_color_light">@android:color/system_accent1_600</color>
+ <color name="page_indicator_dot_color_dark">@android:color/system_accent1_200</color>
<color name="home_settings_header_accent">@android:color/system_accent1_600</color>
<color name="home_settings_header_collapsed">@android:color/system_neutral1_100</color>
@@ -97,6 +97,10 @@
@android:color/system_neutral2_700</color>
<color name="widget_picker_collapse_handle_color_light">
@android:color/system_neutral2_200</color>
+ <color name="widget_picker_add_button_background_color_light">
+ @android:color/system_accent1_600</color>
+ <color name="widget_picker_add_button_text_color_light">
+ @android:color/system_accent1_0</color>
<color name="work_fab_bg_color">
@android:color/system_accent1_200</color>
diff --git a/res/values-vi/strings.xml b/res/values-vi/strings.xml
index 290adac..17817ac 100644
--- a/res/values-vi/strings.xml
+++ b/res/values-vi/strings.xml
@@ -38,16 +38,19 @@
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"Rộng %1$d x cao %2$d"</string>
<string name="widget_preview_context_description" msgid="9045841361655787574">"Tiện ích <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
+ <string name="widget_preview_name_and_dims_content_description" msgid="8489038126122831595">"Tiện ích <xliff:g id="WIDGET_NAME">%1$s</xliff:g>, rộng %2$d x cao %3$d"</string>
<string name="add_item_request_drag_hint" msgid="8730547755622776606">"Chạm và giữ tiện ích để di chuyển tiện ích đó xung quanh màn hình chính"</string>
<string name="add_to_home_screen" msgid="9168649446635919791">"Thêm vào màn hình chính"</string>
<string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"Đã thêm tiện ích <xliff:g id="WIDGET_NAME">%1$s</xliff:g> vào màn hình chính"</string>
<string name="suggested_widgets_header_title" msgid="1844314680798145222">"Nội dung đề xuất"</string>
- <string name="productivity_widget_recommendation_category_label" msgid="1722113555721820766">"Các tiện ích thiết yếu hằng ngày của bạn"</string>
- <string name="news_widget_recommendation_category_label" msgid="3908242346768119070">"Tin tức cho bạn"</string>
+ <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Các tiện ích thiết yếu"</string>
+ <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"Tin tức và tạp chí"</string>
<string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"Giai điệu thư giãn của bạn"</string>
- <string name="fitness_widget_recommendation_category_label" msgid="2657652999128882431">"Đạt được mục tiêu tập thể dục"</string>
- <string name="weather_widget_recommendation_category_label" msgid="6712678763480668598">"Luôn nắm bắt tình hình thời tiết"</string>
- <string name="others_widget_recommendation_category_label" msgid="897876078077284733">"Có thể bạn cũng thích"</string>
+ <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"Giải trí"</string>
+ <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"Mạng xã hội"</string>
+ <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"Sức khoẻ và thể chất"</string>
+ <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"Thời tiết"</string>
+ <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"Đề xuất cho bạn"</string>
<string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"Tiện ích <xliff:g id="SELECTED_HEADER">%1$s</xliff:g> ở bên phải, công cụ tìm kiếm và tuỳ chọn ở bên trái"</string>
<string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# tiện ích}other{# tiện ích}}"</string>
<string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# lối tắt}other{# lối tắt}}"</string>
@@ -61,6 +64,8 @@
<string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Công việc"</string>
<string name="widget_category_conversations" msgid="8894438636213590446">"Cuộc trò chuyện"</string>
<string name="widget_category_note_taking" msgid="3469689394504266039">"Ghi chú"</string>
+ <string name="widget_add_button_label" msgid="2761267068711937179">"Thêm"</string>
+ <string name="widget_add_button_content_description" msgid="1810530016360039643">"Thêm tiện ích <xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
<string name="widget_education_header" msgid="4874760613775913787">"Thông tin hữu ích ngay trong tầm tay bạn"</string>
<string name="widget_education_content" msgid="1731667670753497052">"Để nhận thông tin mà không cần mở các ứng dụng, bạn có thể thêm tiện ích vào màn hình chính"</string>
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Nhấn để thay đổi chế độ cài đặt tiện ích"</string>
diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml
index 26f42d8..4e77075 100644
--- a/res/values-zh-rCN/strings.xml
+++ b/res/values-zh-rCN/strings.xml
@@ -38,16 +38,19 @@
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"宽 %1$d,高 %2$d"</string>
<string name="widget_preview_context_description" msgid="9045841361655787574">"“<xliff:g id="WIDGET_NAME">%1$s</xliff:g>”微件"</string>
+ <string name="widget_preview_name_and_dims_content_description" msgid="8489038126122831595">"“<xliff:g id="WIDGET_NAME">%1$s</xliff:g>”微件,宽 %2$d,高 %3$d"</string>
<string name="add_item_request_drag_hint" msgid="8730547755622776606">"轻触并按住此微件即可在主屏幕上随意移动它"</string>
<string name="add_to_home_screen" msgid="9168649446635919791">"添加到主屏幕"</string>
<string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"已将“<xliff:g id="WIDGET_NAME">%1$s</xliff:g>”微件添加到主屏幕"</string>
<string name="suggested_widgets_header_title" msgid="1844314680798145222">"建议"</string>
- <string name="productivity_widget_recommendation_category_label" msgid="1722113555721820766">"您的日常必需品"</string>
- <string name="news_widget_recommendation_category_label" msgid="3908242346768119070">"更多相关新闻"</string>
+ <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"必备"</string>
+ <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"新闻与杂志"</string>
<string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"您的休闲区"</string>
- <string name="fitness_widget_recommendation_category_label" msgid="2657652999128882431">"达成您的健身目标"</string>
- <string name="weather_widget_recommendation_category_label" msgid="6712678763480668598">"天气早知道"</string>
- <string name="others_widget_recommendation_category_label" msgid="897876078077284733">"您可能还会喜欢"</string>
+ <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"娱乐"</string>
+ <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"社交"</string>
+ <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"健康与健身"</string>
+ <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"天气"</string>
+ <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"为您推荐"</string>
<string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"右边是<xliff:g id="SELECTED_HEADER">%1$s</xliff:g>微件,左边是搜索功能和选项"</string>
<string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# 个微件}other{# 个微件}}"</string>
<string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# 个快捷方式}other{# 个快捷方式}}"</string>
@@ -61,6 +64,8 @@
<string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"工作"</string>
<string name="widget_category_conversations" msgid="8894438636213590446">"对话"</string>
<string name="widget_category_note_taking" msgid="3469689394504266039">"记事"</string>
+ <string name="widget_add_button_label" msgid="2761267068711937179">"添加"</string>
+ <string name="widget_add_button_content_description" msgid="1810530016360039643">"添加“<xliff:g id="WIDGET_NAME">%1$s</xliff:g>”微件"</string>
<string name="widget_education_header" msgid="4874760613775913787">"实用信息触手可及"</string>
<string name="widget_education_content" msgid="1731667670753497052">"要想不打开应用就能获取信息,您可以将相应微件添加到主屏幕"</string>
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"点按即可更改微件设置"</string>
diff --git a/res/values-zh-rHK/strings.xml b/res/values-zh-rHK/strings.xml
index ff71ad2..735ff2f 100644
--- a/res/values-zh-rHK/strings.xml
+++ b/res/values-zh-rHK/strings.xml
@@ -38,16 +38,19 @@
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d 闊,%2$d 高"</string>
<string name="widget_preview_context_description" msgid="9045841361655787574">"「<xliff:g id="WIDGET_NAME">%1$s</xliff:g>」小工具"</string>
+ <string name="widget_preview_name_and_dims_content_description" msgid="8489038126122831595">"<xliff:g id="WIDGET_NAME">%1$s</xliff:g>小工具,闊 %2$d,高 %3$d"</string>
<string name="add_item_request_drag_hint" msgid="8730547755622776606">"按住小工具即可移到主畫面的任何位置"</string>
<string name="add_to_home_screen" msgid="9168649446635919791">"加去主畫面"</string>
<string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"已經將「<xliff:g id="WIDGET_NAME">%1$s</xliff:g>」小工具加咗去主畫面"</string>
<string name="suggested_widgets_header_title" msgid="1844314680798145222">"建議"</string>
- <string name="productivity_widget_recommendation_category_label" msgid="1722113555721820766">"你的日常必需品"</string>
- <string name="news_widget_recommendation_category_label" msgid="3908242346768119070">"你的專屬新聞"</string>
+ <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"必備之選"</string>
+ <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"新聞和雜誌"</string>
<string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"放鬆專區"</string>
- <string name="fitness_widget_recommendation_category_label" msgid="2657652999128882431">"向健身目標邁進"</string>
- <string name="weather_widget_recommendation_category_label" msgid="6712678763480668598">"隨時掌握天氣資料"</string>
- <string name="others_widget_recommendation_category_label" msgid="897876078077284733">"相關推薦"</string>
+ <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"娛樂"</string>
+ <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"社交"</string>
+ <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"健康和健身"</string>
+ <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"天氣"</string>
+ <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"為你推薦"</string>
<string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"右邊係「<xliff:g id="SELECTED_HEADER">%1$s</xliff:g>」小工具,左邊係搜尋功能同選項"</string>
<string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# 個小工具}other{# 個小工具}}"</string>
<string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# 個捷徑}other{# 個捷徑}}"</string>
@@ -61,6 +64,8 @@
<string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"工作"</string>
<string name="widget_category_conversations" msgid="8894438636213590446">"對話"</string>
<string name="widget_category_note_taking" msgid="3469689394504266039">"做筆記"</string>
+ <string name="widget_add_button_label" msgid="2761267068711937179">"新增"</string>
+ <string name="widget_add_button_content_description" msgid="1810530016360039643">"加<xliff:g id="WIDGET_NAME">%1$s</xliff:g>小工具"</string>
<string name="widget_education_header" msgid="4874760613775913787">"實用資訊,唾手可得"</string>
<string name="widget_education_content" msgid="1731667670753497052">"只要將小工具新增至主畫面,就可以直接查看資料,無需開啟應用程式"</string>
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"輕按即可變更小工具設定"</string>
diff --git a/res/values-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml
index 1385fce..b74bde2 100644
--- a/res/values-zh-rTW/strings.xml
+++ b/res/values-zh-rTW/strings.xml
@@ -38,16 +38,19 @@
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"寬度為 %1$d,高度為 %2$d"</string>
<string name="widget_preview_context_description" msgid="9045841361655787574">"「<xliff:g id="WIDGET_NAME">%1$s</xliff:g>」小工具"</string>
+ <string name="widget_preview_name_and_dims_content_description" msgid="8489038126122831595">"「<xliff:g id="WIDGET_NAME">%1$s</xliff:g>」小工具 (寬 %2$d,高 %3$d)"</string>
<string name="add_item_request_drag_hint" msgid="8730547755622776606">"按住小工具即可將它移到主畫面上的任何位置"</string>
<string name="add_to_home_screen" msgid="9168649446635919791">"新增至主畫面"</string>
<string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"已將「<xliff:g id="WIDGET_NAME">%1$s</xliff:g>」小工具新增到主畫面"</string>
<string name="suggested_widgets_header_title" msgid="1844314680798145222">"建議"</string>
- <string name="productivity_widget_recommendation_category_label" msgid="1722113555721820766">"生活好幫手"</string>
- <string name="news_widget_recommendation_category_label" msgid="3908242346768119070">"你的專屬新聞"</string>
+ <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"常用項目"</string>
+ <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"新聞與雜誌"</string>
<string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"放鬆專區"</string>
- <string name="fitness_widget_recommendation_category_label" msgid="2657652999128882431">"達成健身目標"</string>
- <string name="weather_widget_recommendation_category_label" msgid="6712678763480668598">"隨時掌握天氣資訊"</string>
- <string name="others_widget_recommendation_category_label" msgid="897876078077284733">"你可能也會喜歡的內容"</string>
+ <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"娛樂"</string>
+ <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"社群"</string>
+ <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"健康與塑身"</string>
+ <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"天氣"</string>
+ <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"個人化建議"</string>
<string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"右邊是「<xliff:g id="SELECTED_HEADER">%1$s</xliff:g>」小工具,左邊是搜尋功能和選項"</string>
<string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# 項小工具}other{# 項小工具}}"</string>
<string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# 個捷徑}other{# 個捷徑}}"</string>
@@ -61,6 +64,8 @@
<string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"工作"</string>
<string name="widget_category_conversations" msgid="8894438636213590446">"對話"</string>
<string name="widget_category_note_taking" msgid="3469689394504266039">"做筆記"</string>
+ <string name="widget_add_button_label" msgid="2761267068711937179">"新增"</string>
+ <string name="widget_add_button_content_description" msgid="1810530016360039643">"新增「<xliff:g id="WIDGET_NAME">%1$s</xliff:g>」小工具"</string>
<string name="widget_education_header" msgid="4874760613775913787">"實用資訊隨手可得"</string>
<string name="widget_education_content" msgid="1731667670753497052">"只要將小工具新增到主畫面,就可以直接查看資訊,不必開啟應用程式"</string>
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"輕觸即可變更小工具設定"</string>
diff --git a/res/values-zu/strings.xml b/res/values-zu/strings.xml
index 8388bb7..3fdb6a7 100644
--- a/res/values-zu/strings.xml
+++ b/res/values-zu/strings.xml
@@ -38,16 +38,19 @@
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d ububanzi ngokungu-%2$d ukuya phezulu"</string>
<string name="widget_preview_context_description" msgid="9045841361655787574">"Iwijethi elingu-<xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
+ <string name="widget_preview_name_and_dims_content_description" msgid="8489038126122831595">"Iwijethi ye-<xliff:g id="WIDGET_NAME">%1$s</xliff:g>, ububanzi obungu-%2$d ngokuphakama okungu-%3$d"</string>
<string name="add_item_request_drag_hint" msgid="8730547755622776606">"Thinta uphinde ubambe iwijethi ukuyihambisa kusikrini sasekhaya"</string>
<string name="add_to_home_screen" msgid="9168649446635919791">"Faka kusikrini sasekhaya"</string>
<string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"Iwijethi ye-<xliff:g id="WIDGET_NAME">%1$s</xliff:g> yengezwe kusikrini sasekhaya"</string>
<string name="suggested_widgets_header_title" msgid="1844314680798145222">"Iziphakamiso"</string>
- <string name="productivity_widget_recommendation_category_label" msgid="1722113555721820766">"Okusemqoka kwakho kwansuku zonke"</string>
- <string name="news_widget_recommendation_category_label" msgid="3908242346768119070">"Izindaba Zakho"</string>
+ <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Okusemqoka"</string>
+ <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"Izindaba nomagazini"</string>
<string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"Indawo Ozipholela Kuyo"</string>
- <string name="fitness_widget_recommendation_category_label" msgid="2657652999128882431">"Finyelela Imigomo Yakho Yokufaneleka"</string>
- <string name="weather_widget_recommendation_category_label" msgid="6712678763480668598">"Hlale Wazi Ngesimo Sezulu"</string>
- <string name="others_widget_recommendation_category_label" msgid="897876078077284733">"Ungase Futhi Uthande"</string>
+ <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"Okokozijabulisa"</string>
+ <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"Okomphakathi"</string>
+ <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"Ezempilo nokufaneleka"</string>
+ <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"Isimo sezulu"</string>
+ <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"Okuphakanyiselwe wena"</string>
<string name="widget_picker_right_pane_accessibility_title" msgid="1673313931455067502">"Amawijethi okuthi <xliff:g id="SELECTED_HEADER">%1$s</xliff:g> kwesokudla, ukusesha nokukhethwayo kwesobunxele"</string>
<string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{iwijethi #}one{amawijethi #}other{amawijethi #}}"</string>
<string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{isinqamuleli #}one{izinqamuleli #}other{izinqamuleli #}}"</string>
@@ -61,6 +64,8 @@
<string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Umsebenzi"</string>
<string name="widget_category_conversations" msgid="8894438636213590446">"Izingxoxo"</string>
<string name="widget_category_note_taking" msgid="3469689394504266039">"Ukuthatha amanothi"</string>
+ <string name="widget_add_button_label" msgid="2761267068711937179">"Engeza"</string>
+ <string name="widget_add_button_content_description" msgid="1810530016360039643">"Engeza iwijethi ye-<xliff:g id="WIDGET_NAME">%1$s</xliff:g>"</string>
<string name="widget_education_header" msgid="4874760613775913787">"Ulwazi oluwusizo phambi nje kwakho"</string>
<string name="widget_education_content" msgid="1731667670753497052">"Ukuze uthole ulwazi ngaphandle kokuvula ama-app, ungakwazi ukwengeza amawijethi kusikrini sakho sasekhaya"</string>
<string name="reconfigurable_widget_education_tip" msgid="6336962690888067057">"Thepha ukuze ushintshe amasethingi ewijethi"</string>
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index 4a0b5e8..ffc8bd0 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -43,8 +43,9 @@
<attr name="popupNotificationDotColor" format="color" />
<attr name="notificationDotColor" format="color" />
<attr name="focusOutlineColor" format="color" />
+ <attr name="focusInnerOutlineColor" format="color" />
- <attr name="folderPaginationColor" format="color" />
+ <attr name="pageIndicatorDotColor" format="color" />
<attr name="folderPreviewColor" format="color" />
<attr name="folderBackgroundColor" format="color" />
<attr name="folderIconRadius" format="float" />
@@ -72,6 +73,8 @@
<attr name="widgetPickerSelectedTabTextColor" format="color"/>
<attr name="widgetPickerUnselectedTabTextColor" format="color"/>
<attr name="widgetPickerCollapseHandleColor" format="color"/>
+ <attr name="widgetPickerAddButtonBackgroundColor" format="color"/>
+ <attr name="widgetPickerAddButtonTextColor" format="color"/>
<!-- BubbleTextView specific attributes. -->
<declare-styleable name="BubbleTextView">
diff --git a/res/values/colors.xml b/res/values/colors.xml
index 6a484d7..a620eb0 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -73,8 +73,8 @@
<color name="folder_preview_light">#7FCFFF</color>
<color name="folder_preview_dark">#1E1F20</color>
- <color name="folder_pagination_color_light">#0B57D0</color>
- <color name="folder_pagination_color_dark">#A8C7FA</color>
+ <color name="pagination_indicator_dot_color_light">#0B57D0</color>
+ <color name="pagination_indicator_dot_color_dark">#A8C7FA</color>
<color name="text_color_primary_dark">#FFFFFFFF</color>
<color name="text_color_secondary_dark">#FFFFFFFF</color>
@@ -113,6 +113,8 @@
<color name="widget_picker_selected_tab_text_color_light">#FFFFFF</color>
<color name="widget_picker_unselected_tab_text_color_light">#444746</color>
<color name="widget_picker_collapse_handle_color_light">#C4C7C5</color>
+ <color name="widget_picker_add_button_background_color_light">#0B57D0</color>
+ <color name="widget_picker_add_button_text_color_light">#0B57D0</color>
<color name="widget_picker_primary_surface_color_dark">#1F2020</color>
<color name="widget_picker_secondary_surface_color_dark">#393939</color>
@@ -128,6 +130,8 @@
<color name="widget_picker_selected_tab_text_color_dark">#2D312F</color>
<color name="widget_picker_unselected_tab_text_color_dark">#C4C7C5</color>
<color name="widget_picker_collapse_handle_color_dark">#444746</color>
+ <color name="widget_picker_add_button_background_color_dark">#062E6F</color>
+ <color name="widget_picker_add_button_text_color_dark">#FFFFFF</color>
<color name="material_color_on_secondary_fixed_variant">#3F4759</color>
<color name="material_color_on_tertiary_fixed_variant">#583E5B</color>
diff --git a/res/values/config.xml b/res/values/config.xml
index 21d6024..048d6cc 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -159,6 +159,9 @@
<item name="swipe_up_rect_scale_damping_ratio" type="dimen" format="float">0.75</item>
<item name="swipe_up_rect_scale_stiffness" type="dimen" format="float">200</item>
<item name="swipe_up_rect_scale_higher_stiffness" type="dimen" format="float">400</item>
+ <!-- Flag: enableScalingRevealHomeAnimation() -->
+ <item name="swipe_up_rect_scale_damping_ratio_v2" type="dimen" format="float">0.8</item>
+ <item name="swipe_up_rect_scale_stiffness_v2" type="dimen" format="float">650</item>
<item name="swipe_up_rect_xy_fling_friction" type="dimen" format="float">1.5</item>
@@ -166,6 +169,11 @@
<item name="swipe_up_rect_xy_damping_ratio" type="dimen" format="float">0.8</item>
<item name="swipe_up_rect_xy_stiffness" type="dimen" format="float">200</item>
+ <!-- Flag: enableScalingRevealHomeAnimation() -->
+ <item name="swipe_up_rect_x_damping_ratio" type="dimen" format="float">0.965</item>
+ <item name="swipe_up_rect_x_stiffness" type="dimen" format="float">300</item>
+ <item name="swipe_up_rect_y_damping_ratio" type="dimen" format="float">0.95</item>
+ <item name="swipe_up_rect_y_stiffness" type="dimen" format="float">190</item>
<!-- Taskbar -->
<!-- This is a float because it is converted to dp later in DeviceProfile -->
@@ -190,6 +198,12 @@
<dimen name="swipe_up_fling_min_visible_change">18dp</dimen>
<dimen name="swipe_up_max_workspace_trans_y">-60dp</dimen>
<dimen name="swipe_up_max_velocity">7.619dp</dimen>
+ <!-- Flag: enableScalingRevealHomeAnimation() -->
+ <item name="swipe_up_min_velocity_x_px_per_s" type="dimen" format="integer">300</item>
+ <item name="swipe_up_max_velocity_x_px_per_s" type="dimen" format="integer">500</item>
+ <item name="swipe_up_min_velocity_y_px_per_s" type="dimen" format="integer">2000</item>
+ <item name="swipe_up_max_velocity_y_px_per_s" type="dimen" format="integer">5000</item>
+ <item name="swipe_up_max_velocity_fall_off_factor" type="dimen" format="float">1.4</item>
<array name="dynamic_resources">
<item>@dimen/swipe_up_scale_start</item>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index a912e2d..9b4460a 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -22,7 +22,7 @@
<dimen name="dynamic_grid_edge_margin">10.77dp</dimen>
<dimen name="dynamic_grid_left_right_margin">8dp</dimen>
<!-- Minimum amount of next page visible in spring loaded mode -->
- <dimen name="dynamic_grid_spring_loaded_min_next_space_visible">24dp</dimen>
+ <dimen name="dynamic_grid_spring_loaded_min_next_space_visible">48dp</dimen>
<dimen name="dynamic_grid_cell_border_spacing">16dp</dimen>
<dimen name="cell_layout_padding">10.77dp</dimen>
@@ -176,10 +176,14 @@
<!-- Widget tray -->
<dimen name="widget_cell_vertical_padding">8dp</dimen>
- <dimen name="widget_cell_horizontal_padding">16dp</dimen>
+ <dimen name="widget_cell_horizontal_padding">8dp</dimen>
<dimen name="widget_cell_font_size">14sp</dimen>
<dimen name="widget_cell_app_icon_size">24dp</dimen>
<dimen name="widget_cell_app_icon_padding">8dp</dimen>
+ <dimen name="widget_cell_add_button_height">48dp</dimen>
+ <dimen name="widget_cell_add_button_start_padding">8dp</dimen>
+ <dimen name="widget_cell_add_button_end_padding">16dp</dimen>
+ <dimen name="widget_cell_add_button_vertical_padding">10dp</dimen>
<dimen name="widget_tabs_button_horizontal_padding">4dp</dimen>
<dimen name="widget_tabs_horizontal_padding">16dp</dimen>
@@ -187,7 +191,6 @@
<dimen name="widget_picker_landscape_tablet_left_right_margin">117dp</dimen>
<dimen name="widget_picker_two_panels_left_right_margin">0dp</dimen>
<dimen name="widget_recommendations_table_vertical_padding">8dp</dimen>
- <dimen name="widget_recommendations_table_horizontal_padding">16dp</dimen>
<!-- Bottom margin for the search and recommended widgets container without work profile -->
<dimen name="search_and_recommended_widgets_container_bottom_margin">16dp</dimen>
<!-- Bottom margin for the search and recommended widgets container with work profile -->
@@ -198,7 +201,8 @@
<dimen name="widget_list_header_view_vertical_padding">20dp</dimen>
<dimen name="widget_list_entry_spacing">2dp</dimen>
- <dimen name="widget_list_horizontal_margin">16dp</dimen>
+ <!-- Less margin on sides to let widgets table width be close to the workspace width. -->
+ <dimen name="widget_list_horizontal_margin">11dp</dimen>
<!-- Margin applied to the recycler view with search bar & the list of widget apps below it. -->
<dimen name="widget_list_left_pane_horizontal_margin">0dp</dimen>
<dimen name="widget_list_horizontal_margin_two_pane">24dp</dimen>
@@ -447,7 +451,11 @@
<dimen name="split_instructions_start_margin_cancel">8dp</dimen>
<dimen name="focus_outline_radius">16dp</dimen>
- <dimen name="focus_outline_stroke_width">3dp</dimen>
+ <dimen name="focus_inner_outline_radius">14dp</dimen>
+ <dimen name="focus_outline_stroke_width">2dp</dimen>
+ <!-- -4dp for double stroke focus outlines, -2dp for padding between outline and widget,
+ make it negative to outset the rect and leave space for drawing focus outline and padding -->
+ <dimen name="focus_rect_widget_outsets">-6dp</dimen>
<!-- Workspace grid visualization parameters -->
<dimen name="grid_visualization_rounding_radius">16dp</dimen>
diff --git a/res/values/id.xml b/res/values/id.xml
index 198496f..59813ad 100644
--- a/res/values/id.xml
+++ b/res/values/id.xml
@@ -40,6 +40,7 @@
<item type="id" name="cache_entry_tag_id" />
<item type="id" name="saved_clip_children_tag_id" />
+ <item type="id" name="saved_clip_to_padding_tag_id" />
<item type="id" name="saved_floating_widget_foreground" />
<item type="id" name="saved_floating_widget_background" />
diff --git a/res/values/strings.xml b/res/values/strings.xml
index aaef15b..eb3441e 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -64,6 +64,12 @@
<!-- Spoken text for a screen reader. The placeholder text is the widget name.
[CHAR_LIMIT=none]-->
<string name="widget_preview_context_description"><xliff:g id="widget_name" example="Calendar month view">%1$s</xliff:g> widget</string>
+ <!-- Spoken text for a screen reader. The first placeholder text is the widget name, the
+ remaining placeholders are for the widget dimensions.
+ [CHAR_LIMIT=none]-->
+ <string name="widget_preview_name_and_dims_content_description">
+ <xliff:g id="widget_name" example="Calendar month view">%1$s</xliff:g> widget, %2$d wide by %3$d high
+ </string>
<!-- Message to tell the user to press and hold a widget/icon to add it to the home screen.
[CHAR LIMIT=NONE] -->
<string name="add_item_request_drag_hint">Touch & hold the widget to move it around the home screen</string>
@@ -75,12 +81,14 @@
<!-- Widget suggestions header title in the full widgets picker for large screen devices
in landscape mode. [CHAR_LIMIT=50] -->
<string name="suggested_widgets_header_title">Suggestions</string>
- <string name="productivity_widget_recommendation_category_label">Your Daily Essentials</string>
- <string name="news_widget_recommendation_category_label">News For You</string>
+ <string name="productivity_widget_recommendation_category_label">Essentials</string>
+ <string name="news_widget_recommendation_category_label">News & magazines</string>
<string name="social_and_entertainment_widget_recommendation_category_label">Your Chill Zone</string>
- <string name="fitness_widget_recommendation_category_label">Reach Your Fitness Goals</string>
- <string name="weather_widget_recommendation_category_label">Stay Ahead of the Weather</string>
- <string name="others_widget_recommendation_category_label">You Might Also Like</string>
+ <string name="entertainment_widget_recommendation_category_label">Entertainment</string>
+ <string name="social_widget_recommendation_category_label">Social</string>
+ <string name="fitness_widget_recommendation_category_label">Health & fitness</string>
+ <string name="weather_widget_recommendation_category_label">Weather</string>
+ <string name="others_widget_recommendation_category_label">Suggested for you</string>
<!-- accessibilityPaneTitle for the right pane when showing suggested widgets. -->
<string name="widget_picker_right_pane_accessibility_title"><xliff:g id="selected_header" example="Calendar">%1$s</xliff:g> widgets on right, search and options on left</string>
<!-- Label for showing the number of widgets an app has in the full widgets picker.
@@ -123,6 +131,12 @@
<!-- A widget category label for grouping widgets related to note taking. [CHAR_LIMIT=30] -->
<string name="widget_category_note_taking">Note-taking</string>
+ <!-- Text on the button that adds a widget to the home screen. [CHAR_LIMIT=15] -->
+ <string name="widget_add_button_label">Add</string>
+ <!-- Accessibility content description for the button that adds a widget to the home screen. The
+ placeholder text is the widget name. [CHAR_LIMIT=none] -->
+ <string name="widget_add_button_content_description">Add <xliff:g id="widget_name" example="Calendar month view">%1$s</xliff:g> widget</string>
+
<!-- Title of a dialog. This dialog lets a user know how they can use widgets on their phone.
[CHAR_LIMIT=NONE] -->
<string name="widget_education_header">Useful info at your fingertips</string>
@@ -435,6 +449,8 @@
<string name="work_profile_edu_work_apps">Work apps are badged and visible to your IT admin</string>
<!-- Action label to finish work profile edu-->
<string name="work_profile_edu_accept">Got it</string>
+ <!-- Info icon unicode for alpha scroller when work edu card is present -->
+ <string name="work_profile_edu_section" translatable="false">\u24D8</string>
<!--- heading shown when user opens work apps tab while work apps are paused -->
<string name="work_apps_paused_title">Work apps are paused</string>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 401155d..e35d241 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -52,8 +52,9 @@
<item name="workspaceAmbientShadowColor">#40000000</item>
<item name="workspaceKeyShadowColor">#89000000</item>
<item name="widgetsTheme">@style/WidgetContainerTheme</item>
- <item name="focusOutlineColor">@color/material_color_on_secondary_container</item>
- <item name="folderPaginationColor">@color/folder_pagination_color_light</item>
+ <item name="pageIndicatorDotColor">@color/page_indicator_dot_color_light</item>
+ <item name="focusOutlineColor">@color/material_color_secondary_fixed</item>
+ <item name="focusInnerOutlineColor">@color/material_color_on_secondary_fixed_variant</item>
<item name="folderPreviewColor">@color/folder_preview_light</item>
<item name="folderBackgroundColor">@color/folder_background_light</item>
<item name="folderIconBorderColor">?android:attr/colorPrimary</item>
@@ -164,7 +165,7 @@
<item name="popupShadeThird">@color/popup_shade_third_dark</item>
<item name="notificationDotColor">@color/notification_dot_color_dark</item>
<item name="widgetsTheme">@style/WidgetContainerTheme.Dark</item>
- <item name="folderPaginationColor">@color/folder_pagination_color_dark</item>
+ <item name="pageIndicatorDotColor">@color/page_indicator_dot_color_dark</item>
<item name="folderPreviewColor">@color/folder_preview_dark</item>
<item name="folderBackgroundColor">@color/folder_background_dark</item>
<item name="folderIconBorderColor">?android:attr/colorPrimary</item>
@@ -261,6 +262,10 @@
@color/widget_picker_unselected_tab_text_color_light</item>
<item name="widgetPickerCollapseHandleColor">
@color/widget_picker_collapse_handle_color_light</item>
+ <item name="widgetPickerAddButtonBackgroundColor">
+ @color/widget_picker_add_button_background_color_light</item>
+ <item name="widgetPickerAddButtonTextColor">
+ @color/widget_picker_add_button_text_color_light</item>
</style>
<style name="WidgetContainerTheme.Dark" parent="AppTheme.Dark">
<item name="android:colorEdgeEffect">?android:attr/textColorSecondary</item>
@@ -292,6 +297,10 @@
@color/widget_picker_unselected_tab_text_color_dark</item>
<item name="widgetPickerCollapseHandleColor">
@color/widget_picker_collapse_handle_color_dark</item>
+ <item name="widgetPickerAddButtonBackgroundColor">
+ @color/widget_picker_add_button_background_color_dark</item>
+ <item name="widgetPickerAddButtonTextColor">
+ @color/widget_picker_add_button_text_color_dark</item>
</style>
<style name="FastScrollerPopup" parent="@android:style/TextAppearance.DeviceDefault.DialogWindowTitle">
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index 4f071fe..3ee1c61 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -285,9 +285,6 @@
mDotParams.scale = 0f;
mForceHideDot = false;
setBackground(null);
- if (FeatureFlags.enableTwolineAllapps() || FeatureFlags.ENABLE_TWOLINE_DEVICESEARCH.get()) {
- setMaxLines(1);
- }
setTag(null);
if (mIconLoadRequest != null) {
@@ -299,6 +296,7 @@
setAlpha(1);
setScaleY(1);
setTranslationY(0);
+ setMaxLines(1);
setVisibility(VISIBLE);
}
@@ -428,10 +426,9 @@
* Only if actual text can be displayed in two line, the {@code true} value will be effective.
*/
protected boolean shouldUseTwoLine() {
- return FeatureFlags.enableTwolineAllapps() && isCurrentLanguageEnglish()
- && (mDisplay == DISPLAY_ALL_APPS || mDisplay == DISPLAY_PREDICTION_ROW)
- && (!Flags.enableTwolineToggle() || (Flags.enableTwolineToggle()
- && LauncherPrefs.ENABLE_TWOLINE_ALLAPPS_TOGGLE.get(getContext())));
+ return isCurrentLanguageEnglish() && (mDisplay == DISPLAY_ALL_APPS
+ || mDisplay == DISPLAY_PREDICTION_ROW) && (Flags.enableTwolineToggle()
+ && LauncherPrefs.ENABLE_TWOLINE_ALLAPPS_TOGGLE.get(getContext()));
}
protected boolean isCurrentLanguageEnglish() {
@@ -916,7 +913,8 @@
/** Applies the given progress level to the this icon's progress bar. */
@Nullable
public PreloadIconDrawable applyProgressLevel() {
- if (!(getTag() instanceof ItemInfoWithIcon)) {
+ if (!(getTag() instanceof ItemInfoWithIcon)
+ || !((ItemInfoWithIcon) getTag()).isActiveArchive()) {
return null;
}
@@ -973,6 +971,7 @@
return info.isDisabled() || info.isPendingDownload();
}
+
public void applyDotState(ItemInfo itemInfo, boolean animate) {
if (mIcon instanceof FastBitmapDrawable) {
boolean wasDotted = mDotInfo != null;
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index 4b4bdc2..3ddc7aa 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -18,6 +18,7 @@
import static com.android.app.animation.Interpolators.LINEAR;
import static com.android.launcher3.Flags.enableOverviewIconMenu;
+import static com.android.launcher3.Flags.enableScalingRevealHomeAnimation;
import static com.android.launcher3.InvariantDeviceProfile.INDEX_DEFAULT;
import static com.android.launcher3.InvariantDeviceProfile.INDEX_LANDSCAPE;
import static com.android.launcher3.InvariantDeviceProfile.INDEX_TWO_PANEL_LANDSCAPE;
@@ -101,6 +102,7 @@
public final boolean transposeLayoutWithOrientation;
public final boolean isMultiDisplay;
public final boolean isTwoPanels;
+ public boolean isPredictiveBackSwipe;
public final boolean isQsbInline;
// Device properties in current orientation
@@ -436,7 +438,11 @@
if (isMultiDisplay && !ENABLE_MULTI_DISPLAY_PARTIAL_DEPTH.get()) {
// TODO(b/259893832): Revert to use maxWallpaperScale to calculate bottomSheetDepth
// when screen recorder bug is fixed.
- bottomSheetDepth = 1f;
+ if (enableScalingRevealHomeAnimation()) {
+ bottomSheetDepth = 0.3f;
+ } else {
+ bottomSheetDepth = 1f;
+ }
} else {
// The goal is to set wallpaper to zoom at workspaceContentScale when in AllApps.
// When depth is 0, wallpaper zoom is set to maxWallpaperScale.
@@ -1233,9 +1239,8 @@
if (isVerticalLayout && !mIsResponsiveGrid) {
hideWorkspaceLabelsIfNotEnoughSpace();
}
- if (FeatureFlags.enableTwolineAllapps()
- && (!Flags.enableTwolineToggle() || (Flags.enableTwolineToggle()
- && LauncherPrefs.ENABLE_TWOLINE_ALLAPPS_TOGGLE.get(context)))) {
+ if ((Flags.enableTwolineToggle()
+ && LauncherPrefs.ENABLE_TWOLINE_ALLAPPS_TOGGLE.get(context))) {
// Add extra textHeight to the existing allAppsCellHeight.
allAppsCellHeightPx += Utilities.calculateTextHeight(allAppsIconTextSizePx);
}
diff --git a/src/com/android/launcher3/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java
index 42d4d50..2e0f676 100644
--- a/src/com/android/launcher3/InvariantDeviceProfile.java
+++ b/src/com/android/launcher3/InvariantDeviceProfile.java
@@ -72,6 +72,7 @@
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
+import java.util.Objects;
import java.util.stream.Collectors;
public class InvariantDeviceProfile {
@@ -578,6 +579,45 @@
}
/**
+ * Returns the GridOption associated to the given file name or null if the fileName is not
+ * supported.
+ * Ej, launcher.db -> "normal grid", launcher_4_by_4.db -> "practical grid"
+ */
+ public GridOption getGridOptionFromFileName(Context context, String fileName) {
+ return parseAllGridOptions(context).stream()
+ .filter(gridOption -> Objects.equals(gridOption.dbFile, fileName))
+ .findFirst()
+ .orElse(null);
+ }
+
+ /**
+ * Returns the name of the given size on the current device or empty string if the size is not
+ * supported. Ej. 4x4 -> normal, 5x4 -> practical, etc.
+ * (Note: the name of the grid can be different for the same grid size depending of
+ * the values of the InvariantDeviceProfile)
+ *
+ */
+ public String getGridNameFromSize(Context context, Point size) {
+ return parseAllGridOptions(context).stream()
+ .filter(gridOption -> gridOption.numColumns == size.x
+ && gridOption.numRows == size.y)
+ .map(gridOption -> gridOption.name)
+ .findFirst()
+ .orElse("");
+ }
+
+ /**
+ * Returns the grid option for the given gridName on the current device (Note: the gridOption
+ * be different for the same gridName depending on the values of the InvariantDeviceProfile).
+ */
+ public GridOption getGridOptionFromName(Context context, String gridName) {
+ return parseAllGridOptions(context).stream()
+ .filter(gridOption -> Objects.equals(gridOption.name, gridName))
+ .findFirst()
+ .orElse(null);
+ }
+
+ /**
* @return all the grid options that can be shown on the device
*/
public List<GridOption> parseAllGridOptions(Context context) {
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index c1ebbe5..e7d2843 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -259,6 +259,7 @@
import com.android.launcher3.widget.custom.CustomWidgetManager;
import com.android.launcher3.widget.model.WidgetsListBaseEntry;
import com.android.launcher3.widget.picker.WidgetsFullSheet;
+import com.android.launcher3.widget.util.WidgetSizes;
import com.android.systemui.plugins.LauncherOverlayPlugin;
import com.android.systemui.plugins.PluginListener;
import com.android.systemui.plugins.shared.LauncherOverlayManager;
@@ -829,7 +830,7 @@
announceForAccessibility(R.string.item_added_to_workspace);
break;
case REQUEST_CREATE_APPWIDGET:
- completeAddAppWidget(appWidgetId, info, null, null, false, null);
+ completeAddAppWidget(appWidgetId, info, null, null, false, true, null);
break;
case REQUEST_RECONFIGURE_APPWIDGET:
getStatsLogManager().logger().withItemInfo(info).log(LAUNCHER_WIDGET_RECONFIGURED);
@@ -1027,7 +1028,7 @@
requestArgs.getWidgetHandler().getProviderInfo(this));
boundWidget = layout;
onCompleteRunnable = () -> {
- completeAddAppWidget(appWidgetId, requestArgs, layout, null, false, null);
+ completeAddAppWidget(appWidgetId, requestArgs, layout, null, false, true, null);
if (!isInState(EDIT_MODE)) {
mStateManager.goToState(NORMAL, SPRING_LOADED_EXIT_DELAY);
}
@@ -1458,7 +1459,8 @@
@Thunk
void completeAddAppWidget(int appWidgetId, ItemInfo itemInfo,
@Nullable AppWidgetHostView hostView, LauncherAppWidgetProviderInfo appWidgetInfo,
- boolean showPendingWidget, @Nullable Bitmap widgetPreviewBitmap) {
+ boolean showPendingWidget, boolean updateWidgetSize,
+ @Nullable Bitmap widgetPreviewBitmap) {
if (appWidgetInfo == null) {
appWidgetInfo = mAppWidgetManager.getLauncherAppWidgetInfo(appWidgetId,
@@ -1483,11 +1485,10 @@
if (showPendingWidget) {
launcherInfo.restoreStatus = LauncherAppWidgetInfo.FLAG_UI_NOT_READY;
PendingAppWidgetHostView pendingAppWidgetHostView = new PendingAppWidgetHostView(
- this, mAppWidgetHolder, launcherInfo, appWidgetInfo);
- pendingAppWidgetHostView.setPreviewBitmap(widgetPreviewBitmap);
+ this, mAppWidgetHolder, launcherInfo, appWidgetInfo, widgetPreviewBitmap);
hostView = pendingAppWidgetHostView;
} else if (hostView instanceof PendingAppWidgetHostView) {
- ((PendingAppWidgetHostView) hostView).setPreviewBitmap(null);
+ ((PendingAppWidgetHostView) hostView).setPreviewBitmapAndUpdateBackground(null);
// User has selected a widget config and exited the config activity, we can trigger
// re-inflation of PendingAppWidgetHostView to replace it with
// LauncherAppWidgetHostView in workspace.
@@ -1500,8 +1501,14 @@
reInflatedHostView,
(LauncherAppWidgetInfo) reInflatedHostView.getTag(),
presenterPos);
+ // We always update widget size after re-inflating PendingAppWidgetHostView
+ WidgetSizes.updateWidgetSizeRanges(
+ reInflatedHostView, this, itemInfo.spanX, itemInfo.spanY);
return;
}
+ if (updateWidgetSize) {
+ WidgetSizes.updateWidgetSizeRanges(hostView, this, itemInfo.spanX, itemInfo.spanY);
+ }
if (itemInfo instanceof PendingAddWidgetInfo) {
launcherInfo.sourceContainer = ((PendingAddWidgetInfo) itemInfo).sourceContainer;
} else if (itemInfo instanceof PendingRequestArgs) {
@@ -1629,7 +1636,8 @@
} else if (INTENT_ACTION_ALL_APPS_TOGGLE.equals(intent.getAction())) {
toggleAllAppsFromIntent(alreadyOnHome);
} else if (Intent.ACTION_SHOW_WORK_APPS.equals(intent.getAction())) {
- showAllAppsWorkTabFromIntent(alreadyOnHome);
+ showAllAppsWithSelectedTabFromIntent(alreadyOnHome,
+ ActivityAllAppsContainerView.AdapterHolder.WORK);
}
TraceHelper.INSTANCE.endSection();
@@ -1661,13 +1669,19 @@
}
protected void showAllAppsFromIntent(boolean alreadyOnHome) {
- AbstractFloatingView.closeAllOpenViews(this);
- getStateManager().goToState(ALL_APPS, alreadyOnHome);
+ showAllAppsWithSelectedTabFromIntent(alreadyOnHome,
+ ActivityAllAppsContainerView.AdapterHolder.MAIN);
}
- private void showAllAppsWorkTabFromIntent(boolean alreadyOnHome) {
- showAllAppsFromIntent(alreadyOnHome);
- mAppsView.switchToTab(ActivityAllAppsContainerView.AdapterHolder.WORK);
+ private void showAllAppsWithSelectedTabFromIntent(boolean alreadyOnHome, int tab) {
+ AbstractFloatingView.closeAllOpenViews(this);
+ getStateManager().goToState(ALL_APPS, alreadyOnHome);
+ if (mAppsView.isSearching()) {
+ mAppsView.reset(alreadyOnHome);
+ }
+ if (mAppsView.getCurrentPage() != tab) {
+ mAppsView.switchToTab(tab);
+ }
}
/**
@@ -1822,7 +1836,9 @@
if (isActivityStarted) {
DragView dropView = getDragLayer().clearAnimatedView();
if (dropView != null && dropView.containsAppWidgetHostView()) {
- widgetPreviewBitmap = getBitmapFromView(dropView.getContentView());
+ // Extracting Bitmap from dropView instead of its content view produces the correct
+ // bitmap.
+ widgetPreviewBitmap = getBitmapFromView(dropView);
}
}
@@ -1831,7 +1847,7 @@
: () -> mStateManager.goToState(NORMAL, SPRING_LOADED_EXIT_DELAY);
completeAddAppWidget(appWidgetId, info, boundWidget,
addFlowHandler.getProviderInfo(this), addFlowHandler.needsConfigure(),
- widgetPreviewBitmap);
+ false, widgetPreviewBitmap);
mWorkspace.removeExtraEmptyScreenDelayed(delay, false, onComplete);
}
@@ -2357,7 +2373,8 @@
* Similar to {@link #getFirstMatch} but optimized to finding a suitable view for the app close
* animation.
*
- * @param preferredItemId The id of the preferred item to match to if it exists.
+ * @param preferredItemId The id of the preferred item to match to if it exists,
+ * or ItemInfo#NO_MATCHING_ID if you want to not match by item id
* @param packageName The package name of the app to match.
* @param user The user of the app to match.
* @param supportsAllAppsState If true and we are in All Apps state, looks for view in All Apps.
@@ -2721,7 +2738,7 @@
private void updateDisallowBack() {
if (Flags.enableDesktopWindowingMode()) {
- // Do not disable back in launcher when prototype behavior is enabled
+ // TODO(b/330183377) disable back in launcher when when we productionize
return;
}
LauncherRootView rv = getRootView();
diff --git a/src/com/android/launcher3/LauncherPrefs.kt b/src/com/android/launcher3/LauncherPrefs.kt
index b0a644b..27e084c 100644
--- a/src/com/android/launcher3/LauncherPrefs.kt
+++ b/src/com/android/launcher3/LauncherPrefs.kt
@@ -309,6 +309,13 @@
val LONG_PRESS_NAV_HANDLE_SLOP_PERCENTAGE =
nonRestorableItem("LPNH_SLOP_PERCENTAGE", 100, EncryptionType.MOVE_TO_DEVICE_PROTECTED)
@JvmField
+ val LONG_PRESS_NAV_HANDLE_EXTRA_TOUCH_WIDTH_DP =
+ nonRestorableItem(
+ "LPNH_EXTRA_TOUCH_WIDTH_DP",
+ 0,
+ EncryptionType.MOVE_TO_DEVICE_PROTECTED
+ )
+ @JvmField
val LONG_PRESS_NAV_HANDLE_TIMEOUT_MS =
nonRestorableItem(
"LPNH_TIMEOUT_MS",
@@ -349,8 +356,8 @@
@JvmField
val PRIVATE_SPACE_APPS =
nonRestorableItem("pref_private_space_apps", 0, EncryptionType.MOVE_TO_DEVICE_PROTECTED)
- @JvmField val ENABLE_TWOLINE_ALLAPPS_TOGGLE =
- backedUpItem("pref_enable_two_line_toggle", false)
+ @JvmField
+ val ENABLE_TWOLINE_ALLAPPS_TOGGLE = backedUpItem("pref_enable_two_line_toggle", false)
@JvmField
val THEMED_ICONS =
backedUpItem(Themes.KEY_THEMED_ICONS, false, EncryptionType.MOVE_TO_DEVICE_PROTECTED)
diff --git a/src/com/android/launcher3/LauncherSettings.java b/src/com/android/launcher3/LauncherSettings.java
index 34ebaf2..84b8ba1 100644
--- a/src/com/android/launcher3/LauncherSettings.java
+++ b/src/com/android/launcher3/LauncherSettings.java
@@ -139,6 +139,11 @@
public static final int ITEM_TYPE_SEARCH_ACTION = 9;
/**
+ * Private space install app button.
+ */
+ public static final int ITEM_TYPE_PRIVATE_SPACE_INSTALL_APP_BUTTON = 11;
+
+ /**
* The custom icon bitmap.
* <P>Type: BLOB</P>
*/
@@ -206,6 +211,8 @@
case ITEM_TYPE_TASK: return "TASK";
case ITEM_TYPE_QSB: return "QSB";
case ITEM_TYPE_APP_PAIR: return "APP_PAIR";
+ case ITEM_TYPE_PRIVATE_SPACE_INSTALL_APP_BUTTON:
+ return "PRIVATE_SPACE_INSTALL_APP_BUTTON";
default: return String.valueOf(type);
}
}
diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java
index 1fede56..1c23644 100644
--- a/src/com/android/launcher3/PagedView.java
+++ b/src/com/android/launcher3/PagedView.java
@@ -826,7 +826,9 @@
// or right edge for RTL.
final int pageScroll =
mIsRtl ? childPrimaryEnd - scrollOffsetEnd : childStart - scrollOffsetStart;
- if (outPageScrolls[i] != pageScroll) {
+ // If there's more than one panel, only update scroll on leftmost panel.
+ if (outPageScrolls[i] != pageScroll
+ && (panelCount <= 1 || i == getLeftmostVisiblePageForIndex(i))) {
pageScrollChanged = true;
outPageScrolls[i] = pageScroll;
}
@@ -842,7 +844,7 @@
if (panelCount > 1) {
for (int i = 0; i < childCount; i++) {
- // In case we have multiple panels, always use left most panel's page scroll for all
+ // In case we have multiple panels, always use leftmost panel's page scroll for all
// panels on the screen.
int adjustedScroll = outPageScrolls[getLeftmostVisiblePageForIndex(i)];
if (outPageScrolls[i] != adjustedScroll) {
diff --git a/src/com/android/launcher3/UtilitiesKt.kt b/src/com/android/launcher3/UtilitiesKt.kt
new file mode 100644
index 0000000..a207d57
--- /dev/null
+++ b/src/com/android/launcher3/UtilitiesKt.kt
@@ -0,0 +1,159 @@
+/*
+ * 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.view.View
+import android.view.ViewGroup
+import android.view.ViewParent
+
+object UtilitiesKt {
+
+ /**
+ * Modify [ViewGroup]'s attribute with type [T]. The overridden attribute is saved by calling
+ * [View.setTag] and can be later restored by [View.getTag].
+ *
+ * @param <T> type of [ViewGroup] attribute. For example, [T] is [Boolean] if modifying
+ * [ViewGroup.setClipChildren]
+ */
+ abstract class ViewGroupAttrModifier<T>(
+ private val targetAttrValue: T,
+ private val tagKey: Int
+ ) {
+ /**
+ * If [targetAttrValue] is different from existing view attribute returned from
+ * [getAttribute], this method will save existing attribute by calling [ViewGroup.setTag].
+ * Then call [setAttribute] to set attribute with [targetAttrValue].
+ */
+ fun saveAndChangeAttribute(viewGroup: ViewGroup) {
+ val oldAttrValue = getAttribute(viewGroup)
+ if (oldAttrValue !== targetAttrValue) {
+ viewGroup.setTag(tagKey, oldAttrValue)
+ setAttribute(viewGroup, targetAttrValue)
+ }
+ }
+
+ /** Restore saved attribute in [saveAndChangeAttribute] by calling [ViewGroup.getTag]. */
+ @Suppress("UNCHECKED_CAST")
+ fun restoreAttribute(viewGroup: ViewGroup) {
+ val oldAttrValue: T = viewGroup.getTag(tagKey) as T ?: return
+ setAttribute(viewGroup, oldAttrValue)
+ viewGroup.setTag(tagKey, null)
+ }
+
+ /** Subclass will override this method to decide how to get [ViewGroup] attribute. */
+ abstract fun getAttribute(viewGroup: ViewGroup): T
+
+ /** Subclass will override this method to decide how to set [ViewGroup] attribute. */
+ abstract fun setAttribute(viewGroup: ViewGroup, attr: T)
+ }
+
+ /** [ViewGroupAttrModifier] to call [ViewGroup.setClipChildren] to false. */
+ @JvmField
+ val CLIP_CHILDREN_FALSE_MODIFIER: ViewGroupAttrModifier<Boolean> =
+ object : ViewGroupAttrModifier<Boolean>(false, R.id.saved_clip_children_tag_id) {
+ override fun getAttribute(viewGroup: ViewGroup): Boolean {
+ return viewGroup.clipChildren
+ }
+
+ override fun setAttribute(viewGroup: ViewGroup, clipChildren: Boolean) {
+ viewGroup.clipChildren = clipChildren
+ }
+ }
+
+ /** [ViewGroupAttrModifier] to call [ViewGroup.setClipToPadding] to false. */
+ @JvmField
+ val CLIP_TO_PADDING_FALSE_MODIFIER: ViewGroupAttrModifier<Boolean> =
+ object : ViewGroupAttrModifier<Boolean>(false, R.id.saved_clip_to_padding_tag_id) {
+ override fun getAttribute(viewGroup: ViewGroup): Boolean {
+ return viewGroup.clipToPadding
+ }
+
+ override fun setAttribute(viewGroup: ViewGroup, clipToPadding: Boolean) {
+ viewGroup.clipToPadding = clipToPadding
+ }
+ }
+
+ /**
+ * Recursively call [ViewGroupAttrModifier.saveAndChangeAttribute] from [View] to its parent
+ * (direct or indirect) inclusive.
+ *
+ * [ViewGroupAttrModifier.saveAndChangeAttribute] will save the existing attribute value on each
+ * view with [View.setTag], which can be restored in [restoreAttributesOnViewTree].
+ *
+ * Note that if parent is null or not a parent of the view, this method will be applied all the
+ * way to root view.
+ *
+ * @param v child view
+ * @param parent direct or indirect parent of child view
+ * @param modifiers list of [ViewGroupAttrModifier] to modify view attribute
+ */
+ @JvmStatic
+ fun modifyAttributesOnViewTree(
+ v: View?,
+ parent: ViewParent?,
+ vararg modifiers: ViewGroupAttrModifier<*>
+ ) {
+ if (v == null) {
+ return
+ }
+ if (v is ViewGroup) {
+ for (modifier in modifiers) {
+ modifier.saveAndChangeAttribute(v)
+ }
+ }
+ if (v === parent) {
+ return
+ }
+ if (v.parent is View) {
+ modifyAttributesOnViewTree(v.parent as View, parent, *modifiers)
+ }
+ }
+
+ /**
+ * Recursively call [ViewGroupAttrModifier.restoreAttribute]} to restore view attributes
+ * previously saved in [ViewGroupAttrModifier.saveAndChangeAttribute] on view to its parent
+ * (direct or indirect) inclusive.
+ *
+ * Note that if parent is null or not a parent of the view, this method will be applied all the
+ * way to root view.
+ *
+ * @param v child view
+ * @param parent direct or indirect parent of child view
+ * @param modifiers list of [ViewGroupAttrModifier] to restore view attributes
+ */
+ @JvmStatic
+ fun restoreAttributesOnViewTree(
+ v: View?,
+ parent: ViewParent?,
+ vararg modifiers: ViewGroupAttrModifier<*>
+ ) {
+ if (v == null) {
+ return
+ }
+ if (v is ViewGroup) {
+ for (modifier in modifiers) {
+ modifier.restoreAttribute(v)
+ }
+ }
+ if (v === parent) {
+ return
+ }
+ if (v.parent is View) {
+ restoreAttributesOnViewTree(v.parent as View, parent, *modifiers)
+ }
+ }
+}
diff --git a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
index e861d38..66b8216 100644
--- a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
+++ b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
@@ -22,6 +22,8 @@
import android.view.View;
import android.view.accessibility.AccessibilityEvent;
+import androidx.annotation.Nullable;
+
import com.android.launcher3.BubbleTextView;
import com.android.launcher3.ButtonDropTarget;
import com.android.launcher3.CellLayout;
@@ -53,11 +55,13 @@
import com.android.launcher3.views.OptionsPopupView;
import com.android.launcher3.views.OptionsPopupView.OptionItem;
import com.android.launcher3.widget.LauncherAppWidgetHostView;
+import com.android.launcher3.widget.PendingAddWidgetInfo;
import com.android.launcher3.widget.util.WidgetSizes;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import java.util.function.Consumer;
public class LauncherAccessibilityDelegate extends BaseAccessibilityDelegate<Launcher> {
@@ -173,7 +177,7 @@
} else if (action == MOVE) {
return beginAccessibleDrag(host, item, fromKeyboard);
} else if (action == ADD_TO_WORKSPACE) {
- return addToWorkspace(item, true);
+ return addToWorkspace(item, true /*accessibility*/, null /*finishCallback*/);
} else if (action == MOVE_TO_WORKSPACE) {
return moveToWorkspace(item);
} else if (action == RESIZE) {
@@ -384,13 +388,19 @@
* @param item item to be added
* @param accessibility true if the first item to be added to the workspace
* should be focused for accessibility.
+ * @param finishCallback Callback which will be run after this item has been added
+ * and the view has been transitioned to the workspace, or on failure.
*
* @return true if the item could be successfully added
*/
- public boolean addToWorkspace(ItemInfo item, boolean accessibility) {
+ public boolean addToWorkspace(ItemInfo item, boolean accessibility,
+ @Nullable Consumer<Boolean> finishCallback) {
final int[] coordinates = new int[2];
final int screenId = findSpaceOnWorkspace(item, coordinates);
if (screenId == -1) {
+ if (finishCallback != null) {
+ finishCallback.accept(false /*success*/);
+ }
return false;
}
mContext.getStateManager().goToState(NORMAL, true, forSuccessCallback(() -> {
@@ -400,12 +410,18 @@
LauncherSettings.Favorites.CONTAINER_DESKTOP,
screenId, coordinates[0], coordinates[1]);
- bindItem(item, accessibility);
+ bindItem(item, accessibility, finishCallback);
announceConfirmation(R.string.item_added_to_workspace);
} else if (item instanceof PendingAddItemInfo) {
PendingAddItemInfo info = (PendingAddItemInfo) item;
+ if (info instanceof PendingAddWidgetInfo widgetInfo
+ && widgetInfo.bindOptions == null) {
+ widgetInfo.bindOptions = widgetInfo.getDefaultSizeOptions(mContext);
+ }
Workspace<?> workspace = mContext.getWorkspace();
- workspace.snapToPage(workspace.getPageIndexForScreenId(screenId));
+ workspace.post(
+ () -> workspace.snapToPage(workspace.getPageIndexForScreenId(screenId))
+ );
mContext.addPendingItem(info, LauncherSettings.Favorites.CONTAINER_DESKTOP,
screenId, coordinates, info.spanX, info.spanY);
} else if (item instanceof WorkspaceItemInfo) {
@@ -413,7 +429,7 @@
mContext.getModelWriter().addItemToDatabase(info,
LauncherSettings.Favorites.CONTAINER_DESKTOP,
screenId, coordinates[0], coordinates[1]);
- bindItem(info, accessibility);
+ bindItem(info, accessibility, finishCallback);
} else if (item instanceof FolderInfo fi) {
Workspace<?> workspace = mContext.getWorkspace();
workspace.snapToPage(workspace.getPageIndexForScreenId(screenId));
@@ -423,23 +439,30 @@
fi.contents.forEach(member -> {
mContext.getModelWriter().addItemToDatabase(member, fi.id, -1, -1, -1);
});
- bindItem(fi, accessibility);
+ bindItem(fi, accessibility, finishCallback);
}
}));
return true;
}
- private void bindItem(ItemInfo item, boolean focusForAccessibility) {
+ private void bindItem(ItemInfo item, boolean focusForAccessibility,
+ @Nullable Consumer<Boolean> finishCallback) {
View view = mContext.getItemInflater().inflateItem(item, mContext.getModelWriter());
if (view == null) {
+ if (finishCallback != null) {
+ finishCallback.accept(false /*success*/);
+ }
return;
}
- AnimatorSet anim = null;
- if (focusForAccessibility) {
- anim = new AnimatorSet();
- anim.addListener(forEndCallback(
- () -> view.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED)));
- }
+ AnimatorSet anim = new AnimatorSet();
+ anim.addListener(forEndCallback((success) -> {
+ if (focusForAccessibility) {
+ view.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
+ }
+ if (finishCallback != null) {
+ finishCallback.accept(success);
+ }
+ }));
mContext.bindInflatedItems(Collections.singletonList(Pair.create(item, view)), anim);
}
diff --git a/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java b/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
index 6acfcd0..fbeab4e 100644
--- a/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
@@ -189,6 +189,7 @@
private float mBottomSheetAlpha = 1f;
private boolean mForceBottomSheetVisible;
private int mTabsProtectionAlpha;
+ private float mTotalHeaderProtectionHeight;
@Nullable private AllAppsTransitionController mAllAppsTransitionController;
private PrivateSpaceHeaderViewController mPrivateSpaceHeaderViewController;
@@ -1372,7 +1373,8 @@
}
@Override
- public void drawOnScrimWithScale(Canvas canvas, float scale) {
+ public void drawOnScrimWithScaleAndBottomOffset(
+ Canvas canvas, float scale, @Px int bottomOffsetPx) {
final View panel = mBottomSheetBackground;
final boolean hasBottomSheet = panel.getVisibility() == VISIBLE;
final float translationY = ((View) panel.getParent()).getTranslationY();
@@ -1384,6 +1386,7 @@
final float topWithScale = topNoScale + verticalScaleOffset;
final float leftWithScale = panel.getLeft() + horizontalScaleOffset;
final float rightWithScale = panel.getRight() - horizontalScaleOffset;
+ final float bottomWithOffset = panel.getBottom() + bottomOffsetPx;
// Draw full background panel for tablets.
if (hasBottomSheet) {
mHeaderPaint.setColor(mBottomSheetBackgroundColor);
@@ -1393,7 +1396,7 @@
leftWithScale,
topWithScale,
rightWithScale,
- panel.getBottom());
+ bottomWithOffset);
mTmpPath.reset();
mTmpPath.addRoundRect(mTmpRectF, mBottomSheetCornerRadii, Direction.CW);
canvas.drawPath(mTmpPath, mHeaderPaint);
@@ -1429,9 +1432,11 @@
mTmpPath.reset();
mTmpPath.addRoundRect(mTmpRectF, mBottomSheetCornerRadii, Direction.CW);
canvas.drawPath(mTmpPath, mHeaderPaint);
+ mTotalHeaderProtectionHeight = headerBottomWithScaleOnTablet;
}
} else {
canvas.drawRect(0, 0, canvas.getWidth(), headerBottomWithScaleOnPhone, mHeaderPaint);
+ mTotalHeaderProtectionHeight = headerBottomWithScaleOnPhone;
}
// If tab exist (such as work profile), extend header with tab height
@@ -1461,10 +1466,19 @@
right,
tabBottomWithScale,
mHeaderPaint);
+ mTotalHeaderProtectionHeight = tabBottomWithScale;
}
}
/**
+ * The height of the header protection is dynamically calculated during the time of drawing the
+ * header.
+ */
+ float getHeaderProtectionHeight() {
+ return mTotalHeaderProtectionHeight;
+ }
+
+ /**
* redraws header protection
*/
public void invalidateHeader() {
diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
index e2c5795..3678109 100644
--- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java
+++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
@@ -24,6 +24,9 @@
import static com.android.launcher3.LauncherState.ALL_APPS_CONTENT;
import static com.android.launcher3.LauncherState.BACKGROUND_APP;
import static com.android.launcher3.LauncherState.NORMAL;
+import static com.android.launcher3.UtilitiesKt.CLIP_CHILDREN_FALSE_MODIFIER;
+import static com.android.launcher3.UtilitiesKt.modifyAttributesOnViewTree;
+import static com.android.launcher3.UtilitiesKt.restoreAttributesOnViewTree;
import static com.android.launcher3.anim.PropertySetter.NO_ANIM_PROPERTY_SETTER;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_APPS_BOTTOM_SHEET_FADE;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_APPS_FADE;
@@ -38,12 +41,9 @@
import android.util.FloatProperty;
import android.view.HapticFeedbackConstants;
import android.view.View;
-import android.view.ViewGroup;
-import android.view.ViewParent;
import android.view.animation.Interpolator;
import androidx.annotation.FloatRange;
-import androidx.annotation.Nullable;
import com.android.app.animation.Interpolators;
import com.android.launcher3.DeviceProfile;
@@ -306,9 +306,11 @@
if (hasScaleEffect != mHasScaleEffect) {
mHasScaleEffect = hasScaleEffect;
if (mHasScaleEffect) {
- setClipChildrenOnViewTree(rv, mLauncher.getAppsView(), false);
+ modifyAttributesOnViewTree(rv, mLauncher.getAppsView(),
+ CLIP_CHILDREN_FALSE_MODIFIER);
} else {
- restoreClipChildrenOnViewTree(rv, mLauncher.getAppsView());
+ restoreAttributesOnViewTree(rv, mLauncher.getAppsView(),
+ CLIP_CHILDREN_FALSE_MODIFIER);
}
}
}
@@ -423,79 +425,6 @@
}
/**
- * Recursively call {@link ViewGroup#setClipChildren(boolean)} from {@link View} to ts parent
- * (direct or indirect) inclusive. This method will also save the old clipChildren value on each
- * view with {@link View#setTag(int, Object)}, which can be restored in
- * {@link #restoreClipChildrenOnViewTree(View, ViewParent)}.
- *
- * Note that if parent is null or not a parent of the view, this method will be applied all the
- * way to root view.
- *
- * @param v child view
- * @param parent direct or indirect parent of child view
- * @param clipChildren whether we should clip children
- */
- private static void setClipChildrenOnViewTree(
- @Nullable View v,
- @Nullable ViewParent parent,
- boolean clipChildren) {
- if (v == null) {
- return;
- }
-
- if (v instanceof ViewGroup) {
- ViewGroup viewGroup = (ViewGroup) v;
- boolean oldClipChildren = viewGroup.getClipChildren();
- if (oldClipChildren != clipChildren) {
- v.setTag(R.id.saved_clip_children_tag_id, oldClipChildren);
- viewGroup.setClipChildren(clipChildren);
- }
- }
-
- if (v == parent) {
- return;
- }
-
- if (v.getParent() instanceof View) {
- setClipChildrenOnViewTree((View) v.getParent(), parent, clipChildren);
- }
- }
-
- /**
- * Recursively call {@link ViewGroup#setClipChildren(boolean)} to restore clip children value
- * set in {@link #setClipChildrenOnViewTree(View, ViewParent, boolean)} on view to its parent
- * (direct or indirect) inclusive.
- *
- * Note that if parent is null or not a parent of the view, this method will be applied all the
- * way to root view.
- *
- * @param v child view
- * @param parent direct or indirect parent of child view
- */
- private static void restoreClipChildrenOnViewTree(
- @Nullable View v, @Nullable ViewParent parent) {
- if (v == null) {
- return;
- }
- if (v instanceof ViewGroup) {
- ViewGroup viewGroup = (ViewGroup) v;
- Object viewTag = viewGroup.getTag(R.id.saved_clip_children_tag_id);
- if (viewTag instanceof Boolean) {
- viewGroup.setClipChildren((boolean) viewTag);
- viewGroup.setTag(R.id.saved_clip_children_tag_id, null);
- }
- }
-
- if (v == parent) {
- return;
- }
-
- if (v.getParent() instanceof View) {
- restoreClipChildrenOnViewTree((View) v.getParent(), parent);
- }
- }
-
- /**
* Updates the total scroll range but does not update the UI.
*/
public void setShiftRange(float shiftRange) {
diff --git a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
index fba7537..81cfa86 100644
--- a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
+++ b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
@@ -18,6 +18,8 @@
import static com.android.launcher3.allapps.SectionDecorationInfo.ROUND_BOTTOM_LEFT;
import static com.android.launcher3.allapps.SectionDecorationInfo.ROUND_BOTTOM_RIGHT;
import static com.android.launcher3.allapps.SectionDecorationInfo.ROUND_NOTHING;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_PRIVATE_SPACE_PREINSTALLED_APPS_COUNT;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_PRIVATE_SPACE_USER_INSTALLED_APPS_COUNT;
import android.content.Context;
@@ -26,6 +28,7 @@
import androidx.recyclerview.widget.DiffUtil;
import com.android.launcher3.Flags;
+import com.android.launcher3.R;
import com.android.launcher3.allapps.BaseAllAppsAdapter.AdapterItem;
import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.model.data.ItemInfo;
@@ -270,6 +273,12 @@
addApps = mWorkProviderManager.shouldShowWorkApps();
}
if (addApps) {
+ if (/* education card was added */ position == 1) {
+ // Add work educard section with "info icon" at 0th position.
+ mFastScrollerSections.add(new FastScrollSectionInfo(
+ mActivityContext.getResources().getString(
+ R.string.work_profile_edu_section), 0));
+ }
position = addAppsWithSections(mApps, position);
}
if (Flags.enablePrivateSpace()) {
@@ -342,6 +351,20 @@
Map<Boolean, List<AppInfo>> split = mPrivateApps.stream()
.collect(Collectors.partitioningBy(mPrivateProviderManager
.splitIntoUserInstalledAndSystemApps()));
+
+ // TODO(b/329688630): switch to the pulled LayoutStaticSnapshot atom
+ mActivityContext
+ .getStatsLogManager()
+ .logger()
+ .withCardinality(split.get(true).size())
+ .log(LAUNCHER_PRIVATE_SPACE_USER_INSTALLED_APPS_COUNT);
+
+ mActivityContext
+ .getStatsLogManager()
+ .logger()
+ .withCardinality(split.get(false).size())
+ .log(LAUNCHER_PRIVATE_SPACE_PREINSTALLED_APPS_COUNT);
+
// Add user installed apps
position = addAppsWithSections(split.get(true), position);
// Add system apps separator.
diff --git a/src/com/android/launcher3/allapps/BaseAllAppsAdapter.java b/src/com/android/launcher3/allapps/BaseAllAppsAdapter.java
index ca587c1..5e5795d 100644
--- a/src/com/android/launcher3/allapps/BaseAllAppsAdapter.java
+++ b/src/com/android/launcher3/allapps/BaseAllAppsAdapter.java
@@ -223,10 +223,9 @@
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
switch (viewType) {
case VIEW_TYPE_ICON:
- int layout = (FeatureFlags.enableTwolineAllapps() &&
- (!Flags.enableTwolineToggle() || (Flags.enableTwolineToggle()
- && LauncherPrefs.ENABLE_TWOLINE_ALLAPPS_TOGGLE.get(
- mActivityContext.getApplicationContext()))))
+ int layout = (Flags.enableTwolineToggle()
+ && LauncherPrefs.ENABLE_TWOLINE_ALLAPPS_TOGGLE.get(
+ mActivityContext.getApplicationContext()))
? R.layout.all_apps_icon_twoline : R.layout.all_apps_icon;
BubbleTextView icon = (BubbleTextView) mLayoutInflater.inflate(
layout, parent, false);
diff --git a/src/com/android/launcher3/allapps/PrivateAppsSectionDecorator.java b/src/com/android/launcher3/allapps/PrivateAppsSectionDecorator.java
index 8712b84..18175b6 100644
--- a/src/com/android/launcher3/allapps/PrivateAppsSectionDecorator.java
+++ b/src/com/android/launcher3/allapps/PrivateAppsSectionDecorator.java
@@ -56,7 +56,7 @@
new SectionDecorationHandler.UnionDecorationHandler(
decorationHandler, parent.getPaddingLeft(),
parent.getPaddingRight()));
- unionHandler.addChild(decorationHandler, view, true /* applyBackground */);
+ unionHandler.addChild(decorationHandler, view);
deferredDecorations.put(PRIVATE_APP_SECTION, unionHandler);
} else {
decorationHandler.onFocusDraw(c, view);
diff --git a/src/com/android/launcher3/allapps/PrivateProfileManager.java b/src/com/android/launcher3/allapps/PrivateProfileManager.java
index 1ebd49e..e7bb1d0 100644
--- a/src/com/android/launcher3/allapps/PrivateProfileManager.java
+++ b/src/com/android/launcher3/allapps/PrivateProfileManager.java
@@ -23,15 +23,12 @@
import static com.android.launcher3.allapps.SectionDecorationInfo.ROUND_NOTHING;
import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_PRIVATE_PROFILE_QUIET_MODE_ENABLED;
import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_NOT_PINNABLE;
-import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_PRIVATE_SPACE_INSTALL_APP;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
import static com.android.launcher3.util.SettingsCache.PRIVATE_SPACE_HIDE_WHEN_LOCKED_URI;
import android.content.Context;
import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
import android.os.UserHandle;
import android.os.UserManager;
@@ -43,6 +40,7 @@
import com.android.launcher3.icons.LauncherIcons;
import com.android.launcher3.logging.StatsLogManager;
import com.android.launcher3.model.data.AppInfo;
+import com.android.launcher3.model.data.PrivateSpaceInstallAppButtonInfo;
import com.android.launcher3.pm.UserCache;
import com.android.launcher3.uioverrides.ApiWrapper;
import com.android.launcher3.util.Preconditions;
@@ -60,10 +58,6 @@
*/
public class PrivateProfileManager extends UserProfileManager {
- // TODO (b/324573634): Fix the intent string.
- public static final Intent PRIVATE_SPACE_INTENT = new
- Intent("com.android.settings.action.PRIVATE_SPACE_SETUP_FLOW");
-
private final ActivityAllAppsContainerView<?> mAllApps;
private final Predicate<UserHandle> mPrivateProfileMatcher;
private Set<String> mPreInstalledSystemPackages = new HashSet<>();
@@ -105,13 +99,13 @@
context, com.android.launcher3.R.drawable.private_space_install_app_icon);
BitmapInfo bitmapInfo = LauncherIcons.obtain(context).createIconBitmap(shortcut);
- AppInfo itemInfo = new AppInfo();
+ PrivateSpaceInstallAppButtonInfo itemInfo = new PrivateSpaceInstallAppButtonInfo();
itemInfo.title = context.getResources().getString(R.string.ps_add_button_label);
itemInfo.intent = mAppInstallerIntent;
itemInfo.bitmap = bitmapInfo;
itemInfo.contentDescription = context.getResources().getString(
com.android.launcher3.R.string.ps_add_button_content_description);
- itemInfo.runtimeStatusFlags |= FLAG_PRIVATE_SPACE_INSTALL_APP | FLAG_NOT_PINNABLE;
+ itemInfo.runtimeStatusFlags |= FLAG_NOT_PINNABLE;
BaseAllAppsAdapter.AdapterItem item = new BaseAllAppsAdapter.AdapterItem(VIEW_TYPE_ICON);
item.itemInfo = itemInfo;
@@ -162,7 +156,8 @@
/** Opens the Private Space Settings Page. */
public void openPrivateSpaceSettings() {
if (mPrivateSpaceSettingsAvailable) {
- mAllApps.getContext().startActivity(PRIVATE_SPACE_INTENT);
+ mAllApps.getContext()
+ .startActivity(ApiWrapper.getPrivateSpaceSettingsIntent(mAllApps.getContext()));
}
}
@@ -194,9 +189,8 @@
private void initializePrivateSpaceSettingsState() {
Preconditions.assertNonUiThread();
- ResolveInfo resolveInfo = mAllApps.getContext().getPackageManager()
- .resolveActivity(PRIVATE_SPACE_INTENT, PackageManager.MATCH_SYSTEM_ONLY);
- setPrivateSpaceSettingsAvailable(resolveInfo != null);
+ Intent psSettingsIntent = ApiWrapper.getPrivateSpaceSettingsIntent(mAllApps.getContext());
+ setPrivateSpaceSettingsAvailable(psSettingsIntent != null);
}
private void setPreInstalledSystemPackages() {
diff --git a/src/com/android/launcher3/allapps/PrivateSpaceHeaderViewController.java b/src/com/android/launcher3/allapps/PrivateSpaceHeaderViewController.java
index 6067454..fdc035e 100644
--- a/src/com/android/launcher3/allapps/PrivateSpaceHeaderViewController.java
+++ b/src/com/android/launcher3/allapps/PrivateSpaceHeaderViewController.java
@@ -41,15 +41,18 @@
import android.widget.RelativeLayout;
import android.widget.TextView;
+import androidx.annotation.VisibleForTesting;
import androidx.recyclerview.widget.LinearSmoothScroller;
import androidx.recyclerview.widget.RecyclerView;
import com.android.app.animation.Interpolators;
+import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Flags;
import com.android.launcher3.R;
import com.android.launcher3.allapps.UserProfileManager.UserProfileState;
import com.android.launcher3.anim.AnimatedPropertySetter;
import com.android.launcher3.anim.PropertySetter;
+import com.android.launcher3.views.ActivityContext;
import com.android.launcher3.views.RecyclerViewFastScroller;
import java.util.List;
@@ -59,7 +62,6 @@
* {@link UserProfileState}
*/
public class PrivateSpaceHeaderViewController {
- private static final int EXPAND_SCROLL_DURATION = 2000;
private static final int EXPAND_COLLAPSE_DURATION = 800;
private static final int SETTINGS_OPACITY_DURATION = 160;
private final ActivityAllAppsContainerView mAllApps;
@@ -174,23 +176,24 @@
&& mAllApps.getActiveRecyclerView() == mainAdapterHolder.mRecyclerView) {
// Animate the text and settings icon.
updatePrivateStateAnimator(true, header);
- mAllApps.getActiveRecyclerView().scrollToBottomWithMotion(EXPAND_SCROLL_DURATION);
+ DeviceProfile deviceProfile =
+ ActivityContext.lookupContext(mAllApps.getContext()).getDeviceProfile();
+ AllAppsRecyclerView allAppsRecyclerView = mAllApps.getActiveRecyclerView();
+ scrollForViewToBeVisibleInContainer(allAppsRecyclerView,
+ allAppsRecyclerView.getApps().getAdapterItems(),
+ header.getHeight(), deviceProfile.allAppsCellHeightPx);
}
}
/** Finds the private space header to scroll to and set the private space icons to GONE. */
private void collapse() {
AllAppsRecyclerView allAppsRecyclerView = mAllApps.getActiveRecyclerView();
- for (int i = allAppsRecyclerView.getChildCount() - 1; i > 0; i--) {
- int adapterPosition = allAppsRecyclerView.getChildAdapterPosition(
- allAppsRecyclerView.getChildAt(i));
- List<BaseAllAppsAdapter.AdapterItem> allAppsAdapters = allAppsRecyclerView.getApps()
- .getAdapterItems();
- if (adapterPosition < 0 || adapterPosition >= allAppsAdapters.size()) {
- continue;
- }
+ List<BaseAllAppsAdapter.AdapterItem> appListAdapterItems =
+ allAppsRecyclerView.getApps().getAdapterItems();
+ for (int i = appListAdapterItems.size() - 1; i > 0; i--) {
+ BaseAllAppsAdapter.AdapterItem currentItem = appListAdapterItems.get(i);
// Scroll to the private space header.
- if (allAppsAdapters.get(adapterPosition).viewType == VIEW_TYPE_PRIVATE_SPACE_HEADER) {
+ if (currentItem.viewType == VIEW_TYPE_PRIVATE_SPACE_HEADER) {
// Note: SmoothScroller is meant to be used once.
RecyclerView.SmoothScroller smoothScroller =
new LinearSmoothScroller(mAllApps.getContext()) {
@@ -198,7 +201,7 @@
return LinearSmoothScroller.SNAP_TO_END;
}
};
- smoothScroller.setTargetPosition(adapterPosition);
+ smoothScroller.setTargetPosition(i);
RecyclerView.LayoutManager layoutManager = allAppsRecyclerView.getLayoutManager();
if (layoutManager != null) {
layoutManager.startSmoothScroll(smoothScroller);
@@ -206,12 +209,67 @@
break;
}
// Make the private space apps gone to "collapse".
- if (allAppsAdapters.get(adapterPosition).decorationInfo != null) {
- allAppsRecyclerView.getChildAt(i).setVisibility(GONE);
+ if (currentItem.decorationInfo != null) {
+ RecyclerView.ViewHolder viewHolder =
+ allAppsRecyclerView.findViewHolderForAdapterPosition(i);
+ if (viewHolder != null) {
+ viewHolder.itemView.setVisibility(GONE);
+ }
}
}
}
+ /**
+ * Upon expanding, only scroll to the item position in the adapter that allows the header to be
+ * visible.
+ */
+ @VisibleForTesting
+ public int scrollForViewToBeVisibleInContainer(
+ AllAppsRecyclerView allAppsRecyclerView,
+ List<BaseAllAppsAdapter.AdapterItem> appListAdapterItems,
+ int psHeaderHeight,
+ int allAppsCellHeight) {
+ int rowToExpandToWithRespectToHeader = -1;
+ int itemToScrollTo = -1;
+ // Looks for the item in the app list to scroll to so that the header is visible.
+ for (int i = 0; i < appListAdapterItems.size(); i++) {
+ BaseAllAppsAdapter.AdapterItem currentItem = appListAdapterItems.get(i);
+ if (currentItem.viewType == VIEW_TYPE_PRIVATE_SPACE_HEADER) {
+ itemToScrollTo = i;
+ continue;
+ }
+ if (itemToScrollTo != -1) {
+ if (rowToExpandToWithRespectToHeader == -1) {
+ rowToExpandToWithRespectToHeader = currentItem.rowIndex;
+ }
+ int rowToScrollTo =
+ (int) Math.floor((double) (mAllApps.getHeight() - psHeaderHeight
+ - mAllApps.getHeaderProtectionHeight()) / allAppsCellHeight);
+ int currentRowDistance = currentItem.rowIndex - rowToExpandToWithRespectToHeader;
+ // rowToScrollTo - 1 since the item to scroll to is 0 indexed.
+ if (currentRowDistance == rowToScrollTo - 1) {
+ itemToScrollTo = i;
+ break;
+ }
+ }
+ }
+ if (itemToScrollTo != -1) {
+ // Note: SmoothScroller is meant to be used once.
+ RecyclerView.SmoothScroller smoothScroller =
+ new LinearSmoothScroller(mAllApps.getContext()) {
+ @Override protected int getVerticalSnapPreference() {
+ return LinearSmoothScroller.SNAP_TO_ANY;
+ }
+ };
+ smoothScroller.setTargetPosition(itemToScrollTo);
+ RecyclerView.LayoutManager layoutManager = allAppsRecyclerView.getLayoutManager();
+ if (layoutManager != null) {
+ layoutManager.startSmoothScroll(smoothScroller);
+ }
+ }
+ return itemToScrollTo;
+ }
+
PrivateProfileManager getPrivateProfileManager() {
return mPrivateProfileManager;
}
diff --git a/src/com/android/launcher3/allapps/SectionDecorationHandler.java b/src/com/android/launcher3/allapps/SectionDecorationHandler.java
index f79b82c..ac9b146 100644
--- a/src/com/android/launcher3/allapps/SectionDecorationHandler.java
+++ b/src/com/android/launcher3/allapps/SectionDecorationHandler.java
@@ -176,13 +176,10 @@
/**
* Expands decoration bounds to include child {@link PrivateAppsSectionDecorator}
*/
- public void addChild(SectionDecorationHandler child, View view, boolean applyBackground) {
+ public void addChild(SectionDecorationHandler child, View view) {
int scaledHeight = (int) (view.getHeight() * view.getScaleY());
mBounds.union(view.getLeft(), view.getY(),
view.getRight(), view.getY() + scaledHeight);
- if (applyBackground) {
- applyBackground(view, mContext, null, false);
- }
mIsBottomRound |= child.mIsBottomRound;
mIsBottomLeftRound |= child.mIsBottomLeftRound;
mIsBottomRightRound |= child.mIsBottomRightRound;
diff --git a/src/com/android/launcher3/allapps/WorkEduCard.java b/src/com/android/launcher3/allapps/WorkEduCard.java
index 1059097..5fe2f0c 100644
--- a/src/com/android/launcher3/allapps/WorkEduCard.java
+++ b/src/com/android/launcher3/allapps/WorkEduCard.java
@@ -103,6 +103,8 @@
AllAppsRecyclerView rv = mActivityContext.getAppsView().mAH.get(
ActivityAllAppsContainerView.AdapterHolder.WORK).mRecyclerView;
rv.getApps().getAdapterItems().remove(mPosition);
+ // Remove the educard fast scroll section.
+ rv.getApps().getFastScrollerSections().remove(0);
rv.getAdapter().notifyItemRemoved(mPosition);
}
}
diff --git a/src/com/android/launcher3/allapps/WorkProfileManager.java b/src/com/android/launcher3/allapps/WorkProfileManager.java
index a54e52c..96998a3 100644
--- a/src/com/android/launcher3/allapps/WorkProfileManager.java
+++ b/src/com/android/launcher3/allapps/WorkProfileManager.java
@@ -73,7 +73,7 @@
* Posts quite mode enable/disable call for work profile user
*/
public void setWorkProfileEnabled(boolean enabled) {
- setCurrentState(STATE_TRANSITION);
+ updateCurrentState(STATE_TRANSITION);
setQuietMode(!enabled);
}
diff --git a/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java b/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java
index f9d047b..ec45415 100644
--- a/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java
+++ b/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java
@@ -155,6 +155,7 @@
public void reset() {
mCallback.clearSearchResult();
mInput.reset();
+ mInput.clearFocus();
mQuery = null;
mInput.removeOnFocusChangeListener(this);
}
diff --git a/src/com/android/launcher3/apppairs/AppPairIcon.java b/src/com/android/launcher3/apppairs/AppPairIcon.java
index a3800f7..13fefc4 100644
--- a/src/com/android/launcher3/apppairs/AppPairIcon.java
+++ b/src/com/android/launcher3/apppairs/AppPairIcon.java
@@ -17,7 +17,6 @@
package com.android.launcher3.apppairs;
import android.content.Context;
-import android.graphics.Canvas;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.util.Log;
@@ -94,17 +93,18 @@
icon.setOnClickListener(activity.getItemOnClickListener());
icon.mInfo = appPairInfo;
+ // TODO (b/326664798): Delete this check, instead check at launcher load time
if (icon.mInfo.contents.size() != 2) {
Log.wtf(TAG, "AppPair contents not 2, size: " + icon.mInfo.contents.size());
return icon;
}
- icon.checkScreenSize();
-
// Set up icon drawable area
icon.mIconGraphic = icon.findViewById(R.id.app_pair_icon_graphic);
icon.mIconGraphic.init(activity, icon);
+ icon.checkDisabledState();
+
// Set up app pair title
icon.mAppPairName = icon.findViewById(R.id.app_pair_icon_name);
icon.mAppPairName.setCompoundDrawablePadding(0);
@@ -183,23 +183,20 @@
}
/**
- * Checks if the app pair is launchable in the current device configuration.
- *
+ * Updates the "disabled" state of the app pair in the current device configuration.
* App pairs can be "disabled" in two ways:
* 1) One of the member WorkspaceItemInfos is disabled (i.e. the app software itself is paused
- * by the user or can't be launched).
+ * by the user or can't be launched for some other reason).
* 2) This specific instance of an app pair can't be launched due to screen size requirements.
- *
- * This method checks and updates #2. Both #1 and #2 are checked when app pairs are drawn
- * {@link AppPairIconGraphic#dispatchDraw(Canvas)} or clicked on
- * {@link com.android.launcher3.touch.ItemClickHandler#onClickAppPairIcon(View)}
*/
- public void checkScreenSize() {
+ public void checkDisabledState() {
DeviceProfile dp = ActivityContext.lookupContext(getContext()).getDeviceProfile();
// If user is on a small screen, we can't launch if either of the apps is non-resizeable
mIsLaunchableAtScreenSize =
dp.isTablet || getInfo().contents.stream().noneMatch(
wii -> wii.hasStatusFlag(WorkspaceItemInfo.FLAG_NON_RESIZEABLE));
+ // Call applyIcons to check and update icons
+ mIconGraphic.applyIcons();
}
/**
@@ -209,7 +206,7 @@
// If either of the app pair icons return true on the predicate (i.e. in the list of
// updated apps), redraw the icon graphic (icon background and both icons).
if (getInfo().contents.stream().anyMatch(itemCheck)) {
- checkScreenSize();
+ checkDisabledState();
mIconGraphic.invalidate();
}
}
diff --git a/src/com/android/launcher3/apppairs/AppPairIconBackground.java b/src/com/android/launcher3/apppairs/AppPairIconBackground.java
index b5011f1..187541f 100644
--- a/src/com/android/launcher3/apppairs/AppPairIconBackground.java
+++ b/src/com/android/launcher3/apppairs/AppPairIconBackground.java
@@ -162,6 +162,6 @@
@Override
public void setColorFilter(ColorFilter colorFilter) {
- // Required by Drawable but not used.
+ mBackgroundPaint.setColorFilter(colorFilter);
}
}
diff --git a/src/com/android/launcher3/apppairs/AppPairIconGraphic.kt b/src/com/android/launcher3/apppairs/AppPairIconGraphic.kt
index a4ac4c8..777831b 100644
--- a/src/com/android/launcher3/apppairs/AppPairIconGraphic.kt
+++ b/src/com/android/launcher3/apppairs/AppPairIconGraphic.kt
@@ -27,8 +27,8 @@
import com.android.launcher3.DeviceProfile
import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener
import com.android.launcher3.icons.BitmapInfo
-import com.android.launcher3.icons.PlaceHolderIconDrawable
-import com.android.launcher3.model.data.WorkspaceItemInfo
+import com.android.launcher3.icons.FastBitmapDrawable
+import com.android.launcher3.icons.FastBitmapDrawable.getDisabledColorFilter
import com.android.launcher3.util.Themes
import com.android.launcher3.views.ActivityContext
@@ -48,9 +48,6 @@
private const val CENTER_CHANNEL_SCALE = 1 / 30f
private const val BIG_RADIUS_SCALE = 1 / 5f
private const val SMALL_RADIUS_SCALE = 1 / 15f
- // Disabled alpha is 38%, or 97/255
- private const val DISABLED_ALPHA = 97
- private const val ENABLED_ALPHA = 255
}
// App pair icons are slightly smaller than regular icons, so we pad the icon by this much on
@@ -74,8 +71,8 @@
private lateinit var activityContext: ActivityContext
private lateinit var parentIcon: AppPairIcon
private lateinit var appPairBackground: Drawable
- private var appIcon1: Drawable? = null
- private var appIcon2: Drawable? = null
+ private lateinit var appIcon1: FastBitmapDrawable
+ private lateinit var appIcon2: FastBitmapDrawable
fun init(activity: ActivityContext, icon: AppPairIcon) {
activityContext = activity
@@ -94,7 +91,8 @@
appPairBackground = AppPairIconBackground(context, this)
appPairBackground.setBounds(0, 0, backgroundSize.toInt(), backgroundSize.toInt())
- applyIcons(parentIcon.info.contents)
+
+ applyIcons()
// Center the drawable area in the larger icon canvas
val lp: LayoutParams = layoutParams as LayoutParams
@@ -128,26 +126,29 @@
}
/** Sets up app pair member icons for drawing. */
- private fun applyIcons(contents: ArrayList<WorkspaceItemInfo>) {
- // App pair should always contain 2 members; if not 2, return to avoid a crash loop
- if (contents.size != 2) {
- Log.wtf(TAG, "AppPair contents not 2, size: " + contents.size, Throwable())
+ fun applyIcons() {
+ val apps = parentIcon.info.contents
+
+ // TODO (b/326664798): Delete this check, instead check at launcher load time
+ if (apps.size != 2) {
+ Log.wtf(TAG, "AppPair contents not 2, size: " + apps.size, Throwable())
return
}
// Generate new icons, using themed flag if needed
val flags = if (Themes.isThemedIconEnabled(context)) BitmapInfo.FLAG_THEMED else 0
- val newIcon1 = parentIcon.info.contents[0].newIcon(context, flags)
- val newIcon2 = parentIcon.info.contents[1].newIcon(context, flags)
+ appIcon1 = apps[0].newIcon(context, flags)
+ appIcon2 = apps[1].newIcon(context, flags)
+ appIcon1.setBounds(0, 0, memberIconSize.toInt(), memberIconSize.toInt())
+ appIcon2.setBounds(0, 0, memberIconSize.toInt(), memberIconSize.toInt())
- // If app icons did not draw fully last time, animate to full icon
- (appIcon1 as? PlaceHolderIconDrawable)?.animateIconUpdate(newIcon1)
- (appIcon2 as? PlaceHolderIconDrawable)?.animateIconUpdate(newIcon2)
+ // Check disabled state
+ val shouldDrawAsDisabled =
+ parentIcon.info.isDisabled || !parentIcon.isLaunchableAtScreenSize
- appIcon1 = newIcon1
- appIcon2 = newIcon2
- appIcon1?.setBounds(0, 0, memberIconSize.toInt(), memberIconSize.toInt())
- appIcon2?.setBounds(0, 0, memberIconSize.toInt(), memberIconSize.toInt())
+ appPairBackground.colorFilter = if (shouldDrawAsDisabled) getDisabledColorFilter() else null
+ appIcon1.setIsDisabled(shouldDrawAsDisabled)
+ appIcon2.setIsDisabled(shouldDrawAsDisabled)
}
/** Gets this icon graphic's bounds, with respect to the parent icon's coordinate system. */
@@ -164,17 +165,9 @@
override fun dispatchDraw(canvas: Canvas) {
super.dispatchDraw(canvas)
- val drawAlpha =
- if (!parentIcon.isLaunchableAtScreenSize || parentIcon.info.isDisabled) DISABLED_ALPHA
- else ENABLED_ALPHA
-
// Draw background
- appPairBackground.alpha = drawAlpha
appPairBackground.draw(canvas)
- // Make sure icons are loaded and fresh
- applyIcons(parentIcon.info.contents)
-
// Draw first icon
canvas.save()
// The app icons are placed differently depending on device orientation.
@@ -183,8 +176,8 @@
} else {
canvas.translate(width / 2f - memberIconSize / 2f, innerPadding)
}
- appIcon1?.alpha = drawAlpha
- appIcon1?.draw(canvas)
+
+ appIcon1.draw(canvas)
canvas.restore()
// Draw second icon
@@ -201,8 +194,8 @@
height - (innerPadding + memberIconSize)
)
}
- appIcon2?.alpha = drawAlpha
- appIcon2?.draw(canvas)
+
+ appIcon2.draw(canvas)
canvas.restore()
}
}
diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index bcdf997..6d64c22 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -17,6 +17,7 @@
package com.android.launcher3.config;
import static com.android.launcher3.BuildConfig.WIDGET_ON_FIRST_SCREEN;
+import static com.android.launcher3.LauncherPrefs.LONG_PRESS_NAV_HANDLE_EXTRA_TOUCH_WIDTH_DP;
import static com.android.launcher3.LauncherPrefs.LONG_PRESS_NAV_HANDLE_HAPTIC_HINT_DELAY;
import static com.android.launcher3.LauncherPrefs.LONG_PRESS_NAV_HANDLE_HAPTIC_HINT_END_SCALE_PERCENT;
import static com.android.launcher3.LauncherPrefs.LONG_PRESS_NAV_HANDLE_HAPTIC_HINT_ITERATIONS;
@@ -147,6 +148,12 @@
"Controls touch slop percentage for lpnh",
LONG_PRESS_NAV_HANDLE_SLOP_PERCENTAGE);
+ public static final IntFlag LPNH_EXTRA_TOUCH_WIDTH_DP =
+ FlagsFactory.getIntFlag(301680992, "LPNH_EXTRA_TOUCH_WIDTH_DP", 0,
+ "Controls extra dp on the nav bar sides to trigger LPNH."
+ + " Can be negative for a smaller touch region.",
+ LONG_PRESS_NAV_HANDLE_EXTRA_TOUCH_WIDTH_DP);
+
public static final IntFlag LPNH_TIMEOUT_MS =
FlagsFactory.getIntFlag(301680992, "LPNH_TIMEOUT_MS",
ViewConfiguration.getLongPressTimeout(),
@@ -158,7 +165,7 @@
// TODO(Block 5): Clean up flags
public static final BooleanFlag ENABLE_TWOLINE_DEVICESEARCH = getDebugFlag(201388851,
- "ENABLE_TWOLINE_DEVICESEARCH", ENABLED,
+ "ENABLE_TWOLINE_DEVICESEARCH", DISABLED,
"Enable two line label for icons with labels on device search.");
public static final BooleanFlag ENABLE_ICON_IN_TEXT_HEADER = getDebugFlag(270395143,
@@ -177,11 +184,6 @@
"SECONDARY_DRAG_N_DROP_TO_PIN", DISABLED,
"Enable dragging and dropping to pin apps within secondary display");
- // TODO(Block 7): Clean up flags
- public static final BooleanFlag ENABLE_FORCED_MONO_ICON = getDebugFlag(270396209,
- "ENABLE_FORCED_MONO_ICON", DISABLED,
- "Enable the ability to generate monochromatic icons, if it is not provided by the app");
-
// TODO(Block 8): Clean up flags
// TODO(Block 9): Clean up flags
@@ -274,10 +276,7 @@
// Aconfig migration complete for ENABLE_TWOLINE_ALLAPPS.
public static final BooleanFlag ENABLE_TWOLINE_ALLAPPS = getDebugFlag(270390937,
- "ENABLE_TWOLINE_ALLAPPS", ENABLED, "Enables two line label inside all apps.");
- public static boolean enableTwolineAllapps() {
- return ENABLE_TWOLINE_ALLAPPS.get() || Flags.enableTwolineAllapps();
- }
+ "ENABLE_TWOLINE_ALLAPPS", DISABLED, "Enables two line label inside all apps.");
public static final BooleanFlag IME_STICKY_SNACKBAR_EDU = getDebugFlag(270391693,
"IME_STICKY_SNACKBAR_EDU", ENABLED, "Show sticky IME edu in AllApps");
diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java
index ecb5c8f..c8c634a 100644
--- a/src/com/android/launcher3/folder/Folder.java
+++ b/src/com/android/launcher3/folder/Folder.java
@@ -284,6 +284,8 @@
mContent.setFolder(this);
mPageIndicator = findViewById(R.id.folder_page_indicator);
+ mFooter = findViewById(R.id.folder_footer);
+ mFooterHeight = dp.folderFooterHeightPx;
mFolderName = findViewById(R.id.folder_name);
mFolderName.setTextSize(TypedValue.COMPLEX_UNIT_PX, dp.folderLabelTextSizePx);
mFolderName.setOnBackKeyListener(this);
@@ -294,9 +296,10 @@
| InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS
| InputType.TYPE_TEXT_FLAG_CAP_WORDS);
mFolderName.forceDisableSuggestions(true);
-
- mFooter = findViewById(R.id.folder_footer);
- mFooterHeight = dp.folderFooterHeightPx;
+ mFolderName.setPadding(mFolderName.getPaddingLeft(),
+ (mFooterHeight - mFolderName.getLineHeight()) / 2,
+ mFolderName.getPaddingRight(),
+ (mFooterHeight - mFolderName.getLineHeight()) / 2);
mKeyboardInsetAnimationCallback = new KeyboardInsetAnimationCallback(this);
setWindowInsetsAnimationCallback(mKeyboardInsetAnimationCallback);
diff --git a/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java b/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java
index 051fb6f..fe327d0 100644
--- a/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java
+++ b/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java
@@ -37,7 +37,6 @@
import android.view.SurfaceControlViewHost;
import android.view.SurfaceControlViewHost.SurfacePackage;
import android.view.View;
-import android.view.WindowManager.LayoutParams;
import android.view.animation.AccelerateDecelerateInterpolator;
import androidx.annotation.NonNull;
@@ -87,6 +86,7 @@
private final int mHeight;
private String mGridName;
+ private final int mDisplayId;
private final Display mDisplay;
private final WallpaperColors mWallpaperColors;
private final RunnableList mOnDestroyCallbacks = new RunnableList();
@@ -110,8 +110,12 @@
mHostToken = bundle.getBinder(KEY_HOST_TOKEN);
mWidth = bundle.getInt(KEY_VIEW_WIDTH);
mHeight = bundle.getInt(KEY_VIEW_HEIGHT);
+ mDisplayId = bundle.getInt(KEY_DISPLAY_ID);
mDisplay = context.getSystemService(DisplayManager.class)
- .getDisplay(bundle.getInt(KEY_DISPLAY_ID));
+ .getDisplay(mDisplayId);
+ if (mDisplay == null) {
+ throw new IllegalArgumentException("Display ID does not match any displays.");
+ }
mSurfaceControlViewHost = MAIN_EXECUTOR.submit(() ->
new SurfaceControlViewHost(mContext, context.getSystemService(DisplayManager.class)
@@ -121,7 +125,7 @@
}
public int getDisplayId() {
- return mDisplay.getDisplayId();
+ return mDisplayId;
}
public IBinder getHostToken() {
@@ -210,7 +214,6 @@
return new ContextThemeWrapper(context,
Themes.getActivityThemeRes(context));
}
- context = context.createWindowContext(LayoutParams.TYPE_APPLICATION_OVERLAY, null);
LocalColorExtractor.newInstance(context)
.applyColorsOverride(context, mWallpaperColors);
return new ContextThemeWrapper(context,
diff --git a/src/com/android/launcher3/icons/LauncherIcons.java b/src/com/android/launcher3/icons/LauncherIcons.java
index a15348b..513377a 100644
--- a/src/com/android/launcher3/icons/LauncherIcons.java
+++ b/src/com/android/launcher3/icons/LauncherIcons.java
@@ -16,14 +16,13 @@
package com.android.launcher3.icons;
-import static com.android.launcher3.config.FeatureFlags.ENABLE_FORCED_MONO_ICON;
-
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.os.UserHandle;
import androidx.annotation.NonNull;
+import com.android.launcher3.Flags;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.graphics.IconShape;
import com.android.launcher3.graphics.LauncherPreviewRenderer;
@@ -103,7 +102,7 @@
@Override
protected Drawable getMonochromeDrawable(Drawable base) {
Drawable mono = super.getMonochromeDrawable(base);
- if (mono != null || !ENABLE_FORCED_MONO_ICON.get()) {
+ if (mono != null || !Flags.forceMonochromeAppIcons()) {
return mono;
}
if (mMonochromeIconFactory == null) {
diff --git a/src/com/android/launcher3/keyboard/FocusIndicatorHelper.java b/src/com/android/launcher3/keyboard/FocusIndicatorHelper.java
index 3e320bd..72dc63e 100644
--- a/src/com/android/launcher3/keyboard/FocusIndicatorHelper.java
+++ b/src/com/android/launcher3/keyboard/FocusIndicatorHelper.java
@@ -31,9 +31,11 @@
implements OnFocusChangeListener {
public FocusIndicatorHelper(View container) {
- super(container, Flags.enableFocusOutline() ? Themes.getAttrColor(container.getContext(),
- R.attr.focusOutlineColor)
- : container.getResources().getColor(R.color.focused_background));
+ super(container,
+ Flags.enableFocusOutline() ? new int[]{Themes.getAttrColor(container.getContext(),
+ R.attr.focusOutlineColor), Themes.getAttrColor(container.getContext(),
+ R.attr.focusInnerOutlineColor)}
+ : new int[]{container.getResources().getColor(R.color.focused_background)});
}
@Override
diff --git a/src/com/android/launcher3/keyboard/ItemFocusIndicatorHelper.java b/src/com/android/launcher3/keyboard/ItemFocusIndicatorHelper.java
index a8cd03b..456cde8 100644
--- a/src/com/android/launcher3/keyboard/ItemFocusIndicatorHelper.java
+++ b/src/com/android/launcher3/keyboard/ItemFocusIndicatorHelper.java
@@ -74,7 +74,8 @@
private static final Rect sTempRect2 = new Rect();
private final View mContainer;
- protected final Paint mPaint;
+ protected final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+ private final Paint mInnerPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
private final int mMaxAlpha;
private final Rect mDirtyRect = new Rect();
@@ -93,24 +94,31 @@
private ObjectAnimator mCurrentAnimation;
private float mAlpha;
private float mRadius;
+ private float mInnerRadius;
- public ItemFocusIndicatorHelper(View container, int color) {
+ public ItemFocusIndicatorHelper(View container, int... colors) {
mContainer = container;
- mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
- mPaint.setColor(0xFF000000 | color);
- if (Flags.enableFocusOutline()) {
+ mPaint.setColor(0xFF000000 | colors[0]);
+ if (Flags.enableFocusOutline() && colors.length > 1) {
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(container.getResources().getDimensionPixelSize(
R.dimen.focus_outline_stroke_width));
mRadius = container.getResources().getDimensionPixelSize(
R.dimen.focus_outline_radius);
+
+ mInnerPaint.setStyle(Paint.Style.STROKE);
+ mInnerPaint.setColor(0xFF000000 | colors[1]);
+ mInnerPaint.setStrokeWidth(container.getResources().getDimensionPixelSize(
+ R.dimen.focus_outline_stroke_width));
+ mInnerRadius = container.getResources().getDimensionPixelSize(
+ R.dimen.focus_inner_outline_radius);
} else {
mPaint.setStyle(Paint.Style.FILL);
mRadius = container.getResources().getDimensionPixelSize(
R.dimen.grid_visualization_rounding_radius);
}
- mMaxAlpha = Color.alpha(color);
+ mMaxAlpha = Color.alpha(colors[0]);
setAlpha(0);
mShift = 0;
@@ -119,6 +127,7 @@
protected void setAlpha(float alpha) {
mAlpha = alpha;
mPaint.setAlpha((int) (mAlpha * mMaxAlpha));
+ mInnerPaint.setAlpha((int) (mAlpha * mMaxAlpha));
}
@Override
@@ -147,11 +156,18 @@
Rect newRect = getDrawRect();
if (newRect != null) {
if (Flags.enableFocusOutline()) {
- // Stroke is drawn with half outside and half inside the view. Inset by half
- // stroke width to move the whole stroke inside the view and avoid other views
- // occluding it
- int halfStrokeWidth = (int) mPaint.getStrokeWidth() / 2;
- newRect.inset(halfStrokeWidth, halfStrokeWidth);
+ int strokeWidth = (int) mPaint.getStrokeWidth();
+ // Inset for inner outline. Stroke is drawn with half outside and half inside
+ // the view. Inset by half stroke width to move the whole stroke inside the view
+ // and avoid other views occluding it. Inset one more stroke width to leave space
+ // for outer outline.
+ newRect.inset((int) (strokeWidth * 1.5), (int) (strokeWidth * 1.5));
+ c.drawRoundRect((float) newRect.left, (float) newRect.top,
+ (float) newRect.right, (float) newRect.bottom,
+ mInnerRadius, mInnerRadius, mInnerPaint);
+
+ // Inset outward for drawing outer outline
+ newRect.inset(-strokeWidth, -strokeWidth);
}
mDirtyRect.set(newRect);
c.drawRoundRect((float) mDirtyRect.left, (float) mDirtyRect.top,
diff --git a/src/com/android/launcher3/keyboard/ViewGroupFocusHelper.java b/src/com/android/launcher3/keyboard/ViewGroupFocusHelper.java
index f9bd343..4653bf1 100644
--- a/src/com/android/launcher3/keyboard/ViewGroupFocusHelper.java
+++ b/src/com/android/launcher3/keyboard/ViewGroupFocusHelper.java
@@ -27,6 +27,7 @@
public class ViewGroupFocusHelper extends FocusIndicatorHelper {
private final View mContainer;
+ private static final Rect sTempRect = new Rect();
public ViewGroupFocusHelper(View container) {
super(container);
@@ -35,18 +36,22 @@
@Override
public void viewToRect(View v, Rect outRect) {
- outRect.left = 0;
- outRect.top = 0;
+ // Using FocusedRect here allows views to provide their custom rect for drawing outline,
+ // e.g. making the Rect bigger than the content to leave some padding between view and
+ // outline
+ v.getFocusedRect(sTempRect);
+ outRect.left = sTempRect.left;
+ outRect.top = sTempRect.top;
computeLocationRelativeToContainer(v, outRect);
// If a view is scaled, its position will also shift accordingly. For optimization, only
// consider this for the last node.
- outRect.left += (1 - v.getScaleX()) * v.getWidth() / 2;
- outRect.top += (1 - v.getScaleY()) * v.getHeight() / 2;
+ outRect.left = (int) (outRect.left + (1 - v.getScaleX()) * sTempRect.width() / 2);
+ outRect.top = (int) (outRect.top + (1 - v.getScaleY()) * sTempRect.height() / 2);
- outRect.right = outRect.left + (int) (v.getScaleX() * v.getWidth());
- outRect.bottom = outRect.top + (int) (v.getScaleY() * v.getHeight());
+ outRect.right = outRect.left + (int) (v.getScaleX() * sTempRect.width());
+ outRect.bottom = outRect.top + (int) (v.getScaleY() * sTempRect.height());
}
private void computeLocationRelativeToContainer(View child, Rect outRect) {
diff --git a/src/com/android/launcher3/logging/StatsLogManager.java b/src/com/android/launcher3/logging/StatsLogManager.java
index 5cb1540..52fb122 100644
--- a/src/com/android/launcher3/logging/StatsLogManager.java
+++ b/src/com/android/launcher3/logging/StatsLogManager.java
@@ -751,6 +751,15 @@
+ " metric.")
LAUNCHER_SPLIT_SELECTION_EXIT_INTERRUPTED(1612),
+ @UiEvent(doc = "User tapped add widget button in widget sheet.")
+ LAUNCHER_WIDGET_ADD_BUTTON_TAP(1622),
+
+ @UiEvent(doc = "Number of user installed Private profile apps, shown above separator line")
+ LAUNCHER_PRIVATE_SPACE_USER_INSTALLED_APPS_COUNT(1672),
+
+ @UiEvent(doc = "Number of preinstalled Private profile apps, shown under separator line")
+ LAUNCHER_PRIVATE_SPACE_PREINSTALLED_APPS_COUNT(1673)
+
// ADD MORE
;
diff --git a/src/com/android/launcher3/model/BaseLauncherBinder.java b/src/com/android/launcher3/model/BaseLauncherBinder.java
index fa2a1b0..966b6a6 100644
--- a/src/com/android/launcher3/model/BaseLauncherBinder.java
+++ b/src/com/android/launcher3/model/BaseLauncherBinder.java
@@ -29,6 +29,8 @@
import android.util.Pair;
import android.view.View;
+import androidx.annotation.NonNull;
+
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherModel.CallbackTask;
@@ -309,9 +311,16 @@
// Bind workspace screens
executeCallbacksTask(c -> c.bindScreens(mOrderedScreenIds), mUiExecutor);
+ ItemInflater inflater = mCallbacks.getItemInflater();
+
// Load items on the current page.
- bindItemsInChunks(currentWorkspaceItems, ITEMS_CHUNK, mUiExecutor);
- bindItemsInChunks(currentAppWidgets, 1, mUiExecutor);
+ if (enableWorkspaceInflation() && inflater != null) {
+ inflateAsyncAndBind(currentWorkspaceItems, inflater, mUiExecutor);
+ inflateAsyncAndBind(currentAppWidgets, inflater, mUiExecutor);
+ } else {
+ bindItemsInChunks(currentWorkspaceItems, ITEMS_CHUNK, mUiExecutor);
+ bindItemsInChunks(currentAppWidgets, 1, mUiExecutor);
+ }
if (!FeatureFlags.CHANGE_MODEL_DELEGATE_LOADING_ORDER.get()) {
mExtraItems.forEach(item ->
executeCallbacksTask(c -> c.bindExtraContainerItems(item), mUiExecutor));
@@ -322,18 +331,20 @@
RunnableList onCompleteSignal = new RunnableList();
- if (enableWorkspaceInflation()) {
+ if (enableWorkspaceInflation() && inflater != null) {
MODEL_EXECUTOR.execute(() -> {
- setupPendingBind(otherWorkspaceItems, otherAppWidgets, currentScreenIds,
- pendingExecutor);
+ inflateAsyncAndBind(otherWorkspaceItems, inflater, pendingExecutor);
+ inflateAsyncAndBind(otherAppWidgets, inflater, pendingExecutor);
+ setupPendingBind(currentScreenIds, pendingExecutor);
// Wait for the async inflation to complete and then notify the completion
// signal on UI thread.
MAIN_EXECUTOR.execute(onCompleteSignal::executeAllAndDestroy);
});
} else {
- setupPendingBind(
- otherWorkspaceItems, otherAppWidgets, currentScreenIds, pendingExecutor);
+ bindItemsInChunks(otherWorkspaceItems, ITEMS_CHUNK, pendingExecutor);
+ bindItemsInChunks(otherAppWidgets, 1, pendingExecutor);
+ setupPendingBind(currentScreenIds, pendingExecutor);
onCompleteSignal.executeAllAndDestroy();
}
@@ -348,13 +359,8 @@
}
private void setupPendingBind(
- List<ItemInfo> otherWorkspaceItems,
- List<ItemInfo> otherAppWidgets,
IntSet currentScreenIds,
Executor pendingExecutor) {
- bindItemsInChunks(otherWorkspaceItems, ITEMS_CHUNK, pendingExecutor);
- bindItemsInChunks(otherAppWidgets, 1, pendingExecutor);
-
StringCache cacheClone = mBgDataModel.stringCache.clone();
executeCallbacksTask(c -> c.bindStringCache(cacheClone), pendingExecutor);
@@ -371,18 +377,11 @@
* Tries to inflate the items asynchronously and bind. Returns true on success or false if
* async-binding is not supported in this case.
*/
- private boolean inflateAsyncAndBind(List<ItemInfo> items, Executor executor) {
- if (!enableWorkspaceInflation()) {
- return false;
- }
- ItemInflater inflater = mCallbacks.getItemInflater();
- if (inflater == null) {
- return false;
- }
-
+ private void inflateAsyncAndBind(
+ List<ItemInfo> items, @NonNull ItemInflater inflater, Executor executor) {
if (mMyBindingId != mBgDataModel.lastBindId) {
Log.d(TAG, "Too many consecutive reloads, skipping obsolete view inflation");
- return true;
+ return;
}
ModelWriter writer = mApp.getModel()
@@ -390,15 +389,10 @@
List<Pair<ItemInfo, View>> bindItems = items.stream().map(i ->
Pair.create(i, inflater.inflateItem(i, writer, null))).toList();
executeCallbacksTask(c -> c.bindInflatedItems(bindItems), executor);
- return true;
}
- private void bindItemsInChunks(List<ItemInfo> workspaceItems, int chunkCount,
- Executor executor) {
- if (inflateAsyncAndBind(workspaceItems, executor)) {
- return;
- }
-
+ private void bindItemsInChunks(
+ List<ItemInfo> workspaceItems, int chunkCount, Executor executor) {
// Bind the workspace items
int count = workspaceItems.size();
for (int i = 0; i < count; i += chunkCount) {
diff --git a/src/com/android/launcher3/model/DatabaseHelper.java b/src/com/android/launcher3/model/DatabaseHelper.java
index 1360510..88ca009 100644
--- a/src/com/android/launcher3/model/DatabaseHelper.java
+++ b/src/com/android/launcher3/model/DatabaseHelper.java
@@ -333,7 +333,7 @@
for (int widgetId : allWidgets) {
if (!validWidgets.contains(widgetId)) {
try {
- FileLog.d(TAG, "Deleting invalid widget " + widgetId);
+ FileLog.d(TAG, "Deleting widget not found in db: appWidgetId=" + widgetId);
holder.deleteAppWidgetId(widgetId);
isAnyWidgetRemoved = true;
} catch (RuntimeException e) {
@@ -342,15 +342,17 @@
}
}
if (isAnyWidgetRemoved) {
- final String allWidgetsIds = Arrays.stream(allWidgets).mapToObj(String::valueOf)
+ final String allLauncherHostWidgetIds = Arrays.stream(allWidgets)
+ .mapToObj(String::valueOf)
.collect(Collectors.joining(",", "[", "]"));
- final String validWidgetsIds = Arrays.stream(
+ final String allValidLauncherDbWidgetIds = Arrays.stream(
validWidgets.getArray().toArray()).mapToObj(String::valueOf)
.collect(Collectors.joining(",", "[", "]"));
FileLog.d(TAG,
- "One or more widgets was removed. db_path=" + db.getPath()
- + " allWidgetsIds=" + allWidgetsIds
- + ", validWidgetsIds=" + validWidgetsIds);
+ "One or more widgets was removed: "
+ + " allLauncherHostWidgetIds=" + allLauncherHostWidgetIds
+ + ", allValidLauncherDbWidgetIds=" + allValidLauncherDbWidgetIds
+ );
}
} finally {
holder.destroy();
diff --git a/src/com/android/launcher3/model/DeviceGridState.java b/src/com/android/launcher3/model/DeviceGridState.java
index f24d1d2..8c68eb8 100644
--- a/src/com/android/launcher3/model/DeviceGridState.java
+++ b/src/com/android/launcher3/model/DeviceGridState.java
@@ -53,6 +53,13 @@
private final @DeviceType int mDeviceType;
private final String mDbFile;
+ public DeviceGridState(int columns, int row, int numHotseat, int deviceType, String dbFile) {
+ mGridSizeString = String.format(Locale.ENGLISH, "%d,%d", columns, row);
+ mNumHotseat = numHotseat;
+ mDeviceType = deviceType;
+ mDbFile = dbFile;
+ }
+
public DeviceGridState(InvariantDeviceProfile idp) {
mGridSizeString = String.format(Locale.ENGLISH, "%d,%d", idp.numColumns, idp.numRows);
mNumHotseat = idp.numDatabaseHotseatIcons;
diff --git a/src/com/android/launcher3/model/GridSizeMigrationUtil.java b/src/com/android/launcher3/model/GridSizeMigrationUtil.java
index af66431..15190c7 100644
--- a/src/com/android/launcher3/model/GridSizeMigrationUtil.java
+++ b/src/com/android/launcher3/model/GridSizeMigrationUtil.java
@@ -38,7 +38,9 @@
import android.util.Log;
import androidx.annotation.NonNull;
+import androidx.annotation.VisibleForTesting;
+import com.android.launcher3.Flags;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.LauncherPrefs;
import com.android.launcher3.LauncherSettings;
@@ -94,6 +96,15 @@
return needsToMigrate;
}
+ @VisibleForTesting
+ public static List<DbEntry> readAllEntries(SQLiteDatabase db, String tableName,
+ Context context) {
+ DbReader dbReader = new DbReader(db, tableName, context, getValidPackages(context));
+ List<DbEntry> result = dbReader.loadAllWorkspaceEntries();
+ result.addAll(dbReader.loadHotseatEntries());
+ return result;
+ }
+
/**
* When migrating the grid, we copy the table
* {@link LauncherSettings.Favorites#TABLE_NAME} from {@code source} into
@@ -105,15 +116,23 @@
*/
public static boolean migrateGridIfNeeded(
@NonNull Context context,
- @NonNull InvariantDeviceProfile idp,
+ @NonNull DeviceGridState srcDeviceState,
+ @NonNull DeviceGridState destDeviceState,
@NonNull DatabaseHelper target,
@NonNull SQLiteDatabase source) {
-
- DeviceGridState srcDeviceState = new DeviceGridState(context);
- DeviceGridState destDeviceState = new DeviceGridState(idp);
if (!needsToMigrate(srcDeviceState, destDeviceState)) {
return true;
}
+
+ if (Flags.enableGridMigrationFix()
+ && srcDeviceState.getColumns().equals(destDeviceState.getColumns())
+ && srcDeviceState.getRows() < destDeviceState.getRows()) {
+ // Only use this strategy when comparing the previous grid to the new grid and the
+ // columns are the same and the destination has more rows
+ copyTable(source, TABLE_NAME, target.getWritableDatabase(), TABLE_NAME, context);
+ destDeviceState.writeToPrefs(context);
+ return true;
+ }
copyTable(source, TABLE_NAME, target.getWritableDatabase(), TMP_TABLE, context);
HashSet<String> validPackages = getValidPackages(context);
@@ -656,7 +675,7 @@
}
}
- protected static class DbEntry extends ItemInfo implements Comparable<DbEntry> {
+ public static class DbEntry extends ItemInfo implements Comparable<DbEntry> {
private String mIntent;
private String mProvider;
diff --git a/src/com/android/launcher3/model/ModelDbController.java b/src/com/android/launcher3/model/ModelDbController.java
index ba2b64d..8ed554a 100644
--- a/src/com/android/launcher3/model/ModelDbController.java
+++ b/src/com/android/launcher3/model/ModelDbController.java
@@ -308,8 +308,12 @@
mOpenHelper = (mContext instanceof SandboxContext) ? oldHelper
: createDatabaseHelper(true /* forMigration */);
try {
- return GridSizeMigrationUtil.migrateGridIfNeeded(mContext, idp, mOpenHelper,
- oldHelper.getWritableDatabase());
+ // This is the current grid we have, given by the mContext
+ DeviceGridState srcDeviceState = new DeviceGridState(mContext);
+ // This is the state we want to migrate to that is given by the idp
+ DeviceGridState destDeviceState = new DeviceGridState(idp);
+ return GridSizeMigrationUtil.migrateGridIfNeeded(mContext, srcDeviceState,
+ destDeviceState, mOpenHelper, oldHelper.getWritableDatabase());
} catch (Exception e) {
FileLog.e(TAG, "Failed to migrate grid", e);
return false;
diff --git a/src/com/android/launcher3/model/WidgetItem.java b/src/com/android/launcher3/model/WidgetItem.java
index 1dd58c3..3f88717 100644
--- a/src/com/android/launcher3/model/WidgetItem.java
+++ b/src/com/android/launcher3/model/WidgetItem.java
@@ -123,6 +123,7 @@
if (!Flags.enableGeneratedPreviews() || generatedPreviews == null) {
return false;
}
- return generatedPreviews.contains(widgetCategory);
+ return generatedPreviews.contains(widgetCategory)
+ && generatedPreviews.get(widgetCategory) != null;
}
}
diff --git a/src/com/android/launcher3/model/WorkspaceItemProcessor.kt b/src/com/android/launcher3/model/WorkspaceItemProcessor.kt
index 59f56df..22e5eb4 100644
--- a/src/com/android/launcher3/model/WorkspaceItemProcessor.kt
+++ b/src/com/android/launcher3/model/WorkspaceItemProcessor.kt
@@ -410,14 +410,21 @@
appWidgetInfo.restoreStatus = c.restoreFlag
if (appWidgetInfo.spanX <= 0 || appWidgetInfo.spanY <= 0) {
c.markDeleted(
- "Widget has invalid size: ${appWidgetInfo.spanX}x${appWidgetInfo.spanY}",
+ "processWidget: Widget has invalid size: ${appWidgetInfo.spanX}x${appWidgetInfo.spanY}" +
+ ", id=${c.id}," +
+ ", appWidgetId=${c.appWidgetId}," +
+ ", component=${component}",
RestoreError.INVALID_LOCATION
)
return
}
if (!c.isOnWorkspaceOrHotseat) {
c.markDeleted(
- "Widget found where container != CONTAINER_DESKTOP nor CONTAINER_HOTSEAT - ignoring!",
+ "processWidget: invalid Widget container != CONTAINER_DESKTOP nor CONTAINER_HOTSEAT." +
+ " id=${c.id}," +
+ ", appWidgetId=${c.appWidgetId}," +
+ ", component=${component}," +
+ ", container=${c.container}",
RestoreError.INVALID_LOCATION
)
return
@@ -428,7 +435,12 @@
val inflationResult = widgetInflater.inflateAppWidget(appWidgetInfo)
var shouldUpdate = inflationResult.isUpdate
val lapi = inflationResult.widgetInfo
-
+ FileLog.d(
+ TAG,
+ "processWidget: id=${c.id}" +
+ ", appWidgetId=${c.appWidgetId}" +
+ ", inflationResult=$inflationResult"
+ )
when (inflationResult.type) {
WidgetInflater.TYPE_DELETE -> {
c.markDeleted(inflationResult.reason, inflationResult.restoreErrorType)
@@ -448,7 +460,11 @@
) {
// Restore never started
c.markDeleted(
- "Unrestored widget removed: $component",
+ "processWidget: Unrestored Pending widget removed:" +
+ " id=${c.id}" +
+ ", appWidgetId=${c.appWidgetId}" +
+ ", component=${component}" +
+ ", restoreFlag:=${c.restoreFlag}",
RestoreError.APP_NOT_INSTALLED
)
return
@@ -491,7 +507,10 @@
if (appWidgetInfo.spanX < lapi.minSpanX || appWidgetInfo.spanY < lapi.minSpanY) {
FileLog.d(
TAG,
- "Widget ${lapi.component} minSizes not meet: span=${appWidgetInfo.spanX}x${appWidgetInfo.spanY} minSpan=${lapi.minSpanX}x${lapi.minSpanY}"
+ " processWidget: Widget ${lapi.component} minSizes not met: span=${appWidgetInfo.spanX}x${appWidgetInfo.spanY} minSpan=${lapi.minSpanX}x${lapi.minSpanY}," +
+ " id: ${c.id}," +
+ " appWidgetId: ${c.appWidgetId}," +
+ " component=${component}"
)
logWidgetInfo(app.invariantDeviceProfile, lapi)
}
diff --git a/src/com/android/launcher3/model/data/ItemInfo.java b/src/com/android/launcher3/model/data/ItemInfo.java
index 55849c2..f7cff78 100644
--- a/src/com/android/launcher3/model/data/ItemInfo.java
+++ b/src/com/android/launcher3/model/data/ItemInfo.java
@@ -94,6 +94,10 @@
* {@link Favorites#ITEM_TYPE_APP_PAIR},
* {@link Favorites#ITEM_TYPE_APPWIDGET} or
* {@link Favorites#ITEM_TYPE_CUSTOM_APPWIDGET}.
+ * {@link Favorites#ITEM_TYPE_TASK}.
+ * {@link Favorites#ITEM_TYPE_QSB}.
+ * {@link Favorites#ITEM_TYPE_SEARCH_ACTION}.
+ * {@link Favorites#ITEM_TYPE_PRIVATE_SPACE_INSTALL_APP_BUTTON}.
*/
public int itemType;
diff --git a/src/com/android/launcher3/model/data/ItemInfoWithIcon.java b/src/com/android/launcher3/model/data/ItemInfoWithIcon.java
index 352c363..9fbc6bf 100644
--- a/src/com/android/launcher3/model/data/ItemInfoWithIcon.java
+++ b/src/com/android/launcher3/model/data/ItemInfoWithIcon.java
@@ -121,11 +121,6 @@
public static final int FLAG_ARCHIVED = 1 << 14;
/**
- * Flag indicating it's the Private Space Install App icon.
- */
- public static final int FLAG_PRIVATE_SPACE_INSTALL_APP = 1 << 15;
-
- /**
* Status associated with the system state of the underlying item. This is calculated every
* time a new info is created and not persisted on the disk.
*/
@@ -160,10 +155,6 @@
* and its install session is active
*/
public boolean isPendingDownload() {
- if (isArchived()) {
- return this.getProgressLevel() == 0
- && (this.runtimeStatusFlags & FLAG_INSTALL_SESSION_ACTIVE) != 0;
- }
return getProgressLevel() == 0;
}
@@ -177,6 +168,11 @@
return (runtimeStatusFlags & FLAG_ARCHIVED) != 0;
}
+ /** Returns true if the app is archived and has an active install session. */
+ public boolean isActiveArchive() {
+ return isArchived() && (runtimeStatusFlags & FLAG_INSTALL_SESSION_ACTIVE) != 0;
+ }
+
/**
* Indicates whether we're using a low res icon
*/
diff --git a/src/com/android/launcher3/model/data/PrivateSpaceInstallAppButtonInfo.java b/src/com/android/launcher3/model/data/PrivateSpaceInstallAppButtonInfo.java
new file mode 100644
index 0000000..1e7281d
--- /dev/null
+++ b/src/com/android/launcher3/model/data/PrivateSpaceInstallAppButtonInfo.java
@@ -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.launcher3.model.data;
+
+import com.android.launcher3.LauncherSettings;
+
+/**
+ * Represents the Private Space Install App button in AllAppsView.
+ */
+public class PrivateSpaceInstallAppButtonInfo extends AppInfo {
+
+ public PrivateSpaceInstallAppButtonInfo() {
+ itemType = LauncherSettings.Favorites.ITEM_TYPE_PRIVATE_SPACE_INSTALL_APP_BUTTON;
+ }
+}
diff --git a/src/com/android/launcher3/pageindicators/PageIndicatorDots.java b/src/com/android/launcher3/pageindicators/PageIndicatorDots.java
index 77effca..e44ea1d 100644
--- a/src/com/android/launcher3/pageindicators/PageIndicatorDots.java
+++ b/src/com/android/launcher3/pageindicators/PageIndicatorDots.java
@@ -153,7 +153,7 @@
mPaginationPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaginationPaint.setStyle(Style.FILL);
- mPaginationPaint.setColor(Themes.getAttrColor(context, R.attr.folderPaginationColor));
+ mPaginationPaint.setColor(Themes.getAttrColor(context, R.attr.pageIndicatorDotColor));
mDotRadius = getResources().getDimension(R.dimen.page_indicator_dot_size) / 2;
mCircleGap = DOT_GAP_FACTOR * mDotRadius;
setOutlineProvider(new MyOutlineProver());
diff --git a/src/com/android/launcher3/provider/LauncherDbUtils.java b/src/com/android/launcher3/provider/LauncherDbUtils.java
index 1f15947..b992a92 100644
--- a/src/com/android/launcher3/provider/LauncherDbUtils.java
+++ b/src/com/android/launcher3/provider/LauncherDbUtils.java
@@ -152,7 +152,12 @@
}
ShortcutInfo info = infoBuilder.build();
- if (!PinRequestHelper.createRequestForShortcut(context, info).accept()) {
+ try {
+ if (!PinRequestHelper.createRequestForShortcut(context, info).accept()) {
+ deletedShortcuts.add(lc.id);
+ continue;
+ }
+ } catch (Exception e) {
deletedShortcuts.add(lc.id);
continue;
}
diff --git a/src/com/android/launcher3/provider/RestoreDbTask.java b/src/com/android/launcher3/provider/RestoreDbTask.java
index 22bc13b..756153e 100644
--- a/src/com/android/launcher3/provider/RestoreDbTask.java
+++ b/src/com/android/launcher3/provider/RestoreDbTask.java
@@ -50,8 +50,10 @@
import androidx.annotation.VisibleForTesting;
import androidx.annotation.WorkerThread;
+import com.android.launcher3.Flags;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.LauncherFiles;
import com.android.launcher3.LauncherPrefs;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.LauncherSettings.Favorites;
@@ -121,7 +123,48 @@
// executed again.
LauncherPrefs.get(context).removeSync(RESTORE_DEVICE);
- idp.reinitializeAfterRestore(context);
+ if (Flags.enableNarrowGridRestore()) {
+ String oldPhoneFileName = idp.dbFile;
+ removeOldDBs(context, oldPhoneFileName);
+ trySettingPreviousGidAsCurrent(context, idp, oldPhoneFileName);
+ } else {
+ idp.reinitializeAfterRestore(context);
+ }
+ }
+
+ /**
+ * Try setting the gird used in the previous phone to the new one. If the current device doesn't
+ * support the previous grid option it will not be set.
+ */
+ private static void trySettingPreviousGidAsCurrent(Context context, InvariantDeviceProfile idp,
+ String oldPhoneDbFileName) {
+ InvariantDeviceProfile.GridOption gridOption = idp.getGridOptionFromFileName(context,
+ oldPhoneDbFileName);
+ if (gridOption != null) {
+ /*
+ * We do this because in some cases different devices have different names for grid
+ * options, in one device the grid option "normal" can be 4x4 while in other it
+ * could be "practical". Calling this changes the current device grid to the same
+ * we had in the other phone, in the case the current phone doesn't support the grid
+ * option we use the default and migrate the db to the default. Migration occurs on
+ * {@code GridSizeMigrationUtil#migrateGridIfNeeded}
+ */
+ idp.setCurrentGrid(context, gridOption.name);
+ }
+ }
+
+ /**
+ * Only keep the last database used on the previous device.
+ */
+ private static void removeOldDBs(Context context, String oldPhoneDbFileName) {
+ // At this point idp.dbFile contains the name of the dbFile from the previous phone
+ LauncherFiles.GRID_DB_FILES.stream()
+ .filter(dbName -> !dbName.equals(oldPhoneDbFileName))
+ .forEach(dbName -> {
+ if (context.getDatabasePath(dbName).delete()) {
+ FileLog.d(TAG, "Removed old grid db file: " + dbName);
+ }
+ });
}
private static boolean performRestore(Context context, ModelDbController controller) {
@@ -413,7 +456,7 @@
logDatabaseWidgetInfo(controller);
for (int i = 0; i < oldWidgetIds.length; i++) {
- FileLog.i(TAG, "Widget state restore id " + oldWidgetIds[i] + " => " + newWidgetIds[i]);
+ FileLog.i(TAG, "migrating appWidgetId: " + oldWidgetIds[i] + " => " + newWidgetIds[i]);
final AppWidgetProviderInfo provider = widgets.getAppWidgetInfo(newWidgetIds[i]);
final int state;
diff --git a/src/com/android/launcher3/states/EditModeState.kt b/src/com/android/launcher3/states/EditModeState.kt
index aafaaa0..6ff47ae 100644
--- a/src/com/android/launcher3/states/EditModeState.kt
+++ b/src/com/android/launcher3/states/EditModeState.kt
@@ -16,6 +16,7 @@
package com.android.launcher3.states
import android.content.Context
+import com.android.launcher3.Flags.enableScalingRevealHomeAnimation
import com.android.launcher3.Launcher
import com.android.launcher3.LauncherState
import com.android.launcher3.logging.StatsLogManager
@@ -25,6 +26,8 @@
class EditModeState(id: Int) : LauncherState(id, StatsLogManager.LAUNCHER_STATE_HOME, STATE_FLAGS) {
companion object {
+ const val DEPTH_15_PERCENT = 0.15f
+
private val STATE_FLAGS =
(FLAG_MULTI_PAGE or
FLAG_WORKSPACE_INACCESSIBLE or
@@ -40,7 +43,11 @@
}
override fun <T> getDepthUnchecked(context: T): Float where T : Context?, T : ActivityContext? {
- return 0.5f
+ if (enableScalingRevealHomeAnimation()) {
+ return DEPTH_15_PERCENT
+ } else {
+ return 0.5f
+ }
}
override fun getWorkspaceScaleAndTranslation(launcher: Launcher): ScaleAndTranslation {
diff --git a/src/com/android/launcher3/states/HintState.java b/src/com/android/launcher3/states/HintState.java
index 4cfced8..bf2fb30 100644
--- a/src/com/android/launcher3/states/HintState.java
+++ b/src/com/android/launcher3/states/HintState.java
@@ -15,6 +15,7 @@
*/
package com.android.launcher3.states;
+import static com.android.launcher3.Flags.enableScalingRevealHomeAnimation;
import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_HOME;
import android.content.Context;
@@ -34,6 +35,8 @@
private static final int STATE_FLAGS = FLAG_WORKSPACE_INACCESSIBLE | FLAG_DISABLE_RESTORE
| FLAG_HAS_SYS_UI_SCRIM;
+ public static final float DEPTH_5_PERCENT = 0.05f;
+
public HintState(int id) {
this(id, LAUNCHER_STATE_HOME);
}
@@ -49,7 +52,11 @@
@Override
protected float getDepthUnchecked(Context context) {
- return 0.15f;
+ if (enableScalingRevealHomeAnimation()) {
+ return DEPTH_5_PERCENT;
+ } else {
+ return 0.15f;
+ }
}
@Override
diff --git a/src/com/android/launcher3/states/SpringLoadedState.java b/src/com/android/launcher3/states/SpringLoadedState.java
index 3286afb..2e57ed8 100644
--- a/src/com/android/launcher3/states/SpringLoadedState.java
+++ b/src/com/android/launcher3/states/SpringLoadedState.java
@@ -15,6 +15,7 @@
*/
package com.android.launcher3.states;
+import static com.android.launcher3.Flags.enableScalingRevealHomeAnimation;
import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_HOME;
import android.content.Context;
@@ -33,6 +34,8 @@
| FLAG_WORKSPACE_INACCESSIBLE | FLAG_DISABLE_RESTORE
| FLAG_WORKSPACE_ICONS_CAN_BE_DRAGGED | FLAG_WORKSPACE_HAS_BACKGROUNDS;
+ public static final float DEPTH_15_PERCENT = 0.15f;
+
public SpringLoadedState(int id) {
super(id, LAUNCHER_STATE_HOME, STATE_FLAGS);
}
@@ -62,7 +65,11 @@
@Override
protected float getDepthUnchecked(Context context) {
- return 0.5f;
+ if (enableScalingRevealHomeAnimation()) {
+ return DEPTH_15_PERCENT;
+ } else {
+ return 0.5f;
+ }
}
@Override
diff --git a/src/com/android/launcher3/testing/TestInformationHandler.java b/src/com/android/launcher3/testing/TestInformationHandler.java
index 07df7af..12cdd67 100644
--- a/src/com/android/launcher3/testing/TestInformationHandler.java
+++ b/src/com/android/launcher3/testing/TestInformationHandler.java
@@ -19,9 +19,9 @@
import static com.android.launcher3.allapps.AllAppsStore.DEFER_UPDATES_TEST;
import static com.android.launcher3.config.FeatureFlags.ENABLE_TASKBAR_NAVBAR_UNIFICATION;
import static com.android.launcher3.config.FeatureFlags.FOLDABLE_SINGLE_PAGE;
+import static com.android.launcher3.config.FeatureFlags.enableAppPairs;
import static com.android.launcher3.config.FeatureFlags.enableSplitContextually;
import static com.android.launcher3.testing.shared.TestProtocol.TEST_INFO_RESPONSE_FIELD;
-import static com.android.launcher3.config.FeatureFlags.enableAppPairs;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import android.app.Activity;
@@ -183,7 +183,10 @@
case TestProtocol.REQUEST_IS_TABLET:
response.putBoolean(TestProtocol.TEST_INFO_RESPONSE_FIELD, mDeviceProfile.isTablet);
return response;
-
+ case TestProtocol.REQUEST_IS_PREDICTIVE_BACK_SWIPE_ENABLED:
+ response.putBoolean(TestProtocol.TEST_INFO_RESPONSE_FIELD,
+ mDeviceProfile.isPredictiveBackSwipe);
+ return response;
case TestProtocol.REQUEST_ENABLE_TASKBAR_NAVBAR_UNIFICATION:
response.putBoolean(TestProtocol.TEST_INFO_RESPONSE_FIELD,
ENABLE_TASKBAR_NAVBAR_UNIFICATION);
diff --git a/src/com/android/launcher3/testing/TestLogging.java b/src/com/android/launcher3/testing/TestLogging.java
index 60d0e95..459fa07 100644
--- a/src/com/android/launcher3/testing/TestLogging.java
+++ b/src/com/android/launcher3/testing/TestLogging.java
@@ -62,7 +62,14 @@
public static void recordKeyEvent(String sequence, String message, KeyEvent event) {
if (Utilities.isRunningInTestHarness()) {
- recordEventSlow(sequence, message + ": " + event, true);
+ // This removes expecting ACTION_DOWN key event in the test. ACTION_UP is
+ // always preceded by ACTION_DOWN.
+ // Sometimes test doesn't receive ACTION_DOWN key event and we will assume that
+ // Launcher relies only on ACTION_UP.
+ // However in the test we will send both ACTION_DOWN and ACTION_UP key events.
+ // But stop reporting to tapl if action is down.
+ boolean reportToTapl = event.getAction() != KeyEvent.ACTION_DOWN;
+ recordEventSlow(sequence, message + ": " + event, reportToTapl);
registerEventNotFromTest(event);
}
}
diff --git a/src/com/android/launcher3/touch/ItemClickHandler.java b/src/com/android/launcher3/touch/ItemClickHandler.java
index 111931e..911568c 100644
--- a/src/com/android/launcher3/touch/ItemClickHandler.java
+++ b/src/com/android/launcher3/touch/ItemClickHandler.java
@@ -369,8 +369,8 @@
intent = ApiWrapper.getAppMarketActivityIntent(launcher,
itemInfoWithIcon.getTargetComponent().getPackageName(),
Process.myUserHandle());
- } else if ((itemInfoWithIcon.runtimeStatusFlags
- & ItemInfoWithIcon.FLAG_PRIVATE_SPACE_INSTALL_APP) != 0) {
+ } else if (itemInfoWithIcon.itemType
+ == LauncherSettings.Favorites.ITEM_TYPE_PRIVATE_SPACE_INSTALL_APP_BUTTON) {
intent = ApiWrapper.getAppMarketActivityIntent(launcher,
BuildConfig.APPLICATION_ID,
launcher.getAppsView().getPrivateProfileManager().getProfileUser());
diff --git a/src/com/android/launcher3/util/PackageManagerHelper.java b/src/com/android/launcher3/util/PackageManagerHelper.java
index 606918e..b66b96a 100644
--- a/src/com/android/launcher3/util/PackageManagerHelper.java
+++ b/src/com/android/launcher3/util/PackageManagerHelper.java
@@ -107,6 +107,7 @@
/**
* Returns whether the target app is archived for a given user
*/
+ @SuppressWarnings("NewApi")
public boolean isAppArchivedForUser(@NonNull final String packageName,
@NonNull final UserHandle user) {
if (!Utilities.enableSupportForArchiving()) {
diff --git a/src/com/android/launcher3/views/AbstractSlideInView.java b/src/com/android/launcher3/views/AbstractSlideInView.java
index 30e0971..4c9371d 100644
--- a/src/com/android/launcher3/views/AbstractSlideInView.java
+++ b/src/com/android/launcher3/views/AbstractSlideInView.java
@@ -296,17 +296,13 @@
float scaleProgress = mSlideInViewScale.value;
SCALE_PROPERTY.set(this, scaleProgress);
setClipChildren(!mIsBackProgressing);
+ setClipToPadding(!mIsBackProgressing);
mContent.setClipChildren(!mIsBackProgressing);
+ mContent.setClipToPadding(!mIsBackProgressing);
invalidate();
}
@Override
- public void onBackInvoked() {
- super.onBackInvoked();
- animateSlideInViewToNoScale();
- }
-
- @Override
public void onBackCancelled() {
super.onBackCancelled();
animateSlideInViewToNoScale();
@@ -351,8 +347,7 @@
/** Return extra space revealed during predictive back animation. */
@Px
protected int getBottomOffsetPx() {
- final int height = getMeasuredHeight();
- return (int) ((height / PREDICTIVE_BACK_MIN_SCALE - height) / 2);
+ return (int) (getMeasuredHeight() * (1 - PREDICTIVE_BACK_MIN_SCALE));
}
/**
diff --git a/src/com/android/launcher3/views/FloatingSurfaceView.java b/src/com/android/launcher3/views/FloatingSurfaceView.java
index c60e1a4..cab7982 100644
--- a/src/com/android/launcher3/views/FloatingSurfaceView.java
+++ b/src/com/android/launcher3/views/FloatingSurfaceView.java
@@ -15,6 +15,7 @@
*/
package com.android.launcher3.views;
+import static com.android.launcher3.model.data.ItemInfo.NO_MATCHING_ID;
import static com.android.launcher3.views.FloatingIconView.getLocationBoundsForView;
import static com.android.launcher3.views.IconLabelDotView.setIconAndDotVisible;
@@ -159,7 +160,7 @@
if (mContract == null) {
return;
}
- View icon = mLauncher.getFirstMatchForAppClose(-1,
+ View icon = mLauncher.getFirstMatchForAppClose(NO_MATCHING_ID,
mContract.componentName.getPackageName(), mContract.user,
false /* supportsAllAppsState */);
diff --git a/src/com/android/launcher3/views/ScrimView.java b/src/com/android/launcher3/views/ScrimView.java
index ca80c51..f6c4984 100644
--- a/src/com/android/launcher3/views/ScrimView.java
+++ b/src/com/android/launcher3/views/ScrimView.java
@@ -26,6 +26,7 @@
import android.view.View;
import androidx.annotation.NonNull;
+import androidx.annotation.Px;
import androidx.core.graphics.ColorUtils;
import com.android.launcher3.BaseActivity;
@@ -187,9 +188,19 @@
* A Utility interface allowing for other surfaces to draw on ScrimView
*/
public interface ScrimDrawingController {
- /**
- * Called inside ScrimView#OnDraw
- */
- void drawOnScrimWithScale(Canvas canvas, float scale);
+
+ /** Draw scrim view on canvas with scale. */
+ default void drawOnScrimWithScale(Canvas canvas, float scale) {
+ drawOnScrimWithScaleAndBottomOffset(canvas, scale, 0);
+ }
+
+ /** Draw scrim view on canvas with bottomOffset. */
+ default void drawOnScrimWithBottomOffset(Canvas canvas, @Px int bottomOffsetPx) {
+ drawOnScrimWithScaleAndBottomOffset(canvas, 1f, bottomOffsetPx);
+ }
+
+ /** Draw scrim view on canvas with scale and bottomOffset. */
+ void drawOnScrimWithScaleAndBottomOffset(
+ Canvas canvas, float scale, @Px int bottomOffsetPx);
}
}
diff --git a/src/com/android/launcher3/views/WidgetsEduView.java b/src/com/android/launcher3/views/WidgetsEduView.java
index 40c6115..45ff9de 100644
--- a/src/com/android/launcher3/views/WidgetsEduView.java
+++ b/src/com/android/launcher3/views/WidgetsEduView.java
@@ -59,6 +59,7 @@
mContent = findViewById(R.id.edu_view);
findViewById(R.id.edu_close_button)
.setOnClickListener(v -> close(/* animate= */ true));
+ setContentBackgroundWithParent(mContent.getBackground(), mContent);
}
@Override
@@ -68,6 +69,12 @@
mContent.getPaddingTop(), mContent.getPaddingEnd(), insets.bottom);
}
+ @Override
+ protected void onScaleProgressChanged() {
+ super.onScaleProgressChanged();
+ setTranslationY(getMeasuredHeight() * (1 - mSlideInViewScale.value) / 2);
+ }
+
private void show() {
attachToContainer();
animateOpen();
diff --git a/src/com/android/launcher3/widget/BaseWidgetSheet.java b/src/com/android/launcher3/widget/BaseWidgetSheet.java
index 54ce973..5266448 100644
--- a/src/com/android/launcher3/widget/BaseWidgetSheet.java
+++ b/src/com/android/launcher3/widget/BaseWidgetSheet.java
@@ -18,6 +18,7 @@
import static com.android.app.animation.Interpolators.EMPHASIZED;
import static com.android.launcher3.Flags.enableCategorizedWidgetSuggestions;
import static com.android.launcher3.Flags.enableUnfoldedTwoPanePicker;
+import static com.android.launcher3.Flags.enableWidgetTapToAdd;
import static com.android.launcher3.LauncherPrefs.WIDGETS_EDUCATION_TIP_SEEN;
import android.content.Context;
@@ -41,8 +42,10 @@
import com.android.launcher3.Insettable;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherPrefs;
+import com.android.launcher3.PendingAddItemInfo;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
+import com.android.launcher3.logging.StatsLogManager;
import com.android.launcher3.popup.PopupDataProvider;
import com.android.launcher3.testing.TestLogging;
import com.android.launcher3.testing.shared.TestProtocol;
@@ -73,6 +76,8 @@
private boolean mDisableNavBarScrim = false;
+ @Nullable private WidgetCell mWidgetCellWithAddButton = null;
+
public BaseWidgetSheet(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mContentHorizontalMargin = getWidgetListHorizontalMargin();
@@ -123,13 +128,49 @@
@Override
public final void onClick(View v) {
- if (v instanceof WidgetCell) {
- mActivityContext.getItemOnClickListener().onClick(v);
- } else if (v.getParent() instanceof WidgetCell wc) {
+ WidgetCell wc;
+ if (v instanceof WidgetCell view) {
+ wc = view;
+ } else if (v.getParent() instanceof WidgetCell parent) {
+ wc = parent;
+ } else {
+ return;
+ }
+
+ if (enableWidgetTapToAdd()) {
+ if (mWidgetCellWithAddButton != null) {
+ // If there is a add button currently showing, hide it.
+ mWidgetCellWithAddButton.hideAddButton(/* animate= */ true);
+ }
+
+ if (mWidgetCellWithAddButton != wc) {
+ // If click is on a cell not showing an add button, show it now.
+ final PendingAddItemInfo info = (PendingAddItemInfo) wc.getTag();
+ if (mActivityContext instanceof Launcher) {
+ wc.showAddButton((view) -> addWidget(info));
+ } else {
+ wc.showAddButton((view) -> mActivityContext.getItemOnClickListener()
+ .onClick(wc));
+ }
+ }
+
+ mWidgetCellWithAddButton = mWidgetCellWithAddButton != wc ? wc : null;
+ } else {
mActivityContext.getItemOnClickListener().onClick(wc);
}
}
+ /**
+ * Click handler for tap to add button.
+ */
+ public void addWidget(PendingAddItemInfo info) {
+ mActivityContext.getStatsLogManager().logger().withItemInfo(info).log(
+ StatsLogManager.LauncherEvent.LAUNCHER_WIDGET_ADD_BUTTON_TAP);
+ handleClose(true);
+ Launcher.getLauncher(mActivityContext).getAccessibilityDelegate()
+ .addToWorkspace(info, /*accessibility=*/ false, /*finishCallback=*/ null);
+ }
+
@Override
public boolean onLongClick(View v) {
TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "Widgets.onLongClick");
diff --git a/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java b/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java
index 2259e3c..c3e9ad6 100644
--- a/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java
+++ b/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java
@@ -92,6 +92,8 @@
private boolean mTrackingWidgetUpdate = false;
+ private int mFocusRectOutsets = 0;
+
public LauncherAppWidgetHostView(Context context) {
super(context);
mActivityContext = ActivityContext.lookupContext(context);
@@ -100,6 +102,8 @@
setBackgroundResource(R.drawable.widget_internal_focus_bg);
if (Flags.enableFocusOutline()) {
setDefaultFocusHighlightEnabled(false);
+ mFocusRectOutsets = context.getResources().getDimensionPixelSize(
+ R.dimen.focus_rect_widget_outsets);
}
if (Themes.getAttrBoolean(context, R.attr.isWorkspaceDarkText)) {
@@ -269,6 +273,13 @@
}
@Override
+ public void getFocusedRect(Rect r) {
+ super.getFocusedRect(r);
+ // Outset to a larger rect for drawing a padding between focus outline and widget
+ r.inset(mFocusRectOutsets, mFocusRectOutsets);
+ }
+
+ @Override
public void onTouchComplete() {
if (!mLongPressHelper.hasPerformedLongPress()) {
// If a long press has been performed, we don't want to clear the record of that since
diff --git a/src/com/android/launcher3/widget/LauncherWidgetHolder.java b/src/com/android/launcher3/widget/LauncherWidgetHolder.java
index 2fcf8c5..0fb4e09 100644
--- a/src/com/android/launcher3/widget/LauncherWidgetHolder.java
+++ b/src/com/android/launcher3/widget/LauncherWidgetHolder.java
@@ -349,7 +349,13 @@
@NonNull
public final AppWidgetHostView attachViewToHostAndGetAttachedView(
@NonNull LauncherAppWidgetHostView view) {
- if (mViews.get(view.getAppWidgetId()) != view) {
+
+ // Binder can also inflate placeholder widgets in case of backup-restore. Skip
+ // attaching such widgets
+ boolean isRealWidget = ((view instanceof PendingAppWidgetHostView pw)
+ ? pw.isDeferredWidget() : true)
+ && view.getAppWidgetInfo() != null;
+ if (isRealWidget && mViews.get(view.getAppWidgetId()) != view) {
view = recycleExistingView(view);
mViews.put(view.getAppWidgetId(), view);
}
diff --git a/src/com/android/launcher3/widget/PendingAppWidgetHostView.java b/src/com/android/launcher3/widget/PendingAppWidgetHostView.java
index 86400ba..9c9b80d 100644
--- a/src/com/android/launcher3/widget/PendingAppWidgetHostView.java
+++ b/src/com/android/launcher3/widget/PendingAppWidgetHostView.java
@@ -29,9 +29,11 @@
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
+import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.Rect;
+import android.graphics.RectF;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
@@ -76,6 +78,10 @@
private final Rect mRect = new Rect();
+ private final Matrix mMatrix = new Matrix();
+ private final RectF mPreviewBitmapRect = new RectF();
+ private final RectF mCanvasRect = new RectF();
+
private final LauncherWidgetHolder mWidgetHolder;
private final LauncherAppWidgetProviderInfo mAppwidget;
private final LauncherAppWidgetInfo mInfo;
@@ -103,9 +109,14 @@
public PendingAppWidgetHostView(Context context, LauncherWidgetHolder widgetHolder,
LauncherAppWidgetInfo info, @Nullable LauncherAppWidgetProviderInfo appWidget) {
- this(context, widgetHolder, info, appWidget,
- context.getResources().getText(R.string.gadget_complete_setup_text));
+ this(context, widgetHolder, info, appWidget, null);
+ }
+ public PendingAppWidgetHostView(Context context, LauncherWidgetHolder widgetHolder,
+ LauncherAppWidgetInfo info, @Nullable LauncherAppWidgetProviderInfo appWidget,
+ @Nullable Bitmap previewBitmap) {
+ this(context, widgetHolder, info, appWidget,
+ context.getResources().getText(R.string.gadget_complete_setup_text), previewBitmap);
super.updateAppWidget(null);
setOnClickListener(mActivityContext.getItemOnClickListener());
@@ -123,7 +134,7 @@
Context context, LauncherWidgetHolder widgetHolder,
int appWidgetId, @NonNull LauncherAppWidgetProviderInfo appWidget) {
this(context, widgetHolder, new LauncherAppWidgetInfo(appWidgetId, appWidget.provider),
- appWidget, appWidget.label);
+ appWidget, appWidget.label, null);
getBackground().mutate().setAlpha(DEFERRED_ALPHA);
mCenterDrawable = new ColorDrawable(Color.TRANSPARENT);
@@ -132,8 +143,12 @@
mIsDeferredWidget = true;
}
- /** Set {@link Bitmap} of widget preview. */
- public void setPreviewBitmap(@Nullable Bitmap previewBitmap) {
+ /**
+ * Set {@link Bitmap} of widget preview and update background drawable. When showing preview
+ * bitmap, we shouldn't draw background.
+ */
+ public void setPreviewBitmapAndUpdateBackground(@Nullable Bitmap previewBitmap) {
+ setBackgroundResource(previewBitmap != null ? 0 : R.drawable.pending_widget_bg);
if (this.mPreviewBitmap == previewBitmap) {
return;
}
@@ -143,7 +158,8 @@
private PendingAppWidgetHostView(Context context,
LauncherWidgetHolder widgetHolder, LauncherAppWidgetInfo info,
- LauncherAppWidgetProviderInfo appwidget, CharSequence label) {
+ LauncherAppWidgetProviderInfo appwidget, CharSequence label,
+ @Nullable Bitmap previewBitmap) {
super(new ContextThemeWrapper(context, R.style.WidgetContainerTheme));
mWidgetHolder = widgetHolder;
mAppwidget = appwidget;
@@ -161,7 +177,7 @@
mPreviewPaint = new Paint(ANTI_ALIAS_FLAG | DITHER_FLAG | FILTER_BITMAP_FLAG);
setWillNotDraw(false);
- setBackgroundResource(R.drawable.pending_widget_bg);
+ setPreviewBitmapAndUpdateBackground(previewBitmap);
}
@Override
@@ -440,7 +456,11 @@
protected void onDraw(Canvas canvas) {
if (mPreviewBitmap != null
&& (mInfo.restoreStatus & LauncherAppWidgetInfo.FLAG_UI_NOT_READY) != 0) {
- canvas.drawBitmap(mPreviewBitmap, 0, 0, mPreviewPaint);
+ mPreviewBitmapRect.set(0, 0, mPreviewBitmap.getWidth(), mPreviewBitmap.getHeight());
+ mCanvasRect.set(0, 0, getWidth(), getHeight());
+
+ mMatrix.setRectToRect(mPreviewBitmapRect, mCanvasRect, Matrix.ScaleToFit.CENTER);
+ canvas.drawBitmap(mPreviewBitmap, mMatrix, mPreviewPaint);
return;
}
if (mCenterDrawable == null) {
@@ -463,7 +483,6 @@
mSetupTextLayout.draw(canvas);
canvas.restore();
}
-
}
/**
diff --git a/src/com/android/launcher3/widget/WidgetCell.java b/src/com/android/launcher3/widget/WidgetCell.java
index f2f83c8..3dff555 100644
--- a/src/com/android/launcher3/widget/WidgetCell.java
+++ b/src/com/android/launcher3/widget/WidgetCell.java
@@ -18,6 +18,7 @@
import static android.appwidget.AppWidgetProviderInfo.WIDGET_CATEGORY_HOME_SCREEN;
+import static com.android.launcher3.Flags.enableWidgetTapToAdd;
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_WIDGETS_TRAY;
import static com.android.launcher3.widget.LauncherAppWidgetProviderInfo.fromProviderInfo;
import static com.android.launcher3.widget.util.WidgetSizes.getWidgetItemSizePx;
@@ -36,6 +37,7 @@
import android.view.ViewGroup;
import android.view.ViewPropertyAnimator;
import android.view.accessibility.AccessibilityNodeInfo;
+import android.widget.Button;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.LinearLayout;
@@ -57,6 +59,8 @@
import com.android.launcher3.model.data.PackageItemInfo;
import com.android.launcher3.util.CancellableTask;
import com.android.launcher3.views.ActivityContext;
+import com.android.launcher3.widget.picker.util.WidgetPreviewContainerSize;
+import com.android.launcher3.widget.util.WidgetSizes;
import java.util.function.Consumer;
@@ -75,18 +79,21 @@
private static final boolean DEBUG = false;
private static final int FADE_IN_DURATION_MS = 90;
+ private static final int ADD_BUTTON_FADE_DURATION_MS = 300;
/**
* The requested scale of the preview container. It can be lower than this as well.
*/
private float mPreviewContainerScale = 1f;
-
+ private Size mPreviewContainerSize = new Size(0, 0);
private FrameLayout mWidgetImageContainer;
private WidgetImageView mWidgetImage;
private ImageView mWidgetBadge;
private TextView mWidgetName;
private TextView mWidgetDims;
private TextView mWidgetDescription;
+ private Button mWidgetAddButton;
+ private LinearLayout mWidgetTextContainer;
private WidgetItem mItem;
private Size mWidgetSize;
@@ -139,6 +146,11 @@
mWidgetName = findViewById(R.id.widget_name);
mWidgetDims = findViewById(R.id.widget_dims);
mWidgetDescription = findViewById(R.id.widget_description);
+ mWidgetTextContainer = findViewById(R.id.widget_text_container);
+ mWidgetAddButton = findViewById(R.id.widget_add_button);
+ if (enableWidgetTapToAdd()) {
+ mWidgetAddButton.setVisibility(INVISIBLE);
+ }
}
public void setRemoteViewsPreview(RemoteViews view) {
@@ -176,6 +188,12 @@
mWidgetDims.setText(null);
mWidgetDescription.setText(null);
mWidgetDescription.setVisibility(GONE);
+ showDescription(true);
+ showDimensions(true);
+
+ if (enableWidgetTapToAdd()) {
+ hideAddButton(/* animate= */ false);
+ }
if (mActiveRequest != null) {
mActiveRequest.cancel();
@@ -186,6 +204,7 @@
mWidgetImageContainer.removeView(mAppWidgetHostViewPreview);
}
mAppWidgetHostViewPreview = null;
+ mPreviewContainerSize = new Size(0, 0);
mAppWidgetHostViewScale = 1f;
mPreviewContainerScale = 1f;
mItem = null;
@@ -201,38 +220,25 @@
* Applies the item to this view
*/
public void applyFromCellItem(WidgetItem item) {
- applyFromCellItem(item, 1f);
- }
-
- /**
- * Applies the item to this view
- */
- public void applyFromCellItem(WidgetItem item, float previewScale) {
- applyFromCellItem(item, previewScale, this::applyPreview, null);
+ applyFromCellItem(item, this::applyPreview, /*cachedPreview=*/null);
}
/**
* Applies the item to this view
* @param item item to apply
- * @param previewScale factor to scale the preview
* @param callback callback when preview is loaded in case the preview is being loaded or cached
* @param cachedPreview previously cached preview bitmap is present
*/
- public void applyFromCellItem(WidgetItem item, float previewScale,
- @NonNull Consumer<Bitmap> callback, @Nullable Bitmap cachedPreview) {
- mPreviewContainerScale = previewScale;
-
+ public void applyFromCellItem(WidgetItem item, @NonNull Consumer<Bitmap> callback,
+ @Nullable Bitmap cachedPreview) {
Context context = getContext();
mItem = item;
mWidgetSize = getWidgetItemSizePx(getContext(), mActivity.getDeviceProfile(), mItem);
+ initPreviewContainerSizeAndScale();
mWidgetName.setText(mItem.label);
- mWidgetName.setContentDescription(
- context.getString(R.string.widget_preview_context_description, mItem.label));
mWidgetDims.setText(context.getString(R.string.widget_dims_format,
mItem.spanX, mItem.spanY));
- mWidgetDims.setContentDescription(context.getString(
- R.string.widget_accessible_dims_format, mItem.spanX, mItem.spanY));
if (!TextUtils.isEmpty(mItem.description)) {
mWidgetDescription.setText(mItem.description);
mWidgetDescription.setVisibility(VISIBLE);
@@ -240,6 +246,14 @@
mWidgetDescription.setVisibility(GONE);
}
+ // Setting the content description on the WidgetCell itself ensures that it remains
+ // screen reader focusable when the add button is showing and the text is hidden.
+ setContentDescription(createContentDescription(context));
+ if (mWidgetAddButton != null) {
+ mWidgetAddButton.setContentDescription(context.getString(
+ R.string.widget_add_button_content_description, mItem.label));
+ }
+
if (item.activityInfo != null) {
setTag(new PendingAddShortcutInfo(item.activityInfo));
} else {
@@ -278,6 +292,27 @@
}
}
+ private void initPreviewContainerSizeAndScale() {
+ WidgetPreviewContainerSize previewSize = WidgetPreviewContainerSize.Companion.forItem(mItem,
+ mActivity.getDeviceProfile());
+ mPreviewContainerSize = WidgetSizes.getWidgetSizePx(mActivity.getDeviceProfile(),
+ previewSize.spanX, previewSize.spanY);
+
+ float scaleX = (float) mPreviewContainerSize.getWidth() / mWidgetSize.getWidth();
+ float scaleY = (float) mPreviewContainerSize.getHeight() / mWidgetSize.getHeight();
+ mPreviewContainerScale = Math.min(scaleX, scaleY);
+ }
+
+ private String createContentDescription(Context context) {
+ String contentDescription =
+ context.getString(R.string.widget_preview_name_and_dims_content_description,
+ mItem.label, mItem.spanX, mItem.spanY);
+ if (!TextUtils.isEmpty(mItem.description)) {
+ contentDescription += " " + mItem.description;
+ }
+ return contentDescription;
+ }
+
private void setAppWidgetHostViewPreview(
NavigableAppWidgetHostView appWidgetHostViewPreview,
LauncherAppWidgetProviderInfo providerInfo,
@@ -384,6 +419,16 @@
}
/**
+ * Shows or hides the dimensions displayed below each widget.
+ *
+ * @param show a flag that shows the dimensions of the widget if {@code true}, hides it if
+ * {@code false}.
+ */
+ public void showDimensions(boolean show) {
+ mWidgetDims.setVisibility(show ? VISIBLE : GONE);
+ }
+
+ /**
* Set whether the app icon, for the app that provides the widget, should be shown next to the
* title text of the widget.
*
@@ -448,17 +493,22 @@
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
ViewGroup.LayoutParams containerLp = mWidgetImageContainer.getLayoutParams();
-
- mAppWidgetHostViewScale = mPreviewContainerScale;
int maxWidth = MeasureSpec.getSize(widthMeasureSpec);
- containerLp.width = Math.round(mWidgetSize.getWidth() * mAppWidgetHostViewScale);
+
+ // mPreviewContainerScale ensures the needed scaling with respect to original widget size.
+ mAppWidgetHostViewScale = mPreviewContainerScale;
+ containerLp.width = mPreviewContainerSize.getWidth();
+ containerLp.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 / mWidgetSize.getWidth();
+ mAppWidgetHostViewScale = (float) containerLp.width / mPreviewContainerSize.getWidth();
+ containerLp.height = Math.round(
+ mPreviewContainerSize.getHeight() * mAppWidgetHostViewScale);
}
- containerLp.height = Math.round(mWidgetSize.getHeight() * mAppWidgetHostViewScale);
- // No need to call mWidgetImageContainer.setLayoutParams as we are in measure pass
+ // No need to call mWidgetImageContainer.setLayoutParams as we are in measure pass
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
@@ -495,4 +545,55 @@
mIconLoadRequest = null;
}
}
+
+ /**
+ * Show tap to add button.
+ * @param callback Callback to be set on the button.
+ */
+ public void showAddButton(View.OnClickListener callback) {
+ mWidgetAddButton.setAlpha(0F);
+ mWidgetAddButton.setVisibility(VISIBLE);
+ mWidgetAddButton.setOnClickListener(callback);
+ mWidgetAddButton.animate().cancel();
+ mWidgetAddButton.animate()
+ .alpha(1F)
+ .setDuration(ADD_BUTTON_FADE_DURATION_MS);
+
+ mWidgetTextContainer.animate().cancel();
+ mWidgetTextContainer.animate()
+ .alpha(0F)
+ .setDuration(ADD_BUTTON_FADE_DURATION_MS)
+ .withEndAction(() -> {
+ mWidgetTextContainer.setVisibility(INVISIBLE);
+ });
+ }
+
+ /**
+ * Hide tap to add button.
+ */
+ public void hideAddButton(boolean animate) {
+ mWidgetAddButton.setOnClickListener(null);
+ mWidgetAddButton.animate().cancel();
+ mWidgetTextContainer.animate().cancel();
+
+ if (!animate) {
+ mWidgetAddButton.setVisibility(INVISIBLE);
+ mWidgetTextContainer.setVisibility(VISIBLE);
+ mWidgetTextContainer.setAlpha(1F);
+ return;
+ }
+
+ mWidgetAddButton.animate()
+ .alpha(0F)
+ .setDuration(ADD_BUTTON_FADE_DURATION_MS)
+ .withEndAction(() -> {
+ mWidgetAddButton.setVisibility(INVISIBLE);
+ });
+
+ mWidgetTextContainer.setAlpha(0F);
+ mWidgetTextContainer.setVisibility(VISIBLE);
+ mWidgetTextContainer.animate()
+ .alpha(1F)
+ .setDuration(ADD_BUTTON_FADE_DURATION_MS);
+ }
}
diff --git a/src/com/android/launcher3/widget/WidgetImageView.java b/src/com/android/launcher3/widget/WidgetImageView.java
index 11f4485..f0a23be 100644
--- a/src/com/android/launcher3/widget/WidgetImageView.java
+++ b/src/com/android/launcher3/widget/WidgetImageView.java
@@ -82,15 +82,27 @@
private void updateDstRectF() {
float myWidth = getWidth();
float myHeight = getHeight();
- float bitmapWidth = mDrawable.getIntrinsicWidth();
+ final float bitmapWidth = mDrawable.getIntrinsicWidth();
+ final float bitmapHeight = mDrawable.getIntrinsicHeight();
+ final float bitmapAspectRatio = bitmapWidth / bitmapHeight;
+ final float containerAspectRatio = myWidth / myHeight;
- final float scale = bitmapWidth > myWidth ? myWidth / bitmapWidth : 1;
- float scaledWidth = bitmapWidth * scale;
- float scaledHeight = mDrawable.getIntrinsicHeight() * scale;
+ // Scale by width if image has larger aspect ratio than the container else by height; and
+ // avoid cropping the previews
+ final float scale = bitmapAspectRatio > containerAspectRatio ? myWidth / bitmapWidth
+ : myHeight / bitmapHeight;
- mDstRectF.left = (myWidth - scaledWidth) / 2;
- mDstRectF.right = (myWidth + scaledWidth) / 2;
+ final float scaledWidth = bitmapWidth * scale;
+ final float scaledHeight = bitmapHeight * scale;
+ // Avoid cropping by checking bounds after scaling.
+ if (scaledWidth > myWidth) {
+ mDstRectF.left = 0;
+ mDstRectF.right = scaledWidth;
+ } else {
+ mDstRectF.left = (myWidth - scaledWidth) / 2;
+ mDstRectF.right = (myWidth + scaledWidth) / 2;
+ }
if (scaledHeight > myHeight) {
mDstRectF.top = 0;
mDstRectF.bottom = scaledHeight;
diff --git a/src/com/android/launcher3/widget/WidgetManagerHelper.java b/src/com/android/launcher3/widget/WidgetManagerHelper.java
index 52767a4..ba7e075 100644
--- a/src/com/android/launcher3/widget/WidgetManagerHelper.java
+++ b/src/com/android/launcher3/widget/WidgetManagerHelper.java
@@ -30,6 +30,7 @@
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
+import com.android.launcher3.logging.FileLog;
import com.android.launcher3.model.WidgetsModel;
import com.android.launcher3.model.data.LauncherAppWidgetInfo;
import com.android.launcher3.pm.UserCache;
@@ -46,6 +47,7 @@
*/
public class WidgetManagerHelper {
+ private static final String TAG = "WidgetManagerHelper";
//TODO: replace this with OPTION_APPWIDGET_RESTORE_COMPLETED b/63667276
public static final String WIDGET_OPTION_RESTORE_COMPLETED = "appWidgetRestoreCompleted";
@@ -60,6 +62,7 @@
/**
* @see AppWidgetManager#getAppWidgetInfo(int)
*/
+ @Nullable
public LauncherAppWidgetProviderInfo getLauncherAppWidgetInfo(
int appWidgetId, ComponentName componentName) {
@@ -69,7 +72,14 @@
}
AppWidgetProviderInfo info = mAppWidgetManager.getAppWidgetInfo(appWidgetId);
- return info == null ? null : LauncherAppWidgetProviderInfo.fromProviderInfo(mContext, info);
+ if (info == null) {
+ FileLog.w(TAG,
+ "getLauncherAppWidgetInfo: AppWidgetManager returned null AppWidgetInfo for"
+ + " appWidgetId=" + appWidgetId
+ + ", componentName=" + componentName);
+ return null;
+ }
+ return LauncherAppWidgetProviderInfo.fromProviderInfo(mContext, info);
}
/**
@@ -137,9 +147,8 @@
/**
* Load RemoteViews preview for this provider if available.
*
- * @param info The provider info for the widget you want to preview.
+ * @param info The provider info for the widget you want to preview.
* @param widgetCategory The widget category for which you want to display previews.
- *
* @return Returns the widget preview that matches selected category, if available.
*/
@Nullable
diff --git a/src/com/android/launcher3/widget/WidgetsBottomSheet.java b/src/com/android/launcher3/widget/WidgetsBottomSheet.java
index ceb0072..e6b9c9b 100644
--- a/src/com/android/launcher3/widget/WidgetsBottomSheet.java
+++ b/src/com/android/launcher3/widget/WidgetsBottomSheet.java
@@ -16,7 +16,6 @@
package com.android.launcher3.widget;
-import static com.android.launcher3.Flags.enableCategorizedWidgetSuggestions;
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_BOTTOM_WIDGETS_TRAY;
import android.content.Context;
@@ -119,6 +118,9 @@
mContent = findViewById(R.id.widgets_bottom_sheet);
setContentBackgroundWithParent(
getContext().getDrawable(R.drawable.bg_rounded_corner_bottom_sheet), mContent);
+ View scrollView = findViewById(R.id.widgets_table_scroll_view);
+ scrollView.setOutlineProvider(mViewOutlineProvider);
+ scrollView.setClipToOutline(true);
}
@Override
@@ -188,13 +190,7 @@
mWidgetCellHorizontalPadding)
.forEach(row -> {
TableRow tableRow = new TableRow(getContext());
- if (enableCategorizedWidgetSuggestions()) {
- // Vertically center align items, so that even if they don't fill bounds,
- // they can look organized when placed together in a row.
- tableRow.setGravity(Gravity.CENTER_VERTICAL);
- } else {
- tableRow.setGravity(Gravity.TOP);
- }
+ tableRow.setGravity(Gravity.TOP);
row.forEach(widgetItem -> {
WidgetCell widget = addItemCell(tableRow);
widget.applyFromCellItem(widgetItem);
diff --git a/src/com/android/launcher3/widget/model/WidgetsListHeaderEntry.java b/src/com/android/launcher3/widget/model/WidgetsListHeaderEntry.java
index 68f18ae..0d775c3 100644
--- a/src/com/android/launcher3/widget/model/WidgetsListHeaderEntry.java
+++ b/src/com/android/launcher3/widget/model/WidgetsListHeaderEntry.java
@@ -36,41 +36,49 @@
(context, entry) -> entry.mWidgets.stream()
.map(item -> item.label).sorted().collect(Collectors.joining(", "));
- private static final BiFunction<Context, WidgetsListHeaderEntry, String> SUBTITLE_DEFAULT =
- (context, entry) -> {
- List<WidgetItem> items = entry.mWidgets;
- int wc = (int) items.stream().filter(item -> item.widgetInfo != null).count();
- int sc = Math.max(0, items.size() - wc);
+ @Nullable
+ private static String buildWidgetsCountString(Context context, int wc, int sc) {
+ Resources resources = context.getResources();
+ if (wc == 0 && sc == 0) {
+ return null;
+ }
- Resources resources = context.getResources();
- if (wc == 0 && sc == 0) {
- return null;
- }
-
- String subtitle;
- if (wc > 0 && sc > 0) {
- String widgetsCount = PluralMessageFormat.getIcuPluralString(context,
- R.string.widgets_count, wc);
- String shortcutsCount = PluralMessageFormat.getIcuPluralString(context,
- R.string.shortcuts_count, sc);
- subtitle = resources.getString(R.string.widgets_and_shortcuts_count,
- widgetsCount, shortcutsCount);
- } else if (wc > 0) {
- subtitle = PluralMessageFormat.getIcuPluralString(context,
- R.string.widgets_count, wc);
- } else {
- subtitle = PluralMessageFormat.getIcuPluralString(context,
- R.string.shortcuts_count, sc);
- }
- return subtitle;
- };
+ String subtitle;
+ if (wc > 0 && sc > 0) {
+ String widgetsCount = PluralMessageFormat.getIcuPluralString(context,
+ R.string.widgets_count, wc);
+ String shortcutsCount = PluralMessageFormat.getIcuPluralString(context,
+ R.string.shortcuts_count, sc);
+ subtitle = resources.getString(R.string.widgets_and_shortcuts_count,
+ widgetsCount, shortcutsCount);
+ } else if (wc > 0) {
+ subtitle = PluralMessageFormat.getIcuPluralString(context,
+ R.string.widgets_count, wc);
+ } else {
+ subtitle = PluralMessageFormat.getIcuPluralString(context,
+ R.string.shortcuts_count, sc);
+ }
+ return subtitle;
+ }
private final boolean mIsWidgetListShown;
+ /** Selected widgets displayed */
+ private final int mVisibleWidgetsCount;
private final boolean mIsSearchEntry;
private WidgetsListHeaderEntry(PackageItemInfo pkgItem, String titleSectionName,
+ List<WidgetItem> items, int visibleWidgetsCount,
+ boolean isSearchEntry, boolean isWidgetListShown) {
+ super(pkgItem, titleSectionName, items);
+ mVisibleWidgetsCount = visibleWidgetsCount;
+ mIsSearchEntry = isSearchEntry;
+ mIsWidgetListShown = isWidgetListShown;
+ }
+
+ private WidgetsListHeaderEntry(PackageItemInfo pkgItem, String titleSectionName,
List<WidgetItem> items, boolean isSearchEntry, boolean isWidgetListShown) {
super(pkgItem, titleSectionName, items);
+ mVisibleWidgetsCount = (int) items.stream().filter(w -> w.widgetInfo != null).count();
mIsSearchEntry = isSearchEntry;
mIsWidgetListShown = isWidgetListShown;
}
@@ -91,8 +99,13 @@
@Nullable
public String getSubtitle(Context context) {
- return mIsSearchEntry
- ? SUBTITLE_SEARCH.apply(context, this) : SUBTITLE_DEFAULT.apply(context, this);
+ if (mIsSearchEntry) {
+ return SUBTITLE_SEARCH.apply(context, this);
+ } else {
+ int shortcutsCount = Math.max(0,
+ (int) mWidgets.stream().filter(WidgetItem::isShortcut).count());
+ return buildWidgetsCountString(context, mVisibleWidgetsCount, shortcutsCount);
+ }
}
@Override
@@ -102,6 +115,7 @@
return mWidgets.equals(otherEntry.mWidgets) && mPkgItem.equals(otherEntry.mPkgItem)
&& mTitleSectionName.equals(otherEntry.mTitleSectionName)
&& mIsWidgetListShown == otherEntry.mIsWidgetListShown
+ && mVisibleWidgetsCount == otherEntry.mVisibleWidgetsCount
&& mIsSearchEntry == otherEntry.mIsSearchEntry;
}
@@ -112,6 +126,7 @@
mPkgItem,
mTitleSectionName,
mWidgets,
+ mVisibleWidgetsCount,
mIsSearchEntry,
/* isWidgetListShown= */ true);
}
@@ -122,7 +137,28 @@
pkgItem,
titleSectionName,
items,
- /* forSearch */ false,
+ /* isSearchEntry= */ false,
+ /* isWidgetListShown= */ false);
+ }
+
+ /**
+ * Creates a widget list holder for an header ("app" / "suggestions") which has widgets or/and
+ * shortcuts.
+ *
+ * @param pkgItem package item info for the header section
+ * @param titleSectionName title string for the header
+ * @param items all items for the given header
+ * @param visibleWidgetsCount widgets count when only selected widgets are shown due to
+ * limited space.
+ */
+ public static WidgetsListHeaderEntry create(PackageItemInfo pkgItem, String titleSectionName,
+ List<WidgetItem> items, int visibleWidgetsCount) {
+ return new WidgetsListHeaderEntry(
+ pkgItem,
+ titleSectionName,
+ items,
+ visibleWidgetsCount,
+ /* isSearchEntry= */ false,
/* isWidgetListShown= */ false);
}
@@ -132,7 +168,7 @@
pkgItem,
titleSectionName,
items,
- /* forSearch */ true,
+ /* isSearchEntry */ true,
/* isWidgetListShown= */ false);
}
}
diff --git a/src/com/android/launcher3/widget/picker/WidgetRecommendationCategoryProvider.java b/src/com/android/launcher3/widget/picker/WidgetRecommendationCategoryProvider.java
index 801b1f6..c3906a6 100644
--- a/src/com/android/launcher3/widget/picker/WidgetRecommendationCategoryProvider.java
+++ b/src/com/android/launcher3/widget/picker/WidgetRecommendationCategoryProvider.java
@@ -18,13 +18,13 @@
import android.content.Context;
import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.util.Log;
+import androidx.annotation.Nullable;
import androidx.annotation.WorkerThread;
import com.android.launcher3.R;
import com.android.launcher3.model.WidgetItem;
+import com.android.launcher3.util.PackageManagerHelper;
import com.android.launcher3.util.Preconditions;
import com.android.launcher3.util.ResourceBasedOverride;
@@ -54,6 +54,7 @@
* to display the recommendation grouped by categories.
*/
@WorkerThread
+ @Nullable
public WidgetRecommendationCategory getWidgetRecommendationCategory(Context context,
WidgetItem item) {
// This is a default implementation that uses application category to derive the category to
@@ -61,17 +62,16 @@
// via the overridden WidgetRecommendationCategoryProvider resource.
Preconditions.assertWorkerThread();
- PackageManager pm = context.getPackageManager();
+ PackageManagerHelper pmHelper = new PackageManagerHelper(context);
if (item.widgetInfo != null && item.widgetInfo.getComponent() != null) {
String widgetComponentName = item.widgetInfo.getComponent().getClassName();
- try {
- int predictionCategory = pm.getApplicationInfo(
- item.widgetInfo.getComponent().getPackageName(), 0 /* flags */).category;
+ ApplicationInfo applicationInfo = pmHelper.getApplicationInfo(
+ item.widgetInfo.getComponent().getPackageName(), item.widgetInfo.getUser(),
+ 0 /* flags */);
+ if (applicationInfo != null) {
+ int predictionCategory = applicationInfo.category;
return getCategoryFromApplicationCategory(context, predictionCategory,
widgetComponentName);
- } catch (PackageManager.NameNotFoundException e) {
- Log.e(TAG, "Failed to retrieve application category when determining the "
- + "widget category for " + widgetComponentName, e);
}
}
return null;
@@ -112,17 +112,22 @@
R.string.news_widget_recommendation_category_label, /*order=*/1);
}
- if (applicationCategory == ApplicationInfo.CATEGORY_SOCIAL
- || applicationCategory == ApplicationInfo.CATEGORY_AUDIO
+ if (applicationCategory == ApplicationInfo.CATEGORY_SOCIAL) {
+ return new WidgetRecommendationCategory(
+ R.string.social_widget_recommendation_category_label,
+ /*order=*/5);
+ }
+
+ if (applicationCategory == ApplicationInfo.CATEGORY_AUDIO
|| applicationCategory == ApplicationInfo.CATEGORY_VIDEO
|| applicationCategory == ApplicationInfo.CATEGORY_IMAGE) {
return new WidgetRecommendationCategory(
- R.string.social_and_entertainment_widget_recommendation_category_label,
- /*order=*/4);
+ R.string.entertainment_widget_recommendation_category_label,
+ /*order=*/6);
}
return new WidgetRecommendationCategory(
- R.string.others_widget_recommendation_category_label, /*order=*/5);
+ R.string.others_widget_recommendation_category_label, /*order=*/4);
}
}
diff --git a/src/com/android/launcher3/widget/picker/WidgetRecommendationsView.java b/src/com/android/launcher3/widget/picker/WidgetRecommendationsView.java
index 426a3ae..255a6d2 100644
--- a/src/com/android/launcher3/widget/picker/WidgetRecommendationsView.java
+++ b/src/com/android/launcher3/widget/picker/WidgetRecommendationsView.java
@@ -38,6 +38,7 @@
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
+import java.util.function.Consumer;
/**
* A {@link PagedView} that displays widget recommendations in categories with dots as paged
@@ -45,11 +46,13 @@
*/
public final class WidgetRecommendationsView extends PagedView<PageIndicatorDots> {
private @Px float mAvailableHeight = Float.MAX_VALUE;
-
private static final int MAX_CATEGORIES = 3;
private TextView mRecommendationPageTitle;
private final List<String> mCategoryTitles = new ArrayList<>();
+ /** Callbacks to run when page changes */
+ private final List<Consumer<Integer>> mPageSwitchListeners = new ArrayList<>();
+
@Nullable
private OnLongClickListener mWidgetCellOnLongClickListener;
@Nullable
@@ -84,6 +87,13 @@
}
/**
+ * Add a callback to run when the current displayed page changes.
+ */
+ public void addPageSwitchListener(Consumer<Integer> pageChangeListener) {
+ mPageSwitchListeners.add(pageChangeListener);
+ }
+
+ /**
* Displays all the provided recommendations in a single table if they fit.
*
* @param recommendedWidgets list of widgets to be displayed in recommendation section.
@@ -93,18 +103,19 @@
* @param availableWidth width in px that the recommendations should display in
* @param cellPadding padding in px that should be applied to each widget in the
* recommendations
- * @return {@code false} if no recommendations could fit in the available space.
+ * @return number of recommendations that could fit in the available space.
*/
- public boolean setRecommendations(
+ public int setRecommendations(
List<WidgetItem> recommendedWidgets, DeviceProfile deviceProfile,
final @Px float availableHeight, final @Px int availableWidth,
final @Px int cellPadding) {
this.mAvailableHeight = availableHeight;
- removeAllViews();
+ clear();
- maybeDisplayInTable(recommendedWidgets, deviceProfile, availableWidth, cellPadding);
- updateTitleAndIndicator();
- return getChildCount() > 0;
+ int displayedWidgets = maybeDisplayInTable(recommendedWidgets, deviceProfile,
+ availableWidth, cellPadding);
+ updateTitleAndIndicator(/* requestedPage= */ 0);
+ return displayedWidgets;
}
/**
@@ -118,29 +129,35 @@
* @param availableWidth width in px that the recommendations should display in
* @param cellPadding padding in px that should be applied to each widget in the
* recommendations
- * @return {@code false} if no recommendations could fit in the available space.
+ * @param requestedPage page number to display initially.
+ * @return number of recommendations that could fit in the available space.
*/
- public boolean setRecommendations(
+ public int setRecommendations(
Map<WidgetRecommendationCategory, List<WidgetItem>> recommendations,
- DeviceProfile deviceProfile,
- final @Px float availableHeight, final @Px int availableWidth,
- final @Px int cellPadding) {
+ DeviceProfile deviceProfile, final @Px float availableHeight,
+ final @Px int availableWidth, final @Px int cellPadding, final int requestedPage) {
this.mAvailableHeight = availableHeight;
Context context = getContext();
- mPageIndicator.setPauseScroll(true, deviceProfile.isTwoPanels);
- removeAllViews();
+ // For purpose of recommendations section, we don't want paging dots to be halved in two
+ // pane display, so, we always provide isTwoPanels = "false".
+ mPageIndicator.setPauseScroll(/*pause=*/true, /*isTwoPanels=*/ false);
+ clear();
int displayedCategories = 0;
+ int totalDisplayedWidgets = 0;
// Render top MAX_CATEGORIES in separate tables. Each table becomes a page.
for (Map.Entry<WidgetRecommendationCategory, List<WidgetItem>> entry :
new TreeMap<>(recommendations).entrySet()) {
// If none of the recommendations for the category could fit in the mAvailableHeight, we
// don't want to add that category; and we look for the next one.
- if (maybeDisplayInTable(entry.getValue(), deviceProfile, availableWidth, cellPadding)) {
+ int displayedCount = maybeDisplayInTable(entry.getValue(), deviceProfile,
+ availableWidth, cellPadding);
+ if (displayedCount > 0) {
mCategoryTitles.add(
context.getResources().getString(entry.getKey().categoryTitleRes));
displayedCategories++;
+ totalDisplayedWidgets += displayedCount;
}
if (displayedCategories == MAX_CATEGORIES) {
@@ -148,21 +165,33 @@
}
}
- updateTitleAndIndicator();
- mPageIndicator.setPauseScroll(false, deviceProfile.isTwoPanels);
- return getChildCount() > 0;
+ updateTitleAndIndicator(requestedPage);
+ // For purpose of recommendations section, we don't want paging dots to be halved in two
+ // pane display, so, we always provide isTwoPanels = "false".
+ mPageIndicator.setPauseScroll(/*pause=*/false, /*isTwoPanels=*/false);
+ return totalDisplayedWidgets;
+ }
+
+ private void clear() {
+ mCategoryTitles.clear();
+ removeAllViews();
+ setCurrentPage(0);
+ mPageIndicator.setActiveMarker(0);
}
/** Displays the page title and paging indicator if there are multiple pages. */
- private void updateTitleAndIndicator() {
+ private void updateTitleAndIndicator(int requestedPage) {
boolean showPaginatedView = getPageCount() > 1;
int titleAndIndicatorVisibility = showPaginatedView ? View.VISIBLE : View.GONE;
mRecommendationPageTitle.setVisibility(titleAndIndicatorVisibility);
mPageIndicator.setVisibility(titleAndIndicatorVisibility);
if (showPaginatedView) {
- mPageIndicator.setActiveMarker(0);
- setCurrentPage(0);
- mRecommendationPageTitle.setText(mCategoryTitles.get(0));
+ if (requestedPage <= 0 || requestedPage >= getPageCount()) {
+ requestedPage = 0;
+ }
+ setCurrentPage(requestedPage);
+ mPageIndicator.setActiveMarker(requestedPage);
+ mRecommendationPageTitle.setText(mCategoryTitles.get(requestedPage));
}
}
@@ -170,9 +199,10 @@
protected void notifyPageSwitchListener(int prevPage) {
if (getPageCount() > 1) {
// Since the title is outside the paging scroll, we update the title on page switch.
- mRecommendationPageTitle.setText(mCategoryTitles.get(getNextPage()));
+ int nextPage = getNextPage();
+ mRecommendationPageTitle.setText(mCategoryTitles.get(nextPage));
+ mPageSwitchListeners.forEach(listener -> listener.accept(nextPage));
super.notifyPageSwitchListener(prevPage);
- requestLayout();
}
}
@@ -199,21 +229,8 @@
for (int i = 0; i < getChildCount(); i++) {
View child = getChildAt(i);
measureChild(child, widthMeasureSpec, heightMeasureSpec);
- if (mAvailableHeight == Float.MAX_VALUE) {
- // When we are not limited by height, use currentPage's height. This is the case
- // when the paged layout is placed in a scrollable container. We cannot use
- // height
- // of tallest child in such case, as it will display a scrollbar even for
- // smaller
- // pages that don't have more content.
- if (i == mCurrentPage) {
- int parentHeight = MeasureSpec.getSize(heightMeasureSpec);
- desiredHeight = Math.max(parentHeight, child.getMeasuredHeight());
- }
- } else {
- // Use height of tallest child when we are limited to a certain height.
- desiredHeight = Math.max(desiredHeight, child.getMeasuredHeight());
- }
+ // Use height of tallest child as we have limited height.
+ desiredHeight = Math.max(desiredHeight, child.getMeasuredHeight());
}
int finalHeight = resolveSizeAndState(desiredHeight, heightMeasureSpec, 0);
@@ -228,12 +245,14 @@
* fits.
* <p>Returns false if none of the recommendations could fit.</p>
*/
- private boolean maybeDisplayInTable(List<WidgetItem> recommendedWidgets,
+ private int maybeDisplayInTable(List<WidgetItem> recommendedWidgets,
DeviceProfile deviceProfile,
final @Px int availableWidth, final @Px int cellPadding) {
Context context = getContext();
LayoutInflater inflater = LayoutInflater.from(context);
+ // Since we are limited by space, we don't sort recommendations - to show most relevant
+ // (if possible).
List<ArrayList<WidgetItem>> rows = groupWidgetItemsUsingRowPxWithoutReordering(
recommendedWidgets,
context,
@@ -249,13 +268,13 @@
recommendationsTable.setWidgetCellOnClickListener(mWidgetCellOnClickListener);
recommendationsTable.setWidgetCellLongClickListener(mWidgetCellOnLongClickListener);
- boolean displayedAtLeastOne = recommendationsTable.setRecommendedWidgets(rows,
+ int displayedCount = recommendationsTable.setRecommendedWidgets(rows,
deviceProfile, mAvailableHeight);
- if (displayedAtLeastOne) {
+ if (displayedCount > 0) {
addView(recommendationsTable);
}
- return displayedAtLeastOne;
+ return displayedCount;
}
/** Returns location of a widget cell for displaying the "touch and hold" education tip. */
diff --git a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
index f5742af..ba6c4cf 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
@@ -15,8 +15,6 @@
*/
package com.android.launcher3.widget.picker;
-import static android.view.View.MeasureSpec.makeMeasureSpec;
-
import static com.android.launcher3.Flags.enableCategorizedWidgetSuggestions;
import static com.android.launcher3.Flags.enableUnfoldedTwoPanePicker;
import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y;
@@ -30,6 +28,7 @@
import android.content.res.Resources;
import android.graphics.Rect;
import android.os.Build;
+import android.os.Bundle;
import android.os.Parcelable;
import android.os.Process;
import android.os.UserHandle;
@@ -67,6 +66,7 @@
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.compat.AccessibilityManagerCompat;
import com.android.launcher3.model.UserManagerState;
+import com.android.launcher3.model.WidgetItem;
import com.android.launcher3.pm.UserCache;
import com.android.launcher3.views.ArrowTipView;
import com.android.launcher3.views.RecyclerViewFastScroller;
@@ -81,7 +81,9 @@
import com.android.launcher3.workprofile.PersonalWorkSlidingTabStrip.OnActivePageChangedListener;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import java.util.function.Predicate;
import java.util.stream.IntStream;
@@ -98,8 +100,11 @@
// The widget recommendation table can easily take over the entire screen on devices with small
// resolution or landscape on phone. This ratio defines the max percentage of content area that
- // the table can display.
- private static final float RECOMMENDATION_TABLE_HEIGHT_RATIO = 0.75f;
+ // the table can display with respect to bottom sheet's height.
+ private static final float RECOMMENDATION_TABLE_HEIGHT_RATIO = 0.45f;
+ private static final String RECOMMENDATIONS_SAVED_STATE_KEY =
+ "widgetsFullSheet:mRecommendationsCurrentPage";
+ private static final String SUPER_SAVED_STATE_KEY = "widgetsFullSheet:superHierarchyState";
private final UserCache mUserCache;
private final UserManagerState mUserManagerState = new UserManagerState();
private final UserHandle mCurrentUser = Process.myUserHandle();
@@ -107,9 +112,15 @@
entry -> mCurrentUser.equals(entry.mPkgItem.user);
private final Predicate<WidgetsListBaseEntry> mWorkWidgetsFilter;
protected final boolean mHasWorkProfile;
- protected boolean mHasRecommendedWidgets;
+ // Number of recommendations displayed
+ protected int mRecommendedWidgetsCount;
+ private List<WidgetItem> mRecommendedWidgets = new ArrayList<>();
+ private Map<WidgetRecommendationCategory, List<WidgetItem>> mRecommendedWidgetsMap =
+ new HashMap<>();
+ protected int mRecommendationsCurrentPage = 0;
protected final SparseArray<AdapterHolder> mAdapters = new SparseArray();
- @Nullable private ArrowTipView mLatestEducationalTip;
+ @Nullable
+ private ArrowTipView mLatestEducationalTip;
private final OnLayoutChangeListener mLayoutChangeListenerToShowTips =
new OnLayoutChangeListener() {
@Override
@@ -155,14 +166,19 @@
}
};
- @Px private final int mTabsHeight;
+ @Px
+ private final int mTabsHeight;
- @Nullable private WidgetsRecyclerView mCurrentWidgetsRecyclerView;
- @Nullable private WidgetsRecyclerView mCurrentTouchEventRecyclerView;
- @Nullable PersonalWorkPagedView mViewPager;
+ @Nullable
+ private WidgetsRecyclerView mCurrentWidgetsRecyclerView;
+ @Nullable
+ private WidgetsRecyclerView mCurrentTouchEventRecyclerView;
+ @Nullable
+ PersonalWorkPagedView mViewPager;
private boolean mIsInSearchMode;
private boolean mIsNoWidgetsViewNeeded;
- @Px protected int mMaxSpanPerRow;
+ @Px
+ protected int mMaxSpanPerRow;
protected DeviceProfile mDeviceProfile;
protected TextView mNoWidgetsView;
@@ -226,13 +242,16 @@
R.id.widget_recommendations_container);
mWidgetRecommendationsView = mSearchScrollView.findViewById(
R.id.widget_recommendations_view);
+ // To save the currently displayed page, so that, it can be requested when rebinding
+ // recommendations with different size constraints.
+ mWidgetRecommendationsView.addPageSwitchListener(
+ newPage -> mRecommendationsCurrentPage = newPage);
mWidgetRecommendationsView.initParentViews(mWidgetRecommendationsContainer);
mWidgetRecommendationsView.setWidgetCellLongClickListener(this);
mWidgetRecommendationsView.setWidgetCellOnClickListener(this);
mHeaderTitle = mSearchScrollView.findViewById(R.id.title);
- onRecommendedWidgetsBound();
onWidgetsBound();
setUpEducationViewsIfNeeded();
}
@@ -291,13 +310,6 @@
attachScrollbarToRecyclerView(currentRecyclerView);
}
- @Override
- @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
- public void onBackProgressed(@NonNull BackEvent backEvent) {
- super.onBackProgressed(backEvent);
- mFastScroller.setVisibility(backEvent.getProgress() > 0 ? View.INVISIBLE : View.VISIBLE);
- }
-
private void attachScrollbarToRecyclerView(WidgetsRecyclerView recyclerView) {
recyclerView.bindFastScrollbar(mFastScroller);
if (mCurrentWidgetsRecyclerView != recyclerView) {
@@ -360,7 +372,6 @@
super.onAttachedToWindow();
LauncherAppState.getInstance(mActivityContext).getModel()
.refreshAndBindWidgetsAndShortcuts(null);
- onRecommendedWidgetsBound();
}
@Override
@@ -588,44 +599,46 @@
}
if (enableCategorizedWidgetSuggestions()) {
- mHasRecommendedWidgets = mWidgetRecommendationsView.setRecommendations(
- mActivityContext.getPopupDataProvider().getCategorizedRecommendedWidgets(),
+ // We avoid applying new recommendations when some are already displayed.
+ if (mRecommendedWidgetsMap.isEmpty()) {
+ mRecommendedWidgetsMap =
+ mActivityContext.getPopupDataProvider().getCategorizedRecommendedWidgets();
+ }
+ mRecommendedWidgetsCount = mWidgetRecommendationsView.setRecommendations(
+ mRecommendedWidgetsMap,
mDeviceProfile,
/* availableHeight= */ getMaxAvailableHeightForRecommendations(),
/* availableWidth= */ mMaxSpanPerRow,
- /* cellPadding= */ mWidgetCellHorizontalPadding
+ /* cellPadding= */ mWidgetCellHorizontalPadding,
+ /* requestedPage= */ mRecommendationsCurrentPage
);
} else {
- mHasRecommendedWidgets = mWidgetRecommendationsView.setRecommendations(
- mActivityContext.getPopupDataProvider().getRecommendedWidgets(),
+ if (mRecommendedWidgets.isEmpty()) {
+ mRecommendedWidgets =
+ mActivityContext.getPopupDataProvider().getRecommendedWidgets();
+ }
+ mRecommendedWidgetsCount = mWidgetRecommendationsView.setRecommendations(
+ mRecommendedWidgets,
mDeviceProfile,
/* availableHeight= */ getMaxAvailableHeightForRecommendations(),
/* availableWidth= */ mMaxSpanPerRow,
/* cellPadding= */ mWidgetCellHorizontalPadding
);
}
- mWidgetRecommendationsContainer.setVisibility(mHasRecommendedWidgets ? VISIBLE : GONE);
+ mWidgetRecommendationsContainer.setVisibility(
+ mRecommendedWidgetsCount > 0 ? VISIBLE : GONE);
}
@Px
private float getMaxAvailableHeightForRecommendations() {
- float noWidgetsViewHeight = 0;
- if (mIsNoWidgetsViewNeeded) {
- // Make sure recommended section leaves enough space for noWidgetsView.
- Rect noWidgetsViewTextBounds = new Rect();
- mNoWidgetsView.getPaint()
- .getTextBounds(mNoWidgetsView.getText().toString(), /* start= */ 0,
- mNoWidgetsView.getText().length(), noWidgetsViewTextBounds);
- noWidgetsViewHeight = noWidgetsViewTextBounds.height();
+ // There isn't enough space to show recommendations in landscape orientation on phones with
+ // a full sheet design. Tablets use a two pane picker.
+ if (!isTwoPane() && mDeviceProfile.isLandscape) {
+ return 0f;
}
- if (!isTwoPane()) {
- doMeasure(
- makeMeasureSpec(mActivityContext.getDeviceProfile().availableWidthPx,
- MeasureSpec.EXACTLY),
- makeMeasureSpec(mActivityContext.getDeviceProfile().availableHeightPx,
- MeasureSpec.EXACTLY));
- }
- return getMaxTableHeight(noWidgetsViewHeight);
+
+ return (mDeviceProfile.heightPx - mDeviceProfile.bottomSheetTopPadding)
+ * getRecommendationSectionHeightRatio();
}
/** b/209579563: "Widgets" header should be focused first. */
@@ -634,12 +647,12 @@
return mHeaderTitle;
}
+ /**
+ * Ratio of recommendations section with respect to bottom sheet's height on scale of 0 to 1.
+ */
@Px
- protected float getMaxTableHeight(@Px float noWidgetsViewHeight) {
- return (mContent.getMeasuredHeight()
- - mTabsHeight - getHeaderViewHeight()
- - noWidgetsViewHeight)
- * RECOMMENDATION_TABLE_HEIGHT_RATIO;
+ protected float getRecommendationSectionHeightRatio() {
+ return RECOMMENDATION_TABLE_HEIGHT_RATIO;
}
private void open(boolean animate) {
@@ -710,6 +723,27 @@
return sheet;
}
+ @Override
+ public void saveHierarchyState(SparseArray<Parcelable> sparseArray) {
+ Bundle bundle = new Bundle();
+ // With widget picker open, when we open shade to switch theme, Launcher re-creates the
+ // picker and calls save/restore hierarchy state. We save the state of recommendations
+ // across those updates.
+ bundle.putInt(RECOMMENDATIONS_SAVED_STATE_KEY, mRecommendationsCurrentPage);
+ SparseArray<Parcelable> superState = new SparseArray<>();
+ super.saveHierarchyState(superState);
+ bundle.putSparseParcelableArray(SUPER_SAVED_STATE_KEY, superState);
+ sparseArray.put(0, bundle);
+ }
+
+ @Override
+ public void restoreHierarchyState(SparseArray<Parcelable> sparseArray) {
+ Bundle state = (Bundle) sparseArray.get(0);
+ mRecommendationsCurrentPage = state.getInt(
+ RECOMMENDATIONS_SAVED_STATE_KEY, /*defaultValue=*/0);
+ super.restoreHierarchyState(state.getSparseParcelableArray(SUPER_SAVED_STATE_KEY));
+ }
+
private static int getWidgetSheetId(BaseActivity activity) {
boolean isTwoPane = (activity.getDeviceProfile().isTablet
// Enables two pane picker for tablets in all orientations when the
@@ -797,8 +831,8 @@
}
/** private the height, in pixel, + the vertical margins of a given view. */
- private static int measureHeightWithVerticalMargins(View view) {
- if (view.getVisibility() != VISIBLE) {
+ protected static int measureHeightWithVerticalMargins(View view) {
+ if (view == null || view.getVisibility() != VISIBLE) {
return 0;
}
MarginLayoutParams marginLayoutParams = (MarginLayoutParams) view.getLayoutParams();
@@ -833,6 +867,7 @@
saveHierarchyState(widgetsState);
handleClose(false);
WidgetsFullSheet sheet = show(BaseActivity.fromContext(getContext()), false);
+ sheet.restoreRecommendations(mRecommendedWidgets, mRecommendedWidgetsMap);
sheet.restoreHierarchyState(widgetsState);
sheet.restorePreviousAdapterHolderType(getCurrentAdapterHolderType());
} else if (!isTwoPane()) {
@@ -843,6 +878,12 @@
mDeviceProfile = dp;
}
+ private void restoreRecommendations(List<WidgetItem> recommendedWidgets,
+ Map<WidgetRecommendationCategory, List<WidgetItem>> recommendedWidgetsMap) {
+ mRecommendedWidgets = recommendedWidgets;
+ mRecommendedWidgetsMap = recommendedWidgetsMap;
+ }
+
/**
* Indicates if layout should be re-created on device profile change - so that a different
* layout can be displayed.
@@ -863,6 +904,17 @@
}
@Override
+ @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+ public void onBackProgressed(@NonNull BackEvent backEvent) {
+ super.onBackProgressed(backEvent);
+ // In two pane picker, scroll bar is always hidden.
+ if (!isTwoPane()) {
+ mFastScroller.setVisibility(
+ backEvent.getProgress() > 0 ? View.INVISIBLE : View.VISIBLE);
+ }
+ }
+
+ @Override
public void onBackInvoked() {
if (mIsInSearchMode) {
mSearchBar.reset();
@@ -881,7 +933,8 @@
}
}
- @Nullable private View getViewToShowEducationTip() {
+ @Nullable
+ private View getViewToShowEducationTip() {
if (mWidgetRecommendationsContainer.getVisibility() == VISIBLE) {
return mWidgetRecommendationsView.getViewForEducationTip();
}
diff --git a/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinder.java b/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinder.java
index ef3ccf0..a7f7785 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinder.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinder.java
@@ -15,8 +15,6 @@
*/
package com.android.launcher3.widget.picker;
-import static com.android.launcher3.Flags.enableCategorizedWidgetSuggestions;
-
import android.content.Context;
import android.graphics.Bitmap;
import android.util.Log;
@@ -121,7 +119,7 @@
widget.setVisibility(View.VISIBLE);
// When preview loads, notify adapter to rebind the item and possibly animate
- widget.applyFromCellItem(widgetItem, 1f,
+ widget.applyFromCellItem(widgetItem,
bitmap -> holder.onPreviewLoaded(Pair.create(widgetItem, bitmap)),
holder.previewCache.get(widgetItem));
widget.requestLayout();
@@ -150,13 +148,7 @@
tableRow = (TableRow) table.getChildAt(i);
} else {
tableRow = new TableRow(table.getContext());
- if (enableCategorizedWidgetSuggestions()) {
- // Vertically center align items, so that even if they don't fill bounds, they
- // can look organized when placed together in a row.
- tableRow.setGravity(Gravity.CENTER_VERTICAL);
- } else {
- tableRow.setGravity(Gravity.TOP);
- }
+ tableRow.setGravity(Gravity.TOP);
table.addView(tableRow);
}
if (tableRow.getChildCount() > widgetItems.size()) {
diff --git a/src/com/android/launcher3/widget/picker/WidgetsRecommendationTableLayout.java b/src/com/android/launcher3/widget/picker/WidgetsRecommendationTableLayout.java
index 12564f4..7a2b4ef 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsRecommendationTableLayout.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsRecommendationTableLayout.java
@@ -17,11 +17,13 @@
import static com.android.launcher3.Flags.enableCategorizedWidgetSuggestions;
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_WIDGETS_PREDICTION;
+import static com.android.launcher3.widget.util.WidgetSizes.getWidgetSizePx;
+import static com.android.launcher3.widget.util.WidgetsTableUtils.WIDGETS_TABLE_ROW_SIZE_COMPARATOR;
+
+import static java.lang.Math.max;
import android.content.Context;
import android.util.AttributeSet;
-import android.util.Log;
-import android.util.Size;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
@@ -30,26 +32,23 @@
import android.widget.TableRow;
import androidx.annotation.Nullable;
+import androidx.annotation.Px;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.R;
import com.android.launcher3.model.WidgetItem;
import com.android.launcher3.widget.WidgetCell;
-import com.android.launcher3.widget.util.WidgetSizes;
+import com.android.launcher3.widget.picker.util.WidgetPreviewContainerSize;
import java.util.ArrayList;
import java.util.List;
/** A {@link TableLayout} for showing recommended widgets. */
public final class WidgetsRecommendationTableLayout extends TableLayout {
- private static final String TAG = "WidgetsRecommendationTableLayout";
- private static final float DOWN_SCALE_RATIO = 0.9f;
- private static final float MAX_DOWN_SCALE_RATIO = 0.5f;
private final float mWidgetsRecommendationTableVerticalPadding;
private final float mWidgetCellVerticalPadding;
private final float mWidgetCellTextViewsHeight;
- private float mRecommendationTableMaxHeight = Float.MAX_VALUE;
@Nullable private OnLongClickListener mWidgetCellOnLongClickListener;
@Nullable private OnClickListener mWidgetCellOnClickListener;
@@ -82,47 +81,38 @@
* desired {@code recommendationTableMaxHeight}.
*
* <p>If the content can't fit {@code recommendationTableMaxHeight}, this view will remove a
- * last row from the {@code recommendedWidgets} until it fits or only one row left. If the only
- * row still doesn't fit, we scale down the preview image.
+ * last row from the {@code recommendedWidgets} until it fits or only one row left.
*
* <p>Returns {@code false} if none of the widgets could fit</p>
*/
- public boolean setRecommendedWidgets(List<ArrayList<WidgetItem>> recommendedWidgets,
- DeviceProfile deviceProfile,
- float recommendationTableMaxHeight) {
- mRecommendationTableMaxHeight = recommendationTableMaxHeight;
- RecommendationTableData data = fitRecommendedWidgetsToTableSpace(/* previewScale= */ 1f,
- deviceProfile,
- recommendedWidgets);
- bindData(data);
- return !data.mRecommendationTable.isEmpty();
+ public int setRecommendedWidgets(List<ArrayList<WidgetItem>> recommendedWidgets,
+ DeviceProfile deviceProfile, float recommendationTableMaxHeight) {
+ List<ArrayList<WidgetItem>> rows = selectRowsThatFitInAvailableHeight(recommendedWidgets,
+ recommendationTableMaxHeight, deviceProfile);
+ bindData(rows);
+ return rows.stream().mapToInt(ArrayList::size).sum();
}
- private void bindData(RecommendationTableData data) {
- if (data.mRecommendationTable.isEmpty()) {
+ private void bindData(List<ArrayList<WidgetItem>> recommendationTable) {
+ if (recommendationTable.isEmpty()) {
setVisibility(GONE);
return;
}
removeAllViews();
- for (int i = 0; i < data.mRecommendationTable.size(); i++) {
- List<WidgetItem> widgetItems = data.mRecommendationTable.get(i);
+ for (int i = 0; i < recommendationTable.size(); i++) {
+ List<WidgetItem> widgetItems = recommendationTable.get(i);
TableRow tableRow = new TableRow(getContext());
- if (enableCategorizedWidgetSuggestions()) {
- // Vertically center align items, so that even if they don't fill bounds, they can
- // look organized when placed together in a row.
- tableRow.setGravity(Gravity.CENTER_VERTICAL);
- } else {
- tableRow.setGravity(Gravity.TOP);
- }
+ tableRow.setGravity(Gravity.TOP);
for (WidgetItem widgetItem : widgetItems) {
WidgetCell widgetCell = addItemCell(tableRow);
- widgetCell.applyFromCellItem(widgetItem, data.mPreviewScale);
+ widgetCell.applyFromCellItem(widgetItem);
widgetCell.showAppIconInWidgetTitle(true);
widgetCell.showBadge();
if (enableCategorizedWidgetSuggestions()) {
widgetCell.showDescription(false);
+ widgetCell.showDimensions(false);
}
}
addView(tableRow);
@@ -144,58 +134,32 @@
return widget;
}
- private RecommendationTableData fitRecommendedWidgetsToTableSpace(
- float previewScale,
- DeviceProfile deviceProfile,
- List<ArrayList<WidgetItem>> recommendedWidgetsInTable) {
- if (previewScale < MAX_DOWN_SCALE_RATIO) {
- Log.w(TAG, "Hide recommended widgets. Can't down scale previews to " + previewScale);
- return new RecommendationTableData(List.of(), previewScale);
- }
+ private List<ArrayList<WidgetItem>> selectRowsThatFitInAvailableHeight(
+ List<ArrayList<WidgetItem>> recommendedWidgets, @Px float recommendationTableMaxHeight,
+ DeviceProfile deviceProfile) {
+ List<ArrayList<WidgetItem>> filteredRows = new ArrayList<>();
// A naive estimation of the widgets recommendation table height without inflation.
float totalHeight = mWidgetsRecommendationTableVerticalPadding;
- for (int i = 0; i < recommendedWidgetsInTable.size(); i++) {
- List<WidgetItem> widgetItems = recommendedWidgetsInTable.get(i);
+
+ for (int i = 0; i < recommendedWidgets.size(); i++) {
+ List<WidgetItem> widgetItems = recommendedWidgets.get(i);
float rowHeight = 0;
for (int j = 0; j < widgetItems.size(); j++) {
WidgetItem widgetItem = widgetItems.get(j);
- Size widgetSize = WidgetSizes.getWidgetItemSizePx(getContext(), deviceProfile,
- widgetItem);
- float previewHeight = widgetSize.getHeight() * previewScale;
- rowHeight = Math.max(rowHeight,
- previewHeight + mWidgetCellTextViewsHeight + mWidgetCellVerticalPadding);
+ WidgetPreviewContainerSize previewContainerSize =
+ WidgetPreviewContainerSize.Companion.forItem(widgetItem, deviceProfile);
+ float widgetItemHeight = getWidgetSizePx(deviceProfile, previewContainerSize.spanX,
+ previewContainerSize.spanY).getHeight();
+ rowHeight = max(rowHeight,
+ widgetItemHeight + mWidgetCellTextViewsHeight + mWidgetCellVerticalPadding);
}
- totalHeight += rowHeight;
+ if (totalHeight + rowHeight <= recommendationTableMaxHeight) {
+ totalHeight += rowHeight;
+ filteredRows.add(new ArrayList<>(widgetItems));
+ }
}
- if (totalHeight < mRecommendationTableMaxHeight) {
- return new RecommendationTableData(recommendedWidgetsInTable, previewScale);
- }
-
- if (recommendedWidgetsInTable.size() > 1) {
- // We don't want to scale down widgets preview unless we really need to. Reduce the
- // num of row by 1 to see if it fits.
- return fitRecommendedWidgetsToTableSpace(
- previewScale,
- deviceProfile,
- recommendedWidgetsInTable.subList(/* fromIndex= */0,
- /* toIndex= */recommendedWidgetsInTable.size() - 1));
- }
-
- float nextPreviewScale = previewScale * DOWN_SCALE_RATIO;
- return fitRecommendedWidgetsToTableSpace(nextPreviewScale, deviceProfile,
- recommendedWidgetsInTable);
- }
-
- /** Data class for the widgets recommendation table and widgets preview scaling. */
- private class RecommendationTableData {
- private final List<ArrayList<WidgetItem>> mRecommendationTable;
- private final float mPreviewScale;
-
- RecommendationTableData(List<ArrayList<WidgetItem>> recommendationTable,
- float previewScale) {
- mRecommendationTable = recommendationTable;
- mPreviewScale = previewScale;
- }
+ // Perform re-ordering once we have filtered out recommendations that fit.
+ return filteredRows.stream().sorted(WIDGETS_TABLE_ROW_SIZE_COMPARATOR).toList();
}
}
diff --git a/src/com/android/launcher3/widget/picker/WidgetsTwoPaneSheet.java b/src/com/android/launcher3/widget/picker/WidgetsTwoPaneSheet.java
index 165b2fe..c60bca0 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsTwoPaneSheet.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsTwoPaneSheet.java
@@ -17,6 +17,10 @@
import static com.android.launcher3.Flags.enableCategorizedWidgetSuggestions;
import static com.android.launcher3.Flags.enableUnfoldedTwoPanePicker;
+import static com.android.launcher3.UtilitiesKt.CLIP_CHILDREN_FALSE_MODIFIER;
+import static com.android.launcher3.UtilitiesKt.CLIP_TO_PADDING_FALSE_MODIFIER;
+import static com.android.launcher3.UtilitiesKt.modifyAttributesOnViewTree;
+import static com.android.launcher3.UtilitiesKt.restoreAttributesOnViewTree;
import android.content.Context;
import android.graphics.Outline;
@@ -27,6 +31,7 @@
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewOutlineProvider;
+import android.view.ViewParent;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.ScrollView;
@@ -57,13 +62,19 @@
private static final int MAXIMUM_WIDTH_LEFT_PANE_FOLDABLE_DP = 395;
private static final String SUGGESTIONS_PACKAGE_NAME = "widgets_list_suggestions_entry";
+ // 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.75f;
private FrameLayout mSuggestedWidgetsContainer;
private WidgetsListHeader mSuggestedWidgetsHeader;
private PackageUserKey mSuggestedWidgetsPackageUserKey;
+ private View mPrimaryWidgetListView;
private LinearLayout mRightPane;
private ScrollView mRightPaneScrollView;
private WidgetsListTableViewHolderBinder mWidgetsListTableViewHolderBinder;
+
+ private boolean mOldIsBackSwipeProgressing;
private int mActivePage = -1;
private PackageUserKey mSelectedHeader;
@@ -118,14 +129,23 @@
mWidgetRecommendationsView.initParentViews(mWidgetRecommendationsContainer);
mWidgetRecommendationsView.setWidgetCellLongClickListener(this);
mWidgetRecommendationsView.setWidgetCellOnClickListener(this);
+ // To save the currently displayed page, so that, it can be requested when rebinding
+ // recommendations with different size constraints.
+ mWidgetRecommendationsView.addPageSwitchListener(
+ newPage -> mRecommendationsCurrentPage = newPage);
mHeaderTitle = mContent.findViewById(R.id.title);
mRightPane = mContent.findViewById(R.id.right_pane);
mRightPane.setOutlineProvider(mViewOutlineProviderRightPane);
mRightPaneScrollView = mContent.findViewById(R.id.right_pane_scroll_view);
mRightPaneScrollView.setOverScrollMode(View.OVER_SCROLL_NEVER);
+ mRightPaneScrollView.setOutlineProvider(mViewOutlineProvider);
+ mRightPaneScrollView.setClipToOutline(true);
- onRecommendedWidgetsBound();
+ mPrimaryWidgetListView = findViewById(R.id.primary_widgets_list_view);
+ mPrimaryWidgetListView.setOutlineProvider(mViewOutlineProvider);
+ mPrimaryWidgetListView.setClipToOutline(true);
+
onWidgetsBound();
setUpEducationViewsIfNeeded();
@@ -134,6 +154,27 @@
}
@Override
+ protected void onScaleProgressChanged() {
+ super.onScaleProgressChanged();
+ boolean isBackSwipeProgressing = mSlideInViewScale.value > 0;
+ if (isBackSwipeProgressing == mOldIsBackSwipeProgressing) {
+ return;
+ }
+ mOldIsBackSwipeProgressing = isBackSwipeProgressing;
+ if (isBackSwipeProgressing) {
+ modifyAttributesOnViewTree(mPrimaryWidgetListView, (ViewParent) mContent,
+ CLIP_CHILDREN_FALSE_MODIFIER);
+ modifyAttributesOnViewTree(mRightPaneScrollView, (ViewParent) mContent,
+ CLIP_CHILDREN_FALSE_MODIFIER, CLIP_TO_PADDING_FALSE_MODIFIER);
+ } else {
+ restoreAttributesOnViewTree(mPrimaryWidgetListView, mContent,
+ CLIP_CHILDREN_FALSE_MODIFIER);
+ restoreAttributesOnViewTree(mRightPaneScrollView, mContent,
+ CLIP_CHILDREN_FALSE_MODIFIER, CLIP_TO_PADDING_FALSE_MODIFIER);
+ }
+ }
+
+ @Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
if (changed && mDeviceProfile.isTwoPanels && enableUnfoldedTwoPanePicker()) {
@@ -167,7 +208,7 @@
@Override
public void onWidgetsBound() {
super.onWidgetsBound();
- if (!mHasRecommendedWidgets && mSelectedHeader == null) {
+ if (mRecommendedWidgetsCount == 0 && mSelectedHeader == null) {
mAdapters.get(mActivePage).mWidgetsListAdapter.selectFirstHeaderEntry();
mAdapters.get(mActivePage).mWidgetsRecyclerView.scrollToTop();
}
@@ -177,7 +218,7 @@
public void onRecommendedWidgetsBound() {
super.onRecommendedWidgetsBound();
- if (mSuggestedWidgetsContainer == null && mHasRecommendedWidgets) {
+ if (mSuggestedWidgetsContainer == null && mRecommendedWidgetsCount > 0) {
setupSuggestedWidgets(LayoutInflater.from(getContext()));
mSuggestedWidgetsHeader.callOnClick();
}
@@ -207,10 +248,13 @@
String suggestionsRightPaneTitle = getContext().getString(
R.string.widget_picker_right_pane_accessibility_title, suggestionsHeaderTitle);
packageItemInfo.title = suggestionsHeaderTitle;
+ // Suggestions may update at run time. The widgets count on suggestions doesn't add any
+ // value, so, we don't show the count.
WidgetsListHeaderEntry widgetsListHeaderEntry = WidgetsListHeaderEntry.create(
packageItemInfo,
- suggestionsHeaderTitle,
- mActivityContext.getPopupDataProvider().getRecommendedWidgets())
+ /*titleSectionName=*/ suggestionsHeaderTitle,
+ /*items=*/ mActivityContext.getPopupDataProvider().getRecommendedWidgets(),
+ /*visibleWidgetsCount=*/ 0)
.withWidgetListShown();
mSuggestedWidgetsHeader.applyFromItemInfoWithIcon(widgetsListHeaderEntry);
@@ -232,8 +276,8 @@
@Override
@Px
- protected float getMaxTableHeight(@Px float noWidgetsViewHeight) {
- return Float.MAX_VALUE;
+ protected float getRecommendationSectionHeightRatio() {
+ return RECOMMENDATION_SECTION_HEIGHT_RATIO_TWO_PANE;
}
@Override
diff --git a/src/com/android/launcher3/widget/picker/util/WidgetPreviewContainerSize.kt b/src/com/android/launcher3/widget/picker/util/WidgetPreviewContainerSize.kt
new file mode 100644
index 0000000..a0414ba
--- /dev/null
+++ b/src/com/android/launcher3/widget/picker/util/WidgetPreviewContainerSize.kt
@@ -0,0 +1,91 @@
+/*
+ * 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.util
+
+import com.android.launcher3.DeviceProfile
+import com.android.launcher3.model.WidgetItem
+import kotlin.math.abs
+
+/** Size of a preview container in terms of the grid spans. */
+data class WidgetPreviewContainerSize(@JvmField val spanX: Int, @JvmField val spanY: Int) {
+ companion object {
+ /**
+ * Returns the size of the preview container in which the given widget's preview should be
+ * displayed (by scaling it if necessary).
+ */
+ fun forItem(item: WidgetItem, dp: DeviceProfile): WidgetPreviewContainerSize {
+ val sizes =
+ if (dp.isTablet && !dp.isTwoPanels) {
+ TABLET_WIDGET_PREVIEW_SIZES
+ } else {
+ HANDHELD_WIDGET_PREVIEW_SIZES
+ }
+
+ for ((index, containerSize) in sizes.withIndex()) {
+ if (containerSize.spanX == item.spanX && containerSize.spanY == item.spanY) {
+ return containerSize // Exact match!
+ }
+ if (containerSize.spanX <= item.spanX && containerSize.spanY <= item.spanY) {
+ return findClosestFittingContainer(
+ containerSizes = sizes.toList(),
+ startIndex = index,
+ item = item
+ )
+ }
+ }
+ // Use largest container if no match found
+ return sizes.elementAt(0)
+ }
+
+ private fun findClosestFittingContainer(
+ containerSizes: List<WidgetPreviewContainerSize>,
+ startIndex: Int,
+ item: WidgetItem
+ ): WidgetPreviewContainerSize {
+ // Checks if it's a smaller container, but close enough to keep the down-scale minimal.
+ fun hasAcceptableSize(currentIndex: Int): Boolean {
+ val container = containerSizes[currentIndex]
+ val isSmallerThanItem =
+ container.spanX <= item.spanX && container.spanY <= item.spanY
+ val isCloseToItemSize =
+ (item.spanY - container.spanY <= 1) && (item.spanX - container.spanX <= 1)
+
+ return isSmallerThanItem && isCloseToItemSize
+ }
+
+ var currentIndex = startIndex
+ var match = containerSizes[currentIndex]
+ val itemCellSizeRatio = item.spanX.toFloat() / item.spanY
+ var lastCellSizeRatioDiff = Float.MAX_VALUE
+
+ // Look for a smaller container (up to an acceptable extent) with closest cell size
+ // ratio.
+ while (currentIndex <= containerSizes.lastIndex && hasAcceptableSize(currentIndex)) {
+ val current = containerSizes[currentIndex]
+ val currentCellSizeRatio = current.spanX.toFloat() / current.spanY
+ val currentCellSizeRatioDiff = abs(itemCellSizeRatio - currentCellSizeRatio)
+
+ if (currentCellSizeRatioDiff < lastCellSizeRatioDiff) {
+ lastCellSizeRatioDiff = currentCellSizeRatioDiff
+ match = containerSizes[currentIndex]
+ }
+ currentIndex++
+ }
+ return match
+ }
+ }
+}
diff --git a/src/com/android/launcher3/widget/picker/util/WidgetPreviewContainerSizes.kt b/src/com/android/launcher3/widget/picker/util/WidgetPreviewContainerSizes.kt
new file mode 100644
index 0000000..a016676
--- /dev/null
+++ b/src/com/android/launcher3/widget/picker/util/WidgetPreviewContainerSizes.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.launcher3.widget.picker.util
+
+/**
+ * An ordered list of recommended sizes for the preview containers in handheld devices.
+ *
+ * Size of the preview container in which a widget's preview can be displayed.
+ */
+val HANDHELD_WIDGET_PREVIEW_SIZES: List<WidgetPreviewContainerSize> =
+ listOf(
+ WidgetPreviewContainerSize(spanX = 4, spanY = 3),
+ WidgetPreviewContainerSize(spanX = 4, spanY = 2),
+ WidgetPreviewContainerSize(spanX = 2, spanY = 3),
+ WidgetPreviewContainerSize(spanX = 2, spanY = 2),
+ WidgetPreviewContainerSize(spanX = 4, spanY = 1),
+ WidgetPreviewContainerSize(spanX = 2, spanY = 1),
+ WidgetPreviewContainerSize(spanX = 1, spanY = 1),
+ )
+
+/**
+ * An ordered list of recommended sizes for the preview containers in tablet devices (with larger
+ * grids).
+ *
+ * Size of the preview container in which a widget's preview can be displayed (by scaling the
+ * preview if necessary).
+ */
+val TABLET_WIDGET_PREVIEW_SIZES: List<WidgetPreviewContainerSize> =
+ listOf(
+ WidgetPreviewContainerSize(spanX = 3, spanY = 4),
+ WidgetPreviewContainerSize(spanX = 3, spanY = 3),
+ WidgetPreviewContainerSize(spanX = 3, spanY = 2),
+ WidgetPreviewContainerSize(spanX = 2, spanY = 3),
+ WidgetPreviewContainerSize(spanX = 2, spanY = 2),
+ WidgetPreviewContainerSize(spanX = 3, spanY = 1),
+ WidgetPreviewContainerSize(spanX = 2, spanY = 1),
+ WidgetPreviewContainerSize(spanX = 1, spanY = 1),
+ )
diff --git a/src/com/android/launcher3/widget/util/WidgetsTableUtils.java b/src/com/android/launcher3/widget/util/WidgetsTableUtils.java
index 74d3062..5e0e203 100644
--- a/src/com/android/launcher3/widget/util/WidgetsTableUtils.java
+++ b/src/com/android/launcher3/widget/util/WidgetsTableUtils.java
@@ -16,11 +16,13 @@
package com.android.launcher3.widget.util;
import android.content.Context;
+import android.util.Size;
import androidx.annotation.Px;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.model.WidgetItem;
+import com.android.launcher3.widget.picker.util.WidgetPreviewContainerSize;
import java.util.ArrayList;
import java.util.Comparator;
@@ -33,8 +35,8 @@
/**
* Groups widgets in the following order:
* 1. Widgets always go before shortcuts.
- * 2. Widgets with smaller horizontal spans will be shown first.
- * 3. If widgets have the same horizontal spans, then widgets with a smaller vertical spans will
+ * 2. Widgets with smaller vertical spans will be shown first.
+ * 3. If widgets have the same vertical spans, then widgets with a smaller horizontal spans will
* go first.
* 4. If both widgets have the same horizontal and vertical spans, they will use the same order
* from the given {@code widgetItems}.
@@ -43,14 +45,29 @@
if (item.widgetInfo != null && otherItem.widgetInfo == null) return -1;
if (item.widgetInfo == null && otherItem.widgetInfo != null) return 1;
- if (item.spanX == otherItem.spanX) {
- if (item.spanY == otherItem.spanY) return 0;
- return item.spanY > otherItem.spanY ? 1 : -1;
+ if (item.spanY == otherItem.spanY) {
+ if (item.spanX == otherItem.spanX) return 0;
+ return item.spanX > otherItem.spanX ? 1 : -1;
}
- return item.spanX > otherItem.spanX ? 1 : -1;
+ return item.spanY > otherItem.spanY ? 1 : -1;
};
/**
+ * Comparator that enables displaying rows in increasing order of their size (totalW * H);
+ * except for shortcuts which always show at the bottom.
+ */
+ public static final Comparator<ArrayList<WidgetItem>> WIDGETS_TABLE_ROW_SIZE_COMPARATOR =
+ Comparator.comparingInt(row -> {
+ if (row.stream().anyMatch(WidgetItem::isShortcut)) {
+ return Integer.MAX_VALUE;
+ } else {
+ int rowWidth = row.stream().mapToInt(w -> w.spanX).sum();
+ int rowHeight = row.get(0).spanY;
+ return (rowWidth * rowHeight);
+ }
+ });
+
+ /**
* Groups {@code widgetItems} items into a 2D array which matches their appearance in a UI
* table. This takes liberty to rearrange widgets to make the table visually appealing.
*/
@@ -59,72 +76,70 @@
final @Px int rowPx, final @Px int cellPadding) {
List<WidgetItem> sortedWidgetItems = widgetItems.stream().sorted(WIDGET_SHORTCUT_COMPARATOR)
.collect(Collectors.toList());
- return groupWidgetItemsUsingRowPxWithoutReordering(sortedWidgetItems, context, dp, rowPx,
+ List<ArrayList<WidgetItem>> rows = groupWidgetItemsUsingRowPxWithoutReordering(
+ sortedWidgetItems, context, dp, rowPx,
cellPadding);
+ return rows.stream().sorted(WIDGETS_TABLE_ROW_SIZE_COMPARATOR).toList();
}
/**
* Groups {@code widgetItems} into a 2D array which matches their appearance in a UI table while
* maintaining their order. This function is a variant of
- * {@code groupWidgetItemsIntoTableWithoutReordering} in that this uses widget pixels for
- * calculation.
+ * {@code groupWidgetItemsIntoTableWithoutReordering} in that this uses widget container's
+ * pixels for calculation.
*
* <p>Grouping:
* 1. Widgets and shortcuts never group together in the same row.
- * 2. The ordered widgets are grouped together in the same row until their individual occupying
- * pixels exceed the total allowed pixels for the cell.
+ * 2. Widgets are grouped together only if they have same preview container size.
+ * 3. Widgets are grouped together in the same row until the total of individual container sizes
+ * exceed the total allowed pixels for the row.
* 3. The ordered shortcuts are grouped together in the same row until their individual
* occupying pixels exceed the total allowed pixels for the cell.
* 4. If there is only one widget in a row, its width may exceed the {@code rowPx}.
*
- * <p>Let's say the {@code rowPx} is set to 600 and we have 5 widgets. Widgets can be grouped
- * in the same row if each of their individual occupying pixels does not exceed
- * {@code rowPx} / 5 - 2 * {@code cellPadding}.
- * Example 1: Row 1: 200x200, 200x300, 100x100. Average horizontal pixels is 200 and no widgets
- * exceed that width. This is okay.
- * Example 2: Row 1: 200x200, 400x300, 100x100. Average horizontal pixels is 200 and one widget
- * exceed that width. This is not allowed.
- * Example 3: Row 1: 700x400. This is okay because this is the only item in the row.
+ * <p>See WidgetTableUtilsTest
*/
public static List<ArrayList<WidgetItem>> groupWidgetItemsUsingRowPxWithoutReordering(
List<WidgetItem> widgetItems, Context context, final DeviceProfile dp,
final @Px int rowPx, final @Px int cellPadding) {
-
List<ArrayList<WidgetItem>> widgetItemsTable = new ArrayList<>();
ArrayList<WidgetItem> widgetItemsAtRow = null;
+ // A row displays only items of same container size.
+ WidgetPreviewContainerSize containerSizeForRow = null;
+ @Px int currentRowWidth = 0;
+
for (WidgetItem widgetItem : widgetItems) {
if (widgetItemsAtRow == null) {
widgetItemsAtRow = new ArrayList<>();
widgetItemsTable.add(widgetItemsAtRow);
}
int numOfWidgetItems = widgetItemsAtRow.size();
- @Px int individualSpan = (rowPx / (numOfWidgetItems + 1)) - (2 * cellPadding);
+
+ WidgetPreviewContainerSize containerSize =
+ WidgetPreviewContainerSize.Companion.forItem(widgetItem, dp);
+ Size containerSizePx = WidgetSizes.getWidgetSizePx(dp, containerSize.spanX,
+ containerSize.spanY);
+ @Px int containerWidth = containerSizePx.getWidth() + (2 * cellPadding);
+
if (numOfWidgetItems == 0) {
widgetItemsAtRow.add(widgetItem);
- } else if (
- // Since the size of the widget cell is determined by dividing the maximum span
- // pixels evenly, making sure that each widget would have enough span pixels to
- // show their contents.
- widgetItem.hasSameType(widgetItemsAtRow.get(numOfWidgetItems - 1))
- && widgetItemsAtRow.stream().allMatch(
- item -> WidgetSizes.getWidgetItemSizePx(context, dp, item)
- .getWidth() <= individualSpan)
- && WidgetSizes.getWidgetItemSizePx(context, dp, widgetItem)
- .getWidth() <= individualSpan) {
+ containerSizeForRow = containerSize;
+ currentRowWidth = containerWidth;
+ } else if ((currentRowWidth + containerWidth) <= rowPx
+ && widgetItem.hasSameType(widgetItemsAtRow.get(numOfWidgetItems - 1))
+ && containerSize.equals(containerSizeForRow)) {
// Group items in the same row if
// 1. they are with the same type, i.e. a row can only have widgets or shortcuts but
// never a mix of both.
- // 2. Each widget will have horizontal cell span pixels that is at least as large as
- // it is required to fit in the horizontal content, unless the widget horizontal
- // span pixels is larger than the maximum allowed.
- // If an item has horizontal span pixels larger than the maximum allowed pixels
- // per row, we just place it in its own row regardless of the horizontal span
- // limit.
+ // 2. Each widget in the given row has same preview container size.
widgetItemsAtRow.add(widgetItem);
+ currentRowWidth += containerWidth;
} else {
widgetItemsAtRow = new ArrayList<>();
widgetItemsTable.add(widgetItemsAtRow);
widgetItemsAtRow.add(widgetItem);
+ containerSizeForRow = containerSize;
+ currentRowWidth = containerWidth;
}
}
return widgetItemsTable;
diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/ApiWrapper.java b/src_ui_overrides/com/android/launcher3/uioverrides/ApiWrapper.java
index efde7d8..90271c1 100644
--- a/src_ui_overrides/com/android/launcher3/uioverrides/ApiWrapper.java
+++ b/src_ui_overrides/com/android/launcher3/uioverrides/ApiWrapper.java
@@ -108,6 +108,13 @@
}
/**
+ * Returns an intent which can be used to open Private Space Settings.
+ */
+ public static Intent getPrivateSpaceSettingsIntent(Context context) {
+ return null;
+ }
+
+ /**
* Checks if an activity is flagged as non-resizeable.
*/
public static boolean isNonResizeableActivity(LauncherActivityInfo lai) {
diff --git a/tests/Android.bp b/tests/Android.bp
index e9111ea..eeafdba 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -23,37 +23,20 @@
srcs: [
"src/**/*.java",
"src/**/*.kt",
- "multivalentTests/src/**/*.java",
- "multivalentTests/src/**/*.kt",
+ ":launcher3-robo-src",
],
exclude_srcs: [
":launcher-non-quickstep-tests-src",
],
}
-// Source code used for screenshot tests
filegroup {
- name: "launcher-image-tests-helpers",
+ name: "launcher3-robo-src",
+ // multivalentTests directory is a shared folder for not only robolectric converted test
+ // classes but also shared helper classes.
srcs: [
- "src/com/android/launcher3/celllayout/board/*.java",
- "src/com/android/launcher3/celllayout/board/*.kt",
- "src/com/android/launcher3/celllayout/FavoriteItemsTransaction.java",
- "src/com/android/launcher3/ui/AbstractLauncherUiTest.java",
- "src/com/android/launcher3/ui/PortraitLandscapeRunner.java",
- "src/com/android/launcher3/ui/TestViewHelpers.java",
- "multivalentTests/src/com/android/launcher3/util/LauncherLayoutBuilder.java",
- "src/com/android/launcher3/util/ModelTestExtensions.kt",
- "src/com/android/launcher3/util/TestConstants.java",
- "multivalentTests/src/com/android/launcher3/util/TestUtil.java",
- "src/com/android/launcher3/util/Wait.java",
- "multivalentTests/src/com/android/launcher3/util/WidgetUtils.java",
- "src/com/android/launcher3/util/rule/*.java",
- "src/com/android/launcher3/util/rule/*.kt",
- "multivalentTests/src/com/android/launcher3/util/rule/*.java",
- "multivalentTests/src/com/android/launcher3/util/rule/*.kt",
- "src/com/android/launcher3/util/viewcapture_analysis/*.java",
- "src/com/android/launcher3/testcomponent/*.java",
- "src/com/android/launcher3/testcomponent/*.kt",
+ "multivalentTests/src/**/*.java",
+ "multivalentTests/src/**/*.kt",
],
}
@@ -70,35 +53,15 @@
filegroup {
name: "launcher-oop-tests-src",
srcs: [
+ ":launcher-testing-helpers",
"src/com/android/launcher3/allapps/TaplOpenCloseAllAppsTest.java",
"src/com/android/launcher3/allapps/TaplAllAppsIconsWorkingTest.java",
"src/com/android/launcher3/appiconmenu/TaplAppIconMenuTest.java",
"src/com/android/launcher3/dragging/TaplDragTest.java",
"src/com/android/launcher3/dragging/TaplUninstallRemoveTest.java",
- "src/com/android/launcher3/ui/AbstractLauncherUiTest.java",
- "src/com/android/launcher3/ui/PortraitLandscapeRunner.java",
"src/com/android/launcher3/ui/TaplTestsLauncher3Test.java",
"src/com/android/launcher3/ui/widget/TaplWidgetPickerTest.java",
"src/com/android/launcher3/ui/workspace/TaplWorkspaceTest.java",
- "multivalentTests/src/com/android/launcher3/util/LauncherLayoutBuilder.java",
- "src/com/android/launcher3/util/TestConstants.java",
- "multivalentTests/src/com/android/launcher3/util/TestUtil.java",
- "src/com/android/launcher3/util/Wait.java",
- "multivalentTests/src/com/android/launcher3/util/WidgetUtils.java",
- "src/com/android/launcher3/util/rule/FailureWatcher.java",
- "src/com/android/launcher3/util/rule/ViewCaptureRule.kt",
- "src/com/android/launcher3/util/rule/SamplerRule.java",
- "src/com/android/launcher3/util/rule/ScreenRecordRule.java",
- "src/com/android/launcher3/util/rule/ShellCommandRule.java",
- "src/com/android/launcher3/util/rule/TestIsolationRule.java",
- "multivalentTests/src/com/android/launcher3/util/rule/TestStabilityRule.java",
- "src/com/android/launcher3/util/viewcapture_analysis/*.java",
- "src/com/android/launcher3/testcomponent/BaseTestingActivity.java",
- "src/com/android/launcher3/testcomponent/OtherBaseTestingActivity.java",
- "src/com/android/launcher3/testcomponent/CustomShortcutConfigActivity.java",
- "src/com/android/launcher3/testcomponent/TestCommandReceiver.java",
- "src/com/android/launcher3/testcomponent/TestLauncherActivity.java",
- "src/com/android/launcher3/testcomponent/ImeTestActivity.java",
],
}
@@ -177,7 +140,7 @@
name: "launcher-testing-shared",
srcs: [
"multivalentTests/shared/com/android/launcher3/testing/shared/**/*.java",
- "multivalentTests/shared/com/android/launcher3/testing/shared/**/*.kt"
+ "multivalentTests/shared/com/android/launcher3/testing/shared/**/*.kt",
],
resource_dirs: [],
manifest: "multivalentTests/shared/AndroidManifest.xml",
@@ -222,11 +185,8 @@
android_robolectric_test {
enabled: true,
name: "Launcher3RoboTests",
- // multivalentTests directory is a shared folder for not only robolectric converted test
- // classes but also shared helper classes.
srcs: [
- "multivalentTests/src/com/android/launcher3/util/*.java",
- "multivalentTests/src/com/android/launcher3/util/*.kt",
+ ":launcher3-robo-src",
// Test util classes
":launcher-testing-helpers",
@@ -246,7 +206,9 @@
"androidx.test.uiautomator_uiautomator",
"androidx.core_core-animation-testing",
"androidx.test.ext.junit",
- "inline-mockito-robolectric-prebuilt",
+ "androidx.test.rules",
+ "mockito-robolectric-prebuilt",
+ "mockito-kotlin2",
"platform-parametric-runner-lib",
"testables",
"Launcher3TestResources",
diff --git a/tests/AndroidManifest-common.xml b/tests/AndroidManifest-common.xml
index 7cb7964..27dd2a9 100644
--- a/tests/AndroidManifest-common.xml
+++ b/tests/AndroidManifest-common.xml
@@ -388,6 +388,15 @@
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity-alias>
+ <activity android:name="com.android.launcher3.testcomponent.ExcludeFromRecentsTestActivity"
+ android:label="ExcludeFromRecentsTestActivity"
+ android:exported="true"
+ android:excludeFromRecents="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.intent.category.LAUNCHER"/>
+ </intent-filter>
+ </activity>
<!-- [b/197780098] Disable eager initialization of Jetpack libraries. -->
<provider
diff --git a/tests/Launcher3Tests.xml b/tests/Launcher3Tests.xml
index bcbe343..29c34be 100644
--- a/tests/Launcher3Tests.xml
+++ b/tests/Launcher3Tests.xml
@@ -13,14 +13,27 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<!-- This test config file is auto-generated. -->
+
<configuration description="Runs Launcher3 tests.">
<option name="test-suite-tag" value="apct" />
<option name="test-suite-tag" value="apct-instrumentation" />
+ <option name="max-tmp-logcat-file" value="104857600" /> <!-- 100 * 1024 * 1024 -->
+
+ <logger class="com.android.tradefed.log.FileLogger">
+ <option name="max-log-size" value="20" />
+ </logger>
+
+ <!-- Disables the "Ramdump uploader to betterbug" -->
+ <option name="post-boot-command" value="am broadcast --async --user 0 -a com.google.gservices.intent.action.GSERVICES_OVERRIDE -e betterbug_enable_ramdump_uploader false" />
+
<target_preparer class="com.android.tradefed.targetprep.DeviceSetup">
<option name="set-test-harness" value="true" />
- <option name="run-command" value="am force-stop com.android.launcher3" />
+
+ <option name="run-command" value="svc nfc disable" />
+ <option name="run-command" value="settings put global ble_scan_always_enabled 0" />
+ <option name="run-command" value="svc bluetooth disable" />
+
<option name="run-command" value="pm uninstall com.google.android.apps.nexuslauncher" />
<option name="run-command" value="pm uninstall com.google.android.apps.nexuslauncher.out_of_proc_tests" />
<option name="run-command" value="pm uninstall com.google.android.apps.nexuslauncher.tests" />
@@ -30,6 +43,9 @@
<option name="run-command" value="settings delete secure assistant" />
<option name="run-command" value="settings put global airplane_mode_on 1" />
<option name="run-command" value="am broadcast -a android.intent.action.AIRPLANE_MODE" />
+
+ <option name="run-command" value="settings put system pointer_location 1" />
+ <option name="run-command" value="settings put system show_touches 1" />
</target_preparer>
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
diff --git a/tests/assets/databases/BackupAndRestore/launcher.db b/tests/assets/databases/BackupAndRestore/launcher.db
new file mode 100644
index 0000000..126d166
--- /dev/null
+++ b/tests/assets/databases/BackupAndRestore/launcher.db
Binary files differ
diff --git a/tests/assets/databases/BackupAndRestore/launcher_3_by_3.db b/tests/assets/databases/BackupAndRestore/launcher_3_by_3.db
new file mode 100644
index 0000000..6d8cd73
--- /dev/null
+++ b/tests/assets/databases/BackupAndRestore/launcher_3_by_3.db
Binary files differ
diff --git a/tests/assets/databases/BackupAndRestore/launcher_4_by_4.db b/tests/assets/databases/BackupAndRestore/launcher_4_by_4.db
new file mode 100644
index 0000000..00061dd
--- /dev/null
+++ b/tests/assets/databases/BackupAndRestore/launcher_4_by_4.db
Binary files differ
diff --git a/tests/assets/databases/BackupAndRestore/launcher_4_by_5.db b/tests/assets/databases/BackupAndRestore/launcher_4_by_5.db
new file mode 100644
index 0000000..e2e65aa
--- /dev/null
+++ b/tests/assets/databases/BackupAndRestore/launcher_4_by_5.db
Binary files differ
diff --git a/tests/assets/databases/GridMigrationTest/flagged_result5x5to5x8.db b/tests/assets/databases/GridMigrationTest/flagged_result5x5to5x8.db
new file mode 100644
index 0000000..8bea3ce
--- /dev/null
+++ b/tests/assets/databases/GridMigrationTest/flagged_result5x5to5x8.db
Binary files differ
diff --git a/tests/assets/databases/GridMigrationTest/result5x5to3x3.db b/tests/assets/databases/GridMigrationTest/result5x5to3x3.db
new file mode 100644
index 0000000..686056d
--- /dev/null
+++ b/tests/assets/databases/GridMigrationTest/result5x5to3x3.db
Binary files differ
diff --git a/tests/assets/databases/GridMigrationTest/result5x5to4x7.db b/tests/assets/databases/GridMigrationTest/result5x5to4x7.db
new file mode 100644
index 0000000..cd105c5
--- /dev/null
+++ b/tests/assets/databases/GridMigrationTest/result5x5to4x7.db
Binary files differ
diff --git a/tests/assets/databases/GridMigrationTest/result5x5to5x8.db b/tests/assets/databases/GridMigrationTest/result5x5to5x8.db
new file mode 100644
index 0000000..4b46969
--- /dev/null
+++ b/tests/assets/databases/GridMigrationTest/result5x5to5x8.db
Binary files differ
diff --git a/tests/assets/databases/GridMigrationTest/test_launcher.db b/tests/assets/databases/GridMigrationTest/test_launcher.db
new file mode 100644
index 0000000..c680e95
--- /dev/null
+++ b/tests/assets/databases/GridMigrationTest/test_launcher.db
Binary files differ
diff --git a/tests/assets/dumpTests/DeviceProfileDumpTest/phonePortrait.txt b/tests/assets/dumpTests/DeviceProfileDumpTest/phonePortrait.txt
index af8f67f..197e687 100644
--- a/tests/assets/dumpTests/DeviceProfileDumpTest/phonePortrait.txt
+++ b/tests/assets/dumpTests/DeviceProfileDumpTest/phonePortrait.txt
@@ -123,7 +123,7 @@
dropTargetBarBottomMarginPx: 42.0px (16.0dp)
getCellLayoutSpringLoadShrunkTop(): 391.0px (148.95238dp)
getCellLayoutSpringLoadShrunkBottom(): 1927.0px (734.0952dp)
- workspaceSpringLoadedMinNextPageVisiblePx: 63.0px (24.0dp)
- getWorkspaceSpringLoadScale(): 0.7781155px (0.29642496dp)
+ workspaceSpringLoadedMinNextPageVisiblePx: 126.0px (48.0dp)
+ getWorkspaceSpringLoadScale(): 0.7666667px (0.2920635dp)
getCellLayoutHeight(): 1974.0px (752.0dp)
getCellLayoutWidth(): 1080.0px (411.42856dp)
diff --git a/tests/assets/dumpTests/DeviceProfileDumpTest/phonePortrait3Button.txt b/tests/assets/dumpTests/DeviceProfileDumpTest/phonePortrait3Button.txt
index 5b83dd7..4a9e2e6 100644
--- a/tests/assets/dumpTests/DeviceProfileDumpTest/phonePortrait3Button.txt
+++ b/tests/assets/dumpTests/DeviceProfileDumpTest/phonePortrait3Button.txt
@@ -123,7 +123,7 @@
dropTargetBarBottomMarginPx: 42.0px (16.0dp)
getCellLayoutSpringLoadShrunkTop(): 391.0px (148.95238dp)
getCellLayoutSpringLoadShrunkBottom(): 1906.0px (726.0952dp)
- workspaceSpringLoadedMinNextPageVisiblePx: 63.0px (24.0dp)
- getWorkspaceSpringLoadScale(): 0.77572966px (0.29551607dp)
+ workspaceSpringLoadedMinNextPageVisiblePx: 126.0px (48.0dp)
+ getWorkspaceSpringLoadScale(): 0.76666665px (0.29206347dp)
getCellLayoutHeight(): 1953.0px (744.0dp)
getCellLayoutWidth(): 1080.0px (411.42856dp)
diff --git a/tests/assets/dumpTests/DeviceProfileDumpTest/phoneVerticalBar.txt b/tests/assets/dumpTests/DeviceProfileDumpTest/phoneVerticalBar.txt
index 03a0048..9e89a16 100644
--- a/tests/assets/dumpTests/DeviceProfileDumpTest/phoneVerticalBar.txt
+++ b/tests/assets/dumpTests/DeviceProfileDumpTest/phoneVerticalBar.txt
@@ -123,7 +123,7 @@
dropTargetBarBottomMarginPx: 16.0px (6.095238dp)
getCellLayoutSpringLoadShrunkTop(): 201.0px (76.57143dp)
getCellLayoutSpringLoadShrunkBottom(): 961.0px (366.09525dp)
- workspaceSpringLoadedMinNextPageVisiblePx: 63.0px (24.0dp)
+ workspaceSpringLoadedMinNextPageVisiblePx: 126.0px (48.0dp)
getWorkspaceSpringLoadScale(): 0.8059385px (0.30702418dp)
getCellLayoutHeight(): 943.0px (359.2381dp)
getCellLayoutWidth(): 2073.0px (789.7143dp)
diff --git a/tests/assets/dumpTests/DeviceProfileDumpTest/phoneVerticalBar3Button.txt b/tests/assets/dumpTests/DeviceProfileDumpTest/phoneVerticalBar3Button.txt
index 84258b3..ce168b4 100644
--- a/tests/assets/dumpTests/DeviceProfileDumpTest/phoneVerticalBar3Button.txt
+++ b/tests/assets/dumpTests/DeviceProfileDumpTest/phoneVerticalBar3Button.txt
@@ -123,7 +123,7 @@
dropTargetBarBottomMarginPx: 16.0px (6.095238dp)
getCellLayoutSpringLoadShrunkTop(): 201.0px (76.57143dp)
getCellLayoutSpringLoadShrunkBottom(): 1017.0px (387.42856dp)
- workspaceSpringLoadedMinNextPageVisiblePx: 63.0px (24.0dp)
+ workspaceSpringLoadedMinNextPageVisiblePx: 126.0px (48.0dp)
getWorkspaceSpringLoadScale(): 0.8111332px (0.3090031dp)
getCellLayoutHeight(): 1006.0px (383.2381dp)
getCellLayoutWidth(): 1947.0px (741.7143dp)
diff --git a/tests/assets/dumpTests/DeviceProfileDumpTest/tabletLandscape.txt b/tests/assets/dumpTests/DeviceProfileDumpTest/tabletLandscape.txt
index 87a9700..7926033 100644
--- a/tests/assets/dumpTests/DeviceProfileDumpTest/tabletLandscape.txt
+++ b/tests/assets/dumpTests/DeviceProfileDumpTest/tabletLandscape.txt
@@ -123,7 +123,7 @@
dropTargetBarBottomMarginPx: 64.0px (32.0dp)
getCellLayoutSpringLoadShrunkTop(): 312.0px (156.0dp)
getCellLayoutSpringLoadShrunkBottom(): 1272.0px (636.0dp)
- workspaceSpringLoadedMinNextPageVisiblePx: 48.0px (24.0dp)
+ workspaceSpringLoadedMinNextPageVisiblePx: 96.0px (48.0dp)
getWorkspaceSpringLoadScale(): 0.76677316px (0.38338658dp)
getCellLayoutHeight(): 1252.0px (626.0dp)
getCellLayoutWidth(): 2198.0px (1099.0dp)
diff --git a/tests/assets/dumpTests/DeviceProfileDumpTest/tabletLandscape3Button.txt b/tests/assets/dumpTests/DeviceProfileDumpTest/tabletLandscape3Button.txt
index 169256d..eb20578 100644
--- a/tests/assets/dumpTests/DeviceProfileDumpTest/tabletLandscape3Button.txt
+++ b/tests/assets/dumpTests/DeviceProfileDumpTest/tabletLandscape3Button.txt
@@ -123,7 +123,7 @@
dropTargetBarBottomMarginPx: 64.0px (32.0dp)
getCellLayoutSpringLoadShrunkTop(): 312.0px (156.0dp)
getCellLayoutSpringLoadShrunkBottom(): 1272.0px (636.0dp)
- workspaceSpringLoadedMinNextPageVisiblePx: 48.0px (24.0dp)
+ workspaceSpringLoadedMinNextPageVisiblePx: 96.0px (48.0dp)
getWorkspaceSpringLoadScale(): 0.76677316px (0.38338658dp)
getCellLayoutHeight(): 1252.0px (626.0dp)
getCellLayoutWidth(): 2198.0px (1099.0dp)
diff --git a/tests/assets/dumpTests/DeviceProfileDumpTest/tabletPortrait.txt b/tests/assets/dumpTests/DeviceProfileDumpTest/tabletPortrait.txt
index 59da1be..dba0ec4 100644
--- a/tests/assets/dumpTests/DeviceProfileDumpTest/tabletPortrait.txt
+++ b/tests/assets/dumpTests/DeviceProfileDumpTest/tabletPortrait.txt
@@ -123,7 +123,7 @@
dropTargetBarBottomMarginPx: 96.0px (48.0dp)
getCellLayoutSpringLoadShrunkTop(): 564.0px (282.0dp)
getCellLayoutSpringLoadShrunkBottom(): 2072.0px (1036.0dp)
- workspaceSpringLoadedMinNextPageVisiblePx: 48.0px (24.0dp)
+ workspaceSpringLoadedMinNextPageVisiblePx: 96.0px (48.0dp)
getWorkspaceSpringLoadScale(): 0.8125px (0.40625dp)
getCellLayoutHeight(): 1856.0px (928.0dp)
getCellLayoutWidth(): 1528.0px (764.0dp)
diff --git a/tests/assets/dumpTests/DeviceProfileDumpTest/tabletPortrait3Button.txt b/tests/assets/dumpTests/DeviceProfileDumpTest/tabletPortrait3Button.txt
index ad1f095..495afb2 100644
--- a/tests/assets/dumpTests/DeviceProfileDumpTest/tabletPortrait3Button.txt
+++ b/tests/assets/dumpTests/DeviceProfileDumpTest/tabletPortrait3Button.txt
@@ -123,7 +123,7 @@
dropTargetBarBottomMarginPx: 96.0px (48.0dp)
getCellLayoutSpringLoadShrunkTop(): 564.0px (282.0dp)
getCellLayoutSpringLoadShrunkBottom(): 2072.0px (1036.0dp)
- workspaceSpringLoadedMinNextPageVisiblePx: 48.0px (24.0dp)
+ workspaceSpringLoadedMinNextPageVisiblePx: 96.0px (48.0dp)
getWorkspaceSpringLoadScale(): 0.8125px (0.40625dp)
getCellLayoutHeight(): 1856.0px (928.0dp)
getCellLayoutWidth(): 1528.0px (764.0dp)
diff --git a/tests/assets/dumpTests/DeviceProfileDumpTest/twoPanelLandscape.txt b/tests/assets/dumpTests/DeviceProfileDumpTest/twoPanelLandscape.txt
index 61d5ee6..e7dd3e0 100644
--- a/tests/assets/dumpTests/DeviceProfileDumpTest/twoPanelLandscape.txt
+++ b/tests/assets/dumpTests/DeviceProfileDumpTest/twoPanelLandscape.txt
@@ -123,7 +123,7 @@
dropTargetBarBottomMarginPx: 42.0px (16.0dp)
getCellLayoutSpringLoadShrunkTop(): 299.0px (113.90476dp)
getCellLayoutSpringLoadShrunkBottom(): 1457.0px (555.0476dp)
- workspaceSpringLoadedMinNextPageVisiblePx: 63.0px (24.0dp)
+ workspaceSpringLoadedMinNextPageVisiblePx: 126.0px (48.0dp)
getWorkspaceSpringLoadScale(): 0.8452555px (0.32200208dp)
getCellLayoutHeight(): 1370.0px (521.9048dp)
getCellLayoutWidth(): 1083.0px (412.57144dp)
diff --git a/tests/assets/dumpTests/DeviceProfileDumpTest/twoPanelLandscape3Button.txt b/tests/assets/dumpTests/DeviceProfileDumpTest/twoPanelLandscape3Button.txt
index ac73e2f..fcbd427 100644
--- a/tests/assets/dumpTests/DeviceProfileDumpTest/twoPanelLandscape3Button.txt
+++ b/tests/assets/dumpTests/DeviceProfileDumpTest/twoPanelLandscape3Button.txt
@@ -123,7 +123,7 @@
dropTargetBarBottomMarginPx: 42.0px (16.0dp)
getCellLayoutSpringLoadShrunkTop(): 299.0px (113.90476dp)
getCellLayoutSpringLoadShrunkBottom(): 1457.0px (555.0476dp)
- workspaceSpringLoadedMinNextPageVisiblePx: 63.0px (24.0dp)
+ workspaceSpringLoadedMinNextPageVisiblePx: 126.0px (48.0dp)
getWorkspaceSpringLoadScale(): 0.8452555px (0.32200208dp)
getCellLayoutHeight(): 1370.0px (521.9048dp)
getCellLayoutWidth(): 1083.0px (412.57144dp)
diff --git a/tests/assets/dumpTests/DeviceProfileDumpTest/twoPanelPortrait.txt b/tests/assets/dumpTests/DeviceProfileDumpTest/twoPanelPortrait.txt
index 3169f41..319247c 100644
--- a/tests/assets/dumpTests/DeviceProfileDumpTest/twoPanelPortrait.txt
+++ b/tests/assets/dumpTests/DeviceProfileDumpTest/twoPanelPortrait.txt
@@ -123,7 +123,7 @@
dropTargetBarBottomMarginPx: 84.0px (32.0dp)
getCellLayoutSpringLoadShrunkTop(): 364.0px (138.66667dp)
getCellLayoutSpringLoadShrunkBottom(): 1773.0px (675.4286dp)
- workspaceSpringLoadedMinNextPageVisiblePx: 63.0px (24.0dp)
+ workspaceSpringLoadedMinNextPageVisiblePx: 126.0px (48.0dp)
getWorkspaceSpringLoadScale(): 0.81871px (0.31188953dp)
getCellLayoutHeight(): 1721.0px (655.619dp)
getCellLayoutWidth(): 899.0px (342.4762dp)
diff --git a/tests/assets/dumpTests/DeviceProfileDumpTest/twoPanelPortrait3Button.txt b/tests/assets/dumpTests/DeviceProfileDumpTest/twoPanelPortrait3Button.txt
index 9d3d7bc..50c1d4b 100644
--- a/tests/assets/dumpTests/DeviceProfileDumpTest/twoPanelPortrait3Button.txt
+++ b/tests/assets/dumpTests/DeviceProfileDumpTest/twoPanelPortrait3Button.txt
@@ -123,7 +123,7 @@
dropTargetBarBottomMarginPx: 84.0px (32.0dp)
getCellLayoutSpringLoadShrunkTop(): 364.0px (138.66667dp)
getCellLayoutSpringLoadShrunkBottom(): 1773.0px (675.4286dp)
- workspaceSpringLoadedMinNextPageVisiblePx: 63.0px (24.0dp)
+ workspaceSpringLoadedMinNextPageVisiblePx: 126.0px (48.0dp)
getWorkspaceSpringLoadScale(): 0.81871px (0.31188953dp)
getCellLayoutHeight(): 1721.0px (655.619dp)
getCellLayoutWidth(): 899.0px (342.4762dp)
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 7d195fd..e0fafcc 100644
--- a/tests/multivalentTests/shared/com/android/launcher3/testing/shared/TestProtocol.java
+++ b/tests/multivalentTests/shared/com/android/launcher3/testing/shared/TestProtocol.java
@@ -120,6 +120,8 @@
public static final String REQUEST_CLEAR_DATA = "clear-data";
public static final String REQUEST_HOTSEAT_ICON_NAMES = "get-hotseat-icon-names";
public static final String REQUEST_IS_TABLET = "is-tablet";
+ public static final String REQUEST_IS_PREDICTIVE_BACK_SWIPE_ENABLED =
+ "is-predictive-back-swipe-enabled";
public static final String REQUEST_ENABLE_TASKBAR_NAVBAR_UNIFICATION =
"enable-taskbar-navbar-unification";
public static final String REQUEST_NUM_ALL_APPS_COLUMNS = "num-all-apps-columns";
@@ -176,6 +178,8 @@
public static final String SUCCESSFUL_GESTURE_MISMATCH_EVENTS = "b/324940434";
public static final String TEST_DRAG_APP_ICON_TO_MULTIPLE_WORKSPACES_FAILURE = "b/326908466";
public static final String TEST_TAPL_OVERVIEW_ACTIONS_MENU_FAILURE = "b/326073471";
+ public static final String WIDGET_CONFIG_NULL_EXTRA_INTENT = "b/324419890";
+ public static final String ACTIVITY_NOT_RESUMED_AFTER_BACK = "b/322823209";
public static final String REQUEST_EMULATE_DISPLAY = "emulate-display";
public static final String REQUEST_STOP_EMULATE_DISPLAY = "stop-emulate-display";
diff --git a/tests/src/com/android/launcher3/celllayout/CellPosMapperTest.java b/tests/multivalentTests/src/com/android/launcher3/celllayout/CellPosMapperTest.java
similarity index 100%
rename from tests/src/com/android/launcher3/celllayout/CellPosMapperTest.java
rename to tests/multivalentTests/src/com/android/launcher3/celllayout/CellPosMapperTest.java
diff --git a/tests/src/com/android/launcher3/celllayout/FavoriteItemsTransaction.java b/tests/multivalentTests/src/com/android/launcher3/celllayout/FavoriteItemsTransaction.java
similarity index 89%
rename from tests/src/com/android/launcher3/celllayout/FavoriteItemsTransaction.java
rename to tests/multivalentTests/src/com/android/launcher3/celllayout/FavoriteItemsTransaction.java
index dbb2715..b86333c 100644
--- a/tests/src/com/android/launcher3/celllayout/FavoriteItemsTransaction.java
+++ b/tests/multivalentTests/src/com/android/launcher3/celllayout/FavoriteItemsTransaction.java
@@ -15,8 +15,6 @@
*/
package com.android.launcher3.celllayout;
-import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
-
import static com.android.launcher3.LauncherSettings.Favorites.TABLE_NAME;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
@@ -24,8 +22,6 @@
import android.content.Context;
-import androidx.test.uiautomator.UiDevice;
-
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherModel;
import com.android.launcher3.LauncherSettings;
@@ -33,7 +29,6 @@
import com.android.launcher3.model.data.FolderInfo;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.provider.LauncherDbUtils.SQLiteTransaction;
-import com.android.launcher3.tapl.LauncherInstrumentation;
import com.android.launcher3.util.ContentWriter;
import com.android.launcher3.util.ModelTestExtensions;
@@ -106,15 +101,4 @@
runOnExecutorSync(MAIN_EXECUTOR, model::forceReload);
ModelTestExtensions.INSTANCE.loadModelSync(model);
}
-
- /**
- * Commits the transaction and waits for home load
- */
- public void commitAndLoadHome(LauncherInstrumentation inst) {
- commit();
-
- // Launch the home activity
- UiDevice.getInstance(getInstrumentation()).pressHome();
- inst.waitForLauncherInitialized();
- }
}
diff --git a/tests/src/com/android/launcher3/celllayout/board/CellLayoutBoard.java b/tests/multivalentTests/src/com/android/launcher3/celllayout/board/CellLayoutBoard.java
similarity index 96%
rename from tests/src/com/android/launcher3/celllayout/board/CellLayoutBoard.java
rename to tests/multivalentTests/src/com/android/launcher3/celllayout/board/CellLayoutBoard.java
index 62f2259..e5ad888 100644
--- a/tests/src/com/android/launcher3/celllayout/board/CellLayoutBoard.java
+++ b/tests/multivalentTests/src/com/android/launcher3/celllayout/board/CellLayoutBoard.java
@@ -199,6 +199,19 @@
return 'z';
}
+ /**
+ * Check if the given area is empty.
+ */
+ public boolean isEmpty(int x, int y, int spanX, int spanY) {
+ for (int xi = x; xi < x + spanX; xi++) {
+ for (int yi = y; yi < y + spanY; yi++) {
+ if (mWidget[xi][yi] == CellType.IGNORE) continue;
+ if (mWidget[xi][yi] != CellType.EMPTY) return false;
+ }
+ }
+ return true;
+ }
+
public void addWidget(int x, int y, int spanX, int spanY, char type) {
Rect rect = new Rect(x, y + spanY - 1, x + spanX - 1, y);
removeOverlappingItems(rect);
diff --git a/tests/src/com/android/launcher3/celllayout/board/CellType.java b/tests/multivalentTests/src/com/android/launcher3/celllayout/board/CellType.java
similarity index 100%
rename from tests/src/com/android/launcher3/celllayout/board/CellType.java
rename to tests/multivalentTests/src/com/android/launcher3/celllayout/board/CellType.java
diff --git a/tests/src/com/android/launcher3/celllayout/board/FolderPoint.java b/tests/multivalentTests/src/com/android/launcher3/celllayout/board/FolderPoint.java
similarity index 100%
rename from tests/src/com/android/launcher3/celllayout/board/FolderPoint.java
rename to tests/multivalentTests/src/com/android/launcher3/celllayout/board/FolderPoint.java
diff --git a/tests/src/com/android/launcher3/celllayout/board/IconPoint.java b/tests/multivalentTests/src/com/android/launcher3/celllayout/board/IconPoint.java
similarity index 100%
rename from tests/src/com/android/launcher3/celllayout/board/IconPoint.java
rename to tests/multivalentTests/src/com/android/launcher3/celllayout/board/IconPoint.java
diff --git a/tests/src/com/android/launcher3/celllayout/board/IdenticalBoardComparator.kt b/tests/multivalentTests/src/com/android/launcher3/celllayout/board/IdenticalBoardComparator.kt
similarity index 100%
rename from tests/src/com/android/launcher3/celllayout/board/IdenticalBoardComparator.kt
rename to tests/multivalentTests/src/com/android/launcher3/celllayout/board/IdenticalBoardComparator.kt
diff --git a/tests/src/com/android/launcher3/celllayout/board/PermutedBoardComparator.kt b/tests/multivalentTests/src/com/android/launcher3/celllayout/board/PermutedBoardComparator.kt
similarity index 100%
rename from tests/src/com/android/launcher3/celllayout/board/PermutedBoardComparator.kt
rename to tests/multivalentTests/src/com/android/launcher3/celllayout/board/PermutedBoardComparator.kt
diff --git a/tests/src/com/android/launcher3/celllayout/board/TestWorkspaceBuilder.java b/tests/multivalentTests/src/com/android/launcher3/celllayout/board/TestWorkspaceBuilder.java
similarity index 94%
rename from tests/src/com/android/launcher3/celllayout/board/TestWorkspaceBuilder.java
rename to tests/multivalentTests/src/com/android/launcher3/celllayout/board/TestWorkspaceBuilder.java
index 06a7db2..8a427dd 100644
--- a/tests/src/com/android/launcher3/celllayout/board/TestWorkspaceBuilder.java
+++ b/tests/multivalentTests/src/com/android/launcher3/celllayout/board/TestWorkspaceBuilder.java
@@ -22,7 +22,6 @@
import static com.android.launcher3.util.WidgetUtils.createWidgetInfo;
import android.content.ComponentName;
-import android.content.ContentResolver;
import android.content.Context;
import android.graphics.Rect;
import android.os.Process;
@@ -32,11 +31,6 @@
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.celllayout.FavoriteItemsTransaction;
-import com.android.launcher3.celllayout.board.CellLayoutBoard;
-import com.android.launcher3.celllayout.board.CellType;
-import com.android.launcher3.celllayout.board.FolderPoint;
-import com.android.launcher3.celllayout.board.IconPoint;
-import com.android.launcher3.celllayout.board.WidgetRect;
import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.model.data.FolderInfo;
import com.android.launcher3.model.data.ItemInfo;
@@ -56,12 +50,10 @@
private UserHandle mMyUser;
private Context mContext;
- private ContentResolver mResolver;
public TestWorkspaceBuilder(Context context) {
mMyUser = Process.myUserHandle();
mContext = context;
- mResolver = mContext.getContentResolver();
}
/**
diff --git a/tests/src/com/android/launcher3/celllayout/board/WidgetRect.java b/tests/multivalentTests/src/com/android/launcher3/celllayout/board/WidgetRect.java
similarity index 100%
rename from tests/src/com/android/launcher3/celllayout/board/WidgetRect.java
rename to tests/multivalentTests/src/com/android/launcher3/celllayout/board/WidgetRect.java
diff --git a/tests/src/com/android/launcher3/icons/FastBitmapDrawableTest.java b/tests/multivalentTests/src/com/android/launcher3/icons/FastBitmapDrawableTest.java
similarity index 100%
rename from tests/src/com/android/launcher3/icons/FastBitmapDrawableTest.java
rename to tests/multivalentTests/src/com/android/launcher3/icons/FastBitmapDrawableTest.java
diff --git a/tests/src/com/android/launcher3/logging/FileLogTest.java b/tests/multivalentTests/src/com/android/launcher3/logging/FileLogTest.java
similarity index 100%
rename from tests/src/com/android/launcher3/logging/FileLogTest.java
rename to tests/multivalentTests/src/com/android/launcher3/logging/FileLogTest.java
diff --git a/tests/src/com/android/launcher3/model/data/ItemInfoWithIconTest.kt b/tests/multivalentTests/src/com/android/launcher3/model/data/ItemInfoWithIconTest.kt
similarity index 100%
rename from tests/src/com/android/launcher3/model/data/ItemInfoWithIconTest.kt
rename to tests/multivalentTests/src/com/android/launcher3/model/data/ItemInfoWithIconTest.kt
diff --git a/tests/src/com/android/launcher3/popup/PopupPopulatorTest.java b/tests/multivalentTests/src/com/android/launcher3/popup/PopupPopulatorTest.java
similarity index 100%
rename from tests/src/com/android/launcher3/popup/PopupPopulatorTest.java
rename to tests/multivalentTests/src/com/android/launcher3/popup/PopupPopulatorTest.java
diff --git a/tests/src/com/android/launcher3/testcomponent/AppWidgetDynamicColors.java b/tests/multivalentTests/src/com/android/launcher3/testcomponent/AppWidgetDynamicColors.java
similarity index 100%
rename from tests/src/com/android/launcher3/testcomponent/AppWidgetDynamicColors.java
rename to tests/multivalentTests/src/com/android/launcher3/testcomponent/AppWidgetDynamicColors.java
diff --git a/tests/src/com/android/launcher3/testcomponent/AppWidgetHidden.java b/tests/multivalentTests/src/com/android/launcher3/testcomponent/AppWidgetHidden.java
similarity index 100%
rename from tests/src/com/android/launcher3/testcomponent/AppWidgetHidden.java
rename to tests/multivalentTests/src/com/android/launcher3/testcomponent/AppWidgetHidden.java
diff --git a/tests/src/com/android/launcher3/testcomponent/AppWidgetNoConfig.java b/tests/multivalentTests/src/com/android/launcher3/testcomponent/AppWidgetNoConfig.java
similarity index 100%
rename from tests/src/com/android/launcher3/testcomponent/AppWidgetNoConfig.java
rename to tests/multivalentTests/src/com/android/launcher3/testcomponent/AppWidgetNoConfig.java
diff --git a/tests/src/com/android/launcher3/testcomponent/AppWidgetWithConfig.java b/tests/multivalentTests/src/com/android/launcher3/testcomponent/AppWidgetWithConfig.java
similarity index 100%
rename from tests/src/com/android/launcher3/testcomponent/AppWidgetWithConfig.java
rename to tests/multivalentTests/src/com/android/launcher3/testcomponent/AppWidgetWithConfig.java
diff --git a/tests/src/com/android/launcher3/ui/TestViewHelpers.java b/tests/multivalentTests/src/com/android/launcher3/ui/TestViewHelpers.java
similarity index 100%
rename from tests/src/com/android/launcher3/ui/TestViewHelpers.java
rename to tests/multivalentTests/src/com/android/launcher3/ui/TestViewHelpers.java
diff --git a/tests/src/com/android/launcher3/util/ModelTestExtensions.kt b/tests/multivalentTests/src/com/android/launcher3/util/ModelTestExtensions.kt
similarity index 100%
rename from tests/src/com/android/launcher3/util/ModelTestExtensions.kt
rename to tests/multivalentTests/src/com/android/launcher3/util/ModelTestExtensions.kt
diff --git a/tests/multivalentTests/src/com/android/launcher3/util/rule/BackAndRestoreRule.kt b/tests/multivalentTests/src/com/android/launcher3/util/rule/BackAndRestoreRule.kt
new file mode 100644
index 0000000..da96939
--- /dev/null
+++ b/tests/multivalentTests/src/com/android/launcher3/util/rule/BackAndRestoreRule.kt
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.util.rule
+
+import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
+import com.android.launcher3.InvariantDeviceProfile
+import com.android.launcher3.LauncherPrefs
+import java.io.File
+import java.nio.file.Paths
+import kotlin.io.path.pathString
+import org.junit.rules.TestRule
+import org.junit.runner.Description
+import org.junit.runners.model.Statement
+
+/**
+ * Removes all launcher's DBs from the device and copies the dbs in
+ * assets/databases/BackupAndRestore to the device. It also set's the needed LauncherPrefs variables
+ * needed to kickstart a backup and restore.
+ */
+class BackAndRestoreRule : TestRule {
+
+ private val phoneContext = getInstrumentation().targetContext
+
+ private fun dbBackUp() = File(phoneContext.dataDir.path, "/databasesBackUp")
+
+ private fun dbDirectory() = File(phoneContext.dataDir.path, "/databases")
+
+ private fun isWorkspaceDatabase(rawFileName: String): Boolean {
+ val fileName = Paths.get(rawFileName).fileName.pathString
+ return fileName.startsWith("launcher") && fileName.endsWith(".db")
+ }
+
+ fun getDatabaseFiles() = dbDirectory().listFiles().filter { isWorkspaceDatabase(it.name) }
+
+ /**
+ * Setting RESTORE_DEVICE would trigger a restore next time the Launcher starts, and we remove
+ * the widgets and apps ids to prevent issues when loading the database.
+ */
+ private fun setRestoreConstants() {
+ LauncherPrefs.get(phoneContext)
+ .put(LauncherPrefs.RESTORE_DEVICE.to(InvariantDeviceProfile.TYPE_MULTI_DISPLAY))
+ LauncherPrefs.get(phoneContext)
+ .remove(LauncherPrefs.OLD_APP_WIDGET_IDS, LauncherPrefs.APP_WIDGET_IDS)
+ }
+
+ private fun uploadDatabase(dbName: String) {
+ val file = File(File(getInstrumentation().targetContext.dataDir, "/databases"), dbName)
+ file.writeBytes(
+ getInstrumentation()
+ .context
+ .assets
+ .open("databases/BackupAndRestore/$dbName")
+ .readBytes()
+ )
+ file.setWritable(true, false)
+ }
+
+ private fun uploadDbs() {
+ uploadDatabase("launcher.db")
+ uploadDatabase("launcher_4_by_4.db")
+ uploadDatabase("launcher_4_by_5.db")
+ uploadDatabase("launcher_3_by_3.db")
+ }
+
+ private fun savePreviousState() {
+ dbBackUp().deleteRecursively()
+ if (!dbDirectory().renameTo(dbBackUp())) {
+ throw Exception("Unable to move databases to backup directory")
+ }
+ dbDirectory().mkdir()
+ if (!dbDirectory().exists()) {
+ throw Exception("Databases directory doesn't exists")
+ }
+ }
+
+ private fun restorePreviousState() {
+ dbDirectory().deleteRecursively()
+ if (!dbBackUp().renameTo(dbDirectory())) {
+ throw Exception("Unable to restore backup directory to databases directory")
+ }
+ dbBackUp().delete()
+ }
+
+ fun before() {
+ savePreviousState()
+ setRestoreConstants()
+ uploadDbs()
+ }
+
+ fun after() {
+ restorePreviousState()
+ }
+
+ override fun apply(base: Statement?, description: Description?): Statement =
+ object : Statement() {
+ override fun evaluate() {
+ before()
+ try {
+ base?.evaluate()
+ } finally {
+ after()
+ }
+ }
+ }
+}
diff --git a/tests/src/com/android/launcher3/util/window/WindowManagerProxyTest.kt b/tests/multivalentTests/src/com/android/launcher3/util/window/WindowManagerProxyTest.kt
similarity index 100%
rename from tests/src/com/android/launcher3/util/window/WindowManagerProxyTest.kt
rename to tests/multivalentTests/src/com/android/launcher3/util/window/WindowManagerProxyTest.kt
diff --git a/tests/src/com/android/launcher3/AbstractDeviceProfileTest.kt b/tests/src/com/android/launcher3/AbstractDeviceProfileTest.kt
index 3ba563d..ffcf83f 100644
--- a/tests/src/com/android/launcher3/AbstractDeviceProfileTest.kt
+++ b/tests/src/com/android/launcher3/AbstractDeviceProfileTest.kt
@@ -38,17 +38,17 @@
import com.android.launcher3.util.window.CachedDisplayInfo
import com.android.launcher3.util.window.WindowManagerProxy
import com.google.common.truth.Truth
+import org.junit.Rule
+import org.mockito.kotlin.any
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.spy
+import org.mockito.kotlin.whenever
import java.io.BufferedReader
import java.io.File
import java.io.PrintWriter
import java.io.StringWriter
import kotlin.math.max
import kotlin.math.min
-import org.junit.Rule
-import org.mockito.kotlin.any
-import org.mockito.kotlin.mock
-import org.mockito.kotlin.spy
-import org.mockito.kotlin.whenever
/**
* This is an abstract class for DeviceProfile tests that create an InvariantDeviceProfile based on
@@ -274,7 +274,8 @@
isGestureMode: Boolean = true,
densityDpi: Int
) {
- setFlagsRule.setFlags(false, Flags.FLAG_ENABLE_TWOLINE_TOGGLE)
+ setFlagsRule.setFlags(true, Flags.FLAG_ENABLE_TWOLINE_TOGGLE)
+ LauncherPrefs.get(testContext).put(LauncherPrefs.ENABLE_TWOLINE_ALLAPPS_TOGGLE, true)
val windowsBounds = perDisplayBoundsCache[displayInfo]!!
val realBounds = windowsBounds[rotation]
whenever(windowManagerProxy.getDisplayInfo(any())).thenReturn(displayInfo)
diff --git a/tests/src/com/android/launcher3/LauncherIntentTest.java b/tests/src/com/android/launcher3/LauncherIntentTest.java
new file mode 100644
index 0000000..e8822c3
--- /dev/null
+++ b/tests/src/com/android/launcher3/LauncherIntentTest.java
@@ -0,0 +1,108 @@
+/*
+ * 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 static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.content.Intent;
+import android.platform.test.annotations.LargeTest;
+import android.view.KeyEvent;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.launcher3.allapps.ActivityAllAppsContainerView;
+import com.android.launcher3.allapps.SearchRecyclerView;
+import com.android.launcher3.ui.AbstractLauncherUiTest;
+
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@LargeTest
+@RunWith(AndroidJUnit4.class)
+public class LauncherIntentTest extends AbstractLauncherUiTest {
+
+ public final Intent allAppsIntent = new Intent(Intent.ACTION_ALL_APPS);
+
+ @Test
+ @Ignore("b/329152799")
+ public void testAllAppsIntent() {
+ // setup by moving to home
+ mLauncher.goHome();
+ assertTrue("Launcher internal state is not Home", isInState(() -> LauncherState.NORMAL));
+
+ // Try executing ALL_APPS intent
+ executeOnLauncher(launcher -> launcher.onNewIntent(allAppsIntent));
+ // A-Z view with Main adapter should be loaded
+ assertOnMainAdapterAToZView();
+
+
+ // Try Moving to search view now
+ moveToSearchView();
+ // Try executing ALL_APPS intent
+ executeOnLauncher(launcher -> launcher.onNewIntent(allAppsIntent));
+ // A-Z view with Main adapter should be loaded
+ assertOnMainAdapterAToZView();
+
+ // finish
+ mLauncher.goHome();
+ assertTrue("Launcher internal state is not Home", isInState(() -> LauncherState.NORMAL));
+ }
+
+ // Highlights the search bar, then fills text to display the SearchView.
+ private void moveToSearchView() {
+ mLauncher.goHome().switchToAllApps();
+
+ // All Apps view should be loaded
+ assertTrue("Launcher internal state is not All Apps",
+ isInState(() -> LauncherState.ALL_APPS));
+ executeOnLauncher(launcher -> launcher.getAppsView().getSearchView().requestFocus());
+ // Search view should be in focus
+ waitForLauncherCondition("Search view is not in focus.",
+ launcher -> launcher.getAppsView().getSearchView().hasFocus());
+ mLauncher.pressAndHoldKeyCode(KeyEvent.KEYCODE_C, 0);
+ // Upon key press, search recycler view should be loaded
+ waitForLauncherCondition("Search view not active.",
+ launcher -> launcher.getAppsView().getActiveRecyclerView()
+ instanceof SearchRecyclerView);
+ mLauncher.unpressKeyCode(KeyEvent.KEYCODE_C, 0);
+ }
+
+ // Checks if main adapter view is selected, search bar is out of focus and scroller is at start.
+ private void assertOnMainAdapterAToZView() {
+ // All Apps State should be loaded
+ assertTrue("Launcher internal state is not All Apps",
+ isInState(() -> LauncherState.ALL_APPS));
+
+ // A-Z recycler view should be active.
+ waitForLauncherCondition("A-Z view not active.",
+ launcher -> !(launcher.getAppsView().getActiveRecyclerView()
+ instanceof SearchRecyclerView));
+ // Personal Adapter should be selected.
+ waitForLauncherCondition("Not on Main Adapter View",
+ launcher -> launcher.getAppsView().getCurrentPage()
+ == ActivityAllAppsContainerView.AdapterHolder.MAIN);
+ // Search view should not be in focus
+ waitForLauncherCondition("Search view has focus.",
+ launcher -> !launcher.getAppsView().getSearchView().hasFocus());
+ // Scroller should be at top
+ executeOnLauncher(launcher -> assertEquals(
+ "All Apps started in already scrolled state", 0,
+ getAllAppsScroll(launcher)));
+ }
+}
diff --git a/tests/src/com/android/launcher3/UtilitiesKtTest.kt b/tests/src/com/android/launcher3/UtilitiesKtTest.kt
new file mode 100644
index 0000000..dd83871
--- /dev/null
+++ b/tests/src/com/android/launcher3/UtilitiesKtTest.kt
@@ -0,0 +1,118 @@
+/*
+ * 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.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.launcher3.UtilitiesKt.CLIP_CHILDREN_FALSE_MODIFIER
+import com.android.launcher3.UtilitiesKt.CLIP_TO_PADDING_FALSE_MODIFIER
+import com.android.launcher3.UtilitiesKt.modifyAttributesOnViewTree
+import com.android.launcher3.UtilitiesKt.restoreAttributesOnViewTree
+import com.android.launcher3.util.ActivityContextWrapper
+import com.android.launcher3.views.WidgetsEduView
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class UtilitiesKtTest {
+
+ val context: Context = ActivityContextWrapper(ApplicationProvider.getApplicationContext())
+
+ private lateinit var rootView: WidgetsEduView
+ private lateinit var midView: ViewGroup
+ private lateinit var childView: View
+ @Before
+ fun setup() {
+ rootView =
+ LayoutInflater.from(context).inflate(R.layout.widgets_edu, null) as WidgetsEduView
+ midView = rootView.requireViewById(R.id.edu_view)
+ childView = rootView.requireViewById(R.id.edu_header)
+ }
+
+ @Test
+ fun set_clipChildren_false() {
+ assertThat(rootView.clipChildren).isTrue()
+ assertThat(midView.clipChildren).isTrue()
+
+ modifyAttributesOnViewTree(childView, rootView, CLIP_CHILDREN_FALSE_MODIFIER)
+
+ assertThat(rootView.clipChildren).isFalse()
+ assertThat(midView.clipChildren).isFalse()
+ }
+
+ @Test
+ fun restore_clipChildren_true() {
+ assertThat(rootView.clipChildren).isTrue()
+ assertThat(midView.clipChildren).isTrue()
+ modifyAttributesOnViewTree(childView, rootView, CLIP_CHILDREN_FALSE_MODIFIER)
+ assertThat(rootView.clipChildren).isFalse()
+ assertThat(midView.clipChildren).isFalse()
+
+ restoreAttributesOnViewTree(childView, rootView, CLIP_CHILDREN_FALSE_MODIFIER)
+
+ assertThat(rootView.clipChildren).isTrue()
+ assertThat(midView.clipChildren).isTrue()
+ }
+
+ @Test
+ fun restore_clipChildren_skipRestoreMidView() {
+ assertThat(rootView.clipChildren).isTrue()
+ assertThat(midView.clipChildren).isTrue()
+ rootView.clipChildren = false
+ modifyAttributesOnViewTree(childView, rootView, CLIP_CHILDREN_FALSE_MODIFIER)
+ assertThat(rootView.clipChildren).isFalse()
+ assertThat(midView.clipChildren).isFalse()
+
+ restoreAttributesOnViewTree(childView, rootView, CLIP_CHILDREN_FALSE_MODIFIER)
+
+ assertThat(rootView.clipChildren).isFalse()
+ assertThat(midView.clipChildren).isTrue()
+ }
+
+ @Test
+ fun set_clipToPadding_false() {
+ assertThat(rootView.clipToPadding).isTrue()
+ assertThat(midView.clipToPadding).isTrue()
+
+ modifyAttributesOnViewTree(childView, rootView, CLIP_TO_PADDING_FALSE_MODIFIER)
+
+ assertThat(rootView.clipToPadding).isFalse()
+ assertThat(midView.clipToPadding).isFalse()
+ }
+
+ @Test
+ fun restore_clipToPadding_true() {
+ assertThat(rootView.clipToPadding).isTrue()
+ assertThat(midView.clipToPadding).isTrue()
+ modifyAttributesOnViewTree(childView, rootView, CLIP_TO_PADDING_FALSE_MODIFIER)
+ assertThat(rootView.clipToPadding).isFalse()
+ assertThat(midView.clipToPadding).isFalse()
+
+ restoreAttributesOnViewTree(childView, rootView, CLIP_TO_PADDING_FALSE_MODIFIER)
+
+ assertThat(rootView.clipToPadding).isTrue()
+ assertThat(midView.clipToPadding).isTrue()
+ }
+}
diff --git a/tests/src/com/android/launcher3/allapps/PrivateProfileManagerTest.java b/tests/src/com/android/launcher3/allapps/PrivateProfileManagerTest.java
index 0907f8f..eea4fe5 100644
--- a/tests/src/com/android/launcher3/allapps/PrivateProfileManagerTest.java
+++ b/tests/src/com/android/launcher3/allapps/PrivateProfileManagerTest.java
@@ -47,6 +47,7 @@
import com.android.launcher3.logging.StatsLogManager;
import com.android.launcher3.pm.UserCache;
+import com.android.launcher3.uioverrides.ApiWrapper;
import com.android.launcher3.util.ActivityContextWrapper;
import com.android.launcher3.util.UserIconInfo;
import com.android.launcher3.util.rule.TestStabilityRule;
@@ -176,17 +177,15 @@
}
@Test
- public void openPrivateSpaceSettings_triggersSecurityAndPrivacyIntent() {
- Intent expectedIntent = PrivateProfileManager.PRIVATE_SPACE_INTENT;
+ public void openPrivateSpaceSettings_triggersCorrectIntent() {
+ Intent expectedIntent = ApiWrapper.getPrivateSpaceSettingsIntent(mContext);
ArgumentCaptor<Intent> acIntent = ArgumentCaptor.forClass(Intent.class);
mPrivateProfileManager.setPrivateSpaceSettingsAvailable(true);
mPrivateProfileManager.openPrivateSpaceSettings();
Mockito.verify(mContext).startActivity(acIntent.capture());
- Intent actualIntent = acIntent.getValue();
- assertEquals("Intent Action is different", expectedIntent.getAction(),
- actualIntent.getAction());
+ assertEquals("Intent Action is different", expectedIntent, acIntent.getValue());
}
private static void awaitTasksCompleted() throws Exception {
diff --git a/tests/src/com/android/launcher3/allapps/PrivateSpaceHeaderViewControllerTest.java b/tests/src/com/android/launcher3/allapps/PrivateSpaceHeaderViewControllerTest.java
index 490cb47..043461d 100644
--- a/tests/src/com/android/launcher3/allapps/PrivateSpaceHeaderViewControllerTest.java
+++ b/tests/src/com/android/launcher3/allapps/PrivateSpaceHeaderViewControllerTest.java
@@ -18,6 +18,7 @@
import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
+import static com.android.launcher3.allapps.BaseAllAppsAdapter.VIEW_TYPE_PRIVATE_SPACE_HEADER;
import static com.android.launcher3.allapps.UserProfileManager.STATE_DISABLED;
import static com.android.launcher3.allapps.UserProfileManager.STATE_ENABLED;
import static com.android.launcher3.allapps.UserProfileManager.STATE_TRANSITION;
@@ -25,13 +26,19 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
+import static org.mockito.AdditionalAnswers.answer;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.when;
+import android.content.ComponentName;
import android.content.Context;
+import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
+import android.os.Process;
+import android.os.UserHandle;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.ImageButton;
@@ -44,6 +51,7 @@
import androidx.test.runner.AndroidJUnit4;
import com.android.launcher3.R;
+import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.util.ActivityContextWrapper;
import org.junit.Before;
@@ -52,32 +60,53 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.util.ArrayList;
+import java.util.List;
+
@SmallTest
@RunWith(AndroidJUnit4.class)
public class PrivateSpaceHeaderViewControllerTest {
+ private static final UserHandle MAIN_HANDLE = Process.myUserHandle();
+ private static final UserHandle PRIVATE_HANDLE = new UserHandle(11);
private static final int CONTAINER_HEADER_ELEMENT_COUNT = 1;
private static final int LOCK_UNLOCK_BUTTON_COUNT = 1;
private static final int PS_SETTINGS_BUTTON_COUNT_VISIBLE = 1;
private static final int PS_SETTINGS_BUTTON_COUNT_INVISIBLE = 0;
private static final int PS_TRANSITION_IMAGE_COUNT = 1;
+ private static final int NUM_APP_COLS = 4;
+ private static final int NUM_PRIVATE_SPACE_APPS = 50;
+ private static final int ALL_APPS_HEIGHT = 10;
+ private static final int ALL_APPS_CELL_HEIGHT = 1;
+ private static final int PS_HEADER_HEIGHT = 1;
+ private static final int BIGGER_PS_HEADER_HEIGHT = 2;
+ private static final int SCROLL_NO_WHERE = -1;
+ private static final float HEADER_PROTECTION_HEIGHT = 1F;
private Context mContext;
private PrivateSpaceHeaderViewController mPsHeaderViewController;
private RelativeLayout mPsHeaderLayout;
+ private AlphabeticalAppsList<?> mAlphabeticalAppsList;
@Mock
private PrivateProfileManager mPrivateProfileManager;
@Mock
private ActivityAllAppsContainerView mAllApps;
+ @Mock
+ private AllAppsStore<?> mAllAppsStore;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = new ActivityContextWrapper(getApplicationContext());
+ when(mPrivateProfileManager.getItemInfoMatcher()).thenReturn(info ->
+ info != null && info.user.equals(PRIVATE_HANDLE));
mPsHeaderViewController = new PrivateSpaceHeaderViewController(mAllApps,
mPrivateProfileManager);
mPsHeaderLayout = (RelativeLayout) LayoutInflater.from(mContext).inflate(
R.layout.private_space_header, null);
+ mAlphabeticalAppsList = new AlphabeticalAppsList<>(mContext, mAllAppsStore,
+ null, mPrivateProfileManager);
+ mAlphabeticalAppsList.setNumAppsPerRowAllApps(NUM_APP_COLS);
}
@Test
@@ -223,6 +252,88 @@
assertEquals(PS_TRANSITION_IMAGE_COUNT, totalLockUnlockButtonView);
}
+ @Test
+ public void scrollForViewToBeVisibleInContainer_withHeader() {
+ when(mAllAppsStore.getApps()).thenReturn(createAppInfoList());
+ when(mPrivateProfileManager.addPrivateSpaceHeader(any()))
+ .thenAnswer(answer(this::addPrivateSpaceHeader));
+ when(mPrivateProfileManager.getCurrentState()).thenReturn(STATE_ENABLED);
+ when(mPrivateProfileManager.splitIntoUserInstalledAndSystemApps())
+ .thenReturn(iteminfo -> iteminfo.componentName == null
+ || !iteminfo.componentName.getPackageName()
+ .equals("com.android.launcher3.tests.camera"));
+ when(mAllApps.getContext()).thenReturn(mContext);
+ mAlphabeticalAppsList.updateItemFilter(info -> info != null
+ && info.user.equals(MAIN_HANDLE));
+ when(mAllApps.getHeight()).thenReturn(ALL_APPS_HEIGHT);
+ when(mAllApps.getHeaderProtectionHeight()).thenReturn(HEADER_PROTECTION_HEIGHT);
+ int rows = (int) (ALL_APPS_HEIGHT - PS_HEADER_HEIGHT - HEADER_PROTECTION_HEIGHT);
+ int position = rows * NUM_APP_COLS - (NUM_APP_COLS-1) + 1;
+
+ // The number of adapterItems should be the private space apps + one main app + header.
+ assertEquals(NUM_PRIVATE_SPACE_APPS + 1 + 1,
+ mAlphabeticalAppsList.getAdapterItems().size());
+ assertEquals(position,
+ mPsHeaderViewController.scrollForViewToBeVisibleInContainer(
+ new AllAppsRecyclerView(mContext),
+ mAlphabeticalAppsList.getAdapterItems(),
+ PS_HEADER_HEIGHT,
+ ALL_APPS_CELL_HEIGHT));
+ }
+
+ @Test
+ public void scrollForViewToBeVisibleInContainer_withHeaderAndLessAppRowSpace() {
+ when(mAllAppsStore.getApps()).thenReturn(createAppInfoList());
+ when(mPrivateProfileManager.addPrivateSpaceHeader(any()))
+ .thenAnswer(answer(this::addPrivateSpaceHeader));
+ when(mPrivateProfileManager.getCurrentState()).thenReturn(STATE_ENABLED);
+ when(mPrivateProfileManager.splitIntoUserInstalledAndSystemApps())
+ .thenReturn(iteminfo -> iteminfo.componentName == null
+ || !iteminfo.componentName.getPackageName()
+ .equals("com.android.launcher3.tests.camera"));
+ when(mAllApps.getContext()).thenReturn(mContext);
+ mAlphabeticalAppsList.updateItemFilter(info -> info != null
+ && info.user.equals(MAIN_HANDLE));
+ when(mAllApps.getHeight()).thenReturn(ALL_APPS_HEIGHT);
+ when(mAllApps.getHeaderProtectionHeight()).thenReturn(HEADER_PROTECTION_HEIGHT);
+ int rows = (int) (ALL_APPS_HEIGHT - BIGGER_PS_HEADER_HEIGHT - HEADER_PROTECTION_HEIGHT);
+ int position = rows * NUM_APP_COLS - (NUM_APP_COLS-1) + 1;
+
+ // The number of adapterItems should be the private space apps + one main app + header.
+ assertEquals(NUM_PRIVATE_SPACE_APPS + 1 + 1,
+ mAlphabeticalAppsList.getAdapterItems().size());
+ assertEquals(position,
+ mPsHeaderViewController.scrollForViewToBeVisibleInContainer(
+ new AllAppsRecyclerView(mContext),
+ mAlphabeticalAppsList.getAdapterItems(),
+ BIGGER_PS_HEADER_HEIGHT,
+ ALL_APPS_CELL_HEIGHT));
+ }
+
+ @Test
+ public void scrollForViewToBeVisibleInContainer_withNoHeader() {
+ when(mAllAppsStore.getApps()).thenReturn(createAppInfoList());
+ when(mPrivateProfileManager.getCurrentState()).thenReturn(STATE_ENABLED);
+ when(mPrivateProfileManager.splitIntoUserInstalledAndSystemApps())
+ .thenReturn(iteminfo -> iteminfo.componentName == null
+ || !iteminfo.componentName.getPackageName()
+ .equals("com.android.launcher3.tests.camera"));
+ when(mAllApps.getContext()).thenReturn(mContext);
+ mAlphabeticalAppsList.updateItemFilter(info -> info != null
+ && info.user.equals(MAIN_HANDLE));
+ when(mAllApps.getHeight()).thenReturn(ALL_APPS_HEIGHT);
+ when(mAllApps.getHeaderProtectionHeight()).thenReturn(HEADER_PROTECTION_HEIGHT);
+
+ // The number of adapterItems should be the private space apps + one main app.
+ assertEquals(NUM_PRIVATE_SPACE_APPS + 1,
+ mAlphabeticalAppsList.getAdapterItems().size());
+ assertEquals(SCROLL_NO_WHERE, mPsHeaderViewController.scrollForViewToBeVisibleInContainer(
+ new AllAppsRecyclerView(mContext),
+ mAlphabeticalAppsList.getAdapterItems(),
+ BIGGER_PS_HEADER_HEIGHT,
+ ALL_APPS_CELL_HEIGHT));
+ }
+
private Bitmap getBitmap(Drawable drawable) {
Bitmap result;
if (drawable instanceof BitmapDrawable) {
@@ -249,4 +360,28 @@
private static void awaitTasksCompleted() throws Exception {
UI_HELPER_EXECUTOR.submit(() -> null).get();
}
+
+ private int addPrivateSpaceHeader(List<BaseAllAppsAdapter.AdapterItem> adapterItemList) {
+ BaseAllAppsAdapter.AdapterItem privateSpaceHeader =
+ new BaseAllAppsAdapter.AdapterItem(VIEW_TYPE_PRIVATE_SPACE_HEADER);
+ adapterItemList.add(privateSpaceHeader);
+ return adapterItemList.size();
+ }
+
+ private AppInfo[] createAppInfoList() {
+ List<AppInfo> appInfos = new ArrayList<>();
+ ComponentName gmailComponentName = new ComponentName(mContext,
+ "com.android.launcher3.tests.Activity" + "Gmail");
+ AppInfo gmailAppInfo = new
+ AppInfo(gmailComponentName, "Gmail", MAIN_HANDLE, new Intent());
+ appInfos.add(gmailAppInfo);
+ ComponentName privateCameraComponentName = new ComponentName(
+ "com.android.launcher3.tests.camera", "CameraActivity");
+ for (int i = 0; i < NUM_PRIVATE_SPACE_APPS; i++) {
+ AppInfo privateCameraAppInfo = new AppInfo(privateCameraComponentName,
+ "Private Camera " + i, PRIVATE_HANDLE, new Intent());
+ appInfos.add(privateCameraAppInfo);
+ }
+ return appInfos.toArray(AppInfo[]::new);
+ }
}
diff --git a/tests/src/com/android/launcher3/allapps/TaplOpenCloseAllAppsTest.java b/tests/src/com/android/launcher3/allapps/TaplOpenCloseAllAppsTest.java
index 4d73f7a..e462c4f 100644
--- a/tests/src/com/android/launcher3/allapps/TaplOpenCloseAllAppsTest.java
+++ b/tests/src/com/android/launcher3/allapps/TaplOpenCloseAllAppsTest.java
@@ -20,7 +20,6 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
-import static org.junit.Assume.assumeFalse;
import static org.junit.Assume.assumeTrue;
import android.content.Intent;
@@ -29,7 +28,6 @@
import androidx.test.filters.FlakyTest;
import androidx.test.platform.app.InstrumentationRegistry;
-import com.android.launcher3.Flags;
import com.android.launcher3.LauncherState;
import com.android.launcher3.tapl.AllApps;
import com.android.launcher3.ui.AbstractLauncherUiTest;
@@ -199,7 +197,6 @@
public void testPressBackFromAllAppsToHome() {
InstrumentationRegistry.getInstrumentation().getUiAutomation().adoptShellPermissionIdentity(
READ_DEVICE_CONFIG_PERMISSION);
- assumeFalse(Flags.enablePredictiveBackGesture());
mLauncher
.getWorkspace()
.switchToAllApps()
diff --git a/tests/src/com/android/launcher3/backuprestore/BackupAndRestoreDBSelectionTest.kt b/tests/src/com/android/launcher3/backuprestore/BackupAndRestoreDBSelectionTest.kt
new file mode 100644
index 0000000..479b201
--- /dev/null
+++ b/tests/src/com/android/launcher3/backuprestore/BackupAndRestoreDBSelectionTest.kt
@@ -0,0 +1,70 @@
+/*
+ * 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.backuprestore
+
+import android.platform.test.flag.junit.SetFlagsRule
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.MediumTest
+import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
+import com.android.launcher3.Flags
+import com.android.launcher3.LauncherPrefs
+import com.android.launcher3.model.ModelDbController
+import com.android.launcher3.util.Executors.MODEL_EXECUTOR
+import com.android.launcher3.util.TestUtil
+import com.android.launcher3.util.rule.BackAndRestoreRule
+import com.android.launcher3.util.rule.setFlags
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/**
+ * Makes sure to test {@code RestoreDbTask#removeOldDBs}, we need to remove all the dbs that are not
+ * the last one used when we restore the device.
+ */
+@RunWith(AndroidJUnit4::class)
+@MediumTest
+class BackupAndRestoreDBSelectionTest {
+
+ @JvmField @Rule var backAndRestoreRule = BackAndRestoreRule()
+
+ @JvmField
+ @Rule
+ val setFlagsRule = SetFlagsRule(SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT)
+
+ @Before
+ fun setUp() {
+ setFlagsRule.setFlags(true, Flags.FLAG_ENABLE_NARROW_GRID_RESTORE)
+ }
+
+ @Test
+ fun oldDatabasesNotPresentAfterRestore() {
+ val dbController = ModelDbController(getInstrumentation().targetContext)
+ dbController.tryMigrateDB(null)
+ TestUtil.runOnExecutorSync(MODEL_EXECUTOR) {
+ assert(backAndRestoreRule.getDatabaseFiles().size == 1) {
+ "There should only be one database after restoring, the last one used. Actual databases ${backAndRestoreRule.getDatabaseFiles()}"
+ }
+ assert(
+ !LauncherPrefs.get(getInstrumentation().targetContext)
+ .has(LauncherPrefs.RESTORE_DEVICE)
+ ) {
+ "RESTORE_DEVICE shouldn't be present after a backup and restore."
+ }
+ }
+ }
+}
diff --git a/tests/src/com/android/launcher3/celllayout/TaplReorderWidgetsTest.java b/tests/src/com/android/launcher3/celllayout/TaplReorderWidgetsTest.java
index aca9765..1b03645 100644
--- a/tests/src/com/android/launcher3/celllayout/TaplReorderWidgetsTest.java
+++ b/tests/src/com/android/launcher3/celllayout/TaplReorderWidgetsTest.java
@@ -39,6 +39,7 @@
import com.android.launcher3.tapl.WidgetResizeFrame;
import com.android.launcher3.ui.AbstractLauncherUiTest;
import com.android.launcher3.util.ModelTestExtensions;
+import com.android.launcher3.util.rule.ScreenRecordRule;
import com.android.launcher3.util.rule.ShellCommandRule;
import org.junit.After;
@@ -236,6 +237,7 @@
}
@Test
+ @ScreenRecordRule.ScreenRecord // b/330019521
public void simpleReorder() throws Exception {
runTestCaseMap(getTestMap("ReorderWidgets/simple_reorder_case"),
"push_reorder_case");
diff --git a/tests/src/com/android/launcher3/model/GridMigrationTest.kt b/tests/src/com/android/launcher3/model/GridMigrationTest.kt
new file mode 100644
index 0000000..15222a4
--- /dev/null
+++ b/tests/src/com/android/launcher3/model/GridMigrationTest.kt
@@ -0,0 +1,242 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.model
+
+import android.platform.test.flag.junit.SetFlagsRule
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.launcher3.Flags
+import com.android.launcher3.InvariantDeviceProfile.TYPE_PHONE
+import com.android.launcher3.LauncherSettings.Favorites.TABLE_NAME
+import com.android.launcher3.celllayout.board.CellLayoutBoard
+import com.android.launcher3.pm.UserCache
+import com.android.launcher3.util.rule.TestToPhoneFileCopier
+import com.android.launcher3.util.rule.setFlags
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+private val phoneContext = InstrumentationRegistry.getInstrumentation().targetContext
+
+data class EntryData(val x: Int, val y: Int, val spanX: Int, val spanY: Int, val rank: Int)
+
+/**
+ * Holds the data needed to run a test in GridMigrationTest, usually we would have a src
+ * GridMigrationData and a dst GridMigrationData meaning the data after a migration has occurred.
+ * This class holds a gridState, which is the size of the grid like 5x5 (among other things). a
+ * dbHelper which contains the readable database and writable database used to migrate the
+ * databases.
+ *
+ * You can also get all the entries defined in the dbHelper database.
+ */
+class GridMigrationData(dbFileName: String?, val gridState: DeviceGridState) {
+
+ val dbHelper: DatabaseHelper =
+ DatabaseHelper(
+ phoneContext,
+ dbFileName,
+ { UserCache.INSTANCE.get(phoneContext).getSerialNumberForUser(it) },
+ {}
+ )
+
+ fun readEntries(): List<GridSizeMigrationUtil.DbEntry> =
+ GridSizeMigrationUtil.readAllEntries(dbHelper.readableDatabase, TABLE_NAME, phoneContext)
+}
+
+/**
+ * Test the migration of a database from one size to another. It reads a database from the test
+ * assets, uploads it into the phone and migrates the database to a database in memory which is
+ * later compared against a database in the test assets to make sure they are identical.
+ */
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class GridMigrationTest {
+ private val DB_FILE = "test_launcher.db"
+
+ @JvmField
+ @Rule
+ val setFlagsRule = SetFlagsRule(SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT)
+
+ // Copying the src db for all tests.
+ @JvmField
+ @Rule
+ val fileCopier =
+ TestToPhoneFileCopier(
+ src = "databases/GridMigrationTest/$DB_FILE",
+ dest = "databases/$DB_FILE",
+ removeOnFinish = true
+ )
+
+ @Before
+ fun setup() {
+ setFlagsRule.setFlags(false, Flags.FLAG_ENABLE_GRID_MIGRATION_FIX)
+ }
+
+ private fun migrate(src: GridMigrationData, dst: GridMigrationData) {
+ GridSizeMigrationUtil.migrateGridIfNeeded(
+ phoneContext,
+ src.gridState,
+ dst.gridState,
+ dst.dbHelper,
+ src.dbHelper.readableDatabase
+ )
+ }
+
+ /**
+ * Makes sure that none of the items overlaps on the result, i.e. no widget or icons share the
+ * same space in the db.
+ */
+ private fun validateDb(data: GridMigrationData) {
+ // The array size is just a big enough number to fit all the number of workspaces
+ val boards = Array(100) { CellLayoutBoard(data.gridState.columns, data.gridState.rows) }
+ data.readEntries().forEach {
+ val cellLayoutBoard = boards[it.screenId]
+ assert(cellLayoutBoard.isEmpty(it.cellX, it.cellY, it.spanX, it.spanY)) {
+ "Db has overlapping items"
+ }
+ cellLayoutBoard.addWidget(it.cellX, it.cellY, it.spanX, it.spanY)
+ }
+ }
+
+ private fun compare(dst: GridMigrationData, target: GridMigrationData) {
+ val sort = compareBy<GridSizeMigrationUtil.DbEntry>({ it.cellX }, { it.cellY })
+ val mapF = { it: GridSizeMigrationUtil.DbEntry ->
+ EntryData(it.cellX, it.cellY, it.spanX, it.spanY, it.rank)
+ }
+ val entriesDst = dst.readEntries().sortedWith(sort).map(mapF)
+ val entriesTarget = target.readEntries().sortedWith(sort).map(mapF)
+
+ assert(entriesDst == entriesTarget) {
+ "The elements on the dst database is not the same as in the target"
+ }
+ }
+
+ /**
+ * Migrate src into dst and compare to target. This method validates 3 things:
+ * 1. dst has the same number of items as src after the migration, meaning, none of the items
+ * were removed during the migration.
+ * 2. dst is valid, meaning that none of the items overlap with each other.
+ * 3. dst is equal to target to ensure we don't unintentionally change the migration logic.
+ */
+ private fun runTest(src: GridMigrationData, dst: GridMigrationData, target: GridMigrationData) {
+ migrate(src, dst)
+ assert(src.readEntries().size == dst.readEntries().size) {
+ "Source db and destination db do not contain the same number of elements"
+ }
+ validateDb(dst)
+ compare(dst, target)
+ }
+
+ @JvmField
+ @Rule
+ val result5x5to3x3 =
+ TestToPhoneFileCopier(
+ src = "databases/GridMigrationTest/result5x5to3x3.db",
+ dest = "databases/result5x5to3x3.db",
+ removeOnFinish = true
+ )
+
+ @Test
+ fun `5x5 to 3x3`() =
+ runTest(
+ src = GridMigrationData(DB_FILE, DeviceGridState(5, 5, 5, TYPE_PHONE, DB_FILE)),
+ dst =
+ GridMigrationData(
+ null, // in memory db, to download a new db change null for the filename of the
+ // db name to store it. Do not use existing names.
+ DeviceGridState(3, 3, 3, TYPE_PHONE, "")
+ ),
+ target =
+ GridMigrationData("result5x5to3x3.db", DeviceGridState(3, 3, 3, TYPE_PHONE, ""))
+ )
+
+ @JvmField
+ @Rule
+ val result5x5to4x7 =
+ TestToPhoneFileCopier(
+ src = "databases/GridMigrationTest/result5x5to4x7.db",
+ dest = "databases/result5x5to4x7.db",
+ removeOnFinish = true
+ )
+
+ @Test
+ fun `5x5 to 4x7`() =
+ runTest(
+ src = GridMigrationData(DB_FILE, DeviceGridState(5, 5, 5, TYPE_PHONE, DB_FILE)),
+ dst =
+ GridMigrationData(
+ null, // in memory db, to download a new db change null for the filename of the
+ // db name to store it. Do not use existing names.
+ DeviceGridState(4, 7, 4, TYPE_PHONE, "")
+ ),
+ target =
+ GridMigrationData("result5x5to4x7.db", DeviceGridState(4, 7, 4, TYPE_PHONE, ""))
+ )
+
+ @JvmField
+ @Rule
+ val result5x5to5x8 =
+ TestToPhoneFileCopier(
+ src = "databases/GridMigrationTest/result5x5to5x8.db",
+ dest = "databases/result5x5to5x8.db",
+ removeOnFinish = true
+ )
+
+ @Test
+ fun `5x5 to 5x8`() =
+ runTest(
+ src = GridMigrationData(DB_FILE, DeviceGridState(5, 5, 5, TYPE_PHONE, DB_FILE)),
+ dst =
+ GridMigrationData(
+ null, // in memory db, to download a new db change null for the filename of the
+ // db name to store it. Do not use existing names.
+ DeviceGridState(5, 8, 5, TYPE_PHONE, "")
+ ),
+ target =
+ GridMigrationData("result5x5to5x8.db", DeviceGridState(5, 8, 5, TYPE_PHONE, ""))
+ )
+
+ @JvmField
+ @Rule
+ val flaggedResult5x5to5x8 =
+ TestToPhoneFileCopier(
+ src = "databases/GridMigrationTest/flagged_result5x5to5x8.db",
+ dest = "databases/flagged_result5x5to5x8.db",
+ removeOnFinish = true
+ )
+
+ @Test
+ fun `flagged 5x5 to 5x8`() {
+ setFlagsRule.setFlags(true, Flags.FLAG_ENABLE_GRID_MIGRATION_FIX)
+ runTest(
+ src = GridMigrationData(DB_FILE, DeviceGridState(5, 5, 5, TYPE_PHONE, DB_FILE)),
+ dst =
+ GridMigrationData(
+ null, // in memory db, to download a new db change null for the filename of the
+ // db name to store it. Do not use existing names.
+ DeviceGridState(5, 8, 5, TYPE_PHONE, "")
+ ),
+ target =
+ GridMigrationData(
+ "flagged_result5x5to5x8.db",
+ DeviceGridState(5, 8, 5, TYPE_PHONE, "")
+ )
+ )
+ }
+}
diff --git a/tests/src/com/android/launcher3/secondarydisplay/TaplSecondaryDisplayLauncherTest.java b/tests/src/com/android/launcher3/secondarydisplay/TaplSecondaryDisplayLauncherTest.java
deleted file mode 100644
index d7b9638..0000000
--- a/tests/src/com/android/launcher3/secondarydisplay/TaplSecondaryDisplayLauncherTest.java
+++ /dev/null
@@ -1,279 +0,0 @@
-/*
- * Copyright (C) 2021 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.secondarydisplay;
-
-import static android.content.Context.MODE_PRIVATE;
-import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
-import static android.view.MotionEvent.ACTION_DOWN;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.content.Intent;
-import android.graphics.Point;
-import android.os.SystemClock;
-import android.view.MotionEvent;
-import android.widget.TextView;
-
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.LargeTest;
-import androidx.test.uiautomator.By;
-import androidx.test.uiautomator.UiObject2;
-import androidx.test.uiautomator.Until;
-
-import com.android.launcher3.tapl.LauncherInstrumentation;
-import com.android.launcher3.ui.AbstractLauncherUiTest;
-
-import org.junit.After;
-import org.junit.Ignore;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-/**
- * Tests for {@link SecondaryDisplayLauncher}.
- * TODO (b/242776943): Remove anti-patterns & migrate prediction row tests to Quickstep directory
- */
-@LargeTest
-@RunWith(AndroidJUnit4.class)
-public final class TaplSecondaryDisplayLauncherTest extends AbstractLauncherUiTest {
- private static final int WAIT_TIME_MS = 5000;
- private static final int LONG_PRESS_DURATION_MS = 1000;
- private static final int DRAG_TIME_MS = 160;
-
- private static final String PINNED_APPS_KEY = "pinned_apps";
-
- // Variables required to coordinate drag steps.
- private Point mStartPoint;
- private Point mEndPoint;
- private long mDownTime;
-
- @Override
- public void setUp() throws Exception {
- super.setUp();
- setDragNDropFlag(true);
- }
-
- @After
- public void tearDown() {
- mTargetContext.getSharedPreferences(PINNED_APPS_KEY, MODE_PRIVATE)
- .edit().clear().commit();
- }
-
- @Test
- @Ignore
- public void initializeSecondaryDisplayLauncher_allAppsButtonVisible() {
- assertThat(findObjectByResourceName("all_apps_button")).isNotNull();
- }
-
- @Test
- @Ignore
- public void allAppsButtonTap_opensAppDrawer() {
- openAppDrawer();
- assertThat(findObjectByResourceName("search_container_all_apps")).isNotNull();
- }
-
- @Test
- @Ignore("Launcher3 without quickstep doesn't have a predictions row.")
- public void appDrawerOpened_predictionRowAppDividerVisible() {
- openAppDrawer();
- assertThat(findObjectByResourceName("apps_divider_view")).isNotNull();
- }
-
- @Test
- @Ignore
- public void dragNDropDisabled_pinIconAddsToWorkspace() {
- setDragNDropFlag(false);
- openAppDrawer();
- UiObject2 app = findDescendantByResourceName(
- findObjectByResourceName("apps_list_view"), "icon");
- app.click(LONG_PRESS_DURATION_MS);
- UiObject2 popupContainer = findObjectByResourceName("popup_container");
- assertThat(popupContainer).isNotNull();
- UiObject2 pinIcon = findDescendantByTextOrDesc(popupContainer, "Add to home screen");
- assertThat(pinIcon).isNotNull();
- pinIcon.click();
- String appName = app.getContentDescription();
- assertThat(findAppInWorkspace(appName)).isNotNull();
- }
-
- @Test
- @Ignore
- public void pressBackFromAllApps_popupMenuOpen_returnsToWorkspace() {
- openAppDrawer();
- assertThat(findObjectByResourceName("search_container_all_apps")).isNotNull();
-
- findDescendantByResourceName(findObjectByResourceName("apps_list_view"), "icon")
- .click(LONG_PRESS_DURATION_MS);
- assertThat(findObjectByResourceName("popup_container")).isNotNull();
-
- // First back press should close only popup menu.
- mDevice.pressBack();
- assertThat(findObjectByResourceName("search_container_all_apps")).isNotNull();
- assertThat(findObjectByResourceName("popup_container")).isNull();
-
- // Second back press should close app drawer.
- mDevice.pressBack();
- assertThat(findObjectByResourceName("popup_container")).isNull();
- assertThat(findObjectByResourceName("search_container_all_apps")).isNull();
- }
-
- @Test
- @Ignore("Launcher3 without quickstep doesn't have a predictions row.")
- public void dragNDropFromPredictionsRow_pinToGrid() {
- openAppDrawer();
- assertThat(findObjectByResourceName("prediction_row")).isNotNull();
- String appName = startDragFromPredictionRow();
- moveAppToCenterOfScreen();
- dropApp();
-
- // Ensure app was added.
- assertThat(findAppInWorkspace(appName)).isNotNull();
- }
-
- @Test
- @Ignore
- public void dragNDropFromAppDrawer_pinToGrid() {
- openAppDrawer();
- String draggedAppName = startDragFromAllApps();
- moveAppToCenterOfScreen();
- dropApp();
-
- // Ensure app was added.
- assertThat(findAppInWorkspace(draggedAppName)).isNotNull();
- }
-
- @Test
- @Ignore
- public void tapRemoveButton_unpinApp() {
- openAppDrawer();
- String draggedAppName = startDragFromAllApps();
- moveAppToCenterOfScreen();
- dropApp();
- removeAppByName(draggedAppName);
- assertThat(findAppInWorkspace(draggedAppName)).isNull();
- }
-
- private void openAppDrawer() {
- UiObject2 allAppsButton = findObjectByResourceName("all_apps_button");
- assertThat(allAppsButton).isNotNull();
- allAppsButton.click();
- }
-
- private String startDragFromAllApps() {
- // Find app from app drawer.
- UiObject2 allApps = findObjectByResourceName("apps_list_view");
- assertThat(allApps).isNotNull();
- UiObject2 icon = findDescendantByResourceName(allApps, "icon");
- assertThat(icon).isNotNull();
- String appName = icon.getContentDescription();
-
- // Start drag action.
- mDownTime = SystemClock.uptimeMillis();
- mStartPoint = icon.getVisibleCenter();
- mEndPoint = new Point(mStartPoint.x, mStartPoint.y);
- mLauncher.sendPointer(mDownTime, mDownTime, ACTION_DOWN, mStartPoint,
- LauncherInstrumentation.GestureScope.DONT_EXPECT_PILFER);
- assertThat(findObjectByResourceName("popup_container")).isNotNull();
- return appName;
- }
-
- private String startDragFromPredictionRow() {
- // Find app from predictions.
- UiObject2 predictionRow = findObjectByResourceName("prediction_row");
- assertThat(predictionRow).isNotNull();
-
- UiObject2 icon = findDescendantByResourceName(predictionRow, "icon");
- assertThat(icon).isNotNull();
-
- String appName = icon.getContentDescription();
- UiObject2 app = findDescendantByAppName(predictionRow, appName);
- assertThat(app).isNotNull();
-
- // Start drag action.
- mDownTime = SystemClock.uptimeMillis();
- mStartPoint = icon.getVisibleCenter();
- mEndPoint = new Point(mStartPoint.x, mStartPoint.y);
- mLauncher.sendPointer(mDownTime, mDownTime, ACTION_DOWN, mStartPoint,
- LauncherInstrumentation.GestureScope.DONT_EXPECT_PILFER);
- assertThat(findObjectByResourceName("popup_container")).isNotNull();
- return appName;
- }
-
- private void moveAppToCenterOfScreen() {
- mEndPoint.set(mDevice.getDisplayWidth() / 2, mDevice.getDisplayHeight() / 2);
- mLauncher.movePointer(mDownTime, SystemClock.uptimeMillis(), DRAG_TIME_MS, true,
- mStartPoint, mEndPoint, LauncherInstrumentation.GestureScope.DONT_EXPECT_PILFER);
- }
-
- private void dropApp() {
- mLauncher.sendPointer(mDownTime, SystemClock.uptimeMillis(), MotionEvent.ACTION_UP,
- mEndPoint, LauncherInstrumentation.GestureScope.DONT_EXPECT_PILFER);
- }
-
- private void removeAppByName(String appName) {
- // Find app within home screen.
- UiObject2 app = findDescendantByAppName(findObjectByResourceName("workspace_grid"),
- appName);
- if (app == null) return;
-
- // Open app's popup container.
- app.click(LONG_PRESS_DURATION_MS);
- UiObject2 popupContainer = findObjectByResourceName("popup_container");
- assertThat(popupContainer).isNotNull();
-
- // Grab & click remove button.
- UiObject2 removeButton = findDescendantByTextOrDesc(popupContainer, "Remove");
- assertThat(removeButton).isNotNull();
- removeButton.click();
- }
-
- private UiObject2 findAppInWorkspace(String appName) {
- UiObject2 workspace = findObjectByResourceName("workspace_grid");
- return findDescendantByAppName(workspace, appName);
- }
-
- private UiObject2 findObjectByResourceName(String resourceName) {
- return mDevice.wait(Until.findObject(By.res(mTargetPackage, resourceName)), WAIT_TIME_MS);
- }
-
- private UiObject2 findDescendantByResourceName(UiObject2 outerObject,
- String resourceName) {
- assertThat(outerObject).isNotNull();
- return outerObject.findObject(By.res(mTargetPackage, resourceName));
- }
-
- private UiObject2 findDescendantByAppName(UiObject2 outerObject, String appName) {
- assertThat(outerObject).isNotNull();
- return outerObject.findObject(By.clazz(TextView.class).text(appName)
- .pkg(mDevice.getLauncherPackageName()));
- }
-
- private UiObject2 findDescendantByTextOrDesc(UiObject2 outerObject, String content) {
- assertThat(outerObject).isNotNull();
- UiObject2 innerObject = outerObject.findObject(By.desc(content));
- if (innerObject == null) innerObject = outerObject.findObject(By.text(content));
- return innerObject;
- }
-
- private void startSecondaryDisplayActivity() {
- mTargetContext.startActivity((
- new Intent(mTargetContext, SecondaryDisplayLauncher.class).addFlags(
- FLAG_ACTIVITY_NEW_TASK)));
- }
-
- private void setDragNDropFlag(Boolean status) {
- startSecondaryDisplayActivity();
- }
-}
diff --git a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
index 1f824b8..972be80 100644
--- a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
+++ b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
@@ -20,6 +20,7 @@
import static androidx.test.InstrumentationRegistry.getInstrumentation;
import static com.android.launcher3.testing.shared.TestProtocol.ICON_MISSING;
+import static com.android.launcher3.testing.shared.TestProtocol.WIDGET_CONFIG_NULL_EXTRA_INTENT;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static org.junit.Assert.assertEquals;
@@ -55,6 +56,7 @@
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
import com.android.launcher3.Utilities;
+import com.android.launcher3.celllayout.FavoriteItemsTransaction;
import com.android.launcher3.tapl.HomeAllApps;
import com.android.launcher3.tapl.HomeAppIcon;
import com.android.launcher3.tapl.LauncherInstrumentation;
@@ -529,13 +531,23 @@
@Override
public void onReceive(Context context, Intent intent) {
+ Log.d(WIDGET_CONFIG_NULL_EXTRA_INTENT, intent == null
+ ? "AbstractLauncherUiTest.onReceive(): inputted intent NULL"
+ : "AbstractLauncherUiTest.onReceive(): inputted intent NOT NULL");
mIntent = intent;
latch.countDown();
+ Log.d(WIDGET_CONFIG_NULL_EXTRA_INTENT,
+ "AbstractLauncherUiTest.onReceive() Countdown Latch started");
}
public Intent blockingGetIntent() throws InterruptedException {
+ Log.d(WIDGET_CONFIG_NULL_EXTRA_INTENT,
+ "AbstractLauncherUiTest.blockingGetIntent()");
latch.await(DEFAULT_BROADCAST_TIMEOUT_SECS, TimeUnit.SECONDS);
mTargetContext.unregisterReceiver(this);
+ Log.d(WIDGET_CONFIG_NULL_EXTRA_INTENT, mIntent == null
+ ? "AbstractLauncherUiTest.onReceive(): mIntent NULL"
+ : "AbstractLauncherUiTest.onReceive(): mIntent NOT NULL");
return mIntent;
}
@@ -578,6 +590,17 @@
false /* newTask */);
}
+ /** Starts ExcludeFromRecentsTestActivity, which has excludeFromRecents="true". */
+ public static void startExcludeFromRecentsTestActivity() {
+ final String packageName = getAppPackageName();
+ final Intent intent = getInstrumentation().getContext().getPackageManager()
+ .getLaunchIntentForPackage(packageName);
+ intent.setComponent(new ComponentName(packageName,
+ "com.android.launcher3.testcomponent.ExcludeFromRecentsTestActivity"));
+ startIntent(intent, By.pkg(packageName).text("ExcludeFromRecentsTestActivity"),
+ false /* newTask */);
+ }
+
private static void startIntent(Intent intent, BySelector selector, boolean newTask) {
intent.addCategory(Intent.CATEGORY_LAUNCHER);
if (newTask) {
@@ -663,4 +686,12 @@
}
return homeAppIcon;
}
+
+ protected void commitTransactionAndLoadHome(FavoriteItemsTransaction transaction) {
+ transaction.commit();
+
+ // Launch the home activity
+ UiDevice.getInstance(getInstrumentation()).pressHome();
+ mLauncher.waitForLauncherInitialized();
+ }
}
diff --git a/tests/src/com/android/launcher3/ui/BubbleTextViewTest.java b/tests/src/com/android/launcher3/ui/BubbleTextViewTest.java
index 6df3ecd..90ded10 100644
--- a/tests/src/com/android/launcher3/ui/BubbleTextViewTest.java
+++ b/tests/src/com/android/launcher3/ui/BubbleTextViewTest.java
@@ -22,7 +22,7 @@
import static com.android.launcher3.BubbleTextView.DISPLAY_PREDICTION_ROW;
import static com.android.launcher3.BubbleTextView.DISPLAY_SEARCH_RESULT;
import static com.android.launcher3.BubbleTextView.DISPLAY_SEARCH_RESULT_SMALL;
-import static com.android.launcher3.config.FeatureFlags.ENABLE_TWOLINE_ALLAPPS;
+import static com.android.launcher3.LauncherPrefs.ENABLE_TWOLINE_ALLAPPS_TOGGLE;
import static com.google.common.truth.Truth.assertThat;
@@ -39,6 +39,7 @@
import com.android.launcher3.BubbleTextView;
import com.android.launcher3.Flags;
+import com.android.launcher3.LauncherPrefs;
import com.android.launcher3.Utilities;
import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.model.data.ItemInfoWithIcon;
@@ -46,7 +47,6 @@
import com.android.launcher3.util.ActivityContextWrapper;
import com.android.launcher3.util.FlagOp;
import com.android.launcher3.util.IntArray;
-import com.android.launcher3.util.TestUtil;
import com.android.launcher3.views.BaseDragLayer;
import org.junit.Before;
@@ -96,13 +96,14 @@
private Context mContext;
private int mLimitedWidth;
private AppInfo mGmailAppInfo;
+ private LauncherPrefs mLauncherPrefs;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
Utilities.enableRunningInTestHarnessForTests();
- mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_TWOLINE_TOGGLE);
mContext = new ActivityContextWrapper(getApplicationContext());
+ mLauncherPrefs = LauncherPrefs.get(mContext);
mBubbleTextView = new BubbleTextView(mContext);
mBubbleTextView.reset();
@@ -130,190 +131,155 @@
@Test
public void testEmptyString_flagOn() {
- mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_TWOLINE_ALLAPPS);
- try (AutoCloseable flag = TestUtil.overrideFlag(ENABLE_TWOLINE_ALLAPPS, true)) {
- mItemInfoWithIcon.title = EMPTY_STRING;
- mBubbleTextView.setDisplay(DISPLAY_ALL_APPS);
- mBubbleTextView.applyLabel(mItemInfoWithIcon);
- mBubbleTextView.setTypeface(Typeface.MONOSPACE);
- mBubbleTextView.measure(mLimitedWidth, LIMITED_HEIGHT);
+ mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_TWOLINE_TOGGLE);
+ mLauncherPrefs.put(ENABLE_TWOLINE_ALLAPPS_TOGGLE, true);
+ mItemInfoWithIcon.title = EMPTY_STRING;
+ mBubbleTextView.setDisplay(DISPLAY_ALL_APPS);
+ mBubbleTextView.applyLabel(mItemInfoWithIcon);
+ mBubbleTextView.setTypeface(Typeface.MONOSPACE);
+ mBubbleTextView.measure(mLimitedWidth, LIMITED_HEIGHT);
- mBubbleTextView.onPreDraw();
+ mBubbleTextView.onPreDraw();
- assertNotEquals(TWO_LINE, mBubbleTextView.getMaxLines());
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
+ assertNotEquals(TWO_LINE, mBubbleTextView.getMaxLines());
}
@Test
public void testEmptyString_flagOff() {
- mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_TWOLINE_ALLAPPS);
- try (AutoCloseable flag = TestUtil.overrideFlag(ENABLE_TWOLINE_ALLAPPS, false)) {
- mItemInfoWithIcon.title = EMPTY_STRING;
- mBubbleTextView.setDisplay(DISPLAY_ALL_APPS);
- mBubbleTextView.applyLabel(mItemInfoWithIcon);
- mBubbleTextView.setTypeface(Typeface.MONOSPACE);
- mBubbleTextView.measure(mLimitedWidth, LIMITED_HEIGHT);
+ mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_TWOLINE_TOGGLE);
+ mItemInfoWithIcon.title = EMPTY_STRING;
+ mBubbleTextView.setDisplay(DISPLAY_ALL_APPS);
+ mBubbleTextView.applyLabel(mItemInfoWithIcon);
+ mBubbleTextView.setTypeface(Typeface.MONOSPACE);
+ mBubbleTextView.measure(mLimitedWidth, LIMITED_HEIGHT);
- mBubbleTextView.onPreDraw();
+ mBubbleTextView.onPreDraw();
- assertEquals(ONE_LINE, mBubbleTextView.getLineCount());
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
+ assertEquals(ONE_LINE, mBubbleTextView.getLineCount());
}
@Test
public void testStringWithSpaceLongerThanCharLimit_flagOn() {
- mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_TWOLINE_ALLAPPS);
- try (AutoCloseable flag = TestUtil.overrideFlag(ENABLE_TWOLINE_ALLAPPS, true)) {
- // test string: "Battery Stats"
- mItemInfoWithIcon.title = TEST_STRING_WITH_SPACE_LONGER_THAN_CHAR_LIMIT;
- mBubbleTextView.applyLabel(mItemInfoWithIcon);
- mBubbleTextView.setDisplay(DISPLAY_ALL_APPS);
- mBubbleTextView.setTypeface(Typeface.MONOSPACE);
- mBubbleTextView.measure(mLimitedWidth, MAX_HEIGHT);
+ mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_TWOLINE_TOGGLE);
+ mLauncherPrefs.put(ENABLE_TWOLINE_ALLAPPS_TOGGLE, true);
+ // test string: "Battery Stats"
+ mItemInfoWithIcon.title = TEST_STRING_WITH_SPACE_LONGER_THAN_CHAR_LIMIT;
+ mBubbleTextView.applyLabel(mItemInfoWithIcon);
+ mBubbleTextView.setDisplay(DISPLAY_ALL_APPS);
+ mBubbleTextView.setTypeface(Typeface.MONOSPACE);
+ mBubbleTextView.measure(mLimitedWidth, MAX_HEIGHT);
- mBubbleTextView.onPreDraw();
+ mBubbleTextView.onPreDraw();
- assertEquals(TWO_LINE, mBubbleTextView.getLineCount());
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
+ assertEquals(TWO_LINE, mBubbleTextView.getLineCount());
}
@Test
public void testStringWithSpaceLongerThanCharLimit_flagOff() {
- mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_TWOLINE_ALLAPPS);
- try (AutoCloseable flag = TestUtil.overrideFlag(ENABLE_TWOLINE_ALLAPPS, false)) {
- // test string: "Battery Stats"
- mItemInfoWithIcon.title = TEST_STRING_WITH_SPACE_LONGER_THAN_CHAR_LIMIT;
- mBubbleTextView.applyLabel(mItemInfoWithIcon);
- mBubbleTextView.setDisplay(DISPLAY_ALL_APPS);
- mBubbleTextView.setTypeface(Typeface.MONOSPACE);
- mBubbleTextView.measure(mLimitedWidth, LIMITED_HEIGHT);
+ mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_TWOLINE_TOGGLE);
+ // test string: "Battery Stats"
+ mItemInfoWithIcon.title = TEST_STRING_WITH_SPACE_LONGER_THAN_CHAR_LIMIT;
+ mBubbleTextView.applyLabel(mItemInfoWithIcon);
+ mBubbleTextView.setDisplay(DISPLAY_ALL_APPS);
+ mBubbleTextView.setTypeface(Typeface.MONOSPACE);
+ mBubbleTextView.measure(mLimitedWidth, LIMITED_HEIGHT);
- mBubbleTextView.onPreDraw();
+ mBubbleTextView.onPreDraw();
- assertEquals(ONE_LINE, mBubbleTextView.getLineCount());
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
+ assertEquals(ONE_LINE, mBubbleTextView.getLineCount());
}
@Test
public void testLongStringNoSpaceLongerThanCharLimit_flagOn() {
- mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_TWOLINE_ALLAPPS);
- try (AutoCloseable flag = TestUtil.overrideFlag(ENABLE_TWOLINE_ALLAPPS, true)) {
- // test string: "flutterappflorafy"
- mItemInfoWithIcon.title = TEST_LONG_STRING_NO_SPACE_LONGER_THAN_CHAR_LIMIT;
- mBubbleTextView.applyLabel(mItemInfoWithIcon);
- mBubbleTextView.setDisplay(DISPLAY_ALL_APPS);
- mBubbleTextView.setTypeface(Typeface.MONOSPACE);
- mBubbleTextView.measure(mLimitedWidth, LIMITED_HEIGHT);
+ mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_TWOLINE_TOGGLE);
+ mLauncherPrefs.put(ENABLE_TWOLINE_ALLAPPS_TOGGLE, true);
+ // test string: "flutterappflorafy"
+ mItemInfoWithIcon.title = TEST_LONG_STRING_NO_SPACE_LONGER_THAN_CHAR_LIMIT;
+ mBubbleTextView.applyLabel(mItemInfoWithIcon);
+ mBubbleTextView.setDisplay(DISPLAY_ALL_APPS);
+ mBubbleTextView.setTypeface(Typeface.MONOSPACE);
+ mBubbleTextView.measure(mLimitedWidth, LIMITED_HEIGHT);
- mBubbleTextView.onPreDraw();
+ mBubbleTextView.onPreDraw();
- assertEquals(ONE_LINE, mBubbleTextView.getLineCount());
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
+ assertEquals(ONE_LINE, mBubbleTextView.getLineCount());
}
@Test
public void testLongStringNoSpaceLongerThanCharLimit_flagOff() {
- mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_TWOLINE_ALLAPPS);
- try (AutoCloseable flag = TestUtil.overrideFlag(ENABLE_TWOLINE_ALLAPPS, false)) {
- // test string: "flutterappflorafy"
- mItemInfoWithIcon.title = TEST_LONG_STRING_NO_SPACE_LONGER_THAN_CHAR_LIMIT;
- mBubbleTextView.applyLabel(mItemInfoWithIcon);
- mBubbleTextView.setDisplay(DISPLAY_ALL_APPS);
- mBubbleTextView.setTypeface(Typeface.MONOSPACE);
- mBubbleTextView.measure(mLimitedWidth, LIMITED_HEIGHT);
+ mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_TWOLINE_TOGGLE);
+ // test string: "flutterappflorafy"
+ mItemInfoWithIcon.title = TEST_LONG_STRING_NO_SPACE_LONGER_THAN_CHAR_LIMIT;
+ mBubbleTextView.applyLabel(mItemInfoWithIcon);
+ mBubbleTextView.setDisplay(DISPLAY_ALL_APPS);
+ mBubbleTextView.setTypeface(Typeface.MONOSPACE);
+ mBubbleTextView.measure(mLimitedWidth, LIMITED_HEIGHT);
- mBubbleTextView.onPreDraw();
+ mBubbleTextView.onPreDraw();
- assertEquals(ONE_LINE, mBubbleTextView.getLineCount());
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
+ assertEquals(ONE_LINE, mBubbleTextView.getLineCount());
}
@Test
public void testLongStringWithSpaceLongerThanCharLimit_flagOn() {
- mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_TWOLINE_ALLAPPS);
- try (AutoCloseable flag = TestUtil.overrideFlag(ENABLE_TWOLINE_ALLAPPS, true)) {
- // test string: "System UWB Field Test"
- mItemInfoWithIcon.title = TEST_LONG_STRING_WITH_SPACE_LONGER_THAN_CHAR_LIMIT;
- mBubbleTextView.applyLabel(mItemInfoWithIcon);
- mBubbleTextView.setDisplay(DISPLAY_ALL_APPS);
- mBubbleTextView.setTypeface(Typeface.MONOSPACE);
- mBubbleTextView.measure(mLimitedWidth, MAX_HEIGHT);
+ mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_TWOLINE_TOGGLE);
+ mLauncherPrefs.put(ENABLE_TWOLINE_ALLAPPS_TOGGLE, true);
+ // test string: "System UWB Field Test"
+ mItemInfoWithIcon.title = TEST_LONG_STRING_WITH_SPACE_LONGER_THAN_CHAR_LIMIT;
+ mBubbleTextView.applyLabel(mItemInfoWithIcon);
+ mBubbleTextView.setDisplay(DISPLAY_ALL_APPS);
+ mBubbleTextView.setTypeface(Typeface.MONOSPACE);
+ mBubbleTextView.measure(mLimitedWidth, MAX_HEIGHT);
- mBubbleTextView.onPreDraw();
+ mBubbleTextView.onPreDraw();
- assertEquals(TWO_LINE, mBubbleTextView.getLineCount());
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
+ assertEquals(TWO_LINE, mBubbleTextView.getLineCount());
}
@Test
public void testLongStringWithSpaceLongerThanCharLimit_flagOff() {
- mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_TWOLINE_ALLAPPS);
- try (AutoCloseable flag = TestUtil.overrideFlag(ENABLE_TWOLINE_ALLAPPS, false)) {
- // test string: "System UWB Field Test"
- mItemInfoWithIcon.title = TEST_LONG_STRING_WITH_SPACE_LONGER_THAN_CHAR_LIMIT;
- mBubbleTextView.applyLabel(mItemInfoWithIcon);
- mBubbleTextView.setDisplay(DISPLAY_ALL_APPS);
- mBubbleTextView.setTypeface(Typeface.MONOSPACE);
- mBubbleTextView.measure(mLimitedWidth, LIMITED_HEIGHT);
+ mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_TWOLINE_TOGGLE);
+ // test string: "System UWB Field Test"
+ mItemInfoWithIcon.title = TEST_LONG_STRING_WITH_SPACE_LONGER_THAN_CHAR_LIMIT;
+ mBubbleTextView.applyLabel(mItemInfoWithIcon);
+ mBubbleTextView.setDisplay(DISPLAY_ALL_APPS);
+ mBubbleTextView.setTypeface(Typeface.MONOSPACE);
+ mBubbleTextView.measure(mLimitedWidth, LIMITED_HEIGHT);
- mBubbleTextView.onPreDraw();
+ mBubbleTextView.onPreDraw();
- assertEquals(ONE_LINE, mBubbleTextView.getLineCount());
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
+ assertEquals(ONE_LINE, mBubbleTextView.getLineCount());
}
@Test
public void testLongStringSymbolLongerThanCharLimit_flagOn() {
- mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_TWOLINE_ALLAPPS);
- try (AutoCloseable flag = TestUtil.overrideFlag(ENABLE_TWOLINE_ALLAPPS, true)) {
- // test string: "LEGO®Builder"
- mItemInfoWithIcon.title = TEST_LONG_STRING_SYMBOL_LONGER_THAN_CHAR_LIMIT;
- mBubbleTextView.applyLabel(mItemInfoWithIcon);
- mBubbleTextView.setDisplay(DISPLAY_ALL_APPS);
- mBubbleTextView.setTypeface(Typeface.MONOSPACE);
- mBubbleTextView.measure(mLimitedWidth, MAX_HEIGHT);
+ mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_TWOLINE_TOGGLE);
+ mLauncherPrefs.put(ENABLE_TWOLINE_ALLAPPS_TOGGLE, true);
+ // test string: "LEGO®Builder"
+ mItemInfoWithIcon.title = TEST_LONG_STRING_SYMBOL_LONGER_THAN_CHAR_LIMIT;
+ mBubbleTextView.applyLabel(mItemInfoWithIcon);
+ mBubbleTextView.setDisplay(DISPLAY_ALL_APPS);
+ mBubbleTextView.setTypeface(Typeface.MONOSPACE);
+ mBubbleTextView.measure(mLimitedWidth, MAX_HEIGHT);
- mBubbleTextView.onPreDraw();
+ mBubbleTextView.onPreDraw();
- assertEquals(TWO_LINE, mBubbleTextView.getLineCount());
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
+ assertEquals(TWO_LINE, mBubbleTextView.getLineCount());
}
@Test
public void testLongStringSymbolLongerThanCharLimit_flagOff() {
- mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_TWOLINE_ALLAPPS);
- try (AutoCloseable flag = TestUtil.overrideFlag(ENABLE_TWOLINE_ALLAPPS, false)) {
- // test string: "LEGO®Builder"
- mItemInfoWithIcon.title = TEST_LONG_STRING_SYMBOL_LONGER_THAN_CHAR_LIMIT;
- mBubbleTextView.applyLabel(mItemInfoWithIcon);
- mBubbleTextView.setDisplay(DISPLAY_ALL_APPS);
- mBubbleTextView.setTypeface(Typeface.MONOSPACE);
- mBubbleTextView.measure(mLimitedWidth, LIMITED_HEIGHT);
+ mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_TWOLINE_TOGGLE);
+ // test string: "LEGO®Builder"
+ mItemInfoWithIcon.title = TEST_LONG_STRING_SYMBOL_LONGER_THAN_CHAR_LIMIT;
+ mBubbleTextView.applyLabel(mItemInfoWithIcon);
+ mBubbleTextView.setDisplay(DISPLAY_ALL_APPS);
+ mBubbleTextView.setTypeface(Typeface.MONOSPACE);
+ mBubbleTextView.measure(mLimitedWidth, LIMITED_HEIGHT);
- mBubbleTextView.onPreDraw();
+ mBubbleTextView.onPreDraw();
- assertEquals(ONE_LINE, mBubbleTextView.getLineCount());
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
+ assertEquals(ONE_LINE, mBubbleTextView.getLineCount());
}
@Test
@@ -374,58 +340,49 @@
@Test
public void testEnsurePredictionRowIsTwoLine() {
- mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_TWOLINE_ALLAPPS);
- try (AutoCloseable flag = TestUtil.overrideFlag(ENABLE_TWOLINE_ALLAPPS, true)) {
- // test string: "Battery Stats"
- mItemInfoWithIcon.title = TEST_STRING_WITH_SPACE_LONGER_THAN_CHAR_LIMIT;
- mBubbleTextView.setDisplay(DISPLAY_PREDICTION_ROW);
- mBubbleTextView.applyLabel(mItemInfoWithIcon);
- mBubbleTextView.setTypeface(Typeface.MONOSPACE);
- mBubbleTextView.measure(mLimitedWidth, MAX_HEIGHT);
+ mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_TWOLINE_TOGGLE);
+ mLauncherPrefs.put(ENABLE_TWOLINE_ALLAPPS_TOGGLE, true);
+ // test string: "Battery Stats"
+ mItemInfoWithIcon.title = TEST_STRING_WITH_SPACE_LONGER_THAN_CHAR_LIMIT;
+ mBubbleTextView.setDisplay(DISPLAY_PREDICTION_ROW);
+ mBubbleTextView.applyLabel(mItemInfoWithIcon);
+ mBubbleTextView.setTypeface(Typeface.MONOSPACE);
+ mBubbleTextView.measure(mLimitedWidth, MAX_HEIGHT);
- mBubbleTextView.onPreDraw();
+ mBubbleTextView.onPreDraw();
- assertEquals(TWO_LINE, mBubbleTextView.getLineCount());
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
+ assertEquals(TWO_LINE, mBubbleTextView.getLineCount());
}
@Test
public void modifyTitleToSupportMultiLine_whenLimitedHeight_shouldBeOneLine() {
- mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_TWOLINE_ALLAPPS);
- try (AutoCloseable flag = TestUtil.overrideFlag(ENABLE_TWOLINE_ALLAPPS, true)) {
- // test string: "LEGO®Builder"
- mItemInfoWithIcon.title = TEST_LONG_STRING_SYMBOL_LONGER_THAN_CHAR_LIMIT;
- mBubbleTextView.applyLabel(mItemInfoWithIcon);
- mBubbleTextView.setTypeface(Typeface.MONOSPACE);
- mBubbleTextView.measure(mLimitedWidth, LIMITED_HEIGHT);
+ mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_TWOLINE_TOGGLE);
+ mLauncherPrefs.put(ENABLE_TWOLINE_ALLAPPS_TOGGLE, true);
+ // test string: "LEGO®Builder"
+ mItemInfoWithIcon.title = TEST_LONG_STRING_SYMBOL_LONGER_THAN_CHAR_LIMIT;
+ mBubbleTextView.applyLabel(mItemInfoWithIcon);
+ mBubbleTextView.setTypeface(Typeface.MONOSPACE);
+ mBubbleTextView.measure(mLimitedWidth, LIMITED_HEIGHT);
- mBubbleTextView.onPreDraw();
+ mBubbleTextView.onPreDraw();
- assertEquals(ONE_LINE, mBubbleTextView.getLineCount());
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
+ assertEquals(ONE_LINE, mBubbleTextView.getLineCount());
}
@Test
public void modifyTitleToSupportMultiLine_whenUnlimitedHeight_shouldBeTwoLine() {
- mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_TWOLINE_ALLAPPS);
- try (AutoCloseable flag = TestUtil.overrideFlag(ENABLE_TWOLINE_ALLAPPS, true)) {
- // test string: "LEGO®Builder"
- mItemInfoWithIcon.title = TEST_LONG_STRING_SYMBOL_LONGER_THAN_CHAR_LIMIT;
- mBubbleTextView.setDisplay(DISPLAY_ALL_APPS);
- mBubbleTextView.applyLabel(mItemInfoWithIcon);
- mBubbleTextView.setTypeface(Typeface.MONOSPACE);
- mBubbleTextView.measure(mLimitedWidth, MAX_HEIGHT);
+ mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_TWOLINE_TOGGLE);
+ mLauncherPrefs.put(ENABLE_TWOLINE_ALLAPPS_TOGGLE, true);
+ // test string: "LEGO®Builder"
+ mItemInfoWithIcon.title = TEST_LONG_STRING_SYMBOL_LONGER_THAN_CHAR_LIMIT;
+ mBubbleTextView.setDisplay(DISPLAY_ALL_APPS);
+ mBubbleTextView.applyLabel(mItemInfoWithIcon);
+ mBubbleTextView.setTypeface(Typeface.MONOSPACE);
+ mBubbleTextView.measure(mLimitedWidth, MAX_HEIGHT);
- mBubbleTextView.onPreDraw();
+ mBubbleTextView.onPreDraw();
- assertEquals(TWO_LINE, mBubbleTextView.getLineCount());
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
+ assertEquals(TWO_LINE, mBubbleTextView.getLineCount());
}
@Test
diff --git a/tests/src/com/android/launcher3/ui/PortraitLandscapeRunner.java b/tests/src/com/android/launcher3/ui/PortraitLandscapeRunner.java
index 5ef63da..f01bdb3 100644
--- a/tests/src/com/android/launcher3/ui/PortraitLandscapeRunner.java
+++ b/tests/src/com/android/launcher3/ui/PortraitLandscapeRunner.java
@@ -4,6 +4,7 @@
import android.view.Surface;
import com.android.launcher3.tapl.TestHelpers;
+import com.android.launcher3.util.rule.FailureWatcher;
import com.android.launcher3.util.rule.TestStabilityRule;
import org.junit.rules.TestRule;
@@ -45,17 +46,23 @@
@Override
public void evaluate() throws Throwable {
try {
- // we expect to begin unlocked...
- AbstractLauncherUiTest.verifyKeyguardInvisible();
+ try {
+ // we expect to begin unlocked...
+ AbstractLauncherUiTest.verifyKeyguardInvisible();
- mTest.mDevice.pressHome();
- mTest.waitForLauncherCondition("Launcher activity wasn't created",
- launcher -> launcher != null,
- TimeUnit.SECONDS.toMillis(20));
+ mTest.mDevice.pressHome();
+ mTest.waitForLauncherCondition("Launcher activity wasn't created",
+ launcher -> launcher != null,
+ TimeUnit.SECONDS.toMillis(20));
- mTest.executeOnLauncher(launcher ->
- launcher.getRotationHelper().forceAllowRotationForTesting(
- true));
+ mTest.executeOnLauncher(launcher ->
+ launcher.getRotationHelper().forceAllowRotationForTesting(
+ true));
+
+ } catch (Throwable e) {
+ FailureWatcher.onError(mTest.mLauncher, description, e);
+ throw e;
+ }
evaluateInPortrait();
evaluateInLandscape();
diff --git a/tests/src/com/android/launcher3/ui/widget/TaplAddConfigWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/TaplAddConfigWidgetTest.java
index 7aa26a1..1db6014 100644
--- a/tests/src/com/android/launcher3/ui/widget/TaplAddConfigWidgetTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/TaplAddConfigWidgetTest.java
@@ -87,7 +87,7 @@
* @param acceptConfig accept the config activity
*/
private void runTest(boolean acceptConfig) throws Throwable {
- new FavoriteItemsTransaction(mTargetContext).commitAndLoadHome(mLauncher);
+ commitTransactionAndLoadHome(new FavoriteItemsTransaction(mTargetContext));
// Drag widget to homescreen
WidgetConfigStartupMonitor monitor = new WidgetConfigStartupMonitor();
diff --git a/tests/src/com/android/launcher3/ui/widget/TaplAddWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/TaplAddWidgetTest.java
index 5ca5ba5..6c24329 100644
--- a/tests/src/com/android/launcher3/ui/widget/TaplAddWidgetTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/TaplAddWidgetTest.java
@@ -56,7 +56,7 @@
@ScreenRecord // b/316910614
public void testDragIcon() throws Throwable {
mLauncher.enableDebugTracing(); // b/289161193
- new FavoriteItemsTransaction(mTargetContext).commitAndLoadHome(mLauncher);
+ commitTransactionAndLoadHome(new FavoriteItemsTransaction(mTargetContext));
waitForLauncherCondition("Workspace didn't finish loading", l -> !l.isWorkspaceLoading());
@@ -92,7 +92,7 @@
// TODO(b/322820039): Enable test for tablets - the picker UI has changed and test needs to
// be updated to look for appropriate UI elements.
Assume.assumeFalse(mLauncher.isTablet());
- new FavoriteItemsTransaction(mTargetContext).commitAndLoadHome(mLauncher);
+ commitTransactionAndLoadHome(new FavoriteItemsTransaction(mTargetContext));
mLauncher.getWorkspace().openAllWidgets()
.getWidget("com.android.launcher3.testcomponent.CustomShortcutConfigActivity")
@@ -104,11 +104,12 @@
/**
* Test dragging a widget to the workspace and resize it.
*/
+ @Stability(flavors = LOCAL | PLATFORM_POSTSUBMIT) // b/316910614
@PlatinumTest(focusArea = "launcher")
@Test
@ScreenRecord // b/316910614
public void testResizeWidget() throws Throwable {
- new FavoriteItemsTransaction(mTargetContext).commitAndLoadHome(mLauncher);
+ commitTransactionAndLoadHome(new FavoriteItemsTransaction(mTargetContext));
waitForLauncherCondition("Workspace didn't finish loading", l -> !l.isWorkspaceLoading());
diff --git a/tests/src/com/android/launcher3/ui/widget/TaplBindWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/TaplBindWidgetTest.java
index 6aa746d..e69cb4c 100644
--- a/tests/src/com/android/launcher3/ui/widget/TaplBindWidgetTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/TaplBindWidgetTest.java
@@ -251,22 +251,20 @@
private void addPendingItemToScreen(LauncherAppWidgetInfo item, int restoreStatus) {
item.restoreStatus = restoreStatus;
item.screenId = FIRST_SCREEN_ID;
- new FavoriteItemsTransaction(mTargetContext)
- .addItem(() -> item)
- .commitAndLoadHome(mLauncher);
+ commitTransactionAndLoadHome(
+ new FavoriteItemsTransaction(mTargetContext).addItem(() -> item));
}
private LauncherAppWidgetProviderInfo addWidgetToScreen(boolean hasConfigureScreen,
boolean bindWidget, Consumer<LauncherAppWidgetInfo> itemOverride) {
LauncherAppWidgetProviderInfo info = TestViewHelpers.findWidgetProvider(hasConfigureScreen);
- new FavoriteItemsTransaction(mTargetContext)
+ commitTransactionAndLoadHome(new FavoriteItemsTransaction(mTargetContext)
.addItem(() -> {
LauncherAppWidgetInfo item = createWidgetInfo(info, mTargetContext, bindWidget);
item.screenId = FIRST_SCREEN_ID;
itemOverride.accept(item);
return item;
- })
- .commitAndLoadHome(mLauncher);
+ }));
return info;
}
diff --git a/tests/src/com/android/launcher3/ui/widget/TaplRequestPinItemTest.java b/tests/src/com/android/launcher3/ui/widget/TaplRequestPinItemTest.java
index 7dba728..90a6d22 100644
--- a/tests/src/com/android/launcher3/ui/widget/TaplRequestPinItemTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/TaplRequestPinItemTest.java
@@ -128,7 +128,7 @@
private void runTest(String activityMethod, boolean isWidget, ItemOperator itemMatcher,
Intent... commandIntents) throws Throwable {
- new FavoriteItemsTransaction(mTargetContext).commitAndLoadHome(mLauncher);
+ commitTransactionAndLoadHome(new FavoriteItemsTransaction(mTargetContext));
// Open Pin item activity
BlockingBroadcastReceiver openMonitor = new BlockingBroadcastReceiver(
diff --git a/tests/src/com/android/launcher3/ui/widget/TaplWidgetPickerTest.java b/tests/src/com/android/launcher3/ui/widget/TaplWidgetPickerTest.java
index 1cd8c88..4acdddc 100644
--- a/tests/src/com/android/launcher3/ui/widget/TaplWidgetPickerTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/TaplWidgetPickerTest.java
@@ -15,6 +15,8 @@
*/
package com.android.launcher3.ui.widget;
+import static com.android.launcher3.util.rule.ShellCommandRule.createEnableInputTransportPublisherRule;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
@@ -30,7 +32,9 @@
import com.android.launcher3.widget.picker.WidgetsFullSheet;
import com.android.launcher3.widget.picker.WidgetsRecyclerView;
+import org.junit.Rule;
import org.junit.Test;
+import org.junit.rules.TestRule;
import org.junit.runner.RunWith;
/**
@@ -40,6 +44,9 @@
@MediumTest
@RunWith(AndroidJUnit4.class)
public class TaplWidgetPickerTest extends AbstractLauncherUiTest {
+ // b/325377690 : To get the log printed where DOWN key event is getting lost from TAPL.
+ @Rule public final TestRule mEnableInputTransportPublisherRule =
+ createEnableInputTransportPublisherRule();
private WidgetsRecyclerView getWidgetsView(Launcher launcher) {
return WidgetsFullSheet.getWidgetsView(launcher);
diff --git a/tests/src/com/android/launcher3/util/rule/ShellCommandRule.java b/tests/src/com/android/launcher3/util/rule/ShellCommandRule.java
index 08953fc..977995e 100644
--- a/tests/src/com/android/launcher3/util/rule/ShellCommandRule.java
+++ b/tests/src/com/android/launcher3/util/rule/ShellCommandRule.java
@@ -21,6 +21,7 @@
import android.content.ComponentName;
import android.content.pm.ActivityInfo;
+import android.text.TextUtils;
import androidx.annotation.Nullable;
import androidx.test.InstrumentationRegistry;
@@ -40,6 +41,9 @@
*/
public class ShellCommandRule implements TestRule {
+ private static final String SETPROP_PREFIX = "setprop";
+ private static final String GETPROP_PREFIX = "getprop";
+ private static final String UNKNOWN = "UNKNOWN";
private final String mCmd;
private final String mRevertCommand;
private final boolean mCheckSuccess;
@@ -62,6 +66,19 @@
return new Statement() {
@Override
public void evaluate() throws Throwable {
+ String revertSetPropCmd = null;
+ if (mCmd.startsWith(SETPROP_PREFIX) && mRevertCommand == null) {
+ // setprop command always follows format : setprop <TAG> <value>
+ // We are stripping out only the TAG here
+ String tag = mCmd.split("\\s")[1];
+ String getpropCmd = GETPROP_PREFIX + " " + tag;
+ String initialValue = UiDevice.getInstance(
+ getInstrumentation()).executeShellCommand(getpropCmd);
+ if (TextUtils.isEmpty(initialValue.trim())) {
+ initialValue = UNKNOWN;
+ }
+ revertSetPropCmd = SETPROP_PREFIX + " " + tag + " " + initialValue;
+ }
final String result =
UiDevice.getInstance(getInstrumentation()).executeShellCommand(mCmd);
if (mCheckSuccess) {
@@ -73,13 +90,15 @@
try {
base.evaluate();
} finally {
- if (mRevertCommand != null) {
+ if (mRevertCommand != null || revertSetPropCmd != null) {
+ String revertCmd =
+ mRevertCommand != null ? mRevertCommand : revertSetPropCmd;
final String revertResult = UiDevice.getInstance(
getInstrumentation()).executeShellCommand(
- mRevertCommand);
+ revertCmd);
if (mCheckSuccess) {
Assert.assertTrue(
- "Failed command: " + mRevertCommand
+ "Failed command: " + revertCmd
+ ", result: " + revertResult,
"Success".equals(result.replaceAll("\\s", "")));
}
@@ -122,4 +141,14 @@
return new ShellCommandRule("settings put global heads_up_notifications_enabled 0",
"settings put global heads_up_notifications_enabled 1");
}
+
+ /**
+ * Enables "InputTransportPublisher" debug flag. This prints the key input events dispatched by
+ * the system server.
+ * adb shell setprop log.tag.InputTransportPublisher DEBUG
+ * See {@link com.android.cts.input.DebugInputRule} for more details.
+ */
+ public static ShellCommandRule createEnableInputTransportPublisherRule() {
+ return new ShellCommandRule("setprop log.tag.InputTransportPublisher DEBUG", null);
+ }
}
diff --git a/tests/src/com/android/launcher3/util/rule/TestToPhoneFileCopier.kt b/tests/src/com/android/launcher3/util/rule/TestToPhoneFileCopier.kt
new file mode 100644
index 0000000..d3516d1
--- /dev/null
+++ b/tests/src/com/android/launcher3/util/rule/TestToPhoneFileCopier.kt
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.util.rule
+
+import androidx.test.platform.app.InstrumentationRegistry
+import java.io.File
+import org.junit.rules.TestRule
+import org.junit.runner.Description
+import org.junit.runners.model.Statement
+
+/** Copy a file from the tests assets folder to the phone. */
+class TestToPhoneFileCopier(
+ val src: String,
+ dest: String,
+ private val removeOnFinish: Boolean = false
+) : TestRule {
+
+ private val dstFile =
+ File(InstrumentationRegistry.getInstrumentation().targetContext.dataDir, dest)
+
+ fun getDst() = dstFile.absolutePath
+
+ fun before() =
+ dstFile.writeBytes(
+ InstrumentationRegistry.getInstrumentation().context.assets.open(src).readBytes()
+ )
+
+ fun after() {
+ if (removeOnFinish) {
+ dstFile.delete()
+ }
+ }
+
+ override fun apply(base: Statement, description: Description): Statement =
+ object : Statement() {
+ override fun evaluate() {
+ before()
+ try {
+ base.evaluate()
+ } finally {
+ after()
+ }
+ }
+ }
+}
diff --git a/tests/src/com/android/launcher3/widget/GeneratedPreviewTest.kt b/tests/src/com/android/launcher3/widget/GeneratedPreviewTest.kt
index 11855e6..460058b 100644
--- a/tests/src/com/android/launcher3/widget/GeneratedPreviewTest.kt
+++ b/tests/src/com/android/launcher3/widget/GeneratedPreviewTest.kt
@@ -12,6 +12,7 @@
import android.platform.test.annotations.RequiresFlagsEnabled
import android.platform.test.flag.junit.CheckFlagsRule
import android.platform.test.flag.junit.DeviceFlagsValueProvider
+import android.view.ContextThemeWrapper
import android.view.LayoutInflater
import android.widget.RemoteViews
import androidx.test.core.app.ApplicationProvider.getApplicationContext
@@ -53,7 +54,14 @@
context = getApplicationContext()
generatedPreview = RemoteViews(context.packageName, generatedPreviewLayout)
widgetCell =
- LayoutInflater.from(ActivityContextWrapper(context))
+ LayoutInflater.from(
+ ActivityContextWrapper(
+ ContextThemeWrapper(
+ context,
+ com.android.launcher3.R.style.WidgetContainerTheme
+ )
+ )
+ )
.inflate(com.android.launcher3.R.layout.widget_cell, null) as WidgetCell
appWidgetProviderInfo =
AppWidgetProviderInfo()
@@ -111,6 +119,18 @@
}
@Test
+ @RequiresFlagsEnabled(FLAG_ENABLE_GENERATED_PREVIEWS)
+ fun widgetItem_hasGeneratedPreview_nullPreview() {
+ appWidgetProviderInfo.generatedPreviewCategories =
+ WIDGET_CATEGORY_HOME_SCREEN or WIDGET_CATEGORY_KEYGUARD
+ createWidgetItem()
+ assertThat(widgetItem.hasGeneratedPreview(WIDGET_CATEGORY_HOME_SCREEN)).isTrue()
+ // loadGeneratedPreview returns null for KEYGUARD, so this should still be false.
+ assertThat(widgetItem.hasGeneratedPreview(WIDGET_CATEGORY_KEYGUARD)).isFalse()
+ assertThat(widgetItem.hasGeneratedPreview(WIDGET_CATEGORY_SEARCHBOX)).isFalse()
+ }
+
+ @Test
@RequiresFlagsDisabled(FLAG_ENABLE_GENERATED_PREVIEWS)
fun widgetItem_hasGeneratedPreview_flagDisabled() {
assertThat(widgetItem.hasGeneratedPreview(WIDGET_CATEGORY_HOME_SCREEN)).isFalse()
diff --git a/tests/src/com/android/launcher3/widget/picker/WidgetImageViewTest.kt b/tests/src/com/android/launcher3/widget/picker/WidgetImageViewTest.kt
new file mode 100644
index 0000000..6e751e0
--- /dev/null
+++ b/tests/src/com/android/launcher3/widget/picker/WidgetImageViewTest.kt
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.widget.picker
+
+import android.content.Context
+import android.graphics.Rect
+import android.graphics.drawable.Drawable
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.MediumTest
+import com.android.launcher3.util.ActivityContextWrapper
+import com.android.launcher3.widget.WidgetImageView
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.spy
+import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.whenever
+
+@MediumTest
+@RunWith(AndroidJUnit4::class)
+class WidgetImageViewTest {
+ private lateinit var context: Context
+ private lateinit var widgetImageView: WidgetImageView
+
+ @Mock private lateinit var testDrawable: Drawable
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+
+ context = ActivityContextWrapper(ApplicationProvider.getApplicationContext())
+ widgetImageView = spy(WidgetImageView(context))
+ }
+
+ @Test
+ fun getBitmapBounds_aspectRatioLargerThanView_scaledByWidth() {
+ // view - 100 x 100
+ whenever(widgetImageView.width).thenReturn(100)
+ whenever(widgetImageView.height).thenReturn(100)
+ // bitmap - 200 x 100
+ whenever(testDrawable.intrinsicWidth).thenReturn(200)
+ whenever(testDrawable.intrinsicHeight).thenReturn(100)
+
+ widgetImageView.drawable = testDrawable
+ val bitmapBounds = widgetImageView.bitmapBounds
+
+ // new scaled width of bitmap is = 100, and height is scaled to 1/2 = 50
+ assertThat(bitmapBounds).isEqualTo(Rect(0, 25, 100, 75))
+ }
+
+ @Test
+ fun getBitmapBounds_aspectRatioSmallerThanView_scaledByHeight() {
+ // view - 100 x 100
+ whenever(widgetImageView.width).thenReturn(100)
+ whenever(widgetImageView.height).thenReturn(100)
+ // bitmap - 100 x 200
+ whenever(testDrawable.intrinsicWidth).thenReturn(100)
+ whenever(testDrawable.intrinsicHeight).thenReturn(200)
+ widgetImageView.drawable = testDrawable
+
+ val bitmapBounds = widgetImageView.bitmapBounds
+
+ // new scaled height of bitmap is = 100, and width is scaled to 1/2 = 50
+ assertThat(bitmapBounds).isEqualTo(Rect(25, 0, 75, 100))
+ }
+
+ @Test
+ fun getBitmapBounds_noScale_returnsOriginalDrawableBounds() {
+ // view - 200 x 100
+ whenever(widgetImageView.width).thenReturn(200)
+ whenever(widgetImageView.height).thenReturn(100)
+ // bitmap - 200 x 100
+ whenever(testDrawable.intrinsicWidth).thenReturn(200)
+ whenever(testDrawable.intrinsicHeight).thenReturn(100)
+
+ widgetImageView.drawable = testDrawable
+ val bitmapBounds = widgetImageView.bitmapBounds
+
+ // no scaling
+ assertThat(bitmapBounds).isEqualTo(Rect(0, 0, 200, 100))
+ }
+}
diff --git a/tests/src/com/android/launcher3/widget/picker/WidgetRecommendationCategoryProviderTest.java b/tests/src/com/android/launcher3/widget/picker/WidgetRecommendationCategoryProviderTest.java
index c807771..60a4cd3 100644
--- a/tests/src/com/android/launcher3/widget/picker/WidgetRecommendationCategoryProviderTest.java
+++ b/tests/src/com/android/launcher3/widget/picker/WidgetRecommendationCategoryProviderTest.java
@@ -23,6 +23,7 @@
import static android.content.pm.ApplicationInfo.CATEGORY_SOCIAL;
import static android.content.pm.ApplicationInfo.CATEGORY_UNDEFINED;
import static android.content.pm.ApplicationInfo.CATEGORY_VIDEO;
+import static android.content.pm.ApplicationInfo.FLAG_INSTALLED;
import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
@@ -30,8 +31,7 @@
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.when;
@@ -41,7 +41,7 @@
import android.content.Context;
import android.content.ContextWrapper;
import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
+import android.content.pm.LauncherApps;
import android.os.Process;
import androidx.test.core.content.pm.ApplicationInfoBuilder;
@@ -70,10 +70,25 @@
public class WidgetRecommendationCategoryProviderTest {
private static final String TEST_PACKAGE = "com.foo.test";
private static final String TEST_APP_NAME = "foo";
- public static final WidgetRecommendationCategory SOCIAL_AND_ENTERTAINMENT_CATEGORY =
+ private static final WidgetRecommendationCategory PRODUCTIVITY =
new WidgetRecommendationCategory(
- R.string.social_and_entertainment_widget_recommendation_category_label,
- /*order=*/4);
+ R.string.productivity_widget_recommendation_category_label,
+ /*order=*/0);
+ private static final WidgetRecommendationCategory NEWS =
+ new WidgetRecommendationCategory(
+ R.string.news_widget_recommendation_category_label, /*order=*/1);
+ private static final WidgetRecommendationCategory SUGGESTED_FOR_YOU =
+ new WidgetRecommendationCategory(
+ R.string.others_widget_recommendation_category_label, /*order=*/4);
+ private static final WidgetRecommendationCategory SOCIAL =
+ new WidgetRecommendationCategory(
+ R.string.social_widget_recommendation_category_label,
+ /*order=*/5);
+ private static final WidgetRecommendationCategory ENTERTAINMENT =
+ new WidgetRecommendationCategory(
+ R.string.entertainment_widget_recommendation_category_label,
+ /*order=*/6);
+
private final ApplicationInfo mTestAppInfo = ApplicationInfoBuilder.newBuilder().setPackageName(
TEST_PACKAGE).setName(TEST_APP_NAME).build();
private Context mContext;
@@ -82,7 +97,7 @@
private WidgetItem mTestWidgetItem;
@Mock
- private PackageManager mPackageManager;
+ private LauncherApps mLauncherApps;
private InvariantDeviceProfile mTestProfile;
@Before
@@ -90,10 +105,12 @@
MockitoAnnotations.initMocks(this);
mContext = new ContextWrapper(getInstrumentation().getTargetContext()) {
@Override
- public PackageManager getPackageManager() {
- return mPackageManager;
+ public Object getSystemService(String name) {
+ return LAUNCHER_APPS_SERVICE.equals(name) ? mLauncherApps : super.getSystemService(
+ name);
}
};
+ mTestAppInfo.flags = FLAG_INSTALLED;
mTestProfile = new InvariantDeviceProfile();
mTestProfile.numRows = 5;
mTestProfile.numColumns = 5;
@@ -103,25 +120,22 @@
@Test
public void getWidgetRecommendationCategory_returnsMappedCategory() throws Exception {
ImmutableMap<Integer, WidgetRecommendationCategory> testCategories = ImmutableMap.of(
- CATEGORY_PRODUCTIVITY, new WidgetRecommendationCategory(
- R.string.productivity_widget_recommendation_category_label,
- /*order=*/
- 0),
- CATEGORY_NEWS, new WidgetRecommendationCategory(
- R.string.news_widget_recommendation_category_label, /*order=*/1),
- CATEGORY_SOCIAL, SOCIAL_AND_ENTERTAINMENT_CATEGORY,
- CATEGORY_AUDIO, SOCIAL_AND_ENTERTAINMENT_CATEGORY,
- CATEGORY_IMAGE, SOCIAL_AND_ENTERTAINMENT_CATEGORY,
- CATEGORY_VIDEO, SOCIAL_AND_ENTERTAINMENT_CATEGORY,
- CATEGORY_UNDEFINED, new WidgetRecommendationCategory(
- R.string.others_widget_recommendation_category_label, /*order=*/5));
+ CATEGORY_PRODUCTIVITY, PRODUCTIVITY,
+ CATEGORY_NEWS, NEWS,
+ CATEGORY_SOCIAL, SOCIAL,
+ CATEGORY_AUDIO, ENTERTAINMENT,
+ CATEGORY_IMAGE, ENTERTAINMENT,
+ CATEGORY_VIDEO, ENTERTAINMENT,
+ CATEGORY_UNDEFINED, SUGGESTED_FOR_YOU);
for (Map.Entry<Integer, WidgetRecommendationCategory> testCategory :
testCategories.entrySet()) {
mTestAppInfo.category = testCategory.getKey();
- when(mPackageManager.getApplicationInfo(anyString(), anyInt())).thenReturn(
- mTestAppInfo);
+ when(mLauncherApps.getApplicationInfo(/*packageName=*/ eq(TEST_PACKAGE),
+ /*flags=*/ eq(0),
+ /*user=*/ eq(Process.myUserHandle())))
+ .thenReturn(mTestAppInfo);
WidgetRecommendationCategory category = Executors.MODEL_EXECUTOR.submit(() ->
new WidgetRecommendationCategoryProvider().getWidgetRecommendationCategory(
diff --git a/tests/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinderTest.java b/tests/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinderTest.java
index 0286279..85fb380 100644
--- a/tests/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinderTest.java
+++ b/tests/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinderTest.java
@@ -29,6 +29,7 @@
import android.content.Context;
import android.graphics.Bitmap;
import android.os.UserHandle;
+import android.view.ContextThemeWrapper;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
@@ -96,7 +97,8 @@
mViewHolderBinder = new WidgetsListTableViewHolderBinder(
mContext,
- LayoutInflater.from(mContext),
+ LayoutInflater.from(new ContextThemeWrapper(mContext,
+ com.android.launcher3.R.style.WidgetContainerTheme)),
mOnIconClickListener,
mOnLongClickListener);
}
diff --git a/tests/src/com/android/launcher3/widget/picker/util/WidgetPreviewContainerSizesTest.kt b/tests/src/com/android/launcher3/widget/picker/util/WidgetPreviewContainerSizesTest.kt
new file mode 100644
index 0000000..040fbf5
--- /dev/null
+++ b/tests/src/com/android/launcher3/widget/picker/util/WidgetPreviewContainerSizesTest.kt
@@ -0,0 +1,154 @@
+/*
+ * 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.util
+
+import android.content.ComponentName
+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.InvariantDeviceProfile
+import com.android.launcher3.LauncherAppState
+import com.android.launcher3.icons.IconCache
+import com.android.launcher3.model.WidgetItem
+import com.android.launcher3.util.ActivityContextWrapper
+import com.android.launcher3.util.WidgetUtils.createAppWidgetProviderInfo
+import com.android.launcher3.widget.LauncherAppWidgetProviderInfo
+import com.google.common.truth.Truth.assertWithMessage
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class WidgetPreviewContainerSizesTest {
+ private lateinit var context: Context
+ private lateinit var deviceProfile: DeviceProfile
+ private lateinit var testInvariantProfile: InvariantDeviceProfile
+
+ @Mock private lateinit var iconCache: IconCache
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ context = ActivityContextWrapper(ApplicationProvider.getApplicationContext())
+ testInvariantProfile = LauncherAppState.getIDP(context)
+ deviceProfile = testInvariantProfile.getDeviceProfile(context).copy(context)
+ }
+
+ @Test
+ fun widgetPreviewContainerSize_forItem_returnsCorrectContainerSize() {
+ val testSizes = getTestSizes(deviceProfile)
+ val expectedPreviewContainers = testSizes.values.toList()
+
+ for ((index, widgetSize) in testSizes.keys.withIndex()) {
+ val widgetItem = createWidgetItem(widgetSize, context, testInvariantProfile, iconCache)
+
+ assertWithMessage("size for $widgetSize should be: ${expectedPreviewContainers[index]}")
+ .that(WidgetPreviewContainerSize.forItem(widgetItem, deviceProfile))
+ .isEqualTo(expectedPreviewContainers[index])
+ }
+ }
+
+ companion object {
+ private const val TEST_PACKAGE = "com.google.test"
+
+ private val HANDHELD_TEST_SIZES: Map<Point, WidgetPreviewContainerSize> =
+ mapOf(
+ // 1x1
+ Point(1, 1) to WidgetPreviewContainerSize(1, 1),
+ // 2x1
+ Point(2, 1) to WidgetPreviewContainerSize(2, 1),
+ Point(3, 1) to WidgetPreviewContainerSize(2, 1),
+ // 4x1
+ Point(4, 1) to WidgetPreviewContainerSize(4, 1),
+ // 2x2
+ Point(2, 2) to WidgetPreviewContainerSize(2, 2),
+ Point(3, 3) to WidgetPreviewContainerSize(2, 2),
+ Point(3, 2) to WidgetPreviewContainerSize(2, 2),
+ // 2x3
+ Point(2, 3) to WidgetPreviewContainerSize(2, 3),
+ Point(3, 4) to WidgetPreviewContainerSize(2, 3),
+ Point(3, 5) to WidgetPreviewContainerSize(2, 3),
+ // 4x2
+ Point(4, 2) to WidgetPreviewContainerSize(4, 2),
+ // 4x3
+ Point(4, 3) to WidgetPreviewContainerSize(4, 3),
+ Point(4, 4) to WidgetPreviewContainerSize(4, 3),
+ )
+
+ private val TABLET_TEST_SIZES: Map<Point, WidgetPreviewContainerSize> =
+ mapOf(
+ // 1x1
+ Point(1, 1) to WidgetPreviewContainerSize(1, 1),
+ // 2x1
+ Point(2, 1) to WidgetPreviewContainerSize(2, 1),
+ // 3x1
+ Point(3, 1) to WidgetPreviewContainerSize(3, 1),
+ Point(4, 1) to WidgetPreviewContainerSize(3, 1),
+ // 2x2
+ Point(2, 2) to WidgetPreviewContainerSize(2, 2),
+ // 2x3
+ Point(2, 3) to WidgetPreviewContainerSize(2, 3),
+ // 3x2
+ Point(3, 2) to WidgetPreviewContainerSize(3, 2),
+ Point(4, 2) to WidgetPreviewContainerSize(3, 2),
+ Point(5, 2) to WidgetPreviewContainerSize(3, 2),
+ // 3x3
+ Point(3, 3) to WidgetPreviewContainerSize(3, 3),
+ Point(4, 4) to WidgetPreviewContainerSize(3, 3),
+ // 3x4
+ Point(5, 4) to WidgetPreviewContainerSize(3, 4),
+ Point(3, 4) to WidgetPreviewContainerSize(3, 4),
+ Point(5, 5) to WidgetPreviewContainerSize(3, 4),
+ Point(6, 4) to WidgetPreviewContainerSize(3, 4),
+ Point(6, 5) to WidgetPreviewContainerSize(3, 4),
+ )
+
+ private fun getTestSizes(dp: DeviceProfile) =
+ if (dp.isTablet && !dp.isTwoPanels) {
+ TABLET_TEST_SIZES
+ } else {
+ HANDHELD_TEST_SIZES
+ }
+
+ private fun createWidgetItem(
+ widgetSize: Point,
+ context: Context,
+ invariantDeviceProfile: InvariantDeviceProfile,
+ iconCache: IconCache
+ ): WidgetItem {
+ val providerInfo =
+ createAppWidgetProviderInfo(
+ ComponentName.createRelative(
+ TEST_PACKAGE,
+ /*cls=*/ ".WidgetProvider_" + widgetSize.x + "x" + widgetSize.y
+ )
+ )
+ val widgetInfo =
+ LauncherAppWidgetProviderInfo.fromProviderInfo(context, providerInfo).apply {
+ spanX = widgetSize.x
+ spanY = widgetSize.y
+ }
+ return WidgetItem(widgetInfo, invariantDeviceProfile, iconCache, context)
+ }
+ }
+}
diff --git a/tests/src/com/android/launcher3/widget/picker/util/WidgetsTableUtilsTest.java b/tests/src/com/android/launcher3/widget/picker/util/WidgetsTableUtilsTest.java
index 2c5a396..b2cb266 100644
--- a/tests/src/com/android/launcher3/widget/picker/util/WidgetsTableUtilsTest.java
+++ b/tests/src/com/android/launcher3/widget/picker/util/WidgetsTableUtilsTest.java
@@ -63,6 +63,7 @@
private static final String TEST_PACKAGE = "com.google.test";
private static final int SPACE_SIZE = 10;
+ // Note - actual widget size includes SPACE_SIZE (border) + cell padding.
private static final int CELL_SIZE = 50;
private static final int NUM_OF_COLS = 5;
private static final int NUM_OF_ROWS = 5;
@@ -105,7 +106,7 @@
@Test
- public void groupWidgetItemsIntoTableWithReordering_widgetsOnly_maxSpanPxPerRow220_cellPadding0_shouldGroupWidgetsInTable() {
+ public void groupWithReordering_widgetsOnly_maxSpanPxPerRow220_cellPadding0() {
List<WidgetItem> widgetItems = List.of(mWidget4x4, mWidget2x3, mWidget1x1, mWidget2x4,
mWidget2x2);
@@ -113,17 +114,20 @@
WidgetsTableUtils.groupWidgetItemsUsingRowPxWithReordering(widgetItems, mContext,
mTestDeviceProfile, 220, 0);
- // Row 0: 1x1(50px), 2x2(110px)
- // Row 1: 2x3(110px), 2x4(110px)
- // Row 2: 4x4(230px)
- assertThat(widgetItemInTable).hasSize(3);
- assertThat(widgetItemInTable.get(0)).containsExactly(mWidget1x1, mWidget2x2);
- assertThat(widgetItemInTable.get(1)).containsExactly(mWidget2x3, mWidget2x4);
- assertThat(widgetItemInTable.get(2)).containsExactly(mWidget4x4);
+ // With reordering, rows displayed in order of increasing size.
+ // Row 0: 1x1(50px)
+ // Row 1: 2x2(in a 2x2 container - 110px)
+ // Row 2: 2x3(in a 2x3 container - 110px), 2x4(in a 2x3 container - 110px)
+ // Row 3: 4x4(in a 3x3 container in tablet - 170px; 4x3 on phone - 230px)
+ assertThat(widgetItemInTable).hasSize(4);
+ assertThat(widgetItemInTable.get(0)).containsExactly(mWidget1x1);
+ assertThat(widgetItemInTable.get(1)).containsExactly(mWidget2x2);
+ assertThat(widgetItemInTable.get(2)).containsExactly(mWidget2x3, mWidget2x4);
+ assertThat(widgetItemInTable.get(3)).containsExactly(mWidget4x4);
}
@Test
- public void groupWidgetItemsIntoTableWithReordering_widgetsOnly_maxSpanPxPerRow220_cellPadding10_shouldGroupWidgetsInTable() {
+ public void groupWithReordering_widgetsOnly_maxSpanPxPerRow220_cellPadding10() {
List<WidgetItem> widgetItems = List.of(mWidget4x4, mWidget2x3, mWidget1x1, mWidget2x4,
mWidget2x2);
@@ -131,9 +135,13 @@
WidgetsTableUtils.groupWidgetItemsUsingRowPxWithReordering(widgetItems, mContext,
mTestDeviceProfile, 220, 10);
- // Row 0: 1x1(50px), 2x2(110px)
- // Row 1: 2x3(110px), 2x4(110px)
- // Row 2: 4x4(230px)
+ // With reordering, but space taken up by cell padding, so, no grouping (even if 2x2 and 2x3
+ // use same preview container).
+ // Row 0: 1x1(50px)
+ // Row 1: 2x2(in a 2x2 container: 130px)
+ // Row 2: 2x3(in a 2x3 container: 130px)
+ // Row 3: 2x4(in a 2x3 container: 130px)
+ // Row 4: 4x4(in a 3x3 container in tablet - 190px; 4x3 on phone - 250px)
assertThat(widgetItemInTable).hasSize(5);
assertThat(widgetItemInTable.get(0)).containsExactly(mWidget1x1);
assertThat(widgetItemInTable.get(1)).containsExactly(mWidget2x2);
@@ -143,7 +151,29 @@
}
@Test
- public void groupWidgetItemsIntoTableWithReordering_widgetsOnly_maxSpanPxPerRow350_cellPadding0_shouldGroupWidgetsInTable() {
+ public void groupWithReordering_widgetsOnly_maxSpanPxPerRow260_cellPadding10() {
+ List<WidgetItem> widgetItems = List.of(mWidget4x4, mWidget2x3, mWidget1x1, mWidget2x4,
+ mWidget2x2);
+
+ List<ArrayList<WidgetItem>> widgetItemInTable =
+ WidgetsTableUtils.groupWidgetItemsUsingRowPxWithReordering(widgetItems, mContext,
+ mTestDeviceProfile, 260, 10);
+
+ // With reordering, even with cellPadding, enough space to group 2x3 and 2x4 (which also use
+ // same container)
+ // Row 0: 1x1(50px)
+ // Row 1: 2x2(in a 2x2 container: 130px)
+ // Row 2: 2x3(in a 2x3 container: 130px), 2x4(in a 2x3 container: 130px)
+ // Row 3: 4x4(in a 3x3 container in tablet - 190px; 4x3 on phone - 250px)
+ assertThat(widgetItemInTable).hasSize(4);
+ assertThat(widgetItemInTable.get(0)).containsExactly(mWidget1x1);
+ assertThat(widgetItemInTable.get(1)).containsExactly(mWidget2x2);
+ assertThat(widgetItemInTable.get(2)).containsExactly(mWidget2x3, mWidget2x4);
+ assertThat(widgetItemInTable.get(3)).containsExactly(mWidget4x4);
+ }
+
+ @Test
+ public void groupWithReordering_widgetsOnly_maxSpanPxPerRow350_cellPadding0() {
List<WidgetItem> widgetItems = List.of(mWidget4x4, mWidget2x3, mWidget1x1, mWidget2x4,
mWidget2x2);
@@ -151,17 +181,20 @@
WidgetsTableUtils.groupWidgetItemsUsingRowPxWithReordering(widgetItems, mContext,
mTestDeviceProfile, 350, 0);
- // Row 0: 1x1(50px), 2x2(110px), 2x3(110px)
- // Row 1: 2x4(110px)
- // Row 2: 4x4(230px)
- assertThat(widgetItemInTable).hasSize(3);
- assertThat(widgetItemInTable.get(0)).containsExactly(mWidget1x1, mWidget2x2, mWidget2x3);
- assertThat(widgetItemInTable.get(1)).containsExactly(mWidget2x4);
- assertThat(widgetItemInTable.get(2)).containsExactly(mWidget4x4);
+ // With reordering, rows displayed in order of increasing size.
+ // Row 0: 1x1(50px)
+ // Row 1: 2x2(in a 2x2 container: 110px)
+ // Row 2: 2x3(in a 2x3 container: 110px), 2x4(in a 2x3 container: 110px)
+ // Row 3: 4x4(in a 3x3 container in tablet - 170px; 4x3 on phone - 230px)
+ assertThat(widgetItemInTable).hasSize(4);
+ assertThat(widgetItemInTable.get(0)).containsExactly(mWidget1x1);
+ assertThat(widgetItemInTable.get(1)).containsExactly(mWidget2x2);
+ assertThat(widgetItemInTable.get(2)).containsExactly(mWidget2x3, mWidget2x4);
+ assertThat(widgetItemInTable.get(3)).containsExactly(mWidget4x4);
}
@Test
- public void groupWidgetItemsIntoTableWithReordering_mixItems_maxSpanPxPerRow350_cellPadding0_shouldGroupWidgetsInTable() {
+ public void groupWithReordering_mixItems_maxSpanPxPerRow350_cellPadding0() {
List<WidgetItem> widgetItems = List.of(mWidget4x4, mShortcut3, mWidget2x3, mShortcut1,
mWidget1x1, mShortcut2, mWidget2x4, mWidget2x2);
@@ -169,19 +202,22 @@
WidgetsTableUtils.groupWidgetItemsUsingRowPxWithReordering(widgetItems, mContext,
mTestDeviceProfile, 350, 0);
- // Row 0: 1x1(50px), 2x2(110px), 2x3(110px)
- // Row 1: 2x4(110px),
- // Row 2: 4x4(230px)
- // Row 3: shortcut3(50px), shortcut1(50px), shortcut2(50px)
- assertThat(widgetItemInTable).hasSize(4);
- assertThat(widgetItemInTable.get(0)).containsExactly(mWidget1x1, mWidget2x2, mWidget2x3);
- assertThat(widgetItemInTable.get(1)).containsExactly(mWidget2x4);
- assertThat(widgetItemInTable.get(2)).containsExactly(mWidget4x4);
- assertThat(widgetItemInTable.get(3)).containsExactly(mShortcut3, mShortcut2, mShortcut1);
+ // With reordering - rows displays in order of increasing size:
+ // Row 0: 1x1(50px)
+ // Row 1: 2x2(110px)
+ // Row 2: 2x3 (in a 2x3 container 110px), 2x4 (in a 2x3 container 110px)
+ // Row 3: 4x4 (in a 3x3 container in tablet - 170px; 4x3 on phone - 230px)
+ // Row 4: shortcut3, shortcut1, shortcut2 (shortcuts are always displayed at bottom)
+ assertThat(widgetItemInTable).hasSize(5);
+ assertThat(widgetItemInTable.get(0)).containsExactly(mWidget1x1);
+ assertThat(widgetItemInTable.get(1)).containsExactly(mWidget2x2);
+ assertThat(widgetItemInTable.get(2)).containsExactly(mWidget2x3, mWidget2x4);
+ assertThat(widgetItemInTable.get(3)).containsExactly(mWidget4x4);
+ assertThat(widgetItemInTable.get(4)).containsExactly(mShortcut3, mShortcut2, mShortcut1);
}
@Test
- public void groupWidgetItemsIntoTableWithoutReordering_maxSpanPxPerRow220_cellPadding0_shouldMaintainTheOrder() {
+ public void groupWithoutReordering_maxSpanPxPerRow220_cellPadding0() {
List<WidgetItem> widgetItems =
List.of(mWidget4x4, mWidget2x3, mWidget1x1, mWidget2x4, mWidget2x2);
@@ -189,13 +225,19 @@
WidgetsTableUtils.groupWidgetItemsUsingRowPxWithoutReordering(widgetItems, mContext,
mTestDeviceProfile, 220, 0);
- // Row 0: 4x4(230px)
- // Row 1: 2x3(110px), 1x1(50px)
- // Row 2: 2x4(110px), 2x2(110px)
- assertThat(widgetItemInTable).hasSize(3);
+ // Without reordering, widgets are grouped only if the next one fits and uses same preview
+ // container:
+ // Row 0: 4x4(in a 3x3 container in tablet - 170px; 4x3 on phone - 230px)
+ // Row 1: 2x3(in a 2x3 container - 110px)
+ // Row 2: 1x1(50px)
+ // Row 3: 2x4(in a 2x3 container - 110px)
+ // Row 4: 2x2(in a 2x2 container - 110px)
+ assertThat(widgetItemInTable).hasSize(5);
assertThat(widgetItemInTable.get(0)).containsExactly(mWidget4x4);
- assertThat(widgetItemInTable.get(1)).containsExactly(mWidget2x3, mWidget1x1);
- assertThat(widgetItemInTable.get(2)).containsExactly(mWidget2x4, mWidget2x2);
+ assertThat(widgetItemInTable.get(1)).containsExactly(mWidget2x3);
+ assertThat(widgetItemInTable.get(2)).containsExactly(mWidget1x1);
+ assertThat(widgetItemInTable.get(3)).containsExactly(mWidget2x4);
+ assertThat(widgetItemInTable.get(4)).containsExactly(mWidget2x2);
}
private void initDP() {
diff --git a/tests/tapl/com/android/launcher3/tapl/AllApps.java b/tests/tapl/com/android/launcher3/tapl/AllApps.java
index 9591891..847dc4b 100644
--- a/tests/tapl/com/android/launcher3/tapl/AllApps.java
+++ b/tests/tapl/com/android/launcher3/tapl/AllApps.java
@@ -56,9 +56,6 @@
private static final String BOTTOM_SHEET_RES_ID = "bottom_sheet_background";
private static final String FAST_SCROLLER_RES_ID = "fast_scroller";
-
- private static final Pattern EVENT_ALT_ESC_DOWN = Pattern.compile(
- "Key event: KeyEvent.*?action=ACTION_DOWN.*?keyCode=KEYCODE_ESCAPE.*?metaState=0");
private static final Pattern EVENT_ALT_ESC_UP = Pattern.compile(
"Key event: KeyEvent.*?action=ACTION_UP.*?keyCode=KEYCODE_ESCAPE.*?metaState=0");
@@ -407,7 +404,6 @@
/** Presses the esc key to dismiss AllApps. */
public void dismissByEscKey() {
try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) {
- mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_ALT_ESC_DOWN);
mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_ALT_ESC_UP);
mLauncher.runToState(
() -> mLauncher.getDevice().pressKeyCode(KEYCODE_ESCAPE),
diff --git a/tests/tapl/com/android/launcher3/tapl/BaseOverview.java b/tests/tapl/com/android/launcher3/tapl/BaseOverview.java
index 4f20c57..3f70bb9 100644
--- a/tests/tapl/com/android/launcher3/tapl/BaseOverview.java
+++ b/tests/tapl/com/android/launcher3/tapl/BaseOverview.java
@@ -45,9 +45,6 @@
*/
public class BaseOverview extends LauncherInstrumentation.VisibleContainer {
protected static final String TASK_RES_ID = "task";
-
- private static final Pattern EVENT_ALT_ESC_DOWN = Pattern.compile(
- "Key event: KeyEvent.*?action=ACTION_DOWN.*?keyCode=KEYCODE_ESCAPE.*?metaState=0");
private static final Pattern EVENT_ALT_ESC_UP = Pattern.compile(
"Key event: KeyEvent.*?action=ACTION_UP.*?keyCode=KEYCODE_ESCAPE.*?metaState=0");
private static final Pattern EVENT_ENTER_DOWN = Pattern.compile(
@@ -413,7 +410,6 @@
*/
public Workspace dismissByEscKey() {
try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) {
- mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_ALT_ESC_DOWN);
mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_ALT_ESC_UP);
mLauncher.runToState(
() -> mLauncher.getDevice().pressKeyCode(KEYCODE_ESCAPE),
@@ -432,7 +428,6 @@
*/
public LaunchedAppState launchFocusedTaskByEnterKey(@NonNull String expectedPackageName) {
try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) {
- mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_ENTER_DOWN);
mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_ENTER_UP);
mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, TASK_START_EVENT);
diff --git a/tests/tapl/com/android/launcher3/tapl/KeyboardQuickSwitch.java b/tests/tapl/com/android/launcher3/tapl/KeyboardQuickSwitch.java
index 5ef82ca..7ff55fe 100644
--- a/tests/tapl/com/android/launcher3/tapl/KeyboardQuickSwitch.java
+++ b/tests/tapl/com/android/launcher3/tapl/KeyboardQuickSwitch.java
@@ -37,15 +37,9 @@
private static final Pattern EVENT_ALT_TAB_UP = Pattern.compile(
"KeyboardQuickSwitchView key event: KeyEvent.*?action=ACTION_UP.*?keyCode=KEYCODE_TAB"
+ ".*?metaState=META_ALT_ON");
- private static final Pattern EVENT_ALT_SHIFT_TAB_DOWN = Pattern.compile(
- "KeyboardQuickSwitchView key event: KeyEvent.*?action=ACTION_DOWN.*?keyCode=KEYCODE_TAB"
- + ".*?metaState=META_ALT_ON|META_SHIFT_ON");
private static final Pattern EVENT_ALT_SHIFT_TAB_UP = Pattern.compile(
"KeyboardQuickSwitchView key event: KeyEvent.*?action=ACTION_UP.*?keyCode=KEYCODE_TAB"
+ ".*?metaState=META_ALT_ON|META_SHIFT_ON");
- private static final Pattern EVENT_ALT_ESC_DOWN = Pattern.compile(
- "KeyboardQuickSwitchView key event: KeyEvent.*?action=ACTION_DOWN"
- + ".*?keyCode=KEYCODE_ESCAPE.*?metaState=META_ALT_ON");
private static final Pattern EVENT_ALT_ESC_UP = Pattern.compile(
"KeyboardQuickSwitchView key event: KeyEvent.*?action=ACTION_UP"
+ ".*?keyCode=KEYCODE_ESCAPE.*?metaState=META_ALT_ON");
@@ -87,8 +81,6 @@
"want to move keyboard quick switch focus forward");
LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) {
mLauncher.waitForLauncherObject(KEYBOARD_QUICK_SWITCH_RES_ID);
-
- mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_ALT_TAB_DOWN);
mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_ALT_TAB_UP);
mLauncher.assertTrue("Failed to press alt+tab",
mLauncher.getDevice().pressKeyCode(
@@ -122,7 +114,6 @@
LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) {
mLauncher.waitForLauncherObject(KEYBOARD_QUICK_SWITCH_RES_ID);
- mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_ALT_SHIFT_TAB_DOWN);
mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_ALT_SHIFT_TAB_UP);
mLauncher.assertTrue("Failed to press alt+shift+tab",
mLauncher.getDevice().pressKeyCode(
@@ -150,7 +141,6 @@
LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) {
mLauncher.waitForLauncherObject(KEYBOARD_QUICK_SWITCH_RES_ID);
- mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_ALT_ESC_DOWN);
mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_ALT_ESC_UP);
mLauncher.assertTrue("Failed to press alt+tab",
mLauncher.getDevice().pressKeyCode(
diff --git a/tests/tapl/com/android/launcher3/tapl/LaunchedAppState.java b/tests/tapl/com/android/launcher3/tapl/LaunchedAppState.java
index b5414b7..3f96999 100644
--- a/tests/tapl/com/android/launcher3/tapl/LaunchedAppState.java
+++ b/tests/tapl/com/android/launcher3/tapl/LaunchedAppState.java
@@ -26,11 +26,11 @@
import static com.android.launcher3.testing.shared.TestProtocol.REQUEST_TASKBAR_FROM_NAV_THRESHOLD;
import static com.android.launcher3.testing.shared.TestProtocol.SUCCESSFUL_GESTURE_MISMATCH_EVENTS;
import static com.android.launcher3.testing.shared.TestProtocol.TEST_INFO_RESPONSE_FIELD;
-import static com.android.launcher3.testing.shared.TestProtocol.testLogD;
import android.graphics.Point;
import android.graphics.Rect;
import android.os.SystemClock;
+import android.util.Log;
import android.view.InputDevice;
import android.view.MotionEvent;
import android.view.ViewConfiguration;
@@ -141,7 +141,7 @@
return new Taskbar(mLauncher);
} finally {
- testLogD(SUCCESSFUL_GESTURE_MISMATCH_EVENTS,
+ Log.d(SUCCESSFUL_GESTURE_MISMATCH_EVENTS,
"swipeUpToUnstashTaskbar: completed gesture");
mLauncher.getTestInfo(REQUEST_DISABLE_BLOCK_TIMEOUT);
}
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index 70a5336..0e523c3 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -114,9 +114,6 @@
static final Pattern EVENT_PILFER_POINTERS = Pattern.compile("pilferPointers");
static final Pattern EVENT_START = Pattern.compile("start:");
-
- private static final Pattern EVENT_KEY_BACK_DOWN =
- getKeyEventPattern("ACTION_DOWN", "KEYCODE_BACK");
private static final Pattern EVENT_KEY_BACK_UP =
getKeyEventPattern("ACTION_UP", "KEYCODE_BACK");
private static final Pattern EVENT_ON_BACK_INVOKED = Pattern.compile("onBackInvoked");
@@ -405,6 +402,11 @@
.getBoolean(TestProtocol.TEST_INFO_RESPONSE_FIELD);
}
+ private boolean isPredictiveBackSwipeEnabled() {
+ return getTestInfo(TestProtocol.REQUEST_IS_PREDICTIVE_BACK_SWIPE_ENABLED)
+ .getBoolean(TestProtocol.TEST_INFO_RESPONSE_FIELD);
+ }
+
public boolean isTaskbarNavbarUnificationEnabled() {
return getTestInfo(TestProtocol.REQUEST_ENABLE_TASKBAR_NAVBAR_UNIFICATION)
.getBoolean(TestProtocol.TEST_INFO_RESPONSE_FIELD);
@@ -1214,11 +1216,9 @@
waitForNavigationUiObject("back").click();
}
if (launcherVisible) {
- if (InstrumentationRegistry.getTargetContext().getApplicationInfo()
- .isOnBackInvokedCallbackEnabled()) {
+ if (isPredictiveBackSwipeEnabled()) {
expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_ON_BACK_INVOKED);
} else {
- expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_KEY_BACK_DOWN);
expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_KEY_BACK_UP);
}
}
@@ -2452,7 +2452,7 @@
int bottomBound = Math.min(
containerBounds.bottom,
getRealDisplaySize().y - systemGestureRegion.bottom);
- int y = (bottomBound - containerBounds.top) / 2;
+ int y = (bottomBound + containerBounds.top) / 2;
// Do not tap in the status bar.
y = Math.max(y, systemGestureRegion.top);
diff --git a/tests/tapl/com/android/launcher3/tapl/OverviewTask.java b/tests/tapl/com/android/launcher3/tapl/OverviewTask.java
index afe5722..99da5c3 100644
--- a/tests/tapl/com/android/launcher3/tapl/OverviewTask.java
+++ b/tests/tapl/com/android/launcher3/tapl/OverviewTask.java
@@ -19,10 +19,13 @@
import static com.android.launcher3.tapl.OverviewTask.OverviewSplitTask.DEFAULT;
import static com.android.launcher3.tapl.OverviewTask.OverviewSplitTask.SPLIT_BOTTOM_OR_RIGHT;
import static com.android.launcher3.tapl.OverviewTask.OverviewSplitTask.SPLIT_TOP_OR_LEFT;
+import static com.android.launcher3.testing.shared.TestProtocol.SUCCESSFUL_GESTURE_MISMATCH_EVENTS;
import android.graphics.Rect;
+import android.util.Log;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import androidx.test.uiautomator.By;
import androidx.test.uiautomator.BySelector;
import androidx.test.uiautomator.UiObject2;
@@ -219,6 +222,7 @@
return new LaunchedAppState(mLauncher);
}
} else {
+ Log.d(SUCCESSFUL_GESTURE_MISMATCH_EVENTS, "TaskView.launchTaskAnimated");
mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, TASK_START_EVENT);
return new LaunchedAppState(mLauncher);
}
@@ -256,6 +260,23 @@
}
/**
+ * Returns whether the given String is contained in this Task's contentDescription. Also returns
+ * true if both Strings are null.
+ *
+ * TODO(b/326565120): remove Nullable support once the bug causing it to be null is fixed.
+ */
+ public boolean containsContentDescription(@Nullable String expected) {
+ String actual = mTask.getContentDescription();
+ if (actual == null && expected == null) {
+ return true;
+ }
+ if (actual == null || expected == null) {
+ return false;
+ }
+ return actual.contains(expected);
+ }
+
+ /**
* Enum used to specify which task is retrieved when it is a split task.
*/
public enum OverviewSplitTask {
diff --git a/tests/tapl/com/android/launcher3/tapl/SelectModeButtons.java b/tests/tapl/com/android/launcher3/tapl/SelectModeButtons.java
index e2bc17b..6d2631f 100644
--- a/tests/tapl/com/android/launcher3/tapl/SelectModeButtons.java
+++ b/tests/tapl/com/android/launcher3/tapl/SelectModeButtons.java
@@ -33,9 +33,6 @@
public class SelectModeButtons {
private final UiObject2 mSelectModeButtons;
private final LauncherInstrumentation mLauncher;
-
- private static final Pattern EVENT_ALT_ESC_DOWN = Pattern.compile(
- "Key event: KeyEvent.*?action=ACTION_DOWN.*?keyCode=KEYCODE_ESCAPE.*?metaState=0");
private static final Pattern EVENT_ALT_ESC_UP = Pattern.compile(
"Key event: KeyEvent.*?action=ACTION_UP.*?keyCode=KEYCODE_ESCAPE.*?metaState=0");
@@ -69,7 +66,6 @@
@NonNull
public Overview dismissByEscKey() {
try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) {
- mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_ALT_ESC_DOWN);
mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_ALT_ESC_UP);
mLauncher.runToState(
() -> mLauncher.getDevice().pressKeyCode(KEYCODE_ESCAPE),
diff --git a/tests/tapl/com/android/launcher3/tapl/Workspace.java b/tests/tapl/com/android/launcher3/tapl/Workspace.java
index 4e92634..d176136 100644
--- a/tests/tapl/com/android/launcher3/tapl/Workspace.java
+++ b/tests/tapl/com/android/launcher3/tapl/Workspace.java
@@ -66,10 +66,6 @@
private static final String DROP_BAR_RES_ID = "drop_target_bar";
private static final String DELETE_TARGET_TEXT_ID = "delete_target_text";
private static final String UNINSTALL_TARGET_TEXT_ID = "uninstall_target_text";
-
- static final Pattern EVENT_CTRL_W_DOWN = Pattern.compile(
- "Key event: KeyEvent.*?action=ACTION_DOWN.*?keyCode=KEYCODE_W"
- + ".*?metaState=META_CTRL_ON");
static final Pattern EVENT_CTRL_W_UP = Pattern.compile(
"Key event: KeyEvent.*?action=ACTION_UP.*?keyCode=KEYCODE_W"
+ ".*?metaState=META_CTRL_ON");
@@ -822,7 +818,6 @@
public Widgets openAllWidgets() {
try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) {
verifyActiveContainer();
- mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_CTRL_W_DOWN);
mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_CTRL_W_UP);
mLauncher.getDevice().pressKeyCode(KeyEvent.KEYCODE_W, KeyEvent.META_CTRL_ON);
try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer("pressed Ctrl+W")) {