Add app to overview anim for Recents Go.
This CL adds the first iteration of window animation to go from the app back to recents provided that the view is ready to be visible to the user. Bug: 114136250 Test: Go to recents, launch app, press overview to go back to recents Change-Id: Ic0567e7c87fa964bdad25d07eca61b78407a9ff5
This commit is contained in:
parent
2ec9f290bf
commit
502847f7eb
|
@ -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"
|
||||
|
|
|
@ -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>
|
|
@ -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 @@ import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
|
|||
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 @@ final class AppToOverviewAnimationProvider<T extends BaseDraggingActivity> imple
|
|||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@ import static com.android.launcher3.LauncherState.OVERVIEW;
|
|||
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 final class LauncherActivityControllerHelper extends GoActivityControlHel
|
|||
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() {}
|
||||
|
|
|
@ -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 @@ public final class TaskAdapter extends Adapter<TaskHolder> {
|
|||
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 @@ public final class TaskAdapter extends Adapter<TaskHolder> {
|
|||
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 @@ public final class TaskAdapter extends Adapter<TaskHolder> {
|
|||
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
|
||||
|
|
|
@ -27,6 +27,7 @@ import android.view.ViewDebug;
|
|||
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 @@ public final class IconRecentsView extends FrameLayout {
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 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));
|
||||
|
|
|
@ -19,6 +19,7 @@ import android.content.Context;
|
|||
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 final class TaskItemView extends LinearLayout {
|
|||
public void setThumbnail(Bitmap thumbnail) {
|
||||
mThumbnailView.setImageBitmap(thumbnail);
|
||||
}
|
||||
|
||||
public View getThumbnailView() {
|
||||
return mThumbnailView;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue