Arrows to scroll recent tasks in KQS

Bug: 397878339
Bug: 397975686
Flag: com.android.launcher3.taskbar_overflow
Test: Enter taskbar overflow, and open the overflow view.
    Verify that arrow buttons are present; tapping them scrolls in the
    correct direction; when fully scrolled in a direction, the
    associated arrow button is disabled; verify arrow buttons have hover
    effects.
    Open Alt+Tab view, verify no buttons are present if the view cannot
    be scrolled (and present if the view can be scrolled).

Change-Id: Id1c37a2967a629aeb4b3cc2dc5ff7efb21c90cbe
This commit is contained in:
Toni Barzic
2025-02-22 01:09:26 +00:00
parent c383866630
commit 8ca3eaa345
11 changed files with 395 additions and 39 deletions
@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ 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.
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_enabled="false"
android:color="@color/materialColorPrimary"
android:alpha="0.38"/>
<item android:color="@color/materialColorPrimary"/>
</selector>
@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ 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.
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_enabled="true"
android:state_pressed="true"
android:color="@color/materialColorOnPrimary"
android:alpha="0.15"/>
<item android:state_enabled="true"
android:state_hovered="true"
android:color="@color/materialColorOnPrimary"
android:alpha="0.11" />
<item android:color="@android:color/transparent" />
</selector>
@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ 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.
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_enabled="false"
android:color="@color/materialColorOnPrimary"
android:alpha="0.38"/>
<item android:color="@color/materialColorOnPrimary"/>
</selector>
@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ 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.
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
<solid android:color="@color/keyboard_quick_switch_scroll_button_bg" />
<corners android:radius="@dimen/keyboard_quick_switch_scroll_button_corner_radius" />
</shape>
@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ 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.
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
<solid android:color="@color/keyboard_quick_switch_scroll_button_fg" />
<corners android:radius="@dimen/keyboard_quick_switch_scroll_button_corner_radius" />
</shape>
+27
View File
@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<vector
xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960"
android:autoMirrored="true"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M504,480L320,296L376,240L616,480L376,720L320,664L504,480Z" />
</vector>
@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<vector
xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960"
android:autoMirrored="true"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M560,720L320,480L560,240L616,296L432,480L616,664L560,720Z" />
</vector>
@@ -67,6 +67,24 @@
</androidx.constraintlayout.widget.ConstraintLayout>
<ImageButton
android:id="@+id/scroll_button_start"
android:src="@drawable/ic_chevron_start"
android:contentDescription="@string/quick_switch_scroll_arrow_left"
android:background="@drawable/bg_keyboard_quick_switch_scroll_button"
android:foreground="@drawable/fg_keyboard_quick_switch_scroll_button"
android:tint="@color/keyboard_quick_switch_scroll_button_icon"
android:layout_width="@dimen/keyboard_quick_switch_scroll_button_width"
android:layout_height="@dimen/keyboard_quick_switch_scroll_button_height"
android:paddingHorizontal="@dimen/keyboard_quick_switch_scroll_button_horizontal_padding"
android:paddingVertical="@dimen/keyboard_quick_switch_scroll_button_vertical_padding"
android:layout_marginStart="@dimen/keyboard_quick_switch_view_spacing"
android:visibility="gone"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"/>
<HorizontalScrollView
android:id="@+id/scroll_view"
android:layout_width="wrap_content"
@@ -76,9 +94,12 @@
android:alpha="0"
android:visibility="gone"
app:layout_constraintHorizontal_bias="0"
app:layout_constrainedWidth="true"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent">
app:layout_constraintStart_toEndOf="@id/scroll_button_start"
app:layout_constraintEnd_toStartOf="@id/scroll_button_end">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/content"
@@ -89,4 +110,22 @@
</HorizontalScrollView>
<ImageButton
android:id="@+id/scroll_button_end"
android:src="@drawable/ic_chevron_end"
android:contentDescription="@string/quick_switch_scroll_arrow_right"
android:background="@drawable/bg_keyboard_quick_switch_scroll_button"
android:foreground="@drawable/fg_keyboard_quick_switch_scroll_button"
android:tint="@color/keyboard_quick_switch_scroll_button_icon"
android:layout_width="@dimen/keyboard_quick_switch_scroll_button_width"
android:layout_height="@dimen/keyboard_quick_switch_scroll_button_height"
android:paddingHorizontal="@dimen/keyboard_quick_switch_scroll_button_horizontal_padding"
android:paddingVertical="@dimen/keyboard_quick_switch_scroll_button_vertical_padding"
android:layout_marginEnd="@dimen/keyboard_quick_switch_view_spacing"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
</com.android.launcher3.taskbar.KeyboardQuickSwitchView>
+5
View File
@@ -530,6 +530,11 @@
<dimen name="keyboard_quick_switch_text_button_radius">360dp</dimen>
<dimen name="keyboard_quick_switch_text_button_horizontal_padding">16dp</dimen>
<dimen name="keyboard_quick_switch_text_button_fade_edge_length">20dp</dimen>
<dimen name="keyboard_quick_switch_scroll_button_width">36dp</dimen>
<dimen name="keyboard_quick_switch_scroll_button_height">56dp</dimen>
<dimen name="keyboard_quick_switch_scroll_button_horizontal_padding">12dp</dimen>
<dimen name="keyboard_quick_switch_scroll_button_vertical_padding">32dp</dimen>
<dimen name="keyboard_quick_switch_scroll_button_corner_radius">18dp</dimen>
<!-- Digital Wellbeing -->
<dimen name="digital_wellbeing_toast_height">48dp</dimen>
+7
View File
@@ -335,6 +335,13 @@
<!-- Accessibility label for quick switch tiles showing split tasks [CHAR LIMIT=NONE] -->
<string name="quick_switch_split_task"><xliff:g id="app_name_1" example="Chrome">%1$s</xliff:g> and <xliff:g id="app_name_2" example="Gmail">%2$s</xliff:g></string>
<!-- Accessibility label for an arrow button within quick switch UI that scrolls the quick switch content left
TODO(b/397975686): Make these translatable when verified by UX. -->
<string name="quick_switch_scroll_arrow_left" translatable="false">Scroll left</string>
<!-- Accessibility label for an arrow button within quick switch UI that scrolls the quick switch content right
TODO(b/397975686): Make these translatable when verified by UX. -->
<string name="quick_switch_scroll_arrow_right" translatable="false">Scroll right</string>
<!-- Strings for bubble bar -->
<!-- Fallback name for a bubble if it does have a title [CHAR_LIMIT=none] -->
<string name="bubble_bar_bubble_fallback_description">Bubble</string>
@@ -30,10 +30,12 @@ import android.util.AttributeSet;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewOutlineProvider;
import android.view.ViewTreeObserver;
import android.view.animation.Interpolator;
import android.widget.HorizontalScrollView;
import android.widget.ImageButton;
import android.widget.TextView;
import android.window.OnBackInvokedDispatcher;
import android.window.WindowOnBackInvokedDispatcher;
@@ -45,6 +47,7 @@ import androidx.constraintlayout.widget.ConstraintLayout;
import com.android.app.animation.Interpolators;
import com.android.internal.jank.Cuj;
import com.android.launcher3.Flags;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AnimatedFloat;
@@ -102,8 +105,12 @@ public class KeyboardQuickSwitchView extends ConstraintLayout {
private HorizontalScrollView mScrollView;
private ConstraintLayout mContent;
private int mTaskViewWidth;
private int mTaskViewHeight;
private boolean mSupportsScrollArrows = false;
private ImageButton mStartScrollArrow;
private ImageButton mEndScrollArrow;
private int mTaskViewBorderWidth;
private int mTaskViewRadius;
private int mSpacing;
private int mSmallSpacing;
private int mOutlineRadius;
@@ -112,11 +119,13 @@ public class KeyboardQuickSwitchView extends ConstraintLayout {
private int mOverviewTaskIndex = -1;
private int mDesktopTaskIndex = -1;
@Nullable private AnimatorSet mOpenAnimation;
@Nullable
private AnimatorSet mOpenAnimation;
private boolean mIsBackCallbackRegistered = false;
@Nullable private KeyboardQuickSwitchViewController.ViewCallbacks mViewCallbacks;
@Nullable
private KeyboardQuickSwitchViewController.ViewCallbacks mViewCallbacks;
public KeyboardQuickSwitchView(@NonNull Context context) {
this(context, null);
@@ -152,18 +161,35 @@ public class KeyboardQuickSwitchView extends ConstraintLayout {
mNoRecentItemsPane = findViewById(R.id.no_recent_items_pane);
mScrollView = findViewById(R.id.scroll_view);
mContent = findViewById(R.id.content);
mStartScrollArrow = findViewById(R.id.scroll_button_start);
mEndScrollArrow = findViewById(R.id.scroll_button_end);
setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
Resources resources = getResources();
mTaskViewWidth = resources.getDimensionPixelSize(
R.dimen.keyboard_quick_switch_taskview_width);
mTaskViewHeight = resources.getDimensionPixelSize(
R.dimen.keyboard_quick_switch_taskview_height);
mSpacing = resources.getDimensionPixelSize(R.dimen.keyboard_quick_switch_view_spacing);
mSmallSpacing = resources.getDimensionPixelSize(
R.dimen.keyboard_quick_switch_view_small_spacing);
mOutlineRadius = resources.getDimensionPixelSize(R.dimen.keyboard_quick_switch_view_radius);
mTaskViewBorderWidth = resources.getDimensionPixelSize(
R.dimen.keyboard_quick_switch_border_width);
mTaskViewRadius = resources.getDimensionPixelSize(
R.dimen.keyboard_quick_switch_task_view_radius);
mIsRtl = Utilities.isRtl(resources);
if (Flags.taskbarOverflow()) {
initializeScrollArrows();
if (mIsRtl) {
mStartScrollArrow.setContentDescription(
resources.getString(R.string.quick_switch_scroll_arrow_right));
mEndScrollArrow.setContentDescription(
resources.getString(R.string.quick_switch_scroll_arrow_left));
}
}
TypefaceUtils.setTypeface(
mNoRecentItemsPane.findViewById(R.id.no_recent_items_text),
TypefaceUtils.FONT_FAMILY_LABEL_LARGE_BASELINE);
@@ -331,6 +357,78 @@ public class KeyboardQuickSwitchView extends ConstraintLayout {
});
}
private void initializeScrollArrows() {
mSupportsScrollArrows = true;
mStartScrollArrow.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (mIsRtl) {
runScrollCommand(false, () -> {
mScrollView.smoothScrollBy(mScrollView.getWidth(), 0);
});
} else {
runScrollCommand(false, () -> {
mScrollView.smoothScrollBy(-mScrollView.getWidth(), 0);
});
}
}
});
mEndScrollArrow.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (mIsRtl) {
runScrollCommand(false, () -> {
mScrollView.smoothScrollBy(-mScrollView.getWidth(), 0);
});
} else {
runScrollCommand(false, () -> {
mScrollView.smoothScrollBy(mScrollView.getWidth(), 0);
});
}
}
});
// Add listeners to disable arrow buttons when the scroll view cannot be further scrolled in
// the associated direction.
mScrollView.setOnScrollChangeListener(new OnScrollChangeListener() {
@Override
public void onScrollChange(View v, int scrollX, int scrollY, int oldScrollX,
int oldScrollY) {
updateArrowButtonsEnabledState();
}
});
// Update scroll view outline to clip its contents with rounded corners.
mScrollView.setClipToOutline(true);
mScrollView.setOutlineProvider(new ViewOutlineProvider() {
@Override
public void getOutline(View view, Outline outline) {
int spacingWithoutBorder = mSpacing - mTaskViewBorderWidth;
outline.setRoundRect(spacingWithoutBorder,
spacingWithoutBorder, view.getWidth() - spacingWithoutBorder,
view.getHeight() - spacingWithoutBorder,
mTaskViewRadius);
}
});
}
private void updateArrowButtonsEnabledState() {
if (!mDisplayingRecentTasks) {
return;
}
int scrollX = mScrollView.getScrollX();
if (mIsRtl) {
mEndScrollArrow.setEnabled(scrollX > 0);
mStartScrollArrow.setEnabled(scrollX < mContent.getWidth() - mScrollView.getWidth());
} else {
mStartScrollArrow.setEnabled(scrollX > 0);
mEndScrollArrow.setEnabled(scrollX < mContent.getWidth() - mScrollView.getWidth());
}
}
int getOverviewTaskIndex() {
return mOverviewTaskIndex;
}
@@ -346,6 +444,21 @@ public class KeyboardQuickSwitchView extends ConstraintLayout {
mViewCallbacks = null;
}
private void animateDisplayedContentForClose(View view, AnimatorSet animator) {
Animator translationYAnimation = ObjectAnimator.ofFloat(
view,
TRANSLATION_Y,
0, -Utilities.dpToPx(CONTENT_START_TRANSLATION_Y_DP));
translationYAnimation.setDuration(CONTENT_TRANSLATION_Y_ANIMATION_DURATION_MS);
translationYAnimation.setInterpolator(CLOSE_TRANSLATION_Y_INTERPOLATOR);
animator.play(translationYAnimation);
Animator contentAlphaAnimation = ObjectAnimator.ofFloat(view, ALPHA, 1f, 0f);
contentAlphaAnimation.setDuration(CONTENT_ALPHA_ANIMATION_DURATION_MS);
animator.play(contentAlphaAnimation);
}
protected Animator getCloseAnimation() {
AnimatorSet closeAnimation = new AnimatorSet();
@@ -360,17 +473,11 @@ public class KeyboardQuickSwitchView extends ConstraintLayout {
closeAnimation.play(alphaAnimation);
View displayedContent = mDisplayingRecentTasks ? mScrollView : mNoRecentItemsPane;
Animator translationYAnimation = ObjectAnimator.ofFloat(
displayedContent,
TRANSLATION_Y,
0, -Utilities.dpToPx(CONTENT_START_TRANSLATION_Y_DP));
translationYAnimation.setDuration(CONTENT_TRANSLATION_Y_ANIMATION_DURATION_MS);
translationYAnimation.setInterpolator(CLOSE_TRANSLATION_Y_INTERPOLATOR);
closeAnimation.play(translationYAnimation);
Animator contentAlphaAnimation = ObjectAnimator.ofFloat(displayedContent, ALPHA, 1f, 0f);
contentAlphaAnimation.setDuration(CONTENT_ALPHA_ANIMATION_DURATION_MS);
closeAnimation.play(contentAlphaAnimation);
animateDisplayedContentForClose(displayedContent, closeAnimation);
if (mSupportsScrollArrows) {
animateDisplayedContentForClose(mStartScrollArrow, closeAnimation);
animateDisplayedContentForClose(mEndScrollArrow, closeAnimation);
}
closeAnimation.addListener(new AnimatorListenerAdapter() {
@Override
@@ -385,6 +492,31 @@ public class KeyboardQuickSwitchView extends ConstraintLayout {
return closeAnimation;
}
private void animateDisplayedContentForOpen(View view, AnimatorSet animator) {
Animator translationXAnimation = ObjectAnimator.ofFloat(
view,
TRANSLATION_X,
-Utilities.dpToPx(CONTENT_START_TRANSLATION_X_DP), 0);
translationXAnimation.setDuration(CONTENT_TRANSLATION_X_ANIMATION_DURATION_MS);
translationXAnimation.setInterpolator(OPEN_TRANSLATION_X_INTERPOLATOR);
animator.play(translationXAnimation);
Animator translationYAnimation = ObjectAnimator.ofFloat(
view,
TRANSLATION_Y,
-Utilities.dpToPx(CONTENT_START_TRANSLATION_Y_DP), 0);
translationYAnimation.setDuration(CONTENT_TRANSLATION_Y_ANIMATION_DURATION_MS);
translationYAnimation.setInterpolator(OPEN_TRANSLATION_Y_INTERPOLATOR);
animator.play(translationYAnimation);
view.setAlpha(0.0f);
Animator contentAlphaAnimation = ObjectAnimator.ofFloat(view, ALPHA, 0f,
1f);
contentAlphaAnimation.setStartDelay(CONTENT_ALPHA_ANIMATION_START_DELAY_MS);
contentAlphaAnimation.setDuration(CONTENT_ALPHA_ANIMATION_DURATION_MS);
animator.play(contentAlphaAnimation);
}
protected void animateOpen(int currentFocusIndexOverride) {
if (mOpenAnimation != null) {
// Restart animation since currentFocusIndexOverride can change the initial scroll.
@@ -407,26 +539,12 @@ public class KeyboardQuickSwitchView extends ConstraintLayout {
mOpenAnimation.play(alphaAnimation);
View displayedContent = mDisplayingRecentTasks ? mScrollView : mNoRecentItemsPane;
Animator translationXAnimation = ObjectAnimator.ofFloat(
displayedContent,
TRANSLATION_X,
-Utilities.dpToPx(CONTENT_START_TRANSLATION_X_DP), 0);
translationXAnimation.setDuration(CONTENT_TRANSLATION_X_ANIMATION_DURATION_MS);
translationXAnimation.setInterpolator(OPEN_TRANSLATION_X_INTERPOLATOR);
mOpenAnimation.play(translationXAnimation);
animateDisplayedContentForOpen(displayedContent, mOpenAnimation);
if (mSupportsScrollArrows) {
animateDisplayedContentForOpen(mStartScrollArrow, mOpenAnimation);
animateDisplayedContentForOpen(mEndScrollArrow, mOpenAnimation);
}
Animator translationYAnimation = ObjectAnimator.ofFloat(
displayedContent,
TRANSLATION_Y,
-Utilities.dpToPx(CONTENT_START_TRANSLATION_Y_DP), 0);
translationYAnimation.setDuration(CONTENT_TRANSLATION_Y_ANIMATION_DURATION_MS);
translationYAnimation.setInterpolator(OPEN_TRANSLATION_Y_INTERPOLATOR);
mOpenAnimation.play(translationYAnimation);
Animator contentAlphaAnimation = ObjectAnimator.ofFloat(displayedContent, ALPHA, 0f, 1f);
contentAlphaAnimation.setStartDelay(CONTENT_ALPHA_ANIMATION_START_DELAY_MS);
contentAlphaAnimation.setDuration(CONTENT_ALPHA_ANIMATION_DURATION_MS);
mOpenAnimation.play(contentAlphaAnimation);
ViewOutlineProvider outlineProvider = getOutlineProvider();
mOpenAnimation.addListener(new AnimatorListenerAdapter() {
@@ -461,6 +579,27 @@ public class KeyboardQuickSwitchView extends ConstraintLayout {
OPEN_OUTLINE_INTERPOLATOR));
}
});
if (mSupportsScrollArrows) {
mScrollView.getViewTreeObserver().addOnGlobalLayoutListener(
new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
if (mScrollView.getWidth() == 0) {
return;
}
if (mContent.getWidth() > mScrollView.getWidth()) {
mStartScrollArrow.setVisibility(VISIBLE);
mEndScrollArrow.setVisibility(VISIBLE);
updateArrowButtonsEnabledState();
}
mScrollView.getViewTreeObserver().removeOnGlobalLayoutListener(
this);
}
});
}
animateFocusMove(-1, Math.min(
getTaskCount() - 1,
currentFocusIndexOverride == -1 ? 1 : currentFocusIndexOverride));