Merge "Complete gesture nav sandbox for for UXR." into sc-dev

This commit is contained in:
Schneider Victor-tulias 2021-04-22 17:35:29 +00:00 committed by Android (Google) Code Review
commit 5a6e10bba8
30 changed files with 2901 additions and 381 deletions

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<solid android:color="#303030"/>
</shape>

View File

@ -1,10 +1,20 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="394dp"
android:height="172dp"
android:viewportWidth="394"
android:viewportHeight="172">
<path
android:pathData="M20,0L374,0A20,20 0,0 1,394 20L394,152A20,20 0,0 1,374 172L20,172A20,20 0,0 1,0 152L0,20A20,20 0,0 1,20 0z"
android:fillColor="?android:attr/colorBackgroundFloating"
android:fillAlpha="0.9"/>
</vector>
<!--
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.
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners android:radius="28dp"/>
<solid android:color="?android:attr/colorBackgroundFloating"/>
</shape>

View File

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="#909090"
android:pathData="M19,6.41L17.59,5 12,10.59 6.41,5 5,6.41 10.59,12 5,17.59 6.41,19 12,13.41 17.59,19 19,17.59 13.41,12z"/>
</vector>

View File

@ -15,6 +15,6 @@
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners android:radius="@dimen/default_dialog_corner_radius"/>
<corners android:radius="50dp"/>
<solid android:color="@color/gesture_tutorial_primary_color"/>
</shape>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,21 @@
<!--
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.
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners android:radius="50dp"/>
<solid android:color="@android:color/transparent"/>
<stroke android:width="1dp" android:color="@color/gesture_tutorial_primary_color"/>
</shape>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,74 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/bg_sandbox_feedback"
android:paddingTop="24dp"
android:paddingBottom="24dp"
android:paddingStart="16dp"
android:paddingEnd="16dp">
<TextView
android:id="@+id/gesture_tutorial_dialog_title"
style="@style/TextAppearance.GestureTutorial.Dialog.Title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="@string/skip_tutorial_dialog_title"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
<TextView
android:id="@+id/gesture_tutorial_dialog_subtitle"
style="@style/TextAppearance.GestureTutorial.Dialog.Subtitle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="@string/skip_tutorial_dialog_subtitle"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/gesture_tutorial_dialog_title"/>
<!-- android:stateListAnimator="@null" removes shadow and normal on click behavior (increase
of elevation and shadow) which is replaced by ripple effect in android:foreground -->
<Button
android:id="@+id/gesture_tutorial_dialog_cancel_button"
style="@style/TextAppearance.GestureTutorial.CancelButtonLabel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="46dp"
android:paddingTop="8dp"
android:paddingBottom="8dp"
android:paddingStart="16dp"
android:paddingEnd="16dp"
android:background="@drawable/gesture_tutorial_cancel_button_background"
android:foreground="?android:attr/selectableItemBackgroundBorderless"
android:stateListAnimator="@null"
android:text="@string/gesture_tutorial_action_button_label_cancel"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/gesture_tutorial_dialog_subtitle"/>
<Button
android:id="@+id/gesture_tutorial_dialog_confirm_button"
style="@style/TextAppearance.GestureTutorial.ButtonLabel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="46dp"
android:paddingTop="8dp"
android:paddingBottom="8dp"
android:paddingStart="16dp"
android:paddingEnd="16dp"
android:background="@drawable/gesture_tutorial_action_button_background"
android:foreground="?android:attr/selectableItemBackgroundBorderless"
android:stateListAnimator="@null"
android:text="@string/gesture_tutorial_action_button_label_skip"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/gesture_tutorial_dialog_subtitle"/>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -15,6 +15,7 @@
-->
<com.android.quickstep.interaction.RootSandboxLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipChildren="false">
@ -53,112 +54,93 @@
android:layout_height="match_parent"
android:background="@drawable/gesture_tutorial_ripple"/>
<VideoView
<ImageView
android:id="@+id/gesture_tutorial_feedback_video"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignParentTop="true"
android:layout_alignParentBottom="true"
android:layout_alignParentStart="true"
android:layout_alignParentEnd="true"/>
<ImageButton
android:id="@+id/gesture_tutorial_fragment_close_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
android:layout_marginStart="4dp"
android:layout_marginTop="30dp"
android:accessibilityTraversalAfter="@id/gesture_tutorial_fragment_titles_container"
android:background="@android:color/transparent"
android:contentDescription="@string/gesture_tutorial_close_button_content_description"
android:padding="18dp"
android:src="@drawable/gesture_tutorial_close_button"
android:tint="?android:attr/textColorPrimary"
android:layout_alignParentEnd="true"
android:scaleType="fitXY"
android:visibility="gone"/>
<LinearLayout
android:id="@+id/gesture_tutorial_fragment_titles_container"
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/gesture_tutorial_fragment_feedback_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_marginTop="70dp"
android:focusable="true"
android:gravity="center_horizontal"
android:orientation="vertical">
<TextView
android:id="@+id/gesture_tutorial_fragment_title_view"
style="@style/TextAppearance.GestureTutorial.Title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/gesture_tutorial_title_margin_start_end"
android:layout_marginEnd="@dimen/gesture_tutorial_title_margin_start_end"/>
<TextView
android:id="@+id/gesture_tutorial_fragment_subtitle_view"
style="@style/TextAppearance.GestureTutorial.Subtitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/gesture_tutorial_subtitle_margin_start_end"
android:layout_marginTop="10dp"
android:layout_marginEnd="@dimen/gesture_tutorial_subtitle_margin_start_end"/>
</LinearLayout>
<LinearLayout
android:id="@+id/gesture_tutorial_fragment_feedback_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:layout_marginStart="@dimen/gesture_tutorial_feedback_margin_start_end"
android:layout_marginEnd="@dimen/gesture_tutorial_feedback_margin_start_end"
android:layout_marginTop="24dp"
android:padding="24dp"
android:gravity="center_vertical"
android:background="@drawable/bg_sandbox_feedback">
<ImageButton
android:id="@+id/gesture_tutorial_fragment_close_button"
android:layout_width="32dp"
android:layout_height="32dp"
android:src="@drawable/close_icon"
android:background="@drawable/bg_sandbox_close_button"
android:contentDescription="@string/gesture_tutorial_close_button_content_description"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
<TextView
android:id="@+id/gesture_tutorial_fragment_feedback_title"
style="@style/TextAppearance.GestureTutorial.Feedback.Title"
android:layout_width="wrap_content"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginBottom="10dp"/>
android:layout_marginTop="16dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/gesture_tutorial_fragment_close_button"/>
<TextView
android:id="@+id/gesture_tutorial_fragment_feedback_subtitle"
style="@style/TextAppearance.GestureTutorial.Feedback.Subtitle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/gesture_tutorial_fragment_feedback_title"/>
<TextView
android:id="@+id/gesture_tutorial_fragment_feedback_tutorial_step"
style="@style/TextAppearance.GestureTutorial.Feedback.Subtext"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toStartOf="@id/gesture_tutorial_fragment_action_button"
app:layout_constraintTop_toTopOf="@id/gesture_tutorial_fragment_action_button"
app:layout_constraintBottom_toBottomOf="@id/gesture_tutorial_fragment_action_button"/>
<!-- android:stateListAnimator="@null" removes shadow and normal on click behavior (increase
of elevation and shadow) which is replaced by ripple effect in android:foreground -->
<Button
android:id="@+id/gesture_tutorial_fragment_action_button"
style="@style/TextAppearance.GestureTutorial.ButtonLabel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:paddingTop="8dp"
android:paddingBottom="8dp"
android:paddingStart="16dp"
android:paddingEnd="16dp"
android:background="@drawable/gesture_tutorial_action_button_background"
android:foreground="?android:attr/selectableItemBackgroundBorderless"
android:stateListAnimator="@null"
android:visibility="invisible"
<!-- android:stateListAnimator="@null" removes shadow and normal on click behavior (increase
of elevation and shadow) which is replaced by ripple effect in android:foreground -->
<Button
android:id="@+id/gesture_tutorial_fragment_action_button"
style="@style/TextAppearance.GestureTutorial.ButtonLabel"
android:layout_width="142dp"
android:layout_height="49dp"
android:layout_alignParentEnd="true"
android:layout_alignParentBottom="true"
android:layout_marginEnd="@dimen/gesture_tutorial_button_margin_start_end"
android:layout_marginBottom="48dp"
android:background="@drawable/gesture_tutorial_action_button_background"
android:foreground="?android:attr/selectableItemBackgroundBorderless"
android:stateListAnimator="@null"/>
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/gesture_tutorial_fragment_feedback_subtitle"/>
</androidx.constraintlayout.widget.ConstraintLayout>
<Button
android:id="@+id/gesture_tutorial_fragment_action_text_button"
style="@style/TextAppearance.GestureTutorial.TextButtonLabel"
android:layout_width="142dp"
android:layout_height="49dp"
android:layout_alignParentStart="true"
android:layout_alignParentBottom="true"
android:layout_marginStart="@dimen/gesture_tutorial_button_margin_start_end"
android:layout_marginBottom="48dp"
android:background="@null"
android:foreground="?android:attr/selectableItemBackgroundBorderless"
android:stateListAnimator="@null"/>
</com.android.quickstep.interaction.RootSandboxLayout>

