Merge "Tips Gesture Navigation Tutorial [Part 2]" into ub-launcher3-master

This commit is contained in:
TreeHugger Robot 2020-01-07 17:04:57 +00:00 committed by Android (Google) Code Review
commit defb0562c4
12 changed files with 804 additions and 55 deletions

View File

@ -91,6 +91,17 @@
android:taskAffinity="${packageName}.locktask" android:taskAffinity="${packageName}.locktask"
android:directBootAware="true" /> android:directBootAware="true" />
<activity
android:name="com.android.quickstep.interaction.BackGestureTutorialActivity"
android:autoRemoveFromRecents="true"
android:excludeFromRecents="true"
android:screenOrientation="portrait">
<intent-filter>
<action android:name="com.android.quickstep.action.BACK_GESTURE_TUTORIAL" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
</application> </application>
</manifest> </manifest>

View File

@ -1,29 +0,0 @@
<!--
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.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="192dp"
android:height="192dp"
android:viewportWidth="192"
android:viewportHeight="192">
<path
android:pathData="M96,90.01"
android:strokeWidth="2.4297"
android:fillColor="#00000000"
android:strokeColor="#FFC800"/>
<path
android:pathData="M153.24,48.09c-0.5,-1.51 -0.99,-2.86 -1.51,-4.12c-3.03,-7.1 -7.26,-13.45 -12.59,-18.88C127.68,13.42 112.4,7 96.1,7c-14,0 -27.66,4.93 -38.45,13.87C47,29.69 39.61,41.99 36.82,55.51c-0.76,3.9 -1.14,7.86 -1.14,11.78c0,16.39 6.36,31.77 17.9,43.3c2.65,2.65 5.59,5.08 8.74,7.23l0.09,24.14v22.03c0,5.33 4.82,10.01 10.32,10.01h0.41h3.85v0c0,6.23 4.53,10.93 10.53,10.93h16.94c6.01,0 10.54,-4.7 10.54,-10.93v0h4.31c5.56,0 10.26,-4.5 10.26,-9.83v-22.01c0.01,-0.06 0.01,-0.13 0.01,-0.2v-23.74c16.75,-11.15 26.73,-30.15 26.73,-50.94C156.31,60.76 155.28,54.3 153.24,48.09zM118.51,111.08l-0.46,0.29l-0.46,0.29v0.55v0.55v4.38v22.77l-14.12,0V95.9h14h2v-2v-8.5v-2h-2H74.53h-2v2v8.5v2h2h14v44.02l-14.13,0l-0.09,-23.21l-0.02,-4.3l0,-0.54l0,-0.54l-0.45,-0.29l-0.45,-0.29l-3.6,-2.36c-2.81,-1.84 -5.41,-3.95 -7.73,-6.28c-9.27,-9.26 -14.38,-21.63 -14.38,-34.82c0,-3.14 0.3,-6.31 0.9,-9.43C53.25,35.35 73.23,19 96.1,19c13.05,0 25.3,5.15 34.48,14.5c4.27,4.34 7.66,9.43 10.08,15.1c0.39,0.96 0.78,2.03 1.18,3.24c1.64,5 2.47,10.2 2.47,15.45c0,17.1 -8.27,32.58 -22.11,41.43L118.51,111.08z"
android:fillColor="#4285F4"/>
</vector>

View File

