1 /* 2 * Copyright (C) 2021 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.android.quickstep.interaction; 17 18 import android.animation.Animator; 19 import android.animation.AnimatorListenerAdapter; 20 import android.animation.AnimatorSet; 21 import android.animation.ObjectAnimator; 22 import android.animation.ValueAnimator; 23 import android.annotation.ColorInt; 24 import android.content.Context; 25 import android.graphics.Outline; 26 import android.graphics.Rect; 27 import android.util.AttributeSet; 28 import android.view.View; 29 import android.view.ViewOutlineProvider; 30 31 import androidx.annotation.NonNull; 32 import androidx.annotation.Nullable; 33 import androidx.cardview.widget.CardView; 34 import androidx.constraintlayout.widget.ConstraintLayout; 35 36 import com.android.launcher3.R; 37 38 import java.util.ArrayList; 39 40 /** 41 * Helper View for the gesture tutorial mock previous app task view. 42 * 43 * This helper class allows animating from a single-row layout to a two-row layout as seen in 44 * large screen devices. 45 */ 46 public class AnimatedTaskView extends ConstraintLayout { 47 48 private View mFullTaskView; 49 private CardView mTopTaskView; 50 private CardView mBottomTaskView; 51 52 private ViewOutlineProvider mTaskViewOutlineProvider = null; 53 private final Rect mTaskViewAnimatedRect = new Rect(); 54 private float mTaskViewAnimatedRadius; 55 AnimatedTaskView(@onNull Context context)56 public AnimatedTaskView(@NonNull Context context) { 57 super(context); 58 } 59 AnimatedTaskView(@onNull Context context, @Nullable AttributeSet attrs)60 public AnimatedTaskView(@NonNull Context context, 61 @Nullable AttributeSet attrs) { 62 super(context, attrs); 63 } 64 AnimatedTaskView( @onNull Context context, @Nullable AttributeSet attrs, int defStyleAttr)65 public AnimatedTaskView( 66 @NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) { 67 super(context, attrs, defStyleAttr); 68 } 69 AnimatedTaskView( @onNull Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes)70 public AnimatedTaskView( 71 @NonNull Context context, 72 @Nullable AttributeSet attrs, 73 int defStyleAttr, 74 int defStyleRes) { 75 super(context, attrs, defStyleAttr, defStyleRes); 76 } 77 78 @Override onFinishInflate()79 protected void onFinishInflate() { 80 super.onFinishInflate(); 81 82 mFullTaskView = findViewById(R.id.full_task_view); 83 mTopTaskView = findViewById(R.id.top_task_view); 84 mBottomTaskView = findViewById(R.id.bottom_task_view); 85 86 setToSingleRowLayout(false); 87 } 88 createAnimationToMultiRowLayout()89 AnimatorSet createAnimationToMultiRowLayout() { 90 if (mTaskViewOutlineProvider == null) { 91 // This is an illegal state. 92 return null; 93 } 94 Outline startOutline = new Outline(); 95 mTaskViewOutlineProvider.getOutline(this, startOutline); 96 Rect outlineStartRect = new Rect(); 97 startOutline.getRect(outlineStartRect); 98 int endRectBottom = mTopTaskView.getHeight(); 99 float outlineStartRadius = startOutline.getRadius(); 100 float outlineEndRadius = getContext().getResources().getDimensionPixelSize( 101 R.dimen.gesture_tutorial_small_task_view_corner_radius); 102 103 ValueAnimator outlineAnimator = ValueAnimator.ofFloat(0f, 1f); 104 outlineAnimator.addUpdateListener(valueAnimator -> { 105 float progress = (float) valueAnimator.getAnimatedValue(); 106 mTaskViewAnimatedRect.bottom = (int) (outlineStartRect.bottom 107 + progress * (endRectBottom - outlineStartRect.bottom)); 108 mTaskViewAnimatedRadius = outlineStartRadius 109 + progress * (outlineEndRadius - outlineStartRadius); 110 mFullTaskView.invalidateOutline(); 111 }); 112 outlineAnimator.addListener(new AnimatorListenerAdapter() { 113 @Override 114 public void onAnimationStart(Animator animation) { 115 super.onAnimationStart(animation); 116 117 mTaskViewAnimatedRect.set(outlineStartRect); 118 mTaskViewAnimatedRadius = outlineStartRadius; 119 120 mFullTaskView.setClipToOutline(true); 121 mFullTaskView.setOutlineProvider(new ViewOutlineProvider() { 122 @Override 123 public void getOutline(View view, Outline outline) { 124 outline.setRoundRect(mTaskViewAnimatedRect, mTaskViewAnimatedRadius); 125 } 126 }); 127 } 128 129 @Override 130 public void onAnimationEnd(Animator animation) { 131 super.onAnimationEnd(animation); 132 mFullTaskView.setOutlineProvider(mTaskViewOutlineProvider); 133 } 134 135 @Override 136 public void onAnimationCancel(Animator animation) { 137 super.onAnimationCancel(animation); 138 mFullTaskView.setOutlineProvider(mTaskViewOutlineProvider); 139 } 140 }); 141 142 ArrayList<Animator> animations = new ArrayList<>(); 143 animations.add(ObjectAnimator.ofFloat( 144 mBottomTaskView, View.TRANSLATION_X, -mBottomTaskView.getWidth(), 0)); 145 animations.add(outlineAnimator); 146 147 AnimatorSet animatorSet = new AnimatorSet(); 148 animatorSet.playTogether(animations); 149 animatorSet.addListener(new AnimatorListenerAdapter() { 150 @Override 151 public void onAnimationStart(Animator animation) { 152 super.onAnimationStart(animation); 153 setToSingleRowLayout(true); 154 155 setPadding(0, outlineStartRect.top, 0, getHeight() - outlineStartRect.bottom); 156 } 157 158 @Override 159 public void onAnimationEnd(Animator animation) { 160 super.onAnimationEnd(animation); 161 setToMultiRowLayout(); 162 } 163 164 @Override 165 public void onAnimationCancel(Animator animation) { 166 super.onAnimationCancel(animation); 167 setToMultiRowLayout(); 168 } 169 }); 170 171 return animatorSet; 172 } 173 setToSingleRowLayout(boolean forAnimation)174 void setToSingleRowLayout(boolean forAnimation) { 175 mFullTaskView.setVisibility(VISIBLE); 176 mTopTaskView.setVisibility(INVISIBLE); 177 mBottomTaskView.setVisibility(forAnimation ? VISIBLE : INVISIBLE); 178 } 179 setToMultiRowLayout()180 void setToMultiRowLayout() { 181 mFullTaskView.setVisibility(INVISIBLE); 182 mTopTaskView.setVisibility(VISIBLE); 183 mBottomTaskView.setVisibility(VISIBLE); 184 } 185 setFakeTaskViewFillColor(@olorInt int colorResId)186 void setFakeTaskViewFillColor(@ColorInt int colorResId) { 187 mFullTaskView.setBackgroundColor(colorResId); 188 mTopTaskView.setCardBackgroundColor(colorResId); 189 mBottomTaskView.setCardBackgroundColor(colorResId); 190 } 191 192 @Override setClipToOutline(boolean clipToOutline)193 public void setClipToOutline(boolean clipToOutline) { 194 mFullTaskView.setClipToOutline(clipToOutline); 195 } 196 197 @Override setOutlineProvider(ViewOutlineProvider provider)198 public void setOutlineProvider(ViewOutlineProvider provider) { 199 mTaskViewOutlineProvider = provider; 200 mFullTaskView.setOutlineProvider(mTaskViewOutlineProvider); 201 } 202 } 203