diff options
author | Hyunyoung Song <hyunyoungs@google.com> | 2019-11-20 00:32:39 -0800 |
---|---|---|
committer | Hyunyoung Song <hyunyoungs@google.com> | 2019-11-20 08:40:59 +0000 |
commit | 793f5c518656f94732be695eac4d5b01fa50ae01 (patch) | |
tree | a1a981f65c0ac1744f46c2f05f7777419127a86e /quickstep | |
parent | 47835458089c656139246f13dbcd5baedfec3c16 (diff) | |
parent | 9353b00616ac688d2df6b9f8513c4895bb4119d7 (diff) | |
download | packages_apps_Trebuchet-793f5c518656f94732be695eac4d5b01fa50ae01.tar.gz packages_apps_Trebuchet-793f5c518656f94732be695eac4d5b01fa50ae01.tar.bz2 packages_apps_Trebuchet-793f5c518656f94732be695eac4d5b01fa50ae01.zip |
Merging from ub-launcher3-qt-future-dev @ build 6018744
Test: manual, presubmit on the source branch
http://x20/teams/android-launcher/merge/ub-launcher3-qt-future-dev_6018744.htmlMerge commit '9353b00616ac688d2df6b9f8513c4895bb4119d7' into merge_ub-launcher3-qt-future-dev_6018744
Change-Id: Ie6235a262b7bba0aa9f2a2962a7aa499a0a2cb53
Merged-In: I8a1e20d0b175f03a1a05c81749f07ab5314e872a
Diffstat (limited to 'quickstep')
65 files changed, 1378 insertions, 401 deletions
diff --git a/quickstep/AndroidManifest.xml b/quickstep/AndroidManifest.xml index 546548036..826a27553 100644 --- a/quickstep/AndroidManifest.xml +++ b/quickstep/AndroidManifest.xml @@ -88,6 +88,7 @@ <activity android:name="com.android.quickstep.LockScreenRecentsActivity" android:theme="@android:style/Theme.NoDisplay" android:showOnLockScreen="true" + android:taskAffinity="${packageName}.locktask" android:directBootAware="true" /> </application> diff --git a/quickstep/recents_ui_overrides/res/values/override.xml b/quickstep/recents_ui_overrides/res/values/override.xml index 1ddd3f594..ed3ba929a 100644 --- a/quickstep/recents_ui_overrides/res/values/override.xml +++ b/quickstep/recents_ui_overrides/res/values/override.xml @@ -24,5 +24,7 @@ <string name="app_launch_tracker_class" translatable="false">com.android.launcher3.appprediction.PredictionAppTracker</string> <string name="main_process_initializer_class" translatable="false">com.android.quickstep.QuickstepProcessInitializer</string> + + <string name="user_event_dispatcher_class" translatable="false">com.android.quickstep.logging.UserEventDispatcherAppPredictionExtension</string> </resources> diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java index 711594386..cdff33bf9 100644 --- a/quickstep/recents_ui_overrides/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java @@ -17,7 +17,10 @@ package com.android.launcher3; import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X; +import static com.android.launcher3.LauncherState.BACKGROUND_APP; +import static com.android.launcher3.LauncherState.HOTSEAT_ICONS; import static com.android.launcher3.LauncherState.NORMAL; +import static com.android.launcher3.LauncherState.OVERVIEW; import static com.android.launcher3.anim.Interpolators.AGGRESSIVE_EASE; import static com.android.launcher3.anim.Interpolators.LINEAR; import static com.android.quickstep.TaskViewUtils.findTaskViewToLaunch; @@ -27,9 +30,15 @@ import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; import android.animation.ObjectAnimator; +import android.animation.ValueAnimator; import android.content.Context; import android.view.View; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.android.launcher3.LauncherState.ScaleAndTranslation; +import com.android.launcher3.allapps.AllAppsTransitionController; import com.android.launcher3.anim.AnimatorPlaybackController; import com.android.launcher3.anim.Interpolators; import com.android.launcher3.anim.SpringAnimationBuilder; @@ -38,9 +47,6 @@ import com.android.quickstep.views.RecentsView; import com.android.quickstep.views.TaskView; import com.android.systemui.shared.system.RemoteAnimationTargetCompat; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - /** * A {@link QuickstepAppTransitionManagerImpl} that also implements recents transitions from * {@link RecentsView}. @@ -144,8 +150,37 @@ public final class LauncherAppTransitionManagerImpl extends QuickstepAppTransiti @Override public Animator createStateElementAnimation(int index, float... values) { switch (index) { - case INDEX_SHELF_ANIM: - return mLauncher.getAllAppsController().createSpringAnimation(values); + case INDEX_SHELF_ANIM: { + AllAppsTransitionController aatc = mLauncher.getAllAppsController(); + Animator springAnim = aatc.createSpringAnimation(values); + + if ((OVERVIEW.getVisibleElements(mLauncher) & HOTSEAT_ICONS) != 0) { + // Translate hotseat with the shelf until reaching overview. + float overviewProgress = OVERVIEW.getVerticalProgress(mLauncher); + ScaleAndTranslation sat = OVERVIEW.getHotseatScaleAndTranslation(mLauncher); + float shiftRange = aatc.getShiftRange(); + if (values.length == 1) { + values = new float[] {aatc.getProgress(), values[0]}; + } + ValueAnimator hotseatAnim = ValueAnimator.ofFloat(values); + hotseatAnim.addUpdateListener(anim -> { + float progress = (Float) anim.getAnimatedValue(); + if (progress >= overviewProgress || mLauncher.isInState(BACKGROUND_APP)) { + float hotseatShift = (progress - overviewProgress) * shiftRange; + mLauncher.getHotseat().setTranslationY(hotseatShift + sat.translationY); + } + }); + hotseatAnim.setInterpolator(LINEAR); + hotseatAnim.setDuration(springAnim.getDuration()); + + AnimatorSet anim = new AnimatorSet(); + anim.play(hotseatAnim); + anim.play(springAnim); + return anim; + } + + return springAnim; + } case INDEX_RECENTS_FADE_ANIM: return ObjectAnimator.ofFloat(mLauncher.getOverviewPanel(), RecentsView.CONTENT_ALPHA, values); 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 4ecc39cf6..65e69b604 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 @@ -17,6 +17,8 @@ package com.android.launcher3.appprediction; import static android.content.pm.PackageManager.MATCH_INSTANT; +import static com.android.launcher3.util.Executors.MODEL_EXECUTOR; + import android.content.Context; import android.content.Intent; import android.content.pm.ApplicationInfo; @@ -30,8 +32,12 @@ import android.os.Message; import android.util.ArrayMap; import android.util.Log; +import androidx.annotation.MainThread; +import androidx.annotation.Nullable; +import androidx.annotation.UiThread; +import androidx.annotation.WorkerThread; + import com.android.launcher3.LauncherAppState; -import com.android.launcher3.LauncherModel; import com.android.launcher3.WorkspaceItemInfo; import com.android.launcher3.icons.IconCache; import com.android.launcher3.icons.LauncherIcons; @@ -45,11 +51,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import androidx.annotation.MainThread; -import androidx.annotation.Nullable; -import androidx.annotation.UiThread; -import androidx.annotation.WorkerThread; - /** * Utility class which loads and caches predicted items like instant apps and shortcuts, before * they can be displayed on the UI @@ -77,7 +78,7 @@ public class DynamicItemCache { public DynamicItemCache(Context context, Runnable onUpdateCallback) { mContext = context; - mWorker = new Handler(LauncherModel.getWorkerLooper(), this::handleWorkerMessage); + mWorker = new Handler(MODEL_EXECUTOR.getLooper(), this::handleWorkerMessage); mUiHandler = new Handler(Looper.getMainLooper(), this::handleUiMessage); mInstantAppResolver = InstantAppResolver.newInstance(context); mOnUpdateCallback = onUpdateCallback; diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionAppTracker.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionAppTracker.java index 24fc61bef..4c7943b10 100644 --- a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionAppTracker.java +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionAppTracker.java @@ -1,4 +1,4 @@ -/** +/* * Copyright (C) 2019 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -16,6 +16,7 @@ package com.android.launcher3.appprediction; import static com.android.launcher3.InvariantDeviceProfile.CHANGE_FLAG_GRID; +import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR; import android.annotation.TargetApi; import android.app.prediction.AppPredictionContext; @@ -34,19 +35,25 @@ import android.os.UserHandle; import android.util.Log; import androidx.annotation.Nullable; +import androidx.annotation.UiThread; +import androidx.annotation.WorkerThread; + import com.android.launcher3.InvariantDeviceProfile; import com.android.launcher3.appprediction.PredictionUiStateManager.Client; import com.android.launcher3.model.AppLaunchTracker; -import com.android.launcher3.util.UiThreadHelper; +import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper; +import com.android.systemui.plugins.AppLaunchEventsPlugin; +import com.android.systemui.plugins.PluginListener; -import androidx.annotation.UiThread; -import androidx.annotation.WorkerThread; +import java.util.ArrayList; +import java.util.List; /** * Subclass of app tracker which publishes the data to the prediction engine and gets back results. */ @TargetApi(Build.VERSION_CODES.Q) -public class PredictionAppTracker extends AppLaunchTracker { +public class PredictionAppTracker extends AppLaunchTracker + implements PluginListener<AppLaunchEventsPlugin> { private static final String TAG = "PredictionAppTracker"; private static final boolean DBG = false; @@ -58,6 +65,7 @@ public class PredictionAppTracker extends AppLaunchTracker { protected final Context mContext; private final Handler mMessageHandler; + private final List<AppLaunchEventsPlugin> mAppLaunchEventsPluginsList; // Accessed only on worker thread private AppPredictor mHomeAppPredictor; @@ -65,10 +73,14 @@ public class PredictionAppTracker extends AppLaunchTracker { public PredictionAppTracker(Context context) { mContext = context; - mMessageHandler = new Handler(UiThreadHelper.getBackgroundLooper(), this::handleMessage); + mMessageHandler = new Handler(UI_HELPER_EXECUTOR.getLooper(), this::handleMessage); InvariantDeviceProfile.INSTANCE.get(mContext).addOnChangeListener(this::onIdpChanged); mMessageHandler.sendEmptyMessage(MSG_INIT); + + mAppLaunchEventsPluginsList = new ArrayList<>(); + PluginManagerWrapper.INSTANCE.get(context) + .addPluginListener(this, AppLaunchEventsPlugin.class, true); } @UiThread @@ -96,7 +108,7 @@ public class PredictionAppTracker extends AppLaunchTracker { AppPredictionManager apm = mContext.getSystemService(AppPredictionManager.class); if (apm == null) { - return null; + return null; } AppPredictor predictor = apm.createAppPredictionSession( @@ -116,7 +128,7 @@ public class PredictionAppTracker extends AppLaunchTracker { */ @WorkerThread @Nullable - public Bundle getAppPredictionContextExtras(Client client){ + public Bundle getAppPredictionContextExtras(Client client) { return null; } @@ -128,7 +140,7 @@ public class PredictionAppTracker extends AppLaunchTracker { destroy(); // Initialize the clients - int count = InvariantDeviceProfile.INSTANCE.get(mContext).numColumns; + int count = InvariantDeviceProfile.INSTANCE.get(mContext).numAllAppsColumns; mHomeAppPredictor = createPredictor(Client.HOME, count); mRecentsOverviewPredictor = createPredictor(Client.OVERVIEW, count); return true; @@ -167,37 +179,98 @@ public class PredictionAppTracker extends AppLaunchTracker { if (DBG) { Log.d(TAG, String.format("Sent immediate message to update %s", client)); } + + // Relay onReturnedToHome to every plugin. + mAppLaunchEventsPluginsList.forEach(AppLaunchEventsPlugin::onReturnedToHome); } @Override @UiThread public void onStartShortcut(String packageName, String shortcutId, UserHandle user, - String container) { + String container) { // TODO: Use the full shortcut info - AppTarget target = new AppTarget - .Builder(new AppTargetId("shortcut:" + shortcutId), packageName, user) - .setClassName(shortcutId) - .build(); + AppTarget target = new AppTarget.Builder( + new AppTargetId("shortcut:" + shortcutId), packageName, user) + .setClassName(shortcutId) + .build(); + sendLaunch(target, container); + + // Relay onStartShortcut info to every connected plugin. + mAppLaunchEventsPluginsList + .forEach(plugin -> plugin.onStartShortcut( + packageName, + shortcutId, + user, + container != null ? container : CONTAINER_DEFAULT) + ); + } @Override @UiThread public void onStartApp(ComponentName cn, UserHandle user, String container) { if (cn != null) { - AppTarget target = new AppTarget - .Builder(new AppTargetId("app:" + cn), cn.getPackageName(), user) - .setClassName(cn.getClassName()) - .build(); + AppTarget target = new AppTarget.Builder( + new AppTargetId("app:" + cn), cn.getPackageName(), user) + .setClassName(cn.getClassName()) + .build(); sendLaunch(target, container); + + // Relay onStartApp to every connected plugin. + mAppLaunchEventsPluginsList + .forEach(plugin -> plugin.onStartApp( + cn, + user, + container != null ? container : CONTAINER_DEFAULT) + ); } } + @Override @UiThread - private void sendLaunch(AppTarget target, String container) { - AppTargetEvent event = new AppTargetEvent.Builder(target, AppTargetEvent.ACTION_LAUNCH) + public void onDismissApp(ComponentName cn, UserHandle user, String container) { + if (cn == null) return; + AppTarget target = new AppTarget.Builder( + new AppTargetId("app: " + cn), cn.getPackageName(), user) + .setClassName(cn.getClassName()) + .build(); + sendDismiss(target, container); + + // Relay onDismissApp to every connected plugin. + mAppLaunchEventsPluginsList + .forEach(plugin -> plugin.onDismissApp( + cn, + user, + container != null ? container : CONTAINER_DEFAULT) + ); + } + + @UiThread + private void sendEvent(AppTarget target, String container, int eventId) { + AppTargetEvent event = new AppTargetEvent.Builder(target, eventId) .setLaunchLocation(container == null ? CONTAINER_DEFAULT : container) .build(); Message.obtain(mMessageHandler, MSG_LAUNCH, event).sendToTarget(); } + + @UiThread + private void sendLaunch(AppTarget target, String container) { + sendEvent(target, container, AppTargetEvent.ACTION_LAUNCH); + } + + @UiThread + private void sendDismiss(AppTarget target, String container) { + sendEvent(target, container, AppTargetEvent.ACTION_DISMISS); + } + + @Override + public void onPluginConnected(AppLaunchEventsPlugin appLaunchEventsPlugin, Context context) { + mAppLaunchEventsPluginsList.add(appLaunchEventsPlugin); + } + + @Override + public void onPluginDisconnected(AppLaunchEventsPlugin appLaunchEventsPlugin) { + mAppLaunchEventsPluginsList.remove(appLaunchEventsPlugin); + } } 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 0c7ba9c95..23db5df2e 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 @@ -43,6 +43,7 @@ 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.R; import com.android.launcher3.WorkspaceItemInfo; import com.android.launcher3.allapps.AllAppsStore; @@ -92,7 +93,7 @@ public class PredictionRowView extends LinearLayout implements private final Launcher mLauncher; private final PredictionUiStateManager mPredictionUiStateManager; - private final int mNumPredictedAppsPerRow; + private int mNumPredictedAppsPerRow; // The set of predicted app component names private final List<ComponentKeyMapper> mPredictedAppComponents = new ArrayList<>(); @@ -128,7 +129,7 @@ public class PredictionRowView extends LinearLayout implements mFocusHelper = new SimpleFocusIndicatorHelper(this); - mNumPredictedAppsPerRow = LauncherAppState.getIDP(context).numColumns; + mNumPredictedAppsPerRow = LauncherAppState.getIDP(context).numAllAppsColumns; mLauncher = Launcher.getLauncher(context); mLauncher.addOnDeviceProfileChangeListener(this); @@ -226,6 +227,7 @@ public class PredictionRowView extends LinearLayout implements @Override public void onDeviceProfileChanged(DeviceProfile dp) { + mNumPredictedAppsPerRow = dp.inv.numAllAppsColumns; removeAllViews(); applyPredictionApps(); } @@ -281,7 +283,7 @@ public class PredictionRowView extends LinearLayout implements } private List<ItemInfoWithIcon> processPredictedAppComponents(List<ComponentKeyMapper> components) { - if (getAppsStore().getApps().isEmpty()) { + if (getAppsStore().getApps().length == 0) { // Apps have not been bound yet. return Collections.emptyList(); } @@ -290,7 +292,9 @@ public class PredictionRowView extends LinearLayout implements for (ComponentKeyMapper mapper : components) { ItemInfoWithIcon info = mapper.getApp(getAppsStore()); if (info != null) { - predictedApps.add(info); + ItemInfoWithIcon predictedApp = info.clone(); + predictedApp.container = LauncherSettings.Favorites.CONTAINER_PREDICTION; + predictedApps.add(predictedApp); } else { if (FeatureFlags.IS_DOGFOOD_BUILD) { Log.e(TAG, "Predicted app not found: " + mapper); 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 1a59770a0..9c661074e 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 @@ -25,12 +25,16 @@ import android.app.prediction.AppTarget; import android.content.ComponentName; import android.content.Context; +import androidx.annotation.NonNull; + import com.android.launcher3.AppInfo; 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; import com.android.launcher3.LauncherState; import com.android.launcher3.LauncherStateManager.StateListener; import com.android.launcher3.Utilities; @@ -39,12 +43,14 @@ import com.android.launcher3.allapps.AllAppsStore.OnUpdateListener; import com.android.launcher3.icons.IconCache; import com.android.launcher3.icons.IconCache.ItemInfoUpdateReceiver; import com.android.launcher3.shortcuts.ShortcutKey; +import com.android.launcher3.userevent.nano.LauncherLogProto; import com.android.launcher3.util.ComponentKey; import com.android.launcher3.util.MainThreadInitializedObject; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.stream.IntStream; /** * Handler responsible to updating the UI due to predicted apps changes. Operations: @@ -322,6 +328,30 @@ public class PredictionUiStateManager implements StateListener, ItemInfoUpdateRe return mCurrentState; } + /** + * Fill in predicted_rank field based on app prediction. + * Only applicable when {@link ItemInfo#itemType} is one of the followings: + * {@link LauncherSettings.Favorites#ITEM_TYPE_APPLICATION}, + * {@link LauncherSettings.Favorites#ITEM_TYPE_SHORTCUT}, + * {@link LauncherSettings.Favorites#ITEM_TYPE_DEEP_SHORTCUT} + */ + public static void fillInPredictedRank( + @NonNull ItemInfo itemInfo, @NonNull LauncherLogProto.Target target) { + final PredictionUiStateManager manager = PredictionUiStateManager.INSTANCE.getNoCreate(); + if (manager == null || itemInfo.getTargetComponent() == null || itemInfo.user == null + || (itemInfo.itemType != LauncherSettings.Favorites.ITEM_TYPE_APPLICATION + && itemInfo.itemType != LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT + && itemInfo.itemType != LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT)) { + return; + } + final ComponentKey k = new ComponentKey(itemInfo.getTargetComponent(), itemInfo.user); + final List<ComponentKeyMapper> predictedApps = manager.getCurrentState().apps; + IntStream.range(0, predictedApps.size()) + .filter((i) -> k.equals(predictedApps.get(i).getComponentKey())) + .findFirst() + .ifPresent((rank) -> target.predictedRank = rank); + } + public static class PredictionState { public boolean isEnabled; diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java index 50cfac8e4..63ac52804 100644 --- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java @@ -19,7 +19,6 @@ import static com.android.launcher3.LauncherAnimUtils.OVERVIEW_TRANSITION_MS; import com.android.launcher3.AbstractFloatingView; import com.android.launcher3.Launcher; -import com.android.launcher3.Utilities; import com.android.launcher3.allapps.AllAppsTransitionController; import com.android.launcher3.userevent.nano.LauncherLogProto; import com.android.quickstep.util.LayoutUtils; @@ -69,8 +68,16 @@ public class BackgroundAppState extends OverviewState { if (taskCount == 0) { return super.getOverviewScaleAndTranslation(launcher); } - TaskView dummyTask = recentsView.getTaskViewAt(Utilities.boundToRange( - recentsView.getCurrentPage(), 0, taskCount - 1)); + TaskView dummyTask; + if (recentsView.getCurrentPage() >= 0) { + if (recentsView.getCurrentPage() <= taskCount - 1) { + dummyTask = recentsView.getCurrentPageTaskView(); + } else { + dummyTask = recentsView.getTaskViewAt(taskCount - 1); + } + } else { + dummyTask = recentsView.getTaskViewAt(0); + } return recentsView.getTempClipAnimationHelper().updateForFullscreenOverview(dummyTask) .getScaleAndTranslation(); } @@ -91,7 +98,7 @@ public class BackgroundAppState extends OverviewState { if ((getVisibleElements(launcher) & HOTSEAT_ICONS) != 0) { // Translate hotseat offscreen if we show it in overview. ScaleAndTranslation scaleAndTranslation = super.getHotseatScaleAndTranslation(launcher); - scaleAndTranslation.translationY = LayoutUtils.getShelfTrackingDistance(launcher, + scaleAndTranslation.translationY += LayoutUtils.getShelfTrackingDistance(launcher, launcher.getDeviceProfile()); return scaleAndTranslation; } diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewState.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewState.java index 93d4de17d..25eaab187 100644 --- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewState.java +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewState.java @@ -32,7 +32,6 @@ import static com.android.launcher3.anim.Interpolators.OVERSHOOT_1_7; import static com.android.launcher3.logging.LoggerUtils.newContainerTarget; import static com.android.launcher3.states.RotationHelper.REQUEST_ROTATE; -import android.content.Context; import android.graphics.Rect; import android.view.View; @@ -47,6 +46,7 @@ import com.android.launcher3.anim.AnimatorSetBuilder; import com.android.launcher3.userevent.nano.LauncherLogProto.Action; import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType; import com.android.quickstep.SysUINavigationMode; +import com.android.quickstep.util.LayoutUtils; import com.android.quickstep.views.RecentsView; import com.android.quickstep.views.TaskView; @@ -91,8 +91,19 @@ public class OverviewState extends LauncherState { @Override public ScaleAndTranslation getHotseatScaleAndTranslation(Launcher launcher) { if ((getVisibleElements(launcher) & HOTSEAT_ICONS) != 0) { - // If the hotseat icons are visible in overview, keep them in their normal position. - return super.getWorkspaceScaleAndTranslation(launcher); + DeviceProfile dp = launcher.getDeviceProfile(); + if (dp.allAppsIconSizePx >= dp.iconSizePx) { + return new ScaleAndTranslation(1, 0, 0); + } else { + float scale = ((float) dp.allAppsIconSizePx) / dp.iconSizePx; + // Distance between the screen center (which is the pivotY for hotseat) and the + // bottom of the hotseat (which we want to preserve) + float distanceFromBottom = dp.heightPx / 2 - dp.hotseatBarBottomPaddingPx; + // On scaling, the bottom edge is moved closer to the pivotY. We move the + // hotseat back down so that the bottom edge's position is preserved. + float translationY = distanceFromBottom * (1 - scale); + return new ScaleAndTranslation(scale, 0, translationY); + } } return getWorkspaceScaleAndTranslation(launcher); } @@ -160,15 +171,7 @@ public class OverviewState extends LauncherState { } public static float getDefaultSwipeHeight(Launcher launcher) { - return getDefaultSwipeHeight(launcher, launcher.getDeviceProfile()); - } - - public static float getDefaultSwipeHeight(Context context, DeviceProfile dp) { - float swipeHeight = dp.allAppsCellHeightPx - dp.allAppsIconTextSizePx; - if (SysUINavigationMode.getMode(context) == SysUINavigationMode.Mode.NO_BUTTON) { - swipeHeight -= dp.getInsets().bottom; - } - return swipeHeight; + return LayoutUtils.getDefaultSwipeHeight(launcher, launcher.getDeviceProfile()); } @Override diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/FlingAndHoldTouchController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/FlingAndHoldTouchController.java index ab346c059..38a0b6673 100644 --- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/FlingAndHoldTouchController.java +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/FlingAndHoldTouchController.java @@ -32,16 +32,17 @@ import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_FADE; import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_SCALE; import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_TRANSLATE; import static com.android.launcher3.anim.Interpolators.ACCEL; +import static com.android.launcher3.anim.Interpolators.DEACCEL; import static com.android.launcher3.anim.Interpolators.DEACCEL_3; -import static com.android.launcher3.anim.Interpolators.LINEAR; import static com.android.launcher3.anim.Interpolators.OVERSHOOT_1_2; +import static com.android.launcher3.util.VibratorWrapper.OVERVIEW_HAPTIC; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_OVERVIEW_DISABLED; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; -import android.view.HapticFeedbackConstants; import android.view.MotionEvent; +import android.view.View; import android.view.ViewConfiguration; import com.android.launcher3.Launcher; @@ -49,6 +50,7 @@ import com.android.launcher3.LauncherState; import com.android.launcher3.anim.AnimatorSetBuilder; import com.android.launcher3.anim.Interpolators; import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch; +import com.android.launcher3.util.VibratorWrapper; import com.android.quickstep.OverviewInteractionState; import com.android.quickstep.util.MotionPauseDetector; import com.android.quickstep.views.RecentsView; @@ -105,8 +107,7 @@ public class FlingAndHoldTouchController extends PortraitStatesTouchController { } }); mPeekAnim.start(); - recentsView.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY, - HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING); + VibratorWrapper.INSTANCE.get(mLauncher).vibrate(OVERVIEW_HAPTIC); mLauncher.getDragLayer().getScrim().animateToSysuiMultiplier(isPaused ? 0 : 1, peekDuration, 0); @@ -131,16 +132,33 @@ public class FlingAndHoldTouchController extends PortraitStatesTouchController { // Fade in prediction icons quickly, then rest of all apps after reaching overview. float progressToReachOverview = NORMAL.getVerticalProgress(mLauncher) - OVERVIEW.getVerticalProgress(mLauncher); - builder.setInterpolator(ANIM_ALL_APPS_HEADER_FADE, Interpolators.clampToProgress(ACCEL, - 0, ALL_APPS_CONTENT_FADE_THRESHOLD)); - builder.setInterpolator(ANIM_ALL_APPS_FADE, Interpolators.clampToProgress(LINEAR, - progressToReachOverview, 1)); + builder.setInterpolator(ANIM_ALL_APPS_HEADER_FADE, Interpolators.clampToProgress( + ACCEL, + 0, + ALL_APPS_CONTENT_FADE_THRESHOLD)); + builder.setInterpolator(ANIM_ALL_APPS_FADE, Interpolators.clampToProgress( + ACCEL, + progressToReachOverview, + progressToReachOverview + ALL_APPS_CONTENT_FADE_THRESHOLD)); // Get workspace out of the way quickly, to prepare for potential pause. builder.setInterpolator(ANIM_WORKSPACE_SCALE, DEACCEL_3); builder.setInterpolator(ANIM_WORKSPACE_TRANSLATE, DEACCEL_3); builder.setInterpolator(ANIM_WORKSPACE_FADE, DEACCEL_3); return builder; + } else if (fromState == ALL_APPS && toState == NORMAL) { + AnimatorSetBuilder builder = new AnimatorSetBuilder(); + // Keep all apps/predictions opaque until the very end of the transition. + float progressToReachOverview = OVERVIEW.getVerticalProgress(mLauncher); + builder.setInterpolator(ANIM_ALL_APPS_FADE, Interpolators.clampToProgress( + DEACCEL, + progressToReachOverview - ALL_APPS_CONTENT_FADE_THRESHOLD, + progressToReachOverview)); + builder.setInterpolator(ANIM_ALL_APPS_HEADER_FADE, Interpolators.clampToProgress( + DEACCEL, + 1 - ALL_APPS_CONTENT_FADE_THRESHOLD, + 1)); + return builder; } return super.getAnimatorSetBuilderForStates(fromState, toState); } @@ -155,7 +173,7 @@ public class FlingAndHoldTouchController extends PortraitStatesTouchController { } @Override - public void onDragEnd(float velocity, boolean fling) { + public void onDragEnd(float velocity) { if (mMotionPauseDetector.isPaused() && handlingOverviewAnim()) { if (mPeekAnim != null) { mPeekAnim.cancel(); @@ -163,6 +181,7 @@ public class FlingAndHoldTouchController extends PortraitStatesTouchController { AnimatorSetBuilder builder = new AnimatorSetBuilder(); builder.setInterpolator(ANIM_VERTICAL_PROGRESS, OVERSHOOT_1_2); + builder.setInterpolator(ANIM_ALL_APPS_FADE, DEACCEL_3); if ((OVERVIEW.getVisibleElements(mLauncher) & HOTSEAT_ICONS) != 0) { builder.setInterpolator(ANIM_HOTSEAT_SCALE, OVERSHOOT_1_2); builder.setInterpolator(ANIM_HOTSEAT_TRANSLATE, OVERSHOOT_1_2); @@ -177,7 +196,12 @@ public class FlingAndHoldTouchController extends PortraitStatesTouchController { }); overviewAnim.start(); } else { - super.onDragEnd(velocity, fling); + super.onDragEnd(velocity); + } + + View searchView = mLauncher.getAppsView().getSearchView(); + if (searchView instanceof FeedbackHandler) { + ((FeedbackHandler) searchView).resetFeedback(); } mMotionPauseDetector.clear(); } @@ -205,4 +229,16 @@ public class FlingAndHoldTouchController extends PortraitStatesTouchController { builder.addFlag(AnimatorSetBuilder.FLAG_DONT_ANIMATE_OVERVIEW); } } + + /** + * Interface for views with feedback animation requiring reset + */ + public interface FeedbackHandler { + + /** + * reset searchWidget feedback + */ + void resetFeedback(); + } + } diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java index d66af1ae2..ef50c7b5d 100644 --- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java @@ -23,6 +23,7 @@ import static com.android.launcher3.LauncherState.OVERVIEW; import static com.android.launcher3.allapps.AllAppsTransitionController.ALL_APPS_PROGRESS; import static com.android.launcher3.anim.Interpolators.DEACCEL_3; import static com.android.launcher3.touch.AbstractStateChangeTouchController.SUCCESS_TRANSITION_PROGRESS; +import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS; import android.animation.Animator; import android.animation.AnimatorSet; @@ -43,21 +44,25 @@ import com.android.launcher3.anim.AnimatorPlaybackController; import com.android.launcher3.anim.AnimatorSetBuilder; import com.android.launcher3.anim.Interpolators; import com.android.launcher3.compat.AccessibilityManagerCompat; -import com.android.launcher3.touch.SwipeDetector; +import com.android.launcher3.config.BaseFlags; +import com.android.launcher3.touch.SingleAxisSwipeDetector; import com.android.launcher3.userevent.nano.LauncherLogProto; import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch; import com.android.launcher3.util.TouchController; +import com.android.quickstep.util.AssistantUtilities; import com.android.quickstep.views.RecentsView; +import com.android.systemui.shared.system.ActivityManagerWrapper; /** * Handles swiping up on the nav bar to go home from launcher, e.g. overview or all apps. */ -public class NavBarToHomeTouchController implements TouchController, SwipeDetector.Listener { +public class NavBarToHomeTouchController implements TouchController, + SingleAxisSwipeDetector.Listener { private static final Interpolator PULLBACK_INTERPOLATOR = DEACCEL_3; private final Launcher mLauncher; - private final SwipeDetector mSwipeDetector; + private final SingleAxisSwipeDetector mSwipeDetector; private final float mPullbackDistance; private boolean mNoIntercept; @@ -67,7 +72,8 @@ public class NavBarToHomeTouchController implements TouchController, SwipeDetect public NavBarToHomeTouchController(Launcher launcher) { mLauncher = launcher; - mSwipeDetector = new SwipeDetector(mLauncher, this, SwipeDetector.VERTICAL); + mSwipeDetector = new SingleAxisSwipeDetector(mLauncher, this, + SingleAxisSwipeDetector.VERTICAL); mPullbackDistance = mLauncher.getResources().getDimension(R.dimen.home_pullback_distance); } @@ -79,7 +85,8 @@ public class NavBarToHomeTouchController implements TouchController, SwipeDetect if (mNoIntercept) { return false; } - mSwipeDetector.setDetectableScrollConditions(SwipeDetector.DIRECTION_POSITIVE, false); + mSwipeDetector.setDetectableScrollConditions(SingleAxisSwipeDetector.DIRECTION_POSITIVE, + false /* ignoreSlop */); } if (mNoIntercept) { @@ -101,6 +108,10 @@ public class NavBarToHomeTouchController implements TouchController, SwipeDetect if (AbstractFloatingView.getTopOpenView(mLauncher) != null) { return true; } + if (BaseFlags.ASSISTANT_GIVES_LAUNCHER_FOCUS.get() + && AssistantUtilities.isExcludedAssistantRunning()) { + return true; + } return false; } @@ -173,7 +184,8 @@ public class NavBarToHomeTouchController implements TouchController, SwipeDetect } @Override - public void onDragEnd(float velocity, boolean fling) { + public void onDragEnd(float velocity) { + boolean fling = mSwipeDetector.isFling(velocity); final int logAction = fling ? Touch.FLING : Touch.SWIPE; float progress = mCurrentAnimation.getProgressFraction(); float interpolatedProgress = PULLBACK_INTERPOLATOR.getInterpolation(progress); @@ -190,6 +202,8 @@ public class NavBarToHomeTouchController implements TouchController, SwipeDetect AbstractFloatingView.closeAllOpenViews(mLauncher); logStateChange(topOpenView.getLogContainerType(), logAction); } + ActivityManagerWrapper.getInstance() + .closeSystemWindows(CLOSE_SYSTEM_WINDOWS_REASON_RECENTS); } else { // Quickly return to the state we came from (we didn't move far). ValueAnimator anim = mCurrentAnimation.getAnimationPlayer(); diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitOverviewStateTouchHelper.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitOverviewStateTouchHelper.java index 20a248734..03862db1d 100644 --- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitOverviewStateTouchHelper.java +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitOverviewStateTouchHelper.java @@ -47,9 +47,9 @@ public final class PortraitOverviewStateTouchHelper { * @return true if we should intercept the motion event */ boolean canInterceptTouch(MotionEvent ev) { - if (mRecentsView.getChildCount() > 0) { + if (mRecentsView.getTaskViewCount() > 0) { // Allow swiping up in the gap between the hotseat and overview. - return ev.getY() >= mRecentsView.getChildAt(0).getBottom(); + return ev.getY() >= mRecentsView.getTaskViewAt(0).getBottom(); } else { // If there are no tasks, we only intercept if we're below the hotseat height. return isTouchOverHotseat(mLauncher, ev); @@ -63,7 +63,7 @@ public final class PortraitOverviewStateTouchHelper { * @return true if going back should take the user to the currently running task */ boolean shouldSwipeDownReturnToApp() { - TaskView taskView = mRecentsView.getTaskViewAt(mRecentsView.getNextPage()); + TaskView taskView = mRecentsView.getNextPageTaskView(); return taskView != null && mRecentsView.shouldSwipeDownLaunchApp(); } @@ -76,7 +76,7 @@ public final class PortraitOverviewStateTouchHelper { */ PendingAnimation createSwipeDownToTaskAppAnimation(long duration) { mRecentsView.setCurrentPage(mRecentsView.getPageNearestToCenterOfScreen()); - TaskView taskView = mRecentsView.getTaskViewAt(mRecentsView.getCurrentPage()); + TaskView taskView = mRecentsView.getCurrentPageTaskView(); if (taskView == null) { throw new IllegalStateException("There is no task view to animate to."); } diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java index eb571f607..14216ffdf 100644 --- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java @@ -30,6 +30,7 @@ import static com.android.launcher3.anim.Interpolators.INSTANT; import static com.android.launcher3.anim.Interpolators.LINEAR; import static com.android.launcher3.util.SystemUiController.UI_STATE_OVERVIEW; import static com.android.quickstep.views.RecentsView.UPDATE_SYSUI_FLAGS_THRESHOLD; +import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_OVERVIEW_DISABLED; import android.view.MotionEvent; @@ -42,7 +43,7 @@ import com.android.launcher3.LauncherStateManager; import com.android.launcher3.Utilities; import com.android.launcher3.anim.AnimatorSetBuilder; import com.android.launcher3.touch.AbstractStateChangeTouchController; -import com.android.launcher3.touch.SwipeDetector; +import com.android.launcher3.touch.SingleAxisSwipeDetector; import com.android.launcher3.userevent.nano.LauncherLogProto; import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction; import com.android.quickstep.OverviewInteractionState; @@ -50,7 +51,7 @@ import com.android.quickstep.SysUINavigationMode; import com.android.quickstep.SysUINavigationMode.Mode; import com.android.quickstep.views.RecentsView; import com.android.quickstep.views.TaskView; -import com.android.systemui.shared.system.QuickStepContract; +import com.android.systemui.shared.system.ActivityManagerWrapper; /** * Handles quick switching to a recent task from the home screen. @@ -60,10 +61,10 @@ public class QuickSwitchTouchController extends AbstractStateChangeTouchControll private @Nullable TaskView mTaskToLaunch; public QuickSwitchTouchController(Launcher launcher) { - this(launcher, SwipeDetector.HORIZONTAL); + this(launcher, SingleAxisSwipeDetector.HORIZONTAL); } - protected QuickSwitchTouchController(Launcher l, SwipeDetector.Direction dir) { + protected QuickSwitchTouchController(Launcher l, SingleAxisSwipeDetector.Direction dir) { super(l, dir); } @@ -95,6 +96,8 @@ public class QuickSwitchTouchController extends AbstractStateChangeTouchControll super.onDragStart(start); mStartContainerType = LauncherLogProto.ContainerType.NAVBAR; mTaskToLaunch = mLauncher.<RecentsView>getOverviewPanel().getTaskViewAt(0); + ActivityManagerWrapper.getInstance() + .closeSystemWindows(CLOSE_SYSTEM_WINDOWS_REASON_RECENTS); } @Override diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java index 00e4f58e9..ad02de109 100644 --- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java @@ -19,6 +19,9 @@ import static com.android.launcher3.AbstractFloatingView.TYPE_ACCESSIBLE; import static com.android.launcher3.anim.Interpolators.scrollInterpolatorForVelocity; import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE; import static com.android.launcher3.config.FeatureFlags.QUICKSTEP_SPRINGS; +import static com.android.launcher3.touch.SingleAxisSwipeDetector.DIRECTION_BOTH; +import static com.android.launcher3.touch.SingleAxisSwipeDetector.DIRECTION_NEGATIVE; +import static com.android.launcher3.touch.SingleAxisSwipeDetector.DIRECTION_POSITIVE; import static com.android.launcher3.util.DefaultDisplay.getSingleFrameMs; import android.animation.Animator; @@ -32,7 +35,8 @@ import com.android.launcher3.LauncherAnimUtils; import com.android.launcher3.Utilities; import com.android.launcher3.anim.AnimatorPlaybackController; import com.android.launcher3.anim.Interpolators; -import com.android.launcher3.touch.SwipeDetector; +import com.android.launcher3.touch.BaseSwipeDetector; +import com.android.launcher3.touch.SingleAxisSwipeDetector; import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch; import com.android.launcher3.util.FlingBlockCheck; import com.android.launcher3.util.PendingAnimation; @@ -46,15 +50,14 @@ import com.android.quickstep.views.TaskView; * Touch controller for handling task view card swipes */ public abstract class TaskViewTouchController<T extends BaseDraggingActivity> - extends AnimatorListenerAdapter implements TouchController, SwipeDetector.Listener { - - private static final String TAG = "OverviewSwipeController"; + extends AnimatorListenerAdapter implements TouchController, + SingleAxisSwipeDetector.Listener { // Progress after which the transition is assumed to be a success in case user does not fling public static final float SUCCESS_TRANSITION_PROGRESS = 0.5f; protected final T mActivity; - private final SwipeDetector mDetector; + private final SingleAxisSwipeDetector mDetector; private final RecentsView mRecentsView; private final int[] mTempCords = new int[2]; @@ -74,7 +77,7 @@ public abstract class TaskViewTouchController<T extends BaseDraggingActivity> public TaskViewTouchController(T activity) { mActivity = activity; mRecentsView = activity.getOverviewPanel(); - mDetector = new SwipeDetector(activity, this, SwipeDetector.VERTICAL); + mDetector = new SingleAxisSwipeDetector(activity, this, SingleAxisSwipeDetector.VERTICAL); } private boolean canInterceptTouch() { @@ -113,7 +116,7 @@ public abstract class TaskViewTouchController<T extends BaseDraggingActivity> int directionsToDetectScroll = 0; boolean ignoreSlopWhenSettling = false; if (mCurrentAnimation != null) { - directionsToDetectScroll = SwipeDetector.DIRECTION_BOTH; + directionsToDetectScroll = DIRECTION_BOTH; ignoreSlopWhenSettling = true; } else { mTaskBeingDragged = null; @@ -126,12 +129,12 @@ public abstract class TaskViewTouchController<T extends BaseDraggingActivity> if (!SysUINavigationMode.getMode(mActivity).hasGestures) { // Don't allow swipe down to open if we don't support swipe up // to enter overview. - directionsToDetectScroll = SwipeDetector.DIRECTION_POSITIVE; + directionsToDetectScroll = DIRECTION_POSITIVE; } else { // The task can be dragged up to dismiss it, // and down to open if it's the current page. directionsToDetectScroll = i == mRecentsView.getCurrentPage() - ? SwipeDetector.DIRECTION_BOTH : SwipeDetector.DIRECTION_POSITIVE; + ? DIRECTION_BOTH : DIRECTION_POSITIVE; } break; } @@ -165,8 +168,8 @@ public abstract class TaskViewTouchController<T extends BaseDraggingActivity> return; } int scrollDirections = mDetector.getScrollDirections(); - if (goingUp && ((scrollDirections & SwipeDetector.DIRECTION_POSITIVE) == 0) - || !goingUp && ((scrollDirections & SwipeDetector.DIRECTION_NEGATIVE) == 0)) { + if (goingUp && ((scrollDirections & DIRECTION_POSITIVE) == 0) + || !goingUp && ((scrollDirections & DIRECTION_NEGATIVE) == 0)) { // Trying to re-init in an unsupported direction. return; } @@ -243,7 +246,8 @@ public abstract class TaskViewTouchController<T extends BaseDraggingActivity> } @Override - public void onDragEnd(float velocity, boolean fling) { + public void onDragEnd(float velocity) { + boolean fling = mDetector.isFling(velocity); final boolean goingToEnd; final int logAction; boolean blockedFling = fling && mFlingBlockCheck.isBlocked(); @@ -260,7 +264,7 @@ public abstract class TaskViewTouchController<T extends BaseDraggingActivity> logAction = Touch.SWIPE; goingToEnd = interpolatedProgress > SUCCESS_TRANSITION_PROGRESS; } - long animationDuration = SwipeDetector.calculateDuration( + long animationDuration = BaseSwipeDetector.calculateDuration( velocity, goingToEnd ? (1 - progress) : progress); if (blockedFling && !goingToEnd) { animationDuration *= LauncherAnimUtils.blockedFlingDurationFactor(velocity); diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/TransposedQuickSwitchTouchController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/TransposedQuickSwitchTouchController.java index f1e4041eb..0ed529184 100644 --- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/TransposedQuickSwitchTouchController.java +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/TransposedQuickSwitchTouchController.java @@ -17,12 +17,12 @@ package com.android.launcher3.uioverrides.touchcontrollers; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherState; -import com.android.launcher3.touch.SwipeDetector; +import com.android.launcher3.touch.SingleAxisSwipeDetector; public class TransposedQuickSwitchTouchController extends QuickSwitchTouchController { public TransposedQuickSwitchTouchController(Launcher launcher) { - super(launcher, SwipeDetector.VERTICAL); + super(launcher, SingleAxisSwipeDetector.VERTICAL); } @Override 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 d627a7f14..5cce53ebf 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java @@ -15,16 +15,13 @@ */ package com.android.quickstep; -import static android.os.VibrationEffect.EFFECT_CLICK; -import static android.os.VibrationEffect.createPredefined; - import static com.android.launcher3.Utilities.postAsyncCallback; import static com.android.launcher3.anim.Interpolators.ACCEL_1_5; import static com.android.launcher3.anim.Interpolators.DEACCEL; import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE; +import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; +import static com.android.launcher3.util.VibratorWrapper.OVERVIEW_HAPTIC; import static com.android.launcher3.views.FloatingIconView.SHAPE_PROGRESS_DURATION; -import static com.android.quickstep.TouchInteractionService.BACKGROUND_EXECUTOR; -import static com.android.quickstep.TouchInteractionService.MAIN_THREAD_EXECUTOR; import static com.android.quickstep.TouchInteractionService.TOUCH_INTERACTION_LOG; import android.animation.Animator; @@ -39,14 +36,12 @@ import android.graphics.RectF; import android.os.Build; import android.os.Handler; import android.os.Looper; -import android.os.VibrationEffect; -import android.os.Vibrator; -import android.provider.Settings; import android.view.MotionEvent; import android.view.View; -import android.view.WindowManager; import android.view.animation.Interpolator; +import androidx.annotation.UiThread; + import com.android.launcher3.BaseDraggingActivity; import com.android.launcher3.DeviceProfile; import com.android.launcher3.InvariantDeviceProfile; @@ -54,8 +49,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.anim.Interpolators; import com.android.launcher3.graphics.RotationMode; +import com.android.launcher3.util.VibratorWrapper; import com.android.launcher3.views.FloatingIconView; import com.android.quickstep.ActivityControlHelper.ActivityInitListener; import com.android.quickstep.ActivityControlHelper.HomeAnimationFactory; @@ -75,8 +70,6 @@ import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat; import java.util.function.Consumer; -import androidx.annotation.UiThread; - /** * Base class for swipe up handler with some utility methods */ @@ -107,7 +100,6 @@ public abstract class BaseSwipeUpHandler<T extends BaseDraggingActivity, Q exten protected final ClipAnimationHelper mClipAnimationHelper; protected final TransformParams mTransformParams = new TransformParams(); - private final Vibrator mVibrator; protected final Mode mMode; // Shift in the range of [0, 1]. @@ -126,7 +118,7 @@ public abstract class BaseSwipeUpHandler<T extends BaseDraggingActivity, Q exten protected Runnable mGestureEndCallback; - protected final Handler mMainThreadHandler = MAIN_THREAD_EXECUTOR.getHandler(); + protected final Handler mMainThreadHandler = MAIN_EXECUTOR.getHandler(); protected MultiStateCallback mStateCallback; protected boolean mCanceled; @@ -148,7 +140,6 @@ public abstract class BaseSwipeUpHandler<T extends BaseDraggingActivity, Q exten mClipAnimationHelper = new ClipAnimationHelper(context); mPageSpacing = context.getResources().getDimensionPixelSize(R.dimen.recents_page_spacing); - mVibrator = context.getSystemService(Vibrator.class); initTransitionEndpoints(InvariantDeviceProfile.INSTANCE.get(mContext) .getDeviceProfile(mContext)); } @@ -162,19 +153,7 @@ public abstract class BaseSwipeUpHandler<T extends BaseDraggingActivity, Q exten } protected void performHapticFeedback() { - if (!mVibrator.hasVibrator()) { - return; - } - if (Settings.System.getInt( - mContext.getContentResolver(), Settings.System.HAPTIC_FEEDBACK_ENABLED, 0) == 0) { - return; - } - - VibrationEffect effect = createPredefined(EFFECT_CLICK); - if (effect == null) { - return; - } - BACKGROUND_EXECUTOR.execute(() -> mVibrator.vibrate(effect)); + VibratorWrapper.INSTANCE.get(mContext).vibrate(OVERVIEW_HAPTIC); } public Consumer<MotionEvent> getRecentsViewDispatcher(RotationMode rotationMode) { @@ -229,10 +208,10 @@ public abstract class BaseSwipeUpHandler<T extends BaseDraggingActivity, Q exten // 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.getTaskViewAt(mRecentsView.getNextPage()).launchTask(false /* animate */, + mRecentsView.getNextPageTaskView().launchTask(false /* animate */, true /* freezeTaskList */); } else { - int taskId = mRecentsView.getTaskViewAt(mRecentsView.getNextPage()).getTask().key.id; + int taskId = mRecentsView.getNextPageTaskView().getTask().key.id; mFinishingRecentsAnimationForNewTaskId = taskId; mRecentsAnimationWrapper.finish(true /* toRecents */, () -> { if (!mCanceled) { @@ -275,7 +254,7 @@ public abstract class BaseSwipeUpHandler<T extends BaseDraggingActivity, Q exten overviewStackBounds = getStackBounds(dp); } dp.updateInsets(targetSet.homeContentInsets); - dp.updateIsSeascape(mContext.getSystemService(WindowManager.class)); + dp.updateIsSeascape(mContext); if (runningTaskTarget != null) { mClipAnimationHelper.updateSource(overviewStackBounds, runningTaskTarget); } diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java index 36eb8a13b..40e13154d 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java @@ -268,22 +268,12 @@ public final class LauncherActivityControllerHelper implements ActivityControlHe INDEX_RECENTS_FADE_ANIM, attached ? 1 : 0); int runningTaskIndex = recentsView.getRunningTaskIndex(); - if (runningTaskIndex == 0) { + if (runningTaskIndex == recentsView.getTaskViewStartIndex()) { // If we are on the first task (we haven't quick switched), translate recents in // from the side. Calculate the start translation based on current scale/scroll. float currScale = recentsView.getScaleX(); float scrollOffsetX = recentsView.getScrollOffset(); - - float offscreenX = NORMAL.getOverviewScaleAndTranslation(activity).translationX; - // The first task is hidden, so offset by its width. - int firstTaskWidth = recentsView.getTaskViewAt(0).getWidth(); - offscreenX -= (firstTaskWidth + recentsView.getPageSpacing()) * currScale; - // Offset since scale pushes tasks outwards. - offscreenX += firstTaskWidth * (currScale - 1) / 2; - offscreenX = Math.max(0, offscreenX); - if (recentsView.isRtl()) { - offscreenX = -offscreenX; - } + float offscreenX = recentsView.getOffscreenTranslationX(currScale); float fromTranslationX = attached ? offscreenX - scrollOffsetX : 0; float toTranslationX = attached ? 0 : offscreenX - scrollOffsetX; @@ -351,8 +341,7 @@ public final class LauncherActivityControllerHelper implements ActivityControlHe private void playScaleDownAnim(AnimatorSet anim, Launcher launcher, LauncherState fromState, LauncherState endState) { RecentsView recentsView = launcher.getOverviewPanel(); - TaskView v = recentsView.getTaskViewAt(recentsView.getCurrentPage()); - if (v == null) { + if (recentsView.getCurrentPageTaskView() == null) { return; } @@ -380,7 +369,7 @@ public final class LauncherActivityControllerHelper implements ActivityControlHe // recents as a whole needs to translate further to keep up with the app window. TaskView runningTaskView = recentsView.getRunningTaskView(); if (runningTaskView == null) { - runningTaskView = recentsView.getTaskViewAt(recentsView.getCurrentPage()); + runningTaskView = recentsView.getCurrentPageTaskView(); if (runningTaskView == null) { // There are no task views in LockTask mode when Overview is enabled. return; @@ -483,4 +472,4 @@ public final class LauncherActivityControllerHelper implements ActivityControlHe public void onLaunchTaskSuccess(Launcher launcher) { launcher.getStateManager().moveToRestState(); } -}
\ No newline at end of file +} diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/OverviewCommandHelper.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/OverviewCommandHelper.java index 14ff47b6a..a8d402e56 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/OverviewCommandHelper.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/OverviewCommandHelper.java @@ -15,8 +15,8 @@ */ package com.android.quickstep; -import static com.android.systemui.shared.system.ActivityManagerWrapper - .CLOSE_SYSTEM_WINDOWS_REASON_RECENTS; +import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; +import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; @@ -29,7 +29,6 @@ import android.util.Log; import android.view.ViewConfiguration; import com.android.launcher3.BaseDraggingActivity; -import com.android.launcher3.MainThreadExecutor; import com.android.launcher3.logging.UserEventDispatcher; import com.android.launcher3.testing.TestProtocol; import com.android.launcher3.userevent.nano.LauncherLogProto; @@ -49,7 +48,6 @@ public class OverviewCommandHelper { private final Context mContext; private final ActivityManagerWrapper mAM; private final RecentsModel mRecentsModel; - private final MainThreadExecutor mMainThreadExecutor; private final OverviewComponentObserver mOverviewComponentObserver; private long mLastToggleTime; @@ -57,7 +55,6 @@ public class OverviewCommandHelper { public OverviewCommandHelper(Context context, OverviewComponentObserver observer) { mContext = context; mAM = ActivityManagerWrapper.getInstance(); - mMainThreadExecutor = new MainThreadExecutor(); mRecentsModel = RecentsModel.INSTANCE.get(mContext); mOverviewComponentObserver = observer; } @@ -69,19 +66,19 @@ public class OverviewCommandHelper { } mAM.closeSystemWindows(CLOSE_SYSTEM_WINDOWS_REASON_RECENTS); - mMainThreadExecutor.execute(new RecentsActivityCommand<>()); + MAIN_EXECUTOR.execute(new RecentsActivityCommand<>()); } public void onOverviewShown(boolean triggeredFromAltTab) { - mMainThreadExecutor.execute(new ShowRecentsCommand(triggeredFromAltTab)); + MAIN_EXECUTOR.execute(new ShowRecentsCommand(triggeredFromAltTab)); } public void onOverviewHidden() { - mMainThreadExecutor.execute(new HideRecentsCommand()); + MAIN_EXECUTOR.execute(new HideRecentsCommand()); } public void onTip(int actionType, int viewType) { - mMainThreadExecutor.execute(() -> + MAIN_EXECUTOR.execute(() -> UserEventDispatcher.newInstance(mContext).logActionTip(actionType, viewType)); } @@ -112,7 +109,7 @@ public class OverviewCommandHelper { TaskView taskView = rv.getNextTaskView(); if (taskView == null) { if (rv.getTaskViewCount() > 0) { - taskView = (TaskView) rv.getPageAt(0); + taskView = rv.getTaskViewAt(0); taskView.requestFocus(); } else { rv.requestFocus(); @@ -164,9 +161,6 @@ public class OverviewCommandHelper { @Override public void run() { - if (TestProtocol.sDebugTracing) { - Log.d(TestProtocol.ALL_APPS_UPON_RECENTS, "RecentsActivityCommand.run"); - } long elapsedTime = mCreateTime - mLastToggleTime; mLastToggleTime = mCreateTime; @@ -183,7 +177,7 @@ public class OverviewCommandHelper { // Otherwise, start overview. mListener = mHelper.createActivityInitListener(this::onActivityReady); mListener.registerAndStartActivity(mOverviewComponentObserver.getOverviewIntent(), - this::createWindowAnimation, mContext, mMainThreadExecutor.getHandler(), + this::createWindowAnimation, mContext, MAIN_EXECUTOR.getHandler(), mAnimationProvider.getRecentsLaunchDuration()); } diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/QuickstepTestInformationHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/QuickstepTestInformationHandler.java index da4642636..3c78dd8af 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/QuickstepTestInformationHandler.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/QuickstepTestInformationHandler.java @@ -1,12 +1,12 @@ package com.android.quickstep; +import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; + import android.content.Context; import android.os.Bundle; -import com.android.launcher3.MainThreadExecutor; import com.android.launcher3.testing.TestInformationHandler; import com.android.launcher3.testing.TestProtocol; -import com.android.launcher3.uioverrides.states.OverviewState; import com.android.launcher3.uioverrides.touchcontrollers.PortraitStatesTouchController; import com.android.quickstep.util.LayoutUtils; import com.android.quickstep.views.RecentsView; @@ -24,7 +24,7 @@ public class QuickstepTestInformationHandler extends TestInformationHandler { switch (method) { case TestProtocol.REQUEST_HOME_TO_OVERVIEW_SWIPE_HEIGHT: { final float swipeHeight = - OverviewState.getDefaultSwipeHeight(mContext, mDeviceProfile); + LayoutUtils.getDefaultSwipeHeight(mContext, mDeviceProfile); response.putInt(TestProtocol.TEST_INFO_RESPONSE_FIELD, (int) swipeHeight); return response; } @@ -36,12 +36,6 @@ public class QuickstepTestInformationHandler extends TestInformationHandler { return response; } - case TestProtocol.REQUEST_IS_LAUNCHER_INITIALIZED: { - response.putBoolean(TestProtocol.TEST_INFO_RESPONSE_FIELD, - TouchInteractionService.isInitialized()); - return response; - } - case TestProtocol.REQUEST_HOTSEAT_TOP: { if (mLauncher == null) return null; @@ -52,7 +46,7 @@ public class QuickstepTestInformationHandler extends TestInformationHandler { case TestProtocol.REQUEST_OVERVIEW_LEFT_GESTURE_MARGIN: { try { - final int leftMargin = new MainThreadExecutor().submit(() -> + final int leftMargin = MAIN_EXECUTOR.submit(() -> mLauncher.<RecentsView>getOverviewPanel().getLeftGestureMargin()).get(); response.putInt(TestProtocol.TEST_INFO_RESPONSE_FIELD, leftMargin); } catch (ExecutionException e) { @@ -65,7 +59,7 @@ public class QuickstepTestInformationHandler extends TestInformationHandler { case TestProtocol.REQUEST_OVERVIEW_RIGHT_GESTURE_MARGIN: { try { - final int rightMargin = new MainThreadExecutor().submit(() -> + final int rightMargin = MAIN_EXECUTOR.submit(() -> mLauncher.<RecentsView>getOverviewPanel().getRightGestureMargin()). get(); response.putInt(TestProtocol.TEST_INFO_RESPONSE_FIELD, rightMargin); @@ -80,4 +74,9 @@ public class QuickstepTestInformationHandler extends TestInformationHandler { return super.call(method); } + + @Override + protected boolean isLauncherInitialized() { + return super.isLauncherInitialized() && TouchInteractionService.isInitialized(); + } } diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsActivity.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsActivity.java index f08ae4a82..9bdc98bf8 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsActivity.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsActivity.java @@ -136,6 +136,12 @@ public final class RecentsActivity extends BaseRecentsActivity { } @Override + public void returnToHomescreen() { + super.returnToHomescreen(); + // TODO(b/137318995) This should go home, but doing so removes freeform windows + } + + @Override public ActivityOptions getActivityLaunchOptions(final View v) { if (!(v instanceof TaskView)) { return null; diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/SwipeSharedState.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/SwipeSharedState.java index c55f656df..8783ee3cc 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/SwipeSharedState.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/SwipeSharedState.java @@ -15,7 +15,7 @@ */ package com.android.quickstep; -import static com.android.quickstep.TouchInteractionService.MAIN_THREAD_EXECUTOR; +import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; import android.util.Log; @@ -25,6 +25,7 @@ import com.android.launcher3.util.Preconditions; import com.android.quickstep.util.RecentsAnimationListenerSet; import com.android.quickstep.util.SwipeAnimationTargetSet; import com.android.quickstep.util.SwipeAnimationTargetSet.SwipeAnimationListener; + import java.io.PrintWriter; /** @@ -44,6 +45,7 @@ public class SwipeSharedState implements SwipeAnimationListener { public boolean goingToLauncher; public boolean recentsAnimationFinishInterrupted; public int nextRunningTaskId = -1; + private int mLogId; public void setOverviewComponentObserver(OverviewComponentObserver observer) { mOverviewComponentObserver = observer; @@ -77,7 +79,7 @@ public class SwipeSharedState implements SwipeAnimationListener { mRecentsAnimationListener.removeListener(this); mRecentsAnimationListener.cancelListener(); if (mLastAnimationRunning && mLastAnimationTarget != null) { - Utilities.postAsyncCallback(MAIN_THREAD_EXECUTOR.getHandler(), + Utilities.postAsyncCallback(MAIN_EXECUTOR.getHandler(), finishAnimation ? mLastAnimationTarget::finishAnimation : mLastAnimationTarget::cancelAnimation); @@ -155,5 +157,10 @@ public class SwipeSharedState implements SwipeAnimationListener { pw.println(prefix + "nextRunningTaskId=" + nextRunningTaskId); pw.println(prefix + "lastAnimationCancelled=" + mLastAnimationCancelled); pw.println(prefix + "lastAnimationRunning=" + mLastAnimationRunning); + pw.println(prefix + "logTraceId=" + mLogId); + } + + public void setLogTraceId(int logId) { + this.mLogId = logId; } } diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskSystemShortcut.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskSystemShortcut.java index fd4592307..1af0db07d 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskSystemShortcut.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskSystemShortcut.java @@ -36,8 +36,6 @@ import android.view.View; import com.android.launcher3.BaseDraggingActivity; import com.android.launcher3.DeviceProfile; import com.android.launcher3.ItemInfo; -import com.android.launcher3.Launcher; -import com.android.launcher3.LauncherState; import com.android.launcher3.R; import com.android.launcher3.WorkspaceItemInfo; import com.android.launcher3.popup.SystemShortcut; @@ -267,12 +265,16 @@ public class TaskSystemShortcut<T extends SystemShortcut> extends SystemShortcut @Override protected ActivityOptions makeLaunchOptions(Activity activity) { - return ActivityOptionsCompat.makeFreeformOptions(); + ActivityOptions activityOptions = ActivityOptionsCompat.makeFreeformOptions(); + // Arbitrary bounds only because freeform is in dev mode right now + Rect r = new Rect(50, 50, 200, 200); + activityOptions.setLaunchBounds(r); + return activityOptions; } @Override protected boolean onActivityStarted(BaseDraggingActivity activity) { - Launcher.getLauncher(activity).getStateManager().goToState(LauncherState.NORMAL); + activity.returnToHomescreen(); return true; } } 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 86ba85578..f32182600 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java @@ -23,6 +23,8 @@ import static com.android.launcher3.config.FeatureFlags.ENABLE_HINTS_IN_OVERVIEW import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE; import static com.android.launcher3.config.FeatureFlags.FAKE_LANDSCAPE_UI; import static com.android.launcher3.config.FeatureFlags.QUICKSTEP_SPRINGS; +import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; +import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR; 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_A11Y_BUTTON_CLICKABLE; @@ -40,7 +42,6 @@ import android.annotation.TargetApi; import android.app.ActivityManager; import android.app.ActivityManager.RunningTaskInfo; import android.app.Service; -import android.app.TaskInfo; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; @@ -50,8 +51,6 @@ import android.content.res.Configuration; import android.graphics.Point; import android.graphics.RectF; import android.graphics.Region; -import android.hardware.display.DisplayManager; -import android.hardware.display.DisplayManager.DisplayListener; import android.os.Build; import android.os.Bundle; import android.os.IBinder; @@ -61,29 +60,26 @@ import android.os.RemoteException; import android.text.TextUtils; import android.util.Log; import android.view.Choreographer; -import android.view.Display; import android.view.InputEvent; import android.view.MotionEvent; import android.view.Surface; -import android.view.WindowManager; import androidx.annotation.BinderThread; import androidx.annotation.UiThread; import androidx.annotation.WorkerThread; import com.android.launcher3.BaseDraggingActivity; -import com.android.launcher3.MainThreadExecutor; import com.android.launcher3.R; import com.android.launcher3.ResourceUtils; import com.android.launcher3.Utilities; import com.android.launcher3.compat.UserManagerCompat; +import com.android.launcher3.config.BaseFlags; import com.android.launcher3.logging.EventLogArray; import com.android.launcher3.logging.UserEventDispatcher; import com.android.launcher3.model.AppLaunchTracker; import com.android.launcher3.provider.RestoreDbTask; import com.android.launcher3.testing.TestProtocol; -import com.android.launcher3.util.LooperExecutor; -import com.android.launcher3.util.UiThreadHelper; +import com.android.launcher3.util.DefaultDisplay; import com.android.quickstep.SysUINavigationMode.Mode; import com.android.quickstep.SysUINavigationMode.NavigationModeChangeListener; import com.android.quickstep.inputconsumers.AccessibilityInputConsumer; @@ -96,6 +92,7 @@ import com.android.quickstep.inputconsumers.OverviewInputConsumer; import com.android.quickstep.inputconsumers.OverviewWithoutFocusInputConsumer; import com.android.quickstep.inputconsumers.ResetGestureInputConsumer; import com.android.quickstep.inputconsumers.ScreenPinnedInputConsumer; +import com.android.quickstep.util.AssistantUtilities; import com.android.systemui.shared.recents.IOverviewProxy; import com.android.systemui.shared.recents.ISystemUiProxy; import com.android.systemui.shared.system.ActivityManagerWrapper; @@ -107,7 +104,6 @@ import com.android.systemui.shared.system.QuickStepContract.SystemUiStateFlags; import com.android.systemui.shared.system.RecentsAnimationListener; import com.android.systemui.shared.system.SystemGestureExclusionListenerCompat; -import com.android.systemui.shared.system.TaskInfoCompat; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.Arrays; @@ -136,11 +132,14 @@ class ArgList extends LinkedList<String> { */ @TargetApi(Build.VERSION_CODES.Q) public class TouchInteractionService extends Service implements - NavigationModeChangeListener, DisplayListener { + NavigationModeChangeListener, DefaultDisplay.DisplayInfoChangeListener { + + /** + * NOTE: This value should be kept same as + * ActivityTaskManagerService#INTENT_EXTRA_LOG_TRACE_ID in platform + */ + public static final String INTENT_EXTRA_LOG_TRACE_ID = "INTENT_EXTRA_LOG_TRACE_ID"; - public static final MainThreadExecutor MAIN_THREAD_EXECUTOR = new MainThreadExecutor(); - public static final LooperExecutor BACKGROUND_EXECUTOR = - new LooperExecutor(UiThreadHelper.getBackgroundLooper()); public static final EventLogArray TOUCH_INTERACTION_LOG = new EventLogArray("touch_interaction_log", 40); @@ -161,9 +160,9 @@ public class TouchInteractionService extends Service implements public void onInitialize(Bundle bundle) { mISystemUiProxy = ISystemUiProxy.Stub .asInterface(bundle.getBinder(KEY_EXTRA_SYSUI_PROXY)); - MAIN_THREAD_EXECUTOR.execute(TouchInteractionService.this::initInputMonitor); - MAIN_THREAD_EXECUTOR.execute(TouchInteractionService.this::onSystemUiProxySet); - MAIN_THREAD_EXECUTOR.execute(() -> preloadOverview(true /* fromInit */)); + MAIN_EXECUTOR.execute(TouchInteractionService.this::initInputMonitor); + MAIN_EXECUTOR.execute(TouchInteractionService.this::onSystemUiProxySet); + MAIN_EXECUTOR.execute(() -> preloadOverview(true /* fromInit */)); sIsInitialized = true; } @@ -198,7 +197,7 @@ public class TouchInteractionService extends Service implements @Override public void onAssistantVisibilityChanged(float visibility) { mLastAssistantVisibility = visibility; - MAIN_THREAD_EXECUTOR.execute( + MAIN_EXECUTOR.execute( TouchInteractionService.this::onAssistantVisibilityChanged); } @@ -214,13 +213,13 @@ public class TouchInteractionService extends Service implements isButton, gestureSwipeLeft, activityControl.getContainerType()); if (completed && !isButton && shouldNotifyBackGesture()) { - BACKGROUND_EXECUTOR.execute(TouchInteractionService.this::tryNotifyBackGesture); + UI_HELPER_EXECUTOR.execute(TouchInteractionService.this::tryNotifyBackGesture); } } public void onSystemUiStateChanged(int stateFlags) { mSystemUiStateFlags = stateFlags; - MAIN_THREAD_EXECUTOR.execute(TouchInteractionService.this::onSystemUiFlagsChanged); + MAIN_EXECUTOR.execute(TouchInteractionService.this::onSystemUiFlagsChanged); } /** Deprecated methods **/ @@ -244,6 +243,7 @@ public class TouchInteractionService extends Service implements private static boolean sConnected = false; private static boolean sIsInitialized = false; private static final SwipeSharedState sSwipeSharedState = new SwipeSharedState(); + private int mLogId; public static boolean isConnected() { return sConnected; @@ -323,8 +323,7 @@ public class TouchInteractionService extends Service implements registerReceiver(mUserUnlockedReceiver, new IntentFilter(Intent.ACTION_USER_UNLOCKED)); } - mDefaultDisplayId = getSystemService(WindowManager.class).getDefaultDisplay() - .getDisplayId(); + mDefaultDisplayId = DefaultDisplay.INSTANCE.get(this).getInfo().id; String blockingActivity = getString(R.string.gesture_blocking_activity); mGestureBlockingActivity = TextUtils.isEmpty(blockingActivity) ? null : ComponentName.unflattenFromString(blockingActivity); @@ -391,9 +390,8 @@ public class TouchInteractionService extends Service implements return; } - Display defaultDisplay = getSystemService(WindowManager.class).getDefaultDisplay(); - Point realSize = new Point(); - defaultDisplay.getRealSize(realSize); + DefaultDisplay.Info displayInfo = DefaultDisplay.INSTANCE.get(this).getInfo(); + Point realSize = new Point(displayInfo.realSize); mSwipeTouchRegion.set(0, 0, realSize.x, realSize.y); if (mMode == Mode.NO_BUTTON) { int touchHeight = getNavbarSize(ResourceUtils.NAVBAR_BOTTOM_GESTURE_SIZE); @@ -415,7 +413,7 @@ public class TouchInteractionService extends Service implements } else { mAssistantLeftRegion.setEmpty(); mAssistantRightRegion.setEmpty(); - switch (defaultDisplay.getRotation()) { + switch (displayInfo.rotation) { case Surface.ROTATION_90: mSwipeTouchRegion.left = mSwipeTouchRegion.right - getNavbarSize(ResourceUtils.NAVBAR_LANDSCAPE_LEFT_RIGHT_SIZE); @@ -438,10 +436,9 @@ public class TouchInteractionService extends Service implements } if (mMode.hasGestures != newMode.hasGestures) { if (newMode.hasGestures) { - getSystemService(DisplayManager.class).registerDisplayListener( - this, MAIN_THREAD_EXECUTOR.getHandler()); + DefaultDisplay.INSTANCE.get(this).addChangeListener(this); } else { - getSystemService(DisplayManager.class).unregisterDisplayListener(this); + DefaultDisplay.INSTANCE.get(this).removeChangeListener(this); } } mMode = newMode; @@ -457,14 +454,8 @@ public class TouchInteractionService extends Service implements } @Override - public void onDisplayAdded(int i) { } - - @Override - public void onDisplayRemoved(int i) { } - - @Override - public void onDisplayChanged(int displayId) { - if (displayId != mDefaultDisplayId) { + public void onDisplayInfoChanged(DefaultDisplay.Info info, int flags) { + if (info.id != mDefaultDisplayId) { return; } @@ -529,7 +520,7 @@ public class TouchInteractionService extends Service implements } disposeEventHandlers(); if (mMode.hasGestures) { - getSystemService(DisplayManager.class).unregisterDisplayListener(this); + DefaultDisplay.INSTANCE.get(this).removeChangeListener(this); } sConnected = false; @@ -554,9 +545,12 @@ public class TouchInteractionService extends Service implements Log.e(TAG, "Unknown event " + ev); return; } + MotionEvent event = (MotionEvent) ev; - TOUCH_INTERACTION_LOG.addLog("onMotionEvent", event.getActionMasked()); if (event.getAction() == ACTION_DOWN) { + mLogId = TOUCH_INTERACTION_LOG.generateAndSetLogId(); + sSwipeSharedState.setLogTraceId(mLogId); + if (mSwipeTouchRegion.contains(event.getX(), event.getY())) { boolean useSharedState = mConsumer.useSharedSwipeState(); mConsumer.onConsumerAboutToBeSwitched(); @@ -576,6 +570,8 @@ public class TouchInteractionService extends Service implements mUncheckedConsumer = InputConsumer.NO_OP; } } + + TOUCH_INTERACTION_LOG.addLog("onMotionEvent", event.getActionMasked()); mUncheckedConsumer.onMotionEvent(event); } @@ -653,16 +649,14 @@ public class TouchInteractionService extends Service implements mOverviewComponentObserver.getActivityControlHelper(); boolean forceOverviewInputConsumer = false; - if (isExcludedAssistant(runningTaskInfo)) { + if (AssistantUtilities.isExcludedAssistant(runningTaskInfo)) { // In the case where we are in the excluded assistant state, ignore it and treat the // running activity as the task behind the assistant - runningTaskInfo = mAM.getRunningTask(ACTIVITY_TYPE_ASSISTANT); - if (!ActivityManagerWrapper.isHomeTask(runningTaskInfo)) { - final ComponentName homeComponent = - mOverviewComponentObserver.getHomeIntent().getComponent(); - forceOverviewInputConsumer = - runningTaskInfo.baseIntent.getComponent().equals(homeComponent); - } + runningTaskInfo = mAM.getRunningTask(ACTIVITY_TYPE_ASSISTANT /* ignoreActivityType */); + ComponentName homeComponent = mOverviewComponentObserver.getHomeIntent().getComponent(); + ComponentName runningComponent = runningTaskInfo.baseIntent.getComponent(); + forceOverviewInputConsumer = + runningComponent != null && runningComponent.equals(homeComponent); } if (runningTaskInfo == null && !sSwipeSharedState.goingToLauncher @@ -676,9 +670,9 @@ public class TouchInteractionService extends Service implements return createOtherActivityInputConsumer(event, info); } else if (sSwipeSharedState.goingToLauncher || activityControl.isResumed() || forceOverviewInputConsumer) { - return createOverviewInputConsumer(event); + return createOverviewInputConsumer(event, forceOverviewInputConsumer); } else if (ENABLE_QUICKSTEP_LIVE_TILE.get() && activityControl.isInLiveTileMode()) { - return createOverviewInputConsumer(event); + return createOverviewInputConsumer(event, forceOverviewInputConsumer); } else if (mGestureBlockingActivity != null && runningTaskInfo != null && mGestureBlockingActivity.equals(runningTaskInfo.topActivity)) { return mResetGestureInputConsumer; @@ -687,12 +681,6 @@ public class TouchInteractionService extends Service implements } } - private boolean isExcludedAssistant(TaskInfo info) { - return info != null - && TaskInfoCompat.getActivityType(info) == ACTIVITY_TYPE_ASSISTANT - && (info.baseIntent.getFlags() & Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) != 0; - } - private boolean disableHorizontalSwipe(MotionEvent event) { // mExclusionRegion can change on binder thread, use a local instance here. Region exclusionRegion = mExclusionRegion; @@ -718,19 +706,20 @@ public class TouchInteractionService extends Service implements return new OtherActivityInputConsumer(this, runningTaskInfo, shouldDefer, mOverviewCallbacks, this::onConsumerInactive, sSwipeSharedState, mInputMonitorCompat, mSwipeTouchRegion, - disableHorizontalSwipe(event), factory); + disableHorizontalSwipe(event), factory, mLogId); } private InputConsumer createDeviceLockedInputConsumer(RunningTaskInfo taskInfo) { if (mMode == Mode.NO_BUTTON && taskInfo != null) { return new DeviceLockedInputConsumer(this, sSwipeSharedState, mInputMonitorCompat, - mSwipeTouchRegion, taskInfo.taskId); + mSwipeTouchRegion, taskInfo.taskId, mLogId); } else { return mResetGestureInputConsumer; } } - public InputConsumer createOverviewInputConsumer(MotionEvent event) { + public InputConsumer createOverviewInputConsumer(MotionEvent event, + boolean forceOverviewInputConsumer) { final ActivityControlHelper activityControl = mOverviewComponentObserver.getActivityControlHelper(); BaseDraggingActivity activity = activityControl.getCreatedActivity(); @@ -738,7 +727,10 @@ public class TouchInteractionService extends Service implements return mResetGestureInputConsumer; } - if (activity.getRootView().hasWindowFocus() || sSwipeSharedState.goingToLauncher) { + if (activity.getRootView().hasWindowFocus() + || sSwipeSharedState.goingToLauncher + || (BaseFlags.ASSISTANT_GIVES_LAUNCHER_FOCUS.get() + && forceOverviewInputConsumer)) { return new OverviewInputConsumer(activity, mInputMonitorCompat, false /* startingInActivityBounds */); } else { @@ -788,7 +780,8 @@ public class TouchInteractionService extends Service implements } // Pass null animation handler to indicate this start is preload. - startRecentsActivityAsync(mOverviewComponentObserver.getOverviewIntentIgnoreSysUiState(), null); + startRecentsActivityAsync(mOverviewComponentObserver.getOverviewIntentIgnoreSysUiState(), + null); } @Override @@ -897,7 +890,7 @@ public class TouchInteractionService extends Service implements } public static void startRecentsActivityAsync(Intent intent, RecentsAnimationListener listener) { - BACKGROUND_EXECUTOR.execute(() -> ActivityManagerWrapper.getInstance() + UI_HELPER_EXECUTOR.execute(() -> ActivityManagerWrapper.getInstance() .startRecentsActivity(intent, null, listener, null, null)); } } diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java index e1085e608..c80dede53 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java @@ -479,8 +479,8 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> recentsAttachedToAppWindow = mIsShelfPeeking || mIsLikelyToStartNewTask; if (animate) { // Only animate if an adjacent task view is visible on screen. - TaskView adjacentTask1 = mRecentsView.getTaskViewAt(runningTaskIndex + 1); - TaskView adjacentTask2 = mRecentsView.getTaskViewAt(runningTaskIndex - 1); + TaskView adjacentTask1 = mRecentsView.getNextTaskView(); + TaskView adjacentTask2 = mRecentsView.getPreviousTaskView(); float prevTranslationX = mRecentsView.getTranslationX(); mRecentsView.setTranslationX(0); animate = (adjacentTask1 != null && adjacentTask1.getGlobalVisibleRect(TEMP_RECT)) @@ -590,8 +590,7 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> */ private void updateSysUiFlags(float windowProgress) { if (mRecentsView != null) { - TaskView centermostTask = mRecentsView.getTaskViewAt(mRecentsView - .getPageNearestToCenterOfScreen()); + TaskView centermostTask = mRecentsView.getTaskViewNearestToCenterOfScreen(); int centermostTaskFlags = centermostTask == null ? 0 : centermostTask.getThumbnail().getSysUiStatusNavFlags(); boolean useHomeScreenFlags = windowProgress > 1 - UPDATE_SYSUI_FLAGS_THRESHOLD; diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/FallbackRecentsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/FallbackRecentsView.java index 2e9c0a3c4..7f1aae5e2 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/FallbackRecentsView.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/FallbackRecentsView.java @@ -176,7 +176,7 @@ public class FallbackRecentsView extends RecentsView<RecentsActivity> { protected void applyLoadPlan(ArrayList<Task> tasks) { // When quick-switching on 3p-launcher, we add a "dummy" tile corresponding to Launcher // as well. This tile is never shown as we have setCurrentTaskHidden, but allows use to - // track the index of the next task appropriately, as it we are switching on any other app. + // track the index of the next task appropriately, as if we are switching on any other app. if (mRunningTaskInfo != null && mRunningTaskInfo.taskId == mRunningTaskId) { // Check if the task list has running task boolean found = false; diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java index 3d763ab52..b24c78810 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java @@ -22,8 +22,9 @@ import static android.view.MotionEvent.ACTION_UP; import static com.android.launcher3.Utilities.squaredHypot; import static com.android.launcher3.Utilities.squaredTouchSlop; import static com.android.quickstep.MultiStateCallback.DEBUG_STATES; -import static com.android.quickstep.WindowTransformSwipeHandler.MIN_PROGRESS_FOR_OVERVIEW; +import static com.android.quickstep.TouchInteractionService.INTENT_EXTRA_LOG_TRACE_ID; import static com.android.quickstep.TouchInteractionService.startRecentsActivityAsync; +import static com.android.quickstep.WindowTransformSwipeHandler.MIN_PROGRESS_FOR_OVERVIEW; import android.content.ComponentName; import android.content.Context; @@ -35,10 +36,10 @@ import android.graphics.RectF; import android.view.MotionEvent; import android.view.VelocityTracker; import android.view.ViewConfiguration; -import android.view.WindowManager; import com.android.launcher3.R; import com.android.launcher3.Utilities; +import com.android.launcher3.util.DefaultDisplay; import com.android.quickstep.LockScreenRecentsActivity; import com.android.quickstep.MultiStateCallback; import com.android.quickstep.SwipeSharedState; @@ -76,6 +77,7 @@ public class DeviceLockedInputConsumer implements InputConsumer, private final PointF mTouchDown = new PointF(); private final ClipAnimationHelper mClipAnimationHelper; + private int mLogId; private final ClipAnimationHelper.TransformParams mTransformParams; private final Point mDisplaySize; private final MultiStateCallback mStateCallback; @@ -90,19 +92,20 @@ public class DeviceLockedInputConsumer implements InputConsumer, private SwipeAnimationTargetSet mTargetSet; public DeviceLockedInputConsumer(Context context, SwipeSharedState swipeSharedState, - InputMonitorCompat inputMonitorCompat, RectF swipeTouchRegion, int runningTaskId) { + InputMonitorCompat inputMonitorCompat, RectF swipeTouchRegion, int runningTaskId, + int logId) { mContext = context; mTouchSlopSquared = squaredTouchSlop(context); mSwipeSharedState = swipeSharedState; mClipAnimationHelper = new ClipAnimationHelper(context); + mLogId = logId; mTransformParams = new ClipAnimationHelper.TransformParams(); mInputMonitorCompat = inputMonitorCompat; mSwipeTouchRegion = swipeTouchRegion; mRunningTaskId = runningTaskId; // Do not use DeviceProfile as the user data might be locked - mDisplaySize = new Point(); - context.getSystemService(WindowManager.class).getDefaultDisplay().getRealSize(mDisplaySize); + mDisplaySize = DefaultDisplay.INSTANCE.get(context).getInfo().realSize; // Init states mStateCallback = new MultiStateCallback(STATE_NAMES); @@ -205,7 +208,8 @@ public class DeviceLockedInputConsumer implements InputConsumer, Intent intent = new Intent(Intent.ACTION_MAIN) .addCategory(Intent.CATEGORY_DEFAULT) .setComponent(new ComponentName(mContext, LockScreenRecentsActivity.class)) - .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); + .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK) + .putExtra(INTENT_EXTRA_LOG_TRACE_ID, mLogId); mInputMonitorCompat.pilferPointers(); startRecentsActivityAsync(intent, newListenerSet); diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/FallbackNoButtonInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/FallbackNoButtonInputConsumer.java index 6ec1da0c4..e0ff8afe6 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/FallbackNoButtonInputConsumer.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/FallbackNoButtonInputConsumer.java @@ -50,7 +50,6 @@ import com.android.quickstep.fallback.FallbackRecentsView; import com.android.quickstep.util.ObjectWrapper; import com.android.quickstep.util.RectFSpringAnim; import com.android.quickstep.util.SwipeAnimationTargetSet; -import com.android.quickstep.views.RecentsView; import com.android.quickstep.views.TaskView; import com.android.systemui.shared.recents.model.ThumbnailData; import com.android.systemui.shared.system.ActivityManagerWrapper; diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java index 86766d99f..e41880df5 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java @@ -22,11 +22,11 @@ import static android.view.MotionEvent.ACTION_POINTER_DOWN; import static android.view.MotionEvent.ACTION_POINTER_UP; import static android.view.MotionEvent.ACTION_UP; import static android.view.MotionEvent.INVALID_POINTER_ID; - import static com.android.launcher3.Utilities.EDGE_NAV_BAR; import static com.android.launcher3.Utilities.squaredHypot; import static com.android.launcher3.util.RaceConditionTracker.ENTER; import static com.android.launcher3.util.RaceConditionTracker.EXIT; +import static com.android.quickstep.TouchInteractionService.INTENT_EXTRA_LOG_TRACE_ID; import static com.android.quickstep.TouchInteractionService.TOUCH_INTERACTION_LOG; import static com.android.quickstep.TouchInteractionService.startRecentsActivityAsync; import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS; @@ -35,6 +35,7 @@ import android.annotation.TargetApi; import android.app.ActivityManager.RunningTaskInfo; import android.content.Context; import android.content.ContextWrapper; +import android.content.Intent; import android.graphics.PointF; import android.graphics.RectF; import android.os.Build; @@ -43,12 +44,13 @@ import android.os.Looper; import android.view.MotionEvent; import android.view.VelocityTracker; import android.view.ViewConfiguration; - +import androidx.annotation.UiThread; import com.android.launcher3.R; import com.android.launcher3.util.Preconditions; import com.android.launcher3.util.RaceConditionTracker; import com.android.launcher3.util.TraceHelper; import com.android.quickstep.BaseSwipeUpHandler; +import com.android.quickstep.BaseSwipeUpHandler.Factory; import com.android.quickstep.OverviewCallbacks; import com.android.quickstep.SwipeSharedState; import com.android.quickstep.SysUINavigationMode; @@ -59,11 +61,8 @@ import com.android.quickstep.util.NavBarPosition; import com.android.quickstep.util.RecentsAnimationListenerSet; import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.shared.system.InputMonitorCompat; - import java.util.function.Consumer; -import androidx.annotation.UiThread; - /** * Input consumer for handling events originating from an activity other than Launcher */ @@ -119,14 +118,16 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC ActivityManagerWrapper.getInstance().cancelRecentsAnimation( true /* restoreHomeStackPosition */); }; + private int mLogId; public OtherActivityInputConsumer(Context base, RunningTaskInfo runningTaskInfo, boolean isDeferredDownTarget, OverviewCallbacks overviewCallbacks, Consumer<OtherActivityInputConsumer> onCompleteCallback, SwipeSharedState swipeSharedState, InputMonitorCompat inputMonitorCompat, RectF swipeTouchRegion, boolean disableHorizontalSwipe, - BaseSwipeUpHandler.Factory handlerFactory) { + Factory handlerFactory, int logId) { super(base); + mLogId = logId; mMainThreadHandler = new Handler(Looper.getMainLooper()); mRunningTask = runningTaskInfo; @@ -341,7 +342,9 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC RecentsAnimationListenerSet newListenerSet = mSwipeSharedState.newRecentsAnimationListenerSet(); newListenerSet.addListener(handler); - startRecentsActivityAsync(handler.getLaunchIntent(), newListenerSet); + Intent intent = handler.getLaunchIntent(); + intent.putExtra(INTENT_EXTRA_LOG_TRACE_ID, mLogId); + startRecentsActivityAsync(intent, newListenerSet); } } diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverviewWithoutFocusInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverviewWithoutFocusInputConsumer.java index 05cbb789d..f40d55263 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverviewWithoutFocusInputConsumer.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverviewWithoutFocusInputConsumer.java @@ -22,9 +22,9 @@ import static android.view.MotionEvent.ACTION_UP; import static com.android.launcher3.Utilities.squaredHypot; import static com.android.quickstep.TouchInteractionService.TOUCH_INTERACTION_LOG; -import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS; import android.content.Context; +import android.content.Intent; import android.graphics.PointF; import android.view.MotionEvent; import android.view.VelocityTracker; @@ -34,12 +34,9 @@ import com.android.launcher3.BaseActivity; import com.android.launcher3.BaseDraggingActivity; import com.android.launcher3.Utilities; import com.android.launcher3.logging.StatsLogUtils; -import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType; import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction; import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch; -import com.android.quickstep.OverviewCallbacks; import com.android.quickstep.util.NavBarPosition; -import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.shared.system.InputMonitorCompat; public class OverviewWithoutFocusInputConsumer implements InputConsumer { @@ -148,9 +145,9 @@ public class OverviewWithoutFocusInputConsumer implements InputConsumer { } if (triggerQuickstep) { - OverviewCallbacks.get(mContext).closeAllWindows(); - ActivityManagerWrapper.getInstance() - .closeSystemWindows(CLOSE_SYSTEM_WINDOWS_REASON_RECENTS); + mContext.startActivity(new Intent(Intent.ACTION_MAIN) + .addCategory(Intent.CATEGORY_HOME) + .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)); TOUCH_INTERACTION_LOG.addLog("startQuickstep"); BaseActivity activity = BaseDraggingActivity.fromContext(mContext); int pageIndex = -1; // This number doesn't reflect workspace page index. diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/logging/UserEventDispatcherAppPredictionExtension.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/logging/UserEventDispatcherAppPredictionExtension.java new file mode 100644 index 000000000..b251f9e69 --- /dev/null +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/logging/UserEventDispatcherAppPredictionExtension.java @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.quickstep.logging; + +import android.content.Context; + +import androidx.annotation.NonNull; + +import com.android.launcher3.ItemInfo; +import com.android.launcher3.appprediction.PredictionUiStateManager; +import com.android.launcher3.userevent.nano.LauncherLogProto; + +/** + * This class handles AOSP MetricsLogger function calls and logging around + * quickstep interactions and app launches. + */ +@SuppressWarnings("unused") +public class UserEventDispatcherAppPredictionExtension extends UserEventDispatcherExtension { + + public static final int ALL_APPS_PREDICTION_TIPS = 2; + + private static final String TAG = "UserEventDispatcher"; + + public UserEventDispatcherAppPredictionExtension(Context context) { + super(context); + } + + @Override + protected void onFillInLogContainerData( + @NonNull ItemInfo itemInfo, @NonNull LauncherLogProto.Target target, + @NonNull LauncherLogProto.Target targetParent) { + PredictionUiStateManager.fillInPredictedRank(itemInfo, target); + } +} diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/ClipAnimationHelper.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/ClipAnimationHelper.java index 90989feb4..a12ae7a45 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/ClipAnimationHelper.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/ClipAnimationHelper.java @@ -37,6 +37,7 @@ import com.android.launcher3.DeviceProfile; import com.android.launcher3.LauncherState; import com.android.launcher3.R; import com.android.launcher3.Utilities; +import com.android.launcher3.anim.Interpolators; import com.android.launcher3.views.BaseDragLayer; import com.android.quickstep.RecentsModel; import com.android.quickstep.views.RecentsView; @@ -50,8 +51,6 @@ import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat. import com.android.systemui.shared.system.TransactionCompat; import com.android.systemui.shared.system.WindowManagerWrapper; -import java.util.function.BiFunction; - /** * Utility class to handle window clip animation */ @@ -213,6 +212,11 @@ public class ClipAnimationHelper { } mCurrentCornerRadius = cornerRadius; } + // Fade out Assistant overlay. + if (app.activityType == RemoteAnimationTargetCompat.ACTIVITY_TYPE_ASSISTANT + && app.isNotInRecents) { + alpha = 1 - Interpolators.DEACCEL_2_5.getInterpolation(progress); + } } else if (targetSet.hasRecents) { // If home has a different target then recents, reverse anim the // home target. diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/NavBarPosition.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/NavBarPosition.java index 3ce341d8c..bbb318a02 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/NavBarPosition.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/NavBarPosition.java @@ -21,9 +21,9 @@ import static com.android.quickstep.SysUINavigationMode.Mode.NO_BUTTON; import android.content.Context; import android.view.Surface; -import android.view.WindowManager; import com.android.launcher3.graphics.RotationMode; +import com.android.launcher3.util.DefaultDisplay; import com.android.quickstep.SysUINavigationMode; /** @@ -36,8 +36,7 @@ public class NavBarPosition { public NavBarPosition(Context context) { mMode = SysUINavigationMode.getMode(context); - mDisplayRotation = context.getSystemService(WindowManager.class) - .getDefaultDisplay().getRotation(); + mDisplayRotation = DefaultDisplay.INSTANCE.get(context).getInfo().rotation; } public boolean isRightEdge() { diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/RecentsAnimationListenerSet.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/RecentsAnimationListenerSet.java index 14083dd95..b1999d716 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/RecentsAnimationListenerSet.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/RecentsAnimationListenerSet.java @@ -15,11 +15,13 @@ */ package com.android.quickstep.util; -import static com.android.quickstep.TouchInteractionService.MAIN_THREAD_EXECUTOR; +import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; import android.graphics.Rect; import android.util.ArraySet; +import androidx.annotation.UiThread; + import com.android.launcher3.Utilities; import com.android.launcher3.util.Preconditions; import com.android.quickstep.util.SwipeAnimationTargetSet.SwipeAnimationListener; @@ -31,8 +33,6 @@ import com.android.systemui.shared.system.RemoteAnimationTargetCompat; import java.util.Set; import java.util.function.Consumer; -import androidx.annotation.UiThread; - /** * Wrapper around {@link RecentsAnimationListener} which delegates callbacks to multiple listeners * on the main thread @@ -82,7 +82,7 @@ public class RecentsAnimationListenerSet implements RecentsAnimationListener { if (mCancelled) { targetSet.cancelAnimation(); } else { - Utilities.postAsyncCallback(MAIN_THREAD_EXECUTOR.getHandler(), () -> { + Utilities.postAsyncCallback(MAIN_EXECUTOR.getHandler(), () -> { for (SwipeAnimationListener listener : getListeners()) { listener.onRecentsAnimationStart(targetSet); } @@ -92,14 +92,14 @@ public class RecentsAnimationListenerSet implements RecentsAnimationListener { @Override public final void onAnimationCanceled(ThumbnailData thumbnailData) { - Utilities.postAsyncCallback(MAIN_THREAD_EXECUTOR.getHandler(), () -> { + Utilities.postAsyncCallback(MAIN_EXECUTOR.getHandler(), () -> { for (SwipeAnimationListener listener : getListeners()) { listener.onRecentsAnimationCanceled(); } }); // TODO: handle the transition better instead of simply using a transition delay. if (thumbnailData != null) { - MAIN_THREAD_EXECUTOR.getHandler().postDelayed(() -> mController.cleanupScreenshot(), + MAIN_EXECUTOR.getHandler().postDelayed(() -> mController.cleanupScreenshot(), TRANSITION_DELAY); } } diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/SwipeAnimationTargetSet.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/SwipeAnimationTargetSet.java index 381c27a28..3619d3a0e 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/SwipeAnimationTargetSet.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/SwipeAnimationTargetSet.java @@ -15,8 +15,8 @@ */ package com.android.quickstep.util; -import static com.android.quickstep.TouchInteractionService.BACKGROUND_EXECUTOR; -import static com.android.quickstep.TouchInteractionService.MAIN_THREAD_EXECUTOR; +import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; +import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR; import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING; import android.graphics.Rect; @@ -68,25 +68,25 @@ public class SwipeAnimationTargetSet extends RemoteAnimationTargetSet { public void finishController(boolean toRecents, Runnable callback, boolean sendUserLeaveHint) { mOnFinishListener.accept(this); - BACKGROUND_EXECUTOR.execute(() -> { + UI_HELPER_EXECUTOR.execute(() -> { controller.setInputConsumerEnabled(false); controller.finish(toRecents, sendUserLeaveHint); if (callback != null) { - MAIN_THREAD_EXECUTOR.execute(callback); + MAIN_EXECUTOR.execute(callback); } }); } public void enableInputConsumer() { - BACKGROUND_EXECUTOR.submit(() -> { + UI_HELPER_EXECUTOR.submit(() -> { controller.hideCurrentInputMethod(); controller.setInputConsumerEnabled(true); }); } public void setWindowThresholdCrossed(boolean thresholdCrossed) { - BACKGROUND_EXECUTOR.execute(() -> { + UI_HELPER_EXECUTOR.execute(() -> { controller.setAnimationTargetsBehindSystemBars(!thresholdCrossed); if (mShouldMinimizeSplitScreen && thresholdCrossed) { // NOTE: As a workaround for conflicting animations (Launcher animating the task diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/DigitalWellBeingToast.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/DigitalWellBeingToast.java index 7fac81385..b06d4bc35 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/DigitalWellBeingToast.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/DigitalWellBeingToast.java @@ -19,6 +19,7 @@ package com.android.quickstep.views; import static android.provider.Settings.ACTION_APP_USAGE_SETTINGS; import static com.android.launcher3.Utilities.prefixTextWithIcon; +import static com.android.launcher3.util.Executors.THREAD_POOL_EXECUTOR; import android.annotation.TargetApi; import android.app.ActivityOptions; @@ -41,7 +42,6 @@ import androidx.annotation.StringRes; import com.android.launcher3.BaseActivity; import com.android.launcher3.BaseDraggingActivity; import com.android.launcher3.R; -import com.android.launcher3.Utilities; import com.android.launcher3.userevent.nano.LauncherLogProto; import com.android.systemui.shared.recents.model.Task; @@ -117,7 +117,7 @@ public final class DigitalWellBeingToast { return; } - Utilities.THREAD_POOL_EXECUTOR.execute(() -> { + THREAD_POOL_EXECUTOR.execute(() -> { final AppUsageLimit usageLimit = mLauncherApps.getAppUsageLimit( task.getTopComponent().getPackageName(), UserHandle.of(task.key.userId)); diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java index 03441c87e..c2cb720ae 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java @@ -35,6 +35,7 @@ import android.os.Build; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.View; +import android.widget.FrameLayout; import com.android.launcher3.DeviceProfile; import com.android.launcher3.Hotseat; @@ -45,11 +46,14 @@ import com.android.launcher3.R; import com.android.launcher3.anim.Interpolators; import com.android.launcher3.appprediction.PredictionUiStateManager; import com.android.launcher3.appprediction.PredictionUiStateManager.Client; +import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper; import com.android.launcher3.views.ScrimView; import com.android.quickstep.SysUINavigationMode; import com.android.quickstep.util.ClipAnimationHelper; import com.android.quickstep.util.ClipAnimationHelper.TransformParams; import com.android.quickstep.util.LayoutUtils; +import com.android.systemui.plugins.PluginListener; +import com.android.systemui.plugins.RecentsExtraCard; /** * {@link RecentsView} used in Launcher activity @@ -57,8 +61,29 @@ import com.android.quickstep.util.LayoutUtils; @TargetApi(Build.VERSION_CODES.O) public class LauncherRecentsView extends RecentsView<Launcher> implements StateListener { + private static final Rect sTempRect = new Rect(); + private final TransformParams mTransformParams = new TransformParams(); + private RecentsExtraCard mRecentsExtraCardPlugin; + private RecentsExtraViewContainer mRecentsExtraViewContainer; + private PluginListener<RecentsExtraCard> mRecentsExtraCardPluginListener = + new PluginListener<RecentsExtraCard>() { + @Override + public void onPluginConnected(RecentsExtraCard recentsExtraCard, Context context) { + createRecentsExtraCard(); + mRecentsExtraCardPlugin = recentsExtraCard; + mRecentsExtraCardPlugin.setupView(context, mRecentsExtraViewContainer, mActivity); + } + + @Override + public void onPluginDisconnected(RecentsExtraCard plugin) { + removeView(mRecentsExtraViewContainer); + mRecentsExtraCardPlugin = null; + mRecentsExtraViewContainer = null; + } + }; + public LauncherRecentsView(Context context) { this(context, null); } @@ -144,6 +169,25 @@ public class LauncherRecentsView extends RecentsView<Launcher> implements StateL LayoutUtils.calculateLauncherTaskSize(getContext(), dp, outRect); } + /** + * @return The translationX to apply to this view so that the first task is just offscreen. + */ + public float getOffscreenTranslationX(float recentsScale) { + float offscreenX = NORMAL.getOverviewScaleAndTranslation(mActivity).translationX; + // Offset since scale pushes tasks outwards. + getTaskSize(sTempRect); + int taskWidth = sTempRect.width(); + offscreenX += taskWidth * (recentsScale - 1) / 2; + if (mRunningTaskTileHidden) { + // The first task is hidden, so offset by its width. + offscreenX -= (taskWidth + getPageSpacing()) * recentsScale; + } + if (isRtl()) { + offscreenX = -offscreenX; + } + return offscreenX; + } + @Override protected void onTaskLaunchAnimationUpdate(float progress, TaskView tv) { if (ENABLE_QUICKSTEP_LIVE_TILE.get()) { @@ -264,4 +308,66 @@ public class LauncherRecentsView extends RecentsView<Launcher> implements StateL } return super.shouldStealTouchFromSiblingsBelow(ev); } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + PluginManagerWrapper.INSTANCE.get(getContext()) + .addPluginListener(mRecentsExtraCardPluginListener, RecentsExtraCard.class); + } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + PluginManagerWrapper.INSTANCE.get(getContext()).removePluginListener( + mRecentsExtraCardPluginListener); + } + + @Override + protected int computeMinScrollX() { + if (canComputeScrollX() && !mIsRtl) { + return computeScrollX(); + } + return super.computeMinScrollX(); + } + + @Override + protected int computeMaxScrollX() { + if (canComputeScrollX() && mIsRtl) { + return computeScrollX(); + } + return super.computeMaxScrollX(); + } + + private boolean canComputeScrollX() { + return mRecentsExtraCardPlugin != null && getTaskViewCount() > 0 + && !mDisallowScrollToClearAll; + } + + private int computeScrollX() { + int scrollIndex = getTaskViewStartIndex() - 1; + while (scrollIndex >= 0 && getChildAt(scrollIndex) instanceof RecentsExtraViewContainer + && ((RecentsExtraViewContainer) getChildAt(scrollIndex)).isScrollable()) { + scrollIndex--; + } + return getScrollForPage(scrollIndex + 1); + } + + private void createRecentsExtraCard() { + mRecentsExtraViewContainer = new RecentsExtraViewContainer(getContext()); + FrameLayout.LayoutParams helpCardParams = + new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, + FrameLayout.LayoutParams.MATCH_PARENT); + mRecentsExtraViewContainer.setLayoutParams(helpCardParams); + mRecentsExtraViewContainer.setScrollable(true); + addView(mRecentsExtraViewContainer, 0); + } + + @Override + public void resetTaskVisuals() { + super.resetTaskVisuals(); + if (mRecentsExtraViewContainer != null) { + mRecentsExtraViewContainer.setAlpha(mContentAlpha); + } + } } diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsExtraViewContainer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsExtraViewContainer.java new file mode 100644 index 000000000..1ea6d4a24 --- /dev/null +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsExtraViewContainer.java @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.quickstep.views; + +import android.content.Context; +import android.util.AttributeSet; +import android.widget.FrameLayout; + +/** + * Empty view to house recents overview extra card + */ +public class RecentsExtraViewContainer extends FrameLayout implements RecentsView.PageCallbacks { + + private boolean mScrollable = false; + + public RecentsExtraViewContainer(Context context) { + super(context); + } + + public RecentsExtraViewContainer(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public RecentsExtraViewContainer(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + /** + * Determine whether the view should be scrolled to in the recents overview, similar to the + * taskviews. + * @return true if viewed should be scrolled to, false if not + */ + public boolean isScrollable() { + return mScrollable; + } + + public void setScrollable(boolean scrollable) { + this.mScrollable = scrollable; + } +} 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 ef54d3f89..ca3b92aa7 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 @@ -34,9 +34,9 @@ import static com.android.launcher3.config.FeatureFlags.QUICKSTEP_SPRINGS; import static com.android.launcher3.uioverrides.touchcontrollers.TaskViewTouchController.SUCCESS_TRANSITION_PROGRESS; import static com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch.TAP; import static com.android.launcher3.userevent.nano.LauncherLogProto.ControlType.CLEAR_ALL_BUTTON; +import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR; import static com.android.launcher3.util.SystemUiController.UI_STATE_OVERVIEW; import static com.android.quickstep.TaskUtils.checkCurrentOrManagedUserId; -import static com.android.quickstep.TouchInteractionService.BACKGROUND_EXECUTOR; import android.animation.Animator; import android.animation.AnimatorSet; @@ -186,7 +186,7 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl private final ViewPool<TaskView> mTaskViewPool; private boolean mDwbToastShown; - private boolean mDisallowScrollToClearAll; + protected boolean mDisallowScrollToClearAll; private boolean mOverlayEnabled; private boolean mFreezeViewVisibility; @@ -227,7 +227,7 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl return; } - BACKGROUND_EXECUTOR.execute(() -> { + UI_HELPER_EXECUTOR.execute(() -> { TaskView taskView = getTaskView(taskId); if (taskView == null) { return; @@ -271,7 +271,7 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl // Only valid until the launcher state changes to NORMAL protected int mRunningTaskId = -1; - private boolean mRunningTaskTileHidden; + protected boolean mRunningTaskTileHidden; private Task mTmpRunningTask; private boolean mRunningTaskIconScaledDown = false; @@ -288,7 +288,7 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl private LayoutTransition mLayoutTransition; @ViewDebug.ExportedProperty(category = "launcher") - private float mContentAlpha = 1; + protected float mContentAlpha = 1; @ViewDebug.ExportedProperty(category = "launcher") protected float mFullscreenProgress = 0; @@ -305,6 +305,9 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl private Layout mEmptyTextLayout; private LiveTileOverlay mLiveTileOverlay; + // Keeps track of the index where the first TaskView should be + private int mTaskViewStartIndex = 0; + private BaseActivity.MultiWindowModeChangedListener mMultiWindowModeChangedListener = (inMultiWindowMode) -> { if (!inMultiWindowMode && mOverviewStateEnabled) { @@ -328,7 +331,6 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl mClearAllButton = (ClearAllButton) LayoutInflater.from(context) .inflate(R.layout.overview_clear_all_button, this, false); mClearAllButton.setOnClickListener(this::dismissAllTasks); - mTaskViewPool = new ViewPool<>(context, this, R.layout.task, 20 /* max size */, 10 /* initial size */); @@ -429,7 +431,7 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl super.onViewRemoved(child); // Clear the task data for the removed child if it was visible - if (child != mClearAllButton) { + if (child instanceof TaskView) { TaskView taskView = (TaskView) child; mHasVisibleTaskData.delete(taskView.getTask().key.id); mTaskViewPool.recycle(taskView); @@ -443,7 +445,7 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl public TaskView getTaskView(int taskId) { for (int i = 0; i < getTaskViewCount(); i++) { - TaskView tv = (TaskView) getChildAt(i); + TaskView tv = getTaskViewAt(i); if (tv.getTask() != null && tv.getTask().key != null && tv.getTask().key.id == taskId) { return tv; } @@ -535,28 +537,26 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl } if (tasks == null || tasks.isEmpty()) { - removeAllViews(); + removeTasksViewsAndClearAllButton(); onTaskStackUpdated(); return; } - int oldChildCount = getChildCount(); - // Unload existing visible task data unloadVisibleTaskData(); - TaskView ignoreRestTaskView = + TaskView ignoreResetTaskView = mIgnoreResetTaskId == -1 ? null : getTaskView(mIgnoreResetTaskId); final int requiredTaskCount = tasks.size(); if (getTaskViewCount() != requiredTaskCount) { - if (oldChildCount > 0) { + if (indexOfChild(mClearAllButton) != -1) { removeView(mClearAllButton); } - for (int i = getChildCount(); i < requiredTaskCount; i++) { + for (int i = getTaskViewCount(); i < requiredTaskCount; i++) { addView(mTaskViewPool.getView()); } - while (getChildCount() > requiredTaskCount) { + while (getTaskViewCount() > requiredTaskCount) { removeView(getChildAt(getChildCount() - 1)); } if (requiredTaskCount > 0) { @@ -566,7 +566,7 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl // Rebind and reset all task views for (int i = requiredTaskCount - 1; i >= 0; i--) { - final int pageIndex = requiredTaskCount - i - 1; + final int pageIndex = requiredTaskCount - i - 1 + mTaskViewStartIndex; final Task task = tasks.get(i); final TaskView taskView = (TaskView) getChildAt(pageIndex); taskView.bind(task); @@ -577,10 +577,12 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl TaskView runningTaskView = getRunningTaskView(); if (runningTaskView != null) { setCurrentPage(indexOfChild(runningTaskView)); + } else if (getTaskViewCount() > 0) { + setCurrentPage(indexOfChild(getTaskViewAt(0))); } } - if (mIgnoreResetTaskId != -1 && getTaskView(mIgnoreResetTaskId) != ignoreRestTaskView) { + if (mIgnoreResetTaskId != -1 && getTaskView(mIgnoreResetTaskId) != ignoreResetTaskView) { // If the taskView mapping is changing, do not preserve the visuals. Since we are // mostly preserving the first task, and new taskViews are added to the end, it should // generally map to the same task. @@ -591,17 +593,28 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl updateEnabledOverlays(); } + private void removeTasksViewsAndClearAllButton() { + for (int i = getTaskViewCount() - 1; i >= 0; i--) { + removeView(getTaskViewAt(i)); + } + if (indexOfChild(mClearAllButton) != -1) { + removeView(mClearAllButton); + } + } + public int getTaskViewCount() { - // Account for the clear all button. - int childCount = getChildCount(); - return childCount == 0 ? 0 : childCount - 1; + int taskViewCount = getChildCount() - mTaskViewStartIndex; + if (indexOfChild(mClearAllButton) != -1) { + taskViewCount--; + } + return taskViewCount; } protected void onTaskStackUpdated() { } public void resetTaskVisuals() { for (int i = getTaskViewCount() - 1; i >= 0; i--) { - TaskView taskView = (TaskView) getChildAt(i); + TaskView taskView = getTaskViewAt(i); if (mIgnoreResetTaskId != taskView.getTask().key.id) { taskView.resetVisualProperties(); taskView.setStableAlpha(mContentAlpha); @@ -707,7 +720,7 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl } /** - * Iterates through all thet asks, and loads the associated task data for newly visible tasks, + * Iterates through all the tasks, and loads the associated task data for newly visible tasks, * and unloads the associated task data for tasks that are no longer visible. */ public void loadVisibleTaskData() { @@ -718,15 +731,16 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl } int centerPageIndex = getPageNearestToCenterOfScreen(); - int numChildren = getTaskViewCount(); + int numChildren = getChildCount(); int lower = Math.max(0, centerPageIndex - 2); int upper = Math.min(centerPageIndex + 2, numChildren - 1); // Update the task data for the in/visible children - for (int i = 0; i < numChildren; i++) { - TaskView taskView = (TaskView) getChildAt(i); + for (int i = 0; i < getTaskViewCount(); i++) { + TaskView taskView = getTaskViewAt(i); Task task = taskView.getTask(); - boolean visible = lower <= i && i <= upper; + int index = indexOfChild(taskView); + boolean visible = lower <= index && index <= upper; if (visible) { if (task == mTmpRunningTask) { // Skip loading if this is the task that we are animating into @@ -801,6 +815,10 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl return tv == null ? -1 : indexOfChild(tv); } + public int getTaskViewStartIndex() { + return mTaskViewStartIndex; + } + /** * Reloads the view if anything in recents changed. */ @@ -856,10 +874,10 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl */ public void showCurrentTask(int runningTaskId) { if (getTaskView(runningTaskId) == null) { - boolean wasEmpty = getChildCount() == 0; + boolean wasEmpty = getTaskViewCount() == 0; // Add an empty view for now until the task plan is loaded and applied final TaskView taskView = mTaskViewPool.getView(); - addView(taskView, 0); + addView(taskView, mTaskViewStartIndex); if (wasEmpty) { addView(mClearAllButton); } @@ -925,14 +943,13 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl if (runningTaskView == null) { // Launch the first task if (getTaskViewCount() > 0) { - getTaskViewAt(0).launchTask(true /* animate */); + getTaskViewAt(0).launchTask(true); } } else { - TaskView nextTaskView = getNextTaskView(); - if (nextTaskView != null) { - nextTaskView.launchTask(true /* animate */); + if (getNextTaskView() != null) { + getNextTaskView().launchTask(true); } else { - runningTaskView.launchTask(true /* animate */); + runningTaskView.launchTask(true); } } } @@ -1196,7 +1213,7 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl int count = getTaskViewCount(); for (int i = 0; i < count; i++) { - addDismissedTaskAnimations(getChildAt(i), anim, duration); + addDismissedTaskAnimations(getTaskViewAt(i), anim, duration); } mPendingAnimation = pendingAnimation; @@ -1204,7 +1221,7 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl if (onEndListener.isSuccess) { // Remove all the task views now ActivityManagerWrapper.getInstance().removeAllRecentTasks(); - removeAllViews(); + removeTasksViewsAndClearAllButton(); startHome(); } mPendingAnimation = null; @@ -1351,26 +1368,50 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl child.setAlpha(mContentAlpha); } - /** - * @return The most recent task that is older than the currently running task. If there is - * currently no running task or there is no task older than it, then return null. - */ @Nullable public TaskView getNextTaskView() { - TaskView runningTaskView = getRunningTaskView(); - if (runningTaskView == null) { - return null; - } - return getTaskViewAt(indexOfChild(runningTaskView) + 1); + return getTaskViewAtByAbsoluteIndex(getRunningTaskIndex() + 1); + } + + @Nullable + public TaskView getPreviousTaskView() { + return getTaskViewAtByAbsoluteIndex(getRunningTaskIndex() - 1); + } + + @Nullable + public TaskView getCurrentPageTaskView() { + return getTaskViewAtByAbsoluteIndex(getCurrentPage()); + } + + @Nullable + public TaskView getNextPageTaskView() { + return getTaskViewAtByAbsoluteIndex(getNextPage()); + } + + @Nullable + public TaskView getTaskViewNearestToCenterOfScreen() { + return getTaskViewAtByAbsoluteIndex(getPageNearestToCenterOfScreen()); } + /** + * Returns null instead of indexOutOfBoundsError when index is not in range + */ + @Nullable public TaskView getTaskViewAt(int index) { - View child = getChildAt(index); - return child == mClearAllButton ? null : (TaskView) child; + return getTaskViewAtByAbsoluteIndex(index + mTaskViewStartIndex); + } + + @Nullable + private TaskView getTaskViewAtByAbsoluteIndex(int index) { + if (index < getChildCount() && index >= 0) { + View child = getChildAt(index); + return child instanceof TaskView ? (TaskView) child : null; + } + return null; } public void updateEmptyMessage() { - boolean isEmpty = getChildCount() == 0; + boolean isEmpty = getTaskViewCount() == 0; boolean hasSizeChanged = mLastMeasureSize.x != getWidth() || mLastMeasureSize.y != getHeight(); if (isEmpty == mShowEmptyMessage && !hasSizeChanged) { @@ -1504,7 +1545,7 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl throw new IllegalStateException("Another pending animation is still running"); } - int count = getChildCount(); + int count = getTaskViewCount(); if (count == 0) { return new PendingAnimation(new AnimatorSet()); } @@ -1677,18 +1718,38 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl @Override protected int computeMinScrollX() { - if (mIsRtl && mDisallowScrollToClearAll) { - // We aren't showing the clear all button, so use the leftmost task as the min scroll. - return getScrollForPage(getTaskViewCount() - 1); + if (getTaskViewCount() > 0) { + if (mDisallowScrollToClearAll) { + // We aren't showing the clear all button, + // so use the leftmost task as the min scroll. + if (mIsRtl) { + return getScrollForPage(indexOfChild(getTaskViewAt(getTaskViewCount() - 1))); + } + return getScrollForPage(mTaskViewStartIndex); + } + if (mIsRtl) { + return getScrollForPage(indexOfChild(getTaskViewAt(getTaskViewCount() - 1)) + 1); + } + return getScrollForPage(mTaskViewStartIndex); } return super.computeMinScrollX(); } @Override protected int computeMaxScrollX() { - if (!mIsRtl && mDisallowScrollToClearAll) { - // We aren't showing the clear all button, so use the rightmost task as the max scroll. - return getScrollForPage(getTaskViewCount() - 1); + if (getTaskViewCount() > 0) { + if (mDisallowScrollToClearAll) { + // We aren't showing the clear all button, + // so use the rightmost task as the min scroll. + if (mIsRtl) { + return getScrollForPage(mTaskViewStartIndex); + } + return getScrollForPage(indexOfChild(getTaskViewAt(getTaskViewCount() - 1))); + } + if (mIsRtl) { + return getScrollForPage(mTaskViewStartIndex); + } + return getScrollForPage(indexOfChild(getTaskViewAt(getTaskViewCount() - 1)) + 1); } return super.computeMaxScrollX(); } @@ -1740,7 +1801,7 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl int overlayEnabledPage = mOverlayEnabled ? getNextPage() : -1; int taskCount = getTaskViewCount(); for (int i = 0; i < taskCount; i++) { - ((TaskView) getChildAt(i)).setOverlayEnabled(i == overlayEnabledPage); + getTaskViewAt(i).setOverlayEnabled(i == overlayEnabledPage); } } @@ -1760,4 +1821,25 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl final WindowInsets insets = getRootWindowInsets(); return Math.max(insets.getSystemGestureInsets().right, insets.getSystemWindowInsetRight()); } + + @Override + public void addView(View child, int index) { + super.addView(child, index); + if (isExtraCardView(child, index)) { + mTaskViewStartIndex++; + } + } + + @Override + public void removeView(View view) { + if (isExtraCardView(view, indexOfChild(view))) { + mTaskViewStartIndex--; + } + super.removeView(view); + } + + private boolean isExtraCardView(View view, int index) { + return !(view instanceof TaskView) && !(view instanceof ClearAllButton) + && index <= mTaskViewStartIndex; + } } diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskMenuView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskMenuView.java index c1f6b82ec..07d079664 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskMenuView.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskMenuView.java @@ -26,6 +26,7 @@ import android.content.Context; import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.util.AttributeSet; +import android.util.Log; import android.view.Gravity; import android.view.MotionEvent; import android.view.View; @@ -40,6 +41,7 @@ import com.android.launcher3.R; import com.android.launcher3.anim.AnimationSuccessListener; import com.android.launcher3.anim.Interpolators; import com.android.launcher3.anim.RoundedRectRevealOutlineProvider; +import com.android.launcher3.testing.TestProtocol; import com.android.launcher3.util.Themes; import com.android.launcher3.views.BaseDragLayer; import com.android.quickstep.TaskOverlayFactory; diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskThumbnailView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskThumbnailView.java index 7f1e8980b..044292aa6 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskThumbnailView.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskThumbnailView.java @@ -39,24 +39,28 @@ import android.util.AttributeSet; import android.util.FloatProperty; import android.util.Property; import android.view.View; +import android.view.ViewGroup; import com.android.launcher3.BaseActivity; import com.android.launcher3.DeviceProfile; import com.android.launcher3.R; import com.android.launcher3.Utilities; import com.android.launcher3.config.FeatureFlags; +import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper; import com.android.launcher3.util.SystemUiController; import com.android.launcher3.util.Themes; import com.android.quickstep.TaskOverlayFactory; import com.android.quickstep.TaskOverlayFactory.TaskOverlay; import com.android.quickstep.util.TaskCornerRadius; +import com.android.systemui.plugins.OverviewScreenshotActions; +import com.android.systemui.plugins.PluginListener; import com.android.systemui.shared.recents.model.Task; import com.android.systemui.shared.recents.model.ThumbnailData; /** * A task in the Recents view. */ -public class TaskThumbnailView extends View { +public class TaskThumbnailView extends View implements PluginListener<OverviewScreenshotActions> { private final static ColorMatrix COLOR_MATRIX = new ColorMatrix(); private final static ColorMatrix SATURATION_COLOR_MATRIX = new ColorMatrix(); @@ -100,6 +104,7 @@ public class TaskThumbnailView extends View { private boolean mOverlayEnabled; private boolean mRotated; + private OverviewScreenshotActions mOverviewScreenshotActionsPlugin; public TaskThumbnailView(Context context) { this(context, null); @@ -147,6 +152,11 @@ public class TaskThumbnailView extends View { mPaint.setShader(null); mOverlay.reset(); } + + if (mOverviewScreenshotActionsPlugin != null) { + mOverviewScreenshotActionsPlugin + .setupActions((ViewGroup) getTaskView(), getThumbnail(), mActivity); + } updateThumbnailPaintFilter(); } @@ -211,6 +221,33 @@ public class TaskThumbnailView extends View { canvas.restore(); } + @Override + public void onPluginConnected(OverviewScreenshotActions overviewScreenshotActions, + Context context) { + mOverviewScreenshotActionsPlugin = overviewScreenshotActions; + mOverviewScreenshotActionsPlugin.setupActions(getTaskView(), getThumbnail(), mActivity); + } + + @Override + public void onPluginDisconnected(OverviewScreenshotActions plugin) { + if (mOverviewScreenshotActionsPlugin != null) { + mOverviewScreenshotActionsPlugin = null; + } + } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + PluginManagerWrapper.INSTANCE.get(getContext()) + .addPluginListener(this, OverviewScreenshotActions.class); + } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + PluginManagerWrapper.INSTANCE.get(getContext()).removePluginListener(this); + } + public RectF getInsetsToDrawInFullscreen(boolean isMultiWindowMode) { // Don't show insets in multi window mode. return isMultiWindowMode ? EMPTY_RECT_F : mClippedInsets; 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 bfb961320..51802dffd 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 @@ -53,6 +53,7 @@ import com.android.launcher3.Utilities; import com.android.launcher3.anim.AnimatorPlaybackController; import com.android.launcher3.anim.Interpolators; import com.android.launcher3.logging.UserEventDispatcher; +import com.android.launcher3.testing.TestProtocol; import com.android.launcher3.userevent.nano.LauncherLogProto; import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction; import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch; diff --git a/quickstep/res/values-en-rCA/strings.xml b/quickstep/res/values-en-rCA/strings.xml new file mode 100644 index 000000000..2d1418e5f --- /dev/null +++ b/quickstep/res/values-en-rCA/strings.xml @@ -0,0 +1,37 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/* +* Copyright (C) 2017 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. +*/ + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="recent_task_option_split_screen" msgid="5353188922202653570">"Split screen"</string> + <string name="recent_task_option_pin" msgid="7929860679018978258">"Pin"</string> + <string name="recent_task_option_freeform" msgid="48863056265284071">"Freeform"</string> + <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Overview"</string> + <string name="recents_empty_message" msgid="7040467240571714191">"No recent items"</string> + <string name="accessibility_close_task" msgid="5354563209433803643">"Close"</string> + <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"App usage settings"</string> + <string name="recents_clear_all" msgid="5328176793634888831">"Clear all"</string> + <string name="accessibility_recent_apps" msgid="4058661986695117371">"Recent apps"</string> + <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string> + <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"< 1 minute"</string> + <string name="time_left_for_app" msgid="3111996412933644358">"<xliff:g id="TIME">%1$s</xliff:g> left today"</string> + <string name="title_app_suggestions" msgid="4185902664111965088">"App suggestions"</string> + <string name="all_apps_label" msgid="8542784161730910663">"All apps"</string> + <string name="all_apps_prediction_tip" msgid="2672336544844936186">"Your predicted apps"</string> +</resources> diff --git a/quickstep/res/values-en-rXC/strings.xml b/quickstep/res/values-en-rXC/strings.xml new file mode 100644 index 000000000..bb186db26 --- /dev/null +++ b/quickstep/res/values-en-rXC/strings.xml @@ -0,0 +1,37 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/* +* Copyright (C) 2017 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. +*/ + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="recent_task_option_split_screen" msgid="5353188922202653570">"Split screen"</string> + <string name="recent_task_option_pin" msgid="7929860679018978258">"Pin"</string> + <string name="recent_task_option_freeform" msgid="48863056265284071">"Freeform"</string> + <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Overview"</string> + <string name="recents_empty_message" msgid="7040467240571714191">"No recent items"</string> + <string name="accessibility_close_task" msgid="5354563209433803643">"Close"</string> + <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"App usage settings"</string> + <string name="recents_clear_all" msgid="5328176793634888831">"Clear all"</string> + <string name="accessibility_recent_apps" msgid="4058661986695117371">"Recent apps"</string> + <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string> + <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"< 1 minute"</string> + <string name="time_left_for_app" msgid="3111996412933644358">"<xliff:g id="TIME">%1$s</xliff:g> left today"</string> + <string name="title_app_suggestions" msgid="4185902664111965088">"App suggestions"</string> + <string name="all_apps_label" msgid="8542784161730910663">"All apps"</string> + <string name="all_apps_prediction_tip" msgid="2672336544844936186">"Your predicted apps"</string> +</resources> diff --git a/quickstep/res/values/config.xml b/quickstep/res/values/config.xml index 5c4d6d869..292eaaa99 100644 --- a/quickstep/res/values/config.xml +++ b/quickstep/res/values/config.xml @@ -21,8 +21,6 @@ <!-- Activity which blocks home gesture --> <string name="gesture_blocking_activity" translatable="false"></string> - <string name="user_event_dispatcher_class" translatable="false">com.android.quickstep.logging.UserEventDispatcherExtension</string> - <string name="stats_log_manager_class" translatable="false">com.android.quickstep.logging.StatsLogCompatManager</string> <string name="test_information_handler_class" translatable="false">com.android.quickstep.QuickstepTestInformationHandler</string> diff --git a/quickstep/src/com/android/launcher3/uioverrides/plugins/PluginInitializerImpl.java b/quickstep/src/com/android/launcher3/uioverrides/plugins/PluginInitializerImpl.java index 910fa0df6..7beb9db84 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/plugins/PluginInitializerImpl.java +++ b/quickstep/src/com/android/launcher3/uioverrides/plugins/PluginInitializerImpl.java @@ -14,16 +14,17 @@ package com.android.launcher3.uioverrides.plugins; +import static com.android.launcher3.util.Executors.MODEL_EXECUTOR; + import android.content.Context; import android.os.Looper; -import com.android.launcher3.LauncherModel; import com.android.systemui.shared.plugins.PluginInitializer; public class PluginInitializerImpl implements PluginInitializer { @Override public Looper getBgLooper() { - return LauncherModel.getWorkerLooper(); + return MODEL_EXECUTOR.getLooper(); } @Override diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/LandscapeEdgeSwipeController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/LandscapeEdgeSwipeController.java index bb72315d3..39b0f8d21 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/LandscapeEdgeSwipeController.java +++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/LandscapeEdgeSwipeController.java @@ -11,7 +11,7 @@ import com.android.launcher3.Launcher; import com.android.launcher3.LauncherState; import com.android.launcher3.LauncherStateManager.AnimationComponents; import com.android.launcher3.touch.AbstractStateChangeTouchController; -import com.android.launcher3.touch.SwipeDetector; +import com.android.launcher3.touch.SingleAxisSwipeDetector; import com.android.launcher3.userevent.nano.LauncherLogProto; import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction; import com.android.quickstep.RecentsModel; @@ -24,7 +24,7 @@ public class LandscapeEdgeSwipeController extends AbstractStateChangeTouchContro private static final String TAG = "LandscapeEdgeSwipeCtrl"; public LandscapeEdgeSwipeController(Launcher l) { - super(l, SwipeDetector.HORIZONTAL); + super(l, SingleAxisSwipeDetector.HORIZONTAL); } @Override diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java index b81edfa4c..db6a40f2e 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java +++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java @@ -43,7 +43,7 @@ import com.android.launcher3.anim.AnimatorPlaybackController; import com.android.launcher3.anim.AnimatorSetBuilder; import com.android.launcher3.anim.Interpolators; import com.android.launcher3.touch.AbstractStateChangeTouchController; -import com.android.launcher3.touch.SwipeDetector; +import com.android.launcher3.touch.SingleAxisSwipeDetector; import com.android.launcher3.uioverrides.states.OverviewState; import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch; import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType; @@ -79,7 +79,7 @@ public class PortraitStatesTouchController extends AbstractStateChangeTouchContr private boolean mFinishFastOnSecondTouch; public PortraitStatesTouchController(Launcher l, boolean allowDragToOverview) { - super(l, SwipeDetector.VERTICAL); + super(l, SingleAxisSwipeDetector.VERTICAL); mOverviewPortraitStateTouchHelper = new PortraitOverviewStateTouchHelper(l); mAllowDragToOverview = allowDragToOverview; } @@ -177,6 +177,20 @@ public class PortraitStatesTouchController extends AbstractStateChangeTouchContr return builder; } + private AnimatorSetBuilder getNormalToAllAppsAnimation() { + AnimatorSetBuilder builder = new AnimatorSetBuilder(); + builder.setInterpolator(ANIM_ALL_APPS_FADE, Interpolators.clampToProgress(ACCEL, + 0, ALL_APPS_CONTENT_FADE_THRESHOLD)); + return builder; + } + + private AnimatorSetBuilder getAllAppsToNormalAnimation() { + AnimatorSetBuilder builder = new AnimatorSetBuilder(); + builder.setInterpolator(ANIM_ALL_APPS_FADE, Interpolators.clampToProgress(DEACCEL, + 1 - ALL_APPS_CONTENT_FADE_THRESHOLD, 1)); + return builder; + } + @Override protected AnimatorSetBuilder getAnimatorSetBuilderForStates(LauncherState fromState, LauncherState toState) { @@ -187,6 +201,10 @@ public class PortraitStatesTouchController extends AbstractStateChangeTouchContr builder = getOverviewToAllAppsAnimation(); } else if (fromState == ALL_APPS && toState == OVERVIEW) { builder = getAllAppsToOverviewAnimation(); + } else if (fromState == NORMAL && toState == ALL_APPS) { + builder = getNormalToAllAppsAnimation(); + } else if (fromState == ALL_APPS && toState == NORMAL) { + builder = getAllAppsToNormalAnimation(); } return builder; } diff --git a/quickstep/src/com/android/quickstep/BaseRecentsActivity.java b/quickstep/src/com/android/quickstep/BaseRecentsActivity.java index c84013257..1ac7ed4a3 100644 --- a/quickstep/src/com/android/quickstep/BaseRecentsActivity.java +++ b/quickstep/src/com/android/quickstep/BaseRecentsActivity.java @@ -157,6 +157,6 @@ public abstract class BaseRecentsActivity extends BaseDraggingActivity { public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) { super.dump(prefix, fd, writer, args); writer.println(prefix + "Misc:"); - dumpMisc(writer); + dumpMisc(prefix + "\t", writer); } } diff --git a/quickstep/src/com/android/quickstep/OverviewInteractionState.java b/quickstep/src/com/android/quickstep/OverviewInteractionState.java index 78b48d77a..858c3b6b3 100644 --- a/quickstep/src/com/android/quickstep/OverviewInteractionState.java +++ b/quickstep/src/com/android/quickstep/OverviewInteractionState.java @@ -15,20 +15,21 @@ */ package com.android.quickstep; +import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR; + import android.content.Context; import android.os.Handler; import android.os.Message; import android.os.RemoteException; import android.util.Log; +import androidx.annotation.WorkerThread; + import com.android.launcher3.Utilities; import com.android.launcher3.allapps.DiscoveryBounce; import com.android.launcher3.util.MainThreadInitializedObject; -import com.android.launcher3.util.UiThreadHelper; import com.android.systemui.shared.recents.ISystemUiProxy; -import androidx.annotation.WorkerThread; - /** * Sets alpha for the back button */ @@ -62,7 +63,7 @@ public class OverviewInteractionState { // because of its high send frequency and data may be very different than the previous value // For example, send back alpha on uihandler to avoid flickering when setting its visibility mUiHandler = new Handler(this::handleUiMessage); - mBgHandler = new Handler(UiThreadHelper.getBackgroundLooper(), this::handleBgMessage); + mBgHandler = new Handler(UI_HELPER_EXECUTOR.getLooper(), this::handleBgMessage); onNavigationModeChanged(SysUINavigationMode.INSTANCE.get(context) .addModeChangeListener(this::onNavigationModeChanged)); diff --git a/quickstep/src/com/android/quickstep/RecentTasksList.java b/quickstep/src/com/android/quickstep/RecentTasksList.java index e41dba94c..10f9febfb 100644 --- a/quickstep/src/com/android/quickstep/RecentTasksList.java +++ b/quickstep/src/com/android/quickstep/RecentTasksList.java @@ -16,19 +16,22 @@ package com.android.quickstep; -import static com.android.quickstep.TouchInteractionService.BACKGROUND_EXECUTOR; +import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR; import android.annotation.TargetApi; import android.app.ActivityManager; -import android.content.Context; import android.os.Build; import android.os.Process; import android.util.SparseBooleanArray; -import com.android.launcher3.MainThreadExecutor; + +import androidx.annotation.VisibleForTesting; + +import com.android.launcher3.util.LooperExecutor; import com.android.systemui.shared.recents.model.Task; import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.shared.system.KeyguardManagerCompat; import com.android.systemui.shared.system.TaskStackChangeListener; + import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -41,7 +44,8 @@ import java.util.function.Consumer; public class RecentTasksList extends TaskStackChangeListener { private final KeyguardManagerCompat mKeyguardManager; - private final MainThreadExecutor mMainThreadExecutor; + private final LooperExecutor mMainThreadExecutor; + private final ActivityManagerWrapper mActivityManagerWrapper; // The list change id, increments as the task list changes in the system private int mChangeId; @@ -52,11 +56,13 @@ public class RecentTasksList extends TaskStackChangeListener { ArrayList<Task> mTasks = new ArrayList<>(); - public RecentTasksList(Context context) { - mMainThreadExecutor = new MainThreadExecutor(); - mKeyguardManager = new KeyguardManagerCompat(context); + public RecentTasksList(LooperExecutor mainThreadExecutor, + KeyguardManagerCompat keyguardManager, ActivityManagerWrapper activityManagerWrapper) { + mMainThreadExecutor = mainThreadExecutor; + mKeyguardManager = keyguardManager; mChangeId = 1; - ActivityManagerWrapper.getInstance().registerTaskStackListener(this); + mActivityManagerWrapper = activityManagerWrapper; + mActivityManagerWrapper.registerTaskStackListener(this); } /** @@ -64,7 +70,7 @@ public class RecentTasksList extends TaskStackChangeListener { */ public void getTaskKeys(int numTasks, Consumer<ArrayList<Task>> callback) { // Kick off task loading in the background - BACKGROUND_EXECUTOR.execute(() -> { + UI_HELPER_EXECUTOR.execute(() -> { ArrayList<Task> tasks = loadTasksInBackground(numTasks, true /* loadKeysOnly */); mMainThreadExecutor.execute(() -> callback.accept(tasks)); }); @@ -86,12 +92,12 @@ public class RecentTasksList extends TaskStackChangeListener { if (mLastLoadedId == mChangeId && (!mLastLoadHadKeysOnly || loadKeysOnly)) { // The list is up to date, send the callback on the next frame, // so that requestID can be returned first. - mMainThreadExecutor.getHandler().post(resultCallback); + mMainThreadExecutor.post(resultCallback); return requestLoadId; } // Kick off task loading in the background - BACKGROUND_EXECUTOR.execute(() -> { + UI_HELPER_EXECUTOR.execute(() -> { ArrayList<Task> tasks = loadTasksInBackground(Integer.MAX_VALUE, loadKeysOnly); mMainThreadExecutor.execute(() -> { @@ -136,12 +142,13 @@ public class RecentTasksList extends TaskStackChangeListener { /** * Loads and creates a list of all the recent tasks. */ - private ArrayList<Task> loadTasksInBackground(int numTasks, + @VisibleForTesting + ArrayList<Task> loadTasksInBackground(int numTasks, boolean loadKeysOnly) { int currentUserId = Process.myUserHandle().getIdentifier(); ArrayList<Task> allTasks = new ArrayList<>(); List<ActivityManager.RecentTaskInfo> rawTasks = - ActivityManagerWrapper.getInstance().getRecentTasks(numTasks, currentUserId); + mActivityManagerWrapper.getRecentTasks(numTasks, currentUserId); // The raw tasks are given in most-recent to least-recent order, we need to reverse it Collections.reverse(rawTasks); diff --git a/quickstep/src/com/android/quickstep/RecentsActivityTracker.java b/quickstep/src/com/android/quickstep/RecentsActivityTracker.java index f9d2f11cb..4d1d9ef8a 100644 --- a/quickstep/src/com/android/quickstep/RecentsActivityTracker.java +++ b/quickstep/src/com/android/quickstep/RecentsActivityTracker.java @@ -15,6 +15,8 @@ */ package com.android.quickstep; +import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; + import android.annotation.TargetApi; import android.content.Context; import android.content.Intent; @@ -22,7 +24,6 @@ import android.os.Build; import android.os.Bundle; import android.os.Handler; -import com.android.launcher3.MainThreadExecutor; import com.android.quickstep.ActivityControlHelper.ActivityInitListener; import com.android.quickstep.util.RemoteAnimationProvider; @@ -92,14 +93,10 @@ public class RecentsActivityTracker<T extends BaseRecentsActivity> implements Ac private static class Scheduler implements Runnable { private WeakReference<RecentsActivityTracker> mPendingTracker = new WeakReference<>(null); - private MainThreadExecutor mMainThreadExecutor; public synchronized void schedule(RecentsActivityTracker tracker) { mPendingTracker = new WeakReference<>(tracker); - if (mMainThreadExecutor == null) { - mMainThreadExecutor = new MainThreadExecutor(); - } - mMainThreadExecutor.execute(this); + MAIN_EXECUTOR.execute(this); } @Override diff --git a/quickstep/src/com/android/quickstep/RecentsModel.java b/quickstep/src/com/android/quickstep/RecentsModel.java index dfab43459..2e59ed5e4 100644 --- a/quickstep/src/com/android/quickstep/RecentsModel.java +++ b/quickstep/src/com/android/quickstep/RecentsModel.java @@ -15,6 +15,10 @@ */ package com.android.quickstep; +import static android.os.Process.THREAD_PRIORITY_BACKGROUND; + +import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; +import static com.android.launcher3.util.Executors.createAndStartNewLooper; import static com.android.quickstep.TaskUtils.checkCurrentOrManagedUserId; import android.annotation.TargetApi; @@ -22,16 +26,20 @@ import android.app.ActivityManager; import android.content.ComponentCallbacks2; import android.content.Context; import android.os.Build; -import android.os.HandlerThread; +import android.os.Looper; import android.os.Process; import android.os.RemoteException; +import android.os.UserHandle; import android.util.Log; +import com.android.launcher3.compat.LauncherAppsCompat; +import com.android.launcher3.compat.LauncherAppsCompat.OnAppsChangedCallbackCompat; import com.android.launcher3.util.MainThreadInitializedObject; import com.android.systemui.shared.recents.ISystemUiProxy; import com.android.systemui.shared.recents.model.Task; import com.android.systemui.shared.recents.model.ThumbnailData; import com.android.systemui.shared.system.ActivityManagerWrapper; +import com.android.systemui.shared.system.KeyguardManagerCompat; import com.android.systemui.shared.system.TaskStackChangeListener; import java.util.ArrayList; @@ -61,13 +69,14 @@ public class RecentsModel extends TaskStackChangeListener { private RecentsModel(Context context) { mContext = context; - HandlerThread loaderThread = new HandlerThread("TaskThumbnailIconCache", - Process.THREAD_PRIORITY_BACKGROUND); - loaderThread.start(); - mTaskList = new RecentTasksList(context); - mIconCache = new TaskIconCache(context, loaderThread.getLooper()); - mThumbnailCache = new TaskThumbnailCache(context, loaderThread.getLooper()); + Looper looper = + createAndStartNewLooper("TaskThumbnailIconCache", THREAD_PRIORITY_BACKGROUND); + mTaskList = new RecentTasksList(MAIN_EXECUTOR, + new KeyguardManagerCompat(context), ActivityManagerWrapper.getInstance()); + mIconCache = new TaskIconCache(context, looper); + mThumbnailCache = new TaskThumbnailCache(context, looper); ActivityManagerWrapper.getInstance().registerTaskStackListener(this); + setupPackageListener(); } public TaskIconCache getIconCache() { @@ -166,6 +175,7 @@ public class RecentsModel extends TaskStackChangeListener { public void onTaskRemoved(int taskId) { Task.TaskKey dummyKey = new Task.TaskKey(taskId, 0, null, null, 0, 0); mThumbnailCache.remove(dummyKey); + mIconCache.onTaskRemoved(dummyKey); } public void setSystemUiProxy(ISystemUiProxy systemUiProxy) { @@ -200,6 +210,21 @@ public class RecentsModel extends TaskStackChangeListener { } } + private void setupPackageListener() { + LauncherAppsCompat.getInstance(mContext) + .addOnAppsChangedCallback(new OnAppsChangedCallbackCompat() { + @Override + public void onPackageRemoved(String packageName, UserHandle user) { + mIconCache.invalidatePackage(packageName); + } + + @Override + public void onPackageChanged(String packageName, UserHandle user) { + mIconCache.invalidatePackage(packageName); + } + }); + } + public void addThumbnailChangeListener(TaskThumbnailChangeListener listener) { mThumbnailChangeListeners.add(listener); } diff --git a/quickstep/src/com/android/quickstep/TaskIconCache.java b/quickstep/src/com/android/quickstep/TaskIconCache.java index 07af9b385..289a12970 100644 --- a/quickstep/src/com/android/quickstep/TaskIconCache.java +++ b/quickstep/src/com/android/quickstep/TaskIconCache.java @@ -16,6 +16,7 @@ package com.android.quickstep; import static com.android.launcher3.uioverrides.RecentsUiFactory.GO_LOW_RAM_RECENTS_ENABLED; +import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; import android.content.ComponentName; import android.content.Context; @@ -27,26 +28,25 @@ import android.os.Looper; import android.util.LruCache; import android.view.accessibility.AccessibilityManager; -import com.android.launcher3.MainThreadExecutor; import com.android.launcher3.R; import com.android.launcher3.Utilities; import com.android.launcher3.icons.cache.HandlerRunnable; -import com.android.launcher3.uioverrides.RecentsUiFactory; import com.android.launcher3.util.Preconditions; import com.android.systemui.shared.recents.model.Task; +import com.android.systemui.shared.recents.model.Task.TaskKey; import com.android.systemui.shared.recents.model.TaskKeyLruCache; import com.android.systemui.shared.system.ActivityManagerWrapper; +import java.util.Map; import java.util.function.Consumer; /** * Manages the caching of task icons and related data. - * TODO: This class should later be merged into IconCache. + * TODO(b/138944598): This class should later be merged into IconCache. */ public class TaskIconCache { private final Handler mBackgroundHandler; - private final MainThreadExecutor mMainThreadExecutor; private final AccessibilityManager mAccessibilityManager; private final NormalizedIconLoader mIconLoader; @@ -67,7 +67,6 @@ public class TaskIconCache { public TaskIconCache(Context context, Looper backgroundLooper) { mBackgroundHandler = new Handler(backgroundLooper); - mMainThreadExecutor = new MainThreadExecutor(); mAccessibilityManager = context.getSystemService(AccessibilityManager.class); Resources res = context.getResources(); @@ -103,7 +102,7 @@ public class TaskIconCache { // We don't call back to the provided callback in this case return; } - mMainThreadExecutor.execute(() -> { + MAIN_EXECUTOR.execute(() -> { task.icon = icon; task.titleDescription = contentDescription; callback.accept(task); @@ -149,6 +148,21 @@ public class TaskIconCache { return label; } + + void onTaskRemoved(TaskKey taskKey) { + mIconCache.remove(taskKey); + } + + void invalidatePackage(String packageName) { + // TODO(b/138944598): Merge this class into IconCache so we can do this at the base level + Map<ComponentName, ActivityInfo> activityInfoCache = mActivityInfoCache.snapshot(); + for (ComponentName cn : activityInfoCache.keySet()) { + if (cn.getPackageName().equals(packageName)) { + mActivityInfoCache.remove(cn); + } + } + } + public static abstract class IconLoadRequest extends HandlerRunnable { IconLoadRequest(Handler handler) { super(handler, null); diff --git a/quickstep/src/com/android/quickstep/TaskThumbnailCache.java b/quickstep/src/com/android/quickstep/TaskThumbnailCache.java index 57c5a2783..3b50c2623 100644 --- a/quickstep/src/com/android/quickstep/TaskThumbnailCache.java +++ b/quickstep/src/com/android/quickstep/TaskThumbnailCache.java @@ -15,12 +15,14 @@ */ package com.android.quickstep; +import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; + import android.app.ActivityManager; import android.content.Context; import android.content.res.Resources; import android.os.Handler; import android.os.Looper; -import com.android.launcher3.MainThreadExecutor; + import com.android.launcher3.R; import com.android.launcher3.Utilities; import com.android.launcher3.icons.cache.HandlerRunnable; @@ -30,13 +32,13 @@ import com.android.systemui.shared.recents.model.Task.TaskKey; import com.android.systemui.shared.recents.model.TaskKeyLruCache; import com.android.systemui.shared.recents.model.ThumbnailData; import com.android.systemui.shared.system.ActivityManagerWrapper; + import java.util.ArrayList; import java.util.function.Consumer; public class TaskThumbnailCache { private final Handler mBackgroundHandler; - private final MainThreadExecutor mMainThreadExecutor; private final int mCacheSize; private final ThumbnailCache mCache; @@ -94,7 +96,6 @@ public class TaskThumbnailCache { public TaskThumbnailCache(Context context, Looper backgroundLooper) { mBackgroundHandler = new Handler(backgroundLooper); - mMainThreadExecutor = new MainThreadExecutor(); mHighResLoadingState = new HighResLoadingState(context); Resources res = context.getResources(); @@ -168,7 +169,7 @@ public class TaskThumbnailCache { // We don't call back to the provided callback in this case return; } - mMainThreadExecutor.execute(() -> { + MAIN_EXECUTOR.execute(() -> { mCache.put(key, thumbnail); callback.accept(thumbnail); onEnd(); diff --git a/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java b/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java index bf3cd8afe..8e5ed1a3e 100644 --- a/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java +++ b/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java @@ -29,12 +29,16 @@ import android.content.Intent; import android.stats.launcher.nano.Launcher; import android.stats.launcher.nano.LauncherExtension; import android.stats.launcher.nano.LauncherTarget; +import android.util.Log; import android.view.View; import com.android.launcher3.ItemInfo; import com.android.launcher3.logging.StatsLogManager; import com.android.launcher3.logging.StatsLogUtils; import com.android.launcher3.userevent.nano.LauncherLogProto.Target; +import com.android.launcher3.userevent.nano.LauncherLogProto.ItemType; +import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType; +import com.android.launcher3.userevent.nano.LauncherLogProto.ControlType; import com.android.launcher3.util.ComponentKey; import com.android.systemui.shared.system.StatsLogCompat; import com.google.protobuf.nano.MessageNano; @@ -50,6 +54,8 @@ import com.google.protobuf.nano.MessageNano; public class StatsLogCompatManager extends StatsLogManager { private static final int SUPPORTED_TARGET_DEPTH = 2; + private static final String TAG = "StatsLogCompatManager"; + private static final boolean DEBUG = false; public StatsLogCompatManager(Context context) { } @@ -59,6 +65,9 @@ public class StatsLogCompatManager extends StatsLogManager { ext.srcTarget = new LauncherTarget[SUPPORTED_TARGET_DEPTH]; int srcState = mStateProvider.getCurrentState(); fillInLauncherExtension(v, ext); + if (ext.srcTarget[0] != null) { + ext.srcTarget[0].item = LauncherTarget.APP_ICON; + } StatsLogCompat.write(LAUNCH_APP, srcState, BACKGROUND /* dstState */, MessageNano.toByteArray(ext), true); } @@ -95,28 +104,132 @@ public class StatsLogCompatManager extends StatsLogManager { } public static boolean fillInLauncherExtension(View v, LauncherExtension extension) { + if (DEBUG) { + Log.d(TAG, "fillInLauncherExtension"); + } + StatsLogUtils.LogContainerProvider provider = StatsLogUtils.getLaunchProviderRecursive(v); if (v == null || !(v.getTag() instanceof ItemInfo) || provider == null) { + if (DEBUG) { + Log.d(TAG, "View or provider is null, or view doesn't have an ItemInfo tag."); + } + return false; } ItemInfo itemInfo = (ItemInfo) v.getTag(); Target child = new Target(); Target parent = new Target(); provider.fillInLogContainerData(v, itemInfo, child, parent); + extension.srcTarget[0] = new LauncherTarget(); + extension.srcTarget[1] = new LauncherTarget(); copy(child, extension.srcTarget[0]); copy(parent, extension.srcTarget[1]); return true; } public static boolean fillInLauncherExtensionWithPageId(LauncherExtension ext, int pageId) { + if (DEBUG) { + Log.d(TAG, "fillInLauncherExtensionWithPageId, pageId = " + pageId); + } + Target target = new Target(); target.pageIndex = pageId; + ext.srcTarget[0] = new LauncherTarget(); copy(target, ext.srcTarget[0]); return true; } private static void copy(Target src, LauncherTarget dst) { - // fill in + if (DEBUG) { + Log.d(TAG, "copy target information from clearcut Target to LauncherTarget."); + } + + // Fill in type + switch (src.type) { + case Target.Type.ITEM: + dst.type = LauncherTarget.ITEM_TYPE; + break; + case Target.Type.CONTROL: + dst.type = LauncherTarget.CONTROL_TYPE; + break; + case Target.Type.CONTAINER: + dst.type = LauncherTarget.CONTAINER_TYPE; + break; + default: + dst.type = LauncherTarget.NONE; + break; + } + + // Fill in item + switch (src.itemType) { + case ItemType.APP_ICON: + dst.item = LauncherTarget.APP_ICON; + break; + case ItemType.SHORTCUT: + dst.item = LauncherTarget.SHORTCUT; + break; + case ItemType.WIDGET: + dst.item = LauncherTarget.WIDGET; + break; + case ItemType.FOLDER_ICON: + dst.item = LauncherTarget.FOLDER_ICON; + break; + case ItemType.DEEPSHORTCUT: + dst.item = LauncherTarget.DEEPSHORTCUT; + break; + case ItemType.SEARCHBOX: + dst.item = LauncherTarget.SEARCHBOX; + break; + case ItemType.EDITTEXT: + dst.item = LauncherTarget.EDITTEXT; + break; + case ItemType.NOTIFICATION: + dst.item = LauncherTarget.NOTIFICATION; + break; + case ItemType.TASK: + dst.item = LauncherTarget.TASK; + break; + default: + dst.item = LauncherTarget.DEFAULT_ITEM; + break; + } + + // Fill in container + switch (src.containerType) { + case ContainerType.HOTSEAT: + dst.container = LauncherTarget.HOTSEAT; + break; + case ContainerType.FOLDER: + dst.container = LauncherTarget.FOLDER; + break; + case ContainerType.PREDICTION: + dst.container = LauncherTarget.PREDICTION; + break; + case ContainerType.SEARCHRESULT: + dst.container = LauncherTarget.SEARCHRESULT; + break; + default: + dst.container = LauncherTarget.DEFAULT_CONTAINER; + break; + } + + // Fill in control + switch (src.controlType) { + case ControlType.UNINSTALL_TARGET: + dst.control = LauncherTarget.UNINSTALL; + break; + case ControlType.REMOVE_TARGET: + dst.control = LauncherTarget.REMOVE; + break; + default: + dst.control = LauncherTarget.DEFAULT_CONTROL; + break; + } + + // Fill in other fields + dst.pageId = src.pageIndex; + dst.gridX = src.gridX; + dst.gridY = src.gridY; } @Override diff --git a/quickstep/src/com/android/quickstep/logging/UserEventDispatcherExtension.java b/quickstep/src/com/android/quickstep/logging/UserEventDispatcherExtension.java index 4a11601bb..9ca7f234e 100644 --- a/quickstep/src/com/android/quickstep/logging/UserEventDispatcherExtension.java +++ b/quickstep/src/com/android/quickstep/logging/UserEventDispatcherExtension.java @@ -20,10 +20,10 @@ import android.util.Log; import static com.android.launcher3.logging.LoggerUtils.newLauncherEvent; import static com.android.launcher3.userevent.nano.LauncherLogProto.ControlType.CANCEL_TARGET; -import static com.android.systemui.shared.system.LauncherEventUtil.VISIBLE; import static com.android.systemui.shared.system.LauncherEventUtil.DISMISS; import static com.android.systemui.shared.system.LauncherEventUtil.RECENTS_QUICK_SCRUB_ONBOARDING_TIP; import static com.android.systemui.shared.system.LauncherEventUtil.RECENTS_SWIPE_UP_ONBOARDING_TIP; +import static com.android.systemui.shared.system.LauncherEventUtil.VISIBLE; import com.android.launcher3.logging.UserEventDispatcher; import com.android.launcher3.userevent.nano.LauncherLogProto; diff --git a/quickstep/src/com/android/quickstep/util/AssistantUtilities.java b/quickstep/src/com/android/quickstep/util/AssistantUtilities.java new file mode 100644 index 000000000..552db1f4d --- /dev/null +++ b/quickstep/src/com/android/quickstep/util/AssistantUtilities.java @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.quickstep.util; + +import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.ACTIVITY_TYPE_ASSISTANT; + +import android.annotation.TargetApi; +import android.app.TaskInfo; +import android.content.Intent; +import android.os.Build; + +import com.android.systemui.shared.system.ActivityManagerWrapper; +import com.android.systemui.shared.system.TaskInfoCompat; + +/** + * Utility class for interacting with the Assistant. + */ +@TargetApi(Build.VERSION_CODES.Q) +public final class AssistantUtilities { + + /** Returns true if an Assistant activity that is excluded from recents is running. */ + public static boolean isExcludedAssistantRunning() { + return isExcludedAssistant(ActivityManagerWrapper.getInstance().getRunningTask()); + } + + /** Returns true if the given task holds an Assistant activity that is excluded from recents. */ + public static boolean isExcludedAssistant(TaskInfo info) { + return info != null + && TaskInfoCompat.getActivityType(info) == ACTIVITY_TYPE_ASSISTANT + && (info.baseIntent.getFlags() & Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) != 0; + } + + private AssistantUtilities() {} +} diff --git a/quickstep/src/com/android/quickstep/util/LayoutUtils.java b/quickstep/src/com/android/quickstep/util/LayoutUtils.java index 050bdff09..2e118b44d 100644 --- a/quickstep/src/com/android/quickstep/util/LayoutUtils.java +++ b/quickstep/src/com/android/quickstep/util/LayoutUtils.java @@ -26,7 +26,7 @@ import androidx.annotation.IntDef; import com.android.launcher3.DeviceProfile; import com.android.launcher3.R; -import com.android.launcher3.config.FeatureFlags; +import com.android.quickstep.SysUINavigationMode; import java.lang.annotation.Retention; @@ -39,12 +39,27 @@ public class LayoutUtils { @IntDef({MULTI_WINDOW_STRATEGY_HALF_SCREEN, MULTI_WINDOW_STRATEGY_DEVICE_PROFILE}) private @interface MultiWindowStrategy {} + /** + * The height for the swipe up motion + */ + public static float getDefaultSwipeHeight(Context context, DeviceProfile dp) { + float swipeHeight = dp.allAppsCellHeightPx - dp.allAppsIconTextSizePx; + if (SysUINavigationMode.getMode(context) == SysUINavigationMode.Mode.NO_BUTTON) { + swipeHeight -= dp.getInsets().bottom; + } + return swipeHeight; + } + public static void calculateLauncherTaskSize(Context context, DeviceProfile dp, Rect outRect) { float extraSpace; if (dp.isVerticalBarLayout()) { extraSpace = 0; } else { - extraSpace = dp.hotseatBarSizePx + dp.verticalDragHandleSizePx; + Resources res = context.getResources(); + + extraSpace = getDefaultSwipeHeight(context, dp) + dp.verticalDragHandleSizePx + + res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_extra_vertical_size) + + res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_bottom_padding); } calculateTaskSize(context, dp, extraSpace, MULTI_WINDOW_STRATEGY_HALF_SCREEN, outRect); } diff --git a/quickstep/src/com/android/quickstep/views/ShelfScrimView.java b/quickstep/src/com/android/quickstep/views/ShelfScrimView.java index dc6b56eec..0e591cac7 100644 --- a/quickstep/src/com/android/quickstep/views/ShelfScrimView.java +++ b/quickstep/src/com/android/quickstep/views/ShelfScrimView.java @@ -38,12 +38,12 @@ import com.android.launcher3.DeviceProfile; import com.android.launcher3.R; import com.android.launcher3.Utilities; import com.android.launcher3.anim.Interpolators; -import com.android.launcher3.uioverrides.states.OverviewState; import com.android.launcher3.util.Themes; import com.android.launcher3.views.ScrimView; import com.android.quickstep.SysUINavigationMode; import com.android.quickstep.SysUINavigationMode.Mode; import com.android.quickstep.SysUINavigationMode.NavigationModeChangeListener; +import com.android.quickstep.util.LayoutUtils; /** * Scrim used for all-apps and shelf in Overview @@ -161,9 +161,9 @@ public class ShelfScrimView extends ScrimView implements NavigationModeChangeLis mMidProgress = OVERVIEW.getVerticalProgress(mLauncher); Rect hotseatPadding = dp.getHotseatLayoutPadding(); int hotseatSize = dp.hotseatBarSizePx + dp.getInsets().bottom - - hotseatPadding.bottom - hotseatPadding.top; + + hotseatPadding.bottom + hotseatPadding.top; float dragHandleTop = - Math.min(hotseatSize, OverviewState.getDefaultSwipeHeight(context, dp)); + Math.min(hotseatSize, LayoutUtils.getDefaultSwipeHeight(context, dp)); mDragHandleProgress = 1 - (dragHandleTop / mShiftRange); } mTopOffset = dp.getInsets().top - mShelfOffset; diff --git a/quickstep/tests/src/com/android/quickstep/AppPredictionsUITests.java b/quickstep/tests/src/com/android/quickstep/AppPredictionsUITests.java index d0956d1f6..7801775d4 100644 --- a/quickstep/tests/src/com/android/quickstep/AppPredictionsUITests.java +++ b/quickstep/tests/src/com/android/quickstep/AppPredictionsUITests.java @@ -150,10 +150,10 @@ public class AppPredictionsUITests extends AbstractQuickStepTest { List<AppTarget> targets = new ArrayList<>(activities.length); for (LauncherActivityInfo info : activities) { ComponentName cn = info.getComponentName(); - AppTarget target = - new AppTarget.Builder(new AppTargetId("app:" + cn), cn.getPackageName(), info.getUser()) - .setClassName(cn.getClassName()) - .build(); + AppTarget target = new AppTarget.Builder( + new AppTargetId("app:" + cn), cn.getPackageName(), info.getUser()) + .setClassName(cn.getClassName()) + .build(); targets.add(target); } mCallback.onTargetsAvailable(targets); diff --git a/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java b/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java index e5f949b88..8c11c1c2c 100644 --- a/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java +++ b/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java @@ -42,6 +42,7 @@ import androidx.test.uiautomator.UiDevice; import androidx.test.uiautomator.Until; import com.android.launcher3.tapl.LauncherInstrumentation; +import com.android.launcher3.tapl.TestHelpers; import com.android.launcher3.testcomponent.TestCommandReceiver; import com.android.launcher3.util.rule.FailureWatcher; import com.android.quickstep.NavigationModeSwitchRule.NavigationModeSwitch; @@ -78,7 +79,7 @@ public class FallbackRecentsTest { Context context = instrumentation.getContext(); mDevice = UiDevice.getInstance(instrumentation); mDevice.setOrientationNatural(); - mLauncher = new LauncherInstrumentation(instrumentation); + mLauncher = new LauncherInstrumentation(); mOrderSensitiveRules = RuleChain. outerRule(new NavigationModeSwitchRule(mLauncher)). @@ -103,6 +104,11 @@ public class FallbackRecentsTest { } } }; + if (TestHelpers.isInLauncherProcess()) { + mLauncher.setSystemHealthSupplier(startTime -> TestCommandReceiver.callCommand( + TestCommandReceiver.GET_SYSTEM_HEALTH_MESSAGE, startTime.toString()). + getString("result")); + } } @NavigationModeSwitch(mode = THREE_BUTTON) diff --git a/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java b/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java index e29552713..c2197ab70 100644 --- a/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java +++ b/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java @@ -181,7 +181,7 @@ public class NavigationModeSwitchRule implements TestRule { SysUINavigationMode.INSTANCE.get(targetContext); targetContext.getMainExecutor().execute(() -> sysUINavigationMode.addModeChangeListener(listener)); - latch.await(10, TimeUnit.SECONDS); + latch.await(60, TimeUnit.SECONDS); targetContext.getMainExecutor().execute(() -> sysUINavigationMode.removeModeChangeListener(listener)); assertTrue("Navigation mode didn't change to " + expectedMode, diff --git a/quickstep/tests/src/com/android/quickstep/RecentTasksListTest.java b/quickstep/tests/src/com/android/quickstep/RecentTasksListTest.java new file mode 100644 index 000000000..34eb7f817 --- /dev/null +++ b/quickstep/tests/src/com/android/quickstep/RecentTasksListTest.java @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.quickstep; + +import static junit.framework.TestCase.assertNull; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Matchers.anyInt; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.app.ActivityManager; + +import androidx.test.filters.SmallTest; + +import com.android.launcher3.util.LooperExecutor; +import com.android.systemui.shared.recents.model.Task; +import com.android.systemui.shared.system.ActivityManagerWrapper; +import com.android.systemui.shared.system.KeyguardManagerCompat; + +import org.junit.Before; +import org.junit.Test; + +import java.util.Collections; +import java.util.List; + +@SmallTest +public class RecentTasksListTest { + + private ActivityManagerWrapper mockActivityManagerWrapper; + + // Class under test + private RecentTasksList mRecentTasksList; + + @Before + public void setup() { + LooperExecutor mockMainThreadExecutor = mock(LooperExecutor.class); + KeyguardManagerCompat mockKeyguardManagerCompat = mock(KeyguardManagerCompat.class); + mockActivityManagerWrapper = mock(ActivityManagerWrapper.class); + mRecentTasksList = new RecentTasksList(mockMainThreadExecutor, mockKeyguardManagerCompat, + mockActivityManagerWrapper); + } + + @Test + public void onTaskRemoved_reloadsAllTasks() { + mRecentTasksList.onTaskRemoved(0); + verify(mockActivityManagerWrapper, times(1)) + .getRecentTasks(anyInt(), anyInt()); + } + + @Test + public void onTaskStackChanged_doesNotFetchTasks() { + mRecentTasksList.onTaskStackChanged(); + verify(mockActivityManagerWrapper, times(0)) + .getRecentTasks(anyInt(), anyInt()); + } + + @Test + public void loadTasksInBackground_onlyKeys_noValidTaskDescription() { + ActivityManager.RecentTaskInfo recentTaskInfo = new ActivityManager.RecentTaskInfo(); + when(mockActivityManagerWrapper.getRecentTasks(anyInt(), anyInt())) + .thenReturn(Collections.singletonList(recentTaskInfo)); + + List<Task> taskList = mRecentTasksList.loadTasksInBackground(Integer.MAX_VALUE, true); + + assertEquals(1, taskList.size()); + assertNull(taskList.get(0).taskDescription.getLabel()); + } + + @Test + public void loadTasksInBackground_moreThanKeys_hasValidTaskDescription() { + String taskDescription = "Wheeee!"; + ActivityManager.RecentTaskInfo recentTaskInfo = new ActivityManager.RecentTaskInfo(); + recentTaskInfo.taskDescription = new ActivityManager.TaskDescription(taskDescription); + when(mockActivityManagerWrapper.getRecentTasks(anyInt(), anyInt())) + .thenReturn(Collections.singletonList(recentTaskInfo)); + + List<Task> taskList = mRecentTasksList.loadTasksInBackground(Integer.MAX_VALUE, false); + + assertEquals(1, taskList.size()); + assertEquals(taskDescription, taskList.get(0).taskDescription.getLabel()); + } +} diff --git a/quickstep/tests/src/com/android/quickstep/StartLauncherViaGestureTests.java b/quickstep/tests/src/com/android/quickstep/StartLauncherViaGestureTests.java index c5b560c3f..2111e2ca2 100644 --- a/quickstep/tests/src/com/android/quickstep/StartLauncherViaGestureTests.java +++ b/quickstep/tests/src/com/android/quickstep/StartLauncherViaGestureTests.java @@ -25,7 +25,6 @@ import androidx.test.filters.LargeTest; import androidx.test.runner.AndroidJUnit4; import com.android.launcher3.Launcher; -import com.android.launcher3.tapl.LauncherInstrumentation; import com.android.launcher3.util.RaceConditionReproducer; import com.android.quickstep.NavigationModeSwitchRule.Mode; import com.android.quickstep.NavigationModeSwitchRule.NavigationModeSwitch; @@ -80,8 +79,6 @@ public class StartLauncherViaGestureTests extends AbstractQuickStepTest { @Test @NavigationModeSwitch public void testStressPressHome() { - if (LauncherInstrumentation.isAvd()) return; // b/136278866 - for (int i = 0; i < STRESS_REPEAT_COUNT; ++i) { // Destroy Launcher activity. closeLauncherActivity(); @@ -94,8 +91,6 @@ public class StartLauncherViaGestureTests extends AbstractQuickStepTest { @Test @NavigationModeSwitch public void testStressSwipeToOverview() { - if (LauncherInstrumentation.isAvd()) return; // b/136278866 - for (int i = 0; i < STRESS_REPEAT_COUNT; ++i) { // Destroy Launcher activity. closeLauncherActivity(); diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java index 885fdbf42..8cd3bb61b 100644 --- a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java +++ b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java @@ -17,6 +17,7 @@ package com.android.quickstep; import static com.android.launcher3.ui.TaplTestsLauncher3.getAppPackageName; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; @@ -34,6 +35,7 @@ import com.android.launcher3.LauncherState; import com.android.launcher3.tapl.AllApps; import com.android.launcher3.tapl.AllAppsFromOverview; import com.android.launcher3.tapl.Background; +import com.android.launcher3.tapl.LauncherInstrumentation.NavigationModel; import com.android.launcher3.tapl.Overview; import com.android.launcher3.tapl.OverviewTask; import com.android.launcher3.tapl.TestHelpers; @@ -210,16 +212,21 @@ public class TaplTestsQuickstep extends AbstractQuickStepTest { @PortraitLandscape public void testBackground() throws Exception { startAppFast(resolveSystemApp(Intent.CATEGORY_APP_CALCULATOR)); + final Background background = getAndAssertBackground(); + + assertNotNull("Background.switchToOverview() returned null", background.switchToOverview()); + assertTrue("Launcher internal state didn't switch to Overview", + isInState(LauncherState.OVERVIEW)); + } + + private Background getAndAssertBackground() { final Background background = mLauncher.getBackground(); assertNotNull("Launcher.getBackground() returned null", background); executeOnLauncher(launcher -> assertTrue( "Launcher activity is the top activity; expecting another activity to be the top " + "one", isInBackground(launcher))); - - assertNotNull("Background.switchToOverview() returned null", background.switchToOverview()); - assertTrue("Launcher internal state didn't switch to Overview", - isInState(LauncherState.OVERVIEW)); + return background; } @Test @@ -237,4 +244,47 @@ public class TaplTestsQuickstep extends AbstractQuickStepTest { assertTrue("Launcher internal state is not Home", isInState(LauncherState.NORMAL)); assertNotNull("getHome returned null", mLauncher.getWorkspace()); } + + @Test + @NavigationModeSwitch + @PortraitLandscape + public void testQuickSwitchFromApp() throws Exception { + startTestActivity(2); + startTestActivity(3); + startTestActivity(4); + + Background background = getAndAssertBackground(); + background.quickSwitchToPreviousApp(); + assertTrue("The first app we should have quick switched to is not running", + isTestActivityRunning(3)); + + background = getAndAssertBackground(); + background.quickSwitchToPreviousApp(); + if (mLauncher.getNavigationModel() == NavigationModel.THREE_BUTTON) { + // 3-button mode toggles between 2 apps, rather than going back further. + assertTrue("Second quick switch should have returned to the first app.", + isTestActivityRunning(4)); + } else { + assertTrue("The second app we should have quick switched to is not running", + isTestActivityRunning(2)); + } + getAndAssertBackground(); + } + + private boolean isTestActivityRunning(int activityNumber) { + return mDevice.wait(Until.hasObject(By.pkg(getAppPackageName()) + .text("TestActivity" + activityNumber)), + DEFAULT_UI_TIMEOUT); + } + + @Test + @NavigationModeSwitch + @PortraitLandscape + public void testQuickSwitchFromHome() throws Exception { + startTestActivity(2); + mLauncher.pressHome().quickSwitchToPreviousApp(); + assertTrue("The most recent task is not running after quick switching from home", + isTestActivityRunning(2)); + getAndAssertBackground(); + } } |