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.systemui.navigationbar.buttons; 18 19 import static com.android.systemui.animation.Interpolators.LINEAR; 20 21 import android.animation.Animator; 22 import android.animation.AnimatorListenerAdapter; 23 import android.animation.ValueAnimator; 24 import android.view.View; 25 import android.view.View.AccessibilityDelegate; 26 27 import com.android.systemui.Dependency; 28 import com.android.systemui.assist.AssistManager; 29 30 import java.util.ArrayList; 31 32 /** 33 * Dispatches common view calls to multiple views. This is used to handle 34 * multiples of the same nav bar icon appearing. 35 */ 36 public class ButtonDispatcher { 37 private static final int FADE_DURATION_IN = 150; 38 private static final int FADE_DURATION_OUT = 250; 39 40 private final ArrayList<View> mViews = new ArrayList<>(); 41 42 private final int mId; 43 44 private View.OnClickListener mClickListener; 45 private View.OnTouchListener mTouchListener; 46 private View.OnLongClickListener mLongClickListener; 47 private View.OnHoverListener mOnHoverListener; 48 private Boolean mLongClickable; 49 private Float mAlpha; 50 private Float mDarkIntensity; 51 private Integer mVisibility = View.VISIBLE; 52 private Boolean mDelayTouchFeedback; 53 private KeyButtonDrawable mImageDrawable; 54 private View mCurrentView; 55 private boolean mVertical; 56 private ValueAnimator mFadeAnimator; 57 private AccessibilityDelegate mAccessibilityDelegate; 58 59 private final ValueAnimator.AnimatorUpdateListener mAlphaListener = animation -> 60 setAlpha( 61 (float) animation.getAnimatedValue(), 62 false /* animate */, 63 false /* cancelAnimator */); 64 65 private final AnimatorListenerAdapter mFadeListener = new AnimatorListenerAdapter() { 66 @Override 67 public void onAnimationEnd(Animator animation) { 68 mFadeAnimator = null; 69 setVisibility(getAlpha() == 1 ? View.VISIBLE : View.INVISIBLE); 70 } 71 }; 72 ButtonDispatcher(int id)73 public ButtonDispatcher(int id) { 74 mId = id; 75 } 76 clear()77 public void clear() { 78 mViews.clear(); 79 } 80 addView(View view)81 public void addView(View view) { 82 mViews.add(view); 83 view.setOnClickListener(mClickListener); 84 view.setOnTouchListener(mTouchListener); 85 view.setOnLongClickListener(mLongClickListener); 86 view.setOnHoverListener(mOnHoverListener); 87 if (mLongClickable != null) { 88 view.setLongClickable(mLongClickable); 89 } 90 if (mAlpha != null) { 91 view.setAlpha(mAlpha); 92 } 93 if (mVisibility != null) { 94 view.setVisibility(mVisibility); 95 } 96 if (mAccessibilityDelegate != null) { 97 view.setAccessibilityDelegate(mAccessibilityDelegate); 98 } 99 if (view instanceof ButtonInterface) { 100 final ButtonInterface button = (ButtonInterface) view; 101 if (mDarkIntensity != null) { 102 button.setDarkIntensity(mDarkIntensity); 103 } 104 if (mImageDrawable != null) { 105 button.setImageDrawable(mImageDrawable); 106 } 107 if (mDelayTouchFeedback != null) { 108 button.setDelayTouchFeedback(mDelayTouchFeedback); 109 } 110 button.setVertical(mVertical); 111 } 112 } 113 getId()114 public int getId() { 115 return mId; 116 } 117 getVisibility()118 public int getVisibility() { 119 return mVisibility != null ? mVisibility : View.VISIBLE; 120 } 121 isVisible()122 public boolean isVisible() { 123 return getVisibility() == View.VISIBLE; 124 } 125 getAlpha()126 public float getAlpha() { 127 return mAlpha != null ? mAlpha : 1; 128 } 129 getImageDrawable()130 public KeyButtonDrawable getImageDrawable() { 131 return mImageDrawable; 132 } 133 setImageDrawable(KeyButtonDrawable drawable)134 public void setImageDrawable(KeyButtonDrawable drawable) { 135 mImageDrawable = drawable; 136 final int N = mViews.size(); 137 for (int i = 0; i < N; i++) { 138 if (mViews.get(i) instanceof ButtonInterface) { 139 ((ButtonInterface) mViews.get(i)).setImageDrawable(mImageDrawable); 140 } 141 } 142 if (mImageDrawable != null) { 143 mImageDrawable.setCallback(mCurrentView); 144 } 145 } 146 setVisibility(int visibility)147 public void setVisibility(int visibility) { 148 if (mVisibility == visibility) return; 149 if (mFadeAnimator != null) { 150 mFadeAnimator.cancel(); 151 } 152 153 mVisibility = visibility; 154 final int N = mViews.size(); 155 for (int i = 0; i < N; i++) { 156 mViews.get(i).setVisibility(mVisibility); 157 } 158 } 159 abortCurrentGesture()160 public void abortCurrentGesture() { 161 // This seems to be an instantaneous thing, so not going to persist it. 162 final int N = mViews.size(); 163 for (int i = 0; i < N; i++) { 164 if (mViews.get(i) instanceof ButtonInterface) { 165 ((ButtonInterface) mViews.get(i)).abortCurrentGesture(); 166 } 167 } 168 } 169 setAlpha(float alpha)170 public void setAlpha(float alpha) { 171 setAlpha(alpha, false /* animate */); 172 } 173 setAlpha(float alpha, boolean animate)174 public void setAlpha(float alpha, boolean animate) { 175 setAlpha(alpha, animate, true /* cancelAnimator */); 176 } 177 setAlpha(float alpha, boolean animate, long duration)178 public void setAlpha(float alpha, boolean animate, long duration) { 179 setAlpha(alpha, animate, duration, true /* cancelAnimator */); 180 } 181 setAlpha(float alpha, boolean animate, boolean cancelAnimator)182 public void setAlpha(float alpha, boolean animate, boolean cancelAnimator) { 183 setAlpha( 184 alpha, 185 animate, 186 (getAlpha() < alpha) ? FADE_DURATION_IN : FADE_DURATION_OUT, 187 cancelAnimator); 188 } 189 setAlpha(float alpha, boolean animate, long duration, boolean cancelAnimator)190 public void setAlpha(float alpha, boolean animate, long duration, boolean cancelAnimator) { 191 if (mFadeAnimator != null && (cancelAnimator || animate)) { 192 mFadeAnimator.cancel(); 193 } 194 if (animate) { 195 setVisibility(View.VISIBLE); 196 mFadeAnimator = ValueAnimator.ofFloat(getAlpha(), alpha); 197 mFadeAnimator.setDuration(duration); 198 mFadeAnimator.setInterpolator(LINEAR); 199 mFadeAnimator.addListener(mFadeListener); 200 mFadeAnimator.addUpdateListener(mAlphaListener); 201 mFadeAnimator.start(); 202 } else { 203 // Discretize the alpha updates to prevent too frequent updates when there is a long 204 // alpha animation 205 int prevAlpha = (int) (getAlpha() * 255); 206 int nextAlpha = (int) (alpha * 255); 207 if (prevAlpha != nextAlpha) { 208 mAlpha = nextAlpha / 255f; 209 final int N = mViews.size(); 210 for (int i = 0; i < N; i++) { 211 mViews.get(i).setAlpha(mAlpha); 212 } 213 } 214 } 215 } 216 setDarkIntensity(float darkIntensity)217 public void setDarkIntensity(float darkIntensity) { 218 mDarkIntensity = darkIntensity; 219 final int N = mViews.size(); 220 for (int i = 0; i < N; i++) { 221 if (mViews.get(i) instanceof ButtonInterface) { 222 ((ButtonInterface) mViews.get(i)).setDarkIntensity(darkIntensity); 223 } 224 } 225 } 226 setDelayTouchFeedback(boolean delay)227 public void setDelayTouchFeedback(boolean delay) { 228 mDelayTouchFeedback = delay; 229 final int N = mViews.size(); 230 for (int i = 0; i < N; i++) { 231 if (mViews.get(i) instanceof ButtonInterface) { 232 ((ButtonInterface) mViews.get(i)).setDelayTouchFeedback(delay); 233 } 234 } 235 } 236 setOnClickListener(View.OnClickListener clickListener)237 public void setOnClickListener(View.OnClickListener clickListener) { 238 mClickListener = clickListener; 239 final int N = mViews.size(); 240 for (int i = 0; i < N; i++) { 241 mViews.get(i).setOnClickListener(mClickListener); 242 } 243 } 244 setOnTouchListener(View.OnTouchListener touchListener)245 public void setOnTouchListener(View.OnTouchListener touchListener) { 246 mTouchListener = touchListener; 247 final int N = mViews.size(); 248 for (int i = 0; i < N; i++) { 249 mViews.get(i).setOnTouchListener(mTouchListener); 250 } 251 } 252 setLongClickable(boolean isLongClickable)253 public void setLongClickable(boolean isLongClickable) { 254 mLongClickable = isLongClickable; 255 final int N = mViews.size(); 256 for (int i = 0; i < N; i++) { 257 mViews.get(i).setLongClickable(mLongClickable); 258 } 259 } 260 setOnLongClickListener(View.OnLongClickListener longClickListener)261 public void setOnLongClickListener(View.OnLongClickListener longClickListener) { 262 mLongClickListener = longClickListener; 263 final int N = mViews.size(); 264 for (int i = 0; i < N; i++) { 265 mViews.get(i).setOnLongClickListener(mLongClickListener); 266 } 267 } 268 setOnHoverListener(View.OnHoverListener hoverListener)269 public void setOnHoverListener(View.OnHoverListener hoverListener) { 270 mOnHoverListener = hoverListener; 271 final int N = mViews.size(); 272 for (int i = 0; i < N; i++) { 273 mViews.get(i).setOnHoverListener(mOnHoverListener); 274 } 275 } 276 setAccessibilityDelegate(AccessibilityDelegate delegate)277 public void setAccessibilityDelegate(AccessibilityDelegate delegate) { 278 mAccessibilityDelegate = delegate; 279 final int N = mViews.size(); 280 for (int i = 0; i < N; i++) { 281 mViews.get(i).setAccessibilityDelegate(delegate); 282 } 283 } 284 setClickable(boolean clickable)285 public void setClickable(boolean clickable) { 286 abortCurrentGesture(); 287 final int N = mViews.size(); 288 for (int i = 0; i < N; i++) { 289 mViews.get(i).setClickable(clickable); 290 } 291 } 292 setTranslation(int x, int y, int z)293 public void setTranslation(int x, int y, int z) { 294 final int N = mViews.size(); 295 for (int i = 0; i < N; i++) { 296 final View view = mViews.get(i); 297 view.setTranslationX(x); 298 view.setTranslationY(y); 299 view.setTranslationZ(z); 300 } 301 } 302 getViews()303 public ArrayList<View> getViews() { 304 return mViews; 305 } 306 getCurrentView()307 public View getCurrentView() { 308 return mCurrentView; 309 } 310 setCurrentView(View currentView)311 public void setCurrentView(View currentView) { 312 mCurrentView = currentView.findViewById(mId); 313 if (mImageDrawable != null) { 314 mImageDrawable.setCallback(mCurrentView); 315 } 316 if (mCurrentView != null) { 317 mCurrentView.setTranslationX(0); 318 mCurrentView.setTranslationY(0); 319 mCurrentView.setTranslationZ(0); 320 } 321 } 322 setVertical(boolean vertical)323 public void setVertical(boolean vertical) { 324 mVertical = vertical; 325 final int N = mViews.size(); 326 for (int i = 0; i < N; i++) { 327 final View view = mViews.get(i); 328 if (view instanceof ButtonInterface) { 329 ((ButtonInterface) view).setVertical(vertical); 330 } 331 } 332 } 333 334 /** 335 * Executes when button is detached from window. 336 */ onDestroy()337 public void onDestroy() { 338 } 339 } 340