Merge "Add app to overview anim for Recents Go." into ub-launcher3-master
diff --git a/go/quickstep/res/layout/task_item_view.xml b/go/quickstep/res/layout/task_item_view.xml
index ee67d49..048e9c5 100644
--- a/go/quickstep/res/layout/task_item_view.xml
+++ b/go/quickstep/res/layout/task_item_view.xml
@@ -21,15 +21,15 @@
android:orientation="horizontal">
<FrameLayout
android:id="@+id/task_icon_and_thumbnail"
- android:layout_width="@dimen/task_item_height"
- android:layout_height="@dimen/task_item_height"
+ android:layout_width="@dimen/task_thumbnail_and_icon_view_size"
+ android:layout_height="@dimen/task_thumbnail_and_icon_view_size"
android:layout_gravity="center_vertical"
android:layout_marginHorizontal="8dp"
android:layout_marginVertical="@dimen/task_item_half_vert_margin">
<ImageView
android:id="@+id/task_thumbnail"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
+ android:layout_width="@dimen/task_thumbnail_width"
+ android:layout_height="@dimen/task_thumbnail_height"
android:layout_gravity="top|start"/>
<ImageView
android:id="@+id/task_icon"
diff --git a/go/quickstep/res/values/dimens.xml b/go/quickstep/res/values/dimens.xml
index 28cc1eb..7269704 100644
--- a/go/quickstep/res/values/dimens.xml
+++ b/go/quickstep/res/values/dimens.xml
@@ -15,7 +15,9 @@
limitations under the License.
-->
<resources>
- <dimen name="task_item_height">60dp</dimen>
<dimen name="task_item_half_vert_margin">8dp</dimen>
+ <dimen name="task_thumbnail_and_icon_view_size">60dp</dimen>
+ <dimen name="task_thumbnail_height">60dp</dimen>
+ <dimen name="task_thumbnail_width">36dp</dimen>
<dimen name="task_icon_size">36dp</dimen>
</resources>
\ No newline at end of file
diff --git a/go/quickstep/src/com/android/quickstep/AppToOverviewAnimationProvider.java b/go/quickstep/src/com/android/quickstep/AppToOverviewAnimationProvider.java
index f199643..defed84 100644
--- a/go/quickstep/src/com/android/quickstep/AppToOverviewAnimationProvider.java
+++ b/go/quickstep/src/com/android/quickstep/AppToOverviewAnimationProvider.java
@@ -15,15 +15,29 @@
*/
package com.android.quickstep;
+import static com.android.launcher3.anim.Interpolators.ACCEL_2;
+import static com.android.launcher3.anim.Interpolators.ACCEL_DEACCEL;
import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
+import static com.android.quickstep.util.RemoteAnimationProvider.getLayer;
+import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
+import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_OPENING;
import android.animation.AnimatorSet;
import android.animation.ValueAnimator;
+import android.graphics.Matrix;
+import android.graphics.Rect;
import android.util.Log;
+import android.view.View;
+
+import androidx.annotation.NonNull;
import com.android.launcher3.BaseDraggingActivity;
+import com.android.quickstep.util.MultiValueUpdateListener;
import com.android.quickstep.util.RemoteAnimationProvider;
+import com.android.quickstep.views.IconRecentsView;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
+import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat;
+import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams;
/**
* Provider for the atomic remote window animation from the app to the overview.
@@ -33,13 +47,17 @@
final class AppToOverviewAnimationProvider<T extends BaseDraggingActivity> implements
RemoteAnimationProvider {
- private static final long RECENTS_LAUNCH_DURATION = 250;
+ private static final long APP_TO_THUMBNAIL_FADE_DURATION = 50;
+ private static final long APP_SCALE_DOWN_DURATION = 400;
private static final String TAG = "AppToOverviewAnimationProvider";
private final ActivityControlHelper<T> mHelper;
+ private final int mTargetTaskId;
+ private IconRecentsView mRecentsView;
AppToOverviewAnimationProvider(ActivityControlHelper<T> helper, int targetTaskId) {
mHelper = helper;
+ mTargetTaskId = targetTaskId;
}
/**
@@ -54,35 +72,157 @@
false /* animate activity */, (controller) -> {
controller.dispatchOnStart();
ValueAnimator anim = controller.getAnimationPlayer()
- .setDuration(RECENTS_LAUNCH_DURATION);
+ .setDuration(getRecentsLaunchDuration());
anim.setInterpolator(FAST_OUT_SLOW_IN);
anim.start();
});
factory.onRemoteAnimationReceived(null);
- factory.createActivityController(RECENTS_LAUNCH_DURATION);
+ factory.createActivityController(getRecentsLaunchDuration());
+ mRecentsView = activity.getOverviewPanel();
return false;
}
/**
- * Create remote window animation from the currently running app to the overview panel.
+ * Create remote window animation from the currently running app to the overview panel. Should
+ * be called after {@link #onActivityReady}.
*
* @param targetCompats the target apps
* @return animation from app to overview
*/
@Override
public AnimatorSet createWindowAnimation(RemoteAnimationTargetCompat[] targetCompats) {
- //TODO: Implement the overview to app window animation for Go.
AnimatorSet anim = new AnimatorSet();
- anim.play(ValueAnimator.ofInt(0, 1).setDuration(RECENTS_LAUNCH_DURATION));
+ if (mRecentsView == null) {
+ if (Log.isLoggable(TAG, Log.WARN)) {
+ Log.w(TAG, "No recents view. Using stub animation.");
+ }
+ anim.play(ValueAnimator.ofInt(0, 1).setDuration(getRecentsLaunchDuration()));
+ return anim;
+ }
+
+ RemoteAnimationTargetCompat recentsTarget = null;
+ RemoteAnimationTargetCompat closingAppTarget = null;
+
+ for (RemoteAnimationTargetCompat target : targetCompats) {
+ if (target.mode == MODE_OPENING) {
+ recentsTarget = target;
+ } else if (target.mode == MODE_CLOSING && target.taskId == mTargetTaskId) {
+ closingAppTarget = target;
+ }
+ }
+
+ if (closingAppTarget == null) {
+ if (Log.isLoggable(TAG, Log.WARN)) {
+ Log.w(TAG, "No closing app target. Using stub animation.");
+ }
+ anim.play(ValueAnimator.ofInt(0, 1).setDuration(getRecentsLaunchDuration()));
+ return anim;
+ }
+ if (recentsTarget == null) {
+ if (Log.isLoggable(TAG, Log.WARN)) {
+ Log.w(TAG, "No recents target. Using stub animation.");
+ }
+ anim.play(ValueAnimator.ofInt(0, 1).setDuration(getRecentsLaunchDuration()));
+ return anim;
+ }
+
+ View thumbnailView = mRecentsView.getThumbnailViewForTask(mTargetTaskId);
+ if (thumbnailView == null) {
+ // TODO: We should either 1) guarantee the view is loaded before attempting this
+ // or 2) have a backup animation.
+ if (Log.isLoggable(TAG, Log.WARN)) {
+ Log.w(TAG, "No thumbnail view for running task. Using stub animation.");
+ }
+ anim.play(ValueAnimator.ofInt(0, 1).setDuration(getRecentsLaunchDuration()));
+ return anim;
+ }
+
+ playAppScaleDownAnim(anim, closingAppTarget, recentsTarget, thumbnailView);
+
return anim;
}
/**
+ * Animate a closing app to scale down to the location of the thumbnail view in recents.
+ *
+ * @param anim animator set
+ * @param appTarget the app surface thats closing
+ * @param recentsTarget the surface containing recents
+ * @param thumbnailView the thumbnail view to animate to
+ */
+ private void playAppScaleDownAnim(@NonNull AnimatorSet anim,
+ @NonNull RemoteAnimationTargetCompat appTarget,
+ @NonNull RemoteAnimationTargetCompat recentsTarget, @NonNull View thumbnailView) {
+
+ // Identify where the entering remote app should animate to.
+ Rect endRect = new Rect();
+ thumbnailView.getGlobalVisibleRect(endRect);
+
+ Rect appBounds = appTarget.sourceContainerBounds;
+
+ ValueAnimator valueAnimator = ValueAnimator.ofInt(0, 1);
+ valueAnimator.setDuration(APP_SCALE_DOWN_DURATION);
+
+ SyncRtSurfaceTransactionApplierCompat surfaceApplier =
+ new SyncRtSurfaceTransactionApplierCompat(thumbnailView);
+
+ // Keep recents visible throughout the animation.
+ SurfaceParams[] params = new SurfaceParams[2];
+ params[0] = new SurfaceParams(recentsTarget.leash, 1f, null /* matrix */,
+ null /* windowCrop */, getLayer(recentsTarget, MODE_OPENING), 0 /* cornerRadius */);
+
+ valueAnimator.addUpdateListener(new MultiValueUpdateListener() {
+ private final FloatProp mScaleX;
+ private final FloatProp mScaleY;
+ private final FloatProp mTranslationX;
+ private final FloatProp mTranslationY;
+ private final FloatProp mAlpha;
+
+ {
+ // Scale down and move to view location.
+ float endScaleX = ((float) endRect.width()) / appBounds.width();
+ mScaleX = new FloatProp(1f, endScaleX, 0, APP_SCALE_DOWN_DURATION,
+ ACCEL_DEACCEL);
+ float endScaleY = ((float) endRect.height()) / appBounds.height();
+ mScaleY = new FloatProp(1f, endScaleY, 0, APP_SCALE_DOWN_DURATION,
+ ACCEL_DEACCEL);
+ float endTranslationX = endRect.left -
+ (appBounds.width() - thumbnailView.getWidth()) / 2.0f;
+ mTranslationX = new FloatProp(0, endTranslationX, 0, APP_SCALE_DOWN_DURATION,
+ ACCEL_DEACCEL);
+ float endTranslationY = endRect.top -
+ (appBounds.height() - thumbnailView.getHeight()) / 2.0f;
+ mTranslationY = new FloatProp(0, endTranslationY, 0, APP_SCALE_DOWN_DURATION,
+ ACCEL_DEACCEL);
+
+ // Fade out quietly near the end to be replaced by the real view.
+ mAlpha = new FloatProp(1.0f, 0,
+ APP_SCALE_DOWN_DURATION - APP_TO_THUMBNAIL_FADE_DURATION,
+ APP_TO_THUMBNAIL_FADE_DURATION, ACCEL_2);
+ }
+
+ @Override
+ public void onUpdate(float percent) {
+ Matrix m = new Matrix();
+ m.setScale(mScaleX.value, mScaleY.value,
+ appBounds.width() / 2.0f, appBounds.height() / 2.0f);
+ m.postTranslate(mTranslationX.value, mTranslationY.value);
+
+ params[1] = new SurfaceParams(appTarget.leash, mAlpha.value, m,
+ null /* windowCrop */, getLayer(appTarget, MODE_CLOSING),
+ 0 /* cornerRadius */);
+ surfaceApplier.scheduleApply(params);
+ }
+ });
+ anim.play(valueAnimator);
+ }
+
+ /**
* Get duration of animation from app to overview.
*
* @return duration of animation
*/
long getRecentsLaunchDuration() {
- return RECENTS_LAUNCH_DURATION;
+ return APP_SCALE_DOWN_DURATION;
}
}
diff --git a/go/quickstep/src/com/android/quickstep/LauncherActivityControllerHelper.java b/go/quickstep/src/com/android/quickstep/LauncherActivityControllerHelper.java
index d55d2e5..40db7dd 100644
--- a/go/quickstep/src/com/android/quickstep/LauncherActivityControllerHelper.java
+++ b/go/quickstep/src/com/android/quickstep/LauncherActivityControllerHelper.java
@@ -21,6 +21,7 @@
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherInitListener;
+import com.android.launcher3.LauncherState;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.quickstep.views.IconRecentsView;
@@ -38,10 +39,14 @@
public AnimationFactory prepareRecentsUI(Launcher activity,
boolean activityVisible, boolean animateActivity,
Consumer<AnimatorPlaybackController> callback) {
+ LauncherState fromState = activity.getStateManager().getState();
//TODO: Implement this based off where the recents view needs to be for app => recents anim.
return new AnimationFactory() {
@Override
- public void createActivityController(long transitionLength) {}
+ public void createActivityController(long transitionLength) {
+ callback.accept(activity.getStateManager().createAnimationToNewWorkspace(
+ fromState, OVERVIEW, transitionLength));
+ }
@Override
public void onTransitionCancelled() {}
diff --git a/go/quickstep/src/com/android/quickstep/TaskAdapter.java b/go/quickstep/src/com/android/quickstep/TaskAdapter.java
index 4f3d1e4..9a2c0f8 100644
--- a/go/quickstep/src/com/android/quickstep/TaskAdapter.java
+++ b/go/quickstep/src/com/android/quickstep/TaskAdapter.java
@@ -15,10 +15,12 @@
*/
package com.android.quickstep;
+import android.util.ArrayMap;
import android.view.LayoutInflater;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import androidx.recyclerview.widget.RecyclerView.Adapter;
import com.android.launcher3.R;
@@ -36,6 +38,7 @@
private static final int MAX_TASKS_TO_DISPLAY = 6;
private static final String TAG = "TaskAdapter";
private final TaskListLoader mLoader;
+ private final ArrayMap<Integer, TaskItemView> mTaskIdToViewMap = new ArrayMap<>();
private TaskInputController mInputController;
public TaskAdapter(@NonNull TaskListLoader loader) {
@@ -46,6 +49,16 @@
mInputController = inputController;
}
+ /**
+ * Get task item view for a given task id if it's attached to the view.
+ *
+ * @param taskId task id to search for
+ * @return corresponding task item view if it's attached, null otherwise
+ */
+ public @Nullable TaskItemView getTaskItemView(int taskId) {
+ return mTaskIdToViewMap.get(taskId);
+ }
+
@Override
public TaskHolder onCreateViewHolder(ViewGroup parent, int viewType) {
TaskItemView itemView = (TaskItemView) LayoutInflater.from(parent.getContext())
@@ -63,6 +76,17 @@
return;
}
holder.bindTask(tasks.get(position));
+
+ }
+
+ @Override
+ public void onViewAttachedToWindow(@NonNull TaskHolder holder) {
+ mTaskIdToViewMap.put(holder.getTask().key.id, (TaskItemView) holder.itemView);
+ }
+
+ @Override
+ public void onViewDetachedFromWindow(@NonNull TaskHolder holder) {
+ mTaskIdToViewMap.remove(holder.getTask().key.id);
}
@Override
diff --git a/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java b/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java
index e294282..3fdaefe 100644
--- a/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java
+++ b/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java
@@ -27,6 +27,7 @@
import android.widget.FrameLayout;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import androidx.recyclerview.widget.ItemTouchHelper;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
@@ -155,6 +156,20 @@
});
}
+ /**
+ * Get the thumbnail view associated with a task for the purposes of animation.
+ *
+ * @param taskId task id of thumbnail view to get
+ * @return the thumbnail view for the task if attached, null otherwise
+ */
+ public @Nullable View getThumbnailViewForTask(int taskId) {
+ TaskItemView view = mTaskAdapter.getTaskItemView(taskId);
+ if (view == null) {
+ return null;
+ }
+ return view.getThumbnailView();
+ }
+
public void setTranslationYFactor(float translationFactor) {
mTranslationYFactor = translationFactor;
setTranslationY(computeTranslationYForFactor(mTranslationYFactor));
diff --git a/go/quickstep/src/com/android/quickstep/views/TaskItemView.java b/go/quickstep/src/com/android/quickstep/views/TaskItemView.java
index 3818965..373f107 100644
--- a/go/quickstep/src/com/android/quickstep/views/TaskItemView.java
+++ b/go/quickstep/src/com/android/quickstep/views/TaskItemView.java
@@ -19,6 +19,7 @@
import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
+import android.view.View;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
@@ -76,4 +77,8 @@
public void setThumbnail(Bitmap thumbnail) {
mThumbnailView.setImageBitmap(thumbnail);
}
+
+ public View getThumbnailView() {
+ return mThumbnailView;
+ }
}