Merge "Enforce the correct orientation for the gesture navigation tutorial" into udc-dev am: 372de4f521

Original change: https://googleplex-android-review.googlesource.com/c/platform/packages/apps/Launcher3/+/23493333

Change-Id: Ib689779cdd7ac6de5eb61cff22d1967bc8cb10fe
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/quickstep/AndroidManifest.xml b/quickstep/AndroidManifest.xml
index 8e1baba..7c0a5ae 100644
--- a/quickstep/AndroidManifest.xml
+++ b/quickstep/AndroidManifest.xml
@@ -104,7 +104,8 @@
             android:autoRemoveFromRecents="true"
             android:excludeFromRecents="true"
             android:theme="@style/GestureTutorialActivity"
-            android:exported="true">
+            android:exported="true"
+            android:configChanges="orientation">
             <intent-filter>
                 <action android:name="com.android.quickstep.action.GESTURE_SANDBOX"/>
                 <category android:name="android.intent.category.DEFAULT"/>
diff --git a/quickstep/res/drawable/rotate_prompt_bg.xml b/quickstep/res/drawable/rotate_prompt_bg.xml
new file mode 100644
index 0000000..528a2bc
--- /dev/null
+++ b/quickstep/res/drawable/rotate_prompt_bg.xml
@@ -0,0 +1,21 @@
+<!--
+    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">
+    <solid android:color="?attr/surfaceContainer" />
+    <corners android:radius="@dimen/gesture_tutorial_menu_button_radius" />
+</shape>
\ No newline at end of file
diff --git a/quickstep/res/drawable/rotate_tutorial_warning.xml b/quickstep/res/drawable/rotate_tutorial_warning.xml
new file mode 100644
index 0000000..d1117a4
--- /dev/null
+++ b/quickstep/res/drawable/rotate_tutorial_warning.xml
@@ -0,0 +1,26 @@
+<!--
+    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"
+    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+    android:width="32dp"
+    android:height="32dp"
+    android:tint="?attr/colorControlNormal"
+    android:viewportHeight="960"
+    android:viewportWidth="960">
+    <path
+        android:fillColor="?androidprv:attr/materialColorOnSurface"
+        android:pathData="M40,840L480,80L920,840L40,840ZM178,760L782,760L480,240L178,760ZM480,720Q497,720 508.5,708.5Q520,697 520,680Q520,663 508.5,651.5Q497,640 480,640Q463,640 451.5,651.5Q440,663 440,680Q440,697 451.5,708.5Q463,720 480,720ZM440,600L520,600L520,400L440,400L440,600ZM480,500L480,500L480,500L480,500Z" />
+</vector>
diff --git a/quickstep/res/layout/gesture_tutorial_activity.xml b/quickstep/res/layout/gesture_tutorial_activity.xml
index 4dc8913..0e763ec 100644
--- a/quickstep/res/layout/gesture_tutorial_activity.xml
+++ b/quickstep/res/layout/gesture_tutorial_activity.xml
@@ -13,7 +13,80 @@
     See the License for the specific language governing permissions and
     limitations under the License.
 -->
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/gesture_tutorial_fragment_container"
+<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"/>
\ No newline at end of file
+    android:layout_height="match_parent">
+
+    <FrameLayout
+        android:id="@+id/gesture_tutorial_fragment_container"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintEnd_toEndOf="parent" />
+
+    <RelativeLayout
+        android:id="@+id/rotation_prompt"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:background="?attr/surfaceHome"
+        android:visibility="gone"
+
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintEnd_toEndOf="parent">
+
+        <androidx.constraintlayout.widget.ConstraintLayout
+            android:id="@+id/background"
+            android:layout_width="300dp"
+            android:layout_height="wrap_content"
+            android:layout_centerHorizontal="true"
+            android:layout_centerVertical="true"
+            android:background="@drawable/rotate_prompt_bg"
+            android:padding="24dp">
+
+            <ImageView
+                android:id="@+id/icon"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:src="@drawable/rotate_tutorial_warning"
+
+                app:layout_constraintStart_toStartOf="parent"
+                app:layout_constraintTop_toTopOf="parent"
+                app:layout_constraintEnd_toEndOf="parent" />
+
+            <TextView
+                android:id="@+id/rotate_title"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginTop="20dp"
+                android:lineSpacingExtra="2sp"
+                android:text="@string/gesture_tutorial_rotation_prompt_title"
+                android:textAppearance="@style/rotate_prompt_title"
+
+                app:layout_constraintStart_toStartOf="parent"
+                app:layout_constraintTop_toBottomOf="@id/icon"
+                app:layout_constraintEnd_toEndOf="parent" />
+
+            <TextView
+                android:id="@+id/rotate_screen_subtitle"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginTop="16dp"
+                android:gravity="center"
+                android:text="@string/gesture_tutorial_rotation_prompt"
+                android:textAppearance="@style/rotate_prompt_subtitle"
+
+                app:layout_constraintStart_toStartOf="parent"
+                app:layout_constraintTop_toBottomOf="@id/rotate_title"
+                app:layout_constraintEnd_toEndOf="parent" />
+
+        </androidx.constraintlayout.widget.ConstraintLayout>
+    </RelativeLayout>
+
+</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
diff --git a/quickstep/res/values/strings.xml b/quickstep/res/values/strings.xml
index bd69f9f..3b82784 100644
--- a/quickstep/res/values/strings.xml
+++ b/quickstep/res/values/strings.xml
@@ -94,6 +94,10 @@
     <!-- content description for hotseat items -->
     <string name="hotseat_prediction_content_description">Predicted app: <xliff:g id="title" example="Chrome">%1$s</xliff:g></string>
 
