Snap for 7863714 from e12b321d5f1e9594be6116b1dc826959826ae8f8 to sc-v2-release
Change-Id: I36e23a38a35d40e74d51ee2026b288cea4cf5d24
diff --git a/quickstep/res/layout/gesture_tutorial_foldable_mock_conversation.xml b/quickstep/res/layout/gesture_tutorial_foldable_mock_conversation.xml
index 34bd4e2..b0cc00b 100644
--- a/quickstep/res/layout/gesture_tutorial_foldable_mock_conversation.xml
+++ b/quickstep/res/layout/gesture_tutorial_foldable_mock_conversation.xml
@@ -84,7 +84,7 @@
android:layout_width="match_parent"
android:layout_height="0dp"
android:background="@color/mock_conversation_background"
- android:paddingBottom="80dp"
+ android:paddingBottom="@dimen/gesture_tutorial_mock_taskbar_height"
app:layout_constraintTop_toBottomOf="@id/top_bar"
app:layout_constraintBottom_toBottomOf="parent"
diff --git a/quickstep/res/layout/gesture_tutorial_foldable_mock_conversation_list.xml b/quickstep/res/layout/gesture_tutorial_foldable_mock_conversation_list.xml
index 0309cc3..e5cd9bc 100644
--- a/quickstep/res/layout/gesture_tutorial_foldable_mock_conversation_list.xml
+++ b/quickstep/res/layout/gesture_tutorial_foldable_mock_conversation_list.xml
@@ -51,7 +51,7 @@
android:layout_width="match_parent"
android:layout_height="0dp"
android:background="@color/mock_list_background"
- android:paddingBottom="80dp"
+ android:paddingBottom="@dimen/gesture_tutorial_mock_taskbar_height"
app:layout_constraintTop_toBottomOf="@id/top_bar"
app:layout_constraintBottom_toBottomOf="parent"
diff --git a/quickstep/res/layout/gesture_tutorial_foldable_mock_taskbar.xml b/quickstep/res/layout/gesture_tutorial_foldable_mock_taskbar.xml
new file mode 100644
index 0000000..ddfeeec
--- /dev/null
+++ b/quickstep/res/layout/gesture_tutorial_foldable_mock_taskbar.xml
@@ -0,0 +1,118 @@
+<?xml version="1.0" encoding="utf-8"?>
+<com.android.quickstep.interaction.AnimatedTaskbarView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/gesture_tutorial_mock_taskbar_height">
+
+ <View
+ android:id="@+id/taskbar_background"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@color/gesture_tutorial_taskbar_color"
+
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"/>
+
+ <androidx.constraintlayout.widget.ConstraintLayout
+ android:id="@+id/icon_container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintEnd_toEndOf="parent">
+
+ <androidx.cardview.widget.CardView
+ android:id="@+id/taskbar_icon_1"
+ android:layout_width="@dimen/gesture_tutorial_taskbar_icon_size"
+ android:layout_height="@dimen/gesture_tutorial_taskbar_icon_size"
+ android:layout_marginStart="@dimen/gesture_tutorial_taskbar_padding_start_end"
+
+ app:cardElevation="0dp"
+ app:cardCornerRadius="@dimen/gesture_tutorial_taskbar_icon_corner_radius"
+ app:cardBackgroundColor="@color/mock_app_icon_1"
+ app:layout_constraintDimensionRatio="1:1"
+ app:layout_constraintHorizontal_chainStyle="spread_inside"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintEnd_toStartOf="@id/taskbar_icon_2"/>
+
+ <androidx.cardview.widget.CardView
+ android:id="@+id/taskbar_icon_2"
+ android:layout_width="@dimen/gesture_tutorial_taskbar_icon_size"
+ android:layout_height="@dimen/gesture_tutorial_taskbar_icon_size"
+
+ app:cardElevation="0dp"
+ app:cardCornerRadius="@dimen/gesture_tutorial_taskbar_icon_corner_radius"
+ app:cardBackgroundColor="@color/mock_app_icon_2"
+ app:layout_constraintDimensionRatio="1:1"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintStart_toEndOf="@id/taskbar_icon_1"
+ app:layout_constraintEnd_toStartOf="@id/taskbar_icon_3"/>
+
+ <androidx.cardview.widget.CardView
+ android:id="@+id/taskbar_icon_3"
+ android:layout_width="@dimen/gesture_tutorial_taskbar_icon_size"
+ android:layout_height="@dimen/gesture_tutorial_taskbar_icon_size"
+
+ app:cardElevation="0dp"
+ app:cardCornerRadius="@dimen/gesture_tutorial_taskbar_icon_corner_radius"
+ app:cardBackgroundColor="@color/mock_app_icon_3"
+ app:layout_constraintDimensionRatio="1:1"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintStart_toEndOf="@id/taskbar_icon_2"
+ app:layout_constraintEnd_toStartOf="@id/taskbar_icon_4"/>
+
+ <androidx.cardview.widget.CardView
+ android:id="@+id/taskbar_icon_4"
+ android:layout_width="@dimen/gesture_tutorial_taskbar_icon_size"
+ android:layout_height="@dimen/gesture_tutorial_taskbar_icon_size"
+
+ app:cardElevation="0dp"
+ app:cardCornerRadius="@dimen/gesture_tutorial_taskbar_icon_corner_radius"
+ app:cardBackgroundColor="@color/mock_app_icon_1"
+ app:layout_constraintDimensionRatio="1:1"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintStart_toEndOf="@id/taskbar_icon_3"
+ app:layout_constraintEnd_toStartOf="@id/taskbar_icon_5"/>
+
+ <androidx.cardview.widget.CardView
+ android:id="@+id/taskbar_icon_5"
+ android:layout_width="@dimen/gesture_tutorial_taskbar_icon_size"
+ android:layout_height="@dimen/gesture_tutorial_taskbar_icon_size"
+
+ app:cardElevation="0dp"
+ app:cardCornerRadius="@dimen/gesture_tutorial_taskbar_icon_corner_radius"
+ app:cardBackgroundColor="@color/mock_app_icon_4"
+ app:layout_constraintDimensionRatio="1:1"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintStart_toEndOf="@id/taskbar_icon_4"
+ app:layout_constraintEnd_toStartOf="@id/taskbar_icon_6"/>
+
+ <androidx.cardview.widget.CardView
+ android:id="@+id/taskbar_icon_6"
+ android:layout_width="@dimen/gesture_tutorial_taskbar_icon_size"
+ android:layout_height="@dimen/gesture_tutorial_taskbar_icon_size"
+ android:layout_marginEnd="@dimen/gesture_tutorial_taskbar_padding_start_end"
+
+ app:cardElevation="0dp"
+ app:cardCornerRadius="@dimen/gesture_tutorial_taskbar_icon_corner_radius"
+ app:cardBackgroundColor="@color/mock_app_icon_2"
+ app:layout_constraintDimensionRatio="1:1"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintStart_toEndOf="@id/taskbar_icon_5"
+ app:layout_constraintEnd_toEndOf="parent"/>
+
+ </androidx.constraintlayout.widget.ConstraintLayout>
+
+</com.android.quickstep.interaction.AnimatedTaskbarView>
\ No newline at end of file
diff --git a/quickstep/res/layout/gesture_tutorial_fragment.xml b/quickstep/res/layout/gesture_tutorial_fragment.xml
index 41d0a1d..08e6178 100644
--- a/quickstep/res/layout/gesture_tutorial_fragment.xml
+++ b/quickstep/res/layout/gesture_tutorial_fragment.xml
@@ -101,6 +101,15 @@
android:layout_height="match_parent"
android:background="@drawable/gesture_tutorial_ripple"/>
+ <include
+ layout="@layout/gesture_tutorial_foldable_mock_taskbar"
+ android:id="@+id/gesture_tutorial_fake_taskbar_view"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/gesture_tutorial_mock_taskbar_height"
+ android:layout_alignParentBottom="true"
+ android:layout_alignParentStart="true"
+ android:layout_alignParentEnd="true" />
+
<ImageView
android:id="@+id/gesture_tutorial_edge_gesture_video"
android:layout_width="match_parent"
diff --git a/quickstep/res/layout/predicted_hotseat_edu.xml b/quickstep/res/layout/predicted_hotseat_edu.xml
index 1dab482..e4e3956 100644
--- a/quickstep/res/layout/predicted_hotseat_edu.xml
+++ b/quickstep/res/layout/predicted_hotseat_edu.xml
@@ -73,6 +73,7 @@
launcher:containerType="hotseat" />
<LinearLayout
+ android:id="@+id/button_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingLeft="@dimen/bottom_sheet_edu_padding"
diff --git a/quickstep/res/values/colors.xml b/quickstep/res/values/colors.xml
index 1bddb57..fb2ee1c 100644
--- a/quickstep/res/values/colors.xml
+++ b/quickstep/res/values/colors.xml
@@ -40,6 +40,7 @@
<color name="gesture_tutorial_fake_previous_task_view_color">#3C4043</color> <!-- Gray -->
<color name="gesture_tutorial_action_button_label_color">#FF000000</color>
<color name="gesture_tutorial_primary_color">#B7F29F</color> <!-- Light Green -->
+ <color name="gesture_tutorial_taskbar_color">#202124</color>
<!-- Mock hotseat -->
<color name="mock_app_icon_1">#8AB4F8</color>
diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml
index fa21b0a..4ebf5cf 100644
--- a/quickstep/res/values/dimens.xml
+++ b/quickstep/res/values/dimens.xml
@@ -121,6 +121,7 @@
<dimen name="gesture_tutorial_foldable_feedback_margin_start_end">140dp</dimen>
<dimen name="gesture_tutorial_multi_row_task_view_spacing">72dp</dimen>
<dimen name="gesture_tutorial_small_task_view_corner_radius">18dp</dimen>
+ <dimen name="gesture_tutorial_mock_taskbar_height">80dp</dimen>
<!-- Gesture Tutorial mock conversations -->
<dimen name="gesture_tutorial_message_icon_size">44dp</dimen>
@@ -155,6 +156,11 @@
<dimen name="gesture_tutorial_webpage_large_line_height">36dp</dimen>
<dimen name="gesture_tutorial_webpage_small_line_height">22dp</dimen>
+ <!-- Gesture Tutorial mock taskbar -->
+ <dimen name="gesture_tutorial_taskbar_icon_size">44dp</dimen>
+ <dimen name="gesture_tutorial_taskbar_icon_corner_radius">100dp</dimen>
+ <dimen name="gesture_tutorial_taskbar_padding_start_end">218dp</dimen>
+
<!-- All Set page -->
<dimen name="allset_page_margin_horizontal">40dp</dimen>
<dimen name="allset_title_margin_top">24dp</dimen>
diff --git a/quickstep/res/values/strings.xml b/quickstep/res/values/strings.xml
index 6af0d60..88c98c0 100644
--- a/quickstep/res/values/strings.xml
+++ b/quickstep/res/values/strings.xml
@@ -193,6 +193,8 @@
<string name="action_split">Split</string>
<!-- Label for toast with instructions for split screen selection mode. [CHAR_LIMIT=50] -->
<string name="toast_split_select_app">Tap another app to use splitscreen</string>
+ <!-- Label for toast when app selected for split isn't supported. [CHAR_LIMIT=50] -->
+ <string name="toast_split_app_unsupported">App does not support split-screen.</string>
<!-- Message shown when an action is blocked by a policy enforced by the app or the organization managing the device. [CHAR_LIMIT=NONE] -->
<string name="blocked_by_policy">This action isn\'t allowed by the app or your organization</string>
diff --git a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java
index c41f2ce..119ae90 100644
--- a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java
+++ b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java
@@ -28,17 +28,20 @@
import android.view.LayoutInflater;
import android.view.View;
import android.widget.Button;
+import android.widget.LinearLayout;
import android.widget.TextView;
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.CellLayout;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Insettable;
+import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.Launcher;
import com.android.launcher3.R;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.model.data.WorkspaceItemInfo;
+import com.android.launcher3.uioverrides.ApiWrapper;
import com.android.launcher3.uioverrides.PredictedAppIcon;
import com.android.launcher3.views.AbstractSlideInView;
@@ -89,8 +92,9 @@
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 @@
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(
diff --git a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionModel.java b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionModel.java
index 080633a..56945ba 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 @@
ArrayList<AppTargetEvent> events = new ArrayList<>();
ArrayList<ItemInfo> 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<AppTarget> 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 @@
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 b665db6..1305bbc 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 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.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.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 @@
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,12 +163,15 @@
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);
}
} 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);
@@ -302,19 +310,4 @@
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 0000000..738dd83
--- /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 55a140d..7794d27 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_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.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.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 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 @@
registerWidgetsPredictor(apm.createAppPredictionSession(
new AppPredictionContext.Builder(context)
.setUiSurface("widgets")
+ .setExtras(getBundleForWidgetsOnWorkspace(context, mDataModel))
.setPredictedTargetCount(NUM_OF_RECOMMENDED_WIDGETS_PREDICATION)
.build()));
}
@@ -306,12 +314,41 @@
}
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<AppTargetEvent> 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;
diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
index 3ab73bb..e8aa2fa 100644
--- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
+++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
@@ -444,6 +444,10 @@
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/src/com/android/quickstep/RecentsAnimationDeviceState.java b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
index e2441ed..73d1424 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
@@ -59,7 +59,6 @@
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 @@
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;
}
diff --git a/quickstep/src/com/android/quickstep/interaction/AnimatedTaskbarView.java b/quickstep/src/com/android/quickstep/interaction/AnimatedTaskbarView.java
new file mode 100644
index 0000000..e8cc45b
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/interaction/AnimatedTaskbarView.java
@@ -0,0 +1,368 @@
+/*
+ * 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.quickstep.interaction;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.ViewGroup;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.constraintlayout.widget.ConstraintLayout;
+
+import com.android.launcher3.R;
+
+import java.util.ArrayList;
+
+
+/**
+ * Helper View for the gesture tutorial mock taskbar view.
+ *
+ * This helper class allows animating this mock taskview to and from a mock hotseat and the bottom
+ * of the screen.
+ */
+public class AnimatedTaskbarView extends ConstraintLayout {
+
+ private View mBackground;
+ private View mIconContainer;
+ private View mIcon1;
+ private View mIcon2;
+ private View mIcon3;
+ private View mIcon4;
+ private View mIcon5;
+ private View mIcon6;
+
+ @Nullable private Animator mRunningAnimator;
+
+ public AnimatedTaskbarView(@NonNull Context context) {
+ super(context);
+ }
+
+ public AnimatedTaskbarView(@NonNull Context context,
+ @Nullable AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public AnimatedTaskbarView(@NonNull Context context, @Nullable AttributeSet attrs,
+ int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ }
+
+ public AnimatedTaskbarView(@NonNull Context context, @Nullable AttributeSet attrs,
+ int defStyleAttr,
+ int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+
+ mBackground = findViewById(R.id.taskbar_background);
+ mIconContainer = findViewById(R.id.icon_container);
+ mIcon1 = findViewById(R.id.taskbar_icon_1);
+ mIcon2 = findViewById(R.id.taskbar_icon_2);
+ mIcon3 = findViewById(R.id.taskbar_icon_3);
+ mIcon4 = findViewById(R.id.taskbar_icon_4);
+ mIcon5 = findViewById(R.id.taskbar_icon_5);
+ mIcon6 = findViewById(R.id.taskbar_icon_6);
+ }
+
+ /**
+ * Animates this fake taskbar's disappearance into the given hotseat view.
+ */
+ public void animateDisappearanceToHotseat(ViewGroup hotseat) {
+ ArrayList<Animator> animators = new ArrayList<>();
+ int hotseatTop = hotseat.getTop();
+
+ animators.add(ObjectAnimator.ofFloat(
+ mBackground, View.TRANSLATION_Y, 0, mBackground.getHeight()));
+ animators.add(ObjectAnimator.ofFloat(mBackground, View.ALPHA, 1f, 0f));
+ animators.add(createIconDisappearanceToHotseatAnimator(
+ mIcon1, hotseat.findViewById(R.id.hotseat_icon_1), hotseatTop));
+ animators.add(createIconDisappearanceToHotseatAnimator(
+ mIcon2, hotseat.findViewById(R.id.hotseat_icon_2), hotseatTop));
+ animators.add(createIconDisappearanceToHotseatAnimator(
+ mIcon3, hotseat.findViewById(R.id.hotseat_icon_3), hotseatTop));
+ animators.add(createIconDisappearanceToHotseatAnimator(
+ mIcon4, hotseat.findViewById(R.id.hotseat_icon_4), hotseatTop));
+ animators.add(createIconDisappearanceToHotseatAnimator(
+ mIcon5, hotseat.findViewById(R.id.hotseat_icon_5), hotseatTop));
+ animators.add(createIconDisappearanceToHotseatAnimator(
+ mIcon6, hotseat.findViewById(R.id.hotseat_icon_6), hotseatTop));
+
+ AnimatorSet animatorSet = new AnimatorSet();
+
+ animatorSet.playTogether(animators);
+ animatorSet.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ super.onAnimationEnd(animation);
+ setVisibility(INVISIBLE);
+ }
+
+ @Override
+ public void onAnimationStart(Animator animation) {
+ super.onAnimationStart(animation);
+ setVisibility(VISIBLE);
+ }
+ });
+
+ start(animatorSet);
+ }
+
+ /**
+ * Animates this fake taskbar's appearance from the given hotseat view.
+ */
+ public void animateAppearanceFromHotseat(ViewGroup hotseat) {
+ ArrayList<Animator> animators = new ArrayList<>();
+ int hotseatTop = hotseat.getTop();
+
+ animators.add(ObjectAnimator.ofFloat(
+ mBackground, View.TRANSLATION_Y, mBackground.getHeight(), 0));
+ animators.add(ObjectAnimator.ofFloat(mBackground, View.ALPHA, 0f, 1f));
+ animators.add(createIconAppearanceFromHotseatAnimator(
+ mIcon1, hotseat.findViewById(R.id.hotseat_icon_1), hotseatTop));
+ animators.add(createIconAppearanceFromHotseatAnimator(
+ mIcon2, hotseat.findViewById(R.id.hotseat_icon_2), hotseatTop));
+ animators.add(createIconAppearanceFromHotseatAnimator(
+ mIcon3, hotseat.findViewById(R.id.hotseat_icon_3), hotseatTop));
+ animators.add(createIconAppearanceFromHotseatAnimator(
+ mIcon4, hotseat.findViewById(R.id.hotseat_icon_4), hotseatTop));
+ animators.add(createIconAppearanceFromHotseatAnimator(
+ mIcon5, hotseat.findViewById(R.id.hotseat_icon_5), hotseatTop));
+ animators.add(createIconAppearanceFromHotseatAnimator(
+ mIcon6, hotseat.findViewById(R.id.hotseat_icon_6), hotseatTop));
+
+ AnimatorSet animatorSet = new AnimatorSet();
+
+ animatorSet.playTogether(animators);
+ animatorSet.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ super.onAnimationStart(animation);
+ setVisibility(VISIBLE);
+ }
+ });
+
+ start(animatorSet);
+ }
+
+ /**
+ * Animates this fake taskbar's disappearance to the bottom of the screen.
+ */
+ public void animateDisappearanceToBottom() {
+ ArrayList<Animator> animators = new ArrayList<>();
+
+ animators.add(ObjectAnimator.ofFloat(
+ mBackground, View.TRANSLATION_Y, 0, mBackground.getHeight()));
+ animators.add(ObjectAnimator.ofFloat(mBackground, View.ALPHA, 1f, 0f));
+ animators.add(ObjectAnimator.ofFloat(mIconContainer, View.SCALE_X, 1f, 0f));
+ animators.add(ObjectAnimator.ofFloat(mIconContainer, View.SCALE_Y, 1f, 0f));
+
+ initializeIconContainerPivot();
+
+ AnimatorSet animatorSet = new AnimatorSet();
+
+ animatorSet.playTogether(animators);
+ animatorSet.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ super.onAnimationEnd(animation);
+ setVisibility(INVISIBLE);
+ resetIconContainerPivot();
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ super.onAnimationCancel(animation);
+ resetIconContainerPivot();
+ }
+
+ @Override
+ public void onAnimationStart(Animator animation) {
+ super.onAnimationStart(animation);
+ setVisibility(VISIBLE);
+ }
+ });
+
+ start(animatorSet);
+ }
+
+ /**
+ * Animates this fake taskbar's appearance from the bottom of the screen.
+ */
+ public void animateAppearanceFromBottom() {
+ ArrayList<Animator> animators = new ArrayList<>();
+
+ animators.add(ObjectAnimator.ofFloat(
+ mBackground, View.TRANSLATION_Y, mBackground.getHeight(), 0));
+ animators.add(ObjectAnimator.ofFloat(mBackground, View.ALPHA, 0f, 1f));
+ animators.add(ObjectAnimator.ofFloat(mIconContainer, View.SCALE_X, 0f, 1f));
+ animators.add(ObjectAnimator.ofFloat(mIconContainer, View.SCALE_Y, 0f, 1f));
+
+ initializeIconContainerPivot();
+
+ AnimatorSet animatorSet = new AnimatorSet();
+
+ animatorSet.playTogether(animators);
+ animatorSet.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ super.onAnimationStart(animation);
+ setVisibility(VISIBLE);
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ super.onAnimationEnd(animation);
+ resetIconContainerPivot();
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ super.onAnimationCancel(animation);
+ resetIconContainerPivot();
+ }
+ });
+
+ start(animatorSet);
+ }
+
+ private void initializeIconContainerPivot() {
+ mIconContainer.setPivotX(getWidth() / 2f);
+ mIconContainer.setPivotY(getHeight() * 0.8f);
+ }
+
+ private void resetIconContainerPivot() {
+ mIconContainer.resetPivot();
+ mIconContainer.setScaleX(1f);
+ mIconContainer.setScaleY(1f);
+ }
+
+ private void start(Animator animator) {
+ if (mRunningAnimator != null) {
+ mRunningAnimator.cancel();
+ }
+
+ animator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ super.onAnimationCancel(animation);
+ mRunningAnimator = null;
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ super.onAnimationEnd(animation);
+ mRunningAnimator = null;
+ }
+
+ @Override
+ public void onAnimationStart(Animator animation) {
+ super.onAnimationStart(animation);
+ mRunningAnimator = animator;
+ }
+ });
+
+ animator.start();
+ }
+
+ private Animator createIconDisappearanceToHotseatAnimator(
+ View taskbarIcon, View hotseatIcon, int hotseatTop) {
+ ArrayList<Animator> animators = new ArrayList<>();
+
+ animators.add(ObjectAnimator.ofFloat(
+ taskbarIcon,
+ View.TRANSLATION_Y,
+ 0,
+ (hotseatTop + hotseatIcon.getTop()) - (getTop() + taskbarIcon.getTop())));
+ animators.add(ObjectAnimator.ofFloat(
+ taskbarIcon, View.TRANSLATION_X, 0, hotseatIcon.getLeft() - taskbarIcon.getLeft()));
+ animators.add(ObjectAnimator.ofFloat(
+ taskbarIcon,
+ View.SCALE_X,
+ 1f,
+ (float) hotseatIcon.getWidth() / (float) taskbarIcon.getWidth()));
+ animators.add(ObjectAnimator.ofFloat(
+ taskbarIcon,
+ View.SCALE_Y,
+ 1f,
+ (float) hotseatIcon.getHeight() / (float) taskbarIcon.getHeight()));
+ animators.add(ObjectAnimator.ofFloat(taskbarIcon, View.ALPHA, 1f, 0f));
+
+ AnimatorSet animatorSet = new AnimatorSet();
+
+ animatorSet.playTogether(animators);
+ animatorSet.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ super.onAnimationEnd(animation);
+ taskbarIcon.setVisibility(INVISIBLE);
+ }
+
+ @Override
+ public void onAnimationStart(Animator animation) {
+ super.onAnimationStart(animation);
+ taskbarIcon.setVisibility(VISIBLE);
+ }
+ });
+
+ return animatorSet;
+ }
+
+ private Animator createIconAppearanceFromHotseatAnimator(
+ View taskbarIcon, View hotseatIcon, int hotseatTop) {
+ ArrayList<Animator> animators = new ArrayList<>();
+
+ animators.add(ObjectAnimator.ofFloat(
+ taskbarIcon,
+ View.TRANSLATION_Y,
+ (hotseatTop + hotseatIcon.getTop()) - (getTop() + taskbarIcon.getTop()),
+ 0));
+ animators.add(ObjectAnimator.ofFloat(
+ taskbarIcon, View.TRANSLATION_X, hotseatIcon.getLeft() - taskbarIcon.getLeft(), 0));
+ animators.add(ObjectAnimator.ofFloat(
+ taskbarIcon,
+ View.SCALE_X,
+ (float) hotseatIcon.getWidth() / (float) taskbarIcon.getWidth(),
+ 1f));
+ animators.add(ObjectAnimator.ofFloat(
+ taskbarIcon,
+ View.SCALE_Y,
+ (float) hotseatIcon.getHeight() / (float) taskbarIcon.getHeight(),
+ 1f));
+ animators.add(ObjectAnimator.ofFloat(taskbarIcon, View.ALPHA, 0f, 1f));
+
+ AnimatorSet animatorSet = new AnimatorSet();
+
+ animatorSet.playTogether(animators);
+ animatorSet.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ super.onAnimationStart(animation);
+ taskbarIcon.setVisibility(VISIBLE);
+ }
+ });
+
+ return animatorSet;
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/interaction/HomeGestureTutorialController.java b/quickstep/src/com/android/quickstep/interaction/HomeGestureTutorialController.java
index a45f273..bbb22e6 100644
--- a/quickstep/src/com/android/quickstep/interaction/HomeGestureTutorialController.java
+++ b/quickstep/src/com/android/quickstep/interaction/HomeGestureTutorialController.java
@@ -95,8 +95,10 @@
showFeedback(R.string.home_gesture_feedback_swipe_too_far_from_edge);
break;
case OVERVIEW_GESTURE_COMPLETED:
- fadeOutFakeTaskView(true, true, () ->
- showFeedback(R.string.home_gesture_feedback_overview_detected));
+ fadeOutFakeTaskView(true, true, () -> {
+ showFeedback(R.string.home_gesture_feedback_overview_detected);
+ showFakeTaskbar(/* animateFromHotseat= */ false);
+ });
break;
case HOME_OR_OVERVIEW_NOT_STARTED_WRONG_SWIPE_DIRECTION:
case HOME_OR_OVERVIEW_CANCELLED:
diff --git a/quickstep/src/com/android/quickstep/interaction/HomeGestureTutorialFragment.java b/quickstep/src/com/android/quickstep/interaction/HomeGestureTutorialFragment.java
index dcae07d..423e66f 100644
--- a/quickstep/src/com/android/quickstep/interaction/HomeGestureTutorialFragment.java
+++ b/quickstep/src/com/android/quickstep/interaction/HomeGestureTutorialFragment.java
@@ -61,7 +61,7 @@
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
- controller.resetFakeTaskView();
+ controller.resetFakeTaskView(true);
}
});
ArrayList<Animator> animators = new ArrayList<>();
@@ -76,7 +76,7 @@
@Override
public void onAnimationCancel(Animator animation) {
super.onAnimationCancel(animation);
- controller.resetFakeTaskView();
+ controller.resetFakeTaskView(true);
}
});
finalAnimation.playSequentially(animators);
diff --git a/quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialController.java b/quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialController.java
index 24ef1fa..0fea0d7 100644
--- a/quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialController.java
+++ b/quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialController.java
@@ -93,8 +93,8 @@
switch (result) {
case HOME_GESTURE_COMPLETED: {
animateFakeTaskViewHome(finalVelocity, () -> {
- resetFakeTaskView();
showFeedback(R.string.overview_gesture_feedback_home_detected);
+ resetFakeTaskView(true);
});
break;
}
@@ -146,6 +146,7 @@
AnimatorSet animset = new AnimatorSet();
animset.playTogether(animators);
+ hideFakeTaskbar(/* animateToHotseat= */ false);
animset.start();
mRunningWindowAnim = SwipeUpAnimationLogic.RunningWindowAnim.wrap(animset);
}
diff --git a/quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialFragment.java b/quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialFragment.java
index 57a76ca..f63a945 100644
--- a/quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialFragment.java
+++ b/quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialFragment.java
@@ -72,7 +72,7 @@
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
- controller.resetFakeTaskView();
+ controller.resetFakeTaskView(false);
}
});
ArrayList<Animator> animators = new ArrayList<>();
@@ -88,7 +88,7 @@
@Override
public void onAnimationCancel(Animator animation) {
super.onAnimationCancel(animation);
- controller.resetFakeTaskView();
+ controller.resetFakeTaskView(false);
}
});
finalAnimation.playSequentially(animators);
diff --git a/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java b/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java
index a923519..68df208 100644
--- a/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java
+++ b/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java
@@ -198,11 +198,12 @@
}
}
AnimatorSet animset = anim.buildAnim();
+ hideFakeTaskbar(/* animateToHotseat= */ false);
animset.start();
mRunningWindowAnim = RunningWindowAnim.wrap(animset);
}
- void resetFakeTaskView() {
+ void resetFakeTaskView(boolean animateFromHome) {
mFakeTaskView.setVisibility(View.VISIBLE);
PendingAnimation anim = new PendingAnimation(300);
anim.setFloat(mTaskViewSwipeUpAnimation
@@ -210,12 +211,14 @@
anim.setViewAlpha(mFakeTaskView, 1, ACCEL);
anim.addListener(mResetTaskView);
AnimatorSet animset = anim.buildAnim();
+ showFakeTaskbar(animateFromHome);
animset.start();
mRunningWindowAnim = RunningWindowAnim.wrap(animset);
}
void animateFakeTaskViewHome(PointF finalVelocity, @Nullable Runnable onEndRunnable) {
cancelRunningAnimation();
+ hideFakeTaskbar(/* animateToHotseat= */ true);
mFakePreviousTaskView.setVisibility(View.INVISIBLE);
mFakeHotseatView.setVisibility(View.VISIBLE);
mShowPreviousTasks = false;
diff --git a/quickstep/src/com/android/quickstep/interaction/TutorialController.java b/quickstep/src/com/android/quickstep/interaction/TutorialController.java
index 9c1ff4d..a59b0d7 100644
--- a/quickstep/src/com/android/quickstep/interaction/TutorialController.java
+++ b/quickstep/src/com/android/quickstep/interaction/TutorialController.java
@@ -89,6 +89,7 @@
@Nullable View mHotseatIconView;
final ClipIconView mFakeIconView;
final FrameLayout mFakeTaskView;
+ final AnimatedTaskbarView mFakeTaskbarView;
final AnimatedTaskView mFakePreviousTaskView;
final View mRippleView;
final RippleDrawable mRippleDrawable;
@@ -104,6 +105,7 @@
private final Runnable mTitleViewCallback;
@Nullable private Runnable mFeedbackViewCallback;
@Nullable private Runnable mFakeTaskViewCallback;
+ @Nullable private Runnable mFakeTaskbarViewCallback;
private final Runnable mShowFeedbackRunnable;
TutorialController(TutorialFragment tutorialFragment, TutorialType tutorialType) {
@@ -122,6 +124,7 @@
mFakeHotseatView = rootView.findViewById(R.id.gesture_tutorial_fake_hotseat_view);
mFakeIconView = rootView.findViewById(R.id.gesture_tutorial_fake_icon_view);
mFakeTaskView = rootView.findViewById(R.id.gesture_tutorial_fake_task_view);
+ mFakeTaskbarView = rootView.findViewById(R.id.gesture_tutorial_fake_taskbar_view);
mFakePreviousTaskView =
rootView.findViewById(R.id.gesture_tutorial_fake_previous_task_view);
mRippleView = rootView.findViewById(R.id.gesture_tutorial_ripple_view);
@@ -319,6 +322,10 @@
mFakeTaskView.removeCallbacks(mFakeTaskViewCallback);
mFakeTaskViewCallback = null;
}
+ if (mFakeTaskbarViewCallback != null) {
+ mFakeTaskbarView.removeCallbacks(mFakeTaskbarViewCallback);
+ mFakeTaskbarViewCallback = null;
+ }
mFeedbackTitleView.removeCallbacks(mTitleViewCallback);
}
@@ -429,6 +436,38 @@
mActionButton.setOnClickListener(this::onActionButtonClicked);
}
+ void hideFakeTaskbar(boolean animateToHotseat) {
+ if (!mTutorialFragment.isLargeScreen()) {
+ return;
+ }
+ if (mFakeTaskbarViewCallback != null) {
+ mFakeTaskbarView.removeCallbacks(mFakeTaskbarViewCallback);
+ }
+ if (animateToHotseat) {
+ mFakeTaskbarViewCallback = () ->
+ mFakeTaskbarView.animateDisappearanceToHotseat(mFakeHotseatView);
+ } else {
+ mFakeTaskbarViewCallback = mFakeTaskbarView::animateDisappearanceToBottom;
+ }
+ mFakeTaskbarView.post(mFakeTaskbarViewCallback);
+ }
+
+ void showFakeTaskbar(boolean animateFromHotseat) {
+ if (!mTutorialFragment.isLargeScreen()) {
+ return;
+ }
+ if (mFakeTaskbarViewCallback != null) {
+ mFakeTaskbarView.removeCallbacks(mFakeTaskbarViewCallback);
+ }
+ if (animateFromHotseat) {
+ mFakeTaskbarViewCallback = () ->
+ mFakeTaskbarView.animateAppearanceFromHotseat(mFakeHotseatView);
+ } else {
+ mFakeTaskbarViewCallback = mFakeTaskbarView::animateAppearanceFromBottom;
+ }
+ mFakeTaskbarView.post(mFakeTaskbarViewCallback);
+ }
+
void updateFakeAppTaskViewLayout(@LayoutRes int mockAppTaskLayoutResId) {
updateFakeViewLayout(mFakeTaskView, mockAppTaskLayoutResId);
}
@@ -480,6 +519,8 @@
mTutorialFragment.isLargeScreen()
? R.dimen.gesture_tutorial_foldable_feedback_margin_start_end
: R.dimen.gesture_tutorial_feedback_margin_start_end));
+
+ mFakeTaskbarView.setVisibility(mTutorialFragment.isLargeScreen() ? View.VISIBLE : GONE);
}
}
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index 5a455c1..687a13a 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -599,6 +599,8 @@
private SplitConfigurationOptions.StagedSplitBounds mSplitBoundsConfig;
private final Toast mSplitToast = Toast.makeText(getContext(),
R.string.toast_split_select_app, Toast.LENGTH_SHORT);
+ private final Toast mSplitUnsupportedToast = Toast.makeText(getContext(),
+ R.string.toast_split_app_unsupported, Toast.LENGTH_SHORT);
/**
* Keeps track of the index of the TaskView that split screen was initialized with so we know
@@ -3878,6 +3880,11 @@
public void confirmSplitSelect(TaskView taskView) {
mSplitToast.cancel();
+ if (!taskView.getTask().isDockable) {
+ // Task not split screen supported
+ mSplitUnsupportedToast.show();
+ return;
+ }
RectF secondTaskStartingBounds = new RectF();
Rect secondTaskEndingBounds = new Rect();
// TODO(194414938) starting bounds seem slightly off, investigate
@@ -3920,6 +3927,7 @@
int duration = mActivity.getStateManager().getState().getTransitionDuration(getContext());
PendingAnimation pendingAnim = new PendingAnimation(duration);
mSplitToast.cancel();
+ mSplitUnsupportedToast.cancel();
if (!animate) {
resetFromSplitSelectionState();
return pendingAnim;
diff --git a/quickstep/tests/src/com/android/quickstep/AbstractQuickStepTest.java b/quickstep/tests/src/com/android/quickstep/AbstractQuickStepTest.java
index ca47de3..189dff8 100644
--- a/quickstep/tests/src/com/android/quickstep/AbstractQuickStepTest.java
+++ b/quickstep/tests/src/com/android/quickstep/AbstractQuickStepTest.java
@@ -44,7 +44,7 @@
protected void onLauncherActivityClose(Launcher launcher) {
RecentsView recentsView = launcher.getOverviewPanel();
if (recentsView != null) {
- recentsView.finishRecentsAnimation(true, null);
+ recentsView.finishRecentsAnimation(false /* toRecents */, null);
}
}
diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
index 710afe0..4895b10 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
@@ -43,7 +43,6 @@
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 @@
@Test
@PortraitLandscape
- @Ignore("b/203781041")
public void testOverviewForTablet() throws Exception {
if (!mLauncher.isTablet()) {
return;
diff --git a/src/com/android/launcher3/DropTargetBar.java b/src/com/android/launcher3/DropTargetBar.java
index eb42a65..9fb14f6 100644
--- a/src/com/android/launcher3/DropTargetBar.java
+++ b/src/com/android/launcher3/DropTargetBar.java
@@ -275,8 +275,12 @@
@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());
+ }
}
}
}
diff --git a/src/com/android/launcher3/views/OptionsPopupView.java b/src/com/android/launcher3/views/OptionsPopupView.java
index 33ab0d2..9774c46 100644
--- a/src/com/android/launcher3/views/OptionsPopupView.java
+++ b/src/com/android/launcher3/views/OptionsPopupView.java
@@ -40,6 +40,7 @@
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 @@
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 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 OptionItem(CharSequence label, Drawable icon, EventEnum eventId,
- OnLongClickListener clickListener) {
+ OnLongClickListener clickListener) {
this.labelRes = 0;
this.label = label;
this.icon = icon;
diff --git a/tests/tapl/com/android/launcher3/tapl/BaseOverview.java b/tests/tapl/com/android/launcher3/tapl/BaseOverview.java
index 0e3b501..d5479fb 100644
--- a/tests/tapl/com/android/launcher3/tapl/BaseOverview.java
+++ b/tests/tapl/com/android/launcher3/tapl/BaseOverview.java
@@ -54,18 +54,14 @@
}
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 @@
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 9ee0e25..7ffdf4c 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -1190,10 +1190,19 @@
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 @@
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 15bddd7..a860e7d 100644
--- a/tests/tapl/com/android/launcher3/tapl/OverviewTask.java
+++ b/tests/tapl/com/android/launcher3/tapl/OverviewTask.java
@@ -53,6 +53,10 @@
return mTask.getVisibleBounds().height();
}
+ int getVisibleWidth() {
+ return mTask.getVisibleBounds().width();
+ }
+
int getTaskCenterX() {
return mTask.getVisibleCenter().x;
}