@ -69,13 +69,6 @@
<!-- Content description for a close button. [CHAR LIMIT=NONE] --> <!-- Content description for a close button. [CHAR LIMIT=NONE] -->
<string name="back_gesture_tutorial_close_button_content_description" translatable="false">Close</string> <string name="back_gesture_tutorial_close_button_content_description" translatable="false">Close</string>
<!-- Title shown on the notification of Back gesture tutorial. [CHAR LIMIT=30] -->
<string name="back_gesture_tutorial_notification_title" translatable="false">Try the new back gesture</string>
<!-- Subtitle shown on the notification of Back gesture tutorial. [CHAR LIMIT=60] -->
<string name="back_gesture_tutorial_notification_subtitle" translatable="false">Learn how to go back while using your apps</string>
<!-- Action text shown on the notification of Back gesture tutorial. [CHAR LIMIT=14] -->
<string name="back_gesture_tutorial_notification_action_label" translatable="false">Try it</string>
<!-- Title shown during interactive part of Back gesture tutorial for right edge. [CHAR LIMIT=30] --> <!-- Title shown during interactive part of Back gesture tutorial for right edge. [CHAR LIMIT=30] -->
<string name="back_gesture_tutorial_playground_title_swipe_inward_right_edge" translatable="false">Try the back gesture</string> <string name="back_gesture_tutorial_playground_title_swipe_inward_right_edge" translatable="false">Try the back gesture</string>
<!-- Subtitle shown during interactive parts of Back gesture tutorial for right edge. [CHAR LIMIT=60] --> <!-- Subtitle shown during interactive parts of Back gesture tutorial for right edge. [CHAR LIMIT=60] -->

View File

@ -1,19 +0,0 @@
<!--
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.
-->
<alias xmlns:android="http://schemas.android.com/apk/res/android">
<intent
android:action="com.android.quickstep.action.BACK_GESTURE_TUTORIAL" />
</alias>

View File

@ -0,0 +1,73 @@
/*
* 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.
*/
package com.android.quickstep.interaction;
import android.graphics.Color;
import android.os.Bundle;
import android.view.View;
import android.view.Window;
import androidx.fragment.app.FragmentActivity;
import com.android.launcher3.R;
import com.android.quickstep.interaction.BackGestureTutorialFragment.TutorialStep;
import com.android.quickstep.interaction.BackGestureTutorialFragment.TutorialType;
import java.util.Optional;
/** Shows the Back gesture interactive tutorial in full screen mode. */
public class BackGestureTutorialActivity extends FragmentActivity {
Optional<BackGestureTutorialFragment> mFragment = Optional.empty();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.back_gesture_tutorial_activity);
mFragment = Optional.of(BackGestureTutorialFragment.newInstance(TutorialStep.ENGAGED,
TutorialType.RIGHT_EDGE_BACK_NAVIGATION));
getSupportFragmentManager().beginTransaction()
.add(R.id.back_gesture_tutorial_fragment_container, mFragment.get())
.commit();
}
@Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
if (hasFocus) {
hideSystemUI();
}
}
@Override
public void onBackPressed() {
if (mFragment.isPresent()) {
mFragment.get().onBackPressed();
}
}
private void hideSystemUI() {
getWindow().getDecorView().setSystemUiVisibility(
View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
| View.SYSTEM_UI_FLAG_LAYOUT_STABLE
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
| View.SYSTEM_UI_FLAG_FULLSCREEN);
getWindow().setNavigationBarColor(Color.TRANSPARENT);
}
}

View File

@ -0,0 +1,64 @@
/*
* 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.
*/
package com.android.quickstep.interaction;
import android.view.View;
import com.android.launcher3.R;
import com.android.quickstep.interaction.BackGestureTutorialFragment.TutorialStep;
import java.util.Optional;
/**
* An implementation of {@link BackGestureTutorialController} that defines the behavior of the
* {@link TutorialStep#CONFIRM}.
*/
final class BackGestureTutorialConfirmController extends BackGestureTutorialController {
BackGestureTutorialConfirmController(BackGestureTutorialFragment fragment,
BackGestureTutorialTypeInfo tutorialTypeInfo) {
super(fragment, TutorialStep.CONFIRM, Optional.of(tutorialTypeInfo));
}
@Override
Optional<Integer> getTitleStringId() {
return Optional.of(mTutorialTypeInfo.get().getTutorialConfirmTitleId());
}
@Override
Optional<Integer> getSubtitleStringId() {
return Optional.of(mTutorialTypeInfo.get().getTutorialConfirmSubtitleId());
}
@Override
Optional<Integer> getActionButtonStringId() {
return Optional.of(R.string.back_gesture_tutorial_action_button_label);
}
@Override
Optional<Integer> getActionTextButtonStringId() {
return Optional.of(R.string.back_gesture_tutorial_action_text_button_label);
}
@Override
void onActionButtonClicked(View button) {
hideHandCoachingAnimation();
if (button == mActionTextButton) {
mFragment.startSystemNavigationSetting();
}
mFragment.closeTutorial();
}
}

