From a84672965e964de445286a2fa77601646d8c77d8 Mon Sep 17 00:00:00 2001 From: Schneider Victor-tulias Date: Fri, 1 Oct 2021 13:53:41 -0700 Subject: [PATCH 01/10] Make gesture nav tutorial skip button always visible. Bug: 198285152 Test: started, completed and tried failing gesture nav tutorial. Change-Id: Ic30a54e6592783abafcf1efe61ecedb9514d094d --- .../quickstep/interaction/TutorialController.java | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/quickstep/src/com/android/quickstep/interaction/TutorialController.java b/quickstep/src/com/android/quickstep/interaction/TutorialController.java index 94fb55621e..ae3fe670d8 100644 --- a/quickstep/src/com/android/quickstep/interaction/TutorialController.java +++ b/quickstep/src/com/android/quickstep/interaction/TutorialController.java @@ -258,7 +258,6 @@ abstract class TutorialController implements BackGestureAttemptCallback, mFeedbackView.findViewById(R.id.gesture_tutorial_fragment_feedback_subtitle); subtitle.setText(subtitleResId); if (isGestureSuccessful) { - hideCloseButton(); if (mTutorialFragment.isAtFinalStep()) { showActionButton(); } @@ -382,6 +381,7 @@ abstract class TutorialController implements BackGestureAttemptCallback, void transitToController() { hideFeedback(); hideActionButton(); + updateCloseButton(); updateSubtext(); updateDrawables(); @@ -391,26 +391,21 @@ abstract class TutorialController implements BackGestureAttemptCallback, } } - void hideCloseButton() { - mCloseButton.setVisibility(GONE); - } - - void showCloseButton() { - mCloseButton.setVisibility(View.VISIBLE); + void updateCloseButton() { mCloseButton.setTextAppearance(Utilities.isDarkTheme(mContext) ? R.style.TextAppearance_GestureTutorial_Feedback_Subtext : R.style.TextAppearance_GestureTutorial_Feedback_Subtext_Dark); } void hideActionButton() { - showCloseButton(); + mCloseButton.setVisibility(View.VISIBLE); // Invisible to maintain the layout. mActionButton.setVisibility(View.INVISIBLE); mActionButton.setOnClickListener(null); } void showActionButton() { - hideCloseButton(); + mCloseButton.setVisibility(GONE); mActionButton.setVisibility(View.VISIBLE); mActionButton.setOnClickListener(this::onActionButtonClicked); } From 2745327c5ef591781d24fa0ddef6ac0f6ff45cdf Mon Sep 17 00:00:00 2001 From: Bill Lin Date: Mon, 25 Oct 2021 14:40:51 +0800 Subject: [PATCH 02/10] Allow One-handed gesture when densityDpi > 600 This logic was aim to prevent tablet device enable One-handed gesture so added displayInfo.densityDpi < DisplayMetrics.DENSITY_600 before. However, it seems this densityDpi=600 does not able to represent to tablet device, instead we already have system property "ro.support_one_handed_mode" which provides feasibility to config false on tablet project, as the reason we can safe to remove this condition. Bug: 203936659 Test: adb shell wm density 600 , and observe OHM gesture is available Test: atest WMShellUnitTests Change-Id: Ic7ae10e8a47d26b9bb39ab80e22d591d74f89ae5 --- .../com/android/quickstep/RecentsAnimationDeviceState.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java index e2441edee6..73d14246a0 100644 --- a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java +++ b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java @@ -59,7 +59,6 @@ import android.os.SystemProperties; import android.os.UserManager; import android.provider.Settings; import android.text.TextUtils; -import android.util.DisplayMetrics; import android.view.MotionEvent; import android.view.Surface; @@ -581,8 +580,7 @@ public class RecentsAnimationDeviceState implements final Info displayInfo = mDisplayController.getInfo(); return (mRotationTouchHelper.touchInOneHandedModeRegion(ev) && displayInfo.rotation != Surface.ROTATION_90 - && displayInfo.rotation != Surface.ROTATION_270 - && displayInfo.densityDpi < DisplayMetrics.DENSITY_600); + && displayInfo.rotation != Surface.ROTATION_270); } return false; } From 987221a1cc706e36e7193b9d270537f871a19bcc Mon Sep 17 00:00:00 2001 From: Alina Zaidi Date: Thu, 7 Oct 2021 16:33:22 +0100 Subject: [PATCH 03/10] Send widget added and removed events to AiAi. -Pass the widgets on workspace as an extra List while creating prediction session. -Notify the session about widget added/removed events after creation. Bug: 186648032 Test: Manually checked events are received Change-Id: I4de6c011a1e24c129a3e995ddba16bfc55074939 --- .../hybridhotseat/HotseatPredictionModel.java | 74 +--------- .../launcher3/model/AppEventProducer.java | 25 ++-- .../launcher3/model/PredictionHelper.java | 130 ++++++++++++++++++ .../model/QuickstepModelDelegate.java | 39 +++++- 4 files changed, 184 insertions(+), 84 deletions(-) create mode 100644 quickstep/src/com/android/launcher3/model/PredictionHelper.java diff --git a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionModel.java b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionModel.java index 080633a653..56945ba0a6 100644 --- a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionModel.java +++ b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionModel.java @@ -16,33 +16,25 @@ package com.android.launcher3.hybridhotseat; import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION; +import static com.android.launcher3.model.PredictionHelper.getAppTargetFromItemInfo; +import static com.android.launcher3.model.PredictionHelper.isTrackedForHotseatPrediction; +import static com.android.launcher3.model.PredictionHelper.wrapAppTargetWithItemLocation; import android.app.prediction.AppTarget; import android.app.prediction.AppTargetEvent; -import android.app.prediction.AppTargetId; -import android.content.ComponentName; import android.content.Context; import android.os.Bundle; -import com.android.launcher3.LauncherSettings; -import com.android.launcher3.Workspace; import com.android.launcher3.model.BgDataModel; import com.android.launcher3.model.BgDataModel.FixedContainerItems; import com.android.launcher3.model.data.ItemInfo; -import com.android.launcher3.model.data.LauncherAppWidgetInfo; -import com.android.launcher3.model.data.WorkspaceItemInfo; -import com.android.launcher3.shortcuts.ShortcutKey; import java.util.ArrayList; -import java.util.Locale; /** * Model helper for app predictions in workspace */ public class HotseatPredictionModel { - private static final String APP_LOCATION_HOTSEAT = "hotseat"; - private static final String APP_LOCATION_WORKSPACE = "workspace"; - private static final String BUNDLE_KEY_PIN_EVENTS = "pin_events"; private static final String BUNDLE_KEY_CURRENT_ITEMS = "current_items"; @@ -54,15 +46,15 @@ public class HotseatPredictionModel { ArrayList events = new ArrayList<>(); ArrayList workspaceItems = dataModel.getAllWorkspaceItems(); for (ItemInfo item : workspaceItems) { - AppTarget target = getAppTargetFromInfo(context, item); - if (target != null && !isTrackedForPrediction(item)) continue; - events.add(wrapAppTargetWithLocation(target, AppTargetEvent.ACTION_PIN, item)); + AppTarget target = getAppTargetFromItemInfo(context, item); + if (target != null && !isTrackedForHotseatPrediction(item)) continue; + events.add(wrapAppTargetWithItemLocation(target, AppTargetEvent.ACTION_PIN, item)); } ArrayList currentTargets = new ArrayList<>(); FixedContainerItems hotseatItems = dataModel.extraItems.get(CONTAINER_HOTSEAT_PREDICTION); if (hotseatItems != null) { for (ItemInfo itemInfo : hotseatItems.items) { - AppTarget target = getAppTargetFromInfo(context, itemInfo); + AppTarget target = getAppTargetFromItemInfo(context, itemInfo); if (target != null) currentTargets.add(target); } } @@ -70,56 +62,4 @@ public class HotseatPredictionModel { bundle.putParcelableArrayList(BUNDLE_KEY_CURRENT_ITEMS, currentTargets); return bundle; } - - /** - * Creates and returns for {@link AppTarget} object given an {@link ItemInfo}. Returns null - * if item is not supported prediction - */ - public static AppTarget getAppTargetFromInfo(Context context, ItemInfo info) { - if (info == null) return null; - if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET - && info instanceof LauncherAppWidgetInfo - && ((LauncherAppWidgetInfo) info).providerName != null) { - ComponentName cn = ((LauncherAppWidgetInfo) info).providerName; - return new AppTarget.Builder(new AppTargetId("widget:" + cn.getPackageName()), - cn.getPackageName(), info.user).setClassName(cn.getClassName()).build(); - } else if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION - && info.getTargetComponent() != null) { - ComponentName cn = info.getTargetComponent(); - return new AppTarget.Builder(new AppTargetId("app:" + cn.getPackageName()), - cn.getPackageName(), info.user).setClassName(cn.getClassName()).build(); - } else if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT - && info instanceof WorkspaceItemInfo) { - ShortcutKey shortcutKey = ShortcutKey.fromItemInfo(info); - //TODO: switch to using full shortcut info - return new AppTarget.Builder(new AppTargetId("shortcut:" + shortcutKey.getId()), - shortcutKey.componentName.getPackageName(), shortcutKey.user).build(); - } else if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_FOLDER) { - return new AppTarget.Builder(new AppTargetId("folder:" + info.id), - context.getPackageName(), info.user).build(); - } - return null; - } - - /** - * Creates and returns {@link AppTargetEvent} from an {@link AppTarget}, action, and item - * location using {@link ItemInfo} - */ - public static AppTargetEvent wrapAppTargetWithLocation( - AppTarget target, int action, ItemInfo info) { - String location = String.format(Locale.ENGLISH, "%s/%d/[%d,%d]/[%d,%d]", - info.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT - ? APP_LOCATION_HOTSEAT : APP_LOCATION_WORKSPACE, - info.screenId, info.cellX, info.cellY, info.spanX, info.spanY); - return new AppTargetEvent.Builder(target, action).setLaunchLocation(location).build(); - } - - /** - * Helper method to determine if {@link ItemInfo} should be tracked and reported to predictors - */ - public static boolean isTrackedForPrediction(ItemInfo info) { - return info.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT || ( - info.container == LauncherSettings.Favorites.CONTAINER_DESKTOP - && info.screenId == Workspace.FIRST_SCREEN_ID); - } } diff --git a/quickstep/src/com/android/launcher3/model/AppEventProducer.java b/quickstep/src/com/android/launcher3/model/AppEventProducer.java index b665db6ffc..09233a4e17 100644 --- a/quickstep/src/com/android/launcher3/model/AppEventProducer.java +++ b/quickstep/src/com/android/launcher3/model/AppEventProducer.java @@ -22,6 +22,7 @@ import static android.app.prediction.AppTargetEvent.ACTION_UNPIN; import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION; import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_PREDICTION; +import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_WIDGETS_PREDICTION; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_APP_LAUNCH_TAP; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_FOLDER_CONVERTED_TO_ICON; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_HOTSEAT_PREDICTION_PINNED; @@ -35,6 +36,8 @@ import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCH import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_QUICKSWITCH_RIGHT; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASK_LAUNCH_SWIPE_DOWN; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASK_LAUNCH_TAP; +import static com.android.launcher3.model.PredictionHelper.isTrackedForHotseatPrediction; +import static com.android.launcher3.model.PredictionHelper.isTrackedForWidgetPrediction; import static com.android.launcher3.util.Executors.MODEL_EXECUTOR; import android.annotation.TargetApi; @@ -62,7 +65,6 @@ import com.android.launcher3.logger.LauncherAtom.FolderContainer; import com.android.launcher3.logger.LauncherAtom.HotseatContainer; import com.android.launcher3.logger.LauncherAtom.WorkspaceContainer; import com.android.launcher3.logging.StatsLogManager.EventEnum; -import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.pm.UserCache; import com.android.launcher3.shortcuts.ShortcutRequest; import com.android.quickstep.logging.StatsLogCompatManager.StatsLogConsumer; @@ -141,6 +143,9 @@ public class AppEventProducer implements StatsLogConsumer { if (isTrackedForHotseatPrediction(mLastDragItem)) { sendEvent(mLastDragItem, ACTION_UNPIN, CONTAINER_HOTSEAT_PREDICTION); } + if (isTrackedForWidgetPrediction(atomInfo)) { + sendEvent(atomInfo, ACTION_PIN, CONTAINER_WIDGETS_PREDICTION); + } mLastDragItem = null; } else if (event == LAUNCHER_ITEM_DROP_FOLDER_CREATED) { if (isTrackedForHotseatPrediction(atomInfo)) { @@ -158,6 +163,9 @@ public class AppEventProducer implements StatsLogConsumer { if (mLastDragItem != null && isTrackedForHotseatPrediction(mLastDragItem)) { sendEvent(mLastDragItem, ACTION_UNPIN, CONTAINER_HOTSEAT_PREDICTION); } + if (mLastDragItem != null && isTrackedForWidgetPrediction(mLastDragItem)) { + sendEvent(mLastDragItem, ACTION_UNPIN, CONTAINER_WIDGETS_PREDICTION); + } } else if (event == LAUNCHER_HOTSEAT_PREDICTION_PINNED) { if (isTrackedForHotseatPrediction(atomInfo)) { sendEvent(atomInfo, ACTION_PIN, CONTAINER_HOTSEAT_PREDICTION); @@ -302,19 +310,4 @@ public class AppEventProducer implements StatsLogConsumer { return TextUtils.isEmpty(componentNameString) ? null : ComponentName.unflattenFromString(componentNameString); } - - /** - * Helper method to determine if {@link ItemInfo} should be tracked and reported to predictors - */ - private static boolean isTrackedForHotseatPrediction(LauncherAtom.ItemInfo info) { - ContainerInfo ci = info.getContainerInfo(); - switch (ci.getContainerCase()) { - case HOTSEAT: - return true; - case WORKSPACE: - return ci.getWorkspace().getPageIndex() == 0; - default: - return false; - } - } } diff --git a/quickstep/src/com/android/launcher3/model/PredictionHelper.java b/quickstep/src/com/android/launcher3/model/PredictionHelper.java new file mode 100644 index 0000000000..738dd83cbc --- /dev/null +++ b/quickstep/src/com/android/launcher3/model/PredictionHelper.java @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.launcher3.model; + +import static com.android.launcher3.logger.LauncherAtom.ContainerInfo.ContainerCase.WORKSPACE; + +import android.app.prediction.AppTarget; +import android.app.prediction.AppTargetEvent; +import android.app.prediction.AppTargetId; +import android.content.ComponentName; +import android.content.Context; + +import androidx.annotation.Nullable; + +import com.android.launcher3.LauncherSettings; +import com.android.launcher3.Workspace; +import com.android.launcher3.logger.LauncherAtom; +import com.android.launcher3.model.data.ItemInfo; +import com.android.launcher3.model.data.LauncherAppWidgetInfo; +import com.android.launcher3.model.data.WorkspaceItemInfo; +import com.android.launcher3.shortcuts.ShortcutKey; + +import java.util.Locale; + +/** Helper class with methods for converting launcher items to form usable by predictors */ +public final class PredictionHelper { + private static final String APP_LOCATION_HOTSEAT = "hotseat"; + private static final String APP_LOCATION_WORKSPACE = "workspace"; + + /** + * Creates and returns an {@link AppTarget} object for an {@link ItemInfo}. Returns null + * if item type is not supported in predictions + */ + @Nullable + public static AppTarget getAppTargetFromItemInfo(Context context, ItemInfo info) { + if (info == null) return null; + if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET + && info instanceof LauncherAppWidgetInfo + && ((LauncherAppWidgetInfo) info).providerName != null) { + ComponentName cn = ((LauncherAppWidgetInfo) info).providerName; + return new AppTarget.Builder(new AppTargetId("widget:" + cn.getPackageName()), + cn.getPackageName(), info.user).setClassName(cn.getClassName()).build(); + } else if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION + && info.getTargetComponent() != null) { + ComponentName cn = info.getTargetComponent(); + return new AppTarget.Builder(new AppTargetId("app:" + cn.getPackageName()), + cn.getPackageName(), info.user).setClassName(cn.getClassName()).build(); + } else if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT + && info instanceof WorkspaceItemInfo) { + ShortcutKey shortcutKey = ShortcutKey.fromItemInfo(info); + //TODO: switch to using full shortcut info + return new AppTarget.Builder(new AppTargetId("shortcut:" + shortcutKey.getId()), + shortcutKey.componentName.getPackageName(), shortcutKey.user).build(); + } else if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_FOLDER) { + return new AppTarget.Builder(new AppTargetId("folder:" + info.id), + context.getPackageName(), info.user).build(); + } + return null; + } + + /** + * Creates and returns {@link AppTargetEvent} from an {@link AppTarget}, action, and item + * location using {@link ItemInfo} + */ + public static AppTargetEvent wrapAppTargetWithItemLocation( + AppTarget target, int action, ItemInfo info) { + String location = String.format(Locale.ENGLISH, "%s/%d/[%d,%d]/[%d,%d]", + info.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT + ? APP_LOCATION_HOTSEAT : APP_LOCATION_WORKSPACE, + info.screenId, info.cellX, info.cellY, info.spanX, info.spanY); + return new AppTargetEvent.Builder(target, action).setLaunchLocation(location).build(); + } + + /** + * Helper method to determine if {@link ItemInfo} should be tracked and reported to hotseat + * predictors + */ + public static boolean isTrackedForHotseatPrediction(ItemInfo info) { + return info.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT || ( + info.container == LauncherSettings.Favorites.CONTAINER_DESKTOP + && info.screenId == Workspace.FIRST_SCREEN_ID); + } + + /** + * Helper method to determine if {@link LauncherAtom.ItemInfo} should be tracked and reported to + * hotseat predictors + */ + public static boolean isTrackedForHotseatPrediction(LauncherAtom.ItemInfo info) { + LauncherAtom.ContainerInfo ci = info.getContainerInfo(); + switch (ci.getContainerCase()) { + case HOTSEAT: + return true; + case WORKSPACE: + return ci.getWorkspace().getPageIndex() == 0; + default: + return false; + } + } + + /** + * Helper method to determine if {@link ItemInfo} should be tracked and reported to widget + * predictors + */ + public static boolean isTrackedForWidgetPrediction(ItemInfo info) { + return info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET + && info.container == LauncherSettings.Favorites.CONTAINER_DESKTOP; + } + + /** + * Helper method to determine if {@link LauncherAtom.ItemInfo} should be tracked and reported + * to widget predictors + */ + public static boolean isTrackedForWidgetPrediction(LauncherAtom.ItemInfo info) { + return info.getItemCase() == LauncherAtom.ItemInfo.ItemCase.WIDGET + && info.getContainerInfo().getContainerCase() == WORKSPACE; + } +} diff --git a/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java b/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java index 55a140dffb..7794d27d26 100644 --- a/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java +++ b/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java @@ -25,8 +25,12 @@ import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICA import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT; import static com.android.launcher3.Utilities.getDevicePrefs; import static com.android.launcher3.hybridhotseat.HotseatPredictionModel.convertDataModelToAppTargetBundle; +import static com.android.launcher3.model.PredictionHelper.getAppTargetFromItemInfo; +import static com.android.launcher3.model.PredictionHelper.wrapAppTargetWithItemLocation; import static com.android.launcher3.util.Executors.MODEL_EXECUTOR; +import static java.util.stream.Collectors.toCollection; + import android.app.StatsManager; import android.app.prediction.AppPredictionContext; import android.app.prediction.AppPredictionManager; @@ -39,6 +43,7 @@ import android.content.SharedPreferences; import android.content.pm.LauncherActivityInfo; import android.content.pm.LauncherApps; import android.content.pm.ShortcutInfo; +import android.os.Bundle; import android.os.UserHandle; import android.util.Log; import android.util.StatsEvent; @@ -62,6 +67,7 @@ import com.android.launcher3.util.PersistedItemArray; import com.android.quickstep.logging.StatsLogCompatManager; import com.android.systemui.shared.system.SysUiStatsLog; +import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; @@ -75,6 +81,7 @@ public class QuickstepModelDelegate extends ModelDelegate { public static final String LAST_PREDICTION_ENABLED_STATE = "last_prediction_enabled_state"; private static final String LAST_SNAPSHOT_TIME_MILLIS = "LAST_SNAPSHOT_TIME_MILLIS"; + private static final String BUNDLE_KEY_ADDED_APP_WIDGETS = "added_app_widgets"; private static final int NUM_OF_RECOMMENDED_WIDGETS_PREDICATION = 20; private static final boolean IS_DEBUG = false; @@ -272,6 +279,7 @@ public class QuickstepModelDelegate extends ModelDelegate { registerWidgetsPredictor(apm.createAppPredictionSession( new AppPredictionContext.Builder(context) .setUiSurface("widgets") + .setExtras(getBundleForWidgetsOnWorkspace(context, mDataModel)) .setPredictedTargetCount(NUM_OF_RECOMMENDED_WIDGETS_PREDICATION) .build())); } @@ -306,12 +314,41 @@ public class QuickstepModelDelegate extends ModelDelegate { } private void onAppTargetEvent(AppTargetEvent event, int client) { - PredictorState state = client == CONTAINER_PREDICTION ? mAllAppsState : mHotseatState; + PredictorState state; + switch(client) { + case CONTAINER_PREDICTION: + state = mAllAppsState; + break; + case CONTAINER_WIDGETS_PREDICTION: + state = mWidgetsRecommendationState; + break; + case CONTAINER_HOTSEAT_PREDICTION: + default: + state = mHotseatState; + break; + } if (state.predictor != null) { state.predictor.notifyAppTargetEvent(event); } } + private Bundle getBundleForWidgetsOnWorkspace(Context context, BgDataModel dataModel) { + Bundle bundle = new Bundle(); + ArrayList widgetEvents = + dataModel.getAllWorkspaceItems().stream() + .filter(PredictionHelper::isTrackedForWidgetPrediction) + .map(item -> { + AppTarget target = getAppTargetFromItemInfo(context, item); + if (target == null) return null; + return wrapAppTargetWithItemLocation( + target, AppTargetEvent.ACTION_PIN, item); + }) + .filter(Objects::nonNull) + .collect(toCollection(ArrayList::new)); + bundle.putParcelableArrayList(BUNDLE_KEY_ADDED_APP_WIDGETS, widgetEvents); + return bundle; + } + static class PredictorState { public final FixedContainerItems items; From 97503ab4fbbda1ef44b710fce964514c007f5a4c Mon Sep 17 00:00:00 2001 From: Sebastian Franco Date: Mon, 25 Oct 2021 15:19:38 -0500 Subject: [PATCH 04/10] Fixing the possibility of opening multiple WidgetsFullSheet The error is that you can open multiple times the view for selecting widgets for the Home Screen (i.e. WidgetsFullSheet.java) but only when using a keyboard by using the command ctrl+w creating a new instance every time you use it. To fix this I added a singleton pattern so that only one instance of WidgetsFullSheet.java can be open at any given point. Test: Manually Tested Fix: 196704061 Change-Id: I7a364ee5e746e77455da326a21aff6830ac18e8c --- src/com/android/launcher3/views/OptionsPopupView.java | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/com/android/launcher3/views/OptionsPopupView.java b/src/com/android/launcher3/views/OptionsPopupView.java index 33ab0d28fb..9774c46624 100644 --- a/src/com/android/launcher3/views/OptionsPopupView.java +++ b/src/com/android/launcher3/views/OptionsPopupView.java @@ -40,6 +40,7 @@ import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; import androidx.core.content.ContextCompat; +import com.android.launcher3.AbstractFloatingView; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherSettings; import com.android.launcher3.R; @@ -220,6 +221,11 @@ public class OptionsPopupView extends ArrowPopup Toast.makeText(launcher, R.string.safemode_widget_error, Toast.LENGTH_SHORT).show(); return null; } else { + AbstractFloatingView floatingView = AbstractFloatingView.getTopOpenViewWithType( + launcher, TYPE_WIDGETS_FULL_SHEET); + if (floatingView != null) { + return (WidgetsFullSheet) floatingView; + } return WidgetsFullSheet.show(launcher, true /* animated */); } } @@ -279,7 +285,7 @@ public class OptionsPopupView extends ArrowPopup public final OnLongClickListener clickListener; public OptionItem(Context context, int labelRes, int iconRes, EventEnum eventId, - OnLongClickListener clickListener) { + OnLongClickListener clickListener) { this.labelRes = labelRes; this.label = context.getText(labelRes); this.icon = ContextCompat.getDrawable(context, iconRes); @@ -288,7 +294,7 @@ public class OptionsPopupView extends ArrowPopup } public OptionItem(CharSequence label, Drawable icon, EventEnum eventId, - OnLongClickListener clickListener) { + OnLongClickListener clickListener) { this.labelRes = 0; this.label = label; this.icon = icon; From 1b5975c205796448dd5b207275a1c9e2e03a4e84 Mon Sep 17 00:00:00 2001 From: Pat Manning Date: Tue, 26 Oct 2021 18:08:45 +0100 Subject: [PATCH 05/10] Fix scroll task off screen to support different screen dimensions. This will fix the breaks on cuttlefish as seen in b/203781041. Test: TaplTestsQuickstep.java Bug: 197630182 Change-Id: I3b582a9df1790543c4e1521b45494fb462bb5c1c --- .../android/quickstep/TaplTestsQuickstep.java | 2 -- .../android/launcher3/tapl/BaseOverview.java | 10 +++----- .../tapl/LauncherInstrumentation.java | 24 +++++++++++++++++++ .../android/launcher3/tapl/OverviewTask.java | 4 ++++ 4 files changed, 31 insertions(+), 9 deletions(-) diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java index 710afe00c2..4895b107e1 100644 --- a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java +++ b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java @@ -43,7 +43,6 @@ import com.android.quickstep.views.RecentsView; import org.junit.After; import org.junit.Before; -import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; @@ -289,7 +288,6 @@ public class TaplTestsQuickstep extends AbstractQuickStepTest { @Test @PortraitLandscape - @Ignore("b/203781041") public void testOverviewForTablet() throws Exception { if (!mLauncher.isTablet()) { return; diff --git a/tests/tapl/com/android/launcher3/tapl/BaseOverview.java b/tests/tapl/com/android/launcher3/tapl/BaseOverview.java index 0e3b5013d8..d5479fbd9e 100644 --- a/tests/tapl/com/android/launcher3/tapl/BaseOverview.java +++ b/tests/tapl/com/android/launcher3/tapl/BaseOverview.java @@ -54,18 +54,14 @@ public class BaseOverview extends LauncherInstrumentation.VisibleContainer { } private void flingForwardImpl() { - flingForwardImpl(0); - } - - private void flingForwardImpl(int rightMargin) { try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer("want to fling forward in overview")) { LauncherInstrumentation.log("Overview.flingForward before fling"); final UiObject2 overview = verifyActiveContainer(); final int leftMargin = mLauncher.getTargetInsets().left + mLauncher.getEdgeSensitivityWidth(); - mLauncher.scroll(overview, Direction.LEFT, new Rect(leftMargin + 1, 0, rightMargin, 0), - 20, false); + mLauncher.scroll(overview, Direction.LEFT, new Rect(leftMargin + 1, 0, 0, 0), 20, + false); try (LauncherInstrumentation.Closable c2 = mLauncher.addContextLayer("flung forwards")) { verifyActiveContainer(); @@ -131,7 +127,7 @@ public class BaseOverview extends LauncherInstrumentation.VisibleContainer { OverviewTask task = getCurrentTask(); mLauncher.assertNotNull("current task is null", task); - flingForwardImpl(task.getTaskCenterX()); + mLauncher.scrollLeftByDistance(verifyActiveContainer(), task.getVisibleWidth()); try (LauncherInstrumentation.Closable c2 = mLauncher.addContextLayer("scrolled task off screen")) { diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java index 9ee0e25bd7..7ffdf4cef8 100644 --- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java +++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java @@ -1190,10 +1190,19 @@ public final class LauncherInstrumentation { return getVisibleBounds(container).bottom - bottomGestureStartOnScreen; } + int getRightGestureMarginInContainer(UiObject2 container) { + final int rightGestureStartOnScreen = getRightGestureStartOnScreen(); + return getVisibleBounds(container).right - rightGestureStartOnScreen; + } + int getBottomGestureStartOnScreen() { return getRealDisplaySize().y - getBottomGestureSize(); } + int getRightGestureStartOnScreen() { + return getRealDisplaySize().x - getWindowInsets().right; + } + void clickLauncherObject(UiObject2 object) { waitForObjectEnabled(object, "clickLauncherObject"); expectEvent(TestProtocol.SEQUENCE_MAIN, LauncherInstrumentation.EVENT_TOUCH_DOWN); @@ -1235,6 +1244,21 @@ public final class LauncherInstrumentation { true); } + void scrollLeftByDistance(UiObject2 container, int distance) { + final Rect containerRect = getVisibleBounds(container); + final int rightGestureMarginInContainer = getRightGestureMarginInContainer(container); + scroll( + container, + Direction.LEFT, + new Rect( + 0, + containerRect.width() - distance - rightGestureMarginInContainer, + 0, + rightGestureMarginInContainer), + 10, + true); + } + void scroll( UiObject2 container, Direction direction, Rect margins, int steps, boolean slowDown) { final Rect rect = getVisibleBounds(container); diff --git a/tests/tapl/com/android/launcher3/tapl/OverviewTask.java b/tests/tapl/com/android/launcher3/tapl/OverviewTask.java index 15bddd7c88..a860e7d894 100644 --- a/tests/tapl/com/android/launcher3/tapl/OverviewTask.java +++ b/tests/tapl/com/android/launcher3/tapl/OverviewTask.java @@ -53,6 +53,10 @@ public final class OverviewTask { return mTask.getVisibleBounds().height(); } + int getVisibleWidth() { + return mTask.getVisibleBounds().width(); + } + int getTaskCenterX() { return mTask.getVisibleCenter().x; } From 3e9bd87ca92d077b999a069dae181b21e57c4130 Mon Sep 17 00:00:00 2001 From: Vinit Nayak Date: Tue, 26 Oct 2021 11:10:23 -0700 Subject: [PATCH 06/10] Prevent launching app in split if task split is unsupported Bug: 204026157 Change-Id: Ie13ac74e0c1d357db242d49083be97f0f45e6ca5 --- quickstep/res/values/strings.xml | 2 ++ .../src/com/android/quickstep/views/RecentsView.java | 8 ++++++++ 2 files changed, 10 insertions(+) diff --git a/quickstep/res/values/strings.xml b/quickstep/res/values/strings.xml index 6af0d602ab..88c98c0f59 100644 --- a/quickstep/res/values/strings.xml +++ b/quickstep/res/values/strings.xml @@ -193,6 +193,8 @@ Split Tap another app to use splitscreen + + App does not support split-screen. This action isn\'t allowed by the app or your organization diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java index f7a95624ca..5cad31d415 100644 --- a/quickstep/src/com/android/quickstep/views/RecentsView.java +++ b/quickstep/src/com/android/quickstep/views/RecentsView.java @@ -597,6 +597,8 @@ public abstract class RecentsView Date: Tue, 26 Oct 2021 19:57:49 +0000 Subject: [PATCH 07/10] Partially restoring reverted logging ag/15858126 slowed down everything so that the flake stopped to repro. Attempting to reintroduce a more lightweight logging hoping that the flake will keep reproing. Test: presubmit Bug: 195031154 Change-Id: Ib533c22253e367cced1680a6f4d6736087f0837a --- src/com/android/launcher3/DropTargetBar.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/com/android/launcher3/DropTargetBar.java b/src/com/android/launcher3/DropTargetBar.java index eb42a652f9..9fb14f68cb 100644 --- a/src/com/android/launcher3/DropTargetBar.java +++ b/src/com/android/launcher3/DropTargetBar.java @@ -275,8 +275,12 @@ public class DropTargetBar extends FrameLayout @Override protected void onVisibilityChanged(@NonNull View changedView, int visibility) { super.onVisibilityChanged(changedView, visibility); - if (TestProtocol.sDebugTracing && visibility == VISIBLE) { - Log.d(TestProtocol.NO_DROP_TARGET, "9"); + if (TestProtocol.sDebugTracing) { + if (visibility == VISIBLE) { + Log.d(TestProtocol.NO_DROP_TARGET, "9"); + } else { + Log.d(TestProtocol.NO_DROP_TARGET, "Hiding drop target", new Exception()); + } } } } From 9188b6b993dbea0e22cd32bf22794f0ea5a57757 Mon Sep 17 00:00:00 2001 From: Schneider Victor-tulias Date: Tue, 26 Oct 2021 14:56:06 -0700 Subject: [PATCH 08/10] Update the hotseat edu layout for foldable devices. Test: ran the hotseat edu on folded and unfolded foldable device in portrait and landscape mode and in three-button and gesture nav mode. ran hotseat edu on regular phone Fixes: 203734732 Change-Id: Iea2140c8241cdea60cb5db37fbfb7f7701d63bd6 --- quickstep/res/layout/predicted_hotseat_edu.xml | 1 + .../launcher3/hybridhotseat/HotseatEduDialog.java | 15 ++++++++++++++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/quickstep/res/layout/predicted_hotseat_edu.xml b/quickstep/res/layout/predicted_hotseat_edu.xml index 1dab48267b..e4e3956c6e 100644 --- a/quickstep/res/layout/predicted_hotseat_edu.xml +++ b/quickstep/res/layout/predicted_hotseat_edu.xml @@ -73,6 +73,7 @@ launcher:containerType="hotseat" /> implements I mHotseatWrapper = findViewById(R.id.hotseat_wrapper); mSampleHotseat = findViewById(R.id.sample_prediction); + Context context = getContext(); DeviceProfile grid = mActivityContext.getDeviceProfile(); - Rect padding = grid.getHotseatLayoutPadding(getContext()); + Rect padding = grid.getHotseatLayoutPadding(context); mSampleHotseat.getLayoutParams().height = grid.cellHeightPx; mSampleHotseat.setGridSize(grid.numShownHotseatIcons, 1); @@ -102,6 +106,15 @@ public class HotseatEduDialog extends AbstractSlideInView implements I mDismissBtn = findViewById(R.id.no_thanks); mDismissBtn.setOnClickListener(this::onDismiss); + LinearLayout buttonContainer = findViewById(R.id.button_container); + int adjustedMarginEnd = ApiWrapper.getHotseatEndOffset(context) + - buttonContainer.getPaddingEnd(); + if (InvariantDeviceProfile.INSTANCE.get(context) + .getDeviceProfile(context).isTaskbarPresent && adjustedMarginEnd > 0) { + ((LinearLayout.LayoutParams) buttonContainer.getLayoutParams()).setMarginEnd( + adjustedMarginEnd); + } + // update ui to reflect which migration method is going to be used if (FeatureFlags.HOTSEAT_MIGRATE_TO_FOLDER.get()) { ((TextView) findViewById(R.id.hotseat_edu_content)).setText( From ce6bf7dd7f4b9977012e23b4d5554b340907cf2d Mon Sep 17 00:00:00 2001 From: Tony Wickham Date: Wed, 27 Oct 2021 12:35:31 -0700 Subject: [PATCH 09/10] Fix testStressSwipeToOverview - Finish recents controller to app rather than to launcher, to ensure taskbar state uses in-app configuration - Also fix an issue when a gesture completes before onLauncherStart, which happens in 3 button mode. The error I saw in the test was: java.lang.AssertionError: http://go/tapl test failure: Failed to receive an event for the state change: expected [Overview], actual: [Background, Normal]; Context: want to switch from background to overview, clicking Recents button; now visible state is Background (This also accurately describes what I saw on the device, where the LauncherState went to Normal but the task was still running in the live tile) Test: testStressSwipeToOverview Fixes: 203577620 Change-Id: I19616f7921c9821f1b45a90a3e4bec4fb3b8a9d3 --- quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java | 4 ++++ .../src/com/android/quickstep/AbstractQuickStepTest.java | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java index 3ab73bbc5c..e8aa2fa3c7 100644 --- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java +++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java @@ -444,6 +444,10 @@ public abstract class AbsSwipeUpHandler, mAnimationFactory = mActivityInterface.prepareRecentsUI(mDeviceState, mWasLauncherAlreadyVisible, this::onAnimatorPlaybackControllerCreated); maybeUpdateRecentsAttachedState(false /* animate */); + if (mGestureState.getEndTarget() != null) { + // Update the end target in case the gesture ended before we init. + mAnimationFactory.setEndTarget(mGestureState.getEndTarget()); + } }; if (mWasLauncherAlreadyVisible) { // Launcher is visible, but might be about to stop. Thus, if we prepare recents diff --git a/quickstep/tests/src/com/android/quickstep/AbstractQuickStepTest.java b/quickstep/tests/src/com/android/quickstep/AbstractQuickStepTest.java index ca47de3e51..189dff8c5f 100644 --- a/quickstep/tests/src/com/android/quickstep/AbstractQuickStepTest.java +++ b/quickstep/tests/src/com/android/quickstep/AbstractQuickStepTest.java @@ -44,7 +44,7 @@ public abstract class AbstractQuickStepTest extends AbstractLauncherUiTest { protected void onLauncherActivityClose(Launcher launcher) { RecentsView recentsView = launcher.getOverviewPanel(); if (recentsView != null) { - recentsView.finishRecentsAnimation(true, null); + recentsView.finishRecentsAnimation(false /* toRecents */, null); } } From e12b321d5f1e9594be6116b1dc826959826ae8f8 Mon Sep 17 00:00:00 2001 From: bquezada Date: Thu, 28 Oct 2021 17:52:02 +0000 Subject: [PATCH 10/10] Change AppTargetId for launcher events to align with other AppTargetId's. Bug: 202535375 Test: Manual Change-Id: I0c28f7f64935fdaf08edb7781e4e7378a068e8e6 --- quickstep/src/com/android/launcher3/model/AppEventProducer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/quickstep/src/com/android/launcher3/model/AppEventProducer.java b/quickstep/src/com/android/launcher3/model/AppEventProducer.java index 09233a4e17..1305bbc7b2 100644 --- a/quickstep/src/com/android/launcher3/model/AppEventProducer.java +++ b/quickstep/src/com/android/launcher3/model/AppEventProducer.java @@ -171,7 +171,7 @@ public class AppEventProducer implements StatsLogConsumer { sendEvent(atomInfo, ACTION_PIN, CONTAINER_HOTSEAT_PREDICTION); } } else if (event == LAUNCHER_ONRESUME) { - AppTarget target = new AppTarget.Builder(new AppTargetId("id:launcher"), + AppTarget target = new AppTarget.Builder(new AppTargetId("launcher:launcher"), mContext.getPackageName(), Process.myUserHandle()) .build(); sendEvent(target, atomInfo, ACTION_LAUNCH, CONTAINER_PREDICTION);