Binary file not shown.

View File

@ -126,7 +126,7 @@
<!-- Feedback shown during interactive parts of Overview gesture tutorial when the gesture is started too far from the edge. [CHAR LIMIT=100] -->
<string name="overview_gesture_feedback_swipe_too_far_from_edge">Make sure you swipe up from the bottom edge of the screen.</string>
<!-- Feedback shown during interactive parts of Overview gesture tutorial when the Home gesture is detected. [CHAR LIMIT=100] -->
<!-- Feedback shown during interactive parts of Overview gesture tutorial when the Home gesture is detected. The window refers to the current app's window during the gesture. [CHAR LIMIT=100] -->
<string name="overview_gesture_feedback_home_detected">Try holding the window for longer before releasing.</string>
<!-- Feedback shown during interactive parts of Overview gesture tutorial when the gesture is horizontal instead of vertical. [CHAR LIMIT=100] -->
<string name="overview_gesture_feedback_wrong_swipe_direction">Make sure you swipe straight up, then pause.</string>

View File

@ -57,6 +57,14 @@
<item name="android:lineHeight">42sp</item>
</style>
<style name="TextAppearance.GestureTutorial.Dialog.Title"
parent="TextAppearance.GestureTutorial.Feedback.Title">
<item name="android:gravity">center_horizontal</item>
<item name="android:fontFamily">google-sans</item>
<item name="android:lineHeight">32sp</item>
<item name="android:textSize">24sp</item>
</style>
<style name="TextAppearance.GestureTutorial.Feedback.Subtitle"
parent="TextAppearance.GestureTutorial">
<item name="android:gravity">start</item>
@ -67,6 +75,21 @@
<item name="android:lineHeight">24sp</item>
</style>
<style name="TextAppearance.GestureTutorial.Dialog.Subtitle"
parent="TextAppearance.GestureTutorial.Feedback.Subtitle">
<item name="android:gravity">center_horizontal</item>
<item name="android:fontFamily">google-sans-text</item>
<item name="android:letterSpacing">0.025</item>
<item name="android:lineHeight">20sp</item>
<item name="android:textSize">14sp</item>
</style>
<style name="TextAppearance.GestureTutorial.Feedback.Subtext"
parent="TextAppearance.GestureTutorial.Feedback.Subtitle">
<item name="android:textSize">16sp</item>
<item name="android:textColor">#909090</item>
</style>
<style name="TextAppearance.GestureTutorial.ButtonLabel"
parent="TextAppearance.GestureTutorial.CallToAction">
<item name="android:gravity">center</item>
@ -76,6 +99,11 @@
<item name="android:textAllCaps">false</item>
</style>
<style name="TextAppearance.GestureTutorial.CancelButtonLabel"
parent="TextAppearance.GestureTutorial.ButtonLabel">
<item name="android:textColor">?android:attr/textColorPrimary</item>
</style>
<style name="TextAppearance.GestureTutorial.TextButtonLabel"
parent="TextAppearance.GestureTutorial.ButtonLabel">
<item name="android:textColor">@color/gesture_tutorial_primary_color</item>