View File

@ -0,0 +1,165 @@
/*
* 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.
*/
package com.android.quickstep.interaction;
import android.view.View;
import android.widget.Button;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.android.launcher3.R;
import com.android.quickstep.interaction.BackGestureTutorialFragment.TutorialStep;
import com.android.quickstep.interaction.BackGestureTutorialFragment.TutorialType;
import java.util.Optional;
/**
* Defines the behavior of the particular {@link TutorialStep} and implements the transition to it.
*/
abstract class BackGestureTutorialController {
final BackGestureTutorialFragment mFragment;
final TutorialStep mTutorialStep;
final Optional<BackGestureTutorialTypeInfo> mTutorialTypeInfo;
final Button mActionTextButton;
final Button mActionButton;
final TextView mSubtitleTextView;
final ImageButton mCloseButton;
final BackGestureTutorialHandAnimation mHandCoachingAnimation;
final LinearLayout mTitlesContainer;
private final TextView mTitleTextView;
private final ImageView mHandCoachingView;
BackGestureTutorialController(
BackGestureTutorialFragment fragment,
TutorialStep tutorialStep,
Optional<BackGestureTutorialTypeInfo> tutorialTypeInfo) {
mFragment = fragment;
mTutorialStep = tutorialStep;
mTutorialTypeInfo = tutorialTypeInfo;
View rootView = fragment.getRootView();
mActionTextButton = rootView.findViewById(
R.id.back_gesture_tutorial_fragment_action_text_button);
mActionButton = rootView.findViewById(R.id.back_gesture_tutorial_fragment_action_button);
mSubtitleTextView = rootView.findViewById(
R.id.back_gesture_tutorial_fragment_subtitle_view);
mTitleTextView = rootView.findViewById(R.id.back_gesture_tutorial_fragment_title_view);
mHandCoachingView = rootView.findViewById(
R.id.back_gesture_tutorial_fragment_hand_coaching);
mHandCoachingAnimation = mFragment.getHandAnimation();
mHandCoachingView.bringToFront();
mCloseButton = rootView.findViewById(R.id.back_gesture_tutorial_fragment_close_button);
mTitlesContainer = rootView.findViewById(
R.id.back_gesture_tutorial_fragment_titles_container);
}
void transitToController() {
updateTitles();
updateActionButtons();
}
void hideHandCoachingAnimation() {
mHandCoachingAnimation.stop();
}
void onGestureDetected() {
hideHandCoachingAnimation();
if (mTutorialStep == TutorialStep.CONFIRM) {
mFragment.closeTutorial();
return;
}
if (mTutorialTypeInfo.get().getTutorialType() == TutorialType.RIGHT_EDGE_BACK_NAVIGATION) {
mFragment.changeController(TutorialStep.ENGAGED,
TutorialType.LEFT_EDGE_BACK_NAVIGATION);
return;
}
mFragment.changeController(TutorialStep.CONFIRM);
}
abstract Optional<Integer> getTitleStringId();
abstract Optional<Integer> getSubtitleStringId();
abstract Optional<Integer> getActionButtonStringId();
abstract Optional<Integer> getActionTextButtonStringId();
abstract void onActionButtonClicked(View button);
private void updateActionButtons() {
updateButton(mActionButton, getActionButtonStringId(), this::onActionButtonClicked);
updateButton(mActionTextButton, getActionTextButtonStringId(), this::onActionButtonClicked);
}
private static void updateButton(Button button, Optional<Integer> stringId,
View.OnClickListener listener) {
if (!stringId.isPresent()) {
button.setVisibility(View.INVISIBLE);
return;
}
button.setVisibility(View.VISIBLE);
button.setText(stringId.get());
button.setOnClickListener(listener);
}
private void updateTitles() {
updateTitleView(mTitleTextView, getTitleStringId(),
R.style.TextAppearance_BackGestureTutorial_Title);
updateTitleView(mSubtitleTextView, getSubtitleStringId(),
R.style.TextAppearance_BackGestureTutorial_Subtitle);
}
private static void updateTitleView(TextView textView, Optional<Integer> stringId,
int styleId) {
if (!stringId.isPresent()) {
textView.setVisibility(View.GONE);
return;
}
textView.setVisibility(View.VISIBLE);
textView.setText(stringId.get());
textView.setTextAppearance(styleId);
}
/**
* Constructs {@link BackGestureTutorialController} for providing {@link TutorialType} and
* {@link TutorialStep}.
*/
static Optional<BackGestureTutorialController> getTutorialController(
BackGestureTutorialFragment fragment, TutorialStep tutorialStep,
TutorialType tutorialType) {
BackGestureTutorialTypeInfo tutorialTypeInfo =
BackGestureTutorialTypeInfoProvider.getTutorialTypeInfo(tutorialType);
switch (tutorialStep) {
case ENGAGED:
return Optional.of(
new BackGestureTutorialEngagedController(fragment, tutorialTypeInfo));
case CONFIRM:
return Optional.of(
new BackGestureTutorialConfirmController(fragment, tutorialTypeInfo));
default:
throw new AssertionError("Unexpected tutorial step: " + tutorialStep);
}
}
}

