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