1 /* 2 * Copyright (C) 2024 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.launcher3.allapps; 18 19 import static androidx.constraintlayout.widget.ConstraintSet.MATCH_CONSTRAINT; 20 import static androidx.constraintlayout.widget.ConstraintSet.WRAP_CONTENT; 21 22 import android.content.Context; 23 import android.graphics.PorterDuff; 24 import android.graphics.PorterDuffColorFilter; 25 import android.graphics.drawable.Drawable; 26 import android.util.AttributeSet; 27 import android.widget.TextView; 28 29 import androidx.constraintlayout.widget.ConstraintLayout; 30 import androidx.core.graphics.ColorUtils; 31 32 import com.android.launcher3.R; 33 import com.android.launcher3.Utilities; 34 35 /** 36 * A TextView that is used to display the letter list in the fast scroller. 37 */ 38 public class LetterListTextView extends TextView { 39 private static final float ABSOLUTE_TRANSLATION_X = 30f; 40 private static final float ABSOLUTE_SCALE = 1.4f; 41 private final Drawable mLetterBackground; 42 private final int mLetterListTextWidthAndHeight; 43 private final int mTextColor; 44 LetterListTextView(Context context)45 public LetterListTextView(Context context) { 46 this(context, null, 0); 47 } 48 LetterListTextView(Context context, AttributeSet attrs)49 public LetterListTextView(Context context, AttributeSet attrs) { 50 this(context, attrs, 0); 51 } 52 LetterListTextView(Context context, AttributeSet attrs, int defStyle)53 public LetterListTextView(Context context, AttributeSet attrs, int defStyle) { 54 super(context, attrs, defStyle); 55 mLetterBackground = context.getDrawable(R.drawable.bg_letter_list_text); 56 mLetterListTextWidthAndHeight = context.getResources().getDimensionPixelSize( 57 R.dimen.fastscroll_list_letter_size); 58 mTextColor = context.getColor(R.color.materialColorOnSurface); 59 } 60 61 @Override onFinishInflate()62 public void onFinishInflate() { 63 super.onFinishInflate(); 64 setBackground(mLetterBackground); 65 setTextColor(mTextColor); 66 setClickable(false); 67 setWidth(mLetterListTextWidthAndHeight); 68 setTextSize(mLetterListTextWidthAndHeight); 69 setVisibility(VISIBLE); 70 } 71 72 /** 73 * Applies a viewId to the letter list text view and sets the background and text based on the 74 * sectionInfo. 75 */ apply(AlphabeticalAppsList.FastScrollSectionInfo fastScrollSectionInfo, int viewId)76 public void apply(AlphabeticalAppsList.FastScrollSectionInfo fastScrollSectionInfo, 77 int viewId) { 78 setId(viewId); 79 setText(fastScrollSectionInfo.sectionName); 80 ConstraintLayout.LayoutParams lp = new ConstraintLayout.LayoutParams( 81 MATCH_CONSTRAINT, WRAP_CONTENT); 82 lp.dimensionRatio = "v,1:1"; 83 setLayoutParams(lp); 84 } 85 86 /** 87 * Animates the letter list text view based on the current finger position. 88 * 89 * @param currentFingerY The Y position of where the finger is placed on the fastScroller in 90 * pixels. 91 */ animateBasedOnYPosition(int currentFingerY)92 public void animateBasedOnYPosition(int currentFingerY) { 93 if (getBackground() == null) { 94 return; 95 } 96 float cutOffMin = currentFingerY - (getHeight() * 2); 97 float cutOffMax = currentFingerY + (getHeight() * 2); 98 float cutOffDistance = cutOffMax - cutOffMin; 99 boolean isWithinAnimationBounds = getY() < cutOffMax && getY() > cutOffMin; 100 translateBasedOnYPosition(currentFingerY, cutOffDistance, isWithinAnimationBounds); 101 scaleBasedOnYPosition(currentFingerY, cutOffDistance, isWithinAnimationBounds); 102 } 103 scaleBasedOnYPosition(int y, float cutOffDistance, boolean isWithinAnimationBounds)104 private void scaleBasedOnYPosition(int y, float cutOffDistance, 105 boolean isWithinAnimationBounds) { 106 float raisedCosineScale = (float) Math.cos(((y - getY()) / (cutOffDistance)) * Math.PI) 107 * ABSOLUTE_SCALE; 108 if (isWithinAnimationBounds) { 109 raisedCosineScale = Utilities.boundToRange(raisedCosineScale, 1f, ABSOLUTE_SCALE); 110 setScaleX(raisedCosineScale); 111 setScaleY(raisedCosineScale); 112 } else { 113 setScaleX(1); 114 setScaleY(1); 115 } 116 } 117 translateBasedOnYPosition(int y, float cutOffDistance, boolean isWithinAnimationBounds)118 private void translateBasedOnYPosition(int y, float cutOffDistance, 119 boolean isWithinAnimationBounds) { 120 float raisedCosineTranslation = 121 (float) Math.cos(((y - getY()) / (cutOffDistance)) * Math.PI) 122 * ABSOLUTE_TRANSLATION_X; 123 if (isWithinAnimationBounds) { 124 raisedCosineTranslation = -1 * Utilities.boundToRange(raisedCosineTranslation, 125 0, ABSOLUTE_TRANSLATION_X); 126 setTranslationX(raisedCosineTranslation); 127 } else { 128 setTranslationX(0); 129 } 130 } 131 } 132