View File

@ -0,0 +1,64 @@
/*
* 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.
*/
package com.android.quickstep.interaction;
import android.view.View;
import com.android.quickstep.interaction.BackGestureTutorialFragment.TutorialStep;
import java.util.Optional;
/**
* An implementation of {@link BackGestureTutorialController} that defines the behavior of the
* {@link TutorialStep#ENGAGED}.
*/
final class BackGestureTutorialEngagedController extends BackGestureTutorialController {
BackGestureTutorialEngagedController(
BackGestureTutorialFragment fragment, BackGestureTutorialTypeInfo tutorialTypeInfo) {
super(fragment, TutorialStep.ENGAGED, Optional.of(tutorialTypeInfo));
}
@Override
void transitToController() {
super.transitToController();
mHandCoachingAnimation.maybeStartLoopedAnimation(mTutorialTypeInfo.get().getTutorialType());
}
@Override
Optional<Integer> getTitleStringId() {
return Optional.of(mTutorialTypeInfo.get().getTutorialPlaygroundTitleId());
}
@Override
Optional<Integer> getSubtitleStringId() {
return Optional.of(mTutorialTypeInfo.get().getTutorialEngagedSubtitleId());
}
@Override
Optional<Integer> getActionButtonStringId() {
return Optional.empty();
}
@Override
Optional<Integer> getActionTextButtonStringId() {
return Optional.empty();
}
@Override
void onActionButtonClicked(View button) {
}
}

View File

