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