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