@ -0,0 +1,165 @@
/*
* 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.
*/
package com.android.quickstep.interaction;
import android.content.ActivityNotFoundException;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.fragment.app.Fragment;
import com.android.launcher3.R;
import java.net.URISyntaxException;
import java.util.Optional;
/** Shows the Back gesture interactive tutorial. */
public class BackGestureTutorialFragment extends Fragment {
private static final String LOG_TAG = "TutorialFragment";
private static final String KEY_TUTORIAL_STEP = "tutorialStep";
private static final String KEY_TUTORIAL_TYPE = "tutorialType";
private static final String SYSTEM_NAVIGATION_SETTING_INTENT =
"#Intent;action=com.android.settings.SEARCH_RESULT_TRAMPOLINE;S"
+ ".:settings:fragment_args_key=gesture_system_navigation_input_summary;S"
+ ".:settings:show_fragment=com.android.settings.gestures"
+ ".SystemNavigationGestureSettings;end";
private TutorialStep mTutorialStep;
private TutorialType mTutorialType;
private Optional<BackGestureTutorialController> mTutorialController = Optional.empty();
private View mRootView;
private BackGestureTutorialHandAnimation mHandCoachingAnimation;
public static BackGestureTutorialFragment newInstance(
TutorialStep tutorialStep, TutorialType tutorialType) {
BackGestureTutorialFragment fragment = new BackGestureTutorialFragment();
Bundle args = new Bundle();
args.putSerializable(KEY_TUTORIAL_STEP, tutorialStep);
args.putSerializable(KEY_TUTORIAL_TYPE, tutorialType);
fragment.setArguments(args);
return fragment;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Bundle args = savedInstanceState != null ? savedInstanceState : getArguments();
mTutorialStep = (TutorialStep) args.getSerializable(KEY_TUTORIAL_STEP);
mTutorialType = (TutorialType) args.getSerializable(KEY_TUTORIAL_TYPE);
}
@Override
public View onCreateView(
LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
mRootView = inflater.inflate(R.layout.back_gesture_tutorial_fragment,
container, /* attachToRoot= */ false);
mRootView.findViewById(R.id.back_gesture_tutorial_fragment_close_button)
.setOnClickListener(this::onCloseButtonClicked);
mHandCoachingAnimation = new BackGestureTutorialHandAnimation(getContext(), mRootView);
return mRootView;
}
@Override
public void onResume() {
super.onResume();
changeController(mTutorialStep, mTutorialType);
}
@Override
public void onPause() {
super.onPause();
mHandCoachingAnimation.stop();
}
@Override
public void onSaveInstanceState(Bundle savedInstanceState) {
savedInstanceState.putSerializable(KEY_TUTORIAL_STEP, mTutorialStep);
savedInstanceState.putSerializable(KEY_TUTORIAL_TYPE, mTutorialType);
super.onSaveInstanceState(savedInstanceState);
}
View getRootView() {
return mRootView;
}
BackGestureTutorialHandAnimation getHandAnimation() {
return mHandCoachingAnimation;
}
void changeController(TutorialStep tutorialStep) {
changeController(tutorialStep, mTutorialType);
}
void changeController(TutorialStep tutorialStep, TutorialType tutorialType) {
Optional<BackGestureTutorialController> tutorialController =
BackGestureTutorialController.getTutorialController(/* fragment= */ this,
tutorialStep, tutorialType);
if (!tutorialController.isPresent()) {
return;
}
mTutorialController = tutorialController;
mTutorialController.get().transitToController();
this.mTutorialStep = mTutorialController.get().mTutorialStep;
this.mTutorialType = tutorialType;
}
void onBackPressed() {
if (mTutorialController.isPresent()) {
mTutorialController.get().onGestureDetected();
}
}
void closeTutorial() {
getActivity().finish();
}
void startSystemNavigationSetting() {
try {
startActivityForResult(
Intent.parseUri(SYSTEM_NAVIGATION_SETTING_INTENT, /* flags= */ 0),
/* requestCode= */ 0);
} catch (URISyntaxException e) {
Log.e(LOG_TAG, "The launch Intent Uri is wrong syntax: " + e);
} catch (ActivityNotFoundException e) {
Log.e(LOG_TAG, "The launch Activity not found: " + e);
}
}
private void onCloseButtonClicked(View button) {
closeTutorial();
}
/** Denotes the step of the tutorial. */
enum TutorialStep {
ENGAGED,
CONFIRM,
}
/** Denotes the type of the tutorial. */
enum TutorialType {
RIGHT_EDGE_BACK_NAVIGATION,
LEFT_EDGE_BACK_NAVIGATION,
}
}

View File

