Add the gesture nav tutorial menu page

- Added the gesture tutorial menu page (launched using an intent extra)
- tutorial steps now launch the menu when complete if launched from the menu
- the new default set of tutorial steps is home -> back -> overview. this is to handle the case where an intent is launched that is meant to launch the tutorial menu, but ENABLE_NEW_GESTURE_NAV_TUTORIAL is false

Flag: ENABLE_NEW_GESTURE_NAV_TUTORIAL
Bug: 274463555
Test: Ran the tutorial and menu on a large screen, foldable and phone
Change-Id: I2eb5f658115be4d5ecb0233a8f09d22efe6ebadc
This commit is contained in:
Schneider Victor-tulias 2023-03-16 15:01:15 -07:00
parent afdf2f1020
commit 2851be8cba
32 changed files with 963 additions and 143 deletions

View File

@ -0,0 +1,23 @@
<!-- Copyright (C) 2023 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="84dp"
android:height="208dp"
android:viewportWidth="84"
android:viewportHeight="208">
<path
android:pathData="M24.53,169.2L32.09,165.56C77.7,143.55 77.7,64.45 32.09,42.35L24.53,38.71C14.55,33.95 6.06,25.56 0,14.92V193.08C6.06,182.44 14.55,174.05 24.53,169.2Z"
android:fillColor="#217500"/>
</vector>

View File

@ -0,0 +1,27 @@
<!-- Copyright (C) 2023 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="232dp"
android:height="67dp"
android:viewportWidth="232"
android:viewportHeight="67">
<group>
<clip-path
android:pathData="M0,0h232v67h-232z"/>
<path
android:pathData="M180.9,0.6H51.1C22.9,0.6 0,23.4 0,51.7V67.6H232V51.7C232,23.4 209.1,0.6 180.9,0.6Z"
android:fillColor="#4B67AE"/>
</group>
</vector>

View File

@ -0,0 +1,27 @@
<!-- Copyright (C) 2023 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="194dp"
android:height="94dp"
android:viewportWidth="194"
android:viewportHeight="94">
<group>
<clip-path
android:pathData="M0,0h194v94.09h-194z"/>
<path
android:pathData="M185.56,76.95C184.79,75.3 184.3,73.56 184.21,71.81L182.85,55.81C182.46,51.34 180.13,47.27 176.45,44.65L163.25,35.44C161.8,34.37 160.54,33.11 159.47,31.65L150.16,18.46C147.54,14.77 143.47,12.45 139,12.06L123,10.6C121.25,10.41 119.51,10.02 117.86,9.24L103.31,2.45C101.27,1.49 99.04,1 96.91,1C94.77,1 92.54,1.49 90.5,2.45L75.95,9.24C74.31,10.02 72.56,10.51 70.81,10.6L54.81,11.96C50.35,12.35 46.27,14.68 43.65,18.36L34.44,31.56C33.37,33.01 32.11,34.27 30.66,35.34L17.27,44.65C13.58,47.27 11.26,51.34 10.87,55.81L9.41,71.81C9.22,73.56 8.83,75.3 8.05,76.95L1.26,91.5C0.78,92.67 0.39,93.83 0.1,94.99H193.52C193.32,93.83 192.94,92.57 192.35,91.5L185.56,76.95Z"
android:fillColor="#7E44AD"/>
</group>
</vector>

View File

@ -0,0 +1,23 @@
<!-- Copyright (C) 2023 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="122dp"
android:height="303dp"
android:viewportWidth="122"
android:viewportHeight="303">
<path
android:pathData="M35.65,245.9L46.63,240.61C112.92,208.62 112.92,93.67 46.63,61.54L35.65,56.26C21.15,49.34 8.81,37.14 0,21.69V280.6C8.81,265.15 21.15,252.95 35.65,245.9Z"
android:fillColor="#217500"/>
</vector>

View File

@ -0,0 +1,27 @@
<!-- Copyright (C) 2023 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="362dp"
android:height="73dp"
android:viewportWidth="362"
android:viewportHeight="73">
<group>
<clip-path
android:pathData="M0,0h362v73h-362z"/>
<path
android:pathData="M282.3,0H79.7C38,0 3.7,32.1 0.3,73H361.7C358.3,32.1 324,0 282.3,0Z"
android:fillColor="#4B67AE"/>
</group>
</vector>

View File

@ -0,0 +1,27 @@
<!-- Copyright (C) 2023 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="297dp"
android:height="144dp"
android:viewportWidth="297"
android:viewportHeight="144">
<group>
<clip-path
android:pathData="M0,0h297v144.04h-297z"/>
<path
android:pathData="M284.38,116.48C283.19,113.95 282.45,111.28 282.3,108.61L280.22,84.1C279.63,77.27 276.06,71.03 270.42,67.03L250.22,52.92C247.99,51.28 246.06,49.35 244.43,47.13L230.18,26.93C226.16,21.29 219.93,17.72 213.1,17.13L188.6,14.9C185.92,14.6 183.25,14.01 180.72,12.82L158.45,2.43C155.33,0.94 151.91,0.2 148.65,0.2C145.38,0.2 141.97,0.94 138.85,2.43L116.57,12.82C114.05,14.01 111.38,14.75 108.7,14.9L84.2,16.98C77.37,17.57 71.13,21.14 67.12,26.78L53.01,46.98C51.38,49.21 49.45,51.14 47.22,52.77L26.73,67.03C21.09,71.03 17.52,77.27 16.93,84.1L14.7,108.61C14.4,111.28 13.81,113.95 12.62,116.48L2.23,138.75C1.48,140.53 0.89,142.32 0.45,144.1H296.55C296.26,142.32 295.66,140.38 294.77,138.75L284.38,116.48Z"
android:fillColor="#7E44AD"/>
</group>
</vector>

View File

