Merge "Improving TAPL diagnostics (contains changes in Launcher)" into ub-launcher3-master
diff --git a/Android.bp b/Android.bp
index 1121a79..5acec37 100644
--- a/Android.bp
+++ b/Android.bp
@@ -23,7 +23,6 @@
],
srcs: [
"tests/tapl/**/*.java",
- "quickstep/src/com/android/quickstep/SwipeUpSetting.java",
"src/com/android/launcher3/util/SecureSettingsObserver.java",
"src/com/android/launcher3/TestProtocol.java",
],
diff --git a/go/quickstep/src/com/android/launcher3/uioverrides/RecentsUiFactory.java b/go/quickstep/src/com/android/launcher3/uioverrides/RecentsUiFactory.java
index 321861b..d9b9686 100644
--- a/go/quickstep/src/com/android/launcher3/uioverrides/RecentsUiFactory.java
+++ b/go/quickstep/src/com/android/launcher3/uioverrides/RecentsUiFactory.java
@@ -30,7 +30,7 @@
import com.android.launcher3.uioverrides.touchcontrollers.PortraitStatesTouchController;
import com.android.launcher3.uioverrides.touchcontrollers.StatusBarTouchController;
import com.android.launcher3.util.TouchController;
-import com.android.quickstep.OverviewInteractionState;
+import com.android.quickstep.SysUINavigationMode;
import com.android.quickstep.views.IconRecentsView;
import java.util.ArrayList;
@@ -52,8 +52,8 @@
list.add(new LandscapeStatesTouchController(launcher));
list.add(new LandscapeEdgeSwipeController(launcher));
} else {
- boolean allowDragToOverview = OverviewInteractionState.INSTANCE.get(launcher)
- .isSwipeUpGestureEnabled();
+ boolean allowDragToOverview = SysUINavigationMode.INSTANCE.get(launcher)
+ .getMode().hasGestures;
list.add(new PortraitStatesTouchController(launcher, allowDragToOverview));
}
if (FeatureFlags.PULL_DOWN_STATUS_BAR && Utilities.IS_DEBUG_DEVICE
diff --git a/go/quickstep/src/com/android/quickstep/AppToOverviewAnimationProvider.java b/go/quickstep/src/com/android/quickstep/AppToOverviewAnimationProvider.java
index 051c80f..d1d697c 100644
--- a/go/quickstep/src/com/android/quickstep/AppToOverviewAnimationProvider.java
+++ b/go/quickstep/src/com/android/quickstep/AppToOverviewAnimationProvider.java
@@ -173,8 +173,10 @@
// Keep recents visible throughout the animation.
SurfaceParams[] params = new SurfaceParams[2];
+ // Closing app should stay on top.
+ int boostedMode = MODE_CLOSING;
params[0] = new SurfaceParams(recentsTarget.leash, 1f, null /* matrix */,
- null /* windowCrop */, getLayer(recentsTarget, MODE_OPENING), 0 /* cornerRadius */);
+ null /* windowCrop */, getLayer(recentsTarget, boostedMode), 0 /* cornerRadius */);
valueAnimator.addUpdateListener(new MultiValueUpdateListener() {
private final FloatProp mScaleX;
@@ -214,7 +216,7 @@
m.postTranslate(mTranslationX.value, mTranslationY.value);
params[1] = new SurfaceParams(appTarget.leash, mAlpha.value, m,
- null /* windowCrop */, getLayer(appTarget, MODE_CLOSING),
+ null /* windowCrop */, getLayer(appTarget, boostedMode),
0 /* cornerRadius */);
surfaceApplier.scheduleApply(params);
}
diff --git a/go/quickstep/src/com/android/quickstep/RecentsActivity.java b/go/quickstep/src/com/android/quickstep/RecentsActivity.java
index 447e7e7..f2ca368 100644
--- a/go/quickstep/src/com/android/quickstep/RecentsActivity.java
+++ b/go/quickstep/src/com/android/quickstep/RecentsActivity.java
@@ -67,9 +67,7 @@
@Override
protected void onStart() {
- // Set the alpha to 1 before calling super, as it may get set back to 0 due to
- // onActivityStart callback.
- mIconRecentsView.setAlpha(0);
+ mIconRecentsView.onBeginTransitionToOverview();
super.onStart();
}
}
diff --git a/go/quickstep/src/com/android/quickstep/TaskActionController.java b/go/quickstep/src/com/android/quickstep/TaskActionController.java
index b2d495b..77b287b 100644
--- a/go/quickstep/src/com/android/quickstep/TaskActionController.java
+++ b/go/quickstep/src/com/android/quickstep/TaskActionController.java
@@ -71,7 +71,6 @@
* Clears all tasks and updates the model and view.
*/
public void clearAllTasks() {
- // TODO: Play an animation so transition is more natural.
int count = mAdapter.getItemCount();
ActivityManagerWrapper.getInstance().removeAllRecentTasks();
mLoader.clearAllTasks();
diff --git a/go/quickstep/src/com/android/quickstep/TaskAdapter.java b/go/quickstep/src/com/android/quickstep/TaskAdapter.java
index e56cc51..c98eca6 100644
--- a/go/quickstep/src/com/android/quickstep/TaskAdapter.java
+++ b/go/quickstep/src/com/android/quickstep/TaskAdapter.java
@@ -75,8 +75,20 @@
// Task list has updated.
return;
}
- holder.bindTask(tasks.get(position));
-
+ Task task = tasks.get(position);
+ holder.bindTask(task);
+ mLoader.loadTaskIconAndLabel(task, () -> {
+ // Ensure holder still has the same task.
+ if (task.equals(holder.getTask())) {
+ holder.getTaskItemView().setIcon(task.icon);
+ holder.getTaskItemView().setLabel(task.titleDescription);
+ }
+ });
+ mLoader.loadTaskThumbnail(task, () -> {
+ if (task.equals(holder.getTask())) {
+ holder.getTaskItemView().setThumbnail(task.thumbnail.thumbnail);
+ }
+ });
}
@Override
diff --git a/go/quickstep/src/com/android/quickstep/TaskHolder.java b/go/quickstep/src/com/android/quickstep/TaskHolder.java
index a89229f..744afd7 100644
--- a/go/quickstep/src/com/android/quickstep/TaskHolder.java
+++ b/go/quickstep/src/com/android/quickstep/TaskHolder.java
@@ -35,17 +35,18 @@
mTaskItemView = itemView;
}
+ public TaskItemView getTaskItemView() {
+ return mTaskItemView;
+ }
+
/**
- * Bind task content to the view. This includes the task icon and title as well as binding
- * input handlers such as which task to launch/remove.
+ * Bind a task to the holder, resetting the view and preparing it for content to load in.
*
* @param task the task to bind to the view
*/
public void bindTask(Task task) {
mTask = task;
- mTaskItemView.setLabel(task.titleDescription);
- mTaskItemView.setIcon(task.icon);
- mTaskItemView.setThumbnail(task.thumbnail.thumbnail);
+ mTaskItemView.resetTaskItemView();
}
/**
diff --git a/go/quickstep/src/com/android/quickstep/TaskListLoader.java b/go/quickstep/src/com/android/quickstep/TaskListLoader.java
index c86c24e..1234989 100644
--- a/go/quickstep/src/com/android/quickstep/TaskListLoader.java
+++ b/go/quickstep/src/com/android/quickstep/TaskListLoader.java
@@ -25,7 +25,6 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
-import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;
/**
@@ -39,35 +38,48 @@
private ArrayList<Task> mTaskList = new ArrayList<>();
private int mTaskListChangeId;
+ private RecentsModel.TaskThumbnailChangeListener listener = (taskId, thumbnailData) -> {
+ Task foundTask = null;
+ for (Task task : mTaskList) {
+ if (task.key.id == taskId) {
+ foundTask = task;
+ break;
+ }
+ }
+ if (foundTask != null) {
+ foundTask.thumbnail = thumbnailData;
+ }
+ return foundTask;
+ };
public TaskListLoader(Context context) {
mRecentsModel = RecentsModel.INSTANCE.get(context);
+ mRecentsModel.addThumbnailChangeListener(listener);
}
/**
- * Returns the current task list as of the last completed load (see
- * {@link #loadTaskList}) as a read-only list. This list of tasks is guaranteed to always have
- * all its task content loaded.
+ * Returns the current task list as of the last completed load (see {@link #loadTaskList}) as a
+ * read-only list. This list of tasks is not guaranteed to have all content loaded.
*
- * @return the current list of tasks w/ all content loaded
+ * @return the current list of tasks
*/
public List<Task> getCurrentTaskList() {
return Collections.unmodifiableList(mTaskList);
}
/**
- * Fetches the most recent tasks and updates the task list asynchronously. In addition it
- * loads the content for each task (icon and label). The callback and task list being updated
- * only occur when all task content is fully loaded and up-to-date.
+ * Fetches the most recent tasks and updates the task list asynchronously. This call does not
+ * provide guarantees the task content (icon, thumbnail, label) are loaded but will fill in
+ * what it has. May run the callback immediately if there have been no changes in the task
+ * list.
*
- * @param onTasksLoadedCallback callback for when the tasks are fully loaded. Done on the UI
- * thread
+ * @param onLoadedCallback callback to run when task list is loaded
*/
- public void loadTaskList(@Nullable Consumer<ArrayList<Task>> onTasksLoadedCallback) {
+ public void loadTaskList(@Nullable Consumer<ArrayList<Task>> onLoadedCallback) {
if (mRecentsModel.isTaskListValid(mTaskListChangeId)) {
// Current task list is already up to date. No need to update.
- if (onTasksLoadedCallback != null) {
- onTasksLoadedCallback.accept(mTaskList);
+ if (onLoadedCallback != null) {
+ onLoadedCallback.accept(mTaskList);
}
return;
}
@@ -76,16 +88,46 @@
// Reverse tasks to put most recent at the bottom of the view
Collections.reverse(tasks);
// Load task content
- loadTaskContents(tasks, () -> {
- mTaskList = tasks;
- if (onTasksLoadedCallback != null) {
- onTasksLoadedCallback.accept(mTaskList);
+ for (Task task : tasks) {
+ int loadedPos = mTaskList.indexOf(task);
+ if (loadedPos == -1) {
+ continue;
}
- });
+ Task loadedTask = mTaskList.get(loadedPos);
+ task.icon = loadedTask.icon;
+ task.titleDescription = loadedTask.titleDescription;
+ task.thumbnail = loadedTask.thumbnail;
+ }
+ mTaskList = tasks;
+ onLoadedCallback.accept(tasks);
});
}
/**
+ * Load task icon and label asynchronously if it is not already loaded in the task. If the task
+ * already has an icon, this calls the callback immediately.
+ *
+ * @param task task to update with icon + label
+ * @param onLoadedCallback callback to run when task has icon and label
+ */
+ public void loadTaskIconAndLabel(Task task, @Nullable Runnable onLoadedCallback) {
+ mRecentsModel.getIconCache().updateIconInBackground(task,
+ loadedTask -> onLoadedCallback.run());
+ }
+
+ /**
+ * Load thumbnail asynchronously if not already loaded in the task. If the task already has a
+ * thumbnail or if the thumbnail is cached, this calls the callback immediately.
+ *
+ * @param task task to update with the thumbnail
+ * @param onLoadedCallback callback to run when task has thumbnail
+ */
+ public void loadTaskThumbnail(Task task, @Nullable Runnable onLoadedCallback) {
+ mRecentsModel.getThumbnailCache().updateThumbnailInBackground(task,
+ thumbnail -> onLoadedCallback.run());
+ }
+
+ /**
* Removes the task from the current task list.
*/
void removeTask(Task task) {
@@ -98,42 +140,4 @@
void clearAllTasks() {
mTaskList.clear();
}
-
- /**
- * Loads task content for a list of tasks, including the label, icon, and thumbnail. For content
- * that isn't cached, load the content asynchronously in the background.
- *
- * @param tasksToLoad list of tasks that need to load their content
- * @param onFullyLoadedCallback runnable to run after all tasks have loaded their content
- */
- private void loadTaskContents(ArrayList<Task> tasksToLoad,
- @Nullable Runnable onFullyLoadedCallback) {
- // Make two load requests per task, one for the icon/title and one for the thumbnail.
- AtomicInteger loadRequestsCount = new AtomicInteger(tasksToLoad.size() * 2);
- Runnable itemLoadedRunnable = () -> {
- if (loadRequestsCount.decrementAndGet() == 0 && onFullyLoadedCallback != null) {
- onFullyLoadedCallback.run();
- }
- };
- for (Task task : tasksToLoad) {
- // Load icon and title.
- int index = mTaskList.indexOf(task);
- if (index >= 0) {
- // If we've already loaded the task and have its content then just copy it over.
- Task loadedTask = mTaskList.get(index);
- task.titleDescription = loadedTask.titleDescription;
- task.icon = loadedTask.icon;
- itemLoadedRunnable.run();
- } else {
- // Otherwise, load the content in the background.
- mRecentsModel.getIconCache().updateIconInBackground(task,
- loadedTask -> itemLoadedRunnable.run());
- }
-
- // Load the thumbnail. May return immediately and synchronously if the thumbnail is
- // cached.
- mRecentsModel.getThumbnailCache().updateThumbnailInBackground(task,
- thumbnail -> itemLoadedRunnable.run());
- }
- }
}
diff --git a/go/quickstep/src/com/android/quickstep/fallback/GoRecentsActivityRootView.java b/go/quickstep/src/com/android/quickstep/fallback/GoRecentsActivityRootView.java
index d748e89..c0ebcb5 100644
--- a/go/quickstep/src/com/android/quickstep/fallback/GoRecentsActivityRootView.java
+++ b/go/quickstep/src/com/android/quickstep/fallback/GoRecentsActivityRootView.java
@@ -18,6 +18,7 @@
import android.content.Context;
import android.util.AttributeSet;
+import com.android.launcher3.util.TouchController;
import com.android.launcher3.views.BaseDragLayer;
import com.android.quickstep.RecentsActivity;
@@ -27,5 +28,7 @@
public final class GoRecentsActivityRootView extends BaseDragLayer<RecentsActivity> {
public GoRecentsActivityRootView(Context context, AttributeSet attrs) {
super(context, attrs, 1 /* alphaChannelCount */);
+ // Go leaves touch control to the view itself.
+ mControllers = new TouchController[0];
}
}
diff --git a/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java b/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java
index a1d62c2..5bb4c5a 100644
--- a/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java
+++ b/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java
@@ -19,6 +19,10 @@
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.animation.PropertyValuesHolder;
+import android.animation.ValueAnimator;
import android.content.Context;
import android.util.AttributeSet;
import android.util.FloatProperty;
@@ -65,6 +69,10 @@
}
};
private static final long CROSSFADE_DURATION = 300;
+ private static final long ITEM_ANIMATE_OUT_DURATION = 150;
+ private static final long ITEM_ANIMATE_OUT_DELAY_BETWEEN = 40;
+ private static final float ITEM_ANIMATE_OUT_TRANSLATION_X_RATIO = .25f;
+ private static final long CLEAR_ALL_FADE_DELAY = 120;
/**
* A ratio representing the view's relative placement within its padded space. For example, 0
@@ -81,6 +89,7 @@
private RecyclerView mTaskRecyclerView;
private View mEmptyView;
private View mContentView;
+ private View mClearAllView;
private boolean mTransitionedFromApp;
public IconRecentsView(Context context, AttributeSet attrs) {
@@ -117,12 +126,22 @@
updateContentViewVisibility();
}
});
-
- View clearAllView = findViewById(R.id.clear_all_button);
- clearAllView.setOnClickListener(v -> mTaskActionController.clearAllTasks());
+ mClearAllView = findViewById(R.id.clear_all_button);
+ mClearAllView.setOnClickListener(v -> animateClearAllTasks());
}
}
+
+ @Override
+ public void setEnabled(boolean enabled) {
+ super.setEnabled(enabled);
+ TaskItemView[] itemViews = getTaskViews();
+ for (TaskItemView itemView : itemViews) {
+ itemView.setEnabled(enabled);
+ }
+ mClearAllView.setEnabled(enabled);
+ }
+
/**
* Set activity helper for the view to callback to.
*
@@ -136,8 +155,6 @@
* Logic for when we know we are going to overview/recents and will be putting up the recents
* view. This should be used to prepare recents (e.g. load any task data, etc.) before it
* becomes visible.
- *
- * TODO: Hook this up for fallback recents activity as well
*/
public void onBeginTransitionToOverview() {
// Load any task changes
@@ -195,6 +212,78 @@
}
/**
+ * Clear all tasks and animate out.
+ */
+ private void animateClearAllTasks() {
+ setEnabled(false);
+ TaskItemView[] itemViews = getTaskViews();
+
+ AnimatorSet clearAnim = new AnimatorSet();
+ long currentDelay = 0;
+
+ // Animate each item view to the right and fade out.
+ for (TaskItemView itemView : itemViews) {
+ PropertyValuesHolder transXproperty = PropertyValuesHolder.ofFloat(TRANSLATION_X,
+ 0, itemView.getWidth() * ITEM_ANIMATE_OUT_TRANSLATION_X_RATIO);
+ PropertyValuesHolder alphaProperty = PropertyValuesHolder.ofFloat(ALPHA, 1.0f, 0f);
+ ObjectAnimator itemAnim = ObjectAnimator.ofPropertyValuesHolder(itemView,
+ transXproperty, alphaProperty);
+ itemAnim.setDuration(ITEM_ANIMATE_OUT_DURATION);
+ itemAnim.setStartDelay(currentDelay);
+
+ clearAnim.play(itemAnim);
+ currentDelay += ITEM_ANIMATE_OUT_DELAY_BETWEEN;
+ }
+
+ // Animate view fading and leave recents when faded enough.
+ ValueAnimator contentAlpha = ValueAnimator.ofFloat(1.0f, 0f)
+ .setDuration(CROSSFADE_DURATION);
+ contentAlpha.setStartDelay(CLEAR_ALL_FADE_DELAY);
+ contentAlpha.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ private boolean mLeftRecents = false;
+
+ @Override
+ public void onAnimationUpdate(ValueAnimator valueAnimator) {
+ mContentView.setAlpha((float) valueAnimator.getAnimatedValue());
+ // Leave recents while fading out.
+ if ((float) valueAnimator.getAnimatedValue() < .5f && !mLeftRecents) {
+ mActivityHelper.leaveRecents();
+ mLeftRecents = true;
+ }
+ }
+ });
+
+ clearAnim.play(contentAlpha);
+ clearAnim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ for (TaskItemView itemView : itemViews) {
+ itemView.setTranslationX(0);
+ itemView.setAlpha(1.0f);
+ }
+ setEnabled(true);
+ mContentView.setVisibility(GONE);
+ mTaskActionController.clearAllTasks();
+ }
+ });
+ clearAnim.start();
+ }
+
+ /**
+ * Get attached task item views ordered by most recent.
+ *
+ * @return array of attached task item views
+ */
+ private TaskItemView[] getTaskViews() {
+ int taskCount = mTaskRecyclerView.getChildCount();
+ TaskItemView[] itemViews = new TaskItemView[taskCount];
+ for (int i = 0; i < taskCount; i ++) {
+ itemViews[i] = (TaskItemView) mTaskRecyclerView.getChildAt(i);
+ }
+ return itemViews;
+ }
+
+ /**
* Update the content view so that the appropriate view is shown based off the current list
* of tasks.
*/
diff --git a/go/quickstep/src/com/android/quickstep/views/TaskItemView.java b/go/quickstep/src/com/android/quickstep/views/TaskItemView.java
index 373f107..d831b20 100644
--- a/go/quickstep/src/com/android/quickstep/views/TaskItemView.java
+++ b/go/quickstep/src/com/android/quickstep/views/TaskItemView.java
@@ -17,6 +17,7 @@
import android.content.Context;
import android.graphics.Bitmap;
+import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.View;
@@ -24,6 +25,8 @@
import android.widget.LinearLayout;
import android.widget.TextView;
+import androidx.annotation.Nullable;
+
import com.android.launcher3.R;
/**
@@ -31,12 +34,16 @@
*/
public final class TaskItemView extends LinearLayout {
+ private static final String DEFAULT_LABEL = "...";
+ private final Drawable mDefaultIcon;
private TextView mLabelView;
private ImageView mIconView;
private ImageView mThumbnailView;
public TaskItemView(Context context, AttributeSet attrs) {
super(context, attrs);
+ mDefaultIcon = context.getResources().getDrawable(
+ android.R.drawable.sym_def_app_icon, context.getTheme());
}
@Override
@@ -48,33 +55,56 @@
}
/**
- * Set the label for the task item.
+ * Resets task item view to default values.
+ */
+ public void resetTaskItemView() {
+ setLabel(DEFAULT_LABEL);
+ setIcon(null);
+ setThumbnail(null);
+ }
+
+ /**
+ * Set the label for the task item. Sets to a default label if null.
*
* @param label task label
*/
- public void setLabel(String label) {
+ public void setLabel(@Nullable String label) {
+ if (label == null) {
+ mLabelView.setText(DEFAULT_LABEL);
+ return;
+ }
mLabelView.setText(label);
}
/**
- * Set the icon for the task item.
+ * Set the icon for the task item. Sets to a default icon if null.
*
* @param icon task icon
*/
- public void setIcon(Drawable icon) {
+ public void setIcon(@Nullable Drawable icon) {
// TODO: Scale the icon up based off the padding on the side
// The icon proper is actually smaller than the drawable and has "padding" on the side for
// the purpose of drawing the shadow, allowing the icon to pop up, so we need to scale the
// view if we want the icon to be flush with the bottom of the thumbnail.
+ if (icon == null) {
+ mIconView.setImageDrawable(mDefaultIcon);
+ return;
+ }
mIconView.setImageDrawable(icon);
}
/**
- * Set the task thumbnail for the task.
+ * Set the task thumbnail for the task. Sets to a default thumbnail if null.
*
* @param thumbnail task thumbnail for the task
*/
- public void setThumbnail(Bitmap thumbnail) {
+ public void setThumbnail(@Nullable Bitmap thumbnail) {
+ if (thumbnail == null) {
+ mThumbnailView.setImageBitmap(null);
+ mThumbnailView.setBackgroundColor(Color.GRAY);
+ return;
+ }
+ mThumbnailView.setBackgroundColor(Color.TRANSPARENT);
mThumbnailView.setImageBitmap(thumbnail);
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsUiFactory.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsUiFactory.java
index d0a2f27..f507d0f 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsUiFactory.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsUiFactory.java
@@ -20,7 +20,6 @@
import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.LauncherState.OVERVIEW;
-import static com.android.launcher3.config.FeatureFlags.SWIPE_HOME;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Launcher;
@@ -31,6 +30,7 @@
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.uioverrides.touchcontrollers.FlingAndHoldTouchController;
import com.android.launcher3.uioverrides.touchcontrollers.LandscapeEdgeSwipeController;
+import com.android.launcher3.uioverrides.touchcontrollers.NavBarToHomeTouchController;
import com.android.launcher3.uioverrides.touchcontrollers.OverviewToAllAppsTouchController;
import com.android.launcher3.uioverrides.touchcontrollers.PortraitStatesTouchController;
import com.android.launcher3.uioverrides.touchcontrollers.StatusBarTouchController;
@@ -39,7 +39,8 @@
import com.android.launcher3.util.TouchController;
import com.android.launcher3.util.UiThreadHelper;
import com.android.launcher3.util.UiThreadHelper.AsyncCommand;
-import com.android.quickstep.OverviewInteractionState;
+import com.android.quickstep.SysUINavigationMode;
+import com.android.quickstep.SysUINavigationMode.Mode;
import com.android.quickstep.views.RecentsView;
import com.android.systemui.shared.system.WindowManagerWrapper;
@@ -58,16 +59,13 @@
private static final float RECENTS_PREPARE_SCALE = 1.33f;
public static TouchController[] createTouchControllers(Launcher launcher) {
- boolean swipeUpEnabled = OverviewInteractionState.INSTANCE.get(launcher)
- .isSwipeUpGestureEnabled();
- boolean swipeUpToHome = swipeUpEnabled && SWIPE_HOME.get();
-
+ Mode mode = SysUINavigationMode.INSTANCE.get(launcher).getMode();
ArrayList<TouchController> list = new ArrayList<>();
list.add(launcher.getDragController());
-
- if (swipeUpToHome) {
+ if (mode == Mode.NO_BUTTON) {
list.add(new QuickSwitchTouchController(launcher));
+ list.add(new NavBarToHomeTouchController(launcher));
list.add(new FlingAndHoldTouchController(launcher));
} else {
if (launcher.getDeviceProfile().isVerticalBarLayout()) {
@@ -75,8 +73,8 @@
list.add(new LandscapeEdgeSwipeController(launcher));
} else {
list.add(new PortraitStatesTouchController(launcher,
- swipeUpEnabled /* allowDragToOverview */));
- if (swipeUpEnabled) {
+ mode.hasGestures /* allowDragToOverview */));
+ if (mode.hasGestures) {
list.add(new QuickSwitchTouchController(launcher));
}
}
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
new file mode 100644
index 0000000..673beff
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java
@@ -0,0 +1,141 @@
+/*
+ * 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.launcher3.uioverrides.touchcontrollers;
+
+import static com.android.launcher3.LauncherState.ALL_APPS;
+import static com.android.launcher3.LauncherState.NORMAL;
+import static com.android.launcher3.LauncherState.OVERVIEW;
+import static com.android.launcher3.allapps.AllAppsTransitionController.ALL_APPS_PROGRESS;
+import static com.android.launcher3.anim.Interpolators.DEACCEL_3;
+
+import android.animation.Animator;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.animation.Interpolator;
+
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherState;
+import com.android.launcher3.LauncherStateManager.AnimationConfig;
+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.anim.Interpolators;
+import com.android.launcher3.touch.AbstractStateChangeTouchController;
+import com.android.launcher3.touch.SwipeDetector;
+import com.android.launcher3.userevent.nano.LauncherLogProto;
+import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
+import com.android.quickstep.views.RecentsView;
+
+/**
+ * Handles swiping up on the nav bar to go home from overview or all apps.
+ */
+public class NavBarToHomeTouchController extends AbstractStateChangeTouchController {
+
+ private static final Interpolator PULLBACK_INTERPOLATOR = DEACCEL_3;
+
+ public NavBarToHomeTouchController(Launcher launcher) {
+ super(launcher, SwipeDetector.VERTICAL);
+ }
+
+ @Override
+ protected boolean canInterceptTouch(MotionEvent ev) {
+ boolean cameFromNavBar = (ev.getEdgeFlags() & Utilities.EDGE_NAV_BAR) != 0;
+ return cameFromNavBar && (mLauncher.isInState(OVERVIEW) || mLauncher.isInState(ALL_APPS));
+ }
+
+ @Override
+ protected LauncherState getTargetState(LauncherState fromState, boolean isDragTowardPositive) {
+ return isDragTowardPositive ? NORMAL : fromState;
+ }
+
+ @Override
+ protected float initCurrentAnimation(int animComponents) {
+ long accuracy = (long) (getShiftRange() * 2);
+ final AnimatorSet anim;
+ if (mFromState == OVERVIEW) {
+ anim = new AnimatorSet();
+ RecentsView recentsView = mLauncher.getOverviewPanel();
+ float pullbackDistance = recentsView.getPaddingStart() / 2;
+ if (!recentsView.isRtl()) {
+ pullbackDistance = -pullbackDistance;
+ }
+ anim.play(ObjectAnimator.ofFloat(recentsView, View.TRANSLATION_X, pullbackDistance));
+ anim.setInterpolator(PULLBACK_INTERPOLATOR);
+ } else { // if (mFromState == ALL_APPS)
+ AnimatorSetBuilder builder = new AnimatorSetBuilder();
+ AllAppsTransitionController allAppsController = mLauncher.getAllAppsController();
+ final float pullbackDistance = mLauncher.getDeviceProfile().allAppsIconSizePx / 2;
+ Animator allAppsProgress = ObjectAnimator.ofFloat(allAppsController, ALL_APPS_PROGRESS,
+ -pullbackDistance / allAppsController.getShiftRange());
+ allAppsProgress.setInterpolator(PULLBACK_INTERPOLATOR);
+ builder.play(allAppsProgress);
+ // 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();
+ config.duration = accuracy;
+ allAppsController.setAlphas(mToState.getVisibleElements(mLauncher), config, builder);
+ anim = builder.build();
+ }
+ anim.setDuration(accuracy);
+ mCurrentAnimation = AnimatorPlaybackController.wrap(anim, accuracy, this::clearState);
+ return -1 / getShiftRange();
+ }
+
+ @Override
+ public void onDragStart(boolean start) {
+ super.onDragStart(start);
+ mStartContainerType = LauncherLogProto.ContainerType.NAVBAR;
+ }
+
+ @Override
+ public void onDragEnd(float velocity, boolean fling) {
+ final int logAction = fling ? Touch.FLING : Touch.SWIPE;
+ float interpolatedProgress = PULLBACK_INTERPOLATOR.getInterpolation(
+ mCurrentAnimation.getProgressFraction());
+ if (interpolatedProgress >= SUCCESS_TRANSITION_PROGRESS || velocity < 0 && fling) {
+ mLauncher.getStateManager().goToState(mToState, true,
+ () -> onSwipeInteractionCompleted(mToState, logAction));
+ } else {
+ // Quickly return to the state we came from (we didn't move far).
+ AnimatorPlaybackController anim = mLauncher.getStateManager()
+ .createAnimationToNewWorkspace(mFromState, 80);
+ anim.setEndAction(() -> onSwipeInteractionCompleted(mFromState, logAction));
+ anim.start();
+ }
+ mCurrentAnimation.dispatchOnCancel();
+ }
+
+ @Override
+ protected int getDirectionForLog() {
+ return LauncherLogProto.Action.Direction.UP;
+ }
+
+ @Override
+ protected boolean goingBetweenNormalAndOverview(LauncherState fromState,
+ LauncherState toState) {
+ // We don't want to create an atomic animation to/from overview.
+ return false;
+ }
+
+ @Override
+ protected int getLogContainerTypeForNormalState() {
+ return LauncherLogProto.ContainerType.NAVBAR;
+ }
+}
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 a2219e8..62d954b 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
@@ -38,7 +38,7 @@
import com.android.launcher3.util.PendingAnimation;
import com.android.launcher3.util.TouchController;
import com.android.launcher3.views.BaseDragLayer;
-import com.android.quickstep.OverviewInteractionState;
+import com.android.quickstep.SysUINavigationMode;
import com.android.quickstep.views.RecentsView;
import com.android.quickstep.views.TaskView;
@@ -123,8 +123,7 @@
if (mRecentsView.isTaskViewVisible(view) && mActivity.getDragLayer()
.isEventOverView(view, ev)) {
mTaskBeingDragged = view;
- if (!OverviewInteractionState.INSTANCE.get(mActivity)
- .isSwipeUpGestureEnabled()) {
+ if (!SysUINavigationMode.INSTANCE.get(mActivity).getMode().hasGestures) {
// Don't allow swipe down to open if we don't support swipe up
// to enter overview.
directionsToDetectScroll = SwipeDetector.DIRECTION_POSITIVE;
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java
index 9763063..69f3338 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java
@@ -113,8 +113,7 @@
final Rect iconLocation = new Rect();
final FloatingIconView floatingView = workspaceView == null ? null
: FloatingIconView.getFloatingIconView(activity, workspaceView,
- true /* hideOriginal */, false /* useDrawableAsIs */,
- activity.getDeviceProfile().getAspectRatioWithInsets(), iconLocation, null);
+ true /* hideOriginal */, iconLocation, false /* isOpening */, null /* recycle */);
return new HomeAnimationFactory() {
@Nullable
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 8fe0461..323dd9a 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
@@ -44,10 +44,10 @@
import com.android.launcher3.MainThreadExecutor;
import com.android.launcher3.Utilities;
import com.android.launcher3.compat.UserManagerCompat;
-import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.logging.EventLogArray;
import com.android.launcher3.util.LooperExecutor;
import com.android.launcher3.util.UiThreadHelper;
+import com.android.quickstep.SysUINavigationMode.Mode;
import com.android.systemui.shared.recents.IOverviewProxy;
import com.android.systemui.shared.recents.ISystemUiProxy;
import com.android.systemui.shared.system.ActivityManagerWrapper;
@@ -315,8 +315,8 @@
mOverviewComponentObserver.getActivityControlHelper();
if (runningTaskInfo == null && !mSwipeSharedState.goingToLauncher) {
return InputConsumer.NO_OP;
- } else if (mAssistantAvailable && mOverviewInteractionState.isSwipeUpGestureEnabled()
- && FeatureFlags.ENABLE_ASSISTANT_GESTURE.get()
+ } else if (mAssistantAvailable
+ && SysUINavigationMode.INSTANCE.get(this).getMode() == Mode.NO_BUTTON
&& AssistantTouchConsumer.withinTouchRegion(this, event)) {
return new AssistantTouchConsumer(this, mISystemUiProxy, !activityControl.isResumed()
? createOtherActivityInputConsumer(event, runningTaskInfo) : null);
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java
index 15072a2..0065cb5 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java
@@ -28,6 +28,7 @@
import static com.android.launcher3.config.FeatureFlags.SWIPE_HOME;
import static com.android.launcher3.util.RaceConditionTracker.ENTER;
import static com.android.launcher3.util.RaceConditionTracker.EXIT;
+import static com.android.launcher3.views.FloatingIconView.SHAPE_PROGRESS_DURATION;
import static com.android.quickstep.ActivityControlHelper.AnimationFactory.ShelfAnimState.HIDE;
import static com.android.quickstep.ActivityControlHelper.AnimationFactory.ShelfAnimState.PEEK;
import static com.android.quickstep.MultiStateCallback.DEBUG_STATES;
@@ -945,7 +946,7 @@
// We want the window alpha to be 0 once this threshold is met, so that the
// FolderIconView can be seen morphing into the icon shape.
- final float windowAlphaThreshold = isFloatingIconView ? 0.75f : 1f;
+ final float windowAlphaThreshold = isFloatingIconView ? 1f - SHAPE_PROGRESS_DURATION : 1f;
anim.addOnUpdateListener((currentRect, progress) -> {
float interpolatedProgress = Interpolators.ACCEL_1_5.getInterpolation(progress);
@@ -959,7 +960,7 @@
if (isFloatingIconView) {
((FloatingIconView) floatingView).update(currentRect, iconAlpha, progress,
- windowAlphaThreshold);
+ windowAlphaThreshold, mClipAnimationHelper.getCurrentCornerRadius(), false);
}
});
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 8da1d2b..329436b 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
@@ -32,9 +32,7 @@
import android.graphics.Rect;
import android.os.Build;
import android.util.AttributeSet;
-import android.util.FloatProperty;
import android.view.View;
-import android.view.ViewDebug;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Launcher;
@@ -44,7 +42,7 @@
import com.android.launcher3.util.PendingAnimation;
import com.android.launcher3.views.BaseDragLayer;
import com.android.launcher3.views.ScrimView;
-import com.android.quickstep.OverviewInteractionState;
+import com.android.quickstep.SysUINavigationMode;
import com.android.quickstep.hints.ChipsContainer;
import com.android.quickstep.util.ClipAnimationHelper;
import com.android.quickstep.util.ClipAnimationHelper.TransformParams;
@@ -132,7 +130,7 @@
ClipAnimationHelper helper) {
AnimatorSet anim = super.createAdjacentPageAnimForTaskLaunch(tv, helper);
- if (!OverviewInteractionState.INSTANCE.get(mActivity).isSwipeUpGestureEnabled()) {
+ if (!SysUINavigationMode.INSTANCE.get(mActivity).getMode().hasGestures) {
// Hotseat doesn't move when opening recents with the button,
// so don't animate it here either.
return anim;
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskThumbnailView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskThumbnailView.java
index 90604ef..a9f6311 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskThumbnailView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskThumbnailView.java
@@ -46,6 +46,7 @@
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.util.SystemUiController;
import com.android.launcher3.util.Themes;
+import com.android.quickstep.RecentsModel;
import com.android.quickstep.TaskOverlayFactory;
import com.android.quickstep.TaskOverlayFactory.TaskOverlay;
import com.android.systemui.shared.recents.model.Task;
@@ -81,6 +82,7 @@
private final Paint mBackgroundPaint = new Paint();
private final Paint mClearPaint = new Paint();
private final Paint mDimmingPaintAfterClearing = new Paint();
+ private final float mWindowCornerRadius;
private final Matrix mMatrix = new Matrix();
@@ -114,6 +116,7 @@
mDimmingPaintAfterClearing.setColor(Color.BLACK);
mActivity = BaseActivity.fromContext(context);
mIsDarkTextTheme = Themes.getAttrBoolean(mActivity, R.attr.isWorkspaceDarkText);
+ mWindowCornerRadius = RecentsModel.INSTANCE.get(context).getWindowCornerRadius();
}
public void bind(Task task) {
@@ -196,19 +199,22 @@
@Override
protected void onDraw(Canvas canvas) {
- float fullscreenProgress = ((TaskView) getParent()).getFullscreenProgress();
+ TaskView taskView = (TaskView) getParent();
+ float fullscreenProgress = taskView.getFullscreenProgress();
if (mIsRotated) {
// Don't show insets in the wrong orientation.
fullscreenProgress = 0;
}
if (fullscreenProgress > 0) {
// Draw the insets if we're being drawn fullscreen (we do this for quick switch).
+ float cornerRadius = Utilities.mapRange(fullscreenProgress, mCornerRadius,
+ mWindowCornerRadius);
drawOnCanvas(canvas,
-mScaledInsets.left * fullscreenProgress,
-mScaledInsets.top * fullscreenProgress,
getMeasuredWidth() + mScaledInsets.right * fullscreenProgress,
getMeasuredHeight() + mScaledInsets.bottom * fullscreenProgress,
- mCornerRadius);
+ cornerRadius / taskView.getRecentsView().getScaleX());
} else {
drawOnCanvas(canvas, 0, 0, getMeasuredWidth(), getMeasuredHeight(), mCornerRadius);
}
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 6ba89c9..bea705f 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
@@ -432,6 +432,7 @@
}
private void resetViewTransforms() {
+ setCurveScale(1);
setZoomScale(1);
setTranslationX(0f);
setTranslationY(0f);
diff --git a/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java b/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java
index c5475d6..f77bd65 100644
--- a/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java
+++ b/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java
@@ -20,15 +20,16 @@
import static com.android.launcher3.BaseActivity.INVISIBLE_BY_APP_TRANSITIONS;
import static com.android.launcher3.BaseActivity.INVISIBLE_BY_PENDING_FLAGS;
import static com.android.launcher3.BaseActivity.PENDING_INVISIBLE_BY_WALLPAPER_ANIMATION;
-import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
import static com.android.launcher3.LauncherState.ALL_APPS;
import static com.android.launcher3.LauncherState.OVERVIEW;
import static com.android.launcher3.Utilities.postAsyncCallback;
import static com.android.launcher3.allapps.AllAppsTransitionController.ALL_APPS_PROGRESS;
import static com.android.launcher3.anim.Interpolators.AGGRESSIVE_EASE;
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.dragndrop.DragLayer.ALPHA_INDEX_TRANSITIONS;
+import static com.android.launcher3.views.FloatingIconView.SHAPE_PROGRESS_DURATION;
import static com.android.quickstep.TaskUtils.taskIsATargetWithMode;
import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_OPENING;
@@ -45,6 +46,7 @@
import android.content.res.Resources;
import android.graphics.Matrix;
import android.graphics.Rect;
+import android.graphics.RectF;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.CancellationSignal;
@@ -52,10 +54,8 @@
import android.os.Looper;
import android.util.Pair;
import android.view.View;
-import android.view.ViewGroup;
import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener;
-import com.android.launcher3.InsettableFrameLayout.LayoutParams;
import com.android.launcher3.allapps.AllAppsTransitionController;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.dragndrop.DragLayer;
@@ -103,13 +103,18 @@
private static final String CONTROL_REMOTE_APP_TRANSITION_PERMISSION =
"android.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS";
- private static final int APP_LAUNCH_DURATION = 500;
+ private static final long APP_LAUNCH_DURATION = 500;
// Use a shorter duration for x or y translation to create a curve effect
- private static final int APP_LAUNCH_CURVED_DURATION = APP_LAUNCH_DURATION / 2;
+ private static final long APP_LAUNCH_CURVED_DURATION = APP_LAUNCH_DURATION / 2;
+ private static final long APP_LAUNCH_ALPHA_DURATION = 50;
+
// We scale the durations for the downward app launch animations (minus the scale animation).
private static final float APP_LAUNCH_DOWN_DUR_SCALE_FACTOR = 0.8f;
- private static final int APP_LAUNCH_ALPHA_START_DELAY = 32;
- private static final int APP_LAUNCH_ALPHA_DURATION = 50;
+ private static final long APP_LAUNCH_DOWN_DURATION =
+ (long) (APP_LAUNCH_DURATION * APP_LAUNCH_DOWN_DUR_SCALE_FACTOR);
+ private static final long APP_LAUNCH_DOWN_CURVED_DURATION = APP_LAUNCH_DOWN_DURATION / 2;
+ private static final long APP_LAUNCH_ALPHA_DOWN_DURATION =
+ (long) (APP_LAUNCH_ALPHA_DURATION * APP_LAUNCH_DOWN_DUR_SCALE_FACTOR);
public static final int RECENTS_LAUNCH_DURATION = 336;
private static final int LAUNCHER_RESUME_START_DELAY = 100;
@@ -207,11 +212,11 @@
// Note that this duration is a guess as we do not know if the animation will be a
// recents launch or not for sure until we know the opening app targets.
- int duration = fromRecents
+ long duration = fromRecents
? RECENTS_LAUNCH_DURATION
: APP_LAUNCH_DURATION;
- int statusBarTransitionDelay = duration - STATUS_BAR_TRANSITION_DURATION
+ long statusBarTransitionDelay = duration - STATUS_BAR_TRANSITION_DURATION
- STATUS_BAR_TRANSITION_PRE_DELAY;
return ActivityOptionsCompat.makeRemoteAnimation(new RemoteAnimationAdapterCompat(
runner, duration, statusBarTransitionDelay));
@@ -266,7 +271,8 @@
}
if (!isAllOpeningTargetTrs) break;
}
- playIconAnimators(anim, v, windowTargetBounds, !isAllOpeningTargetTrs);
+ anim.play(getOpeningWindowAnimators(v, targets, windowTargetBounds,
+ !isAllOpeningTargetTrs));
if (launcherClosing) {
Pair<AnimatorSet, Runnable> launcherContentAnimator =
getLauncherContentAnimator(true /* isAppOpening */,
@@ -279,7 +285,6 @@
}
});
}
- anim.play(getOpeningWindowAnimators(v, targets, windowTargetBounds));
}
/**
@@ -398,124 +403,13 @@
float[] alphas, float[] trans);
/**
- * Animators for the "floating view" of the view used to launch the target.
- */
- private void playIconAnimators(AnimatorSet appOpenAnimator, View v, Rect windowTargetBounds,
- boolean toggleVisibility) {
- final boolean isBubbleTextView = v instanceof BubbleTextView;
- if (mFloatingView != null) {
- mFloatingView.setTranslationX(0);
- mFloatingView.setTranslationY(0);
- mFloatingView.setScaleX(1);
- mFloatingView.setScaleY(1);
- mFloatingView.setAlpha(1);
- mFloatingView.setBackground(null);
- }
- Rect rect = new Rect();
- mFloatingView = FloatingIconView.getFloatingIconView(mLauncher, v, toggleVisibility,
- true /* useDrawableAsIs */, -1 /* aspectRatio */, rect, mFloatingView);
-
- int viewLocationStart = mIsRtl ? windowTargetBounds.width() - rect.right : rect.left;
- LayoutParams lp = (LayoutParams) mFloatingView.getLayoutParams();
- // Special RTL logic is needed to handle the window target bounds.
- lp.leftMargin = mIsRtl ? windowTargetBounds.width() - rect.right : rect.left;
- mFloatingView.setLayoutParams(lp);
-
- int[] dragLayerBounds = new int[2];
- mDragLayer.getLocationOnScreen(dragLayerBounds);
-
- // Animate the app icon to the center of the window bounds in screen coordinates.
- float centerX = windowTargetBounds.centerX() - dragLayerBounds[0];
- float centerY = windowTargetBounds.centerY() - dragLayerBounds[1];
-
- float xPosition = mIsRtl
- ? windowTargetBounds.width() - lp.getMarginStart() - rect.width()
- : lp.getMarginStart();
- float dX = centerX - xPosition - (lp.width / 2f);
- float dY = centerY - lp.topMargin - (lp.height / 2f);
-
- ObjectAnimator x = ObjectAnimator.ofFloat(mFloatingView, View.TRANSLATION_X, 0f, dX);
- ObjectAnimator y = ObjectAnimator.ofFloat(mFloatingView, View.TRANSLATION_Y, 0f, dY);
-
- // Use upward animation for apps that are either on the bottom half of the screen, or are
- // relatively close to the center.
- boolean useUpwardAnimation = lp.topMargin > centerY
- || Math.abs(dY) < mLauncher.getDeviceProfile().cellHeightPx;
- if (useUpwardAnimation) {
- x.setDuration(APP_LAUNCH_CURVED_DURATION);
- y.setDuration(APP_LAUNCH_DURATION);
- } else {
- x.setDuration((long) (APP_LAUNCH_DOWN_DUR_SCALE_FACTOR * APP_LAUNCH_DURATION));
- y.setDuration((long) (APP_LAUNCH_DOWN_DUR_SCALE_FACTOR * APP_LAUNCH_CURVED_DURATION));
- }
- x.setInterpolator(AGGRESSIVE_EASE);
- y.setInterpolator(AGGRESSIVE_EASE);
- appOpenAnimator.play(x);
- appOpenAnimator.play(y);
-
- // Scale the app icon to take up the entire screen. This simplifies the math when
- // animating the app window position / scale.
- float maxScaleX = windowTargetBounds.width() / (float) rect.width();
- float maxScaleY = windowTargetBounds.height() / (float) rect.height();
- float scale = Math.max(maxScaleX, maxScaleY);
- float startScale = 1f;
- if (isBubbleTextView && !(v.getParent() instanceof DeepShortcutView)) {
- Drawable dr = ((BubbleTextView) v).getIcon();
- if (dr instanceof FastBitmapDrawable) {
- startScale = ((FastBitmapDrawable) dr).getAnimatedScale();
- }
- }
-
- ObjectAnimator scaleAnim = ObjectAnimator
- .ofFloat(mFloatingView, SCALE_PROPERTY, startScale, scale);
- scaleAnim.setDuration(APP_LAUNCH_DURATION)
- .setInterpolator(Interpolators.EXAGGERATED_EASE);
- appOpenAnimator.play(scaleAnim);
-
- // Fade out the app icon.
- ObjectAnimator alpha = ObjectAnimator.ofFloat(mFloatingView, View.ALPHA, 1f, 0f);
- if (useUpwardAnimation) {
- alpha.setStartDelay(APP_LAUNCH_ALPHA_START_DELAY);
- alpha.setDuration(APP_LAUNCH_ALPHA_DURATION);
- } else {
- alpha.setStartDelay((long) (APP_LAUNCH_DOWN_DUR_SCALE_FACTOR
- * APP_LAUNCH_ALPHA_START_DELAY));
- alpha.setDuration((long) (APP_LAUNCH_DOWN_DUR_SCALE_FACTOR * APP_LAUNCH_ALPHA_DURATION));
- }
- alpha.setInterpolator(LINEAR);
- appOpenAnimator.play(alpha);
-
- appOpenAnimator.addListener(mFloatingView);
- appOpenAnimator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- // Reset launcher to normal state
- if (isBubbleTextView) {
- ((BubbleTextView) v).setStayPressed(false);
- }
- v.setVisibility(View.VISIBLE);
- ((ViewGroup) mDragLayer.getParent()).getOverlay().remove(mFloatingView);
- }
- });
- }
-
- /**
* @return Animator that controls the window of the opening targets.
*/
private ValueAnimator getOpeningWindowAnimators(View v, RemoteAnimationTargetCompat[] targets,
- Rect windowTargetBounds) {
+ Rect windowTargetBounds, boolean toggleVisibility) {
Rect bounds = new Rect();
- if (v.getParent() instanceof DeepShortcutView) {
- // Deep shortcut views have their icon drawn in a separate view.
- DeepShortcutView view = (DeepShortcutView) v.getParent();
- mDragLayer.getDescendantRectRelativeToSelf(view.getIconView(), bounds);
- } else if (v instanceof BubbleTextView) {
- ((BubbleTextView) v).getIconBounds(bounds);
- } else {
- mDragLayer.getDescendantRectRelativeToSelf(v, bounds);
- }
- int[] floatingViewBounds = new int[2];
-
+ mFloatingView = FloatingIconView.getFloatingIconView(mLauncher, v, toggleVisibility,
+ bounds, true /* isOpening */, mFloatingView);
Rect crop = new Rect();
Matrix matrix = new Matrix();
@@ -526,37 +420,99 @@
SyncRtSurfaceTransactionApplierCompat surfaceApplier =
new SyncRtSurfaceTransactionApplierCompat(mFloatingView);
+ // Scale the app icon to take up the entire screen. This simplifies the math when
+ // animating the app window position / scale.
+ float maxScaleX = windowTargetBounds.width() / (float) bounds.width();
+ // We use windowTargetBounds.width for scaleY too since we start off the animation where the
+ // window is clipped to a square.
+ float maxScaleY = windowTargetBounds.width() / (float) bounds.height();
+ float scale = Math.max(maxScaleX, maxScaleY);
+ float startScale = 1f;
+ if (v instanceof BubbleTextView && !(v.getParent() instanceof DeepShortcutView)) {
+ Drawable dr = ((BubbleTextView) v).getIcon();
+ if (dr instanceof FastBitmapDrawable) {
+ startScale = ((FastBitmapDrawable) dr).getAnimatedScale();
+ }
+ }
+ final float initialStartScale = startScale;
+
+ int[] dragLayerBounds = new int[2];
+ mDragLayer.getLocationOnScreen(dragLayerBounds);
+
+ // Animate the app icon to the center of the window bounds in screen coordinates.
+ float centerX = windowTargetBounds.centerX() - dragLayerBounds[0];
+ float centerY = windowTargetBounds.centerY() - dragLayerBounds[1];
+
+ float dX = centerX - bounds.centerX();
+ float dY = centerY - bounds.centerY();
+
+ boolean useUpwardAnimation = bounds.top > centerY
+ || Math.abs(dY) < mLauncher.getDeviceProfile().cellHeightPx;
+ final long xDuration = useUpwardAnimation ? APP_LAUNCH_CURVED_DURATION
+ : APP_LAUNCH_DOWN_DURATION;
+ final long yDuration = useUpwardAnimation ? APP_LAUNCH_DURATION
+ : APP_LAUNCH_DOWN_CURVED_DURATION;
+ final long alphaDuration = useUpwardAnimation ? APP_LAUNCH_ALPHA_DURATION
+ : APP_LAUNCH_ALPHA_DOWN_DURATION;
+
+ RectF targetBounds = new RectF(windowTargetBounds);
+ RectF currentBounds = new RectF();
+ RectF temp = new RectF();
+
ValueAnimator appAnimator = ValueAnimator.ofFloat(0, 1);
appAnimator.setDuration(APP_LAUNCH_DURATION);
+ appAnimator.setInterpolator(LINEAR);
+ appAnimator.addListener(mFloatingView);
+ appAnimator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ if (v instanceof BubbleTextView) {
+ ((BubbleTextView) v).setStayPressed(false);
+ }
+ }
+ });
+
+ float shapeRevealDuration = APP_LAUNCH_DURATION * SHAPE_PROGRESS_DURATION;
appAnimator.addUpdateListener(new MultiValueUpdateListener() {
- // Fade alpha for the app window.
- FloatProp mAlpha = new FloatProp(0f, 1f, 0, 60, LINEAR);
+ FloatProp mDx = new FloatProp(0, dX, 0, xDuration, AGGRESSIVE_EASE);
+ FloatProp mDy = new FloatProp(0, dY, 0, yDuration, AGGRESSIVE_EASE);
+ FloatProp mIconScale = new FloatProp(initialStartScale, scale, 0, APP_LAUNCH_DURATION,
+ EXAGGERATED_EASE);
+ FloatProp mIconAlpha = new FloatProp(1f, 0f, shapeRevealDuration, alphaDuration,
+ LINEAR);
+ FloatProp mCropHeight = new FloatProp(windowTargetBounds.width(),
+ windowTargetBounds.height(), 0, shapeRevealDuration, AGGRESSIVE_EASE);
@Override
public void onUpdate(float percent) {
- final float easePercent = AGGRESSIVE_EASE.getInterpolation(percent);
-
// Calculate app icon size.
- float iconWidth = bounds.width() * mFloatingView.getScaleX();
- float iconHeight = bounds.height() * mFloatingView.getScaleY();
+ float iconWidth = bounds.width() * mIconScale.value;
+ float iconHeight = bounds.height() * mIconScale.value;
+
+ // Animate the window crop so that it starts off as a square, and then reveals
+ // horizontally.
+ int windowWidth = windowTargetBounds.width();
+ int windowHeight = (int) mCropHeight.value;
+ crop.set(0, 0, windowWidth, windowHeight);
// Scale the app window to match the icon size.
- float scaleX = iconWidth / windowTargetBounds.width();
- float scaleY = iconHeight / windowTargetBounds.height();
- float scale = Math.min(1f, Math.min(scaleX, scaleY));
+ float scaleX = iconWidth / windowWidth;
+ float scaleY = iconHeight / windowHeight;
+ float scale = Math.min(1f, Math.max(scaleX, scaleY));
- // Position the scaled window on top of the icon
- int windowWidth = windowTargetBounds.width();
- int windowHeight = windowTargetBounds.height();
float scaledWindowWidth = windowWidth * scale;
float scaledWindowHeight = windowHeight * scale;
float offsetX = (scaledWindowWidth - iconWidth) / 2;
float offsetY = (scaledWindowHeight - iconHeight) / 2;
- mFloatingView.getLocationOnScreen(floatingViewBounds);
- float transX0 = floatingViewBounds[0] - offsetX;
- float transY0 = floatingViewBounds[1] - offsetY;
+ // Calculate the window position
+ temp.set(bounds);
+ temp.offset(dragLayerBounds[0], dragLayerBounds[1]);
+ temp.offset(mDx.value, mDy.value);
+ Utilities.scaleRectFAboutCenter(temp, mIconScale.value);
+ float transX0 = temp.left - offsetX;
+ float transY0 = temp.top - offsetY;
float windowRadius = 0;
if (!mDeviceProfile.isMultiWindowMode &&
@@ -565,19 +521,9 @@
.getWindowCornerRadius();
}
- // Animate the window crop so that it starts off as a square, and then reveals
- // horizontally.
- float cropHeight = windowHeight * easePercent + windowWidth * (1 - easePercent);
- float initialTop = (windowHeight - windowWidth) / 2f;
- crop.left = 0;
- crop.top = (int) (initialTop * (1 - easePercent));
- crop.right = windowWidth;
- crop.bottom = (int) (crop.top + cropHeight);
-
SurfaceParams[] params = new SurfaceParams[targets.length];
for (int i = targets.length - 1; i >= 0; i--) {
RemoteAnimationTargetCompat target = targets[i];
-
Rect targetCrop;
final float alpha;
final float cornerRadius;
@@ -585,12 +531,15 @@
matrix.setScale(scale, scale);
matrix.postTranslate(transX0, transY0);
targetCrop = crop;
- alpha = mAlpha.value;
+ alpha = 1f - mIconAlpha.value;
cornerRadius = windowRadius;
+ matrix.mapRect(currentBounds, targetBounds);
+ mFloatingView.update(currentBounds, mIconAlpha.value, percent, 0f,
+ cornerRadius * scale, true /* isOpening */);
} else {
matrix.setTranslate(target.position.x, target.position.y);
- alpha = 1f;
targetCrop = target.sourceContainerBounds;
+ alpha = 1f;
cornerRadius = 0;
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java b/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java
index 25e1c89..6d730b6 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java
@@ -41,8 +41,11 @@
import com.android.launcher3.LauncherStateManager.StateHandler;
import com.android.launcher3.QuickstepAppTransitionManagerImpl;
import com.android.launcher3.Utilities;
+import com.android.launcher3.dragndrop.DragLayer;
import com.android.quickstep.OverviewInteractionState;
import com.android.quickstep.RecentsModel;
+import com.android.quickstep.SysUINavigationMode;
+import com.android.quickstep.SysUINavigationMode.NavigationModeChangeListener;
import com.android.quickstep.util.RemoteFadeOutAnimationListener;
import com.android.systemui.shared.system.ActivityCompat;
@@ -52,8 +55,11 @@
public class UiFactory extends RecentsUiFactory {
- public static void setOnTouchControllersChangedListener(Context context, Runnable listener) {
- OverviewInteractionState.INSTANCE.get(context).setOnSwipeUpSettingChangedListener(listener);
+ public static Runnable enableLiveTouchControllerChanges(DragLayer dl) {
+ NavigationModeChangeListener listener = m -> dl.recreateControllers();
+ SysUINavigationMode mode = SysUINavigationMode.INSTANCE.get(dl.getContext());
+ mode.addModeChangeListener(listener);
+ return () -> mode.removeModeChangeListener(listener);
}
public static StateHandler[] getStateHandler(Launcher launcher) {
@@ -89,8 +95,8 @@
@Override
public void onStateTransitionComplete(LauncherState finalState) {
- boolean swipeUpEnabled = OverviewInteractionState.INSTANCE.get(launcher)
- .isSwipeUpGestureEnabled();
+ boolean swipeUpEnabled = SysUINavigationMode.INSTANCE.get(launcher).getMode()
+ .hasGestures;
LauncherState prevState = launcher.getStateManager().getLastState();
if (((swipeUpEnabled && finalState == OVERVIEW) || (!swipeUpEnabled
diff --git a/quickstep/src/com/android/quickstep/InstantAppResolverImpl.java b/quickstep/src/com/android/quickstep/InstantAppResolverImpl.java
index 12757c0..3e9872a 100644
--- a/quickstep/src/com/android/quickstep/InstantAppResolverImpl.java
+++ b/quickstep/src/com/android/quickstep/InstantAppResolverImpl.java
@@ -19,16 +19,11 @@
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.ApplicationInfo;
-import android.content.pm.InstantAppInfo;
import android.content.pm.PackageManager;
-import android.util.Log;
import com.android.launcher3.AppInfo;
import com.android.launcher3.util.InstantAppResolver;
-import java.util.ArrayList;
-import java.util.List;
-
/**
* Implementation of InstantAppResolver using platform APIs
*/
@@ -40,8 +35,7 @@
private final PackageManager mPM;
- public InstantAppResolverImpl(Context context)
- throws NoSuchMethodException, ClassNotFoundException {
+ public InstantAppResolverImpl(Context context) {
mPM = context.getPackageManager();
}
@@ -55,23 +49,4 @@
ComponentName cn = info.getTargetComponent();
return cn != null && cn.getClassName().equals(COMPONENT_CLASS_MARKER);
}
-
- @Override
- public List<ApplicationInfo> getInstantApps() {
- try {
- List<ApplicationInfo> result = new ArrayList<>();
- for (InstantAppInfo iai : mPM.getInstantApps()) {
- ApplicationInfo info = iai.getApplicationInfo();
- if (info != null) {
- result.add(info);
- }
- }
- return result;
- } catch (SecurityException se) {
- Log.w(TAG, "getInstantApps failed. Launcher may not be the default home app.", se);
- } catch (Exception e) {
- Log.e(TAG, "Error calling API: getInstantApps", e);
- }
- return super.getInstantApps();
- }
}
diff --git a/quickstep/src/com/android/quickstep/NavBarModeOverlayResourceObserver.java b/quickstep/src/com/android/quickstep/NavBarModeOverlayResourceObserver.java
deleted file mode 100644
index 3136632..0000000
--- a/quickstep/src/com/android/quickstep/NavBarModeOverlayResourceObserver.java
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.quickstep;
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.res.Resources;
-import android.util.Log;
-
-import com.android.systemui.shared.system.QuickStepContract;
-
-/**
- * Observer for the resource config that specifies the navigation bar mode.
- */
-public class NavBarModeOverlayResourceObserver extends BroadcastReceiver {
-
- private static final String TAG = "NavBarModeOverlayResourceObserver";
-
- private final String ACTION_OVERLAY_CHANGED = "android.intent.action.OVERLAY_CHANGED";
- private static final String NAV_BAR_INTERACTION_MODE_RES_NAME =
- "config_navBarInteractionMode";
-
- private final Context mContext;
- private final OnChangeListener mOnChangeListener;
-
- public NavBarModeOverlayResourceObserver(Context context, OnChangeListener listener) {
- mContext = context;
- mOnChangeListener = listener;
- }
-
- public void register() {
- IntentFilter filter = new IntentFilter(ACTION_OVERLAY_CHANGED);
- filter.addDataScheme("package");
- mContext.registerReceiver(this, filter);
- }
-
- @Override
- public void onReceive(Context context, Intent intent) {
- mOnChangeListener.onNavBarModeChanged(getSystemIntegerRes(context,
- NAV_BAR_INTERACTION_MODE_RES_NAME));
- }
-
- public interface OnChangeListener {
- void onNavBarModeChanged(int mode);
- }
-
- public static boolean isSwipeUpModeEnabled(Context context) {
- return QuickStepContract.isSwipeUpMode(getSystemIntegerRes(context,
- NAV_BAR_INTERACTION_MODE_RES_NAME));
- }
-
- public static boolean isEdgeToEdgeModeEnabled(Context context) {
- return QuickStepContract.isGesturalMode(getSystemIntegerRes(context,
- NAV_BAR_INTERACTION_MODE_RES_NAME));
- }
-
- public static boolean isLegacyModeEnabled(Context context) {
- return QuickStepContract.isLegacyMode(getSystemIntegerRes(context,
- NAV_BAR_INTERACTION_MODE_RES_NAME));
- }
-
- private static int getSystemIntegerRes(Context context, String resName) {
- Resources res = context.getResources();
- int resId = res.getIdentifier(resName, "integer", "android");
-
- if (resId != 0) {
- return res.getInteger(resId);
- } else {
- Log.e(TAG, "Failed to get system resource ID. Incompatible framework version?");
- return -1;
- }
- }
-}
\ No newline at end of file
diff --git a/quickstep/src/com/android/quickstep/OverviewInteractionState.java b/quickstep/src/com/android/quickstep/OverviewInteractionState.java
index 903701d..ce472c6 100644
--- a/quickstep/src/com/android/quickstep/OverviewInteractionState.java
+++ b/quickstep/src/com/android/quickstep/OverviewInteractionState.java
@@ -30,8 +30,8 @@
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.util.MainThreadInitializedObject;
import com.android.launcher3.util.UiThreadHelper;
+import com.android.quickstep.SysUINavigationMode.Mode;
import com.android.systemui.shared.recents.ISystemUiProxy;
-import com.android.systemui.shared.system.QuickStepContract;
import androidx.annotation.WorkerThread;
@@ -56,10 +56,7 @@
private static final int MSG_SET_PROXY = 200;
private static final int MSG_SET_BACK_BUTTON_ALPHA = 201;
- private static final int MSG_SET_SWIPE_UP_ENABLED = 202;
-
- // TODO: Discriminate between swipe up and edge to edge
- private final NavBarModeOverlayResourceObserver mSwipeUpSettingObserver;
+ private static final int MSG_APPLY_FLAGS = 202;
private final Context mContext;
private final Handler mUiHandler;
@@ -70,8 +67,6 @@
private boolean mSwipeUpEnabled;
private float mBackButtonAlpha = 1;
- private Runnable mOnSwipeUpSettingChangedListener;
-
private OverviewInteractionState(Context context) {
mContext = context;
@@ -81,20 +76,8 @@
mUiHandler = new Handler(this::handleUiMessage);
mBgHandler = new Handler(UiThreadHelper.getBackgroundLooper(), this::handleBgMessage);
- mSwipeUpEnabled = NavBarModeOverlayResourceObserver.isSwipeUpModeEnabled(mContext)
- || NavBarModeOverlayResourceObserver.isEdgeToEdgeModeEnabled(mContext);
- if (SwipeUpSetting.isSystemNavigationSettingAvailable()) {
- mSwipeUpSettingObserver = new NavBarModeOverlayResourceObserver(context,
- this::notifySwipeUpSettingChanged);
- mSwipeUpSettingObserver.register();
- resetHomeBounceSeenOnQuickstepEnabledFirstTime();
- } else {
- mSwipeUpSettingObserver = null;
- }
- }
-
- public boolean isSwipeUpGestureEnabled() {
- return mSwipeUpEnabled;
+ onNavigationModeChanged(SysUINavigationMode.INSTANCE.get(context)
+ .addModeChangeListener(this::onNavigationModeChanged));
}
public float getBackButtonAlpha() {
@@ -130,23 +113,13 @@
case MSG_SET_BACK_BUTTON_ALPHA:
applyBackButtonAlpha((float) msg.obj, msg.arg1 == 1);
return true;
- case MSG_SET_SWIPE_UP_ENABLED:
- mSwipeUpEnabled = msg.arg1 != 0;
- resetHomeBounceSeenOnQuickstepEnabledFirstTime();
-
- if (mOnSwipeUpSettingChangedListener != null) {
- mOnSwipeUpSettingChangedListener.run();
- }
+ case MSG_APPLY_FLAGS:
break;
}
applyFlags();
return true;
}
- public void setOnSwipeUpSettingChangedListener(Runnable listener) {
- mOnSwipeUpSettingChangedListener = listener;
- }
-
@WorkerThread
private void applyFlags() {
if (mISystemUiProxy == null) {
@@ -176,16 +149,12 @@
}
}
- private void notifySwipeUpSettingChanged(int mode) {
- boolean swipeUpEnabled = !QuickStepContract.isLegacyMode(mode);
- boolean gesturalEnabled = QuickStepContract.isGesturalMode(mode);
+ private void onNavigationModeChanged(SysUINavigationMode.Mode mode) {
+ FeatureFlags.SWIPE_HOME.updateStorage(mContext, mode == Mode.NO_BUTTON);
- FeatureFlags.SWIPE_HOME.updateStorage(mContext, gesturalEnabled);
- FeatureFlags.ENABLE_ASSISTANT_GESTURE.updateStorage(mContext, gesturalEnabled);
-
- mUiHandler.removeMessages(MSG_SET_SWIPE_UP_ENABLED);
- mUiHandler.obtainMessage(MSG_SET_SWIPE_UP_ENABLED, swipeUpEnabled ? 1 : 0, 0).
- sendToTarget();
+ mSwipeUpEnabled = mode.hasGestures;
+ resetHomeBounceSeenOnQuickstepEnabledFirstTime();
+ mBgHandler.obtainMessage(MSG_APPLY_FLAGS).sendToTarget();
}
private void resetHomeBounceSeenOnQuickstepEnabledFirstTime() {
diff --git a/quickstep/src/com/android/quickstep/SwipeUpSetting.java b/quickstep/src/com/android/quickstep/SwipeUpSetting.java
deleted file mode 100644
index 7f830f9..0000000
--- a/quickstep/src/com/android/quickstep/SwipeUpSetting.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.quickstep;
-
-import android.content.res.Resources;
-import android.util.Log;
-
-public final class SwipeUpSetting {
- private static final String TAG = "SwipeUpSetting";
-
- private static final String SWIPE_UP_SETTING_AVAILABLE_RES_NAME =
- "config_swipe_up_gesture_setting_available";
-
- public static boolean isSystemNavigationSettingAvailable() {
- return getSystemBooleanRes(SWIPE_UP_SETTING_AVAILABLE_RES_NAME);
- }
-
- private static boolean getSystemBooleanRes(String resName) {
- Resources res = Resources.getSystem();
- int resId = res.getIdentifier(resName, "bool", "android");
-
- if (resId != 0) {
- return res.getBoolean(resId);
- } else {
- Log.e(TAG, "Failed to get system resource ID. Incompatible framework version?");
- return false;
- }
- }
-}
diff --git a/quickstep/src/com/android/quickstep/SysUINavigationMode.java b/quickstep/src/com/android/quickstep/SysUINavigationMode.java
new file mode 100644
index 0000000..1953ecb
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/SysUINavigationMode.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.quickstep;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.res.Resources;
+import android.util.Log;
+
+import com.android.launcher3.util.MainThreadInitializedObject;
+import com.android.systemui.shared.system.QuickStepContract;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Observer for the resource config that specifies the navigation bar mode.
+ */
+public class SysUINavigationMode {
+
+ public enum Mode {
+ THREE_BUTTONS(false, 0),
+ TWO_BUTTONS(true, 1),
+ NO_BUTTON(true, 2);
+
+ public final boolean hasGestures;
+ public final int resValue;
+
+ Mode(boolean hasGestures, int resValue) {
+ this.hasGestures = hasGestures;
+ this.resValue = resValue;
+ }
+ }
+
+ public static MainThreadInitializedObject<SysUINavigationMode> INSTANCE =
+ new MainThreadInitializedObject<>(SysUINavigationMode::new);
+
+ private static final String TAG = "SysUINavigationMode";
+
+ private final String ACTION_OVERLAY_CHANGED = "android.intent.action.OVERLAY_CHANGED";
+ private static final String NAV_BAR_INTERACTION_MODE_RES_NAME =
+ "config_navBarInteractionMode";
+
+ private final Context mContext;
+ private Mode mMode;
+
+ private final List<NavigationModeChangeListener> mChangeListeners = new ArrayList<>();
+
+ public SysUINavigationMode(Context context) {
+ mContext = context;
+ initializeMode();
+
+ IntentFilter filter = new IntentFilter(ACTION_OVERLAY_CHANGED);
+ filter.addDataScheme("package");
+ mContext.registerReceiver(new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ Mode oldMode = mMode;
+ initializeMode();
+ if (mMode != oldMode) {
+ dispatchModeChange();
+ }
+ }
+ }, filter);
+ }
+
+ private void initializeMode() {
+ int modeInt = getSystemIntegerRes(mContext, NAV_BAR_INTERACTION_MODE_RES_NAME);
+ for(Mode m : Mode.values()) {
+ if (m.resValue == modeInt) {
+ mMode = m;
+ }
+ }
+ }
+
+ private void dispatchModeChange() {
+ for (NavigationModeChangeListener listener : mChangeListeners) {
+ listener.onNavigationModeChanged(mMode);
+ }
+ }
+
+ public Mode addModeChangeListener(NavigationModeChangeListener listener) {
+ mChangeListeners.add(listener);
+ return mMode;
+ }
+
+ public void removeModeChangeListener(NavigationModeChangeListener listener) {
+ mChangeListeners.remove(listener);
+ }
+
+ public Mode getMode() {
+ return mMode;
+ }
+
+ private static int getSystemIntegerRes(Context context, String resName) {
+ Resources res = context.getResources();
+ int resId = res.getIdentifier(resName, "integer", "android");
+
+ if (resId != 0) {
+ return res.getInteger(resId);
+ } else {
+ Log.e(TAG, "Failed to get system resource ID. Incompatible framework version?");
+ return -1;
+ }
+ }
+
+ public interface NavigationModeChangeListener {
+
+ void onNavigationModeChanged(Mode newMode);
+ }
+}
\ No newline at end of file
diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
index 226e5ee..6623861 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
@@ -41,6 +41,7 @@
import com.android.quickstep.views.RecentsView;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TestWatcher;
@@ -71,6 +72,7 @@
@Test
@PortraitLandscape
+ @Ignore
public void testPressRecentAppsLauncherAndGetOverview() throws RemoteException {
mDevice.pressRecentApps();
waitForState("Launcher internal state didn't switch to Overview", LauncherState.OVERVIEW);
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index 6397e14..6a3a26f 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -51,6 +51,9 @@
public final int heightPx;
public final int availableWidthPx;
public final int availableHeightPx;
+
+ public final float aspectRatio;
+
/**
* The maximum amount of left/right workspace padding as a percentage of the screen width.
* To be clear, this means that up to 7% of the screen width can be used as left padding, and
@@ -160,7 +163,7 @@
isTablet = res.getBoolean(R.bool.is_tablet);
isLargeTablet = res.getBoolean(R.bool.is_large_tablet);
isPhone = !isTablet && !isLargeTablet;
- float aspectRatio = ((float) Math.max(widthPx, heightPx)) / Math.min(widthPx, heightPx);
+ aspectRatio = ((float) Math.max(widthPx, heightPx)) / Math.min(widthPx, heightPx);
boolean isTallDevice = Float.compare(aspectRatio, TALL_DEVICE_ASPECT_RATIO_THRESHOLD) >= 0;
// Some more constants
@@ -618,12 +621,6 @@
}
}
- public float getAspectRatioWithInsets() {
- int w = widthPx - mInsets.left - mInsets.right;
- int h = heightPx - mInsets.top - mInsets.bottom;
- return ((float) Math.max(w, h)) / Math.min(w, h);
- }
-
private static Context getContext(Context c, int orientation) {
Configuration context = new Configuration(c.getResources().getConfiguration());
context.orientation = orientation;
diff --git a/src/com/android/launcher3/FastBitmapDrawable.java b/src/com/android/launcher3/FastBitmapDrawable.java
index 964e8b6..7ab88a0 100644
--- a/src/com/android/launcher3/FastBitmapDrawable.java
+++ b/src/com/android/launcher3/FastBitmapDrawable.java
@@ -103,9 +103,14 @@
}
protected FastBitmapDrawable(Bitmap b, int iconColor) {
+ this(b, iconColor, false);
+ }
+
+ protected FastBitmapDrawable(Bitmap b, int iconColor, boolean isDisabled) {
mBitmap = b;
mIconColor = iconColor;
setFilterBitmap(true);
+ setIsDisabled(isDisabled);
}
@Override
@@ -249,6 +254,10 @@
}
}
+ protected boolean isDisabled() {
+ return mIsDisabled;
+ }
+
/**
* Sets the saturation of this icon, 0 [full color] -> 1 [desaturated]
*/
@@ -338,21 +347,23 @@
@Override
public ConstantState getConstantState() {
- return new MyConstantState(mBitmap, mIconColor);
+ return new MyConstantState(mBitmap, mIconColor, mIsDisabled);
}
protected static class MyConstantState extends ConstantState {
protected final Bitmap mBitmap;
protected final int mIconColor;
+ protected final boolean mIsDisabled;
- public MyConstantState(Bitmap bitmap, int color) {
+ public MyConstantState(Bitmap bitmap, int color, boolean isDisabled) {
mBitmap = bitmap;
mIconColor = color;
+ mIsDisabled = isDisabled;
}
@Override
public Drawable newDrawable() {
- return new FastBitmapDrawable(mBitmap, mIconColor);
+ return new FastBitmapDrawable(mBitmap, mIconColor, mIsDisabled);
}
@Override
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 867001a..89236aa 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -257,6 +257,7 @@
public ViewGroupFocusHelper mFocusHandler;
private RotationHelper mRotationHelper;
+ private Runnable mCancelTouchController;
final Handler mHandler = new Handler();
private final Runnable mHandleDeferredResume = this::handleDeferredResume;
@@ -946,7 +947,7 @@
// Setup the drag layer
mDragLayer.setup(mDragController, mWorkspace);
- UiFactory.setOnTouchControllersChangedListener(this, mDragLayer::recreateControllers);
+ mCancelTouchController = UiFactory.enableLiveTouchControllerChanges(mDragLayer);
mWorkspace.setup(mDragController);
// Until the workspace is bound, ensure that we keep the wallpaper offset locked to the
@@ -1318,7 +1319,10 @@
unregisterReceiver(mScreenOffReceiver);
mWorkspace.removeFolderListeners();
- UiFactory.setOnTouchControllersChangedListener(this, null);
+ if (mCancelTouchController != null) {
+ mCancelTouchController.run();
+ mCancelTouchController = null;
+ }
// Stop callbacks from LauncherModel
// It's possible to receive onDestroy after a new Launcher activity has
diff --git a/src/com/android/launcher3/config/BaseFlags.java b/src/com/android/launcher3/config/BaseFlags.java
index 106d901..656151f 100644
--- a/src/com/android/launcher3/config/BaseFlags.java
+++ b/src/com/android/launcher3/config/BaseFlags.java
@@ -118,10 +118,6 @@
"ENABLE_HINTS_IN_OVERVIEW", false,
"Show chip hints and gleams on the overview screen");
- public static final TogglableFlag ENABLE_ASSISTANT_GESTURE = new ToggleableGlobalSettingsFlag(
- "ENABLE_ASSISTANT_GESTURE", false,
- "Enable swipe up from the bottom right corner to start assistant");
-
public static void initialize(Context context) {
// Avoid the disk read for user builds
if (Utilities.IS_DEBUG_DEVICE) {
diff --git a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
index c403e76..0274de3 100644
--- a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
+++ b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
@@ -224,7 +224,8 @@
return true;
}
- private boolean goingBetweenNormalAndOverview(LauncherState fromState, LauncherState toState) {
+ protected boolean goingBetweenNormalAndOverview(LauncherState fromState,
+ LauncherState toState) {
return (fromState == NORMAL || fromState == OVERVIEW)
&& (toState == NORMAL || toState == OVERVIEW)
&& mPendingAnimation == null;
@@ -242,7 +243,7 @@
mStartContainerType = LauncherLogProto.ContainerType.ALLAPPS;
} else if (mStartState == NORMAL) {
mStartContainerType = getLogContainerTypeForNormalState();
- } else if (mStartState == OVERVIEW){
+ } else if (mStartState == OVERVIEW){
mStartContainerType = LauncherLogProto.ContainerType.TASKSWITCHER;
}
if (mCurrentAnimation == null) {
diff --git a/src/com/android/launcher3/util/InstantAppResolver.java b/src/com/android/launcher3/util/InstantAppResolver.java
index 5dc7af8..031a40d 100644
--- a/src/com/android/launcher3/util/InstantAppResolver.java
+++ b/src/com/android/launcher3/util/InstantAppResolver.java
@@ -24,9 +24,6 @@
import com.android.launcher3.AppInfo;
import com.android.launcher3.R;
-import java.util.Collections;
-import java.util.List;
-
/**
* A wrapper class to access instant app related APIs.
*/
@@ -55,8 +52,4 @@
}
return false;
}
-
- public List<ApplicationInfo> getInstantApps() {
- return Collections.emptyList();
- }
}
diff --git a/src/com/android/launcher3/views/FloatingIconView.java b/src/com/android/launcher3/views/FloatingIconView.java
index 49ec292..5889468 100644
--- a/src/com/android/launcher3/views/FloatingIconView.java
+++ b/src/com/android/launcher3/views/FloatingIconView.java
@@ -15,6 +15,7 @@
*/
package com.android.launcher3.views;
+import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.config.FeatureFlags.ADAPTIVE_ICON_WINDOW_ANIM;
import android.animation.Animator;
@@ -47,7 +48,6 @@
import com.android.launcher3.LauncherModel;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
-import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.dragndrop.DragLayer;
import com.android.launcher3.dragndrop.FolderAdaptiveIcon;
import com.android.launcher3.folder.FolderIcon;
@@ -60,18 +60,20 @@
import androidx.annotation.Nullable;
import androidx.annotation.WorkerThread;
+import static com.android.launcher3.Utilities.mapToRange;
+
/**
* A view that is created to look like another view with the purpose of creating fluid animations.
*/
public class FloatingIconView extends View implements Animator.AnimatorListener, ClipPathView {
+ public static final float SHAPE_PROGRESS_DURATION = 0.15f;
+
private static final Rect sTmpRect = new Rect();
- private Runnable mStartRunnable;
private Runnable mEndRunnable;
- private int mOriginalHeight;
private final int mBlurSizeOutline;
private boolean mIsAdaptiveIcon = false;
@@ -82,30 +84,28 @@
private final Rect mStartRevealRect = new Rect();
private final Rect mEndRevealRect = new Rect();
private Path mClipPath;
- protected final Rect mOutline = new Rect();
- private final float mTaskCornerRadius;
+ private float mTaskCornerRadius;
private final Rect mFinalDrawableBounds = new Rect();
private final Rect mBgDrawableBounds = new Rect();
private float mBgDrawableStartScale = 1f;
+ private float mBgDrawableEndScale = 1f;
private FloatingIconView(Context context) {
super(context);
-
mBlurSizeOutline = context.getResources().getDimensionPixelSize(
R.dimen.blur_size_medium_outline);
-
- mTaskCornerRadius = 0; // TODO
}
/**
* Positions this view to match the size and location of {@param rect}.
- *
* @param alpha The alpha to set this view.
* @param progress A value from [0, 1] that represents the animation progress.
- * @param windowAlphaThreshold The value at which the window alpha is 0.
+ * @param shapeProgressStart The progress value at which to start the shape reveal.
+ * @param cornerRadius The corner radius of {@param rect}.
*/
- public void update(RectF rect, float alpha, float progress, float windowAlphaThreshold) {
+ public void update(RectF rect, float alpha, float progress, float shapeProgressStart,
+ float cornerRadius, boolean isOpening) {
setAlpha(alpha);
LayoutParams lp = (LayoutParams) getLayoutParams();
@@ -116,49 +116,42 @@
float scaleX = rect.width() / (float) lp.width;
float scaleY = rect.height() / (float) lp.height;
- float scale = mIsAdaptiveIcon ? Math.max(scaleX, scaleY) : Math.min(scaleX, scaleY);
+ float scale = mIsAdaptiveIcon && !isOpening ? Math.max(scaleX, scaleY)
+ : Math.min(scaleX, scaleY);
+ scale = Math.max(1f, scale);
+
setPivotX(0);
setPivotY(0);
setScaleX(scale);
setScaleY(scale);
- // Wait until the window is no longer visible before morphing the icon into its final shape.
- float shapeRevealProgress = Utilities.mapToRange(Math.max(windowAlphaThreshold, progress),
- windowAlphaThreshold, 1f, 0f, 1, Interpolators.LINEAR);
- if (mIsAdaptiveIcon && shapeRevealProgress > 0) {
+ // 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);
+
+ mTaskCornerRadius = cornerRadius;
+ if (mIsAdaptiveIcon && shapeRevealProgress >= 0) {
if (mRevealAnimator == null) {
- mEndRevealRect.set(mOutline);
- // We play the reveal animation in reverse so that we end with the icon shape.
mRevealAnimator = (ValueAnimator) FolderShape.getShape().createRevealAnimator(this,
- mStartRevealRect, mEndRevealRect, mTaskCornerRadius / scale, true);
- mRevealAnimator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- mRevealAnimator = null;
- }
- });
+ mStartRevealRect, mEndRevealRect, mTaskCornerRadius / scale, !isOpening);
mRevealAnimator.start();
// We pause here so we can set the current fraction ourselves.
mRevealAnimator.pause();
}
- float bgScale = shapeRevealProgress + mBgDrawableStartScale * (1 - shapeRevealProgress);
- setBackgroundDrawableBounds(bgScale);
-
mRevealAnimator.setCurrentFraction(shapeRevealProgress);
+
+ float bgScale = (mBgDrawableEndScale * shapeRevealProgress) + mBgDrawableStartScale
+ * (1 - shapeRevealProgress);
+ setBackgroundDrawableBounds(bgScale);
}
invalidate();
invalidateOutline();
}
@Override
- public void onAnimationStart(Animator animator) {
- if (mStartRunnable != null) {
- mStartRunnable.run();
- }
- }
-
- @Override
public void onAnimationEnd(Animator animator) {
if (mEndRunnable != null) {
mEndRunnable.run();
@@ -180,7 +173,6 @@
Utilities.getLocationBoundsForView(launcher, v, positionOut);
final LayoutParams lp = new LayoutParams(positionOut.width(), positionOut.height());
lp.ignoreInsets = true;
- mOriginalHeight = lp.height;
// Position the floating view exactly on top of the original
lp.leftMargin = positionOut.left;
@@ -193,11 +185,11 @@
}
@WorkerThread
- private void getIcon(Launcher launcher, View v, ItemInfo info, boolean useDrawableAsIs,
- float aspectRatio) {
+ private void getIcon(Launcher launcher, View v, ItemInfo info, boolean isOpening,
+ Runnable onIconLoadedRunnable) {
final LayoutParams lp = (LayoutParams) getLayoutParams();
Drawable drawable = null;
- boolean supportsAdaptiveIcons = ADAPTIVE_ICON_WINDOW_ANIM.get() && !useDrawableAsIs
+ boolean supportsAdaptiveIcons = ADAPTIVE_ICON_WINDOW_ANIM.get()
&& Build.VERSION.SDK_INT >= Build.VERSION_CODES.O;
if (!supportsAdaptiveIcons && v instanceof BubbleTextView) {
// Similar to DragView, we simply use the BubbleTextView icon here.
@@ -214,7 +206,7 @@
}
if (drawable == null) {
drawable = Utilities.getFullDrawable(launcher, info, lp.width, lp.height,
- useDrawableAsIs, new Object[1]);
+ false, new Object[1]);
}
Drawable finalDrawable = drawable == null ? null
@@ -247,35 +239,50 @@
sbd.setShiftY(sbd.getShiftY() - sTmpRect.top);
}
+ final int originalHeight = lp.height;
+ final int originalWidth = lp.width;
+
int blurMargin = mBlurSizeOutline / 2;
- mFinalDrawableBounds.set(0, 0, lp.width, mOriginalHeight);
+ mFinalDrawableBounds.set(0, 0, originalWidth, originalHeight);
if (!isFolderIcon) {
mFinalDrawableBounds.inset(iconOffset - blurMargin, iconOffset - blurMargin);
}
mForeground.setBounds(mFinalDrawableBounds);
mBackground.setBounds(mFinalDrawableBounds);
- if (isFolderIcon) {
- mStartRevealRect.set(0, 0, lp.width, mOriginalHeight);
+ mStartRevealRect.set(0, 0, originalWidth, originalHeight);
+
+ if (!isFolderIcon) {
+ mStartRevealRect.inset(mBlurSizeOutline, mBlurSizeOutline);
+ }
+
+ float aspectRatio = launcher.getDeviceProfile().aspectRatio;
+ if (launcher.getDeviceProfile().isVerticalBarLayout()) {
+ lp.width = (int) Math.max(lp.width, lp.height * aspectRatio);
} else {
- mStartRevealRect.set(mBlurSizeOutline, mBlurSizeOutline,
- lp.width - mBlurSizeOutline, mOriginalHeight - mBlurSizeOutline);
- }
-
- if (aspectRatio > 0) {
lp.height = (int) Math.max(lp.height, lp.width * aspectRatio);
- layout(lp.leftMargin, lp.topMargin, lp.leftMargin + lp.width, lp.topMargin
- + lp.height);
}
- mBgDrawableStartScale = (float) lp.height / mOriginalHeight;
- setBackgroundDrawableBounds(mBgDrawableStartScale);
+ layout(lp.leftMargin, lp.topMargin, lp.leftMargin + lp.width, lp.topMargin
+ + lp.height);
- // Set up outline
- mOutline.set(0, 0, lp.width, lp.height);
+ Rect rectOutline = new Rect();
+ float scale = Math.max((float) lp.height / originalHeight,
+ (float) lp.width / originalWidth);
+ if (isOpening) {
+ mBgDrawableStartScale = 1f;
+ mBgDrawableEndScale = scale;
+ rectOutline.set(0, 0, originalWidth, originalHeight);
+ } else {
+ mBgDrawableStartScale = scale;
+ mBgDrawableEndScale = 1f;
+ rectOutline.set(0, 0, lp.width, lp.height);
+ }
+ mEndRevealRect.set(0, 0, lp.width, lp.height);
+ setBackgroundDrawableBounds(mBgDrawableStartScale);
setOutlineProvider(new ViewOutlineProvider() {
@Override
public void getOutline(View view, Outline outline) {
- outline.setRoundRect(mOutline, mTaskCornerRadius);
+ outline.setRoundRect(rectOutline, mTaskCornerRadius);
}
});
setClipToOutline(true);
@@ -283,6 +290,7 @@
setBackground(finalDrawable);
}
+ onIconLoadedRunnable.run();
invalidate();
invalidateOutline();
});
@@ -350,6 +358,9 @@
}
@Override
+ public void onAnimationStart(Animator animator) {}
+
+ @Override
public void onAnimationCancel(Animator animator) {}
@Override
@@ -357,17 +368,16 @@
/**
* Creates a floating icon view for {@param originalView}.
- *
* @param originalView The view to copy
* @param hideOriginal If true, it will hide {@param originalView} while this view is visible.
- * @param useDrawableAsIs If true, we do not separate the foreground/background of adaptive
- * icons. TODO(b/122843905): We can remove this once app opening uses new animation.
- * @param aspectRatio If >= 0, we will use this aspect ratio for the initial adaptive icon size.
* @param positionOut Rect that will hold the size and position of v.
+ * @param isOpening True if this view replaces the icon for app open animation.
*/
public static FloatingIconView getFloatingIconView(Launcher launcher, View originalView,
- boolean hideOriginal, boolean useDrawableAsIs, float aspectRatio, Rect positionOut,
- FloatingIconView recycle) {
+ boolean hideOriginal, Rect positionOut, boolean isOpening, FloatingIconView recycle) {
+ if (recycle != null) {
+ recycle.recycle();
+ }
FloatingIconView view = recycle != null ? recycle : new FloatingIconView(launcher);
// Match the position of the original view.
@@ -376,9 +386,16 @@
// Get the drawable on the background thread
// Must be called after matchPositionOf so that we know what size to load.
if (originalView.getTag() instanceof ItemInfo) {
+ Runnable onIconLoaded = () -> {
+ // Delay swapping views until the icon is loaded to prevent a flash.
+ view.setVisibility(VISIBLE);
+ if (hideOriginal) {
+ originalView.setVisibility(INVISIBLE);
+ }
+ };
new Handler(LauncherModel.getWorkerLooper()).postAtFrontOfQueue(() -> {
- view.getIcon(launcher, originalView, (ItemInfo) originalView.getTag(),
- useDrawableAsIs, aspectRatio);
+ view.getIcon(launcher, originalView, (ItemInfo) originalView.getTag(), isOpening,
+ onIconLoaded);
});
}
@@ -387,12 +404,6 @@
view.setVisibility(INVISIBLE);
((ViewGroup) dragLayer.getParent()).getOverlay().add(view);
- view.mStartRunnable = () -> {
- view.setVisibility(VISIBLE);
- if (hideOriginal) {
- originalView.setVisibility(INVISIBLE);
- }
- };
if (hideOriginal) {
view.mEndRunnable = () -> {
AnimatorSet fade = new AnimatorSet();
@@ -442,4 +453,24 @@
}
return view;
}
+
+ private void recycle() {
+ setTranslationX(0);
+ setTranslationY(0);
+ setScaleX(1);
+ setScaleY(1);
+ setAlpha(1);
+ setBackground(null);
+ mEndRunnable = null;
+ mIsAdaptiveIcon = false;
+ mForeground = null;
+ mBackground = null;
+ mClipPath = null;
+ mFinalDrawableBounds.setEmpty();
+ mBgDrawableBounds.setEmpty();;
+ if (mRevealAnimator != null) {
+ mRevealAnimator.cancel();
+ }
+ mRevealAnimator = null;
+ }
}
diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java b/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java
index 0d727fd..9939c25 100644
--- a/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java
+++ b/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java
@@ -22,6 +22,7 @@
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherStateManager.StateHandler;
+import com.android.launcher3.dragndrop.DragLayer;
import com.android.launcher3.util.TouchController;
import java.io.PrintWriter;
@@ -33,7 +34,9 @@
launcher.getDragController(), new AllAppsSwipeController(launcher)};
}
- public static void setOnTouchControllersChangedListener(Context context, Runnable listener) { }
+ public static Runnable enableLiveTouchControllerChanges(DragLayer dl) {
+ return null;
+ }
public static StateHandler[] getStateHandler(Launcher launcher) {
return new StateHandler[] {
diff --git a/tests/Android.mk b/tests/Android.mk
index ca7395a..080c98b 100644
--- a/tests/Android.mk
+++ b/tests/Android.mk
@@ -30,7 +30,6 @@
LOCAL_STATIC_JAVA_LIBRARIES += libSharedSystemUI
LOCAL_SRC_FILES := $(call all-java-files-under, tapl) \
- ../quickstep/src/com/android/quickstep/SwipeUpSetting.java \
../src/com/android/launcher3/util/SecureSettingsObserver.java \
../src/com/android/launcher3/TestProtocol.java
endif
diff --git a/tests/tapl/com/android/launcher3/tapl/AllApps.java b/tests/tapl/com/android/launcher3/tapl/AllApps.java
index e4dced5..98fb07f 100644
--- a/tests/tapl/com/android/launcher3/tapl/AllApps.java
+++ b/tests/tapl/com/android/launcher3/tapl/AllApps.java
@@ -64,7 +64,7 @@
"want to get app icon on all apps")) {
final UiObject2 allAppsContainer = verifyActiveContainer();
final UiObject2 navBar = mLauncher.waitForSystemUiObject("navigation_bar_frame");
- allAppsContainer.setGestureMargins(0, 0, 0, navBar.getVisibleBounds().height());
+ allAppsContainer.setGestureMargins(0, 0, 0, navBar.getVisibleBounds().height() + 1);
final BySelector appIconSelector = AppIcon.getAppIconSelector(appName, mLauncher);
if (!hasClickableIcon(allAppsContainer, appIconSelector)) {
scrollBackToBeginning();
@@ -141,8 +141,8 @@
* Flings forward (down) and waits the fling's end.
*/
public void flingForward() {
- try(LauncherInstrumentation.Closable c =
- mLauncher.addContextLayer("want to fling forward in all apps")) {
+ try (LauncherInstrumentation.Closable c =
+ mLauncher.addContextLayer("want to fling forward in all apps")) {
final UiObject2 allAppsContainer = verifyActiveContainer();
// Start the gesture in the center to avoid starting at elements near the top.
allAppsContainer.setGestureMargins(0, 0, 0, mHeight / 2);
@@ -156,8 +156,8 @@
* Flings backward (up) and waits the fling's end.
*/
public void flingBackward() {
- try(LauncherInstrumentation.Closable c =
- mLauncher.addContextLayer("want to fling backward in all apps")) {
+ try (LauncherInstrumentation.Closable c =
+ mLauncher.addContextLayer("want to fling backward in all apps")) {
final UiObject2 allAppsContainer = verifyActiveContainer();
// Start the gesture in the center, for symmetry with forward.
allAppsContainer.setGestureMargins(0, mHeight / 2, 0, 0);
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index d60c79d..37a5031 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -354,12 +354,7 @@
log(action = "0-button: from another app");
assertTrue("Launcher is visible, don't know how to go home",
!mDevice.hasObject(By.pkg(getLauncherPackageName())));
- final UiObject2 navBar = waitForSystemUiObject("navigation_bar_frame");
-
- swipe(
- navBar.getVisibleBounds().centerX(), navBar.getVisibleBounds().centerY(),
- navBar.getVisibleBounds().centerX(), 0,
- BACKGROUND_APP_STATE_ORDINAL, ZERO_BUTTON_STEPS_FROM_BACKGROUND_TO_HOME);
+ mDevice.pressHome();
}
} else {
log(action = "clicking home button");