@ -0,0 +1,94 @@
/*
* 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.
*/
package com.android.quickstep.interaction;
import android.content.Context;
import android.graphics.drawable.Animatable2;
import android.graphics.drawable.AnimatedVectorDrawable;
import android.graphics.drawable.Drawable;
import android.view.View;
import android.widget.ImageView;
import androidx.core.content.ContextCompat;
import com.android.launcher3.R;
import com.android.quickstep.interaction.BackGestureTutorialFragment.TutorialType;
import java.time.Duration;
/** Hand coaching animation. */
final class BackGestureTutorialHandAnimation {
// A delay for waiting the Activity fully launches.
private static final Duration ANIMATION_START_DELAY = Duration.ofMillis(300L);
private final ImageView mHandCoachingView;
private final AnimatedVectorDrawable mGestureAnimation;
private boolean mIsAnimationPlayed = false;
BackGestureTutorialHandAnimation(Context context, View rootView) {
mHandCoachingView = rootView.findViewById(
R.id.back_gesture_tutorial_fragment_hand_coaching);
mGestureAnimation = (AnimatedVectorDrawable) ContextCompat.getDrawable(context,
R.drawable.back_gesture);
}
boolean isRunning() {
return mGestureAnimation.isRunning();
}
/**
* Starts animation if the playground is launched for the first time.
*/
void maybeStartLoopedAnimation(TutorialType tutorialType) {
if (isRunning() || mIsAnimationPlayed) {
return;
}
mIsAnimationPlayed = true;
clearAnimationCallbacks();
mGestureAnimation.registerAnimationCallback(
new Animatable2.AnimationCallback() {
@Override
public void onAnimationEnd(Drawable drawable) {
super.onAnimationEnd(drawable);
mGestureAnimation.start();
}
});
start(tutorialType);
}
private void start(TutorialType tutorialType) {
// Because the gesture animation has only the right side form.
// The left side form of the gesture animation is made from flipping the View.
float rotationY = tutorialType == TutorialType.LEFT_EDGE_BACK_NAVIGATION ? 180f : 0f;
mHandCoachingView.setRotationY(rotationY);
mHandCoachingView.setImageDrawable(mGestureAnimation);
mHandCoachingView.postDelayed(() -> mGestureAnimation.start(),
ANIMATION_START_DELAY.toMillis());
}
private void clearAnimationCallbacks() {
mGestureAnimation.clearAnimationCallbacks();
}
void stop() {
mIsAnimationPlayed = false;
clearAnimationCallbacks();
mGestureAnimation.stop();
}
}

View File