@ -0,0 +1,27 @@
<!-- Copyright (C) 2023 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="83dp"
android:height="208dp"
android:viewportWidth="83"
android:viewportHeight="208">
<group>
<clip-path
android:pathData="M0,0h83.95v208h-83.95z"/>
<path
android:pathData="M23.53,169.2L31.09,165.56C76.7,143.55 76.7,64.45 31.09,42.35L23.53,38.71C13.55,33.95 5.06,25.56 -1,14.92V193.08C5.06,182.44 13.55,174.05 23.53,169.2Z"
android:fillColor="#217500"/>
</group>
</vector>

View File

@ -0,0 +1,23 @@
<!-- Copyright (C) 2023 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="364dp"
android:height="66dp"
android:viewportWidth="364"
android:viewportHeight="66">
<path
android:pathData="M283.8,0H80.2C40.7,0 8,28.5 1.3,66H362.8C356,28.5 323.3,0 283.8,0Z"
android:fillColor="#4B67AE"/>
</vector>

View File

@ -0,0 +1,19 @@
<!-- Copyright (C) 2023 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="@dimen/gesture_tutorial_menu_button_radius" />
</shape>

View File

@ -0,0 +1,23 @@
<!-- Copyright (C) 2023 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="200dp"
android:height="76dp"
android:viewportWidth="200"
android:viewportHeight="76">
<path
android:pathData="M188.6,56.5C188.2,51.9 185.8,47.7 182,45L168.4,35.5C166.9,34.4 165.6,33.1 164.5,31.6L155,18C152.3,14.2 148.1,11.8 143.5,11.4L127,9.9C125.2,9.7 123.4,9.3 121.7,8.5L106.7,1.5C104.6,0.5 102.3,0 100.1,0C97.8,0 95.6,0.5 93.5,1.5L78.5,8.5C76.8,9.3 75,9.8 73.2,9.9L56.7,11.3C52.1,11.7 47.9,14.1 45.2,17.9L35.7,31.5C34.6,33 33.3,34.3 31.8,35.4L18,45C14.2,47.7 11.8,51.9 11.4,56.5L9.9,73C9.8,74 9.6,75 9.3,76H190.6C190.3,75 190.1,74 190,73L188.6,56.5Z"
android:fillColor="#7E44AD"/>
</vector>

View File

@ -0,0 +1,157 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2023 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.
-->
<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="match_parent"
android:paddingTop="@dimen/gesture_tutorial_menu_padding_top"
android:paddingBottom="@dimen/gesture_tutorial_menu_padding_bottom"
android:paddingHorizontal="@dimen/gesture_tutorial_menu_padding_horizontal"
android:background="@color/gesture_tutorial_menu_background">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/gesture_tutorial_menu_home_button"
android:layout_width="0dp"
android:layout_height="@dimen/gesture_tutorial_menu_button_height"
android:layout_marginEnd="@dimen/gesture_tutorial_menu_button_spacing"
android:background="@drawable/gesture_tutorial_menu_button_background"
android:clipToOutline="true"
android:backgroundTint="@color/gesture_home_tutorial_background"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toStartOf="@id/gesture_tutorial_menu_back_button">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/gesture_tutorial_home_step_shape"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"/>
<TextView
style="@style/TextAppearance.GestureTutorial.MenuButton"
android:id="@+id/gesture_tutorial_menu_home_button_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/home_gesture_tutorial_title"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/gesture_tutorial_menu_back_button"
android:layout_width="0dp"
android:layout_height="@dimen/gesture_tutorial_menu_button_height"
android:layout_marginEnd="@dimen/gesture_tutorial_menu_button_spacing"
android:background="@drawable/gesture_tutorial_menu_button_background"
android:clipToOutline="true"
android:backgroundTint="@color/gesture_back_tutorial_exiting_app"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toEndOf="@id/gesture_tutorial_menu_home_button"
app:layout_constraintEnd_toStartOf="@id/gesture_tutorial_menu_overview_button">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/gesture_tutorial_back_step_shape"
android:layout_marginBottom="@dimen/gesture_tutorial_menu_back_shape_bottom_margin"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"/>
<TextView
style="@style/TextAppearance.GestureTutorial.MenuButton"
android:id="@+id/gesture_tutorial_menu_back_button_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/back_gesture_tutorial_title"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/gesture_tutorial_menu_overview_button"
android:layout_width="0dp"
android:layout_height="@dimen/gesture_tutorial_menu_button_height"
android:background="@drawable/gesture_tutorial_menu_button_background"
android:clipToOutline="true"
android:backgroundTint="@color/gesture_overview_tutorial_background"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toEndOf="@id/gesture_tutorial_menu_back_button"
app:layout_constraintEnd_toEndOf="parent">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/gesture_tutorial_overview_step_shape"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"/>
<TextView
style="@style/TextAppearance.GestureTutorial.MenuButton"
android:id="@+id/gesture_tutorial_menu_overview_button_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/overview_gesture_tutorial_title"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_end="@dimen/gesture_tutorial_menu_done_button_spacing"/>
<Button
style="@style/TextAppearance.GestureTutorial.ButtonLabel"
android:id="@+id/gesture_tutorial_menu_done_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingVertical="16dp"
android:paddingHorizontal="26dp"
android:layout_marginVertical="@dimen/gesture_tutorial_menu_done_button_margin"
android:text="@string/gesture_tutorial_action_button_label"
android:background="@drawable/gesture_tutorial_action_button_background"
android:stateListAnimator="@null"
app:layout_constraintTop_toBottomOf="@id/guideline"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,161 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2023 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.
-->
<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="match_parent"
android:paddingTop="@dimen/gesture_tutorial_menu_padding_top"
android:paddingBottom="@dimen/gesture_tutorial_menu_padding_bottom"
android:paddingHorizontal="@dimen/gesture_tutorial_menu_padding_horizontal"
android:background="@color/gesture_tutorial_menu_background"
android:clipToPadding="false">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/gesture_tutorial_menu_home_button"
android:layout_width="match_parent"
android:layout_height="@dimen/gesture_tutorial_menu_button_height"
android:background="@drawable/gesture_tutorial_menu_button_background"
android:clipToOutline="true"
android:backgroundTint="@color/gesture_home_tutorial_background"
app:layout_constraintVertical_chainStyle="packed"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="@id/gesture_tutorial_menu_back_button"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/gesture_tutorial_home_step_shape"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"/>
<TextView
style="@style/TextAppearance.GestureTutorial.MenuButton"
android:id="@+id/gesture_tutorial_menu_home_button_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/home_gesture_tutorial_title"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/gesture_tutorial_menu_back_button"
android:layout_width="match_parent"
android:layout_height="@dimen/gesture_tutorial_menu_button_height"
android:layout_marginTop="@dimen/gesture_tutorial_menu_button_spacing"
android:background="@drawable/gesture_tutorial_menu_button_background"
android:clipToOutline="true"
android:backgroundTint="@color/gesture_back_tutorial_exiting_app"
app:layout_constraintTop_toBottomOf="@id/gesture_tutorial_menu_home_button"
app:layout_constraintBottom_toTopOf="@id/gesture_tutorial_menu_overview_button"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/gesture_tutorial_back_step_shape"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"/>
<TextView
style="@style/TextAppearance.GestureTutorial.MenuButton"
android:id="@+id/gesture_tutorial_menu_back_button_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/back_gesture_tutorial_title"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/gesture_tutorial_menu_overview_button"
android:layout_width="match_parent"
android:layout_height="@dimen/gesture_tutorial_menu_button_height"
android:layout_marginTop="@dimen/gesture_tutorial_menu_button_spacing"
android:background="@drawable/gesture_tutorial_menu_button_background"
android:clipToOutline="true"
android:backgroundTint="@color/gesture_overview_tutorial_background"
app:layout_constraintTop_toBottomOf="@id/gesture_tutorial_menu_back_button"
app:layout_constraintBottom_toTopOf="@id/guideline"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/gesture_tutorial_overview_step_shape"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"/>
<TextView
style="@style/TextAppearance.GestureTutorial.MenuButton"
android:id="@+id/gesture_tutorial_menu_overview_button_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/overview_gesture_tutorial_title"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_end="@dimen/gesture_tutorial_menu_done_button_spacing"/>
<Button
style="@style/TextAppearance.GestureTutorial.ButtonLabel"
android:id="@+id/gesture_tutorial_menu_done_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingVertical="16dp"
android:paddingHorizontal="26dp"
android:text="@string/gesture_tutorial_action_button_label"
android:background="@drawable/gesture_tutorial_action_button_background"
android:stateListAnimator="@null"
app:layout_constraintTop_toBottomOf="@id/guideline"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -18,4 +18,14 @@
<!-- All Set page -->
<dimen name="allset_page_margin_horizontal">48dp</dimen>
<!-- Gesture Tutorial menu page -->
<dimen name="gesture_tutorial_menu_padding_horizontal">48dp</dimen>
<dimen name="gesture_tutorial_menu_padding_top">32dp</dimen>
<dimen name="gesture_tutorial_menu_padding_bottom">16dp</dimen>
<dimen name="gesture_tutorial_menu_button_height">491dp</dimen>
<dimen name="gesture_tutorial_menu_button_spacing">24dp</dimen>
<dimen name="gesture_tutorial_menu_done_button_top_spacing">40dp</dimen>
<dimen name="gesture_tutorial_menu_back_shape_bottom_margin">49dp</dimen>
<dimen name="gesture_tutorial_menu_done_button_margin">16dp</dimen>
</resources>

