Merging from ub-launcher3-rvc-dev @ build 6511067
Bug:150504032
Test: manual, presubmit on the source branch
x20/teams/android-launcher/merge/ub-launcher3-rvc-dev_rvc-dev_6511067.html
Change-Id: Ifc8c251ef0f03e66de3b1c9fa94bfcb980704769
diff --git a/protos/launcher_atom.proto b/protos/launcher_atom.proto
index cac2d8f..f1db144 100644
--- a/protos/launcher_atom.proto
+++ b/protos/launcher_atom.proto
@@ -48,12 +48,16 @@
HotseatContainer hotseat = 2;
FolderContainer folder = 3;
AllAppsContainer all_apps_container = 4;
+ WidgetsContainer widgets_container = 5;
}
}
message AllAppsContainer {
}
+message WidgetsContainer {
+}
+
enum Origin {
UNKNOWN = 0;
DEFAULT_LAYOUT = 1; // icon automatically placed in workspace, folder, hotseat
@@ -95,7 +99,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 +135,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/appprediction/PredictionUiStateManager.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionUiStateManager.java
index ab3c71a..b6a8206 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionUiStateManager.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionUiStateManager.java
@@ -211,8 +211,11 @@
}
private void dispatchOnChange(boolean changed) {
- PredictionState newState = changed ? parseLastState() :
- (mPendingState == null ? mCurrentState : mPendingState);
+ PredictionState newState = changed
+ ? parseLastState()
+ : mPendingState != null && canApplyPredictions(mPendingState)
+ ? mPendingState
+ : mCurrentState;
if (changed && mAppsView != null && !canApplyPredictions(newState)) {
scheduleApplyPredictedApps(newState);
} else {
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..1fe3643 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);
@@ -292,100 +274,54 @@
}
if (mAppPredictor != null) {
mAppPredictor.destroy();
+ mAppPredictor = null;
}
- 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 +379,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 +463,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 +483,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 +522,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 +541,7 @@
mRequiresCacheUpdate = true;
}
+
@Nullable
@Override
public SystemShortcut<QuickstepLauncher> getShortcut(QuickstepLauncher activity,
@@ -711,77 +638,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 70addb5..ed831ab 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
@@ -101,7 +101,7 @@
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
- onStateOrResumeChanged();
+ onStateOrResumeChanging(false /* inTransition */);
}
@Override
@@ -116,11 +116,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
@@ -165,14 +163,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);
}
}
@@ -218,7 +218,9 @@
break;
}
case OVERVIEW_STATE_ORDINAL: {
- DiscoveryBounce.showForOverviewIfNeeded(this);
+ RecentsView recentsView = getOverviewPanel();
+ DiscoveryBounce.showForOverviewIfNeeded(this,
+ recentsView.getPagedOrientationHandler());
RecentsView rv = getOverviewPanel();
sendCustomAccessibilityEvent(
rv.getPageAt(rv.getCurrentPage()), TYPE_VIEW_FOCUSED, null);
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsViewStateController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
index a1cc60e..085b9b3 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
@@ -71,7 +71,7 @@
builder.addOnFrameCallback(mRecentsView::loadVisibleTaskData);
mRecentsView.updateEmptyMessage();
} else {
- builder.getAnim().addListener(
+ builder.addListener(
AnimationSuccessListener.forRunnable(mRecentsView::resetTaskVisuals));
}
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/launcher3/uioverrides/states/OverviewState.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewState.java
index 9f31608..9b4e890 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewState.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewState.java
@@ -19,6 +19,8 @@
import static com.android.launcher3.config.FeatureFlags.ENABLE_OVERVIEW_ACTIONS;
import static com.android.launcher3.logging.LoggerUtils.newContainerTarget;
import static com.android.quickstep.SysUINavigationMode.Mode.NO_BUTTON;
+import static com.android.quickstep.SysUINavigationMode.Mode.TWO_BUTTONS;
+import static com.android.quickstep.SysUINavigationMode.getMode;
import static com.android.quickstep.SysUINavigationMode.removeShelfFromOverview;
import android.content.Context;
@@ -127,7 +129,11 @@
@Override
public int getVisibleElements(Launcher launcher) {
- if (ENABLE_OVERVIEW_ACTIONS.get() && removeShelfFromOverview(launcher)) {
+ RecentsView recentsView = launcher.getOverviewPanel();
+ boolean hideShelfTwoButtonLandscape = getMode(launcher) == TWO_BUTTONS &&
+ !recentsView.getPagedOrientationHandler().isLayoutNaturalToLauncher();
+ if (ENABLE_OVERVIEW_ACTIONS.get() && removeShelfFromOverview(launcher) ||
+ hideShelfTwoButtonLandscape) {
return OVERVIEW_BUTTONS;
} else if (launcher.getDeviceProfile().isVerticalBarLayout()) {
return VERTICAL_SWIPE_INDICATOR | OVERVIEW_BUTTONS;
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..dc8fb9e 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/AppToOverviewAnimationProvider.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/AppToOverviewAnimationProvider.java
@@ -18,49 +18,47 @@
import static com.android.launcher3.LauncherState.BACKGROUND_APP;
import static com.android.launcher3.LauncherState.OVERVIEW;
import static com.android.launcher3.anim.Interpolators.TOUCH_RESPONSE_INTERPOLATOR;
+import static com.android.launcher3.anim.Interpolators.clampToProgress;
import static com.android.launcher3.statehandlers.DepthController.DEPTH;
import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
-import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_OPENING;
import android.animation.Animator;
import android.animation.AnimatorSet;
-import android.animation.ObjectAnimator;
-import android.animation.ValueAnimator;
-import android.graphics.Rect;
import android.util.Log;
-import android.view.View;
+import android.view.animation.Interpolator;
import com.android.launcher3.AbstractFloatingView;
-import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.anim.AnimationSuccessListener;
+import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.statehandlers.DepthController;
-import com.android.quickstep.util.AppWindowAnimationHelper;
+import com.android.launcher3.statemanager.StatefulActivity;
import com.android.quickstep.util.RemoteAnimationProvider;
+import com.android.quickstep.util.TaskViewSimulator;
import com.android.quickstep.util.TransformParams;
import com.android.quickstep.views.RecentsView;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat;
-import com.android.systemui.shared.system.TransactionCompat;
/**
* Provider for the atomic (for 3-button mode) remote window animation from the app to the overview.
*
* @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;
}
@@ -96,32 +94,25 @@
@Override
public AnimatorSet createWindowAnimation(RemoteAnimationTargetCompat[] appTargets,
RemoteAnimationTargetCompat[] wallpaperTargets) {
- if (mRecentsView != null) {
- mRecentsView.setRunningTaskIconScaledDown(true);
+ PendingAnimation pa = new PendingAnimation(RECENTS_LAUNCH_DURATION);
+ if (mActivity == null) {
+ Log.e(TAG, "Animation created, before activity");
+ return pa.buildAnim();
}
- AnimatorSet anim = new AnimatorSet();
- anim.addListener(new AnimationSuccessListener() {
+ mRecentsView.setRunningTaskIconScaledDown(true);
+ pa.addListener(new AnimationSuccessListener() {
@Override
public void onAnimationSuccess(Animator animator) {
mActivityInterface.onSwipeUpToRecentsComplete();
- if (mRecentsView != null) {
- mRecentsView.animateUpRunningTaskIconScale();
- }
+ mRecentsView.animateUpRunningTaskIconScale();
}
});
- if (mActivity == null) {
- Log.e(TAG, "Animation created, before activity");
- anim.play(ValueAnimator.ofInt(0, 1).setDuration(RECENTS_LAUNCH_DURATION));
- return anim;
- }
DepthController depthController = mActivityInterface.getDepthController();
if (depthController != null) {
- anim.play(ObjectAnimator.ofFloat(depthController, DEPTH,
- BACKGROUND_APP.getDepth(mActivity),
- OVERVIEW.getDepth(mActivity))
- .setDuration(RECENTS_LAUNCH_DURATION));
+ pa.addFloat(depthController, DEPTH, BACKGROUND_APP.getDepth(mActivity),
+ OVERVIEW.getDepth(mActivity), TOUCH_RESPONSE_INTERPOLATOR);
}
RemoteAnimationTargets targets = new RemoteAnimationTargets(appTargets,
@@ -131,53 +122,39 @@
RemoteAnimationTargetCompat runningTaskTarget = targets.findTask(mTargetTaskId);
if (runningTaskTarget == null) {
Log.e(TAG, "No closing app");
- anim.play(ValueAnimator.ofInt(0, 1).setDuration(RECENTS_LAUNCH_DURATION));
- return anim;
+ return pa.buildAnim();
}
- final AppWindowAnimationHelper clipHelper = new AppWindowAnimationHelper(
- mRecentsView.getPagedViewOrientedState(), mActivity);
-
- // At this point, the activity is already started and laid-out. Get the home-bounds
- // relative to the screen using the rootView of the activity.
- int loc[] = new int[2];
- View rootView = mActivity.getRootView();
- rootView.getLocationOnScreen(loc);
- Rect homeBounds = new Rect(loc[0], loc[1],
- loc[0] + rootView.getWidth(), loc[1] + rootView.getHeight());
- clipHelper.updateSource(homeBounds, runningTaskTarget);
-
- Rect targetRect = new Rect();
- mActivityInterface.getSwipeUpDestinationAndLength(mActivity.getDeviceProfile(), mActivity,
- targetRect);
- clipHelper.updateTargetRect(targetRect);
- clipHelper.prepareAnimation(mActivity.getDeviceProfile());
+ TaskViewSimulator tsv = new TaskViewSimulator(mActivity, mRecentsView.getSizeStrategy());
+ tsv.setDp(mActivity.getDeviceProfile());
+ tsv.setPreview(runningTaskTarget);
+ tsv.setLayoutRotation(mRecentsView.getPagedViewOrientedState().getTouchRotation(),
+ mRecentsView.getPagedViewOrientedState().getDisplayRotation());
TransformParams params = new TransformParams()
- .setSyncTransactionApplier(new SyncRtSurfaceTransactionApplierCompat(rootView));
- ValueAnimator valueAnimator = ValueAnimator.ofFloat(0, 1);
- valueAnimator.setDuration(RECENTS_LAUNCH_DURATION);
- valueAnimator.setInterpolator(TOUCH_RESPONSE_INTERPOLATOR);
- valueAnimator.addUpdateListener((v) -> {
- params.setProgress((float) v.getAnimatedValue()).setTargetSet(targets);
- clipHelper.applyTransform(params);
- });
+ .setTargetSet(targets)
+ .setSyncTransactionApplier(
+ new SyncRtSurfaceTransactionApplierCompat(mActivity.getRootView()));
+ AnimatedFloat recentsAlpha = new AnimatedFloat(() -> { });
+ params.setBaseAlphaCallback((t, a) -> recentsAlpha.value);
+
+ Interpolator taskInterpolator;
if (targets.isAnimatingHome()) {
- // If we are animating home, fade in the opening targets
- RemoteAnimationTargets openingSet = new RemoteAnimationTargets(appTargets,
- wallpaperTargets, MODE_OPENING);
-
- TransactionCompat transaction = new TransactionCompat();
- valueAnimator.addUpdateListener((v) -> {
- for (RemoteAnimationTargetCompat app : openingSet.apps) {
- transaction.setAlpha(app.leash, (float) v.getAnimatedValue());
- }
- transaction.apply();
- });
+ taskInterpolator = TOUCH_RESPONSE_INTERPOLATOR;
+ pa.addFloat(recentsAlpha, AnimatedFloat.VALUE, 0, 1, TOUCH_RESPONSE_INTERPOLATOR);
+ } else {
+ // When animation from app to recents, the recents layer is drawn on top of the app. To
+ // prevent the overlap, we animate the task first and then quickly fade in the recents.
+ taskInterpolator = clampToProgress(TOUCH_RESPONSE_INTERPOLATOR, 0, 0.8f);
+ pa.addFloat(recentsAlpha, AnimatedFloat.VALUE, 0, 1,
+ clampToProgress(TOUCH_RESPONSE_INTERPOLATOR, 0.8f, 1));
}
- anim.play(valueAnimator);
- return anim;
+
+ pa.addFloat(params, TransformParams.PROGRESS, 0, 1, taskInterpolator);
+ tsv.addAppToOverviewAnim(pa, taskInterpolator);
+ pa.addOnFrameCallback(() -> tsv.apply(params));
+ return pa.buildAnim();
}
/**
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..614ba46 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java
@@ -15,8 +15,6 @@
*/
package com.android.quickstep;
-import static com.android.launcher3.LauncherState.BACKGROUND_APP;
-import static com.android.launcher3.LauncherState.OVERVIEW;
import static com.android.launcher3.anim.Interpolators.ACCEL_1_5;
import static com.android.launcher3.anim.Interpolators.DEACCEL;
import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
@@ -25,8 +23,6 @@
import static com.android.launcher3.views.FloatingIconView.SHAPE_PROGRESS_DURATION;
import android.animation.Animator;
-import android.animation.AnimatorSet;
-import android.animation.ObjectAnimator;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.Intent;
@@ -36,6 +32,7 @@
import android.graphics.Rect;
import android.graphics.RectF;
import android.os.Build;
+import android.util.Log;
import android.view.MotionEvent;
import android.view.animation.Interpolator;
@@ -44,23 +41,25 @@
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.anim.PendingAnimation;
+import com.android.launcher3.statemanager.StatefulActivity;
+import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.touch.PagedOrientationHandler;
import com.android.launcher3.util.VibratorWrapper;
import com.android.launcher3.views.FloatingIconView;
import com.android.quickstep.RecentsAnimationCallbacks.RecentsAnimationListener;
import com.android.quickstep.util.ActiveGestureLog;
import com.android.quickstep.util.ActivityInitListener;
+import com.android.quickstep.util.AppWindowAnimationHelper;
import com.android.quickstep.util.RectFSpringAnim;
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());
}
/**
@@ -379,18 +377,9 @@
mDragLengthFactorStartPullback = mDragLengthFactorMaxPullback = 1;
}
- AnimatorSet anim = new AnimatorSet();
- anim.setDuration(mTransitionDragLength * 2);
- anim.setInterpolator(t -> t * mDragLengthFactor);
- anim.play(ObjectAnimator.ofFloat(mTaskViewSimulator.recentsViewScale,
- AnimatedFloat.VALUE,
- mTaskViewSimulator.getFullScreenScale(), 1));
- anim.play(ObjectAnimator.ofFloat(mTaskViewSimulator.fullScreenProgress,
- AnimatedFloat.VALUE,
- BACKGROUND_APP.getOverviewFullscreenProgress(),
- OVERVIEW.getOverviewFullscreenProgress()));
- mWindowTransitionController =
- AnimatorPlaybackController.wrap(anim, mTransitionDragLength * 2);
+ PendingAnimation pa = new PendingAnimation(mTransitionDragLength * 2);
+ mTaskViewSimulator.addAppToOverviewAnim(pa, t -> t * mDragLengthFactor);
+ mWindowTransitionController = pa.createPlaybackController();
}
/**
@@ -400,7 +389,16 @@
protected boolean onActivityInit(Boolean alreadyOnHome) {
T createdActivity = mActivityInterface.getCreatedActivity();
+ if (TestProtocol.sDebugTracing) {
+ Log.d(TestProtocol.PAUSE_NOT_DETECTED, "BaseSwipeUpHandler.1");
+ }
if (createdActivity != null) {
+ if (TestProtocol.sDebugTracing) {
+ Log.d(TestProtocol.PAUSE_NOT_DETECTED, "BaseSwipeUpHandler.2");
+ }
+ ((RecentsView) createdActivity.getOverviewPanel())
+ .setLayoutRotation(mDeviceState.getCurrentActiveRotation(),
+ mDeviceState.getDisplayRotation());
initTransitionEndpoints(InvariantDeviceProfile.INSTANCE.get(mContext)
.getDeviceProfile(mContext));
}
@@ -653,14 +651,11 @@
}
@Override
- public void onBuildParams(Builder builder, RemoteAnimationTargetCompat app, int targetMode,
- TransformParams params) {
- if (app.mode == targetMode
- && app.activityType != RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME) {
- builder.withMatrix(mMatrix)
- .withWindowCrop(mCropRect)
- .withCornerRadius(params.getCornerRadius());
- }
+ public void onBuildTargetParams(
+ Builder builder, RemoteAnimationTargetCompat app, TransformParams params) {
+ builder.withMatrix(mMatrix)
+ .withWindowCrop(mCropRect)
+ .withCornerRadius(params.getCornerRadius());
}
@Override
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..5dbf199 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,14 @@
}
@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) {
+ RecentsView recentsView = launcher.getOverviewPanel();
+ DiscoveryBounce.showForOverviewIfNeeded(launcher,
+ recentsView.getPagedOrientationHandler());
}
- launcher.getStateManager().reapplyState();
- DiscoveryBounce.showForOverviewIfNeeded(launcher);
}
@Override
@@ -113,15 +115,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 +293,11 @@
}
@Override
+ public void setHintUserWillBeActive() {
+ getCreatedActivity().setHintUserWillBeActive();
+ }
+
+ @Override
public boolean deferStartingActivity(RecentsAnimationDeviceState deviceState, MotionEvent ev) {
return deviceState.isInDeferredGestureRegion(ev);
}
@@ -337,15 +336,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 +350,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 +377,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..a8fa630 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;
@@ -277,6 +276,8 @@
if (mActivity == activity) {
return true;
}
+ mTaskViewSimulator.setLayoutRotation(mDeviceState.getCurrentActiveRotation(),
+ mDeviceState.getDisplayRotation());
if (mActivity != null) {
// The launcher may have been recreated as a result of device rotation.
int oldState = mStateCallback.getState() & ~LAUNCHER_UI_STATES;
@@ -587,9 +588,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 +684,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 +940,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..8bffc78 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;
@@ -539,8 +540,6 @@
boolean canStartSystemGesture = mDeviceState.canStartSystemGesture();
if (!mDeviceState.isUserUnlocked()) {
- Log.d(TAG, "User locked. Can start system gesture? " + canStartSystemGesture
- + " sysUiFlags: " + mDeviceState.getSystemUiStateFlags());
if (canStartSystemGesture) {
// This handles apps launched in direct boot mode (e.g. dialer) as well as apps
// launched while device is locked even after exiting direct boot mode (e.g. camera).
@@ -617,21 +616,6 @@
return;
}
mDeviceState.enableMultipleRegions(baseInputConsumer instanceof OtherActivityInputConsumer);
- BaseDraggingActivity activity =
- mOverviewComponentObserver.getActivityInterface().getCreatedActivity();
- if (TestProtocol.sDebugTracing) {
- Log.d(TestProtocol.PAUSE_NOT_DETECTED, "handleOrientationSetup.2");
- }
- if (activity == null || !(activity.getOverviewPanel() instanceof RecentsView)) {
- return;
- }
- if (TestProtocol.sDebugTracing) {
- Log.d(TestProtocol.PAUSE_NOT_DETECTED, "handleOrientationSetup.3");
- }
- ((RecentsView) activity.getOverviewPanel())
- .setLayoutRotation(mDeviceState.getCurrentActiveRotation(),
- mDeviceState.getDisplayRotation());
- activity.getDragLayer().recreateControllers();
}
private InputConsumer newBaseConsumer(GestureState previousGestureState,
@@ -705,7 +689,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 +738,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..abe4af4 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
@@ -25,43 +25,45 @@
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.ObjectAnimator;
import android.content.Context;
import android.content.Intent;
+import android.graphics.Matrix;
import android.graphics.Point;
import android.graphics.PointF;
-import android.graphics.Rect;
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.AnimatedFloat;
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;
import com.android.quickstep.RecentsAnimationDeviceState;
import com.android.quickstep.RecentsAnimationTargets;
import com.android.quickstep.TaskAnimationManager;
-import com.android.quickstep.util.AppWindowAnimationHelper;
import com.android.quickstep.util.TransformParams;
+import com.android.quickstep.util.TransformParams.BuilderProxy;
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;
+import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams.Builder;
/**
* A dummy input consumer used when the device is still locked, e.g. from secure camera.
*/
public class DeviceLockedInputConsumer implements InputConsumer,
- RecentsAnimationCallbacks.RecentsAnimationListener {
-
- private static final float SCALE_DOWN = 0.75f;
+ RecentsAnimationCallbacks.RecentsAnimationListener, BuilderProxy {
private static final String[] STATE_NAMES = DEBUG_STATES ? new String[2] : null;
private static int getFlagForIndex(int index, String name) {
@@ -84,18 +86,20 @@
private final InputMonitorCompat mInputMonitorCompat;
private final PointF mTouchDown = new PointF();
- private final AppWindowAnimationHelper mAppWindowAnimationHelper;
private final TransformParams mTransformParams;
- private final Point mDisplaySize;
private final MultiStateCallback mStateCallback;
+ private final Point mDisplaySize;
+ private final Matrix mMatrix = new Matrix();
+ private final float mMaxTranslationY;
+
private VelocityTracker mVelocityTracker;
- private float mProgress;
+ private final AnimatedFloat mProgress = new AnimatedFloat(this::applyTransform);
private boolean mThresholdCrossed = false;
+ private boolean mHomeLaunched = false;
private RecentsAnimationController mRecentsAnimationController;
- private RecentsAnimationTargets mRecentsAnimationTargets;
public DeviceLockedInputConsumer(Context context, RecentsAnimationDeviceState deviceState,
TaskAnimationManager taskAnimationManager, GestureState gestureState,
@@ -105,9 +109,10 @@
mTaskAnimationManager = taskAnimationManager;
mGestureState = gestureState;
mTouchSlopSquared = squaredTouchSlop(context);
- mAppWindowAnimationHelper = new AppWindowAnimationHelper(context);
mTransformParams = new TransformParams();
mInputMonitorCompat = inputMonitorCompat;
+ mMaxTranslationY = context.getResources().getDimensionPixelSize(
+ R.dimen.device_locked_y_offset);
// Do not use DeviceProfile as the user data might be locked
mDisplaySize = DefaultDisplay.INSTANCE.get(context).getInfo().realSize;
@@ -158,9 +163,7 @@
}
} else {
float dy = Math.max(mTouchDown.y - y, 0);
- mProgress = dy / mDisplaySize.y;
- mTransformParams.setProgress(mProgress);
- mAppWindowAnimationHelper.applyTransform(mTransformParams);
+ mProgress.updateValue(dy / mDisplaySize.y);
}
break;
}
@@ -176,7 +179,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());
@@ -190,14 +192,29 @@
// Is fling
dismissTask = velocityY < 0;
} else {
- dismissTask = mProgress >= (1 - MIN_PROGRESS_FOR_OVERVIEW);
+ dismissTask = mProgress.value >= (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
+ ObjectAnimator animator = mProgress.animateToValue(mProgress.value, 0);
+ animator.setDuration(100);
+ animator.setInterpolator(Interpolators.ACCEL);
+ 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 +222,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);
}
@@ -220,37 +235,40 @@
public void onRecentsAnimationStart(RecentsAnimationController controller,
RecentsAnimationTargets targets) {
mRecentsAnimationController = controller;
- mRecentsAnimationTargets = targets;
-
- Rect displaySize = new Rect(0, 0, mDisplaySize.x, mDisplaySize.y);
- RemoteAnimationTargetCompat targetCompat = targets.findTask(
- mGestureState.getRunningTaskId());
- if (targetCompat != null) {
- mAppWindowAnimationHelper.updateSource(displaySize, targetCompat);
- }
-
- Utilities.scaleRectAboutCenter(displaySize, SCALE_DOWN);
- displaySize.offsetTo(displaySize.left, 0);
- mTransformParams.setTargetSet(mRecentsAnimationTargets);
- mAppWindowAnimationHelper.updateTargetRect(displaySize);
- mAppWindowAnimationHelper.applyTransform(mTransformParams);
-
+ mTransformParams.setTargetSet(targets);
+ applyTransform();
mStateCallback.setState(STATE_TARGET_RECEIVED);
}
@Override
public void onRecentsAnimationCanceled(ThumbnailData thumbnailData) {
mRecentsAnimationController = null;
- mRecentsAnimationTargets = null;
+ mTransformParams.setTargetSet(null);
}
private void endRemoteAnimation() {
- if (mRecentsAnimationController != null) {
+ if (mHomeLaunched) {
+ ActivityManagerWrapper.getInstance().cancelRecentsAnimation(false);
+ } else if (mRecentsAnimationController != null) {
mRecentsAnimationController.finishController(
false /* toRecents */, null /* callback */, false /* sendUserLeaveHint */);
}
}
+ private void applyTransform() {
+ mTransformParams.setProgress(mProgress.value);
+ if (mTransformParams.getTargetSet() != null) {
+ mTransformParams.applySurfaceParams(mTransformParams.createSurfaceParams(this));
+ }
+ }
+
+ @Override
+ public void onBuildTargetParams(
+ Builder builder, RemoteAnimationTargetCompat app, TransformParams params) {
+ mMatrix.setTranslate(0, mProgress.value * mMaxTranslationY);
+ builder.withMatrix(mMatrix);
+ }
+
@Override
public void onConsumerAboutToBeSwitched() {
mStateCallback.setState(STATE_HANDLER_INVALIDATED);
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 b7af8ac..2805f62 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/AppWindowAnimationHelper.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/AppWindowAnimationHelper.java
index 110370a..b743d3f 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/AppWindowAnimationHelper.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/AppWindowAnimationHelper.java
@@ -77,7 +77,6 @@
private final Matrix mTmpMatrix = new Matrix();
private final Rect mTmpRect = new Rect();
private final RectF mTmpRectF = new RectF();
- private final RectF mCurrentRectWithInsets = new RectF();
private RecentsOrientedState mOrientedState;
// Corner radius of windows, in pixels
private final float mWindowCornerRadius;
@@ -88,9 +87,6 @@
// Whether or not to actually use the rounded cornders on windows
private boolean mUseRoundedCornersOnWindows;
- // Corner radius currently applied to transformed window.
- private float mCurrentCornerRadius;
-
public AppWindowAnimationHelper(RecentsOrientedState orientedState, Context context) {
Resources res = context.getResources();
mOrientedState = orientedState;
@@ -100,24 +96,11 @@
mUseRoundedCornersOnWindows = mSupportsRoundedCornersOnWindows;
}
- public AppWindowAnimationHelper(Context context) {
- this(null, context);
- }
-
private void updateSourceStack(RemoteAnimationTargetCompat target) {
mSourceInsets.set(target.contentInsets);
mSourceStackBounds.set(target.screenSpaceBounds);
}
- public void updateSource(Rect homeStackBounds, RemoteAnimationTargetCompat target) {
- updateSourceStack(target);
- updateHomeBounds(homeStackBounds);
- }
-
- public void updateHomeBounds(Rect homeStackBounds) {
- mHomeStackBounds.set(homeStackBounds);
- }
-
public void updateTargetRect(Rect targetRect) {
mSourceRect.set(mSourceInsets.left, mSourceInsets.top,
mSourceStackBounds.width() - mSourceInsets.right,
@@ -202,7 +185,6 @@
cornerRadius = mapRange(boundToRange(params.getProgress(), 0, 1),
windowCornerRadius, mTaskCornerRadius);
}
- mCurrentCornerRadius = cornerRadius;
}
builder.withMatrix(mTmpMatrix)
@@ -238,11 +220,6 @@
mSourceStackBounds.height() - (mSourceWindowClipInsets.bottom * progress);
}
- public RectF getCurrentRectWithInsets() {
- mTmpMatrix.mapRect(mCurrentRectWithInsets, mCurrentClipRectF);
- return mCurrentRectWithInsets;
- }
-
public void fromTaskThumbnailView(TaskThumbnailView ttv, RecentsView rv,
@Nullable RemoteAnimationTargetCompat target) {
BaseDraggingActivity activity = BaseDraggingActivity.fromContext(ttv.getContext());
@@ -314,8 +291,4 @@
return mTargetRect;
}
- public float getCurrentCornerRadius() {
- return mCurrentCornerRadius;
- }
-
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java
index 32fc0de..3e0daaf 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java
@@ -212,12 +212,13 @@
}
private void addScrimAnimationForState(Launcher launcher, LauncherState state, long duration) {
- PendingAnimation builder = new PendingAnimation(duration, mAnimators);
+ PendingAnimation builder = new PendingAnimation(duration);
launcher.getWorkspace().getStateTransitionAnimation().setScrim(builder, state);
builder.setFloat(
launcher.getDragLayer().getOverviewScrim(),
OverviewScrim.SCRIM_PROGRESS,
state.getOverviewScrimAlpha(launcher),
ACCEL_DEACCEL);
+ mAnimators.play(builder.buildAnim());
}
}
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 a204aae..d7a7e4c 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
@@ -20,6 +20,7 @@
import static com.android.quickstep.util.RecentsOrientedState.postDisplayRotation;
import static com.android.systemui.shared.system.WindowManagerWrapper.WINDOWING_MODE_FULLSCREEN;
+import android.animation.TimeInterpolator;
import android.content.Context;
import android.graphics.Matrix;
import android.graphics.PointF;
@@ -29,8 +30,10 @@
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
+import com.android.launcher3.anim.PendingAnimation;
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;
@@ -50,7 +53,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();
@@ -79,7 +82,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;
@@ -142,6 +145,14 @@
}
/**
+ * Adds animation for all the components corresponding to transition from an app to overview
+ */
+ public void addAppToOverviewAnim(PendingAnimation pa, TimeInterpolator interpolator) {
+ pa.addFloat(fullScreenProgress, AnimatedFloat.VALUE, 1, 0, interpolator);
+ pa.addFloat(recentsViewScale, AnimatedFloat.VALUE, getFullScreenScale(), 1, interpolator);
+ }
+
+ /**
* Returns the current clipped/visible window bounds in the window coordinate space
*/
public RectF getCurrentCropRect() {
@@ -247,14 +258,11 @@
}
@Override
- public void onBuildParams(Builder builder, RemoteAnimationTargetCompat app,
- int targetMode, TransformParams params) {
- if (app.mode == targetMode
- && app.activityType != RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME) {
- builder.withMatrix(mMatrix)
- .withWindowCrop(mTmpCropRect)
- .withCornerRadius(getCurrentCornerRadius());
- }
+ public void onBuildTargetParams(
+ Builder builder, RemoteAnimationTargetCompat app, TransformParams params) {
+ builder.withMatrix(mMatrix)
+ .withWindowCrop(mTmpCropRect)
+ .withCornerRadius(getCurrentCornerRadius());
}
/**
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/TransformParams.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/TransformParams.java
index 83b64db..d837e54 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/TransformParams.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/TransformParams.java
@@ -16,6 +16,7 @@
package com.android.quickstep.util;
import android.graphics.RectF;
+import android.util.FloatProperty;
import androidx.annotation.Nullable;
@@ -29,6 +30,19 @@
public class TransformParams {
+ public static FloatProperty<TransformParams> PROGRESS =
+ new FloatProperty<TransformParams>("progress") {
+ @Override
+ public void setValue(TransformParams params, float v) {
+ params.setProgress(v);
+ }
+
+ @Override
+ public Float get(TransformParams params) {
+ return params.getProgress();
+ }
+ };
+
private float mProgress;
private @Nullable RectF mCurrentRect;
private float mTargetAlpha;
@@ -176,10 +190,6 @@
return mTargetSet;
}
- public SyncRtSurfaceTransactionApplierCompat getSyncTransactionApplier() {
- return mSyncTransactionApplier;
- }
-
public void applySurfaceParams(SurfaceParams[] params) {
if (mSyncTransactionApplier != null) {
mSyncTransactionApplier.scheduleApply(params);
@@ -199,7 +209,15 @@
public interface BuilderProxy {
- void onBuildParams(SurfaceParams.Builder builder,
- RemoteAnimationTargetCompat app, int targetMode, TransformParams params);
+ default void onBuildParams(SurfaceParams.Builder builder,
+ RemoteAnimationTargetCompat app, int targetMode, TransformParams params) {
+ if (app.mode == targetMode
+ && app.activityType != RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME) {
+ onBuildTargetParams(builder, app, params);
+ }
+ }
+
+ default void onBuildTargetParams(SurfaceParams.Builder builder,
+ RemoteAnimationTargetCompat app, TransformParams params) { }
}
}
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..3fc235c 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);
@@ -2190,6 +2190,10 @@
*/
public void setModalStateEnabled(boolean isModalState) { }
+ public BaseActivityInterface getSizeStrategy() {
+ return mSizeStrategy;
+ }
+
/**
* Used to register callbacks for when our empty message state changes.
*
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..016ffea 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;
}
@@ -315,7 +322,7 @@
pw.println(" gestureID=" + mGestureId);
pw.println(" runningTask=" + mRunningTask);
pw.println(" endTarget=" + mEndTarget);
- pw.println(" lastAppearedTaskTarget=" + mLastAppearedTaskTarget);
+ pw.println(" lastAppearedTaskTargetId=" + getLastAppearedTaskId());
pw.println(" lastStartedTaskId=" + mLastStartedTaskId);
pw.println(" isRecentsAnimationRunning=" + isRecentsAnimationRunning());
}
diff --git a/quickstep/src/com/android/quickstep/OrientationTouchTransformer.java b/quickstep/src/com/android/quickstep/OrientationTouchTransformer.java
index 879fd1d..4cf7aab 100644
--- a/quickstep/src/com/android/quickstep/OrientationTouchTransformer.java
+++ b/quickstep/src/com/android/quickstep/OrientationTouchTransformer.java
@@ -317,13 +317,6 @@
private class OrientationRectF extends RectF {
- /**
- * Delta to subtract width and height by because if we report the translated touch
- * bounds as the width and height, calling {@link RectF#contains(float, float)} will
- * be false
- */
- private float maxDelta = 0.001f;
-
private int mRotation;
private float mHeight;
private float mWidth;
@@ -331,8 +324,8 @@
OrientationRectF(float left, float top, float right, float bottom, int rotation) {
super(left, top, right, bottom);
this.mRotation = rotation;
- mHeight = bottom - maxDelta;
- mWidth = right - maxDelta;
+ mHeight = bottom;
+ mWidth = right;
}
@Override
@@ -342,6 +335,13 @@
return s;
}
+ @Override
+ public boolean contains(float x, float y) {
+ // Mark bottom right as included in the Rect (copied from Rect src, added "=" in "<=")
+ return left < right && top < bottom // check for empty first
+ && x >= left && x <= right && y >= top && y <= bottom;
+ }
+
boolean applyTransform(MotionEvent event, boolean forceTransform) {
mTmpMatrix.reset();
postDisplayRotation(deltaRotation(mCurrentDisplayRotation, mRotation),
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/RecentsAnimationDeviceState.java b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
index 8ac15e8..a892ddc 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
@@ -73,8 +73,6 @@
NavigationModeChangeListener,
DefaultDisplay.DisplayInfoChangeListener {
- private static final String TAG = "RecentsAnimationDeviceState";
-
private final Context mContext;
private final SysUINavigationMode mSysUiNavMode;
private final DefaultDisplay mDefaultDisplay;
@@ -97,7 +95,6 @@
@Override
public void onReceive(Context context, Intent intent) {
if (ACTION_USER_UNLOCKED.equals(intent.getAction())) {
- Log.d(TAG, "User Unlocked Broadcast Received");
mIsUserUnlocked = true;
notifyUserUnlocked();
}
diff --git a/quickstep/src/com/android/quickstep/RemoteAnimationTargets.java b/quickstep/src/com/android/quickstep/RemoteAnimationTargets.java
index 5fa6bc7..f90df45 100644
--- a/quickstep/src/com/android/quickstep/RemoteAnimationTargets.java
+++ b/quickstep/src/com/android/quickstep/RemoteAnimationTargets.java
@@ -68,7 +68,7 @@
}
public boolean isAnimatingHome() {
- for (RemoteAnimationTargetCompat target : apps) {
+ for (RemoteAnimationTargetCompat target : unfilteredApps) {
if (target.activityType == RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME) {
return true;
}
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..4c47d7f 100644
--- a/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java
+++ b/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java
@@ -27,6 +27,7 @@
import static com.android.launcher3.states.RotationHelper.ALLOW_ROTATION_PREFERENCE_KEY;
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
+import static com.android.quickstep.SysUINavigationMode.Mode.TWO_BUTTONS;
import static java.lang.annotation.RetentionPolicy.SOURCE;
import android.content.ContentResolver;
@@ -52,6 +53,8 @@
import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.touch.PagedOrientationHandler;
import com.android.launcher3.util.WindowBounds;
+import com.android.quickstep.BaseActivityInterface;
+import com.android.quickstep.SysUINavigationMode;
import java.lang.annotation.Retention;
import java.util.function.IntConsumer;
@@ -117,11 +120,14 @@
MASK_MULTIPLE_ORIENTATION_SUPPORTED_BY_DEVICE | FLAG_SYSTEM_ROTATION_ALLOWED
| FLAG_ROTATION_WATCHER_SUPPORTED | FLAG_ROTATION_WATCHER_ENABLED;
+ private SysUINavigationMode.NavigationModeChangeListener mNavModeChangeListener =
+ newMode -> setFlag(FLAG_ROTATION_WATCHER_SUPPORTED, newMode != TWO_BUTTONS);
+
private final Context mContext;
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 +139,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();
@@ -162,13 +168,7 @@
if (isFixedRotationTransformEnabled(context)) {
mFlags |= FLAG_MULTIPLE_ORIENTATION_SUPPORTED_BY_FLAG;
}
- if (mOrientationListener.canDetectOrientation()) {
- mFlags |= FLAG_ROTATION_WATCHER_SUPPORTED;
- }
-
- // initialize external flags
- updateAutoRotateSetting();
- updateHomeRotationSetting();
+ initFlags();
}
/**
@@ -272,6 +272,18 @@
mSharedPrefs.getBoolean(ALLOW_ROTATION_PREFERENCE_KEY, false));
}
+ private void initFlags() {
+ SysUINavigationMode.Mode currentMode = SysUINavigationMode.getMode(mContext);
+ if (mOrientationListener.canDetectOrientation() &&
+ currentMode != TWO_BUTTONS) {
+ mFlags |= FLAG_ROTATION_WATCHER_SUPPORTED;
+ }
+
+ // initialize external flags
+ updateAutoRotateSetting();
+ updateHomeRotationSetting();
+ }
+
/**
* Initializes any system values and registers corresponding change listeners. It must be
* paired with {@link #destroyListeners()} call
@@ -282,9 +294,11 @@
mContentResolver.registerContentObserver(
Settings.System.getUriFor(Settings.System.ACCELEROMETER_ROTATION),
false, mSystemAutoRotateObserver);
+ SysUINavigationMode.Mode currentMode =
+ SysUINavigationMode.INSTANCE.get(mContext)
+ .addModeChangeListener(mNavModeChangeListener);
}
- updateAutoRotateSetting();
- updateHomeRotationSetting();
+ initFlags();
}
/**
@@ -294,6 +308,8 @@
if (isMultipleOrientationSupportedByDevice()) {
mSharedPrefs.unregisterOnSharedPreferenceChangeListener(this);
mContentResolver.unregisterContentObserver(mSystemAutoRotateObserver);
+ SysUINavigationMode.INSTANCE.get(mContext)
+ .removeModeChangeListener(mNavModeChangeListener);
}
setRotationWatcherEnabled(false);
}
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/InsettableFrameLayout.java b/src/com/android/launcher3/InsettableFrameLayout.java
index faa18b8..9a66d32 100644
--- a/src/com/android/launcher3/InsettableFrameLayout.java
+++ b/src/com/android/launcher3/InsettableFrameLayout.java
@@ -91,6 +91,9 @@
@Override
public void onViewAdded(View child) {
super.onViewAdded(child);
+ if (!isAttachedToWindow()) {
+ return;
+ }
setFrameLayoutChildInsets(child, mInsets, new Rect());
}
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/LauncherSettings.java b/src/com/android/launcher3/LauncherSettings.java
index 87ead9e..535c9e6 100644
--- a/src/com/android/launcher3/LauncherSettings.java
+++ b/src/com/android/launcher3/LauncherSettings.java
@@ -153,12 +153,16 @@
public static final int CONTAINER_PREDICTION = -102;
public static final int CONTAINER_HOTSEAT_PREDICTION = -103;
public static final int CONTAINER_ALL_APPS = -104;
+ public static final int CONTAINER_WIDGETS_TRAY = -105;
+
public static final String containerToString(int container) {
switch (container) {
case CONTAINER_DESKTOP: return "desktop";
case CONTAINER_HOTSEAT: return "hotseat";
case CONTAINER_PREDICTION: return "prediction";
+ case CONTAINER_ALL_APPS: return "all_apps";
+ case CONTAINER_WIDGETS_TRAY: return "widgets_tray";
default: return String.valueOf(container);
}
}
diff --git a/src/com/android/launcher3/PendingAddItemInfo.java b/src/com/android/launcher3/PendingAddItemInfo.java
index 29c9d93..be994ee 100644
--- a/src/com/android/launcher3/PendingAddItemInfo.java
+++ b/src/com/android/launcher3/PendingAddItemInfo.java
@@ -18,12 +18,15 @@
import android.content.ComponentName;
+import androidx.annotation.Nullable;
+
import com.android.launcher3.model.data.ItemInfo;
+import java.util.Optional;
+
/**
- * Meta data that is used for deferred binding.
- * e.g., this object is used to pass information on draggable targets when they are dropped onto
- * the workspace from another container.
+ * Meta data that is used for deferred binding. e.g., this object is used to pass information on
+ * draggable targets when they are dropped onto the workspace from another container.
*/
public class PendingAddItemInfo extends ItemInfo {
@@ -36,4 +39,22 @@
protected String dumpProperties() {
return super.dumpProperties() + " componentName=" + componentName;
}
+
+ /**
+ * Returns shallow copy of the object.
+ */
+ @Override
+ public ItemInfo makeShallowCopy() {
+ PendingAddItemInfo itemInfo = new PendingAddItemInfo();
+ itemInfo.copyFrom(this);
+ itemInfo.componentName = this.componentName;
+ return itemInfo;
+ }
+
+ @Nullable
+ @Override
+ public ComponentName getTargetComponent() {
+ return Optional.ofNullable(super.getTargetComponent()).orElse(componentName);
+ }
+
}
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 286b522..6b660c1 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -2438,6 +2438,10 @@
// widgets/shortcuts/folders in a slightly different way
mLauncher.addPendingItem(pendingInfo, container, screenId, mTargetCell,
item.spanX, item.spanY);
+ mStatsLogManager.log(
+ LauncherEvent.LAUNCHER_ITEM_DROP_COMPLETED,
+ d.logInstanceId,
+ d.dragInfo.buildProto(null));
}
};
boolean isWidget = pendingInfo.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET
@@ -2526,11 +2530,12 @@
mLauncher.getDragLayer().animateViewIntoPosition(d.dragView, view, this);
resetTransitionTransform();
}
+ mStatsLogManager.log(
+ LauncherEvent.LAUNCHER_ITEM_DROP_COMPLETED,
+ d.logInstanceId,
+ d.dragInfo.buildProto(null));
}
- mStatsLogManager.log(
- LauncherEvent.LAUNCHER_ITEM_DROP_COMPLETED,
- d.logInstanceId,
- d.dragInfo.buildProto(null));
+
}
public Bitmap createWidgetBitmap(ItemInfo widgetInfo, View layout) {
diff --git a/src/com/android/launcher3/allapps/DiscoveryBounce.java b/src/com/android/launcher3/allapps/DiscoveryBounce.java
index 5397942..b4ff5ea 100644
--- a/src/com/android/launcher3/allapps/DiscoveryBounce.java
+++ b/src/com/android/launcher3/allapps/DiscoveryBounce.java
@@ -34,6 +34,7 @@
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.statemanager.StateManager.StateListener;
+import com.android.launcher3.touch.PagedOrientationHandler;
import com.android.launcher3.util.OnboardingPrefs;
/**
@@ -153,16 +154,19 @@
new DiscoveryBounce(launcher, 0).show(HOTSEAT);
}
- public static void showForOverviewIfNeeded(Launcher launcher) {
- showForOverviewIfNeeded(launcher, true);
+ public static void showForOverviewIfNeeded(Launcher launcher,
+ PagedOrientationHandler orientationHandler) {
+ showForOverviewIfNeeded(launcher, true, orientationHandler);
}
- private static void showForOverviewIfNeeded(Launcher launcher, boolean withDelay) {
+ private static void showForOverviewIfNeeded(Launcher launcher, boolean withDelay,
+ PagedOrientationHandler orientationHandler) {
OnboardingPrefs onboardingPrefs = launcher.getOnboardingPrefs();
if (!launcher.isInState(OVERVIEW)
|| !launcher.hasBeenResumed()
|| launcher.isForceInvisible()
|| launcher.getDeviceProfile().isVerticalBarLayout()
+ || !orientationHandler.isLayoutNaturalToLauncher()
|| onboardingPrefs.getBoolean(OnboardingPrefs.SHELF_BOUNCE_SEEN)
|| launcher.getSystemService(UserManager.class).isDemoUser()
|| Utilities.IS_RUNNING_IN_TEST_HARNESS) {
@@ -170,7 +174,8 @@
}
if (withDelay) {
- new Handler().postDelayed(() -> showForOverviewIfNeeded(launcher, false), DELAY_MS);
+ new Handler().postDelayed(() -> showForOverviewIfNeeded(launcher, false,
+ orientationHandler), DELAY_MS);
return;
} else if (AbstractFloatingView.getTopOpenView(launcher) != null) {
// TODO: Move these checks to the top and call this method after invalidate handler.
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/PendingAnimation.java b/src/com/android/launcher3/anim/PendingAnimation.java
index 740f7f2..0f04104 100644
--- a/src/com/android/launcher3/anim/PendingAnimation.java
+++ b/src/com/android/launcher3/anim/PendingAnimation.java
@@ -18,6 +18,7 @@
import static com.android.launcher3.anim.AnimatorPlaybackController.addAnimationHoldersRecur;
import android.animation.Animator;
+import android.animation.Animator.AnimatorListener;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.TimeInterpolator;
@@ -51,12 +52,8 @@
private ValueAnimator mProgressAnimator;
public PendingAnimation(long duration) {
- this(duration, new AnimatorSet());
- }
-
- public PendingAnimation(long duration, AnimatorSet targetSet) {
mDuration = duration;
- mAnim = targetSet;
+ mAnim = new AnimatorSet();
}
/**
@@ -129,13 +126,32 @@
public void addOnFrameCallback(Runnable runnable) {
if (mProgressAnimator == null) {
mProgressAnimator = ValueAnimator.ofFloat(0, 1).setDuration(mDuration);
- add(mProgressAnimator);
}
mProgressAnimator.addUpdateListener(anim -> runnable.run());
}
- public AnimatorSet getAnim() {
+ /**
+ * @see AnimatorSet#addListener(AnimatorListener)
+ */
+ public void addListener(Animator.AnimatorListener listener) {
+ mAnim.addListener(listener);
+ }
+
+ /**
+ * Creates and returns the underlying AnimatorSet
+ */
+ public AnimatorSet buildAnim() {
+ // Add progress animation to the end, so that frame callback is called after all the other
+ // animation update.
+ if (mProgressAnimator != null) {
+ add(mProgressAnimator);
+ mProgressAnimator = null;
+ }
+ if (mAnimHolders.isEmpty()) {
+ // Add a dummy animation to that the duration is respected
+ add(ValueAnimator.ofFloat(0, 1));
+ }
return mAnim;
}
@@ -143,7 +159,7 @@
* Creates a controller for this animation
*/
public AnimatorPlaybackController createPlaybackController() {
- return new AnimatorPlaybackController(mAnim, mDuration, mAnimHolders);
+ return new AnimatorPlaybackController(buildAnim(), mDuration, mAnimHolders);
}
/**
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..475305f 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,61 +30,65 @@
*/
public class StatsLogManager implements ResourceBasedOverride {
- private static final String TAG = "StatsLogManager";
-
interface EventEnum {
int getId();
}
public enum LauncherEvent implements EventEnum {
- @LauncherUiEvent(doc = "App launched from workspace, hotseat or folder in launcher")
+ @UiEvent(doc = "App launched from workspace, hotseat or folder in launcher")
LAUNCHER_APP_LAUNCH_TAP(338),
- @LauncherUiEvent(doc = "Task launched from overview using TAP")
+ @UiEvent(doc = "Task launched from overview using TAP")
LAUNCHER_TASK_LAUNCH_TAP(339),
- @LauncherUiEvent(doc = "Task launched from overview using SWIPE DOWN")
+ @UiEvent(doc = "Task launched from overview using SWIPE DOWN")
LAUNCHER_TASK_LAUNCH_SWIPE_DOWN(340),
- @LauncherUiEvent(doc = "TASK dismissed from overview using SWIPE UP")
+ @UiEvent(doc = "TASK dismissed from overview using SWIPE UP")
LAUNCHER_TASK_DISMISS_SWIPE_UP(341),
- @LauncherUiEvent(doc = "User dragged a launcher item")
+ @UiEvent(doc = "User dragged a launcher item")
LAUNCHER_ITEM_DRAG_STARTED(383),
- @LauncherUiEvent(doc = "A dragged launcher item is successfully dropped")
+ @UiEvent(doc = "A dragged launcher item is successfully dropped")
LAUNCHER_ITEM_DROP_COMPLETED(385),
- @LauncherUiEvent(doc = "A dragged launcher item is successfully dropped on another item "
+ @UiEvent(doc = "A dragged launcher item is successfully dropped on another item "
+ "resulting in a new folder creation")
LAUNCHER_ITEM_DROP_FOLDER_CREATED(386),
- @LauncherUiEvent(doc = "A dragged item is dropped on 'Remove' button in the target bar")
+ @UiEvent(doc = "User action resulted in or manually updated the folder label to "
+ + "new/same value.")
+ LAUNCHER_FOLDER_LABEL_UPDATED(460),
+
+ @UiEvent(doc = "A dragged item is dropped on 'Remove' button in the target bar")
LAUNCHER_ITEM_DROPPED_ON_REMOVE(465),
- @LauncherUiEvent(doc = "A dragged item is dropped on 'Cancel' button in the target bar")
+ @UiEvent(doc = "A dragged item is dropped on 'Cancel' button in the target bar")
LAUNCHER_ITEM_DROPPED_ON_CANCEL(466),
- @LauncherUiEvent(doc = "A predicted item is dragged and dropped on 'Don't suggest app'"
+ @UiEvent(doc = "A predicted item is dragged and dropped on 'Don't suggest app'"
+ " button in the target bar")
LAUNCHER_ITEM_DROPPED_ON_DONT_SUGGEST(467),
- @LauncherUiEvent(doc = "A dragged item is dropped on 'Uninstall' button in target bar")
+ @UiEvent(doc = "A dragged item is dropped on 'Uninstall' button in target bar")
LAUNCHER_ITEM_DROPPED_ON_UNINSTALL(468),
- @LauncherUiEvent(doc = "User completed uninstalling the package after dropping on "
+ @UiEvent(doc = "User completed uninstalling the package after dropping on "
+ "the icon onto 'Uninstall' button in the target bar")
LAUNCHER_ITEM_UNINSTALL_COMPLETED(469),
- @LauncherUiEvent(doc = "User cancelled uninstalling the package after dropping on "
+ @UiEvent(doc = "User cancelled uninstalling the package after dropping on "
+ "the icon onto 'Uninstall' button in the target bar")
LAUNCHER_ITEM_UNINSTALL_CANCELLED(470);
// 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/logging/LauncherUiEvent.java b/src/com/android/launcher3/logging/UiEvent.java
similarity index 80%
rename from src/com/android/launcher3/logging/LauncherUiEvent.java
rename to src/com/android/launcher3/logging/UiEvent.java
index 4507ff7..20d6c72 100644
--- a/src/com/android/launcher3/logging/LauncherUiEvent.java
+++ b/src/com/android/launcher3/logging/UiEvent.java
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
package com.android.launcher3.logging;
import static java.lang.annotation.ElementType.FIELD;
@@ -23,8 +24,11 @@
@Retention(SOURCE)
@Target(FIELD)
-public @interface LauncherUiEvent {
- /** An explanation, suitable for Android analysts, of the UI event that this log represents. */
+// Copy of frameworks/base/core/java/com/android/internal/logging/UiEvent.java
+public @interface UiEvent {
+
+ /**
+ * An explanation, suitable for Android analysts, of the UI event that this log represents.
+ */
String doc();
}
-
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..1429843 100644
--- a/src/com/android/launcher3/model/PredictionModel.java
+++ b/src/com/android/launcher3/model/PredictionModel.java
@@ -14,80 +14,111 @@
* 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;
- private ArrayList<ComponentKey> mCachedComponentKeys;
+ protected Context mContext;
+ 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();
+ });
}
/**
* parses and returns ComponentKeys saved by
* {@link PredictionModel#cachePredictionComponentKeys(List)}
*/
+ @WorkerThread
public List<ComponentKey> getPredictionComponentKeys() {
- if (mCachedComponentKeys == null) {
- mCachedComponentKeys = new ArrayList<>();
-
- String cachedBlob = mDevicePrefs.getString(CACHED_ITEMS_KEY, "");
- for (String line : cachedBlob.split("\n")) {
- ComponentKey key = ComponentKey.fromString(line);
- if (key != null) {
- mCachedComponentKeys.add(key);
- }
+ Preconditions.assertWorkerThread();
+ ArrayList<ComponentKey> items = new ArrayList<>();
+ String cachedBlob = mDevicePrefs.getString(CACHED_ITEMS_KEY, "");
+ for (String line : cachedBlob.split("\n")) {
+ ComponentKey key = getComponentKeyFromSerializedString(line);
+ if (key != null) {
+ items.add(key);
}
+
}
- return mCachedComponentKeys;
+ return items;
}
- /**
- * 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..a97d529 100644
--- a/src/com/android/launcher3/model/data/ItemInfo.java
+++ b/src/com/android/launcher3/model/data/ItemInfo.java
@@ -20,6 +20,7 @@
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_DESKTOP;
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT;
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION;
+import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_WIDGETS_TRAY;
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET;
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT;
@@ -252,6 +253,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());
@@ -335,6 +343,11 @@
.setAllAppsContainer(
AllAppsContainer.getDefaultInstance())
.build();
+ case CONTAINER_WIDGETS_TRAY:
+ return ContainerInfo.newBuilder()
+ .setWidgetsContainer(
+ LauncherAtom.WidgetsContainer.getDefaultInstance())
+ .build();
}
return ContainerInfo.getDefaultInstance();
}
@@ -345,4 +358,8 @@
itemInfo.copyFrom(this);
return itemInfo;
}
+
+ public void setTitle(CharSequence title) {
+ this.title = title;
+ }
}
diff --git a/src/com/android/launcher3/statemanager/StateManager.java b/src/com/android/launcher3/statemanager/StateManager.java
index 4447166..97dc052 100644
--- a/src/com/android/launcher3/statemanager/StateManager.java
+++ b/src/com/android/launcher3/statemanager/StateManager.java
@@ -239,7 +239,7 @@
? fromState.getTransitionDuration(mActivity)
: state.getTransitionDuration(mActivity);
prepareForAtomicAnimation(fromState, state, mConfig);
- AnimatorSet animation = createAnimationToNewWorkspaceInternal(state).getAnim();
+ AnimatorSet animation = createAnimationToNewWorkspaceInternal(state).buildAnim();
if (onCompleteRunnable != null) {
animation.addListener(AnimationSuccessListener.forRunnable(onCompleteRunnable));
}
@@ -267,7 +267,7 @@
for (StateHandler handler : mActivity.getStateManager().getStateHandlers()) {
handler.setStateWithAnimation(toState, config, builder);
}
- return builder.getAnim();
+ return builder.buildAnim();
}
/**
@@ -309,7 +309,7 @@
for (StateHandler handler : getStateHandlers()) {
handler.setStateWithAnimation(state, mConfig, builder);
}
- builder.getAnim().addListener(new AnimationSuccessListener() {
+ builder.addListener(new AnimationSuccessListener() {
@Override
public void onAnimationStart(Animator animation) {
@@ -325,7 +325,7 @@
onStateTransitionEnd(state);
}
});
- mConfig.setAnimation(builder.getAnim(), state);
+ mConfig.setAnimation(builder.buildAnim(), state);
return builder;
}
diff --git a/src/com/android/launcher3/widget/PendingAddShortcutInfo.java b/src/com/android/launcher3/widget/PendingAddShortcutInfo.java
index 6e21a41..9601652 100644
--- a/src/com/android/launcher3/widget/PendingAddShortcutInfo.java
+++ b/src/com/android/launcher3/widget/PendingAddShortcutInfo.java
@@ -15,6 +15,8 @@
*/
package com.android.launcher3.widget;
+import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_WIDGETS_TRAY;
+
import com.android.launcher3.PendingAddItemInfo;
import com.android.launcher3.pm.ShortcutConfigActivityInfo;
@@ -32,5 +34,6 @@
componentName = activityInfo.getComponent();
user = activityInfo.getUser();
itemType = activityInfo.getItemType();
+ this.container = CONTAINER_WIDGETS_TRAY;
}
}
diff --git a/src/com/android/launcher3/widget/PendingAddWidgetInfo.java b/src/com/android/launcher3/widget/PendingAddWidgetInfo.java
index bc40484..bef9a08 100644
--- a/src/com/android/launcher3/widget/PendingAddWidgetInfo.java
+++ b/src/com/android/launcher3/widget/PendingAddWidgetInfo.java
@@ -15,6 +15,8 @@
*/
package com.android.launcher3.widget;
+import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_WIDGETS_TRAY;
+
import android.appwidget.AppWidgetHostView;
import android.os.Bundle;
@@ -50,6 +52,7 @@
spanY = i.spanY;
minSpanX = i.minSpanX;
minSpanY = i.minSpanY;
+ this.container = CONTAINER_WIDGETS_TRAY;
}
public WidgetAddFlowHandler getHandler() {
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 c51b87e..f0b7fae 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -362,7 +362,7 @@
public void checkForAnomaly() {
final String systemAnomalyMessage = getSystemAnomalyMessage();
if (systemAnomalyMessage != null) {
- Assert.fail(formatSystemHealthMessage(closeEvents(
+ Assert.fail(formatSystemHealthMessage(formatErrorWithEvents(
"http://go/tapl : Tests are broken by a non-Launcher system error: "
+ systemAnomalyMessage, false)));
}
@@ -424,7 +424,7 @@
return message;
}
- private String closeEvents(String message, boolean checkEvents) {
+ private String formatErrorWithEvents(String message, boolean checkEvents) {
if (sCheckingEvents) {
sCheckingEvents = false;
if (checkEvents) {
@@ -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();
}
@@ -451,7 +454,7 @@
private void fail(String message) {
checkForAnomaly();
- Assert.fail(formatSystemHealthMessage(closeEvents(
+ Assert.fail(formatSystemHealthMessage(formatErrorWithEvents(
"http://go/tapl : " + getContextDescription() + message
+ " (visible state: " + getVisibleStateMessage() + ")", true)));
}
@@ -629,8 +632,6 @@
* @return the Workspace object.
*/
public Workspace pressHome() {
- mInstrumentation.getUiAutomation().setOnAccessibilityEventListener(
- e -> Log.d("b/155926212", e.toString()));
try (LauncherInstrumentation.Closable e = eventsCheck()) {
waitForLauncherInitialized();
// Click home, then wait for any accessibility event, then wait until accessibility
@@ -639,9 +640,7 @@
// otherwise waitForIdle may return immediately in case when there was a big enough
// pause in accessibility events prior to pressing Home.
final String action;
- Log.d("b/155926212", "Before isLauncherVisible()");
final boolean launcherWasVisible = isLauncherVisible();
- Log.d("b/155926212", "After isLauncherVisible(): " + launcherWasVisible);
if (getNavigationModel() == NavigationModel.ZERO_BUTTON) {
checkForAnomaly();
@@ -697,8 +696,6 @@
"performed action to switch to Home - " + action)) {
return getWorkspace();
}
- } finally {
- mInstrumentation.getUiAutomation().setOnAccessibilityEventListener(null);
}
}
@@ -1312,7 +1309,8 @@
if (mOnLauncherCrashed != null) mOnLauncherCrashed.run();
checkForAnomaly();
Assert.fail(
- formatSystemHealthMessage(closeEvents("Launcher crashed", false)));
+ formatSystemHealthMessage(
+ formatErrorWithEvents("Launcher crashed", false)));
}
if (sCheckingEvents) {
@@ -1320,6 +1318,16 @@
if (mCheckEventsForSuccessfulGestures) {
final String message = sEventChecker.verify(WAIT_TIME_MS, true);
if (message != null) {
+ try {
+ Log.e("b/156287114", "Input:");
+ for (String line : mDevice.executeShellCommand("dumpsys input").split(
+ "\\n")) {
+ Log.d("b/156287114", line);
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+
checkForAnomaly();
Assert.fail(formatSystemHealthMessage(
"http://go/tapl : successful gesture produced " + message));