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 return types == 0 ? "none" : WindowInsets.Type.toString(types); 92 } 93 94 @Override onApplyWindowInsets(View v, WindowInsets insets)95 public WindowInsets onApplyWindowInsets(View v, WindowInsets insets) { 96 mNotFromUser[0] = true; 97 updateWidgets(insets, Type.statusBars(), mToggleStatus, mSeekStatus); 98 updateWidgets(insets, Type.navigationBars(), mToggleNavigation, mSeekNavigation); 99 updateWidgets(insets, Type.ime(), mToggleIme, mSeekIme); 100 mLastInsets = insets; 101 mNotFromUser[0] = false; 102 103 // Prevent triggering system gestures while controlling seek bars. 104 final Insets gestureInsets = insets.getInsets(Type.systemGestures()); 105 v.setPadding(gestureInsets.left, 0, gestureInsets.right, 0); 106 107 return v.onApplyWindowInsets(insets); 108 } 109 updateWidgets(WindowInsets insets, int types, ToggleButton toggle, SeekBar seek)110 private void updateWidgets(WindowInsets insets, int types, ToggleButton toggle, SeekBar seek) { 111 final boolean isVisible = insets.isVisible(types); 112 final boolean wasVisible = mLastInsets != null ? mLastInsets.isVisible(types) : !isVisible; 113 if (isVisible != wasVisible) { 114 toggle.setChecked(isVisible); 115 if (!seek.isPressed()) { 116 seek.setProgress(isVisible ? seek.getMax() : seek.getMin(), true /* animate*/); 117 } 118 } 119 120 } 121 122 private static class ToggleListener implements CompoundButton.OnCheckedChangeListener { 123 124 private final @Type.InsetsType int mTypes; 125 ToggleListener(int types)126 ToggleListener(int types) { 127 mTypes = types; 128 } 129 130 @Override onCheckedChanged(CompoundButton buttonView, boolean isChecked)131 public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { 132 if (((boolean[]) buttonView.getTag())[0]) { 133 // not from user 134 return; 135 } 136 if (isChecked) { 137 buttonView.getWindowInsetsController().show(mTypes); 138 } else { 139 buttonView.getWindowInsetsController().hide(mTypes); 140 } 141 } 142 } 143 144 private static class SeekBarListener implements SeekBar.OnSeekBarChangeListener { 145 146 private final @Type.InsetsType int mTypes; 147 148 private WindowInsetsAnimationController mController; 149 SeekBarListener(int types)150 SeekBarListener(int types) { 151 mTypes = types; 152 } 153 154 @Override onProgressChanged(SeekBar seekBar, int progress, boolean fromUser)155 public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { 156 if (mController != null && fromUser) { 157 final int min = seekBar.getMin(); 158 final float fraction = (progress - min) / (float) (seekBar.getMax() - min); 159 final Insets shownInsets = mController.getShownStateInsets(); 160 final Insets hiddenInsets = mController.getHiddenStateInsets(); 161 final Insets currentInsets = Insets.of( 162 (int) (0.5f + fraction * (shownInsets.left - hiddenInsets.left)), 163 (int) (0.5f + fraction * (shownInsets.top - hiddenInsets.top)), 164 (int) (0.5f + fraction * (shownInsets.right - hiddenInsets.right)), 165 (int) (0.5f + fraction * (shownInsets.bottom - hiddenInsets.bottom))); 166 mController.setInsetsAndAlpha(currentInsets, 1f /* alpha */, fraction); 167 } 168 } 169 170 @Override onStartTrackingTouch(SeekBar seekBar)171 public void onStartTrackingTouch(SeekBar seekBar) { 172 if (mController != null) { 173 return; 174 } 175 seekBar.getWindowInsetsController().controlWindowInsetsAnimation(mTypes, 176 -1 /* durationMs */, null /* interpolator */, null /* cancellationSignal */, 177 new WindowInsetsAnimationControlListener() { 178 @Override 179 public void onReady(WindowInsetsAnimationController controller, int types) { 180 mController = controller; 181 if (!seekBar.isPressed()) { 182 onStopTrackingTouch(seekBar); 183 } 184 } 185 186 @Override 187 public void onFinished(WindowInsetsAnimationController controller) { 188 mController = null; 189 } 190 191 @Override 192 public void onCancelled(WindowInsetsAnimationController controller) { 193 mController = null; 194 } 195 }); 196 } 197 198 @Override onStopTrackingTouch(SeekBar seekBar)199 public void onStopTrackingTouch(SeekBar seekBar) { 200 final int min = seekBar.getMin(); 201 final int max = seekBar.getMax(); 202 final boolean shown = (seekBar.getProgress() - min) * 2 > max - min; 203 seekBar.setProgress(shown ? max : min); 204 if (mController != null) { 205 mController.finish(shown); 206 } 207 } 208 } 209 } 210