@ -0,0 +1,109 @@
/*
* 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.
*/
package com.android.quickstep.interaction;
import com.android.quickstep.interaction.BackGestureTutorialFragment.TutorialType;
/** Defines the UI element identifiers for the particular {@link TutorialType}. */
final class BackGestureTutorialTypeInfo {
private final TutorialType mTutorialType;
private final int mTutorialPlaygroundTitleId;
private final int mTutorialEngagedSubtitleId;
private final int mTutorialConfirmTitleId;
private final int mTutorialConfirmSubtitleId;
TutorialType getTutorialType() {
return mTutorialType;
}
int getTutorialPlaygroundTitleId() {
return mTutorialPlaygroundTitleId;
}
int getTutorialEngagedSubtitleId() {
return mTutorialEngagedSubtitleId;
}
int getTutorialConfirmTitleId() {
return mTutorialConfirmTitleId;
}
int getTutorialConfirmSubtitleId() {
return mTutorialConfirmSubtitleId;
}
static Builder builder() {
return new Builder();
}
private BackGestureTutorialTypeInfo(
TutorialType tutorialType,
int tutorialPlaygroundTitleId,
int tutorialEngagedSubtitleId,
int tutorialConfirmTitleId,
int tutorialConfirmSubtitleId) {
mTutorialType = tutorialType;
mTutorialPlaygroundTitleId = tutorialPlaygroundTitleId;
mTutorialEngagedSubtitleId = tutorialEngagedSubtitleId;
mTutorialConfirmTitleId = tutorialConfirmTitleId;
mTutorialConfirmSubtitleId = tutorialConfirmSubtitleId;
}
/** Builder for producing {@link BackGestureTutorialTypeInfo} objects. */
static class Builder {
private TutorialType mTutorialType;
private Integer mTutorialPlaygroundTitleId;
private Integer mTutorialEngagedSubtitleId;
private Integer mTutorialConfirmTitleId;
private Integer mTutorialConfirmSubtitleId;
Builder setTutorialType(TutorialType tutorialType) {
mTutorialType = tutorialType;
return this;
}
Builder setTutorialPlaygroundTitleId(int stringId) {
mTutorialPlaygroundTitleId = stringId;
return this;
}
Builder setTutorialEngagedSubtitleId(int stringId) {
mTutorialEngagedSubtitleId = stringId;
return this;
}
Builder setTutorialConfirmTitleId(int stringId) {
mTutorialConfirmTitleId = stringId;
return this;
}
Builder setTutorialConfirmSubtitleId(int stringId) {
mTutorialConfirmSubtitleId = stringId;
return this;
}
BackGestureTutorialTypeInfo build() {
return new BackGestureTutorialTypeInfo(
mTutorialType,
mTutorialPlaygroundTitleId,
mTutorialEngagedSubtitleId,
mTutorialConfirmTitleId,
mTutorialConfirmSubtitleId);
}
}
}

View File

@ -0,0 +1,59 @@
/*
* 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.
*/
package com.android.quickstep.interaction;
import com.android.launcher3.R;
import com.android.quickstep.interaction.BackGestureTutorialFragment.TutorialType;
/** Provides instances of {@link BackGestureTutorialTypeInfo} for each {@link TutorialType}. */
final class BackGestureTutorialTypeInfoProvider {
private static final BackGestureTutorialTypeInfo RIGHT_EDGE_BACK_NAV_TUTORIAL_INFO =
BackGestureTutorialTypeInfo.builder()
.setTutorialType(TutorialType.RIGHT_EDGE_BACK_NAVIGATION)
.setTutorialPlaygroundTitleId(
R.string.back_gesture_tutorial_playground_title_swipe_inward_right_edge)
.setTutorialEngagedSubtitleId(
R.string.back_gesture_tutorial_engaged_subtitle_swipe_inward_right_edge)
.setTutorialConfirmTitleId(R.string.back_gesture_tutorial_confirm_title)
.setTutorialConfirmSubtitleId(R.string.back_gesture_tutorial_confirm_subtitle)
.build();
private static final BackGestureTutorialTypeInfo LEFT_EDGE_BACK_NAV_TUTORIAL_INFO =
BackGestureTutorialTypeInfo.builder()
.setTutorialType(TutorialType.LEFT_EDGE_BACK_NAVIGATION)
.setTutorialPlaygroundTitleId(
R.string.back_gesture_tutorial_playground_title_swipe_inward_left_edge)
.setTutorialEngagedSubtitleId(
R.string.back_gesture_tutorial_engaged_subtitle_swipe_inward_left_edge)
.setTutorialConfirmTitleId(R.string.back_gesture_tutorial_confirm_title)
.setTutorialConfirmSubtitleId(R.string.back_gesture_tutorial_confirm_subtitle)
.build();
static BackGestureTutorialTypeInfo getTutorialTypeInfo(TutorialType tutorialType) {
switch (tutorialType) {
case RIGHT_EDGE_BACK_NAVIGATION:
return RIGHT_EDGE_BACK_NAV_TUTORIAL_INFO;
case LEFT_EDGE_BACK_NAVIGATION:
return LEFT_EDGE_BACK_NAV_TUTORIAL_INFO;
default:
throw new AssertionError("Unexpected tutorial type: " + tutorialType);
}
}
private BackGestureTutorialTypeInfoProvider() {
}
}