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:
@@ -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>
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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));
|
||||
|
||||
Reference in New Issue
Block a user