Merge "Add app to overview anim for Recents Go." into ub-launcher3-master

This commit is contained in:
TreeHugger Robot 2019-03-19 00:32:40 +00:00 committed by Android (Google) Code Review
commit 0825e414f0
7 changed files with 204 additions and 13 deletions

View File

@ -21,15 +21,15 @@
android:orientation="horizontal"> android:orientation="horizontal">
<FrameLayout <FrameLayout
android:id="@+id/task_icon_and_thumbnail" android:id="@+id/task_icon_and_thumbnail"
android:layout_width="@dimen/task_item_height" android:layout_width="@dimen/task_thumbnail_and_icon_view_size"
android:layout_height="@dimen/task_item_height" android:layout_height="@dimen/task_thumbnail_and_icon_view_size"
android:layout_gravity="center_vertical" android:layout_gravity="center_vertical"
android:layout_marginHorizontal="8dp" android:layout_marginHorizontal="8dp"
android:layout_marginVertical="@dimen/task_item_half_vert_margin"> android:layout_marginVertical="@dimen/task_item_half_vert_margin">
<ImageView <ImageView
android:id="@+id/task_thumbnail" android:id="@+id/task_thumbnail"
android:layout_width="wrap_content" android:layout_width="@dimen/task_thumbnail_width"
android:layout_height="match_parent" android:layout_height="@dimen/task_thumbnail_height"
android:layout_gravity="top|start"/> android:layout_gravity="top|start"/>
<ImageView <ImageView
android:id="@+id/task_icon" android:id="@+id/task_icon"

View File

@ -15,7 +15,9 @@
limitations under the License. limitations under the License.
--> -->
<resources> <resources>
<dimen name="task_item_height">60dp</dimen>
<dimen name="task_item_half_vert_margin">8dp</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> <dimen name="task_icon_size">36dp</dimen>
</resources> </resources>

View File

@ -15,15 +15,29 @@
*/ */
package com.android.quickstep; 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.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.AnimatorSet;
import android.animation.ValueAnimator; import android.animation.ValueAnimator;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.util.Log; import android.util.Log;
import android.view.View;
import androidx.annotation.NonNull;
import com.android.launcher3.BaseDraggingActivity; import com.android.launcher3.BaseDraggingActivity;
import com.android.quickstep.util.MultiValueUpdateListener;
import com.android.quickstep.util.RemoteAnimationProvider; 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.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. * 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 final class AppToOverviewAnimationProvider<T extends BaseDraggingActivity> implements
RemoteAnimationProvider { 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 static final String TAG = "AppToOverviewAnimationProvider";
private final ActivityControlHelper<T> mHelper; private final ActivityControlHelper<T> mHelper;
private final int mTargetTaskId;
private IconRecentsView mRecentsView;
AppToOverviewAnimationProvider(ActivityControlHelper<T> helper, int targetTaskId) { AppToOverviewAnimationProvider(ActivityControlHelper<T> helper, int targetTaskId) {
mHelper = helper; mHelper = helper;
mTargetTaskId = targetTaskId;
} }
/** /**
@ -54,35 +72,157 @@ final class AppToOverviewAnimationProvider<T extends BaseDraggingActivity> imple
false /* animate activity */, (controller) -> { false /* animate activity */, (controller) -> {
controller.dispatchOnStart(); controller.dispatchOnStart();
ValueAnimator anim = controller.getAnimationPlayer() ValueAnimator anim = controller.getAnimationPlayer()
.setDuration(RECENTS_LAUNCH_DURATION); .setDuration(getRecentsLaunchDuration());
anim.setInterpolator(FAST_OUT_SLOW_IN); anim.setInterpolator(FAST_OUT_SLOW_IN);
anim.start(); anim.start();
}); });
factory.onRemoteAnimationReceived(null); factory.onRemoteAnimationReceived(null);
factory.createActivityController(RECENTS_LAUNCH_DURATION); factory.createActivityController(getRecentsLaunchDuration());
mRecentsView = activity.getOverviewPanel();
return false; 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 * @param targetCompats the target apps
* @return animation from app to overview * @return animation from app to overview
*/ */
@Override @Override
public AnimatorSet createWindowAnimation(RemoteAnimationTargetCompat[] targetCompats) { public AnimatorSet createWindowAnimation(RemoteAnimationTargetCompat[] targetCompats) {
//TODO: Implement the overview to app window animation for Go.
AnimatorSet anim = new AnimatorSet(); 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; 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. * Get duration of animation from app to overview.
* *
* @return duration of animation * @return duration of animation
*/ */
long getRecentsLaunchDuration() { long getRecentsLaunchDuration() {
return RECENTS_LAUNCH_DURATION; return APP_SCALE_DOWN_DURATION;
} }
} }

