Merge "Respect accessibility animation guidelines in AllSetActivity" into main
diff --git a/AndroidManifest-common.xml b/AndroidManifest-common.xml
index fe57da1..46f0e41 100644
--- a/AndroidManifest-common.xml
+++ b/AndroidManifest-common.xml
@@ -131,13 +131,11 @@
android:writePermission="${applicationId}.permission.WRITE_SETTINGS"
android:readPermission="${applicationId}.permission.READ_SETTINGS" />
- <!--
- The content provider for exposing various launcher grid options.
- TODO: Add proper permissions
- -->
+ <!-- The content provider for exposing various launcher grid options. -->
<provider
android:name="com.android.launcher3.graphics.LauncherCustomizationProvider"
android:authorities="${applicationId}.grid_control"
+ android:permission="android.permission.BIND_WALLPAPER"
android:exported="true" />
<!--
diff --git a/quickstep/res/layout/task_thumbnail_view_header.xml b/quickstep/res/layout/task_thumbnail_view_header.xml
index ecc1559..70e4a42 100644
--- a/quickstep/res/layout/task_thumbnail_view_header.xml
+++ b/quickstep/res/layout/task_thumbnail_view_header.xml
@@ -18,6 +18,7 @@
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
+ android:id="@+id/task_thumbnail_view_header"
android:background="@drawable/task_thumbnail_header_bg">
<androidx.constraintlayout.widget.ConstraintLayout
@@ -61,6 +62,7 @@
android:layout_marginStart="@dimen/task_thumbnail_header_margin_between_views"
android:src="@drawable/task_header_close_button"
android:tint="@android:color/darker_gray"
+ android:background="@null"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
diff --git a/quickstep/res/values-fr-rCA/strings.xml b/quickstep/res/values-fr-rCA/strings.xml
index e7619c2..3201886 100644
--- a/quickstep/res/values-fr-rCA/strings.xml
+++ b/quickstep/res/values-fr-rCA/strings.xml
@@ -90,7 +90,7 @@
<string name="allset_title" msgid="5021126669778966707">"Tout est prêt!"</string>
<string name="allset_hint" msgid="459504134589971527">"Balayez l\'écran vers le haut pour accéder à l\'écran d\'accueil"</string>
<string name="allset_button_hint" msgid="2395219947744706291">"Toucher le bouton d\'accueil pour passer sur votre écran d\'accueil"</string>
- <string name="allset_description_generic" msgid="5385500062202019855">"Vous êtes maintenant prêt à utiliser votre <xliff:g id="DEVICE">%1$s</xliff:g>"</string>
+ <string name="allset_description_generic" msgid="5385500062202019855">"Vous êtes maintenant prêt à utiliser votre <xliff:g id="DEVICE">%1$s</xliff:g>."</string>
<string name="default_device_name" msgid="6660656727127422487">"appareil"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Paramètres de navigation du système"</annotation></string>
<string name="action_share" msgid="2648470652637092375">"Partager"</string>
diff --git a/quickstep/res/values-uk/strings.xml b/quickstep/res/values-uk/strings.xml
index dab58d7..2313a07 100644
--- a/quickstep/res/values-uk/strings.xml
+++ b/quickstep/res/values-uk/strings.xml
@@ -92,7 +92,7 @@
<string name="allset_button_hint" msgid="2395219947744706291">"Натисніть кнопку головного екрана, щоб відкрити його"</string>
<string name="allset_description_generic" msgid="5385500062202019855">"Тепер ви можете використовувати <xliff:g id="DEVICE">%1$s</xliff:g>"</string>
<string name="default_device_name" msgid="6660656727127422487">"пристрій"</string>
- <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Системні налаштування навігації"</annotation></string>
+ <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Налаштування навігації в системі"</annotation></string>
<string name="action_share" msgid="2648470652637092375">"Поділитися"</string>
<string name="action_screenshot" msgid="8171125848358142917">"Знімок екрана"</string>
<string name="action_split" msgid="2098009717623550676">"Розділити"</string>
diff --git a/quickstep/res/values-uz/strings.xml b/quickstep/res/values-uz/strings.xml
index 1cd2e87..06ca97a 100644
--- a/quickstep/res/values-uz/strings.xml
+++ b/quickstep/res/values-uz/strings.xml
@@ -90,7 +90,7 @@
<string name="allset_title" msgid="5021126669778966707">"Hammasi tayyor!"</string>
<string name="allset_hint" msgid="459504134589971527">"Boshiga qaytish uchun tepaga suring"</string>
<string name="allset_button_hint" msgid="2395219947744706291">"Bosh ekranga oʻtish uchun bosh ekran tugmasini bosing"</string>
- <string name="allset_description_generic" msgid="5385500062202019855">"<xliff:g id="DEVICE">%1$s</xliff:g> xizmatga tayyor"</string>
+ <string name="allset_description_generic" msgid="5385500062202019855">"Sizning <xliff:g id="DEVICE">%1$s</xliff:g> xizmatga tayyor"</string>
<string name="default_device_name" msgid="6660656727127422487">"qurilma"</string>
<string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Tizim navigatsiya sozlamalari"</annotation></string>
<string name="action_share" msgid="2648470652637092375">"Ulashish"</string>
diff --git a/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.kt b/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.kt
index 6ee43ff..138f40a 100644
--- a/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.kt
+++ b/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.kt
@@ -152,6 +152,20 @@
}
}
+ /**
+ * Returns the ID of the active desk (if any) on the display whose ID is [displayId], or
+ * [INACTIVE_DESK_ID] if no desk is currently active or the multiple desks feature is disabled.
+ */
+ fun getActiveDeskId(displayId: Int): Int {
+ if (!DesktopModeStatus.enableMultipleDesktops(context)) {
+ // When the multiple desks feature is disabled, callers should not rely on the concept
+ // of a desk ID.
+ return INACTIVE_DESK_ID
+ }
+
+ return getDisplayDeskConfig(displayId)?.activeDeskId ?: INACTIVE_DESK_ID
+ }
+
/** Returns whether a desk is currently active on the display with the given [displayId]. */
fun isInDesktopMode(displayId: Int): Boolean {
if (!DesktopModeStatus.enableMultipleDesktops(context)) {
@@ -615,6 +629,6 @@
private const val TAG = "DesktopVisController"
private const val DEBUG = false
- private const val INACTIVE_DESK_ID = -1
+ public const val INACTIVE_DESK_ID = -1
}
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchTaskView.java b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchTaskView.java
index bf5c0c8..f80dc90 100644
--- a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchTaskView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchTaskView.java
@@ -197,8 +197,7 @@
final boolean isLeftRightSplit = !splitBounds.appsStackedVertically;
- final float leftOrTopTaskPercent = isLeftRightSplit
- ? splitBounds.leftTaskPercent : splitBounds.topTaskPercent;
+ final float leftOrTopTaskPercent = splitBounds.getLeftTopTaskPercent();
ConstraintLayout.LayoutParams leftTopParams = (ConstraintLayout.LayoutParams)
mThumbnailView1.getLayoutParams();
diff --git a/quickstep/src/com/android/launcher3/taskbar/TypefaceUtils.kt b/quickstep/src/com/android/launcher3/taskbar/TypefaceUtils.kt
index 8b53ff1..e9c62d1 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TypefaceUtils.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/TypefaceUtils.kt
@@ -30,11 +30,14 @@
class TypefaceUtils {
companion object {
- const val FONT_FAMILY_HEADLINE_SMALL_EMPHASIZED = "variable-headline-small-emphasized"
- const val FONT_FAMILY_HEADLINE_LARGE_EMPHASIZED = "variable-headline-large-emphasized"
const val FONT_FAMILY_BODY_SMALL_BASELINE = "variable-body-small"
const val FONT_FAMILY_BODY_MEDIUM_BASELINE = "variable-body-medium"
+ const val FONT_FAMILY_BODY_LARGE_BASELINE = "variable-body-large"
const val FONT_FAMILY_LABEL_LARGE_BASELINE = "variable-label-large"
+ const val FONT_FAMILY_DISPLAY_SMALL_EMPHASIZED = "variable-display-small-emphasized"
+ const val FONT_FAMILY_DISPLAY_MEDIUM_EMPHASIZED = "variable-display-medium-emphasized"
+ const val FONT_FAMILY_HEADLINE_SMALL_EMPHASIZED = "variable-headline-small-emphasized"
+ const val FONT_FAMILY_HEADLINE_LARGE_EMPHASIZED = "variable-headline-large-emphasized"
@JvmStatic
@JvmOverloads
diff --git a/quickstep/src/com/android/quickstep/RecentTasksList.java b/quickstep/src/com/android/quickstep/RecentTasksList.java
index 561bab4..2c4c2f9 100644
--- a/quickstep/src/com/android/quickstep/RecentTasksList.java
+++ b/quickstep/src/com/android/quickstep/RecentTasksList.java
@@ -32,10 +32,12 @@
import android.os.Process;
import android.os.RemoteException;
import android.util.SparseBooleanArray;
+import android.window.DesktopExperienceFlags;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
+import com.android.launcher3.statehandlers.DesktopVisibilityController;
import com.android.launcher3.util.LooperExecutor;
import com.android.launcher3.util.SplitConfigurationOptions;
import com.android.quickstep.util.DesktopTask;
@@ -354,8 +356,8 @@
int numVisibleTasks = 0;
for (GroupedTaskInfo rawTask : rawTasks) {
if (rawTask.isBaseType(TYPE_DESK)) {
- // TYPE_FREEFORM tasks is only created when desktop mode can be entered,
- // leftover TYPE_FREEFORM tasks created when flag was on should be ignored.
+ // TYPE_DESK tasks is only created when desktop mode can be entered,
+ // leftover TYPE_DESK tasks created when flag was on should be ignored.
if (DesktopModeStatus.canEnterDesktopMode(mContext)) {
List<DesktopTask> desktopTasks = createDesktopTasks(
rawTask.getBaseGroupedTask());
@@ -442,7 +444,11 @@
Set<Integer> minimizedTaskIds = minimizedTaskIdArray != null
? CollectionsKt.toSet(ArraysKt.asIterable(minimizedTaskIdArray))
: Collections.emptySet();
- if (enableSeparateExternalDisplayTasks()) {
+ if (enableSeparateExternalDisplayTasks()
+ && !DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue()) {
+ // This code is not needed when the multiple desktop feature is enabled, since Shell
+ // will send a single `GroupedTaskInfo` for each desk with a unique `deskId` across
+ // all displays.
Map<Integer, List<Task>> perDisplayTasks = new HashMap<>();
for (TaskInfo taskInfo : recentTaskInfo.getTaskInfoList()) {
Task task = createTask(taskInfo, minimizedTaskIds);
@@ -450,11 +456,16 @@
k -> new ArrayList<>());
tasks.add(task);
}
- return MapsKt.map(perDisplayTasks, it -> new DesktopTask(it.getValue()));
+ // When the multiple desktop feature is disabled, there can only be up to a single desk
+ // on each display, The desk ID doesn't matter and should not be used.
+ return MapsKt.map(perDisplayTasks,
+ it -> new DesktopTask(DesktopVisibilityController.INACTIVE_DESK_ID,
+ it.getValue()));
} else {
+ final int deskId = recentTaskInfo.getDeskId();
List<Task> tasks = CollectionsKt.map(recentTaskInfo.getTaskInfoList(),
it -> createTask(it, minimizedTaskIds));
- return List.of(new DesktopTask(tasks));
+ return List.of(new DesktopTask(deskId, tasks));
}
}
diff --git a/quickstep/src/com/android/quickstep/TaskShortcutFactory.java b/quickstep/src/com/android/quickstep/TaskShortcutFactory.java
index a69d472..f92581e 100644
--- a/quickstep/src/com/android/quickstep/TaskShortcutFactory.java
+++ b/quickstep/src/com/android/quickstep/TaskShortcutFactory.java
@@ -312,7 +312,7 @@
RecentsView<?, ?> recentsView = taskView.getRecentsView();
if (recentsView != null) {
dismissTaskMenuView();
- recentsView.dismissTask(taskView, true, true);
+ recentsView.dismissTaskView(taskView, true, true);
mTarget.getStatsLogManager().logger().withItemInfo(mTaskContainer.getItemInfo())
.log(LAUNCHER_SYSTEM_SHORTCUT_CLOSE_APP_TAP);
}
diff --git a/quickstep/src/com/android/quickstep/interaction/TutorialController.java b/quickstep/src/com/android/quickstep/interaction/TutorialController.java
index 0fc95e2..e73fb3b 100644
--- a/quickstep/src/com/android/quickstep/interaction/TutorialController.java
+++ b/quickstep/src/com/android/quickstep/interaction/TutorialController.java
@@ -57,6 +57,7 @@
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AnimatorListeners;
+import com.android.launcher3.taskbar.TypefaceUtils;
import com.android.launcher3.views.ClipIconView;
import com.android.quickstep.interaction.EdgeBackGestureHandler.BackGestureAttemptCallback;
import com.android.quickstep.interaction.NavBarGestureHandler.NavBarGestureAttemptCallback;
@@ -177,6 +178,7 @@
mFeedbackTitleView.setText(getIntroductionTitle());
mFeedbackSubtitleView.setText(getIntroductionSubtitle());
+ setTitleTypefaces();
mExitingAppView.setClipToOutline(true);
mExitingAppView.setOutlineProvider(new ViewOutlineProvider() {
@@ -434,6 +436,10 @@
if (isGestureSuccessful) {
if (mTutorialFragment.isAtFinalStep()) {
+ TypefaceUtils.setTypeface(
+ mDoneButton,
+ TypefaceUtils.FONT_FAMILY_LABEL_LARGE_BASELINE
+ );
showActionButton();
}
@@ -458,7 +464,8 @@
pauseAndHideLottieAnimation();
mCheckmarkAnimation.setVisibility(View.VISIBLE);
mCheckmarkAnimation.playAnimation();
- mFeedbackTitleView.setTextAppearance(mContext, getSuccessTitleTextAppearance());
+ mFeedbackTitleView.setTextAppearance(getSuccessTitleTextAppearance());
+ setTitleTypefaces();
}
public boolean isGestureCompleted() {
@@ -513,8 +520,10 @@
updateDrawables();
updateLayout();
- mFeedbackTitleView.setTextAppearance(mContext, getTitleTextAppearance());
- mDoneButton.setTextAppearance(mContext, getDoneButtonTextAppearance());
+ mFeedbackTitleView.setTextAppearance(getTitleTextAppearance());
+ mDoneButton.setTextAppearance(getDoneButtonTextAppearance());
+
+ setTitleTypefaces();
mDoneButton.getBackground().setTint(getDoneButtonColor());
mCheckmarkAnimation.setAnimation(mTutorialFragment.isAtFinalStep()
? R.raw.checkmark_animation_end
@@ -533,6 +542,21 @@
}
}
+ /**
+ * Apply expressive typefaces to the feedback title and subtitle views.
+ */
+ private void setTitleTypefaces() {
+ TypefaceUtils.setTypeface(
+ mFeedbackTitleView,
+ mTutorialFragment.isLargeScreen()
+ ? TypefaceUtils.FONT_FAMILY_DISPLAY_MEDIUM_EMPHASIZED
+ : TypefaceUtils.FONT_FAMILY_DISPLAY_SMALL_EMPHASIZED);
+ TypefaceUtils.setTypeface(
+ mFeedbackSubtitleView,
+ TypefaceUtils.FONT_FAMILY_BODY_LARGE_BASELINE
+ );
+ }
+
protected void resetViewsForBackGesture() {
mFakeTaskView.setVisibility(View.VISIBLE);
mFakeTaskView.setBackgroundColor(getFakeTaskViewColor());
diff --git a/quickstep/src/com/android/quickstep/orientation/LandscapePagedViewHandler.kt b/quickstep/src/com/android/quickstep/orientation/LandscapePagedViewHandler.kt
index e72ccbf..59ea8fa 100644
--- a/quickstep/src/com/android/quickstep/orientation/LandscapePagedViewHandler.kt
+++ b/quickstep/src/com/android/quickstep/orientation/LandscapePagedViewHandler.kt
@@ -285,11 +285,7 @@
translationY = snapshotParams.topMargin.toFloat()
} else {
val topLeftTaskPlusDividerPercent =
- if (splitBounds.appsStackedVertically) {
- splitBounds.topTaskPercent + splitBounds.dividerHeightPercent
- } else {
- splitBounds.leftTaskPercent + splitBounds.dividerWidthPercent
- }
+ splitBounds.leftTopTaskPercent + splitBounds.dividerPercent
translationY =
snapshotParams.topMargin +
(taskViewHeight - snapshotParams.topMargin) * topLeftTaskPlusDividerPercent
@@ -440,15 +436,8 @@
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
- }
+ val topLeftTaskPercent = splitInfo.leftTopTaskPercent
+ val dividerBarPercent = splitInfo.dividerPercent
if (desiredStagePosition == STAGE_POSITION_TOP_OR_LEFT) {
outRect.bottom = outRect.top + (outRect.height() * topLeftTaskPercent).toInt()
@@ -510,12 +499,7 @@
val totalThumbnailHeight = parentHeight - spaceAboveSnapshot
val dividerBar = getDividerBarSize(totalThumbnailHeight, splitBoundsConfig)
- val taskPercent =
- if (splitBoundsConfig.appsStackedVertically) {
- splitBoundsConfig.topTaskPercent
- } else {
- splitBoundsConfig.leftTaskPercent
- }
+ val taskPercent = splitBoundsConfig.leftTopTaskPercent
val firstTaskViewSize = Point(parentWidth, (totalThumbnailHeight * taskPercent).toInt())
val secondTaskViewSize =
Point(parentWidth, totalThumbnailHeight - firstTaskViewSize.y - dividerBar)
@@ -715,11 +699,7 @@
* @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
- )
+ return Math.round(totalThumbnailHeight * splitConfig.dividerPercent)
}
/**
diff --git a/quickstep/src/com/android/quickstep/orientation/PortraitPagedViewHandler.java b/quickstep/src/com/android/quickstep/orientation/PortraitPagedViewHandler.java
index c1e1c2b..d9ad7ce 100644
--- a/quickstep/src/com/android/quickstep/orientation/PortraitPagedViewHandler.java
+++ b/quickstep/src/com/android/quickstep/orientation/PortraitPagedViewHandler.java
@@ -271,12 +271,8 @@
if (splitBounds != null) {
if (deviceProfile.isLeftRightSplit) {
if (desiredTaskId == splitBounds.rightBottomTaskId) {
- float leftTopTaskPercent = splitBounds.appsStackedVertically
- ? splitBounds.topTaskPercent
- : splitBounds.leftTaskPercent;
- float dividerThicknessPercent = splitBounds.appsStackedVertically
- ? splitBounds.dividerHeightPercent
- : splitBounds.dividerWidthPercent;
+ float leftTopTaskPercent = splitBounds.getLeftTopTaskPercent();
+ float dividerThicknessPercent = splitBounds.getDividerPercent();
translationX = ((taskViewWidth * leftTopTaskPercent)
+ (taskViewWidth * dividerThicknessPercent));
}
@@ -285,9 +281,9 @@
FrameLayout.LayoutParams snapshotParams =
(FrameLayout.LayoutParams) thumbnailViews[0]
.getLayoutParams();
- float bottomRightTaskPlusDividerPercent = splitBounds.appsStackedVertically
- ? (1f - splitBounds.topTaskPercent)
- : (1f - splitBounds.leftTaskPercent);
+ float bottomRightTaskPlusDividerPercent =
+ splitBounds.getRightBottomTaskPercent()
+ + splitBounds.getDividerPercent();
translationY = -((taskViewHeight - snapshotParams.topMargin)
* bottomRightTaskPlusDividerPercent);
}
@@ -506,12 +502,8 @@
@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;
+ float topLeftTaskPercent = splitInfo.getLeftTopTaskPercent();
+ float dividerBarPercent = splitInfo.getDividerPercent();
int taskbarHeight = dp.isTransientTaskbar ? 0 : dp.taskbarHeight;
float scale = (float) outRect.height() / (dp.availableHeightPx - taskbarHeight);
@@ -559,9 +551,7 @@
primaryParams.topMargin = spaceAboveSnapshot;
int totalThumbnailHeight = parentHeight - spaceAboveSnapshot;
- float dividerScale = splitBoundsConfig.appsStackedVertically
- ? splitBoundsConfig.dividerHeightPercent
- : splitBoundsConfig.dividerWidthPercent;
+ float dividerScale = splitBoundsConfig.getDividerPercent();
Pair<Point, Point> taskViewSizes =
getGroupedTaskViewSizes(dp, splitBoundsConfig, parentWidth, parentHeight);
if (!inSplitSelection) {
@@ -610,12 +600,8 @@
int parentHeight) {
int spaceAboveSnapshot = dp.overviewTaskThumbnailTopMarginPx;
int totalThumbnailHeight = parentHeight - spaceAboveSnapshot;
- float dividerScale = splitBoundsConfig.appsStackedVertically
- ? splitBoundsConfig.dividerHeightPercent
- : splitBoundsConfig.dividerWidthPercent;
- float taskPercent = splitBoundsConfig.appsStackedVertically
- ? splitBoundsConfig.topTaskPercent
- : splitBoundsConfig.leftTaskPercent;
+ float dividerScale = splitBoundsConfig.getDividerPercent();
+ float taskPercent = splitBoundsConfig.getLeftTopTaskPercent();
Point firstTaskViewSize = new Point();
Point secondTaskViewSize = new Point();
diff --git a/quickstep/src/com/android/quickstep/orientation/SeascapePagedViewHandler.kt b/quickstep/src/com/android/quickstep/orientation/SeascapePagedViewHandler.kt
index 3fb4f54..9bfa2bf 100644
--- a/quickstep/src/com/android/quickstep/orientation/SeascapePagedViewHandler.kt
+++ b/quickstep/src/com/android/quickstep/orientation/SeascapePagedViewHandler.kt
@@ -106,15 +106,8 @@
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
- }
+ val topLeftTaskPercent = splitInfo.leftTopTaskPercent
+ val dividerBarPercent = splitInfo.dividerPercent
// 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,
@@ -166,11 +159,7 @@
} else {
if (desiredTaskId == splitBounds.leftTopTaskId) {
val bottomRightTaskPlusDividerPercent =
- if (splitBounds.appsStackedVertically) {
- 1f - splitBounds.topTaskPercent
- } else {
- 1f - splitBounds.leftTaskPercent
- }
+ splitBounds.rightBottomTaskPercent + splitBounds.dividerPercent
translationY =
banner.height -
(taskViewHeight - snapshotParams.topMargin) *
@@ -331,12 +320,7 @@
val totalThumbnailHeight = parentHeight - spaceAboveSnapshot
val dividerBar = getDividerBarSize(totalThumbnailHeight, splitBoundsConfig)
- val taskPercent =
- if (splitBoundsConfig.appsStackedVertically) {
- splitBoundsConfig.topTaskPercent
- } else {
- splitBoundsConfig.leftTaskPercent
- }
+ val taskPercent = splitBoundsConfig.leftTopTaskPercent
val firstTaskViewSize = Point(parentWidth, (totalThumbnailHeight * taskPercent).toInt())
val secondTaskViewSize =
Point(parentWidth, totalThumbnailHeight - firstTaskViewSize.y - dividerBar)
diff --git a/quickstep/src/com/android/quickstep/recents/ui/mapper/TaskUiStateMapper.kt b/quickstep/src/com/android/quickstep/recents/ui/mapper/TaskUiStateMapper.kt
index fb62268..619075f 100644
--- a/quickstep/src/com/android/quickstep/recents/ui/mapper/TaskUiStateMapper.kt
+++ b/quickstep/src/com/android/quickstep/recents/ui/mapper/TaskUiStateMapper.kt
@@ -16,6 +16,7 @@
package com.android.quickstep.recents.ui.mapper
+import android.view.View.OnClickListener
import com.android.quickstep.recents.ui.viewmodel.TaskData
import com.android.quickstep.task.thumbnail.TaskThumbnailUiState
import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.BackgroundOnly
@@ -36,29 +37,38 @@
* @param taskData The [TaskData] to convert. Can be null or a specific subclass.
* @param isLiveTile A flag indicating whether the task data represents live tile.
* @param hasHeader A flag indicating whether the UI should display a header.
+ * @param clickCloseListener A callback when the close button in the UI is clicked.
* @return A [TaskThumbnailUiState] representing the UI state for the given task data.
*/
fun toTaskThumbnailUiState(
taskData: TaskData?,
isLiveTile: Boolean,
hasHeader: Boolean,
+ clickCloseListener: OnClickListener?,
): TaskThumbnailUiState =
when {
taskData !is TaskData.Data -> Uninitialized
- isLiveTile -> createLiveTileState(taskData, hasHeader)
+ isLiveTile -> createLiveTileState(taskData, hasHeader, clickCloseListener)
isBackgroundOnly(taskData) -> BackgroundOnly(taskData.backgroundColor)
isSnapshotSplash(taskData) ->
- SnapshotSplash(createSnapshotState(taskData, hasHeader), taskData.icon)
+ SnapshotSplash(
+ createSnapshotState(taskData, hasHeader, clickCloseListener),
+ taskData.icon,
+ )
else -> Uninitialized
}
- private fun createSnapshotState(taskData: TaskData.Data, hasHeader: Boolean): Snapshot =
- if (canHeaderBeCreated(taskData, hasHeader)) {
+ private fun createSnapshotState(
+ taskData: TaskData.Data,
+ hasHeader: Boolean,
+ clickCloseListener: OnClickListener?,
+ ): Snapshot =
+ if (canHeaderBeCreated(taskData, hasHeader, clickCloseListener)) {
Snapshot.WithHeader(
taskData.thumbnailData?.thumbnail!!,
taskData.thumbnailData.rotation,
taskData.backgroundColor,
- ThumbnailHeader(taskData.icon!!, taskData.titleDescription!!),
+ ThumbnailHeader(taskData.icon!!, taskData.titleDescription!!, clickCloseListener!!),
)
} else {
Snapshot.WithoutHeader(
@@ -74,13 +84,26 @@
private fun isSnapshotSplash(taskData: TaskData.Data) =
taskData.thumbnailData?.thumbnail != null && !taskData.isLocked
- private fun canHeaderBeCreated(taskData: TaskData.Data, hasHeader: Boolean) =
- hasHeader && taskData.icon != null && taskData.titleDescription != null
+ private fun canHeaderBeCreated(
+ taskData: TaskData.Data,
+ hasHeader: Boolean,
+ clickCloseListener: OnClickListener?,
+ ) =
+ hasHeader &&
+ taskData.icon != null &&
+ taskData.titleDescription != null &&
+ clickCloseListener != null
- private fun createLiveTileState(taskData: TaskData.Data, hasHeader: Boolean) =
- if (canHeaderBeCreated(taskData, hasHeader)) {
+ private fun createLiveTileState(
+ taskData: TaskData.Data,
+ hasHeader: Boolean,
+ clickCloseListener: OnClickListener?,
+ ) =
+ if (canHeaderBeCreated(taskData, hasHeader, clickCloseListener)) {
// TODO(http://b/353965691): figure out what to do when `icon` or `titleDescription` is
// null.
- LiveTile.WithHeader(ThumbnailHeader(taskData.icon!!, taskData.titleDescription!!))
+ LiveTile.WithHeader(
+ ThumbnailHeader(taskData.icon!!, taskData.titleDescription!!, clickCloseListener!!)
+ )
} else LiveTile.WithoutHeader
}
diff --git a/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailUiState.kt b/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailUiState.kt
index 6118544..db593d3 100644
--- a/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailUiState.kt
+++ b/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailUiState.kt
@@ -19,6 +19,7 @@
import android.graphics.Bitmap
import android.graphics.drawable.Drawable
import android.view.Surface
+import android.view.View.OnClickListener
import androidx.annotation.ColorInt
sealed class TaskThumbnailUiState {
@@ -54,5 +55,9 @@
) : Snapshot()
}
- data class ThumbnailHeader(val icon: Drawable, val title: String)
+ data class ThumbnailHeader(
+ val icon: Drawable,
+ val title: String,
+ val clickCloseListener: OnClickListener,
+ )
}
diff --git a/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailView.kt b/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailView.kt
index e91073a..0edbacc 100644
--- a/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailView.kt
+++ b/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailView.kt
@@ -181,8 +181,10 @@
private fun resetViews() {
liveTileView.isInvisible = true
thumbnailView.isInvisible = true
+ thumbnailView.setImageBitmap(null)
splashBackground.alpha = 0f
splashIcon.alpha = 0f
+ splashIcon.setImageDrawable(null)
scrimView.alpha = 0f
setBackgroundColor(Color.BLACK)
taskThumbnailViewHeader?.isInvisible = true
diff --git a/quickstep/src/com/android/quickstep/util/DesktopTask.kt b/quickstep/src/com/android/quickstep/util/DesktopTask.kt
index 53ea022..fbe3bc6 100644
--- a/quickstep/src/com/android/quickstep/util/DesktopTask.kt
+++ b/quickstep/src/com/android/quickstep/util/DesktopTask.kt
@@ -20,17 +20,19 @@
/**
* A [Task] container that can contain N number of tasks that are part of the desktop in recent
- * tasks list. Note that desktops can be empty with no tasks in them.
+ * tasks list. Note that desktops can be empty with no tasks in them. The [deskId] makes sense only
+ * when the multiple desks feature is enabled.
*/
-class DesktopTask(tasks: List<Task>) : GroupTask(tasks, TaskViewType.DESKTOP) {
+class DesktopTask(val deskId: Int, tasks: List<Task>) : GroupTask(tasks, TaskViewType.DESKTOP) {
- override fun copy() = DesktopTask(tasks)
+ override fun copy() = DesktopTask(deskId, tasks)
- override fun toString() = "type=$taskViewType tasks=$tasks"
+ override fun toString() = "type=$taskViewType deskId=$deskId tasks=$tasks"
override fun equals(o: Any?): Boolean {
if (this === o) return true
if (o !is DesktopTask) return false
+ if (deskId != o.deskId) return false
return super.equals(o)
}
}
diff --git a/quickstep/src/com/android/quickstep/views/AddDesktopButton.kt b/quickstep/src/com/android/quickstep/views/AddDesktopButton.kt
index 9f3c017..9943770 100644
--- a/quickstep/src/com/android/quickstep/views/AddDesktopButton.kt
+++ b/quickstep/src/com/android/quickstep/views/AddDesktopButton.kt
@@ -91,6 +91,9 @@
}
}
+ protected fun getScrollAdjustment(showAsGrid: Boolean): Int =
+ if (showAsGrid) gridTranslationX.toInt() else 0
+
private fun getBorderBounds(bounds: Rect) {
bounds.set(0, 0, width, height)
val outlinePadding =
diff --git a/quickstep/src/com/android/quickstep/views/DesktopTaskView.kt b/quickstep/src/com/android/quickstep/views/DesktopTaskView.kt
index 9bb8f3d..75f3b69 100644
--- a/quickstep/src/com/android/quickstep/views/DesktopTaskView.kt
+++ b/quickstep/src/com/android/quickstep/views/DesktopTaskView.kt
@@ -345,14 +345,7 @@
explodeProgress = 0.0f
viewModel = null
visibility = VISIBLE
- taskContainers.forEach {
- contentView.removeView(it.snapshotView)
- if (enableRefactorTaskThumbnail()) {
- taskThumbnailViewPool!!.recycle(it.thumbnailView)
- } else {
- taskThumbnailViewDeprecatedPool!!.recycle(it.thumbnailViewDeprecated)
- }
- }
+ taskContainers.forEach { removeAndRecycleThumbnailView(it) }
}
@SuppressLint("RtlHardcoded")
@@ -360,19 +353,7 @@
super.updateTaskSize(lastComputedTaskSize, lastComputedGridTaskSize)
this.lastComputedTaskSize.set(lastComputedTaskSize)
- BaseContainerInterface.getTaskDimension(mContext, container.deviceProfile, tempPointF)
- val desktopSize = Size(tempPointF.x.toInt(), tempPointF.y.toInt())
- DEFAULT_BOUNDS.set(0, 0, desktopSize.width / 4, desktopSize.height / 4)
-
- fullscreenTaskPositions =
- taskContainers.map {
- DesktopTaskBoundsData(it.task.key.id, it.task.appBounds ?: DEFAULT_BOUNDS)
- }
-
- if (enableDesktopExplodedView()) {
- viewModel?.organizeDesktopTasks(desktopSize, fullscreenTaskPositions)
- }
- positionTaskWindows()
+ updateTaskPositions()
}
override fun onTaskListVisibilityChanged(visible: Boolean, changes: Int) {
@@ -458,6 +439,56 @@
ViewUtils.addAccessibleChildToList(backgroundView, outChildren)
}
+ fun removeTaskFromExplodedView(taskId: Int, animate: Boolean) {
+ if (!enableDesktopExplodedView()) {
+ Log.e(
+ TAG,
+ "removeTaskFromExplodedView called when enableDesktopExplodedView flag is false",
+ )
+ return
+ }
+
+ // Remove the task's [taskContainer] and its associated Views.
+ val taskContainer = getTaskContainerById(taskId) ?: return
+ removeAndRecycleThumbnailView(taskContainer)
+ taskContainer.destroy()
+ taskContainers = taskContainers.filterNot { it == taskContainer }
+
+ // Dismiss the current DesktopTaskView if all its windows are closed.
+ if (taskContainers.isEmpty()) {
+ recentsView?.dismissTaskView(this, animate, /* removeTask= */ true)
+ } else {
+ // Otherwise, re-position the remaining task windows.
+ // TODO(b/353949276): Implement the re-layout animations.
+ updateTaskPositions()
+ }
+ }
+
+ private fun removeAndRecycleThumbnailView(taskContainer: TaskContainer) {
+ contentView.removeView(taskContainer.snapshotView)
+ if (enableRefactorTaskThumbnail()) {
+ taskThumbnailViewPool!!.recycle(taskContainer.thumbnailView)
+ } else {
+ taskThumbnailViewDeprecatedPool!!.recycle(taskContainer.thumbnailViewDeprecated)
+ }
+ }
+
+ private fun updateTaskPositions() {
+ BaseContainerInterface.getTaskDimension(mContext, container.deviceProfile, tempPointF)
+ val desktopSize = Size(tempPointF.x.toInt(), tempPointF.y.toInt())
+ DEFAULT_BOUNDS.set(0, 0, desktopSize.width / 4, desktopSize.height / 4)
+
+ fullscreenTaskPositions =
+ taskContainers.map {
+ DesktopTaskBoundsData(it.task.key.id, it.task.appBounds ?: DEFAULT_BOUNDS)
+ }
+
+ if (enableDesktopExplodedView()) {
+ viewModel?.organizeDesktopTasks(desktopSize, fullscreenTaskPositions)
+ }
+ positionTaskWindows()
+ }
+
companion object {
private const val TAG = "DesktopTaskView"
private const val DEBUG = false
diff --git a/quickstep/src/com/android/quickstep/views/DigitalWellBeingToast.kt b/quickstep/src/com/android/quickstep/views/DigitalWellBeingToast.kt
index c07b7fb..5c4a35d 100644
--- a/quickstep/src/com/android/quickstep/views/DigitalWellBeingToast.kt
+++ b/quickstep/src/com/android/quickstep/views/DigitalWellBeingToast.kt
@@ -189,11 +189,11 @@
SplitBannerConfig.SPLIT_GRID_BANNER_LARGE
// For landscape grid, for 30% width we only show icon, otherwise show icon and time
task.key.id == splitBounds.leftTopTaskId ->
- if (splitBounds.leftTaskPercent < THRESHOLD_LEFT_ICON_ONLY)
+ if (splitBounds.leftTopTaskPercent < THRESHOLD_LEFT_ICON_ONLY)
SplitBannerConfig.SPLIT_GRID_BANNER_SMALL
else SplitBannerConfig.SPLIT_GRID_BANNER_LARGE
else ->
- if (splitBounds.leftTaskPercent > THRESHOLD_RIGHT_ICON_ONLY)
+ if (splitBounds.leftTopTaskPercent > THRESHOLD_RIGHT_ICON_ONLY)
SplitBannerConfig.SPLIT_GRID_BANNER_SMALL
else SplitBannerConfig.SPLIT_GRID_BANNER_LARGE
}
diff --git a/quickstep/src/com/android/quickstep/views/GroupedTaskView.kt b/quickstep/src/com/android/quickstep/views/GroupedTaskView.kt
index 2abfb13..25011d7 100644
--- a/quickstep/src/com/android/quickstep/views/GroupedTaskView.kt
+++ b/quickstep/src/com/android/quickstep/views/GroupedTaskView.kt
@@ -189,16 +189,12 @@
val inSplitSelection = getThisTaskCurrentlyInSplitSelection() != INVALID_TASK_ID
if (enableFlexibleTwoAppSplit()) {
- val topLeftTaskPercent =
- if (deviceProfile.isLeftRightSplit) splitBoundsConfig.leftTaskPercent
- else splitBoundsConfig.topTaskPercent
- val bottomRightTaskPercent = 1 - topLeftTaskPercent
- leftTopTaskContainer.iconView.setFlexSplitAlpha(
- if (topLeftTaskPercent < MINIMUM_RATIO_TO_SHOW_ICON) 0f else 1f
- )
- rightBottomTaskContainer.iconView.setFlexSplitAlpha(
- if (bottomRightTaskPercent < MINIMUM_RATIO_TO_SHOW_ICON) 0f else 1f
- )
+ val topLeftTaskPercent = splitBoundsConfig.leftTopTaskPercent
+ val bottomRightTaskPercent = splitBoundsConfig.rightBottomTaskPercent
+ val hideTopLeftIcon = topLeftTaskPercent < MINIMUM_RATIO_TO_SHOW_ICON
+ val hideBottomRightIcon = bottomRightTaskPercent < MINIMUM_RATIO_TO_SHOW_ICON
+ leftTopTaskContainer.iconView.setFlexSplitAlpha(if (hideTopLeftIcon) 0f else 1f)
+ rightBottomTaskContainer.iconView.setFlexSplitAlpha(if (hideBottomRightIcon) 0f else 1f)
}
if (enableOverviewIconMenu()) {
diff --git a/quickstep/src/com/android/quickstep/views/RecentsDismissUtils.kt b/quickstep/src/com/android/quickstep/views/RecentsDismissUtils.kt
index 96eed87..3430b39 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsDismissUtils.kt
+++ b/quickstep/src/com/android/quickstep/views/RecentsDismissUtils.kt
@@ -76,7 +76,7 @@
}
.addEndListener { _, _, _, _ ->
if (isDismissing) {
- recentsView.dismissTask(
+ recentsView.dismissTaskView(
draggedTaskView,
/* animateTaskView = */ false,
/* removeTask = */ true,
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index 9e53f11..d17dfb8 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -676,11 +676,11 @@
MAIN_EXECUTOR,
apkRemoved -> {
if (apkRemoved) {
- dismissTask(taskId);
+ dismissTask(taskId, /*animate=*/true, /*removeTask=*/false);
} else {
mModel.isTaskRemoved(taskKey.id, taskRemoved -> {
if (taskRemoved) {
- dismissTask(taskId);
+ dismissTask(taskId, /*animate=*/true, /*removeTask=*/false);
}
}, RecentsFilterState.getFilter(mFilterState.getPackageNameToFilter()));
}
@@ -3048,8 +3048,12 @@
// Add an empty view for now until the task plan is loaded and applied
final TaskView taskView;
if (needDesktopTask) {
+ final int activeDeskId =
+ DesktopVisibilityController.INSTANCE.get(mContext).getActiveDeskId(
+ mContainer.getDisplay().getDisplayId());
taskView = getTaskViewFromPool(TaskViewType.DESKTOP);
- ((DesktopTaskView) taskView).bind(new DesktopTask(Arrays.asList(runningTasks)),
+ ((DesktopTaskView) taskView).bind(
+ new DesktopTask(activeDeskId, Arrays.asList(runningTasks)),
mOrientationState, mTaskOverlayFactory);
} else if (needGroupTaskView) {
taskView = getTaskViewFromPool(TaskViewType.GROUPED);
@@ -3904,6 +3908,22 @@
// the only invariant point in landscape split screen.
snapToLastTask = true;
}
+ if (mUtils.getGridTaskCount() == 1 && dismissedTaskView.isGridTask()) {
+ TaskView lastLargeTile = mUtils.getLastLargeTaskView();
+ if (lastLargeTile != null) {
+ // Calculate the distance to put last large tile back to middle of the screen.
+ int primaryScroll = getPagedOrientationHandler().getPrimaryScroll(this);
+ int lastLargeTileScroll = getScrollForPage(indexOfChild(lastLargeTile));
+ longGridRowWidthDiff = primaryScroll - lastLargeTileScroll;
+
+ if (!isClearAllHidden) {
+ // If ClearAllButton is visible, reduce the distance by scroll difference
+ // between ClearAllButton and the last task.
+ longGridRowWidthDiff += getLastTaskScroll(/*clearAllScroll=*/0,
+ getPagedOrientationHandler().getPrimarySize(mClearAllButton));
+ }
+ }
+ }
// If we need to animate the grid to compensate the clear all gap, we split the second
// half of the dismiss pending animation (in which the non-dismissed tasks slide into
@@ -4627,17 +4647,27 @@
}
@UiThread
- private void dismissTask(int taskId) {
+ public void dismissTask(int taskId, boolean animate, boolean removeTask) {
TaskView taskView = getTaskViewByTaskId(taskId);
if (taskView == null) {
Log.d(TAG, "dismissTask: " + taskId + ", no associated TaskView");
return;
}
Log.d(TAG, "dismissTask: " + taskId);
- dismissTask(taskView, true /* animate */, false /* removeTask */);
+
+ if (enableDesktopExplodedView() && taskView instanceof DesktopTaskView desktopTaskView) {
+ desktopTaskView.removeTaskFromExplodedView(taskId, animate);
+
+ if (removeTask) {
+ ActivityManagerWrapper.getInstance().removeTask(taskId);
+ }
+ } else {
+ dismissTaskView(taskView, animate, removeTask);
+ }
}
- public void dismissTask(TaskView taskView, boolean animateTaskView, boolean removeTask) {
+ /** Dismisses the entire [taskView]. */
+ public void dismissTaskView(TaskView taskView, boolean animateTaskView, boolean removeTask) {
PendingAnimation pa = new PendingAnimation(DISMISS_TASK_DURATION);
createTaskDismissAnimation(pa, taskView, animateTaskView, removeTask, DISMISS_TASK_DURATION,
false /* dismissingForSplitSelection*/);
@@ -4653,7 +4683,7 @@
private void dismissCurrentTask() {
TaskView taskView = getNextPageTaskView();
if (taskView != null) {
- dismissTask(taskView, true /*animateTaskView*/, true /*removeTask*/);
+ dismissTaskView(taskView, true /*animateTaskView*/, true /*removeTask*/);
}
}
@@ -6248,8 +6278,8 @@
int addDesktopButtonIndex = indexOfChild(mAddDesktopButton);
if (addDesktopButtonIndex != -1 && addDesktopButtonIndex < outPageScrolls.length) {
outPageScrolls[addDesktopButtonIndex] =
- newPageScrolls[addDesktopButtonIndex] + Math.round(
- mAddDesktopButton.getGridTranslationX());
+ newPageScrolls[addDesktopButtonIndex] + mAddDesktopButton.getScrollAdjustment(
+ showAsGrid);
}
int lastTaskScroll = getLastTaskScroll(clearAllScroll, clearAllWidth);
diff --git a/quickstep/src/com/android/quickstep/views/RecentsViewUtils.kt b/quickstep/src/com/android/quickstep/views/RecentsViewUtils.kt
index 8df7430..67318ac 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsViewUtils.kt
+++ b/quickstep/src/com/android/quickstep/views/RecentsViewUtils.kt
@@ -85,6 +85,9 @@
/** Counts [TaskView]s that are large tiles. */
fun getLargeTileCount(): Int = taskViews.count { it.isLargeTile }
+ /** Counts [TaskView]s that are grid tasks. */
+ fun getGridTaskCount(): Int = taskViews.count { it.isGridTask }
+
/** Returns the first TaskView that should be displayed as a large tile. */
fun getFirstLargeTaskView(): TaskView? =
taskViews.firstOrNull {
diff --git a/quickstep/src/com/android/quickstep/views/TaskContainer.kt b/quickstep/src/com/android/quickstep/views/TaskContainer.kt
index 7301cfc..2b9d036 100644
--- a/quickstep/src/com/android/quickstep/views/TaskContainer.kt
+++ b/quickstep/src/com/android/quickstep/views/TaskContainer.kt
@@ -19,6 +19,7 @@
import android.graphics.Bitmap
import android.graphics.Matrix
import android.view.View
+import android.view.View.OnClickListener
import com.android.launcher3.Flags.enableRefactorTaskThumbnail
import com.android.launcher3.model.data.TaskViewItemInfo
import com.android.launcher3.util.SplitConfigurationOptions
@@ -108,6 +109,8 @@
overlay.destroy()
if (enableRefactorTaskThumbnail()) {
isThumbnailValid = false
+ thumbnailData = null
+ thumbnailView.onRecycle()
} else {
thumbnailViewDeprecated.setShowSplashForSplitSelection(false)
}
@@ -127,9 +130,19 @@
overlay.addChildForAccessibility(outChildren)
}
- fun setState(state: TaskData?, liveTile: Boolean, hasHeader: Boolean) {
+ fun setState(
+ state: TaskData?,
+ liveTile: Boolean,
+ hasHeader: Boolean,
+ clickCloseListener: OnClickListener?,
+ ) {
thumbnailView.setState(
- TaskUiStateMapper.toTaskThumbnailUiState(state, liveTile, hasHeader),
+ TaskUiStateMapper.toTaskThumbnailUiState(
+ state,
+ liveTile,
+ hasHeader,
+ clickCloseListener,
+ ),
state?.taskId,
)
thumbnailData = if (state is TaskData.Data) state.thumbnailData else null
diff --git a/quickstep/src/com/android/quickstep/views/TaskThumbnailViewHeader.kt b/quickstep/src/com/android/quickstep/views/TaskThumbnailViewHeader.kt
index 9eb294a..9a8805b 100644
--- a/quickstep/src/com/android/quickstep/views/TaskThumbnailViewHeader.kt
+++ b/quickstep/src/com/android/quickstep/views/TaskThumbnailViewHeader.kt
@@ -19,6 +19,7 @@
import android.content.Context
import android.util.AttributeSet
import android.widget.FrameLayout
+import android.widget.ImageButton
import android.widget.ImageView
import android.widget.TextView
import com.android.launcher3.R
@@ -30,9 +31,11 @@
private val headerTitleView: TextView by lazy { findViewById(R.id.header_app_title) }
private val headerIconView: ImageView by lazy { findViewById(R.id.header_app_icon) }
+ private val headerCloseButton: ImageButton by lazy { findViewById(R.id.header_close_button) }
fun setHeader(header: ThumbnailHeader) {
headerTitleView.setText(header.title)
headerIconView.setImageDrawable(header.icon)
+ headerCloseButton.setOnClickListener(header.clickCloseListener)
}
}
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.kt b/quickstep/src/com/android/quickstep/views/TaskView.kt
index ba54232..0e5382a 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.kt
+++ b/quickstep/src/com/android/quickstep/views/TaskView.kt
@@ -44,6 +44,7 @@
import androidx.core.view.updateLayoutParams
import com.android.app.animation.Interpolators
import com.android.launcher3.Flags.enableCursorHoverStates
+import com.android.launcher3.Flags.enableDesktopExplodedView
import com.android.launcher3.Flags.enableGridOnlyOverview
import com.android.launcher3.Flags.enableHoverOfChildElementsInTaskview
import com.android.launcher3.Flags.enableLargeDesktopWindowingTile
@@ -768,11 +769,27 @@
// Updating containers
val mapOfTasks = state.tasks.associateBy { it.taskId }
taskContainers.forEach { container ->
- val containerState = mapOfTasks[container.task.key.id]
+ val taskId = container.task.key.id
+ val containerState = mapOfTasks[taskId]
+ val shouldHaveHeader = (type == TaskViewType.DESKTOP) && enableDesktopExplodedView()
container.setState(
state = containerState,
liveTile = state.isLiveTile,
- hasHeader = state.hasHeader,
+ hasHeader = shouldHaveHeader,
+ clickCloseListener =
+ if (shouldHaveHeader) {
+ {
+ // Update the layout UI to remove this task from the layout grid, and
+ // remove the task from ActivityManager afterwards.
+ recentsView?.dismissTask(
+ taskId,
+ /* animate= */ true,
+ /* removeTask= */ true,
+ )
+ }
+ } else {
+ null
+ },
)
updateThumbnailValidity(container)
updateThumbnailMatrix(
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarOverflowTest.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarOverflowTest.kt
index bfd53ef..2cd09cc 100644
--- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarOverflowTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarOverflowTest.kt
@@ -332,7 +332,7 @@
(0..<tasksToAdd).map {
Task(Task.TaskKey(it, 0, Intent(), ComponentName("", ""), 0, 2000))
}
- recentsModel.updateRecentTasks(listOf(DesktopTask(tasks)))
+ recentsModel.updateRecentTasks(listOf(DesktopTask(deskId = 0, tasks)))
desktopTaskListener?.onTasksVisibilityChanged(
context.virtualDisplay.display.displayId,
tasksToAdd,
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarRecentAppsControllerTest.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarRecentAppsControllerTest.kt
index c792783..002c988 100644
--- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarRecentAppsControllerTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarRecentAppsControllerTest.kt
@@ -877,7 +877,7 @@
val allTasks =
ArrayList<GroupTask>().apply {
if (!runningTasks.isEmpty()) {
- add(DesktopTask(ArrayList(runningTasks)))
+ add(DesktopTask(deskId = 0, ArrayList(runningTasks)))
}
addAll(recentTasks)
}
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/inputconsumers/NavHandleLongPressInputConsumerTest.java b/quickstep/tests/multivalentTests/src/com/android/quickstep/inputconsumers/NavHandleLongPressInputConsumerTest.java
index de6920b..99a34ea 100644
--- a/quickstep/tests/multivalentTests/src/com/android/quickstep/inputconsumers/NavHandleLongPressInputConsumerTest.java
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/inputconsumers/NavHandleLongPressInputConsumerTest.java
@@ -28,6 +28,7 @@
import static com.android.launcher3.logging.StatsLogManager.LauncherLatencyEvent.LAUNCHER_LATENCY_CONTEXTUAL_SEARCH_LPNH_ABANDON;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.quickstep.DeviceConfigWrapper.DEFAULT_LPNH_TIMEOUT_MS;
+import static com.android.quickstep.inputconsumers.NavHandleLongPressInputConsumer.MIN_TIME_TO_LOG_ABANDON_MS;
import static com.google.common.truth.Truth.assertThat;
@@ -147,8 +148,7 @@
public void testDelegateDisallowsTouchInterceptAfterTouchDown() {
// Touch down and wait the minimum abandonment time.
mUnderTest.onMotionEvent(generateCenteredMotionEvent(ACTION_DOWN));
- SystemClock.sleep(NavHandleLongPressInputConsumer.MIN_TIME_TO_LOG_ABANDON_MS);
- InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+ sleep(MIN_TIME_TO_LOG_ABANDON_MS);
// Delegate should still get touches unless long press is triggered.
verify(mDelegate).onMotionEvent(any());
@@ -173,8 +173,7 @@
@Test
public void testLongPressTriggered() {
mUnderTest.onMotionEvent(generateCenteredMotionEvent(ACTION_DOWN));
- SystemClock.sleep(DEFAULT_LPNH_TIMEOUT_MS);
- InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+ sleep(DEFAULT_LPNH_TIMEOUT_MS);
assertThat(mUnderTest.mState).isEqualTo(DelegateInputConsumer.STATE_ACTIVE);
assertTrue(mLongPressTriggered.get());
@@ -191,10 +190,8 @@
@Test
public void testLongPressTriggeredWithSlightVerticalMovement() {
mUnderTest.onMotionEvent(generateCenteredMotionEvent(ACTION_DOWN));
- mUnderTest.onMotionEvent(generateCenteredMotionEventWithYOffset(ACTION_MOVE,
- -(TOUCH_SLOP - 1)));
- SystemClock.sleep(DEFAULT_LPNH_TIMEOUT_MS);
- InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+ mUnderTest.onMotionEvent(generateCenteredMotionEventWithYOffset(ACTION_MOVE, 1));
+ sleep(DEFAULT_LPNH_TIMEOUT_MS);
assertThat(mUnderTest.mState).isEqualTo(DelegateInputConsumer.STATE_ACTIVE);
assertTrue(mLongPressTriggered.get());
@@ -207,10 +204,8 @@
@Test
public void testLongPressTriggeredWithSlightHorizontalMovement() {
mUnderTest.onMotionEvent(generateCenteredMotionEvent(ACTION_DOWN));
- mUnderTest.onMotionEvent(generateMotionEvent(ACTION_MOVE,
- mScreenWidth / 2f - (TOUCH_SLOP - 1), 0));
- SystemClock.sleep(DEFAULT_LPNH_TIMEOUT_MS);
- InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+ mUnderTest.onMotionEvent(generateMotionEvent(ACTION_MOVE, mScreenWidth / 2f + 1, 0));
+ sleep(DEFAULT_LPNH_TIMEOUT_MS);
assertThat(mUnderTest.mState).isEqualTo(DelegateInputConsumer.STATE_ACTIVE);
assertTrue(mLongPressTriggered.get());
@@ -230,8 +225,7 @@
mUnderTest.onMotionEvent(generateMotionEvent(ACTION_MOVE,
mScreenWidth / 2f - (TOUCH_SLOP - 1), 0));
// We have entered the second stage, so the normal timeout shouldn't trigger.
- SystemClock.sleep(DEFAULT_LPNH_TIMEOUT_MS);
- InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+ sleep(DEFAULT_LPNH_TIMEOUT_MS);
assertThat(mUnderTest.mState).isEqualTo(DelegateInputConsumer.STATE_INACTIVE);
assertFalse(mLongPressTriggered.get());
@@ -241,9 +235,8 @@
// After an extended time, the long press should trigger.
float extendedDurationMultiplier =
(DeviceConfigWrapper.get().getTwoStageDurationPercentage() / 100f);
- SystemClock.sleep((long) (DEFAULT_LPNH_TIMEOUT_MS
+ sleep((long) (DEFAULT_LPNH_TIMEOUT_MS
* (extendedDurationMultiplier - 1))); // -1 because we already waited 1x
- InstrumentationRegistry.getInstrumentation().waitForIdleSync();
assertThat(mUnderTest.mState).isEqualTo(DelegateInputConsumer.STATE_ACTIVE);
assertTrue(mLongPressTriggered.get());
@@ -264,8 +257,7 @@
mUnderTest.onMotionEvent(generateCenteredMotionEvent(ACTION_DOWN));
// We have not entered the second stage, so the normal timeout should trigger.
- SystemClock.sleep(DEFAULT_LPNH_TIMEOUT_MS);
- InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+ sleep(DEFAULT_LPNH_TIMEOUT_MS);
assertThat(mUnderTest.mState).isEqualTo(DelegateInputConsumer.STATE_ACTIVE);
assertTrue(mLongPressTriggered.get());
@@ -281,16 +273,14 @@
@Test
public void testLongPressAbortedByTouchUp() {
mUnderTest.onMotionEvent(generateCenteredMotionEvent(ACTION_DOWN));
- SystemClock.sleep(DEFAULT_LPNH_TIMEOUT_MS - 10);
- InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+ sleep(MIN_TIME_TO_LOG_ABANDON_MS);
assertThat(mUnderTest.mState).isEqualTo(DelegateInputConsumer.STATE_INACTIVE);
assertFalse(mLongPressTriggered.get());
mUnderTest.onMotionEvent(generateCenteredMotionEvent(ACTION_UP));
// Wait past the long press timeout, to be extra sure it wouldn't have triggered.
- SystemClock.sleep(20);
- InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+ sleep(DEFAULT_LPNH_TIMEOUT_MS);
assertThat(mUnderTest.mState).isEqualTo(DelegateInputConsumer.STATE_INACTIVE);
assertFalse(mLongPressTriggered.get());
@@ -303,16 +293,14 @@
@Test
public void testLongPressAbortedByTouchCancel() {
mUnderTest.onMotionEvent(generateCenteredMotionEvent(ACTION_DOWN));
- SystemClock.sleep(DEFAULT_LPNH_TIMEOUT_MS - 10);
- InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+ sleep(MIN_TIME_TO_LOG_ABANDON_MS);
assertThat(mUnderTest.mState).isEqualTo(DelegateInputConsumer.STATE_INACTIVE);
assertFalse(mLongPressTriggered.get());
mUnderTest.onMotionEvent(generateCenteredMotionEvent(ACTION_CANCEL));
// Wait past the long press timeout, to be extra sure it wouldn't have triggered.
- SystemClock.sleep(20);
- InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+ sleep(DEFAULT_LPNH_TIMEOUT_MS);
assertThat(mUnderTest.mState).isEqualTo(DelegateInputConsumer.STATE_INACTIVE);
assertFalse(mLongPressTriggered.get());
@@ -325,8 +313,7 @@
@Test
public void testLongPressAbortedByTouchSlopPassedVertically() {
mUnderTest.onMotionEvent(generateCenteredMotionEvent(ACTION_DOWN));
- SystemClock.sleep(DEFAULT_LPNH_TIMEOUT_MS - 10);
- InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+ sleep(MIN_TIME_TO_LOG_ABANDON_MS);
assertThat(mUnderTest.mState).isEqualTo(DelegateInputConsumer.STATE_INACTIVE);
assertFalse(mLongPressTriggered.get());
@@ -334,8 +321,7 @@
mUnderTest.onMotionEvent(generateCenteredMotionEventWithYOffset(ACTION_MOVE,
-(TOUCH_SLOP + 1)));
// Wait past the long press timeout, to be extra sure it wouldn't have triggered.
- SystemClock.sleep(20);
- InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+ sleep(DEFAULT_LPNH_TIMEOUT_MS);
assertThat(mUnderTest.mState).isEqualTo(DelegateInputConsumer.STATE_INACTIVE);
assertFalse(mLongPressTriggered.get());
@@ -348,8 +334,7 @@
@Test
public void testLongPressAbortedByTouchSlopPassedHorizontally() {
mUnderTest.onMotionEvent(generateCenteredMotionEvent(ACTION_DOWN));
- SystemClock.sleep(DEFAULT_LPNH_TIMEOUT_MS - 10);
- InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+ sleep(MIN_TIME_TO_LOG_ABANDON_MS);
assertThat(mUnderTest.mState).isEqualTo(DelegateInputConsumer.STATE_INACTIVE);
assertFalse(mLongPressTriggered.get());
@@ -357,8 +342,7 @@
mUnderTest.onMotionEvent(generateMotionEvent(ACTION_MOVE,
mScreenWidth / 2f - (TOUCH_SLOP + 1), 0));
// Wait past the long press timeout, to be extra sure it wouldn't have triggered.
- SystemClock.sleep(20);
- InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+ sleep(DEFAULT_LPNH_TIMEOUT_MS);
assertThat(mUnderTest.mState).isEqualTo(DelegateInputConsumer.STATE_INACTIVE);
assertFalse(mLongPressTriggered.get());
@@ -379,8 +363,7 @@
mUnderTest.onMotionEvent(generateCenteredMotionEventWithYOffset(ACTION_MOVE,
-(TOUCH_SLOP - 1)));
// Normal duration shouldn't trigger.
- SystemClock.sleep(DEFAULT_LPNH_TIMEOUT_MS);
- InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+ sleep(DEFAULT_LPNH_TIMEOUT_MS);
assertThat(mUnderTest.mState).isEqualTo(DelegateInputConsumer.STATE_INACTIVE);
assertFalse(mLongPressTriggered.get());
@@ -391,9 +374,8 @@
// Wait past the extended long press timeout, to be sure it wouldn't have triggered.
float extendedDurationMultiplier =
(DeviceConfigWrapper.get().getTwoStageDurationPercentage() / 100f);
- SystemClock.sleep((long) (DEFAULT_LPNH_TIMEOUT_MS
+ sleep((long) (DEFAULT_LPNH_TIMEOUT_MS
* (extendedDurationMultiplier - 1))); // -1 because we already waited 1x
- InstrumentationRegistry.getInstrumentation().waitForIdleSync();
assertThat(mUnderTest.mState).isEqualTo(DelegateInputConsumer.STATE_INACTIVE);
assertFalse(mLongPressTriggered.get());
@@ -418,8 +400,7 @@
mUnderTest.onMotionEvent(generateMotionEvent(ACTION_MOVE,
mScreenWidth / 2f - (TOUCH_SLOP - 1), 0));
// Normal duration shouldn't trigger.
- SystemClock.sleep(DEFAULT_LPNH_TIMEOUT_MS);
- InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+ sleep(DEFAULT_LPNH_TIMEOUT_MS);
assertThat(mUnderTest.mState).isEqualTo(DelegateInputConsumer.STATE_INACTIVE);
assertFalse(mLongPressTriggered.get());
@@ -430,9 +411,8 @@
// Wait past the extended long press timeout, to be sure it wouldn't have triggered.
float extendedDurationMultiplier =
(DeviceConfigWrapper.get().getTwoStageDurationPercentage() / 100f);
- SystemClock.sleep((long) (DEFAULT_LPNH_TIMEOUT_MS
+ sleep((long) (DEFAULT_LPNH_TIMEOUT_MS
* (extendedDurationMultiplier - 1))); // -1 because we already waited 1x
- InstrumentationRegistry.getInstrumentation().waitForIdleSync();
assertThat(mUnderTest.mState).isEqualTo(DelegateInputConsumer.STATE_INACTIVE);
assertFalse(mLongPressTriggered.get());
@@ -450,8 +430,7 @@
public void testTouchOutsideNavHandleIgnored() {
// Touch the far left side of the screen. (y=0 is top of navbar region, picked arbitrarily)
mUnderTest.onMotionEvent(generateMotionEvent(ACTION_DOWN, 0, 0));
- SystemClock.sleep(DEFAULT_LPNH_TIMEOUT_MS);
- InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+ sleep(DEFAULT_LPNH_TIMEOUT_MS);
// Should be ignored because the x position was not centered in the navbar region.
assertThat(mUnderTest.mState).isEqualTo(DelegateInputConsumer.STATE_INACTIVE);
@@ -484,8 +463,7 @@
@Test
public void testNoLogsForShortTouch() {
mUnderTest.onMotionEvent(generateCenteredMotionEvent(ACTION_DOWN));
- SystemClock.sleep(10);
- InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+ sleep(10);
mUnderTest.onMotionEvent(generateCenteredMotionEvent(ACTION_UP));
verifyNoMoreInteractions(mStatsLogManager);
verifyNoMoreInteractions(mStatsLogger);
@@ -509,6 +487,11 @@
mDownTimeMs = 0;
}
+ private static void sleep(long sleepMs) {
+ SystemClock.sleep(sleepMs);
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+ }
+
/** Generate a motion event centered horizontally in the screen. */
private MotionEvent generateCenteredMotionEvent(int motionAction) {
return generateCenteredMotionEventWithYOffset(motionAction, 0);
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/TasksRepositoryTest.kt b/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/TasksRepositoryTest.kt
index 10be6fd..6790567 100644
--- a/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/TasksRepositoryTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/data/TasksRepositoryTest.kt
@@ -64,7 +64,7 @@
/* snapPosition = */ SNAP_TO_2_50_50,
),
),
- DesktopTask(tasks.subList(3, 6)),
+ DesktopTask(deskId = 0, tasks.subList(3, 6)),
)
private val recentsModel = FakeRecentTasksDataSource()
private val taskThumbnailDataSource = FakeTaskThumbnailDataSource()
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/ui/mapper/TaskUiStateMapperTest.kt b/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/ui/mapper/TaskUiStateMapperTest.kt
index 124045f..7ca194a 100644
--- a/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/ui/mapper/TaskUiStateMapperTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/recents/ui/mapper/TaskUiStateMapperTest.kt
@@ -21,6 +21,7 @@
import android.graphics.drawable.ShapeDrawable
import android.platform.test.annotations.EnableFlags
import android.view.Surface
+import android.view.View
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.launcher3.Flags
import com.android.quickstep.recents.ui.viewmodel.TaskData
@@ -43,6 +44,7 @@
taskData = null,
isLiveTile = false,
hasHeader = false,
+ clickCloseListener = null,
)
assertThat(result).isEqualTo(TaskThumbnailUiState.Uninitialized)
}
@@ -57,6 +59,7 @@
taskData = input,
isLiveTile = true,
hasHeader = false,
+ clickCloseListener = null,
)
assertThat(result).isEqualTo(LiveTile.WithoutHeader)
}
@@ -72,14 +75,18 @@
TASK_DATA.copy(isLocked = true),
TASK_DATA.copy(title = null),
)
+ val closeCallback = View.OnClickListener {}
val expected =
- LiveTile.WithHeader(header = ThumbnailHeader(TASK_ICON, TASK_TITLE_DESCRIPTION))
+ LiveTile.WithHeader(
+ header = ThumbnailHeader(TASK_ICON, TASK_TITLE_DESCRIPTION, closeCallback)
+ )
inputs.forEach { taskData ->
val result =
TaskUiStateMapper.toTaskThumbnailUiState(
taskData = taskData,
isLiveTile = true,
hasHeader = true,
+ clickCloseListener = closeCallback,
)
assertThat(result).isEqualTo(expected)
}
@@ -101,6 +108,7 @@
taskData = taskData,
isLiveTile = true,
hasHeader = true,
+ clickCloseListener = {},
)
assertThat(result).isEqualTo(LiveTile.WithoutHeader)
}
@@ -113,6 +121,7 @@
taskData = TASK_DATA,
isLiveTile = false,
hasHeader = false,
+ clickCloseListener = null,
)
val expected =
@@ -133,6 +142,7 @@
@Test
fun taskData_isStaticTile_withHeader_returns_SnapshotSplashWithHeader() {
val inputs = listOf(TASK_DATA, TASK_DATA.copy(title = null))
+ val closeCallback = View.OnClickListener {}
val expected =
TaskThumbnailUiState.SnapshotSplash(
snapshot =
@@ -140,7 +150,7 @@
backgroundColor = TASK_BACKGROUND_COLOR,
bitmap = TASK_THUMBNAIL,
thumbnailRotation = Surface.ROTATION_0,
- header = ThumbnailHeader(TASK_ICON, TASK_TITLE_DESCRIPTION),
+ header = ThumbnailHeader(TASK_ICON, TASK_TITLE_DESCRIPTION, closeCallback),
),
splash = TASK_ICON,
)
@@ -150,6 +160,7 @@
taskData = taskData,
isLiveTile = false,
hasHeader = true,
+ clickCloseListener = closeCallback,
)
assertThat(result).isEqualTo(expected)
}
@@ -176,6 +187,7 @@
taskData = taskData,
isLiveTile = false,
hasHeader = true,
+ clickCloseListener = {},
)
assertThat(result).isInstanceOf(TaskThumbnailUiState.SnapshotSplash::class.java)
@@ -191,6 +203,7 @@
taskData = TASK_DATA.copy(thumbnailData = null),
isLiveTile = false,
hasHeader = false,
+ clickCloseListener = null,
)
val expected = TaskThumbnailUiState.BackgroundOnly(TASK_BACKGROUND_COLOR)
@@ -204,6 +217,7 @@
taskData = TASK_DATA.copy(isLocked = true),
isLiveTile = false,
hasHeader = false,
+ clickCloseListener = null,
)
val expected = TaskThumbnailUiState.BackgroundOnly(TASK_BACKGROUND_COLOR)
@@ -212,6 +226,7 @@
private companion object {
const val TASK_TITLE_DESCRIPTION = "Title Description 1"
+ var TASK_ID = 1
val TASK_ICON = ShapeDrawable()
val TASK_THUMBNAIL = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888)
val TASK_THUMBNAIL_DATA =
@@ -219,7 +234,7 @@
val TASK_BACKGROUND_COLOR = Color.rgb(1, 2, 3)
val TASK_DATA =
TaskData.Data(
- 1,
+ TASK_ID,
title = "Task 1",
titleDescription = TASK_TITLE_DESCRIPTION,
icon = TASK_ICON,
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/util/DesktopTaskTest.kt b/quickstep/tests/multivalentTests/src/com/android/quickstep/util/DesktopTaskTest.kt
index 7aed579..6fbf482 100644
--- a/quickstep/tests/multivalentTests/src/com/android/quickstep/util/DesktopTaskTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/util/DesktopTaskTest.kt
@@ -29,35 +29,42 @@
@Test
fun testDesktopTask_sameInstance_isEqual() {
- val task = DesktopTask(createTasks(1))
+ val task = DesktopTask(deskId = 0, createTasks(1))
assertThat(task).isEqualTo(task)
}
@Test
fun testDesktopTask_identicalConstructor_isEqual() {
- val task1 = DesktopTask(createTasks(1))
- val task2 = DesktopTask(createTasks(1))
+ val task1 = DesktopTask(deskId = 0, createTasks(1))
+ val task2 = DesktopTask(deskId = 0, createTasks(1))
assertThat(task1).isEqualTo(task2)
}
@Test
fun testDesktopTask_copy_isEqual() {
- val task1 = DesktopTask(createTasks(1))
+ val task1 = DesktopTask(deskId = 0, createTasks(1))
val task2 = task1.copy()
assertThat(task1).isEqualTo(task2)
}
@Test
- fun testDesktopTask_differentId_isNotEqual() {
- val task1 = DesktopTask(createTasks(1))
- val task2 = DesktopTask(createTasks(2))
+ fun testDesktopTask_differentDeskIds_isNotEqual() {
+ val task1 = DesktopTask(deskId = 0, createTasks(1))
+ val task2 = DesktopTask(deskId = 1, createTasks(1))
+ assertThat(task1).isNotEqualTo(task2)
+ }
+
+ @Test
+ fun testDesktopTask_differentTaskIds_isNotEqual() {
+ val task1 = DesktopTask(deskId = 0, createTasks(1))
+ val task2 = DesktopTask(deskId = 0, createTasks(2))
assertThat(task1).isNotEqualTo(task2)
}
@Test
fun testDesktopTask_differentLength_isNotEqual() {
- val task1 = DesktopTask(createTasks(1))
- val task2 = DesktopTask(createTasks(1, 2))
+ val task1 = DesktopTask(deskId = 0, createTasks(1))
+ val task2 = DesktopTask(deskId = 0, createTasks(1, 2))
assertThat(task1).isNotEqualTo(task2)
}
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/util/GroupTaskTest.kt b/quickstep/tests/multivalentTests/src/com/android/quickstep/util/GroupTaskTest.kt
index fa043b9..67fc62f 100644
--- a/quickstep/tests/multivalentTests/src/com/android/quickstep/util/GroupTaskTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/util/GroupTaskTest.kt
@@ -98,7 +98,7 @@
@Test
fun testGroupTask_differentType_isNotEqual() {
val task1 = SingleTask(createTask(1))
- val task2 = DesktopTask(listOf(createTask(1)))
+ val task2 = DesktopTask(deskId = 0, listOf(createTask(1)))
assertThat(task1).isNotEqualTo(task2)
}
diff --git a/res/drawable/private_space_install_app_icon.xml b/res/drawable/private_space_install_app_icon.xml
index cfec2b1..1e7fe43 100644
--- a/res/drawable/private_space_install_app_icon.xml
+++ b/res/drawable/private_space_install_app_icon.xml
@@ -13,19 +13,7 @@
~ 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="60dp"
- android:height="60dp"
- android:viewportWidth="60"
- android:viewportHeight="60">
- <group>
- <clip-path
- android:pathData="M30 0H30A30 30 0 0 1 60 30V30A30 30 0 0 1 30 60H30A30 30 0 0 1 0 30V30A30 30 0 0 1 30 0Z" />
- <path
- android:pathData="M30 0H30A30 30 0 0 1 60 30V30A30 30 0 0 1 30 60H30A30 30 0 0 1 0 30V30A30 30 0 0 1 30 0Z"
- android:fillColor="@color/material_color_surface_container_lowest" />
- <path
- android:pathData="M29 31h-6v-2h6v-6h2v6h6v2h-6v6h-2v-6Z"
- android:fillColor="@color/material_color_on_surface" />
- </group>
-</vector>
+<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
+ <background android:drawable="@color/material_color_surface_container_lowest"/>
+ <foreground android:drawable="@drawable/private_space_install_app_icon_foreground" />
+</adaptive-icon>
diff --git a/res/drawable/private_space_install_app_icon_foreground.xml b/res/drawable/private_space_install_app_icon_foreground.xml
new file mode 100644
index 0000000..d55abe7
--- /dev/null
+++ b/res/drawable/private_space_install_app_icon_foreground.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2025 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="60dp"
+ android:height="60dp"
+ android:viewportWidth="60"
+ android:viewportHeight="60">
+ <path
+ android:pathData="M29 31h-6v-2h6v-6h2v6h6v2h-6v6h-2v-6Z"
+ android:fillColor="@color/material_color_on_surface" />
+</vector>
diff --git a/res/values-bg/strings.xml b/res/values-bg/strings.xml
index 6d44a97..98571add 100644
--- a/res/values-bg/strings.xml
+++ b/res/values-bg/strings.xml
@@ -62,7 +62,7 @@
<string name="widget_button_text" msgid="2880537293434387943">"Приспособления"</string>
<string name="widgets_full_sheet_search_bar_hint" msgid="8484659090860596457">"Търсене"</string>
<string name="widgets_full_sheet_cancel_button_description" msgid="5766167035728653605">"Изчистване на текста от полето за търсене"</string>
- <string name="no_widgets_available" msgid="4337693382501046170">"Няма налице преки пътища и приспособления"</string>
+ <string name="no_widgets_available" msgid="4337693382501046170">"Няма налични преки пътища и приспособления"</string>
<string name="no_search_results" msgid="3787956167293097509">"Няма открити преки пътища или приспособления"</string>
<string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"Лични"</string>
<string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"Служебни"</string>
diff --git a/res/values-bs/strings.xml b/res/values-bs/strings.xml
index ff8a541..c0568bf 100644
--- a/res/values-bs/strings.xml
+++ b/res/values-bs/strings.xml
@@ -41,7 +41,7 @@
<string name="long_press_widget_to_add" msgid="3587712543577675817">"Dodirnite i zadržite da pomjerite vidžet."</string>
<string name="long_accessible_way_to_add" msgid="2733588281439571974">"Dvaput dodirnite i zadržite da pomjerite vidžet ili da koristite prilagođene radnje."</string>
<string name="widget_picker_widget_options_button_description" msgid="4770099264476852363">"Više opcija"</string>
- <string name="widget_picker_show_all_widgets_menu_item_title" msgid="9023638224586908119">"Prikazuj sve vidžete"</string>
+ <string name="widget_picker_show_all_widgets_menu_item_title" msgid="9023638224586908119">"Prikaži sve vidžete"</string>
<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>
diff --git a/res/values-cs/strings.xml b/res/values-cs/strings.xml
index 2ac7233..f96b907 100644
--- a/res/values-cs/strings.xml
+++ b/res/values-cs/strings.xml
@@ -46,12 +46,12 @@
<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_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="3811812719618323750">"Nejdůležitější aplikace"</string>
- <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"Zprávy a časopisy"</string>
+ <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"Noviny a časopisy"</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="others_widget_recommendation_category_label" msgid="5555987036267226245">"Návrhy pro vás"</string>
diff --git a/res/values-gu/strings.xml b/res/values-gu/strings.xml
index 4925294..5a33983 100644
--- a/res/values-gu/strings.xml
+++ b/res/values-gu/strings.xml
@@ -60,7 +60,7 @@
<string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# શૉર્ટકટ}one{# શૉર્ટકટ}other{# શૉર્ટકટ}}"</string>
<string name="widgets_and_shortcuts_count" msgid="7209136747878365116">"<xliff:g id="WIDGETS_COUNT">%1$s</xliff:g>, <xliff:g id="SHORTCUTS_COUNT">%2$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"વિજેટ"</string>
- <string name="widgets_full_sheet_search_bar_hint" msgid="8484659090860596457">"શોધ"</string>
+ <string name="widgets_full_sheet_search_bar_hint" msgid="8484659090860596457">"શોધો"</string>
<string name="widgets_full_sheet_cancel_button_description" msgid="5766167035728653605">"શોધ બૉક્સમાંથી ટેક્સ્ટ સાફ કરો"</string>
<string name="no_widgets_available" msgid="4337693382501046170">"વિજેટ અને શૉર્ટકટ ઉપલબ્ધ નથી"</string>
<string name="no_search_results" msgid="3787956167293097509">"કોઈ વિજેટ અથવા શૉર્ટકટ મળ્યા નથી"</string>
diff --git a/res/values-it/strings.xml b/res/values-it/strings.xml
index 75e7cd2..3145133 100644
--- a/res/values-it/strings.xml
+++ b/res/values-it/strings.xml
@@ -46,7 +46,7 @@
<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_item_request_drag_hint" msgid="8730547755622776606">"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>
diff --git a/res/values-ky/strings.xml b/res/values-ky/strings.xml
index b6b5b66..b98b61d 100644
--- a/res/values-ky/strings.xml
+++ b/res/values-ky/strings.xml
@@ -46,7 +46,7 @@
<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_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>
diff --git a/res/values-or/strings.xml b/res/values-or/strings.xml
index 46aadc0..3eb680f 100644
--- a/res/values-or/strings.xml
+++ b/res/values-or/strings.xml
@@ -60,7 +60,7 @@
<string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{#ଟି ସର୍ଟକଟ୍}other{#ଟି ସର୍ଟକଟ୍}}"</string>
<string name="widgets_and_shortcuts_count" msgid="7209136747878365116">"<xliff:g id="WIDGETS_COUNT">%1$s</xliff:g>, <xliff:g id="SHORTCUTS_COUNT">%2$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"ୱିଜେଟ୍"</string>
- <string name="widgets_full_sheet_search_bar_hint" msgid="8484659090860596457">"ସନ୍ଧାନ କରନ୍ତୁ"</string>
+ <string name="widgets_full_sheet_search_bar_hint" msgid="8484659090860596457">"ସର୍ଚ୍ଚ କରନ୍ତୁ"</string>
<string name="widgets_full_sheet_cancel_button_description" msgid="5766167035728653605">"ସନ୍ଧାନ ବାକ୍ସରୁ ଟେକ୍ସଟ୍ ଖାଲି କରନ୍ତୁ"</string>
<string name="no_widgets_available" msgid="4337693382501046170">"ୱିଜେଟ୍ ଏବଂ ସର୍ଟକଟଗୁଡ଼ିକ ଉପଲବ୍ଧ ନାହିଁ"</string>
<string name="no_search_results" msgid="3787956167293097509">"କୌଣସି ୱିଜେଟ କିମ୍ବା ସର୍ଟକଟ ମିଳିଲା ନାହିଁ"</string>
@@ -164,7 +164,7 @@
<string name="action_add_to_workspace" msgid="215894119683164916">"ହୋମ ସ୍କ୍ରିନରେ ଯୋଗ କରନ୍ତୁ"</string>
<string name="action_move_here" msgid="2170188780612570250">"ଆଇଟମ୍କୁ ଏଠାକୁ ଘୁଞ୍ଚାନ୍ତୁ"</string>
<string name="item_removed" msgid="851119963877842327">"ଆଇଟମକୁ କାଢ଼ି ଦିଆଯାଇଛି"</string>
- <string name="undo" msgid="4151576204245173321">"ପୂର୍ବବତ କରନ୍ତୁ"</string>
+ <string name="undo" msgid="4151576204245173321">"ଅନଡୁ କରନ୍ତୁ"</string>
<string name="action_move" msgid="4339390619886385032">"ଆଇଟମ୍ ଘୁଞ୍ଚାନ୍ତୁ"</string>
<string name="move_to_empty_cell_description" msgid="5254852678218206889">"<xliff:g id="STRING">%3$s</xliff:g>ରେ ଧାଡି <xliff:g id="NUMBER_0">%1$s</xliff:g> ସ୍ତମ୍ଭ <xliff:g id="NUMBER_1">%2$s</xliff:g>କୁ ମୁଭ କରନ୍ତୁ"</string>
<string name="move_to_position" msgid="6750008980455459790">"<xliff:g id="NUMBER">%1$s</xliff:g> ସ୍ଥିତିକୁ ନିଅନ୍ତୁ"</string>
diff --git a/res/values-te/strings.xml b/res/values-te/strings.xml
index 4145fb6..4b25912 100644
--- a/res/values-te/strings.xml
+++ b/res/values-te/strings.xml
@@ -64,8 +64,8 @@
<string name="widgets_full_sheet_cancel_button_description" msgid="5766167035728653605">"సెర్చ్ బాక్స్ నుండి టెక్స్ట్ను క్లియర్ చేయండి"</string>
<string name="no_widgets_available" msgid="4337693382501046170">"విడ్జెట్లు, షార్ట్కట్లు అందుబాటులో లేవు"</string>
<string name="no_search_results" msgid="3787956167293097509">"విడ్జెట్లు లేదా షార్ట్కట్లు కనుగొనబడలేదు"</string>
- <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"వ్యక్తిగత గ్యాడ్జెట్స్"</string>
- <string name="widgets_full_sheet_work_tab" msgid="3767150027110633765">"ఆఫీస్"</string>
+ <string name="widgets_full_sheet_personal_tab" msgid="2743540105607120182">"వ్యక్తిగత విడ్జెట్స్"</string>
+ <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_cell_tap_to_show_add_button_label" msgid="4354194214317043581">"యాడ్ చేసే (జోడించే) బటన్ను చూపండి"</string>
diff --git a/res/values-tr/strings.xml b/res/values-tr/strings.xml
index cb47400..3fdae59 100644
--- a/res/values-tr/strings.xml
+++ b/res/values-tr/strings.xml
@@ -31,7 +31,7 @@
<string name="recent_task_option_split_screen" msgid="6690461455618725183">"Bölünmüş ekran"</string>
<string name="split_app_info_accessibility" msgid="5475288491241414932">"%1$s uygulama bilgileri"</string>
<string name="split_app_usage_settings" msgid="7214375263347964093">"%1$s ile ilgili kullanım ayarları"</string>
- <string name="new_window_option_taskbar" msgid="6448780542727767211">"Yeni Pencere"</string>
+ <string name="new_window_option_taskbar" msgid="6448780542727767211">"Yeni pencere"</string>
<string name="manage_windows_option_taskbar" msgid="2294109489960654212">"Pencereleri yönet"</string>
<string name="save_app_pair" msgid="5647523853662686243">"Uygulama çiftini kaydedin"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
diff --git a/res/values-ur/strings.xml b/res/values-ur/strings.xml
index ed002ba..855a8c3 100644
--- a/res/values-ur/strings.xml
+++ b/res/values-ur/strings.xml
@@ -32,7 +32,7 @@
<string name="split_app_info_accessibility" msgid="5475288491241414932">"%1$s کے لیے ایپ کی معلومات"</string>
<string name="split_app_usage_settings" msgid="7214375263347964093">"%1$s کیلئے استعمال کی ترتیبات"</string>
<string name="new_window_option_taskbar" msgid="6448780542727767211">"نئی ونڈو"</string>
- <string name="manage_windows_option_taskbar" msgid="2294109489960654212">"Windows کا نظم کریں"</string>
+ <string name="manage_windows_option_taskbar" msgid="2294109489960654212">"ونڈوز کا نظم کریں"</string>
<string name="save_app_pair" msgid="5647523853662686243">"ایپس کے جوڑے کو محفوظ کریں"</string>
<string name="app_pair_default_title" msgid="4045241727446873529">"<xliff:g id="APP1">%1$s</xliff:g> | <xliff:g id="APP2">%2$s</xliff:g>"</string>
<string name="app_pair_unlaunchable_at_screen_size" msgid="3446551575502685376">"ایپس کا یہ جوڑا اس آلے پر تعاون یافتہ نہیں ہے"</string>
diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml
index cecfedb..febc8c2 100644
--- a/res/values-zh-rCN/strings.xml
+++ b/res/values-zh-rCN/strings.xml
@@ -127,7 +127,7 @@
<string name="folder_name_format_overflow" msgid="4270108890534995199">"文件夹:<xliff:g id="NAME">%1$s</xliff:g>,<xliff:g id="SIZE">%2$d</xliff:g> 个或更多项目"</string>
<string name="unnamed_folder" msgid="2420192029474044442">"未命名文件夹"</string>
<string name="app_pair_name_format" msgid="8134106404716224054">"应用对:“<xliff:g id="APP1">%1$s</xliff:g>”和“<xliff:g id="APP2">%2$s</xliff:g>”"</string>
- <string name="styles_wallpaper_button_text" msgid="8216961355289236794">"壁纸与个性化"</string>
+ <string name="styles_wallpaper_button_text" msgid="8216961355289236794">"壁纸与风格"</string>
<string name="edit_home_screen" msgid="8947858375782098427">"修改主屏幕"</string>
<string name="settings_button_text" msgid="8873672322605444408">"主屏幕设置"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"已被您的管理员停用"</string>
diff --git a/res/values/id.xml b/res/values/id.xml
index 67692d8..78b8308 100644
--- a/res/values/id.xml
+++ b/res/values/id.xml
@@ -79,6 +79,7 @@
<item type="id" name="saved_clip_children_tag_id" />
<item type="id" name="saved_clip_to_padding_tag_id" />
+ <item type="id" name="perform_a11y_action_on_launcher_state_normal_tag" />
<item type="id" name="saved_floating_widget_foreground" />
<item type="id" name="saved_floating_widget_background" />
diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java
index 257f911..0ce966b 100644
--- a/src/com/android/launcher3/CellLayout.java
+++ b/src/com/android/launcher3/CellLayout.java
@@ -16,7 +16,6 @@
package com.android.launcher3;
-import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.dragndrop.DraggableView.DRAGGABLE_ICON;
import static com.android.launcher3.icons.IconNormalizer.ICON_VISIBLE_AREA_FACTOR;
import static com.android.launcher3.util.MultiTranslateDelegate.INDEX_REORDER_PREVIEW_OFFSET;
@@ -71,7 +70,6 @@
import com.android.launcher3.folder.PreviewBackground;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.LauncherAppWidgetInfo;
-import com.android.launcher3.statemanager.StateManager;
import com.android.launcher3.util.CellAndSpan;
import com.android.launcher3.util.GridOccupancy;
import com.android.launcher3.util.MSDLPlayerWrapper;
@@ -791,18 +789,8 @@
// Whenever an app is added, if Accessibility service is enabled, focus on that app.
if (mActivity instanceof Launcher) {
- Launcher.cast(mActivity).getStateManager().addStateListener(
- new StateManager.StateListener<LauncherState>() {
- @Override
- public void onStateTransitionComplete(LauncherState finalState) {
- if (finalState == NORMAL) {
- child.performAccessibilityAction(
- AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS, null);
- Launcher.cast(mActivity).getStateManager()
- .removeStateListener(this);
- }
- }
- });
+ child.setTag(R.id.perform_a11y_action_on_launcher_state_normal_tag,
+ AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS);
}
if (markCells) markCellsAsOccupiedForView(child);
diff --git a/src/com/android/launcher3/LauncherModel.kt b/src/com/android/launcher3/LauncherModel.kt
index 33b63ce..892a218 100644
--- a/src/com/android/launcher3/LauncherModel.kt
+++ b/src/com/android/launcher3/LauncherModel.kt
@@ -328,7 +328,7 @@
launcherBinder.bindWidgets()
return true
} else {
- mLoaderTask =
+ val task =
LoaderTask(
appProvider.get(),
mBgAllAppsList,
@@ -337,10 +337,11 @@
launcherBinder,
widgetsFilterDataProvider,
)
+ mLoaderTask = task
// Always post the loader task, instead of running directly
// (even on same thread) so that we exit any nested synchronized blocks
- MODEL_EXECUTOR.post(mLoaderTask)
+ MODEL_EXECUTOR.post(task)
}
}
}
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index cfb1161..94ff441 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -53,6 +53,7 @@
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
+import android.os.Bundle;
import android.os.Parcelable;
import android.util.AttributeSet;
import android.util.Log;
@@ -299,6 +300,17 @@
private final StatsLogManager mStatsLogManager;
private final MSDLPlayerWrapper mMSDLPlayerWrapper;
+
+ private final StateManager.StateListener<LauncherState> mAccessibilityDropListener =
+ new StateListener<>() {
+ @Override
+ public void onStateTransitionComplete(LauncherState finalState) {
+ if (finalState == NORMAL) {
+ performAccessibilityActionOnViewTree(Workspace.this);
+ }
+ }
+ };
+
@Nullable
private DragController.DragListener mAccessibilityDragListener;
@@ -1454,11 +1466,13 @@
super.onAttachedToWindow();
mWallpaperOffset.setWindowToken(getWindowToken());
computeScroll();
+ mLauncher.getStateManager().addStateListener(mAccessibilityDropListener);
}
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
mWallpaperOffset.setWindowToken(null);
+ mLauncher.getStateManager().removeStateListener(mAccessibilityDropListener);
}
@Override
@@ -2239,16 +2253,8 @@
// the order of operations in this method related to the StateListener below, please
// test that accessibility moves retain focus after accessibility dropping an item.
// Accessibility focus must be requested after launcher is back to a normal state
- mLauncher.getStateManager().addStateListener(new StateListener<LauncherState>() {
- @Override
- public void onStateTransitionComplete(LauncherState finalState) {
- if (finalState == NORMAL) {
- cell.performAccessibilityAction(
- AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS, null);
- mLauncher.getStateManager().removeStateListener(this);
- }
- }
- });
+ cell.setTag(R.id.perform_a11y_action_on_launcher_state_normal_tag,
+ AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS);
}
}
@@ -3580,4 +3586,22 @@
onEndStateTransition();
}
}
+
+ /**
+ * Recursively check view tag {@link R.id.perform_a11y_action_on_launcher_state_normal_tag} and
+ * call {@link View#performAccessibilityAction(int, Bundle)} on view tree. The tag is cleared
+ * after this call.
+ */
+ private static void performAccessibilityActionOnViewTree(View view) {
+ Object tag = view.getTag(R.id.perform_a11y_action_on_launcher_state_normal_tag);
+ if (tag instanceof Integer) {
+ view.performAccessibilityAction((int) tag, null);
+ view.setTag(R.id.perform_a11y_action_on_launcher_state_normal_tag, null);
+ }
+ if (view instanceof ViewGroup viewgroup) {
+ for (int i = 0; i < viewgroup.getChildCount(); i++) {
+ performAccessibilityActionOnViewTree(viewgroup.getChildAt(i));
+ }
+ }
+ }
}
diff --git a/src/com/android/launcher3/graphics/GridCustomizationsProxy.java b/src/com/android/launcher3/graphics/GridCustomizationsProxy.java
index 01c9d7e..40863c4 100644
--- a/src/com/android/launcher3/graphics/GridCustomizationsProxy.java
+++ b/src/com/android/launcher3/graphics/GridCustomizationsProxy.java
@@ -24,11 +24,9 @@
import android.content.ContentValues;
import android.content.Context;
-import android.content.pm.PackageManager;
import android.database.Cursor;
import android.database.MatrixCursor;
import android.net.Uri;
-import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder.DeathRecipient;
@@ -297,12 +295,6 @@
@Override
public Bundle call(@NonNull String method, String arg, Bundle extras) {
- if (mContext.checkPermission("android.permission.BIND_WALLPAPER",
- Binder.getCallingPid(), Binder.getCallingUid())
- != PackageManager.PERMISSION_GRANTED) {
- return null;
- }
-
if (METHOD_GET_PREVIEW.equals(method)) {
return getPreview(extras);
} else {
@@ -377,7 +369,9 @@
if (Flags.newCustomizationPickerUi()
&& com.android.launcher3.Flags.enableLauncherIconShapes()) {
String shapeKey = message.getData().getString(KEY_SHAPE_KEY);
- renderer.updateShape(shapeKey);
+ if (!TextUtils.isEmpty(shapeKey)) {
+ renderer.updateShape(shapeKey);
+ }
}
break;
case MESSAGE_ID_UPDATE_GRID:
diff --git a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
index 332a6bb..740b87b 100644
--- a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
+++ b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
@@ -28,6 +28,7 @@
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT;
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION;
import static com.android.launcher3.Utilities.SHOULD_SHOW_FIRST_PAGE_WIDGET;
+import static com.android.launcher3.graphics.ThemeManager.PREF_ICON_SHAPE;
import static com.android.launcher3.model.ModelUtils.currentScreenContentFilter;
import android.app.Fragment;
@@ -137,12 +138,13 @@
private final String mPrefName;
- public PreviewContext(Context base, String gridName) {
+ public PreviewContext(Context base, String gridName, String shapeKey) {
super(base);
mPrefName = "preview-" + UUID.randomUUID().toString();
LauncherPrefs prefs =
new ProxyPrefs(this, getSharedPreferences(mPrefName, MODE_PRIVATE));
prefs.put(GRID_NAME, gridName);
+ prefs.put(PREF_ICON_SHAPE, shapeKey);
initDaggerComponent(
DaggerLauncherPreviewRenderer_PreviewAppComponent.builder().bindPrefs(prefs));
}
diff --git a/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java b/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java
index 6fe5804..4dd9c5b 100644
--- a/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java
+++ b/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java
@@ -73,7 +73,6 @@
import java.util.ArrayList;
import java.util.Map;
-import java.util.Objects;
import java.util.concurrent.TimeUnit;
/** Render preview using surface view. */
@@ -120,6 +119,7 @@
if (mGridName == null) {
mGridName = LauncherPrefs.get(context).get(GRID_NAME);
}
+ mShapeKey = LauncherPrefs.get(context).get(PREF_ICON_SHAPE);
mWallpaperColors = bundle.getParcelable(KEY_COLORS);
if (Flags.newCustomizationPickerUi()) {
updateColorOverrides(bundle);
@@ -225,8 +225,8 @@
*
* @param shapeKey key for the IconShape model
*/
- public void updateShape(@Nullable String shapeKey) {
- if (Objects.equals(mShapeKey, shapeKey)) {
+ public void updateShape(String shapeKey) {
+ if (shapeKey.equals(mShapeKey)) {
Log.w(TAG, "Preview shape already set, skipping. shape=" + mShapeKey);
return;
}
@@ -332,12 +332,10 @@
private void loadModelData() {
final Context inflationContext = getPreviewContext();
if (!mGridName.equals(LauncherPrefs.INSTANCE.get(mContext).get(GRID_NAME))
- || mShapeKey != null) {
+ || !mShapeKey.equals(LauncherPrefs.INSTANCE.get(mContext).get(PREF_ICON_SHAPE))) {
// Start the migration
- PreviewContext previewContext = new PreviewContext(inflationContext, mGridName);
- if (mShapeKey != null) {
- LauncherPrefs.INSTANCE.get(previewContext).put(PREF_ICON_SHAPE, mShapeKey);
- }
+ PreviewContext previewContext =
+ new PreviewContext(inflationContext, mGridName, mShapeKey);
// Copy existing data to preview DB
LauncherDbUtils.copyTable(LauncherAppState.getInstance(mContext)
.getModel().getModelDbController().getDb(),
diff --git a/src/com/android/launcher3/icons/IconCache.java b/src/com/android/launcher3/icons/IconCache.java
index fc0c1bb..119a6b1 100644
--- a/src/com/android/launcher3/icons/IconCache.java
+++ b/src/com/android/launcher3/icons/IconCache.java
@@ -20,6 +20,7 @@
import static com.android.launcher3.icons.cache.CacheLookupFlag.DEFAULT_LOOKUP_FLAG;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
+import static com.android.launcher3.util.LooperExecutor.CALLER_ICON_CACHE;
import static com.android.launcher3.widget.WidgetSections.NO_CATEGORY;
import static java.util.stream.Collectors.groupingBy;
@@ -35,7 +36,6 @@
import android.database.Cursor;
import android.database.sqlite.SQLiteException;
import android.os.Looper;
-import android.os.Process;
import android.os.Trace;
import android.os.UserHandle;
import android.text.TextUtils;
@@ -204,7 +204,7 @@
Runnable endRunnable;
if (Looper.myLooper() == Looper.getMainLooper()) {
if (mPendingIconRequestCount <= 0) {
- MODEL_EXECUTOR.setThreadPriority(Process.THREAD_PRIORITY_FOREGROUND);
+ MODEL_EXECUTOR.elevatePriority(CALLER_ICON_CACHE);
}
mPendingIconRequestCount++;
endRunnable = this::onIconRequestEnd;
@@ -221,7 +221,7 @@
private void onIconRequestEnd() {
mPendingIconRequestCount--;
if (mPendingIconRequestCount <= 0) {
- MODEL_EXECUTOR.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
+ MODEL_EXECUTOR.restorePriority(CALLER_ICON_CACHE);
}
}
diff --git a/src/com/android/launcher3/logging/FileLog.java b/src/com/android/launcher3/logging/FileLog.java
index 924a440..5b25418 100644
--- a/src/com/android/launcher3/logging/FileLog.java
+++ b/src/com/android/launcher3/logging/FileLog.java
@@ -1,6 +1,6 @@
package com.android.launcher3.logging;
-import static com.android.launcher3.util.Executors.createAndStartNewLooper;
+import static com.android.launcher3.util.LooperExecutor.createAndStartNewLooper;
import android.os.Handler;
import android.os.HandlerThread;
diff --git a/src/com/android/launcher3/model/BaseLauncherBinder.java b/src/com/android/launcher3/model/BaseLauncherBinder.java
index 3ee8b87..a2ca6b6 100644
--- a/src/com/android/launcher3/model/BaseLauncherBinder.java
+++ b/src/com/android/launcher3/model/BaseLauncherBinder.java
@@ -28,7 +28,6 @@
import static java.util.Collections.emptyList;
-import android.os.Process;
import android.os.Trace;
import android.util.Log;
import android.util.Pair;
@@ -352,14 +351,8 @@
onCompleteSignal.executeAllAndDestroy();
}
- executeCallbacksTask(
- c -> {
- if (!enableWorkspaceInflation()) {
- MODEL_EXECUTOR.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
- }
- c.onInitialBindComplete(currentScreenIds, pendingTasks, onCompleteSignal,
- workspaceItemCount, isBindSync);
- }, mUiExecutor);
+ executeCallbacksTask(c -> c.onInitialBindComplete(currentScreenIds, pendingTasks,
+ onCompleteSignal, workspaceItemCount, isBindSync), mUiExecutor);
}
private void setupPendingBind(
@@ -369,12 +362,8 @@
executeCallbacksTask(c -> c.bindStringCache(cacheClone), pendingExecutor);
executeCallbacksTask(c -> c.finishBindingItems(currentScreenIds), pendingExecutor);
- pendingExecutor.execute(
- () -> {
- MODEL_EXECUTOR.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
- ItemInstallQueue.INSTANCE.get(mApp.getContext())
- .resumeModelPush(FLAG_LOADER_RUNNING);
- });
+ pendingExecutor.execute(() -> ItemInstallQueue.INSTANCE.get(mApp.getContext())
+ .resumeModelPush(FLAG_LOADER_RUNNING));
}
/**
diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java
index c1ee69b..d44b289 100644
--- a/src/com/android/launcher3/model/LoaderTask.java
+++ b/src/com/android/launcher3/model/LoaderTask.java
@@ -36,6 +36,7 @@
import static com.android.launcher3.model.ModelUtils.currentScreenContentFilter;
import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_INSTALL_SESSION_ACTIVE;
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
+import static com.android.launcher3.util.LooperExecutor.CALLER_LOADER_TASK;
import static com.android.launcher3.util.PackageManagerHelper.hasShortcutsPermission;
import android.appwidget.AppWidgetProviderInfo;
@@ -251,6 +252,7 @@
}
TraceHelper.INSTANCE.beginSection(TAG);
+ MODEL_EXECUTOR.elevatePriority(CALLER_LOADER_TASK);
LoaderMemoryLogger memoryLogger = new LoaderMemoryLogger();
mIsRestoreFromBackup =
LauncherPrefs.get(mApp.getContext()).get(IS_FIRST_LOAD_AFTER_RESTORE);
@@ -403,6 +405,7 @@
memoryLogger.printLogs();
throw e;
}
+ MODEL_EXECUTOR.restorePriority(CALLER_LOADER_TASK);
TraceHelper.INSTANCE.endSection();
}
diff --git a/src/com/android/launcher3/model/PackageUpdatedTask.java b/src/com/android/launcher3/model/PackageUpdatedTask.java
index d1eceb9..3cdb250 100644
--- a/src/com/android/launcher3/model/PackageUpdatedTask.java
+++ b/src/com/android/launcher3/model/PackageUpdatedTask.java
@@ -239,25 +239,31 @@
boolean isTargetValid = !cn.getClassName().equals(
IconCache.EMPTY_CLASS_NAME);
if (itemInfo.itemType == ITEM_TYPE_DEEP_SHORTCUT) {
+ int requestQuery = ShortcutRequest.PINNED;
+ if (Flags.restoreArchivedShortcuts()) {
+ // Avoid race condition where shortcut service has no record of
+ // unarchived shortcut being pinned after restore.
+ // Launcher should be source-of-truth for if shortcut is pinned.
+ requestQuery = ShortcutRequest.ALL;
+ }
List<ShortcutInfo> shortcut =
new ShortcutRequest(context, mUser)
.forPackage(cn.getPackageName(),
itemInfo.getDeepShortcutId())
- .query(ShortcutRequest.PINNED);
- if (shortcut.isEmpty()
- && !(Flags.restoreArchivedShortcuts()
- && !itemInfo.isArchived())
- ) {
+ .query(requestQuery);
+ if (shortcut.isEmpty()) {
isTargetValid = false;
if (DEBUG) {
- Log.d(TAG, "Pinned Shortcut not found for updated"
- + " package=" + itemInfo.getTargetPackage());
- }
- } else if (!shortcut.isEmpty()) {
- if (DEBUG) {
- Log.d(TAG, "Found pinned shortcut for updated"
+ Log.d(TAG, "Shortcut not found for updated"
+ " package=" + itemInfo.getTargetPackage()
- + ", isTargetValid=" + isTargetValid);
+ + ", isArchived=" + itemInfo.isArchived());
+ }
+ } else {
+ if (DEBUG) {
+ Log.d(TAG, "Found shortcut for updated"
+ + " package=" + itemInfo.getTargetPackage()
+ + ", isTargetValid=" + isTargetValid
+ + ", isArchived=" + itemInfo.isArchived());
}
itemInfo.updateFromDeepShortcutInfo(shortcut.get(0), context);
infoUpdated = true;
diff --git a/src/com/android/launcher3/util/Executors.java b/src/com/android/launcher3/util/Executors.java
index c622b71..296fc8a 100644
--- a/src/com/android/launcher3/util/Executors.java
+++ b/src/com/android/launcher3/util/Executors.java
@@ -16,8 +16,8 @@
package com.android.launcher3.util;
import static android.os.Process.THREAD_PRIORITY_BACKGROUND;
+import static android.os.Process.THREAD_PRIORITY_FOREGROUND;
-import android.os.HandlerThread;
import android.os.Looper;
import android.os.Process;
@@ -51,21 +51,20 @@
/**
* An {@link LooperExecutor} to be used with async task where order is important.
*/
- public static final LooperExecutor ORDERED_BG_EXECUTOR = new LooperExecutor(
- createAndStartNewLooper("BackgroundExecutor", THREAD_PRIORITY_BACKGROUND));
+ public static final LooperExecutor ORDERED_BG_EXECUTOR =
+ new LooperExecutor("BackgroundExecutor", THREAD_PRIORITY_BACKGROUND);
/**
* Returns the executor for running tasks on the main thread.
*/
public static final LooperExecutor MAIN_EXECUTOR =
- new LooperExecutor(Looper.getMainLooper());
+ new LooperExecutor(Looper.getMainLooper(), THREAD_PRIORITY_FOREGROUND);
/**
* A background executor for using time sensitive actions where user is waiting for response.
*/
public static final LooperExecutor UI_HELPER_EXECUTOR =
- new LooperExecutor(
- createAndStartNewLooper("UiThreadHelper", Process.THREAD_PRIORITY_FOREGROUND));
+ new LooperExecutor("UiThreadHelper", Process.THREAD_PRIORITY_FOREGROUND);
/** A background executor to preinflate views. */
@@ -75,26 +74,9 @@
"preinflate-allapps-icons", THREAD_PRIORITY_BACKGROUND));
/**
- * Utility method to get a started handler thread statically
- */
- public static Looper createAndStartNewLooper(String name) {
- return createAndStartNewLooper(name, Process.THREAD_PRIORITY_DEFAULT);
- }
-
- /**
- * Utility method to get a started handler thread statically with the provided priority
- */
- public static Looper createAndStartNewLooper(String name, int priority) {
- HandlerThread thread = new HandlerThread(name, priority);
- thread.start();
- return thread.getLooper();
- }
-
- /**
* Executor used for running Launcher model related tasks (eg loading icons or updated db)
*/
- public static final LooperExecutor MODEL_EXECUTOR =
- new LooperExecutor(createAndStartNewLooper("launcher-loader"));
+ public static final LooperExecutor MODEL_EXECUTOR = new LooperExecutor("launcher-loader");
/**
* Returns and caches a single thread executor for a given package.
@@ -102,9 +84,7 @@
* @param packageName Package associated with the executor.
*/
public static LooperExecutor getPackageExecutor(String packageName) {
- return PACKAGE_EXECUTORS.computeIfAbsent(
- packageName, p -> new LooperExecutor(
- createAndStartNewLooper(p, Process.THREAD_PRIORITY_DEFAULT)));
+ return PACKAGE_EXECUTORS.computeIfAbsent(packageName, LooperExecutor::new);
}
/**
diff --git a/src/com/android/launcher3/util/LooperExecutor.java b/src/com/android/launcher3/util/LooperExecutor.java
deleted file mode 100644
index 3a8a13c..0000000
--- a/src/com/android/launcher3/util/LooperExecutor.java
+++ /dev/null
@@ -1,118 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-package com.android.launcher3.util;
-
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.Looper;
-import android.os.Process;
-
-import java.util.List;
-import java.util.concurrent.AbstractExecutorService;
-import java.util.concurrent.TimeUnit;
-
-/**
- * Extension of {@link AbstractExecutorService} which executed on a provided looper.
- */
-public class LooperExecutor extends AbstractExecutorService {
-
- private final Handler mHandler;
-
- public LooperExecutor(Looper looper) {
- mHandler = new Handler(looper);
- }
-
- public Handler getHandler() {
- return mHandler;
- }
-
- @Override
- public void execute(Runnable runnable) {
- if (getHandler().getLooper() == Looper.myLooper()) {
- runnable.run();
- } else {
- getHandler().post(runnable);
- }
- }
-
- /**
- * Same as execute, but never runs the action inline.
- */
- public void post(Runnable runnable) {
- getHandler().post(runnable);
- }
-
- /**
- * Not supported and throws an exception when used.
- */
- @Override
- @Deprecated
- public void shutdown() {
- throw new UnsupportedOperationException();
- }
-
- /**
- * Not supported and throws an exception when used.
- */
- @Override
- @Deprecated
- public List<Runnable> shutdownNow() {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public boolean isShutdown() {
- return false;
- }
-
- @Override
- public boolean isTerminated() {
- return false;
- }
-
- /**
- * Not supported and throws an exception when used.
- */
- @Override
- @Deprecated
- public boolean awaitTermination(long l, TimeUnit timeUnit) {
- throw new UnsupportedOperationException();
- }
-
- /**
- * Returns the thread for this executor
- */
- public Thread getThread() {
- return getHandler().getLooper().getThread();
- }
-
- /**
- * Returns the looper for this executor
- */
- public Looper getLooper() {
- return getHandler().getLooper();
- }
-
- /**
- * Set the priority of a thread, based on Linux priorities.
- * @param priority Linux priority level, from -20 for highest scheduling priority
- * to 19 for lowest scheduling priority.
- * @see Process#setThreadPriority(int, int)
- */
- public void setThreadPriority(int priority) {
- Process.setThreadPriority(((HandlerThread) getThread()).getThreadId(), priority);
- }
-}
diff --git a/src/com/android/launcher3/util/LooperExecutor.kt b/src/com/android/launcher3/util/LooperExecutor.kt
new file mode 100644
index 0000000..6ff528d
--- /dev/null
+++ b/src/com/android/launcher3/util/LooperExecutor.kt
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+package com.android.launcher3.util
+
+import android.os.Handler
+import android.os.HandlerThread
+import android.os.Looper
+import android.os.Process
+import android.os.Process.THREAD_PRIORITY_FOREGROUND
+import androidx.annotation.IntDef
+import java.util.concurrent.AbstractExecutorService
+import java.util.concurrent.TimeUnit
+import kotlin.annotation.AnnotationRetention.SOURCE
+
+/** Extension of [AbstractExecutorService] which executed on a provided looper. */
+class LooperExecutor(looper: Looper, private val defaultPriority: Int) : AbstractExecutorService() {
+ val handler: Handler = Handler(looper)
+
+ @JvmOverloads
+ constructor(
+ name: String,
+ defaultPriority: Int = Process.THREAD_PRIORITY_DEFAULT,
+ ) : this(createAndStartNewLooper(name, defaultPriority), defaultPriority)
+
+ /** Returns the thread for this executor */
+ val thread: Thread
+ get() = handler.looper.thread
+
+ /** Returns the looper for this executor */
+ val looper: Looper
+ get() = handler.looper
+
+ @ElevationCaller private var elevationFlags: Int = 0
+
+ override fun execute(runnable: Runnable) {
+ if (handler.looper == Looper.myLooper()) {
+ runnable.run()
+ } else {
+ handler.post(runnable)
+ }
+ }
+
+ /** Same as execute, but never runs the action inline. */
+ fun post(runnable: Runnable) {
+ handler.post(runnable)
+ }
+
+ @Deprecated("Not supported and throws an exception when used")
+ override fun shutdown() {
+ throw UnsupportedOperationException()
+ }
+
+ @Deprecated("Not supported and throws an exception when used.")
+ override fun shutdownNow(): List<Runnable> {
+ throw UnsupportedOperationException()
+ }
+
+ override fun isShutdown() = false
+
+ override fun isTerminated() = false
+
+ @Deprecated("Not supported and throws an exception when used.")
+ override fun awaitTermination(l: Long, timeUnit: TimeUnit): Boolean {
+ throw UnsupportedOperationException()
+ }
+
+ /**
+ * Increases the priority of the thread for the [caller]. Multiple calls with same caller are
+ * ignored. The priority is reset once wall callers have restored priority
+ */
+ fun elevatePriority(@ElevationCaller caller: Int) {
+ val wasElevated = elevationFlags != 0
+ elevationFlags = elevationFlags.or(caller)
+ if (elevationFlags != 0 && !wasElevated)
+ Process.setThreadPriority(
+ (thread as HandlerThread).threadId,
+ THREAD_PRIORITY_FOREGROUND,
+ )
+ }
+
+ /** Restores to default priority if it was previously elevated */
+ fun restorePriority(@ElevationCaller caller: Int) {
+ val wasElevated = elevationFlags != 0
+ elevationFlags = elevationFlags.and(caller.inv())
+ if (elevationFlags == 0 && wasElevated)
+ Process.setThreadPriority((thread as HandlerThread).threadId, defaultPriority)
+ }
+
+ @Retention(SOURCE)
+ @IntDef(value = [CALLER_LOADER_TASK, CALLER_ICON_CACHE], flag = true)
+ annotation class ElevationCaller
+
+ companion object {
+ /** Utility method to get a started handler thread statically with the provided priority */
+ @JvmOverloads
+ @JvmStatic
+ fun createAndStartNewLooper(
+ name: String,
+ priority: Int = Process.THREAD_PRIORITY_DEFAULT,
+ ): Looper = HandlerThread(name, priority).apply { start() }.looper
+
+ const val CALLER_LOADER_TASK = 1 shl 0
+ const val CALLER_ICON_CACHE = 1 shl 1
+ }
+}
diff --git a/src/com/android/launcher3/util/SplitConfigurationOptions.java b/src/com/android/launcher3/util/SplitConfigurationOptions.java
index 44a7c6f..e1ef77a 100644
--- a/src/com/android/launcher3/util/SplitConfigurationOptions.java
+++ b/src/com/android/launcher3/util/SplitConfigurationOptions.java
@@ -127,10 +127,10 @@
/** This rect represents the actual gap between the two apps */
public final Rect visualDividerBounds;
// This class is orientation-agnostic, so we compute both for later use
- public final float topTaskPercent;
- public final float leftTaskPercent;
- public final float dividerWidthPercent;
- public final float dividerHeightPercent;
+ private final float topTaskPercent;
+ private final float leftTaskPercent;
+ private final float dividerWidthPercent;
+ private final float dividerHeightPercent;
public final int snapPosition;
/**
@@ -190,6 +190,39 @@
dividerHeightPercent = visualDividerBounds.height() / totalHeight;
}
+ /**
+ * Returns the percentage size of the left/top task (compared to the full width/height of
+ * the split pair). E.g. if the left task is 4 units wide, the divider is 2 units, and the
+ * right task is 4 units, this method will return 0.4f.
+ */
+ public float getLeftTopTaskPercent() {
+ // topTaskPercent and leftTaskPercent are defined at creation time, and are not updated
+ // on device rotate, so we have to check appsStackedVertically to return the right
+ // creation-time measurements.
+ return appsStackedVertically ? topTaskPercent : leftTaskPercent;
+ }
+
+ /**
+ * Returns the percentage size of the divider's thickness (compared to the full width/height
+ * of the split pair). E.g. if the left task is 4 units wide, the divider is 2 units, and
+ * the right task is 4 units, this method will return 0.2f.
+ */
+ public float getDividerPercent() {
+ // dividerHeightPercent and dividerWidthPercent are defined at creation time, and are
+ // not updated on device rotate, so we have to check appsStackedVertically to return
+ // the right creation-time measurements.
+ return appsStackedVertically ? dividerHeightPercent : dividerWidthPercent;
+ }
+
+ /**
+ * Returns the percentage size of the right/bottom task (compared to the full width/height
+ * of the split pair). E.g. if the left task is 4 units wide, the divider is 2 units, and
+ * the right task is 4 units, this method will return 0.4f.
+ */
+ public float getRightBottomTaskPercent() {
+ return 1 - (getLeftTopTaskPercent() + getDividerPercent());
+ }
+
@Override
public String toString() {
return "LeftTop: " + leftTopBounds + ", taskId: " + leftTopTaskId + "\n"
diff --git a/tests/src/com/android/launcher3/ui/workspace/ThemeIconsTest.java b/tests/src/com/android/launcher3/ui/workspace/ThemeIconsTest.java
index a123170..38970fe 100644
--- a/tests/src/com/android/launcher3/ui/workspace/ThemeIconsTest.java
+++ b/tests/src/com/android/launcher3/ui/workspace/ThemeIconsTest.java
@@ -23,7 +23,6 @@
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
-import android.content.ContentProviderClient;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.net.Uri;
@@ -37,6 +36,7 @@
import com.android.launcher3.LauncherState;
import com.android.launcher3.allapps.AllAppsRecyclerView;
import com.android.launcher3.celllayout.FavoriteItemsTransaction;
+import com.android.launcher3.dagger.LauncherComponentProvider;
import com.android.launcher3.icons.mono.ThemedIconDrawable;
import com.android.launcher3.popup.ArrowPopup;
import com.android.launcher3.util.BaseLauncherActivityTest;
@@ -139,7 +139,7 @@
return icon;
}
- private void setThemeEnabled(boolean isEnabled) throws Exception {
+ private void setThemeEnabled(boolean isEnabled) {
Uri uri = new Uri.Builder()
.scheme(ContentResolver.SCHEME_CONTENT)
.authority(targetContext().getPackageName() + ".grid_control")
@@ -147,11 +147,10 @@
.build();
ContentValues values = new ContentValues();
values.put("boolean_value", isEnabled);
- try (ContentProviderClient client = targetContext().getContentResolver()
- .acquireContentProviderClient(uri)) {
- int result = client.update(uri, values, null);
- assertTrue(result > 0);
- }
+
+ int result = LauncherComponentProvider.get(targetContext()).getGridCustomizationsProxy()
+ .update(uri, values, null, null);
+ assertTrue(result > 0);
}
private void switchToAllApps() {