+    <!-- Title of prompt shown before the gesture navigation tutorial to users who need to rotate their screen. [CHAR LIMIT=100] -->
+    <string name="gesture_tutorial_rotation_prompt_title">Rotate your device</string>
+    <!-- Prompt shown before the gesture navigation tutorial to users who need to rotate their screen to begin. [CHAR LIMIT=100] -->
+    <string name="gesture_tutorial_rotation_prompt">Please rotate your device to complete the gesture navigation tutorial</string>
     <!-- Feedback shown during interactive parts of Back gesture tutorial for right edge when the gesture is too far from the edge. [CHAR LIMIT=100] -->
     <string name="back_gesture_feedback_swipe_too_far_from_edge">Make sure you swipe from the far-right or far-left edge.</string>
     <!-- Feedback shown during interactive parts of Back gesture tutorial for right edge when the gesture is cancelled. [CHAR LIMIT=100] -->
diff --git a/quickstep/res/values/styles.xml b/quickstep/res/values/styles.xml
index 612682a..21b7fd5 100644
--- a/quickstep/res/values/styles.xml
+++ b/quickstep/res/values/styles.xml
@@ -290,4 +290,12 @@
         <item name="surfaceOverview">@android:color/system_accent2_300</item>
         <item name="secondaryOverview">?androidprv:attr/materialColorOnSecondaryFixedVariant</item>
     </style>
+
+    <style name="rotate_prompt_title" parent="TextAppearance.GestureTutorial.Dialog.Title">
+        <item name="android:textColor">?androidprv:attr/materialColorOnSurface</item>
+    </style>
+
+    <style name="rotate_prompt_subtitle" parent="TextAppearance.GestureTutorial.Dialog.Subtitle">
+        <item name="android:textColor">?androidprv:attr/materialColorOnSurfaceVariant</item>
+    </style>
 </resources>
