Merge "Update gesture navigation tutorial for foldable devices." into sc-v2-dev

This commit is contained in:
Schneider Victor-tulias
2021-09-29 19:42:43 +00:00
committed by Android (Google) Code Review
10 changed files with 320 additions and 40 deletions
-1
View File
@@ -100,7 +100,6 @@
<activity android:name="com.android.quickstep.interaction.GestureSandboxActivity"
android:autoRemoveFromRecents="true"
android:excludeFromRecents="true"
android:screenOrientation="portrait"
android:exported="true">
<intent-filter>
<action android:name="com.android.quickstep.action.GESTURE_SANDBOX"/>
@@ -1,17 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2020 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/gesture_tutorial_fake_previous_task_view_color" />
</shape>
@@ -41,13 +41,54 @@
android:layout_height="20dp"
android:visibility="invisible" />
<View
<com.android.quickstep.interaction.AnimatedTaskView
android:id="@+id/gesture_tutorial_fake_previous_task_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleX="0.98"
android:scaleY="0.98"
android:visibility="invisible" />
android:visibility="invisible">
<View
android:id="@+id/full_task_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="invisible"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"/>
<androidx.cardview.widget.CardView
android:id="@+id/top_task_view"
android:layout_width="match_parent"
android:layout_height="0dp"
android:visibility="invisible"
android:layout_marginBottom="@dimen/gesture_tutorial_multi_row_task_view_spacing"
app:cardElevation="0dp"
app:cardCornerRadius="@dimen/gesture_tutorial_small_task_view_corner_radius"
app:layout_constraintVertical_chainStyle="spread"
app:layout_constraintTop_toTopOf="@id/full_task_view"
app:layout_constraintBottom_toTopOf="@id/bottom_task_view"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"/>
<androidx.cardview.widget.CardView
android:id="@+id/bottom_task_view"
android:layout_width="match_parent"
android:layout_height="0dp"
android:visibility="invisible"
app:cardElevation="0dp"
app:cardCornerRadius="@dimen/gesture_tutorial_small_task_view_corner_radius"
app:layout_constraintTop_toBottomOf="@id/top_task_view"
app:layout_constraintBottom_toBottomOf="@id/full_task_view"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"/>
</com.android.quickstep.interaction.AnimatedTaskView>
<FrameLayout
android:id="@+id/gesture_tutorial_fake_task_view"
@@ -118,19 +118,17 @@
<androidx.cardview.widget.CardView
android:id="@+id/reply_icon_1"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_width="44dp"
android:layout_height="44dp"
android:layout_marginBottom="32dp"
android:layout_marginStart="26dp"
android:layout_marginEnd="342dp"
app:cardElevation="0dp"
app:cardCornerRadius="100dp"
app:cardBackgroundColor="@color/mock_conversation_profile_icon"
app:layout_constraintDimensionRatio="1:1"
app:layout_constraintBottom_toTopOf="@id/message_2"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"/>
app:layout_constraintStart_toStartOf="parent"/>
<androidx.cardview.widget.CardView
android:layout_width="0dp"
@@ -178,19 +176,17 @@
<androidx.cardview.widget.CardView
android:id="@+id/reply_icon_2"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_width="44dp"
android:layout_height="44dp"
android:layout_marginBottom="32dp"
android:layout_marginStart="26dp"
android:layout_marginEnd="342dp"
app:cardElevation="0dp"
app:cardCornerRadius="100dp"
app:cardBackgroundColor="@color/mock_conversation_profile_icon"
app:layout_constraintDimensionRatio="1:1"
app:layout_constraintBottom_toTopOf="@id/message_4"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"/>
app:layout_constraintStart_toStartOf="parent"/>
<androidx.cardview.widget.CardView
android:layout_width="0dp"
+2
View File
@@ -121,6 +121,8 @@
<dimen name="gesture_tutorial_subtitle_margin_start_end">16dp</dimen>
<dimen name="gesture_tutorial_feedback_margin_start_end">24dp</dimen>
<dimen name="gesture_tutorial_button_margin_start_end">18dp</dimen>
<dimen name="gesture_tutorial_multi_row_task_view_spacing">72dp</dimen>
<dimen name="gesture_tutorial_small_task_view_corner_radius">18dp</dimen>
<!-- All Set page -->
<dimen name="allset_page_margin_horizontal">40dp</dimen>
@@ -0,0 +1,202 @@
/*
* Copyright (C) 2021 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.interaction;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.annotation.ColorInt;
import android.content.Context;
import android.graphics.Outline;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewOutlineProvider;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.cardview.widget.CardView;
import androidx.constraintlayout.widget.ConstraintLayout;
import com.android.launcher3.R;
import java.util.ArrayList;
/**
* Helper View for the gesture tutorial mock previous app task view.
*
* This helper class allows animating from a single-row layout to a two-row layout as seen in
* large screen devices.
*/
public class AnimatedTaskView extends ConstraintLayout {
private View mFullTaskView;
private CardView mTopTaskView;
private CardView mBottomTaskView;
private ViewOutlineProvider mTaskViewOutlineProvider = null;
private final Rect mTaskViewAnimatedRect = new Rect();
private float mTaskViewAnimatedRadius;
public AnimatedTaskView(@NonNull Context context) {
super(context);
}
public AnimatedTaskView(@NonNull Context context,
@Nullable AttributeSet attrs) {
super(context, attrs);
}
public AnimatedTaskView(
@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public AnimatedTaskView(
@NonNull Context context,
@Nullable AttributeSet attrs,
int defStyleAttr,
int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
mFullTaskView = findViewById(R.id.full_task_view);
mTopTaskView = findViewById(R.id.top_task_view);
mBottomTaskView = findViewById(R.id.bottom_task_view);
setToSingleRowLayout(false);
}
AnimatorSet createAnimationToMultiRowLayout() {
if (mTaskViewOutlineProvider == null) {
// This is an illegal state.
return null;
}
Outline startOutline = new Outline();
mTaskViewOutlineProvider.getOutline(this, startOutline);
Rect outlineStartRect = new Rect();
startOutline.getRect(outlineStartRect);
int endRectBottom = mTopTaskView.getHeight();
float outlineStartRadius = startOutline.getRadius();
float outlineEndRadius = getContext().getResources().getDimensionPixelSize(
R.dimen.gesture_tutorial_small_task_view_corner_radius);
ValueAnimator outlineAnimator = ValueAnimator.ofFloat(0f, 1f);
outlineAnimator.addUpdateListener(valueAnimator -> {
float progress = (float) valueAnimator.getAnimatedValue();
mTaskViewAnimatedRect.bottom = (int) (outlineStartRect.bottom
+ progress * (endRectBottom - outlineStartRect.bottom));
mTaskViewAnimatedRadius = outlineStartRadius
+ progress * (outlineEndRadius - outlineStartRadius);
mFullTaskView.invalidateOutline();
});
outlineAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
super.onAnimationStart(animation);
mTaskViewAnimatedRect.set(outlineStartRect);
mTaskViewAnimatedRadius = outlineStartRadius;
mFullTaskView.setClipToOutline(true);
mFullTaskView.setOutlineProvider(new ViewOutlineProvider() {
@Override
public void getOutline(View view, Outline outline) {
outline.setRoundRect(mTaskViewAnimatedRect, mTaskViewAnimatedRadius);
}
});
}
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
mFullTaskView.setOutlineProvider(mTaskViewOutlineProvider);
}
@Override
public void onAnimationCancel(Animator animation) {
super.onAnimationCancel(animation);
mFullTaskView.setOutlineProvider(mTaskViewOutlineProvider);
}
});
ArrayList<Animator> animations = new ArrayList<>();
animations.add(ObjectAnimator.ofFloat(
mBottomTaskView, View.TRANSLATION_X, -mBottomTaskView.getWidth(), 0));
animations.add(outlineAnimator);
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.playTogether(animations);
animatorSet.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
super.onAnimationStart(animation);
setToSingleRowLayout(true);
setPadding(0, outlineStartRect.top, 0, getHeight() - outlineStartRect.bottom);
}
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
setToMultiRowLayout();
}
@Override
public void onAnimationCancel(Animator animation) {
super.onAnimationCancel(animation);
setToMultiRowLayout();
}
});
return animatorSet;
}
void setToSingleRowLayout(boolean forAnimation) {
mFullTaskView.setVisibility(VISIBLE);
mTopTaskView.setVisibility(INVISIBLE);
mBottomTaskView.setVisibility(forAnimation ? VISIBLE : INVISIBLE);
}
void setToMultiRowLayout() {
mFullTaskView.setVisibility(INVISIBLE);
mTopTaskView.setVisibility(VISIBLE);
mBottomTaskView.setVisibility(VISIBLE);
}
void setFakeTaskViewFillColor(@ColorInt int colorResId) {
mFullTaskView.setBackgroundColor(colorResId);
mTopTaskView.setCardBackgroundColor(colorResId);
mBottomTaskView.setCardBackgroundColor(colorResId);
}
@Override
public void setClipToOutline(boolean clipToOutline) {
mFullTaskView.setClipToOutline(clipToOutline);
}
@Override
public void setOutlineProvider(ViewOutlineProvider provider) {
mTaskViewOutlineProvider = provider;
mFullTaskView.setOutlineProvider(mTaskViewOutlineProvider);
}
}
@@ -17,6 +17,7 @@ package com.android.quickstep.interaction;
import static com.android.launcher3.anim.Interpolators.ACCEL;
import android.animation.Animator;
import android.animation.AnimatorSet;
import android.annotation.TargetApi;
import android.graphics.PointF;
@@ -29,6 +30,8 @@ import com.android.quickstep.SwipeUpAnimationLogic;
import com.android.quickstep.interaction.EdgeBackGestureHandler.BackGestureResult;
import com.android.quickstep.interaction.NavBarGestureHandler.NavBarGestureResult;
import java.util.ArrayList;
/** A {@link TutorialController} for the Overview tutorial. */
@TargetApi(Build.VERSION_CODES.R)
final class OverviewGestureTutorialController extends SwipeUpGestureTutorialController {
@@ -123,10 +126,24 @@ final class OverviewGestureTutorialController extends SwipeUpGestureTutorialCont
}
public void animateTaskViewToOverview() {
PendingAnimation anim = new PendingAnimation(300);
PendingAnimation anim = new PendingAnimation(TASK_VIEW_END_ANIMATION_DURATION_MILLIS);
anim.setFloat(mTaskViewSwipeUpAnimation
.getCurrentShift(), AnimatedFloat.VALUE, 1, ACCEL);
AnimatorSet animset = anim.buildAnim();
ArrayList<Animator> animators = new ArrayList<>();
if (mTutorialFragment.isLargeScreen()) {
Animator multiRowAnimation = mFakePreviousTaskView.createAnimationToMultiRowLayout();
if (multiRowAnimation != null) {
multiRowAnimation.setDuration(TASK_VIEW_END_ANIMATION_DURATION_MILLIS);
animators.add(multiRowAnimation);
}
}
animators.add(anim.buildAnim());
AnimatorSet animset = new AnimatorSet();
animset.playTogether(animators);
animset.start();
mRunningWindowAnim = SwipeUpAnimationLogic.RunningWindowAnim.wrap(animset);
}
@@ -63,6 +63,7 @@ abstract class SwipeUpGestureTutorialController extends TutorialController {
private static final int FAKE_PREVIOUS_TASK_MARGIN = Utilities.dpToPx(12);
protected static final long TASK_VIEW_END_ANIMATION_DURATION_MILLIS = 300;
private static final long HOME_SWIPE_ANIMATION_DURATION_MILLIS = 625;
private static final long OVERVIEW_SWIPE_ANIMATION_DURATION_MILLIS = 1000;
@@ -89,6 +90,7 @@ abstract class SwipeUpGestureTutorialController extends TutorialController {
mFakeTaskView.setAlpha(1);
mFakePreviousTaskView.setVisibility(View.INVISIBLE);
mFakePreviousTaskView.setAlpha(1);
mFakePreviousTaskView.setToSingleRowLayout(false);
mShowTasks = false;
mShowPreviousTasks = false;
mRunningWindowAnim = null;
@@ -146,7 +148,8 @@ abstract class SwipeUpGestureTutorialController extends TutorialController {
anim.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation, boolean isReverse) {
PendingAnimation fadeAnim = new PendingAnimation(300);
PendingAnimation fadeAnim =
new PendingAnimation(TASK_VIEW_END_ANIMATION_DURATION_MILLIS);
if (reset) {
fadeAnim.setFloat(mTaskViewSwipeUpAnimation
.getCurrentShift(), AnimatedFloat.VALUE, 0, ACCEL);
@@ -159,6 +162,23 @@ abstract class SwipeUpGestureTutorialController extends TutorialController {
fadeAnim.addListener(AnimatorListeners.forSuccessCallback(onEndRunnable));
}
AnimatorSet animset = fadeAnim.buildAnim();
if (reset && mTutorialFragment.isLargeScreen()) {
animset.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
super.onAnimationStart(animation);
Animator multiRowAnimation =
mFakePreviousTaskView.createAnimationToMultiRowLayout();
if (multiRowAnimation != null) {
multiRowAnimation.setDuration(
TASK_VIEW_END_ANIMATION_DURATION_MILLIS).start();
}
}
});
}
animset.setStartDelay(100);
animset.start();
mRunningWindowAnim = RunningWindowAnim.wrap(animset);
@@ -301,7 +321,7 @@ abstract class SwipeUpGestureTutorialController extends TutorialController {
public RectF getWindowTargetRect() {
int fakeHomeIconSizePx = Utilities.dpToPx(60);
int fakeHomeIconLeft = mFakeHotseatView.getLeft();
int fakeHomeIconTop = mDp.heightPx - Utilities.dpToPx(216);
int fakeHomeIconTop = mFakeHotseatView.getTop();
return new RectF(fakeHomeIconLeft, fakeHomeIconTop,
fakeHomeIconLeft + fakeHomeIconSizePx,
fakeHomeIconTop + fakeHomeIconSizePx);
@@ -24,6 +24,7 @@ import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.annotation.ColorRes;
import android.content.Context;
import android.content.pm.PackageManager;
import android.graphics.drawable.AnimatedVectorDrawable;
@@ -87,7 +88,7 @@ abstract class TutorialController implements BackGestureAttemptCallback,
final ImageView mFakeHotseatView;
final ClipIconView mFakeIconView;
final FrameLayout mFakeTaskView;
final View mFakePreviousTaskView;
final AnimatedTaskView mFakePreviousTaskView;
final View mRippleView;
final RippleDrawable mRippleDrawable;
final Button mActionButton;
@@ -177,9 +178,9 @@ abstract class TutorialController implements BackGestureAttemptCallback,
return View.NO_ID;
}
@DrawableRes
protected int getMockPreviousAppTaskThumbnailResId() {
return R.drawable.default_sandbox_app_previous_task_thumbnail;
@ColorRes
protected int getMockPreviousAppTaskThumbnailColorResId() {
return R.color.gesture_tutorial_fake_previous_task_view_color;
}
@DrawableRes
@@ -442,8 +443,8 @@ abstract class TutorialController implements BackGestureAttemptCallback,
updateFakeAppTaskViewLayout(getMockAppTaskLayoutResId());
mFakeTaskView.animate().alpha(1).setListener(
AnimatorListeners.forSuccessCallback(() -> mFakeTaskView.animate().cancel()));
mFakePreviousTaskView.setBackground(AppCompatResources.getDrawable(
mContext, getMockPreviousAppTaskThumbnailResId()));
mFakePreviousTaskView.setFakeTaskViewFillColor(mContext.getResources().getColor(
getMockPreviousAppTaskThumbnailColorResId()));
mFakeIconView.setBackground(AppCompatResources.getDrawable(
mContext, getMockAppIconResId()));
}
@@ -20,6 +20,7 @@ import android.animation.AnimatorListenerAdapter;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.graphics.Insets;
import android.graphics.drawable.Animatable2;
import android.graphics.drawable.AnimatedVectorDrawable;
@@ -40,6 +41,7 @@ import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.R;
import com.android.quickstep.interaction.TutorialController.TutorialType;
@@ -63,6 +65,8 @@ abstract class TutorialFragment extends Fragment implements OnTouchListener {
private boolean mFragmentStopped = false;
private boolean mIsLargeScreen;
public static TutorialFragment newInstance(TutorialType tutorialType) {
TutorialFragment fragment = getFragmentForTutorialType(tutorialType);
if (fragment == null) {
@@ -130,6 +134,21 @@ abstract class TutorialFragment extends Fragment implements OnTouchListener {
mTutorialType = (TutorialType) args.getSerializable(KEY_TUTORIAL_TYPE);
mEdgeBackGestureHandler = new EdgeBackGestureHandler(getContext());
mNavBarGestureHandler = new NavBarGestureHandler(getContext());
mIsLargeScreen = InvariantDeviceProfile.INSTANCE.get(getContext())
.getDeviceProfile(getContext()).isTablet;
if (mIsLargeScreen) {
((Activity) getContext()).setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_USER);
} else {
// Temporary until UI mocks for landscape mode for phones are created.
((Activity) getContext()).setRequestedOrientation(
ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
}
}
public boolean isLargeScreen() {
return mIsLargeScreen;
}
@Override