Merge "[omni] Log LP duration & slop's data source and value." into main
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/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/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 390dec9..98ce2ed 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/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/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/SystemUiProxy.java b/quickstep/src/com/android/quickstep/SystemUiProxy.java
index 08d5415..30bb863 100644
--- a/quickstep/src/com/android/quickstep/SystemUiProxy.java
+++ b/quickstep/src/com/android/quickstep/SystemUiProxy.java
@@ -1475,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/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index a24623b..2e719cd 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -1506,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);
@@ -2571,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)) {
@@ -5416,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);
@@ -6203,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 22a96a8..5338d81 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskView.java
@@ -487,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;
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/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 3273f27..dbd594e 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/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/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/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/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..8f398a6 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,15 @@
@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.toUri(0), acIntent.getValue().toUri(0));
}
private static void awaitTasksCompleted() throws Exception {
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());
+
+ }
+}