From b8aacf2d933cd438bb65acb7fff66b92d74278c7 Mon Sep 17 00:00:00 2001 From: minch Date: Tue, 25 Feb 2025 06:02:19 +0000 Subject: [PATCH] Convert ClearAllButton to Kotlin Bug: 389209338 Flag: EXEMPT refactor Test: m Change-Id: Iaafe69ae14744e418d3c30d4d9fc6f417f36f230 --- .../quickstep/views/ClearAllButton.java | 333 ------------------ .../android/quickstep/views/ClearAllButton.kt | 269 ++++++++++++++ 2 files changed, 269 insertions(+), 333 deletions(-) delete mode 100644 quickstep/src/com/android/quickstep/views/ClearAllButton.java create mode 100644 quickstep/src/com/android/quickstep/views/ClearAllButton.kt diff --git a/quickstep/src/com/android/quickstep/views/ClearAllButton.java b/quickstep/src/com/android/quickstep/views/ClearAllButton.java deleted file mode 100644 index 24266974ce..0000000000 --- a/quickstep/src/com/android/quickstep/views/ClearAllButton.java +++ /dev/null @@ -1,333 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.quickstep.views; - -import static com.android.quickstep.util.BorderAnimator.DEFAULT_BORDER_COLOR; - -import android.content.Context; -import android.content.res.Resources; -import android.content.res.TypedArray; -import android.graphics.Canvas; -import android.graphics.Rect; -import android.util.AttributeSet; -import android.util.FloatProperty; -import android.widget.Button; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import com.android.launcher3.Flags; -import com.android.launcher3.R; -import com.android.quickstep.orientation.RecentsPagedOrientationHandler; -import com.android.quickstep.util.BorderAnimator; - -import kotlin.Unit; - -public class ClearAllButton extends Button { - - public static final FloatProperty VISIBILITY_ALPHA = - new FloatProperty("visibilityAlpha") { - @Override - public Float get(ClearAllButton view) { - return view.mVisibilityAlpha; - } - - @Override - public void setValue(ClearAllButton view, float v) { - view.setVisibilityAlpha(v); - } - }; - - public static final FloatProperty DISMISS_ALPHA = - new FloatProperty("dismissAlpha") { - @Override - public Float get(ClearAllButton view) { - return view.mDismissAlpha; - } - - @Override - public void setValue(ClearAllButton view, float v) { - view.setDismissAlpha(v); - } - }; - - private float mScrollAlpha = 1; - private float mContentAlpha = 1; - private float mVisibilityAlpha = 1; - private float mDismissAlpha = 1; - private float mFullscreenProgress = 1; - private float mGridProgress = 1; - - private boolean mIsRtl; - private float mNormalTranslationPrimary; - private float mFullscreenTranslationPrimary; - private float mGridTranslationPrimary; - private float mTaskAlignmentTranslationY; - private float mGridScrollOffset; - private float mScrollOffsetPrimary; - - private int mSidePadding; - private int mOutlinePadding; - private boolean mBorderEnabled; - @Nullable - private final BorderAnimator mFocusBorderAnimator; - - public ClearAllButton(Context context, AttributeSet attrs) { - super(context, attrs); - mIsRtl = getLayoutDirection() == LAYOUT_DIRECTION_RTL; - - if (Flags.enableFocusOutline()) { - TypedArray styledAttrs = context.obtainStyledAttributes(attrs, - R.styleable.ClearAllButton); - Resources resources = getResources(); - mOutlinePadding = resources.getDimensionPixelSize( - R.dimen.recents_clear_all_outline_padding); - mFocusBorderAnimator = - BorderAnimator.createSimpleBorderAnimator( - /* borderRadiusPx= */ resources.getDimensionPixelSize( - R.dimen.recents_clear_all_outline_radius), - /* borderWidthPx= */ context.getResources().getDimensionPixelSize( - R.dimen.keyboard_quick_switch_border_width), - /* boundsBuilder= */ this::updateBorderBounds, - /* targetView= */ this, - /* borderColor= */ styledAttrs.getColor( - R.styleable.ClearAllButton_focusBorderColor, - DEFAULT_BORDER_COLOR)); - styledAttrs.recycle(); - } else { - mFocusBorderAnimator = null; - } - } - - private Unit updateBorderBounds(@NonNull Rect bounds) { - bounds.set(0, 0, getWidth(), getHeight()); - // Make the value negative to form a padding between button and outline - bounds.inset(-mOutlinePadding, -mOutlinePadding); - return Unit.INSTANCE; - } - - @Override - public void onFocusChanged(boolean gainFocus, int direction, Rect previouslyFocusedRect) { - super.onFocusChanged(gainFocus, direction, previouslyFocusedRect); - if (mFocusBorderAnimator != null && mBorderEnabled) { - mFocusBorderAnimator.setBorderVisibility(gainFocus, /* animated= */ true); - } - } - - /** - * Enable or disable showing border on focus change - */ - public void setBorderEnabled(boolean enabled) { - if (mBorderEnabled == enabled) { - return; - } - - mBorderEnabled = enabled; - if (mFocusBorderAnimator != null) { - mFocusBorderAnimator.setBorderVisibility(/* visible= */ - enabled && isFocused(), /* animated= */true); - } - } - - @Override - public void draw(Canvas canvas) { - if (mFocusBorderAnimator != null) { - mFocusBorderAnimator.drawBorder(canvas); - } - super.draw(canvas); - } - - @Override - protected void onLayout(boolean changed, int left, int top, int right, int bottom) { - super.onLayout(changed, left, top, right, bottom); - RecentsPagedOrientationHandler orientationHandler = - getRecentsView().getPagedOrientationHandler(); - mSidePadding = orientationHandler.getClearAllSidePadding(getRecentsView(), mIsRtl); - } - - private RecentsView getRecentsView() { - return (RecentsView) getParent(); - } - - @Override - public void onRtlPropertiesChanged(int layoutDirection) { - super.onRtlPropertiesChanged(layoutDirection); - mIsRtl = getLayoutDirection() == LAYOUT_DIRECTION_RTL; - } - - @Override - public boolean hasOverlappingRendering() { - return false; - } - - public float getScrollAlpha() { - return mScrollAlpha; - } - - public void setContentAlpha(float alpha) { - if (mContentAlpha != alpha) { - mContentAlpha = alpha; - updateAlpha(); - } - } - - public void setVisibilityAlpha(float alpha) { - if (mVisibilityAlpha != alpha) { - mVisibilityAlpha = alpha; - updateAlpha(); - } - } - - public void setDismissAlpha(float alpha) { - if (mDismissAlpha != alpha) { - mDismissAlpha = alpha; - updateAlpha(); - } - } - - public void onRecentsViewScroll(int scroll, boolean gridEnabled) { - RecentsView recentsView = getRecentsView(); - if (recentsView == null) { - return; - } - - RecentsPagedOrientationHandler orientationHandler = - recentsView.getPagedOrientationHandler(); - float orientationSize = orientationHandler.getPrimaryValue(getWidth(), getHeight()); - if (orientationSize == 0) { - return; - } - - int clearAllScroll = recentsView.getClearAllScroll(); - int adjustedScrollFromEdge = Math.abs(scroll - clearAllScroll); - float shift = Math.min(adjustedScrollFromEdge, orientationSize); - mNormalTranslationPrimary = mIsRtl ? -shift : shift; - if (!gridEnabled) { - mNormalTranslationPrimary += mSidePadding; - } - applyPrimaryTranslation(); - applySecondaryTranslation(); - float clearAllSpacing = - recentsView.getPageSpacing() + recentsView.getClearAllExtraPageSpacing(); - clearAllSpacing = mIsRtl ? -clearAllSpacing : clearAllSpacing; - mScrollAlpha = Math.max((clearAllScroll + clearAllSpacing - scroll) / clearAllSpacing, 0); - updateAlpha(); - } - - private void updateAlpha() { - final float alpha = mScrollAlpha * mContentAlpha * mVisibilityAlpha * mDismissAlpha; - setAlpha(alpha); - setClickable(Math.min(alpha, 1) == 1); - } - - public void setFullscreenTranslationPrimary(float fullscreenTranslationPrimary) { - mFullscreenTranslationPrimary = fullscreenTranslationPrimary; - applyPrimaryTranslation(); - } - - /** - * Sets `mTaskAlignmentTranslationY` to the given `value`. In order to put the button at the - * middle in the secondary coordinate. - */ - public void setTaskAlignmentTranslationY(float value) { - mTaskAlignmentTranslationY = value; - applySecondaryTranslation(); - } - - public void setGridTranslationPrimary(float gridTranslationPrimary) { - mGridTranslationPrimary = gridTranslationPrimary; - applyPrimaryTranslation(); - } - - public void setGridScrollOffset(float gridScrollOffset) { - mGridScrollOffset = gridScrollOffset; - } - - public void setScrollOffsetPrimary(float scrollOffsetPrimary) { - mScrollOffsetPrimary = scrollOffsetPrimary; - } - - public float getScrollAdjustment(boolean fullscreenEnabled, boolean gridEnabled) { - float scrollAdjustment = 0; - if (fullscreenEnabled) { - scrollAdjustment += mFullscreenTranslationPrimary; - } - if (gridEnabled) { - scrollAdjustment += mGridTranslationPrimary + mGridScrollOffset; - } - scrollAdjustment += mScrollOffsetPrimary; - return scrollAdjustment; - } - - public float getOffsetAdjustment(boolean fullscreenEnabled, boolean gridEnabled) { - return getScrollAdjustment(fullscreenEnabled, gridEnabled); - } - - /** - * Adjust translation when this TaskView is about to be shown fullscreen. - * - * @param progress: 0 = no translation; 1 = translate according to TaskVIew translations. - */ - public void setFullscreenProgress(float progress) { - mFullscreenProgress = progress; - applyPrimaryTranslation(); - } - - /** - * Moves ClearAllButton between carousel and 2 row grid. - * - * @param gridProgress 0 = carousel; 1 = 2 row grid. - */ - public void setGridProgress(float gridProgress) { - mGridProgress = gridProgress; - applyPrimaryTranslation(); - } - - private void applyPrimaryTranslation() { - RecentsView recentsView = getRecentsView(); - if (recentsView == null) { - return; - } - - RecentsPagedOrientationHandler orientationHandler = - recentsView.getPagedOrientationHandler(); - orientationHandler.getPrimaryViewTranslate().set(this, - orientationHandler.getPrimaryValue(0f, mTaskAlignmentTranslationY) - + mNormalTranslationPrimary + getFullscreenTrans( - mFullscreenTranslationPrimary) + getGridTrans(mGridTranslationPrimary)); - } - - private void applySecondaryTranslation() { - RecentsView recentsView = getRecentsView(); - if (recentsView == null) { - return; - } - - RecentsPagedOrientationHandler orientationHandler = - recentsView.getPagedOrientationHandler(); - orientationHandler.getSecondaryViewTranslate().set(this, - orientationHandler.getSecondaryValue(0f, mTaskAlignmentTranslationY)); - } - - private float getFullscreenTrans(float endTranslation) { - return mFullscreenProgress > 0 ? endTranslation : 0; - } - - private float getGridTrans(float endTranslation) { - return mGridProgress > 0 ? endTranslation : 0; - } -} diff --git a/quickstep/src/com/android/quickstep/views/ClearAllButton.kt b/quickstep/src/com/android/quickstep/views/ClearAllButton.kt new file mode 100644 index 0000000000..ef62597738 --- /dev/null +++ b/quickstep/src/com/android/quickstep/views/ClearAllButton.kt @@ -0,0 +1,269 @@ +/* + * Copyright (C) 2025 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.quickstep.views + +import android.content.Context +import android.graphics.Canvas +import android.graphics.Rect +import android.util.AttributeSet +import android.util.FloatProperty +import android.widget.Button +import com.android.launcher3.Flags.enableFocusOutline +import com.android.launcher3.R +import com.android.quickstep.util.BorderAnimator +import com.android.quickstep.util.BorderAnimator.Companion.createSimpleBorderAnimator +import kotlin.math.abs +import kotlin.math.min + +class ClearAllButton @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) : + Button(context, attrs) { + + var scrollAlpha = 1f + set(value) { + field = value + updateAlpha() + } + + var contentAlpha = 1f + set(value) { + field = value + updateAlpha() + } + + var visibilityAlpha = 1f + set(value) { + field = value + updateAlpha() + } + + var dismissAlpha = 1f + set(value) { + field = value + updateAlpha() + } + + var fullscreenProgress = 1f + set(value) { + if (field == value) { + return + } + field = value + applyPrimaryTranslation() + } + + /** + * Moves ClearAllButton between carousel and 2 row grid. + * + * 0 = carousel; 1 = 2 row grid. + */ + var gridProgress = 1f + set(value) { + if (field == value) { + return + } + field = value + applyPrimaryTranslation() + } + + private var normalTranslationPrimary = 0f + var fullscreenTranslationPrimary = 0f + set(value) { + if (field == value) { + return + } + field = value + applyPrimaryTranslation() + } + + var gridTranslationPrimary = 0f + set(value) { + if (field == value) { + return + } + field = value + applyPrimaryTranslation() + } + + /** Used to put the button at the middle in the secondary coordinate. */ + var taskAlignmentTranslationY = 0f + set(value) { + if (field == value) { + return + } + field = value + applySecondaryTranslation() + } + + var gridScrollOffset = 0f + var scrollOffsetPrimary = 0f + + private var sidePadding = 0 + var borderEnabled = false + set(value) { + if (field == value) { + return + } + field = value + focusBorderAnimator?.setBorderVisibility(visible = field && isFocused, animated = true) + } + + private val focusBorderAnimator: BorderAnimator? = + if (enableFocusOutline()) + createSimpleBorderAnimator( + context.resources.getDimensionPixelSize(R.dimen.recents_clear_all_outline_radius), + context.resources.getDimensionPixelSize(R.dimen.keyboard_quick_switch_border_width), + this::getBorderBounds, + this, + context + .obtainStyledAttributes(attrs, R.styleable.ClearAllButton) + .getColor( + R.styleable.ClearAllButton_focusBorderColor, + BorderAnimator.DEFAULT_BORDER_COLOR, + ), + ) + else null + + private fun getBorderBounds(bounds: Rect) { + bounds.set(0, 0, width, height) + val outlinePadding = + context.resources.getDimensionPixelSize(R.dimen.recents_clear_all_outline_padding) + // Make the value negative to form a padding between button and outline + bounds.inset(-outlinePadding, -outlinePadding) + } + + public override fun onFocusChanged( + gainFocus: Boolean, + direction: Int, + previouslyFocusedRect: Rect?, + ) { + super.onFocusChanged(gainFocus, direction, previouslyFocusedRect) + if (borderEnabled) { + focusBorderAnimator?.setBorderVisibility(gainFocus, /* animated= */ true) + } + } + + override fun draw(canvas: Canvas) { + focusBorderAnimator?.drawBorder(canvas) + super.draw(canvas) + } + + override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) { + super.onLayout(changed, left, top, right, bottom) + sidePadding = + recentsView?.let { it.pagedOrientationHandler?.getClearAllSidePadding(it, isLayoutRtl) } + ?: 0 + } + + private val recentsView: RecentsView<*, *>? + get() = parent as? RecentsView<*, *>? + + override fun hasOverlappingRendering() = false + + fun onRecentsViewScroll(scroll: Int, gridEnabled: Boolean) { + val recentsView = recentsView ?: return + + val orientationSize = + recentsView.pagedOrientationHandler.getPrimaryValue(width, height).toFloat() + if (orientationSize == 0f) { + return + } + + val clearAllScroll = recentsView.clearAllScroll + val adjustedScrollFromEdge = abs((scroll - clearAllScroll)).toFloat() + val shift = min(adjustedScrollFromEdge, orientationSize) + normalTranslationPrimary = if (isLayoutRtl) -shift else shift + if (!gridEnabled) { + normalTranslationPrimary += sidePadding.toFloat() + } + applyPrimaryTranslation() + applySecondaryTranslation() + var clearAllSpacing = recentsView.pageSpacing + recentsView.clearAllExtraPageSpacing + clearAllSpacing = if (isLayoutRtl) -clearAllSpacing else clearAllSpacing + scrollAlpha = + ((clearAllScroll + clearAllSpacing - scroll) / clearAllSpacing.toFloat()).coerceAtLeast( + 0f + ) + } + + private fun updateAlpha() { + val alpha = scrollAlpha * contentAlpha * visibilityAlpha * dismissAlpha + this.alpha = alpha + isClickable = alpha >= 1f + } + + fun getScrollAdjustment(fullscreenEnabled: Boolean, gridEnabled: Boolean): Float { + var scrollAdjustment = 0f + if (fullscreenEnabled) { + scrollAdjustment += fullscreenTranslationPrimary + } + if (gridEnabled) { + scrollAdjustment += gridTranslationPrimary + gridScrollOffset + } + scrollAdjustment += scrollOffsetPrimary + return scrollAdjustment + } + + fun getOffsetAdjustment(fullscreenEnabled: Boolean, gridEnabled: Boolean) = + getScrollAdjustment(fullscreenEnabled, gridEnabled) + + private fun applyPrimaryTranslation() { + val recentsView = recentsView ?: return + val orientationHandler = recentsView.pagedOrientationHandler + orientationHandler.primaryViewTranslate.set( + this, + (orientationHandler.getPrimaryValue(0f, taskAlignmentTranslationY) + + normalTranslationPrimary + + getFullscreenTrans(fullscreenTranslationPrimary) + + getGridTrans(gridTranslationPrimary)), + ) + } + + private fun applySecondaryTranslation() { + val recentsView = recentsView ?: return + val orientationHandler = recentsView.pagedOrientationHandler + orientationHandler.secondaryViewTranslate.set( + this, + orientationHandler.getSecondaryValue(0f, taskAlignmentTranslationY), + ) + } + + private fun getFullscreenTrans(endTranslation: Float) = + if (fullscreenProgress > 0) endTranslation else 0f + + private fun getGridTrans(endTranslation: Float) = if (gridProgress > 0) endTranslation else 0f + + companion object { + @JvmField + val VISIBILITY_ALPHA: FloatProperty = + object : FloatProperty("visibilityAlpha") { + override fun setValue(clearAllButton: ClearAllButton, value: Float) { + clearAllButton.visibilityAlpha = value + } + + override fun get(clearAllButton: ClearAllButton) = clearAllButton.visibilityAlpha + } + + @JvmField + val DISMISS_ALPHA: FloatProperty = + object : FloatProperty("dismissAlpha") { + override fun setValue(clearAllButton: ClearAllButton, value: Float) { + clearAllButton.dismissAlpha = value + } + + override fun get(clearAllButton: ClearAllButton) = clearAllButton.dismissAlpha + } + } +}