Merge "Default fixed_rotation to off" into ub-launcher3-master
diff --git a/quickstep/recents_ui_overrides/res/layout/arrow_toast.xml b/quickstep/recents_ui_overrides/res/layout/arrow_toast.xml
index b0f2b4b..980bb5a 100644
--- a/quickstep/recents_ui_overrides/res/layout/arrow_toast.xml
+++ b/quickstep/recents_ui_overrides/res/layout/arrow_toast.xml
@@ -34,6 +34,7 @@
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
+ android:gravity="center"
android:layout_gravity="center_vertical"
android:textColor="@android:color/white"
android:textSize="16sp"/>
diff --git a/quickstep/recents_ui_overrides/res/values/config.xml b/quickstep/recents_ui_overrides/res/values/config.xml
index 527eec6..120e034 100644
--- a/quickstep/recents_ui_overrides/res/values/config.xml
+++ b/quickstep/recents_ui_overrides/res/values/config.xml
@@ -14,8 +14,5 @@
limitations under the License.
-->
<resources>
- <integer name="app_background_blur_radius">150</integer>
- <integer name="allapps_background_blur_radius">90</integer>
- <integer name="overview_background_blur_radius">50</integer>
- <integer name="folder_background_blur_radius_adjustment">20</integer>
+ <integer name="max_depth_blur_radius">150</integer>
</resources>
\ No newline at end of file
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/ArrowTipView.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/ArrowTipView.java
new file mode 100644
index 0000000..a5ea523
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/ArrowTipView.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2008 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;
+
+import android.content.Context;
+import android.graphics.CornerPathEffect;
+import android.graphics.Paint;
+import android.graphics.drawable.ShapeDrawable;
+import android.os.Handler;
+import android.util.TypedValue;
+import android.view.Gravity;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import androidx.core.content.ContextCompat;
+
+import com.android.launcher3.anim.Interpolators;
+import com.android.launcher3.dragndrop.DragLayer;
+import com.android.launcher3.graphics.TriangleShape;
+
+/**
+ * A base class for arrow tip view in launcher
+ */
+public class ArrowTipView extends AbstractFloatingView {
+
+ private static final long AUTO_CLOSE_TIMEOUT_MILLIS = 10 * 1000;
+ private static final long SHOW_DELAY_MS = 200;
+ private static final long SHOW_DURATION_MS = 300;
+ private static final long HIDE_DURATION_MS = 100;
+
+ protected final Launcher mLauncher;
+ private final Handler mHandler = new Handler();
+ private Runnable mOnClosed;
+
+ public ArrowTipView(Context context) {
+ super(context, null, 0);
+ mLauncher = Launcher.getLauncher(context);
+ init(context);
+ }
+
+ @Override
+ public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
+ if (ev.getAction() == MotionEvent.ACTION_DOWN) {
+ close(true);
+ }
+ return false;
+ }
+
+ @Override
+ protected void handleClose(boolean animate) {
+ if (mIsOpen) {
+ if (animate) {
+ animate().alpha(0f)
+ .withLayer()
+ .setStartDelay(0)
+ .setDuration(HIDE_DURATION_MS)
+ .setInterpolator(Interpolators.ACCEL)
+ .withEndAction(() -> mLauncher.getDragLayer().removeView(this))
+ .start();
+ } else {
+ animate().cancel();
+ mLauncher.getDragLayer().removeView(this);
+ }
+ if (mOnClosed != null) mOnClosed.run();
+ mIsOpen = false;
+ }
+ }
+
+ @Override
+ public void logActionCommand(int command) {
+ }
+
+ @Override
+ protected boolean isOfType(int type) {
+ return (type & TYPE_ON_BOARD_POPUP) != 0;
+ }
+
+ private void init(Context context) {
+ inflate(context, R.layout.arrow_toast, this);
+ setOrientation(LinearLayout.VERTICAL);
+ View dismissButton = findViewById(R.id.dismiss);
+ dismissButton.setOnClickListener(view -> {
+ handleClose(true);
+ });
+
+ View arrowView = findViewById(R.id.arrow);
+ ViewGroup.LayoutParams arrowLp = arrowView.getLayoutParams();
+ ShapeDrawable arrowDrawable = new ShapeDrawable(TriangleShape.create(
+ arrowLp.width, arrowLp.height, false));
+ Paint arrowPaint = arrowDrawable.getPaint();
+ TypedValue typedValue = new TypedValue();
+ context.getTheme().resolveAttribute(android.R.attr.colorAccent, typedValue, true);
+ arrowPaint.setColor(ContextCompat.getColor(getContext(), typedValue.resourceId));
+ // The corner path effect won't be reflected in the shadow, but shouldn't be noticeable.
+ arrowPaint.setPathEffect(new CornerPathEffect(
+ context.getResources().getDimension(R.dimen.arrow_toast_corner_radius)));
+ arrowView.setBackground(arrowDrawable);
+
+ mIsOpen = true;
+
+ mHandler.postDelayed(() -> handleClose(true), AUTO_CLOSE_TIMEOUT_MILLIS);
+ }
+
+ /**
+ * Show Tip with specified string and Y location
+ */
+ public ArrowTipView show(String text, int top) {
+ ((TextView) findViewById(R.id.text)).setText(text);
+ mLauncher.getDragLayer().addView(this);
+
+ DragLayer.LayoutParams params = (DragLayer.LayoutParams) getLayoutParams();
+ params.gravity = Gravity.CENTER_HORIZONTAL;
+ params.leftMargin = mLauncher.getDeviceProfile().workspacePadding.left;
+ params.rightMargin = mLauncher.getDeviceProfile().workspacePadding.right;
+ post(() -> setY(top - getHeight()));
+ setAlpha(0);
+ animate()
+ .alpha(1f)
+ .withLayer()
+ .setStartDelay(SHOW_DELAY_MS)
+ .setDuration(SHOW_DURATION_MS)
+ .setInterpolator(Interpolators.DEACCEL)
+ .start();
+ return this;
+ }
+
+ /**
+ * Register a callback fired when toast is hidden
+ */
+ public ArrowTipView setOnClosedCallback(Runnable runnable) {
+ mOnClosed = runnable;
+ return this;
+ }
+}
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
index 95198b8..0019ecb 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
@@ -20,15 +20,14 @@
import static com.android.launcher3.LauncherState.HOTSEAT_ICONS;
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.LauncherState.OVERVIEW;
-import static com.android.launcher3.LauncherStateManager.ANIM_ALL_COMPONENTS;
-import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_ALL_APPS_FADE;
-import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_HOTSEAT_SCALE;
-import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_HOTSEAT_TRANSLATE;
-import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_VERTICAL_PROGRESS;
import static com.android.launcher3.anim.Interpolators.AGGRESSIVE_EASE;
import static com.android.launcher3.anim.Interpolators.DEACCEL_3;
import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.anim.Interpolators.OVERSHOOT_1_2;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_APPS_FADE;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_HOTSEAT_SCALE;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_HOTSEAT_TRANSLATE;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_VERTICAL_PROGRESS;
import static com.android.quickstep.TaskViewUtils.findTaskViewToLaunch;
import static com.android.quickstep.TaskViewUtils.getRecentsWindowAnimator;
@@ -47,9 +46,9 @@
import com.android.launcher3.LauncherState.ScaleAndTranslation;
import com.android.launcher3.allapps.AllAppsTransitionController;
import com.android.launcher3.anim.AnimatorPlaybackController;
-import com.android.launcher3.anim.AnimatorSetBuilder;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.anim.SpringAnimationBuilder;
+import com.android.launcher3.states.StateAnimationConfig;
import com.android.launcher3.touch.PagedOrientationHandler;
import com.android.quickstep.util.AppWindowAnimationHelper;
import com.android.quickstep.views.RecentsView;
@@ -92,7 +91,7 @@
AppWindowAnimationHelper helper =
new AppWindowAnimationHelper(recentsView.getPagedViewOrientedState(), mLauncher);
Animator recentsAnimator = getRecentsWindowAnimator(taskView, skipLauncherChanges,
- appTargets, wallpaperTargets, mLauncher.getBackgroundBlurController(), helper);
+ appTargets, wallpaperTargets, mLauncher.getDepthController(), helper);
anim.play(recentsAnimator.setDuration(RECENTS_LAUNCH_DURATION));
Animator childStateAnimation = null;
@@ -210,17 +209,20 @@
.setValues(values)
.build(mLauncher);
case INDEX_PAUSE_TO_OVERVIEW_ANIM: {
- AnimatorSetBuilder builder = new AnimatorSetBuilder();
- builder.setInterpolator(ANIM_VERTICAL_PROGRESS, OVERSHOOT_1_2);
- builder.setInterpolator(ANIM_ALL_APPS_FADE, DEACCEL_3);
+ StateAnimationConfig config = new StateAnimationConfig();
+ config.duration = ATOMIC_DURATION_FROM_PAUSED_TO_OVERVIEW;
+
+ config.setInterpolator(ANIM_VERTICAL_PROGRESS, OVERSHOOT_1_2);
+ config.setInterpolator(ANIM_ALL_APPS_FADE, DEACCEL_3);
if ((OVERVIEW.getVisibleElements(mLauncher) & HOTSEAT_ICONS) != 0) {
- builder.setInterpolator(ANIM_HOTSEAT_SCALE, OVERSHOOT_1_2);
- builder.setInterpolator(ANIM_HOTSEAT_TRANSLATE, OVERSHOOT_1_2);
+ config.setInterpolator(ANIM_HOTSEAT_SCALE, OVERSHOOT_1_2);
+ config.setInterpolator(ANIM_HOTSEAT_TRANSLATE, OVERSHOOT_1_2);
}
+
+
LauncherStateManager stateManager = mLauncher.getStateManager();
return stateManager.createAtomicAnimation(
- stateManager.getCurrentStableState(), OVERVIEW, builder,
- ANIM_ALL_COMPONENTS, ATOMIC_DURATION_FROM_PAUSED_TO_OVERVIEW);
+ stateManager.getCurrentStableState(), OVERVIEW, config);
}
default:
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/AllAppsTipView.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/AllAppsTipView.java
index 0ae7435..b3bb850 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/AllAppsTipView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/AllAppsTipView.java
@@ -16,133 +16,30 @@
package com.android.launcher3.appprediction;
+import static com.android.launcher3.AbstractFloatingView.TYPE_DISCOVERY_BOUNCE;
+import static com.android.launcher3.AbstractFloatingView.TYPE_ON_BOARD_POPUP;
import static com.android.launcher3.LauncherState.ALL_APPS;
import static com.android.quickstep.logging.UserEventDispatcherExtension.ALL_APPS_PREDICTION_TIPS;
-import android.content.Context;
-import android.graphics.CornerPathEffect;
-import android.graphics.Paint;
-import android.graphics.drawable.ShapeDrawable;
-import android.os.Handler;
import android.os.UserManager;
-import android.util.AttributeSet;
-import android.util.TypedValue;
-import android.view.Gravity;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-
-import androidx.core.content.ContextCompat;
import com.android.launcher3.AbstractFloatingView;
+import com.android.launcher3.ArrowTipView;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
import com.android.launcher3.LauncherStateManager;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.allapps.FloatingHeaderView;
-import com.android.launcher3.anim.Interpolators;
-import com.android.launcher3.dragndrop.DragLayer;
-import com.android.launcher3.graphics.TriangleShape;
import com.android.systemui.shared.system.LauncherEventUtil;
/**
- * All apps tip view aligned just above prediction apps, shown to users that enter all apps for the
+ * ArrowTip helper aligned just above prediction apps, shown to users that enter all apps for the
* first time.
*/
-public class AllAppsTipView extends AbstractFloatingView {
+public class AllAppsTipView {
private static final String ALL_APPS_TIP_SEEN = "launcher.all_apps_tip_seen";
- private static final long AUTO_CLOSE_TIMEOUT_MILLIS = 10 * 1000;
- private static final long SHOW_DELAY_MS = 200;
- private static final long SHOW_DURATION_MS = 300;
- private static final long HIDE_DURATION_MS = 100;
-
- private final Launcher mLauncher;
- private final Handler mHandler = new Handler();
-
- private AllAppsTipView(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- private AllAppsTipView(Context context, AttributeSet attrs, int defStyleAttr) {
- super(context, attrs, defStyleAttr);
- setOrientation(LinearLayout.VERTICAL);
-
- mLauncher = Launcher.getLauncher(context);
-
- init(context);
- }
-
- @Override
- public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
- if (ev.getAction() == MotionEvent.ACTION_DOWN) {
- close(true);
- }
- return false;
- }
-
- @Override
- protected void handleClose(boolean animate) {
- if (mIsOpen) {
- if (animate) {
- animate().alpha(0f)
- .withLayer()
- .setStartDelay(0)
- .setDuration(HIDE_DURATION_MS)
- .setInterpolator(Interpolators.ACCEL)
- .withEndAction(() -> mLauncher.getDragLayer().removeView(this))
- .start();
- } else {
- animate().cancel();
- mLauncher.getDragLayer().removeView(this);
- }
- mLauncher.getSharedPrefs().edit().putBoolean(ALL_APPS_TIP_SEEN, true).apply();
- mIsOpen = false;
- }
- }
-
- @Override
- public void logActionCommand(int command) {
- }
-
- @Override
- protected boolean isOfType(int type) {
- return (type & TYPE_ON_BOARD_POPUP) != 0;
- }
-
- private void init(Context context) {
- inflate(context, R.layout.arrow_toast, this);
-
- TextView textView = findViewById(R.id.text);
- textView.setText(R.string.all_apps_prediction_tip);
-
- View dismissButton = findViewById(R.id.dismiss);
- dismissButton.setOnClickListener(view -> {
- mLauncher.getUserEventDispatcher().logActionTip(
- LauncherEventUtil.DISMISS, ALL_APPS_PREDICTION_TIPS);
- handleClose(true);
- });
-
- View arrowView = findViewById(R.id.arrow);
- ViewGroup.LayoutParams arrowLp = arrowView.getLayoutParams();
- ShapeDrawable arrowDrawable = new ShapeDrawable(TriangleShape.create(
- arrowLp.width, arrowLp.height, false));
- Paint arrowPaint = arrowDrawable.getPaint();
- TypedValue typedValue = new TypedValue();
- context.getTheme().resolveAttribute(android.R.attr.colorAccent, typedValue, true);
- arrowPaint.setColor(ContextCompat.getColor(getContext(), typedValue.resourceId));
- // The corner path effect won't be reflected in the shadow, but shouldn't be noticeable.
- arrowPaint.setPathEffect(new CornerPathEffect(
- context.getResources().getDimension(R.dimen.arrow_toast_corner_radius)));
- arrowView.setBackground(arrowDrawable);
-
- mIsOpen = true;
-
- mHandler.postDelayed(() -> handleClose(true), AUTO_CLOSE_TIMEOUT_MILLIS);
- }
private static boolean showAllAppsTipIfNecessary(Launcher launcher) {
FloatingHeaderView floatingHeaderView = launcher.getAppsView().getFloatingHeaderView();
@@ -156,28 +53,15 @@
return false;
}
- AllAppsTipView allAppsTipView = new AllAppsTipView(launcher.getAppsView().getContext(),
- null);
- launcher.getDragLayer().addView(allAppsTipView);
+ int[] coords = new int[2];
+ floatingHeaderView.findFixedRowByType(PredictionRowView.class).getLocationOnScreen(coords);
+ ArrowTipView arrowTipView = new ArrowTipView(launcher).setOnClosedCallback(() -> {
+ launcher.getSharedPrefs().edit().putBoolean(ALL_APPS_TIP_SEEN, true).apply();
+ launcher.getUserEventDispatcher().logActionTip(LauncherEventUtil.DISMISS,
+ ALL_APPS_PREDICTION_TIPS);
+ });
+ arrowTipView.show(launcher.getString(R.string.all_apps_prediction_tip), coords[1]);
- DragLayer.LayoutParams params = (DragLayer.LayoutParams) allAppsTipView.getLayoutParams();
- params.gravity = Gravity.CENTER_HORIZONTAL;
-
- int top = floatingHeaderView.findFixedRowByType(PredictionRowView.class).getTop();
- allAppsTipView.setY(top - launcher.getResources().getDimensionPixelSize(
- R.dimen.all_apps_tip_bottom_margin));
-
- allAppsTipView.setAlpha(0);
- allAppsTipView.animate()
- .alpha(1f)
- .withLayer()
- .setStartDelay(SHOW_DELAY_MS)
- .setDuration(SHOW_DURATION_MS)
- .setInterpolator(Interpolators.DEACCEL)
- .start();
-
- launcher.getUserEventDispatcher().logActionTip(
- LauncherEventUtil.VISIBLE, ALL_APPS_PREDICTION_TIPS);
return true;
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduController.java
index a07cd1d..da58817 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduController.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduController.java
@@ -23,23 +23,28 @@
import android.content.res.Configuration;
import android.os.Build;
import android.view.View;
-import android.view.ViewGroup;
import androidx.core.app.NotificationCompat;
import com.android.launcher3.CellLayout;
+import com.android.launcher3.FolderInfo;
+import com.android.launcher3.Hotseat;
+import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.R;
import com.android.launcher3.Workspace;
import com.android.launcher3.WorkspaceItemInfo;
-import com.android.launcher3.WorkspaceLayoutManager;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.uioverrides.QuickstepLauncher;
import com.android.launcher3.util.ActivityTracker;
+import com.android.launcher3.util.GridOccupancy;
+import com.android.launcher3.util.IntArray;
import com.android.launcher3.util.Themes;
+import java.util.ArrayDeque;
+import java.util.ArrayList;
import java.util.List;
/**
@@ -52,48 +57,179 @@
private static final int ONBOARDING_NOTIFICATION_ID = 7641;
private final Launcher mLauncher;
+ private final NotificationManager mNotificationManager;
+ private final Notification mNotification;
private List<WorkspaceItemInfo> mPredictedApps;
private HotseatEduDialog mActiveDialog;
- private final NotificationManager mNotificationManager;
- private final Notification mNotification;
+ private ArrayList<ItemInfo> mNewItems = new ArrayList<>();
+ private IntArray mNewScreens = null;
+ private Runnable mOnOnboardingComplete;
- HotseatEduController(Launcher launcher) {
+ HotseatEduController(Launcher launcher, Runnable runnable) {
mLauncher = launcher;
+ mOnOnboardingComplete = runnable;
mNotificationManager = mLauncher.getSystemService(NotificationManager.class);
createNotificationChannel();
mNotification = createNotification();
}
- boolean migrate() {
- Workspace workspace = mLauncher.getWorkspace();
- CellLayout firstScreen = workspace.getScreenWithId(WorkspaceLayoutManager.FIRST_SCREEN_ID);
- int toPage = Workspace.FIRST_SCREEN_ID;
- int toRow = mLauncher.getDeviceProfile().inv.numRows - 1;
- if (FeatureFlags.HOTSEAT_MIGRATE_NEW_PAGE.get()) {
- toPage = workspace.getScreenIdForPageIndex(workspace.getPageCount());
- toRow = 0;
- } else if (!firstScreen.makeSpaceForHotseatMigration(true)) {
- return false;
+ /**
+ * Checks what type of migration should be used and migrates hotseat
+ */
+ void migrate() {
+ if (FeatureFlags.HOTSEAT_MIGRATE_TO_FOLDER.get()) {
+ migrateToFolder();
+ } else {
+ migrateHotseatWhole();
}
- ViewGroup hotseatVG = mLauncher.getHotseat().getShortcutsAndWidgets();
- for (int i = 0; i < hotseatVG.getChildCount(); i++) {
- View child = hotseatVG.getChildAt(i);
+ }
+
+ /**
+ * This migration places all non folder items in the hotseat into a folder and then moves
+ * all folders in the hotseat to a workspace page that has enough empty spots.
+ *
+ * @return pageId that has accepted the items.
+ */
+ private int migrateToFolder() {
+ ArrayDeque<FolderInfo> folders = new ArrayDeque<>();
+
+ ArrayList<WorkspaceItemInfo> putIntoFolder = new ArrayList<>();
+
+ //separate folders and items that can get in folders
+ for (int i = 0; i < mLauncher.getDeviceProfile().inv.numHotseatIcons; i++) {
+ View view = mLauncher.getHotseat().getChildAt(i, 0);
+ if (view == null) continue;
+ ItemInfo info = (ItemInfo) view.getTag();
+ if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_FOLDER) {
+ folders.add((FolderInfo) info);
+ } else if (info instanceof WorkspaceItemInfo) {
+ putIntoFolder.add((WorkspaceItemInfo) info);
+ }
+ }
+
+ // create a temp folder and add non folder items to it
+ if (!putIntoFolder.isEmpty()) {
+ ItemInfo firstItem = putIntoFolder.get(0);
+ FolderInfo folderInfo = new FolderInfo();
+ folderInfo.title = "";
+ mLauncher.getModelWriter().addItemToDatabase(folderInfo, firstItem.container,
+ firstItem.screenId, firstItem.cellX, firstItem.cellY);
+ folderInfo.contents.addAll(putIntoFolder);
+ for (int i = 0; i < folderInfo.contents.size(); i++) {
+ ItemInfo item = folderInfo.contents.get(i);
+ item.rank = i;
+ mLauncher.getModelWriter().moveItemInDatabase(item, folderInfo.id, 0,
+ item.cellX, item.cellY);
+ }
+ folders.add(folderInfo);
+ }
+ mNewItems.addAll(folders);
+
+ return placeFoldersInWorkspace(folders);
+ }
+
+ private int placeFoldersInWorkspace(ArrayDeque<FolderInfo> folders) {
+ if (folders.isEmpty()) return 0;
+
+ Workspace workspace = mLauncher.getWorkspace();
+ InvariantDeviceProfile idp = mLauncher.getDeviceProfile().inv;
+
+ GridOccupancy[] occupancyList = new GridOccupancy[workspace.getChildCount()];
+ for (int i = 0; i < occupancyList.length; i++) {
+ occupancyList[i] = ((CellLayout) workspace.getChildAt(i)).cloneGridOccupancy();
+ }
+ //scan every screen to find available spots to place folders
+ int occupancyIndex = 0;
+ int[] itemXY = new int[2];
+ while (occupancyIndex < occupancyList.length && !folders.isEmpty()) {
+ GridOccupancy occupancy = occupancyList[occupancyIndex];
+ if (occupancy.findVacantCell(itemXY, 1, 1)) {
+ FolderInfo info = folders.poll();
+ mLauncher.getModelWriter().moveItemInDatabase(info,
+ LauncherSettings.Favorites.CONTAINER_DESKTOP,
+ workspace.getScreenIdForPageIndex(occupancyIndex), itemXY[0], itemXY[1]);
+ occupancy.markCells(info, true);
+ } else {
+ occupancyIndex++;
+ }
+ }
+ if (folders.isEmpty()) return workspace.getScreenIdForPageIndex(occupancyIndex);
+ int screenId = LauncherSettings.Settings.call(mLauncher.getContentResolver(),
+ LauncherSettings.Settings.METHOD_NEW_SCREEN_ID)
+ .getInt(LauncherSettings.Settings.EXTRA_VALUE);
+ // if all screens are full and we still have folders left, put those on a new page
+ FolderInfo folderInfo;
+ int col = 0;
+ while ((folderInfo = folders.poll()) != null) {
+ mLauncher.getModelWriter().moveItemInDatabase(folderInfo,
+ LauncherSettings.Favorites.CONTAINER_DESKTOP, screenId, col++,
+ idp.numRows - 1);
+ }
+ mNewScreens = IntArray.wrap(screenId);
+ return workspace.getPageCount();
+ }
+
+ /**
+ * This migration option attempts to move the entire hotseat up to the first workspace that
+ * has space to host items. If no such page is found, it moves items to a new page.
+ *
+ * @return pageId where items are migrated
+ */
+ private int migrateHotseatWhole() {
+ Workspace workspace = mLauncher.getWorkspace();
+ Hotseat hotseatVG = mLauncher.getHotseat();
+
+ int pageId = -1;
+ int toRow = 0;
+ for (int i = 0; i < workspace.getPageCount(); i++) {
+ CellLayout target = workspace.getScreenWithId(workspace.getScreenIdForPageIndex(i));
+ if (target.makeSpaceForHotseatMigration(true)) {
+ toRow = mLauncher.getDeviceProfile().inv.numRows - 1;
+ pageId = i;
+ break;
+ }
+ }
+ if (pageId == -1) {
+ pageId = LauncherSettings.Settings.call(mLauncher.getContentResolver(),
+ LauncherSettings.Settings.METHOD_NEW_SCREEN_ID)
+ .getInt(LauncherSettings.Settings.EXTRA_VALUE);
+ }
+ for (int i = 0; i < mLauncher.getDeviceProfile().inv.numHotseatIcons; i++) {
+ View child = hotseatVG.getChildAt(i, 0);
+ if (child == null || child.getTag() == null) continue;
ItemInfo tag = (ItemInfo) child.getTag();
mLauncher.getModelWriter().moveItemInDatabase(tag,
- LauncherSettings.Favorites.CONTAINER_DESKTOP, toPage, tag.screenId, toRow);
+ LauncherSettings.Favorites.CONTAINER_DESKTOP, pageId, i, toRow);
+ mNewItems.add(tag);
}
- return true;
+ return pageId;
}
+
void removeNotification() {
mNotificationManager.cancel(ONBOARDING_NOTIFICATION_ID);
}
void finishOnboarding() {
- mLauncher.getModel().rebindCallbacks();
+ mLauncher.getHotseat().removeAllViewsInLayout();
+ if (!mNewItems.isEmpty()) {
+ int lastPage = mNewItems.get(mNewItems.size() - 1).screenId;
+ ArrayList<ItemInfo> animated = new ArrayList<>();
+ ArrayList<ItemInfo> nonAnimated = new ArrayList<>();
+
+ for (ItemInfo info : mNewItems) {
+ if (info.screenId == lastPage) {
+ animated.add(info);
+ } else {
+ nonAnimated.add(info);
+ }
+ }
+ mLauncher.bindAppsAdded(mNewScreens, nonAnimated, animated);
+ }
+ mOnOnboardingComplete.run();
+ destroy();
mLauncher.getSharedPrefs().edit().putBoolean(KEY_HOTSEAT_EDU_SEEN, true).apply();
- removeNotification();
}
void setPredictedApps(List<WorkspaceItemInfo> predictedApps) {
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java
index 538b7f3..bcce168 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java
@@ -29,13 +29,13 @@
import android.widget.TextView;
import android.widget.Toast;
+import com.android.launcher3.ArrowTipView;
import com.android.launcher3.CellLayout;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Insettable;
import com.android.launcher3.Launcher;
import com.android.launcher3.R;
import com.android.launcher3.WorkspaceItemInfo;
-import com.android.launcher3.WorkspaceLayoutManager;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.logging.UserEventDispatcher;
@@ -54,23 +54,15 @@
private static final int DEFAULT_CLOSE_DURATION = 200;
protected static final int FINAL_SCRIM_BG_COLOR = 0x88000000;
- // We don't migrate if user has more than SAME_PAGE_MAX_ROWS rows of item in their screen
- private static final int SAME_PAGE_MAX_ROWS = 2;
- private static final int MIGRATE_SAME_PAGE = 0;
- private static final int MIGRATE_NEW_PAGE = 1;
- private static final int MIGRATE_NO_MIGRATE = 2;
-
+ // we use this value to keep track of migration logs as we experiment with different migrations
+ private static final int MIGRATION_EXPERIMENT_IDENTIFIER = 1;
private final Rect mInsets = new Rect();
private View mHotseatWrapper;
private CellLayout mSampleHotseat;
- private TextView mEduHeading;
- private TextView mEduContent;
private Button mDismissBtn;
- private int mMigrationMode = MIGRATE_SAME_PAGE;
-
public void setHotseatEduController(HotseatEduController hotseatEduController) {
mHotseatEduController = hotseatEduController;
}
@@ -93,8 +85,6 @@
super.onFinishInflate();
mHotseatWrapper = findViewById(R.id.hotseat_wrapper);
mSampleHotseat = findViewById(R.id.sample_prediction);
- mEduHeading = findViewById(R.id.hotseat_edu_heading);
- mEduContent = findViewById(R.id.hotseat_edu_content);
DeviceProfile grid = mLauncher.getDeviceProfile();
Rect padding = grid.getHotseatLayoutPadding();
@@ -109,25 +99,30 @@
mDismissBtn = findViewById(R.id.no_thanks);
mDismissBtn.setOnClickListener(this::onDismiss);
+ // 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(
+ R.string.hotseat_edu_message_migrate_alt);
+ }
}
private void onAccept(View v) {
- if (mMigrationMode == MIGRATE_NO_MIGRATE || !mHotseatEduController.migrate()) {
- onDismiss(v);
- return;
- }
+ mHotseatEduController.migrate();
handleClose(true);
mHotseatEduController.finishOnboarding();
- logUserAction(true);
- int toastStringRes = mMigrationMode == MIGRATE_SAME_PAGE
+ //TODO: pass actual page index here.
+ // Temporarily we're passing 1 for folder migration and 2 for page migration
+ logUserAction(true, FeatureFlags.HOTSEAT_MIGRATE_TO_FOLDER.get() ? 1 : 2);
+ int toastStringRes = !FeatureFlags.HOTSEAT_MIGRATE_TO_FOLDER.get()
? R.string.hotseat_items_migrated : R.string.hotseat_items_migrated_alt;
Toast.makeText(mLauncher, toastStringRes, Toast.LENGTH_LONG).show();
}
private void onDismiss(View v) {
- Toast.makeText(getContext(), R.string.hotseat_no_migration, Toast.LENGTH_LONG).show();
+ int top = mLauncher.getHotseat().getTop();
+ new ArrowTipView(mLauncher).show(mLauncher.getString(R.string.hotseat_no_migration), top);
mHotseatEduController.finishOnboarding();
- logUserAction(false);
+ logUserAction(false, -1);
handleClose(true);
}
@@ -159,7 +154,7 @@
mLauncher.getDeviceProfile().hotseatBarSizePx + insets.bottom;
}
- private void logUserAction(boolean migrated) {
+ private void logUserAction(boolean migrated, int pageIndex) {
LauncherLogProto.Action action = new LauncherLogProto.Action();
LauncherLogProto.Target target = new LauncherLogProto.Target();
action.type = LauncherLogProto.Action.Type.TOUCH;
@@ -168,8 +163,9 @@
target.tipType = LauncherLogProto.TipType.HYBRID_HOTSEAT;
target.controlType = migrated ? LauncherLogProto.ControlType.HYBRID_HOTSEAT_ACCEPTED
: HYBRID_HOTSEAT_CANCELED;
+ target.rank = MIGRATION_EXPERIMENT_IDENTIFIER;
// encoding migration type on pageIndex
- target.pageIndex = mMigrationMode;
+ target.pageIndex = pageIndex;
LauncherLogProto.LauncherEvent event = newLauncherEvent(action, target);
UserEventDispatcher.newInstance(getContext()).dispatchUserEvent(event, null);
}
@@ -222,28 +218,6 @@
}
}
- @Override
- protected void attachToContainer() {
- super.attachToContainer();
- if (FeatureFlags.HOTSEAT_MIGRATE_NEW_PAGE.get()) {
- mEduContent.setText(R.string.hotseat_edu_message_migrate_alt);
- mMigrationMode = MIGRATE_NEW_PAGE;
- return;
- }
- CellLayout page = mLauncher.getWorkspace().getScreenWithId(
- WorkspaceLayoutManager.FIRST_SCREEN_ID);
-
- int maxItemsOnPage = SAME_PAGE_MAX_ROWS * mLauncher.getDeviceProfile().inv.numColumns
- + (FeatureFlags.QSB_ON_FIRST_SCREEN ? 1 : 0);
- if (page.getShortcutsAndWidgets().getChildCount() > maxItemsOnPage
- || !page.makeSpaceForHotseatMigration(false)) {
- mMigrationMode = MIGRATE_NO_MIGRATE;
- mEduContent.setText(R.string.hotseat_edu_message_no_migrate);
- mEduHeading.setText(R.string.hotseat_edu_title_no_migrate);
- mDismissBtn.setVisibility(GONE);
- }
- }
-
/**
* Opens User education dialog with a list of suggested apps
*/
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java
index 2cdcd20..801408f 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java
@@ -48,6 +48,7 @@
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.LauncherState;
import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
import com.android.launcher3.Workspace;
import com.android.launcher3.WorkspaceItemInfo;
import com.android.launcher3.allapps.AllAppsStore;
@@ -94,7 +95,6 @@
private static final String BUNDLE_KEY_WORKSPACE = "workspace_apps";
private static final String PREDICTION_CLIENT = "hotseat";
-
private DropTarget.DragObject mDragObject;
private int mHotSeatItemsCount;
private int mPredictedSpotsCount = 0;
@@ -115,6 +115,7 @@
private List<PredictedAppIcon.PredictedIconOutlineDrawing> mOutlineDrawings = new ArrayList<>();
+
private final View.OnLongClickListener mPredictionLongClickListener = v -> {
if (!ItemLongClickListener.canStartDrag(mLauncher)) return false;
if (mLauncher.getWorkspace().isSwitchingState()) return false;
@@ -276,12 +277,10 @@
.build());
mAppPredictor.registerPredictionUpdates(mLauncher.getMainExecutor(),
this::setPredictedApps);
+ setPauseUIUpdate(false);
if (!isReady()) {
- if (mHotseatEduController != null) {
- mHotseatEduController.destroy();
- }
- mHotseatEduController = new HotseatEduController(mLauncher);
+ mHotseatEduController = new HotseatEduController(mLauncher, this::createPredictor);
}
mAppPredictor.requestPredictionUpdate();
}
@@ -327,7 +326,7 @@
mComponentKeyMappers.add(new ComponentKeyMapper(key, mDynamicItemCache));
}
predictionLog.append("]");
- FileLog.d(TAG, predictionLog.toString());
+ if (Utilities.IS_DEBUG_DEVICE) FileLog.d(TAG, predictionLog.toString());
updateDependencies();
if (isReady()) {
fillGapsWithPrediction();
@@ -488,7 +487,6 @@
}
}
-
@Override
public void onDragEnd() {
if (mDragObject == null) {
@@ -564,7 +562,8 @@
}
@Override
- public void reapplyItemInfo(ItemInfoWithIcon info) {}
+ public void reapplyItemInfo(ItemInfoWithIcon info) {
+ }
@Override
public void onDropCompleted(View target, DropTarget.DragObject d, boolean success) {
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsViewStateController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
index 65aaf22..549187f 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
@@ -20,7 +20,6 @@
import static com.android.quickstep.views.RecentsView.CONTENT_ALPHA;
import static com.android.quickstep.views.RecentsView.FULLSCREEN_PROGRESS;
-import android.animation.ValueAnimator;
import android.annotation.TargetApi;
import android.os.Build;
import android.util.FloatProperty;
@@ -29,10 +28,10 @@
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
-import com.android.launcher3.LauncherStateManager.AnimationConfig;
import com.android.launcher3.anim.AnimationSuccessListener;
-import com.android.launcher3.anim.AnimatorSetBuilder;
+import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.anim.PropertySetter;
+import com.android.launcher3.states.StateAnimationConfig;
import com.android.quickstep.views.ClearAllButton;
import com.android.quickstep.views.LauncherRecentsView;
import com.android.quickstep.views.RecentsView;
@@ -61,25 +60,22 @@
}
@Override
- void setStateWithAnimationInternal(@NonNull final LauncherState toState,
- @NonNull AnimatorSetBuilder builder, @NonNull AnimationConfig config) {
- super.setStateWithAnimationInternal(toState, builder, config);
+ void setStateWithAnimationInternal(@NonNull LauncherState toState,
+ @NonNull StateAnimationConfig config, @NonNull PendingAnimation builder) {
+ super.setStateWithAnimationInternal(toState, config, builder);
- ValueAnimator updateAnim = ValueAnimator.ofFloat(0, 1).setDuration(config.duration);
if (toState.overviewUi) {
// While animating into recents, update the visible task data as needed
- updateAnim.addUpdateListener(valueAnimator -> mRecentsView.loadVisibleTaskData());
+ builder.addOnFrameCallback(mRecentsView::loadVisibleTaskData);
mRecentsView.updateEmptyMessage();
} else {
- updateAnim.addListener(
+ builder.getAnim().addListener(
AnimationSuccessListener.forRunnable(mRecentsView::resetTaskVisuals));
}
- builder.play(updateAnim);
- PropertySetter propertySetter = config.getPropertySetter(builder);
- setAlphas(propertySetter, toState.getVisibleElements(mLauncher));
- float fullscreenProgress = toState.getOverviewFullscreenProgress();
- propertySetter.setFloat(mRecentsView, FULLSCREEN_PROGRESS, fullscreenProgress, LINEAR);
+ setAlphas(builder, toState.getVisibleElements(mLauncher));
+ builder.setFloat(mRecentsView, FULLSCREEN_PROGRESS,
+ toState.getOverviewFullscreenProgress(), LINEAR);
}
private void setAlphas(PropertySetter propertySetter, int visibleElements) {
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
index 5bac964..de3fce1 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
@@ -19,7 +19,6 @@
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.Launcher;
-import com.android.launcher3.R;
import com.android.launcher3.allapps.AllAppsTransitionController;
import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.quickstep.util.LayoutUtils;
@@ -107,7 +106,7 @@
}
@Override
- public int getBackgroundBlurRadius(Context context) {
- return context.getResources().getInteger(R.integer.app_background_blur_radius);
+ public float getDepth(Context context) {
+ return 1f;
}
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewPeekState.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewPeekState.java
index 427206a..8087611 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewPeekState.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewPeekState.java
@@ -14,17 +14,18 @@
* limitations under the License.
*/
package com.android.launcher3.uioverrides.states;
-import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_FADE;
-import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_SCRIM_FADE;
-import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_TRANSLATE_X;
+
import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
import static com.android.launcher3.anim.Interpolators.INSTANT;
import static com.android.launcher3.anim.Interpolators.OVERSHOOT_1_7;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_FADE;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_SCRIM_FADE;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_TRANSLATE_X;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
import com.android.launcher3.R;
-import com.android.launcher3.anim.AnimatorSetBuilder;
+import com.android.launcher3.states.StateAnimationConfig;
public class OverviewPeekState extends OverviewState {
public OverviewPeekState(int id) {
@@ -41,11 +42,11 @@
@Override
public void prepareForAtomicAnimation(Launcher launcher, LauncherState fromState,
- AnimatorSetBuilder builder) {
+ StateAnimationConfig config) {
if (this == OVERVIEW_PEEK && fromState == NORMAL) {
- builder.setInterpolator(ANIM_OVERVIEW_FADE, INSTANT);
- builder.setInterpolator(ANIM_OVERVIEW_TRANSLATE_X, OVERSHOOT_1_7);
- builder.setInterpolator(ANIM_OVERVIEW_SCRIM_FADE, FAST_OUT_SLOW_IN);
+ config.setInterpolator(ANIM_OVERVIEW_FADE, INSTANT);
+ config.setInterpolator(ANIM_OVERVIEW_TRANSLATE_X, OVERSHOOT_1_7);
+ config.setInterpolator(ANIM_OVERVIEW_SCRIM_FADE, FAST_OUT_SLOW_IN);
}
}
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewState.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewState.java
index bfbb630..024872f 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewState.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewState.java
@@ -18,13 +18,6 @@
import static android.view.View.VISIBLE;
import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
-import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_FADE;
-import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_SCALE;
-import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_TRANSLATE_X;
-import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_TRANSLATE_Y;
-import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_FADE;
-import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_SCALE;
-import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_TRANSLATE;
import static com.android.launcher3.anim.Interpolators.ACCEL;
import static com.android.launcher3.anim.Interpolators.DEACCEL_2;
import static com.android.launcher3.anim.Interpolators.OVERSHOOT_1_2;
@@ -32,6 +25,13 @@
import static com.android.launcher3.config.FeatureFlags.ENABLE_OVERVIEW_ACTIONS;
import static com.android.launcher3.logging.LoggerUtils.newContainerTarget;
import static com.android.launcher3.states.RotationHelper.REQUEST_ROTATE;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_FADE;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_SCALE;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_TRANSLATE_X;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_TRANSLATE_Y;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_FADE;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_SCALE;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_TRANSLATE;
import static com.android.quickstep.SysUINavigationMode.Mode.NO_BUTTON;
import static com.android.quickstep.SysUINavigationMode.removeShelfFromOverview;
@@ -48,8 +48,8 @@
import com.android.launcher3.R;
import com.android.launcher3.Workspace;
import com.android.launcher3.allapps.DiscoveryBounce;
-import com.android.launcher3.anim.AnimatorSetBuilder;
import com.android.launcher3.compat.AccessibilityManagerCompat;
+import com.android.launcher3.states.StateAnimationConfig;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
import com.android.quickstep.SysUINavigationMode;
@@ -207,8 +207,8 @@
}
@Override
- public int getBackgroundBlurRadius(Context context) {
- return context.getResources().getInteger(R.integer.overview_background_blur_radius);
+ public float getDepth(Context context) {
+ return 1f;
}
@Override
@@ -225,14 +225,14 @@
@Override
public void prepareForAtomicAnimation(Launcher launcher, LauncherState fromState,
- AnimatorSetBuilder builder) {
+ StateAnimationConfig config) {
if ((fromState == NORMAL || fromState == HINT_STATE) && this == OVERVIEW) {
if (SysUINavigationMode.getMode(launcher) == NO_BUTTON) {
- builder.setInterpolator(ANIM_WORKSPACE_SCALE,
+ config.setInterpolator(ANIM_WORKSPACE_SCALE,
fromState == NORMAL ? ACCEL : OVERSHOOT_1_2);
- builder.setInterpolator(ANIM_WORKSPACE_TRANSLATE, ACCEL);
+ config.setInterpolator(ANIM_WORKSPACE_TRANSLATE, ACCEL);
} else {
- builder.setInterpolator(ANIM_WORKSPACE_SCALE, OVERSHOOT_1_2);
+ config.setInterpolator(ANIM_WORKSPACE_SCALE, OVERSHOOT_1_2);
// Scale up the recents, if it is not coming from the side
RecentsView overview = launcher.getOverviewPanel();
@@ -240,15 +240,15 @@
SCALE_PROPERTY.set(overview, RECENTS_PREPARE_SCALE);
}
}
- builder.setInterpolator(ANIM_WORKSPACE_FADE, OVERSHOOT_1_2);
- builder.setInterpolator(ANIM_OVERVIEW_SCALE, OVERSHOOT_1_2);
+ config.setInterpolator(ANIM_WORKSPACE_FADE, OVERSHOOT_1_2);
+ config.setInterpolator(ANIM_OVERVIEW_SCALE, OVERSHOOT_1_2);
Interpolator translationInterpolator = ENABLE_OVERVIEW_ACTIONS.get()
&& removeShelfFromOverview(launcher)
? OVERSHOOT_1_2
: OVERSHOOT_1_7;
- builder.setInterpolator(ANIM_OVERVIEW_TRANSLATE_X, translationInterpolator);
- builder.setInterpolator(ANIM_OVERVIEW_TRANSLATE_Y, translationInterpolator);
- builder.setInterpolator(ANIM_OVERVIEW_FADE, OVERSHOOT_1_2);
+ config.setInterpolator(ANIM_OVERVIEW_TRANSLATE_X, translationInterpolator);
+ config.setInterpolator(ANIM_OVERVIEW_TRANSLATE_Y, translationInterpolator);
+ config.setInterpolator(ANIM_OVERVIEW_FADE, OVERSHOOT_1_2);
}
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/FlingAndHoldTouchController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/FlingAndHoldTouchController.java
index b347013..6fc03b1 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/FlingAndHoldTouchController.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/FlingAndHoldTouchController.java
@@ -21,16 +21,16 @@
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.LauncherState.OVERVIEW;
import static com.android.launcher3.LauncherState.OVERVIEW_PEEK;
-import static com.android.launcher3.LauncherStateManager.PLAY_ATOMIC_OVERVIEW_PEEK;
-import static com.android.launcher3.LauncherStateManager.SKIP_OVERVIEW;
-import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_ALL_APPS_FADE;
-import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_ALL_APPS_HEADER_FADE;
-import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_FADE;
-import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_SCALE;
-import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_TRANSLATE;
import static com.android.launcher3.anim.Interpolators.ACCEL;
import static com.android.launcher3.anim.Interpolators.DEACCEL;
import static com.android.launcher3.anim.Interpolators.DEACCEL_3;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_APPS_FADE;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_APPS_HEADER_FADE;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_FADE;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_SCALE;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_TRANSLATE;
+import static com.android.launcher3.states.StateAnimationConfig.PLAY_ATOMIC_OVERVIEW_PEEK;
+import static com.android.launcher3.states.StateAnimationConfig.SKIP_OVERVIEW;
import static com.android.launcher3.util.VibratorWrapper.OVERVIEW_HAPTIC;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_OVERVIEW_DISABLED;
@@ -44,10 +44,10 @@
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAppTransitionManagerImpl;
import com.android.launcher3.LauncherState;
-import com.android.launcher3.LauncherStateManager.AnimationFlags;
import com.android.launcher3.anim.AnimationSuccessListener;
-import com.android.launcher3.anim.AnimatorSetBuilder;
import com.android.launcher3.anim.Interpolators;
+import com.android.launcher3.states.StateAnimationConfig;
+import com.android.launcher3.states.StateAnimationConfig.AnimationFlags;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
import com.android.launcher3.util.VibratorWrapper;
import com.android.quickstep.SystemUiProxy;
@@ -105,8 +105,12 @@
LauncherState fromState = isPaused ? NORMAL : OVERVIEW_PEEK;
LauncherState toState = isPaused ? OVERVIEW_PEEK : NORMAL;
long peekDuration = isPaused ? PEEK_IN_ANIM_DURATION : PEEK_OUT_ANIM_DURATION;
- mPeekAnim = mLauncher.getStateManager().createAtomicAnimation(fromState, toState,
- new AnimatorSetBuilder(), PLAY_ATOMIC_OVERVIEW_PEEK, peekDuration);
+
+ StateAnimationConfig config = new StateAnimationConfig();
+ config.duration = peekDuration;
+ config.animFlags = PLAY_ATOMIC_OVERVIEW_PEEK;
+ mPeekAnim = mLauncher.getStateManager().createAtomicAnimation(
+ fromState, toState, config);
mPeekAnim.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
@@ -116,8 +120,8 @@
mPeekAnim.start();
VibratorWrapper.INSTANCE.get(mLauncher).vibrate(OVERVIEW_HAPTIC);
- mLauncher.getDragLayer().getScrim().animateToSysuiMultiplier(isPaused ? 0 : 1,
- peekDuration, 0);
+ mLauncher.getDragLayer().getScrim().createSysuiMultiplierAnim(isPaused ? 0 : 1)
+ .setDuration(peekDuration).start();
}
/**
@@ -130,10 +134,10 @@
}
@Override
- protected AnimatorSetBuilder getAnimatorSetBuilderForStates(LauncherState fromState,
- LauncherState toState) {
+ protected StateAnimationConfig getConfigForStates(
+ LauncherState fromState, LauncherState toState) {
if (fromState == NORMAL && toState == ALL_APPS) {
- AnimatorSetBuilder builder = new AnimatorSetBuilder();
+ StateAnimationConfig builder = new StateAnimationConfig();
// Fade in prediction icons quickly, then rest of all apps after reaching overview.
float progressToReachOverview = NORMAL.getVerticalProgress(mLauncher)
- OVERVIEW.getVerticalProgress(mLauncher);
@@ -152,7 +156,7 @@
builder.setInterpolator(ANIM_WORKSPACE_FADE, DEACCEL_3);
return builder;
} else if (fromState == ALL_APPS && toState == NORMAL) {
- AnimatorSetBuilder builder = new AnimatorSetBuilder();
+ StateAnimationConfig builder = new StateAnimationConfig();
// Keep all apps/predictions opaque until the very end of the transition.
float progressToReachOverview = OVERVIEW.getVerticalProgress(mLauncher);
builder.setInterpolator(ANIM_ALL_APPS_FADE, Interpolators.clampToProgress(
@@ -165,7 +169,7 @@
1));
return builder;
}
- return super.getAnimatorSetBuilderForStates(fromState, toState);
+ return super.getConfigForStates(fromState, toState);
}
@Override
@@ -211,9 +215,11 @@
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
if (mCancelled) {
- mPeekAnim = mLauncher.getStateManager().createAtomicAnimation(mFromState,
- mToState, new AnimatorSetBuilder(), PLAY_ATOMIC_OVERVIEW_PEEK,
- PEEK_OUT_ANIM_DURATION);
+ StateAnimationConfig config = new StateAnimationConfig();
+ config.animFlags = PLAY_ATOMIC_OVERVIEW_PEEK;
+ config.duration = PEEK_OUT_ANIM_DURATION;
+ mPeekAnim = mLauncher.getStateManager().createAtomicAnimation(
+ mFromState, mToState, config);
mPeekAnim.start();
}
mAtomicAnim = null;
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java
index a0ca886..77118d5 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java
@@ -15,8 +15,7 @@
*/
package com.android.launcher3.uioverrides.touchcontrollers;
-import static android.view.View.TRANSLATION_X;
-
+import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X;
import static com.android.launcher3.LauncherState.ALL_APPS;
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.LauncherState.OVERVIEW;
@@ -26,9 +25,6 @@
import static com.android.launcher3.touch.AbstractStateChangeTouchController.SUCCESS_TRANSITION_PROGRESS;
import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
-import android.animation.Animator;
-import android.animation.AnimatorSet;
-import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.view.MotionEvent;
import android.view.animation.Interpolator;
@@ -36,16 +32,16 @@
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
-import com.android.launcher3.LauncherStateManager.AnimationConfig;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.allapps.AllAppsTransitionController;
import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.anim.AnimatorPlaybackController;
-import com.android.launcher3.anim.AnimatorSetBuilder;
import com.android.launcher3.anim.Interpolators;
+import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.compat.AccessibilityManagerCompat;
import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.states.StateAnimationConfig;
import com.android.launcher3.touch.SingleAxisSwipeDetector;
import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
@@ -132,46 +128,37 @@
private void initCurrentAnimation() {
long accuracy = (long) (getShiftRange() * 2);
- final AnimatorSet anim = new AnimatorSet();
+ final PendingAnimation builder = new PendingAnimation(accuracy);
if (mStartState == OVERVIEW) {
RecentsView recentsView = mLauncher.getOverviewPanel();
float pullbackDist = mPullbackDistance;
if (!recentsView.isRtl()) {
pullbackDist = -pullbackDist;
}
- ObjectAnimator pullback = ObjectAnimator.ofFloat(recentsView, TRANSLATION_X,
- pullbackDist);
- pullback.setInterpolator(PULLBACK_INTERPOLATOR);
+
+ builder.setFloat(recentsView, VIEW_TRANSLATE_X, pullbackDist, PULLBACK_INTERPOLATOR);
if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
- pullback.addUpdateListener(
- valueAnimator -> recentsView.redrawLiveTile(false /* mightNeedToRefill */));
+ builder.addOnFrameCallback(
+ () -> recentsView.redrawLiveTile(false /* mightNeedToRefill */));
}
- anim.play(pullback);
} else if (mStartState == ALL_APPS) {
- AnimatorSetBuilder builder = new AnimatorSetBuilder();
AllAppsTransitionController allAppsController = mLauncher.getAllAppsController();
- Animator allAppsProgress = ObjectAnimator.ofFloat(allAppsController, ALL_APPS_PROGRESS,
- -mPullbackDistance / allAppsController.getShiftRange());
- allAppsProgress.setInterpolator(PULLBACK_INTERPOLATOR);
- builder.play(allAppsProgress);
+ builder.setFloat(allAppsController, ALL_APPS_PROGRESS,
+ -mPullbackDistance / allAppsController.getShiftRange(), PULLBACK_INTERPOLATOR);
+
// Slightly fade out all apps content to further distinguish from scrolling.
- builder.setInterpolator(AnimatorSetBuilder.ANIM_ALL_APPS_FADE, Interpolators
- .mapToProgress(PULLBACK_INTERPOLATOR, 0, 0.5f));
- AnimationConfig config = new AnimationConfig();
+ StateAnimationConfig config = new StateAnimationConfig();
config.duration = accuracy;
- allAppsController.setAlphas(mEndState.getVisibleElements(mLauncher), config, builder);
- anim.play(builder.build());
+ config.setInterpolator(StateAnimationConfig.ANIM_ALL_APPS_FADE, Interpolators
+ .mapToProgress(PULLBACK_INTERPOLATOR, 0, 0.5f));
+
+ allAppsController.setAlphas(mEndState, config, builder);
}
AbstractFloatingView topView = AbstractFloatingView.getTopOpenView(mLauncher);
if (topView != null) {
- Animator hintCloseAnim = topView.createHintCloseAnim(mPullbackDistance);
- if (hintCloseAnim != null) {
- hintCloseAnim.setInterpolator(PULLBACK_INTERPOLATOR);
- anim.play(hintCloseAnim);
- }
+ topView.addHintCloseAnim(mPullbackDistance, PULLBACK_INTERPOLATOR, builder);
}
- anim.setDuration(accuracy);
- mCurrentAnimation = AnimatorPlaybackController.wrap(anim, accuracy)
+ mCurrentAnimation = builder.createPlaybackController()
.setOnCancelRunnable(this::clearState);
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java
index 7cebabe..064133c 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java
@@ -19,9 +19,9 @@
import static com.android.launcher3.LauncherState.HINT_STATE;
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.LauncherState.OVERVIEW;
-import static com.android.launcher3.LauncherStateManager.PLAY_ATOMIC_OVERVIEW_PEEK;
import static com.android.launcher3.Utilities.EDGE_NAV_BAR;
import static com.android.launcher3.anim.Interpolators.ACCEL_DEACCEL;
+import static com.android.launcher3.states.StateAnimationConfig.PLAY_ATOMIC_OVERVIEW_PEEK;
import static com.android.launcher3.util.VibratorWrapper.OVERVIEW_HAPTIC;
import android.animation.AnimatorSet;
@@ -34,7 +34,7 @@
import com.android.launcher3.LauncherStateManager;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AnimationSuccessListener;
-import com.android.launcher3.anim.AnimatorSetBuilder;
+import com.android.launcher3.states.StateAnimationConfig;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
import com.android.launcher3.util.VibratorWrapper;
import com.android.quickstep.util.StaggeredWorkspaceAnim;
@@ -171,11 +171,11 @@
// StaggeredWorkspaceAnim doesn't animate overview, so we handle it here.
stateManager.cancelAnimation();
- AnimatorSetBuilder builder = new AnimatorSetBuilder();
- long duration = OVERVIEW.getTransitionDuration(mLauncher);
+ StateAnimationConfig config = new StateAnimationConfig();
+ config.duration = OVERVIEW.getTransitionDuration(mLauncher);
+ config.animFlags = PLAY_ATOMIC_OVERVIEW_PEEK;
AnimatorSet anim = stateManager.createAtomicAnimation(
- stateManager.getState(), NORMAL, builder,
- PLAY_ATOMIC_OVERVIEW_PEEK, duration);
+ stateManager.getState(), NORMAL, config);
anim.addListener(AnimationSuccessListener.forRunnable(
() -> onSwipeInteractionCompleted(NORMAL, Touch.SWIPE)));
anim.start();
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java
index 81a6d9b..c92a872 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java
@@ -21,18 +21,18 @@
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.LauncherState.OVERVIEW;
import static com.android.launcher3.LauncherState.QUICK_SWITCH;
-import static com.android.launcher3.LauncherStateManager.ANIM_ALL_COMPONENTS;
-import static com.android.launcher3.LauncherStateManager.SKIP_OVERVIEW;
import static com.android.launcher3.anim.AlphaUpdateListener.ALPHA_CUTOFF_THRESHOLD;
-import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_ALL_APPS_FADE;
-import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_VERTICAL_PROGRESS;
-import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_FADE;
-import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_TRANSLATE;
import static com.android.launcher3.anim.Interpolators.ACCEL_0_75;
import static com.android.launcher3.anim.Interpolators.DEACCEL;
import static com.android.launcher3.anim.Interpolators.DEACCEL_5;
import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.anim.Interpolators.scrollInterpolatorForVelocity;
+import static com.android.launcher3.anim.PropertySetter.NO_ANIM_PROPERTY_SETTER;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_APPS_FADE;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_VERTICAL_PROGRESS;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_FADE;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_TRANSLATE;
+import static com.android.launcher3.states.StateAnimationConfig.SKIP_OVERVIEW;
import static com.android.launcher3.touch.BothAxesSwipeDetector.DIRECTION_RIGHT;
import static com.android.launcher3.touch.BothAxesSwipeDetector.DIRECTION_UP;
import static com.android.launcher3.util.DefaultDisplay.getSingleFrameMs;
@@ -55,15 +55,13 @@
import com.android.launcher3.BaseQuickstepLauncher;
import com.android.launcher3.LauncherState;
-import com.android.launcher3.LauncherStateManager.AnimationConfig;
-import com.android.launcher3.LauncherStateManager.AnimationFlags;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.allapps.AllAppsTransitionController;
import com.android.launcher3.anim.AnimatorPlaybackController;
-import com.android.launcher3.anim.AnimatorSetBuilder;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.graphics.OverviewScrim;
+import com.android.launcher3.states.StateAnimationConfig;
import com.android.launcher3.touch.BaseSwipeDetector;
import com.android.launcher3.touch.BothAxesSwipeDetector;
import com.android.launcher3.userevent.nano.LauncherLogProto;
@@ -192,11 +190,9 @@
ShelfAnimState shelfState = isPaused ? PEEK : HIDE;
if (shelfState == PEEK) {
// Some shelf elements (e.g. qsb) were hidden, but we need them visible when peeking.
- AnimatorSetBuilder builder = new AnimatorSetBuilder();
AllAppsTransitionController allAppsController = mLauncher.getAllAppsController();
- allAppsController.setAlphas(NORMAL.getVisibleElements(mLauncher),
- new AnimationConfig(), builder);
- builder.build().setDuration(0).start();
+ allAppsController.setAlphas(
+ NORMAL, new StateAnimationConfig(), NO_ANIM_PROPERTY_SETTER);
if ((OVERVIEW.getVisibleElements(mLauncher) & HOTSEAT_ICONS) != 0) {
// Hotseat was hidden, but we need it visible when peeking.
@@ -209,12 +205,12 @@
private void setupAnimators() {
// Animate the non-overview components (e.g. workspace, shelf) out of the way.
- AnimatorSetBuilder nonOverviewBuilder = new AnimatorSetBuilder();
+ StateAnimationConfig nonOverviewBuilder = new StateAnimationConfig();
nonOverviewBuilder.setInterpolator(ANIM_WORKSPACE_FADE, FADE_OUT_INTERPOLATOR);
nonOverviewBuilder.setInterpolator(ANIM_ALL_APPS_FADE, FADE_OUT_INTERPOLATOR);
nonOverviewBuilder.setInterpolator(ANIM_WORKSPACE_TRANSLATE, TRANSLATE_OUT_INTERPOLATOR);
nonOverviewBuilder.setInterpolator(ANIM_VERTICAL_PROGRESS, TRANSLATE_OUT_INTERPOLATOR);
- updateNonOverviewAnim(QUICK_SWITCH, nonOverviewBuilder, ANIM_ALL_COMPONENTS);
+ updateNonOverviewAnim(QUICK_SWITCH, nonOverviewBuilder);
mNonOverviewAnim.dispatchOnStart();
if (mRecentsView.getTaskViewCount() == 0) {
@@ -230,11 +226,12 @@
}
/** Create state animation to control non-overview components. */
- private void updateNonOverviewAnim(LauncherState toState, AnimatorSetBuilder builder,
- @AnimationFlags int animComponents) {
- long accuracy = (long) (Math.max(mXRange, mYRange) * 2);
- mNonOverviewAnim = mLauncher.getStateManager().createAnimationToNewWorkspace(toState,
- builder, accuracy, this::clearState, animComponents | SKIP_OVERVIEW);
+ private void updateNonOverviewAnim(LauncherState toState, StateAnimationConfig config) {
+ config.duration = (long) (Math.max(mXRange, mYRange) * 2);
+ config.animFlags = config.animFlags | SKIP_OVERVIEW;
+ mNonOverviewAnim = mLauncher.getStateManager()
+ .createAnimationToNewWorkspace(toState, config)
+ .setOnCancelRunnable(this::clearState);
}
private void setupOverviewAnimators() {
@@ -419,8 +416,10 @@
if (flingUpToNormal && !mIsHomeScreenVisible) {
// We are flinging to home while workspace is invisible, run the same staggered
// animation as from an app.
+ StateAnimationConfig config = new StateAnimationConfig();
// Update mNonOverviewAnim to do nothing so it doesn't interfere.
- updateNonOverviewAnim(targetState, new AnimatorSetBuilder(), 0 /* animComponents */);
+ config.animFlags = 0;
+ updateNonOverviewAnim(targetState, config);
nonOverviewAnim = mNonOverviewAnim.getAnimationPlayer();
new StaggeredWorkspaceAnim(mLauncher, velocity.y, false /* animateOverviewScrim */)
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java
index 8de1b3a..9e53959 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java
@@ -17,18 +17,17 @@
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.LauncherState.QUICK_SWITCH;
-import static com.android.launcher3.LauncherStateManager.ANIM_ALL_COMPONENTS;
-import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_ALL_APPS_FADE;
-import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_FADE;
-import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_SCALE;
-import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_TRANSLATE_Y;
-import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_VERTICAL_PROGRESS;
-import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_FADE;
-import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_TRANSLATE;
import static com.android.launcher3.anim.Interpolators.ACCEL_2;
import static com.android.launcher3.anim.Interpolators.DEACCEL_2;
import static com.android.launcher3.anim.Interpolators.INSTANT;
import static com.android.launcher3.anim.Interpolators.LINEAR;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_APPS_FADE;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_FADE;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_SCALE;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_TRANSLATE_Y;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_VERTICAL_PROGRESS;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_FADE;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_TRANSLATE;
import static com.android.launcher3.util.SystemUiController.UI_STATE_OVERVIEW;
import static com.android.quickstep.views.RecentsView.UPDATE_SYSUI_FLAGS_THRESHOLD;
import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
@@ -41,7 +40,7 @@
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
import com.android.launcher3.Utilities;
-import com.android.launcher3.anim.AnimatorSetBuilder;
+import com.android.launcher3.states.StateAnimationConfig;
import com.android.launcher3.touch.AbstractStateChangeTouchController;
import com.android.launcher3.touch.SingleAxisSwipeDetector;
import com.android.launcher3.userevent.nano.LauncherLogProto;
@@ -108,30 +107,30 @@
@Override
protected float initCurrentAnimation(int animComponents) {
- AnimatorSetBuilder animatorSetBuilder = new AnimatorSetBuilder();
- setupInterpolators(animatorSetBuilder);
- long accuracy = (long) (getShiftRange() * 2);
- mCurrentAnimation = mLauncher.getStateManager().createAnimationToNewWorkspace(mToState,
- animatorSetBuilder, accuracy, this::clearState, ANIM_ALL_COMPONENTS);
- mCurrentAnimation.getAnimationPlayer().addUpdateListener(valueAnimator -> {
- updateFullscreenProgress((Float) valueAnimator.getAnimatedValue());
- });
+ StateAnimationConfig config = new StateAnimationConfig();
+ setupInterpolators(config);
+ config.duration = (long) (getShiftRange() * 2);
+ mCurrentAnimation = mLauncher.getStateManager()
+ .createAnimationToNewWorkspace(mToState, config)
+ .setOnCancelRunnable(this::clearState);
+ mCurrentAnimation.getAnimationPlayer().addUpdateListener(valueAnimator ->
+ updateFullscreenProgress((Float) valueAnimator.getAnimatedValue()));
return 1 / getShiftRange();
}
- private void setupInterpolators(AnimatorSetBuilder animatorSetBuilder) {
- animatorSetBuilder.setInterpolator(ANIM_WORKSPACE_FADE, DEACCEL_2);
- animatorSetBuilder.setInterpolator(ANIM_ALL_APPS_FADE, DEACCEL_2);
+ private void setupInterpolators(StateAnimationConfig stateAnimationConfig) {
+ stateAnimationConfig.setInterpolator(ANIM_WORKSPACE_FADE, DEACCEL_2);
+ stateAnimationConfig.setInterpolator(ANIM_ALL_APPS_FADE, DEACCEL_2);
if (SysUINavigationMode.getMode(mLauncher) == Mode.NO_BUTTON) {
// Overview lives to the left of workspace, so translate down later than over
- animatorSetBuilder.setInterpolator(ANIM_WORKSPACE_TRANSLATE, ACCEL_2);
- animatorSetBuilder.setInterpolator(ANIM_VERTICAL_PROGRESS, ACCEL_2);
- animatorSetBuilder.setInterpolator(ANIM_OVERVIEW_SCALE, ACCEL_2);
- animatorSetBuilder.setInterpolator(ANIM_OVERVIEW_TRANSLATE_Y, ACCEL_2);
- animatorSetBuilder.setInterpolator(ANIM_OVERVIEW_FADE, INSTANT);
+ stateAnimationConfig.setInterpolator(ANIM_WORKSPACE_TRANSLATE, ACCEL_2);
+ stateAnimationConfig.setInterpolator(ANIM_VERTICAL_PROGRESS, ACCEL_2);
+ stateAnimationConfig.setInterpolator(ANIM_OVERVIEW_SCALE, ACCEL_2);
+ stateAnimationConfig.setInterpolator(ANIM_OVERVIEW_TRANSLATE_Y, ACCEL_2);
+ stateAnimationConfig.setInterpolator(ANIM_OVERVIEW_FADE, INSTANT);
} else {
- animatorSetBuilder.setInterpolator(ANIM_WORKSPACE_TRANSLATE, LINEAR);
- animatorSetBuilder.setInterpolator(ANIM_VERTICAL_PROGRESS, LINEAR);
+ stateAnimationConfig.setInterpolator(ANIM_WORKSPACE_TRANSLATE, LINEAR);
+ stateAnimationConfig.setInterpolator(ANIM_VERTICAL_PROGRESS, LINEAR);
}
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java
index cc58fcf..1b3610a 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java
@@ -81,6 +81,9 @@
private boolean canInterceptTouch() {
if (mCurrentAnimation != null) {
+ mCurrentAnimation.forceFinishIfCloseToEnd();
+ }
+ if (mCurrentAnimation != null) {
// If we are already animating from a previous state, we can intercept.
return true;
}
@@ -126,6 +129,7 @@
for (int i = 0; i < mRecentsView.getTaskViewCount(); i++) {
TaskView view = mRecentsView.getTaskViewAt(i);
+
if (mRecentsView.isTaskViewVisible(view) && mActivity.getDragLayer()
.isEventOverView(view, ev)) {
// Disable swiping up and down if the task overlay is modal.
@@ -214,7 +218,7 @@
if (mCurrentAnimation != null) {
mCurrentAnimation.setOnCancelRunnable(null);
}
- mCurrentAnimation = AnimatorPlaybackController.wrap(mPendingAnimation, maxDuration)
+ mCurrentAnimation = mPendingAnimation.createPlaybackController()
.setOnCancelRunnable(this::clearState);
onUserControlledAnimationCreated(mCurrentAnimation);
mCurrentAnimation.getTarget().addListener(this);
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/AppToOverviewAnimationProvider.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/AppToOverviewAnimationProvider.java
index e1ff4f4..ce7fa0d 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/AppToOverviewAnimationProvider.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/AppToOverviewAnimationProvider.java
@@ -19,7 +19,7 @@
import static com.android.launcher3.LauncherState.OVERVIEW;
import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
import static com.android.launcher3.anim.Interpolators.TOUCH_RESPONSE_INTERPOLATOR;
-import static com.android.launcher3.uioverrides.BackgroundBlurController.BACKGROUND_BLUR;
+import static com.android.launcher3.uioverrides.DepthController.DEPTH;
import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_OPENING;
@@ -34,7 +34,7 @@
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.anim.AnimationSuccessListener;
-import com.android.launcher3.uioverrides.BackgroundBlurController;
+import com.android.launcher3.uioverrides.DepthController;
import com.android.quickstep.util.AppWindowAnimationHelper;
import com.android.quickstep.util.AppWindowAnimationHelper.TransformParams;
import com.android.quickstep.util.RemoteAnimationProvider;
@@ -48,7 +48,7 @@
*
* @param <T> activity that contains the overview
*/
-final class AppToOverviewAnimationProvider<T extends BaseDraggingActivity> implements
+final class AppToOverviewAnimationProvider<T extends BaseDraggingActivity> extends
RemoteAnimationProvider {
private static final long RECENTS_LAUNCH_DURATION = 250;
@@ -105,10 +105,10 @@
mRecentsView.setRunningTaskIconScaledDown(true);
}
- BackgroundBlurController blurController = mActivityInterface.getBackgroundBlurController();
- if (blurController != null) {
+ DepthController depthController = mActivityInterface.getDepthController();
+ if (depthController != null) {
// Update the surface to be the lowest closing app surface
- blurController.setSurfaceToLauncher(mRecentsView);
+ depthController.setSurfaceToLauncher(mRecentsView);
}
AnimatorSet anim = new AnimatorSet();
@@ -124,7 +124,7 @@
if (mActivity == null) {
Log.e(TAG, "Animation created, before activity");
anim.play(ValueAnimator.ofInt(0, 1).setDuration(RECENTS_LAUNCH_DURATION))
- .with(createBackgroundBlurAnimator(blurController));
+ .with(createDepthAnimator(depthController));
return anim;
}
@@ -136,7 +136,7 @@
if (runningTaskTarget == null) {
Log.e(TAG, "No closing app");
anim.play(ValueAnimator.ofInt(0, 1).setDuration(RECENTS_LAUNCH_DURATION))
- .with(createBackgroundBlurAnimator(blurController));
+ .with(createDepthAnimator(depthController));
return anim;
}
@@ -184,7 +184,7 @@
});
}
anim.play(valueAnimator)
- .with(createBackgroundBlurAnimator(blurController));
+ .with(createDepthAnimator(depthController));
return anim;
}
@@ -197,14 +197,14 @@
return RECENTS_LAUNCH_DURATION;
}
- private Animator createBackgroundBlurAnimator(BackgroundBlurController blurController) {
- if (blurController == null) {
+ private Animator createDepthAnimator(DepthController depthController) {
+ if (depthController == null) {
// Dummy animation
return ValueAnimator.ofInt(0);
}
- return ObjectAnimator.ofInt(blurController, BACKGROUND_BLUR,
- BACKGROUND_APP.getBackgroundBlurRadius(mActivity),
- OVERVIEW.getBackgroundBlurRadius(mActivity))
+ return ObjectAnimator.ofFloat(depthController, DEPTH,
+ BACKGROUND_APP.getDepth(mActivity),
+ OVERVIEW.getDepth(mActivity))
.setDuration(RECENTS_LAUNCH_DURATION);
}
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackActivityInterface.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackActivityInterface.java
index 71580ca..4abb86a 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackActivityInterface.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackActivityInterface.java
@@ -213,7 +213,7 @@
@Override
public Rect getOverviewWindowBounds(Rect homeBounds, RemoteAnimationTargetCompat target) {
// TODO: Remove this once b/77875376 is fixed
- return target.sourceContainerBounds;
+ return target.screenSpaceBounds;
}
@Override
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityInterface.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityInterface.java
index d402a75..55e6ba2 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityInterface.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityInterface.java
@@ -56,8 +56,8 @@
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.appprediction.PredictionUiStateManager;
import com.android.launcher3.touch.PagedOrientationHandler;
-import com.android.launcher3.uioverrides.BackgroundBlurController;
-import com.android.launcher3.uioverrides.BackgroundBlurController.ClampedBlurProperty;
+import com.android.launcher3.uioverrides.DepthController;
+import com.android.launcher3.uioverrides.DepthController.ClampedDepthProperty;
import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.launcher3.views.FloatingIconView;
import com.android.quickstep.SysUINavigationMode.Mode;
@@ -330,18 +330,17 @@
endState.getVerticalProgress(activity)));
}
- // Animate the blur
- BackgroundBlurController blurController = getBackgroundBlurController();
- int fromBlurRadius = fromState.getBackgroundBlurRadius(activity);
- int toBlurRadius = endState.getBackgroundBlurRadius(activity);
- Animator backgroundBlur = ObjectAnimator.ofInt(blurController,
- new ClampedBlurProperty(toBlurRadius, fromBlurRadius),
- fromBlurRadius, toBlurRadius);
- anim.play(backgroundBlur);
+ // Animate the blur and wallpaper zoom
+ DepthController depthController = getDepthController();
+ float fromDepthRatio = fromState.getDepth(activity);
+ float toDepthRatio = endState.getDepth(activity);
+ Animator depthAnimator = ObjectAnimator.ofFloat(depthController,
+ new ClampedDepthProperty(fromDepthRatio, toDepthRatio),
+ fromDepthRatio, toDepthRatio);
+ anim.play(depthAnimator);
playScaleDownAnim(anim, activity, fromState, endState);
-
anim.setDuration(transitionLength * 2);
anim.setInterpolator(LINEAR);
AnimatorPlaybackController controller =
@@ -558,11 +557,11 @@
@Nullable
@Override
- public BackgroundBlurController getBackgroundBlurController() {
+ public DepthController getDepthController() {
BaseQuickstepLauncher launcher = getCreatedActivity();
if (launcher == null) {
return null;
}
- return launcher.getBackgroundBlurController();
+ return launcher.getDepthController();
}
}
\ No newline at end of file
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandler.java
index 3328abc..cafdb62 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandler.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandler.java
@@ -294,7 +294,7 @@
}
setupRecentsViewUi();
- mActivityInterface.getBackgroundBlurController().setSurfaceToLauncher(mRecentsView);
+ mActivityInterface.getDepthController().setSurfaceToLauncher(mRecentsView);
if (mDeviceState.getNavMode() == TWO_BUTTONS) {
// If the device is in two button mode, swiping up will show overview with predictions
@@ -588,7 +588,7 @@
// We will handle the sysui flags based on the centermost task view.
if (mRecentsAnimationController != null) {
mRecentsAnimationController.setWindowThresholdCrossed(centermostTaskFlags != 0
- || useHomeScreenFlags);
+ && useHomeScreenFlags);
}
int sysuiFlags = useHomeScreenFlags ? 0 : centermostTaskFlags;
mActivity.getSystemUiController().updateUiState(UI_STATE_OVERVIEW, sysuiFlags);
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/OverviewCommandHelper.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/OverviewCommandHelper.java
index fc50660..c6b719a 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/OverviewCommandHelper.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/OverviewCommandHelper.java
@@ -33,6 +33,7 @@
import com.android.launcher3.logging.UserEventDispatcher;
import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.quickstep.util.ActivityInitListener;
+import com.android.quickstep.util.RemoteAnimationProvider;
import com.android.quickstep.views.RecentsView;
import com.android.quickstep.views.TaskView;
import com.android.systemui.shared.system.ActivityManagerWrapper;
@@ -187,7 +188,15 @@
// Otherwise, start overview.
mListener = mActivityInterface.createActivityInitListener(this::onActivityReady);
mListener.registerAndStartActivity(mOverviewComponentObserver.getOverviewIntent(),
- this::createWindowAnimation, mContext, MAIN_EXECUTOR.getHandler(),
+ new RemoteAnimationProvider() {
+ @Override
+ public AnimatorSet createWindowAnimation(
+ RemoteAnimationTargetCompat[] appTargets,
+ RemoteAnimationTargetCompat[] wallpaperTargets) {
+ return RecentsActivityCommand.this.createWindowAnimation(appTargets,
+ wallpaperTargets);
+ }
+ }, mContext, MAIN_EXECUTOR.getHandler(),
mAnimationProvider.getRecentsLaunchDuration());
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsActivity.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsActivity.java
index 3ab0f19..42d944f 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsActivity.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsActivity.java
@@ -186,7 +186,7 @@
AppWindowAnimationHelper helper = new AppWindowAnimationHelper(
mFallbackRecentsView.getPagedViewOrientedState(), this);
Animator recentsAnimator = getRecentsWindowAnimator(taskView, !activityClosing, appTargets,
- wallpaperTargets, null /* backgroundBlurController */,
+ wallpaperTargets, null /* depthController */,
helper);
target.play(recentsAnimator.setDuration(RECENTS_LAUNCH_DURATION));
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskViewUtils.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskViewUtils.java
index 38b86ce..7ec083e 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskViewUtils.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskViewUtils.java
@@ -19,7 +19,7 @@
import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.anim.Interpolators.TOUCH_RESPONSE_INTERPOLATOR;
import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
-import static com.android.launcher3.uioverrides.BackgroundBlurController.BACKGROUND_BLUR;
+import static com.android.launcher3.uioverrides.DepthController.DEPTH;
import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_OPENING;
import android.animation.Animator;
@@ -35,7 +35,7 @@
import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.Utilities;
-import com.android.launcher3.uioverrides.BackgroundBlurController;
+import com.android.launcher3.uioverrides.DepthController;
import com.android.quickstep.util.AppWindowAnimationHelper;
import com.android.quickstep.util.MultiValueUpdateListener;
import com.android.quickstep.views.RecentsView;
@@ -123,7 +123,7 @@
public static Animator getRecentsWindowAnimator(TaskView v, boolean skipViewChanges,
RemoteAnimationTargetCompat[] appTargets,
RemoteAnimationTargetCompat[] wallpaperTargets,
- BackgroundBlurController backgroundBlurController,
+ DepthController depthController,
final AppWindowAnimationHelper inOutHelper) {
SyncRtSurfaceTransactionApplierCompat applier =
new SyncRtSurfaceTransactionApplierCompat(v);
@@ -215,9 +215,9 @@
}
});
- if (backgroundBlurController != null) {
- ObjectAnimator backgroundRadiusAnim = ObjectAnimator.ofInt(backgroundBlurController,
- BACKGROUND_BLUR, BACKGROUND_APP.getBackgroundBlurRadius(v.getContext()));
+ if (depthController != null) {
+ ObjectAnimator backgroundRadiusAnim = ObjectAnimator.ofFloat(depthController,
+ DEPTH, BACKGROUND_APP.getDepth(v.getContext()));
animatorSet.playTogether(appAnimator, backgroundRadiusAnim);
} else {
animatorSet.play(appAnimator);
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
index 25a3078..eb5c7f9 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
@@ -15,7 +15,9 @@
*/
package com.android.quickstep;
+import static android.view.MotionEvent.ACTION_CANCEL;
import static android.view.MotionEvent.ACTION_DOWN;
+import static android.view.MotionEvent.ACTION_UP;
import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
@@ -435,7 +437,8 @@
TraceHelper.FLAG_ALLOW_BINDER_TRACKING);
mDeviceState.setOrientationTransformIfNeeded(event);
- if (event.getAction() == ACTION_DOWN) {
+ final int action = event.getAction();
+ if (action == ACTION_DOWN) {
GestureState newGestureState = new GestureState(mOverviewComponentObserver,
ActiveGestureLog.INSTANCE.generateAndSetLogId());
newGestureState.updateRunningTask(TraceHelper.whitelistIpcs("getRunningTask.0",
@@ -468,6 +471,13 @@
ActiveGestureLog.INSTANCE.addLog("onMotionEvent", event.getActionMasked());
mUncheckedConsumer.onMotionEvent(event);
+
+ if (action == ACTION_UP || action == ACTION_CANCEL) {
+ if (mConsumer != null && !mConsumer.isConsumerDetachedFromGesture()) {
+ onConsumerInactive(mConsumer);
+ }
+ }
+
TraceHelper.INSTANCE.endFlagsOverride(traceToken);
}
@@ -661,8 +671,8 @@
*/
private void onConsumerInactive(InputConsumer caller) {
if (mConsumer == caller) {
- mConsumer = mResetGestureInputConsumer;
- mUncheckedConsumer = mConsumer;
+ mConsumer = mUncheckedConsumer = mResetGestureInputConsumer;
+ mGestureState = new GestureState();
}
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/RecentsRootView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/RecentsRootView.java
index 2c5d631..7f5ec9b 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/RecentsRootView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/RecentsRootView.java
@@ -81,7 +81,8 @@
if (!insets.equals(mInsets)) {
super.setInsets(insets);
}
- setBackground(insets.top == 0 ? null
+ setBackground(insets.top == 0 || !mAllowSysuiScrims
+ ? null
: Themes.getAttrDrawable(getContext(), R.attr.workspaceStatusBarScrim));
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DelegateInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DelegateInputConsumer.java
index 05c206f..a87e7eb 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DelegateInputConsumer.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DelegateInputConsumer.java
@@ -25,6 +25,11 @@
}
@Override
+ public boolean isConsumerDetachedFromGesture() {
+ return mDelegate.isConsumerDetachedFromGesture();
+ }
+
+ @Override
public boolean allowInterceptByParent() {
return mDelegate.allowInterceptByParent() && mState != STATE_ACTIVE;
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
index 893868b..416d7a1 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
@@ -160,6 +160,11 @@
return TYPE_OTHER_ACTIVITY;
}
+ @Override
+ public boolean isConsumerDetachedFromGesture() {
+ return true;
+ }
+
private void forceCancelGesture(MotionEvent ev) {
int action = ev.getAction();
ev.setAction(ACTION_CANCEL);
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/AppWindowAnimationHelper.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/AppWindowAnimationHelper.java
index 681ce02..d9e9cc7 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/AppWindowAnimationHelper.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/AppWindowAnimationHelper.java
@@ -50,6 +50,7 @@
import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
import static com.android.systemui.shared.system.QuickStepContract.getWindowCornerRadius;
import static com.android.systemui.shared.system.QuickStepContract.supportsRoundedCornersOnWindows;
+import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME;
import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_OPENING;
@@ -119,7 +120,7 @@
private void updateSourceStack(RemoteAnimationTargetCompat target) {
mSourceInsets.set(target.contentInsets);
- mSourceStackBounds.set(target.sourceContainerBounds);
+ mSourceStackBounds.set(target.screenSpaceBounds);
// TODO: Should sourceContainerBounds already have this offset?
mSourceStackBounds.offsetTo(target.position.x, target.position.y);
@@ -199,9 +200,17 @@
for (int i = 0; i < params.mTargetSet.unfilteredApps.length; i++) {
RemoteAnimationTargetCompat app = params.mTargetSet.unfilteredApps[i];
SurfaceParams.Builder builder = new SurfaceParams.Builder(app.leash);
- mTmpMatrix.setTranslate(app.position.x, app.position.y);
+ if (app.localBounds != null) {
+ mTmpMatrix.setTranslate(0, 0);
+ if (app.activityType == ACTIVITY_TYPE_HOME && app.mode == MODE_CLOSING) {
+ mTmpMatrix.setTranslate(app.localBounds.left, app.localBounds.top);
+ }
+ } else {
+ mTmpMatrix.setTranslate(app.position.x, app.position.y);
+ }
+
Rect crop = mTmpRect;
- crop.set(app.sourceContainerBounds);
+ crop.set(app.screenSpaceBounds);
crop.offsetTo(0, 0);
float alpha;
float cornerRadius = 0f;
@@ -211,7 +220,11 @@
alpha = mTaskAlphaCallback.getAlpha(app, params.mTargetAlpha);
if (app.activityType != RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME) {
mTmpMatrix.setRectToRect(mSourceRect, mCurrentRect, ScaleToFit.FILL);
- mTmpMatrix.postTranslate(app.position.x, app.position.y);
+ if (app.localBounds != null) {
+ mTmpMatrix.postTranslate(app.localBounds.left, app.localBounds.top);
+ } else {
+ mTmpMatrix.postTranslate(app.position.x, app.position.y);
+ }
mCurrentClipRectF.roundOut(crop);
if (mSupportsRoundedCornersOnWindows) {
if (params.mCornerRadius > -1) {
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java
index 7e17fbf..9cf45b3 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java
@@ -18,9 +18,10 @@
import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y;
import static com.android.launcher3.LauncherState.BACKGROUND_APP;
import static com.android.launcher3.LauncherState.NORMAL;
-import static com.android.launcher3.LauncherStateManager.ANIM_ALL_COMPONENTS;
-import static com.android.launcher3.LauncherStateManager.SKIP_OVERVIEW;
+import static com.android.launcher3.anim.Interpolators.ACCEL_DEACCEL;
import static com.android.launcher3.anim.Interpolators.LINEAR;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_COMPONENTS;
+import static com.android.launcher3.states.StateAnimationConfig.SKIP_OVERVIEW;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -33,14 +34,13 @@
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
-import com.android.launcher3.LauncherStateManager.AnimationConfig;
import com.android.launcher3.R;
import com.android.launcher3.ShortcutAndWidgetContainer;
import com.android.launcher3.Workspace;
-import com.android.launcher3.anim.AnimatorSetBuilder;
-import com.android.launcher3.anim.PropertySetter;
+import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.anim.SpringAnimationBuilder;
import com.android.launcher3.graphics.OverviewScrim;
+import com.android.launcher3.states.StateAnimationConfig;
import com.android.launcher3.util.DynamicResource;
import com.android.quickstep.views.RecentsView;
import com.android.systemui.plugins.ResourceProvider;
@@ -126,6 +126,8 @@
addScrimAnimationForState(launcher, NORMAL, ALPHA_DURATION_MS);
}
+ mAnimators.play(launcher.getDragLayer().getScrim().createSysuiMultiplierAnim(0f, 1f)
+ .setDuration(ALPHA_DURATION_MS));
mAnimators.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
@@ -143,16 +145,20 @@
* Setup workspace with 0 duration to prepare for our staggered animation.
*/
private void prepareToAnimate(Launcher launcher) {
- AnimatorSetBuilder builder = new AnimatorSetBuilder();
+ StateAnimationConfig config = new StateAnimationConfig();
+ config.animFlags = ANIM_ALL_COMPONENTS | SKIP_OVERVIEW;
+ config.duration = 0;
// setRecentsAttachedToAppWindow() will animate recents out.
- launcher.getStateManager().createAtomicAnimation(
- BACKGROUND_APP, NORMAL, builder, ANIM_ALL_COMPONENTS | SKIP_OVERVIEW, 0);
- builder.build().start();
+ launcher.getStateManager().createAtomicAnimation(BACKGROUND_APP, NORMAL, config).start();
// Stop scrolling so that it doesn't interfere with the translation offscreen.
launcher.<RecentsView>getOverviewPanel().getScroller().forceFinished(true);
}
+ public AnimatorSet getAnimators() {
+ return mAnimators;
+ }
+
/**
* Starts the animation.
*/
@@ -197,16 +203,12 @@
}
private void addScrimAnimationForState(Launcher launcher, LauncherState state, long duration) {
- AnimatorSetBuilder scrimAnimBuilder = new AnimatorSetBuilder();
- AnimationConfig scrimAnimConfig = new AnimationConfig();
- scrimAnimConfig.duration = duration;
- PropertySetter scrimPropertySetter = scrimAnimConfig.getPropertySetter(scrimAnimBuilder);
- launcher.getWorkspace().getStateTransitionAnimation().setScrim(scrimPropertySetter, state);
- mAnimators.play(scrimAnimBuilder.build());
- Animator fadeOverviewScrim = ObjectAnimator.ofFloat(
- launcher.getDragLayer().getOverviewScrim(), OverviewScrim.SCRIM_PROGRESS,
- state.getOverviewScrimAlpha(launcher));
- fadeOverviewScrim.setDuration(duration);
- mAnimators.play(fadeOverviewScrim);
+ PendingAnimation builder = new PendingAnimation(duration, mAnimators);
+ launcher.getWorkspace().getStateTransitionAnimation().setScrim(builder, state);
+ builder.setFloat(
+ launcher.getDragLayer().getOverviewScrim(),
+ OverviewScrim.SCRIM_PROGRESS,
+ state.getOverviewScrimAlpha(launcher),
+ ACCEL_DEACCEL);
}
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java
index 3ed7530..a027fea 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java
@@ -46,9 +46,9 @@
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.appprediction.PredictionUiStateManager;
import com.android.launcher3.appprediction.PredictionUiStateManager.Client;
-import com.android.launcher3.states.RotationHelper;
-import com.android.launcher3.uioverrides.BackgroundBlurController;
import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.states.RotationHelper;
+import com.android.launcher3.uioverrides.DepthController;
import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
import com.android.launcher3.util.TraceHelper;
import com.android.launcher3.views.ScrimView;
@@ -409,7 +409,7 @@
}
@Override
- protected BackgroundBlurController getBackgroundBlurController() {
- return mActivity.getBackgroundBlurController();
+ protected DepthController getDepthController() {
+ return mActivity.getDepthController();
}
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
index b5e6af4..68c51a0 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
@@ -19,6 +19,7 @@
import static com.android.launcher3.BaseActivity.STATE_HANDLER_INVISIBILITY_FLAGS;
import static com.android.launcher3.InvariantDeviceProfile.CHANGE_FLAG_ICON_PARAMS;
import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
+import static com.android.launcher3.LauncherAnimUtils.VIEW_ALPHA;
import static com.android.launcher3.LauncherState.BACKGROUND_APP;
import static com.android.launcher3.Utilities.EDGE_NAV_BAR;
import static com.android.launcher3.Utilities.squaredHypot;
@@ -29,7 +30,7 @@
import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.config.FeatureFlags.ENABLE_OVERVIEW_ACTIONS;
import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
-import static com.android.launcher3.uioverrides.BackgroundBlurController.BACKGROUND_BLUR;
+import static com.android.launcher3.uioverrides.DepthController.DEPTH;
import static com.android.launcher3.uioverrides.touchcontrollers.TaskViewTouchController.SUCCESS_TRANSITION_PROGRESS;
import static com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch.TAP;
import static com.android.launcher3.userevent.nano.LauncherLogProto.ControlType.CLEAR_ALL_BUTTON;
@@ -68,7 +69,6 @@
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.MotionEvent;
-import android.view.OrientationEventListener;
import android.view.View;
import android.view.ViewDebug;
import android.view.ViewGroup;
@@ -88,7 +88,6 @@
import com.android.launcher3.PagedView;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
-import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.anim.PendingAnimation.EndState;
@@ -96,11 +95,10 @@
import com.android.launcher3.anim.SpringProperty;
import com.android.launcher3.compat.AccessibilityManagerCompat;
import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.dragndrop.DragLayer;
import com.android.launcher3.graphics.RotationMode;
import com.android.launcher3.states.RotationHelper;
import com.android.launcher3.touch.PagedOrientationHandler.CurveProperties;
-import com.android.launcher3.uioverrides.BackgroundBlurController;
+import com.android.launcher3.uioverrides.DepthController;
import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
@@ -720,6 +718,9 @@
for (int i = 0; i < taskCount; i++) {
getTaskViewAt(i).setFullscreenProgress(mFullscreenProgress);
}
+ if (mActionsView != null) {
+ mActionsView.setVisibility(fullscreenProgress == 0 ? VISIBLE : INVISIBLE);
+ }
}
private void updateTaskStackListenerState() {
@@ -1165,7 +1166,9 @@
}
private void addDismissedTaskAnimations(View taskView, long duration, PendingAnimation anim) {
- anim.add(ObjectAnimator.ofFloat(taskView, ALPHA, 0).setDuration(duration), ACCEL_2);
+ // Use setFloat instead of setViewAlpha as we want to keep the view visible even when it's
+ // alpha is set to 0 so that it can be recycled in the view pool properly
+ anim.setFloat(taskView, VIEW_ALPHA, 0, ACCEL_2);
FloatProperty<View> secondaryViewTranslate =
mOrientationHandler.getSecondaryViewTranslate();
int secondaryTaskDimension = mOrientationHandler.getSecondaryDimension(taskView);
@@ -1195,7 +1198,7 @@
if (mPendingAnimation != null) {
mPendingAnimation.finish(false, Touch.SWIPE);
}
- PendingAnimation anim = new PendingAnimation();
+ PendingAnimation anim = new PendingAnimation(duration);
int count = getPageCount();
if (count == 0) {
@@ -1257,9 +1260,7 @@
}
if (needsCurveUpdates) {
- ValueAnimator va = ValueAnimator.ofFloat(0, 1);
- va.addUpdateListener((a) -> updateCurveProperties());
- anim.add(va);
+ anim.addOnFrameCallback(this::updateCurveProperties);
}
// Add a tiny bit of translation Z, so that it draws on top of other views
@@ -1279,6 +1280,7 @@
}
}
+ @SuppressWarnings("WrongCall")
private void onEnd(EndState endState) {
if (endState.isSuccess) {
if (shouldRemoveTask) {
@@ -1290,15 +1292,18 @@
pageToSnapTo == (getTaskViewCount() - 1)) {
pageToSnapTo -= 1;
}
- removeView(taskView);
+ removeViewInLayout(taskView);
if (getTaskViewCount() == 0) {
- removeView(mClearAllButton);
+ removeViewInLayout(mClearAllButton);
hideActionsView();
startHome();
} else {
snapToPageImmediately(pageToSnapTo);
}
+ // Update the layout synchronously so that the position of next view is
+ // immediately available.
+ onLayout(false /* changed */, getLeft(), getTop(), getRight(), getBottom());
}
resetTaskVisuals();
mPendingAnimation = null;
@@ -1311,7 +1316,7 @@
if (FeatureFlags.IS_STUDIO_BUILD && mPendingAnimation != null) {
throw new IllegalStateException("Another pending animation is still running");
}
- PendingAnimation anim = new PendingAnimation();
+ PendingAnimation anim = new PendingAnimation(duration);
int count = getTaskViewCount();
for (int i = 0; i < count; i++) {
@@ -1345,8 +1350,7 @@
}
private void runDismissAnimation(PendingAnimation pendingAnim) {
- AnimatorPlaybackController controller =
- AnimatorPlaybackController.wrap(pendingAnim, DISMISS_TASK_DURATION);
+ AnimatorPlaybackController controller = pendingAnim.createPlaybackController();
controller.dispatchOnStart();
controller.setEndAction(() -> pendingAnim.finish(true, Touch.SWIPE));
controller.getAnimationPlayer().setInterpolator(FAST_OUT_SLOW_IN);
@@ -1549,6 +1553,7 @@
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
+
updateEmptyStateUi(changed);
// Set the pivot points to match the task preview center
@@ -1671,7 +1676,7 @@
int count = getTaskViewCount();
if (count == 0) {
- return new PendingAnimation();
+ return new PendingAnimation(duration);
}
int targetSysUiFlags = tv.getThumbnail().getSysUiStatusNavFlags();
@@ -1703,17 +1708,16 @@
appWindowAnimationHelper.prepareAnimation(mActivity.getDeviceProfile(), true /* isOpening */);
AnimatorSet anim = createAdjacentPageAnimForTaskLaunch(tv, appWindowAnimationHelper);
- BackgroundBlurController blurController = getBackgroundBlurController();
- if (blurController != null) {
- ObjectAnimator backgroundBlur = ObjectAnimator.ofInt(blurController, BACKGROUND_BLUR,
- BACKGROUND_APP.getBackgroundBlurRadius(mActivity));
- anim.play(backgroundBlur);
+ DepthController depthController = getDepthController();
+ if (depthController != null) {
+ ObjectAnimator depthAnimator = ObjectAnimator.ofFloat(depthController, DEPTH,
+ BACKGROUND_APP.getDepth(mActivity));
+ anim.play(depthAnimator);
}
anim.play(progressAnim);
- anim.setDuration(duration)
- .setInterpolator(interpolator);
+ anim.setDuration(duration).setInterpolator(interpolator);
- mPendingAnimation = new PendingAnimation();
+ mPendingAnimation = new PendingAnimation(duration);
mPendingAnimation.add(anim);
mPendingAnimation.addEndListener((endState) -> {
if (endState.isSuccess) {
@@ -2011,7 +2015,7 @@
}
@Nullable
- protected BackgroundBlurController getBackgroundBlurController() {
+ protected DepthController getDepthController() {
return null;
}
@@ -2031,7 +2035,6 @@
void onEmptyMessageUpdated(boolean isEmpty);
}
-
private static class PinnedStackAnimationListener<T extends BaseActivity> extends
IPinnedStackAnimationListener.Stub {
private T mActivity;
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java
index 0e1640e..56e3632 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java
@@ -247,8 +247,17 @@
/** Updates UI based on whether the task is modal. */
public void updateUiForModalTask() {
+ boolean isOverlayModal = isTaskOverlayModal();
if (getRecentsView() != null) {
- getRecentsView().updateUiForModalTask(this, isTaskOverlayModal());
+ getRecentsView().updateUiForModalTask(this, isOverlayModal);
+ }
+ // Hide footers when overlay is modal.
+ if (isOverlayModal) {
+ for (FooterWrapper footer : mFooters) {
+ if (footer != null) {
+ footer.animateHide();
+ }
+ }
}
}
@@ -285,8 +294,7 @@
public AnimatorPlaybackController createLaunchAnimationForRunningTask() {
final PendingAnimation pendingAnimation = getRecentsView().createTaskLaunchAnimation(
this, RECENTS_LAUNCH_DURATION, TOUCH_RESPONSE_INTERPOLATOR);
- AnimatorPlaybackController currentAnimation =
- AnimatorPlaybackController.wrap(pendingAnimation, RECENTS_LAUNCH_DURATION);
+ AnimatorPlaybackController currentAnimation = pendingAnimation.createPlaybackController();
currentAnimation.setEndAction(() -> {
pendingAnimation.finish(true, Touch.SWIPE);
launchTask(false);
@@ -781,6 +789,22 @@
animator.setDuration(100);
animator.start();
}
+
+ void animateHide() {
+ ValueAnimator animator = ValueAnimator.ofFloat(0.0f, 1.0f);
+ animator.addUpdateListener(anim -> {
+ mFooterVerticalOffset = anim.getAnimatedFraction();
+ updateFooterOffset();
+ });
+ animator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ removeView(mView);
+ }
+ });
+ animator.setDuration(100);
+ animator.start();
+ }
}
@Override
diff --git a/quickstep/res/layout/back_gesture_tutorial_fragment.xml b/quickstep/res/layout/back_gesture_tutorial_fragment.xml
index 294e46e..d8c25bd 100644
--- a/quickstep/res/layout/back_gesture_tutorial_fragment.xml
+++ b/quickstep/res/layout/back_gesture_tutorial_fragment.xml
@@ -16,9 +16,7 @@
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:layerType="software"
android:background="@color/back_gesture_tutorial_background_color">
- <!--The layout is rendered on the software layer to avoid b/136158117-->
<ImageView
android:id="@+id/back_gesture_tutorial_fragment_hand_coaching"
diff --git a/quickstep/res/values/colors.xml b/quickstep/res/values/colors.xml
new file mode 100644
index 0000000..3583676
--- /dev/null
+++ b/quickstep/res/values/colors.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources>
+ <color name="back_arrow_color_dark">#99000000</color>
+</resources>
\ No newline at end of file
diff --git a/quickstep/res/values/strings.xml b/quickstep/res/values/strings.xml
index 90d4245..b55b042 100644
--- a/quickstep/res/values/strings.xml
+++ b/quickstep/res/values/strings.xml
@@ -68,30 +68,25 @@
<!-- Hotseat migration notification title -->
- <string translatable="false" name="hotseat_edu_prompt_title">Get app suggestions based on your routines</string>
+ <string name="hotseat_edu_prompt_title">Easily access your most-used apps</string>
<!-- Hotseat migration notification content -->
- <string translatable="false" name="hotseat_edu_prompt_content">Tap to set up</string>
-
+ <string name="hotseat_edu_prompt_content">Pixel predicts apps you\’ll need next, right on your Home screen. Tap to set up.</string>
<!-- Hotseat educational strings for users who don't qualify for migration -->
- <string translatable="false" name="hotseat_edu_title_migrate">Suggested apps replace the bottom row of apps</string>
- <string translatable="false" name="hotseat_edu_message_migrate">Your hotseat items will be moved up on the homescreen</string>
- <string translatable="false" name="hotseat_edu_message_migrate_alt">Your hotseat items will be moved to the last page of your workspace</string>
+ <string name="hotseat_edu_title_migrate">Get app suggestions on the bottom row of your Home screen</string>
-
- <!-- Hotseat educational strings for users who don't qualify -->
- <string translatable="false" name="hotseat_edu_title_no_migrate">Suggested apps will be found at the bottom row of your home screen</string>
- <string translatable="false" name="hotseat_edu_message_no_migrate">Drag one or many apps off the bottom row of home screen to see app suggestions</string>
+ <string name="hotseat_edu_message_migrate">Easily access your most-used apps right on the Home screen. Suggestions will change based on your routines. Apps on the bottom row will move up to your Home screen. </string>
+ <string name="hotseat_edu_message_migrate_alt">Easily access your most-used apps, right on the Home screen. Suggestions will change based on your routines. Apps on the bottom row will move to a new folder.</string>
<!-- Toast message user sees after opting into fully predicted hybrid hotseat -->
- <string translatable="false" name="hotseat_items_migrated">Bottom row of apps moved up.</string>
- <string translatable="false" name="hotseat_items_migrated_alt">Bottom row of apps moved to last page.</string>
+ <string name="hotseat_items_migrated">Your hotseat items have been moved up to your homescreen</string>
+ <string name="hotseat_items_migrated_alt">Your hotseat items have been moved to a folder</string>
<!-- Toast message user sees after opting into fully predicted hybrid hotseat -->
- <string translatable="false" name="hotseat_no_migration">Bottom row won\'t be replaced. Manually drag apps for predictions.</string>
+ <string name="hotseat_no_migration">Drag apps off the bottom row to see app suggestions</string>
<!-- Button text to opt in for fully predicted hotseat -->
- <string translatable="false" name="hotseat_edu_accept">Got it</string>
+ <string name="hotseat_edu_accept">Get app suggestions</string>
<!-- Button text to dismiss opt in for fully predicted hotseat -->
- <string translatable="false" name="hotseat_edu_dismiss">No thanks</string>
+ <string name="hotseat_edu_dismiss">No thanks</string>
<!-- Title shown during interactive part of Back gesture tutorial for right edge. [CHAR LIMIT=30] -->
diff --git a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
index 809543a..ec66f11 100644
--- a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
@@ -52,10 +52,12 @@
import com.android.quickstep.SysUINavigationMode.Mode;
import com.android.quickstep.SysUINavigationMode.NavigationModeChangeListener;
import com.android.quickstep.SystemUiProxy;
+import com.android.quickstep.util.RemoteAnimationProvider;
import com.android.quickstep.util.RemoteFadeOutAnimationListener;
import com.android.quickstep.util.ShelfPeekAnim;
import com.android.quickstep.views.RecentsView;
import com.android.systemui.shared.system.ActivityManagerWrapper;
+import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
import java.util.stream.Stream;
@@ -242,7 +244,7 @@
return new StateHandler[] {
getAllAppsController(),
getWorkspace(),
- getBackgroundBlurController(),
+ getDepthController(),
new RecentsViewStateController(this),
new BackButtonAlphaHandler(this)};
}
@@ -262,17 +264,21 @@
public void useFadeOutAnimationForLauncherStart(CancellationSignal signal) {
QuickstepAppTransitionManagerImpl appTransitionManager =
(QuickstepAppTransitionManagerImpl) getAppTransitionManager();
- appTransitionManager.setRemoteAnimationProvider((appTargets, wallpaperTargets) -> {
+ appTransitionManager.setRemoteAnimationProvider(new RemoteAnimationProvider() {
+ @Override
+ public AnimatorSet createWindowAnimation(RemoteAnimationTargetCompat[] appTargets,
+ RemoteAnimationTargetCompat[] wallpaperTargets) {
- // On the first call clear the reference.
- signal.cancel();
+ // On the first call clear the reference.
+ signal.cancel();
- ValueAnimator fadeAnimation = ValueAnimator.ofFloat(1, 0);
- fadeAnimation.addUpdateListener(new RemoteFadeOutAnimationListener(appTargets,
- wallpaperTargets));
- AnimatorSet anim = new AnimatorSet();
- anim.play(fadeAnimation);
- return anim;
+ ValueAnimator fadeAnimation = ValueAnimator.ofFloat(1, 0);
+ fadeAnimation.addUpdateListener(new RemoteFadeOutAnimationListener(appTargets,
+ wallpaperTargets));
+ AnimatorSet anim = new AnimatorSet();
+ anim.play(fadeAnimation);
+ return anim;
+ }
}, signal);
}
diff --git a/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java b/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java
index 31c1acf..2cb23f1 100644
--- a/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java
+++ b/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java
@@ -34,7 +34,8 @@
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
@TargetApi(Build.VERSION_CODES.P)
-public abstract class LauncherAnimationRunner implements RemoteAnimationRunnerCompat {
+public abstract class LauncherAnimationRunner implements RemoteAnimationRunnerCompat,
+ WrappedAnimationRunnerImpl {
private final Handler mHandler;
private final boolean mStartAtFrontOfQueue;
@@ -49,6 +50,10 @@
mStartAtFrontOfQueue = startAtFrontOfQueue;
}
+ public Handler getHandler() {
+ return mHandler;
+ }
+
// Called only in R+ platform
@BinderThread
public void onAnimationStart(RemoteAnimationTargetCompat[] appTargets,
diff --git a/quickstep/src/com/android/launcher3/LauncherInitListener.java b/quickstep/src/com/android/launcher3/LauncherInitListener.java
index 96340b2..fbd7a8a 100644
--- a/quickstep/src/com/android/launcher3/LauncherInitListener.java
+++ b/quickstep/src/com/android/launcher3/LauncherInitListener.java
@@ -15,6 +15,7 @@
*/
package com.android.launcher3;
+import android.animation.AnimatorSet;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.Intent;
@@ -25,6 +26,7 @@
import com.android.launcher3.util.ActivityTracker;
import com.android.quickstep.util.ActivityInitListener;
import com.android.quickstep.util.RemoteAnimationProvider;
+import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
import java.util.function.BiPredicate;
@@ -51,17 +53,21 @@
// Set a one-time animation provider. After the first call, this will get cleared.
// TODO: Probably also check the intended target id.
CancellationSignal cancellationSignal = new CancellationSignal();
- appTransitionManager.setRemoteAnimationProvider((appTargets, wallpaperTargets) -> {
+ appTransitionManager.setRemoteAnimationProvider(new RemoteAnimationProvider() {
+ @Override
+ public AnimatorSet createWindowAnimation(RemoteAnimationTargetCompat[] appTargets,
+ RemoteAnimationTargetCompat[] wallpaperTargets) {
- // On the first call clear the reference.
- cancellationSignal.cancel();
- RemoteAnimationProvider provider = mRemoteAnimationProvider;
- mRemoteAnimationProvider = null;
+ // On the first call clear the reference.
+ cancellationSignal.cancel();
+ RemoteAnimationProvider provider = mRemoteAnimationProvider;
+ mRemoteAnimationProvider = null;
- if (provider != null && launcher.getStateManager().getState().overviewUi) {
- return provider.createWindowAnimation(appTargets, wallpaperTargets);
+ if (provider != null && launcher.getStateManager().getState().overviewUi) {
+ return provider.createWindowAnimation(appTargets, wallpaperTargets);
+ }
+ return null;
}
- return null;
}, cancellationSignal);
}
launcher.deferOverlayCallbacksUntilNextResumeOrStop();
diff --git a/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java b/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java
index f691359..c93a4ba 100644
--- a/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java
+++ b/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java
@@ -16,6 +16,8 @@
package com.android.launcher3;
+import static android.util.TypedValue.COMPLEX_UNIT_DIP;
+
import static com.android.launcher3.BaseActivity.INVISIBLE_ALL;
import static com.android.launcher3.BaseActivity.INVISIBLE_BY_APP_TRANSITIONS;
import static com.android.launcher3.BaseActivity.INVISIBLE_BY_PENDING_FLAGS;
@@ -29,8 +31,9 @@
import static com.android.launcher3.anim.Interpolators.DEACCEL_1_7;
import static com.android.launcher3.anim.Interpolators.EXAGGERATED_EASE;
import static com.android.launcher3.anim.Interpolators.LINEAR;
+import static com.android.launcher3.config.FeatureFlags.KEYGUARD_ANIMATION;
import static com.android.launcher3.dragndrop.DragLayer.ALPHA_INDEX_TRANSITIONS;
-import static com.android.launcher3.uioverrides.BackgroundBlurController.BACKGROUND_BLUR;
+import static com.android.launcher3.uioverrides.DepthController.DEPTH;
import static com.android.launcher3.views.FloatingIconView.SHAPE_PROGRESS_DURATION;
import static com.android.quickstep.TaskUtils.taskIsATargetWithMode;
import static com.android.systemui.shared.system.QuickStepContract.getWindowCornerRadius;
@@ -49,6 +52,7 @@
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.graphics.Matrix;
+import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
@@ -57,6 +61,7 @@
import android.os.Handler;
import android.os.Looper;
import android.util.Pair;
+import android.util.TypedValue;
import android.view.View;
import androidx.annotation.NonNull;
@@ -64,17 +69,18 @@
import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener;
import com.android.launcher3.allapps.AllAppsTransitionController;
-import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.dragndrop.DragLayer;
import com.android.launcher3.shortcuts.DeepShortcutView;
-import com.android.launcher3.uioverrides.BackgroundBlurController;
+import com.android.launcher3.uioverrides.DepthController;
+import com.android.launcher3.util.DynamicResource;
import com.android.launcher3.util.MultiValueAlpha;
import com.android.launcher3.util.MultiValueAlpha.AlphaProperty;
import com.android.launcher3.views.FloatingIconView;
import com.android.quickstep.RemoteAnimationTargets;
import com.android.quickstep.util.MultiValueUpdateListener;
import com.android.quickstep.util.RemoteAnimationProvider;
+import com.android.quickstep.util.StaggeredWorkspaceAnim;
import com.android.systemui.shared.system.ActivityCompat;
import com.android.systemui.shared.system.ActivityOptionsCompat;
import com.android.systemui.shared.system.QuickStepContract;
@@ -86,8 +92,6 @@
import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams;
import com.android.systemui.shared.system.WindowManagerWrapper;
-import java.lang.ref.WeakReference;
-
/**
* {@link LauncherAppTransitionManager} with Quickstep-specific app transitions for launching from
* home and/or all-apps. Not used for 3p launchers.
@@ -157,6 +161,7 @@
// Strong refs to runners which are cleared when the launcher activity is destroyed
private WrappedAnimationRunnerImpl mWallpaperOpenRunner;
private WrappedAnimationRunnerImpl mAppLaunchRunner;
+ private WrappedAnimationRunnerImpl mKeyguardGoingAwayRunner;
private final AnimatorListenerAdapter mForceInvisibleListener = new AnimatorListenerAdapter() {
@Override
@@ -299,8 +304,12 @@
if (mLauncher.isInMultiWindowMode()) {
for (RemoteAnimationTargetCompat target : appTargets) {
if (target.mode == MODE_OPENING) {
- bounds.set(target.sourceContainerBounds);
- bounds.offsetTo(target.position.x, target.position.y);
+ bounds.set(target.screenSpaceBounds);
+ if (target.localBounds != null) {
+ bounds.set(target.localBounds);
+ } else {
+ bounds.offsetTo(target.position.x, target.position.y);
+ }
return bounds;
}
}
@@ -377,18 +386,35 @@
alpha.setInterpolator(LINEAR);
launcherAnimator.play(alpha);
- mDragLayer.setTranslationY(trans[0]);
- ObjectAnimator transY = ObjectAnimator.ofFloat(mDragLayer, View.TRANSLATION_Y, trans);
- transY.setInterpolator(AGGRESSIVE_EASE);
- transY.setDuration(CONTENT_TRANSLATION_DURATION);
- launcherAnimator.play(transY);
+ Workspace workspace = mLauncher.getWorkspace();
+ View currentPage = ((CellLayout) workspace.getChildAt(workspace.getCurrentPage()))
+ .getShortcutsAndWidgets();
+ View hotseat = mLauncher.getHotseat();
+ View qsb = mLauncher.findViewById(R.id.search_container_all_apps);
- mDragLayer.getScrim().hideSysUiScrim(true);
+ currentPage.setLayerType(View.LAYER_TYPE_HARDWARE, null);
+ hotseat.setLayerType(View.LAYER_TYPE_HARDWARE, null);
+ qsb.setLayerType(View.LAYER_TYPE_HARDWARE, null);
+
+ launcherAnimator.play(ObjectAnimator.ofFloat(currentPage, View.TRANSLATION_Y, trans));
+ launcherAnimator.play(ObjectAnimator.ofFloat(hotseat, View.TRANSLATION_Y, trans));
+ launcherAnimator.play(ObjectAnimator.ofFloat(qsb, View.TRANSLATION_Y, trans));
+
// Pause page indicator animations as they lead to layer trashing.
mLauncher.getWorkspace().getPageIndicator().pauseAnimations();
- mDragLayer.setLayerType(View.LAYER_TYPE_HARDWARE, null);
- endListener = this::resetContentView;
+ endListener = () -> {
+ currentPage.setTranslationY(0);
+ hotseat.setTranslationY(0);
+ qsb.setTranslationY(0);
+
+ currentPage.setLayerType(View.LAYER_TYPE_NONE, null);
+ hotseat.setLayerType(View.LAYER_TYPE_NONE, null);
+ qsb.setLayerType(View.LAYER_TYPE_NONE, null);
+
+ mDragLayerAlpha.setValue(1f);
+ mLauncher.getWorkspace().getPageIndicator().skipAnimationsToEnd();
+ };
}
return new Pair<>(launcherAnimator, endListener);
}
@@ -460,6 +486,7 @@
RectF targetBounds = new RectF(windowTargetBounds);
RectF currentBounds = new RectF();
RectF temp = new RectF();
+ Point tmpPos = new Point();
AnimatorSet animatorSet = new AnimatorSet();
ValueAnimator appAnimator = ValueAnimator.ofFloat(0, 1);
@@ -547,6 +574,13 @@
for (int i = appTargets.length - 1; i >= 0; i--) {
RemoteAnimationTargetCompat target = appTargets[i];
SurfaceParams.Builder builder = new SurfaceParams.Builder(target.leash);
+
+ tmpPos.set(target.position.x, target.position.y);
+ if (target.localBounds != null) {
+ final Rect localBounds = target.localBounds;
+ tmpPos.set(target.localBounds.left, target.localBounds.top);
+ }
+
if (target.mode == MODE_OPENING) {
matrix.setScale(scale, scale);
matrix.postTranslate(transX0, transY0);
@@ -563,9 +597,9 @@
.withAlpha(1f - mIconAlpha.value)
.withCornerRadius(mWindowRadius.value);
} else {
- matrix.setTranslate(target.position.x, target.position.y);
+ matrix.setTranslate(tmpPos.x, tmpPos.y);
builder.withMatrix(matrix)
- .withWindowCrop(target.sourceContainerBounds)
+ .withWindowCrop(target.screenSpaceBounds)
.withAlpha(1f);
}
builder.withLayer(RemoteAnimationProvider.getLayer(target, MODE_OPENING));
@@ -578,17 +612,17 @@
// When launching an app from overview that doesn't map to a task, we still want to just
// blur the wallpaper instead of the launcher surface as well
boolean allowBlurringLauncher = mLauncher.getStateManager().getState() != OVERVIEW;
- BackgroundBlurController blurController = mLauncher.getBackgroundBlurController();
- ObjectAnimator backgroundRadiusAnim = ObjectAnimator.ofInt(blurController, BACKGROUND_BLUR,
- BACKGROUND_APP.getBackgroundBlurRadius(mLauncher))
+ DepthController depthController = mLauncher.getDepthController();
+ ObjectAnimator backgroundRadiusAnim = ObjectAnimator.ofFloat(depthController, DEPTH,
+ BACKGROUND_APP.getDepth(mLauncher))
.setDuration(APP_LAUNCH_DURATION);
if (allowBlurringLauncher) {
- blurController.setSurfaceToApp(RemoteAnimationProvider.findLowestOpaqueLayerTarget(
+ depthController.setSurfaceToApp(RemoteAnimationProvider.findLowestOpaqueLayerTarget(
appTargets, MODE_OPENING));
backgroundRadiusAnim.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
- blurController.setSurfaceToLauncher(mLauncher.getDragLayer());
+ depthController.setSurfaceToLauncher(mLauncher.getDragLayer());
}
});
}
@@ -612,6 +646,17 @@
new WrappedLauncherAnimationRunner<>(mWallpaperOpenRunner,
false /* startAtFrontOfQueue */),
CLOSING_TRANSITION_DURATION_MS, 0 /* statusBarTransitionDelay */));
+
+ if (KEYGUARD_ANIMATION.get()) {
+ mKeyguardGoingAwayRunner = createWallpaperOpenRunner(true /* fromUnlock */);
+ definition.addRemoteAnimation(
+ WindowManagerWrapper.TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER,
+ new RemoteAnimationAdapterCompat(
+ new WrappedLauncherAnimationRunner<>(mKeyguardGoingAwayRunner,
+ true /* startAtFrontOfQueue */),
+ CLOSING_TRANSITION_DURATION_MS, 0 /* statusBarTransitionDelay */));
+ }
+
new ActivityCompat(mLauncher).registerRemoteAnimations(definition);
}
}
@@ -628,6 +673,7 @@
// definition so we don't have to wait for the system gc
mWallpaperOpenRunner = null;
mAppLaunchRunner = null;
+ mKeyguardGoingAwayRunner = null;
}
}
@@ -662,7 +708,7 @@
RemoteAnimationTargetCompat target = appTargets[i];
params[i] = new SurfaceParams.Builder(target.leash)
.withAlpha(1f)
- .withWindowCrop(target.sourceContainerBounds)
+ .withWindowCrop(target.screenSpaceBounds)
.withLayer(RemoteAnimationProvider.getLayer(target, MODE_OPENING))
.withCornerRadius(cornerRadius)
.build();
@@ -681,6 +727,7 @@
SyncRtSurfaceTransactionApplierCompat surfaceApplier =
new SyncRtSurfaceTransactionApplierCompat(mDragLayer);
Matrix matrix = new Matrix();
+ Point tmpPos = new Point();
ValueAnimator closingAnimator = ValueAnimator.ofFloat(0, 1);
int duration = CLOSING_TRANSITION_DURATION_MS;
float windowCornerRadius = mDeviceProfile.isMultiWindowMode
@@ -697,22 +744,28 @@
for (int i = appTargets.length - 1; i >= 0; i--) {
RemoteAnimationTargetCompat target = appTargets[i];
SurfaceParams.Builder builder = new SurfaceParams.Builder(target.leash);
+
+ tmpPos.set(target.position.x, target.position.y);
+ if (target.localBounds != null) {
+ tmpPos.set(target.localBounds.left, target.localBounds.top);
+ }
+
if (target.mode == MODE_CLOSING) {
matrix.setScale(mScale.value, mScale.value,
- target.sourceContainerBounds.centerX(),
- target.sourceContainerBounds.centerY());
+ target.screenSpaceBounds.centerX(),
+ target.screenSpaceBounds.centerY());
matrix.postTranslate(0, mDy.value);
- matrix.postTranslate(target.position.x, target.position.y);
+ matrix.postTranslate(tmpPos.x, tmpPos.y);
builder.withMatrix(matrix)
.withAlpha(mAlpha.value)
.withCornerRadius(windowCornerRadius);
} else {
- matrix.setTranslate(target.position.x, target.position.y);
+ matrix.setTranslate(tmpPos.x, tmpPos.y);
builder.withMatrix(matrix)
.withAlpha(1f);
}
params[i] = builder
- .withWindowCrop(target.sourceContainerBounds)
+ .withWindowCrop(target.screenSpaceBounds)
.withLayer(RemoteAnimationProvider.getLayer(target, MODE_CLOSING))
.build();
}
@@ -723,114 +776,12 @@
return closingAnimator;
}
- /**
- * Creates an animator that modifies Launcher as a result from
- * {@link #createWallpaperOpenRunner}.
- */
- private void createLauncherResumeAnimation(AnimatorSet anim) {
- if (mLauncher.isInState(LauncherState.ALL_APPS)) {
- Pair<AnimatorSet, Runnable> contentAnimator =
- getLauncherContentAnimator(false /* isAppOpening */,
- new float[] {-mContentTransY, 0});
- contentAnimator.first.setStartDelay(LAUNCHER_RESUME_START_DELAY);
- anim.play(contentAnimator.first);
- anim.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- contentAnimator.second.run();
- }
- });
- } else {
- AnimatorSet workspaceAnimator = new AnimatorSet();
-
- mDragLayer.setTranslationY(-mWorkspaceTransY);;
- workspaceAnimator.play(ObjectAnimator.ofFloat(mDragLayer, View.TRANSLATION_Y,
- -mWorkspaceTransY, 0));
-
- mDragLayerAlpha.setValue(0);
- workspaceAnimator.play(ObjectAnimator.ofFloat(
- mDragLayerAlpha, MultiValueAlpha.VALUE, 0, 1f));
-
- workspaceAnimator.setStartDelay(LAUNCHER_RESUME_START_DELAY);
- workspaceAnimator.setDuration(333);
- workspaceAnimator.setInterpolator(Interpolators.DEACCEL_1_7);
-
- mDragLayer.getScrim().hideSysUiScrim(true);
-
- // Pause page indicator animations as they lead to layer trashing.
- mLauncher.getWorkspace().getPageIndicator().pauseAnimations();
- mDragLayer.setLayerType(View.LAYER_TYPE_HARDWARE, null);
-
- workspaceAnimator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- resetContentView();
- }
- });
- anim.play(workspaceAnimator);
- }
- }
-
- private void resetContentView() {
- mLauncher.getWorkspace().getPageIndicator().skipAnimationsToEnd();
- mDragLayerAlpha.setValue(1f);
- mDragLayer.setLayerType(View.LAYER_TYPE_NONE, null);
- mDragLayer.setTranslationY(0f);
- mDragLayer.getScrim().hideSysUiScrim(false);
- }
-
private boolean hasControlRemoteAppTransitionPermission() {
return mLauncher.checkSelfPermission(CONTROL_REMOTE_APP_TRANSITION_PERMISSION)
== PackageManager.PERMISSION_GRANTED;
}
/**
- * Used with WrappedLauncherAnimationRunner as an interface for the runner to call back to the
- * implementation.
- */
- protected interface WrappedAnimationRunnerImpl {
- Handler getHandler();
- void onCreateAnimation(RemoteAnimationTargetCompat[] appTargets,
- RemoteAnimationTargetCompat[] wallpaperTargets,
- LauncherAnimationRunner.AnimationResult result);
- }
-
- /**
- * This class is needed to wrap any animation runner that is a part of the
- * RemoteAnimationDefinition:
- * - Launcher creates a new instance of the LauncherAppTransitionManagerImpl whenever it is
- * created, which in turn registers a new definition
- * - When the definition is registered, window manager retains a strong binder reference to the
- * runner passed in
- * - If the Launcher activity is recreated, the new definition registered will replace the old
- * reference in the system's activity record, but until the system server is GC'd, the binder
- * reference will still exist, which references the runner in the Launcher process, which
- * references the (old) Launcher activity through this class
- *
- * Instead we make the runner provided to the definition static only holding a weak reference to
- * the runner implementation. When this animation manager is destroyed, we remove the Launcher
- * reference to the runner, leaving only the weak ref from the runner.
- */
- protected static class WrappedLauncherAnimationRunner<R extends WrappedAnimationRunnerImpl>
- extends LauncherAnimationRunner {
- private WeakReference<R> mImpl;
-
- public WrappedLauncherAnimationRunner(R animationRunnerImpl, boolean startAtFrontOfQueue) {
- super(animationRunnerImpl.getHandler(), startAtFrontOfQueue);
- mImpl = new WeakReference<>(animationRunnerImpl);
- }
-
- @Override
- public void onCreateAnimation(RemoteAnimationTargetCompat[] appTargets,
- RemoteAnimationTargetCompat[] wallpaperTargets, AnimationResult result) {
- R animationRunnerImpl = mImpl.get();
- if (animationRunnerImpl != null) {
- animationRunnerImpl.onCreateAnimation(appTargets, wallpaperTargets, result);
- }
- }
- }
-
- /**
* Remote animation runner for animation from the app to Launcher, including recents.
*/
protected class WallpaperOpenLauncherAnimationRunner implements WrappedAnimationRunnerImpl {
@@ -896,11 +847,12 @@
|| mLauncher.isForceInvisible()) {
// Only register the content animation for cancellation when state changes
mLauncher.getStateManager().setCurrentAnimation(anim);
- if (mFromUnlock) {
+
+ if (mLauncher.isInState(LauncherState.ALL_APPS)) {
Pair<AnimatorSet, Runnable> contentAnimator =
getLauncherContentAnimator(false /* isAppOpening */,
- new float[] {mContentTransY, 0});
- contentAnimator.first.setStartDelay(0);
+ new float[] {-mContentTransY, 0});
+ contentAnimator.first.setStartDelay(LAUNCHER_RESUME_START_DELAY);
anim.play(contentAnimator.first);
anim.addListener(new AnimatorListenerAdapter() {
@Override
@@ -909,7 +861,12 @@
}
});
} else {
- createLauncherResumeAnimation(anim);
+ float velocityDpPerS = DynamicResource.provider(mLauncher)
+ .getDimension(R.dimen.unlock_staggered_velocity_dp_per_s);
+ float velocityPxPerS = TypedValue.applyDimension(COMPLEX_UNIT_DIP,
+ velocityDpPerS, mLauncher.getResources().getDisplayMetrics());
+ anim.play(new StaggeredWorkspaceAnim(mLauncher, velocityPxPerS, false)
+ .getAnimators());
}
}
}
diff --git a/quickstep/src/com/android/launcher3/WrappedAnimationRunnerImpl.java b/quickstep/src/com/android/launcher3/WrappedAnimationRunnerImpl.java
new file mode 100644
index 0000000..da2aee4
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/WrappedAnimationRunnerImpl.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3;
+
+import android.os.Handler;
+
+import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
+
+/**
+ * Used with WrappedLauncherAnimationRunner as an interface for the runner to call back to the
+ * implementation.
+ */
+public interface WrappedAnimationRunnerImpl {
+ Handler getHandler();
+ void onCreateAnimation(RemoteAnimationTargetCompat[] appTargets,
+ RemoteAnimationTargetCompat[] wallpaperTargets,
+ LauncherAnimationRunner.AnimationResult result);
+}
diff --git a/quickstep/src/com/android/launcher3/WrappedLauncherAnimationRunner.java b/quickstep/src/com/android/launcher3/WrappedLauncherAnimationRunner.java
new file mode 100644
index 0000000..1753b62
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/WrappedLauncherAnimationRunner.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3;
+
+import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
+
+import java.lang.ref.WeakReference;
+
+/**
+ * This class is needed to wrap any animation runner that is a part of the
+ * RemoteAnimationDefinition:
+ * - Launcher creates a new instance of the LauncherAppTransitionManagerImpl whenever it is
+ * created, which in turn registers a new definition
+ * - When the definition is registered, window manager retains a strong binder reference to the
+ * runner passed in
+ * - If the Launcher activity is recreated, the new definition registered will replace the old
+ * reference in the system's activity record, but until the system server is GC'd, the binder
+ * reference will still exist, which references the runner in the Launcher process, which
+ * references the (old) Launcher activity through this class
+ *
+ * Instead we make the runner provided to the definition static only holding a weak reference to
+ * the runner implementation. When this animation manager is destroyed, we remove the Launcher
+ * reference to the runner, leaving only the weak ref from the runner.
+ */
+public class WrappedLauncherAnimationRunner<R extends WrappedAnimationRunnerImpl>
+ extends LauncherAnimationRunner {
+ private WeakReference<R> mImpl;
+
+ public WrappedLauncherAnimationRunner(R animationRunnerImpl, boolean startAtFrontOfQueue) {
+ super(animationRunnerImpl.getHandler(), startAtFrontOfQueue);
+ mImpl = new WeakReference<>(animationRunnerImpl);
+ }
+
+ @Override
+ public void onCreateAnimation(RemoteAnimationTargetCompat[] appTargets,
+ RemoteAnimationTargetCompat[] wallpaperTargets, AnimationResult result) {
+ R animationRunnerImpl = mImpl.get();
+ if (animationRunnerImpl != null) {
+ animationRunnerImpl.onCreateAnimation(appTargets, wallpaperTargets, result);
+ }
+ }
+}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/BackButtonAlphaHandler.java b/quickstep/src/com/android/launcher3/uioverrides/BackButtonAlphaHandler.java
index 671aab0..e82a504 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/BackButtonAlphaHandler.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/BackButtonAlphaHandler.java
@@ -16,19 +16,23 @@
package com.android.launcher3.uioverrides;
-import android.animation.ValueAnimator;
+import static com.android.launcher3.anim.Interpolators.LINEAR;
+import static com.android.quickstep.AnimatedFloat.VALUE;
import com.android.launcher3.BaseQuickstepLauncher;
import com.android.launcher3.LauncherState;
import com.android.launcher3.LauncherStateManager;
-import com.android.launcher3.anim.AnimatorSetBuilder;
+import com.android.launcher3.anim.PendingAnimation;
+import com.android.launcher3.states.StateAnimationConfig;
import com.android.launcher3.util.UiThreadHelper;
+import com.android.quickstep.AnimatedFloat;
import com.android.quickstep.SysUINavigationMode;
import com.android.quickstep.SystemUiProxy;
public class BackButtonAlphaHandler implements LauncherStateManager.StateHandler {
private final BaseQuickstepLauncher mLauncher;
+ private final AnimatedFloat mBackAlpha = new AnimatedFloat(this::updateBackAlpha);
public BackButtonAlphaHandler(BaseQuickstepLauncher launcher) {
mLauncher = launcher;
@@ -38,8 +42,8 @@
public void setState(LauncherState state) { }
@Override
- public void setStateWithAnimation(LauncherState toState,
- AnimatorSetBuilder builder, LauncherStateManager.AnimationConfig config) {
+ public void setStateWithAnimation(LauncherState toState, StateAnimationConfig config,
+ PendingAnimation animation) {
if (config.onlyPlayAtomicComponent()) {
return;
}
@@ -51,17 +55,12 @@
return;
}
- float fromAlpha = SystemUiProxy.INSTANCE.get(mLauncher).getLastBackButtonAlpha();
- float toAlpha = toState.hideBackButton ? 0 : 1;
- if (Float.compare(fromAlpha, toAlpha) != 0) {
- ValueAnimator anim = ValueAnimator.ofFloat(fromAlpha, toAlpha);
- anim.setDuration(config.duration);
- anim.addUpdateListener(valueAnimator -> {
- final float alpha = (float) valueAnimator.getAnimatedValue();
- UiThreadHelper.setBackButtonAlphaAsync(mLauncher,
- BaseQuickstepLauncher.SET_BACK_BUTTON_ALPHA, alpha, false /* animate */);
- });
- builder.play(anim);
- }
+ mBackAlpha.value = SystemUiProxy.INSTANCE.get(mLauncher).getLastBackButtonAlpha();
+ animation.setFloat(mBackAlpha, VALUE, toState.hideBackButton ? 0 : 1, LINEAR);
+ }
+
+ private void updateBackAlpha() {
+ UiThreadHelper.setBackButtonAlphaAsync(mLauncher,
+ BaseQuickstepLauncher.SET_BACK_BUTTON_ALPHA, mBackAlpha.value, false /* animate */);
}
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/BackgroundBlurController.java b/quickstep/src/com/android/launcher3/uioverrides/BackgroundBlurController.java
deleted file mode 100644
index 022a5f7..0000000
--- a/quickstep/src/com/android/launcher3/uioverrides/BackgroundBlurController.java
+++ /dev/null
@@ -1,161 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher3.uioverrides;
-
-import static com.android.launcher3.anim.Interpolators.LINEAR;
-
-import android.util.IntProperty;
-import android.view.View;
-
-import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherState;
-import com.android.launcher3.LauncherStateManager;
-import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
-import com.android.launcher3.anim.AnimatorSetBuilder;
-import com.android.launcher3.anim.PropertySetter;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
-import com.android.systemui.shared.system.SurfaceControlCompat;
-import com.android.systemui.shared.system.TransactionCompat;
-
-/**
- * Controls the blur, for the Launcher surface only.
- */
-public class BackgroundBlurController implements LauncherStateManager.StateHandler {
-
- public static final IntProperty<BackgroundBlurController> BACKGROUND_BLUR =
- new IntProperty<BackgroundBlurController>("backgroundBlur") {
- @Override
- public void setValue(BackgroundBlurController blurController, int blurRadius) {
- blurController.setBackgroundBlurRadius(blurRadius);
- }
-
- @Override
- public Integer get(BackgroundBlurController blurController) {
- return blurController.mBackgroundBlurRadius;
- }
- };
-
- /**
- * A property that updates the background blur within a given range of values (ie. even if the
- * animator goes beyond 0..1, the interpolated value will still be bounded).
- */
- public static class ClampedBlurProperty extends IntProperty<BackgroundBlurController> {
- private final int mMinValue;
- private final int mMaxValue;
-
- public ClampedBlurProperty(int minValue, int maxValue) {
- super(("backgroundBlurClamped"));
- mMinValue = minValue;
- mMaxValue = maxValue;
- }
-
- @Override
- public void setValue(BackgroundBlurController blurController, int blurRadius) {
- blurController.setBackgroundBlurRadius(Utilities.boundToRange(blurRadius,
- mMinValue, mMaxValue));
- }
-
- @Override
- public Integer get(BackgroundBlurController blurController) {
- return blurController.mBackgroundBlurRadius;
- }
- }
-
- private final Launcher mLauncher;
- private SurfaceControlCompat mSurface;
- private int mBackgroundBlurRadius;
-
- public BackgroundBlurController(Launcher l) {
- mLauncher = l;
- }
-
- /**
- * @return the background blur adjustment for folders
- */
- public int getFolderBackgroundBlurAdjustment() {
- return mLauncher.getResources().getInteger(
- R.integer.folder_background_blur_radius_adjustment);
- }
-
- /**
- * Sets the specified app target surface to apply the blur to.
- */
- public void setSurfaceToApp(RemoteAnimationTargetCompat target) {
- if (target != null) {
- setSurface(target.leash);
- }
- }
-
- /**
- * Sets the surface to apply the blur to as the launcher surface.
- */
- public void setSurfaceToLauncher(View v) {
- setSurface(v != null ? new SurfaceControlCompat(v) : null);
- }
-
- private void setSurface(SurfaceControlCompat surface) {
- if (mSurface != surface) {
- mSurface = surface;
- if (surface != null) {
- setBackgroundBlurRadius(mBackgroundBlurRadius);
- } else {
- // If there is no surface, then reset the blur radius
- setBackgroundBlurRadius(0);
- }
- }
- }
-
- @Override
- public void setState(LauncherState toState) {
- if (mSurface == null) {
- return;
- }
-
- int toBackgroundBlurRadius = toState.getBackgroundBlurRadius(mLauncher);
- if (mBackgroundBlurRadius != toBackgroundBlurRadius) {
- setBackgroundBlurRadius(toBackgroundBlurRadius);
- }
- }
-
- @Override
- public void setStateWithAnimation(LauncherState toState, AnimatorSetBuilder builder,
- LauncherStateManager.AnimationConfig config) {
- if (mSurface == null || config.onlyPlayAtomicComponent()) {
- return;
- }
-
- int toBackgroundBlurRadius = toState.getBackgroundBlurRadius(mLauncher);
- if (mBackgroundBlurRadius != toBackgroundBlurRadius) {
- PropertySetter propertySetter = config.getPropertySetter(builder);
- propertySetter.setInt(this, BACKGROUND_BLUR, toBackgroundBlurRadius, LINEAR);
- }
- }
-
- private void setBackgroundBlurRadius(int blurRadius) {
- // TODO: Do nothing if the shadows are not enabled
- // Always update the background blur as it will be reapplied when a surface is next
- // available
- mBackgroundBlurRadius = blurRadius;
- if (mSurface == null || !mSurface.isValid()) {
- return;
- }
- new TransactionCompat()
- .setBackgroundBlurRadius(mSurface, blurRadius)
- .apply();
- }
-}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java b/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java
index 94e67f0..03454f7 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java
@@ -20,17 +20,17 @@
import static com.android.launcher3.LauncherAnimUtils.VIEW_ALPHA;
import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X;
import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y;
-import static com.android.launcher3.LauncherStateManager.PLAY_ATOMIC_OVERVIEW_PEEK;
-import static com.android.launcher3.LauncherStateManager.PLAY_ATOMIC_OVERVIEW_SCALE;
-import static com.android.launcher3.LauncherStateManager.SKIP_OVERVIEW;
-import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_FADE;
-import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_SCALE;
-import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_SCRIM_FADE;
-import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_TRANSLATE_X;
-import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_TRANSLATE_Y;
import static com.android.launcher3.anim.Interpolators.AGGRESSIVE_EASE_IN_OUT;
import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.graphics.Scrim.SCRIM_PROGRESS;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_FADE;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_SCALE;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_SCRIM_FADE;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_TRANSLATE_X;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_TRANSLATE_Y;
+import static com.android.launcher3.states.StateAnimationConfig.PLAY_ATOMIC_OVERVIEW_PEEK;
+import static com.android.launcher3.states.StateAnimationConfig.PLAY_ATOMIC_OVERVIEW_SCALE;
+import static com.android.launcher3.states.StateAnimationConfig.SKIP_OVERVIEW;
import android.util.FloatProperty;
import android.view.View;
@@ -41,11 +41,10 @@
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
import com.android.launcher3.LauncherState.ScaleAndTranslation;
-import com.android.launcher3.LauncherStateManager.AnimationConfig;
import com.android.launcher3.LauncherStateManager.StateHandler;
-import com.android.launcher3.anim.AnimatorSetBuilder;
-import com.android.launcher3.anim.PropertySetter;
+import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.graphics.OverviewScrim;
+import com.android.launcher3.states.StateAnimationConfig;
/**
* State handler for recents view. Manages UI changes and animations for recents view based off the
@@ -86,8 +85,8 @@
}
@Override
- public final void setStateWithAnimation(@NonNull final LauncherState toState,
- @NonNull AnimatorSetBuilder builder, @NonNull AnimationConfig config) {
+ public void setStateWithAnimation(LauncherState toState, StateAnimationConfig config,
+ PendingAnimation builder) {
if (!config.hasAnimationFlag(PLAY_ATOMIC_OVERVIEW_PEEK | PLAY_ATOMIC_OVERVIEW_SCALE)) {
// The entire recents animation is played atomically.
return;
@@ -95,25 +94,24 @@
if (config.hasAnimationFlag(SKIP_OVERVIEW)) {
return;
}
- setStateWithAnimationInternal(toState, builder, config);
+ setStateWithAnimationInternal(toState, config, builder);
}
/**
* Core logic for animating the recents view UI.
*
* @param toState state to animate to
- * @param builder animator set builder
* @param config current animation config
+ * @param setter animator set builder
*/
void setStateWithAnimationInternal(@NonNull final LauncherState toState,
- @NonNull AnimatorSetBuilder builder, @NonNull AnimationConfig config) {
- PropertySetter setter = config.getPropertySetter(builder);
+ @NonNull StateAnimationConfig config, @NonNull PendingAnimation setter) {
ScaleAndTranslation scaleAndTranslation = toState.getOverviewScaleAndTranslation(mLauncher);
- Interpolator scaleInterpolator = builder.getInterpolator(ANIM_OVERVIEW_SCALE, LINEAR);
+ Interpolator scaleInterpolator = config.getInterpolator(ANIM_OVERVIEW_SCALE, LINEAR);
setter.setFloat(mRecentsView, SCALE_PROPERTY, scaleAndTranslation.scale, scaleInterpolator);
- Interpolator translateXInterpolator = builder.getInterpolator(
+ Interpolator translateXInterpolator = config.getInterpolator(
ANIM_OVERVIEW_TRANSLATE_X, LINEAR);
- Interpolator translateYInterpolator = builder.getInterpolator(
+ Interpolator translateYInterpolator = config.getInterpolator(
ANIM_OVERVIEW_TRANSLATE_Y, LINEAR);
float translationX = scaleAndTranslation.translationX;
if (mRecentsView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL) {
@@ -123,14 +121,14 @@
setter.setFloat(mRecentsView, VIEW_TRANSLATE_Y, scaleAndTranslation.translationY,
translateYInterpolator);
setter.setFloat(mRecentsView, getContentAlphaProperty(), toState.overviewUi ? 1 : 0,
- builder.getInterpolator(ANIM_OVERVIEW_FADE, AGGRESSIVE_EASE_IN_OUT));
+ config.getInterpolator(ANIM_OVERVIEW_FADE, AGGRESSIVE_EASE_IN_OUT));
OverviewScrim scrim = mLauncher.getDragLayer().getOverviewScrim();
setter.setFloat(scrim, SCRIM_PROGRESS, toState.getOverviewScrimAlpha(mLauncher),
- builder.getInterpolator(ANIM_OVERVIEW_SCRIM_FADE, LINEAR));
+ config.getInterpolator(ANIM_OVERVIEW_SCRIM_FADE, LINEAR));
if (mActionsView != null) {
setter.setFloat(mActionsView, VIEW_TRANSLATE_X, translationX, translateXInterpolator);
setter.setFloat(mActionsView, VIEW_ALPHA, toState.overviewUi ? 1 : 0,
- builder.getInterpolator(ANIM_OVERVIEW_FADE, AGGRESSIVE_EASE_IN_OUT));
+ config.getInterpolator(ANIM_OVERVIEW_FADE, AGGRESSIVE_EASE_IN_OUT));
}
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/DepthController.java b/quickstep/src/com/android/launcher3/uioverrides/DepthController.java
new file mode 100644
index 0000000..8995a7e
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/uioverrides/DepthController.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.uioverrides;
+
+import static com.android.launcher3.anim.Interpolators.LINEAR;
+
+import android.os.IBinder;
+import android.util.FloatProperty;
+import android.view.View;
+
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherState;
+import com.android.launcher3.LauncherStateManager;
+import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.anim.PendingAnimation;
+import com.android.launcher3.states.StateAnimationConfig;
+import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
+import com.android.systemui.shared.system.SurfaceControlCompat;
+import com.android.systemui.shared.system.TransactionCompat;
+import com.android.systemui.shared.system.WallpaperManagerCompat;
+
+/**
+ * Controls blur and wallpaper zoom, for the Launcher surface only.
+ */
+public class DepthController implements LauncherStateManager.StateHandler {
+
+ public static final FloatProperty<DepthController> DEPTH =
+ new FloatProperty<DepthController>("depth") {
+ @Override
+ public void setValue(DepthController depthController, float depth) {
+ depthController.setDepth(depth);
+ }
+
+ @Override
+ public Float get(DepthController depthController) {
+ return depthController.mDepth;
+ }
+ };
+
+ /**
+ * A property that updates the background blur within a given range of values (ie. even if the
+ * animator goes beyond 0..1, the interpolated value will still be bounded).
+ */
+ public static class ClampedDepthProperty extends FloatProperty<DepthController> {
+ private final float mMinValue;
+ private final float mMaxValue;
+
+ public ClampedDepthProperty(float minValue, float maxValue) {
+ super("depthClamped");
+ mMinValue = minValue;
+ mMaxValue = maxValue;
+ }
+
+ @Override
+ public void setValue(DepthController depthController, float depth) {
+ depthController.setDepth(Utilities.boundToRange(depth, mMinValue, mMaxValue));
+ }
+
+ @Override
+ public Float get(DepthController depthController) {
+ return depthController.mDepth;
+ }
+ }
+
+ private final Launcher mLauncher;
+ /**
+ * Blur radius when completely zoomed out, in pixels.
+ */
+ private int mMaxBlurRadius;
+ private WallpaperManagerCompat mWallpaperManager;
+ private SurfaceControlCompat mSurface;
+ /**
+ * Ratio from 0 to 1, where 0 is fully zoomed out, and 1 is zoomed in.
+ * @see android.service.wallpaper.WallpaperService.Engine#onZoomChanged(float)
+ */
+ private float mDepth;
+
+ public DepthController(Launcher l) {
+ mLauncher = l;
+ }
+
+ private void ensureDependencies() {
+ if (mWallpaperManager != null) {
+ return;
+ }
+ mMaxBlurRadius = mLauncher.getResources().getInteger(R.integer.max_depth_blur_radius);
+ mWallpaperManager = new WallpaperManagerCompat(mLauncher);
+ }
+
+ /**
+ * Sets the specified app target surface to apply the blur to.
+ */
+ public void setSurfaceToApp(RemoteAnimationTargetCompat target) {
+ if (target != null) {
+ setSurface(target.leash);
+ }
+ }
+
+ /**
+ * Sets the surface to apply the blur to as the launcher surface.
+ */
+ public void setSurfaceToLauncher(View v) {
+ setSurface(v != null ? new SurfaceControlCompat(v) : null);
+ }
+
+ private void setSurface(SurfaceControlCompat surface) {
+ if (mSurface != surface) {
+ mSurface = surface;
+ if (surface != null) {
+ setDepth(mDepth);
+ } else {
+ // If there is no surface, then reset the ratio
+ setDepth(0f);
+ }
+ }
+ }
+
+ @Override
+ public void setState(LauncherState toState) {
+ if (mSurface == null) {
+ return;
+ }
+
+ float toDepth = toState.getDepth(mLauncher);
+ if (Float.compare(mDepth, toDepth) != 0) {
+ setDepth(toDepth);
+ }
+ }
+
+ @Override
+ public void setStateWithAnimation(LauncherState toState, StateAnimationConfig config,
+ PendingAnimation animation) {
+ if (mSurface == null || config.onlyPlayAtomicComponent()) {
+ return;
+ }
+
+ float toDepth = toState.getDepth(mLauncher);
+ if (Float.compare(mDepth, toDepth) != 0) {
+ animation.setFloat(this, DEPTH, toDepth, LINEAR);
+ }
+ }
+
+ private void setDepth(float depth) {
+ mDepth = depth;
+ if (mSurface == null || !mSurface.isValid()) {
+ return;
+ }
+ ensureDependencies();
+ IBinder windowToken = mLauncher.getRootView().getWindowToken();
+ if (windowToken != null) {
+ mWallpaperManager.setWallpaperZoomOut(windowToken, mDepth);
+ }
+ new TransactionCompat()
+ .setBackgroundBlurRadius(mSurface, (int) (mDepth * mMaxBlurRadius))
+ .apply();
+ }
+}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/PreviewSurfaceRenderer.java b/quickstep/src/com/android/launcher3/uioverrides/PreviewSurfaceRenderer.java
index 548223a..c7cce0b 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/PreviewSurfaceRenderer.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/PreviewSurfaceRenderer.java
@@ -32,8 +32,11 @@
/** Handle a received surface view request. */
public static void render(Context context, Bundle bundle) {
- final String gridName = bundle.getString("name");
+ String gridName = bundle.getString("name");
bundle.remove("name");
+ if (gridName == null) {
+ gridName = InvariantDeviceProfile.getCurrentGridName(context);
+ }
final InvariantDeviceProfile idp = new InvariantDeviceProfile(context, gridName);
MAIN_EXECUTOR.execute(() -> {
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java b/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java
index 971d917..93e02a1 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java
@@ -22,7 +22,6 @@
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
-import com.android.launcher3.R;
import com.android.launcher3.allapps.AllAppsContainerView;
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
import com.android.quickstep.SysUINavigationMode;
@@ -88,8 +87,8 @@
}
@Override
- public int getBackgroundBlurRadius(Context context) {
- return context.getResources().getInteger(R.integer.allapps_background_blur_radius);
+ public float getDepth(Context context) {
+ return 1f;
}
@Override
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/LandscapeEdgeSwipeController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/LandscapeEdgeSwipeController.java
index 3d0fc56..bef191e 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/LandscapeEdgeSwipeController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/LandscapeEdgeSwipeController.java
@@ -9,7 +9,7 @@
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
-import com.android.launcher3.LauncherStateManager.AnimationFlags;
+import com.android.launcher3.states.StateAnimationConfig.AnimationFlags;
import com.android.launcher3.touch.AbstractStateChangeTouchController;
import com.android.launcher3.touch.SingleAxisSwipeDetector;
import com.android.launcher3.userevent.nano.LauncherLogProto;
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java
index a060d64..cc3fd97 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java
@@ -19,13 +19,13 @@
import static com.android.launcher3.LauncherState.ALL_APPS;
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.LauncherState.OVERVIEW;
-import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_ALL_APPS_FADE;
-import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_FADE;
-import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_VERTICAL_PROGRESS;
import static com.android.launcher3.anim.Interpolators.ACCEL;
import static com.android.launcher3.anim.Interpolators.DEACCEL;
import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.config.FeatureFlags.UNSTABLE_SPRINGS;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_APPS_FADE;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_FADE;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_VERTICAL_PROGRESS;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_OVERVIEW_DISABLED;
import android.animation.TimeInterpolator;
@@ -37,11 +37,10 @@
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
-import com.android.launcher3.LauncherStateManager.AnimationFlags;
import com.android.launcher3.allapps.AllAppsTransitionController;
-import com.android.launcher3.anim.AnimatorPlaybackController;
-import com.android.launcher3.anim.AnimatorSetBuilder;
import com.android.launcher3.anim.Interpolators;
+import com.android.launcher3.states.StateAnimationConfig;
+import com.android.launcher3.states.StateAnimationConfig.AnimationFlags;
import com.android.launcher3.touch.AbstractStateChangeTouchController;
import com.android.launcher3.touch.SingleAxisSwipeDetector;
import com.android.launcher3.uioverrides.states.OverviewState;
@@ -148,16 +147,16 @@
return isTouchOverHotseat(mLauncher, ev) ? ContainerType.HOTSEAT : ContainerType.WORKSPACE;
}
- private AnimatorSetBuilder getNormalToOverviewAnimation() {
+ private StateAnimationConfig getNormalToOverviewAnimation() {
mAllAppsInterpolatorWrapper.baseInterpolator = LINEAR;
- AnimatorSetBuilder builder = new AnimatorSetBuilder();
+ StateAnimationConfig builder = new StateAnimationConfig();
builder.setInterpolator(ANIM_VERTICAL_PROGRESS, mAllAppsInterpolatorWrapper);
return builder;
}
- public static AnimatorSetBuilder getOverviewToAllAppsAnimation() {
- AnimatorSetBuilder builder = new AnimatorSetBuilder();
+ private static StateAnimationConfig getOverviewToAllAppsAnimation() {
+ StateAnimationConfig builder = new StateAnimationConfig();
builder.setInterpolator(ANIM_ALL_APPS_FADE, Interpolators.clampToProgress(ACCEL,
0, ALL_APPS_CONTENT_FADE_THRESHOLD));
builder.setInterpolator(ANIM_OVERVIEW_FADE, Interpolators.clampToProgress(DEACCEL,
@@ -165,8 +164,8 @@
return builder;
}
- private AnimatorSetBuilder getAllAppsToOverviewAnimation() {
- AnimatorSetBuilder builder = new AnimatorSetBuilder();
+ private StateAnimationConfig getAllAppsToOverviewAnimation() {
+ StateAnimationConfig builder = new StateAnimationConfig();
builder.setInterpolator(ANIM_ALL_APPS_FADE, Interpolators.clampToProgress(DEACCEL,
1 - ALL_APPS_CONTENT_FADE_THRESHOLD, 1));
builder.setInterpolator(ANIM_OVERVIEW_FADE, Interpolators.clampToProgress(ACCEL,
@@ -174,41 +173,42 @@
return builder;
}
- private AnimatorSetBuilder getNormalToAllAppsAnimation() {
- AnimatorSetBuilder builder = new AnimatorSetBuilder();
+ private StateAnimationConfig getNormalToAllAppsAnimation() {
+ StateAnimationConfig builder = new StateAnimationConfig();
builder.setInterpolator(ANIM_ALL_APPS_FADE, Interpolators.clampToProgress(ACCEL,
0, ALL_APPS_CONTENT_FADE_THRESHOLD));
return builder;
}
- private AnimatorSetBuilder getAllAppsToNormalAnimation() {
- AnimatorSetBuilder builder = new AnimatorSetBuilder();
+ private StateAnimationConfig getAllAppsToNormalAnimation() {
+ StateAnimationConfig builder = new StateAnimationConfig();
builder.setInterpolator(ANIM_ALL_APPS_FADE, Interpolators.clampToProgress(DEACCEL,
1 - ALL_APPS_CONTENT_FADE_THRESHOLD, 1));
return builder;
}
@Override
- protected AnimatorSetBuilder getAnimatorSetBuilderForStates(LauncherState fromState,
- LauncherState toState) {
- AnimatorSetBuilder builder = new AnimatorSetBuilder();
+ protected StateAnimationConfig getConfigForStates(
+ LauncherState fromState, LauncherState toState) {
+ final StateAnimationConfig config;
if (fromState == NORMAL && toState == OVERVIEW) {
- builder = getNormalToOverviewAnimation();
+ config = getNormalToOverviewAnimation();
} else if (fromState == OVERVIEW && toState == ALL_APPS) {
- builder = getOverviewToAllAppsAnimation();
+ config = getOverviewToAllAppsAnimation();
} else if (fromState == ALL_APPS && toState == OVERVIEW) {
- builder = getAllAppsToOverviewAnimation();
+ config = getAllAppsToOverviewAnimation();
} else if (fromState == NORMAL && toState == ALL_APPS) {
- builder = getNormalToAllAppsAnimation();
+ config = getNormalToAllAppsAnimation();
} else if (fromState == ALL_APPS && toState == NORMAL) {
- builder = getAllAppsToNormalAnimation();
+ config = getAllAppsToNormalAnimation();
+ } else {
+ config = new StateAnimationConfig();
}
- return builder;
+ return config;
}
@Override
- protected float initCurrentAnimation(@AnimationFlags int animComponents) {
- animComponents = updateAnimComponentsOnReinit(animComponents);
+ protected float initCurrentAnimation(@AnimationFlags int animFlags) {
float range = getShiftRange();
long maxAccuracy = (long) (2 * range);
@@ -217,8 +217,10 @@
float totalShift = endVerticalShift - startVerticalShift;
- final AnimatorSetBuilder builder = totalShift == 0 ? new AnimatorSetBuilder()
- : getAnimatorSetBuilderForStates(mFromState, mToState);
+ final StateAnimationConfig config = totalShift == 0 ? new StateAnimationConfig()
+ : getConfigForStates(mFromState, mToState);
+ config.animFlags = updateAnimComponentsOnReinit(animFlags);
+ config.duration = maxAccuracy;
cancelPendingAnim();
@@ -232,15 +234,15 @@
cancelPendingAnim();
clearState();
};
- mCurrentAnimation = AnimatorPlaybackController.wrap(mPendingAnimation, maxAccuracy)
+ mCurrentAnimation = mPendingAnimation.createPlaybackController()
.setOnCancelRunnable(onCancelRunnable);
mLauncher.getStateManager().setCurrentUserControlledAnimation(mCurrentAnimation);
totalShift = LayoutUtils.getShelfTrackingDistance(mLauncher,
mLauncher.getDeviceProfile());
} else {
mCurrentAnimation = mLauncher.getStateManager()
- .createAnimationToNewWorkspace(mToState, builder, maxAccuracy, this::clearState,
- animComponents);
+ .createAnimationToNewWorkspace(mToState, config)
+ .setOnCancelRunnable(this::clearState);
}
if (totalShift == 0) {
diff --git a/quickstep/src/com/android/quickstep/BaseActivityInterface.java b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
index be0bdd8..2a569f5 100644
--- a/quickstep/src/com/android/quickstep/BaseActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
@@ -33,7 +33,7 @@
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.touch.PagedOrientationHandler;
-import com.android.launcher3.uioverrides.BackgroundBlurController;
+import com.android.launcher3.uioverrides.DepthController;
import com.android.quickstep.util.ActivityInitListener;
import com.android.quickstep.util.ShelfPeekAnim;
import com.android.systemui.shared.recents.model.ThumbnailData;
@@ -81,7 +81,8 @@
@Nullable
T getCreatedActivity();
- default @Nullable BackgroundBlurController getBackgroundBlurController() {
+ @Nullable
+ default DepthController getDepthController() {
return null;
}
diff --git a/quickstep/src/com/android/quickstep/InputConsumer.java b/quickstep/src/com/android/quickstep/InputConsumer.java
index 3e84e7d..8efaeb9 100644
--- a/quickstep/src/com/android/quickstep/InputConsumer.java
+++ b/quickstep/src/com/android/quickstep/InputConsumer.java
@@ -60,6 +60,16 @@
}
/**
+ * Returns true if the lifecycle of this input consumer is detached from the normal gesture
+ * down/up flow. If so, it is the responsibility of the input consumer to call back to
+ * {@link TouchInteractionService#onConsumerInactive(InputConsumer)} after the consumer is
+ * finished.
+ */
+ default boolean isConsumerDetachedFromGesture() {
+ return false;
+ }
+
+ /**
* Called by the event queue when the consumer is about to be switched to a new consumer.
* Consumers should update the state accordingly here before the state is passed to the new
* consumer.
diff --git a/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialController.java b/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialController.java
index 3fe91a3..5c2e992 100644
--- a/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialController.java
+++ b/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialController.java
@@ -25,6 +25,7 @@
import com.android.launcher3.R;
import com.android.quickstep.interaction.BackGestureTutorialFragment.TutorialStep;
import com.android.quickstep.interaction.BackGestureTutorialFragment.TutorialType;
+import com.android.quickstep.interaction.EdgeBackGestureHandler.BackGestureResult;
import java.util.Optional;
@@ -79,21 +80,33 @@
mHandCoachingAnimation.stop();
}
- void onGestureDetected() {
- hideHandCoachingAnimation();
-
- if (mTutorialStep == TutorialStep.CONFIRM) {
+ void onGestureAttempted(BackGestureResult result) {
+ if (mTutorialStep == TutorialStep.CONFIRM
+ && (result == BackGestureResult.BACK_COMPLETED_FROM_LEFT
+ || result == BackGestureResult.BACK_COMPLETED_FROM_RIGHT)) {
mFragment.closeTutorial();
return;
}
- if (mTutorialTypeInfo.get().getTutorialType() == TutorialType.RIGHT_EDGE_BACK_NAVIGATION) {
- mFragment.changeController(TutorialStep.ENGAGED,
- TutorialType.LEFT_EDGE_BACK_NAVIGATION);
+ if (!mTutorialTypeInfo.isPresent()) {
return;
}
- mFragment.changeController(TutorialStep.CONFIRM);
+ switch (mTutorialTypeInfo.get().getTutorialType()) {
+ case RIGHT_EDGE_BACK_NAVIGATION:
+ if (result == BackGestureResult.BACK_COMPLETED_FROM_RIGHT) {
+ hideHandCoachingAnimation();
+ mFragment.changeController(
+ TutorialStep.ENGAGED, TutorialType.LEFT_EDGE_BACK_NAVIGATION);
+ }
+ break;
+ case LEFT_EDGE_BACK_NAVIGATION:
+ if (result == BackGestureResult.BACK_COMPLETED_FROM_LEFT) {
+ hideHandCoachingAnimation();
+ mFragment.changeController(TutorialStep.CONFIRM);
+ }
+ break;
+ }
}
abstract Optional<Integer> getTitleStringId();
diff --git a/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialFragment.java b/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialFragment.java
index 54408ce..593b695 100644
--- a/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialFragment.java
+++ b/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialFragment.java
@@ -17,21 +17,26 @@
import android.content.ActivityNotFoundException;
import android.content.Intent;
+import android.graphics.Insets;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
+import android.view.WindowInsets;
+import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import com.android.launcher3.R;
+import com.android.quickstep.interaction.EdgeBackGestureHandler.BackGestureAttemptCallback;
+import com.android.quickstep.interaction.EdgeBackGestureHandler.BackGestureResult;
import java.net.URISyntaxException;
import java.util.Optional;
/** Shows the Back gesture interactive tutorial. */
-public class BackGestureTutorialFragment extends Fragment {
+public class BackGestureTutorialFragment extends Fragment implements BackGestureAttemptCallback {
private static final String LOG_TAG = "TutorialFragment";
private static final String KEY_TUTORIAL_STEP = "tutorialStep";
@@ -47,6 +52,7 @@
private Optional<BackGestureTutorialController> mTutorialController = Optional.empty();
private View mRootView;
private BackGestureTutorialHandAnimation mHandCoachingAnimation;
+ private EdgeBackGestureHandler mEdgeBackGestureHandler;
public static BackGestureTutorialFragment newInstance(
TutorialStep tutorialStep, TutorialType tutorialType) {
@@ -64,17 +70,25 @@
Bundle args = savedInstanceState != null ? savedInstanceState : getArguments();
mTutorialStep = (TutorialStep) args.getSerializable(KEY_TUTORIAL_STEP);
mTutorialType = (TutorialType) args.getSerializable(KEY_TUTORIAL_TYPE);
+ mEdgeBackGestureHandler = new EdgeBackGestureHandler(getContext());
+ mEdgeBackGestureHandler.registerBackGestureAttemptCallback(this);
}
@Override
public View onCreateView(
- LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ @NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
mRootView = inflater.inflate(R.layout.back_gesture_tutorial_fragment,
container, /* attachToRoot= */ false);
mRootView.findViewById(R.id.back_gesture_tutorial_fragment_close_button)
.setOnClickListener(this::onCloseButtonClicked);
+ mRootView.setOnApplyWindowInsetsListener((view, insets) -> {
+ Insets systemInsets = insets.getInsets(WindowInsets.Type.systemBars());
+ mEdgeBackGestureHandler.setInsets(systemInsets.left, systemInsets.right);
+ return insets;
+ });
+ mRootView.setOnTouchListener(mEdgeBackGestureHandler);
mHandCoachingAnimation = new BackGestureTutorialHandAnimation(getContext(), mRootView);
return mRootView;
@@ -92,6 +106,14 @@
mHandCoachingAnimation.stop();
}
+ void onAttachedToWindow() {
+ mEdgeBackGestureHandler.setIsEnabled(true);
+ }
+
+ void onDetachedFromWindow() {
+ mEdgeBackGestureHandler.setIsEnabled(false);
+ }
+
@Override
public void onSaveInstanceState(Bundle savedInstanceState) {
savedInstanceState.putSerializable(KEY_TUTORIAL_STEP, mTutorialStep);
@@ -125,10 +147,9 @@
this.mTutorialType = tutorialType;
}
- void onBackPressed() {
- if (mTutorialController.isPresent()) {
- mTutorialController.get().onGestureDetected();
- }
+ @Override
+ public void onBackGestureAttempted(BackGestureResult result) {
+ mTutorialController.ifPresent(controller -> controller.onGestureAttempted(result));
}
void closeTutorial() {
diff --git a/quickstep/src/com/android/quickstep/interaction/EdgeBackGestureHandler.java b/quickstep/src/com/android/quickstep/interaction/EdgeBackGestureHandler.java
new file mode 100644
index 0000000..04cd2f4
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/interaction/EdgeBackGestureHandler.java
@@ -0,0 +1,274 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.quickstep.interaction;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.PixelFormat;
+import android.graphics.Point;
+import android.graphics.PointF;
+import android.hardware.display.DisplayManager;
+import android.hardware.display.DisplayManager.DisplayListener;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.SystemProperties;
+import android.view.Display;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.View.OnTouchListener;
+import android.view.ViewConfiguration;
+import android.view.WindowManager;
+import android.view.WindowManager.LayoutParams;
+
+import com.android.launcher3.ResourceUtils;
+
+/**
+ * Utility class to handle edge swipes for back gestures.
+ *
+ * Forked from platform/frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java.
+ */
+public class EdgeBackGestureHandler implements DisplayListener, OnTouchListener {
+
+ private static final String TAG = "EdgeBackGestureHandler";
+ private static final int MAX_LONG_PRESS_TIMEOUT = SystemProperties.getInt(
+ "gestures.back_timeout", 250);
+
+ private final Context mContext;
+
+ private final Point mDisplaySize = new Point();
+ private final int mDisplayId;
+
+ // The edge width where touch down is allowed
+ private int mEdgeWidth;
+ // The bottom gesture area height
+ private int mBottomGestureHeight;
+ // The slop to distinguish between horizontal and vertical motion
+ private final float mTouchSlop;
+ // Duration after which we consider the event as longpress.
+ private final int mLongPressTimeout;
+
+ private final PointF mDownPoint = new PointF();
+ private boolean mThresholdCrossed = false;
+ private boolean mAllowGesture = false;
+ private boolean mIsEnabled;
+ private int mLeftInset;
+ private int mRightInset;
+
+ private EdgeBackGesturePanel mEdgeBackPanel;
+ private BackGestureAttemptCallback mGestureCallback;
+
+ private final EdgeBackGesturePanel.BackCallback mBackCallback =
+ new EdgeBackGesturePanel.BackCallback() {
+ @Override
+ public void triggerBack() {
+ if (mGestureCallback != null) {
+ mGestureCallback.onBackGestureAttempted(mEdgeBackPanel.getIsLeftPanel()
+ ? BackGestureResult.BACK_COMPLETED_FROM_LEFT
+ : BackGestureResult.BACK_COMPLETED_FROM_RIGHT);
+ }
+ }
+
+ @Override
+ public void cancelBack() {
+ if (mGestureCallback != null) {
+ mGestureCallback.onBackGestureAttempted(mEdgeBackPanel.getIsLeftPanel()
+ ? BackGestureResult.BACK_CANCELLED_FROM_LEFT
+ : BackGestureResult.BACK_CANCELLED_FROM_RIGHT);
+ }
+ }
+ };
+
+ EdgeBackGestureHandler(Context context) {
+ final Resources res = context.getResources();
+ mContext = context;
+ mDisplayId = context.getDisplay() == null
+ ? Display.DEFAULT_DISPLAY : context.getDisplay().getDisplayId();
+
+ mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
+ mLongPressTimeout = Math.min(MAX_LONG_PRESS_TIMEOUT,
+ ViewConfiguration.getLongPressTimeout());
+
+ mBottomGestureHeight =
+ ResourceUtils.getNavbarSize(ResourceUtils.NAVBAR_BOTTOM_GESTURE_SIZE, res);
+ mEdgeWidth = ResourceUtils.getNavbarSize("config_backGestureInset", res);
+ }
+
+ void setIsEnabled(boolean isEnabled) {
+ if (isEnabled == mIsEnabled) {
+ return;
+ }
+ mIsEnabled = isEnabled;
+
+ if (mEdgeBackPanel != null) {
+ mEdgeBackPanel.onDestroy();
+ mEdgeBackPanel = null;
+ }
+
+ if (!mIsEnabled) {
+ mContext.getSystemService(DisplayManager.class).unregisterDisplayListener(this);
+ } else {
+ updateDisplaySize();
+ mContext.getSystemService(DisplayManager.class).registerDisplayListener(this,
+ new Handler(Looper.getMainLooper()));
+
+ // Add a nav bar panel window.
+ mEdgeBackPanel = new EdgeBackGesturePanel(mContext);
+ mEdgeBackPanel.setBackCallback(mBackCallback);
+ mEdgeBackPanel.setLayoutParams(createLayoutParams());
+ updateDisplaySize();
+ }
+ }
+
+ void registerBackGestureAttemptCallback(BackGestureAttemptCallback callback) {
+ mGestureCallback = callback;
+ }
+
+ private WindowManager.LayoutParams createLayoutParams() {
+ Resources resources = mContext.getResources();
+ WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams(
+ ResourceUtils.getNavbarSize("navigation_edge_panel_width", resources),
+ ResourceUtils.getNavbarSize("navigation_edge_panel_height", resources),
+ LayoutParams.TYPE_APPLICATION_PANEL,
+ WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+ | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
+ | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH
+ | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN,
+ PixelFormat.TRANSLUCENT);
+ layoutParams.setTitle(TAG + mDisplayId);
+ layoutParams.windowAnimations = 0;
+ layoutParams.setFitInsetsTypes(0 /* types */);
+ return layoutParams;
+ }
+
+ @Override
+ public boolean onTouch(View view, MotionEvent motionEvent) {
+ if (mIsEnabled) {
+ onMotionEvent(motionEvent);
+ return true;
+ }
+ return false;
+ }
+
+ private boolean isWithinTouchRegion(int x, int y) {
+ // Disallow if too far from the edge
+ if (x > mEdgeWidth + mLeftInset && x < (mDisplaySize.x - mEdgeWidth - mRightInset)) {
+ return false;
+ }
+
+ // Disallow if we are in the bottom gesture area
+ if (y >= (mDisplaySize.y - mBottomGestureHeight)) {
+ return false;
+ }
+
+ return true;
+ }
+
+ private void cancelGesture(MotionEvent ev) {
+ // Send action cancel to reset all the touch events
+ mAllowGesture = false;
+ MotionEvent cancelEv = MotionEvent.obtain(ev);
+ cancelEv.setAction(MotionEvent.ACTION_CANCEL);
+ mEdgeBackPanel.onMotionEvent(cancelEv);
+ cancelEv.recycle();
+ }
+
+ private void onMotionEvent(MotionEvent ev) {
+ int action = ev.getActionMasked();
+ if (action == MotionEvent.ACTION_DOWN) {
+ boolean isOnLeftEdge = ev.getX() <= mEdgeWidth + mLeftInset;
+ mAllowGesture = isWithinTouchRegion((int) ev.getX(), (int) ev.getY());
+ if (mAllowGesture) {
+ mEdgeBackPanel.setIsLeftPanel(isOnLeftEdge);
+ mEdgeBackPanel.onMotionEvent(ev);
+
+ mDownPoint.set(ev.getX(), ev.getY());
+ mThresholdCrossed = false;
+ }
+ } else if (mAllowGesture) {
+ if (!mThresholdCrossed) {
+ if (action == MotionEvent.ACTION_POINTER_DOWN) {
+ // We do not support multi touch for back gesture
+ cancelGesture(ev);
+ return;
+ } else if (action == MotionEvent.ACTION_MOVE) {
+ if ((ev.getEventTime() - ev.getDownTime()) > mLongPressTimeout) {
+ cancelGesture(ev);
+ return;
+ }
+ float dx = Math.abs(ev.getX() - mDownPoint.x);
+ float dy = Math.abs(ev.getY() - mDownPoint.y);
+ if (dy > dx && dy > mTouchSlop) {
+ cancelGesture(ev);
+ return;
+
+ } else if (dx > dy && dx > mTouchSlop) {
+ mThresholdCrossed = true;
+ }
+ }
+
+ }
+
+ // forward touch
+ mEdgeBackPanel.onMotionEvent(ev);
+ }
+
+ if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
+ if (!mAllowGesture && mGestureCallback != null) {
+ mGestureCallback.onBackGestureAttempted(BackGestureResult.BACK_NOT_STARTED);
+ }
+ }
+ }
+
+ @Override
+ public void onDisplayAdded(int displayId) { }
+
+ @Override
+ public void onDisplayRemoved(int displayId) { }
+
+ @Override
+ public void onDisplayChanged(int displayId) {
+ if (displayId == mDisplayId) {
+ updateDisplaySize();
+ }
+ }
+
+ private void updateDisplaySize() {
+ mContext.getDisplay().getRealSize(mDisplaySize);
+ if (mEdgeBackPanel != null) {
+ mEdgeBackPanel.setDisplaySize(mDisplaySize);
+ }
+ }
+
+ void setInsets(int leftInset, int rightInset) {
+ mLeftInset = leftInset;
+ mRightInset = rightInset;
+ }
+
+ enum BackGestureResult {
+ UNKNOWN,
+ BACK_COMPLETED_FROM_LEFT,
+ BACK_COMPLETED_FROM_RIGHT,
+ BACK_CANCELLED_FROM_LEFT,
+ BACK_CANCELLED_FROM_RIGHT,
+ BACK_NOT_STARTED,
+ }
+
+ /** Callback to let the UI react to attempted back gestures. */
+ interface BackGestureAttemptCallback {
+ /** Called whenever any touch is completed. */
+ void onBackGestureAttempted(BackGestureResult result);
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/interaction/EdgeBackGesturePanel.java b/quickstep/src/com/android/quickstep/interaction/EdgeBackGesturePanel.java
new file mode 100644
index 0000000..34eeafc
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/interaction/EdgeBackGesturePanel.java
@@ -0,0 +1,701 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.quickstep.interaction;
+
+import android.animation.ValueAnimator;
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.graphics.Point;
+import android.os.SystemClock;
+import android.view.Gravity;
+import android.view.MotionEvent;
+import android.view.VelocityTracker;
+import android.view.View;
+import android.view.WindowManager;
+import android.view.animation.Interpolator;
+import android.view.animation.PathInterpolator;
+
+import androidx.core.math.MathUtils;
+import androidx.dynamicanimation.animation.DynamicAnimation;
+import androidx.dynamicanimation.animation.FloatPropertyCompat;
+import androidx.dynamicanimation.animation.SpringAnimation;
+import androidx.dynamicanimation.animation.SpringForce;
+
+import com.android.launcher3.R;
+import com.android.launcher3.ResourceUtils;
+import com.android.launcher3.anim.Interpolators;
+import com.android.launcher3.util.VibratorWrapper;
+
+/** Forked from platform/frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarEdgePanel.java. */
+public class EdgeBackGesturePanel extends View {
+
+ private static final String LOG_TAG = "EdgeBackGesturePanel";
+
+ private static final long DISAPPEAR_FADE_ANIMATION_DURATION_MS = 80;
+ private static final long DISAPPEAR_ARROW_ANIMATION_DURATION_MS = 100;
+
+ /**
+ * The time required since the first vibration effect to automatically trigger a click
+ */
+ private static final int GESTURE_DURATION_FOR_CLICK_MS = 400;
+
+ /**
+ * The basic translation in dp where the arrow resides
+ */
+ private static final int BASE_TRANSLATION_DP = 32;
+
+ /**
+ * The length of the arrow leg measured from the center to the end
+ */
+ private static final int ARROW_LENGTH_DP = 18;
+
+ /**
+ * The angle measured from the xAxis, where the leg is when the arrow rests
+ */
+ private static final int ARROW_ANGLE_WHEN_EXTENDED_DEGREES = 56;
+
+ /**
+ * The angle that is added per 1000 px speed to the angle of the leg
+ */
+ private static final int ARROW_ANGLE_ADDED_PER_1000_SPEED = 4;
+
+ /**
+ * The maximum angle offset allowed due to speed
+ */
+ private static final int ARROW_MAX_ANGLE_SPEED_OFFSET_DEGREES = 4;
+
+ /**
+ * The thickness of the arrow. Adjusted to match the home handle (approximately)
+ */
+ private static final float ARROW_THICKNESS_DP = 2.5f;
+
+ /**
+ * The amount of rubber banding we do for the vertical translation
+ */
+ private static final int RUBBER_BAND_AMOUNT = 15;
+
+ /**
+ * The interpolator used to rubberband
+ */
+ private static final Interpolator RUBBER_BAND_INTERPOLATOR =
+ new PathInterpolator(1.0f / 5.0f, 1.0f, 1.0f, 1.0f);
+
+ /**
+ * The amount of rubber banding we do for the translation before base translation
+ */
+ private static final int RUBBER_BAND_AMOUNT_APPEAR = 4;
+
+ /**
+ * The interpolator used to rubberband the appearing of the arrow.
+ */
+ private static final Interpolator RUBBER_BAND_INTERPOLATOR_APPEAR =
+ new PathInterpolator(1.0f / RUBBER_BAND_AMOUNT_APPEAR, 1.0f, 1.0f, 1.0f);
+
+ private final WindowManager mWindowManager;
+ private BackCallback mBackCallback;
+
+ /**
+ * The paint the arrow is drawn with
+ */
+ private final Paint mPaint = new Paint();
+
+ private final float mDensity;
+ private final float mBaseTranslation;
+ private final float mArrowLength;
+ private final float mArrowThickness;
+
+ /**
+ * The minimum delta needed in movement for the arrow to change direction / stop triggering back
+ */
+ private final float mMinDeltaForSwitch;
+ // The closest to y = 0 that the arrow will be displayed.
+ private int mMinArrowPosition;
+ // The amount the arrow is shifted to avoid the finger.
+ private int mFingerOffset;
+
+ private final float mSwipeThreshold;
+ private final Path mArrowPath = new Path();
+ private final Point mDisplaySize = new Point();
+
+ private final SpringAnimation mAngleAnimation;
+ private final SpringAnimation mTranslationAnimation;
+ private final SpringAnimation mVerticalTranslationAnimation;
+ private final SpringForce mAngleAppearForce;
+ private final SpringForce mAngleDisappearForce;
+ private final ValueAnimator mArrowDisappearAnimation;
+ private final SpringForce mRegularTranslationSpring;
+ private final SpringForce mTriggerBackSpring;
+
+ private VelocityTracker mVelocityTracker;
+ private int mArrowPaddingEnd;
+ private WindowManager.LayoutParams mLayoutParams;
+
+ /**
+ * True if the panel is currently on the left of the screen
+ */
+ private boolean mIsLeftPanel;
+
+ private float mStartX;
+ private float mStartY;
+ private float mCurrentAngle;
+ /**
+ * The current translation of the arrow
+ */
+ private float mCurrentTranslation;
+ /**
+ * Where the arrow will be in the resting position.
+ */
+ private float mDesiredTranslation;
+
+ private boolean mDragSlopPassed;
+ private boolean mArrowsPointLeft;
+ private float mMaxTranslation;
+ private boolean mTriggerBack;
+ private float mPreviousTouchTranslation;
+ private float mTotalTouchDelta;
+ private float mVerticalTranslation;
+ private float mDesiredVerticalTranslation;
+ private float mDesiredAngle;
+ private float mAngleOffset;
+ private float mDisappearAmount;
+ private long mVibrationTime;
+ private int mScreenSize;
+
+ private final DynamicAnimation.OnAnimationEndListener mSetGoneEndListener =
+ new DynamicAnimation.OnAnimationEndListener() {
+ @Override
+ public void onAnimationEnd(
+ DynamicAnimation animation, boolean canceled, float value, float velocity) {
+ animation.removeEndListener(this);
+ if (!canceled) {
+ setVisibility(GONE);
+ }
+ }
+ };
+
+ private static final FloatPropertyCompat<EdgeBackGesturePanel> CURRENT_ANGLE =
+ new FloatPropertyCompat<EdgeBackGesturePanel>("currentAngle") {
+ @Override
+ public void setValue(EdgeBackGesturePanel object, float value) {
+ object.setCurrentAngle(value);
+ }
+
+ @Override
+ public float getValue(EdgeBackGesturePanel object) {
+ return object.getCurrentAngle();
+ }
+ };
+
+ private static final FloatPropertyCompat<EdgeBackGesturePanel> CURRENT_TRANSLATION =
+ new FloatPropertyCompat<EdgeBackGesturePanel>("currentTranslation") {
+ @Override
+ public void setValue(EdgeBackGesturePanel object, float value) {
+ object.setCurrentTranslation(value);
+ }
+
+ @Override
+ public float getValue(EdgeBackGesturePanel object) {
+ return object.getCurrentTranslation();
+ }
+ };
+
+ private static final FloatPropertyCompat<EdgeBackGesturePanel> CURRENT_VERTICAL_TRANSLATION =
+ new FloatPropertyCompat<EdgeBackGesturePanel>("verticalTranslation") {
+
+ @Override
+ public void setValue(EdgeBackGesturePanel object, float value) {
+ object.setVerticalTranslation(value);
+ }
+
+ @Override
+ public float getValue(EdgeBackGesturePanel object) {
+ return object.getVerticalTranslation();
+ }
+ };
+
+ public EdgeBackGesturePanel(Context context) {
+ super(context);
+
+ mWindowManager = context.getSystemService(WindowManager.class);
+
+ mDensity = context.getResources().getDisplayMetrics().density;
+
+ mBaseTranslation = dp(BASE_TRANSLATION_DP);
+ mArrowLength = dp(ARROW_LENGTH_DP);
+ mArrowThickness = dp(ARROW_THICKNESS_DP);
+ mMinDeltaForSwitch = dp(32);
+
+ mPaint.setStrokeWidth(mArrowThickness);
+ mPaint.setStrokeCap(Paint.Cap.ROUND);
+ mPaint.setAntiAlias(true);
+ mPaint.setStyle(Paint.Style.STROKE);
+ mPaint.setStrokeJoin(Paint.Join.ROUND);
+
+ mArrowDisappearAnimation = ValueAnimator.ofFloat(0.0f, 1.0f);
+ mArrowDisappearAnimation.setDuration(DISAPPEAR_ARROW_ANIMATION_DURATION_MS);
+ mArrowDisappearAnimation.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
+ mArrowDisappearAnimation.addUpdateListener(animation -> {
+ mDisappearAmount = (float) animation.getAnimatedValue();
+ invalidate();
+ });
+
+ mAngleAnimation =
+ new SpringAnimation(this, CURRENT_ANGLE);
+ mAngleAppearForce = new SpringForce()
+ .setStiffness(500)
+ .setDampingRatio(0.5f);
+ mAngleDisappearForce = new SpringForce()
+ .setStiffness(SpringForce.STIFFNESS_MEDIUM)
+ .setDampingRatio(SpringForce.DAMPING_RATIO_MEDIUM_BOUNCY)
+ .setFinalPosition(90);
+ mAngleAnimation.setSpring(mAngleAppearForce).setMaxValue(90);
+
+ mTranslationAnimation =
+ new SpringAnimation(this, CURRENT_TRANSLATION);
+ mRegularTranslationSpring = new SpringForce()
+ .setStiffness(SpringForce.STIFFNESS_MEDIUM)
+ .setDampingRatio(SpringForce.DAMPING_RATIO_LOW_BOUNCY);
+ mTriggerBackSpring = new SpringForce()
+ .setStiffness(450)
+ .setDampingRatio(SpringForce.DAMPING_RATIO_LOW_BOUNCY);
+ mTranslationAnimation.setSpring(mRegularTranslationSpring);
+ mVerticalTranslationAnimation =
+ new SpringAnimation(this, CURRENT_VERTICAL_TRANSLATION);
+ mVerticalTranslationAnimation.setSpring(
+ new SpringForce()
+ .setStiffness(SpringForce.STIFFNESS_MEDIUM)
+ .setDampingRatio(SpringForce.DAMPING_RATIO_LOW_BOUNCY));
+ mPaint.setColor(context.getColor(R.color.back_arrow_color_dark));
+ loadDimens();
+ updateArrowDirection();
+
+ mSwipeThreshold = ResourceUtils.getDimenByName(
+ "navigation_edge_action_drag_threshold", context.getResources(), 16 /* defaultValue */);
+ setVisibility(GONE);
+ }
+
+ void onDestroy() {
+ mWindowManager.removeView(this);
+ }
+
+ @Override
+ public boolean hasOverlappingRendering() {
+ return false;
+ }
+
+ @SuppressLint("RtlHardcoded")
+ void setIsLeftPanel(boolean isLeftPanel) {
+ mIsLeftPanel = isLeftPanel;
+ mLayoutParams.gravity = mIsLeftPanel
+ ? (Gravity.LEFT | Gravity.TOP)
+ : (Gravity.RIGHT | Gravity.TOP);
+ }
+
+ boolean getIsLeftPanel() {
+ return mIsLeftPanel;
+ }
+
+ void setDisplaySize(Point displaySize) {
+ mDisplaySize.set(displaySize.x, displaySize.y);
+ mScreenSize = Math.min(mDisplaySize.x, mDisplaySize.y);
+ }
+
+ void setBackCallback(BackCallback callback) {
+ mBackCallback = callback;
+ }
+
+ void setLayoutParams(WindowManager.LayoutParams layoutParams) {
+ mLayoutParams = layoutParams;
+ mWindowManager.addView(this, mLayoutParams);
+ }
+
+ private float getCurrentAngle() {
+ return mCurrentAngle;
+ }
+
+ private float getCurrentTranslation() {
+ return mCurrentTranslation;
+ }
+
+ void onMotionEvent(MotionEvent event) {
+ if (mVelocityTracker == null) {
+ mVelocityTracker = VelocityTracker.obtain();
+ }
+ mVelocityTracker.addMovement(event);
+ switch (event.getActionMasked()) {
+ case MotionEvent.ACTION_DOWN:
+ mDragSlopPassed = false;
+ resetOnDown();
+ mStartX = event.getX();
+ mStartY = event.getY();
+ setVisibility(VISIBLE);
+ updatePosition(event.getY());
+ mWindowManager.updateViewLayout(this, mLayoutParams);
+ break;
+ case MotionEvent.ACTION_MOVE:
+ handleMoveEvent(event);
+ break;
+ case MotionEvent.ACTION_UP:
+ if (mTriggerBack) {
+ triggerBack();
+ } else {
+ cancelBack();
+ }
+ mVelocityTracker.recycle();
+ mVelocityTracker = null;
+ break;
+ case MotionEvent.ACTION_CANCEL:
+ cancelBack();
+ mVelocityTracker.recycle();
+ mVelocityTracker = null;
+ break;
+ }
+ }
+
+ @Override
+ protected void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+ updateArrowDirection();
+ loadDimens();
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ float pointerPosition = mCurrentTranslation - mArrowThickness / 2.0f;
+ canvas.save();
+ canvas.translate(
+ mIsLeftPanel ? pointerPosition : getWidth() - pointerPosition,
+ (getHeight() * 0.5f) + mVerticalTranslation);
+
+ // Let's calculate the position of the end based on the angle
+ float x = (polarToCartX(mCurrentAngle) * mArrowLength);
+ float y = (polarToCartY(mCurrentAngle) * mArrowLength);
+ Path arrowPath = calculatePath(x, y);
+
+ canvas.drawPath(arrowPath, mPaint);
+ canvas.restore();
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ super.onLayout(changed, left, top, right, bottom);
+ mMaxTranslation = getWidth() - mArrowPaddingEnd;
+ }
+
+ private void loadDimens() {
+ Resources res = getResources();
+ mArrowPaddingEnd = ResourceUtils.getDimenByName("navigation_edge_panel_padding", res, 8);
+ mMinArrowPosition = ResourceUtils.getDimenByName("navigation_edge_arrow_min_y", res, 64);
+ mFingerOffset = ResourceUtils.getDimenByName("navigation_edge_finger_offset", res, 48);
+ }
+
+ private void updateArrowDirection() {
+ // Both panels arrow point the same way
+ mArrowsPointLeft = getLayoutDirection() == LAYOUT_DIRECTION_LTR;
+ invalidate();
+ }
+
+ private float getStaticArrowWidth() {
+ return polarToCartX(ARROW_ANGLE_WHEN_EXTENDED_DEGREES) * mArrowLength;
+ }
+
+ private float polarToCartX(float angleInDegrees) {
+ return (float) Math.cos(Math.toRadians(angleInDegrees));
+ }
+
+ private float polarToCartY(float angleInDegrees) {
+ return (float) Math.sin(Math.toRadians(angleInDegrees));
+ }
+
+ private Path calculatePath(float x, float y) {
+ if (!mArrowsPointLeft) {
+ x = -x;
+ }
+ float extent = lerp(1.0f, 0.75f, mDisappearAmount);
+ x = x * extent;
+ y = y * extent;
+ mArrowPath.reset();
+ mArrowPath.moveTo(x, y);
+ mArrowPath.lineTo(0, 0);
+ mArrowPath.lineTo(x, -y);
+ return mArrowPath;
+ }
+
+ private static float lerp(float start, float stop, float amount) {
+ return start + (stop - start) * amount;
+ }
+
+ private void triggerBack() {
+ if (mBackCallback != null) {
+ mBackCallback.triggerBack();
+ }
+
+ if (mVelocityTracker == null) {
+ mVelocityTracker = VelocityTracker.obtain();
+ }
+ mVelocityTracker.computeCurrentVelocity(1000);
+ // Only do the extra translation if we're not already flinging
+ boolean isSlow = Math.abs(mVelocityTracker.getXVelocity()) < 500;
+ if (isSlow
+ || SystemClock.uptimeMillis() - mVibrationTime >= GESTURE_DURATION_FOR_CLICK_MS) {
+ VibratorWrapper.INSTANCE.get(getContext()).vibrate(VibratorWrapper.EFFECT_CLICK);
+ }
+
+ // Let's also snap the angle a bit
+ if (mAngleOffset > -4) {
+ mAngleOffset = Math.max(-8, mAngleOffset - 8);
+ updateAngle(true /* animated */);
+ }
+
+ // Finally, after the translation, animate back and disappear the arrow
+ Runnable translationEnd = () -> {
+ // let's snap it back
+ mAngleOffset = Math.max(0, mAngleOffset + 8);
+ updateAngle(true /* animated */);
+
+ mTranslationAnimation.setSpring(mTriggerBackSpring);
+ // Translate the arrow back a bit to make for a nice transition
+ setDesiredTranslation(mDesiredTranslation - dp(32), true /* animated */);
+ animate().alpha(0f).setDuration(DISAPPEAR_FADE_ANIMATION_DURATION_MS)
+ .withEndAction(() -> setVisibility(GONE));
+ mArrowDisappearAnimation.start();
+ };
+ if (mTranslationAnimation.isRunning()) {
+ mTranslationAnimation.addEndListener(new DynamicAnimation.OnAnimationEndListener() {
+ @Override
+ public void onAnimationEnd(DynamicAnimation animation, boolean canceled,
+ float value,
+ float velocity) {
+ animation.removeEndListener(this);
+ if (!canceled) {
+ translationEnd.run();
+ }
+ }
+ });
+ } else {
+ translationEnd.run();
+ }
+ }
+
+ private void cancelBack() {
+ if (mBackCallback != null) {
+ mBackCallback.cancelBack();
+ }
+
+ if (mTranslationAnimation.isRunning()) {
+ mTranslationAnimation.addEndListener(mSetGoneEndListener);
+ } else {
+ setVisibility(GONE);
+ }
+ }
+
+ private void resetOnDown() {
+ animate().cancel();
+ mAngleAnimation.cancel();
+ mTranslationAnimation.cancel();
+ mVerticalTranslationAnimation.cancel();
+ mArrowDisappearAnimation.cancel();
+ mAngleOffset = 0;
+ mTranslationAnimation.setSpring(mRegularTranslationSpring);
+ // Reset the arrow to the side
+ setTriggerBack(false /* triggerBack */, false /* animated */);
+ setDesiredTranslation(0, false /* animated */);
+ setCurrentTranslation(0);
+ updateAngle(false /* animate */);
+ mPreviousTouchTranslation = 0;
+ mTotalTouchDelta = 0;
+ mVibrationTime = 0;
+ setDesiredVerticalTransition(0, false /* animated */);
+ }
+
+ private void handleMoveEvent(MotionEvent event) {
+ float x = event.getX();
+ float y = event.getY();
+ float touchTranslation = Math.abs(x - mStartX);
+ float yOffset = y - mStartY;
+ float delta = touchTranslation - mPreviousTouchTranslation;
+ if (Math.abs(delta) > 0) {
+ if (Math.signum(delta) == Math.signum(mTotalTouchDelta)) {
+ mTotalTouchDelta += delta;
+ } else {
+ mTotalTouchDelta = delta;
+ }
+ }
+ mPreviousTouchTranslation = touchTranslation;
+
+ // Apply a haptic on drag slop passed
+ if (!mDragSlopPassed && touchTranslation > mSwipeThreshold) {
+ mDragSlopPassed = true;
+ VibratorWrapper.INSTANCE.get(getContext()).vibrate(VibratorWrapper.EFFECT_CLICK);
+ mVibrationTime = SystemClock.uptimeMillis();
+
+ // Let's show the arrow and animate it in!
+ mDisappearAmount = 0.0f;
+ setAlpha(1f);
+ // And animate it go to back by default!
+ setTriggerBack(true /* triggerBack */, true /* animated */);
+ }
+
+ // Let's make sure we only go to the baseextend and apply rubberbanding afterwards
+ if (touchTranslation > mBaseTranslation) {
+ float diff = touchTranslation - mBaseTranslation;
+ float progress = MathUtils.clamp(diff / (mScreenSize - mBaseTranslation), 0, 1);
+ progress = RUBBER_BAND_INTERPOLATOR.getInterpolation(progress)
+ * (mMaxTranslation - mBaseTranslation);
+ touchTranslation = mBaseTranslation + progress;
+ } else {
+ float diff = mBaseTranslation - touchTranslation;
+ float progress = MathUtils.clamp(diff / mBaseTranslation, 0, 1);
+ progress = RUBBER_BAND_INTERPOLATOR_APPEAR.getInterpolation(progress)
+ * (mBaseTranslation / RUBBER_BAND_AMOUNT_APPEAR);
+ touchTranslation = mBaseTranslation - progress;
+ }
+ // By default we just assume the current direction is kept
+ boolean triggerBack = mTriggerBack;
+
+ // First lets see if we had continuous motion in one direction for a while
+ if (Math.abs(mTotalTouchDelta) > mMinDeltaForSwitch) {
+ triggerBack = mTotalTouchDelta > 0;
+ }
+
+ // Then, let's see if our velocity tells us to change direction
+ mVelocityTracker.computeCurrentVelocity(1000);
+ float xVelocity = mVelocityTracker.getXVelocity();
+ float yVelocity = mVelocityTracker.getYVelocity();
+ float velocity = (float) Math.hypot(xVelocity, yVelocity);
+ mAngleOffset = Math.min(velocity / 1000 * ARROW_ANGLE_ADDED_PER_1000_SPEED,
+ ARROW_MAX_ANGLE_SPEED_OFFSET_DEGREES) * Math.signum(xVelocity);
+ if (mIsLeftPanel && mArrowsPointLeft || !mIsLeftPanel && !mArrowsPointLeft) {
+ mAngleOffset *= -1;
+ }
+
+ // Last if the direction in Y is bigger than X * 2 we also abort
+ if (Math.abs(yOffset) > Math.abs(x - mStartX) * 2) {
+ triggerBack = false;
+ }
+ setTriggerBack(triggerBack, true /* animated */);
+
+ if (!mTriggerBack) {
+ touchTranslation = 0;
+ } else if (mIsLeftPanel && mArrowsPointLeft
+ || (!mIsLeftPanel && !mArrowsPointLeft)) {
+ // If we're on the left we should move less, because the arrow is facing the other
+ // direction
+ touchTranslation -= getStaticArrowWidth();
+ }
+ setDesiredTranslation(touchTranslation, true /* animated */);
+ updateAngle(true /* animated */);
+
+ float maxYOffset = getHeight() / 2.0f - mArrowLength;
+ float progress =
+ MathUtils.clamp(Math.abs(yOffset) / (maxYOffset * RUBBER_BAND_AMOUNT), 0, 1);
+ float verticalTranslation = RUBBER_BAND_INTERPOLATOR.getInterpolation(progress)
+ * maxYOffset * Math.signum(yOffset);
+ setDesiredVerticalTransition(verticalTranslation, true /* animated */);
+ }
+
+ private void updatePosition(float touchY) {
+ float position = touchY - mFingerOffset;
+ position = Math.max(position, mMinArrowPosition);
+ position -= mLayoutParams.height / 2.0f;
+ mLayoutParams.y = MathUtils.clamp((int) position, 0, mDisplaySize.y);
+ }
+
+ private void setDesiredVerticalTransition(float verticalTranslation, boolean animated) {
+ if (mDesiredVerticalTranslation != verticalTranslation) {
+ mDesiredVerticalTranslation = verticalTranslation;
+ if (!animated) {
+ setVerticalTranslation(verticalTranslation);
+ } else {
+ mVerticalTranslationAnimation.animateToFinalPosition(verticalTranslation);
+ }
+ invalidate();
+ }
+ }
+
+ private void setVerticalTranslation(float verticalTranslation) {
+ mVerticalTranslation = verticalTranslation;
+ invalidate();
+ }
+
+ private float getVerticalTranslation() {
+ return mVerticalTranslation;
+ }
+
+ private void setDesiredTranslation(float desiredTranslation, boolean animated) {
+ if (mDesiredTranslation != desiredTranslation) {
+ mDesiredTranslation = desiredTranslation;
+ if (!animated) {
+ setCurrentTranslation(desiredTranslation);
+ } else {
+ mTranslationAnimation.animateToFinalPosition(desiredTranslation);
+ }
+ }
+ }
+
+ private void setCurrentTranslation(float currentTranslation) {
+ mCurrentTranslation = currentTranslation;
+ invalidate();
+ }
+
+ private void setTriggerBack(boolean triggerBack, boolean animated) {
+ if (mTriggerBack != triggerBack) {
+ mTriggerBack = triggerBack;
+ mAngleAnimation.cancel();
+ updateAngle(animated);
+ // Whenever the trigger back state changes the existing translation animation should be
+ // cancelled
+ mTranslationAnimation.cancel();
+ }
+ }
+
+ private void updateAngle(boolean animated) {
+ float newAngle = mTriggerBack ? ARROW_ANGLE_WHEN_EXTENDED_DEGREES + mAngleOffset : 90;
+ if (newAngle != mDesiredAngle) {
+ if (!animated) {
+ setCurrentAngle(newAngle);
+ } else {
+ mAngleAnimation.setSpring(mTriggerBack ? mAngleAppearForce : mAngleDisappearForce);
+ mAngleAnimation.animateToFinalPosition(newAngle);
+ }
+ mDesiredAngle = newAngle;
+ }
+ }
+
+ private void setCurrentAngle(float currentAngle) {
+ mCurrentAngle = currentAngle;
+ invalidate();
+ }
+
+ private float dp(float dp) {
+ return mDensity * dp;
+ }
+
+ /** Callback to let the gesture handler react to the detected back gestures. */
+ interface BackCallback {
+ /** Indicates that a Back gesture was recognized. */
+ void triggerBack();
+
+ /** Indicates that the gesture was cancelled. */
+ void cancelBack();
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/interaction/GestureSandboxActivity.java b/quickstep/src/com/android/quickstep/interaction/GestureSandboxActivity.java
index 8081ad7..4815366 100644
--- a/quickstep/src/com/android/quickstep/interaction/GestureSandboxActivity.java
+++ b/quickstep/src/com/android/quickstep/interaction/GestureSandboxActivity.java
@@ -30,12 +30,11 @@
import com.android.quickstep.interaction.BackGestureTutorialFragment.TutorialType;
import java.util.List;
-import java.util.Optional;
/** Shows the gesture interactive sandbox in full screen mode. */
public class GestureSandboxActivity extends FragmentActivity {
- Optional<BackGestureTutorialFragment> mFragment = Optional.empty();
+ private BackGestureTutorialFragment mFragment;
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -43,10 +42,10 @@
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.back_gesture_tutorial_activity);
- mFragment = Optional.of(BackGestureTutorialFragment.newInstance(TutorialStep.ENGAGED,
- TutorialType.RIGHT_EDGE_BACK_NAVIGATION));
+ mFragment = BackGestureTutorialFragment.newInstance(
+ TutorialStep.ENGAGED, TutorialType.RIGHT_EDGE_BACK_NAVIGATION);
getSupportFragmentManager().beginTransaction()
- .add(R.id.back_gesture_tutorial_fragment_container, mFragment.get())
+ .add(R.id.back_gesture_tutorial_fragment_container, mFragment)
.commit();
}
@@ -54,6 +53,13 @@
public void onAttachedToWindow() {
super.onAttachedToWindow();
disableSystemGestures();
+ mFragment.onAttachedToWindow();
+ }
+
+ @Override
+ public void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ mFragment.onDetachedFromWindow();
}
@Override
@@ -64,13 +70,6 @@
}
}
- @Override
- public void onBackPressed() {
- if (mFragment.isPresent()) {
- mFragment.get().onBackPressed();
- }
- }
-
private void hideSystemUI() {
getWindow().getDecorView().setSystemUiVisibility(
View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
diff --git a/quickstep/src/com/android/quickstep/util/RemoteAnimationProvider.java b/quickstep/src/com/android/quickstep/util/RemoteAnimationProvider.java
index 6520c4f..21b97ec 100644
--- a/quickstep/src/com/android/quickstep/util/RemoteAnimationProvider.java
+++ b/quickstep/src/com/android/quickstep/util/RemoteAnimationProvider.java
@@ -21,21 +21,22 @@
import android.os.Handler;
import com.android.launcher3.LauncherAnimationRunner;
+import com.android.launcher3.WrappedLauncherAnimationRunner;
import com.android.systemui.shared.system.ActivityOptionsCompat;
import com.android.systemui.shared.system.RemoteAnimationAdapterCompat;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
import com.android.systemui.shared.system.TransactionCompat;
-@FunctionalInterface
-public interface RemoteAnimationProvider {
+public abstract class RemoteAnimationProvider {
+ LauncherAnimationRunner mAnimationRunner;
static final int Z_BOOST_BASE = 800570000;
- AnimatorSet createWindowAnimation(RemoteAnimationTargetCompat[] appTargets,
+ public abstract AnimatorSet createWindowAnimation(RemoteAnimationTargetCompat[] appTargets,
RemoteAnimationTargetCompat[] wallpaperTargets);
- default ActivityOptions toActivityOptions(Handler handler, long duration, Context context) {
- LauncherAnimationRunner runner = new LauncherAnimationRunner(handler,
+ ActivityOptions toActivityOptions(Handler handler, long duration, Context context) {
+ mAnimationRunner = new LauncherAnimationRunner(handler,
false /* startAtFrontOfQueue */) {
@Override
@@ -44,8 +45,11 @@
result.setAnimation(createWindowAnimation(appTargets, wallpaperTargets), context);
}
};
+ final LauncherAnimationRunner wrapper = new WrappedLauncherAnimationRunner(
+ mAnimationRunner, false /* startAtFrontOfQueue */);
+
return ActivityOptionsCompat.makeRemoteAnimation(
- new RemoteAnimationAdapterCompat(runner, duration, 0));
+ new RemoteAnimationAdapterCompat(wrapper, duration, 0));
}
/**
@@ -63,7 +67,7 @@
}
}
- static int getLayer(RemoteAnimationTargetCompat target, int boostModeTarget) {
+ public static int getLayer(RemoteAnimationTargetCompat target, int boostModeTarget) {
return target.mode == boostModeTarget
? Z_BOOST_BASE + target.prefixOrderIndex
: target.prefixOrderIndex;
@@ -72,7 +76,7 @@
/**
* @return the target with the lowest opaque layer for a certain app animation, or null.
*/
- static RemoteAnimationTargetCompat findLowestOpaqueLayerTarget(
+ public static RemoteAnimationTargetCompat findLowestOpaqueLayerTarget(
RemoteAnimationTargetCompat[] appTargets, int mode) {
int lowestLayer = Integer.MAX_VALUE;
int lowestLayerIndex = -1;
diff --git a/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java b/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java
index 4ac815e..0afe4a8 100644
--- a/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java
+++ b/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java
@@ -166,12 +166,9 @@
return false;
}
- setOverlayPackageEnabled(NAV_BAR_MODE_3BUTTON_OVERLAY,
- overlayPackage == NAV_BAR_MODE_3BUTTON_OVERLAY);
- setOverlayPackageEnabled(NAV_BAR_MODE_2BUTTON_OVERLAY,
- overlayPackage == NAV_BAR_MODE_2BUTTON_OVERLAY);
- setOverlayPackageEnabled(NAV_BAR_MODE_GESTURAL_OVERLAY,
- overlayPackage == NAV_BAR_MODE_GESTURAL_OVERLAY);
+ Log.d(TAG, "setActiveOverlay: " + overlayPackage + "...");
+ UiDevice.getInstance(getInstrumentation()).executeShellCommand(
+ "cmd overlay enable-exclusive " + overlayPackage);
if (currentSysUiNavigationMode() != expectedMode) {
final CountDownLatch latch = new CountDownLatch(1);
@@ -210,14 +207,6 @@
return true;
}
- private static void setOverlayPackageEnabled(String overlayPackage, boolean enable)
- throws Exception {
- Log.d(TAG, "setOverlayPackageEnabled: " + overlayPackage + " " + enable);
- final String action = enable ? "enable" : "disable";
- UiDevice.getInstance(getInstrumentation()).executeShellCommand(
- "cmd overlay " + action + " " + overlayPackage);
- }
-
private static boolean packageExists(String packageName) {
try {
PackageManager pm = getInstrumentation().getContext().getPackageManager();
diff --git a/res/layout/work_mode_switch.xml b/res/layout/work_mode_switch.xml
new file mode 100644
index 0000000..9cb7ce8
--- /dev/null
+++ b/res/layout/work_mode_switch.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<com.android.launcher3.allapps.WorkModeSwitch
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ style="@style/PrimaryMediumText"
+ android:id="@+id/work_mode_toggle"
+ android:drawableStart="@drawable/ic_corp"
+ android:drawablePadding="16dp"
+ android:drawableTint="?attr/workProfileOverlayTextColor"
+ android:textColor="?attr/workProfileOverlayTextColor"
+ android:layout_alignParentBottom="true"
+ android:ellipsize="end"
+ android:gravity="start"
+ android:lines="1"
+ android:showText="false"
+ android:textSize="16sp"
+ android:background="?attr/allAppsScrimColor"
+ android:text="@string/work_profile_toggle_label"
+ android:paddingBottom="@dimen/all_apps_work_profile_tab_footer_padding"
+ android:paddingLeft="@dimen/all_apps_work_profile_tab_footer_padding"
+ android:paddingRight="@dimen/all_apps_work_profile_tab_footer_padding"
+ android:paddingTop="@dimen/all_apps_work_profile_tab_footer_padding"
+/>
diff --git a/res/layout/work_tab_footer.xml b/res/layout/work_tab_footer.xml
deleted file mode 100644
index dbcdbdb..0000000
--- a/res/layout/work_tab_footer.xml
+++ /dev/null
@@ -1,50 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<com.android.launcher3.views.WorkFooterContainer
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:id="@+id/work_toggle_container"
- android:focusable="true"
- android:orientation="horizontal"
- android:background="?attr/allAppsScrimColor"
- android:paddingBottom="@dimen/all_apps_work_profile_tab_footer_padding"
- android:paddingLeft="@dimen/all_apps_work_profile_tab_footer_padding"
- android:paddingRight="@dimen/all_apps_work_profile_tab_footer_padding"
- android:paddingTop="@dimen/all_apps_work_profile_tab_footer_padding">
-
- <TextView
- style="@style/PrimaryMediumText"
- android:id="@+id/work_mode_label"
- android:layout_width="0dp"
- android:layout_weight="1"
- android:drawableStart="@drawable/ic_corp"
- android:drawablePadding="16dp"
- android:drawableTint="?attr/workProfileOverlayTextColor"
- android:textColor="?attr/workProfileOverlayTextColor"
- android:layout_height="wrap_content"
- android:ellipsize="end"
- android:gravity="center_vertical"
- android:lines="1"
- android:minHeight="24dp"
- android:paddingEnd="12dp"
- android:textSize="16sp"/>
- <com.android.launcher3.allapps.WorkModeSwitch
- android:id="@+id/work_mode_toggle"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
-
-</com.android.launcher3.views.WorkFooterContainer>
\ No newline at end of file
diff --git a/res/values/config.xml b/res/values/config.xml
index df0f233..1675a98 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -142,6 +142,7 @@
<item name="staggered_damping_ratio" type="dimen" format="float">0.7</item>
<item name="staggered_stiffness" type="dimen" format="float">150</item>
+ <dimen name="unlock_staggered_velocity_dp_per_s">3dp</dimen>
<!-- Swipe up to home related -->
<dimen name="swipe_up_fling_min_visible_change">18dp</dimen>
@@ -170,8 +171,12 @@
<item>@dimen/staggered_damping_ratio</item>
<item>@dimen/staggered_stiffness</item>
+ <item>@dimen/unlock_staggered_velocity_dp_per_s</item>
<item>@dimen/swipe_up_fling_min_visible_change</item>
<item>@dimen/swipe_up_y_overshoot</item>
</array>
+
+ <string-array name="live_wallpapers_remove_sysui_scrims">
+ </string-array>
</resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 9d0fb56..b1077be 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -208,7 +208,7 @@
<string name="notification_dots_service_title">Show notification dots</string>
<!-- Label for the setting that allows the automatic placement of launcher shortcuts for applications and games installed on the device [CHAR LIMIT=40] -->
- <string name="auto_add_shortcuts_label">Add icon to Home screen</string>
+ <string name="auto_add_shortcuts_label">Add app icons to Home screen</string>
<!-- Text description of the setting that allows the automatic placement of launcher shortcuts for applications and games installed on the device [CHAR LIMIT=NONE] -->
<string name="auto_add_shortcuts_description">For new apps</string>
@@ -328,22 +328,21 @@
<!-- This string is in the work profile tab when a user has All Apps open on their phone. This is a label for a toggle to turn the work profile on and off. "Work profile" means a separate profile on a user's phone that's specifically for their work apps and managed by their company. "Work" is used as an adjective.-->
<string name="work_profile_toggle_label">Work profile</string>
<!--- User onboarding title for personal apps -->
- <string name="work_profile_edu_personal_apps">Personal apps are private & can\'t be seen by IT</string>
+ <string name="work_profile_edu_personal_apps">Personal data is separate & hidden from work apps</string>
<!--- User onboarding title for work profile apps -->
- <string name="work_profile_edu_work_apps">Work apps are badged & visible to IT</string>
+ <string name="work_profile_edu_work_apps">Work apps & data are visible to your IT admin</string>
<!-- Action label to proceed to the next work profile edu section-->
<string name="work_profile_edu_next">Next</string>
<!-- Action label to finish work profile edu-->
<string name="work_profile_edu_accept">Got it</string>
- <!-- This string is in the work profile tab when a user has All Apps open on their phone. It describes the label of a toggle, "Work profile," as being managed by the user's employer.
- "Organization" is used to represent a variety of businesses, non-profits, and educational institutions).-->
- <string name="work_mode_on_label">Work apps: On</string>
- <!-- This string appears under a the label of a toggle in the work profile tab on a user's phone. It describes the status of the toggle, "Work profile," when it's turned off. "Work profile" means a separate profile on a user's phone that's speficially for their work apps and is managed by their company.-->
- <string name="work_mode_off_label">Work apps: Paused</string>
+ <!--- heading shown when user opens work apps tab while work apps are paused -->
+ <string name="work_apps_paused_title">Work profile is paused</string>
+ <!--- body shown when user opens work apps tab while work apps are paused -->
+ <string name="work_apps_paused_body">Work apps can\’t send you notifications, use your battery, or access your location</string>
- <string name="work_apps_paused_title">Work apps are paused</string>
- <string name="work_apps_paused_body">You won\'t get any work notifications, and your IT admin can\'t see your location</string>
+ <!-- A tip shown pointing at work toggle -->
+ <string name="work_switch_tip">Pause work apps and notifications</string>
<!-- Failed action error message: e.g. Failed: Pause -->
<string name="remote_action_failed">Failed: <xliff:g id="what" example="Pause">%1$s</xliff:g></string>
diff --git a/robolectric_tests/src/com/android/launcher3/model/GridSizeMigrationTaskV2Test.java b/robolectric_tests/src/com/android/launcher3/model/GridSizeMigrationTaskV2Test.java
new file mode 100644
index 0000000..8f58d8b
--- /dev/null
+++ b/robolectric_tests/src/com/android/launcher3/model/GridSizeMigrationTaskV2Test.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.model;
+
+import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_DESKTOP;
+import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT;
+import static com.android.launcher3.LauncherSettings.Favorites.TMP_CONTENT_URI;
+import static com.android.launcher3.provider.LauncherDbUtils.dropTable;
+import static com.android.launcher3.util.LauncherModelHelper.APP_ICON;
+import static com.android.launcher3.util.LauncherModelHelper.DESKTOP;
+import static com.android.launcher3.util.LauncherModelHelper.HOTSEAT;
+import static com.android.launcher3.util.LauncherModelHelper.SHORTCUT;
+import static com.android.launcher3.util.LauncherModelHelper.TEST_PACKAGE;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.content.Context;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.graphics.Point;
+import android.os.Process;
+
+import com.android.launcher3.InvariantDeviceProfile;
+import com.android.launcher3.LauncherSettings;
+import com.android.launcher3.pm.UserCache;
+import com.android.launcher3.util.LauncherModelHelper;
+import com.android.launcher3.util.LauncherRoboTestRunner;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RuntimeEnvironment;
+
+import java.util.HashSet;
+
+/** Unit tests for {@link GridSizeMigrationTaskV2} */
+@RunWith(LauncherRoboTestRunner.class)
+public class GridSizeMigrationTaskV2Test {
+
+ private LauncherModelHelper mModelHelper;
+ private Context mContext;
+ private SQLiteDatabase mDb;
+
+ private HashSet<String> mValidPackages;
+ private InvariantDeviceProfile mIdp;
+
+ @Before
+ public void setUp() {
+ mModelHelper = new LauncherModelHelper();
+ mContext = RuntimeEnvironment.application;
+ mDb = mModelHelper.provider.getDb();
+
+ mValidPackages = new HashSet<>();
+ mValidPackages.add(TEST_PACKAGE);
+ mIdp = InvariantDeviceProfile.INSTANCE.get(mContext);
+
+ long userSerial = UserCache.INSTANCE.get(mContext).getSerialNumberForUser(
+ Process.myUserHandle());
+ dropTable(mDb, LauncherSettings.Favorites.TMP_TABLE);
+ LauncherSettings.Favorites.addTableToDb(mDb, userSerial, false,
+ LauncherSettings.Favorites.TMP_TABLE);
+ }
+
+ @Test
+ public void testMigration() {
+ final String testPackage1 = "com.android.launcher3.validpackage1";
+ final String testPackage2 = "com.android.launcher3.validpackage2";
+ final String testPackage3 = "com.android.launcher3.validpackage3";
+ final String testPackage4 = "com.android.launcher3.validpackage4";
+ final String testPackage5 = "com.android.launcher3.validpackage5";
+ final String testPackage7 = "com.android.launcher3.validpackage7";
+
+ mValidPackages.add(testPackage1);
+ mValidPackages.add(testPackage2);
+ mValidPackages.add(testPackage3);
+ mValidPackages.add(testPackage4);
+ mValidPackages.add(testPackage5);
+ mValidPackages.add(testPackage7);
+
+ int[] srcHotseatItems = {
+ mModelHelper.addItem(APP_ICON, 0, HOTSEAT, 0, 0, testPackage1, 1, TMP_CONTENT_URI),
+ mModelHelper.addItem(SHORTCUT, 1, HOTSEAT, 0, 0, testPackage2, 2, TMP_CONTENT_URI),
+ -1,
+ mModelHelper.addItem(SHORTCUT, 3, HOTSEAT, 0, 0, testPackage3, 3, TMP_CONTENT_URI),
+ mModelHelper.addItem(APP_ICON, 4, HOTSEAT, 0, 0, testPackage4, 4, TMP_CONTENT_URI),
+ };
+ mModelHelper.addItem(APP_ICON, 0, DESKTOP, 2, 2, testPackage5, 5, TMP_CONTENT_URI);
+
+ int[] destHotseatItems = {
+ -1,
+ mModelHelper.addItem(SHORTCUT, 1, HOTSEAT, 0, 0, testPackage2),
+ -1,
+ };
+ mModelHelper.addItem(APP_ICON, 0, DESKTOP, 2, 2, testPackage7);
+
+ mIdp.numHotseatIcons = 3;
+ mIdp.numColumns = 3;
+ mIdp.numRows = 3;
+ GridSizeMigrationTaskV2.DbReader srcReader = new GridSizeMigrationTaskV2.DbReader(mDb,
+ LauncherSettings.Favorites.TMP_TABLE, mContext, mValidPackages, 5);
+ GridSizeMigrationTaskV2.DbReader destReader = new GridSizeMigrationTaskV2.DbReader(mDb,
+ LauncherSettings.Favorites.TABLE_NAME, mContext, mValidPackages, 3);
+ GridSizeMigrationTaskV2 task = new GridSizeMigrationTaskV2(mContext, mDb, srcReader,
+ destReader, 3, new Point(mIdp.numColumns, mIdp.numRows));
+ task.migrate();
+
+ Cursor c = mContext.getContentResolver().query(LauncherSettings.Favorites.CONTENT_URI,
+ new String[]{LauncherSettings.Favorites.SCREEN, LauncherSettings.Favorites.INTENT},
+ "container=" + CONTAINER_HOTSEAT, null, null, null);
+ assertEquals(c.getCount(), 3);
+ int screenIndex = c.getColumnIndex(LauncherSettings.Favorites.SCREEN);
+ int intentIndex = c.getColumnIndex(LauncherSettings.Favorites.INTENT);
+ c.moveToNext();
+ assertEquals(c.getInt(screenIndex), 1);
+ assertTrue(c.getString(intentIndex).contains(testPackage2));
+ c.moveToNext();
+ assertEquals(c.getInt(screenIndex), 0);
+ assertTrue(c.getString(intentIndex).contains(testPackage1));
+ c.moveToNext();
+ assertEquals(c.getInt(screenIndex), 2);
+ assertTrue(c.getString(intentIndex).contains(testPackage3));
+ c.close();
+
+ c = mContext.getContentResolver().query(LauncherSettings.Favorites.CONTENT_URI,
+ new String[]{LauncherSettings.Favorites.CELLX, LauncherSettings.Favorites.CELLY,
+ LauncherSettings.Favorites.INTENT},
+ "container=" + CONTAINER_DESKTOP, null, null, null);
+ assertEquals(c.getCount(), 2);
+ intentIndex = c.getColumnIndex(LauncherSettings.Favorites.INTENT);
+ int cellXIndex = c.getColumnIndex(LauncherSettings.Favorites.CELLX);
+ int cellYIndex = c.getColumnIndex(LauncherSettings.Favorites.CELLY);
+
+ c.moveToNext();
+ assertTrue(c.getString(intentIndex).contains(testPackage7));
+ c.moveToNext();
+ assertTrue(c.getString(intentIndex).contains(testPackage5));
+ assertEquals(c.getInt(cellXIndex), 0);
+ assertEquals(c.getInt(cellYIndex), 2);
+ }
+}
diff --git a/robolectric_tests/src/com/android/launcher3/util/LauncherModelHelper.java b/robolectric_tests/src/com/android/launcher3/util/LauncherModelHelper.java
index e133cf2..20b1453 100644
--- a/robolectric_tests/src/com/android/launcher3/util/LauncherModelHelper.java
+++ b/robolectric_tests/src/com/android/launcher3/util/LauncherModelHelper.java
@@ -230,7 +230,21 @@
}
public int addItem(int type, int screen, int container, int x, int y) {
- return addItem(type, screen, container, x, y, mDefaultProfileId);
+ return addItem(type, screen, container, x, y, mDefaultProfileId, TEST_PACKAGE);
+ }
+
+ public int addItem(int type, int screen, int container, int x, int y, long profileId) {
+ return addItem(type, screen, container, x, y, profileId, TEST_PACKAGE);
+ }
+
+ public int addItem(int type, int screen, int container, int x, int y, String packageName) {
+ return addItem(type, screen, container, x, y, mDefaultProfileId, packageName);
+ }
+
+ public int addItem(int type, int screen, int container, int x, int y, String packageName,
+ int id, Uri contentUri) {
+ addItem(type, screen, container, x, y, mDefaultProfileId, packageName, id, contentUri);
+ return id;
}
/**
@@ -238,11 +252,19 @@
* @param type {@link #APP_ICON} or {@link #SHORTCUT} or >= 2 for
* folder (where the type represents the number of items in the folder).
*/
- public int addItem(int type, int screen, int container, int x, int y, long profileId) {
+ public int addItem(int type, int screen, int container, int x, int y, long profileId,
+ String packageName) {
Context context = RuntimeEnvironment.application;
int id = LauncherSettings.Settings.call(context.getContentResolver(),
LauncherSettings.Settings.METHOD_NEW_ITEM_ID)
.getInt(LauncherSettings.Settings.EXTRA_VALUE);
+ addItem(type, screen, container, x, y, profileId, packageName, id, CONTENT_URI);
+ return id;
+ }
+
+ public void addItem(int type, int screen, int container, int x, int y, long profileId,
+ String packageName, int id, Uri contentUri) {
+ Context context = RuntimeEnvironment.application;
ContentValues values = new ContentValues();
values.put(LauncherSettings.Favorites._ID, id);
@@ -257,7 +279,7 @@
if (type == APP_ICON || type == SHORTCUT) {
values.put(LauncherSettings.Favorites.ITEM_TYPE, type);
values.put(LauncherSettings.Favorites.INTENT,
- new Intent(Intent.ACTION_MAIN).setPackage(TEST_PACKAGE).toUri(0));
+ new Intent(Intent.ACTION_MAIN).setPackage(packageName).toUri(0));
} else {
values.put(LauncherSettings.Favorites.ITEM_TYPE,
LauncherSettings.Favorites.ITEM_TYPE_FOLDER);
@@ -267,8 +289,7 @@
}
}
- context.getContentResolver().insert(CONTENT_URI, values);
- return id;
+ context.getContentResolver().insert(contentUri, values);
}
public int[][][] createGrid(int[][][] typeArray) {
diff --git a/src/com/android/launcher3/AbstractFloatingView.java b/src/com/android/launcher3/AbstractFloatingView.java
index af2cdc3..bed8278 100644
--- a/src/com/android/launcher3/AbstractFloatingView.java
+++ b/src/com/android/launcher3/AbstractFloatingView.java
@@ -22,7 +22,6 @@
import static com.android.launcher3.compat.AccessibilityManagerCompat.isAccessibilityEnabled;
import static com.android.launcher3.compat.AccessibilityManagerCompat.sendCustomAccessibilityEvent;
-import android.animation.Animator;
import android.annotation.SuppressLint;
import android.content.Context;
import android.util.AttributeSet;
@@ -30,11 +29,12 @@
import android.view.MotionEvent;
import android.view.View;
import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.animation.Interpolator;
import android.widget.LinearLayout;
import androidx.annotation.IntDef;
-import androidx.annotation.Nullable;
+import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
import com.android.launcher3.util.TouchController;
@@ -134,9 +134,8 @@
* Creates a user-controlled animation to hint that the view will be closed if completed.
* @param distanceToMove The max distance that elements should move from their starting point.
*/
- public @Nullable Animator createHintCloseAnim(float distanceToMove) {
- return null;
- }
+ public void addHintCloseAnim(
+ float distanceToMove, Interpolator interpolator, PendingAnimation target) { }
public abstract void logActionCommand(int command);
@@ -181,7 +180,10 @@
return null;
}
- protected static <T extends AbstractFloatingView> T getOpenView(
+ /**
+ * Returns a view matching FloatingViewType
+ */
+ public static <T extends AbstractFloatingView> T getOpenView(
ActivityContext activity, @FloatingViewType int type) {
BaseDragLayer dragLayer = activity.getDragLayer();
if (dragLayer == null) return null;
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index e6f8a85..21a8fd4 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -46,6 +46,7 @@
import com.android.launcher3.Launcher.OnResumeCallback;
import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
import com.android.launcher3.dot.DotInfo;
+import com.android.launcher3.dragndrop.DraggableView;
import com.android.launcher3.folder.FolderIcon;
import com.android.launcher3.graphics.IconPalette;
import com.android.launcher3.graphics.IconShape;
@@ -65,7 +66,7 @@
* too aggressive.
*/
public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, OnResumeCallback,
- IconLabelDotView {
+ IconLabelDotView, DraggableView {
private static final int DISPLAY_WORKSPACE = 0;
private static final int DISPLAY_ALL_APPS = 1;
@@ -103,7 +104,7 @@
private final ActivityContext mActivity;
private Drawable mIcon;
- private final boolean mCenterVertically;
+ private boolean mCenterVertically;
private final int mDisplay;
@@ -701,4 +702,24 @@
public int getIconSize() {
return mIconSize;
}
+
+ @Override
+ public int getViewType() {
+ return DRAGGABLE_ICON;
+ }
+
+ @Override
+ public void getVisualDragBounds(Rect bounds) {
+ DeviceProfile grid = mActivity.getDeviceProfile();
+ BubbleTextView.getIconBounds(this, bounds, grid.iconSizePx);
+ }
+
+ @Override
+ public void prepareDrawDragView() {
+ if (getIcon() instanceof FastBitmapDrawable) {
+ FastBitmapDrawable icon = (FastBitmapDrawable) getIcon();
+ icon.setScale(1f);
+ }
+ setForceHideDot(true);
+ }
}
diff --git a/src/com/android/launcher3/ButtonDropTarget.java b/src/com/android/launcher3/ButtonDropTarget.java
index 2b0da43..34d7067 100644
--- a/src/com/android/launcher3/ButtonDropTarget.java
+++ b/src/com/android/launcher3/ButtonDropTarget.java
@@ -155,7 +155,7 @@
@Override
public final void onDragEnter(DragObject d) {
- if (!d.accessibleDrag && !mTextVisible) {
+ if (!mAccessibleDrag && !mTextVisible) {
// Show tooltip
hideTooltip();
diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java
index e3eb387..9682d09 100644
--- a/src/com/android/launcher3/CellLayout.java
+++ b/src/com/android/launcher3/CellLayout.java
@@ -53,11 +53,10 @@
import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.accessibility.DragAndDropAccessibilityDelegate;
-import com.android.launcher3.accessibility.FolderAccessibilityHelper;
-import com.android.launcher3.accessibility.WorkspaceAccessibilityHelper;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.anim.PropertyListBuilder;
import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.dragndrop.DraggableView;
import com.android.launcher3.folder.PreviewBackground;
import com.android.launcher3.graphics.DragPreviewProvider;
import com.android.launcher3.graphics.RotationMode;
@@ -79,9 +78,6 @@
import java.util.Stack;
public class CellLayout extends ViewGroup implements Transposable {
- public static final int WORKSPACE_ACCESSIBILITY_DRAG = 2;
- public static final int FOLDER_ACCESSIBILITY_DRAG = 1;
-
private static final String TAG = "CellLayout";
private static final boolean LOGD = false;
@@ -105,6 +101,11 @@
@Thunk final int[] mTmpPoint = new int[2];
@Thunk final int[] mTempLocation = new int[2];
+ // Used to visualize / debug the Grid of the CellLayout
+ private static final boolean VISUALIZE_GRID = false;
+ private Rect mVisualizeGridRect = new Rect();
+ private Paint mVisualizeGridPaint = new Paint();
+
private GridOccupancy mOccupied;
private GridOccupancy mTmpOccupied;
@@ -182,7 +183,6 @@
private static final Paint sPaint = new Paint();
// Related to accessible drag and drop
- private DragAndDropAccessibilityDelegate mTouchHelper;
private boolean mUseTouchHelper = false;
private RotationMode mRotationMode = RotationMode.NORMAL;
@@ -292,26 +292,20 @@
addView(mShortcutsAndWidgets);
}
- public void enableAccessibleDrag(boolean enable, int dragType) {
- mUseTouchHelper = enable;
- if (!enable) {
- ViewCompat.setAccessibilityDelegate(this, null);
- setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO);
- getShortcutsAndWidgets().setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO);
- setOnClickListener(null);
- } else {
- if (dragType == WORKSPACE_ACCESSIBILITY_DRAG &&
- !(mTouchHelper instanceof WorkspaceAccessibilityHelper)) {
- mTouchHelper = new WorkspaceAccessibilityHelper(this);
- } else if (dragType == FOLDER_ACCESSIBILITY_DRAG &&
- !(mTouchHelper instanceof FolderAccessibilityHelper)) {
- mTouchHelper = new FolderAccessibilityHelper(this);
- }
- ViewCompat.setAccessibilityDelegate(this, mTouchHelper);
- setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
- getShortcutsAndWidgets().setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
- setOnClickListener(mTouchHelper);
- }
+
+ /**
+ * Sets or clears a delegate used for accessible drag and drop
+ */
+ public void setDragAndDropAccessibilityDelegate(DragAndDropAccessibilityDelegate delegate) {
+ setOnClickListener(delegate);
+ setOnHoverListener(delegate);
+ ViewCompat.setAccessibilityDelegate(this, delegate);
+
+ mUseTouchHelper = delegate != null;
+ int accessibilityFlag = mUseTouchHelper
+ ? IMPORTANT_FOR_ACCESSIBILITY_YES : IMPORTANT_FOR_ACCESSIBILITY_NO;
+ setImportantForAccessibility(accessibilityFlag);
+ getShortcutsAndWidgets().setImportantForAccessibility(accessibilityFlag);
// Invalidate the accessibility hierarchy
if (getParent() != null) {
@@ -339,15 +333,6 @@
}
@Override
- public boolean dispatchHoverEvent(MotionEvent event) {
- // Always attempt to dispatch hover events to accessibility first.
- if (mUseTouchHelper && mTouchHelper.dispatchHoverEvent(event)) {
- return true;
- }
- return super.dispatchHoverEvent(event);
- }
-
- @Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
if (mUseTouchHelper ||
(mInterceptTouchListener != null && mInterceptTouchListener.onTouch(this, ev))) {
@@ -483,6 +468,37 @@
mFolderLeaveBehind.drawLeaveBehind(canvas);
canvas.restore();
}
+
+ if (VISUALIZE_GRID) {
+ visualizeGrid(canvas);
+ }
+ }
+
+ protected void visualizeGrid(Canvas canvas) {
+ mVisualizeGridRect.set(0, 0, mCellWidth, mCellHeight);
+ mVisualizeGridPaint.setStrokeWidth(4);
+
+ for (int i = 0; i < mCountX; i++) {
+ for (int j = 0; j < mCountY; j++) {
+ canvas.save();
+
+ int transX = i * mCellWidth;
+ int transY = j * mCellHeight;
+
+ canvas.translate(getPaddingLeft() + transX, getPaddingTop() + transY);
+
+ mVisualizeGridPaint.setStyle(Paint.Style.FILL);
+ mVisualizeGridPaint.setColor(Color.argb(80, 255, 100, 100));
+
+ canvas.drawRect(mVisualizeGridRect, mVisualizeGridPaint);
+
+ mVisualizeGridPaint.setStyle(Paint.Style.STROKE);
+ mVisualizeGridPaint.setColor(Color.argb(255, 255, 100, 100));
+
+ canvas.drawRect(mVisualizeGridRect, mVisualizeGridPaint);
+ canvas.restore();
+ }
+ }
}
@Override
@@ -949,8 +965,8 @@
return false;
}
- void visualizeDropLocation(View v, DragPreviewProvider outlineProvider, int cellX, int cellY,
- int spanX, int spanY, boolean resize, DropTarget.DragObject dragObject) {
+ void visualizeDropLocation(DraggableView v, DragPreviewProvider outlineProvider, int cellX, int
+ cellY, int spanX, int spanY, boolean resize, DropTarget.DragObject dragObject) {
final int oldDragCellX = mDragCell[0];
final int oldDragCellY = mDragCell[1];
@@ -960,9 +976,6 @@
Bitmap dragOutline = outlineProvider.generatedDragOutline;
if (cellX != oldDragCellX || cellY != oldDragCellY) {
- Point dragOffset = dragObject.dragView.getDragVisualizeOffset();
- Rect dragRegion = dragObject.dragView.getDragRegion();
-
mDragCell[0] = cellX;
mDragCell[1] = cellY;
@@ -971,50 +984,27 @@
mDragOutlineCurrent = (oldIndex + 1) % mDragOutlines.length;
Rect r = mDragOutlines[mDragOutlineCurrent];
+ cellToRect(cellX, cellY, spanX, spanY, r);
+ int left = r.left;
+ int top = r.top;
+
+ int width = dragOutline.getWidth();
+ int height = dragOutline.getHeight();
+
if (resize) {
- cellToRect(cellX, cellY, spanX, spanY, r);
- if (v instanceof LauncherAppWidgetHostView) {
- DeviceProfile profile = mActivity.getWallpaperDeviceProfile();
- Utilities.shrinkRect(r, profile.appWidgetScale.x, profile.appWidgetScale.y);
- }
- } else {
- // Find the top left corner of the rect the object will occupy
- final int[] topLeft = mTmpPoint;
- cellToPoint(cellX, cellY, topLeft);
-
- int left = topLeft[0];
- int top = topLeft[1];
-
- if (v != null && dragOffset == null) {
- // When drawing the drag outline, it did not account for margin offsets
- // added by the view's parent.
- MarginLayoutParams lp = (MarginLayoutParams) v.getLayoutParams();
- left += lp.leftMargin;
- top += lp.topMargin;
-
- // Offsets due to the size difference between the View and the dragOutline.
- // There is a size difference to account for the outer blur, which may lie
- // outside the bounds of the view.
- top += ((mCellHeight * spanY) - dragOutline.getHeight()) / 2;
- // We center about the x axis
- left += ((mCellWidth * spanX) - dragOutline.getWidth()) / 2;
- } else {
- if (dragOffset != null && dragRegion != null) {
- // Center the drag region *horizontally* in the cell and apply a drag
- // outline offset
- left += dragOffset.x + ((mCellWidth * spanX) - dragRegion.width()) / 2;
- int cHeight = getShortcutsAndWidgets().getCellContentHeight();
- int cellPaddingY = (int) Math.max(0, ((mCellHeight - cHeight) / 2f));
- top += dragOffset.y + cellPaddingY;
- } else {
- // Center the drag outline in the cell
- left += ((mCellWidth * spanX) - dragOutline.getWidth()) / 2;
- top += ((mCellHeight * spanY) - dragOutline.getHeight()) / 2;
- }
- }
- r.set(left, top, left + dragOutline.getWidth(), top + dragOutline.getHeight());
+ width = r.width();
+ height = r.height();
}
+ if (v != null && v.getViewType() == DraggableView.DRAGGABLE_ICON) {
+ left += ((mCellWidth * spanX) - dragOutline.getWidth()) / 2;
+ int cHeight = getShortcutsAndWidgets().getCellContentHeight();
+ int cellPaddingY = (int) Math.max(0, ((mCellHeight - cHeight) / 2f));
+ top += cellPaddingY;
+ }
+
+ r.set(left, top, left + width, top + height);
+
Utilities.scaleRectAboutCenter(r, mChildScale);
mDragOutlineAnims[mDragOutlineCurrent].setTag(dragOutline);
mDragOutlineAnims[mDragOutlineCurrent].animateIn();
@@ -1900,7 +1890,7 @@
// This method starts or changes the reorder preview animations
private void beginOrAdjustReorderPreviewAnimations(ItemConfiguration solution,
- View dragView, int delay, int mode) {
+ View dragView, int mode) {
int childCount = mShortcutsAndWidgets.getChildCount();
for (int i = 0; i < childCount; i++) {
View child = mShortcutsAndWidgets.getChildAt(i);
@@ -1967,6 +1957,8 @@
this.child = child;
this.mode = mode;
+
+ // TODO issue!
setInitialAnimationValues(false);
finalScale = (mChildScale - (CHILD_DIVIDEND / child.getWidth())) * initScale;
finalDeltaX = initDeltaX;
@@ -2162,6 +2154,8 @@
*/
private void getDirectionVectorForDrop(int dragViewCenterX, int dragViewCenterY, int spanX,
int spanY, View dragView, int[] resultDirection) {
+
+ //TODO(adamcohen) b/151776141 use the items visual center for the direction vector
int[] targetDestination = new int[2];
findNearestArea(dragViewCenterX, dragViewCenterY, spanX, spanY, targetDestination);
@@ -2272,7 +2266,7 @@
setItemPlacementDirty(false);
} else {
beginOrAdjustReorderPreviewAnimations(swapSolution, dragView,
- REORDER_ANIMATION_DURATION, ReorderPreviewAnimation.MODE_PREVIEW);
+ ReorderPreviewAnimation.MODE_PREVIEW);
}
mShortcutsAndWidgets.requestLayout();
}
@@ -2326,7 +2320,7 @@
if (mode == MODE_SHOW_REORDER_HINT) {
if (finalSolution != null) {
- beginOrAdjustReorderPreviewAnimations(finalSolution, dragView, 0,
+ beginOrAdjustReorderPreviewAnimations(finalSolution, dragView,
ReorderPreviewAnimation.MODE_HINT);
result[0] = finalSolution.cellX;
result[1] = finalSolution.cellY;
@@ -2366,7 +2360,7 @@
setItemPlacementDirty(false);
} else {
beginOrAdjustReorderPreviewAnimations(finalSolution, dragView,
- REORDER_ANIMATION_DURATION, ReorderPreviewAnimation.MODE_PREVIEW);
+ ReorderPreviewAnimation.MODE_PREVIEW);
}
}
} else {
@@ -2787,7 +2781,6 @@
* Finds solution to accept hotseat migration to cell layout. commits solution if commitConfig
*/
public boolean makeSpaceForHotseatMigration(boolean commitConfig) {
- if (FeatureFlags.HOTSEAT_MIGRATE_NEW_PAGE.get()) return false;
int[] cellPoint = new int[2];
int[] directionVector = new int[]{0, -1};
cellToPoint(0, mCountY, cellPoint);
@@ -2797,12 +2790,23 @@
if (commitConfig) {
copySolutionToTempState(configuration, null);
commitTempPlacement();
+ // undo marking cells occupied since there is actually nothing being placed yet.
+ mOccupied.markCells(0, mCountY - 1, mCountX, 1, false);
}
return true;
}
return false;
}
+ /**
+ * returns a copy of cell layout's grid occupancy
+ */
+ public GridOccupancy cloneGridOccupancy() {
+ GridOccupancy occupancy = new GridOccupancy(mCountX, mCountY);
+ mOccupied.copyTo(occupancy);
+ return occupancy;
+ }
+
public boolean isRegionVacant(int x, int y, int spanX, int spanY) {
return mOccupied.isRegionVacant(x, y, spanX, spanY);
}
diff --git a/src/com/android/launcher3/DropTarget.java b/src/com/android/launcher3/DropTarget.java
index a32fd12..ef02e87 100644
--- a/src/com/android/launcher3/DropTarget.java
+++ b/src/com/android/launcher3/DropTarget.java
@@ -23,6 +23,7 @@
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.dragndrop.DragOptions;
import com.android.launcher3.dragndrop.DragView;
+import com.android.launcher3.dragndrop.DraggableView;
import com.android.launcher3.folder.FolderNameProvider;
/**
@@ -59,9 +60,6 @@
/** Where the drag originated */
public DragSource dragSource = null;
- /** The object is part of an accessible drag operation */
- public boolean accessibleDrag;
-
/** Indicates that the drag operation was cancelled */
public boolean cancelled = false;
@@ -72,6 +70,10 @@
public FolderNameProvider folderNameProvider;
+ /** The source view (ie. icon, widget etc.) that is being dragged and which the
+ * DragView represents. May be an actual View class or a virtual stand-in */
+ public DraggableView originalView = null;
+
public DragObject(Context context) {
if (FeatureFlags.FOLDER_NAME_SUGGEST.get()) {
folderNameProvider = FolderNameProvider.newInstance(context);
diff --git a/src/com/android/launcher3/FolderInfo.java b/src/com/android/launcher3/FolderInfo.java
index 336e423..b75a5e7 100644
--- a/src/com/android/launcher3/FolderInfo.java
+++ b/src/com/android/launcher3/FolderInfo.java
@@ -79,7 +79,7 @@
* Add an app or shortcut for a specified rank.
*/
public void add(WorkspaceItemInfo item, int rank, boolean animate) {
- rank = Utilities.boundToRange(rank, 0, contents.size() + 1);
+ rank = Utilities.boundToRange(rank, 0, contents.size());
contents.add(rank, item);
for (int i = 0; i < mListeners.size(); i++) {
mListeners.get(i).onAdd(item, rank);
diff --git a/src/com/android/launcher3/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java
index 2ad84b9..7414a88 100644
--- a/src/com/android/launcher3/InvariantDeviceProfile.java
+++ b/src/com/android/launcher3/InvariantDeviceProfile.java
@@ -162,9 +162,7 @@
"PreviewContext is passed into this IDP constructor");
}
- String gridName = Utilities.getPrefs(context).getBoolean(GRID_OPTIONS_PREFERENCE_KEY, false)
- ? Utilities.getPrefs(context).getString(KEY_IDP_GRID_NAME, null)
- : null;
+ String gridName = getCurrentGridName(context);
initGrid(context, gridName);
mConfigMonitor = new ConfigMonitor(context,
APPLY_CONFIG_AT_RUNTIME.get() ? this::onConfigChanged : this::killProcess);
@@ -188,6 +186,12 @@
initGrid(context, null, new Info(display));
}
+ public static String getCurrentGridName(Context context) {
+ return Utilities.getPrefs(context).getBoolean(GRID_OPTIONS_PREFERENCE_KEY, false)
+ ? Utilities.getPrefs(context).getString(KEY_IDP_GRID_NAME, null)
+ : null;
+ }
+
/**
* Retrieve system defined or RRO overriden icon shape.
*/
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 1413a5c..a83a694 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -124,7 +124,7 @@
import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.touch.AllAppsSwipeController;
import com.android.launcher3.touch.ItemClickHandler;
-import com.android.launcher3.uioverrides.BackgroundBlurController;
+import com.android.launcher3.uioverrides.DepthController;
import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
@@ -326,19 +326,21 @@
private boolean mDeferOverlayCallbacks;
private final Runnable mDeferredOverlayCallbacks = this::checkIfOverlayStillDeferred;
- private BackgroundBlurController mBackgroundBlurController =
- new BackgroundBlurController(this);
+ private DepthController mDepthController =
+ new DepthController(this);
private final ViewTreeObserver.OnDrawListener mOnDrawListener =
new ViewTreeObserver.OnDrawListener() {
@Override
public void onDraw() {
- getBackgroundBlurController().setSurfaceToLauncher(mDragLayer);
+ getDepthController().setSurfaceToLauncher(mDragLayer);
mDragLayer.post(() -> mDragLayer.getViewTreeObserver().removeOnDrawListener(
this));
}
};
+ private long mLastTouchUpTime = -1;
+
@Override
protected void onCreate(Bundle savedInstanceState) {
Object traceToken = TraceHelper.INSTANCE.beginSection(ON_CREATE_EVT,
@@ -954,7 +956,7 @@
NotificationListener.removeNotificationsChangedListener();
getStateManager().moveToRestState();
- getBackgroundBlurController().setSurfaceToLauncher(null);
+ getDepthController().setSurfaceToLauncher(null);
// Workaround for b/78520668, explicitly trim memory once UI is hidden
onTrimMemory(TRIM_MEMORY_UI_HIDDEN);
@@ -1115,7 +1117,7 @@
super.onPause();
mDragController.cancelDrag();
- mDragController.resetLastGestureUpTime();
+ mLastTouchUpTime = -1;
mDropTargetBar.animateToVisibility(false);
if (!mDeferOverlayCallbacks) {
@@ -1838,6 +1840,9 @@
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
+ if (ev.getAction() == MotionEvent.ACTION_UP) {
+ mLastTouchUpTime = System.currentTimeMillis();
+ }
TestLogging.recordMotionEvent(TestProtocol.SEQUENCE_MAIN, "Touch event", ev);
return super.dispatchTouchEvent(ev);
}
@@ -2465,8 +2470,12 @@
}
private boolean canRunNewAppsAnimation() {
- long diff = System.currentTimeMillis() - mDragController.getLastGestureUpTime();
- return diff > (NEW_APPS_ANIMATION_INACTIVE_TIMEOUT_SECONDS * 1000);
+ if (mDragController.isDragging()) {
+ return false;
+ } else {
+ return (System.currentTimeMillis() - mLastTouchUpTime)
+ > (NEW_APPS_ANIMATION_INACTIVE_TIMEOUT_SECONDS * 1000);
+ }
}
private ValueAnimator createNewAppBounceAnimation(View v, int i) {
@@ -2708,7 +2717,7 @@
protected StateHandler[] createStateHandlers() {
return new StateHandler[] { getAllAppsController(), getWorkspace(),
- getBackgroundBlurController() };
+ getDepthController() };
}
public TouchController[] createTouchControllers() {
@@ -2745,8 +2754,8 @@
return Stream.of(APP_INFO, WIDGETS, INSTALL);
}
- public BackgroundBlurController getBackgroundBlurController() {
- return mBackgroundBlurController;
+ public DepthController getDepthController() {
+ return mDepthController;
}
public static Launcher getLauncher(Context context) {
diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java
index 697048a..a699c32 100644
--- a/src/com/android/launcher3/LauncherProvider.java
+++ b/src/com/android/launcher3/LauncherProvider.java
@@ -17,6 +17,7 @@
package com.android.launcher3;
import static com.android.launcher3.config.FeatureFlags.MULTI_DB_GRID_MIRATION_ALGO;
+import static com.android.launcher3.provider.LauncherDbUtils.copyTable;
import static com.android.launcher3.provider.LauncherDbUtils.dropTable;
import static com.android.launcher3.provider.LauncherDbUtils.tableExists;
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
@@ -164,8 +165,11 @@
return false;
}
- mOpenHelper.close();
+ DatabaseHelper oldHelper = mOpenHelper;
mOpenHelper = new DatabaseHelper(getContext());
+ copyTable(oldHelper.getReadableDatabase(), Favorites.TABLE_NAME,
+ mOpenHelper.getWritableDatabase(), Favorites.TMP_TABLE, getContext());
+ oldHelper.close();
return true;
}
diff --git a/src/com/android/launcher3/LauncherSettings.java b/src/com/android/launcher3/LauncherSettings.java
index 216c221..f516446 100644
--- a/src/com/android/launcher3/LauncherSettings.java
+++ b/src/com/android/launcher3/LauncherSettings.java
@@ -103,6 +103,11 @@
public static final String PREVIEW_TABLE_NAME = "favorites_preview";
/**
+ * Temporary table used specifically for multi-db grid migrations
+ */
+ public static final String TMP_TABLE = "favorites_tmp";
+
+ /**
* The content:// style URL for "favorites" table
*/
public static final Uri CONTENT_URI = Uri.parse("content://"
@@ -115,6 +120,12 @@
+ LauncherProvider.AUTHORITY + "/" + PREVIEW_TABLE_NAME);
/**
+ * The content:// style URL for "favorites_tmp" table
+ */
+ public static final Uri TMP_CONTENT_URI = Uri.parse("content://"
+ + LauncherProvider.AUTHORITY + "/" + TMP_TABLE);
+
+ /**
* The content:// style URL for a given row, identified by its id.
*
* @param id The row id.
diff --git a/src/com/android/launcher3/LauncherState.java b/src/com/android/launcher3/LauncherState.java
index 62b8927..6ee82cd 100644
--- a/src/com/android/launcher3/LauncherState.java
+++ b/src/com/android/launcher3/LauncherState.java
@@ -20,11 +20,6 @@
import static android.view.View.VISIBLE;
import static android.view.accessibility.AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED;
-import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_FADE;
-import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_SCALE;
-import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_TRANSLATE_X;
-import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_FADE;
-import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_SCALE;
import static com.android.launcher3.anim.Interpolators.ACCEL;
import static com.android.launcher3.anim.Interpolators.ACCEL_2;
import static com.android.launcher3.anim.Interpolators.DEACCEL;
@@ -32,6 +27,11 @@
import static com.android.launcher3.anim.Interpolators.clampToProgress;
import static com.android.launcher3.config.FeatureFlags.ENABLE_OVERVIEW_ACTIONS;
import static com.android.launcher3.states.RotationHelper.REQUEST_NONE;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_FADE;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_SCALE;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_TRANSLATE_X;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_FADE;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_SCALE;
import static com.android.launcher3.testing.TestProtocol.ALL_APPS_STATE_ORDINAL;
import static com.android.launcher3.testing.TestProtocol.BACKGROUND_APP_STATE_ORDINAL;
import static com.android.launcher3.testing.TestProtocol.HINT_STATE_ORDINAL;
@@ -46,9 +46,9 @@
import android.view.animation.Interpolator;
import com.android.launcher3.allapps.AllAppsContainerView;
-import com.android.launcher3.anim.AnimatorSetBuilder;
import com.android.launcher3.states.HintState;
import com.android.launcher3.states.SpringLoadedState;
+import com.android.launcher3.states.StateAnimationConfig;
import com.android.launcher3.uioverrides.states.AllAppsState;
import com.android.launcher3.uioverrides.states.OverviewState;
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
@@ -271,11 +271,13 @@
}
/**
- * The amount of blur to apply to the background of either the app or Launcher surface in this
- * state.
+ * The amount of blur and wallpaper zoom to apply to the background of either the app
+ * or Launcher surface in this state. Should be a number between 0 and 1, inclusive.
+ *
+ * 0 means completely zoomed in, without blurs. 1 is zoomed out, with blurs.
*/
- public int getBackgroundBlurRadius(Context context) {
- return 0;
+ public float getDepth(Context context) {
+ return 0f;
}
public String getDescription(Launcher launcher) {
@@ -325,13 +327,13 @@
* - Setting some start values (e.g. scale) for views that are hidden but about to be shown.
*/
public void prepareForAtomicAnimation(Launcher launcher, LauncherState fromState,
- AnimatorSetBuilder builder) {
+ StateAnimationConfig config) {
if (this == NORMAL && fromState == OVERVIEW) {
- builder.setInterpolator(ANIM_WORKSPACE_SCALE, DEACCEL);
- builder.setInterpolator(ANIM_WORKSPACE_FADE, ACCEL);
- builder.setInterpolator(ANIM_OVERVIEW_SCALE, clampToProgress(ACCEL, 0, 0.9f));
- builder.setInterpolator(ANIM_OVERVIEW_TRANSLATE_X, ACCEL);
- builder.setInterpolator(ANIM_OVERVIEW_FADE, DEACCEL_1_7);
+ config.setInterpolator(ANIM_WORKSPACE_SCALE, DEACCEL);
+ config.setInterpolator(ANIM_WORKSPACE_FADE, ACCEL);
+ config.setInterpolator(ANIM_OVERVIEW_SCALE, clampToProgress(ACCEL, 0, 0.9f));
+ config.setInterpolator(ANIM_OVERVIEW_TRANSLATE_X, ACCEL);
+ config.setInterpolator(ANIM_OVERVIEW_FADE, DEACCEL_1_7);
Workspace workspace = launcher.getWorkspace();
// Start from a higher workspace scale, but only if we're invisible so we don't jump.
@@ -363,7 +365,7 @@
}
} else if (this == NORMAL && fromState == OVERVIEW_PEEK) {
// Keep fully visible until the very end (when overview is offscreen) to make invisible.
- builder.setInterpolator(ANIM_OVERVIEW_FADE, t -> t < 1 ? 0 : 1);
+ config.setInterpolator(ANIM_OVERVIEW_FADE, t -> t < 1 ? 0 : 1);
}
}
diff --git a/src/com/android/launcher3/LauncherStateManager.java b/src/com/android/launcher3/LauncherStateManager.java
index 04134f2..e071777 100644
--- a/src/com/android/launcher3/LauncherStateManager.java
+++ b/src/com/android/launcher3/LauncherStateManager.java
@@ -17,26 +17,23 @@
package com.android.launcher3;
import static com.android.launcher3.LauncherState.NORMAL;
-import static com.android.launcher3.anim.PropertySetter.NO_ANIM_PROPERTY_SETTER;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_COMPONENTS;
import android.animation.Animator;
+import android.animation.Animator.AnimatorListener;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.os.Handler;
import android.os.Looper;
-import androidx.annotation.IntDef;
-
import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.anim.AnimatorPlaybackController;
-import com.android.launcher3.anim.AnimatorSetBuilder;
-import com.android.launcher3.anim.PropertySetter;
-import com.android.launcher3.anim.PropertySetter.AnimatedPropertySetter;
+import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.compat.AccessibilityManagerCompat;
+import com.android.launcher3.states.StateAnimationConfig;
+import com.android.launcher3.states.StateAnimationConfig.AnimationFlags;
import java.io.PrintWriter;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
/**
@@ -84,28 +81,7 @@
public static final String TAG = "StateManager";
- // We separate the state animations into "atomic" and "non-atomic" components. The atomic
- // components may be run atomically - that is, all at once, instead of user-controlled. However,
- // atomic components are not restricted to this purpose; they can be user-controlled alongside
- // non atomic components as well. Note that each gesture model has exactly one atomic component,
- // PLAY_ATOMIC_OVERVIEW_SCALE *or* PLAY_ATOMIC_OVERVIEW_PEEK.
- @IntDef(flag = true, value = {
- PLAY_NON_ATOMIC,
- PLAY_ATOMIC_OVERVIEW_SCALE,
- PLAY_ATOMIC_OVERVIEW_PEEK,
- SKIP_OVERVIEW,
- })
- @Retention(RetentionPolicy.SOURCE)
- public @interface AnimationFlags {}
- public static final int PLAY_NON_ATOMIC = 1 << 0;
- public static final int PLAY_ATOMIC_OVERVIEW_SCALE = 1 << 1;
- public static final int PLAY_ATOMIC_OVERVIEW_PEEK = 1 << 2;
- public static final int SKIP_OVERVIEW = 1 << 3;
-
- public static final int ANIM_ALL_COMPONENTS = PLAY_NON_ATOMIC | PLAY_ATOMIC_OVERVIEW_SCALE
- | PLAY_ATOMIC_OVERVIEW_PEEK;
-
- private final AnimationConfig mConfig = new AnimationConfig();
+ private final AnimationState mConfig = new AnimationState();
private final Handler mUiHandler;
private final Launcher mLauncher;
private final ArrayList<StateListener> mListeners = new ArrayList<>();
@@ -140,7 +116,7 @@
writer.println(prefix + "\tmCurrentStableState:" + mCurrentStableState);
writer.println(prefix + "\tmState:" + mState);
writer.println(prefix + "\tmRestState:" + mRestState);
- writer.println(prefix + "\tisInTransition:" + (mConfig.mCurrentAnimation != null));
+ writer.println(prefix + "\tisInTransition:" + (mConfig.currentAnimation != null));
}
public StateHandler[] getStateHandlers() {
@@ -171,7 +147,7 @@
*/
public boolean isInStableState(LauncherState state) {
return mState == state && mCurrentStableState == state
- && (mConfig.mTargetState == null || mConfig.mTargetState == state);
+ && (mConfig.targetState == null || mConfig.targetState == state);
}
/**
@@ -218,12 +194,12 @@
}
public void reapplyState(boolean cancelCurrentAnimation) {
- boolean wasInAnimation = mConfig.mCurrentAnimation != null;
+ boolean wasInAnimation = mConfig.currentAnimation != null;
if (cancelCurrentAnimation) {
cancelAllStateElementAnimation();
cancelAnimation();
}
- if (mConfig.mCurrentAnimation == null) {
+ if (mConfig.currentAnimation == null) {
for (StateHandler handler : getStateHandlers()) {
handler.setState(mState);
}
@@ -237,16 +213,16 @@
final Runnable onCompleteRunnable) {
animated &= Utilities.areAnimationsEnabled(mLauncher);
if (mLauncher.isInState(state)) {
- if (mConfig.mCurrentAnimation == null) {
+ if (mConfig.currentAnimation == null) {
// Run any queued runnable
if (onCompleteRunnable != null) {
onCompleteRunnable.run();
}
return;
- } else if (!mConfig.userControlled && animated && mConfig.mTargetState == state) {
+ } else if (!mConfig.userControlled && animated && mConfig.targetState == state) {
// We are running the same animation as requested
if (onCompleteRunnable != null) {
- mConfig.mCurrentAnimation.addListener(
+ mConfig.currentAnimation.addListener(
AnimationSuccessListener.forRunnable(onCompleteRunnable));
}
return;
@@ -276,9 +252,9 @@
if (delay > 0) {
// Create the animation after the delay as some properties can change between preparing
// the animation and running the animation.
- int startChangeId = mConfig.mChangeId;
+ int startChangeId = mConfig.changeId;
mUiHandler.postDelayed(() -> {
- if (mConfig.mChangeId == startChangeId) {
+ if (mConfig.changeId == startChangeId) {
goToStateAnimated(state, fromState, onCompleteRunnable);
}
}, delay);
@@ -294,11 +270,11 @@
mConfig.duration = state == NORMAL
? fromState.getTransitionDuration(mLauncher)
: state.getTransitionDuration(mLauncher);
-
- AnimatorSetBuilder builder = new AnimatorSetBuilder();
- prepareForAtomicAnimation(fromState, state, builder);
- AnimatorSet animation = createAnimationToNewWorkspaceInternal(
- state, builder, onCompleteRunnable);
+ prepareForAtomicAnimation(fromState, state, mConfig);
+ AnimatorSet animation = createAnimationToNewWorkspaceInternal(state).getAnim();
+ if (onCompleteRunnable != null) {
+ animation.addListener(AnimationSuccessListener.forRunnable(onCompleteRunnable));
+ }
mUiHandler.post(new StartAnimRunnable(animation));
}
@@ -308,44 +284,22 @@
* - Setting some start values (e.g. scale) for views that are hidden but about to be shown.
*/
public void prepareForAtomicAnimation(LauncherState fromState, LauncherState toState,
- AnimatorSetBuilder builder) {
- toState.prepareForAtomicAnimation(mLauncher, fromState, builder);
- }
-
- public AnimatorSet createAtomicAnimation(LauncherState fromState, LauncherState toState,
- AnimatorSetBuilder builder, @AnimationFlags int animFlags, long duration) {
- prepareForAtomicAnimation(fromState, toState, builder);
- AnimationConfig config = new AnimationConfig();
- config.mAnimFlags = animFlags;
- config.duration = duration;
- for (StateHandler handler : mLauncher.getStateManager().getStateHandlers()) {
- handler.setStateWithAnimation(toState, builder, config);
- }
- return builder.build();
+ StateAnimationConfig config) {
+ toState.prepareForAtomicAnimation(mLauncher, fromState, config);
}
/**
- * Creates a {@link AnimatorPlaybackController} that can be used for a controlled
- * state transition. The UI is force-set to fromState before creating the controller.
- * @param fromState the initial state for the transition.
- * @param state the final state for the transition.
- * @param duration intended duration for normal playback. Use higher duration for better
- * accuracy.
+ * Creates an animation representing atomic transitions between the provided states
*/
- public AnimatorPlaybackController createAnimationToNewWorkspace(
- LauncherState fromState, LauncherState state, long duration) {
- // Since we are creating a state animation to a different state, temporarily prevent state
- // change as part of config reset.
- LauncherState originalRestState = mRestState;
- mRestState = state;
- mConfig.reset();
- mRestState = originalRestState;
+ public AnimatorSet createAtomicAnimation(
+ LauncherState fromState, LauncherState toState, StateAnimationConfig config) {
+ PendingAnimation builder = new PendingAnimation(config.duration);
+ prepareForAtomicAnimation(fromState, toState, config);
- for (StateHandler handler : getStateHandlers()) {
- handler.setState(fromState);
+ for (StateHandler handler : mLauncher.getStateManager().getStateHandlers()) {
+ handler.setStateWithAnimation(toState, config, builder);
}
-
- return createAnimationToNewWorkspace(state, duration);
+ return builder.getAnim();
}
/**
@@ -362,32 +316,28 @@
public AnimatorPlaybackController createAnimationToNewWorkspace(
LauncherState state, long duration, @AnimationFlags int animComponents) {
- return createAnimationToNewWorkspace(state, new AnimatorSetBuilder(), duration, null,
- animComponents);
+ StateAnimationConfig config = new StateAnimationConfig();
+ config.duration = duration;
+ config.animFlags = animComponents;
+ return createAnimationToNewWorkspace(state, config);
}
public AnimatorPlaybackController createAnimationToNewWorkspace(LauncherState state,
- AnimatorSetBuilder builder, long duration, Runnable onCancelRunnable,
- @AnimationFlags int animComponents) {
+ StateAnimationConfig config) {
+ config.userControlled = true;
mConfig.reset();
- mConfig.userControlled = true;
- mConfig.mAnimFlags = animComponents;
- mConfig.duration = duration;
- mConfig.playbackController = AnimatorPlaybackController.wrap(
- createAnimationToNewWorkspaceInternal(state, builder, null), duration)
- .setOnCancelRunnable(onCancelRunnable);
+ config.copyTo(mConfig);
+ mConfig.playbackController = createAnimationToNewWorkspaceInternal(state)
+ .createPlaybackController();
return mConfig.playbackController;
}
- protected AnimatorSet createAnimationToNewWorkspaceInternal(final LauncherState state,
- AnimatorSetBuilder builder, final Runnable onCompleteRunnable) {
-
+ private PendingAnimation createAnimationToNewWorkspaceInternal(final LauncherState state) {
+ PendingAnimation builder = new PendingAnimation(mConfig.duration);
for (StateHandler handler : getStateHandlers()) {
- handler.setStateWithAnimation(state, builder, mConfig);
+ handler.setStateWithAnimation(state, mConfig, builder);
}
-
- final AnimatorSet animation = builder.build();
- animation.addListener(new AnimationSuccessListener() {
+ builder.getAnim().addListener(new AnimationSuccessListener() {
@Override
public void onAnimationStart(Animator animation) {
@@ -397,15 +347,11 @@
@Override
public void onAnimationSuccess(Animator animator) {
- // Run any queued runnables
- if (onCompleteRunnable != null) {
- onCompleteRunnable.run();
- }
onStateTransitionEnd(state);
}
});
- mConfig.setAnimation(animation, state);
- return mConfig.mCurrentAnimation;
+ mConfig.setAnimation(builder.getAnim(), state);
+ return builder;
}
private void onStateTransitionStart(LauncherState state) {
@@ -452,7 +398,7 @@
}
public void moveToRestState() {
- if (mConfig.mCurrentAnimation != null && mConfig.userControlled) {
+ if (mConfig.currentAnimation != null && mConfig.userControlled) {
// The user is doing something. Lets not mess it up
return;
}
@@ -500,12 +446,12 @@
&& mConfig.playbackController.getTarget() == childAnim) {
clearCurrentAnimation();
break;
- } else if (mConfig.mCurrentAnimation == childAnim) {
+ } else if (mConfig.currentAnimation == childAnim) {
clearCurrentAnimation();
break;
}
}
- boolean reapplyNeeded = mConfig.mCurrentAnimation != null;
+ boolean reapplyNeeded = mConfig.currentAnimation != null;
cancelAnimation();
if (reapplyNeeded) {
reapplyState();
@@ -557,9 +503,9 @@
}
private void clearCurrentAnimation() {
- if (mConfig.mCurrentAnimation != null) {
- mConfig.mCurrentAnimation.removeListener(mConfig);
- mConfig.mCurrentAnimation = null;
+ if (mConfig.currentAnimation != null) {
+ mConfig.currentAnimation.removeListener(mConfig);
+ mConfig.currentAnimation = null;
}
mConfig.playbackController = null;
}
@@ -574,54 +520,42 @@
@Override
public void run() {
- if (mConfig.mCurrentAnimation != mAnim) {
+ if (mConfig.currentAnimation != mAnim) {
return;
}
mAnim.start();
}
}
- public static class AnimationConfig extends AnimatorListenerAdapter {
- public long duration;
- public boolean userControlled;
- public AnimatorPlaybackController playbackController;
- private @AnimationFlags int mAnimFlags = ANIM_ALL_COMPONENTS;
- private PropertySetter mPropertySetter;
+ private static class AnimationState extends StateAnimationConfig implements AnimatorListener {
- private AnimatorSet mCurrentAnimation;
- private LauncherState mTargetState;
+ private static final StateAnimationConfig DEFAULT = new StateAnimationConfig();
+
+ public AnimatorPlaybackController playbackController;
+ public AnimatorSet currentAnimation;
+ public LauncherState targetState;
+
// Id to keep track of config changes, to tie an animation with the corresponding request
- private int mChangeId = 0;
+ public int changeId = 0;
/**
* Cancels the current animation and resets config variables.
*/
public void reset() {
- duration = 0;
- userControlled = false;
- mAnimFlags = ANIM_ALL_COMPONENTS;
- mPropertySetter = null;
- mTargetState = null;
+ DEFAULT.copyTo(this);
+ targetState = null;
if (playbackController != null) {
playbackController.getAnimationPlayer().cancel();
playbackController.dispatchOnCancel();
- } else if (mCurrentAnimation != null) {
- mCurrentAnimation.setDuration(0);
- mCurrentAnimation.cancel();
+ } else if (currentAnimation != null) {
+ currentAnimation.setDuration(0);
+ currentAnimation.cancel();
}
- mCurrentAnimation = null;
+ currentAnimation = null;
playbackController = null;
- mChangeId ++;
- }
-
- public PropertySetter getPropertySetter(AnimatorSetBuilder builder) {
- if (mPropertySetter == null) {
- mPropertySetter = duration == 0 ? NO_ANIM_PROPERTY_SETTER
- : new AnimatedPropertySetter(duration, builder);
- }
- return mPropertySetter;
+ changeId++;
}
@Override
@@ -629,51 +563,25 @@
if (playbackController != null && playbackController.getTarget() == animation) {
playbackController = null;
}
- if (mCurrentAnimation == animation) {
- mCurrentAnimation = null;
+ if (currentAnimation == animation) {
+ currentAnimation = null;
}
}
public void setAnimation(AnimatorSet animation, LauncherState targetState) {
- mCurrentAnimation = animation;
- mTargetState = targetState;
- mCurrentAnimation.addListener(this);
+ currentAnimation = animation;
+ this.targetState = targetState;
+ currentAnimation.addListener(this);
}
- /**
- * @return Whether Overview is scaling as part of this animation. If this is the only
- * component (i.e. NON_ATOMIC_COMPONENT isn't included), then this scaling is happening
- * atomically, rather than being part of a normal state animation. StateHandlers can use
- * this to designate part of their animation that should scale with Overview.
- */
- public boolean playAtomicOverviewScaleComponent() {
- return hasAnimationFlag(PLAY_ATOMIC_OVERVIEW_SCALE);
- }
+ @Override
+ public void onAnimationStart(Animator animator) { }
- /**
- * @return Whether this animation will play atomically at the same time as a different,
- * user-controlled state transition. StateHandlers, which contribute to both animations, can
- * use this to avoid animating the same properties in both animations, since they'd conflict
- * with one another.
- */
- public boolean onlyPlayAtomicComponent() {
- return getAnimComponents() == PLAY_ATOMIC_OVERVIEW_SCALE
- || getAnimComponents() == PLAY_ATOMIC_OVERVIEW_PEEK;
- }
+ @Override
+ public void onAnimationCancel(Animator animator) { }
- /**
- * Returns true if the config and any of the provided component flags
- */
- public boolean hasAnimationFlag(@AnimationFlags int a) {
- return (mAnimFlags & a) != 0;
- }
-
- /**
- * @return Only the flags that determine which animation components to play.
- */
- public @AnimationFlags int getAnimComponents() {
- return mAnimFlags & ANIM_ALL_COMPONENTS;
- }
+ @Override
+ public void onAnimationRepeat(Animator animator) { }
}
public interface StateHandler {
@@ -686,8 +594,8 @@
/**
* Sets the UI to {@param state} by animating any changes.
*/
- void setStateWithAnimation(LauncherState toState,
- AnimatorSetBuilder builder, AnimationConfig config);
+ void setStateWithAnimation(
+ LauncherState toState, StateAnimationConfig config, PendingAnimation animation);
}
public interface StateListener {
diff --git a/src/com/android/launcher3/ShortcutAndWidgetContainer.java b/src/com/android/launcher3/ShortcutAndWidgetContainer.java
index 1bd8263..c07dd9d 100644
--- a/src/com/android/launcher3/ShortcutAndWidgetContainer.java
+++ b/src/com/android/launcher3/ShortcutAndWidgetContainer.java
@@ -145,38 +145,46 @@
final View child = getChildAt(i);
if (child.getVisibility() != GONE) {
CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
-
- if (child instanceof LauncherAppWidgetHostView) {
- LauncherAppWidgetHostView lahv = (LauncherAppWidgetHostView) child;
-
- // Scale and center the widget to fit within its cells.
- DeviceProfile profile = mActivity.getDeviceProfile();
- float scaleX = profile.appWidgetScale.x;
- float scaleY = profile.appWidgetScale.y;
-
- lahv.setScaleToFit(Math.min(scaleX, scaleY));
- lahv.setTranslationForCentering(-(lp.width - (lp.width * scaleX)) / 2.0f,
- -(lp.height - (lp.height * scaleY)) / 2.0f);
- }
-
- int childLeft = lp.x;
- int childTop = lp.y;
- child.layout(childLeft, childTop, childLeft + lp.width, childTop + lp.height);
-
- if (lp.dropped) {
- lp.dropped = false;
-
- final int[] cellXY = mTmpCellXY;
- getLocationOnScreen(cellXY);
- mWallpaperManager.sendWallpaperCommand(getWindowToken(),
- WallpaperManager.COMMAND_DROP,
- cellXY[0] + childLeft + lp.width / 2,
- cellXY[1] + childTop + lp.height / 2, 0, null);
- }
+ layoutChild(child);
}
}
}
+ /**
+ * Core logic to layout a child for this ViewGroup.
+ */
+ public void layoutChild(View child) {
+ CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
+ if (child instanceof LauncherAppWidgetHostView) {
+ LauncherAppWidgetHostView lahv = (LauncherAppWidgetHostView) child;
+
+ // Scale and center the widget to fit within its cells.
+ DeviceProfile profile = mActivity.getDeviceProfile();
+ float scaleX = profile.appWidgetScale.x;
+ float scaleY = profile.appWidgetScale.y;
+
+ lahv.setScaleToFit(Math.min(scaleX, scaleY));
+ lahv.setTranslationForCentering(-(lp.width - (lp.width * scaleX)) / 2.0f,
+ -(lp.height - (lp.height * scaleY)) / 2.0f);
+ }
+
+ int childLeft = lp.x;
+ int childTop = lp.y;
+ child.layout(childLeft, childTop, childLeft + lp.width, childTop + lp.height);
+
+ if (lp.dropped) {
+ lp.dropped = false;
+
+ final int[] cellXY = mTmpCellXY;
+ getLocationOnScreen(cellXY);
+ mWallpaperManager.sendWallpaperCommand(getWindowToken(),
+ WallpaperManager.COMMAND_DROP,
+ cellXY[0] + childLeft + lp.width / 2,
+ cellXY[1] + childTop + lp.height / 2, 0, null);
+ }
+ }
+
+
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
if (ev.getAction() == ACTION_DOWN && getAlpha() == 0) {
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index fc1a074..c42e480 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -60,17 +60,17 @@
import android.widget.Toast;
import com.android.launcher3.LauncherAppWidgetHost.ProviderChangedListener;
-import com.android.launcher3.LauncherStateManager.AnimationConfig;
import com.android.launcher3.accessibility.AccessibleDragListenerAdapter;
import com.android.launcher3.accessibility.WorkspaceAccessibilityHelper;
-import com.android.launcher3.anim.AnimatorSetBuilder;
import com.android.launcher3.anim.Interpolators;
+import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.dot.FolderDotInfo;
import com.android.launcher3.dragndrop.DragController;
import com.android.launcher3.dragndrop.DragLayer;
import com.android.launcher3.dragndrop.DragOptions;
import com.android.launcher3.dragndrop.DragView;
+import com.android.launcher3.dragndrop.DraggableView;
import com.android.launcher3.dragndrop.SpringLoadedDragController;
import com.android.launcher3.folder.Folder;
import com.android.launcher3.folder.FolderIcon;
@@ -82,7 +82,7 @@
import com.android.launcher3.logging.UserEventDispatcher;
import com.android.launcher3.pageindicators.WorkspacePageIndicator;
import com.android.launcher3.popup.PopupContainerWithArrow;
-import com.android.launcher3.shortcuts.ShortcutDragPreviewProvider;
+import com.android.launcher3.states.StateAnimationConfig;
import com.android.launcher3.touch.WorkspaceTouchListener;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
@@ -192,10 +192,7 @@
final WallpaperOffsetInterpolator mWallpaperOffset;
private boolean mUnlockWallpaperFromDefaultPageOnLayout;
- // Variables relating to the creation of user folders by hovering shortcuts over shortcuts
- private static final int FOLDER_CREATION_TIMEOUT = 0;
public static final int REORDER_TIMEOUT = 650;
- private final Alarm mFolderCreationAlarm = new Alarm();
private final Alarm mReorderAlarm = new Alarm();
private PreviewBackground mFolderCreateBg;
private FolderIcon mDragOverFolderIcon = null;
@@ -567,11 +564,6 @@
addView(newScreen, insertIndex);
mStateTransitionAnimation.applyChildState(
mLauncher.getStateManager().getState(), newScreen, insertIndex);
-
- if (mLauncher.getAccessibilityDelegate().isInAccessibleDrag()) {
- newScreen.enableAccessibleDrag(true, CellLayout.WORKSPACE_ACCESSIBILITY_DRAG);
- }
-
return newScreen;
}
@@ -818,11 +810,6 @@
if (indexOfChild(cl) < currentPage) {
pageShift++;
}
-
- if (isInAccessibleDrag) {
- cl.enableAccessibleDrag(false, CellLayout.WORKSPACE_ACCESSIBILITY_DRAG);
- }
-
removeView(cl);
} else {
// if this is the last screen, convert it to the empty screen
@@ -1383,10 +1370,10 @@
* Sets the current workspace {@link LauncherState}, then animates the UI
*/
@Override
- public void setStateWithAnimation(LauncherState toState,
- AnimatorSetBuilder builder, AnimationConfig config) {
+ public void setStateWithAnimation(
+ LauncherState toState, StateAnimationConfig config, PendingAnimation animation) {
StateTransitionListener listener = new StateTransitionListener(toState);
- mStateTransitionAnimation.setStateWithAnimation(toState, builder, config);
+ mStateTransitionAnimation.setStateWithAnimation(toState, config, animation);
// Invalidate the pages now, so that we have the visible pages before the
// animation is started
@@ -1399,7 +1386,7 @@
stepAnimator.addUpdateListener(listener);
stepAnimator.setDuration(config.duration);
stepAnimator.addListener(listener);
- builder.play(stepAnimator);
+ animation.add(stepAnimator);
}
public WorkspaceStateTransitionAnimation getStateTransitionAnimation() {
@@ -1444,14 +1431,14 @@
child.setVisibility(INVISIBLE);
if (options.isAccessibleDrag) {
- mDragController.addDragListener(new AccessibleDragListenerAdapter(
- this, CellLayout.WORKSPACE_ACCESSIBILITY_DRAG) {
- @Override
- protected void enableAccessibleDrag(boolean enable) {
- super.enableAccessibleDrag(enable);
- setEnableForLayout(mLauncher.getHotseat(),enable);
- }
- });
+ mDragController.addDragListener(
+ new AccessibleDragListenerAdapter(this, WorkspaceAccessibilityHelper::new) {
+ @Override
+ protected void enableAccessibleDrag(boolean enable) {
+ super.enableAccessibleDrag(enable);
+ setEnableForLayout(mLauncher.getHotseat(), enable);
+ }
+ });
}
beginDragShared(child, this, options);
@@ -1465,12 +1452,17 @@
+ "View: " + child + " tag: " + child.getTag();
throw new IllegalStateException(msg);
}
- beginDragShared(child, source, (ItemInfo) dragObject,
+ beginDragShared(child, null, source, (ItemInfo) dragObject,
new DragPreviewProvider(child), options);
}
- public DragView beginDragShared(View child, DragSource source, ItemInfo dragObject,
- DragPreviewProvider previewProvider, DragOptions dragOptions) {
+ /**
+ * Core functionality for beginning a drag operation for an item that will be dropped within
+ * the workspace
+ */
+ public DragView beginDragShared(View child, DraggableView draggableView, DragSource source,
+ ItemInfo dragObject, DragPreviewProvider previewProvider, DragOptions dragOptions) {
+
float iconScale = 1f;
if (child instanceof BubbleTextView) {
Drawable icon = ((BubbleTextView) child).getIcon();
@@ -1479,41 +1471,36 @@
}
}
+ // Clear the pressed state if necessary
child.clearFocus();
child.setPressed(false);
+ if (child instanceof BubbleTextView) {
+ BubbleTextView icon = (BubbleTextView) child;
+ icon.clearPressedBackground();
+ }
+
mOutlineProvider = previewProvider;
// The drag bitmap follows the touch point around on the screen
final Bitmap b = previewProvider.createDragBitmap();
int halfPadding = previewProvider.previewPadding / 2;
-
float scale = previewProvider.getScaleAndPosition(b, mTempXY);
int dragLayerX = mTempXY[0];
int dragLayerY = mTempXY[1];
- DeviceProfile grid = mLauncher.getDeviceProfile();
Point dragVisualizeOffset = null;
- Rect dragRect = null;
- if (child instanceof BubbleTextView) {
- dragRect = new Rect();
- BubbleTextView.getIconBounds(child, dragRect, grid.iconSizePx);
+ Rect dragRect = new Rect();
+
+ if (draggableView == null && child instanceof DraggableView) {
+ draggableView = (DraggableView) child;
+ }
+
+ if (draggableView != null) {
+ draggableView.getVisualDragBounds(dragRect);
dragLayerY += dragRect.top;
- // Note: The dragRect is used to calculate drag layer offsets, but the
- // dragVisualizeOffset in addition to the dragRect (the size) to position the outline.
- dragVisualizeOffset = new Point(- halfPadding, halfPadding);
- } else if (child instanceof FolderIcon) {
- int previewSize = grid.folderIconSizePx;
- dragVisualizeOffset = new Point(- halfPadding, halfPadding - child.getPaddingTop());
- dragRect = new Rect(0, child.getPaddingTop(), child.getWidth(), previewSize);
- } else if (previewProvider instanceof ShortcutDragPreviewProvider) {
dragVisualizeOffset = new Point(- halfPadding, halfPadding);
}
- // Clear the pressed state if necessary
- if (child instanceof BubbleTextView) {
- BubbleTextView icon = (BubbleTextView) child;
- icon.clearPressedBackground();
- }
if (child.getParent() instanceof ShortcutAndWidgetContainer) {
mDragSourceInternal = (ShortcutAndWidgetContainer) child.getParent();
@@ -1524,13 +1511,13 @@
.showForIcon((BubbleTextView) child);
if (popupContainer != null) {
dragOptions.preDragCondition = popupContainer.createPreDragCondition();
-
mLauncher.getUserEventDispatcher().resetElapsedContainerMillis("dragging started");
}
}
- DragView dv = mDragController.startDrag(b, dragLayerX, dragLayerY, source,
- dragObject, dragVisualizeOffset, dragRect, scale * iconScale, scale, dragOptions);
+ DragView dv = mDragController.startDrag(b, draggableView, dragLayerX, dragLayerY, source,
+ dragObject, dragVisualizeOffset, dragRect, scale * iconScale,
+ scale, dragOptions);
dv.setIntrinsicIconScaleFactor(dragOptions.intrinsicIconScaleFactor);
return dv;
}
@@ -1883,12 +1870,10 @@
final LauncherAppWidgetHostView hostView = (LauncherAppWidgetHostView) cell;
AppWidgetProviderInfo pInfo = hostView.getAppWidgetInfo();
if (pInfo != null && pInfo.resizeMode != AppWidgetProviderInfo.RESIZE_NONE
- && !d.accessibleDrag) {
- onCompleteRunnable = new Runnable() {
- public void run() {
- if (!isPageInTransition()) {
- AppWidgetResizeFrame.showForWidget(hostView, cellLayout);
- }
+ && !options.isAccessibleDrag) {
+ onCompleteRunnable = () -> {
+ if (!isPageInTransition()) {
+ AppWidgetResizeFrame.showForWidget(hostView, cellLayout);
}
};
}
@@ -2088,8 +2073,6 @@
if (mFolderCreateBg != null) {
mFolderCreateBg.animateToRest();
}
- mFolderCreationAlarm.setOnAlarmListener(null);
- mFolderCreationAlarm.cancelAlarm();
}
private void cleanupAddToFolder() {
@@ -2196,14 +2179,14 @@
float targetCellDistance = mDragTargetLayout.getDistanceFromCell(
mDragViewVisualCenter[0], mDragViewVisualCenter[1], mTargetCell);
- manageFolderFeedback(mDragTargetLayout, mTargetCell, targetCellDistance, d);
+ manageFolderFeedback(targetCellDistance, d);
boolean nearestDropOccupied = mDragTargetLayout.isNearestDropLocationOccupied((int)
mDragViewVisualCenter[0], (int) mDragViewVisualCenter[1], item.spanX,
item.spanY, child, mTargetCell);
if (!nearestDropOccupied) {
- mDragTargetLayout.visualizeDropLocation(child, mOutlineProvider,
+ mDragTargetLayout.visualizeDropLocation(d.originalView, mOutlineProvider,
mTargetCell[0], mTargetCell[1], item.spanX, item.spanY, false, d);
} else if ((mDragMode == DRAG_MODE_NONE || mDragMode == DRAG_MODE_REORDER)
&& !mReorderAlarm.alarmPending() && (mLastReorderX != reorderX ||
@@ -2294,10 +2277,10 @@
return null;
}
- private void manageFolderFeedback(CellLayout targetLayout,
- int[] targetCell, float distance, DragObject dragObject) {
+ private void manageFolderFeedback(float distance, DragObject dragObject) {
if (distance > mMaxDistanceForFolderCreation) {
- if (mDragMode != DRAG_MODE_NONE) {
+ if ((mDragMode == DRAG_MODE_ADD_TO_FOLDER
+ || mDragMode == DRAG_MODE_CREATE_FOLDER)) {
setDragMode(DRAG_MODE_NONE);
}
return;
@@ -2306,18 +2289,18 @@
final View dragOverView = mDragTargetLayout.getChildAt(mTargetCell[0], mTargetCell[1]);
ItemInfo info = dragObject.dragInfo;
boolean userFolderPending = willCreateUserFolder(info, dragOverView, false);
- if (mDragMode == DRAG_MODE_NONE && userFolderPending &&
- !mFolderCreationAlarm.alarmPending()) {
+ if (mDragMode == DRAG_MODE_NONE && userFolderPending) {
- FolderCreationAlarmListener listener = new
- FolderCreationAlarmListener(targetLayout, targetCell[0], targetCell[1]);
+ mFolderCreateBg = new PreviewBackground();
+ mFolderCreateBg.setup(mLauncher, mLauncher, null,
+ dragOverView.getMeasuredWidth(), dragOverView.getPaddingTop());
- if (!dragObject.accessibleDrag) {
- mFolderCreationAlarm.setOnAlarmListener(listener);
- mFolderCreationAlarm.setAlarm(FOLDER_CREATION_TIMEOUT);
- } else {
- listener.onAlarm(mFolderCreationAlarm);
- }
+ // The full preview background should appear behind the icon
+ mFolderCreateBg.isClipping = false;
+
+ mFolderCreateBg.animateToAccept(mDragTargetLayout, mTargetCell[0], mTargetCell[1]);
+ mDragTargetLayout.clearDragOutlines();
+ setDragMode(DRAG_MODE_CREATE_FOLDER);
if (dragObject.stateAnnouncer != null) {
dragObject.stateAnnouncer.announce(WorkspaceAccessibilityHelper
@@ -2330,8 +2313,8 @@
if (willAddToFolder && mDragMode == DRAG_MODE_NONE) {
mDragOverFolderIcon = ((FolderIcon) dragOverView);
mDragOverFolderIcon.onDragEnter(info);
- if (targetLayout != null) {
- targetLayout.clearDragOutlines();
+ if (mDragTargetLayout != null) {
+ mDragTargetLayout.clearDragOutlines();
}
setDragMode(DRAG_MODE_ADD_TO_FOLDER);
@@ -2350,33 +2333,6 @@
}
}
- class FolderCreationAlarmListener implements OnAlarmListener {
- final CellLayout layout;
- final int cellX;
- final int cellY;
-
- final PreviewBackground bg = new PreviewBackground();
-
- public FolderCreationAlarmListener(CellLayout layout, int cellX, int cellY) {
- this.layout = layout;
- this.cellX = cellX;
- this.cellY = cellY;
-
- BubbleTextView cell = (BubbleTextView) layout.getChildAt(cellX, cellY);
- bg.setup(mLauncher, mLauncher, null, cell.getMeasuredWidth(), cell.getPaddingTop());
-
- // The full preview background should appear behind the icon
- bg.isClipping = false;
- }
-
- public void onAlarm(Alarm alarm) {
- mFolderCreateBg = bg;
- mFolderCreateBg.animateToAccept(layout, cellX, cellY);
- layout.clearDragOutlines();
- setDragMode(DRAG_MODE_CREATE_FOLDER);
- }
- }
-
class ReorderAlarmListener implements OnAlarmListener {
final float[] dragViewCenter;
final int minSpanX, minSpanY, spanX, spanY;
@@ -2413,7 +2369,7 @@
}
boolean resize = resultSpan[0] != spanX || resultSpan[1] != spanY;
- mDragTargetLayout.visualizeDropLocation(child, mOutlineProvider,
+ mDragTargetLayout.visualizeDropLocation(dragObject.originalView, mOutlineProvider,
mTargetCell[0], mTargetCell[1], resultSpan[0], resultSpan[1], resize, dragObject);
}
}
diff --git a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
index 6653426..c521c34 100644
--- a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
+++ b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
@@ -22,27 +22,27 @@
import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X;
import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y;
import static com.android.launcher3.LauncherState.HOTSEAT_ICONS;
-import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_HOTSEAT_SCALE;
-import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_HOTSEAT_TRANSLATE;
-import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_FADE;
-import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_SCALE;
-import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_TRANSLATE;
import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.anim.Interpolators.ZOOM_OUT;
import static com.android.launcher3.anim.PropertySetter.NO_ANIM_PROPERTY_SETTER;
import static com.android.launcher3.graphics.WorkspaceAndHotseatScrim.SCRIM_PROGRESS;
import static com.android.launcher3.graphics.WorkspaceAndHotseatScrim.SYSUI_PROGRESS;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_HOTSEAT_SCALE;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_HOTSEAT_TRANSLATE;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_FADE;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_SCALE;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_TRANSLATE;
import android.view.View;
import android.view.animation.Interpolator;
import com.android.launcher3.LauncherState.PageAlphaProvider;
import com.android.launcher3.LauncherState.ScaleAndTranslation;
-import com.android.launcher3.LauncherStateManager.AnimationConfig;
import com.android.launcher3.allapps.AllAppsContainerView;
-import com.android.launcher3.anim.AnimatorSetBuilder;
+import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.anim.PropertySetter;
import com.android.launcher3.graphics.WorkspaceAndHotseatScrim;
+import com.android.launcher3.states.StateAnimationConfig;
/**
* Manages the animations between each of the workspace states.
@@ -60,13 +60,15 @@
}
public void setState(LauncherState toState) {
- setWorkspaceProperty(toState, NO_ANIM_PROPERTY_SETTER, new AnimatorSetBuilder(),
- new AnimationConfig());
+ setWorkspaceProperty(toState, NO_ANIM_PROPERTY_SETTER, new StateAnimationConfig());
}
- public void setStateWithAnimation(LauncherState toState, AnimatorSetBuilder builder,
- AnimationConfig config) {
- setWorkspaceProperty(toState, config.getPropertySetter(builder), builder, config);
+ /**
+ * @see com.android.launcher3.LauncherStateManager.StateHandler#setStateWithAnimation
+ */
+ public void setStateWithAnimation(
+ LauncherState toState, StateAnimationConfig config, PendingAnimation animation) {
+ setWorkspaceProperty(toState, animation, config);
}
public float getFinalScale() {
@@ -77,7 +79,7 @@
* Starts a transition animation for the workspace.
*/
private void setWorkspaceProperty(LauncherState state, PropertySetter propertySetter,
- AnimatorSetBuilder builder, AnimationConfig config) {
+ StateAnimationConfig config) {
ScaleAndTranslation scaleAndTranslation = state.getWorkspaceScaleAndTranslation(mLauncher);
ScaleAndTranslation hotseatScaleAndTranslation = state.getHotseatScaleAndTranslation(
mLauncher);
@@ -87,11 +89,11 @@
final int childCount = mWorkspace.getChildCount();
for (int i = 0; i < childCount; i++) {
applyChildState(state, (CellLayout) mWorkspace.getChildAt(i), i, pageAlphaProvider,
- propertySetter, builder, config);
+ propertySetter, config);
}
int elements = state.getVisibleElements(mLauncher);
- Interpolator fadeInterpolator = builder.getInterpolator(ANIM_WORKSPACE_FADE,
+ Interpolator fadeInterpolator = config.getInterpolator(ANIM_WORKSPACE_FADE,
pageAlphaProvider.interpolator);
boolean playAtomicComponent = config.playAtomicOverviewScaleComponent();
Hotseat hotseat = mWorkspace.getHotseat();
@@ -99,7 +101,7 @@
AllAppsContainerView qsbScaleView = mLauncher.getAppsView();
View qsbView = qsbScaleView.getSearchView();
if (playAtomicComponent) {
- Interpolator scaleInterpolator = builder.getInterpolator(ANIM_WORKSPACE_SCALE, ZOOM_OUT);
+ Interpolator scaleInterpolator = config.getInterpolator(ANIM_WORKSPACE_SCALE, ZOOM_OUT);
propertySetter.setFloat(mWorkspace, SCALE_PROPERTY, mNewScale, scaleInterpolator);
if (!hotseat.getRotationMode().isTransposed) {
@@ -107,7 +109,7 @@
setPivotToScaleWithWorkspace(qsbScaleView);
}
float hotseatScale = hotseatScaleAndTranslation.scale;
- Interpolator hotseatScaleInterpolator = builder.getInterpolator(ANIM_HOTSEAT_SCALE,
+ Interpolator hotseatScaleInterpolator = config.getInterpolator(ANIM_HOTSEAT_SCALE,
scaleInterpolator);
propertySetter.setFloat(hotseat, SCALE_PROPERTY, hotseatScale,
hotseatScaleInterpolator);
@@ -127,13 +129,13 @@
Interpolator translationInterpolator = !playAtomicComponent
? LINEAR
- : builder.getInterpolator(ANIM_WORKSPACE_TRANSLATE, ZOOM_OUT);
+ : config.getInterpolator(ANIM_WORKSPACE_TRANSLATE, ZOOM_OUT);
propertySetter.setFloat(mWorkspace, VIEW_TRANSLATE_X,
scaleAndTranslation.translationX, translationInterpolator);
propertySetter.setFloat(mWorkspace, VIEW_TRANSLATE_Y,
scaleAndTranslation.translationY, translationInterpolator);
- Interpolator hotseatTranslationInterpolator = builder.getInterpolator(
+ Interpolator hotseatTranslationInterpolator = config.getInterpolator(
ANIM_HOTSEAT_TRANSLATE, translationInterpolator);
propertySetter.setFloat(hotseat, VIEW_TRANSLATE_Y,
hotseatScaleAndTranslation.translationY, hotseatTranslationInterpolator);
@@ -166,12 +168,12 @@
public void applyChildState(LauncherState state, CellLayout cl, int childIndex) {
applyChildState(state, cl, childIndex, state.getWorkspacePageAlphaProvider(mLauncher),
- NO_ANIM_PROPERTY_SETTER, new AnimatorSetBuilder(), new AnimationConfig());
+ NO_ANIM_PROPERTY_SETTER, new StateAnimationConfig());
}
private void applyChildState(LauncherState state, CellLayout cl, int childIndex,
PageAlphaProvider pageAlphaProvider, PropertySetter propertySetter,
- AnimatorSetBuilder builder, AnimationConfig config) {
+ StateAnimationConfig config) {
float pageAlpha = pageAlphaProvider.getPageAlpha(childIndex);
int drawableAlpha = Math.round(pageAlpha * (state.hasWorkspacePageBackground ? 255 : 0));
@@ -181,7 +183,7 @@
DRAWABLE_ALPHA, drawableAlpha, ZOOM_OUT);
}
if (config.playAtomicOverviewScaleComponent()) {
- Interpolator fadeInterpolator = builder.getInterpolator(ANIM_WORKSPACE_FADE,
+ Interpolator fadeInterpolator = config.getInterpolator(ANIM_WORKSPACE_FADE,
pageAlphaProvider.interpolator);
propertySetter.setFloat(cl.getShortcutsAndWidgets(), VIEW_ALPHA,
pageAlpha, fadeInterpolator);
diff --git a/src/com/android/launcher3/accessibility/AccessibleDragListenerAdapter.java b/src/com/android/launcher3/accessibility/AccessibleDragListenerAdapter.java
index f8df5d7..0d7df2b 100644
--- a/src/com/android/launcher3/accessibility/AccessibleDragListenerAdapter.java
+++ b/src/com/android/launcher3/accessibility/AccessibleDragListenerAdapter.java
@@ -16,7 +16,9 @@
package com.android.launcher3.accessibility;
+import android.view.View;
import android.view.ViewGroup;
+import android.view.ViewGroup.OnHierarchyChangeListener;
import com.android.launcher3.CellLayout;
import com.android.launcher3.DropTarget.DragObject;
@@ -24,36 +26,55 @@
import com.android.launcher3.dragndrop.DragController.DragListener;
import com.android.launcher3.dragndrop.DragOptions;
+import java.util.function.Function;
+
/**
* Utility listener to enable/disable accessibility drag flags for a ViewGroup
* containing CellLayouts
*/
-public class AccessibleDragListenerAdapter implements DragListener {
+public class AccessibleDragListenerAdapter implements DragListener, OnHierarchyChangeListener {
private final ViewGroup mViewGroup;
- private final int mDragType;
+ private final Function<CellLayout, DragAndDropAccessibilityDelegate> mDelegateFactory;
/**
- * @param parent
- * @param dragType either {@link CellLayout#WORKSPACE_ACCESSIBILITY_DRAG} or
- * {@link CellLayout#FOLDER_ACCESSIBILITY_DRAG}
+ * @param parent the viewgroup containing all the children
+ * @param delegateFactory function to create no delegates
*/
- public AccessibleDragListenerAdapter(ViewGroup parent, int dragType) {
+ public AccessibleDragListenerAdapter(ViewGroup parent,
+ Function<CellLayout, DragAndDropAccessibilityDelegate> delegateFactory) {
mViewGroup = parent;
- mDragType = dragType;
+ mDelegateFactory = delegateFactory;
}
@Override
public void onDragStart(DragObject dragObject, DragOptions options) {
+ mViewGroup.setOnHierarchyChangeListener(this);
enableAccessibleDrag(true);
}
@Override
public void onDragEnd() {
+ mViewGroup.setOnHierarchyChangeListener(null);
enableAccessibleDrag(false);
Launcher.getLauncher(mViewGroup.getContext()).getDragController().removeDragListener(this);
}
+
+ @Override
+ public void onChildViewAdded(View parent, View child) {
+ if (parent == mViewGroup) {
+ setEnableForLayout((CellLayout) child, true);
+ }
+ }
+
+ @Override
+ public void onChildViewRemoved(View parent, View child) {
+ if (parent == mViewGroup) {
+ setEnableForLayout((CellLayout) child, false);
+ }
+ }
+
protected void enableAccessibleDrag(boolean enable) {
for (int i = 0; i < mViewGroup.getChildCount(); i++) {
setEnableForLayout((CellLayout) mViewGroup.getChildAt(i), enable);
@@ -61,6 +82,6 @@
}
protected final void setEnableForLayout(CellLayout layout, boolean enable) {
- layout.enableAccessibleDrag(enable, mDragType);
+ layout.setDragAndDropAccessibilityDelegate(enable ? mDelegateFactory.apply(layout) : null);
}
}
diff --git a/src/com/android/launcher3/accessibility/DragAndDropAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/DragAndDropAccessibilityDelegate.java
index 117296d..ddb547f 100644
--- a/src/com/android/launcher3/accessibility/DragAndDropAccessibilityDelegate.java
+++ b/src/com/android/launcher3/accessibility/DragAndDropAccessibilityDelegate.java
@@ -19,24 +19,26 @@
import android.content.Context;
import android.graphics.Rect;
import android.os.Bundle;
+import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
+import android.view.View.OnHoverListener;
import android.view.accessibility.AccessibilityEvent;
+import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
+import androidx.customview.widget.ExploreByTouchHelper;
+
import com.android.launcher3.CellLayout;
import com.android.launcher3.Launcher;
import com.android.launcher3.R;
import java.util.List;
-import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
-import androidx.customview.widget.ExploreByTouchHelper;
-
/**
* Helper class to make drag-and-drop in a {@link CellLayout} accessible.
*/
public abstract class DragAndDropAccessibilityDelegate extends ExploreByTouchHelper
- implements OnClickListener {
+ implements OnClickListener, OnHoverListener {
protected static final int INVALID_POSITION = -1;
private static final int[] sTempArray = new int[2];
@@ -123,6 +125,11 @@
node.setFocusable(true);
}
+ @Override
+ public boolean onHover(View view, MotionEvent motionEvent) {
+ return dispatchHoverEvent(motionEvent);
+ }
+
protected abstract String getLocationDescriptionForIconDrop(int id);
protected abstract String getConfirmationForIconDrop(int id);
diff --git a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
index 0b439ec..6f7f8e6 100644
--- a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
+++ b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
@@ -7,6 +7,7 @@
import android.app.AlertDialog;
import android.appwidget.AppWidgetProviderInfo;
import android.content.DialogInterface;
+import android.graphics.Point;
import android.graphics.Rect;
import android.os.Bundle;
import android.os.Handler;
@@ -400,11 +401,11 @@
Rect pos = new Rect();
mLauncher.getDragLayer().getDescendantRectRelativeToSelf(item, pos);
- mLauncher.getDragController().prepareAccessibleDrag(pos.centerX(), pos.centerY());
mLauncher.getDragController().addDragListener(this);
DragOptions options = new DragOptions();
options.isAccessibleDrag = true;
+ options.simulatedDndStartPoint = new Point(pos.centerX(), pos.centerY());
ItemLongClickListener.beginDrag(item, mLauncher, info, options);
}
diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java
index e085ff0..dd0212a 100644
--- a/src/com/android/launcher3/allapps/AllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java
@@ -38,6 +38,7 @@
import androidx.annotation.Nullable;
import androidx.annotation.StringRes;
import androidx.dynamicanimation.animation.DynamicAnimation;
+import androidx.recyclerview.widget.DefaultItemAnimator;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
@@ -62,7 +63,6 @@
import com.android.launcher3.util.Themes;
import com.android.launcher3.views.RecyclerViewFastScroller;
import com.android.launcher3.views.SpringRelativeLayout;
-import com.android.launcher3.views.WorkFooterContainer;
import java.util.ArrayList;
@@ -91,7 +91,7 @@
private AllAppsPagedView mViewPager;
private FloatingHeaderView mHeader;
- private WorkFooterContainer mWorkFooterContainer;
+ private WorkModeSwitch mWorkModeSwitch;
private SpannableStringBuilder mSearchQueryBuilder = null;
@@ -156,8 +156,8 @@
return mMultiValueAlpha.getProperty(index);
}
- public WorkFooterContainer getWorkFooterContainer() {
- return mWorkFooterContainer;
+ public WorkModeSwitch getWorkModeSwitch() {
+ return mWorkModeSwitch;
}
@@ -195,7 +195,7 @@
}
private void resetWorkProfile() {
- mWorkFooterContainer.refresh();
+ mWorkModeSwitch.refresh();
mAH[AdapterHolder.WORK].setupOverlay();
mAH[AdapterHolder.WORK].applyPadding();
}
@@ -410,9 +410,9 @@
} else {
mAH[AdapterHolder.MAIN].setup(findViewById(R.id.apps_list_view), null);
mAH[AdapterHolder.WORK].recyclerView = null;
- if (mWorkFooterContainer != null) {
- ((ViewGroup) mWorkFooterContainer.getParent()).removeView(mWorkFooterContainer);
- mWorkFooterContainer = null;
+ if (mWorkModeSwitch != null) {
+ ((ViewGroup) mWorkModeSwitch.getParent()).removeView(mWorkModeSwitch);
+ mWorkModeSwitch = null;
}
}
setupHeader();
@@ -422,14 +422,11 @@
}
private void setupWorkToggle() {
- mWorkFooterContainer = (WorkFooterContainer) mLauncher.getLayoutInflater().inflate(
- R.layout.work_tab_footer, findViewById(R.id.work_toggle_container));
- mWorkFooterContainer.setLayoutParams(
- new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
- ViewGroup.LayoutParams.WRAP_CONTENT));
- this.addView(mWorkFooterContainer);
- mWorkFooterContainer.setInsets(mInsets);
- mWorkFooterContainer.post(() -> mAH[AdapterHolder.WORK].applyPadding());
+ mWorkModeSwitch = (WorkModeSwitch) mLauncher.getLayoutInflater().inflate(
+ R.layout.work_mode_switch, this, false);
+ this.addView(mWorkModeSwitch);
+ mWorkModeSwitch.setInsets(mInsets);
+ mWorkModeSwitch.post(() -> mAH[AdapterHolder.WORK].applyPadding());
}
private void replaceRVContainer(boolean showTabs) {
@@ -469,8 +466,8 @@
findViewById(R.id.tab_work)
.setOnClickListener((View view) -> mViewPager.snapToPage(AdapterHolder.WORK));
}
- if (mWorkFooterContainer != null) {
- mWorkFooterContainer.setWorkTabVisible(pos == AdapterHolder.WORK);
+ if (mWorkModeSwitch != null) {
+ mWorkModeSwitch.setWorkTabVisible(pos == AdapterHolder.WORK);
}
}
@@ -614,6 +611,7 @@
final Rect padding = new Rect();
AllAppsRecyclerView recyclerView;
boolean verticalFadingEdge;
+ private View mOverlay;
boolean mWorkDisabled;
@@ -648,13 +646,22 @@
if (!mIsWork || recyclerView == null) return;
boolean workDisabled = UserCache.INSTANCE.get(mLauncher).isAnyProfileQuietModeEnabled();
if (mWorkDisabled == workDisabled) return;
+ recyclerView.setContentDescription(
+ workDisabled ? mLauncher.getString(R.string.work_apps_paused_title) : null);
+ View overlayView = getOverlayView();
+ recyclerView.setItemAnimator(new DefaultItemAnimator());
if (workDisabled) {
+ overlayView.setAlpha(0);
appsList.updateItemFilter((info, cn) -> false);
- recyclerView.addAutoSizedOverlay(
- mLauncher.getLayoutInflater().inflate(R.layout.work_apps_paused, null));
+ recyclerView.addAutoSizedOverlay(overlayView);
+ overlayView.animate().alpha(1).withEndAction(
+ () -> recyclerView.setItemAnimator(null)).start();
} else if (mInfoMatcher != null) {
appsList.updateItemFilter(mInfoMatcher);
- recyclerView.clearAutoSizedOverlays();
+ overlayView.animate().alpha(0).withEndAction(() -> {
+ recyclerView.setItemAnimator(null);
+ recyclerView.clearAutoSizedOverlays();
+ }).start();
}
mWorkDisabled = workDisabled;
}
@@ -662,8 +669,7 @@
void applyPadding() {
if (recyclerView != null) {
int bottomOffset =
- mWorkFooterContainer != null && mIsWork ? mWorkFooterContainer.getHeight()
- : 0;
+ mWorkModeSwitch != null && mIsWork ? mWorkModeSwitch.getHeight() : 0;
recyclerView.setPadding(padding.left, padding.top, padding.right,
padding.bottom + bottomOffset);
}
@@ -674,5 +680,12 @@
mAH[AdapterHolder.MAIN].recyclerView.setVerticalFadingEdgeEnabled(!mUsingTabs
&& verticalFadingEdge);
}
+
+ private View getOverlayView() {
+ if (mOverlay == null) {
+ mOverlay = mLauncher.getLayoutInflater().inflate(R.layout.work_apps_paused, null);
+ }
+ return mOverlay;
+ }
}
}
diff --git a/src/com/android/launcher3/allapps/AllAppsStore.java b/src/com/android/launcher3/allapps/AllAppsStore.java
index c4b2f68..a6ef10a 100644
--- a/src/com/android/launcher3/allapps/AllAppsStore.java
+++ b/src/com/android/launcher3/allapps/AllAppsStore.java
@@ -18,6 +18,7 @@
import static com.android.launcher3.AppInfo.COMPONENT_KEY_COMPARATOR;
import static com.android.launcher3.AppInfo.EMPTY_ARRAY;
+import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
@@ -55,6 +56,8 @@
private int mDeferUpdatesFlags = 0;
private boolean mUpdatePending = false;
+ private boolean mListenerUpdateInProgress = false;
+
public AppInfo[] getApps() {
return mApps;
}
@@ -99,10 +102,12 @@
mUpdatePending = true;
return;
}
+ mListenerUpdateInProgress = true;
int count = mUpdateListeners.size();
for (int i = 0; i < count; i++) {
mUpdateListeners.get(i).onAppsUpdated();
}
+ mListenerUpdateInProgress = false;
}
public void addUpdateListener(OnUpdateListener listener) {
@@ -110,6 +115,9 @@
}
public void removeUpdateListener(OnUpdateListener listener) {
+ if (mListenerUpdateInProgress) {
+ Log.e("AllAppsStore", "Trying to remove listener during update", new Exception());
+ }
mUpdateListeners.remove(listener);
}
diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
index 744f4eb..7600f52 100644
--- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java
+++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
@@ -5,13 +5,13 @@
import static com.android.launcher3.LauncherState.APPS_VIEW_ITEM_MASK;
import static com.android.launcher3.LauncherState.OVERVIEW;
import static com.android.launcher3.LauncherState.VERTICAL_SWIPE_INDICATOR;
-import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_ALL_APPS_FADE;
-import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_ALL_APPS_HEADER_FADE;
-import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_SCALE;
-import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_VERTICAL_PROGRESS;
import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.anim.PropertySetter.NO_ANIM_PROPERTY_SETTER;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_APPS_FADE;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_APPS_HEADER_FADE;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_SCALE;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_VERTICAL_PROGRESS;
import static com.android.launcher3.util.SystemUiController.UI_STATE_ALL_APPS;
import android.animation.Animator;
@@ -24,12 +24,12 @@
import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
-import com.android.launcher3.LauncherStateManager.AnimationConfig;
import com.android.launcher3.LauncherStateManager.StateHandler;
import com.android.launcher3.R;
import com.android.launcher3.anim.AnimationSuccessListener;
-import com.android.launcher3.anim.AnimatorSetBuilder;
+import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.anim.PropertySetter;
+import com.android.launcher3.states.StateAnimationConfig;
import com.android.launcher3.util.Themes;
import com.android.launcher3.views.ScrimView;
@@ -112,7 +112,7 @@
* @param progress value between 0 and 1, 0 shows all apps and 1 shows workspace
*
* @see #setState(LauncherState)
- * @see #setStateWithAnimation(LauncherState, AnimatorSetBuilder, AnimationConfig)
+ * @see #setStateWithAnimation(LauncherState, StateAnimationConfig, PendingAnimation)
*/
public void setProgress(float progress) {
mProgress = progress;
@@ -143,7 +143,7 @@
@Override
public void setState(LauncherState state) {
setProgress(state.getVerticalProgress(mLauncher));
- setAlphas(state, null, new AnimatorSetBuilder());
+ setAlphas(state, new StateAnimationConfig(), NO_ANIM_PROPERTY_SETTER);
onProgressAnimationEnd();
}
@@ -153,7 +153,7 @@
*/
@Override
public void setStateWithAnimation(LauncherState toState,
- AnimatorSetBuilder builder, AnimationConfig config) {
+ StateAnimationConfig config, PendingAnimation builder) {
float targetProgress = toState.getVerticalProgress(mLauncher);
if (Float.compare(mProgress, targetProgress) == 0) {
setAlphas(toState, config, builder);
@@ -168,14 +168,14 @@
}
Interpolator interpolator = config.userControlled ? LINEAR : toState == OVERVIEW
- ? builder.getInterpolator(ANIM_OVERVIEW_SCALE, FAST_OUT_SLOW_IN)
+ ? config.getInterpolator(ANIM_OVERVIEW_SCALE, FAST_OUT_SLOW_IN)
: FAST_OUT_SLOW_IN;
+
Animator anim = createSpringAnimation(mProgress, targetProgress);
anim.setDuration(config.duration);
- anim.setInterpolator(builder.getInterpolator(ANIM_VERTICAL_PROGRESS, interpolator));
+ anim.setInterpolator(config.getInterpolator(ANIM_VERTICAL_PROGRESS, interpolator));
anim.addListener(getProgressAnimatorListener());
-
- builder.play(anim);
+ builder.add(anim);
setAlphas(toState, config, builder);
}
@@ -184,21 +184,18 @@
return ObjectAnimator.ofFloat(this, ALL_APPS_PROGRESS, progressValues);
}
- private void setAlphas(LauncherState toState, AnimationConfig config,
- AnimatorSetBuilder builder) {
- setAlphas(toState.getVisibleElements(mLauncher), config, builder);
- }
-
- public void setAlphas(int visibleElements, AnimationConfig config, AnimatorSetBuilder builder) {
- PropertySetter setter = config == null ? NO_ANIM_PROPERTY_SETTER
- : config.getPropertySetter(builder);
+ /**
+ * Updates the property for the provided state
+ */
+ public void setAlphas(LauncherState state, StateAnimationConfig config, PropertySetter setter) {
+ int visibleElements = state.getVisibleElements(mLauncher);
boolean hasHeaderExtra = (visibleElements & ALL_APPS_HEADER_EXTRA) != 0;
boolean hasAllAppsContent = (visibleElements & ALL_APPS_CONTENT) != 0;
boolean hasAnyVisibleItem = (visibleElements & APPS_VIEW_ITEM_MASK) != 0;
- Interpolator allAppsFade = builder.getInterpolator(ANIM_ALL_APPS_FADE, LINEAR);
- Interpolator headerFade = builder.getInterpolator(ANIM_ALL_APPS_HEADER_FADE, allAppsFade);
+ Interpolator allAppsFade = config.getInterpolator(ANIM_ALL_APPS_FADE, LINEAR);
+ Interpolator headerFade = config.getInterpolator(ANIM_ALL_APPS_HEADER_FADE, allAppsFade);
setter.setViewAlpha(mAppsView.getContentView(), hasAllAppsContent ? 1 : 0, allAppsFade);
setter.setViewAlpha(mAppsView.getScrollBar(), hasAllAppsContent ? 1 : 0, allAppsFade);
mAppsView.getFloatingHeaderView().setContentVisibility(hasHeaderExtra, hasAllAppsContent,
diff --git a/src/com/android/launcher3/allapps/WorkModeSwitch.java b/src/com/android/launcher3/allapps/WorkModeSwitch.java
index aadb297..f935e4d 100644
--- a/src/com/android/launcher3/allapps/WorkModeSwitch.java
+++ b/src/com/android/launcher3/allapps/WorkModeSwitch.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,7 +15,13 @@
*/
package com.android.launcher3.allapps;
+import static com.android.launcher3.util.PackageManagerHelper.hasShortcutsPermission;
+
+import android.animation.ObjectAnimator;
+import android.animation.PropertyValuesHolder;
import android.content.Context;
+import android.content.pm.PackageManager;
+import android.graphics.Rect;
import android.os.AsyncTask;
import android.os.Process;
import android.os.UserHandle;
@@ -24,28 +30,45 @@
import android.view.MotionEvent;
import android.widget.Switch;
+import com.android.launcher3.Insettable;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.pm.UserCache;
import java.lang.ref.WeakReference;
-public class WorkModeSwitch extends Switch {
+/**
+ * Work profile toggle switch shown at the bottom of AllApps work tab
+ */
+public class WorkModeSwitch extends Switch implements Insettable {
+
+ private Rect mInsets = new Rect();
+ protected ObjectAnimator mOpenCloseAnimator;
+
public WorkModeSwitch(Context context) {
super(context);
+ init();
}
public WorkModeSwitch(Context context, AttributeSet attrs) {
super(context, attrs);
+ init();
}
public WorkModeSwitch(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
+ init();
+ }
+
+ private void init() {
+ mOpenCloseAnimator = ObjectAnimator.ofPropertyValuesHolder(this);
}
@Override
public void setChecked(boolean checked) {
- // No-op, do not change the checked state until broadcast is received.
+
}
@Override
@@ -55,15 +78,24 @@
private void setCheckedInternal(boolean checked) {
super.setChecked(checked);
+ setCompoundDrawablesWithIntrinsicBounds(
+ checked ? R.drawable.ic_corp : R.drawable.ic_corp_off, 0, 0, 0);
}
public void refresh() {
+ if (!shouldShowWorkSwitch()) return;
UserCache userManager = UserCache.INSTANCE.get(getContext());
setCheckedInternal(!userManager.isAnyProfileQuietModeEnabled());
setEnabled(true);
}
@Override
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ super.onLayout(changed, l, t, r, b);
+ this.setVisibility(shouldShowWorkSwitch() ? VISIBLE : GONE);
+ }
+
+ @Override
public boolean onTouchEvent(MotionEvent ev) {
return ev.getActionMasked() == MotionEvent.ACTION_MOVE || super.onTouchEvent(ev);
}
@@ -72,6 +104,24 @@
new SetQuietModeEnabledAsyncTask(enabled, new WeakReference<>(this)).execute();
}
+ @Override
+ public void setInsets(Rect insets) {
+ int bottomInset = insets.bottom - mInsets.bottom;
+ mInsets.set(insets);
+ setPadding(getPaddingLeft(), getPaddingTop(), getPaddingRight(),
+ getPaddingBottom() + bottomInset);
+ }
+
+ /**
+ * Animates in/out work profile toggle panel based on the tab user is on
+ */
+ public void setWorkTabVisible(boolean workTabVisible) {
+ if (!shouldShowWorkSwitch()) return;
+
+ mOpenCloseAnimator.setValues(PropertyValuesHolder.ofFloat(ALPHA, workTabVisible ? 1 : 0));
+ mOpenCloseAnimator.start();
+ }
+
private static final class SetQuietModeEnabledAsyncTask
extends AsyncTask<Void, Void, Boolean> {
@@ -122,4 +172,11 @@
}
}
}
+
+ private boolean shouldShowWorkSwitch() {
+ Launcher launcher = Launcher.getLauncher(getContext());
+ return Utilities.ATLEAST_P && (hasShortcutsPermission(launcher)
+ || launcher.checkSelfPermission("android.permission.MODIFY_QUIET_MODE")
+ == PackageManager.PERMISSION_GRANTED);
+ }
}
diff --git a/src/com/android/launcher3/anim/AnimatorPlaybackController.java b/src/com/android/launcher3/anim/AnimatorPlaybackController.java
index 1fc21fd..f12789a 100644
--- a/src/com/android/launcher3/anim/AnimatorPlaybackController.java
+++ b/src/com/android/launcher3/anim/AnimatorPlaybackController.java
@@ -64,13 +64,6 @@
return new AnimatorPlaybackController(anim, duration, childAnims);
}
- public static AnimatorPlaybackController wrap(PendingAnimation anim, long duration) {
- /**
- * TODO: use {@link AnimatorSet#setCurrentPlayTime(long)} once b/68382377 is fixed.
- */
- return new AnimatorPlaybackController(anim.anim, duration, anim.animHolders);
- }
-
private static final FloatProperty<ValueAnimator> CURRENT_PLAY_TIME =
new FloatProperty<ValueAnimator>("current-play-time") {
@Override
@@ -84,6 +77,9 @@
}
};
+ // Progress factor after which an animation is considered almost completed.
+ private static final float ANIMATION_COMPLETE_THRESHOLD = 0.95f;
+
private final ValueAnimator mAnimationPlayer;
private final long mDuration;
@@ -96,8 +92,8 @@
protected boolean mTargetCancelled = false;
protected Runnable mOnCancelRunnable;
- private AnimatorPlaybackController(
- AnimatorSet anim, long duration, ArrayList<Holder> childAnims) {
+ /** package private */
+ AnimatorPlaybackController(AnimatorSet anim, long duration, ArrayList<Holder> childAnims) {
mAnim = anim;
mDuration = duration;
@@ -217,6 +213,16 @@
}
/**
+ * Tries to finish the running animation if it is close to completion.
+ */
+ public void forceFinishIfCloseToEnd() {
+ if (mAnimationPlayer.isRunning()
+ && mAnimationPlayer.getAnimatedFraction() > ANIMATION_COMPLETE_THRESHOLD) {
+ mAnimationPlayer.end();
+ }
+ }
+
+ /**
* Pauses the currently playing animation.
*/
public void pause() {
diff --git a/src/com/android/launcher3/anim/AnimatorSetBuilder.java b/src/com/android/launcher3/anim/AnimatorSetBuilder.java
deleted file mode 100644
index d814b19..0000000
--- a/src/com/android/launcher3/anim/AnimatorSetBuilder.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.launcher3.anim;
-
-import android.animation.Animator;
-import android.animation.AnimatorSet;
-import android.util.SparseArray;
-import android.view.animation.Interpolator;
-
-import java.util.ArrayList;
-
-/**
- * Utility class for building animator set
- */
-public class AnimatorSetBuilder {
-
- public static final int ANIM_VERTICAL_PROGRESS = 0;
- public static final int ANIM_WORKSPACE_SCALE = 1;
- public static final int ANIM_WORKSPACE_TRANSLATE = 2;
- public static final int ANIM_WORKSPACE_FADE = 3;
- public static final int ANIM_HOTSEAT_SCALE = 4;
- public static final int ANIM_HOTSEAT_TRANSLATE = 5;
- public static final int ANIM_OVERVIEW_SCALE = 6;
- public static final int ANIM_OVERVIEW_TRANSLATE_X = 7;
- public static final int ANIM_OVERVIEW_TRANSLATE_Y = 8;
- public static final int ANIM_OVERVIEW_FADE = 9;
- public static final int ANIM_ALL_APPS_FADE = 10;
- public static final int ANIM_OVERVIEW_SCRIM_FADE = 11;
- public static final int ANIM_ALL_APPS_HEADER_FADE = 12; // e.g. predictions
-
- protected final ArrayList<Animator> mAnims = new ArrayList<>();
-
- private final SparseArray<Interpolator> mInterpolators = new SparseArray<>();
-
- public void play(Animator anim) {
- mAnims.add(anim);
- }
-
- public AnimatorSet build() {
- AnimatorSet anim = new AnimatorSet();
- anim.playTogether(mAnims);
- return anim;
- }
-
- public Interpolator getInterpolator(int animId, Interpolator fallback) {
- return mInterpolators.get(animId, fallback);
- }
-
- public void setInterpolator(int animId, Interpolator interpolator) {
- mInterpolators.put(animId, interpolator);
- }
-}
diff --git a/src/com/android/launcher3/anim/PendingAnimation.java b/src/com/android/launcher3/anim/PendingAnimation.java
index 562d160..a95a5e1 100644
--- a/src/com/android/launcher3/anim/PendingAnimation.java
+++ b/src/com/android/launcher3/anim/PendingAnimation.java
@@ -19,9 +19,12 @@
import android.animation.Animator;
import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
import android.animation.TimeInterpolator;
-import android.annotation.TargetApi;
-import android.os.Build;
+import android.animation.ValueAnimator;
+import android.util.FloatProperty;
+import android.util.IntProperty;
+import android.view.View;
import com.android.launcher3.anim.AnimatorPlaybackController.Holder;
@@ -37,22 +40,28 @@
*
* TODO: Find a better name
*/
-@TargetApi(Build.VERSION_CODES.O)
-public class PendingAnimation {
+public class PendingAnimation implements PropertySetter {
private final ArrayList<Consumer<EndState>> mEndListeners = new ArrayList<>();
- /** package private **/
- final AnimatorSet anim = new AnimatorSet();
- final ArrayList<Holder> animHolders = new ArrayList<>();
+ private final ArrayList<Holder> mAnimHolders = new ArrayList<>();
+ private final AnimatorSet mAnim;
+ private final long mDuration;
+
+ private ValueAnimator mProgressAnimator;
+
+ public PendingAnimation(long duration) {
+ this(duration, new AnimatorSet());
+ }
+
+ public PendingAnimation(long duration, AnimatorSet targetSet) {
+ mDuration = duration;
+ mAnim = targetSet;
+ }
/**
* Utility method to sent an interpolator on an animation and add it to the list
*/
- public void add(Animator anim, TimeInterpolator interpolator) {
- add(anim, interpolator, SpringProperty.DEFAULT);
- }
-
public void add(Animator anim, TimeInterpolator interpolator, SpringProperty springProperty) {
anim.setInterpolator(interpolator);
add(anim, springProperty);
@@ -63,8 +72,8 @@
}
public void add(Animator a, SpringProperty springProperty) {
- anim.play(a);
- addAnimationHoldersRecur(a, springProperty, animHolders);
+ mAnim.play(a);
+ addAnimationHoldersRecur(a, springProperty, mAnimHolders);
}
public void finish(boolean isSuccess, int logAction) {
@@ -74,6 +83,67 @@
mEndListeners.clear();
}
+ @Override
+ public void setViewAlpha(View view, float alpha, TimeInterpolator interpolator) {
+ if (view == null || view.getAlpha() == alpha) {
+ return;
+ }
+ ObjectAnimator anim = ObjectAnimator.ofFloat(view, View.ALPHA, alpha);
+ anim.addListener(new AlphaUpdateListener(view));
+ anim.setDuration(mDuration).setInterpolator(interpolator);
+ add(anim);
+ }
+
+ @Override
+ public <T> void setFloat(T target, FloatProperty<T> property, float value,
+ TimeInterpolator interpolator) {
+ if (property.get(target) == value) {
+ return;
+ }
+ Animator anim = ObjectAnimator.ofFloat(target, property, value);
+ anim.setDuration(mDuration).setInterpolator(interpolator);
+ add(anim);
+ }
+
+ @Override
+ public <T> void setInt(T target, IntProperty<T> property, int value,
+ TimeInterpolator interpolator) {
+ if (property.get(target) == value) {
+ return;
+ }
+ Animator anim = ObjectAnimator.ofInt(target, property, value);
+ anim.setDuration(mDuration).setInterpolator(interpolator);
+ add(anim);
+ }
+
+ /**
+ * Adds a callback to be run on every frame of the animation
+ */
+ public void addOnFrameCallback(Runnable runnable) {
+ if (mProgressAnimator == null) {
+ mProgressAnimator = ValueAnimator.ofFloat(0, 1).setDuration(mDuration);
+ add(mProgressAnimator);
+ }
+
+ mProgressAnimator.addUpdateListener(anim -> runnable.run());
+ }
+
+ public AnimatorSet getAnim() {
+ return mAnim;
+ }
+
+ /**
+ * Creates a controller for this animation
+ */
+ public AnimatorPlaybackController createPlaybackController() {
+ return new AnimatorPlaybackController(mAnim, mDuration, mAnimHolders);
+ }
+
+ /**
+ * Add a listener of receiving the end state.
+ * Note that the listeners are called as a result of calling {@link #finish(boolean, int)}
+ * and not automatically
+ */
public void addEndListener(Consumer<EndState> listener) {
mEndListeners.add(listener);
}
diff --git a/src/com/android/launcher3/anim/PropertySetter.java b/src/com/android/launcher3/anim/PropertySetter.java
index 0b2eb48..2ce620b 100644
--- a/src/com/android/launcher3/anim/PropertySetter.java
+++ b/src/com/android/launcher3/anim/PropertySetter.java
@@ -16,8 +16,6 @@
package com.android.launcher3.anim;
-import android.animation.Animator;
-import android.animation.ObjectAnimator;
import android.animation.TimeInterpolator;
import android.util.FloatProperty;
import android.util.IntProperty;
@@ -26,68 +24,35 @@
/**
* Utility class for setting a property with or without animation
*/
-public class PropertySetter {
+public interface PropertySetter {
- public static final PropertySetter NO_ANIM_PROPERTY_SETTER = new PropertySetter();
+ PropertySetter NO_ANIM_PROPERTY_SETTER = new PropertySetter() { };
- public void setViewAlpha(View view, float alpha, TimeInterpolator interpolator) {
+ /**
+ * Sets the view alpha using the provided interpolator.
+ * Unlike {@link #setFloat}, this also updates the visibility of the view as alpha changes
+ * between zero and non-zero.
+ */
+ default void setViewAlpha(View view, float alpha, TimeInterpolator interpolator) {
if (view != null) {
view.setAlpha(alpha);
AlphaUpdateListener.updateVisibility(view);
}
}
- public <T> void setFloat(T target, FloatProperty<T> property, float value,
+ /**
+ * Updates the float property of the target using the provided interpolator
+ */
+ default <T> void setFloat(T target, FloatProperty<T> property, float value,
TimeInterpolator interpolator) {
property.setValue(target, value);
}
- public <T> void setInt(T target, IntProperty<T> property, int value,
+ /**
+ * Updates the int property of the target using the provided interpolator
+ */
+ default <T> void setInt(T target, IntProperty<T> property, int value,
TimeInterpolator interpolator) {
property.setValue(target, value);
}
-
- public static class AnimatedPropertySetter extends PropertySetter {
-
- private final long mDuration;
- private final AnimatorSetBuilder mStateAnimator;
-
- public AnimatedPropertySetter(long duration, AnimatorSetBuilder builder) {
- mDuration = duration;
- mStateAnimator = builder;
- }
-
- @Override
- public void setViewAlpha(View view, float alpha, TimeInterpolator interpolator) {
- if (view == null || view.getAlpha() == alpha) {
- return;
- }
- ObjectAnimator anim = ObjectAnimator.ofFloat(view, View.ALPHA, alpha);
- anim.addListener(new AlphaUpdateListener(view));
- anim.setDuration(mDuration).setInterpolator(interpolator);
- mStateAnimator.play(anim);
- }
-
- @Override
- public <T> void setFloat(T target, FloatProperty<T> property, float value,
- TimeInterpolator interpolator) {
- if (property.get(target) == value) {
- return;
- }
- Animator anim = ObjectAnimator.ofFloat(target, property, value);
- anim.setDuration(mDuration).setInterpolator(interpolator);
- mStateAnimator.play(anim);
- }
-
- @Override
- public <T> void setInt(T target, IntProperty<T> property, int value,
- TimeInterpolator interpolator) {
- if (property.get(target) == value) {
- return;
- }
- Animator anim = ObjectAnimator.ofInt(target, property, value);
- anim.setDuration(mDuration).setInterpolator(interpolator);
- mStateAnimator.play(anim);
- }
- }
}
diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index 1773888..b3f87f0 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -83,6 +83,9 @@
public static final BooleanFlag UNSTABLE_SPRINGS = getDebugFlag(
"UNSTABLE_SPRINGS", false, "Enable unstable springs for quickstep animations");
+ public static final BooleanFlag KEYGUARD_ANIMATION = getDebugFlag(
+ "KEYGUARD_ANIMATION", false, "Enable animation for keyguard going away on wallpaper");
+
public static final BooleanFlag ADAPTIVE_ICON_WINDOW_ANIM = getDebugFlag(
"ADAPTIVE_ICON_WINDOW_ANIM", true, "Use adaptive icons for window animations.");
@@ -103,8 +106,8 @@
"APP_SEARCH_IMPROVEMENTS", true,
"Adds localized title and keyword search and ranking");
- public static final BooleanFlag ENABLE_PREDICTION_DISMISS = new DeviceFlag(
- "ENABLE_PREDICTION_DISMISS", true, "Allow option to dimiss apps from predicted list");
+ public static final BooleanFlag ENABLE_PREDICTION_DISMISS = getDebugFlag(
+ "ENABLE_PREDICTION_DISMISS", false, "Allow option to dimiss apps from predicted list");
public static final BooleanFlag ENABLE_QUICK_CAPTURE_GESTURE = getDebugFlag(
"ENABLE_QUICK_CAPTURE_GESTURE", true, "Swipe from right to left to quick capture");
@@ -120,9 +123,8 @@
public static final BooleanFlag ENABLE_HYBRID_HOTSEAT = new DeviceFlag(
"ENABLE_HYBRID_HOTSEAT", false, "Fill gaps in hotseat with predicted apps");
- public static final BooleanFlag HOTSEAT_MIGRATE_NEW_PAGE = new DeviceFlag(
- "HOTSEAT_MIGRATE_NEW_PAGE", true,
- "Migrates hotseat to a new workspace page instead of same page");
+ public static final BooleanFlag HOTSEAT_MIGRATE_TO_FOLDER = new DeviceFlag(
+ "HOTSEAT_MIGRATE_TO_FOLDER", false, "Should move hotseat items into a folder");
public static final BooleanFlag ENABLE_DEEP_SHORTCUT_ICON_CACHE = getDebugFlag(
"ENABLE_DEEP_SHORTCUT_ICON_CACHE", true, "R/W deep shortcut in IconCache");
@@ -134,12 +136,15 @@
"ENABLE_LAUNCHER_PREVIEW_IN_GRID_PICKER", true, "Show launcher preview in grid picker");
public static final BooleanFlag USE_SURFACE_VIEW_FOR_GRID_PREVIEW = getDebugFlag(
- "USE_SURFACE_VIEW_FOR_GRID_PREVIEW", false, "Use surface view for grid preview");
+ "USE_SURFACE_VIEW_FOR_GRID_PREVIEW", true, "Use surface view for grid preview");
public static final BooleanFlag ENABLE_OVERVIEW_ACTIONS = getDebugFlag(
"ENABLE_OVERVIEW_ACTIONS", true, "Show app actions instead of the shelf in Overview."
+ " As part of this decoupling, also distinguish swipe up from nav bar vs above it.");
+ public static final BooleanFlag ENABLE_SELECT_MODE = getDebugFlag(
+ "ENABLE_SELECT_MODE", true, "Show Select Mode button in Overview Actions");
+
public static final BooleanFlag ENABLE_DATABASE_RESTORE = getDebugFlag(
"ENABLE_DATABASE_RESTORE", true,
"Enable database restore when new restore session is created");
diff --git a/src/com/android/launcher3/dragndrop/BaseItemDragListener.java b/src/com/android/launcher3/dragndrop/BaseItemDragListener.java
index 75693c6..9b91a1d 100644
--- a/src/com/android/launcher3/dragndrop/BaseItemDragListener.java
+++ b/src/com/android/launcher3/dragndrop/BaseItemDragListener.java
@@ -26,7 +26,6 @@
import android.graphics.Rect;
import android.os.Handler;
import android.os.Looper;
-import android.os.SystemClock;
import android.util.Log;
import android.view.DragEvent;
import android.view.View;
@@ -63,7 +62,6 @@
protected Launcher mLauncher;
private DragController mDragController;
- private long mDragStartTime;
public BaseItemDragListener(Rect previewRect, int previewBitmapWidth, int previewViewWidth) {
mPreviewRect = previewRect;
@@ -102,7 +100,7 @@
return false;
}
}
- return mDragController.onDragEvent(mDragStartTime, event);
+ return mDragController.onDragEvent(event);
}
protected boolean onDragStart(DragEvent event) {
@@ -118,7 +116,7 @@
Point downPos = new Point((int) event.getX(), (int) event.getY());
DragOptions options = new DragOptions();
- options.systemDndStartPoint = downPos;
+ options.simulatedDndStartPoint = downPos;
options.preDragCondition = preDragCondition;
// We use drag event position as the screenPos for the preview image. Since mPreviewRect
@@ -128,7 +126,6 @@
// to source window.
createDragHelper().startDrag(new Rect(mPreviewRect),
mPreviewBitmapWidth, mPreviewViewWidth, downPos, this, options);
- mDragStartTime = SystemClock.uptimeMillis();
return true;
}
diff --git a/src/com/android/launcher3/dragndrop/DragController.java b/src/com/android/launcher3/dragndrop/DragController.java
index de7bc6d..d0d9aaf 100644
--- a/src/com/android/launcher3/dragndrop/DragController.java
+++ b/src/com/android/launcher3/dragndrop/DragController.java
@@ -43,7 +43,6 @@
import com.android.launcher3.WorkspaceItemInfo;
import com.android.launcher3.accessibility.DragViewStateAnnouncer;
import com.android.launcher3.util.ItemInfoMatcher;
-import com.android.launcher3.util.Thunk;
import com.android.launcher3.util.TouchController;
import com.android.launcher3.util.UiThreadHelper;
@@ -61,8 +60,8 @@
*/
private static final int DEEP_PRESS_DISTANCE_FACTOR = 3;
- @Thunk Launcher mLauncher;
- private FlingToDeleteHelper mFlingToDeleteHelper;
+ private final Launcher mLauncher;
+ private final FlingToDeleteHelper mFlingToDeleteHelper;
// temporaries to avoid gc thrash
private Rect mRectTemp = new Rect();
@@ -77,11 +76,12 @@
/** Options controlling the drag behavior. */
private DragOptions mOptions;
- /** X coordinate of the down event. */
- private int mMotionDownX;
+ /** Coordinate for motion down event */
+ private final Point mMotionDown = new Point();
+ /** Coordinate for last touch event **/
+ private final Point mLastTouch = new Point();
- /** Y coordinate of the down event. */
- private int mMotionDownY;
+ private final Point mTmpPoint = new Point();
private DropTarget.DragObject mDragObject;
@@ -96,12 +96,9 @@
private DropTarget mLastDropTarget;
- private final int[] mLastTouch = new int[2];
- private long mLastTouchUpTime = -1;
private int mLastTouchClassification;
private int mDistanceSinceScroll = 0;
- private int mTmpPoint[] = new int[2];
private Rect mDragLayerRect = new Rect();
private boolean mIsInPreDrag;
@@ -140,6 +137,8 @@
*
* @param b The bitmap to display as the drag image. It will be re-scaled to the
* enlarged size.
+ * @param originalView The source view (ie. icon, widget etc.) that is being dragged
+ * and which the DragView represents
* @param dragLayerX The x position in the DragLayer of the left-top of the bitmap.
* @param dragLayerY The y position in the DragLayer of the left-top of the bitmap.
* @param source An object representing where the drag originated
@@ -147,7 +146,7 @@
* @param dragRegion Coordinates within the bitmap b for the position of item being dragged.
* Makes dragging feel more precise, e.g. you can clip out a transparent border
*/
- public DragView startDrag(Bitmap b, int dragLayerX, int dragLayerY,
+ public DragView startDrag(Bitmap b, DraggableView originalView, int dragLayerX, int dragLayerY,
DragSource source, ItemInfo dragInfo, Point dragOffset, Rect dragRegion,
float initialDragViewScale, float dragViewScaleOnDrop, DragOptions options) {
if (PROFILE_DRAWING_DURING_DRAG) {
@@ -159,13 +158,13 @@
AbstractFloatingView.closeOpenViews(mLauncher, false, TYPE_DISCOVERY_BOUNCE);
mOptions = options;
- if (mOptions.systemDndStartPoint != null) {
- mLastTouch[0] = mMotionDownX = mOptions.systemDndStartPoint.x;
- mLastTouch[1] = mMotionDownY = mOptions.systemDndStartPoint.y;
+ if (mOptions.simulatedDndStartPoint != null) {
+ mLastTouch.x = mMotionDown.x = mOptions.simulatedDndStartPoint.x;
+ mLastTouch.y = mMotionDown.y = mOptions.simulatedDndStartPoint.y;
}
- final int registrationX = mMotionDownX - dragLayerX;
- final int registrationY = mMotionDownY - dragLayerY;
+ final int registrationX = mMotionDown.x - dragLayerX;
+ final int registrationY = mMotionDown.y - dragLayerY;
final int dragRegionLeft = dragRegion == null ? 0 : dragRegion.left;
final int dragRegionTop = dragRegion == null ? 0 : dragRegion.top;
@@ -173,6 +172,7 @@
mLastDropTarget = null;
mDragObject = new DropTarget.DragObject(mLauncher.getApplicationContext());
+ mDragObject.originalView = originalView;
mIsInPreDrag = mOptions.preDragCondition != null
&& !mOptions.preDragCondition.shouldStartDrag(0);
@@ -184,17 +184,13 @@
registrationY, initialDragViewScale, dragViewScaleOnDrop, scaleDps);
dragView.setItemInfo(dragInfo);
mDragObject.dragComplete = false;
- if (mOptions.isAccessibleDrag) {
- // For an accessible drag, we assume the view is being dragged from the center.
- mDragObject.xOffset = b.getWidth() / 2;
- mDragObject.yOffset = b.getHeight() / 2;
- mDragObject.accessibleDrag = true;
- } else {
- mDragObject.xOffset = mMotionDownX - (dragLayerX + dragRegionLeft);
- mDragObject.yOffset = mMotionDownY - (dragLayerY + dragRegionTop);
- mDragObject.stateAnnouncer = DragViewStateAnnouncer.createFor(dragView);
- mDragDriver = DragDriver.create(mLauncher, this, mDragObject, mOptions);
+ mDragObject.xOffset = mMotionDown.x - (dragLayerX + dragRegionLeft);
+ mDragObject.yOffset = mMotionDown.y - (dragLayerY + dragRegionTop);
+
+ mDragDriver = DragDriver.create(this, mOptions, mFlingToDeleteHelper::recordMotionEvent);
+ if (!mOptions.isAccessibleDrag) {
+ mDragObject.stateAnnouncer = DragViewStateAnnouncer.createFor(dragView);
}
mDragObject.dragSource = source;
@@ -210,7 +206,7 @@
}
mLauncher.getDragLayer().performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
- dragView.show(mLastTouch[0], mLastTouch[1]);
+ dragView.show(mLastTouch.x, mLastTouch.y);
mDistanceSinceScroll = 0;
if (!mIsInPreDrag) {
@@ -219,7 +215,7 @@
mOptions.preDragCondition.onPreDragStart(mDragObject);
}
- handleMoveEvent(mLastTouch[0], mLastTouch[1]);
+ handleMoveEvent(mLastTouch.x, mLastTouch.y);
mLauncher.getUserEventDispatcher().resetActionDurationMillis();
return dragView;
}
@@ -336,7 +332,7 @@
}
}
};
- mDragObject.dragView.animateTo(mMotionDownX, mMotionDownY, onCompleteRunnable, duration);
+ mDragObject.dragView.animateTo(mMotionDown.x, mMotionDown.y, onCompleteRunnable, duration);
}
private void callOnDragEnd() {
@@ -365,30 +361,17 @@
/**
* Clamps the position to the drag layer bounds.
*/
- private int[] getClampedDragLayerPos(float x, float y) {
+ private Point getClampedDragLayerPos(float x, float y) {
mLauncher.getDragLayer().getLocalVisibleRect(mDragLayerRect);
- mTmpPoint[0] = (int) Math.max(mDragLayerRect.left, Math.min(x, mDragLayerRect.right - 1));
- mTmpPoint[1] = (int) Math.max(mDragLayerRect.top, Math.min(y, mDragLayerRect.bottom - 1));
+ mTmpPoint.x = (int) Math.max(mDragLayerRect.left, Math.min(x, mDragLayerRect.right - 1));
+ mTmpPoint.y = (int) Math.max(mDragLayerRect.top, Math.min(y, mDragLayerRect.bottom - 1));
return mTmpPoint;
}
- public long getLastGestureUpTime() {
- if (mDragDriver != null) {
- return System.currentTimeMillis();
- } else {
- return mLastTouchUpTime;
- }
- }
-
- public void resetLastGestureUpTime() {
- mLastTouchUpTime = -1;
- }
-
@Override
public void onDriverDragMove(float x, float y) {
- final int[] dragLayerPos = getClampedDragLayerPos(x, y);
-
- handleMoveEvent(dragLayerPos[0], dragLayerPos[1]);
+ Point dragLayerPos = getClampedDragLayerPos(x, y);
+ handleMoveEvent(dragLayerPos.x, dragLayerPos.y);
}
@Override
@@ -422,53 +405,38 @@
/**
* Call this from a drag source view.
*/
+ @Override
public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
if (mOptions != null && mOptions.isAccessibleDrag) {
return false;
}
- // Update the velocity tracker
- mFlingToDeleteHelper.recordMotionEvent(ev);
+ Point dragLayerPos = getClampedDragLayerPos(ev.getX(), ev.getY());
+ mLastTouch.set(dragLayerPos.x, dragLayerPos.y);
+ if (ev.getAction() == MotionEvent.ACTION_DOWN) {
+ // Remember location of down touch
+ mMotionDown.set(dragLayerPos.x, dragLayerPos.y);
+ }
- final int action = ev.getAction();
- final int[] dragLayerPos = getClampedDragLayerPos(ev.getX(), ev.getY());
- final int dragLayerX = dragLayerPos[0];
- final int dragLayerY = dragLayerPos[1];
- mLastTouch[0] = dragLayerX;
- mLastTouch[1] = dragLayerY;
if (ATLEAST_Q) {
mLastTouchClassification = ev.getClassification();
}
-
- switch (action) {
- case MotionEvent.ACTION_DOWN:
- // Remember location of down touch
- mMotionDownX = dragLayerX;
- mMotionDownY = dragLayerY;
- break;
- case MotionEvent.ACTION_UP:
- mLastTouchUpTime = System.currentTimeMillis();
- break;
- }
-
return mDragDriver != null && mDragDriver.onInterceptTouchEvent(ev);
}
/**
* Call this from a drag source view.
*/
- public boolean onDragEvent(long dragStartTime, DragEvent event) {
- mFlingToDeleteHelper.recordDragEvent(dragStartTime, event);
- return mDragDriver != null && mDragDriver.onDragEvent(event);
+ @Override
+ public boolean onControllerTouchEvent(MotionEvent ev) {
+ return mDragDriver != null && mDragDriver.onTouchEvent(ev);
}
/**
- * Call this from a drag view.
+ * Call this from a drag source view.
*/
- public void onDragViewAnimationEnd() {
- if (mDragDriver != null) {
- mDragDriver.onDragViewAnimationEnd();
- }
+ public boolean onDragEvent(DragEvent event) {
+ return mDragDriver != null && mDragDriver.onDragEvent(event);
}
/**
@@ -493,9 +461,8 @@
checkTouchMove(dropTarget);
// Check if we are hovering over the scroll areas
- mDistanceSinceScroll += Math.hypot(mLastTouch[0] - x, mLastTouch[1] - y);
- mLastTouch[0] = x;
- mLastTouch[1] = y;
+ mDistanceSinceScroll += Math.hypot(mLastTouch.x - x, mLastTouch.y - y);
+ mLastTouch.set(x, y);
int distanceDragged = mDistanceSinceScroll;
if (ATLEAST_Q && mLastTouchClassification == MotionEvent.CLASSIFICATION_DEEP_PRESS) {
@@ -513,7 +480,7 @@
public void forceTouchMove() {
int[] dummyCoordinates = mCoordinatesTemp;
- DropTarget dropTarget = findDropTarget(mLastTouch[0], mLastTouch[1], dummyCoordinates);
+ DropTarget dropTarget = findDropTarget(mLastTouch.x, mLastTouch.y, dummyCoordinates);
mDragObject.x = dummyCoordinates[0];
mDragObject.y = dummyCoordinates[1];
checkTouchMove(dropTarget);
@@ -537,44 +504,6 @@
}
/**
- * Call this from a drag source view.
- */
- public boolean onControllerTouchEvent(MotionEvent ev) {
- if (mDragDriver == null || mOptions == null || mOptions.isAccessibleDrag) {
- return false;
- }
-
- // Update the velocity tracker
- mFlingToDeleteHelper.recordMotionEvent(ev);
-
- final int action = ev.getAction();
- final int[] dragLayerPos = getClampedDragLayerPos(ev.getX(), ev.getY());
- final int dragLayerX = dragLayerPos[0];
- final int dragLayerY = dragLayerPos[1];
-
- switch (action) {
- case MotionEvent.ACTION_DOWN:
- // Remember where the motion event started
- mMotionDownX = dragLayerX;
- mMotionDownY = dragLayerY;
- break;
- }
-
- return mDragDriver.onTouchEvent(ev);
- }
-
- /**
- * Since accessible drag and drop won't cause the same sequence of touch events, we manually
- * inject the appropriate state which would have been otherwise initiated via touch events.
- */
- public void prepareAccessibleDrag(int x, int y) {
- mMotionDownX = x;
- mMotionDownY = y;
- mLastTouch[0] = x;
- mLastTouch[1] = y;
- }
-
- /**
* As above, since accessible drag and drop won't cause the same sequence of touch events,
* we manually ensure appropriate drag and drop events get emulated for accessible drag.
*/
diff --git a/src/com/android/launcher3/dragndrop/DragDriver.java b/src/com/android/launcher3/dragndrop/DragDriver.java
index 87461d5..d4ce308 100644
--- a/src/com/android/launcher3/dragndrop/DragDriver.java
+++ b/src/com/android/launcher3/dragndrop/DragDriver.java
@@ -16,19 +16,19 @@
package com.android.launcher3.dragndrop;
-import android.content.Context;
-import android.util.Log;
+import android.os.SystemClock;
import android.view.DragEvent;
import android.view.MotionEvent;
-import com.android.launcher3.DropTarget.DragObject;
-import com.android.launcher3.testing.TestProtocol;
+import java.util.function.Consumer;
/**
* Base class for driving a drag/drop operation.
*/
public abstract class DragDriver {
+
protected final EventListener mEventListener;
+ protected final Consumer<MotionEvent> mSecondaryEventConsumer;
public interface EventListener {
void onDriverDragMove(float x, float y);
@@ -37,131 +37,175 @@
void onDriverDragCancel();
}
- public DragDriver(EventListener eventListener) {
+ public DragDriver(EventListener eventListener, Consumer<MotionEvent> sec) {
mEventListener = eventListener;
+ mSecondaryEventConsumer = sec;
}
/**
- * Handles ending of the DragView animation.
+ * Called to handle system touch event
*/
- public void onDragViewAnimationEnd() { }
-
public boolean onTouchEvent(MotionEvent ev) {
- final int action = ev.getAction();
-
- switch (action) {
- case MotionEvent.ACTION_MOVE:
- mEventListener.onDriverDragMove(ev.getX(), ev.getY());
- break;
- case MotionEvent.ACTION_UP:
- mEventListener.onDriverDragMove(ev.getX(), ev.getY());
- mEventListener.onDriverDragEnd(ev.getX(), ev.getY());
- break;
- case MotionEvent.ACTION_CANCEL:
- mEventListener.onDriverDragCancel();
- break;
- }
-
- return true;
+ return false;
}
- public abstract boolean onDragEvent (DragEvent event);
-
-
+ /**
+ * Called to handle system touch intercept event
+ */
public boolean onInterceptTouchEvent(MotionEvent ev) {
- final int action = ev.getAction();
-
- switch (action) {
- case MotionEvent.ACTION_UP:
- mEventListener.onDriverDragEnd(ev.getX(), ev.getY());
- break;
- case MotionEvent.ACTION_CANCEL:
- mEventListener.onDriverDragCancel();
- break;
- }
-
- return true;
+ return false;
}
- public static DragDriver create(Context context, DragController dragController,
- DragObject dragObject, DragOptions options) {
- if (options.systemDndStartPoint != null) {
- return new SystemDragDriver(dragController, context, dragObject);
+ /**
+ * Called to handle system drag event
+ */
+ public boolean onDragEvent(DragEvent event) {
+ return false;
+ }
+
+ /**
+ * Created a driver for handing the actual events
+ */
+ public static DragDriver create(DragController dragController, DragOptions options,
+ Consumer<MotionEvent> sec) {
+ if (options.simulatedDndStartPoint != null) {
+ if (options.isAccessibleDrag) {
+ return null;
+ }
+ return new SystemDragDriver(dragController, sec);
} else {
- return new InternalDragDriver(dragController);
+ return new InternalDragDriver(dragController, sec);
+ }
+ }
+
+ /**
+ * Class for driving a system (i.e. framework) drag/drop operation.
+ */
+ static class SystemDragDriver extends DragDriver {
+
+ private final long mDragStartTime;
+ float mLastX = 0;
+ float mLastY = 0;
+
+ SystemDragDriver(DragController dragController, Consumer<MotionEvent> sec) {
+ super(dragController, sec);
+ mDragStartTime = SystemClock.uptimeMillis();
+ }
+
+ @Override
+ public boolean onInterceptTouchEvent(MotionEvent ev) {
+ return false;
+ }
+
+ /**
+ * It creates a temporary {@link MotionEvent} object for secondary consumer
+ */
+ private void simulateSecondaryMotionEvent(DragEvent event) {
+ final int motionAction;
+ switch (event.getAction()) {
+ case DragEvent.ACTION_DRAG_STARTED:
+ motionAction = MotionEvent.ACTION_DOWN;
+ break;
+ case DragEvent.ACTION_DRAG_LOCATION:
+ motionAction = MotionEvent.ACTION_MOVE;
+ break;
+ case DragEvent.ACTION_DRAG_ENDED:
+ motionAction = MotionEvent.ACTION_UP;
+ break;
+ default:
+ return;
+ }
+ MotionEvent emulatedEvent = MotionEvent.obtain(mDragStartTime,
+ SystemClock.uptimeMillis(), motionAction, event.getX(), event.getY(), 0);
+ mSecondaryEventConsumer.accept(emulatedEvent);
+ emulatedEvent.recycle();
+ }
+
+ @Override
+ public boolean onDragEvent(DragEvent event) {
+ simulateSecondaryMotionEvent(event);
+ final int action = event.getAction();
+
+ switch (action) {
+ case DragEvent.ACTION_DRAG_STARTED:
+ mLastX = event.getX();
+ mLastY = event.getY();
+ return true;
+
+ case DragEvent.ACTION_DRAG_ENTERED:
+ return true;
+
+ case DragEvent.ACTION_DRAG_LOCATION:
+ mLastX = event.getX();
+ mLastY = event.getY();
+ mEventListener.onDriverDragMove(event.getX(), event.getY());
+ return true;
+
+ case DragEvent.ACTION_DROP:
+ mLastX = event.getX();
+ mLastY = event.getY();
+ mEventListener.onDriverDragMove(event.getX(), event.getY());
+ mEventListener.onDriverDragEnd(mLastX, mLastY);
+ return true;
+ case DragEvent.ACTION_DRAG_EXITED:
+ mEventListener.onDriverDragExitWindow();
+ return true;
+
+ case DragEvent.ACTION_DRAG_ENDED:
+ mEventListener.onDriverDragCancel();
+ return true;
+
+ default:
+ return false;
+ }
+ }
+ }
+
+ /**
+ * Class for driving an internal (i.e. not using framework) drag/drop operation.
+ */
+ static class InternalDragDriver extends DragDriver {
+ InternalDragDriver(DragController dragController, Consumer<MotionEvent> sec) {
+ super(dragController, sec);
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent ev) {
+ mSecondaryEventConsumer.accept(ev);
+ final int action = ev.getAction();
+
+ switch (action) {
+ case MotionEvent.ACTION_MOVE:
+ mEventListener.onDriverDragMove(ev.getX(), ev.getY());
+ break;
+ case MotionEvent.ACTION_UP:
+ mEventListener.onDriverDragMove(ev.getX(), ev.getY());
+ mEventListener.onDriverDragEnd(ev.getX(), ev.getY());
+ break;
+ case MotionEvent.ACTION_CANCEL:
+ mEventListener.onDriverDragCancel();
+ break;
+ }
+
+ return true;
+ }
+
+
+ public boolean onInterceptTouchEvent(MotionEvent ev) {
+ mSecondaryEventConsumer.accept(ev);
+ final int action = ev.getAction();
+
+ switch (action) {
+ case MotionEvent.ACTION_UP:
+ mEventListener.onDriverDragEnd(ev.getX(), ev.getY());
+ break;
+ case MotionEvent.ACTION_CANCEL:
+ mEventListener.onDriverDragCancel();
+ break;
+ }
+ return true;
}
}
}
-/**
- * Class for driving a system (i.e. framework) drag/drop operation.
- */
-class SystemDragDriver extends DragDriver {
- float mLastX = 0;
- float mLastY = 0;
-
- SystemDragDriver(DragController dragController, Context context, DragObject dragObject) {
- super(dragController);
- }
-
- @Override
- public boolean onTouchEvent(MotionEvent ev) {
- return false;
- }
-
- @Override
- public boolean onInterceptTouchEvent(MotionEvent ev) {
- return false;
- }
-
- @Override
- public boolean onDragEvent (DragEvent event) {
- final int action = event.getAction();
-
- switch (action) {
- case DragEvent.ACTION_DRAG_STARTED:
- mLastX = event.getX();
- mLastY = event.getY();
- return true;
-
- case DragEvent.ACTION_DRAG_ENTERED:
- return true;
-
- case DragEvent.ACTION_DRAG_LOCATION:
- mLastX = event.getX();
- mLastY = event.getY();
- mEventListener.onDriverDragMove(event.getX(), event.getY());
- return true;
-
- case DragEvent.ACTION_DROP:
- mLastX = event.getX();
- mLastY = event.getY();
- mEventListener.onDriverDragMove(event.getX(), event.getY());
- mEventListener.onDriverDragEnd(mLastX, mLastY);
- return true;
- case DragEvent.ACTION_DRAG_EXITED:
- mEventListener.onDriverDragExitWindow();
- return true;
-
- case DragEvent.ACTION_DRAG_ENDED:
- mEventListener.onDriverDragCancel();
- return true;
-
- default:
- return false;
- }
- }
-}
-
-/**
- * Class for driving an internal (i.e. not using framework) drag/drop operation.
- */
-class InternalDragDriver extends DragDriver {
- InternalDragDriver(DragController dragController) {
- super(dragController);
- }
-
- @Override
- public boolean onDragEvent (DragEvent event) { return false; }
-}
diff --git a/src/com/android/launcher3/dragndrop/DragLayer.java b/src/com/android/launcher3/dragndrop/DragLayer.java
index 369bf28..e18ca54 100644
--- a/src/com/android/launcher3/dragndrop/DragLayer.java
+++ b/src/com/android/launcher3/dragndrop/DragLayer.java
@@ -41,7 +41,6 @@
import android.view.accessibility.AccessibilityManager;
import android.view.animation.Interpolator;
import android.widget.FrameLayout;
-import android.widget.TextView;
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.CellLayout;
@@ -52,7 +51,6 @@
import com.android.launcher3.Workspace;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.folder.Folder;
-import com.android.launcher3.folder.FolderIcon;
import com.android.launcher3.graphics.OverviewScrim;
import com.android.launcher3.graphics.RotationMode;
import com.android.launcher3.graphics.WorkspaceAndHotseatScrim;
@@ -90,6 +88,8 @@
private int mTopViewIndex;
private int mChildCountOnLastUpdate = -1;
+ private Rect mTmpRect = new Rect();
+
// Related to adjacent page hints
private final ViewGroupFocusHelper mFocusIndicatorHelper;
private final WorkspaceAndHotseatScrim mWorkspaceScrim;
@@ -254,59 +254,46 @@
public void animateViewIntoPosition(DragView dragView, final View child, int duration,
View anchorView) {
+
ShortcutAndWidgetContainer parentChildren = (ShortcutAndWidgetContainer) child.getParent();
CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
parentChildren.measureChild(child);
+ parentChildren.layoutChild(child);
- Rect r = new Rect();
- getViewRectRelativeToSelf(dragView, r);
+ getViewRectRelativeToSelf(dragView, mTmpRect);
+ final int fromX = mTmpRect.left;
+ final int fromY = mTmpRect.top;
float coord[] = new float[2];
float childScale = child.getScaleX();
+
coord[0] = lp.x + (child.getMeasuredWidth() * (1 - childScale) / 2);
coord[1] = lp.y + (child.getMeasuredHeight() * (1 - childScale) / 2);
// Since the child hasn't necessarily been laid out, we force the lp to be updated with
// the correct coordinates (above) and use these to determine the final location
float scale = getDescendantCoordRelativeToSelf((View) child.getParent(), coord);
+
// We need to account for the scale of the child itself, as the above only accounts for
// for the scale in parents.
scale *= childScale;
int toX = Math.round(coord[0]);
int toY = Math.round(coord[1]);
float toScale = scale;
- if (child instanceof TextView) {
- TextView tv = (TextView) child;
- // Account for the source scale of the icon (ie. from AllApps to Workspace, in which
- // the workspace may have smaller icon bounds).
- toScale = scale / dragView.getIntrinsicIconScaleFactor();
- // The child may be scaled (always about the center of the view) so to account for it,
- // we have to offset the position by the scaled size. Once we do that, we can center
- // the drag view about the scaled child view.
- // padding will remain constant (does not scale with size)
- toY += tv.getPaddingTop();
- toY -= dragView.getMeasuredHeight() * (1 - toScale) / 2;
- if (dragView.getDragVisualizeOffset() != null) {
- toY -= Math.round(toScale * dragView.getDragVisualizeOffset().y);
- }
+ if (child instanceof DraggableView) {
+ DraggableView d = (DraggableView) child;
+ d.getVisualDragBounds(mTmpRect);
- toX -= (dragView.getMeasuredWidth() - Math.round(scale * child.getMeasuredWidth())) / 2;
- } else if (child instanceof FolderIcon) {
- // Account for holographic blur padding on the drag view
- toY += Math.round(scale * (child.getPaddingTop() - dragView.getDragRegionTop()));
- toY -= scale * dragView.getBlurSizeOutline() / 2;
- toY -= (1 - scale) * dragView.getMeasuredHeight() / 2;
- // Center in the x coordinate about the target's drawable
- toX -= (dragView.getMeasuredWidth() - Math.round(scale * child.getMeasuredWidth())) / 2;
- } else {
- toY -= (Math.round(scale * (dragView.getHeight() - child.getMeasuredHeight()))) / 2;
- toX -= (Math.round(scale * (dragView.getMeasuredWidth()
- - child.getMeasuredWidth()))) / 2;
+ // This accounts for the offset of the DragView created by scaling it about its
+ // center as it animates into place.
+ float scaleShiftX = dragView.getMeasuredWidth() * (1 - scale) / 2;
+ float scaleShiftY = dragView.getMeasuredHeight() * (1 - scale) / 2;
+
+ toX += scale * (mTmpRect.left - dragView.getBlurSizeOutline() / 2) - scaleShiftX;
+ toY += scale * (mTmpRect.top - dragView.getBlurSizeOutline() / 2) - scaleShiftY;
}
- final int fromX = r.left;
- final int fromY = r.top;
child.setVisibility(INVISIBLE);
Runnable onCompleteRunnable = () -> child.setVisibility(VISIBLE);
animateViewIntoPosition(dragView, fromX, fromY, toX, toY, 1, 1, 1, toScale, toScale,
@@ -559,7 +546,7 @@
@Override
public void setInsets(Rect insets) {
super.setInsets(insets);
- mWorkspaceScrim.onInsetsChanged(insets);
+ mWorkspaceScrim.onInsetsChanged(insets, mAllowSysuiScrims);
mOverviewScrim.onInsetsChanged(insets);
}
diff --git a/src/com/android/launcher3/dragndrop/DragOptions.java b/src/com/android/launcher3/dragndrop/DragOptions.java
index 2d19f36..959602b 100644
--- a/src/com/android/launcher3/dragndrop/DragOptions.java
+++ b/src/com/android/launcher3/dragndrop/DragOptions.java
@@ -28,8 +28,11 @@
/** Whether or not an accessible drag operation is in progress. */
public boolean isAccessibleDrag = false;
- /** Specifies the start location for the system DnD, null when using internal DnD */
- public Point systemDndStartPoint = null;
+ /**
+ * Specifies the start location for a simulated DnD (like system drag or accessibility drag),
+ * null when using internal DnD
+ */
+ public Point simulatedDndStartPoint = null;
/** Determines when a pre-drag should transition to a drag. By default, this is immediate. */
public PreDragCondition preDragCondition = null;
diff --git a/src/com/android/launcher3/dragndrop/DragView.java b/src/com/android/launcher3/dragndrop/DragView.java
index 145885a..7c76d34 100644
--- a/src/com/android/launcher3/dragndrop/DragView.java
+++ b/src/com/android/launcher3/dragndrop/DragView.java
@@ -19,8 +19,6 @@
import static com.android.launcher3.Utilities.getBadge;
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
import android.animation.FloatArrayEvaluator;
import android.animation.ValueAnimator;
import android.animation.ValueAnimator.AnimatorUpdateListener;
@@ -146,15 +144,6 @@
}
});
- mAnim.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- if (!mAnimationCancelled) {
- mDragController.onDragViewAnimationEnd();
- }
- }
- });
-
mBitmap = bitmap;
setDragRegion(new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight()));
diff --git a/src/com/android/launcher3/dragndrop/DraggableView.java b/src/com/android/launcher3/dragndrop/DraggableView.java
new file mode 100644
index 0000000..df99902
--- /dev/null
+++ b/src/com/android/launcher3/dragndrop/DraggableView.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.dragndrop;
+
+import android.graphics.Rect;
+
+/**
+ * Interface defining methods required for drawing and previewing DragViews, drag previews, and
+ * related animations
+ */
+public interface DraggableView {
+ int DRAGGABLE_ICON = 0;
+ int DRAGGABLE_WIDGET = 1;
+
+ /**
+ * Static ctr for a simple instance which just returns the view type.
+ */
+ static DraggableView ofType(int type) {
+ return () -> type;
+ }
+
+ /**
+ * Certain handling of DragViews depend only on whether this is an Icon Type item or a Widget
+ * Type item.
+ *
+ * @return DRAGGABLE_ICON or DRAGGABLE_WIDGET as appropriate
+ */
+ int getViewType();
+
+ /**
+ * Before rendering as a DragView bitmap, some views need a preparation step.
+ */
+ default void prepareDrawDragView() { }
+
+ /**
+ * If an actual View subclass, this method returns the rectangle (within the View's coordinates)
+ * of the visual region that should get dragged. This is used to extract exactly that element
+ * as well as to offset that element as appropriate for various animations
+ *
+ * @param bounds Visual bounds in the views coordinates will be written here.
+ */
+ default void getVisualDragBounds(Rect bounds) { }
+}
diff --git a/src/com/android/launcher3/dragndrop/FlingToDeleteHelper.java b/src/com/android/launcher3/dragndrop/FlingToDeleteHelper.java
index 06b5c40..7788f93 100644
--- a/src/com/android/launcher3/dragndrop/FlingToDeleteHelper.java
+++ b/src/com/android/launcher3/dragndrop/FlingToDeleteHelper.java
@@ -17,8 +17,6 @@
package com.android.launcher3.dragndrop;
import android.graphics.PointF;
-import android.os.SystemClock;
-import android.view.DragEvent;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.ViewConfiguration;
@@ -55,31 +53,6 @@
mVelocityTracker.addMovement(ev);
}
- /**
- * Same as {@link #recordMotionEvent}. It creates a temporary {@link MotionEvent} object
- * using {@param event} for tracking velocity.
- */
- public void recordDragEvent(long dragStartTime, DragEvent event) {
- final int motionAction;
- switch (event.getAction()) {
- case DragEvent.ACTION_DRAG_STARTED:
- motionAction = MotionEvent.ACTION_DOWN;
- break;
- case DragEvent.ACTION_DRAG_LOCATION:
- motionAction = MotionEvent.ACTION_MOVE;
- break;
- case DragEvent.ACTION_DRAG_ENDED:
- motionAction = MotionEvent.ACTION_UP;
- break;
- default:
- return;
- }
- MotionEvent emulatedEvent = MotionEvent.obtain(dragStartTime, SystemClock.uptimeMillis(),
- motionAction, event.getX(), event.getY(), 0);
- recordMotionEvent(emulatedEvent);
- emulatedEvent.recycle();
- }
-
public void releaseVelocityTracker() {
if (mVelocityTracker != null) {
mVelocityTracker.recycle();
diff --git a/src/com/android/launcher3/dragndrop/FolderAdaptiveIcon.java b/src/com/android/launcher3/dragndrop/FolderAdaptiveIcon.java
index 0bb3fba..ea1fbdb 100644
--- a/src/com/android/launcher3/dragndrop/FolderAdaptiveIcon.java
+++ b/src/com/android/launcher3/dragndrop/FolderAdaptiveIcon.java
@@ -20,10 +20,10 @@
import android.annotation.TargetApi;
import android.graphics.Bitmap;
-import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Path;
import android.graphics.Point;
+import android.graphics.Rect;
import android.graphics.drawable.AdaptiveIconDrawable;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
@@ -33,7 +33,6 @@
import androidx.annotation.Nullable;
import com.android.launcher3.Launcher;
-import com.android.launcher3.R;
import com.android.launcher3.folder.FolderIcon;
import com.android.launcher3.folder.PreviewBackground;
import com.android.launcher3.graphics.ShiftedBitmapDrawable;
@@ -50,6 +49,7 @@
private final Drawable mBadge;
private final Path mMask;
private final ConstantState mConstantState;
+ private static final Rect sTmpRect = new Rect();
private FolderAdaptiveIcon(Drawable bg, Drawable fg, Drawable badge, Path mask) {
super(bg, fg);
@@ -72,23 +72,14 @@
public static @Nullable FolderAdaptiveIcon createFolderAdaptiveIcon(
Launcher launcher, int folderId, Point dragViewSize) {
Preconditions.assertNonUiThread();
- int margin = launcher.getResources()
- .getDimensionPixelSize(R.dimen.blur_size_medium_outline);
-
- // Allocate various bitmaps on the background thread, because why not!
- int width = dragViewSize.x - margin;
- int height = dragViewSize.y - margin;
- if (width <= 0 || height <= 0) {
- return null;
- }
- final Bitmap badge = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
// Create the actual drawable on the UI thread to avoid race conditions with
// FolderIcon draw pass
try {
return MAIN_EXECUTOR.submit(() -> {
FolderIcon icon = launcher.findFolderIcon(folderId);
- return icon == null ? null : createDrawableOnUiThread(icon, badge, dragViewSize);
+ return icon == null ? null : createDrawableOnUiThread(icon, dragViewSize);
+
}).get();
} catch (Exception e) {
Log.e(TAG, "Unable to create folder icon", e);
@@ -96,49 +87,50 @@
}
}
- /**
- * Initializes various bitmaps on the UI thread and returns the final drawable.
- */
private static FolderAdaptiveIcon createDrawableOnUiThread(FolderIcon icon,
- Bitmap badgeBitmap, Point dragViewSize) {
+ Point dragViewSize) {
Preconditions.assertUIThread();
- float margin = icon.getResources().getDimension(R.dimen.blur_size_medium_outline) / 2;
- Canvas c = new Canvas();
+ icon.getPreviewBounds(sTmpRect);
+
PreviewBackground bg = icon.getFolderBackground();
- // Initialize badge
- c.setBitmap(badgeBitmap);
- bg.drawShadow(c);
- bg.drawBackgroundStroke(c);
- icon.drawDot(c);
+ // assume square
+ assert (dragViewSize.x == dragViewSize.y);
+ final int previewSize = sTmpRect.width();
- // Initialize preview
- final float sizeScaleFactor = 1 + 2 * AdaptiveIconDrawable.getExtraInsetFraction();
- final int previewWidth = (int) (dragViewSize.x * sizeScaleFactor);
- final int previewHeight = (int) (dragViewSize.y * sizeScaleFactor);
+ final int margin = (dragViewSize.x - previewSize) / 2;
+ final float previewShiftX = -sTmpRect.left + margin;
+ final float previewShiftY = -sTmpRect.top + margin;
- final float shiftFactor = AdaptiveIconDrawable.getExtraInsetFraction() / sizeScaleFactor;
- final float previewShiftX = shiftFactor * previewWidth;
- final float previewShiftY = shiftFactor * previewHeight;
-
- Bitmap previewBitmap = BitmapRenderer.createHardwareBitmap(previewWidth, previewHeight,
+ // Initialize badge, which consists of the outline stroke, shadow and dot; these
+ // must be rendered above the foreground
+ Bitmap badgeBmp = BitmapRenderer.createHardwareBitmap(dragViewSize.x, dragViewSize.y,
(canvas) -> {
- int count = canvas.save();
+ canvas.save();
canvas.translate(previewShiftX, previewShiftY);
- icon.getPreviewItemManager().draw(canvas);
- canvas.restoreToCount(count);
+ bg.drawShadow(canvas);
+ bg.drawBackgroundStroke(canvas);
+ icon.drawDot(canvas);
+ canvas.restore();
});
// Initialize mask
Path mask = new Path();
Matrix m = new Matrix();
- m.setTranslate(margin, margin);
+ m.setTranslate(previewShiftX, previewShiftY);
bg.getClipPath().transform(m, mask);
- ShiftedBitmapDrawable badge = new ShiftedBitmapDrawable(badgeBitmap, margin, margin);
- ShiftedBitmapDrawable foreground = new ShiftedBitmapDrawable(previewBitmap,
- margin - previewShiftX, margin - previewShiftY);
+ Bitmap previewBitmap = BitmapRenderer.createHardwareBitmap(dragViewSize.x, dragViewSize.y,
+ (canvas) -> {
+ canvas.save();
+ canvas.translate(previewShiftX, previewShiftY);
+ icon.getPreviewItemManager().draw(canvas);
+ canvas.restore();
+ });
+
+ ShiftedBitmapDrawable badge = new ShiftedBitmapDrawable(badgeBmp, 0, 0);
+ ShiftedBitmapDrawable foreground = new ShiftedBitmapDrawable(previewBitmap, 0, 0);
return new FolderAdaptiveIcon(new ColorDrawable(bg.getBgColor()), foreground, badge, mask);
}
diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java
index 2be8ff4..365e76f 100644
--- a/src/com/android/launcher3/folder/Folder.java
+++ b/src/com/android/launcher3/folder/Folder.java
@@ -81,10 +81,12 @@
import com.android.launcher3.PagedView;
import com.android.launcher3.R;
import com.android.launcher3.ShortcutAndWidgetContainer;
+import com.android.launcher3.Utilities;
import com.android.launcher3.Workspace;
import com.android.launcher3.Workspace.ItemOperator;
import com.android.launcher3.WorkspaceItemInfo;
import com.android.launcher3.accessibility.AccessibleDragListenerAdapter;
+import com.android.launcher3.accessibility.FolderAccessibilityHelper;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.dragndrop.DragController;
import com.android.launcher3.dragndrop.DragController.DragListener;
@@ -271,16 +273,15 @@
mDragController.addDragListener(this);
if (options.isAccessibleDrag) {
mDragController.addDragListener(new AccessibleDragListenerAdapter(
- mContent, CellLayout.FOLDER_ACCESSIBILITY_DRAG) {
-
- @Override
- protected void enableAccessibleDrag(boolean enable) {
- super.enableAccessibleDrag(enable);
- mFooter.setImportantForAccessibility(enable
- ? IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
- : IMPORTANT_FOR_ACCESSIBILITY_AUTO);
- }
- });
+ mContent, FolderAccessibilityHelper::new) {
+ @Override
+ protected void enableAccessibleDrag(boolean enable) {
+ super.enableAccessibleDrag(enable);
+ mFooter.setImportantForAccessibility(enable
+ ? IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
+ : IMPORTANT_FOR_ACCESSIBILITY_AUTO);
+ }
+ });
}
mLauncher.getWorkspace().beginDragShared(v, this, options);
@@ -324,13 +325,11 @@
public void startEditingFolderName() {
post(() -> {
if (FeatureFlags.FOLDER_NAME_SUGGEST.get()) {
- if (isEmpty(mFolderName.getText())) {
- ofNullable(mInfo)
- .map(info -> info.suggestedFolderNames)
- .map(folderNames -> (FolderNameInfo[]) folderNames
- .getParcelableArrayExtra(FolderInfo.EXTRA_FOLDER_SUGGESTIONS))
- .ifPresent(nameInfos -> showLabelSuggestion(nameInfos, false));
- }
+ ofNullable(mInfo)
+ .map(info -> info.suggestedFolderNames)
+ .map(folderNames -> (FolderNameInfo[]) folderNames
+ .getParcelableArrayExtra(FolderInfo.EXTRA_FOLDER_SUGGESTIONS))
+ .ifPresent(nameInfos -> showLabelSuggestion(nameInfos, false));
}
mFolderName.setHint("");
mIsEditingName = true;
@@ -485,19 +484,24 @@
nameInfos[1].getLabel());
if (shouldOpen) {
- CharSequence firstLabel = nameInfos[0] == null ? "" : nameInfos[0].getLabel();
- if (!isEmpty(firstLabel)) {
- mFolderName.setHint("");
- mFolderName.setText(firstLabel);
+ // update the primary suggestion if the folder name is empty.
+ if (isEmpty(mFolderName.getText())) {
+ CharSequence firstLabel = nameInfos[0] == null ? "" : nameInfos[0].getLabel();
+ if (!isEmpty(firstLabel)) {
+ mFolderName.setHint("");
+ mFolderName.setText(firstLabel);
+ }
}
if (animate) {
animateOpen(mInfo.contents, 0, true);
}
mFolderName.showKeyboard();
mFolderName.displayCompletions(
- asList(nameInfos).subList(1, nameInfos.length).stream()
+ asList(nameInfos).subList(0, nameInfos.length).stream()
.filter(Objects::nonNull)
.map(s -> s.getLabel().toString())
+ .filter(s -> !s.isEmpty())
+ .filter(s -> !s.equalsIgnoreCase(mFolderName.getText().toString()))
.collect(Collectors.toList()));
}
}
@@ -516,9 +520,6 @@
}
private void startAnimation(final AnimatorSet a) {
- if (mCurrentAnimator != null && mCurrentAnimator.isRunning()) {
- mCurrentAnimator.cancel();
- }
final Workspace workspace = mLauncher.getWorkspace();
final CellLayout currentCellLayout =
(CellLayout) workspace.getChildAt(workspace.getCurrentPage());
@@ -635,6 +636,9 @@
// dropping. One resulting issue is that replaceFolderWithFinalItem() can be called twice.
mDeleteFolderOnDropCompleted = false;
+ if (mCurrentAnimator != null && mCurrentAnimator.isRunning()) {
+ mCurrentAnimator.cancel();
+ }
AnimatorSet anim = new FolderAnimationManager(this, true /* isOpening */).getAnimator();
anim.addListener(new AnimatorListenerAdapter() {
@Override
@@ -738,6 +742,9 @@
}
private void animateClosed() {
+ if (mCurrentAnimator != null && mCurrentAnimator.isRunning()) {
+ mCurrentAnimator.cancel();
+ }
AnimatorSet a = new FolderAnimationManager(this, false /* isOpening */).getAnimator();
a.addListener(new AnimatorListenerAdapter() {
@Override
@@ -956,6 +963,7 @@
View icon = (mCurrentDragView != null && mCurrentDragView.getTag() == info)
? mCurrentDragView : mContent.createNewView(info);
ArrayList<View> views = getIconsInReadingOrder();
+ info.rank = Utilities.boundToRange(info.rank, 0, views.size());
views.add(info.rank, icon);
mContent.arrangeChildren(views);
mItemsInvalidated = true;
diff --git a/src/com/android/launcher3/folder/FolderAnimationManager.java b/src/com/android/launcher3/folder/FolderAnimationManager.java
index f72e674..b83609e 100644
--- a/src/com/android/launcher3/folder/FolderAnimationManager.java
+++ b/src/com/android/launcher3/folder/FolderAnimationManager.java
@@ -21,7 +21,6 @@
import static com.android.launcher3.folder.ClippedFolderIconLayoutRule.MAX_NUM_ITEMS_IN_PREVIEW;
import static com.android.launcher3.graphics.IconShape.getShape;
import static com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound;
-import static com.android.launcher3.uioverrides.BackgroundBlurController.BACKGROUND_BLUR;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -47,7 +46,6 @@
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.PropertyResetListener;
import com.android.launcher3.dragndrop.DragLayer;
-import com.android.launcher3.uioverrides.BackgroundBlurController;
import com.android.launcher3.util.Themes;
import java.util.List;
@@ -222,14 +220,6 @@
Animator z = getAnimator(mFolder, View.TRANSLATION_Z, -mFolder.getElevation(), 0);
play(a, z, mIsOpening ? midDuration : 0, midDuration);
- BackgroundBlurController blurController = mLauncher.getBackgroundBlurController();
- int stateBackgroundBlur = mLauncher.getStateManager().getState()
- .getBackgroundBlurRadius(mLauncher);
- int folderBackgroundBlurAdjustment = blurController.getFolderBackgroundBlurAdjustment();
- play(a, ObjectAnimator.ofInt(blurController, BACKGROUND_BLUR, mIsOpening
- ? stateBackgroundBlur + folderBackgroundBlurAdjustment
- : stateBackgroundBlur));
-
// Store clip variables
CellLayout cellLayout = mContent.getCurrentCellLayout();
boolean folderClipChildren = mFolder.getClipChildren();
diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java
index ab1ff10..f0d18ae 100644
--- a/src/com/android/launcher3/folder/FolderIcon.java
+++ b/src/com/android/launcher3/folder/FolderIcon.java
@@ -63,6 +63,7 @@
import com.android.launcher3.dragndrop.BaseItemDragListener;
import com.android.launcher3.dragndrop.DragLayer;
import com.android.launcher3.dragndrop.DragView;
+import com.android.launcher3.dragndrop.DraggableView;
import com.android.launcher3.icons.DotRenderer;
import com.android.launcher3.touch.ItemClickHandler;
import com.android.launcher3.util.Executors;
@@ -78,7 +79,8 @@
/**
* An icon that can appear on in the workspace representing an {@link Folder}.
*/
-public class FolderIcon extends FrameLayout implements FolderListener, IconLabelDotView {
+public class FolderIcon extends FrameLayout implements FolderListener, IconLabelDotView,
+ DraggableView {
@Thunk ActivityContext mActivity;
@Thunk Folder mFolder;
@@ -230,6 +232,16 @@
mBackground.getBounds(outBounds);
}
+ @Override
+ public int getViewType() {
+ return DRAGGABLE_ICON;
+ }
+
+ @Override
+ public void getVisualDragBounds(Rect bounds) {
+ getPreviewBounds(bounds);
+ }
+
public float getBackgroundStrokeWidth() {
return mBackground.getStrokeWidth();
}
@@ -525,6 +537,10 @@
invalidate();
}
+ public boolean getIconVisible() {
+ return mBackgroundIsVisible;
+ }
+
public PreviewBackground getFolderBackground() {
return mBackground;
}
diff --git a/src/com/android/launcher3/graphics/DragPreviewProvider.java b/src/com/android/launcher3/graphics/DragPreviewProvider.java
index 94d30f6..ed9dfbb 100644
--- a/src/com/android/launcher3/graphics/DragPreviewProvider.java
+++ b/src/com/android/launcher3/graphics/DragPreviewProvider.java
@@ -30,14 +30,12 @@
import android.view.View;
import com.android.launcher3.BubbleTextView;
-import com.android.launcher3.FastBitmapDrawable;
import com.android.launcher3.Launcher;
import com.android.launcher3.R;
import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.folder.FolderIcon;
+import com.android.launcher3.dragndrop.DraggableView;
import com.android.launcher3.icons.BitmapRenderer;
import com.android.launcher3.widget.LauncherAppWidgetHostView;
-import com.android.launcher3.widget.PendingAppWidgetHostView;
import java.nio.ByteBuffer;
@@ -53,7 +51,7 @@
// The padding added to the drag view during the preview generation.
public final int previewPadding;
- protected final int blurSizeOutline;
+ public final int blurSizeOutline;
private OutlineGeneratorCallback mOutlineGeneratorCallback;
public Bitmap generatedDragOutline;
@@ -66,56 +64,25 @@
mView = view;
blurSizeOutline =
context.getResources().getDimensionPixelSize(R.dimen.blur_size_medium_outline);
-
- if (mView instanceof BubbleTextView) {
- Drawable d = ((BubbleTextView) mView).getIcon();
- Rect bounds = getDrawableBounds(d);
- previewPadding = blurSizeOutline - bounds.left - bounds.top;
- } else {
- previewPadding = blurSizeOutline;
- }
+ previewPadding = blurSizeOutline;
}
/**
* Draws the {@link #mView} into the given {@param destCanvas}.
*/
protected void drawDragView(Canvas destCanvas, float scale) {
- destCanvas.save();
+ int saveCount = destCanvas.save();
destCanvas.scale(scale, scale);
- if (mView instanceof BubbleTextView) {
- Drawable d = ((BubbleTextView) mView).getIcon();
- Rect bounds = getDrawableBounds(d);
- destCanvas.translate(blurSizeOutline / 2 - bounds.left,
- blurSizeOutline / 2 - bounds.top);
- if (d instanceof FastBitmapDrawable) {
- ((FastBitmapDrawable) d).setScale(1);
- }
- d.draw(destCanvas);
- } else {
- final Rect clipRect = mTempRect;
- mView.getDrawingRect(clipRect);
-
- boolean textVisible = false;
- if (mView instanceof FolderIcon) {
- // For FolderIcons the text can bleed into the icon area, and so we need to
- // hide the text completely (which can't be achieved by clipping).
- if (((FolderIcon) mView).getTextVisible()) {
- ((FolderIcon) mView).setTextVisible(false);
- textVisible = true;
- }
- }
- destCanvas.translate(-mView.getScrollX() + blurSizeOutline / 2,
- -mView.getScrollY() + blurSizeOutline / 2);
- destCanvas.clipRect(clipRect);
+ if (mView instanceof DraggableView) {
+ DraggableView dv = (DraggableView) mView;
+ dv.prepareDrawDragView();
+ dv.getVisualDragBounds(mTempRect);
+ destCanvas.translate(blurSizeOutline / 2 - mTempRect.left,
+ blurSizeOutline / 2 - mTempRect.top);
mView.draw(destCanvas);
-
- // Restore text visibility of FolderIcon if necessary
- if (textVisible) {
- ((FolderIcon) mView).setTextVisible(true);
- }
}
- destCanvas.restore();
+ destCanvas.restoreToCount(saveCount);
}
/**
@@ -123,28 +90,15 @@
* Responsibility for the bitmap is transferred to the caller.
*/
public Bitmap createDragBitmap() {
- int width = mView.getWidth();
- int height = mView.getHeight();
-
- if (mView instanceof BubbleTextView) {
- Drawable d = ((BubbleTextView) mView).getIcon();
- Rect bounds = getDrawableBounds(d);
- width = bounds.width();
- height = bounds.height();
- } else if (mView instanceof LauncherAppWidgetHostView) {
- float scale = ((LauncherAppWidgetHostView) mView).getScaleToFit();
- width = (int) (mView.getWidth() * scale);
- height = (int) (mView.getHeight() * scale);
-
- if (mView instanceof PendingAppWidgetHostView) {
- // Use hardware renderer as the icon for the pending app widget may be a hw bitmap
- return BitmapRenderer.createHardwareBitmap(width + blurSizeOutline,
- height + blurSizeOutline, (c) -> drawDragView(c, scale));
- } else {
- // Use software renderer for widgets as we know that they already work
- return BitmapRenderer.createSoftwareBitmap(width + blurSizeOutline,
- height + blurSizeOutline, (c) -> drawDragView(c, scale));
- }
+ int width = 0;
+ int height = 0;
+ if (mView instanceof DraggableView) {
+ ((DraggableView) mView).getVisualDragBounds(mTempRect);
+ width = mTempRect.width();
+ height = mTempRect.height();
+ } else {
+ width = mView.getWidth();
+ height = mView.getHeight();
}
return BitmapRenderer.createHardwareBitmap(width + blurSizeOutline,
diff --git a/src/com/android/launcher3/graphics/WorkspaceAndHotseatScrim.java b/src/com/android/launcher3/graphics/WorkspaceAndHotseatScrim.java
index 83349bc..7b7ab5e 100644
--- a/src/com/android/launcher3/graphics/WorkspaceAndHotseatScrim.java
+++ b/src/com/android/launcher3/graphics/WorkspaceAndHotseatScrim.java
@@ -19,6 +19,7 @@
import static android.content.Intent.ACTION_SCREEN_OFF;
import static android.content.Intent.ACTION_USER_PRESENT;
+import static com.android.launcher3.config.FeatureFlags.KEYGUARD_ANIMATION;
import static com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound;
import android.animation.ObjectAnimator;
@@ -39,12 +40,14 @@
import android.util.DisplayMetrics;
import android.util.FloatProperty;
import android.view.View;
+import android.view.WindowInsets;
import androidx.core.graphics.ColorUtils;
import com.android.launcher3.CellLayout;
import com.android.launcher3.R;
import com.android.launcher3.ResourceUtils;
+import com.android.launcher3.Utilities;
import com.android.launcher3.Workspace;
import com.android.launcher3.uioverrides.WallpaperColorInfo;
import com.android.launcher3.util.Themes;
@@ -81,6 +84,10 @@
}
};
+ /**
+ * Receiver used to get a signal that the user unlocked their device.
+ * @see KEYGUARD_ANIMATION For proper signal.
+ */
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -164,8 +171,10 @@
mSysUiAnimMultiplier = 0;
reapplySysUiAlphaNoInvalidate();
- animateToSysuiMultiplier(1, 600,
- mLauncher.getWindow().getTransitionBackgroundFadeDuration());
+ ObjectAnimator oa = createSysuiMultiplierAnim(1);
+ oa.setDuration(600);
+ oa.setStartDelay(mLauncher.getWindow().getTransitionBackgroundFadeDuration());
+ oa.start();
mAnimateScrimOnNextDraw = false;
}
@@ -178,26 +187,42 @@
}
}
- public void animateToSysuiMultiplier(float toMultiplier, long duration,
- long startDelay) {
- ObjectAnimator anim = ObjectAnimator.ofFloat(this, SYSUI_ANIM_MULTIPLIER, toMultiplier);
+ /**
+ * @return an ObjectAnimator that controls the fade in/out of the sys ui scrim.
+ */
+ public ObjectAnimator createSysuiMultiplierAnim(float... values) {
+ ObjectAnimator anim = ObjectAnimator.ofFloat(this, SYSUI_ANIM_MULTIPLIER, values);
anim.setAutoCancel(true);
- anim.setDuration(duration);
- anim.setStartDelay(startDelay);
- anim.start();
+ return anim;
}
- public void onInsetsChanged(Rect insets) {
- mDrawTopScrim = mTopScrim != null && insets.top > 0;
- mDrawBottomScrim = mBottomMask != null &&
- !mLauncher.getDeviceProfile().isVerticalBarLayout();
+ /**
+ * Determines whether to draw the top and/or bottom scrim based on new insets.
+ */
+ public void onInsetsChanged(Rect insets, boolean allowSysuiScrims) {
+ mDrawTopScrim = allowSysuiScrims
+ && mTopScrim != null
+ && insets.top > 0;
+ mDrawBottomScrim = allowSysuiScrims
+ && mBottomMask != null
+ && !mLauncher.getDeviceProfile().isVerticalBarLayout()
+ && hasBottomNavButtons();
+ }
+
+ private boolean hasBottomNavButtons() {
+ if (Utilities.ATLEAST_Q && mLauncher.getRootView() != null
+ && mLauncher.getRootView().getRootWindowInsets() != null) {
+ WindowInsets windowInsets = mLauncher.getRootView().getRootWindowInsets();
+ return windowInsets.getTappableElementInsets().bottom > 0;
+ }
+ return true;
}
@Override
public void onViewAttachedToWindow(View view) {
super.onViewAttachedToWindow(view);
- if (mTopScrim != null) {
+ if (!KEYGUARD_ANIMATION.get() && mTopScrim != null) {
IntentFilter filter = new IntentFilter(ACTION_SCREEN_OFF);
filter.addAction(ACTION_USER_PRESENT); // When the device wakes up + keyguard is gone
mRoot.getContext().registerReceiver(mReceiver, filter);
@@ -207,7 +232,7 @@
@Override
public void onViewDetachedFromWindow(View view) {
super.onViewDetachedFromWindow(view);
- if (mTopScrim != null) {
+ if (!KEYGUARD_ANIMATION.get() && mTopScrim != null) {
mRoot.getContext().unregisterReceiver(mReceiver);
}
}
@@ -229,14 +254,6 @@
}
}
- public void hideSysUiScrim(boolean hideSysUiScrim) {
- mHideSysUiScrim = hideSysUiScrim || (mTopScrim == null);
- if (!hideSysUiScrim) {
- mAnimateScrimOnNextDraw = true;
- }
- invalidate();
- }
-
private void setSysUiProgress(float progress) {
if (progress != mSysUiProgress) {
mSysUiProgress = progress;
diff --git a/src/com/android/launcher3/logging/LoggerUtils.java b/src/com/android/launcher3/logging/LoggerUtils.java
index a9d10d7..1b70fde 100644
--- a/src/com/android/launcher3/logging/LoggerUtils.java
+++ b/src/com/android/launcher3/logging/LoggerUtils.java
@@ -15,8 +15,6 @@
*/
package com.android.launcher3.logging;
-import static com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType.NAVBAR;
-
import android.util.ArrayMap;
import android.util.SparseArray;
import android.view.View;
@@ -27,12 +25,9 @@
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.userevent.nano.LauncherLogExtensions.TargetExtension;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
-import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
-import com.android.launcher3.userevent.nano.LauncherLogProto.ControlType;
import com.android.launcher3.userevent.nano.LauncherLogProto.ItemType;
import com.android.launcher3.userevent.nano.LauncherLogProto.LauncherEvent;
import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
-import com.android.launcher3.userevent.nano.LauncherLogProto.TipType;
import com.android.launcher3.util.InstantAppResolver;
import java.lang.reflect.Field;
@@ -70,93 +65,6 @@
return result != null ? result : UNKNOWN;
}
- public static String getActionStr(Action action) {
- String str = "";
- switch (action.type) {
- case Action.Type.TOUCH:
- str += getFieldName(action.touch, Action.Touch.class);
- if (action.touch == Action.Touch.SWIPE || action.touch == Action.Touch.FLING) {
- str += " direction=" + getFieldName(action.dir, Action.Direction.class);
- }
- break;
- case Action.Type.COMMAND:
- str += getFieldName(action.command, Action.Command.class);
- break;
- default: return getFieldName(action.type, Action.Type.class);
- }
- if (action.touch == Action.Touch.SWIPE || action.touch == Action.Touch.FLING ||
- (action.command == Action.Command.BACK && action.dir != Action.Direction.NONE)) {
- str += " direction=" + getFieldName(action.dir, Action.Direction.class);
- }
- return str;
- }
-
- public static String getTargetStr(Target t) {
- if (t == null) {
- return "";
- }
- String str = "";
- switch (t.type) {
- case Target.Type.ITEM:
- str = getItemStr(t);
- break;
- case Target.Type.CONTROL:
- str = getFieldName(t.controlType, ControlType.class);
- break;
- case Target.Type.CONTAINER:
- str = getFieldName(t.containerType, ContainerType.class);
- if (t.containerType == ContainerType.WORKSPACE ||
- t.containerType == ContainerType.HOTSEAT ||
- t.containerType == NAVBAR) {
- str += " id=" + t.pageIndex;
- } else if (t.containerType == ContainerType.FOLDER) {
- str += " grid(" + t.gridX + "," + t.gridY + ")";
- }
- break;
- default:
- str += "UNKNOWN TARGET TYPE";
- }
-
- if (t.spanX != 1 || t.spanY != 1) {
- str += " span(" + t.spanX + "," + t.spanY + ")";
- }
-
- if (t.tipType != TipType.DEFAULT_NONE) {
- str += " " + getFieldName(t.tipType, TipType.class);
- }
-
- return str;
- }
-
- private static String getItemStr(Target t) {
- String typeStr = getFieldName(t.itemType, ItemType.class);
- if (t.packageNameHash != 0) {
- typeStr += ", packageHash=" + t.packageNameHash;
- }
- if (t.componentHash != 0) {
- typeStr += ", componentHash=" + t.componentHash;
- }
- if (t.intentHash != 0) {
- typeStr += ", intentHash=" + t.intentHash;
- }
- if (t.itemType == ItemType.FOLDER_ICON) {
- typeStr += ", grid(" + t.gridX + "," + t.gridY + ")";
- } else if ((t.packageNameHash != 0 || t.componentHash != 0 || t.intentHash != 0)
- && t.itemType != ItemType.TASK) {
- typeStr +=
- ", isWorkApp=" + t.isWorkApp + ", predictiveRank=" + t.predictedRank + ", grid("
- + t.gridX + "," + t.gridY + "), span(" + t.spanX + "," + t.spanY
- + "), pageIdx=" + t.pageIndex;
- }
- if (t.searchQueryLength != 0) {
- typeStr += ", searchQueryLength=" + t.searchQueryLength;
- }
- if (t.itemType == ItemType.TASK) {
- typeStr += ", pageIdx=" + t.pageIndex;
- }
- return typeStr;
- }
-
public static Target newItemTarget(int itemType) {
Target t = newTarget(Target.Type.ITEM);
t.itemType = itemType;
diff --git a/src/com/android/launcher3/logging/UserEventDispatcher.java b/src/com/android/launcher3/logging/UserEventDispatcher.java
index 3770d17..89077ee 100644
--- a/src/com/android/launcher3/logging/UserEventDispatcher.java
+++ b/src/com/android/launcher3/logging/UserEventDispatcher.java
@@ -143,7 +143,7 @@
fillIntentInfo(itemTarget, intent, userHandle);
}
LauncherEvent event = newLauncherEvent(action, targets);
- ItemInfo info = (ItemInfo) v.getTag();
+ ItemInfo info = v == null ? null : (ItemInfo) v.getTag();
if (info != null && Utilities.IS_DEBUG_DEVICE && FeatureFlags.ENABLE_HYBRID_HOTSEAT.get()) {
FileLog.d(TAG, "appLaunch: packageName:" + info.getTargetComponent().getPackageName()
+ ",isWorkApp:" + (info.user != null && !Process.myUserHandle().equals(
diff --git a/src/com/android/launcher3/model/BgDataModel.java b/src/com/android/launcher3/model/BgDataModel.java
index 32fce0b..fdfcef1 100644
--- a/src/com/android/launcher3/model/BgDataModel.java
+++ b/src/com/android/launcher3/model/BgDataModel.java
@@ -424,7 +424,8 @@
// Now add the new shortcuts to the map.
for (ShortcutInfo shortcut : shortcuts) {
boolean shouldShowInContainer = shortcut.isEnabled()
- && (shortcut.isDeclaredInManifest() || shortcut.isDynamic());
+ && (shortcut.isDeclaredInManifest() || shortcut.isDynamic())
+ && shortcut.getActivity() != null;
if (shouldShowInContainer) {
ComponentKey targetComponent
= new ComponentKey(shortcut.getActivity(), shortcut.getUserHandle());
diff --git a/src/com/android/launcher3/model/GridSizeMigrationTask.java b/src/com/android/launcher3/model/GridSizeMigrationTask.java
index 3ba740d..a084600 100644
--- a/src/com/android/launcher3/model/GridSizeMigrationTask.java
+++ b/src/com/android/launcher3/model/GridSizeMigrationTask.java
@@ -936,8 +936,8 @@
boolean dbChanged = false;
if (migrateForPreview) {
- copyTable(transaction.getDb(), Favorites.TABLE_NAME, Favorites.PREVIEW_TABLE_NAME,
- context);
+ copyTable(transaction.getDb(), Favorites.TABLE_NAME, transaction.getDb(),
+ Favorites.PREVIEW_TABLE_NAME, context);
}
GridBackupTable backupTable = new GridBackupTable(context, transaction.getDb(),
@@ -950,10 +950,11 @@
HashSet<String> validPackages = getValidPackages(context);
// Hotseat.
- if (srcHotseatCount != idp.numHotseatIcons) {
- // Migrate hotseat.
- dbChanged = new GridSizeMigrationTask(context, transaction.getDb(), validPackages,
- migrateForPreview, srcHotseatCount, idp.numHotseatIcons).migrateHotseat();
+ if (srcHotseatCount != idp.numHotseatIcons
+ && new GridSizeMigrationTask(context, transaction.getDb(), validPackages,
+ migrateForPreview, srcHotseatCount,
+ idp.numHotseatIcons).migrateHotseat()) {
+ dbChanged = true;
}
// Grid size
diff --git a/src/com/android/launcher3/model/GridSizeMigrationTaskV2.java b/src/com/android/launcher3/model/GridSizeMigrationTaskV2.java
index 197b29c..0bdccfa 100644
--- a/src/com/android/launcher3/model/GridSizeMigrationTaskV2.java
+++ b/src/com/android/launcher3/model/GridSizeMigrationTaskV2.java
@@ -16,9 +16,46 @@
package com.android.launcher3.model;
+import static com.android.launcher3.Utilities.getPointString;
+import static com.android.launcher3.provider.LauncherDbUtils.dropTable;
+
+import android.content.ComponentName;
+import android.content.ContentValues;
import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.database.Cursor;
+import android.database.DatabaseUtils;
+import android.database.sqlite.SQLiteDatabase;
+import android.graphics.Point;
+import android.text.TextUtils;
+import android.util.Log;
+
+import androidx.annotation.VisibleForTesting;
import com.android.launcher3.InvariantDeviceProfile;
+import com.android.launcher3.ItemInfo;
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.LauncherAppWidgetProviderInfo;
+import com.android.launcher3.LauncherSettings;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.graphics.LauncherPreviewRenderer;
+import com.android.launcher3.pm.InstallSessionHelper;
+import com.android.launcher3.provider.LauncherDbUtils.SQLiteTransaction;
+import com.android.launcher3.util.GridOccupancy;
+import com.android.launcher3.util.IntArray;
+import com.android.launcher3.widget.WidgetManagerHelper;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
/**
* This class takes care of shrinking the workspace (by maximum of one row and one column), as a
@@ -26,14 +63,63 @@
*/
public class GridSizeMigrationTaskV2 {
- private GridSizeMigrationTaskV2(Context context) {
+ public static final String KEY_MIGRATION_SRC_WORKSPACE_SIZE = "migration_src_workspace_size";
+ public static final String KEY_MIGRATION_SRC_HOTSEAT_COUNT = "migration_src_hotseat_count";
+ private static final String TAG = "GridSizeMigrationTaskV2";
+ private static final boolean DEBUG = true;
+
+ private final Context mContext;
+ private final SQLiteDatabase mDb;
+ private final DbReader mSrcReader;
+ private final DbReader mDestReader;
+
+ private final List<DbEntry> mHotseatItems;
+ private final List<DbEntry> mWorkspaceItems;
+
+ private final List<DbEntry> mHotseatDiff;
+ private final List<DbEntry> mWorkspaceDiff;
+
+ private final int mDestHotseatSize;
+ private final int mTrgX, mTrgY;
+
+ @VisibleForTesting
+ protected GridSizeMigrationTaskV2(Context context, SQLiteDatabase db, DbReader srcReader,
+ DbReader destReader, int destHotseatSize, Point targetSize) {
+ mContext = context;
+ mDb = db;
+ mSrcReader = srcReader;
+ mDestReader = destReader;
+
+ mHotseatItems = destReader.loadHotseatEntries();
+ mWorkspaceItems = destReader.loadAllWorkspaceEntries();
+
+ mHotseatDiff = calcDiff(mSrcReader.loadHotseatEntries(), mHotseatItems);
+ mWorkspaceDiff = calcDiff(mSrcReader.loadAllWorkspaceEntries(), mWorkspaceItems);
+ mDestHotseatSize = destHotseatSize;
+
+ mTrgX = targetSize.x;
+ mTrgY = targetSize.y;
+ }
+
+ /**
+ * Check given a new IDP, if migration is necessary.
+ */
+ public static boolean needsToMigrate(Context context, InvariantDeviceProfile idp) {
+ SharedPreferences prefs = Utilities.getPrefs(context);
+ String gridSizeString = getPointString(idp.numColumns, idp.numRows);
+
+ return !gridSizeString.equals(prefs.getString(KEY_MIGRATION_SRC_WORKSPACE_SIZE, ""))
+ || idp.numHotseatIcons != prefs.getInt(KEY_MIGRATION_SRC_HOTSEAT_COUNT,
+ idp.numHotseatIcons);
}
/** See {@link #migrateGridIfNeeded(Context, InvariantDeviceProfile)} */
public static boolean migrateGridIfNeeded(Context context) {
- // To be implemented.
- return true;
+ if (context instanceof LauncherPreviewRenderer.PreviewContext) {
+ return true;
+ }
+ return migrateGridIfNeeded(context, null);
}
/**
@@ -43,7 +129,608 @@
* @return false if the migration failed.
*/
public static boolean migrateGridIfNeeded(Context context, InvariantDeviceProfile idp) {
- // To be implemented.
+ boolean migrateForPreview = idp != null;
+ if (!migrateForPreview) {
+ idp = LauncherAppState.getIDP(context);
+ }
+
+ if (!needsToMigrate(context, idp)) {
+ return true;
+ }
+
+ SharedPreferences prefs = Utilities.getPrefs(context);
+ String gridSizeString = getPointString(idp.numColumns, idp.numRows);
+
+ if (gridSizeString.equals(prefs.getString(KEY_MIGRATION_SRC_WORKSPACE_SIZE, ""))
+ && idp.numHotseatIcons == prefs.getInt(KEY_MIGRATION_SRC_HOTSEAT_COUNT,
+ idp.numHotseatIcons)) {
+ // Skip if workspace and hotseat sizes have not changed.
+ return true;
+ }
+
+ HashSet<String> validPackages = getValidPackages(context);
+ int srcHotseatCount = prefs.getInt(KEY_MIGRATION_SRC_HOTSEAT_COUNT, idp.numHotseatIcons);
+
+ if (!LauncherSettings.Settings.call(
+ context.getContentResolver(),
+ LauncherSettings.Settings.METHOD_UPDATE_CURRENT_OPEN_HELPER).getBoolean(
+ LauncherSettings.Settings.EXTRA_VALUE)) {
+ return false;
+ }
+
+ long migrationStartTime = System.currentTimeMillis();
+ try (SQLiteTransaction t = (SQLiteTransaction) LauncherSettings.Settings.call(
+ context.getContentResolver(),
+ LauncherSettings.Settings.METHOD_NEW_TRANSACTION).getBinder(
+ LauncherSettings.Settings.EXTRA_VALUE)) {
+
+ DbReader srcReader = new DbReader(t.getDb(), LauncherSettings.Favorites.TMP_TABLE,
+ context, validPackages, srcHotseatCount);
+ DbReader destReader = new DbReader(t.getDb(), LauncherSettings.Favorites.TABLE_NAME,
+ context, validPackages, idp.numHotseatIcons);
+
+ Point targetSize = new Point(idp.numColumns, idp.numRows);
+ GridSizeMigrationTaskV2 task = new GridSizeMigrationTaskV2(context, t.getDb(),
+ srcReader, destReader, idp.numHotseatIcons, targetSize);
+ task.migrate();
+
+ dropTable(t.getDb(), LauncherSettings.Favorites.TMP_TABLE);
+
+ t.commit();
+ return true;
+ } catch (Exception e) {
+ Log.e(TAG, "Error during grid migration", e);
+
+ return false;
+ } finally {
+ Log.v(TAG, "Workspace migration completed in "
+ + (System.currentTimeMillis() - migrationStartTime));
+
+ // Save current configuration, so that the migration does not run again.
+ prefs.edit()
+ .putString(KEY_MIGRATION_SRC_WORKSPACE_SIZE, gridSizeString)
+ .putInt(KEY_MIGRATION_SRC_HOTSEAT_COUNT, idp.numHotseatIcons)
+ .apply();
+ }
+ }
+
+ @VisibleForTesting
+ protected boolean migrate() {
+ if (mHotseatDiff.isEmpty() && mWorkspaceDiff.isEmpty()) {
+ return false;
+ }
+
+ // Migrate hotseat
+ HotseatPlacementSolution hotseatSolution = new HotseatPlacementSolution(mDb, mSrcReader,
+ mContext, mDestHotseatSize, mHotseatItems, mHotseatDiff);
+ hotseatSolution.find();
+
+ // Sort the items by the reading order.
+ Collections.sort(mWorkspaceDiff);
+
+ // Migrate workspace.
+ for (int screenId = 0; screenId <= mDestReader.mLastScreenId; screenId++) {
+ if (DEBUG) {
+ Log.d(TAG, "Migrating " + screenId);
+ }
+ List<DbEntry> entries = mDestReader.loadWorkspaceEntries(screenId);
+ GridPlacementSolution workspaceSolution = new GridPlacementSolution(mDb, mSrcReader,
+ mContext, entries, screenId, mTrgX, mTrgY, mWorkspaceDiff);
+ workspaceSolution.find();
+ if (mWorkspaceDiff.isEmpty()) {
+ break;
+ }
+ }
+
+ int screenId = mDestReader.mLastScreenId + 1;
+ while (!mWorkspaceDiff.isEmpty()) {
+ GridPlacementSolution workspaceSolution = new GridPlacementSolution(mDb, mSrcReader,
+ mContext, new ArrayList<>(), screenId, mTrgX, mTrgY, mWorkspaceDiff);
+ workspaceSolution.find();
+ screenId++;
+ }
return true;
}
+
+ /** Return what's in the src but not in the dest */
+ private static List<DbEntry> calcDiff(List<DbEntry> src, List<DbEntry> dest) {
+ Set<String> destSet = dest.parallelStream().map(DbEntry::getIntentStr).collect(
+ Collectors.toSet());
+ List<DbEntry> diff = new ArrayList<>();
+ for (DbEntry entry : src) {
+ if (!destSet.contains(entry.mIntent)) {
+ diff.add(entry);
+ }
+ }
+ return diff;
+ }
+
+ private static void insertEntryInDb(SQLiteDatabase db, Context context,
+ ArrayList<DbEntry> entriesFromSrcDb, DbEntry entry) {
+ int id = -1;
+ switch (entry.itemType) {
+ case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
+ case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT:
+ case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION: {
+ for (DbEntry e : entriesFromSrcDb) {
+ if (TextUtils.equals(e.mIntent, entry.mIntent)) {
+ id = e.id;
+ }
+ }
+
+ break;
+ }
+ case LauncherSettings.Favorites.ITEM_TYPE_FOLDER: {
+ for (DbEntry e : entriesFromSrcDb) {
+ if (e.mFolderItems.size() == entry.mFolderItems.size()
+ && e.mFolderItems.containsAll(entry.mFolderItems)) {
+ id = e.id;
+ }
+ }
+ break;
+ }
+ case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
+ case LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET: {
+ for (DbEntry e : entriesFromSrcDb) {
+ if (TextUtils.equals(e.mProvider, entry.mProvider)) {
+ id = e.id;
+ break;
+ }
+ }
+ break;
+ }
+ default:
+ return;
+ }
+
+ Cursor c = db.query(LauncherSettings.Favorites.TMP_TABLE, null,
+ LauncherSettings.Favorites._ID + " = '" + id + "'", null, null, null, null);
+
+ while (c.moveToNext()) {
+ ContentValues values = new ContentValues();
+ DatabaseUtils.cursorRowToContentValues(c, values);
+ entry.updateContentValues(values);
+ values.put(LauncherSettings.Favorites._ID,
+ LauncherSettings.Settings.call(context.getContentResolver(),
+ LauncherSettings.Settings.METHOD_NEW_ITEM_ID).getInt(
+ LauncherSettings.Settings.EXTRA_VALUE));
+ db.insert(LauncherSettings.Favorites.TABLE_NAME, null, values);
+ }
+ c.close();
+ }
+
+ private static void removeEntryFromDb(SQLiteDatabase db, IntArray entryId) {
+ db.delete(LauncherSettings.Favorites.TABLE_NAME, Utilities.createDbSelectionQuery(
+ LauncherSettings.Favorites._ID, entryId), null);
+ }
+
+ private static HashSet<String> getValidPackages(Context context) {
+ // Initialize list of valid packages. This contain all the packages which are already on
+ // the device and packages which are being installed. Any item which doesn't belong to
+ // this set is removed.
+ // Since the loader removes such items anyway, removing these items here doesn't cause
+ // any extra data loss and gives us more free space on the grid for better migration.
+ HashSet<String> validPackages = new HashSet<>();
+ for (PackageInfo info : context.getPackageManager()
+ .getInstalledPackages(PackageManager.GET_UNINSTALLED_PACKAGES)) {
+ validPackages.add(info.packageName);
+ }
+ InstallSessionHelper.INSTANCE.get(context)
+ .getActiveSessions().keySet()
+ .forEach(packageUserKey -> validPackages.add(packageUserKey.mPackageName));
+ return validPackages;
+ }
+
+ protected static class GridPlacementSolution {
+
+ private final SQLiteDatabase mDb;
+ private final DbReader mSrcReader;
+ private final Context mContext;
+ private final GridOccupancy mOccupied;
+ private final int mScreenId;
+ private final int mTrgX;
+ private final int mTrgY;
+ private final List<DbEntry> mItemsToPlace;
+
+ private int mNextStartX;
+ private int mNextStartY;
+
+ GridPlacementSolution(SQLiteDatabase db, DbReader srcReader, Context context,
+ List<DbEntry> placedWorkspaceItems, int screenId, int trgX,
+ int trgY, List<DbEntry> itemsToPlace) {
+ mDb = db;
+ mSrcReader = srcReader;
+ mContext = context;
+ mOccupied = new GridOccupancy(trgX, trgY);
+ mScreenId = screenId;
+ mTrgX = trgX;
+ mTrgY = trgY;
+ mNextStartX = 0;
+ mNextStartY = mTrgY - 1;
+ for (DbEntry entry : placedWorkspaceItems) {
+ mOccupied.markCells(entry, true);
+ }
+ mItemsToPlace = itemsToPlace;
+ }
+
+ public void find() {
+ Iterator<DbEntry> iterator = mItemsToPlace.iterator();
+ while (iterator.hasNext()) {
+ final DbEntry entry = iterator.next();
+ if (entry.minSpanX > mTrgX || entry.minSpanY > mTrgY) {
+ iterator.remove();
+ continue;
+ }
+ if (findPlacement(entry)) {
+ insertEntryInDb(mDb, mContext, mSrcReader.mWorkspaceEntries, entry);
+ iterator.remove();
+ }
+ }
+ }
+
+ private boolean findPlacement(DbEntry entry) {
+ for (int y = mNextStartY; y >= 0; y--) {
+ for (int x = mNextStartX; x < mTrgX; x++) {
+ boolean fits = mOccupied.isRegionVacant(x, y, entry.spanX, entry.spanY);
+ boolean minFits = mOccupied.isRegionVacant(x, y, entry.minSpanX,
+ entry.minSpanY);
+ if (minFits) {
+ entry.spanX = entry.minSpanX;
+ entry.spanY = entry.minSpanY;
+ }
+ if (fits || minFits) {
+ entry.screenId = mScreenId;
+ entry.cellX = x;
+ entry.cellY = y;
+ mOccupied.markCells(entry, true);
+ mNextStartX = x + entry.spanX;
+ mNextStartY = y;
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+ }
+
+ protected static class HotseatPlacementSolution {
+
+ private final SQLiteDatabase mDb;
+ private final DbReader mSrcReader;
+ private final Context mContext;
+ private final HotseatOccupancy mOccupied;
+ private final List<DbEntry> mItemsToPlace;
+
+ HotseatPlacementSolution(SQLiteDatabase db, DbReader srcReader, Context context,
+ int hotseatSize, List<DbEntry> placedHotseatItems, List<DbEntry> itemsToPlace) {
+ mDb = db;
+ mSrcReader = srcReader;
+ mContext = context;
+ mOccupied = new HotseatOccupancy(hotseatSize);
+ for (DbEntry entry : placedHotseatItems) {
+ mOccupied.markCells(entry, true);
+ }
+ mItemsToPlace = itemsToPlace;
+ }
+
+ public void find() {
+ for (int i = 0; i < mOccupied.mCells.length; i++) {
+ if (!mOccupied.mCells[i] && !mItemsToPlace.isEmpty()) {
+ DbEntry entry = mItemsToPlace.remove(0);
+ entry.screenId = i;
+ // These values does not affect the item position, but we should set them
+ // to something other than -1.
+ entry.cellX = i;
+ entry.cellY = 0;
+ insertEntryInDb(mDb, mContext, mSrcReader.mHotseatEntries, entry);
+ mOccupied.markCells(entry, true);
+ }
+ }
+ }
+
+ private class HotseatOccupancy {
+
+ private final boolean[] mCells;
+
+ private HotseatOccupancy(int hotseatSize) {
+ mCells = new boolean[hotseatSize];
+ }
+
+ private void markCells(ItemInfo item, boolean value) {
+ mCells[item.screenId] = value;
+ }
+ }
+ }
+
+ protected static class DbReader {
+
+ private final SQLiteDatabase mDb;
+ private final String mTableName;
+ private final Context mContext;
+ private final HashSet<String> mValidPackages;
+ private final int mHotseatSize;
+ private int mLastScreenId = -1;
+
+ private final ArrayList<DbEntry> mHotseatEntries = new ArrayList<>();
+ private final ArrayList<DbEntry> mWorkspaceEntries = new ArrayList<>();
+
+ DbReader(SQLiteDatabase db, String tableName, Context context,
+ HashSet<String> validPackages, int hotseatSize) {
+ mDb = db;
+ mTableName = tableName;
+ mContext = context;
+ mValidPackages = validPackages;
+ mHotseatSize = hotseatSize;
+ }
+
+ protected ArrayList<DbEntry> loadHotseatEntries() {
+ Cursor c = queryWorkspace(
+ new String[]{
+ LauncherSettings.Favorites._ID, // 0
+ LauncherSettings.Favorites.ITEM_TYPE, // 1
+ LauncherSettings.Favorites.INTENT, // 2
+ LauncherSettings.Favorites.SCREEN}, // 3
+ LauncherSettings.Favorites.CONTAINER + " = "
+ + LauncherSettings.Favorites.CONTAINER_HOTSEAT);
+
+ final int indexId = c.getColumnIndexOrThrow(LauncherSettings.Favorites._ID);
+ final int indexItemType = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ITEM_TYPE);
+ final int indexIntent = c.getColumnIndexOrThrow(LauncherSettings.Favorites.INTENT);
+ final int indexScreen = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SCREEN);
+
+ IntArray entriesToRemove = new IntArray();
+ while (c.moveToNext()) {
+ DbEntry entry = new DbEntry();
+ entry.id = c.getInt(indexId);
+ entry.itemType = c.getInt(indexItemType);
+ entry.screenId = c.getInt(indexScreen);
+
+ if (entry.screenId >= mHotseatSize) {
+ entriesToRemove.add(entry.id);
+ continue;
+ }
+
+ try {
+ // calculate weight
+ switch (entry.itemType) {
+ case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
+ case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT:
+ case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION: {
+ entry.mIntent = c.getString(indexIntent);
+ verifyIntent(c.getString(indexIntent));
+ break;
+ }
+ case LauncherSettings.Favorites.ITEM_TYPE_FOLDER: {
+ int total = getFolderItemsCount(entry);
+ if (total == 0) {
+ throw new Exception("Folder is empty");
+ }
+ break;
+ }
+ default:
+ throw new Exception("Invalid item type");
+ }
+ } catch (Exception e) {
+ if (DEBUG) {
+ Log.d(TAG, "Removing item " + entry.id, e);
+ }
+ entriesToRemove.add(entry.id);
+ continue;
+ }
+ mHotseatEntries.add(entry);
+ }
+ removeEntryFromDb(mDb, entriesToRemove);
+ c.close();
+ return mHotseatEntries;
+ }
+
+ protected ArrayList<DbEntry> loadAllWorkspaceEntries() {
+ Cursor c = queryWorkspace(
+ new String[]{
+ LauncherSettings.Favorites._ID, // 0
+ LauncherSettings.Favorites.ITEM_TYPE, // 1
+ LauncherSettings.Favorites.SCREEN, // 2
+ LauncherSettings.Favorites.CELLX, // 3
+ LauncherSettings.Favorites.CELLY, // 4
+ LauncherSettings.Favorites.SPANX, // 5
+ LauncherSettings.Favorites.SPANY, // 6
+ LauncherSettings.Favorites.INTENT, // 7
+ LauncherSettings.Favorites.APPWIDGET_PROVIDER, // 8
+ LauncherSettings.Favorites.APPWIDGET_ID}, // 9
+ LauncherSettings.Favorites.CONTAINER + " = "
+ + LauncherSettings.Favorites.CONTAINER_DESKTOP);
+ return loadWorkspaceEntries(c);
+ }
+
+ protected ArrayList<DbEntry> loadWorkspaceEntries(int screen) {
+ Cursor c = queryWorkspace(
+ new String[]{
+ LauncherSettings.Favorites._ID, // 0
+ LauncherSettings.Favorites.ITEM_TYPE, // 1
+ LauncherSettings.Favorites.SCREEN, // 2
+ LauncherSettings.Favorites.CELLX, // 3
+ LauncherSettings.Favorites.CELLY, // 4
+ LauncherSettings.Favorites.SPANX, // 5
+ LauncherSettings.Favorites.SPANY, // 6
+ LauncherSettings.Favorites.INTENT, // 7
+ LauncherSettings.Favorites.APPWIDGET_PROVIDER, // 8
+ LauncherSettings.Favorites.APPWIDGET_ID}, // 9
+ LauncherSettings.Favorites.CONTAINER + " = "
+ + LauncherSettings.Favorites.CONTAINER_DESKTOP
+ + " AND " + LauncherSettings.Favorites.SCREEN + " = " + screen);
+ return loadWorkspaceEntries(c);
+ }
+
+ private ArrayList<DbEntry> loadWorkspaceEntries(Cursor c) {
+ final int indexId = c.getColumnIndexOrThrow(LauncherSettings.Favorites._ID);
+ final int indexItemType = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ITEM_TYPE);
+ final int indexScreen = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SCREEN);
+ final int indexCellX = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLX);
+ final int indexCellY = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLY);
+ final int indexSpanX = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SPANX);
+ final int indexSpanY = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SPANY);
+ final int indexIntent = c.getColumnIndexOrThrow(LauncherSettings.Favorites.INTENT);
+ final int indexAppWidgetProvider = c.getColumnIndexOrThrow(
+ LauncherSettings.Favorites.APPWIDGET_PROVIDER);
+ final int indexAppWidgetId = c.getColumnIndexOrThrow(
+ LauncherSettings.Favorites.APPWIDGET_ID);
+
+ IntArray entriesToRemove = new IntArray();
+ WidgetManagerHelper widgetManagerHelper = new WidgetManagerHelper(mContext);
+ while (c.moveToNext()) {
+ DbEntry entry = new DbEntry();
+ entry.id = c.getInt(indexId);
+ entry.itemType = c.getInt(indexItemType);
+ entry.screenId = c.getInt(indexScreen);
+ mLastScreenId = Math.max(mLastScreenId, entry.screenId);
+ entry.cellX = c.getInt(indexCellX);
+ entry.cellY = c.getInt(indexCellY);
+ entry.spanX = c.getInt(indexSpanX);
+ entry.spanY = c.getInt(indexSpanY);
+
+ try {
+ // calculate weight
+ switch (entry.itemType) {
+ case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
+ case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT:
+ case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION: {
+ entry.mIntent = c.getString(indexIntent);
+ verifyIntent(entry.mIntent);
+ break;
+ }
+ case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET: {
+ entry.mProvider = c.getString(indexAppWidgetProvider);
+ ComponentName cn = ComponentName.unflattenFromString(entry.mProvider);
+ verifyPackage(cn.getPackageName());
+
+ int widgetId = c.getInt(indexAppWidgetId);
+ LauncherAppWidgetProviderInfo pInfo =
+ widgetManagerHelper.getLauncherAppWidgetInfo(widgetId);
+ Point spans = null;
+ if (pInfo != null) {
+ spans = pInfo.getMinSpans();
+ }
+ if (spans != null) {
+ entry.minSpanX = spans.x > 0 ? spans.x : entry.spanX;
+ entry.minSpanY = spans.y > 0 ? spans.y : entry.spanY;
+ } else {
+ // Assume that the widget be resized down to 2x2
+ entry.minSpanX = entry.minSpanY = 2;
+ }
+
+ break;
+ }
+ case LauncherSettings.Favorites.ITEM_TYPE_FOLDER: {
+ int total = getFolderItemsCount(entry);
+ if (total == 0) {
+ throw new Exception("Folder is empty");
+ }
+ break;
+ }
+ default:
+ throw new Exception("Invalid item type");
+ }
+ } catch (Exception e) {
+ if (DEBUG) {
+ Log.d(TAG, "Removing item " + entry.id, e);
+ }
+ entriesToRemove.add(entry.id);
+ continue;
+ }
+ mWorkspaceEntries.add(entry);
+ }
+ removeEntryFromDb(mDb, entriesToRemove);
+ c.close();
+ return mWorkspaceEntries;
+ }
+
+ private int getFolderItemsCount(DbEntry entry) {
+ Cursor c = queryWorkspace(
+ new String[]{LauncherSettings.Favorites._ID, LauncherSettings.Favorites.INTENT},
+ LauncherSettings.Favorites.CONTAINER + " = " + entry.id);
+
+ int total = 0;
+ while (c.moveToNext()) {
+ try {
+ String intent = c.getString(1);
+ verifyIntent(intent);
+ total++;
+ entry.mFolderItems.add(intent);
+ } catch (Exception e) {
+ removeEntryFromDb(mDb, IntArray.wrap(c.getInt(0)));
+ }
+ }
+ c.close();
+ return total;
+ }
+
+ private Cursor queryWorkspace(String[] columns, String where) {
+ return mDb.query(mTableName, columns, where, null, null, null, null);
+ }
+
+ /** Verifies if the mIntent should be restored. */
+ private void verifyIntent(String intentStr)
+ throws Exception {
+ Intent intent = Intent.parseUri(intentStr, 0);
+ if (intent.getComponent() != null) {
+ verifyPackage(intent.getComponent().getPackageName());
+ } else if (intent.getPackage() != null) {
+ // Only verify package if the component was null.
+ verifyPackage(intent.getPackage());
+ }
+ }
+
+ /** Verifies if the package should be restored */
+ private void verifyPackage(String packageName)
+ throws Exception {
+ if (!mValidPackages.contains(packageName)) {
+ // TODO(b/151468819): Handle promise app icon restoration during grid migration.
+ throw new Exception("Package not available");
+ }
+ }
+ }
+
+ protected static class DbEntry extends ItemInfo implements Comparable<DbEntry> {
+
+ private String mIntent;
+ private String mProvider;
+ private Set<String> mFolderItems = new HashSet<>();
+
+ /** Comparator according to the reading order */
+ @Override
+ public int compareTo(DbEntry another) {
+ if (screenId != another.screenId) {
+ return Integer.compare(screenId, another.screenId);
+ }
+ if (cellY != another.cellY) {
+ return -Integer.compare(cellY, another.cellY);
+ }
+ return Integer.compare(cellX, another.cellX);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ DbEntry entry = (DbEntry) o;
+ return Objects.equals(mIntent, entry.mIntent);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mIntent);
+ }
+
+ public void updateContentValues(ContentValues values) {
+ values.put(LauncherSettings.Favorites.SCREEN, screenId);
+ values.put(LauncherSettings.Favorites.CELLX, cellX);
+ values.put(LauncherSettings.Favorites.CELLY, cellY);
+ values.put(LauncherSettings.Favorites.SPANX, spanX);
+ values.put(LauncherSettings.Favorites.SPANY, spanY);
+ }
+
+ public String getIntentStr() {
+ return mIntent;
+ }
+ }
}
diff --git a/src/com/android/launcher3/popup/PopupContainerWithArrow.java b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
index 5af5ebb..9bac259 100644
--- a/src/com/android/launcher3/popup/PopupContainerWithArrow.java
+++ b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
@@ -59,6 +59,7 @@
import com.android.launcher3.dragndrop.DragController;
import com.android.launcher3.dragndrop.DragOptions;
import com.android.launcher3.dragndrop.DragView;
+import com.android.launcher3.dragndrop.DraggableView;
import com.android.launcher3.notification.NotificationInfo;
import com.android.launcher3.notification.NotificationItemView;
import com.android.launcher3.notification.NotificationKeyData;
@@ -662,7 +663,8 @@
iconShift.x = mIconLastTouchPos.x - sv.getIconCenter().x;
iconShift.y = mIconLastTouchPos.y - mLauncher.getDeviceProfile().iconSizePx;
- DragView dv = mLauncher.getWorkspace().beginDragShared(sv.getIconView(),
+ DraggableView draggableView = DraggableView.ofType(DraggableView.DRAGGABLE_ICON);
+ DragView dv = mLauncher.getWorkspace().beginDragShared(sv.getIconView(), draggableView,
mContainer, sv.getFinalInfo(),
new ShortcutDragPreviewProvider(sv.getIconView(), iconShift),
new DragOptions());
diff --git a/src/com/android/launcher3/provider/LauncherDbUtils.java b/src/com/android/launcher3/provider/LauncherDbUtils.java
index f7ecc3f..dacea84 100644
--- a/src/com/android/launcher3/provider/LauncherDbUtils.java
+++ b/src/com/android/launcher3/provider/LauncherDbUtils.java
@@ -118,13 +118,20 @@
db.execSQL("DROP TABLE IF EXISTS " + tableName);
}
- /** Copy from table to the to table. */
- public static void copyTable(SQLiteDatabase db, String from, String to, Context context) {
+ /** Copy fromTable in fromDb to toTable in toDb. */
+ public static void copyTable(SQLiteDatabase fromDb, String fromTable, SQLiteDatabase toDb,
+ String toTable, Context context) {
long userSerial = UserCache.INSTANCE.get(context).getSerialNumberForUser(
Process.myUserHandle());
- dropTable(db, to);
- Favorites.addTableToDb(db, userSerial, false, to);
- db.execSQL("INSERT INTO " + to + " SELECT * FROM " + from);
+ dropTable(toDb, toTable);
+ Favorites.addTableToDb(toDb, userSerial, false, toTable);
+ if (fromDb != toDb) {
+ toDb.execSQL("ATTACH DATABASE '" + fromDb.getPath() + "' AS from_db");
+ toDb.execSQL(
+ "INSERT INTO " + toTable + " SELECT * FROM from_db." + fromTable);
+ } else {
+ toDb.execSQL("INSERT INTO " + toTable + " SELECT * FROM " + fromTable);
+ }
}
/**
diff --git a/src/com/android/launcher3/states/StateAnimationConfig.java b/src/com/android/launcher3/states/StateAnimationConfig.java
new file mode 100644
index 0000000..82cde64
--- /dev/null
+++ b/src/com/android/launcher3/states/StateAnimationConfig.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.states;
+
+import android.view.animation.Interpolator;
+
+import androidx.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Utility class for building animator set
+ */
+public class StateAnimationConfig {
+
+ // We separate the state animations into "atomic" and "non-atomic" components. The atomic
+ // components may be run atomically - that is, all at once, instead of user-controlled. However,
+ // atomic components are not restricted to this purpose; they can be user-controlled alongside
+ // non atomic components as well. Note that each gesture model has exactly one atomic component,
+ // PLAY_ATOMIC_OVERVIEW_SCALE *or* PLAY_ATOMIC_OVERVIEW_PEEK.
+ @IntDef(flag = true, value = {
+ PLAY_NON_ATOMIC,
+ PLAY_ATOMIC_OVERVIEW_SCALE,
+ PLAY_ATOMIC_OVERVIEW_PEEK,
+ SKIP_OVERVIEW,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface AnimationFlags {}
+ public static final int PLAY_NON_ATOMIC = 1 << 0;
+ public static final int PLAY_ATOMIC_OVERVIEW_SCALE = 1 << 1;
+ public static final int PLAY_ATOMIC_OVERVIEW_PEEK = 1 << 2;
+ public static final int SKIP_OVERVIEW = 1 << 3;
+
+ public long duration;
+ public boolean userControlled;
+ public @AnimationFlags int animFlags = ANIM_ALL_COMPONENTS;
+
+ public static final int ANIM_ALL_COMPONENTS = PLAY_NON_ATOMIC | PLAY_ATOMIC_OVERVIEW_SCALE
+ | PLAY_ATOMIC_OVERVIEW_PEEK;
+
+ // Various types of animation state transition
+ @IntDef(value = {
+ ANIM_VERTICAL_PROGRESS,
+ ANIM_WORKSPACE_SCALE,
+ ANIM_WORKSPACE_TRANSLATE,
+ ANIM_WORKSPACE_FADE,
+ ANIM_HOTSEAT_SCALE,
+ ANIM_HOTSEAT_TRANSLATE,
+ ANIM_OVERVIEW_SCALE,
+ ANIM_OVERVIEW_TRANSLATE_X,
+ ANIM_OVERVIEW_TRANSLATE_Y,
+ ANIM_OVERVIEW_FADE,
+ ANIM_ALL_APPS_FADE,
+ ANIM_OVERVIEW_SCRIM_FADE,
+ ANIM_ALL_APPS_HEADER_FADE,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface AnimType {}
+ public static final int ANIM_VERTICAL_PROGRESS = 0;
+ public static final int ANIM_WORKSPACE_SCALE = 1;
+ public static final int ANIM_WORKSPACE_TRANSLATE = 2;
+ public static final int ANIM_WORKSPACE_FADE = 3;
+ public static final int ANIM_HOTSEAT_SCALE = 4;
+ public static final int ANIM_HOTSEAT_TRANSLATE = 5;
+ public static final int ANIM_OVERVIEW_SCALE = 6;
+ public static final int ANIM_OVERVIEW_TRANSLATE_X = 7;
+ public static final int ANIM_OVERVIEW_TRANSLATE_Y = 8;
+ public static final int ANIM_OVERVIEW_FADE = 9;
+ public static final int ANIM_ALL_APPS_FADE = 10;
+ public static final int ANIM_OVERVIEW_SCRIM_FADE = 11;
+ public static final int ANIM_ALL_APPS_HEADER_FADE = 12; // e.g. predictions
+
+ private static final int ANIM_TYPES_COUNT = 13;
+
+ private final Interpolator[] mInterpolators = new Interpolator[ANIM_TYPES_COUNT];
+
+ public StateAnimationConfig() { }
+
+ /**
+ * Copies the config to target
+ */
+ public void copyTo(StateAnimationConfig target) {
+ target.duration = duration;
+ target.animFlags = animFlags;
+ target.userControlled = userControlled;
+ for (int i = 0; i < ANIM_TYPES_COUNT; i++) {
+ target.mInterpolators[i] = mInterpolators[i];
+ }
+ }
+
+ /**
+ * Returns the interpolator set for animId or fallback if nothing is set
+ *
+ * @see #setInterpolator(int, Interpolator)
+ */
+ public Interpolator getInterpolator(@AnimType int animId, Interpolator fallback) {
+ return mInterpolators[animId] == null ? fallback : mInterpolators[animId];
+ }
+
+ /**
+ * Sets an interpolator for a given animation type
+ */
+ public void setInterpolator(@AnimType int animId, Interpolator interpolator) {
+ mInterpolators[animId] = interpolator;
+ }
+
+ /**
+ * @return Whether Overview is scaling as part of this animation. If this is the only
+ * component (i.e. NON_ATOMIC_COMPONENT isn't included), then this scaling is happening
+ * atomically, rather than being part of a normal state animation. StateHandlers can use
+ * this to designate part of their animation that should scale with Overview.
+ */
+ public boolean playAtomicOverviewScaleComponent() {
+ return hasAnimationFlag(StateAnimationConfig.PLAY_ATOMIC_OVERVIEW_SCALE);
+ }
+
+ /**
+ * @return Whether this animation will play atomically at the same time as a different,
+ * user-controlled state transition. StateHandlers, which contribute to both animations, can
+ * use this to avoid animating the same properties in both animations, since they'd conflict
+ * with one another.
+ */
+ public boolean onlyPlayAtomicComponent() {
+ return getAnimComponents() == StateAnimationConfig.PLAY_ATOMIC_OVERVIEW_SCALE
+ || getAnimComponents() == StateAnimationConfig.PLAY_ATOMIC_OVERVIEW_PEEK;
+ }
+
+ /**
+ * Returns true if the config and any of the provided component flags
+ */
+ public boolean hasAnimationFlag(@AnimationFlags int a) {
+ return (animFlags & a) != 0;
+ }
+
+ /**
+ * @return Only the flags that determine which animation components to play.
+ */
+ public @AnimationFlags int getAnimComponents() {
+ return animFlags & StateAnimationConfig.ANIM_ALL_COMPONENTS;
+ }
+}
diff --git a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
index c3664c3..cbc5257 100644
--- a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
+++ b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
@@ -19,11 +19,11 @@
import static com.android.launcher3.LauncherState.ALL_APPS;
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.LauncherState.OVERVIEW;
-import static com.android.launcher3.LauncherStateManager.ANIM_ALL_COMPONENTS;
-import static com.android.launcher3.LauncherStateManager.PLAY_ATOMIC_OVERVIEW_SCALE;
-import static com.android.launcher3.LauncherStateManager.PLAY_NON_ATOMIC;
import static com.android.launcher3.anim.Interpolators.scrollInterpolatorForVelocity;
import static com.android.launcher3.config.FeatureFlags.UNSTABLE_SPRINGS;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_COMPONENTS;
+import static com.android.launcher3.states.StateAnimationConfig.PLAY_ATOMIC_OVERVIEW_SCALE;
+import static com.android.launcher3.states.StateAnimationConfig.PLAY_NON_ATOMIC;
import static com.android.launcher3.util.DefaultDisplay.getSingleFrameMs;
import android.animation.Animator;
@@ -37,12 +37,12 @@
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAnimUtils;
import com.android.launcher3.LauncherState;
-import com.android.launcher3.LauncherStateManager.AnimationFlags;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.anim.AnimatorPlaybackController;
-import com.android.launcher3.anim.AnimatorSetBuilder;
import com.android.launcher3.anim.PendingAnimation;
+import com.android.launcher3.states.StateAnimationConfig;
+import com.android.launcher3.states.StateAnimationConfig.AnimationFlags;
import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
@@ -358,14 +358,18 @@
private AnimatorSet createAtomicAnimForState(LauncherState fromState, LauncherState targetState,
long duration) {
- AnimatorSetBuilder builder = getAnimatorSetBuilderForStates(fromState, targetState);
- return mLauncher.getStateManager().createAtomicAnimation(fromState, targetState, builder,
- PLAY_ATOMIC_OVERVIEW_SCALE, duration);
+ StateAnimationConfig config = getConfigForStates(fromState, targetState);
+ config.animFlags = PLAY_ATOMIC_OVERVIEW_SCALE;
+ config.duration = duration;
+ return mLauncher.getStateManager().createAtomicAnimation(fromState, targetState, config);
}
- protected AnimatorSetBuilder getAnimatorSetBuilderForStates(LauncherState fromState,
- LauncherState toState) {
- return new AnimatorSetBuilder();
+ /**
+ * Returns animation config for state transition between provided states
+ */
+ protected StateAnimationConfig getConfigForStates(
+ LauncherState fromState, LauncherState toState) {
+ return new StateAnimationConfig();
}
@Override
@@ -531,7 +535,7 @@
// case the user started interacting with it before the animation finished.
mLauncher.getStateManager().goToState(targetState, false /* animated */);
}
- mLauncher.getDragLayer().getScrim().animateToSysuiMultiplier(1, 0, 0);
+ mLauncher.getDragLayer().getScrim().createSysuiMultiplierAnim(1f).setDuration(0).start();
}
private void logReachedState(int logAction, LauncherState targetState) {
diff --git a/src/com/android/launcher3/touch/AllAppsSwipeController.java b/src/com/android/launcher3/touch/AllAppsSwipeController.java
index 8d5f33d..4a202b6 100644
--- a/src/com/android/launcher3/touch/AllAppsSwipeController.java
+++ b/src/com/android/launcher3/touch/AllAppsSwipeController.java
@@ -23,7 +23,7 @@
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
-import com.android.launcher3.LauncherStateManager.AnimationFlags;
+import com.android.launcher3.states.StateAnimationConfig.AnimationFlags;
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
/**
diff --git a/src/com/android/launcher3/util/VibratorWrapper.java b/src/com/android/launcher3/util/VibratorWrapper.java
index 04741a1..b0defd4 100644
--- a/src/com/android/launcher3/util/VibratorWrapper.java
+++ b/src/com/android/launcher3/util/VibratorWrapper.java
@@ -39,7 +39,7 @@
public static final MainThreadInitializedObject<VibratorWrapper> INSTANCE =
new MainThreadInitializedObject<>(VibratorWrapper::new);
- private static final VibrationEffect EFFECT_CLICK =
+ public static final VibrationEffect EFFECT_CLICK =
createPredefined(VibrationEffect.EFFECT_CLICK);
/**
diff --git a/src/com/android/launcher3/views/BaseDragLayer.java b/src/com/android/launcher3/views/BaseDragLayer.java
index 25748ae..868c91d 100644
--- a/src/com/android/launcher3/views/BaseDragLayer.java
+++ b/src/com/android/launcher3/views/BaseDragLayer.java
@@ -23,7 +23,11 @@
import static com.android.launcher3.util.DefaultDisplay.getSingleFrameMs;
import android.annotation.TargetApi;
+import android.app.WallpaperInfo;
+import android.app.WallpaperManager;
+import android.content.ComponentName;
import android.content.Context;
+import android.content.Intent;
import android.graphics.Insets;
import android.graphics.Rect;
import android.graphics.RectF;
@@ -38,11 +42,15 @@
import android.view.accessibility.AccessibilityEvent;
import android.widget.FrameLayout;
+import androidx.annotation.Nullable;
+
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.InsettableFrameLayout;
+import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.util.MultiValueAlpha;
import com.android.launcher3.util.MultiValueAlpha.AlphaProperty;
+import com.android.launcher3.util.SimpleBroadcastReceiver;
import com.android.launcher3.util.TouchController;
import java.io.PrintWriter;
@@ -98,6 +106,10 @@
protected final T mActivity;
private final MultiValueAlpha mMultiValueAlpha;
+ private final WallpaperManager mWallpaperManager;
+ private final SimpleBroadcastReceiver mWallpaperChangeReceiver =
+ new SimpleBroadcastReceiver(this::onWallpaperChanged);
+ private final String[] mWallpapersWithoutSysuiScrims;
// All the touch controllers for the view
protected TouchController[] mControllers;
@@ -108,10 +120,15 @@
private TouchCompleteListener mTouchCompleteListener;
+ protected boolean mAllowSysuiScrims = true;
+
public BaseDragLayer(Context context, AttributeSet attrs, int alphaChannelCount) {
super(context, attrs);
mActivity = (T) ActivityContext.lookupContext(context);
mMultiValueAlpha = new MultiValueAlpha(this, alphaChannelCount);
+ mWallpaperManager = context.getSystemService(WallpaperManager.class);
+ mWallpapersWithoutSysuiScrims = getResources().getStringArray(
+ R.array.live_wallpapers_remove_sysui_scrims);
}
/**
@@ -517,4 +534,46 @@
}
return super.dispatchApplyWindowInsets(insets);
}
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ mWallpaperChangeReceiver.register(mActivity, Intent.ACTION_WALLPAPER_CHANGED);
+ onWallpaperChanged(null);
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ mActivity.unregisterReceiver(mWallpaperChangeReceiver);
+ }
+
+ private void onWallpaperChanged(Intent unusedBroadcastIntent) {
+ WallpaperInfo newWallpaperInfo = mWallpaperManager.getWallpaperInfo();
+ boolean oldAllowSysuiScrims = mAllowSysuiScrims;
+ mAllowSysuiScrims = computeAllowSysuiScrims(newWallpaperInfo);
+ if (mAllowSysuiScrims != oldAllowSysuiScrims) {
+ // Reapply insets so scrim can be removed or re-added if necessary.
+ setInsets(mInsets);
+ }
+ }
+
+ /**
+ * Determines whether we can scrim the status bar and nav bar for the given wallpaper by
+ * checking against a list of live wallpapers that we don't show the scrims on.
+ */
+ private boolean computeAllowSysuiScrims(@Nullable WallpaperInfo newWallpaperInfo) {
+ if (newWallpaperInfo == null) {
+ // New wallpaper is static, not live. Thus, blacklist isn't applicable.
+ return true;
+ }
+ ComponentName newWallpaper = newWallpaperInfo.getComponent();
+ for (String wallpaperWithoutScrim : mWallpapersWithoutSysuiScrims) {
+ if (newWallpaper.equals(ComponentName.unflattenFromString(wallpaperWithoutScrim))) {
+ // New wallpaper is blacklisted from showing a scrim.
+ return false;
+ }
+ }
+ return true;
+ }
}
diff --git a/src/com/android/launcher3/views/ClipIconView.java b/src/com/android/launcher3/views/ClipIconView.java
new file mode 100644
index 0000000..478141a
--- /dev/null
+++ b/src/com/android/launcher3/views/ClipIconView.java
@@ -0,0 +1,349 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.views;
+
+import static com.android.launcher3.Utilities.mapToRange;
+import static com.android.launcher3.anim.Interpolators.LINEAR;
+import static com.android.launcher3.views.FloatingIconView.SHAPE_PROGRESS_DURATION;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Outline;
+import android.graphics.Path;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.graphics.drawable.AdaptiveIconDrawable;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
+import android.os.Build;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.ViewOutlineProvider;
+
+import androidx.annotation.Nullable;
+import androidx.dynamicanimation.animation.FloatPropertyCompat;
+import androidx.dynamicanimation.animation.SpringAnimation;
+import androidx.dynamicanimation.animation.SpringForce;
+
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.InsettableFrameLayout.LayoutParams;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.dragndrop.FolderAdaptiveIcon;
+import com.android.launcher3.graphics.IconShape;
+
+/**
+ * A view used to draw both layers of an {@link AdaptiveIconDrawable}.
+ * Supports springing just the foreground layer.
+ * Supports clipping the icon to/from its icon shape.
+ */
+@TargetApi(Build.VERSION_CODES.Q)
+public class ClipIconView extends View implements ClipPathView {
+
+ private static final Rect sTmpRect = new Rect();
+
+ // We spring the foreground drawable relative to the icon's movement in the DragLayer.
+ // We then use these two factor values to scale the movement of the fg within this view.
+ private static final int FG_TRANS_X_FACTOR = 60;
+ private static final int FG_TRANS_Y_FACTOR = 75;
+
+ private static final FloatPropertyCompat<ClipIconView> mFgTransYProperty =
+ new FloatPropertyCompat<ClipIconView>("ClipIconViewFgTransY") {
+ @Override
+ public float getValue(ClipIconView view) {
+ return view.mFgTransY;
+ }
+
+ @Override
+ public void setValue(ClipIconView view, float transY) {
+ view.mFgTransY = transY;
+ view.invalidate();
+ }
+ };
+
+ private static final FloatPropertyCompat<ClipIconView> mFgTransXProperty =
+ new FloatPropertyCompat<ClipIconView>("ClipIconViewFgTransX") {
+ @Override
+ public float getValue(ClipIconView view) {
+ return view.mFgTransX;
+ }
+
+ @Override
+ public void setValue(ClipIconView view, float transX) {
+ view.mFgTransX = transX;
+ view.invalidate();
+ }
+ };
+
+ private final Launcher mLauncher;
+ private final int mBlurSizeOutline;
+ private final boolean mIsRtl;
+
+ private @Nullable Drawable mForeground;
+ private @Nullable Drawable mBackground;
+
+ private boolean mIsVerticalBarLayout = false;
+ private boolean mIsAdaptiveIcon = false;
+
+ private ValueAnimator mRevealAnimator;
+
+ private final Rect mStartRevealRect = new Rect();
+ private final Rect mEndRevealRect = new Rect();
+ private Path mClipPath;
+ private float mTaskCornerRadius;
+
+ private final Rect mOutline = new Rect();
+ private final Rect mFinalDrawableBounds = new Rect();
+
+ private final SpringAnimation mFgSpringY;
+ private float mFgTransY;
+ private final SpringAnimation mFgSpringX;
+ private float mFgTransX;
+
+ public ClipIconView(Context context) {
+ this(context, null);
+ }
+
+ public ClipIconView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public ClipIconView(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ mLauncher = Launcher.getLauncher(context);
+ mBlurSizeOutline = getResources().getDimensionPixelSize(
+ R.dimen.blur_size_medium_outline);
+ mIsRtl = Utilities.isRtl(getResources());
+
+ mFgSpringX = new SpringAnimation(this, mFgTransXProperty)
+ .setSpring(new SpringForce()
+ .setDampingRatio(SpringForce.DAMPING_RATIO_LOW_BOUNCY)
+ .setStiffness(SpringForce.STIFFNESS_LOW));
+ mFgSpringY = new SpringAnimation(this, mFgTransYProperty)
+ .setSpring(new SpringForce()
+ .setDampingRatio(SpringForce.DAMPING_RATIO_LOW_BOUNCY)
+ .setStiffness(SpringForce.STIFFNESS_LOW));
+ }
+
+ void update(RectF rect, float progress, float shapeProgressStart, float cornerRadius,
+ boolean isOpening, float scale, float minSize, LayoutParams parentLp) {
+ DeviceProfile dp = mLauncher.getDeviceProfile();
+ float dX = mIsRtl
+ ? rect.left - (dp.widthPx - parentLp.getMarginStart() - parentLp.width)
+ : rect.left - parentLp.getMarginStart();
+ float dY = rect.top - parentLp.topMargin;
+
+ // shapeRevealProgress = 1 when progress = shapeProgressStart + SHAPE_PROGRESS_DURATION
+ float toMax = isOpening ? 1 / SHAPE_PROGRESS_DURATION : 1f;
+ float shapeRevealProgress = Utilities.boundToRange(mapToRange(
+ Math.max(shapeProgressStart, progress), shapeProgressStart, 1f, 0, toMax,
+ LINEAR), 0, 1);
+
+ if (mIsVerticalBarLayout) {
+ mOutline.right = (int) (rect.width() / scale);
+ } else {
+ mOutline.bottom = (int) (rect.height() / scale);
+ }
+
+ mTaskCornerRadius = cornerRadius / scale;
+ if (mIsAdaptiveIcon) {
+ if (!isOpening && progress >= shapeProgressStart) {
+ if (mRevealAnimator == null) {
+ mRevealAnimator = (ValueAnimator) IconShape.getShape().createRevealAnimator(
+ this, mStartRevealRect, mOutline, mTaskCornerRadius, !isOpening);
+ mRevealAnimator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mRevealAnimator = null;
+ }
+ });
+ mRevealAnimator.start();
+ // We pause here so we can set the current fraction ourselves.
+ mRevealAnimator.pause();
+ }
+ mRevealAnimator.setCurrentFraction(shapeRevealProgress);
+ }
+
+ float drawableScale = (mIsVerticalBarLayout ? mOutline.width() : mOutline.height())
+ / minSize;
+ setBackgroundDrawableBounds(drawableScale);
+ if (isOpening) {
+ // Center align foreground
+ int height = mFinalDrawableBounds.height();
+ int width = mFinalDrawableBounds.width();
+ int diffY = mIsVerticalBarLayout ? 0
+ : (int) (((height * drawableScale) - height) / 2);
+ int diffX = mIsVerticalBarLayout ? (int) (((width * drawableScale) - width) / 2)
+ : 0;
+ sTmpRect.set(mFinalDrawableBounds);
+ sTmpRect.offset(diffX, diffY);
+ mForeground.setBounds(sTmpRect);
+ } else {
+ // Spring the foreground relative to the icon's movement within the DragLayer.
+ int diffX = (int) (dX / dp.availableWidthPx * FG_TRANS_X_FACTOR);
+ int diffY = (int) (dY / dp.availableHeightPx * FG_TRANS_Y_FACTOR);
+
+ mFgSpringX.animateToFinalPosition(diffX);
+ mFgSpringY.animateToFinalPosition(diffY);
+ }
+ }
+ invalidate();
+ invalidateOutline();
+ }
+
+ private void setBackgroundDrawableBounds(float scale) {
+ sTmpRect.set(mFinalDrawableBounds);
+ Utilities.scaleRectAboutCenter(sTmpRect, scale);
+ // Since the drawable is at the top of the view, we need to offset to keep it centered.
+ if (mIsVerticalBarLayout) {
+ sTmpRect.offsetTo((int) (mFinalDrawableBounds.left * scale), sTmpRect.top);
+ } else {
+ sTmpRect.offsetTo(sTmpRect.left, (int) (mFinalDrawableBounds.top * scale));
+ }
+ mBackground.setBounds(sTmpRect);
+ }
+
+ protected void endReveal() {
+ if (mRevealAnimator != null) {
+ mRevealAnimator.end();
+ }
+ }
+
+ void setIcon(@Nullable Drawable drawable, int iconOffset, LayoutParams lp, boolean isOpening) {
+ mIsAdaptiveIcon = drawable instanceof AdaptiveIconDrawable;
+ if (mIsAdaptiveIcon) {
+ boolean isFolderIcon = drawable instanceof FolderAdaptiveIcon;
+
+ AdaptiveIconDrawable adaptiveIcon = (AdaptiveIconDrawable) drawable;
+ Drawable background = adaptiveIcon.getBackground();
+ if (background == null) {
+ background = new ColorDrawable(Color.TRANSPARENT);
+ }
+ mBackground = background;
+ Drawable foreground = adaptiveIcon.getForeground();
+ if (foreground == null) {
+ foreground = new ColorDrawable(Color.TRANSPARENT);
+ }
+ mForeground = foreground;
+
+ final int originalHeight = lp.height;
+ final int originalWidth = lp.width;
+
+ int blurMargin = mBlurSizeOutline / 2;
+ mFinalDrawableBounds.set(0, 0, originalWidth, originalHeight);
+
+ if (!isFolderIcon) {
+ mFinalDrawableBounds.inset(iconOffset - blurMargin, iconOffset - blurMargin);
+ }
+ mForeground.setBounds(mFinalDrawableBounds);
+ mBackground.setBounds(mFinalDrawableBounds);
+
+ mStartRevealRect.set(0, 0, originalWidth, originalHeight);
+
+ if (!isFolderIcon) {
+ Utilities.scaleRectAboutCenter(mStartRevealRect, IconShape.getNormalizationScale());
+ }
+
+ float aspectRatio = mLauncher.getDeviceProfile().aspectRatio;
+ if (mIsVerticalBarLayout) {
+ lp.width = (int) Math.max(lp.width, lp.height * aspectRatio);
+ } else {
+ lp.height = (int) Math.max(lp.height, lp.width * aspectRatio);
+ }
+
+ int left = mIsRtl
+ ? mLauncher.getDeviceProfile().widthPx - lp.getMarginStart() - lp.width
+ : lp.leftMargin;
+ layout(left, lp.topMargin, left + lp.width, lp.topMargin + lp.height);
+
+ float scale = Math.max((float) lp.height / originalHeight,
+ (float) lp.width / originalWidth);
+ float bgDrawableStartScale;
+ if (isOpening) {
+ bgDrawableStartScale = 1f;
+ mOutline.set(0, 0, originalWidth, originalHeight);
+ } else {
+ bgDrawableStartScale = scale;
+ mOutline.set(0, 0, lp.width, lp.height);
+ }
+ setBackgroundDrawableBounds(bgDrawableStartScale);
+ mEndRevealRect.set(0, 0, lp.width, lp.height);
+ setOutlineProvider(new ViewOutlineProvider() {
+ @Override
+ public void getOutline(View view, Outline outline) {
+ outline.setRoundRect(mOutline, mTaskCornerRadius);
+ }
+ });
+ setClipToOutline(true);
+ } else {
+ setBackground(drawable);
+ setClipToOutline(false);
+ }
+
+ invalidate();
+ invalidateOutline();
+ }
+
+ @Override
+ public void setClipPath(Path clipPath) {
+ mClipPath = clipPath;
+ invalidate();
+ }
+
+ @Override
+ public void draw(Canvas canvas) {
+ int count = canvas.save();
+ if (mClipPath != null) {
+ canvas.clipPath(mClipPath);
+ }
+ super.draw(canvas);
+ if (mBackground != null) {
+ mBackground.draw(canvas);
+ }
+ if (mForeground != null) {
+ int count2 = canvas.save();
+ canvas.translate(mFgTransX, mFgTransY);
+ mForeground.draw(canvas);
+ canvas.restoreToCount(count2);
+ }
+ canvas.restoreToCount(count);
+ }
+
+ void recycle() {
+ setBackground(null);
+ mIsAdaptiveIcon = false;
+ mForeground = null;
+ mBackground = null;
+ mClipPath = null;
+ mFinalDrawableBounds.setEmpty();
+ if (mRevealAnimator != null) {
+ mRevealAnimator.cancel();
+ }
+ mRevealAnimator = null;
+ mTaskCornerRadius = 0;
+ mOutline.setEmpty();
+ mFgTransY = 0;
+ mFgSpringX.cancel();
+ mFgTransX = 0;
+ mFgSpringY.cancel();
+ }
+}
diff --git a/src/com/android/launcher3/views/FloatingIconView.java b/src/com/android/launcher3/views/FloatingIconView.java
index fa625ed..3e2560f 100644
--- a/src/com/android/launcher3/views/FloatingIconView.java
+++ b/src/com/android/launcher3/views/FloatingIconView.java
@@ -18,8 +18,6 @@
import static com.android.launcher3.LauncherAnimUtils.DRAWABLE_ALPHA;
import static com.android.launcher3.Utilities.getBadge;
import static com.android.launcher3.Utilities.getFullDrawable;
-import static com.android.launcher3.Utilities.mapToRange;
-import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.config.FeatureFlags.ADAPTIVE_ICON_WINDOW_ANIM;
import static com.android.launcher3.states.RotationHelper.REQUEST_LOCK;
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
@@ -28,17 +26,12 @@
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
-import android.animation.ValueAnimator;
import android.annotation.TargetApi;
import android.content.Context;
import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.Outline;
-import android.graphics.Path;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.AdaptiveIconDrawable;
-import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.CancellationSignal;
@@ -46,19 +39,17 @@
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
-import android.view.ViewOutlineProvider;
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
+import android.widget.FrameLayout;
import android.widget.ImageView;
import androidx.annotation.Nullable;
import androidx.annotation.UiThread;
import androidx.annotation.WorkerThread;
-import androidx.dynamicanimation.animation.FloatPropertyCompat;
-import androidx.dynamicanimation.animation.SpringAnimation;
-import androidx.dynamicanimation.animation.SpringForce;
import com.android.launcher3.BubbleTextView;
-import com.android.launcher3.InsettableFrameLayout.LayoutParams;
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.InsettableFrameLayout;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.Launcher;
import com.android.launcher3.R;
@@ -66,8 +57,6 @@
import com.android.launcher3.dragndrop.DragLayer;
import com.android.launcher3.dragndrop.FolderAdaptiveIcon;
import com.android.launcher3.folder.FolderIcon;
-import com.android.launcher3.graphics.IconShape;
-import com.android.launcher3.graphics.ShiftedBitmapDrawable;
import com.android.launcher3.icons.LauncherIcons;
import com.android.launcher3.popup.SystemShortcut;
import com.android.launcher3.shortcuts.DeepShortcutView;
@@ -76,8 +65,8 @@
* A view that is created to look like another view with the purpose of creating fluid animations.
*/
@TargetApi(Build.VERSION_CODES.Q)
-public class FloatingIconView extends View implements
- Animator.AnimatorListener, ClipPathView, OnGlobalLayoutListener {
+public class FloatingIconView extends FrameLayout implements
+ Animator.AnimatorListener, OnGlobalLayoutListener {
private static final String TAG = FloatingIconView.class.getSimpleName();
@@ -86,81 +75,34 @@
public static final float SHAPE_PROGRESS_DURATION = 0.10f;
private static final int FADE_DURATION_MS = 200;
- private static final Rect sTmpRect = new Rect();
private static final RectF sTmpRectF = new RectF();
private static final Object[] sTmpObjArray = new Object[1];
- // We spring the foreground drawable relative to the icon's movement in the DragLayer.
- // We then use these two factor values to scale the movement of the fg within this view.
- private static final int FG_TRANS_X_FACTOR = 60;
- private static final int FG_TRANS_Y_FACTOR = 75;
-
- private static final FloatPropertyCompat<FloatingIconView> mFgTransYProperty
- = new FloatPropertyCompat<FloatingIconView>("FloatingViewFgTransY") {
- @Override
- public float getValue(FloatingIconView view) {
- return view.mFgTransY;
- }
-
- @Override
- public void setValue(FloatingIconView view, float transY) {
- view.mFgTransY = transY;
- view.invalidate();
- }
- };
-
- private static final FloatPropertyCompat<FloatingIconView> mFgTransXProperty
- = new FloatPropertyCompat<FloatingIconView>("FloatingViewFgTransX") {
- @Override
- public float getValue(FloatingIconView view) {
- return view.mFgTransX;
- }
-
- @Override
- public void setValue(FloatingIconView view, float transX) {
- view.mFgTransX = transX;
- view.invalidate();
- }
- };
-
private Runnable mEndRunnable;
private CancellationSignal mLoadIconSignal;
private final Launcher mLauncher;
- private final int mBlurSizeOutline;
private final boolean mIsRtl;
private boolean mIsVerticalBarLayout = false;
- private boolean mIsAdaptiveIcon = false;
private boolean mIsOpening;
private IconLoadResult mIconLoadResult;
+ private ClipIconView mClipIconView;
private @Nullable Drawable mBadge;
- private @Nullable Drawable mForeground;
- private @Nullable Drawable mBackground;
+
private float mRotation;
- private ValueAnimator mRevealAnimator;
- private final Rect mStartRevealRect = new Rect();
- private final Rect mEndRevealRect = new Rect();
- private Path mClipPath;
- private float mTaskCornerRadius;
private View mOriginalIcon;
private RectF mPositionOut;
private Runnable mOnTargetChangeRunnable;
- private final Rect mOutline = new Rect();
private final Rect mFinalDrawableBounds = new Rect();
private AnimatorSet mFadeAnimatorSet;
private ListenerView mListenerView;
- private final SpringAnimation mFgSpringY;
- private float mFgTransY;
- private final SpringAnimation mFgSpringX;
- private float mFgTransX;
-
public FloatingIconView(Context context) {
this(context, null);
}
@@ -172,19 +114,11 @@
public FloatingIconView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mLauncher = Launcher.getLauncher(context);
- mBlurSizeOutline = getResources().getDimensionPixelSize(
- R.dimen.blur_size_medium_outline);
mIsRtl = Utilities.isRtl(getResources());
mListenerView = new ListenerView(context, attrs);
-
- mFgSpringX = new SpringAnimation(this, mFgTransXProperty)
- .setSpring(new SpringForce()
- .setDampingRatio(SpringForce.DAMPING_RATIO_LOW_BOUNCY)
- .setStiffness(SpringForce.STIFFNESS_LOW));
- mFgSpringY = new SpringAnimation(this, mFgTransYProperty)
- .setSpring(new SpringForce()
- .setDampingRatio(SpringForce.DAMPING_RATIO_LOW_BOUNCY)
- .setStiffness(SpringForce.STIFFNESS_LOW));
+ mClipIconView = new ClipIconView(context, attrs);
+ addView(mClipIconView);
+ setWillNotDraw(false);
}
@Override
@@ -213,10 +147,12 @@
float cornerRadius, boolean isOpening) {
setAlpha(alpha);
- LayoutParams lp = (LayoutParams) getLayoutParams();
+ InsettableFrameLayout.LayoutParams lp =
+ (InsettableFrameLayout.LayoutParams) getLayoutParams();
+
+ DeviceProfile dp = mLauncher.getDeviceProfile();
float dX = mIsRtl
- ? rect.left
- - (mLauncher.getDeviceProfile().widthPx - lp.getMarginStart() - lp.width)
+ ? rect.left - (dp.widthPx - lp.getMarginStart() - lp.width)
: rect.left - lp.getMarginStart();
float dY = rect.top - lp.topMargin;
setTranslationX(dX);
@@ -227,69 +163,15 @@
float scaleY = rect.height() / minSize;
float scale = Math.max(1f, Math.min(scaleX, scaleY));
+ mClipIconView.update(rect, progress, shapeProgressStart, cornerRadius, isOpening, scale,
+ minSize, lp);
+
setPivotX(0);
setPivotY(0);
setScaleX(scale);
setScaleY(scale);
- // shapeRevealProgress = 1 when progress = shapeProgressStart + SHAPE_PROGRESS_DURATION
- float toMax = isOpening ? 1 / SHAPE_PROGRESS_DURATION : 1f;
- float shapeRevealProgress = Utilities.boundToRange(mapToRange(
- Math.max(shapeProgressStart, progress), shapeProgressStart, 1f, 0, toMax,
- LINEAR), 0, 1);
-
- if (mIsVerticalBarLayout) {
- mOutline.right = (int) (rect.width() / scale);
- } else {
- mOutline.bottom = (int) (rect.height() / scale);
- }
-
- mTaskCornerRadius = cornerRadius / scale;
- if (mIsAdaptiveIcon) {
- if (!isOpening && progress >= shapeProgressStart) {
- if (mRevealAnimator == null) {
- mRevealAnimator = (ValueAnimator) IconShape.getShape().createRevealAnimator(
- this, mStartRevealRect, mOutline, mTaskCornerRadius, !isOpening);
- mRevealAnimator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- mRevealAnimator = null;
- }
- });
- mRevealAnimator.start();
- // We pause here so we can set the current fraction ourselves.
- mRevealAnimator.pause();
- }
- mRevealAnimator.setCurrentFraction(shapeRevealProgress);
- }
-
- float drawableScale = (mIsVerticalBarLayout ? mOutline.width() : mOutline.height())
- / minSize;
- setBackgroundDrawableBounds(drawableScale);
- if (isOpening) {
- // Center align foreground
- int height = mFinalDrawableBounds.height();
- int width = mFinalDrawableBounds.width();
- int diffY = mIsVerticalBarLayout ? 0
- : (int) (((height * drawableScale) - height) / 2);
- int diffX = mIsVerticalBarLayout ? (int) (((width * drawableScale) - width) / 2)
- : 0;
- sTmpRect.set(mFinalDrawableBounds);
- sTmpRect.offset(diffX, diffY);
- mForeground.setBounds(sTmpRect);
- } else {
- // Spring the foreground relative to the icon's movement within the DragLayer.
- int diffX = (int) (dX / mLauncher.getDeviceProfile().availableWidthPx
- * FG_TRANS_X_FACTOR);
- int diffY = (int) (dY / mLauncher.getDeviceProfile().availableHeightPx
- * FG_TRANS_Y_FACTOR);
-
- mFgSpringX.animateToFinalPosition(diffX);
- mFgSpringY.animateToFinalPosition(diffY);
- }
- }
invalidate();
- invalidateOutline();
}
@Override
@@ -301,9 +183,7 @@
mEndRunnable.run();
} else {
// End runnable also ends the reveal animator, so we manually handle it here.
- if (mRevealAnimator != null) {
- mRevealAnimator.end();
- }
+ mClipIconView.endReveal();
}
}
@@ -315,23 +195,25 @@
*/
private void matchPositionOf(Launcher launcher, View v, boolean isOpening, RectF positionOut) {
float rotation = getLocationBoundsForView(launcher, v, isOpening, positionOut);
- final LayoutParams lp = new LayoutParams(
+ final InsettableFrameLayout.LayoutParams lp = new InsettableFrameLayout.LayoutParams(
Math.round(positionOut.width()),
Math.round(positionOut.height()));
updatePosition(rotation, positionOut, lp);
setLayoutParams(lp);
+
+ mClipIconView.setLayoutParams(new FrameLayout.LayoutParams(lp.width, lp.height));
}
- private void updatePosition(float rotation, RectF position, LayoutParams lp) {
+ private void updatePosition(float rotation, RectF pos, InsettableFrameLayout.LayoutParams lp) {
mRotation = rotation;
- mPositionOut.set(position);
+ mPositionOut.set(pos);
lp.ignoreInsets = true;
// Position the floating view exactly on top of the original
- lp.topMargin = Math.round(position.top);
+ lp.topMargin = Math.round(pos.top);
if (mIsRtl) {
- lp.setMarginStart(Math.round(mLauncher.getDeviceProfile().widthPx - position.right));
+ lp.setMarginStart(Math.round(mLauncher.getDeviceProfile().widthPx - pos.right));
} else {
- lp.setMarginStart(Math.round(position.left));
+ lp.setMarginStart(Math.round(pos.left));
}
// Set the properties here already to make sure they are available when running the first
// animation frame.
@@ -412,9 +294,8 @@
drawable = originalView.getBackground();
}
} else {
- boolean isFolderIcon = originalView instanceof FolderIcon;
- int width = isFolderIcon ? originalView.getWidth() : (int) pos.width();
- int height = isFolderIcon ? originalView.getHeight() : (int) pos.height();
+ int width = (int) pos.width();
+ int height = (int) pos.height();
if (supportsAdaptiveIcons) {
drawable = getFullDrawable(l, info, width, height, sTmpObjArray);
if (drawable instanceof AdaptiveIconDrawable) {
@@ -451,110 +332,42 @@
/**
* Sets the drawables of the {@param originalView} onto this view.
*
- * @param originalView The View that the FloatingIconView will replace.
* @param drawable The drawable of the original view.
* @param badge The badge of the original view.
* @param iconOffset The amount of offset needed to match this view with the original view.
*/
@UiThread
- private void setIcon(View originalView, @Nullable Drawable drawable, @Nullable Drawable badge,
- int iconOffset) {
+ private void setIcon(@Nullable Drawable drawable, @Nullable Drawable badge, int iconOffset) {
+ final InsettableFrameLayout.LayoutParams lp =
+ (InsettableFrameLayout.LayoutParams) getLayoutParams();
mBadge = badge;
-
- mIsAdaptiveIcon = drawable instanceof AdaptiveIconDrawable;
- if (mIsAdaptiveIcon) {
- boolean isFolderIcon = drawable instanceof FolderAdaptiveIcon;
-
- AdaptiveIconDrawable adaptiveIcon = (AdaptiveIconDrawable) drawable;
- Drawable background = adaptiveIcon.getBackground();
- if (background == null) {
- background = new ColorDrawable(Color.TRANSPARENT);
- }
- mBackground = background;
- Drawable foreground = adaptiveIcon.getForeground();
- if (foreground == null) {
- foreground = new ColorDrawable(Color.TRANSPARENT);
- }
- mForeground = foreground;
-
- final LayoutParams lp = (LayoutParams) getLayoutParams();
+ mClipIconView.setIcon(drawable, iconOffset, lp, mIsOpening);
+ if (drawable instanceof AdaptiveIconDrawable) {
final int originalHeight = lp.height;
final int originalWidth = lp.width;
- int blurMargin = mBlurSizeOutline / 2;
mFinalDrawableBounds.set(0, 0, originalWidth, originalHeight);
- if (!isFolderIcon) {
- mFinalDrawableBounds.inset(iconOffset - blurMargin, iconOffset - blurMargin);
- }
- mForeground.setBounds(mFinalDrawableBounds);
- mBackground.setBounds(mFinalDrawableBounds);
-
- mStartRevealRect.set(0, 0, originalWidth, originalHeight);
-
- if (mBadge != null) {
- mBadge.setBounds(mStartRevealRect);
- if (!mIsOpening && !isFolderIcon) {
- DRAWABLE_ALPHA.set(mBadge, 0);
- }
- }
-
- if (isFolderIcon) {
- ((FolderIcon) originalView).getPreviewBounds(sTmpRect);
- float bgStroke = ((FolderIcon) originalView).getBackgroundStrokeWidth();
- if (mForeground instanceof ShiftedBitmapDrawable) {
- ShiftedBitmapDrawable sbd = (ShiftedBitmapDrawable) mForeground;
- sbd.setShiftX(sbd.getShiftX() - sTmpRect.left - bgStroke);
- sbd.setShiftY(sbd.getShiftY() - sTmpRect.top - bgStroke);
- }
- if (mBadge instanceof ShiftedBitmapDrawable) {
- ShiftedBitmapDrawable sbd = (ShiftedBitmapDrawable) mBadge;
- sbd.setShiftX(sbd.getShiftX() - sTmpRect.left - bgStroke);
- sbd.setShiftY(sbd.getShiftY() - sTmpRect.top - bgStroke);
- }
- } else {
- Utilities.scaleRectAboutCenter(mStartRevealRect,
- IconShape.getNormalizationScale());
- }
-
float aspectRatio = mLauncher.getDeviceProfile().aspectRatio;
if (mIsVerticalBarLayout) {
lp.width = (int) Math.max(lp.width, lp.height * aspectRatio);
} else {
lp.height = (int) Math.max(lp.height, lp.width * aspectRatio);
}
+ setLayoutParams(lp);
- int left = mIsRtl
- ? mLauncher.getDeviceProfile().widthPx - lp.getMarginStart() - lp.width
- : lp.leftMargin;
- layout(left, lp.topMargin, left + lp.width, lp.topMargin + lp.height);
+ final LayoutParams clipViewLp = (LayoutParams) mClipIconView.getLayoutParams();
+ final int clipViewOgHeight = clipViewLp.height;
+ final int clipViewOgWidth = clipViewLp.width;
+ clipViewLp.width = lp.width;
+ clipViewLp.height = lp.height;
+ mClipIconView.setLayoutParams(clipViewLp);
- float scale = Math.max((float) lp.height / originalHeight,
- (float) lp.width / originalWidth);
- float bgDrawableStartScale;
- if (mIsOpening) {
- bgDrawableStartScale = 1f;
- mOutline.set(0, 0, originalWidth, originalHeight);
- } else {
- bgDrawableStartScale = scale;
- mOutline.set(0, 0, lp.width, lp.height);
+ if (mBadge != null) {
+ mBadge.setBounds(0, 0, clipViewOgWidth, clipViewOgHeight);
}
- setBackgroundDrawableBounds(bgDrawableStartScale);
- mEndRevealRect.set(0, 0, lp.width, lp.height);
- setOutlineProvider(new ViewOutlineProvider() {
- @Override
- public void getOutline(View view, Outline outline) {
- outline.setRoundRect(mOutline, mTaskCornerRadius);
- }
- });
- setClipToOutline(true);
- } else {
- setBackground(drawable);
- setClipToOutline(false);
}
-
invalidate();
- invalidateOutline();
}
/**
@@ -571,7 +384,7 @@
synchronized (mIconLoadResult) {
if (mIconLoadResult.isIconLoaded) {
- setIcon(originalView, mIconLoadResult.drawable, mIconLoadResult.badge,
+ setIcon(mIconLoadResult.drawable, mIconLoadResult.badge,
mIconLoadResult.iconOffset);
hideOriginalView(originalView);
} else {
@@ -580,7 +393,7 @@
return;
}
- setIcon(originalView, mIconLoadResult.drawable, mIconLoadResult.badge,
+ setIcon(mIconLoadResult.drawable, mIconLoadResult.badge,
mIconLoadResult.iconOffset);
setVisibility(VISIBLE);
@@ -600,23 +413,12 @@
}
}
- private void setBackgroundDrawableBounds(float scale) {
- sTmpRect.set(mFinalDrawableBounds);
- Utilities.scaleRectAboutCenter(sTmpRect, scale);
- // Since the drawable is at the top of the view, we need to offset to keep it centered.
- if (mIsVerticalBarLayout) {
- sTmpRect.offsetTo((int) (mFinalDrawableBounds.left * scale), sTmpRect.top);
- } else {
- sTmpRect.offsetTo(sTmpRect.left, (int) (mFinalDrawableBounds.top * scale));
- }
- mBackground.setBounds(sTmpRect);
- }
-
@WorkerThread
@SuppressWarnings("WrongThread")
private static int getOffsetForIconBounds(Launcher l, Drawable drawable, RectF position) {
- if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O ||
- !(drawable instanceof AdaptiveIconDrawable)) {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O
+ || !(drawable instanceof AdaptiveIconDrawable)
+ || (drawable instanceof FolderAdaptiveIcon)) {
return 0;
}
int blurSizeOutline =
@@ -640,29 +442,11 @@
}
@Override
- public void setClipPath(Path clipPath) {
- mClipPath = clipPath;
- invalidate();
- }
-
- @Override
- public void draw(Canvas canvas) {
+ protected void dispatchDraw(Canvas canvas) {
int count = canvas.save();
canvas.rotate(mRotation,
mFinalDrawableBounds.exactCenterX(), mFinalDrawableBounds.exactCenterY());
- if (mClipPath != null) {
- canvas.clipPath(mClipPath);
- }
- super.draw(canvas);
- if (mBackground != null) {
- mBackground.draw(canvas);
- }
- if (mForeground != null) {
- int count2 = canvas.save();
- canvas.translate(mFgTransX, mFgTransY);
- mForeground.draw(canvas);
- canvas.restoreToCount(count2);
- }
+ super.dispatchDraw(canvas);
if (mBadge != null) {
mBadge.draw(canvas);
}
@@ -706,7 +490,8 @@
float rotation = getLocationBoundsForView(mLauncher, mOriginalIcon, mIsOpening,
sTmpRectF);
if (rotation != mRotation || !sTmpRectF.equals(mPositionOut)) {
- updatePosition(rotation, sTmpRectF, (LayoutParams) getLayoutParams());
+ updatePosition(rotation, sTmpRectF,
+ (InsettableFrameLayout.LayoutParams) getLayoutParams());
if (mOnTargetChangeRunnable != null) {
mOnTargetChangeRunnable.run();
}
@@ -822,12 +607,6 @@
}
});
- if (mBadge != null) {
- ObjectAnimator badgeFade = ObjectAnimator.ofInt(mBadge, DRAWABLE_ALPHA, 255);
- badgeFade.addUpdateListener(valueAnimator -> invalidate());
- fade.play(badgeFade);
- }
-
if (originalView instanceof IconLabelDotView) {
IconLabelDotView view = (IconLabelDotView) originalView;
fade.addListener(new AnimatorListenerAdapter() {
@@ -869,21 +648,12 @@
setScaleX(1);
setScaleY(1);
setAlpha(1);
- setBackground(null);
if (mLoadIconSignal != null) {
mLoadIconSignal.cancel();
}
mLoadIconSignal = null;
mEndRunnable = null;
- mIsAdaptiveIcon = false;
- mForeground = null;
- mBackground = null;
- mClipPath = null;
mFinalDrawableBounds.setEmpty();
- if (mRevealAnimator != null) {
- mRevealAnimator.cancel();
- }
- mRevealAnimator = null;
if (mFadeAnimatorSet != null) {
mFadeAnimatorSet.cancel();
}
@@ -892,15 +662,10 @@
mListenerView.setListener(null);
mOriginalIcon = null;
mOnTargetChangeRunnable = null;
- mTaskCornerRadius = 0;
- mOutline.setEmpty();
- mFgTransY = 0;
- mFgSpringX.cancel();
- mFgTransX = 0;
- mFgSpringY.cancel();
mBadge = null;
sTmpObjArray[0] = null;
mIconLoadResult = null;
+ mClipIconView.recycle();
}
private static class IconLoadResult {
@@ -911,7 +676,7 @@
Runnable onIconLoaded;
boolean isIconLoaded;
- public IconLoadResult(ItemInfo itemInfo) {
+ IconLoadResult(ItemInfo itemInfo) {
this.itemInfo = itemInfo;
}
}
diff --git a/src/com/android/launcher3/views/WorkFooterContainer.java b/src/com/android/launcher3/views/WorkFooterContainer.java
deleted file mode 100644
index 9ac8230..0000000
--- a/src/com/android/launcher3/views/WorkFooterContainer.java
+++ /dev/null
@@ -1,135 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.launcher3.views;
-
-import static com.android.launcher3.util.PackageManagerHelper.hasShortcutsPermission;
-
-import android.animation.ObjectAnimator;
-import android.animation.PropertyValuesHolder;
-import android.content.Context;
-import android.content.pm.PackageManager;
-import android.graphics.Rect;
-import android.util.AttributeSet;
-import android.view.View;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-
-import com.android.launcher3.Insettable;
-import com.android.launcher3.Launcher;
-import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
-import com.android.launcher3.allapps.WorkModeSwitch;
-import com.android.launcher3.pm.UserCache;
-
-/**
- * Container to show work footer in all-apps.
- */
-public class WorkFooterContainer extends LinearLayout implements Insettable {
- private Rect mInsets = new Rect();
-
- private WorkModeSwitch mWorkModeSwitch;
- private TextView mWorkModeLabel;
-
- protected final ObjectAnimator mOpenCloseAnimator;
-
- public WorkFooterContainer(Context context) {
- this(context, null, 0);
- }
-
- public WorkFooterContainer(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public WorkFooterContainer(Context context, AttributeSet attrs, int defStyleAttr) {
- super(context, attrs, defStyleAttr);
- mOpenCloseAnimator = ObjectAnimator.ofPropertyValuesHolder(this);
- }
-
- @Override
- protected void onLayout(boolean changed, int l, int t, int r, int b) {
- super.onLayout(changed, l, t, r, b);
- updateTranslation();
- this.setVisibility(shouldShowWorkFooter() ? VISIBLE : GONE);
- }
-
- @Override
- protected void onFinishInflate() {
- super.onFinishInflate();
- mWorkModeSwitch = findViewById(R.id.work_mode_toggle);
- mWorkModeLabel = findViewById(R.id.work_mode_label);
- }
-
- @Override
- public void offsetTopAndBottom(int offset) {
- super.offsetTopAndBottom(offset);
- updateTranslation();
- }
-
- private void updateTranslation() {
- if (getParent() instanceof View) {
- View parent = (View) getParent();
- int availableBot = parent.getHeight() - parent.getPaddingBottom();
- setTranslationY(Math.max(0, availableBot - getBottom()));
- }
- }
-
- @Override
- public void setInsets(Rect insets) {
- int bottomInset = insets.bottom - mInsets.bottom;
- mInsets.set(insets);
- setPadding(getPaddingLeft(), getPaddingTop(), getPaddingRight(),
- getPaddingBottom() + bottomInset);
- }
-
- /**
- * Animates in/out work profile toggle panel based on the tab user is on
- */
- public void setWorkTabVisible(boolean workTabVisible) {
- if (!shouldShowWorkFooter()) return;
-
- mOpenCloseAnimator.setValues(PropertyValuesHolder.ofFloat(ALPHA, workTabVisible ? 1 : 0));
- mOpenCloseAnimator.start();
- }
-
- /**
- * Refreshes views based on current work profile enabled status
- */
- public void refresh() {
- if (!shouldShowWorkFooter()) return;
- boolean anyProfileQuietModeEnabled = UserCache.INSTANCE.get(
- getContext()).isAnyProfileQuietModeEnabled();
-
- mWorkModeLabel.setText(anyProfileQuietModeEnabled
- ? R.string.work_mode_off_label : R.string.work_mode_on_label);
- mWorkModeLabel.setCompoundDrawablesWithIntrinsicBounds(
- anyProfileQuietModeEnabled ? R.drawable.ic_corp_off : R.drawable.ic_corp, 0, 0, 0);
- mWorkModeSwitch.refresh();
- }
-
- /**
- * Returns work mode switch
- */
- public WorkModeSwitch getWorkModeSwitch() {
- return mWorkModeSwitch;
- }
-
- private boolean shouldShowWorkFooter() {
- Launcher launcher = Launcher.getLauncher(getContext());
- return Utilities.ATLEAST_P && (hasShortcutsPermission(launcher)
- || launcher.checkSelfPermission("android.permission.MODIFY_QUIET_MODE")
- == PackageManager.PERMISSION_GRANTED);
- }
-}
diff --git a/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java b/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java
index f3fd7ca..c1310e3 100644
--- a/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java
+++ b/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java
@@ -20,6 +20,7 @@
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.PointF;
+import android.graphics.Rect;
import android.os.Handler;
import android.os.SystemClock;
import android.util.SparseBooleanArray;
@@ -44,6 +45,7 @@
import com.android.launcher3.StylusEventHelper;
import com.android.launcher3.Utilities;
import com.android.launcher3.dragndrop.DragLayer;
+import com.android.launcher3.dragndrop.DraggableView;
import com.android.launcher3.util.Executors;
import com.android.launcher3.util.Themes;
import com.android.launcher3.views.BaseDragLayer.TouchCompleteListener;
@@ -52,7 +54,7 @@
* {@inheritDoc}
*/
public class LauncherAppWidgetHostView extends NavigableAppWidgetHostView
- implements TouchCompleteListener, View.OnLongClickListener {
+ implements TouchCompleteListener, View.OnLongClickListener, DraggableView {
// Related to the auto-advancing of widgets
private static final long ADVANCE_INTERVAL = 20000;
@@ -412,4 +414,18 @@
}
return false;
}
+
+ @Override
+ public int getViewType() {
+ return DRAGGABLE_WIDGET;
+ }
+
+ @Override
+ public void getVisualDragBounds(Rect bounds) {
+ int x = (int) (1 - getScaleToFit()) * getMeasuredWidth() / 2;
+ int y = (int) (1 - getScaleToFit()) * getMeasuredWidth() / 2;
+ int width = (int) getScaleToFit() * getMeasuredWidth();
+ int height = (int) getScaleToFit() * getMeasuredHeight();
+ bounds.set(x, y , x + width, y + height);
+ }
}
diff --git a/src/com/android/launcher3/widget/NavigableAppWidgetHostView.java b/src/com/android/launcher3/widget/NavigableAppWidgetHostView.java
index 68b1595..104ad77 100644
--- a/src/com/android/launcher3/widget/NavigableAppWidgetHostView.java
+++ b/src/com/android/launcher3/widget/NavigableAppWidgetHostView.java
@@ -24,12 +24,15 @@
import android.view.ViewDebug;
import android.view.ViewGroup;
+import com.android.launcher3.dragndrop.DraggableView;
+
import java.util.ArrayList;
/**
* Extension of AppWidgetHostView with support for controlled keyboard navigation.
*/
-public abstract class NavigableAppWidgetHostView extends AppWidgetHostView {
+public abstract class NavigableAppWidgetHostView extends AppWidgetHostView
+ implements DraggableView {
@ViewDebug.ExportedProperty(category = "launcher")
private boolean mChildrenFocused;
@@ -133,4 +136,14 @@
// The host view's background changes when selected, to indicate the focus is inside.
setSelected(childIsFocused);
}
+
+ @Override
+ public int getViewType() {
+ return DRAGGABLE_WIDGET;
+ }
+
+ @Override
+ public void getVisualDragBounds(Rect bounds) {
+ bounds.set(0, 0 , getMeasuredWidth(), getMeasuredHeight());
+ }
}
diff --git a/src/com/android/launcher3/widget/PendingItemDragHelper.java b/src/com/android/launcher3/widget/PendingItemDragHelper.java
index 662e627..3c11274 100644
--- a/src/com/android/launcher3/widget/PendingItemDragHelper.java
+++ b/src/com/android/launcher3/widget/PendingItemDragHelper.java
@@ -32,6 +32,7 @@
import com.android.launcher3.PendingAddItemInfo;
import com.android.launcher3.R;
import com.android.launcher3.dragndrop.DragOptions;
+import com.android.launcher3.dragndrop.DraggableView;
import com.android.launcher3.dragndrop.LivePreviewWidgetCell;
import com.android.launcher3.graphics.DragPreviewProvider;
import com.android.launcher3.icons.LauncherIcons;
@@ -79,6 +80,8 @@
mEstimatedCellSize = launcher.getWorkspace().estimateItemSize(mAddInfo);
+ DraggableView draggableView;
+
if (mAddInfo instanceof PendingAddWidgetInfo) {
PendingAddWidgetInfo createWidgetInfo = (PendingAddWidgetInfo) mAddInfo;
@@ -110,6 +113,7 @@
dragOffset = null;
dragRegion = null;
+ draggableView = DraggableView.ofType(DraggableView.DRAGGABLE_WIDGET);
} else {
PendingAddShortcutInfo createShortcutInfo = (PendingAddShortcutInfo) mAddInfo;
Drawable icon = createShortcutInfo.activityInfo.getFullResIcon(app.getIconCache());
@@ -136,6 +140,7 @@
dragRegion.top = (mEstimatedCellSize[1]
- iconSize - dp.iconTextSizePx - dp.iconDrawablePaddingPx) / 2;
dragRegion.bottom = dragRegion.top + iconSize;
+ draggableView = DraggableView.ofType(DraggableView.DRAGGABLE_ICON);
}
// Since we are not going through the workspace for starting the drag, set drag related
@@ -148,8 +153,8 @@
+ (int) ((scale * preview.getHeight() - preview.getHeight()) / 2);
// Start the drag
- launcher.getDragController().startDrag(preview, dragLayerX, dragLayerY, source, mAddInfo,
- dragOffset, dragRegion, scale, scale, options);
+ launcher.getDragController().startDrag(preview, draggableView, dragLayerX, dragLayerY,
+ source, mAddInfo, dragOffset, dragRegion, scale, scale, options);
}
@Override
diff --git a/src/com/android/launcher3/widget/WidgetsBottomSheet.java b/src/com/android/launcher3/widget/WidgetsBottomSheet.java
index a7078a2..8d1a3b0 100644
--- a/src/com/android/launcher3/widget/WidgetsBottomSheet.java
+++ b/src/com/android/launcher3/widget/WidgetsBottomSheet.java
@@ -16,8 +16,8 @@
package com.android.launcher3.widget;
-import android.animation.Animator;
-import android.animation.ObjectAnimator;
+import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
+
import android.animation.PropertyValuesHolder;
import android.content.Context;
import android.graphics.Rect;
@@ -28,16 +28,15 @@
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
+import android.view.animation.Interpolator;
import android.widget.TextView;
-import androidx.annotation.Nullable;
-
import com.android.launcher3.Insettable;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.R;
import com.android.launcher3.ResourceUtils;
-import com.android.launcher3.anim.Interpolators;
+import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.model.WidgetItem;
import com.android.launcher3.util.PackageUserKey;
@@ -156,7 +155,7 @@
setupNavBarColor();
mOpenCloseAnimator.setValues(
PropertyValuesHolder.ofFloat(TRANSLATION_SHIFT, TRANSLATION_SHIFT_OPENED));
- mOpenCloseAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
+ mOpenCloseAnimator.setInterpolator(FAST_OUT_SLOW_IN);
mOpenCloseAnimator.start();
}
@@ -191,9 +190,9 @@
mIsOpen ? R.string.widgets_list : R.string.widgets_list_closed));
}
- @Nullable
@Override
- public Animator createHintCloseAnim(float distanceToMove) {
- return ObjectAnimator.ofInt(this, PADDING_BOTTOM, (int) (distanceToMove + mInsets.bottom));
+ public void addHintCloseAnim(
+ float distanceToMove, Interpolator interpolator, PendingAnimation target) {
+ target.setInt(this, PADDING_BOTTOM, (int) (distanceToMove + mInsets.bottom), interpolator);
}
}
diff --git a/src/com/android/launcher3/widget/WidgetsFullSheet.java b/src/com/android/launcher3/widget/WidgetsFullSheet.java
index 2a102d2..b07a4f4 100644
--- a/src/com/android/launcher3/widget/WidgetsFullSheet.java
+++ b/src/com/android/launcher3/widget/WidgetsFullSheet.java
@@ -15,12 +15,11 @@
*/
package com.android.launcher3.widget;
+import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y;
import static com.android.launcher3.testing.TestProtocol.NORMAL_STATE_ORDINAL;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
-import android.animation.AnimatorSet;
-import android.animation.ObjectAnimator;
import android.animation.PropertyValuesHolder;
import android.content.Context;
import android.graphics.Rect;
@@ -30,8 +29,8 @@
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.AnimationUtils;
+import android.view.animation.Interpolator;
-import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import com.android.launcher3.Insettable;
@@ -39,6 +38,7 @@
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherAppWidgetHost.ProviderChangedListener;
import com.android.launcher3.R;
+import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.compat.AccessibilityManagerCompat;
import com.android.launcher3.views.RecyclerViewFastScroller;
import com.android.launcher3.views.TopRoundedCornerView;
@@ -242,13 +242,11 @@
return mAdapter.getItemCount();
}
- @Nullable
@Override
- public Animator createHintCloseAnim(float distanceToMove) {
- AnimatorSet anim = new AnimatorSet();
- anim.play(ObjectAnimator.ofFloat(mRecyclerView, TRANSLATION_Y, -distanceToMove));
- anim.play(ObjectAnimator.ofFloat(mRecyclerView, ALPHA, 0.5f));
- return anim;
+ public void addHintCloseAnim(
+ float distanceToMove, Interpolator interpolator, PendingAnimation target) {
+ target.setFloat(mRecyclerView, VIEW_TRANSLATE_Y, -distanceToMove, interpolator);
+ target.setViewAlpha(mRecyclerView, 0.5f, interpolator);
}
@Override
diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/BackgroundBlurController.java b/src_ui_overrides/com/android/launcher3/uioverrides/BackgroundBlurController.java
deleted file mode 100644
index 232bad3..0000000
--- a/src_ui_overrides/com/android/launcher3/uioverrides/BackgroundBlurController.java
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher3.uioverrides;
-
-
-import android.util.IntProperty;
-import android.view.View;
-import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherState;
-import com.android.launcher3.LauncherStateManager;
-import com.android.launcher3.anim.AnimatorSetBuilder;
-
-/**
- * Controls the blur, for the Launcher surface only.
- */
-public class BackgroundBlurController implements LauncherStateManager.StateHandler {
-
- public static final IntProperty<BackgroundBlurController> BACKGROUND_BLUR =
- new IntProperty<BackgroundBlurController>("backgroundBlur") {
- @Override
- public void setValue(BackgroundBlurController blurController, int blurRadius) {}
-
- @Override
- public Integer get(BackgroundBlurController blurController) {
- return 0;
- }
- };
-
- public BackgroundBlurController(Launcher l) {}
-
- public int getFolderBackgroundBlurAdjustment() {
- return 0;
- }
-
- public void setSurfaceToLauncher(View v) {}
-
- @Override
- public void setState(LauncherState toState) {}
-
- @Override
- public void setStateWithAnimation(LauncherState toState, AnimatorSetBuilder builder,
- LauncherStateManager.AnimationConfig config) {}
-}
diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/DepthController.java b/src_ui_overrides/com/android/launcher3/uioverrides/DepthController.java
new file mode 100644
index 0000000..7ad85e2
--- /dev/null
+++ b/src_ui_overrides/com/android/launcher3/uioverrides/DepthController.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.uioverrides;
+
+
+import android.util.FloatProperty;
+import android.view.View;
+
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherState;
+import com.android.launcher3.LauncherStateManager;
+import com.android.launcher3.anim.PendingAnimation;
+import com.android.launcher3.states.StateAnimationConfig;
+
+/**
+ * Controls blur and wallpaper zoom, for the Launcher surface only.
+ */
+public class DepthController implements LauncherStateManager.StateHandler {
+
+ public static final FloatProperty<DepthController> DEPTH =
+ new FloatProperty<DepthController>("depth") {
+ @Override
+ public void setValue(DepthController depthController, float depth) {}
+
+ @Override
+ public Float get(DepthController depthController) {
+ return 0f;
+ }
+ };
+
+ public DepthController(Launcher l) {}
+
+ public void setSurfaceToLauncher(View v) {}
+
+ @Override
+ public void setState(LauncherState toState) {}
+
+ @Override
+ public void setStateWithAnimation(LauncherState toState, StateAnimationConfig config,
+ PendingAnimation animation) { }
+}
diff --git a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
index 3d12248..7cd656e 100644
--- a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
+++ b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
@@ -18,7 +18,6 @@
import static androidx.test.InstrumentationRegistry.getInstrumentation;
import static com.android.launcher3.WorkspaceLayoutManager.FIRST_SCREEN_ID;
-import static com.android.launcher3.tapl.LauncherInstrumentation.ContainerType;
import static com.android.launcher3.ui.TaplTestsLauncher3.getAppPackageName;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
@@ -57,6 +56,7 @@
import com.android.launcher3.Utilities;
import com.android.launcher3.model.AppLaunchTracker;
import com.android.launcher3.tapl.LauncherInstrumentation;
+import com.android.launcher3.tapl.LauncherInstrumentation.ContainerType;
import com.android.launcher3.tapl.TestHelpers;
import com.android.launcher3.testcomponent.TestCommandReceiver;
import com.android.launcher3.testing.TestProtocol;
@@ -102,6 +102,7 @@
private static String sDetectedActivityLeak;
private static boolean sActivityLeakReported;
+ private static final String SYSTEMUI_PACKAGE = "com.android.systemui";
protected LooperExecutor mMainThreadExecutor = MAIN_EXECUTOR;
protected final UiDevice mDevice = UiDevice.getInstance(getInstrumentation());
@@ -213,8 +214,26 @@
return mDevice;
}
+ private boolean hasSystemUiObject(String resId) {
+ return mDevice.hasObject(By.res(SYSTEMUI_PACKAGE, resId));
+ }
+
@Before
public void setUp() throws Exception {
+ Log.d(TAG, "Before disabling battery defender");
+ mDevice.executeShellCommand("setprop vendor.battery.defender.disable 1");
+ Log.d(TAG, "Before enabling stay awake");
+ mDevice.executeShellCommand("settings put global stay_on_while_plugged_in 3");
+ for (int i = 0; i < 10 && hasSystemUiObject("keyguard_status_view"); ++i) {
+ Log.d(TAG, "Before unlocking the phone");
+ mDevice.executeShellCommand("input keyevent 82");
+ mDevice.waitForIdle();
+ }
+ Assert.assertTrue("Keyguard still visible",
+ mDevice.wait(
+ Until.gone(By.res(SYSTEMUI_PACKAGE, "keyguard_status_view")), 60000));
+ Log.d(TAG, "Keyguard is not visible");
+
final String launcherPackageName = mDevice.getLauncherPackageName();
try {
final Context context = InstrumentationRegistry.getContext();
@@ -275,7 +294,8 @@
protected void resetLoaderState() {
try {
mMainThreadExecutor.execute(
- () -> LauncherAppState.getInstance(mTargetContext).getModel().forceReload());
+ () -> LauncherAppState.getInstance(
+ mTargetContext).getModel().forceReload());
} catch (Throwable t) {
throw new IllegalArgumentException(t);
}
@@ -289,7 +309,8 @@
ContentResolver resolver = mTargetContext.getContentResolver();
int screenId = FIRST_SCREEN_ID;
// Update the screen id counter for the provider.
- LauncherSettings.Settings.call(resolver, LauncherSettings.Settings.METHOD_NEW_SCREEN_ID);
+ LauncherSettings.Settings.call(resolver,
+ LauncherSettings.Settings.METHOD_NEW_SCREEN_ID);
if (screenId > FIRST_SCREEN_ID) {
screenId = FIRST_SCREEN_ID;
@@ -303,7 +324,8 @@
item.screenId = screenId;
item.onAddToDatabase(writer);
writer.put(LauncherSettings.Favorites._ID, item.id);
- resolver.insert(LauncherSettings.Favorites.CONTENT_URI, writer.getValues(mTargetContext));
+ resolver.insert(LauncherSettings.Favorites.CONTENT_URI,
+ writer.getValues(mTargetContext));
resetLoaderState();
// Launch the home activity
@@ -334,7 +356,8 @@
});
}
- // Cannot be used in TaplTests between a Tapl call injecting a gesture and a tapl call expecting
+ // Cannot be used in TaplTests between a Tapl call injecting a gesture and a tapl call
+ // expecting
// the results of that gesture because the wait can hide flakeness.
protected void waitForState(String message, Supplier<LauncherState> state) {
waitForLauncherCondition(message,
@@ -347,7 +370,8 @@
// Cannot be used in TaplTests after injecting any gesture using Tapl because this can hide
// flakiness.
- protected void waitForLauncherCondition(String message, Function<Launcher, Boolean> condition) {
+ protected void waitForLauncherCondition(String
+ message, Function<Launcher, Boolean> condition) {
waitForLauncherCondition(message, condition, DEFAULT_ACTIVITY_TIMEOUT);
}
@@ -423,7 +447,8 @@
public Intent blockingGetExtraIntent() throws InterruptedException {
Intent intent = blockingGetIntent();
- return intent == null ? null : (Intent) intent.getParcelableExtra(Intent.EXTRA_INTENT);
+ return intent == null ? null : (Intent) intent.getParcelableExtra(
+ Intent.EXTRA_INTENT);
}
}
@@ -450,7 +475,8 @@
if (newTask) {
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
} else {
- intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK | Intent.FLAG_ACTIVITY_NEW_DOCUMENT);
+ intent.addFlags(
+ Intent.FLAG_ACTIVITY_MULTIPLE_TASK | Intent.FLAG_ACTIVITY_NEW_DOCUMENT);
}
getInstrumentation().getTargetContext().startActivity(intent);
assertTrue("App didn't start: " + selector,
@@ -487,7 +513,8 @@
protected boolean isInState(Supplier<LauncherState> state) {
if (!TestHelpers.isInLauncherProcess()) return true;
- return getFromLauncher(launcher -> launcher.getStateManager().getState() == state.get());
+ return getFromLauncher(
+ launcher -> launcher.getStateManager().getState() == state.get());
}
protected int getAllAppsScroll(Launcher launcher) {
diff --git a/tests/src/com/android/launcher3/ui/WorkTabTest.java b/tests/src/com/android/launcher3/ui/WorkTabTest.java
index db2d974..8d571ff 100644
--- a/tests/src/com/android/launcher3/ui/WorkTabTest.java
+++ b/tests/src/com/android/launcher3/ui/WorkTabTest.java
@@ -16,6 +16,7 @@
package com.android.launcher3.ui;
import static com.android.launcher3.LauncherState.ALL_APPS;
+import static com.android.launcher3.LauncherState.NORMAL;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
@@ -31,9 +32,9 @@
import com.android.launcher3.R;
import com.android.launcher3.allapps.AllAppsContainerView;
import com.android.launcher3.allapps.AllAppsPagedView;
+import com.android.launcher3.allapps.WorkModeSwitch;
import com.android.launcher3.dragndrop.DragLayer;
import com.android.launcher3.views.WorkEduView;
-import com.android.launcher3.views.WorkFooterContainer;
import org.junit.After;
import org.junit.Before;
@@ -87,7 +88,7 @@
executeOnLauncher(launcher -> launcher.getStateManager().goToState(ALL_APPS));
waitForState("Launcher internal state didn't switch to All Apps", () -> ALL_APPS);
getOnceNotNull("Apps view did not bind",
- launcher -> launcher.getAppsView().getWorkFooterContainer(), 60000);
+ launcher -> launcher.getAppsView().getWorkModeSwitch(), 60000);
UserManager userManager = getFromLauncher(l -> l.getSystemService(UserManager.class));
assertEquals(2, userManager.getUserProfiles().size());
@@ -102,10 +103,10 @@
assertTrue(userManager.isQuietModeEnabled(workProfile));
executeOnLauncher(launcher -> {
- WorkFooterContainer wf = launcher.getAppsView().getWorkFooterContainer();
+ WorkModeSwitch wf = launcher.getAppsView().getWorkModeSwitch();
((AllAppsPagedView) launcher.getAppsView().getContentView()).snapToPageImmediately(
AllAppsContainerView.AdapterHolder.WORK);
- wf.getWorkModeSwitch().toggle();
+ wf.toggle();
});
waitForLauncherCondition("Work toggle did not work",
l -> l.getSystemService(UserManager.class).isQuietModeEnabled(workProfile));
@@ -158,9 +159,11 @@
// dismiss personal edu
mDevice.pressHome();
+ waitForState("Launcher did not go home", () -> NORMAL);
// open work tab
executeOnLauncher(launcher -> launcher.getStateManager().goToState(ALL_APPS));
+ waitForState("Launcher did not switch to all apps", () -> ALL_APPS);
executeOnLauncher(launcher -> {
AllAppsPagedView pagedView = (AllAppsPagedView) launcher.getAppsView().getContentView();
pagedView.setCurrentPage(WORK_PAGE);