diff --git a/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialController.java b/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialController.java
index 54c441b..9083d51 100644
--- a/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialController.java
+++ b/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialController.java
@@ -147,7 +147,7 @@
 
     @Override
     public void onBackGestureAttempted(BackGestureResult result) {
-        if (isGestureCompleted()) {
+        if (skipGestureAttempt()) {
             return;
         }
         switch (mTutorialType) {
@@ -165,7 +165,7 @@
 
     @Override
     public void onBackGestureProgress(float diffx, float diffy, boolean isLeftGesture) {
-        if (isGestureCompleted()) {
+        if (skipGestureAttempt()) {
             return;
         }
 
@@ -234,7 +234,7 @@
 
     @Override
     public void onNavBarGestureAttempted(NavBarGestureResult result, PointF finalVelocity) {
-        if (isGestureCompleted()) {
+        if (skipGestureAttempt()) {
             return;
         }
         if (mTutorialType == BACK_NAVIGATION_COMPLETE) {
diff --git a/quickstep/src/com/android/quickstep/interaction/GestureSandboxActivity.java b/quickstep/src/com/android/quickstep/interaction/GestureSandboxActivity.java
index aeac760..62726a0 100644
--- a/quickstep/src/com/android/quickstep/interaction/GestureSandboxActivity.java
+++ b/quickstep/src/com/android/quickstep/interaction/GestureSandboxActivity.java
@@ -16,6 +16,8 @@
 package com.android.quickstep.interaction;
 
 import android.content.SharedPreferences;
+import android.content.pm.ActivityInfo;
+import android.content.res.Configuration;
 import android.graphics.Color;
 import android.graphics.Rect;
 import android.os.Bundle;
@@ -29,6 +31,8 @@
 import androidx.annotation.Nullable;
 import androidx.fragment.app.FragmentActivity;
 
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.InvariantDeviceProfile;
 import com.android.launcher3.LauncherPrefs;
 import com.android.launcher3.R;
 import com.android.launcher3.config.FeatureFlags;
@@ -53,10 +57,12 @@
 
     private int mCurrentStep;
     private int mNumSteps;
+    private boolean mShowRotationPrompt;
 
     private SharedPreferences mSharedPrefs;
     private StatsLogManager mStatsLogManager;
 
+    private View mRotationPrompt;
     private TISBindHelper mTISBindHelper;
     private TISBinder mBinder;
 
@@ -94,10 +100,50 @@
                 .add(R.id.gesture_tutorial_fragment_container, mFragment)
                 .commit();
 
+        mRotationPrompt = findViewById(R.id.rotation_prompt);
+        if (FeatureFlags.ENABLE_NEW_GESTURE_NAV_TUTORIAL.get()) {
+            correctUserOrientation();
+        }
         mTISBindHelper = new TISBindHelper(this, this::onTISConnected);
     }
 
     @Override
+    public void onConfigurationChanged(Configuration newConfig) {
+        super.onConfigurationChanged(newConfig);
+
+        // Ensure the prompt to rotate the screen is updated
+        if (FeatureFlags.ENABLE_NEW_GESTURE_NAV_TUTORIAL.get()) {
+            correctUserOrientation();
+        }
+    }
+
+    /**
+     * Gesture animations are only in landscape for large screens and portrait for mobile. This
+     * method enforces the following flows:
+     *     1) phone / two-panel closed -> lock to portrait
+     *     2) two-panel open / tablet + portrait -> prompt the user to rotate the screen
+     *     3) two-panel open / tablet + landscape -> hide potential rotating prompt
+     */
+    private void correctUserOrientation() {
+        DeviceProfile deviceProfile = InvariantDeviceProfile.INSTANCE.get(
+                getApplicationContext()).getDeviceProfile(this);
+        if (deviceProfile.isTablet) {
+            mShowRotationPrompt = getResources().getConfiguration().orientation
+                    == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
+            updateVisibility(mRotationPrompt, mShowRotationPrompt ? View.VISIBLE : View.GONE);
+        } else {
+            setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
+        }
+    }
+
+    void updateVisibility(View view, int visibility) {
+        if (view == null || view.getVisibility() == visibility) {
+            return;
+        }
+        view.setVisibility(visibility);
+    }
+
+    @Override
     public void onAttachedToWindow() {
         super.onAttachedToWindow();
         if (mFragment.shouldDisableSystemGestures()) {
@@ -128,6 +174,10 @@
         super.onSaveInstanceState(savedInstanceState);
     }
 
+    protected boolean isRotationPromptShowing() {
+        return mShowRotationPrompt;
+    }
+
     protected SharedPreferences getSharedPrefs() {
         return mSharedPrefs;
     }
diff --git a/quickstep/src/com/android/quickstep/interaction/HomeGestureTutorialController.java b/quickstep/src/com/android/quickstep/interaction/HomeGestureTutorialController.java
index 891f20e..333ecbb 100644
--- a/quickstep/src/com/android/quickstep/interaction/HomeGestureTutorialController.java
+++ b/quickstep/src/com/android/quickstep/interaction/HomeGestureTutorialController.java
@@ -140,7 +140,7 @@
 
     @Override
     public void onBackGestureAttempted(BackGestureResult result) {
-        if (isGestureCompleted()) {
+        if (skipGestureAttempt()) {
             return;
         }
         switch (mTutorialType) {
@@ -167,7 +167,7 @@
 
     @Override
     public void onNavBarGestureAttempted(NavBarGestureResult result, PointF finalVelocity) {
-        if (isGestureCompleted()) {
+        if (skipGestureAttempt()) {
             return;
         }
         switch (mTutorialType) {
diff --git a/quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialController.java b/quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialController.java
index 667fe4d..09a6bbe 100644
--- a/quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialController.java
+++ b/quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialController.java
@@ -170,7 +170,7 @@
 
     @Override
     public void onBackGestureAttempted(BackGestureResult result) {
-        if (isGestureCompleted()) {
+        if (skipGestureAttempt()) {
             return;
         }
         switch (mTutorialType) {
@@ -197,7 +197,7 @@
 
     @Override
     public void onNavBarGestureAttempted(NavBarGestureResult result, PointF finalVelocity) {
-        if (isGestureCompleted()) {
+        if (skipGestureAttempt()) {
             return;
         }
         switch (mTutorialType) {
diff --git a/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java b/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java
index 0bbf373..f1cbfcc 100644
--- a/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java
+++ b/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java
@@ -260,7 +260,7 @@
 
     @Override
     public void setNavBarGestureProgress(@Nullable Float displacement) {
-        if (isGestureCompleted()) {
+        if (skipGestureAttempt()) {
             return;
         }
         if (mTutorialType == HOME_NAVIGATION_COMPLETE
@@ -281,7 +281,7 @@
 
     @Override
     public void onMotionPaused(boolean unused) {
-        if (isGestureCompleted()) {
+        if (skipGestureAttempt()) {
             return;
         }
         if (mShowTasks) {
diff --git a/quickstep/src/com/android/quickstep/interaction/TutorialController.java b/quickstep/src/com/android/quickstep/interaction/TutorialController.java
index 5ec92a6..a58f453 100644
--- a/quickstep/src/com/android/quickstep/interaction/TutorialController.java
+++ b/quickstep/src/com/android/quickstep/interaction/TutorialController.java
@@ -462,6 +462,10 @@
         return mGestureCompleted;
     }
 
+    public boolean skipGestureAttempt() {
+        return isGestureCompleted() || mTutorialFragment.isRotationPromptShowing();
+    }
+
     void hideFeedback() {
         if (mFeedbackView.getVisibility() != View.VISIBLE) {
             return;
diff --git a/quickstep/src/com/android/quickstep/interaction/TutorialFragment.java b/quickstep/src/com/android/quickstep/interaction/TutorialFragment.java
index bfad643..84326f5 100644
--- a/quickstep/src/com/android/quickstep/interaction/TutorialFragment.java
+++ b/quickstep/src/com/android/quickstep/interaction/TutorialFragment.java
@@ -498,6 +498,11 @@
         return activity != null ? activity.getStatsLogManager() : null;
     }
 
+    protected boolean isRotationPromptShowing() {
+        GestureSandboxActivity activity = getGestureSandboxActivity();
+        return activity != null && activity.isRotationPromptShowing();
+    }
+
     @Nullable
     private SharedPreferences getSharedPreferences() {
         GestureSandboxActivity activity = getGestureSandboxActivity();