Convert ClearAllButton to Kotlin
Bug: 389209338
Flag: EXEMPT refactor
Test: m
Change-Id: Iaafe69ae14744e418d3c30d4d9fc6f417f36f230
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 2426697..0000000
--- 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<ClearAllButton> VISIBILITY_ALPHA =
- new FloatProperty<ClearAllButton>("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<ClearAllButton> DISMISS_ALPHA =
- new FloatProperty<ClearAllButton>("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 0000000..ef62597
--- /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<ClearAllButton> =
+ object : FloatProperty<ClearAllButton>("visibilityAlpha") {
+ override fun setValue(clearAllButton: ClearAllButton, value: Float) {
+ clearAllButton.visibilityAlpha = value
+ }
+
+ override fun get(clearAllButton: ClearAllButton) = clearAllButton.visibilityAlpha
+ }
+
+ @JvmField
+ val DISMISS_ALPHA: FloatProperty<ClearAllButton> =
+ object : FloatProperty<ClearAllButton>("dismissAlpha") {
+ override fun setValue(clearAllButton: ClearAllButton, value: Float) {
+ clearAllButton.dismissAlpha = value
+ }
+
+ override fun get(clearAllButton: ClearAllButton) = clearAllButton.dismissAlpha
+ }
+ }
+}