• 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 
17 package com.google.android.test.windowinsetstests;
18 
19 import android.app.Activity;
20 import android.graphics.Insets;
21 import android.os.Bundle;
22 import android.view.View;
23 import android.view.WindowInsets;
24 import android.view.WindowInsets.Type;
25 import android.view.WindowInsetsAnimationControlListener;
26 import android.view.WindowInsetsAnimationController;
27 import android.widget.AdapterView;
28 import android.widget.ArrayAdapter;
29 import android.widget.CompoundButton;
30 import android.widget.SeekBar;
31 import android.widget.Spinner;
32 import android.widget.TextView;
33 import android.widget.ToggleButton;
34 
35 public class ControllerActivity extends Activity implements View.OnApplyWindowInsetsListener {
36 
37     private ToggleButton mToggleStatus;
38     private SeekBar mSeekStatus;
39     private ToggleButton mToggleNavigation;
40     private SeekBar mSeekNavigation;
41     private ToggleButton mToggleIme;
42     private SeekBar mSeekIme;
43     private TextView mTextControllableInsets;
44     private boolean[] mNotFromUser = {false};
45     private WindowInsets mLastInsets;
46 
47     @Override
onCreate(Bundle savedInstanceState)48     protected void onCreate(Bundle savedInstanceState) {
49         super.onCreate(savedInstanceState);
50         setContentView(R.layout.controller_activity);
51         final Spinner spinnerBehavior = findViewById(R.id.spinnerBehavior);
52         ArrayAdapter<CharSequence> adapterBehavior = ArrayAdapter.createFromResource(this,
53                 R.array.behaviors, android.R.layout.simple_spinner_item);
54         adapterBehavior.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
55         spinnerBehavior.setAdapter(adapterBehavior);
56         spinnerBehavior.setSelection(
57                 spinnerBehavior.getWindowInsetsController().getSystemBarsBehavior());
58         spinnerBehavior.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
59             @Override
60             public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
61                 parent.getWindowInsetsController().setSystemBarsBehavior(position);
62             }
63 
64             @Override
65             public void onNothingSelected(AdapterView<?> parent) { }
66         });
67         mToggleStatus = findViewById(R.id.toggleButtonStatus);
68         mToggleStatus.setTag(mNotFromUser);
69         mToggleStatus.setOnCheckedChangeListener(new ToggleListener(Type.statusBars()));
70         mSeekStatus = findViewById(R.id.seekBarStatus);
71         mSeekStatus.setOnSeekBarChangeListener(new SeekBarListener(Type.statusBars()));
72         mToggleNavigation = findViewById(R.id.toggleButtonNavigation);
73         mToggleNavigation.setTag(mNotFromUser);
74         mToggleNavigation.setOnCheckedChangeListener(new ToggleListener(Type.navigationBars()));
75         mSeekNavigation = findViewById(R.id.seekBarNavigation);
76         mSeekNavigation.setOnSeekBarChangeListener(new SeekBarListener(Type.navigationBars()));
77         mToggleIme = findViewById(R.id.toggleButtonIme);
78         mToggleIme.setTag(mNotFromUser);
79         mToggleIme.setOnCheckedChangeListener(new ToggleListener(Type.ime()));
80         mSeekIme = findViewById(R.id.seekBarIme);
81         mSeekIme.setOnSeekBarChangeListener(new SeekBarListener(Type.ime()));
82         mTextControllableInsets = findViewById(R.id.textViewControllableInsets);
83         final View contentView = findViewById(R.id.content);
84         contentView.setOnApplyWindowInsetsListener(this);
85         contentView.getWindowInsetsController().addOnControllableInsetsChangedListener(
86                 (c, types) -> mTextControllableInsets.setText(
87                         "ControllableInsetsTypes:\n" + insetsTypesToString(types)));
88     }
89 
insetsTypesToString(int types)90     private static String insetsTypesToString(int types) {
91         if (types == 0) {
92             return "none";
93         }
94         final StringBuilder sb = new StringBuilder();
95         if ((types & Type.statusBars()) != 0) {
96             types &= ~Type.statusBars();
97             sb.append("statusBars ");
98         }
99         if ((types & Type.navigationBars()) != 0) {
100             types &= ~Type.navigationBars();
101             sb.append("navigationBars ");
102         }
103         if ((types & Type.captionBar()) != 0) {
104             types &= ~Type.captionBar();
105             sb.append("captionBar ");
106         }
107         if ((types & Type.ime()) != 0) {
108             types &= ~Type.ime();
109             sb.append("ime ");
110         }
111         if ((types & Type.systemGestures()) != 0) {
112             types &= ~Type.systemGestures();
113             sb.append("systemGestures ");
114         }
115         if ((types & Type.mandatorySystemGestures()) != 0) {
116             types &= ~Type.mandatorySystemGestures();
117             sb.append("mandatorySystemGestures ");
118         }
119         if ((types & Type.tappableElement()) != 0) {
120             types &= ~Type.tappableElement();
121             sb.append("tappableElement ");
122         }
123         if ((types & Type.displayCutout()) != 0) {
124             types &= ~Type.displayCutout();
125             sb.append("displayCutout ");
126         }
127         if (types != 0) {
128             sb.append("unknownTypes:").append(types);
129         }
130         return sb.toString();
131     }
132 
133     @Override
onApplyWindowInsets(View v, WindowInsets insets)134     public WindowInsets onApplyWindowInsets(View v, WindowInsets insets) {
135         mNotFromUser[0] = true;
136         updateWidgets(insets, Type.statusBars(), mToggleStatus, mSeekStatus);
137         updateWidgets(insets, Type.navigationBars(), mToggleNavigation, mSeekNavigation);
138         updateWidgets(insets, Type.ime(), mToggleIme, mSeekIme);
139         mLastInsets = insets;
140         mNotFromUser[0] = false;
141 
142         // Prevent triggering system gestures while controlling seek bars.
143         final Insets gestureInsets =  insets.getInsets(Type.systemGestures());
144         v.setPadding(gestureInsets.left, 0, gestureInsets.right, 0);
145 
146         return v.onApplyWindowInsets(insets);
147     }
148 
updateWidgets(WindowInsets insets, int types, ToggleButton toggle, SeekBar seek)149     private void updateWidgets(WindowInsets insets, int types, ToggleButton toggle, SeekBar seek) {
150         final boolean isVisible = insets.isVisible(types);
151         final boolean wasVisible = mLastInsets != null ? mLastInsets.isVisible(types) : !isVisible;
152         if (isVisible != wasVisible) {
153             toggle.setChecked(isVisible);
154             if (!seek.isPressed()) {
155                 seek.setProgress(isVisible ? seek.getMax() : seek.getMin(), true /* animate*/);
156             }
157         }
158 
159     }
160 
161     private static class ToggleListener implements CompoundButton.OnCheckedChangeListener {
162 
163         private final @Type.InsetsType int mTypes;
164 
ToggleListener(int types)165         ToggleListener(int types) {
166             mTypes = types;
167         }
168 
169         @Override
onCheckedChanged(CompoundButton buttonView, boolean isChecked)170         public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
171             if (((boolean[]) buttonView.getTag())[0]) {
172                 // not from user
173                 return;
174             }
175             if (isChecked) {
176                 buttonView.getWindowInsetsController().show(mTypes);
177             } else {
178                 buttonView.getWindowInsetsController().hide(mTypes);
179             }
180         }
181     }
182 
183     private static class SeekBarListener implements SeekBar.OnSeekBarChangeListener {
184 
185         private final @Type.InsetsType int mTypes;
186 
187         private WindowInsetsAnimationController mController;
188 
SeekBarListener(int types)189         SeekBarListener(int types) {
190             mTypes = types;
191         }
192 
193         @Override
onProgressChanged(SeekBar seekBar, int progress, boolean fromUser)194         public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
195             if (mController != null && fromUser) {
196                 final int min = seekBar.getMin();
197                 final float fraction = (progress - min) / (float) (seekBar.getMax() - min);
198                 final Insets shownInsets = mController.getShownStateInsets();
199                 final Insets hiddenInsets = mController.getHiddenStateInsets();
200                 final Insets currentInsets = Insets.of(
201                         (int) (0.5f + fraction * (shownInsets.left - hiddenInsets.left)),
202                         (int) (0.5f + fraction * (shownInsets.top - hiddenInsets.top)),
203                         (int) (0.5f + fraction * (shownInsets.right - hiddenInsets.right)),
204                         (int) (0.5f + fraction * (shownInsets.bottom - hiddenInsets.bottom)));
205                 mController.setInsetsAndAlpha(currentInsets, 1f /* alpha */, fraction);
206             }
207         }
208 
209         @Override
onStartTrackingTouch(SeekBar seekBar)210         public void onStartTrackingTouch(SeekBar seekBar) {
211             if (mController != null) {
212                 return;
213             }
214             seekBar.getWindowInsetsController().controlWindowInsetsAnimation(mTypes,
215                     -1  /* durationMs */, null /* interpolator */, null /* cancellationSignal */,
216                     new WindowInsetsAnimationControlListener() {
217                         @Override
218                         public void onReady(WindowInsetsAnimationController controller, int types) {
219                             mController = controller;
220                             if (!seekBar.isPressed()) {
221                                 onStopTrackingTouch(seekBar);
222                             }
223                         }
224 
225                         @Override
226                         public void onFinished(WindowInsetsAnimationController controller) {
227                             mController = null;
228                         }
229 
230                         @Override
231                         public void onCancelled(WindowInsetsAnimationController controller) {
232                             mController = null;
233                         }
234                     });
235         }
236 
237         @Override
onStopTrackingTouch(SeekBar seekBar)238         public void onStopTrackingTouch(SeekBar seekBar) {
239             final int min = seekBar.getMin();
240             final int max = seekBar.getMax();
241             final boolean shown = (seekBar.getProgress() - min) * 2 > max - min;
242             seekBar.setProgress(shown ? max : min);
243             if (mController != null) {
244                 mController.finish(shown);
245             }
246         }
247     }
248 }
249