Merge "Complete gesture nav sandbox for for UXR." into sc-dev
This commit is contained in:
commit
5a6e10bba8
|
@ -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>
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
|
@ -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
|
@ -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
|
@ -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>
|
|
@ -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.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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. */
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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");
|
||||
|
|
Loading…
Reference in New Issue