1 /* 2 * Copyright (C) 2018 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.quickstep.views; 18 19 import static com.android.launcher3.Flags.enableGridOnlyOverview; 20 import static com.android.quickstep.util.BorderAnimator.DEFAULT_BORDER_COLOR; 21 22 import android.content.Context; 23 import android.content.res.Resources; 24 import android.content.res.TypedArray; 25 import android.graphics.Canvas; 26 import android.graphics.Rect; 27 import android.util.AttributeSet; 28 import android.util.FloatProperty; 29 import android.widget.Button; 30 31 import androidx.annotation.NonNull; 32 import androidx.annotation.Nullable; 33 34 import com.android.launcher3.DeviceProfile; 35 import com.android.launcher3.Flags; 36 import com.android.launcher3.R; 37 import com.android.quickstep.orientation.RecentsPagedOrientationHandler; 38 import com.android.quickstep.util.BorderAnimator; 39 40 import kotlin.Unit; 41 42 public class ClearAllButton extends Button { 43 44 public static final FloatProperty<ClearAllButton> VISIBILITY_ALPHA = 45 new FloatProperty<ClearAllButton>("visibilityAlpha") { 46 @Override 47 public Float get(ClearAllButton view) { 48 return view.mVisibilityAlpha; 49 } 50 51 @Override 52 public void setValue(ClearAllButton view, float v) { 53 view.setVisibilityAlpha(v); 54 } 55 }; 56 57 public static final FloatProperty<ClearAllButton> DISMISS_ALPHA = 58 new FloatProperty<ClearAllButton>("dismissAlpha") { 59 @Override 60 public Float get(ClearAllButton view) { 61 return view.mDismissAlpha; 62 } 63 64 @Override 65 public void setValue(ClearAllButton view, float v) { 66 view.setDismissAlpha(v); 67 } 68 }; 69 70 private final RecentsViewContainer mContainer; 71 private float mScrollAlpha = 1; 72 private float mContentAlpha = 1; 73 private float mVisibilityAlpha = 1; 74 private float mDismissAlpha = 1; 75 private float mFullscreenProgress = 1; 76 private float mGridProgress = 1; 77 78 private boolean mIsRtl; 79 private float mNormalTranslationPrimary; 80 private float mFullscreenTranslationPrimary; 81 private float mGridTranslationPrimary; 82 private float mGridScrollOffset; 83 private float mScrollOffsetPrimary; 84 85 private int mSidePadding; 86 private int mOutlinePadding; 87 private boolean mBorderEnabled; 88 @Nullable 89 private final BorderAnimator mFocusBorderAnimator; 90 ClearAllButton(Context context, AttributeSet attrs)91 public ClearAllButton(Context context, AttributeSet attrs) { 92 super(context, attrs); 93 mIsRtl = getLayoutDirection() == LAYOUT_DIRECTION_RTL; 94 mContainer = RecentsViewContainer.containerFromContext(context); 95 96 if (Flags.enableFocusOutline()) { 97 TypedArray styledAttrs = context.obtainStyledAttributes(attrs, 98 R.styleable.ClearAllButton); 99 Resources resources = getResources(); 100 mOutlinePadding = resources.getDimensionPixelSize( 101 R.dimen.recents_clear_all_outline_padding); 102 mFocusBorderAnimator = 103 BorderAnimator.createSimpleBorderAnimator( 104 /* borderRadiusPx= */ resources.getDimensionPixelSize( 105 R.dimen.recents_clear_all_outline_radius), 106 /* borderWidthPx= */ context.getResources().getDimensionPixelSize( 107 R.dimen.keyboard_quick_switch_border_width), 108 /* boundsBuilder= */ this::updateBorderBounds, 109 /* targetView= */ this, 110 /* borderColor= */ styledAttrs.getColor( 111 R.styleable.ClearAllButton_focusBorderColor, 112 DEFAULT_BORDER_COLOR)); 113 styledAttrs.recycle(); 114 } else { 115 mFocusBorderAnimator = null; 116 } 117 } 118 updateBorderBounds(@onNull Rect bounds)119 private Unit updateBorderBounds(@NonNull Rect bounds) { 120 bounds.set(0, 0, getWidth(), getHeight()); 121 // Make the value negative to form a padding between button and outline 122 bounds.inset(-mOutlinePadding, -mOutlinePadding); 123 return Unit.INSTANCE; 124 } 125 126 @Override onFocusChanged(boolean gainFocus, int direction, Rect previouslyFocusedRect)127 public void onFocusChanged(boolean gainFocus, int direction, Rect previouslyFocusedRect) { 128 super.onFocusChanged(gainFocus, direction, previouslyFocusedRect); 129 if (mFocusBorderAnimator != null && mBorderEnabled) { 130 mFocusBorderAnimator.setBorderVisibility(gainFocus, /* animated= */ true); 131 } 132 } 133 134 /** 135 * Enable or disable showing border on focus change 136 */ setBorderEnabled(boolean enabled)137 public void setBorderEnabled(boolean enabled) { 138 if (mBorderEnabled == enabled) { 139 return; 140 } 141 142 mBorderEnabled = enabled; 143 if (mFocusBorderAnimator != null) { 144 mFocusBorderAnimator.setBorderVisibility(/* visible= */ 145 enabled && isFocused(), /* animated= */true); 146 } 147 } 148 149 @Override draw(Canvas canvas)150 public void draw(Canvas canvas) { 151 if (mFocusBorderAnimator != null) { 152 mFocusBorderAnimator.drawBorder(canvas); 153 } 154 super.draw(canvas); 155 } 156 157 @Override onLayout(boolean changed, int left, int top, int right, int bottom)158 protected void onLayout(boolean changed, int left, int top, int right, int bottom) { 159 super.onLayout(changed, left, top, right, bottom); 160 RecentsPagedOrientationHandler orientationHandler = 161 getRecentsView().getPagedOrientationHandler(); 162 mSidePadding = orientationHandler.getClearAllSidePadding(getRecentsView(), mIsRtl); 163 } 164 getRecentsView()165 private RecentsView getRecentsView() { 166 return (RecentsView) getParent(); 167 } 168 169 @Override onRtlPropertiesChanged(int layoutDirection)170 public void onRtlPropertiesChanged(int layoutDirection) { 171 super.onRtlPropertiesChanged(layoutDirection); 172 mIsRtl = getLayoutDirection() == LAYOUT_DIRECTION_RTL; 173 } 174 175 @Override hasOverlappingRendering()176 public boolean hasOverlappingRendering() { 177 return false; 178 } 179 getScrollAlpha()180 public float getScrollAlpha() { 181 return mScrollAlpha; 182 } 183 setContentAlpha(float alpha)184 public void setContentAlpha(float alpha) { 185 if (mContentAlpha != alpha) { 186 mContentAlpha = alpha; 187 updateAlpha(); 188 } 189 } 190 setVisibilityAlpha(float alpha)191 public void setVisibilityAlpha(float alpha) { 192 if (mVisibilityAlpha != alpha) { 193 mVisibilityAlpha = alpha; 194 updateAlpha(); 195 } 196 } 197 setDismissAlpha(float alpha)198 public void setDismissAlpha(float alpha) { 199 if (mDismissAlpha != alpha) { 200 mDismissAlpha = alpha; 201 updateAlpha(); 202 } 203 } 204 onRecentsViewScroll(int scroll, boolean gridEnabled)205 public void onRecentsViewScroll(int scroll, boolean gridEnabled) { 206 RecentsView recentsView = getRecentsView(); 207 if (recentsView == null) { 208 return; 209 } 210 211 RecentsPagedOrientationHandler orientationHandler = 212 recentsView.getPagedOrientationHandler(); 213 float orientationSize = orientationHandler.getPrimaryValue(getWidth(), getHeight()); 214 if (orientationSize == 0) { 215 return; 216 } 217 218 int clearAllScroll = recentsView.getClearAllScroll(); 219 int adjustedScrollFromEdge = Math.abs(scroll - clearAllScroll); 220 float shift = Math.min(adjustedScrollFromEdge, orientationSize); 221 mNormalTranslationPrimary = mIsRtl ? -shift : shift; 222 if (!gridEnabled) { 223 mNormalTranslationPrimary += mSidePadding; 224 } 225 applyPrimaryTranslation(); 226 applySecondaryTranslation(); 227 float clearAllSpacing = 228 recentsView.getPageSpacing() + recentsView.getClearAllExtraPageSpacing(); 229 clearAllSpacing = mIsRtl ? -clearAllSpacing : clearAllSpacing; 230 mScrollAlpha = Math.max((clearAllScroll + clearAllSpacing - scroll) / clearAllSpacing, 0); 231 updateAlpha(); 232 } 233 updateAlpha()234 private void updateAlpha() { 235 final float alpha = mScrollAlpha * mContentAlpha * mVisibilityAlpha * mDismissAlpha; 236 setAlpha(alpha); 237 setClickable(Math.min(alpha, 1) == 1); 238 } 239 setFullscreenTranslationPrimary(float fullscreenTranslationPrimary)240 public void setFullscreenTranslationPrimary(float fullscreenTranslationPrimary) { 241 mFullscreenTranslationPrimary = fullscreenTranslationPrimary; 242 applyPrimaryTranslation(); 243 } 244 setGridTranslationPrimary(float gridTranslationPrimary)245 public void setGridTranslationPrimary(float gridTranslationPrimary) { 246 mGridTranslationPrimary = gridTranslationPrimary; 247 applyPrimaryTranslation(); 248 } 249 setGridScrollOffset(float gridScrollOffset)250 public void setGridScrollOffset(float gridScrollOffset) { 251 mGridScrollOffset = gridScrollOffset; 252 } 253 setScrollOffsetPrimary(float scrollOffsetPrimary)254 public void setScrollOffsetPrimary(float scrollOffsetPrimary) { 255 mScrollOffsetPrimary = scrollOffsetPrimary; 256 } 257 getScrollAdjustment(boolean fullscreenEnabled, boolean gridEnabled)258 public float getScrollAdjustment(boolean fullscreenEnabled, boolean gridEnabled) { 259 float scrollAdjustment = 0; 260 if (fullscreenEnabled) { 261 scrollAdjustment += mFullscreenTranslationPrimary; 262 } 263 if (gridEnabled) { 264 scrollAdjustment += mGridTranslationPrimary + mGridScrollOffset; 265 } 266 scrollAdjustment += mScrollOffsetPrimary; 267 return scrollAdjustment; 268 } 269 getOffsetAdjustment(boolean fullscreenEnabled, boolean gridEnabled)270 public float getOffsetAdjustment(boolean fullscreenEnabled, boolean gridEnabled) { 271 return getScrollAdjustment(fullscreenEnabled, gridEnabled); 272 } 273 274 /** 275 * Adjust translation when this TaskView is about to be shown fullscreen. 276 * 277 * @param progress: 0 = no translation; 1 = translate according to TaskVIew translations. 278 */ setFullscreenProgress(float progress)279 public void setFullscreenProgress(float progress) { 280 mFullscreenProgress = progress; 281 applyPrimaryTranslation(); 282 } 283 284 /** 285 * Moves ClearAllButton between carousel and 2 row grid. 286 * 287 * @param gridProgress 0 = carousel; 1 = 2 row grid. 288 */ setGridProgress(float gridProgress)289 public void setGridProgress(float gridProgress) { 290 mGridProgress = gridProgress; 291 applyPrimaryTranslation(); 292 } 293 applyPrimaryTranslation()294 private void applyPrimaryTranslation() { 295 RecentsView recentsView = getRecentsView(); 296 if (recentsView == null) { 297 return; 298 } 299 300 RecentsPagedOrientationHandler orientationHandler = 301 recentsView.getPagedOrientationHandler(); 302 orientationHandler.getPrimaryViewTranslate().set(this, 303 orientationHandler.getPrimaryValue(0f, getOriginalTranslationY()) 304 + mNormalTranslationPrimary + getFullscreenTrans( 305 mFullscreenTranslationPrimary) + getGridTrans(mGridTranslationPrimary)); 306 } 307 applySecondaryTranslation()308 private void applySecondaryTranslation() { 309 RecentsView recentsView = getRecentsView(); 310 if (recentsView == null) { 311 return; 312 } 313 314 RecentsPagedOrientationHandler orientationHandler = 315 recentsView.getPagedOrientationHandler(); 316 orientationHandler.getSecondaryViewTranslate().set(this, 317 orientationHandler.getSecondaryValue(0f, getOriginalTranslationY())); 318 } 319 getFullscreenTrans(float endTranslation)320 private float getFullscreenTrans(float endTranslation) { 321 return mFullscreenProgress > 0 ? endTranslation : 0; 322 } 323 getGridTrans(float endTranslation)324 private float getGridTrans(float endTranslation) { 325 return mGridProgress > 0 ? endTranslation : 0; 326 } 327 328 /** 329 * Get the Y translation that is set in the original layout position, before scrolling. 330 */ getOriginalTranslationY()331 private float getOriginalTranslationY() { 332 DeviceProfile deviceProfile = mContainer.getDeviceProfile(); 333 if (deviceProfile.isTablet) { 334 if (enableGridOnlyOverview()) { 335 return (getRecentsView().getLastComputedTaskSize().height() 336 + deviceProfile.overviewTaskThumbnailTopMarginPx) / 2.0f 337 + deviceProfile.overviewRowSpacing; 338 } else { 339 return deviceProfile.overviewRowSpacing; 340 } 341 } 342 return deviceProfile.overviewTaskThumbnailTopMarginPx / 2.0f; 343 } 344 } 345