Merge "Add debug logs to debug flaky workspace binding" into tm-qpr-dev
diff --git a/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java b/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java
index 4fbe8cf..e8f2496 100644
--- a/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java
+++ b/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java
@@ -36,7 +36,6 @@
import com.android.launcher3.allapps.FloatingHeaderRow;
import com.android.launcher3.allapps.FloatingHeaderView;
import com.android.launcher3.anim.AlphaUpdateListener;
-import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.keyboard.FocusIndicatorHelper;
import com.android.launcher3.keyboard.FocusIndicatorHelper.SimpleFocusIndicatorHelper;
import com.android.launcher3.model.data.ItemInfo;
@@ -65,7 +64,6 @@
private FloatingHeaderView mParent;
private boolean mPredictionsEnabled = false;
- private @Nullable List<ItemInfo> mPendingPredictedItems;
private OnLongClickListener mOnIconLongClickListener = ItemLongClickListener.INSTANCE_ALL_APPS;
public PredictionRowView(@NonNull Context context) {
@@ -159,18 +157,10 @@
* we can optimize by swapping them in place.
*/
public void setPredictedApps(List<ItemInfo> items) {
- if (!FeatureFlags.ENABLE_APP_PREDICTIONS_WHILE_VISIBLE.get()
- && !mActivityContext.isBindingItems()
- && isShown()
- && getWindowVisibility() == View.VISIBLE) {
- mPendingPredictedItems = items;
- return;
- }
applyPredictedApps(items);
}
private void applyPredictedApps(List<ItemInfo> items) {
- mPendingPredictedItems = null;
mPredictedApps.clear();
mPredictedApps.addAll(items.stream()
.filter(itemInfo -> itemInfo instanceof WorkspaceItemInfo)
@@ -265,13 +255,4 @@
return getChildAt(0);
}
-
- @Override
- public void onVisibilityAggregated(boolean isVisible) {
- super.onVisibilityAggregated(isVisible);
-
- if (mPendingPredictedItems != null && !isVisible) {
- applyPredictedApps(mPendingPredictedItems);
- }
- }
}
diff --git a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java
index 0a2a9b3..5c2f6b1 100644
--- a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java
+++ b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java
@@ -41,7 +41,6 @@
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.R;
import com.android.launcher3.anim.AnimationSuccessListener;
-import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.dragndrop.DragController;
import com.android.launcher3.dragndrop.DragOptions;
import com.android.launcher3.graphics.DragPreviewProvider;
@@ -279,32 +278,6 @@
* Sets or updates the predicted items
*/
public void setPredictedItems(FixedContainerItems items) {
- boolean shouldIgnoreVisibility = FeatureFlags.ENABLE_APP_PREDICTIONS_WHILE_VISIBLE.get()
- || mLauncher.isWorkspaceLoading()
- || mPredictedItems.equals(items.items)
- || mHotseat.getShortcutsAndWidgets().getChildCount() < mHotSeatItemsCount;
- if (!shouldIgnoreVisibility
- && mHotseat.isShown()
- && mHotseat.getWindowVisibility() == View.VISIBLE) {
- mHotseat.setOnVisibilityAggregatedCallback((isVisible) -> {
- if (isVisible) {
- return;
- }
- mHotseat.setOnVisibilityAggregatedCallback(null);
-
- applyPredictedItems(items);
- });
- } else {
- mHotseat.setOnVisibilityAggregatedCallback(null);
-
- applyPredictedItems(items);
- }
- }
-
- /**
- * Sets or updates the predicted items only once the hotseat becomes hidden to the user
- */
- private void applyPredictedItems(FixedContainerItems items) {
mPredictedItems = new ArrayList(items.items);
if (mPredictedItems.isEmpty()) {
HotseatRestoreHelper.restoreBackup(mLauncher);
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
index 3dbe6c8..951a9b6 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
@@ -35,7 +35,6 @@
import static com.android.launcher3.compat.AccessibilityManagerCompat.sendCustomAccessibilityEvent;
import static com.android.launcher3.config.FeatureFlags.ENABLE_SPLIT_FROM_WORKSPACE;
import static com.android.launcher3.config.FeatureFlags.ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE;
-import static com.android.launcher3.config.FeatureFlags.ENABLE_WIDGET_PICKER_DEPTH;
import static com.android.launcher3.config.FeatureFlags.RECEIVE_UNFOLD_EVENTS_FROM_SYSUI;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_APP_LAUNCH_TAP;
import static com.android.launcher3.model.data.ItemInfo.NO_MATCHING_ID;
@@ -169,7 +168,6 @@
import com.android.quickstep.views.OverviewActionsView;
import com.android.quickstep.views.RecentsView;
import com.android.quickstep.views.TaskView;
-import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.unfold.RemoteUnfoldSharedComponent;
import com.android.systemui.unfold.UnfoldSharedComponent;
@@ -187,7 +185,6 @@
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
-import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.stream.Stream;
@@ -235,7 +232,8 @@
RecentsView overviewPanel = getOverviewPanel();
mSplitSelectStateController =
new SplitSelectStateController(this, mHandler, getStateManager(),
- getDepthController(), getStatsLogManager());
+ getDepthController(), getStatsLogManager(),
+ SystemUiProxy.INSTANCE.get(this), RecentsModel.INSTANCE.get(this));
overviewPanel.init(mActionsView, mSplitSelectStateController);
mSplitWithKeyboardShortcutController = new SplitWithKeyboardShortcutController(this,
mSplitSelectStateController);
@@ -253,8 +251,7 @@
mDesktopVisibilityController = new DesktopVisibilityController(this);
mHotseatPredictionController = new HotseatPredictionController(this);
- mEnableWidgetDepth = ENABLE_WIDGET_PICKER_DEPTH.get()
- && SystemProperties.getBoolean("ro.launcher.depth.widget", true);
+ mEnableWidgetDepth = SystemProperties.getBoolean("ro.launcher.depth.widget", true);
getWorkspace().addOverlayCallback(progress ->
onTaskbarInAppDisplayProgressUpdate(progress, MINUS_ONE_PAGE_PROGRESS_INDEX));
}
diff --git a/quickstep/src/com/android/quickstep/RecentsActivity.java b/quickstep/src/com/android/quickstep/RecentsActivity.java
index f26189c..3f8da56 100644
--- a/quickstep/src/com/android/quickstep/RecentsActivity.java
+++ b/quickstep/src/com/android/quickstep/RecentsActivity.java
@@ -134,7 +134,8 @@
SplitSelectStateController controller =
new SplitSelectStateController(this, mHandler, getStateManager(),
- /* depthController */ null, getStatsLogManager());
+ null /* depthController */, getStatsLogManager(),
+ SystemUiProxy.INSTANCE.get(this), RecentsModel.INSTANCE.get(this));
mDragLayer.recreateControllers();
mFallbackRecentsView.init(mActionsView, controller);
diff --git a/quickstep/src/com/android/quickstep/TaskShortcutFactory.java b/quickstep/src/com/android/quickstep/TaskShortcutFactory.java
index e58845a..255b910 100644
--- a/quickstep/src/com/android/quickstep/TaskShortcutFactory.java
+++ b/quickstep/src/com/android/quickstep/TaskShortcutFactory.java
@@ -18,7 +18,6 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
-import static com.android.launcher3.config.FeatureFlags.ENABLE_OVERVIEW_SELECTIONS;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SYSTEM_SHORTCUT_FREE_FORM_TAP;
import android.app.Activity;
@@ -393,10 +392,7 @@
taskContainer.getThumbnailView().getTaskOverlay()
.getModalStateSystemShortcut(
taskContainer.getItemInfo(), taskContainer.getTaskView());
- if (ENABLE_OVERVIEW_SELECTIONS.get()) {
- return createSingletonShortcutList(modalStateSystemShortcut);
- }
- return null;
+ return createSingletonShortcutList(modalStateSystemShortcut);
}
};
}
diff --git a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
index 51afb5b..d1100c7 100644
--- a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
+++ b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
@@ -107,14 +107,15 @@
private FloatingTaskView mFirstFloatingTaskView;
public SplitSelectStateController(Context context, Handler handler, StateManager stateManager,
- DepthController depthController, StatsLogManager statsLogManager) {
+ DepthController depthController, StatsLogManager statsLogManager,
+ SystemUiProxy systemUiProxy, RecentsModel recentsModel) {
mContext = context;
mHandler = handler;
mStatsLogManager = statsLogManager;
- mSystemUiProxy = SystemUiProxy.INSTANCE.get(mContext);
+ mSystemUiProxy = systemUiProxy;
mStateManager = stateManager;
mDepthController = depthController;
- mRecentTasksModel = RecentsModel.INSTANCE.get(context);
+ mRecentTasksModel = recentsModel;
}
/**
diff --git a/quickstep/src/com/android/quickstep/views/OverviewActionsView.java b/quickstep/src/com/android/quickstep/views/OverviewActionsView.java
index f578b87..de7e2f0 100644
--- a/quickstep/src/com/android/quickstep/views/OverviewActionsView.java
+++ b/quickstep/src/com/android/quickstep/views/OverviewActionsView.java
@@ -56,7 +56,8 @@
HIDDEN_NO_RECENTS,
HIDDEN_SPLIT_SCREEN,
HIDDEN_SPLIT_SELECT_ACTIVE,
- HIDDEN_ACTIONS_IN_MENU
+ HIDDEN_ACTIONS_IN_MENU,
+ HIDDEN_DESKTOP
})
@Retention(RetentionPolicy.SOURCE)
public @interface ActionsHiddenFlags { }
@@ -67,6 +68,7 @@
public static final int HIDDEN_SPLIT_SCREEN = 1 << 3;
public static final int HIDDEN_SPLIT_SELECT_ACTIVE = 1 << 4;
public static final int HIDDEN_ACTIONS_IN_MENU = 1 << 5;
+ public static final int HIDDEN_DESKTOP = 1 << 6;
@IntDef(flag = true, value = {
DISABLED_SCROLLING,
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index e529b04..56e85c6 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -62,6 +62,7 @@
import static com.android.quickstep.views.OverviewActionsView.FLAG_IS_NOT_TABLET;
import static com.android.quickstep.views.OverviewActionsView.FLAG_SINGLE_TASK;
import static com.android.quickstep.views.OverviewActionsView.HIDDEN_ACTIONS_IN_MENU;
+import static com.android.quickstep.views.OverviewActionsView.HIDDEN_DESKTOP;
import static com.android.quickstep.views.OverviewActionsView.HIDDEN_NON_ZERO_ROTATION;
import static com.android.quickstep.views.OverviewActionsView.HIDDEN_NO_RECENTS;
import static com.android.quickstep.views.OverviewActionsView.HIDDEN_NO_TASKS;
@@ -3767,6 +3768,10 @@
!mActivity.getDeviceProfile().isTablet);
mActionsView.updateSplitButtonDisabledFlags(FLAG_SINGLE_TASK,
!FeatureFlags.ENABLE_TASKBAR_IN_OVERVIEW.get() && getTaskViewCount() <= 1);
+ if (DESKTOP_MODE_SUPPORTED) {
+ boolean isCurrentDesktop = getCurrentPageTaskView() instanceof DesktopTaskView;
+ mActionsView.updateHiddenFlags(HIDDEN_DESKTOP, isCurrentDesktop);
+ }
}
/**
diff --git a/quickstep/src/com/android/quickstep/views/TaskMenuViewWithArrow.kt b/quickstep/src/com/android/quickstep/views/TaskMenuViewWithArrow.kt
index 0da70a9..428bd95 100644
--- a/quickstep/src/com/android/quickstep/views/TaskMenuViewWithArrow.kt
+++ b/quickstep/src/com/android/quickstep/views/TaskMenuViewWithArrow.kt
@@ -75,6 +75,7 @@
clipToOutline = true
shouldScaleArrow = true
+ mIsArrowRotated = true
// This synchronizes the arrow and menu to open at the same time
OPEN_CHILD_FADE_START_DELAY = OPEN_FADE_START_DELAY
OPEN_CHILD_FADE_DURATION = OPEN_FADE_DURATION
diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsTaskbar.java b/quickstep/tests/src/com/android/quickstep/TaplTestsTaskbar.java
index aed26d3..735c5e6 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplTestsTaskbar.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplTestsTaskbar.java
@@ -92,6 +92,7 @@
@TaskbarModeSwitch(mode = TRANSIENT)
public void testTransientLaunchApp() throws Exception {
getTaskbar().getAppIcon(TEST_APP_NAME).launch(TEST_APP_PACKAGE);
+ mLauncher.getLaunchedAppState().assertTaskbarHidden();
}
@Test
@@ -140,6 +141,7 @@
public void testTransientLaunchAppInSplitscreen() throws Exception {
getTaskbar().getAppIcon(TEST_APP_NAME).dragToSplitscreen(
TEST_APP_PACKAGE, CALCULATOR_APP_PACKAGE);
+ mLauncher.getLaunchedAppState().assertTaskbarHidden();
}
@Test
diff --git a/quickstep/tests/src/com/android/quickstep/util/SplitSelectStateControllerTest.kt b/quickstep/tests/src/com/android/quickstep/util/SplitSelectStateControllerTest.kt
new file mode 100644
index 0000000..9db0368
--- /dev/null
+++ b/quickstep/tests/src/com/android/quickstep/util/SplitSelectStateControllerTest.kt
@@ -0,0 +1,212 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.quickstep.util
+
+import android.app.ActivityManager
+import android.content.ComponentName
+import android.content.Context
+import android.content.Intent
+import android.graphics.Rect
+import android.os.Handler
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.launcher3.LauncherState
+import com.android.launcher3.logging.StatsLogManager
+import com.android.launcher3.model.data.ItemInfo
+import com.android.launcher3.statehandlers.DepthController
+import com.android.launcher3.statemanager.StateManager
+import com.android.launcher3.util.SplitConfigurationOptions
+import com.android.launcher3.util.withArgCaptor
+import com.android.quickstep.RecentsModel
+import com.android.quickstep.SystemUiProxy
+import com.android.systemui.shared.recents.model.Task
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertNull
+import org.junit.Assert.assertTrue
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+import java.util.ArrayList
+import java.util.function.Consumer
+
+
+@RunWith(AndroidJUnit4::class)
+class SplitSelectStateControllerTest {
+
+ @Mock lateinit var systemUiProxy: SystemUiProxy
+ @Mock lateinit var depthController: DepthController
+ @Mock lateinit var statsLogManager: StatsLogManager
+ @Mock lateinit var stateManager: StateManager<LauncherState>
+ @Mock lateinit var handler: Handler
+ @Mock lateinit var context: Context
+ @Mock lateinit var recentsModel: RecentsModel
+
+ lateinit var splitSelectStateController: SplitSelectStateController
+
+ @Before
+ fun setup() {
+ MockitoAnnotations.initMocks(this)
+ splitSelectStateController = SplitSelectStateController(context, handler,
+ stateManager, depthController, statsLogManager, systemUiProxy, recentsModel)
+ }
+
+ @Test
+ fun activeTasks_noMatchingTasks() {
+ val groupTask1 = generateGroupTask(
+ ComponentName("pomegranate", "juice"),
+ ComponentName("pumpkin", "pie"))
+ val groupTask2 = generateGroupTask(
+ ComponentName("hotdog", "juice"),
+ ComponentName("personal", "computer"))
+ val tasks: ArrayList<GroupTask> = ArrayList()
+ tasks.add(groupTask1)
+ tasks.add(groupTask2)
+
+ // Assertions happen in the callback we get from what we pass into
+ // #findLastActiveTaskAndRunCallback
+ val taskConsumer = Consumer<Task> {
+ assertNull("No tasks should have matched", it /*task*/)
+ }
+
+ // Capture callback from recentsModel#getTasks()
+ val consumer = withArgCaptor<Consumer<ArrayList<GroupTask>>> {
+ splitSelectStateController.findLastActiveTaskAndRunCallback(
+ ComponentName("no", "match"), taskConsumer)
+ verify(recentsModel).getTasks(capture())
+ }
+
+ // Send our mocked tasks
+ consumer.accept(tasks)
+ }
+
+ @Test
+ fun activeTasks_singleMatchingTask() {
+ val matchingPackage = "hotdog"
+ val matchingClass = "juice"
+ val groupTask1 = generateGroupTask(
+ ComponentName(matchingPackage, matchingClass),
+ ComponentName("pomegranate", "juice"))
+ val groupTask2 = generateGroupTask(
+ ComponentName("pumpkin", "pie"),
+ ComponentName("personal", "computer"))
+ val tasks: ArrayList<GroupTask> = ArrayList()
+ tasks.add(groupTask1)
+ tasks.add(groupTask2)
+
+ // Assertions happen in the callback we get from what we pass into
+ // #findLastActiveTaskAndRunCallback
+ val taskConsumer = Consumer<Task> {
+ assertEquals("ComponentName package mismatched",
+ it.key.baseIntent.component.packageName, matchingPackage)
+ assertEquals("ComponentName class mismatched",
+ it.key.baseIntent.component.className, matchingClass)
+ assertEquals(it, groupTask1.task1)
+ }
+
+ // Capture callback from recentsModel#getTasks()
+ val consumer = withArgCaptor<Consumer<ArrayList<GroupTask>>> {
+ splitSelectStateController.findLastActiveTaskAndRunCallback(
+ ComponentName(matchingPackage, matchingClass), taskConsumer)
+ verify(recentsModel).getTasks(capture())
+ }
+
+ // Send our mocked tasks
+ consumer.accept(tasks)
+ }
+
+ @Test
+ fun activeTasks_multipleMatchMostRecentTask() {
+ val matchingPackage = "hotdog"
+ val matchingClass = "juice"
+ val groupTask1 = generateGroupTask(
+ ComponentName(matchingPackage, matchingClass),
+ ComponentName("pumpkin", "pie"))
+ val groupTask2 = generateGroupTask(
+ ComponentName("pomegranate", "juice"),
+ ComponentName(matchingPackage, matchingClass))
+ val tasks: ArrayList<GroupTask> = ArrayList()
+ tasks.add(groupTask2)
+ tasks.add(groupTask1)
+
+ // Assertions happen in the callback we get from what we pass into
+ // #findLastActiveTaskAndRunCallback
+ val taskConsumer = Consumer<Task> {
+ assertEquals("ComponentName package mismatched",
+ it.key.baseIntent.component.packageName, matchingPackage)
+ assertEquals("ComponentName class mismatched",
+ it.key.baseIntent.component.className, matchingClass)
+ assertEquals(it, groupTask2.task2)
+ }
+
+ // Capture callback from recentsModel#getTasks()
+ val consumer = withArgCaptor<Consumer<ArrayList<GroupTask>>> {
+ splitSelectStateController.findLastActiveTaskAndRunCallback(
+ ComponentName(matchingPackage, matchingClass), taskConsumer)
+ verify(recentsModel).getTasks(capture())
+ }
+
+ // Send our mocked tasks
+ consumer.accept(tasks)
+ }
+
+ @Test
+ fun setInitialApp_withTaskId() {
+ splitSelectStateController.setInitialTaskSelect(null /*intent*/,
+ -1 /*stagePosition*/, ItemInfo(), null /*splitEvent*/, 10 /*alreadyRunningTask*/)
+ assertTrue(splitSelectStateController.isSplitSelectActive)
+ }
+
+ @Test
+ fun setInitialApp_withIntent() {
+ splitSelectStateController.setInitialTaskSelect(Intent() /*intent*/,
+ -1 /*stagePosition*/, ItemInfo(), null /*splitEvent*/, -1 /*alreadyRunningTask*/)
+ assertTrue(splitSelectStateController.isSplitSelectActive)
+ }
+
+ @Test
+ fun resetAfterInitial() {
+ splitSelectStateController.setInitialTaskSelect(Intent() /*intent*/,
+ -1 /*stagePosition*/, ItemInfo(), null /*splitEvent*/,
+ -1)
+ splitSelectStateController.resetState()
+ assertFalse(splitSelectStateController.isSplitSelectActive)
+ }
+
+ private fun generateGroupTask(task1ComponentName: ComponentName,
+ task2ComponentName: ComponentName): GroupTask {
+ val task1 = Task()
+ var taskInfo = ActivityManager.RunningTaskInfo()
+ var intent = Intent()
+ intent.component = task1ComponentName
+ taskInfo.baseIntent = intent
+ task1.key = Task.TaskKey(taskInfo)
+
+ val task2 = Task()
+ taskInfo = ActivityManager.RunningTaskInfo()
+ intent = Intent()
+ intent.component = task2ComponentName
+ taskInfo.baseIntent = intent
+ task2.key = Task.TaskKey(taskInfo)
+ return GroupTask(task1, task2, SplitConfigurationOptions.SplitBounds(
+ Rect(), Rect(), -1, -1
+ ))
+ }
+}
\ No newline at end of file
diff --git a/res/color/app_subtitle_text_dark.xml b/res/color/app_subtitle_text_dark.xml
new file mode 100644
index 0000000..220d10f
--- /dev/null
+++ b/res/color/app_subtitle_text_dark.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2023 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="#EFF1F2" android:state_expanded="false" />
+ <item android:color="#191C1D" android:state_expanded="true" />
+</selector>
diff --git a/res/color/app_subtitle_text_light.xml b/res/color/app_subtitle_text_light.xml
new file mode 100644
index 0000000..fb00baa
--- /dev/null
+++ b/res/color/app_subtitle_text_light.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2023 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="?android:attr/textColorSecondary"/>
+</selector>
diff --git a/res/color/app_title_text_dark.xml b/res/color/app_title_text_dark.xml
new file mode 100644
index 0000000..220d10f
--- /dev/null
+++ b/res/color/app_title_text_dark.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2023 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="#EFF1F2" android:state_expanded="false" />
+ <item android:color="#191C1D" android:state_expanded="true" />
+</selector>
diff --git a/res/color/app_title_text_light.xml b/res/color/app_title_text_light.xml
new file mode 100644
index 0000000..bb52973
--- /dev/null
+++ b/res/color/app_title_text_light.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2023 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="?android:attr/textColorPrimary"/>
+</selector>
diff --git a/res/drawable/bg_widgets_content.xml b/res/drawable/bg_widgets_content.xml
index 8060430..b0b699b 100644
--- a/res/drawable/bg_widgets_content.xml
+++ b/res/drawable/bg_widgets_content.xml
@@ -18,16 +18,32 @@
<!--
L -> large radius
s -> small radius
- 0 -> no radiuls
+ 0 -> no radius
-->
+ <!-- SINGLE : L L L L -->
+ <item android:state_single="true">
+ <shape android:shape="rectangle">
+ <solid android:color="@color/surface" />
+ <corners android:radius="@dimen/widget_list_top_bottom_corner_radius"/>
+ </shape>
+ </item>
+
+ <!-- FIRST : 0 0 s s -->
+ <item android:state_first="true">
+ <shape android:shape="rectangle">
+ <solid android:color="@color/surface" />
+ <corners
+ android:bottomLeftRadius="@dimen/widget_list_content_corner_radius"
+ android:bottomRightRadius="@dimen/widget_list_content_corner_radius" />
+ </shape>
+ </item>
+
<!-- MIDDLE : 0 0 s s -->
<item android:state_middle="true">
<shape android:shape="rectangle">
<solid android:color="@color/surface" />
<corners
- android:topLeftRadius="0dp"
- android:topRightRadius="0dp"
android:bottomLeftRadius="@dimen/widget_list_content_corner_radius"
android:bottomRightRadius="@dimen/widget_list_content_corner_radius" />
</shape>
@@ -38,8 +54,6 @@
<shape android:shape="rectangle">
<solid android:color="@color/surface" />
<corners
- android:topLeftRadius="0dp"
- android:topRightRadius="0dp"
android:bottomLeftRadius="@dimen/widget_list_top_bottom_corner_radius"
android:bottomRightRadius="@dimen/widget_list_top_bottom_corner_radius" />
</shape>
diff --git a/res/drawable/bg_widgets_header_large_screen.xml b/res/drawable/bg_widgets_header_large_screen.xml
new file mode 100644
index 0000000..e1408cc
--- /dev/null
+++ b/res/drawable/bg_widgets_header_large_screen.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2023 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<inset xmlns:android="http://schemas.android.com/apk/res/android"
+ android:insetTop="@dimen/widget_list_entry_spacing" >
+ <ripple
+ android:color="@color/accent_ripple_color"
+ android:paddingTop="@dimen/widget_list_header_view_vertical_padding"
+ android:paddingBottom="@dimen/widget_list_header_view_vertical_padding" >
+ <item android:id="@android:id/mask"
+ android:drawable="@drawable/bg_widgets_header_states_large_screen" />
+ <item android:drawable="@drawable/bg_widgets_header_states_large_screen" />
+ </ripple>
+</inset>
diff --git a/res/drawable/bg_widgets_header_states_large_screen.xml b/res/drawable/bg_widgets_header_states_large_screen.xml
new file mode 100644
index 0000000..1ee5fe5
--- /dev/null
+++ b/res/drawable/bg_widgets_header_states_large_screen.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2023 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:state_expanded="true">
+ <shape android:shape="rectangle">
+ <solid android:color="@color/widget_picker_background_selected" />
+ <corners android:radius="@dimen/widget_list_top_bottom_corner_radius" />
+ </shape>
+ </item>
+
+ <item android:state_expanded="false">
+ <shape android:shape="rectangle">
+ <solid android:color="@android:color/transparent" />
+ <corners android:radius="@dimen/widget_list_top_bottom_corner_radius" />
+ </shape>
+ </item>
+</selector>
diff --git a/res/layout/widgets_full_sheet_large_screen.xml b/res/layout/widgets_full_sheet_large_screen.xml
index 1c0037d..b99ac5c 100644
--- a/res/layout/widgets_full_sheet_large_screen.xml
+++ b/res/layout/widgets_full_sheet_large_screen.xml
@@ -31,23 +31,12 @@
android:id="@+id/recycler_view_container"
android:layout_width="0dp"
android:layout_height="0dp"
- app:layout_constraintEnd_toStartOf="@id/right_pane"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toBottomOf="@id/title"
app:layout_constraintWidth_percent="0.33">
<TextView
- android:id="@+id/no_widgets_text"
- style="@style/PrimaryHeadline"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:gravity="center"
- android:textSize="18sp"
- android:visibility="gone"
- tools:text="No widgets available" />
-
- <TextView
android:id="@+id/fast_scroller_popup"
style="@style/FastScrollerPopup"
android:layout_marginEnd="@dimen/fastscroll_popup_margin" />
@@ -68,28 +57,42 @@
android:visibility="gone" />
</FrameLayout>
- <ScrollView
- android:id="@+id/right_pane"
+ <FrameLayout
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/recycler_view_container"
app:layout_constraintTop_toBottomOf="@id/title"
- app:layout_constraintBottom_toBottomOf="parent"
android:paddingEnd="16dp"
android:paddingStart="8dp"
android:layout_marginTop="26dp"
- app:layout_constraintWidth_percent="0.67">
-
- <com.android.launcher3.widget.picker.WidgetsRecommendationTableLayout
- android:id="@+id/recommended_widget_table"
+ app:layout_constraintWidth_percent="0.67"
+ app:layout_constraintBottom_toBottomOf="parent"
+ android:orientation="horizontal">
+ <TextView
+ android:id="@+id/no_widgets_text"
+ style="@style/PrimaryHeadline"
android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:background="@drawable/widgets_surface_background"
- android:paddingHorizontal="@dimen/widget_list_horizontal_margin_large_screen"
- android:paddingVertical="@dimen/recommended_widgets_table_vertical_padding"
- android:visibility="gone" />
- </ScrollView>
+ android:layout_height="match_parent"
+ android:gravity="center"
+ android:textSize="18sp"
+ android:visibility="gone"
+ tools:text="No widgets available" />
+ <ScrollView
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:id="@+id/right_pane">
+ <com.android.launcher3.widget.picker.WidgetsRecommendationTableLayout
+ android:id="@+id/recommended_widget_table"
+ android:background="@drawable/widgets_surface_background"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingHorizontal=
+ "@dimen/widget_list_horizontal_margin_large_screen"
+ android:paddingVertical="@dimen/recommended_widgets_table_vertical_padding"
+ android:visibility="gone" />
+ </ScrollView>
+ </FrameLayout>
<View
android:id="@+id/collapse_handle"
diff --git a/res/layout/widgets_list_row_header_two_pane.xml b/res/layout/widgets_list_row_header_two_pane.xml
new file mode 100644
index 0000000..6465db5
--- /dev/null
+++ b/res/layout/widgets_list_row_header_two_pane.xml
@@ -0,0 +1,69 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<com.android.launcher3.widget.picker.WidgetsListHeader xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ xmlns:launcher="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/widgets_list_header"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:importantForAccessibility="yes"
+ android:focusable="true"
+ launcher:appIconSize="48dp"
+ android:descendantFocusability="afterDescendants"
+ android:background="@drawable/bg_widgets_header_large_screen" >
+
+ <ImageView
+ android:id="@+id/app_icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginHorizontal="16dp"
+ android:importantForAccessibility="no"
+ tools:src="@drawable/ic_corp"/>
+
+ <LinearLayout
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:layout_weight="1"
+ android:orientation="vertical"
+ android:duplicateParentState="true">
+
+ <TextView
+ android:id="@+id/app_title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="start|center_vertical"
+ android:ellipsize="end"
+ android:maxLines="1"
+ android:textColor="?attr/widgetPickerHeaderAppTitleColor"
+ android:textSize="16sp"
+ android:duplicateParentState="true"
+ tools:text="App name" />
+
+ <TextView
+ android:id="@+id/app_subtitle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:ellipsize="end"
+ android:maxLines="1"
+ android:textColor="?attr/widgetPickerHeaderAppSubtitleColor"
+ android:alpha="0.7"
+ android:duplicateParentState="true"
+ tools:text="m widgets, n shortcuts" />
+
+ </LinearLayout>
+</com.android.launcher3.widget.picker.WidgetsListHeader>
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index c3bd90e..f70937a 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -55,6 +55,8 @@
<attr name="dropTargetHoverTextColor" format="color" />
<attr name="preloadIconAccentColor" format="color" />
<attr name="preloadIconBackgroundColor" format="color" />
+ <attr name="widgetPickerHeaderAppTitleColor" format="color"/>
+ <attr name="widgetPickerHeaderAppSubtitleColor" format="color"/>
<!-- BubbleTextView specific attributes. -->
<declare-styleable name="BubbleTextView">
diff --git a/res/values/styles.xml b/res/values/styles.xml
index a0b5570..474a289 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -176,10 +176,14 @@
<item name="android:colorPrimaryDark">#E8EAED</item>
<item name="android:textColorSecondary">?android:attr/textColorPrimary</item>
<item name="android:colorEdgeEffect">?android:attr/textColorSecondary</item>
+ <item name="widgetPickerHeaderAppTitleColor">@color/app_title_text_light</item>
+ <item name="widgetPickerHeaderAppSubtitleColor">@color/app_subtitle_text_light</item>
</style>
<style name="WidgetContainerTheme.Dark" parent="AppTheme.Dark">
<item name="android:colorEdgeEffect">?android:attr/textColorSecondary</item>
<item name="android:colorPrimaryDark">#616161</item> <!-- Gray 700 -->
+ <item name="widgetPickerHeaderAppTitleColor">@color/app_title_text_dark</item>
+ <item name="widgetPickerHeaderAppSubtitleColor">@color/app_subtitle_text_dark</item>
</style>
<style name="FastScrollerPopup" parent="@android:style/TextAppearance.DeviceDefault.DialogWindowTitle">
diff --git a/src/com/android/launcher3/Hotseat.java b/src/com/android/launcher3/Hotseat.java
index bf492a9..03afba1 100644
--- a/src/com/android/launcher3/Hotseat.java
+++ b/src/com/android/launcher3/Hotseat.java
@@ -27,10 +27,6 @@
import android.view.ViewGroup;
import android.widget.FrameLayout;
-import androidx.annotation.Nullable;
-
-import java.util.function.Consumer;
-
/**
* View class that represents the bottom row of the home screen.
*/
@@ -43,8 +39,6 @@
private boolean mHasVerticalHotseat;
private Workspace<?> mWorkspace;
private boolean mSendTouchToWorkspace;
- @Nullable
- private Consumer<Boolean> mOnVisibilityAggregatedCallback;
private final View mQsb;
@@ -151,20 +145,6 @@
}
@Override
- public void onVisibilityAggregated(boolean isVisible) {
- super.onVisibilityAggregated(isVisible);
-
- if (mOnVisibilityAggregatedCallback != null) {
- mOnVisibilityAggregatedCallback.accept(isVisible);
- }
- }
-
- /** Sets a callback to be called onVisibilityAggregated */
- public void setOnVisibilityAggregatedCallback(@Nullable Consumer<Boolean> callback) {
- mOnVisibilityAggregatedCallback = callback;
- }
-
- @Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
diff --git a/src/com/android/launcher3/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java
index 604c1b8..4ac7f07 100644
--- a/src/com/android/launcher3/InvariantDeviceProfile.java
+++ b/src/com/android/launcher3/InvariantDeviceProfile.java
@@ -16,8 +16,8 @@
package com.android.launcher3;
+import static com.android.launcher3.LauncherPrefs.GRID_NAME;
import static com.android.launcher3.Utilities.dpiFromPx;
-import static com.android.launcher3.config.FeatureFlags.ENABLE_DEVICE_PROFILE_LOGGING;
import static com.android.launcher3.config.FeatureFlags.ENABLE_TWO_PANEL_HOME;
import static com.android.launcher3.testing.shared.ResourceUtils.INVALID_RESOURCE_HANDLE;
import static com.android.launcher3.util.DisplayController.CHANGE_DENSITY;
@@ -69,8 +69,6 @@
import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
-import java.io.PrintWriter;
-import java.io.StringWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
@@ -93,8 +91,6 @@
public static final int TYPE_MULTI_DISPLAY = 1;
public static final int TYPE_TABLET = 2;
- private static final String KEY_IDP_GRID_NAME = "idp_grid_name";
-
private static final float ICON_SIZE_DEFINED_IN_APP_DP = 48;
// Constants that affects the interpolation curve between statically defined device profile
@@ -207,8 +203,7 @@
String gridName = getCurrentGridName(context);
String newGridName = initGrid(context, gridName);
if (!newGridName.equals(gridName)) {
- LauncherPrefs.getPrefs(context).edit().putString(KEY_IDP_GRID_NAME, newGridName)
- .apply();
+ LauncherPrefs.get(context).put(GRID_NAME, newGridName);
}
new DeviceGridState(this).writeToPrefs(context);
@@ -316,7 +311,7 @@
}
public static String getCurrentGridName(Context context) {
- return LauncherPrefs.getPrefs(context).getString(KEY_IDP_GRID_NAME, null);
+ return LauncherPrefs.get(context).get(GRID_NAME);
}
private String initGrid(Context context, String gridName) {
@@ -458,9 +453,8 @@
public void setCurrentGrid(Context context, String gridName) {
- Context appContext = context.getApplicationContext();
- LauncherPrefs.getPrefs(appContext).edit().putString(KEY_IDP_GRID_NAME, gridName).apply();
- MAIN_EXECUTOR.execute(() -> onConfigChanged(appContext));
+ LauncherPrefs.get(context).put(GRID_NAME, gridName);
+ MAIN_EXECUTOR.execute(() -> onConfigChanged(context.getApplicationContext()));
}
private Object[] toModelState() {
@@ -683,18 +677,6 @@
float screenHeight = config.screenHeightDp * res.getDisplayMetrics().density;
int rotation = WindowManagerProxy.INSTANCE.get(context).getRotation(context);
- if (Utilities.IS_DEBUG_DEVICE && ENABLE_DEVICE_PROFILE_LOGGING.get()) {
- StringWriter stringWriter = new StringWriter();
- PrintWriter printWriter = new PrintWriter(stringWriter);
- DisplayController.INSTANCE.get(context).dump(printWriter);
- printWriter.flush();
- Log.d("b/253338238", "getDeviceProfile -"
- + "\nconfig: " + config
- + "\ndisplayMetrics: " + res.getDisplayMetrics()
- + "\nrotation: " + rotation
- + "\n" + stringWriter,
- new Exception());
- }
return getBestMatch(screenWidth, screenHeight, rotation);
}
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 7dffd33..de60d05 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -19,7 +19,6 @@
import static android.app.PendingIntent.FLAG_IMMUTABLE;
import static android.app.PendingIntent.FLAG_UPDATE_CURRENT;
import static android.content.pm.ActivityInfo.CONFIG_UI_MODE;
-import static android.view.View.IMPORTANT_FOR_ACCESSIBILITY_NO;
import static android.view.accessibility.AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED;
import static com.android.launcher3.AbstractFloatingView.TYPE_ALL;
@@ -86,7 +85,6 @@
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.database.sqlite.SQLiteDatabase;
-import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.Rect;
import android.graphics.RectF;
@@ -117,7 +115,6 @@
import android.view.WindowManager.LayoutParams;
import android.view.accessibility.AccessibilityEvent;
import android.view.animation.OvershootInterpolator;
-import android.widget.ImageView;
import android.widget.Toast;
import androidx.annotation.CallSuper;
@@ -151,7 +148,6 @@
import com.android.launcher3.folder.Folder;
import com.android.launcher3.folder.FolderGridOrganizer;
import com.android.launcher3.folder.FolderIcon;
-import com.android.launcher3.icons.BitmapRenderer;
import com.android.launcher3.icons.IconCache;
import com.android.launcher3.keyboard.ViewGroupFocusHelper;
import com.android.launcher3.logger.LauncherAtom;
@@ -306,8 +302,6 @@
private static final int NEW_APPS_ANIMATION_INACTIVE_TIMEOUT_SECONDS = 5;
@Thunk @VisibleForTesting public static final int NEW_APPS_ANIMATION_DELAY = 500;
- private static final int THEME_CROSS_FADE_ANIMATION_DURATION = 375;
-
private static final String DISPLAY_WORKSPACE_TRACE_METHOD_NAME = "DisplayWorkspaceFirstFrame";
private static final String DISPLAY_ALL_APPS_TRACE_METHOD_NAME = "DisplayAllApps";
public static final int DISPLAY_WORKSPACE_TRACE_COOKIE = 0;
@@ -504,7 +498,6 @@
mAppWidgetHolder.startListening();
setupViews();
- crossFadeWithPreviousAppearance();
mPopupDataProvider = new PopupDataProvider(this::updateNotificationDots);
boolean internalStateHandled = ACTIVITY_TRACKER.handleCreate(this);
@@ -1579,16 +1572,6 @@
public Object onRetainNonConfigurationInstance() {
NonConfigInstance instance = new NonConfigInstance();
instance.config = new Configuration(mOldConfig);
-
- int width = mDragLayer.getWidth();
- int height = mDragLayer.getHeight();
-
- if (FeatureFlags.ENABLE_LAUNCHER_ACTIVITY_THEME_CROSSFADE.get()
- && width > 0
- && height > 0) {
- instance.snapshot =
- BitmapRenderer.createHardwareBitmap(width, height, mDragLayer::draw);
- }
return instance;
}
@@ -3294,41 +3277,6 @@
return (T) activityContext;
}
- /**
- * Cross-fades the launcher's updated appearance with its previous appearance.
- *
- * This method is used to cross-fade UI updates on activity creation, specifically dark mode
- * updates.
- */
- private void crossFadeWithPreviousAppearance() {
- NonConfigInstance lastInstance = (NonConfigInstance) getLastNonConfigurationInstance();
-
- if (lastInstance == null || lastInstance.snapshot == null) {
- return;
- }
-
- ImageView crossFadeHelper = new ImageView(this);
- crossFadeHelper.setImageBitmap(lastInstance.snapshot);
- crossFadeHelper.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO);
-
- InsettableFrameLayout.LayoutParams layoutParams = new InsettableFrameLayout.LayoutParams(
- InsettableFrameLayout.LayoutParams.MATCH_PARENT,
- InsettableFrameLayout.LayoutParams.MATCH_PARENT);
-
- layoutParams.ignoreInsets = true;
-
- crossFadeHelper.setLayoutParams(layoutParams);
-
- getRootView().addView(crossFadeHelper);
-
- crossFadeHelper
- .animate()
- .setDuration(THEME_CROSS_FADE_ANIMATION_DURATION)
- .alpha(0f)
- .withEndAction(() -> getRootView().removeView(crossFadeHelper))
- .start();
- }
-
public boolean supportsAdaptiveIconAnimation(View clickedView) {
return false;
}
@@ -3360,7 +3308,6 @@
private static class NonConfigInstance {
public Configuration config;
- public Bitmap snapshot;
}
@Override
diff --git a/src/com/android/launcher3/LauncherPrefs.kt b/src/com/android/launcher3/LauncherPrefs.kt
index 2e07e30..e5a1879 100644
--- a/src/com/android/launcher3/LauncherPrefs.kt
+++ b/src/com/android/launcher3/LauncherPrefs.kt
@@ -4,6 +4,8 @@
import android.content.SharedPreferences
import android.content.SharedPreferences.OnSharedPreferenceChangeListener
import androidx.annotation.VisibleForTesting
+import com.android.launcher3.LauncherFiles.DEVICE_PREFERENCES_KEY
+import com.android.launcher3.LauncherFiles.SHARED_PREFERENCES_KEY
import com.android.launcher3.allapps.WorkProfileManager
import com.android.launcher3.model.DeviceGridState
import com.android.launcher3.pm.InstallSessionHelper
@@ -20,11 +22,10 @@
class LauncherPrefs(private val context: Context) {
/** Wrapper around `getInner` for a `ContextualItem` */
- fun <T : Any> get(item: ContextualItem<T>): T =
- getInner(item, item.defaultValueFromContext(context))
+ fun <T> get(item: ContextualItem<T>): T = getInner(item, item.defaultValueFromContext(context))
/** Wrapper around `getInner` for an `Item` */
- fun <T : Any> get(item: ConstantItem<T>): T = getInner(item, item.defaultValue)
+ fun <T> get(item: ConstantItem<T>): T = getInner(item, item.defaultValue)
/**
* Retrieves the value for an [Item] from [SharedPreferences]. It handles method typing via the
@@ -32,11 +33,11 @@
* `String`, `Boolean`, `Float`, `Int`, `Long`, or `Set<String>`.
*/
@Suppress("IMPLICIT_CAST_TO_ANY", "UNCHECKED_CAST")
- private fun <T : Any> getInner(item: Item, default: T): T {
+ private fun <T> getInner(item: Item, default: T): T {
val sp = context.getSharedPreferences(item.sharedPrefFile, Context.MODE_PRIVATE)
- return when (default::class.java) {
- String::class.java -> sp.getString(item.sharedPrefKey, default as String)
+ return when (item.type) {
+ String::class.java -> sp.getString(item.sharedPrefKey, default as? String)
Boolean::class.java,
java.lang.Boolean::class.java -> sp.getBoolean(item.sharedPrefKey, default as Boolean)
Int::class.java,
@@ -45,11 +46,10 @@
java.lang.Float::class.java -> sp.getFloat(item.sharedPrefKey, default as Float)
Long::class.java,
java.lang.Long::class.java -> sp.getLong(item.sharedPrefKey, default as Long)
- Set::class.java -> sp.getStringSet(item.sharedPrefKey, default as Set<String>)
+ Set::class.java -> sp.getStringSet(item.sharedPrefKey, default as? Set<String>)
else ->
throw IllegalArgumentException(
- "item type: ${default::class.java}" +
- " is not compatible with sharedPref methods"
+ "item type: ${item.type}" + " is not compatible with sharedPref methods"
)
}
as T
@@ -224,39 +224,36 @@
backedUpItem(RestoreDbTask.RESTORED_DEVICE_TYPE, InvariantDeviceProfile.TYPE_PHONE)
@JvmField val APP_WIDGET_IDS = backedUpItem(RestoreDbTask.APPWIDGET_IDS, "")
@JvmField val OLD_APP_WIDGET_IDS = backedUpItem(RestoreDbTask.APPWIDGET_OLD_IDS, "")
+ @JvmField val GRID_NAME = ConstantItem("idp_grid_name", true, null, String::class.java)
@JvmField
val ALLOW_ROTATION =
- backedUpItem(RotationHelper.ALLOW_ROTATION_PREFERENCE_KEY) {
+ backedUpItem(RotationHelper.ALLOW_ROTATION_PREFERENCE_KEY, Boolean::class.java) {
RotationHelper.getAllowRotationDefaultValue(DisplayController.INSTANCE.get(it).info)
}
@VisibleForTesting
@JvmStatic
fun <T> backedUpItem(sharedPrefKey: String, defaultValue: T): ConstantItem<T> =
- ConstantItem(sharedPrefKey, LauncherFiles.SHARED_PREFERENCES_KEY, defaultValue)
+ ConstantItem(sharedPrefKey, true, defaultValue)
@JvmStatic
fun <T> backedUpItem(
sharedPrefKey: String,
+ type: Class<out T>,
defaultValueFromContext: (c: Context) -> T
- ): ContextualItem<T> =
- ContextualItem(
- sharedPrefKey,
- LauncherFiles.SHARED_PREFERENCES_KEY,
- defaultValueFromContext
- )
+ ): ContextualItem<T> = ContextualItem(sharedPrefKey, true, defaultValueFromContext, type)
@VisibleForTesting
@JvmStatic
fun <T> nonRestorableItem(sharedPrefKey: String, defaultValue: T): ConstantItem<T> =
- ConstantItem(sharedPrefKey, LauncherFiles.DEVICE_PREFERENCES_KEY, defaultValue)
+ ConstantItem(sharedPrefKey, false, defaultValue)
@Deprecated("Don't use shared preferences directly. Use other LauncherPref methods.")
@JvmStatic
fun getPrefs(context: Context): SharedPreferences {
// Use application context for shared preferences, so we use single cached instance
return context.applicationContext.getSharedPreferences(
- LauncherFiles.SHARED_PREFERENCES_KEY,
+ SHARED_PREFERENCES_KEY,
Context.MODE_PRIVATE
)
}
@@ -266,7 +263,7 @@
fun getDevicePrefs(context: Context): SharedPreferences {
// Use application context for shared preferences, so we use a single cached instance
return context.applicationContext.getSharedPreferences(
- LauncherFiles.DEVICE_PREFERENCES_KEY,
+ DEVICE_PREFERENCES_KEY,
Context.MODE_PRIVATE
)
}
@@ -275,21 +272,27 @@
abstract class Item {
abstract val sharedPrefKey: String
- abstract val sharedPrefFile: String
+ abstract val isBackedUp: Boolean
+ abstract val type: Class<*>
+ val sharedPrefFile: String
+ get() = if (isBackedUp) SHARED_PREFERENCES_KEY else DEVICE_PREFERENCES_KEY
fun <T> to(value: T): Pair<Item, T> = Pair(this, value)
}
data class ConstantItem<T>(
override val sharedPrefKey: String,
- override val sharedPrefFile: String,
- val defaultValue: T
+ override val isBackedUp: Boolean,
+ val defaultValue: T,
+ // The default value can be null. If so, the type needs to be explicitly stated, or else NPE
+ override val type: Class<out T> = defaultValue!!::class.java
) : Item()
data class ContextualItem<T>(
override val sharedPrefKey: String,
- override val sharedPrefFile: String,
- private val defaultSupplier: (c: Context) -> T
+ override val isBackedUp: Boolean,
+ private val defaultSupplier: (c: Context) -> T,
+ override val type: Class<out T>
) : Item() {
private var default: T? = null
diff --git a/src/com/android/launcher3/MultipageCellLayout.java b/src/com/android/launcher3/MultipageCellLayout.java
index a2c5255..0d59848 100644
--- a/src/com/android/launcher3/MultipageCellLayout.java
+++ b/src/com/android/launcher3/MultipageCellLayout.java
@@ -69,17 +69,14 @@
@Override
boolean createAreaForResize(int cellX, int cellY, int spanX, int spanY, View dragView,
int[] direction, boolean commit) {
+ // Add seam to x position
+ if (cellX > mCountX / 2) {
+ cellX++;
+ }
+ int finalCellX = cellX;
return simulateSeam(
- () -> super.createAreaForResize(cellX, cellY, spanX, spanY, dragView, direction,
- commit));
- }
-
- @Override
- void regionToCenterPoint(int cellX, int cellY, int spanX, int spanY, int[] result) {
- simulateSeam(() -> {
- super.regionToCenterPoint(cellX, cellY, spanX, spanY, result);
- return 0;
- });
+ () -> super.createAreaForResize(finalCellX, cellY, spanX, spanY, dragView,
+ direction, commit));
}
@Override
diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index 0ec036c..cf5cf4e 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -65,98 +65,109 @@
* Declare a new ToggleableFlag below. Give it a unique key (e.g. "QSB_ON_FIRST_SCREEN"),
* and set a default value for the flag. This will be the default value on Debug builds.
*/
+ // b/270390028
public static final BooleanFlag ENABLE_INPUT_CONSUMER_REASON_LOGGING = getDebugFlag(
"ENABLE_INPUT_CONSUMER_REASON_LOGGING",
true,
"Log the reason why an Input Consumer was selected for a gesture.");
+ // b/270389990
public static final BooleanFlag ENABLE_GESTURE_ERROR_DETECTION = getDebugFlag(
"ENABLE_GESTURE_ERROR_DETECTION",
true,
"Analyze gesture events and log detected errors");
// When enabled the promise icon is visible in all apps while installation an app.
+ // b/270390012
public static final BooleanFlag PROMISE_APPS_IN_ALL_APPS = getDebugFlag(
"PROMISE_APPS_IN_ALL_APPS", false, "Add promise icon in all-apps");
+ // b/270390904
public static final BooleanFlag KEYGUARD_ANIMATION = getDebugFlag(
"KEYGUARD_ANIMATION", false, "Enable animation for keyguard going away on wallpaper");
+ // b/270390907
public static final BooleanFlag ENABLE_DEVICE_SEARCH = new DeviceFlag(
"ENABLE_DEVICE_SEARCH", true, "Allows on device search in all apps");
+ // b/270390286
public static final BooleanFlag ENABLE_FLOATING_SEARCH_BAR =
getDebugFlag("ENABLE_FLOATING_SEARCH_BAR", false,
"Keep All Apps search bar at the bottom (but above keyboard if open)");
+ // b/270390930
public static final BooleanFlag ENABLE_HIDE_HEADER = new DeviceFlag("ENABLE_HIDE_HEADER",
true, "Hide header on keyboard before typing in all apps");
+ // b/270390779
public static final BooleanFlag ENABLE_EXPANDING_PAUSE_WORK_BUTTON = new DeviceFlag(
"ENABLE_EXPANDING_PAUSE_WORK_BUTTON", false,
"Expand and collapse pause work button while scrolling");
+ // b/270390950
public static final BooleanFlag ENABLE_RECENT_BLOCK = getDebugFlag("ENABLE_RECENT_BLOCK",
false, "Show recently tapped search target block in zero state");
+ // b/270391455
public static final BooleanFlag COLLECT_SEARCH_HISTORY = new DeviceFlag(
"COLLECT_SEARCH_HISTORY", false, "Allow launcher to collect search history for log");
+ // b/270390937
public static final BooleanFlag ENABLE_TWOLINE_ALLAPPS = getDebugFlag(
"ENABLE_TWOLINE_ALLAPPS", false, "Enables two line label inside all apps.");
+ // b/270391397
public static final BooleanFlag ENABLE_DEVICE_SEARCH_PERFORMANCE_LOGGING = new DeviceFlag(
"ENABLE_DEVICE_SEARCH_PERFORMANCE_LOGGING", false,
"Allows on device search in all apps logging");
+ // b/270391693
public static final BooleanFlag IME_STICKY_SNACKBAR_EDU = getDebugFlag(
"IME_STICKY_SNACKBAR_EDU", true, "Show sticky IME edu in AllApps");
+ // b/270391653
public static final BooleanFlag ENABLE_PEOPLE_TILE_PREVIEW = getDebugFlag(
"ENABLE_PEOPLE_TILE_PREVIEW", false,
"Experimental: Shows conversation shortcuts on home screen as search results");
+ // b/270391638
public static final BooleanFlag FOLDER_NAME_MAJORITY_RANKING = getDebugFlag(
"FOLDER_NAME_MAJORITY_RANKING", true,
"Suggests folder names based on majority based ranking.");
+ // b/270391706
public static final BooleanFlag INJECT_FALLBACK_APP_CORPUS_RESULTS = new DeviceFlag(
"INJECT_FALLBACK_APP_CORPUS_RESULTS", false, "Inject "
+ "fallback app corpus result when AiAi fails to return it.");
+ // b/270391641
public static final BooleanFlag ASSISTANT_GIVES_LAUNCHER_FOCUS = getDebugFlag(
"ASSISTANT_GIVES_LAUNCHER_FOCUS", false,
"Allow Launcher to handle nav bar gestures while Assistant is running over it");
+ // b/270392203
public static final BooleanFlag ENABLE_BULK_WORKSPACE_ICON_LOADING = getDebugFlag(
"ENABLE_BULK_WORKSPACE_ICON_LOADING",
true,
"Enable loading workspace icons in bulk.");
+ // b/270392465
public static final BooleanFlag ENABLE_BULK_ALL_APPS_ICON_LOADING = getDebugFlag(
"ENABLE_BULK_ALL_APPS_ICON_LOADING",
true,
"Enable loading all apps icons in bulk.");
- // Keep as DeviceFlag for remote disable in emergency.
- public static final BooleanFlag ENABLE_OVERVIEW_SELECTIONS = new DeviceFlag(
- "ENABLE_OVERVIEW_SELECTIONS", true, "Show Select Mode button in Overview Actions");
-
- public static final BooleanFlag ENABLE_WIDGETS_PICKER_AIAI_SEARCH = new DeviceFlag(
- "ENABLE_WIDGETS_PICKER_AIAI_SEARCH", true, "Enable AiAi search in the widgets picker");
-
- public static final BooleanFlag ENABLE_OVERVIEW_SHARING_TO_PEOPLE = getDebugFlag(
- "ENABLE_OVERVIEW_SHARING_TO_PEOPLE", true,
- "Show indicators for content on Overview to share with top people. ");
-
+ // b/270392706
public static final BooleanFlag ENABLE_DATABASE_RESTORE = getDebugFlag(
"ENABLE_DATABASE_RESTORE", false,
"Enable database restore when new restore session is created");
+ // b/270391664
public static final BooleanFlag ENABLE_SMARTSPACE_DISMISS = getDebugFlag(
"ENABLE_SMARTSPACE_DISMISS", true,
"Adds a menu option to dismiss the current Enhanced Smartspace card.");
+ // b/270392629
public static final BooleanFlag ENABLE_OVERLAY_CONNECTION_OPTIM = getDebugFlag(
"ENABLE_OVERLAY_CONNECTION_OPTIM",
false,
@@ -165,251 +176,296 @@
/**
* Enables region sampling for text color: Needs system health assessment before turning on
*/
+ // b/270391669
public static final BooleanFlag ENABLE_REGION_SAMPLING = getDebugFlag(
"ENABLE_REGION_SAMPLING", false,
"Enable region sampling to determine color of text on screen.");
+ // b/270393096
public static final BooleanFlag ALWAYS_USE_HARDWARE_OPTIMIZATION_FOR_FOLDER_ANIMATIONS =
getDebugFlag(
"ALWAYS_USE_HARDWARE_OPTIMIZATION_FOR_FOLDER_ANIMATIONS", false,
"Always use hardware optimization for folder animations.");
+ // b/270392980
public static final BooleanFlag SEPARATE_RECENTS_ACTIVITY = getDebugFlag(
"SEPARATE_RECENTS_ACTIVITY", false,
"Uses a separate recents activity instead of using the integrated recents+Launcher UI");
+ // b/270392984
public static final BooleanFlag ENABLE_MINIMAL_DEVICE = getDebugFlag(
"ENABLE_MINIMAL_DEVICE", false,
"Allow user to toggle minimal device mode in launcher.");
- // TODO: b/172467144 Remove ENABLE_LAUNCHER_ACTIVITY_THEME_CROSSFADE feature flag.
- public static final BooleanFlag ENABLE_LAUNCHER_ACTIVITY_THEME_CROSSFADE = new DeviceFlag(
- "ENABLE_LAUNCHER_ACTIVITY_THEME_CROSSFADE", false, "Enables a "
- + "crossfade animation when the system these changes.");
-
- // TODO: b/174174514 Remove ENABLE_APP_PREDICTIONS_WHILE_VISIBLE feature flag.
- public static final BooleanFlag ENABLE_APP_PREDICTIONS_WHILE_VISIBLE = new DeviceFlag(
- "ENABLE_APP_PREDICTIONS_WHILE_VISIBLE", true, "Allows app "
- + "predictions to be updated while they are visible to the user.");
-
+ // b/270392477
public static final BooleanFlag ENABLE_TASKBAR_POPUP_MENU = getDebugFlag(
"ENABLE_TASKBAR_POPUP_MENU", true, "Enables long pressing taskbar icons to show the"
+ " popup menu.");
+ // b/270392643
public static final BooleanFlag ENABLE_TWO_PANEL_HOME = getDebugFlag(
"ENABLE_TWO_PANEL_HOME", true,
"Uses two panel on home screen. Only applicable on large screen devices.");
+ // b/270393276
public static final BooleanFlag ENABLE_SCRIM_FOR_APP_LAUNCH = getDebugFlag(
"ENABLE_SCRIM_FOR_APP_LAUNCH", false,
"Enables scrim during app launch animation.");
+ // b/270393258
public static final BooleanFlag ENABLE_ENFORCED_ROUNDED_CORNERS = new DeviceFlag(
"ENABLE_ENFORCED_ROUNDED_CORNERS", true, "Enforce rounded corners on all App Widgets");
+ // b/270393108
public static final BooleanFlag NOTIFY_CRASHES = getDebugFlag("NOTIFY_CRASHES", false,
"Sends a notification whenever launcher encounters an uncaught exception.");
+ // b/270393604
public static final BooleanFlag ENABLE_WALLPAPER_SCRIM = getDebugFlag(
"ENABLE_WALLPAPER_SCRIM", false,
"Enables scrim over wallpaper for text protection.");
+ // b/270393268
public static final BooleanFlag WIDGETS_IN_LAUNCHER_PREVIEW = getDebugFlag(
"WIDGETS_IN_LAUNCHER_PREVIEW", true,
"Enables widgets in Launcher preview for the Wallpaper app.");
+ // b/270393112
public static final BooleanFlag QUICK_WALLPAPER_PICKER = getDebugFlag(
"QUICK_WALLPAPER_PICKER", true,
"Shows quick wallpaper picker in long-press menu");
+ // b/270393426
public static final BooleanFlag ENABLE_BACK_SWIPE_HOME_ANIMATION = getDebugFlag(
"ENABLE_BACK_SWIPE_HOME_ANIMATION", true,
"Enables home animation to icon when user swipes back.");
+ // b/270393294
public static final BooleanFlag ENABLE_ICON_LABEL_AUTO_SCALING = getDebugFlag(
"ENABLE_ICON_LABEL_AUTO_SCALING", true,
"Enables scaling/spacing for icon labels to make more characters visible");
+ // b/270393897
public static final BooleanFlag ENABLE_ALL_APPS_BUTTON_IN_HOTSEAT = getDebugFlag(
"ENABLE_ALL_APPS_BUTTON_IN_HOTSEAT", false,
"Enables displaying the all apps button in the hotseat.");
+ // b/270393900
public static final BooleanFlag ENABLE_ALL_APPS_ONE_SEARCH_IN_TASKBAR = getDebugFlag(
"ENABLE_ALL_APPS_ONE_SEARCH_IN_TASKBAR", false,
"Enables One Search box in Taskbar All Apps.");
+ // b/270393449
public static final BooleanFlag ENABLE_TASKBAR_IN_OVERVIEW = getDebugFlag(
"ENABLE_TASKBAR_IN_OVERVIEW", true,
"Enables accessing the system Taskbar in overview.");
+ // b/270393906
public static final BooleanFlag ENABLE_SPLIT_FROM_WORKSPACE = getDebugFlag(
"ENABLE_SPLIT_FROM_WORKSPACE", true,
"Enable initiating split screen from workspace.");
+ // b/270394122
public static final BooleanFlag ENABLE_SPLIT_FROM_FULLSCREEN_WITH_KEYBOARD_SHORTCUTS =
getDebugFlag("ENABLE_SPLIT_FROM_FULLSCREEN_SHORTCUT", false,
"Enable splitting from fullscreen app with keyboard shortcuts");
+ // b/270393453
public static final BooleanFlag ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE = getDebugFlag(
"ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE", false,
"Enable initiating split screen from workspace to workspace.");
+ // b/270393455
public static final BooleanFlag ENABLE_NEW_MIGRATION_LOGIC = getDebugFlag(
"ENABLE_NEW_MIGRATION_LOGIC", true,
"Enable the new grid migration logic, keeping pages when src < dest");
+ // b/270394384
public static final BooleanFlag ENABLE_WIDGET_HOST_IN_BACKGROUND = getDebugFlag(
"ENABLE_WIDGET_HOST_IN_BACKGROUND", false,
"Enable background widget updates listening for widget holder");
+ // b/270394223
public static final BooleanFlag ENABLE_ONE_SEARCH_MOTION = new DeviceFlag(
"ENABLE_ONE_SEARCH_MOTION", true, "Enables animations in OneSearch.");
+ // b/270394041
public static final BooleanFlag ENABLE_SEARCH_RESULT_BACKGROUND_DRAWABLES = new DeviceFlag(
"ENABLE_SEARCH_RESULT_BACKGROUND_DRAWABLES", false,
"Enable option to replace decorator-based search result backgrounds with drawables");
+ // b/270394392
public static final BooleanFlag ENABLE_SEARCH_RESULT_LAUNCH_TRANSITION = new DeviceFlag(
"ENABLE_SEARCH_RESULT_LAUNCH_TRANSITION", false,
"Enable option to launch search results using the new view container transitions");
+ // b/270394225
public static final BooleanFlag TWO_PREDICTED_ROWS_ALL_APPS_SEARCH = new DeviceFlag(
"TWO_PREDICTED_ROWS_ALL_APPS_SEARCH", false,
"Use 2 rows of app predictions in All Apps search zero-state");
+ // b/270394468
public static final BooleanFlag ENABLE_SHOW_KEYBOARD_OPTION_IN_ALL_APPS = new DeviceFlag(
"ENABLE_SHOW_KEYBOARD_OPTION_IN_ALL_APPS", true,
"Enable option to show keyboard when going to all-apps");
+ // b/270394973
public static final BooleanFlag USE_LOCAL_ICON_OVERRIDES = getDebugFlag(
"USE_LOCAL_ICON_OVERRIDES", true,
"Use inbuilt monochrome icons if app doesn't provide one");
+ // b/270394476
public static final BooleanFlag ENABLE_DISMISS_PREDICTION_UNDO = getDebugFlag(
"ENABLE_DISMISS_PREDICTION_UNDO", false,
"Show an 'Undo' snackbar when users dismiss a predicted hotseat item");
+ // b/270395008
public static final BooleanFlag ENABLE_CACHED_WIDGET = getDebugFlag(
"ENABLE_CACHED_WIDGET", true,
"Show previously cached widgets as opposed to deferred widget where available");
+ // b/270395010
public static final BooleanFlag USE_SEARCH_REQUEST_TIMEOUT_OVERRIDES = getDebugFlag(
"USE_SEARCH_REQUEST_TIMEOUT_OVERRIDES", false,
"Use local overrides for search request timeout");
+ // b/270395171
public static final BooleanFlag CONTINUOUS_VIEW_TREE_CAPTURE = getDebugFlag(
"CONTINUOUS_VIEW_TREE_CAPTURE", false, "Capture View tree every frame");
+ // b/270395070
public static final BooleanFlag FOLDABLE_WORKSPACE_REORDER = getDebugFlag(
"FOLDABLE_WORKSPACE_REORDER", true,
"In foldables, when reordering the icons and widgets, is now going to use both sides");
- public static final BooleanFlag ENABLE_WIDGET_PICKER_DEPTH = new DeviceFlag(
- "ENABLE_WIDGET_PICKER_DEPTH", true, "Enable changing depth in widget picker.");
-
+ // b/270395073
public static final BooleanFlag ENABLE_MULTI_DISPLAY_PARTIAL_DEPTH = getDebugFlag(
"ENABLE_MULTI_DISPLAY_PARTIAL_DEPTH", false,
"Allow bottom sheet depth to be smaller than 1 for multi-display devices.");
+ // b/270395177
public static final BooleanFlag SCROLL_TOP_TO_RESET = new DeviceFlag(
"SCROLL_TOP_TO_RESET", true, "Bring up IME and focus on "
+ "input when scroll to top if 'Always show keyboard' is enabled or in prefix state");
+ // b/270395516
public static final BooleanFlag ENABLE_MATERIAL_U_POPUP = getDebugFlag(
"ENABLE_MATERIAL_U_POPUP", false, "Switch popup UX to use material U");
+ // b/270395269
public static final BooleanFlag ENABLE_SEARCH_UNINSTALLED_APPS = new DeviceFlag(
"ENABLE_SEARCH_UNINSTALLED_APPS", false, "Search uninstalled app results.");
+ // b/270395183
public static final BooleanFlag SHOW_HOME_GARDENING = getDebugFlag(
"SHOW_HOME_GARDENING", false,
"Show the new home gardening mode");
+ // b/270395133
public static final BooleanFlag HOME_GARDENING_WORKSPACE_BUTTONS = getDebugFlag(
"HOME_GARDENING_WORKSPACE_BUTTONS", false,
"Change workspace edit buttons to reflect home gardening");
+ // b/270395134
public static final BooleanFlag ENABLE_DOWNLOAD_APP_UX_V2 = new DeviceFlag(
"ENABLE_DOWNLOAD_APP_UX_V2", true, "Updates the download app UX"
+ " to have better visuals");
+ // b/270395186
public static final BooleanFlag ENABLE_DOWNLOAD_APP_UX_V3 = getDebugFlag(
"ENABLE_DOWNLOAD_APP_UX_V3", false, "Updates the download app UX"
+ " to have better visuals, improve contrast, and color");
+ // b/270395077
public static final BooleanFlag FORCE_PERSISTENT_TASKBAR = getDebugFlag(
"FORCE_PERSISTENT_TASKBAR", false, "Forces taskbar to be persistent, even in gesture"
+ " nav mode and when transient taskbar is enabled.");
+ // b/270395274
public static final BooleanFlag FOLDABLE_SINGLE_PAGE = getDebugFlag(
"FOLDABLE_SINGLE_PAGE", false,
"Use a single page for the workspace");
+ // b/270395798
public static final BooleanFlag ENABLE_TRANSIENT_TASKBAR = getDebugFlag(
"ENABLE_TRANSIENT_TASKBAR", true, "Enables transient taskbar.");
+ // b/270395140
public static final BooleanFlag SECONDARY_DRAG_N_DROP_TO_PIN = getDebugFlag(
"SECONDARY_DRAG_N_DROP_TO_PIN", false,
"Enable dragging and dropping to pin apps within secondary display");
+ // b/270395143
public static final BooleanFlag ENABLE_ICON_IN_TEXT_HEADER = getDebugFlag(
"ENABLE_ICON_IN_TEXT_HEADER", false, "Show icon in textheader");
+ // b/270395087
public static final BooleanFlag ENABLE_APP_ICON_FOR_INLINE_SHORTCUTS = getDebugFlag(
"ENABLE_APP_ICON_IN_INLINE_SHORTCUTS", false, "Show app icon for inline shortcut");
+ // b/270395278
public static final BooleanFlag SHOW_DOT_PAGINATION = getDebugFlag(
"SHOW_DOT_PAGINATION", false, "Enable showing dot pagination in workspace");
+ // b/270395809
public static final BooleanFlag LARGE_SCREEN_WIDGET_PICKER = getDebugFlag(
"LARGE_SCREEN_WIDGET_PICKER", false, "Enable new widget picker that takes "
+ "advantage of large screen format");
+ // b/270396257
public static final BooleanFlag ENABLE_NEW_GESTURE_NAV_TUTORIAL = getDebugFlag(
"ENABLE_NEW_GESTURE_NAV_TUTORIAL", false,
"Enable the redesigned gesture navigation tutorial");
- public static final BooleanFlag ENABLE_DEVICE_PROFILE_LOGGING = new DeviceFlag(
- "ENABLE_DEVICE_PROFILE_LOGGING", false, "Allows DeviceProfile logging");
-
+ // b/270395567
public static final BooleanFlag ENABLE_LAUNCH_FROM_STAGED_APP = getDebugFlag(
"ENABLE_LAUNCH_FROM_STAGED_APP", true,
"Enable the ability to tap a staged app during split select to launch it in full screen"
);
+
+ // b/270396358
public static final BooleanFlag ENABLE_HAPTICS_ALL_APPS = getDebugFlag(
"ENABLE_HAPTICS_ALL_APPS", false, "Enables haptics opening/closing All apps");
+ // b/270396209
public static final BooleanFlag ENABLE_FORCED_MONO_ICON = getDebugFlag(
"ENABLE_FORCED_MONO_ICON", false,
"Enable the ability to generate monochromatic icons, if it is not provided by the app"
);
+ // b/270396364
public static final BooleanFlag ENABLE_DREAM_TRANSITION = getDebugFlag(
"ENABLE_DREAM_TRANSITION", true,
"Enable the launcher transition when the device enters a dream");
+ // b/270396268
public static final BooleanFlag ENABLE_TASKBAR_EDU_TOOLTIP = getDebugFlag(
"ENABLE_TASKBAR_EDU_TOOLTIP", true,
"Enable the tooltip version of the Taskbar education flow.");
+ // b/270396680
public static final BooleanFlag ENABLE_MULTI_INSTANCE = getDebugFlag(
"ENABLE_MULTI_INSTANCE", false,
"Enables creation and filtering of multiple task instances in overview");
+ // b/270396583
public static final BooleanFlag ENABLE_TASKBAR_PINNING = getDebugFlag(
"ENABLE_TASKBAR_PINNING", false,
"Enables taskbar pinning to allow user to switch between transient and persistent "
+ "taskbar flavors");
+ // b/270397206
public static final BooleanFlag ENABLE_GRID_ONLY_OVERVIEW = getDebugFlag(
"ENABLE_GRID_ONLY_OVERVIEW", false,
"Enable a grid-only overview without a focused task.");
+ // b/270397209
public static final BooleanFlag RECEIVE_UNFOLD_EVENTS_FROM_SYSUI = getDebugFlag(
"RECEIVE_UNFOLD_EVENTS_FROM_SYSUI", true,
"Enables receiving unfold animation events from sysui instead of calculating "
+ "them in launcher process using hinge sensor values.");
+ // b/270396844
public static final BooleanFlag ENABLE_KEYBOARD_QUICK_SWITCH = getDebugFlag(
"ENABLE_KEYBOARD_QUICK_SWITCH", false,
"Enables keyboard quick switching");
diff --git a/src/com/android/launcher3/popup/ArrowPopup.java b/src/com/android/launcher3/popup/ArrowPopup.java
index c23ea8a..a0f21dc 100644
--- a/src/com/android/launcher3/popup/ArrowPopup.java
+++ b/src/com/android/launcher3/popup/ArrowPopup.java
@@ -20,6 +20,8 @@
import static com.android.launcher3.anim.Interpolators.ACCELERATED_EASE;
import static com.android.launcher3.anim.Interpolators.DECELERATED_EASE;
+import static com.android.launcher3.anim.Interpolators.EMPHASIZED_ACCELERATE;
+import static com.android.launcher3.anim.Interpolators.EMPHASIZED_DECELERATE;
import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.config.FeatureFlags.ENABLE_MATERIAL_U_POPUP;
@@ -37,11 +39,13 @@
import android.graphics.drawable.GradientDrawable;
import android.util.AttributeSet;
import android.util.Pair;
+import android.util.Property;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.Interpolator;
+import android.view.animation.PathInterpolator;
import android.widget.FrameLayout;
import androidx.annotation.Nullable;
@@ -81,6 +85,19 @@
protected int CLOSE_CHILD_FADE_START_DELAY = 0;
protected int CLOSE_CHILD_FADE_DURATION = 140;
+ private static final int OPEN_DURATION_U = 200;
+ private static final int OPEN_FADE_START_DELAY_U = 0;
+ private static final int OPEN_FADE_DURATION_U = 83;
+ private static final int OPEN_CHILD_FADE_START_DELAY_U = 0;
+ private static final int OPEN_CHILD_FADE_DURATION_U = 83;
+ private static final int OPEN_OVERSHOOT_DURATION_U = 200;
+
+ private static final int CLOSE_DURATION_U = 233;
+ private static final int CLOSE_FADE_START_DELAY_U = 150;
+ private static final int CLOSE_FADE_DURATION_U = 83;
+ private static final int CLOSE_CHILD_FADE_START_DELAY_U = 150;
+ private static final int CLOSE_CHILD_FADE_DURATION_U = 83;
+
protected final Rect mTempRect = new Rect();
protected final LayoutInflater mInflater;
@@ -104,6 +121,7 @@
protected AnimatorSet mOpenCloseAnimator;
protected boolean mDeferContainerRemoval;
protected boolean shouldScaleArrow = false;
+ protected boolean mIsArrowRotated = false;
private final GradientDrawable mRoundedTop;
private final GradientDrawable mRoundedBottom;
@@ -591,10 +609,24 @@
protected void animateOpen() {
setVisibility(View.VISIBLE);
+ mOpenCloseAnimator = ENABLE_MATERIAL_U_POPUP.get()
+ ? getMaterialUOpenCloseAnimator(
+ true,
+ OPEN_DURATION_U,
+ OPEN_FADE_START_DELAY_U,
+ OPEN_FADE_DURATION_U,
+ OPEN_CHILD_FADE_START_DELAY_U,
+ OPEN_CHILD_FADE_DURATION_U,
+ EMPHASIZED_DECELERATE)
+ : getOpenCloseAnimator(
+ true,
+ OPEN_DURATION,
+ OPEN_FADE_START_DELAY,
+ OPEN_FADE_DURATION,
+ OPEN_CHILD_FADE_START_DELAY,
+ OPEN_CHILD_FADE_DURATION,
+ DECELERATED_EASE);
- mOpenCloseAnimator = getOpenCloseAnimator(true, OPEN_DURATION, OPEN_FADE_START_DELAY,
- OPEN_FADE_DURATION, OPEN_CHILD_FADE_START_DELAY, OPEN_CHILD_FADE_DURATION,
- DECELERATED_EASE);
onCreateOpenAnimation(mOpenCloseAnimator);
mOpenCloseAnimator.addListener(new AnimatorListenerAdapter() {
@Override
@@ -680,6 +712,24 @@
mOpenCloseAnimator = getOpenCloseAnimator(false, CLOSE_DURATION, CLOSE_FADE_START_DELAY,
CLOSE_FADE_DURATION, CLOSE_CHILD_FADE_START_DELAY, CLOSE_CHILD_FADE_DURATION,
ACCELERATED_EASE);
+
+ mOpenCloseAnimator = ENABLE_MATERIAL_U_POPUP.get()
+ ? getMaterialUOpenCloseAnimator(
+ false,
+ CLOSE_DURATION_U,
+ CLOSE_FADE_START_DELAY_U,
+ CLOSE_FADE_DURATION_U,
+ CLOSE_CHILD_FADE_START_DELAY_U,
+ CLOSE_CHILD_FADE_DURATION_U,
+ EMPHASIZED_ACCELERATE)
+ : getOpenCloseAnimator(false,
+ CLOSE_DURATION,
+ CLOSE_FADE_START_DELAY,
+ CLOSE_FADE_DURATION,
+ CLOSE_CHILD_FADE_START_DELAY,
+ CLOSE_CHILD_FADE_DURATION,
+ ACCELERATED_EASE);
+
onCreateCloseAnimation(mOpenCloseAnimator);
mOpenCloseAnimator.addListener(new AnimatorListenerAdapter() {
@Override
@@ -695,6 +745,59 @@
mOpenCloseAnimator.start();
}
+ protected AnimatorSet getMaterialUOpenCloseAnimator(boolean isOpening, int scaleDuration,
+ int fadeStartDelay, int fadeDuration, int childFadeStartDelay, int childFadeDuration,
+ Interpolator interpolator) {
+
+ int arrowCenter = mArrowOffsetHorizontal + mArrowWidth / 2;
+ if (mIsArrowRotated) {
+ setPivotX(mIsLeftAligned ? 0f : getMeasuredWidth());
+ setPivotY(arrowCenter);
+ } else {
+ setPivotX(mIsLeftAligned ? arrowCenter : getMeasuredWidth() - arrowCenter);
+ setPivotY(mIsAboveIcon ? getMeasuredHeight() : 0f);
+ }
+
+ float[] alphaValues = isOpening ? new float[] {0, 1} : new float[] {1, 0};
+ float[] scaleValues = isOpening ? new float[] {0.5f, 1.02f} : new float[] {1f, 0.5f};
+ Animator alpha = getAnimatorOfFloat(this, View.ALPHA, fadeDuration, fadeStartDelay,
+ LINEAR, alphaValues);
+ Animator arrowAlpha = getAnimatorOfFloat(mArrow, View.ALPHA, fadeDuration, fadeStartDelay,
+ LINEAR, alphaValues);
+ Animator scaleY = getAnimatorOfFloat(this, View.SCALE_Y, scaleDuration, 0, interpolator,
+ scaleValues);
+ Animator scaleX = getAnimatorOfFloat(this, View.SCALE_X, scaleDuration, 0, interpolator,
+ scaleValues);
+
+ final AnimatorSet animatorSet = new AnimatorSet();
+ if (isOpening) {
+ float[] scaleValuesOvershoot = new float[] {1.02f, 1f};
+ PathInterpolator overshootInterpolator = new PathInterpolator(0.3f, 0, 0.33f, 1f);
+ Animator overshootY = getAnimatorOfFloat(this, View.SCALE_Y,
+ OPEN_OVERSHOOT_DURATION_U, scaleDuration, overshootInterpolator,
+ scaleValuesOvershoot);
+ Animator overshootX = getAnimatorOfFloat(this, View.SCALE_X,
+ OPEN_OVERSHOOT_DURATION_U, scaleDuration, overshootInterpolator,
+ scaleValuesOvershoot);
+
+ animatorSet.playTogether(alpha, arrowAlpha, scaleY, scaleX, overshootX, overshootY);
+ } else {
+ animatorSet.playTogether(alpha, arrowAlpha, scaleY, scaleX);
+ }
+
+ fadeInChildViews(this, alphaValues, childFadeStartDelay, childFadeDuration, animatorSet);
+ return animatorSet;
+ }
+
+ private Animator getAnimatorOfFloat(View view, Property<View, Float> property,
+ int duration, int startDelay, Interpolator interpolator, float... values) {
+ Animator animator = ObjectAnimator.ofFloat(view, property, values);
+ animator.setDuration(duration);
+ animator.setInterpolator(interpolator);
+ animator.setStartDelay(startDelay);
+ return animator;
+ }
+
/**
* Called when creating the open transition allowing subclass can add additional animations.
*/
diff --git a/src/com/android/launcher3/util/LockedUserState.kt b/src/com/android/launcher3/util/LockedUserState.kt
new file mode 100644
index 0000000..7b49583
--- /dev/null
+++ b/src/com/android/launcher3/util/LockedUserState.kt
@@ -0,0 +1,57 @@
+package com.android.launcher3.util
+
+import android.content.Context
+import android.content.Intent
+import android.os.Process
+import android.os.UserManager
+import androidx.annotation.VisibleForTesting
+
+class LockedUserState(private val mContext: Context) : SafeCloseable {
+ var isUserUnlocked: Boolean
+ private set
+ private val mUserUnlockedActions: RunnableList = RunnableList()
+
+ @VisibleForTesting
+ val mUserUnlockedReceiver = SimpleBroadcastReceiver {
+ if (Intent.ACTION_USER_UNLOCKED == it.action) {
+ isUserUnlocked = true
+ notifyUserUnlocked()
+ }
+ }
+
+ init {
+ isUserUnlocked =
+ mContext
+ .getSystemService(UserManager::class.java)!!
+ .isUserUnlocked(Process.myUserHandle())
+ if (isUserUnlocked) {
+ notifyUserUnlocked()
+ } else {
+ mUserUnlockedReceiver.register(mContext, Intent.ACTION_USER_UNLOCKED)
+ }
+ }
+
+ private fun notifyUserUnlocked() {
+ mUserUnlockedActions.executeAllAndDestroy()
+ mUserUnlockedReceiver.unregisterReceiverSafely(mContext)
+ }
+
+ /** Stops the receiver from listening for ACTION_USER_UNLOCK broadcasts. */
+ override fun close() {
+ mUserUnlockedReceiver.unregisterReceiverSafely(mContext)
+ }
+
+ /**
+ * Adds a `Runnable` to be executed when a user is unlocked. If the user is already unlocked,
+ * this runnable will run immediately because RunnableList will already have been destroyed.
+ */
+ fun runOnUserUnlocked(action: Runnable) {
+ mUserUnlockedActions.add(action)
+ }
+
+ companion object {
+ @VisibleForTesting val INSTANCE = MainThreadInitializedObject { LockedUserState(it) }
+
+ @JvmStatic fun get(context: Context): LockedUserState = INSTANCE.get(context)
+ }
+}
diff --git a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
index d72914b..f8068aa 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
@@ -46,8 +46,8 @@
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
import android.widget.Button;
+import android.widget.FrameLayout;
import android.widget.LinearLayout;
-import android.widget.ScrollView;
import android.widget.TextView;
import androidx.annotation.FloatRange;
@@ -67,6 +67,7 @@
import com.android.launcher3.model.WidgetItem;
import com.android.launcher3.model.data.PackageItemInfo;
import com.android.launcher3.pm.UserCache;
+import com.android.launcher3.recyclerview.ViewHolderBinder;
import com.android.launcher3.util.PackageUserKey;
import com.android.launcher3.views.ArrowTipView;
import com.android.launcher3.views.RecyclerViewFastScroller;
@@ -198,7 +199,7 @@
private View mSearchBarContainer;
private WidgetsSearchBar mSearchBar;
private TextView mHeaderTitle;
- private ScrollView mRightPane;
+ private FrameLayout mRightPane;
private WidgetsListTableViewHolderBinder mWidgetsListTableViewHolderBinder;
private final boolean mIsTwoPane;
@@ -293,7 +294,9 @@
// Inflate the suggestions header.
mSuggestedWidgetsHeader = (WidgetsListHeader) layoutInflater.inflate(
- R.layout.widgets_list_row_header, mSuggestedWidgetsContainer, false);
+ R.layout.widgets_list_row_header_two_pane,
+ mSuggestedWidgetsContainer,
+ false);
mSuggestedWidgetsHeader.setExpanded(true);
PackageItemInfo packageItemInfo = new PackageItemInfo(
@@ -314,8 +317,8 @@
mSuggestedWidgetsHeader.applyFromItemInfoWithIcon(widgetsListHeaderEntry);
mSuggestedWidgetsHeader.setIcon(
getContext().getDrawable(R.drawable.widget_suggestions_icon));
- mSuggestedWidgetsHeader.setOnExpandChangeListener(isExpanded -> {
- mSuggestedWidgetsHeader.setExpanded(isExpanded);
+ mSuggestedWidgetsHeader.setOnClickListener(view -> {
+ mSuggestedWidgetsHeader.setExpanded(true);
resetExpandedHeaders();
mRightPane.removeAllViews();
mRightPane.addView(mRecommendedWidgetsTable);
@@ -389,6 +392,11 @@
private void updateRecyclerViewVisibility(AdapterHolder adapterHolder) {
// The first item is always an empty space entry. Look for any more items.
boolean isWidgetAvailable = adapterHolder.mWidgetsListAdapter.hasVisibleEntries();
+
+ if (mIsTwoPane) {
+ mRightPane.setVisibility(isWidgetAvailable ? VISIBLE : GONE);
+ }
+
adapterHolder.mWidgetsRecyclerView.setVisibility(isWidgetAvailable ? VISIBLE : GONE);
if (adapterHolder.mAdapterType == AdapterHolder.SEARCH) {
@@ -979,11 +987,13 @@
mWidgetsListTableViewHolderBinder.newViewHolder(mRightPane);
mWidgetsListTableViewHolderBinder.bindViewHolder(widgetsRowViewHolder,
contentEntry,
- 0, Collections.EMPTY_LIST);
+ ViewHolderBinder.POSITION_FIRST | ViewHolderBinder.POSITION_LAST,
+ Collections.EMPTY_LIST);
widgetsRowViewHolder.mDataCallback = data -> {
mWidgetsListTableViewHolderBinder.bindViewHolder(widgetsRowViewHolder,
contentEntry,
- 0, Collections.singletonList(data));
+ ViewHolderBinder.POSITION_FIRST | ViewHolderBinder.POSITION_LAST,
+ Collections.singletonList(data));
};
mRightPane.removeAllViews();
mRightPane.addView(widgetsRowViewHolder.itemView);
diff --git a/src/com/android/launcher3/widget/picker/WidgetsListAdapter.java b/src/com/android/launcher3/widget/picker/WidgetsListAdapter.java
index 20b1d9b..c28402e 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsListAdapter.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsListAdapter.java
@@ -115,7 +115,8 @@
mViewHolderBinders.put(
VIEW_TYPE_WIDGETS_HEADER,
new WidgetsListHeaderViewHolderBinder(
- layoutInflater, /* onHeaderClickListener= */ this));
+ layoutInflater, /* onHeaderClickListener= */ this,
+ headerChangeListener != null));
mViewHolderBinders.put(
VIEW_TYPE_WIDGETS_SPACE,
new WidgetsSpaceViewHolderBinder(emptySpaceHeightProvider));
diff --git a/src/com/android/launcher3/widget/picker/WidgetsListDrawableState.java b/src/com/android/launcher3/widget/picker/WidgetsListDrawableState.java
index 94f292b..ca69ffe 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsListDrawableState.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsListDrawableState.java
@@ -21,9 +21,7 @@
*/
enum WidgetsListDrawableState {
FIRST(new int[]{android.R.attr.state_first}),
- FIRST_EXPANDED(new int[]{android.R.attr.state_first, android.R.attr.state_expanded}),
MIDDLE(new int[]{android.R.attr.state_middle}),
- MIDDLE_EXPANDED(new int[]{android.R.attr.state_middle, android.R.attr.state_expanded}),
LAST(new int[]{android.R.attr.state_last}),
SINGLE(new int[]{android.R.attr.state_single});
@@ -33,12 +31,10 @@
mStateSet = stateSet;
}
- static WidgetsListDrawableState obtain(boolean isFirst, boolean isLast, boolean isExpanded) {
+ static WidgetsListDrawableState obtain(boolean isFirst, boolean isLast) {
if (isFirst && isLast) return SINGLE;
- if (isFirst && isExpanded) return FIRST_EXPANDED;
if (isFirst) return FIRST;
if (isLast) return LAST;
- if (isExpanded) return MIDDLE_EXPANDED;
return MIDDLE;
}
}
diff --git a/src/com/android/launcher3/widget/picker/WidgetsListHeader.java b/src/com/android/launcher3/widget/picker/WidgetsListHeader.java
index 547f39e..b5e7401 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsListHeader.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsListHeader.java
@@ -15,17 +15,12 @@
*/
package com.android.launcher3.widget.picker;
-import static com.android.launcher3.config.FeatureFlags.LARGE_SCREEN_WIDGET_PICKER;
-
import android.content.Context;
import android.content.res.TypedArray;
-import android.graphics.Color;
import android.graphics.drawable.Drawable;
-import android.graphics.drawable.GradientDrawable;
import android.os.Bundle;
import android.text.TextUtils;
import android.util.AttributeSet;
-import android.util.TypedValue;
import android.view.View;
import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.ImageView;
@@ -38,13 +33,11 @@
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
import com.android.launcher3.icons.IconCache.ItemInfoUpdateReceiver;
import com.android.launcher3.icons.PlaceHolderIconDrawable;
import com.android.launcher3.icons.cache.HandlerRunnable;
import com.android.launcher3.model.data.ItemInfoWithIcon;
import com.android.launcher3.model.data.PackageItemInfo;
-import com.android.launcher3.util.Themes;
import com.android.launcher3.views.ActivityContext;
import com.android.launcher3.widget.model.WidgetsListHeaderEntry;
@@ -56,8 +49,9 @@
*/
public final class WidgetsListHeader extends LinearLayout implements ItemInfoUpdateReceiver {
+ private static final int[] EXPANDED_DRAWABLE_STATE = new int[] {android.R.attr.state_expanded};
+
private final int mIconSize;
- private final boolean mIsTwoPane;
@Nullable private HandlerRunnable mIconLoadRequest;
@Nullable private Drawable mIconDrawable;
@@ -65,7 +59,6 @@
private ImageView mAppIcon;
private TextView mTitle;
private TextView mSubtitle;
- private GradientDrawable mBackground;
private boolean mEnableIconUpdateAnimation = false;
private boolean mIsExpanded = false;
@@ -86,11 +79,6 @@
R.styleable.WidgetsListRowHeader, defStyleAttr, /* defStyleRes= */ 0);
mIconSize = a.getDimensionPixelSize(R.styleable.WidgetsListRowHeader_appIconSize,
grid.iconSizePx);
-
- mIsTwoPane = grid.isLandscape && grid.isTablet && LARGE_SCREEN_WIDGET_PICKER.get();
- if (mIsTwoPane) {
- setLargeScreenTheme();
- }
}
@Override
@@ -99,9 +87,6 @@
mAppIcon = findViewById(R.id.app_icon);
mTitle = findViewById(R.id.app_title);
mSubtitle = findViewById(R.id.app_subtitle);
- if (mIsTwoPane) {
- findViewById(R.id.toggle).setVisibility(GONE);
- }
setAccessibilityDelegate(new AccessibilityDelegate() {
@Override
@@ -130,58 +115,18 @@
});
}
- /**
- * Sets a {@link OnExpansionChangeListener} to get a callback when this app widgets section
- * expands / collapses.
- */
- @UiThread
- public void setOnExpandChangeListener(
- @Nullable OnExpansionChangeListener onExpandChangeListener) {
- // Use the entire touch area of this view to expand / collapse an app widgets section.
- setOnClickListener(view -> {
- setExpanded(mIsTwoPane || !mIsExpanded);
- if (onExpandChangeListener != null) {
- onExpandChangeListener.onExpansionChange(mIsExpanded);
- }
- });
- }
-
/** Sets the expand toggle to expand / collapse. */
@UiThread
public void setExpanded(boolean isExpanded) {
this.mIsExpanded = isExpanded;
- if (mIsTwoPane) {
- if (Utilities.isDarkTheme(getContext())) {
- if (mIsExpanded) {
- mTitle.setTextColor(Color.BLACK);
- mSubtitle.setTextColor(Color.BLACK);
- } else {
- mTitle.setTextColor(Color.WHITE);
- mSubtitle.setTextColor(Themes.getAttrColor(getContext(),
- android.R.attr.textColorSecondary));
- }
- }
- setLargeScreenTheme();
- }
+ refreshDrawableState();
}
- /**
- * Sets the style for the header when we are using large screens in landscape.
- */
- private void setLargeScreenTheme() {
- if (mBackground == null) {
- mBackground = new GradientDrawable();
- mBackground.setCornerRadius((int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
- 28,
- getContext().getResources().getDisplayMetrics()));
- }
- mBackground.setColor(mIsExpanded
- ? getResources().getColor(R.color.widget_picker_background_selected)
- : Color.TRANSPARENT);
- this.setBackground(mBackground);
+ /** @return true if this header is expanded. */
+ public boolean isExpanded() {
+ return mIsExpanded;
}
-
/** Sets the {@link WidgetsListDrawableState} and refreshes the background drawable. */
@UiThread
public void setListDrawableState(WidgetsListDrawableState state) {
@@ -260,12 +205,15 @@
@Override
protected int[] onCreateDrawableState(int extraSpace) {
- if (mListDrawableState == null) return super.onCreateDrawableState(extraSpace);
- // Augment the state set from the super implementation with the custom states from
- // mListDrawableState.
- int[] drawableState =
- super.onCreateDrawableState(extraSpace + mListDrawableState.mStateSet.length);
- mergeDrawableStates(drawableState, mListDrawableState.mStateSet);
+ // We create a drawable state with an additional two spaces to be able to fit expanded state
+ // and the list drawable state.
+ int[] drawableState = super.onCreateDrawableState(extraSpace + 2);
+ if (mIsExpanded) {
+ mergeDrawableStates(drawableState, EXPANDED_DRAWABLE_STATE);
+ }
+ if (mListDrawableState != null) {
+ mergeDrawableStates(drawableState, mListDrawableState.mStateSet);
+ }
return drawableState;
}
@@ -283,10 +231,4 @@
}
}
}
-
- /** A listener for the widget section expansion / collapse events. */
- public interface OnExpansionChangeListener {
- /** Notifies that the widget section is expanded or collapsed. */
- void onExpansionChange(boolean isExpanded);
- }
}
diff --git a/src/com/android/launcher3/widget/picker/WidgetsListHeaderViewHolderBinder.java b/src/com/android/launcher3/widget/picker/WidgetsListHeaderViewHolderBinder.java
index 27a2b1c..af4a5e6 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsListHeaderViewHolderBinder.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsListHeaderViewHolderBinder.java
@@ -32,17 +32,23 @@
ViewHolderBinder<WidgetsListHeaderEntry, WidgetsListHeaderHolder> {
private final LayoutInflater mLayoutInflater;
private final OnHeaderClickListener mOnHeaderClickListener;
+ private final boolean mIsTwoPane;
public WidgetsListHeaderViewHolderBinder(LayoutInflater layoutInflater,
- OnHeaderClickListener onHeaderClickListener) {
+ OnHeaderClickListener onHeaderClickListener, boolean isTwoPane) {
mLayoutInflater = layoutInflater;
mOnHeaderClickListener = onHeaderClickListener;
+ mIsTwoPane = isTwoPane;
}
@Override
public WidgetsListHeaderHolder newViewHolder(ViewGroup parent) {
return new WidgetsListHeaderHolder((WidgetsListHeader) mLayoutInflater.inflate(
- R.layout.widgets_list_row_header, parent, false));
+ mIsTwoPane
+ ? R.layout.widgets_list_row_header_two_pane
+ : R.layout.widgets_list_row_header,
+ parent,
+ false));
}
@Override
@@ -54,10 +60,11 @@
widgetsListHeader.setListDrawableState(
WidgetsListDrawableState.obtain(
(position & POSITION_FIRST) != 0,
- (position & POSITION_LAST) != 0,
- /* isExpanded= */ data.isWidgetListShown()));
- widgetsListHeader.setOnExpandChangeListener(isExpanded ->
- mOnHeaderClickListener.onHeaderClicked(isExpanded,
- PackageUserKey.fromPackageItemInfo(data.mPkgItem)));
+ (position & POSITION_LAST) != 0));
+ widgetsListHeader.setOnClickListener(view -> {
+ widgetsListHeader.setExpanded(mIsTwoPane || !widgetsListHeader.isExpanded());
+ mOnHeaderClickListener.onHeaderClicked(widgetsListHeader.isExpanded(),
+ PackageUserKey.fromPackageItemInfo(data.mPkgItem));
+ });
}
}
diff --git a/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinder.java b/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinder.java
index 4c1f7a9..2e8f0ab 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinder.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinder.java
@@ -15,9 +15,6 @@
*/
package com.android.launcher3.widget.picker;
-import static com.android.launcher3.widget.picker.WidgetsListDrawableState.LAST;
-import static com.android.launcher3.widget.picker.WidgetsListDrawableState.MIDDLE;
-
import android.graphics.Bitmap;
import android.util.Log;
import android.util.Pair;
@@ -84,7 +81,11 @@
Log.d(TAG, String.format("onBindViewHolder [widget#=%d, table.getChildCount=%d]",
entry.mWidgets.size(), table.getChildCount()));
}
- table.setListDrawableState(((position & POSITION_LAST) != 0) ? LAST : MIDDLE);
+ table.setListDrawableState(
+ WidgetsListDrawableState.obtain(
+ (position & POSITION_FIRST) != 0,
+ (position & POSITION_LAST) != 0));
+
List<ArrayList<WidgetItem>> widgetItemsTable =
WidgetsTableUtils.groupWidgetItemsIntoTableWithReordering(
entry.mWidgets, entry.getMaxSpanSizeInCells());
diff --git a/tests/src/com/android/launcher3/LauncherPrefsTest.kt b/tests/src/com/android/launcher3/LauncherPrefsTest.kt
index 31e8d30..e8372b1 100644
--- a/tests/src/com/android/launcher3/LauncherPrefsTest.kt
+++ b/tests/src/com/android/launcher3/LauncherPrefsTest.kt
@@ -13,7 +13,10 @@
private val TEST_BOOLEAN_ITEM = LauncherPrefs.nonRestorableItem("1", false)
private val TEST_STRING_ITEM = LauncherPrefs.nonRestorableItem("2", "( ͡❛ ͜ʖ ͡❛)")
private val TEST_INT_ITEM = LauncherPrefs.nonRestorableItem("3", -1)
-private val TEST_CONTEXTUAL_ITEM = LauncherPrefs.backedUpItem("4") { true }
+private val TEST_CONTEXTUAL_ITEM = ContextualItem("4", true, { true }, Boolean::class.java)
+
+private const val TEST_DEFAULT_VALUE = "default"
+private const val TEST_PREF_KEY = "test_pref_key"
@SmallTest
@RunWith(AndroidJUnit4::class)
@@ -151,4 +154,16 @@
fun get_contextualItem_returnsCorrectDefault() {
assertThat(launcherPrefs.get(TEST_CONTEXTUAL_ITEM)).isTrue()
}
+
+ @Test
+ fun getItemSharedPrefFile_forNonRestorableItem_isCorrect() {
+ val nonRestorableItem = LauncherPrefs.nonRestorableItem(TEST_PREF_KEY, TEST_DEFAULT_VALUE)
+ assertThat(nonRestorableItem.sharedPrefFile).isEqualTo(LauncherFiles.DEVICE_PREFERENCES_KEY)
+ }
+
+ @Test
+ fun getItemSharedPrefFile_forBackedUpItem_isCorrect() {
+ val backedUpItem = LauncherPrefs.backedUpItem(TEST_PREF_KEY, TEST_DEFAULT_VALUE)
+ assertThat(backedUpItem.sharedPrefFile).isEqualTo(LauncherFiles.SHARED_PREFERENCES_KEY)
+ }
}
diff --git a/tests/src/com/android/launcher3/util/LockedUserStateTest.kt b/tests/src/com/android/launcher3/util/LockedUserStateTest.kt
new file mode 100644
index 0000000..84156e7
--- /dev/null
+++ b/tests/src/com/android/launcher3/util/LockedUserStateTest.kt
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.util
+
+import android.content.Context
+import android.content.Intent
+import android.os.Process
+import android.os.UserManager
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.verifyZeroInteractions
+import org.mockito.Mockito.`when`
+import org.mockito.MockitoAnnotations
+
+/** Unit tests for {@link LockedUserUtil} */
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class LockedUserStateTest {
+
+ @Mock lateinit var userManager: UserManager
+ @Mock lateinit var context: Context
+
+ @Before
+ fun setup() {
+ MockitoAnnotations.initMocks(this)
+ `when`(context.getSystemService(UserManager::class.java)).thenReturn(userManager)
+ }
+
+ @Test
+ fun runOnUserUnlocked_runs_action_immediately_if_already_unlocked() {
+ `when`(userManager.isUserUnlocked(Process.myUserHandle())).thenReturn(true)
+ LockedUserState.INSTANCE.initializeForTesting(LockedUserState(context))
+ val action: Runnable = mock()
+
+ LockedUserState.get(context).runOnUserUnlocked(action)
+ verify(action).run()
+ }
+
+ @Test
+ fun runOnUserUnlocked_waits_to_run_action_until_user_is_unlocked() {
+ `when`(userManager.isUserUnlocked(Process.myUserHandle())).thenReturn(false)
+ LockedUserState.INSTANCE.initializeForTesting(LockedUserState(context))
+ val action: Runnable = mock()
+
+ LockedUserState.get(context).runOnUserUnlocked(action)
+ verifyZeroInteractions(action)
+
+ LockedUserState.get(context)
+ .mUserUnlockedReceiver
+ .onReceive(context, Intent(Intent.ACTION_USER_UNLOCKED))
+
+ verify(action).run()
+ }
+
+ @Test
+ fun isUserUnlocked_returns_true_when_user_is_unlocked() {
+ `when`(userManager.isUserUnlocked(Process.myUserHandle())).thenReturn(true)
+ LockedUserState.INSTANCE.initializeForTesting(LockedUserState(context))
+ assertThat(LockedUserState.get(context).isUserUnlocked).isTrue()
+ }
+
+ @Test
+ fun isUserUnlocked_returns_false_when_user_is_locked() {
+ `when`(userManager.isUserUnlocked(Process.myUserHandle())).thenReturn(false)
+ LockedUserState.INSTANCE.initializeForTesting(LockedUserState(context))
+ assertThat(LockedUserState.get(context).isUserUnlocked).isFalse()
+ }
+}
diff --git a/tests/src/com/android/launcher3/widget/picker/WidgetsListHeaderViewHolderBinderTest.java b/tests/src/com/android/launcher3/widget/picker/WidgetsListHeaderViewHolderBinderTest.java
index 612a4c6..76492ba 100644
--- a/tests/src/com/android/launcher3/widget/picker/WidgetsListHeaderViewHolderBinderTest.java
+++ b/tests/src/com/android/launcher3/widget/picker/WidgetsListHeaderViewHolderBinderTest.java
@@ -90,7 +90,8 @@
}).when(mIconCache).getTitleNoCache(any());
mViewHolderBinder = new WidgetsListHeaderViewHolderBinder(
LayoutInflater.from(mContext),
- mOnHeaderClickListener);
+ mOnHeaderClickListener,
+ false);
}
@Test
diff --git a/tests/tapl/com/android/launcher3/tapl/AllAppsQsb.java b/tests/tapl/com/android/launcher3/tapl/AllAppsQsb.java
new file mode 100644
index 0000000..0931cd4
--- /dev/null
+++ b/tests/tapl/com/android/launcher3/tapl/AllAppsQsb.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2022 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.tapl;
+
+import androidx.test.uiautomator.UiObject2;
+
+/**
+ * Operations on AllApp screen qsb.
+ */
+class AllAppsQsb extends Qsb {
+
+ private final UiObject2 mAllAppsContainer;
+
+ AllAppsQsb(LauncherInstrumentation launcher, UiObject2 allAppsContainer) {
+ super(launcher);
+ mAllAppsContainer = allAppsContainer;
+ waitForQsbObject();
+ }
+
+ @Override
+ protected UiObject2 waitForQsbObject() {
+ return mLauncher.waitForObjectInContainer(mAllAppsContainer, "search_container_all_apps");
+ }
+}
diff --git a/tests/tapl/com/android/launcher3/tapl/HomeAllApps.java b/tests/tapl/com/android/launcher3/tapl/HomeAllApps.java
index e0c4c19..44de65a 100644
--- a/tests/tapl/com/android/launcher3/tapl/HomeAllApps.java
+++ b/tests/tapl/com/android/launcher3/tapl/HomeAllApps.java
@@ -109,4 +109,13 @@
}
}
}
+
+ /**
+ * Return the QSB UI object on the AllApps screen.
+ * @return the QSB UI object.
+ */
+ @NonNull
+ public Qsb getQsb() {
+ return new AllAppsQsb(mLauncher, verifyActiveContainer());
+ }
}
diff --git a/tests/tapl/com/android/launcher3/tapl/HomeQsb.java b/tests/tapl/com/android/launcher3/tapl/HomeQsb.java
index c365708..20d09a1 100644
--- a/tests/tapl/com/android/launcher3/tapl/HomeQsb.java
+++ b/tests/tapl/com/android/launcher3/tapl/HomeQsb.java
@@ -15,69 +15,23 @@
*/
package com.android.launcher3.tapl;
-import androidx.annotation.NonNull;
-import androidx.test.uiautomator.By;
-import androidx.test.uiautomator.BySelector;
import androidx.test.uiautomator.UiObject2;
-import androidx.test.uiautomator.Until;
/**
- * Operations on home screen qsb.
+ * Operations on Home screen qsb.
*/
-public class HomeQsb {
+class HomeQsb extends Qsb {
- private final LauncherInstrumentation mLauncher;
- private static final String ASSISTANT_APP_PACKAGE = "com.google.android.googlequicksearchbox";
- private static final String ASSISTANT_ICON_RES_ID = "mic_icon";
+ private final UiObject2 mHotSeat;
-
- HomeQsb(LauncherInstrumentation launcher) {
- mLauncher = launcher;
- mLauncher.waitForLauncherObject("search_container_hotseat");
+ HomeQsb(LauncherInstrumentation launcher, UiObject2 hotseat) {
+ super(launcher);
+ mHotSeat = hotseat;
+ waitForQsbObject();
}
- /**
- * Launch assistant app by tapping mic icon on qsb.
- */
- @NonNull
- public LaunchedAppState launchAssistant() {
- try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
- "want to click assistant mic icon button");
- LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) {
- UiObject2 assistantIcon = mLauncher.waitForLauncherObject(ASSISTANT_ICON_RES_ID);
-
- LauncherInstrumentation.log("HomeQsb.launchAssistant before click "
- + assistantIcon.getVisibleCenter() + " in "
- + mLauncher.getVisibleBounds(assistantIcon));
-
- mLauncher.clickLauncherObject(assistantIcon);
-
- try (LauncherInstrumentation.Closable c2 = mLauncher.addContextLayer("clicked")) {
- // assert Assistant App Launched
- BySelector selector = By.pkg(ASSISTANT_APP_PACKAGE);
- mLauncher.assertTrue(
- "assistant app didn't start: (" + selector + ")",
- mLauncher.getDevice().wait(Until.hasObject(selector),
- LauncherInstrumentation.WAIT_TIME_MS)
- );
- return new LaunchedAppState(mLauncher);
- }
- }
- }
-
- /**
- * Show search result page from tapping qsb.
- */
- public SearchResultFromQsb showSearchResult() {
- try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
- "want to open search result page");
- LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) {
- mLauncher.clickLauncherObject(
- mLauncher.waitForLauncherObject("search_container_hotseat"));
- try (LauncherInstrumentation.Closable c2 = mLauncher.addContextLayer(
- "clicked qsb to open search result page")) {
- return new SearchResultFromQsb(mLauncher);
- }
- }
+ @Override
+ protected UiObject2 waitForQsbObject() {
+ return mLauncher.waitForObjectInContainer(mHotSeat, "search_container_hotseat");
}
}
diff --git a/tests/tapl/com/android/launcher3/tapl/Qsb.java b/tests/tapl/com/android/launcher3/tapl/Qsb.java
new file mode 100644
index 0000000..6bc4f21
--- /dev/null
+++ b/tests/tapl/com/android/launcher3/tapl/Qsb.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2022 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.tapl;
+
+import androidx.annotation.NonNull;
+import androidx.test.uiautomator.By;
+import androidx.test.uiautomator.BySelector;
+import androidx.test.uiautomator.UiObject2;
+import androidx.test.uiautomator.Until;
+
+/**
+ * Operations on qsb from either Home screen or AllApp screen.
+ */
+public abstract class Qsb {
+
+ private static final String ASSISTANT_APP_PACKAGE = "com.google.android.googlequicksearchbox";
+ private static final String ASSISTANT_ICON_RES_ID = "mic_icon";
+ protected final LauncherInstrumentation mLauncher;
+
+ protected Qsb(LauncherInstrumentation launcher) {
+ mLauncher = launcher;
+ }
+
+ // Waits for the quick search box.
+ protected abstract UiObject2 waitForQsbObject();
+ /**
+ * Launch assistant app by tapping mic icon on qsb.
+ */
+
+ @NonNull
+ public LaunchedAppState launchAssistant() {
+ try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
+ "want to click assistant mic icon button");
+ LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) {
+ UiObject2 assistantIcon = mLauncher.waitForLauncherObject(ASSISTANT_ICON_RES_ID);
+
+ LauncherInstrumentation.log("Qsb.launchAssistant before click "
+ + assistantIcon.getVisibleCenter() + " in "
+ + mLauncher.getVisibleBounds(assistantIcon));
+
+ mLauncher.clickLauncherObject(assistantIcon);
+
+ try (LauncherInstrumentation.Closable c2 = mLauncher.addContextLayer("clicked")) {
+ // assert Assistant App Launched
+ BySelector selector = By.pkg(ASSISTANT_APP_PACKAGE);
+ mLauncher.assertTrue(
+ "assistant app didn't start: (" + selector + ")",
+ mLauncher.getDevice().wait(Until.hasObject(selector),
+ LauncherInstrumentation.WAIT_TIME_MS)
+ );
+ return new LaunchedAppState(mLauncher);
+ }
+ }
+ }
+
+ /**
+ * Show search result page from tapping qsb.
+ */
+ public SearchResultFromQsb showSearchResult() {
+ try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
+ "want to open search result page");
+ LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) {
+ mLauncher.clickLauncherObject(waitForQsbObject());
+ // wait for the result rendering to complete
+ mLauncher.waitForIdle();
+ try (LauncherInstrumentation.Closable c2 = mLauncher.addContextLayer(
+ "clicked qsb to open search result page")) {
+ return new SearchResultFromQsb(mLauncher);
+ }
+ }
+ }
+}
diff --git a/tests/tapl/com/android/launcher3/tapl/SearchResultFromQsb.java b/tests/tapl/com/android/launcher3/tapl/SearchResultFromQsb.java
index 80e4116..80176e9 100644
--- a/tests/tapl/com/android/launcher3/tapl/SearchResultFromQsb.java
+++ b/tests/tapl/com/android/launcher3/tapl/SearchResultFromQsb.java
@@ -23,7 +23,7 @@
import java.util.ArrayList;
/**
- * Operations on search result page opened from home screen qsb.
+ * Operations on search result page opened from qsb.
*/
public class SearchResultFromQsb {
// The input resource id in the search box.
diff --git a/tests/tapl/com/android/launcher3/tapl/Workspace.java b/tests/tapl/com/android/launcher3/tapl/Workspace.java
index 0197a11..1939dc6 100644
--- a/tests/tapl/com/android/launcher3/tapl/Workspace.java
+++ b/tests/tapl/com/android/launcher3/tapl/Workspace.java
@@ -119,10 +119,11 @@
*
* The qsb must already be visible when calling this method.
*/
- public HomeQsb getQsb() {
+ @NonNull
+ public Qsb getQsb() {
try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
"want to get the home qsb")) {
- return new HomeQsb(mLauncher);
+ return new HomeQsb(mLauncher, mHotseat);
}
}