View File

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?><!--
* Copyright (c) 2023, 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.
*/
-->
<resources>
<!-- Gesture Tutorial menu page -->
<dimen name="gesture_tutorial_menu_button_height">580dp</dimen>
<dimen name="gesture_tutorial_menu_button_spacing">49dp</dimen>
<dimen name="gesture_tutorial_menu_done_button_top_spacing">24dp</dimen>
<dimen name="gesture_tutorial_menu_back_shape_bottom_margin">21dp</dimen>
<dimen name="gesture_tutorial_menu_done_button_spacing">80dp</dimen>
<dimen name="gesture_tutorial_menu_done_button_margin">0dp</dimen>
</resources>

View File

@ -43,13 +43,14 @@
<color name="gesture_tutorial_taskbar_color">#E8EAED</color>
<!-- Redesigned gesture navigation tutorial -->
<color name="gesture_home_tutorial_background">#FFB399</color>
<color name="gesture_home_tutorial_background">#FFAD91</color>
<color name="gesture_home_tutorial_swipe_up_rect">#3857C7</color>
<color name="gesture_back_tutorial_exiting_app">#F3A5B9</color>
<color name="gesture_back_tutorial_exiting_app">#F9A2B9</color>
<color name="gesture_back_tutorial_background">#3857C7</color>
<color name="gesture_overview_tutorial_background">#E2F3AF</color>
<color name="gesture_overview_tutorial_background">#DFF3AF</color>
<color name="gesture_overview_tutorial_swipe_rect">#7E44AD</color>
<color name="gesture_overview_background">#BFC8CB</color>
<color name="gesture_tutorial_menu_background">#1C1B1F</color>
<!-- Mock hotseat -->
<color name="mock_app_icon">#BDC1C6</color>

View File

@ -124,6 +124,18 @@
<dimen name="gesture_tutorial_back_gesture_exiting_app_margin">8dp</dimen>
<dimen name="gesture_tutorial_back_gesture_end_corner_radius">36dp</dimen>
<!-- Gesture Tutorial menu page -->
<dimen name="gesture_tutorial_menu_padding_horizontal">24dp</dimen>
<dimen name="gesture_tutorial_menu_padding_top">31dp</dimen>
<dimen name="gesture_tutorial_menu_padding_bottom">24dp</dimen>
<dimen name="gesture_tutorial_menu_button_height">0dp</dimen>
<dimen name="gesture_tutorial_menu_button_spacing">16dp</dimen>
<dimen name="gesture_tutorial_menu_button_radius">28dp</dimen>
<dimen name="gesture_tutorial_menu_done_button_top_spacing">0dp</dimen>
<dimen name="gesture_tutorial_menu_back_shape_bottom_margin">0dp</dimen>
<dimen name="gesture_tutorial_menu_done_button_spacing">72dp</dimen>
<dimen name="gesture_tutorial_menu_done_button_margin">0dp</dimen>
<!-- Gesture Tutorial mock conversations -->
<dimen name="gesture_tutorial_message_icon_size">44dp</dimen>
<dimen name="gesture_tutorial_message_icon_corner_radius">100dp</dimen>

