From 7e676c6a40fcdad13ba186374554c0019b7bb014 Mon Sep 17 00:00:00 2001 From: Jordan Silva Date: Tue, 25 Mar 2025 12:23:28 +0000 Subject: [PATCH] Add marquee for AppChip and simplify text view handling in IconAppChipView - Consolidates the two TextViews (collapsed and expanded) in `IconAppChipView` into a single `appTitle` TextView. - Updates the app name's width based on the current chip status (expanded/collapsed) - Removes the `icon_text_collapsed` and `icon_text_expanded` from layout and uses only `icon_title`. - Adds marquee support to handle text ellipsis in the collapsed state. - Refactors text width calculation for collapsed state to take into account maximum available width. - Updates text animations to use the new `appTitle`. - Updates the maximum width to not be set to less than the min width. Bug: 403193877 Flag: com.android.launcher3.enable_overview_icon_menu Test: OverviewImageTest Test: OverviewSplitTaskImageTest Change-Id: Ic82790d0a79b65adb7ba14dad7848748cd568f62 --- quickstep/res/layout/icon_app_chip_view.xml | 21 +-- .../quickstep/views/IconAppChipView.kt | 158 +++++++++++------- 2 files changed, 109 insertions(+), 70 deletions(-) diff --git a/quickstep/res/layout/icon_app_chip_view.xml b/quickstep/res/layout/icon_app_chip_view.xml index 09fb5094fe..3173528248 100644 --- a/quickstep/res/layout/icon_app_chip_view.xml +++ b/quickstep/res/layout/icon_app_chip_view.xml @@ -51,25 +51,18 @@ android:importantForAccessibility="no" /> - - = MultiPropertyFactory(this, VIEW_TRANSLATE_Y, INDEX_COUNT_TRANSLATION, SUM_AGGREGATOR) - var maxWidth = Int.MAX_VALUE + // Width showing only the app icon and arrow. Max width should not be set to less than + // this. + private val minWidthAllowed = iconViewMarginStart + appIconSize + arrowSize + arrowMarginEnd + var maxWidth = Integer.MAX_VALUE /** * Sets the maximum width of this Icon Menu. This is usually used when space is limited for * split screen. @@ -118,8 +125,7 @@ constructor( set(value) { // Width showing only the app icon and arrow. Max width should not be set to less than // this. - val minMaxWidth = iconViewMarginStart + appIconSize + arrowSize + arrowMarginEnd - field = max(value, minMaxWidth) + field = max(value, minWidthAllowed) } var status: AppChipStatus = AppChipStatus.Collapsed @@ -128,15 +134,14 @@ constructor( override fun onFinishInflate() { super.onFinishInflate() iconView = findViewById(R.id.icon_view) - iconTextCollapsedView = findViewById(R.id.icon_text_collapsed) - iconTextExpandedView = findViewById(R.id.icon_text_expanded) + appTitle = findViewById(R.id.icon_title) iconArrowView = findViewById(R.id.icon_arrow) menuAnchorView = findViewById(R.id.icon_view_menu_anchor) } override fun setText(text: CharSequence?) { - iconTextCollapsedView?.text = text - iconTextExpandedView?.text = text + if (text == appTitle?.text) return + appTitle?.text = text } override fun getDrawable(): Drawable? = iconView?.drawable @@ -200,30 +205,17 @@ constructor( // Layout Params for the collapsed Icon Text View val textMarginStart = iconMarginStartRelativeToParent + appIconSize + appNameHorizontalMargin - val iconTextCollapsedParams = iconTextCollapsedView!!.layoutParams as LayoutParams + val iconTextCollapsedParams = appTitle!!.layoutParams as LayoutParams orientationHandler.setIconAppChipChildrenParams(iconTextCollapsedParams, textMarginStart) - val collapsedTextWidth = - (collapsedBackgroundBounds.width() - - iconViewMarginStart - - appIconSize - - arrowSize - - appNameHorizontalMargin - - arrowMarginEnd) - iconTextCollapsedParams.width = collapsedTextWidth - iconTextCollapsedView!!.layoutParams = iconTextCollapsedParams - iconTextCollapsedView!!.alpha = 1f - - // Layout Params for the expanded Icon Text View - val iconTextExpandedParams = iconTextExpandedView!!.layoutParams as LayoutParams - orientationHandler.setIconAppChipChildrenParams(iconTextExpandedParams, textMarginStart) - iconTextExpandedView!!.layoutParams = iconTextExpandedParams - iconTextExpandedView!!.alpha = 0f - iconTextExpandedView!!.setRevealClip( - true, - 0f, - appIconSize / 2f, - collapsedTextWidth.toFloat(), - ) + iconTextCollapsedParams.width = + calculateCollapsedTextWidth(collapsedBackgroundBounds.width()) + appTitle!!.layoutParams = iconTextCollapsedParams + // select to enable marquee and disable it when is running test harness. + if (Utilities.isRunningInTestHarness()) { + disableAppTitleMarquee() + } else { + appTitle!!.isSelected = true + } // Layout Params for the Icon Arrow View val iconArrowParams = iconArrowView!!.layoutParams as LayoutParams @@ -243,6 +235,38 @@ constructor( ) } + private fun disableAppTitleMarquee() { + appTitle?.let { + it.isHorizontalFadingEdgeEnabled = false + it.ellipsize = TextUtils.TruncateAt.END + it.horizontallyScrolling = false + it.isSelected = false + } + } + + /** + * Calculates the width available for the collapsed text (app name) within the view. + * + * This function determines the maximum width that the app name can occupy when the view is in + * its collapsed state. It considers various factors such as the maximum allowed width, the + * bounds of the collapsed background, the size of the app icon, the arrow, and the margins + * around these elements. + * + * @return The calculated width available for the collapsed text (app name). + */ + private fun calculateCollapsedTextWidth(width: Int): Int { + val collapsedTextWidth = + (width - + iconViewMarginStart - + appIconSize - + arrowSize - + appNameHorizontalMargin - + arrowMarginEnd) + + val spaceLeftForText = maxWidth - minWidthAllowed + return minOf(collapsedTextWidth, spaceLeftForText) + } + override fun setIconColorTint(color: Int, amount: Float) { // RecentsView's COLOR_TINT animates between 0 and 0.5f, we want to hide the app chip menu. val colorTintAlpha = Utilities.mapToRange(amount, 0f, 0.5f, 1f, 0f, Interpolators.LINEAR) @@ -312,11 +336,11 @@ constructor( // Clip expanded text with reveal animation so it doesn't go beyond the edge of the menu val expandedTextRevealAnim = ViewAnimationUtils.createCircularReveal( - iconTextExpandedView, + appTitle, 0, - iconTextExpandedView!!.height / 2, - iconTextCollapsedView!!.width.toFloat(), - iconTextExpandedView!!.width.toFloat(), + appTitle!!.height / 2, + appTitle!!.width.toFloat(), + expandedMaxTextWidth.toFloat(), ) // Animate background clipping val backgroundAnimator = @@ -343,18 +367,7 @@ constructor( backgroundAnimator, ObjectAnimator.ofFloat(iconView, SCALE_X, iconViewScaling), ObjectAnimator.ofFloat(iconView, SCALE_Y, iconViewScaling), - ObjectAnimator.ofFloat( - iconTextCollapsedView, - TRANSLATION_X, - textTranslationXWithRtl, - ), - ObjectAnimator.ofFloat( - iconTextExpandedView, - TRANSLATION_X, - textTranslationXWithRtl, - ), - ObjectAnimator.ofFloat(iconTextCollapsedView, ALPHA, 0f), - ObjectAnimator.ofFloat(iconTextExpandedView, ALPHA, 1f), + ObjectAnimator.ofFloat(appTitle, TRANSLATION_X, textTranslationXWithRtl), ObjectAnimator.ofFloat(iconArrowView, TRANSLATION_X, arrowTranslationWithRtl), ObjectAnimator.ofFloat(iconArrowView, SCALE_Y, -1f), ) @@ -364,11 +377,11 @@ constructor( // Clip expanded text with reveal animation so it doesn't go beyond the edge of the menu val expandedTextClipAnim = ViewAnimationUtils.createCircularReveal( - iconTextExpandedView, + appTitle, 0, - iconTextExpandedView!!.height / 2, - iconTextExpandedView!!.width.toFloat(), - iconTextCollapsedView!!.width.toFloat(), + appTitle!!.height / 2, + appTitle!!.width.toFloat(), + calculateCollapsedTextWidth(collapsedBackgroundBounds.width()).toFloat(), ) // Animate background clipping @@ -386,10 +399,7 @@ constructor( expandedTextClipAnim, backgroundAnimator, ObjectAnimator.ofFloat(iconView, SCALE_PROPERTY, 1f), - ObjectAnimator.ofFloat(iconTextCollapsedView, TRANSLATION_X, 0f), - ObjectAnimator.ofFloat(iconTextExpandedView, TRANSLATION_X, 0f), - ObjectAnimator.ofFloat(iconTextCollapsedView, ALPHA, 1f), - ObjectAnimator.ofFloat(iconTextExpandedView, ALPHA, 0f), + ObjectAnimator.ofFloat(appTitle, TRANSLATION_X, 0f), ObjectAnimator.ofFloat(iconArrowView, TRANSLATION_X, 0f), ObjectAnimator.ofFloat(iconArrowView, SCALE_Y, 1f), ) @@ -399,9 +409,45 @@ constructor( if (!animated) animator!!.duration = 0 animator!!.interpolator = Interpolators.EMPHASIZED + animator!!.addListener( + onStart = { + appTitle!!.isSelected = false + if (status == AppChipStatus.Expanded) updateTitleSize() + }, + onEnd = { + if (status == AppChipStatus.Collapsed) updateTitleSize() + appTitle!!.isSelected = true + }, + ) + animator!!.start() } + /** + * Updates the width of the app title based on the current [AppChipStatus]. + * + * This function dynamically adjusts the width of the `appTitle` TextView depending on whether + * the app chip is in an expanded or collapsed state. + * - When the chip is [AppChipStatus.Expanded], the title width is set to + * [expandedMaxTextWidth], allowing the title to potentially take up more space. + * - When the chip is [AppChipStatus.Collapsed], the title width is calculated based on the + * width of the collapsed background. This ensures the title fits within the smaller, + * collapsed chip boundaries. The width is then determined by calling + * [calculateCollapsedTextWidth]. + */ + private fun updateTitleSize() { + val appTitleWidth = + when (status) { + AppChipStatus.Expanded -> expandedMaxTextWidth + AppChipStatus.Collapsed -> { + val collapsedBackgroundWidth = getCollapsedBackgroundLtrBounds().width() + calculateCollapsedTextWidth(collapsedBackgroundWidth) + } + } + + appTitle!!.updateLayoutParams { width = appTitleWidth } + } + private fun getCollapsedBackgroundLtrBounds(): Rect { val bounds = Rect(0, 0, min(maxWidth, collapsedMenuDefaultWidth), collapsedMenuDefaultHeight)