View File

@ -21,6 +21,7 @@ import static com.android.launcher3.LauncherState.OVERVIEW;
import com.android.launcher3.Launcher; import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAppState; import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherInitListener; import com.android.launcher3.LauncherInitListener;
import com.android.launcher3.LauncherState;
import com.android.launcher3.anim.AnimatorPlaybackController; import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.userevent.nano.LauncherLogProto; import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.quickstep.views.IconRecentsView; import com.android.quickstep.views.IconRecentsView;
@ -38,10 +39,14 @@ public final class LauncherActivityControllerHelper extends GoActivityControlHel
public AnimationFactory prepareRecentsUI(Launcher activity, public AnimationFactory prepareRecentsUI(Launcher activity,
boolean activityVisible, boolean animateActivity, boolean activityVisible, boolean animateActivity,
Consumer<AnimatorPlaybackController> callback) { Consumer<AnimatorPlaybackController> callback) {
LauncherState fromState = activity.getStateManager().getState();
//TODO: Implement this based off where the recents view needs to be for app => recents anim. //TODO: Implement this based off where the recents view needs to be for app => recents anim.
return new AnimationFactory() { return new AnimationFactory() {
@Override @Override
public void createActivityController(long transitionLength) {} public void createActivityController(long transitionLength) {
callback.accept(activity.getStateManager().createAnimationToNewWorkspace(
fromState, OVERVIEW, transitionLength));
}
@Override @Override
public void onTransitionCancelled() {} public void onTransitionCancelled() {}

View File

@ -15,10 +15,12 @@
*/ */
package com.android.quickstep; package com.android.quickstep;
import android.util.ArrayMap;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.ViewGroup; import android.view.ViewGroup;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.RecyclerView.Adapter; import androidx.recyclerview.widget.RecyclerView.Adapter;
import com.android.launcher3.R; 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 int MAX_TASKS_TO_DISPLAY = 6;
private static final String TAG = "TaskAdapter"; private static final String TAG = "TaskAdapter";
private final TaskListLoader mLoader; private final TaskListLoader mLoader;
private final ArrayMap<Integer, TaskItemView> mTaskIdToViewMap = new ArrayMap<>();
private TaskInputController mInputController; private TaskInputController mInputController;
public TaskAdapter(@NonNull TaskListLoader loader) { public TaskAdapter(@NonNull TaskListLoader loader) {
@ -46,6 +49,16 @@ public final class TaskAdapter extends Adapter<TaskHolder> {
mInputController = inputController; 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 @Override
public TaskHolder onCreateViewHolder(ViewGroup parent, int viewType) { public TaskHolder onCreateViewHolder(ViewGroup parent, int viewType) {
TaskItemView itemView = (TaskItemView) LayoutInflater.from(parent.getContext()) TaskItemView itemView = (TaskItemView) LayoutInflater.from(parent.getContext())
@ -63,6 +76,17 @@ public final class TaskAdapter extends Adapter<TaskHolder> {
return; return;
} }
holder.bindTask(tasks.get(position)); 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 @Override

View File

@ -27,6 +27,7 @@ import android.view.ViewDebug;
import android.widget.FrameLayout; import android.widget.FrameLayout;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.ItemTouchHelper; import androidx.recyclerview.widget.ItemTouchHelper;
import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView; 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) { public void setTranslationYFactor(float translationFactor) {
mTranslationYFactor = translationFactor; mTranslationYFactor = translationFactor;
setTranslationY(computeTranslationYForFactor(mTranslationYFactor)); setTranslationY(computeTranslationYForFactor(mTranslationYFactor));

View File

@ -19,6 +19,7 @@ import android.content.Context;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.view.View;
import android.widget.ImageView; import android.widget.ImageView;
import android.widget.LinearLayout; import android.widget.LinearLayout;
import android.widget.TextView; import android.widget.TextView;
@ -76,4 +77,8 @@ public final class TaskItemView extends LinearLayout {
public void setThumbnail(Bitmap thumbnail) { public void setThumbnail(Bitmap thumbnail) {
mThumbnailView.setImageBitmap(thumbnail); mThumbnailView.setImageBitmap(thumbnail);
} }
public View getThumbnailView() {
return mThumbnailView;
}
} }