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();