Merge "Separate code paths to launch split from existing pair vs new pair" into udc-dev am: 258019fe8e am: 1f27273c06
Original change: https://googleplex-android-review.googlesource.com/c/platform/packages/apps/Launcher3/+/23320818
Change-Id: I11104b119109468f3b0984fb670b6196c418b97d
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/quickstep/res/drawable/bg_floating_desktop_select.xml b/quickstep/res/drawable/bg_floating_desktop_select.xml
new file mode 100644
index 0000000..d7df338
--- /dev/null
+++ b/quickstep/res/drawable/bg_floating_desktop_select.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ Copyright (C) 2023 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<shape
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+ android:shape="rectangle">
+
+ <corners android:radius="@dimen/rounded_button_radius" />
+ <solid android:color="?androidprv:attr/materialColorPrimaryContainer" />
+</shape>
\ No newline at end of file
diff --git a/quickstep/res/layout/floating_desktop_app_select.xml b/quickstep/res/layout/floating_desktop_app_select.xml
new file mode 100644
index 0000000..375fc44
--- /dev/null
+++ b/quickstep/res/layout/floating_desktop_app_select.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ Copyright (C) 2023 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<com.android.quickstep.views.DesktopAppSelectView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="@dimen/desktop_mode_floating_app_select_height"
+ android:layout_gravity="top|center_horizontal"
+ android:background="@drawable/bg_floating_desktop_select"
+ android:elevation="@dimen/desktop_mode_floating_app_select_elevation"
+ android:gravity="center_vertical"
+ android:orientation="horizontal">
+
+ <TextView
+ android:id="@+id/desktop_app_select_text"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginEnd="@dimen/desktop_mode_floating_app_select_text_margin"
+ android:layout_marginStart="@dimen/desktop_mode_floating_app_select_margin"
+ android:drawablePadding="@dimen/desktop_mode_floating_app_select_text_margin"
+ android:drawableStart="@drawable/ic_desktop"
+ android:drawableTint="?androidprv:attr/materialColorOnPrimaryContainer"
+ android:fontFamily="google-sans-medium"
+ android:gravity="center_vertical"
+ android:text="@string/desktop_select_app_toast"
+ android:textColor="?androidprv:attr/materialColorOnPrimaryContainer"
+ android:textSize="@dimen/desktop_mode_floating_app_select_text_size" />
+
+ <Button
+ android:id="@+id/close_button"
+ style="@android:style/Widget.DeviceDefault.Button.Borderless"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginEnd="@dimen/desktop_mode_floating_app_select_margin"
+ android:minWidth="0dp"
+ android:fontFamily="google-sans-medium"
+ android:text="@string/desktop_button_close_app_toast"
+ android:textAllCaps="false"
+ android:textColor="?androidprv:attr/materialColorPrimary"
+ android:textSize="@dimen/desktop_mode_floating_app_select_text_size" />
+
+</com.android.quickstep.views.DesktopAppSelectView>
diff --git a/quickstep/res/values-mk/strings.xml b/quickstep/res/values-mk/strings.xml
index 9d36c67..8e11a2d 100644
--- a/quickstep/res/values-mk/strings.xml
+++ b/quickstep/res/values-mk/strings.xml
@@ -98,7 +98,7 @@
<string name="accessibility_rotate_button" msgid="4771825231336502943">"Ротирајте го екранот"</string>
<string name="taskbar_edu_a11y_title" msgid="5417986057866415355">"Обука за лентата со задачи"</string>
<string name="taskbar_edu_splitscreen" msgid="5605512479258053350">"Повлечете апликација настрана за да користите 2 апликации"</string>
- <string name="taskbar_edu_stashing" msgid="5645461372669217294">"Полека повлечете нагоре за да се прикаже лентата со задачи"</string>
+ <string name="taskbar_edu_stashing" msgid="5645461372669217294">"Полека повлечете нагоре за да се прикаже „Лентата со задачи“"</string>
<string name="taskbar_edu_suggestions" msgid="8215044496435527982">"Добивајте предлози за апликации според вашата рутина"</string>
<string name="taskbar_edu_settings_persistent" msgid="1387372982791296151">"Вклучете навигација со движење во „Поставки“ за автоматско сокривање на „Лентата со задачи“"</string>
<string name="taskbar_edu_features" msgid="3320337287472848162">"Правете повеќе со една лента со задачи"</string>
diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml
index bb4f74d..c9f84c7 100644
--- a/quickstep/res/values/dimens.xml
+++ b/quickstep/res/values/dimens.xml
@@ -383,4 +383,12 @@
<dimen name="keyboard_quick_switch_task_view_radius">16dp</dimen>
<dimen name="keyboard_quick_switch_no_recent_items_icon_size">24dp</dimen>
<dimen name="keyboard_quick_switch_no_recent_items_icon_margin">8dp</dimen>
+
+ <!-- Desktop mode -->
+ <dimen name="desktop_mode_floating_app_select_height">56dp</dimen>
+ <dimen name="desktop_mode_floating_app_select_elevation">4dp</dimen>
+ <dimen name="desktop_mode_floating_app_select_margin">16dp</dimen>
+ <dimen name="desktop_mode_floating_app_select_text_size">14sp</dimen>
+ <dimen name="desktop_mode_floating_app_select_text_margin">8dp</dimen>
+
</resources>
diff --git a/quickstep/res/values/strings.xml b/quickstep/res/values/strings.xml
index 2d8c45a..ebcc817 100644
--- a/quickstep/res/values/strings.xml
+++ b/quickstep/res/values/strings.xml
@@ -298,4 +298,10 @@
<!-- Accessibility label for quick switch tiles showing split tasks [CHAR LIMIT=NONE] -->
<string name="quick_switch_split_task"><xliff:g id="app_name_1" example="Chrome">%1$s</xliff:g> and <xliff:g id="app_name_2" example="Gmail">%2$s</xliff:g></string>
+
+ <!-- ******* Desktop ******* -->
+ <!-- Text shown in popup to choose a desktop app. [CHAR LIMIT=60] -->
+ <string name="desktop_select_app_toast">Adding app to Desktop</string>
+ <!-- Text shown on a button that closes the popup for choosing a desktop app. [CHAR_LIMIT=40] -->
+ <string name="desktop_button_close_app_toast">Cancel</string>
</resources>
diff --git a/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.java b/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.java
index d087d39..b052deb 100644
--- a/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.java
+++ b/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.java
@@ -15,14 +15,21 @@
*/
package com.android.launcher3.statehandlers;
+import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+
import android.os.SystemProperties;
import android.util.Log;
import android.view.View;
+import androidx.annotation.Nullable;
+
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
import com.android.launcher3.statemanager.StatefulActivity;
import com.android.launcher3.uioverrides.QuickstepLauncher;
+import com.android.quickstep.SystemUiProxy;
+import com.android.quickstep.views.DesktopAppSelectView;
+import com.android.wm.shell.desktopmode.IDesktopTaskListener;
/**
* Controls the visibility of the workspace and the resumed / paused state when desktop mode
@@ -39,11 +46,51 @@
private boolean mInOverviewState;
private boolean mGestureInProgress;
+ @Nullable
+ private IDesktopTaskListener mDesktopTaskListener;
+ private DesktopAppSelectView mSelectAppToast;
+
public DesktopVisibilityController(Launcher launcher) {
mLauncher = launcher;
}
/**
+ * Register a listener with System UI to receive updates about desktop tasks state
+ */
+ public void registerSystemUiListener() {
+ mDesktopTaskListener = new IDesktopTaskListener.Stub() {
+ @Override
+ public void onVisibilityChanged(int displayId, boolean visible) {
+ // TODO(b/261234402): move visibility from sysui state to listener
+ }
+
+ @Override
+ public void onStashedChanged(int displayId, boolean stashed) {
+ MAIN_EXECUTOR.execute(() -> {
+ if (displayId == mLauncher.getDisplayId()) {
+ if (DEBUG) {
+ Log.d(TAG, "desktop stashed changed value=" + stashed);
+ }
+ if (stashed) {
+ showSelectAppToast();
+ } else {
+ hideSelectAppToast();
+ }
+ }
+ });
+ }
+ };
+ SystemUiProxy.INSTANCE.get(mLauncher).setDesktopTaskListener(mDesktopTaskListener);
+ }
+
+ /**
+ * Clear listener from System UI that was set with {@link #registerSystemUiListener()}
+ */
+ public void unregisterSystemUiListener() {
+ SystemUiProxy.INSTANCE.get(mLauncher).setDesktopTaskListener(null);
+ }
+
+ /**
* Whether desktop mode is supported.
*/
private boolean isDesktopModeSupported() {
@@ -68,6 +115,7 @@
if (!isDesktopModeSupported()) {
return;
}
+
if (freeformTasksVisible != mFreeformTasksVisible) {
mFreeformTasksVisible = freeformTasksVisible;
if (mFreeformTasksVisible) {
@@ -130,6 +178,15 @@
}
}
+ /**
+ * Handle launcher moving to home due to home gesture or home button press.
+ */
+ public void onHomeActionTriggered() {
+ if (areFreeformTasksVisible()) {
+ SystemUiProxy.INSTANCE.get(mLauncher).stashDesktopApps(mLauncher.getDisplayId());
+ }
+ }
+
private void setLauncherViewsVisibility(int visibility) {
if (DEBUG) {
Log.d(TAG, "setLauncherViewsVisibility: visibility=" + visibility);
@@ -168,4 +225,28 @@
activity.setResumed();
}
}
+
+ private void showSelectAppToast() {
+ if (mSelectAppToast != null) {
+ return;
+ }
+ if (DEBUG) {
+ Log.d(TAG, "show toast to select desktop apps");
+ }
+ Runnable onCloseCallback = () -> {
+ SystemUiProxy.INSTANCE.get(mLauncher).hideStashedDesktopApps(mLauncher.getDisplayId());
+ };
+ mSelectAppToast = DesktopAppSelectView.show(mLauncher, onCloseCallback);
+ }
+
+ private void hideSelectAppToast() {
+ if (mSelectAppToast == null) {
+ return;
+ }
+ if (DEBUG) {
+ Log.d(TAG, "hide toast to select desktop apps");
+ }
+ mSelectAppToast.hide();
+ mSelectAppToast = null;
+ }
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java
index 610efeb..0f8de34 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java
@@ -43,12 +43,15 @@
import com.android.launcher3.R;
import com.android.launcher3.logging.StatsLogManager;
+import com.android.launcher3.statehandlers.DesktopVisibilityController;
import com.android.launcher3.testing.TestLogging;
import com.android.launcher3.testing.shared.TestProtocol;
+import com.android.quickstep.LauncherActivityInterface;
import com.android.quickstep.OverviewCommandHelper;
import com.android.quickstep.SystemUiProxy;
import com.android.quickstep.TaskUtils;
import com.android.quickstep.TouchInteractionService;
+import com.android.quickstep.views.DesktopTaskView;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
@@ -267,6 +270,15 @@
private void navigateHome() {
TaskUtils.closeSystemWindowsAsync(CLOSE_SYSTEM_WINDOWS_REASON_HOME_KEY);
+
+ if (DesktopTaskView.DESKTOP_IS_PROTO2_ENABLED) {
+ DesktopVisibilityController desktopVisibilityController =
+ LauncherActivityInterface.INSTANCE.getDesktopVisibilityController();
+ if (desktopVisibilityController != null) {
+ desktopVisibilityController.onHomeActionTriggered();
+ }
+ }
+
mService.getOverviewCommandHelper().addCommand(OverviewCommandHelper.TYPE_HOME);
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java b/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java
index a8b7698..b059cbd 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java
@@ -337,7 +337,6 @@
if (getTag() instanceof WorkspaceItemInfo) {
WorkspaceItemInfo info = (WorkspaceItemInfo) getTag();
isBadged = !Process.myUserHandle().equals(info.user)
- || info.itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT
|| info.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT;
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
index 95c2326..e62a778 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
@@ -25,7 +25,6 @@
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT;
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT;
-import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
import static com.android.launcher3.LauncherState.ALL_APPS;
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.LauncherState.NO_OFFSET;
@@ -259,6 +258,9 @@
mTISBindHelper = new TISBindHelper(this, this::onTISConnected);
mDepthController = new DepthController(this);
mDesktopVisibilityController = new DesktopVisibilityController(this);
+ if (DesktopTaskView.DESKTOP_MODE_SUPPORTED) {
+ mDesktopVisibilityController.registerSystemUiListener();
+ }
mHotseatPredictionController = new HotseatPredictionController(this);
mEnableWidgetDepth = SystemProperties.getBoolean("ro.launcher.depth.widget", true);
@@ -280,7 +282,6 @@
if (mAllAppsPredictions != null
&& (info.itemType == ITEM_TYPE_APPLICATION
- || info.itemType == ITEM_TYPE_SHORTCUT
|| info.itemType == ITEM_TYPE_DEEP_SHORTCUT)) {
int count = mAllAppsPredictions.items.size();
for (int i = 0; i < count; i++) {
@@ -483,6 +484,10 @@
mLauncherUnfoldAnimationController.onDestroy();
}
+ if (mDesktopVisibilityController != null) {
+ mDesktopVisibilityController.unregisterSystemUiListener();
+ }
+
super.onDestroy();
mHotseatPredictionController.destroy();
mSplitWithKeyboardShortcutController.onDestroy();
@@ -1155,7 +1160,6 @@
}
switch (info.itemType) {
case Favorites.ITEM_TYPE_APPLICATION:
- case Favorites.ITEM_TYPE_SHORTCUT:
case Favorites.ITEM_TYPE_DEEP_SHORTCUT:
case Favorites.ITEM_TYPE_APPWIDGET:
// Fall through and continue if it's an app, shortcut, or widget
diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
index 928910d..f3689a9 100644
--- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
+++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
@@ -101,6 +101,7 @@
import com.android.launcher3.dragndrop.DragView;
import com.android.launcher3.logging.StatsLogManager;
import com.android.launcher3.logging.StatsLogManager.StatsLogger;
+import com.android.launcher3.statehandlers.DesktopVisibilityController;
import com.android.launcher3.statemanager.BaseState;
import com.android.launcher3.statemanager.StatefulActivity;
import com.android.launcher3.taskbar.TaskbarUIController;
@@ -1133,6 +1134,14 @@
mStateCallback.setState(STATE_SCALED_CONTROLLER_HOME | STATE_CAPTURE_SCREENSHOT);
// Notify the SysUI to use fade-in animation when entering PiP
SystemUiProxy.INSTANCE.get(mContext).setPipAnimationTypeToAlpha();
+ if (DesktopTaskView.DESKTOP_IS_PROTO2_ENABLED) {
+ // Notify the SysUI to stash desktop apps if they are visible
+ DesktopVisibilityController desktopVisibilityController =
+ mActivityInterface.getDesktopVisibilityController();
+ if (desktopVisibilityController != null) {
+ desktopVisibilityController.onHomeActionTriggered();
+ }
+ }
break;
case RECENTS:
mStateCallback.setState(STATE_SCALED_CONTROLLER_RECENTS | STATE_CAPTURE_SCREENSHOT
diff --git a/quickstep/src/com/android/quickstep/RotationTouchHelper.java b/quickstep/src/com/android/quickstep/RotationTouchHelper.java
index 8626c40..2d47097 100644
--- a/quickstep/src/com/android/quickstep/RotationTouchHelper.java
+++ b/quickstep/src/com/android/quickstep/RotationTouchHelper.java
@@ -19,6 +19,7 @@
import static android.view.Surface.ROTATION_0;
import static com.android.launcher3.MotionEventsUtils.isTrackpadMultiFingerSwipe;
+import static com.android.launcher3.MotionEventsUtils.isTrackpadScroll;
import static com.android.launcher3.util.DisplayController.CHANGE_ACTIVE_SCREEN;
import static com.android.launcher3.util.DisplayController.CHANGE_ALL;
import static com.android.launcher3.util.DisplayController.CHANGE_NAVIGATION_MODE;
@@ -232,16 +233,18 @@
/**
* @return whether the coordinates of the {@param event} is in the swipe up gesture region.
*/
- public boolean isInSwipeUpTouchRegion(MotionEvent event, BaseActivityInterface activity) {
- return isInSwipeUpTouchRegion(event, 0, activity);
+ public boolean isInSwipeUpTouchRegion(MotionEvent event) {
+ return isInSwipeUpTouchRegion(event, 0);
}
/**
* @return whether the coordinates of the {@param event} with the given {@param pointerIndex}
* is in the swipe up gesture region.
*/
- public boolean isInSwipeUpTouchRegion(MotionEvent event, int pointerIndex,
- BaseActivityInterface activity) {
+ public boolean isInSwipeUpTouchRegion(MotionEvent event, int pointerIndex) {
+ if (isTrackpadScroll(event)) {
+ return false;
+ }
if (isTrackpadMultiFingerSwipe(event)) {
return true;
}
diff --git a/quickstep/src/com/android/quickstep/SystemUiProxy.java b/quickstep/src/com/android/quickstep/SystemUiProxy.java
index ba80951..380aa69 100644
--- a/quickstep/src/com/android/quickstep/SystemUiProxy.java
+++ b/quickstep/src/com/android/quickstep/SystemUiProxy.java
@@ -74,6 +74,7 @@
import com.android.wm.shell.bubbles.IBubbles;
import com.android.wm.shell.bubbles.IBubblesListener;
import com.android.wm.shell.desktopmode.IDesktopMode;
+import com.android.wm.shell.desktopmode.IDesktopTaskListener;
import com.android.wm.shell.draganddrop.IDragAndDrop;
import com.android.wm.shell.onehanded.IOneHanded;
import com.android.wm.shell.pip.IPip;
@@ -130,6 +131,7 @@
private ILauncherUnlockAnimationController mLauncherUnlockAnimationController;
private IRecentTasksListener mRecentTasksListener;
private IUnfoldTransitionListener mUnfoldAnimationListener;
+ private IDesktopTaskListener mDesktopTaskListener;
private final LinkedHashMap<RemoteTransition, TransitionFilter> mRemoteTransitions =
new LinkedHashMap<>();
private IBinder mOriginalTransactionToken = null;
@@ -243,6 +245,7 @@
registerRecentTasksListener(mRecentTasksListener);
setBackToLauncherCallback(mBackToLauncherCallback, mBackToLauncherRunner);
setUnfoldAnimationListener(mUnfoldAnimationListener);
+ setDesktopTaskListener(mDesktopTaskListener);
}
/**
@@ -1161,6 +1164,28 @@
}
}
+ /** Call shell to stash desktop apps */
+ public void stashDesktopApps(int displayId) {
+ if (mDesktopMode != null) {
+ try {
+ mDesktopMode.stashDesktopApps(displayId);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call stashDesktopApps", e);
+ }
+ }
+ }
+
+ /** Call shell to hide desktop apps that may be stashed */
+ public void hideStashedDesktopApps(int displayId) {
+ if (mDesktopMode != null) {
+ try {
+ mDesktopMode.hideStashedDesktopApps(displayId);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call hideStashedDesktopApps", e);
+ }
+ }
+ }
+
/** Call shell to get number of visible freeform tasks */
public int getVisibleDesktopTaskCount(int displayId) {
if (mDesktopMode != null) {
@@ -1173,6 +1198,18 @@
return 0;
}
+ /** Set a listener on shell to get updates about desktop task state */
+ public void setDesktopTaskListener(@Nullable IDesktopTaskListener listener) {
+ mDesktopTaskListener = listener;
+ if (mDesktopMode != null) {
+ try {
+ mDesktopMode.setTaskListener(listener);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call setDesktopTaskListener", e);
+ }
+ }
+ }
+
//
// Unfold transition
//
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index 1fbfbe6..edaee8c 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -661,8 +661,7 @@
mRotationTouchHelper.setOrientationTransformIfNeeded(event);
if ((!mDeviceState.isOneHandedModeActive()
- && mRotationTouchHelper.isInSwipeUpTouchRegion(event,
- mOverviewComponentObserver.getActivityInterface()))
+ && mRotationTouchHelper.isInSwipeUpTouchRegion(event))
|| isHoverActionWithoutConsumer) {
// Clone the previous gesture state since onConsumerAboutToBeSwitched might trigger
// onConsumerInactive and wipe the previous gesture state
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/AccessibilityInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/AccessibilityInputConsumer.java
index 6a36d9f..2abc7ba 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/AccessibilityInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/AccessibilityInputConsumer.java
@@ -103,8 +103,7 @@
if (mState == STATE_INACTIVE) {
int pointerIndex = ev.getActionIndex();
if (mDeviceState.getRotationTouchHelper().isInSwipeUpTouchRegion(ev,
- pointerIndex, mGestureState.getActivityInterface())
- && mDelegate.allowInterceptByParent()) {
+ pointerIndex) && mDelegate.allowInterceptByParent()) {
setActive(ev);
mActivePointerId = ev.getPointerId(pointerIndex);
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java
index 59a9582..a553648 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java
@@ -153,8 +153,7 @@
if (!mThresholdCrossed) {
// Cancel interaction in case of multi-touch interaction
int ptrIdx = ev.getActionIndex();
- if (!mDeviceState.getRotationTouchHelper().isInSwipeUpTouchRegion(ev, ptrIdx,
- mGestureState.getActivityInterface())) {
+ if (!mDeviceState.getRotationTouchHelper().isInSwipeUpTouchRegion(ev, ptrIdx)) {
int action = ev.getAction();
ev.setAction(ACTION_CANCEL);
finishTouchTracking(ev);
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
index f9cd4ee..7d0937f 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
@@ -248,8 +248,7 @@
if (!mPassedPilferInputSlop) {
// Cancel interaction in case of multi-touch interaction
int ptrIdx = ev.getActionIndex();
- if (!mRotationTouchHelper.isInSwipeUpTouchRegion(ev, ptrIdx,
- mActivityInterface)) {
+ if (!mRotationTouchHelper.isInSwipeUpTouchRegion(ev, ptrIdx)) {
forceCancelGesture(ev);
}
}
diff --git a/quickstep/src/com/android/quickstep/util/SystemWindowManagerProxy.java b/quickstep/src/com/android/quickstep/util/SystemWindowManagerProxy.java
index a34888f..c82cdb7 100644
--- a/quickstep/src/com/android/quickstep/util/SystemWindowManagerProxy.java
+++ b/quickstep/src/com/android/quickstep/util/SystemWindowManagerProxy.java
@@ -28,6 +28,7 @@
import com.android.launcher3.util.window.CachedDisplayInfo;
import com.android.launcher3.util.window.WindowManagerProxy;
+import java.util.List;
import java.util.Set;
/**
@@ -53,15 +54,15 @@
}
@Override
- public ArrayMap<CachedDisplayInfo, WindowBounds[]> estimateInternalDisplayBounds(
+ public ArrayMap<CachedDisplayInfo, List<WindowBounds>> estimateInternalDisplayBounds(
Context displayInfoContext) {
- ArrayMap<CachedDisplayInfo, WindowBounds[]> result = new ArrayMap<>();
+ ArrayMap<CachedDisplayInfo, List<WindowBounds>> result = new ArrayMap<>();
WindowManager windowManager = displayInfoContext.getSystemService(WindowManager.class);
Set<WindowMetrics> possibleMaximumWindowMetrics =
windowManager.getPossibleMaximumWindowMetrics(DEFAULT_DISPLAY);
for (WindowMetrics windowMetrics : possibleMaximumWindowMetrics) {
CachedDisplayInfo info = getDisplayInfo(windowMetrics, Surface.ROTATION_0);
- WindowBounds[] bounds = estimateWindowBounds(displayInfoContext, info);
+ List<WindowBounds> bounds = estimateWindowBounds(displayInfoContext, info);
result.put(info, bounds);
}
return result;
diff --git a/quickstep/src/com/android/quickstep/views/DesktopAppSelectView.java b/quickstep/src/com/android/quickstep/views/DesktopAppSelectView.java
new file mode 100644
index 0000000..53101fb
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/views/DesktopAppSelectView.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.quickstep.views;
+
+import static com.android.launcher3.anim.Interpolators.LINEAR;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.widget.LinearLayout;
+
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.Launcher;
+import com.android.launcher3.R;
+
+/**
+ * Floating view show on launcher home screen that notifies the user that an app will be launched to
+ * the desktop.
+ */
+public class DesktopAppSelectView extends LinearLayout {
+
+ private static final int HIDE_DURATION = 83;
+
+ private final Launcher mLauncher;
+
+ @Nullable
+ private Runnable mOnCloseCallback = null;
+ private boolean mIsHideAnimationRunning;
+
+ public DesktopAppSelectView(Context context) {
+ this(context, null);
+ }
+
+ public DesktopAppSelectView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public DesktopAppSelectView(Context context, AttributeSet attrs, int defStyleAttr) {
+ this(context, attrs, defStyleAttr, 0);
+ }
+
+ public DesktopAppSelectView(Context context, AttributeSet attrs, int defStyleAttr,
+ int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ mLauncher = Launcher.getLauncher(context);
+ }
+
+ /**
+ * Show the popup on launcher home screen
+ *
+ * @param onCloseCallback optional callback that is called when user clicks the close button
+ * @return the created view
+ */
+ public static DesktopAppSelectView show(Launcher launcher, @Nullable Runnable onCloseCallback) {
+ DesktopAppSelectView view = (DesktopAppSelectView) launcher.getLayoutInflater().inflate(
+ R.layout.floating_desktop_app_select, launcher.getDragLayer(), false);
+ view.setOnCloseClickCallback(onCloseCallback);
+ launcher.getDragLayer().addView(view);
+ return view;
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ findViewById(R.id.close_button).setOnClickListener(v -> {
+ if (!mIsHideAnimationRunning) {
+ hide();
+ if (mOnCloseCallback != null) {
+ mOnCloseCallback.run();
+ }
+ }
+ });
+ }
+
+ /**
+ * Hide the floating view
+ */
+ public void hide() {
+ if (!mIsHideAnimationRunning) {
+ mIsHideAnimationRunning = true;
+ animate().alpha(0).setDuration(HIDE_DURATION).setInterpolator(LINEAR).withEndAction(
+ () -> {
+ mLauncher.getDragLayer().removeView(this);
+ mIsHideAnimationRunning = false;
+ });
+ }
+ }
+
+ /**
+ * Add a callback that is called when close button is clicked
+ */
+ public void setOnCloseClickCallback(@Nullable Runnable callback) {
+ mOnCloseCallback = callback;
+ }
+}
diff --git a/quickstep/tests/src/com/android/launcher3/model/WidgetsPredicationUpdateTaskTest.java b/quickstep/tests/src/com/android/launcher3/model/WidgetsPredicationUpdateTaskTest.java
index 83341cb..b12d98b 100644
--- a/quickstep/tests/src/com/android/launcher3/model/WidgetsPredicationUpdateTaskTest.java
+++ b/quickstep/tests/src/com/android/launcher3/model/WidgetsPredicationUpdateTaskTest.java
@@ -20,6 +20,7 @@
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_WIDGETS_PREDICTION;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
+import static com.android.launcher3.util.TestUtil.runOnExecutorSync;
import static com.android.launcher3.util.WidgetUtils.createAppWidgetProviderInfo;
import static com.google.common.truth.Truth.assertThat;
@@ -40,11 +41,9 @@
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
-import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.icons.ComponentWithLabel;
-import com.android.launcher3.icons.IconCache;
import com.android.launcher3.model.BgDataModel.FixedContainerItems;
import com.android.launcher3.model.QuickstepModelDelegate.PredictorState;
+import com.android.launcher3.util.LauncherLayoutBuilder;
import com.android.launcher3.util.LauncherModelHelper;
import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
import com.android.launcher3.widget.PendingAddWidgetInfo;
@@ -53,8 +52,6 @@
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
import java.util.Arrays;
import java.util.List;
@@ -76,17 +73,9 @@
private LauncherModelHelper mModelHelper;
private UserHandle mUserHandle;
- @Mock
- private IconCache mIconCache;
-
@Before
public void setup() throws Exception {
mModelHelper = new LauncherModelHelper();
- MockitoAnnotations.initMocks(this);
- doAnswer(invocation -> {
- ComponentWithLabel componentWithLabel = invocation.getArgument(0);
- return componentWithLabel.getComponent().getShortClassName();
- }).when(mIconCache).getTitleNoCache(any());
mUserHandle = myUserHandle();
mApp1Provider1 = createAppWidgetProviderInfo(
@@ -114,16 +103,12 @@
.collect(Collectors.toList());
}).when(manager).getInstalledProvidersForPackage(any(), eq(myUserHandle()));
- // 2 widgets, app4/provider1 & app5/provider1, have already been added to the workspace.
- mModelHelper.initializeData("widgets_predication_update_task_data");
-
+ LauncherLayoutBuilder builder = new LauncherLayoutBuilder()
+ .atWorkspace(0, 1, 2).putWidget("app4", "provider1", 1, 1)
+ .atWorkspace(0, 1, 3).putWidget("app5", "provider1", 1, 1);
+ mModelHelper.setupDefaultLayoutProvider(builder);
MAIN_EXECUTOR.submit(() -> mModelHelper.getModel().addCallbacks(mCallback)).get();
- MODEL_EXECUTOR.post(() -> mModelHelper.getBgDataModel().widgetsModel.update(
- LauncherAppState.getInstance(mModelHelper.sandboxContext),
- /* packageUser= */ null));
-
- MODEL_EXECUTOR.submit(() -> { }).get();
- MAIN_EXECUTOR.submit(() -> { }).get();
+ mModelHelper.loadModelSync();
}
@After
@@ -132,65 +117,72 @@
}
@Test
- public void widgetsRecommendationRan_shouldOnlyReturnNotAddedWidgetsInAppPredictionOrder()
- throws Exception {
- // WHEN newPredicationTask is executed with app predication of 5 apps.
- AppTarget app1 = new AppTarget(new AppTargetId("app1"), "app1", "provider1",
- mUserHandle);
- AppTarget app2 = new AppTarget(new AppTargetId("app2"), "app2", "provider1",
- mUserHandle);
- AppTarget app3 = new AppTarget(new AppTargetId("app3"), "app3", "className",
- mUserHandle);
- AppTarget app4 = new AppTarget(new AppTargetId("app4"), "app4", "provider1",
- mUserHandle);
- AppTarget app5 = new AppTarget(new AppTargetId("app5"), "app5", "provider1",
- mUserHandle);
- mModelHelper.executeTaskForTest(
- newWidgetsPredicationTask(List.of(app5, app3, app2, app4, app1)))
- .forEach(Runnable::run);
+ public void widgetsRecommendationRan_shouldOnlyReturnNotAddedWidgetsInAppPredictionOrder() {
+ // Run on model executor so that no other task runs in the middle.
+ runOnExecutorSync(MODEL_EXECUTOR, () -> {
+ // WHEN newPredicationTask is executed with app predication of 5 apps.
+ AppTarget app1 = new AppTarget(new AppTargetId("app1"), "app1", "provider1",
+ mUserHandle);
+ AppTarget app2 = new AppTarget(new AppTargetId("app2"), "app2", "provider1",
+ mUserHandle);
+ AppTarget app3 = new AppTarget(new AppTargetId("app3"), "app3", "className",
+ mUserHandle);
+ AppTarget app4 = new AppTarget(new AppTargetId("app4"), "app4", "provider1",
+ mUserHandle);
+ AppTarget app5 = new AppTarget(new AppTargetId("app5"), "app5", "provider1",
+ mUserHandle);
+ mCallback.mRecommendedWidgets = null;
+ mModelHelper.getModel().enqueueModelUpdateTask(
+ newWidgetsPredicationTask(List.of(app5, app3, app2, app4, app1)));
+ runOnExecutorSync(MAIN_EXECUTOR, () -> { });
- // THEN only 2 widgets are returned because
- // 1. app5/provider1 & app4/provider1 have already been added to workspace. They are
- // excluded from the result.
- // 2. app3 doesn't have a widget.
- // 3. only 1 widget is picked from app1 because we only want to promote one widget per app.
- List<PendingAddWidgetInfo> recommendedWidgets = mCallback.mRecommendedWidgets.items
- .stream()
- .map(itemInfo -> (PendingAddWidgetInfo) itemInfo)
- .collect(Collectors.toList());
- assertThat(recommendedWidgets).hasSize(2);
- assertWidgetInfo(recommendedWidgets.get(0).info, mApp2Provider1);
- assertWidgetInfo(recommendedWidgets.get(1).info, mApp1Provider1);
+ // THEN only 2 widgets are returned because
+ // 1. app5/provider1 & app4/provider1 have already been added to workspace. They are
+ // excluded from the result.
+ // 2. app3 doesn't have a widget.
+ // 3. only 1 widget is picked from app1 because we only want to promote one widget per app.
+ List<PendingAddWidgetInfo> recommendedWidgets = mCallback.mRecommendedWidgets.items
+ .stream()
+ .map(itemInfo -> (PendingAddWidgetInfo) itemInfo)
+ .collect(Collectors.toList());
+ assertThat(recommendedWidgets).hasSize(2);
+ assertWidgetInfo(recommendedWidgets.get(0).info, mApp2Provider1);
+ assertWidgetInfo(recommendedWidgets.get(1).info, mApp1Provider1);
+ });
}
@Test
- public void widgetsRecommendationRan_shouldReturnPackageWidgetsWhenEmpty()
- throws Exception {
+ public void widgetsRecommendationRan_shouldReturnPackageWidgetsWhenEmpty() {
+ runOnExecutorSync(MODEL_EXECUTOR, () -> {
- // Not installed widget
- AppTarget widget1 = new AppTarget(new AppTargetId("app1"), "app1", "provider3",
- mUserHandle);
- // Not installed app
- AppTarget widget3 = new AppTarget(new AppTargetId("app2"), "app3", "provider1",
- mUserHandle);
- // Workspace added widgets
- AppTarget widget4 = new AppTarget(new AppTargetId("app4"), "app4", "provider1",
- mUserHandle);
- AppTarget widget5 = new AppTarget(new AppTargetId("app5"), "app5", "provider1",
- mUserHandle);
- mModelHelper.executeTaskForTest(
- newWidgetsPredicationTask(List.of(widget5, widget3, widget4, widget1)))
- .forEach(Runnable::run);
+ // Not installed widget
+ AppTarget widget1 = new AppTarget(new AppTargetId("app1"), "app1", "provider3",
+ mUserHandle);
+ // Not installed app
+ AppTarget widget3 = new AppTarget(new AppTargetId("app2"), "app3", "provider1",
+ mUserHandle);
+ // Workspace added widgets
+ AppTarget widget4 = new AppTarget(new AppTargetId("app4"), "app4", "provider1",
+ mUserHandle);
+ AppTarget widget5 = new AppTarget(new AppTargetId("app5"), "app5", "provider1",
+ mUserHandle);
- // THEN only 2 widgets are returned because the launcher only filters out non-exist widgets.
- List<PendingAddWidgetInfo> recommendedWidgets = mCallback.mRecommendedWidgets.items
- .stream()
- .map(itemInfo -> (PendingAddWidgetInfo) itemInfo)
- .collect(Collectors.toList());
- assertThat(recommendedWidgets).hasSize(2);
- // Another widget from the same package
- assertWidgetInfo(recommendedWidgets.get(0).info, mApp4Provider2);
- assertWidgetInfo(recommendedWidgets.get(1).info, mApp1Provider1);
+ mCallback.mRecommendedWidgets = null;
+ mModelHelper.getModel().enqueueModelUpdateTask(
+ newWidgetsPredicationTask(List.of(widget5, widget3, widget4, widget1)));
+ runOnExecutorSync(MAIN_EXECUTOR, () -> { });
+
+ // THEN only 2 widgets are returned because the launcher only filters out
+ // non-exist widgets.
+ List<PendingAddWidgetInfo> recommendedWidgets = mCallback.mRecommendedWidgets.items
+ .stream()
+ .map(itemInfo -> (PendingAddWidgetInfo) itemInfo)
+ .collect(Collectors.toList());
+ assertThat(recommendedWidgets).hasSize(2);
+ // Another widget from the same package
+ assertWidgetInfo(recommendedWidgets.get(0).info, mApp4Provider2);
+ assertWidgetInfo(recommendedWidgets.get(1).info, mApp1Provider1);
+ });
}
private void assertWidgetInfo(
diff --git a/quickstep/tests/src/com/android/quickstep/OrientationTouchTransformerTest.java b/quickstep/tests/src/com/android/quickstep/OrientationTouchTransformerTest.java
index 9c240f0..298dd6c 100644
--- a/quickstep/tests/src/com/android/quickstep/OrientationTouchTransformerTest.java
+++ b/quickstep/tests/src/com/android/quickstep/OrientationTouchTransformerTest.java
@@ -53,6 +53,8 @@
import org.junit.runner.RunWith;
import org.mockito.MockitoAnnotations;
+import java.util.List;
+
@SmallTest
@RunWith(AndroidJUnit4.class)
public class OrientationTouchTransformerTest {
@@ -296,7 +298,7 @@
WindowManagerProxy wmProxy = mock(WindowManagerProxy.class);
doReturn(cachedDisplayInfo).when(wmProxy).getDisplayInfo(any());
doReturn(windowBounds).when(wmProxy).getRealBounds(any(), any());
- ArrayMap<CachedDisplayInfo, WindowBounds[]> internalDisplayBounds = new ArrayMap<>();
+ ArrayMap<CachedDisplayInfo, List<WindowBounds>> internalDisplayBounds = new ArrayMap<>();
doReturn(internalDisplayBounds).when(wmProxy).estimateInternalDisplayBounds(any());
return new DisplayController.Info(
getApplicationContext(), wmProxy, new ArrayMap<>());
diff --git a/quickstep/tests/src/com/android/quickstep/util/TaskViewSimulatorTest.java b/quickstep/tests/src/com/android/quickstep/util/TaskViewSimulatorTest.java
index 83602be..a54dc2d 100644
--- a/quickstep/tests/src/com/android/quickstep/util/TaskViewSimulatorTest.java
+++ b/quickstep/tests/src/com/android/quickstep/util/TaskViewSimulatorTest.java
@@ -50,6 +50,9 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.ArrayList;
+import java.util.List;
+
@SmallTest
@RunWith(AndroidJUnit4.class)
public class TaskViewSimulatorTest {
@@ -150,7 +153,7 @@
WindowBounds wm = new WindowBounds(
new Rect(0, 0, mDisplaySize.x, mDisplaySize.y),
mDisplayInsets);
- WindowBounds[] allBounds = new WindowBounds[4];
+ List<WindowBounds> allBounds = new ArrayList<>(4);
for (int i = 0; i < 4; i++) {
Rect boundsR = new Rect(wm.bounds);
Rect insetsR = new Rect(wm.insets);
@@ -158,7 +161,7 @@
RotationUtils.rotateRect(insetsR, RotationUtils.deltaRotation(rotation, i));
RotationUtils.rotateRect(boundsR, RotationUtils.deltaRotation(rotation, i));
boundsR.set(0, 0, Math.abs(boundsR.width()), Math.abs(boundsR.height()));
- allBounds[i] = new WindowBounds(boundsR, insetsR);
+ allBounds.add(new WindowBounds(boundsR, insetsR));
}
WindowManagerProxy wmProxy = mock(WindowManagerProxy.class);
@@ -166,7 +169,7 @@
doReturn(wm).when(wmProxy).getRealBounds(any(), any());
doReturn(NavigationMode.NO_BUTTON).when(wmProxy).getNavigationMode(any());
- ArrayMap<CachedDisplayInfo, WindowBounds[]> perDisplayBoundsCache =
+ ArrayMap<CachedDisplayInfo, List<WindowBounds>> perDisplayBoundsCache =
new ArrayMap<>();
perDisplayBoundsCache.put(cdi.normalize(), allBounds);
diff --git a/res/values-mk/strings.xml b/res/values-mk/strings.xml
index c2ee822..de60f3a 100644
--- a/res/values-mk/strings.xml
+++ b/res/values-mk/strings.xml
@@ -83,7 +83,7 @@
<string name="permlab_write_settings" msgid="4820028712156303762">"да пишува поставки и кратенки на почетна страница"</string>
<string name="permdesc_write_settings" msgid="726859348127868466">"Овозможува апликацијата да ги менува поставките и кратенките на почетната страница."</string>
<string name="gadget_error_text" msgid="740356548025791839">"Не може да се вчита виџетот"</string>
- <string name="gadget_setup_text" msgid="8348374825537681407">"Поставки за виџетот"</string>
+ <string name="gadget_setup_text" msgid="8348374825537681407">"Поставки за виџет"</string>
<string name="gadget_complete_setup_text" msgid="309040266978007925">"Допрете за да го завршите поставувањето"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"Ова е системска апликација и не може да се деинсталира."</string>
<string name="folder_hint_text" msgid="5174843001373488816">"Изменете го името"</string>
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 0b75c45..5af8e1e 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -1928,7 +1928,7 @@
case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
addAppWidgetFromDrop((PendingAddWidgetInfo) info);
break;
- case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
+ case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT:
processShortcutFromDrop((PendingAddShortcutInfo) info);
break;
default:
@@ -2435,7 +2435,6 @@
final View view;
switch (item.itemType) {
case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
- case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT: {
WorkspaceItemInfo info = (WorkspaceItemInfo) item;
view = createShortcut(info);
diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java
index 4d15ac7..cc1c0f5 100644
--- a/src/com/android/launcher3/LauncherAppState.java
+++ b/src/com/android/launcher3/LauncherAppState.java
@@ -104,6 +104,8 @@
});
mContext.getSystemService(LauncherApps.class).registerCallback(mModel);
+ mOnTerminateCallback.add(() ->
+ mContext.getSystemService(LauncherApps.class).unregisterCallback(mModel));
SimpleBroadcastReceiver modelChangeReceiver =
new SimpleBroadcastReceiver(mModel::onBroadcastIntent);
@@ -123,8 +125,9 @@
mOnTerminateCallback.add(userChangeListener::close);
LockedUserState.get(context).runOnUserUnlocked(() -> {
- CustomWidgetManager.INSTANCE.get(mContext)
- .setWidgetRefreshCallback(mModel::refreshAndBindWidgetsAndShortcuts);
+ CustomWidgetManager cwm = CustomWidgetManager.INSTANCE.get(mContext);
+ cwm.setWidgetRefreshCallback(mModel::refreshAndBindWidgetsAndShortcuts);
+ mOnTerminateCallback.add(() -> cwm.setWidgetRefreshCallback(null));
IconObserver observer = new IconObserver();
SafeCloseable iconChangeTracker = mIconProvider.registerIconChangeListener(
@@ -159,6 +162,7 @@
mModel = new LauncherModel(context, this, mIconCache, new AppFilter(mContext),
iconCacheFileName != null);
mOnTerminateCallback.add(mIconCache::close);
+ mOnTerminateCallback.add(mModel::destroy);
}
private void onNotificationSettingsChanged(boolean areNotificationDotsEnabled) {
@@ -180,9 +184,6 @@
*/
@Override
public void close() {
- mModel.destroy();
- mContext.getSystemService(LauncherApps.class).unregisterCallback(mModel);
- CustomWidgetManager.INSTANCE.get(mContext).setWidgetRefreshCallback(null);
mOnTerminateCallback.executeAllAndDestroy();
}
diff --git a/src/com/android/launcher3/LauncherSettings.java b/src/com/android/launcher3/LauncherSettings.java
index 7fda326..2397429 100644
--- a/src/com/android/launcher3/LauncherSettings.java
+++ b/src/com/android/launcher3/LauncherSettings.java
@@ -89,7 +89,9 @@
/**
* The gesture is an application created shortcut
+ * @deprecated This is no longer supported. Use {@link #ITEM_TYPE_DEEP_SHORTCUT} instead
*/
+ @Deprecated
public static final int ITEM_TYPE_SHORTCUT = 1;
/**
@@ -213,7 +215,6 @@
public static final String itemTypeToString(int type) {
switch(type) {
case ITEM_TYPE_APPLICATION: return "APP";
- case ITEM_TYPE_SHORTCUT: return "SHORTCUT";
case ITEM_TYPE_FOLDER: return "FOLDER";
case ITEM_TYPE_APPWIDGET: return "WIDGET";
case ITEM_TYPE_CUSTOM_APPWIDGET: return "CUSTOMWIDGET";
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 73bb828..dbf0894 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -1840,7 +1840,6 @@
!= LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION);
boolean willBecomeShortcut =
(info.itemType == ITEM_TYPE_APPLICATION ||
- info.itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT ||
info.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT);
return (aboveShortcut && willBecomeShortcut);
@@ -2759,7 +2758,7 @@
final PendingAddItemInfo pendingInfo = (PendingAddItemInfo) info;
boolean findNearestVacantCell = true;
- if (pendingInfo.itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT) {
+ if (pendingInfo.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
mTargetCell = findNearestArea(touchXY[0], touchXY[1], spanX, spanY,
cellLayout, mTargetCell);
float distance = cellLayout.getDistanceFromWorkspaceCellVisualCenter(
@@ -2832,8 +2831,7 @@
View view;
switch (info.itemType) {
- case ITEM_TYPE_APPLICATION:
- case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
+ case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT:
case LauncherSettings.Favorites.ITEM_TYPE_SEARCH_ACTION:
if (info instanceof WorkspaceItemFactory) {
diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index 621c2ab..dcc34ba 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -313,7 +313,7 @@
"Enable a grid-only overview without a focused task.");
public static final BooleanFlag ENABLE_CURSOR_HOVER_STATES = getDebugFlag(243191650,
- "ENABLE_CURSOR_HOVER_STATES", DISABLED,
+ "ENABLE_CURSOR_HOVER_STATES", ENABLED,
"Enables cursor hover states for certain elements.");
// TODO(Block 24): Clean up flags
@@ -381,7 +381,7 @@
"Enable initiating split screen from workspace to workspace.");
public static final BooleanFlag ENABLE_TRACKPAD_GESTURE = getDebugFlag(271010401,
- "ENABLE_TRACKPAD_GESTURE", DISABLED, "Enables trackpad gesture.");
+ "ENABLE_TRACKPAD_GESTURE", ENABLED, "Enables trackpad gesture.");
// TODO(Block 29): Clean up flags
public static final BooleanFlag ENABLE_ALL_APPS_BUTTON_IN_HOTSEAT = getDebugFlag(270393897,
diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java
index 4ae54e6..f38cce1 100644
--- a/src/com/android/launcher3/folder/Folder.java
+++ b/src/com/android/launcher3/folder/Folder.java
@@ -881,7 +881,6 @@
final ItemInfo item = d.dragInfo;
final int itemType = item.itemType;
return ((itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION ||
- itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT ||
itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT));
}
diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java
index 2c1100f..be643b3 100644
--- a/src/com/android/launcher3/folder/FolderIcon.java
+++ b/src/com/android/launcher3/folder/FolderIcon.java
@@ -260,7 +260,6 @@
private boolean willAcceptItem(ItemInfo item) {
final int itemType = item.itemType;
return ((itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION ||
- itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT ||
itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) &&
item != mInfo && !mFolder.isOpen());
}
diff --git a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
index 47677ea..7241b17 100644
--- a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
+++ b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
@@ -34,13 +34,9 @@
import android.appwidget.AppWidgetProviderInfo;
import android.content.Context;
import android.content.ContextWrapper;
-import android.content.Intent;
import android.content.res.TypedArray;
-import android.graphics.Color;
import android.graphics.PointF;
import android.graphics.Rect;
-import android.graphics.drawable.AdaptiveIconDrawable;
-import android.graphics.drawable.ColorDrawable;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
@@ -78,8 +74,6 @@
import com.android.launcher3.celllayout.CellPosMapper;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.folder.FolderIcon;
-import com.android.launcher3.icons.BaseIconFactory;
-import com.android.launcher3.icons.BitmapInfo;
import com.android.launcher3.icons.LauncherIcons;
import com.android.launcher3.model.BgDataModel;
import com.android.launcher3.model.BgDataModel.FixedContainerItems;
@@ -183,7 +177,6 @@
private final DeviceProfile mDp;
private final DeviceProfile mDpOrig;
private final Rect mInsets;
- private final WorkspaceItemInfo mWorkspaceItemInfo;
private final LayoutInflater mHomeElementInflater;
private final InsettableFrameLayout mRootView;
private final Hotseat mHotseat;
@@ -221,19 +214,6 @@
mDp.isTaskbarPresent ? 0 : currentWindowInsets.getSystemWindowInsetBottom());
mDp.updateInsets(mInsets);
- BaseIconFactory iconFactory =
- new BaseIconFactory(context, mIdp.fillResIconDpi, mIdp.iconBitmapSize) { };
- BitmapInfo iconInfo = iconFactory.createBadgedIconBitmap(
- new AdaptiveIconDrawable(
- new ColorDrawable(Color.WHITE),
- new ColorDrawable(Color.WHITE)));
-
- mWorkspaceItemInfo = new WorkspaceItemInfo();
- mWorkspaceItemInfo.bitmap = iconInfo;
- mWorkspaceItemInfo.intent = new Intent();
- mWorkspaceItemInfo.contentDescription = mWorkspaceItemInfo.title =
- context.getString(R.string.label_application);
-
mHomeElementInflater = LayoutInflater.from(
new ContextThemeWrapper(this, R.style.HomeScreenElementTheme));
mHomeElementInflater.setFactory2(this);
@@ -483,7 +463,6 @@
for (ItemInfo itemInfo : currentWorkspaceItems) {
switch (itemInfo.itemType) {
case Favorites.ITEM_TYPE_APPLICATION:
- case Favorites.ITEM_TYPE_SHORTCUT:
case Favorites.ITEM_TYPE_DEEP_SHORTCUT:
inflateAndAddIcon((WorkspaceItemInfo) itemInfo);
break;
diff --git a/src/com/android/launcher3/model/AddWorkspaceItemsTask.java b/src/com/android/launcher3/model/AddWorkspaceItemsTask.java
index 27d1f78..5e86bd6 100644
--- a/src/com/android/launcher3/model/AddWorkspaceItemsTask.java
+++ b/src/com/android/launcher3/model/AddWorkspaceItemsTask.java
@@ -91,8 +91,7 @@
List<ItemInfo> filteredItems = new ArrayList<>();
for (Pair<ItemInfo, Object> entry : mItemList) {
ItemInfo item = entry.first;
- if (item.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION ||
- item.itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT) {
+ if (item.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
// Short-circuit this logic if the icon exists somewhere on the workspace
if (shortcutExists(dataModel, item.getIntent(), item.user)) {
continue;
diff --git a/src/com/android/launcher3/model/BgDataModel.java b/src/com/android/launcher3/model/BgDataModel.java
index 0861e9d..5b0da5b 100644
--- a/src/com/android/launcher3/model/BgDataModel.java
+++ b/src/com/android/launcher3/model/BgDataModel.java
@@ -210,7 +210,6 @@
// Fall through.
}
case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
- case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
workspaceItems.remove(item);
break;
case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
@@ -245,7 +244,6 @@
break;
case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT:
case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
- case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP ||
item.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
workspaceItems.add(item);
diff --git a/src/com/android/launcher3/model/GridSizeMigrationUtil.java b/src/com/android/launcher3/model/GridSizeMigrationUtil.java
index 9a6cde6..9d16610 100644
--- a/src/com/android/launcher3/model/GridSizeMigrationUtil.java
+++ b/src/com/android/launcher3/model/GridSizeMigrationUtil.java
@@ -455,7 +455,6 @@
try {
// calculate weight
switch (entry.itemType) {
- case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT:
case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION: {
entry.mIntent = c.getString(indexIntent);
@@ -531,7 +530,6 @@
try {
// calculate weight
switch (entry.itemType) {
- case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT:
case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION: {
entry.mIntent = c.getString(indexIntent);
diff --git a/src/com/android/launcher3/model/ItemInstallQueue.java b/src/com/android/launcher3/model/ItemInstallQueue.java
index fa0511c..9a3abd4 100644
--- a/src/com/android/launcher3/model/ItemInstallQueue.java
+++ b/src/com/android/launcher3/model/ItemInstallQueue.java
@@ -286,7 +286,6 @@
final WorkspaceItemInfo si = new WorkspaceItemInfo();
si.user = user;
- si.itemType = ITEM_TYPE_APPLICATION;
LauncherActivityInfo lai;
boolean usePackageIcon = laiList.isEmpty();
diff --git a/src/com/android/launcher3/model/LoaderCursor.java b/src/com/android/launcher3/model/LoaderCursor.java
index 2054d93..33332f0 100644
--- a/src/com/android/launcher3/model/LoaderCursor.java
+++ b/src/com/android/launcher3/model/LoaderCursor.java
@@ -193,9 +193,7 @@
public IconRequestInfo<WorkspaceItemInfo> createIconRequestInfo(
WorkspaceItemInfo wai, boolean useLowResIcon) {
- byte[] iconBlob = itemType == Favorites.ITEM_TYPE_SHORTCUT
- || itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT
- || restoreFlag != 0
+ byte[] iconBlob = itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT || restoreFlag != 0
? getIconBlob() : null;
return new IconRequestInfo<>(wai, mActivityInfo, iconBlob, useLowResIcon);
@@ -347,7 +345,6 @@
}
final WorkspaceItemInfo info = new WorkspaceItemInfo();
- info.itemType = Favorites.ITEM_TYPE_APPLICATION;
info.user = user;
info.intent = newIntent;
diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java
index 1a8cf24..d2a8174 100644
--- a/src/com/android/launcher3/model/LoaderTask.java
+++ b/src/com/android/launcher3/model/LoaderTask.java
@@ -503,7 +503,6 @@
boolean allowMissingTarget = false;
switch (c.itemType) {
- case Favorites.ITEM_TYPE_SHORTCUT:
case Favorites.ITEM_TYPE_APPLICATION:
case Favorites.ITEM_TYPE_DEEP_SHORTCUT:
Intent intent = c.parseIntent();
@@ -517,9 +516,8 @@
ComponentName cn = intent.getComponent();
String targetPkg = cn == null ? intent.getPackage() : cn.getPackageName();
- if (TextUtils.isEmpty(targetPkg)
- && c.itemType != Favorites.ITEM_TYPE_SHORTCUT) {
- c.markDeleted("Only legacy shortcuts can have null package");
+ if (TextUtils.isEmpty(targetPkg)) {
+ c.markDeleted("Shortcuts can't have null package");
return;
}
diff --git a/src/com/android/launcher3/model/ModelWriter.java b/src/com/android/launcher3/model/ModelWriter.java
index ddb8b05..f2afaeb 100644
--- a/src/com/android/launcher3/model/ModelWriter.java
+++ b/src/com/android/launcher3/model/ModelWriter.java
@@ -498,7 +498,6 @@
modelItem.container == Favorites.CONTAINER_HOTSEAT)) {
switch (modelItem.itemType) {
case Favorites.ITEM_TYPE_APPLICATION:
- case Favorites.ITEM_TYPE_SHORTCUT:
case Favorites.ITEM_TYPE_DEEP_SHORTCUT:
case Favorites.ITEM_TYPE_FOLDER:
if (!mBgDataModel.workspaceItems.contains(modelItem)) {
diff --git a/src/com/android/launcher3/model/data/ItemInfo.java b/src/com/android/launcher3/model/data/ItemInfo.java
index bfb80b3..1c68292 100644
--- a/src/com/android/launcher3/model/data/ItemInfo.java
+++ b/src/com/android/launcher3/model/data/ItemInfo.java
@@ -30,7 +30,6 @@
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET;
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT;
-import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_TASK;
import static com.android.launcher3.logger.LauncherAtom.ContainerInfo.ContainerCase.CONTAINER_NOT_SET;
import static com.android.launcher3.shortcuts.ShortcutKey.EXTRA_SHORTCUT_ID;
@@ -87,7 +86,6 @@
/**
* One of {@link Favorites#ITEM_TYPE_APPLICATION},
- * {@link Favorites#ITEM_TYPE_SHORTCUT},
* {@link Favorites#ITEM_TYPE_DEEP_SHORTCUT}
* {@link Favorites#ITEM_TYPE_FOLDER},
* {@link Favorites#ITEM_TYPE_APP_PAIR},
@@ -361,13 +359,6 @@
})
.orElse(LauncherAtom.Shortcut.newBuilder()));
break;
- case ITEM_TYPE_SHORTCUT:
- itemBuilder
- .setShortcut(nullableComponent
- .map(component -> LauncherAtom.Shortcut.newBuilder()
- .setShortcutName(component.flattenToShortString()))
- .orElse(LauncherAtom.Shortcut.newBuilder()));
- break;
case ITEM_TYPE_APPWIDGET:
itemBuilder
.setWidget(nullableComponent
diff --git a/src/com/android/launcher3/model/data/WorkspaceItemInfo.java b/src/com/android/launcher3/model/data/WorkspaceItemInfo.java
index 01606d4..3ce194d 100644
--- a/src/com/android/launcher3/model/data/WorkspaceItemInfo.java
+++ b/src/com/android/launcher3/model/data/WorkspaceItemInfo.java
@@ -96,7 +96,7 @@
public WorkspaceItemInfo() {
- itemType = LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
+ itemType = LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
}
public WorkspaceItemInfo(WorkspaceItemInfo info) {
@@ -205,8 +205,8 @@
@Override
public ComponentName getTargetComponent() {
ComponentName cn = super.getTargetComponent();
- if (cn == null && (itemType == Favorites.ITEM_TYPE_SHORTCUT || hasStatusFlag(
- FLAG_SUPPORTS_WEB_UI | FLAG_AUTOINSTALL_ICON | FLAG_RESTORED_ICON))) {
+ if (cn == null && hasStatusFlag(
+ FLAG_SUPPORTS_WEB_UI | FLAG_AUTOINSTALL_ICON | FLAG_RESTORED_ICON)) {
// Legacy shortcuts and promise icons with web UI may not have a componentName but just
// a packageName. In that case create a empty componentName instead of adding additional
// check everywhere.
diff --git a/src/com/android/launcher3/pm/ShortcutConfigActivityInfo.java b/src/com/android/launcher3/pm/ShortcutConfigActivityInfo.java
index b24ee34..06da8c5 100644
--- a/src/com/android/launcher3/pm/ShortcutConfigActivityInfo.java
+++ b/src/com/android/launcher3/pm/ShortcutConfigActivityInfo.java
@@ -72,7 +72,7 @@
}
public int getItemType() {
- return LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
+ return LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT;
}
@Override
diff --git a/src/com/android/launcher3/util/DisplayController.java b/src/com/android/launcher3/util/DisplayController.java
index 776fe40..68ed78a 100644
--- a/src/com/android/launcher3/util/DisplayController.java
+++ b/src/com/android/launcher3/util/DisplayController.java
@@ -53,8 +53,8 @@
import java.io.PrintWriter;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Collections;
+import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
@@ -105,7 +105,8 @@
private final LauncherPrefs mPrefs;
- private DisplayController(Context context) {
+ @VisibleForTesting
+ protected DisplayController(Context context) {
mContext = context;
mDM = context.getSystemService(DisplayManager.class);
mPrefs = LauncherPrefs.get(context);
@@ -323,7 +324,7 @@
// WindowBounds
public final WindowBounds realBounds;
public final Set<WindowBounds> supportedBounds = new ArraySet<>();
- private final ArrayMap<CachedDisplayInfo, WindowBounds[]> mPerDisplayBounds =
+ private final ArrayMap<CachedDisplayInfo, List<WindowBounds>> mPerDisplayBounds =
new ArrayMap<>();
public Info(Context displayInfoContext) {
@@ -334,7 +335,7 @@
// Used for testing
public Info(Context displayInfoContext,
WindowManagerProxy wmProxy,
- Map<CachedDisplayInfo, WindowBounds[]> perDisplayBoundsCache) {
+ Map<CachedDisplayInfo, List<WindowBounds>> perDisplayBoundsCache) {
CachedDisplayInfo displayInfo = wmProxy.getDisplayInfo(displayInfoContext);
normalizedDisplayInfo = displayInfo.normalize();
rotation = displayInfo.rotation;
@@ -348,7 +349,7 @@
navigationMode = wmProxy.getNavigationMode(displayInfoContext);
mPerDisplayBounds.putAll(perDisplayBoundsCache);
- WindowBounds[] cachedValue = mPerDisplayBounds.get(normalizedDisplayInfo);
+ List<WindowBounds> cachedValue = mPerDisplayBounds.get(normalizedDisplayInfo);
realBounds = wmProxy.getRealBounds(displayInfoContext, displayInfo);
if (cachedValue == null) {
@@ -366,22 +367,20 @@
if (cachedValue != null) {
// Verify that the real bounds are a match
- WindowBounds expectedBounds = cachedValue[displayInfo.rotation];
+ WindowBounds expectedBounds = cachedValue.get(displayInfo.rotation);
if (!realBounds.equals(expectedBounds)) {
- WindowBounds[] clone = new WindowBounds[4];
- System.arraycopy(cachedValue, 0, clone, 0, 4);
- clone[displayInfo.rotation] = realBounds;
+ List<WindowBounds> clone = new ArrayList<>(cachedValue);
+ clone.set(displayInfo.rotation, realBounds);
mPerDisplayBounds.put(normalizedDisplayInfo, clone);
}
}
- mPerDisplayBounds.values().forEach(
- windowBounds -> Collections.addAll(supportedBounds, windowBounds));
+ mPerDisplayBounds.values().forEach(supportedBounds::addAll);
if (DEBUG) {
Log.d(TAG, "displayInfo: " + displayInfo);
Log.d(TAG, "realBounds: " + realBounds);
Log.d(TAG, "normalizedDisplayInfo: " + normalizedDisplayInfo);
mPerDisplayBounds.forEach((key, value) -> Log.d(TAG,
- "perDisplayBounds - " + key + ": " + Arrays.deepToString(value)));
+ "perDisplayBounds - " + key + ": " + value));
}
}
@@ -438,7 +437,7 @@
pw.println(" navigationMode=" + info.navigationMode.name());
pw.println(" currentSize=" + info.currentSize);
info.mPerDisplayBounds.forEach((key, value) -> pw.println(
- " perDisplayBounds - " + key + ": " + Arrays.deepToString(value)));
+ " perDisplayBounds - " + key + ": " + value));
}
/**
diff --git a/src/com/android/launcher3/util/IntArray.java b/src/com/android/launcher3/util/IntArray.java
index 1c78795..2498242 100644
--- a/src/com/android/launcher3/util/IntArray.java
+++ b/src/com/android/launcher3/util/IntArray.java
@@ -250,6 +250,11 @@
return b.toString();
}
+ @Override
+ public String toString() {
+ return "IntArray [" + toConcatString() + "]";
+ }
+
public static IntArray fromConcatString(String concatString) {
StringTokenizer tokenizer = new StringTokenizer(concatString, ",");
int[] array = new int[tokenizer.countTokens()];
diff --git a/src/com/android/launcher3/util/MainThreadInitializedObject.java b/src/com/android/launcher3/util/MainThreadInitializedObject.java
index 6a4e528..1cb9994 100644
--- a/src/com/android/launcher3/util/MainThreadInitializedObject.java
+++ b/src/com/android/launcher3/util/MainThreadInitializedObject.java
@@ -48,8 +48,8 @@
}
public T get(Context context) {
- if (context instanceof SandboxContext) {
- return ((SandboxContext) context).getObject(this, mProvider);
+ if (context instanceof SandboxContext sc) {
+ return sc.getObject(this);
}
if (mValue == null) {
@@ -131,23 +131,22 @@
* Find a cached object from mObjectMap if we have already created one. If not, generate
* an object using the provider.
*/
- private <T> T getObject(MainThreadInitializedObject<T> object, ObjectProvider<T> provider) {
+ protected <T> T getObject(MainThreadInitializedObject<T> object) {
synchronized (mDestroyLock) {
if (mDestroyed) {
Log.e(TAG, "Static object access with a destroyed context");
}
-
T t = (T) mObjectMap.get(object);
if (t != null) {
return t;
}
if (Looper.myLooper() == Looper.getMainLooper()) {
- t = createObject(provider);
+ t = createObject(object);
// Check if we've explicitly allowed the object or if it's a SafeCloseable,
// it will get destroyed in onDestroy()
if (!mAllowedObjects.contains(object) && !(t instanceof SafeCloseable)) {
- throw new IllegalStateException(
- "Leaking unknown objects " + object + " " + provider + " " + t);
+ throw new IllegalStateException("Leaking unknown objects "
+ + object + " " + object.mProvider + " " + t);
}
mObjectMap.put(object, t);
mOrderedObjects.add(t);
@@ -156,15 +155,15 @@
}
try {
- return MAIN_EXECUTOR.submit(() -> getObject(object, provider)).get();
+ return MAIN_EXECUTOR.submit(() -> getObject(object)).get();
} catch (InterruptedException | ExecutionException e) {
throw new RuntimeException(e);
}
}
@UiThread
- protected <T> T createObject(ObjectProvider<T> provider) {
- return provider.get(this);
+ protected <T> T createObject(MainThreadInitializedObject<T> object) {
+ return object.mProvider.get(this);
}
}
}
diff --git a/src/com/android/launcher3/util/window/WindowManagerProxy.java b/src/com/android/launcher3/util/window/WindowManagerProxy.java
index 4093bc9..278a37e 100644
--- a/src/com/android/launcher3/util/window/WindowManagerProxy.java
+++ b/src/com/android/launcher3/util/window/WindowManagerProxy.java
@@ -57,6 +57,9 @@
import com.android.launcher3.util.ResourceBasedOverride;
import com.android.launcher3.util.WindowBounds;
+import java.util.ArrayList;
+import java.util.List;
+
/**
* Utility class for mocking some window manager behaviours
*/
@@ -90,11 +93,11 @@
* Returns a map of normalized info of internal displays to estimated window bounds
* for that display
*/
- public ArrayMap<CachedDisplayInfo, WindowBounds[]> estimateInternalDisplayBounds(
+ public ArrayMap<CachedDisplayInfo, List<WindowBounds>> estimateInternalDisplayBounds(
Context displayInfoContext) {
CachedDisplayInfo info = getDisplayInfo(displayInfoContext).normalize();
- WindowBounds[] bounds = estimateWindowBounds(displayInfoContext, info);
- ArrayMap<CachedDisplayInfo, WindowBounds[]> result = new ArrayMap<>();
+ List<WindowBounds> bounds = estimateWindowBounds(displayInfoContext, info);
+ ArrayMap<CachedDisplayInfo, List<WindowBounds>> result = new ArrayMap<>();
result.put(info, bounds);
return result;
}
@@ -200,7 +203,8 @@
/**
* Returns a list of possible WindowBounds for the display keyed on the 4 surface rotations
*/
- protected WindowBounds[] estimateWindowBounds(Context context, CachedDisplayInfo displayInfo) {
+ protected List<WindowBounds> estimateWindowBounds(Context context,
+ CachedDisplayInfo displayInfo) {
int densityDpi = context.getResources().getConfiguration().densityDpi;
int rotation = displayInfo.rotation;
Rect safeCutout = displayInfo.cutout;
@@ -243,7 +247,7 @@
? 0
: getDimenByName(systemRes, NAVBAR_LANDSCAPE_LEFT_RIGHT_SIZE);
- WindowBounds[] result = new WindowBounds[4];
+ List<WindowBounds> result = new ArrayList<>(4);
Point tempSize = new Point();
for (int i = 0; i < 4; i++) {
int rotationChange = deltaRotation(rotation, i);
@@ -274,7 +278,7 @@
} else {
insets.right = Math.max(insets.right, navbarWidth);
}
- result[i] = new WindowBounds(bounds, insets, i);
+ result.add(new WindowBounds(bounds, insets, i));
}
return result;
}
diff --git a/src/com/android/launcher3/views/ActivityContext.java b/src/com/android/launcher3/views/ActivityContext.java
index 515a2d8..3c8dfbb 100644
--- a/src/com/android/launcher3/views/ActivityContext.java
+++ b/src/com/android/launcher3/views/ActivityContext.java
@@ -332,6 +332,12 @@
return null;
}
+ boolean isShortcut = (item instanceof WorkspaceItemInfo)
+ && item.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT
+ && !((WorkspaceItemInfo) item).isPromise();
+ if (isShortcut && GO_DISABLE_WIDGETS) {
+ return null;
+ }
ActivityOptionsWrapper options = v != null ? getActivityLaunchOptions(v, item)
: makeDefaultActivityOptions(item != null && item.animationType == DEFAULT_NO_ICON
? SPLASH_SCREEN_STYLE_SOLID_COLOR : -1 /* SPLASH_SCREEN_STYLE_UNDEFINED */);
@@ -343,13 +349,11 @@
intent.setSourceBounds(Utilities.getViewBounds(v));
}
try {
- boolean isShortcut = (item instanceof WorkspaceItemInfo)
- && (item.itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT
- || item.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT)
- && !((WorkspaceItemInfo) item).isPromise();
if (isShortcut) {
- // Shortcuts need some special checks due to legacy reasons.
- startShortcutIntentSafely(intent, optsBundle, item);
+ String id = ((WorkspaceItemInfo) item).getDeepShortcutId();
+ String packageName = intent.getPackage();
+ ((Context) this).getSystemService(LauncherApps.class).startShortcut(
+ packageName, id, intent.getSourceBounds(), optsBundle, user);
} else if (user == null || user.equals(Process.myUserHandle())) {
// Could be launching some bookkeeping activity
context.startActivity(intent, optsBundle);
@@ -424,55 +428,6 @@
return new ActivityOptionsWrapper(options, new RunnableList());
}
- /**
- * Safely launches an intent for a shortcut.
- *
- * @param intent Intent to start.
- * @param optsBundle Optional launch arguments.
- * @param info Shortcut information.
- */
- default void startShortcutIntentSafely(Intent intent, Bundle optsBundle, ItemInfo info) {
- try {
- StrictMode.VmPolicy oldPolicy = StrictMode.getVmPolicy();
- try {
- // Temporarily disable deathPenalty on all default checks. For eg, shortcuts
- // containing file Uri's would cause a crash as penaltyDeathOnFileUriExposure
- // is enabled by default on NYC.
- StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder().detectAll()
- .penaltyLog().build());
-
- if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
- String id = ((WorkspaceItemInfo) info).getDeepShortcutId();
- String packageName = intent.getPackage();
- startShortcut(packageName, id, intent.getSourceBounds(), optsBundle, info.user);
- } else {
- // Could be launching some bookkeeping activity
- ((Context) this).startActivity(intent, optsBundle);
- }
- } finally {
- StrictMode.setVmPolicy(oldPolicy);
- }
- } catch (SecurityException e) {
- throw e;
- }
- }
-
- /**
- * A wrapper around the platform method with Launcher specific checks.
- */
- default void startShortcut(String packageName, String id, Rect sourceBounds,
- Bundle startActivityOptions, UserHandle user) {
- if (GO_DISABLE_WIDGETS) {
- return;
- }
- try {
- ((Context) this).getSystemService(LauncherApps.class).startShortcut(packageName, id,
- sourceBounds, startActivityOptions, user);
- } catch (SecurityException | IllegalStateException e) {
- Log.e(TAG, "Failed to start shortcut", e);
- }
- }
-
default CellPosMapper getCellPosMapper() {
return CellPosMapper.DEFAULT;
}
diff --git a/src/com/android/launcher3/views/OptionsPopupView.java b/src/com/android/launcher3/views/OptionsPopupView.java
index 4641e31..aebf752 100644
--- a/src/com/android/launcher3/views/OptionsPopupView.java
+++ b/src/com/android/launcher3/views/OptionsPopupView.java
@@ -290,7 +290,6 @@
static WorkspaceItemInfo placeholderInfo(Intent intent) {
WorkspaceItemInfo placeholderInfo = new WorkspaceItemInfo();
placeholderInfo.intent = intent;
- placeholderInfo.itemType = LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
placeholderInfo.container = LauncherSettings.Favorites.CONTAINER_SETTINGS;
return placeholderInfo;
}
diff --git a/tests/res/raw/cache_data_updated_task_data.txt b/tests/res/raw/cache_data_updated_task_data.txt
deleted file mode 100644
index 603dbe3..0000000
--- a/tests/res/raw/cache_data_updated_task_data.txt
+++ /dev/null
@@ -1,28 +0,0 @@
-# Model data used by CacheDataUpdatedTaskTest
-
-classMap s com.android.launcher3.model.data.WorkspaceItemInfo
-
-# Items for the BgDataModel
-
-# App shortcuts
-bgItem s itemType=0 title=app1-class1 intent=component=app1/class1 id=1
-bgItem s itemType=0 title=app1-class2 intent=component=app1/class2 id=2
-bgItem s itemType=0 title=app2-class1 intent=component=app2/class1 id=3
-bgItem s itemType=0 title=app2-class2 intent=component=app2/class2 id=4
-
-# Auto install app shortcut
-bgItem s itemType=0 status=2 title=app3-class1 intent=component=app3/class1 id=5
-bgItem s itemType=0 status=2 title=app3-class2 intent=component=app3/class2 id=6
-
-# Custom shortcuts
-bgItem s itemType=1 title=app1-shrt intent=component=app1/class3 id=7
-bgItem s itemType=1 title=app4-shrt intent=component=app4/class1 id=8
-
-# Restored custom shortcut
-bgItem s itemType=1 status=1 title=app3-shrt intent=component=app3/class3 id=9
-bgItem s itemType=1 status=1 title=app5-shrt intent=component=app5/class1 id=10
-
-allApps componentName=app1/class1 intent=component=app1/class1
-allApps componentName=app1/class2 intent=component=app1/class2
-allApps componentName=app2/class1 intent=component=app2/class1
-allApps componentName=app2/class2 intent=component=app2/class2
\ No newline at end of file
diff --git a/tests/res/raw/package_install_state_change_task_data.txt b/tests/res/raw/package_install_state_change_task_data.txt
deleted file mode 100644
index e82ea9d..0000000
--- a/tests/res/raw/package_install_state_change_task_data.txt
+++ /dev/null
@@ -1,24 +0,0 @@
-# Model data used by PackageInstallStateChangeTaskTest
-
-classMap s com.android.launcher3.model.data.WorkspaceItemInfo
-classMap w com.android.launcher3.model.data.LauncherAppWidgetInfo
-
-# Items for the BgDataModel
-
-# App shortcuts
-bgItem s itemType=0 title=app1-class1 intent=component=app1/class1 id=1
-bgItem s itemType=0 title=app1-class2 intent=component=app1/class2 id=2
-bgItem s itemType=0 title=app2-class1 intent=component=app2/class1 id=3
-bgItem s itemType=0 title=app2-class2 intent=component=app2/class2 id=4
-
-# Promise icons for app3
-bgItem s itemType=0 status=2 title=app3-class1 intent=component=app3/class1 id=5
-bgItem s itemType=0 status=2 title=app3-class2 intent=component=app3/class2 id=6
-bgItem s itemType=1 status=1 title=app3-shrt intent=component=app3/class3 id=7
-
-# Promise icon for app4
-bgItem s itemType=1 status=1 title=app4-shrt intent=component=app4/class1 id=8
-
-# Widget
-bgItem w providerName=app4/provider1 id=9
-bgItem w providerName=app5/provider1 id=10
\ No newline at end of file
diff --git a/tests/res/raw/widgets_predication_update_task_data.txt b/tests/res/raw/widgets_predication_update_task_data.txt
deleted file mode 100644
index 941d195..0000000
--- a/tests/res/raw/widgets_predication_update_task_data.txt
+++ /dev/null
@@ -1,24 +0,0 @@
-# Model data used by WidgetsPredictionUpdateTasksTest
-
-classMap s com.android.launcher3.model.data.WorkspaceItemInfo
-classMap w com.android.launcher3.model.data.LauncherAppWidgetInfo
-
-# Items for the BgDataModel
-
-# App shortcuts
-bgItem s itemType=0 title=app1-class1 intent=component=app1/class1 id=1
-bgItem s itemType=0 title=app1-class2 intent=component=app1/class2 id=2
-bgItem s itemType=0 title=app2-class1 intent=component=app2/class1 id=3
-bgItem s itemType=0 title=app2-class2 intent=component=app2/class2 id=4
-
-# Promise icons for app3
-bgItem s itemType=0 status=2 title=app3-class1 intent=component=app3/class1 id=5
-bgItem s itemType=0 status=2 title=app3-class2 intent=component=app3/class2 id=6
-bgItem s itemType=1 status=1 title=app3-shrt intent=component=app3/class3 id=7
-
-# Promise icon for app4
-bgItem s itemType=1 status=1 title=app4-shrt intent=component=app4/class1 id=8
-
-# Widget
-bgItem w providerName=app4/provider1 id=9
-bgItem w providerName=app5/provider1 id=10
\ No newline at end of file
diff --git a/tests/src/com/android/launcher3/AbstractDeviceProfileTest.kt b/tests/src/com/android/launcher3/AbstractDeviceProfileTest.kt
index 3de4d55..6dec67e 100644
--- a/tests/src/com/android/launcher3/AbstractDeviceProfileTest.kt
+++ b/tests/src/com/android/launcher3/AbstractDeviceProfileTest.kt
@@ -196,7 +196,7 @@
isGestureMode: Boolean,
naturalX: Int,
naturalY: Int
- ): Array<WindowBounds> {
+ ): List<WindowBounds> {
val buttonsNavHeight = Utilities.dpToPx(48f, deviceSpec.densityDpi)
val rotation0Insets =
@@ -231,7 +231,7 @@
if (isGestureMode) deviceSpec.gesturePx else 0
)
- return arrayOf(
+ return listOf(
WindowBounds(Rect(0, 0, naturalX, naturalY), rotation0Insets, Surface.ROTATION_0),
WindowBounds(Rect(0, 0, naturalY, naturalX), rotation90Insets, Surface.ROTATION_90),
WindowBounds(Rect(0, 0, naturalX, naturalY), rotation180Insets, Surface.ROTATION_180),
@@ -243,11 +243,11 @@
deviceSpec: DeviceSpec,
naturalX: Int,
naturalY: Int
- ): Array<WindowBounds> {
+ ): List<WindowBounds> {
val naturalInsets = Rect(0, deviceSpec.statusBarNaturalPx, 0, 0)
val rotatedInsets = Rect(0, deviceSpec.statusBarRotatedPx, 0, 0)
- return arrayOf(
+ return listOf(
WindowBounds(Rect(0, 0, naturalX, naturalY), naturalInsets, Surface.ROTATION_0),
WindowBounds(Rect(0, 0, naturalY, naturalX), rotatedInsets, Surface.ROTATION_90),
WindowBounds(Rect(0, 0, naturalX, naturalY), naturalInsets, Surface.ROTATION_180),
@@ -256,7 +256,7 @@
}
private fun initializeCommonVars(
- perDisplayBoundsCache: Map<CachedDisplayInfo, Array<WindowBounds>>,
+ perDisplayBoundsCache: Map<CachedDisplayInfo, List<WindowBounds>>,
displayInfo: CachedDisplayInfo,
rotation: Int,
isGestureMode: Boolean = true,
diff --git a/tests/src/com/android/launcher3/model/AbstractWorkspaceModelTest.kt b/tests/src/com/android/launcher3/model/AbstractWorkspaceModelTest.kt
index 03352fe..98191fe 100644
--- a/tests/src/com/android/launcher3/model/AbstractWorkspaceModelTest.kt
+++ b/tests/src/com/android/launcher3/model/AbstractWorkspaceModelTest.kt
@@ -17,17 +17,18 @@
import android.content.ComponentName
import android.content.Context
-import android.content.Intent
import android.graphics.Rect
import com.android.launcher3.InvariantDeviceProfile
import com.android.launcher3.LauncherAppState
-import com.android.launcher3.LauncherSettings
+import com.android.launcher3.model.data.AppInfo
import com.android.launcher3.model.data.WorkspaceItemInfo
-import com.android.launcher3.util.ContentWriter
import com.android.launcher3.util.GridOccupancy
import com.android.launcher3.util.IntArray
import com.android.launcher3.util.IntSparseArrayMap
+import com.android.launcher3.util.LauncherLayoutBuilder
import com.android.launcher3.util.LauncherModelHelper
+import com.android.launcher3.util.LauncherModelHelper.TEST_ACTIVITY
+import com.android.launcher3.util.LauncherModelHelper.TEST_PACKAGE
import java.util.UUID
/** Base class for workspace related tests. */
@@ -38,6 +39,7 @@
val nonEmptyScreenSpaces = listOf(Rect(1, 2, 3, 4))
}
+ protected lateinit var mLayoutBuilder: LauncherLayoutBuilder
protected lateinit var mTargetContext: Context
protected lateinit var mIdp: InvariantDeviceProfile
protected lateinit var mAppState: LauncherAppState
@@ -47,6 +49,7 @@
protected lateinit var mScreenOccupancy: IntSparseArrayMap<GridOccupancy>
open fun setup() {
+ mLayoutBuilder = LauncherLayoutBuilder()
mModelHelper = LauncherModelHelper()
mTargetContext = mModelHelper.sandboxContext
mIdp = InvariantDeviceProfile.INSTANCE[mTargetContext]
@@ -64,10 +67,11 @@
/** Sets up workspaces with the given screen IDs with some items and a 2x2 space. */
fun setupWorkspaces(screenIdsWithItems: List<Int>) {
- var nextItemId = 1
- screenIdsWithItems.forEach { screenId ->
- nextItemId = setupWorkspace(nextItemId, screenId, nonEmptyScreenSpaces)
- }
+ screenIdsWithItems.forEach { screenId -> setupWorkspace(screenId, nonEmptyScreenSpaces) }
+ mModelHelper.setupDefaultLayoutProvider(mLayoutBuilder)
+ mIdp.numRows = 5
+ mIdp.numColumns = mIdp.numRows
+ mModelHelper.loadModelSync()
}
/**
@@ -78,30 +82,23 @@
screen1: List<Rect>? = null,
screen2: List<Rect>? = null,
screen3: List<Rect>? = null,
- ) = listOf(screen0, screen1, screen2, screen3).let(this::setupWithSpaces)
+ ) {
+ listOf(screen0, screen1, screen2, screen3).let(this::setupWithSpaces)
+ mModelHelper.setupDefaultLayoutProvider(mLayoutBuilder)
+ mIdp.numRows = 5
+ mIdp.numColumns = mIdp.numRows
+ mModelHelper.loadModelSync()
+ }
private fun setupWithSpaces(workspaceSpaces: List<List<Rect>?>) {
- var nextItemId = 1
workspaceSpaces.forEachIndexed { screenId, spaces ->
if (spaces != null) {
- nextItemId = setupWorkspace(nextItemId, screenId, spaces)
+ setupWorkspace(screenId, spaces)
}
}
}
- private fun setupWorkspace(startId: Int, screenId: Int, spaces: List<Rect>): Int {
- return mModelHelper.executeSimpleTask { dataModel ->
- writeWorkspaceWithSpaces(dataModel, startId, screenId, spaces)
- }
- }
-
- private fun writeWorkspaceWithSpaces(
- bgDataModel: BgDataModel,
- itemStartId: Int,
- screenId: Int,
- spaces: List<Rect>,
- ): Int {
- var itemId = itemStartId
+ private fun setupWorkspace(screenId: Int, spaces: List<Rect>) {
val occupancy = GridOccupancy(mIdp.numColumns, mIdp.numRows)
occupancy.markCells(0, 0, mIdp.numColumns, mIdp.numRows, true)
spaces.forEach { spaceRect -> occupancy.markCells(spaceRect, false) }
@@ -109,35 +106,22 @@
mScreenOccupancy.append(screenId, occupancy)
for (x in 0 until mIdp.numColumns) {
for (y in 0 until mIdp.numRows) {
- if (!occupancy.cells[x][y]) {
- continue
+ if (occupancy.cells[x][y]) {
+ mLayoutBuilder.atWorkspace(x, y, screenId).putApp(TEST_PACKAGE, TEST_ACTIVITY)
}
- val info = getExistingItem()
- info.id = itemId++
- info.screenId = screenId
- info.cellX = x
- info.cellY = y
- info.container = LauncherSettings.Favorites.CONTAINER_DESKTOP
- bgDataModel.addItem(mTargetContext, info, false)
- val writer = ContentWriter(mTargetContext)
- info.writeToValues(writer)
- writer.put(LauncherSettings.Favorites._ID, info.id)
- mTargetContext.contentResolver.insert(
- LauncherSettings.Favorites.CONTENT_URI,
- writer.getValues(mTargetContext)
- )
}
}
- return itemId
}
fun getExistingItem() =
- WorkspaceItemInfo().apply { intent = Intent().setComponent(ComponentName("a", "b")) }
+ WorkspaceItemInfo().apply {
+ intent = AppInfo.makeLaunchIntent(ComponentName(TEST_PACKAGE, TEST_ACTIVITY))
+ }
fun getNewItem(): WorkspaceItemInfo {
val itemPackage = UUID.randomUUID().toString()
return WorkspaceItemInfo().apply {
- intent = Intent().setComponent(ComponentName(itemPackage, itemPackage))
+ intent = AppInfo.makeLaunchIntent(ComponentName(itemPackage, itemPackage))
}
}
}
diff --git a/tests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.kt b/tests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.kt
index 6636b8a..1155227 100644
--- a/tests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.kt
+++ b/tests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.kt
@@ -23,7 +23,7 @@
import com.android.launcher3.model.data.WorkspaceItemInfo
import com.android.launcher3.util.Executors
import com.android.launcher3.util.IntArray
-import com.android.launcher3.util.IntSet
+import com.android.launcher3.util.TestUtil.runOnExecutorSync
import com.android.launcher3.util.any
import com.android.launcher3.util.eq
import com.android.launcher3.util.same
@@ -32,8 +32,6 @@
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
-import org.mockito.ArgumentCaptor
-import org.mockito.Captor
import org.mockito.Mock
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
@@ -46,11 +44,7 @@
@RunWith(AndroidJUnit4::class)
class AddWorkspaceItemsTaskTest : AbstractWorkspaceModelTest() {
- @Captor private lateinit var mAnimatedItemArgumentCaptor: ArgumentCaptor<ArrayList<ItemInfo>>
-
- @Captor private lateinit var mNotAnimatedItemArgumentCaptor: ArgumentCaptor<ArrayList<ItemInfo>>
-
- @Mock private lateinit var mDataModelCallbacks: BgDataModel.Callbacks
+ private lateinit var mDataModelCallbacks: MyCallbacks
@Mock private lateinit var mWorkspaceItemSpaceFinder: WorkspaceItemSpaceFinder
@@ -58,7 +52,7 @@
override fun setup() {
super.setup()
MockitoAnnotations.initMocks(this)
- whenever(mDataModelCallbacks.getPagesToBindSynchronously(any())).thenReturn(IntSet())
+ mDataModelCallbacks = MyCallbacks()
Executors.MAIN_EXECUTOR.submit { mModelHelper.model.addCallbacks(mDataModelCallbacks) }
.get()
}
@@ -105,7 +99,7 @@
val addedItems = testAddItems(nonEmptyScreenIds, itemToAdd)
assertThat(addedItems.size).isEqualTo(0)
- verifyZeroInteractions(mWorkspaceItemSpaceFinder, mDataModelCallbacks)
+ verifyZeroInteractions(mWorkspaceItemSpaceFinder)
}
@Test
@@ -191,22 +185,14 @@
): List<AddedItem> {
setupWorkspaces(nonEmptyScreenIds)
val task = newTask(*itemsToAdd)
- var updateCount = 0
- mModelHelper.executeTaskForTest(task).forEach {
- updateCount++
- it.run()
- }
val addedItems = mutableListOf<AddedItem>()
- if (updateCount > 0) {
- verify(mDataModelCallbacks)
- .bindAppsAdded(
- any(),
- mNotAnimatedItemArgumentCaptor.capture(),
- mAnimatedItemArgumentCaptor.capture()
- )
- addedItems.addAll(mAnimatedItemArgumentCaptor.value.map { AddedItem(it, true) })
- addedItems.addAll(mNotAnimatedItemArgumentCaptor.value.map { AddedItem(it, false) })
+
+ runOnExecutorSync(Executors.MODEL_EXECUTOR) {
+ mDataModelCallbacks.addedItems.clear()
+ mModelHelper.model.enqueueModelUpdateTask(task)
+ runOnExecutorSync(Executors.MAIN_EXECUTOR) {}
+ addedItems.addAll(mDataModelCallbacks.addedItems)
}
return addedItems
@@ -224,3 +210,17 @@
}
private data class AddedItem(val itemInfo: ItemInfo, val isAnimated: Boolean)
+
+private class MyCallbacks : BgDataModel.Callbacks {
+
+ val addedItems = mutableListOf<AddedItem>()
+
+ override fun bindAppsAdded(
+ newScreens: IntArray?,
+ addNotAnimated: ArrayList<ItemInfo>,
+ addAnimated: ArrayList<ItemInfo>
+ ) {
+ addedItems.addAll(addAnimated.map { AddedItem(it, true) })
+ addedItems.addAll(addNotAnimated.map { AddedItem(it, false) })
+ }
+}
diff --git a/tests/src/com/android/launcher3/model/CacheDataUpdatedTaskTest.java b/tests/src/com/android/launcher3/model/CacheDataUpdatedTaskTest.java
index f55b244..f771052 100644
--- a/tests/src/com/android/launcher3/model/CacheDataUpdatedTaskTest.java
+++ b/tests/src/com/android/launcher3/model/CacheDataUpdatedTaskTest.java
@@ -1,32 +1,31 @@
package com.android.launcher3.model;
+import static android.os.Process.myUserHandle;
+
+import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
+import static com.android.launcher3.util.LauncherModelHelper.TEST_ACTIVITY;
+import static com.android.launcher3.util.LauncherModelHelper.TEST_ACTIVITY2;
+import static com.android.launcher3.util.LauncherModelHelper.TEST_ACTIVITY3;
+import static com.android.launcher3.util.LauncherModelHelper.TEST_PACKAGE;
+import static com.android.launcher3.util.TestUtil.runOnExecutorSync;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertTrue;
-import android.content.ComponentName;
import android.content.Context;
-import android.content.pm.PackageInfo;
-import android.graphics.Bitmap;
-import android.graphics.Bitmap.Config;
-import android.graphics.Color;
-import android.os.Process;
-import android.os.UserHandle;
-import android.os.UserManager;
-import androidx.annotation.NonNull;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.icons.BitmapInfo;
-import com.android.launcher3.icons.IconCache;
-import com.android.launcher3.icons.cache.CachingLogic;
-import com.android.launcher3.model.data.AppInfo;
-import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.model.data.FolderInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
+import com.android.launcher3.util.IntSet;
+import com.android.launcher3.util.LauncherLayoutBuilder;
import com.android.launcher3.util.LauncherModelHelper;
+import com.android.launcher3.util.PackageUserKey;
import org.junit.After;
import org.junit.Before;
@@ -35,6 +34,7 @@
import java.util.Arrays;
import java.util.HashSet;
+import java.util.List;
/**
* Tests for {@link CacheDataUpdatedTask}
@@ -43,49 +43,40 @@
@RunWith(AndroidJUnit4.class)
public class CacheDataUpdatedTaskTest {
- private static final String NEW_LABEL_PREFIX = "new-label-";
+ private static final String PENDING_APP_1 = TEST_PACKAGE + ".pending1";
+ private static final String PENDING_APP_2 = TEST_PACKAGE + ".pending2";
private LauncherModelHelper mModelHelper;
+ private Context mContext;
+
+ private int mSession1;
@Before
public void setup() throws Exception {
mModelHelper = new LauncherModelHelper();
- mModelHelper.initializeData("cache_data_updated_task_data");
+ mContext = mModelHelper.sandboxContext;
+ mSession1 = mModelHelper.createInstallerSession(PENDING_APP_1);
+ mModelHelper.createInstallerSession(PENDING_APP_2);
- // Add placeholder entries in the cache to simulate update
- Context context = mModelHelper.sandboxContext;
- IconCache iconCache = LauncherAppState.getInstance(context).getIconCache();
- CachingLogic<ItemInfo> placeholderLogic = new CachingLogic<ItemInfo>() {
- @Override
- @NonNull
- public ComponentName getComponent(@NonNull ItemInfo info) {
- return info.getTargetComponent();
- }
+ LauncherLayoutBuilder builder = new LauncherLayoutBuilder()
+ .atHotseat(1).putFolder("MyFolder")
+ .addApp(TEST_PACKAGE, TEST_ACTIVITY) // 2
+ .addApp(TEST_PACKAGE, TEST_ACTIVITY2) // 3
+ .addApp(TEST_PACKAGE, TEST_ACTIVITY3) // 4
- @NonNull
- @Override
- public UserHandle getUser(@NonNull ItemInfo info) {
- return info.user;
- }
+ // Pending App 1
+ .addApp(PENDING_APP_1, TEST_ACTIVITY) // 5
+ .addApp(PENDING_APP_1, TEST_ACTIVITY2) // 6
+ .addApp(PENDING_APP_1, TEST_ACTIVITY3) // 7
- @NonNull
- @Override
- public CharSequence getLabel(@NonNull ItemInfo info) {
- return NEW_LABEL_PREFIX + info.id;
- }
-
- @NonNull
- @Override
- public BitmapInfo loadIcon(@NonNull Context context, @NonNull ItemInfo info) {
- return BitmapInfo.of(Bitmap.createBitmap(1, 1, Config.ARGB_8888), Color.RED);
- }
- };
-
- UserManager um = context.getSystemService(UserManager.class);
- for (ItemInfo info : mModelHelper.getBgDataModel().itemsIdMap) {
- iconCache.addIconToDBAndMemCache(info, placeholderLogic, new PackageInfo(),
- um.getSerialNumberForUser(info.user), true);
- }
+ // Pending App 2
+ .addApp(PENDING_APP_2, TEST_ACTIVITY) // 8
+ .addApp(PENDING_APP_2, TEST_ACTIVITY2) // 9
+ .addApp(PENDING_APP_2, TEST_ACTIVITY3) // 10
+ .build();
+ mModelHelper.setupDefaultLayoutProvider(builder);
+ mModelHelper.loadModelSync();
+ assertEquals(10, mModelHelper.getBgDataModel().itemsIdMap.size());
}
@After
@@ -94,27 +85,63 @@
}
private CacheDataUpdatedTask newTask(int op, String... pkg) {
- return new CacheDataUpdatedTask(op, Process.myUserHandle(),
+ return new CacheDataUpdatedTask(op, myUserHandle(),
new HashSet<>(Arrays.asList(pkg)));
}
@Test
- public void testCacheUpdate_update_apps() throws Exception {
- // Clear all icons from apps list so that its easy to check what was updated
- for (AppInfo info : mModelHelper.getAllAppsList().data) {
- info.bitmap = BitmapInfo.LOW_RES_INFO;
- }
+ public void testCacheUpdate_update_apps() {
+ // Run on model executor so that no other task runs in the middle.
+ runOnExecutorSync(MODEL_EXECUTOR, () -> {
+ // Clear all icons from apps list so that its easy to check what was updated
+ allItems().forEach(wi -> wi.bitmap = BitmapInfo.LOW_RES_INFO);
- mModelHelper.executeTaskForTest(newTask(CacheDataUpdatedTask.OP_CACHE_UPDATE, "app1"));
+ mModelHelper.getModel().enqueueModelUpdateTask(
+ newTask(CacheDataUpdatedTask.OP_CACHE_UPDATE, TEST_PACKAGE));
- // Verify that only the app icons of app1 (id 1 & 2) are updated. Custom shortcut (id 7)
- // is not updated
- verifyUpdate(1, 2);
+ // Verify that only the app icons of TEST_PACKAGE (id 2, 3, 4) are updated.
+ verifyUpdate(2, 3, 4);
+ });
+ }
- // Verify that only app1 var updated in allAppsList
- assertFalse(mModelHelper.getAllAppsList().data.isEmpty());
- for (AppInfo info : mModelHelper.getAllAppsList().data) {
- if (info.componentName.getPackageName().equals("app1")) {
+ @Test
+ public void testSessionUpdate_ignores_normal_apps() {
+ // Run on model executor so that no other task runs in the middle.
+ runOnExecutorSync(MODEL_EXECUTOR, () -> {
+ // Clear all icons from apps list so that its easy to check what was updated
+ allItems().forEach(wi -> wi.bitmap = BitmapInfo.LOW_RES_INFO);
+
+ mModelHelper.getModel().enqueueModelUpdateTask(
+ newTask(CacheDataUpdatedTask.OP_SESSION_UPDATE, TEST_PACKAGE));
+
+ // TEST_PACKAGE has no restored shortcuts. Verify that nothing was updated.
+ verifyUpdate();
+ });
+ }
+
+ @Test
+ public void testSessionUpdate_updates_pending_apps() {
+ // Run on model executor so that no other task runs in the middle.
+ runOnExecutorSync(MODEL_EXECUTOR, () -> {
+ LauncherAppState.getInstance(mContext).getIconCache().updateSessionCache(
+ new PackageUserKey(PENDING_APP_1, myUserHandle()),
+ mContext.getPackageManager().getPackageInstaller().getSessionInfo(mSession1));
+
+ // Clear all icons from apps list so that its easy to check what was updated
+ allItems().forEach(wi -> wi.bitmap = BitmapInfo.LOW_RES_INFO);
+
+ mModelHelper.getModel().enqueueModelUpdateTask(
+ newTask(CacheDataUpdatedTask.OP_SESSION_UPDATE, PENDING_APP_1));
+
+ // Only restored apps from PENDING_APP_1 (id 5, 6, 7) are updated
+ verifyUpdate(5, 6, 7);
+ });
+ }
+
+ private void verifyUpdate(int... idsUpdated) {
+ IntSet updates = IntSet.wrap(idsUpdated);
+ for (WorkspaceItemInfo info : allItems()) {
+ if (updates.contains(info.id)) {
assertFalse(info.bitmap.isNullOrLowRes());
} else {
assertTrue(info.bitmap.isNullOrLowRes());
@@ -122,33 +149,7 @@
}
}
- @Test
- public void testSessionUpdate_ignores_normal_apps() throws Exception {
- mModelHelper.executeTaskForTest(newTask(CacheDataUpdatedTask.OP_SESSION_UPDATE, "app1"));
-
- // app1 has no restored shortcuts. Verify that nothing was updated.
- verifyUpdate();
- }
-
- @Test
- public void testSessionUpdate_updates_pending_apps() throws Exception {
- mModelHelper.executeTaskForTest(newTask(CacheDataUpdatedTask.OP_SESSION_UPDATE, "app3"));
-
- // app3 has only restored apps (id 5, 6) and shortcuts (id 9). Verify that only apps were
- // were updated
- verifyUpdate(5, 6);
- }
-
- private void verifyUpdate(Integer... idsUpdated) {
- HashSet<Integer> updates = new HashSet<>(Arrays.asList(idsUpdated));
- for (ItemInfo info : mModelHelper.getBgDataModel().itemsIdMap) {
- if (updates.contains(info.id)) {
- assertEquals(NEW_LABEL_PREFIX + info.id, info.title);
- assertFalse(((WorkspaceItemInfo) info).bitmap.isNullOrLowRes());
- } else {
- assertNotSame(NEW_LABEL_PREFIX + info.id, info.title);
- assertTrue(((WorkspaceItemInfo) info).bitmap.isNullOrLowRes());
- }
- }
+ private List<WorkspaceItemInfo> allItems() {
+ return ((FolderInfo) mModelHelper.getBgDataModel().itemsIdMap.get(1)).contents;
}
}
diff --git a/tests/src/com/android/launcher3/model/GridSizeMigrationUtilTest.kt b/tests/src/com/android/launcher3/model/GridSizeMigrationUtilTest.kt
index 3b480ca..4fa5352 100644
--- a/tests/src/com/android/launcher3/model/GridSizeMigrationUtilTest.kt
+++ b/tests/src/com/android/launcher3/model/GridSizeMigrationUtilTest.kt
@@ -24,7 +24,6 @@
import android.os.Process
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import androidx.test.platform.app.InstrumentationRegistry
import com.android.launcher3.InvariantDeviceProfile
import com.android.launcher3.LauncherPrefs
import com.android.launcher3.LauncherPrefs.Companion.WORKSPACE_SIZE
@@ -108,8 +107,8 @@
fun testMigration() {
// Src Hotseat icons
addItem(ITEM_TYPE_APPLICATION, 0, CONTAINER_HOTSEAT, 0, 0, testPackage1, 1, TMP_TABLE)
- addItem(ITEM_TYPE_SHORTCUT, 1, CONTAINER_HOTSEAT, 0, 0, testPackage2, 2, TMP_TABLE)
- addItem(ITEM_TYPE_SHORTCUT, 3, CONTAINER_HOTSEAT, 0, 0, testPackage3, 3, TMP_TABLE)
+ addItem(ITEM_TYPE_DEEP_SHORTCUT, 1, CONTAINER_HOTSEAT, 0, 0, testPackage2, 2, TMP_TABLE)
+ addItem(ITEM_TYPE_DEEP_SHORTCUT, 3, CONTAINER_HOTSEAT, 0, 0, testPackage3, 3, TMP_TABLE)
addItem(ITEM_TYPE_APPLICATION, 4, CONTAINER_HOTSEAT, 0, 0, testPackage4, 4, TMP_TABLE)
// Src grid icons
// _ _ _ _ _
@@ -124,7 +123,7 @@
addItem(ITEM_TYPE_APPLICATION, 0, CONTAINER_DESKTOP, 4, 3, testPackage9, 9, TMP_TABLE)
// Dest hotseat icons
- addItem(ITEM_TYPE_SHORTCUT, 1, CONTAINER_HOTSEAT, 0, 0, testPackage2)
+ addItem(ITEM_TYPE_DEEP_SHORTCUT, 1, CONTAINER_HOTSEAT, 0, 0, testPackage2)
// Dest grid icons
addItem(ITEM_TYPE_APPLICATION, 0, CONTAINER_DESKTOP, 2, 2, testPackage10)
@@ -219,8 +218,8 @@
// Hotseat items in grid A
// 1 2 _ 3 4
addItem(ITEM_TYPE_APPLICATION, 0, CONTAINER_HOTSEAT, 0, 0, testPackage1, 1, TMP_TABLE)
- addItem(ITEM_TYPE_SHORTCUT, 1, CONTAINER_HOTSEAT, 0, 0, testPackage2, 2, TMP_TABLE)
- addItem(ITEM_TYPE_SHORTCUT, 3, CONTAINER_HOTSEAT, 0, 0, testPackage3, 3, TMP_TABLE)
+ addItem(ITEM_TYPE_DEEP_SHORTCUT, 1, CONTAINER_HOTSEAT, 0, 0, testPackage2, 2, TMP_TABLE)
+ addItem(ITEM_TYPE_DEEP_SHORTCUT, 3, CONTAINER_HOTSEAT, 0, 0, testPackage3, 3, TMP_TABLE)
addItem(ITEM_TYPE_APPLICATION, 4, CONTAINER_HOTSEAT, 0, 0, testPackage4, 4, TMP_TABLE)
// Workspace items in grid A
// _ _ _ _ _
@@ -235,7 +234,7 @@
// Hotseat items in grid B
// 2 _ _ _
- addItem(ITEM_TYPE_SHORTCUT, 0, CONTAINER_HOTSEAT, 0, 0, testPackage2)
+ addItem(ITEM_TYPE_DEEP_SHORTCUT, 0, CONTAINER_HOTSEAT, 0, 0, testPackage2)
// Workspace items in grid B
// _ _ _ _
// _ _ _ 10
@@ -291,7 +290,7 @@
null
)
?: throw IllegalStateException()
- var locMap = parseLocMap(context, c)
+ var locMap = parseLocMap(c)
// Expected items in grid B
// _ _ _ _
// 5 6 7 8
@@ -348,7 +347,7 @@
null
)
?: throw IllegalStateException()
- locMap = parseLocMap(context, c)
+ locMap = parseLocMap(c)
// Expected workspace items in grid A
// _ _ _ _ _
// _ _ _ _ 5
@@ -410,7 +409,7 @@
null
)
?: throw IllegalStateException()
- locMap = parseLocMap(context, c)
+ locMap = parseLocMap(c)
// Expected workspace items in grid B
// _ _ _ _
// 5 6 _ 8
@@ -436,7 +435,7 @@
c.close()
}
- private fun parseLocMap(context: Context, c: Cursor): Map<String, Triple<Int, Int, Int>> {
+ private fun parseLocMap(c: Cursor): Map<String, Triple<Int, Int, Int>> {
// Check workspace items
val intentIndex = c.getColumnIndex(INTENT)
val screenIndex = c.getColumnIndex(SCREEN)
@@ -465,7 +464,16 @@
1,
TMP_TABLE
),
- addItem(ITEM_TYPE_SHORTCUT, 1, CONTAINER_HOTSEAT, 0, 0, testPackage2, 2, TMP_TABLE),
+ addItem(
+ ITEM_TYPE_DEEP_SHORTCUT,
+ 1,
+ CONTAINER_HOTSEAT,
+ 0,
+ 0,
+ testPackage2,
+ 2,
+ TMP_TABLE
+ ),
addItem(
ITEM_TYPE_APPLICATION,
2,
@@ -476,7 +484,16 @@
3,
TMP_TABLE
),
- addItem(ITEM_TYPE_SHORTCUT, 3, CONTAINER_HOTSEAT, 0, 0, testPackage4, 4, TMP_TABLE)
+ addItem(
+ ITEM_TYPE_DEEP_SHORTCUT,
+ 3,
+ CONTAINER_HOTSEAT,
+ 0,
+ 0,
+ testPackage4,
+ 4,
+ TMP_TABLE
+ )
)
val numSrcDatabaseHotseatIcons = srcHotseatItems.size
idp.numDatabaseHotseatIcons = 6
@@ -532,9 +549,9 @@
@Test
fun migrateFromLargerHotseat() {
addItem(ITEM_TYPE_APPLICATION, 0, CONTAINER_HOTSEAT, 0, 0, testPackage1, 1, TMP_TABLE)
- addItem(ITEM_TYPE_SHORTCUT, 2, CONTAINER_HOTSEAT, 0, 0, testPackage2, 2, TMP_TABLE)
+ addItem(ITEM_TYPE_DEEP_SHORTCUT, 2, CONTAINER_HOTSEAT, 0, 0, testPackage2, 2, TMP_TABLE)
addItem(ITEM_TYPE_APPLICATION, 3, CONTAINER_HOTSEAT, 0, 0, testPackage3, 3, TMP_TABLE)
- addItem(ITEM_TYPE_SHORTCUT, 4, CONTAINER_HOTSEAT, 0, 0, testPackage4, 4, TMP_TABLE)
+ addItem(ITEM_TYPE_DEEP_SHORTCUT, 4, CONTAINER_HOTSEAT, 0, 0, testPackage4, 4, TMP_TABLE)
addItem(ITEM_TYPE_APPLICATION, 5, CONTAINER_HOTSEAT, 0, 0, testPackage5, 5, TMP_TABLE)
idp.numDatabaseHotseatIcons = 4
diff --git a/tests/src/com/android/launcher3/model/LoaderCursorTest.java b/tests/src/com/android/launcher3/model/LoaderCursorTest.java
index 78812c0..544ed6b 100644
--- a/tests/src/com/android/launcher3/model/LoaderCursorTest.java
+++ b/tests/src/com/android/launcher3/model/LoaderCursorTest.java
@@ -30,7 +30,7 @@
import static com.android.launcher3.LauncherSettings.Favorites.INTENT;
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE;
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
-import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
+import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT;
import static com.android.launcher3.LauncherSettings.Favorites.OPTIONS;
import static com.android.launcher3.LauncherSettings.Favorites.PROFILE_ID;
import static com.android.launcher3.LauncherSettings.Favorites.RANK;
@@ -158,13 +158,13 @@
@Test
public void loadSimpleShortcut() {
- initCursor(ITEM_TYPE_SHORTCUT, "my-shortcut");
+ initCursor(ITEM_TYPE_DEEP_SHORTCUT, "my-shortcut");
assertTrue(mLoaderCursor.moveToNext());
WorkspaceItemInfo info = mLoaderCursor.loadSimpleWorkspaceItem();
assertTrue(mApp.getIconCache().isDefaultIcon(info.bitmap, info.user));
assertEquals("my-shortcut", info.title);
- assertEquals(ITEM_TYPE_SHORTCUT, info.itemType);
+ assertEquals(ITEM_TYPE_DEEP_SHORTCUT, info.itemType);
}
@Test
diff --git a/tests/src/com/android/launcher3/model/PackageInstallStateChangedTaskTest.java b/tests/src/com/android/launcher3/model/PackageInstallStateChangedTaskTest.java
index 519191e..4ba61ac 100644
--- a/tests/src/com/android/launcher3/model/PackageInstallStateChangedTaskTest.java
+++ b/tests/src/com/android/launcher3/model/PackageInstallStateChangedTaskTest.java
@@ -1,5 +1,12 @@
package com.android.launcher3.model;
+import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
+import static com.android.launcher3.util.LauncherModelHelper.TEST_ACTIVITY;
+import static com.android.launcher3.util.LauncherModelHelper.TEST_ACTIVITY2;
+import static com.android.launcher3.util.LauncherModelHelper.TEST_ACTIVITY3;
+import static com.android.launcher3.util.LauncherModelHelper.TEST_PACKAGE;
+import static com.android.launcher3.util.TestUtil.runOnExecutorSync;
+
import static org.junit.Assert.assertEquals;
import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -9,6 +16,8 @@
import com.android.launcher3.model.data.LauncherAppWidgetInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.pm.PackageInstallInfo;
+import com.android.launcher3.util.IntSet;
+import com.android.launcher3.util.LauncherLayoutBuilder;
import com.android.launcher3.util.LauncherModelHelper;
import org.junit.After;
@@ -16,9 +25,6 @@
import org.junit.Test;
import org.junit.runner.RunWith;
-import java.util.Arrays;
-import java.util.HashSet;
-
/**
* Tests for {@link PackageInstallStateChangedTask}
*/
@@ -26,12 +32,36 @@
@RunWith(AndroidJUnit4.class)
public class PackageInstallStateChangedTaskTest {
+ private static final String PENDING_APP_1 = TEST_PACKAGE + ".pending1";
+ private static final String PENDING_APP_2 = TEST_PACKAGE + ".pending2";
+
private LauncherModelHelper mModelHelper;
+ private IntSet mDownloadingApps;
@Before
public void setup() throws Exception {
mModelHelper = new LauncherModelHelper();
- mModelHelper.initializeData("package_install_state_change_task_data");
+ mModelHelper.createInstallerSession(PENDING_APP_1);
+ mModelHelper.createInstallerSession(PENDING_APP_2);
+
+ LauncherLayoutBuilder builder = new LauncherLayoutBuilder()
+ .atWorkspace(0, 0, 1).putApp(TEST_PACKAGE, TEST_ACTIVITY) // 1
+ .atWorkspace(0, 0, 2).putApp(TEST_PACKAGE, TEST_ACTIVITY2) // 2
+ .atWorkspace(0, 0, 3).putApp(TEST_PACKAGE, TEST_ACTIVITY3) // 3
+
+ .atWorkspace(0, 0, 4).putApp(PENDING_APP_1, TEST_ACTIVITY) // 4
+ .atWorkspace(0, 0, 5).putApp(PENDING_APP_1, TEST_ACTIVITY2) // 5
+ .atWorkspace(0, 0, 6).putApp(PENDING_APP_1, TEST_ACTIVITY3) // 6
+ .atWorkspace(0, 0, 7).putWidget(PENDING_APP_1, "pending.widget", 1, 1) // 7
+
+ .atWorkspace(0, 0, 8).putApp(PENDING_APP_2, TEST_ACTIVITY) // 8
+ .atWorkspace(0, 0, 9).putApp(PENDING_APP_2, TEST_ACTIVITY2) // 9
+ .atWorkspace(0, 0, 10).putApp(PENDING_APP_2, TEST_ACTIVITY3); // 10
+
+ mDownloadingApps = IntSet.wrap(4, 5, 6, 7, 8, 9, 10);
+ mModelHelper.setupDefaultLayoutProvider(builder);
+ mModelHelper.loadModelSync();
+ assertEquals(10, mModelHelper.getBgDataModel().itemsIdMap.size());
}
@After
@@ -47,36 +77,45 @@
}
@Test
- public void testSessionUpdate_ignore_installed() throws Exception {
- mModelHelper.executeTaskForTest(newTask("app1", 30));
+ public void testSessionUpdate_ignore_installed() {
+ // Run on model executor so that no other task runs in the middle.
+ runOnExecutorSync(MODEL_EXECUTOR, () -> {
+ mModelHelper.getModel().enqueueModelUpdateTask(newTask(TEST_PACKAGE, 30));
- // No shortcuts were updated
- verifyProgressUpdate(0);
+ // No shortcuts were updated
+ verifyProgressUpdate(0);
+ });
}
@Test
- public void testSessionUpdate_shortcuts_updated() throws Exception {
- mModelHelper.executeTaskForTest(newTask("app3", 30));
+ public void testSessionUpdate_shortcuts_updated() {
+ // Run on model executor so that no other task runs in the middle.
+ runOnExecutorSync(MODEL_EXECUTOR, () -> {
+ mModelHelper.getModel().enqueueModelUpdateTask(newTask(PENDING_APP_1, 30));
- verifyProgressUpdate(30, 5, 6, 7);
+ verifyProgressUpdate(30, 4, 5, 6, 7);
+ });
}
@Test
- public void testSessionUpdate_widgets_updated() throws Exception {
- mModelHelper.executeTaskForTest(newTask("app4", 30));
+ public void testSessionUpdate_widgets_updated() {
+ // Run on model executor so that no other task runs in the middle.
+ runOnExecutorSync(MODEL_EXECUTOR, () -> {
+ mModelHelper.getModel().enqueueModelUpdateTask(newTask(PENDING_APP_2, 30));
- verifyProgressUpdate(30, 8, 9);
+ verifyProgressUpdate(30, 8, 9, 10);
+ });
}
- private void verifyProgressUpdate(int progress, Integer... idsUpdated) {
- HashSet<Integer> updates = new HashSet<>(Arrays.asList(idsUpdated));
+ private void verifyProgressUpdate(int progress, int... idsUpdated) {
+ IntSet updates = IntSet.wrap(idsUpdated);
for (ItemInfo info : mModelHelper.getBgDataModel().itemsIdMap) {
- if (info instanceof WorkspaceItemInfo) {
- assertEquals(updates.contains(info.id) ? progress: 100,
- ((WorkspaceItemInfo) info).getProgressLevel());
+ int expectedProgress = updates.contains(info.id) ? progress
+ : (mDownloadingApps.contains(info.id) ? 0 : 100);
+ if (info instanceof WorkspaceItemInfo wi) {
+ assertEquals(expectedProgress, wi.getProgressLevel());
} else {
- assertEquals(updates.contains(info.id) ? progress: -1,
- ((LauncherAppWidgetInfo) info).installProgress);
+ assertEquals(expectedProgress, ((LauncherAppWidgetInfo) info).installProgress);
}
}
}
diff --git a/tests/src/com/android/launcher3/util/DisplayControllerTest.kt b/tests/src/com/android/launcher3/util/DisplayControllerTest.kt
new file mode 100644
index 0000000..8e4e998
--- /dev/null
+++ b/tests/src/com/android/launcher3/util/DisplayControllerTest.kt
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.util
+
+import android.content.Context
+import android.content.res.Configuration
+import android.content.res.Resources
+import android.graphics.Point
+import android.graphics.Rect
+import android.hardware.display.DisplayManager
+import android.util.ArrayMap
+import android.util.DisplayMetrics
+import android.view.Display
+import android.view.Surface
+import androidx.test.annotation.UiThreadTest
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.launcher3.LauncherPrefs
+import com.android.launcher3.util.DisplayController.CHANGE_DENSITY
+import com.android.launcher3.util.DisplayController.CHANGE_ROTATION
+import com.android.launcher3.util.DisplayController.DisplayInfoChangeListener
+import com.android.launcher3.util.MainThreadInitializedObject.SandboxContext
+import com.android.launcher3.util.window.CachedDisplayInfo
+import com.android.launcher3.util.window.WindowManagerProxy
+import kotlin.math.min
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.doNothing
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when` as whenever
+import org.mockito.MockitoAnnotations
+import org.mockito.stubbing.Answer
+
+/** Unit tests for {@link DisplayController} */
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class DisplayControllerTest {
+
+ private val appContext: Context = ApplicationProvider.getApplicationContext()
+
+ @Mock private lateinit var context: SandboxContext
+ @Mock private lateinit var windowManagerProxy: WindowManagerProxy
+ @Mock private lateinit var launcherPrefs: LauncherPrefs
+ @Mock private lateinit var displayManager: DisplayManager
+ @Mock private lateinit var display: Display
+ @Mock private lateinit var resources: Resources
+ @Mock private lateinit var displayInfoChangeListener: DisplayInfoChangeListener
+
+ private lateinit var displayController: DisplayController
+
+ private val width = 2208
+ private val height = 1840
+ private val inset = 110
+ private val densityDpi = 420
+ private val density = densityDpi / DisplayMetrics.DENSITY_DEFAULT.toFloat()
+ private val bounds =
+ arrayOf(
+ WindowBounds(Rect(0, 0, width, height), Rect(0, inset, 0, 0), Surface.ROTATION_0),
+ WindowBounds(Rect(0, 0, height, width), Rect(0, inset, 0, 0), Surface.ROTATION_90),
+ WindowBounds(Rect(0, 0, width, height), Rect(0, inset, 0, 0), Surface.ROTATION_180),
+ WindowBounds(Rect(0, 0, height, width), Rect(0, inset, 0, 0), Surface.ROTATION_270)
+ )
+ private val configuration =
+ Configuration(appContext.resources.configuration).apply {
+ densityDpi = this@DisplayControllerTest.densityDpi
+ screenWidthDp = (bounds[0].bounds.width() / density).toInt()
+ screenHeightDp = (bounds[0].bounds.height() / density).toInt()
+ smallestScreenWidthDp = min(screenWidthDp, screenHeightDp)
+ }
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ whenever(context.getObject(eq(WindowManagerProxy.INSTANCE))).thenReturn(windowManagerProxy)
+ whenever(context.getObject(eq(LauncherPrefs.INSTANCE))).thenReturn(launcherPrefs)
+
+ // Mock WindowManagerProxy
+ val displayInfo =
+ CachedDisplayInfo(Point(width, height), Surface.ROTATION_0, Rect(0, 0, 0, 0))
+ whenever(windowManagerProxy.getDisplayInfo(any())).thenReturn(displayInfo)
+ whenever(windowManagerProxy.estimateInternalDisplayBounds(any()))
+ .thenAnswer(
+ Answer {
+ // Always create a new copy of bounds
+ val perDisplayBounds = ArrayMap<CachedDisplayInfo, List<WindowBounds>>()
+ perDisplayBounds[displayInfo] = bounds.toList()
+ return@Answer perDisplayBounds
+ }
+ )
+ whenever(windowManagerProxy.getRealBounds(any(), any())).thenAnswer { i ->
+ bounds[i.getArgument<CachedDisplayInfo>(1).rotation]
+ }
+
+ // Mock context
+ whenever(context.createWindowContext(any(), any(), nullable())).thenReturn(context)
+ whenever(context.getSystemService(eq(DisplayManager::class.java)))
+ .thenReturn(displayManager)
+ doNothing().`when`(context).registerComponentCallbacks(any())
+
+ // Mock display
+ whenever(display.rotation).thenReturn(displayInfo.rotation)
+ whenever(context.display).thenReturn(display)
+ whenever(displayManager.getDisplay(any())).thenReturn(display)
+
+ // Mock resources
+ whenever(resources.configuration).thenReturn(configuration)
+ whenever(context.resources).thenReturn(resources)
+
+ // Initialize DisplayController
+ displayController = DisplayController(context)
+ displayController.addChangeListener(displayInfoChangeListener)
+ }
+
+ @Test
+ @UiThreadTest
+ fun testRotation() {
+ val displayInfo =
+ CachedDisplayInfo(Point(height, width), Surface.ROTATION_90, Rect(0, 0, 0, 0))
+ whenever(windowManagerProxy.getDisplayInfo(any())).thenReturn(displayInfo)
+ whenever(display.rotation).thenReturn(displayInfo.rotation)
+ val configuration =
+ Configuration(configuration).apply {
+ screenWidthDp = configuration.screenHeightDp
+ screenHeightDp = configuration.screenWidthDp
+ }
+ whenever(resources.configuration).thenReturn(configuration)
+
+ displayController.onConfigurationChanged(configuration)
+
+ verify(displayInfoChangeListener).onDisplayInfoChanged(any(), any(), eq(CHANGE_ROTATION))
+ }
+
+ @Test
+ @UiThreadTest
+ fun testFontScale() {
+ val configuration = Configuration(configuration).apply { fontScale = 1.2f }
+ whenever(resources.configuration).thenReturn(configuration)
+
+ displayController.onConfigurationChanged(configuration)
+
+ verify(displayInfoChangeListener).onDisplayInfoChanged(any(), any(), eq(CHANGE_DENSITY))
+ }
+}
diff --git a/tests/src/com/android/launcher3/util/LauncherModelHelper.java b/tests/src/com/android/launcher3/util/LauncherModelHelper.java
index 976afcd..9dc04a1 100644
--- a/tests/src/com/android/launcher3/util/LauncherModelHelper.java
+++ b/tests/src/com/android/launcher3/util/LauncherModelHelper.java
@@ -15,34 +15,31 @@
*/
package com.android.launcher3.util;
-import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
+import static android.content.pm.PackageInstaller.SessionParams.MODE_FULL_INSTALL;
+
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
-import static com.android.launcher3.LauncherSettings.Favorites.CONTENT_URI;
+import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
+import static com.android.launcher3.util.TestUtil.runOnExecutorSync;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.verify;
-import android.content.ComponentName;
import android.content.ContentProvider;
import android.content.ContentResolver;
-import android.content.ContentValues;
-import android.content.Context;
-import android.content.Intent;
+import android.content.pm.PackageInstaller;
+import android.content.pm.PackageInstaller.SessionParams;
import android.content.pm.PackageManager;
import android.content.pm.ProviderInfo;
-import android.content.res.Resources;
-import android.database.sqlite.SQLiteDatabase;
+import android.graphics.Bitmap;
+import android.graphics.Bitmap.Config;
+import android.graphics.Color;
import android.net.Uri;
import android.os.ParcelFileDescriptor;
import android.os.ParcelFileDescriptor.AutoCloseOutputStream;
-import android.os.Process;
import android.provider.Settings;
import android.test.mock.MockContentResolver;
import android.util.ArrayMap;
@@ -57,13 +54,10 @@
import com.android.launcher3.LauncherModel.ModelUpdateTask;
import com.android.launcher3.LauncherPrefs;
import com.android.launcher3.LauncherProvider;
-import com.android.launcher3.LauncherSettings;
import com.android.launcher3.model.AllAppsList;
import com.android.launcher3.model.BgDataModel;
import com.android.launcher3.model.BgDataModel.Callbacks;
import com.android.launcher3.model.ItemInstallQueue;
-import com.android.launcher3.model.data.AppInfo;
-import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.pm.InstallSessionHelper;
import com.android.launcher3.pm.UserCache;
import com.android.launcher3.testing.TestInformationProvider;
@@ -72,37 +66,25 @@
import com.android.launcher3.util.window.WindowManagerProxy;
import com.android.launcher3.widget.custom.CustomWidgetManager;
-import org.mockito.ArgumentCaptor;
-
-import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
-import java.io.InputStreamReader;
+import java.io.IOException;
import java.io.OutputStreamWriter;
-import java.lang.reflect.Field;
-import java.util.HashMap;
-import java.util.List;
import java.util.UUID;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
-import java.util.function.Function;
/**
* Utility class to help manage Launcher Model and related objects for test.
*/
public class LauncherModelHelper {
- public static final int DESKTOP = LauncherSettings.Favorites.CONTAINER_DESKTOP;
- public static final int HOTSEAT = LauncherSettings.Favorites.CONTAINER_HOTSEAT;
-
- public static final int APP_ICON = LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
- public static final int SHORTCUT = LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
- public static final int NO__ICON = -1;
-
- public static final String TEST_PACKAGE = testContext().getPackageName();
+ public static final String TEST_PACKAGE = getInstrumentation().getContext().getPackageName();
public static final String TEST_ACTIVITY = "com.android.launcher3.tests.Activity2";
+ public static final String TEST_ACTIVITY2 = "com.android.launcher3.tests.Activity3";
+ public static final String TEST_ACTIVITY3 = "com.android.launcher3.tests.Activity4";
// Authority for providing a test default-workspace-layout data.
private static final String TEST_PROVIDER_AUTHORITY =
@@ -110,39 +92,18 @@
private static final int DEFAULT_BITMAP_SIZE = 10;
private static final int DEFAULT_GRID_SIZE = 4;
- private final HashMap<Class, HashMap<String, Field>> mFieldCache = new HashMap<>();
- private final MockContentResolver mMockResolver = new MockContentResolver();
- public final TestLauncherProvider provider;
public final SandboxModelContext sandboxContext;
- public final long defaultProfileId;
+ private final RunnableList mDestroyTask = new RunnableList();
private BgDataModel mDataModel;
- private AllAppsList mAllAppsList;
public LauncherModelHelper() {
- Context context = getApplicationContext();
- // System settings cache content provider. Ensure that they are statically initialized
- Settings.Secure.getString(context.getContentResolver(), "test");
- Settings.System.getString(context.getContentResolver(), "test");
- Settings.Global.getString(context.getContentResolver(), "test");
-
- provider = new TestLauncherProvider();
sandboxContext = new SandboxModelContext();
- defaultProfileId = UserCache.INSTANCE.get(sandboxContext)
- .getSerialNumberForUser(Process.myUserHandle());
- setupProvider(LauncherProvider.AUTHORITY, provider);
}
public void setupProvider(String authority, ContentProvider provider) {
- ProviderInfo providerInfo = new ProviderInfo();
- providerInfo.authority = authority;
- providerInfo.applicationInfo = sandboxContext.getApplicationInfo();
- provider.attachInfo(sandboxContext, providerInfo);
- mMockResolver.addProvider(providerInfo.authority, provider);
- doReturn(providerInfo)
- .when(sandboxContext.mPm)
- .resolveContentProvider(eq(authority), anyInt());
+ sandboxContext.setupProvider(authority, provider);
}
public LauncherModel getModel() {
@@ -151,16 +112,35 @@
public synchronized BgDataModel getBgDataModel() {
if (mDataModel == null) {
- mDataModel = ReflectionHelpers.getField(getModel(), "mBgDataModel");
+ getModel().enqueueModelUpdateTask(new ModelUpdateTask() {
+ @Override
+ public void init(@NonNull LauncherAppState app, @NonNull LauncherModel model,
+ @NonNull BgDataModel dataModel, @NonNull AllAppsList allAppsList,
+ @NonNull Executor uiExecutor) {
+ mDataModel = dataModel;
+ }
+
+ @Override
+ public void run() { }
+ });
}
return mDataModel;
}
- public synchronized AllAppsList getAllAppsList() {
- if (mAllAppsList == null) {
- mAllAppsList = ReflectionHelpers.getField(getModel(), "mBgAllAppsList");
- }
- return mAllAppsList;
+ /**
+ * Creates a installer session for the provided package.
+ */
+ public int createInstallerSession(String pkg) throws IOException {
+ SessionParams sp = new SessionParams(MODE_FULL_INSTALL);
+ sp.setAppPackageName(pkg);
+ Bitmap icon = Bitmap.createBitmap(100, 100, Config.ARGB_8888);
+ icon.eraseColor(Color.RED);
+ sp.setAppIcon(icon);
+ sp.setAppLabel(pkg);
+ PackageInstaller pi = sandboxContext.getPackageManager().getPackageInstaller();
+ int sessionId = pi.createSession(sp);
+ mDestroyTask.add(() -> pi.abandonSession(sessionId));
+ return sessionId;
}
public void destroy() {
@@ -175,6 +155,8 @@
waitOrThrow(l1);
sandboxContext.onDestroy();
l2.countDown();
+
+ mDestroyTask.executeAllAndDestroy();
}
private void waitOrThrow(CountDownLatch latch) {
@@ -186,184 +168,6 @@
}
/**
- * Synchronously executes the task and returns all the UI callbacks posted.
- */
- public List<Runnable> executeTaskForTest(ModelUpdateTask task) throws Exception {
- LauncherModel model = getModel();
- if (!model.isModelLoaded()) {
- ReflectionHelpers.setField(model, "mModelLoaded", true);
- }
- Executor mockExecutor = mock(Executor.class);
- model.enqueueModelUpdateTask(new ModelUpdateTask() {
- @Override
- public void init(@NonNull final LauncherAppState app,
- @NonNull final LauncherModel model, @NonNull final BgDataModel dataModel,
- @NonNull final AllAppsList allAppsList, @NonNull final Executor uiExecutor) {
- task.init(app, model, dataModel, allAppsList, mockExecutor);
- }
-
- @Override
- public void run() {
- task.run();
- }
- });
- MODEL_EXECUTOR.submit(() -> null).get();
-
- ArgumentCaptor<Runnable> captor = ArgumentCaptor.forClass(Runnable.class);
- verify(mockExecutor, atLeast(0)).execute(captor.capture());
- return captor.getAllValues();
- }
-
- /**
- * Synchronously executes a task on the model
- */
- public <T> T executeSimpleTask(Function<BgDataModel, T> task) throws Exception {
- BgDataModel dataModel = getBgDataModel();
- return MODEL_EXECUTOR.submit(() -> task.apply(dataModel)).get();
- }
-
- /**
- * Initializes mock data for the test.
- */
- public void initializeData(String resourceName) throws Exception {
- BgDataModel bgDataModel = getBgDataModel();
- AllAppsList allAppsList = getAllAppsList();
-
- MODEL_EXECUTOR.submit(() -> {
- // Copy apk from resources to a local file and install from there.
- Resources resources = testContext().getResources();
- int resId = resources.getIdentifier(
- resourceName, "raw", testContext().getPackageName());
- try (BufferedReader reader = new BufferedReader(new InputStreamReader(
- resources.openRawResource(resId)))) {
- String line;
- HashMap<String, Class> classMap = new HashMap<>();
- while ((line = reader.readLine()) != null) {
- line = line.trim();
- if (line.startsWith("#") || line.isEmpty()) {
- continue;
- }
- String[] commands = line.split(" ");
- switch (commands[0]) {
- case "classMap":
- classMap.put(commands[1], Class.forName(commands[2]));
- break;
- case "bgItem":
- bgDataModel.addItem(sandboxContext,
- (ItemInfo) initItem(classMap.get(commands[1]), commands, 2),
- false);
- break;
- case "allApps":
- allAppsList.add((AppInfo) initItem(AppInfo.class, commands, 1), null);
- break;
- }
- }
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- }).get();
- }
-
- private Object initItem(Class clazz, String[] fieldDef, int startIndex) throws Exception {
- HashMap<String, Field> cache = mFieldCache.get(clazz);
- if (cache == null) {
- cache = new HashMap<>();
- Class c = clazz;
- while (c != null) {
- for (Field f : c.getDeclaredFields()) {
- f.setAccessible(true);
- cache.put(f.getName(), f);
- }
- c = c.getSuperclass();
- }
- mFieldCache.put(clazz, cache);
- }
-
- Object item = clazz.newInstance();
- for (int i = startIndex; i < fieldDef.length; i++) {
- String[] fieldData = fieldDef[i].split("=", 2);
- Field f = cache.get(fieldData[0]);
- Class type = f.getType();
- if (type == int.class || type == long.class) {
- f.set(item, Integer.parseInt(fieldData[1]));
- } else if (type == CharSequence.class || type == String.class) {
- f.set(item, fieldData[1]);
- } else if (type == Intent.class) {
- if (!fieldData[1].startsWith("#Intent")) {
- fieldData[1] = "#Intent;" + fieldData[1] + ";end";
- }
- f.set(item, Intent.parseUri(fieldData[1], 0));
- } else if (type == ComponentName.class) {
- f.set(item, ComponentName.unflattenFromString(fieldData[1]));
- } else {
- throw new Exception("Added parsing logic for "
- + f.getName() + " of type " + f.getType());
- }
- }
- return item;
- }
-
- public int addItem(int type, int screen, int container, int x, int y) {
- return addItem(type, screen, container, x, y, defaultProfileId, TEST_PACKAGE);
- }
-
- public int addItem(int type, int screen, int container, int x, int y, long profileId) {
- return addItem(type, screen, container, x, y, profileId, TEST_PACKAGE);
- }
-
- public int addItem(int type, int screen, int container, int x, int y, String packageName) {
- return addItem(type, screen, container, x, y, defaultProfileId, packageName);
- }
-
- public int addItem(int type, int screen, int container, int x, int y, String packageName,
- int id, Uri contentUri) {
- addItem(type, screen, container, x, y, defaultProfileId, packageName, id, contentUri);
- return id;
- }
-
- /**
- * Adds a mock item in the DB.
- * @param type {@link #APP_ICON} or {@link #SHORTCUT} or >= 2 for
- * folder (where the type represents the number of items in the folder).
- */
- public int addItem(int type, int screen, int container, int x, int y, long profileId,
- String packageName) {
- int id = LauncherSettings.Settings.call(sandboxContext.getContentResolver(),
- LauncherSettings.Settings.METHOD_NEW_ITEM_ID)
- .getInt(LauncherSettings.Settings.EXTRA_VALUE);
- addItem(type, screen, container, x, y, profileId, packageName, id, CONTENT_URI);
- return id;
- }
-
- public void addItem(int type, int screen, int container, int x, int y, long profileId,
- String packageName, int id, Uri contentUri) {
- ContentValues values = new ContentValues();
- values.put(LauncherSettings.Favorites._ID, id);
- values.put(LauncherSettings.Favorites.CONTAINER, container);
- values.put(LauncherSettings.Favorites.SCREEN, screen);
- values.put(LauncherSettings.Favorites.CELLX, x);
- values.put(LauncherSettings.Favorites.CELLY, y);
- values.put(LauncherSettings.Favorites.SPANX, 1);
- values.put(LauncherSettings.Favorites.SPANY, 1);
- values.put(LauncherSettings.Favorites.PROFILE_ID, profileId);
-
- if (type == APP_ICON || type == SHORTCUT) {
- values.put(LauncherSettings.Favorites.ITEM_TYPE, type);
- values.put(LauncherSettings.Favorites.INTENT,
- new Intent(Intent.ACTION_MAIN).setPackage(packageName).toUri(0));
- } else {
- values.put(LauncherSettings.Favorites.ITEM_TYPE,
- LauncherSettings.Favorites.ITEM_TYPE_FOLDER);
- // Add folder items.
- for (int i = 0; i < type; i++) {
- addItem(APP_ICON, 0, id, 0, 0, profileId);
- }
- }
-
- sandboxContext.getContentResolver().insert(contentUri, values);
- }
-
- /**
* Sets up a mock provider to load the provided layout by default, next time the layout loads
*/
public LauncherModelHelper setupDefaultLayoutProvider(LauncherLayoutBuilder builder)
@@ -394,6 +198,9 @@
}
};
setupProvider(TEST_PROVIDER_AUTHORITY, cp);
+ mDestroyTask.add(() -> runOnExecutorSync(MODEL_EXECUTOR, () ->
+ UiDevice.getInstance(getInstrumentation()).executeShellCommand(
+ "settings delete secure launcher3.layout.provider")));
return this;
}
@@ -402,46 +209,16 @@
*/
public void loadModelSync() throws ExecutionException, InterruptedException {
Callbacks mockCb = new Callbacks() { };
- Executors.MAIN_EXECUTOR.submit(() -> getModel().addCallbacksAndLoad(mockCb)).get();
+ MAIN_EXECUTOR.submit(() -> getModel().addCallbacksAndLoad(mockCb)).get();
Executors.MODEL_EXECUTOR.submit(() -> { }).get();
- Executors.MAIN_EXECUTOR.submit(() -> { }).get();
- Executors.MAIN_EXECUTOR.submit(() -> getModel().removeCallbacks(mockCb)).get();
+ MAIN_EXECUTOR.submit(() -> { }).get();
+ MAIN_EXECUTOR.submit(() -> getModel().removeCallbacks(mockCb)).get();
}
- /**
- * An extension of LauncherProvider backed up by in-memory database.
- */
- public static class TestLauncherProvider extends LauncherProvider {
+ public static class SandboxModelContext extends SandboxContext {
- @Override
- public boolean onCreate() {
- return true;
- }
-
- public SQLiteDatabase getDb() {
- return getModelDbController().getDb();
- }
- }
-
- public static boolean deleteContents(File dir) {
- File[] files = dir.listFiles();
- boolean success = true;
- if (files != null) {
- for (File file : files) {
- if (file.isDirectory()) {
- success &= deleteContents(file);
- }
- if (!file.delete()) {
- success = false;
- }
- }
- }
- return success;
- }
-
- public class SandboxModelContext extends SandboxContext {
-
+ private final MockContentResolver mMockResolver = new MockContentResolver();
private final ArrayMap<String, Object> mSpiedServices = new ArrayMap<>();
private final PackageManager mPm;
private final File mDbDir;
@@ -453,8 +230,31 @@
DisplayController.INSTANCE, CustomWidgetManager.INSTANCE,
SettingsCache.INSTANCE, PluginManagerWrapper.INSTANCE, LockedUserState.INSTANCE,
ItemInstallQueue.INSTANCE, WindowManagerProxy.INSTANCE);
+
+ // 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");
+
mPm = spy(getBaseContext().getPackageManager());
mDbDir = new File(getCacheDir(), UUID.randomUUID().toString());
+ setupProvider(LauncherProvider.AUTHORITY, new LauncherProvider() {
+ @Override
+ public boolean onCreate() {
+ return true;
+ }
+ });
+ }
+
+ @Override
+ protected <T> T createObject(MainThreadInitializedObject<T> object) {
+ if (object == LauncherAppState.INSTANCE) {
+ return (T) new LauncherAppState(this, null /* iconCacheFileName */);
+ }
+ return super.createObject(object);
}
public SandboxModelContext allow(MainThreadInitializedObject object) {
@@ -505,9 +305,30 @@
mSpiedServices.put(name, result);
return result;
}
- }
- private static Context testContext() {
- return getInstrumentation().getContext();
+ public void setupProvider(String authority, ContentProvider provider) {
+ ProviderInfo providerInfo = new ProviderInfo();
+ providerInfo.authority = authority;
+ providerInfo.applicationInfo = getApplicationInfo();
+ provider.attachInfo(this, providerInfo);
+ mMockResolver.addProvider(providerInfo.authority, provider);
+ doReturn(providerInfo).when(mPm).resolveContentProvider(eq(authority), anyInt());
+ }
+
+ private static boolean deleteContents(File dir) {
+ File[] files = dir.listFiles();
+ boolean success = true;
+ if (files != null) {
+ for (File file : files) {
+ if (file.isDirectory()) {
+ success &= deleteContents(file);
+ }
+ if (!file.delete()) {
+ success = false;
+ }
+ }
+ }
+ return success;
+ }
}
}
diff --git a/tests/src/com/android/launcher3/util/TestUtil.java b/tests/src/com/android/launcher3/util/TestUtil.java
index 4981795..e2c1fde 100644
--- a/tests/src/com/android/launcher3/util/TestUtil.java
+++ b/tests/src/com/android/launcher3/util/TestUtil.java
@@ -53,6 +53,7 @@
import java.io.OutputStream;
import java.security.MessageDigest;
import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutorService;
import java.util.function.Predicate;
import java.util.function.ToIntFunction;
@@ -154,6 +155,30 @@
Settings.Secure.putString(context.getContentResolver(), LAYOUT_DIGEST_KEY, null);
}
+ /**
+ * Utility method to run a task synchronously which converts any exceptions to RuntimeException
+ */
+ public static void runOnExecutorSync(ExecutorService executor, UncheckedRunnable task) {
+ try {
+ executor.submit(() -> {
+ try {
+ task.run();
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }).get();
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /** Interface to indicate a runnable which can throw any exception. */
+ public interface UncheckedRunnable {
+ /** Method to run the task */
+ void run() throws Exception;
+ }
+
+
private static class PackageInstallCheck extends LauncherApps.Callback
implements AutoCloseable {