diff --git a/protos/launcher_log.proto b/protos/launcher_log.proto index 0fe53100b7..9423cb2642 100644 --- a/protos/launcher_log.proto +++ b/protos/launcher_log.proto @@ -63,14 +63,14 @@ message Target { // Note: proto does not support duplicate enum values, even if they belong to different enum type. // Hence "FROM" and "TO" prefix added. - enum FromFolderLabelState{ + enum FromFolderLabelState { FROM_FOLDER_LABEL_STATE_UNSPECIFIED = 0; FROM_EMPTY = 1; FROM_CUSTOM = 2; FROM_SUGGESTED = 3; } - enum ToFolderLabelState{ + enum ToFolderLabelState { TO_FOLDER_LABEL_STATE_UNSPECIFIED = 0; TO_SUGGESTION0_WITH_VALID_PRIMARY = 1; TO_SUGGESTION1_WITH_VALID_PRIMARY = 2; @@ -79,10 +79,14 @@ message Target { TO_SUGGESTION2_WITH_EMPTY_PRIMARY = 5; TO_SUGGESTION3_WITH_VALID_PRIMARY = 6; TO_SUGGESTION3_WITH_EMPTY_PRIMARY = 7; - TO_EMPTY_WITH_VALID_SUGGESTIONS = 8; + TO_EMPTY_WITH_VALID_SUGGESTIONS = 8 [deprecated = true]; + TO_EMPTY_WITH_VALID_PRIMARY = 15; + TO_EMPTY_WITH_VALID_SUGGESTIONS_AND_EMPTY_PRIMARY = 16; TO_EMPTY_WITH_EMPTY_SUGGESTIONS = 9; TO_EMPTY_WITH_SUGGESTIONS_DISABLED = 10; - TO_CUSTOM_WITH_VALID_SUGGESTIONS = 11; + TO_CUSTOM_WITH_VALID_SUGGESTIONS = 11 [deprecated = true]; + TO_CUSTOM_WITH_VALID_PRIMARY = 17; + TO_CUSTOM_WITH_VALID_SUGGESTIONS_AND_EMPTY_PRIMARY = 18; TO_CUSTOM_WITH_EMPTY_SUGGESTIONS = 12; TO_CUSTOM_WITH_SUGGESTIONS_DISABLED = 13; UNCHANGED = 14; diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/ComponentKeyMapper.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/ComponentKeyMapper.java index 0712285aec..d200868899 100644 --- a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/ComponentKeyMapper.java +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/ComponentKeyMapper.java @@ -18,10 +18,9 @@ package com.android.launcher3.appprediction; import static com.android.quickstep.InstantAppResolverImpl.COMPONENT_CLASS_MARKER; -import com.android.launcher3.AppInfo; -import com.android.launcher3.ItemInfoWithIcon; import com.android.launcher3.allapps.AllAppsStore; -import com.android.launcher3.shortcuts.ShortcutKey; +import com.android.launcher3.model.data.AppInfo; +import com.android.launcher3.model.data.ItemInfoWithIcon; import com.android.launcher3.util.ComponentKey; public class ComponentKeyMapper { @@ -57,9 +56,8 @@ public class ComponentKeyMapper { return item; } else if (getComponentClass().equals(COMPONENT_CLASS_MARKER)) { return mCache.getInstantApp(componentKey.componentName.getPackageName()); - } else if (componentKey instanceof ShortcutKey) { - return mCache.getShortcutInfo((ShortcutKey) componentKey); + } else { + return mCache.getShortcutInfo(componentKey); } - return null; } } diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/DynamicItemCache.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/DynamicItemCache.java index 54f58e2081..ab96b1340a 100644 --- a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/DynamicItemCache.java +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/DynamicItemCache.java @@ -38,13 +38,14 @@ import androidx.annotation.Nullable; import androidx.annotation.UiThread; import androidx.annotation.WorkerThread; -import com.android.launcher3.AppInfo; import com.android.launcher3.LauncherAppState; -import com.android.launcher3.WorkspaceItemInfo; import com.android.launcher3.allapps.AllAppsStore; import com.android.launcher3.icons.IconCache; +import com.android.launcher3.model.data.AppInfo; +import com.android.launcher3.model.data.WorkspaceItemInfo; import com.android.launcher3.shortcuts.ShortcutKey; import com.android.launcher3.shortcuts.ShortcutRequest; +import com.android.launcher3.util.ComponentKey; import com.android.launcher3.util.InstantAppResolver; import java.util.ArrayList; @@ -76,7 +77,7 @@ public class DynamicItemCache { private final Runnable mOnUpdateCallback; private final IconCache mIconCache; - private final Map mShortcuts; + private final Map mShortcuts; private final Map mInstantApps; public DynamicItemCache(Context context, Runnable onUpdateCallback) { @@ -230,7 +231,7 @@ public class DynamicItemCache { } @MainThread - public WorkspaceItemInfo getShortcutInfo(ShortcutKey key) { + public WorkspaceItemInfo getShortcutInfo(ComponentKey key) { return mShortcuts.get(key); } diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/InstantAppItemInfo.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/InstantAppItemInfo.java index 6e5f4617f4..6c4c601b45 100644 --- a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/InstantAppItemInfo.java +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/InstantAppItemInfo.java @@ -21,9 +21,9 @@ import static com.android.quickstep.InstantAppResolverImpl.COMPONENT_CLASS_MARKE import android.content.ComponentName; import android.content.Intent; -import com.android.launcher3.AppInfo; import com.android.launcher3.LauncherSettings; -import com.android.launcher3.WorkspaceItemInfo; +import com.android.launcher3.model.data.AppInfo; +import com.android.launcher3.model.data.WorkspaceItemInfo; public class InstantAppItemInfo extends AppInfo { @@ -44,7 +44,7 @@ public class InstantAppItemInfo extends AppInfo { workspaceItemInfo.status = WorkspaceItemInfo.FLAG_AUTOINSTALL_ICON | WorkspaceItemInfo.FLAG_RESTORE_STARTED | WorkspaceItemInfo.FLAG_SUPPORTS_WEB_UI; - workspaceItemInfo.intent.setPackage(componentName.getPackageName()); + workspaceItemInfo.getIntent().setPackage(componentName.getPackageName()); return workspaceItemInfo; } } diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionRowView.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionRowView.java index 8faec46580..d4cc1298c6 100644 --- a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionRowView.java +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionRowView.java @@ -38,18 +38,14 @@ import android.widget.LinearLayout; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import com.android.launcher3.AppInfo; import com.android.launcher3.BubbleTextView; import com.android.launcher3.DeviceProfile; import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener; -import com.android.launcher3.ItemInfo; -import com.android.launcher3.ItemInfoWithIcon; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherAppState; import com.android.launcher3.LauncherSettings; import com.android.launcher3.LauncherState; import com.android.launcher3.R; -import com.android.launcher3.WorkspaceItemInfo; import com.android.launcher3.allapps.AllAppsStore; import com.android.launcher3.allapps.FloatingHeaderRow; import com.android.launcher3.allapps.FloatingHeaderView; @@ -60,6 +56,10 @@ import com.android.launcher3.keyboard.FocusIndicatorHelper; import com.android.launcher3.keyboard.FocusIndicatorHelper.SimpleFocusIndicatorHelper; import com.android.launcher3.logging.StatsLogUtils.LogContainerProvider; import com.android.launcher3.model.AppLaunchTracker; +import com.android.launcher3.model.data.AppInfo; +import com.android.launcher3.model.data.ItemInfo; +import com.android.launcher3.model.data.ItemInfoWithIcon; +import com.android.launcher3.model.data.WorkspaceItemInfo; import com.android.launcher3.touch.ItemClickHandler; import com.android.launcher3.touch.ItemLongClickListener; import com.android.launcher3.userevent.nano.LauncherLogProto; 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 632b9b504a..8e55609e7d 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 @@ -28,8 +28,6 @@ import androidx.annotation.NonNull; import com.android.launcher3.InvariantDeviceProfile; import com.android.launcher3.InvariantDeviceProfile.OnIDPChangeListener; -import com.android.launcher3.ItemInfo; -import com.android.launcher3.ItemInfoWithIcon; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherAppState; import com.android.launcher3.LauncherSettings; @@ -40,6 +38,8 @@ import com.android.launcher3.allapps.AllAppsContainerView; import com.android.launcher3.allapps.AllAppsStore.OnUpdateListener; import com.android.launcher3.hybridhotseat.HotseatPredictionController; import com.android.launcher3.icons.IconCache.ItemInfoUpdateReceiver; +import com.android.launcher3.model.data.ItemInfo; +import com.android.launcher3.model.data.ItemInfoWithIcon; import com.android.launcher3.shortcuts.ShortcutKey; import com.android.launcher3.userevent.nano.LauncherLogProto; import com.android.launcher3.util.ComponentKey; 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 773c6c8a6a..78cc2dcde8 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 @@ -27,16 +27,16 @@ import android.view.View; import androidx.core.app.NotificationCompat; import com.android.launcher3.CellLayout; -import com.android.launcher3.FolderInfo; import com.android.launcher3.Hotseat; import com.android.launcher3.InvariantDeviceProfile; -import com.android.launcher3.ItemInfo; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherSettings; import com.android.launcher3.R; import com.android.launcher3.Workspace; -import com.android.launcher3.WorkspaceItemInfo; import com.android.launcher3.config.FeatureFlags; +import com.android.launcher3.model.data.FolderInfo; +import com.android.launcher3.model.data.ItemInfo; +import com.android.launcher3.model.data.WorkspaceItemInfo; import com.android.launcher3.uioverrides.QuickstepLauncher; import com.android.launcher3.util.ActivityTracker; import com.android.launcher3.util.GridOccupancy; @@ -200,6 +200,7 @@ public class HotseatEduController { pageId = LauncherSettings.Settings.call(mLauncher.getContentResolver(), LauncherSettings.Settings.METHOD_NEW_SCREEN_ID) .getInt(LauncherSettings.Settings.EXTRA_VALUE); + mNewScreens = IntArray.wrap(pageId); } for (int i = 0; i < mLauncher.getDeviceProfile().inv.numHotseatIcons; i++) { View child = mHotseat.getChildAt(i, 0); diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java index cb46c400e7..66c60bca1f 100644 --- a/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java @@ -16,8 +16,7 @@ package com.android.launcher3.hybridhotseat; import static com.android.launcher3.logging.LoggerUtils.newLauncherEvent; -import static com.android.launcher3.userevent.nano.LauncherLogProto.ControlType - .HYBRID_HOTSEAT_CANCELED; +import static com.android.launcher3.userevent.nano.LauncherLogProto.ControlType.HYBRID_HOTSEAT_CANCELED; import android.animation.PropertyValuesHolder; import android.content.Context; @@ -34,10 +33,11 @@ import com.android.launcher3.DeviceProfile; import com.android.launcher3.Insettable; import com.android.launcher3.Launcher; import com.android.launcher3.R; -import com.android.launcher3.WorkspaceItemInfo; +import com.android.launcher3.Workspace; import com.android.launcher3.anim.Interpolators; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.logging.UserEventDispatcher; +import com.android.launcher3.model.data.WorkspaceItemInfo; import com.android.launcher3.uioverrides.PredictedAppIcon; import com.android.launcher3.userevent.nano.LauncherLogProto; import com.android.launcher3.views.AbstractSlideInView; @@ -153,6 +153,12 @@ public class HotseatEduDialog extends AbstractSlideInView implements Insettable private void logUserAction(boolean migrated, int pageIndex) { LauncherLogProto.Action action = new LauncherLogProto.Action(); LauncherLogProto.Target target = new LauncherLogProto.Target(); + + int hotseatItemsCount = mLauncher.getHotseat().getShortcutsAndWidgets().getChildCount(); + // -1 to exclude smart space + int workspaceItemCount = mLauncher.getWorkspace().getScreenWithId( + Workspace.FIRST_SCREEN_ID).getShortcutsAndWidgets().getChildCount() - 1; + action.type = LauncherLogProto.Action.Type.TOUCH; action.touch = LauncherLogProto.Action.Touch.TAP; target.containerType = LauncherLogProto.ContainerType.TIP; @@ -162,7 +168,7 @@ public class HotseatEduDialog extends AbstractSlideInView implements Insettable target.rank = MIGRATION_EXPERIMENT_IDENTIFIER; // encoding migration type on pageIndex target.pageIndex = pageIndex; - target.cardinality = HotseatPredictionController.MAX_ITEMS_FOR_MIGRATION; + target.cardinality = (workspaceItemCount * 1000) + hotseatItemsCount; LauncherLogProto.LauncherEvent event = newLauncherEvent(action, target); UserEventDispatcher.newInstance(getContext()).dispatchUserEvent(event, null); } 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 9bc097527c..1aff8e9799 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 @@ -31,6 +31,7 @@ import android.app.prediction.AppTargetEvent; import android.app.prediction.AppTargetId; import android.content.ComponentName; import android.os.Bundle; +import android.os.Process; import android.provider.DeviceConfig; import android.util.Log; import android.view.View; @@ -39,22 +40,18 @@ import android.view.ViewGroup; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import com.android.launcher3.AppInfo; import com.android.launcher3.BubbleTextView; +import com.android.launcher3.CellLayout; import com.android.launcher3.DragSource; import com.android.launcher3.DropTarget; -import com.android.launcher3.FolderInfo; import com.android.launcher3.Hotseat; import com.android.launcher3.InvariantDeviceProfile; -import com.android.launcher3.ItemInfo; -import com.android.launcher3.ItemInfoWithIcon; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherSettings; import com.android.launcher3.LauncherState; import com.android.launcher3.R; import com.android.launcher3.Utilities; import com.android.launcher3.Workspace; -import com.android.launcher3.WorkspaceItemInfo; import com.android.launcher3.allapps.AllAppsStore; import com.android.launcher3.anim.AnimationSuccessListener; import com.android.launcher3.appprediction.ComponentKeyMapper; @@ -64,6 +61,12 @@ import com.android.launcher3.dragndrop.DragOptions; import com.android.launcher3.icons.IconCache; import com.android.launcher3.logging.FileLog; import com.android.launcher3.logging.UserEventDispatcher; +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; import com.android.launcher3.touch.ItemLongClickListener; @@ -76,6 +79,7 @@ import com.android.launcher3.util.ComponentKey; 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; @@ -91,18 +95,19 @@ public class HotseatPredictionController implements DragController.DragListener, private static final String TAG = "PredictiveHotseat"; private static final boolean DEBUG = false; - public static final int MAX_ITEMS_FOR_MIGRATION = DeviceConfig.getInt( - DeviceFlag.NAMESPACE_LAUNCHER, "max_homepage_items_for_migration", 5); - //TODO: replace this with AppTargetEvent.ACTION_UNPIN (b/144119543) private static final int APPTARGET_ACTION_UNPIN = 4; + private static final String PREDICTED_ITEMS_CACHE_KEY = "predicted_item_keys"; + 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; @@ -119,6 +124,7 @@ public class HotseatPredictionController implements DragController.DragListener, private AllAppsStore mAllAppsStore; private AnimatorSet mIconRemoveAnimators; private boolean mUIUpdatePaused = false; + private boolean mRequiresCacheUpdate = false; private HotseatEduController mHotseatEduController; @@ -145,6 +151,7 @@ public class HotseatPredictionController implements DragController.DragListener, if (mHotseat.isAttachedToWindow()) { onViewAttachedToWindow(mHotseat); } + showCachedItems(); } /** @@ -294,17 +301,57 @@ public class HotseatPredictionController implements DragController.DragListener, mAppPredictor.requestPredictionUpdate(); } + private void showCachedItems() { + ArrayList componentKeys = getCachedComponentKeys(); + mComponentKeyMappers.clear(); + for (ComponentKey key : componentKeys) { + mComponentKeyMappers.add(new ComponentKeyMapper(key, mDynamicItemCache)); + } + updateDependencies(); + fillGapsWithPrediction(); + } + 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 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 getPinEventsForViewGroup(ArrayList 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 getPinnedAppTargetsInViewGroup(ViewGroup viewGroup) { ArrayList pinnedApps = new ArrayList<>(); for (int i = 0; i < viewGroup.getChildCount(); i++) { @@ -320,6 +367,7 @@ public class HotseatPredictionController implements DragController.DragListener, private void setPredictedApps(List appTargets) { mComponentKeyMappers.clear(); StringBuilder predictionLog = new StringBuilder("predictedApps: [\n"); + ArrayList componentKeys = new ArrayList<>(); for (AppTarget appTarget : appTargets) { ComponentKey key; if (appTarget.getShortcutInfo() != null) { @@ -328,6 +376,7 @@ public class HotseatPredictionController implements DragController.DragListener, key = new ComponentKey(new ComponentName(appTarget.getPackageName(), appTarget.getClassName()), appTarget.getUser()); } + componentKeys.add(key); predictionLog.append(key.toString()); predictionLog.append(",rank:"); predictionLog.append(appTarget.getRank()); @@ -342,6 +391,35 @@ public class HotseatPredictionController implements DragController.DragListener, } else if (mHotseatEduController != null) { mHotseatEduController.setPredictedApps(mapToWorkspaceItemInfo(mComponentKeyMappers)); } + // should invalidate cache if AiAi sends empty list of AppTargets + if (appTargets.isEmpty()) { + mRequiresCacheUpdate = true; + } + cachePredictionComponentKeys(componentKeys); + } + + private void cachePredictionComponentKeys(ArrayList componentKeys) { + if (!mRequiresCacheUpdate) return; + StringBuilder builder = new StringBuilder(); + for (ComponentKey componentKey : componentKeys) { + builder.append(componentKey); + builder.append("\n"); + } + mLauncher.getDevicePrefs().edit().putString(PREDICTED_ITEMS_CACHE_KEY, + builder.toString()).apply(); + mRequiresCacheUpdate = false; + } + + private ArrayList getCachedComponentKeys() { + String cachedBlob = mLauncher.getDevicePrefs().getString(PREDICTED_ITEMS_CACHE_KEY, ""); + ArrayList results = new ArrayList<>(); + for (String line : cachedBlob.split("\n")) { + ComponentKey key = ComponentKey.fromString(line); + if (key != null) { + results.add(key); + } + } + return results; } private void updateDependencies() { @@ -367,6 +445,7 @@ public class HotseatPredictionController implements DragController.DragListener, icon.pin(workspaceItemInfo); AppTarget appTarget = getAppTargetFromItemInfo(workspaceItemInfo); notifyItemAction(appTarget, APP_LOCATION_HOTSEAT, AppTargetEvent.ACTION_PIN); + mRequiresCacheUpdate = true; } private List mapToWorkspaceItemInfo( @@ -533,6 +612,7 @@ public class HotseatPredictionController implements DragController.DragListener, } mDragObject = null; fillGapsWithPrediction(true, this::removeOutlineDrawings); + mRequiresCacheUpdate = true; } @Nullable @@ -606,6 +686,9 @@ public class HotseatPredictionController implements DragController.DragListener, if (isReady()) return; int hotseatItemsCount = mHotseat.getShortcutsAndWidgets().getChildCount(); + int maxItems = DeviceConfig.getInt( + DeviceFlag.NAMESPACE_LAUNCHER, "max_homepage_items_for_migration", 5); + // -1 to exclude smart space int workspaceItemCount = mLauncher.getWorkspace().getScreenWithId( Workspace.FIRST_SCREEN_ID).getShortcutsAndWidgets().getChildCount() - 1; @@ -614,7 +697,7 @@ public class HotseatPredictionController implements DragController.DragListener, // open spots in their hotseat and have more than maxItems in their hotseat + workspace if (hotseatItemsCount == mHotSeatItemsCount && workspaceItemCount + hotseatItemsCount - > MAX_ITEMS_FOR_MIGRATION) { + > maxItems) { mLauncher.getSharedPrefs().edit().putBoolean(HotseatEduController.KEY_HOTSEAT_EDU_SEEN, true).apply(); @@ -626,8 +709,8 @@ public class HotseatPredictionController implements DragController.DragListener, // temporarily encode details in log target (go/hotseat_migration) target.rank = 2; - target.cardinality = MAX_ITEMS_FOR_MIGRATION; - target.pageIndex = (workspaceItemCount * 1000) + hotseatItemsCount; + target.cardinality = (workspaceItemCount * 1000) + hotseatItemsCount; + target.pageIndex = maxItems; LauncherLogProto.LauncherEvent event = newLauncherEvent(action, target); UserEventDispatcher.newInstance(mLauncher).dispatchUserEvent(event, null); @@ -689,4 +772,52 @@ public class HotseatPredictionController implements DragController.DragListener, 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/uioverrides/PredictedAppIcon.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/PredictedAppIcon.java index 304c77f3da..8af26c6e8b 100644 --- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/PredictedAppIcon.java +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/PredictedAppIcon.java @@ -33,14 +33,14 @@ import androidx.core.graphics.ColorUtils; import com.android.launcher3.CellLayout; import com.android.launcher3.DeviceProfile; -import com.android.launcher3.ItemInfo; import com.android.launcher3.Launcher; import com.android.launcher3.R; -import com.android.launcher3.WorkspaceItemInfo; import com.android.launcher3.accessibility.LauncherAccessibilityDelegate; import com.android.launcher3.graphics.IconPalette; import com.android.launcher3.hybridhotseat.HotseatPredictionController; import com.android.launcher3.icons.IconNormalizer; +import com.android.launcher3.model.data.ItemInfo; +import com.android.launcher3.model.data.WorkspaceItemInfo; import com.android.launcher3.touch.ItemClickHandler; import com.android.launcher3.touch.ItemLongClickListener; import com.android.launcher3.views.DoubleShadowBubbleTextView; @@ -136,9 +136,11 @@ public class PredictedAppIcon extends DoubleShadowBubbleTextView implements @Override public void addSupportedAccessibilityActions(AccessibilityNodeInfo accessibilityNodeInfo) { - accessibilityNodeInfo.addAction( - new AccessibilityNodeInfo.AccessibilityAction(PIN_PREDICTION, - getContext().getText(R.string.pin_prediction))); + if (!mIsPinned) { + accessibilityNodeInfo.addAction( + new AccessibilityNodeInfo.AccessibilityAction(PIN_PREDICTION, + getContext().getText(R.string.pin_prediction))); + } } @Override 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 a02d9c365c..1110df7179 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 @@ -28,14 +28,14 @@ import androidx.annotation.Nullable; import com.android.launcher3.BaseQuickstepLauncher; import com.android.launcher3.DeviceProfile; -import com.android.launcher3.ItemInfo; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherState; -import com.android.launcher3.WorkspaceItemInfo; import com.android.launcher3.anim.AnimatorPlaybackController; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.folder.Folder; import com.android.launcher3.hybridhotseat.HotseatPredictionController; +import com.android.launcher3.model.data.ItemInfo; +import com.android.launcher3.model.data.WorkspaceItemInfo; import com.android.launcher3.popup.SystemShortcut; import com.android.launcher3.uioverrides.touchcontrollers.FlingAndHoldTouchController; import com.android.launcher3.uioverrides.touchcontrollers.LandscapeEdgeSwipeController; 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 f81c56fc34..fadde374ba 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java @@ -46,11 +46,8 @@ import com.android.launcher3.R; import com.android.launcher3.Utilities; import com.android.launcher3.anim.AnimationSuccessListener; import com.android.launcher3.anim.AnimatorPlaybackController; -import com.android.launcher3.model.PagedViewOrientedState; -import com.android.launcher3.states.RotationHelper; import com.android.launcher3.testing.TestProtocol; import com.android.launcher3.touch.PagedOrientationHandler; -import com.android.launcher3.touch.PortraitPagedViewHandler; import com.android.launcher3.util.VibratorWrapper; import com.android.launcher3.views.FloatingIconView; import com.android.quickstep.BaseActivityInterface.HomeAnimationFactory; @@ -59,6 +56,7 @@ import com.android.quickstep.util.ActiveGestureLog; import com.android.quickstep.util.ActivityInitListener; import com.android.quickstep.util.AppWindowAnimationHelper; import com.android.quickstep.util.AppWindowAnimationHelper.TransformParams; +import com.android.quickstep.util.RecentsOrientedState; import com.android.quickstep.util.RectFSpringAnim; import com.android.quickstep.views.RecentsView; import com.android.quickstep.views.TaskView; @@ -127,7 +125,7 @@ public abstract class BaseSwipeUpHandler resultCallback) { - if (TestProtocol.sDebugTracing) { - Log.d(TestProtocol.NO_START_FROM_RECENTS, "startNewTask1"); - } // Launch the task user scrolled to (mRecentsView.getNextPage()). if (ENABLE_QUICKSTEP_LIVE_TILE.get()) { // We finish recents animation inside launchTask() when live tile is enabled. mRecentsView.getNextPageTaskView().launchTask(false /* animate */, true /* freezeTaskList */); } else { - if (TestProtocol.sDebugTracing) { - Log.d(TestProtocol.NO_START_FROM_RECENTS, "startNewTask2"); - } int taskId = mRecentsView.getNextPageTaskView().getTask().key.id; if (!mCanceled) { TaskView nextTask = mRecentsView.getTaskView(taskId); @@ -337,9 +329,11 @@ public abstract class BaseSwipeUpHandler windowAnim.addListener(new AnimationSuccessListener() { @Override public void onAnimationSuccess(Animator animator) { - if (TestProtocol.sDebugTracing) { - Log.d(TestProtocol.NO_START_FROM_RECENTS, "onAnimationSuccess"); - } if (mRecentsAnimationController == null) { // If the recents animation is interrupted, we still end the running // animation (not canceled) so this is still called. In that case, we can @@ -1210,9 +1207,6 @@ public class LauncherSwipeHandler } private void switchToScreenshot() { - if (TestProtocol.sDebugTracing) { - Log.d(TestProtocol.NO_START_FROM_RECENTS, "switchToScreenshot"); - } final int runningTaskId = mGestureState.getRunningTaskId(); if (ENABLE_QUICKSTEP_LIVE_TILE.get()) { if (mRecentsAnimationController != null) { diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskShortcutFactory.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskShortcutFactory.java index 9ba2e5a684..021d39dd6e 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskShortcutFactory.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskShortcutFactory.java @@ -35,8 +35,8 @@ import android.view.View; import com.android.launcher3.BaseDraggingActivity; import com.android.launcher3.DeviceProfile; import com.android.launcher3.R; -import com.android.launcher3.WorkspaceItemInfo; import com.android.launcher3.model.WellbeingModel; +import com.android.launcher3.model.data.WorkspaceItemInfo; import com.android.launcher3.popup.SystemShortcut; import com.android.launcher3.popup.SystemShortcut.AppInfo; import com.android.launcher3.userevent.nano.LauncherLogProto; @@ -71,7 +71,7 @@ public interface TaskShortcutFactory { WorkspaceItemInfo dummyInfo = new WorkspaceItemInfo(); dummyInfo.intent = new Intent(); ComponentName component = task.getTopComponent(); - dummyInfo.intent.setComponent(component); + dummyInfo.getIntent().setComponent(component); dummyInfo.user = UserHandle.of(task.key.userId); dummyInfo.title = TaskUtils.getTitle(view.getContext(), task); return dummyInfo; diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskViewUtils.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskViewUtils.java index 6a3e1feddb..47c07afa80 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskViewUtils.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskViewUtils.java @@ -33,8 +33,8 @@ import android.view.View; import com.android.launcher3.BaseActivity; import com.android.launcher3.BaseDraggingActivity; -import com.android.launcher3.ItemInfo; import com.android.launcher3.Utilities; +import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.statehandlers.DepthController; import com.android.quickstep.util.AppWindowAnimationHelper; import com.android.quickstep.util.MultiValueUpdateListener; 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 c889632a48..2c2feb21c4 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java @@ -26,11 +26,12 @@ import static com.android.quickstep.GestureState.DEFAULT_STATE; import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_INPUT_MONITOR; import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SYSUI_PROXY; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_TRACING_ENABLED; -import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.ACTIVITY_TYPE_ASSISTANT; import android.annotation.TargetApi; import android.app.ActivityManager; import android.app.ActivityManager.RunningTaskInfo; +import android.app.PendingIntent; +import android.app.RemoteAction; import android.app.Service; import android.content.ComponentName; import android.content.Context; @@ -38,6 +39,7 @@ import android.content.Intent; import android.content.SharedPreferences; import android.content.res.Configuration; import android.graphics.Region; +import android.graphics.drawable.Icon; import android.os.Build; import android.os.Bundle; import android.os.IBinder; @@ -46,6 +48,7 @@ import android.util.Log; import android.view.Choreographer; import android.view.InputEvent; import android.view.MotionEvent; +import android.view.accessibility.AccessibilityManager; import androidx.annotation.BinderThread; import androidx.annotation.Nullable; @@ -53,8 +56,8 @@ import androidx.annotation.UiThread; import androidx.annotation.WorkerThread; import com.android.launcher3.BaseDraggingActivity; +import com.android.launcher3.R; import com.android.launcher3.Utilities; -import com.android.launcher3.allapps.DiscoveryBounce; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.logging.UserEventDispatcher; import com.android.launcher3.model.AppLaunchTracker; @@ -64,6 +67,7 @@ import com.android.launcher3.testing.TestProtocol; import com.android.launcher3.tracing.nano.LauncherTraceProto; import com.android.launcher3.tracing.nano.TouchInteractionServiceProto; import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper; +import com.android.launcher3.util.OnboardingPrefs; import com.android.launcher3.util.TraceHelper; import com.android.quickstep.inputconsumers.AccessibilityInputConsumer; import com.android.quickstep.inputconsumers.AssistantInputConsumer; @@ -126,6 +130,12 @@ public class TouchInteractionService extends Service implements PluginListener mAM.getRunningTask(true /* filterOnlyVisibleRecents */))); + () -> mAM.getRunningTask(false /* filterOnlyVisibleRecents */))); return gestureState; } @@ -505,6 +539,8 @@ public class TouchInteractionService extends Service implements PluginListener mAM.getRunningTask(true /* filterOnlyVisibleRecents */))); + ComponentName homeComponent = mOverviewComponentObserver.getHomeIntent().getComponent(); + ComponentName runningComponent = + gestureState.getRunningTask().baseIntent.getComponent(); + forceOverviewInputConsumer = + runningComponent != null && runningComponent.equals(homeComponent); + } if (previousGestureState.getFinishingRecentsAnimationTaskId() > 0) { // If the finish animation was interrupted, then continue using the other activity input @@ -678,15 +722,21 @@ public class TouchInteractionService extends Service implements PluginListener a; private TargetAlphaProvider mBaseAlphaCallback = (t, a) -> 1; - public AppWindowAnimationHelper(PagedViewOrientedState orientedState, Context context) { + public AppWindowAnimationHelper(RecentsOrientedState orientedState, Context context) { Resources res = context.getResources(); mOrientedState = orientedState; mWindowCornerRadius = getWindowCornerRadius(res); diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/ClearAllButton.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/ClearAllButton.java index 763f5beb10..e455939f0e 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/ClearAllButton.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/ClearAllButton.java @@ -78,21 +78,6 @@ public class ClearAllButton extends Button implements PageCallbacks { } } - public void onLayoutChanged() { - if (mParent == null) { - return; - } - setRotation(mParent.getPagedOrientationHandler().getDegreesRotated()); - } - - public void setRtl(boolean rtl) { - if (mIsRtl == rtl) { - return; - } - mIsRtl = rtl; - invalidate(); - } - public void setVisibilityAlpha(float alpha) { if (mVisibilityAlpha != alpha) { mVisibilityAlpha = alpha; 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 e3b3102562..a18f7bae05 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 @@ -100,8 +100,8 @@ import com.android.launcher3.anim.SpringProperty; import com.android.launcher3.compat.AccessibilityManagerCompat; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.statehandlers.DepthController; -import com.android.launcher3.states.RotationHelper; import com.android.launcher3.testing.TestProtocol; +import com.android.launcher3.touch.PagedOrientationHandler; import com.android.launcher3.touch.PagedOrientationHandler.CurveProperties; import com.android.launcher3.userevent.nano.LauncherLogProto; import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction; @@ -122,6 +122,7 @@ import com.android.quickstep.TaskUtils; import com.android.quickstep.ViewUtils; import com.android.quickstep.util.AppWindowAnimationHelper; import com.android.quickstep.util.LayoutUtils; +import com.android.quickstep.util.RecentsOrientedState; import com.android.systemui.plugins.ResourceProvider; import com.android.systemui.shared.recents.IPinnedStackAnimationListener; import com.android.systemui.shared.recents.model.Task; @@ -171,6 +172,8 @@ public abstract class RecentsView extends PagedView impl } }; + protected final RecentsOrientedState mOrientationState = new RecentsOrientedState(); + private OrientationEventListener mOrientationListener; private int mPreviousRotation; protected RecentsAnimationController mRecentsAnimationController; @@ -383,7 +386,7 @@ public abstract class RecentsView extends PagedView impl mOrientationListener = new OrientationEventListener(getContext()) { @Override public void onOrientationChanged(int i) { - int rotation = RotationHelper.getRotationFromDegrees(i); + int rotation = RecentsOrientedState.getRotationForUserDegreesRotated(i); if (mPreviousRotation != rotation) { animateRecentsRotationInPlace(rotation); if (rotation == 0) { @@ -505,6 +508,29 @@ public abstract class RecentsView extends PagedView impl mHasVisibleTaskData.delete(taskView.getTask().key.id); mTaskViewPool.recycle(taskView); } + updateTaskStartIndex(child); + } + + @Override + public void onViewAdded(View child) { + super.onViewAdded(child); + child.setAlpha(mContentAlpha); + // RecentsView is set to RTL in the constructor when system is using LTR. Here we set the + // child direction back to match system settings. + child.setLayoutDirection(mIsRtl ? View.LAYOUT_DIRECTION_LTR : View.LAYOUT_DIRECTION_RTL); + updateTaskStartIndex(child); + } + + private void updateTaskStartIndex(View affectingView) { + if (!(affectingView instanceof TaskView) && !(affectingView instanceof ClearAllButton)) { + int childCount = getChildCount(); + + mTaskViewStartIndex = 0; + while (mTaskViewStartIndex < childCount + && !(getChildAt(mTaskViewStartIndex) instanceof TaskView)) { + mTaskViewStartIndex++; + } + } } public boolean isTaskViewVisible(TaskView tv) { @@ -661,7 +687,7 @@ public abstract class RecentsView extends PagedView impl final int pageIndex = requiredTaskCount - i - 1 + mTaskViewStartIndex; final Task task = tasks.get(i); final TaskView taskView = (TaskView) getChildAt(pageIndex); - taskView.bind(task, mLayoutRotation); + taskView.bind(task, mOrientationState); } if (mNextPage == INVALID_PAGE) { @@ -710,11 +736,15 @@ public abstract class RecentsView extends PagedView impl int currentIndex = indexOfChild(taskView); TaskView previousTask = getTaskViewAt(currentIndex - 1); TaskView nextTask = getTaskViewAt(currentIndex + 1); + float alpha = isTaskOverlayModal ? 0.0f : 1.0f; if (previousTask != null) { - previousTask.setVisibility(isTaskOverlayModal ? View.INVISIBLE : View.VISIBLE); + previousTask.animate().alpha(alpha) + .translationX(isTaskOverlayModal ? previousTask.getWidth() / 2 : 0); } if (nextTask != null) { - nextTask.setVisibility(isTaskOverlayModal ? View.INVISIBLE : View.VISIBLE); + nextTask.animate().alpha(alpha) + .translationX(isTaskOverlayModal ? -nextTask.getWidth() / 2 : 0); + } } @@ -819,7 +849,8 @@ public abstract class RecentsView extends PagedView impl final int pageCount = getPageCount(); for (int i = 0; i < pageCount; i++) { View page = getPageAt(i); - mScrollState.updateInterpolation(mOrientationHandler.getChildStart(page), mPageSpacing); + mScrollState.updateInterpolation(mOrientationHandler.getChildStartWithTranslation(page), + mPageSpacing); ((PageCallbacks) page).onPageScroll(mScrollState); } } @@ -973,7 +1004,7 @@ public abstract class RecentsView extends PagedView impl AnimatorSet pa = setRecentsChangedOrientation(true); pa.addListener(AnimationSuccessListener.forRunnable(() -> { - updateLayoutRotation(newRotation); + setLayoutRotation(newRotation, mOrientationState.getDisplayRotation()); mActivity.getDragLayer().recreateControllers(); rotateAllChildTasks(); setRecentsChangedOrientation(false).start(); @@ -999,8 +1030,7 @@ public abstract class RecentsView extends PagedView impl private void rotateAllChildTasks() { for (int i = 0; i < getTaskViewCount(); i++) { - TaskView taskView = getTaskViewAt(i); - taskView.setOverviewRotation(mLayoutRotation); + getTaskViewAt(i).setOrientationState(mOrientationState); } } @@ -1041,7 +1071,7 @@ public abstract class RecentsView extends PagedView impl new ComponentName(getContext(), getClass()), 0, 0), null, null, "", "", 0, 0, false, true, false, false, new ActivityManager.TaskDescription(), 0, new ComponentName("", ""), false); - taskView.bind(mTmpRunningTask, mLayoutRotation); + taskView.bind(mTmpRunningTask, mOrientationState); } boolean runningTaskTileHidden = mRunningTaskTileHidden; @@ -1207,7 +1237,7 @@ public abstract class RecentsView extends PagedView impl /** * Updates linearInterpolation for the provided child position */ - public void updateInterpolation(int childStart, int pageSpacing) { + public void updateInterpolation(float childStart, int pageSpacing) { float pageCenter = childStart + halfPageSize; float distanceFromScreenCenter = screenCenter - pageCenter; float distanceToReachEdge = halfScreenSize + halfPageSize + pageSpacing; @@ -1530,22 +1560,28 @@ public abstract class RecentsView extends PagedView impl } } - @Override public void setLayoutRotation(int touchRotation, int displayRotation) { - if (!FeatureFlags.ENABLE_FIXED_ROTATION_TRANSFORM.get()) { - return; + if (mOrientationState.update(touchRotation, displayRotation)) { + mOrientationHandler = mOrientationState.getOrientationHandler(); + mIsRtl = mOrientationHandler.getRecentsRtlSetting(getResources()); + setLayoutDirection(mIsRtl ? View.LAYOUT_DIRECTION_RTL : View.LAYOUT_DIRECTION_LTR); + mClearAllButton.setRotation(mOrientationHandler.getDegreesRotated()); + requestLayout(); } - - super.setLayoutRotation(touchRotation, displayRotation); - mClearAllButton.onLayoutChanged(); - mIsRtl = mOrientationHandler.getRecentsRtlSetting(getResources()); - setLayoutDirection(mIsRtl ? View.LAYOUT_DIRECTION_RTL : View.LAYOUT_DIRECTION_LTR); } - @Override - public void onViewAdded(View child) { - super.onViewAdded(child); - child.setAlpha(mContentAlpha); + public void disableMultipleLayoutRotations(boolean disable) { + mOrientationState.disableMultipleOrientations(disable); + mOrientationHandler = mOrientationState.getOrientationHandler(); + requestLayout(); + } + + public RecentsOrientedState getPagedViewOrientedState() { + return mOrientationState; + } + + public PagedOrientationHandler getPagedOrientationHandler() { + return mOrientationHandler; } @Nullable @@ -1986,8 +2022,7 @@ public abstract class RecentsView extends PagedView impl public Consumer getEventDispatcher(float navbarRotation) { float degreesRotated; if (navbarRotation == 0) { - degreesRotated = mOrientationState.areMultipleLayoutOrientationsDisabled() ? 0 : - RotationHelper.getDegreesFromRotation(mLayoutRotation); + degreesRotated = mOrientationState.getTouchRotationDegrees(); } else { degreesRotated = -navbarRotation; } @@ -2001,14 +2036,14 @@ public abstract class RecentsView extends PagedView impl return e -> { if (navbarRotation != 0 && !mOrientationState.areMultipleLayoutOrientationsDisabled()) { - RotationHelper.transformEventForNavBar(e, true); + mOrientationState.flipVertical(e); super.onTouchEvent(e); - RotationHelper.transformEventForNavBar(e, false); + mOrientationState.flipVertical(e); return; } - RotationHelper.transformEvent(-degreesRotated, e, true); + mOrientationState.transformEvent(-degreesRotated, e, true); super.onTouchEvent(e); - RotationHelper.transformEvent(-degreesRotated, e, false); + mOrientationState.transformEvent(-degreesRotated, e, false); }; } @@ -2056,37 +2091,11 @@ public abstract class RecentsView extends PagedView impl } } - @Override - public void addView(View child, int index) { - // RecentsView is set to RTL in the constructor when system is using LTR. Here we set the - // child direction back to match system settings. - child.setLayoutDirection( - Utilities.isRtl(getResources()) - ? View.LAYOUT_DIRECTION_RTL : View.LAYOUT_DIRECTION_LTR); - super.addView(child, index); - if (isExtraCardView(child, index)) { - mTaskViewStartIndex++; - } - } - - @Override - public void removeView(View view) { - if (isExtraCardView(view, indexOfChild(view))) { - mTaskViewStartIndex--; - } - super.removeView(view); - } - @Nullable protected DepthController getDepthController() { return null; } - private boolean isExtraCardView(View view, int index) { - return !(view instanceof TaskView) && !(view instanceof ClearAllButton) - && index <= mTaskViewStartIndex; - } - /** * Used to register callbacks for when our empty message state changes. * @@ -2131,13 +2140,15 @@ public abstract class RecentsView extends PagedView impl && SysUINavigationMode.removeShelfFromOverview(mActivity)) { mActionsView = ((ViewGroup) getParent()).findViewById(R.id.overview_actions_view); if (mActionsView != null) { - Rect rect = new Rect(); - getTaskSize(rect); InsettableFrameLayout.LayoutParams layoutParams = - new InsettableFrameLayout.LayoutParams(rect.width(), + new InsettableFrameLayout.LayoutParams(LayoutParams.MATCH_PARENT, getResources().getDimensionPixelSize( R.dimen.overview_actions_height)); layoutParams.gravity = Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL; + int margin = getResources().getDimensionPixelSize( + R.dimen.overview_actions_horizontal_margin); + layoutParams.setMarginStart(margin); + layoutParams.setMarginEnd(margin); mActionsView.setLayoutParams(layoutParams); showActionsView(); } diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java index 7010f9ab8a..470b720f5e 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java @@ -64,7 +64,6 @@ import com.android.launcher3.anim.PendingAnimation; import com.android.launcher3.logger.LauncherAtom; import com.android.launcher3.logging.UserEventDispatcher; import com.android.launcher3.popup.SystemShortcut; -import com.android.launcher3.states.RotationHelper; import com.android.launcher3.testing.TestLogging; import com.android.launcher3.testing.TestProtocol; import com.android.launcher3.touch.PagedOrientationHandler; @@ -79,6 +78,7 @@ import com.android.quickstep.TaskOverlayFactory; import com.android.quickstep.TaskThumbnailCache; import com.android.quickstep.TaskUtils; import com.android.quickstep.util.LayoutUtils; +import com.android.quickstep.util.RecentsOrientedState; import com.android.quickstep.util.TaskCornerRadius; import com.android.quickstep.views.RecentsView.PageCallbacks; import com.android.quickstep.views.RecentsView.ScrollState; @@ -119,19 +119,6 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable { private static final List SYSTEM_GESTURE_EXCLUSION_RECT = Collections.singletonList(new Rect()); - public static final FloatProperty FULLSCREEN_PROGRESS = - new FloatProperty("fullscreenProgress") { - @Override - public void setValue(TaskView taskView, float v) { - taskView.setFullscreenProgress(v); - } - - @Override - public Float get(TaskView taskView) { - return taskView.mFullscreenProgress; - } - }; - private static final FloatProperty FOCUS_TRANSITION = new FloatProperty("focusTransition") { @Override @@ -180,6 +167,7 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable { private float mStableAlpha = 1; private boolean mShowScreenshot; + private boolean mRunningModalAnimation = false; // The current background requests to load the task thumbnail and icon private TaskThumbnailCache.ThumbnailLoadRequest mThumbnailLoadRequest; @@ -262,17 +250,39 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable { /** Updates UI based on whether the task is modal. */ public void updateUiForModalTask() { boolean isOverlayModal = isTaskOverlayModal(); + mRunningModalAnimation = true; if (getRecentsView() != null) { getRecentsView().updateUiForModalTask(this, isOverlayModal); } - // Hide footers when overlay is modal. + + // Hides footers and icon when overlay is modal. if (isOverlayModal) { for (FooterWrapper footer : mFooters) { if (footer != null) { footer.animateHide(); } } + mIconView.animate().alpha(0.0f); + } else { + mIconView.animate().alpha(1.0f); } + + // Sets animations for modal UI. We will remove the margins to zoom in the snapshot. + float topMargin = getResources().getDimension(R.dimen.task_thumbnail_top_margin); + float bottomMargin = + getResources().getDimension(R.dimen.task_thumbnail_bottom_margin_with_actions); + float newHeight = mSnapshotView.getHeight() + topMargin + bottomMargin; + float scale = isOverlayModal ? newHeight / mSnapshotView.getHeight() : 1.0f; + float centerDifference = (bottomMargin - topMargin) / 2; + float translationY = isOverlayModal ? centerDifference : 0; + this.animate().scaleX(scale).scaleY(scale).translationY(translationY) + .withEndAction(new Runnable() { + @Override + public void run() { + setCurveScale(scale); + mRunningModalAnimation = false; + } + }); } public TaskMenuView getMenuView() { @@ -286,11 +296,11 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable { /** * Updates this task view to the given {@param task}. */ - public void bind(Task task, int recentsRotation) { + public void bind(Task task, RecentsOrientedState orientedState) { cancelPendingLoadTasks(); mTask = task; mSnapshotView.bind(task); - setOverviewRotation(recentsRotation); + setOrientationState(orientedState); } public Task getTask() { @@ -459,14 +469,15 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable { } } - void setOverviewRotation(int iconRotation) { - PagedOrientationHandler orientationHandler = getRecentsView().getPagedOrientationHandler(); + public void setOrientationState(RecentsOrientedState orientationState) { + int iconRotation = orientationState.getTouchRotation(); + PagedOrientationHandler orientationHandler = orientationState.getOrientationHandler(); boolean isRtl = orientationHandler.getRecentsRtlSetting(getResources()); LayoutParams snapshotParams = (LayoutParams) mSnapshotView.getLayoutParams(); snapshotParams.bottomMargin = LayoutUtils.thumbnailBottomMargin(getContext()); int thumbnailPadding = (int) getResources().getDimension(R.dimen.task_thumbnail_top_margin); LayoutParams iconParams = (LayoutParams) mIconView.getLayoutParams(); - int rotation = RotationHelper.getDegreesFromRotation(iconRotation); + int rotation = orientationState.getTouchRotationDegrees(); switch (iconRotation) { case Surface.ROTATION_90: iconParams.gravity = (isRtl ? END : START) | CENTER_VERTICAL; @@ -579,11 +590,15 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable { @Override public void onPageScroll(ScrollState scrollState) { + // Don't do anything if it's modal. + if (mRunningModalAnimation || isTaskOverlayModal()) { + return; + } + float curveInterpolation = CURVE_INTERPOLATOR.getInterpolation(scrollState.linearInterpolation); float curveScaleForCurveInterpolation = getCurveScaleForCurveInterpolation( curveInterpolation); - mSnapshotView.setDimAlpha(curveInterpolation * MAX_PAGE_SCRIM_ALPHA); setCurveScale(curveScaleForCurveInterpolation); diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml index dcc85d52cc..9a61165c40 100644 --- a/quickstep/res/values/dimens.xml +++ b/quickstep/res/values/dimens.xml @@ -17,6 +17,7 @@ 24dp + 44dp 12dp 48dp @@ -24,6 +25,7 @@ 110dp + 16dp 10dp 70dp diff --git a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java index abdff0d7bb..48e25bdbf7 100644 --- a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java +++ b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java @@ -17,14 +17,7 @@ package com.android.launcher3; import static com.android.launcher3.AbstractFloatingView.TYPE_ALL; import static com.android.launcher3.AbstractFloatingView.TYPE_HIDE_BACK_BUTTON; -import static com.android.launcher3.LauncherState.ALL_APPS; import static com.android.launcher3.LauncherState.NORMAL; -import static com.android.launcher3.LauncherState.OVERVIEW; -import static com.android.launcher3.allapps.DiscoveryBounce.BOUNCE_MAX_COUNT; -import static com.android.launcher3.allapps.DiscoveryBounce.HOME_BOUNCE_COUNT; -import static com.android.launcher3.allapps.DiscoveryBounce.HOME_BOUNCE_SEEN; -import static com.android.launcher3.allapps.DiscoveryBounce.SHELF_BOUNCE_COUNT; -import static com.android.launcher3.allapps.DiscoveryBounce.SHELF_BOUNCE_SEEN; import static com.android.quickstep.SysUINavigationMode.removeShelfFromOverview; import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_HOME_KEY; @@ -32,13 +25,13 @@ import android.animation.AnimatorSet; import android.animation.ValueAnimator; import android.content.Intent; import android.content.IntentSender; +import android.content.SharedPreferences; import android.os.Bundle; import android.os.CancellationSignal; import android.view.View; import com.android.launcher3.LauncherState.ScaleAndTranslation; import com.android.launcher3.LauncherStateManager.StateHandler; -import com.android.launcher3.accessibility.SystemActions; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.model.WellbeingModel; import com.android.launcher3.popup.SystemShortcut; @@ -48,12 +41,14 @@ import com.android.launcher3.statehandlers.BackButtonAlphaHandler; import com.android.launcher3.statehandlers.DepthController; import com.android.launcher3.touch.PagedOrientationHandler; import com.android.launcher3.uioverrides.RecentsViewStateController; +import com.android.launcher3.util.OnboardingPrefs; import com.android.launcher3.util.UiThreadHelper; import com.android.quickstep.RecentsModel; import com.android.quickstep.SysUINavigationMode; import com.android.quickstep.SysUINavigationMode.Mode; import com.android.quickstep.SysUINavigationMode.NavigationModeChangeListener; import com.android.quickstep.SystemUiProxy; +import com.android.quickstep.util.QuickstepOnboardingPrefs; import com.android.quickstep.util.RemoteAnimationProvider; import com.android.quickstep.util.RemoteFadeOutAnimationListener; import com.android.quickstep.util.ShelfPeekAnim; @@ -70,7 +65,6 @@ public abstract class BaseQuickstepLauncher extends Launcher implements NavigationModeChangeListener { private DepthController mDepthController = new DepthController(this); - protected SystemActions mSystemActions; /** * Reusable command for applying the back button alpha on the background thread. @@ -86,48 +80,8 @@ public abstract class BaseQuickstepLauncher extends Launcher @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - mSystemActions = new SystemActions(this); SysUINavigationMode.INSTANCE.get(this).addModeChangeListener(this); - - if (!getSharedPrefs().getBoolean(HOME_BOUNCE_SEEN, false)) { - getStateManager().addStateListener(new LauncherStateManager.StateListener() { - @Override - public void onStateTransitionStart(LauncherState toState) { } - - @Override - public void onStateTransitionComplete(LauncherState finalState) { - boolean swipeUpEnabled = SysUINavigationMode.INSTANCE - .get(BaseQuickstepLauncher.this).getMode().hasGestures; - LauncherState prevState = getStateManager().getLastState(); - - if (((swipeUpEnabled && finalState == OVERVIEW) || (!swipeUpEnabled - && finalState == ALL_APPS && prevState == NORMAL) || BOUNCE_MAX_COUNT - <= getSharedPrefs().getInt(HOME_BOUNCE_COUNT, 0))) { - getSharedPrefs().edit().putBoolean(HOME_BOUNCE_SEEN, true).apply(); - getStateManager().removeStateListener(this); - } - } - }); - } - - if (!getSharedPrefs().getBoolean(SHELF_BOUNCE_SEEN, false)) { - getStateManager().addStateListener(new LauncherStateManager.StateListener() { - @Override - public void onStateTransitionStart(LauncherState toState) { } - - @Override - public void onStateTransitionComplete(LauncherState finalState) { - LauncherState prevState = getStateManager().getLastState(); - - if ((finalState == ALL_APPS && prevState == OVERVIEW) || BOUNCE_MAX_COUNT - <= getSharedPrefs().getInt(SHELF_BOUNCE_COUNT, 0)) { - getSharedPrefs().edit().putBoolean(SHELF_BOUNCE_SEEN, true).apply(); - getStateManager().removeStateListener(this); - } - } - }); - } } @Override @@ -141,12 +95,6 @@ public abstract class BaseQuickstepLauncher extends Launcher getDragLayer().recreateControllers(); } - @Override - public void onActivityResult(int requestCode, int resultCode, Intent data) { - super.onActivityResult(requestCode, resultCode, data); - mSystemActions.onActivityResult(requestCode); - } - @Override public void onEnterAnimationComplete() { super.onEnterAnimationComplete(); @@ -210,15 +158,6 @@ public abstract class BaseQuickstepLauncher extends Launcher // removes the task itself. startActivity(ProxyActivityStarter.getLaunchIntent(this, null)); } - - // Register all system actions once they are available - mSystemActions.register(); - } - - @Override - protected void onPause() { - super.onPause(); - mSystemActions.unregister(); } @Override @@ -261,6 +200,12 @@ public abstract class BaseQuickstepLauncher extends Launcher return mDepthController; } + @Override + protected OnboardingPrefs createOnboardingPrefs(SharedPreferences sharedPrefs, + LauncherStateManager stateManager) { + return new QuickstepOnboardingPrefs(this, sharedPrefs, stateManager); + } + @Override protected ScaleAndTranslation getOverviewScaleAndTranslationForNormalState() { if (SysUINavigationMode.getMode(this) == Mode.NO_BUTTON) { diff --git a/quickstep/src/com/android/launcher3/accessibility/SystemActions.java b/quickstep/src/com/android/launcher3/accessibility/SystemActions.java deleted file mode 100644 index 669877f5b3..0000000000 --- a/quickstep/src/com/android/launcher3/accessibility/SystemActions.java +++ /dev/null @@ -1,90 +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.launcher3.accessibility; - -import static com.android.launcher3.LauncherState.ALL_APPS; -import static com.android.launcher3.LauncherState.NORMAL; - -import android.app.PendingIntent; -import android.app.RemoteAction; -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.graphics.drawable.Icon; -import android.view.accessibility.AccessibilityManager; -import com.android.launcher3.Launcher; -import com.android.launcher3.LauncherState; -import com.android.launcher3.LauncherStateManager; -import com.android.launcher3.R; - -/** - * Manages the launcher system actions presented to accessibility services. - */ -public class SystemActions { - - /** - * System Action ID to show all apps. This ID should follow the ones in - * com.android.systemui.accessibility.SystemActions. - */ - private static final int SYSTEM_ACTION_ID_ALL_APPS = 100; - - private Launcher mLauncher; - private AccessibilityManager mAccessibilityManager; - private RemoteAction mAllAppsAction; - private boolean mRegistered; - - public SystemActions(Launcher launcher) { - mLauncher = launcher; - mAccessibilityManager = (AccessibilityManager) launcher.getSystemService( - Context.ACCESSIBILITY_SERVICE); - mAllAppsAction = new RemoteAction( - Icon.createWithResource(launcher, R.drawable.ic_apps), - launcher.getString(R.string.all_apps_label), - launcher.getString(R.string.all_apps_label), - launcher.createPendingResult(SYSTEM_ACTION_ID_ALL_APPS, new Intent(), - 0 /* flags */)); - } - - public void register() { - if (mRegistered) { - return; - } - mAccessibilityManager.registerSystemAction(mAllAppsAction, SYSTEM_ACTION_ID_ALL_APPS); - mRegistered = true; - } - - public void unregister() { - if (!mRegistered) { - return; - } - mAccessibilityManager.unregisterSystemAction(SYSTEM_ACTION_ID_ALL_APPS); - mRegistered = false; - } - - public void onActivityResult(int requestCode) { - if (requestCode == SYSTEM_ACTION_ID_ALL_APPS) { - showAllApps(); - } - } - - private void showAllApps() { - LauncherStateManager stateManager = mLauncher.getStateManager(); - stateManager.goToState(NORMAL); - stateManager.goToState(ALL_APPS); - } -} diff --git a/quickstep/src/com/android/launcher3/model/WellbeingModel.java b/quickstep/src/com/android/launcher3/model/WellbeingModel.java index 92c857388a..2181aa826a 100644 --- a/quickstep/src/com/android/launcher3/model/WellbeingModel.java +++ b/quickstep/src/com/android/launcher3/model/WellbeingModel.java @@ -45,8 +45,8 @@ import android.util.Log; import androidx.annotation.MainThread; import com.android.launcher3.BaseDraggingActivity; -import com.android.launcher3.ItemInfo; import com.android.launcher3.R; +import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.popup.RemoteActionShortcut; import com.android.launcher3.popup.SystemShortcut; import com.android.launcher3.util.MainThreadInitializedObject; diff --git a/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java b/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java index 123c988a04..0f45196ec4 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java +++ b/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java @@ -62,10 +62,13 @@ public abstract class BaseRecentsViewStateController @Override public void setState(@NonNull LauncherState state) { - ScaleAndTranslation scaleAndTranslation = state - .getOverviewScaleAndTranslation(mLauncher); + ScaleAndTranslation scaleAndTranslation = state.getOverviewScaleAndTranslation(mLauncher); + float translationX = scaleAndTranslation.translationX; + if (mRecentsView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL) { + translationX = -translationX; + } SCALE_PROPERTY.set(mRecentsView, scaleAndTranslation.scale); - mRecentsView.setTranslationX(scaleAndTranslation.translationX); + mRecentsView.setTranslationX(translationX); mRecentsView.setTranslationY(scaleAndTranslation.translationY); getContentAlphaProperty().set(mRecentsView, state.overviewUi ? 1f : 0); @@ -96,9 +99,13 @@ public abstract class BaseRecentsViewStateController void setStateWithAnimationInternal(@NonNull final LauncherState toState, @NonNull StateAnimationConfig config, @NonNull PendingAnimation setter) { ScaleAndTranslation scaleAndTranslation = toState.getOverviewScaleAndTranslation(mLauncher); + float translationX = scaleAndTranslation.translationX; + if (mRecentsView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL) { + translationX = -translationX; + } setter.setFloat(mRecentsView, SCALE_PROPERTY, scaleAndTranslation.scale, config.getInterpolator(ANIM_OVERVIEW_SCALE, LINEAR)); - setter.setFloat(mRecentsView, VIEW_TRANSLATE_X, scaleAndTranslation.translationX, + setter.setFloat(mRecentsView, VIEW_TRANSLATE_X, translationX, config.getInterpolator(ANIM_OVERVIEW_TRANSLATE_X, LINEAR)); setter.setFloat(mRecentsView, VIEW_TRANSLATE_Y, scaleAndTranslation.translationY, config.getInterpolator(ANIM_OVERVIEW_TRANSLATE_Y, LINEAR)); diff --git a/quickstep/src/com/android/launcher3/uioverrides/PreviewSurfaceRenderer.java b/quickstep/src/com/android/launcher3/uioverrides/PreviewSurfaceRenderer.java deleted file mode 100644 index c7cce0b563..0000000000 --- a/quickstep/src/com/android/launcher3/uioverrides/PreviewSurfaceRenderer.java +++ /dev/null @@ -1,48 +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.launcher3.uioverrides; - -import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; - -import android.content.Context; -import android.os.Bundle; -import android.util.Size; -import android.view.View; - -import com.android.launcher3.InvariantDeviceProfile; -import com.android.launcher3.graphics.LauncherPreviewRenderer; -import com.android.systemui.shared.system.SurfaceViewRequestReceiver; - -/** Render preview using surface view. */ -public class PreviewSurfaceRenderer { - - /** Handle a received surface view request. */ - public static void render(Context context, Bundle bundle) { - String gridName = bundle.getString("name"); - bundle.remove("name"); - if (gridName == null) { - gridName = InvariantDeviceProfile.getCurrentGridName(context); - } - final InvariantDeviceProfile idp = new InvariantDeviceProfile(context, gridName); - - MAIN_EXECUTOR.execute(() -> { - View view = new LauncherPreviewRenderer(context, idp).getRenderedView(); - new SurfaceViewRequestReceiver().onReceive(context, bundle, view, - new Size(view.getMeasuredWidth(), view.getMeasuredHeight())); - }); - } -} diff --git a/quickstep/src/com/android/quickstep/InputConsumer.java b/quickstep/src/com/android/quickstep/InputConsumer.java index 818d836c05..a4861dce01 100644 --- a/quickstep/src/com/android/quickstep/InputConsumer.java +++ b/quickstep/src/com/android/quickstep/InputConsumer.java @@ -70,10 +70,10 @@ public interface InputConsumer { } /** - * Returns true if the given input consumer is in the hierarchy of this input consumer. + * Returns the active input consumer is in the hierarchy of this input consumer. */ - default boolean isInConsumerHierarchy(InputConsumer candidate) { - return this == candidate; + default InputConsumer getActiveConsumerInHierarchy() { + return this; } /** @@ -96,15 +96,15 @@ public interface InputConsumer { } default String getName() { - String name = ""; + StringBuilder name = new StringBuilder(); for (int i = 0; i < NAMES.length; i++) { if ((getType() & (1 << i)) != 0) { if (name.length() > 0) { - name += ":"; + name.append(":"); } - name += NAMES[i]; + name.append(NAMES[i]); } } - return name; + return name.toString(); } } diff --git a/quickstep/src/com/android/quickstep/InstantAppResolverImpl.java b/quickstep/src/com/android/quickstep/InstantAppResolverImpl.java index 3e9872a2ff..7638541023 100644 --- a/quickstep/src/com/android/quickstep/InstantAppResolverImpl.java +++ b/quickstep/src/com/android/quickstep/InstantAppResolverImpl.java @@ -21,7 +21,7 @@ import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; -import com.android.launcher3.AppInfo; +import com.android.launcher3.model.data.AppInfo; import com.android.launcher3.util.InstantAppResolver; /** diff --git a/quickstep/src/com/android/quickstep/OrientationTouchTransformer.java b/quickstep/src/com/android/quickstep/OrientationTouchTransformer.java index 3e73f49f37..495c09297e 100644 --- a/quickstep/src/com/android/quickstep/OrientationTouchTransformer.java +++ b/quickstep/src/com/android/quickstep/OrientationTouchTransformer.java @@ -22,6 +22,8 @@ import static android.view.MotionEvent.ACTION_MOVE; import static android.view.MotionEvent.ACTION_POINTER_DOWN; import static android.view.MotionEvent.ACTION_UP; +import static com.android.quickstep.util.RecentsOrientedState.postDisplayRotation; + import android.content.res.Resources; import android.graphics.Matrix; import android.graphics.Point; @@ -33,8 +35,8 @@ import android.view.Surface; import com.android.launcher3.R; import com.android.launcher3.ResourceUtils; -import com.android.launcher3.states.RotationHelper; import com.android.launcher3.util.DefaultDisplay; +import com.android.quickstep.util.RecentsOrientedState.SurfaceRotation; import java.io.PrintWriter; @@ -52,15 +54,30 @@ class OrientationTouchTransformer { private static final boolean DEBUG = false; private static final int MAX_ORIENTATIONS = 4; + private final Matrix mTmpMatrix = new Matrix(); + private final float[] mTmpPoint = new float[2]; + private SparseArray mSwipeTouchRegions = new SparseArray<>(MAX_ORIENTATIONS); private final RectF mAssistantLeftRegion = new RectF(); private final RectF mAssistantRightRegion = new RectF(); - private int mCurrentRotation; + private int mCurrentDisplayRotation; private boolean mEnableMultipleRegions; private Resources mResources; private OrientationRectF mLastRectTouched; private SysUINavigationMode.Mode mMode; private QuickStepContractInfo mContractInfo; + + /** + * Represents if we're currently in a swipe "session" of sorts. If value is -1, then user + * has not tapped on an active nav region. Otherwise it will be the rotation of the display + * when the user first interacted with the active nav bar region. + * The "session" ends when {@link #enableMultipleRegions(boolean, DefaultDisplay.Info)} is + * called - usually from a timeout or if user starts interacting w/ the foreground app. + * + * This is different than {@link #mLastRectTouched} as it can get reset by the system whereas + * the rect is purely used for tracking touch interactions and usually this "session" will + * outlast the touch interaction. + */ private int mQuickStepStartingRotation = -1; /** For testability */ @@ -68,6 +85,7 @@ class OrientationTouchTransformer { float getWindowCornerRadius(); } + OrientationTouchTransformer(Resources resources, SysUINavigationMode.Mode mode, QuickStepContractInfo contractInfo) { mResources = resources; @@ -92,20 +110,21 @@ class OrientationTouchTransformer { * @see #enableMultipleRegions(boolean, DefaultDisplay.Info) */ void createOrAddTouchRegion(DefaultDisplay.Info info) { - mCurrentRotation = info.rotation; - if (mQuickStepStartingRotation > -1 && mCurrentRotation == mQuickStepStartingRotation) { - // Ignore nav bars in other rotations except for the one we started out in + mCurrentDisplayRotation = info.rotation; + if (mQuickStepStartingRotation > -1 + && mCurrentDisplayRotation == mQuickStepStartingRotation) { + // User already was swiping and the current screen is same rotation as the starting one + // Remove active nav bars in other rotations except for the one we started out in resetSwipeRegions(info); return; } - - OrientationRectF region = mSwipeTouchRegions.get(mCurrentRotation); + OrientationRectF region = mSwipeTouchRegions.get(mCurrentDisplayRotation); if (region != null) { return; } if (mEnableMultipleRegions) { - mSwipeTouchRegions.put(mCurrentRotation, createRegionForDisplay(info)); + mSwipeTouchRegions.put(mCurrentDisplayRotation, createRegionForDisplay(info)); } else { resetSwipeRegions(info); } @@ -123,12 +142,6 @@ class OrientationTouchTransformer { if (!enableMultipleRegions) { mQuickStepStartingRotation = -1; resetSwipeRegions(info); - } else { - if (mLastRectTouched != null) { - // mLastRectTouched can be null if gesture type is changed (ex. from settings) - // but nav bar hasn't been interacted with yet. - mQuickStepStartingRotation = mLastRectTouched.mRotation; - } } } @@ -140,17 +153,25 @@ class OrientationTouchTransformer { */ private void resetSwipeRegions(DefaultDisplay.Info region) { if (DEBUG) { - Log.d(TAG, "clearing all regions except rotation: " + mCurrentRotation); + Log.d(TAG, "clearing all regions except rotation: " + mCurrentDisplayRotation); } - mCurrentRotation = region.rotation; + mCurrentDisplayRotation = region.rotation; + OrientationRectF regionToKeep = mSwipeTouchRegions.get(mCurrentDisplayRotation); mSwipeTouchRegions.clear(); - mSwipeTouchRegions.put(mCurrentRotation, createRegionForDisplay(region)); + mSwipeTouchRegions.put(mCurrentDisplayRotation, + regionToKeep != null ? regionToKeep : createRegionForDisplay(region)); + } + + private void resetSwipeRegions() { + OrientationRectF regionToKeep = mSwipeTouchRegions.get(mCurrentDisplayRotation); + mSwipeTouchRegions.clear(); + mSwipeTouchRegions.put(mCurrentDisplayRotation, regionToKeep); } private OrientationRectF createRegionForDisplay(DefaultDisplay.Info display) { if (DEBUG) { - Log.d(TAG, "creating rotation region for: " + mCurrentRotation); + Log.d(TAG, "creating rotation region for: " + mCurrentDisplayRotation); } Point size = display.realSize; @@ -220,6 +241,10 @@ class OrientationTouchTransformer { } } + int getQuickStepStartingRotation() { + return mQuickStepStartingRotation; + } + public void transform(MotionEvent event) { int eventAction = event.getActionMasked(); switch (eventAction) { @@ -252,6 +277,11 @@ class OrientationTouchTransformer { } if (rect.applyTransform(event, false)) { mLastRectTouched = rect; + if (mCurrentDisplayRotation == mLastRectTouched.mRotation) { + // Start a touch session for the default nav region for the display + mQuickStepStartingRotation = mLastRectTouched.mRotation; + resetSwipeRegions(); + } if (DEBUG) { Log.d(TAG, "set active region: " + rect); } @@ -304,42 +334,46 @@ class OrientationTouchTransformer { } boolean applyTransform(MotionEvent event, boolean forceTransform) { - // TODO(b/149658423): See if we can use RotationHelper.getRotationMatrix here - MotionEvent tmp = MotionEvent.obtain(event); - Matrix outMatrix = new Matrix(); - int delta = RotationHelper.deltaRotation(mCurrentRotation, mRotation); - switch (delta) { - case Surface.ROTATION_0: - outMatrix.reset(); - break; - case Surface.ROTATION_90: - outMatrix.setRotate(270); - outMatrix.postTranslate(0, mHeight); - break; - case Surface.ROTATION_180: - outMatrix.setRotate(180); - outMatrix.postTranslate(mHeight, mWidth); - break; - case Surface.ROTATION_270: - outMatrix.setRotate(90); - outMatrix.postTranslate(mWidth, 0); - break; + mTmpMatrix.reset(); + postDisplayRotation(deltaRotation(mCurrentDisplayRotation, mRotation), + mHeight, mWidth, mTmpMatrix); + if (forceTransform) { + if (DEBUG) { + Log.d(TAG, "Transforming rotation due to forceTransform, " + + "mCurrentRotation: " + mCurrentDisplayRotation + + "mRotation: " + mRotation); + } + event.transform(mTmpMatrix); + return true; } + mTmpPoint[0] = event.getX(); + mTmpPoint[1] = event.getY(); + mTmpMatrix.mapPoints(mTmpPoint); - tmp.transform(outMatrix); if (DEBUG) { Log.d(TAG, "original: " + event.getX() + ", " + event.getY() - + " new: " + tmp.getX() + ", " + tmp.getY() + + " new: " + mTmpPoint[0] + ", " + mTmpPoint[1] + " rect: " + this + " forceTransform: " + forceTransform - + " contains: " + contains(tmp.getX(), tmp.getY())); + + " contains: " + contains(mTmpPoint[0], mTmpPoint[1])); } - if (forceTransform || contains(tmp.getX(), tmp.getY())) { - event.transform(outMatrix); - tmp.recycle(); + if (contains(mTmpPoint[0], mTmpPoint[1])) { + event.transform(mTmpMatrix); return true; } return false; } } + + /** + * @return how many factors {@param newRotation} is rotated 90 degrees clockwise. + * E.g. 1->Rotated by 90 degrees clockwise, 2->Rotated 180 clockwise... + * A value of 0 means no rotation has been applied + */ + @SurfaceRotation + private static int deltaRotation(int oldRotation, int newRotation) { + int delta = newRotation - oldRotation; + if (delta < 0) delta += 4; + return delta; + } } diff --git a/quickstep/src/com/android/quickstep/OverviewComponentObserver.java b/quickstep/src/com/android/quickstep/OverviewComponentObserver.java index 9edc86e6be..231ee72f1c 100644 --- a/quickstep/src/com/android/quickstep/OverviewComponentObserver.java +++ b/quickstep/src/com/android/quickstep/OverviewComponentObserver.java @@ -40,6 +40,7 @@ import com.android.systemui.shared.system.PackageManagerWrapper; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Objects; +import java.util.function.Consumer; /** * Class to keep track of the current overview component based off user preferences and app updates @@ -57,6 +58,9 @@ public final class OverviewComponentObserver { private final Intent mMyHomeIntent; private final Intent mFallbackIntent; private final SparseIntArray mConfigChangesMap = new SparseIntArray(); + + private Consumer mOverviewChangeListener = b -> { }; + private String mUpdateRegisteredPackage; private BaseActivityInterface mActivityInterface; private Intent mOverviewIntent; @@ -64,10 +68,10 @@ public final class OverviewComponentObserver { private boolean mIsDefaultHome; private boolean mIsHomeDisabled; + public OverviewComponentObserver(Context context, RecentsAnimationDeviceState deviceState) { mContext = context; mDeviceState = deviceState; - mCurrentHomeIntent = new Intent(Intent.ACTION_MAIN) .addCategory(Intent.CATEGORY_HOME) .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); @@ -95,6 +99,13 @@ public final class OverviewComponentObserver { updateOverviewTargets(); } + /** + * Sets a listener for changes in {@link #isHomeAndOverviewSame()} + */ + public void setOverviewChangeListener(Consumer overviewChangeListener) { + mOverviewChangeListener = overviewChangeListener; + } + public void onSystemUiStateChanged() { if (mDeviceState.isHomeDisabled() != mIsHomeDisabled) { updateOverviewTargets(); @@ -159,6 +170,7 @@ public final class OverviewComponentObserver { ACTION_PACKAGE_REMOVED)); } } + mOverviewChangeListener.accept(mIsHomeAndOverviewSame); } /** diff --git a/quickstep/src/com/android/quickstep/RecentTasksList.java b/quickstep/src/com/android/quickstep/RecentTasksList.java index 10f9febfb7..b0ce8e6cf9 100644 --- a/quickstep/src/com/android/quickstep/RecentTasksList.java +++ b/quickstep/src/com/android/quickstep/RecentTasksList.java @@ -123,6 +123,17 @@ public class RecentTasksList extends TaskStackChangeListener { mChangeId++; } + @Override + public void onRecentTaskListUpdated() { + // In some cases immediately after booting, the tasks in the system recent task list may be + // loaded, but not in the active task hierarchy in the system. These tasks are displayed in + // overview, but removing them don't result in a onTaskStackChanged() nor a onTaskRemoved() + // callback (those are for changes to the active tasks), but the task list is still updated, + // so we should also invalidate the change id to ensure we load a new list instead of + // reusing a stale list. + mChangeId++; + } + @Override public void onTaskRemoved(int taskId) { mTasks = loadTasksInBackground(Integer.MAX_VALUE, false); diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java b/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java index 566a701985..7d568a42ce 100644 --- a/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java +++ b/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java @@ -91,9 +91,6 @@ public class RecentsAnimationCallbacks implements RemoteAnimationTargetCompat[] appTargets, RemoteAnimationTargetCompat[] wallpaperTargets, Rect homeContentInsets, Rect minimizedHomeBounds) { - if (TestProtocol.sDebugTracing) { - Log.d(TestProtocol.NO_START_FROM_RECENTS, "onAnimationStart"); - } RecentsAnimationTargets targets = new RecentsAnimationTargets(appTargets, wallpaperTargets, homeContentInsets, minimizedHomeBounds); mController = new RecentsAnimationController(animationController, diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationController.java b/quickstep/src/com/android/quickstep/RecentsAnimationController.java index 268e5645bd..5ece2d78e8 100644 --- a/quickstep/src/com/android/quickstep/RecentsAnimationController.java +++ b/quickstep/src/com/android/quickstep/RecentsAnimationController.java @@ -22,13 +22,13 @@ import static android.view.MotionEvent.ACTION_UP; import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR; -import android.annotation.NonNull; import android.os.SystemClock; import android.util.Log; import android.view.InputEvent; import android.view.KeyEvent; import android.view.MotionEvent; +import androidx.annotation.NonNull; import androidx.annotation.UiThread; import com.android.launcher3.util.Preconditions; diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java index 491c6110db..6dbf2b0077 100644 --- a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java +++ b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java @@ -17,6 +17,7 @@ package com.android.quickstep; import static android.content.Intent.ACTION_USER_UNLOCKED; +import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR; import static com.android.quickstep.SysUINavigationMode.Mode.NO_BUTTON; import static com.android.quickstep.SysUINavigationMode.Mode.THREE_BUTTONS; import static com.android.quickstep.SysUINavigationMode.Mode.TWO_BUTTONS; @@ -73,6 +74,8 @@ public class RecentsAnimationDeviceState implements NavigationModeChangeListener, DefaultDisplay.DisplayInfoChangeListener { + private static final String TAG = "RecentsAnimationDeviceState"; + private final Context mContext; private final SysUINavigationMode mSysUiNavMode; private final DefaultDisplay mDefaultDisplay; @@ -95,6 +98,7 @@ public class RecentsAnimationDeviceState implements @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(); } @@ -120,7 +124,6 @@ public class RecentsAnimationDeviceState implements private Runnable mOnDestroyFrozenTaskRunnable; public RecentsAnimationDeviceState(Context context) { - final ContentResolver resolver = context.getContentResolver(); mContext = context; mSysUiNavMode = SysUINavigationMode.INSTANCE.get(context); mDefaultDisplay = DefaultDisplay.INSTANCE.get(context); @@ -511,8 +514,16 @@ public class RecentsAnimationDeviceState implements mOrientationTouchTransformer.transform(event); } - public void enableMultipleRegions(boolean enable) { + void enableMultipleRegions(boolean enable) { mOrientationTouchTransformer.enableMultipleRegions(enable, mDefaultDisplay.getInfo()); + if (enable) { + UI_HELPER_EXECUTOR.execute(() -> { + int quickStepStartingRotation = + mOrientationTouchTransformer.getQuickStepStartingRotation(); + SystemUiProxy.INSTANCE.get(mContext) + .onQuickSwitchToNewTask(quickStepStartingRotation); + }); + } } public int getCurrentActiveRotation() { diff --git a/quickstep/src/com/android/quickstep/SystemUiProxy.java b/quickstep/src/com/android/quickstep/SystemUiProxy.java index f5aaf1dece..20d133c3c6 100644 --- a/quickstep/src/com/android/quickstep/SystemUiProxy.java +++ b/quickstep/src/com/android/quickstep/SystemUiProxy.java @@ -334,6 +334,7 @@ public class SystemUiProxy implements ISystemUiProxy { } } + @Override public void onQuickSwitchToNewTask(int rotation) { if (mSystemUiProxy != null) { try { diff --git a/quickstep/src/com/android/quickstep/TaskAnimationManager.java b/quickstep/src/com/android/quickstep/TaskAnimationManager.java index b04a1ae8d1..bbca568472 100644 --- a/quickstep/src/com/android/quickstep/TaskAnimationManager.java +++ b/quickstep/src/com/android/quickstep/TaskAnimationManager.java @@ -53,9 +53,6 @@ public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAn @UiThread public RecentsAnimationCallbacks startRecentsAnimation(GestureState gestureState, Intent intent, RecentsAnimationCallbacks.RecentsAnimationListener listener) { - if (TestProtocol.sDebugTracing) { - Log.d(TestProtocol.NO_START_FROM_RECENTS, "startRecentsAnimation"); - } // Notify if recents animation is still running if (mController != null) { String msg = "New recents animation started before old animation completed"; diff --git a/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialEngagedController.java b/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialEngagedController.java index c9ee1e2006..297c287615 100644 --- a/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialEngagedController.java +++ b/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialEngagedController.java @@ -35,7 +35,7 @@ final class BackGestureTutorialEngagedController extends BackGestureTutorialCont @Override void transitToController() { super.transitToController(); - mHandCoachingAnimation.maybeStartLoopedAnimation(mTutorialTypeInfo.get().getTutorialType()); + mHandCoachingAnimation.startLoopedAnimation(mTutorialTypeInfo.get().getTutorialType()); } @Override diff --git a/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialHandAnimation.java b/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialHandAnimation.java index d03811de55..d7c10b0b9c 100644 --- a/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialHandAnimation.java +++ b/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialHandAnimation.java @@ -38,8 +38,6 @@ final class BackGestureTutorialHandAnimation { private final ImageView mHandCoachingView; private final AnimatedVectorDrawable mGestureAnimation; - private boolean mIsAnimationPlayed = false; - BackGestureTutorialHandAnimation(Context context, View rootView) { mHandCoachingView = rootView.findViewById( R.id.back_gesture_tutorial_fragment_hand_coaching); @@ -47,20 +45,15 @@ final class BackGestureTutorialHandAnimation { R.drawable.back_gesture); } - boolean isRunning() { - return mGestureAnimation.isRunning(); - } - /** - * Starts animation if the playground is launched for the first time. + * [Re]starts animation for the given tutorial. */ - void maybeStartLoopedAnimation(TutorialType tutorialType) { - if (isRunning() || mIsAnimationPlayed) { - return; + void startLoopedAnimation(TutorialType tutorialType) { + if (mGestureAnimation.isRunning()) { + stop(); } - mIsAnimationPlayed = true; - clearAnimationCallbacks(); + mGestureAnimation.clearAnimationCallbacks(); mGestureAnimation.registerAnimationCallback( new Animatable2.AnimationCallback() { @Override @@ -78,17 +71,11 @@ final class BackGestureTutorialHandAnimation { float rotationY = tutorialType == TutorialType.LEFT_EDGE_BACK_NAVIGATION ? 180f : 0f; mHandCoachingView.setRotationY(rotationY); mHandCoachingView.setImageDrawable(mGestureAnimation); - mHandCoachingView.postDelayed(() -> mGestureAnimation.start(), - ANIMATION_START_DELAY.toMillis()); - } - - private void clearAnimationCallbacks() { - mGestureAnimation.clearAnimationCallbacks(); + mHandCoachingView.postDelayed(mGestureAnimation::start, ANIMATION_START_DELAY.toMillis()); } void stop() { - mIsAnimationPlayed = false; - clearAnimationCallbacks(); + mGestureAnimation.clearAnimationCallbacks(); mGestureAnimation.stop(); } } diff --git a/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java b/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java index 58bb980ab6..ac2200dcc4 100644 --- a/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java +++ b/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java @@ -23,16 +23,16 @@ import static android.stats.launcher.nano.Launcher.OVERVIEW; import android.content.Context; -import com.android.launcher3.FolderInfo; -import com.android.launcher3.ItemInfo; import com.android.launcher3.LauncherAppState; -import com.android.launcher3.LauncherAppWidgetInfo; import com.android.launcher3.logger.LauncherAtom; import com.android.launcher3.logging.StatsLogManager; import com.android.launcher3.logging.StatsLogUtils; import com.android.launcher3.model.AllAppsList; import com.android.launcher3.model.BaseModelUpdateTask; import com.android.launcher3.model.BgDataModel; +import com.android.launcher3.model.data.FolderInfo; +import com.android.launcher3.model.data.ItemInfo; +import com.android.launcher3.model.data.LauncherAppWidgetInfo; import com.android.launcher3.util.IntSparseArrayMap; import java.util.ArrayList; diff --git a/quickstep/src/com/android/quickstep/util/QuickstepOnboardingPrefs.java b/quickstep/src/com/android/quickstep/util/QuickstepOnboardingPrefs.java new file mode 100644 index 0000000000..ab2484d733 --- /dev/null +++ b/quickstep/src/com/android/quickstep/util/QuickstepOnboardingPrefs.java @@ -0,0 +1,96 @@ +/* + * 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.LauncherState.ALL_APPS; +import static com.android.launcher3.LauncherState.NORMAL; +import static com.android.launcher3.LauncherState.OVERVIEW; + +import android.content.SharedPreferences; + +import com.android.launcher3.BaseQuickstepLauncher; +import com.android.launcher3.LauncherState; +import com.android.launcher3.LauncherStateManager; +import com.android.launcher3.LauncherStateManager.StateListener; +import com.android.launcher3.util.OnboardingPrefs; +import com.android.quickstep.SysUINavigationMode; + +/** + * Extends {@link OnboardingPrefs} for quickstep-specific onboarding data. + */ +public class QuickstepOnboardingPrefs extends OnboardingPrefs { + + public QuickstepOnboardingPrefs(BaseQuickstepLauncher launcher, SharedPreferences sharedPrefs, + LauncherStateManager stateManager) { + super(launcher, sharedPrefs, stateManager); + + if (!getBoolean(HOME_BOUNCE_SEEN)) { + mStateManager.addStateListener(new StateListener() { + @Override + public void onStateTransitionStart(LauncherState toState) { } + + @Override + public void onStateTransitionComplete(LauncherState finalState) { + boolean swipeUpEnabled = SysUINavigationMode.INSTANCE + .get(mLauncher).getMode().hasGestures; + LauncherState prevState = mStateManager.getLastState(); + + if (((swipeUpEnabled && finalState == OVERVIEW) || (!swipeUpEnabled + && finalState == ALL_APPS && prevState == NORMAL) || + hasReachedMaxCount(HOME_BOUNCE_COUNT))) { + mSharedPrefs.edit().putBoolean(HOME_BOUNCE_SEEN, true).apply(); + mStateManager.removeStateListener(this); + } + } + }); + } + + if (!getBoolean(SHELF_BOUNCE_SEEN)) { + mStateManager.addStateListener(new StateListener() { + @Override + public void onStateTransitionStart(LauncherState toState) { } + + @Override + public void onStateTransitionComplete(LauncherState finalState) { + LauncherState prevState = mStateManager.getLastState(); + + if ((finalState == ALL_APPS && prevState == OVERVIEW) || + hasReachedMaxCount(SHELF_BOUNCE_COUNT)) { + mSharedPrefs.edit().putBoolean(SHELF_BOUNCE_SEEN, true).apply(); + mStateManager.removeStateListener(this); + } + } + }); + } + + if (!hasReachedMaxCount(ALL_APPS_COUNT)) { + mStateManager.addStateListener(new StateListener() { + @Override + public void onStateTransitionStart(LauncherState toState) { } + + @Override + public void onStateTransitionComplete(LauncherState finalState) { + if (finalState == ALL_APPS) { + if (incrementEventCount(ALL_APPS_COUNT)) { + mStateManager.removeStateListener(this); + mLauncher.getScrimView().updateDragHandleVisibility(); + } + } + } + }); + } + } +} diff --git a/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java b/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java new file mode 100644 index 0000000000..f72e4588ca --- /dev/null +++ b/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java @@ -0,0 +1,217 @@ +/* + * 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 android.hardware.camera2.params.OutputConfiguration.ROTATION_180; +import static android.view.Surface.ROTATION_0; +import static android.view.Surface.ROTATION_270; +import static android.view.Surface.ROTATION_90; + +import static java.lang.annotation.RetentionPolicy.SOURCE; + +import android.graphics.Matrix; +import android.graphics.RectF; +import android.view.MotionEvent; +import android.view.Surface; + +import androidx.annotation.IntDef; + +import com.android.launcher3.config.FeatureFlags; +import com.android.launcher3.touch.PagedOrientationHandler; +import com.android.launcher3.touch.PortraitPagedViewHandler; + +import java.lang.annotation.Retention; + +/** + * Container to hold orientation/rotation related information for Launcher. + * This is not meant to be an abstraction layer for applying different functionality between + * the different orientation/rotations. For that see {@link PagedOrientationHandler} + * + * This class has initial default state assuming the device and foreground app have + * no ({@link Surface#ROTATION_0} rotation. + */ +public final class RecentsOrientedState { + + @Retention(SOURCE) + @IntDef({ROTATION_0, ROTATION_90, ROTATION_180, ROTATION_270}) + public @interface SurfaceRotation {} + + private PagedOrientationHandler mOrientationHandler = PagedOrientationHandler.PORTRAIT; + + private @SurfaceRotation int mTouchRotation = ROTATION_0; + private @SurfaceRotation int mDisplayRotation = ROTATION_0; + /** + * If {@code true} we default to {@link PortraitPagedViewHandler} and don't support any fake + * launcher orientations. + */ + private boolean mDisableMultipleOrientations; + + private final Matrix mTmpMatrix = new Matrix(); + private final Matrix mTmpInverseMatrix = new Matrix(); + + /** + * Sets the appropriate {@link PagedOrientationHandler} for {@link #mOrientationHandler} + * @param touchRotation The rotation the nav bar region that is touched is in + * @param displayRotation Rotation of the display/device + * + * @return true if there was any change in the internal state as a result of this call, + * false otherwise + */ + public boolean update( + @SurfaceRotation int touchRotation, @SurfaceRotation int displayRotation) { + if (!FeatureFlags.ENABLE_FIXED_ROTATION_TRANSFORM.get()) { + return false; + } + if (mDisableMultipleOrientations) { + return false; + } + if (mDisplayRotation == displayRotation && mTouchRotation == touchRotation) { + return false; + } + + mDisplayRotation = displayRotation; + mTouchRotation = touchRotation; + if (mTouchRotation == ROTATION_90) { + mOrientationHandler = PagedOrientationHandler.LANDSCAPE; + } else if (mTouchRotation == ROTATION_270) { + mOrientationHandler = PagedOrientationHandler.SEASCAPE; + } else { + mOrientationHandler = PagedOrientationHandler.PORTRAIT; + } + return true; + } + + public boolean areMultipleLayoutOrientationsDisabled() { + return mDisableMultipleOrientations; + } + + /** + * Setting this preference will render future calls to {@link #update(int, int)} as a no-op. + */ + public void disableMultipleOrientations(boolean disable) { + mDisableMultipleOrientations = disable; + if (disable) { + mDisplayRotation = mTouchRotation = ROTATION_0; + mOrientationHandler = PagedOrientationHandler.PORTRAIT; + } + } + + @SurfaceRotation + public int getDisplayRotation() { + return mDisplayRotation; + } + + @SurfaceRotation + public int getTouchRotation() { + return mTouchRotation; + } + + public int getTouchRotationDegrees() { + switch (mTouchRotation) { + case ROTATION_90: + return 90; + case ROTATION_180: + return 180; + case ROTATION_270: + return 270; + case ROTATION_0: + default: + return 0; + } + } + + public PagedOrientationHandler getOrientationHandler() { + return mOrientationHandler; + } + + /** + * For landscape, since the navbar is already in a vertical position, we don't have to do any + * rotations as the change in Y coordinate is what is read. We only flip the sign of the + * y coordinate to make it match existing behavior of swipe to the top to go previous + */ + public void flipVertical(MotionEvent ev) { + mTmpMatrix.setScale(1, -1); + ev.transform(mTmpMatrix); + } + + /** + * Creates a matrix to transform the given motion event specified by degrees. + * If inverse is {@code true}, the inverse of that matrix will be applied + */ + public void transformEvent(float degrees, MotionEvent ev, boolean inverse) { + mTmpMatrix.setRotate(inverse ? -degrees : degrees); + ev.transform(mTmpMatrix); + + // TODO: Add scaling back in based on degrees + /* + if (getWidth() > 0 && getHeight() > 0) { + float scale = ((float) getWidth()) / getHeight(); + transform.postScale(scale, 1 / scale); + } + */ + } + + public void mapRectFromNormalOrientation(RectF src, int screenWidth, int screenHeight) { + mTmpMatrix.reset(); + postDisplayRotation(mDisplayRotation, screenWidth, screenHeight, mTmpMatrix); + mTmpMatrix.mapRect(src); + } + + public void mapInverseRectFromNormalOrientation(RectF src, int screenWidth, int screenHeight) { + mTmpMatrix.reset(); + postDisplayRotation(mDisplayRotation, screenWidth, screenHeight, mTmpMatrix); + mTmpMatrix.invert(mTmpInverseMatrix); + mTmpInverseMatrix.mapRect(src); + } + + @SurfaceRotation + public static int getRotationForUserDegreesRotated(float degrees) { + int threshold = 70; + if (degrees >= (360 - threshold) || degrees < (threshold)) { + return ROTATION_0; + } else if (degrees < (90 + threshold)) { + return ROTATION_270; + } else if (degrees < 180 + threshold) { + return ROTATION_180; + } else { + return ROTATION_90; + } + } + + /** + * Posts the transformation on the matrix representing the provided display rotation + */ + public static void postDisplayRotation(@SurfaceRotation int displayRotation, + float screenWidth, float screenHeight, Matrix out) { + switch (displayRotation) { + case ROTATION_0: + return; + case ROTATION_90: + out.postRotate(270); + out.postTranslate(0, screenWidth); + break; + case ROTATION_180: + out.postRotate(180); + out.postTranslate(screenHeight, screenWidth); + break; + case ROTATION_270: + out.postRotate(90); + out.postTranslate(screenHeight, 0); + break; + } + } +} diff --git a/quickstep/src/com/android/quickstep/views/ShelfScrimView.java b/quickstep/src/com/android/quickstep/views/ShelfScrimView.java index c2ccd90d62..f5498c9961 100644 --- a/quickstep/src/com/android/quickstep/views/ShelfScrimView.java +++ b/quickstep/src/com/android/quickstep/views/ShelfScrimView.java @@ -43,6 +43,7 @@ import com.android.launcher3.Utilities; import com.android.launcher3.anim.Interpolators; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.uioverrides.states.OverviewState; +import com.android.launcher3.util.OnboardingPrefs; import com.android.launcher3.util.Themes; import com.android.launcher3.views.ScrimView; import com.android.quickstep.SysUINavigationMode; @@ -75,6 +76,7 @@ public class ShelfScrimView extends ScrimView private final float mRadius; private final int mMaxScrimAlpha; private final Paint mPaint; + private final OnboardingPrefs mOnboardingPrefs; // Mid point where the alpha changes private int mMidAlpha; @@ -100,6 +102,7 @@ public class ShelfScrimView extends ScrimView private boolean mRemainingScreenPathValid = false; private Mode mSysUINavigationMode; + private boolean mIsTwoZoneSwipeModel; public ShelfScrimView(Context context, AttributeSet attrs) { super(context, attrs); @@ -108,6 +111,7 @@ public class ShelfScrimView extends ScrimView mEndAlpha = Color.alpha(mEndScrim); mRadius = BOTTOM_CORNER_RADIUS_RATIO * Themes.getDialogCornerRadius(context); mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + mOnboardingPrefs = mLauncher.getOnboardingPrefs(); // Just assume the easiest UI for now, until we have the proper layout information. mDrawingFlatColor = true; @@ -140,9 +144,11 @@ public class ShelfScrimView extends ScrimView // Show the shelf more quickly before reaching overview progress. mBeforeMidProgressColorInterpolator = ACCEL_2; mAfterMidProgressColorInterpolator = ACCEL; + mIsTwoZoneSwipeModel = FeatureFlags.ENABLE_OVERVIEW_ACTIONS.get(); } else { mBeforeMidProgressColorInterpolator = ACCEL; mAfterMidProgressColorInterpolator = Interpolators.clampToProgress(ACCEL, 0.5f, 1f); + mIsTwoZoneSwipeModel = false; } } @@ -236,9 +242,9 @@ public class ShelfScrimView extends ScrimView @Override protected boolean shouldDragHandleBeVisible() { - boolean twoZoneSwipeModel = FeatureFlags.ENABLE_OVERVIEW_ACTIONS.get() - && SysUINavigationMode.removeShelfFromOverview(mLauncher); - return twoZoneSwipeModel || super.shouldDragHandleBeVisible(); + boolean needsAllAppsEdu = mIsTwoZoneSwipeModel + && !mOnboardingPrefs.hasReachedMaxCount(OnboardingPrefs.ALL_APPS_COUNT); + return needsAllAppsEdu || super.shouldDragHandleBeVisible(); } @Override diff --git a/quickstep/tests/src/com/android/quickstep/ViewInflationDuringSwipeUp.java b/quickstep/tests/src/com/android/quickstep/ViewInflationDuringSwipeUp.java index f8f22a1943..8ecd88a45e 100644 --- a/quickstep/tests/src/com/android/quickstep/ViewInflationDuringSwipeUp.java +++ b/quickstep/tests/src/com/android/quickstep/ViewInflationDuringSwipeUp.java @@ -48,9 +48,9 @@ import androidx.test.uiautomator.By; import androidx.test.uiautomator.UiDevice; import androidx.test.uiautomator.Until; -import com.android.launcher3.LauncherAppWidgetInfo; import com.android.launcher3.LauncherAppWidgetProviderInfo; import com.android.launcher3.LauncherSettings; +import com.android.launcher3.model.data.LauncherAppWidgetInfo; import com.android.launcher3.tapl.Background; import com.android.launcher3.testcomponent.ListViewService; import com.android.launcher3.testcomponent.ListViewService.SimpleViewsFactory; diff --git a/res/layout/work_apps_paused.xml b/res/layout/work_apps_paused.xml index f64b2d9cd7..08e1c981ff 100644 --- a/res/layout/work_apps_paused.xml +++ b/res/layout/work_apps_paused.xml @@ -20,7 +20,7 @@ android:gravity="center"> + + @@ -133,6 +135,12 @@ + + + + + + diff --git a/res/values/config.xml b/res/values/config.xml index ef6761387c..0657b86a6c 100644 --- a/res/values/config.xml +++ b/res/values/config.xml @@ -131,8 +131,8 @@ 0.5 1500 - 0.75 - 200 + 0.8 + 400 0.75 200 diff --git a/res/values/strings.xml b/res/values/strings.xml index ac04262ae8..a9ce24c320 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -343,6 +343,10 @@ Work profile is paused Work apps can\’t send you notifications, use your battery, or access your location + + Work profile is paused. Work apps can\’t send you notifications, use your battery, or access your location + + Pause work apps and notifications diff --git a/res/values/styles.xml b/res/values/styles.xml index bc6ab45920..fcc651b7b6 100644 --- a/res/values/styles.xml +++ b/res/values/styles.xml @@ -226,6 +226,9 @@