• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2023 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 
17 package com.android.settings.inputmethod;
18 
19 import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG;
20 import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL;
21 
22 import android.app.Activity;
23 import android.app.Dialog;
24 import android.content.Context;
25 import android.graphics.Color;
26 import android.graphics.Point;
27 import android.os.Bundle;
28 import android.view.Display;
29 import android.view.LayoutInflater;
30 import android.view.View;
31 import android.view.ViewGroup;
32 import android.view.ViewTreeObserver;
33 import android.view.Window;
34 import android.view.WindowInsets;
35 import android.view.WindowManager;
36 import android.widget.Button;
37 import android.widget.FrameLayout;
38 import android.widget.ImageView;
39 
40 import androidx.annotation.NonNull;
41 import androidx.viewpager.widget.PagerAdapter;
42 import androidx.viewpager.widget.ViewPager;
43 
44 import com.android.settings.R;
45 
46 import com.google.android.material.bottomsheet.BottomSheetBehavior;
47 import com.google.android.material.bottomsheet.BottomSheetDialogFragment;
48 
49 import java.util.ArrayList;
50 import java.util.Arrays;
51 
52 public class TouchpadGestureDialogFragment extends BottomSheetDialogFragment {
53 
54     private Context mContext;
55     private ArrayList<View> mPageList;
56     private ImageView[] mDotIndicators;
57     private View[] mViewPagerItems;
58     private Button mButtonStartSkip;
59     private Button mButtonStartRestart;
60     private Button mButtonEndNext;
61     private Button mButtonEndDone;
62 
63     private static final int DOT_INDICATOR_SIZE = 12;
64     private static final int DOT_INDICATOR_LEFT_PADDING = 6;
65     private static final int DOT_INDICATOR_RIGHT_PADDING = 6;
66 
67     @Override
onAttach(@onNull Context context)68     public void onAttach(@NonNull Context context) {
69         super.onAttach(context);
70         mContext = context;
71     }
72 
73     @Override
onCreate(Bundle savedInstanceState)74     public void onCreate(Bundle savedInstanceState) {
75         super.onCreate(savedInstanceState);
76     }
77 
78     @Override
onStart()79     public void onStart() {
80         super.onStart();
81         final Dialog dialog = getDialog();
82         if (dialog == null) {
83             return;
84         }
85         Window window = dialog.getWindow();
86         if (window == null) {
87             return;
88         }
89         setLayoutEdgeToEdge(window);
90         final Point size = getScreenSize();
91         final WindowManager.LayoutParams attributes = window.getAttributes();
92         attributes.width = (int) (size.x * 0.75);
93         window.setAttributes(attributes);
94     }
95 
96     @Override
onPause()97     public void onPause() {
98         super.onPause();
99         dismiss();
100     }
101 
setLayoutEdgeToEdge(Window window)102     private static void setLayoutEdgeToEdge(Window window) {
103         View windowDecorView = window.getDecorView();
104         windowDecorView.setSystemUiVisibility(
105                 windowDecorView.getSystemUiVisibility()
106                         | View.SYSTEM_UI_FLAG_LAYOUT_STABLE
107                         | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
108         window.setNavigationBarColor(Color.TRANSPARENT);
109     }
110 
111     @NonNull
112     @Override
onCreateDialog(final Bundle savedInstanceState)113     public Dialog onCreateDialog(final Bundle savedInstanceState) {
114         Dialog dialog = super.onCreateDialog(savedInstanceState);
115 
116         LayoutInflater inflater = mContext.getSystemService(LayoutInflater.class);
117         View contentView = inflater.inflate(R.layout.touchpad_gesture_preview, null);
118         addViewPager(contentView, inflater);
119         dialog.setContentView(contentView);
120         Window gestureDialogWindow = dialog.getWindow();
121         gestureDialogWindow.setType(TYPE_SYSTEM_DIALOG);
122 
123         // Workaround for solve issue about dialog not full expanded when landscape.
124         FrameLayout bottomSheet = (FrameLayout)
125                 dialog.findViewById(com.google.android.material.R.id.design_bottom_sheet);
126         bottomSheet.setBackgroundResource(android.R.color.transparent);
127         BottomSheetBehavior<FrameLayout> behavior = BottomSheetBehavior.from(bottomSheet);
128         if (!isGestureNavigationEnabled()) {
129             behavior.setState(BottomSheetBehavior.STATE_EXPANDED);
130         }
131         ViewTreeObserver observer = contentView.getViewTreeObserver();
132         observer.addOnGlobalLayoutListener(
133                 new ViewTreeObserver.OnGlobalLayoutListener() {
134                     @Override
135                     public void onGlobalLayout() {
136                         contentView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
137                         int contentViewHeight = contentView.getMeasuredHeight();
138                         int navigationBarHeight = getNavigationBarHeight();
139                         behavior.setPeekHeight(contentViewHeight - navigationBarHeight);
140                     }
141                 });
142 
143         // The gesture education view shouldn't be draggable."
144         behavior.setBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() {
145             @Override
146             public void onStateChanged(@NonNull View bottomSheet, int newState) {
147                 if (newState == BottomSheetBehavior.STATE_DRAGGING) {
148                     if (isGestureNavigationEnabled()) {
149                         behavior.setState(BottomSheetBehavior.STATE_COLLAPSED);
150                     } else {
151                         behavior.setState(BottomSheetBehavior.STATE_EXPANDED);
152                     }
153                 }
154             }
155 
156             @Override
157             public void onSlide(@NonNull View bottomSheet, float slideOffset) {
158                 // Do nothing.
159             }
160         });
161 
162         return dialog;
163     }
164 
getViewPagerResource()165     private ArrayList<Integer> getViewPagerResource() {
166         return new ArrayList<Integer>(
167                 Arrays.asList(
168                         R.layout.gesture_tip1_go_home,
169                         R.layout.gesture_tip2_go_back,
170                         R.layout.gesture_tip3_recent_apps,
171                         R.layout.gesture_tip4_notifications,
172                         R.layout.gesture_tip5_switch_apps));
173     }
174 
addViewPager(View preview, LayoutInflater inflater)175     private void addViewPager(View preview, LayoutInflater inflater) {
176         ViewPager viewPager = preview.findViewById(R.id.viewpager);
177         int viewPagerResourceSize = getViewPagerResource().size();
178         mViewPagerItems = new View[viewPagerResourceSize];
179         for (int i = 0; i < viewPagerResourceSize; i++) {
180             mViewPagerItems[i] = inflater.inflate(getViewPagerResource().get(i), null /* root */);
181         }
182 
183         mPageList = new ArrayList<View>();
184         for (int i = 0; i < mViewPagerItems.length; i++) {
185             mPageList.add(mViewPagerItems[i]);
186         }
187 
188         viewPager.setAdapter(new GesturePagerAdapter(mPageList));
189 
190         mButtonStartRestart = (Button) preview.findViewById(R.id.button_restart);
191         mButtonStartRestart.setOnClickListener(v -> {
192             final int firstPos = viewPager.getCurrentItem() - mViewPagerItems.length;
193             viewPager.setCurrentItem(firstPos, true);
194         });
195 
196         mButtonEndDone = (Button) preview.findViewById(R.id.button_done);
197         mButtonEndDone.setOnClickListener(v -> {
198             dismiss();
199         });
200 
201         mButtonStartSkip = (Button) preview.findViewById(R.id.button_skip);
202         mButtonStartSkip.setOnClickListener(v -> {
203             dismiss();
204         });
205 
206         mButtonEndNext = (Button) preview.findViewById(R.id.button_next);
207         mButtonEndNext.setOnClickListener(v -> {
208             final int nextPos = viewPager.getCurrentItem() + 1;
209             viewPager.setCurrentItem(nextPos, true);
210         });
211 
212         viewPager.addOnPageChangeListener(createPageListener());
213         final ViewGroup viewGroup = (ViewGroup) preview.findViewById(R.id.viewGroup);
214         mDotIndicators = new ImageView[mPageList.size()];
215         for (int i = 0; i < mPageList.size(); i++) {
216             final ImageView imageView = new ImageView(mContext);
217             final ViewGroup.MarginLayoutParams lp =
218                     new ViewGroup.MarginLayoutParams(DOT_INDICATOR_SIZE, DOT_INDICATOR_SIZE);
219             lp.setMargins(DOT_INDICATOR_LEFT_PADDING, 0, DOT_INDICATOR_RIGHT_PADDING, 0);
220             imageView.setLayoutParams(lp);
221             mDotIndicators[i] = imageView;
222             viewGroup.addView(mDotIndicators[i]);
223         }
224     }
225 
226     private static class GesturePagerAdapter extends PagerAdapter {
227         private final ArrayList<View> mPageViewList;
228 
GesturePagerAdapter(ArrayList<View> pageViewList)229         GesturePagerAdapter(ArrayList<View> pageViewList) {
230             mPageViewList = pageViewList;
231         }
232 
233         @Override
destroyItem(@onNull ViewGroup container, int position, @NonNull Object object)234         public void destroyItem(@NonNull ViewGroup container, int position,
235                                 @NonNull Object object) {
236             if (mPageViewList.get(position) != null) {
237                 container.removeView(mPageViewList.get(position));
238             }
239         }
240 
241         @NonNull
242         @Override
instantiateItem(ViewGroup container, int position)243         public Object instantiateItem(ViewGroup container, int position) {
244             container.addView(mPageViewList.get(position));
245             return mPageViewList.get(position);
246         }
247 
248         @Override
getCount()249         public int getCount() {
250             return mPageViewList.size();
251         }
252 
253         @Override
isViewFromObject(@onNull View view, @NonNull Object object)254         public boolean isViewFromObject(@NonNull View view, @NonNull Object object) {
255             return object == view;
256         }
257     }
258 
createPageListener()259     private ViewPager.OnPageChangeListener createPageListener() {
260         return new ViewPager.OnPageChangeListener() {
261             @Override
262             public void onPageScrolled(
263                     int position, float positionOffset, int positionOffsetPixels) {
264                 if (positionOffset != 0) {
265                     for (int i = 0; i < mPageList.size(); i++) {
266                         mViewPagerItems[i].setVisibility(View.VISIBLE);
267                     }
268                 } else {
269                     updateIndicator(position);
270                 }
271             }
272 
273             @Override
274             public void onPageSelected(int position) {}
275 
276             @Override
277             public void onPageScrollStateChanged(int state) {}
278         };
279     }
280 
281     private void updateIndicator(int position) {
282         for (int i = 0; i < mPageList.size(); i++) {
283             if (position == i) {
284                 mDotIndicators[i].setBackgroundResource(
285                         R.drawable.ic_color_page_indicator_focused);
286                 mViewPagerItems[i].setVisibility(View.VISIBLE);
287             } else {
288                 mDotIndicators[i].setBackgroundResource(
289                         R.drawable.ic_color_page_indicator_unfocused);
290                 mViewPagerItems[i].setVisibility(View.INVISIBLE);
291             }
292         }
293 
294         if (position >= 0 && position < mViewPagerItems.length - 1) {
295             mButtonStartSkip.setVisibility(View.VISIBLE);
296             mButtonEndNext.setVisibility(View.VISIBLE);
297             mButtonStartRestart.setVisibility(View.GONE);
298             mButtonEndDone.setVisibility(View.GONE);
299         } else {
300             mButtonStartSkip.setVisibility(View.GONE);
301             mButtonEndNext.setVisibility(View.GONE);
302             mButtonStartRestart.setVisibility(View.VISIBLE);
303             mButtonEndDone.setVisibility(View.VISIBLE);
304         }
305     }
306 
307     private Point getScreenSize() {
308         final Point size = new Point();
309         final Activity activity = (Activity) mContext;
310         final Display display = activity.getWindowManager().getDefaultDisplay();
311         display.getSize(size);
312         return size;
313     }
314 
315     private int getNavigationBarHeight() {
316         final Activity activity = (Activity) mContext;
317         WindowInsets insets =
318                 activity.getWindowManager().getCurrentWindowMetrics().getWindowInsets();
319         return insets.getInsets(WindowInsets.Type.navigationBars()).bottom;
320     }
321 
322     private boolean isGestureNavigationEnabled() {
323         return NAV_BAR_MODE_GESTURAL == mContext.getResources().getInteger(
324                 com.android.internal.R.integer.config_navBarInteractionMode);
325     }
326 }
327