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: Ic74738724e31203cddbb2eccc187ca562e3de6bd
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
Saumya Prakash
2023-06-11 23:14:35 +00:00
committed by Automerger Merge Worker
13 changed files with 205 additions and 13 deletions

View File

@@ -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"/>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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"/>
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>

View File

@@ -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] -->

View File

@@ -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>

View File

@@ -147,7 +147,7 @@ final class BackGestureTutorialController extends TutorialController {
@Override
public void onBackGestureAttempted(BackGestureResult result) {
if (isGestureCompleted()) {
if (skipGestureAttempt()) {
return;
}
switch (mTutorialType) {
@@ -165,7 +165,7 @@ final class BackGestureTutorialController extends TutorialController {
@Override
public void onBackGestureProgress(float diffx, float diffy, boolean isLeftGesture) {
if (isGestureCompleted()) {
if (skipGestureAttempt()) {
return;
}
@@ -234,7 +234,7 @@ final class BackGestureTutorialController extends TutorialController {
@Override
public void onNavBarGestureAttempted(NavBarGestureResult result, PointF finalVelocity) {
if (isGestureCompleted()) {
if (skipGestureAttempt()) {
return;
}
if (mTutorialType == BACK_NAVIGATION_COMPLETE) {

View File

@@ -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.NonNull;
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 @@ public class GestureSandboxActivity extends FragmentActivity {
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,9 +100,49 @@ public class GestureSandboxActivity extends FragmentActivity {
.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();
@@ -128,6 +174,10 @@ public class GestureSandboxActivity extends FragmentActivity {
super.onSaveInstanceState(savedInstanceState);
}
protected boolean isRotationPromptShowing() {
return mShowRotationPrompt;
}
protected SharedPreferences getSharedPrefs() {
return mSharedPrefs;
}

View File

@@ -140,7 +140,7 @@ final class HomeGestureTutorialController extends SwipeUpGestureTutorialControll
@Override
public void onBackGestureAttempted(BackGestureResult result) {
if (isGestureCompleted()) {
if (skipGestureAttempt()) {
return;
}
switch (mTutorialType) {
@@ -167,7 +167,7 @@ final class HomeGestureTutorialController extends SwipeUpGestureTutorialControll
@Override
public void onNavBarGestureAttempted(NavBarGestureResult result, PointF finalVelocity) {
if (isGestureCompleted()) {
if (skipGestureAttempt()) {
return;
}
switch (mTutorialType) {

View File

@@ -170,7 +170,7 @@ final class OverviewGestureTutorialController extends SwipeUpGestureTutorialCont
@Override
public void onBackGestureAttempted(BackGestureResult result) {
if (isGestureCompleted()) {
if (skipGestureAttempt()) {
return;
}
switch (mTutorialType) {
@@ -197,7 +197,7 @@ final class OverviewGestureTutorialController extends SwipeUpGestureTutorialCont
@Override
public void onNavBarGestureAttempted(NavBarGestureResult result, PointF finalVelocity) {
if (isGestureCompleted()) {
if (skipGestureAttempt()) {
return;
}
switch (mTutorialType) {

View File

@@ -260,7 +260,7 @@ abstract class SwipeUpGestureTutorialController extends TutorialController {
@Override
public void setNavBarGestureProgress(@Nullable Float displacement) {
if (isGestureCompleted()) {
if (skipGestureAttempt()) {
return;
}
if (mTutorialType == HOME_NAVIGATION_COMPLETE
@@ -281,7 +281,7 @@ abstract class SwipeUpGestureTutorialController extends TutorialController {
@Override
public void onMotionPaused(boolean unused) {
if (isGestureCompleted()) {
if (skipGestureAttempt()) {
return;
}
if (mShowTasks) {

View File

@@ -462,6 +462,10 @@ abstract class TutorialController implements BackGestureAttemptCallback,
return mGestureCompleted;
}
public boolean skipGestureAttempt() {
return isGestureCompleted() || mTutorialFragment.isRotationPromptShowing();
}
void hideFeedback() {
if (mFeedbackView.getVisibility() != View.VISIBLE) {
return;

View File

@@ -498,6 +498,11 @@ abstract class TutorialFragment extends GestureSandboxFragment implements OnTouc
return activity != null ? activity.getStatsLogManager() : null;
}
protected boolean isRotationPromptShowing() {
GestureSandboxActivity activity = getGestureSandboxActivity();
return activity != null && activity.isRotationPromptShowing();
}
@Nullable
private SharedPreferences getSharedPreferences() {
GestureSandboxActivity activity = getGestureSandboxActivity();