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.launcher3.taskbar; 17 18 import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY; 19 20 import android.animation.Animator; 21 import android.animation.AnimatorListenerAdapter; 22 import android.animation.ObjectAnimator; 23 import android.animation.PropertyValuesHolder; 24 import android.annotation.Nullable; 25 import android.content.Context; 26 import android.graphics.Rect; 27 import android.util.AttributeSet; 28 import android.view.View; 29 30 import androidx.annotation.ColorInt; 31 import androidx.core.content.ContextCompat; 32 33 import com.android.launcher3.LauncherAnimUtils; 34 import com.android.launcher3.R; 35 36 /** 37 * View to render a handle that changes color based on the background to ensure contrast. Used for 38 * the taskbar when stashed as well as the bubble bar when stashed. 39 */ 40 public class StashedHandleView extends View { 41 42 private static final long COLOR_CHANGE_DURATION = 120; 43 44 private final @ColorInt int mStashedHandleLightColor; 45 private final @ColorInt int mStashedHandleDarkColor; 46 private final Rect mSampledRegion = new Rect(); 47 private final int[] mTmpArr = new int[2]; 48 49 private @Nullable ObjectAnimator mColorChangeAnim; 50 StashedHandleView(Context context)51 public StashedHandleView(Context context) { 52 this(context, null); 53 } 54 StashedHandleView(Context context, AttributeSet attrs)55 public StashedHandleView(Context context, AttributeSet attrs) { 56 this(context, attrs, 0); 57 } 58 StashedHandleView(Context context, AttributeSet attrs, int defStyleAttr)59 public StashedHandleView(Context context, AttributeSet attrs, int defStyleAttr) { 60 this(context, attrs, defStyleAttr, 0); 61 } 62 StashedHandleView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)63 public StashedHandleView(Context context, AttributeSet attrs, int defStyleAttr, 64 int defStyleRes) { 65 super(context, attrs, defStyleAttr, defStyleRes); 66 67 mStashedHandleLightColor = ContextCompat.getColor(context, 68 R.color.taskbar_stashed_handle_light_color); 69 mStashedHandleDarkColor = ContextCompat.getColor(context, 70 R.color.taskbar_stashed_handle_dark_color); 71 } 72 73 /** 74 * Updates mSampledRegion to be the location of the stashedHandleBounds relative to the screen. 75 * @see #getSampledRegion() 76 */ updateSampledRegion(Rect stashedHandleBounds)77 public void updateSampledRegion(Rect stashedHandleBounds) { 78 getLocationOnScreen(mTmpArr); 79 // Translations are temporary due to animations, remove them for the purpose of determining 80 // the final region we want sampled. 81 mTmpArr[0] -= Math.round(getTranslationX()); 82 mTmpArr[1] -= Math.round(getTranslationY()); 83 mSampledRegion.set(stashedHandleBounds); 84 mSampledRegion.offset(mTmpArr[0], mTmpArr[1]); 85 } 86 getSampledRegion()87 public Rect getSampledRegion() { 88 return mSampledRegion; 89 } 90 91 /** 92 * Updates the handle color. 93 * @param isRegionDark Whether the background behind the handle is dark, and thus the handle 94 * should be light (and vice versa). 95 * @param animate Whether to animate the change, or apply it immediately. 96 */ updateHandleColor(boolean isRegionDark, boolean animate)97 public void updateHandleColor(boolean isRegionDark, boolean animate) { 98 int newColor = isRegionDark ? mStashedHandleLightColor : mStashedHandleDarkColor; 99 if (mColorChangeAnim != null) { 100 mColorChangeAnim.cancel(); 101 } 102 if (animate) { 103 mColorChangeAnim = ObjectAnimator.ofArgb(this, 104 LauncherAnimUtils.VIEW_BACKGROUND_COLOR, newColor); 105 mColorChangeAnim.addListener(new AnimatorListenerAdapter() { 106 @Override 107 public void onAnimationEnd(Animator animation) { 108 mColorChangeAnim = null; 109 } 110 }); 111 mColorChangeAnim.setDuration(COLOR_CHANGE_DURATION); 112 mColorChangeAnim.start(); 113 } else { 114 setBackgroundColor(newColor); 115 } 116 } 117 118 /** 119 * Updates the handle scale. 120 * 121 * @param scale target scale to animate towards (starting from current scale) 122 * @param durationMs milliseconds for the animation to take 123 */ animateScale(float scale, long durationMs)124 public void animateScale(float scale, long durationMs) { 125 ObjectAnimator scaleAnim = ObjectAnimator.ofPropertyValuesHolder(this, 126 PropertyValuesHolder.ofFloat(SCALE_PROPERTY, scale)); 127 scaleAnim.setDuration(durationMs).setAutoCancel(true); 128 scaleAnim.start(); 129 } 130 } 131