View File

@ -31,33 +31,6 @@ final class AssistantGestureTutorialController extends TutorialController {
super(fragment, tutorialType);
}
@Override
Integer getTitleStringId() {
switch (mTutorialType) {
case ASSISTANT:
return R.string.assistant_gesture_tutorial_playground_title;
case ASSISTANT_COMPLETE:
return R.string.gesture_tutorial_confirm_title;
}
return null;
}
@Override
Integer getSubtitleStringId() {
if (mTutorialType == TutorialType.ASSISTANT) {
return R.string.assistant_gesture_tutorial_playground_subtitle;
}
return null;
}
@Override
Integer getActionButtonStringId() {
if (mTutorialType == ASSISTANT_COMPLETE) {
return R.string.gesture_tutorial_action_button_label_done;
}
return null;
}
@Override
public void onBackGestureAttempted(BackGestureResult result) {
switch (mTutorialType) {
@ -96,14 +69,8 @@ final class AssistantGestureTutorialController extends TutorialController {
break;
case ASSISTANT_COMPLETED:
hideFeedback(true);
showRippleEffect(
() -> {
if (mTutorialFragment.isTutorialComplete()) {
mTutorialFragment.changeController(ASSISTANT_COMPLETE);
} else {
mTutorialFragment.continueTutorial();
}
});
showRippleEffect(null);
showFeedback(R.string.assistant_gesture_tutorial_playground_subtitle);
break;
case ASSISTANT_NOT_STARTED_BAD_ANGLE:
showFeedback(R.string.assistant_gesture_feedback_swipe_not_diagonal);

View File

@ -36,57 +36,23 @@ final class BackGestureTutorialController extends TutorialController {
super(fragment, tutorialType);
}
@Override
Integer getTitleStringId() {
switch (mTutorialType) {
case RIGHT_EDGE_BACK_NAVIGATION:
case LEFT_EDGE_BACK_NAVIGATION:
return R.string.back_gesture_intro_title;
case BACK_NAVIGATION_COMPLETE:
return R.string.gesture_tutorial_confirm_title;
}
return null;
}
@Override
Integer getSubtitleStringId() {
switch (mTutorialType) {
case RIGHT_EDGE_BACK_NAVIGATION:
case LEFT_EDGE_BACK_NAVIGATION:
return R.string.back_gesture_intro_subtitle;
case BACK_NAVIGATION_COMPLETE:
return R.string.back_gesture_tutorial_confirm_subtitle;
}
return null;
}
@Override
Integer getActionButtonStringId() {
if (mTutorialType == BACK_NAVIGATION_COMPLETE) {
return R.string.gesture_tutorial_action_button_label_done;
}
return null;
}
@Override
Integer getActionTextButtonStringId() {
if (mTutorialType == BACK_NAVIGATION_COMPLETE) {
return R.string.gesture_tutorial_action_button_label_settings;
}
return null;
}
@Override
void onActionTextButtonClicked(View button) {
mTutorialFragment.startSystemNavigationSetting();
}
@Nullable
@Override
public View getMockLauncherView() {
return null;
}
@Override
public Integer getIntroductionTitle() {
return mTutorialType == LEFT_EDGE_BACK_NAVIGATION
? R.string.back_gesture_intro_title : null;
}
@Override
public Integer getIntroductionSubtitle() {
return mTutorialType == LEFT_EDGE_BACK_NAVIGATION
? R.string.back_gesture_intro_subtitle : null;
}
@Override
public void onBackGestureAttempted(BackGestureResult result) {
switch (mTutorialType) {
@ -112,8 +78,7 @@ final class BackGestureTutorialController extends TutorialController {
mFakeTaskView.setBackground(AppCompatResources.getDrawable(mContext,
R.drawable.sandbox_fake_google_search));
showRippleEffect(null);
showFeedback(R.string.back_gesture_feedback_complete,
mTutorialFragment::continueTutorial);
showFeedback(R.string.back_gesture_feedback_complete, true);
break;
case BACK_CANCELLED_FROM_RIGHT:
showFeedback(R.string.back_gesture_feedback_cancelled_right_edge);

View File

@ -29,8 +29,8 @@ public class BackGestureTutorialFragment extends TutorialFragment {
@Override
Integer getFeedbackVideoResId() {
return mTutorialType == TutorialType.RIGHT_EDGE_BACK_NAVIGATION
? R.raw.tips_nav_back_right
: R.raw.tips_nav_back_left;
? R.drawable.gesture_tutorial_back_right
: R.drawable.gesture_tutorial_back_left;
}
@Override

View File

@ -41,8 +41,12 @@ public class GestureSandboxActivity extends FragmentActivity {
private static final String KEY_TUTORIAL_STEPS = "tutorial_steps";
private Deque<TutorialType> mTutorialSteps;
private TutorialType mCurrentTutorialStep;
private TutorialFragment mFragment;
private int mCurrentStep;
private int mNumSteps;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@ -51,7 +55,10 @@ public class GestureSandboxActivity extends FragmentActivity {
Bundle args = savedInstanceState == null ? getIntent().getExtras() : savedInstanceState;
mTutorialSteps = getTutorialSteps(args);
mFragment = TutorialFragment.newInstance(mTutorialSteps.pop());
mCurrentStep = 1;
mNumSteps = mTutorialSteps.size();
mCurrentTutorialStep = mTutorialSteps.pop();
mFragment = TutorialFragment.newInstance(mCurrentTutorialStep);
getSupportFragmentManager().beginTransaction()
.add(R.id.gesture_tutorial_fragment_container, mFragment)
.commit();
@ -89,6 +96,21 @@ public class GestureSandboxActivity extends FragmentActivity {
return mTutorialSteps.isEmpty();
}
public int getCurrentStep() {
return mCurrentStep;
}
public int getNumSteps() {
return mNumSteps;
}
/**
* Closes the tutorial and this activity.
*/
public void closeTutorial() {
mFragment.closeTutorial();
}
/**
* Replaces the current TutorialFragment, continuing to the next tutorial step if there is one.
*
@ -99,17 +121,20 @@ public class GestureSandboxActivity extends FragmentActivity {
mFragment.closeTutorial();
return;
}
mFragment = TutorialFragment.newInstance(mTutorialSteps.pop());
mCurrentTutorialStep = mTutorialSteps.pop();
mFragment = TutorialFragment.newInstance(mCurrentTutorialStep);
getSupportFragmentManager().beginTransaction()
.replace(R.id.gesture_tutorial_fragment_container, mFragment)
.runOnCommit(() -> mFragment.onAttachedToWindow())
.commit();
mCurrentStep++;
}
private String[] getTutorialStepNames() {
String[] tutorialStepNames = new String[mTutorialSteps.size()];
String[] tutorialStepNames = new String[mTutorialSteps.size() + 1];
int i = 0;
int i = 1;
tutorialStepNames[0] = mCurrentTutorialStep.name();
for (TutorialType tutorialStep : mTutorialSteps) {
tutorialStepNames[i++] = tutorialStep.name();
}

View File

@ -15,8 +15,6 @@
*/
package com.android.quickstep.interaction;
import static com.android.quickstep.interaction.TutorialController.TutorialType.HOME_NAVIGATION_COMPLETE;
import android.annotation.TargetApi;
import android.graphics.PointF;
import android.os.Build;
@ -34,30 +32,13 @@ final class HomeGestureTutorialController extends SwipeUpGestureTutorialControll
}
@Override
Integer getTitleStringId() {
switch (mTutorialType) {
case HOME_NAVIGATION:
return R.string.home_gesture_intro_title;
case HOME_NAVIGATION_COMPLETE:
return R.string.gesture_tutorial_confirm_title;
}
return null;
public Integer getIntroductionTitle() {
return R.string.home_gesture_intro_title;
}
@Override
Integer getSubtitleStringId() {
if (mTutorialType == TutorialType.HOME_NAVIGATION) {
return R.string.home_gesture_intro_subtitle;
}
return null;
}
@Override
Integer getActionButtonStringId() {
if (mTutorialType == HOME_NAVIGATION_COMPLETE) {
return R.string.gesture_tutorial_action_button_label_done;
}
return null;
public Integer getIntroductionSubtitle() {
return R.string.home_gesture_intro_subtitle;
}
@Override
@ -92,8 +73,9 @@ final class HomeGestureTutorialController extends SwipeUpGestureTutorialControll
switch (result) {
case HOME_GESTURE_COMPLETED: {
animateFakeTaskViewHome(finalVelocity, null);
showActionButton();
showFeedback(R.string.home_gesture_feedback_complete,
mTutorialFragment::continueTutorial);
true);
break;
}
case HOME_NOT_STARTED_TOO_FAR_FROM_EDGE:

View File

@ -25,7 +25,7 @@ public class HomeGestureTutorialFragment extends TutorialFragment {
@Nullable
@Override
Integer getFeedbackVideoResId() {
return R.raw.tips_nav_home;
return R.drawable.gesture_tutorial_home;
}
@Override

View File

@ -43,30 +43,13 @@ final class OverviewGestureTutorialController extends SwipeUpGestureTutorialCont
}
@Override
Integer getTitleStringId() {
switch (mTutorialType) {
case OVERVIEW_NAVIGATION:
return R.string.overview_gesture_intro_title;
case OVERVIEW_NAVIGATION_COMPLETE:
return R.string.gesture_tutorial_confirm_title;
}
return null;
public Integer getIntroductionTitle() {
return R.string.overview_gesture_intro_title;
}
@Override
Integer getSubtitleStringId() {
if (mTutorialType == TutorialType.OVERVIEW_NAVIGATION) {
return R.string.overview_gesture_intro_subtitle;
}
return null;
}
@Override
Integer getActionButtonStringId() {
if (mTutorialType == OVERVIEW_NAVIGATION_COMPLETE) {
return R.string.gesture_tutorial_action_button_label_done;
}
return null;
public Integer getIntroductionSubtitle() {
return R.string.overview_gesture_intro_subtitle;
}
@Nullable
@ -124,8 +107,7 @@ final class OverviewGestureTutorialController extends SwipeUpGestureTutorialCont
animset.start();
mRunningWindowAnim = SwipeUpAnimationLogic.RunningWindowAnim.wrap(animset);
onMotionPaused(true /*arbitrary value*/);
showFeedback(R.string.overview_gesture_feedback_complete,
mTutorialFragment::continueTutorial);
showFeedback(R.string.overview_gesture_feedback_complete, true);
break;
case HOME_OR_OVERVIEW_NOT_STARTED_WRONG_SWIPE_DIRECTION:
case HOME_OR_OVERVIEW_CANCELLED:

View File

@ -25,7 +25,7 @@ public class OverviewGestureTutorialFragment extends TutorialFragment {
@Nullable
@Override
Integer getFeedbackVideoResId() {
return R.raw.tips_nav_overview;
return R.drawable.gesture_tutorial_overview;
}
@Override

View File

@ -17,8 +17,6 @@ package com.android.quickstep.interaction;
import android.graphics.PointF;
import androidx.annotation.Nullable;
import com.android.launcher3.R;
import com.android.quickstep.interaction.EdgeBackGestureHandler.BackGestureResult;
import com.android.quickstep.interaction.NavBarGestureHandler.NavBarGestureResult;
@ -30,24 +28,6 @@ public class SandboxModeTutorialController extends SwipeUpGestureTutorialControl
super(fragment, tutorialType);
}
@Nullable
@Override
Integer getTitleStringId() {
return R.string.sandbox_mode_title;
}
@Nullable
@Override
Integer getSubtitleStringId() {
return R.string.sandbox_mode_subtitle;
}
@Nullable
@Override
Integer getActionButtonStringId() {
return null;
}
@Override
public void onBackGestureAttempted(BackGestureResult result) {
switch (result) {

View File

@ -16,19 +16,25 @@
package com.android.quickstep.interaction;
import android.content.Context;
import android.content.pm.PackageManager;
import android.graphics.drawable.Animatable2;
import android.graphics.drawable.AnimatedVectorDrawable;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.RippleDrawable;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.VideoView;
import androidx.annotation.CallSuper;
import androidx.annotation.DrawableRes;
import androidx.annotation.Nullable;
import androidx.annotation.StringRes;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.content.res.AppCompatResources;
import com.android.launcher3.InvariantDeviceProfile;
@ -41,6 +47,11 @@ import com.android.quickstep.interaction.NavBarGestureHandler.NavBarGestureAttem
abstract class TutorialController implements BackGestureAttemptCallback,
NavBarGestureAttemptCallback {
private static final String TAG = "TutorialController";
private static final String PIXEL_TIPS_APP_PACKAGE_NAME = "com.google.android.apps.tips";
private static final CharSequence DEFAULT_PIXEL_TIPS_APP_NAME = "Pixel Tips";
private static final int FEEDBACK_VISIBLE_MS = 2500;
private static final int FEEDBACK_ANIMATION_MS = 250;
private static final int RIPPLE_VISIBLE_MS = 300;
@ -50,20 +61,19 @@ abstract class TutorialController implements BackGestureAttemptCallback,
final Context mContext;
final ImageButton mCloseButton;
final TextView mTitleTextView;
final TextView mSubtitleTextView;
final ViewGroup mFeedbackView;
final VideoView mFeedbackVideoView;
final ImageView mFeedbackVideoView;
final ImageView mFakeLauncherView;
final ClipIconView mFakeIconView;
final View mFakeTaskView;
final View mFakePreviousTaskView;
final View mRippleView;
final RippleDrawable mRippleDrawable;
final Button mActionTextButton;
final Button mActionButton;
final TextView mTutorialStepView;
private final Runnable mHideFeedbackRunnable;
Runnable mHideFeedbackEndAction;
private final AlertDialog mSkipTutorialDialog;
TutorialController(TutorialFragment tutorialFragment, TutorialType tutorialType) {
mTutorialFragment = tutorialFragment;
@ -72,9 +82,7 @@ abstract class TutorialController implements BackGestureAttemptCallback,
RootSandboxLayout rootView = tutorialFragment.getRootView();
mCloseButton = rootView.findViewById(R.id.gesture_tutorial_fragment_close_button);
mCloseButton.setOnClickListener(button -> mTutorialFragment.closeTutorial());
mTitleTextView = rootView.findViewById(R.id.gesture_tutorial_fragment_title_view);
mSubtitleTextView = rootView.findViewById(R.id.gesture_tutorial_fragment_subtitle_view);
mCloseButton.setOnClickListener(button -> showSkipTutorialDialog());
mFeedbackView = rootView.findViewById(R.id.gesture_tutorial_fragment_feedback_view);
mFeedbackVideoView = rootView.findViewById(R.id.gesture_tutorial_feedback_video);
mFakeLauncherView = rootView.findViewById(R.id.gesture_tutorial_fake_launcher_view);
@ -84,9 +92,10 @@ abstract class TutorialController implements BackGestureAttemptCallback,
rootView.findViewById(R.id.gesture_tutorial_fake_previous_task_view);
mRippleView = rootView.findViewById(R.id.gesture_tutorial_ripple_view);
mRippleDrawable = (RippleDrawable) mRippleView.getBackground();
mActionTextButton =
rootView.findViewById(R.id.gesture_tutorial_fragment_action_text_button);
mActionButton = rootView.findViewById(R.id.gesture_tutorial_fragment_action_button);
mTutorialStepView =
rootView.findViewById(R.id.gesture_tutorial_fragment_feedback_tutorial_step);
mSkipTutorialDialog = createSkipTutorialDialog();
mHideFeedbackRunnable =
() -> mFeedbackView.animate()
@ -95,30 +104,16 @@ abstract class TutorialController implements BackGestureAttemptCallback,
.withEndAction(this::hideFeedbackEndAction).start();
}
private void showSkipTutorialDialog() {
if (mSkipTutorialDialog != null) {
mSkipTutorialDialog.show();
}
}
void setTutorialType(TutorialType tutorialType) {
mTutorialType = tutorialType;
}
@Nullable
Integer getTitleStringId() {
return null;
}
@Nullable
Integer getSubtitleStringId() {
return null;
}
@Nullable
Integer getActionButtonStringId() {
return null;
}
@Nullable
Integer getActionTextButtonStringId() {
return null;
}
@DrawableRes
protected int getMockLauncherResId() {
return R.drawable.default_sandbox_mock_launcher;
@ -155,13 +150,39 @@ abstract class TutorialController implements BackGestureAttemptCallback,
mFakeTaskView.animate().alpha(0).setListener(AnimationSuccessListener.forRunnable(r));
}
@StringRes
public Integer getIntroductionTitle() {
return null;
}
@StringRes
public Integer getIntroductionSubtitle() {
return null;
}
/**
* Show feedback reflecting a failed gesture attempt.
*
* @param subtitleResId Resource of the text to display.
**/
void showFeedback(int subtitleResId) {
showFeedback(subtitleResId, null);
showFeedback(subtitleResId, false);
}
/**
* Show feedback reflecting a failed gesture attempt.
*
* @param showActionButton Whether the tutorial feedback's action button should be shown.
**/
void showFeedback(int subtitleResId, boolean showActionButton) {
showFeedback(subtitleResId, showActionButton ? () -> {} : null, showActionButton);
}
/**
* Show feedback reflecting a failed gesture attempt.
**/
void showFeedback(int subtitleResId, @Nullable Runnable successEndAction) {
showFeedback(subtitleResId, successEndAction, false);
}
/**
@ -170,32 +191,73 @@ abstract class TutorialController implements BackGestureAttemptCallback,
* @param successEndAction Non-null iff the gesture was successful; this is run after the
* feedback is shown (i.e. to go to the next step)
**/
void showFeedback(int subtitleResId, @Nullable Runnable successEndAction) {
void showFeedback(
int subtitleResId, @Nullable Runnable successEndAction, boolean showActionButton) {
showFeedback(
successEndAction == null
? R.string.gesture_tutorial_try_again
: R.string.gesture_tutorial_nice,
subtitleResId,
successEndAction,
showActionButton);
}
void showFeedback(
int titleResId,
int subtitleResId,
@Nullable Runnable successEndAction,
boolean showActionButton) {
if (mHideFeedbackEndAction != null) {
return;
}
int visibleDuration = FEEDBACK_VISIBLE_MS;
if (mTutorialFragment.getFeedbackVideoResId() != null) {
TextView title = mFeedbackView.findViewById(R.id.gesture_tutorial_fragment_feedback_title);
title.setText(titleResId);
TextView subtitle =
mFeedbackView.findViewById(R.id.gesture_tutorial_fragment_feedback_subtitle);
subtitle.setText(subtitleResId);
if (showActionButton) {
showActionButton();
}
mHideFeedbackEndAction = successEndAction;
AnimatedVectorDrawable tutorialAnimation = mTutorialFragment.getTutorialAnimation();
if (tutorialAnimation != null) {
if (successEndAction == null) {
if (mFeedbackVideoView.isPlaying()) {
mFeedbackVideoView.seekTo(1);
} else {
mFeedbackVideoView.start();
if (tutorialAnimation.isRunning()) {
tutorialAnimation.reset();
}
tutorialAnimation.registerAnimationCallback(new Animatable2.AnimationCallback() {
@Override
public void onAnimationStart(Drawable drawable) {
super.onAnimationStart(drawable);
mFeedbackView.setTranslationY(
-mFeedbackView.getHeight() - mFeedbackView.getTop());
mFeedbackView.setVisibility(View.VISIBLE);
mFeedbackView.animate()
.setDuration(FEEDBACK_ANIMATION_MS)
.translationY(0)
.start();
}
@Override
public void onAnimationEnd(Drawable drawable) {
super.onAnimationEnd(drawable);
mFeedbackView.removeCallbacks(mHideFeedbackRunnable);
mFeedbackView.post(mHideFeedbackRunnable);
tutorialAnimation.unregisterAnimationCallback(this);
}
});
tutorialAnimation.start();
mFeedbackVideoView.setVisibility(View.VISIBLE);
visibleDuration = mTutorialFragment.getFeedbackVideoDuration();
return;
} else {
mTutorialFragment.releaseFeedbackVideoView();
}
}
TextView title = mFeedbackView.findViewById(R.id.gesture_tutorial_fragment_feedback_title);
title.setText(successEndAction == null
? R.string.gesture_tutorial_try_again
: R.string.gesture_tutorial_nice);
TextView subtitle =
mFeedbackView.findViewById(R.id.gesture_tutorial_fragment_feedback_subtitle);
subtitle.setText(subtitleResId);
mHideFeedbackEndAction = successEndAction;
mFeedbackView.setTranslationY(-mFeedbackView.getHeight() - mFeedbackView.getTop());
mFeedbackView.setVisibility(View.VISIBLE);
mFeedbackView.animate()
@ -203,7 +265,9 @@ abstract class TutorialController implements BackGestureAttemptCallback,
.translationY(0)
.start();
mFeedbackView.removeCallbacks(mHideFeedbackRunnable);
mFeedbackView.postDelayed(mHideFeedbackRunnable, visibleDuration);
if (!showActionButton) {
mFeedbackView.postDelayed(mHideFeedbackRunnable, FEEDBACK_VISIBLE_MS);
}
}
void hideFeedback(boolean releaseFeedbackVideo) {
@ -239,16 +303,14 @@ abstract class TutorialController implements BackGestureAttemptCallback,
}
void onActionButtonClicked(View button) {
mTutorialFragment.closeTutorial();
mTutorialFragment.continueTutorial();
}
void onActionTextButtonClicked(View button) {}
@CallSuper
void transitToController() {
hideFeedback(false);
updateTitles();
updateActionButtons();
hideActionButton();
updateSubtext();
updateDrawables();
if (mFakeLauncherView != null) {
@ -256,39 +318,33 @@ abstract class TutorialController implements BackGestureAttemptCallback,
}
}
private void updateTitles() {
updateTitleView(mTitleTextView, getTitleStringId(),
R.style.TextAppearance_GestureTutorial_Title);
updateTitleView(mSubtitleTextView, getSubtitleStringId(),
R.style.TextAppearance_GestureTutorial_Subtitle);
void hideActionButton() {
// Invisible to maintain the layout.
mActionButton.setVisibility(View.INVISIBLE);
mActionButton.setOnClickListener(null);
}
private void updateTitleView(TextView textView, @Nullable Integer stringId, int styleId) {
if (stringId == null) {
textView.setVisibility(View.GONE);
return;
void showActionButton() {
int stringResId = -1;
if (mContext instanceof GestureSandboxActivity) {
GestureSandboxActivity sandboxActivity = (GestureSandboxActivity) mContext;
stringResId = sandboxActivity.isTutorialComplete()
? R.string.gesture_tutorial_action_button_label_done
: R.string.gesture_tutorial_action_button_label_next;
}
textView.setVisibility(View.VISIBLE);
textView.setText(stringId);
textView.setTextAppearance(styleId);
mActionButton.setText(stringResId == -1 ? null : mContext.getString(stringResId));
mActionButton.setVisibility(View.VISIBLE);
mActionButton.setOnClickListener(this::onActionButtonClicked);
}
private void updateActionButtons() {
updateButton(mActionButton, getActionButtonStringId(), this::onActionButtonClicked);
updateButton(
mActionTextButton, getActionTextButtonStringId(), this::onActionTextButtonClicked);
}
private void updateButton(Button button, @Nullable Integer stringId, OnClickListener listener) {
if (stringId == null) {
button.setVisibility(View.INVISIBLE);
return;
}
button.setVisibility(View.VISIBLE);
button.setText(stringId);
button.setOnClickListener(listener);
private void updateSubtext() {
mTutorialStepView.setText(mContext.getString(
R.string.gesture_tutorial_step,
mTutorialFragment.getCurrentStep(),
mTutorialFragment.getNumSteps()));
}
private void updateDrawables() {
@ -309,11 +365,67 @@ abstract class TutorialController implements BackGestureAttemptCallback,
}
}
private boolean isComplete() {
return mTutorialType == TutorialType.BACK_NAVIGATION_COMPLETE
|| mTutorialType == TutorialType.HOME_NAVIGATION_COMPLETE
|| mTutorialType == TutorialType.OVERVIEW_NAVIGATION_COMPLETE
|| mTutorialType == TutorialType.ASSISTANT_COMPLETE;
private AlertDialog createSkipTutorialDialog() {
if (mContext instanceof GestureSandboxActivity) {
GestureSandboxActivity sandboxActivity = (GestureSandboxActivity) mContext;
View contentView = View.inflate(
sandboxActivity, R.layout.gesture_tutorial_dialog, null);
AlertDialog tutorialDialog = new AlertDialog
.Builder(sandboxActivity, R.style.Theme_AppCompat_Dialog_Alert)
.setView(contentView)
.create();
PackageManager packageManager = mContext.getPackageManager();
CharSequence tipsAppName = DEFAULT_PIXEL_TIPS_APP_NAME;
try {
tipsAppName = packageManager.getApplicationLabel(
packageManager.getApplicationInfo(
PIXEL_TIPS_APP_PACKAGE_NAME, PackageManager.GET_META_DATA));
} catch (PackageManager.NameNotFoundException e) {
Log.e(TAG,
"Could not find app label for package name: "
+ PIXEL_TIPS_APP_PACKAGE_NAME
+ ". Defaulting to 'Pixel Tips.'",
e);
}
TextView subtitleTextView = (TextView) contentView.findViewById(
R.id.gesture_tutorial_dialog_subtitle);
if (subtitleTextView != null) {
subtitleTextView.setText(
mContext.getString(R.string.skip_tutorial_dialog_subtitle, tipsAppName));
} else {
Log.w(TAG, "No subtitle view in the skip tutorial dialog to update.");
}
Button cancelButton = (Button) contentView.findViewById(
R.id.gesture_tutorial_dialog_cancel_button);
if (cancelButton != null) {
cancelButton.setOnClickListener(
v -> tutorialDialog.dismiss());
} else {
Log.w(TAG, "No cancel button in the skip tutorial dialog to update.");
}
Button confirmButton = contentView.findViewById(
R.id.gesture_tutorial_dialog_confirm_button);
if (confirmButton != null) {
confirmButton.setOnClickListener(v -> {
sandboxActivity.closeTutorial();
tutorialDialog.dismiss();
});
} else {
Log.w(TAG, "No confirm button in the skip tutorial dialog to update.");
}
tutorialDialog.getWindow().setBackgroundDrawable(
new ColorDrawable(sandboxActivity.getColor(android.R.color.transparent)));
return tutorialDialog;
}
return null;
}
/** Denotes the type of the tutorial. */

View File

@ -16,9 +16,12 @@
package com.android.quickstep.interaction;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.graphics.Insets;
import android.net.Uri;
import android.graphics.drawable.Animatable2;
import android.graphics.drawable.AnimatedVectorDrawable;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
@ -27,7 +30,7 @@ import android.view.View;
import android.view.View.OnTouchListener;
import android.view.ViewGroup;
import android.view.WindowInsets;
import android.widget.VideoView;
import android.widget.ImageView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@ -35,7 +38,6 @@ import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.quickstep.interaction.TutorialController.TutorialType;
abstract class TutorialFragment extends Fragment implements OnTouchListener {
@ -48,8 +50,10 @@ abstract class TutorialFragment extends Fragment implements OnTouchListener {
RootSandboxLayout mRootView;
EdgeBackGestureHandler mEdgeBackGestureHandler;
NavBarGestureHandler mNavBarGestureHandler;
private VideoView mFeedbackVideoView;
private int mFeedbackVideoDuration;
private ImageView mFeedbackVideoView;
@Nullable private AnimatedVectorDrawable mTutorialAnimation = null;
private boolean mIntroductionShown = false;
public static TutorialFragment newInstance(TutorialType tutorialType) {
TutorialFragment fragment = getFragmentForTutorialType(tutorialType);
@ -92,6 +96,11 @@ abstract class TutorialFragment extends Fragment implements OnTouchListener {
return null;
}
@Nullable
AnimatedVectorDrawable getTutorialAnimation() {
return mTutorialAnimation;
}
abstract TutorialController createController(TutorialType type);
abstract Class<? extends TutorialController> getControllerClass();
@ -129,12 +138,6 @@ abstract class TutorialFragment extends Fragment implements OnTouchListener {
return mRootView;
}
@Override
public void onStart() {
super.onStart();
initializeFeedbackVideoView();
}
@Override
public void onStop() {
super.onStop();
@ -143,20 +146,18 @@ abstract class TutorialFragment extends Fragment implements OnTouchListener {
void initializeFeedbackVideoView() {
if (!updateFeedbackVideo()) {
mFeedbackVideoView.setVisibility(View.INVISIBLE);
return;
} else {
mFeedbackVideoView.setVisibility(View.VISIBLE);
}
int heightPixels = getResources().getDisplayMetrics().heightPixels;
int heightPixelsWithMargin = heightPixels + Utilities.dpToPx(80);
int widthPixels = getResources().getDisplayMetrics().widthPixels - Utilities.dpToPx(12);
mFeedbackVideoView.setScaleY((float) heightPixelsWithMargin / heightPixels);
mFeedbackVideoView.setScaleX((float) heightPixelsWithMargin / widthPixels);
mFeedbackVideoView.start();
mFeedbackVideoView.setOnPreparedListener(
mp -> mFeedbackVideoDuration = mFeedbackVideoView.getDuration());
mFeedbackVideoView.setOnCompletionListener(mp -> releaseFeedbackVideoView());
if (!mIntroductionShown && mTutorialController != null) {
Integer introTileStringResId = mTutorialController.getIntroductionTitle();
Integer introSubtitleResId = mTutorialController.getIntroductionSubtitle();
if (introTileStringResId != null && introSubtitleResId != null) {
mTutorialController.showFeedback(introTileStringResId,
introSubtitleResId, null, false);
mIntroductionShown = true;
}
}
}
boolean updateFeedbackVideo() {
@ -164,19 +165,37 @@ abstract class TutorialFragment extends Fragment implements OnTouchListener {
if (feedbackVideoResId == null || getContext() == null) {
return false;
}
Uri uri = Uri.parse("android.resource://" + getContext().getPackageName() + "/"
+ feedbackVideoResId);
mFeedbackVideoView.setVideoURI(uri);
mTutorialAnimation = (AnimatedVectorDrawable) getContext().getDrawable(feedbackVideoResId);
if (mTutorialAnimation != null) {
mTutorialAnimation.registerAnimationCallback(new Animatable2.AnimationCallback() {
@Override
public void onAnimationStart(Drawable drawable) {
super.onAnimationStart(drawable);
mFeedbackVideoView.setVisibility(View.VISIBLE);
}
@Override
public void onAnimationEnd(Drawable drawable) {
super.onAnimationEnd(drawable);
releaseFeedbackVideoView();
}
});
}
mFeedbackVideoView.setImageDrawable(mTutorialAnimation);
return true;
}
void releaseFeedbackVideoView() {
mFeedbackVideoView.stopPlayback();
mFeedbackVideoView.setVisibility(View.INVISIBLE);
}
if (mTutorialAnimation != null && mTutorialAnimation.isRunning()) {
mTutorialAnimation.stop();
}
int getFeedbackVideoDuration() {
return mFeedbackVideoDuration;
mFeedbackVideoView.setVisibility(View.GONE);
}
@Override
@ -217,6 +236,7 @@ abstract class TutorialFragment extends Fragment implements OnTouchListener {
mEdgeBackGestureHandler.registerBackGestureAttemptCallback(mTutorialController);
mNavBarGestureHandler.registerNavBarGestureAttemptCallback(mTutorialController);
mTutorialType = tutorialType;
initializeFeedbackVideoView();
}
@Override
@ -230,11 +250,7 @@ abstract class TutorialFragment extends Fragment implements OnTouchListener {
}
void continueTutorial() {
if (!(getContext() instanceof GestureSandboxActivity)) {
closeTutorial();
return;
}
GestureSandboxActivity gestureSandboxActivity = (GestureSandboxActivity) getContext();
GestureSandboxActivity gestureSandboxActivity = getGestureSandboxActivity();
if (gestureSandboxActivity == null) {
closeTutorial();
@ -255,12 +271,22 @@ abstract class TutorialFragment extends Fragment implements OnTouchListener {
startActivity(new Intent("com.android.settings.GESTURE_NAVIGATION_SETTINGS"));
}
boolean isTutorialComplete() {
if (!(getContext() instanceof GestureSandboxActivity)) {
return true;
}
GestureSandboxActivity gestureSandboxActivity = (GestureSandboxActivity) getContext();
int getCurrentStep() {
GestureSandboxActivity gestureSandboxActivity = getGestureSandboxActivity();
return gestureSandboxActivity == null || gestureSandboxActivity.isTutorialComplete();
return gestureSandboxActivity == null ? -1 : gestureSandboxActivity.getCurrentStep();
}
int getNumSteps() {
GestureSandboxActivity gestureSandboxActivity = getGestureSandboxActivity();
return gestureSandboxActivity == null ? -1 : gestureSandboxActivity.getNumSteps();
}
@Nullable
private GestureSandboxActivity getGestureSandboxActivity() {
Context context = getContext();
return context instanceof GestureSandboxActivity ? (GestureSandboxActivity) context : null;
}
}

View File

@ -38,8 +38,8 @@
<color name="gesture_tutorial_ripple_color">#A0C2F9</color> <!-- Light Blue -->
<color name="gesture_tutorial_fake_task_view_color">#6DA1FF</color> <!-- Light Blue -->
<color name="gesture_tutorial_fake_previous_task_view_color">#3C4043</color> <!-- Gray -->
<color name="gesture_tutorial_action_button_label_color">#FFFFFFFF</color>
<color name="gesture_tutorial_primary_color">#1A73E8</color> <!-- Blue -->
<color name="gesture_tutorial_action_button_label_color">#FF000000</color>
<color name="gesture_tutorial_primary_color">#B7F29F</color> <!-- Light Green -->
<color name="popup_color_primary_light">#FFF</color>
<color name="popup_color_secondary_light">#F1F3F4</color>

View File

@ -269,6 +269,20 @@ public class DeveloperOptionsFragment extends PreferenceFragmentCompat {
}
PreferenceCategory sandboxCategory = newCategory("Gesture Navigation Sandbox");
sandboxCategory.setSummary("Learn and practice navigation gestures");
Preference launchOnboardingTutorialPreference = new Preference(context);
launchOnboardingTutorialPreference.setKey("launchOnboardingTutorial");
launchOnboardingTutorialPreference.setTitle("Launch Onboarding Tutorial");
launchOnboardingTutorialPreference.setSummary("Learn the basic navigation gestures.");
launchOnboardingTutorialPreference.setOnPreferenceClickListener(preference -> {
startActivity(launchSandboxIntent.putExtra(
"tutorial_steps",
new String[] {
"LEFT_EDGE_BACK_NAVIGATION",
"HOME_NAVIGATION",
"OVERVIEW_NAVIGATION"}));
return true;
});
sandboxCategory.addPreference(launchOnboardingTutorialPreference);
Preference launchBackTutorialPreference = new Preference(context);
launchBackTutorialPreference.setKey("launchBackTutorial");
launchBackTutorialPreference.setTitle("Launch Back Tutorial");