Merge "Prevent meta shortcut from cancelling split select state." into main
diff --git a/OWNERS b/OWNERS
index dd2d00e..654493f 100644
--- a/OWNERS
+++ b/OWNERS
@@ -45,3 +45,6 @@
per-file FeatureFlags.java, globs = set noparent
per-file FeatureFlags.java = sunnygoyal@google.com, winsonc@google.com, adamcohen@google.com, hyunyoungs@google.com, captaincole@google.com
+
+per-file DeviceConfigWrapper.java, globs = set noparent
+per-file DeviceConfigWrapper.java = sunnygoyal@google.com, winsonc@google.com, adamcohen@google.com, hyunyoungs@google.com
diff --git a/quickstep/res/drawable/bg_overview_clear_all_button.xml b/quickstep/res/drawable/bg_overview_clear_all_button.xml
index f3ff6ce..143761f 100644
--- a/quickstep/res/drawable/bg_overview_clear_all_button.xml
+++ b/quickstep/res/drawable/bg_overview_clear_all_button.xml
@@ -20,7 +20,7 @@
<item>
<shape android:shape="rectangle"
android:tint="?colorButtonNormal">
- <corners android:radius="24dp" />
+ <corners android:radius="@dimen/recents_clear_all_outline_radius" />
<solid android:color="?androidprv:attr/materialColorSurfaceBright"/>
</shape>
</item>
diff --git a/quickstep/res/layout/overview_clear_all_button.xml b/quickstep/res/layout/overview_clear_all_button.xml
index da94c3a..3380ea4 100644
--- a/quickstep/res/layout/overview_clear_all_button.xml
+++ b/quickstep/res/layout/overview_clear_all_button.xml
@@ -17,10 +17,12 @@
<com.android.quickstep.views.ClearAllButton
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+ xmlns:launcher="http://schemas.android.com/apk/res-auto"
style="@style/OverviewClearAllButton"
android:id="@+id/clear_all"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/recents_clear_all"
android:textColor="?androidprv:attr/materialColorOnSurface"
+ launcher:focusBorderColor="?androidprv:attr/materialColorOutline"
android:textSize="14sp" />
\ No newline at end of file
diff --git a/quickstep/res/values/attrs.xml b/quickstep/res/values/attrs.xml
index 7288774..ccc7f18 100644
--- a/quickstep/res/values/attrs.xml
+++ b/quickstep/res/values/attrs.xml
@@ -30,6 +30,11 @@
<attr name="hoverBorderColor" format="color" />
</declare-styleable>
+ <declare-styleable name="ClearAllButton">
+ <!-- focus border color for overview clear all button views -->
+ <attr name="focusBorderColor" />
+ </declare-styleable>
+
<!--
Gesture nav edu specific attributes. These attributes are used to customize Gesture nav edu
view lottie animation colors in XML files.
diff --git a/quickstep/res/values/config.xml b/quickstep/res/values/config.xml
index 31d4071..c4ee11a 100644
--- a/quickstep/res/values/config.xml
+++ b/quickstep/res/values/config.xml
@@ -38,6 +38,7 @@
<string name="nav_handle_long_press_handler_class" translatable="false"></string>
<string name="assist_utils_class" translatable="false"></string>
<string name="assist_state_manager_class" translatable="false"></string>
+ <string name="api_wrapper_class" translatable="false">com.android.launcher3.uioverrides.SystemApiWrapper</string>
<!-- The number of thumbnails and icons to keep in the cache. The thumbnail cache size also
determines how many thumbnails will be fetched in the background. -->
diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml
index af1ab99..00dc7cd 100644
--- a/quickstep/res/values/dimens.xml
+++ b/quickstep/res/values/dimens.xml
@@ -98,6 +98,8 @@
<dimen name="default_task_dismiss_drag_velocity_grid_focus_task">5dp</dimen>
<dimen name="recents_clear_all_deadzone_vertical_margin">70dp</dimen>
+ <dimen name="recents_clear_all_outline_radius">24dp</dimen>
+ <dimen name="recents_clear_all_outline_padding">2dp</dimen>
<!-- The speed in dp/s at which the user needs to be scrolling in recents such that we start
loading full resolution screenshots. -->
diff --git a/quickstep/res/values/strings.xml b/quickstep/res/values/strings.xml
index eb9c5f0..71855eb 100644
--- a/quickstep/res/values/strings.xml
+++ b/quickstep/res/values/strings.xml
@@ -26,6 +26,8 @@
<string name="recent_task_option_pin">Pin</string>
<!-- Title for an option to enter freeform mode for a given app -->
<string name="recent_task_option_freeform">Freeform</string>
+ <!-- Title for an option to enter desktop windowing mode for a given app -->
+ <string name="recent_task_option_desktop">Desktop</string>
<!-- Recents: The empty recents string. [CHAR LIMIT=NONE] -->
<string name="recents_empty_message">No recent items</string>
diff --git a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
index be532b4..0697f47 100644
--- a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
+++ b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
@@ -173,6 +173,7 @@
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
+import java.util.Map.Entry;
/**
* Manages the opening and closing app transitions from Launcher
diff --git a/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java b/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java
index 3e9272d..70e01f5 100644
--- a/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java
+++ b/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java
@@ -240,7 +240,11 @@
icon.reset();
if (predictionCount > i) {
icon.setVisibility(View.VISIBLE);
- icon.applyFromWorkspaceItem(mPredictedApps.get(i));
+ WorkspaceItemInfo predictedItem = mPredictedApps.get(i);
+ predictedItem.rank = i;
+ predictedItem.cellX = i;
+ predictedItem.cellY = 0;
+ icon.applyFromWorkspaceItem(predictedItem);
} else {
icon.setVisibility(predictionCount == 0 ? GONE : INVISIBLE);
}
diff --git a/quickstep/src/com/android/launcher3/desktop/DesktopRecentsTransitionController.kt b/quickstep/src/com/android/launcher3/desktop/DesktopRecentsTransitionController.kt
index 10733fb..64fe30c 100644
--- a/quickstep/src/com/android/launcher3/desktop/DesktopRecentsTransitionController.kt
+++ b/quickstep/src/com/android/launcher3/desktop/DesktopRecentsTransitionController.kt
@@ -56,6 +56,11 @@
systemUiProxy.showDesktopApps(desktopTaskView.display.displayId, transition)
}
+ /** Launch desktop tasks from recents view */
+ fun moveToDesktop(taskId: Int) {
+ systemUiProxy.moveToDesktop(taskId)
+ }
+
private class RemoteDesktopLaunchTransitionRunner(
private val desktopTaskView: DesktopTaskView,
private val stateManager: StateManager<*>,
@@ -99,8 +104,7 @@
finishCallback: IRemoteTransitionFinishedCallback
) {}
- override fun onTransitionConsumed(transition: IBinder?, aborted: Boolean) {
- }
+ override fun onTransitionConsumed(transition: IBinder?, aborted: Boolean) {}
}
companion object {
diff --git a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java
index 672bd1d..1c5a75d 100644
--- a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java
+++ b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java
@@ -499,7 +499,7 @@
@Override
public void onClick(View view) {
- dismissTaskMenuView(mTarget);
+ dismissTaskMenuView();
pinPrediction(mItemInfo);
}
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
index e802e72..49d4afe 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
@@ -113,8 +113,8 @@
import com.android.launcher3.testing.shared.TestProtocol;
import com.android.launcher3.touch.ItemClickHandler;
import com.android.launcher3.touch.ItemClickHandler.ItemClickProxy;
-import com.android.launcher3.uioverrides.ApiWrapper;
import com.android.launcher3.util.ActivityOptionsWrapper;
+import com.android.launcher3.util.ApiWrapper;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.Executors;
@@ -1120,7 +1120,7 @@
} else if (info.isPromise()) {
TestLogging.recordEvent(
TestProtocol.SEQUENCE_MAIN, "start: taskbarPromiseIcon");
- intent = ApiWrapper.getAppMarketActivityIntent(this,
+ intent = ApiWrapper.INSTANCE.get(this).getAppMarketActivityIntent(
info.getTargetPackage(), Process.myUserHandle());
startActivity(intent);
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
index 8f15d61..e47640b 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
@@ -444,7 +444,8 @@
LauncherAppState.getIDP(mContext).getDeviceProfile(mContext) : null;
// All Apps action is unrelated to navbar unification, so we only need to check DP.
- mAllAppsActionManager.setTaskbarPresent(dp != null && dp.isTaskbarPresent);
+ final boolean isLargeScreenTaskbar = dp != null && dp.isTaskbarPresent;
+ mAllAppsActionManager.setTaskbarPresent(isLargeScreenTaskbar);
destroyExistingTaskbar();
@@ -468,6 +469,7 @@
}
mSharedState.startTaskbarVariantIsTransient =
DisplayController.isTransientTaskbar(mTaskbarActivityContext);
+ mSharedState.allAppsVisible = mSharedState.allAppsVisible && isLargeScreenTaskbar;
mTaskbarActivityContext.init(mSharedState);
if (mActivity != null) {
diff --git a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsSlideInView.java b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsSlideInView.java
index 99937f8..6ceec3e 100644
--- a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsSlideInView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsSlideInView.java
@@ -96,7 +96,7 @@
mAllAppsCallbacks.onAllAppsTransitionStart(true);
if (!animate) {
mAllAppsCallbacks.onAllAppsTransitionEnd(true);
- mTranslationShift = TRANSLATION_SHIFT_OPENED;
+ setTranslationShift(TRANSLATION_SHIFT_OPENED);
return;
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/ApiWrapper.java b/quickstep/src/com/android/launcher3/uioverrides/ApiWrapper.java
deleted file mode 100644
index 873dea8..0000000
--- a/quickstep/src/com/android/launcher3/uioverrides/ApiWrapper.java
+++ /dev/null
@@ -1,201 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher3.uioverrides;
-
-import android.app.ActivityOptions;
-import android.app.PendingIntent;
-import android.app.Person;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentSender;
-import android.content.pm.ActivityInfo;
-import android.content.pm.LauncherActivityInfo;
-import android.content.pm.LauncherApps;
-import android.content.pm.LauncherUserInfo;
-import android.content.pm.ShortcutInfo;
-import android.graphics.drawable.ColorDrawable;
-import android.net.Uri;
-import android.os.UserHandle;
-import android.os.UserManager;
-import android.util.ArrayMap;
-import android.window.RemoteTransition;
-
-import com.android.launcher3.Flags;
-import com.android.launcher3.Utilities;
-import com.android.launcher3.proxy.ProxyActivityStarter;
-import com.android.launcher3.util.StartActivityParams;
-import com.android.launcher3.util.UserIconInfo;
-import com.android.quickstep.util.FadeOutRemoteTransition;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-
-/**
- * A wrapper for the hidden API calls
- */
-public class ApiWrapper {
-
- public static final boolean TASKBAR_DRAWN_IN_PROCESS = true;
-
- public static Person[] getPersons(ShortcutInfo si) {
- Person[] persons = si.getPersons();
- return persons == null ? Utilities.EMPTY_PERSON_ARRAY : persons;
- }
-
- public static Map<String, LauncherActivityInfo> getActivityOverrides(Context context) {
- return context.getSystemService(LauncherApps.class).getActivityOverrides();
- }
-
- /**
- * Creates an ActivityOptions to play fade-out animation on closing targets
- */
- public static ActivityOptions createFadeOutAnimOptions(Context context) {
- ActivityOptions options = ActivityOptions.makeBasic();
- options.setRemoteTransition(new RemoteTransition(new FadeOutRemoteTransition()));
- return options;
- }
-
- /**
- * Returns a map of all users on the device to their corresponding UI properties
- */
- public static Map<UserHandle, UserIconInfo> queryAllUsers(Context context) {
- UserManager um = context.getSystemService(UserManager.class);
- Map<UserHandle, UserIconInfo> users = new ArrayMap<>();
- List<UserHandle> usersActual = um.getUserProfiles();
- if (usersActual != null) {
- for (UserHandle user : usersActual) {
- if (android.os.Flags.allowPrivateProfile() && Flags.enablePrivateSpace()) {
- LauncherApps launcherApps = context.getSystemService(LauncherApps.class);
- LauncherUserInfo launcherUserInfo = launcherApps.getLauncherUserInfo(user);
- if (launcherUserInfo == null) {
- continue;
- }
- // UserTypes not supported in Launcher are deemed to be the current
- // Foreground User.
- int userType = switch (launcherUserInfo.getUserType()) {
- case UserManager.USER_TYPE_PROFILE_MANAGED -> UserIconInfo.TYPE_WORK;
- case UserManager.USER_TYPE_PROFILE_CLONE -> UserIconInfo.TYPE_CLONED;
- case UserManager.USER_TYPE_PROFILE_PRIVATE -> UserIconInfo.TYPE_PRIVATE;
- default -> UserIconInfo.TYPE_MAIN;
- };
- long serial = launcherUserInfo.getUserSerialNumber();
- users.put(user, new UserIconInfo(user, userType, serial));
- } else {
- long serial = um.getSerialNumberForUser(user);
-
- // Simple check to check if the provided user is work profile
- // TODO: Migrate to a better platform API
- NoopDrawable d = new NoopDrawable();
- boolean isWork = (d != context.getPackageManager().getUserBadgedIcon(d, user));
- UserIconInfo info = new UserIconInfo(
- user,
- isWork ? UserIconInfo.TYPE_WORK : UserIconInfo.TYPE_MAIN,
- serial);
- users.put(user, info);
- }
- }
- }
- return users;
- }
-
- /**
- * Returns the list of the system packages that are installed at user creation.
- * An empty list denotes that all system packages are installed for that user at creation.
- */
- public static List<String> getPreInstalledSystemPackages(Context context, UserHandle user) {
- LauncherApps launcherApps = context.getSystemService(LauncherApps.class);
- if (android.os.Flags.allowPrivateProfile() && Flags.enablePrivateSpace()
- && Flags.privateSpaceSysAppsSeparation()) {
- return launcherApps.getPreInstalledSystemPackages(user);
- } else {
- return new ArrayList<>();
- }
- }
-
- /**
- * Returns an intent which can be used to start the App Market activity (Installer
- * Activity).
- */
- public static Intent getAppMarketActivityIntent(Context context, String packageName,
- UserHandle user) {
- LauncherApps launcherApps = context.getSystemService(LauncherApps.class);
- if (android.os.Flags.allowPrivateProfile()
- && Flags.enablePrivateSpace()
- && (Flags.privateSpaceAppInstallerButton()
- || Flags.enablePrivateSpaceInstallShortcut())) {
- StartActivityParams params = new StartActivityParams((PendingIntent) null, 0);
- params.intentSender = launcherApps.getAppMarketActivityIntent(packageName, user);
- ActivityOptions options = ActivityOptions.makeBasic()
- .setPendingIntentBackgroundActivityStartMode(ActivityOptions
- .MODE_BACKGROUND_ACTIVITY_START_ALLOWED);
- params.options = options.toBundle();
- params.requireActivityResult = false;
- return ProxyActivityStarter.getLaunchIntent(context, params);
- } else {
- return new Intent(Intent.ACTION_VIEW)
- .setData(new Uri.Builder()
- .scheme("market")
- .authority("details")
- .appendQueryParameter("id", packageName)
- .build())
- .putExtra(Intent.EXTRA_REFERRER, new Uri.Builder().scheme("android-app")
- .authority(context.getPackageName()).build());
- }
- }
-
- /**
- * Returns an intent which can be used to open Private Space Settings.
- */
- public static Intent getPrivateSpaceSettingsIntent(Context context) {
- if (android.os.Flags.allowPrivateProfile() && Flags.enablePrivateSpace()) {
- LauncherApps launcherApps = context.getSystemService(LauncherApps.class);
- IntentSender intentSender = launcherApps.getPrivateSpaceSettingsIntent();
- if (intentSender == null) {
- return null;
- }
- StartActivityParams params = new StartActivityParams((PendingIntent) null, 0);
- params.intentSender = intentSender;
- ActivityOptions options = ActivityOptions.makeBasic()
- .setPendingIntentBackgroundActivityStartMode(ActivityOptions
- .MODE_BACKGROUND_ACTIVITY_START_ALLOWED);
- params.options = options.toBundle();
- params.requireActivityResult = false;
- return ProxyActivityStarter.getLaunchIntent(context, params);
- }
- return null;
- }
-
- /**
- * Checks if an activity is flagged as non-resizeable.
- */
- public static boolean isNonResizeableActivity(LauncherActivityInfo lai) {
- return lai.getActivityInfo().resizeMode == ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
- }
-
- private static class NoopDrawable extends ColorDrawable {
- @Override
- public int getIntrinsicHeight() {
- return 1;
- }
-
- @Override
- public int getIntrinsicWidth() {
- return 1;
- }
- }
-}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/SystemApiWrapper.kt b/quickstep/src/com/android/launcher3/uioverrides/SystemApiWrapper.kt
new file mode 100644
index 0000000..535b4c2
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/uioverrides/SystemApiWrapper.kt
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.uioverrides
+
+import android.app.ActivityOptions
+import android.app.PendingIntent
+import android.content.Context
+import android.content.Intent
+import android.content.pm.ActivityInfo
+import android.content.pm.LauncherActivityInfo
+import android.content.pm.LauncherApps
+import android.content.pm.ShortcutInfo
+import android.os.Flags.allowPrivateProfile
+import android.os.UserHandle
+import android.os.UserManager
+import android.util.ArrayMap
+import android.window.RemoteTransition
+import com.android.launcher3.Flags.enablePrivateSpace
+import com.android.launcher3.Flags.enablePrivateSpaceInstallShortcut
+import com.android.launcher3.Flags.privateSpaceAppInstallerButton
+import com.android.launcher3.Flags.privateSpaceSysAppsSeparation
+import com.android.launcher3.Utilities
+import com.android.launcher3.proxy.ProxyActivityStarter
+import com.android.launcher3.util.ApiWrapper
+import com.android.launcher3.util.StartActivityParams
+import com.android.launcher3.util.UserIconInfo
+import com.android.quickstep.util.FadeOutRemoteTransition
+
+/** A wrapper for the hidden API calls */
+class SystemApiWrapper(context: Context?) : ApiWrapper(context) {
+
+ override fun getPersons(si: ShortcutInfo) = si.persons ?: Utilities.EMPTY_PERSON_ARRAY
+
+ override fun getActivityOverrides(): Map<String, LauncherActivityInfo> =
+ mContext.getSystemService(LauncherApps::class.java)!!.activityOverrides
+
+ override fun createFadeOutAnimOptions(): ActivityOptions =
+ ActivityOptions.makeBasic().apply {
+ remoteTransition = RemoteTransition(FadeOutRemoteTransition())
+ }
+
+ override fun queryAllUsers(): Map<UserHandle, UserIconInfo> {
+ if (!allowPrivateProfile() || !enablePrivateSpace()) {
+ return super.queryAllUsers()
+ }
+ val users = ArrayMap<UserHandle, UserIconInfo>()
+ mContext.getSystemService(UserManager::class.java)!!.userProfiles?.forEach { user ->
+ mContext.getSystemService(LauncherApps::class.java)!!.getLauncherUserInfo(user)?.apply {
+ users[user] =
+ UserIconInfo(
+ user,
+ when (userType) {
+ UserManager.USER_TYPE_PROFILE_MANAGED -> UserIconInfo.TYPE_WORK
+ UserManager.USER_TYPE_PROFILE_CLONE -> UserIconInfo.TYPE_CLONED
+ UserManager.USER_TYPE_PROFILE_PRIVATE -> UserIconInfo.TYPE_PRIVATE
+ else -> UserIconInfo.TYPE_MAIN
+ },
+ userSerialNumber.toLong()
+ )
+ }
+ }
+ return users
+ }
+
+ override fun getPreInstalledSystemPackages(user: UserHandle): List<String> =
+ if (allowPrivateProfile() && enablePrivateSpace() && privateSpaceSysAppsSeparation())
+ mContext
+ .getSystemService(LauncherApps::class.java)!!
+ .getPreInstalledSystemPackages(user)
+ else ArrayList()
+
+ override fun getAppMarketActivityIntent(packageName: String, user: UserHandle): Intent =
+ if (
+ allowPrivateProfile() &&
+ enablePrivateSpace() &&
+ (privateSpaceAppInstallerButton() || enablePrivateSpaceInstallShortcut())
+ )
+ ProxyActivityStarter.getLaunchIntent(
+ mContext,
+ StartActivityParams(null as PendingIntent?, 0).apply {
+ intentSender =
+ mContext
+ .getSystemService(LauncherApps::class.java)!!
+ .getAppMarketActivityIntent(packageName, user)
+ options =
+ ActivityOptions.makeBasic()
+ .setPendingIntentBackgroundActivityStartMode(
+ ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED
+ )
+ .toBundle()
+ requireActivityResult = false
+ }
+ )
+ else super.getAppMarketActivityIntent(packageName, user)
+
+ /** Returns an intent which can be used to open Private Space Settings. */
+ override fun getPrivateSpaceSettingsIntent(): Intent? =
+ if (allowPrivateProfile() && enablePrivateSpace())
+ ProxyActivityStarter.getLaunchIntent(
+ mContext,
+ StartActivityParams(null as PendingIntent?, 0).apply {
+ intentSender =
+ mContext
+ .getSystemService(LauncherApps::class.java)
+ ?.privateSpaceSettingsIntent
+ ?: return null
+ options =
+ ActivityOptions.makeBasic()
+ .setPendingIntentBackgroundActivityStartMode(
+ ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED
+ )
+ .toBundle()
+ requireActivityResult = false
+ }
+ )
+ else null
+
+ override fun isNonResizeableActivity(lai: LauncherActivityInfo) =
+ lai.activityInfo.resizeMode == ActivityInfo.RESIZE_MODE_UNRESIZEABLE
+}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/flags/DevOptionsUiHelper.kt b/quickstep/src/com/android/launcher3/uioverrides/flags/DevOptionsUiHelper.kt
new file mode 100644
index 0000000..6b44ca7
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/uioverrides/flags/DevOptionsUiHelper.kt
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.uioverrides.flags
+
+import android.os.Handler
+import android.provider.DeviceConfig
+import android.text.Html
+import android.view.inputmethod.EditorInfo
+import androidx.preference.Preference
+import androidx.preference.PreferenceGroup
+import androidx.preference.PreferenceViewHolder
+import androidx.preference.SwitchPreference
+import com.android.launcher3.ExtendedEditText
+import com.android.launcher3.R
+import com.android.quickstep.util.DeviceConfigHelper
+import com.android.quickstep.util.DeviceConfigHelper.Companion.NAMESPACE_LAUNCHER
+import com.android.quickstep.util.DeviceConfigHelper.DebugInfo
+
+/** Helper class to generate UI for Device Config */
+class DevOptionsUiHelper {
+
+ /** Inflates preferences for all server flags in the provider PreferenceGroup */
+ fun inflateServerFlags(parent: PreferenceGroup) {
+ val prefs = DeviceConfigHelper.prefs
+ // Sort the keys in the order of modified first followed by natural order
+ val allProps =
+ DeviceConfigHelper.allProps.values
+ .toList()
+ .sortedWith(
+ Comparator.comparingInt { prop: DebugInfo<*> ->
+ if (prefs.contains(prop.key)) 0 else 1
+ }
+ .thenComparing { prop: DebugInfo<*> -> prop.key }
+ )
+
+ // First add boolean flags
+ allProps.forEach {
+ if (it.isInt) return@forEach
+ val info = it as DebugInfo<Boolean>
+
+ val preference =
+ object : SwitchPreference(parent.context) {
+ override fun onBindViewHolder(holder: PreferenceViewHolder) {
+ super.onBindViewHolder(holder)
+ holder.itemView.setOnLongClickListener {
+ prefs.edit().remove(key).apply()
+ setChecked(info.getBoolValue())
+ summary = info.getSummary()
+ true
+ }
+ }
+ }
+ preference.key = info.key
+ preference.isPersistent = false
+ preference.title = info.key
+ preference.summary = info.getSummary()
+ preference.setChecked(prefs.getBoolean(info.key, info.getBoolValue()))
+ preference.setOnPreferenceChangeListener { _, newVal ->
+ DeviceConfigHelper.prefs.edit().putBoolean(info.key, newVal as Boolean).apply()
+ preference.summary = info.getSummary()
+ true
+ }
+ parent.addPreference(preference)
+ }
+
+ // Apply Int flags
+ allProps.forEach {
+ if (!it.isInt) return@forEach
+ val info = it as DebugInfo<Int>
+
+ val preference =
+ object : Preference(parent.context) {
+ override fun onBindViewHolder(holder: PreferenceViewHolder) {
+ super.onBindViewHolder(holder)
+ val textView = holder.findViewById(R.id.pref_edit_text) as ExtendedEditText
+ textView.setText(info.getIntValueAsString())
+ textView.setOnEditorActionListener { _, actionId, _ ->
+ if (actionId == EditorInfo.IME_ACTION_DONE) {
+ DeviceConfigHelper.prefs
+ .edit()
+ .putInt(key, textView.text.toString().toInt())
+ .apply()
+ Handler().post { summary = info.getSummary() }
+ true
+ }
+ false
+ }
+ textView.setOnBackKeyListener {
+ textView.setText(info.getIntValueAsString())
+ true
+ }
+
+ holder.itemView.setOnLongClickListener {
+ prefs.edit().remove(key).apply()
+ textView.setText(info.getIntValueAsString())
+ summary = info.getSummary()
+ true
+ }
+ }
+ }
+ preference.key = info.key
+ preference.isPersistent = false
+ preference.title = info.key
+ preference.summary = info.getSummary()
+ preference.widgetLayoutResource = R.layout.develop_options_edit_text
+ parent.addPreference(preference)
+ }
+ }
+
+ /**
+ * Returns the summary to show the description and whether the flag overrides the default value.
+ */
+ private fun DebugInfo<*>.getSummary() =
+ Html.fromHtml(
+ (if (DeviceConfigHelper.prefs.contains(this.key))
+ "<font color='red'><b>[OVERRIDDEN]</b></font><br>"
+ else "") + this.desc
+ )
+
+ private fun DebugInfo<Boolean>.getBoolValue() =
+ DeviceConfigHelper.prefs.getBoolean(
+ this.key,
+ DeviceConfig.getBoolean(NAMESPACE_LAUNCHER, this.key, this.valueInCode)
+ )
+
+ private fun DebugInfo<Int>.getIntValueAsString() =
+ DeviceConfigHelper.prefs
+ .getInt(this.key, DeviceConfig.getInt(NAMESPACE_LAUNCHER, this.key, this.valueInCode))
+ .toString()
+
+ companion object {
+ const val TAG = "DeviceConfigUIHelper"
+ }
+}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/flags/DeveloperOptionsUI.java b/quickstep/src/com/android/launcher3/uioverrides/flags/DeveloperOptionsUI.java
index 6713964..fd6bf4d 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/flags/DeveloperOptionsUI.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/flags/DeveloperOptionsUI.java
@@ -20,19 +20,8 @@
import static android.view.View.GONE;
import static android.view.View.VISIBLE;
-import static com.android.launcher3.LauncherPrefs.ALL_APPS_OVERVIEW_THRESHOLD;
-import static com.android.launcher3.LauncherPrefs.PRIVATE_SPACE_APPS;
-import static com.android.launcher3.config.FeatureFlags.LPNH_EXTRA_TOUCH_WIDTH_DP;
-import static com.android.launcher3.config.FeatureFlags.LPNH_HAPTIC_HINT_DELAY;
-import static com.android.launcher3.config.FeatureFlags.LPNH_HAPTIC_HINT_END_SCALE_PERCENT;
-import static com.android.launcher3.config.FeatureFlags.LPNH_HAPTIC_HINT_ITERATIONS;
-import static com.android.launcher3.config.FeatureFlags.LPNH_HAPTIC_HINT_SCALE_EXPONENT;
-import static com.android.launcher3.config.FeatureFlags.LPNH_HAPTIC_HINT_START_SCALE_PERCENT;
-import static com.android.launcher3.config.FeatureFlags.LPNH_SLOP_PERCENTAGE;
-import static com.android.launcher3.config.FeatureFlags.LPNH_TIMEOUT_MS;
import static com.android.launcher3.settings.SettingsActivity.EXTRA_FRAGMENT_HIGHLIGHT_KEY;
import static com.android.launcher3.uioverrides.plugins.PluginManagerWrapper.PLUGIN_CHANGED;
-import static com.android.launcher3.uioverrides.plugins.PluginManagerWrapper.pluginEnabledKey;
import static com.android.launcher3.util.OnboardingPrefs.ALL_APPS_VISITED_COUNT;
import static com.android.launcher3.util.OnboardingPrefs.HOME_BOUNCE_COUNT;
import static com.android.launcher3.util.OnboardingPrefs.HOME_BOUNCE_SEEN;
@@ -51,7 +40,6 @@
import android.text.Editable;
import android.text.TextWatcher;
import android.util.ArrayMap;
-import android.util.Log;
import android.util.Pair;
import android.view.LayoutInflater;
import android.view.View;
@@ -61,21 +49,18 @@
import androidx.preference.Preference;
import androidx.preference.PreferenceCategory;
-import androidx.preference.PreferenceDataStore;
import androidx.preference.PreferenceFragmentCompat;
import androidx.preference.PreferenceGroup;
import androidx.preference.PreferenceScreen;
import androidx.preference.PreferenceViewHolder;
-import androidx.preference.SeekBarPreference;
import androidx.preference.SwitchPreference;
-import com.android.launcher3.ConstantItem;
-import com.android.launcher3.Flags;
import com.android.launcher3.LauncherPrefs;
import com.android.launcher3.R;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.secondarydisplay.SecondaryDisplayLauncher;
import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
+import com.android.systemui.shared.plugins.PluginEnabler;
import java.util.ArrayList;
import java.util.List;
@@ -96,8 +81,6 @@
private final PreferenceFragmentCompat mFragment;
private final PreferenceScreen mPreferenceScreen;
- private final FlagTogglerPrefUi mFlagTogglerPrefUi;
-
private PreferenceCategory mPluginsCategory;
public DeveloperOptionsUI(PreferenceFragmentCompat fragment, PreferenceCategory flags) {
@@ -112,20 +95,14 @@
parent.addView(topBar, parent.indexOfChild(listView));
initSearch(topBar.findViewById(R.id.filter_box));
- mFlagTogglerPrefUi = new FlagTogglerPrefUi(mFragment.requireActivity(),
- topBar.findViewById(R.id.flag_apply_btn));
- mFlagTogglerPrefUi.applyTo(flags);
+ new FlagTogglerPrefUi(mFragment.requireActivity(), topBar.findViewById(R.id.flag_apply_btn))
+ .applyTo(flags);
+ DevOptionsUiHelper uiHelper = new DevOptionsUiHelper();
+ uiHelper.inflateServerFlags(newCategory("Server flags"));
loadPluginPrefs();
maybeAddSandboxCategory();
addOnboardingPrefsCatergory();
- if (FeatureFlags.ENABLE_ALL_APPS_FROM_OVERVIEW.get()) {
- addAllAppsFromOverviewCatergory();
- }
- addCustomLpnhCategory();
- if (Flags.enablePrivateSpace()) {
- addCustomPrivateAppsCategory();
- }
}
private void filterPreferences(String query, PreferenceGroup pg) {
@@ -205,7 +182,7 @@
new ArrayMap<>();
Set<String> pluginPermissionApps = pm.getPackagesHoldingPermissions(
- new String[]{PLUGIN_PERMISSION}, MATCH_DISABLED_COMPONENTS)
+ new String[]{PLUGIN_PERMISSION}, MATCH_DISABLED_COMPONENTS)
.stream()
.map(pi -> pi.packageName)
.collect(Collectors.toSet());
@@ -228,7 +205,7 @@
}
}
- PreferenceDataStore enabler = manager.getPluginEnabler();
+ PluginEnabler enabler = manager.getPluginEnabler();
plugins.forEach((key, si) -> {
String packageName = key.first;
List<ComponentName> componentNames = si.stream()
@@ -347,111 +324,10 @@
return onboardingPref;
}
- private void addAllAppsFromOverviewCatergory() {
- PreferenceCategory category = newCategory("All Apps from Overview Config");
- category.addPreference(createSeekBarPreference("Threshold to open All Apps from Overview",
- 105, 500, 100, ALL_APPS_OVERVIEW_THRESHOLD));
- }
-
- private void addCustomLpnhCategory() {
- PreferenceCategory category = newCategory("Long Press Nav Handle Config");
- if (FeatureFlags.CUSTOM_LPNH_THRESHOLDS.get()) {
- category.addPreference(createSeekBarPreference(
- "Slop multiplier (applied to edge slop, "
- + "which is generally already 50% higher than touch slop)",
- 25, 200, 100, LPNH_SLOP_PERCENTAGE));
- category.addPreference(createSeekBarPreference(
- "Extra width DP (how far outside the sides of the nav bar to trigger)",
- // Stashed taskbar is currently 220dp; -86 (x2) would result in 48dp touch area.
- -86, 100, 1, LPNH_EXTRA_TOUCH_WIDTH_DP));
- category.addPreference(createSeekBarPreference("LPNH timeout",
- 100, 500, 1, LPNH_TIMEOUT_MS));
- }
- if (FeatureFlags.ENABLE_SEARCH_HAPTIC_HINT.get()) {
- category.addPreference(
- createSeekBarPreference("Haptic hint start scale",
- 0, 100, 100, LPNH_HAPTIC_HINT_START_SCALE_PERCENT));
- category.addPreference(createSeekBarPreference("Haptic hint end scale",
- 0, 100, 100, LPNH_HAPTIC_HINT_END_SCALE_PERCENT));
- category.addPreference(
- createSeekBarPreference("Haptic hint scale exponent",
- 1, 5, 1, LPNH_HAPTIC_HINT_SCALE_EXPONENT));
- category.addPreference(
- createSeekBarPreference("Haptic hint iterations (12 ms each)",
- 0, 200, 1, LPNH_HAPTIC_HINT_ITERATIONS));
- category.addPreference(createSeekBarPreference("Haptic hint delay (ms)",
- 0, 400, 1, LPNH_HAPTIC_HINT_DELAY));
- }
- }
-
- private void addCustomPrivateAppsCategory() {
- PreferenceCategory category = newCategory("Apps in Private Space Config");
- category.addPreference(createSeekBarPreference(
- "Number of Apps to put in private region", 0, 100, 1, PRIVATE_SPACE_APPS));
- }
-
- private SeekBarPreference createSeekBarPreference(String title, int min,
- int max, int scale, FeatureFlags.IntFlag flag) {
- if (!(flag instanceof IntDebugFlag)) {
- Log.e(TAG, "Cannot create seekbar preference with IntFlag. Use a launcher preference "
- + "flag or pref-backed IntDebugFlag instead");
- return null;
- }
- IntDebugFlag debugflag = (IntDebugFlag) flag;
- if (debugflag.launcherPrefFlag == null) {
- Log.e(TAG, "Cannot create seekbar preference with IntDebugFlag. Use a launcher "
- + "preference flag or pref-backed IntDebugFlag instead");
- return null;
- }
- SeekBarPreference seekBarPref = createSeekBarPreference(title, min, max, scale,
- debugflag.launcherPrefFlag);
- int value = flag.get();
- seekBarPref.setValue(value);
- // For some reason the initial value is not triggering the summary update, so call manually.
- seekBarPref.setSummary(String.valueOf(scale == 1 ? value
- : value / (float) scale));
- return seekBarPref;
- }
-
- /**
- * Create a preference with text and a seek bar. Should be added to a PreferenceCategory.
- *
- * @param title text to show for this seek bar
- * @param min min value for the seek bar
- * @param max max value for the seek bar
- * @param scale how much to divide the value to convert int to float
- * @param launcherPref used to store the current value
- */
- private SeekBarPreference createSeekBarPreference(String title, int min, int max, int scale,
- ConstantItem<Integer> launcherPref) {
- SeekBarPreference seekBarPref = new SeekBarPreference(getContext());
- seekBarPref.setTitle(title);
- seekBarPref.setSingleLineTitle(false);
-
- seekBarPref.setMax(max);
- seekBarPref.setMin(min);
- seekBarPref.setUpdatesContinuously(true);
- seekBarPref.setIconSpaceReserved(false);
- // Don't directly save to shared prefs, use LauncherPrefs instead.
- seekBarPref.setPersistent(false);
- seekBarPref.setOnPreferenceChangeListener((preference, newValue) -> {
- LauncherPrefs.get(getContext()).put(launcherPref, newValue);
- preference.setSummary(String.valueOf(scale == 1 ? newValue
- : (int) newValue / (float) scale));
- mFlagTogglerPrefUi.updateMenu();
- return true;
- });
- int value = LauncherPrefs.get(getContext()).get(launcherPref);
- seekBarPref.setValue(value);
- // For some reason the initial value is not triggering the summary update, so call manually.
- seekBarPref.setSummary(String.valueOf(scale == 1 ? value
- : value / (float) scale));
- return seekBarPref;
- }
-
private String toName(String action) {
String str = action.replace("com.android.systemui.action.PLUGIN_", "")
.replace("com.android.launcher3.action.PLUGIN_", "");
+
StringBuilder b = new StringBuilder();
for (String s : str.split("_")) {
if (b.length() != 0) {
@@ -466,11 +342,11 @@
private static class PluginPreference extends SwitchPreference {
private final String mPackageName;
private final ResolveInfo mSettingsInfo;
- private final PreferenceDataStore mPluginEnabler;
+ private final PluginEnabler mPluginEnabler;
private final List<ComponentName> mComponentNames;
PluginPreference(Context prefContext, ResolveInfo pluginInfo,
- PreferenceDataStore pluginEnabler, List<ComponentName> componentNames) {
+ PluginEnabler pluginEnabler, List<ComponentName> componentNames) {
super(prefContext);
PackageManager pm = prefContext.getPackageManager();
mPackageName = pluginInfo.serviceInfo.applicationInfo.packageName;
@@ -495,14 +371,9 @@
setWidgetLayoutResource(R.layout.switch_preference_with_settings);
}
- private boolean isEnabled(ComponentName cn) {
- return mPluginEnabler.getBoolean(pluginEnabledKey(cn), true);
-
- }
-
private boolean isPluginEnabled() {
for (ComponentName componentName : mComponentNames) {
- if (!isEnabled(componentName)) {
+ if (!mPluginEnabler.isEnabled(componentName)) {
return false;
}
}
@@ -513,8 +384,9 @@
protected boolean persistBoolean(boolean isEnabled) {
boolean shouldSendBroadcast = false;
for (ComponentName componentName : mComponentNames) {
- if (isEnabled(componentName) != isEnabled) {
- mPluginEnabler.putBoolean(pluginEnabledKey(componentName), isEnabled);
+ if (mPluginEnabler.isEnabled(componentName) != isEnabled) {
+ mPluginEnabler.setDisabled(componentName,
+ isEnabled ? PluginEnabler.ENABLED : PluginEnabler.DISABLED_MANUALLY);
shouldSendBroadcast = true;
}
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/flags/FlagTogglerPrefUi.java b/quickstep/src/com/android/launcher3/uioverrides/flags/FlagTogglerPrefUi.java
index 4326c67..fc39ce4 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/flags/FlagTogglerPrefUi.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/flags/FlagTogglerPrefUi.java
@@ -34,7 +34,6 @@
import androidx.preference.PreferenceViewHolder;
import androidx.preference.SwitchPreference;
-import com.android.launcher3.LauncherPrefs;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.util.ActivityLifecycleCallbacksAdapter;
@@ -162,22 +161,12 @@
return mDataStore.getBoolean(flag.key, defaultValue);
}
- private int getIntFlagStateFromSharedPrefs(IntDebugFlag flag) {
- LauncherPrefs prefs = LauncherPrefs.get(mContext);
- return flag.launcherPrefFlag == null ? flag.get() : prefs.get(flag.launcherPrefFlag);
- }
-
private boolean anyChanged() {
for (DebugFlag flag : FlagsFactory.getDebugFlags()) {
if (getFlagStateFromSharedPrefs(flag) != flag.get()) {
return true;
}
}
- for (IntDebugFlag flag : FlagsFactory.getIntDebugFlags()) {
- if (getIntFlagStateFromSharedPrefs(flag) != flag.get()) {
- return true;
- }
- }
return false;
}
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/flags/FlagsFactory.java b/quickstep/src/com/android/launcher3/uioverrides/flags/FlagsFactory.java
index 686ed64..7fd6344 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/flags/FlagsFactory.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/flags/FlagsFactory.java
@@ -23,8 +23,6 @@
import static com.android.launcher3.config.FeatureFlags.FlagState.ENABLED;
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
-import static java.util.Collections.unmodifiableList;
-
import android.content.Context;
import android.content.SharedPreferences;
import android.provider.DeviceConfig;
@@ -32,13 +30,9 @@
import android.util.Log;
import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import com.android.launcher3.ConstantItem;
-import com.android.launcher3.LauncherPrefs;
import com.android.launcher3.config.FeatureFlags.BooleanFlag;
import com.android.launcher3.config.FeatureFlags.FlagState;
-import com.android.launcher3.config.FeatureFlags.IntFlag;
import com.android.launcher3.util.ScreenOnTracker;
import java.io.PrintWriter;
@@ -62,7 +56,6 @@
public static final String NAMESPACE_LAUNCHER = "launcher";
private static final List<DebugFlag> sDebugFlags = new ArrayList<>();
- private static final List<IntDebugFlag> sIntDebugFlags = new ArrayList<>();
private static SharedPreferences sSharedPreferences;
static final BooleanFlag TEAMFOOD_FLAG = getReleaseFlag(
@@ -132,42 +125,6 @@
}
}
- /**
- * Creates a new integer flag. Integer flags are always release flags
- */
- public static IntFlag getIntFlag(
- int bugId, String key, int defaultValueInCode, String description) {
- return getIntFlag(bugId, key, defaultValueInCode, description, null);
- }
-
- /**
- * Creates a new integer flag.
- *
- * @param launcherPrefFlag Set launcherPrefFlag to non-null if you want
- * to modify the int flag in Launcher Developer Options and IntDebugFlag
- * will be backed up by LauncherPrefs. Modified int value will be saved
- * in LauncherPrefs.
- */
- public static IntFlag getIntFlag(
- int bugId, String key, int defaultValueInCode, String description,
- @Nullable ConstantItem<Integer> launcherPrefFlag) {
- INSTANCE.mKeySet.add(key);
- int defaultValue = DeviceConfig.getInt(NAMESPACE_LAUNCHER, key, defaultValueInCode);
- if (IS_DEBUG_DEVICE) {
- int currentValue;
- if (launcherPrefFlag == null) {
- currentValue = defaultValue;
- } else {
- currentValue = LauncherPrefs.get(currentApplication()).get(launcherPrefFlag);
- }
- IntDebugFlag flag = new IntDebugFlag(key, currentValue, defaultValueInCode,
- launcherPrefFlag);
- sIntDebugFlags.add(flag);
- return flag;
- } else {
- return new IntFlag(defaultValue);
- }
- }
static List<DebugFlag> getDebugFlags() {
if (!IS_DEBUG_DEVICE) {
@@ -178,15 +135,6 @@
}
}
- static List<IntDebugFlag> getIntDebugFlags() {
- if (!IS_DEBUG_DEVICE) {
- return unmodifiableList(Collections.emptyList());
- }
- synchronized (sIntDebugFlags) {
- return unmodifiableList(sIntDebugFlags);
- }
- }
-
/** Returns the SharedPreferences instance backing Debug FeatureFlags. */
@NonNull
static SharedPreferences getSharedPreferences() {
@@ -214,12 +162,6 @@
}
}
}
- pw.println(" IntFlags:");
- synchronized (sIntDebugFlags) {
- for (IntFlag flag : sIntDebugFlags) {
- pw.println(" " + flag);
- }
- }
pw.println(" DebugFlags:");
synchronized (sDebugFlags) {
for (DebugFlag flag : sDebugFlags) {
diff --git a/quickstep/src/com/android/launcher3/uioverrides/flags/IntDebugFlag.java b/quickstep/src/com/android/launcher3/uioverrides/flags/IntDebugFlag.java
deleted file mode 100644
index 1350aa8..0000000
--- a/quickstep/src/com/android/launcher3/uioverrides/flags/IntDebugFlag.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * 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.uioverrides.flags;
-
-import androidx.annotation.Nullable;
-
-import com.android.launcher3.ConstantItem;
-import com.android.launcher3.config.FeatureFlags.IntFlag;
-
-public class IntDebugFlag extends IntFlag {
- public final String key;
- private final int mDefaultValueInCode;
- @Nullable
- public final ConstantItem<Integer> launcherPrefFlag;
-
- public IntDebugFlag(String key, int currentValue, int defaultValueInCode,
- @Nullable ConstantItem<Integer> launcherPrefFlag) {
- super(currentValue);
- this.key = key;
- mDefaultValueInCode = defaultValueInCode;
- this.launcherPrefFlag = launcherPrefFlag;
- }
-
- @Override
- public String toString() {
- return key + ": mCurrentValue=" + get() + ", defaultValueInCode=" + mDefaultValueInCode;
- }
-}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/plugins/PluginEnablerImpl.java b/quickstep/src/com/android/launcher3/uioverrides/plugins/PluginEnablerImpl.java
index faa900b..4e09f1f 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/plugins/PluginEnablerImpl.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/plugins/PluginEnablerImpl.java
@@ -18,12 +18,10 @@
import android.content.Context;
import android.content.SharedPreferences;
-import androidx.preference.PreferenceDataStore;
-
import com.android.launcher3.LauncherPrefs;
import com.android.systemui.shared.plugins.PluginEnabler;
-public class PluginEnablerImpl extends PreferenceDataStore implements PluginEnabler {
+public class PluginEnablerImpl implements PluginEnabler {
private static final String PREFIX_PLUGIN_ENABLED = "PLUGIN_ENABLED_";
@@ -44,12 +42,12 @@
}
private void setState(ComponentName component, boolean enabled) {
- putBoolean(pluginEnabledKey(component), enabled);
+ mSharedPrefs.edit().putBoolean(pluginEnabledKey(component), enabled).apply();
}
@Override
public boolean isEnabled(ComponentName component) {
- return getBoolean(pluginEnabledKey(component), true);
+ return mSharedPrefs.getBoolean(pluginEnabledKey(component), true);
}
@Override
@@ -57,17 +55,7 @@
return isEnabled(componentName) ? ENABLED : DISABLED_MANUALLY;
}
- @Override
- public void putBoolean(String key, boolean value) {
- mSharedPrefs.edit().putBoolean(key, value).apply();
- }
-
- @Override
- public boolean getBoolean(String key, boolean defValue) {
- return mSharedPrefs.getBoolean(key, defValue);
- }
-
- static String pluginEnabledKey(ComponentName cn) {
+ private static String pluginEnabledKey(ComponentName cn) {
return PREFIX_PLUGIN_ENABLED + cn.flattenToString();
}
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/plugins/PluginManagerWrapper.java b/quickstep/src/com/android/launcher3/uioverrides/plugins/PluginManagerWrapper.java
index 7f78713..a09d0a1 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/plugins/PluginManagerWrapper.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/plugins/PluginManagerWrapper.java
@@ -98,13 +98,6 @@
return new PluginPrefs(mContext).getPluginList();
}
- /**
- * Returns the string key used to store plugin enabled/disabled setting
- */
- public static String pluginEnabledKey(ComponentName cn) {
- return PluginEnablerImpl.pluginEnabledKey(cn);
- }
-
public static boolean hasPlugins(Context context) {
return PluginPrefs.hasPlugins(context);
}
diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
index 62e823a..7ee7751 100644
--- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
+++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
@@ -22,8 +22,8 @@
import static android.widget.Toast.LENGTH_SHORT;
import static com.android.app.animation.Interpolators.ACCELERATE_DECELERATE;
-import static com.android.app.animation.Interpolators.EMPHASIZED;
import static com.android.app.animation.Interpolators.DECELERATE;
+import static com.android.app.animation.Interpolators.EMPHASIZED;
import static com.android.app.animation.Interpolators.LINEAR;
import static com.android.app.animation.Interpolators.OVERSHOOT_1_2;
import static com.android.launcher3.BaseActivity.EVENT_DESTROYED;
@@ -31,7 +31,6 @@
import static com.android.launcher3.BaseActivity.INVISIBLE_BY_STATE_HANDLER;
import static com.android.launcher3.BaseActivity.STATE_HANDLER_INVISIBILITY_FLAGS;
import static com.android.launcher3.Flags.enableGridOnlyOverview;
-import static com.android.launcher3.LauncherPrefs.ALL_APPS_OVERVIEW_THRESHOLD;
import static com.android.launcher3.PagedView.INVALID_PAGE;
import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_BACKGROUND;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.IGNORE;
@@ -100,7 +99,6 @@
import com.android.internal.util.LatencyTracker;
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.LauncherPrefs;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AnimationSuccessListener;
@@ -892,7 +890,7 @@
@UiThread
@Override
public void onCurrentShiftUpdated() {
- float threshold = LauncherPrefs.get(mContext).get(ALL_APPS_OVERVIEW_THRESHOLD) / 100f;
+ float threshold = DeviceConfigWrapper.get().getAllAppsOverviewThreshold() / 100f;
setIsInAllAppsRegion(mCurrentShift.value >= threshold);
updateSysUiFlags(mCurrentShift.value);
applyScrollAndTransform();
diff --git a/quickstep/src/com/android/quickstep/DesktopSystemShortcut.kt b/quickstep/src/com/android/quickstep/DesktopSystemShortcut.kt
new file mode 100644
index 0000000..8c71d92
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/DesktopSystemShortcut.kt
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.quickstep
+
+import android.view.View
+import com.android.launcher3.AbstractFloatingViewHelper
+import com.android.launcher3.BaseDraggingActivity
+import com.android.launcher3.R
+import com.android.launcher3.logging.StatsLogManager.LauncherEvent
+import com.android.launcher3.popup.SystemShortcut
+import com.android.quickstep.views.RecentsView
+import com.android.quickstep.views.TaskView.TaskIdAttributeContainer
+import com.android.window.flags.Flags
+
+/** A menu item, "Desktop", that allows the user to bring the current app into Desktop Windowing. */
+class DesktopSystemShortcut(
+ activity: BaseDraggingActivity,
+ private val mTaskContainer: TaskIdAttributeContainer,
+ abstractFloatingViewHelper: AbstractFloatingViewHelper
+) :
+ SystemShortcut<BaseDraggingActivity>(
+ R.drawable.ic_caption_desktop_button_foreground,
+ R.string.recent_task_option_desktop,
+ activity,
+ mTaskContainer.itemInfo,
+ mTaskContainer.taskView,
+ abstractFloatingViewHelper
+ ) {
+ override fun onClick(view: View) {
+ dismissTaskMenuView()
+ val recentsView = mTarget!!.getOverviewPanel<RecentsView<*, *>>()
+ recentsView.moveTaskToDesktop(mTaskContainer) {
+ mTarget.statsLogManager
+ .logger()
+ .withItemInfo(mTaskContainer.itemInfo)
+ .log(LauncherEvent.LAUNCHER_SYSTEM_SHORTCUT_DESKTOP_TAP)
+ }
+ }
+
+ companion object {
+ /** Creates a factory for creating Desktop system shorcuts. */
+ @JvmOverloads
+ fun createFactory(
+ abstractFloatingViewHelper: AbstractFloatingViewHelper = AbstractFloatingViewHelper()
+ ): TaskShortcutFactory {
+ return object : TaskShortcutFactory {
+ override fun getShortcuts(
+ activity: BaseDraggingActivity,
+ taskContainer: TaskIdAttributeContainer
+ ): List<DesktopSystemShortcut>? {
+ return if (!Flags.enableDesktopWindowingMode()) null
+ else if (!taskContainer.task.isDockable) null
+ else
+ listOf(
+ DesktopSystemShortcut(
+ activity,
+ taskContainer,
+ abstractFloatingViewHelper
+ )
+ )
+ }
+
+ override fun showForSplitscreen() = true
+ }
+ }
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/DeviceConfigWrapper.kt b/quickstep/src/com/android/quickstep/DeviceConfigWrapper.kt
new file mode 100644
index 0000000..678709c
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/DeviceConfigWrapper.kt
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.quickstep
+
+import com.android.quickstep.util.DeviceConfigHelper
+import com.android.quickstep.util.DeviceConfigHelper.PropReader
+import java.io.PrintWriter
+
+/** Various configurations specific to nav-bar functionalities */
+class DeviceConfigWrapper private constructor(propReader: PropReader) {
+
+ val customLpnhThresholds =
+ propReader.get(
+ "CUSTOM_LPNH_THRESHOLDS",
+ true,
+ "Add dev options and server side control to customize the LPNH trigger slop and milliseconds"
+ )
+
+ val customLphThresholds =
+ propReader.get(
+ "CUSTOM_LPH_THRESHOLDS",
+ false,
+ "Server side control to customize LPH timeout and touch slop"
+ )
+
+ val overrideLpnhLphThresholds =
+ propReader.get(
+ "OVERRIDE_LPNH_LPH_THRESHOLDS",
+ false,
+ "Enable AGSA override for LPNH and LPH timeout and touch slop"
+ )
+
+ val lpnhSlopPercentage =
+ propReader.get("LPNH_SLOP_PERCENTAGE", 100, "Controls touch slop percentage for lpnh")
+
+ val animateLpnh = propReader.get("ANIMATE_LPNH", false, "Animates navbar when long pressing")
+
+ val shrinkNavHandleOnPress =
+ propReader.get(
+ "SHRINK_NAV_HANDLE_ON_PRESS",
+ false,
+ "Shrinks navbar when long pressing if ANIMATE_LPNH is enabled"
+ )
+
+ val lpnhTimeoutMs =
+ propReader.get("LPNH_TIMEOUT_MS", 450, "Controls lpnh timeout in milliseconds")
+
+ val enableLongPressNavHandle =
+ propReader.get(
+ "ENABLE_LONG_PRESS_NAV_HANDLE",
+ true,
+ "Enables long pressing on the bottom bar nav handle to trigger events."
+ )
+
+ val enableSearchHapticHint =
+ propReader.get(
+ "ENABLE_SEARCH_HAPTIC_HINT",
+ true,
+ "Enables haptic hint while long pressing on the bottom bar nav handle."
+ )
+
+ val enableSearchHapticCommit =
+ propReader.get(
+ "ENABLE_SEARCH_HAPTIC_COMMIT",
+ true,
+ "Enables haptic hint at end of long pressing on the bottom bar nav handle."
+ )
+
+ val lpnhHapticHintStartScalePercent =
+ propReader.get("LPNH_HAPTIC_HINT_START_SCALE_PERCENT", 0, "Haptic hint start scale.")
+
+ val lpnhHapticHintEndScalePercent =
+ propReader.get("LPNH_HAPTIC_HINT_END_SCALE_PERCENT", 100, "Haptic hint end scale.")
+
+ val lpnhHapticHintScaleExponent =
+ propReader.get("LPNH_HAPTIC_HINT_SCALE_EXPONENT", 1, "Haptic hint scale exponent.")
+
+ val lpnhHapticHintIterations =
+ propReader.get("LPNH_HAPTIC_HINT_ITERATIONS", 50, "Haptic hint number of iterations.")
+
+ val enableLpnhDeepPress =
+ propReader.get(
+ "ENABLE_LPNH_DEEP_PRESS",
+ true,
+ "Long press of nav handle is instantly triggered if deep press is detected."
+ )
+
+ val lpnhHapticHintDelay =
+ propReader.get("LPNH_HAPTIC_HINT_DELAY", 0, "Delay before haptic hint starts.")
+
+ val lpnhExtraTouchWidthDp =
+ propReader.get(
+ "LPNH_EXTRA_TOUCH_WIDTH_DP",
+ 0,
+ "Controls extra dp on the nav bar sides to trigger LPNH. Can be negative for a smaller touch region."
+ )
+
+ val allAppsOverviewThreshold =
+ propReader.get(
+ "ALL_APPS_OVERVIEW_THRESHOLD",
+ 180,
+ "Threshold to open All Apps from Overview"
+ )
+
+ /** Dump config values. */
+ fun dump(prefix: String, writer: PrintWriter) {
+ writer.println("$prefix DeviceConfigWrapper:")
+ writer.println("$prefix\tcustomLpnhThresholds=$customLpnhThresholds")
+ writer.println("$prefix\tcustomLphThresholds=$customLphThresholds")
+ writer.println("$prefix\toverrideLpnhLphThresholds=$overrideLpnhLphThresholds")
+ writer.println("$prefix\tlpnhSlopPercentage=$lpnhSlopPercentage")
+ writer.println("$prefix\tanimateLpnh=$animateLpnh")
+ writer.println("$prefix\tshrinkNavHandleOnPress=$shrinkNavHandleOnPress")
+ writer.println("$prefix\tlpnhTimeoutMs=$lpnhTimeoutMs")
+ writer.println("$prefix\tenableLongPressNavHandle=$enableLongPressNavHandle")
+ writer.println("$prefix\tenableSearchHapticHint=$enableSearchHapticHint")
+ writer.println("$prefix\tenableSearchHapticCommit=$enableSearchHapticCommit")
+ writer.println("$prefix\tlpnhHapticHintStartScalePercent=$lpnhHapticHintStartScalePercent")
+ writer.println("$prefix\tlpnhHapticHintEndScalePercent=$lpnhHapticHintEndScalePercent")
+ writer.println("$prefix\tlpnhHapticHintScaleExponent=$lpnhHapticHintScaleExponent")
+ writer.println("$prefix\tlpnhHapticHintIterations=$lpnhHapticHintIterations")
+ writer.println("$prefix\tenableLpnhDeepPress=$enableLpnhDeepPress")
+ writer.println("$prefix\tlpnhHapticHintDelay=$lpnhHapticHintDelay")
+ writer.println("$prefix\tlpnhExtraTouchWidthDp=$lpnhExtraTouchWidthDp")
+ writer.println("$prefix\tallAppsOverviewThreshold=$allAppsOverviewThreshold")
+ }
+
+ companion object {
+ val configHelper by lazy { DeviceConfigHelper(::DeviceConfigWrapper) }
+
+ @JvmStatic fun get() = configHelper.config
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
index c56a621..4b4f914 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
@@ -60,7 +60,6 @@
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
-import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.DisplayController.DisplayInfoChangeListener;
import com.android.launcher3.util.DisplayController.Info;
@@ -68,6 +67,7 @@
import com.android.launcher3.util.SettingsCache;
import com.android.quickstep.TopTaskTracker.CachedTaskInfo;
import com.android.quickstep.util.ActiveGestureLog;
+import com.android.quickstep.util.AssistStateManager;
import com.android.quickstep.util.GestureExclusionManager;
import com.android.quickstep.util.GestureExclusionManager.ExclusionListener;
import com.android.quickstep.util.NavBarPosition;
@@ -97,7 +97,7 @@
private final DisplayController mDisplayController;
private final GestureExclusionManager mExclusionManager;
-
+ private final AssistStateManager mAssistStateManager;
private final RotationTouchHelper mRotationTouchHelper;
private final TaskStackChangeListener mPipListener;
@@ -148,6 +148,7 @@
mContext = context;
mDisplayController = DisplayController.INSTANCE.get(context);
mExclusionManager = exclusionManager;
+ mAssistStateManager = AssistStateManager.INSTANCE.get(context);
mIsOneHandedModeSupported = SystemProperties.getBoolean(SUPPORT_ONE_HANDED_MODE, false);
mRotationTouchHelper = RotationTouchHelper.INSTANCE.get(context);
if (isInstanceForTouches) {
@@ -588,9 +589,8 @@
: QUICKSTEP_TOUCH_SLOP_RATIO_TWO_BUTTON;
float touchSlop = ViewConfiguration.get(mContext).getScaledTouchSlop();
- if (FeatureFlags.CUSTOM_LPNH_THRESHOLDS.get()) {
- float customSlopMultiplier =
- FeatureFlags.LPNH_SLOP_PERCENTAGE.get() / 100f;
+ if (mAssistStateManager.getLPNHCustomSlopMultiplier().isPresent()) {
+ float customSlopMultiplier = mAssistStateManager.getLPNHCustomSlopMultiplier().get();
return customSlopMultiplier * slopMultiplier * touchSlop;
} else {
return slopMultiplier * touchSlop;
diff --git a/quickstep/src/com/android/quickstep/SystemUiProxy.java b/quickstep/src/com/android/quickstep/SystemUiProxy.java
index ab609fd..30bb863 100644
--- a/quickstep/src/com/android/quickstep/SystemUiProxy.java
+++ b/quickstep/src/com/android/quickstep/SystemUiProxy.java
@@ -836,7 +836,9 @@
*/
public void setBubbleBarBounds(Rect bubbleBarBounds) {
try {
- mBubbles.setBubbleBarBounds(bubbleBarBounds);
+ if (mBubbles != null) {
+ mBubbles.setBubbleBarBounds(bubbleBarBounds);
+ }
} catch (RemoteException e) {
Log.w(TAG, "Failed call setBubbleBarBounds");
}
@@ -1473,6 +1475,17 @@
}
}
+ /** Call shell to move a task with given `taskId` to desktop */
+ public void moveToDesktop(int taskId) {
+ if (mDesktopMode != null) {
+ try {
+ mDesktopMode.moveToDesktop(taskId);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call moveToDesktop", e);
+ }
+ }
+ }
+
//
// Unfold transition
//
diff --git a/quickstep/src/com/android/quickstep/TaskOverlayFactory.java b/quickstep/src/com/android/quickstep/TaskOverlayFactory.java
index cc582d1..8a4989b 100644
--- a/quickstep/src/com/android/quickstep/TaskOverlayFactory.java
+++ b/quickstep/src/com/android/quickstep/TaskOverlayFactory.java
@@ -136,6 +136,7 @@
TaskShortcutFactory.PIN,
TaskShortcutFactory.INSTALL,
TaskShortcutFactory.FREE_FORM,
+ DesktopSystemShortcut.Companion.createFactory(),
TaskShortcutFactory.WELLBEING,
TaskShortcutFactory.SAVE_APP_PAIR
};
@@ -315,7 +316,7 @@
@Override
public void onClick(View view) {
saveScreenshot(mThumbnailView.getTaskView().getTask());
- dismissTaskMenuView(mActivity);
+ dismissTaskMenuView();
}
}
diff --git a/quickstep/src/com/android/quickstep/TaskShortcutFactory.java b/quickstep/src/com/android/quickstep/TaskShortcutFactory.java
index 147a3e2..5e970f1 100644
--- a/quickstep/src/com/android/quickstep/TaskShortcutFactory.java
+++ b/quickstep/src/com/android/quickstep/TaskShortcutFactory.java
@@ -145,7 +145,7 @@
@Override
public void onClick(View view) {
- dismissTaskMenuView(mTarget);
+ dismissTaskMenuView();
((RecentsView) mTarget.getOverviewPanel())
.getSplitSelectController().getAppPairsController().saveAppPair(mTaskView);
}
@@ -174,7 +174,7 @@
@Override
public void onClick(View view) {
- dismissTaskMenuView(mTarget);
+ dismissTaskMenuView();
RecentsView rv = mTarget.getOverviewPanel();
rv.switchToScreenshot(() -> {
rv.finishRecentsAnimation(true /* toRecents */, false /* shouldPip */, () -> {
@@ -420,7 +420,7 @@
SystemUiProxy.INSTANCE.get(mTarget).startScreenPinning(
mTaskView.getTask().key.id);
}
- dismissTaskMenuView(mTarget);
+ dismissTaskMenuView();
mTarget.getStatsLogManager().logger().withItemInfo(mTaskView.getItemInfo())
.log(LauncherEvent.LAUNCHER_SYSTEM_SHORTCUT_PIN_TAP);
}
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index e66268a..8f1bf34 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -1454,6 +1454,7 @@
pw.println("AssistStateManager:");
AssistStateManager.INSTANCE.get(this).dump(" ", pw);
SystemUiProxy.INSTANCE.get(this).dump(pw);
+ DeviceConfigWrapper.get().dump(" ", pw);
}
private AbsSwipeUpHandler createLauncherSwipeHandler(
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/NavHandleLongPressInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/NavHandleLongPressInputConsumer.java
index e22703b..5ab2fcc 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/NavHandleLongPressInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/NavHandleLongPressInputConsumer.java
@@ -27,9 +27,9 @@
import android.view.ViewConfiguration;
import com.android.launcher3.Utilities;
-import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.logging.StatsLogManager;
import com.android.launcher3.util.DisplayController;
+import com.android.quickstep.DeviceConfigWrapper;
import com.android.quickstep.InputConsumer;
import com.android.quickstep.NavHandle;
import com.android.quickstep.RecentsAnimationDeviceState;
@@ -64,7 +64,7 @@
NavHandle navHandle) {
super(delegate, inputMonitor);
mScreenWidth = DisplayController.INSTANCE.get(context).getInfo().currentSize.x;
- mDeepPressEnabled = FeatureFlags.ENABLE_LPNH_DEEP_PRESS.get();
+ mDeepPressEnabled = DeviceConfigWrapper.get().getEnableLpnhDeepPress();
AssistStateManager assistStateManager = AssistStateManager.INSTANCE.get(context);
if (assistStateManager.getLPNHDurationMillis().isPresent()) {
mLongPressTimeout = assistStateManager.getLPNHDurationMillis().get().intValue();
@@ -181,8 +181,9 @@
private boolean isInNavBarHorizontalArea(float x) {
float areaFromMiddle = mNavHandleWidth / 2.0f;
- if (FeatureFlags.CUSTOM_LPNH_THRESHOLDS.get()) {
- areaFromMiddle += Utilities.dpToPx(FeatureFlags.LPNH_EXTRA_TOUCH_WIDTH_DP.get());
+ if (DeviceConfigWrapper.get().getCustomLpnhThresholds()) {
+ areaFromMiddle += Utilities.dpToPx(
+ DeviceConfigWrapper.get().getLpnhExtraTouchWidthDp());
}
int minAccessibleSize = Utilities.dpToPx(24); // Half of 48dp because this is per side.
if (areaFromMiddle < minAccessibleSize) {
diff --git a/quickstep/src/com/android/quickstep/util/AnimatorControllerWithResistance.java b/quickstep/src/com/android/quickstep/util/AnimatorControllerWithResistance.java
index 16f2065..561e951 100644
--- a/quickstep/src/com/android/quickstep/util/AnimatorControllerWithResistance.java
+++ b/quickstep/src/com/android/quickstep/util/AnimatorControllerWithResistance.java
@@ -18,7 +18,6 @@
import static com.android.app.animation.Interpolators.DECELERATE;
import static com.android.app.animation.Interpolators.LINEAR;
import static com.android.launcher3.Flags.enableGridOnlyOverview;
-import static com.android.launcher3.LauncherPrefs.ALL_APPS_OVERVIEW_THRESHOLD;
import static com.android.quickstep.views.RecentsView.RECENTS_SCALE_PROPERTY;
import static com.android.quickstep.views.RecentsView.TASK_SECONDARY_TRANSLATION;
@@ -35,7 +34,6 @@
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherPrefs;
import com.android.launcher3.LauncherState;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AnimatorPlaybackController;
@@ -44,6 +42,7 @@
import com.android.launcher3.statemanager.StatefulActivity;
import com.android.launcher3.states.StateAnimationConfig;
import com.android.launcher3.touch.AllAppsSwipeController;
+import com.android.quickstep.DeviceConfigWrapper;
import com.android.quickstep.orientation.RecentsPagedOrientationHandler;
import com.android.quickstep.views.RecentsView;
@@ -191,7 +190,7 @@
recentsOrientedState.getOrientationHandler());
float dragLengthFactor = (float) dp.heightPx / transitionDragLength;
// -1s are because 0-1 is reserved for the normal transition.
- float threshold = LauncherPrefs.get(context).get(ALL_APPS_OVERVIEW_THRESHOLD) / 100f;
+ float threshold = DeviceConfigWrapper.get().getAllAppsOverviewThreshold() / 100f;
return (threshold - 1) / (dragLengthFactor - 1);
}
diff --git a/quickstep/src/com/android/quickstep/util/AssistStateManager.java b/quickstep/src/com/android/quickstep/util/AssistStateManager.java
index a1fdbbb..a3904bc 100644
--- a/quickstep/src/com/android/quickstep/util/AssistStateManager.java
+++ b/quickstep/src/com/android/quickstep/util/AssistStateManager.java
@@ -52,16 +52,38 @@
return Optional.empty();
}
- /** Get the Launcher overridden long press duration to trigger Assistant. */
+ /** Get the Launcher overridden long press nav handle duration to trigger Assistant. */
public Optional<Long> getLPNHDurationMillis() {
return Optional.empty();
}
- /** Get the Launcher overridden long press touch slop multiplier to trigger Assistant. */
- public Optional<Long> getLPNHCustomSlopMultiplier() {
+ /**
+ * Get the Launcher overridden long press nav handle touch slop multiplier to trigger Assistant.
+ */
+ public Optional<Float> getLPNHCustomSlopMultiplier() {
return Optional.empty();
}
+ /** Get the Launcher overridden long press home duration to trigger Assistant. */
+ public Optional<Long> getLPHDurationMillis() {
+ return Optional.empty();
+ }
+
+ /** Get the Launcher overridden long press home touch slop multiplier to trigger Assistant. */
+ public Optional<Float> getLPHCustomSlopMultiplier() {
+ return Optional.empty();
+ }
+
+ /** Get the long press duration data source. */
+ public int getDurationDataSource() {
+ return 0;
+ }
+
+ /** Get the long press touch slop multiplier data source. */
+ public int getSlopDataSource() {
+ return 0;
+ }
+
/** Return {@code true} if the Settings toggle is enabled. */
public boolean isSettingsAllEntrypointsEnabled() {
return false;
diff --git a/quickstep/src/com/android/quickstep/util/DeviceConfigHelper.kt b/quickstep/src/com/android/quickstep/util/DeviceConfigHelper.kt
new file mode 100644
index 0000000..f601fee
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/DeviceConfigHelper.kt
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.quickstep.util
+
+import android.app.ActivityThread
+import android.content.Context
+import android.content.SharedPreferences
+import android.content.SharedPreferences.*
+import android.provider.DeviceConfig
+import android.provider.DeviceConfig.OnPropertiesChangedListener
+import android.provider.DeviceConfig.Properties
+import androidx.annotation.WorkerThread
+import com.android.launcher3.BuildConfig
+import com.android.launcher3.uioverrides.flags.FlagsFactory
+import com.android.launcher3.util.Executors
+
+/** Utility class to manage a set of device configurations */
+class DeviceConfigHelper<ConfigType>(private val factory: (PropReader) -> ConfigType) {
+
+ var config: ConfigType
+ private set
+ private val allKeys: Set<String>
+ private val propertiesListener = OnPropertiesChangedListener { onDevicePropsChanges(it) }
+ private val sharedPrefChangeListener = OnSharedPreferenceChangeListener { _, _ ->
+ recreateConfig()
+ }
+
+ private val changeListeners = mutableListOf<Runnable>()
+
+ init {
+ // Initialize the default config once.
+ allKeys = HashSet()
+ config =
+ factory(
+ PropReader(
+ object : PropProvider {
+ override fun <T : Any> get(key: String, fallback: T): T {
+ if (fallback is Int)
+ return DeviceConfig.getInt(NAMESPACE_LAUNCHER, key, fallback) as T
+ else if (fallback is Boolean)
+ return DeviceConfig.getBoolean(NAMESPACE_LAUNCHER, key, fallback)
+ as T
+ else return fallback
+ }
+ }
+ )
+ )
+
+ DeviceConfig.addOnPropertiesChangedListener(
+ NAMESPACE_LAUNCHER,
+ Executors.UI_HELPER_EXECUTOR,
+ propertiesListener
+ )
+ if (BuildConfig.IS_DEBUG_DEVICE) {
+ prefs.registerOnSharedPreferenceChangeListener(sharedPrefChangeListener)
+ }
+ }
+
+ @WorkerThread
+ private fun onDevicePropsChanges(properties: Properties) {
+ if (NAMESPACE_LAUNCHER != properties.namespace) return
+ if (!allKeys.any(properties.keyset::contains)) return
+ recreateConfig()
+ }
+
+ private fun recreateConfig() {
+ val myProps =
+ DeviceConfig.getProperties(
+ FlagsFactory.NAMESPACE_LAUNCHER,
+ *allKeys.toTypedArray<String>()
+ )
+ config =
+ factory(
+ PropReader(
+ object : PropProvider {
+ override fun <T : Any> get(key: String, fallback: T): T {
+ if (fallback is Int) return myProps.getInt(key, fallback) as T
+ else if (fallback is Boolean)
+ return myProps.getBoolean(key, fallback) as T
+ else return fallback
+ }
+ }
+ )
+ )
+ Executors.MAIN_EXECUTOR.execute { changeListeners.forEach(Runnable::run) }
+ }
+
+ /** Adds a listener for property changes */
+ fun addChangeListener(r: Runnable) = changeListeners.add(r)
+
+ /** Removes a previously added listener */
+ fun removeChangeListener(r: Runnable) = changeListeners.remove(r)
+
+ fun close() {
+ DeviceConfig.removeOnPropertiesChangedListener(propertiesListener)
+ if (BuildConfig.IS_DEBUG_DEVICE) {
+ prefs.unregisterOnSharedPreferenceChangeListener(sharedPrefChangeListener)
+ }
+ }
+
+ internal interface PropProvider {
+ fun <T : Any> get(key: String, fallback: T): T
+ }
+
+ /** The reader is sent to the config for initialization */
+ class PropReader internal constructor(private val f: PropProvider) {
+
+ @JvmOverloads
+ fun <T : Any> get(key: String, fallback: T, desc: String? = null): T {
+ val v = f.get(key, fallback)
+ if (BuildConfig.IS_DEBUG_DEVICE && desc != null) {
+ if (v is Int) {
+ allProps[key] = DebugInfo(key, desc, true, fallback)
+ return prefs.getInt(key, v) as T
+ } else if (v is Boolean) {
+ allProps[key] = DebugInfo(key, desc, false, fallback)
+ return prefs.getBoolean(key, v) as T
+ }
+ }
+ return v
+ }
+ }
+
+ class DebugInfo<T>(
+ val key: String,
+ val desc: String,
+ val isInt: Boolean,
+ val valueInCode: T,
+ )
+
+ companion object {
+ const val NAMESPACE_LAUNCHER = "launcher"
+
+ val allProps = mutableMapOf<String, DebugInfo<*>>()
+
+ private const val FLAGS_PREF_NAME = "featureFlags"
+
+ val prefs: SharedPreferences by lazy {
+ ActivityThread.currentApplication()
+ .createDeviceProtectedStorageContext()
+ .getSharedPreferences(FLAGS_PREF_NAME, Context.MODE_PRIVATE)
+ }
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/views/ClearAllButton.java b/quickstep/src/com/android/quickstep/views/ClearAllButton.java
index 32ef904..acda2e1 100644
--- a/quickstep/src/com/android/quickstep/views/ClearAllButton.java
+++ b/quickstep/src/com/android/quickstep/views/ClearAllButton.java
@@ -17,15 +17,28 @@
package com.android.quickstep.views;
import static com.android.launcher3.Flags.enableGridOnlyOverview;
+import static com.android.quickstep.util.BorderAnimator.DEFAULT_BORDER_COLOR;
import android.content.Context;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.graphics.Canvas;
+import android.graphics.Rect;
import android.util.AttributeSet;
import android.util.FloatProperty;
import android.widget.Button;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.Flags;
+import com.android.launcher3.R;
import com.android.launcher3.statemanager.StatefulActivity;
import com.android.quickstep.orientation.RecentsPagedOrientationHandler;
+import com.android.quickstep.util.BorderAnimator;
+
+import kotlin.Unit;
public class ClearAllButton extends Button {
@@ -71,11 +84,71 @@
private float mScrollOffsetPrimary;
private int mSidePadding;
+ private int mOutlinePadding;
+ private boolean mBorderEnabled;
+ @Nullable
+ private final BorderAnimator mFocusBorderAnimator;
public ClearAllButton(Context context, AttributeSet attrs) {
super(context, attrs);
mIsRtl = getLayoutDirection() == LAYOUT_DIRECTION_RTL;
mActivity = StatefulActivity.fromContext(context);
+
+ if (Flags.enableFocusOutline()) {
+ TypedArray styledAttrs = context.obtainStyledAttributes(attrs,
+ R.styleable.ClearAllButton);
+ Resources resources = getResources();
+ mOutlinePadding = resources.getDimensionPixelSize(
+ R.dimen.recents_clear_all_outline_padding);
+ mFocusBorderAnimator =
+ BorderAnimator.createSimpleBorderAnimator(
+ /* borderRadiusPx= */ resources.getDimensionPixelSize(
+ R.dimen.recents_clear_all_outline_radius),
+ /* borderWidthPx= */ context.getResources().getDimensionPixelSize(
+ R.dimen.keyboard_quick_switch_border_width),
+ /* boundsBuilder= */ this::updateBorderBounds,
+ /* targetView= */ this,
+ /* borderColor= */ styledAttrs.getColor(
+ R.styleable.ClearAllButton_focusBorderColor,
+ DEFAULT_BORDER_COLOR));
+ styledAttrs.recycle();
+ } else {
+ mFocusBorderAnimator = null;
+ }
+ }
+
+ private Unit updateBorderBounds(@NonNull Rect bounds) {
+ bounds.set(0, 0, getWidth(), getHeight());
+ // Make the value negative to form a padding between button and outline
+ bounds.inset(-mOutlinePadding, -mOutlinePadding);
+ return Unit.INSTANCE;
+ }
+
+ @Override
+ public void onFocusChanged(boolean gainFocus, int direction, Rect previouslyFocusedRect) {
+ super.onFocusChanged(gainFocus, direction, previouslyFocusedRect);
+ if (mFocusBorderAnimator != null && mBorderEnabled) {
+ mFocusBorderAnimator.setBorderVisibility(gainFocus, /* animated= */ true);
+ }
+ }
+
+ /**
+ * Enable or disable showing border on focus change
+ */
+ public void setBorderEnabled(boolean enabled) {
+ mBorderEnabled = enabled;
+ if (mFocusBorderAnimator != null) {
+ mFocusBorderAnimator.setBorderVisibility(/* visible= */
+ enabled && isFocused(), /* animated= */true);
+ }
+ }
+
+ @Override
+ public void draw(Canvas canvas) {
+ if (mFocusBorderAnimator != null) {
+ mFocusBorderAnimator.drawBorder(canvas);
+ }
+ super.draw(canvas);
}
@Override
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index eca70b7..2e719cd 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -163,6 +163,7 @@
import com.android.launcher3.util.SplitConfigurationOptions.SplitSelectSource;
import com.android.launcher3.util.SplitConfigurationOptions.StagePosition;
import com.android.launcher3.util.Themes;
+import com.android.launcher3.util.TraceHelper;
import com.android.launcher3.util.TranslateEdgeEffect;
import com.android.launcher3.util.VibratorWrapper;
import com.android.launcher3.util.ViewPool;
@@ -1454,6 +1455,7 @@
TaskView taskView = requireTaskViewAt(i);
taskView.setBorderEnabled(enabled);
}
+ mClearAllButton.setBorderEnabled(enabled);
}
/**
@@ -1504,6 +1506,15 @@
}
@Override
+ public boolean onInterceptTouchEvent(MotionEvent ev) {
+ boolean intercept = super.onInterceptTouchEvent(ev);
+ if (ev.getActionMasked() == MotionEvent.ACTION_DOWN) {
+ Log.d("b/318590728", "onInterceptTouchEvent: " + ev);
+ }
+ return intercept;
+ }
+
+ @Override
public boolean onTouchEvent(MotionEvent ev) {
super.onTouchEvent(ev);
@@ -2569,6 +2580,7 @@
*/
public void onGestureAnimationStart(
Task[] runningTasks, RotationTouchHelper rotationTouchHelper) {
+ Log.d(TAG, "onGestureAnimationStart");
mActiveGestureRunningTasks = runningTasks;
// This needs to be called before the other states are set since it can create the task view
if (mOrientationState.setGestureActive(true)) {
@@ -4293,7 +4305,8 @@
* Updates {@link RecentsOrientedState}'s cached RecentsView rotation.
*/
public void updateRecentsRotation() {
- final int rotation = mActivity.getDisplay().getRotation();
+ final int rotation = TraceHelper.allowIpcs(
+ "RecentsView.updateRecentsRotation", () -> mActivity.getDisplay().getRotation());
mOrientationState.setRecentsRotation(rotation);
}
@@ -5413,6 +5426,8 @@
* Called when a running recents animation has finished or canceled.
*/
public void onRecentsAnimationComplete() {
+ Log.d(TAG, "onRecentsAnimationComplete - mRecentsAnimationController: "
+ + mRecentsAnimationController);
// At this point, the recents animation is not running and if the animation was canceled
// by a display rotation then reset this state to show the screenshot
setRunningTaskViewShowScreenshot(true);
@@ -6200,6 +6215,27 @@
UI_HELPER_EXECUTOR.post(() -> mActivity.setLocusContext(id, Bundle.EMPTY));
}
+ /**
+ * Moves the provided task into desktop mode, and invoke {@code successCallback} if succeeded.
+ */
+ public void moveTaskToDesktop(TaskIdAttributeContainer taskContainer,
+ Runnable successCallback) {
+ if (!enableDesktopWindowingMode()) {
+ return;
+ }
+ switchToScreenshot(() -> finishRecentsAnimation(/* toRecents= */true, /* shouldPip= */false,
+ () -> moveTaskToDesktopInternal(taskContainer, successCallback)));
+ }
+
+ private void moveTaskToDesktopInternal(TaskIdAttributeContainer taskContainer,
+ Runnable successCallback) {
+ if (mDesktopRecentsTransitionController == null) {
+ return;
+ }
+ mDesktopRecentsTransitionController.moveToDesktop(taskContainer.getTask().key.id);
+ successCallback.run();
+ }
+
public interface TaskLaunchListener {
void onTaskLaunched();
}
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java
index cec0982..5338d81 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskView.java
@@ -423,7 +423,8 @@
mCurrentFullscreenParams = new FullscreenDrawParams(context);
mDigitalWellBeingToast = new DigitalWellBeingToast(mActivity, this);
- boolean keyboardFocusHighlightEnabled = FeatureFlags.ENABLE_KEYBOARD_QUICK_SWITCH.get();
+ boolean keyboardFocusHighlightEnabled = FeatureFlags.ENABLE_KEYBOARD_QUICK_SWITCH.get()
+ || Flags.enableFocusOutline();
boolean cursorHoverStatesEnabled = enableCursorHoverStates();
setWillNotDraw(!keyboardFocusHighlightEnabled && !cursorHoverStatesEnabled);
@@ -486,7 +487,11 @@
return getItemInfo(mTask);
}
- protected WorkspaceItemInfo getItemInfo(@Nullable Task task) {
+ /**
+ * Builds proto for logging
+ */
+ @VisibleForTesting
+ public WorkspaceItemInfo getItemInfo(@Nullable Task task) {
WorkspaceItemInfo stubInfo = new WorkspaceItemInfo();
stubInfo.itemType = LauncherSettings.Favorites.ITEM_TYPE_TASK;
stubInfo.container = LauncherSettings.Favorites.CONTAINER_TASKSWITCHER;
@@ -812,12 +817,18 @@
private void onClick(View view) {
if (getTask() == null) {
+ Log.d("b/310064698", "onClick - task is null");
return;
}
if (confirmSecondSplitSelectApp()) {
+ Log.d("b/310064698", mTask + " - onClick - split select is active");
return;
}
- launchTasks();
+ RunnableList callbackList = launchTasks();
+ Log.d("b/310064698", mTask + " - onClick - callbackList: " + callbackList);
+ if (callbackList != null) {
+ callbackList.add(() -> Log.d("b/310064698", mTask + " - onClick - launchCompleted"));
+ }
mActivity.getStatsLogManager().logger().withItemInfo(getItemInfo())
.log(LAUNCHER_TASK_LAUNCH_TAP);
}
diff --git a/quickstep/tests/src/com/android/quickstep/DesktopSystemShortcutTest.kt b/quickstep/tests/src/com/android/quickstep/DesktopSystemShortcutTest.kt
new file mode 100644
index 0000000..b90839d
--- /dev/null
+++ b/quickstep/tests/src/com/android/quickstep/DesktopSystemShortcutTest.kt
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.quickstep
+
+import android.content.ComponentName
+import android.content.Intent
+import android.platform.test.flag.junit.SetFlagsRule
+import com.android.launcher3.AbstractFloatingView
+import com.android.launcher3.AbstractFloatingViewHelper
+import com.android.launcher3.Launcher
+import com.android.launcher3.logging.StatsLogManager
+import com.android.launcher3.logging.StatsLogManager.LauncherEvent
+import com.android.launcher3.model.data.WorkspaceItemInfo
+import com.android.launcher3.util.SplitConfigurationOptions
+import com.android.quickstep.views.LauncherRecentsView
+import com.android.quickstep.views.TaskView
+import com.android.systemui.shared.recents.model.Task
+import com.android.systemui.shared.recents.model.Task.TaskKey
+import com.android.window.flags.Flags
+import com.google.common.truth.Truth.assertThat
+import org.junit.Rule
+import org.junit.Test
+import org.mockito.kotlin.any
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
+
+/** Test for DesktopSystemShortcut */
+class DesktopSystemShortcutTest {
+
+ @get:Rule val setFlagsRule = SetFlagsRule(SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT)
+
+ private val launcher: Launcher = mock()
+ private val statsLogManager: StatsLogManager = mock()
+ private val statsLogger: StatsLogManager.StatsLogger = mock()
+ private val recentsView: LauncherRecentsView = mock()
+ private val taskView: TaskView = mock()
+ private val workspaceItemInfo: WorkspaceItemInfo = mock()
+ private val abstractFloatingViewHelper: AbstractFloatingViewHelper = mock()
+ private val factory: TaskShortcutFactory =
+ DesktopSystemShortcut.createFactory(abstractFloatingViewHelper)
+
+ @Test
+ fun createDesktopTaskShortcutFactory_featureOff() {
+ setFlagsRule.disableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
+
+ val task =
+ Task(TaskKey(1, 0, Intent(), ComponentName("", ""), 0, 2000)).apply {
+ isDockable = true
+ }
+ val taskContainer =
+ taskView.TaskIdAttributeContainer(
+ task,
+ null,
+ null,
+ SplitConfigurationOptions.STAGE_POSITION_UNDEFINED
+ )
+
+ val shortcuts = factory.getShortcuts(launcher, taskContainer)
+ assertThat(shortcuts).isNull()
+ }
+
+ @Test
+ fun createDesktopTaskShortcutFactory_undockable() {
+ setFlagsRule.enableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
+
+ val task =
+ Task(TaskKey(1, 0, Intent(), ComponentName("", ""), 0, 2000)).apply {
+ isDockable = false
+ }
+ val taskContainer =
+ taskView.TaskIdAttributeContainer(
+ task,
+ null,
+ null,
+ SplitConfigurationOptions.STAGE_POSITION_UNDEFINED
+ )
+
+ val shortcuts = factory.getShortcuts(launcher, taskContainer)
+ assertThat(shortcuts).isNull()
+ }
+
+ @Test
+ fun desktopSystemShortcutClicked() {
+ setFlagsRule.enableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
+
+ val task =
+ Task(TaskKey(1, 0, Intent(), ComponentName("", ""), 0, 2000)).apply {
+ isDockable = true
+ }
+ val taskContainer =
+ taskView.TaskIdAttributeContainer(
+ task,
+ null,
+ null,
+ SplitConfigurationOptions.STAGE_POSITION_UNDEFINED
+ )
+
+ whenever(launcher.getOverviewPanel<LauncherRecentsView>()).thenReturn(recentsView)
+ whenever(launcher.statsLogManager).thenReturn(statsLogManager)
+ whenever(statsLogManager.logger()).thenReturn(statsLogger)
+ whenever(statsLogger.withItemInfo(any())).thenReturn(statsLogger)
+ whenever(taskView.getItemInfo(task)).thenReturn(workspaceItemInfo)
+ whenever(recentsView.moveTaskToDesktop(any(), any())).thenAnswer {
+ val successCallback = it.getArgument<Runnable>(1)
+ successCallback.run()
+ }
+
+ val shortcuts = factory.getShortcuts(launcher, taskContainer)
+ assertThat(shortcuts).hasSize(1)
+ assertThat(shortcuts!!.first()).isInstanceOf(DesktopSystemShortcut::class.java)
+
+ val desktopShortcut = shortcuts.first() as DesktopSystemShortcut
+
+ desktopShortcut.onClick(taskView)
+
+ val allTypesExceptRebindSafe =
+ AbstractFloatingView.TYPE_ALL and AbstractFloatingView.TYPE_REBIND_SAFE.inv()
+ verify(abstractFloatingViewHelper).closeOpenViews(launcher, true, allTypesExceptRebindSafe)
+ verify(recentsView).moveTaskToDesktop(eq(taskContainer), any())
+ verify(statsLogger).withItemInfo(workspaceItemInfo)
+ verify(statsLogger).log(LauncherEvent.LAUNCHER_SYSTEM_SHORTCUT_DESKTOP_TAP)
+ }
+}
diff --git a/quickstep/tests/src/com/android/quickstep/TaplPrivateSpaceTest.java b/quickstep/tests/src/com/android/quickstep/TaplPrivateSpaceTest.java
new file mode 100644
index 0000000..0c143b4
--- /dev/null
+++ b/quickstep/tests/src/com/android/quickstep/TaplPrivateSpaceTest.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.quickstep;
+
+import static com.android.launcher3.LauncherState.ALL_APPS;
+import static com.android.launcher3.LauncherState.NORMAL;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
+
+import android.util.Log;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.LargeTest;
+
+import com.android.launcher3.tapl.LauncherInstrumentation;
+
+import org.junit.After;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.IOException;
+import java.util.Objects;
+
+@LargeTest
+@RunWith(AndroidJUnit4.class)
+public class TaplPrivateSpaceTest extends AbstractQuickStepTest {
+
+ private int mProfileUserId;
+ private boolean mPrivateProfileSetupSuccessful;
+ private static final String TAG = "TaplPrivateSpaceTest";
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ initialize(this);
+
+ createAndStartPrivateProfileUser();
+ assumeTrue("Private Profile Setup not successful, aborting",
+ mPrivateProfileSetupSuccessful);
+
+ mDevice.pressHome();
+ waitForLauncherCondition("Launcher didn't start", Objects::nonNull);
+ waitForStateTransitionToEnd("Launcher internal state didn't switch to Normal",
+ () -> NORMAL);
+ waitForResumed("Launcher internal state is still Background");
+ mLauncher.getWorkspace().switchToAllApps();
+ waitForStateTransitionToEnd("Launcher internal state didn't switch to All Apps",
+ () -> ALL_APPS);
+
+ // Wait for Private Space being available in Launcher.
+ waitForPrivateSpaceSetup();
+ // Wait for Launcher UI to be updated with Private Space Items.
+ waitForLauncherUIUpdate();
+ }
+
+ private void createAndStartPrivateProfileUser() {
+ String createUserOutput = executeShellCommand("pm create-user --profileOf 0 --user-type "
+ + "android.os.usertype.profile.PRIVATE LauncherPrivateProfile");
+ updatePrivateProfileSetupSuccessful("pm create-user", createUserOutput);
+ String[] tokens = createUserOutput.split("\\s+");
+ mProfileUserId = Integer.parseInt(tokens[tokens.length - 1]);
+ StringBuilder logStr = new StringBuilder().append("profileId: ").append(mProfileUserId);
+ for (String str : tokens) {
+ logStr.append(str).append("\n");
+ }
+ String startUserOutput = executeShellCommand("am start-user " + mProfileUserId);
+ updatePrivateProfileSetupSuccessful("am start-user", startUserOutput);
+ }
+
+ @After
+ public void removePrivateProfile() {
+ String output = executeShellCommand("pm remove-user " + mProfileUserId);
+ updateProfileRemovalSuccessful("pm remove-user", output);
+ waitForPrivateSpaceRemoval();
+ }
+
+ @Test
+ public void testPrivateSpaceContainerIsPresent() {
+ assumeTrue(mPrivateProfileSetupSuccessful);
+ // Scroll to the bottom of All Apps
+ executeOnLauncher(launcher -> launcher.getAppsView().resetAndScrollToPrivateSpaceHeader());
+ waitForResumed("Launcher internal state is still Background");
+
+ // Verify Unlocked View elements are present.
+ assertNotNull("Private Space Unlocked View not found, or is not correct",
+ mLauncher.getAllApps().getPrivateSpaceUnlockedView());
+ }
+
+ private void waitForPrivateSpaceSetup() {
+ waitForLauncherCondition("Private Profile not setup",
+ launcher -> launcher.getAppsView().hasPrivateProfile(),
+ LauncherInstrumentation.WAIT_TIME_MS);
+ }
+
+ private void waitForPrivateSpaceRemoval() {
+ waitForLauncherCondition("Private Profile not setup",
+ launcher -> !launcher.getAppsView().hasPrivateProfile(),
+ LauncherInstrumentation.WAIT_TIME_MS);
+ }
+
+ private void waitForLauncherUIUpdate() {
+ // Wait for model thread completion as it may be processing
+ // the install event from the SystemService
+ mLauncher.waitForModelQueueCleared();
+ // Wait for Launcher UI thread completion, as it may be processing updating the UI in
+ // response to the model update. Not that `waitForLauncherInitialized` is just a proxy
+ // method, we can use any method which touches Launcher UI thread,
+ mLauncher.waitForLauncherInitialized();
+ }
+
+ private void updatePrivateProfileSetupSuccessful(String cli, String output) {
+ Log.d(TAG, "updatePrivateProfileSetupSuccessful, cli=" + cli + " " + "output="
+ + output);
+ mPrivateProfileSetupSuccessful = output.startsWith("Success");
+ }
+
+ private void updateProfileRemovalSuccessful(String cli, String output) {
+ Log.d(TAG, "updateProfileRemovalSuccessful, cli=" + cli + " " + "output=" + output);
+ assertTrue(output, output.startsWith("Success"));
+ }
+
+ private String executeShellCommand(String command) {
+ try {
+ return mDevice.executeShellCommand(command);
+ } catch (IOException e) {
+ Log.e(TAG, "error running shell command", e);
+ throw new RuntimeException(e);
+ }
+ }
+}
diff --git a/quickstep/tests/src/com/android/quickstep/TaplStartLauncherViaGestureTests.java b/quickstep/tests/src/com/android/quickstep/TaplStartLauncherViaGestureTests.java
index b31f470..e4caa26 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplStartLauncherViaGestureTests.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplStartLauncherViaGestureTests.java
@@ -16,13 +16,9 @@
package com.android.quickstep;
-import static com.android.launcher3.util.rule.TestStabilityRule.LOCAL;
-import static com.android.launcher3.util.rule.TestStabilityRule.PLATFORM_POSTSUBMIT;
-
import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
-import com.android.launcher3.util.rule.TestStabilityRule.Stability;
import com.android.quickstep.NavigationModeSwitchRule.NavigationModeSwitch;
import org.junit.Before;
@@ -47,8 +43,6 @@
@Test
@NavigationModeSwitch
- // Stress tests are long. We permanently demote them from presubmit to match the presubmit SLO.
- @Stability(flavors = LOCAL | PLATFORM_POSTSUBMIT)
public void testStressPressHome() {
for (int i = 0; i < STRESS_REPEAT_COUNT; ++i) {
// Destroy Launcher activity.
@@ -61,8 +55,6 @@
@Test
@NavigationModeSwitch
- // Stress tests are long. We permanently demote them from presubmit to match the presubmit SLO.
- @Stability(flavors = LOCAL | PLATFORM_POSTSUBMIT)
public void testStressSwipeToOverview() {
for (int i = 0; i < STRESS_REPEAT_COUNT; ++i) {
// Destroy Launcher activity.
diff --git a/res/layout/develop_options_edit_text.xml b/res/layout/develop_options_edit_text.xml
new file mode 100644
index 0000000..5e44228
--- /dev/null
+++ b/res/layout/develop_options_edit_text.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2024 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<com.android.launcher3.ExtendedEditText
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:minWidth="100dp"
+ android:inputType="numberSigned"
+ android:id="@+id/pref_edit_text"
+ android:selectAllOnFocus="true"
+ android:imeOptions="actionDone"
+ android:maxLines="1" />
\ No newline at end of file
diff --git a/res/values/config.xml b/res/values/config.xml
index 599584b..f820e76 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -78,6 +78,7 @@
<string name="launcher_restore_event_logger_class" translatable="false"></string>
<!-- Used for determining category of a widget presented in widget recommendations. -->
<string name="widget_recommendation_category_provider_class" translatable="false"></string>
+ <string name="api_wrapper_class" translatable="false"></string>
<!-- Default packages -->
<string name="wallpaper_picker_package" translatable="false"></string>
diff --git a/src/com/android/launcher3/AbstractFloatingView.java b/src/com/android/launcher3/AbstractFloatingView.java
index e7b88dc..4ccf3db 100644
--- a/src/com/android/launcher3/AbstractFloatingView.java
+++ b/src/com/android/launcher3/AbstractFloatingView.java
@@ -278,18 +278,7 @@
public static void closeOpenViews(ActivityContext activity, boolean animate,
@FloatingViewType int type) {
- BaseDragLayer dragLayer = activity.getDragLayer();
- // Iterate in reverse order. AbstractFloatingView is added later to the dragLayer,
- // and will be one of the last views.
- for (int i = dragLayer.getChildCount() - 1; i >= 0; i--) {
- View child = dragLayer.getChildAt(i);
- if (child instanceof AbstractFloatingView) {
- AbstractFloatingView abs = (AbstractFloatingView) child;
- if (abs.isOfType(type)) {
- abs.close(animate);
- }
- }
- }
+ new AbstractFloatingViewHelper().closeOpenViews(activity, animate, type);
}
public static void closeAllOpenViews(ActivityContext activity, boolean animate) {
diff --git a/src/com/android/launcher3/AbstractFloatingViewHelper.kt b/src/com/android/launcher3/AbstractFloatingViewHelper.kt
new file mode 100644
index 0000000..0bfbc6e
--- /dev/null
+++ b/src/com/android/launcher3/AbstractFloatingViewHelper.kt
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3
+
+import com.android.launcher3.AbstractFloatingView.FloatingViewType
+import com.android.launcher3.views.ActivityContext
+
+/**
+ * Helper class for manaing AbstractFloatingViews which shows a floating UI on top of the launcher
+ * UI.
+ */
+class AbstractFloatingViewHelper {
+ fun closeOpenViews(activity: ActivityContext, animate: Boolean, @FloatingViewType type: Int) {
+ val dragLayer = activity.getDragLayer()
+ // Iterate in reverse order. AbstractFloatingView is added later to the dragLayer,
+ // and will be one of the last views.
+ for (i in dragLayer.getChildCount() - 1 downTo 0) {
+ val child = dragLayer.getChildAt(i)
+ if (child is AbstractFloatingView && child.isOfType(type)) {
+ child.close(animate)
+ }
+ }
+ }
+}
diff --git a/src/com/android/launcher3/AutoInstallsLayout.java b/src/com/android/launcher3/AutoInstallsLayout.java
index c7cdfa8..cf86528 100644
--- a/src/com/android/launcher3/AutoInstallsLayout.java
+++ b/src/com/android/launcher3/AutoInstallsLayout.java
@@ -52,7 +52,7 @@
import com.android.launcher3.pm.UserCache;
import com.android.launcher3.qsb.QsbContainerView;
import com.android.launcher3.shortcuts.ShortcutKey;
-import com.android.launcher3.uioverrides.ApiWrapper;
+import com.android.launcher3.util.ApiWrapper;
import com.android.launcher3.util.IntArray;
import com.android.launcher3.util.Partner;
import com.android.launcher3.util.Thunk;
@@ -203,7 +203,7 @@
mIdp = LauncherAppState.getIDP(context);
mRowCount = mIdp.numRows;
mColumnCount = mIdp.numColumns;
- mActivityOverride = ApiWrapper.getActivityOverrides(context);
+ mActivityOverride = ApiWrapper.INSTANCE.get(context).getActivityOverrides();
}
/**
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index 3ddc7aa..6cb33a8 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -63,13 +63,13 @@
import com.android.launcher3.responsive.ResponsiveSpec.Companion.ResponsiveSpecType;
import com.android.launcher3.responsive.ResponsiveSpec.DimensionType;
import com.android.launcher3.responsive.ResponsiveSpecsProvider;
-import com.android.launcher3.uioverrides.ApiWrapper;
import com.android.launcher3.util.CellContentDimensions;
import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.DisplayController.Info;
import com.android.launcher3.util.IconSizeSteps;
import com.android.launcher3.util.ResourceHelper;
import com.android.launcher3.util.WindowBounds;
+import com.android.launcher3.util.window.WindowManagerProxy;
import java.io.PrintWriter;
import java.util.Locale;
@@ -350,7 +350,8 @@
isTablet = info.isTablet(windowBounds);
isPhone = !isTablet;
isTwoPanels = isTablet && isMultiDisplay;
- isTaskbarPresent = isTablet && ApiWrapper.TASKBAR_DRAWN_IN_PROCESS;
+ isTaskbarPresent = isTablet
+ && WindowManagerProxy.INSTANCE.get(context).isTaskbarDrawnInProcess();
// Some more constants.
context = getContext(context, info, isVerticalBarLayout() || (isTablet && isLandscape)
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 155bfa1..65e1978 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -1551,7 +1551,13 @@
LauncherAppWidgetInfo launcherInfo,
CellPos presenterPos) {
CellLayout cellLayout = getCellLayout(launcherInfo.container, presenterPos.screenId);
- if (mStateManager.getState() == NORMAL) {
+ // We should wait until launcher is not animating to show resize frame so that
+ // {@link View#hasIdentityMatrix()} returns true (no scale effect) from CellLayout and
+ // Workspace (they are widget's parent view). Otherwise widget's
+ // {@link View#getLocationInWindow(int[])} will set skewed location, causing resize
+ // frame not showing at skewed location in
+ // {@link AppWidgetResizeFrame#snapToWidget(boolean)}.
+ if (mStateManager.getState() == NORMAL && !mStateManager.isInTransition()) {
AppWidgetResizeFrame.showForWidget(launcherHostView, cellLayout);
} else {
mStateManager.addStateListener(new StateManager.StateListener<LauncherState>() {
diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java
index 60a6be6..50a597d 100644
--- a/src/com/android/launcher3/LauncherAppState.java
+++ b/src/com/android/launcher3/LauncherAppState.java
@@ -110,7 +110,7 @@
mOnTerminateCallback.add(() ->
mContext.getSystemService(LauncherApps.class).unregisterCallback(callbacks));
- if (Utilities.enableSupportForArchiving()) {
+ if (Flags.enableSupportForArchiving()) {
ArchiveCompatibilityParams params = new ArchiveCompatibilityParams();
params.setEnableUnarchivalConfirmation(false);
launcherApps.setArchiveCompatibility(params);
diff --git a/src/com/android/launcher3/LauncherPrefs.kt b/src/com/android/launcher3/LauncherPrefs.kt
index cb19b14..875c407 100644
--- a/src/com/android/launcher3/LauncherPrefs.kt
+++ b/src/com/android/launcher3/LauncherPrefs.kt
@@ -297,64 +297,7 @@
@JvmField
val ICON_STATE =
nonRestorableItem("pref_icon_shape_path", "", EncryptionType.MOVE_TO_DEVICE_PROTECTED)
- @JvmField
- val ALL_APPS_OVERVIEW_THRESHOLD =
- nonRestorableItem(
- "pref_all_apps_overview_threshold",
- 180,
- EncryptionType.MOVE_TO_DEVICE_PROTECTED
- )
- @JvmField
- val LONG_PRESS_NAV_HANDLE_SLOP_PERCENTAGE =
- nonRestorableItem("LPNH_SLOP_PERCENTAGE", 100, EncryptionType.MOVE_TO_DEVICE_PROTECTED)
- @JvmField
- val LONG_PRESS_NAV_HANDLE_EXTRA_TOUCH_WIDTH_DP =
- nonRestorableItem(
- "LPNH_EXTRA_TOUCH_WIDTH_DP",
- 0,
- EncryptionType.MOVE_TO_DEVICE_PROTECTED
- )
- @JvmField
- val LONG_PRESS_NAV_HANDLE_TIMEOUT_MS =
- nonRestorableItem(
- "LPNH_TIMEOUT_MS",
- 450,
- EncryptionType.MOVE_TO_DEVICE_PROTECTED
- )
- @JvmField
- val LONG_PRESS_NAV_HANDLE_HAPTIC_HINT_START_SCALE_PERCENT =
- nonRestorableItem(
- "LPNH_HAPTIC_HINT_START_SCALE_PERCENT",
- 0,
- EncryptionType.MOVE_TO_DEVICE_PROTECTED
- )
- @JvmField
- val LONG_PRESS_NAV_HANDLE_HAPTIC_HINT_END_SCALE_PERCENT =
- nonRestorableItem(
- "LPNH_HAPTIC_HINT_END_SCALE_PERCENT",
- 100,
- EncryptionType.MOVE_TO_DEVICE_PROTECTED
- )
- @JvmField
- val LONG_PRESS_NAV_HANDLE_HAPTIC_HINT_SCALE_EXPONENT =
- nonRestorableItem(
- "LPNH_HAPTIC_HINT_SCALE_EXPONENT",
- 1,
- EncryptionType.MOVE_TO_DEVICE_PROTECTED
- )
- @JvmField
- val LONG_PRESS_NAV_HANDLE_HAPTIC_HINT_ITERATIONS =
- nonRestorableItem(
- "LPNH_HAPTIC_HINT_ITERATIONS",
- 50,
- EncryptionType.MOVE_TO_DEVICE_PROTECTED
- )
- @JvmField
- val LONG_PRESS_NAV_HANDLE_HAPTIC_HINT_DELAY =
- nonRestorableItem("LPNH_HAPTIC_HINT_DELAY", 0, EncryptionType.MOVE_TO_DEVICE_PROTECTED)
- @JvmField
- val PRIVATE_SPACE_APPS =
- nonRestorableItem("pref_private_space_apps", 0, EncryptionType.MOVE_TO_DEVICE_PROTECTED)
+
@JvmField
val ENABLE_TWOLINE_ALLAPPS_TOGGLE = backedUpItem("pref_enable_two_line_toggle", false)
@JvmField
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index d44438f..2b886e4 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -830,10 +830,4 @@
// No-Op
}
}
-
- /** Encapsulates two flag checks into a single one. */
- public static boolean enableSupportForArchiving() {
- return Flags.enableSupportForArchiving()
- || getSystemProperty("pm.archiving.enabled", "false").equals("true");
- }
}
diff --git a/src/com/android/launcher3/allapps/PrivateProfileManager.java b/src/com/android/launcher3/allapps/PrivateProfileManager.java
index be120cc..1def8a3 100644
--- a/src/com/android/launcher3/allapps/PrivateProfileManager.java
+++ b/src/com/android/launcher3/allapps/PrivateProfileManager.java
@@ -72,7 +72,7 @@
import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.model.data.PrivateSpaceInstallAppButtonInfo;
import com.android.launcher3.pm.UserCache;
-import com.android.launcher3.uioverrides.ApiWrapper;
+import com.android.launcher3.util.ApiWrapper;
import com.android.launcher3.util.Preconditions;
import com.android.launcher3.util.SettingsCache;
import com.android.launcher3.views.ActivityContext;
@@ -122,7 +122,9 @@
super(userManager, statsLogManager, userCache);
mAllApps = allApps;
mPrivateProfileMatcher = (user) -> userCache.getUserInfo(user).isPrivate();
- UI_HELPER_EXECUTOR.post(this::initializeInBackgroundThread);
+
+ Context appContext = allApps.getContext().getApplicationContext();
+ UI_HELPER_EXECUTOR.post(() -> initializeInBackgroundThread(appContext));
mPsHeaderHeight = mAllApps.getContext().getResources().getDimensionPixelSize(
R.dimen.ps_header_height);
}
@@ -187,7 +189,7 @@
/** Whether private profile should be hidden on Launcher. */
public boolean isPrivateSpaceHidden() {
return getCurrentState() == STATE_DISABLED && SettingsCache.INSTANCE
- .get(mAllApps.mActivityContext).getValue(PRIVATE_SPACE_HIDE_WHEN_LOCKED_URI, 0);
+ .get(mAllApps.getContext()).getValue(PRIVATE_SPACE_HIDE_WHEN_LOCKED_URI, 0);
}
/**
@@ -215,8 +217,8 @@
/** Opens the Private Space Settings Page. */
public void openPrivateSpaceSettings() {
if (mPrivateSpaceSettingsAvailable) {
- mAllApps.getContext()
- .startActivity(ApiWrapper.getPrivateSpaceSettingsIntent(mAllApps.getContext()));
+ mAllApps.getContext().startActivity(
+ ApiWrapper.INSTANCE.get(mAllApps.getContext()).getPrivateSpaceSettingsIntent());
}
}
@@ -239,33 +241,17 @@
* This case should still be ok, as locking the Private Space container and unlocking it,
* reloads the values, fixing the incorrect UI.
*/
- private void initializeInBackgroundThread() {
+ private void initializeInBackgroundThread(Context appContext) {
Preconditions.assertNonUiThread();
- setPreInstalledSystemPackages();
- setAppInstallerIntent();
- initializePrivateSpaceSettingsState();
- }
-
- private void initializePrivateSpaceSettingsState() {
- Preconditions.assertNonUiThread();
- Intent psSettingsIntent = ApiWrapper.getPrivateSpaceSettingsIntent(mAllApps.getContext());
- setPrivateSpaceSettingsAvailable(psSettingsIntent != null);
- }
-
- private void setPreInstalledSystemPackages() {
- Preconditions.assertNonUiThread();
- if (getProfileUser() != null) {
- mPreInstalledSystemPackages = new HashSet<>(ApiWrapper
- .getPreInstalledSystemPackages(mAllApps.getContext(), getProfileUser()));
+ ApiWrapper apiWrapper = ApiWrapper.INSTANCE.get(appContext);
+ UserHandle profileUser = getProfileUser();
+ if (profileUser != null) {
+ mPreInstalledSystemPackages = new HashSet<>(
+ apiWrapper.getPreInstalledSystemPackages(profileUser));
+ mAppInstallerIntent = apiWrapper
+ .getAppMarketActivityIntent(BuildConfig.APPLICATION_ID, profileUser);
}
- }
-
- private void setAppInstallerIntent() {
- Preconditions.assertNonUiThread();
- if (getProfileUser() != null) {
- mAppInstallerIntent = ApiWrapper.getAppMarketActivityIntent(mAllApps.getContext(),
- BuildConfig.APPLICATION_ID, getProfileUser());
- }
+ setPrivateSpaceSettingsAvailable(apiWrapper.getPrivateSpaceSettingsIntent() != null);
}
@VisibleForTesting
diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index e476138..d6ce2b3 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -17,14 +17,6 @@
package com.android.launcher3.config;
import static com.android.launcher3.BuildConfig.WIDGET_ON_FIRST_SCREEN;
-import static com.android.launcher3.LauncherPrefs.LONG_PRESS_NAV_HANDLE_EXTRA_TOUCH_WIDTH_DP;
-import static com.android.launcher3.LauncherPrefs.LONG_PRESS_NAV_HANDLE_HAPTIC_HINT_DELAY;
-import static com.android.launcher3.LauncherPrefs.LONG_PRESS_NAV_HANDLE_HAPTIC_HINT_END_SCALE_PERCENT;
-import static com.android.launcher3.LauncherPrefs.LONG_PRESS_NAV_HANDLE_HAPTIC_HINT_ITERATIONS;
-import static com.android.launcher3.LauncherPrefs.LONG_PRESS_NAV_HANDLE_HAPTIC_HINT_SCALE_EXPONENT;
-import static com.android.launcher3.LauncherPrefs.LONG_PRESS_NAV_HANDLE_HAPTIC_HINT_START_SCALE_PERCENT;
-import static com.android.launcher3.LauncherPrefs.LONG_PRESS_NAV_HANDLE_SLOP_PERCENTAGE;
-import static com.android.launcher3.LauncherPrefs.LONG_PRESS_NAV_HANDLE_TIMEOUT_MS;
import static com.android.launcher3.config.FeatureFlags.FlagState.DISABLED;
import static com.android.launcher3.config.FeatureFlags.FlagState.ENABLED;
import static com.android.launcher3.config.FeatureFlags.FlagState.TEAMFOOD;
@@ -38,10 +30,8 @@
import com.android.launcher3.BuildConfig;
import com.android.launcher3.Flags;
-import com.android.launcher3.uioverrides.flags.FlagsFactory;
import java.util.function.Predicate;
-import java.util.function.ToIntFunction;
/**
* Defines a set of flags used to control various launcher behaviors.
@@ -52,8 +42,6 @@
@VisibleForTesting
public static Predicate<BooleanFlag> sBooleanReader = f -> f.mCurrentValue;
- @VisibleForTesting
- public static ToIntFunction<IntFlag> sIntReader = f -> f.mCurrentValue;
private FeatureFlags() { }
@@ -130,42 +118,6 @@
getDebugFlag(275132633, "ENABLE_ALL_APPS_FROM_OVERVIEW", DISABLED,
"Allow entering All Apps from Overview (e.g. long swipe up from app)");
- public static final BooleanFlag CUSTOM_LPNH_THRESHOLDS =
- getReleaseFlag(301680992, "CUSTOM_LPNH_THRESHOLDS", ENABLED,
- "Add dev options and server side control to customize the LPNH "
- + "trigger slop and milliseconds");
-
- public static final BooleanFlag CUSTOM_LPH_THRESHOLDS = getReleaseFlag(331800576,
- "CUSTOM_LPH_THRESHOLDS", DISABLED,
- "Server side control to customize LPH timeout and touch slop");
-
- public static final BooleanFlag OVERRIDE_LPNH_LPH_THRESHOLDS = getReleaseFlag(331799727,
- "OVERRIDE_LPNH_LPH_THRESHOLDS", DISABLED,
- "Enable AGSA override for LPNH and LPH timeout and touch slop");
-
- public static final BooleanFlag ANIMATE_LPNH =
- getReleaseFlag(308693847, "ANIMATE_LPNH", TEAMFOOD,
- "Animates navbar when long pressing");
-
- public static final BooleanFlag SHRINK_NAV_HANDLE_ON_PRESS =
- getReleaseFlag(314158312, "SHRINK_NAV_HANDLE_ON_PRESS", DISABLED,
- "Shrinks navbar when long pressing if ANIMATE_LPNH is enabled");
-
- public static final IntFlag LPNH_SLOP_PERCENTAGE =
- FlagsFactory.getIntFlag(301680992, "LPNH_SLOP_PERCENTAGE", 100,
- "Controls touch slop percentage for lpnh",
- LONG_PRESS_NAV_HANDLE_SLOP_PERCENTAGE);
-
- public static final IntFlag LPNH_EXTRA_TOUCH_WIDTH_DP =
- FlagsFactory.getIntFlag(301680992, "LPNH_EXTRA_TOUCH_WIDTH_DP", 0,
- "Controls extra dp on the nav bar sides to trigger LPNH."
- + " Can be negative for a smaller touch region.",
- LONG_PRESS_NAV_HANDLE_EXTRA_TOUCH_WIDTH_DP);
-
- public static final IntFlag LPNH_TIMEOUT_MS =
- FlagsFactory.getIntFlag(301680992, "LPNH_TIMEOUT_MS", 450,
- "Controls lpnh timeout in milliseconds", LONG_PRESS_NAV_HANDLE_TIMEOUT_MS);
-
public static final BooleanFlag ENABLE_SHOW_KEYBOARD_OPTION_IN_ALL_APPS = getReleaseFlag(
270394468, "ENABLE_SHOW_KEYBOARD_OPTION_IN_ALL_APPS", ENABLED,
"Enable option to show keyboard when going to all-apps");
@@ -296,49 +248,6 @@
"INJECT_FALLBACK_APP_CORPUS_RESULTS", DISABLED,
"Inject fallback app corpus result when AiAi fails to return it.");
- public static final BooleanFlag ENABLE_LONG_PRESS_NAV_HANDLE =
- getReleaseFlag(299682306, "ENABLE_LONG_PRESS_NAV_HANDLE", ENABLED,
- "Enables long pressing on the bottom bar nav handle to trigger events.");
-
- public static final BooleanFlag ENABLE_SEARCH_HAPTIC_HINT =
- getReleaseFlag(314005131, "ENABLE_SEARCH_HAPTIC_HINT", ENABLED,
- "Enables haptic hint while long pressing on the bottom bar nav handle.");
-
- public static final BooleanFlag ENABLE_SEARCH_HAPTIC_COMMIT =
- getReleaseFlag(314005577, "ENABLE_SEARCH_HAPTIC_COMMIT", ENABLED,
- "Enables haptic hint at end of long pressing on the bottom bar nav handle.");
-
- public static final IntFlag LPNH_HAPTIC_HINT_START_SCALE_PERCENT =
- FlagsFactory.getIntFlag(309972570,
- "LPNH_HAPTIC_HINT_START_SCALE_PERCENT", 0,
- "Haptic hint start scale.",
- LONG_PRESS_NAV_HANDLE_HAPTIC_HINT_START_SCALE_PERCENT);
-
- public static final IntFlag LPNH_HAPTIC_HINT_END_SCALE_PERCENT =
- FlagsFactory.getIntFlag(309972570,
- "LPNH_HAPTIC_HINT_END_SCALE_PERCENT", 100,
- "Haptic hint end scale.", LONG_PRESS_NAV_HANDLE_HAPTIC_HINT_END_SCALE_PERCENT);
-
- public static final IntFlag LPNH_HAPTIC_HINT_SCALE_EXPONENT =
- FlagsFactory.getIntFlag(309972570,
- "LPNH_HAPTIC_HINT_SCALE_EXPONENT", 1,
- "Haptic hint scale exponent.",
- LONG_PRESS_NAV_HANDLE_HAPTIC_HINT_SCALE_EXPONENT);
-
- public static final IntFlag LPNH_HAPTIC_HINT_ITERATIONS =
- FlagsFactory.getIntFlag(309972570, "LPNH_HAPTIC_HINT_ITERATIONS",
- 50,
- "Haptic hint number of iterations.",
- LONG_PRESS_NAV_HANDLE_HAPTIC_HINT_ITERATIONS);
-
- public static final BooleanFlag ENABLE_LPNH_DEEP_PRESS =
- getReleaseFlag(310952290, "ENABLE_LPNH_DEEP_PRESS", ENABLED,
- "Long press of nav handle is instantly triggered if deep press is detected.");
-
- public static final IntFlag LPNH_HAPTIC_HINT_DELAY =
- FlagsFactory.getIntFlag(309972570, "LPNH_HAPTIC_HINT_DELAY", 0,
- "Delay before haptic hint starts.", LONG_PRESS_NAV_HANDLE_HAPTIC_HINT_DELAY);
-
// TODO(Block 17): Clean up flags
// Aconfig migration complete for ENABLE_TASKBAR_PINNING.
private static final BooleanFlag ENABLE_TASKBAR_PINNING = getDebugFlag(296231746,
@@ -515,22 +424,6 @@
}
/**
- * Class representing an integer flag
- */
- public static class IntFlag {
-
- private final int mCurrentValue;
-
- public IntFlag(int currentValue) {
- mCurrentValue = currentValue;
- }
-
- public int get() {
- return sIntReader.applyAsInt(this);
- }
- }
-
- /**
* Enabled state for a flag
*/
public enum FlagState {
diff --git a/src/com/android/launcher3/dragndrop/AddItemActivity.java b/src/com/android/launcher3/dragndrop/AddItemActivity.java
index 29aa216..05fdcef 100644
--- a/src/com/android/launcher3/dragndrop/AddItemActivity.java
+++ b/src/com/android/launcher3/dragndrop/AddItemActivity.java
@@ -67,7 +67,7 @@
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.PackageItemInfo;
import com.android.launcher3.pm.PinRequestHelper;
-import com.android.launcher3.uioverrides.ApiWrapper;
+import com.android.launcher3.util.ApiWrapper;
import com.android.launcher3.util.PackageManagerHelper;
import com.android.launcher3.util.SystemUiController;
import com.android.launcher3.views.AbstractSlideInView;
@@ -259,7 +259,8 @@
.setPackage(getPackageName())
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
Launcher.ACTIVITY_TRACKER.registerCallback(listener, "AddItemActivity.onLongClick");
- startActivity(homeIntent, ApiWrapper.createFadeOutAnimOptions(this).toBundle());
+ startActivity(homeIntent,
+ ApiWrapper.INSTANCE.get(this).createFadeOutAnimOptions().toBundle());
logCommand(LAUNCHER_ADD_EXTERNAL_ITEM_DRAGGED);
mFinishOnPause = true;
return false;
diff --git a/src/com/android/launcher3/icons/IconCache.java b/src/com/android/launcher3/icons/IconCache.java
index 1633eba..af704a8 100644
--- a/src/com/android/launcher3/icons/IconCache.java
+++ b/src/com/android/launcher3/icons/IconCache.java
@@ -51,6 +51,7 @@
import androidx.annotation.VisibleForTesting;
import androidx.core.util.Pair;
+import com.android.launcher3.Flags;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.Utilities;
import com.android.launcher3.icons.ComponentWithLabel.ComponentCachingLogic;
@@ -248,7 +249,7 @@
@SuppressWarnings("NewApi")
public synchronized void getTitleAndIcon(ItemInfoWithIcon info,
LauncherActivityInfo activityInfo, boolean useLowResIcon) {
- boolean isAppArchived = Utilities.enableSupportForArchiving() && activityInfo != null
+ boolean isAppArchived = Flags.enableSupportForArchiving() && activityInfo != null
&& activityInfo.getActivityInfo().isArchived;
// If we already have activity info, no need to use package icon
getTitleAndIcon(info, () -> activityInfo, isAppArchived, useLowResIcon,
diff --git a/src/com/android/launcher3/logging/StatsLogManager.java b/src/com/android/launcher3/logging/StatsLogManager.java
index e8f8ae2..441bbb5 100644
--- a/src/com/android/launcher3/logging/StatsLogManager.java
+++ b/src/com/android/launcher3/logging/StatsLogManager.java
@@ -218,6 +218,9 @@
@UiEvent(doc = "User tapped on free form icon on a task menu.")
LAUNCHER_SYSTEM_SHORTCUT_FREE_FORM_TAP(519),
+ @UiEvent(doc = "User tapped on desktop icon on a task menu.")
+ LAUNCHER_SYSTEM_SHORTCUT_DESKTOP_TAP(1706),
+
@UiEvent(doc = "User tapped on pause app system shortcut.")
LAUNCHER_SYSTEM_SHORTCUT_PAUSE_TAP(521),
diff --git a/src/com/android/launcher3/model/AllAppsList.java b/src/com/android/launcher3/model/AllAppsList.java
index 8659471..8c5ea79 100644
--- a/src/com/android/launcher3/model/AllAppsList.java
+++ b/src/com/android/launcher3/model/AllAppsList.java
@@ -33,6 +33,7 @@
import androidx.annotation.Nullable;
import com.android.launcher3.AppFilter;
+import com.android.launcher3.Flags;
import com.android.launcher3.Utilities;
import com.android.launcher3.compat.AlphabeticIndexCompat;
import com.android.launcher3.icons.IconCache;
@@ -330,7 +331,7 @@
PackageManagerHelper.getLoadingProgress(info),
PackageInstallInfo.STATUS_INSTALLED_DOWNLOADING);
applicationInfo.intent = launchIntent;
- if (Utilities.enableSupportForArchiving()) {
+ if (Flags.enableSupportForArchiving()) {
// In case an app is archived, the respective item flag corresponding to
// archiving should also be applied during package updates
if (info.getActivityInfo().isArchived) {
diff --git a/src/com/android/launcher3/model/ItemInstallQueue.java b/src/com/android/launcher3/model/ItemInstallQueue.java
index d350879..90aba2a 100644
--- a/src/com/android/launcher3/model/ItemInstallQueue.java
+++ b/src/com/android/launcher3/model/ItemInstallQueue.java
@@ -40,6 +40,7 @@
import androidx.annotation.Nullable;
import androidx.annotation.WorkerThread;
+import com.android.launcher3.Flags;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAppState;
@@ -300,7 +301,7 @@
} else {
lai = laiList.get(0);
si.intent = makeLaunchIntent(lai);
- if (Utilities.enableSupportForArchiving()
+ if (Flags.enableSupportForArchiving()
&& lai.getActivityInfo().isArchived) {
si.runtimeStatusFlags |= FLAG_ARCHIVED;
}
diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java
index e0ced83..ac4c087 100644
--- a/src/com/android/launcher3/model/LoaderTask.java
+++ b/src/com/android/launcher3/model/LoaderTask.java
@@ -421,7 +421,7 @@
final HashMap<PackageUserKey, SessionInfo> installingPkgs =
mSessionHelper.getActiveSessions();
- if (Utilities.enableSupportForArchiving()) {
+ if (Flags.enableSupportForArchiving()) {
mInstallingPkgsCached = installingPkgs;
}
installingPkgs.forEach(mApp.getIconCache()::updateSessionCache);
@@ -695,7 +695,7 @@
for (int i = 0; i < apps.size(); i++) {
LauncherActivityInfo app = apps.get(i);
AppInfo appInfo = new AppInfo(app, mUserCache.getUserInfo(user), quietMode);
- if (Utilities.enableSupportForArchiving() && app.getApplicationInfo().isArchived) {
+ if (Flags.enableSupportForArchiving() && app.getApplicationInfo().isArchived) {
// For archived apps, include progress info in case there is a pending
// install session post restart of device.
String appPackageName = app.getApplicationInfo().packageName;
diff --git a/src/com/android/launcher3/model/PackageUpdatedTask.java b/src/com/android/launcher3/model/PackageUpdatedTask.java
index ea1ae2e..1923065 100644
--- a/src/com/android/launcher3/model/PackageUpdatedTask.java
+++ b/src/com/android/launcher3/model/PackageUpdatedTask.java
@@ -48,7 +48,7 @@
import com.android.launcher3.pm.PackageInstallInfo;
import com.android.launcher3.pm.UserCache;
import com.android.launcher3.shortcuts.ShortcutRequest;
-import com.android.launcher3.uioverrides.ApiWrapper;
+import com.android.launcher3.util.ApiWrapper;
import com.android.launcher3.util.FlagOp;
import com.android.launcher3.util.IntSet;
import com.android.launcher3.util.ItemInfoMatcher;
@@ -276,7 +276,7 @@
PackageInstallInfo.STATUS_INSTALLED_DOWNLOADING);
// In case an app is archived, we need to make sure that archived state
// in WorkspaceItemInfo is refreshed.
- if (Utilities.enableSupportForArchiving() && !activities.isEmpty()) {
+ if (Flags.enableSupportForArchiving() && !activities.isEmpty()) {
boolean newArchivalState = activities.get(
0).getActivityInfo().isArchived;
if (newArchivalState != si.isArchived()) {
@@ -286,7 +286,7 @@
}
if (si.itemType == Favorites.ITEM_TYPE_APPLICATION) {
if (activities != null && !activities.isEmpty()) {
- si.status = ApiWrapper
+ si.status = ApiWrapper.INSTANCE.get(context)
.isNonResizeableActivity(activities.get(0))
? si.status | WorkspaceItemInfo.FLAG_NON_RESIZEABLE
: si.status & ~WorkspaceItemInfo.FLAG_NON_RESIZEABLE;
diff --git a/src/com/android/launcher3/model/WorkspaceItemProcessor.kt b/src/com/android/launcher3/model/WorkspaceItemProcessor.kt
index a900d1f..ee45c0f 100644
--- a/src/com/android/launcher3/model/WorkspaceItemProcessor.kt
+++ b/src/com/android/launcher3/model/WorkspaceItemProcessor.kt
@@ -26,6 +26,7 @@
import android.text.TextUtils
import android.util.Log
import android.util.LongSparseArray
+import com.android.launcher3.Flags
import com.android.launcher3.InvariantDeviceProfile
import com.android.launcher3.LauncherAppState
import com.android.launcher3.LauncherSettings.Favorites
@@ -41,7 +42,7 @@
import com.android.launcher3.model.data.WorkspaceItemInfo
import com.android.launcher3.pm.PackageInstallInfo
import com.android.launcher3.shortcuts.ShortcutKey
-import com.android.launcher3.uioverrides.ApiWrapper
+import com.android.launcher3.util.ApiWrapper
import com.android.launcher3.util.ComponentKey
import com.android.launcher3.util.PackageManagerHelper
import com.android.launcher3.util.PackageUserKey
@@ -325,7 +326,7 @@
}
val activityInfo = c.launcherActivityInfo
if (activityInfo != null) {
- if (ApiWrapper.isNonResizeableActivity(activityInfo)) {
+ if (ApiWrapper.INSTANCE.get(app.context).isNonResizeableActivity(activityInfo)) {
info.status = info.status or WorkspaceItemInfo.FLAG_NON_RESIZEABLE
}
info.setProgressLevel(
@@ -335,7 +336,7 @@
}
if (
(c.restoreFlag != 0 ||
- Utilities.enableSupportForArchiving() &&
+ Flags.enableSupportForArchiving() &&
activityInfo != null &&
activityInfo.applicationInfo.isArchived) && !TextUtils.isEmpty(targetPkg)
) {
@@ -347,7 +348,7 @@
ItemInfoWithIcon.FLAG_INSTALL_SESSION_ACTIVE.inv()
} else if (
activityInfo == null ||
- (Utilities.enableSupportForArchiving() &&
+ (Flags.enableSupportForArchiving() &&
activityInfo.applicationInfo.isArchived)
) {
// For archived apps, include progress info in case there is
@@ -479,7 +480,7 @@
!isSafeMode &&
(si == null) &&
(lapi == null) &&
- !(Utilities.enableSupportForArchiving() &&
+ !(Flags.enableSupportForArchiving() &&
pmHelper.isAppArchived(component.packageName))
) {
// Restore never started
diff --git a/src/com/android/launcher3/model/data/AppInfo.java b/src/com/android/launcher3/model/data/AppInfo.java
index 210d720..93ba619 100644
--- a/src/com/android/launcher3/model/data/AppInfo.java
+++ b/src/com/android/launcher3/model/data/AppInfo.java
@@ -181,7 +181,7 @@
if (PackageManagerHelper.isAppSuspended(appInfo)) {
info.runtimeStatusFlags |= FLAG_DISABLED_SUSPENDED;
}
- if (Utilities.enableSupportForArchiving() && lai.getActivityInfo().isArchived) {
+ if (Flags.enableSupportForArchiving() && lai.getActivityInfo().isArchived) {
info.runtimeStatusFlags |= FLAG_ARCHIVED;
}
info.runtimeStatusFlags |= (appInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0
diff --git a/src/com/android/launcher3/model/data/ItemInfoWithIcon.java b/src/com/android/launcher3/model/data/ItemInfoWithIcon.java
index be3aa10..3a74ff2 100644
--- a/src/com/android/launcher3/model/data/ItemInfoWithIcon.java
+++ b/src/com/android/launcher3/model/data/ItemInfoWithIcon.java
@@ -22,13 +22,14 @@
import androidx.annotation.Nullable;
+import com.android.launcher3.Flags;
import com.android.launcher3.Utilities;
import com.android.launcher3.icons.BitmapInfo;
import com.android.launcher3.icons.BitmapInfo.DrawableCreationFlags;
import com.android.launcher3.icons.FastBitmapDrawable;
import com.android.launcher3.logging.FileLog;
import com.android.launcher3.pm.PackageInstallInfo;
-import com.android.launcher3.uioverrides.ApiWrapper;
+import com.android.launcher3.util.ApiWrapper;
/**
* Represents an ItemInfo which also holds an icon.
@@ -162,7 +163,7 @@
* Returns true if the app corresponding to the item is archived.
*/
public boolean isArchived() {
- if (!Utilities.enableSupportForArchiving()) {
+ if (!Flags.enableSupportForArchiving()) {
return false;
}
return (runtimeStatusFlags & FLAG_ARCHIVED) != 0;
@@ -251,8 +252,8 @@
String targetPackage = getTargetPackage();
return targetPackage != null
- ? ApiWrapper.getAppMarketActivityIntent(
- context, targetPackage, Process.myUserHandle())
+ ? ApiWrapper.INSTANCE.get(context).getAppMarketActivityIntent(
+ targetPackage, Process.myUserHandle())
: null;
}
diff --git a/src/com/android/launcher3/model/data/WorkspaceItemInfo.java b/src/com/android/launcher3/model/data/WorkspaceItemInfo.java
index 9917ad7..5ae7003 100644
--- a/src/com/android/launcher3/model/data/WorkspaceItemInfo.java
+++ b/src/com/android/launcher3/model/data/WorkspaceItemInfo.java
@@ -32,7 +32,7 @@
import com.android.launcher3.icons.IconCache;
import com.android.launcher3.pm.UserCache;
import com.android.launcher3.shortcuts.ShortcutKey;
-import com.android.launcher3.uioverrides.ApiWrapper;
+import com.android.launcher3.util.ApiWrapper;
import com.android.launcher3.util.ContentWriter;
import java.util.Arrays;
@@ -201,7 +201,7 @@
runtimeStatusFlags &= ~FLAG_DISABLED_VERSION_LOWER;
}
- Person[] persons = ApiWrapper.getPersons(shortcutInfo);
+ Person[] persons = ApiWrapper.INSTANCE.get(context).getPersons(shortcutInfo);
personKeys = persons.length == 0 ? Utilities.EMPTY_STRING_ARRAY
: Arrays.stream(persons).map(Person::getKey).sorted().toArray(String[]::new);
}
diff --git a/src/com/android/launcher3/pm/InstallSessionHelper.java b/src/com/android/launcher3/pm/InstallSessionHelper.java
index f3769d5..4a3318e 100644
--- a/src/com/android/launcher3/pm/InstallSessionHelper.java
+++ b/src/com/android/launcher3/pm/InstallSessionHelper.java
@@ -29,6 +29,7 @@
import androidx.annotation.Nullable;
import androidx.annotation.WorkerThread;
+import com.android.launcher3.Flags;
import com.android.launcher3.LauncherPrefs;
import com.android.launcher3.SessionCommitReceiver;
import com.android.launcher3.Utilities;
@@ -217,7 +218,7 @@
&& !promiseIconAddedForId(sessionInfo.getSessionId())) {
// In case of unarchival, we do not want to add a workspace promise icon if one is
// not already present. For general app installations however, we do support it.
- if (!Utilities.enableSupportForArchiving() || !sessionInfo.isUnarchival()) {
+ if (!Flags.enableSupportForArchiving() || !sessionInfo.isUnarchival()) {
FileLog.d(LOG, "Adding package name to install queue: "
+ sessionInfo.getAppPackageName());
@@ -232,7 +233,7 @@
public boolean verifySessionInfo(@Nullable final PackageInstaller.SessionInfo sessionInfo) {
// For archived apps we always want to show promise icons and the checks below don't apply.
- if (Utilities.enableSupportForArchiving() && sessionInfo != null
+ if (Flags.enableSupportForArchiving() && sessionInfo != null
&& sessionInfo.isUnarchival()) {
return true;
}
diff --git a/src/com/android/launcher3/pm/InstallSessionTracker.java b/src/com/android/launcher3/pm/InstallSessionTracker.java
index eacbc11..24d58f3 100644
--- a/src/com/android/launcher3/pm/InstallSessionTracker.java
+++ b/src/com/android/launcher3/pm/InstallSessionTracker.java
@@ -31,6 +31,7 @@
import androidx.annotation.Nullable;
import androidx.annotation.WorkerThread;
+import com.android.launcher3.Flags;
import com.android.launcher3.Utilities;
import com.android.launcher3.util.PackageUserKey;
@@ -80,7 +81,7 @@
helper.tryQueuePromiseAppIcon(sessionInfo);
- if (Utilities.enableSupportForArchiving() && sessionInfo != null
+ if (Flags.enableSupportForArchiving() && sessionInfo != null
&& sessionInfo.isUnarchival()) {
// For archived apps, icon could already be present on the workspace. To make sure
// the icon state is updated, we send a change event.
diff --git a/src/com/android/launcher3/pm/UserCache.java b/src/com/android/launcher3/pm/UserCache.java
index 032de31..185e0b5 100644
--- a/src/com/android/launcher3/pm/UserCache.java
+++ b/src/com/android/launcher3/pm/UserCache.java
@@ -17,7 +17,6 @@
package com.android.launcher3.pm;
import static com.android.launcher3.Utilities.ATLEAST_U;
-import static com.android.launcher3.uioverrides.ApiWrapper.queryAllUsers;
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
import android.content.Context;
@@ -34,6 +33,7 @@
import com.android.launcher3.icons.BitmapInfo;
import com.android.launcher3.icons.UserBadgeDrawable;
+import com.android.launcher3.util.ApiWrapper;
import com.android.launcher3.util.FlagOp;
import com.android.launcher3.util.MainThreadInitializedObject;
import com.android.launcher3.util.SafeCloseable;
@@ -119,7 +119,7 @@
@WorkerThread
private void updateCache() {
- mUserToSerialMap = queryAllUsers(mContext);
+ mUserToSerialMap = ApiWrapper.INSTANCE.get(mContext).queryAllUsers();
}
/**
diff --git a/src/com/android/launcher3/popup/SystemShortcut.java b/src/com/android/launcher3/popup/SystemShortcut.java
index 0af7e67..f56d732 100644
--- a/src/com/android/launcher3/popup/SystemShortcut.java
+++ b/src/com/android/launcher3/popup/SystemShortcut.java
@@ -22,6 +22,7 @@
import androidx.annotation.Nullable;
import com.android.launcher3.AbstractFloatingView;
+import com.android.launcher3.AbstractFloatingViewHelper;
import com.android.launcher3.Flags;
import com.android.launcher3.R;
import com.android.launcher3.SecondaryDropTarget;
@@ -31,7 +32,7 @@
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.pm.UserCache;
-import com.android.launcher3.uioverrides.ApiWrapper;
+import com.android.launcher3.util.ApiWrapper;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.InstantAppResolver;
import com.android.launcher3.util.PackageManagerHelper;
@@ -61,23 +62,23 @@
protected final ItemInfo mItemInfo;
protected final View mOriginalView;
+ private final AbstractFloatingViewHelper mAbstractFloatingViewHelper;
+
public SystemShortcut(int iconResId, int labelResId, T target, ItemInfo itemInfo,
View originalView) {
+ this(iconResId, labelResId, target, itemInfo, originalView,
+ new AbstractFloatingViewHelper());
+ }
+
+ public SystemShortcut(int iconResId, int labelResId, T target, ItemInfo itemInfo,
+ View originalView, AbstractFloatingViewHelper abstractFloatingViewHelper) {
mIconResId = iconResId;
mLabelResId = labelResId;
mAccessibilityActionId = labelResId;
mTarget = target;
mItemInfo = itemInfo;
mOriginalView = originalView;
- }
-
- public SystemShortcut(SystemShortcut<T> other) {
- mIconResId = other.mIconResId;
- mLabelResId = other.mLabelResId;
- mAccessibilityActionId = other.mAccessibilityActionId;
- mTarget = other.mTarget;
- mItemInfo = other.mItemInfo;
- mOriginalView = other.mOriginalView;
+ mAbstractFloatingViewHelper = abstractFloatingViewHelper;
}
public void setIconAndLabelFor(View iconView, TextView labelView) {
@@ -178,7 +179,7 @@
@Override
public void onClick(View view) {
- dismissTaskMenuView(mTarget);
+ dismissTaskMenuView();
Rect sourceBounds = Utilities.getViewBounds(view);
new PackageManagerHelper(view.getContext()).startDetailsActivityForInfo(
mItemInfo, sourceBounds, ActivityOptions.makeBasic().toBundle());
@@ -259,10 +260,8 @@
@Override
public void onClick(View view) {
Intent intent =
- ApiWrapper.getAppMarketActivityIntent(
- view.getContext(),
- mItemInfo.getTargetComponent().getPackageName(),
- mSpaceUser);
+ ApiWrapper.INSTANCE.get(view.getContext()).getAppMarketActivityIntent(
+ mItemInfo.getTargetComponent().getPackageName(), mSpaceUser);
mTarget.startActivitySafely(view, intent, mItemInfo);
AbstractFloatingView.closeAllOpenViews(mTarget);
mTarget.getStatsLogManager()
@@ -303,9 +302,8 @@
@Override
public void onClick(View view) {
- Intent intent = ApiWrapper.getAppMarketActivityIntent(view.getContext(),
- mItemInfo.getTargetComponent().getPackageName(),
- Process.myUserHandle());
+ Intent intent = ApiWrapper.INSTANCE.get(view.getContext()).getAppMarketActivityIntent(
+ mItemInfo.getTargetComponent().getPackageName(), Process.myUserHandle());
mTarget.startActivitySafely(view, intent, mItemInfo);
AbstractFloatingView.closeAllOpenViews(mTarget);
}
@@ -327,7 +325,7 @@
@Override
public void onClick(View view) {
- dismissTaskMenuView(mTarget);
+ dismissTaskMenuView();
mTarget.getStatsLogManager().logger()
.withItemInfo(mItemInfo)
.log(LAUNCHER_SYSTEM_SHORTCUT_DONT_SUGGEST_APP_TAP);
@@ -370,7 +368,7 @@
@Override
public void onClick(View view) {
- dismissTaskMenuView(mTarget);
+ dismissTaskMenuView();
SecondaryDropTarget.performUninstall(view.getContext(), mComponentName, mItemInfo);
mTarget.getStatsLogManager()
.logger()
@@ -379,8 +377,8 @@
}
}
- public static <T extends ActivityContext> void dismissTaskMenuView(T activity) {
- AbstractFloatingView.closeOpenViews(activity, true,
+ protected void dismissTaskMenuView() {
+ mAbstractFloatingViewHelper.closeOpenViews(mTarget, true,
AbstractFloatingView.TYPE_ALL & ~AbstractFloatingView.TYPE_REBIND_SAFE);
}
}
diff --git a/src/com/android/launcher3/provider/RestoreDbTask.java b/src/com/android/launcher3/provider/RestoreDbTask.java
index b4e6365..d702e51 100644
--- a/src/com/android/launcher3/provider/RestoreDbTask.java
+++ b/src/com/android/launcher3/provider/RestoreDbTask.java
@@ -70,7 +70,7 @@
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.pm.UserCache;
import com.android.launcher3.provider.LauncherDbUtils.SQLiteTransaction;
-import com.android.launcher3.uioverrides.ApiWrapper;
+import com.android.launcher3.util.ApiWrapper;
import com.android.launcher3.util.ContentWriter;
import com.android.launcher3.util.IntArray;
import com.android.launcher3.util.LogConfig;
@@ -560,9 +560,8 @@
protected static void maybeOverrideShortcuts(Context context, ModelDbController controller,
SQLiteDatabase db, long currentUser) {
- Map<String, LauncherActivityInfo> activityOverrides = ApiWrapper.getActivityOverrides(
- context);
-
+ Map<String, LauncherActivityInfo> activityOverrides =
+ ApiWrapper.INSTANCE.get(context).getActivityOverrides();
if (activityOverrides == null || activityOverrides.isEmpty()) {
return;
}
diff --git a/src/com/android/launcher3/touch/ItemClickHandler.java b/src/com/android/launcher3/touch/ItemClickHandler.java
index 50df775..2a9ebbd 100644
--- a/src/com/android/launcher3/touch/ItemClickHandler.java
+++ b/src/com/android/launcher3/touch/ItemClickHandler.java
@@ -41,6 +41,7 @@
import com.android.launcher3.BubbleTextView;
import com.android.launcher3.BuildConfig;
+import com.android.launcher3.Flags;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherSettings;
@@ -63,7 +64,7 @@
import com.android.launcher3.shortcuts.ShortcutKey;
import com.android.launcher3.testing.TestLogging;
import com.android.launcher3.testing.shared.TestProtocol;
-import com.android.launcher3.uioverrides.ApiWrapper;
+import com.android.launcher3.util.ApiWrapper;
import com.android.launcher3.util.ItemInfoMatcher;
import com.android.launcher3.views.FloatingIconView;
import com.android.launcher3.views.Snackbar;
@@ -234,7 +235,7 @@
}
}
// Fallback to using custom market intent.
- Intent intent = ApiWrapper.getAppMarketActivityIntent(launcher,
+ Intent intent = ApiWrapper.INSTANCE.get(launcher).getAppMarketActivityIntent(
packageName, Process.myUserHandle());
launcher.startActivitySafely(v, intent, item);
};
@@ -346,7 +347,7 @@
// Check for abandoned promise
if ((v instanceof BubbleTextView) && shortcut.hasPromiseIconUi()
- && (!Utilities.enableSupportForArchiving() || !shortcut.isArchived())) {
+ && (!Flags.enableSupportForArchiving() || !shortcut.isArchived())) {
String packageName = shortcut.getIntent().getComponent() != null
? shortcut.getIntent().getComponent().getPackageName()
: shortcut.getIntent().getPackage();
@@ -372,12 +373,12 @@
if (item instanceof ItemInfoWithIcon itemInfoWithIcon) {
if ((itemInfoWithIcon.runtimeStatusFlags
& ItemInfoWithIcon.FLAG_INSTALL_SESSION_ACTIVE) != 0) {
- intent = ApiWrapper.getAppMarketActivityIntent(launcher,
+ intent = ApiWrapper.INSTANCE.get(launcher).getAppMarketActivityIntent(
itemInfoWithIcon.getTargetComponent().getPackageName(),
Process.myUserHandle());
} else if (itemInfoWithIcon.itemType
== LauncherSettings.Favorites.ITEM_TYPE_PRIVATE_SPACE_INSTALL_APP_BUTTON) {
- intent = ApiWrapper.getAppMarketActivityIntent(launcher,
+ intent = ApiWrapper.INSTANCE.get(launcher).getAppMarketActivityIntent(
BuildConfig.APPLICATION_ID,
launcher.getAppsView().getPrivateProfileManager().getProfileUser());
launcher.getStatsLogManager().logger().log(
diff --git a/src_no_quickstep/com/android/launcher3/uioverrides/ApiWrapper.java b/src/com/android/launcher3/util/ApiWrapper.java
similarity index 67%
rename from src_no_quickstep/com/android/launcher3/uioverrides/ApiWrapper.java
rename to src/com/android/launcher3/util/ApiWrapper.java
index 90271c1..6429a43 100644
--- a/src_no_quickstep/com/android/launcher3/uioverrides/ApiWrapper.java
+++ b/src/com/android/launcher3/util/ApiWrapper.java
@@ -14,7 +14,9 @@
* limitations under the License.
*/
-package com.android.launcher3.uioverrides;
+package com.android.launcher3.util;
+
+import static com.android.launcher3.util.MainThreadInitializedObject.forOverride;
import android.app.ActivityOptions;
import android.app.Person;
@@ -28,10 +30,12 @@
import android.os.UserManager;
import android.util.ArrayMap;
-import com.android.launcher3.Utilities;
-import com.android.launcher3.util.UserIconInfo;
+import androidx.annotation.Nullable;
-import java.util.ArrayList;
+import com.android.launcher3.BuildConfig;
+import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
+
import java.util.Collections;
import java.util.List;
import java.util.Map;
@@ -39,30 +43,40 @@
/**
* A wrapper for the hidden API calls
*/
-public class ApiWrapper {
+public class ApiWrapper implements ResourceBasedOverride, SafeCloseable {
- public static final boolean TASKBAR_DRAWN_IN_PROCESS = false;
+ public static final MainThreadInitializedObject<ApiWrapper> INSTANCE =
+ forOverride(ApiWrapper.class, R.string.api_wrapper_class);
- public static Person[] getPersons(ShortcutInfo si) {
+ protected final Context mContext;
+
+ public ApiWrapper(Context context) {
+ mContext = context;
+ }
+
+ /**
+ * Returns the list of persons associated with the provided shortcut info
+ */
+ public Person[] getPersons(ShortcutInfo si) {
return Utilities.EMPTY_PERSON_ARRAY;
}
- public static Map<String, LauncherActivityInfo> getActivityOverrides(Context context) {
+ public Map<String, LauncherActivityInfo> getActivityOverrides() {
return Collections.emptyMap();
}
/**
* Creates an ActivityOptions to play fade-out animation on closing targets
*/
- public static ActivityOptions createFadeOutAnimOptions(Context context) {
- return ActivityOptions.makeCustomAnimation(context, 0, android.R.anim.fade_out);
+ public ActivityOptions createFadeOutAnimOptions() {
+ return ActivityOptions.makeCustomAnimation(mContext, 0, android.R.anim.fade_out);
}
/**
* Returns a map of all users on the device to their corresponding UI properties
*/
- public static Map<UserHandle, UserIconInfo> queryAllUsers(Context context) {
- UserManager um = context.getSystemService(UserManager.class);
+ public Map<UserHandle, UserIconInfo> queryAllUsers() {
+ UserManager um = mContext.getSystemService(UserManager.class);
Map<UserHandle, UserIconInfo> users = new ArrayMap<>();
List<UserHandle> usersActual = um.getUserProfiles();
if (usersActual != null) {
@@ -72,7 +86,7 @@
// Simple check to check if the provided user is work profile
// TODO: Migrate to a better platform API
NoopDrawable d = new NoopDrawable();
- boolean isWork = (d != context.getPackageManager().getUserBadgedIcon(d, user));
+ boolean isWork = (d != mContext.getPackageManager().getUserBadgedIcon(d, user));
UserIconInfo info = new UserIconInfo(
user,
isWork ? UserIconInfo.TYPE_WORK : UserIconInfo.TYPE_MAIN,
@@ -87,16 +101,15 @@
* Returns the list of the system packages that are installed at user creation.
* An empty list denotes that all system packages are installed for that user at creation.
*/
- public static List<String> getPreInstalledSystemPackages(Context context, UserHandle user) {
- return new ArrayList<>();
+ public List<String> getPreInstalledSystemPackages(UserHandle user) {
+ return Collections.emptyList();
}
/**
* Returns an intent which can be used to start the App Market activity (Installer
* Activity).
*/
- public static Intent getAppMarketActivityIntent(Context context, String packageName,
- UserHandle user) {
+ public Intent getAppMarketActivityIntent(String packageName, UserHandle user) {
return new Intent(Intent.ACTION_VIEW)
.setData(new Uri.Builder()
.scheme("market")
@@ -104,24 +117,27 @@
.appendQueryParameter("id", packageName)
.build())
.putExtra(Intent.EXTRA_REFERRER, new Uri.Builder().scheme("android-app")
- .authority(context.getPackageName()).build());
+ .authority(BuildConfig.APPLICATION_ID).build());
}
/**
* Returns an intent which can be used to open Private Space Settings.
*/
- public static Intent getPrivateSpaceSettingsIntent(Context context) {
+ @Nullable
+ public Intent getPrivateSpaceSettingsIntent() {
return null;
}
/**
* Checks if an activity is flagged as non-resizeable.
*/
- public static boolean isNonResizeableActivity(LauncherActivityInfo lai) {
+ public boolean isNonResizeableActivity(LauncherActivityInfo lai) {
// Overridden in quickstep
return false;
}
+ @Override
+ public void close() { }
private static class NoopDrawable extends ColorDrawable {
@Override
diff --git a/src/com/android/launcher3/util/PackageManagerHelper.java b/src/com/android/launcher3/util/PackageManagerHelper.java
index 316506a..608bed7 100644
--- a/src/com/android/launcher3/util/PackageManagerHelper.java
+++ b/src/com/android/launcher3/util/PackageManagerHelper.java
@@ -16,6 +16,8 @@
package com.android.launcher3.util;
+import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_INSTALL_SESSION_ACTIVE;
+
import android.content.ActivityNotFoundException;
import android.content.ComponentName;
import android.content.Context;
@@ -38,6 +40,7 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import com.android.launcher3.Flags;
import com.android.launcher3.PendingAddItemInfo;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
@@ -46,7 +49,6 @@
import com.android.launcher3.model.data.ItemInfoWithIcon;
import com.android.launcher3.model.data.LauncherAppWidgetInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
-import com.android.launcher3.uioverrides.ApiWrapper;
import java.util.List;
import java.util.Objects;
@@ -109,7 +111,7 @@
@SuppressWarnings("NewApi")
public boolean isAppArchivedForUser(@NonNull final String packageName,
@NonNull final UserHandle user) {
- if (!Utilities.enableSupportForArchiving()) {
+ if (!Flags.enableSupportForArchiving()) {
return false;
}
final ApplicationInfo info = getApplicationInfo(
@@ -169,11 +171,9 @@
* Starts the details activity for {@code info}
*/
public void startDetailsActivityForInfo(ItemInfo info, Rect sourceBounds, Bundle opts) {
- if (info instanceof ItemInfoWithIcon
- && (((ItemInfoWithIcon) info).runtimeStatusFlags
- & ItemInfoWithIcon.FLAG_INSTALL_SESSION_ACTIVE) != 0) {
- ItemInfoWithIcon appInfo = (ItemInfoWithIcon) info;
- mContext.startActivity(ApiWrapper.getAppMarketActivityIntent(mContext,
+ if (info instanceof ItemInfoWithIcon appInfo
+ && (appInfo.runtimeStatusFlags & FLAG_INSTALL_SESSION_ACTIVE) != 0) {
+ mContext.startActivity(ApiWrapper.INSTANCE.get(mContext).getAppMarketActivityIntent(
appInfo.getTargetComponent().getPackageName(), Process.myUserHandle()));
return;
}
@@ -274,6 +274,6 @@
@SuppressWarnings("NewApi")
private boolean isPackageInstalledOrArchived(ApplicationInfo info) {
return (info.flags & ApplicationInfo.FLAG_INSTALLED) != 0 || (
- Utilities.enableSupportForArchiving() && info.isArchived);
+ Flags.enableSupportForArchiving() && info.isArchived);
}
}
diff --git a/src/com/android/launcher3/util/VibratorWrapper.java b/src/com/android/launcher3/util/VibratorWrapper.java
index e1695e9..cd60c1d 100644
--- a/src/com/android/launcher3/util/VibratorWrapper.java
+++ b/src/com/android/launcher3/util/VibratorWrapper.java
@@ -19,11 +19,6 @@
import static android.os.VibrationEffect.createPredefined;
import static android.provider.Settings.System.HAPTIC_FEEDBACK_ENABLED;
-import static com.android.launcher3.config.FeatureFlags.LPNH_HAPTIC_HINT_DELAY;
-import static com.android.launcher3.config.FeatureFlags.LPNH_HAPTIC_HINT_END_SCALE_PERCENT;
-import static com.android.launcher3.config.FeatureFlags.LPNH_HAPTIC_HINT_ITERATIONS;
-import static com.android.launcher3.config.FeatureFlags.LPNH_HAPTIC_HINT_SCALE_EXPONENT;
-import static com.android.launcher3.config.FeatureFlags.LPNH_HAPTIC_HINT_START_SCALE_PERCENT;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
@@ -40,7 +35,6 @@
import androidx.annotation.Nullable;
import com.android.launcher3.Utilities;
-import com.android.launcher3.config.FeatureFlags;
/**
* Wrapper around {@link Vibrator} to easily perform haptic feedback where necessary.
@@ -71,9 +65,6 @@
@Nullable
private final VibrationEffect mBumpEffect;
- @Nullable
- private final VibrationEffect mSearchEffect;
-
private long mLastDragTime;
private final int mThresholdUntilNextDragCallMillis;
@@ -133,25 +124,6 @@
mBumpEffect = null;
mThresholdUntilNextDragCallMillis = 0;
}
-
- if (mVibrator.areAllPrimitivesSupported(
- VibrationEffect.Composition.PRIMITIVE_QUICK_RISE,
- VibrationEffect.Composition.PRIMITIVE_TICK)) {
- if (FeatureFlags.ENABLE_SEARCH_HAPTIC_HINT.get()) {
- mSearchEffect = VibrationEffect.startComposition()
- .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 1f)
- .compose();
- } else {
- // quiet ramp, short pause, then sharp tick
- mSearchEffect = VibrationEffect.startComposition()
- .addPrimitive(VibrationEffect.Composition.PRIMITIVE_QUICK_RISE, 0.25f)
- .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 1f, 50)
- .compose();
- }
- } else {
- // fallback for devices without composition support
- mSearchEffect = VibrationEffect.createPredefined(VibrationEffect.EFFECT_HEAVY_CLICK);
- }
}
/**
@@ -233,13 +205,6 @@
}
}
- /** Indicates that search has been invoked. */
- public void vibrateForSearch() {
- if (mSearchEffect != null) {
- vibrate(mSearchEffect);
- }
- }
-
/** Indicates that Taskbar has been invoked. */
public void vibrateForTaskbarUnstash() {
if (Utilities.ATLEAST_S && mVibrator.areAllPrimitivesSupported(PRIMITIVE_LOW_TICK)) {
@@ -251,32 +216,4 @@
vibrate(primitiveLowTickEffect);
}
}
-
- /** Indicates that search will be invoked if the current gesture is maintained. */
- public void vibrateForSearchHint() {
- if (FeatureFlags.ENABLE_SEARCH_HAPTIC_HINT.get() && Utilities.ATLEAST_S
- && mVibrator.areAllPrimitivesSupported(PRIMITIVE_LOW_TICK)) {
- float startScale = LPNH_HAPTIC_HINT_START_SCALE_PERCENT.get() / 100f;
- float endScale = LPNH_HAPTIC_HINT_END_SCALE_PERCENT.get() / 100f;
- int scaleExponent = LPNH_HAPTIC_HINT_SCALE_EXPONENT.get();
- int iterations = LPNH_HAPTIC_HINT_ITERATIONS.get();
- int delayMs = LPNH_HAPTIC_HINT_DELAY.get();
-
- VibrationEffect.Composition composition = VibrationEffect.startComposition();
- for (int i = 0; i < iterations; i++) {
- float t = i / (iterations - 1f);
- float scale = (float) Math.pow((1 - t) * startScale + t * endScale,
- scaleExponent);
- if (i == 0) {
- // Adds a delay before the ramp starts
- composition.addPrimitive(PRIMITIVE_LOW_TICK, scale,
- delayMs);
- } else {
- composition.addPrimitive(PRIMITIVE_LOW_TICK, scale);
- }
- }
-
- vibrate(composition.compose());
- }
- }
}
diff --git a/src/com/android/launcher3/util/window/WindowManagerProxy.java b/src/com/android/launcher3/util/window/WindowManagerProxy.java
index 4a906d3..998191e 100644
--- a/src/com/android/launcher3/util/window/WindowManagerProxy.java
+++ b/src/com/android/launcher3/util/window/WindowManagerProxy.java
@@ -94,6 +94,13 @@
}
/**
+ * Returns true if taskbar is drawn in process
+ */
+ public boolean isTaskbarDrawnInProcess() {
+ return mTaskbarDrawnInProcess;
+ }
+
+ /**
* Returns a map of normalized info of internal displays to estimated window bounds
* for that display
*/
diff --git a/src_no_quickstep/com/android/launcher3/uioverrides/flags/FlagsFactory.java b/src_no_quickstep/com/android/launcher3/uioverrides/flags/FlagsFactory.java
index b193d37..02e0de0 100644
--- a/src_no_quickstep/com/android/launcher3/uioverrides/flags/FlagsFactory.java
+++ b/src_no_quickstep/com/android/launcher3/uioverrides/flags/FlagsFactory.java
@@ -18,12 +18,8 @@
import static com.android.launcher3.config.FeatureFlags.FlagState.ENABLED;
-import androidx.annotation.Nullable;
-
-import com.android.launcher3.ConstantItem;
import com.android.launcher3.config.FeatureFlags.BooleanFlag;
import com.android.launcher3.config.FeatureFlags.FlagState;
-import com.android.launcher3.config.FeatureFlags.IntFlag;
import java.io.PrintWriter;
@@ -50,23 +46,6 @@
}
/**
- * Creates a new integer flag. Integer flags are always release flags
- */
- public static IntFlag getIntFlag(
- int bugId, String key, int defaultValueInCode, String description) {
- return new IntFlag(defaultValueInCode);
- }
-
- /**
- * Creates a new debug integer flag and it is saved in LauncherPrefs.
- */
- public static IntFlag getIntFlag(
- int bugId, String key, int defaultValueInCode, String description,
- @Nullable ConstantItem<Integer> launcherPrefFlag) {
- return new IntFlag(defaultValueInCode);
- }
-
- /**
* Dumps the current flags state to the print writer
*/
public static void dump(PrintWriter pw) { }
diff --git a/tests/multivalentTests/src/com/android/launcher3/util/TestUtil.java b/tests/multivalentTests/src/com/android/launcher3/util/TestUtil.java
index d70ecdd..b5707ff 100644
--- a/tests/multivalentTests/src/com/android/launcher3/util/TestUtil.java
+++ b/tests/multivalentTests/src/com/android/launcher3/util/TestUtil.java
@@ -48,7 +48,6 @@
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.config.FeatureFlags.BooleanFlag;
-import com.android.launcher3.config.FeatureFlags.IntFlag;
import com.android.launcher3.tapl.LauncherInstrumentation;
import com.android.launcher3.tapl.Workspace;
@@ -66,7 +65,6 @@
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.function.Predicate;
-import java.util.function.ToIntFunction;
public class TestUtil {
private static final String TAG = "TestUtil";
@@ -161,21 +159,6 @@
};
}
- /**
- * Utility class to override a int flag during test. Note that the returned SafeCloseable
- * must be closed to restore the original state
- */
- public static SafeCloseable overrideFlag(IntFlag flag, int value) {
- ToIntFunction<IntFlag> originalProxy = FeatureFlags.sIntReader;
- ToIntFunction<IntFlag> testProxy = f -> f == flag ? value : originalProxy.applyAsInt(f);
- FeatureFlags.sIntReader = testProxy;
- return () -> {
- if (FeatureFlags.sIntReader == testProxy) {
- FeatureFlags.sIntReader = originalProxy;
- }
- };
- }
-
public static void uninstallDummyApp() throws IOException {
UiDevice.getInstance(getInstrumentation()).executeShellCommand(
"pm uninstall " + DUMMY_PACKAGE);
diff --git a/tests/src/com/android/launcher3/AbstractDeviceProfileTest.kt b/tests/src/com/android/launcher3/AbstractDeviceProfileTest.kt
index ffcf83f..577334b 100644
--- a/tests/src/com/android/launcher3/AbstractDeviceProfileTest.kt
+++ b/tests/src/com/android/launcher3/AbstractDeviceProfileTest.kt
@@ -38,17 +38,18 @@
import com.android.launcher3.util.window.CachedDisplayInfo
import com.android.launcher3.util.window.WindowManagerProxy
import com.google.common.truth.Truth
-import org.junit.Rule
-import org.mockito.kotlin.any
-import org.mockito.kotlin.mock
-import org.mockito.kotlin.spy
-import org.mockito.kotlin.whenever
import java.io.BufferedReader
import java.io.File
import java.io.PrintWriter
import java.io.StringWriter
import kotlin.math.max
import kotlin.math.min
+import org.junit.Rule
+import org.mockito.kotlin.any
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.spy
+import org.mockito.kotlin.whenever
/**
* This is an abstract class for DeviceProfile tests that create an InvariantDeviceProfile based on
@@ -286,6 +287,9 @@
.thenReturn(
if (isGestureMode) NavigationMode.NO_BUTTON else NavigationMode.THREE_BUTTONS
)
+ doReturn(WindowManagerProxy.INSTANCE[runningContext].isTaskbarDrawnInProcess)
+ .whenever(windowManagerProxy)
+ .isTaskbarDrawnInProcess()
val density = densityDpi / DisplayMetrics.DENSITY_DEFAULT.toFloat()
val config =
diff --git a/tests/src/com/android/launcher3/AbstractFloatingViewHelperTest.kt b/tests/src/com/android/launcher3/AbstractFloatingViewHelperTest.kt
new file mode 100644
index 0000000..7ff544d
--- /dev/null
+++ b/tests/src/com/android/launcher3/AbstractFloatingViewHelperTest.kt
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3
+
+import android.view.View
+import com.android.launcher3.dragndrop.DragLayer
+import com.android.launcher3.views.ActivityContext
+import org.junit.Before
+import org.junit.Test
+import org.mockito.kotlin.any
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.never
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.verifyZeroInteractions
+import org.mockito.kotlin.whenever
+
+/** Test for AbstractFloatingViewHelper */
+class AbstractFloatingViewHelperTest {
+ private val activityContext: ActivityContext = mock()
+ private val dragLayer: DragLayer = mock()
+ private val view: View = mock()
+ private val folderView: AbstractFloatingView = mock()
+ private val taskMenuView: AbstractFloatingView = mock()
+ private val abstractFloatingViewHelper = AbstractFloatingViewHelper()
+
+ @Before
+ fun setup() {
+ whenever(activityContext.dragLayer).thenReturn(dragLayer)
+ whenever(dragLayer.childCount).thenReturn(3)
+ whenever(dragLayer.getChildAt(0)).thenReturn(view)
+ whenever(dragLayer.getChildAt(1)).thenReturn(folderView)
+ whenever(dragLayer.getChildAt(2)).thenReturn(taskMenuView)
+ whenever(folderView.isOfType(any())).thenAnswer {
+ (it.getArgument<Int>(0) and AbstractFloatingView.TYPE_FOLDER) != 0
+ }
+ whenever(taskMenuView.isOfType(any())).thenAnswer {
+ (it.getArgument<Int>(0) and AbstractFloatingView.TYPE_TASK_MENU) != 0
+ }
+ }
+
+ @Test
+ fun closeOpenViews_all() {
+ abstractFloatingViewHelper.closeOpenViews(
+ activityContext,
+ true,
+ AbstractFloatingView.TYPE_ALL
+ )
+
+ verifyZeroInteractions(view)
+ verify(folderView).close(true)
+ verify(taskMenuView).close(true)
+ }
+
+ @Test
+ fun closeOpenViews_taskMenu() {
+ abstractFloatingViewHelper.closeOpenViews(
+ activityContext,
+ true,
+ AbstractFloatingView.TYPE_TASK_MENU
+ )
+
+ verifyZeroInteractions(view)
+ verify(folderView, never()).close(any())
+ verify(taskMenuView).close(true)
+ }
+
+ @Test
+ fun closeOpenViews_other() {
+ abstractFloatingViewHelper.closeOpenViews(
+ activityContext,
+ true,
+ AbstractFloatingView.TYPE_PIN_IME_POPUP
+ )
+
+ verifyZeroInteractions(view)
+ verify(folderView, never()).close(any())
+ verify(taskMenuView, never()).close(any())
+ }
+
+ @Test
+ fun closeOpenViews_both_animationOff() {
+ abstractFloatingViewHelper.closeOpenViews(
+ activityContext,
+ false,
+ AbstractFloatingView.TYPE_FOLDER or AbstractFloatingView.TYPE_TASK_MENU
+ )
+
+ verifyZeroInteractions(view)
+ verify(folderView).close(false)
+ verify(taskMenuView).close(false)
+ }
+}
diff --git a/tests/src/com/android/launcher3/allapps/PrivateProfileManagerTest.java b/tests/src/com/android/launcher3/allapps/PrivateProfileManagerTest.java
index e9d2f6e..2dcbda4 100644
--- a/tests/src/com/android/launcher3/allapps/PrivateProfileManagerTest.java
+++ b/tests/src/com/android/launcher3/allapps/PrivateProfileManagerTest.java
@@ -47,8 +47,8 @@
import com.android.launcher3.logging.StatsLogManager;
import com.android.launcher3.pm.UserCache;
-import com.android.launcher3.uioverrides.ApiWrapper;
import com.android.launcher3.util.ActivityContextWrapper;
+import com.android.launcher3.util.ApiWrapper;
import com.android.launcher3.util.UserIconInfo;
import com.android.launcher3.util.rule.TestStabilityRule;
@@ -108,7 +108,8 @@
when(mUserCache.getUserInfo(Process.myUserHandle())).thenReturn(MAIN_ICON_INFO);
when(mUserCache.getUserInfo(PRIVATE_HANDLE)).thenReturn(PRIVATE_ICON_INFO);
when(mAllApps.getContext()).thenReturn(mContext);
- when(mAllApps.getContext().getResources()).thenReturn(mResources);
+ when(mContext.getResources()).thenReturn(mResources);
+ when(mContext.getApplicationContext()).thenReturn(getApplicationContext());
when(mAllApps.getAppsStore()).thenReturn(mAllAppsStore);
when(mAllApps.getActiveRecyclerView()).thenReturn(mAllAppsRecyclerView);
when(mContext.getPackageManager()).thenReturn(mPackageManager);
@@ -200,14 +201,16 @@
@Test
public void openPrivateSpaceSettings_triggersCorrectIntent() {
- Intent expectedIntent = ApiWrapper.getPrivateSpaceSettingsIntent(mContext);
+ Intent expectedIntent = ApiWrapper.INSTANCE.get(mContext).getPrivateSpaceSettingsIntent();
ArgumentCaptor<Intent> acIntent = ArgumentCaptor.forClass(Intent.class);
mPrivateProfileManager.setPrivateSpaceSettingsAvailable(true);
mPrivateProfileManager.openPrivateSpaceSettings();
Mockito.verify(mContext).startActivity(acIntent.capture());
- assertEquals("Intent Action is different", expectedIntent, acIntent.getValue());
+ assertEquals("Intent Action is different",
+ expectedIntent == null ? null : expectedIntent.toUri(0),
+ acIntent.getValue() == null ? null : acIntent.getValue().toUri(0));
}
private static void awaitTasksCompleted() throws Exception {
diff --git a/tests/src/com/android/launcher3/ui/PortraitLandscapeRunner.java b/tests/src/com/android/launcher3/ui/PortraitLandscapeRunner.java
index f01bdb3..fabe3f7 100644
--- a/tests/src/com/android/launcher3/ui/PortraitLandscapeRunner.java
+++ b/tests/src/com/android/launcher3/ui/PortraitLandscapeRunner.java
@@ -5,7 +5,6 @@
import com.android.launcher3.tapl.TestHelpers;
import com.android.launcher3.util.rule.FailureWatcher;
-import com.android.launcher3.util.rule.TestStabilityRule;
import org.junit.rules.TestRule;
import org.junit.runner.Description;
@@ -34,11 +33,7 @@
@Override
public Statement apply(Statement base, Description description) {
if (!TestHelpers.isInLauncherProcess()
- || description.getAnnotation(PortraitLandscape.class) == null
- // If running in presubmit, don't run in both orientations.
- // It's important to keep presubmits fast even if we will occasionally miss
- // regressions in presubmit.
- || TestStabilityRule.isPresubmit()) {
+ || description.getAnnotation(PortraitLandscape.class) == null) {
return base;
}
diff --git a/tests/tapl/com/android/launcher3/tapl/AllApps.java b/tests/tapl/com/android/launcher3/tapl/AllApps.java
index 847dc4b..245ec09 100644
--- a/tests/tapl/com/android/launcher3/tapl/AllApps.java
+++ b/tests/tapl/com/android/launcher3/tapl/AllApps.java
@@ -416,6 +416,14 @@
}
}
+ /** Returns PrivateSpaceContainer if present in view. */
+ @NonNull
+ public PrivateSpaceContainer getPrivateSpaceUnlockedView() {
+ final UiObject2 allAppsContainer = verifyActiveContainer();
+ final UiObject2 appListRecycler = getAppListRecycler(allAppsContainer);
+ return new PrivateSpaceContainer(mLauncher, appListRecycler);
+ }
+
protected abstract void verifyVisibleContainerOnDismiss();
/**
diff --git a/tests/tapl/com/android/launcher3/tapl/PrivateSpaceContainer.java b/tests/tapl/com/android/launcher3/tapl/PrivateSpaceContainer.java
new file mode 100644
index 0000000..2c16e01
--- /dev/null
+++ b/tests/tapl/com/android/launcher3/tapl/PrivateSpaceContainer.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.tapl;
+
+import androidx.test.uiautomator.UiObject2;
+
+/**
+ * View containing Private Space elements.
+ */
+public class PrivateSpaceContainer {
+ private static final String PS_HEADER_RES_ID = "ps_header_layout";
+ private static final String INSTALL_APP_TITLE = "Install apps";
+ private static final String DIVIDER_RES_ID = "private_space_divider";
+
+ private final LauncherInstrumentation mLauncher;
+ private final UiObject2 mAppListRecycler;
+
+ PrivateSpaceContainer(LauncherInstrumentation launcherInstrumentation,
+ UiObject2 appListRecycler) {
+ mLauncher = launcherInstrumentation;
+ mAppListRecycler = appListRecycler;
+
+ verifyHeaderIsPresent();
+ verifyInstallAppButtonIsPresent();
+ verifyDividerIsPresent();
+ }
+
+ // Assert PS Header is in view.
+ // Assert PS header has the correct elements.
+ private void verifyHeaderIsPresent() {
+ final UiObject2 psHeader = mLauncher.waitForObjectInContainer(mAppListRecycler,
+ PS_HEADER_RES_ID);
+ new PrivateSpaceHeader(mLauncher, psHeader, true);
+ }
+
+
+ // Assert Install App Item is present in view.
+ private void verifyInstallAppButtonIsPresent() {
+ mLauncher.getAllApps().getAppIcon(INSTALL_APP_TITLE);
+ }
+
+ // Assert Sys App Divider is present in view.
+ private void verifyDividerIsPresent() {
+ mLauncher.waitForObjectInContainer(mAppListRecycler, DIVIDER_RES_ID);
+ }
+}
diff --git a/tests/tapl/com/android/launcher3/tapl/PrivateSpaceHeader.java b/tests/tapl/com/android/launcher3/tapl/PrivateSpaceHeader.java
new file mode 100644
index 0000000..3a7f038
--- /dev/null
+++ b/tests/tapl/com/android/launcher3/tapl/PrivateSpaceHeader.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.tapl;
+
+import androidx.test.uiautomator.UiObject2;
+
+/**
+ * View containing Private Space Header.
+ */
+public class PrivateSpaceHeader {
+ private static final String PS_HEADER_TEXT_RES_ID = "ps_container_header";
+ private static final String SETTINGS_BUTTON_RES_ID = "ps_settings_button";
+ private static final String UNLOCK_BUTTON_VIEW_RES_ID = "ps_lock_unlock_button";
+ private static final String LOCK_ICON_RES_ID = "lock_icon";
+ private static final String LOCK_TEXT_RES_ID = "lock_text";
+
+ private final UiObject2 mPrivateSpaceHeader;
+ private final boolean mPrivateSpaceEnabled;
+ private final LauncherInstrumentation mLauncher;
+
+ PrivateSpaceHeader(LauncherInstrumentation launcherInstrumentation,
+ UiObject2 privateSpaceHeader, boolean privateSpaceEnabled) {
+ mLauncher = launcherInstrumentation;
+ mPrivateSpaceHeader = privateSpaceHeader;
+ mPrivateSpaceEnabled = privateSpaceEnabled;
+ verifyPrivateSpaceHeaderState();
+ }
+
+ /** Verify elements in Private Space Header as per state */
+ private void verifyPrivateSpaceHeaderState() {
+ if (mPrivateSpaceEnabled) {
+ verifyUnlockedState();
+ } else {
+ mLauncher.fail("Private Space found in non enabled state");
+ }
+ }
+
+ /** Verify Unlocked State elements in Private Space Header */
+ private void verifyUnlockedState() {
+ UiObject2 headerText = mLauncher.waitForObjectInContainer(mPrivateSpaceHeader,
+ PS_HEADER_TEXT_RES_ID);
+ mLauncher.assertEquals("PS Header Text is incorrect ",
+ "Private", headerText.getText());
+
+ UiObject2 settingsButton = mLauncher.waitForObjectInContainer(mPrivateSpaceHeader,
+ SETTINGS_BUTTON_RES_ID);
+ mLauncher.waitForObjectEnabled(settingsButton, "Private Space Settings Button");
+ mLauncher.assertTrue("PS Settings button is non-clickable", settingsButton.isClickable());
+
+ UiObject2 unLockButtonView = mLauncher.waitForObjectInContainer(mPrivateSpaceHeader,
+ UNLOCK_BUTTON_VIEW_RES_ID);
+ mLauncher.waitForObjectEnabled(unLockButtonView, "Private Space Unlock Button");
+ mLauncher.assertTrue("PS Unlock Button is non-clickable", unLockButtonView.isClickable());
+
+ mLauncher.waitForObjectInContainer(mPrivateSpaceHeader,
+ LOCK_ICON_RES_ID);
+
+ UiObject2 lockText = mLauncher.waitForObjectInContainer(mPrivateSpaceHeader,
+ LOCK_TEXT_RES_ID);
+ mLauncher.assertEquals("PS lock text is incorrect", "Lock", lockText.getText());
+
+ }
+}