• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.android.quickstep.interaction;
17 
18 import android.graphics.Color;
19 import android.graphics.Rect;
20 import android.os.Bundle;
21 import android.text.TextUtils;
22 import android.util.DisplayMetrics;
23 import android.view.Display;
24 import android.view.View;
25 import android.view.Window;
26 
27 import androidx.annotation.NonNull;
28 import androidx.fragment.app.FragmentActivity;
29 
30 import com.android.launcher3.R;
31 import com.android.quickstep.interaction.TutorialController.TutorialType;
32 
33 import java.util.List;
34 
35 /** Shows the gesture interactive sandbox in full screen mode. */
36 public class GestureSandboxActivity extends FragmentActivity {
37 
38     private static final String LOG_TAG = "GestureSandboxActivity";
39 
40     private static final String KEY_TUTORIAL_STEPS = "tutorial_steps";
41     private static final String KEY_CURRENT_STEP = "current_step";
42 
43     private TutorialType[] mTutorialSteps;
44     private TutorialType mCurrentTutorialStep;
45     private TutorialFragment mFragment;
46 
47     private int mCurrentStep;
48     private int mNumSteps;
49 
50     @Override
onCreate(Bundle savedInstanceState)51     protected void onCreate(Bundle savedInstanceState) {
52         super.onCreate(savedInstanceState);
53         requestWindowFeature(Window.FEATURE_NO_TITLE);
54         setContentView(R.layout.gesture_tutorial_activity);
55 
56         Bundle args = savedInstanceState == null ? getIntent().getExtras() : savedInstanceState;
57         mTutorialSteps = getTutorialSteps(args);
58         mCurrentTutorialStep = mTutorialSteps[mCurrentStep - 1];
59         mFragment = TutorialFragment.newInstance(mCurrentTutorialStep);
60         getSupportFragmentManager().beginTransaction()
61                 .add(R.id.gesture_tutorial_fragment_container, mFragment)
62                 .commit();
63     }
64 
65     @Override
onAttachedToWindow()66     public void onAttachedToWindow() {
67         super.onAttachedToWindow();
68         disableSystemGestures();
69         mFragment.onAttachedToWindow();
70     }
71 
72     @Override
onDetachedFromWindow()73     public void onDetachedFromWindow() {
74         super.onDetachedFromWindow();
75         mFragment.onDetachedFromWindow();
76     }
77 
78     @Override
onWindowFocusChanged(boolean hasFocus)79     public void onWindowFocusChanged(boolean hasFocus) {
80         super.onWindowFocusChanged(hasFocus);
81         if (hasFocus) {
82             hideSystemUI();
83         }
84     }
85 
86     @Override
onSaveInstanceState(@onNull Bundle savedInstanceState)87     protected void onSaveInstanceState(@NonNull Bundle savedInstanceState) {
88         savedInstanceState.putStringArray(KEY_TUTORIAL_STEPS, getTutorialStepNames());
89         savedInstanceState.putInt(KEY_CURRENT_STEP, mCurrentStep);
90         super.onSaveInstanceState(savedInstanceState);
91     }
92 
93     /** Returns true iff there aren't anymore tutorial types to display to the user. */
isTutorialComplete()94     public boolean isTutorialComplete() {
95         return mCurrentStep >= mNumSteps;
96     }
97 
getCurrentStep()98     public int getCurrentStep() {
99         return mCurrentStep;
100     }
101 
getNumSteps()102     public int getNumSteps() {
103         return mNumSteps;
104     }
105 
106     /**
107      * Closes the tutorial and this activity.
108      */
closeTutorial()109     public void closeTutorial() {
110         mFragment.closeTutorial();
111     }
112 
113     /**
114      * Replaces the current TutorialFragment, continuing to the next tutorial step if there is one.
115      *
116      * If there is no following step, the tutorial is closed.
117      */
continueTutorial()118     public void continueTutorial() {
119         if (isTutorialComplete()) {
120             mFragment.closeTutorial();
121             return;
122         }
123         mCurrentTutorialStep = mTutorialSteps[mCurrentStep];
124         mFragment = TutorialFragment.newInstance(mCurrentTutorialStep);
125         getSupportFragmentManager().beginTransaction()
126                 .replace(R.id.gesture_tutorial_fragment_container, mFragment)
127                 .runOnCommit(() -> mFragment.onAttachedToWindow())
128                 .commit();
129         mCurrentStep++;
130     }
131 
getTutorialStepNames()132     private String[] getTutorialStepNames() {
133         String[] tutorialStepNames = new String[mTutorialSteps.length];
134 
135         int i = 0;
136         for (TutorialType tutorialStep : mTutorialSteps) {
137             tutorialStepNames[i++] = tutorialStep.name();
138         }
139 
140         return tutorialStepNames;
141     }
142 
getTutorialSteps(Bundle extras)143     private TutorialType[] getTutorialSteps(Bundle extras) {
144         TutorialType[] defaultSteps = new TutorialType[] {TutorialType.BACK_NAVIGATION};
145         mCurrentStep = 1;
146         mNumSteps = 1;
147 
148         if (extras == null || !extras.containsKey(KEY_TUTORIAL_STEPS)) {
149             return defaultSteps;
150         }
151 
152         Object savedSteps = extras.get(KEY_TUTORIAL_STEPS);
153         int currentStep = extras.getInt(KEY_CURRENT_STEP, -1);
154         String[] savedStepsNames;
155 
156         if (savedSteps instanceof String) {
157             savedStepsNames = TextUtils.isEmpty((String) savedSteps)
158                     ? null : ((String) savedSteps).split(",");
159         } else if (savedSteps instanceof String[]) {
160             savedStepsNames = (String[]) savedSteps;
161         } else {
162             return defaultSteps;
163         }
164 
165         if (savedStepsNames == null) {
166             return defaultSteps;
167         }
168 
169         TutorialType[] tutorialSteps = new TutorialType[savedStepsNames.length];
170         for (int i = 0; i < savedStepsNames.length; i++) {
171             tutorialSteps[i] = TutorialType.valueOf(savedStepsNames[i]);
172         }
173 
174         mCurrentStep = Math.max(currentStep, 1);
175         mNumSteps = tutorialSteps.length;
176 
177         return tutorialSteps;
178     }
179 
hideSystemUI()180     private void hideSystemUI() {
181         getWindow().getDecorView().setSystemUiVisibility(
182                 View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
183                         | View.SYSTEM_UI_FLAG_LAYOUT_STABLE
184                         | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
185                         | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
186                         | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
187                         | View.SYSTEM_UI_FLAG_FULLSCREEN);
188         getWindow().setNavigationBarColor(Color.TRANSPARENT);
189     }
190 
disableSystemGestures()191     private void disableSystemGestures() {
192         Display display = getDisplay();
193         if (display != null) {
194             DisplayMetrics metrics = new DisplayMetrics();
195             display.getMetrics(metrics);
196             getWindow().setSystemGestureExclusionRects(
197                     List.of(new Rect(0, 0, metrics.widthPixels, metrics.heightPixels)));
198         }
199     }
200 }
201