Merge "Prepare for wiring bubble bar flyout" into main
diff --git a/aconfig/launcher.aconfig b/aconfig/launcher.aconfig
index c3c42fa..457fdd8 100644
--- a/aconfig/launcher.aconfig
+++ b/aconfig/launcher.aconfig
@@ -65,6 +65,13 @@
}
flag {
+ name: "enable_taskbar_connected_displays"
+ namespace: "launcher"
+ description: "Enables connected displays in taskbar."
+ bug: "362720616"
+}
+
+flag {
name: "enable_taskbar_customization"
namespace: "launcher"
description: "Enables taskbar customization framework."
diff --git a/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java b/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java
index 6af5a30..4014f06 100644
--- a/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java
+++ b/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java
@@ -237,7 +237,7 @@
InstanceId instanceId = new InstanceIdSequence().newInstanceId();
for (ItemInfo info : itemsIdMap) {
CollectionInfo parent = getContainer(info, itemsIdMap);
- StatsLogCompatManager.writeSnapshot(info.buildProto(parent), instanceId);
+ StatsLogCompatManager.writeSnapshot(info.buildProto(parent, mContext), instanceId);
}
additionalSnapshotEvents(instanceId);
prefs.put(LAST_SNAPSHOT_TIME_MILLIS, now);
@@ -274,7 +274,7 @@
for (ItemInfo info : itemsIdMap) {
CollectionInfo parent = getContainer(info, itemsIdMap);
- LauncherAtom.ItemInfo itemInfo = info.buildProto(parent);
+ LauncherAtom.ItemInfo itemInfo = info.buildProto(parent, mContext);
Log.d(TAG, itemInfo.toString());
StatsEvent statsEvent = StatsLogCompatManager.buildStatsEvent(itemInfo,
instanceId);
diff --git a/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.java b/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.java
index 3dcb2ac..2ac87ff 100644
--- a/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.java
+++ b/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.java
@@ -16,7 +16,7 @@
package com.android.launcher3.statehandlers;
import static android.view.View.VISIBLE;
-import static android.window.flags.DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY;
+import static android.window.DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
diff --git a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
index 10ff9ac..09dbeb6 100644
--- a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
@@ -15,8 +15,10 @@
*/
package com.android.launcher3.taskbar;
-import static android.window.flags.DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY;
+import static android.window.DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY;
+import static com.android.launcher3.QuickstepTransitionManager.TASKBAR_TO_APP_DURATION;
+import static com.android.launcher3.QuickstepTransitionManager.getTaskbarToHomeDuration;
import static com.android.launcher3.QuickstepTransitionManager.TRANSIENT_TASKBAR_TRANSITION_DURATION;
import static com.android.launcher3.statemanager.BaseState.FLAG_NON_INTERACTIVE;
import static com.android.launcher3.taskbar.TaskbarEduTooltipControllerKt.TOOLTIP_STEP_FEATURES;
@@ -32,7 +34,6 @@
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Flags;
import com.android.launcher3.LauncherState;
-import com.android.launcher3.QuickstepTransitionManager;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AnimatedFloat;
import com.android.launcher3.logging.InstanceId;
@@ -205,11 +206,17 @@
isVisible,
fromInitOrDestroy,
/* startAnimation= */ true,
- DisplayController.isTransientTaskbar(mLauncher)
- ? TRANSIENT_TASKBAR_TRANSITION_DURATION
- : (!isVisible
- ? QuickstepTransitionManager.TASKBAR_TO_APP_DURATION
- : QuickstepTransitionManager.getTaskbarToHomeDuration()));
+ getTaskbarAnimationDuration(isVisible));
+ }
+
+ private int getTaskbarAnimationDuration(boolean isVisible) {
+ if (isVisible && !mLauncher.getPredictiveBackToHomeInProgress()) {
+ return getTaskbarToHomeDuration();
+ } else {
+ return DisplayController.isTransientTaskbar(mLauncher)
+ ? TRANSIENT_TASKBAR_TRANSITION_DURATION
+ : TASKBAR_TO_APP_DURATION;
+ }
}
@Nullable
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
index a4fbb25..707d4b3 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
@@ -16,6 +16,7 @@
package com.android.launcher3.taskbar;
import static com.android.app.animation.Interpolators.EMPHASIZED;
+import static com.android.launcher3.Flags.enableScalingRevealHomeAnimation;
import static com.android.launcher3.Hotseat.ALPHA_CHANNEL_TASKBAR_ALIGNMENT;
import static com.android.launcher3.Hotseat.ALPHA_CHANNEL_TASKBAR_STASH;
import static com.android.launcher3.LauncherState.HOTSEAT_ICONS;
@@ -42,6 +43,7 @@
import android.animation.ObjectAnimator;
import android.os.SystemClock;
import android.util.Log;
+import android.view.animation.Interpolator;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -64,6 +66,7 @@
import com.android.launcher3.util.MultiPropertyFactory.MultiProperty;
import com.android.quickstep.RecentsAnimationCallbacks;
import com.android.quickstep.RecentsAnimationController;
+import com.android.quickstep.util.ScalingWorkspaceRevealAnim;
import com.android.quickstep.util.SystemUiFlagUtils;
import com.android.quickstep.views.RecentsView;
import com.android.systemui.animation.ViewRootSync;
@@ -682,7 +685,9 @@
animatorSet.play(iconAlignAnim);
}
- animatorSet.setInterpolator(EMPHASIZED);
+ Interpolator interpolator = enableScalingRevealHomeAnimation()
+ ? ScalingWorkspaceRevealAnim.SCALE_INTERPOLATOR : EMPHASIZED;
+ animatorSet.setInterpolator(interpolator);
if (start) {
animatorSet.start();
@@ -781,6 +786,9 @@
}
protected void stashHotseat(boolean stash) {
+ // align taskbar with the hotseat icons before performing any animation
+ mControllers.taskbarViewController.setLauncherIconAlignment(/* alignmentRatio = */ 1,
+ mLauncher.getDeviceProfile());
TaskbarStashController stashController = mControllers.taskbarStashController;
stashController.updateStateForFlag(FLAG_STASHED_FOR_BUBBLES, stash);
Runnable swapHotseatWithTaskbar = new Runnable() {
@@ -893,19 +901,6 @@
mHotseatTranslationXAnimation.cancel();
mHotseatTranslationXAnimation = null;
}
- Runnable postAnimationAction = new Runnable() {
- @Override
- public void run() {
- mHotseatTranslationXAnimation = null;
- // We only need to align the task bar when on launcher home screen
- if (mControllers.taskbarStashController.isOnHome()) {
- mControllers.taskbarViewController.setLauncherIconAlignment(
- /* alignmentRatio = */ 1,
- mLauncher.getDeviceProfile()
- );
- }
- }
- };
Hotseat hotseat = mLauncher.getHotseat();
AnimatorSet translationXAnimation = new AnimatorSet();
MultiProperty iconsTranslationX = mLauncher.getHotseat()
@@ -928,14 +923,12 @@
}
}
if (!animate) {
- postAnimationAction.run();
return;
}
mHotseatTranslationXAnimation = translationXAnimation;
translationXAnimation.setStartDelay(FADE_OUT_ANIM_POSITION_DURATION_MS);
translationXAnimation.setDuration(FADE_IN_ANIM_ALPHA_DURATION_MS);
translationXAnimation.setInterpolator(Interpolators.EMPHASIZED);
- translationXAnimation.addListener(AnimatorListeners.forEndCallback(postAnimationAction));
translationXAnimation.start();
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarRecentAppsController.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarRecentAppsController.kt
index 57d4dbb..9c34ff0 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarRecentAppsController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarRecentAppsController.kt
@@ -16,7 +16,7 @@
package com.android.launcher3.taskbar
import android.content.Context
-import android.window.flags.DesktopModeFlags
+import android.window.DesktopModeFlags
import androidx.annotation.VisibleForTesting
import com.android.launcher3.Flags.enableRecentsInTaskbar
import com.android.launcher3.model.data.ItemInfo
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
index 0e7abf2..39bf6ac 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
@@ -19,7 +19,7 @@
import static android.os.Trace.TRACE_TAG_APP;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_OPTIMIZE_MEASURE;
import static android.view.accessibility.AccessibilityEvent.TYPE_VIEW_FOCUSED;
-import static android.window.flags.DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY;
+import static android.window.DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY;
import static com.android.app.animation.Interpolators.EMPHASIZED;
import static com.android.internal.jank.Cuj.CUJ_LAUNCHER_LAUNCH_APP_PAIR_FROM_WORKSPACE;
@@ -1316,6 +1316,10 @@
mTISBindHelper.setPredictiveBackToHomeInProgress(isInProgress);
}
+ public boolean getPredictiveBackToHomeInProgress() {
+ return mIsPredictiveBackToHomeInProgress;
+ }
+
@Override
public boolean areDesktopTasksVisible() {
DesktopVisibilityController desktopVisibilityController = getDesktopVisibilityController();
diff --git a/quickstep/src/com/android/launcher3/uioverrides/flags/DevOptionsUiHelper.kt b/quickstep/src/com/android/launcher3/uioverrides/flags/DevOptionsUiHelper.kt
index 181cba0..417bb74 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/flags/DevOptionsUiHelper.kt
+++ b/quickstep/src/com/android/launcher3/uioverrides/flags/DevOptionsUiHelper.kt
@@ -35,9 +35,6 @@
import android.provider.Settings.Secure
import android.text.Html
import android.util.AttributeSet
-import android.util.Base64
-import android.util.Base64.NO_PADDING
-import android.util.Base64.NO_WRAP
import android.view.inputmethod.EditorInfo
import android.widget.TextView
import android.widget.Toast
@@ -57,9 +54,10 @@
import com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET
import com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT
import com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_FOLDER
-import com.android.launcher3.LauncherSettings.Settings.LAYOUT_DIGEST_KEY
import com.android.launcher3.LauncherSettings.Settings.LAYOUT_DIGEST_LABEL
import com.android.launcher3.LauncherSettings.Settings.LAYOUT_DIGEST_TAG
+import com.android.launcher3.LauncherSettings.Settings.LAYOUT_PROVIDER_KEY
+import com.android.launcher3.LauncherSettings.Settings.createBlobProviderKey
import com.android.launcher3.R
import com.android.launcher3.model.data.FolderInfo
import com.android.launcher3.model.data.ItemInfo
@@ -241,7 +239,7 @@
private fun DebugInfo<Boolean>.getBoolValue() =
DeviceConfigHelper.prefs.getBoolean(
this.key,
- DeviceConfig.getBoolean(NAMESPACE_LAUNCHER, this.key, this.valueInCode)
+ DeviceConfig.getBoolean(NAMESPACE_LAUNCHER, this.key, this.valueInCode),
)
private fun DebugInfo<Int>.getIntValueAsString() =
@@ -265,7 +263,7 @@
val pluginPermissionApps =
pm.getPackagesHoldingPermissions(
arrayOf(PLUGIN_PERMISSION),
- PackageManager.MATCH_DISABLED_COMPONENTS
+ PackageManager.MATCH_DISABLED_COMPONENTS,
)
.map { it.packageName }
@@ -274,7 +272,7 @@
pm.queryIntentServices(
Intent(action),
PackageManager.MATCH_DISABLED_COMPONENTS or
- PackageManager.GET_RESOLVED_FILTER
+ PackageManager.GET_RESOLVED_FILTER,
)
.filter { pluginPermissionApps.contains(it.serviceInfo.packageName) }
}
@@ -316,7 +314,7 @@
infoList.forEach {
manager.pluginEnabler.setDisabled(
it.serviceInfo.componentName,
- disabledState
+ disabledState,
)
}
manager.notifyChange(Intent(Intent.ACTION_PACKAGE_CHANGED, pluginUri))
@@ -387,12 +385,12 @@
addOnboardPref(
"All Apps Bounce",
HOME_BOUNCE_SEEN.sharedPrefKey,
- HOME_BOUNCE_COUNT.sharedPrefKey
+ HOME_BOUNCE_COUNT.sharedPrefKey,
)
addOnboardPref(
"Hybrid Hotseat Education",
HOTSEAT_DISCOVERY_TIP_COUNT.sharedPrefKey,
- HOTSEAT_LONGPRESS_TIP_SEEN.sharedPrefKey
+ HOTSEAT_LONGPRESS_TIP_SEEN.sharedPrefKey,
)
addOnboardPref("Taskbar Education", TASKBAR_EDU_TOOLTIP_STEP.sharedPrefKey)
addOnboardPref("Taskbar Search Education", TASKBAR_SEARCH_EDU_SEEN.sharedPrefKey)
@@ -470,13 +468,16 @@
session.allowPublicAccess()
session.commit(ORDERED_BG_EXECUTOR) {
- val key = Base64.encodeToString(digest, NO_WRAP or NO_PADDING)
- Secure.putString(resolver, LAYOUT_DIGEST_KEY, key)
+ Secure.putString(
+ resolver,
+ LAYOUT_PROVIDER_KEY,
+ createBlobProviderKey(digest),
+ )
MODEL_EXECUTOR.submit { model.modelDbController.createEmptyDB() }.get()
MAIN_EXECUTOR.submit { model.forceReload() }.get()
MODEL_EXECUTOR.submit {}.get()
- Secure.putString(resolver, LAYOUT_DIGEST_KEY, null)
+ Secure.putString(resolver, LAYOUT_PROVIDER_KEY, null)
}
}
}
@@ -512,7 +513,7 @@
info.providerName.className,
info.spanX,
info.spanY,
- userType
+ userType,
)
}
}
@@ -520,7 +521,7 @@
private fun createUriPickerIntent(
action: String,
executor: Executor,
- callback: (uri: Uri) -> Unit
+ callback: (uri: Uri) -> Unit,
): Intent {
val pendingIntent =
PendingIntent(
@@ -532,7 +533,7 @@
allowlistToken: IBinder?,
finishedReceiver: IIntentReceiver?,
requiredPermission: String?,
- options: Bundle?
+ options: Bundle?,
) {
intent.data?.let { uri -> executor.execute { callback(uri) } }
}
diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
index 3413532..fbb2c06 100644
--- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
+++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
@@ -88,8 +88,8 @@
import android.view.WindowInsets;
import android.view.animation.Interpolator;
import android.widget.Toast;
+import android.window.DesktopModeFlags;
import android.window.PictureInPictureSurfaceTransaction;
-import android.window.flags.DesktopModeFlags;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -156,6 +156,8 @@
import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
import com.android.wm.shell.shared.startingsurface.SplashScreenExitAnimationUtils;
+import kotlin.Unit;
+
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
@@ -165,8 +167,6 @@
import java.util.OptionalInt;
import java.util.function.Consumer;
-import kotlin.Unit;
-
/**
* Handles the navigation gestures when Launcher is the default home activity.
*/
diff --git a/quickstep/src/com/android/quickstep/SystemUiProxy.java b/quickstep/src/com/android/quickstep/SystemUiProxy.java
index a55cf18..5f02893 100644
--- a/quickstep/src/com/android/quickstep/SystemUiProxy.java
+++ b/quickstep/src/com/android/quickstep/SystemUiProxy.java
@@ -46,11 +46,11 @@
import android.view.MotionEvent;
import android.view.RemoteAnimationTarget;
import android.view.SurfaceControl;
+import android.window.DesktopModeFlags;
import android.window.IOnBackInvokedCallback;
import android.window.RemoteTransition;
import android.window.TaskSnapshot;
import android.window.TransitionFilter;
-import android.window.flags.DesktopModeFlags;
import androidx.annotation.MainThread;
import androidx.annotation.Nullable;
@@ -1492,6 +1492,17 @@
}
}
+ /** Call shell to remove the desktop that is on given `displayId` */
+ public void removeDesktop(int displayId) {
+ if (mDesktopMode != null) {
+ try {
+ mDesktopMode.removeDesktop(displayId);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call removeDesktop", e);
+ }
+ }
+ }
+
//
// Unfold transition
//
diff --git a/quickstep/src/com/android/quickstep/dagger/QuickstepBaseAppComponent.java b/quickstep/src/com/android/quickstep/dagger/QuickstepBaseAppComponent.java
index eef1f96..341c868 100644
--- a/quickstep/src/com/android/quickstep/dagger/QuickstepBaseAppComponent.java
+++ b/quickstep/src/com/android/quickstep/dagger/QuickstepBaseAppComponent.java
@@ -20,6 +20,7 @@
import com.android.launcher3.dagger.LauncherBaseAppComponent;
import com.android.launcher3.model.WellbeingModel;
import com.android.quickstep.logging.SettingsChangeLogger;
+import com.android.quickstep.util.AsyncClockEventDelegate;
/**
* Launcher Quickstep base component for Dagger injection.
@@ -33,4 +34,6 @@
SettingsChangeLogger getSettingsChangeLogger();
WellbeingModel getWellbeingModel();
+
+ AsyncClockEventDelegate getAsyncClockEventDelegate();
}
diff --git a/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java b/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
index 1d4160d..2daaaf9 100644
--- a/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
+++ b/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
@@ -386,12 +386,12 @@
// and then write to StatsLog.
app.getModel().enqueueModelUpdateTask((taskController, dataModel, apps) ->
write(event, applyOverwrites(mItemInfo.buildProto(
- dataModel.collections.get(mItemInfo.container)))));
+ dataModel.collections.get(mItemInfo.container), mContext))));
})) {
// Write log on the model thread so that logs do not go out of order
// (for eg: drop comes after drag)
Executors.MODEL_EXECUTOR.execute(
- () -> write(event, applyOverwrites(mItemInfo.buildProto())));
+ () -> write(event, applyOverwrites(mItemInfo.buildProto(mContext))));
}
}
diff --git a/quickstep/src/com/android/quickstep/util/AsyncClockEventDelegate.java b/quickstep/src/com/android/quickstep/util/AsyncClockEventDelegate.java
index 38ae303..4a84b1b 100644
--- a/quickstep/src/com/android/quickstep/util/AsyncClockEventDelegate.java
+++ b/quickstep/src/com/android/quickstep/util/AsyncClockEventDelegate.java
@@ -32,23 +32,33 @@
import androidx.annotation.WorkerThread;
+import com.android.launcher3.dagger.ApplicationContext;
+import com.android.launcher3.dagger.LauncherAppSingleton;
+import com.android.launcher3.dagger.LauncherBaseAppComponent;
+import com.android.launcher3.util.DaggerSingletonObject;
+import com.android.launcher3.util.DaggerSingletonTracker;
+import com.android.launcher3.util.ExecutorUtil;
import com.android.launcher3.util.MainThreadInitializedObject;
import com.android.launcher3.util.SafeCloseable;
import com.android.launcher3.util.SettingsCache;
import com.android.launcher3.util.SettingsCache.OnChangeListener;
import com.android.launcher3.util.SimpleBroadcastReceiver;
+import com.android.quickstep.dagger.QuickstepBaseAppComponent;
import java.util.ArrayList;
import java.util.List;
+import javax.inject.Inject;
+
/**
* Extension of {@link ClockEventDelegate} to support async event registration
*/
+@LauncherAppSingleton
public class AsyncClockEventDelegate extends ClockEventDelegate
implements OnChangeListener, SafeCloseable {
- public static final MainThreadInitializedObject<AsyncClockEventDelegate> INSTANCE =
- new MainThreadInitializedObject<>(AsyncClockEventDelegate::new);
+ public static final DaggerSingletonObject<AsyncClockEventDelegate> INSTANCE =
+ new DaggerSingletonObject<>(QuickstepBaseAppComponent::getAsyncClockEventDelegate);
private final Context mContext;
private final SimpleBroadcastReceiver mReceiver =
@@ -61,10 +71,12 @@
private boolean mFormatRegistered = false;
private boolean mDestroyed = false;
- private AsyncClockEventDelegate(Context context) {
+ @Inject
+ AsyncClockEventDelegate(@ApplicationContext Context context, DaggerSingletonTracker tracker) {
super(context);
mContext = context;
mReceiver.register(mContext, ACTION_TIME_CHANGED, ACTION_TIMEZONE_CHANGED);
+ ExecutorUtil.executeSyncOnMainOrFail(() -> tracker.addCloseable(this));
}
@Override
diff --git a/quickstep/src/com/android/quickstep/util/ScalingWorkspaceRevealAnim.kt b/quickstep/src/com/android/quickstep/util/ScalingWorkspaceRevealAnim.kt
index a94d023..2f0a6df 100644
--- a/quickstep/src/com/android/quickstep/util/ScalingWorkspaceRevealAnim.kt
+++ b/quickstep/src/com/android/quickstep/util/ScalingWorkspaceRevealAnim.kt
@@ -62,7 +62,8 @@
* Custom interpolator for both the home and wallpaper scaling. Necessary because EMPHASIZED
* is too aggressive, but EMPHASIZED_DECELERATE is too soft.
*/
- private val SCALE_INTERPOLATOR =
+ @JvmField
+ val SCALE_INTERPOLATOR =
PathInterpolator(
Path().apply {
moveTo(0f, 0f)
diff --git a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
index 00e57c2..bbb8cc8 100644
--- a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
@@ -16,7 +16,7 @@
package com.android.quickstep.views;
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
-import static android.window.flags.DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY;
+import static android.window.DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY;
import static com.android.launcher3.LauncherState.CLEAR_ALL_BUTTON;
import static com.android.launcher3.LauncherState.NORMAL;
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index c405080..7554c44 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -129,6 +129,7 @@
import android.widget.ListView;
import android.widget.OverScroller;
import android.widget.Toast;
+import android.window.DesktopModeFlags;
import android.window.PictureInPictureSurfaceTransaction;
import androidx.annotation.NonNull;
@@ -3935,9 +3936,11 @@
if (shouldRemoveTask) {
if (dismissedTaskView.isRunningTask()) {
finishRecentsAnimation(true /* toRecents */, false /* shouldPip */,
- () -> removeTaskInternal(dismissedTaskViewId));
+ () -> removeTaskInternal(dismissedTaskViewId,
+ dismissedTaskView instanceof DesktopTaskView));
} else {
- removeTaskInternal(dismissedTaskViewId);
+ removeTaskInternal(dismissedTaskViewId,
+ dismissedTaskView instanceof DesktopTaskView);
}
announceForAccessibility(
getResources().getString(R.string.task_view_closed));
@@ -4305,16 +4308,21 @@
return lastVisibleIndex;
}
- private void removeTaskInternal(int dismissedTaskViewId) {
+ private void removeTaskInternal(int dismissedTaskViewId, boolean isDesktop) {
int[] taskIds = getTaskIdsForTaskViewId(dismissedTaskViewId);
- UI_HELPER_EXECUTOR.getHandler().post(
- () -> {
- for (int taskId : taskIds) {
- if (taskId != -1) {
- ActivityManagerWrapper.getInstance().removeTask(taskId);
- }
+ UI_HELPER_EXECUTOR.getHandler().post(() -> {
+ if (DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION.isTrue() && isDesktop) {
+ // TODO: b/372005228 - Use the api with desktop id instead.
+ SystemUiProxy.INSTANCE.get(getContext()).removeDesktop(
+ mContainer.getDisplay().getDisplayId());
+ } else {
+ for (int taskId : taskIds) {
+ if (taskId != -1) {
+ ActivityManagerWrapper.getInstance().removeTask(taskId);
}
- });
+ }
+ }
+ });
}
protected void onDismissAnimationEnds() {
diff --git a/src/com/android/launcher3/LauncherSettings.java b/src/com/android/launcher3/LauncherSettings.java
index 87ac193..1d2d161 100644
--- a/src/com/android/launcher3/LauncherSettings.java
+++ b/src/com/android/launcher3/LauncherSettings.java
@@ -16,8 +16,12 @@
package com.android.launcher3;
+import static android.util.Base64.NO_PADDING;
+import static android.util.Base64.NO_WRAP;
+
import android.database.sqlite.SQLiteDatabase;
import android.provider.BaseColumns;
+import android.util.Base64;
import androidx.annotation.NonNull;
@@ -354,8 +358,17 @@
* Launcher settings
*/
public static final class Settings {
- public static final String LAYOUT_DIGEST_KEY = "launcher3.layout.provider.blob";
+ public static final String LAYOUT_PROVIDER_KEY = "launcher3.layout.provider";
public static final String LAYOUT_DIGEST_LABEL = "launcher-layout";
public static final String LAYOUT_DIGEST_TAG = "ignore";
+ public static final String BLOB_KEY_PREFIX = "blob://";
+
+ /**
+ * Creates a key to be used for {@link #LAYOUT_PROVIDER_KEY}
+ * @param digest byte[] representing the message digest for the blob handle
+ */
+ public static String createBlobProviderKey(byte[] digest) {
+ return BLOB_KEY_PREFIX + Base64.encodeToString(digest, NO_WRAP | NO_PADDING);
+ }
}
}
diff --git a/src/com/android/launcher3/dagger/LauncherBaseAppComponent.java b/src/com/android/launcher3/dagger/LauncherBaseAppComponent.java
index d39c5de..088277b 100644
--- a/src/com/android/launcher3/dagger/LauncherBaseAppComponent.java
+++ b/src/com/android/launcher3/dagger/LauncherBaseAppComponent.java
@@ -21,6 +21,7 @@
import com.android.launcher3.pm.InstallSessionHelper;
import com.android.launcher3.util.DaggerSingletonTracker;
import com.android.launcher3.util.ScreenOnTracker;
+import com.android.launcher3.util.SettingsCache;
import dagger.BindsInstance;
@@ -36,6 +37,7 @@
DaggerSingletonTracker getDaggerSingletonTracker();
InstallSessionHelper getInstallSessionHelper();
ScreenOnTracker getScreenOnTracker();
+ SettingsCache getSettingsCache();
/** Builder for LauncherBaseAppComponent. */
interface Builder {
diff --git a/src/com/android/launcher3/model/ModelDbController.java b/src/com/android/launcher3/model/ModelDbController.java
index da1a221..5d66d16 100644
--- a/src/com/android/launcher3/model/ModelDbController.java
+++ b/src/com/android/launcher3/model/ModelDbController.java
@@ -25,7 +25,8 @@
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APP_PAIR;
import static com.android.launcher3.LauncherSettings.Favorites.TABLE_NAME;
import static com.android.launcher3.LauncherSettings.Favorites.addTableToDb;
-import static com.android.launcher3.LauncherSettings.Settings.LAYOUT_DIGEST_KEY;
+import static com.android.launcher3.LauncherSettings.Settings.BLOB_KEY_PREFIX;
+import static com.android.launcher3.LauncherSettings.Settings.LAYOUT_PROVIDER_KEY;
import static com.android.launcher3.LauncherSettings.Settings.LAYOUT_DIGEST_LABEL;
import static com.android.launcher3.LauncherSettings.Settings.LAYOUT_DIGEST_TAG;
import static com.android.launcher3.provider.LauncherDbUtils.tableExists;
@@ -548,9 +549,15 @@
private AutoInstallsLayout createWorkspaceLoaderFromAppRestriction(
LauncherWidgetHolder widgetHolder) {
ContentResolver cr = mContext.getContentResolver();
- String blobHandlerDigest = Settings.Secure.getString(cr, LAYOUT_DIGEST_KEY);
- if (!TextUtils.isEmpty(blobHandlerDigest)) {
+ String systemLayoutProvider = Settings.Secure.getString(cr, LAYOUT_PROVIDER_KEY);
+ if (TextUtils.isEmpty(systemLayoutProvider)) {
+ return null;
+ }
+
+ // Try the blob store first
+ if (systemLayoutProvider.startsWith(BLOB_KEY_PREFIX)) {
BlobStoreManager blobManager = mContext.getSystemService(BlobStoreManager.class);
+ String blobHandlerDigest = systemLayoutProvider.substring(BLOB_KEY_PREFIX.length());
try (InputStream in = new ParcelFileDescriptor.AutoCloseInputStream(
blobManager.openBlob(BlobHandle.createWithSha256(
Base64.decode(blobHandlerDigest, NO_WRAP | NO_PADDING),
@@ -562,25 +569,21 @@
}
}
- String authority = Settings.Secure.getString(cr, "launcher3.layout.provider");
- if (TextUtils.isEmpty(authority)) {
- return null;
- }
-
+ // Try contentProvider based provider
PackageManager pm = mContext.getPackageManager();
- ProviderInfo pi = pm.resolveContentProvider(authority, 0);
+ ProviderInfo pi = pm.resolveContentProvider(systemLayoutProvider, 0);
if (pi == null) {
- Log.e(TAG, "No provider found for authority " + authority);
+ Log.e(TAG, "No provider found for authority " + systemLayoutProvider);
return null;
}
- Uri uri = getLayoutUri(authority, mContext);
+ Uri uri = getLayoutUri(systemLayoutProvider, mContext);
try (InputStream in = cr.openInputStream(uri)) {
- Log.d(TAG, "Loading layout from " + authority);
+ Log.d(TAG, "Loading layout from " + systemLayoutProvider);
Resources res = pm.getResourcesForApplication(pi.applicationInfo);
return getAutoInstallsLayoutFromIS(in, widgetHolder, SourceResources.wrap(res));
} catch (Exception e) {
- Log.e(TAG, "Error getting layout stream from: " + authority , e);
+ Log.e(TAG, "Error getting layout stream from: " + systemLayoutProvider , e);
return null;
}
}
diff --git a/src/com/android/launcher3/model/data/AppPairInfo.kt b/src/com/android/launcher3/model/data/AppPairInfo.kt
index 2eb6154..3496c17 100644
--- a/src/com/android/launcher3/model/data/AppPairInfo.kt
+++ b/src/com/android/launcher3/model/data/AppPairInfo.kt
@@ -74,7 +74,7 @@
(ActivityContext.lookupContext(context) as ActivityContext).getDeviceProfile().isTablet
return Pair(
isTablet || !getFirstApp().isNonResizeable(),
- isTablet || !getSecondApp().isNonResizeable()
+ isTablet || !getSecondApp().isNonResizeable(),
)
}
@@ -105,10 +105,10 @@
}
/** Generates an ItemInfo for logging. */
- override fun buildProto(cInfo: CollectionInfo?): LauncherAtom.ItemInfo {
+ override fun buildProto(cInfo: CollectionInfo?, context: Context): LauncherAtom.ItemInfo {
val appPairIcon = LauncherAtom.FolderIcon.newBuilder().setCardinality(contents.size)
appPairIcon.setLabelInfo(title.toString())
- return getDefaultItemInfoBuilder()
+ return getDefaultItemInfoBuilder(context)
.setFolderIcon(appPairIcon)
.setRank(rank)
.setContainerInfo(getContainerInfo())
diff --git a/src/com/android/launcher3/model/data/CollectionInfo.kt b/src/com/android/launcher3/model/data/CollectionInfo.kt
index 4f5e12f..12ba164 100644
--- a/src/com/android/launcher3/model/data/CollectionInfo.kt
+++ b/src/com/android/launcher3/model/data/CollectionInfo.kt
@@ -17,7 +17,6 @@
package com.android.launcher3.model.data
import com.android.launcher3.LauncherSettings
-import com.android.launcher3.logger.LauncherAtom
import com.android.launcher3.util.ContentWriter
import java.util.function.Predicate
@@ -42,9 +41,4 @@
super.onAddToDatabase(writer)
writer.put(LauncherSettings.Favorites.TITLE, title)
}
-
- /** Returns the collection wrapped as {@link LauncherAtom.ItemInfo} for logging. */
- override fun buildProto(): LauncherAtom.ItemInfo {
- return buildProto(null)
- }
}
diff --git a/src/com/android/launcher3/model/data/FolderInfo.java b/src/com/android/launcher3/model/data/FolderInfo.java
index 18d2b85..f0f2892 100644
--- a/src/com/android/launcher3/model/data/FolderInfo.java
+++ b/src/com/android/launcher3/model/data/FolderInfo.java
@@ -24,6 +24,8 @@
import static com.android.launcher3.logger.LauncherAtom.Attribute.MANUAL_LABEL;
import static com.android.launcher3.logger.LauncherAtom.Attribute.SUGGESTED_LABEL;
+import android.content.Context;
+
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -245,13 +247,13 @@
@NonNull
@Override
- public LauncherAtom.ItemInfo buildProto(@Nullable CollectionInfo cInfo) {
+ public LauncherAtom.ItemInfo buildProto(@Nullable CollectionInfo cInfo, Context context) {
FolderIcon.Builder folderIcon = FolderIcon.newBuilder()
.setCardinality(getContents().size());
if (LabelState.SUGGESTED.equals(getLabelState())) {
folderIcon.setLabelInfo(title.toString());
}
- return getDefaultItemInfoBuilder()
+ return getDefaultItemInfoBuilder(context)
.setFolderIcon(folderIcon)
.setRank(rank)
.addItemAttributes(getLabelState().mLogAttribute)
diff --git a/src/com/android/launcher3/model/data/ItemInfo.java b/src/com/android/launcher3/model/data/ItemInfo.java
index b706d24..c22a8a5 100644
--- a/src/com/android/launcher3/model/data/ItemInfo.java
+++ b/src/com/android/launcher3/model/data/ItemInfo.java
@@ -36,6 +36,7 @@
import android.content.ComponentName;
import android.content.ContentValues;
+import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Process;
@@ -352,16 +353,16 @@
* Creates {@link LauncherAtom.ItemInfo} with important fields and parent container info.
*/
@NonNull
- public LauncherAtom.ItemInfo buildProto() {
- return buildProto(null);
+ public LauncherAtom.ItemInfo buildProto(Context context) {
+ return buildProto(null, context);
}
/**
* Creates {@link LauncherAtom.ItemInfo} with important fields and parent container info.
*/
@NonNull
- public LauncherAtom.ItemInfo buildProto(@Nullable final CollectionInfo cInfo) {
- LauncherAtom.ItemInfo.Builder itemBuilder = getDefaultItemInfoBuilder();
+ public LauncherAtom.ItemInfo buildProto(@Nullable final CollectionInfo cInfo, Context context) {
+ LauncherAtom.ItemInfo.Builder itemBuilder = getDefaultItemInfoBuilder(context);
Optional<ComponentName> nullableComponent = Optional.ofNullable(getTargetComponent());
switch (itemType) {
case ITEM_TYPE_APPLICATION:
@@ -434,10 +435,10 @@
}
@NonNull
- protected LauncherAtom.ItemInfo.Builder getDefaultItemInfoBuilder() {
+ protected LauncherAtom.ItemInfo.Builder getDefaultItemInfoBuilder(Context context) {
LauncherAtom.ItemInfo.Builder itemBuilder = LauncherAtom.ItemInfo.newBuilder();
- SettingsCache.INSTANCE.executeIfCreated(cache ->
- itemBuilder.setIsKidsMode(cache.getValue(NAV_BAR_KIDS_MODE, 0)));
+ itemBuilder.setIsKidsMode(
+ SettingsCache.INSTANCE.get(context).getValue(NAV_BAR_KIDS_MODE, 0));
UserCache.INSTANCE.executeIfCreated(cache ->
itemBuilder.setUserType(getUserType(cache.getUserInfo(user))));
itemBuilder.setRank(rank);
diff --git a/src/com/android/launcher3/model/data/LauncherAppWidgetInfo.java b/src/com/android/launcher3/model/data/LauncherAppWidgetInfo.java
index 361f09d..7569ed5 100644
--- a/src/com/android/launcher3/model/data/LauncherAppWidgetInfo.java
+++ b/src/com/android/launcher3/model/data/LauncherAppWidgetInfo.java
@@ -24,6 +24,7 @@
import android.appwidget.AppWidgetHostView;
import android.content.ComponentName;
+import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
import android.os.Process;
@@ -270,8 +271,9 @@
@NonNull
@Override
- public LauncherAtom.ItemInfo buildProto(@Nullable CollectionInfo collectionInfo) {
- LauncherAtom.ItemInfo info = super.buildProto(collectionInfo);
+ public LauncherAtom.ItemInfo buildProto(
+ @Nullable CollectionInfo collectionInfo, Context context) {
+ LauncherAtom.ItemInfo info = super.buildProto(collectionInfo, context);
return info.toBuilder()
.setWidget(info.getWidget().toBuilder().setWidgetFeatures(widgetFeatures))
.addItemAttributes(getAttribute(sourceContainer))
diff --git a/src/com/android/launcher3/util/MainThreadInitializedObject.java b/src/com/android/launcher3/util/MainThreadInitializedObject.java
index a7d5c13..9a70298 100644
--- a/src/com/android/launcher3/util/MainThreadInitializedObject.java
+++ b/src/com/android/launcher3/util/MainThreadInitializedObject.java
@@ -50,7 +50,7 @@
public T get(Context context) {
Context app = context.getApplicationContext();
- if (app instanceof SandboxApplication sc) {
+ if (app instanceof ObjectSandbox sc) {
return sc.getObject(this);
}
@@ -100,7 +100,8 @@
T get(Context context);
}
- public interface SandboxApplication {
+ /** Sandbox for isolating {@link MainThreadInitializedObject} instances from Launcher. */
+ public interface ObjectSandbox {
/**
* Find a cached object from mObjectMap if we have already created one. If not, generate
@@ -116,7 +117,7 @@
<T extends SafeCloseable> void putObject(MainThreadInitializedObject<T> object, T value);
/**
- * Returns whether this context should cleanup all objects when its destroyed or leave it
+ * Returns whether this sandbox should cleanup all objects when its destroyed or leave it
* to the GC.
* These objects can have listeners attached to the system server and mey not be able to get
* GCed themselves when running on a device.
@@ -137,7 +138,7 @@
* Abstract Context which allows custom implementations for
* {@link MainThreadInitializedObject} providers
*/
- public static class SandboxContext extends LauncherApplication implements SandboxApplication {
+ public static class SandboxContext extends LauncherApplication implements ObjectSandbox {
private static final String TAG = "SandboxContext";
@@ -159,8 +160,8 @@
@Override
public boolean shouldCleanUpOnDestroy() {
- return (getBaseContext().getApplicationContext() instanceof SandboxApplication sa)
- ? sa.shouldCleanUpOnDestroy() : true;
+ return (getBaseContext().getApplicationContext() instanceof ObjectSandbox os)
+ ? os.shouldCleanUpOnDestroy() : true;
}
public void onDestroy() {
diff --git a/src/com/android/launcher3/util/SettingsCache.java b/src/com/android/launcher3/util/SettingsCache.java
index cd6701d..a1ed499 100644
--- a/src/com/android/launcher3/util/SettingsCache.java
+++ b/src/com/android/launcher3/util/SettingsCache.java
@@ -25,14 +25,21 @@
import android.database.ContentObserver;
import android.net.Uri;
import android.os.Handler;
+import android.os.Looper;
import android.provider.Settings;
+import com.android.launcher3.dagger.ApplicationContext;
+import com.android.launcher3.dagger.LauncherAppSingleton;
+import com.android.launcher3.dagger.LauncherBaseAppComponent;
+
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
+import javax.inject.Inject;
+
/**
* ContentObserver over Settings keys that also has a caching layer.
* Consumers can register for callbacks via {@link #register(Uri, OnChangeListener)} and
@@ -47,6 +54,7 @@
*
* Cache will also be updated if a key queried is missing (even if it has no listeners registered).
*/
+@LauncherAppSingleton
public class SettingsCache extends ContentObserver implements SafeCloseable {
/** Hidden field Settings.Secure.NOTIFICATION_BADGING */
@@ -79,12 +87,14 @@
/**
* Singleton instance
*/
- public static MainThreadInitializedObject<SettingsCache> INSTANCE =
- new MainThreadInitializedObject<>(SettingsCache::new);
+ public static final DaggerSingletonObject<SettingsCache> INSTANCE =
+ new DaggerSingletonObject<>(LauncherBaseAppComponent::getSettingsCache);
- private SettingsCache(Context context) {
- super(new Handler());
+ @Inject
+ SettingsCache(@ApplicationContext Context context, DaggerSingletonTracker tracker) {
+ super(new Handler(Looper.getMainLooper()));
mResolver = context.getContentResolver();
+ ExecutorUtil.executeSyncOnMainOrFail(() -> tracker.addCloseable(this));
}
@Override
diff --git a/src/com/android/launcher3/widget/PendingAddWidgetInfo.java b/src/com/android/launcher3/widget/PendingAddWidgetInfo.java
index a916252..23ab0fb 100644
--- a/src/com/android/launcher3/widget/PendingAddWidgetInfo.java
+++ b/src/com/android/launcher3/widget/PendingAddWidgetInfo.java
@@ -82,8 +82,9 @@
@NonNull
@Override
- public LauncherAtom.ItemInfo buildProto(@Nullable CollectionInfo collectionInfo) {
- LauncherAtom.ItemInfo info = super.buildProto(collectionInfo);
+ public LauncherAtom.ItemInfo buildProto(
+ @Nullable CollectionInfo collectionInfo, Context context) {
+ LauncherAtom.ItemInfo info = super.buildProto(collectionInfo, context);
return info.toBuilder()
.addItemAttributes(LauncherAppWidgetInfo.getAttribute(sourceContainer))
.build();
diff --git a/tests/multivalentTests/src/com/android/launcher3/folder/PreviewItemManagerTest.kt b/tests/multivalentTests/src/com/android/launcher3/folder/PreviewItemManagerTest.kt
index d236551..111ffaa 100644
--- a/tests/multivalentTests/src/com/android/launcher3/folder/PreviewItemManagerTest.kt
+++ b/tests/multivalentTests/src/com/android/launcher3/folder/PreviewItemManagerTest.kt
@@ -29,7 +29,6 @@
import com.android.launcher3.icons.BaseIconFactory
import com.android.launcher3.icons.FastBitmapDrawable
import com.android.launcher3.icons.UserBadgeDrawable
-import com.android.launcher3.model.ModelTestRule
import com.android.launcher3.model.data.FolderInfo
import com.android.launcher3.model.data.ItemInfo
import com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_ARCHIVED
@@ -45,7 +44,6 @@
import com.google.common.truth.Truth.assertThat
import org.junit.After
import org.junit.Before
-import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@@ -54,8 +52,6 @@
@RunWith(AndroidJUnit4::class)
class PreviewItemManagerTest {
- @get:Rule val modelTestRule = ModelTestRule()
-
private lateinit var previewItemManager: PreviewItemManager
private lateinit var context: Context
private lateinit var folderItems: ArrayList<ItemInfo>
@@ -99,8 +95,8 @@
BaseIconFactory(
context,
context.resources.configuration.densityDpi,
- previewItemManager.mIconSize
- )
+ previewItemManager.mIconSize,
+ ),
)
// Set second icon to be non-themed.
@@ -111,8 +107,8 @@
BaseIconFactory(
context,
context.resources.configuration.densityDpi,
- previewItemManager.mIconSize
- )
+ previewItemManager.mIconSize,
+ ),
)
// Set third icon to be themed with badge.
@@ -123,8 +119,8 @@
BaseIconFactory(
context,
context.resources.configuration.densityDpi,
- previewItemManager.mIconSize
- )
+ previewItemManager.mIconSize,
+ ),
)
folderApps[2].bitmap = folderApps[2].bitmap.withFlags(profileFlagOp(UserIconInfo.TYPE_WORK))
@@ -137,8 +133,8 @@
BaseIconFactory(
context,
context.resources.configuration.densityDpi,
- previewItemManager.mIconSize
- )
+ previewItemManager.mIconSize,
+ ),
)
defaultThemedIcons = get(context).get(THEMED_ICONS)
diff --git a/tests/multivalentTests/src/com/android/launcher3/icons/IconCacheTest.java b/tests/multivalentTests/src/com/android/launcher3/icons/IconCacheTest.java
index 519108d..ce00b28 100644
--- a/tests/multivalentTests/src/com/android/launcher3/icons/IconCacheTest.java
+++ b/tests/multivalentTests/src/com/android/launcher3/icons/IconCacheTest.java
@@ -68,7 +68,6 @@
import com.android.launcher3.util.ApplicationInfoWrapper;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.PackageUserKey;
-import com.android.launcher3.util.RoboApiWrapper;
import com.google.common.truth.Truth;
@@ -148,7 +147,6 @@
@Test
public void launcherActivityInfo_cached_in_memory() {
- RoboApiWrapper.INSTANCE.initialize();
ComponentName cn = new ComponentName(TEST_PACKAGE, TEST_ACTIVITY);
UserHandle user = myUserHandle();
ComponentKey cacheKey = new ComponentKey(cn, user);
@@ -213,7 +211,6 @@
@Test
public void item_kept_in_db_if_nothing_changes() {
- RoboApiWrapper.INSTANCE.initialize();
ComponentName cn = new ComponentName(TEST_PACKAGE, TEST_ACTIVITY);
UserHandle user = myUserHandle();
@@ -232,7 +229,6 @@
@Test
public void item_updated_in_db_if_appInfo_changes() {
- RoboApiWrapper.INSTANCE.initialize();
ComponentName cn = new ComponentName(TEST_PACKAGE, TEST_ACTIVITY);
UserHandle user = myUserHandle();
@@ -253,7 +249,6 @@
@Test
public void item_removed_in_db_if_item_removed() {
- RoboApiWrapper.INSTANCE.initialize();
ComponentName cn = new ComponentName(TEST_PACKAGE, TEST_ACTIVITY);
UserHandle user = myUserHandle();
diff --git a/tests/multivalentTests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.kt b/tests/multivalentTests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.kt
index 43dc36b..ce04682 100644
--- a/tests/multivalentTests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.kt
+++ b/tests/multivalentTests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.kt
@@ -27,7 +27,6 @@
import com.google.common.truth.Truth.assertThat
import org.junit.After
import org.junit.Before
-import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mockito.times
@@ -44,8 +43,6 @@
@RunWith(AndroidJUnit4::class)
class AddWorkspaceItemsTaskTest : AbstractWorkspaceModelTest() {
- @get:Rule val modelTestRule = ModelTestRule()
-
private lateinit var mDataModelCallbacks: MyCallbacks
private val mWorkspaceItemSpaceFinder: WorkspaceItemSpaceFinder = mock()
@@ -121,18 +118,8 @@
@Test
fun givenMultipleItems_whenExecuteTask_thenAddThem() {
val itemsToAdd =
- arrayOf(
- getNewItem(),
- getExistingItem(),
- getNewItem(),
- getNewItem(),
- getExistingItem(),
- )
- givenNewItemSpaces(
- NewItemSpace(1, 3, 3),
- NewItemSpace(2, 0, 0),
- NewItemSpace(2, 0, 1),
- )
+ arrayOf(getNewItem(), getExistingItem(), getNewItem(), getNewItem(), getExistingItem())
+ givenNewItemSpaces(NewItemSpace(1, 3, 3), NewItemSpace(2, 0, 0), NewItemSpace(2, 0, 1))
val nonEmptyScreenIds = listOf(0, 1)
val addedItems = testAddItems(nonEmptyScreenIds, *itemsToAdd)
@@ -173,7 +160,7 @@
eq(IntArray.wrap(*nonEmptyScreenIds.toIntArray())),
eq(IntArray()),
eq(1),
- eq(1)
+ eq(1),
)
}
@@ -183,7 +170,7 @@
*/
private fun testAddItems(
nonEmptyScreenIds: List<Int>,
- vararg itemsToAdd: WorkspaceItemInfo
+ vararg itemsToAdd: WorkspaceItemInfo,
): List<AddedItem> {
setupWorkspaces(nonEmptyScreenIds)
val task = newTask(*itemsToAdd)
@@ -220,7 +207,7 @@
override fun bindAppsAdded(
newScreens: IntArray?,
addNotAnimated: ArrayList<ItemInfo>,
- addAnimated: ArrayList<ItemInfo>
+ addAnimated: ArrayList<ItemInfo>,
) {
addedItems.addAll(addAnimated.map { AddedItem(it, true) })
addedItems.addAll(addNotAnimated.map { AddedItem(it, false) })
diff --git a/tests/multivalentTests/src/com/android/launcher3/model/AsyncBindingTest.kt b/tests/multivalentTests/src/com/android/launcher3/model/AsyncBindingTest.kt
index dce75b9..c91577f 100644
--- a/tests/multivalentTests/src/com/android/launcher3/model/AsyncBindingTest.kt
+++ b/tests/multivalentTests/src/com/android/launcher3/model/AsyncBindingTest.kt
@@ -64,8 +64,6 @@
@get:Rule val setFlagsRule = SetFlagsRule()
- @get:Rule val modelTestRule = ModelTestRule()
-
@Spy private var callbacks = MyCallbacks()
@Mock private lateinit var itemInflater: ItemInflater<*>
@@ -203,7 +201,7 @@
pendingTasks: RunnableList,
onCompleteSignal: RunnableList,
workspaceItemCount: Int,
- isBindSync: Boolean
+ isBindSync: Boolean,
) {
this.pendingTasks = pendingTasks
this.onCompleteSignal = onCompleteSignal
diff --git a/tests/multivalentTests/src/com/android/launcher3/model/CacheDataUpdatedTaskTest.java b/tests/multivalentTests/src/com/android/launcher3/model/CacheDataUpdatedTaskTest.java
index 535080a..600af42 100644
--- a/tests/multivalentTests/src/com/android/launcher3/model/CacheDataUpdatedTaskTest.java
+++ b/tests/multivalentTests/src/com/android/launcher3/model/CacheDataUpdatedTaskTest.java
@@ -64,11 +64,7 @@
@RunWith(AndroidJUnit4.class)
public class CacheDataUpdatedTaskTest {
- @Rule(order = 0)
- public TestRule testStabilityRule = new TestStabilityRule();
-
- @Rule(order = 1)
- public ModelTestRule mModelTestRule = new ModelTestRule();
+ @Rule public TestRule testStabilityRule = new TestStabilityRule();
private static final String PENDING_APP_1 = TEST_PACKAGE + ".pending1";
private static final String PENDING_APP_2 = TEST_PACKAGE + ".pending2";
diff --git a/tests/multivalentTests/src/com/android/launcher3/model/DefaultLayoutProviderTest.java b/tests/multivalentTests/src/com/android/launcher3/model/DefaultLayoutProviderTest.java
index e14e145..1e2431f 100644
--- a/tests/multivalentTests/src/com/android/launcher3/model/DefaultLayoutProviderTest.java
+++ b/tests/multivalentTests/src/com/android/launcher3/model/DefaultLayoutProviderTest.java
@@ -39,7 +39,6 @@
import org.junit.After;
import org.junit.Before;
-import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -50,8 +49,6 @@
@RunWith(AndroidJUnit4.class)
public class DefaultLayoutProviderTest {
- @Rule public ModelTestRule rule = new ModelTestRule();
-
private LauncherModelHelper mModelHelper;
private LauncherModelHelper.SandboxModelContext mTargetContext;
diff --git a/tests/multivalentTests/src/com/android/launcher3/model/FirstScreenBroadcastHelperTest.kt b/tests/multivalentTests/src/com/android/launcher3/model/FirstScreenBroadcastHelperTest.kt
index d2d9512..9cc380e 100644
--- a/tests/multivalentTests/src/com/android/launcher3/model/FirstScreenBroadcastHelperTest.kt
+++ b/tests/multivalentTests/src/com/android/launcher3/model/FirstScreenBroadcastHelperTest.kt
@@ -34,7 +34,6 @@
import com.android.launcher3.util.PackageManagerHelper
import com.android.launcher3.util.PackageUserKey
import junit.framework.Assert.assertEquals
-import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
@@ -46,8 +45,6 @@
@RunWith(AndroidJUnit4::class)
class FirstScreenBroadcastHelperTest {
- @get:Rule val modelTestRule = ModelTestRule()
-
private val context = spy(InstrumentationRegistry.getInstrumentation().targetContext)
private val mockPmHelper = mock<PackageManagerHelper>()
private val expectedAppPackage = "appPackageExpected"
@@ -70,7 +67,7 @@
container = CONTAINER_HOTSEAT
intent = expectedIntent
},
- LauncherAppWidgetInfo().apply { providerName = expectedComponentName }
+ LauncherAppWidgetInfo().apply { providerName = expectedComponentName },
)
@Test
@@ -89,7 +86,7 @@
val sessionInfoMap: HashMap<PackageUserKey, SessionInfo> =
hashMapOf(
PackageUserKey(unexpectedAppPackage, UserHandle(0)) to sessionInfoExpected,
- PackageUserKey(expectedAppPackage, UserHandle(0)) to sessionInfoUnexpected
+ PackageUserKey(expectedAppPackage, UserHandle(0)) to sessionInfoUnexpected,
)
// When
@@ -98,7 +95,7 @@
packageManagerHelper = mockPmHelper,
firstScreenItems = firstScreenItems,
userKeyToSessionMap = sessionInfoMap,
- allWidgets = listOf()
+ allWidgets = listOf(),
)
// Then
@@ -108,7 +105,7 @@
installerPackage = expectedInstallerPackage,
pendingWorkspaceItems = mutableSetOf(expectedAppPackage),
pendingHotseatItems = mutableSetOf(expectedAppPackage),
- pendingWidgetItems = mutableSetOf(expectedAppPackage)
+ pendingWidgetItems = mutableSetOf(expectedAppPackage),
)
)
@@ -133,7 +130,7 @@
providerName = expectedComponentName
screenId = 0
}
- )
+ ),
)
// Then
@@ -143,7 +140,7 @@
installerPackage = expectedInstallerPackage,
installedHotseatItems = mutableSetOf(expectedAppPackage),
installedWorkspaceItems = mutableSetOf(expectedAppPackage),
- firstScreenInstalledWidgets = mutableSetOf(expectedAppPackage)
+ firstScreenInstalledWidgets = mutableSetOf(expectedAppPackage),
)
)
assertEquals(expectedResult, actualResult)
@@ -178,8 +175,8 @@
LauncherAppWidgetInfo().apply {
providerName = unexpectedComponentName
screenId = 0
- }
- )
+ },
+ ),
)
// Then
@@ -190,7 +187,7 @@
installedHotseatItems = mutableSetOf(),
installedWorkspaceItems = mutableSetOf(),
firstScreenInstalledWidgets = mutableSetOf(expectedAppPackage),
- secondaryScreenInstalledWidgets = mutableSetOf(expectedAppPackage2)
+ secondaryScreenInstalledWidgets = mutableSetOf(expectedAppPackage2),
)
)
assertEquals(expectedResult, actualResult)
@@ -224,7 +221,7 @@
packageManagerHelper = mockPmHelper,
firstScreenItems = firstScreenItems,
userKeyToSessionMap = sessionInfoMap,
- allWidgets = listOf()
+ allWidgets = listOf(),
)
// Then
@@ -232,7 +229,7 @@
listOf(
FirstScreenBroadcastModel(
installerPackage = expectedInstallerPackage,
- pendingCollectionItems = mutableSetOf(expectedAppPackage)
+ pendingCollectionItems = mutableSetOf(expectedAppPackage),
)
)
assertEquals(expectedResult, actualResult)
@@ -259,7 +256,7 @@
firstScreenInstalledWidgets =
mutableSetOf<String>().apply { repeat(20) { add(it.toString()) } },
secondaryScreenInstalledWidgets =
- mutableSetOf<String>().apply { repeat(20) { add(it.toString()) } }
+ mutableSetOf<String>().apply { repeat(20) { add(it.toString()) } },
)
// When
@@ -334,7 +331,7 @@
installedWorkspaceItems = mutableSetOf("installedWorkspaceItems"),
installedHotseatItems = mutableSetOf("installedHotseatItems"),
firstScreenInstalledWidgets = mutableSetOf("firstScreenInstalledWidgetItems"),
- secondaryScreenInstalledWidgets = mutableSetOf("secondaryInstalledWidgetItems")
+ secondaryScreenInstalledWidgets = mutableSetOf("secondaryInstalledWidgetItems"),
)
)
val expectedPendingIntent =
@@ -342,7 +339,7 @@
context,
0 /* requestCode */,
Intent(),
- PendingIntent.FLAG_ONE_SHOT or PendingIntent.FLAG_IMMUTABLE
+ PendingIntent.FLAG_ONE_SHOT or PendingIntent.FLAG_IMMUTABLE,
)
// When
@@ -354,40 +351,40 @@
assertEquals(
"com.android.launcher3.action.FIRST_SCREEN_ACTIVE_INSTALLS",
- argumentCaptor.value.action
+ argumentCaptor.value.action,
)
assertEquals(expectedInstallerPackage, argumentCaptor.value.`package`)
assertEquals(
expectedPendingIntent,
- argumentCaptor.value.getParcelableExtra("verificationToken")
+ argumentCaptor.value.getParcelableExtra("verificationToken"),
)
assertEquals(
arrayListOf("pendingCollectionItem"),
- argumentCaptor.value.getStringArrayListExtra("folderItem")
+ argumentCaptor.value.getStringArrayListExtra("folderItem"),
)
assertEquals(
arrayListOf("pendingWorkspaceItem"),
- argumentCaptor.value.getStringArrayListExtra("workspaceItem")
+ argumentCaptor.value.getStringArrayListExtra("workspaceItem"),
)
assertEquals(
arrayListOf("pendingHotseatItems"),
- argumentCaptor.value.getStringArrayListExtra("hotseatItem")
+ argumentCaptor.value.getStringArrayListExtra("hotseatItem"),
)
assertEquals(
arrayListOf("pendingWidgetItems"),
- argumentCaptor.value.getStringArrayListExtra("widgetItem")
+ argumentCaptor.value.getStringArrayListExtra("widgetItem"),
)
assertEquals(
arrayListOf("installedWorkspaceItems"),
- argumentCaptor.value.getStringArrayListExtra("workspaceInstalledItems")
+ argumentCaptor.value.getStringArrayListExtra("workspaceInstalledItems"),
)
assertEquals(
arrayListOf("installedHotseatItems"),
- argumentCaptor.value.getStringArrayListExtra("hotseatInstalledItems")
+ argumentCaptor.value.getStringArrayListExtra("hotseatInstalledItems"),
)
assertEquals(
arrayListOf("firstScreenInstalledWidgetItems", "secondaryInstalledWidgetItems"),
- argumentCaptor.value.getStringArrayListExtra("widgetInstalledItems")
+ argumentCaptor.value.getStringArrayListExtra("widgetInstalledItems"),
)
}
}
diff --git a/tests/multivalentTests/src/com/android/launcher3/model/FolderIconLoadTest.kt b/tests/multivalentTests/src/com/android/launcher3/model/FolderIconLoadTest.kt
index 4ca47e3..e8f778f 100644
--- a/tests/multivalentTests/src/com/android/launcher3/model/FolderIconLoadTest.kt
+++ b/tests/multivalentTests/src/com/android/launcher3/model/FolderIconLoadTest.kt
@@ -30,7 +30,6 @@
import com.google.common.truth.Truth.assertWithMessage
import org.junit.After
import org.junit.Before
-import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@@ -39,8 +38,6 @@
@RunWith(AndroidJUnit4::class)
class FolderIconLoadTest {
- @get:Rule(order = 0) val modelTestRule = ModelTestRule()
-
private lateinit var modelHelper: LauncherModelHelper
private val uniqueActivities =
diff --git a/tests/multivalentTests/src/com/android/launcher3/model/LoaderCursorTest.java b/tests/multivalentTests/src/com/android/launcher3/model/LoaderCursorTest.java
index ac911b3..b4945d7 100644
--- a/tests/multivalentTests/src/com/android/launcher3/model/LoaderCursorTest.java
+++ b/tests/multivalentTests/src/com/android/launcher3/model/LoaderCursorTest.java
@@ -67,7 +67,6 @@
import org.junit.After;
import org.junit.Before;
-import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -78,8 +77,6 @@
@RunWith(AndroidJUnit4.class)
public class LoaderCursorTest {
- @Rule public ModelTestRule rule = new ModelTestRule();
-
private LauncherModelHelper mModelHelper;
private LauncherAppState mApp;
private PackageManagerHelper mPmHelper;
diff --git a/tests/multivalentTests/src/com/android/launcher3/model/ModelTestRule.kt b/tests/multivalentTests/src/com/android/launcher3/model/ModelTestRule.kt
deleted file mode 100644
index ad2c2a4..0000000
--- a/tests/multivalentTests/src/com/android/launcher3/model/ModelTestRule.kt
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher3.model
-
-import com.android.launcher3.util.RoboApiWrapper
-import org.junit.rules.TestWatcher
-import org.junit.runner.Description
-
-class ModelTestRule : TestWatcher() {
- override fun starting(description: Description?) {
- RoboApiWrapper.initialize()
- }
-}
diff --git a/tests/multivalentTests/src/com/android/launcher3/model/PackageInstallStateChangedTaskTest.java b/tests/multivalentTests/src/com/android/launcher3/model/PackageInstallStateChangedTaskTest.java
index a0d9da9..0f1fc00 100644
--- a/tests/multivalentTests/src/com/android/launcher3/model/PackageInstallStateChangedTaskTest.java
+++ b/tests/multivalentTests/src/com/android/launcher3/model/PackageInstallStateChangedTaskTest.java
@@ -37,7 +37,6 @@
import org.junit.After;
import org.junit.Before;
-import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -48,8 +47,6 @@
@RunWith(AndroidJUnit4.class)
public class PackageInstallStateChangedTaskTest {
- @Rule public ModelTestRule mModelTestRule = new ModelTestRule();
-
private static final String PENDING_APP_1 = TEST_PACKAGE + ".pending1";
private static final String PENDING_APP_2 = TEST_PACKAGE + ".pending2";
diff --git a/tests/multivalentTests/src/com/android/launcher3/model/WorkspaceItemProcessorTest.kt b/tests/multivalentTests/src/com/android/launcher3/model/WorkspaceItemProcessorTest.kt
index c7abce6..ed8b397 100644
--- a/tests/multivalentTests/src/com/android/launcher3/model/WorkspaceItemProcessorTest.kt
+++ b/tests/multivalentTests/src/com/android/launcher3/model/WorkspaceItemProcessorTest.kt
@@ -58,7 +58,6 @@
import com.google.common.truth.Truth.assertThat
import com.google.common.truth.Truth.assertWithMessage
import org.junit.Before
-import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
@@ -77,8 +76,6 @@
@RunWith(AndroidJUnit4::class)
class WorkspaceItemProcessorTest {
- @get:Rule val modelTestRule = ModelTestRule()
-
@Mock private lateinit var mockIconRequestInfo: IconRequestInfo<WorkspaceItemInfo>
@Mock private lateinit var mockWorkspaceInfo: WorkspaceItemInfo
@Mock private lateinit var mockBgDataModel: BgDataModel
diff --git a/tests/multivalentTests/src/com/android/launcher3/model/WorkspaceItemSpaceFinderTest.kt b/tests/multivalentTests/src/com/android/launcher3/model/WorkspaceItemSpaceFinderTest.kt
index ae8e966..dd03eee 100644
--- a/tests/multivalentTests/src/com/android/launcher3/model/WorkspaceItemSpaceFinderTest.kt
+++ b/tests/multivalentTests/src/com/android/launcher3/model/WorkspaceItemSpaceFinderTest.kt
@@ -21,7 +21,6 @@
import com.google.common.truth.Truth.assertThat
import org.junit.After
import org.junit.Before
-import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@@ -30,8 +29,6 @@
@RunWith(AndroidJUnit4::class)
class WorkspaceItemSpaceFinderTest : AbstractWorkspaceModelTest() {
- @get:Rule val modelTestRule = ModelTestRule()
-
private val mItemSpaceFinder = WorkspaceItemSpaceFinder()
@Before
@@ -52,7 +49,7 @@
mExistingScreens,
mNewScreens,
spanX,
- spanY
+ spanY,
)
.let { NewItemSpace.fromIntArray(it) }
@@ -62,7 +59,7 @@
newItemSpace.cellX,
newItemSpace.cellY,
spanX,
- spanY
+ spanY,
)
)
.isTrue()
@@ -171,7 +168,7 @@
screen0 = listOf(Rect(2, 0, 5, 2)),
screen1 = fullScreenSpaces, // full screens are skipped
screen2 = fullScreenSpaces, // full screens are skipped
- screen3 = emptyScreenSpaces
+ screen3 = emptyScreenSpaces,
)
val spaceFound = findSpace(3, 1)
diff --git a/tests/multivalentTests/src/com/android/launcher3/pm/InstallSessionTrackerTest.kt b/tests/multivalentTests/src/com/android/launcher3/pm/InstallSessionTrackerTest.kt
index d860710..15a9964 100644
--- a/tests/multivalentTests/src/com/android/launcher3/pm/InstallSessionTrackerTest.kt
+++ b/tests/multivalentTests/src/com/android/launcher3/pm/InstallSessionTrackerTest.kt
@@ -26,7 +26,6 @@
import androidx.test.filters.SdkSuppress
import androidx.test.filters.SmallTest
import com.android.launcher3.Flags.FLAG_ENABLE_SUPPORT_FOR_ARCHIVING
-import com.android.launcher3.model.ModelTestRule
import com.android.launcher3.util.Executors.MODEL_EXECUTOR
import com.android.launcher3.util.LauncherModelHelper
import com.android.launcher3.util.PackageUserKey
@@ -45,9 +44,7 @@
@SmallTest
@RunWith(AndroidJUnit4::class)
class InstallSessionTrackerTest {
- @get:Rule(order = 0) val setFlagsRule = SetFlagsRule()
-
- @get:Rule(order = 1) val modelTestRule = ModelTestRule()
+ @get:Rule val setFlagsRule = SetFlagsRule()
private val mockInstallSessionHelper: InstallSessionHelper = mock()
private val mockCallback: InstallSessionTracker.Callback = mock()
@@ -67,7 +64,7 @@
mockInstallSessionHelper,
mockCallback,
mockPackageInstaller,
- launcherApps
+ launcherApps,
)
}
diff --git a/tests/multivalentTests/src/com/android/launcher3/pm/UserCacheTest.kt b/tests/multivalentTests/src/com/android/launcher3/pm/UserCacheTest.kt
index 482dced..5f08c31 100644
--- a/tests/multivalentTests/src/com/android/launcher3/pm/UserCacheTest.kt
+++ b/tests/multivalentTests/src/com/android/launcher3/pm/UserCacheTest.kt
@@ -20,7 +20,6 @@
import android.os.UserHandle
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry
-import com.android.launcher3.model.ModelTestRule
import com.android.launcher3.util.Executors.MODEL_EXECUTOR
import com.android.launcher3.util.LauncherModelHelper
import com.android.launcher3.util.TestUtil
@@ -28,15 +27,12 @@
import com.google.common.truth.Truth.assertThat
import org.junit.After
import org.junit.Before
-import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class UserCacheTest {
- @get:Rule val modelTestRule = ModelTestRule()
-
private val launcherModelHelper = LauncherModelHelper()
private val sandboxContext = launcherModelHelper.sandboxContext
private lateinit var userCache: UserCache
diff --git a/tests/multivalentTests/src/com/android/launcher3/util/LauncherModelHelper.java b/tests/multivalentTests/src/com/android/launcher3/util/LauncherModelHelper.java
index 748d376..09b9a3b 100644
--- a/tests/multivalentTests/src/com/android/launcher3/util/LauncherModelHelper.java
+++ b/tests/multivalentTests/src/com/android/launcher3/util/LauncherModelHelper.java
@@ -31,6 +31,7 @@
import android.content.ContentProvider;
import android.content.ContentResolver;
+import android.content.Context;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageInstaller.SessionParams;
import android.content.pm.PackageManager;
@@ -250,15 +251,16 @@
private final File mDbDir;
public SandboxModelContext() {
- super(ApplicationProvider.getApplicationContext());
+ this(ApplicationProvider.getApplicationContext());
+ }
+
+ public SandboxModelContext(Context context) {
+ super(context);
// System settings cache content provider. Ensure that they are statically initialized
- Settings.Secure.getString(
- ApplicationProvider.getApplicationContext().getContentResolver(), "test");
- Settings.System.getString(
- ApplicationProvider.getApplicationContext().getContentResolver(), "test");
- Settings.Global.getString(
- ApplicationProvider.getApplicationContext().getContentResolver(), "test");
+ Settings.Secure.getString(context.getContentResolver(), "test");
+ Settings.System.getString(context.getContentResolver(), "test");
+ Settings.Global.getString(context.getContentResolver(), "test");
mPm = spy(getBaseContext().getPackageManager());
mDbDir = new File(getCacheDir(), UUID.randomUUID().toString());
diff --git a/tests/multivalentTests/src/com/android/launcher3/util/SandboxApplication.kt b/tests/multivalentTests/src/com/android/launcher3/util/SandboxApplication.kt
new file mode 100644
index 0000000..4f9b8c7
--- /dev/null
+++ b/tests/multivalentTests/src/com/android/launcher3/util/SandboxApplication.kt
@@ -0,0 +1,166 @@
+/*
+ * 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.util
+
+import android.content.Context
+import android.content.ContextParams
+import android.content.ContextWrapper
+import android.content.pm.ApplicationInfo
+import android.content.res.Configuration
+import android.os.Bundle
+import android.os.IBinder
+import android.os.UserHandle
+import android.view.Display
+import androidx.test.core.app.ApplicationProvider
+import com.android.launcher3.util.LauncherModelHelper.SandboxModelContext
+import com.android.launcher3.util.MainThreadInitializedObject.ObjectSandbox
+import org.junit.Rule
+import org.junit.rules.ExternalResource
+import org.junit.rules.TestRule
+import org.junit.runner.Description
+import org.junit.runners.model.Statement
+
+/**
+ * Sandbox application where created [Context] instances are still sandboxed within it.
+ *
+ * Tests can declare this application as a [Rule], so that it is set up and destroyed automatically.
+ * Alternatively, they can call [init] and [onDestroy] directly. Either way, these need to be called
+ * for it to work and avoid leaks from created singletons.
+ *
+ * The create [Context] APIs construct a `ContextImpl`, which resets the application to the true
+ * application, thus leaving the sandbox. This implementation wraps the created contexts to
+ * propagate this application (see [SandboxApplicationWrapper]).
+ */
+class SandboxApplication private constructor(private val base: SandboxApplicationWrapper) :
+ SandboxModelContext(base), TestRule {
+
+ constructor(
+ base: Context = ApplicationProvider.getApplicationContext()
+ ) : this(SandboxApplicationWrapper(base))
+
+ /**
+ * Initializes the sandbox application propagation logic.
+ *
+ * This function either needs to be called manually or automatically through using [Rule].
+ */
+ fun init() {
+ base.app = this@SandboxApplication
+ }
+
+ /** Returns `this` if [init] was called, otherwise crashes the test. */
+ override fun getApplicationContext(): Context = base.applicationContext
+
+ override fun shouldCleanUpOnDestroy(): Boolean {
+ // Defer to the true application to decide whether to clean up. For instance, we do not want
+ // to cleanup under Robolectric.
+ val app = ApplicationProvider.getApplicationContext<Context>()
+ return if (app is ObjectSandbox) app.shouldCleanUpOnDestroy() else true
+ }
+
+ override fun apply(statement: Statement, description: Description): Statement {
+ return object : ExternalResource() {
+ override fun before() {
+ base.app = this@SandboxApplication
+ }
+
+ override fun after() = onDestroy()
+ }
+ .apply(statement, description)
+ }
+}
+
+private class SandboxApplicationWrapper(base: Context, var app: Context? = null) :
+ ContextWrapper(base) {
+
+ override fun getApplicationContext(): Context {
+ return checkNotNull(app) { "SandboxApplication accessed before #init() was called." }
+ }
+
+ override fun createPackageContext(packageName: String?, flags: Int): Context {
+ return SandboxApplicationWrapper(super.createPackageContext(packageName, flags), app)
+ }
+
+ override fun createPackageContextAsUser(
+ packageName: String,
+ flags: Int,
+ user: UserHandle,
+ ): Context {
+ return SandboxApplicationWrapper(
+ super.createPackageContextAsUser(packageName, flags, user),
+ app,
+ )
+ }
+
+ override fun createContextAsUser(user: UserHandle, flags: Int): Context {
+ return SandboxApplicationWrapper(super.createContextAsUser(user, flags), app)
+ }
+
+ override fun createApplicationContext(application: ApplicationInfo?, flags: Int): Context {
+ return SandboxApplicationWrapper(super.createApplicationContext(application, flags), app)
+ }
+
+ override fun createContextForSdkInSandbox(sdkInfo: ApplicationInfo, flags: Int): Context {
+ return SandboxApplicationWrapper(super.createContextForSdkInSandbox(sdkInfo, flags), app)
+ }
+
+ override fun createContextForSplit(splitName: String?): Context {
+ return SandboxApplicationWrapper(super.createContextForSplit(splitName), app)
+ }
+
+ override fun createConfigurationContext(overrideConfiguration: Configuration): Context {
+ return SandboxApplicationWrapper(
+ super.createConfigurationContext(overrideConfiguration),
+ app,
+ )
+ }
+
+ override fun createDisplayContext(display: Display): Context {
+ return SandboxApplicationWrapper(super.createDisplayContext(display), app)
+ }
+
+ override fun createDeviceContext(deviceId: Int): Context {
+ return SandboxApplicationWrapper(super.createDeviceContext(deviceId), app)
+ }
+
+ override fun createWindowContext(type: Int, options: Bundle?): Context {
+ return SandboxApplicationWrapper(super.createWindowContext(type, options), app)
+ }
+
+ override fun createWindowContext(display: Display, type: Int, options: Bundle?): Context {
+ return SandboxApplicationWrapper(super.createWindowContext(display, type, options), app)
+ }
+
+ override fun createContext(contextParams: ContextParams): Context {
+ return SandboxApplicationWrapper(super.createContext(contextParams), app)
+ }
+
+ override fun createAttributionContext(attributionTag: String?): Context {
+ return SandboxApplicationWrapper(super.createAttributionContext(attributionTag), app)
+ }
+
+ override fun createCredentialProtectedStorageContext(): Context {
+ return SandboxApplicationWrapper(super.createCredentialProtectedStorageContext(), app)
+ }
+
+ override fun createDeviceProtectedStorageContext(): Context {
+ return SandboxApplicationWrapper(super.createDeviceProtectedStorageContext(), app)
+ }
+
+ override fun createTokenContext(token: IBinder, display: Display): Context {
+ return SandboxApplicationWrapper(super.createTokenContext(token, display), app)
+ }
+}
diff --git a/tests/multivalentTests/src/com/android/launcher3/util/SandboxApplicationTest.kt b/tests/multivalentTests/src/com/android/launcher3/util/SandboxApplicationTest.kt
new file mode 100644
index 0000000..d87a406
--- /dev/null
+++ b/tests/multivalentTests/src/com/android/launcher3/util/SandboxApplicationTest.kt
@@ -0,0 +1,78 @@
+/*
+ * 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.util
+
+import android.content.Context
+import android.hardware.display.DisplayManager
+import android.view.Display
+import android.view.Display.DEFAULT_DISPLAY
+import android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
+import com.google.common.truth.Truth.assertThat
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(LauncherMultivalentJUnit::class)
+class SandboxApplicationTest {
+ @get:Rule val app = SandboxApplication()
+
+ private val display: Display
+ get() {
+ return checkNotNull(app.getSystemService(DisplayManager::class.java))
+ .getDisplay(DEFAULT_DISPLAY)
+ }
+
+ @Test
+ fun testCreateDisplayContext_isSandboxed() {
+ val displayContext = app.createDisplayContext(display)
+ assertThat(displayContext.applicationContext).isEqualTo(app)
+ }
+
+ @Test
+ fun testCreateWindowContext_fromSandboxedDisplayContext_isSandboxed() {
+ val displayContext = app.createDisplayContext(display)
+ val nestedContext = displayContext.createWindowContext(TYPE_APPLICATION_OVERLAY, null)
+ assertThat(nestedContext.applicationContext).isEqualTo(app)
+ }
+
+ @Test(expected = IllegalStateException::class)
+ fun testGetApplicationContext_beforeManualInit_throwsException() {
+ val manualApp = SandboxApplication()
+ assertThat(manualApp.applicationContext).isEqualTo(manualApp)
+ }
+
+ @Test
+ fun testGetApplicationContext_afterManualInit_isApplication() {
+ SandboxApplication().run {
+ init()
+ assertThat(applicationContext).isEqualTo(this)
+ onDestroy()
+ }
+ }
+
+ @Test
+ fun testGetObject_objectCreatesDisplayContext_isSandboxed() {
+ class TestSingleton(context: Context) : SafeCloseable {
+ override fun close() = Unit
+
+ val displayContext = context.createDisplayContext(display)
+ }
+
+ val displayContext = MainThreadInitializedObject { TestSingleton(it) }[app].displayContext
+ assertThat(displayContext.applicationContext).isEqualTo(app)
+ }
+}
diff --git a/tests/multivalentTests/src/com/android/launcher3/util/TestUtil.java b/tests/multivalentTests/src/com/android/launcher3/util/TestUtil.java
index 64035da..ce682f1 100644
--- a/tests/multivalentTests/src/com/android/launcher3/util/TestUtil.java
+++ b/tests/multivalentTests/src/com/android/launcher3/util/TestUtil.java
@@ -15,14 +15,12 @@
*/
package com.android.launcher3.util;
-import static android.util.Base64.NO_PADDING;
-import static android.util.Base64.NO_WRAP;
-
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
-import static com.android.launcher3.LauncherSettings.Settings.LAYOUT_DIGEST_KEY;
import static com.android.launcher3.LauncherSettings.Settings.LAYOUT_DIGEST_LABEL;
import static com.android.launcher3.LauncherSettings.Settings.LAYOUT_DIGEST_TAG;
+import static com.android.launcher3.LauncherSettings.Settings.LAYOUT_PROVIDER_KEY;
+import static com.android.launcher3.LauncherSettings.Settings.createBlobProviderKey;
import static org.junit.Assert.assertTrue;
@@ -42,7 +40,6 @@
import android.os.UserHandle;
import android.provider.Settings;
import android.system.OsConstants;
-import android.util.Base64;
import android.util.Log;
import androidx.test.uiautomator.UiDevice;
@@ -169,13 +166,12 @@
session.commit(AsyncTask.THREAD_POOL_EXECUTOR, i -> wait.countDown());
}
- String key = Base64.encodeToString(digest, NO_WRAP | NO_PADDING);
-
grantWriteSecurePermission();
- Settings.Secure.putString(context.getContentResolver(), LAYOUT_DIGEST_KEY, key);
+ Settings.Secure.putString(
+ context.getContentResolver(), LAYOUT_PROVIDER_KEY, createBlobProviderKey(digest));
wait.await();
return () ->
- Settings.Secure.putString(context.getContentResolver(), LAYOUT_DIGEST_KEY, null);
+ Settings.Secure.putString(context.getContentResolver(), LAYOUT_PROVIDER_KEY, null);
}
/**
diff --git a/tests/src/com/android/launcher3/model/PackageUpdatedTaskTest.kt b/tests/src/com/android/launcher3/model/PackageUpdatedTaskTest.kt
index 05f626d..d9af07a 100644
--- a/tests/src/com/android/launcher3/model/PackageUpdatedTaskTest.kt
+++ b/tests/src/com/android/launcher3/model/PackageUpdatedTaskTest.kt
@@ -58,8 +58,7 @@
@RunWith(AndroidJUnit4::class)
class PackageUpdatedTaskTest {
- @get:Rule(order = 0) val setFlagsRule = SetFlagsRule()
- @get:Rule(order = 1) val modelTestRule = ModelTestRule()
+ @get:Rule val setFlagsRule = SetFlagsRule()
private val mUser = UserHandle(0)
private val mDataModel: BgDataModel = BgDataModel()
diff --git a/tests/src/com/android/launcher3/util/RoboApiWrapper.kt b/tests/src/com/android/launcher3/util/RoboApiWrapper.kt
index 583652d..7f74e56 100644
--- a/tests/src/com/android/launcher3/util/RoboApiWrapper.kt
+++ b/tests/src/com/android/launcher3/util/RoboApiWrapper.kt
@@ -24,12 +24,10 @@
object RoboApiWrapper {
- fun initialize() {}
-
fun registerInputStream(
contentResolver: ContentResolver,
uri: Uri,
- inputStreamSupplier: Supplier<InputStream>
+ inputStreamSupplier: Supplier<InputStream>,
) {}
fun waitForLooperSync(looper: Looper) {}
diff --git a/tests/src_deviceless/com/android/launcher3/util/RoboApiWrapper.kt b/tests/src_deviceless/com/android/launcher3/util/RoboApiWrapper.kt
index 9232268..a2b8303 100644
--- a/tests/src_deviceless/com/android/launcher3/util/RoboApiWrapper.kt
+++ b/tests/src_deviceless/com/android/launcher3/util/RoboApiWrapper.kt
@@ -16,70 +16,19 @@
package com.android.launcher3.util
-import android.content.ComponentName
import android.content.ContentResolver
-import android.content.Intent
-import android.content.IntentFilter
-import android.content.pm.ApplicationInfo
-import android.content.pm.LauncherActivityInfo
-import android.content.pm.LauncherApps
import android.net.Uri
import android.os.Looper
-import android.os.Process
-import androidx.test.platform.app.InstrumentationRegistry
import java.io.InputStream
import java.util.function.Supplier
-import org.mockito.Mockito
-import org.mockito.kotlin.whenever
-import org.robolectric.RuntimeEnvironment
import org.robolectric.Shadows
object RoboApiWrapper {
- fun initialize() {
- Shadows.shadowOf(
- RuntimeEnvironment.getApplication().getSystemService(LauncherApps::class.java)
- )
- .addEnabledPackage(
- Process.myUserHandle(),
- InstrumentationRegistry.getInstrumentation().context.packageName
- )
- LauncherModelHelper.ACTIVITY_LIST.forEach {
- installApp(ComponentName(InstrumentationRegistry.getInstrumentation().context, it))
- }
- }
-
- private fun installApp(componentName: ComponentName) {
- val app = RuntimeEnvironment.getApplication()
- val user = Process.myUserHandle()
-
- val pm = Shadows.shadowOf(app.packageManager)
- val ai = pm.addActivityIfNotPresent(componentName)
- pm.addIntentFilterForActivity(
- componentName,
- IntentFilter(Intent.ACTION_MAIN).apply { addCategory(Intent.CATEGORY_LAUNCHER) }
- )
-
- val li = Mockito.mock(LauncherActivityInfo::class.java)
- val appInfo = ApplicationInfo().apply { flags = 0 }
- Mockito.doReturn(ai).whenever(li).activityInfo
- Mockito.doReturn(appInfo).whenever(li).applicationInfo
- Mockito.doReturn(user).whenever(li).user
- Mockito.doReturn(1f).whenever(li).loadingProgress
- Mockito.doReturn(componentName).whenever(li).componentName
-
- Shadows.shadowOf(app.getSystemService(LauncherApps::class.java)).apply {
- addActivity(user, li)
- addEnabledPackage(user, componentName.packageName)
- setActivityEnabled(user, componentName)
- addApplicationInfo(user, componentName.packageName, ai.applicationInfo)
- }
- }
-
fun registerInputStream(
contentResolver: ContentResolver,
uri: Uri,
- inputStreamSupplier: Supplier<InputStream>
+ inputStreamSupplier: Supplier<InputStream>,
) {
Shadows.shadowOf(contentResolver).registerInputStreamSupplier(uri, inputStreamSupplier)
}