Merge "Revert "Add flag for launcher pill"" into main
diff --git a/Android.bp b/Android.bp
index 9d7aa73..223e2c2 100644
--- a/Android.bp
+++ b/Android.bp
@@ -66,6 +66,8 @@
srcs: [
"quickstep/src/**/*.kt",
"quickstep/src/**/*.java",
+ ],
+ device_common_srcs: [
":launcher-quickstep-processed-protolog-src",
],
}
@@ -90,7 +92,7 @@
],
}
-genrule {
+java_genrule {
name: "launcher-quickstep-processed-protolog-src",
srcs: [
":protolog-impl",
@@ -108,7 +110,7 @@
out: ["launcher.quickstep.protolog.srcjar"],
}
-genrule {
+java_genrule {
name: "gen-launcher.quickstep.protolog.pb",
srcs: [
":launcher-quickstep-unprocessed-protolog-src",
diff --git a/aconfig/launcher.aconfig b/aconfig/launcher.aconfig
index c71b833..d334cf4 100644
--- a/aconfig/launcher.aconfig
+++ b/aconfig/launcher.aconfig
@@ -369,6 +369,13 @@
}
flag {
+ name: "work_scheduler_in_work_profile"
+ namespace: "launcher"
+ description: "Enables work scheduler view above the work pause button in work profile."
+ bug: "361589193"
+}
+
+flag {
name: "one_grid_specs"
namespace: "launcher"
description: "Defines the new specs for grids based on OneGrid"
diff --git a/aconfig/launcher_overview.aconfig b/aconfig/launcher_overview.aconfig
index c59978f..4335f76 100644
--- a/aconfig/launcher_overview.aconfig
+++ b/aconfig/launcher_overview.aconfig
@@ -47,4 +47,11 @@
metadata {
purpose: PURPOSE_BUGFIX
}
+}
+
+flag {
+ name: "enable_desktop_windowing_carousel_detach"
+ namespace: "launcher_overview"
+ description: "Makes the desktop windowing task carousel detaches from fullscreen task carousel during quickswitch."
+ bug: "353947917"
}
\ No newline at end of file
diff --git a/quickstep/res/color/taskbar_minimized_app_indicator_color.xml b/quickstep/res/color/taskbar_minimized_app_indicator_color.xml
new file mode 100644
index 0000000..1596fe1
--- /dev/null
+++ b/quickstep/res/color/taskbar_minimized_app_indicator_color.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2024 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:color="?attr/materialColorOutline"/>
+</selector>
diff --git a/quickstep/res/color/taskbar_running_app_indicator_color.xml b/quickstep/res/color/taskbar_running_app_indicator_color.xml
new file mode 100644
index 0000000..5dc9781
--- /dev/null
+++ b/quickstep/res/color/taskbar_running_app_indicator_color.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2024 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:color="?attr/materialColorTertiary"/>
+</selector>
diff --git a/quickstep/res/values-ar/strings.xml b/quickstep/res/values-ar/strings.xml
index 0d3825f..b699d93 100644
--- a/quickstep/res/values-ar/strings.xml
+++ b/quickstep/res/values-ar/strings.xml
@@ -22,8 +22,7 @@
<string name="recent_task_option_pin" msgid="7929860679018978258">"تثبيت"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"شكل مجاني"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"الكمبيوتر المكتبي"</string>
- <!-- no translation found for recent_task_option_external_display (4533840664313389484) -->
- <skip />
+ <string name="recent_task_option_external_display" msgid="4533840664313389484">"نقل التطبيق إلى شاشة خارجية"</string>
<string name="recent_task_desktop" msgid="8081113562549637334">"كمبيوتر مكتبي"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"ما مِن عناصر تم استخدامها مؤخرًا"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"إعدادات استخدام التطبيق"</string>
diff --git a/quickstep/res/values-ja/strings.xml b/quickstep/res/values-ja/strings.xml
index 890959a..b30b000 100644
--- a/quickstep/res/values-ja/strings.xml
+++ b/quickstep/res/values-ja/strings.xml
@@ -22,8 +22,7 @@
<string name="recent_task_option_pin" msgid="7929860679018978258">"固定"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"フリーフォーム"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"デスクトップ"</string>
- <!-- no translation found for recent_task_option_external_display (4533840664313389484) -->
- <skip />
+ <string name="recent_task_option_external_display" msgid="4533840664313389484">"外部ディスプレイに移動する"</string>
<string name="recent_task_desktop" msgid="8081113562549637334">"パソコン"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"最近のアイテムはありません"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"アプリの使用状況の設定"</string>
diff --git a/quickstep/res/values-mk/strings.xml b/quickstep/res/values-mk/strings.xml
index d5216a5..2634b94 100644
--- a/quickstep/res/values-mk/strings.xml
+++ b/quickstep/res/values-mk/strings.xml
@@ -22,8 +22,7 @@
<string name="recent_task_option_pin" msgid="7929860679018978258">"Закачи"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"Freeform"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"Работна површина"</string>
- <!-- no translation found for recent_task_option_external_display (4533840664313389484) -->
- <skip />
+ <string name="recent_task_option_external_display" msgid="4533840664313389484">"Префрлете се на надворешниот екран"</string>
<string name="recent_task_desktop" msgid="8081113562549637334">"За компјутер"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"Нема неодамнешни ставки"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Поставки за користење на апликациите"</string>
diff --git a/quickstep/res/values-th/strings.xml b/quickstep/res/values-th/strings.xml
index 1f93cb6..99b53d9 100644
--- a/quickstep/res/values-th/strings.xml
+++ b/quickstep/res/values-th/strings.xml
@@ -22,8 +22,7 @@
<string name="recent_task_option_pin" msgid="7929860679018978258">"ปักหมุด"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"รูปแบบอิสระ"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"เดสก์ท็อป"</string>
- <!-- no translation found for recent_task_option_external_display (4533840664313389484) -->
- <skip />
+ <string name="recent_task_option_external_display" msgid="4533840664313389484">"ย้ายไปยังจอแสดงผลภายนอก"</string>
<string name="recent_task_desktop" msgid="8081113562549637334">"เดสก์ท็อป"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"ไม่มีรายการล่าสุด"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"การตั้งค่าการใช้แอป"</string>
diff --git a/quickstep/res/values-vi/strings.xml b/quickstep/res/values-vi/strings.xml
index 2bf4a13..3a32551 100644
--- a/quickstep/res/values-vi/strings.xml
+++ b/quickstep/res/values-vi/strings.xml
@@ -22,8 +22,7 @@
<string name="recent_task_option_pin" msgid="7929860679018978258">"Ghim"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"Dạng tự do"</string>
<string name="recent_task_option_desktop" msgid="8280879717125435668">"Máy tính"</string>
- <!-- no translation found for recent_task_option_external_display (4533840664313389484) -->
- <skip />
+ <string name="recent_task_option_external_display" msgid="4533840664313389484">"Chuyển sang màn hình ngoài"</string>
<string name="recent_task_desktop" msgid="8081113562549637334">"Máy tính"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"Không có mục gần đây nào"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Cài đặt mức sử dụng ứng dụng"</string>
diff --git a/quickstep/res/values/config.xml b/quickstep/res/values/config.xml
index db5ff19..3ae2b89 100644
--- a/quickstep/res/values/config.xml
+++ b/quickstep/res/values/config.xml
@@ -38,7 +38,6 @@
<string name="nav_handle_long_press_handler_class" translatable="false"></string>
<string name="contextual_search_invoker_class" translatable="false"></string>
<string name="contextual_search_state_manager_class" translatable="false"></string>
- <string name="api_wrapper_class" translatable="false">com.android.launcher3.uioverrides.SystemApiWrapper</string>
<!-- The number of thumbnails and icons to keep in the cache. The thumbnail cache size also
determines how many thumbnails will be fetched in the background. -->
diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml
index 451ba55..782a705 100644
--- a/quickstep/res/values/dimens.xml
+++ b/quickstep/res/values/dimens.xml
@@ -111,6 +111,7 @@
<dimen name="motion_pause_detector_speed_very_slow">0.0285dp</dimen>
<dimen name="motion_pause_detector_speed_slow">0.15dp</dimen>
<dimen name="motion_pause_detector_speed_somewhat_fast">0.285dp</dimen>
+ <dimen name="motion_pause_detector_speed_trackpad_somewhat_fast">0.7dp</dimen>
<dimen name="motion_pause_detector_speed_fast">1.4dp</dimen>
<dimen name="motion_pause_detector_min_displacement_from_app">36dp</dimen>
<dimen name="quickstep_fling_threshold_speed">0.5dp</dimen>
diff --git a/quickstep/src/com/android/launcher3/desktop/DesktopAppLaunchTransition.kt b/quickstep/src/com/android/launcher3/desktop/DesktopAppLaunchTransition.kt
index dd2ff2d..6916a1d 100644
--- a/quickstep/src/com/android/launcher3/desktop/DesktopAppLaunchTransition.kt
+++ b/quickstep/src/com/android/launcher3/desktop/DesktopAppLaunchTransition.kt
@@ -32,6 +32,8 @@
import androidx.core.animation.addListener
import com.android.app.animation.Interpolators
import com.android.quickstep.RemoteRunnable
+import com.android.wm.shell.shared.animation.MinimizeAnimator
+import com.android.wm.shell.shared.animation.WindowAnimator
import java.util.concurrent.Executor
/**
@@ -77,7 +79,13 @@
val launchAnimator =
createLaunchAnimator(getLaunchChange(info), transaction, finishCallback)
val minimizeChange = getMinimizeChange(info) ?: return listOf(launchAnimator)
- val minimizeAnimator = createMinimizeAnimator(minimizeChange, transaction, finishCallback)
+ val minimizeAnimator =
+ MinimizeAnimator.create(
+ context.resources.displayMetrics,
+ minimizeChange,
+ transaction,
+ finishCallback,
+ )
return listOf(launchAnimator, minimizeAnimator)
}
@@ -96,7 +104,7 @@
): Animator {
val boundsAnimator =
WindowAnimator.createBoundsAnimator(
- context,
+ context.resources.displayMetrics,
launchBoundsAnimationDef,
change,
transaction,
@@ -115,32 +123,6 @@
}
}
- private fun createMinimizeAnimator(
- change: Change,
- transaction: Transaction,
- onAnimFinish: (Animator) -> Unit,
- ): Animator {
- val boundsAnimator =
- WindowAnimator.createBoundsAnimator(
- context,
- minimizeBoundsAnimationDef,
- change,
- transaction,
- )
- val alphaAnimator =
- ValueAnimator.ofFloat(1f, 0f).apply {
- duration = MINIMIZE_ANIM_ALPHA_DURATION_MS
- interpolator = Interpolators.LINEAR
- addUpdateListener { animation ->
- transaction.setAlpha(change.leash, animation.animatedValue as Float).apply()
- }
- }
- return AnimatorSet().apply {
- playTogether(boundsAnimator, alphaAnimator)
- addListener(onEnd = { animation -> onAnimFinish(animation) })
- }
- }
-
companion object {
private val LAUNCH_CHANGE_MODES = intArrayOf(TRANSIT_OPEN, TRANSIT_TO_FRONT)
@@ -154,13 +136,5 @@
startScale = 0.97f,
interpolator = Interpolators.STANDARD_DECELERATE,
)
-
- private val minimizeBoundsAnimationDef =
- WindowAnimator.BoundsAnimationParams(
- durationMs = 200,
- endOffsetYDp = 12f,
- endScale = 0.97f,
- interpolator = Interpolators.STANDARD_ACCELERATE,
- )
}
}
diff --git a/quickstep/src/com/android/launcher3/desktop/WindowAnimator.kt b/quickstep/src/com/android/launcher3/desktop/WindowAnimator.kt
deleted file mode 100644
index 1a99a36..0000000
--- a/quickstep/src/com/android/launcher3/desktop/WindowAnimator.kt
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher3.desktop
-
-import android.animation.RectEvaluator
-import android.animation.ValueAnimator
-import android.content.Context
-import android.graphics.Rect
-import android.util.TypedValue
-import android.view.SurfaceControl
-import android.view.animation.Interpolator
-import android.window.TransitionInfo
-
-/** Creates animations that can be applied to windows/surfaces. */
-object WindowAnimator {
-
- /** Parameters defining a window bounds animation. */
- data class BoundsAnimationParams(
- val durationMs: Long,
- val startOffsetYDp: Float = 0f,
- val endOffsetYDp: Float = 0f,
- val startScale: Float = 1f,
- val endScale: Float = 1f,
- val interpolator: Interpolator,
- )
-
- /**
- * Creates an animator to reposition and scale the bounds of the leash of the given change.
- *
- * @param boundsAnimDef the parameters for the animation itself (duration, scale, position)
- * @param change the change to which the animation should be applied
- * @param transaction the transaction to apply the animation to
- */
- fun createBoundsAnimator(
- context: Context,
- boundsAnimDef: BoundsAnimationParams,
- change: TransitionInfo.Change,
- transaction: SurfaceControl.Transaction,
- ): ValueAnimator {
- val startBounds =
- createBounds(
- context,
- change.startAbsBounds,
- boundsAnimDef.startScale,
- boundsAnimDef.startOffsetYDp,
- )
- val leash = change.leash
- val endBounds =
- createBounds(
- context,
- change.startAbsBounds,
- boundsAnimDef.endScale,
- boundsAnimDef.endOffsetYDp,
- )
- return ValueAnimator.ofObject(RectEvaluator(), startBounds, endBounds).apply {
- duration = boundsAnimDef.durationMs
- interpolator = boundsAnimDef.interpolator
- addUpdateListener { animation ->
- val animBounds = animation.animatedValue as Rect
- val animScale = 1 - (1 - boundsAnimDef.endScale) * animation.animatedFraction
- transaction
- .setPosition(leash, animBounds.left.toFloat(), animBounds.top.toFloat())
- .setScale(leash, animScale, animScale)
- .apply()
- }
- }
- }
-
- private fun createBounds(context: Context, origBounds: Rect, scale: Float, offsetYDp: Float) =
- Rect(origBounds).apply {
- check(scale in 0.0..1.0)
- // Scale the bounds down with an anchor in the center
- inset(
- (origBounds.width().toFloat() * (1 - scale) / 2).toInt(),
- (origBounds.height().toFloat() * (1 - scale) / 2).toInt(),
- )
- val offsetYPx =
- TypedValue.applyDimension(
- TypedValue.COMPLEX_UNIT_DIP,
- offsetYDp,
- context.resources.displayMetrics,
- )
- .toInt()
- offset(/* dx= */ 0, offsetYPx)
- }
-}
diff --git a/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java b/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java
index 29e1f4e..2f4c6f6 100644
--- a/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java
+++ b/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java
@@ -59,7 +59,6 @@
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherPrefs;
-import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.logger.LauncherAtom;
import com.android.launcher3.logging.InstanceId;
import com.android.launcher3.logging.InstanceIdSequence;
@@ -156,9 +155,6 @@
state.containerId);
FixedContainerItems fci = new FixedContainerItems(state.containerId,
state.storage.read(mApp.getContext(), factory, ums.allUsers::get));
- if (FeatureFlags.CHANGE_MODEL_DELEGATE_LOADING_ORDER.get()) {
- bindPredictionItems(callbacks, fci);
- }
mDataModel.extraItems.put(state.containerId, fci);
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchController.java b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchController.java
index 9912c6c..711a49a 100644
--- a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchController.java
@@ -63,6 +63,11 @@
private int mTaskListChangeId = -1;
// Only empty before the recent tasks list has been loaded the first time
@NonNull private List<GroupTask> mTasks = new ArrayList<>();
+ // Set of task IDs filtered out of tasks in recents model to generate list of tasks to show in
+ // the Keyboard Quick Switch view. Non empty only if the view has been shown in response to
+ // toggling taskbar overflow button.
+ @NonNull private Set<Integer> mExcludedTaskIds = Collections.emptySet();
+
private int mNumHiddenTasks = 0;
// Initialized in init
@@ -90,10 +95,12 @@
return;
}
int currentFocusedIndex = mQuickSwitchViewController.getCurrentFocusedIndex();
+ boolean wasOpenedFromTaskbar = mQuickSwitchViewController.wasOpenedFromTaskbar();
onDestroy();
if (currentFocusedIndex != -1) {
mControllers.taskbarActivityContext.getMainThreadHandler().post(
- () -> openQuickSwitchView(currentFocusedIndex));
+ () -> openQuickSwitchView(currentFocusedIndex, mExcludedTaskIds,
+ wasOpenedFromTaskbar));
}
}
@@ -102,10 +109,19 @@
}
/**
- * Opens the view with a filtered list of tasks.
+ * Opens or closes the view in response to taskbar action. The view shows a filtered list of
+ * tasks.
* @param taskIdsToExclude A list of tasks to exclude in the opened view.
*/
- void openQuickSwitchView(@NonNull Set<Integer> taskIdsToExclude) {
+ void toggleQuickSwitchViewForTaskbar(@NonNull Set<Integer> taskIdsToExclude) {
+ // Close the view if its shown, and was opened from the taskbar.
+ if (mQuickSwitchViewController != null
+ && !mQuickSwitchViewController.isCloseAnimationRunning()
+ && mQuickSwitchViewController.wasOpenedFromTaskbar()) {
+ closeQuickSwitchView(true);
+ return;
+ }
+
openQuickSwitchView(-1, taskIdsToExclude, true);
}
@@ -117,10 +133,16 @@
@NonNull Set<Integer> taskIdsToExclude,
boolean wasOpenedFromTaskbar) {
if (mQuickSwitchViewController != null) {
- if (!mQuickSwitchViewController.isCloseAnimationRunning()) {
+ if (!mQuickSwitchViewController.isCloseAnimationRunning()
+ && mQuickSwitchViewController.wasOpenedFromTaskbar() == wasOpenedFromTaskbar) {
return;
}
- // Allow the KQS to be reopened during the close animation to make it more responsive
+
+ // Allow the KQS to be reopened during the close animation to make it more responsive.
+ // Similarly, if KQS was opened in different mode (from taskbar vs. keyboard event),
+ // close it so it can be reopened in the correct mode.
+ // TODO(b/368119679) Consider updating list of shown tasks in place, or at least reopen
+ // the view in the same vertical location.
closeQuickSwitchView(false);
}
mOverlayContext = mControllers.taskbarOverlayController.requestWindow();
@@ -139,9 +161,8 @@
final boolean onDesktop =
mControllers.taskbarDesktopModeController.getAreDesktopTasksVisible();
- // TODO(b/368119679) For now we will re-process the task list every time, but this can be
- // optimized if we have the same set of task ids to exclude.
- if (mModel.isTaskListValid(mTaskListChangeId) && !Flags.taskbarOverflow()) {
+ if (mModel.isTaskListValid(mTaskListChangeId)
+ && taskIdsToExclude.equals(mExcludedTaskIds)) {
// When we are opening the KQS with no focus override, check if the first task is
// running. If not, focus that first task.
mQuickSwitchViewController.openQuickSwitchView(
@@ -157,6 +178,7 @@
return;
}
+ mExcludedTaskIds = taskIdsToExclude;
mTaskListChangeId = mModel.getTasks((tasks) -> {
mHasDesktopTask = false;
mWasDesktopTaskFilteredOut = false;
diff --git a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchView.java b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchView.java
index 50a253c..05d34b5 100644
--- a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchView.java
@@ -131,6 +131,15 @@
}
@Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+
+ if (mViewCallbacks != null) {
+ mViewCallbacks.onViewDetchedFromWindow();
+ }
+ }
+
+ @Override
protected void onFinishInflate() {
super.onFinishInflate();
mNoRecentItemsPane = findViewById(R.id.no_recent_items_pane);
@@ -281,6 +290,10 @@
return mDesktopTaskIndex;
}
+ void resetViewCallbacks() {
+ mViewCallbacks = null;
+ }
+
protected Animator getCloseAnimation() {
AnimatorSet closeAnimation = new AnimatorSet();
diff --git a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchViewController.java b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchViewController.java
index 2902d55..3f71870 100644
--- a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchViewController.java
@@ -69,6 +69,9 @@
private boolean mOnDesktop;
private boolean mWasDesktopTaskFilteredOut;
+ private boolean mWasOpenedFromTaskbar;
+
+ private boolean mDetachingFromWindow = false;
protected KeyboardQuickSwitchViewController(
@NonNull TaskbarControllers controllers,
@@ -85,6 +88,10 @@
return mCurrentFocusIndex;
}
+ protected boolean wasOpenedFromTaskbar() {
+ return mWasOpenedFromTaskbar;
+ }
+
protected void openQuickSwitchView(
@NonNull List<GroupTask> tasks,
int numHiddenTasks,
@@ -98,6 +105,7 @@
mOverlayContext.getDragLayer().addView(mKeyboardQuickSwitchView);
mOnDesktop = onDesktop;
mWasDesktopTaskFilteredOut = wasDesktopTaskFilteredOut;
+ mWasOpenedFromTaskbar = wasOpenedFromTaskbar;
mKeyboardQuickSwitchView.applyLoadPlan(
mOverlayContext,
@@ -237,7 +245,12 @@
private void onCloseComplete() {
mCloseAnimation = null;
- mOverlayContext.getDragLayer().removeView(mKeyboardQuickSwitchView);
+ // Reset the view callbacks to prevent `onDetachedFromWindow` getting called in response to
+ // the `removeView(mKeyboardQuickSwitchView)` call.
+ mKeyboardQuickSwitchView.resetViewCallbacks();
+ if (!mDetachingFromWindow) {
+ mOverlayContext.getDragLayer().removeView(mKeyboardQuickSwitchView);
+ }
mControllerCallbacks.onCloseComplete();
InteractionJankMonitorWrapper.end(Cuj.CUJ_LAUNCHER_KEYBOARD_QUICK_SWITCH_CLOSE);
}
@@ -254,6 +267,7 @@
pw.println(prefix + "\tmCurrentFocusIndex=" + mCurrentFocusIndex);
pw.println(prefix + "\tmOnDesktop=" + mOnDesktop);
pw.println(prefix + "\tmWasDesktopTaskFilteredOut=" + mWasDesktopTaskFilteredOut);
+ pw.println(prefix + "\tmWasOpenedFromTaskbar=" + mWasOpenedFromTaskbar);
}
/**
@@ -327,5 +341,11 @@
boolean isAspectRatioSquare() {
return mControllerCallbacks.isAspectRatioSquare();
}
+
+ void onViewDetchedFromWindow() {
+ mDetachingFromWindow = true;
+ closeQuickSwitchView(false);
+ mDetachingFromWindow = false;
+ }
}
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
index 042bc9a..09dbeb6 100644
--- a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
@@ -36,7 +36,6 @@
import com.android.launcher3.LauncherState;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AnimatedFloat;
-import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.logging.InstanceId;
import com.android.launcher3.logging.InstanceIdSequence;
import com.android.launcher3.model.data.ItemInfo;
@@ -69,17 +68,14 @@
public static final int ALL_APPS_PAGE_PROGRESS_INDEX = 1;
public static final int WIDGETS_PAGE_PROGRESS_INDEX = 2;
public static final int SYSUI_SURFACE_PROGRESS_INDEX = 3;
- public static final int LAUNCHER_PAUSE_PROGRESS_INDEX = 4;
- public static final int DISPLAY_PROGRESS_COUNT = 5;
+ public static final int DISPLAY_PROGRESS_COUNT = 4;
private final AnimatedFloat mTaskbarInAppDisplayProgress = new AnimatedFloat(
this::onInAppDisplayProgressChanged);
private final MultiPropertyFactory<AnimatedFloat> mTaskbarInAppDisplayProgressMultiProp =
new MultiPropertyFactory<>(mTaskbarInAppDisplayProgress,
AnimatedFloat.VALUE, DISPLAY_PROGRESS_COUNT, Float::max);
- private final AnimatedFloat mLauncherPauseProgress = new AnimatedFloat(
- this::launcherPauseProgressUpdate);
private final QuickstepLauncher mLauncher;
private final HomeVisibilityState mHomeState;
@@ -195,33 +191,6 @@
}
/**
- * Called when Launcher Activity is paused/resumed.
- * <p>
- * To avoid UI clash between taskbar & bottom sheet, shift nav buttons down on launcher
- * pause/resume at home.
- * @param paused if launcher is currently paused.
- */
- public void onLauncherPausedOrResumed(boolean paused) {
- if (!FeatureFlags.enableHomeTransitionListener()) {
- onLauncherVisibilityChanged(mLauncher.hasBeenResumed());
- return;
- }
-
- // Animate navbar iff pause/resume from home, NOT to/from app (avoid overriding existing
- // animations).
- boolean launcherPauseOrResumeFromHome = mHomeState.isHomeVisible() && mControllers
- .taskbarAutohideSuspendController.isSuspendedForTransientTaskbarInLauncher();
- if (launcherPauseOrResumeFromHome) {
- mLauncherPauseProgress.animateToValue(paused ? 1.0f : 0.0f).start();
- }
- }
-
- private void launcherPauseProgressUpdate() {
- onTaskbarInAppDisplayProgressUpdate(
- mLauncherPauseProgress.value, LAUNCHER_PAUSE_PROGRESS_INDEX);
- }
-
- /**
* Should be called from onResume() and onPause(), and animates the Taskbar accordingly.
*/
@Override
@@ -395,20 +364,18 @@
}
if (mControllers.uiController.isIconAlignedWithHotseat()
&& !mTaskbarLauncherStateController.isAnimatingToLauncher()) {
- // Only animate nav button position while home and not animating home, otherwise let
+ // Only animate the nav buttons while home and not animating home, otherwise let
// the TaskbarViewController handle it.
mControllers.navbarButtonsViewController
- .getNavButtonTranslationYForInAppDisplay()
+ .getTaskbarNavButtonTranslationYForInAppDisplay()
.updateValue(mLauncher.getDeviceProfile().getTaskbarOffsetY()
* mTaskbarInAppDisplayProgress.value);
- if (!mLauncher.isPaused()) {
- mControllers.navbarButtonsViewController
- .getOnTaskbarBackgroundNavButtonColorOverride().updateValue(progress);
- }
+ mControllers.navbarButtonsViewController
+ .getOnTaskbarBackgroundNavButtonColorOverride().updateValue(progress);
}
}
- @Override
+ /** Returns true iff any in-app display progress > 0. */
public boolean shouldUseInAppLayout() {
return mTaskbarInAppDisplayProgress.value > 0;
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
index cfcbd2f..7d8e93c 100644
--- a/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
@@ -183,7 +183,7 @@
private final AnimatedFloat mTaskbarNavButtonTranslationY = new AnimatedFloat(
this::updateNavButtonTranslationY);
- private final AnimatedFloat mNavButtonTranslationYForInAppDisplay = new AnimatedFloat(
+ private final AnimatedFloat mTaskbarNavButtonTranslationYForInAppDisplay = new AnimatedFloat(
this::updateNavButtonTranslationY);
private final AnimatedFloat mTaskbarNavButtonTranslationYForIme = new AnimatedFloat(
this::updateNavButtonTranslationY);
@@ -704,8 +704,8 @@
}
/** Use to set the translationY for the all nav+contextual buttons when in Launcher */
- public AnimatedFloat getNavButtonTranslationYForInAppDisplay() {
- return mNavButtonTranslationYForInAppDisplay;
+ public AnimatedFloat getTaskbarNavButtonTranslationYForInAppDisplay() {
+ return mTaskbarNavButtonTranslationYForInAppDisplay;
}
/** Use to set the dark intensity for the all nav+contextual buttons */
@@ -751,20 +751,18 @@
if (mContext.isPhoneButtonNavMode()) {
return;
}
- mLastSetNavButtonTranslationY = calculateNavButtonTranslationY();
- mNavButtonsView.setTranslationY(mLastSetNavButtonTranslationY);
- }
+ final float normalTranslationY = mTaskbarNavButtonTranslationY.value;
+ final float imeAdjustmentTranslationY = mTaskbarNavButtonTranslationYForIme.value;
+ TaskbarUIController uiController = mControllers.uiController;
+ final float inAppDisplayAdjustmentTranslationY =
+ (uiController instanceof LauncherTaskbarUIController
+ && ((LauncherTaskbarUIController) uiController).shouldUseInAppLayout())
+ ? mTaskbarNavButtonTranslationYForInAppDisplay.value : 0;
- /**
- * Calculates the translationY of the nav buttons based on the current device state.
- */
- private float calculateNavButtonTranslationY() {
- float translationY =
- mTaskbarNavButtonTranslationY.value + mTaskbarNavButtonTranslationYForIme.value;
- if (mControllers.uiController.shouldUseInAppLayout()) {
- translationY += mNavButtonTranslationYForInAppDisplay.value;
- }
- return translationY;
+ mLastSetNavButtonTranslationY = normalTranslationY
+ + imeAdjustmentTranslationY
+ + inAppDisplayAdjustmentTranslationY;
+ mNavButtonsView.setTranslationY(mLastSetNavButtonTranslationY);
}
/**
@@ -1164,7 +1162,7 @@
pw.println(prefix + "\t\tmTaskbarNavButtonTranslationY="
+ mTaskbarNavButtonTranslationY.value);
pw.println(prefix + "\t\tmTaskbarNavButtonTranslationYForInAppDisplay="
- + mNavButtonTranslationYForInAppDisplay.value);
+ + mTaskbarNavButtonTranslationYForInAppDisplay.value);
pw.println(prefix + "\t\tmTaskbarNavButtonTranslationYForIme="
+ mTaskbarNavButtonTranslationYForIme.value);
pw.println(prefix + "\t\tmTaskbarNavButtonDarkIntensity="
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
index 6f1e96f..e22de06 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
@@ -29,7 +29,6 @@
import static com.android.launcher3.AbstractFloatingView.TYPE_REBIND_SAFE;
import static com.android.launcher3.AbstractFloatingView.TYPE_TASKBAR_OVERLAY_PROXY;
import static com.android.launcher3.Flags.enableCursorHoverStates;
-import static com.android.launcher3.Flags.taskbarOverflow;
import static com.android.launcher3.Utilities.calculateTextHeight;
import static com.android.launcher3.Utilities.isRunningInTestHarness;
import static com.android.launcher3.config.FeatureFlags.ENABLE_TASKBAR_NAVBAR_UNIFICATION;
@@ -1217,9 +1216,7 @@
boolean shouldCloseAllOpenViews = true;
Object tag = view.getTag();
- if (taskbarOverflow()) {
- mControllers.keyboardQuickSwitchController.closeQuickSwitchView(false);
- }
+ mControllers.keyboardQuickSwitchController.closeQuickSwitchView(false);
if (tag instanceof GroupTask groupTask) {
handleGroupTaskLaunch(
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt
index 4a6b6d4..a8ce10f 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt
@@ -21,7 +21,6 @@
import android.graphics.Paint
import android.graphics.Rect
import android.graphics.Region
-import android.inputmethodservice.InputMethodService.ENABLE_HIDE_IME_CAPTION_BAR
import android.os.Binder
import android.os.IBinder
import android.view.DisplayInfo
@@ -150,7 +149,7 @@
}
if (
taskbarStashController.isInApp ||
- taskbarStashController.isInOverview ||
+ controllers.uiController.isInOverviewUi ||
DisplayController.showLockedTaskbarOnHome(context)
) {
// only add the taskbar touch region if not on home
@@ -259,7 +258,7 @@
// When in gesture nav, report the stashed height to the IME, to allow hiding the
// IME navigation bar.
val imeInsetsSize =
- if (ENABLE_HIDE_IME_CAPTION_BAR && context.isGestureNav) {
+ if (context.isGestureNav) {
getInsetsForGravity(controllers.taskbarStashController.stashedHeight, gravity)
} else {
getInsetsForGravity(taskbarHeightForIme, gravity)
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarPinningController.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarPinningController.kt
index 7848b7e..bcfc718 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarPinningController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarPinningController.kt
@@ -30,6 +30,7 @@
import com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_UNPINNED
import com.android.launcher3.taskbar.TaskbarDividerPopupView.Companion.createAndPopulate
import java.io.PrintWriter
+import kotlin.jvm.optionals.getOrNull
/** Controls taskbar pinning through a popup view. */
class TaskbarPinningController(private val context: TaskbarActivityContext) :
@@ -119,7 +120,11 @@
taskbarViewController.taskbarIconScaleForPinning.animateToValue(animateToValue),
taskbarViewController.taskbarIconTranslationXForPinning.animateToValue(animateToValue),
)
-
+ controllers.bubbleControllers.getOrNull()?.bubbleBarViewController?.let {
+ // if bubble bar is not visible no need to add it`s animations
+ if (!it.isBubbleBarVisible) return@let
+ animatorSet.playTogether(it.bubbleBarPinning.animateToValue(animateToValue))
+ }
animatorSet.interpolator = Interpolators.EMPHASIZED
return animatorSet
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
index 7030088..f7f5cf6 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
@@ -91,14 +91,6 @@
protected void onStashedInAppChanged() { }
/**
- * Whether the Taskbar should use in-app layout.
- * @return {@code true} iff in-app display progress > 0 or Launcher Activity paused.
- */
- public boolean shouldUseInAppLayout() {
- return false;
- }
-
- /**
* Called when taskbar icon layout bounds change.
*/
protected void onIconLayoutBoundsChanged() { }
@@ -126,6 +118,8 @@
* Manually closes the overlay window.
*/
public void hideOverlayWindow() {
+ mControllers.keyboardQuickSwitchController.closeQuickSwitchView();
+
if (!DisplayController.isTransientTaskbar(mControllers.taskbarActivityContext)
|| mControllers.taskbarAllAppsController.isOpen()) {
mControllers.taskbarOverlayController.hideWindow();
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
index 6cc03ab..55bcb23 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
@@ -198,11 +198,13 @@
private int calculateMaxNumIcons() {
DeviceProfile deviceProfile = mActivityContext.getDeviceProfile();
int availableWidth = deviceProfile.widthPx;
+ int defaultEdgeMargin =
+ (int) getResources().getDimension(deviceProfile.inv.inlineNavButtonsEndSpacing);
// Reserve space required for edge margins, or for navbar if shown. If task bar needs to be
// center aligned with nav bar shown, reserve space on both sides.
- availableWidth -= Math.max(deviceProfile.edgeMarginPx, deviceProfile.hotseatBarEndOffset);
- availableWidth -= Math.max(deviceProfile.edgeMarginPx,
+ availableWidth -= Math.max(defaultEdgeMargin, deviceProfile.hotseatBarEndOffset);
+ availableWidth -= Math.max(defaultEdgeMargin,
mShouldTryStartAlign ? 0 : deviceProfile.hotseatBarEndOffset);
// The space taken by an item icon used during layout.
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewCallbacks.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewCallbacks.java
index 4591f9b..5d769d2 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewCallbacks.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewCallbacks.java
@@ -148,7 +148,7 @@
return new View.OnClickListener() {
@Override
public void onClick(View v) {
- mControllers.keyboardQuickSwitchController.openQuickSwitchView(
+ mControllers.keyboardQuickSwitchController.toggleQuickSwitchViewForTaskbar(
mControllers.taskbarViewController.getTaskIdsForPinnedApps());
}
};
@@ -159,7 +159,7 @@
return new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
- mControllers.keyboardQuickSwitchController.openQuickSwitchView(
+ mControllers.keyboardQuickSwitchController.toggleQuickSwitchViewForTaskbar(
mControllers.taskbarViewController.getTaskIdsForPinnedApps());
return true;
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
index 6f2d459..87e19be 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
@@ -233,7 +233,7 @@
mTaskbarNavButtonTranslationY =
controllers.navbarButtonsViewController.getTaskbarNavButtonTranslationY();
mTaskbarNavButtonTranslationYForInAppDisplay = controllers.navbarButtonsViewController
- .getNavButtonTranslationYForInAppDisplay();
+ .getTaskbarNavButtonTranslationYForInAppDisplay();
mActivity.addOnDeviceProfileChangeListener(mDeviceProfileChangeListener);
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java
index c5d649e..d91d10a 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java
@@ -38,6 +38,7 @@
import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.FrameLayout;
+import com.android.app.animation.Interpolators;
import com.android.launcher3.R;
import com.android.launcher3.anim.AnimatorListeners;
import com.android.launcher3.taskbar.BarsLocationAnimatorHelper;
@@ -86,7 +87,7 @@
private static final int MAX_BUBBLES = 5;
private static final int MAX_VISIBLE_BUBBLES_COLLAPSED = 2;
private static final int ARROW_POSITION_ANIMATION_DURATION_MS = 200;
- private static final int WIDTH_ANIMATION_DURATION_MS = 200;
+ private static final int WIDTH_ANIMATION_DURATION_MS = 400;
private static final int SCALE_ANIMATION_DURATION_MS = 200;
/**
@@ -143,7 +144,7 @@
// An animator that represents the expansion state of the bubble bar, where 0 corresponds to the
// collapsed state and 1 to the fully expanded state.
- private final ValueAnimator mWidthAnimator = ValueAnimator.ofFloat(0, 1);
+ private ValueAnimator mWidthAnimator = createExpansionAnimator(/* expanding = */ false);
/** An animator used for animating individual bubbles in the bubble bar while expanded. */
@Nullable
@@ -207,35 +208,6 @@
mBubbleBarBackground = new BubbleBarBackground(context, getBubbleBarExpandedHeight());
setBackgroundDrawable(mBubbleBarBackground);
-
- mWidthAnimator.setDuration(WIDTH_ANIMATION_DURATION_MS);
-
- addAnimationCallBacks(mWidthAnimator,
- /* onStart= */ () -> mBubbleBarBackground.showArrow(true),
- /* onEnd= */ () -> {
- mBubbleBarBackground.showArrow(mIsBarExpanded);
- if (!mIsBarExpanded && mReorderRunnable != null) {
- mReorderRunnable.run();
- mReorderRunnable = null;
- }
- // If the bar was just collapsed and the overflow was the last bubble that was
- // selected, set the first bubble as selected.
- if (!mIsBarExpanded && mUpdateSelectedBubbleAfterCollapse != null
- && mSelectedBubbleView != null
- && mSelectedBubbleView.getBubble() instanceof BubbleBarOverflow) {
- BubbleView firstBubble = (BubbleView) getChildAt(0);
- mUpdateSelectedBubbleAfterCollapse.accept(firstBubble.getBubble().getKey());
- }
- // If the bar was just expanded, remove the dot from the selected bubble.
- if (mIsBarExpanded && mSelectedBubbleView != null) {
- mSelectedBubbleView.markSeen();
- }
- updateLayoutParams();
- },
- /* onUpdate= */ animator -> {
- updateBubblesLayoutProperties(mBubbleBarLocation);
- invalidate();
- });
}
@@ -332,6 +304,17 @@
}
/**
+ * Set the bubble icons size and spacing between the bubbles and the borders of the bubble
+ * bar.
+ */
+ public void setIconSizeAndPaddingForPinning(float newIconSize, float newBubbleBarPadding) {
+ mBubbleBarPadding = newBubbleBarPadding;
+ mIconScale = newIconSize / mIconSize;
+ updateBubblesLayoutProperties(mBubbleBarLocation);
+ invalidate();
+ }
+
+ /**
* Sets new icon sizes and newBubbleBarPadding between icons and bubble bar borders.
*
* @param newIconSize new icon size
@@ -1251,11 +1234,8 @@
mIsBarExpanded = isBarExpanded;
updateArrowForSelected(/* shouldAnimate= */ false);
setOrUnsetClickListener();
- if (isBarExpanded) {
- mWidthAnimator.start();
- } else {
- mWidthAnimator.reverse();
- }
+ mWidthAnimator = createExpansionAnimator(isBarExpanded);
+ mWidthAnimator.start();
updateBubbleAccessibilityStates();
announceExpandedStateChange();
}
@@ -1492,6 +1472,46 @@
return bubbles;
}
+ /** Creates an animator based on the expanding or collapsing action. */
+ private ValueAnimator createExpansionAnimator(boolean expanding) {
+ float startValue = expanding ? 0 : 1;
+ if ((mWidthAnimator != null && mWidthAnimator.isRunning())) {
+ startValue = (float) mWidthAnimator.getAnimatedValue();
+ mWidthAnimator.cancel();
+ }
+ float endValue = expanding ? 1 : 0;
+ ValueAnimator animator = ValueAnimator.ofFloat(startValue, endValue);
+ animator.setDuration(WIDTH_ANIMATION_DURATION_MS);
+ animator.setInterpolator(Interpolators.EMPHASIZED);
+ addAnimationCallBacks(animator,
+ /* onStart= */ () -> mBubbleBarBackground.showArrow(true),
+ /* onEnd= */ () -> {
+ mBubbleBarBackground.showArrow(mIsBarExpanded);
+ if (!mIsBarExpanded && mReorderRunnable != null) {
+ mReorderRunnable.run();
+ mReorderRunnable = null;
+ }
+ // If the bar was just collapsed and the overflow was the last bubble that was
+ // selected, set the first bubble as selected.
+ if (!mIsBarExpanded && mUpdateSelectedBubbleAfterCollapse != null
+ && mSelectedBubbleView != null
+ && mSelectedBubbleView.getBubble() instanceof BubbleBarOverflow) {
+ BubbleView firstBubble = (BubbleView) getChildAt(0);
+ mUpdateSelectedBubbleAfterCollapse.accept(firstBubble.getBubble().getKey());
+ }
+ // If the bar was just expanded, remove the dot from the selected bubble.
+ if (mIsBarExpanded && mSelectedBubbleView != null) {
+ mSelectedBubbleView.markSeen();
+ }
+ updateLayoutParams();
+ },
+ /* onUpdate= */ anim -> {
+ updateBubblesLayoutProperties(mBubbleBarLocation);
+ invalidate();
+ });
+ return animator;
+ }
+
/**
* Returns the distance between the top left corner of the bubble bar to the center of the dot
* of the selected bubble.
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java
index 6db62b5..31b1ea0 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java
@@ -18,6 +18,10 @@
import static android.view.View.INVISIBLE;
import static android.view.View.VISIBLE;
+import static com.android.launcher3.Utilities.mapRange;
+import static com.android.launcher3.taskbar.TaskbarPinningController.PINNING_PERSISTENT;
+import static com.android.launcher3.taskbar.TaskbarPinningController.PINNING_TRANSIENT;
+
import android.animation.Animator;
import android.animation.AnimatorSet;
import android.content.res.Resources;
@@ -34,6 +38,7 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import com.android.app.animation.Interpolators;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.R;
import com.android.launcher3.anim.AnimatedFloat;
@@ -41,12 +46,14 @@
import com.android.launcher3.taskbar.TaskbarActivityContext;
import com.android.launcher3.taskbar.TaskbarControllers;
import com.android.launcher3.taskbar.TaskbarInsetsController;
+import com.android.launcher3.taskbar.TaskbarSharedState;
import com.android.launcher3.taskbar.TaskbarStashController;
import com.android.launcher3.taskbar.bubbles.animation.BubbleBarViewAnimator;
import com.android.launcher3.taskbar.bubbles.flyout.BubbleBarFlyoutController;
import com.android.launcher3.taskbar.bubbles.flyout.BubbleBarFlyoutPositioner;
import com.android.launcher3.taskbar.bubbles.flyout.FlyoutCallbacks;
import com.android.launcher3.taskbar.bubbles.stashing.BubbleStashController;
+import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.MultiPropertyFactory;
import com.android.launcher3.util.MultiValueAlpha;
import com.android.quickstep.SystemUiProxy;
@@ -69,6 +76,9 @@
private static final float APP_ICON_LARGE_DP = 52f;
/** The dot size is defined as a percentage of the icon size. */
private static final float DOT_TO_BUBBLE_SIZE_RATIO = 0.228f;
+ public static final int TASKBAR_FADE_IN_DURATION_MS = 150;
+ public static final int TASKBAR_FADE_IN_DELAY_MS = 50;
+ public static final int TASKBAR_FADE_OUT_DURATION_MS = 100;
private final SystemUiProxy mSystemUiProxy;
private final TaskbarActivityContext mActivity;
private final BubbleBarView mBarView;
@@ -102,6 +112,10 @@
this::updateTranslationY);
private final AnimatedFloat mBubbleOffsetY = new AnimatedFloat(
this::updateBubbleOffsetY);
+ private final AnimatedFloat mBubbleBarPinning = new AnimatedFloat(pinningProgress -> {
+ updateTranslationY();
+ setBubbleBarScaleAndPadding(pinningProgress);
+ });
// Modified when swipe up is happening on the bubble bar or task bar.
private float mBubbleBarSwipeUpTranslationY;
@@ -121,8 +135,9 @@
private BubbleBarViewAnimator mBubbleBarViewAnimator;
private final FrameLayout mBubbleBarContainer;
private BubbleBarFlyoutController mBubbleBarFlyoutController;
-
+ private TaskbarSharedState mTaskbarSharedState;
private final TimeSource mTimeSource = System::currentTimeMillis;
+ private final int mTaskbarTranslationDelta;
@Nullable
private BubbleBarBoundsChangeListener mBoundsChangeListener;
@@ -136,11 +151,13 @@
mBubbleBarAlpha = new MultiValueAlpha(mBarView, 1 /* num alpha channels */);
mIconSize = activity.getResources().getDimensionPixelSize(
R.dimen.bubblebar_icon_size);
+ mTaskbarTranslationDelta = getBubbleBarTranslationDeltaForTaskbar(activity);
}
/** Initializes controller. */
public void init(TaskbarControllers controllers, BubbleControllers bubbleControllers,
TaskbarViewPropertiesProvider taskbarViewPropertiesProvider) {
+ mTaskbarSharedState = controllers.getSharedState();
mBubbleStashController = bubbleControllers.bubbleStashController;
mBubbleBarController = bubbleControllers.bubbleBarController;
mBubbleDragController = bubbleControllers.bubbleDragController;
@@ -168,6 +185,10 @@
mBoundsChangeListener.onBoundsChanged();
}
});
+ float pinningValue = DisplayController.isTransientTaskbar(mActivity)
+ ? PINNING_TRANSIENT
+ : PINNING_PERSISTENT;
+ mBubbleBarPinning.updateValue(pinningValue);
mBarView.setController(new BubbleBarView.Controller() {
@Override
public float getBubbleBarTranslationY() {
@@ -223,6 +244,11 @@
};
}
+ /** Returns animated float property responsible for pinning transition animation. */
+ public AnimatedFloat getBubbleBarPinning() {
+ return mBubbleBarPinning;
+ }
+
private BubbleBarFlyoutPositioner createFlyoutPositioner() {
return new BubbleBarFlyoutPositioner() {
@@ -601,9 +627,11 @@
updateBubbleBarIconSizeAndPadding(newIconSize, newPadding, animate);
}
-
private int getBubbleBarIconSizeFromDeviceProfile(Resources res) {
- DeviceProfile deviceProfile = mActivity.getDeviceProfile();
+ return getBubbleBarIconSizeFromDeviceProfile(res, mActivity.getDeviceProfile());
+ }
+
+ private int getBubbleBarIconSizeFromDeviceProfile(Resources res, DeviceProfile deviceProfile) {
DisplayMetrics dm = res.getDisplayMetrics();
float smallIconSize = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
APP_ICON_SMALL_DP, dm);
@@ -618,7 +646,10 @@
}
private int getBubbleBarPaddingFromDeviceProfile(Resources res) {
- DeviceProfile deviceProfile = mActivity.getDeviceProfile();
+ return getBubbleBarPaddingFromDeviceProfile(res, mActivity.getDeviceProfile());
+ }
+
+ private int getBubbleBarPaddingFromDeviceProfile(Resources res, DeviceProfile deviceProfile) {
DisplayMetrics dm = res.getDisplayMetrics();
float mediumIconSize = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
APP_ICON_MEDIUM_DP, dm);
@@ -659,7 +690,53 @@
private void updateTranslationY() {
mBarView.setTranslationY(mBubbleBarTranslationY.value + mBubbleBarSwipeUpTranslationY
- + mBubbleBarStashTranslationY);
+ + mBubbleBarStashTranslationY + getBubbleBarTranslationYForTaskbarPinning());
+ }
+
+ /** Computes translation y for taskbar pinning. */
+ private float getBubbleBarTranslationYForTaskbarPinning() {
+ if (mTaskbarSharedState == null) return 0f;
+ float pinningProgress = mBubbleBarPinning.value;
+ if (mTaskbarSharedState.startTaskbarVariantIsTransient) {
+ return mapRange(pinningProgress, /* min = */ 0f, mTaskbarTranslationDelta);
+ } else {
+ return mapRange(pinningProgress, -mTaskbarTranslationDelta, /* max = */ 0f);
+ }
+ }
+
+ private void setBubbleBarScaleAndPadding(float pinningProgress) {
+ Resources res = mActivity.getResources();
+ // determine icon scale for pinning
+ int persistentIconSize = res.getDimensionPixelSize(
+ R.dimen.bubblebar_icon_size_persistent_taskbar);
+ int transientIconSize = getBubbleBarIconSizeFromDeviceProfile(res,
+ mActivity.getTransientTaskbarDeviceProfile());
+ float pinningIconSize = mapRange(pinningProgress, transientIconSize, persistentIconSize);
+
+ // determine bubble bar padding for pinning
+ int persistentPadding = res.getDimensionPixelSize(
+ R.dimen.bubblebar_icon_spacing_persistent_taskbar);
+ int transientPadding = getBubbleBarPaddingFromDeviceProfile(res,
+ mActivity.getTransientTaskbarDeviceProfile());
+ float pinningPadding = mapRange(pinningProgress, transientPadding, persistentPadding);
+ mBarView.setIconSizeAndPaddingForPinning(pinningIconSize, pinningPadding);
+ }
+
+ /**
+ * Calculates the vertical difference in the bubble bar positions for pinned and transient
+ * taskbar modes.
+ */
+ private int getBubbleBarTranslationDeltaForTaskbar(TaskbarActivityContext activity) {
+ Resources res = activity.getResources();
+ int persistentBubbleSize = res
+ .getDimensionPixelSize(R.dimen.bubblebar_icon_size_persistent_taskbar);
+ int persistentSpacingSize = res
+ .getDimensionPixelSize(R.dimen.bubblebar_icon_spacing_persistent_taskbar);
+ int persistentBubbleBarSize = persistentBubbleSize + persistentSpacingSize * 2;
+ int persistentTaskbarHeight = activity.getPersistentTaskbarDeviceProfile().taskbarHeight;
+ int persistentBubbleBarY = (persistentTaskbarHeight - persistentBubbleBarSize) / 2;
+ int transientBubbleBarY = activity.getTransientTaskbarDeviceProfile().taskbarBottomMargin;
+ return transientBubbleBarY - persistentBubbleBarY;
}
private void updateScaleX(float scale) {
@@ -862,10 +939,15 @@
if (!mBubbleStashController.isBubblesShowingOnHome()
&& !mBubbleStashController.isTransientTaskBar()) {
boolean hideTaskbar = isBubbleBarExpanded && isIntersectingTaskbar();
- mTaskbarViewPropertiesProvider
- .getIconsAlpha()
- .animateToValue(hideTaskbar ? 0 : 1)
- .start();
+ Animator taskbarAlphaAnimator = mTaskbarViewPropertiesProvider.getIconsAlpha()
+ .animateToValue(hideTaskbar ? 0 : 1);
+ taskbarAlphaAnimator.setDuration(hideTaskbar
+ ? TASKBAR_FADE_OUT_DURATION_MS : TASKBAR_FADE_IN_DURATION_MS);
+ if (!hideTaskbar) {
+ taskbarAlphaAnimator.setStartDelay(TASKBAR_FADE_IN_DELAY_MS);
+ }
+ taskbarAlphaAnimator.setInterpolator(Interpolators.LINEAR);
+ taskbarAlphaAnimator.start();
}
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutController.kt b/quickstep/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutController.kt
index fdbbbb0..f389d7e 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutController.kt
@@ -72,32 +72,63 @@
lp.marginEnd = horizontalMargin
container.addView(flyout, lp)
+ this.flyout = flyout
+ flyout.showFromCollapsed(message) { showFlyout(AnimationType.COLLAPSE, onEnd) }
+ }
+
+ private fun showFlyout(animationType: AnimationType, endAction: () -> Unit) {
+ val flyout = this.flyout ?: return
val animator = ValueAnimator.ofFloat(0f, 1f).setDuration(ANIMATION_DURATION_MS)
- animator.addUpdateListener { _ ->
- flyout.updateExpansionProgress(animator.animatedValue as Float)
+ when (animationType) {
+ AnimationType.FADE ->
+ animator.addUpdateListener { _ -> flyout.alpha = animator.animatedValue as Float }
+ AnimationType.COLLAPSE ->
+ animator.addUpdateListener { _ ->
+ flyout.updateExpansionProgress(animator.animatedValue as Float)
+ }
}
animator.addListener(
- onStart = {
- val flyoutTop = flyout.top + flyout.translationY
- // If the top position of the flyout is negative, then it's bleeding over the
- // top boundary of its parent view
- if (flyoutTop < 0) callbacks.extendTopBoundary(space = -flyoutTop.toInt())
- },
+ onStart = { extendTopBoundary() },
onEnd = {
- onEnd()
+ endAction()
flyout.setOnClickListener { callbacks.flyoutClicked() }
},
)
- flyout.showFromCollapsed(message) { animator.start() }
- this.flyout = flyout
+ animator.start()
+ }
+
+ fun updateFlyoutFullyExpanded(message: BubbleBarFlyoutMessage, onEnd: () -> Unit) {
+ val flyout = flyout ?: return
+ hideFlyout(AnimationType.FADE) {
+ flyout.updateData(message) { showFlyout(AnimationType.FADE, onEnd) }
+ }
+ }
+
+ fun updateFlyoutWhileExpanding(message: BubbleBarFlyoutMessage) {
+ val flyout = flyout ?: return
+ flyout.updateData(message) { extendTopBoundary() }
+ }
+
+ private fun extendTopBoundary() {
+ val flyout = flyout ?: return
+ val flyoutTop = flyout.top + flyout.translationY
+ // If the top position of the flyout is negative, then it's bleeding over the
+ // top boundary of its parent view
+ if (flyoutTop < 0) callbacks.extendTopBoundary(space = -flyoutTop.toInt())
}
fun cancelFlyout(endAction: () -> Unit) {
- hideFlyout(AnimationType.FADE, endAction)
+ hideFlyout(AnimationType.FADE) {
+ cleanupFlyoutView()
+ endAction()
+ }
}
fun collapseFlyout(endAction: () -> Unit) {
- hideFlyout(AnimationType.COLLAPSE, endAction)
+ hideFlyout(AnimationType.COLLAPSE) {
+ cleanupFlyoutView()
+ endAction()
+ }
}
private fun hideFlyout(animationType: AnimationType, endAction: () -> Unit) {
@@ -112,15 +143,15 @@
flyout.updateExpansionProgress(animator.animatedValue as Float)
}
}
- animator.addListener(
- onStart = { flyout.setOnClickListener(null) },
- onEnd = {
- container.removeView(flyout)
- this@BubbleBarFlyoutController.flyout = null
- callbacks.resetTopBoundary()
- endAction()
- },
- )
+ animator.addListener(onStart = { flyout.setOnClickListener(null) }, onEnd = { endAction() })
animator.start()
}
+
+ private fun cleanupFlyoutView() {
+ container.removeView(flyout)
+ this@BubbleBarFlyoutController.flyout = null
+ callbacks.resetTopBoundary()
+ }
+
+ fun hasFlyout() = flyout != null
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutView.kt b/quickstep/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutView.kt
index bb8a392..af8aaf8 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutView.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutView.kt
@@ -198,6 +198,8 @@
} else {
-positioner.distanceToCollapsedPosition.x
}
+ // TODO: b/277815200 - before collapsing, recalculate translationToCollapsedPosition because
+ // the collapsed position may have changed
val tyToCollapsedPosition =
positioner.distanceToCollapsedPosition.y + triangleHeight - triangleOverlap
translationToCollapsedPosition = PointF(txToCollapsedPosition, tyToCollapsedPosition)
@@ -217,6 +219,12 @@
scheduler.runAfterLayout(expandAnimation)
}
+ /** Updates the content of the flyout and schedules [afterLayout] to run after a layout pass. */
+ fun updateData(flyoutMessage: BubbleBarFlyoutMessage, afterLayout: () -> Unit) {
+ setData(flyoutMessage)
+ scheduler.runAfterLayout(afterLayout)
+ }
+
private fun setData(flyoutMessage: BubbleBarFlyoutMessage) {
if (flyoutMessage.icon != null) {
icon.visibility = VISIBLE
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
index 4ad65e1..228dc91 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
@@ -419,8 +419,10 @@
mDepthController.setActivityStarted(isStarted());
}
- if ((changeBits & ACTIVITY_STATE_RESUMED) != 0 && mTaskbarUIController != null) {
- mTaskbarUIController.onLauncherPausedOrResumed(isPaused());
+ if ((changeBits & ACTIVITY_STATE_RESUMED) != 0) {
+ if (!FeatureFlags.enableHomeTransitionListener() && mTaskbarUIController != null) {
+ mTaskbarUIController.onLauncherVisibilityChanged(hasBeenResumed());
+ }
}
super.onActivityFlagsChanged(changeBits);
diff --git a/quickstep/src/com/android/launcher3/uioverrides/SystemApiWrapper.kt b/quickstep/src/com/android/launcher3/uioverrides/SystemApiWrapper.kt
index f542b8c..374db6a 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/SystemApiWrapper.kt
+++ b/quickstep/src/com/android/launcher3/uioverrides/SystemApiWrapper.kt
@@ -41,15 +41,20 @@
import com.android.launcher3.Flags.privateSpaceSysAppsSeparation
import com.android.launcher3.R
import com.android.launcher3.Utilities
+import com.android.launcher3.dagger.ApplicationContext
+import com.android.launcher3.dagger.LauncherAppSingleton
import com.android.launcher3.proxy.ProxyActivityStarter
import com.android.launcher3.util.ApiWrapper
import com.android.launcher3.util.Executors
import com.android.launcher3.util.StartActivityParams
import com.android.launcher3.util.UserIconInfo
import com.android.quickstep.util.FadeOutRemoteTransition
+import javax.inject.Inject
/** A wrapper for the hidden API calls */
-open class SystemApiWrapper(context: Context?) : ApiWrapper(context) {
+@LauncherAppSingleton
+open class SystemApiWrapper @Inject constructor(@ApplicationContext context: Context?) :
+ ApiWrapper(context) {
override fun getPersons(si: ShortcutInfo) = si.persons ?: Utilities.EMPTY_PERSON_ARRAY
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java b/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
index e87ac2f..ca388c6 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
@@ -15,6 +15,7 @@
*/
package com.android.launcher3.uioverrides.states;
+import static com.android.launcher3.Flags.enableDesktopWindowingCarouselDetach;
import static com.android.launcher3.Flags.enableScalingRevealHomeAnimation;
import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_BACKGROUND;
@@ -91,7 +92,7 @@
@Override
public boolean detachDesktopCarousel() {
- return true;
+ return enableDesktopWindowingCarouselDetach();
}
@Override
diff --git a/quickstep/src/com/android/quickstep/GestureState.java b/quickstep/src/com/android/quickstep/GestureState.java
index 015a449..cff352c 100644
--- a/quickstep/src/com/android/quickstep/GestureState.java
+++ b/quickstep/src/com/android/quickstep/GestureState.java
@@ -309,7 +309,9 @@
* changes in the WM hierarchy (ie. starting recents transition when you are already over home).
*/
public boolean useSyntheticRecentsTransition() {
- return mRunningTask.isHomeTask() && Flags.enableFallbackOverviewInWindow();
+ return mRunningTask.isHomeTask()
+ && (Flags.enableFallbackOverviewInWindow()
+ || Flags.enableLauncherOverviewInWindow());
}
/**
diff --git a/quickstep/src/com/android/quickstep/OverviewComponentObserver.java b/quickstep/src/com/android/quickstep/OverviewComponentObserver.java
index 66112c1..1f6c671 100644
--- a/quickstep/src/com/android/quickstep/OverviewComponentObserver.java
+++ b/quickstep/src/com/android/quickstep/OverviewComponentObserver.java
@@ -155,7 +155,7 @@
mContainerInterface.onAssistantVisibilityChanged(0.f);
}
- if (SEPARATE_RECENTS_ACTIVITY.get()) {
+ if (SEPARATE_RECENTS_ACTIVITY.get() || Flags.enableLauncherOverviewInWindow()) {
mIsDefaultHome = false;
if (defaultHome == null) {
defaultHome = mMyHomeIntent.getComponent();
@@ -179,7 +179,7 @@
} else {
// The default home app is a different launcher. Use the fallback Overview instead.
- if (Flags.enableFallbackOverviewInWindow()) {
+ if (Flags.enableLauncherOverviewInWindow() || Flags.enableFallbackOverviewInWindow()) {
mContainerInterface = FallbackWindowInterface.getInstance();
} else {
mContainerInterface = FallbackActivityInterface.INSTANCE;
diff --git a/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java b/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java
index c3b9736..2828a84 100644
--- a/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java
+++ b/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java
@@ -3,7 +3,6 @@
import static com.android.launcher3.taskbar.TaskbarThresholdUtils.getFromNavThreshold;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
-import android.app.Activity;
import android.content.Context;
import android.content.res.Resources;
import android.os.Bundle;
@@ -208,8 +207,9 @@
RecentsAnimationDeviceState rads = new RecentsAnimationDeviceState(mContext);
OverviewComponentObserver observer = new OverviewComponentObserver(mContext, rads);
try {
- return observer.getContainerInterface()
- .getCreatedContainer().getRootView().getRootWindowInsets();
+ RecentsViewContainer container = observer.getContainerInterface().getCreatedContainer();
+
+ return container == null ? null : container.getRootView().getRootWindowInsets();
} finally {
observer.onDestroy();
rads.destroy();
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java b/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java
index 7d5bd37..8fc1a78 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java
@@ -109,7 +109,8 @@
boolean isOpeningHome = Arrays.stream(appTargets).filter(app -> app.mode == MODE_OPENING
&& app.windowConfiguration.getActivityType() == ACTIVITY_TYPE_HOME)
.count() > 0;
- if (appCount == 0 && (!Flags.enableFallbackOverviewInWindow() || isOpeningHome)) {
+ if (appCount == 0 && (!(Flags.enableFallbackOverviewInWindow()
+ || Flags.enableLauncherOverviewInWindow()) || isOpeningHome)) {
ActiveGestureProtoLogProxy.logOnRecentsAnimationStartCancelled();
// Edge case, if there are no closing app targets, then Launcher has nothing to handle
notifyAnimationCanceled();
diff --git a/quickstep/src/com/android/quickstep/RemoteTargetGluer.java b/quickstep/src/com/android/quickstep/RemoteTargetGluer.java
index 8adc11a..06b2972 100644
--- a/quickstep/src/com/android/quickstep/RemoteTargetGluer.java
+++ b/quickstep/src/com/android/quickstep/RemoteTargetGluer.java
@@ -94,7 +94,7 @@
RemoteTargetHandle[] handles = new RemoteTargetHandle[numHandles];
for (int i = 0; i < numHandles; i++) {
TaskViewSimulator tvs = new TaskViewSimulator(context, sizingStrategy);
- tvs.setIsDesktopTask(forDesktop);
+ tvs.setIsDesktopTask(forDesktop , i);
TransformParams transformParams = new TransformParams();
handles[i] = new RemoteTargetHandle(tvs, transformParams);
}
diff --git a/quickstep/src/com/android/quickstep/SystemUiProxy.java b/quickstep/src/com/android/quickstep/SystemUiProxy.java
index c1d7ffa..cd39c09 100644
--- a/quickstep/src/com/android/quickstep/SystemUiProxy.java
+++ b/quickstep/src/com/android/quickstep/SystemUiProxy.java
@@ -54,15 +54,16 @@
import androidx.annotation.MainThread;
import androidx.annotation.Nullable;
-import androidx.annotation.VisibleForTesting;
import androidx.annotation.WorkerThread;
import com.android.internal.logging.InstanceId;
import com.android.internal.util.ScreenshotRequest;
import com.android.internal.view.AppearanceRegion;
-import com.android.launcher3.util.MainThreadInitializedObject;
+import com.android.launcher3.dagger.ApplicationContext;
+import com.android.launcher3.dagger.LauncherAppSingleton;
+import com.android.launcher3.util.DaggerSingletonObject;
import com.android.launcher3.util.Preconditions;
-import com.android.launcher3.util.SafeCloseable;
+import com.android.quickstep.dagger.QuickstepBaseAppComponent;
import com.android.quickstep.util.ActiveGestureProtoLogProxy;
import com.android.quickstep.util.ContextualSearchInvoker;
import com.android.quickstep.util.unfold.ProxyUnfoldTransitionProvider;
@@ -109,14 +110,17 @@
import java.util.LinkedHashMap;
import java.util.List;
+import javax.inject.Inject;
+
/**
* Holds the reference to SystemUI.
*/
-public class SystemUiProxy implements ISystemUiProxy, NavHandle, SafeCloseable {
+@LauncherAppSingleton
+public class SystemUiProxy implements ISystemUiProxy, NavHandle {
private static final String TAG = "SystemUiProxy";
- public static final MainThreadInitializedObject<SystemUiProxy> INSTANCE =
- new MainThreadInitializedObject<>(SystemUiProxy::new);
+ public static final DaggerSingletonObject<SystemUiProxy> INSTANCE =
+ new DaggerSingletonObject<>(QuickstepBaseAppComponent::getSystemUiProxy);
private static final int MSG_SET_SHELF_HEIGHT = 1;
private static final int MSG_SET_LAUNCHER_KEEP_CLEAR_AREA_HEIGHT = 2;
@@ -188,8 +192,8 @@
@Nullable
private final ProxyUnfoldTransitionProvider mUnfoldTransitionProvider;
- @VisibleForTesting
- protected SystemUiProxy(Context context) {
+ @Inject
+ public SystemUiProxy(@ApplicationContext Context context) {
mContext = context;
mAsyncHandler = new Handler(UI_HELPER_EXECUTOR.getLooper(), this::handleMessageAsync);
final Intent baseIntent = new Intent().setPackage(mContext.getPackageName());
@@ -206,9 +210,6 @@
}
@Override
- public void close() { }
-
- @Override
public void onBackPressed() {
if (mSystemUiProxy != null) {
try {
diff --git a/quickstep/src/com/android/quickstep/TaskAnimationManager.java b/quickstep/src/com/android/quickstep/TaskAnimationManager.java
index 56c978a..0b6794c 100644
--- a/quickstep/src/com/android/quickstep/TaskAnimationManager.java
+++ b/quickstep/src/com/android/quickstep/TaskAnimationManager.java
@@ -295,7 +295,8 @@
// TODO:(b/365777482) if flag is enabled, but on launcher it will crash.
if(containerInterface.getCreatedContainer() instanceof RecentsWindowManager
- && Flags.enableFallbackOverviewInWindow()){
+ && (Flags.enableFallbackOverviewInWindow()
+ || Flags.enableLauncherOverviewInWindow())) {
mRecentsAnimationStartPending = getSystemUiProxy().startRecentsActivity(intent, options,
mCallbacks, gestureState.useSyntheticRecentsTransition());
mRecentsWindowsManager.startRecentsWindow(mCallbacks);
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index 1481ef2..032e755 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -675,7 +675,7 @@
mDesktopVisibilityController = new DesktopVisibilityController(this);
mTaskbarManager = new TaskbarManager(
this, mAllAppsActionManager, mNavCallbacks, mDesktopVisibilityController);
- if(Flags.enableFallbackOverviewInWindow()) {
+ if (Flags.enableLauncherOverviewInWindow() || Flags.enableFallbackOverviewInWindow()) {
mRecentsWindowManager = new RecentsWindowManager(this);
}
mInputConsumer = InputConsumerController.getRecentsAnimationInputConsumer();
@@ -927,9 +927,6 @@
BubbleControllers bubbleControllers = tac != null ? tac.getBubbleControllers() : null;
boolean isOnBubbles = bubbleControllers != null
&& BubbleBarInputConsumer.isEventOnBubbles(tac, event);
- if (isInSwipeUpTouchRegion && tac != null) {
- tac.closeKeyboardQuickSwitchView();
- }
if (mDeviceState.isButtonNavMode()
&& mDeviceState.supportsAssistantGestureInButtonNav()) {
reasonString.append("in three button mode which supports Assistant gesture");
@@ -1410,8 +1407,10 @@
}
public AbsSwipeUpHandler.Factory getSwipeUpHandlerFactory() {
+ boolean recentsInWindow =
+ Flags.enableFallbackOverviewInWindow() || Flags.enableLauncherOverviewInWindow();
return mOverviewComponentObserver.isHomeAndOverviewSame()
- ? mLauncherSwipeHandlerFactory : (Flags.enableFallbackOverviewInWindow()
+ ? mLauncherSwipeHandlerFactory : (recentsInWindow
? mRecentsWindowSwipeHandlerFactory : mFallbackSwipeHandlerFactory);
}
diff --git a/quickstep/src/com/android/quickstep/ViewUtils.java b/quickstep/src/com/android/quickstep/ViewUtils.java
index 3b58dfc..cf6b04e 100644
--- a/quickstep/src/com/android/quickstep/ViewUtils.java
+++ b/quickstep/src/com/android/quickstep/ViewUtils.java
@@ -23,6 +23,7 @@
import com.android.launcher3.Utilities;
+import java.util.ArrayList;
import java.util.function.BooleanSupplier;
/**
@@ -129,4 +130,18 @@
}
}
}
+
+ /**
+ * Adds the view to the list of accessible children.
+ *
+ * @param view The view to add.
+ * @param outChildren The list of accessible children.
+ */
+ public static void addAccessibleChildToList(View view, ArrayList<View> outChildren) {
+ if (view.includeForAccessibility()) {
+ outChildren.add(view);
+ } else {
+ view.addChildrenForAccessibility(outChildren);
+ }
+ }
}
diff --git a/quickstep/src/com/android/quickstep/dagger/QuickStepModule.java b/quickstep/src/com/android/quickstep/dagger/QuickStepModule.java
index ab77a7f..3870b9b 100644
--- a/quickstep/src/com/android/quickstep/dagger/QuickStepModule.java
+++ b/quickstep/src/com/android/quickstep/dagger/QuickStepModule.java
@@ -15,7 +15,9 @@
*/
package com.android.quickstep.dagger;
+import com.android.launcher3.uioverrides.SystemApiWrapper;
import com.android.launcher3.uioverrides.plugins.PluginManagerWrapperImpl;
+import com.android.launcher3.util.ApiWrapper;
import com.android.launcher3.util.PluginManagerWrapper;
import dagger.Binds;
@@ -25,4 +27,5 @@
public abstract class QuickStepModule {
@Binds abstract PluginManagerWrapper bindPluginManagerWrapper(PluginManagerWrapperImpl impl);
+ @Binds abstract ApiWrapper bindApiWrapper(SystemApiWrapper systemApiWrapper);
}
diff --git a/quickstep/src/com/android/quickstep/dagger/QuickstepBaseAppComponent.java b/quickstep/src/com/android/quickstep/dagger/QuickstepBaseAppComponent.java
index 977c036..b2670e8 100644
--- a/quickstep/src/com/android/quickstep/dagger/QuickstepBaseAppComponent.java
+++ b/quickstep/src/com/android/quickstep/dagger/QuickstepBaseAppComponent.java
@@ -19,6 +19,7 @@
import com.android.launcher3.dagger.LauncherAppComponent;
import com.android.launcher3.dagger.LauncherBaseAppComponent;
import com.android.launcher3.model.WellbeingModel;
+import com.android.quickstep.SystemUiProxy;
import com.android.quickstep.util.AsyncClockEventDelegate;
/**
@@ -34,4 +35,6 @@
WellbeingModel getWellbeingModel();
AsyncClockEventDelegate getAsyncClockEventDelegate();
+
+ SystemUiProxy getSystemUiProxy();
}
diff --git a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
index 5a4c769..daad6b7 100644
--- a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
+++ b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
@@ -78,7 +78,7 @@
}
private static BaseContainerInterface<RecentsState, ?> getContainerInterface() {
- return Flags.enableFallbackOverviewInWindow()
+ return (Flags.enableFallbackOverviewInWindow() || Flags.enableLauncherOverviewInWindow())
? FallbackWindowInterface.getInstance()
: FallbackActivityInterface.INSTANCE;
}
@@ -294,7 +294,8 @@
}
// disabling this so app icons aren't drawn on top of recent tasks.
- if (isOverlayEnabled && !Flags.enableFallbackOverviewInWindow()) {
+ if (isOverlayEnabled && !(Flags.enableFallbackOverviewInWindow()
+ || Flags.enableLauncherOverviewInWindow())) {
runActionOnRemoteHandles(remoteTargetHandle ->
remoteTargetHandle.getTaskViewSimulator().setDrawsBelowRecents(true));
}
diff --git a/quickstep/src/com/android/quickstep/fallback/RecentsState.java b/quickstep/src/com/android/quickstep/fallback/RecentsState.java
index 082b96c..34783c7 100644
--- a/quickstep/src/com/android/quickstep/fallback/RecentsState.java
+++ b/quickstep/src/com/android/quickstep/fallback/RecentsState.java
@@ -15,6 +15,7 @@
*/
package com.android.quickstep.fallback;
+import static com.android.launcher3.Flags.enableDesktopWindowingCarouselDetach;
import static com.android.launcher3.LauncherState.FLAG_CLOSE_POPUPS;
import static com.android.launcher3.uioverrides.states.BackgroundAppState.getOverviewScaleAndOffsetForBackgroundState;
import static com.android.launcher3.uioverrides.states.OverviewModalTaskState.getOverviewScaleAndOffsetForModalState;
@@ -152,7 +153,7 @@
@Override
public boolean detachDesktopCarousel() {
- return hasFlag(FLAG_DETACH_DESKTOP_CAROUSEL);
+ return hasFlag(FLAG_DETACH_DESKTOP_CAROUSEL) && enableDesktopWindowingCarouselDetach();
}
/**
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
index e19b338..c4198db 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
@@ -157,6 +157,7 @@
mStartDisplacement = continuingPreviousGesture ? 0 : -mTouchSlop;
mDisableHorizontalSwipe = !mPassedPilferInputSlop && disableHorizontalSwipe;
mRotationTouchHelper = mDeviceState.getRotationTouchHelper();
+
}
@Override
@@ -426,8 +427,9 @@
notifyGestureStarted(true /*isLikelyToStartNewTask*/);
} else {
// todo differentiate intent based on if we are on home or in app for overview in window
- Intent intent = new Intent(Flags.enableFallbackOverviewInWindow()
- ? mInteractionHandler.getHomeIntent()
+ boolean useHomeIntentForWindow = Flags.enableFallbackOverviewInWindow()
+ || Flags.enableLauncherOverviewInWindow();
+ Intent intent = new Intent(useHomeIntentForWindow ? mInteractionHandler.getHomeIntent()
: mInteractionHandler.getLaunchIntent());
intent.putExtra(INTENT_EXTRA_LOG_TRACE_ID, mGestureState.getGestureId());
mActiveCallbacks = mTaskAnimationManager.startRecentsAnimation(mGestureState, intent,
diff --git a/quickstep/src/com/android/quickstep/recents/di/RecentsDependencies.kt b/quickstep/src/com/android/quickstep/recents/di/RecentsDependencies.kt
index 44b8b8d..f2b9976 100644
--- a/quickstep/src/com/android/quickstep/recents/di/RecentsDependencies.kt
+++ b/quickstep/src/com/android/quickstep/recents/di/RecentsDependencies.kt
@@ -113,6 +113,10 @@
instance =
factory?.invoke(extras) as T ?: createDependency(modelClass, scopeId, extras)
scope[modelClass.simpleName] = instance!!
+ log(
+ "instance of $modelClass" +
+ " (${instance.hashCode()}) added to scope ${scope.scopeId}"
+ )
}
}
return instance!!
@@ -148,6 +152,13 @@
fun getScope(scopeId: RecentsScopeId): RecentsDependenciesScope =
scopes[scopeId] ?: createScope(scopeId)
+ fun removeScope(scope: Any) {
+ val scopeId: RecentsScopeId = scope as? RecentsScopeId ?: scope.hashCode().toString()
+ scopes[scopeId]?.close()
+ scopes.remove(scopeId)
+ log("Scope $scopeId removed")
+ }
+
// TODO(b/353912757): Create a factory so we can prevent this method of growing indefinitely.
// Each class should be responsible for providing a factory function to create a new instance.
@Suppress("UNCHECKED_CAST")
diff --git a/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailView.kt b/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailView.kt
index e7416ec..eb9c047 100644
--- a/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailView.kt
+++ b/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailView.kt
@@ -32,6 +32,7 @@
import com.android.launcher3.Utilities
import com.android.launcher3.util.ViewPool
import com.android.quickstep.recents.di.RecentsDependencies
+import com.android.quickstep.recents.di.get
import com.android.quickstep.recents.di.inject
import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.BackgroundOnly
import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.LiveTile
@@ -54,7 +55,7 @@
class TaskThumbnailView : ConstraintLayout, ViewPool.Reusable {
private val viewData: TaskThumbnailViewData by RecentsDependencies.inject(this)
- private val viewModel: TaskThumbnailViewModel by RecentsDependencies.inject(this)
+ private lateinit var viewModel: TaskThumbnailViewModel
private lateinit var viewAttachedScope: CoroutineScope
@@ -91,6 +92,7 @@
super.onAttachedToWindow()
viewAttachedScope =
CoroutineScope(SupervisorJob() + Dispatchers.Main + CoroutineName("TaskThumbnailView"))
+ viewModel = RecentsDependencies.get(this)
viewModel.uiState
.onEach { viewModelUiState ->
Log.d(TAG, "viewModelUiState changed from $uiState to: $viewModelUiState")
diff --git a/quickstep/src/com/android/quickstep/task/util/TaskOverlayHelper.kt b/quickstep/src/com/android/quickstep/task/util/TaskOverlayHelper.kt
index 9253dbf..c82ed9a 100644
--- a/quickstep/src/com/android/quickstep/task/util/TaskOverlayHelper.kt
+++ b/quickstep/src/com/android/quickstep/task/util/TaskOverlayHelper.kt
@@ -17,6 +17,7 @@
package com.android.quickstep.task.util
import android.util.Log
+import android.view.View.OnLayoutChangeListener
import com.android.quickstep.TaskOverlayFactory
import com.android.quickstep.recents.di.RecentsDependencies
import com.android.quickstep.recents.di.get
@@ -41,31 +42,35 @@
private lateinit var overlayInitializedScope: CoroutineScope
private var uiState: TaskOverlayUiState = Disabled
- private val viewModel: TaskOverlayViewModel by lazy {
- TaskOverlayViewModel(
- task = task,
- recentsViewData = RecentsDependencies.get(),
- getThumbnailPositionUseCase = RecentsDependencies.get(),
- recentTasksRepository = RecentsDependencies.get()
- )
- }
+ private lateinit var viewModel: TaskOverlayViewModel
// TODO(b/331753115): TaskOverlay should listen for state changes and react.
val enabledState: Enabled
get() = uiState as Enabled
+ private val snapshotLayoutChangeListener = OnLayoutChangeListener { _, _, _, _, _, _, _, _, _ ->
+ (uiState as? Enabled)?.let { initOverlay(it) }
+ }
+
fun getThumbnailMatrix() = getThumbnailPositionState().matrix
private fun getThumbnailPositionState() =
viewModel.getThumbnailPositionState(
overlay.snapshotView.width,
overlay.snapshotView.height,
- overlay.snapshotView.isLayoutRtl
+ overlay.snapshotView.isLayoutRtl,
)
fun init() {
overlayInitializedScope =
CoroutineScope(SupervisorJob() + Dispatchers.Main + CoroutineName("TaskOverlayHelper"))
+ viewModel =
+ TaskOverlayViewModel(
+ task = task,
+ recentsViewData = RecentsDependencies.get(),
+ getThumbnailPositionUseCase = RecentsDependencies.get(),
+ recentTasksRepository = RecentsDependencies.get(),
+ )
viewModel.overlayState
.onEach {
uiState = it
@@ -76,9 +81,7 @@
}
}
.launchIn(overlayInitializedScope)
- overlay.snapshotView.addOnLayoutChangeListener { _, _, _, _, _, _, _, _, _ ->
- (uiState as? Enabled)?.let { initOverlay(it) }
- }
+ overlay.snapshotView.addOnLayoutChangeListener(snapshotLayoutChangeListener)
}
private fun initOverlay(enabledState: Enabled) {
@@ -96,6 +99,7 @@
fun destroy() {
overlayInitializedScope.cancel()
uiState = Disabled
+ overlay.snapshotView.removeOnLayoutChangeListener(snapshotLayoutChangeListener)
reset()
}
diff --git a/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java b/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java
index 8762e86..623bc53 100644
--- a/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java
+++ b/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java
@@ -39,6 +39,7 @@
// The percentage of the previous speed that determines whether this is a rapid deceleration.
// The bigger this number, the easier it is to trigger the first pause.
private static final float RAPID_DECELERATION_FACTOR = 0.6f;
+ private static final float RAPID_DECELERATION_FACTOR_TRACKPAD = 0.85f;
/** If no motion is added for this amount of time, assume the motion has paused. */
private static final long FORCE_PAUSE_TIMEOUT = 300;
@@ -57,6 +58,7 @@
private final float mSpeedVerySlow;
private final float mSpeedSlow;
private final float mSpeedSomewhatFast;
+ private final float mSpeedTrackpadSomewhatFast;
private final float mSpeedFast;
private final Alarm mForcePauseTimeout;
private final boolean mMakePauseHarderToTrigger;
@@ -95,6 +97,8 @@
mSpeedVerySlow = res.getDimension(R.dimen.motion_pause_detector_speed_very_slow);
mSpeedSlow = res.getDimension(R.dimen.motion_pause_detector_speed_slow);
mSpeedSomewhatFast = res.getDimension(R.dimen.motion_pause_detector_speed_somewhat_fast);
+ mSpeedTrackpadSomewhatFast = res.getDimension(
+ R.dimen.motion_pause_detector_speed_trackpad_somewhat_fast);
mSpeedFast = res.getDimension(R.dimen.motion_pause_detector_speed_fast);
mForcePauseTimeout = new Alarm();
mForcePauseTimeout.setOnAlarmListener(alarm -> {
@@ -183,7 +187,9 @@
// takes too long, so also check for a rapid deceleration.
boolean isRapidDeceleration =
speed < previousSpeed * getRapidDecelerationFactor();
- isPaused = isRapidDeceleration && speed < mSpeedSomewhatFast;
+ boolean notSuperFast = speed < mSpeedSomewhatFast
+ || (mIsTrackpadGesture && speed < mSpeedTrackpadSomewhatFast);
+ isPaused = isRapidDeceleration && notSuperFast;
isPausedReason = new ActiveGestureLog.CompoundString(
"Didn't have back to back slow speeds, checking for rapid "
+ " deceleration on first pause only");
@@ -265,7 +271,8 @@
private float getRapidDecelerationFactor() {
return mIsTrackpadGesture ? Float.parseFloat(
Utilities.getSystemProperty("trackpad_in_app_swipe_up_deceleration_factor",
- String.valueOf(RAPID_DECELERATION_FACTOR))) : RAPID_DECELERATION_FACTOR;
+ String.valueOf(RAPID_DECELERATION_FACTOR_TRACKPAD)))
+ : RAPID_DECELERATION_FACTOR;
}
public interface OnMotionPauseListener {
diff --git a/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java b/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
index f5be103..a4b8fec 100644
--- a/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
+++ b/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
@@ -120,6 +120,7 @@
private boolean mScaleToCarouselTaskSize = false;
private int mTaskRectTranslationX;
private int mTaskRectTranslationY;
+ private int mDesktopTaskIndex = 0;
public TaskViewSimulator(Context context, BaseContainerInterface sizeStrategy) {
mContext = context;
@@ -290,8 +291,9 @@
/**
* Sets whether this task is part of desktop tasks in overview.
*/
- public void setIsDesktopTask(boolean desktop) {
+ public void setIsDesktopTask(boolean desktop, int index) {
mIsDesktopTask = desktop;
+ mDesktopTaskIndex = index;
}
/**
@@ -545,9 +547,9 @@
// In shell transitions, the animation leashes are reparented to an animation container
// so we can bump layers as needed.
builder.setLayer(mDrawsBelowRecents
- ? Integer.MIN_VALUE + app.prefixOrderIndex
// 1000 is an arbitrary number to give room for multiple layers.
- : Integer.MAX_VALUE - 1000 + app.prefixOrderIndex);
+ ? Integer.MIN_VALUE + 1000 + app.prefixOrderIndex - mDesktopTaskIndex
+ : Integer.MAX_VALUE - 1000 + app.prefixOrderIndex - mDesktopTaskIndex);
}
}
diff --git a/quickstep/src/com/android/quickstep/views/DesktopTaskView.kt b/quickstep/src/com/android/quickstep/views/DesktopTaskView.kt
index 8c854e7..15b0a6b 100644
--- a/quickstep/src/com/android/quickstep/views/DesktopTaskView.kt
+++ b/quickstep/src/com/android/quickstep/views/DesktopTaskView.kt
@@ -25,7 +25,6 @@
import android.util.AttributeSet
import android.util.Log
import android.view.Gravity
-import android.view.LayoutInflater
import android.view.View
import androidx.core.content.res.ResourcesCompat
import androidx.core.view.updateLayoutParams
@@ -40,6 +39,8 @@
import com.android.launcher3.util.rects.set
import com.android.quickstep.BaseContainerInterface
import com.android.quickstep.TaskOverlayFactory
+import com.android.quickstep.ViewUtils
+import com.android.quickstep.task.thumbnail.TaskThumbnailView
import com.android.quickstep.util.RecentsOrientedState
import com.android.systemui.shared.recents.model.Task
@@ -53,14 +54,29 @@
override fun computeTaskCornerRadius(context: Context) =
computeWindowCornerRadius(context)
}
+
private val taskThumbnailViewDeprecatedPool =
- ViewPool<TaskThumbnailViewDeprecated>(
- context,
- this,
- R.layout.task_thumbnail_deprecated,
- VIEW_POOL_MAX_SIZE,
- VIEW_POOL_INITIAL_SIZE,
- )
+ if (!enableRefactorTaskThumbnail()) {
+ ViewPool<TaskThumbnailViewDeprecated>(
+ context,
+ this,
+ R.layout.task_thumbnail_deprecated,
+ VIEW_POOL_MAX_SIZE,
+ VIEW_POOL_INITIAL_SIZE,
+ )
+ } else null
+
+ private val taskThumbnailViewPool =
+ if (enableRefactorTaskThumbnail()) {
+ ViewPool<TaskThumbnailView>(
+ context,
+ this,
+ R.layout.task_thumbnail,
+ VIEW_POOL_MAX_SIZE,
+ VIEW_POOL_INITIAL_SIZE,
+ )
+ } else null
+
private val tempPointF = PointF()
private val tempRect = Rect()
private lateinit var backgroundView: View
@@ -117,9 +133,9 @@
tasks.map { task ->
val snapshotView =
if (enableRefactorTaskThumbnail()) {
- LayoutInflater.from(context).inflate(R.layout.task_thumbnail, this, false)
+ taskThumbnailViewPool!!.view
} else {
- taskThumbnailViewDeprecatedPool.view
+ taskThumbnailViewDeprecatedPool!!.view
}
addView(
@@ -148,9 +164,11 @@
super.onRecycle()
visibility = VISIBLE
taskContainers.forEach {
- if (!enableRefactorTaskThumbnail()) {
- removeView(it.thumbnailViewDeprecated)
- taskThumbnailViewDeprecatedPool.recycle(it.thumbnailViewDeprecated)
+ removeView(it.snapshotView)
+ if (enableRefactorTaskThumbnail()) {
+ taskThumbnailViewPool!!.recycle(it.thumbnailView)
+ } else {
+ taskThumbnailViewDeprecatedPool!!.recycle(it.thumbnailViewDeprecated)
}
}
}
@@ -296,10 +314,15 @@
override fun getThumbnailFullscreenParams() = snapshotDrawParams
+ override fun addChildrenForAccessibility(outChildren: ArrayList<View>) {
+ super.addChildrenForAccessibility(outChildren)
+ ViewUtils.addAccessibleChildToList(backgroundView, outChildren)
+ }
+
companion object {
private const val TAG = "DesktopTaskView"
private const val DEBUG = false
- private const val VIEW_POOL_MAX_SIZE = 10
+ private const val VIEW_POOL_MAX_SIZE = 5
// As DesktopTaskView is inflated in background, use initialSize=0 to avoid initPool.
private const val VIEW_POOL_INITIAL_SIZE = 0
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index b38d0d7..2d0f15e 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -5058,7 +5058,8 @@
mSplitHiddenTaskViewIndex = indexOfChild(mSplitHiddenTaskView);
mSplitSelectStateController
.setAnimateCurrentTaskDismissal(splitSelectSource.animateCurrentTaskDismissal
- && mSplitHiddenTaskView != null);
+ && mSplitHiddenTaskView != null
+ && !(mSplitHiddenTaskView instanceof DesktopTaskView));
// Prevent dismissing whole task if we're only initiating from one of 2 tasks in split pair
mSplitSelectStateController.setDismissingFromSplitPair(mSplitHiddenTaskView != null
diff --git a/quickstep/src/com/android/quickstep/views/TaskContainer.kt b/quickstep/src/com/android/quickstep/views/TaskContainer.kt
index 6cb7741..959516f 100644
--- a/quickstep/src/com/android/quickstep/views/TaskContainer.kt
+++ b/quickstep/src/com/android/quickstep/views/TaskContainer.kt
@@ -29,6 +29,7 @@
import com.android.launcher3.util.TransformingTouchDelegate
import com.android.quickstep.TaskOverlayFactory
import com.android.quickstep.TaskUtils
+import com.android.quickstep.ViewUtils.addAccessibleChildToList
import com.android.quickstep.recents.di.RecentsDependencies
import com.android.quickstep.recents.di.get
import com.android.quickstep.recents.di.getScope
@@ -157,12 +158,13 @@
fun destroy() {
digitalWellBeingToast?.destroy()
- if (enableRefactorTaskThumbnail()) {
- taskView.removeView(thumbnailView)
- }
snapshotView.scaleX = 1f
snapshotView.scaleY = 1f
overlay.destroy()
+ if (enableRefactorTaskThumbnail()) {
+ RecentsDependencies.getInstance().removeScope(snapshotView)
+ RecentsDependencies.getInstance().removeScope(this)
+ }
}
fun bindThumbnailView() {
@@ -181,12 +183,4 @@
showWindowsView?.let { addAccessibleChildToList(it, outChildren) }
digitalWellBeingToast?.let { addAccessibleChildToList(it, outChildren) }
}
-
- private fun addAccessibleChildToList(view: View, outChildren: ArrayList<View>) {
- if (view.includeForAccessibility()) {
- outChildren.add(view)
- } else {
- view.addChildrenForAccessibility(outChildren)
- }
- }
}
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.kt b/quickstep/src/com/android/quickstep/views/TaskView.kt
index cc64dba..28ecf96 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.kt
+++ b/quickstep/src/com/android/quickstep/views/TaskView.kt
@@ -82,6 +82,7 @@
import com.android.quickstep.orientation.RecentsPagedOrientationHandler
import com.android.quickstep.recents.di.RecentsDependencies
import com.android.quickstep.recents.di.get
+import com.android.quickstep.task.thumbnail.TaskThumbnailView
import com.android.quickstep.task.viewmodel.TaskViewModel
import com.android.quickstep.util.ActiveGestureErrorDetector
import com.android.quickstep.util.ActiveGestureLog
@@ -723,20 +724,23 @@
@StagePosition stagePosition: Int,
taskOverlayFactory: TaskOverlayFactory,
): TaskContainer {
- val thumbnailViewDeprecated: TaskThumbnailViewDeprecated = findViewById(thumbnailViewId)!!
+ val existingThumbnailView: View = findViewById(thumbnailViewId)!!
val snapshotView =
- if (enableRefactorTaskThumbnail()) {
- thumbnailViewDeprecated.visibility = GONE
- val indexOfSnapshotView = indexOfChild(thumbnailViewDeprecated)
- LayoutInflater.from(context).inflate(R.layout.task_thumbnail, this, false).also {
- it.id = thumbnailViewId
- addView(it, indexOfSnapshotView, thumbnailViewDeprecated.layoutParams)
+ when {
+ !enableRefactorTaskThumbnail() -> existingThumbnailView
+ existingThumbnailView is TaskThumbnailView -> existingThumbnailView
+ else -> {
+ val indexOfSnapshotView = indexOfChild(existingThumbnailView)
+ LayoutInflater.from(context)
+ .inflate(R.layout.task_thumbnail, this, false)
+ .also {
+ it.id = thumbnailViewId
+ addView(it, indexOfSnapshotView, existingThumbnailView.layoutParams)
+ removeView(existingThumbnailView)
+ }
}
- } else {
- thumbnailViewDeprecated
}
val iconView = getOrInflateIconView(iconViewId)
- val digitalWellBeingToast = findViewById<DigitalWellBeingToast>(digitalWellbeingBannerId)!!
return TaskContainer(
this,
task,
@@ -744,7 +748,7 @@
iconView,
TransformingTouchDelegate(iconView.asView()),
stagePosition,
- digitalWellBeingToast,
+ findViewById(digitalWellbeingBannerId)!!,
findViewById(showWindowViewId)!!,
taskOverlayFactory,
)
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarAutohideSuspendControllerTest.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarAutohideSuspendControllerTest.kt
index 59900b1..46a7733 100644
--- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarAutohideSuspendControllerTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarAutohideSuspendControllerTest.kt
@@ -24,6 +24,7 @@
import com.android.launcher3.taskbar.rules.TaskbarModeRule
import com.android.launcher3.taskbar.rules.TaskbarModeRule.Mode.TRANSIENT
import com.android.launcher3.taskbar.rules.TaskbarModeRule.TaskbarMode
+import com.android.launcher3.taskbar.rules.TaskbarSandboxComponent
import com.android.launcher3.taskbar.rules.TaskbarUnitTestRule
import com.android.launcher3.taskbar.rules.TaskbarUnitTestRule.InjectController
import com.android.launcher3.taskbar.rules.TaskbarWindowSandboxContext
@@ -33,36 +34,28 @@
import com.google.common.truth.Truth.assertThat
import org.junit.Rule
import org.junit.Test
-import org.junit.rules.TestRule
import org.junit.runner.RunWith
-import org.junit.runners.model.Statement
@RunWith(LauncherMultivalentJUnit::class)
@EmulatedDevices(["pixelTablet2023"])
class TaskbarAutohideSuspendControllerTest {
- @get:Rule(order = 0) val context = TaskbarWindowSandboxContext.create()
- @get:Rule(order = 1) val animatorTestRule = AnimatorTestRule(this)
- @get:Rule(order = 2)
- val systemUiProxyRule = TestRule { base, _ ->
- object : Statement() {
- override fun evaluate() {
- getInstrumentation().runOnMainSync {
- context.putObject(
- SystemUiProxy.INSTANCE,
- object : SystemUiProxy(context) {
- override fun notifyTaskbarAutohideSuspend(suspend: Boolean) {
- latestSuspendNotification = suspend
- }
- },
- )
+ @get:Rule(order = 0)
+ val context =
+ TaskbarWindowSandboxContext.create {
+ builder: TaskbarSandboxComponent.Builder,
+ sandboxContext: TaskbarWindowSandboxContext ->
+ builder.bindSystemUiProxy(
+ object : SystemUiProxy(sandboxContext) {
+ override fun notifyTaskbarAutohideSuspend(suspend: Boolean) {
+ latestSuspendNotification = suspend
+ }
}
- base.evaluate()
- }
+ )
}
- }
- @get:Rule(order = 3) val taskbarModeRule = TaskbarModeRule(context)
- @get:Rule(order = 4) val taskbarUnitTestRule = TaskbarUnitTestRule(this, context)
+ @get:Rule(order = 1) val animatorTestRule = AnimatorTestRule(this)
+ @get:Rule(order = 2) val taskbarModeRule = TaskbarModeRule(context)
+ @get:Rule(order = 3) val taskbarUnitTestRule = TaskbarUnitTestRule(this, context)
@InjectController lateinit var autohideSuspendController: TaskbarAutohideSuspendController
@InjectController lateinit var stashController: TaskbarStashController
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarScrimViewControllerTest.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarScrimViewControllerTest.kt
index 12e84b8..b1bb472 100644
--- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarScrimViewControllerTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/TaskbarScrimViewControllerTest.kt
@@ -24,6 +24,7 @@
import com.android.launcher3.taskbar.rules.TaskbarModeRule.Mode.PINNED
import com.android.launcher3.taskbar.rules.TaskbarModeRule.Mode.TRANSIENT
import com.android.launcher3.taskbar.rules.TaskbarModeRule.TaskbarMode
+import com.android.launcher3.taskbar.rules.TaskbarSandboxComponent
import com.android.launcher3.taskbar.rules.TaskbarUnitTestRule
import com.android.launcher3.taskbar.rules.TaskbarUnitTestRule.InjectController
import com.android.launcher3.taskbar.rules.TaskbarWindowSandboxContext
@@ -42,7 +43,20 @@
@RunWith(LauncherMultivalentJUnit::class)
@EmulatedDevices(["pixelTablet2023"])
class TaskbarScrimViewControllerTest {
- @get:Rule(order = 0) val context = TaskbarWindowSandboxContext.create()
+ @get:Rule(order = 0)
+ val context =
+ TaskbarWindowSandboxContext.create {
+ builder: TaskbarSandboxComponent.Builder,
+ sandboxContext: TaskbarWindowSandboxContext ->
+ builder.bindSystemUiProxy(
+ object : SystemUiProxy(sandboxContext) {
+ override fun onBackPressed() {
+ super.onBackPressed()
+ backPressed = true
+ }
+ }
+ )
+ }
@get:Rule(order = 1) val taskbarModeRule = TaskbarModeRule(context)
@get:Rule(order = 2) val animatorTestRule = AnimatorTestRule(this)
@get:Rule(order = 3) val taskbarUnitTestRule = TaskbarUnitTestRule(this, context)
@@ -53,6 +67,8 @@
private val animationDuration =
context.resources.getInteger(android.R.integer.config_mediumAnimTime).toLong()
+ private var backPressed = false
+
@Test
@TaskbarMode(PINNED)
fun testOnTaskbarVisibleChanged_onlyTaskbarVisible_noScrim() {
@@ -130,16 +146,6 @@
@Test
@TaskbarMode(PINNED)
fun testOnClick_scrimShown_performsSystemBack() {
- var backPressed = false
- context.putObject(
- SystemUiProxy.INSTANCE,
- object : SystemUiProxy(context) {
- override fun onBackPressed() {
- backPressed = true
- }
- },
- )
-
getInstrumentation().runOnMainSync {
scrimViewController.updateStateForSysuiFlags(SYSUI_STATE_BUBBLES_EXPANDED, true)
scrimViewController.onTaskbarVisibilityChanged(VISIBLE)
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutControllerTest.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutControllerTest.kt
index 0eea741..50bb9bc 100644
--- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutControllerTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/flyout/BubbleBarFlyoutControllerTest.kt
@@ -115,11 +115,13 @@
fun hideFlyout_removedFromContainer() {
InstrumentationRegistry.getInstrumentation().runOnMainSync {
flyoutController.setUpAndShowFlyout(flyoutMessage) {}
+ assertThat(flyoutController.hasFlyout()).isTrue()
assertThat(flyoutContainer.childCount).isEqualTo(1)
flyoutController.collapseFlyout {}
animatorTestRule.advanceTimeBy(300)
}
assertThat(flyoutContainer.childCount).isEqualTo(0)
+ assertThat(flyoutController.hasFlyout()).isFalse()
}
@Test
@@ -189,6 +191,61 @@
assertThat(flyoutCallbacks.flyoutClicked).isTrue()
}
+ @Test
+ fun updateFlyoutWhileExpanding() {
+ InstrumentationRegistry.getInstrumentation().runOnMainSync {
+ flyoutController.setUpAndShowFlyout(flyoutMessage) {}
+ assertThat(flyoutController.hasFlyout()).isTrue()
+ val flyout = flyoutContainer.findViewById<View>(R.id.bubble_bar_flyout_view)
+ assertThat(flyout.findViewById<TextView>(R.id.bubble_flyout_text).text)
+ .isEqualTo("message")
+ // advance the animation about halfway
+ animatorTestRule.advanceTimeBy(100)
+ }
+ assertThat(flyoutController.hasFlyout()).isTrue()
+
+ val newFlyoutMessage = flyoutMessage.copy(message = "new message")
+ InstrumentationRegistry.getInstrumentation().runOnMainSync {
+ val flyout = flyoutContainer.findViewById<View>(R.id.bubble_bar_flyout_view)
+ // set negative translation to verify that the top boundary extends as a result of
+ // updating while expanding
+ flyout.translationY = -50f
+ flyoutController.updateFlyoutWhileExpanding(newFlyoutMessage)
+ assertThat(flyout.findViewById<TextView>(R.id.bubble_flyout_text).text)
+ .isEqualTo("new message")
+ }
+ assertThat(flyoutCallbacks.topBoundaryExtendedSpace).isEqualTo(50)
+ }
+
+ @Test
+ fun updateFlyoutFullyExpanded() {
+ InstrumentationRegistry.getInstrumentation().runOnMainSync {
+ flyoutController.setUpAndShowFlyout(flyoutMessage) {}
+ animatorTestRule.advanceTimeBy(300)
+ }
+ assertThat(flyoutController.hasFlyout()).isTrue()
+
+ val newFlyoutMessage = flyoutMessage.copy(message = "new message")
+ InstrumentationRegistry.getInstrumentation().runOnMainSync {
+ val flyout = flyoutContainer.findViewById<View>(R.id.bubble_bar_flyout_view)
+ // set negative translation to verify that the top boundary extends as a result of
+ // updating while fully expanded
+ flyout.translationY = -50f
+ flyoutController.updateFlyoutFullyExpanded(newFlyoutMessage) {}
+
+ // advance the timer so that the fade out animation plays
+ animatorTestRule.advanceTimeBy(250)
+ assertThat(flyout.alpha).isEqualTo(0)
+ assertThat(flyout.findViewById<TextView>(R.id.bubble_flyout_text).text)
+ .isEqualTo("new message")
+
+ // advance the timer so that the fade in animation plays
+ animatorTestRule.advanceTimeBy(250)
+ assertThat(flyout.alpha).isEqualTo(1)
+ }
+ assertThat(flyoutCallbacks.topBoundaryExtendedSpace).isEqualTo(50)
+ }
+
class FakeFlyoutCallbacks : FlyoutCallbacks {
var topBoundaryExtendedSpace = 0
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/rules/TaskbarWindowSandboxContext.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/rules/TaskbarWindowSandboxContext.kt
index 2d3bfd6..a331a25 100644
--- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/rules/TaskbarWindowSandboxContext.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/rules/TaskbarWindowSandboxContext.kt
@@ -24,8 +24,13 @@
import androidx.test.core.app.ApplicationProvider
import com.android.launcher3.FakeLauncherPrefs
import com.android.launcher3.LauncherPrefs
+import com.android.launcher3.dagger.LauncherAppComponent
+import com.android.launcher3.dagger.LauncherAppSingleton
import com.android.launcher3.util.MainThreadInitializedObject.ObjectSandbox
import com.android.launcher3.util.SandboxApplication
+import com.android.quickstep.SystemUiProxy
+import dagger.BindsInstance
+import dagger.Component
import org.junit.rules.ExternalResource
import org.junit.rules.RuleChain
import org.junit.rules.TestRule
@@ -50,7 +55,14 @@
private const val VIRTUAL_DISPLAY_NAME = "TaskbarSandboxDisplay"
/** Creates a [SandboxApplication] for Taskbar tests. */
- fun create(): TaskbarWindowSandboxContext {
+ fun create(
+ daggerComponentBinder:
+ ((
+ builder: TaskbarSandboxComponent.Builder,
+ sandboxContext: TaskbarWindowSandboxContext,
+ ) -> TaskbarSandboxComponent.Builder)? =
+ null
+ ): TaskbarWindowSandboxContext {
val base = ApplicationProvider.getApplicationContext<Context>()
val displayManager = checkNotNull(base.getSystemService(DisplayManager::class.java))
@@ -67,10 +79,21 @@
)
}
- return TaskbarWindowSandboxContext(
- SandboxApplication(base.createDisplayContext(virtualDisplay.display)),
- virtualDisplay,
- )
+ val sandboxApplication =
+ SandboxApplication(base.createDisplayContext(virtualDisplay.display))
+ val taskbarWindowSandboxContext =
+ TaskbarWindowSandboxContext(sandboxApplication, virtualDisplay)
+
+ if (daggerComponentBinder != null) {
+ sandboxApplication.initDaggerComponent(
+ daggerComponentBinder(
+ DaggerTaskbarSandboxComponent.builder(),
+ taskbarWindowSandboxContext,
+ )
+ )
+ }
+
+ return taskbarWindowSandboxContext
}
}
}
@@ -80,3 +103,14 @@
override fun after() = virtualDisplay.release()
}
}
+
+@LauncherAppSingleton
+@Component
+interface TaskbarSandboxComponent : LauncherAppComponent {
+ @Component.Builder
+ interface Builder : LauncherAppComponent.Builder {
+ @BindsInstance fun bindSystemUiProxy(proxy: SystemUiProxy): Builder
+
+ override fun build(): TaskbarSandboxComponent
+ }
+}
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/LauncherSwipeHandlerV2Test.kt b/quickstep/tests/multivalentTests/src/com/android/quickstep/LauncherSwipeHandlerV2Test.kt
index 1f88743..6212b59 100644
--- a/quickstep/tests/multivalentTests/src/com/android/quickstep/LauncherSwipeHandlerV2Test.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/LauncherSwipeHandlerV2Test.kt
@@ -20,6 +20,7 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.launcher3.R
+import com.android.launcher3.taskbar.rules.DaggerTaskbarSandboxComponent
import com.android.launcher3.util.LauncherModelHelper
import com.android.systemui.contextualeducation.GestureType
import com.android.systemui.shared.system.InputConsumerController
@@ -58,7 +59,9 @@
@Before
fun setup() {
- sandboxContext.putObject(SystemUiProxy.INSTANCE, systemUiProxy)
+ sandboxContext.initDaggerComponent(
+ DaggerTaskbarSandboxComponent.builder().bindSystemUiProxy(systemUiProxy)
+ )
val deviceState = mock(RecentsAnimationDeviceState::class.java)
whenever(deviceState.rotationTouchHelper).thenReturn(mock(RotationTouchHelper::class.java))
gestureState = spy(GestureState(OverviewComponentObserver(sandboxContext, deviceState), 0))
@@ -71,7 +74,7 @@
gestureState,
0,
false,
- inputConsumerController
+ inputConsumerController,
)
underTest.onGestureStarted(/* isLikelyToStartNewTask= */ false)
}
@@ -83,7 +86,7 @@
verify(systemUiProxy)
.updateContextualEduStats(
/* isTrackpadGesture= */ eq(true),
- eq(GestureType.HOME.toString())
+ eq(GestureType.HOME.toString()),
)
}
@@ -93,7 +96,7 @@
verify(systemUiProxy)
.updateContextualEduStats(
/* isTrackpadGesture= */ eq(false),
- eq(GestureType.HOME.toString())
+ eq(GestureType.HOME.toString()),
)
}
}
diff --git a/quickstep/tests/src/com/android/quickstep/TaplOverviewIconTest.java b/quickstep/tests/src/com/android/quickstep/TaplOverviewIconTest.java
index 9bc1c59..2c275f4 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplOverviewIconTest.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplOverviewIconTest.java
@@ -22,7 +22,7 @@
import android.platform.test.annotations.PlatinumTest;
import com.android.launcher3.tapl.Overview;
-import com.android.launcher3.tapl.OverviewTask.OverviewSplitTask;
+import com.android.launcher3.tapl.OverviewTask.OverviewTaskContainer;
import com.android.launcher3.tapl.OverviewTaskMenu;
import com.android.launcher3.ui.AbstractLauncherUiTest;
import com.android.launcher3.uioverrides.QuickstepLauncher;
@@ -76,7 +76,7 @@
taskMenu.touchOutsideTaskMenuToDismiss();
OverviewTaskMenu splitMenu = overview.getCurrentTask().tapMenu(
- OverviewSplitTask.SPLIT_BOTTOM_OR_RIGHT);
+ OverviewTaskContainer.SPLIT_BOTTOM_OR_RIGHT);
assertTrue("App info item not appearing in expanded split task's menu.",
splitMenu.hasMenuItem("App info"));
splitMenu.touchOutsideTaskMenuToDismiss();
diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsOverviewDesktop.kt b/quickstep/tests/src/com/android/quickstep/TaplTestsOverviewDesktop.kt
index 694a382..2a8afbf 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplTestsOverviewDesktop.kt
+++ b/quickstep/tests/src/com/android/quickstep/TaplTestsOverviewDesktop.kt
@@ -21,6 +21,8 @@
import androidx.test.uiautomator.By
import androidx.test.uiautomator.Until
import com.android.launcher3.BuildConfig
+import com.android.launcher3.tapl.LaunchedAppState
+import com.android.launcher3.tapl.OverviewTask
import com.android.launcher3.ui.AbstractLauncherUiTest
import com.android.launcher3.ui.PortraitLandscapeRunner.PortraitLandscape
import com.android.launcher3.uioverrides.QuickstepLauncher
@@ -45,22 +47,16 @@
@Test
@PortraitLandscape
fun enterDesktopViaOverviewMenu() {
- // Move last launched TEST_ACTIVITY_2 into Desktop
- mLauncher.workspace
- .switchToOverview()
- .getTestActivityTask(TEST_ACTIVITY_2)
- .tapMenu()
- .tapDesktopMenuItem()
- assertTestAppLaunched(TEST_ACTIVITY_2)
+ mLauncher.workspace.switchToOverview()
+ moveTaskToDesktop(TEST_ACTIVITY_2) // Move last launched TEST_ACTIVITY_2 into Desktop
// Scroll back to TEST_ACTIVITY_1, then move it into Desktop
mLauncher
.goHome()
.switchToOverview()
.apply { flingForward() }
- .getTestActivityTask(TEST_ACTIVITY_1)
- .tapMenu()
- .tapDesktopMenuItem()
+ .also { moveTaskToDesktop(TEST_ACTIVITY_1) }
+
TEST_ACTIVITIES.forEach { assertTestAppLaunched(it) }
// Launch static DesktopTaskView
@@ -73,6 +69,83 @@
TEST_ACTIVITIES.forEach { assertTestAppLaunched(it) }
}
+ @Test
+ @PortraitLandscape
+ fun dismissFocusedTasks_thenDesktopIsCentered() {
+ // Create DesktopTaskView
+ mLauncher.goHome().switchToOverview()
+ moveTaskToDesktop(TEST_ACTIVITY_2)
+
+ // Create a new task activity to be the focused task
+ mLauncher.goHome()
+ startTestActivity(TEST_ACTIVITY_EXTRA)
+
+ val overview = mLauncher.goHome().switchToOverview()
+
+ // Dismiss focused task
+ val focusedTask1 = overview.currentTask
+ assertTaskContentDescription(focusedTask1, TEST_ACTIVITY_EXTRA)
+ focusedTask1.dismiss()
+
+ // Dismiss new focused task
+ val focusedTask2 = overview.currentTask
+ assertTaskContentDescription(focusedTask2, TEST_ACTIVITY_1)
+ focusedTask2.dismiss()
+
+ // Dismiss DesktopTaskView
+ val desktopTask = overview.currentTask
+ assertWithMessage("The current task is not a Desktop.").that(desktopTask.isDesktop).isTrue()
+ desktopTask.dismiss()
+
+ assertWithMessage("Still have tasks after dismissing all the tasks")
+ .that(mLauncher.workspace.switchToOverview().hasTasks())
+ .isFalse()
+ }
+
+ @Test
+ @PortraitLandscape
+ fun dismissFocusedTask_thenDesktopTask_thenFocusedTaskIsCentered() {
+ // Create extra activity to be DesktopTaskView
+ startTestActivity(TEST_ACTIVITY_EXTRA)
+ mLauncher.goHome().switchToOverview()
+ val desktop = moveTaskToDesktop(TEST_ACTIVITY_EXTRA)
+
+ val overview = desktop.switchToOverview()
+
+ // Dismiss focused task
+ val focusedTask1 = overview.getTestActivityTask(TEST_ACTIVITY_2)
+ assertTaskContentDescription(focusedTask1, TEST_ACTIVITY_2)
+ focusedTask1.dismiss()
+
+ // Dismiss DesktopTaskView
+ val desktopTask = overview.currentTask
+ assertWithMessage("The current task is not a Desktop.").that(desktopTask.isDesktop).isTrue()
+ desktopTask.dismiss()
+
+ // Dismiss focused task
+ val focusedTask2 = overview.currentTask
+ assertTaskContentDescription(focusedTask2, TEST_ACTIVITY_1)
+ focusedTask2.dismiss()
+
+ assertWithMessage("Still have tasks after dismissing all the tasks")
+ .that(mLauncher.workspace.switchToOverview().hasTasks())
+ .isFalse()
+ }
+
+ private fun assertTaskContentDescription(task: OverviewTask, activityIndex: Int) {
+ assertWithMessage("The current task content description is not TestActivity$activityIndex.")
+ .that(task.containsContentDescription("TestActivity$activityIndex"))
+ .isTrue()
+ }
+
+ private fun moveTaskToDesktop(activityIndex: Int): LaunchedAppState {
+ return mLauncher.overview
+ .getTestActivityTask(activityIndex)
+ .tapMenu()
+ .tapDesktopMenuItem()
+ .also { assertTestAppLaunched(activityIndex) }
+ }
+
private fun startTestAppsWithCheck() {
TEST_ACTIVITIES.forEach {
startTestActivity(it)
@@ -91,7 +164,7 @@
.that(
mDevice.wait(
Until.hasObject(By.pkg(getAppPackageName()).text("TestActivity$index")),
- DEFAULT_UI_TIMEOUT
+ DEFAULT_UI_TIMEOUT,
)
)
.isTrue()
@@ -100,6 +173,7 @@
companion object {
const val TEST_ACTIVITY_1 = 2
const val TEST_ACTIVITY_2 = 3
+ const val TEST_ACTIVITY_EXTRA = 4
val TEST_ACTIVITIES = listOf(TEST_ACTIVITY_1, TEST_ACTIVITY_2)
}
}
diff --git a/quickstep/tests/src/com/android/quickstep/desktop/WindowAnimatorTest.kt b/quickstep/tests/src/com/android/quickstep/desktop/WindowAnimatorTest.kt
deleted file mode 100644
index e5e6df3..0000000
--- a/quickstep/tests/src/com/android/quickstep/desktop/WindowAnimatorTest.kt
+++ /dev/null
@@ -1,139 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.quickstep.desktop
-
-import android.animation.ValueAnimator
-import android.content.Context
-import android.content.res.Resources
-import android.graphics.Rect
-import android.util.DisplayMetrics
-import android.view.SurfaceControl
-import android.window.TransitionInfo
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.SmallTest
-import androidx.test.internal.runner.junit4.statement.UiThreadStatement.runOnUiThread
-import com.android.app.animation.Interpolators
-import com.android.launcher3.desktop.WindowAnimator
-import com.google.common.truth.Truth.assertThat
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.ArgumentMatchers.any
-import org.mockito.ArgumentMatchers.anyFloat
-import org.mockito.kotlin.mock
-import org.mockito.kotlin.whenever
-
-@SmallTest
-@RunWith(AndroidJUnit4::class)
-class WindowAnimatorTest {
-
- private val context = mock<Context>()
- private val resources = mock<Resources>()
- private val transaction = mock<SurfaceControl.Transaction>()
- private val change = mock<TransitionInfo.Change>()
- private val leash = mock<SurfaceControl>()
-
- private val displayMetrics = DisplayMetrics().apply { density = 1f }
-
- @Before
- fun setup() {
- whenever(context.resources).thenReturn(resources)
- whenever(resources.displayMetrics).thenReturn(displayMetrics)
- whenever(change.leash).thenReturn(leash)
- whenever(change.startAbsBounds).thenReturn(START_BOUNDS)
- whenever(transaction.setPosition(any(), anyFloat(), anyFloat())).thenReturn(transaction)
- whenever(transaction.setScale(any(), anyFloat(), anyFloat())).thenReturn(transaction)
- }
-
- @Test
- fun createBoundsAnimator_returnsCorrectDefaultAnimatorParams() = runOnUiThread {
- val boundsAnimParams =
- WindowAnimator.BoundsAnimationParams(
- durationMs = 100L,
- interpolator = Interpolators.STANDARD_ACCELERATE,
- )
-
- val valueAnimator =
- WindowAnimator.createBoundsAnimator(context, boundsAnimParams, change, transaction)
-
- assertThat(valueAnimator.duration).isEqualTo(100L)
- assertThat(valueAnimator.interpolator).isEqualTo(Interpolators.STANDARD_ACCELERATE)
- assertStartAndEndBounds(valueAnimator, startBounds = START_BOUNDS, endBounds = START_BOUNDS)
- }
-
- @Test
- fun createBoundsAnimator_startScaleAndOffset_returnsCorrectBounds() = runOnUiThread {
- val bounds = Rect(/* left= */ 100, /* top= */ 200, /* right= */ 300, /* bottom= */ 400)
- whenever(change.startAbsBounds).thenReturn(bounds)
- val boundsAnimParams =
- WindowAnimator.BoundsAnimationParams(
- durationMs = 100L,
- startOffsetYDp = 10f,
- startScale = 0.5f,
- interpolator = Interpolators.STANDARD_ACCELERATE,
- )
-
- val valueAnimator =
- WindowAnimator.createBoundsAnimator(context, boundsAnimParams, change, transaction)
-
- assertStartAndEndBounds(
- valueAnimator,
- startBounds =
- Rect(/* left= */ 150, /* top= */ 260, /* right= */ 250, /* bottom= */ 360),
- endBounds = bounds,
- )
- }
-
- @Test
- fun createBoundsAnimator_endScaleAndOffset_returnsCorrectBounds() = runOnUiThread {
- val bounds = Rect(/* left= */ 100, /* top= */ 200, /* right= */ 300, /* bottom= */ 400)
- whenever(change.startAbsBounds).thenReturn(bounds)
- val boundsAnimParams =
- WindowAnimator.BoundsAnimationParams(
- durationMs = 100L,
- endOffsetYDp = 10f,
- endScale = 0.5f,
- interpolator = Interpolators.STANDARD_ACCELERATE,
- )
-
- val valueAnimator =
- WindowAnimator.createBoundsAnimator(context, boundsAnimParams, change, transaction)
-
- assertStartAndEndBounds(
- valueAnimator,
- startBounds = bounds,
- endBounds = Rect(/* left= */ 150, /* top= */ 260, /* right= */ 250, /* bottom= */ 360),
- )
- }
-
- private fun assertStartAndEndBounds(
- valueAnimator: ValueAnimator,
- startBounds: Rect,
- endBounds: Rect,
- ) {
- valueAnimator.start()
- valueAnimator.animatedValue
- assertThat(valueAnimator.animatedValue).isEqualTo(startBounds)
- valueAnimator.end()
- assertThat(valueAnimator.animatedValue).isEqualTo(endBounds)
- }
-
- companion object {
- private val START_BOUNDS =
- Rect(/* left= */ 10, /* top= */ 20, /* right= */ 30, /* bottom= */ 40)
- }
-}
diff --git a/quickstep/tests/src/com/android/quickstep/util/SplitScreenTestUtils.kt b/quickstep/tests/src/com/android/quickstep/util/SplitScreenTestUtils.kt
index 82361aa..99c74be 100644
--- a/quickstep/tests/src/com/android/quickstep/util/SplitScreenTestUtils.kt
+++ b/quickstep/tests/src/com/android/quickstep/util/SplitScreenTestUtils.kt
@@ -43,11 +43,11 @@
val currentTask = overviewWithSplitPair.currentTask
currentTask.containsContentDescription(
By.pkg(AbstractLauncherUiTest.getAppPackageName()).text("TestActivity3").toString(),
- OverviewTask.OverviewSplitTask.SPLIT_TOP_OR_LEFT
+ OverviewTask.OverviewTaskContainer.SPLIT_TOP_OR_LEFT,
)
currentTask.containsContentDescription(
By.pkg(AbstractLauncherUiTest.getAppPackageName()).text("TestActivity2").toString(),
- OverviewTask.OverviewSplitTask.SPLIT_BOTTOM_OR_RIGHT
+ OverviewTask.OverviewTaskContainer.SPLIT_BOTTOM_OR_RIGHT,
)
return overviewWithSplitPair
}
diff --git a/res/drawable/work_mode_fab_background.xml b/res/drawable/work_mode_fab_background.xml
index 6be33e8..fd948d1 100644
--- a/res/drawable/work_mode_fab_background.xml
+++ b/res/drawable/work_mode_fab_background.xml
@@ -19,6 +19,9 @@
<shape android:shape="rectangle">
<corners android:radius="@dimen/work_fab_radius" />
<solid android:color="@color/work_fab_bg_color" />
+ <padding
+ android:left="@dimen/work_mode_fab_background_horizontal_padding"
+ android:right="@dimen/work_mode_fab_background_horizontal_padding"/>
</shape>
</item>
</ripple>
diff --git a/res/layout/widgets_two_pane_sheet_paged_view.xml b/res/layout/widgets_two_pane_sheet_paged_view.xml
index 71c77b5..33a50b0 100644
--- a/res/layout/widgets_two_pane_sheet_paged_view.xml
+++ b/res/layout/widgets_two_pane_sheet_paged_view.xml
@@ -15,7 +15,7 @@
<merge xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:launcher="http://schemas.android.com/apk/res-auto">
- <FrameLayout
+ <LinearLayout
android:id="@+id/widgets_two_pane_sheet_paged_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
@@ -23,40 +23,17 @@
android:layout_gravity="start"
android:clipChildren="false"
android:clipToPadding="false"
- android:layout_alignParentStart="true">
- <!-- Note: the paddingHorizontal has to be on WidgetPagedView level so that talkback
- correctly orders the lists to be after the search and suggestions header. See b/209579563.
- -->
- <com.android.launcher3.widget.picker.WidgetPagedView
- android:id="@+id/widgets_view_pager"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:clipToPadding="false"
- android:paddingHorizontal="@dimen/widget_list_horizontal_margin_two_pane"
- android:descendantFocusability="afterDescendants"
- launcher:pageIndicator="@+id/tabs" >
-
- <com.android.launcher3.widget.picker.WidgetsRecyclerView
- android:id="@+id/primary_widgets_list_view"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:clipToPadding="false" />
-
- <com.android.launcher3.widget.picker.WidgetsRecyclerView
- android:id="@+id/work_widgets_list_view"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:clipToPadding="false" />
-
- </com.android.launcher3.widget.picker.WidgetPagedView>
-
+ android:layout_alignParentStart="true"
+ android:orientation="vertical">
<!-- SearchAndRecommendationsView without the tab layout as well -->
<!-- Note: the horizontal padding matches with the WidgetPagedView -->
- <com.android.launcher3.views.StickyHeaderLayout
+ <LinearLayout
android:id="@+id/search_and_recommendations_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clipToOutline="true"
+ android:elevation="1dp"
+ android:background="?attr/widgetPickerPrimarySurfaceColor"
android:paddingHorizontal="@dimen/widget_list_horizontal_margin_two_pane"
android:orientation="vertical">
@@ -67,6 +44,7 @@
android:orientation="horizontal"
android:background="?attr/widgetPickerPrimarySurfaceColor"
android:gravity="center_vertical"
+ android:layout_marginBottom="8dp"
launcher:layout_sticky="true">
<FrameLayout
android:layout_width="0dp"
@@ -98,7 +76,6 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/suggestions_header"
- android:layout_marginTop="8dp"
android:orientation="horizontal"
android:background="?attr/widgetPickerPrimarySurfaceColor"
launcher:layout_sticky="true">
@@ -140,6 +117,31 @@
style="?android:attr/borderlessButtonStyle" />
</com.android.launcher3.workprofile.PersonalWorkSlidingTabStrip>
- </com.android.launcher3.views.StickyHeaderLayout>
- </FrameLayout>
+ </LinearLayout>
+ <!-- Note: the paddingHorizontal has to be on WidgetPagedView level so that talkback
+ correctly orders the lists to be after the search and suggestions header. See b/209579563.
+ -->
+ <com.android.launcher3.widget.picker.WidgetPagedView
+ android:id="@+id/widgets_view_pager"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:clipToPadding="false"
+ android:paddingHorizontal="@dimen/widget_list_horizontal_margin_two_pane"
+ android:descendantFocusability="afterDescendants"
+ launcher:pageIndicator="@+id/tabs" >
+
+ <com.android.launcher3.widget.picker.WidgetsRecyclerView
+ android:id="@+id/primary_widgets_list_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:clipToPadding="false" />
+
+ <com.android.launcher3.widget.picker.WidgetsRecyclerView
+ android:id="@+id/work_widgets_list_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:clipToPadding="false" />
+
+ </com.android.launcher3.widget.picker.WidgetPagedView>
+ </LinearLayout>
</merge>
diff --git a/res/layout/widgets_two_pane_sheet_recyclerview.xml b/res/layout/widgets_two_pane_sheet_recyclerview.xml
index c6b3b74..94f141b 100644
--- a/res/layout/widgets_two_pane_sheet_recyclerview.xml
+++ b/res/layout/widgets_two_pane_sheet_recyclerview.xml
@@ -15,28 +15,22 @@
<merge xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:launcher="http://schemas.android.com/apk/res-auto">
- <FrameLayout
+ <LinearLayout
android:id="@+id/widgets_two_pane_sheet_recyclerview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="start"
android:layout_gravity="start"
android:clipChildren="false"
- android:layout_alignParentStart="true">
-
- <com.android.launcher3.widget.picker.WidgetsRecyclerView
- android:id="@+id/primary_widgets_list_view"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_marginHorizontal="@dimen/widget_list_horizontal_margin_two_pane"
- android:clipToPadding="false" />
-
+ android:layout_alignParentStart="true"
+ android:orientation="vertical">
<!-- SearchAndRecommendationsView without the tab layout as well -->
- <com.android.launcher3.views.StickyHeaderLayout
+ <LinearLayout
android:id="@+id/search_and_recommendations_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clipToOutline="true"
+ android:background="?attr/widgetPickerPrimarySurfaceColor"
android:orientation="vertical">
<LinearLayout
@@ -83,6 +77,13 @@
android:background="?attr/widgetPickerPrimarySurfaceColor"
launcher:layout_sticky="true">
</FrameLayout>
- </com.android.launcher3.views.StickyHeaderLayout>
- </FrameLayout>
+ </LinearLayout>
+
+ <com.android.launcher3.widget.picker.WidgetsRecyclerView
+ android:id="@+id/primary_widgets_list_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_marginHorizontal="@dimen/widget_list_horizontal_margin_two_pane"
+ android:clipToPadding="false" />
+ </LinearLayout>
</merge>
\ No newline at end of file
diff --git a/res/layout/work_mode_fab.xml b/res/layout/work_mode_fab.xml
index b3484c9..fc59e56 100644
--- a/res/layout/work_mode_fab.xml
+++ b/res/layout/work_mode_fab.xml
@@ -23,18 +23,16 @@
android:gravity="center_vertical"
android:background="@drawable/work_mode_fab_background"
android:forceHasOverlappingRendering="false"
- android:contentDescription="@string/work_apps_pause_btn_text"
- android:paddingStart="@dimen/work_mode_fab_background_start_padding"
- android:paddingEnd="@dimen/work_mode_fab_background_end_padding"
- android:animateLayoutChanges="true">
+ android:contentDescription="@string/work_apps_pause_btn_text">
<ImageView
android:id="@+id/work_icon"
android:layout_width="@dimen/work_fab_icon_size"
android:layout_height="@dimen/work_fab_icon_size"
+ android:layout_marginVertical="@dimen/work_fab_icon_vertical_margin"
android:importantForAccessibility="no"
- android:layout_marginEnd="@dimen/work_fab_icon_end_margin"
android:src="@drawable/ic_corp_off"
android:tint="@color/work_fab_icon_color"
+ android:layout_marginStart="@dimen/work_fab_icon_start_margin_expanded"
android:scaleType="center"/>
<TextView
android:id="@+id/pause_text"
@@ -46,8 +44,8 @@
android:includeFontPadding="false"
android:textDirection="locale"
android:text="@string/work_apps_pause_btn_text"
+ android:layout_marginStart="@dimen/work_fab_text_start_margin"
android:layout_marginEnd="@dimen/work_fab_text_end_margin"
- android:ellipsize="end"
android:maxLines="1"
style="@style/TextHeadline"/>
</com.android.launcher3.allapps.WorkModeSwitch>
diff --git a/res/values-ms/strings.xml b/res/values-ms/strings.xml
index 5ddf8a2..6941139 100644
--- a/res/values-ms/strings.xml
+++ b/res/values-ms/strings.xml
@@ -189,7 +189,7 @@
<string name="work_apps_enable_btn_text" msgid="1736198302467317371">"Nyahjeda"</string>
<string name="developer_options_filter_hint" msgid="5896817443635989056">"Tapis"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Gagal: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
- <string name="private_space_label" msgid="2359721649407947001">"Ruang privasi"</string>
+ <string name="private_space_label" msgid="2359721649407947001">"Ruang persendirian"</string>
<string name="private_space_secondary_label" msgid="9203933341714508907">"Ketik untuk menyediakan atau membuka"</string>
<string name="ps_container_title" msgid="4391796149519594205">"Persendirian"</string>
<string name="ps_container_settings" msgid="6059734123353320479">"Tetapan Ruang Peribadi"</string>
diff --git a/res/values/config.xml b/res/values/config.xml
index 504218b..b0b7aa2 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -79,7 +79,6 @@
<string name="contextual_edu_manager_class" translatable="false"></string>
<!-- Used for determining category of a widget presented in widget recommendations. -->
<string name="widget_recommendation_category_provider_class" translatable="false"></string>
- <string name="api_wrapper_class" translatable="false"></string>
<!-- Default packages -->
<string name="wallpaper_picker_package" translatable="false"></string>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 731e24e..037687d 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -156,15 +156,16 @@
<dimen name="work_fab_height">56dp</dimen>
<dimen name="work_fab_radius">16dp</dimen>
<dimen name="work_fab_icon_size">24dp</dimen>
- <dimen name="work_fab_icon_end_margin">12dp</dimen>
- <dimen name="work_fab_text_end_margin">16dp</dimen>
+ <dimen name="work_fab_icon_vertical_margin">16dp</dimen>
+ <dimen name="work_fab_icon_start_margin_expanded">4dp</dimen>
+ <dimen name="work_fab_text_start_margin">8dp</dimen>
+ <dimen name="work_fab_text_end_margin">10dp</dimen>
<dimen name="work_card_padding_horizontal">10dp</dimen>
<dimen name="work_fab_width">214dp</dimen>
<dimen name="work_card_button_height">52dp</dimen>
<dimen name="work_fab_margin">16dp</dimen>
<dimen name="work_fab_margin_bottom">20dp</dimen>
- <dimen name="work_mode_fab_background_start_padding">16dp</dimen>
- <dimen name="work_mode_fab_background_end_padding">4dp</dimen>
+ <dimen name="work_mode_fab_background_horizontal_padding">16dp</dimen>
<dimen name="work_profile_footer_padding">20dp</dimen>
<dimen name="work_edu_card_margin">16dp</dimen>
<dimen name="work_edu_card_radius">16dp</dimen>
diff --git a/src/com/android/launcher3/BaseActivity.java b/src/com/android/launcher3/BaseActivity.java
index 3774ae3..2e75261 100644
--- a/src/com/android/launcher3/BaseActivity.java
+++ b/src/com/android/launcher3/BaseActivity.java
@@ -306,10 +306,6 @@
removeActivityFlags(ACTIVITY_STATE_RESUMED | ACTIVITY_STATE_DEFERRED_RESUMED);
}
- public boolean isPaused() {
- return !hasBeenResumed() && (mActivityFlags & ACTIVITY_STATE_DEFERRED_RESUMED) == 0;
- }
-
/**
* Sets the activity to appear as resumed.
*/
diff --git a/src/com/android/launcher3/BaseDraggingActivity.java b/src/com/android/launcher3/BaseDraggingActivity.java
index 5f5fb72..50e78ac 100644
--- a/src/com/android/launcher3/BaseDraggingActivity.java
+++ b/src/com/android/launcher3/BaseDraggingActivity.java
@@ -50,8 +50,6 @@
// automatically when user interacts with the launcher.
public static final Object AUTO_CANCEL_ACTION_MODE = new Object();
- private boolean mIsThemeUpdatedBeforeRecreate;
-
private ActionMode mCurrentActionMode;
private int mThemeRes = R.style.AppTheme;
@@ -82,13 +80,8 @@
updateTheme();
}
- public boolean isThemeUpdatedBeforeRecreate() {
- return mIsThemeUpdatedBeforeRecreate;
- }
-
protected void updateTheme() {
if (mThemeRes != Themes.getActivityThemeRes(this)) {
- mIsThemeUpdatedBeforeRecreate = true;
recreate();
}
}
diff --git a/src/com/android/launcher3/Hotseat.java b/src/com/android/launcher3/Hotseat.java
index 6b478be..27602af 100644
--- a/src/com/android/launcher3/Hotseat.java
+++ b/src/com/android/launcher3/Hotseat.java
@@ -186,9 +186,6 @@
*/
public void adjustForBubbleBar(boolean isBubbleBarVisible) {
DeviceProfile dp = mActivity.getDeviceProfile();
- if (!dp.shouldAdjustHotseatForBubbleBar(getContext(), isBubbleBarVisible)) {
- return;
- }
ShortcutAndWidgetContainer icons = getShortcutsAndWidgets();
AnimatorSet animatorSet = new AnimatorSet();
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 8547eb4..983cf8d 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -1345,7 +1345,8 @@
if (requestArgs != null) {
setWaitingForResult(requestArgs);
}
- mPendingActivityRequestCode = savedState.getInt(RUNTIME_STATE_PENDING_REQUEST_CODE);
+ mPendingActivityRequestCode = savedState.getInt(
+ RUNTIME_STATE_PENDING_REQUEST_CODE, mPendingActivityRequestCode);
mPendingActivityResult = savedState.getParcelable(RUNTIME_STATE_PENDING_ACTIVITY_RESULT);
diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java
index 42a28d6..b6da164 100644
--- a/src/com/android/launcher3/LauncherAppState.java
+++ b/src/com/android/launcher3/LauncherAppState.java
@@ -163,8 +163,7 @@
LockedUserState.get(context).runOnUserUnlocked(() -> {
CustomWidgetManager cwm = CustomWidgetManager.INSTANCE.get(mContext);
- cwm.setWidgetRefreshCallback(mModel::refreshAndBindWidgetsAndShortcuts);
- mOnTerminateCallback.add(() -> cwm.setWidgetRefreshCallback(null));
+ mOnTerminateCallback.add(cwm.addWidgetRefreshCallback(mModel::rebindCallbacks)::close);
IconObserver observer = new IconObserver();
SafeCloseable iconChangeTracker = mIconProvider.registerIconChangeListener(
diff --git a/src/com/android/launcher3/LauncherApplication.java b/src/com/android/launcher3/LauncherApplication.java
index 4c82e56..678901b 100644
--- a/src/com/android/launcher3/LauncherApplication.java
+++ b/src/com/android/launcher3/LauncherApplication.java
@@ -26,15 +26,25 @@
*/
public class LauncherApplication extends Application {
- private LauncherBaseAppComponent mAppComponent;
+ private volatile LauncherBaseAppComponent mAppComponent;
@Override
public void onCreate() {
super.onCreate();
MainProcessInitializer.initialize(this);
- initDagger();
}
public LauncherAppComponent getAppComponent() {
+ if (mAppComponent == null) {
+ synchronized (this) {
+ // Check for null again, as it may have been assigned on a different thread. This
+ // avoids holding synchronization locks everytime.
+ if (mAppComponent == null) {
+ // Initialize the dagger component on demand as content providers can get
+ // accessed before the Launcher application (b/36917845#comment4)
+ initDaggerComponent(DaggerLauncherAppComponent.builder());
+ }
+ }
+ }
// Since supertype setters will return a supertype.builder and @Component.Builder types
// must not have any generic types.
// We need to cast mAppComponent to {@link LauncherAppComponent} since appContext()
@@ -42,7 +52,10 @@
return (LauncherAppComponent) mAppComponent;
}
- protected void initDagger() {
- mAppComponent = DaggerLauncherAppComponent.builder().appContext(this).build();
+ /**
+ * Init with the desired dagger component.
+ */
+ public void initDaggerComponent(LauncherAppComponent.Builder componentBuilder) {
+ mAppComponent = componentBuilder.appContext(this).build();
}
}
diff --git a/src/com/android/launcher3/LauncherModel.kt b/src/com/android/launcher3/LauncherModel.kt
index a013eaa..85ecd58 100644
--- a/src/com/android/launcher3/LauncherModel.kt
+++ b/src/com/android/launcher3/LauncherModel.kt
@@ -25,7 +25,6 @@
import android.util.Pair
import androidx.annotation.WorkerThread
import com.android.launcher3.celllayout.CellPosMapper
-import com.android.launcher3.config.FeatureFlags
import com.android.launcher3.icons.IconCache
import com.android.launcher3.model.AddWorkspaceItemsTask
import com.android.launcher3.model.AllAppsList
@@ -67,8 +66,8 @@
private val context: Context,
private val mApp: LauncherAppState,
private val iconCache: IconCache,
- private val appFilter: AppFilter,
- private val mPmHelper: PackageManagerHelper,
+ appFilter: AppFilter,
+ mPmHelper: PackageManagerHelper,
isPrimaryInstance: Boolean,
) {
@@ -304,9 +303,6 @@
launcherBinder.bindAllApps()
launcherBinder.bindDeepShortcuts()
launcherBinder.bindWidgets()
- if (FeatureFlags.CHANGE_MODEL_DELEGATE_LOADING_ORDER.get()) {
- this.modelDelegate.bindAllModelExtras(callbacksList)
- }
return true
} else {
mLoaderTask =
diff --git a/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java b/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
index 1094768..0dd2791 100644
--- a/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
@@ -296,6 +296,10 @@
// Add the search box above everything else in this container (if the flag is enabled,
// it's added to drag layer in onAttach instead).
addView(mSearchContainer);
+ // The search container is visually at the top of the all apps UI, and should thus be
+ // focused by default. It's added to end of the children list, so it needs to be
+ // explicitly marked as focused by default.
+ mSearchContainer.setFocusedByDefault(true);
}
mSearchUiManager = (SearchUiManager) mSearchContainer;
}
diff --git a/src/com/android/launcher3/allapps/WorkModeSwitch.java b/src/com/android/launcher3/allapps/WorkModeSwitch.java
index 6049574..f1f72b2 100644
--- a/src/com/android/launcher3/allapps/WorkModeSwitch.java
+++ b/src/com/android/launcher3/allapps/WorkModeSwitch.java
@@ -15,10 +15,18 @@
*/
package com.android.launcher3.allapps;
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Rect;
+import android.text.TextUtils;
import android.util.AttributeSet;
+import android.view.ViewGroup;
import android.view.WindowInsets;
+import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
@@ -26,10 +34,12 @@
import androidx.core.graphics.Insets;
import androidx.core.view.WindowInsetsCompat;
+import com.android.app.animation.Interpolators;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Insettable;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
+import com.android.launcher3.anim.AnimatedPropertySetter;
import com.android.launcher3.anim.KeyboardInsetAnimationCallback;
import com.android.launcher3.model.StringCache;
import com.android.launcher3.views.ActivityContext;
@@ -39,9 +49,14 @@
public class WorkModeSwitch extends LinearLayout implements Insettable,
KeyboardInsetAnimationCallback.KeyboardInsetListener {
+ private static final int TEXT_EXPAND_OPACITY_DURATION = 300;
+ private static final int TEXT_COLLAPSE_OPACITY_DURATION = 50;
+ private static final int EXPAND_COLLAPSE_DURATION = 300;
+ private static final int TEXT_ALPHA_EXPAND_DELAY = 80;
+ private static final int TEXT_ALPHA_COLLAPSE_DELAY = 0;
private static final int FLAG_FADE_ONGOING = 1 << 1;
private static final int FLAG_TRANSLATION_ONGOING = 1 << 2;
- private static final int FLAG_PROFILE_TOGGLE_ONGOING = 1 << 3;
+ private static final int FLAG_IS_EXPAND = 1 << 3;
private static final int SCROLL_THRESHOLD_DP = 10;
private final Rect mInsets = new Rect();
@@ -49,11 +64,15 @@
private int mFlags;
private final ActivityContext mActivityContext;
private final Context mContext;
+ private final int mTextMarginStart;
+ private final int mTextMarginEnd;
+ private final int mIconMarginStart;
// Threshold when user scrolls up/down to determine when should button extend/collapse
private final int mScrollThreshold;
private TextView mTextView;
-
+ private ImageView mIcon;
+ private ValueAnimator mPauseFABAnim;
public WorkModeSwitch(@NonNull Context context) {
this(context, null, 0);
@@ -68,6 +87,12 @@
mContext = context;
mScrollThreshold = Utilities.dpToPx(SCROLL_THRESHOLD_DP);
mActivityContext = ActivityContext.lookupContext(getContext());
+ mTextMarginStart = mContext.getResources().getDimensionPixelSize(
+ R.dimen.work_fab_text_start_margin);
+ mTextMarginEnd = mContext.getResources().getDimensionPixelSize(
+ R.dimen.work_fab_text_end_margin);
+ mIconMarginStart = mContext.getResources().getDimensionPixelSize(
+ R.dimen.work_fab_icon_start_margin_expanded);
}
@Override
@@ -75,11 +100,13 @@
super.onFinishInflate();
mTextView = findViewById(R.id.pause_text);
+ mIcon = findViewById(R.id.work_icon);
setSelected(true);
KeyboardInsetAnimationCallback keyboardInsetAnimationCallback =
new KeyboardInsetAnimationCallback(this);
setWindowInsetsAnimationCallback(keyboardInsetAnimationCallback);
-
+ // Expand is the default state upon initialization.
+ addFlag(FLAG_IS_EXPAND);
setInsets(mActivityContext.getDeviceProfile().getInsets());
updateStringFromCache();
}
@@ -114,18 +141,18 @@
@Override
public boolean isEnabled() {
- return super.isEnabled() && getVisibility() == VISIBLE && mFlags == 0;
+ return super.isEnabled() && getVisibility() == VISIBLE;
}
public void animateVisibility(boolean visible) {
clearAnimation();
if (visible) {
- setFlag(FLAG_FADE_ONGOING);
+ addFlag(FLAG_FADE_ONGOING);
setVisibility(VISIBLE);
extend();
animate().alpha(1).withEndAction(() -> removeFlag(FLAG_FADE_ONGOING)).start();
} else if (getVisibility() != GONE) {
- setFlag(FLAG_FADE_ONGOING);
+ addFlag(FLAG_FADE_ONGOING);
animate().alpha(0).withEndAction(() -> {
removeFlag(FLAG_FADE_ONGOING);
setVisibility(GONE);
@@ -156,6 +183,79 @@
super.setTranslationY(Math.min(translationY, -mInsets.bottom));
}
+
+ private void animatePillTransition(boolean isExpanding) {
+ if (!shouldAnimate(isExpanding)) {
+ return;
+ }
+ AnimatorSet animatorSet = new AnimatedPropertySetter().buildAnim();
+ mTextView.measure(0,0);
+ int currentWidth = mTextView.getWidth();
+ int fullWidth = mTextView.getMeasuredWidth();
+ float from = isExpanding ? 0 : currentWidth;
+ float to = isExpanding ? fullWidth : 0;
+ mPauseFABAnim = ObjectAnimator.ofFloat(from, to);
+ mPauseFABAnim.setDuration(EXPAND_COLLAPSE_DURATION);
+ mPauseFABAnim.setInterpolator(Interpolators.STANDARD);
+ mPauseFABAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator valueAnimator) {
+ float translation = (float) valueAnimator.getAnimatedValue();
+ float translationFraction = translation / fullWidth;
+ ViewGroup.MarginLayoutParams textViewLayoutParams =
+ (ViewGroup.MarginLayoutParams) mTextView.getLayoutParams();
+ textViewLayoutParams.width = (int) translation;
+ textViewLayoutParams.setMarginStart((int) (mTextMarginStart * translationFraction));
+ textViewLayoutParams.setMarginEnd((int) (mTextMarginEnd * translationFraction));
+ mTextView.setLayoutParams(textViewLayoutParams);
+ ViewGroup.MarginLayoutParams iconLayoutParams =
+ (ViewGroup.MarginLayoutParams) mIcon.getLayoutParams();
+ iconLayoutParams.setMarginStart((int) (mIconMarginStart * translationFraction));
+ mIcon.setLayoutParams(iconLayoutParams);
+ }
+ });
+ mPauseFABAnim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animator) {
+ if (isExpanding) {
+ addFlag(FLAG_IS_EXPAND);
+ } else {
+ mTextView.setVisibility(GONE);
+ removeFlag(FLAG_IS_EXPAND);
+ }
+ mTextView.setHorizontallyScrolling(false);
+ mTextView.setEllipsize(TextUtils.TruncateAt.END);
+ }
+
+ @Override
+ public void onAnimationStart(Animator animator) {
+ mTextView.setHorizontallyScrolling(true);
+ mTextView.setVisibility(VISIBLE);
+ mTextView.setEllipsize(null);
+ }
+ });
+ animatorSet.playTogether(mPauseFABAnim, updatePauseTextAlpha(isExpanding));
+ animatorSet.start();
+ }
+
+
+ private ValueAnimator updatePauseTextAlpha(boolean expand) {
+ float from = expand ? 0 : 1;
+ float to = expand ? 1 : 0;
+ ValueAnimator alphaAnim = ObjectAnimator.ofFloat(from, to);
+ alphaAnim.setDuration(expand ? TEXT_EXPAND_OPACITY_DURATION
+ : TEXT_COLLAPSE_OPACITY_DURATION);
+ alphaAnim.setStartDelay(expand ? TEXT_ALPHA_EXPAND_DELAY : TEXT_ALPHA_COLLAPSE_DELAY);
+ alphaAnim.setInterpolator(Interpolators.LINEAR);
+ alphaAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator valueAnimator) {
+ mTextView.setAlpha((float) valueAnimator.getAnimatedValue());
+ }
+ });
+ return alphaAnim;
+ }
+
private void setInsets(Rect rect, Insets insets) {
rect.set(insets.left, insets.top, insets.right, insets.bottom);
}
@@ -166,7 +266,7 @@
@Override
public void onTranslationStart() {
- setFlag(FLAG_TRANSLATION_ONGOING);
+ addFlag(FLAG_TRANSLATION_ONGOING);
}
@Override
@@ -174,7 +274,7 @@
removeFlag(FLAG_TRANSLATION_ONGOING);
}
- private void setFlag(int flag) {
+ private void addFlag(int flag) {
mFlags |= flag;
}
@@ -182,12 +282,25 @@
mFlags &= ~flag;
}
+ private boolean containsFlag(int flag) {
+ return (mFlags & flag) == flag;
+ }
+
public void extend() {
- mTextView.setVisibility(VISIBLE);
+ animatePillTransition(true);
}
public void shrink(){
- mTextView.setVisibility(GONE);
+ animatePillTransition(false);
+ }
+
+ /**
+ * Determines if the button should animate based on current state. It should animate the button
+ * only if it is not in the same state it is animating to.
+ */
+ private boolean shouldAnimate(boolean expanding) {
+ return expanding != containsFlag(FLAG_IS_EXPAND)
+ && (mPauseFABAnim == null || !mPauseFABAnim.isRunning());
}
public int getScrollThreshold() {
diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index 8fe1b34..9e38824 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -62,17 +62,6 @@
* and set a default value for the flag. This will be the default value on Debug builds.
* <p>
*/
- // TODO(Block 3): Clean up flags
- public static final BooleanFlag ENABLE_WORKSPACE_LOADING_OPTIMIZATION = getDebugFlag(251502424,
- "ENABLE_WORKSPACE_LOADING_OPTIMIZATION", DISABLED,
- "load the current workspace screen visible to the user before the rest rather than "
- + "loading all of them at once.");
-
- public static final BooleanFlag CHANGE_MODEL_DELEGATE_LOADING_ORDER = getDebugFlag(251502424,
- "CHANGE_MODEL_DELEGATE_LOADING_ORDER", DISABLED,
- "changes the timing of the loading and binding of delegate items during "
- + "data preparation for loading the home screen");
-
// TODO(Block 6): Clean up flags
public static final BooleanFlag SECONDARY_DRAG_N_DROP_TO_PIN = getDebugFlag(270395140,
"SECONDARY_DRAG_N_DROP_TO_PIN", DISABLED,
diff --git a/src/com/android/launcher3/dagger/LauncherBaseAppComponent.java b/src/com/android/launcher3/dagger/LauncherBaseAppComponent.java
index 4537785..ecc5bb2 100644
--- a/src/com/android/launcher3/dagger/LauncherBaseAppComponent.java
+++ b/src/com/android/launcher3/dagger/LauncherBaseAppComponent.java
@@ -19,6 +19,7 @@
import android.content.Context;
import com.android.launcher3.pm.InstallSessionHelper;
+import com.android.launcher3.util.ApiWrapper;
import com.android.launcher3.util.DaggerSingletonTracker;
import com.android.launcher3.util.PluginManagerWrapper;
import com.android.launcher3.util.ScreenOnTracker;
@@ -40,6 +41,7 @@
DaggerSingletonTracker getDaggerSingletonTracker();
RefreshRateTracker getRefreshRateTracker();
InstallSessionHelper getInstallSessionHelper();
+ ApiWrapper getApiWrapper();
ScreenOnTracker getScreenOnTracker();
SettingsCache getSettingsCache();
CustomWidgetManager getCustomWidgetManager();
diff --git a/src/com/android/launcher3/graphics/GridCustomizationsProvider.java b/src/com/android/launcher3/graphics/GridCustomizationsProvider.java
index 259e543..bc51a66 100644
--- a/src/com/android/launcher3/graphics/GridCustomizationsProvider.java
+++ b/src/com/android/launcher3/graphics/GridCustomizationsProvider.java
@@ -36,18 +36,24 @@
import android.text.TextUtils;
import android.util.Log;
+import androidx.annotation.NonNull;
+
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.InvariantDeviceProfile.GridOption;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherModel;
import com.android.launcher3.LauncherPrefs;
import com.android.launcher3.model.BgDataModel;
+import com.android.launcher3.shapes.AppShape;
+import com.android.launcher3.shapes.AppShapesProvider;
import com.android.launcher3.util.Executors;
import com.android.launcher3.util.Preconditions;
import com.android.launcher3.util.RunnableList;
import com.android.systemui.shared.Flags;
import java.util.Collections;
+import java.util.List;
+import java.util.Optional;
import java.util.Set;
import java.util.WeakHashMap;
import java.util.concurrent.ExecutionException;
@@ -55,31 +61,44 @@
/**
* Exposes various launcher grid options and allows the caller to change them.
* APIs:
- * /list_options: List the various available grip options, has following columns
- * name: name of the grid
+ * /shape_options: List of various available shape options, where each has following fields
+ * shape_key: key of the shape option
+ * title: translated title of the shape option
+ * path: path of the shape, assuming drawn on 100x100 view port
+ * is_default: true if this shape option is currently set to the system
+ *
+ * /grid_options: List the various available grid options, where each has following fields
+ * name: key of the grid option
* rows: number of rows in the grid
* cols: number of columns in the grid
* preview_count: number of previews available for this grid option. The preview uri
* looks like /preview/<grid-name>/<preview index starting with 0>
- * is_default: true if this grid is currently active
+ * is_default: true if this grid option is currently set to the system
*
- * /preview: Opens a file stream for the grid preview
+ * /get_preview: Open a file stream for the grid preview
*
- * /default_grid: Call update to set the current grid, with values
- * name: name of the grid to apply
+ * /default_grid: Call update to set the current shape and grid, with values
+ * shape_key: key of the shape to apply
+ * name: key of the grid to apply
*/
public class GridCustomizationsProvider extends ContentProvider {
private static final String TAG = "GridCustomizationsProvider";
- private static final String KEY_NAME = "name";
+ private static final String KEY_SHAPE_KEY = "shape_key";
+ private static final String KEY_TITLE = "title";
+ private static final String KEY_PATH = "path";
+ // is_default means if a certain option is currently set to the system
+ private static final String KEY_IS_DEFAULT = "is_default";
+ // Key of grid option. We do not change the name to grid_key for backward compatibility
+ private static final String KEY_GRID_KEY = "name";
private static final String KEY_ROWS = "rows";
private static final String KEY_COLS = "cols";
private static final String KEY_PREVIEW_COUNT = "preview_count";
- private static final String KEY_IS_DEFAULT = "is_default";
- private static final String KEY_LIST_OPTIONS = "/list_options";
- private static final String KEY_DEFAULT_GRID = "/default_grid";
+ private static final String KEY_SHAPE_OPTIONS = "/shape_options";
+ private static final String KEY_GRID_OPTIONS = "/grid_options";
+ private static final String KEY_SHAPE_GRID = "/default_grid";
private static final String METHOD_GET_PREVIEW = "get_preview";
@@ -91,9 +110,9 @@
private static final String KEY_SURFACE_PACKAGE = "surface_package";
private static final String KEY_CALLBACK = "callback";
public static final String KEY_HIDE_BOTTOM_ROW = "hide_bottom_row";
- public static final String KEY_GRID_NAME = "grid_name";
private static final int MESSAGE_ID_UPDATE_PREVIEW = 1337;
+ private static final int MESSAGE_ID_UPDATE_SHAPE = 2586;
private static final int MESSAGE_ID_UPDATE_GRID = 7414;
private static final int MESSAGE_ID_UPDATE_COLOR = 856;
@@ -109,14 +128,39 @@
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
- switch (uri.getPath()) {
- case KEY_LIST_OPTIONS: {
+ Context context = getContext();
+ String path = uri.getPath();
+ if (context == null || path == null) {
+ return null;
+ }
+ switch (path) {
+ case KEY_SHAPE_OPTIONS: {
+ if (Flags.newCustomizationPickerUi()) {
+ MatrixCursor cursor = new MatrixCursor(new String[]{
+ KEY_SHAPE_KEY, KEY_TITLE, KEY_PATH, KEY_IS_DEFAULT});
+ List<AppShape> shapes = AppShapesProvider.INSTANCE.getShapes();
+ for (int i = 0; i < shapes.size(); i++) {
+ AppShape shape = shapes.get(i);
+ cursor.newRow()
+ .add(KEY_SHAPE_KEY, shape.getKey())
+ .add(KEY_TITLE, shape.getTitle())
+ .add(KEY_PATH, shape.getPath())
+ // TODO (b/348664593): We should fetch the currently-set shape
+ // option from the preferences.
+ .add(KEY_IS_DEFAULT, i == 0);
+ }
+ return cursor;
+ } else {
+ return null;
+ }
+ }
+ case KEY_GRID_OPTIONS: {
MatrixCursor cursor = new MatrixCursor(new String[]{
- KEY_NAME, KEY_ROWS, KEY_COLS, KEY_PREVIEW_COUNT, KEY_IS_DEFAULT});
- InvariantDeviceProfile idp = InvariantDeviceProfile.INSTANCE.get(getContext());
- for (GridOption gridOption : idp.parseAllGridOptions(getContext())) {
+ KEY_GRID_KEY, KEY_ROWS, KEY_COLS, KEY_PREVIEW_COUNT, KEY_IS_DEFAULT});
+ InvariantDeviceProfile idp = InvariantDeviceProfile.INSTANCE.get(context);
+ for (GridOption gridOption : idp.parseAllGridOptions(context)) {
cursor.newRow()
- .add(KEY_NAME, gridOption.name)
+ .add(KEY_GRID_KEY, gridOption.name)
.add(KEY_ROWS, gridOption.numRows)
.add(KEY_COLS, gridOption.numColumns)
.add(KEY_PREVIEW_COUNT, 1)
@@ -159,14 +203,22 @@
return 0;
}
switch (path) {
- case KEY_DEFAULT_GRID: {
- String gridName = values.getAsString(KEY_NAME);
+ case KEY_SHAPE_GRID: {
+ if (Flags.newCustomizationPickerUi()) {
+ String shapeKey = values.getAsString(KEY_SHAPE_KEY);
+ Optional<AppShape> optionalShape = AppShapesProvider.INSTANCE.getShapes()
+ .stream().filter(shape -> shape.getKey().equals(shapeKey)).findFirst();
+ String pathToSet = optionalShape.map(AppShape::getPath).orElse(null);
+ // TODO (b/348664593): Apply shapeName to the system. This needs to be a
+ // synchronous call.
+ }
+ String gridKey = values.getAsString(KEY_GRID_KEY);
InvariantDeviceProfile idp = InvariantDeviceProfile.INSTANCE.get(context);
// Verify that this is a valid grid option
GridOption match = null;
for (GridOption option : idp.parseAllGridOptions(context)) {
String name = option.name;
- if (name != null && name.equals(gridName)) {
+ if (name != null && name.equals(gridKey)) {
match = option;
break;
}
@@ -175,7 +227,7 @@
return 0;
}
- idp.setCurrentGrid(context, gridName);
+ idp.setCurrentGrid(context, gridKey);
if (Flags.newCustomizationPickerUi()) {
try {
// Wait for device profile to be fully reloaded and applied to the launcher
@@ -217,20 +269,30 @@
}
@Override
- public Bundle call(String method, String arg, Bundle extras) {
- if (getContext().checkPermission("android.permission.BIND_WALLPAPER",
+ public Bundle call(@NonNull String method, String arg, Bundle extras) {
+ Context context = getContext();
+ if (context == null) {
+ return null;
+ }
+
+ if (context.checkPermission("android.permission.BIND_WALLPAPER",
Binder.getCallingPid(), Binder.getCallingUid())
!= PackageManager.PERMISSION_GRANTED) {
return null;
}
- if (!METHOD_GET_PREVIEW.equals(method)) {
+ if (METHOD_GET_PREVIEW.equals(method)) {
+ return getPreview(extras);
+ } else {
return null;
}
- return getPreview(extras);
}
private synchronized Bundle getPreview(Bundle request) {
+ Context context = getContext();
+ if (context == null) {
+ return null;
+ }
RunnableList lifeCycleTracker = new RunnableList();
try {
PreviewSurfaceRenderer renderer = new PreviewSurfaceRenderer(
@@ -268,7 +330,9 @@
public final PreviewSurfaceRenderer renderer;
public boolean destroyed = false;
- PreviewLifecycleObserver(RunnableList lifeCycleTracker, PreviewSurfaceRenderer renderer) {
+ PreviewLifecycleObserver(
+ RunnableList lifeCycleTracker,
+ PreviewSurfaceRenderer renderer) {
this.lifeCycleTracker = lifeCycleTracker;
this.renderer = renderer;
lifeCycleTracker.add(() -> destroyed = true);
@@ -284,10 +348,21 @@
case MESSAGE_ID_UPDATE_PREVIEW:
renderer.hideBottomRow(message.getData().getBoolean(KEY_HIDE_BOTTOM_ROW));
break;
+ case MESSAGE_ID_UPDATE_SHAPE:
+ if (Flags.newCustomizationPickerUi()) {
+ String shapeKey = message.getData().getString(KEY_SHAPE_KEY);
+ Optional<AppShape> optionalShape = AppShapesProvider.INSTANCE.getShapes()
+ .stream()
+ .filter(shape -> shape.getKey().equals(shapeKey))
+ .findFirst();
+ String pathToSet = optionalShape.map(AppShape::getPath).orElse(null);
+ // TODO (b/348664593): Update launcher preview with the given shape
+ }
+ break;
case MESSAGE_ID_UPDATE_GRID:
- String gridName = message.getData().getString(KEY_GRID_NAME);
- if (!TextUtils.isEmpty(gridName)) {
- renderer.updateGrid(gridName);
+ String gridKey = message.getData().getString(KEY_GRID_KEY);
+ if (!TextUtils.isEmpty(gridKey)) {
+ renderer.updateGrid(gridKey);
}
break;
case MESSAGE_ID_UPDATE_COLOR:
diff --git a/src/com/android/launcher3/model/BaseLauncherBinder.java b/src/com/android/launcher3/model/BaseLauncherBinder.java
index 5faa2b8..b51f855 100644
--- a/src/com/android/launcher3/model/BaseLauncherBinder.java
+++ b/src/com/android/launcher3/model/BaseLauncherBinder.java
@@ -36,7 +36,6 @@
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherModel.CallbackTask;
import com.android.launcher3.LauncherSettings;
-import com.android.launcher3.Workspace;
import com.android.launcher3.celllayout.CellPosMapper;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.model.BgDataModel.Callbacks;
@@ -59,11 +58,9 @@
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
-import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
-import java.util.Set;
import java.util.concurrent.Executor;
import java.util.stream.Collectors;
@@ -100,13 +97,29 @@
public void bindWorkspace(boolean incrementBindId, boolean isBindSync) {
Trace.beginSection("BaseLauncherBinder#bindWorkspace");
try {
- if (FeatureFlags.ENABLE_WORKSPACE_LOADING_OPTIMIZATION.get()) {
- DisjointWorkspaceBinder workspaceBinder =
- initWorkspaceBinder(incrementBindId, mBgDataModel.collectWorkspaceScreens());
- workspaceBinder.bindCurrentWorkspacePages(isBindSync);
- workspaceBinder.bindOtherWorkspacePages();
- } else {
- bindWorkspaceAllAtOnce(incrementBindId, isBindSync);
+ // Save a copy of all the bg-thread collections
+ ArrayList<ItemInfo> workspaceItems = new ArrayList<>();
+ ArrayList<LauncherAppWidgetInfo> appWidgets = new ArrayList<>();
+ final IntArray orderedScreenIds = new IntArray();
+ ArrayList<FixedContainerItems> extraItems = new ArrayList<>();
+ final int workspaceItemCount;
+ synchronized (mBgDataModel) {
+ workspaceItems.addAll(mBgDataModel.workspaceItems);
+ appWidgets.addAll(mBgDataModel.appWidgets);
+ orderedScreenIds.addAll(mBgDataModel.collectWorkspaceScreens());
+ mBgDataModel.extraItems.forEach(extraItems::add);
+ if (incrementBindId) {
+ mBgDataModel.lastBindId++;
+ mBgDataModel.lastLoadId = mApp.getModel().getLastLoadId();
+ }
+ mMyBindingId = mBgDataModel.lastBindId;
+ workspaceItemCount = mBgDataModel.itemsIdMap.size();
+ }
+
+ for (Callbacks cb : mCallbacksList) {
+ new UnifiedWorkspaceBinder(cb, mUiExecutor, mApp, mBgDataModel, mMyBindingId,
+ workspaceItems, appWidgets, extraItems, orderedScreenIds)
+ .bind(isBindSync, workspaceItemCount);
}
} finally {
Trace.endSection();
@@ -114,53 +127,6 @@
}
/**
- * Initializes the WorkspaceBinder for binding.
- *
- * @param incrementBindId this is used to stop previously started binding tasks that are
- * obsolete but still queued.
- * @param workspacePages this allows the Launcher to add the correct workspace screens.
- */
- public DisjointWorkspaceBinder initWorkspaceBinder(boolean incrementBindId,
- IntArray workspacePages) {
-
- synchronized (mBgDataModel) {
- if (incrementBindId) {
- mBgDataModel.lastBindId++;
- mBgDataModel.lastLoadId = mApp.getModel().getLastLoadId();
- }
- mMyBindingId = mBgDataModel.lastBindId;
- return new DisjointWorkspaceBinder(workspacePages);
- }
- }
-
- private void bindWorkspaceAllAtOnce(boolean incrementBindId, boolean isBindSync) {
- // Save a copy of all the bg-thread collections
- ArrayList<ItemInfo> workspaceItems = new ArrayList<>();
- ArrayList<LauncherAppWidgetInfo> appWidgets = new ArrayList<>();
- final IntArray orderedScreenIds = new IntArray();
- ArrayList<FixedContainerItems> extraItems = new ArrayList<>();
- final int workspaceItemCount;
- synchronized (mBgDataModel) {
- workspaceItems.addAll(mBgDataModel.workspaceItems);
- appWidgets.addAll(mBgDataModel.appWidgets);
- orderedScreenIds.addAll(mBgDataModel.collectWorkspaceScreens());
- mBgDataModel.extraItems.forEach(extraItems::add);
- if (incrementBindId) {
- mBgDataModel.lastBindId++;
- mBgDataModel.lastLoadId = mApp.getModel().getLastLoadId();
- }
- mMyBindingId = mBgDataModel.lastBindId;
- workspaceItemCount = mBgDataModel.itemsIdMap.size();
- }
-
- for (Callbacks cb : mCallbacksList) {
- new UnifiedWorkspaceBinder(cb, mUiExecutor, mApp, mBgDataModel, mMyBindingId,
- workspaceItems, appWidgets, extraItems, orderedScreenIds)
- .bind(isBindSync, workspaceItemCount);
- }
- }
-
- /**
* BindDeepShortcuts is abstract because it is a no-op for the go launcher.
*/
public void bindDeepShortcuts() {
@@ -347,10 +313,8 @@
bindItemsInChunks(currentWorkspaceItems, ITEMS_CHUNK, mUiExecutor);
bindItemsInChunks(currentAppWidgets, 1, mUiExecutor);
}
- if (!FeatureFlags.CHANGE_MODEL_DELEGATE_LOADING_ORDER.get()) {
- mExtraItems.forEach(item ->
- executeCallbacksTask(c -> c.bindExtraContainerItems(item), mUiExecutor));
- }
+ mExtraItems.forEach(item ->
+ executeCallbacksTask(c -> c.bindExtraContainerItems(item), mUiExecutor));
RunnableList pendingTasks = new RunnableList();
Executor pendingExecutor = pendingTasks::add;
@@ -440,126 +404,4 @@
});
}
}
-
- private class DisjointWorkspaceBinder {
- private final IntArray mOrderedScreenIds;
- private final IntSet mCurrentScreenIds = new IntSet();
- private final Set<Integer> mBoundItemIds = new HashSet<>();
-
- protected DisjointWorkspaceBinder(IntArray orderedScreenIds) {
- mOrderedScreenIds = orderedScreenIds;
-
- for (Callbacks cb : mCallbacksList) {
- mCurrentScreenIds.addAll(cb.getPagesToBindSynchronously(orderedScreenIds));
- }
- if (mCurrentScreenIds.size() == 0) {
- mCurrentScreenIds.add(Workspace.FIRST_SCREEN_ID);
- }
- }
-
- /**
- * Binds the currently loaded items in the Data Model. Also signals to the Callbacks[]
- * that these items have been bound and their respective screens are ready to be shown.
- *
- * If this method is called after all the items on the workspace screen have already been
- * loaded, it will bind all workspace items immediately, and bindOtherWorkspacePages() will
- * not bind any items.
- */
- protected void bindCurrentWorkspacePages(boolean isBindSync) {
- // Save a copy of all the bg-thread collections
- ArrayList<ItemInfo> workspaceItems;
- ArrayList<LauncherAppWidgetInfo> appWidgets;
- ArrayList<FixedContainerItems> fciList = new ArrayList<>();
- final int workspaceItemCount;
- synchronized (mBgDataModel) {
- workspaceItems = new ArrayList<>(mBgDataModel.workspaceItems);
- appWidgets = new ArrayList<>(mBgDataModel.appWidgets);
- if (!FeatureFlags.CHANGE_MODEL_DELEGATE_LOADING_ORDER.get()) {
- mBgDataModel.extraItems.forEach(fciList::add);
- }
- workspaceItemCount = mBgDataModel.itemsIdMap.size();
- }
-
- workspaceItems.forEach(it -> mBoundItemIds.add(it.id));
- appWidgets.forEach(it -> mBoundItemIds.add(it.id));
- if (!FeatureFlags.CHANGE_MODEL_DELEGATE_LOADING_ORDER.get()) {
- fciList.forEach(item ->
- executeCallbacksTask(c -> c.bindExtraContainerItems(item), mUiExecutor));
- }
-
- sortWorkspaceItemsSpatially(mApp.getInvariantDeviceProfile(), workspaceItems);
-
- // Tell the workspace that we're about to start binding items
- executeCallbacksTask(c -> {
- c.clearPendingBinds();
- c.startBinding();
- }, mUiExecutor);
-
- // Bind workspace screens
- executeCallbacksTask(c -> c.bindScreens(mOrderedScreenIds), mUiExecutor);
-
- bindWorkspaceItems(workspaceItems);
- bindAppWidgets(appWidgets);
- executeCallbacksTask(c -> {
- MODEL_EXECUTOR.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
-
- RunnableList onCompleteSignal = new RunnableList();
- onCompleteSignal.executeAllAndDestroy();
- c.onInitialBindComplete(mCurrentScreenIds, new RunnableList(), onCompleteSignal,
- workspaceItemCount, isBindSync);
- }, mUiExecutor);
- }
-
- protected void bindOtherWorkspacePages() {
- // Save a copy of all the bg-thread collections
- ArrayList<ItemInfo> workspaceItems;
- ArrayList<LauncherAppWidgetInfo> appWidgets;
-
- synchronized (mBgDataModel) {
- workspaceItems = new ArrayList<>(mBgDataModel.workspaceItems);
- appWidgets = new ArrayList<>(mBgDataModel.appWidgets);
- }
-
- workspaceItems.removeIf(it -> mBoundItemIds.contains(it.id));
- appWidgets.removeIf(it -> mBoundItemIds.contains(it.id));
-
- sortWorkspaceItemsSpatially(mApp.getInvariantDeviceProfile(), workspaceItems);
-
- bindWorkspaceItems(workspaceItems);
- bindAppWidgets(appWidgets);
-
- executeCallbacksTask(c -> c.finishBindingItems(mCurrentScreenIds), mUiExecutor);
- mUiExecutor.execute(() -> {
- MODEL_EXECUTOR.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
- ItemInstallQueue.INSTANCE.get(mApp.getContext())
- .resumeModelPush(FLAG_LOADER_RUNNING);
- });
-
- StringCache cacheClone = mBgDataModel.stringCache.clone();
- executeCallbacksTask(c -> c.bindStringCache(cacheClone), mUiExecutor);
- }
-
- private void bindWorkspaceItems(final ArrayList<ItemInfo> workspaceItems) {
- // Bind the workspace items
- int count = workspaceItems.size();
- for (int i = 0; i < count; i += ITEMS_CHUNK) {
- final int start = i;
- final int chunkSize = (i + ITEMS_CHUNK <= count) ? ITEMS_CHUNK : (count - i);
- executeCallbacksTask(
- c -> c.bindItems(workspaceItems.subList(start, start + chunkSize), false),
- mUiExecutor);
- }
- }
-
- private void bindAppWidgets(List<LauncherAppWidgetInfo> appWidgets) {
- // Bind the widgets, one at a time
- int count = appWidgets.size();
- for (int i = 0; i < count; i++) {
- final ItemInfo widget = appWidgets.get(i);
- executeCallbacksTask(
- c -> c.bindItems(Collections.singletonList(widget), false),
- mUiExecutor);
- }
- }
- }
}
diff --git a/src/com/android/launcher3/model/GridSizeMigrationDBController.java b/src/com/android/launcher3/model/GridSizeMigrationDBController.java
index 9531d5b..d8ca095 100644
--- a/src/com/android/launcher3/model/GridSizeMigrationDBController.java
+++ b/src/com/android/launcher3/model/GridSizeMigrationDBController.java
@@ -328,10 +328,9 @@
} else {
values.put(LauncherSettings.Favorites.CONTAINER, folderId);
}
- newId = helper.generateNewItemId();
- while (idsInUse.contains(newId)) {
+ do {
newId = helper.generateNewItemId();
- }
+ } while (idsInUse.contains(newId));
values.put(LauncherSettings.Favorites._ID, newId);
helper.getWritableDatabase().insert(destTableName, null, values);
}
diff --git a/src/com/android/launcher3/model/GridSizeMigrationLogic.java b/src/com/android/launcher3/model/GridSizeMigrationLogic.java
index 12a14b2..f8c8f77 100644
--- a/src/com/android/launcher3/model/GridSizeMigrationLogic.java
+++ b/src/com/android/launcher3/model/GridSizeMigrationLogic.java
@@ -35,6 +35,7 @@
import android.util.Log;
import androidx.annotation.NonNull;
+import androidx.annotation.VisibleForTesting;
import com.android.launcher3.Flags;
import com.android.launcher3.LauncherPrefs;
@@ -95,10 +96,17 @@
t.getDb(), TABLE_NAME, context);
Point targetSize = new Point(destDeviceState.getColumns(), destDeviceState.getRows());
+
+ // Here we keep all the DB ids we have in the destination DB such that we don't assign
+ // an item that we want to add to the destination DB the same id as an already existing
+ // item.
+ List<Integer> idsInUse = new ArrayList<>();
+
// Migrate hotseat.
- migrateHotseat(destDeviceState.getNumHotseat(), srcReader, destReader, target);
+ migrateHotseat(destDeviceState.getNumHotseat(), srcReader, destReader, target,
+ idsInUse);
// Migrate workspace.
- migrateWorkspace(srcReader, destReader, target, targetSize);
+ migrateWorkspace(srcReader, destReader, target, targetSize, idsInUse);
dropTable(t.getDb(), TMP_TABLE);
t.commit();
@@ -113,17 +121,22 @@
}
}
- private void migrateHotseat(int destHotseatSize,
+ /**
+ * Handles hotseat migration.
+ */
+ @VisibleForTesting
+ public void migrateHotseat(int destHotseatSize,
GridSizeMigrationDBController.DbReader srcReader,
- GridSizeMigrationDBController.DbReader destReader, DatabaseHelper helper) {
+ GridSizeMigrationDBController.DbReader destReader,
+ DatabaseHelper helper, List<Integer> idsInUse) {
final List<DbEntry> srcHotseatItems =
srcReader.loadHotseatEntries();
final List<DbEntry> dstHotseatItems =
destReader.loadHotseatEntries();
-
final List<DbEntry> hotseatToBeAdded =
getItemsToBeAdded(srcHotseatItems, dstHotseatItems);
+
final IntArray toBeRemoved = new IntArray();
toBeRemoved.addAll(getItemsToBeRemoved(srcHotseatItems, dstHotseatItems));
@@ -144,19 +157,19 @@
removeEntryFromDb(destReader.mDb, destReader.mTableName, toBeRemoved);
}
- placeHotseatItems(
- hotseatToBeAdded, dstHotseatItems, destHotseatSize, helper, srcReader, destReader);
+ placeHotseatItems(hotseatToBeAdded, dstHotseatItems, destHotseatSize, helper, srcReader,
+ destReader, idsInUse);
}
private void placeHotseatItems(List<DbEntry> hotseatToBeAdded,
List<DbEntry> dstHotseatItems, int destHotseatSize,
DatabaseHelper helper, GridSizeMigrationDBController.DbReader srcReader,
- GridSizeMigrationDBController.DbReader destReader) {
+ GridSizeMigrationDBController.DbReader destReader, List<Integer> idsInUse) {
if (hotseatToBeAdded.isEmpty()) {
return;
}
- List<Integer> idsInUse = dstHotseatItems.stream().map(entry -> entry.id).toList();
+ idsInUse.addAll(dstHotseatItems.stream().map(entry -> entry.id).toList());
Collections.sort(hotseatToBeAdded);
@@ -168,11 +181,14 @@
}
}
- private void migrateWorkspace(GridSizeMigrationDBController.DbReader srcReader,
+
+ /**
+ * Handles workspace migration.
+ */
+ @VisibleForTesting
+ public void migrateWorkspace(GridSizeMigrationDBController.DbReader srcReader,
GridSizeMigrationDBController.DbReader destReader, DatabaseHelper helper,
- Point targetSize) {
-
-
+ Point targetSize, List<Integer> idsInUse) {
final List<DbEntry> srcWorkspaceItems =
srcReader.loadAllWorkspaceEntries();
@@ -213,7 +229,7 @@
}
placeWorkspaceItems(workspaceToBeAdded, dstWorkspaceItems, targetSize.x, targetSize.y,
- helper, srcReader, destReader);
+ helper, srcReader, destReader, idsInUse);
}
private void placeWorkspaceItems(
@@ -221,13 +237,12 @@
List<DbEntry> dstWorkspaceItems,
int trgX, int trgY, DatabaseHelper helper,
GridSizeMigrationDBController.DbReader srcReader,
- GridSizeMigrationDBController.DbReader destReader) {
+ GridSizeMigrationDBController.DbReader destReader, List<Integer> idsInUse) {
if (workspaceToBeAdded.isEmpty()) {
return;
}
- List<Integer> idsInUse = dstWorkspaceItems.stream().map(entry -> entry.id).collect(
- Collectors.toList());
+ idsInUse.addAll(dstWorkspaceItems.stream().map(entry -> entry.id).toList());
Collections.sort(workspaceToBeAdded);
diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java
index b0108c2..06d8b59 100644
--- a/src/com/android/launcher3/model/LoaderTask.java
+++ b/src/com/android/launcher3/model/LoaderTask.java
@@ -287,11 +287,6 @@
}
logASplit("loadAllApps");
- if (FeatureFlags.CHANGE_MODEL_DELEGATE_LOADING_ORDER.get()) {
- mModelDelegate.loadAndBindAllAppsItems(mUserManagerState,
- mLauncherBinder.mCallbacksList, mShortcutKeyToPinnedShortcuts);
- logASplit("allAppsDelegateItems");
- }
verifyNotStopped();
mLauncherBinder.bindAllApps();
logASplit("bindAllApps");
@@ -356,12 +351,6 @@
prefs.putSync(SHOULD_SHOW_SMARTSPACE.to(true));
}
- if (FeatureFlags.CHANGE_MODEL_DELEGATE_LOADING_ORDER.get()) {
- mModelDelegate.loadAndBindOtherItems(mLauncherBinder.mCallbacksList);
- logASplit("otherDelegateItems");
- verifyNotStopped();
- }
-
updateHandler.updateIcons(allWidgetsList,
CachedObjectCachingLogic.INSTANCE,
mApp.getModel()::onWidgetLabelsUpdated);
@@ -413,13 +402,6 @@
}
logASplit("loadWorkspace");
- if (FeatureFlags.CHANGE_MODEL_DELEGATE_LOADING_ORDER.get()) {
- verifyNotStopped();
- mModelDelegate.loadAndBindWorkspaceItems(mUserManagerState,
- mLauncherBinder.mCallbacksList, mShortcutKeyToPinnedShortcuts);
- mModelDelegate.markActive();
- logASplit("workspaceDelegateItems");
- }
mBgDataModel.isFirstPagePinnedItemEnabled = FeatureFlags.QSB_ON_FIRST_SCREEN
&& (!enableSmartspaceRemovalToggle() || LauncherPrefs.getPrefs(
mApp.getContext()).getBoolean(SMARTSPACE_ON_HOME_SCREEN, true));
@@ -490,14 +472,12 @@
IOUtils.closeSilently(c);
}
- if (!FeatureFlags.CHANGE_MODEL_DELEGATE_LOADING_ORDER.get()) {
- mModelDelegate.loadAndBindWorkspaceItems(mUserManagerState,
- mLauncherBinder.mCallbacksList, mShortcutKeyToPinnedShortcuts);
- mModelDelegate.loadAndBindAllAppsItems(mUserManagerState,
- mLauncherBinder.mCallbacksList, mShortcutKeyToPinnedShortcuts);
- mModelDelegate.loadAndBindOtherItems(mLauncherBinder.mCallbacksList);
- mModelDelegate.markActive();
- }
+ mModelDelegate.loadAndBindWorkspaceItems(mUserManagerState,
+ mLauncherBinder.mCallbacksList, mShortcutKeyToPinnedShortcuts);
+ mModelDelegate.loadAndBindAllAppsItems(mUserManagerState,
+ mLauncherBinder.mCallbacksList, mShortcutKeyToPinnedShortcuts);
+ mModelDelegate.loadAndBindOtherItems(mLauncherBinder.mCallbacksList);
+ mModelDelegate.markActive();
// Break early if we've stopped loading
if (mStopped) {
diff --git a/src/com/android/launcher3/model/ModelDbController.java b/src/com/android/launcher3/model/ModelDbController.java
index 4f0f162..8c3e860 100644
--- a/src/com/android/launcher3/model/ModelDbController.java
+++ b/src/com/android/launcher3/model/ModelDbController.java
@@ -325,7 +325,7 @@
private boolean isThereExistingDb() {
if (LauncherPrefs.get(mContext).get(getEmptyDbCreatedKey())) {
// If we already have a new DB, ignore migration
- Log.d(TAG, "migrateGridIfNeeded: new DB already created, skipping migration");
+ FileLog.d(TAG, "migrateGridIfNeeded: new DB already created, skipping migration");
return true;
}
return false;
@@ -336,7 +336,7 @@
if (GridSizeMigrationDBController.needsToMigrate(mContext, idp)) {
return true;
}
- Log.d(TAG, "migrateGridIfNeeded: no grid migration needed");
+ FileLog.d(TAG, "migrateGridIfNeeded: no grid migration needed");
return false;
}
@@ -344,7 +344,7 @@
InvariantDeviceProfile idp = LauncherAppState.getIDP(mContext);
String targetDbName = new DeviceGridState(idp).getDbFile();
if (TextUtils.equals(targetDbName, mOpenHelper.getDatabaseName())) {
- Log.e(TAG, "migrateGridIfNeeded: target db is same as current: " + targetDbName);
+ FileLog.e(TAG, "migrateGridIfNeeded: target db is same as current: " + targetDbName);
return true;
}
return false;
@@ -428,7 +428,7 @@
}
InvariantDeviceProfile idp = LauncherAppState.getIDP(mContext);
if (!GridSizeMigrationDBController.needsToMigrate(mContext, idp)) {
- Log.d(TAG, "migrateGridIfNeeded: no grid migration needed");
+ FileLog.d(TAG, "migrateGridIfNeeded: no grid migration needed");
return true;
}
String targetDbName = new DeviceGridState(idp).getDbFile();
diff --git a/src/com/android/launcher3/provider/LauncherDbUtils.java b/src/com/android/launcher3/provider/LauncherDbUtils.java
deleted file mode 100644
index 3ae643e..0000000
--- a/src/com/android/launcher3/provider/LauncherDbUtils.java
+++ /dev/null
@@ -1,210 +0,0 @@
-/*
- * Copyright (C) 2016 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.provider;
-
-import static com.android.launcher3.LauncherSettings.Favorites.getColumns;
-import static com.android.launcher3.icons.IconCache.EXTRA_SHORTCUT_BADGE_OVERRIDE_PACKAGE;
-
-import android.content.ContentValues;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.ResolveInfo;
-import android.content.pm.ShortcutInfo;
-import android.database.Cursor;
-import android.database.sqlite.SQLiteDatabase;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.graphics.drawable.Icon;
-import android.os.PersistableBundle;
-import android.os.Process;
-import android.os.UserManager;
-import android.text.TextUtils;
-
-import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.LauncherSettings.Favorites;
-import com.android.launcher3.Utilities;
-import com.android.launcher3.model.LoaderCursor;
-import com.android.launcher3.model.UserManagerState;
-import com.android.launcher3.pm.PinRequestHelper;
-import com.android.launcher3.pm.UserCache;
-import com.android.launcher3.shortcuts.ShortcutKey;
-import com.android.launcher3.util.IntArray;
-import com.android.launcher3.util.IntSet;
-import com.android.launcher3.util.PackageManagerHelper;
-
-/**
- * A set of utility methods for Launcher DB used for DB updates and migration.
- */
-public class LauncherDbUtils {
- /**
- * Returns a string which can be used as a where clause for DB query to match the given itemId
- */
- public static String itemIdMatch(int itemId) {
- return "_id=" + itemId;
- }
-
- public static IntArray queryIntArray(boolean distinct, SQLiteDatabase db, String tableName,
- String columnName, String selection, String groupBy, String orderBy) {
- IntArray out = new IntArray();
- try (Cursor c = db.query(distinct, tableName, new String[] { columnName }, selection, null,
- groupBy, null, orderBy, null)) {
- while (c.moveToNext()) {
- out.add(c.getInt(0));
- }
- }
- return out;
- }
-
- public static boolean tableExists(SQLiteDatabase db, String tableName) {
- try (Cursor c = db.query(true, "sqlite_master", new String[] {"tbl_name"},
- "tbl_name = ?", new String[] {tableName},
- null, null, null, null, null)) {
- return c.getCount() > 0;
- }
- }
-
- public static void dropTable(SQLiteDatabase db, String tableName) {
- db.execSQL("DROP TABLE IF EXISTS " + tableName);
- }
-
- /** Copy fromTable in fromDb to toTable in toDb. */
- public static void copyTable(SQLiteDatabase fromDb, String fromTable, SQLiteDatabase toDb,
- String toTable, Context context) {
- long userSerial = UserCache.INSTANCE.get(context).getSerialNumberForUser(
- Process.myUserHandle());
- dropTable(toDb, toTable);
- Favorites.addTableToDb(toDb, userSerial, false, toTable);
- if (fromDb != toDb) {
- toDb.execSQL("ATTACH DATABASE '" + fromDb.getPath() + "' AS from_db");
- toDb.execSQL(
- "INSERT INTO " + toTable + " SELECT " + getColumns(userSerial)
- + " FROM from_db." + fromTable);
- toDb.execSQL("DETACH DATABASE 'from_db'");
- } else {
- toDb.execSQL("INSERT INTO " + toTable + " SELECT " + getColumns(userSerial) + " FROM "
- + fromTable);
- }
- }
-
- /**
- * Migrates the legacy shortcuts to deep shortcuts pinned under Launcher.
- * Removes any invalid shortcut or any shortcut which requires some permission to launch
- */
- public static void migrateLegacyShortcuts(Context context, SQLiteDatabase db) {
- Cursor c = db.query(
- Favorites.TABLE_NAME, null, "itemType = 1", null, null, null, null);
- UserManagerState ums = new UserManagerState();
- PackageManagerHelper pmHelper = PackageManagerHelper.INSTANCE.get(context);
- ums.init(UserCache.INSTANCE.get(context),
- context.getSystemService(UserManager.class));
- LoaderCursor lc = new LoaderCursor(c, LauncherAppState.getInstance(context), ums, pmHelper,
- null);
- IntSet deletedShortcuts = new IntSet();
-
- while (lc.moveToNext()) {
- if (lc.user != Process.myUserHandle()) {
- deletedShortcuts.add(lc.id);
- continue;
- }
- Intent intent = lc.parseIntent();
- if (intent == null) {
- deletedShortcuts.add(lc.id);
- continue;
- }
- if (TextUtils.isEmpty(lc.getTitle())) {
- deletedShortcuts.add(lc.id);
- continue;
- }
-
- // Make sure the target intent can be launched without any permissions. Otherwise remove
- // the shortcut
- ResolveInfo ri = context.getPackageManager().resolveActivity(intent, 0);
- if (ri == null || !TextUtils.isEmpty(ri.activityInfo.permission)) {
- deletedShortcuts.add(lc.id);
- continue;
- }
- PersistableBundle extras = new PersistableBundle();
- extras.putString(EXTRA_SHORTCUT_BADGE_OVERRIDE_PACKAGE, ri.activityInfo.packageName);
- ShortcutInfo.Builder infoBuilder = new ShortcutInfo.Builder(
- context, "migrated_shortcut-" + lc.id)
- .setIntent(intent)
- .setExtras(extras)
- .setShortLabel(lc.getTitle());
-
- Bitmap bitmap = null;
- byte[] iconData = lc.getIconBlob();
- if (iconData != null) {
- bitmap = BitmapFactory.decodeByteArray(iconData, 0, iconData.length);
- }
- if (bitmap != null) {
- infoBuilder.setIcon(Icon.createWithBitmap(bitmap));
- }
-
- ShortcutInfo info = infoBuilder.build();
- try {
- if (!PinRequestHelper.createRequestForShortcut(context, info).accept()) {
- deletedShortcuts.add(lc.id);
- continue;
- }
- } catch (Exception e) {
- deletedShortcuts.add(lc.id);
- continue;
- }
- ContentValues update = new ContentValues();
- update.put(Favorites.ITEM_TYPE, Favorites.ITEM_TYPE_DEEP_SHORTCUT);
- update.put(Favorites.INTENT,
- ShortcutKey.makeIntent(info.getId(), context.getPackageName()).toUri(0));
- db.update(Favorites.TABLE_NAME, update, "_id = ?",
- new String[] {Integer.toString(lc.id)});
- }
- lc.close();
- if (!deletedShortcuts.isEmpty()) {
- db.delete(Favorites.TABLE_NAME,
- Utilities.createDbSelectionQuery(Favorites._ID, deletedShortcuts.getArray()),
- null);
- }
-
- // Drop the unused columns
- db.execSQL("ALTER TABLE " + Favorites.TABLE_NAME + " DROP COLUMN iconPackage;");
- db.execSQL("ALTER TABLE " + Favorites.TABLE_NAME + " DROP COLUMN iconResource;");
- }
-
- /**
- * Utility class to simplify managing sqlite transactions
- */
- public static class SQLiteTransaction implements AutoCloseable {
- private final SQLiteDatabase mDb;
-
- public SQLiteTransaction(SQLiteDatabase db) {
- mDb = db;
- db.beginTransaction();
- }
-
- public void commit() {
- mDb.setTransactionSuccessful();
- }
-
- @Override
- public void close() {
- mDb.endTransaction();
- }
-
- public SQLiteDatabase getDb() {
- return mDb;
- }
- }
-}
diff --git a/src/com/android/launcher3/provider/LauncherDbUtils.kt b/src/com/android/launcher3/provider/LauncherDbUtils.kt
new file mode 100644
index 0000000..3c68e46
--- /dev/null
+++ b/src/com/android/launcher3/provider/LauncherDbUtils.kt
@@ -0,0 +1,261 @@
+/*
+ * Copyright (C) 2016 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.provider
+
+import android.content.ContentValues
+import android.content.Context
+import android.content.pm.ShortcutInfo
+import android.database.sqlite.SQLiteDatabase
+import android.graphics.Bitmap
+import android.graphics.BitmapFactory
+import android.graphics.drawable.Icon
+import android.os.PersistableBundle
+import android.os.Process
+import android.os.UserManager
+import android.text.TextUtils
+import com.android.launcher3.LauncherAppState
+import com.android.launcher3.LauncherSettings
+import com.android.launcher3.Utilities
+import com.android.launcher3.icons.IconCache
+import com.android.launcher3.model.LoaderCursor
+import com.android.launcher3.model.UserManagerState
+import com.android.launcher3.pm.PinRequestHelper
+import com.android.launcher3.pm.UserCache
+import com.android.launcher3.shortcuts.ShortcutKey
+import com.android.launcher3.util.IntArray
+import com.android.launcher3.util.IntSet
+import com.android.launcher3.util.PackageManagerHelper
+
+/** A set of utility methods for Launcher DB used for DB updates and migration. */
+object LauncherDbUtils {
+ /**
+ * Returns a string which can be used as a where clause for DB query to match the given itemId
+ */
+ @JvmStatic fun itemIdMatch(itemId: Int): String = "_id=$itemId"
+
+ @JvmStatic
+ fun queryIntArray(
+ distinct: Boolean,
+ db: SQLiteDatabase,
+ tableName: String,
+ columnName: String,
+ selection: String?,
+ groupBy: String?,
+ orderBy: String?,
+ ): IntArray {
+ val out = IntArray()
+ db.query(
+ distinct,
+ tableName,
+ arrayOf(columnName),
+ selection,
+ null,
+ groupBy,
+ null,
+ orderBy,
+ null,
+ )
+ .use { c ->
+ while (c.moveToNext()) {
+ out.add(c.getInt(0))
+ }
+ }
+ return out
+ }
+
+ @JvmStatic
+ fun tableExists(db: SQLiteDatabase, tableName: String): Boolean =
+ db.query(
+ /* distinct = */ true,
+ /* table = */ "sqlite_master",
+ /* columns = */ arrayOf("tbl_name"),
+ /* selection = */ "tbl_name = ?",
+ /* selectionArgs = */ arrayOf(tableName),
+ /* groupBy = */ null,
+ /* having = */ null,
+ /* orderBy = */ null,
+ /* limit = */ null,
+ /* cancellationSignal = */ null,
+ )
+ .use { c ->
+ return c.count > 0
+ }
+
+ @JvmStatic
+ fun dropTable(db: SQLiteDatabase, tableName: String) =
+ db.execSQL("DROP TABLE IF EXISTS $tableName")
+
+ /** Copy fromTable in fromDb to toTable in toDb. */
+ @JvmStatic
+ fun copyTable(
+ fromDb: SQLiteDatabase,
+ fromTable: String,
+ toDb: SQLiteDatabase,
+ toTable: String,
+ context: Context,
+ ) {
+ val userSerial = UserCache.INSTANCE[context].getSerialNumberForUser(Process.myUserHandle())
+ dropTable(toDb, toTable)
+ LauncherSettings.Favorites.addTableToDb(toDb, userSerial, false, toTable)
+ if (fromDb != toDb) {
+ toDb.run {
+ execSQL("ATTACH DATABASE '${fromDb.path}' AS from_db")
+ execSQL(
+ "INSERT INTO $toTable SELECT ${LauncherSettings.Favorites.getColumns(userSerial)} FROM from_db.$fromTable"
+ )
+ execSQL("DETACH DATABASE 'from_db'")
+ }
+ } else {
+ toDb.run {
+ execSQL(
+ "INSERT INTO $toTable SELECT ${
+ LauncherSettings.Favorites.getColumns(
+ userSerial
+ )
+ } FROM $fromTable"
+ )
+ }
+ }
+ }
+
+ /**
+ * Migrates the legacy shortcuts to deep shortcuts pinned under Launcher. Removes any invalid
+ * shortcut or any shortcut which requires some permission to launch
+ */
+ @JvmStatic
+ fun migrateLegacyShortcuts(context: Context, db: SQLiteDatabase) {
+ val c =
+ db.query(
+ LauncherSettings.Favorites.TABLE_NAME,
+ null,
+ "itemType = 1",
+ null,
+ null,
+ null,
+ null,
+ )
+ val pmHelper = PackageManagerHelper.INSTANCE[context]
+ val ums = UserManagerState()
+ ums.run {
+ init(UserCache.INSTANCE[context], context.getSystemService(UserManager::class.java))
+ }
+ val lc = LoaderCursor(c, LauncherAppState.getInstance(context), ums, pmHelper, null)
+ val deletedShortcuts = IntSet()
+
+ while (lc.moveToNext()) {
+ if (lc.user !== Process.myUserHandle()) {
+ deletedShortcuts.add(lc.id)
+ continue
+ }
+ val intent = lc.parseIntent()
+ if (intent == null) {
+ deletedShortcuts.add(lc.id)
+ continue
+ }
+ if (TextUtils.isEmpty(lc.title)) {
+ deletedShortcuts.add(lc.id)
+ continue
+ }
+
+ // Make sure the target intent can be launched without any permissions. Otherwise remove
+ // the shortcut
+ val ri = context.packageManager.resolveActivity(intent, 0)
+ if (ri == null || !TextUtils.isEmpty(ri.activityInfo.permission)) {
+ deletedShortcuts.add(lc.id)
+ continue
+ }
+ val extras =
+ PersistableBundle().apply {
+ putString(
+ IconCache.EXTRA_SHORTCUT_BADGE_OVERRIDE_PACKAGE,
+ ri.activityInfo.packageName,
+ )
+ }
+ val infoBuilder =
+ ShortcutInfo.Builder(context, "migrated_shortcut-${lc.id}")
+ .setIntent(intent)
+ .setExtras(extras)
+ .setShortLabel(lc.title)
+
+ var bitmap: Bitmap? = null
+ val iconData = lc.iconBlob
+ if (iconData != null) {
+ bitmap = BitmapFactory.decodeByteArray(iconData, 0, iconData.size)
+ }
+ if (bitmap != null) {
+ infoBuilder.setIcon(Icon.createWithBitmap(bitmap))
+ }
+
+ val info = infoBuilder.build()
+ try {
+ if (!PinRequestHelper.createRequestForShortcut(context, info).accept()) {
+ deletedShortcuts.add(lc.id)
+ continue
+ }
+ } catch (e: Exception) {
+ deletedShortcuts.add(lc.id)
+ continue
+ }
+ val update =
+ ContentValues().apply {
+ put(
+ LauncherSettings.Favorites.ITEM_TYPE,
+ LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT,
+ )
+ put(
+ LauncherSettings.Favorites.INTENT,
+ ShortcutKey.makeIntent(info.id, context.packageName).toUri(0),
+ )
+ }
+ db.update(
+ LauncherSettings.Favorites.TABLE_NAME,
+ update,
+ "_id = ?",
+ arrayOf(lc.id.toString()),
+ )
+ }
+ lc.close()
+ if (deletedShortcuts.isEmpty.not()) {
+ db.delete(
+ /* table = */ LauncherSettings.Favorites.TABLE_NAME,
+ /* whereClause = */ Utilities.createDbSelectionQuery(
+ LauncherSettings.Favorites._ID,
+ deletedShortcuts.array,
+ ),
+ /* whereArgs = */ null,
+ )
+ }
+
+ // Drop the unused columns
+ db.run {
+ execSQL("ALTER TABLE ${LauncherSettings.Favorites.TABLE_NAME} DROP COLUMN iconPackage;")
+ execSQL(
+ "ALTER TABLE ${LauncherSettings.Favorites.TABLE_NAME} DROP COLUMN iconResource;"
+ )
+ }
+ }
+
+ /** Utility class to simplify managing sqlite transactions */
+ class SQLiteTransaction(val db: SQLiteDatabase) : AutoCloseable {
+ init {
+ db.beginTransaction()
+ }
+
+ fun commit() = db.setTransactionSuccessful()
+
+ override fun close() = db.endTransaction()
+ }
+}
diff --git a/src/com/android/launcher3/shapes/AppShape.kt b/src/com/android/launcher3/shapes/AppShape.kt
new file mode 100644
index 0000000..68200a0
--- /dev/null
+++ b/src/com/android/launcher3/shapes/AppShape.kt
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.shapes
+
+class AppShape(val key: String, val title: String, val path: String)
diff --git a/src/com/android/launcher3/shapes/AppShapesProvider.kt b/src/com/android/launcher3/shapes/AppShapesProvider.kt
new file mode 100644
index 0000000..41bac6a
--- /dev/null
+++ b/src/com/android/launcher3/shapes/AppShapesProvider.kt
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.shapes
+
+object AppShapesProvider {
+
+ val shapes =
+ listOf(
+ AppShape(
+ "arch",
+ "arch",
+ "M100 83.46C100 85.471 100 86.476 99.9 87.321 99.116 93.916 93.916 99.116 87.321 99.9 86.476 100 85.471 100 83.46 100H16.54C14.529 100 13.524 100 12.679 99.9 6.084 99.116.884 93.916.1 87.321 0 86.476 0 85.471 0 83.46L0 50C0 22.386 22.386 0 50 0 77.614 0 100 22.386 100 50V83.46Z",
+ ),
+ AppShape(
+ "4_sided_cookie",
+ "4 sided cookie",
+ "M63.605 3C84.733-6.176 106.176 15.268 97 36.395L95.483 39.888C92.681 46.338 92.681 53.662 95.483 60.112L97 63.605C106.176 84.732 84.733 106.176 63.605 97L60.112 95.483C53.662 92.681 46.338 92.681 39.888 95.483L36.395 97C15.267 106.176-6.176 84.732 3 63.605L4.517 60.112C7.319 53.662 7.319 46.338 4.517 39.888L3 36.395C-6.176 15.268 15.267-6.176 36.395 3L39.888 4.517C46.338 7.319 53.662 7.319 60.112 4.517L63.605 3Z",
+ ),
+ AppShape(
+ "seven_sided_cookie",
+ "7 sided cookie",
+ "M35.209 4.878C36.326 3.895 36.884 3.404 37.397 3.006 44.82-2.742 55.18-2.742 62.603 3.006 63.116 3.404 63.674 3.895 64.791 4.878 65.164 5.207 65.351 5.371 65.539 5.529 68.167 7.734 71.303 9.248 74.663 9.932 74.902 9.981 75.147 10.025 75.637 10.113 77.1 10.375 77.831 10.506 78.461 10.66 87.573 12.893 94.032 21.011 94.176 30.412 94.186 31.062 94.151 31.805 94.08 33.293 94.057 33.791 94.045 34.04 94.039 34.285 93.958 37.72 94.732 41.121 96.293 44.18 96.404 44.399 96.522 44.618 96.759 45.056 97.467 46.366 97.821 47.021 98.093 47.611 102.032 56.143 99.727 66.266 92.484 72.24 91.983 72.653 91.381 73.089 90.177 73.961 89.774 74.254 89.572 74.4 89.377 74.548 86.647 76.626 84.477 79.353 83.063 82.483 82.962 82.707 82.865 82.936 82.671 83.395 82.091 84.766 81.8 85.451 81.51 86.033 77.31 94.44 67.977 98.945 58.801 96.994 58.166 96.859 57.451 96.659 56.019 96.259 55.54 96.125 55.3 96.058 55.063 95.998 51.74 95.154 48.26 95.154 44.937 95.998 44.699 96.058 44.46 96.125 43.981 96.259 42.549 96.659 41.834 96.859 41.199 96.994 32.023 98.945 22.69 94.44 18.49 86.033 18.2 85.451 17.909 84.766 17.329 83.395 17.135 82.936 17.038 82.707 16.937 82.483 15.523 79.353 13.353 76.626 10.623 74.548 10.428 74.4 10.226 74.254 9.823 73.961 8.619 73.089 8.017 72.653 7.516 72.24.273 66.266-2.032 56.143 1.907 47.611 2.179 47.021 2.533 46.366 3.241 45.056 3.478 44.618 3.596 44.399 3.707 44.18 5.268 41.121 6.042 37.72 5.961 34.285 5.955 34.04 5.943 33.791 5.92 33.293 5.849 31.805 5.814 31.062 5.824 30.412 5.968 21.011 12.427 12.893 21.539 10.66 22.169 10.506 22.9 10.375 24.363 10.113 24.853 10.025 25.098 9.981 25.337 9.932 28.697 9.248 31.833 7.734 34.461 5.529 34.649 5.371 34.836 5.207 35.209 4.878Z",
+ ),
+ AppShape(
+ "sunny",
+ "sunny",
+ "M42.846 4.873C46.084-.531 53.916-.531 57.154 4.873L60.796 10.951C62.685 14.103 66.414 15.647 69.978 14.754L76.851 13.032C82.962 11.5 88.5 17.038 86.968 23.149L85.246 30.022C84.353 33.586 85.897 37.315 89.049 39.204L95.127 42.846C100.531 46.084 100.531 53.916 95.127 57.154L89.049 60.796C85.897 62.685 84.353 66.414 85.246 69.978L86.968 76.851C88.5 82.962 82.962 88.5 76.851 86.968L69.978 85.246C66.414 84.353 62.685 85.898 60.796 89.049L57.154 95.127C53.916 100.531 46.084 100.531 42.846 95.127L39.204 89.049C37.315 85.898 33.586 84.353 30.022 85.246L23.149 86.968C17.038 88.5 11.5 82.962 13.032 76.851L14.754 69.978C15.647 66.414 14.103 62.685 10.951 60.796L4.873 57.154C-.531 53.916-.531 46.084 4.873 42.846L10.951 39.204C14.103 37.315 15.647 33.586 14.754 30.022L13.032 23.149C11.5 17.038 17.038 11.5 23.149 13.032L30.022 14.754C33.586 15.647 37.315 14.103 39.204 10.951L42.846 4.873Z",
+ ),
+ AppShape(
+ "circle",
+ "circle",
+ "M99.18 50C99.18 77.162 77.162 99.18 50 99.18 22.838 99.18.82 77.162.82 50 .82 22.839 22.838.82 50 .82 77.162.82 99.18 22.839 99.18 50Z",
+ ),
+ AppShape(
+ "square",
+ "square",
+ "M99.18 53.689C99.18 67.434 99.18 74.306 97.022 79.758 93.897 87.649 87.649 93.897 79.758 97.022 74.306 99.18 67.434 99.18 53.689 99.18H46.311C32.566 99.18 25.694 99.18 20.242 97.022 12.351 93.897 6.103 87.649 2.978 79.758.82 74.306.82 67.434.82 53.689L.82 46.311C.82 32.566.82 25.694 2.978 20.242 6.103 12.351 12.351 6.103 20.242 2.978 25.694.82 32.566.82 46.311.82L53.689.82C67.434.82 74.306.82 79.758 2.978 87.649 6.103 93.897 12.351 97.022 20.242 99.18 25.694 99.18 32.566 99.18 46.311V53.689Z\n",
+ ),
+ )
+}
diff --git a/src/com/android/launcher3/util/ApiWrapper.java b/src/com/android/launcher3/util/ApiWrapper.java
index 21f91acd..467a7ec 100644
--- a/src/com/android/launcher3/util/ApiWrapper.java
+++ b/src/com/android/launcher3/util/ApiWrapper.java
@@ -17,7 +17,6 @@
package com.android.launcher3.util;
import static com.android.launcher3.LauncherConstants.ActivityCodes.REQUEST_HOME_ROLE;
-import static com.android.launcher3.util.MainThreadInitializedObject.forOverride;
import android.app.ActivityOptions;
import android.app.Person;
@@ -38,24 +37,30 @@
import com.android.launcher3.BuildConfig;
import com.android.launcher3.Launcher;
-import com.android.launcher3.R;
import com.android.launcher3.Utilities;
+import com.android.launcher3.dagger.ApplicationContext;
+import com.android.launcher3.dagger.LauncherAppComponent;
+import com.android.launcher3.dagger.LauncherAppSingleton;
import java.util.Collections;
import java.util.List;
import java.util.Map;
+import javax.inject.Inject;
+
/**
* A wrapper for the hidden API calls
*/
-public class ApiWrapper implements ResourceBasedOverride, SafeCloseable {
+@LauncherAppSingleton
+public class ApiWrapper {
- public static final MainThreadInitializedObject<ApiWrapper> INSTANCE =
- forOverride(ApiWrapper.class, R.string.api_wrapper_class);
+ public static final DaggerSingletonObject<ApiWrapper> INSTANCE = new DaggerSingletonObject<>(
+ LauncherAppComponent::getApiWrapper);
protected final Context mContext;
- public ApiWrapper(Context context) {
+ @Inject
+ public ApiWrapper(@ApplicationContext Context context) {
mContext = context;
}
@@ -166,9 +171,6 @@
return appInfo.sourceDir;
}
- @Override
- public void close() { }
-
private static class NoopDrawable extends ColorDrawable {
@Override
public int getIntrinsicHeight() {
diff --git a/src/com/android/launcher3/util/MainThreadInitializedObject.java b/src/com/android/launcher3/util/MainThreadInitializedObject.java
index 9a70298..356a551 100644
--- a/src/com/android/launcher3/util/MainThreadInitializedObject.java
+++ b/src/com/android/launcher3/util/MainThreadInitializedObject.java
@@ -150,7 +150,6 @@
public SandboxContext(Context base) {
attachBaseContext(base);
- initDagger();
}
@Override
diff --git a/src/com/android/launcher3/widget/custom/CustomWidgetManager.java b/src/com/android/launcher3/widget/custom/CustomWidgetManager.java
index 9dddc18..20cce8f 100644
--- a/src/com/android/launcher3/widget/custom/CustomWidgetManager.java
+++ b/src/com/android/launcher3/widget/custom/CustomWidgetManager.java
@@ -39,8 +39,8 @@
import com.android.launcher3.dagger.LauncherBaseAppComponent;
import com.android.launcher3.util.DaggerSingletonObject;
import com.android.launcher3.util.DaggerSingletonTracker;
-import com.android.launcher3.util.PackageUserKey;
import com.android.launcher3.util.PluginManagerWrapper;
+import com.android.launcher3.util.SafeCloseable;
import com.android.launcher3.widget.LauncherAppWidgetHostView;
import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
import com.android.systemui.plugins.CustomWidgetPlugin;
@@ -51,7 +51,7 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import java.util.function.Consumer;
+import java.util.concurrent.CopyOnWriteArrayList;
import java.util.stream.Stream;
import javax.inject.Inject;
@@ -70,7 +70,7 @@
private final Context mContext;
private final HashMap<ComponentName, CustomWidgetPlugin> mPlugins;
private final List<CustomAppWidgetProviderInfo> mCustomWidgets;
- private Consumer<PackageUserKey> mWidgetRefreshCallback;
+ private final List<Runnable> mWidgetRefreshCallbacks = new CopyOnWriteArrayList<>();
private final @NonNull AppWidgetManager mAppWidgetManager;
@Inject
@@ -111,23 +111,20 @@
@Override
public void onPluginConnected(CustomWidgetPlugin plugin, Context context) {
- List<AppWidgetProviderInfo> providers = mAppWidgetManager
- .getInstalledProvidersForProfile(Process.myUserHandle());
- if (providers.isEmpty()) return;
- Parcel parcel = Parcel.obtain();
- providers.get(0).writeToParcel(parcel, 0);
- parcel.setDataPosition(0);
- CustomAppWidgetProviderInfo info = newInfo(plugin, parcel);
- parcel.recycle();
- mPlugins.put(info.provider, plugin);
- mCustomWidgets.add(info);
+ CustomAppWidgetProviderInfo info = getAndAddInfo(new ComponentName(
+ PLUGIN_PKG, CLS_CUSTOM_WIDGET_PREFIX + plugin.getClass().getName()));
+ if (info != null) {
+ plugin.updateWidgetInfo(info, mContext);
+ mPlugins.put(info.provider, plugin);
+ mWidgetRefreshCallbacks.forEach(MAIN_EXECUTOR::execute);
+ }
}
@Override
public void onPluginDisconnected(CustomWidgetPlugin plugin) {
- ComponentName cn = getWidgetProviderComponent(plugin);
- mPlugins.remove(cn);
- mCustomWidgets.removeIf(w -> w.getComponent().equals(cn));
+ // Leave the providerInfo as plugins can get disconnected/reconnected multiple times
+ mPlugins.values().remove(plugin);
+ mWidgetRefreshCallbacks.forEach(MAIN_EXECUTOR::execute);
}
@VisibleForTesting
@@ -138,9 +135,11 @@
/**
* Inject a callback function to refresh the widgets.
+ * @return a closeable to remove this callback
*/
- public void setWidgetRefreshCallback(Consumer<PackageUserKey> cb) {
- mWidgetRefreshCallback = cb;
+ public SafeCloseable addWidgetRefreshCallback(Runnable callback) {
+ mWidgetRefreshCallbacks.add(callback);
+ return () -> mWidgetRefreshCallbacks.remove(callback);
}
/**
@@ -149,8 +148,9 @@
public void onViewCreated(LauncherAppWidgetHostView view) {
CustomAppWidgetProviderInfo info = (CustomAppWidgetProviderInfo) view.getAppWidgetInfo();
CustomWidgetPlugin plugin = mPlugins.get(info.provider);
- if (plugin == null) return;
- plugin.onViewCreated(view);
+ if (plugin != null) {
+ plugin.onViewCreated(view);
+ }
}
/**
@@ -166,14 +166,13 @@
*/
@Nullable
public LauncherAppWidgetProviderInfo getWidgetProvider(ComponentName cn) {
- return mCustomWidgets.stream()
+ LauncherAppWidgetProviderInfo info = mCustomWidgets.stream()
.filter(w -> w.getComponent().equals(cn)).findAny().orElse(null);
- }
-
- private CustomAppWidgetProviderInfo newInfo(CustomWidgetPlugin plugin, Parcel parcel) {
- CustomAppWidgetProviderInfo info = new CustomAppWidgetProviderInfo(parcel, false);
- info.provider = getWidgetProviderComponent(plugin);
- plugin.updateWidgetInfo(info, mContext);
+ if (info == null) {
+ // If the info is not present, add a placeholder info since the
+ // plugin might get loaded later
+ info = getAndAddInfo(cn);
+ }
return info;
}
@@ -184,8 +183,24 @@
return CUSTOM_WIDGET_ID - mCustomWidgets.indexOf(getWidgetProvider(componentName));
}
- private ComponentName getWidgetProviderComponent(CustomWidgetPlugin plugin) {
- return new ComponentName(
- PLUGIN_PKG, CLS_CUSTOM_WIDGET_PREFIX + plugin.getClass().getName());
+ @Nullable
+ private CustomAppWidgetProviderInfo getAndAddInfo(ComponentName cn) {
+ for (CustomAppWidgetProviderInfo info : mCustomWidgets) {
+ if (info.provider.equals(cn)) return info;
+ }
+
+ List<AppWidgetProviderInfo> providers = mAppWidgetManager
+ .getInstalledProvidersForProfile(Process.myUserHandle());
+ if (providers.isEmpty()) return null;
+ Parcel parcel = Parcel.obtain();
+ providers.get(0).writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+ CustomAppWidgetProviderInfo info = new CustomAppWidgetProviderInfo(parcel, false);
+ parcel.recycle();
+
+ info.provider = cn;
+ info.initialLayout = 0;
+ mCustomWidgets.add(info);
+ return info;
}
}
diff --git a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
index c8ad564..1860977 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
@@ -145,7 +145,9 @@
protected DeviceProfile mDeviceProfile;
protected TextView mNoWidgetsView;
- protected StickyHeaderLayout mSearchScrollView;
+ protected LinearLayout mSearchScrollView;
+ // Reference to the mSearchScrollView when it is is a sticky header.
+ private @Nullable StickyHeaderLayout mStickyHeaderLayout;
protected WidgetRecommendationsView mWidgetRecommendationsView;
protected LinearLayout mWidgetRecommendationsContainer;
protected View mTabBar;
@@ -220,7 +222,11 @@
protected void setupViews() {
mSearchScrollView = findViewById(R.id.search_and_recommendations_container);
- mSearchScrollView.setCurrentRecyclerView(findViewById(R.id.primary_widgets_list_view));
+ if (mSearchScrollView instanceof StickyHeaderLayout) {
+ mStickyHeaderLayout = (StickyHeaderLayout) mSearchScrollView;
+ mStickyHeaderLayout.setCurrentRecyclerView(
+ findViewById(R.id.primary_widgets_list_view));
+ }
mNoWidgetsView = findViewById(R.id.no_widgets_text);
mFastScroller = findViewById(R.id.fast_scroller);
mFastScroller.setPopupView(findViewById(R.id.fast_scroller_popup));
@@ -284,7 +290,9 @@
reset();
resetExpandedHeaders();
mCurrentWidgetsRecyclerView = recyclerView;
- mSearchScrollView.setCurrentRecyclerView(recyclerView);
+ if (mStickyHeaderLayout != null) {
+ mStickyHeaderLayout.setCurrentRecyclerView(recyclerView);
+ }
}
}
@@ -313,7 +321,9 @@
mAdapters.get(AdapterHolder.WORK).mWidgetsRecyclerView.scrollToTop();
}
mAdapters.get(AdapterHolder.SEARCH).mWidgetsRecyclerView.scrollToTop();
- mSearchScrollView.reset(/* animate= */ true);
+ if (mStickyHeaderLayout != null) {
+ mStickyHeaderLayout.reset(/* animate= */ true);
+ }
}
@VisibleForTesting
@@ -1051,7 +1061,7 @@
}
private int getEmptySpaceHeight() {
- return mSearchScrollView.getHeaderHeight();
+ return mStickyHeaderLayout != null ? mStickyHeaderLayout.getHeaderHeight() : 0;
}
void setup(WidgetsRecyclerView recyclerView) {
diff --git a/src/com/android/launcher3/widget/picker/WidgetsListAdapter.java b/src/com/android/launcher3/widget/picker/WidgetsListAdapter.java
index 8dd1de4..3d3a669 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsListAdapter.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsListAdapter.java
@@ -396,15 +396,6 @@
LinearLayoutManager layoutManager = (LinearLayoutManager) mRecyclerView.getLayoutManager();
if (layoutManager == null) return;
- if (position == mVisibleEntries.size() - 2
- && mVisibleEntries.get(mVisibleEntries.size() - 1)
- instanceof WidgetsListContentEntry) {
- // If the selected header is in the last position and its content is showing, then
- // scroll to the final position so the last list of widgets will show.
- layoutManager.scrollToPosition(mVisibleEntries.size() - 1);
- return;
- }
-
// Scroll to the header view's current offset, accounting for the recycler view's padding.
// If the header view couldn't be found, then it will appear at the top of the list.
layoutManager.scrollToPositionWithOffset(
diff --git a/tests/Android.bp b/tests/Android.bp
index 9667277..35a2275 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -148,6 +148,7 @@
platform_apis: true,
test_config: "Launcher3Tests.xml",
data: [":Launcher3"],
+ plugins: ["dagger2-compiler"],
test_suites: ["general-tests"],
}
@@ -237,6 +238,7 @@
"truth",
],
instrumentation_for: "Launcher3",
+ plugins: ["dagger2-compiler"],
upstream: true,
strict_mode: false,
}
diff --git a/tests/multivalentTests/src/com/android/launcher3/AutoInstallsLayoutTest.kt b/tests/multivalentTests/src/com/android/launcher3/AutoInstallsLayoutTest.kt
index b04bcca..f73a9d3 100644
--- a/tests/multivalentTests/src/com/android/launcher3/AutoInstallsLayoutTest.kt
+++ b/tests/multivalentTests/src/com/android/launcher3/AutoInstallsLayoutTest.kt
@@ -41,6 +41,8 @@
import com.android.launcher3.LauncherSettings.Favorites.SPANX
import com.android.launcher3.LauncherSettings.Favorites.SPANY
import com.android.launcher3.LauncherSettings.Favorites._ID
+import com.android.launcher3.dagger.LauncherAppComponent
+import com.android.launcher3.dagger.LauncherAppSingleton
import com.android.launcher3.model.data.AppInfo
import com.android.launcher3.pm.UserCache
import com.android.launcher3.util.ApiWrapper
@@ -54,6 +56,8 @@
import com.android.launcher3.util.UserIconInfo.TYPE_WORK
import com.android.launcher3.widget.LauncherWidgetHolder
import com.google.common.truth.Truth.assertThat
+import dagger.BindsInstance
+import dagger.Component
import java.io.StringReader
import org.junit.After
import org.junit.Before
@@ -162,7 +166,9 @@
@Test
fun work_item_added_to_home() {
val apiWrapperMock = spy(ApiWrapper.INSTANCE[targetContext])
- targetContext.putObject(ApiWrapper.INSTANCE, apiWrapperMock)
+ targetContext.initDaggerComponent(
+ DaggerAutoInstallsLayoutTestComponent.builder().bindApiWrapper(apiWrapperMock)
+ )
doReturn(
mapOf(
myUserHandle() to UserIconInfo(myUserHandle(), TYPE_MAIN, 0),
@@ -198,7 +204,7 @@
callback,
SourceResources.wrap(targetContext.resources),
{ Xml.newPullParser().also { it.setInput(StringReader(build())) } },
- TAG_WORKSPACE
+ TAG_WORKSPACE,
)
class MyCallback : LayoutParserCallback {
@@ -214,3 +220,14 @@
}
}
}
+
+@LauncherAppSingleton
+@Component
+interface AutoInstallsLayoutTestComponent : LauncherAppComponent {
+ @Component.Builder
+ interface Builder : LauncherAppComponent.Builder {
+ @BindsInstance fun bindApiWrapper(wrapper: ApiWrapper): Builder
+
+ override fun build(): AutoInstallsLayoutTestComponent
+ }
+}
diff --git a/tests/multivalentTests/src/com/android/launcher3/model/GridSizeMigrationDBControllerTest.kt b/tests/multivalentTests/src/com/android/launcher3/model/GridSizeMigrationTest.kt
similarity index 94%
rename from tests/multivalentTests/src/com/android/launcher3/model/GridSizeMigrationDBControllerTest.kt
rename to tests/multivalentTests/src/com/android/launcher3/model/GridSizeMigrationTest.kt
index c6f291d..7933331 100644
--- a/tests/multivalentTests/src/com/android/launcher3/model/GridSizeMigrationDBControllerTest.kt
+++ b/tests/multivalentTests/src/com/android/launcher3/model/GridSizeMigrationTest.kt
@@ -41,10 +41,10 @@
import org.junit.Test
import org.junit.runner.RunWith
-/** Unit tests for [GridSizeMigrationUtil] */
+/** Unit tests for [GridSizeMigrationDBController, GridSizeMigrationLogic] */
@SmallTest
@RunWith(AndroidJUnit4::class)
-class GridSizeMigrationUtilTest {
+class GridSizeMigrationTest {
private lateinit var modelHelper: LauncherModelHelper
private lateinit var context: Context
@@ -130,13 +130,21 @@
val srcReader = DbReader(db, TMP_TABLE, context)
val destReader = DbReader(db, TABLE_NAME, context)
if (Flags.gridMigrationRefactor()) {
- val gridSizeMigrationLogic = GridSizeMigrationLogic()
- gridSizeMigrationLogic.migrateGrid(
- context,
- DeviceGridState(context),
- DeviceGridState(idp),
+ var gridSizeMigrationLogic = GridSizeMigrationLogic()
+ val idsInUse = mutableListOf<Int>()
+ gridSizeMigrationLogic.migrateHotseat(
+ idp.numDatabaseHotseatIcons,
+ srcReader,
+ destReader,
dbHelper,
- db,
+ idsInUse,
+ )
+ gridSizeMigrationLogic.migrateWorkspace(
+ srcReader,
+ destReader,
+ dbHelper,
+ Point(idp.numColumns, idp.numRows),
+ idsInUse,
)
} else {
GridSizeMigrationDBController.migrate(
@@ -266,12 +274,20 @@
// migrate from A -> B
if (Flags.gridMigrationRefactor()) {
var gridSizeMigrationLogic = GridSizeMigrationLogic()
- gridSizeMigrationLogic.migrateGrid(
- context,
- DeviceGridState(context),
- DeviceGridState(idp),
+ val idsInUse = mutableListOf<Int>()
+ gridSizeMigrationLogic.migrateHotseat(
+ idp.numDatabaseHotseatIcons,
+ readerGridA,
+ readerGridB,
dbHelper,
- db,
+ idsInUse,
+ )
+ gridSizeMigrationLogic.migrateWorkspace(
+ readerGridA,
+ readerGridB,
+ dbHelper,
+ Point(idp.numColumns, idp.numRows),
+ idsInUse,
)
} else {
GridSizeMigrationDBController.migrate(
@@ -445,12 +461,20 @@
) {
if (Flags.gridMigrationRefactor()) {
var gridSizeMigrationLogic = GridSizeMigrationLogic()
- gridSizeMigrationLogic.migrateGrid(
- context,
- DeviceGridState(context),
- DeviceGridState(idp),
+ val idsInUse = mutableListOf<Int>()
+ gridSizeMigrationLogic.migrateHotseat(
+ idp.numDatabaseHotseatIcons,
+ srcReader,
+ destReader,
dbHelper,
- db,
+ idsInUse,
+ )
+ gridSizeMigrationLogic.migrateWorkspace(
+ srcReader,
+ destReader,
+ dbHelper,
+ Point(idp.numColumns, idp.numRows),
+ idsInUse,
)
} else {
GridSizeMigrationDBController.migrate(
diff --git a/tests/src/com/android/launcher3/model/LoaderTaskTest.kt b/tests/src/com/android/launcher3/model/LoaderTaskTest.kt
index b17cd4d..ef7242f 100644
--- a/tests/src/com/android/launcher3/model/LoaderTaskTest.kt
+++ b/tests/src/com/android/launcher3/model/LoaderTaskTest.kt
@@ -3,6 +3,8 @@
import android.appwidget.AppWidgetManager
import android.content.Intent
import android.os.UserHandle
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
import android.platform.test.flag.junit.SetFlagsRule
import android.provider.Settings
import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -203,7 +205,8 @@
}
@Test
- fun `When launcher_broadcast_installed_apps and is restore then send installed item broadcast`() {
+ @DisableFlags(Flags.FLAG_ENABLE_FIRST_SCREEN_BROADCAST_ARCHIVING_EXTRAS)
+ fun `When secure setting true and is restore then send installed item broadcast`() {
// Given
val spyContext = spy(context)
`when`(app.context).thenReturn(spyContext)
@@ -271,6 +274,76 @@
}
@Test
+ @EnableFlags(Flags.FLAG_ENABLE_FIRST_SCREEN_BROADCAST_ARCHIVING_EXTRAS)
+ fun `When broadcast flag true and is restore then send installed item broadcast`() {
+ // Given
+ val spyContext = spy(context)
+ `when`(app.context).thenReturn(spyContext)
+ whenever(
+ FirstScreenBroadcastHelper.createModelsForFirstScreenBroadcast(
+ any(),
+ any(),
+ any(),
+ any(),
+ )
+ )
+ .thenReturn(listOf(expectedBroadcastModel))
+
+ whenever(
+ FirstScreenBroadcastHelper.sendBroadcastsForModels(
+ spyContext,
+ listOf(expectedBroadcastModel),
+ )
+ )
+ .thenCallRealMethod()
+
+ Settings.Secure.putInt(spyContext.contentResolver, "launcher_broadcast_installed_apps", 0)
+ RestoreDbTask.setPending(spyContext)
+
+ // When
+ LoaderTask(app, bgAllAppsList, BgDataModel(), modelDelegate, launcherBinder)
+ .runSyncOnBackgroundThread()
+
+ // Then
+ val argumentCaptor = ArgumentCaptor.forClass(Intent::class.java)
+ verify(spyContext).sendBroadcast(argumentCaptor.capture())
+ val actualBroadcastIntent = argumentCaptor.value
+ assertEquals(expectedBroadcastModel.installerPackage, actualBroadcastIntent.`package`)
+ assertEquals(
+ ArrayList(expectedBroadcastModel.installedWorkspaceItems),
+ actualBroadcastIntent.getStringArrayListExtra("workspaceInstalledItems"),
+ )
+ assertEquals(
+ ArrayList(expectedBroadcastModel.installedHotseatItems),
+ actualBroadcastIntent.getStringArrayListExtra("hotseatInstalledItems"),
+ )
+ assertEquals(
+ ArrayList(
+ expectedBroadcastModel.firstScreenInstalledWidgets +
+ expectedBroadcastModel.secondaryScreenInstalledWidgets
+ ),
+ actualBroadcastIntent.getStringArrayListExtra("widgetInstalledItems"),
+ )
+ assertEquals(
+ ArrayList(expectedBroadcastModel.pendingCollectionItems),
+ actualBroadcastIntent.getStringArrayListExtra("folderItem"),
+ )
+ assertEquals(
+ ArrayList(expectedBroadcastModel.pendingWorkspaceItems),
+ actualBroadcastIntent.getStringArrayListExtra("workspaceItem"),
+ )
+ assertEquals(
+ ArrayList(expectedBroadcastModel.pendingHotseatItems),
+ actualBroadcastIntent.getStringArrayListExtra("hotseatItem"),
+ )
+ assertEquals(
+ ArrayList(expectedBroadcastModel.pendingWidgetItems),
+ actualBroadcastIntent.getStringArrayListExtra("widgetItem"),
+ )
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_FIRST_SCREEN_BROADCAST_ARCHIVING_EXTRAS)
fun `When not a restore then installed item broadcast not sent`() {
// Given
val spyContext = spy(context)
@@ -304,7 +377,8 @@
}
@Test
- fun `When launcher_broadcast_installed_apps false then installed item broadcast not sent`() {
+ @DisableFlags(Flags.FLAG_ENABLE_FIRST_SCREEN_BROADCAST_ARCHIVING_EXTRAS)
+ fun `When broadcast flag and secure setting false then installed item broadcast not sent`() {
// Given
val spyContext = spy(context)
`when`(app.context).thenReturn(spyContext)
diff --git a/tests/src/com/android/launcher3/model/gridmigration/ValidGridMigrationUnitTest.kt b/tests/src/com/android/launcher3/model/gridmigration/ValidGridMigrationUnitTest.kt
index c08237c..b96dbcd 100644
--- a/tests/src/com/android/launcher3/model/gridmigration/ValidGridMigrationUnitTest.kt
+++ b/tests/src/com/android/launcher3/model/gridmigration/ValidGridMigrationUnitTest.kt
@@ -136,12 +136,20 @@
LauncherDbUtils.SQLiteTransaction(dbHelper.writableDatabase).use {
if (Flags.gridMigrationRefactor()) {
val gridSizeMigrationLogic = GridSizeMigrationLogic()
- gridSizeMigrationLogic.migrateGrid(
- context,
- srcGrid.toGridState(),
- dstGrid.toGridState(),
+ val idsInUse = mutableListOf<Int>()
+ gridSizeMigrationLogic.migrateHotseat(
+ dstGrid.size.x,
+ GridSizeMigrationDBController.DbReader(it.db, srcGrid.tableName, context),
+ GridSizeMigrationDBController.DbReader(it.db, dstGrid.tableName, context),
dbHelper,
- it.db,
+ idsInUse,
+ )
+ gridSizeMigrationLogic.migrateWorkspace(
+ GridSizeMigrationDBController.DbReader(it.db, srcGrid.tableName, context),
+ GridSizeMigrationDBController.DbReader(it.db, dstGrid.tableName, context),
+ dbHelper,
+ dstGrid.size,
+ idsInUse,
)
} else {
GridSizeMigrationDBController.migrate(
diff --git a/tests/src/com/android/launcher3/popup/SystemShortcutTest.java b/tests/src/com/android/launcher3/popup/SystemShortcutTest.java
index f54668c..ae54e95 100644
--- a/tests/src/com/android/launcher3/popup/SystemShortcutTest.java
+++ b/tests/src/com/android/launcher3/popup/SystemShortcutTest.java
@@ -62,6 +62,8 @@
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.R;
import com.android.launcher3.allapps.PrivateProfileManager;
+import com.android.launcher3.dagger.LauncherAppComponent;
+import com.android.launcher3.dagger.LauncherAppSingleton;
import com.android.launcher3.logging.StatsLogManager;
import com.android.launcher3.logging.StatsLogManager.StatsLogger;
import com.android.launcher3.model.data.AppInfo;
@@ -79,6 +81,9 @@
import com.android.launcher3.widget.picker.model.WidgetPickerDataProvider;
import com.android.launcher3.widget.picker.model.data.WidgetPickerData;
+import dagger.BindsInstance;
+import dagger.Component;
+
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
@@ -115,8 +120,10 @@
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
+ mSandboxContext.initDaggerComponent(
+ DaggerSystemShortcutTest_TestComponent.builder().bindApiWrapper(
+ ApiWrapper.INSTANCE.get(mSandboxContext)));
mSandboxContext.putObject(UserCache.INSTANCE, mUserCache);
- mSandboxContext.putObject(ApiWrapper.INSTANCE, mApiWrapper);
mTestContext = new TestSandboxModelContextWrapper(mSandboxContext) {
@Override
public StatsLogManager getStatsLogManager() {
@@ -405,4 +412,16 @@
systemShortcut.onClick(mView);
verify(mSandboxContext).startActivity(any());
}
+
+ @LauncherAppSingleton
+ @Component
+ interface TestComponent extends LauncherAppComponent {
+ @Component.Builder
+ interface Builder extends LauncherAppComponent.Builder {
+ @BindsInstance Builder bindApiWrapper(ApiWrapper wrapper);
+
+ @Override
+ TestComponent build();
+ }
+ }
}
diff --git a/tests/tapl/com/android/launcher3/tapl/BaseOverview.java b/tests/tapl/com/android/launcher3/tapl/BaseOverview.java
index 0edcfea..1002ca4 100644
--- a/tests/tapl/com/android/launcher3/tapl/BaseOverview.java
+++ b/tests/tapl/com/android/launcher3/tapl/BaseOverview.java
@@ -369,7 +369,6 @@
}
}
-
int getTaskCount() {
return getTasks().size();
}
@@ -441,7 +440,7 @@
"Not expecting an actions bar: device is tablet and task is not centered");
return false;
}
- if (task.isTaskSplit() && (!mLauncher.isAppPairsEnabled() || !isTablet)) {
+ if (task.isGrouped() && (!mLauncher.isAppPairsEnabled() || !isTablet)) {
testLogD(TAG, "Not expecting an actions bar: device is phone and task is split");
// Overview actions aren't visible for split screen tasks, except for save app pair
// button on tablets.
@@ -504,11 +503,11 @@
"want to assert overview actions view visibility="
+ isActionsViewVisible()
+ ", focused task is "
- + (task == null ? "null" : (task.isTaskSplit() ? "split" : "not split"))
+ + (task == null ? "null" : (task.isGrouped() ? "split" : "not split"))
)) {
if (isActionsViewVisible()) {
- if (task.isTaskSplit()) {
+ if (task.isGrouped()) {
mLauncher.waitForOverviewObject("action_save_app_pair");
} else {
mLauncher.waitForOverviewObject("action_buttons");
diff --git a/tests/tapl/com/android/launcher3/tapl/OverviewTask.java b/tests/tapl/com/android/launcher3/tapl/OverviewTask.java
index 9a8d952..5fd4dac 100644
--- a/tests/tapl/com/android/launcher3/tapl/OverviewTask.java
+++ b/tests/tapl/com/android/launcher3/tapl/OverviewTask.java
@@ -16,9 +16,10 @@
package com.android.launcher3.tapl;
-import static com.android.launcher3.tapl.OverviewTask.OverviewSplitTask.DEFAULT;
-import static com.android.launcher3.tapl.OverviewTask.OverviewSplitTask.SPLIT_BOTTOM_OR_RIGHT;
-import static com.android.launcher3.tapl.OverviewTask.OverviewSplitTask.SPLIT_TOP_OR_LEFT;
+import static com.android.launcher3.tapl.OverviewTask.OverviewTaskContainer.DEFAULT;
+import static com.android.launcher3.tapl.OverviewTask.OverviewTaskContainer.DESKTOP;
+import static com.android.launcher3.tapl.OverviewTask.OverviewTaskContainer.SPLIT_BOTTOM_OR_RIGHT;
+import static com.android.launcher3.tapl.OverviewTask.OverviewTaskContainer.SPLIT_TOP_OR_LEFT;
import android.graphics.Rect;
@@ -69,11 +70,11 @@
* divider between.
*/
int getVisibleHeight() {
- if (isTaskSplit()) {
+ if (isGrouped()) {
return getCombinedSplitTaskHeight();
}
- UiObject2 taskSnapshot1 = findObjectInTask(DEFAULT.snapshotRes);
+ UiObject2 taskSnapshot1 = findObjectInTask((isDesktop() ? DESKTOP : DEFAULT).snapshotRes);
return taskSnapshot1.getVisibleBounds().height();
}
@@ -102,7 +103,7 @@
* divider between.
*/
int getVisibleWidth() {
- if (isTaskSplit()) {
+ if (isGrouped()) {
return getCombinedSplitTaskWidth();
}
@@ -164,8 +165,11 @@
dismissBySwipingUp();
+ long numNonDesktopTasks = mOverview.getCurrentTasksForTablet()
+ .stream().filter(t -> !t.isDesktop()).count();
+
try (LauncherInstrumentation.Closable c2 = mLauncher.addContextLayer("dismissed")) {
- if (taskWasFocused) {
+ if (taskWasFocused && numNonDesktopTasks > 0) {
mLauncher.assertNotNull("No task became focused",
mOverview.getFocusedTaskForTablet());
}
@@ -256,7 +260,7 @@
/** Taps the task menu of the split task. Returns the split task's menu object. */
@NonNull
- public OverviewTaskMenu tapMenu(OverviewSplitTask task) {
+ public OverviewTaskMenu tapMenu(OverviewTaskContainer task) {
try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck();
LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
"want to tap the task menu")) {
@@ -270,10 +274,6 @@
}
}
- boolean isTaskSplit() {
- return findObjectInTask(SPLIT_BOTTOM_OR_RIGHT.snapshotRes) != null;
- }
-
private UiObject2 findObjectInTask(String resName) {
return mTask.findObject(mLauncher.getOverviewObjectSelector(resName));
}
@@ -285,8 +285,8 @@
* TODO(b/342627272): remove Nullable support once the bug causing it to be null is fixed.
*/
public boolean containsContentDescription(@Nullable String expected,
- OverviewSplitTask overviewSplitTask) {
- String actual = findObjectInTask(overviewSplitTask.snapshotRes).getContentDescription();
+ OverviewTaskContainer overviewTaskContainer) {
+ String actual = findObjectInTask(overviewTaskContainer.snapshotRes).getContentDescription();
if (actual == null && expected == null) {
return true;
}
@@ -315,21 +315,31 @@
}
}
+ boolean isGrouped() {
+ return mType == TaskViewType.GROUPED;
+ }
+
+ public boolean isDesktop() {
+ return mType == TaskViewType.DESKTOP;
+ }
+
/**
- * Enum used to specify which task is retrieved when it is a split task.
+ * Enum used to specify which resource name should be used depending on the type of the task.
*/
- public enum OverviewSplitTask {
+ public enum OverviewTaskContainer {
// The main task when the task is not split.
DEFAULT("snapshot", "icon"),
// The first task in split task.
SPLIT_TOP_OR_LEFT("snapshot", "icon"),
// The second task in split task.
- SPLIT_BOTTOM_OR_RIGHT("bottomright_snapshot", "bottomRight_icon");
+ SPLIT_BOTTOM_OR_RIGHT("bottomright_snapshot", "bottomRight_icon"),
+ // The desktop task.
+ DESKTOP("background", "icon");
public final String snapshotRes;
public final String iconAppRes;
- OverviewSplitTask(String snapshotRes, String iconAppRes) {
+ OverviewTaskContainer(String snapshotRes, String iconAppRes) {
this.snapshotRes = snapshotRes;
this.iconAppRes = iconAppRes;
}