1 /* 2 * Copyright (C) 2020 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 17 package com.android.wm.shell.draganddrop; 18 19 import android.animation.ObjectAnimator; 20 import android.animation.RectEvaluator; 21 import android.content.Context; 22 import android.graphics.Canvas; 23 import android.graphics.Color; 24 import android.graphics.ColorFilter; 25 import android.graphics.Paint; 26 import android.graphics.PixelFormat; 27 import android.graphics.Rect; 28 import android.graphics.drawable.Drawable; 29 import android.util.IntProperty; 30 import android.util.Property; 31 import android.view.animation.Interpolator; 32 33 import androidx.annotation.NonNull; 34 import androidx.annotation.Nullable; 35 36 import com.android.internal.graphics.ColorUtils; 37 import com.android.internal.policy.ScreenDecorationsUtils; 38 import com.android.internal.protolog.common.ProtoLog; 39 import com.android.wm.shell.protolog.ShellProtoLogGroup; 40 import com.android.wm.shell.R; 41 42 /** 43 * Drawable to draw the region that the target will have once it is dropped. 44 */ 45 public class DropOutlineDrawable extends Drawable { 46 47 private static final int BOUNDS_DURATION = 200; 48 private static final int ALPHA_DURATION = 135; 49 50 private final IntProperty<DropOutlineDrawable> ALPHA = 51 new IntProperty<DropOutlineDrawable>("alpha") { 52 @Override 53 public void setValue(DropOutlineDrawable d, int alpha) { 54 d.setAlpha(alpha); 55 } 56 57 @Override 58 public Integer get(DropOutlineDrawable d) { 59 return d.getAlpha(); 60 } 61 }; 62 63 private final Property<DropOutlineDrawable, Rect> BOUNDS = 64 new Property<DropOutlineDrawable, Rect>(Rect.class, "bounds") { 65 @Override 66 public void set(DropOutlineDrawable d, Rect bounds) { 67 d.setRegionBounds(bounds); 68 } 69 70 @Override 71 public Rect get(DropOutlineDrawable d) { 72 return d.getRegionBounds(); 73 } 74 }; 75 76 private final RectEvaluator mRectEvaluator = new RectEvaluator(new Rect()); 77 private ObjectAnimator mBoundsAnimator; 78 private ObjectAnimator mAlphaAnimator; 79 80 private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); 81 private final Rect mBounds = new Rect(); 82 private final float mCornerRadius; 83 private final int mMaxAlpha; 84 private int mColor; 85 DropOutlineDrawable(Context context)86 public DropOutlineDrawable(Context context) { 87 super(); 88 // TODO(b/169894807): Use corner specific radii and maybe lower radius for non-edge corners 89 mCornerRadius = ScreenDecorationsUtils.getWindowCornerRadius(context); 90 mColor = context.getColor(R.color.drop_outline_background); 91 mMaxAlpha = Color.alpha(mColor); 92 // Initialize as hidden 93 ALPHA.set(this, 0); 94 } 95 96 @Override setColorFilter(@ullable ColorFilter colorFilter)97 public void setColorFilter(@Nullable ColorFilter colorFilter) { 98 // Do nothing 99 } 100 101 @Override setAlpha(int alpha)102 public void setAlpha(int alpha) { 103 mColor = ColorUtils.setAlphaComponent(mColor, alpha); 104 mPaint.setColor(mColor); 105 invalidateSelf(); 106 } 107 108 @Override getAlpha()109 public int getAlpha() { 110 return Color.alpha(mColor); 111 } 112 113 @Override getOpacity()114 public int getOpacity() { 115 return PixelFormat.TRANSLUCENT; 116 } 117 118 @Override onBoundsChange(Rect bounds)119 protected void onBoundsChange(Rect bounds) { 120 invalidateSelf(); 121 } 122 123 @Override draw(@onNull Canvas canvas)124 public void draw(@NonNull Canvas canvas) { 125 canvas.drawRoundRect(mBounds.left, mBounds.top, mBounds.right, mBounds.bottom, 126 mCornerRadius, mCornerRadius, mPaint); 127 } 128 setRegionBounds(Rect bounds)129 public void setRegionBounds(Rect bounds) { 130 mBounds.set(bounds); 131 invalidateSelf(); 132 } 133 getRegionBounds()134 public Rect getRegionBounds() { 135 return mBounds; 136 } 137 startBoundsAnimation(Rect toBounds, Interpolator interpolator)138 ObjectAnimator startBoundsAnimation(Rect toBounds, Interpolator interpolator) { 139 ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP, "Animate bounds: from=%s to=%s", 140 mBounds, toBounds); 141 if (mBoundsAnimator != null) { 142 mBoundsAnimator.cancel(); 143 } 144 mBoundsAnimator = ObjectAnimator.ofObject(this, BOUNDS, mRectEvaluator, 145 mBounds, toBounds); 146 mBoundsAnimator.setDuration(BOUNDS_DURATION); 147 mBoundsAnimator.setInterpolator(interpolator); 148 mBoundsAnimator.start(); 149 return mBoundsAnimator; 150 } 151 startVisibilityAnimation(boolean visible, Interpolator interpolator)152 ObjectAnimator startVisibilityAnimation(boolean visible, Interpolator interpolator) { 153 ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP, "Animate alpha: from=%d to=%d", 154 Color.alpha(mColor), visible ? mMaxAlpha : 0); 155 if (mAlphaAnimator != null) { 156 mAlphaAnimator.cancel(); 157 } 158 mAlphaAnimator = ObjectAnimator.ofInt(this, ALPHA, Color.alpha(mColor), 159 visible ? mMaxAlpha : 0); 160 mAlphaAnimator.setDuration(ALPHA_DURATION); 161 mAlphaAnimator.setInterpolator(interpolator); 162 mAlphaAnimator.start(); 163 return mAlphaAnimator; 164 } 165 } 166