View File

@ -47,6 +47,13 @@
<item name="android:lineHeight">44sp</item>
</style>
<style name="TextAppearance.GestureTutorial.MenuButton"
parent="TextAppearance.GestureTutorial.Feedback.Title">
<item name="android:gravity">center</item>
<item name="android:textColor">?android:attr/textColorPrimaryInverse</item>
<item name="fontWeight">400</item>
</style>
<style name="TextAppearance.GestureTutorial.Feedback.Title.AllSet"
parent="TextAppearance.GestureTutorial.Feedback.Title">
<item name="android:letterSpacing">0.03</item>

View File

@ -294,17 +294,27 @@ public class DeveloperOptionsFragment extends PreferenceFragmentCompat {
}
PreferenceCategory sandboxCategory = newCategory("Gesture Navigation Sandbox");
sandboxCategory.setSummary("Learn and practice navigation gestures");
Preference launchTutorialStepMenuPreference = new Preference(context);
launchTutorialStepMenuPreference.setKey("launchTutorialStepMenu");
launchTutorialStepMenuPreference.setTitle("Launch Gesture Tutorial Steps menu");
launchTutorialStepMenuPreference.setSummary("Select a gesture tutorial step.");
launchTutorialStepMenuPreference.setOnPreferenceClickListener(preference -> {
startActivity(launchSandboxIntent.putExtra("use_tutorial_menu", true));
return true;
});
sandboxCategory.addPreference(launchTutorialStepMenuPreference);
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[] {
"HOME_NAVIGATION",
"BACK_NAVIGATION",
"OVERVIEW_NAVIGATION"}));
startActivity(launchSandboxIntent
.putExtra("use_tutorial_menu", false)
.putExtra("tutorial_steps",
new String[] {
"HOME_NAVIGATION",
"BACK_NAVIGATION",
"OVERVIEW_NAVIGATION"}));
return true;
});
sandboxCategory.addPreference(launchOnboardingTutorialPreference);
@ -313,9 +323,9 @@ public class DeveloperOptionsFragment extends PreferenceFragmentCompat {
launchBackTutorialPreference.setTitle("Launch Back Tutorial");
launchBackTutorialPreference.setSummary("Learn how to use the Back gesture");
launchBackTutorialPreference.setOnPreferenceClickListener(preference -> {
startActivity(launchSandboxIntent.putExtra(
"tutorial_steps",
new String[] {"BACK_NAVIGATION"}));
startActivity(launchSandboxIntent
.putExtra("use_tutorial_menu", false)
.putExtra("tutorial_steps", new String[] {"BACK_NAVIGATION"}));
return true;
});
sandboxCategory.addPreference(launchBackTutorialPreference);
@ -324,9 +334,9 @@ public class DeveloperOptionsFragment extends PreferenceFragmentCompat {
launchHomeTutorialPreference.setTitle("Launch Home Tutorial");
launchHomeTutorialPreference.setSummary("Learn how to use the Home gesture");
launchHomeTutorialPreference.setOnPreferenceClickListener(preference -> {
startActivity(launchSandboxIntent.putExtra(
"tutorial_steps",
new String[] {"HOME_NAVIGATION"}));
startActivity(launchSandboxIntent
.putExtra("use_tutorial_menu", false)
.putExtra("tutorial_steps", new String[] {"HOME_NAVIGATION"}));
return true;
});
sandboxCategory.addPreference(launchHomeTutorialPreference);
@ -335,9 +345,9 @@ public class DeveloperOptionsFragment extends PreferenceFragmentCompat {
launchOverviewTutorialPreference.setTitle("Launch Overview Tutorial");
launchOverviewTutorialPreference.setSummary("Learn how to use the Overview gesture");
launchOverviewTutorialPreference.setOnPreferenceClickListener(preference -> {
startActivity(launchSandboxIntent.putExtra(
"tutorial_steps",
new String[] {"OVERVIEW_NAVIGATION"}));
startActivity(launchSandboxIntent
.putExtra("use_tutorial_menu", false)
.putExtra("tutorial_steps", new String[] {"OVERVIEW_NAVIGATION"}));
return true;
});
sandboxCategory.addPreference(launchOverviewTutorialPreference);
@ -346,9 +356,9 @@ public class DeveloperOptionsFragment extends PreferenceFragmentCompat {
launchAssistantTutorialPreference.setTitle("Launch Assistant Tutorial");
launchAssistantTutorialPreference.setSummary("Learn how to use the Assistant gesture");
launchAssistantTutorialPreference.setOnPreferenceClickListener(preference -> {
startActivity(launchSandboxIntent.putExtra(
"tutorial_steps",
new String[] {"ASSISTANT"}));
startActivity(launchSandboxIntent
.putExtra("use_tutorial_menu", false)
.putExtra("tutorial_steps", new String[] {"ASSISTANT"}));
return true;
});
sandboxCategory.addPreference(launchAssistantTutorialPreference);
@ -357,9 +367,9 @@ public class DeveloperOptionsFragment extends PreferenceFragmentCompat {
launchSandboxModeTutorialPreference.setTitle("Launch Sandbox Mode");
launchSandboxModeTutorialPreference.setSummary("Practice navigation gestures");
launchSandboxModeTutorialPreference.setOnPreferenceClickListener(preference -> {
startActivity(launchSandboxIntent.putExtra(
"tutorial_steps",
new String[] {"SANDBOX_MODE"}));
startActivity(launchSandboxIntent
.putExtra("use_tutorial_menu", false)
.putExtra("tutorial_steps", new String[] {"SANDBOX_MODE"}));
return true;
});
sandboxCategory.addPreference(launchSandboxModeTutorialPreference);

View File

@ -15,8 +15,6 @@
*/
package com.android.quickstep.interaction;
import static com.android.quickstep.interaction.TutorialController.TutorialType.ASSISTANT_COMPLETE;
import android.graphics.PointF;
import com.android.launcher3.R;
@ -47,7 +45,7 @@ final class AssistantGestureTutorialController extends TutorialController {
case ASSISTANT_COMPLETE:
if (result == BackGestureResult.BACK_COMPLETED_FROM_LEFT
|| result == BackGestureResult.BACK_COMPLETED_FROM_RIGHT) {
mTutorialFragment.closeTutorial();
mTutorialFragment.close();
}
break;
}
@ -81,7 +79,7 @@ final class AssistantGestureTutorialController extends TutorialController {
break;
case ASSISTANT_COMPLETE:
if (result == NavBarGestureResult.HOME_GESTURE_COMPLETED) {
mTutorialFragment.closeTutorial();
mTutorialFragment.close();
}
break;
}

View File

@ -26,7 +26,9 @@ import com.android.quickstep.interaction.TutorialController.TutorialType;
/** Shows the Home gesture interactive tutorial. */
public class AssistantGestureTutorialFragment extends TutorialFragment {
public AssistantGestureTutorialFragment() {}
public AssistantGestureTutorialFragment(boolean fromTutorialMenu) {
super(fromTutorialMenu);
}
@Override
TutorialController createController(TutorialType type) {

View File

@ -109,7 +109,7 @@ final class BackGestureTutorialController extends TutorialController {
case BACK_NAVIGATION_COMPLETE:
if (result == BackGestureResult.BACK_COMPLETED_FROM_LEFT
|| result == BackGestureResult.BACK_COMPLETED_FROM_RIGHT) {
mTutorialFragment.closeTutorial();
mTutorialFragment.close();
}
break;
}
@ -191,7 +191,7 @@ final class BackGestureTutorialController extends TutorialController {
}
if (mTutorialType == BACK_NAVIGATION_COMPLETE) {
if (result == NavBarGestureResult.HOME_GESTURE_COMPLETED) {
mTutorialFragment.closeTutorial();
mTutorialFragment.close();
}
} else if (mTutorialType == BACK_NAVIGATION) {
switch (result) {

View File

@ -34,7 +34,13 @@ import java.util.ArrayList;
/** Shows the Back gesture interactive tutorial. */
public class BackGestureTutorialFragment extends TutorialFragment {
public BackGestureTutorialFragment() {}
public BackGestureTutorialFragment() {
this(false);
}
public BackGestureTutorialFragment(boolean fromTutorialMenu) {
super(fromTutorialMenu);
}
@Nullable
@Override

View File

@ -26,10 +26,12 @@ import android.view.View;
import android.view.Window;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.FragmentActivity;
import com.android.launcher3.LauncherPrefs;
import com.android.launcher3.R;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.logging.StatsLogManager;
import com.android.quickstep.TouchInteractionService.TISBinder;
import com.android.quickstep.interaction.TutorialController.TutorialType;
@ -42,11 +44,12 @@ public class GestureSandboxActivity extends FragmentActivity {
private static final String KEY_TUTORIAL_STEPS = "tutorial_steps";
private static final String KEY_CURRENT_STEP = "current_step";
private static final String KEY_GESTURE_COMPLETE = "gesture_complete";
static final String KEY_TUTORIAL_TYPE = "tutorial_type";
static final String KEY_GESTURE_COMPLETE = "gesture_complete";
static final String KEY_USE_TUTORIAL_MENU = "use_tutorial_menu";
private TutorialType[] mTutorialSteps;
private TutorialType mCurrentTutorialStep;
private TutorialFragment mFragment;
@Nullable private TutorialType[] mTutorialSteps;
private GestureSandboxFragment mFragment;
private int mCurrentStep;
private int mNumSteps;
@ -67,10 +70,26 @@ public class GestureSandboxActivity extends FragmentActivity {
mStatsLogManager = StatsLogManager.newInstance(getApplicationContext());
Bundle args = savedInstanceState == null ? getIntent().getExtras() : savedInstanceState;
mTutorialSteps = getTutorialSteps(args);
mCurrentTutorialStep = mTutorialSteps[mCurrentStep - 1];
mFragment = TutorialFragment.newInstance(
mCurrentTutorialStep, args.getBoolean(KEY_GESTURE_COMPLETE, false));
boolean gestureComplete = args != null && args.getBoolean(KEY_GESTURE_COMPLETE, false);
if (FeatureFlags.ENABLE_NEW_GESTURE_NAV_TUTORIAL.get()
&& args != null
&& args.getBoolean(KEY_USE_TUTORIAL_MENU, false)) {
mTutorialSteps = null;
TutorialType tutorialTypeOverride = (TutorialType) args.get(KEY_TUTORIAL_TYPE);
mFragment = tutorialTypeOverride == null
? new MenuFragment()
: makeTutorialFragment(
tutorialTypeOverride,
gestureComplete,
/* fromMenu= */ true);
} else {
mTutorialSteps = getTutorialSteps(args);
mFragment = makeTutorialFragment(
mTutorialSteps[mCurrentStep - 1],
gestureComplete,
/* fromMenu= */ false);
}
getSupportFragmentManager().beginTransaction()
.add(R.id.gesture_tutorial_fragment_container, mFragment)
.commit();
@ -81,7 +100,9 @@ public class GestureSandboxActivity extends FragmentActivity {
@Override
public void onAttachedToWindow() {
super.onAttachedToWindow();
disableSystemGestures();
if (mFragment.shouldDisableSystemGestures()) {
disableSystemGestures();
}
mFragment.onAttachedToWindow();
}
@ -103,7 +124,7 @@ public class GestureSandboxActivity extends FragmentActivity {
protected void onSaveInstanceState(@NonNull Bundle savedInstanceState) {
savedInstanceState.putStringArray(KEY_TUTORIAL_STEPS, getTutorialStepNames());
savedInstanceState.putInt(KEY_CURRENT_STEP, mCurrentStep);
savedInstanceState.putBoolean(KEY_GESTURE_COMPLETE, mFragment.isGestureComplete());
mFragment.onSaveInstanceState(savedInstanceState);
super.onSaveInstanceState(savedInstanceState);
}
@ -134,21 +155,45 @@ public class GestureSandboxActivity extends FragmentActivity {
* If there is no following step, the tutorial is closed.
*/
public void continueTutorial() {
if (isTutorialComplete()) {
mFragment.closeTutorial();
if (isTutorialComplete() || mTutorialSteps == null) {
mFragment.close();
return;
}
mCurrentTutorialStep = mTutorialSteps[mCurrentStep];
mFragment = TutorialFragment.newInstance(
mCurrentTutorialStep, /* gestureComplete= */ false);
launchTutorialStep(mTutorialSteps[mCurrentStep], false);
mCurrentStep++;
}
private TutorialFragment makeTutorialFragment(
@NonNull TutorialType tutorialType, boolean gestureComplete, boolean fromMenu) {
return TutorialFragment.newInstance(tutorialType, gestureComplete, fromMenu);
}
/**
* Launches the given gesture nav tutorial step.
*
* If the step is being launched from the gesture nav tutorial menu, then that step will launch
* the menu when complete.
*/
public void launchTutorialStep(@NonNull TutorialType tutorialType, boolean fromMenu) {
mFragment = makeTutorialFragment(tutorialType, false, fromMenu);
getSupportFragmentManager().beginTransaction()
.replace(R.id.gesture_tutorial_fragment_container, mFragment)
.runOnCommit(() -> mFragment.onAttachedToWindow())
.commit();
mCurrentStep++;
}
/** Launches the gesture nav tutorial menu page */
public void launchTutorialMenu() {
mFragment = new MenuFragment();
getSupportFragmentManager().beginTransaction()
.add(R.id.gesture_tutorial_fragment_container, mFragment)
.commit();
}
private String[] getTutorialStepNames() {
if (mTutorialSteps == null) {
return new String[0];
}
String[] tutorialStepNames = new String[mTutorialSteps.length];
int i = 0;
@ -160,18 +205,19 @@ public class GestureSandboxActivity extends FragmentActivity {
}
private TutorialType[] getTutorialSteps(Bundle extras) {
TutorialType[] defaultSteps = new TutorialType[] {TutorialType.BACK_NAVIGATION};
TutorialType[] defaultSteps = new TutorialType[] {
TutorialType.HOME_NAVIGATION,
TutorialType.BACK_NAVIGATION,
TutorialType.OVERVIEW_NAVIGATION};
mCurrentStep = 1;
mNumSteps = 1;
mNumSteps = defaultSteps.length;
if (extras == null || !extras.containsKey(KEY_TUTORIAL_STEPS)) {
return defaultSteps;
}
Object savedSteps = extras.get(KEY_TUTORIAL_STEPS);
int currentStep = extras.getInt(KEY_CURRENT_STEP, -1);
String[] savedStepsNames;
Object savedSteps = extras.get(KEY_TUTORIAL_STEPS);
if (savedSteps instanceof String) {
savedStepsNames = TextUtils.isEmpty((String) savedSteps)
? null : ((String) savedSteps).split(",");
@ -181,7 +227,7 @@ public class GestureSandboxActivity extends FragmentActivity {
return defaultSteps;
}
if (savedStepsNames == null) {
if (savedStepsNames == null || savedStepsNames.length == 0) {
return defaultSteps;
}
@ -190,7 +236,7 @@ public class GestureSandboxActivity extends FragmentActivity {
tutorialSteps[i] = TutorialType.valueOf(savedStepsNames[i]);
}
mCurrentStep = Math.max(currentStep, 1);
mCurrentStep = Math.max(extras.getInt(KEY_CURRENT_STEP, -1), 1);
mNumSteps = tutorialSteps.length;
return tutorialSteps;

View File

@ -0,0 +1,41 @@
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.quickstep.interaction;
import android.app.Activity;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
/** Displays one page of the gesture nav tutorial. */
public abstract class GestureSandboxFragment extends Fragment {
void onAttachedToWindow() {}
void onDetachedFromWindow() {}
boolean shouldDisableSystemGestures() {
return true;
}
void close() {
FragmentActivity activity = getActivity();
if (activity != null) {
activity.setResult(Activity.RESULT_OK);
activity.finish();
}
}
}

View File

@ -99,7 +99,7 @@ final class HomeGestureTutorialController extends SwipeUpGestureTutorialControll
case HOME_NAVIGATION_COMPLETE:
if (result == BackGestureResult.BACK_COMPLETED_FROM_LEFT
|| result == BackGestureResult.BACK_COMPLETED_FROM_RIGHT) {
mTutorialFragment.closeTutorial();
mTutorialFragment.close();
}
break;
}
@ -138,7 +138,7 @@ final class HomeGestureTutorialController extends SwipeUpGestureTutorialControll
break;
case HOME_NAVIGATION_COMPLETE:
if (result == NavBarGestureResult.HOME_GESTURE_COMPLETED) {
mTutorialFragment.closeTutorial();
mTutorialFragment.close();
}
break;
}

View File

@ -33,7 +33,13 @@ import java.util.ArrayList;
/** Shows the Home gesture interactive tutorial. */
public class HomeGestureTutorialFragment extends TutorialFragment {
public HomeGestureTutorialFragment() {}
public HomeGestureTutorialFragment() {
this(false);
}
public HomeGestureTutorialFragment(boolean fromTutorialMenu) {
super(fromTutorialMenu);
}
@Nullable
@Override

View File

@ -0,0 +1,69 @@
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.quickstep.interaction;
import static com.android.quickstep.interaction.GestureSandboxActivity.KEY_GESTURE_COMPLETE;
import static com.android.quickstep.interaction.GestureSandboxActivity.KEY_TUTORIAL_TYPE;
import static com.android.quickstep.interaction.GestureSandboxActivity.KEY_USE_TUTORIAL_MENU;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.launcher3.R;
/** Displays the gesture nav tutorial menu. */
public final class MenuFragment extends GestureSandboxFragment {
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
View root = inflater.inflate(
R.layout.gesture_tutorial_step_menu, container, false);
root.findViewById(R.id.gesture_tutorial_menu_home_button).setOnClickListener(
v -> launchTutorialStep(TutorialController.TutorialType.HOME_NAVIGATION));
root.findViewById(R.id.gesture_tutorial_menu_back_button).setOnClickListener(
v -> launchTutorialStep(TutorialController.TutorialType.BACK_NAVIGATION));
root.findViewById(R.id.gesture_tutorial_menu_overview_button).setOnClickListener(
v -> launchTutorialStep(TutorialController.TutorialType.OVERVIEW_NAVIGATION));
root.findViewById(R.id.gesture_tutorial_menu_done_button).setOnClickListener(
v -> close());
return root;
}
@Override
boolean shouldDisableSystemGestures() {
return false;
}
@Override
public void onSaveInstanceState(Bundle savedInstanceState) {
savedInstanceState.putBoolean(KEY_USE_TUTORIAL_MENU, true);
savedInstanceState.remove(KEY_TUTORIAL_TYPE);
savedInstanceState.remove(KEY_GESTURE_COMPLETE);
super.onSaveInstanceState(savedInstanceState);
}
private void launchTutorialStep(@NonNull TutorialController.TutorialType tutorialType) {
((GestureSandboxActivity) getActivity()).launchTutorialStep(tutorialType, true);
}
}

View File

@ -116,7 +116,7 @@ final class OverviewGestureTutorialController extends SwipeUpGestureTutorialCont
case OVERVIEW_NAVIGATION_COMPLETE:
if (result == BackGestureResult.BACK_COMPLETED_FROM_LEFT
|| result == BackGestureResult.BACK_COMPLETED_FROM_RIGHT) {
mTutorialFragment.closeTutorial();
mTutorialFragment.close();
}
break;
}
@ -178,7 +178,7 @@ final class OverviewGestureTutorialController extends SwipeUpGestureTutorialCont
break;
case OVERVIEW_NAVIGATION_COMPLETE:
if (result == NavBarGestureResult.HOME_GESTURE_COMPLETED) {
mTutorialFragment.closeTutorial();
mTutorialFragment.close();
}
break;
}

View File

@ -33,7 +33,13 @@ import java.util.ArrayList;
/** Shows the Overview gesture interactive tutorial. */
public class OverviewGestureTutorialFragment extends TutorialFragment {
public OverviewGestureTutorialFragment() {}
public OverviewGestureTutorialFragment() {
this(false);
}
public OverviewGestureTutorialFragment(boolean fromTutorialMenu) {
super(fromTutorialMenu);
}
@Nullable
@Override

View File

@ -26,7 +26,9 @@ import com.android.quickstep.interaction.TutorialController.TutorialType;
/** Shows the general navigation gesture sandbox environment. */
public class SandboxModeTutorialFragment extends TutorialFragment {
public SandboxModeTutorialFragment() {}
public SandboxModeTutorialFragment(boolean fromTutorialMenu) {
super(fromTutorialMenu);
}
@Override
TutorialController createController(TutorialType type) {

View File

@ -703,66 +703,65 @@ abstract class TutorialController implements BackGestureAttemptCallback,
}
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();
if (!(mContext instanceof GestureSandboxActivity)) {
return null;
}
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;
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(LOG_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(LOG_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(LOG_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 -> {
mTutorialFragment.closeTutorial(true);
tutorialDialog.dismiss();
});
} else {
Log.w(LOG_TAG, "No confirm button in the skip tutorial dialog to update.");
}
tutorialDialog.getWindow().setBackgroundDrawable(
new ColorDrawable(sandboxActivity.getColor(android.R.color.transparent)));
return tutorialDialog;
try {
tipsAppName = packageManager.getApplicationLabel(
packageManager.getApplicationInfo(
PIXEL_TIPS_APP_PACKAGE_NAME, PackageManager.GET_META_DATA));
} catch (PackageManager.NameNotFoundException e) {
Log.e(LOG_TAG,
"Could not find app label for package name: "
+ PIXEL_TIPS_APP_PACKAGE_NAME
+ ". Defaulting to 'Pixel Tips.'",
e);
}
return null;
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(LOG_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(LOG_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 -> {
mTutorialFragment.closeTutorialStep(true);
tutorialDialog.dismiss();
});
} else {
Log.w(LOG_TAG, "No confirm button in the skip tutorial dialog to update.");
}
tutorialDialog.getWindow().setBackgroundDrawable(
new ColorDrawable(sandboxActivity.getColor(android.R.color.transparent)));
return tutorialDialog;
}
protected AnimatorSet createFingerDotAppearanceAnimatorSet() {

View File

@ -18,11 +18,13 @@ package com.android.quickstep.interaction;
import static android.view.View.NO_ID;
import static com.android.launcher3.config.FeatureFlags.ENABLE_NEW_GESTURE_NAV_TUTORIAL;
import static com.android.quickstep.interaction.GestureSandboxActivity.KEY_GESTURE_COMPLETE;
import static com.android.quickstep.interaction.GestureSandboxActivity.KEY_TUTORIAL_TYPE;
import static com.android.quickstep.interaction.GestureSandboxActivity.KEY_USE_TUTORIAL_MENU;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.graphics.Insets;
@ -43,8 +45,6 @@ import android.widget.ImageView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.InvariantDeviceProfile;
@ -54,16 +54,17 @@ import com.android.quickstep.interaction.TutorialController.TutorialType;
import java.util.Set;
abstract class TutorialFragment extends Fragment implements OnTouchListener {
/** Displays a gesture nav tutorial step. */
abstract class TutorialFragment extends GestureSandboxFragment implements OnTouchListener {
private static final String LOG_TAG = "TutorialFragment";
static final String KEY_TUTORIAL_TYPE = "tutorial_type";
static final String KEY_GESTURE_COMPLETE = "gesture_complete";
private static final String TUTORIAL_SKIPPED_PREFERENCE_KEY = "pref_gestureTutorialSkipped";
private static final String COMPLETED_TUTORIAL_STEPS_PREFERENCE_KEY =
"pref_completedTutorialSteps";
private final boolean mFromTutorialMenu;
TutorialType mTutorialType;
boolean mGestureComplete = false;
@Nullable TutorialController mTutorialController = null;
@ -85,10 +86,10 @@ abstract class TutorialFragment extends Fragment implements OnTouchListener {
private boolean mIsFoldable;
public static TutorialFragment newInstance(
TutorialType tutorialType, boolean gestureComplete) {
TutorialFragment fragment = getFragmentForTutorialType(tutorialType);
TutorialType tutorialType, boolean gestureComplete, boolean fromTutorialMenu) {
TutorialFragment fragment = getFragmentForTutorialType(tutorialType, fromTutorialMenu);
if (fragment == null) {
fragment = new BackGestureTutorialFragment();
fragment = new BackGestureTutorialFragment(fromTutorialMenu);
tutorialType = TutorialType.BACK_NAVIGATION;
}
@ -99,23 +100,28 @@ abstract class TutorialFragment extends Fragment implements OnTouchListener {
return fragment;
}
TutorialFragment(boolean fromTutorialMenu) {
mFromTutorialMenu = fromTutorialMenu;
}
@Nullable
private static TutorialFragment getFragmentForTutorialType(TutorialType tutorialType) {
private static TutorialFragment getFragmentForTutorialType(
TutorialType tutorialType, boolean fromTutorialMenu) {
switch (tutorialType) {
case BACK_NAVIGATION:
case BACK_NAVIGATION_COMPLETE:
return new BackGestureTutorialFragment();
return new BackGestureTutorialFragment(fromTutorialMenu);
case HOME_NAVIGATION:
case HOME_NAVIGATION_COMPLETE:
return new HomeGestureTutorialFragment();
return new HomeGestureTutorialFragment(fromTutorialMenu);
case OVERVIEW_NAVIGATION:
case OVERVIEW_NAVIGATION_COMPLETE:
return new OverviewGestureTutorialFragment();
return new OverviewGestureTutorialFragment(fromTutorialMenu);
case ASSISTANT:
case ASSISTANT_COMPLETE:
return new AssistantGestureTutorialFragment();
return new AssistantGestureTutorialFragment(fromTutorialMenu);
case SANDBOX_MODE:
return new SandboxModeTutorialFragment();
return new SandboxModeTutorialFragment(fromTutorialMenu);
default:
Log.e(LOG_TAG, "Failed to find an appropriate fragment for " + tutorialType.name());
}
@ -202,6 +208,7 @@ abstract class TutorialFragment extends Fragment implements OnTouchListener {
mFingerDotView = mRootView.findViewById(R.id.gesture_tutorial_finger_dot);
mFakePreviousTaskView = mRootView.findViewById(
R.id.gesture_tutorial_fake_previous_task_view);
return mRootView;
}
@ -355,6 +362,7 @@ abstract class TutorialFragment extends Fragment implements OnTouchListener {
| mNavBarGestureHandler.onInterceptTouch(motionEvent);
}
@Override
void onAttachedToWindow() {
StatsLogManager statsLogManager = getStatsLogManager();
if (statsLogManager != null) {
@ -363,6 +371,7 @@ abstract class TutorialFragment extends Fragment implements OnTouchListener {
mEdgeBackGestureHandler.setViewGroupParent(getRootView());
}
@Override
void onDetachedFromWindow() {
mEdgeBackGestureHandler.setViewGroupParent(null);
}
@ -385,6 +394,8 @@ abstract class TutorialFragment extends Fragment implements OnTouchListener {
@Override
public void onSaveInstanceState(Bundle savedInstanceState) {
savedInstanceState.putSerializable(KEY_TUTORIAL_TYPE, mTutorialType);
savedInstanceState.putBoolean(KEY_GESTURE_COMPLETE, isGestureComplete());
savedInstanceState.putBoolean(KEY_USE_TUTORIAL_MENU, mFromTutorialMenu);
super.onSaveInstanceState(savedInstanceState);
}
@ -410,17 +421,18 @@ abstract class TutorialFragment extends Fragment implements OnTouchListener {
GestureSandboxActivity gestureSandboxActivity = getGestureSandboxActivity();
if (gestureSandboxActivity == null) {
closeTutorial();
close();
return;
}
gestureSandboxActivity.continueTutorial();
}
void closeTutorial() {
closeTutorial(false);
@Override
void close() {
closeTutorialStep(false);
}
void closeTutorial(boolean tutorialSkipped) {
void closeTutorialStep(boolean tutorialSkipped) {
if (tutorialSkipped) {
SharedPreferences sharedPrefs = getSharedPreferences();
if (sharedPrefs != null) {
@ -432,11 +444,12 @@ abstract class TutorialFragment extends Fragment implements OnTouchListener {
StatsLogManager.LauncherEvent.LAUNCHER_GESTURE_TUTORIAL_SKIPPED);
}
}
FragmentActivity activity = getActivity();
if (activity != null) {
activity.setResult(Activity.RESULT_OK);
activity.finish();
GestureSandboxActivity gestureSandboxActivity = getGestureSandboxActivity();
if (mFromTutorialMenu && gestureSandboxActivity != null) {
gestureSandboxActivity.launchTutorialMenu();
return;
}
super.close();
}
void startSystemNavigationSetting() {
@ -470,9 +483,10 @@ abstract class TutorialFragment extends Fragment implements OnTouchListener {
@Nullable
private GestureSandboxActivity getGestureSandboxActivity() {
Context context = getContext();
Activity activity = getActivity();
return context instanceof GestureSandboxActivity ? (GestureSandboxActivity) context : null;
return activity instanceof GestureSandboxActivity
? (GestureSandboxActivity) activity : null;
}
@Nullable