Merging from ub-launcher3-master @ build 6219992
Test: manual, presubmit on the source branch
http://x20/teams/android-launcher/merge/ub-launcher3-master_6219992.html
Change-Id: Iff1e0d32b4ded4cb51c91c187d85f128b92157ca
diff --git a/Android.bp b/Android.bp
index fc99880..cb695df 100644
--- a/Android.bp
+++ b/Android.bp
@@ -32,7 +32,7 @@
}
java_library_static {
- name: "launcher-log-protos-lite",
+ name: "launcher_log_protos_lite",
srcs: [
"protos/*.proto",
"proto_overrides/*.proto",
@@ -45,4 +45,5 @@
"proto_overrides",
],
},
+ static_libs: ["libprotobuf-java-lite"],
}
diff --git a/Android.mk b/Android.mk
index a099ada..c066a12 100644
--- a/Android.mk
+++ b/Android.mk
@@ -48,7 +48,9 @@
androidx.preference_preference \
iconloader_base
-LOCAL_STATIC_JAVA_LIBRARIES := LauncherPluginLib
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ LauncherPluginLib \
+ launcher_log_protos_lite
LOCAL_SRC_FILES := \
$(call all-proto-files-under, protos) \
@@ -144,7 +146,10 @@
LOCAL_AAPT2_ONLY := true
LOCAL_MODULE_TAGS := optional
-LOCAL_STATIC_JAVA_LIBRARIES := SystemUISharedLibLauncherWrapper launcherprotosnano
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ SystemUISharedLib \
+ launcherprotosnano \
+ launcher_log_protos_lite
ifneq (,$(wildcard frameworks/base))
LOCAL_PRIVATE_PLATFORM_APIS := true
else
@@ -213,7 +218,10 @@
LOCAL_USE_AAPT2 := true
LOCAL_MODULE_TAGS := optional
-LOCAL_STATIC_JAVA_LIBRARIES := SystemUISharedLibLauncherWrapper launcherprotosnano
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ SystemUISharedLib \
+ launcherprotosnano \
+ launcher_log_protos_lite
ifneq (,$(wildcard frameworks/base))
LOCAL_PRIVATE_PLATFORM_APIS := true
else
diff --git a/go/src/com/android/launcher3/model/WidgetsModel.java b/go/src/com/android/launcher3/model/WidgetsModel.java
index 7269b41..7690b9d 100644
--- a/go/src/com/android/launcher3/model/WidgetsModel.java
+++ b/go/src/com/android/launcher3/model/WidgetsModel.java
@@ -22,7 +22,7 @@
import androidx.annotation.Nullable;
import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.icons.ComponentWithLabel;
+import com.android.launcher3.icons.ComponentWithLabelAndIcon;
import com.android.launcher3.util.PackageUserKey;
import com.android.launcher3.widget.WidgetListRowEntry;
@@ -59,7 +59,7 @@
* @param packageUser If null, all widgets and shortcuts are updated and returned, otherwise
* only widgets and shortcuts associated with the package/user are.
*/
- public List<ComponentWithLabel> update(LauncherAppState app,
+ public List<ComponentWithLabelAndIcon> update(LauncherAppState app,
@Nullable PackageUserKey packageUser) {
return Collections.emptyList();
}
diff --git a/iconloaderlib/src/com/android/launcher3/icons/BaseIconFactory.java b/iconloaderlib/src/com/android/launcher3/icons/BaseIconFactory.java
index bce4e0f..31a923e 100644
--- a/iconloaderlib/src/com/android/launcher3/icons/BaseIconFactory.java
+++ b/iconloaderlib/src/com/android/launcher3/icons/BaseIconFactory.java
@@ -350,6 +350,17 @@
iconDpi);
}
+ /**
+ * Badges the provided source with the badge info
+ */
+ public BitmapInfo badgeBitmap(Bitmap source, BitmapInfo badgeInfo) {
+ Bitmap icon = BitmapRenderer.createHardwareBitmap(mIconBitmapSize, mIconBitmapSize, (c) -> {
+ getShadowGenerator().recreateIcon(source, c);
+ badgeWithDrawable(c, new FixedSizeBitmapDrawable(badgeInfo.icon));
+ });
+ return BitmapInfo.of(icon, badgeInfo.color);
+ }
+
private int extractColor(Bitmap bitmap) {
return mDisableColorExtractor ? 0 : mColorExtractor.findDominantColorByHue(bitmap);
}
diff --git a/iconloaderlib/src/com/android/launcher3/icons/cache/BaseIconCache.java b/iconloaderlib/src/com/android/launcher3/icons/cache/BaseIconCache.java
index e807791..4c634cb 100644
--- a/iconloaderlib/src/com/android/launcher3/icons/cache/BaseIconCache.java
+++ b/iconloaderlib/src/com/android/launcher3/icons/cache/BaseIconCache.java
@@ -281,7 +281,8 @@
ContentValues values = newContentValues(entry.bitmap, entry.title.toString(),
componentName.getPackageName(), cachingLogic.getKeywords(object, mLocaleList));
- addIconToDB(values, componentName, info, userSerial);
+ addIconToDB(values, componentName, info, userSerial,
+ cachingLogic.getLastUpdatedTime(object, info));
}
/**
@@ -289,10 +290,10 @@
* @param values {@link ContentValues} containing icon & title
*/
private void addIconToDB(ContentValues values, ComponentName key,
- PackageInfo info, long userSerial) {
+ PackageInfo info, long userSerial, long lastUpdateTime) {
values.put(IconDB.COLUMN_COMPONENT, key.flattenToString());
values.put(IconDB.COLUMN_USER, userSerial);
- values.put(IconDB.COLUMN_LAST_UPDATED, info.lastUpdateTime);
+ values.put(IconDB.COLUMN_LAST_UPDATED, lastUpdateTime);
values.put(IconDB.COLUMN_VERSION, info.versionCode);
mIconDb.insertOrReplace(values);
}
@@ -362,7 +363,8 @@
}
if (object != null) {
entry.title = cachingLogic.getLabel(object);
- entry.contentDescription = mPackageManager.getUserBadgedLabel(entry.title, user);
+ entry.contentDescription = mPackageManager.getUserBadgedLabel(
+ cachingLogic.getDescription(object, entry.title), user);
}
}
}
@@ -449,7 +451,8 @@
// package updates.
ContentValues values = newContentValues(
iconInfo, entry.title.toString(), packageName, null);
- addIconToDB(values, cacheKey.componentName, info, getSerialNumberForUser(user));
+ addIconToDB(values, cacheKey.componentName, info, getSerialNumberForUser(user),
+ info.lastUpdateTime);
} catch (NameNotFoundException e) {
if (DEBUG) Log.d(TAG, "Application not installed " + packageName);
diff --git a/iconloaderlib/src/com/android/launcher3/icons/cache/CachingLogic.java b/iconloaderlib/src/com/android/launcher3/icons/cache/CachingLogic.java
index a89ede7..c12e9dc 100644
--- a/iconloaderlib/src/com/android/launcher3/icons/cache/CachingLogic.java
+++ b/iconloaderlib/src/com/android/launcher3/icons/cache/CachingLogic.java
@@ -34,6 +34,10 @@
CharSequence getLabel(T object);
+ default CharSequence getDescription(T object, CharSequence fallback) {
+ return fallback;
+ }
+
@NonNull
BitmapInfo loadIcon(Context context, T object);
diff --git a/iconloaderlib/src/com/android/launcher3/icons/cache/IconCacheUpdateHandler.java b/iconloaderlib/src/com/android/launcher3/icons/cache/IconCacheUpdateHandler.java
index d0db157..9e1ad7b 100644
--- a/iconloaderlib/src/com/android/launcher3/icons/cache/IconCacheUpdateHandler.java
+++ b/iconloaderlib/src/com/android/launcher3/icons/cache/IconCacheUpdateHandler.java
@@ -24,6 +24,7 @@
import android.os.SystemClock;
import android.os.UserHandle;
import android.text.TextUtils;
+import android.util.ArrayMap;
import android.util.Log;
import android.util.SparseBooleanArray;
@@ -61,7 +62,7 @@
private final HashMap<String, PackageInfo> mPkgInfoMap;
private final BaseIconCache mIconCache;
- private final HashMap<UserHandle, Set<String>> mPackagesToIgnore = new HashMap<>();
+ private final ArrayMap<UserHandle, Set<String>> mPackagesToIgnore = new ArrayMap<>();
private final SparseBooleanArray mItemsToDelete = new SparseBooleanArray();
private boolean mFilterMode = MODE_SET_INVALID_ITEMS;
@@ -77,8 +78,16 @@
createPackageInfoMap();
}
- public void setPackagesToIgnore(UserHandle userHandle, Set<String> packages) {
- mPackagesToIgnore.put(userHandle, packages);
+ /**
+ * Sets a package to ignore for processing
+ */
+ public void addPackagesToIgnore(UserHandle userHandle, String packageName) {
+ Set<String> packages = mPackagesToIgnore.get(userHandle);
+ if (packages == null) {
+ packages = new HashSet<>();
+ mPackagesToIgnore.put(userHandle, packages);
+ }
+ packages.add(packageName);
}
private void createPackageInfoMap() {
@@ -180,6 +189,7 @@
}
continue;
}
+
if (app == null) {
if (mFilterMode == MODE_SET_INVALID_ITEMS) {
mIconCache.remove(component, user);
@@ -262,6 +272,7 @@
T app = mAppsToUpdate.pop();
String pkg = mCachingLogic.getComponent(app).getPackageName();
PackageInfo info = mPkgInfoMap.get(pkg);
+
mIconCache.addIconToDBAndMemCache(
app, mCachingLogic, info, mUserSerial, true /*replace existing*/);
mUpdatedPackages.add(pkg);
diff --git a/proguard.flags b/proguard.flags
index 01302cf..e556c94 100644
--- a/proguard.flags
+++ b/proguard.flags
@@ -48,3 +48,6 @@
-dontwarn android.view.**
-dontwarn android.os.**
-dontwarn android.graphics.**
+
+# Ignore warnings for hidden utility classes referenced from the shared lib
+-dontwarn com.android.internal.util.**
\ No newline at end of file
diff --git a/protos/launcher_log.proto b/protos/launcher_log.proto
index 0560d68..ec1d55b 100644
--- a/protos/launcher_log.proto
+++ b/protos/launcher_log.proto
@@ -58,6 +58,35 @@
optional TipType tip_type = 17;
optional int32 search_query_length = 18;
optional bool is_work_app = 19;
+ optional FromFolderLabelState from_folder_label_state = 20;
+ optional ToFolderLabelState to_folder_label_state = 21;
+
+ // Note: proto does not support duplicate enum values, even if they belong to different enum type.
+ // Hence "FROM" and "TO" prefix added.
+ enum FromFolderLabelState{
+ FROM_FOLDER_LABEL_STATE_UNSPECIFIED = 0;
+ FROM_EMPTY = 1;
+ FROM_CUSTOM = 2;
+ FROM_SUGGESTED = 3;
+ }
+
+ enum ToFolderLabelState{
+ TO_FOLDER_LABEL_STATE_UNSPECIFIED = 0;
+ TO_SUGGESTION0_WITH_VALID_PRIMARY = 1;
+ TO_SUGGESTION1_WITH_VALID_PRIMARY = 2;
+ TO_SUGGESTION1_WITH_EMPTY_PRIMARY = 3;
+ TO_SUGGESTION2_WITH_VALID_PRIMARY = 4;
+ TO_SUGGESTION2_WITH_EMPTY_PRIMARY = 5;
+ TO_SUGGESTION3_WITH_VALID_PRIMARY = 6;
+ TO_SUGGESTION3_WITH_EMPTY_PRIMARY = 7;
+ TO_EMPTY_WITH_VALID_SUGGESTIONS = 8;
+ TO_EMPTY_WITH_EMPTY_SUGGESTIONS = 9;
+ TO_EMPTY_WITH_SUGGESTIONS_DISABLED = 10;
+ TO_CUSTOM_WITH_VALID_SUGGESTIONS = 11;
+ TO_CUSTOM_WITH_EMPTY_SUGGESTIONS = 12;
+ TO_CUSTOM_WITH_SUGGESTIONS_DISABLED = 13;
+ UNCHANGED = 14;
+ }
}
// Used to define what type of item a Target would represent.
@@ -120,6 +149,8 @@
BACK_GESTURE = 19;
UNDO = 20;
DISMISS_PREDICTION = 21;
+ HYBRID_HOTSEAT_ACCEPTED = 22;
+ HYBRID_HOTSEAT_CANCELED = 23;
}
enum TipType {
@@ -129,6 +160,7 @@
QUICK_SCRUB_TEXT = 3;
PREDICTION_TEXT = 4;
DWB_TOAST = 5;
+ HYBRID_HOTSEAT = 6;
}
// Used to define the action component of the LauncherEvent.
@@ -138,7 +170,8 @@
AUTOMATED = 1;
COMMAND = 2;
TIP = 3;
- // SOFT_KEYBOARD, HARD_KEYBOARD, ASSIST
+ SOFT_KEYBOARD = 4;
+ // HARD_KEYBOARD, ASSIST
}
enum Touch {
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/DynamicItemCache.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/DynamicItemCache.java
index 76972af..54f58e2 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/DynamicItemCache.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/DynamicItemCache.java
@@ -43,7 +43,6 @@
import com.android.launcher3.WorkspaceItemInfo;
import com.android.launcher3.allapps.AllAppsStore;
import com.android.launcher3.icons.IconCache;
-import com.android.launcher3.icons.LauncherIcons;
import com.android.launcher3.shortcuts.ShortcutKey;
import com.android.launcher3.shortcuts.ShortcutRequest;
import com.android.launcher3.util.InstantAppResolver;
@@ -170,14 +169,7 @@
List<ShortcutInfo> details = shortcutKey.buildRequest(mContext).query(ShortcutRequest.ALL);
if (!details.isEmpty()) {
WorkspaceItemInfo si = new WorkspaceItemInfo(details.get(0), mContext);
- try (LauncherIcons li = LauncherIcons.obtain(mContext)) {
- si.bitmap = li.createShortcutIcon(details.get(0), true /* badged */, null);
- } catch (Exception e) {
- if (DEBUG) {
- Log.e(TAG, "Error loading shortcut icon for " + shortcutKey.toString());
- }
- return null;
- }
+ mIconCache.getShortcutIcon(si, details.get(0));
return si;
}
if (DEBUG) {
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionRowView.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionRowView.java
index f82af62..834e6cf 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionRowView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionRowView.java
@@ -296,7 +296,7 @@
predictedApp.container = LauncherSettings.Favorites.CONTAINER_PREDICTION;
predictedApps.add(predictedApp);
} else {
- if (FeatureFlags.IS_DOGFOOD_BUILD) {
+ if (FeatureFlags.IS_STUDIO_BUILD) {
Log.e(TAG, "Predicted app not found: " + mapper);
}
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java
index 4c87945..00e72b1 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java
@@ -15,6 +15,9 @@
*/
package com.android.launcher3.hybridhotseat;
+import static com.android.launcher3.logging.LoggerUtils.newLauncherEvent;
+import static com.android.launcher3.userevent.nano.LauncherLogProto.ControlType.HYBRID_HOTSEAT_CANCELED;
+
import android.animation.PropertyValuesHolder;
import android.content.Context;
import android.content.res.Configuration;
@@ -32,6 +35,7 @@
import com.android.launcher3.R;
import com.android.launcher3.WorkspaceItemInfo;
import com.android.launcher3.anim.Interpolators;
+import com.android.launcher3.logging.UserEventDispatcher;
import com.android.launcher3.uioverrides.PredictedAppIcon;
import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.launcher3.views.AbstractSlideInView;
@@ -95,6 +99,7 @@
handleClose(true);
mHotseatEduController.migrate();
mHotseatEduController.finishOnboarding();
+ logUserAction(true);
Toast.makeText(mLauncher, R.string.hotseat_items_migrated, Toast.LENGTH_LONG).show();
}
@@ -102,6 +107,7 @@
if (mHotseatEduController == null) return;
Toast.makeText(getContext(), R.string.hotseat_no_migration, Toast.LENGTH_LONG).show();
mHotseatEduController.finishOnboarding();
+ logUserAction(false);
handleClose(true);
}
@@ -133,7 +139,28 @@
mLauncher.getDeviceProfile().hotseatBarSizePx + insets.bottom;
}
+ private void logUserAction(boolean migrated) {
+ LauncherLogProto.Action action = new LauncherLogProto.Action();
+ LauncherLogProto.Target target = new LauncherLogProto.Target();
+ action.type = LauncherLogProto.Action.Type.TOUCH;
+ action.touch = LauncherLogProto.Action.Touch.TAP;
+ target.containerType = LauncherLogProto.ContainerType.TIP;
+ target.tipType = LauncherLogProto.TipType.HYBRID_HOTSEAT;
+ target.controlType = migrated ? LauncherLogProto.ControlType.HYBRID_HOTSEAT_ACCEPTED
+ : HYBRID_HOTSEAT_CANCELED;
+ LauncherLogProto.LauncherEvent event = newLauncherEvent(action, target);
+ UserEventDispatcher.newInstance(getContext()).dispatchUserEvent(event, null);
+ }
+ private void logOnBoardingSeen() {
+ LauncherLogProto.Action action = new LauncherLogProto.Action();
+ LauncherLogProto.Target target = new LauncherLogProto.Target();
+ action.type = LauncherLogProto.Action.Type.TIP;
+ target.containerType = LauncherLogProto.ContainerType.TIP;
+ target.tipType = LauncherLogProto.TipType.HYBRID_HOTSEAT;
+ LauncherLogProto.LauncherEvent event = newLauncherEvent(action, target);
+ UserEventDispatcher.newInstance(getContext()).dispatchUserEvent(event, null);
+ }
private void animateOpen() {
if (mIsOpen || mOpenCloseAnimator.isRunning()) {
return;
@@ -165,6 +192,7 @@
return;
}
mLauncher.getDragLayer().addView(this);
+ logOnBoardingSeen();
animateOpen();
for (int i = 0; i < mLauncher.getDeviceProfile().inv.numHotseatIcons; i++) {
WorkspaceItemInfo info = predictions.get(i);
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java
index cc6ec69..0b05427 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java
@@ -109,6 +109,7 @@
private AppPredictor mAppPredictor;
private AllAppsStore mAllAppsStore;
private AnimatorSet mIconRemoveAnimators;
+ private boolean mUIUpdatePaused = false;
private HotseatEduController mHotseatEduController;
@@ -168,7 +169,7 @@
}
private void fillGapsWithPrediction(boolean animate, Runnable callback) {
- if (!isReady() || mDragObject != null) {
+ if (!isReady() || mUIUpdatePaused || mDragObject != null) {
return;
}
List<WorkspaceItemInfo> predictedApps = mapToWorkspaceItemInfo(mComponentKeyMappers);
@@ -250,6 +251,16 @@
}
/**
+ * start and pauses predicted apps update on the hotseat
+ */
+ public void setPauseUIUpdate(boolean paused) {
+ mUIUpdatePaused = paused;
+ if (!paused) {
+ fillGapsWithPrediction();
+ }
+ }
+
+ /**
* Creates App Predictor with all the current apps pinned on the hotseat
*/
public void createPredictor() {
@@ -447,17 +458,40 @@
/**
* Unpins pinned app when it's converted into a folder
*/
- public void folderCreatedFromIcon(ItemInfo info, FolderInfo folderInfo) {
+ public void folderCreatedFromWorkspaceItem(ItemInfo info, FolderInfo folderInfo) {
+ if (info.itemType != LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
+ return;
+ }
AppTarget target = getAppTargetFromItemInfo(info);
- if (folderInfo.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT && !isInHotseat(
- info)) {
+ ViewGroup hotseatVG = mHotseat.getShortcutsAndWidgets();
+ ViewGroup firstScreenVG = mLauncher.getWorkspace().getScreenWithId(
+ Workspace.FIRST_SCREEN_ID).getShortcutsAndWidgets();
+
+ if (isInHotseat(folderInfo) && !getPinnedAppTargetsInViewGroup(hotseatVG).contains(
+ target)) {
notifyItemAction(target, APP_LOCATION_HOTSEAT, APPTARGET_ACTION_UNPIN);
- } else if (folderInfo.container == LauncherSettings.Favorites.CONTAINER_DESKTOP
- && folderInfo.screenId == Workspace.FIRST_SCREEN_ID && !isInFirstPage(info)) {
+ } else if (isInFirstPage(folderInfo) && !getPinnedAppTargetsInViewGroup(
+ firstScreenVG).contains(target)) {
notifyItemAction(target, APP_LOCATION_WORKSPACE, APPTARGET_ACTION_UNPIN);
}
}
+ /**
+ * Pins workspace item created when all folder items are removed but one
+ */
+ public void folderConvertedToWorkspaceItem(ItemInfo info, FolderInfo folderInfo) {
+ if (info.itemType != LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
+ return;
+ }
+ AppTarget target = getAppTargetFromItemInfo(info);
+ if (isInHotseat(info)) {
+ notifyItemAction(target, APP_LOCATION_HOTSEAT, AppTargetEvent.ACTION_PIN);
+ } else if (isInFirstPage(info)) {
+ notifyItemAction(target, APP_LOCATION_WORKSPACE, AppTargetEvent.ACTION_PIN);
+ }
+ }
+
+
@Override
public void onDragEnd() {
if (mDragObject == null) {
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/PredictedAppIcon.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/PredictedAppIcon.java
index b2e1798..4bbb48c 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/PredictedAppIcon.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/PredictedAppIcon.java
@@ -86,6 +86,7 @@
mIsDrawingDot = true;
int count = canvas.save();
canvas.translate(-getWidth() * RING_EFFECT_RATIO, -getHeight() * RING_EFFECT_RATIO);
+ canvas.scale(1 + 2 * RING_EFFECT_RATIO, 1 + 2 * RING_EFFECT_RATIO);
super.drawDotIfNecessary(canvas);
canvas.restoreToCount(count);
mIsDrawingDot = false;
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
index f6bc5ea..30a4e50 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
@@ -20,20 +20,24 @@
import static com.android.quickstep.SysUINavigationMode.Mode.NO_BUTTON;
import android.content.Context;
+import android.content.Intent;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.Bundle;
import android.view.Gravity;
+import android.view.View;
+
+import androidx.annotation.Nullable;
import com.android.launcher3.BaseQuickstepLauncher;
-import com.android.launcher3.CellLayout;
import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.ItemInfo;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
import com.android.launcher3.WorkspaceItemInfo;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.folder.FolderIcon;
+import com.android.launcher3.folder.Folder;
import com.android.launcher3.graphics.RotationMode;
import com.android.launcher3.hybridhotseat.HotseatPredictionController;
import com.android.launcher3.popup.SystemShortcut;
@@ -162,6 +166,15 @@
}
@Override
+ public boolean startActivitySafely(View v, Intent intent, ItemInfo item,
+ @Nullable String sourceContainer) {
+ if (mHotseatPredictionController != null) {
+ mHotseatPredictionController.setPauseUIUpdate(true);
+ }
+ return super.startActivitySafely(v, intent, item, sourceContainer);
+ }
+
+ @Override
protected void onActivityFlagsChanged(int changeBits) {
super.onActivityFlagsChanged(changeBits);
@@ -170,16 +183,27 @@
&& (getActivityFlags() & ACTIVITY_STATE_TRANSITION_ACTIVE) == 0) {
onStateOrResumeChanged();
}
+
+ if ((changeBits & ACTIVITY_STATE_STARTED) != 0 && mHotseatPredictionController != null
+ && (getActivityFlags() & ACTIVITY_STATE_USER_ACTIVE) == 0) {
+ mHotseatPredictionController.setPauseUIUpdate(false);
+ }
}
@Override
- public FolderIcon addFolder(CellLayout layout, WorkspaceItemInfo info, int container,
- int screenId, int cellX, int cellY) {
- FolderIcon fi = super.addFolder(layout, info, container, screenId, cellX, cellY);
+ public void folderCreatedFromItem(Folder folder, WorkspaceItemInfo itemInfo) {
+ super.folderCreatedFromItem(folder, itemInfo);
if (mHotseatPredictionController != null) {
- mHotseatPredictionController.folderCreatedFromIcon(info, fi.getFolder().getInfo());
+ mHotseatPredictionController.folderCreatedFromWorkspaceItem(itemInfo, folder.getInfo());
}
- return fi;
+ }
+
+ @Override
+ public void folderConvertedToItem(Folder folder, WorkspaceItemInfo itemInfo) {
+ super.folderConvertedToItem(folder, itemInfo);
+ if (mHotseatPredictionController != null) {
+ mHotseatPredictionController.folderConvertedToWorkspaceItem(itemInfo, folder.getInfo());
+ }
}
@Override
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewState.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewState.java
index 644cfcb..a1c8378 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewState.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewState.java
@@ -33,6 +33,7 @@
import static com.android.launcher3.logging.LoggerUtils.newContainerTarget;
import static com.android.launcher3.states.RotationHelper.REQUEST_ROTATE;
import static com.android.quickstep.SysUINavigationMode.Mode.NO_BUTTON;
+import static com.android.quickstep.SysUINavigationMode.removeShelfFromOverview;
import android.graphics.Rect;
import android.view.View;
@@ -125,7 +126,8 @@
@Override
public ScaleAndTranslation getQsbScaleAndTranslation(Launcher launcher) {
- if (this == OVERVIEW && ENABLE_OVERVIEW_ACTIONS.get()) {
+ if (this == OVERVIEW && ENABLE_OVERVIEW_ACTIONS.get()
+ && removeShelfFromOverview(launcher)) {
// Treat the QSB as part of the hotseat so they move together.
return getHotseatScaleAndTranslation(launcher);
}
@@ -158,7 +160,7 @@
if (launcher.getDeviceProfile().isVerticalBarLayout()) {
return VERTICAL_SWIPE_INDICATOR | RECENTS_CLEAR_ALL_BUTTON;
} else {
- if (ENABLE_OVERVIEW_ACTIONS.get()) {
+ if (ENABLE_OVERVIEW_ACTIONS.get() && removeShelfFromOverview(launcher)) {
return VERTICAL_SWIPE_INDICATOR | RECENTS_CLEAR_ALL_BUTTON;
}
@@ -229,6 +231,7 @@
builder.setInterpolator(ANIM_WORKSPACE_FADE, OVERSHOOT_1_2);
builder.setInterpolator(ANIM_OVERVIEW_SCALE, OVERSHOOT_1_2);
Interpolator translationInterpolator = ENABLE_OVERVIEW_ACTIONS.get()
+ && removeShelfFromOverview(launcher)
? OVERSHOOT_1_2
: OVERSHOOT_1_7;
builder.setInterpolator(ANIM_OVERVIEW_TRANSLATE_X, translationInterpolator);
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityInterface.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityInterface.java
index 28fc3da..1b6d291 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityInterface.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityInterface.java
@@ -234,6 +234,7 @@
public void adjustActivityControllerInterpolators() {
if (mAdjustInterpolatorsRunnable != null) {
mAdjustInterpolatorsRunnable.run();
+ mAdjustInterpolatorsRunnable = null;
}
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandler.java
index 1b60404..345a147 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandler.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandler.java
@@ -21,7 +21,7 @@
import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.anim.Interpolators.OVERSHOOT_1_2;
import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
-import static com.android.launcher3.config.FeatureFlags.QUICKSTEP_SPRINGS;
+import static com.android.launcher3.config.FeatureFlags.UNSTABLE_SPRINGS;
import static com.android.launcher3.util.DefaultDisplay.getSingleFrameMs;
import static com.android.launcher3.util.SystemUiController.UI_STATE_OVERVIEW;
import static com.android.quickstep.GestureState.GestureEndTarget.HOME;
@@ -75,7 +75,6 @@
import com.android.quickstep.util.ActiveGestureLog;
import com.android.quickstep.util.AppWindowAnimationHelper.TargetAlphaProvider;
import com.android.quickstep.util.RectFSpringAnim;
-import com.android.quickstep.util.SharedApiCompat;
import com.android.quickstep.util.ShelfPeekAnim;
import com.android.quickstep.util.ShelfPeekAnim.ShelfAnimState;
import com.android.quickstep.views.LiveTileOverlay;
@@ -918,6 +917,12 @@
windowAnim.addAnimatorListener(new AnimationSuccessListener() {
@Override
public void onAnimationSuccess(Animator animator) {
+ if (mRecentsAnimationController == null) {
+ // If the recents animation is interrupted, we still end the running
+ // animation (not canceled) so this is still called. In that case, we can
+ // skip doing any future work here for the current gesture.
+ return;
+ }
// Finalize the state and notify of the change
mGestureState.setState(STATE_END_TARGET_ANIMATION_FINISHED);
}
@@ -939,6 +944,12 @@
windowAnim.addListener(new AnimationSuccessListener() {
@Override
public void onAnimationSuccess(Animator animator) {
+ if (mRecentsAnimationController == null) {
+ // If the recents animation is interrupted, we still end the running
+ // animation (not canceled) so this is still called. In that case, we can
+ // skip doing any future work here for the current gesture.
+ return;
+ }
if (target == NEW_TASK && mRecentsView != null
&& mRecentsView.getNextPage() == mRecentsView.getRunningTaskIndex()) {
// We are about to launch the current running task, so use LAST_TASK state
@@ -973,7 +984,7 @@
}
mLauncherTransitionController.getAnimationPlayer().setDuration(Math.max(0, duration));
- if (QUICKSTEP_SPRINGS.get()) {
+ if (UNSTABLE_SPRINGS.get()) {
mLauncherTransitionController.dispatchOnStartWithVelocity(end, velocityPxPerMs.y);
}
mLauncherTransitionController.getAnimationPlayer().start();
@@ -1142,8 +1153,7 @@
final int runningTaskId = mGestureState.getRunningTaskId();
if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
if (mRecentsAnimationController != null) {
- SharedApiCompat.setWillFinishToHome(mRecentsAnimationController.getController(),
- true /* willFinishToHome */);
+ mRecentsAnimationController.getController().setWillFinishToHome(true);
// Update the screenshot of the task
if (mTaskSnapshot == null) {
mTaskSnapshot = mRecentsAnimationController.screenshotTask(runningTaskId);
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/QuickstepTestInformationHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/QuickstepTestInformationHandler.java
index 3e6def3..34b2bdb 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/QuickstepTestInformationHandler.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/QuickstepTestInformationHandler.java
@@ -1,23 +1,18 @@
package com.android.quickstep;
-import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
-
+import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.util.Log;
-import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherAppState;
import com.android.launcher3.testing.TestInformationHandler;
import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.uioverrides.touchcontrollers.PortraitStatesTouchController;
import com.android.quickstep.util.LayoutUtils;
-import com.android.quickstep.views.RecentsView;
import com.android.systemui.shared.recents.model.Task;
import java.util.ArrayList;
import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
public class QuickstepTestInformationHandler extends TestInformationHandler {
@@ -46,33 +41,8 @@
}
case TestProtocol.REQUEST_HOTSEAT_TOP: {
- if (mLauncher == null) return null;
-
- response.putInt(TestProtocol.TEST_INFO_RESPONSE_FIELD,
- PortraitStatesTouchController.getHotseatTop(mLauncher));
- return response;
- }
-
- case TestProtocol.REQUEST_OVERVIEW_LEFT_GESTURE_MARGIN: {
- try {
- final int leftMargin = MAIN_EXECUTOR.submit(() ->
- getRecentsView().getLeftGestureMargin()).get();
- response.putInt(TestProtocol.TEST_INFO_RESPONSE_FIELD, leftMargin);
- } catch (ExecutionException | InterruptedException e) {
- throw new RuntimeException(e);
- }
- return response;
- }
-
- case TestProtocol.REQUEST_OVERVIEW_RIGHT_GESTURE_MARGIN: {
- try {
- final int rightMargin = MAIN_EXECUTOR.submit(() ->
- getRecentsView().getRightGestureMargin()).get();
- response.putInt(TestProtocol.TEST_INFO_RESPONSE_FIELD, rightMargin);
- } catch (ExecutionException | InterruptedException e) {
- throw new RuntimeException(e);
- }
- return response;
+ return getLauncherUIProperty(
+ Bundle::putInt, PortraitStatesTouchController::getHotseatTop);
}
case TestProtocol.REQUEST_RECENT_TASKS_LIST: {
@@ -99,11 +69,12 @@
return super.call(method);
}
- private RecentsView getRecentsView() {
+ @Override
+ protected Activity getCurrentActivity() {
OverviewComponentObserver observer = new OverviewComponentObserver(mContext,
new RecentsAnimationDeviceState(mContext));
try {
- return observer.getActivityInterface().getCreatedActivity().getOverviewPanel();
+ return observer.getActivityInterface().getCreatedActivity();
} finally {
observer.onDestroy();
}
@@ -111,11 +82,6 @@
@Override
protected boolean isLauncherInitialized() {
- if (TestProtocol.sDebugTracing) {
- Log.d(TestProtocol.LAUNCHER_DIDNT_INITIALIZE,
- "isLauncherInitialized.TouchInteractionService.isInitialized=" +
- TouchInteractionService.isInitialized());
- }
return super.isLauncherInitialized() && TouchInteractionService.isInitialized();
}
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
index 6af6da3..5820029 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
@@ -142,16 +142,13 @@
TouchInteractionService.this.initInputMonitor();
preloadOverview(true /* fromInit */);
});
- if (TestProtocol.sDebugTracing) {
- Log.d(TestProtocol.LAUNCHER_DIDNT_INITIALIZE, "TIS initialized");
- }
sIsInitialized = true;
}
@BinderThread
@Override
public void onOverviewToggle() {
- TestLogging.recordEvent("onOverviewToggle");
+ TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "onOverviewToggle");
mOverviewCommandHelper.onOverviewToggle();
}
@@ -411,9 +408,6 @@
@Override
public void onDestroy() {
- if (TestProtocol.sDebugTracing) {
- Log.d(TestProtocol.LAUNCHER_DIDNT_INITIALIZE, "TIS destroyed");
- }
sIsInitialized = false;
if (mDeviceState.isUserUnlocked()) {
mInputConsumer.unregisterInputConsumer();
@@ -444,13 +438,17 @@
Log.e(TAG, "Unknown event " + ev);
return;
}
+ MotionEvent event = (MotionEvent) ev;
+
+ TestLogging.recordMotionEvent(
+ TestProtocol.SEQUENCE_TIS, "TouchInteractionService.onInputEvent", event);
+
if (!mDeviceState.isUserUnlocked()) {
return;
}
Object traceToken = TraceHelper.INSTANCE.beginFlagsOverride(
TraceHelper.FLAG_ALLOW_BINDER_TRACKING);
- MotionEvent event = (MotionEvent) ev;
if (event.getAction() == ACTION_DOWN) {
GestureState newGestureState = new GestureState(mOverviewComponentObserver,
ActiveGestureLog.INSTANCE.generateAndSetLogId());
@@ -731,20 +729,15 @@
}
} else {
// Dump everything
+ FeatureFlags.dump(pw);
+ PluginManagerWrapper.INSTANCE.get(getBaseContext()).dump(pw);
mDeviceState.dump(pw);
+ mOverviewComponentObserver.dump(pw);
pw.println("TouchState:");
boolean resumed = mOverviewComponentObserver != null
&& mOverviewComponentObserver.getActivityInterface().isResumed();
pw.println(" resumed=" + resumed);
pw.println(" mConsumer=" + mConsumer.getName());
- pw.println("FeatureFlags:");
- pw.println(" APPLY_CONFIG_AT_RUNTIME=" + APPLY_CONFIG_AT_RUNTIME.get());
- pw.println(" QUICKSTEP_SPRINGS=" + QUICKSTEP_SPRINGS.get());
- pw.println(" UNSTABLE_SPRINGS=" + UNSTABLE_SPRINGS.get());
- pw.println(" ADAPTIVE_ICON_WINDOW_ANIM=" + ADAPTIVE_ICON_WINDOW_ANIM.get());
- pw.println(" ENABLE_QUICKSTEP_LIVE_TILE=" + ENABLE_QUICKSTEP_LIVE_TILE.get());
- pw.println(" ENABLE_HINTS_IN_OVERVIEW=" + ENABLE_HINTS_IN_OVERVIEW.get());
- pw.println(" FAKE_LANDSCAPE_UI=" + FAKE_LANDSCAPE_UI.get());
ActiveGestureLog.INSTANCE.dump("", pw);
pw.println("ProtoTrace:");
pw.println(" file="
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DelegateInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DelegateInputConsumer.java
index 8ae4f06..05c206f 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DelegateInputConsumer.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DelegateInputConsumer.java
@@ -3,6 +3,7 @@
import android.view.MotionEvent;
import com.android.launcher3.testing.TestLogging;
+import com.android.launcher3.testing.TestProtocol;
import com.android.quickstep.InputConsumer;
import com.android.systemui.shared.system.InputMonitorCompat;
@@ -35,7 +36,7 @@
protected void setActive(MotionEvent ev) {
mState = STATE_ACTIVE;
- TestLogging.recordEvent("pilferPointers");
+ TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "pilferPointers");
mInputMonitor.pilferPointers();
// Send cancel event
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java
index d01e1a4..ba1d38c 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java
@@ -38,6 +38,7 @@
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.testing.TestLogging;
+import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.util.DefaultDisplay;
import com.android.quickstep.GestureState;
import com.android.quickstep.InputConsumer;
@@ -203,7 +204,7 @@
private void startRecentsTransition() {
mThresholdCrossed = true;
- TestLogging.recordEvent("pilferPointers");
+ TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "pilferPointers");
mInputMonitorCompat.pilferPointers();
Intent intent = new Intent(Intent.ACTION_MAIN)
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
index 1b0e05a..3ee3c2d 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
@@ -45,6 +45,7 @@
import com.android.launcher3.R;
import com.android.launcher3.testing.TestLogging;
+import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.util.Preconditions;
import com.android.launcher3.util.TraceHelper;
import com.android.quickstep.BaseActivityInterface;
@@ -303,7 +304,7 @@
if (mInteractionHandler == null) {
return;
}
- TestLogging.recordEvent("pilferPointers");
+ TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "pilferPointers");
mInputMonitorCompat.pilferPointers();
mActivityInterface.closeOverlay();
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java
index 710ce26..f8abba5 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java
@@ -26,6 +26,7 @@
import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.Utilities;
import com.android.launcher3.testing.TestLogging;
+import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.views.BaseDragLayer;
import com.android.quickstep.BaseActivityInterface;
import com.android.quickstep.GestureState;
@@ -107,7 +108,7 @@
ActiveGestureLog.INSTANCE.addLog("startQuickstep");
}
if (mInputMonitor != null) {
- TestLogging.recordEvent("pilferPointers");
+ TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "pilferPointers");
mInputMonitor.pilferPointers();
}
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverviewWithoutFocusInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverviewWithoutFocusInputConsumer.java
index 6bfc3fd..823b254 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverviewWithoutFocusInputConsumer.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverviewWithoutFocusInputConsumer.java
@@ -23,6 +23,7 @@
import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.logging.StatsLogUtils;
import com.android.launcher3.testing.TestLogging;
+import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
import com.android.quickstep.GestureState;
@@ -64,7 +65,7 @@
private void onInterceptTouch() {
if (mInputMonitor != null) {
- TestLogging.recordEvent("pilferPointers");
+ TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "pilferPointers");
mInputMonitor.pilferPointers();
}
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
index cc10ca9..a673ab6 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
@@ -73,7 +73,6 @@
import android.view.View;
import android.view.ViewDebug;
import android.view.ViewGroup;
-import android.view.WindowInsets;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.ListView;
@@ -345,7 +344,7 @@
setLayoutDirection(mIsRtl ? View.LAYOUT_DIRECTION_RTL : View.LAYOUT_DIRECTION_LTR);
mTaskTopMargin = getResources()
.getDimensionPixelSize(R.dimen.task_thumbnail_top_margin);
- mTaskBottomMargin = LayoutUtils.thumbnailBottomMargin(getResources());
+ mTaskBottomMargin = LayoutUtils.thumbnailBottomMargin(context);
mSquaredTouchSlop = squaredTouchSlop(context);
mEmptyIcon = context.getDrawable(R.drawable.ic_empty_recents);
@@ -1251,7 +1250,7 @@
}
public PendingAnimation createAllTasksDismissAnimation(long duration) {
- if (FeatureFlags.IS_DOGFOOD_BUILD && mPendingAnimation != null) {
+ if (FeatureFlags.IS_STUDIO_BUILD && mPendingAnimation != null) {
throw new IllegalStateException("Another pending animation is still running");
}
AnimatorSet anim = new AnimatorSet();
@@ -1595,7 +1594,7 @@
}
public PendingAnimation createTaskLauncherAnimation(TaskView tv, long duration) {
- if (FeatureFlags.IS_DOGFOOD_BUILD && mPendingAnimation != null) {
+ if (FeatureFlags.IS_STUDIO_BUILD && mPendingAnimation != null) {
throw new IllegalStateException("Another pending animation is still running");
}
@@ -1889,16 +1888,6 @@
}
}
- public int getLeftGestureMargin() {
- final WindowInsets insets = getRootWindowInsets();
- return Math.max(insets.getSystemGestureInsets().left, insets.getSystemWindowInsetLeft());
- }
-
- public int getRightGestureMargin() {
- final WindowInsets insets = getRootWindowInsets();
- return Math.max(insets.getSystemGestureInsets().right, insets.getSystemWindowInsetRight());
- }
-
/** If it's in the live tile mode, switch the running task into screenshot mode. */
public void switchToScreenshot(ThumbnailData thumbnailData, Runnable onFinishRunnable) {
TaskView taskView = getRunningTaskView();
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskThumbnailView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskThumbnailView.java
index 49f667e..8ed1392 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskThumbnailView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskThumbnailView.java
@@ -54,6 +54,7 @@
import com.android.systemui.plugins.PluginListener;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.recents.model.ThumbnailData;
+import com.android.systemui.shared.system.ConfigurationCompat;
/**
* A task in the Recents view.
@@ -357,7 +358,8 @@
final float thumbnailScale;
int thumbnailRotation = mThumbnailData.rotation;
- int currentRotation = getDisplay() != null ? getDisplay().getRotation() : 0;
+ int currentRotation = ConfigurationCompat.getWindowConfigurationRotation(
+ getResources().getConfiguration());
int deltaRotate = getRotationDelta(currentRotation, thumbnailRotation);
// Landscape vs portrait change
boolean windowingModeSupportsRotation = !mActivity.isInMultiWindowMode()
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java
index 5954b86..8b7ce10 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java
@@ -22,6 +22,7 @@
import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
+import static com.android.quickstep.SysUINavigationMode.removeShelfFromOverview;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -30,7 +31,6 @@
import android.animation.ValueAnimator;
import android.app.ActivityOptions;
import android.content.Context;
-import android.content.res.Resources;
import android.graphics.Outline;
import android.graphics.Rect;
import android.graphics.RectF;
@@ -58,6 +58,7 @@
import com.android.launcher3.logging.UserEventDispatcher;
import com.android.launcher3.popup.SystemShortcut;
import com.android.launcher3.testing.TestLogging;
+import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
@@ -221,7 +222,7 @@
mCurrentFullscreenParams = new FullscreenDrawParams(mCornerRadius);
mDigitalWellBeingToast = new DigitalWellBeingToast(mActivity, this);
- mOutlineProvider = new TaskOutlineProvider(getResources(), mCurrentFullscreenParams);
+ mOutlineProvider = new TaskOutlineProvider(getContext(), mCurrentFullscreenParams);
setOutlineProvider(mOutlineProvider);
}
@@ -230,13 +231,14 @@
super.onFinishInflate();
mSnapshotView = findViewById(R.id.snapshot);
mIconView = findViewById(R.id.icon);
+ final Context context = getContext();
TaskView.LayoutParams thumbnailParams = (LayoutParams) mSnapshotView.getLayoutParams();
- thumbnailParams.bottomMargin = LayoutUtils.thumbnailBottomMargin(getResources());
+ thumbnailParams.bottomMargin = LayoutUtils.thumbnailBottomMargin(context);
mSnapshotView.setLayoutParams(thumbnailParams);
- if (FeatureFlags.ENABLE_OVERVIEW_ACTIONS.get()) {
+ if (FeatureFlags.ENABLE_OVERVIEW_ACTIONS.get() && removeShelfFromOverview(context)) {
mActionsView = mSnapshotView.getTaskOverlay().getActionsView();
if (mActionsView != null) {
TaskView.LayoutParams params = new TaskView.LayoutParams(LayoutParams.MATCH_PARENT,
@@ -333,9 +335,8 @@
Consumer<Boolean> resultCallback, Handler resultCallbackHandler) {
if (mTask != null) {
final ActivityOptions opts;
- if (Utilities.IS_RUNNING_IN_TEST_HARNESS) {
- TestLogging.recordEvent("startActivityFromRecentsAsync:" + mTask);
- }
+ TestLogging.recordEvent(
+ TestProtocol.SEQUENCE_MAIN, "startActivityFromRecentsAsync", mTask);
if (animate) {
opts = mActivity.getActivityLaunchOptions(this);
if (freezeTaskList) {
@@ -679,9 +680,10 @@
private final int mMarginBottom;
private FullscreenDrawParams mFullscreenParams;
- TaskOutlineProvider(Resources res, FullscreenDrawParams fullscreenParams) {
- mMarginTop = res.getDimensionPixelSize(R.dimen.task_thumbnail_top_margin);
- mMarginBottom = LayoutUtils.thumbnailBottomMargin(res);
+ TaskOutlineProvider(Context context, FullscreenDrawParams fullscreenParams) {
+ mMarginTop = context.getResources().getDimensionPixelSize(
+ R.dimen.task_thumbnail_top_margin);
+ mMarginBottom = LayoutUtils.thumbnailBottomMargin(context);
mFullscreenParams = fullscreenParams;
}
diff --git a/quickstep/res/values/strings.xml b/quickstep/res/values/strings.xml
index 5f81e1f..45a62ab 100644
--- a/quickstep/res/values/strings.xml
+++ b/quickstep/res/values/strings.xml
@@ -67,13 +67,13 @@
<string name="back_gesture_tutorial_close_button_content_description" translatable="false">Close</string>
<!-- Hotseat migration notification title -->
- <string translatable="false" name="hotseat_migrate_prompt_title">Get suggested apps on the home screen</string>
+ <string translatable="false" name="hotseat_migrate_prompt_title">Easily access your most-used apps</string>
<!-- Hotseat migration notification content -->
- <string translatable="false" name="hotseat_migrate_prompt_content">Tap to set up</string>
+ <string translatable="false" name="hotseat_migrate_prompt_content">Pixel suggests your favorite apps based on your routines. Tap to learn more.</string>
<!-- Hotseat migration wizard title -->
<string translatable="false" name="hotseat_migrate_title">Suggested apps replace the bottom row of apps</string>
<!-- Hotseat migration wizard message -->
- <string translatable="false" name="hotseat_migrate_message">To pin a favorite app, drag it over a suggested app. To hide a suggested app, touch & hold it.</string>
+ <string translatable="false" name="hotseat_migrate_message">Your current apps will move to the last screen. To pin or block a suggested app, drag it off the bottom row.</string>
<!-- Toast message user sees after opting into fully predicted hybrid hotseat -->
<string translatable="false" name="hotseat_items_migrated">Bottom row of apps moved to last screen</string>
<!-- Toast message user sees after opting into fully predicted hybrid hotseat -->
diff --git a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
index 8eb639b..4470407 100644
--- a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
@@ -25,6 +25,7 @@
import static com.android.launcher3.allapps.DiscoveryBounce.HOME_BOUNCE_SEEN;
import static com.android.launcher3.allapps.DiscoveryBounce.SHELF_BOUNCE_COUNT;
import static com.android.launcher3.allapps.DiscoveryBounce.SHELF_BOUNCE_SEEN;
+import static com.android.quickstep.SysUINavigationMode.removeShelfFromOverview;
import android.animation.AnimatorSet;
import android.animation.ValueAnimator;
@@ -35,6 +36,7 @@
import com.android.launcher3.LauncherState.ScaleAndTranslation;
import com.android.launcher3.LauncherStateManager.StateHandler;
+import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.model.WellbeingModel;
import com.android.launcher3.popup.SystemShortcut;
import com.android.launcher3.proxy.ProxyActivityStarter;
@@ -172,7 +174,7 @@
@Override
public void startActivityForResult(Intent intent, int requestCode, Bundle options) {
if (requestCode != -1) {
- mPendingActivityRequestCode = -1;
+ mPendingActivityRequestCode = requestCode;
StartActivityParams params = new StartActivityParams(this, requestCode);
params.intent = intent;
params.options = options;
@@ -194,6 +196,16 @@
}
@Override
+ protected void setupViews() {
+ super.setupViews();
+
+ if (FeatureFlags.ENABLE_OVERVIEW_ACTIONS.get() && removeShelfFromOverview(this)) {
+ // Overview is above all other launcher elements, including qsb, so move it to the top.
+ getOverviewPanel().bringToFront();
+ }
+ }
+
+ @Override
protected StateHandler[] createStateHandlers() {
return new StateHandler[] {
getAllAppsController(),
diff --git a/quickstep/src/com/android/launcher3/uioverrides/DisplayRotationListener.java b/quickstep/src/com/android/launcher3/uioverrides/DisplayRotationListener.java
deleted file mode 100644
index 2d9a161..0000000
--- a/quickstep/src/com/android/launcher3/uioverrides/DisplayRotationListener.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.launcher3.uioverrides;
-
-import android.content.Context;
-import android.os.Handler;
-
-import com.android.systemui.shared.system.RotationWatcher;
-
-/**
- * Utility class for listening for rotation changes
- */
-public class DisplayRotationListener extends RotationWatcher {
-
- private final Runnable mCallback;
- private Handler mHandler;
-
- public DisplayRotationListener(Context context, Runnable callback) {
- super(context);
- mCallback = callback;
- }
-
- @Override
- public void enable() {
- if (mHandler == null) {
- mHandler = new Handler();
- }
- super.enable();
- }
-
- @Override
- protected void onRotationChanged(int i) {
- mHandler.post(mCallback);
- }
-}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/plugins/PluginManagerWrapper.java b/quickstep/src/com/android/launcher3/uioverrides/plugins/PluginManagerWrapper.java
index 6e7c087..2e422b7 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/plugins/PluginManagerWrapper.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/plugins/PluginManagerWrapper.java
@@ -14,8 +14,12 @@
package com.android.launcher3.uioverrides.plugins;
+import static android.content.pm.PackageManager.MATCH_DISABLED_COMPONENTS;
+
import android.content.ComponentName;
import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ResolveInfo;
import com.android.launcher3.util.MainThreadInitializedObject;
import com.android.systemui.plugins.Plugin;
@@ -24,6 +28,9 @@
import com.android.systemui.shared.plugins.PluginManagerImpl;
import com.android.systemui.shared.plugins.PluginPrefs;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
import java.util.Set;
public class PluginManagerWrapper {
@@ -75,4 +82,27 @@
public static boolean hasPlugins(Context context) {
return PluginPrefs.hasPlugins(context);
}
+
+ public void dump(PrintWriter pw) {
+ final List<ComponentName> enabledPlugins = new ArrayList<>();
+ final List<ComponentName> disabledPlugins = new ArrayList<>();
+ for (String action : getPluginActions()) {
+ for (ResolveInfo resolveInfo : mContext.getPackageManager().queryIntentServices(
+ new Intent(action), MATCH_DISABLED_COMPONENTS)) {
+ ComponentName installedPlugin = new ComponentName(
+ resolveInfo.serviceInfo.packageName, resolveInfo.serviceInfo.name);
+ if (mPluginEnabler.isEnabled(installedPlugin)) {
+ enabledPlugins.add(installedPlugin);
+ } else {
+ disabledPlugins.add(installedPlugin);
+ }
+ }
+ }
+
+ pw.println("PluginManager:");
+ pw.println(" numEnabledPlugins=" + enabledPlugins.size());
+ pw.println(" numDisabledPlugins=" + disabledPlugins.size());
+ pw.println(" enabledPlugins=" + enabledPlugins);
+ pw.println(" disabledPlugins=" + disabledPlugins);
+ }
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java
index 99b2a81..d5ce734 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java
@@ -25,7 +25,7 @@
import static com.android.launcher3.anim.Interpolators.ACCEL;
import static com.android.launcher3.anim.Interpolators.DEACCEL;
import static com.android.launcher3.anim.Interpolators.LINEAR;
-import static com.android.launcher3.config.FeatureFlags.QUICKSTEP_SPRINGS;
+import static com.android.launcher3.config.FeatureFlags.UNSTABLE_SPRINGS;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_OVERVIEW_DISABLED;
import android.animation.TimeInterpolator;
@@ -277,7 +277,7 @@
private void handleFirstSwipeToOverview(final ValueAnimator animator,
final long expectedDuration, final LauncherState targetState, final float velocity,
final boolean isFling) {
- if (QUICKSTEP_SPRINGS.get() && mFromState == OVERVIEW && mToState == ALL_APPS
+ if (UNSTABLE_SPRINGS.get() && mFromState == OVERVIEW && mToState == ALL_APPS
&& targetState == OVERVIEW) {
mFinishFastOnSecondTouch = true;
} else if (mFromState == NORMAL && mToState == OVERVIEW && targetState == OVERVIEW) {
diff --git a/quickstep/src/com/android/quickstep/MultiStateCallback.java b/quickstep/src/com/android/quickstep/MultiStateCallback.java
index 6c65e01..b3875ae 100644
--- a/quickstep/src/com/android/quickstep/MultiStateCallback.java
+++ b/quickstep/src/com/android/quickstep/MultiStateCallback.java
@@ -130,7 +130,7 @@
final LinkedList<Runnable> callbacks;
if (mCallbacks.indexOfKey(stateMask) >= 0) {
callbacks = mCallbacks.get(stateMask);
- if (FeatureFlags.IS_DOGFOOD_BUILD && callbacks.contains(callback)) {
+ if (FeatureFlags.IS_STUDIO_BUILD && callbacks.contains(callback)) {
throw new IllegalStateException("Existing callback for state found");
}
} else {
diff --git a/quickstep/src/com/android/quickstep/OverviewComponentObserver.java b/quickstep/src/com/android/quickstep/OverviewComponentObserver.java
index 73b78db..85ef4c6 100644
--- a/quickstep/src/com/android/quickstep/OverviewComponentObserver.java
+++ b/quickstep/src/com/android/quickstep/OverviewComponentObserver.java
@@ -35,6 +35,7 @@
import com.android.systemui.shared.system.PackageManagerWrapper;
+import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Objects;
@@ -233,4 +234,13 @@
public BaseActivityInterface getActivityInterface() {
return mActivityInterface;
}
+
+ public void dump(PrintWriter pw) {
+ pw.println("OverviewComponentObserver:");
+ pw.println(" isDefaultHome=" + mIsDefaultHome);
+ pw.println(" isHomeDisabled=" + mIsHomeDisabled);
+ pw.println(" homeAndOverviewSame=" + mIsHomeAndOverviewSame);
+ pw.println(" overviewIntent=" + mOverviewIntent);
+ pw.println(" homeIntent=" + mCurrentHomeIntent);
+ }
}
diff --git a/quickstep/src/com/android/quickstep/QuickstepProcessInitializer.java b/quickstep/src/com/android/quickstep/QuickstepProcessInitializer.java
index 9817e32..32268a4 100644
--- a/quickstep/src/com/android/quickstep/QuickstepProcessInitializer.java
+++ b/quickstep/src/com/android/quickstep/QuickstepProcessInitializer.java
@@ -15,11 +15,6 @@
*/
package com.android.quickstep;
-import static android.content.Context.MODE_PRIVATE;
-
-import static com.android.launcher3.config.FeatureFlags.ENABLE_OVERVIEW_ACTIONS;
-import static com.android.launcher3.config.FeatureFlags.FLAGS_PREF_NAME;
-
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.UserManager;
@@ -27,7 +22,6 @@
import com.android.launcher3.BuildConfig;
import com.android.launcher3.MainProcessInitializer;
-import com.android.launcher3.config.FeatureFlags;
import com.android.systemui.shared.system.ThreadedRendererCompat;
@SuppressWarnings("unused")
@@ -55,22 +49,7 @@
super.init(context);
// Elevate GPU priority for Quickstep and Remote animations.
- ThreadedRendererCompat.setContextPriority(ThreadedRendererCompat.EGL_CONTEXT_PRIORITY_HIGH_IMG);
-
- // Force disable some feature flags based on the system ui navigation mode.
- SysUINavigationMode.Mode currMode = SysUINavigationMode.INSTANCE.get(context)
- .addModeChangeListener(mode -> disableFeatureFlagsForSysuiNavMode(context, mode));
- disableFeatureFlagsForSysuiNavMode(context, currMode);
- }
-
- private void disableFeatureFlagsForSysuiNavMode(Context ctx, SysUINavigationMode.Mode mode) {
- if (mode == SysUINavigationMode.Mode.TWO_BUTTONS) {
- ctx.getSharedPreferences(FLAGS_PREF_NAME, MODE_PRIVATE)
- .edit()
- .putBoolean(ENABLE_OVERVIEW_ACTIONS.key, false)
- .apply();
-
- FeatureFlags.initialize(ctx);
- }
+ ThreadedRendererCompat.setContextPriority(
+ ThreadedRendererCompat.EGL_CONTEXT_PRIORITY_HIGH_IMG);
}
}
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java b/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java
index acf61b4..0f98b32 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java
@@ -62,6 +62,12 @@
mListeners.remove(listener);
}
+ @UiThread
+ public void removeAllListeners() {
+ Preconditions.assertUIThread();
+ mListeners.clear();
+ }
+
public void notifyAnimationCanceled() {
mCancelled = true;
onAnimationCanceled(null);
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationController.java b/quickstep/src/com/android/quickstep/RecentsAnimationController.java
index d3be0b9..21a4918 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationController.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationController.java
@@ -198,6 +198,7 @@
if (mInputConsumerController != null) {
mInputConsumerController.setInputListener(null);
}
+ mInputProxySupplier = null;
}
private boolean onInputConsumerEvent(InputEvent ev) {
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
index d0afb21..4b33d21 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
@@ -180,7 +180,7 @@
mDefaultDisplay.addChangeListener(this);
}
- if (mMode == NO_BUTTON) {
+ if (newMode == NO_BUTTON) {
mExclusionListener.register();
} else {
mExclusionListener.unregister();
diff --git a/quickstep/src/com/android/quickstep/SysUINavigationMode.java b/quickstep/src/com/android/quickstep/SysUINavigationMode.java
index 388b323..375e589 100644
--- a/quickstep/src/com/android/quickstep/SysUINavigationMode.java
+++ b/quickstep/src/com/android/quickstep/SysUINavigationMode.java
@@ -122,6 +122,12 @@
}
}
+ /** @return Whether we can remove the shelf from overview. */
+ public static boolean removeShelfFromOverview(Context context) {
+ // The shelf is core to the two-button mode model, so we need to continue supporting it.
+ return getMode(context) != Mode.TWO_BUTTONS;
+ }
+
public interface NavigationModeChangeListener {
void onNavigationModeChanged(Mode newMode);
diff --git a/quickstep/src/com/android/quickstep/SystemUiProxy.java b/quickstep/src/com/android/quickstep/SystemUiProxy.java
index d9cb240..458d6a9 100644
--- a/quickstep/src/com/android/quickstep/SystemUiProxy.java
+++ b/quickstep/src/com/android/quickstep/SystemUiProxy.java
@@ -29,7 +29,6 @@
import android.view.MotionEvent;
import com.android.launcher3.util.MainThreadInitializedObject;
-import com.android.quickstep.util.SharedApiCompat;
import com.android.systemui.shared.recents.ISystemUiProxy;
/**
@@ -278,7 +277,7 @@
mLastShelfVisible = visible;
mLastShelfHeight = shelfHeight;
try {
- SharedApiCompat.setShelfHeight(mSystemUiProxy, visible, shelfHeight);
+ mSystemUiProxy.setShelfHeight(visible, shelfHeight);
} catch (RemoteException e) {
Log.w(TAG, "Failed call setShelfHeight visible: " + visible
+ " height: " + shelfHeight, e);
diff --git a/quickstep/src/com/android/quickstep/TaskAnimationManager.java b/quickstep/src/com/android/quickstep/TaskAnimationManager.java
index e3e8ace..6902e37 100644
--- a/quickstep/src/com/android/quickstep/TaskAnimationManager.java
+++ b/quickstep/src/com/android/quickstep/TaskAnimationManager.java
@@ -55,7 +55,7 @@
// Notify if recents animation is still running
if (mController != null) {
String msg = "New recents animation started before old animation completed";
- if (FeatureFlags.IS_DOGFOOD_BUILD) {
+ if (FeatureFlags.IS_STUDIO_BUILD) {
throw new IllegalArgumentException(msg);
} else {
Log.e("TaskAnimationManager", msg, new Exception());
@@ -156,9 +156,9 @@
mTargets.release();
}
- // Remove gesture state from callbacks
- if (mCallbacks != null && mLastGestureState != null) {
- mCallbacks.removeListener(mLastGestureState);
+ // Clean up all listeners to ensure we don't get subsequent callbacks
+ if (mCallbacks != null) {
+ mCallbacks.removeAllListeners();
}
mController = null;
diff --git a/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialActivity.java b/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialActivity.java
index 295ab48..d2a0951 100644
--- a/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialActivity.java
+++ b/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialActivity.java
@@ -16,7 +16,10 @@
package com.android.quickstep.interaction;
import android.graphics.Color;
+import android.graphics.Rect;
import android.os.Bundle;
+import android.util.DisplayMetrics;
+import android.view.Display;
import android.view.View;
import android.view.Window;
@@ -26,6 +29,7 @@
import com.android.quickstep.interaction.BackGestureTutorialFragment.TutorialStep;
import com.android.quickstep.interaction.BackGestureTutorialFragment.TutorialType;
+import java.util.List;
import java.util.Optional;
/** Shows the Back gesture interactive tutorial in full screen mode. */
@@ -47,6 +51,12 @@
}
@Override
+ public void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ disableSystemGestures();
+ }
+
+ @Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
if (hasFocus) {
@@ -70,4 +80,14 @@
| View.SYSTEM_UI_FLAG_FULLSCREEN);
getWindow().setNavigationBarColor(Color.TRANSPARENT);
}
+
+ private void disableSystemGestures() {
+ Display display = getDisplay();
+ if (display != null) {
+ DisplayMetrics metrics = new DisplayMetrics();
+ display.getMetrics(metrics);
+ getWindow().setSystemGestureExclusionRects(
+ List.of(new Rect(0, 0, metrics.widthPixels, metrics.heightPixels)));
+ }
+ }
}
diff --git a/quickstep/src/com/android/quickstep/util/BinderTracker.java b/quickstep/src/com/android/quickstep/util/BinderTracker.java
index 32d0d53..cb04e5b 100644
--- a/quickstep/src/com/android/quickstep/util/BinderTracker.java
+++ b/quickstep/src/com/android/quickstep/util/BinderTracker.java
@@ -31,7 +31,7 @@
private static final String TAG = "BinderTracker";
public static void start() {
- if (!FeatureFlags.IS_DOGFOOD_BUILD) {
+ if (!FeatureFlags.IS_STUDIO_BUILD) {
Log.wtf(TAG, "Accessing tracker in released code.", new Exception());
return;
}
@@ -40,7 +40,7 @@
}
public static void stop() {
- if (!FeatureFlags.IS_DOGFOOD_BUILD) {
+ if (!FeatureFlags.IS_STUDIO_BUILD) {
Log.wtf(TAG, "Accessing tracker in released code.", new Exception());
return;
}
diff --git a/quickstep/src/com/android/quickstep/util/LayoutUtils.java b/quickstep/src/com/android/quickstep/util/LayoutUtils.java
index b249f48..1d8a79f 100644
--- a/quickstep/src/com/android/quickstep/util/LayoutUtils.java
+++ b/quickstep/src/com/android/quickstep/util/LayoutUtils.java
@@ -16,6 +16,7 @@
package com.android.quickstep.util;
import static com.android.launcher3.config.FeatureFlags.ENABLE_OVERVIEW_ACTIONS;
+import static com.android.quickstep.SysUINavigationMode.removeShelfFromOverview;
import static java.lang.annotation.RetentionPolicy.SOURCE;
@@ -59,7 +60,7 @@
} else {
Resources res = context.getResources();
- if (ENABLE_OVERVIEW_ACTIONS.get()) {
+ if (ENABLE_OVERVIEW_ACTIONS.get() && removeShelfFromOverview(context)) {
//TODO: this needs to account for the swipe gesture height and accessibility
// UI when shown.
extraSpace = 0;
@@ -83,6 +84,7 @@
float taskWidth, taskHeight, paddingHorz;
Resources res = context.getResources();
Rect insets = dp.getInsets();
+ final boolean overviewActionsEnabled = ENABLE_OVERVIEW_ACTIONS.get();
if (dp.isMultiWindowMode) {
if (multiWindowStrategy == MULTI_WINDOW_STRATEGY_HALF_SCREEN) {
@@ -112,7 +114,7 @@
final int paddingResId;
if (dp.isVerticalBarLayout()) {
paddingResId = R.dimen.landscape_task_card_horz_space;
- } else if (ENABLE_OVERVIEW_ACTIONS.get()) {
+ } else if (overviewActionsEnabled && removeShelfFromOverview(context)) {
paddingResId = R.dimen.portrait_task_card_horz_space_big_overview;
} else {
paddingResId = R.dimen.portrait_task_card_horz_space;
@@ -120,9 +122,11 @@
paddingHorz = res.getDimension(paddingResId);
}
- float topIconMargin = res.getDimension(R.dimen.task_thumbnail_top_margin);
- float bottomMargin = thumbnailBottomMargin(res);
- float paddingVert = res.getDimension(R.dimen.task_card_vert_space);
+ float topIconMargin = res.getDimension(R.dimen.task_thumbnail_top_margin);
+ float bottomMargin = thumbnailBottomMargin(context);
+
+ float paddingVert = overviewActionsEnabled && removeShelfFromOverview(context)
+ ? 0 : res.getDimension(R.dimen.task_card_vert_space);
// Note this should be same as dp.availableWidthPx and dp.availableHeightPx unless
// we override the insets ourselves.
@@ -140,14 +144,14 @@
// Center in the visible space
float x = insets.left + (launcherVisibleWidth - outWidth) / 2;
float y = insets.top + Math.max(topIconMargin,
- (launcherVisibleHeight - extraVerticalSpace - outHeight) / 2);
+ (launcherVisibleHeight - extraVerticalSpace - outHeight - bottomMargin) / 2);
outRect.set(Math.round(x), Math.round(y),
Math.round(x) + Math.round(outWidth), Math.round(y) + Math.round(outHeight));
}
public static int getShelfTrackingDistance(Context context, DeviceProfile dp) {
// Track the bottom of the window.
- if (ENABLE_OVERVIEW_ACTIONS.get()) {
+ if (ENABLE_OVERVIEW_ACTIONS.get() && removeShelfFromOverview(context)) {
Rect taskSize = new Rect();
calculateLauncherTaskSize(context, dp, taskSize);
return (dp.heightPx - taskSize.height()) / 2;
@@ -162,9 +166,9 @@
* Get the margin that the task thumbnail view should use.
* @return the margin in pixels.
*/
- public static int thumbnailBottomMargin(Resources resources) {
- if (ENABLE_OVERVIEW_ACTIONS.get()) {
- return resources.getDimensionPixelSize(R.dimen.overview_actions_height);
+ public static int thumbnailBottomMargin(Context context) {
+ if (ENABLE_OVERVIEW_ACTIONS.get() && removeShelfFromOverview(context)) {
+ return context.getResources().getDimensionPixelSize(R.dimen.overview_actions_height);
} else {
return 0;
}
diff --git a/quickstep/src/com/android/quickstep/views/ShelfScrimView.java b/quickstep/src/com/android/quickstep/views/ShelfScrimView.java
index 7ed1e21..1ce3549 100644
--- a/quickstep/src/com/android/quickstep/views/ShelfScrimView.java
+++ b/quickstep/src/com/android/quickstep/views/ShelfScrimView.java
@@ -160,7 +160,8 @@
Context context = getContext();
if ((OVERVIEW.getVisibleElements(mLauncher) & ALL_APPS_HEADER_EXTRA) == 0) {
mDragHandleProgress = 1;
- if (FeatureFlags.ENABLE_OVERVIEW_ACTIONS.get()) {
+ if (FeatureFlags.ENABLE_OVERVIEW_ACTIONS.get()
+ && SysUINavigationMode.removeShelfFromOverview(context)) {
// Fade in all apps background quickly to distinguish from swiping from nav bar.
mMidAlpha = Themes.getAttrInteger(context, R.attr.allAppsInterimScrimAlpha);
mMidProgress = OverviewState.getDefaultVerticalProgress(mLauncher);
diff --git a/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java b/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java
index 97424bb..1229a63 100644
--- a/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java
+++ b/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java
@@ -197,7 +197,8 @@
Wait.atMost(() -> "Switching nav mode: "
+ launcher.getNavigationModeMismatchError(),
- () -> launcher.getNavigationModeMismatchError() == null, WAIT_TIME_MS, launcher);
+ () -> launcher.getNavigationModeMismatchError() == null,
+ 60000 /* b/148422894 */, launcher);
return true;
}
diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
index d2f5d8f..724af66 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
@@ -25,7 +25,6 @@
import static org.junit.Assert.assertTrue;
import android.content.Intent;
-import android.os.RemoteException;
import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
@@ -48,7 +47,6 @@
import org.junit.After;
import org.junit.Before;
-import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -84,17 +82,6 @@
}
@Test
- @PortraitLandscape
- @Ignore // Enable after b/131115533
- public void testPressRecentAppsLauncherAndGetOverview() throws RemoteException {
- mDevice.pressRecentApps();
- waitForState("Launcher internal state didn't switch to Overview",
- () -> LauncherState.OVERVIEW);
-
- assertNotNull("getOverview() returned null", mLauncher.getOverview());
- }
-
- @Test
@NavigationModeSwitch
@PortraitLandscape
public void testWorkspaceSwitchToAllApps() {
@@ -271,10 +258,8 @@
@Test
@NavigationModeSwitch
@PortraitLandscape
- // b/143285809 Remove @Stability on 02/21/20 if the test doesn't flake.
+ // b/143285809 Remove @Stability on 02/27/20 if the test doesn't flake.
@TestStabilityRule.Stability(flavors = LOCAL | UNBUNDLED_POSTSUBMIT)
- // b/143285809
- @Ignore
public void testQuickSwitchFromApp() throws Exception {
startTestActivity(2);
startTestActivity(3);
diff --git a/res/layout/user_folder_icon_normalized.xml b/res/layout/user_folder_icon_normalized.xml
index 893d796..923352e 100644
--- a/res/layout/user_folder_icon_normalized.xml
+++ b/res/layout/user_folder_icon_normalized.xml
@@ -46,9 +46,9 @@
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
+ style="@style/TextHeadline"
android:layout_weight="1"
android:background="@android:color/transparent"
- android:fontFamily="sans-serif-condensed"
android:textStyle="bold"
android:gravity="center_horizontal"
android:hint="@string/folder_hint_text"
@@ -58,7 +58,7 @@
android:singleLine="true"
android:textColor="?attr/folderTextColor"
android:textColorHighlight="?android:attr/colorControlHighlight"
- android:textColorHint="?attr/folderTextColor"
+ android:textColorHint="?attr/folderHintColor"
android:textSize="@dimen/folder_label_text_size" />
<com.android.launcher3.pageindicators.PageIndicatorDots
diff --git a/res/layout/work_apps_paused.xml b/res/layout/work_apps_paused.xml
index 5607e78..33502d0 100644
--- a/res/layout/work_apps_paused.xml
+++ b/res/layout/work_apps_paused.xml
@@ -23,14 +23,14 @@
<ImageView
android:id="@+id/icon"
android:contentDescription="@string/work_apps_paused_title"
- android:layout_width="48dp"
- android:layout_height="48dp"
- android:tint="?attr/folderTextColor"
+ android:layout_width="32dp"
+ android:layout_height="32dp"
+ android:tint="?attr/workProfileOverlayTextColor"
android:src="@drawable/ic_corp_off" />
<TextView
style="@style/TextHeadline"
- android:textColor="?attr/folderTextColor"
+ android:textColor="?attr/workProfileOverlayTextColor"
android:id="@+id/work_apps_paused_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@@ -43,7 +43,7 @@
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:textColor="?attr/folderTextColor"
+ android:textColor="?attr/workProfileOverlayTextColor"
android:text="@string/work_apps_paused_body"
android:textAlignment="center"
android:textSize="16sp" />
diff --git a/res/layout/work_tab_footer.xml b/res/layout/work_tab_footer.xml
index db95416..2cffedd 100644
--- a/res/layout/work_tab_footer.xml
+++ b/res/layout/work_tab_footer.xml
@@ -32,6 +32,8 @@
android:layout_weight="1"
android:drawableStart="@drawable/ic_corp"
android:drawablePadding="3dp"
+ android:drawableTint="?attr/workProfileOverlayTextColor"
+ android:textColor="?attr/workProfileOverlayTextColor"
android:layout_height="wrap_content"
android:ellipsize="end"
android:gravity="center_vertical"
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index de17eb7..5a15ec6 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -40,6 +40,8 @@
<attr name="folderIconRadius" format="float" />
<attr name="folderIconBorderColor" format="color" />
<attr name="folderTextColor" format="color" />
+ <attr name="folderHintColor" format="color" />
+ <attr name="workProfileOverlayTextColor" format="color" />
<!-- BubbleTextView specific attributes. -->
<declare-styleable name="BubbleTextView">
@@ -115,6 +117,7 @@
<attr name="numFolderColumns" format="integer" />
<!-- numHotseatIcons defaults to numColumns, if not specified -->
<attr name="numHotseatIcons" format="integer" />
+ <attr name="dbFile" format="string" />
<attr name="defaultLayoutId" format="reference" />
<attr name="demoModeLayoutId" format="reference" />
</declare-styleable>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index bfa92f7..0775c0c 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -142,7 +142,7 @@
<string name="uninstall_system_app_text">This is a system app and can\'t be uninstalled.</string>
<!-- Default folder title -->
- <string name="folder_hint_text">Unnamed Folder</string>
+ <string name="folder_hint_text">Edit Name</string>
<!-- Accessibility -->
<!-- The format string for when an app is temporarily disabled. -->
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 35ae49c..cee268b 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -48,7 +48,9 @@
<item name="folderFillColor">#CDFFFFFF</item>
<item name="folderIconBorderColor">?android:attr/colorPrimary</item>
<item name="folderTextColor">#FF212121</item>
+ <item name="folderHintColor">#FF616161</item>
<item name="loadingIconColor">#CCFFFFFF</item>
+ <item name="workProfileOverlayTextColor">#FF212121</item>
<item name="android:windowTranslucentStatus">false</item>
<item name="android:windowTranslucentNavigation">false</item>
@@ -76,6 +78,7 @@
<item name="folderFillColor">#CDFFFFFF</item>
<item name="folderIconBorderColor">#FF80868B</item>
<item name="folderTextColor">?attr/workspaceTextColor</item>
+
</style>
<style name="LauncherTheme.Dark" parent="@style/LauncherTheme">
@@ -96,8 +99,10 @@
<item name="folderFillColor">#DD3C4043</item> <!-- 87% GM2 800 -->
<item name="folderIconBorderColor">#FF80868B</item>
<item name="folderTextColor">@android:color/white</item>
+ <item name="folderHintColor">#FFCCCCCC</item>
<item name="isMainColorDark">true</item>
<item name="loadingIconColor">#99FFFFFF</item>
+ <item name="workProfileOverlayTextColor">@android:color/white</item>
</style>
<style name="LauncherTheme.Dark.DarkMainColor" parent="@style/LauncherTheme.Dark">
diff --git a/res/xml/device_profiles.xml b/res/xml/device_profiles.xml
index 82547d5..1c99dfc 100644
--- a/res/xml/device_profiles.xml
+++ b/res/xml/device_profiles.xml
@@ -24,6 +24,7 @@
launcher:numFolderRows="2"
launcher:numFolderColumns="3"
launcher:numHotseatIcons="3"
+ launcher:dbFile="launcher_3_by_3.db"
launcher:defaultLayoutId="@xml/default_workspace_3x3" >
<display-option
@@ -51,6 +52,7 @@
launcher:numFolderRows="3"
launcher:numFolderColumns="4"
launcher:numHotseatIcons="4"
+ launcher:dbFile="launcher_4_by_4.db"
launcher:defaultLayoutId="@xml/default_workspace_4x4" >
<display-option
@@ -102,6 +104,7 @@
launcher:numFolderRows="4"
launcher:numFolderColumns="4"
launcher:numHotseatIcons="5"
+ launcher:dbFile="launcher.db"
launcher:defaultLayoutId="@xml/default_workspace_5x5" >
<display-option
diff --git a/robolectric_tests/Android.mk b/robolectric_tests/Android.mk
index 6059981..7c7e73c 100644
--- a/robolectric_tests/Android.mk
+++ b/robolectric_tests/Android.mk
@@ -52,7 +52,7 @@
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
LOCAL_TEST_PACKAGE := Launcher3
-LOCAL_INSTRUMENT_SOURCE_DIRS := $(dir $(LOCAL_PATH))../src
+LOCAL_INSTRUMENT_SOURCE_DIRS := packages/apps/Launcher3/src
LOCAL_ROBOTEST_TIMEOUT := 36000
diff --git a/robolectric_tests/src/com/android/launcher3/folder/FolderNameProviderTest.java b/robolectric_tests/src/com/android/launcher3/folder/FolderNameProviderTest.java
index f769055..74ef55e 100644
--- a/robolectric_tests/src/com/android/launcher3/folder/FolderNameProviderTest.java
+++ b/robolectric_tests/src/com/android/launcher3/folder/FolderNameProviderTest.java
@@ -15,7 +15,7 @@
*/
package com.android.launcher3.folder;
-import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertEquals;
import android.content.ComponentName;
import android.content.Context;
@@ -61,15 +61,16 @@
ArrayList<WorkspaceItemInfo> list = new ArrayList<>();
list.add(mItem1);
list.add(mItem2);
- String[] suggestedNameOut = new String[FolderNameProvider.SUGGEST_MAX];
- new FolderNameProvider().getSuggestedFolderName(mContext, list, suggestedNameOut);
- assertTrue(suggestedNameOut[0].equals("Work"));
+ FolderNameInfo[] nameInfos =
+ new FolderNameInfo[FolderNameProvider.SUGGEST_MAX];
+ new FolderNameProvider().getSuggestedFolderName(mContext, list, nameInfos);
+ assertEquals("Work", nameInfos[0].getLabel());
- suggestedNameOut[0] = "candidate1";
- suggestedNameOut[1] = "candidate2";
- suggestedNameOut[2] = "candidate3";
- new FolderNameProvider().getSuggestedFolderName(mContext, list, suggestedNameOut);
- assertTrue(suggestedNameOut[3].equals("Work"));
+ nameInfos[0] = new FolderNameInfo("candidate1", 0.9);
+ nameInfos[1] = new FolderNameInfo("candidate2", 0.8);
+ nameInfos[2] = new FolderNameInfo("candidate3", 0.7);
+ new FolderNameProvider().getSuggestedFolderName(mContext, list, nameInfos);
+ assertEquals("Work", nameInfos[3].getLabel());
}
}
diff --git a/robolectric_tests/src/com/android/launcher3/logging/FileLogTest.java b/robolectric_tests/src/com/android/launcher3/logging/FileLogTest.java
index 48b5a45..95a4146 100644
--- a/robolectric_tests/src/com/android/launcher3/logging/FileLogTest.java
+++ b/robolectric_tests/src/com/android/launcher3/logging/FileLogTest.java
@@ -49,8 +49,9 @@
@After
public void tearDown() {
// Clear existing logs
- new File(mTempDir, "log-0").delete();
- new File(mTempDir, "log-1").delete();
+ for (int i = 0; i < FileLog.LOG_DAYS; i++) {
+ new File(mTempDir, "log-" + i).delete();
+ }
mTempDir.delete();
mTestActive = false;
@@ -89,8 +90,9 @@
Calendar threeDaysAgo = Calendar.getInstance();
threeDaysAgo.add(Calendar.HOUR, -72);
- new File(mTempDir, "log-0").setLastModified(threeDaysAgo.getTimeInMillis());
- new File(mTempDir, "log-1").setLastModified(threeDaysAgo.getTimeInMillis());
+ for (int i = 0; i < FileLog.LOG_DAYS; i++) {
+ new File(mTempDir, "log-" + i).setLastModified(threeDaysAgo.getTimeInMillis());
+ }
FileLog.print("Testing", "abracadabra", new Exception("cat! cat!"));
writer = new StringWriter();
diff --git a/src/com/android/launcher3/BaseActivity.java b/src/com/android/launcher3/BaseActivity.java
index f3c5191..217a41c 100644
--- a/src/com/android/launcher3/BaseActivity.java
+++ b/src/com/android/launcher3/BaseActivity.java
@@ -42,6 +42,7 @@
import com.android.launcher3.logging.UserEventDispatcher;
import com.android.launcher3.logging.UserEventDispatcher.UserEventDelegate;
import com.android.launcher3.testing.TestLogging;
+import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.launcher3.util.SystemUiController;
import com.android.launcher3.util.ViewCache;
@@ -330,9 +331,7 @@
return;
}
try {
- if (Utilities.IS_RUNNING_IN_TEST_HARNESS) {
- TestLogging.recordEvent("start: shortcut: " + packageName);
- }
+ TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "start: shortcut", packageName);
getSystemService(LauncherApps.class).startShortcut(packageName, id, sourceBounds,
startActivityOptions, user);
} catch (SecurityException | IllegalStateException e) {
diff --git a/src/com/android/launcher3/BaseDraggingActivity.java b/src/com/android/launcher3/BaseDraggingActivity.java
index dda38b3..9f3b48f 100644
--- a/src/com/android/launcher3/BaseDraggingActivity.java
+++ b/src/com/android/launcher3/BaseDraggingActivity.java
@@ -16,6 +16,8 @@
package com.android.launcher3;
+import static com.android.launcher3.util.DefaultDisplay.CHANGE_ROTATION;
+
import android.app.ActivityOptions;
import android.content.ActivityNotFoundException;
import android.content.Intent;
@@ -37,9 +39,12 @@
import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.model.AppLaunchTracker;
import com.android.launcher3.testing.TestLogging;
+import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.touch.ItemClickHandler;
-import com.android.launcher3.uioverrides.DisplayRotationListener;
import com.android.launcher3.uioverrides.WallpaperColorInfo;
+import com.android.launcher3.util.DefaultDisplay;
+import com.android.launcher3.util.DefaultDisplay.DisplayInfoChangeListener;
+import com.android.launcher3.util.DefaultDisplay.Info;
import com.android.launcher3.util.PackageManagerHelper;
import com.android.launcher3.util.Themes;
import com.android.launcher3.util.TraceHelper;
@@ -48,7 +53,7 @@
* Extension of BaseActivity allowing support for drag-n-drop
*/
public abstract class BaseDraggingActivity extends BaseActivity
- implements WallpaperColorInfo.OnChangeListener {
+ implements WallpaperColorInfo.OnChangeListener, DisplayInfoChangeListener {
private static final String TAG = "BaseDraggingActivity";
@@ -63,8 +68,6 @@
private int mThemeRes = R.style.AppTheme;
- private DisplayRotationListener mRotationListener;
-
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -72,7 +75,7 @@
mIsSafeModeEnabled = TraceHelper.whitelistIpcs("isSafeMode",
() -> getPackageManager().isSafeMode());
- mRotationListener = new DisplayRotationListener(this, this::onDeviceRotationChanged);
+ DefaultDisplay.INSTANCE.get(this).addChangeListener(this);
// Update theme
WallpaperColorInfo.INSTANCE.get(this).addOnChangeListener(this);
@@ -167,9 +170,7 @@
startShortcutIntentSafely(intent, optsBundle, item, sourceContainer);
} else if (user == null || user.equals(Process.myUserHandle())) {
// Could be launching some bookkeeping activity
- if (Utilities.IS_RUNNING_IN_TEST_HARNESS) {
- TestLogging.recordEvent("start: activity: " + intent);
- }
+ TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "start: activity", intent);
startActivity(intent, optsBundle);
AppLaunchTracker.INSTANCE.get(this).onStartApp(intent.getComponent(),
Process.myUserHandle(), sourceContainer);
@@ -238,7 +239,7 @@
protected void onDestroy() {
super.onDestroy();
WallpaperColorInfo.INSTANCE.get(this).removeOnChangeListener(this);
- mRotationListener.disable();
+ DefaultDisplay.INSTANCE.get(this).removeChangeListener(this);
}
public void runOnceOnStart(Runnable action) {
@@ -251,15 +252,13 @@
protected void onDeviceProfileInitiated() {
if (mDeviceProfile.isVerticalBarLayout()) {
- mRotationListener.enable();
mDeviceProfile.updateIsSeascape(this);
- } else {
- mRotationListener.disable();
}
}
- private void onDeviceRotationChanged() {
- if (mDeviceProfile.updateIsSeascape(this)) {
+ @Override
+ public void onDisplayInfoChanged(Info info, int flags) {
+ if ((flags & CHANGE_ROTATION) != 0 && mDeviceProfile.updateIsSeascape(this)) {
reapplyUi();
}
}
diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java
index ac61c49..8718820 100644
--- a/src/com/android/launcher3/CellLayout.java
+++ b/src/com/android/launcher3/CellLayout.java
@@ -538,7 +538,7 @@
try {
dispatchRestoreInstanceState(states);
} catch (IllegalArgumentException ex) {
- if (FeatureFlags.IS_DOGFOOD_BUILD) {
+ if (FeatureFlags.IS_STUDIO_BUILD) {
throw ex;
}
// Mismatched viewId / viewType preventing restore. Skip restore on production builds.
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index 0d71da4..c049069 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -271,7 +271,7 @@
// In multi-window mode, we can have widthPx = availableWidthPx
// and heightPx = availableHeightPx because Launcher uses the InvariantDeviceProfiles'
// widthPx and heightPx values where it's needed.
- DeviceProfile profile = new DeviceProfile(context, inv, originalIdp, mwSize, mwSize,
+ DeviceProfile profile = new DeviceProfile(context, inv, null, mwSize, mwSize,
mwSize.x, mwSize.y, isLandscape, true);
// If there isn't enough vertical cell padding with the labels displayed, hide the labels.
diff --git a/src/com/android/launcher3/FocusHelper.java b/src/com/android/launcher3/FocusHelper.java
index 466b7b2..f07040d 100644
--- a/src/com/android/launcher3/FocusHelper.java
+++ b/src/com/android/launcher3/FocusHelper.java
@@ -96,7 +96,7 @@
}
if (!(v.getParent() instanceof ShortcutAndWidgetContainer)) {
- if (FeatureFlags.IS_DOGFOOD_BUILD) {
+ if (FeatureFlags.IS_STUDIO_BUILD) {
throw new IllegalStateException("Parent of the focused item is not supported.");
} else {
return false;
diff --git a/src/com/android/launcher3/FolderInfo.java b/src/com/android/launcher3/FolderInfo.java
index 787eee1..336e423 100644
--- a/src/com/android/launcher3/FolderInfo.java
+++ b/src/com/android/launcher3/FolderInfo.java
@@ -48,6 +48,8 @@
public static final int FLAG_MANUAL_FOLDER_NAME = 0x00000008;
+ public static final String EXTRA_FOLDER_SUGGESTIONS = "suggest";
+
public int options;
public Intent suggestedFolderNames;
diff --git a/src/com/android/launcher3/InstallShortcutReceiver.java b/src/com/android/launcher3/InstallShortcutReceiver.java
index 3eb02b3..a5142d8 100644
--- a/src/com/android/launcher3/InstallShortcutReceiver.java
+++ b/src/com/android/launcher3/InstallShortcutReceiver.java
@@ -17,7 +17,6 @@
package com.android.launcher3;
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
-import static com.android.launcher3.util.ShortcutUtil.fetchAndUpdateShortcutIconAsync;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProviderInfo;
@@ -44,7 +43,6 @@
import androidx.annotation.Nullable;
import androidx.annotation.WorkerThread;
-import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.icons.BitmapInfo;
import com.android.launcher3.icons.GraphicsUtils;
import com.android.launcher3.icons.LauncherIcons;
@@ -61,7 +59,6 @@
import java.net.URISyntaxException;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
@@ -491,13 +488,8 @@
return Pair.create(si, null);
} else if (shortcutInfo != null) {
WorkspaceItemInfo itemInfo = new WorkspaceItemInfo(shortcutInfo, mContext);
- if (FeatureFlags.ENABLE_DEEP_SHORTCUT_ICON_CACHE.get()) {
- fetchAndUpdateShortcutIconAsync(mContext, itemInfo, shortcutInfo, true);
- } else {
- LauncherIcons li = LauncherIcons.obtain(mContext);
- itemInfo.bitmap = li.createShortcutIcon(shortcutInfo);
- li.recycle();
- }
+ LauncherAppState.getInstance(mContext).getIconCache().getShortcutIcon(
+ itemInfo, shortcutInfo);
return Pair.create(itemInfo, shortcutInfo);
} else if (providerInfo != null) {
LauncherAppWidgetProviderInfo info = LauncherAppWidgetProviderInfo
diff --git a/src/com/android/launcher3/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java
index a807e4f..857db8e 100644
--- a/src/com/android/launcher3/InvariantDeviceProfile.java
+++ b/src/com/android/launcher3/InvariantDeviceProfile.java
@@ -119,6 +119,7 @@
*/
public int numAllAppsColumns;
+ public String dbFile;
public int defaultLayoutId;
int demoModeLayoutId;
@@ -146,6 +147,7 @@
iconTextSize = p.iconTextSize;
numHotseatIcons = p.numHotseatIcons;
numAllAppsColumns = p.numAllAppsColumns;
+ dbFile = p.dbFile;
defaultLayoutId = p.defaultLayoutId;
demoModeLayoutId = p.demoModeLayoutId;
mExtraAttrs = p.mExtraAttrs;
@@ -292,6 +294,7 @@
numRows = closestProfile.numRows;
numColumns = closestProfile.numColumns;
numHotseatIcons = closestProfile.numHotseatIcons;
+ dbFile = closestProfile.dbFile;
defaultLayoutId = closestProfile.defaultLayoutId;
demoModeLayoutId = closestProfile.demoModeLayoutId;
numFolderRows = closestProfile.numFolderRows;
@@ -559,6 +562,7 @@
private final int numHotseatIcons;
+ private final String dbFile;
private final int defaultLayoutId;
private final int demoModeLayoutId;
@@ -571,6 +575,7 @@
numRows = a.getInt(R.styleable.GridDisplayOption_numRows, 0);
numColumns = a.getInt(R.styleable.GridDisplayOption_numColumns, 0);
+ dbFile = a.getString(R.styleable.GridDisplayOption_dbFile);
defaultLayoutId = a.getResourceId(
R.styleable.GridDisplayOption_defaultLayoutId, 0);
demoModeLayoutId = a.getResourceId(
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 80f8ae2..7e8377a 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -100,6 +100,7 @@
import com.android.launcher3.dragndrop.DragController;
import com.android.launcher3.dragndrop.DragLayer;
import com.android.launcher3.dragndrop.DragView;
+import com.android.launcher3.folder.Folder;
import com.android.launcher3.folder.FolderGridOrganizer;
import com.android.launcher3.folder.FolderIcon;
import com.android.launcher3.graphics.RotationMode;
@@ -1155,7 +1156,7 @@
/**
* Finds all the views we need and configure them properly.
*/
- private void setupViews() {
+ protected void setupViews() {
mDragLayer = findViewById(R.id.drag_layer);
mFocusHandler = mDragLayer.getFocusIndicatorHelper();
mWorkspace = mDragLayer.findViewById(R.id.workspace);
@@ -1191,11 +1192,6 @@
mDropTargetBar.setup(mDragController);
mAllAppsController.setupViews(mAppsView);
-
- if (FeatureFlags.ENABLE_OVERVIEW_ACTIONS.get()) {
- // Overview is above all other launcher elements, including qsb, so move it to the top.
- mOverviewPanel.bringToFront();
- }
}
/**
@@ -1745,8 +1741,8 @@
/**
* Creates and adds new folder to CellLayout
*/
- public FolderIcon addFolder(CellLayout layout, WorkspaceItemInfo info, int container,
- final int screenId, int cellX, int cellY) {
+ public FolderIcon addFolder(CellLayout layout, int container, final int screenId, int cellX,
+ int cellY) {
final FolderInfo folderInfo = new FolderInfo();
folderInfo.title = "";
@@ -1764,6 +1760,16 @@
}
/**
+ * Called when a workspace item is converted into a folder
+ */
+ public void folderCreatedFromItem(Folder folder, WorkspaceItemInfo itemInfo){}
+
+ /**
+ * Called when a folder is converted into a workspace item
+ */
+ public void folderConvertedToItem(Folder folder, WorkspaceItemInfo itemInfo) {}
+
+ /**
* Unbinds the view for the specified item, and removes the item and all its children.
*
* @param v the view being removed.
@@ -1805,17 +1811,13 @@
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
- if (Utilities.IS_RUNNING_IN_TEST_HARNESS) {
- TestLogging.recordEvent("Key event: " + event);
- }
+ TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "Key event", event);
return (event.getKeyCode() == KeyEvent.KEYCODE_HOME) || super.dispatchKeyEvent(event);
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
- if (Utilities.IS_RUNNING_IN_TEST_HARNESS && ev.getAction() != MotionEvent.ACTION_MOVE) {
- TestLogging.recordEvent("Touch event: " + ev);
- }
+ TestLogging.recordMotionEvent(TestProtocol.SEQUENCE_MAIN, "Touch event", ev);
return super.dispatchTouchEvent(ev);
}
@@ -2164,7 +2166,7 @@
Object tag = v.getTag();
String desc = "Collision while binding workspace item: " + item
+ ". Collides with " + tag;
- if (FeatureFlags.IS_DOGFOOD_BUILD) {
+ if (FeatureFlags.IS_STUDIO_BUILD) {
throw (new RuntimeException(desc));
} else {
Log.d(TAG, desc);
diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java
index 4cd038d..d77285d 100644
--- a/src/com/android/launcher3/LauncherAppState.java
+++ b/src/com/android/launcher3/LauncherAppState.java
@@ -91,7 +91,7 @@
Intent.ACTION_MANAGED_PROFILE_AVAILABLE,
Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE,
Intent.ACTION_MANAGED_PROFILE_UNLOCKED);
- if (FeatureFlags.IS_DOGFOOD_BUILD) {
+ if (FeatureFlags.IS_STUDIO_BUILD) {
mModelChangeReceiver.register(mContext, ACTION_FORCE_ROLOAD);
}
diff --git a/src/com/android/launcher3/LauncherAppWidgetProviderInfo.java b/src/com/android/launcher3/LauncherAppWidgetProviderInfo.java
index 228c07e..56cce78 100644
--- a/src/com/android/launcher3/LauncherAppWidgetProviderInfo.java
+++ b/src/com/android/launcher3/LauncherAppWidgetProviderInfo.java
@@ -7,10 +7,12 @@
import android.content.pm.PackageManager;
import android.graphics.Point;
import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
import android.os.Parcel;
import android.os.UserHandle;
-import com.android.launcher3.icons.ComponentWithLabel;
+import com.android.launcher3.icons.ComponentWithLabelAndIcon;
+import com.android.launcher3.icons.IconCache;
/**
* This class is a thin wrapper around the framework AppWidgetProviderInfo class. This class affords
@@ -19,7 +21,7 @@
* as opposed to a widget instance, and so should not be confused with {@link LauncherAppWidgetInfo}
*/
public class LauncherAppWidgetProviderInfo extends AppWidgetProviderInfo
- implements ComponentWithLabel {
+ implements ComponentWithLabelAndIcon {
public static final String CLS_CUSTOM_WIDGET_PREFIX = "#custom-widget-";
@@ -111,4 +113,9 @@
public final UserHandle getUser() {
return getProfile();
}
+
+ @Override
+ public Drawable getFullResIcon(IconCache cache) {
+ return cache.getFullResIcon(provider.getPackageName(), icon);
+ }
}
diff --git a/src/com/android/launcher3/LauncherFiles.java b/src/com/android/launcher3/LauncherFiles.java
index 9c4646b..25afb55 100644
--- a/src/com/android/launcher3/LauncherFiles.java
+++ b/src/com/android/launcher3/LauncherFiles.java
@@ -15,8 +15,13 @@
private static final String XML = ".xml";
public static final String LAUNCHER_DB = "launcher.db";
+ public static final String LAUNCHER_4_BY_4_DB = "launcher_4_by_4.db";
+ public static final String LAUNCHER_3_BY_3_DB = "launcher_3_by_3.db";
+ public static final String LAUNCHER_2_BY_2_DB = "launcher_2_by_2.db";
+ public static final String BACKUP_DB = "backup.db";
public static final String SHARED_PREFERENCES_KEY = "com.android.launcher3.prefs";
- public static final String MANAGED_USER_PREFERENCES_KEY = "com.android.launcher3.managedusers.prefs";
+ public static final String MANAGED_USER_PREFERENCES_KEY =
+ "com.android.launcher3.managedusers.prefs";
// This preference file is not backed up to cloud.
public static final String DEVICE_PREFERENCES_KEY = "com.android.launcher3.device.prefs";
@@ -25,6 +30,10 @@
public static final List<String> ALL_FILES = Collections.unmodifiableList(Arrays.asList(
LAUNCHER_DB,
+ LAUNCHER_4_BY_4_DB,
+ LAUNCHER_3_BY_3_DB,
+ LAUNCHER_2_BY_2_DB,
+ BACKUP_DB,
SHARED_PREFERENCES_KEY + XML,
WIDGET_PREVIEWS_DB,
MANAGED_USER_PREFERENCES_KEY + XML,
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index 04a7ecd..f618fe1 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -17,7 +17,7 @@
package com.android.launcher3;
import static com.android.launcher3.LauncherAppState.ACTION_FORCE_ROLOAD;
-import static com.android.launcher3.config.FeatureFlags.IS_DOGFOOD_BUILD;
+import static com.android.launcher3.config.FeatureFlags.IS_STUDIO_BUILD;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
import static com.android.launcher3.util.PackageManagerHelper.hasShortcutsPermission;
@@ -37,7 +37,6 @@
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.icons.IconCache;
-import com.android.launcher3.icons.LauncherIcons;
import com.android.launcher3.logging.FileLog;
import com.android.launcher3.model.AddWorkspaceItemsTask;
import com.android.launcher3.model.AllAppsList;
@@ -56,7 +55,6 @@
import com.android.launcher3.pm.PackageInstallInfo;
import com.android.launcher3.pm.UserCache;
import com.android.launcher3.shortcuts.ShortcutRequest;
-import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.util.IntSparseArrayMap;
import com.android.launcher3.util.ItemInfoMatcher;
import com.android.launcher3.util.LooperExecutor;
@@ -68,7 +66,6 @@
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
-import java.util.Optional;
import java.util.concurrent.CancellationException;
import java.util.concurrent.Executor;
import java.util.function.Supplier;
@@ -97,10 +94,6 @@
private boolean mModelLoaded;
public boolean isModelLoaded() {
synchronized (mLock) {
- if (TestProtocol.sDebugTracing) {
- Log.d(TestProtocol.LAUNCHER_DIDNT_INITIALIZE,
- "isModelLoaded: " + mModelLoaded + ", " + mLoaderTask);
- }
return mModelLoaded && mLoaderTask == null;
}
}
@@ -133,16 +126,6 @@
}
/**
- * Returns AppInfo with corresponding package name.
- * TODO: move to enqueueModelTask
- */
- public Optional<AppInfo> getAppInfoByPackageName(String pkg) {
- return mBgAllAppsList.data.stream()
- .filter(info -> info.componentName.getPackageName().equals(pkg))
- .findAny();
- }
-
- /**
* Adds the provided items to the workspace.
*/
public void addAndBindAddedWorkspaceItems(List<Pair<ItemInfo, Object>> itemList) {
@@ -255,7 +238,7 @@
enqueueModelUpdateTask(new UserLockStateChangedTask(user));
}
}
- } else if (IS_DOGFOOD_BUILD && ACTION_FORCE_ROLOAD.equals(action)) {
+ } else if (IS_STUDIO_BUILD && ACTION_FORCE_ROLOAD.equals(action)) {
for (Callbacks cb : getCallbacks()) {
if (cb instanceof Launcher) {
((Launcher) cb).recreate();
@@ -373,9 +356,6 @@
public boolean stopLoader() {
synchronized (mLock) {
LoaderTask oldTask = mLoaderTask;
- if (TestProtocol.sDebugTracing) {
- Log.d(TestProtocol.LAUNCHER_DIDNT_INITIALIZE, "LauncherModel.stopLoader");
- }
mLoaderTask = null;
if (oldTask != null) {
oldTask.stopLocked();
@@ -389,10 +369,6 @@
synchronized (mLock) {
stopLoader();
mLoaderTask = new LoaderTask(mApp, mBgAllAppsList, mBgDataModel, results);
- if (TestProtocol.sDebugTracing) {
- Log.d(TestProtocol.LAUNCHER_DIDNT_INITIALIZE,
- "LauncherModel.startLoaderForResults " + mLoaderTask);
- }
// Always post the loader task, instead of running directly (even on same thread) so
// that we exit any nested synchronized blocks
@@ -494,10 +470,6 @@
public void close() {
synchronized (mLock) {
// If we are still the last one to be scheduled, remove ourselves.
- if (TestProtocol.sDebugTracing) {
- Log.d(TestProtocol.LAUNCHER_DIDNT_INITIALIZE,
- "LauncherModel.close " + mLoaderTask + ", " + mTask);
- }
if (mLoaderTask == mTask) {
mLoaderTask = null;
}
@@ -573,9 +545,7 @@
public void updateAndBindWorkspaceItem(WorkspaceItemInfo si, ShortcutInfo info) {
updateAndBindWorkspaceItem(() -> {
si.updateFromDeepShortcutInfo(info, mApp.getContext());
- LauncherIcons li = LauncherIcons.obtain(mApp.getContext());
- si.bitmap = li.createShortcutIcon(info);
- li.recycle();
+ mApp.getIconCache().getShortcutIcon(si, info);
return si;
});
}
diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java
index b0ab35c..697048a 100644
--- a/src/com/android/launcher3/LauncherProvider.java
+++ b/src/com/android/launcher3/LauncherProvider.java
@@ -16,6 +16,7 @@
package com.android.launcher3;
+import static com.android.launcher3.config.FeatureFlags.MULTI_DB_GRID_MIRATION_ALGO;
import static com.android.launcher3.provider.LauncherDbUtils.dropTable;
import static com.android.launcher3.provider.LauncherDbUtils.tableExists;
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
@@ -118,7 +119,7 @@
@Override
public boolean onCreate() {
- if (FeatureFlags.IS_DOGFOOD_BUILD) {
+ if (FeatureFlags.IS_STUDIO_BUILD) {
Log.d(TAG, "Launcher process started");
}
@@ -157,6 +158,17 @@
}
}
+ private synchronized boolean updateCurrentOpenHelper() {
+ final InvariantDeviceProfile idp = InvariantDeviceProfile.INSTANCE.get(getContext());
+ if (TextUtils.equals(idp.dbFile, mOpenHelper.getDatabaseName())) {
+ return false;
+ }
+
+ mOpenHelper.close();
+ mOpenHelper = new DatabaseHelper(getContext());
+ return true;
+ }
+
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
@@ -210,7 +222,7 @@
addModifiedTime(initialValues);
final int rowId = dbInsertAndCheck(mOpenHelper, db, args.table, null, initialValues);
if (rowId < 0) return null;
- mOpenHelper.onAddOrDeleteOp(db);
+ onAddOrDeleteOp(db);
uri = ContentUris.withAppendedId(uri, rowId);
reloadLauncherIfExternal();
@@ -268,7 +280,7 @@
return 0;
}
}
- mOpenHelper.onAddOrDeleteOp(db);
+ onAddOrDeleteOp(db);
t.commit();
}
@@ -294,7 +306,7 @@
results[i].count != null && results[i].count > 0;
}
if (isAddOrDelete) {
- mOpenHelper.onAddOrDeleteOp(t.getDb());
+ onAddOrDeleteOp(t.getDb());
}
t.commit();
@@ -316,7 +328,7 @@
}
int count = db.delete(args.table, args.where, args.args);
if (count > 0) {
- mOpenHelper.onAddOrDeleteOp(db);
+ onAddOrDeleteOp(db);
reloadLauncherIfExternal();
}
return count;
@@ -360,12 +372,14 @@
}
case LauncherSettings.Settings.METHOD_NEW_ITEM_ID: {
Bundle result = new Bundle();
- result.putInt(LauncherSettings.Settings.EXTRA_VALUE, mOpenHelper.generateNewItemId());
+ result.putInt(LauncherSettings.Settings.EXTRA_VALUE,
+ mOpenHelper.generateNewItemId());
return result;
}
case LauncherSettings.Settings.METHOD_NEW_SCREEN_ID: {
Bundle result = new Bundle();
- result.putInt(LauncherSettings.Settings.EXTRA_VALUE, mOpenHelper.generateNewScreenId());
+ result.putInt(LauncherSettings.Settings.EXTRA_VALUE,
+ mOpenHelper.generateNewScreenId());
return result;
}
case LauncherSettings.Settings.METHOD_CREATE_EMPTY_DB: {
@@ -387,8 +401,12 @@
return result;
}
case LauncherSettings.Settings.METHOD_REFRESH_BACKUP_TABLE: {
- mOpenHelper.mBackupTableExists =
- tableExists(mOpenHelper.getReadableDatabase(), Favorites.BACKUP_TABLE_NAME);
+ // TODO(pinyaoting): Update the behavior here.
+ if (!MULTI_DB_GRID_MIRATION_ALGO.get()) {
+ mOpenHelper.mBackupTableExists =
+ tableExists(mOpenHelper.getReadableDatabase(),
+ Favorites.BACKUP_TABLE_NAME);
+ }
return null;
}
case LauncherSettings.Settings.METHOD_RESTORE_BACKUP_TABLE: {
@@ -399,10 +417,26 @@
TOKEN_RESTORE_BACKUP_TABLE, RESTORE_BACKUP_TABLE_DELAY);
return null;
}
+ case LauncherSettings.Settings.METHOD_UPDATE_CURRENT_OPEN_HELPER: {
+ if (MULTI_DB_GRID_MIRATION_ALGO.get()) {
+ Bundle result = new Bundle();
+ result.putBoolean(LauncherSettings.Settings.EXTRA_VALUE,
+ updateCurrentOpenHelper());
+ return result;
+ }
+ }
}
return null;
}
+ private void onAddOrDeleteOp(SQLiteDatabase db) {
+ if (MULTI_DB_GRID_MIRATION_ALGO.get()) {
+ // TODO(pingyaoting): Implement the behavior here.
+ } else {
+ mOpenHelper.onAddOrDeleteOp(db);
+ }
+ }
+
/**
* Deletes any empty folder from the DB.
* @return Ids of deleted folders.
@@ -551,14 +585,16 @@
/**
* The class is subclassed in tests to create an in-memory db.
*/
- public static class DatabaseHelper extends NoLocaleSQLiteHelper implements LayoutParserCallback {
+ public static class DatabaseHelper extends NoLocaleSQLiteHelper implements
+ LayoutParserCallback {
private final Context mContext;
private int mMaxItemId = -1;
private int mMaxScreenId = -1;
private boolean mBackupTableExists;
DatabaseHelper(Context context) {
- this(context, LauncherFiles.LAUNCHER_DB);
+ this(context, MULTI_DB_GRID_MIRATION_ALGO.get() ? InvariantDeviceProfile.INSTANCE.get(
+ context).dbFile : LauncherFiles.LAUNCHER_DB);
// Table creation sometimes fails silently, which leads to a crash loop.
// This way, we will try to create a table every time after crash, so the device
// would eventually be able to recover.
@@ -567,7 +603,10 @@
// This operation is a no-op if the table already exists.
addFavoritesTable(getWritableDatabase(), true);
}
- mBackupTableExists = tableExists(getReadableDatabase(), Favorites.BACKUP_TABLE_NAME);
+ if (!MULTI_DB_GRID_MIRATION_ALGO.get()) {
+ mBackupTableExists = tableExists(getReadableDatabase(),
+ Favorites.BACKUP_TABLE_NAME);
+ }
initIds();
}
@@ -575,8 +614,8 @@
/**
* Constructor used in tests and for restore.
*/
- public DatabaseHelper(Context context, String tableName) {
- super(context, tableName, SCHEMA_VERSION);
+ public DatabaseHelper(Context context, String dbName) {
+ super(context, dbName, SCHEMA_VERSION);
mContext = context;
}
@@ -606,7 +645,7 @@
}
protected void onAddOrDeleteOp(SQLiteDatabase db) {
- if (mBackupTableExists) {
+ if (!MULTI_DB_GRID_MIRATION_ALGO.get() && mBackupTableExists) {
dropTable(db, Favorites.BACKUP_TABLE_NAME);
mBackupTableExists = false;
}
diff --git a/src/com/android/launcher3/LauncherSettings.java b/src/com/android/launcher3/LauncherSettings.java
index 4c5c61c..49831f6 100644
--- a/src/com/android/launcher3/LauncherSettings.java
+++ b/src/com/android/launcher3/LauncherSettings.java
@@ -302,6 +302,8 @@
public static final String METHOD_RESTORE_BACKUP_TABLE = "restore_backup_table";
+ public static final String METHOD_UPDATE_CURRENT_OPEN_HELPER = "update_current_open_helper";
+
public static final String EXTRA_VALUE = "value";
public static Bundle call(ContentResolver cr, String method) {
diff --git a/src/com/android/launcher3/LauncherState.java b/src/com/android/launcher3/LauncherState.java
index 7e06099..16be391 100644
--- a/src/com/android/launcher3/LauncherState.java
+++ b/src/com/android/launcher3/LauncherState.java
@@ -41,8 +41,10 @@
import static com.android.launcher3.testing.TestProtocol.QUICK_SWITCH_STATE_ORDINAL;
import static com.android.launcher3.testing.TestProtocol.SPRING_LOADED_STATE_ORDINAL;
+import android.view.View;
import android.view.animation.Interpolator;
+import com.android.launcher3.allapps.AllAppsContainerView;
import com.android.launcher3.anim.AnimatorSetBuilder;
import com.android.launcher3.states.HintState;
import com.android.launcher3.states.SpringLoadedState;
@@ -337,8 +339,13 @@
hotseat.setScaleX(0.92f);
hotseat.setScaleY(0.92f);
if (ENABLE_OVERVIEW_ACTIONS.get()) {
- launcher.getAppsView().setScaleX(0.92f);
- launcher.getAppsView().setScaleY(0.92f);
+ AllAppsContainerView qsbContainer = launcher.getAppsView();
+ View qsb = qsbContainer.getSearchView();
+ boolean qsbVisible = qsb.getVisibility() == VISIBLE && qsb.getAlpha() > 0;
+ if (!qsbVisible) {
+ qsbContainer.setScaleX(0.92f);
+ qsbContainer.setScaleY(0.92f);
+ }
}
}
} else if (this == NORMAL && fromState == OVERVIEW_PEEK) {
diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java
index ae4eae9..4c6e1f3 100644
--- a/src/com/android/launcher3/PagedView.java
+++ b/src/com/android/launcher3/PagedView.java
@@ -1502,7 +1502,7 @@
return false;
}
- if (FeatureFlags.IS_DOGFOOD_BUILD) {
+ if (FeatureFlags.IS_STUDIO_BUILD) {
duration *= Settings.Global.getFloat(getContext().getContentResolver(),
Settings.Global.WINDOW_ANIMATION_SCALE, 1);
}
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index 7f443b6..e0e4cc0 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -584,9 +584,8 @@
return new FixedSizeEmptyDrawable(iconSize);
}
ShortcutInfo si = (ShortcutInfo) obj;
- LauncherIcons li = LauncherIcons.obtain(appState.getContext());
- Bitmap badge = li.getShortcutInfoBadge(si, appState.getIconCache()).bitmap.icon;
- li.recycle();
+ Bitmap badge = LauncherAppState.getInstance(appState.getContext())
+ .getIconCache().getShortcutInfoBadge(si).icon;
float badgeSize = LauncherIcons.getBadgeSizeForIconSize(iconSize);
float insetFraction = (iconSize - badgeSize) / iconSize;
return new InsetDrawable(new FastBitmapDrawable(badge),
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index b725002..a8f492f 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -1702,8 +1702,8 @@
float scale = mLauncher.getDragLayer().getDescendantRectRelativeToSelf(v, folderLocation);
target.removeView(v);
- FolderIcon fi = mLauncher.addFolder(target, sourceInfo, container, screenId,
- targetCell[0], targetCell[1]);
+ FolderIcon fi = mLauncher.addFolder(target, container, screenId, targetCell[0],
+ targetCell[1]);
destInfo.cellX = -1;
destInfo.cellY = -1;
sourceInfo.cellX = -1;
@@ -1722,6 +1722,7 @@
fi.addItem(destInfo);
fi.addItem(sourceInfo);
}
+ mLauncher.folderCreatedFromItem(fi.getFolder(), destInfo);
return true;
}
return false;
@@ -1857,7 +1858,7 @@
CellLayout parentCell = getParentCellLayoutForView(cell);
if (parentCell != null) {
parentCell.removeView(cell);
- } else if (FeatureFlags.IS_DOGFOOD_BUILD) {
+ } else if (FeatureFlags.IS_STUDIO_BUILD) {
throw new NullPointerException("mDragInfo.cell has null parent");
}
addInScreen(cell, container, screenId, mTargetCell[0], mTargetCell[1],
@@ -2152,7 +2153,7 @@
ItemInfo item = d.dragInfo;
if (item == null) {
- if (FeatureFlags.IS_DOGFOOD_BUILD) {
+ if (FeatureFlags.IS_STUDIO_BUILD) {
throw new NullPointerException("DragObject has null info");
}
return;
@@ -2774,7 +2775,7 @@
mDragInfo.container, mDragInfo.screenId);
if (cellLayout != null) {
cellLayout.onDropChild(mDragInfo.cell);
- } else if (FeatureFlags.IS_DOGFOOD_BUILD) {
+ } else if (FeatureFlags.IS_STUDIO_BUILD) {
throw new RuntimeException("Invalid state: cellLayout == null in "
+ "Workspace#onDropCompleted. Please file a bug. ");
}
@@ -2793,7 +2794,7 @@
CellLayout parentCell = getParentCellLayoutForView(v);
if (parentCell != null) {
parentCell.removeView(v);
- } else if (FeatureFlags.IS_DOGFOOD_BUILD) {
+ } else if (FeatureFlags.IS_STUDIO_BUILD) {
// When an app is uninstalled using the drop target, we wait until resume to remove
// the icon. We also remove all the corresponding items from the workspace at
// {@link Launcher#bindComponentsRemoved}. That call can come before or after
diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java
index 56bd1b6..4e5b031 100644
--- a/src/com/android/launcher3/allapps/AllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java
@@ -153,6 +153,11 @@
return mMultiValueAlpha.getProperty(index);
}
+ public WorkFooterContainer getWorkFooterContainer() {
+ return mWorkFooterContainer;
+ }
+
+
@Override
protected void setDampedScrollShift(float shift) {
// Bound the shift amount to avoid content from drawing on top (Y-val) of the QSB.
diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
index 1bde138..93bdac9 100644
--- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java
+++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
@@ -11,7 +11,7 @@
import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.anim.PropertySetter.NO_ANIM_PROPERTY_SETTER;
-import static com.android.launcher3.config.FeatureFlags.QUICKSTEP_SPRINGS;
+import static com.android.launcher3.config.FeatureFlags.UNSTABLE_SPRINGS;
import static com.android.launcher3.util.SystemUiController.UI_STATE_ALL_APPS;
import android.animation.Animator;
@@ -185,7 +185,7 @@
}
public Animator createSpringAnimation(float... progressValues) {
- if (QUICKSTEP_SPRINGS.get()) {
+ if (UNSTABLE_SPRINGS.get()) {
return new SpringObjectAnimator<>(this, ALL_APPS_PROGRESS, 1f / mShiftRange,
SPRING_DAMPING_RATIO, SPRING_STIFFNESS, progressValues);
}
diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index b1a2c33..6413044 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -22,6 +22,7 @@
import com.android.launcher3.Utilities;
import com.android.launcher3.uioverrides.DeviceFlag;
+import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
@@ -42,7 +43,10 @@
return Utilities.IS_DEBUG_DEVICE && Utilities.isDevelopersOptionsEnabled(context);
}
- public static final boolean IS_DOGFOOD_BUILD = BuildConfig.DEBUG;
+ /**
+ * True when the build has come from Android Studio and is being used for local debugging.
+ */
+ public static final boolean IS_STUDIO_BUILD = BuildConfig.DEBUG;
/**
* Enable moving the QSB on the 0th screen of the workspace. This is not a configuration feature
@@ -90,13 +94,13 @@
"FAKE_LANDSCAPE_UI", false, "Rotate launcher UI instead of using transposed layout");
public static final BooleanFlag FOLDER_NAME_SUGGEST = getDebugFlag(
- "FOLDER_NAME_SUGGEST", false, "Suggests folder names instead of blank text.");
+ "FOLDER_NAME_SUGGEST", true, "Suggests folder names instead of blank text.");
public static final BooleanFlag APP_SEARCH_IMPROVEMENTS = new DeviceFlag(
"APP_SEARCH_IMPROVEMENTS", false,
"Adds localized title and keyword search and ranking");
- public static final BooleanFlag ENABLE_PREDICTION_DISMISS = getDebugFlag(
+ public static final BooleanFlag ENABLE_PREDICTION_DISMISS = new DeviceFlag(
"ENABLE_PREDICTION_DISMISS", false, "Allow option to dimiss apps from predicted list");
public static final BooleanFlag ENABLE_QUICK_CAPTURE_GESTURE = getDebugFlag(
@@ -106,12 +110,15 @@
"ASSISTANT_GIVES_LAUNCHER_FOCUS", false,
"Allow Launcher to handle nav bar gestures while Assistant is running over it");
- public static final BooleanFlag ENABLE_HYBRID_HOTSEAT = getDebugFlag(
+ public static final BooleanFlag ENABLE_HYBRID_HOTSEAT = new DeviceFlag(
"ENABLE_HYBRID_HOTSEAT", false, "Fill gaps in hotseat with predicted apps");
public static final BooleanFlag ENABLE_DEEP_SHORTCUT_ICON_CACHE = getDebugFlag(
"ENABLE_DEEP_SHORTCUT_ICON_CACHE", true, "R/W deep shortcut in IconCache");
+ public static final BooleanFlag MULTI_DB_GRID_MIRATION_ALGO = getDebugFlag(
+ "MULTI_DB_GRID_MIRATION_ALGO", false, "Use the multi-db grid migration algorithm");
+
public static final BooleanFlag ENABLE_LAUNCHER_PREVIEW_IN_GRID_PICKER = getDebugFlag(
"ENABLE_LAUNCHER_PREVIEW_IN_GRID_PICKER", false,
"Show launcher preview in grid picker");
@@ -142,6 +149,15 @@
}
}
+ public static void dump(PrintWriter pw) {
+ pw.println("FeatureFlags:");
+ synchronized (sDebugFlags) {
+ for (DebugFlag flag : sDebugFlags) {
+ pw.println(" " + flag.key + "=" + flag.get());
+ }
+ }
+ }
+
public static class BooleanFlag {
public final String key;
diff --git a/src/com/android/launcher3/dragndrop/DragController.java b/src/com/android/launcher3/dragndrop/DragController.java
index 1b7b015..de7bc6d 100644
--- a/src/com/android/launcher3/dragndrop/DragController.java
+++ b/src/com/android/launcher3/dragndrop/DragController.java
@@ -160,8 +160,8 @@
mOptions = options;
if (mOptions.systemDndStartPoint != null) {
- mMotionDownX = mOptions.systemDndStartPoint.x;
- mMotionDownY = mOptions.systemDndStartPoint.y;
+ mLastTouch[0] = mMotionDownX = mOptions.systemDndStartPoint.x;
+ mLastTouch[1] = mMotionDownY = mOptions.systemDndStartPoint.y;
}
final int registrationX = mMotionDownX - dragLayerX;
diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java
index 024c7dd..1d79e01 100644
--- a/src/com/android/launcher3/folder/Folder.java
+++ b/src/com/android/launcher3/folder/Folder.java
@@ -16,9 +16,23 @@
package com.android.launcher3.folder;
+import static android.text.TextUtils.isEmpty;
+
+import static androidx.core.util.Preconditions.checkNotNull;
+
+import static com.android.launcher3.FolderInfo.FLAG_MANUAL_FOLDER_NAME;
import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_EXIT_DELAY;
+import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_DESKTOP;
+import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT;
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.compat.AccessibilityManagerCompat.sendCustomAccessibilityEvent;
+import static com.android.launcher3.userevent.LauncherLogProto.Target.FromFolderLabelState.FROM_CUSTOM;
+import static com.android.launcher3.userevent.LauncherLogProto.Target.FromFolderLabelState.FROM_EMPTY;
+import static com.android.launcher3.userevent.LauncherLogProto.Target.FromFolderLabelState.FROM_FOLDER_LABEL_STATE_UNSPECIFIED;
+import static com.android.launcher3.userevent.LauncherLogProto.Target.FromFolderLabelState.FROM_SUGGESTED;
+
+import static java.util.Arrays.asList;
+import static java.util.Arrays.stream;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -75,20 +89,24 @@
import com.android.launcher3.dragndrop.DragOptions;
import com.android.launcher3.logging.LoggerUtils;
import com.android.launcher3.pageindicators.PageIndicatorDots;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
-import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
-import com.android.launcher3.userevent.nano.LauncherLogProto.ItemType;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
+import com.android.launcher3.userevent.LauncherLogProto.Action;
+import com.android.launcher3.userevent.LauncherLogProto.ContainerType;
+import com.android.launcher3.userevent.LauncherLogProto.ItemType;
+import com.android.launcher3.userevent.LauncherLogProto.LauncherEvent;
+import com.android.launcher3.userevent.LauncherLogProto.Target;
+import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.launcher3.util.Thunk;
import com.android.launcher3.views.ClipPathView;
import com.android.launcher3.widget.PendingAddShortcutInfo;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
/**
* Represents a set of icons chosen by the user or generated by the system.
@@ -186,6 +204,9 @@
@Thunk int mScrollHintDir = SCROLL_NONE;
@Thunk int mCurrentScrollDir = SCROLL_NONE;
+ private String mPreviousLabel;
+ private boolean mIsPreviousLabelSuggested;
+
/**
* Used to inflate the Workspace from XML.
*
@@ -300,13 +321,13 @@
public void startEditingFolderName() {
post(() -> {
if (FeatureFlags.FOLDER_NAME_SUGGEST.get()) {
- if (TextUtils.isEmpty(mFolderName.getText())) {
- String[] suggestedNames =
- mInfo.suggestedFolderNames.getStringArrayExtra("suggest");
- mFolderName.setText(suggestedNames[0]);
- mFolderName.displayCompletions(Arrays.asList(suggestedNames).subList(1,
- suggestedNames.length));
- mFolderName.setEnteredCompose(false);
+ if (isEmpty(mFolderName.getText())) {
+ FolderNameInfo[] nameInfos =
+ (FolderNameInfo[]) mInfo.suggestedFolderNames.getParcelableArrayExtra(
+ FolderInfo.EXTRA_FOLDER_SUGGESTIONS);
+ if (nameInfos != null) {
+ showLabelSuggestion(nameInfos, false);
+ }
}
}
mFolderName.setHint("");
@@ -324,23 +345,16 @@
}
mInfo.title = newTitle;
- mInfo.setOption(FolderInfo.FLAG_MANUAL_FOLDER_NAME, mFolderName.isEnteredCompose(),
+ mInfo.setOption(FLAG_MANUAL_FOLDER_NAME, mFolderName.isEnteredCompose(),
mLauncher.getModelWriter());
mFolderIcon.onTitleChanged(newTitle);
mLauncher.getModelWriter().updateItemInDatabase(mInfo);
- if (FeatureFlags.FOLDER_NAME_SUGGEST.get()) {
- mFolderName.setText(mInfo.title);
- // TODO: depending on whether the title was manually edited or automatically
- // suggested, apply different hint.
- mFolderName.setHint("");
+ if (TextUtils.isEmpty(mInfo.title)) {
+ mFolderName.setHint(R.string.folder_hint_text);
+ mFolderName.setText("");
} else {
- if (TextUtils.isEmpty(mInfo.title)) {
- mFolderName.setHint(R.string.folder_hint_text);
- mFolderName.setText("");
- } else {
- mFolderName.setHint(null);
- }
+ mFolderName.setHint(null);
}
sendCustomAccessibilityEvent(
@@ -423,8 +437,10 @@
mItemsInvalidated = true;
mInfo.addListener(this);
- if (!TextUtils.isEmpty(mInfo.title)) {
+ if (!isEmpty(mInfo.title)) {
mFolderName.setText(mInfo.title);
+ mPreviousLabel = mInfo.title.toString();
+ mIsPreviousLabelSuggested = !mInfo.hasOption(FLAG_MANUAL_FOLDER_NAME);
mFolderName.setHint(null);
} else {
mFolderName.setText("");
@@ -443,27 +459,54 @@
}
/**
- * Show suggested folder title.
+ * Show suggested folder title in FolderEditText, push InputMethodManager suggestions and save
+ * the suggestedFolderNames.
*/
- public void showSuggestedTitle(String[] suggestName) {
+ public void showSuggestedTitle(FolderNameInfo[] nameInfos) {
if (FeatureFlags.FOLDER_NAME_SUGGEST.get()) {
- mInfo.suggestedFolderNames = new Intent().putExtra("suggest", suggestName);
- if (TextUtils.isEmpty(mFolderName.getText().toString())
- && !mInfo.hasOption(FolderInfo.FLAG_MANUAL_FOLDER_NAME)) {
- if (suggestName.length > 0 && !TextUtils.isEmpty(suggestName[0])) {
- mFolderName.setHint("");
- mFolderName.setText(suggestName[0]);
- mInfo.title = suggestName[0];
- animateOpen(mInfo.contents, 0, true);
- mFolderName.showKeyboard();
- mFolderName.displayCompletions(
- Arrays.asList(suggestName).subList(1, suggestName.length));
- }
+ mInfo.suggestedFolderNames = new Intent().putExtra(FolderInfo.EXTRA_FOLDER_SUGGESTIONS,
+ nameInfos);
+ if (isEmpty(mFolderName.getText().toString())
+ && !mInfo.hasOption(FLAG_MANUAL_FOLDER_NAME)) {
+ showLabelSuggestion(nameInfos, true);
}
}
}
/**
+ * Show suggested folder title in FolderEditText if the first suggestion is non-empty, push
+ * InputMethodManager suggestions.
+ */
+ private void showLabelSuggestion(FolderNameInfo[] nameInfos, boolean animate) {
+ if (nameInfos == null) {
+ return;
+ }
+ // Open the Folder and Keyboard when the first or second suggestion is valid non-empty
+ // string.
+ boolean shouldOpen = nameInfos.length > 0 && nameInfos[0] != null && !isEmpty(
+ nameInfos[0].getLabel())
+ || nameInfos.length > 1 && nameInfos[1] != null && !isEmpty(
+ nameInfos[1].getLabel());
+ CharSequence firstLabel = nameInfos[0].getLabel();
+
+ if (shouldOpen) {
+ if (!isEmpty(firstLabel)) {
+ mFolderName.setHint("");
+ mFolderName.setText(firstLabel);
+ }
+ if (animate) {
+ animateOpen(mInfo.contents, 0, true);
+ }
+ mFolderName.showKeyboard();
+ mFolderName.displayCompletions(
+ asList(nameInfos).subList(1, nameInfos.length).stream()
+ .filter(Objects::nonNull)
+ .map(s -> s.getLabel().toString())
+ .collect(Collectors.toList()));
+ }
+ }
+
+ /**
* Creates a new UserFolder, inflated from R.layout.user_folder.
*
* @param launcher The main activity.
@@ -580,7 +623,7 @@
dragLayer.addView(this);
mDragController.addDropTarget(this);
} else {
- if (FeatureFlags.IS_DOGFOOD_BUILD) {
+ if (FeatureFlags.IS_STUDIO_BUILD) {
Log.e(TAG, "Opening folder (" + this + ") which already has a parent:"
+ getParent());
}
@@ -608,9 +651,9 @@
if (!skipUserEventLog) {
mLauncher.getUserEventDispatcher().logActionOnItem(
- Touch.TAP,
- Direction.NONE,
- ItemType.FOLDER_ICON, mInfo.cellX, mInfo.cellY);
+ LauncherLogProto.Action.Touch.TAP,
+ LauncherLogProto.Action.Direction.NONE,
+ LauncherLogProto.ItemType.FOLDER_ICON, mInfo.cellX, mInfo.cellY);
}
@@ -1109,13 +1152,14 @@
int itemCount = getItemCount();
if (itemCount <= 1) {
View newIcon = null;
+ WorkspaceItemInfo finalItem = null;
if (itemCount == 1) {
// Move the item from the folder to the workspace, in the position of the
// folder
CellLayout cellLayout = mLauncher.getCellLayout(mInfo.container,
mInfo.screenId);
- WorkspaceItemInfo finalItem = mInfo.contents.remove(0);
+ finalItem = mInfo.contents.remove(0);
newIcon = mLauncher.createShortcut(cellLayout, finalItem);
mLauncher.getModelWriter().addOrMoveItemInDatabase(finalItem,
mInfo.container, mInfo.screenId, mInfo.cellX, mInfo.cellY);
@@ -1136,6 +1180,9 @@
// Focus the newly created child
newIcon.requestFocus();
}
+ if (finalItem != null) {
+ mLauncher.folderConvertedToItem(mFolderIcon.getFolder(), finalItem);
+ }
}
}
};
@@ -1388,6 +1435,7 @@
if (hasFocus) {
startEditingFolderName();
} else {
+ logEditFolderLabel();
mFolderName.dispatchBackKey();
}
}
@@ -1401,11 +1449,12 @@
}
@Override
- public void fillInLogContainerData(View v, ItemInfo info, Target target, Target targetParent) {
+ public void fillInLogContainerData(View v, ItemInfo info, LauncherLogProto.Target target,
+ LauncherLogProto.Target targetParent) {
target.gridX = info.cellX;
target.gridY = info.cellY;
target.pageIndex = mContent.getCurrentPage();
- targetParent.containerType = ContainerType.FOLDER;
+ targetParent.containerType = LauncherLogProto.ContainerType.FOLDER;
}
private class OnScrollHintListener implements OnAlarmListener {
@@ -1503,7 +1552,7 @@
@Override
public int getLogContainerType() {
- return ContainerType.FOLDER;
+ return LauncherLogProto.ContainerType.FOLDER;
}
/**
@@ -1538,7 +1587,7 @@
}
} else {
mLauncher.getUserEventDispatcher().logActionTapOutside(
- LoggerUtils.newContainerTarget(ContainerType.FOLDER));
+ LoggerUtils.newContainerTarget(LauncherLogProto.ContainerType.FOLDER));
close(true);
return true;
}
@@ -1568,4 +1617,113 @@
super.draw(canvas);
}
}
+
+ private void logEditFolderLabel() {
+ LauncherEvent launcherEvent = LauncherEvent.newBuilder()
+ .setAction(Action.newBuilder().setType(Action.Type.SOFT_KEYBOARD))
+ .addSrcTarget(newEditTextTargetBuilder()
+ .setFromFolderLabelState(getFromFolderLabelState())
+ .setToFolderLabelState(getToFolderLabelState()))
+ .addSrcTarget(newFolderTargetBuilder())
+ .addSrcTarget(newParentContainerTarget())
+ .build();
+ mLauncher.getUserEventDispatcher().logLauncherEvent(launcherEvent);
+ mPreviousLabel = mFolderName.getText().toString();
+ mIsPreviousLabelSuggested = !mInfo.hasOption(FLAG_MANUAL_FOLDER_NAME);
+ }
+
+ private Target.FromFolderLabelState getFromFolderLabelState() {
+ return mPreviousLabel == null
+ ? FROM_FOLDER_LABEL_STATE_UNSPECIFIED
+ : mPreviousLabel.isEmpty()
+ ? FROM_EMPTY
+ : mIsPreviousLabelSuggested
+ ? FROM_SUGGESTED
+ : FROM_CUSTOM;
+ }
+
+ private Target.ToFolderLabelState getToFolderLabelState() {
+ String newLabel =
+ checkNotNull(mFolderName.getText().toString(),
+ "Expected valid folder label, but found null");
+
+ Optional<String[]> suggestedLabels = Optional.ofNullable(
+ (FolderNameInfo[]) mInfo.suggestedFolderNames
+ .getParcelableArrayExtra(FolderInfo.EXTRA_FOLDER_SUGGESTIONS))
+ .map(folderNameInfoArray ->
+ stream(folderNameInfoArray)
+ .filter(Objects::nonNull)
+ .map(FolderNameInfo::getLabel)
+ .map(CharSequence::toString)
+ .toArray(String[]::new));
+
+
+ int accepted_suggestion_index = suggestedLabels
+ .map(folderNameInfoArray ->
+ IntStream.range(0, folderNameInfoArray.length)
+ .filter(index -> newLabel.equalsIgnoreCase(
+ folderNameInfoArray[index]))
+ .findFirst()
+ .orElse(-1)
+ ).orElse(-1);
+
+ boolean hasValidPrimary = suggestedLabels
+ .map(labels -> labels.length > 0 && !isEmpty(labels[0]))
+ .orElse(false);
+ String primarySuffix = hasValidPrimary
+ ? "_WITH_VALID_PRIMARY"
+ : "_WITH_EMPTY_PRIMARY";
+
+ boolean isEmptySuggestions = suggestedLabels
+ .map(labels -> stream(labels).allMatch(TextUtils::isEmpty))
+ .orElse(true);
+ boolean isSuggestionsEnabled = FeatureFlags.FOLDER_NAME_SUGGEST.get();
+ String suggestionsSuffix = isSuggestionsEnabled
+ ? isEmptySuggestions
+ ? "_WITH_EMPTY_SUGGESTIONS"
+ : "_WITH_VALID_SUGGESTIONS"
+ : "_WITH_SUGGESTIONS_DISABLED";
+
+ return newLabel.equals(mPreviousLabel)
+ ? Target.ToFolderLabelState.UNCHANGED
+ : newLabel.isEmpty()
+ ? Target.ToFolderLabelState.valueOf("TO_EMPTY" + suggestionsSuffix)
+ : accepted_suggestion_index >= 0
+ ? Target.ToFolderLabelState.valueOf("TO_SUGGESTION"
+ + accepted_suggestion_index
+ + primarySuffix)
+ : Target.ToFolderLabelState.valueOf("TO_CUSTOM" + suggestionsSuffix);
+ }
+
+
+ private Target.Builder newEditTextTargetBuilder() {
+ return Target.newBuilder().setType(Target.Type.ITEM).setItemType(ItemType.EDITTEXT);
+ }
+
+ private Target.Builder newFolderTargetBuilder() {
+ return Target.newBuilder()
+ .setType(Target.Type.CONTAINER)
+ .setContainerType(ContainerType.FOLDER)
+ .setPageIndex(mInfo.screenId)
+ .setGridX(mInfo.cellX)
+ .setGridY(mInfo.cellY)
+ .setCardinality(mInfo.contents.size());
+ }
+
+ private Target.Builder newParentContainerTarget() {
+ Target.Builder builder = Target.newBuilder().setType(Target.Type.CONTAINER);
+
+ switch (mInfo.container) {
+ case CONTAINER_HOTSEAT:
+ return builder.setContainerType(ContainerType.HOTSEAT);
+ case CONTAINER_DESKTOP:
+ return builder.setContainerType(ContainerType.WORKSPACE);
+ default:
+ throw new AssertionError(String
+ .format("Expected container to be either %s or %s but found %s.",
+ CONTAINER_HOTSEAT,
+ CONTAINER_DESKTOP,
+ mInfo.container));
+ }
+ }
}
diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java
index 6a47b98..ab1ff10 100644
--- a/src/com/android/launcher3/folder/FolderIcon.java
+++ b/src/com/android/launcher3/folder/FolderIcon.java
@@ -393,15 +393,16 @@
if (!itemAdded) mPreviewItemManager.hidePreviewItem(index, true);
final int finalIndex = index;
- String[] suggestedNameOut = new String[FolderNameProvider.SUGGEST_MAX];
+ FolderNameInfo[] nameInfos =
+ new FolderNameInfo[FolderNameProvider.SUGGEST_MAX];
if (FeatureFlags.FOLDER_NAME_SUGGEST.get()) {
Executors.UI_HELPER_EXECUTOR.post(() -> {
d.folderNameProvider.getSuggestedFolderName(
- getContext(), mInfo.contents, suggestedNameOut);
- showFinalView(finalIndex, item, suggestedNameOut);
+ getContext(), mInfo.contents, nameInfos);
+ showFinalView(finalIndex, item, nameInfos);
});
} else {
- showFinalView(finalIndex, item, suggestedNameOut);
+ showFinalView(finalIndex, item, nameInfos);
}
} else {
addItem(item);
@@ -409,12 +410,12 @@
}
private void showFinalView(int finalIndex, final WorkspaceItemInfo item,
- String[] suggestedNameOut) {
+ FolderNameInfo[] nameInfos) {
postDelayed(() -> {
mPreviewItemManager.hidePreviewItem(finalIndex, false);
mFolder.showItem(item);
invalidate();
- mFolder.showSuggestedTitle(suggestedNameOut);
+ mFolder.showSuggestedTitle(nameInfos);
}, DROP_IN_ANIMATION_DURATION);
}
diff --git a/src/com/android/launcher3/folder/FolderNameInfo.java b/src/com/android/launcher3/folder/FolderNameInfo.java
new file mode 100644
index 0000000..eb9da90
--- /dev/null
+++ b/src/com/android/launcher3/folder/FolderNameInfo.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2020 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.folder;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+
+import androidx.annotation.NonNull;
+
+/**
+ * Information about a single label suggestions of the Folder.
+ */
+
+public final class FolderNameInfo implements Parcelable {
+ private final double mScore;
+ private final CharSequence mLabel;
+
+ /**
+ * Create a simple completion with label.
+ *
+ * @param label The text that should be inserted into the editor and pushed to
+ * InputMethodManager suggestions.
+ * @param score The score for the label between 0.0 and 1.0.
+ */
+ public FolderNameInfo(CharSequence label, double score) {
+ mScore = score;
+ mLabel = label;
+ }
+
+ private FolderNameInfo(Parcel source) {
+ mScore = source.readDouble();
+ mLabel = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
+ }
+
+ public CharSequence getLabel() {
+ return mLabel;
+ }
+
+ /**
+ * Used to package this object into a {@link Parcel}.
+ *
+ * @param dest The {@link Parcel} to be written.
+ * @param flags The flags used for parceling.
+ */
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeDouble(mScore);
+ TextUtils.writeToParcel(mLabel, dest, flags);
+ }
+
+ /**
+ * Used to make this class parcelable.
+ */
+ @NonNull
+ public static final Parcelable.Creator<FolderNameInfo> CREATOR =
+ new Parcelable.Creator<FolderNameInfo>() {
+ public FolderNameInfo createFromParcel(Parcel source) {
+ return new FolderNameInfo(source);
+ }
+
+ public FolderNameInfo[] newArray(int size) {
+ return new FolderNameInfo[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+}
diff --git a/src/com/android/launcher3/folder/FolderNameProvider.java b/src/com/android/launcher3/folder/FolderNameProvider.java
index 957e636..184dbb9 100644
--- a/src/com/android/launcher3/folder/FolderNameProvider.java
+++ b/src/com/android/launcher3/folder/FolderNameProvider.java
@@ -21,15 +21,21 @@
import android.util.Log;
import com.android.launcher3.AppInfo;
+import com.android.launcher3.FolderInfo;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.R;
import com.android.launcher3.WorkspaceItemInfo;
+import com.android.launcher3.model.AllAppsList;
+import com.android.launcher3.model.BaseModelUpdateTask;
+import com.android.launcher3.model.BgDataModel;
+import com.android.launcher3.util.IntSparseArrayMap;
import com.android.launcher3.util.ResourceBasedOverride;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
@@ -49,6 +55,8 @@
* name edit box can also be used to provide suggestion.
*/
public static final int SUGGEST_MAX = 4;
+ protected IntSparseArrayMap<FolderInfo> mFolderInfos;
+ protected List<AppInfo> mAppInfos;
/**
* Retrieve instance of this object that can be overridden in runtime based on the build
@@ -57,78 +65,113 @@
public static FolderNameProvider newInstance(Context context) {
FolderNameProvider fnp = Overrides.getObject(FolderNameProvider.class,
context.getApplicationContext(), R.string.folder_name_provider_class);
+ fnp.load(context);
+
return fnp;
}
- public CharSequence getSuggestedFolderName(Context context,
- ArrayList<WorkspaceItemInfo> workspaceItemInfos, CharSequence[] candidates) {
+ public static FolderNameProvider newInstance(Context context, List<AppInfo> appInfos,
+ IntSparseArrayMap<FolderInfo> folderInfos) {
+ FolderNameProvider fnp = Overrides.getObject(FolderNameProvider.class,
+ context.getApplicationContext(), R.string.folder_name_provider_class);
+ fnp.load(appInfos, folderInfos);
+
+ return fnp;
+ }
+
+ private void load(Context context) {
+ LauncherAppState.getInstance(context).getModel().enqueueModelUpdateTask(
+ new FolderNameWorker());
+ }
+
+ private void load(List<AppInfo> appInfos, IntSparseArrayMap<FolderInfo> folderInfos) {
+ mAppInfos = appInfos;
+ mFolderInfos = folderInfos;
+ }
+
+ /**
+ * Generate and rank the suggested Folder names.
+ */
+ public void getSuggestedFolderName(Context context,
+ ArrayList<WorkspaceItemInfo> workspaceItemInfos,
+ FolderNameInfo[] nameInfos) {
if (DEBUG) {
- Log.d(TAG, "getSuggestedFolderName:" + Arrays.toString(candidates));
+ Log.d(TAG, "getSuggestedFolderName:" + Arrays.toString(nameInfos));
}
// If all the icons are from work profile,
// Then, suggest "Work" as the folder name
List<WorkspaceItemInfo> distinctItemInfos = workspaceItemInfos.stream()
- .filter(distinctByKey(p-> p.user))
+ .filter(distinctByKey(p -> p.user))
.collect(Collectors.toList());
if (distinctItemInfos.size() == 1
&& !distinctItemInfos.get(0).user.equals(Process.myUserHandle())) {
// Place it as last viable suggestion
- setAsLastSuggestion(candidates,
+ setAsLastSuggestion(nameInfos,
context.getResources().getString(R.string.work_folder_name));
}
// If all the icons are from same package (e.g., main icon, shortcut, shortcut)
// Then, suggest the package's title as the folder name
distinctItemInfos = workspaceItemInfos.stream()
- .filter(distinctByKey(p-> p.getTargetComponent() != null
+ .filter(distinctByKey(p -> p.getTargetComponent() != null
? p.getTargetComponent().getPackageName() : ""))
.collect(Collectors.toList());
if (distinctItemInfos.size() == 1) {
- Optional<AppInfo> info = LauncherAppState.getInstance(context).getModel()
- .getAppInfoByPackageName(distinctItemInfos.get(0).getTargetComponent()
- .getPackageName());
+ Optional<AppInfo> info = getAppInfoByPackageName(
+ distinctItemInfos.get(0).getTargetComponent().getPackageName());
// Place it as first viable suggestion and shift everything else
- info.ifPresent(i -> setAsFirstSuggestion(candidates, i.title.toString()));
+ info.ifPresent(i -> setAsFirstSuggestion(nameInfos, i.title.toString()));
}
if (DEBUG) {
- Log.d(TAG, "getSuggestedFolderName:" + Arrays.toString(candidates));
+ Log.d(TAG, "getSuggestedFolderName:" + Arrays.toString(nameInfos));
}
- return candidates[0];
}
- private void setAsFirstSuggestion(CharSequence[] candidatesOut, CharSequence candidate) {
- if (contains(candidatesOut, candidate)) {
+ private Optional<AppInfo> getAppInfoByPackageName(String packageName) {
+ if (mAppInfos == null || mAppInfos.isEmpty()) {
+ return Optional.empty();
+ }
+ return mAppInfos.stream()
+ .filter(info -> info.componentName.getPackageName().equals(packageName))
+ .findAny();
+ }
+
+ private void setAsFirstSuggestion(FolderNameInfo[] nameInfos, CharSequence label) {
+ if (nameInfos.length == 0 || contains(nameInfos, label)) {
return;
}
- for (int i = candidatesOut.length - 1; i > 0; i--) {
- if (!TextUtils.isEmpty(candidatesOut[i - 1])) {
- candidatesOut[i] = candidatesOut[i - 1];
+ for (int i = nameInfos.length - 1; i > 0; i--) {
+ if (nameInfos[i - 1] != null && !TextUtils.isEmpty(nameInfos[i - 1].getLabel())) {
+ nameInfos[i] = nameInfos[i - 1];
}
}
- candidatesOut[0] = candidate;
+ nameInfos[0] = new FolderNameInfo(label, 1.0);
}
- private void setAsLastSuggestion(CharSequence[] candidatesOut, CharSequence candidate) {
- if (contains(candidatesOut, candidate)) {
+ private void setAsLastSuggestion(FolderNameInfo[] nameInfos, CharSequence label) {
+ if (nameInfos.length == 0 || contains(nameInfos, label)) {
return;
}
- for (int i = 0; i < candidate.length(); i++) {
- if (TextUtils.isEmpty(candidatesOut[i])) {
- candidatesOut[i] = candidate;
+ for (int i = 0; i < nameInfos.length; i++) {
+ if (nameInfos[i] == null || TextUtils.isEmpty(nameInfos[i].getLabel())) {
+ nameInfos[i] = new FolderNameInfo(label, 1.0);
return;
}
}
- candidatesOut[candidate.length() - 1] = candidate;
+ // Overwrite the last suggestion.
+ int lastIndex = nameInfos.length - 1;
+ nameInfos[lastIndex] = new FolderNameInfo(label, 1.0);
}
- private boolean contains(CharSequence[] list, CharSequence key) {
- return Arrays.asList(list).stream()
- .filter(s -> s != null)
- .anyMatch(s -> s.toString().equalsIgnoreCase(key.toString()));
+ private boolean contains(FolderNameInfo[] nameInfos, CharSequence label) {
+ return Arrays.stream(nameInfos)
+ .filter(Objects::nonNull)
+ .anyMatch(nameInfo -> nameInfo.getLabel().toString().equalsIgnoreCase(
+ label.toString()));
}
// This method can be moved to some Utility class location.
@@ -136,4 +179,13 @@
Map<Object, Boolean> map = new ConcurrentHashMap<>();
return t -> map.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null;
}
+
+ private class FolderNameWorker extends BaseModelUpdateTask {
+ @Override
+ public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
+ mFolderInfos = dataModel.folders.clone();
+ mAppInfos = Arrays.asList(apps.copyData());
+ }
+ }
+
}
diff --git a/src/com/android/launcher3/graphics/DragPreviewProvider.java b/src/com/android/launcher3/graphics/DragPreviewProvider.java
index f579451..94d30f6 100644
--- a/src/com/android/launcher3/graphics/DragPreviewProvider.java
+++ b/src/com/android/launcher3/graphics/DragPreviewProvider.java
@@ -152,7 +152,7 @@
}
public final void generateDragOutline(Bitmap preview) {
- if (FeatureFlags.IS_DOGFOOD_BUILD && mOutlineGeneratorCallback != null) {
+ if (FeatureFlags.IS_STUDIO_BUILD && mOutlineGeneratorCallback != null) {
throw new RuntimeException("Drag outline generated twice");
}
diff --git a/src/com/android/launcher3/icons/ComponentWithLabel.java b/src/com/android/launcher3/icons/ComponentWithLabel.java
index f7ee5f9..372c591 100644
--- a/src/com/android/launcher3/icons/ComponentWithLabel.java
+++ b/src/com/android/launcher3/icons/ComponentWithLabel.java
@@ -31,7 +31,7 @@
CharSequence getLabel(PackageManager pm);
- class ComponentCachingLogic implements CachingLogic<ComponentWithLabel> {
+ class ComponentCachingLogic<T extends ComponentWithLabel> implements CachingLogic<T> {
private final PackageManager mPackageManager;
private final boolean mAddToMemCache;
@@ -42,22 +42,22 @@
}
@Override
- public ComponentName getComponent(ComponentWithLabel object) {
+ public ComponentName getComponent(T object) {
return object.getComponent();
}
@Override
- public UserHandle getUser(ComponentWithLabel object) {
+ public UserHandle getUser(T object) {
return object.getUser();
}
@Override
- public CharSequence getLabel(ComponentWithLabel object) {
+ public CharSequence getLabel(T object) {
return object.getLabel(mPackageManager);
}
@Override
- public BitmapInfo loadIcon(Context context, ComponentWithLabel object) {
+ public BitmapInfo loadIcon(Context context, T object) {
return BitmapInfo.LOW_RES_INFO;
}
diff --git a/src/com/android/launcher3/icons/ComponentWithLabelAndIcon.java b/src/com/android/launcher3/icons/ComponentWithLabelAndIcon.java
new file mode 100644
index 0000000..248a57d
--- /dev/null
+++ b/src/com/android/launcher3/icons/ComponentWithLabelAndIcon.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2020 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.graphics.drawable.Drawable;
+
+import androidx.annotation.NonNull;
+
+import com.android.launcher3.LauncherAppState;
+
+/**
+ * Extension of ComponentWithLabel to also support loading icons
+ */
+public interface ComponentWithLabelAndIcon extends ComponentWithLabel {
+
+ /**
+ * Provide an icon for this object
+ */
+ Drawable getFullResIcon(IconCache cache);
+
+ class ComponentWithIconCachingLogic extends ComponentCachingLogic<ComponentWithLabelAndIcon> {
+
+ public ComponentWithIconCachingLogic(Context context, boolean addToMemCache) {
+ super(context, addToMemCache);
+ }
+
+ @NonNull
+ @Override
+ public BitmapInfo loadIcon(Context context, ComponentWithLabelAndIcon object) {
+ Drawable d = object.getFullResIcon(LauncherAppState.getInstance(context)
+ .getIconCache());
+ if (d == null) {
+ return super.loadIcon(context, object);
+ }
+ try (LauncherIcons li = LauncherIcons.obtain(context)) {
+ return li.createBadgedIconBitmap(d, object.getUser(), 0);
+ }
+ }
+ }
+}
diff --git a/src/com/android/launcher3/icons/IconCache.java b/src/com/android/launcher3/icons/IconCache.java
index 69b8125..804acc3 100644
--- a/src/com/android/launcher3/icons/IconCache.java
+++ b/src/com/android/launcher3/icons/IconCache.java
@@ -19,6 +19,7 @@
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
+import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
@@ -56,6 +57,7 @@
import com.android.launcher3.util.PackageUserKey;
import com.android.launcher3.util.Preconditions;
+import java.util.function.Predicate;
import java.util.function.Supplier;
/**
@@ -65,6 +67,9 @@
private static final String TAG = "Launcher.IconCache";
+ private final Predicate<ItemInfoWithIcon> mIsUsingFallbackIconCheck = w -> w.bitmap != null
+ && w.bitmap.isNullOrLowRes() && !isDefaultIcon(w.bitmap, w.user);
+
private final CachingLogic<ComponentWithLabel> mComponentWithLabelCachingLogic;
private final CachingLogic<LauncherActivityInfo> mLauncherActivityInfoCachingLogic;
private final CachingLogic<ShortcutInfo> mShortcutCachingLogic;
@@ -180,6 +185,77 @@
}
/**
+ * Fill in {@param info} with the icon for {@param si}
+ */
+ public void getShortcutIcon(ItemInfoWithIcon info, ShortcutInfo si) {
+ getShortcutIcon(info, si, true, mIsUsingFallbackIconCheck);
+ }
+
+ /**
+ * Fill in {@param info} with an unbadged icon for {@param si}
+ */
+ public void getUnbadgedShortcutIcon(ItemInfoWithIcon info, ShortcutInfo si) {
+ getShortcutIcon(info, si, false, mIsUsingFallbackIconCheck);
+ }
+
+ /**
+ * Fill in {@param info} with the icon and label for {@param si}. If the icon is not
+ * available, and fallback check returns true, it keeps the old icon.
+ */
+ public <T extends ItemInfoWithIcon> void getShortcutIcon(T info, ShortcutInfo si,
+ @NonNull Predicate<T> fallbackIconCheck) {
+ getShortcutIcon(info, si, true /* use badged */, fallbackIconCheck);
+ }
+
+ private synchronized <T extends ItemInfoWithIcon> void getShortcutIcon(T info, ShortcutInfo si,
+ boolean useBadged, @NonNull Predicate<T> fallbackIconCheck) {
+ BitmapInfo bitmapInfo;
+ if (FeatureFlags.ENABLE_DEEP_SHORTCUT_ICON_CACHE.get()) {
+ bitmapInfo = cacheLocked(ShortcutKey.fromInfo(si).componentName, si.getUserHandle(),
+ () -> si, mShortcutCachingLogic, false, false).bitmap;
+ } else {
+ // If caching is disabled, load the full icon
+ bitmapInfo = mShortcutCachingLogic.loadIcon(mContext, si);
+ }
+ if (bitmapInfo.isNullOrLowRes()) {
+ bitmapInfo = getDefaultIcon(si.getUserHandle());
+ }
+
+ if (isDefaultIcon(bitmapInfo, si.getUserHandle()) && fallbackIconCheck.test(info)) {
+ return;
+ }
+ info.bitmap = bitmapInfo;
+ if (useBadged) {
+ BitmapInfo badgeInfo = getShortcutInfoBadge(si);
+ try (LauncherIcons li = LauncherIcons.obtain(mContext)) {
+ info.bitmap = li.badgeBitmap(info.bitmap.icon, badgeInfo);
+ }
+ }
+ }
+
+ /**
+ * Returns the badging info for the shortcut
+ */
+ public BitmapInfo getShortcutInfoBadge(ShortcutInfo shortcutInfo) {
+ ComponentName cn = shortcutInfo.getActivity();
+ if (cn != null) {
+ // Get the app info for the source activity.
+ AppInfo appInfo = new AppInfo();
+ appInfo.user = shortcutInfo.getUserHandle();
+ appInfo.componentName = cn;
+ appInfo.intent = new Intent(Intent.ACTION_MAIN)
+ .addCategory(Intent.CATEGORY_LAUNCHER)
+ .setComponent(cn);
+ getTitleAndIcon(appInfo, false);
+ return appInfo.bitmap;
+ } else {
+ PackageItemInfo pkgInfo = new PackageItemInfo(shortcutInfo.getPackage());
+ getTitleAndIconForApp(pkgInfo, false);
+ return pkgInfo.bitmap;
+ }
+ }
+
+ /**
* Fill in info with the icon and label for deep shortcut.
*/
public synchronized CacheEntry getDeepShortcutTitleAndIcon(ShortcutInfo info) {
diff --git a/src/com/android/launcher3/icons/LauncherIcons.java b/src/com/android/launcher3/icons/LauncherIcons.java
index e67d244..cbd7c53 100644
--- a/src/com/android/launcher3/icons/LauncherIcons.java
+++ b/src/com/android/launcher3/icons/LauncherIcons.java
@@ -16,28 +16,11 @@
package com.android.launcher3.icons;
-import android.content.ComponentName;
import android.content.Context;
-import android.content.Intent;
-import android.content.pm.ShortcutInfo;
-import android.graphics.Bitmap;
-import android.graphics.drawable.Drawable;
-import android.os.Process;
-import androidx.annotation.Nullable;
-import androidx.annotation.WorkerThread;
-
-import com.android.launcher3.AppInfo;
-import com.android.launcher3.FastBitmapDrawable;
import com.android.launcher3.InvariantDeviceProfile;
-import com.android.launcher3.ItemInfoWithIcon;
import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.graphics.IconShape;
-import com.android.launcher3.model.PackageItemInfo;
-import com.android.launcher3.util.Themes;
-
-import java.util.function.Supplier;
/**
* Wrapper class to provide access to {@link BaseIconFactory} and also to provide pool of this class
@@ -111,114 +94,4 @@
public void close() {
recycle();
}
-
- // below methods should also migrate to BaseIconFactory
- public BitmapInfo createShortcutIcon(ShortcutInfo shortcutInfo) {
- return createShortcutIcon(shortcutInfo, true /* badged */);
- }
-
- public BitmapInfo createShortcutIcon(ShortcutInfo shortcutInfo, boolean badged) {
- return createShortcutIcon(shortcutInfo, badged, null);
- }
-
- public BitmapInfo createShortcutIcon(ShortcutInfo shortcutInfo, boolean badged,
- @Nullable Supplier<ItemInfoWithIcon> fallbackIconProvider) {
- if (FeatureFlags.ENABLE_DEEP_SHORTCUT_ICON_CACHE.get()) {
- return createShortcutIconCached(shortcutInfo, badged, true, fallbackIconProvider);
- } else {
- return createShortcutIconLegacy(shortcutInfo, badged, fallbackIconProvider);
- }
- }
-
- public BitmapInfo createShortcutIconLegacy(ShortcutInfo shortcutInfo, boolean badged,
- @Nullable Supplier<ItemInfoWithIcon> fallbackIconProvider) {
- Drawable unbadgedDrawable = ShortcutCachingLogic.getIcon(
- mContext, shortcutInfo, mFillResIconDpi);
- IconCache cache = LauncherAppState.getInstance(mContext).getIconCache();
- final Bitmap unbadgedBitmap;
- if (unbadgedDrawable != null) {
- unbadgedBitmap = createScaledBitmapWithoutShadow(unbadgedDrawable, 0);
- } else {
- if (fallbackIconProvider != null) {
- // Fallback icons are already badged and with appropriate shadow
- ItemInfoWithIcon fullIcon = fallbackIconProvider.get();
- if (fullIcon != null && fullIcon.bitmap != null) {
- return fullIcon.bitmap;
- }
- }
- unbadgedBitmap = cache.getDefaultIcon(Process.myUserHandle()).icon;
- }
-
- if (!badged) {
- return BitmapInfo.of(unbadgedBitmap, Themes.getColorAccent(mContext));
- }
-
- final Bitmap unbadgedfinal = unbadgedBitmap;
- final ItemInfoWithIcon badge = getShortcutInfoBadge(shortcutInfo, cache);
-
- Bitmap icon = BitmapRenderer.createHardwareBitmap(mIconBitmapSize, mIconBitmapSize, (c) -> {
- getShadowGenerator().recreateIcon(unbadgedfinal, c);
- badgeWithDrawable(c, new FastBitmapDrawable(badge.bitmap));
- });
- return BitmapInfo.of(icon, badge.bitmap.color);
- }
-
- @WorkerThread
- public BitmapInfo createShortcutIconCached(ShortcutInfo shortcutInfo, boolean badged,
- boolean useCache, @Nullable Supplier<ItemInfoWithIcon> fallbackIconProvider) {
- IconCache cache = LauncherAppState.getInstance(mContext).getIconCache();
- final BitmapInfo bitmapInfo;
- if (useCache) {
- bitmapInfo = cache.getDeepShortcutTitleAndIcon(shortcutInfo).bitmap;
- } else {
- bitmapInfo = new ShortcutCachingLogic().loadIcon(mContext, shortcutInfo);
- }
- final Bitmap unbadgedBitmap;
- if (bitmapInfo.icon != null) {
- unbadgedBitmap = bitmapInfo.icon;
- } else {
- if (fallbackIconProvider != null) {
- // Fallback icons are already badged and with appropriate shadow
- ItemInfoWithIcon fullIcon = fallbackIconProvider.get();
- if (fullIcon != null && fullIcon.bitmap != null) {
- return fullIcon.bitmap;
- }
- }
- unbadgedBitmap = cache.getDefaultIcon(Process.myUserHandle()).icon;
- }
-
- if (!badged) {
- return BitmapInfo.of(unbadgedBitmap, Themes.getColorAccent(mContext));
- }
-
- final Bitmap unbadgedfinal = unbadgedBitmap;
- final ItemInfoWithIcon badge = getShortcutInfoBadge(shortcutInfo, cache);
-
- Bitmap icon = BitmapRenderer.createHardwareBitmap(mIconBitmapSize, mIconBitmapSize, (c) -> {
- getShadowGenerator().recreateIcon(unbadgedfinal, c);
- badgeWithDrawable(c, new FastBitmapDrawable(badge.bitmap));
- });
- return BitmapInfo.of(icon, badge.bitmap.color);
- }
-
- public ItemInfoWithIcon getShortcutInfoBadge(ShortcutInfo shortcutInfo, IconCache cache) {
- ComponentName cn = shortcutInfo.getActivity();
- String badgePkg = shortcutInfo.getPackage();
- boolean hasBadgePkgSet = !badgePkg.equals(shortcutInfo.getPackage());
- if (cn != null && !hasBadgePkgSet) {
- // Get the app info for the source activity.
- AppInfo appInfo = new AppInfo();
- appInfo.user = shortcutInfo.getUserHandle();
- appInfo.componentName = cn;
- appInfo.intent = new Intent(Intent.ACTION_MAIN)
- .addCategory(Intent.CATEGORY_LAUNCHER)
- .setComponent(cn);
- cache.getTitleAndIcon(appInfo, false);
- return appInfo;
- } else {
- PackageItemInfo pkgInfo = new PackageItemInfo(badgePkg);
- cache.getTitleAndIconForApp(pkgInfo, false);
- return pkgInfo;
- }
- }
}
diff --git a/src/com/android/launcher3/icons/ShortcutCachingLogic.java b/src/com/android/launcher3/icons/ShortcutCachingLogic.java
index b856dd1..d7eed06 100644
--- a/src/com/android/launcher3/icons/ShortcutCachingLogic.java
+++ b/src/com/android/launcher3/icons/ShortcutCachingLogic.java
@@ -25,6 +25,7 @@
import android.content.pm.ShortcutInfo;
import android.graphics.drawable.Drawable;
import android.os.UserHandle;
+import android.text.TextUtils;
import android.util.Log;
import androidx.annotation.NonNull;
@@ -57,6 +58,12 @@
return info.getShortLabel();
}
+ @Override
+ public CharSequence getDescription(ShortcutInfo object, CharSequence fallback) {
+ CharSequence label = object.getLongLabel();
+ return TextUtils.isEmpty(label) ? fallback : label;
+ }
+
@NonNull
@Override
public BitmapInfo loadIcon(Context context, ShortcutInfo info) {
diff --git a/src/com/android/launcher3/logging/FileLog.java b/src/com/android/launcher3/logging/FileLog.java
index 2c972a0..67f07b1 100644
--- a/src/com/android/launcher3/logging/FileLog.java
+++ b/src/com/android/launcher3/logging/FileLog.java
@@ -42,7 +42,7 @@
private static Handler sHandler = null;
private static File sLogsDirectory = null;
- private static final int LOG_DAYS = 2;
+ public static final int LOG_DAYS = 4;
public static void setDir(File logsDir) {
if (ENABLED) {
diff --git a/src/com/android/launcher3/logging/UserEventDispatcher.java b/src/com/android/launcher3/logging/UserEventDispatcher.java
index 8289da9..199d13f 100644
--- a/src/com/android/launcher3/logging/UserEventDispatcher.java
+++ b/src/com/android/launcher3/logging/UserEventDispatcher.java
@@ -69,8 +69,7 @@
public class UserEventDispatcher implements ResourceBasedOverride {
private static final String TAG = "UserEvent";
- private static final boolean IS_VERBOSE =
- FeatureFlags.IS_DOGFOOD_BUILD && Utilities.isPropertyEnabled(LogConfig.USEREVENT);
+ private static final boolean IS_VERBOSE = Utilities.isPropertyEnabled(LogConfig.USEREVENT);
private static final String UUID_STORAGE = "uuid";
public static UserEventDispatcher newInstance(Context context,
@@ -372,6 +371,25 @@
dispatchUserEvent(event, null);
}
+ /**
+ * Logs proto lite version of LauncherEvent object to clearcut.
+ */
+ public void logLauncherEvent(
+ com.android.launcher3.userevent.LauncherLogProto.LauncherEvent launcherEvent) {
+
+ if (mPreviousHomeGesture) {
+ mPreviousHomeGesture = false;
+ }
+ mAppOrTaskLaunch = false;
+ launcherEvent.toBuilder()
+ .setElapsedContainerMillis(SystemClock.uptimeMillis() - mElapsedContainerMillis)
+ .setElapsedSessionMillis(SystemClock.uptimeMillis() - mElapsedSessionMillis).build();
+ if (!IS_VERBOSE) {
+ return;
+ }
+ Log.d(TAG, launcherEvent.toString());
+ }
+
public void logDeepShortcutsOpen(View icon) {
LogContainerProvider provider = StatsLogUtils.getLaunchProviderRecursive(icon);
if (icon == null || !(icon.getTag() instanceof ItemInfo || provider == null)) {
diff --git a/src/com/android/launcher3/model/BgDataModel.java b/src/com/android/launcher3/model/BgDataModel.java
index c24b939..32fce0b 100644
--- a/src/com/android/launcher3/model/BgDataModel.java
+++ b/src/com/android/launcher3/model/BgDataModel.java
@@ -273,7 +273,7 @@
switch (item.itemType) {
case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
folders.remove(item.id);
- if (FeatureFlags.IS_DOGFOOD_BUILD) {
+ if (FeatureFlags.IS_STUDIO_BUILD) {
for (ItemInfo info : itemsIdMap) {
if (info.container == item.id) {
// We are deleting a folder which still contains items that
diff --git a/src/com/android/launcher3/model/GridBackupTable.java b/src/com/android/launcher3/model/GridBackupTable.java
index fc9948e..07a7551 100644
--- a/src/com/android/launcher3/model/GridBackupTable.java
+++ b/src/com/android/launcher3/model/GridBackupTable.java
@@ -74,8 +74,8 @@
@IntDef({STATE_NOT_FOUND, STATE_RAW, STATE_SANITIZED})
private @interface BackupState { }
- public GridBackupTable(Context context, SQLiteDatabase db,
- int hotseatSize, int gridX, int gridY) {
+ public GridBackupTable(Context context, SQLiteDatabase db, int hotseatSize, int gridX,
+ int gridY) {
mContext = context;
mDb = db;
@@ -105,7 +105,7 @@
}
long userSerial = UserCache.INSTANCE.get(mContext).getSerialNumberForUser(
Process.myUserHandle());
- copyTable(BACKUP_TABLE_NAME, Favorites.TABLE_NAME, userSerial);
+ copyTable(mDb, BACKUP_TABLE_NAME, Favorites.TABLE_NAME, userSerial);
Log.d(TAG, "Backup table found");
return true;
}
@@ -118,10 +118,10 @@
/**
* Copy valid grid entries from one table to another.
*/
- private void copyTable(String from, String to, long userSerial) {
- dropTable(mDb, to);
- Favorites.addTableToDb(mDb, userSerial, false, to);
- mDb.execSQL("INSERT INTO " + to + " SELECT * FROM " + from + " where _id > " + ID_PROPERTY);
+ private static void copyTable(SQLiteDatabase db, String from, String to, long userSerial) {
+ dropTable(db, to);
+ Favorites.addTableToDb(db, userSerial, false, to);
+ db.execSQL("INSERT INTO " + to + " SELECT * FROM " + from + " where _id > " + ID_PROPERTY);
}
private void encodeDBProperties(int options) {
@@ -174,7 +174,7 @@
// skip restore if dimensions in backup table differs from current setup.
return false;
}
- copyTable(Favorites.BACKUP_TABLE_NAME, Favorites.TABLE_NAME, oldProfileId);
+ copyTable(mDb, Favorites.BACKUP_TABLE_NAME, Favorites.TABLE_NAME, oldProfileId);
Log.d(TAG, "Backup restored");
return true;
}
@@ -183,7 +183,7 @@
* Performs a backup on the workspace layout.
*/
public void doBackup(long profileId, int options) {
- copyTable(Favorites.TABLE_NAME, Favorites.BACKUP_TABLE_NAME, profileId);
+ copyTable(mDb, Favorites.TABLE_NAME, Favorites.BACKUP_TABLE_NAME, profileId);
encodeDBProperties(options);
}
diff --git a/src/com/android/launcher3/model/GridSizeMigrationTaskV2.java b/src/com/android/launcher3/model/GridSizeMigrationTaskV2.java
new file mode 100644
index 0000000..63b7191
--- /dev/null
+++ b/src/com/android/launcher3/model/GridSizeMigrationTaskV2.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.model;
+
+import android.content.Context;
+
+/**
+ * This class takes care of shrinking the workspace (by maximum of one row and one column), as a
+ * result of restoring from a larger device or device density change.
+ */
+public class GridSizeMigrationTaskV2 {
+
+ private GridSizeMigrationTaskV2(Context context) {
+
+ }
+
+ /**
+ * Migrates the workspace and hotseat in case their sizes changed.
+ *
+ * @return false if the migration failed.
+ */
+ public static boolean migrateGridIfNeeded(Context context) {
+ // To be implemented.
+ return true;
+ }
+}
diff --git a/src/com/android/launcher3/model/LoaderCursor.java b/src/com/android/launcher3/model/LoaderCursor.java
index 95268d0..4961432 100644
--- a/src/com/android/launcher3/model/LoaderCursor.java
+++ b/src/com/android/launcher3/model/LoaderCursor.java
@@ -16,6 +16,8 @@
package com.android.launcher3.model;
+import static android.graphics.BitmapFactory.decodeByteArray;
+
import android.content.ComponentName;
import android.content.ContentValues;
import android.content.Context;
@@ -26,7 +28,6 @@
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.database.CursorWrapper;
-import android.graphics.BitmapFactory;
import android.os.UserHandle;
import android.provider.BaseColumns;
import android.text.TextUtils;
@@ -166,37 +167,30 @@
*/
protected boolean loadIcon(WorkspaceItemInfo info) {
try (LauncherIcons li = LauncherIcons.obtain(mContext)) {
- return loadIcon(info, li);
- }
- }
-
- /**
- * Loads the icon from the cursor and updates the {@param info} if the icon is an app resource.
- */
- protected boolean loadIcon(WorkspaceItemInfo info, LauncherIcons li) {
- if (itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT) {
- String packageName = getString(iconPackageIndex);
- String resourceName = getString(iconResourceIndex);
- if (!TextUtils.isEmpty(packageName) || !TextUtils.isEmpty(resourceName)) {
- info.iconResource = new ShortcutIconResource();
- info.iconResource.packageName = packageName;
- info.iconResource.resourceName = resourceName;
- BitmapInfo iconInfo = li.createIconBitmap(info.iconResource);
- if (iconInfo != null) {
- info.bitmap = iconInfo;
- return true;
+ if (itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT) {
+ String packageName = getString(iconPackageIndex);
+ String resourceName = getString(iconResourceIndex);
+ if (!TextUtils.isEmpty(packageName) || !TextUtils.isEmpty(resourceName)) {
+ info.iconResource = new ShortcutIconResource();
+ info.iconResource.packageName = packageName;
+ info.iconResource.resourceName = resourceName;
+ BitmapInfo iconInfo = li.createIconBitmap(info.iconResource);
+ if (iconInfo != null) {
+ info.bitmap = iconInfo;
+ return true;
+ }
}
}
- }
- // Failed to load from resource, try loading from DB.
- byte[] data = getBlob(iconIndex);
- try {
- info.bitmap = li.createIconBitmap(BitmapFactory.decodeByteArray(data, 0, data.length));
- return true;
- } catch (Exception e) {
- Log.e(TAG, "Failed to decode byte array for info " + info, e);
- return false;
+ // Failed to load from resource, try loading from DB.
+ byte[] data = getBlob(iconIndex);
+ try {
+ info.bitmap = li.createIconBitmap(decodeByteArray(data, 0, data.length));
+ return true;
+ } catch (Exception e) {
+ Log.e(TAG, "Failed to decode byte array for info " + info, e);
+ return false;
+ }
}
}
diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java
index 23ec459..6223a23 100644
--- a/src/com/android/launcher3/model/LoaderTask.java
+++ b/src/com/android/launcher3/model/LoaderTask.java
@@ -19,6 +19,7 @@
import static com.android.launcher3.ItemInfoWithIcon.FLAG_DISABLED_LOCKED_USER;
import static com.android.launcher3.ItemInfoWithIcon.FLAG_DISABLED_SAFEMODE;
import static com.android.launcher3.ItemInfoWithIcon.FLAG_DISABLED_SUSPENDED;
+import static com.android.launcher3.config.FeatureFlags.MULTI_DB_GRID_MIRATION_ALGO;
import static com.android.launcher3.model.ModelUtils.filterCurrentWorkspaceItems;
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
import static com.android.launcher3.util.PackageManagerHelper.hasShortcutsPermission;
@@ -35,7 +36,6 @@
import android.content.pm.PackageInstaller;
import android.content.pm.PackageInstaller.SessionInfo;
import android.content.pm.ShortcutInfo;
-import android.os.Process;
import android.os.UserHandle;
import android.os.UserManager;
import android.text.TextUtils;
@@ -50,7 +50,6 @@
import com.android.launcher3.FolderInfo;
import com.android.launcher3.InstallShortcutReceiver;
import com.android.launcher3.ItemInfo;
-import com.android.launcher3.ItemInfoWithIcon;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherAppWidgetInfo;
import com.android.launcher3.LauncherModel;
@@ -60,12 +59,12 @@
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.folder.Folder;
import com.android.launcher3.folder.FolderGridOrganizer;
+import com.android.launcher3.folder.FolderNameInfo;
import com.android.launcher3.folder.FolderNameProvider;
-import com.android.launcher3.icons.ComponentWithLabel;
-import com.android.launcher3.icons.ComponentWithLabel.ComponentCachingLogic;
+import com.android.launcher3.icons.ComponentWithLabelAndIcon;
+import com.android.launcher3.icons.ComponentWithLabelAndIcon.ComponentWithIconCachingLogic;
import com.android.launcher3.icons.IconCache;
import com.android.launcher3.icons.LauncherActivityCachingLogic;
-import com.android.launcher3.icons.LauncherIcons;
import com.android.launcher3.icons.ShortcutCachingLogic;
import com.android.launcher3.icons.cache.IconCacheUpdateHandler;
import com.android.launcher3.logging.FileLog;
@@ -94,7 +93,6 @@
import java.util.List;
import java.util.Map;
import java.util.concurrent.CancellationException;
-import java.util.function.Supplier;
/**
* Runnable for the thread that loads the contents of the launcher:
@@ -170,32 +168,15 @@
}
public void run() {
- if (TestProtocol.sDebugTracing) {
- Log.d(TestProtocol.LAUNCHER_DIDNT_INITIALIZE,
- "LoaderTask1 " + this);
- }
synchronized (this) {
// Skip fast if we are already stopped.
if (mStopped) {
return;
}
}
- if (TestProtocol.sDebugTracing) {
- Log.d(TestProtocol.LAUNCHER_DIDNT_INITIALIZE,
- "LoaderTask2 " + this);
- }
Object traceToken = TraceHelper.INSTANCE.beginSection(TAG);
- TimingLogger logger = TestProtocol.sDebugTracing ?
- new TimingLogger(TAG, "run") {
- @Override
- public void addSplit(String splitLabel) {
- super.addSplit(splitLabel);
- Log.d(TestProtocol.LAUNCHER_DIDNT_INITIALIZE,
- "LoaderTask.addSplit " + splitLabel);
- }
- }
- : new TimingLogger(TAG, "run");
+ TimingLogger logger = new TimingLogger(TAG, "run");
try (LauncherModel.LoaderTransaction transaction = mApp.getModel().beginLoader(this)) {
List<ShortcutInfo> allShortcuts = new ArrayList<>();
loadWorkspace(allShortcuts);
@@ -263,7 +244,8 @@
verifyNotStopped();
// fourth step
- List<ComponentWithLabel> allWidgetsList = mBgDataModel.widgetsModel.update(mApp, null);
+ List<ComponentWithLabelAndIcon> allWidgetsList =
+ mBgDataModel.widgetsModel.update(mApp, null);
logger.addSplit("load widgets");
verifyNotStopped();
@@ -271,8 +253,9 @@
logger.addSplit("bindWidgets");
verifyNotStopped();
- updateHandler.updateIcons(allWidgetsList, new ComponentCachingLogic(
- mApp.getContext(), true), mApp.getModel()::onWidgetLabelsUpdated);
+ updateHandler.updateIcons(allWidgetsList,
+ new ComponentWithIconCachingLogic(mApp.getContext(), true),
+ mApp.getModel()::onWidgetLabelsUpdated);
logger.addSplit("save widgets in icon cache");
// fifth step
@@ -284,10 +267,6 @@
updateHandler.finish();
logger.addSplit("finish icon update");
- if (TestProtocol.sDebugTracing) {
- Log.d(TestProtocol.LAUNCHER_DIDNT_INITIALIZE,
- "LoaderTask3 " + this);
- }
transaction.commit();
} catch (CancellationException e) {
// Loader stopped, ignore
@@ -320,7 +299,9 @@
clearDb = true;
}
- if (!clearDb && !GridSizeMigrationTask.migrateGridIfNeeded(context)) {
+ if (!clearDb && (MULTI_DB_GRID_MIRATION_ALGO.get()
+ ? !GridSizeMigrationTaskV2.migrateGridIfNeeded(context)
+ : !GridSizeMigrationTask.migrateGridIfNeeded(context))) {
// Migration failed. Clear workspace.
clearDb = true;
}
@@ -544,17 +525,10 @@
continue;
}
info = new WorkspaceItemInfo(pinnedShortcut, context);
- final WorkspaceItemInfo finalInfo = info;
-
- LauncherIcons li = LauncherIcons.obtain(context);
// If the pinned deep shortcut is no longer published,
// use the last saved icon instead of the default.
- Supplier<ItemInfoWithIcon> fallbackIconProvider = () ->
- c.loadIcon(finalInfo, li) ? finalInfo : null;
- info.bitmap = li.createShortcutIcon(
- pinnedShortcut, true /* badged */,
- fallbackIconProvider);
- li.recycle();
+ mIconCache.getShortcutIcon(info, pinnedShortcut, c::loadIcon);
+
if (pmHelper.isAppSuspended(
pinnedShortcut.getPackage(), info.user)) {
info.runtimeStatusFlags |= FLAG_DISABLED_SUSPENDED;
@@ -854,23 +828,23 @@
private void setIgnorePackages(IconCacheUpdateHandler updateHandler) {
// Ignore packages which have a promise icon.
- HashSet<String> packagesToIgnore = new HashSet<>();
synchronized (mBgDataModel) {
for (ItemInfo info : mBgDataModel.itemsIdMap) {
if (info instanceof WorkspaceItemInfo) {
WorkspaceItemInfo si = (WorkspaceItemInfo) info;
if (si.isPromise() && si.getTargetComponent() != null) {
- packagesToIgnore.add(si.getTargetComponent().getPackageName());
+ updateHandler.addPackagesToIgnore(
+ si.user, si.getTargetComponent().getPackageName());
}
} else if (info instanceof LauncherAppWidgetInfo) {
LauncherAppWidgetInfo lawi = (LauncherAppWidgetInfo) info;
if (lawi.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY)) {
- packagesToIgnore.add(lawi.providerName.getPackageName());
+ updateHandler.addPackagesToIgnore(
+ lawi.user, lawi.providerName.getPackageName());
}
}
}
}
- updateHandler.setPackagesToIgnore(Process.myUserHandle(), packagesToIgnore);
}
private List<LauncherActivityInfo> loadAllApps() {
@@ -927,15 +901,18 @@
}
private void loadFolderNames() {
- FolderNameProvider provider = FolderNameProvider.newInstance(mApp.getContext());
+ FolderNameProvider provider = FolderNameProvider.newInstance(mApp.getContext(),
+ mBgAllAppsList.data, mBgDataModel.folders);
synchronized (mBgDataModel) {
for (int i = 0; i < mBgDataModel.folders.size(); i++) {
- String[] suggestedOut = new String[FolderNameProvider.SUGGEST_MAX];
+ FolderNameInfo[] suggestionInfos =
+ new FolderNameInfo[FolderNameProvider.SUGGEST_MAX];
FolderInfo info = mBgDataModel.folders.valueAt(i);
if (info.suggestedFolderNames == null) {
- provider.getSuggestedFolderName(mApp.getContext(), info.contents, suggestedOut);
- info.suggestedFolderNames = new Intent().putExtra("suggest", suggestedOut);
+ provider.getSuggestedFolderName(mApp.getContext(), info.contents,
+ suggestionInfos);
+ info.suggestedFolderNames = new Intent().putExtra("suggest", suggestionInfos);
}
}
}
diff --git a/src/com/android/launcher3/model/ModelUtils.java b/src/com/android/launcher3/model/ModelUtils.java
index 628dd95..1473124 100644
--- a/src/com/android/launcher3/model/ModelUtils.java
+++ b/src/com/android/launcher3/model/ModelUtils.java
@@ -97,7 +97,7 @@
return Integer.compare(lhs.screenId, rhs.screenId);
}
default:
- if (FeatureFlags.IS_DOGFOOD_BUILD) {
+ if (FeatureFlags.IS_STUDIO_BUILD) {
throw new RuntimeException(
"Unexpected container type when sorting workspace items.");
}
diff --git a/src/com/android/launcher3/model/ModelWriter.java b/src/com/android/launcher3/model/ModelWriter.java
index ccd1554..27fa580 100644
--- a/src/com/android/launcher3/model/ModelWriter.java
+++ b/src/com/android/launcher3/model/ModelWriter.java
@@ -115,8 +115,9 @@
ItemInfo modelItem = mBgDataModel.itemsIdMap.get(itemId);
if (modelItem != null && item != modelItem) {
// check all the data is consistent
- if (!Utilities.IS_DEBUG_DEVICE && !FeatureFlags.IS_DOGFOOD_BUILD &&
- modelItem instanceof WorkspaceItemInfo && item instanceof WorkspaceItemInfo) {
+ if (!Utilities.IS_DEBUG_DEVICE && !FeatureFlags.IS_STUDIO_BUILD
+ && modelItem instanceof WorkspaceItemInfo
+ && item instanceof WorkspaceItemInfo) {
if (modelItem.title.toString().equals(item.title.toString()) &&
modelItem.getIntent().filterEquals(item.getIntent()) &&
modelItem.id == item.id &&
@@ -320,7 +321,7 @@
*/
public void prepareToUndoDelete() {
if (!mPreparingToUndo) {
- if (!mDeleteRunnables.isEmpty() && FeatureFlags.IS_DOGFOOD_BUILD) {
+ if (!mDeleteRunnables.isEmpty() && FeatureFlags.IS_STUDIO_BUILD) {
throw new IllegalStateException("There are still uncommitted delete operations!");
}
mDeleteRunnables.clear();
diff --git a/src/com/android/launcher3/model/ShortcutsChangedTask.java b/src/com/android/launcher3/model/ShortcutsChangedTask.java
index b0e7a69..3f79ad0 100644
--- a/src/com/android/launcher3/model/ShortcutsChangedTask.java
+++ b/src/com/android/launcher3/model/ShortcutsChangedTask.java
@@ -23,7 +23,6 @@
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.WorkspaceItemInfo;
-import com.android.launcher3.icons.LauncherIcons;
import com.android.launcher3.shortcuts.ShortcutKey;
import com.android.launcher3.shortcuts.ShortcutRequest;
import com.android.launcher3.util.ItemInfoMatcher;
@@ -89,12 +88,7 @@
}
for (final WorkspaceItemInfo workspaceItemInfo : workspaceItemInfos) {
workspaceItemInfo.updateFromDeepShortcutInfo(fullDetails, context);
- // If the shortcut is pinned but no longer has an icon in the system,
- // keep the current icon instead of reverting to the default icon.
- LauncherIcons li = LauncherIcons.obtain(context);
- workspaceItemInfo.bitmap = li.createShortcutIcon(
- fullDetails, true, () -> workspaceItemInfo);
- li.recycle();
+ app.getIconCache().getShortcutIcon(workspaceItemInfo, fullDetails);
updatedWorkspaceItemInfos.add(workspaceItemInfo);
}
}
diff --git a/src/com/android/launcher3/model/UserLockStateChangedTask.java b/src/com/android/launcher3/model/UserLockStateChangedTask.java
index d527423..a3adc82 100644
--- a/src/com/android/launcher3/model/UserLockStateChangedTask.java
+++ b/src/com/android/launcher3/model/UserLockStateChangedTask.java
@@ -26,7 +26,6 @@
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.WorkspaceItemInfo;
-import com.android.launcher3.icons.LauncherIcons;
import com.android.launcher3.shortcuts.ShortcutKey;
import com.android.launcher3.shortcuts.ShortcutRequest;
import com.android.launcher3.shortcuts.ShortcutRequest.QueryResult;
@@ -89,11 +88,7 @@
}
si.runtimeStatusFlags &= ~FLAG_DISABLED_LOCKED_USER;
si.updateFromDeepShortcutInfo(shortcut, context);
- // If the shortcut is pinned but no longer has an icon in the system,
- // keep the current icon instead of reverting to the default icon.
- LauncherIcons li = LauncherIcons.obtain(context);
- si.bitmap = li.createShortcutIcon(shortcut, true, () -> si);
- li.recycle();
+ app.getIconCache().getShortcutIcon(si, shortcut);
} else {
si.runtimeStatusFlags |= FLAG_DISABLED_LOCKED_USER;
}
diff --git a/src/com/android/launcher3/pm/PinRequestHelper.java b/src/com/android/launcher3/pm/PinRequestHelper.java
index a15399d..74a5a31 100644
--- a/src/com/android/launcher3/pm/PinRequestHelper.java
+++ b/src/com/android/launcher3/pm/PinRequestHelper.java
@@ -17,7 +17,6 @@
package com.android.launcher3.pm;
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
-import static com.android.launcher3.util.ShortcutUtil.fetchAndUpdateShortcutIconAsync;
import android.annotation.TargetApi;
import android.content.Context;
@@ -32,8 +31,7 @@
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.WorkspaceItemInfo;
-import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.icons.LauncherIcons;
+import com.android.launcher3.icons.ShortcutCachingLogic;
public class PinRequestHelper {
@@ -82,16 +80,10 @@
ShortcutInfo si = request.getShortcutInfo();
WorkspaceItemInfo info = new WorkspaceItemInfo(si, context);
- // Apply the unbadged icon and fetch the actual icon asynchronously.
- if (FeatureFlags.ENABLE_DEEP_SHORTCUT_ICON_CACHE.get()) {
- fetchAndUpdateShortcutIconAsync(context, info, si, false);
- } else {
- LauncherIcons li = LauncherIcons.obtain(context);
- info.bitmap = li.createShortcutIcon(si, false /* badged */);
- li.recycle();
- LauncherAppState.getInstance(context).getModel()
- .updateAndBindWorkspaceItem(info, si);
- }
+ // Apply the unbadged icon synchronously using the caching logic directly and
+ // fetch the actual icon asynchronously.
+ info.bitmap = new ShortcutCachingLogic().loadIcon(context, si);
+ LauncherAppState.getInstance(context).getModel().updateAndBindWorkspaceItem(info, si);
return info;
} else {
return null;
diff --git a/src/com/android/launcher3/pm/ShortcutConfigActivityInfo.java b/src/com/android/launcher3/pm/ShortcutConfigActivityInfo.java
index b563171..ac0e065 100644
--- a/src/com/android/launcher3/pm/ShortcutConfigActivityInfo.java
+++ b/src/com/android/launcher3/pm/ShortcutConfigActivityInfo.java
@@ -41,7 +41,7 @@
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.WorkspaceItemInfo;
-import com.android.launcher3.icons.ComponentWithLabel;
+import com.android.launcher3.icons.ComponentWithLabelAndIcon;
import com.android.launcher3.icons.IconCache;
import com.android.launcher3.util.PackageUserKey;
@@ -51,7 +51,7 @@
/**
* Wrapper class for representing a shortcut configure activity.
*/
-public abstract class ShortcutConfigActivityInfo implements ComponentWithLabel {
+public abstract class ShortcutConfigActivityInfo implements ComponentWithLabelAndIcon {
private static final String TAG = "SCActivityInfo";
@@ -77,6 +77,7 @@
return LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
}
+ @Override
public abstract Drawable getFullResIcon(IconCache cache);
/**
diff --git a/src/com/android/launcher3/popup/PopupPopulator.java b/src/com/android/launcher3/popup/PopupPopulator.java
index 9faeb40..fdcf04f 100644
--- a/src/com/android/launcher3/popup/PopupPopulator.java
+++ b/src/com/android/launcher3/popup/PopupPopulator.java
@@ -26,8 +26,9 @@
import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.ItemInfo;
+import com.android.launcher3.LauncherAppState;
import com.android.launcher3.WorkspaceItemInfo;
-import com.android.launcher3.icons.LauncherIcons;
+import com.android.launcher3.icons.IconCache;
import com.android.launcher3.notification.NotificationInfo;
import com.android.launcher3.notification.NotificationKeyData;
import com.android.launcher3.notification.NotificationListener;
@@ -153,13 +154,11 @@
String shortcutIdToDeDupe = notificationKeys.isEmpty() ? null
: notificationKeys.get(0).shortcutId;
shortcuts = PopupPopulator.sortAndFilterShortcuts(shortcuts, shortcutIdToDeDupe);
+ IconCache cache = LauncherAppState.getInstance(launcher).getIconCache();
for (int i = 0; i < shortcuts.size() && i < shortcutViews.size(); i++) {
final ShortcutInfo shortcut = shortcuts.get(i);
final WorkspaceItemInfo si = new WorkspaceItemInfo(shortcut, launcher);
- // Use unbadged icon for the menu.
- LauncherIcons li = LauncherIcons.obtain(launcher);
- si.bitmap = li.createShortcutIcon(shortcut, false /* badged */);
- li.recycle();
+ cache.getUnbadgedShortcutIcon(si, shortcut);
si.rank = i;
final DeepShortcutView view = shortcutViews.get(i);
diff --git a/src/com/android/launcher3/provider/RestoreDbTask.java b/src/com/android/launcher3/provider/RestoreDbTask.java
index 407ff31..0fe3673 100644
--- a/src/com/android/launcher3/provider/RestoreDbTask.java
+++ b/src/com/android/launcher3/provider/RestoreDbTask.java
@@ -107,6 +107,7 @@
*/
private void backupWorkspace(Context context, SQLiteDatabase db) throws Exception {
InvariantDeviceProfile idp = LauncherAppState.getIDP(context);
+ // TODO(pinyaoting): Support backing up workspace with multiple grid options.
new GridBackupTable(context, db, idp.numHotseatIcons, idp.numColumns, idp.numRows)
.doBackup(getDefaultProfileId(db), GridBackupTable.OPTION_REQUIRES_SANITIZATION);
}
@@ -114,9 +115,10 @@
private void restoreWorkspace(@NonNull Context context, @NonNull SQLiteDatabase db,
@NonNull DatabaseHelper helper, @NonNull BackupManager backupManager)
throws Exception {
+ // TODO(pinyaoting): Support restoring workspace with multiple grid options.
final InvariantDeviceProfile idp = LauncherAppState.getIDP(context);
- GridBackupTable backupTable = new GridBackupTable(context, db,
- idp.numHotseatIcons, idp.numColumns, idp.numRows);
+ GridBackupTable backupTable = new GridBackupTable(context, db, idp.numHotseatIcons,
+ idp.numColumns, idp.numRows);
if (backupTable.restoreFromRawBackupIfAvailable(getDefaultProfileId(db))) {
sanitizeDB(helper, db, backupManager);
LauncherAppState.getInstance(context).getModel().forceReload();
diff --git a/src/com/android/launcher3/settings/NotificationDotsPreference.java b/src/com/android/launcher3/settings/NotificationDotsPreference.java
index f30470a..a91303a 100644
--- a/src/com/android/launcher3/settings/NotificationDotsPreference.java
+++ b/src/com/android/launcher3/settings/NotificationDotsPreference.java
@@ -20,7 +20,6 @@
import android.app.AlertDialog;
import android.app.Dialog;
-import android.app.DialogFragment;
import android.content.ComponentName;
import android.content.Context;
import android.content.DialogInterface;
@@ -30,13 +29,14 @@
import android.util.AttributeSet;
import android.view.View;
+import androidx.fragment.app.DialogFragment;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceViewHolder;
+
import com.android.launcher3.R;
import com.android.launcher3.notification.NotificationListener;
import com.android.launcher3.util.SecureSettingsObserver;
-import androidx.preference.Preference;
-import androidx.preference.PreferenceViewHolder;
-
/**
* A {@link Preference} for indicating notification dots status.
* Also has utility methods for updating UI based on dots status changes.
diff --git a/src/com/android/launcher3/testing/TestInformationHandler.java b/src/com/android/launcher3/testing/TestInformationHandler.java
index 4af5e0a..4e49c6e 100644
--- a/src/com/android/launcher3/testing/TestInformationHandler.java
+++ b/src/com/android/launcher3/testing/TestInformationHandler.java
@@ -17,16 +17,22 @@
import static android.graphics.Bitmap.Config.ARGB_8888;
+import static com.android.launcher3.allapps.AllAppsStore.DEFER_UPDATES_TEST;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+import android.annotation.TargetApi;
+import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Color;
+import android.graphics.Insets;
+import android.os.Build;
import android.os.Bundle;
import android.os.Debug;
import android.system.Os;
import android.util.Log;
import android.view.View;
+import android.view.WindowInsets;
import androidx.annotation.Keep;
@@ -36,14 +42,19 @@
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherState;
import com.android.launcher3.R;
-import com.android.launcher3.allapps.AllAppsStore;
import com.android.launcher3.util.ResourceBasedOverride;
import java.util.LinkedList;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
+import java.util.function.Function;
+import java.util.function.Supplier;
+/**
+ * Class to handle requests from tests
+ */
+@TargetApi(Build.VERSION_CODES.Q)
public class TestInformationHandler implements ResourceBasedOverride {
public static TestInformationHandler newInstance(Context context) {
@@ -54,7 +65,6 @@
protected Context mContext;
protected DeviceProfile mDeviceProfile;
protected LauncherAppState mLauncherAppState;
- protected Launcher mLauncher;
private static LinkedList mLeaks;
public void init(Context context) {
@@ -62,35 +72,31 @@
mDeviceProfile = InvariantDeviceProfile.INSTANCE.
get(context).getDeviceProfile(context);
mLauncherAppState = LauncherAppState.getInstanceNoCreate();
- mLauncher = Launcher.ACTIVITY_TRACKER.getCreatedActivity();
}
public Bundle call(String method) {
final Bundle response = new Bundle();
switch (method) {
case TestProtocol.REQUEST_ALL_APPS_TO_OVERVIEW_SWIPE_HEIGHT: {
- if (mLauncher == null) return null;
-
- final float progress = LauncherState.OVERVIEW.getVerticalProgress(mLauncher)
- - LauncherState.ALL_APPS.getVerticalProgress(mLauncher);
- final float distance = mLauncher.getAllAppsController().getShiftRange() * progress;
- response.putInt(TestProtocol.TEST_INFO_RESPONSE_FIELD, (int) distance);
- break;
+ return getLauncherUIProperty(Bundle::putInt, l -> {
+ final float progress = LauncherState.OVERVIEW.getVerticalProgress(l)
+ - LauncherState.ALL_APPS.getVerticalProgress(l);
+ final float distance = l.getAllAppsController().getShiftRange() * progress;
+ return (int) distance;
+ });
}
case TestProtocol.REQUEST_HOME_TO_ALL_APPS_SWIPE_HEIGHT: {
- if (mLauncher == null) return null;
-
- final float progress = LauncherState.NORMAL.getVerticalProgress(mLauncher)
- - LauncherState.ALL_APPS.getVerticalProgress(mLauncher);
- final float distance = mLauncher.getAllAppsController().getShiftRange() * progress;
- response.putInt(TestProtocol.TEST_INFO_RESPONSE_FIELD, (int) distance);
- break;
+ return getLauncherUIProperty(Bundle::putInt, l -> {
+ final float progress = LauncherState.NORMAL.getVerticalProgress(l)
+ - LauncherState.ALL_APPS.getVerticalProgress(l);
+ final float distance = l.getAllAppsController().getShiftRange() * progress;
+ return (int) distance;
+ });
}
case TestProtocol.REQUEST_IS_LAUNCHER_INITIALIZED: {
- response.putBoolean(TestProtocol.TEST_INFO_RESPONSE_FIELD, isLauncherInitialized());
- break;
+ return getUIProperty(Bundle::putBoolean, t -> isLauncherInitialized(), () -> true);
}
case TestProtocol.REQUEST_ENABLE_DEBUG_TRACING:
@@ -102,40 +108,33 @@
break;
case TestProtocol.REQUEST_FREEZE_APP_LIST:
- MAIN_EXECUTOR.execute(() ->
- mLauncher.getAppsView().getAppsStore().enableDeferUpdates(
- AllAppsStore.DEFER_UPDATES_TEST));
- break;
-
+ return getLauncherUIProperty(Bundle::putBoolean, l -> {
+ l.getAppsView().getAppsStore().enableDeferUpdates(DEFER_UPDATES_TEST);
+ return true;
+ });
case TestProtocol.REQUEST_UNFREEZE_APP_LIST:
- MAIN_EXECUTOR.execute(() ->
- mLauncher.getAppsView().getAppsStore().disableDeferUpdates(
- AllAppsStore.DEFER_UPDATES_TEST));
- break;
+ return getLauncherUIProperty(Bundle::putBoolean, l -> {
+ l.getAppsView().getAppsStore().disableDeferUpdates(DEFER_UPDATES_TEST);
+ return true;
+ });
case TestProtocol.REQUEST_APP_LIST_FREEZE_FLAGS: {
- try {
- final int deferUpdatesFlags = MAIN_EXECUTOR.submit(() ->
- mLauncher.getAppsView().getAppsStore().getDeferUpdatesFlags()).get();
- response.putInt(TestProtocol.TEST_INFO_RESPONSE_FIELD,
- deferUpdatesFlags);
- } catch (ExecutionException | InterruptedException e) {
- throw new RuntimeException(e);
- }
- break;
+ return getLauncherUIProperty(Bundle::putInt,
+ l -> l.getAppsView().getAppsStore().getDeferUpdatesFlags());
}
case TestProtocol.REQUEST_APPS_LIST_SCROLL_Y: {
- try {
- final int scroll = MAIN_EXECUTOR.submit(() ->
- mLauncher.getAppsView().getActiveRecyclerView().getCurrentScrollY())
- .get();
- response.putInt(TestProtocol.TEST_INFO_RESPONSE_FIELD,
- scroll);
- } catch (ExecutionException | InterruptedException e) {
- throw new RuntimeException(e);
- }
- break;
+ return getLauncherUIProperty(Bundle::putInt,
+ l -> l.getAppsView().getActiveRecyclerView().getCurrentScrollY());
+ }
+
+ case TestProtocol.REQUEST_WINDOW_INSETS: {
+ return getUIProperty(Bundle::putParcelable, a -> {
+ WindowInsets insets = a.getWindow()
+ .getDecorView().getRootWindowInsets();
+ return Insets.max(
+ insets.getSystemGestureInsets(), insets.getSystemWindowInsets());
+ }, this::getCurrentActivity);
}
case TestProtocol.REQUEST_PID: {
@@ -176,7 +175,6 @@
case TestProtocol.REQUEST_VIEW_LEAK: {
if (mLeaks == null) mLeaks = new LinkedList();
-
mLeaks.add(new View(mContext));
break;
}
@@ -191,15 +189,14 @@
}
protected boolean isLauncherInitialized() {
- if (TestProtocol.sDebugTracing) {
- Log.d(TestProtocol.LAUNCHER_DIDNT_INITIALIZE,
- "isLauncherInitialized " + Launcher.ACTIVITY_TRACKER.getCreatedActivity() + ", "
- + LauncherAppState.getInstance(mContext).getModel().isModelLoaded());
- }
return Launcher.ACTIVITY_TRACKER.getCreatedActivity() == null
|| LauncherAppState.getInstance(mContext).getModel().isModelLoaded();
}
+ protected Activity getCurrentActivity() {
+ return Launcher.ACTIVITY_TRACKER.getCreatedActivity();
+ }
+
private static void runGcAndFinalizersSync() {
Runtime.getRuntime().gc();
Runtime.getRuntime().runFinalization();
@@ -216,6 +213,47 @@
}
}
+ /**
+ * Returns the result by getting a Launcher property on UI thread
+ */
+ public static <T> Bundle getLauncherUIProperty(
+ BundleSetter<T> bundleSetter, Function<Launcher, T> provider) {
+ return getUIProperty(bundleSetter, provider, Launcher.ACTIVITY_TRACKER::getCreatedActivity);
+ }
+
+ /**
+ * Returns the result by getting a generic property on UI thread
+ */
+ private static <S, T> Bundle getUIProperty(
+ BundleSetter<T> bundleSetter, Function<S, T> provider, Supplier<S> targetSupplier) {
+ try {
+ return MAIN_EXECUTOR.submit(() -> {
+ S target = targetSupplier.get();
+ if (target == null) {
+ return null;
+ }
+ T value = provider.apply(target);
+ Bundle response = new Bundle();
+ bundleSetter.set(response, TestProtocol.TEST_INFO_RESPONSE_FIELD, value);
+ return response;
+ }).get();
+ } catch (ExecutionException | InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Generic interface for setting a fiend in bundle
+ * @param <T> the type of value being set
+ */
+ public interface BundleSetter<T> {
+
+ /**
+ * Sets any generic property to the bundle
+ */
+ void set(Bundle b, String key, T value);
+ }
+
// Create the observer in the scope of a method to minimize the chance that
// it remains live in a DEX/machine register at the point of the fence guard.
// This must be kept to avoid R8 inlining it.
diff --git a/src/com/android/launcher3/testing/TestLogging.java b/src/com/android/launcher3/testing/TestLogging.java
index fd066c1..d522d81 100644
--- a/src/com/android/launcher3/testing/TestLogging.java
+++ b/src/com/android/launcher3/testing/TestLogging.java
@@ -17,13 +17,30 @@
package com.android.launcher3.testing;
import android.util.Log;
+import android.view.MotionEvent;
import com.android.launcher3.Utilities;
public final class TestLogging {
- public static void recordEvent(String event) {
+ private static void recordEventSlow(String sequence, String event) {
+ Log.d(TestProtocol.TAPL_EVENTS_TAG, sequence + " / " + event);
+ }
+
+ public static void recordEvent(String sequence, String event) {
if (Utilities.IS_RUNNING_IN_TEST_HARNESS) {
- Log.d(TestProtocol.TAPL_EVENTS_TAG, event);
+ recordEventSlow(sequence, event);
+ }
+ }
+
+ public static void recordEvent(String sequence, String message, Object parameter) {
+ if (Utilities.IS_RUNNING_IN_TEST_HARNESS) {
+ recordEventSlow(sequence, message + ": " + parameter);
+ }
+ }
+
+ public static void recordMotionEvent(String sequence, String message, MotionEvent event) {
+ if (Utilities.IS_RUNNING_IN_TEST_HARNESS && event.getAction() != MotionEvent.ACTION_MOVE) {
+ recordEventSlow(sequence, message + ": " + event);
}
}
}
diff --git a/src/com/android/launcher3/testing/TestProtocol.java b/src/com/android/launcher3/testing/TestProtocol.java
index 2f053c9..f995c61 100644
--- a/src/com/android/launcher3/testing/TestProtocol.java
+++ b/src/com/android/launcher3/testing/TestProtocol.java
@@ -33,6 +33,8 @@
public static final int BACKGROUND_APP_STATE_ORDINAL = 6;
public static final int HINT_STATE_ORDINAL = 7;
public static final String TAPL_EVENTS_TAG = "TaplEvents";
+ public static final String SEQUENCE_MAIN = "Main";
+ public static final String SEQUENCE_TIS = "TIS";
public static String stateOrdinalToString(int ordinal) {
switch (ordinal) {
@@ -75,8 +77,7 @@
public static final String REQUEST_UNFREEZE_APP_LIST = "unfreeze-app-list";
public static final String REQUEST_APP_LIST_FREEZE_FLAGS = "app-list-freeze-flags";
public static final String REQUEST_APPS_LIST_SCROLL_Y = "apps-list-scroll-y";
- public static final String REQUEST_OVERVIEW_LEFT_GESTURE_MARGIN = "overview-left-margin";
- public static final String REQUEST_OVERVIEW_RIGHT_GESTURE_MARGIN = "overview-right-margin";
+ public static final String REQUEST_WINDOW_INSETS = "window-insets";
public static final String REQUEST_PID = "pid";
public static final String REQUEST_TOTAL_PSS_KB = "total_pss";
public static final String REQUEST_JAVA_LEAK = "java-leak";
@@ -92,5 +93,4 @@
public static final String NO_BACKGROUND_TO_OVERVIEW_TAG = "b/138251824";
public static final String APP_NOT_DISABLED = "b/139891609";
- public static final String LAUNCHER_DIDNT_INITIALIZE = "b/148313079";
}
diff --git a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
index 7ae0526..9df6241 100644
--- a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
+++ b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
@@ -23,7 +23,7 @@
import static com.android.launcher3.LauncherStateManager.ATOMIC_OVERVIEW_SCALE_COMPONENT;
import static com.android.launcher3.LauncherStateManager.NON_ATOMIC_COMPONENT;
import static com.android.launcher3.anim.Interpolators.scrollInterpolatorForVelocity;
-import static com.android.launcher3.config.FeatureFlags.QUICKSTEP_SPRINGS;
+import static com.android.launcher3.config.FeatureFlags.UNSTABLE_SPRINGS;
import static com.android.launcher3.util.DefaultDisplay.getSingleFrameMs;
import android.animation.Animator;
@@ -435,7 +435,7 @@
updateSwipeCompleteAnimation(anim, Math.max(duration, getRemainingAtomicDuration()),
targetState, velocity, fling);
mCurrentAnimation.dispatchOnStartWithVelocity(endProgress, progressVelocity);
- if (fling && targetState == LauncherState.ALL_APPS && !QUICKSTEP_SPRINGS.get()) {
+ if (fling && targetState == LauncherState.ALL_APPS && !UNSTABLE_SPRINGS.get()) {
mLauncher.getAppsView().addSpringFromFlingUpdateListener(anim, velocity);
}
anim.start();
diff --git a/src/com/android/launcher3/util/ActivityTracker.java b/src/com/android/launcher3/util/ActivityTracker.java
index b83c8fc..499f655 100644
--- a/src/com/android/launcher3/util/ActivityTracker.java
+++ b/src/com/android/launcher3/util/ActivityTracker.java
@@ -45,13 +45,7 @@
}
public void onActivityDestroyed(T activity) {
- if (TestProtocol.sDebugTracing) {
- Log.d(TestProtocol.LAUNCHER_DIDNT_INITIALIZE, "onActivityDestroyed");
- }
if (mCurrentActivity.get() == activity) {
- if (TestProtocol.sDebugTracing) {
- Log.d(TestProtocol.LAUNCHER_DIDNT_INITIALIZE, "onActivityDestroyed: clear");
- }
mCurrentActivity.clear();
}
}
@@ -116,10 +110,6 @@
}
public boolean handleCreate(T activity) {
- if (TestProtocol.sDebugTracing) {
- Log.d(TestProtocol.LAUNCHER_DIDNT_INITIALIZE,
- "ActivityTracker.handleCreate " + mCurrentActivity.get() + " => " + activity);
- }
mCurrentActivity = new WeakReference<>(activity);
return handleIntent(activity, activity.getIntent(), false, false);
}
diff --git a/src/com/android/launcher3/util/IOUtils.java b/src/com/android/launcher3/util/IOUtils.java
index 4a4a5ca..fcb96d7 100644
--- a/src/com/android/launcher3/util/IOUtils.java
+++ b/src/com/android/launcher3/util/IOUtils.java
@@ -19,7 +19,6 @@
import android.content.Context;
import android.util.Log;
-import com.android.launcher3.Utilities;
import com.android.launcher3.config.FeatureFlags;
import java.io.ByteArrayOutputStream;
@@ -67,7 +66,7 @@
* Utility method to debug binary data
*/
public static String createTempFile(Context context, byte[] data) {
- if (!FeatureFlags.IS_DOGFOOD_BUILD) {
+ if (!FeatureFlags.IS_STUDIO_BUILD) {
throw new IllegalStateException("Method only allowed in development mode");
}
@@ -87,7 +86,7 @@
try {
c.close();
} catch (IOException e) {
- if (FeatureFlags.IS_DOGFOOD_BUILD) {
+ if (FeatureFlags.IS_STUDIO_BUILD) {
Log.d(TAG, "Error closing", e);
}
}
diff --git a/src/com/android/launcher3/util/Preconditions.java b/src/com/android/launcher3/util/Preconditions.java
index ed66422..63d56e4 100644
--- a/src/com/android/launcher3/util/Preconditions.java
+++ b/src/com/android/launcher3/util/Preconditions.java
@@ -28,25 +28,25 @@
public class Preconditions {
public static void assertNotNull(Object o) {
- if (FeatureFlags.IS_DOGFOOD_BUILD && o == null) {
+ if (FeatureFlags.IS_STUDIO_BUILD && o == null) {
throw new IllegalStateException();
}
}
public static void assertWorkerThread() {
- if (FeatureFlags.IS_DOGFOOD_BUILD && !isSameLooper(MODEL_EXECUTOR.getLooper())) {
+ if (FeatureFlags.IS_STUDIO_BUILD && !isSameLooper(MODEL_EXECUTOR.getLooper())) {
throw new IllegalStateException();
}
}
public static void assertUIThread() {
- if (FeatureFlags.IS_DOGFOOD_BUILD && !isSameLooper(Looper.getMainLooper())) {
+ if (FeatureFlags.IS_STUDIO_BUILD && !isSameLooper(Looper.getMainLooper())) {
throw new IllegalStateException();
}
}
public static void assertNonUiThread() {
- if (FeatureFlags.IS_DOGFOOD_BUILD && isSameLooper(Looper.getMainLooper())) {
+ if (FeatureFlags.IS_STUDIO_BUILD && isSameLooper(Looper.getMainLooper())) {
throw new IllegalStateException();
}
}
diff --git a/src/com/android/launcher3/util/ShortcutUtil.java b/src/com/android/launcher3/util/ShortcutUtil.java
index a03b743..49c97da 100644
--- a/src/com/android/launcher3/util/ShortcutUtil.java
+++ b/src/com/android/launcher3/util/ShortcutUtil.java
@@ -15,19 +15,10 @@
*/
package com.android.launcher3.util;
-import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
-
-import android.content.Context;
-import android.content.pm.ShortcutInfo;
-
-import androidx.annotation.NonNull;
-
import com.android.launcher3.ItemInfo;
-import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.Utilities;
import com.android.launcher3.WorkspaceItemInfo;
-import com.android.launcher3.icons.LauncherIcons;
import com.android.launcher3.model.WidgetsModel;
import com.android.launcher3.shortcuts.ShortcutKey;
@@ -70,21 +61,6 @@
&& info instanceof WorkspaceItemInfo;
}
- /**
- * Fetch the shortcut icon in background, then update the UI.
- */
- public static void fetchAndUpdateShortcutIconAsync(
- @NonNull Context context, @NonNull WorkspaceItemInfo info, @NonNull ShortcutInfo si,
- boolean badged) {
- MODEL_EXECUTOR.execute(() -> {
- try (LauncherIcons li = LauncherIcons.obtain(context)) {
- info.bitmap = li.createShortcutIcon(si, badged, null);
- LauncherAppState.getInstance(context).getModel()
- .updateAndBindWorkspaceItem(info, si);
- }
- });
- }
-
private static boolean isActive(ItemInfo info) {
boolean isLoading = info instanceof WorkspaceItemInfo
&& ((WorkspaceItemInfo) info).hasPromiseIconUi();
diff --git a/src/com/android/launcher3/views/WorkFooterContainer.java b/src/com/android/launcher3/views/WorkFooterContainer.java
index f8add9a..9ac8230 100644
--- a/src/com/android/launcher3/views/WorkFooterContainer.java
+++ b/src/com/android/launcher3/views/WorkFooterContainer.java
@@ -119,6 +119,13 @@
mWorkModeSwitch.refresh();
}
+ /**
+ * Returns work mode switch
+ */
+ public WorkModeSwitch getWorkModeSwitch() {
+ return mWorkModeSwitch;
+ }
+
private boolean shouldShowWorkFooter() {
Launcher launcher = Launcher.getLauncher(getContext());
return Utilities.ATLEAST_P && (hasShortcutsPermission(launcher)
diff --git a/src_shortcuts_overrides/com/android/launcher3/model/WidgetsModel.java b/src_shortcuts_overrides/com/android/launcher3/model/WidgetsModel.java
index 22c0874..282867a 100644
--- a/src_shortcuts_overrides/com/android/launcher3/model/WidgetsModel.java
+++ b/src_shortcuts_overrides/com/android/launcher3/model/WidgetsModel.java
@@ -21,7 +21,7 @@
import com.android.launcher3.Utilities;
import com.android.launcher3.compat.AlphabeticIndexCompat;
import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.icons.ComponentWithLabel;
+import com.android.launcher3.icons.ComponentWithLabelAndIcon;
import com.android.launcher3.icons.IconCache;
import com.android.launcher3.pm.ShortcutConfigActivityInfo;
import com.android.launcher3.util.MultiHashMap;
@@ -85,12 +85,13 @@
* @param packageUser If null, all widgets and shortcuts are updated and returned, otherwise
* only widgets and shortcuts associated with the package/user are.
*/
- public List<ComponentWithLabel> update(LauncherAppState app, @Nullable PackageUserKey packageUser) {
+ public List<ComponentWithLabelAndIcon> update(
+ LauncherAppState app, @Nullable PackageUserKey packageUser) {
Preconditions.assertWorkerThread();
Context context = app.getContext();
final ArrayList<WidgetItem> widgetsAndShortcuts = new ArrayList<>();
- List<ComponentWithLabel> updatedItems = new ArrayList<>();
+ List<ComponentWithLabelAndIcon> updatedItems = new ArrayList<>();
try {
InvariantDeviceProfile idp = app.getInvariantDeviceProfile();
PackageManager pm = app.getContext().getPackageManager();
@@ -114,7 +115,7 @@
}
setWidgetsAndShortcuts(widgetsAndShortcuts, app, packageUser);
} catch (Exception e) {
- if (!FeatureFlags.IS_DOGFOOD_BUILD && Utilities.isBinderSizeError(e)) {
+ if (!FeatureFlags.IS_STUDIO_BUILD && Utilities.isBinderSizeError(e)) {
// the returned value may be incomplete and will not be refreshed until the next
// time Launcher starts.
// TODO: after figuring out a repro step, introduce a dirty bit to check when
diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/DisplayRotationListener.java b/src_ui_overrides/com/android/launcher3/uioverrides/DisplayRotationListener.java
deleted file mode 100644
index b1a67e9..0000000
--- a/src_ui_overrides/com/android/launcher3/uioverrides/DisplayRotationListener.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.launcher3.uioverrides;
-
-import android.content.Context;
-import android.view.OrientationEventListener;
-
-/**
- * Utility class for listening for rotation changes
- */
-public class DisplayRotationListener extends OrientationEventListener {
-
- private final Runnable mCallback;
-
- public DisplayRotationListener(Context context, Runnable callback) {
- super(context);
- mCallback = callback;
- }
-
- @Override
- public void onOrientationChanged(int i) {
- mCallback.run();
- }
-}
diff --git a/tests/Android.mk b/tests/Android.mk
index d1a6c06..a9fff8e 100644
--- a/tests/Android.mk
+++ b/tests/Android.mk
@@ -51,7 +51,7 @@
androidx.test.rules \
androidx.test.uiautomator_uiautomator \
mockito-target-minus-junit4 \
- launcher-log-protos-lite
+ launcher_log_protos_lite
ifneq (,$(wildcard frameworks/base))
LOCAL_PRIVATE_PLATFORM_APIS := true
diff --git a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
index 4b72882..54caf1e 100644
--- a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
+++ b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
@@ -18,9 +18,6 @@
import static androidx.test.InstrumentationRegistry.getInstrumentation;
-import static com.android.launcher3.util.rule.TestStabilityRule.LOCAL;
-import static com.android.launcher3.util.rule.TestStabilityRule.UNBUNDLED_POSTSUBMIT;
-
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
@@ -39,7 +36,6 @@
import com.android.launcher3.tapl.AppIconMenuItem;
import com.android.launcher3.tapl.Widgets;
import com.android.launcher3.tapl.Workspace;
-import com.android.launcher3.util.rule.TestStabilityRule.Stability;
import com.android.launcher3.views.OptionsPopupView;
import com.android.launcher3.widget.WidgetsFullSheet;
import com.android.launcher3.widget.WidgetsRecyclerView;
@@ -117,9 +113,7 @@
mLauncher.pressHome();
}
- // b/146432215: remove @Stability after 2/1/2020 if this test doesn't flake
@Test
- @Stability(flavors = LOCAL | UNBUNDLED_POSTSUBMIT)
public void testOpenHomeSettingsFromWorkspace() {
mDevice.pressMenu();
mDevice.waitForIdle();
diff --git a/tests/src/com/android/launcher3/ui/WorkTabTest.java b/tests/src/com/android/launcher3/ui/WorkTabTest.java
index d9edc35..5aa0090 100644
--- a/tests/src/com/android/launcher3/ui/WorkTabTest.java
+++ b/tests/src/com/android/launcher3/ui/WorkTabTest.java
@@ -16,17 +16,32 @@
package com.android.launcher3.ui;
import static com.android.launcher3.LauncherState.ALL_APPS;
+import static com.android.launcher3.util.rule.TestStabilityRule.LOCAL;
+import static com.android.launcher3.util.rule.TestStabilityRule.UNBUNDLED_POSTSUBMIT;
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
+import android.os.Process;
+import android.os.UserHandle;
+import android.os.UserManager;
+
import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.launcher3.allapps.AllAppsContainerView;
+import com.android.launcher3.allapps.AllAppsPagedView;
+import com.android.launcher3.util.rule.TestStabilityRule;
+import com.android.launcher3.views.WorkFooterContainer;
+
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.List;
+import java.util.Objects;
+
@LargeTest
@RunWith(AndroidJUnit4.class)
public class WorkTabTest extends AbstractLauncherUiTest {
@@ -52,16 +67,50 @@
}
@Test
+ // b/143285809 Remove @Stability on 02/21/20 if the test doesn't flake.
+ @TestStabilityRule.Stability(flavors = LOCAL | UNBUNDLED_POSTSUBMIT)
public void workTabExists() {
mDevice.pressHome();
- waitForLauncherCondition("Launcher didn't start", launcher -> launcher != null);
+ waitForLauncherCondition("Launcher didn't start", Objects::nonNull);
executeOnLauncher(launcher -> launcher.getStateManager().goToState(ALL_APPS));
- /*
- assertTrue("Personal tab is missing", waitForLauncherCondition(
- launcher -> launcher.getAppsView().isPersonalTabVisible()));
- assertTrue("Work tab is missing", waitForLauncherCondition(
- launcher -> launcher.getAppsView().isWorkTabVisible()));
- */
+ waitForLauncherCondition("Personal tab is missing",
+ launcher -> launcher.getAppsView().isPersonalTabVisible());
+ waitForLauncherCondition("Work tab is missing",
+ launcher -> launcher.getAppsView().isWorkTabVisible());
}
+
+ @Test
+ // b/143285809 Remove @Stability on 02/21/20 if the test doesn't flake.
+ @TestStabilityRule.Stability(flavors = LOCAL | UNBUNDLED_POSTSUBMIT)
+ public void toggleWorks() {
+ mDevice.pressHome();
+ waitForLauncherCondition("Launcher didn't start", Objects::nonNull);
+ executeOnLauncher(launcher -> launcher.getStateManager().goToState(ALL_APPS));
+ waitForState("Launcher internal state didn't switch to All Apps", () -> ALL_APPS);
+ getOnceNotNull("Apps view did not bind",
+ launcher -> launcher.getAppsView().getWorkFooterContainer());
+
+ UserManager userManager = getFromLauncher(l -> l.getSystemService(UserManager.class));
+ assertEquals(2, userManager.getUserProfiles().size());
+ UserHandle workProfile = getFromLauncher(l -> {
+ UserHandle myHandle = Process.myUserHandle();
+ List<UserHandle> userProfiles = userManager.getUserProfiles();
+ return userProfiles.get(0) == myHandle ? userProfiles.get(1) : userProfiles.get(0);
+ });
+
+ waitForLauncherCondition("work profile can't be turned off",
+ l -> userManager.requestQuietModeEnabled(true, workProfile));
+
+ assertTrue(userManager.isQuietModeEnabled(workProfile));
+ executeOnLauncher(launcher -> {
+ WorkFooterContainer wf = launcher.getAppsView().getWorkFooterContainer();
+ ((AllAppsPagedView) launcher.getAppsView().getContentView()).snapToPageImmediately(
+ AllAppsContainerView.AdapterHolder.WORK);
+ wf.getWorkModeSwitch().toggle();
+ });
+ waitForLauncherCondition("Work toggle did not work",
+ l -> l.getSystemService(UserManager.class).isQuietModeEnabled(workProfile));
+ }
+
}
\ No newline at end of file
diff --git a/tests/src/com/android/launcher3/util/rule/FailureInvestigator.java b/tests/src/com/android/launcher3/util/rule/FailureInvestigator.java
index d6dfdd9..5880eb6 100644
--- a/tests/src/com/android/launcher3/util/rule/FailureInvestigator.java
+++ b/tests/src/com/android/launcher3/util/rule/FailureInvestigator.java
@@ -55,7 +55,7 @@
private static final ExceptionMatch[] EXCEPTION_MATCHES = {
new ExceptionMatch(
"java.lang.AssertionError: http://go/tapl : Tests are broken by a "
- + "non-Launcher system error: Phone is locked",
+ + "non-Launcher system error: (Phone is locked|Screen is empty)",
new LogcatMatch[]{
new LogcatMatch(
"BroadcastQueue: Can't deliver broadcast to com.android"
@@ -87,6 +87,11 @@
return 145935261;
}
+ if (matches("java\\.lang\\.AssertionError\\: http\\:\\/\\/go\\/tapl \\: want to get "
+ + "workspace object; Presence of recents button doesn't match the interaction "
+ + "mode, mode\\=ZERO_BUTTON, hasRecents\\=true", exception)) {
+ return 148422894;
+ }
final String logSinceBoot;
try {
diff --git a/tests/src/com/android/launcher3/util/rule/FailureWatcher.java b/tests/src/com/android/launcher3/util/rule/FailureWatcher.java
index 02d07bb..cdda0f0 100644
--- a/tests/src/com/android/launcher3/util/rule/FailureWatcher.java
+++ b/tests/src/com/android/launcher3/util/rule/FailureWatcher.java
@@ -8,7 +8,6 @@
import org.junit.rules.TestWatcher;
import org.junit.runner.Description;
-import org.junit.runners.model.Statement;
import java.io.ByteArrayOutputStream;
import java.io.File;
@@ -16,7 +15,6 @@
public class FailureWatcher extends TestWatcher {
private static final String TAG = "FailureWatcher";
- private static boolean sHadFailedTestDeinitialization;
final private UiDevice mDevice;
public FailureWatcher(UiDevice device) {
@@ -62,35 +60,4 @@
device.takeScreenshot(new File(pathname));
}
-
- @Override
- public Statement apply(Statement base, Description description) {
- return new Statement() {
-
- @Override
- public void evaluate() throws Throwable {
- if (sHadFailedTestDeinitialization) {
- Log.d(TAG, "Skipping due to a recent test deinitialization failure: " +
- description.getDisplayName());
- return;
- }
-
- try {
- base.evaluate();
- } catch (Throwable e) {
- final String stackTrace = Log.getStackTraceString(e);
- if (!stackTrace.contains(
- "androidx.test.internal.runner.junit4.statement.RunBefores.evaluate")) {
- // Test failed to deinitialize. Since the global state is probably
- // corrupted, won't execute other tests.
- Log.d(TAG,
- "Detected an exception from test finalizer, will skip further "
- + "tests: " + stackTrace);
- sHadFailedTestDeinitialization = true;
- }
- throw e;
- }
- }
- };
- }
}
diff --git a/tests/tapl/com/android/launcher3/tapl/AddToHomeScreenPrompt.java b/tests/tapl/com/android/launcher3/tapl/AddToHomeScreenPrompt.java
index afb50e0..468f54c 100644
--- a/tests/tapl/com/android/launcher3/tapl/AddToHomeScreenPrompt.java
+++ b/tests/tapl/com/android/launcher3/tapl/AddToHomeScreenPrompt.java
@@ -21,6 +21,8 @@
import androidx.test.uiautomator.By;
import androidx.test.uiautomator.UiObject2;
+import com.android.launcher3.testing.TestProtocol;
+
import java.util.regex.Pattern;
public class AddToHomeScreenPrompt {
@@ -38,6 +40,13 @@
public void addAutomatically() {
try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) {
+ if (mLauncher.getNavigationModel()
+ != LauncherInstrumentation.NavigationModel.THREE_BUTTON) {
+ 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();
diff --git a/tests/tapl/com/android/launcher3/tapl/AppIcon.java b/tests/tapl/com/android/launcher3/tapl/AppIcon.java
index 3f814fd..8932291 100644
--- a/tests/tapl/com/android/launcher3/tapl/AppIcon.java
+++ b/tests/tapl/com/android/launcher3/tapl/AppIcon.java
@@ -22,6 +22,8 @@
import androidx.test.uiautomator.BySelector;
import androidx.test.uiautomator.UiObject2;
+import com.android.launcher3.testing.TestProtocol;
+
import java.util.regex.Pattern;
/**
@@ -56,6 +58,6 @@
@Override
protected void expectActivityStartEvents() {
- mLauncher.expectEvent(START_EVENT);
+ mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, START_EVENT);
}
}
diff --git a/tests/tapl/com/android/launcher3/tapl/AppIconMenuItem.java b/tests/tapl/com/android/launcher3/tapl/AppIconMenuItem.java
index fadfd9f..f8dd89c 100644
--- a/tests/tapl/com/android/launcher3/tapl/AppIconMenuItem.java
+++ b/tests/tapl/com/android/launcher3/tapl/AppIconMenuItem.java
@@ -18,6 +18,8 @@
import androidx.test.uiautomator.UiObject2;
+import com.android.launcher3.testing.TestProtocol;
+
import java.util.regex.Pattern;
/**
@@ -45,6 +47,6 @@
@Override
protected void expectActivityStartEvents() {
- mLauncher.expectEvent(START_SHORTCUT_EVENT);
+ mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, START_SHORTCUT_EVENT);
}
}
diff --git a/tests/tapl/com/android/launcher3/tapl/Background.java b/tests/tapl/com/android/launcher3/tapl/Background.java
index 9f29a1a..2acab97 100644
--- a/tests/tapl/com/android/launcher3/tapl/Background.java
+++ b/tests/tapl/com/android/launcher3/tapl/Background.java
@@ -131,7 +131,7 @@
}
case THREE_BUTTON:
- mLauncher.expectEvent(SQUARE_BUTTON_EVENT);
+ mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, SQUARE_BUTTON_EVENT);
mLauncher.runToState(
() -> mLauncher.waitForSystemUiObject("recent_apps").click(),
OVERVIEW_STATE_ORDINAL);
@@ -195,14 +195,14 @@
case THREE_BUTTON:
// Double press the recents button.
UiObject2 recentsButton = mLauncher.waitForSystemUiObject("recent_apps");
- mLauncher.expectEvent(SQUARE_BUTTON_EVENT);
+ mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, SQUARE_BUTTON_EVENT);
mLauncher.runToState(() -> recentsButton.click(), OVERVIEW_STATE_ORDINAL);
mLauncher.getOverview();
- mLauncher.expectEvent(SQUARE_BUTTON_EVENT);
+ mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, SQUARE_BUTTON_EVENT);
recentsButton.click();
break;
}
- mLauncher.expectEvent(TASK_START_EVENT);
+ mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, TASK_START_EVENT);
}
protected String getSwipeHeightRequestName() {
diff --git a/tests/tapl/com/android/launcher3/tapl/BaseOverview.java b/tests/tapl/com/android/launcher3/tapl/BaseOverview.java
index e5c83e2..a769acf 100644
--- a/tests/tapl/com/android/launcher3/tapl/BaseOverview.java
+++ b/tests/tapl/com/android/launcher3/tapl/BaseOverview.java
@@ -23,8 +23,6 @@
import androidx.test.uiautomator.Direction;
import androidx.test.uiautomator.UiObject2;
-import com.android.launcher3.testing.TestProtocol;
-
import java.util.Collections;
import java.util.List;
@@ -58,9 +56,7 @@
mLauncher.addContextLayer("want to fling forward in overview")) {
LauncherInstrumentation.log("Overview.flingForward before fling");
final UiObject2 overview = verifyActiveContainer();
- final int leftMargin = mLauncher.getTestInfo(
- TestProtocol.REQUEST_OVERVIEW_LEFT_GESTURE_MARGIN).
- getInt(TestProtocol.TEST_INFO_RESPONSE_FIELD);
+ final int leftMargin = mLauncher.getTargetInsets().left;
mLauncher.scroll(
overview, Direction.LEFT, new Rect(leftMargin + 1, 0, 0, 0), 20, false);
verifyActiveContainer();
@@ -96,9 +92,7 @@
mLauncher.addContextLayer("want to fling backward in overview")) {
LauncherInstrumentation.log("Overview.flingBackward before fling");
final UiObject2 overview = verifyActiveContainer();
- final int rightMargin = mLauncher.getTestInfo(
- TestProtocol.REQUEST_OVERVIEW_RIGHT_GESTURE_MARGIN).
- getInt(TestProtocol.TEST_INFO_RESPONSE_FIELD);
+ final int rightMargin = mLauncher.getTargetInsets().right;
mLauncher.scroll(
overview, Direction.RIGHT, new Rect(0, 0, rightMargin + 1, 0), 20, false);
verifyActiveContainer();
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index 7c49f50..60ec3a4 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -33,6 +33,7 @@
import android.content.pm.PackageManager;
import android.content.pm.ProviderInfo;
import android.content.res.Resources;
+import android.graphics.Insets;
import android.graphics.Point;
import android.graphics.Rect;
import android.net.Uri;
@@ -75,8 +76,10 @@
import java.util.Collections;
import java.util.Date;
import java.util.Deque;
+import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
+import java.util.Map;
import java.util.concurrent.TimeoutException;
import java.util.function.Consumer;
import java.util.function.Function;
@@ -101,13 +104,17 @@
static final Pattern EVENT_LOG_ENTRY = Pattern.compile(
"(?<time>[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9] "
+ "[0-9][0-9]:[0-9][0-9]:[0-9][0-9]\\.[0-9][0-9][0-9])"
- + ".*" + TestProtocol.TAPL_EVENTS_TAG + ": (?<event>.*)");
+ + ".*" + TestProtocol.TAPL_EVENTS_TAG + ": (?<sequence>[a-zA-Z]+) / "
+ + "(?<event>.*)");
private static final Pattern EVENT_TOUCH_DOWN = getTouchEventPattern("ACTION_DOWN");
private static final Pattern EVENT_TOUCH_UP = getTouchEventPattern("ACTION_UP");
private static final Pattern EVENT_TOUCH_CANCEL = getTouchEventPattern("ACTION_CANCEL");
private static final Pattern EVENT_PILFER_POINTERS = Pattern.compile("pilferPointers");
+ static final Pattern EVENT_TOUCH_DOWN_TIS = getTouchEventPatternTIS("ACTION_DOWN");
+ static final Pattern EVENT_TOUCH_UP_TIS = getTouchEventPatternTIS("ACTION_UP");
+
// Types for launcher containers that the user is interacting with. "Background" is a
// pseudo-container corresponding to inactive launcher covered by another app.
public enum ContainerType {
@@ -117,7 +124,7 @@
public enum NavigationModel {ZERO_BUTTON, TWO_BUTTON, THREE_BUTTON}
// Where the gesture happens: outside of Launcher, inside or from inside to outside.
- enum GestureScope {
+ public enum GestureScope {
OUTSIDE, INSIDE, INSIDE_TO_OUTSIDE
}
@@ -146,7 +153,7 @@
}
}
- interface Closable extends AutoCloseable {
+ public interface Closable extends AutoCloseable {
void close();
}
@@ -169,19 +176,27 @@
private Consumer<ContainerType> mOnSettledStateAction;
+ // Map from an event sequence name to an ordered list of expected events in that sequence.
// Not null when we are collecting expected events to compare with actual ones.
- private List<Pattern> mExpectedEvents;
+ private Map<String, List<Pattern>> mExpectedEvents;
private Date mStartRecordingTime;
private boolean mCheckEventsForSuccessfulGestures = false;
- private static Pattern getTouchEventPattern(String action) {
+ private static Pattern getTouchEventPattern(String prefix, String action) {
// The pattern includes sanity checks that we don't get a multi-touch events or other
// surprises.
return Pattern.compile(
- "Touch event: MotionEvent.*?action=" + action + ".*?id\\[0\\]=0"
- +
- ".*?toolType\\[0\\]=TOOL_TYPE_FINGER.*?buttonState=0.*?pointerCount=1");
+ prefix + ": MotionEvent.*?action=" + action + ".*?id\\[0\\]=0"
+ + ".*?toolType\\[0\\]=TOOL_TYPE_FINGER.*?buttonState=0.*?pointerCount=1");
+ }
+
+ private static Pattern getTouchEventPattern(String action) {
+ return getTouchEventPattern("Touch event", action);
+ }
+
+ private static Pattern getTouchEventPatternTIS(String action) {
+ return getTouchEventPattern("TouchInteractionService.onInputEvent", action);
}
/**
@@ -257,6 +272,11 @@
return getContext().getContentResolver().call(mTestProviderUri, request, null, null);
}
+ Insets getTargetInsets() {
+ return getTestInfo(TestProtocol.REQUEST_WINDOW_INSETS)
+ .getParcelable(TestProtocol.TEST_INFO_RESPONSE_FIELD);
+ }
+
void setActiveContainer(VisibleContainer container) {
sActiveContainer = new WeakReference<>(container);
}
@@ -341,7 +361,8 @@
public void checkForAnomaly() {
final String anomalyMessage = getAnomalyMessage();
if (anomalyMessage != null) {
- String message = "Tests are broken by a non-Launcher system error: " + anomalyMessage;
+ String message = "http://go/tapl : Tests are broken by a non-Launcher system error: "
+ + anomalyMessage;
log("Hierarchy dump for: " + message);
dumpViewHierarchy();
@@ -489,6 +510,12 @@
assertEquals("Unexpected display rotation",
mExpectedRotation, mDevice.getDisplayRotation());
+ // b/148422894
+ for (int i = 0; i != 600; ++i) {
+ if (getNavigationModeMismatchError() == null) break;
+ sleep(100);
+ }
+
final String error = getNavigationModeMismatchError();
assertTrue(error, error == null);
log("verifyContainerType: " + containerType);
@@ -639,10 +666,14 @@
if (hasLauncherObject(CONTEXT_MENU_RES_ID) ||
hasLauncherObject(WIDGETS_RES_ID)
&& !mDevice.isNaturalOrientation()) {
- expectEvent(EVENT_PILFER_POINTERS);
+ expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_PILFER_POINTERS);
}
}
+ if (getNavigationModel() == NavigationModel.TWO_BUTTON) {
+ expectEvent(TestProtocol.SEQUENCE_TIS, EVENT_TOUCH_DOWN_TIS);
+ expectEvent(TestProtocol.SEQUENCE_TIS, EVENT_TOUCH_UP_TIS);
+ }
runToState(
waitForSystemUiObject("home")::click,
NORMAL_STATE_ORDINAL,
@@ -922,8 +953,12 @@
}
void clickLauncherObject(UiObject2 object) {
- expectEvent(LauncherInstrumentation.EVENT_TOUCH_DOWN);
- expectEvent(LauncherInstrumentation.EVENT_TOUCH_UP);
+ expectEvent(TestProtocol.SEQUENCE_MAIN, LauncherInstrumentation.EVENT_TOUCH_DOWN);
+ expectEvent(TestProtocol.SEQUENCE_MAIN, LauncherInstrumentation.EVENT_TOUCH_UP);
+ if (getNavigationModel() != NavigationModel.THREE_BUTTON) {
+ expectEvent(TestProtocol.SEQUENCE_TIS, LauncherInstrumentation.EVENT_TOUCH_DOWN_TIS);
+ expectEvent(TestProtocol.SEQUENCE_TIS, LauncherInstrumentation.EVENT_TOUCH_UP_TIS);
+ }
object.click();
}
@@ -1007,7 +1042,8 @@
// Inject a swipe gesture. Inject exactly 'steps' motion points, incrementing event time by a
// fixed interval each time.
- void linearGesture(int startX, int startY, int endX, int endY, int steps, boolean slowDown,
+ public void linearGesture(int startX, int startY, int endX, int endY, int steps,
+ boolean slowDown,
GestureScope gestureScope) {
log("linearGesture: " + startX + ", " + startY + " -> " + endX + ", " + endY);
final long downTime = SystemClock.uptimeMillis();
@@ -1059,22 +1095,28 @@
0, 0, 1.0f, 1.0f, 0, 0, InputDevice.SOURCE_TOUCHSCREEN, 0);
}
- void sendPointer(long downTime, long currentTime, int action, Point point,
+ public void sendPointer(long downTime, long currentTime, int action, Point point,
GestureScope gestureScope) {
switch (action) {
case MotionEvent.ACTION_DOWN:
if (gestureScope != GestureScope.OUTSIDE) {
- expectEvent(EVENT_TOUCH_DOWN);
+ expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_TOUCH_DOWN);
+ }
+ if (getNavigationModel() != NavigationModel.THREE_BUTTON) {
+ expectEvent(TestProtocol.SEQUENCE_TIS, EVENT_TOUCH_DOWN_TIS);
}
break;
case MotionEvent.ACTION_UP:
if (gestureScope != GestureScope.INSIDE) {
- expectEvent(EVENT_PILFER_POINTERS);
+ expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_PILFER_POINTERS);
}
if (gestureScope != GestureScope.OUTSIDE) {
- expectEvent(gestureScope == GestureScope.INSIDE
+ expectEvent(TestProtocol.SEQUENCE_MAIN, gestureScope == GestureScope.INSIDE
? EVENT_TOUCH_UP : EVENT_TOUCH_CANCEL);
}
+ if (getNavigationModel() != NavigationModel.THREE_BUTTON) {
+ expectEvent(TestProtocol.SEQUENCE_TIS, EVENT_TOUCH_UP_TIS);
+ }
break;
}
@@ -1083,7 +1125,7 @@
event.recycle();
}
- long movePointer(long downTime, long startTime, long duration, Point from, Point to,
+ public long movePointer(long downTime, long startTime, long duration, Point from, Point to,
GestureScope gestureScope) {
log("movePointer: " + from + " to " + to);
final Point point = new Point();
@@ -1203,13 +1245,20 @@
return tasks;
}
- private List<String> getEvents() {
- final ArrayList<String> events = new ArrayList<>();
+ // Returns actual events retrieved from logcat. The return value's key set is the set of all
+ // sequence names that actually had at least one event, and the values are lists of events in
+ // the given sequence, in the order they were recorded.
+ private Map<String, List<String>> getEvents() {
+ final Map<String, List<String>> events = new HashMap<>();
try {
- final String logcatEvents = mDevice.executeShellCommand(
- "logcat -d -v year --pid=" + getPid() + " -t "
- + DATE_TIME_FORMAT.format(mStartRecordingTime).replaceAll(" ", "")
- + " -s " + TestProtocol.TAPL_EVENTS_TAG);
+ // Logcat may skip events after the specified time. Querying for events starting 1 min
+ // earlier.
+ final Date startTime = new Date(mStartRecordingTime.getTime() - 60000);
+ final String logcatCommand = "logcat -d -v year --pid=" + getPid() + " -t "
+ + DATE_TIME_FORMAT.format(startTime).replaceAll(" ", "")
+ + " -s " + TestProtocol.TAPL_EVENTS_TAG;
+ log("Events query command: " + logcatCommand);
+ final String logcatEvents = mDevice.executeShellCommand(logcatCommand);
final Matcher matcher = EVENT_LOG_ENTRY.matcher(logcatEvents);
while (matcher.find()) {
// Skip events before recording start time.
@@ -1218,7 +1267,8 @@
continue;
}
- events.add(matcher.group("event"));
+ eventsListForSequence(matcher.group("sequence"), events).add(
+ matcher.group("event"));
}
return events;
} catch (IOException e) {
@@ -1228,9 +1278,20 @@
}
}
+ // Returns an event list for a given sequence, adding it to the map as needed.
+ private static <T> List<T> eventsListForSequence(
+ String sequenceName, Map<String, List<T>> events) {
+ List<T> eventSequence = events.get(sequenceName);
+ if (eventSequence == null) {
+ eventSequence = new ArrayList<>();
+ events.put(sequenceName, eventSequence);
+ }
+ return eventSequence;
+ }
+
private void startRecordingEvents() {
Assert.assertTrue("Already recording events", mExpectedEvents == null);
- mExpectedEvents = new ArrayList<>();
+ mExpectedEvents = new HashMap<>();
mStartRecordingTime = new Date();
log("startRecordingEvents: " + DATE_TIME_FORMAT.format(mStartRecordingTime));
}
@@ -1240,7 +1301,7 @@
mStartRecordingTime = null;
}
- Closable eventsCheck() {
+ public Closable eventsCheck() {
if ("com.android.launcher3".equals(getLauncherPackageName())) {
// Not checking specific Launcher3 event sequences.
return () -> {
@@ -1264,65 +1325,124 @@
final String message = getEventMismatchMessage(true);
if (message != null) {
Assert.fail(formatSystemHealthMessage(
- "http://go/tapl : unexpected event sequence: " + message));
+ "http://go/tapl : successful gesture produced " + message));
}
};
}
- void expectEvent(Pattern expected) {
- if (mExpectedEvents != null) mExpectedEvents.add(expected);
+ void expectEvent(String sequence, Pattern expected) {
+ if (mExpectedEvents != null) {
+ eventsListForSequence(sequence, mExpectedEvents).add(expected);
+ }
}
+ // Returns non-null error message if the actual events in logcat don't match expected events.
+ // If we are not checking events, returns null.
private String getEventMismatchMessage(boolean waitForExpectedCount) {
if (mExpectedEvents == null) return null;
try {
- List<String> actual = getEvents();
+ Map<String, List<String>> actual = getEvents();
if (waitForExpectedCount) {
// Wait until Launcher generates the expected number of events.
final long endTime = SystemClock.uptimeMillis() + WAIT_TIME_MS;
while (SystemClock.uptimeMillis() < endTime
- && actual.size() < mExpectedEvents.size()) {
+ && !receivedEnoughEvents(actual)) {
SystemClock.sleep(100);
actual = getEvents();
}
}
- for (int i = 0; i < mExpectedEvents.size(); ++i) {
- if (i >= actual.size()) {
- return formatEventMismatchMessage("too few actual events", actual, i);
- }
- if (!mExpectedEvents.get(i).matcher(actual.get(i)).find()) {
- return formatEventMismatchMessage("a mismatched event", actual, i);
- }
- }
-
- if (actual.size() > mExpectedEvents.size()) {
- return formatEventMismatchMessage(
- "too many actual events", actual, mExpectedEvents.size());
- }
+ return getEventMismatchErrorMessage(actual);
} finally {
stopRecordingEvents();
}
-
- return null;
}
- private String formatEventList(List events, int position) {
+ // Returns whether there is a sufficient number of events in the logcat to match the expected
+ // events.
+ private boolean receivedEnoughEvents(Map<String, List<String>> actual) {
+ for (Map.Entry<String, List<Pattern>> expectedNamedSequence : mExpectedEvents.entrySet()) {
+ final List<String> actualEventSequence = actual.get(expectedNamedSequence.getKey());
+ if (actualEventSequence == null
+ || actualEventSequence.size() < expectedNamedSequence.getValue().size()) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ // If the list of actual events matches the list of expected events, returns -1, otherwise
+ // the position of the mismatch.
+ private static int getMismatchPosition(List<Pattern> expected, List<String> actual) {
+ for (int i = 0; i < expected.size(); ++i) {
+ if (i >= actual.size()
+ || !expected.get(i).matcher(actual.get(i)).find()) {
+ return i;
+ }
+ }
+
+ if (actual.size() > expected.size()) return expected.size();
+
+ return -1;
+ }
+
+ // Returns non-null error message if the actual events passed as a param don't match expected
+ // events.
+ private String getEventMismatchErrorMessage(Map<String, List<String>> actualEvents) {
final StringBuilder sb = new StringBuilder();
+
+ // Check that all expected even sequences match the actual data.
+ for (Map.Entry<String, List<Pattern>> expectedNamedSequence : mExpectedEvents.entrySet()) {
+ List<String> actualEventSequence = actualEvents.get(expectedNamedSequence.getKey());
+ if (actualEventSequence == null) actualEventSequence = new ArrayList<>();
+ final int mismatchPosition = getMismatchPosition(
+ expectedNamedSequence.getValue(), actualEventSequence);
+ if (mismatchPosition != -1) {
+ formatSequenceWithMismatch(
+ sb,
+ expectedNamedSequence.getKey(),
+ expectedNamedSequence.getValue(),
+ actualEventSequence,
+ mismatchPosition);
+ }
+ }
+
+ // Check for unexpected event sequences in the actual data.
+ for (Map.Entry<String, List<String>> actualNamedSequence : actualEvents.entrySet()) {
+ if (!mExpectedEvents.containsKey(actualNamedSequence.getKey())) {
+ formatSequenceWithMismatch(
+ sb,
+ actualNamedSequence.getKey(),
+ new ArrayList<>(),
+ actualNamedSequence.getValue(),
+ 0);
+ }
+ }
+
+ return sb.length() != 0 ? "mismatching events: " + sb.toString() : null;
+ }
+
+ private static void formatSequenceWithMismatch(
+ StringBuilder sb,
+ String sequenceName,
+ List<Pattern> expected,
+ List<String> actualEvents,
+ int mismatchPosition) {
+ sb.append("\n>> Sequence " + sequenceName);
+ sb.append("\n Expected:");
+ formatEventListWithMismatch(sb, expected, mismatchPosition);
+ sb.append("\n Actual:");
+ formatEventListWithMismatch(sb, actualEvents, mismatchPosition);
+ }
+
+ private static void formatEventListWithMismatch(StringBuilder sb, List events, int position) {
for (int i = 0; i < events.size(); ++i) {
- sb.append("\n| ");
+ sb.append("\n | ");
sb.append(i == position ? "---> " : " ");
sb.append(events.get(i).toString());
}
- if (position == events.size()) sb.append("\n| ---> (end)");
- return sb.toString();
- }
-
- private String formatEventMismatchMessage(String message, List<String> actual, int position) {
- return message + ":"
- + "\nExpected:" + formatEventList(mExpectedEvents, position)
- + "\nActual:" + formatEventList(actual, position);
+ if (position == events.size()) sb.append("\n | ---> (end)");
}
}
diff --git a/tests/tapl/com/android/launcher3/tapl/OverviewTask.java b/tests/tapl/com/android/launcher3/tapl/OverviewTask.java
index 410e5a1..f955cf2 100644
--- a/tests/tapl/com/android/launcher3/tapl/OverviewTask.java
+++ b/tests/tapl/com/android/launcher3/tapl/OverviewTask.java
@@ -22,6 +22,8 @@
import androidx.test.uiautomator.UiObject2;
+import com.android.launcher3.testing.TestProtocol;
+
import java.util.regex.Pattern;
/**
@@ -76,7 +78,7 @@
event -> event.getEventType() == TYPE_WINDOW_STATE_CHANGED,
() -> "Launching task didn't open a new window: "
+ mTask.getParent().getContentDescription());
- mLauncher.expectEvent(TASK_START_EVENT);
+ mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, TASK_START_EVENT);
}
return new Background(mLauncher);
}
diff --git a/tests/tapl/com/android/launcher3/tapl/Workspace.java b/tests/tapl/com/android/launcher3/tapl/Workspace.java
index a0d5443..3f5dc8d 100644
--- a/tests/tapl/com/android/launcher3/tapl/Workspace.java
+++ b/tests/tapl/com/android/launcher3/tapl/Workspace.java
@@ -260,8 +260,8 @@
public Widgets openAllWidgets() {
try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) {
verifyActiveContainer();
- mLauncher.expectEvent(EVENT_CTRL_W_DOWN);
- mLauncher.expectEvent(EVENT_CTRL_W_UP);
+ mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_CTRL_W_DOWN);
+ mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_CTRL_W_UP);
mLauncher.getDevice().pressKeyCode(KeyEvent.KEYCODE_W, KeyEvent.META_CTRL_ON);
try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer("pressed Ctrl+W")) {
return new Widgets(mLauncher);