Merge "Remving SurfaceControlCompat"
diff --git a/quickstep/res/values/strings.xml b/quickstep/res/values/strings.xml
index 59997e9..9604a77 100644
--- a/quickstep/res/values/strings.xml
+++ b/quickstep/res/values/strings.xml
@@ -39,6 +39,9 @@
<!-- Accessibility title for the list of recent apps [CHAR_LIMIT=none] -->
<string name="accessibility_recent_apps">Recent apps</string>
+ <!-- Accessibility confirmation for task closed -->
+ <string name="task_view_closed">Task Closed</string>
+
<!-- Accessibility title for an app card in Recents for apps that have time limit set
[CHAR_LIMIT=none] -->
<string name="task_contents_description_with_remaining_time"><xliff:g id="task_description" example="GMail">%1$s</xliff:g>, <xliff:g id="remaining_time" example="7 minutes left today">%2$s</xliff:g></string>
diff --git a/quickstep/src/com/android/launcher3/statehandlers/DepthController.java b/quickstep/src/com/android/launcher3/statehandlers/DepthController.java
index e1d89a1..3242d42 100644
--- a/quickstep/src/com/android/launcher3/statehandlers/DepthController.java
+++ b/quickstep/src/com/android/launcher3/statehandlers/DepthController.java
@@ -289,9 +289,8 @@
if (Float.compare(mDepth, depthF) == 0) {
return;
}
- if (dispatchTransactionSurface(depthF)) {
- mDepth = depthF;
- }
+ dispatchTransactionSurface(depthF);
+ mDepth = depthF;
}
public void onOverlayScrollChanged(float progress) {
diff --git a/quickstep/src/com/android/quickstep/GestureState.java b/quickstep/src/com/android/quickstep/GestureState.java
index 01f6e23..ed0623d 100644
--- a/quickstep/src/com/android/quickstep/GestureState.java
+++ b/quickstep/src/com/android/quickstep/GestureState.java
@@ -20,6 +20,7 @@
import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_OVERVIEW;
import static com.android.quickstep.MultiStateCallback.DEBUG_STATES;
+import android.annotation.Nullable;
import android.annotation.TargetApi;
import android.app.ActivityManager;
import android.content.Intent;
@@ -397,11 +398,15 @@
* while STATE_RECENTS_ANIMATION_CANCELED state is being set, and the caller is responsible for
* calling {@link RecentsAnimationController#cleanupScreenshot()}.
*/
+ @Nullable
HashMap<Integer, ThumbnailData> consumeRecentsAnimationCanceledSnapshot() {
- HashMap<Integer, ThumbnailData> data =
- new HashMap<Integer, ThumbnailData>(mRecentsAnimationCanceledSnapshots);
- mRecentsAnimationCanceledSnapshots = null;
- return data;
+ if (mRecentsAnimationCanceledSnapshots != null) {
+ HashMap<Integer, ThumbnailData> data =
+ new HashMap<Integer, ThumbnailData>(mRecentsAnimationCanceledSnapshots);
+ mRecentsAnimationCanceledSnapshots = null;
+ return data;
+ }
+ return null;
}
void setSwipeUpStartTimeMs(long uptimeMs) {
diff --git a/quickstep/src/com/android/quickstep/SystemUiProxy.java b/quickstep/src/com/android/quickstep/SystemUiProxy.java
index 8a923bf..b428b67 100644
--- a/quickstep/src/com/android/quickstep/SystemUiProxy.java
+++ b/quickstep/src/com/android/quickstep/SystemUiProxy.java
@@ -34,7 +34,6 @@
import android.os.RemoteException;
import android.os.UserHandle;
import android.util.Log;
-import android.util.Slog;
import android.view.MotionEvent;
import android.view.RemoteAnimationAdapter;
import android.view.RemoteAnimationTarget;
@@ -772,10 +771,8 @@
public ArrayList<GroupedRecentTaskInfo> getRecentTasks(int numTasks, int userId) {
if (mRecentTasks != null) {
try {
- final GroupedRecentTaskInfo[] tasks = mRecentTasks.getRecentTasks(numTasks,
- RECENT_IGNORE_UNAVAILABLE, userId);
- Log.d("b/206648922", "getRecentTasks(" + numTasks + "): result=" + tasks);
- return new ArrayList<>(Arrays.asList(tasks));
+ return new ArrayList<>(Arrays.asList(mRecentTasks.getRecentTasks(numTasks,
+ RECENT_IGNORE_UNAVAILABLE, userId)));
} catch (RemoteException e) {
Log.w(TAG, "Failed call getRecentTasks", e);
}
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index f0e57c9..02261af 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -1144,17 +1144,6 @@
// Reset the running task when leaving overview since it can still have a reference to
// its thumbnail
mTmpRunningTasks = null;
- // Remove grouped tasks and recycle once we exit overview
- int taskCount = getTaskViewCount();
- for (int i = 0; i < taskCount; i++) {
- View v = getTaskViewAt(i);
- if (!(v instanceof GroupedTaskView)) {
- return;
- }
- GroupedTaskView gtv = (GroupedTaskView) v;
- gtv.onTaskListVisibilityChanged(false);
- removeView(gtv);
- }
mSplitBoundsConfig = null;
}
updateLocusId();
@@ -2185,17 +2174,14 @@
*/
protected boolean shouldAddStubTaskView(RunningTaskInfo[] runningTaskInfos) {
if (runningTaskInfos.length > 1) {
- // * Always create new view for GroupedTaskView
- // * Remove existing associated taskViews for tasks currently in split
- for (RunningTaskInfo rti : runningTaskInfos) {
- TaskView taskView = getTaskViewByTaskId(rti.taskId);
- if (taskView == null) {
- continue;
- }
- taskView.onTaskListVisibilityChanged(false);
- removeView(taskView);
- }
- return true;
+ TaskView primaryTaskView = getTaskViewByTaskId(runningTaskInfos[0].taskId);
+ TaskView secondaryTaskView = getTaskViewByTaskId(runningTaskInfos[1].taskId);
+ int leftTopTaskViewId =
+ (primaryTaskView == null) ? -1 : primaryTaskView.getTaskViewId();
+ int rightBottomTaskViewId =
+ (secondaryTaskView == null) ? -1 : secondaryTaskView.getTaskViewId();
+ // Add a new stub view if both taskIds don't match any taskViews
+ return leftTopTaskViewId != rightBottomTaskViewId || leftTopTaskViewId == -1;
}
RunningTaskInfo runningTaskInfo = runningTaskInfos[0];
return runningTaskInfo != null && getTaskViewByTaskId(runningTaskInfo.taskId) == null;
@@ -2246,7 +2232,7 @@
measure(makeMeasureSpec(getMeasuredWidth(), EXACTLY),
makeMeasureSpec(getMeasuredHeight(), EXACTLY));
layout(getLeft(), getTop(), getRight(), getBottom());
- } else if (!needGroupTaskView && getTaskViewByTaskId(taskInfo.taskId) != null) {
+ } else if (getTaskViewByTaskId(taskInfo.taskId) != null) {
runningTaskViewId = getTaskViewByTaskId(taskInfo.taskId).getTaskViewId();
}
@@ -2797,6 +2783,8 @@
}
}
+ announceForAccessibility(getResources().getString(R.string.task_view_closed));
+
float dismissTranslationInterpolationEnd = 1;
boolean closeGapBetweenClearAll = false;
boolean isClearAllHidden = isClearAllHidden();
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java
index 3e0eb91..2a442a7 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskView.java
@@ -16,6 +16,7 @@
package com.android.quickstep.views;
+import static android.view.Display.DEFAULT_DISPLAY;
import static android.widget.Toast.LENGTH_SHORT;
import static com.android.launcher3.AbstractFloatingView.TYPE_TASK_MENU;
@@ -51,6 +52,7 @@
import android.util.AttributeSet;
import android.util.FloatProperty;
import android.util.Log;
+import android.view.Display;
import android.view.MotionEvent;
import android.view.TouchDelegate;
import android.view.View;
@@ -638,6 +640,7 @@
// If the recents animation is cancelled somehow between the parent if block and
// here, try to launch the task as a non live tile task.
launchTaskAnimated();
+ mIsClickableAsLiveTile = true;
return;
}
@@ -659,6 +662,9 @@
@Override
public void onAnimationEnd(Animator animator) {
+ if (mTask != null && mTask.key.displayId != getRootViewDisplayId()) {
+ launchTaskAnimated();
+ }
mIsClickableAsLiveTile = true;
}
});
@@ -1519,7 +1525,8 @@
private int getRootViewDisplayId() {
- return getRootView().getDisplay().getDisplayId();
+ Display display = getRootView().getDisplay();
+ return display != null ? display.getDisplayId() : DEFAULT_DISPLAY;
}
/**
diff --git a/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java b/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java
index 318f04e..1cb7529 100644
--- a/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java
+++ b/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java
@@ -20,9 +20,7 @@
import static com.android.quickstep.NavigationModeSwitchRule.Mode.ALL;
import static com.android.quickstep.NavigationModeSwitchRule.Mode.THREE_BUTTON;
-import static com.android.quickstep.NavigationModeSwitchRule.Mode.TWO_BUTTON;
import static com.android.quickstep.NavigationModeSwitchRule.Mode.ZERO_BUTTON;
-import static com.android.systemui.shared.system.QuickStepContract.NAV_BAR_MODE_2BUTTON_OVERLAY;
import static com.android.systemui.shared.system.QuickStepContract.NAV_BAR_MODE_3BUTTON_OVERLAY;
import static com.android.systemui.shared.system.QuickStepContract.NAV_BAR_MODE_GESTURAL_OVERLAY;
@@ -61,7 +59,7 @@
public static final int WAIT_TIME_MS = 10000;
public enum Mode {
- THREE_BUTTON, TWO_BUTTON, ZERO_BUTTON, ALL
+ THREE_BUTTON, ZERO_BUTTON, ALL
}
// Annotation for tests that need to be run with quickstep enabled and disabled.
@@ -99,9 +97,6 @@
if (mode == ZERO_BUTTON || mode == ALL) {
evaluateWithZeroButtons();
}
- if (mode == TWO_BUTTON || mode == ALL) {
- evaluateWithTwoButtons();
- }
if (mode == THREE_BUTTON || mode == ALL) {
evaluateWithThreeButtons();
}
@@ -123,13 +118,6 @@
}
}
- private void evaluateWithTwoButtons() throws Throwable {
- if (setActiveOverlay(mLauncher, NAV_BAR_MODE_2BUTTON_OVERLAY,
- LauncherInstrumentation.NavigationModel.TWO_BUTTON, description)) {
- base.evaluate();
- }
- }
-
private void evaluateWithZeroButtons() throws Throwable {
if (setActiveOverlay(mLauncher, NAV_BAR_MODE_GESTURAL_OVERLAY,
LauncherInstrumentation.NavigationModel.ZERO_BUTTON, description)) {
@@ -145,9 +133,7 @@
public static String getCurrentOverlayPackage(int currentInteractionMode) {
return QuickStepContract.isGesturalMode(currentInteractionMode)
? NAV_BAR_MODE_GESTURAL_OVERLAY
- : QuickStepContract.isSwipeUpMode(currentInteractionMode)
- ? NAV_BAR_MODE_2BUTTON_OVERLAY
- : NAV_BAR_MODE_3BUTTON_OVERLAY;
+ : NAV_BAR_MODE_3BUTTON_OVERLAY;
}
private static LauncherInstrumentation.NavigationModel currentSysUiNavigationMode() {
diff --git a/quickstep/tests/src/com/android/quickstep/StartLauncherViaGestureTests.java b/quickstep/tests/src/com/android/quickstep/StartLauncherViaGestureTests.java
index f44a812..75d057e 100644
--- a/quickstep/tests/src/com/android/quickstep/StartLauncherViaGestureTests.java
+++ b/quickstep/tests/src/com/android/quickstep/StartLauncherViaGestureTests.java
@@ -16,23 +16,16 @@
package com.android.quickstep;
-import static com.android.launcher3.util.RaceConditionReproducer.enterEvt;
-import static com.android.launcher3.util.RaceConditionReproducer.exitEvt;
-
import android.content.Intent;
import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
-import com.android.launcher3.Launcher;
import com.android.launcher3.ui.TaplTestsLauncher3;
import com.android.launcher3.util.RaceConditionReproducer;
-import com.android.quickstep.NavigationModeSwitchRule.Mode;
import com.android.quickstep.NavigationModeSwitchRule.NavigationModeSwitch;
-import com.android.quickstep.inputconsumers.OtherActivityInputConsumer;
import org.junit.Before;
-import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -66,21 +59,6 @@
}
@Test
- @Ignore // Ignoring until race condition repro framework is changes for multi-process case.
- @NavigationModeSwitch(mode = Mode.TWO_BUTTON)
- public void testPressHome() {
- runTest(enterEvt(Launcher.ON_CREATE_EVT),
- exitEvt(Launcher.ON_CREATE_EVT),
- enterEvt(OtherActivityInputConsumer.DOWN_EVT),
- exitEvt(OtherActivityInputConsumer.DOWN_EVT));
-
- runTest(enterEvt(OtherActivityInputConsumer.DOWN_EVT),
- exitEvt(OtherActivityInputConsumer.DOWN_EVT),
- enterEvt(Launcher.ON_CREATE_EVT),
- exitEvt(Launcher.ON_CREATE_EVT));
- }
-
- @Test
@NavigationModeSwitch
public void testStressPressHome() {
for (int i = 0; i < STRESS_REPEAT_COUNT; ++i) {
diff --git a/res/raw/downgrade_schema.json b/res/raw/downgrade_schema.json
index 14eac9f..b8d0c6f 100644
--- a/res/raw/downgrade_schema.json
+++ b/res/raw/downgrade_schema.json
@@ -2,8 +2,9 @@
// Note: Comments are not supported in JSON schema, but android parser is lenient.
// Maximum DB version supported by this schema
- "version" : 30,
+ "version" : 31,
+ "downgrade_to_30" : [],
"downgrade_to_29" : [],
"downgrade_to_28" : [
"ALTER TABLE favorites RENAME TO temp_favorites;",
diff --git a/res/xml/backupscheme.xml b/res/xml/backupscheme.xml
index 299e92e..0f0dde2 100644
--- a/res/xml/backupscheme.xml
+++ b/res/xml/backupscheme.xml
@@ -2,6 +2,11 @@
<full-backup-content xmlns:android="http://schemas.android.com/apk/res/android">
<include domain="database" path="launcher.db" />
+ <include domain="database" path="launcher_6_by_5.db" />
+ <include domain="database" path="launcher_4_by_5.db" />
+ <include domain="database" path="launcher_4_by_4.db" />
+ <include domain="database" path="launcher_3_by_3.db" />
+ <include domain="database" path="launcher_2_by_2.db" />
<include domain="sharedpref" path="com.android.launcher3.prefs.xml" />
<include domain="file" path="downgrade_schema.json" />
diff --git a/res/xml/grayscale_icon_map.xml b/res/xml/grayscale_icon_map.xml
new file mode 100644
index 0000000..f6383ce
--- /dev/null
+++ b/res/xml/grayscale_icon_map.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2022 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<icons></icons>
diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java
index 10023b4..4501159 100644
--- a/src/com/android/launcher3/LauncherAppState.java
+++ b/src/com/android/launcher3/LauncherAppState.java
@@ -36,6 +36,7 @@
import com.android.launcher3.graphics.IconShape;
import com.android.launcher3.icons.IconCache;
import com.android.launcher3.icons.IconProvider;
+import com.android.launcher3.icons.LauncherIconProvider;
import com.android.launcher3.icons.LauncherIcons;
import com.android.launcher3.notification.NotificationListener;
import com.android.launcher3.pm.InstallSessionHelper;
@@ -61,7 +62,7 @@
private final Context mContext;
private final LauncherModel mModel;
- private final IconProvider mIconProvider;
+ private final LauncherIconProvider mIconProvider;
private final IconCache mIconCache;
private final InvariantDeviceProfile mInvariantDeviceProfile;
private final RunnableList mOnTerminateCallback = new RunnableList();
@@ -98,7 +99,7 @@
Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE,
Intent.ACTION_MANAGED_PROFILE_UNLOCKED);
if (FeatureFlags.IS_STUDIO_BUILD) {
- modelChangeReceiver.register(mContext, ACTION_FORCE_ROLOAD);
+ modelChangeReceiver.register(mContext, Context.RECEIVER_EXPORTED, ACTION_FORCE_ROLOAD);
}
mOnTerminateCallback.add(() -> mContext.unregisterReceiver(modelChangeReceiver));
@@ -138,7 +139,7 @@
mContext = context;
mInvariantDeviceProfile = InvariantDeviceProfile.INSTANCE.get(context);
- mIconProvider = new IconProvider(context, Themes.isThemedIconEnabled(context));
+ mIconProvider = new LauncherIconProvider(context);
mIconCache = new IconCache(mContext, mInvariantDeviceProfile,
iconCacheFileName, mIconProvider);
mModel = new LauncherModel(context, this, mIconCache, new AppFilter(mContext),
diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java
index 68e19cb..e004a4b 100644
--- a/src/com/android/launcher3/LauncherProvider.java
+++ b/src/com/android/launcher3/LauncherProvider.java
@@ -97,7 +97,7 @@
* Represents the schema of the database. Changes in scheme need not be backwards compatible.
* When increasing the scheme version, ensure that downgrade_schema.json is updated
*/
- public static final int SCHEMA_VERSION = 30;
+ public static final int SCHEMA_VERSION = 31;
public static final String AUTHORITY = BuildConfig.APPLICATION_ID + ".settings";
public static final String KEY_LAYOUT_PROVIDER_AUTHORITY = "KEY_LAYOUT_PROVIDER_AUTHORITY";
@@ -864,6 +864,19 @@
Favorites.SCREEN, IntArray.wrap(-777, -778)), null);
}
case 30: {
+ if (FeatureFlags.QSB_ON_FIRST_SCREEN) {
+ // Clean up first row in screen 0 as it might contain junk data.
+ Log.d(TAG, "Cleaning up first row");
+ db.delete(Favorites.TABLE_NAME,
+ String.format(Locale.ENGLISH,
+ "%1$s = %2$d AND %3$s = %4$d AND %5$s = %6$d",
+ Favorites.SCREEN, 0,
+ Favorites.CONTAINER, Favorites.CONTAINER_DESKTOP,
+ Favorites.CELLY, 0), null);
+ }
+ return;
+ }
+ case 31: {
// DB Upgraded successfully
return;
}
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index ac194d2..b062b91 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -76,9 +76,8 @@
import com.android.launcher3.dragndrop.FolderAdaptiveIcon;
import com.android.launcher3.graphics.GridCustomizationsProvider;
import com.android.launcher3.graphics.TintedDrawableSpan;
-import com.android.launcher3.icons.BitmapInfo;
-import com.android.launcher3.icons.FastBitmapDrawable;
import com.android.launcher3.icons.ShortcutCachingLogic;
+import com.android.launcher3.icons.ThemedIconDrawable;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.ItemInfoWithIcon;
import com.android.launcher3.model.data.SearchActionItemInfo;
@@ -87,6 +86,7 @@
import com.android.launcher3.shortcuts.ShortcutRequest;
import com.android.launcher3.util.IntArray;
import com.android.launcher3.util.PackageManagerHelper;
+import com.android.launcher3.util.Themes;
import com.android.launcher3.views.ActivityContext;
import com.android.launcher3.views.BaseDragLayer;
import com.android.launcher3.widget.PendingAddShortcutInfo;
@@ -123,8 +123,9 @@
public static final boolean ATLEAST_R = Build.VERSION.SDK_INT >= Build.VERSION_CODES.R;
- public static final boolean ATLEAST_S = BuildCompat.isAtLeastS()
- || Build.VERSION.SDK_INT >= Build.VERSION_CODES.S;
+ public static final boolean ATLEAST_S = Build.VERSION.SDK_INT >= Build.VERSION_CODES.S;
+
+ public static final boolean ATLEAST_T = BuildCompat.isAtLeastT();
/**
* Set on a motion event dispatched from the nav bar. See {@link MotionEvent#setEdgeFlags(int)}.
@@ -672,11 +673,19 @@
* @param outObj this is set to the internal data associated with {@param info},
* eg {@link LauncherActivityInfo} or {@link ShortcutInfo}.
*/
+ @TargetApi(Build.VERSION_CODES.TIRAMISU)
public static Drawable getFullDrawable(Context context, ItemInfo info, int width, int height,
Object[] outObj) {
Drawable icon = loadFullDrawableWithoutTheme(context, info, width, height, outObj);
- if (icon instanceof BitmapInfo.Extender) {
- icon = ((BitmapInfo.Extender) icon).getThemedDrawable(context);
+ if (ATLEAST_T && icon instanceof AdaptiveIconDrawable) {
+ AdaptiveIconDrawable aid = (AdaptiveIconDrawable) icon.mutate();
+ Drawable mono = aid.getMonochrome();
+ if (mono != null && Themes.isThemedIconEnabled(context)) {
+ int[] colors = ThemedIconDrawable.getColors(context);
+ mono = mono.mutate();
+ mono.setTint(colors[1]);
+ return new AdaptiveIconDrawable(new ColorDrawable(colors[0]), mono);
+ }
}
return icon;
}
@@ -719,8 +728,7 @@
return icon;
} else if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_SEARCH_ACTION
&& info instanceof SearchActionItemInfo) {
- return new AdaptiveIconDrawable(
- new FastBitmapDrawable(((SearchActionItemInfo) info).bitmap), null);
+ return ((SearchActionItemInfo) info).bitmap.newIcon(context);
} else {
return null;
}
diff --git a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
index f4d64df..88ce57e 100644
--- a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
+++ b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
@@ -133,23 +133,25 @@
mObjectMap.put(InvariantDeviceProfile.INSTANCE, idp);
mObjectMap.put(LauncherAppState.INSTANCE,
new LauncherAppState(this, null /* iconCacheFileName */));
-
}
- public LauncherIcons newLauncherIcons(Context context, boolean shapeDetection) {
+ /**
+ * Creates a new LauncherIcons for the preview, skipping the global pool
+ */
+ public LauncherIcons newLauncherIcons(Context context) {
LauncherIconsForPreview launcherIconsForPreview = mIconPool.poll();
if (launcherIconsForPreview != null) {
return launcherIconsForPreview;
}
return new LauncherIconsForPreview(context, mIdp.fillResIconDpi, mIdp.iconBitmapSize,
- -1 /* poolId */, shapeDetection);
+ -1 /* poolId */);
}
private final class LauncherIconsForPreview extends LauncherIcons {
private LauncherIconsForPreview(Context context, int fillResIconDpi, int iconBitmapSize,
- int poolId, boolean shapeDetection) {
- super(context, fillResIconDpi, iconBitmapSize, poolId, shapeDetection);
+ int poolId) {
+ super(context, fillResIconDpi, iconBitmapSize, poolId);
}
@Override
diff --git a/src/com/android/launcher3/icons/LauncherIconProvider.java b/src/com/android/launcher3/icons/LauncherIconProvider.java
new file mode 100644
index 0000000..a7379bb
--- /dev/null
+++ b/src/com/android/launcher3/icons/LauncherIconProvider.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.icons;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.content.res.XmlResourceParser;
+import android.text.TextUtils;
+import android.util.ArrayMap;
+import android.util.Log;
+
+import com.android.launcher3.R;
+import com.android.launcher3.util.Themes;
+
+import org.xmlpull.v1.XmlPullParser;
+
+import java.util.Collections;
+import java.util.Map;
+
+/**
+ * Extension of {@link IconProvider} with support for overriding theme icons
+ */
+public class LauncherIconProvider extends IconProvider {
+
+ private static final String TAG_ICON = "icon";
+ private static final String ATTR_PACKAGE = "package";
+ private static final String ATTR_DRAWABLE = "drawable";
+
+ private static final String TAG = "LIconProvider";
+ private static final Map<String, ThemeData> DISABLED_MAP = Collections.emptyMap();
+
+ private Map<String, ThemeData> mThemedIconMap;
+ private boolean mSupportsIconTheme;
+
+ public LauncherIconProvider(Context context) {
+ super(context);
+ setIconThemeSupported(Themes.isThemedIconEnabled(context));
+ }
+
+ /**
+ * Enables or disables icon theme support
+ */
+ public void setIconThemeSupported(boolean isSupported) {
+ mSupportsIconTheme = isSupported;
+ mThemedIconMap = isSupported ? null : DISABLED_MAP;
+ }
+
+ @Override
+ protected ThemeData getThemeDataForPackage(String packageName) {
+ return getThemedIconMap().get(packageName);
+ }
+
+ @Override
+ public String getSystemIconState() {
+ return super.getSystemIconState() + (mSupportsIconTheme ? ",with-theme" : ",no-theme");
+ }
+
+ private Map<String, ThemeData> getThemedIconMap() {
+ if (mThemedIconMap != null) {
+ return mThemedIconMap;
+ }
+ ArrayMap<String, ThemeData> map = new ArrayMap<>();
+ Resources res = mContext.getResources();
+ try (XmlResourceParser parser = res.getXml(R.xml.grayscale_icon_map)) {
+ final int depth = parser.getDepth();
+ int type;
+ while ((type = parser.next()) != XmlPullParser.START_TAG
+ && type != XmlPullParser.END_DOCUMENT);
+
+ while (((type = parser.next()) != XmlPullParser.END_TAG
+ || parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {
+ if (type != XmlPullParser.START_TAG) {
+ continue;
+ }
+ if (TAG_ICON.equals(parser.getName())) {
+ String pkg = parser.getAttributeValue(null, ATTR_PACKAGE);
+ int iconId = parser.getAttributeResourceValue(null, ATTR_DRAWABLE, 0);
+ if (iconId != 0 && !TextUtils.isEmpty(pkg)) {
+ map.put(pkg, new ThemeData(res, iconId));
+ }
+ }
+ }
+ } catch (Exception e) {
+ Log.e(TAG, "Unable to parse icon map", e);
+ }
+ mThemedIconMap = map;
+ return mThemedIconMap;
+ }
+}
diff --git a/src/com/android/launcher3/icons/LauncherIcons.java b/src/com/android/launcher3/icons/LauncherIcons.java
index bf7897e..5508c49 100644
--- a/src/com/android/launcher3/icons/LauncherIcons.java
+++ b/src/com/android/launcher3/icons/LauncherIcons.java
@@ -21,6 +21,7 @@
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.graphics.IconShape;
import com.android.launcher3.graphics.LauncherPreviewRenderer;
+import com.android.launcher3.util.Themes;
/**
* Wrapper class to provide access to {@link BaseIconFactory} and also to provide pool of this class
@@ -32,18 +33,13 @@
private static LauncherIcons sPool;
private static int sPoolId = 0;
- public static LauncherIcons obtain(Context context) {
- return obtain(context, IconShape.getShape().enableShapeDetection());
- }
-
/**
* Return a new Message instance from the global pool. Allows us to
* avoid allocating new objects in many cases.
*/
- public static LauncherIcons obtain(Context context, boolean shapeDetection) {
+ public static LauncherIcons obtain(Context context) {
if (context instanceof LauncherPreviewRenderer.PreviewContext) {
- return ((LauncherPreviewRenderer.PreviewContext) context).newLauncherIcons(context,
- shapeDetection);
+ return ((LauncherPreviewRenderer.PreviewContext) context).newLauncherIcons(context);
}
int poolId;
@@ -58,8 +54,7 @@
}
InvariantDeviceProfile idp = InvariantDeviceProfile.INSTANCE.get(context);
- return new LauncherIcons(context, idp.fillResIconDpi, idp.iconBitmapSize, poolId,
- shapeDetection);
+ return new LauncherIcons(context, idp.fillResIconDpi, idp.iconBitmapSize, poolId);
}
public static void clearPool() {
@@ -73,9 +68,9 @@
private LauncherIcons next;
- protected LauncherIcons(Context context, int fillResIconDpi, int iconBitmapSize, int poolId,
- boolean shapeDetection) {
- super(context, fillResIconDpi, iconBitmapSize, shapeDetection);
+ protected LauncherIcons(Context context, int fillResIconDpi, int iconBitmapSize, int poolId) {
+ super(context, fillResIconDpi, iconBitmapSize, IconShape.getShape().enableShapeDetection());
+ mMonoIconEnabled = Themes.isThemedIconEnabled(context);
mPoolId = poolId;
}
diff --git a/src/com/android/launcher3/model/LoaderCursor.java b/src/com/android/launcher3/model/LoaderCursor.java
index 08b38e8..739a64a 100644
--- a/src/com/android/launcher3/model/LoaderCursor.java
+++ b/src/com/android/launcher3/model/LoaderCursor.java
@@ -457,11 +457,13 @@
if (!occupied.containsKey(item.screenId)) {
GridOccupancy screen = new GridOccupancy(countX + 1, countY + 1);
- if (item.screenId == Workspace.FIRST_SCREEN_ID) {
- // Mark the first row as occupied (if the feature is enabled)
- // in order to account for the QSB.
+ if (item.screenId == Workspace.FIRST_SCREEN_ID && FeatureFlags.QSB_ON_FIRST_SCREEN) {
+ // Mark the first X columns (X is width of the search container) in the first row as
+ // occupied (if the feature is enabled) in order to account for the search
+ // container.
+ int spanX = mIDP.numSearchContainerColumns;
int spanY = FeatureFlags.EXPANDED_SMARTSPACE.get() ? 2 : 1;
- screen.markCells(0, 0, countX + 1, spanY, FeatureFlags.QSB_ON_FIRST_SCREEN);
+ screen.markCells(0, 0, spanX, spanY, true);
}
occupied.put(item.screenId, screen);
}
diff --git a/src/com/android/launcher3/util/SimpleBroadcastReceiver.java b/src/com/android/launcher3/util/SimpleBroadcastReceiver.java
index 465a0e8..4dfa5cc 100644
--- a/src/com/android/launcher3/util/SimpleBroadcastReceiver.java
+++ b/src/com/android/launcher3/util/SimpleBroadcastReceiver.java
@@ -39,10 +39,17 @@
* Helper method to register multiple actions
*/
public void register(Context context, String... actions) {
+ register(context, 0, actions);
+ }
+
+ /**
+ * Helper method to register multiple actions with one or more {@code flags}.
+ */
+ public void register(Context context, int flags, String... actions) {
IntentFilter filter = new IntentFilter();
for (String action : actions) {
filter.addAction(action);
}
- context.registerReceiver(this, filter);
+ context.registerReceiver(this, filter, flags);
}
}
diff --git a/tests/Android.bp b/tests/Android.bp
index 3670c37..8def20f 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -32,6 +32,7 @@
srcs: [
"src/com/android/launcher3/ui/AbstractLauncherUiTest.java",
"src/com/android/launcher3/ui/PortraitLandscapeRunner.java",
+ "src/com/android/launcher3/util/TestUtil.java",
"src/com/android/launcher3/util/Wait.java",
"src/com/android/launcher3/util/WidgetUtils.java",
"src/com/android/launcher3/util/rule/FailureWatcher.java",
@@ -71,6 +72,11 @@
platform_apis: true,
}
+android_library {
+ name: "Launcher3TestResources",
+ resource_dirs: ["res"],
+}
+
android_test {
name: "Launcher3Tests",
srcs: [
diff --git a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
index 5b940a8..f193e24 100644
--- a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
+++ b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
@@ -38,6 +38,7 @@
import com.android.launcher3.tapl.FolderIcon;
import com.android.launcher3.tapl.Widgets;
import com.android.launcher3.tapl.Workspace;
+import com.android.launcher3.util.TestUtil;
import com.android.launcher3.widget.picker.WidgetsFullSheet;
import com.android.launcher3.widget.picker.WidgetsRecyclerView;
@@ -50,6 +51,7 @@
@RunWith(AndroidJUnit4.class)
public class TaplTestsLauncher3 extends AbstractLauncherUiTest {
private static final String APP_NAME = "LauncherTestApp";
+ private static final String DUMMY_APP_NAME = "Aardwolf";
@Before
public void setUp() throws Exception {
@@ -435,7 +437,49 @@
}
}
+ private void verifyAppUninstalledFromAllApps(Workspace workspace, String appName) {
+ final AllApps allApps = workspace.switchToAllApps();
+ allApps.freeze();
+ try {
+ assertNull(appName + " app was found on all apps after being uninstalled",
+ allApps.tryGetAppIcon(appName));
+ } finally {
+ allApps.unfreeze();
+ }
+ }
+
+ @Test
+ @PortraitLandscape
+ public void testUninstallFromWorkspace() throws Exception {
+ TestUtil.installDummyApp();
+ try {
+ verifyAppUninstalledFromAllApps(
+ createShortcutIfNotExist(DUMMY_APP_NAME).uninstall(), DUMMY_APP_NAME);
+ } finally {
+ TestUtil.uninstallDummyApp();
+ }
+ }
+
+ @Test
+ @PortraitLandscape
+ public void testUninstallFromAllApps() throws Exception {
+ TestUtil.installDummyApp();
+ try {
+ Workspace workspace = mLauncher.getWorkspace();
+ final AllApps allApps = workspace.switchToAllApps();
+ allApps.freeze();
+ try {
+ workspace = allApps.getAppIcon(DUMMY_APP_NAME).uninstall();
+ } finally {
+ allApps.unfreeze();
+ }
+ verifyAppUninstalledFromAllApps(workspace, DUMMY_APP_NAME);
+ } finally {
+ TestUtil.uninstallDummyApp();
+ }
+ }
+
public static String getAppPackageName() {
return getInstrumentation().getContext().getPackageName();
}
-}
+}
\ No newline at end of file
diff --git a/tests/tapl/com/android/launcher3/tapl/AddToHomeScreenPrompt.java b/tests/tapl/com/android/launcher3/tapl/AddToHomeScreenPrompt.java
index 0582bc9..98eb32e 100644
--- a/tests/tapl/com/android/launcher3/tapl/AddToHomeScreenPrompt.java
+++ b/tests/tapl/com/android/launcher3/tapl/AddToHomeScreenPrompt.java
@@ -22,8 +22,6 @@
import androidx.test.uiautomator.BySelector;
import androidx.test.uiautomator.UiObject2;
-import com.android.launcher3.testing.TestProtocol;
-
import java.util.regex.Pattern;
public class AddToHomeScreenPrompt {
@@ -44,19 +42,10 @@
public void addAutomatically() {
try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) {
- if (mLauncher.getNavigationModel()
- != LauncherInstrumentation.NavigationModel.THREE_BUTTON) {
- if (!mLauncher.isLauncher3()) {
- mLauncher.expectEvent(
- TestProtocol.SEQUENCE_TIS,
- LauncherInstrumentation.EVENT_TOUCH_DOWN_TIS);
- mLauncher.expectEvent(
- TestProtocol.SEQUENCE_TIS, LauncherInstrumentation.EVENT_TOUCH_UP_TIS);
- }
- }
- mLauncher.waitForObjectInContainer(
- mWidgetCell.getParent().getParent().getParent().getParent(),
- By.text(ADD_AUTOMATICALLY)).click();
+ mLauncher.clickObject(
+ mLauncher.waitForObjectInContainer(
+ mWidgetCell.getParent().getParent().getParent().getParent(),
+ By.text(ADD_AUTOMATICALLY)));
mLauncher.waitUntilLauncherObjectGone(getSelector());
}
}
diff --git a/tests/tapl/com/android/launcher3/tapl/AllApps.java b/tests/tapl/com/android/launcher3/tapl/AllApps.java
index 78301e4..c1b0220 100644
--- a/tests/tapl/com/android/launcher3/tapl/AllApps.java
+++ b/tests/tapl/com/android/launcher3/tapl/AllApps.java
@@ -22,6 +22,7 @@
import android.widget.TextView;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import androidx.test.uiautomator.By;
import androidx.test.uiautomator.BySelector;
import androidx.test.uiautomator.Direction;
@@ -51,8 +52,8 @@
mLauncher.waitForObjectInContainer(appListRecycler, By.clazz(TextView.class));
verifyNotFrozen("All apps freeze flags upon opening all apps");
mIconHeight = mLauncher.getTestInfo(
- TestProtocol.REQUEST_ICON_HEIGHT).
- getInt(TestProtocol.TEST_INFO_RESPONSE_FIELD);
+ TestProtocol.REQUEST_ICON_HEIGHT)
+ .getInt(TestProtocol.TEST_INFO_RESPONSE_FIELD);
}
@Override
@@ -98,14 +99,14 @@
}
/**
- * Finds an icon. Fails if the icon doesn't exist. Scrolls the app list when needed to make
- * sure the icon is visible.
+ * Finds an icon. If the icon doesn't exist, return null.
+ * Scrolls the app list when needed to make sure the icon is visible.
*
* @param appName name of the app.
- * @return The app.
+ * @return The app if found, and null if not found.
*/
- @NonNull
- public AppIcon getAppIcon(String appName) {
+ @Nullable
+ public AppIcon tryGetAppIcon(String appName) {
try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck();
LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
"getting app icon " + appName + " on all apps")) {
@@ -150,19 +151,34 @@
}
verifyActiveContainer();
}
-
// Ignore bottom offset selection here as there might not be any scroll more scroll
// region available.
- mLauncher.assertTrue("Unable to scroll to a clickable icon: " + appName,
- hasClickableIcon(allAppsContainer, appListRecycler, appIconSelector,
- deviceHeight));
+ if (hasClickableIcon(
+ allAppsContainer, appListRecycler, appIconSelector, deviceHeight)) {
- final UiObject2 appIcon = mLauncher.waitForObjectInContainer(appListRecycler,
- appIconSelector);
- return new AppIcon(mLauncher, appIcon);
+ final UiObject2 appIcon = mLauncher.waitForObjectInContainer(appListRecycler,
+ appIconSelector);
+ return new AllAppsAppIcon(mLauncher, appIcon);
+ } else {
+ return null;
+ }
}
}
+ /**
+ * Finds an icon. Fails if the icon doesn't exist. Scrolls the app list when needed to make
+ * sure the icon is visible.
+ *
+ * @param appName name of the app.
+ * @return The app.
+ */
+ @NonNull
+ public AppIcon getAppIcon(String appName) {
+ AppIcon appIcon = tryGetAppIcon(appName);
+ mLauncher.assertNotNull("Unable to scroll to a clickable icon: " + appName, appIcon);
+ return appIcon;
+ }
+
private void scrollBackToBeginning() {
try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
"want to scroll back in all apps")) {
@@ -195,7 +211,7 @@
private int getAllAppsScroll() {
return mLauncher.getTestInfo(
- TestProtocol.REQUEST_APPS_LIST_SCROLL_Y)
+ TestProtocol.REQUEST_APPS_LIST_SCROLL_Y)
.getInt(TestProtocol.TEST_INFO_RESPONSE_FIELD);
}
@@ -253,4 +269,4 @@
if (testInfo == null) return;
mLauncher.assertEquals(message, 0, testInfo.getInt(TestProtocol.TEST_INFO_RESPONSE_FIELD));
}
-}
+}
\ No newline at end of file
diff --git a/tests/tapl/com/android/launcher3/tapl/AllAppsAppIcon.java b/tests/tapl/com/android/launcher3/tapl/AllAppsAppIcon.java
new file mode 100644
index 0000000..03d387e
--- /dev/null
+++ b/tests/tapl/com/android/launcher3/tapl/AllAppsAppIcon.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.tapl;
+
+import androidx.test.uiautomator.UiObject2;
+
+import java.util.regex.Pattern;
+
+/**
+ * App icon in all apps.
+ */
+final class AllAppsAppIcon extends AppIcon {
+
+ private static final Pattern LONG_CLICK_EVENT = Pattern.compile("onAllAppsItemLongClick");
+
+ AllAppsAppIcon(LauncherInstrumentation launcher, UiObject2 icon) {
+ super(launcher, icon);
+ }
+
+ @Override
+ protected Pattern getLongClickEvent() {
+ return LONG_CLICK_EVENT;
+ }
+}
diff --git a/tests/tapl/com/android/launcher3/tapl/AppIcon.java b/tests/tapl/com/android/launcher3/tapl/AppIcon.java
index 6da59da..8fa9e12 100644
--- a/tests/tapl/com/android/launcher3/tapl/AppIcon.java
+++ b/tests/tapl/com/android/launcher3/tapl/AppIcon.java
@@ -32,9 +32,7 @@
/**
* App icon, whether in all apps or in workspace/
*/
-public final class AppIcon extends Launchable implements FolderDragTarget {
-
- private static final Pattern LONG_CLICK_EVENT = Pattern.compile("onAllAppsItemLongClick");
+public abstract class AppIcon extends Launchable implements FolderDragTarget {
AppIcon(LauncherInstrumentation launcher, UiObject2 icon) {
super(launcher, icon);
@@ -44,13 +42,15 @@
return By.clazz(TextView.class).text(appName).pkg(launcher.getLauncherPackageName());
}
+ protected abstract Pattern getLongClickEvent();
+
/**
* Long-clicks the icon to open its menu.
*/
public AppIconMenu openMenu() {
try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) {
return new AppIconMenu(mLauncher, mLauncher.clickAndGet(
- mObject, "popup_container", LONG_CLICK_EVENT));
+ mObject, "popup_container", getLongClickEvent()));
}
}
@@ -60,7 +60,7 @@
public AppIconMenu openDeepShortcutMenu() {
try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) {
return new AppIconMenu(mLauncher, mLauncher.clickAndGet(
- mObject, "deep_shortcuts_container", LONG_CLICK_EVENT));
+ mObject, "deep_shortcuts_container", getLongClickEvent()));
}
}
@@ -89,7 +89,7 @@
@Override
protected void addExpectedEventsForLongClick() {
- mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, LONG_CLICK_EVENT);
+ mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, getLongClickEvent());
}
@Override
@@ -122,4 +122,20 @@
}
return null;
}
-}
+
+ /**
+ * Uninstall the appIcon by dragging it to the 'uninstall' drop point of the drop_target_bar.
+ *
+ * @return validated workspace after the existing appIcon being uninstalled.
+ */
+ public Workspace uninstall() {
+ try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck();
+ LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
+ "uninstalling app icon")) {
+ return Workspace.uninstallAppIcon(
+ mLauncher, this,
+ this::addExpectedEventsForLongClick
+ );
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/tapl/com/android/launcher3/tapl/Background.java b/tests/tapl/com/android/launcher3/tapl/Background.java
index 4eaecca..d84d723 100644
--- a/tests/tapl/com/android/launcher3/tapl/Background.java
+++ b/tests/tapl/com/android/launcher3/tapl/Background.java
@@ -134,31 +134,6 @@
break;
}
- case TWO_BUTTON: {
- final int startX;
- final int startY;
- final int endX;
- final int endY;
- final int swipeLength = mLauncher.getTestInfo(getSwipeHeightRequestName()).
- getInt(TestProtocol.TEST_INFO_RESPONSE_FIELD) + mLauncher.getTouchSlop();
-
- if (mLauncher.getDevice().isNaturalOrientation()) {
- startX = endX = mLauncher.getDevice().getDisplayWidth() / 2;
- startY = getSwipeStartY();
- endY = startY - swipeLength;
- } else {
- startX = getSwipeStartX();
- // TODO(b/184059820) make horizontal swipe use swipe width not height, for the
- // moment just double the swipe length.
- endX = startX - swipeLength * 2;
- startY = endY = mLauncher.getDevice().getDisplayHeight() / 2;
- }
-
- mLauncher.swipeToState(startX, startY, endX, endY, 10, OVERVIEW_STATE_ORDINAL,
- LauncherInstrumentation.GestureScope.OUTSIDE_WITH_PILFER);
- break;
- }
-
case THREE_BUTTON:
if (mLauncher.isTablet()) {
mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN,
@@ -253,11 +228,7 @@
"want to quick switch to the previous app")) {
verifyActiveContainer();
final boolean launcherWasVisible = mLauncher.isLauncherVisible();
- boolean transposeInLandscape = false;
switch (mLauncher.getNavigationModel()) {
- case TWO_BUTTON:
- transposeInLandscape = true;
- // Fall through, zero button and two button modes behave the same.
case ZERO_BUTTON: {
final int startX;
final int startY;
@@ -265,33 +236,17 @@
final int endY;
final int cornerRadius = (int) Math.ceil(mLauncher.getWindowCornerRadius());
if (toRight) {
- if (mLauncher.getDevice().isNaturalOrientation() || !transposeInLandscape) {
- // Swipe from the bottom left to the bottom right of the screen.
- startX = cornerRadius;
- startY = getSwipeStartY();
- endX = mLauncher.getDevice().getDisplayWidth() - cornerRadius;
- endY = startY;
- } else {
- // Swipe from the bottom right to the top right of the screen.
- startX = getSwipeStartX();
- startY = mLauncher.getRealDisplaySize().y - 1 - cornerRadius;
- endX = startX;
- endY = cornerRadius;
- }
+ // Swipe from the bottom left to the bottom right of the screen.
+ startX = cornerRadius;
+ startY = getSwipeStartY();
+ endX = mLauncher.getDevice().getDisplayWidth() - cornerRadius;
+ endY = startY;
} else {
- if (mLauncher.getDevice().isNaturalOrientation() || !transposeInLandscape) {
- // Swipe from the bottom right to the bottom left of the screen.
- startX = mLauncher.getDevice().getDisplayWidth() - cornerRadius;
- startY = getSwipeStartY();
- endX = cornerRadius;
- endY = startY;
- } else {
- // Swipe from the bottom left to the top left of the screen.
- startX = getSwipeStartX();
- startY = cornerRadius;
- endX = startX;
- endY = mLauncher.getRealDisplaySize().y - 1 - cornerRadius;
- }
+ // Swipe from the bottom right to the bottom left of the screen.
+ startX = mLauncher.getDevice().getDisplayWidth() - cornerRadius;
+ startY = getSwipeStartY();
+ endX = cornerRadius;
+ endY = startY;
}
final boolean isZeroButton = mLauncher.getNavigationModel()
diff --git a/tests/tapl/com/android/launcher3/tapl/Folder.java b/tests/tapl/com/android/launcher3/tapl/Folder.java
index dba308d..f046b6e 100644
--- a/tests/tapl/com/android/launcher3/tapl/Folder.java
+++ b/tests/tapl/com/android/launcher3/tapl/Folder.java
@@ -43,7 +43,7 @@
public AppIcon getAppIcon(String appName) {
try (LauncherInstrumentation.Closable ignored = mLauncher.addContextLayer(
"Want to get app icon in folder")) {
- return new AppIcon(mLauncher,
+ return new WorkspaceAppIcon(mLauncher,
mLauncher.waitForObjectInContainer(
mContainer,
AppIcon.getAppIconSelector(appName, mLauncher)));
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index 46a7c71..f46921f 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -124,7 +124,7 @@
WORKSPACE, ALL_APPS, OVERVIEW, WIDGETS, BACKGROUND, FALLBACK_OVERVIEW
}
- public enum NavigationModel {ZERO_BUTTON, TWO_BUTTON, THREE_BUTTON}
+ public enum NavigationModel {ZERO_BUTTON, THREE_BUTTON}
// Where the gesture happens: outside of Launcher, inside or from inside to outside and
// whether the gesture recognition triggers pilfer.
@@ -391,8 +391,6 @@
public static NavigationModel getNavigationModel(int currentInteractionMode) {
if (QuickStepContract.isGesturalMode(currentInteractionMode)) {
return NavigationModel.ZERO_BUTTON;
- } else if (QuickStepContract.isSwipeUpMode(currentInteractionMode)) {
- return NavigationModel.TWO_BUTTON;
} else if (QuickStepContract.isLegacyMode(currentInteractionMode)) {
return NavigationModel.THREE_BUTTON;
}
@@ -864,7 +862,7 @@
swipeToState(
displaySize.x / 2, displaySize.y - 1,
- displaySize.x / 2, 0,
+ displaySize.x / 2, displaySize.y / 2,
ZERO_BUTTON_STEPS_FROM_BACKGROUND_TO_HOME, NORMAL_STATE_ORDINAL,
gestureStartFromLauncher ? GestureScope.INSIDE_TO_OUTSIDE
: GestureScope.OUTSIDE_WITH_PILFER);
@@ -873,10 +871,6 @@
log("Hierarchy before clicking home:");
dumpViewHierarchy();
action = "clicking home button";
- if (!isLauncher3() && getNavigationModel() == NavigationModel.TWO_BUTTON) {
- expectEvent(TestProtocol.SEQUENCE_TIS, EVENT_TOUCH_DOWN_TIS);
- expectEvent(TestProtocol.SEQUENCE_TIS, EVENT_TOUCH_UP_TIS);
- }
if (isTablet()) {
expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_TOUCH_DOWN);
expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_TOUCH_UP);
@@ -917,9 +911,6 @@
if (isTablet()) {
expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_TOUCH_DOWN);
expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_TOUCH_UP);
- } else if (!isLauncher3() && getNavigationModel() == NavigationModel.TWO_BUTTON) {
- expectEvent(TestProtocol.SEQUENCE_TIS, EVENT_TOUCH_DOWN_TIS);
- expectEvent(TestProtocol.SEQUENCE_TIS, EVENT_TOUCH_UP_TIS);
}
}
if (launcherVisible) {
@@ -1284,10 +1275,8 @@
return getRealDisplaySize().x - getWindowInsets().right - 1;
}
- void clickLauncherObject(UiObject2 object) {
- waitForObjectEnabled(object, "clickLauncherObject");
- expectEvent(TestProtocol.SEQUENCE_MAIN, LauncherInstrumentation.EVENT_TOUCH_DOWN);
- expectEvent(TestProtocol.SEQUENCE_MAIN, LauncherInstrumentation.EVENT_TOUCH_UP);
+ void clickObject(UiObject2 object) {
+ waitForObjectEnabled(object, "clickObject");
if (!isLauncher3() && getNavigationModel() != NavigationModel.THREE_BUTTON) {
expectEvent(TestProtocol.SEQUENCE_TIS, LauncherInstrumentation.EVENT_TOUCH_DOWN_TIS);
expectEvent(TestProtocol.SEQUENCE_TIS, LauncherInstrumentation.EVENT_TOUCH_UP_TIS);
@@ -1295,6 +1284,12 @@
object.click();
}
+ void clickLauncherObject(UiObject2 object) {
+ expectEvent(TestProtocol.SEQUENCE_MAIN, LauncherInstrumentation.EVENT_TOUCH_DOWN);
+ expectEvent(TestProtocol.SEQUENCE_MAIN, LauncherInstrumentation.EVENT_TOUCH_UP);
+ clickObject(object);
+ }
+
void scrollToLastVisibleRow(
UiObject2 container,
Collection<UiObject2> items,
diff --git a/tests/tapl/com/android/launcher3/tapl/Workspace.java b/tests/tapl/com/android/launcher3/tapl/Workspace.java
index e457354..16987e9 100644
--- a/tests/tapl/com/android/launcher3/tapl/Workspace.java
+++ b/tests/tapl/com/android/launcher3/tapl/Workspace.java
@@ -22,6 +22,7 @@
import static com.android.launcher3.testing.TestProtocol.NORMAL_STATE_ORDINAL;
import static com.android.launcher3.testing.TestProtocol.SPRING_LOADED_STATE_ORDINAL;
+import static junit.framework.TestCase.assertNotNull;
import static junit.framework.TestCase.assertTrue;
import android.graphics.Point;
@@ -33,8 +34,11 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.test.uiautomator.By;
+import androidx.test.uiautomator.BySelector;
import androidx.test.uiautomator.Direction;
+import androidx.test.uiautomator.UiDevice;
import androidx.test.uiautomator.UiObject2;
+import androidx.test.uiautomator.Until;
import com.android.launcher3.testing.TestProtocol;
@@ -51,6 +55,7 @@
private static final int DEFAULT_DRAG_STEPS = 10;
private static final String DROP_BAR_RES_ID = "drop_target_bar";
private static final String DELETE_TARGET_TEXT_ID = "delete_target_text";
+ private static final String UNINSTALL_TARGET_TEXT_ID = "uninstall_target_text";
static final Pattern EVENT_CTRL_W_DOWN = Pattern.compile(
"Key event: KeyEvent.*?action=ACTION_DOWN.*?keyCode=KEYCODE_W"
@@ -58,7 +63,7 @@
static final Pattern EVENT_CTRL_W_UP = Pattern.compile(
"Key event: KeyEvent.*?action=ACTION_UP.*?keyCode=KEYCODE_W"
+ ".*?metaState=META_CTRL_ON");
- private static final Pattern LONG_CLICK_EVENT = Pattern.compile("onWorkspaceItemLongClick");
+ static final Pattern LONG_CLICK_EVENT = Pattern.compile("onWorkspaceItemLongClick");
private final UiObject2 mHotseat;
@@ -83,8 +88,8 @@
final int windowCornerRadius = (int) Math.ceil(mLauncher.getWindowCornerRadius());
final int startY = deviceHeight - Math.max(bottomGestureMargin, windowCornerRadius) - 1;
final int swipeHeight = mLauncher.getTestInfo(
- TestProtocol.REQUEST_HOME_TO_ALL_APPS_SWIPE_HEIGHT).
- getInt(TestProtocol.TEST_INFO_RESPONSE_FIELD);
+ TestProtocol.REQUEST_HOME_TO_ALL_APPS_SWIPE_HEIGHT)
+ .getInt(TestProtocol.TEST_INFO_RESPONSE_FIELD);
LauncherInstrumentation.log(
"switchToAllApps: deviceHeight = " + deviceHeight + ", startY = " + startY
+ ", swipeHeight = " + swipeHeight + ", slop = "
@@ -118,7 +123,7 @@
final UiObject2 workspace = verifyActiveContainer();
final UiObject2 icon = workspace.findObject(
AppIcon.getAppIconSelector(appName, mLauncher));
- return icon != null ? new AppIcon(mLauncher, icon) : null;
+ return icon != null ? new WorkspaceAppIcon(mLauncher, icon) : null;
}
}
@@ -133,7 +138,7 @@
public AppIcon getWorkspaceAppIcon(String appName) {
try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
"want to get a workspace icon")) {
- return new AppIcon(mLauncher,
+ return new WorkspaceAppIcon(mLauncher,
mLauncher.waitForObjectInContainer(
verifyActiveContainer(),
AppIcon.getAppIconSelector(appName, mLauncher)));
@@ -214,7 +219,7 @@
@NonNull
public AppIcon getHotseatAppIcon(String appName) {
- return new AppIcon(mLauncher, mLauncher.waitForObjectInContainer(
+ return new WorkspaceAppIcon(mLauncher, mLauncher.waitForObjectInContainer(
mHotseat, AppIcon.getAppIconSelector(appName, mLauncher)));
}
@@ -224,12 +229,13 @@
}
/*
- * Get the center point of the delete icon in the drop target bar.
+ * Get the center point of the delete/uninstall icon in the drop target bar.
*/
- private Point getDeleteDropPoint() {
- return mLauncher.waitForObjectInContainer(
- mLauncher.waitForLauncherObject(DROP_BAR_RES_ID),
- DELETE_TARGET_TEXT_ID).getVisibleCenter();
+ private static Point getDropPointFromDropTargetBar(
+ LauncherInstrumentation launcher, String targetId) {
+ return launcher.waitForObjectInContainer(
+ launcher.waitForLauncherObject(DROP_BAR_RES_ID),
+ targetId).getVisibleCenter();
}
/**
@@ -244,7 +250,7 @@
"removing app icon from workspace")) {
dragIconToWorkspace(
mLauncher, appIcon,
- () -> getDeleteDropPoint(),
+ () -> getDropPointFromDropTargetBar(mLauncher, DELETE_TARGET_TEXT_ID),
true, /* decelerating */
appIcon.getLongPressIndicator(),
() -> mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, LONG_CLICK_EVENT),
@@ -258,6 +264,47 @@
}
/**
+ * Uninstall the appIcon by dragging it to the 'uninstall' drop point of the drop_target_bar.
+ *
+ * @param launcher the root TAPL instrumentation object of {@link LauncherInstrumentation} type.
+ * @param appIcon to be uninstalled.
+ * @param expectLongClickEvents the runnable to be executed to verify expected longclick event.
+ * @return validated workspace after the existing appIcon being uninstalled.
+ */
+ static Workspace uninstallAppIcon(LauncherInstrumentation launcher, AppIcon appIcon,
+ Runnable expectLongClickEvents) {
+ try (LauncherInstrumentation.Closable c = launcher.addContextLayer(
+ "uninstalling app icon")) {
+ dragIconToWorkspace(
+ launcher, appIcon,
+ () -> getDropPointFromDropTargetBar(launcher, UNINSTALL_TARGET_TEXT_ID),
+ true, /* decelerating */
+ appIcon.getLongPressIndicator(),
+ expectLongClickEvents,
+ null);
+
+ launcher.waitUntilLauncherObjectGone(DROP_BAR_RES_ID);
+
+ final BySelector installerAlert = By.text(Pattern.compile(
+ "Do you want to uninstall this app\\?",
+ Pattern.DOTALL | Pattern.MULTILINE));
+ final UiDevice device = launcher.getDevice();
+ assertTrue("uninstall alert is not shown", device.wait(
+ Until.hasObject(installerAlert), LauncherInstrumentation.WAIT_TIME_MS));
+ final UiObject2 ok = device.findObject(By.text("OK"));
+ assertNotNull("OK button is not shown", ok);
+ launcher.clickObject(ok);
+ assertTrue("Uninstall alert is not dismissed after clicking OK", device.wait(
+ Until.gone(installerAlert), LauncherInstrumentation.WAIT_TIME_MS));
+
+ try (LauncherInstrumentation.Closable c1 = launcher.addContextLayer(
+ "uninstalled app by dragging to the drop bar")) {
+ return new Workspace(launcher);
+ }
+ }
+ }
+
+ /**
* Finds folder icons in the current workspace.
*
* @return a list of folder icons.
diff --git a/tests/tapl/com/android/launcher3/tapl/WorkspaceAppIcon.java b/tests/tapl/com/android/launcher3/tapl/WorkspaceAppIcon.java
new file mode 100644
index 0000000..5f4e469
--- /dev/null
+++ b/tests/tapl/com/android/launcher3/tapl/WorkspaceAppIcon.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.tapl;
+
+import androidx.test.uiautomator.UiObject2;
+
+import java.util.regex.Pattern;
+
+/**
+ * App icon in workspace.
+ */
+final class WorkspaceAppIcon extends AppIcon {
+
+ WorkspaceAppIcon(LauncherInstrumentation launcher, UiObject2 icon) {
+ super(launcher, icon);
+ }
+
+ @Override
+ protected Pattern getLongClickEvent() {
+ return Workspace.LONG_CLICK_EVENT;
+ }
+}