Merge "Prevent all apps prediction update while visible" into ub-launcher3-rvc-dev
diff --git a/protos/launcher_atom.proto b/protos/launcher_atom.proto
index cac2d8f..f1b71e8 100644
--- a/protos/launcher_atom.proto
+++ b/protos/launcher_atom.proto
@@ -95,7 +95,18 @@
// Represents folder in a closed state.
message FolderIcon {
+ // Number of items inside folder.
optional int32 cardinality = 1;
+
+ // State of the folder label before the event.
+ optional FromState from_label_state = 2;
+
+ // State of the folder label after the event.
+ optional ToState to_label_state = 3;
+
+ // Details about actual folder label.
+ // Populated when folder label is not a PII.
+ optional string label_info = 4;
}
//////////////////////////////////////////////
@@ -120,3 +131,78 @@
HotseatContainer hotseat = 5;
}
}
+
+// Represents state of EditText field before update.
+enum FromState {
+ // Default value.
+ // Used when a FromState is not applicable, for example, during folder creation.
+ FROM_STATE_UNSPECIFIED = 0;
+
+ // EditText was empty.
+ // Eg: When a folder label is updated from empty string.
+ FROM_EMPTY = 1;
+
+ // EditText was non-empty and manually entered by the user.
+ // Eg: When a folder label is updated from a user-entered value.
+ FROM_CUSTOM = 2;
+
+ // EditText was non-empty and one of the suggestions.
+ // Eg: When a folder label is updated from a suggested value.
+ FROM_SUGGESTED = 3;
+}
+
+// Represents state of EditText field after update.
+enum ToState {
+ // Default value.
+ // Used when ToState is not applicable, for example, when folder label is updated to a different
+ // value when folder label suggestion feature is disabled.
+ TO_STATE_UNSPECIFIED = 0;
+
+ // User attempted to change the EditText, but was not changed.
+ UNCHANGED = 1;
+
+ // New label matches with primary(aka top) suggestion.
+ TO_SUGGESTION0 = 2;
+
+ // New value matches with second top suggestion even though the top suggestion was non-empty.
+ TO_SUGGESTION1_WITH_VALID_PRIMARY = 3;
+
+ // New value matches with second top suggestion given that top suggestion was empty.
+ TO_SUGGESTION1_WITH_EMPTY_PRIMARY = 4;
+
+ // New value matches with third top suggestion even though the top suggestion was non-empty.
+ TO_SUGGESTION2_WITH_VALID_PRIMARY = 5;
+
+ // New value matches with third top suggestion given that top suggestion was empty.
+ TO_SUGGESTION2_WITH_EMPTY_PRIMARY = 6;
+
+ // New value matches with 4th top suggestion even though the top suggestion was non-empty.
+ TO_SUGGESTION3_WITH_VALID_PRIMARY = 7;
+
+ // New value matches with 4th top suggestion given that top suggestion was empty.
+ TO_SUGGESTION3_WITH_EMPTY_PRIMARY = 8;
+
+ // New value is empty even though the top suggestion was non-empty.
+ TO_EMPTY_WITH_VALID_PRIMARY = 9;
+
+ // New value is empty given that top suggestion was empty.
+ TO_EMPTY_WITH_VALID_SUGGESTIONS_AND_EMPTY_PRIMARY = 10;
+
+ // New value is empty given that no suggestions were provided.
+ TO_EMPTY_WITH_EMPTY_SUGGESTIONS = 11;
+
+ // New value is empty given that suggestions feature was disabled.
+ TO_EMPTY_WITH_SUGGESTIONS_DISABLED = 12;
+
+ // New value is non-empty and does not match with any of the suggestions even though the top suggestion was non-empty.
+ TO_CUSTOM_WITH_VALID_PRIMARY = 13;
+
+ // New value is non-empty and not match with any suggestions given that top suggestion was empty.
+ TO_CUSTOM_WITH_VALID_SUGGESTIONS_AND_EMPTY_PRIMARY = 14;
+
+ // New value is non-empty and also no suggestions were provided.
+ TO_CUSTOM_WITH_EMPTY_SUGGESTIONS = 15;
+
+ // New value is non-empty and also suggestions feature was disable.
+ TO_CUSTOM_WITH_SUGGESTIONS_DISABLED = 16;
+}
diff --git a/quickstep/AndroidManifest.xml b/quickstep/AndroidManifest.xml
index 04506b5..b2286f1 100644
--- a/quickstep/AndroidManifest.xml
+++ b/quickstep/AndroidManifest.xml
@@ -95,12 +95,6 @@
android:clearTaskOnLaunch="true"
android:exported="false" />
- <activity android:name="com.android.quickstep.LockScreenRecentsActivity"
- android:theme="@android:style/Theme.NoDisplay"
- android:showOnLockScreen="true"
- android:taskAffinity="${packageName}.locktask"
- android:directBootAware="true" />
-
<activity
android:name="com.android.quickstep.interaction.GestureSandboxActivity"
android:autoRemoveFromRecents="true"
diff --git a/quickstep/recents_ui_overrides/res/values/override.xml b/quickstep/recents_ui_overrides/res/values/override.xml
index ed3ba92..6aa9619 100644
--- a/quickstep/recents_ui_overrides/res/values/override.xml
+++ b/quickstep/recents_ui_overrides/res/values/override.xml
@@ -26,5 +26,7 @@
<string name="main_process_initializer_class" translatable="false">com.android.quickstep.QuickstepProcessInitializer</string>
<string name="user_event_dispatcher_class" translatable="false">com.android.quickstep.logging.UserEventDispatcherAppPredictionExtension</string>
+
+ <string name="prediction_model_class" translatable="false">com.android.launcher3.hybridhotseat.HotseatPredictionModel</string>
</resources>
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduController.java
index 7f8f0a0..e4d0adf 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduController.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduController.java
@@ -121,7 +121,7 @@
if (!putIntoFolder.isEmpty()) {
ItemInfo firstItem = putIntoFolder.get(0);
FolderInfo folderInfo = new FolderInfo();
- folderInfo.title = "";
+ folderInfo.setTitle("");
mLauncher.getModelWriter().addItemToDatabase(folderInfo, firstItem.container,
firstItem.screenId, firstItem.cellX, firstItem.cellY);
folderInfo.contents.addAll(putIntoFolder);
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 e9f3534..7a73e50 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
@@ -25,10 +25,7 @@
import android.app.prediction.AppPredictor;
import android.app.prediction.AppTarget;
import android.app.prediction.AppTargetEvent;
-import android.app.prediction.AppTargetId;
import android.content.ComponentName;
-import android.os.Bundle;
-import android.os.Process;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
@@ -36,8 +33,6 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import com.android.launcher3.BubbleTextView;
-import com.android.launcher3.CellLayout;
import com.android.launcher3.DragSource;
import com.android.launcher3.DropTarget;
import com.android.launcher3.Hotseat;
@@ -48,7 +43,6 @@
import com.android.launcher3.LauncherState;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
-import com.android.launcher3.Workspace;
import com.android.launcher3.allapps.AllAppsStore;
import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.appprediction.ComponentKeyMapper;
@@ -57,12 +51,10 @@
import com.android.launcher3.dragndrop.DragOptions;
import com.android.launcher3.icons.IconCache;
import com.android.launcher3.logging.FileLog;
-import com.android.launcher3.model.PredictionModel;
import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.model.data.FolderInfo;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.ItemInfoWithIcon;
-import com.android.launcher3.model.data.LauncherAppWidgetInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.popup.SystemShortcut;
import com.android.launcher3.shortcuts.ShortcutKey;
@@ -77,7 +69,6 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
-import java.util.Locale;
import java.util.OptionalInt;
import java.util.stream.IntStream;
@@ -93,17 +84,6 @@
private static final String TAG = "PredictiveHotseat";
private static final boolean DEBUG = false;
- //TODO: replace this with AppTargetEvent.ACTION_UNPIN (b/144119543)
- private static final int APPTARGET_ACTION_UNPIN = 4;
-
- private static final String APP_LOCATION_HOTSEAT = "hotseat";
- private static final String APP_LOCATION_WORKSPACE = "workspace";
-
- private static final String BUNDLE_KEY_HOTSEAT = "hotseat_apps";
- private static final String BUNDLE_KEY_WORKSPACE = "workspace_apps";
-
- private static final String BUNDLE_KEY_PIN_EVENTS = "pin_events";
-
private static final String PREDICTION_CLIENT = "hotseat";
private DropTarget.DragObject mDragObject;
private int mHotSeatItemsCount;
@@ -116,13 +96,14 @@
private DynamicItemCache mDynamicItemCache;
- private final PredictionModel mPredictionModel;
+ private final HotseatPredictionModel mPredictionModel;
private AppPredictor mAppPredictor;
private AllAppsStore mAllAppsStore;
private AnimatorSet mIconRemoveAnimators;
private boolean mUIUpdatePaused = false;
private boolean mRequiresCacheUpdate = true;
private boolean mIsCacheEmpty;
+ private boolean mIsDestroyed = false;
private HotseatEduController mHotseatEduController;
@@ -141,13 +122,13 @@
mLauncher = launcher;
mHotseat = launcher.getHotseat();
mAllAppsStore = mLauncher.getAppsView().getAppsStore();
- mPredictionModel = LauncherAppState.INSTANCE.get(launcher).getPredictionModel();
+ LauncherAppState appState = LauncherAppState.getInstance(launcher);
+ mPredictionModel = (HotseatPredictionModel) appState.getPredictionModel();
mAllAppsStore.addUpdateListener(this);
mDynamicItemCache = new DynamicItemCache(mLauncher, this::fillGapsWithPrediction);
mHotSeatItemsCount = mLauncher.getDeviceProfile().inv.numHotseatIcons;
launcher.getDeviceProfile().inv.addOnChangeListener(this);
mHotseat.addOnAttachStateChangeListener(this);
- mIsCacheEmpty = mPredictionModel.getPredictionComponentKeys().isEmpty();
if (mHotseat.isAttachedToWindow()) {
onViewAttachedToWindow(mHotseat);
}
@@ -260,6 +241,7 @@
* Unregisters callbacks and frees resources
*/
public void destroy() {
+ mIsDestroyed = true;
mAllAppsStore.removeUpdateListener(this);
mLauncher.getDeviceProfile().inv.removeOnChangeListener(this);
mHotseat.removeOnAttachStateChangeListener(this);
@@ -293,99 +275,52 @@
if (mAppPredictor != null) {
mAppPredictor.destroy();
}
- mAppPredictor = apm.createAppPredictionSession(
- new AppPredictionContext.Builder(mLauncher)
- .setUiSurface(PREDICTION_CLIENT)
- .setPredictedTargetCount(mHotSeatItemsCount)
- .setExtras(getAppPredictionContextExtra())
- .build());
WeakReference<HotseatPredictionController> controllerRef = new WeakReference<>(this);
- mAppPredictor.registerPredictionUpdates(mLauncher.getApplicationContext().getMainExecutor(),
- list -> {
- if (controllerRef.get() != null) {
- controllerRef.get().setPredictedApps(list);
- }
- });
+
+ mPredictionModel.createBundle(bundle -> {
+ if (mIsDestroyed) return;
+ mAppPredictor = apm.createAppPredictionSession(
+ new AppPredictionContext.Builder(mLauncher)
+ .setUiSurface(PREDICTION_CLIENT)
+ .setPredictedTargetCount(mHotSeatItemsCount)
+ .setExtras(bundle)
+ .build());
+ mAppPredictor.registerPredictionUpdates(
+ mLauncher.getApplicationContext().getMainExecutor(),
+ list -> {
+ if (controllerRef.get() != null) {
+ controllerRef.get().setPredictedApps(list);
+ }
+ });
+ mAppPredictor.requestPredictionUpdate();
+ });
setPauseUIUpdate(false);
if (!isEduSeen()) {
mHotseatEduController = new HotseatEduController(mLauncher, this::createPredictor);
}
- mAppPredictor.requestPredictionUpdate();
}
/**
* Create WorkspaceItemInfo objects and binds PredictedAppIcon views for cached predicted items.
*/
- public void showCachedItems(List<AppInfo> apps, IntArray ranks) {
+ public void showCachedItems(List<AppInfo> apps, IntArray ranks) {
+ mIsCacheEmpty = apps.isEmpty();
int count = Math.min(ranks.size(), apps.size());
List<WorkspaceItemInfo> items = new ArrayList<>(count);
+ mComponentKeyMappers.clear();
for (int i = 0; i < count; i++) {
WorkspaceItemInfo item = new WorkspaceItemInfo(apps.get(i));
+ ComponentKey componentKey = new ComponentKey(item.getTargetComponent(), item.user);
preparePredictionInfo(item, ranks.get(i));
items.add(item);
- }
- mComponentKeyMappers.clear();
- for (ComponentKey key : mPredictionModel.getPredictionComponentKeys()) {
- mComponentKeyMappers.add(new ComponentKeyMapper(key, mDynamicItemCache));
+
+ mComponentKeyMappers.add(new ComponentKeyMapper(componentKey, mDynamicItemCache));
}
updateDependencies();
bindItems(items, false, null);
}
- private Bundle getAppPredictionContextExtra() {
- Bundle bundle = new Bundle();
-
- //TODO: remove this way of reporting items
- bundle.putParcelableArrayList(BUNDLE_KEY_HOTSEAT,
- getPinnedAppTargetsInViewGroup((mHotseat.getShortcutsAndWidgets())));
- bundle.putParcelableArrayList(BUNDLE_KEY_WORKSPACE, getPinnedAppTargetsInViewGroup(
- mLauncher.getWorkspace().getScreenWithId(
- Workspace.FIRST_SCREEN_ID).getShortcutsAndWidgets()));
-
- ArrayList<AppTargetEvent> pinEvents = new ArrayList<>();
- getPinEventsForViewGroup(pinEvents, mHotseat.getShortcutsAndWidgets(),
- APP_LOCATION_HOTSEAT);
- getPinEventsForViewGroup(pinEvents, mLauncher.getWorkspace().getScreenWithId(
- Workspace.FIRST_SCREEN_ID).getShortcutsAndWidgets(), APP_LOCATION_WORKSPACE);
- bundle.putParcelableArrayList(BUNDLE_KEY_PIN_EVENTS, pinEvents);
-
- return bundle;
- }
-
- private ArrayList<AppTargetEvent> getPinEventsForViewGroup(ArrayList<AppTargetEvent> pinEvents,
- ViewGroup views, String root) {
- for (int i = 0; i < views.getChildCount(); i++) {
- View child = views.getChildAt(i);
- final AppTargetEvent event;
- if (child.getTag() instanceof ItemInfo && getAppTargetFromInfo(
- (ItemInfo) child.getTag()) != null) {
- ItemInfo info = (ItemInfo) child.getTag();
- event = wrapAppTargetWithLocation(getAppTargetFromInfo(info),
- AppTargetEvent.ACTION_PIN, info);
- } else {
- CellLayout.LayoutParams params = (CellLayout.LayoutParams) views.getLayoutParams();
- event = wrapAppTargetWithLocation(getBlockAppTarget(), AppTargetEvent.ACTION_PIN,
- root, 0, params.cellX, params.cellY, params.cellHSpan, params.cellVSpan);
- }
- pinEvents.add(event);
- }
- return pinEvents;
- }
-
-
- private ArrayList<AppTarget> getPinnedAppTargetsInViewGroup(ViewGroup viewGroup) {
- ArrayList<AppTarget> pinnedApps = new ArrayList<>();
- for (int i = 0; i < viewGroup.getChildCount(); i++) {
- View child = viewGroup.getChildAt(i);
- if (isPinnedIcon(child)) {
- WorkspaceItemInfo itemInfo = (WorkspaceItemInfo) child.getTag();
- pinnedApps.add(getAppTargetFromItemInfo(itemInfo));
- }
- }
- return pinnedApps;
- }
-
private void setPredictedApps(List<AppTarget> appTargets) {
mComponentKeyMappers.clear();
StringBuilder predictionLog = new StringBuilder("predictedApps: [\n");
@@ -443,8 +378,11 @@
workspaceItemInfo.cellX, workspaceItemInfo.cellY);
ObjectAnimator.ofFloat(icon, SCALE_PROPERTY, 1, 0.8f, 1).start();
icon.pin(workspaceItemInfo);
- AppTarget appTarget = getAppTargetFromItemInfo(workspaceItemInfo);
- notifyItemAction(appTarget, APP_LOCATION_HOTSEAT, AppTargetEvent.ACTION_PIN);
+ AppTarget appTarget = mPredictionModel.getAppTargetFromInfo(workspaceItemInfo);
+ if (appTarget != null) {
+ notifyItemAction(mPredictionModel.wrapAppTargetWithLocation(appTarget,
+ AppTargetEvent.ACTION_PIN, workspaceItemInfo));
+ }
mRequiresCacheUpdate = true;
}
@@ -524,10 +462,9 @@
mIconRemoveAnimators.start();
}
- private void notifyItemAction(AppTarget target, String location, int action) {
+ private void notifyItemAction(AppTargetEvent event) {
if (mAppPredictor != null) {
- mAppPredictor.notifyAppTargetEvent(new AppTargetEvent.Builder(target,
- action).setLaunchLocation(location).build());
+ mAppPredictor.notifyAppTargetEvent(event);
}
}
@@ -545,36 +482,35 @@
/**
* Unpins pinned app when it's converted into a folder
*/
- public void folderCreatedFromWorkspaceItem(ItemInfo info, FolderInfo folderInfo) {
- if (info.itemType != LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
- return;
+ public void folderCreatedFromWorkspaceItem(ItemInfo itemInfo, FolderInfo folderInfo) {
+ AppTarget folderTarget = mPredictionModel.getAppTargetFromInfo(folderInfo);
+ AppTarget itemTarget = mPredictionModel.getAppTargetFromInfo(itemInfo);
+ if (folderTarget != null && HotseatPredictionModel.isTrackedForPrediction(folderInfo)) {
+ notifyItemAction(mPredictionModel.wrapAppTargetWithLocation(folderTarget,
+ AppTargetEvent.ACTION_PIN, folderInfo));
}
- AppTarget target = getAppTargetFromItemInfo(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 (isInFirstPage(folderInfo) && !getPinnedAppTargetsInViewGroup(
- firstScreenVG).contains(target)) {
- notifyItemAction(target, APP_LOCATION_WORKSPACE, APPTARGET_ACTION_UNPIN);
+ // using folder info with isTrackedForPrediction as itemInfo.container is already changed
+ // to folder by this point
+ if (itemTarget != null && HotseatPredictionModel.isTrackedForPrediction(folderInfo)) {
+ notifyItemAction(mPredictionModel.wrapAppTargetWithLocation(itemTarget,
+ AppTargetEvent.ACTION_UNPIN, folderInfo
+ ));
}
}
/**
* 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;
+ public void folderConvertedToWorkspaceItem(ItemInfo itemInfo, FolderInfo folderInfo) {
+ AppTarget folderTarget = mPredictionModel.getAppTargetFromInfo(folderInfo);
+ AppTarget itemTarget = mPredictionModel.getAppTargetFromInfo(itemInfo);
+ if (folderTarget != null && HotseatPredictionModel.isTrackedForPrediction(folderInfo)) {
+ notifyItemAction(mPredictionModel.wrapAppTargetWithLocation(folderTarget,
+ AppTargetEvent.ACTION_UNPIN, folderInfo));
}
- 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);
+ if (itemTarget != null && HotseatPredictionModel.isTrackedForPrediction(itemInfo)) {
+ notifyItemAction(mPredictionModel.wrapAppTargetWithLocation(itemTarget,
+ AppTargetEvent.ACTION_PIN, itemInfo));
}
}
@@ -585,29 +521,18 @@
}
ItemInfo dragInfo = mDragObject.dragInfo;
- ViewGroup hotseatVG = mHotseat.getShortcutsAndWidgets();
- ViewGroup firstScreenVG = mLauncher.getWorkspace().getScreenWithId(
- Workspace.FIRST_SCREEN_ID).getShortcutsAndWidgets();
-
- if (dragInfo instanceof WorkspaceItemInfo
- && dragInfo.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION
- && dragInfo.getTargetComponent() != null) {
- AppTarget appTarget = getAppTargetFromItemInfo(dragInfo);
- if (!isInHotseat(dragInfo) && isInHotseat(mDragObject.originalDragInfo)) {
- if (!getPinnedAppTargetsInViewGroup(hotseatVG).contains(appTarget)) {
- notifyItemAction(appTarget, APP_LOCATION_HOTSEAT, APPTARGET_ACTION_UNPIN);
- }
+ if (mDragObject.isMoved()) {
+ AppTarget appTarget = mPredictionModel.getAppTargetFromInfo(dragInfo);
+ //always send pin event first to prevent AiAi from predicting an item moved within
+ // the same page
+ if (appTarget != null && HotseatPredictionModel.isTrackedForPrediction(dragInfo)) {
+ notifyItemAction(mPredictionModel.wrapAppTargetWithLocation(appTarget,
+ AppTargetEvent.ACTION_PIN, dragInfo));
}
- if (!isInFirstPage(dragInfo) && isInFirstPage(mDragObject.originalDragInfo)) {
- if (!getPinnedAppTargetsInViewGroup(firstScreenVG).contains(appTarget)) {
- notifyItemAction(appTarget, APP_LOCATION_WORKSPACE, APPTARGET_ACTION_UNPIN);
- }
- }
- if (isInHotseat(dragInfo) && !isInHotseat(mDragObject.originalDragInfo)) {
- notifyItemAction(appTarget, APP_LOCATION_HOTSEAT, AppTargetEvent.ACTION_PIN);
- }
- if (isInFirstPage(dragInfo) && !isInFirstPage(mDragObject.originalDragInfo)) {
- notifyItemAction(appTarget, APP_LOCATION_WORKSPACE, AppTargetEvent.ACTION_PIN);
+ if (appTarget != null && HotseatPredictionModel.isTrackedForPrediction(
+ mDragObject.originalDragInfo)) {
+ notifyItemAction(mPredictionModel.wrapAppTargetWithLocation(appTarget,
+ AppTargetEvent.ACTION_UNPIN, mDragObject.originalDragInfo));
}
}
mDragObject = null;
@@ -615,6 +540,7 @@
mRequiresCacheUpdate = true;
}
+
@Nullable
@Override
public SystemShortcut<QuickstepLauncher> getShortcut(QuickstepLauncher activity,
@@ -711,77 +637,4 @@
&& ((WorkspaceItemInfo) view.getTag()).container
== LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION;
}
-
- private static boolean isPinnedIcon(View view) {
- if (!(view instanceof BubbleTextView && view.getTag() instanceof WorkspaceItemInfo)) {
- return false;
- }
- ItemInfo info = (ItemInfo) view.getTag();
- return info.container != LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION && (
- info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION);
- }
-
- private static boolean isInHotseat(ItemInfo itemInfo) {
- return itemInfo.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT;
- }
-
- private static boolean isInFirstPage(ItemInfo itemInfo) {
- return itemInfo.container == LauncherSettings.Favorites.CONTAINER_DESKTOP
- && itemInfo.screenId == Workspace.FIRST_SCREEN_ID;
- }
-
- private static AppTarget getAppTargetFromItemInfo(ItemInfo info) {
- if (info.getTargetComponent() == null) return null;
- ComponentName cn = info.getTargetComponent();
- return new AppTarget.Builder(new AppTargetId("app:" + cn.getPackageName()),
- cn.getPackageName(), info.user).setClassName(cn.getClassName()).build();
- }
-
- private AppTarget getAppTargetFromInfo(ItemInfo info) {
- if (info == null) return null;
- if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET
- && info instanceof LauncherAppWidgetInfo
- && ((LauncherAppWidgetInfo) info).providerName != null) {
- ComponentName cn = ((LauncherAppWidgetInfo) info).providerName;
- return new AppTarget.Builder(new AppTargetId("widget:" + cn.getPackageName()),
- cn.getPackageName(), info.user).setClassName(cn.getClassName()).build();
- } else if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION
- && info.getTargetComponent() != null) {
- ComponentName cn = info.getTargetComponent();
- return new AppTarget.Builder(new AppTargetId("app:" + cn.getPackageName()),
- cn.getPackageName(), info.user).setClassName(cn.getClassName()).build();
- } else if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT
- && info instanceof WorkspaceItemInfo) {
- ShortcutKey shortcutKey = ShortcutKey.fromItemInfo(info);
- //TODO: switch to using full shortcut info
- return new AppTarget.Builder(new AppTargetId("shortcut:" + shortcutKey.getId()),
- shortcutKey.componentName.getPackageName(), shortcutKey.user).build();
- } else if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_FOLDER) {
- return new AppTarget.Builder(new AppTargetId("folder:" + info.id),
- mLauncher.getPackageName(), info.user).build();
- }
- return null;
- }
-
- private AppTargetEvent wrapAppTargetWithLocation(AppTarget target, int action, ItemInfo info) {
- return wrapAppTargetWithLocation(target, action,
- info.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT
- ? APP_LOCATION_HOTSEAT : APP_LOCATION_WORKSPACE, info.screenId, info.cellX,
- info.cellY, info.spanX, info.spanY);
- }
-
- private AppTargetEvent wrapAppTargetWithLocation(AppTarget target, int action, String root,
- int screenId, int x, int y, int spanX, int spanY) {
- return new AppTargetEvent.Builder(target, action).setLaunchLocation(
- String.format(Locale.ENGLISH, "%s/%d/[%d,%d]/[%d,%d]", root, screenId, x, y, spanX,
- spanY)).build();
- }
-
- /**
- * A helper method to generate an AppTarget that's used to communicate workspace layout
- */
- private AppTarget getBlockAppTarget() {
- return new AppTarget.Builder(new AppTargetId("block"),
- mLauncher.getPackageName(), Process.myUserHandle()).build();
- }
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatPredictionModel.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatPredictionModel.java
new file mode 100644
index 0000000..5a038d2
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatPredictionModel.java
@@ -0,0 +1,136 @@
+/*
+ * 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.hybridhotseat;
+
+import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+
+import android.app.prediction.AppTarget;
+import android.app.prediction.AppTargetEvent;
+import android.app.prediction.AppTargetId;
+import android.content.ComponentName;
+import android.content.Context;
+import android.os.Bundle;
+
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.LauncherSettings;
+import com.android.launcher3.Workspace;
+import com.android.launcher3.model.AllAppsList;
+import com.android.launcher3.model.BaseModelUpdateTask;
+import com.android.launcher3.model.BgDataModel;
+import com.android.launcher3.model.PredictionModel;
+import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.model.data.LauncherAppWidgetInfo;
+import com.android.launcher3.model.data.WorkspaceItemInfo;
+import com.android.launcher3.shortcuts.ShortcutKey;
+
+import java.util.ArrayList;
+import java.util.Locale;
+import java.util.function.Consumer;
+
+/**
+ * Model helper for app predictions in workspace
+ */
+public class HotseatPredictionModel extends PredictionModel {
+ private static final String APP_LOCATION_HOTSEAT = "hotseat";
+ private static final String APP_LOCATION_WORKSPACE = "workspace";
+
+ private static final String BUNDLE_KEY_PIN_EVENTS = "pin_events";
+ private static final String BUNDLE_KEY_CURRENT_ITEMS = "current_items";
+
+
+ public HotseatPredictionModel(Context context) { }
+
+ /**
+ * Creates and returns bundle using workspace items and cached items
+ */
+ public void createBundle(Consumer<Bundle> cb) {
+ LauncherAppState appState = LauncherAppState.getInstance(mContext);
+ appState.getModel().enqueueModelUpdateTask(new BaseModelUpdateTask() {
+ @Override
+ public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
+ Bundle bundle = new Bundle();
+ ArrayList<AppTargetEvent> events = new ArrayList<>();
+ ArrayList<ItemInfo> workspaceItems = new ArrayList<>(dataModel.workspaceItems);
+ workspaceItems.addAll(dataModel.appWidgets);
+ for (ItemInfo item : workspaceItems) {
+ AppTarget target = getAppTargetFromInfo(item);
+ if (target != null && !isTrackedForPrediction(item)) continue;
+ events.add(wrapAppTargetWithLocation(target, AppTargetEvent.ACTION_PIN, item));
+ }
+ ArrayList<AppTarget> currentTargets = new ArrayList<>();
+ for (ItemInfo itemInfo : dataModel.cachedPredictedItems) {
+ AppTarget target = getAppTargetFromInfo(itemInfo);
+ if (target != null) currentTargets.add(target);
+ }
+ bundle.putParcelableArrayList(BUNDLE_KEY_PIN_EVENTS, events);
+ bundle.putParcelableArrayList(BUNDLE_KEY_CURRENT_ITEMS, currentTargets);
+ MAIN_EXECUTOR.execute(() -> cb.accept(bundle));
+ }
+ });
+ }
+
+ /**
+ * Creates and returns for {@link AppTarget} object given an {@link ItemInfo}. Returns null
+ * if item is not supported prediction
+ */
+ public AppTarget getAppTargetFromInfo(ItemInfo info) {
+ if (info == null) return null;
+ if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET
+ && info instanceof LauncherAppWidgetInfo
+ && ((LauncherAppWidgetInfo) info).providerName != null) {
+ ComponentName cn = ((LauncherAppWidgetInfo) info).providerName;
+ return new AppTarget.Builder(new AppTargetId("widget:" + cn.getPackageName()),
+ cn.getPackageName(), info.user).setClassName(cn.getClassName()).build();
+ } else if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION
+ && info.getTargetComponent() != null) {
+ ComponentName cn = info.getTargetComponent();
+ return new AppTarget.Builder(new AppTargetId("app:" + cn.getPackageName()),
+ cn.getPackageName(), info.user).setClassName(cn.getClassName()).build();
+ } else if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT
+ && info instanceof WorkspaceItemInfo) {
+ ShortcutKey shortcutKey = ShortcutKey.fromItemInfo(info);
+ //TODO: switch to using full shortcut info
+ return new AppTarget.Builder(new AppTargetId("shortcut:" + shortcutKey.getId()),
+ shortcutKey.componentName.getPackageName(), shortcutKey.user).build();
+ } else if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_FOLDER) {
+ return new AppTarget.Builder(new AppTargetId("folder:" + info.id),
+ mContext.getPackageName(), info.user).build();
+ }
+ return null;
+ }
+
+
+ /**
+ * Creates and returns {@link AppTargetEvent} from an {@link AppTarget}, action, and item
+ * location using {@link ItemInfo}
+ */
+ public AppTargetEvent wrapAppTargetWithLocation(AppTarget target, int action, ItemInfo info) {
+ String location = String.format(Locale.ENGLISH, "%s/%d/[%d,%d]/[%d,%d]",
+ info.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT
+ ? APP_LOCATION_HOTSEAT : APP_LOCATION_WORKSPACE,
+ info.screenId, info.cellX, info.cellY, info.spanX, info.spanY);
+ return new AppTargetEvent.Builder(target, action).setLaunchLocation(location).build();
+ }
+
+ /**
+ * Helper method to determine if {@link ItemInfo} should be tracked and reported to predictors
+ */
+ public static boolean isTrackedForPrediction(ItemInfo info) {
+ return info.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT || (
+ info.container == LauncherSettings.Favorites.CONTAINER_DESKTOP
+ && info.screenId == Workspace.FIRST_SCREEN_ID);
+ }
+}
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 6cfc846..ad6a10b 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
@@ -100,7 +100,7 @@
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
- onStateOrResumeChanged();
+ onStateOrResumeChanging(false /* inTransition */);
}
@Override
@@ -115,11 +115,9 @@
@Override
protected void onActivityFlagsChanged(int changeBits) {
super.onActivityFlagsChanged(changeBits);
-
if ((changeBits & (ACTIVITY_STATE_DEFERRED_RESUMED | ACTIVITY_STATE_STARTED
- | ACTIVITY_STATE_USER_ACTIVE | ACTIVITY_STATE_TRANSITION_ACTIVE)) != 0
- && (getActivityFlags() & ACTIVITY_STATE_TRANSITION_ACTIVE) == 0) {
- onStateOrResumeChanged();
+ | ACTIVITY_STATE_USER_ACTIVE | ACTIVITY_STATE_TRANSITION_ACTIVE)) != 0) {
+ onStateOrResumeChanging((getActivityFlags() & ACTIVITY_STATE_TRANSITION_ACTIVE) == 0);
}
if (mHotseatPredictionController != null && ((changeBits & ACTIVITY_STATE_STARTED) != 0
@@ -164,14 +162,16 @@
/**
* Recents logic that triggers when launcher state changes or launcher activity stops/resumes.
*/
- private void onStateOrResumeChanged() {
+ private void onStateOrResumeChanging(boolean inTransition) {
LauncherState state = getStateManager().getState();
DeviceProfile profile = getDeviceProfile();
- boolean visible = (state == NORMAL || state == OVERVIEW) && isUserActive()
+ boolean willUserBeActive = (getActivityFlags() & ACTIVITY_STATE_USER_WILL_BE_ACTIVE) != 0;
+ boolean visible = (state == NORMAL || state == OVERVIEW)
+ && (willUserBeActive || isUserActive())
&& !profile.isVerticalBarLayout();
UiThreadHelper.runAsyncCommand(this, SET_SHELF_HEIGHT, visible ? 1 : 0,
profile.hotseatBarSizePx);
- if (state == NORMAL) {
+ if (state == NORMAL && !inTransition) {
((RecentsView) getOverviewPanel()).setSwipeDownShouldLaunchApp(false);
}
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewModalTaskState.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewModalTaskState.java
index 0be2486..d5b0687 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewModalTaskState.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewModalTaskState.java
@@ -16,7 +16,6 @@
package com.android.launcher3.uioverrides.states;
import android.content.Context;
-import android.content.res.Resources;
import android.graphics.Rect;
import com.android.launcher3.BaseDraggingActivity;
@@ -58,8 +57,6 @@
}
public static float[] getOverviewScaleAndOffsetForModalState(BaseDraggingActivity activity) {
- Resources res = activity.getResources();
-
Rect out = new Rect();
activity.<RecentsView>getOverviewPanel().getTaskSize(out);
int taskHeight = out.height();
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/AppToOverviewAnimationProvider.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/AppToOverviewAnimationProvider.java
index 1dd5fb7..f38ff10 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/AppToOverviewAnimationProvider.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/AppToOverviewAnimationProvider.java
@@ -31,9 +31,9 @@
import android.view.View;
import com.android.launcher3.AbstractFloatingView;
-import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.statehandlers.DepthController;
+import com.android.launcher3.statemanager.StatefulActivity;
import com.android.quickstep.util.AppWindowAnimationHelper;
import com.android.quickstep.util.RemoteAnimationProvider;
import com.android.quickstep.util.TransformParams;
@@ -47,20 +47,21 @@
*
* @param <T> activity that contains the overview
*/
-final class AppToOverviewAnimationProvider<T extends BaseDraggingActivity> extends
+final class AppToOverviewAnimationProvider<T extends StatefulActivity<?>> extends
RemoteAnimationProvider {
private static final long RECENTS_LAUNCH_DURATION = 250;
private static final String TAG = "AppToOverviewAnimationProvider";
- private final BaseActivityInterface<T> mActivityInterface;
+ private final BaseActivityInterface<?, T> mActivityInterface;
// The id of the currently running task that is transitioning to overview.
private final int mTargetTaskId;
private T mActivity;
private RecentsView mRecentsView;
- AppToOverviewAnimationProvider(BaseActivityInterface<T> activityInterface, int targetTaskId) {
+ AppToOverviewAnimationProvider(
+ BaseActivityInterface<?, T> activityInterface, int targetTaskId) {
mActivityInterface = activityInterface;
mTargetTaskId = targetTaskId;
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java
index 76c6060..bbee67c 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java
@@ -44,12 +44,12 @@
import androidx.annotation.Nullable;
import androidx.annotation.UiThread;
-import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.anim.AnimatorPlaybackController;
+import com.android.launcher3.statemanager.StatefulActivity;
import com.android.launcher3.touch.PagedOrientationHandler;
import com.android.launcher3.util.VibratorWrapper;
import com.android.launcher3.views.FloatingIconView;
@@ -60,7 +60,6 @@
import com.android.quickstep.util.TaskViewSimulator;
import com.android.quickstep.util.TransformParams;
import com.android.quickstep.util.TransformParams.BuilderProxy;
-import com.android.quickstep.util.WindowSizeStrategy;
import com.android.quickstep.views.RecentsView;
import com.android.quickstep.views.TaskView;
import com.android.systemui.shared.recents.model.ThumbnailData;
@@ -76,7 +75,7 @@
* Base class for swipe up handler with some utility methods
*/
@TargetApi(Build.VERSION_CODES.Q)
-public abstract class BaseSwipeUpHandler<T extends BaseDraggingActivity, Q extends RecentsView>
+public abstract class BaseSwipeUpHandler<T extends StatefulActivity<?>, Q extends RecentsView>
implements RecentsAnimationListener {
private static final String TAG = "BaseSwipeUpHandler";
@@ -97,7 +96,7 @@
protected final Context mContext;
protected final RecentsAnimationDeviceState mDeviceState;
protected final GestureState mGestureState;
- protected final BaseActivityInterface<T> mActivityInterface;
+ protected final BaseActivityInterface<?, T> mActivityInterface;
protected final InputConsumerController mInputConsumer;
protected final TaskViewSimulator mTaskViewSimulator;
@@ -132,15 +131,14 @@
private boolean mRecentsViewScrollLinked = false;
protected BaseSwipeUpHandler(Context context, RecentsAnimationDeviceState deviceState,
- GestureState gestureState, InputConsumerController inputConsumer,
- WindowSizeStrategy windowSizeStrategy) {
+ GestureState gestureState, InputConsumerController inputConsumer) {
mContext = context;
mDeviceState = deviceState;
mGestureState = gestureState;
mActivityInterface = gestureState.getActivityInterface();
mActivityInitListener = mActivityInterface.createActivityInitListener(this::onActivityInit);
mInputConsumer = inputConsumer;
- mTaskViewSimulator = new TaskViewSimulator(context, windowSizeStrategy);
+ mTaskViewSimulator = new TaskViewSimulator(context, gestureState.getActivityInterface());
}
/**
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackActivityInterface.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackActivityInterface.java
index cd546e2..4b3af31 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackActivityInterface.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackActivityInterface.java
@@ -20,20 +20,22 @@
import static com.android.quickstep.SysUINavigationMode.Mode.NO_BUTTON;
import static com.android.quickstep.fallback.RecentsState.BACKGROUND_APP;
import static com.android.quickstep.fallback.RecentsState.DEFAULT;
-import static com.android.quickstep.util.WindowSizeStrategy.FALLBACK_RECENTS_SIZE_STRATEGY;
import static com.android.quickstep.views.RecentsView.CONTENT_ALPHA;
import static com.android.quickstep.views.RecentsView.FULLSCREEN_PROGRESS;
import android.content.Context;
+import android.graphics.PointF;
import android.graphics.Rect;
import androidx.annotation.Nullable;
import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.R;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.quickstep.fallback.FallbackRecentsView;
+import com.android.quickstep.fallback.RecentsState;
import com.android.quickstep.util.ActivityInitListener;
import com.android.quickstep.views.RecentsView;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
@@ -46,19 +48,18 @@
* currently running one and apps should interact with the {@link RecentsActivity} as opposed
* to the in-launcher one.
*/
-public final class FallbackActivityInterface implements
- BaseActivityInterface<RecentsActivity> {
+public final class FallbackActivityInterface extends
+ BaseActivityInterface<RecentsState, RecentsActivity> {
- public FallbackActivityInterface() { }
+ public static final FallbackActivityInterface INSTANCE = new FallbackActivityInterface();
- @Override
- public void onTransitionCancelled(boolean activityVisible) {
- // TODO:
+ private FallbackActivityInterface() {
+ super(false);
}
@Override
public int getSwipeUpDestinationAndLength(DeviceProfile dp, Context context, Rect outRect) {
- FALLBACK_RECENTS_SIZE_STRATEGY.calculateTaskSize(context, dp, outRect);
+ calculateTaskSize(context, dp, outRect);
if (dp.isVerticalBarLayout()
&& SysUINavigationMode.INSTANCE.get(context).getMode() != NO_BUTTON) {
Rect targetInsets = dp.getInsets();
@@ -70,17 +71,6 @@
}
@Override
- public void onSwipeUpToRecentsComplete() {
- RecentsActivity activity = getCreatedActivity();
- if (activity == null) {
- return;
- }
- RecentsView recentsView = activity.getOverviewPanel();
- recentsView.getClearAllButton().setVisibilityAlpha(1);
- recentsView.setDisallowScrollToClearAll(false);
- }
-
- @Override
public void onAssistantVisibilityChanged(float visibility) {
// This class becomes active when the screen is locked.
// Rather than having it handle assistant visibility changes, the assistant visibility is
@@ -198,11 +188,14 @@
}
@Override
- public void onLaunchTaskSuccess() {
- RecentsActivity activity = getCreatedActivity();
- if (activity == null) {
- return;
- }
- activity.onTaskLaunched();
+ public void getMultiWindowSize(Context context, DeviceProfile dp, PointF out) {
+ out.set(dp.widthPx, dp.heightPx);
+ }
+
+ @Override
+ protected float getExtraSpace(Context context, DeviceProfile dp) {
+ return showOverviewActions(context)
+ ? context.getResources().getDimensionPixelSize(R.dimen.overview_actions_height)
+ : 0;
}
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackSwipeHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackSwipeHandler.java
index 77e50ca..db41bd6 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackSwipeHandler.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackSwipeHandler.java
@@ -24,7 +24,6 @@
import static com.android.quickstep.MultiStateCallback.DEBUG_STATES;
import static com.android.quickstep.RecentsActivity.EXTRA_TASK_ID;
import static com.android.quickstep.RecentsActivity.EXTRA_THUMBNAIL;
-import static com.android.quickstep.util.WindowSizeStrategy.FALLBACK_RECENTS_SIZE_STRATEGY;
import static com.android.quickstep.views.RecentsView.UPDATE_SYSUI_FLAGS_THRESHOLD;
import android.animation.Animator;
@@ -114,7 +113,7 @@
public FallbackSwipeHandler(Context context, RecentsAnimationDeviceState deviceState,
GestureState gestureState, InputConsumerController inputConsumer,
boolean isLikelyToStartNewTask, boolean continuingLastGesture) {
- super(context, deviceState, gestureState, inputConsumer, FALLBACK_RECENTS_SIZE_STRATEGY);
+ super(context, deviceState, gestureState, inputConsumer);
mInQuickSwitchMode = isLikelyToStartNewTask || continuingLastGesture;
mContinuingLastGesture = continuingLastGesture;
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 9ff5d942..4ebfbd6 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityInterface.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityInterface.java
@@ -21,16 +21,21 @@
import static com.android.launcher3.anim.Interpolators.ACCEL_2;
import static com.android.launcher3.anim.Interpolators.INSTANT;
import static com.android.launcher3.anim.Interpolators.LINEAR;
+import static com.android.launcher3.config.FeatureFlags.ENABLE_OVERVIEW_ACTIONS;
import static com.android.launcher3.uioverrides.states.QuickstepAtomicAnimationFactory.INDEX_RECENTS_FADE_ANIM;
import static com.android.launcher3.uioverrides.states.QuickstepAtomicAnimationFactory.INDEX_RECENTS_TRANSLATE_X_ANIM;
import static com.android.launcher3.uioverrides.states.QuickstepAtomicAnimationFactory.INDEX_SHELF_ANIM;
import static com.android.quickstep.LauncherSwipeHandler.RECENTS_ATTACH_DURATION;
-import static com.android.quickstep.util.WindowSizeStrategy.LAUNCHER_ACTIVITY_SIZE_STRATEGY;
+import static com.android.quickstep.SysUINavigationMode.getMode;
+import static com.android.quickstep.SysUINavigationMode.removeShelfFromOverview;
+import static com.android.quickstep.util.LayoutUtils.getDefaultSwipeHeight;
import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_OFFSET;
import static com.android.quickstep.views.RecentsView.FULLSCREEN_PROGRESS;
import android.animation.Animator;
import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.PointF;
import android.graphics.Rect;
import android.util.Log;
import android.view.MotionEvent;
@@ -44,6 +49,7 @@
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherInitListener;
import com.android.launcher3.LauncherState;
+import com.android.launcher3.R;
import com.android.launcher3.allapps.DiscoveryBounce;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.PendingAnimation;
@@ -60,7 +66,6 @@
import com.android.quickstep.views.LauncherRecentsView;
import com.android.quickstep.views.RecentsView;
import com.android.systemui.plugins.shared.LauncherOverlayManager;
-import com.android.systemui.shared.recents.model.ThumbnailData;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
import java.util.function.Consumer;
@@ -69,11 +74,18 @@
/**
* {@link BaseActivityInterface} for the in-launcher recents.
*/
-public final class LauncherActivityInterface implements BaseActivityInterface<Launcher> {
+public final class LauncherActivityInterface extends
+ BaseActivityInterface<LauncherState, Launcher> {
+
+ public static final LauncherActivityInterface INSTANCE = new LauncherActivityInterface();
+
+ private LauncherActivityInterface() {
+ super(true);
+ }
@Override
public int getSwipeUpDestinationAndLength(DeviceProfile dp, Context context, Rect outRect) {
- LAUNCHER_ACTIVITY_SIZE_STRATEGY.calculateTaskSize(context, dp, outRect);
+ calculateTaskSize(context, dp, outRect);
if (dp.isVerticalBarLayout() && SysUINavigationMode.getMode(context) != Mode.NO_BUTTON) {
Rect targetInsets = dp.getInsets();
int hotseatInset = dp.isSeascape() ? targetInsets.left : targetInsets.right;
@@ -84,24 +96,12 @@
}
@Override
- public void onTransitionCancelled(boolean activityVisible) {
- Launcher launcher = getCreatedActivity();
- if (launcher == null) {
- return;
- }
- LauncherState startState = launcher.getStateManager().getRestState();
- launcher.getStateManager().goToState(startState, activityVisible);
- }
-
- @Override
public void onSwipeUpToRecentsComplete() {
- // Re apply state in case we did something funky during the transition.
+ super.onSwipeUpToRecentsComplete();
Launcher launcher = getCreatedActivity();
- if (launcher == null) {
- return;
+ if (launcher != null) {
+ DiscoveryBounce.showForOverviewIfNeeded(launcher);
}
- launcher.getStateManager().reapplyState();
- DiscoveryBounce.showForOverviewIfNeeded(launcher);
}
@Override
@@ -113,15 +113,7 @@
// Ensure recents is at the correct position for NORMAL state. For example, when we detach
// recents, we assume the first task is invisible, making translation off by one task.
launcher.getStateManager().reapplyState();
- setLauncherHideBackArrow(false);
- }
-
- private void setLauncherHideBackArrow(boolean hideBackArrow) {
- Launcher launcher = getCreatedActivity();
- if (launcher == null) {
- return;
- }
- launcher.getRootView().setForceHideBackArrow(hideBackArrow);
+ launcher.getRootView().setForceHideBackArrow(false);
}
@Override
@@ -299,6 +291,11 @@
}
@Override
+ public void setHintUserWillBeActive() {
+ getCreatedActivity().setHintUserWillBeActive();
+ }
+
+ @Override
public boolean deferStartingActivity(RecentsAnimationDeviceState deviceState, MotionEvent ev) {
return deviceState.isInDeferredGestureRegion(ev);
}
@@ -337,15 +334,6 @@
}
@Override
- public void onLaunchTaskSuccess() {
- Launcher launcher = getCreatedActivity();
- if (launcher == null) {
- return;
- }
- launcher.getStateManager().moveToRestState();
- }
-
- @Override
public void closeOverlay() {
Launcher launcher = getCreatedActivity();
if (launcher == null) {
@@ -360,23 +348,6 @@
}
@Override
- public void switchRunningTaskViewToScreenshot(ThumbnailData thumbnailData,
- Runnable onFinishRunnable) {
- Launcher launcher = getCreatedActivity();
- if (launcher == null) {
- return;
- }
- RecentsView recentsView = launcher.getOverviewPanel();
- if (recentsView == null) {
- if (onFinishRunnable != null) {
- onFinishRunnable.run();
- }
- return;
- }
- recentsView.switchToScreenshot(thumbnailData, onFinishRunnable);
- }
-
- @Override
public void setOnDeferredActivityLaunchCallback(Runnable r) {
Launcher launcher = getCreatedActivity();
if (launcher == null) {
@@ -404,4 +375,50 @@
}
return launcher.getDepthController();
}
+
+ @Override
+ public void getMultiWindowSize(Context context, DeviceProfile dp, PointF out) {
+ DeviceProfile fullDp = dp.getFullScreenProfile();
+ // Use availableWidthPx and availableHeightPx instead of widthPx and heightPx to
+ // account for system insets
+ out.set(fullDp.availableWidthPx, fullDp.availableHeightPx);
+ float halfDividerSize = context.getResources()
+ .getDimension(R.dimen.multi_window_task_divider_size) / 2;
+
+ if (fullDp.isLandscape) {
+ out.x = out.x / 2 - halfDividerSize;
+ } else {
+ out.y = out.y / 2 - halfDividerSize;
+ }
+ }
+
+ @Override
+ protected float getExtraSpace(Context context, DeviceProfile dp) {
+ if (dp.isVerticalBarLayout()) {
+ return 0;
+ } else {
+ Resources res = context.getResources();
+ if (showOverviewActions(context)) {
+ //TODO: this needs to account for the swipe gesture height and accessibility
+ // UI when shown.
+ float actionsBottomMargin = 0;
+ if (getMode(context) == Mode.THREE_BUTTONS) {
+ actionsBottomMargin = res.getDimensionPixelSize(
+ R.dimen.overview_actions_bottom_margin_three_button);
+ } else {
+ actionsBottomMargin = res.getDimensionPixelSize(
+ R.dimen.overview_actions_bottom_margin_gesture);
+ }
+ float actionsHeight = actionsBottomMargin
+ + res.getDimensionPixelSize(R.dimen.overview_actions_height);
+ return actionsHeight;
+ } else {
+ return getDefaultSwipeHeight(context, dp) + dp.workspacePageIndicatorHeight
+ + res.getDimensionPixelSize(
+ R.dimen.dynamic_grid_hotseat_extra_vertical_size)
+ + res.getDimensionPixelSize(
+ R.dimen.dynamic_grid_hotseat_bottom_padding);
+ }
+ }
+ }
}
\ No newline at end of file
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 0bf81ef..82a3e79 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandler.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandler.java
@@ -35,7 +35,6 @@
import static com.android.quickstep.SysUINavigationMode.Mode.TWO_BUTTONS;
import static com.android.quickstep.util.ShelfPeekAnim.ShelfAnimState.HIDE;
import static com.android.quickstep.util.ShelfPeekAnim.ShelfAnimState.PEEK;
-import static com.android.quickstep.util.WindowSizeStrategy.LAUNCHER_ACTIVITY_SIZE_STRATEGY;
import static com.android.quickstep.views.RecentsView.UPDATE_SYSUI_FLAGS_THRESHOLD;
import android.animation.Animator;
@@ -60,7 +59,6 @@
import androidx.annotation.UiThread;
import com.android.launcher3.AbstractFloatingView;
-import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Launcher;
import com.android.launcher3.R;
@@ -69,6 +67,7 @@
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.logging.UserEventDispatcher;
+import com.android.launcher3.statemanager.StatefulActivity;
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;
@@ -198,7 +197,7 @@
TaskAnimationManager taskAnimationManager, GestureState gestureState,
long touchTimeMs, boolean continuingLastGesture,
InputConsumerController inputConsumer) {
- super(context, deviceState, gestureState, inputConsumer, LAUNCHER_ACTIVITY_SIZE_STRATEGY);
+ super(context, deviceState, gestureState, inputConsumer);
mTaskAnimationManager = taskAnimationManager;
mTouchTimeMs = touchTimeMs;
mContinuingLastGesture = continuingLastGesture;
@@ -587,9 +586,8 @@
boolean quickswitchThresholdPassed = centermostTask != runningTask;
// We will handle the sysui flags based on the centermost task view.
- mRecentsAnimationController.setUseLauncherSystemBarFlags(
- (swipeUpThresholdPassed || quickswitchThresholdPassed)
- && centermostTaskFlags != 0);
+ mRecentsAnimationController.setUseLauncherSystemBarFlags(swipeUpThresholdPassed
+ || (quickswitchThresholdPassed && centermostTaskFlags != 0));
mRecentsAnimationController.setSplitScreenMinimized(swipeUpThresholdPassed);
int sysuiFlags = swipeUpThresholdPassed ? 0 : centermostTaskFlags;
@@ -684,7 +682,7 @@
setTargetAlphaProvider(LauncherSwipeHandler::getHiddenTargetAlpha);
}
- BaseDraggingActivity activity = mActivityInterface.getCreatedActivity();
+ StatefulActivity activity = mActivityInterface.getCreatedActivity();
return activity == null ? InputConsumer.NO_OP
: new OverviewInputConsumer(mGestureState, activity, null, true);
}
@@ -940,6 +938,7 @@
: null;
mActivity.getRootView().setForceHideBackArrow(true);
+ mActivityInterface.setHintUserWillBeActive();
homeAnimFactory = new HomeAnimationFactory(floatingIconView) {
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/LockScreenRecentsActivity.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/LockScreenRecentsActivity.java
deleted file mode 100644
index 65f323c..0000000
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/LockScreenRecentsActivity.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.quickstep;
-
-import android.app.Activity;
-import android.os.Bundle;
-
-/**
- * Empty activity to start a recents transition
- */
-public class LockScreenRecentsActivity extends Activity {
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- finish();
- }
-}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/OverviewCommandHelper.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/OverviewCommandHelper.java
index c6b719a..8846727 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/OverviewCommandHelper.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/OverviewCommandHelper.java
@@ -28,9 +28,10 @@
import android.view.ViewConfiguration;
import androidx.annotation.BinderThread;
-import com.android.launcher3.BaseDraggingActivity;
+
import com.android.launcher3.appprediction.PredictionUiStateManager;
import com.android.launcher3.logging.UserEventDispatcher;
+import com.android.launcher3.statemanager.StatefulActivity;
import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.quickstep.util.ActivityInitListener;
import com.android.quickstep.util.RemoteAnimationProvider;
@@ -111,7 +112,7 @@
protected void onTransitionComplete() {
// TODO(b/138729100) This doesn't execute first time launcher is run
if (mTriggeredFromAltTab) {
- RecentsView rv = (RecentsView) mActivityInterface.getVisibleRecentsView();
+ RecentsView rv = mActivityInterface.getVisibleRecentsView();
if (rv == null) {
return;
}
@@ -136,7 +137,7 @@
@Override
protected boolean handleCommand(long elapsedTime) {
- RecentsView recents = (RecentsView) mActivityInterface.getVisibleRecentsView();
+ RecentsView recents = mActivityInterface.getVisibleRecentsView();
if (recents == null) {
return false;
}
@@ -150,9 +151,9 @@
}
}
- private class RecentsActivityCommand<T extends BaseDraggingActivity> implements Runnable {
+ private class RecentsActivityCommand<T extends StatefulActivity<?>> implements Runnable {
- protected final BaseActivityInterface<T> mActivityInterface;
+ protected final BaseActivityInterface<?, T> mActivityInterface;
private final long mCreateTime;
private final AppToOverviewAnimationProvider<T> mAnimationProvider;
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsActivity.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsActivity.java
index 03d522e..a4670fd 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsActivity.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsActivity.java
@@ -261,10 +261,6 @@
AccessibilityManagerCompat.sendStateEventToTest(getBaseContext(), OVERVIEW_STATE_ORDINAL);
}
- public void onTaskLaunched() {
- mFallbackRecentsView.resetTaskVisuals();
- }
-
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
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 258d60c..acc7794 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
@@ -63,6 +63,7 @@
import com.android.launcher3.logging.UserEventDispatcher;
import com.android.launcher3.model.AppLaunchTracker;
import com.android.launcher3.provider.RestoreDbTask;
+import com.android.launcher3.statemanager.StatefulActivity;
import com.android.launcher3.testing.TestLogging;
import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.tracing.nano.LauncherTraceProto;
@@ -705,7 +706,7 @@
public InputConsumer createOverviewInputConsumer(GestureState previousGestureState,
GestureState gestureState, MotionEvent event,
boolean forceOverviewInputConsumer) {
- BaseDraggingActivity activity = gestureState.getActivityInterface().getCreatedActivity();
+ StatefulActivity activity = gestureState.getActivityInterface().getCreatedActivity();
if (activity == null) {
return mResetGestureInputConsumer;
}
@@ -754,7 +755,7 @@
return;
}
- final BaseActivityInterface<BaseDraggingActivity> activityInterface =
+ final BaseActivityInterface activityInterface =
mOverviewComponentObserver.getActivityInterface();
final Intent overviewIntent = new Intent(
mOverviewComponentObserver.getOverviewIntentIgnoreSysUiState());
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/FallbackRecentsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/FallbackRecentsView.java
index 1ab317b..f958e6d 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/FallbackRecentsView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/FallbackRecentsView.java
@@ -17,7 +17,6 @@
import static com.android.quickstep.fallback.RecentsState.DEFAULT;
import static com.android.quickstep.fallback.RecentsState.MODAL_TASK;
-import static com.android.quickstep.util.WindowSizeStrategy.FALLBACK_RECENTS_SIZE_STRATEGY;
import android.annotation.TargetApi;
import android.app.ActivityManager.RunningTaskInfo;
@@ -26,6 +25,7 @@
import android.util.AttributeSet;
import com.android.launcher3.statemanager.StateManager.StateListener;
+import com.android.quickstep.FallbackActivityInterface;
import com.android.quickstep.RecentsActivity;
import com.android.quickstep.views.OverviewActionsView;
import com.android.quickstep.views.RecentsView;
@@ -45,7 +45,7 @@
}
public FallbackRecentsView(Context context, AttributeSet attrs, int defStyleAttr) {
- super(context, attrs, defStyleAttr, FALLBACK_RECENTS_SIZE_STRATEGY);
+ super(context, attrs, defStyleAttr, FallbackActivityInterface.INSTANCE);
mActivity.getStateManager().addStateListener(this);
}
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 adf19df..2dc7f5f 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
@@ -18,14 +18,15 @@
import static android.view.MotionEvent.ACTION_CANCEL;
import static android.view.MotionEvent.ACTION_POINTER_DOWN;
import static android.view.MotionEvent.ACTION_UP;
-
import static com.android.launcher3.Utilities.squaredHypot;
import static com.android.launcher3.Utilities.squaredTouchSlop;
import static com.android.quickstep.LauncherSwipeHandler.MIN_PROGRESS_FOR_OVERVIEW;
import static com.android.quickstep.MultiStateCallback.DEBUG_STATES;
import static com.android.quickstep.util.ActiveGestureLog.INTENT_EXTRA_LOG_TRACE_ID;
-import android.content.ComponentName;
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
import android.content.Context;
import android.content.Intent;
import android.graphics.Point;
@@ -34,15 +35,14 @@
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.ViewConfiguration;
-
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
+import com.android.launcher3.anim.Interpolators;
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;
-import com.android.quickstep.LockScreenRecentsActivity;
import com.android.quickstep.MultiStateCallback;
import com.android.quickstep.RecentsAnimationCallbacks;
import com.android.quickstep.RecentsAnimationController;
@@ -52,6 +52,7 @@
import com.android.quickstep.util.AppWindowAnimationHelper;
import com.android.quickstep.util.TransformParams;
import com.android.systemui.shared.recents.model.ThumbnailData;
+import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.InputMonitorCompat;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
@@ -61,8 +62,6 @@
public class DeviceLockedInputConsumer implements InputConsumer,
RecentsAnimationCallbacks.RecentsAnimationListener {
- private static final float SCALE_DOWN = 0.75f;
-
private static final String[] STATE_NAMES = DEBUG_STATES ? new String[2] : null;
private static int getFlagForIndex(int index, String name) {
if (DEBUG_STATES) {
@@ -93,6 +92,7 @@
private float mProgress;
private boolean mThresholdCrossed = false;
+ private boolean mHomeLaunched = false;
private RecentsAnimationController mRecentsAnimationController;
private RecentsAnimationTargets mRecentsAnimationTargets;
@@ -176,7 +176,6 @@
* the animation can still be running.
*/
private void finishTouchTracking(MotionEvent ev) {
- mStateCallback.setState(STATE_HANDLER_INVALIDATED);
if (mThresholdCrossed && ev.getAction() == ACTION_UP) {
mVelocityTracker.computeCurrentVelocity(1000,
ViewConfiguration.get(mContext).getScaledMaximumFlingVelocity());
@@ -192,12 +191,34 @@
} else {
dismissTask = mProgress >= (1 - MIN_PROGRESS_FOR_OVERVIEW);
}
- if (dismissTask) {
- // For now, just start the home intent so user is prompted to unlock the device.
- mContext.startActivity(new Intent(Intent.ACTION_MAIN)
- .addCategory(Intent.CATEGORY_HOME)
- .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
- }
+
+ // Animate back to fullscreen before finishing
+ ValueAnimator animator = ValueAnimator.ofFloat(mTransformParams.getProgress(), 0f);
+ animator.setDuration(100);
+ animator.setInterpolator(Interpolators.ACCEL);
+ animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator valueAnimator) {
+ mTransformParams.setProgress((float) valueAnimator.getAnimatedValue());
+ mAppWindowAnimationHelper.applyTransform(mTransformParams);
+ }
+ });
+ animator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ if (dismissTask) {
+ // For now, just start the home intent so user is prompted to unlock the device.
+ mContext.startActivity(new Intent(Intent.ACTION_MAIN)
+ .addCategory(Intent.CATEGORY_HOME)
+ .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
+ mHomeLaunched = true;
+ }
+ mStateCallback.setState(STATE_HANDLER_INVALIDATED);
+ }
+ });
+ animator.start();
+ } else {
+ mStateCallback.setState(STATE_HANDLER_INVALIDATED);
}
mVelocityTracker.recycle();
mVelocityTracker = null;
@@ -205,13 +226,11 @@
private void startRecentsTransition() {
mThresholdCrossed = true;
+ mHomeLaunched = false;
TestLogging.recordEvent(TestProtocol.SEQUENCE_PILFER, "pilferPointers");
mInputMonitorCompat.pilferPointers();
- Intent intent = new Intent(Intent.ACTION_MAIN)
- .addCategory(Intent.CATEGORY_DEFAULT)
- .setComponent(new ComponentName(mContext, LockScreenRecentsActivity.class))
- .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK)
+ Intent intent = mGestureState.getHomeIntent()
.putExtra(INTENT_EXTRA_LOG_TRACE_ID, mGestureState.getGestureId());
mTaskAnimationManager.startRecentsAnimation(mGestureState, intent, this);
}
@@ -229,8 +248,9 @@
mAppWindowAnimationHelper.updateSource(displaySize, targetCompat);
}
- Utilities.scaleRectAboutCenter(displaySize, SCALE_DOWN);
- displaySize.offsetTo(displaySize.left, 0);
+ // Offset the surface slightly
+ displaySize.offset(0, mContext.getResources().getDimensionPixelSize(
+ R.dimen.device_locked_y_offset));
mTransformParams.setTargetSet(mRecentsAnimationTargets);
mAppWindowAnimationHelper.updateTargetRect(displaySize);
mAppWindowAnimationHelper.applyTransform(mTransformParams);
@@ -245,7 +265,9 @@
}
private void endRemoteAnimation() {
- if (mRecentsAnimationController != null) {
+ if (mHomeLaunched) {
+ ActivityManagerWrapper.getInstance().cancelRecentsAnimation(false);
+ } else if (mRecentsAnimationController != null) {
mRecentsAnimationController.finishController(
false /* toRecents */, null /* callback */, false /* sendUserLeaveHint */);
}
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 1f6c506..6b0d7a3 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
@@ -26,6 +26,7 @@
import static com.android.launcher3.Utilities.EDGE_NAV_BAR;
import static com.android.launcher3.Utilities.squaredHypot;
import static com.android.launcher3.util.TraceHelper.FLAG_CHECK_FOR_RACE_CONDITIONS;
+import static com.android.quickstep.GestureState.STATE_OVERSCROLL_WINDOW_CREATED;
import static com.android.quickstep.util.ActiveGestureLog.INTENT_EXTRA_LOG_TRACE_ID;
import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
@@ -430,6 +431,6 @@
@Override
public boolean allowInterceptByParent() {
- return !mPassedPilferInputSlop;
+ return !mPassedPilferInputSlop || mGestureState.hasState(STATE_OVERSCROLL_WINDOW_CREATED);
}
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverscrollInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverscrollInputConsumer.java
index c49b8f2..fb420a2 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverscrollInputConsumer.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverscrollInputConsumer.java
@@ -24,12 +24,15 @@
import static com.android.launcher3.Utilities.squaredHypot;
+import static java.lang.Math.abs;
+
import android.content.Context;
import android.graphics.PointF;
-import android.view.GestureDetector;
+import android.util.Log;
import android.view.MotionEvent;
import android.view.ViewConfiguration;
+import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.launcher3.BaseDraggingActivity;
@@ -44,24 +47,36 @@
* Input consumer for handling events to pass to an {@code OverscrollPlugin}.
*/
public class OverscrollInputConsumer extends DelegateInputConsumer {
-
private static final String TAG = "OverscrollInputConsumer";
+ private static final boolean DEBUG_LOGS_ENABLED = false;
+ private static void debugPrint(String log) {
+ if (DEBUG_LOGS_ENABLED) {
+ Log.v(TAG, log);
+ }
+ }
private final PointF mDownPos = new PointF();
private final PointF mLastPos = new PointF();
private final PointF mStartDragPos = new PointF();
private final int mAngleThreshold;
- private final float mFlingThresholdPx;
+ private final int mFlingDistanceThresholdPx;
+ private final int mFlingVelocityThresholdPx;
private int mActivePointerId = -1;
private boolean mPassedSlop = false;
-
+ // True if we set ourselves as active, meaning we no longer pass events to the delegate.
+ private boolean mPassedActiveThreshold = false;
+ // When a gesture crosses this length, this recognizer will attempt to interpret touch events.
private final float mSquaredSlop;
+ // When a gesture crosses this length, this recognizer will become the sole active recognizer.
+ private final float mSquaredActiveThreshold;
+ // When a gesture crosses this length, the overscroll view should be shown.
+ private final float mSquaredFinishThreshold;
+ private boolean mThisDownIsIgnored = false;
private final GestureState mGestureState;
@Nullable
private final OverscrollPlugin mPlugin;
- private final GestureDetector mGestureDetector;
@Nullable
private RecentsView mRecentsView;
@@ -72,15 +87,24 @@
mAngleThreshold = context.getResources()
.getInteger(R.integer.assistant_gesture_corner_deg_threshold);
- mFlingThresholdPx = context.getResources()
- .getDimension(R.dimen.gestures_overscroll_fling_threshold);
+ mFlingDistanceThresholdPx = (int) context.getResources()
+ .getDimension(R.dimen.gestures_overscroll_fling_threshold);
+ mFlingVelocityThresholdPx = ViewConfiguration.get(context).getScaledMinimumFlingVelocity();
mGestureState = gestureState;
mPlugin = plugin;
float slop = ViewConfiguration.get(context).getScaledTouchSlop();
mSquaredSlop = slop * slop;
- mGestureDetector = new GestureDetector(context, new FlingGestureListener());
+
+
+ float finishGestureThreshold = (int) context.getResources()
+ .getDimension(R.dimen.gestures_overscroll_finish_threshold);
+ mSquaredFinishThreshold = finishGestureThreshold * finishGestureThreshold;
+
+ float activeThreshold = (int) context.getResources()
+ .getDimension(R.dimen.gestures_overscroll_active_threshold);
+ mSquaredActiveThreshold = activeThreshold * activeThreshold;
}
@Override
@@ -90,12 +114,26 @@
@Override
public void onMotionEvent(MotionEvent ev) {
+ if (mPlugin == null) {
+ return;
+ }
+
+ debugPrint("got event, underlying activity is " + getUnderlyingActivity());
switch (ev.getActionMasked()) {
case ACTION_DOWN: {
+ debugPrint("ACTION_DOWN");
mActivePointerId = ev.getPointerId(0);
mDownPos.set(ev.getX(), ev.getY());
mLastPos.set(mDownPos);
-
+ if (mPlugin.blockOtherGestures()) {
+ debugPrint("mPlugin.blockOtherGestures(), becoming active on ACTION_DOWN");
+ // Otherwise, if an appear gesture is performed when the Activity is visible,
+ // the Activity will dismiss its keyboard.
+ mPassedActiveThreshold = true;
+ mPassedSlop = true;
+ mStartDragPos.set(mLastPos.x, mLastPos.y);
+ setActive(ev);
+ }
break;
}
case ACTION_POINTER_DOWN: {
@@ -121,57 +159,61 @@
if (mState == STATE_DELEGATE_ACTIVE) {
break;
}
+
if (!mDelegate.allowInterceptByParent()) {
mState = STATE_DELEGATE_ACTIVE;
break;
}
+
+ // Update last touch position.
int pointerIndex = ev.findPointerIndex(mActivePointerId);
if (pointerIndex == -1) {
break;
}
mLastPos.set(ev.getX(pointerIndex), ev.getY(pointerIndex));
- if (!mPassedSlop) {
- // Normal gesture, ensure we pass the slop before we start tracking the gesture
- if (squaredHypot(mLastPos.x - mDownPos.x, mLastPos.y - mDownPos.y)
- > mSquaredSlop) {
-
- mPassedSlop = true;
- mStartDragPos.set(mLastPos.x, mLastPos.y);
- if (isOverscrolled()) {
- setActive(ev);
-
- if (mPlugin != null) {
- mPlugin.onTouchStart(getDeviceState(), getUnderlyingActivity());
- }
- } else {
- mState = STATE_DELEGATE_ACTIVE;
- }
- }
+ float squaredDist = squaredHypot(mLastPos.x - mDownPos.x, mLastPos.y - mDownPos.y);
+ if ((!mPassedSlop) && (squaredDist > mSquaredSlop)) {
+ mPassedSlop = true;
+ mStartDragPos.set(mLastPos.x, mLastPos.y);
+ mGestureState.setState(GestureState.STATE_OVERSCROLL_WINDOW_CREATED);
}
- if (mPassedSlop && mState != STATE_DELEGATE_ACTIVE && isOverscrolled()
- && mPlugin != null) {
- mPlugin.onTouchTraveled(getDistancePx());
+ boolean becomeActive = mPassedSlop && !mPassedActiveThreshold && isOverscrolled()
+ && (squaredDist > mSquaredActiveThreshold);
+ if (becomeActive) {
+ debugPrint("Past slop and past threshold, set active");
+ mPassedActiveThreshold = true;
+ setActive(ev);
+ }
+
+ if (mPassedActiveThreshold) {
+ debugPrint("ACTION_MOVE Relaying touch event");
+ mPlugin.onTouchEvent(ev, getHorizontalDistancePx(), getVerticalDistancePx(),
+ (int) Math.sqrt(mSquaredFinishThreshold), mFlingDistanceThresholdPx,
+ mFlingVelocityThresholdPx, getDeviceState(), getUnderlyingActivity());
}
break;
}
case ACTION_CANCEL:
case ACTION_UP:
- if (mState != STATE_DELEGATE_ACTIVE && mPassedSlop && mPlugin != null) {
- mPlugin.onTouchEnd(getDistancePx());
+ debugPrint("ACTION_UP");
+ if (mPassedActiveThreshold) {
+ debugPrint("ACTION_UP Relaying touch event");
+
+ mPlugin.onTouchEvent(ev, getHorizontalDistancePx(), getVerticalDistancePx(),
+ (int) Math.sqrt(mSquaredFinishThreshold), mFlingDistanceThresholdPx,
+ mFlingVelocityThresholdPx, getDeviceState(), getUnderlyingActivity());
}
+
mPassedSlop = false;
+ mPassedActiveThreshold = false;
mState = STATE_INACTIVE;
break;
}
- if (mState != STATE_DELEGATE_ACTIVE) {
- mGestureDetector.onTouchEvent(ev);
- }
-
if (mState != STATE_ACTIVE) {
mDelegate.onMotionEvent(ev);
}
@@ -192,15 +234,20 @@
maxIndex = 1;
}
- boolean atRightMostApp = (mRecentsView == null
- || mRecentsView.getRunningTaskIndex() <= maxIndex);
+ boolean atRightMostApp = mRecentsView == null
+ || (mRecentsView.getRunningTaskIndex() <= maxIndex);
// Check if the gesture is within our angle threshold of horizontal
- float deltaY = Math.abs(mLastPos.y - mDownPos.y);
- float deltaX = mDownPos.x - mLastPos.x; // Positive if this is a gesture to the left
- boolean angleInBounds = Math.toDegrees(Math.atan2(deltaY, deltaX)) < mAngleThreshold;
+ float deltaY = abs(mLastPos.y - mDownPos.y);
+ float deltaX = mLastPos.x - mDownPos.x;
- return atRightMostApp && angleInBounds;
+ boolean angleInBounds = (Math.toDegrees(Math.atan2(deltaY, abs(deltaX))) < mAngleThreshold);
+
+ boolean overscrollVisible = mPlugin.blockOtherGestures();
+ boolean overscrollInvisibleAndLeftSwipe = !overscrollVisible && deltaX < 0;
+ boolean gestureDirectionMatchesVisibility = overscrollVisible
+ || overscrollInvisibleAndLeftSwipe;
+ return atRightMostApp && angleInBounds && gestureDirectionMatchesVisibility;
}
private String getDeviceState() {
@@ -219,35 +266,22 @@
return deviceState;
}
- private int getDistancePx() {
- return (int) Math.hypot(mLastPos.x - mDownPos.x, mLastPos.y - mDownPos.y);
+ private int getHorizontalDistancePx() {
+ return (int) (mLastPos.x - mDownPos.x);
}
- private String getUnderlyingActivity() {
+ private int getVerticalDistancePx() {
+ return (int) (mLastPos.y - mDownPos.y);
+ }
+
+ private @NonNull String getUnderlyingActivity() {
+ // Overly defensive, got guidance on code review that something in the chain of
+ // `mGestureState.getRunningTask().topActivity` can be null and thus cause a null pointer
+ // exception to be thrown, but we aren't sure which part can be null.
+ if ((mGestureState == null) || (mGestureState.getRunningTask() == null)
+ || (mGestureState.getRunningTask().topActivity == null)) {
+ return "";
+ }
return mGestureState.getRunningTask().topActivity.flattenToString();
}
-
- private class FlingGestureListener extends GestureDetector.SimpleOnGestureListener {
- @Override
- public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
- if (isValidAngle(velocityX, -velocityY)
- && getDistancePx() >= mFlingThresholdPx
- && mState != STATE_DELEGATE_ACTIVE) {
-
- if (mPlugin != null) {
- mPlugin.onFling(-velocityX);
- }
- }
- return true;
- }
-
- private boolean isValidAngle(float deltaX, float deltaY) {
- float angle = (float) Math.toDegrees(Math.atan2(deltaY, deltaX));
- // normalize so that angle is measured clockwise from horizontal in the bottom right
- // corner and counterclockwise from horizontal in the bottom left corner
-
- angle = angle > 90 ? 180 - angle : angle;
- return (angle < mAngleThreshold);
- }
- }
}
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 c82d4b5..11fee2f 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
@@ -24,8 +24,8 @@
import androidx.annotation.Nullable;
-import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.Utilities;
+import com.android.launcher3.statemanager.StatefulActivity;
import com.android.launcher3.testing.TestLogging;
import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.views.BaseDragLayer;
@@ -41,11 +41,11 @@
/**
* Input consumer for handling touch on the recents/Launcher activity.
*/
-public class OverviewInputConsumer<T extends BaseDraggingActivity>
+public class OverviewInputConsumer<T extends StatefulActivity<?>>
implements InputConsumer {
private final T mActivity;
- private final BaseActivityInterface<T> mActivityInterface;
+ private final BaseActivityInterface<?, T> mActivityInterface;
private final BaseDragLayer mTarget;
private final InputMonitorCompat mInputMonitor;
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/TaskViewSimulator.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/TaskViewSimulator.java
index b8f0f4d..a3db940 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/TaskViewSimulator.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/TaskViewSimulator.java
@@ -15,8 +15,6 @@
*/
package com.android.quickstep.util;
-import static android.view.Surface.ROTATION_0;
-
import static com.android.launcher3.states.RotationHelper.deltaRotation;
import static com.android.launcher3.touch.PagedOrientationHandler.MATRIX_POST_TRANSLATE;
import static com.android.quickstep.util.RecentsOrientedState.postDisplayRotation;
@@ -33,6 +31,7 @@
import com.android.launcher3.Utilities;
import com.android.launcher3.touch.PagedOrientationHandler;
import com.android.quickstep.AnimatedFloat;
+import com.android.quickstep.BaseActivityInterface;
import com.android.quickstep.views.RecentsView.ScrollState;
import com.android.quickstep.views.TaskThumbnailView.PreviewPositionHelper;
import com.android.quickstep.views.TaskView;
@@ -52,7 +51,7 @@
private final RecentsOrientedState mOrientationState;
private final Context mContext;
- private final WindowSizeStrategy mSizeStrategy;
+ private final BaseActivityInterface mSizeStrategy;
private final Rect mTaskRect = new Rect();
private final PointF mPivot = new PointF();
@@ -81,7 +80,7 @@
private boolean mLayoutValid = false;
private boolean mScrollValid = false;
- public TaskViewSimulator(Context context, WindowSizeStrategy sizeStrategy) {
+ public TaskViewSimulator(Context context, BaseActivityInterface sizeStrategy) {
mContext = context;
mSizeStrategy = sizeStrategy;
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java
index 250c78b..3d89403 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java
@@ -25,7 +25,6 @@
import static com.android.launcher3.QuickstepAppTransitionManagerImpl.ALL_APPS_PROGRESS_OFF_SCREEN;
import static com.android.launcher3.allapps.AllAppsTransitionController.ALL_APPS_PROGRESS;
import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
-import static com.android.quickstep.util.WindowSizeStrategy.LAUNCHER_ACTIVITY_SIZE_STRATEGY;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
@@ -47,6 +46,7 @@
import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
import com.android.launcher3.util.TraceHelper;
import com.android.launcher3.views.ScrimView;
+import com.android.quickstep.LauncherActivityInterface;
import com.android.quickstep.SysUINavigationMode;
import com.android.quickstep.util.TransformParams;
import com.android.systemui.plugins.PluginListener;
@@ -89,7 +89,7 @@
}
public LauncherRecentsView(Context context, AttributeSet attrs, int defStyleAttr) {
- super(context, attrs, defStyleAttr, LAUNCHER_ACTIVITY_SIZE_STRATEGY);
+ super(context, attrs, defStyleAttr, LauncherActivityInterface.INSTANCE);
mActivity.getStateManager().addStateListener(this);
}
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 54ed40f..253e83c 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
@@ -115,6 +115,7 @@
import com.android.launcher3.util.OverScroller;
import com.android.launcher3.util.Themes;
import com.android.launcher3.util.ViewPool;
+import com.android.quickstep.BaseActivityInterface;
import com.android.quickstep.RecentsAnimationController;
import com.android.quickstep.RecentsAnimationTargets;
import com.android.quickstep.RecentsModel;
@@ -128,7 +129,6 @@
import com.android.quickstep.util.RecentsOrientedState;
import com.android.quickstep.util.SplitScreenBounds;
import com.android.quickstep.util.TransformParams;
-import com.android.quickstep.util.WindowSizeStrategy;
import com.android.systemui.plugins.ResourceProvider;
import com.android.systemui.shared.recents.IPinnedStackAnimationListener;
import com.android.systemui.shared.recents.model.Task;
@@ -209,7 +209,7 @@
};
protected final RecentsOrientedState mOrientationState;
- protected final WindowSizeStrategy mSizeStrategy;
+ protected final BaseActivityInterface mSizeStrategy;
protected RecentsAnimationController mRecentsAnimationController;
protected RecentsAnimationTargets mRecentsAnimationTargets;
protected AppWindowAnimationHelper mAppWindowAnimationHelper;
@@ -381,7 +381,7 @@
};
public RecentsView(Context context, AttributeSet attrs, int defStyleAttr,
- WindowSizeStrategy sizeStrategy) {
+ BaseActivityInterface sizeStrategy) {
super(context, attrs, defStyleAttr);
setPageSpacing(getResources().getDimensionPixelSize(R.dimen.recents_page_spacing));
setEnableFreeScroll(true);
diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml
index 72275c8..f1ea6bb 100644
--- a/quickstep/res/values/dimens.xml
+++ b/quickstep/res/values/dimens.xml
@@ -79,8 +79,13 @@
<!-- Distance to move elements when swiping up to go home from launcher -->
<dimen name="home_pullback_distance">28dp</dimen>
+ <!-- Distance to move the tasks when swiping up while the device is locked -->
+ <dimen name="device_locked_y_offset">-80dp</dimen>
+
<!-- Overscroll Gesture -->
<dimen name="gestures_overscroll_fling_threshold">40dp</dimen>
+ <dimen name="gestures_overscroll_active_threshold">80dp</dimen>
+ <dimen name="gestures_overscroll_finish_threshold">136dp</dimen>
<!-- Tips Gesture Tutorial -->
<dimen name="gesture_tutorial_title_margin_start_end">40dp</dimen>
diff --git a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
index 629a74b..0968d8e 100644
--- a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
@@ -304,4 +304,8 @@
public ShelfPeekAnim getShelfPeekAnim() {
return mShelfPeekAnim;
}
+
+ public void setHintUserWillBeActive() {
+ addActivityFlags(ACTIVITY_STATE_USER_WILL_BE_ACTIVE);
+ }
}
diff --git a/quickstep/src/com/android/quickstep/BaseActivityInterface.java b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
index 43328b6..f29f0ff 100644
--- a/quickstep/src/com/android/quickstep/BaseActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
@@ -15,23 +15,34 @@
*/
package com.android.quickstep;
+import static com.android.launcher3.config.FeatureFlags.ENABLE_OVERVIEW_ACTIONS;
+import static com.android.quickstep.SysUINavigationMode.getMode;
+import static com.android.quickstep.SysUINavigationMode.removeShelfFromOverview;
+
import android.annotation.TargetApi;
import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.PointF;
import android.graphics.Rect;
import android.os.Build;
import android.view.MotionEvent;
-import android.view.View;
import android.view.animation.Interpolator;
import androidx.annotation.Nullable;
import androidx.annotation.UiThread;
-import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.R;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.statehandlers.DepthController;
+import com.android.launcher3.statemanager.BaseState;
+import com.android.launcher3.statemanager.StatefulActivity;
+import com.android.launcher3.util.WindowBounds;
+import com.android.quickstep.SysUINavigationMode.Mode;
import com.android.quickstep.util.ActivityInitListener;
import com.android.quickstep.util.ShelfPeekAnim;
+import com.android.quickstep.util.SplitScreenBounds;
+import com.android.quickstep.views.RecentsView;
import com.android.systemui.shared.recents.model.ThumbnailData;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
@@ -42,84 +53,234 @@
* Utility class which abstracts out the logical differences between Launcher and RecentsActivity.
*/
@TargetApi(Build.VERSION_CODES.P)
-public interface BaseActivityInterface<T extends BaseDraggingActivity> {
+public abstract class BaseActivityInterface<STATE_TYPE extends BaseState<STATE_TYPE>,
+ ACTIVITY_TYPE extends StatefulActivity<STATE_TYPE>> {
- void onTransitionCancelled(boolean activityVisible);
+ private final PointF mTempPoint = new PointF();
+ public final boolean rotationSupportedByActivity;
- int getSwipeUpDestinationAndLength(DeviceProfile dp, Context context, Rect outRect);
+ protected BaseActivityInterface(boolean rotationSupportedByActivity) {
+ this.rotationSupportedByActivity = rotationSupportedByActivity;
+ }
- void onSwipeUpToRecentsComplete();
+ public void onTransitionCancelled(boolean activityVisible) {
+ ACTIVITY_TYPE activity = getCreatedActivity();
+ if (activity == null) {
+ return;
+ }
+ STATE_TYPE startState = activity.getStateManager().getRestState();
+ activity.getStateManager().goToState(startState, activityVisible);
+ }
- default void onSwipeUpToHomeComplete() { }
- void onAssistantVisibilityChanged(float visibility);
+ public abstract int getSwipeUpDestinationAndLength(
+ DeviceProfile dp, Context context, Rect outRect);
- AnimationFactory prepareRecentsUI(
+ public void onSwipeUpToRecentsComplete() {
+ // Re apply state in case we did something funky during the transition.
+ ACTIVITY_TYPE activity = getCreatedActivity();
+ if (activity == null) {
+ return;
+ }
+ activity.getStateManager().reapplyState();
+ }
+
+ public void onSwipeUpToHomeComplete() { }
+
+ public abstract void onAssistantVisibilityChanged(float visibility);
+
+ public abstract AnimationFactory prepareRecentsUI(
boolean activityVisible, Consumer<AnimatorPlaybackController> callback);
- ActivityInitListener createActivityInitListener(Predicate<Boolean> onInitListener);
+ public abstract ActivityInitListener createActivityInitListener(
+ Predicate<Boolean> onInitListener);
/**
* Sets a callback to be run when an activity launch happens while launcher is not yet resumed.
*/
- default void setOnDeferredActivityLaunchCallback(Runnable r) {}
+ public void setOnDeferredActivityLaunchCallback(Runnable r) {}
@Nullable
- T getCreatedActivity();
+ public abstract ACTIVITY_TYPE getCreatedActivity();
@Nullable
- default DepthController getDepthController() {
+ public DepthController getDepthController() {
return null;
}
- default boolean isResumed() {
- BaseDraggingActivity activity = getCreatedActivity();
+ public final boolean isResumed() {
+ ACTIVITY_TYPE activity = getCreatedActivity();
return activity != null && activity.hasBeenResumed();
}
- default boolean isStarted() {
- BaseDraggingActivity activity = getCreatedActivity();
+ public final boolean isStarted() {
+ ACTIVITY_TYPE activity = getCreatedActivity();
return activity != null && activity.isStarted();
}
@UiThread
@Nullable
- <T extends View> T getVisibleRecentsView();
+ public abstract <T extends RecentsView> T getVisibleRecentsView();
@UiThread
- boolean switchToRecentsIfVisible(Runnable onCompleteCallback);
+ public abstract boolean switchToRecentsIfVisible(Runnable onCompleteCallback);
- Rect getOverviewWindowBounds(Rect homeBounds, RemoteAnimationTargetCompat target);
+ public abstract Rect getOverviewWindowBounds(
+ Rect homeBounds, RemoteAnimationTargetCompat target);
- boolean allowMinimizeSplitScreen();
+ public abstract boolean allowMinimizeSplitScreen();
- default boolean deferStartingActivity(RecentsAnimationDeviceState deviceState, MotionEvent ev) {
+ public boolean deferStartingActivity(RecentsAnimationDeviceState deviceState, MotionEvent ev) {
return true;
}
/**
* Updates the prediction state to the overview state.
*/
- default void updateOverviewPredictionState() {
- // By default overview predictions are not supported
+ public void updateOverviewPredictionState() {
+ // By public overview predictions are not supported
}
/**
* Used for containerType in {@link com.android.launcher3.logging.UserEventDispatcher}
*/
- int getContainerType();
+ public abstract int getContainerType();
- boolean isInLiveTileMode();
+ public abstract boolean isInLiveTileMode();
- void onLaunchTaskFailed();
+ public abstract void onLaunchTaskFailed();
- void onLaunchTaskSuccess();
+ public void onLaunchTaskSuccess() {
+ ACTIVITY_TYPE activity = getCreatedActivity();
+ if (activity == null) {
+ return;
+ }
+ activity.getStateManager().moveToRestState();
+ }
- default void closeOverlay() { }
+ public void closeOverlay() { }
- default void switchRunningTaskViewToScreenshot(ThumbnailData thumbnailData,
- Runnable runnable) {}
+ public void switchRunningTaskViewToScreenshot(ThumbnailData thumbnailData, Runnable runnable) {
+ ACTIVITY_TYPE activity = getCreatedActivity();
+ if (activity == null) {
+ return;
+ }
+ RecentsView recentsView = activity.getOverviewPanel();
+ if (recentsView == null) {
+ if (runnable != null) {
+ runnable.run();
+ }
+ return;
+ }
+ recentsView.switchToScreenshot(thumbnailData, runnable);
+ }
- interface AnimationFactory {
+ public void setHintUserWillBeActive() {}
+
+ /**
+ * Sets the expected window size in multi-window mode
+ */
+ public abstract void getMultiWindowSize(Context context, DeviceProfile dp, PointF out);
+
+ /**
+ * Calculates the taskView size for the provided device configuration
+ */
+ public final void calculateTaskSize(Context context, DeviceProfile dp, Rect outRect) {
+ calculateTaskSize(context, dp, getExtraSpace(context, dp), outRect);
+ }
+
+ protected abstract float getExtraSpace(Context context, DeviceProfile dp);
+
+ private void calculateTaskSize(
+ Context context, DeviceProfile dp, float extraVerticalSpace, Rect outRect) {
+ Resources res = context.getResources();
+ final boolean showLargeTaskSize = showOverviewActions(context);
+
+ final int paddingResId;
+ if (dp.isMultiWindowMode) {
+ paddingResId = R.dimen.multi_window_task_card_horz_space;
+ } else if (dp.isVerticalBarLayout()) {
+ paddingResId = R.dimen.landscape_task_card_horz_space;
+ } else if (showLargeTaskSize) {
+ paddingResId = R.dimen.portrait_task_card_horz_space_big_overview;
+ } else {
+ paddingResId = R.dimen.portrait_task_card_horz_space;
+ }
+ float paddingHorz = res.getDimension(paddingResId);
+ float paddingVert = showLargeTaskSize
+ ? 0 : res.getDimension(R.dimen.task_card_vert_space);
+
+ calculateTaskSizeInternal(context, dp, extraVerticalSpace, paddingHorz, paddingVert,
+ res.getDimension(R.dimen.task_thumbnail_top_margin), outRect);
+ }
+
+ private void calculateTaskSizeInternal(Context context, DeviceProfile dp,
+ float extraVerticalSpace, float paddingHorz, float paddingVert, float topIconMargin,
+ Rect outRect) {
+ float taskWidth, taskHeight;
+ Rect insets = dp.getInsets();
+ if (dp.isMultiWindowMode) {
+ WindowBounds bounds = SplitScreenBounds.INSTANCE.getSecondaryWindowBounds(context);
+ taskWidth = bounds.availableSize.x;
+ taskHeight = bounds.availableSize.y;
+ } else {
+ taskWidth = dp.availableWidthPx;
+ taskHeight = dp.availableHeightPx;
+ }
+
+ // Note this should be same as dp.availableWidthPx and dp.availableHeightPx unless
+ // we override the insets ourselves.
+ int launcherVisibleWidth = dp.widthPx - insets.left - insets.right;
+ int launcherVisibleHeight = dp.heightPx - insets.top - insets.bottom;
+
+ float availableHeight = launcherVisibleHeight
+ - topIconMargin - extraVerticalSpace - paddingVert;
+ float availableWidth = launcherVisibleWidth - paddingHorz;
+
+ float scale = Math.min(availableWidth / taskWidth, availableHeight / taskHeight);
+ float outWidth = scale * taskWidth;
+ float outHeight = scale * taskHeight;
+
+ // Center in the visible space
+ float x = insets.left + (launcherVisibleWidth - outWidth) / 2;
+ float y = insets.top + Math.max(topIconMargin,
+ (launcherVisibleHeight - extraVerticalSpace - outHeight) / 2);
+ outRect.set(Math.round(x), Math.round(y),
+ Math.round(x) + Math.round(outWidth), Math.round(y) + Math.round(outHeight));
+ }
+
+ /**
+ * Calculates the modal taskView size for the provided device configuration
+ */
+ public void calculateModalTaskSize(Context context, DeviceProfile dp, Rect outRect) {
+ float paddingHorz = context.getResources().getDimension(dp.isMultiWindowMode
+ ? R.dimen.multi_window_task_card_horz_space
+ : dp.isVerticalBarLayout()
+ ? R.dimen.landscape_task_card_horz_space
+ : R.dimen.portrait_modal_task_card_horz_space);
+ float extraVerticalSpace = getOverviewActionsHeight(context);
+ float paddingVert = 0;
+ float topIconMargin = 0;
+ calculateTaskSizeInternal(context, dp, extraVerticalSpace, paddingHorz, paddingVert,
+ topIconMargin, outRect);
+ }
+
+ /** Gets the space that the overview actions will take, including margins. */
+ public float getOverviewActionsHeight(Context context) {
+ Resources res = context.getResources();
+ float actionsBottomMargin = 0;
+ if (getMode(context) == Mode.THREE_BUTTONS) {
+ actionsBottomMargin = res.getDimensionPixelSize(
+ R.dimen.overview_actions_bottom_margin_three_button);
+ } else {
+ actionsBottomMargin = res.getDimensionPixelSize(
+ R.dimen.overview_actions_bottom_margin_gesture);
+ }
+ float overviewActionsHeight = actionsBottomMargin
+ + res.getDimensionPixelSize(R.dimen.overview_actions_height);
+ return overviewActionsHeight;
+ }
+
+ public interface AnimationFactory {
default void onRemoteAnimationReceived(RemoteAnimationTargets targets) { }
@@ -137,4 +298,8 @@
*/
default void setRecentsAttachedToAppWindow(boolean attached, boolean animate) { }
}
+
+ protected static boolean showOverviewActions(Context context) {
+ return ENABLE_OVERVIEW_ACTIONS.get() && removeShelfFromOverview(context);
+ }
}
diff --git a/quickstep/src/com/android/quickstep/GestureState.java b/quickstep/src/com/android/quickstep/GestureState.java
index 9b515ae..8e14bbb 100644
--- a/quickstep/src/com/android/quickstep/GestureState.java
+++ b/quickstep/src/com/android/quickstep/GestureState.java
@@ -17,10 +17,12 @@
import static com.android.quickstep.MultiStateCallback.DEBUG_STATES;
+import android.annotation.TargetApi;
import android.app.ActivityManager;
import android.content.Intent;
+import android.os.Build;
-import com.android.launcher3.BaseDraggingActivity;
+import com.android.launcher3.statemanager.StatefulActivity;
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
import com.android.systemui.shared.recents.model.ThumbnailData;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
@@ -32,6 +34,7 @@
* Manages the state for an active system gesture, listens for events from the system and Launcher,
* and fires events when the states change.
*/
+@TargetApi(Build.VERSION_CODES.R)
public class GestureState implements RecentsAnimationCallbacks.RecentsAnimationListener {
/**
@@ -106,6 +109,10 @@
public static final int STATE_RECENTS_ANIMATION_ENDED =
getFlagForIndex("STATE_RECENTS_ANIMATION_ENDED");
+ // Called when we create an overscroll window when swiping right to left on the most recent app
+ public static final int STATE_OVERSCROLL_WINDOW_CREATED =
+ getFlagForIndex("STATE_OVERSCROLL_WINDOW_CREATED");
+
// Called when RecentsView stops scrolling and settles on a TaskView.
public static final int STATE_RECENTS_SCROLLING_FINISHED =
getFlagForIndex("STATE_RECENTS_SCROLLING_FINISHED");
@@ -189,7 +196,7 @@
/**
* @return the interface to the activity handing the UI updates for this gesture.
*/
- public <T extends BaseDraggingActivity> BaseActivityInterface<T> getActivityInterface() {
+ public <T extends StatefulActivity<?>> BaseActivityInterface<?, T> getActivityInterface() {
return mActivityInterface;
}
diff --git a/quickstep/src/com/android/quickstep/OverviewComponentObserver.java b/quickstep/src/com/android/quickstep/OverviewComponentObserver.java
index 231ee72..0449d0c 100644
--- a/quickstep/src/com/android/quickstep/OverviewComponentObserver.java
+++ b/quickstep/src/com/android/quickstep/OverviewComponentObserver.java
@@ -140,7 +140,7 @@
if (!mDeviceState.isHomeDisabled() && (defaultHome == null || mIsDefaultHome)) {
// User default home is same as out home app. Use Overview integrated in Launcher.
- mActivityInterface = new LauncherActivityInterface();
+ mActivityInterface = LauncherActivityInterface.INSTANCE;
mIsHomeAndOverviewSame = true;
mOverviewIntent = mMyHomeIntent;
mCurrentHomeIntent.setComponent(mMyHomeIntent.getComponent());
@@ -150,7 +150,7 @@
} else {
// The default home app is a different launcher. Use the fallback Overview instead.
- mActivityInterface = new FallbackActivityInterface();
+ mActivityInterface = FallbackActivityInterface.INSTANCE;
mIsHomeAndOverviewSame = false;
mOverviewIntent = mFallbackIntent;
mCurrentHomeIntent.setComponent(defaultHome);
diff --git a/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java b/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
index a98aad1..8889560 100644
--- a/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
+++ b/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
@@ -64,24 +64,46 @@
}
/**
- * Logs an event and accompanying {@link ItemInfo}
+ * Logs a {@link LauncherEvent}.
*/
+ @Override
+ public void log(LauncherEvent event) {
+ log(event, DEFAULT_INSTANCE_ID, LauncherAtom.ItemInfo.getDefaultInstance());
+ }
+
+ /**
+ * Logs an event and accompanying {@link InstanceId}.
+ */
+ @Override
+ public void log(LauncherEvent event, InstanceId instanceId) {
+ log(event, instanceId, LauncherAtom.ItemInfo.getDefaultInstance());
+ }
+
+ /**
+ * Logs an event and accompanying {@link ItemInfo}.
+ */
+ @Override
public void log(LauncherEvent event, LauncherAtom.ItemInfo itemInfo) {
log(event, DEFAULT_INSTANCE_ID, itemInfo);
}
/**
- * Logs an event and accompanying {@link LauncherAtom.ItemInfo}
+ * Logs an event and accompanying {@link InstanceId} and {@link LauncherAtom.ItemInfo}.
*/
@Override
public void log(LauncherEvent event, InstanceId instanceId, LauncherAtom.ItemInfo itemInfo) {
if (IS_VERBOSE) {
- Log.d(TAG, String.format("\n%s\n%s", event.name(), itemInfo));
+ Log.d(TAG, instanceId == DEFAULT_INSTANCE_ID
+ ? String.format("\n%s\n%s", event.name(), itemInfo)
+ : String.format("%s(InstanceId:%s)\n%s", event.name(), instanceId, itemInfo));
}
+
if (!Utilities.ATLEAST_R) {
return;
}
- SysUiStatsLog.write(SysUiStatsLog.LAUNCHER_EVENT,
+
+ SysUiStatsLog.write(
+ SysUiStatsLog.LAUNCHER_EVENT,
SysUiStatsLog.LAUNCHER_UICHANGED__ACTION__DEFAULT_ACTION /* deprecated */,
SysUiStatsLog.LAUNCHER_UICHANGED__DST_STATE__HOME /* TODO */,
SysUiStatsLog.LAUNCHER_UICHANGED__DST_STATE__BACKGROUND /* TODO */,
@@ -118,6 +140,7 @@
}
private class SnapshotWorker extends BaseModelUpdateTask {
+
@Override
public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
IntSparseArrayMap<FolderInfo> folders = dataModel.folders.clone();
@@ -140,6 +163,7 @@
}
}
}
+
private static void writeSnapshot(LauncherAtom.ItemInfo itemInfo) {
if (IS_VERBOSE) {
Log.d(TAG, "\nwriteSnapshot:" + itemInfo);
diff --git a/quickstep/src/com/android/quickstep/util/LayoutUtils.java b/quickstep/src/com/android/quickstep/util/LayoutUtils.java
index fa53be2..c1b276a 100644
--- a/quickstep/src/com/android/quickstep/util/LayoutUtils.java
+++ b/quickstep/src/com/android/quickstep/util/LayoutUtils.java
@@ -17,7 +17,6 @@
import static com.android.launcher3.config.FeatureFlags.ENABLE_OVERVIEW_ACTIONS;
import static com.android.quickstep.SysUINavigationMode.removeShelfFromOverview;
-import static com.android.quickstep.util.WindowSizeStrategy.LAUNCHER_ACTIVITY_SIZE_STRATEGY;
import android.content.Context;
import android.graphics.Rect;
@@ -26,6 +25,7 @@
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.R;
+import com.android.quickstep.LauncherActivityInterface;
import com.android.quickstep.SysUINavigationMode;
public class LayoutUtils {
@@ -45,7 +45,7 @@
// Track the bottom of the window.
if (ENABLE_OVERVIEW_ACTIONS.get() && removeShelfFromOverview(context)) {
Rect taskSize = new Rect();
- LAUNCHER_ACTIVITY_SIZE_STRATEGY.calculateTaskSize(context, dp, taskSize);
+ LauncherActivityInterface.INSTANCE.calculateTaskSize(context, dp, taskSize);
return (dp.heightPx - taskSize.height()) / 2;
}
int shelfHeight = dp.hotseatBarSizePx + dp.getInsets().bottom;
diff --git a/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java b/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java
index e7ff48f..e03f4b8 100644
--- a/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java
+++ b/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java
@@ -52,6 +52,7 @@
import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.touch.PagedOrientationHandler;
import com.android.launcher3.util.WindowBounds;
+import com.android.quickstep.BaseActivityInterface;
import java.lang.annotation.Retention;
import java.util.function.IntConsumer;
@@ -121,7 +122,7 @@
private final ContentResolver mContentResolver;
private final SharedPreferences mSharedPrefs;
private final OrientationEventListener mOrientationListener;
- private final WindowSizeStrategy mSizeStrategy;
+ private final BaseActivityInterface mSizeStrategy;
private final Matrix mTmpMatrix = new Matrix();
@@ -133,7 +134,7 @@
* is enabled
* @see #setRotationWatcherEnabled(boolean)
*/
- public RecentsOrientedState(Context context, WindowSizeStrategy sizeStrategy,
+ public RecentsOrientedState(Context context, BaseActivityInterface sizeStrategy,
IntConsumer rotationChangeListener) {
mContext = context;
mContentResolver = context.getContentResolver();
diff --git a/quickstep/src/com/android/quickstep/util/WindowSizeStrategy.java b/quickstep/src/com/android/quickstep/util/WindowSizeStrategy.java
deleted file mode 100644
index b088ba8..0000000
--- a/quickstep/src/com/android/quickstep/util/WindowSizeStrategy.java
+++ /dev/null
@@ -1,185 +0,0 @@
-/*
- * 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.quickstep.util;
-
-import static com.android.launcher3.config.FeatureFlags.ENABLE_OVERVIEW_ACTIONS;
-import static com.android.quickstep.SysUINavigationMode.getMode;
-import static com.android.quickstep.SysUINavigationMode.removeShelfFromOverview;
-import static com.android.quickstep.util.LayoutUtils.getDefaultSwipeHeight;
-
-import android.annotation.TargetApi;
-import android.content.Context;
-import android.content.res.Resources;
-import android.graphics.Rect;
-import android.os.Build;
-
-import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.R;
-import com.android.launcher3.util.WindowBounds;
-import com.android.quickstep.SysUINavigationMode.Mode;
-
-/**
- * Utility class to wrap different layout behavior for Launcher and RecentsView
- * TODO: Merge is with {@link com.android.quickstep.BaseActivityInterface} once we remove the
- * state dependent members from {@link com.android.quickstep.LauncherActivityInterface}
- */
-@TargetApi(Build.VERSION_CODES.R)
-public abstract class WindowSizeStrategy {
-
- public final boolean rotationSupportedByActivity;
-
- private WindowSizeStrategy(boolean rotationSupportedByActivity) {
- this.rotationSupportedByActivity = rotationSupportedByActivity;
- }
-
- /**
- * Calculates the taskView size for the provided device configuration
- */
- public final void calculateTaskSize(Context context, DeviceProfile dp, Rect outRect) {
- calculateTaskSize(context, dp, getExtraSpace(context, dp), outRect);
- }
-
- abstract float getExtraSpace(Context context, DeviceProfile dp);
-
- private void calculateTaskSize(
- Context context, DeviceProfile dp, float extraVerticalSpace, Rect outRect) {
- Resources res = context.getResources();
- final boolean showLargeTaskSize = showOverviewActions(context);
-
- final int paddingResId;
- if (dp.isMultiWindowMode) {
- paddingResId = R.dimen.multi_window_task_card_horz_space;
- } else if (dp.isVerticalBarLayout()) {
- paddingResId = R.dimen.landscape_task_card_horz_space;
- } else if (showLargeTaskSize) {
- paddingResId = R.dimen.portrait_task_card_horz_space_big_overview;
- } else {
- paddingResId = R.dimen.portrait_task_card_horz_space;
- }
- float paddingHorz = res.getDimension(paddingResId);
- float paddingVert = showLargeTaskSize
- ? 0 : res.getDimension(R.dimen.task_card_vert_space);
-
- calculateTaskSizeInternal(context, dp, extraVerticalSpace, paddingHorz, paddingVert,
- res.getDimension(R.dimen.task_thumbnail_top_margin), outRect);
- }
-
- private void calculateTaskSizeInternal(Context context, DeviceProfile dp,
- float extraVerticalSpace, float paddingHorz, float paddingVert, float topIconMargin,
- Rect outRect) {
- float taskWidth, taskHeight;
- Rect insets = dp.getInsets();
- if (dp.isMultiWindowMode) {
- WindowBounds bounds = SplitScreenBounds.INSTANCE.getSecondaryWindowBounds(context);
- taskWidth = bounds.availableSize.x;
- taskHeight = bounds.availableSize.y;
- } else {
- taskWidth = dp.availableWidthPx;
- taskHeight = dp.availableHeightPx;
- }
-
- // Note this should be same as dp.availableWidthPx and dp.availableHeightPx unless
- // we override the insets ourselves.
- int launcherVisibleWidth = dp.widthPx - insets.left - insets.right;
- int launcherVisibleHeight = dp.heightPx - insets.top - insets.bottom;
-
- float availableHeight = launcherVisibleHeight
- - topIconMargin - extraVerticalSpace - paddingVert;
- float availableWidth = launcherVisibleWidth - paddingHorz;
-
- float scale = Math.min(availableWidth / taskWidth, availableHeight / taskHeight);
- float outWidth = scale * taskWidth;
- float outHeight = scale * taskHeight;
-
- // Center in the visible space
- float x = insets.left + (launcherVisibleWidth - outWidth) / 2;
- float y = insets.top + Math.max(topIconMargin,
- (launcherVisibleHeight - extraVerticalSpace - outHeight) / 2);
- outRect.set(Math.round(x), Math.round(y),
- Math.round(x) + Math.round(outWidth), Math.round(y) + Math.round(outHeight));
- }
-
- /**
- * Calculates the modal taskView size for the provided device configuration
- */
- public void calculateModalTaskSize(Context context, DeviceProfile dp, Rect outRect) {
- float paddingHorz = context.getResources().getDimension(dp.isMultiWindowMode
- ? R.dimen.multi_window_task_card_horz_space
- : dp.isVerticalBarLayout()
- ? R.dimen.landscape_task_card_horz_space
- : R.dimen.portrait_modal_task_card_horz_space);
- float extraVerticalSpace = getOverviewActionsHeight(context);
- float paddingVert = 0;
- float topIconMargin = 0;
- calculateTaskSizeInternal(context, dp, extraVerticalSpace, paddingHorz, paddingVert,
- topIconMargin, outRect);
- }
-
- /** Gets the space that the overview actions will take, including margins. */
- public float getOverviewActionsHeight(Context context) {
- Resources res = context.getResources();
- float actionsBottomMargin = 0;
- if (getMode(context) == Mode.THREE_BUTTONS) {
- actionsBottomMargin = res.getDimensionPixelSize(
- R.dimen.overview_actions_bottom_margin_three_button);
- } else {
- actionsBottomMargin = res.getDimensionPixelSize(
- R.dimen.overview_actions_bottom_margin_gesture);
- }
- float overviewActionsHeight = actionsBottomMargin
- + res.getDimensionPixelSize(R.dimen.overview_actions_height);
- return overviewActionsHeight;
- }
-
- public static final WindowSizeStrategy LAUNCHER_ACTIVITY_SIZE_STRATEGY =
- new WindowSizeStrategy(true) {
-
- @Override
- float getExtraSpace(Context context, DeviceProfile dp) {
- if (dp.isVerticalBarLayout()) {
- return 0;
- } else {
- Resources res = context.getResources();
- if (showOverviewActions(context)) {
- //TODO: this needs to account for the swipe gesture height and accessibility
- // UI when shown.
- return getOverviewActionsHeight(context);
- } else {
- return getDefaultSwipeHeight(context, dp) + dp.workspacePageIndicatorHeight
- + res.getDimensionPixelSize(
- R.dimen.dynamic_grid_hotseat_extra_vertical_size)
- + res.getDimensionPixelSize(
- R.dimen.dynamic_grid_hotseat_bottom_padding);
- }
- }
- }
- };
-
- public static final WindowSizeStrategy FALLBACK_RECENTS_SIZE_STRATEGY =
- new WindowSizeStrategy(false) {
-
- @Override
- float getExtraSpace(Context context, DeviceProfile dp) {
- return showOverviewActions(context)
- ? context.getResources().getDimensionPixelSize(R.dimen.overview_actions_height)
- : 0;
- }
- };
-
- static boolean showOverviewActions(Context context) {
- return ENABLE_OVERVIEW_ACTIONS.get() && removeShelfFromOverview(context);
- }
-}
diff --git a/res/values/config.xml b/res/values/config.xml
index 603dc91..4cbc597 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -69,6 +69,7 @@
<string name="app_launch_tracker_class" translatable="false"></string>
<string name="test_information_handler_class" translatable="false"></string>
<string name="launcher_activity_logic_class" translatable="false"></string>
+ <string name="prediction_model_class" translatable="false"></string>
<!-- View ID to use for QSB widget -->
<item type="id" name="qsb_widget" />
diff --git a/src/com/android/launcher3/BaseActivity.java b/src/com/android/launcher3/BaseActivity.java
index 7de44a3..7d80d81 100644
--- a/src/com/android/launcher3/BaseActivity.java
+++ b/src/com/android/launcher3/BaseActivity.java
@@ -108,9 +108,14 @@
public static final int ACTIVITY_STATE_USER_ACTIVE = 1 << 4;
/**
+ * State flag indicating if the user will be active shortly.
+ */
+ public static final int ACTIVITY_STATE_USER_WILL_BE_ACTIVE = 1 << 5;
+
+ /**
* State flag indicating that a state transition is in progress
*/
- public static final int ACTIVITY_STATE_TRANSITION_ACTIVE = 1 << 5;
+ public static final int ACTIVITY_STATE_TRANSITION_ACTIVE = 1 << 6;
@Retention(SOURCE)
@IntDef(
@@ -180,6 +185,7 @@
@Override
protected void onResume() {
addActivityFlags(ACTIVITY_STATE_RESUMED | ACTIVITY_STATE_USER_ACTIVE);
+ removeActivityFlags(ACTIVITY_STATE_USER_WILL_BE_ACTIVE);
super.onResume();
}
diff --git a/src/com/android/launcher3/DropTarget.java b/src/com/android/launcher3/DropTarget.java
index 0b0983c..c1aed98 100644
--- a/src/com/android/launcher3/DropTarget.java
+++ b/src/com/android/launcher3/DropTarget.java
@@ -110,6 +110,18 @@
return res;
}
+
+
+ /**
+ * This is used to determine if an object is dropped at a different location than it was
+ * dragged from
+ */
+ public boolean isMoved() {
+ return dragInfo.cellX != originalDragInfo.cellX
+ || dragInfo.cellY != originalDragInfo.cellY
+ || dragInfo.screenId != originalDragInfo.screenId
+ || dragInfo.container != originalDragInfo.container;
+ }
}
/**
diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java
index 14e604d..53e5274 100644
--- a/src/com/android/launcher3/LauncherAppState.java
+++ b/src/com/android/launcher3/LauncherAppState.java
@@ -129,7 +129,7 @@
mIconCache = new IconCache(mContext, mInvariantDeviceProfile, iconCacheFileName);
mWidgetCache = new WidgetPreviewLoader(mContext, mIconCache);
mModel = new LauncherModel(this, mIconCache, AppFilter.newInstance(mContext));
- mPredictionModel = new PredictionModel(mContext);
+ mPredictionModel = PredictionModel.newInstance(mContext);
}
protected void onNotificationSettingsChanged(boolean areNotificationDotsEnabled) {
diff --git a/src/com/android/launcher3/allapps/LauncherAllAppsContainerView.java b/src/com/android/launcher3/allapps/LauncherAllAppsContainerView.java
index 80b6a5a..a6bc6cf 100644
--- a/src/com/android/launcher3/allapps/LauncherAllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/LauncherAllAppsContainerView.java
@@ -60,6 +60,14 @@
}
@Override
+ public boolean onTouchEvent(MotionEvent ev) {
+ if (!mLauncher.isInState(LauncherState.ALL_APPS)) {
+ return false;
+ }
+ return super.onTouchEvent(ev);
+ }
+
+ @Override
public void setInsets(Rect insets) {
super.setInsets(insets);
mLauncher.getAllAppsController()
diff --git a/src/com/android/launcher3/anim/SpringAnimationBuilder.java b/src/com/android/launcher3/anim/SpringAnimationBuilder.java
index 770df03..a9702b4 100644
--- a/src/com/android/launcher3/anim/SpringAnimationBuilder.java
+++ b/src/com/android/launcher3/anim/SpringAnimationBuilder.java
@@ -18,7 +18,6 @@
import static com.android.launcher3.anim.Interpolators.LINEAR;
import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.content.Context;
import android.util.FloatProperty;
@@ -197,9 +196,9 @@
animator.setDuration(getDuration()).setInterpolator(LINEAR);
animator.addUpdateListener(anim ->
property.set(target, getInterpolatedValue(anim.getAnimatedFraction())));
- animator.addListener(new AnimatorListenerAdapter() {
+ animator.addListener(new AnimationSuccessListener() {
@Override
- public void onAnimationEnd(Animator animation) {
+ public void onAnimationSuccess(Animator animation) {
property.set(target, mEndValue);
}
});
diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index 78d194b..6919339 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -110,6 +110,9 @@
public static final BooleanFlag ENABLE_QUICK_CAPTURE_GESTURE = getDebugFlag(
"ENABLE_QUICK_CAPTURE_GESTURE", true, "Swipe from right to left to quick capture");
+ public static final BooleanFlag ENABLE_QUICK_CAPTURE_WINDOW = getDebugFlag(
+ "ENABLE_QUICK_CAPTURE_WINDOW", false, "Use window to host quick capture");
+
public static final BooleanFlag FORCE_LOCAL_OVERSCROLL_PLUGIN = getDebugFlag(
"FORCE_LOCAL_OVERSCROLL_PLUGIN", false,
"Use a launcher-provided OverscrollPlugin if available");
diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java
index 9a36b3e..f7fe535 100644
--- a/src/com/android/launcher3/folder/Folder.java
+++ b/src/com/android/launcher3/folder/Folder.java
@@ -18,23 +18,17 @@
import static android.text.TextUtils.isEmpty;
-import static androidx.core.util.Preconditions.checkNotNull;
-
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.config.FeatureFlags.ALWAYS_USE_HARDWARE_OPTIMIZATION_FOR_FOLDER_ANIMATIONS;
import static com.android.launcher3.logging.LoggerUtils.newContainerTarget;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_FOLDER_LABEL_UPDATED;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ITEM_DROP_COMPLETED;
import static com.android.launcher3.model.data.FolderInfo.FLAG_MANUAL_FOLDER_NAME;
-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 static java.util.Optional.ofNullable;
import android.animation.Animator;
@@ -94,12 +88,6 @@
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.pageindicators.PageIndicatorDots;
-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.LauncherLogProto.Target.ToFolderLabelState;
import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.launcher3.util.Executors;
import com.android.launcher3.util.Thunk;
@@ -111,10 +99,7 @@
import java.util.Comparator;
import java.util.List;
import java.util.Objects;
-import java.util.Optional;
-import java.util.OptionalInt;
import java.util.stream.Collectors;
-import java.util.stream.IntStream;
/**
* Represents a set of icons chosen by the user or generated by the system.
@@ -213,8 +198,7 @@
@Thunk int mScrollHintDir = SCROLL_NONE;
@Thunk int mCurrentScrollDir = SCROLL_NONE;
- private String mPreviousLabel;
- private boolean mIsPreviousLabelSuggested;
+ private StatsLogManager mStatsLogManager;
/**
* Used to inflate the Workspace from XML.
@@ -227,10 +211,12 @@
setAlwaysDrawnWithCacheEnabled(false);
mLauncher = Launcher.getLauncher(context);
+ mStatsLogManager = StatsLogManager.newInstance(context);
// We need this view to be focusable in touch mode so that when text editing of the folder
// name is complete, we have something to focus on, thus hiding the cursor and giving
// reliable behavior when clicking the text field (since it will always gain focus on click).
setFocusableInTouchMode(true);
+
}
@Override
@@ -348,9 +334,9 @@
if (DEBUG) {
Log.d(TAG, "onBackKey newTitle=" + newTitle);
}
-
- mInfo.title = newTitle;
- mInfo.setOption(FLAG_MANUAL_FOLDER_NAME, !getAcceptedSuggestionIndex().isPresent(),
+ mInfo.setTitle(newTitle);
+ mInfo.fromCustom = mInfo.hasOption(FLAG_MANUAL_FOLDER_NAME);
+ mInfo.setOption(FLAG_MANUAL_FOLDER_NAME, !mInfo.getAcceptedSuggestionIndex().isPresent(),
mLauncher.getModelWriter());
mFolderIcon.onTitleChanged(newTitle);
mLauncher.getModelWriter().updateItemInDatabase(mInfo);
@@ -441,8 +427,6 @@
}
mItemsInvalidated = true;
mInfo.addListener(this);
- Optional.ofNullable(mInfo.title).ifPresent(title -> mPreviousLabel = title.toString());
- mIsPreviousLabelSuggested = !mInfo.hasOption(FLAG_MANUAL_FOLDER_NAME);
if (!isEmpty(mInfo.title)) {
mFolderName.setText(mInfo.title);
@@ -1347,10 +1331,8 @@
if (d.stateAnnouncer != null) {
d.stateAnnouncer.completeAction(R.string.item_moved);
}
- StatsLogManager.newInstance(getContext())
- .log(StatsLogManager.LauncherEvent.LAUNCHER_ITEM_DROP_COMPLETED,
- d.logInstanceId,
- d.dragInfo.buildProto(mInfo));
+ mStatsLogManager
+ .log(LAUNCHER_ITEM_DROP_COMPLETED, d.logInstanceId, d.dragInfo.buildProto(mInfo));
}
// This is used so the item doesn't immediately appear in the folder when added. In one case
@@ -1455,7 +1437,8 @@
if (hasFocus) {
startEditingFolderName();
} else {
- logCurrentFolderLabelState();
+ mStatsLogManager.log(LAUNCHER_FOLDER_LABEL_UPDATED, mInfo.buildProto());
+ logFolderLabelState();
mFolderName.dispatchBackKey();
}
}
@@ -1654,147 +1637,14 @@
return mContent;
}
- protected void logCurrentFolderLabelState() {
- 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");
- if (newLabel.equals(mPreviousLabel)) {
- return Target.ToFolderLabelState.UNCHANGED;
- }
-
- if (!FeatureFlags.FOLDER_NAME_SUGGEST.get()) {
- return newLabel.isEmpty()
- ? ToFolderLabelState.TO_EMPTY_WITH_SUGGESTIONS_DISABLED
- : ToFolderLabelState.TO_CUSTOM_WITH_SUGGESTIONS_DISABLED;
- }
-
- Optional<String[]> suggestedLabels = getSuggestedLabels();
- boolean isEmptySuggestions = suggestedLabels
- .map(labels -> stream(labels).allMatch(TextUtils::isEmpty))
- .orElse(true);
- if (isEmptySuggestions) {
- return newLabel.isEmpty()
- ? ToFolderLabelState.TO_EMPTY_WITH_EMPTY_SUGGESTIONS
- : ToFolderLabelState.TO_CUSTOM_WITH_EMPTY_SUGGESTIONS;
- }
-
- boolean hasValidPrimary = suggestedLabels
- .map(labels -> !isEmpty(labels[0]))
- .orElse(false);
- if (newLabel.isEmpty()) {
- return hasValidPrimary ? ToFolderLabelState.TO_EMPTY_WITH_VALID_PRIMARY
- : ToFolderLabelState.TO_EMPTY_WITH_VALID_SUGGESTIONS_AND_EMPTY_PRIMARY;
- }
-
- OptionalInt accepted_suggestion_index = getAcceptedSuggestionIndex();
- if (!accepted_suggestion_index.isPresent()) {
- return hasValidPrimary ? ToFolderLabelState.TO_CUSTOM_WITH_VALID_PRIMARY
- : ToFolderLabelState.TO_CUSTOM_WITH_VALID_SUGGESTIONS_AND_EMPTY_PRIMARY;
- }
-
- switch (accepted_suggestion_index.getAsInt()) {
- case 0:
- return ToFolderLabelState.TO_SUGGESTION0_WITH_VALID_PRIMARY;
- case 1:
- return hasValidPrimary ? ToFolderLabelState.TO_SUGGESTION1_WITH_VALID_PRIMARY
- : ToFolderLabelState.TO_SUGGESTION1_WITH_EMPTY_PRIMARY;
- case 2:
- return hasValidPrimary ? ToFolderLabelState.TO_SUGGESTION2_WITH_VALID_PRIMARY
- : ToFolderLabelState.TO_SUGGESTION2_WITH_EMPTY_PRIMARY;
- case 3:
- return hasValidPrimary ? ToFolderLabelState.TO_SUGGESTION3_WITH_VALID_PRIMARY
- : ToFolderLabelState.TO_SUGGESTION3_WITH_EMPTY_PRIMARY;
- default:
- // fall through
- }
- return ToFolderLabelState.TO_FOLDER_LABEL_STATE_UNSPECIFIED;
-
- }
-
- private Optional<String[]> getSuggestedLabels() {
- return ofNullable(mInfo)
- .map(info -> info.suggestedFolderNames)
- .map(
- folderNames ->
- (FolderNameInfo[])
- folderNames.getParcelableArrayExtra(FolderInfo.EXTRA_FOLDER_SUGGESTIONS))
- .map(
- folderNameInfoArray ->
- stream(folderNameInfoArray)
- .filter(Objects::nonNull)
- .map(FolderNameInfo::getLabel)
- .filter(Objects::nonNull)
- .map(CharSequence::toString)
- .toArray(String[]::new));
- }
-
- private OptionalInt getAcceptedSuggestionIndex() {
- String newLabel = checkNotNull(mFolderName.getText().toString(),
- "Expected valid folder label, but found null");
- return getSuggestedLabels()
- .map(suggestionsArray ->
- IntStream.range(0, suggestionsArray.length)
- .filter(
- index -> !isEmpty(suggestionsArray[index])
- && newLabel.equalsIgnoreCase(suggestionsArray[index]))
- .sequential()
- .findFirst()
- ).orElse(OptionalInt.empty());
-
- }
-
-
- 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));
- }
+ /**
+ * Logs current folder label info.
+ *
+ * @deprecated This method is only used for log validation and soon will be removed.
+ */
+ @Deprecated
+ public void logFolderLabelState() {
+ mLauncher.getUserEventDispatcher()
+ .logLauncherEvent(mInfo.getFolderLabelStateLauncherEvent());
}
}
diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java
index 93208d4..153d6bc 100644
--- a/src/com/android/launcher3/folder/FolderIcon.java
+++ b/src/com/android/launcher3/folder/FolderIcon.java
@@ -20,6 +20,7 @@
import static com.android.launcher3.folder.ClippedFolderIconLayoutRule.MAX_NUM_ITEMS_IN_PREVIEW;
import static com.android.launcher3.folder.PreviewItemManager.INITIAL_ITEM_ANIMATION_DURATION;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_FOLDER_LABEL_UPDATED;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -62,6 +63,8 @@
import com.android.launcher3.dragndrop.DragView;
import com.android.launcher3.dragndrop.DraggableView;
import com.android.launcher3.icons.DotRenderer;
+import com.android.launcher3.logging.InstanceId;
+import com.android.launcher3.logging.StatsLogManager;
import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.model.data.FolderInfo;
import com.android.launcher3.model.data.FolderInfo.FolderListener;
@@ -410,10 +413,10 @@
Executors.UI_HELPER_EXECUTOR.post(() -> {
d.folderNameProvider.getSuggestedFolderName(
getContext(), mInfo.contents, nameInfos);
- showFinalView(finalIndex, item, nameInfos);
+ showFinalView(finalIndex, item, nameInfos, d.logInstanceId);
});
} else {
- showFinalView(finalIndex, item, nameInfos);
+ showFinalView(finalIndex, item, nameInfos, d.logInstanceId);
}
} else {
addItem(item);
@@ -421,12 +424,12 @@
}
private void showFinalView(int finalIndex, final WorkspaceItemInfo item,
- FolderNameInfo[] nameInfos) {
+ FolderNameInfo[] nameInfos, InstanceId instanceId) {
postDelayed(() -> {
mPreviewItemManager.hidePreviewItem(finalIndex, false);
mFolder.showItem(item);
- setLabelSuggestion(nameInfos);
- mFolder.logCurrentFolderLabelState();
+ setLabelSuggestion(nameInfos, instanceId);
+ mFolder.logFolderLabelState();
invalidate();
}, DROP_IN_ANIMATION_DURATION);
}
@@ -434,7 +437,7 @@
/**
* Set the suggested folder name.
*/
- public void setLabelSuggestion(FolderNameInfo[] nameInfos) {
+ public void setLabelSuggestion(FolderNameInfo[] nameInfos, InstanceId instanceId) {
if (!FeatureFlags.FOLDER_NAME_SUGGEST.get()) {
return;
}
@@ -445,7 +448,9 @@
if (nameInfos == null || nameInfos[0] == null || isEmpty(nameInfos[0].getLabel())) {
return;
}
- mInfo.title = nameInfos[0].getLabel();
+ mInfo.setTitle(nameInfos[0].getLabel());
+ StatsLogManager.newInstance(getContext())
+ .log(LAUNCHER_FOLDER_LABEL_UPDATED, instanceId, mInfo.buildProto());
onTitleChanged(mInfo.title);
mFolder.mFolderName.setText(mInfo.title);
mFolder.mLauncher.getModelWriter().updateItemInDatabase(mInfo);
diff --git a/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java b/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java
index c62f308..350f221 100644
--- a/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java
+++ b/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java
@@ -81,10 +81,12 @@
binderDied();
}
+ SurfaceControlViewHost.SurfacePackage surfacePackage;
try {
mSurfaceControlViewHost = MAIN_EXECUTOR
.submit(() -> new SurfaceControlViewHost(mContext, mDisplay, mHostToken))
.get(5, TimeUnit.SECONDS);
+ surfacePackage = mSurfaceControlViewHost.getSurfacePackage();
mHostToken.linkToDeath(this, 0);
} catch (Exception e) {
e.printStackTrace();
@@ -92,6 +94,14 @@
}
MAIN_EXECUTOR.execute(() -> {
+ // If mSurfaceControlViewHost is null due to any reason (e.g. binder died,
+ // happening when user leaves the preview screen before preview rendering finishes),
+ // we should return here.
+ SurfaceControlViewHost host = mSurfaceControlViewHost;
+ if (host == null) {
+ return;
+ }
+
View view = new LauncherPreviewRenderer(mContext, mIdp).getRenderedView();
// This aspect scales the view to fit in the surface and centers it
final float scale = Math.min(mWidth / (float) view.getMeasuredWidth(),
@@ -107,14 +117,14 @@
.setInterpolator(new AccelerateDecelerateInterpolator())
.setDuration(FADE_IN_ANIMATION_DURATION)
.start();
- mSurfaceControlViewHost.setView(view, view.getMeasuredWidth(),
+ host.setView(view, view.getMeasuredWidth(),
view.getMeasuredHeight());
});
Bundle result = new Bundle();
- result.putParcelable(KEY_SURFACE_PACKAGE, mSurfaceControlViewHost.getSurfacePackage());
+ result.putParcelable(KEY_SURFACE_PACKAGE, surfacePackage);
- Handler handler = new Handler(Looper.getMainLooper(), Loopermessage -> {
+ Handler handler = new Handler(Looper.getMainLooper(), message -> {
binderDied();
return true;
});
@@ -128,8 +138,10 @@
@Override
public void binderDied() {
if (mSurfaceControlViewHost != null) {
- mSurfaceControlViewHost.release();
- mSurfaceControlViewHost = null;
+ MAIN_EXECUTOR.execute(() -> {
+ mSurfaceControlViewHost.release();
+ mSurfaceControlViewHost = null;
+ });
}
mHostToken.unlinkToDeath(this, 0);
}
diff --git a/src/com/android/launcher3/logging/StatsLogManager.java b/src/com/android/launcher3/logging/StatsLogManager.java
index 9455bd3..b240f0b 100644
--- a/src/com/android/launcher3/logging/StatsLogManager.java
+++ b/src/com/android/launcher3/logging/StatsLogManager.java
@@ -16,10 +16,8 @@
package com.android.launcher3.logging;
import android.content.Context;
-import android.util.Log;
import com.android.launcher3.R;
-import com.android.launcher3.logger.LauncherAtom;
import com.android.launcher3.logger.LauncherAtom.ItemInfo;
import com.android.launcher3.logging.StatsLogUtils.LogStateProvider;
import com.android.launcher3.util.ResourceBasedOverride;
@@ -32,8 +30,6 @@
*/
public class StatsLogManager implements ResourceBasedOverride {
- private static final String TAG = "StatsLogManager";
-
interface EventEnum {
int getId();
}
@@ -61,6 +57,10 @@
+ "resulting in a new folder creation")
LAUNCHER_ITEM_DROP_FOLDER_CREATED(386),
+ @LauncherUiEvent(doc = "User action resulted in or manually updated the folder label to "
+ + "new/same value.")
+ LAUNCHER_FOLDER_LABEL_UPDATED(460),
+
@LauncherUiEvent(doc = "A dragged item is dropped on 'Remove' button in the target bar")
LAUNCHER_ITEM_DROPPED_ON_REMOVE(465),
@@ -84,9 +84,11 @@
// ADD MORE
private final int mId;
+
LauncherEvent(int id) {
mId = id;
}
+
public int getId() {
return mId;
}
@@ -109,22 +111,32 @@
}
/**
- * Logs an event and accompanying {@link ItemInfo}
+ * Logs a {@link LauncherEvent}.
*/
- public void log(LauncherEvent event, InstanceId instanceId) {
- Log.d(TAG, String.format("%s(InstanceId:%s)", event.name(), instanceId));
- // Call StatsLog method
+ public void log(LauncherEvent event) {
}
/**
- * Logs an event and accompanying {@link LauncherAtom.ItemInfo}
+ * Logs an event and accompanying {@link InstanceId}.
*/
- public void log(LauncherEvent event, InstanceId instanceId, LauncherAtom.ItemInfo itemInfo) { }
- public void log(LauncherEvent event, LauncherAtom.ItemInfo itemInfo) { }
+ public void log(LauncherEvent event, InstanceId instanceId) {
+ }
+ /**
+ * Logs an event and accompanying {@link ItemInfo}.
+ */
+ public void log(LauncherEvent event, ItemInfo itemInfo) {
+ }
+
+ /**
+ * Logs an event and accompanying {@link InstanceId} and {@link ItemInfo}.
+ */
+ public void log(LauncherEvent event, InstanceId instanceId, ItemInfo itemInfo) {
+ }
/**
* Logs snapshot, or impression of the current workspace.
*/
- public void logSnapshot() { }
+ public void logSnapshot() {
+ }
}
diff --git a/src/com/android/launcher3/model/BaseLoaderResults.java b/src/com/android/launcher3/model/BaseLoaderResults.java
index 1465100..ab921ea 100644
--- a/src/com/android/launcher3/model/BaseLoaderResults.java
+++ b/src/com/android/launcher3/model/BaseLoaderResults.java
@@ -253,8 +253,8 @@
}
private void bindPredictedItems(IntArray ranks, final Executor executor) {
- executeCallbacksTask(
- c -> c.bindPredictedItems(mBgDataModel.cachedPredictedItems, ranks), executor);
+ ArrayList<AppInfo> items = new ArrayList<>(mBgDataModel.cachedPredictedItems);
+ executeCallbacksTask(c -> c.bindPredictedItems(items, ranks), executor);
}
protected void executeCallbacksTask(CallbackTask task, Executor executor) {
diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java
index 9e6282e..d05d70b 100644
--- a/src/com/android/launcher3/model/LoaderTask.java
+++ b/src/com/android/launcher3/model/LoaderTask.java
@@ -45,6 +45,8 @@
import android.util.MutableInt;
import android.util.TimingLogger;
+import androidx.annotation.WorkerThread;
+
import com.android.launcher3.InstallShortcutReceiver;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherModel;
@@ -850,12 +852,11 @@
}
}
- private List<AppInfo> loadCachedPredictions() {
+ @WorkerThread
+ private void loadCachedPredictions() {
synchronized (mBgDataModel) {
List<ComponentKey> componentKeys =
mApp.getPredictionModel().getPredictionComponentKeys();
- List<AppInfo> results = new ArrayList<>();
- if (componentKeys == null) return results;
List<LauncherActivityInfo> l;
mBgDataModel.cachedPredictedItems.clear();
for (ComponentKey key : componentKeys) {
@@ -866,7 +867,6 @@
mBgDataModel.cachedPredictedItems.add(info);
mIconCache.getTitleAndIcon(info, false);
}
- return results;
}
}
diff --git a/src/com/android/launcher3/model/PredictionModel.java b/src/com/android/launcher3/model/PredictionModel.java
index 6aa41eb..f8140eb 100644
--- a/src/com/android/launcher3/model/PredictionModel.java
+++ b/src/com/android/launcher3/model/PredictionModel.java
@@ -14,60 +14,86 @@
* limitations under the License.
*/
package com.android.launcher3.model;
+import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
+import android.content.ComponentName;
import android.content.Context;
import android.content.SharedPreferences;
import android.os.UserHandle;
+import androidx.annotation.AnyThread;
+import androidx.annotation.WorkerThread;
+
+import com.android.launcher3.R;
import com.android.launcher3.Utilities;
-import com.android.launcher3.model.data.AppInfo;
+import com.android.launcher3.pm.UserCache;
import com.android.launcher3.util.ComponentKey;
+import com.android.launcher3.util.Preconditions;
+import com.android.launcher3.util.ResourceBasedOverride;
import java.util.ArrayList;
import java.util.List;
-import java.util.stream.Collectors;
/**
- * Model helper for app predictions in workspace
+ * Model Helper for app predictions
*/
-public class PredictionModel {
+public class PredictionModel implements ResourceBasedOverride {
+
private static final String CACHED_ITEMS_KEY = "predicted_item_keys";
private static final int MAX_CACHE_ITEMS = 5;
- private final Context mContext;
- private final SharedPreferences mDevicePrefs;
+ protected Context mContext;
private ArrayList<ComponentKey> mCachedComponentKeys;
+ private SharedPreferences mDevicePrefs;
+ private UserCache mUserCache;
- public PredictionModel(Context context) {
- mContext = context;
- mDevicePrefs = Utilities.getDevicePrefs(mContext);
+
+ /**
+ * Retrieve instance of this object that can be overridden in runtime based on the build
+ * variant of the application.
+ */
+ public static PredictionModel newInstance(Context context) {
+ PredictionModel model = Overrides.getObject(PredictionModel.class, context,
+ R.string.prediction_model_class);
+ model.init(context);
+ return model;
}
+ protected void init(Context context) {
+ mContext = context;
+ mDevicePrefs = Utilities.getDevicePrefs(mContext);
+ mUserCache = UserCache.INSTANCE.get(mContext);
+
+ }
/**
* Formats and stores a list of component key in device preferences.
*/
+ @AnyThread
public void cachePredictionComponentKeys(List<ComponentKey> componentKeys) {
- StringBuilder builder = new StringBuilder();
- int count = Math.min(componentKeys.size(), MAX_CACHE_ITEMS);
- for (int i = 0; i < count; i++) {
- builder.append(componentKeys.get(i));
- builder.append("\n");
- }
- mDevicePrefs.edit().putString(CACHED_ITEMS_KEY, builder.toString()).apply();
- mCachedComponentKeys = null;
+ MODEL_EXECUTOR.execute(() -> {
+ StringBuilder builder = new StringBuilder();
+ int count = Math.min(componentKeys.size(), MAX_CACHE_ITEMS);
+ for (int i = 0; i < count; i++) {
+ builder.append(serializeComponentKeyToString(componentKeys.get(i)));
+ builder.append("\n");
+ }
+ mDevicePrefs.edit().putString(CACHED_ITEMS_KEY, builder.toString()).apply();
+ mCachedComponentKeys = null;
+ });
}
/**
* parses and returns ComponentKeys saved by
* {@link PredictionModel#cachePredictionComponentKeys(List)}
*/
+ @WorkerThread
public List<ComponentKey> getPredictionComponentKeys() {
+ Preconditions.assertWorkerThread();
if (mCachedComponentKeys == null) {
mCachedComponentKeys = new ArrayList<>();
-
String cachedBlob = mDevicePrefs.getString(CACHED_ITEMS_KEY, "");
for (String line : cachedBlob.split("\n")) {
- ComponentKey key = ComponentKey.fromString(line);
+ ComponentKey key = getComponentKeyFromSerializedString(line);
if (key != null) {
mCachedComponentKeys.add(key);
}
@@ -76,18 +102,26 @@
return mCachedComponentKeys;
}
- /**
- * Remove uninstalled applications from model
- */
- public void removePackage(String pkgName, UserHandle user, ArrayList<AppInfo> ids) {
- for (int i = ids.size() - 1; i >= 0; i--) {
- AppInfo info = ids.get(i);
- if (info.user.equals(user) && pkgName.equals(info.componentName.getPackageName())) {
- ids.remove(i);
- }
+ private String serializeComponentKeyToString(ComponentKey componentKey) {
+ long userSerialNumber = mUserCache.getSerialNumberForUser(componentKey.user);
+ return componentKey.componentName.flattenToString() + "#" + userSerialNumber;
+ }
+
+ private ComponentKey getComponentKeyFromSerializedString(String str) {
+ int sep = str.indexOf('#');
+ if (sep < 0 || (sep + 1) >= str.length()) {
+ return null;
}
- cachePredictionComponentKeys(getPredictionComponentKeys().stream()
- .filter(cn -> !(cn.user.equals(user) && cn.componentName.getPackageName().equals(
- pkgName))).collect(Collectors.toList()));
+ ComponentName componentName = ComponentName.unflattenFromString(str.substring(0, sep));
+ if (componentName == null) {
+ return null;
+ }
+ try {
+ long serialNumber = Long.parseLong(str.substring(sep + 1));
+ UserHandle userHandle = mUserCache.getUserForSerialNumber(serialNumber);
+ return userHandle != null ? new ComponentKey(componentName, userHandle) : null;
+ } catch (NumberFormatException ex) {
+ return null;
+ }
}
}
diff --git a/src/com/android/launcher3/model/data/FolderInfo.java b/src/com/android/launcher3/model/data/FolderInfo.java
index 3ac6a22..096743a 100644
--- a/src/com/android/launcher3/model/data/FolderInfo.java
+++ b/src/com/android/launcher3/model/data/FolderInfo.java
@@ -16,16 +16,45 @@
package com.android.launcher3.model.data;
+import static android.text.TextUtils.isEmpty;
+
+import static androidx.core.util.Preconditions.checkNotNull;
+
+import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_DESKTOP;
+import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT;
+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.stream;
+import static java.util.Optional.ofNullable;
+
import android.content.Intent;
import android.os.Process;
+import android.text.TextUtils;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.Utilities;
+import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.folder.FolderNameInfo;
import com.android.launcher3.logger.LauncherAtom;
+import com.android.launcher3.logger.LauncherAtom.FromState;
+import com.android.launcher3.logger.LauncherAtom.ToState;
import com.android.launcher3.model.ModelWriter;
+import com.android.launcher3.userevent.LauncherLogProto;
+import com.android.launcher3.userevent.LauncherLogProto.Target;
+import com.android.launcher3.userevent.LauncherLogProto.Target.FromFolderLabelState;
+import com.android.launcher3.userevent.LauncherLogProto.Target.ToFolderLabelState;
import com.android.launcher3.util.ContentWriter;
import java.util.ArrayList;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.OptionalInt;
+import java.util.StringJoiner;
+import java.util.stream.IntStream;
+
/**
* Represents a folder containing shortcuts or apps.
@@ -57,6 +86,20 @@
public Intent suggestedFolderNames;
+ // Represents the title before current.
+ // Primarily used for logging purpose.
+ private CharSequence mPreviousTitle;
+
+ // True if the title before was manually entered, suggested otherwise.
+ // Primarily used for logging purpose.
+ public boolean fromCustom;
+
+ /**
+ * Used for separating {@link #mPreviousTitle} and {@link #title} when concatenating them
+ * for logging.
+ */
+ private static final CharSequence FOLDER_LABEL_DELIMITER = "=>";
+
/**
* The apps and shortcuts
*/
@@ -160,9 +203,20 @@
@Override
public LauncherAtom.ItemInfo buildProto(FolderInfo fInfo) {
return getDefaultItemInfoBuilder()
- .setFolderIcon(LauncherAtom.FolderIcon.newBuilder().setCardinality(contents.size()))
- .setContainerInfo(getContainerInfo())
- .build();
+ .setFolderIcon(LauncherAtom.FolderIcon.newBuilder().setCardinality(contents.size()))
+ .setRank(rank)
+ .setContainerInfo(getContainerInfo())
+ .build();
+ }
+
+ @Override
+ public void setTitle(CharSequence title) {
+ mPreviousTitle = this.title;
+ this.title = title;
+ }
+
+ public CharSequence getPreviousTitle() {
+ return mPreviousTitle;
}
@Override
@@ -172,4 +226,244 @@
folderInfo.contents = this.contents;
return folderInfo;
}
+
+ /**
+ * Returns {@link LauncherAtom.FolderIcon} wrapped as {@link LauncherAtom.ItemInfo} for logging.
+ */
+ @Override
+ public LauncherAtom.ItemInfo buildProto() {
+ FromState fromFolderLabelState = getFromFolderLabelState();
+ ToState toFolderLabelState = getToFolderLabelState();
+ LauncherAtom.FolderIcon.Builder folderIconBuilder = LauncherAtom.FolderIcon.newBuilder()
+ .setCardinality(contents.size())
+ .setFromLabelState(fromFolderLabelState)
+ .setToLabelState(toFolderLabelState);
+
+ // If the folder label is suggested, it is logged to improve prediction model.
+ // When both old and new labels are logged together delimiter is used.
+ StringJoiner labelInfoBuilder = new StringJoiner(FOLDER_LABEL_DELIMITER);
+ if (fromFolderLabelState.equals(FromState.FROM_SUGGESTED)) {
+ labelInfoBuilder.add(mPreviousTitle);
+ }
+ if (toFolderLabelState.toString().startsWith("TO_SUGGESTION")) {
+ labelInfoBuilder.add(title);
+ }
+ if (labelInfoBuilder.length() > 0) {
+ folderIconBuilder.setLabelInfo(labelInfoBuilder.toString());
+ }
+
+ return getDefaultItemInfoBuilder()
+ .setFolderIcon(folderIconBuilder)
+ .setContainerInfo(getContainerInfo())
+ .build();
+ }
+
+ /**
+ * Returns index of the accepted suggestion.
+ */
+ public OptionalInt getAcceptedSuggestionIndex() {
+ String newLabel = checkNotNull(title,
+ "Expected valid folder label, but found null").toString();
+ return getSuggestedLabels()
+ .map(suggestionsArray ->
+ IntStream.range(0, suggestionsArray.length)
+ .filter(
+ index -> !isEmpty(suggestionsArray[index])
+ && newLabel.equalsIgnoreCase(
+ suggestionsArray[index]))
+ .sequential()
+ .findFirst()
+ ).orElse(OptionalInt.empty());
+
+ }
+
+ private LauncherAtom.ToState getToFolderLabelState() {
+ if (title == null) {
+ return LauncherAtom.ToState.TO_STATE_UNSPECIFIED;
+ }
+
+ if (title.equals(mPreviousTitle)) {
+ return LauncherAtom.ToState.UNCHANGED;
+ }
+
+ if (!FeatureFlags.FOLDER_NAME_SUGGEST.get()) {
+ return title.length() > 0
+ ? LauncherAtom.ToState.TO_CUSTOM_WITH_SUGGESTIONS_DISABLED
+ : LauncherAtom.ToState.TO_EMPTY_WITH_SUGGESTIONS_DISABLED;
+ }
+
+ Optional<String[]> suggestedLabels = getSuggestedLabels();
+ boolean isEmptySuggestions = suggestedLabels
+ .map(labels -> stream(labels).allMatch(TextUtils::isEmpty))
+ .orElse(true);
+ if (isEmptySuggestions) {
+ return title.length() > 0
+ ? LauncherAtom.ToState.TO_CUSTOM_WITH_EMPTY_SUGGESTIONS
+ : LauncherAtom.ToState.TO_EMPTY_WITH_EMPTY_SUGGESTIONS;
+ }
+
+ boolean hasValidPrimary = suggestedLabels
+ .map(labels -> !isEmpty(labels[0]))
+ .orElse(false);
+ if (title.length() == 0) {
+ return hasValidPrimary ? LauncherAtom.ToState.TO_EMPTY_WITH_VALID_PRIMARY
+ : LauncherAtom.ToState.TO_EMPTY_WITH_VALID_SUGGESTIONS_AND_EMPTY_PRIMARY;
+ }
+
+ OptionalInt accepted_suggestion_index = getAcceptedSuggestionIndex();
+ if (!accepted_suggestion_index.isPresent()) {
+ return hasValidPrimary ? LauncherAtom.ToState.TO_CUSTOM_WITH_VALID_PRIMARY
+ : LauncherAtom.ToState.TO_CUSTOM_WITH_VALID_SUGGESTIONS_AND_EMPTY_PRIMARY;
+ }
+
+ switch (accepted_suggestion_index.getAsInt()) {
+ case 0:
+ return LauncherAtom.ToState.TO_SUGGESTION0;
+ case 1:
+ return hasValidPrimary ? LauncherAtom.ToState.TO_SUGGESTION1_WITH_VALID_PRIMARY
+ : LauncherAtom.ToState.TO_SUGGESTION1_WITH_EMPTY_PRIMARY;
+ case 2:
+ return hasValidPrimary ? LauncherAtom.ToState.TO_SUGGESTION2_WITH_VALID_PRIMARY
+ : LauncherAtom.ToState.TO_SUGGESTION2_WITH_EMPTY_PRIMARY;
+ case 3:
+ return hasValidPrimary ? LauncherAtom.ToState.TO_SUGGESTION3_WITH_VALID_PRIMARY
+ : LauncherAtom.ToState.TO_SUGGESTION3_WITH_EMPTY_PRIMARY;
+ default:
+ // fall through
+ }
+ return LauncherAtom.ToState.TO_STATE_UNSPECIFIED;
+
+ }
+
+ private LauncherAtom.FromState getFromFolderLabelState() {
+ return mPreviousTitle == null
+ ? LauncherAtom.FromState.FROM_STATE_UNSPECIFIED
+ : mPreviousTitle.length() == 0
+ ? LauncherAtom.FromState.FROM_EMPTY
+ : fromCustom
+ ? LauncherAtom.FromState.FROM_CUSTOM
+ : LauncherAtom.FromState.FROM_SUGGESTED;
+ }
+
+ private Optional<String[]> getSuggestedLabels() {
+ return ofNullable(suggestedFolderNames)
+ .map(folderNames ->
+ (FolderNameInfo[])
+ folderNames.getParcelableArrayExtra(EXTRA_FOLDER_SUGGESTIONS))
+ .map(folderNameInfoArray ->
+ stream(folderNameInfoArray)
+ .filter(Objects::nonNull)
+ .map(FolderNameInfo::getLabel)
+ .filter(Objects::nonNull)
+ .map(CharSequence::toString)
+ .toArray(String[]::new));
+ }
+
+ /**
+ * Returns {@link LauncherLogProto.LauncherEvent} to log current folder label info.
+ *
+ * @deprecated This method is used only for validation purpose and soon will be removed.
+ */
+ @Deprecated
+ public LauncherLogProto.LauncherEvent getFolderLabelStateLauncherEvent() {
+ return LauncherLogProto.LauncherEvent.newBuilder()
+ .setAction(LauncherLogProto.Action
+ .newBuilder()
+ .setType(LauncherLogProto.Action.Type.SOFT_KEYBOARD))
+ .addSrcTarget(Target
+ .newBuilder()
+ .setType(Target.Type.ITEM)
+ .setItemType(LauncherLogProto.ItemType.EDITTEXT)
+ .setFromFolderLabelState(convertFolderLabelState(getFromFolderLabelState()))
+ .setToFolderLabelState(convertFolderLabelState(getToFolderLabelState())))
+ .addSrcTarget(Target.newBuilder()
+ .setType(Target.Type.CONTAINER)
+ .setContainerType(LauncherLogProto.ContainerType.FOLDER)
+ .setPageIndex(screenId)
+ .setGridX(cellX)
+ .setGridY(cellY)
+ .setCardinality(contents.size()))
+ .addSrcTarget(newParentContainerTarget())
+ .build();
+ }
+
+ /**
+ * @deprecated This method is used only for validation purpose and soon will be removed.
+ */
+ @Deprecated
+ private Target.Builder newParentContainerTarget() {
+ Target.Builder builder = Target.newBuilder().setType(Target.Type.CONTAINER);
+ switch (container) {
+ case CONTAINER_HOTSEAT:
+ return builder.setContainerType(LauncherLogProto.ContainerType.HOTSEAT);
+ case CONTAINER_DESKTOP:
+ return builder.setContainerType(LauncherLogProto.ContainerType.WORKSPACE);
+ default:
+ throw new AssertionError(String
+ .format("Expected container to be either %s or %s but found %s.",
+ CONTAINER_HOTSEAT,
+ CONTAINER_DESKTOP,
+ container));
+ }
+ }
+
+ /**
+ * @deprecated This method is used only for validation purpose and soon will be removed.
+ */
+ @Deprecated
+ private static FromFolderLabelState convertFolderLabelState(FromState fromState) {
+ switch (fromState) {
+ case FROM_EMPTY:
+ return FROM_EMPTY;
+ case FROM_SUGGESTED:
+ return FROM_SUGGESTED;
+ case FROM_CUSTOM:
+ return FROM_CUSTOM;
+ default:
+ return FROM_FOLDER_LABEL_STATE_UNSPECIFIED;
+ }
+ }
+
+ /**
+ * @deprecated This method is used only for validation purpose and soon will be removed.
+ */
+ @Deprecated
+ private static ToFolderLabelState convertFolderLabelState(ToState toState) {
+ switch (toState) {
+ case UNCHANGED:
+ return ToFolderLabelState.UNCHANGED;
+ case TO_SUGGESTION0:
+ return ToFolderLabelState.TO_SUGGESTION0_WITH_VALID_PRIMARY;
+ case TO_SUGGESTION1_WITH_VALID_PRIMARY:
+ return ToFolderLabelState.TO_SUGGESTION1_WITH_VALID_PRIMARY;
+ case TO_SUGGESTION1_WITH_EMPTY_PRIMARY:
+ return ToFolderLabelState.TO_SUGGESTION1_WITH_EMPTY_PRIMARY;
+ case TO_SUGGESTION2_WITH_VALID_PRIMARY:
+ return ToFolderLabelState.TO_SUGGESTION2_WITH_VALID_PRIMARY;
+ case TO_SUGGESTION2_WITH_EMPTY_PRIMARY:
+ return ToFolderLabelState.TO_SUGGESTION2_WITH_EMPTY_PRIMARY;
+ case TO_SUGGESTION3_WITH_VALID_PRIMARY:
+ return ToFolderLabelState.TO_SUGGESTION3_WITH_VALID_PRIMARY;
+ case TO_SUGGESTION3_WITH_EMPTY_PRIMARY:
+ return ToFolderLabelState.TO_SUGGESTION3_WITH_EMPTY_PRIMARY;
+ case TO_EMPTY_WITH_VALID_PRIMARY:
+ return ToFolderLabelState.TO_EMPTY_WITH_VALID_PRIMARY;
+ case TO_EMPTY_WITH_VALID_SUGGESTIONS_AND_EMPTY_PRIMARY:
+ return ToFolderLabelState.TO_EMPTY_WITH_VALID_SUGGESTIONS_AND_EMPTY_PRIMARY;
+ case TO_EMPTY_WITH_EMPTY_SUGGESTIONS:
+ return ToFolderLabelState.TO_EMPTY_WITH_EMPTY_SUGGESTIONS;
+ case TO_EMPTY_WITH_SUGGESTIONS_DISABLED:
+ return ToFolderLabelState.TO_EMPTY_WITH_SUGGESTIONS_DISABLED;
+ case TO_CUSTOM_WITH_VALID_PRIMARY:
+ return ToFolderLabelState.TO_CUSTOM_WITH_VALID_PRIMARY;
+ case TO_CUSTOM_WITH_VALID_SUGGESTIONS_AND_EMPTY_PRIMARY:
+ return ToFolderLabelState.TO_CUSTOM_WITH_VALID_SUGGESTIONS_AND_EMPTY_PRIMARY;
+ case TO_CUSTOM_WITH_EMPTY_SUGGESTIONS:
+ return ToFolderLabelState.TO_CUSTOM_WITH_EMPTY_SUGGESTIONS;
+ case TO_CUSTOM_WITH_SUGGESTIONS_DISABLED:
+ return ToFolderLabelState.TO_CUSTOM_WITH_SUGGESTIONS_DISABLED;
+ default:
+ return ToFolderLabelState.TO_FOLDER_LABEL_STATE_UNSPECIFIED;
+ }
+ }
}
diff --git a/src/com/android/launcher3/model/data/ItemInfo.java b/src/com/android/launcher3/model/data/ItemInfo.java
index 7611ee7..f2b7e54 100644
--- a/src/com/android/launcher3/model/data/ItemInfo.java
+++ b/src/com/android/launcher3/model/data/ItemInfo.java
@@ -252,6 +252,13 @@
/**
* Creates {@link LauncherAtom.ItemInfo} with important fields and parent container info.
*/
+ public LauncherAtom.ItemInfo buildProto() {
+ return buildProto(null);
+ }
+
+ /**
+ * Creates {@link LauncherAtom.ItemInfo} with important fields and parent container info.
+ */
public LauncherAtom.ItemInfo buildProto(FolderInfo fInfo) {
LauncherAtom.ItemInfo.Builder itemBuilder = getDefaultItemInfoBuilder();
Optional<ComponentName> nullableComponent = Optional.ofNullable(getTargetComponent());
@@ -345,4 +352,8 @@
itemInfo.copyFrom(this);
return itemInfo;
}
+
+ public void setTitle(CharSequence title) {
+ this.title = title;
+ }
}
diff --git a/src_plugins/com/android/systemui/plugins/OverscrollPlugin.java b/src_plugins/com/android/systemui/plugins/OverscrollPlugin.java
index 28a9193..a434d07 100644
--- a/src_plugins/com/android/systemui/plugins/OverscrollPlugin.java
+++ b/src_plugins/com/android/systemui/plugins/OverscrollPlugin.java
@@ -15,6 +15,8 @@
*/
package com.android.systemui.plugins;
+import android.view.MotionEvent;
+
import com.android.systemui.plugins.annotations.ProvidesInterface;
/**
@@ -28,7 +30,7 @@
public interface OverscrollPlugin extends Plugin {
String ACTION = "com.android.systemui.action.PLUGIN_LAUNCHER_OVERSCROLL";
- int VERSION = 3;
+ int VERSION = 4;
String DEVICE_STATE_LOCKED = "Locked";
String DEVICE_STATE_LAUNCHER = "Launcher";
@@ -41,33 +43,33 @@
boolean isActive();
/**
- * Called when a touch is down and has been recognized as an overscroll gesture.
- * A call of this method will always result in `onTouchUp` being called, and possibly
- * `onFling` as well.
- *
+ * Called when a touch has been recognized as an overscroll gesture.
+ * @param horizontalDistancePx Horizontal distance from the last finger location to the finger
+ * location when it first touched the screen.
+ * @param verticalDistancePx Horizontal distance from the last finger location to the finger
+ * location when it first touched the screen.
+ * @param thresholdPx Minimum distance for gesture.
+ * @param flingDistanceThresholdPx Minimum distance for gesture by fling.
+ * @param flingVelocityThresholdPx Minimum velocity for gesture by fling.
* @param deviceState String representing the current device state
* @param underlyingActivity String representing the currently active Activity
*/
- void onTouchStart(String deviceState, String underlyingActivity);
+ void onTouchEvent(MotionEvent event,
+ int horizontalDistancePx,
+ int verticalDistancePx,
+ int thresholdPx,
+ int flingDistanceThresholdPx,
+ int flingVelocityThresholdPx,
+ String deviceState,
+ String underlyingActivity);
/**
- * Called when a touch that was previously recognized has moved.
- *
- * @param px distance between the position of touch on this update and the position of the
- * touch when it was initially recognized.
+ * @return `true` if overscroll gesture handling should override all other gestures.
*/
- void onTouchTraveled(int px);
+ boolean blockOtherGestures();
/**
- * Called when a touch that was previously recognized has ended.
- *
- * @param px distance between the position of touch on this update and the position of the
- * touch when it was initially recognized.
+ * @return `true` if the overscroll gesture can pan the underlying app.
*/
- void onTouchEnd(int px);
-
- /**
- * Called when the user starts Compose with a fling. `onTouchUp` will also be called.
- */
- void onFling(float velocity);
+ boolean allowsUnderlyingActivityOverscroll();
}
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index 14212be..b333100 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -436,9 +436,12 @@
sEventChecker.finishNoWait();
}
}
- // b/156287114
+
try {
- log("Input: " + mDevice.executeShellCommand("dumpsys input"));
+ Log.e("b/156287114", "Input:");
+ for (String line : mDevice.executeShellCommand("dumpsys input").split("\\n")) {
+ Log.d("b/156287114", line);
+ }
} catch (IOException e) {
e.printStackTrace();
}