Add an activity for testing WindowInsetsController
Which tests these APIs:
- WindowInsetsController#setSystemBarsBehavior
- WindowInsetsController#show
- WindowInsetsController#hide
- WindowInsetsController#addOnControllableInsetsChangedListener
- WindowInsetsController#controlWindowInsetsAnimation
- WindowInsetsAnimationController#setInsetsAndAlpha
- WindowInsetsAnimationController#finish
Bug: 118118435
Test: Manual test
Change-Id: I7f365007abf2c3e6f04e1493e8be7aaef07cb807
diff --git a/tests/WindowInsetsTests/AndroidManifest.xml b/tests/WindowInsetsTests/AndroidManifest.xml
index 5978054..61dd9d4 100644
--- a/tests/WindowInsetsTests/AndroidManifest.xml
+++ b/tests/WindowInsetsTests/AndroidManifest.xml
@@ -16,18 +16,24 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.google.android.test.windowinsetstests">
+ package="com.google.android.test.windowinsetstests">
- <application android:label="@string/activity_title">
- <activity android:name=".WindowInsetsActivity"
- android:theme="@style/appTheme"
- android:windowSoftInputMode="adjustResize"
- android:exported="true">
-
+ <application android:label="@string/application_title">
+ <activity android:name=".WindowInsetsTestsMainActivity"
+ android:exported="true">
<intent-filter>
- <action android:name="android.intent.action.MAIN"/>
- <category android:name="android.intent.category.LAUNCHER"/>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
+
+ <activity android:name=".ChatActivity"
+ android:label="@string/chat_activity_title"
+ android:theme="@style/chat"
+ android:windowSoftInputMode="adjustResize" />
+
+ <activity android:name=".ControllerActivity"
+ android:label="@string/controller_activity_title"
+ android:theme="@style/controller" />
</application>
</manifest>
diff --git a/tests/WindowInsetsTests/res/layout/window_inset_activity.xml b/tests/WindowInsetsTests/res/layout/chat_activity.xml
similarity index 100%
rename from tests/WindowInsetsTests/res/layout/window_inset_activity.xml
rename to tests/WindowInsetsTests/res/layout/chat_activity.xml
diff --git a/tests/WindowInsetsTests/res/layout/controller_activity.xml b/tests/WindowInsetsTests/res/layout/controller_activity.xml
new file mode 100644
index 0000000..d51a4dd
--- /dev/null
+++ b/tests/WindowInsetsTests/res/layout/controller_activity.xml
@@ -0,0 +1,106 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+
+<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <LinearLayout
+ android:id="@+id/content"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+ <Spinner
+ android:id="@+id/spinnerBehavior"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="10dp"
+ android:layout_marginBottom="20dp" />
+
+ <ToggleButton
+ android:id="@+id/toggleButtonStatus"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:checked="true"
+ android:text="Status Bars Toggle Button"
+ android:textOff="Status Bars Invisible"
+ android:textOn="Status Bars Visible" />
+
+ <SeekBar
+ android:id="@+id/seekBarStatus"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="10dp"
+ android:layout_marginBottom="20dp"
+ android:max="10000"
+ android:progress="10000" />
+
+ <ToggleButton
+ android:id="@+id/toggleButtonNavigation"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:checked="true"
+ android:text="Navigation Bars Toggle Button"
+ android:textOff="Navigation Bars Invisible"
+ android:textOn="Navigation Bars Visible" />
+
+ <SeekBar
+ android:id="@+id/seekBarNavigation"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="10dp"
+ android:layout_marginBottom="20dp"
+ android:max="10000"
+ android:progress="10000" />
+
+ <ToggleButton
+ android:id="@+id/toggleButtonIme"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:checked="true"
+ android:text="IME Toggle Button"
+ android:textOff="IME Invisible"
+ android:textOn="IME Visible" />
+
+ <SeekBar
+ android:id="@+id/seekBarIme"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="10dp"
+ android:layout_marginBottom="20dp"
+ android:max="10000"
+ android:progress="0" />
+
+ <TextView
+ android:id="@+id/textViewControllableInsets"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_margin="5dp" />
+
+ <EditText
+ android:id="@+id/editText"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:ems="10"
+ android:hint="For testing IME..."
+ android:inputType="text"
+ android:text="" />
+
+ </LinearLayout>
+
+</ScrollView>
\ No newline at end of file
diff --git a/tests/WindowInsetsTests/res/layout/main_activity.xml b/tests/WindowInsetsTests/res/layout/main_activity.xml
new file mode 100644
index 0000000..621ed89
--- /dev/null
+++ b/tests/WindowInsetsTests/res/layout/main_activity.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+ <Button
+ android:id="@+id/chat_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/chat_activity_title"
+ android:textAllCaps="false"/>
+
+ <Button
+ android:id="@+id/controller_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/controller_activity_title"
+ android:textAllCaps="false"/>
+
+</LinearLayout>
diff --git a/tests/WindowInsetsTests/res/values/strings.xml b/tests/WindowInsetsTests/res/values/strings.xml
index 2b8e5f3d..1a236c6 100644
--- a/tests/WindowInsetsTests/res/values/strings.xml
+++ b/tests/WindowInsetsTests/res/values/strings.xml
@@ -16,5 +16,14 @@
-->
<resources>
- <string name="activity_title">New Insets Chat</string>
+ <string name="application_title">Window Insets Tests</string>
+ <string name="chat_activity_title">New Insets Chat</string>
+ <string name="controller_activity_title">Window Insets Controller</string>
+
+ <!-- The item positions should match the flag values respectively. -->
+ <string-array name="behaviors">
+ <item>BEHAVIOR_SHOW_BARS_BY_TOUCH</item>
+ <item>BEHAVIOR_SHOW_BARS_BY_SWIPE</item>
+ <item>BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE</item>
+ </string-array>
</resources>
diff --git a/tests/WindowInsetsTests/res/values/styles.xml b/tests/WindowInsetsTests/res/values/styles.xml
index 220671f..a84ffbe 100644
--- a/tests/WindowInsetsTests/res/values/styles.xml
+++ b/tests/WindowInsetsTests/res/values/styles.xml
@@ -17,7 +17,7 @@
<resources>
- <style name="appTheme" parent="@style/Theme.MaterialComponents.Light">
+ <style name="chat" parent="@style/Theme.MaterialComponents.Light">
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
@@ -63,5 +63,11 @@
<dimen name="bubble_padding">8dp</dimen>
<dimen name="bubble_padding_side">16dp</dimen>
+ <style name="controller" parent="android:Theme.Material">
+ <item name="android:colorPrimaryDark">#111111</item>
+ <item name="android:navigationBarColor">#111111</item>
+ <item name="android:colorPrimary">#222222</item>
+ <item name="android:colorAccent">#33ccff</item>
+ </style>
</resources>
\ No newline at end of file
diff --git a/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/WindowInsetsActivity.java b/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/ChatActivity.java
similarity index 96%
rename from tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/WindowInsetsActivity.java
rename to tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/ChatActivity.java
index 498cb7c..ba12acb 100644
--- a/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/WindowInsetsActivity.java
+++ b/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/ChatActivity.java
@@ -30,7 +30,6 @@
import android.graphics.Insets;
import android.os.Bundle;
import android.util.AttributeSet;
-import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
@@ -39,8 +38,6 @@
import android.view.WindowInsetsAnimation.Callback;
import android.view.WindowInsetsAnimationControlListener;
import android.view.WindowInsetsAnimationController;
-import android.view.WindowInsetsController;
-import android.view.WindowInsetsController.OnControllableInsetsChangedListener;
import android.view.animation.LinearInterpolator;
import android.widget.LinearLayout;
@@ -49,7 +46,7 @@
import androidx.appcompat.app.AppCompatActivity;
-public class WindowInsetsActivity extends AppCompatActivity {
+public class ChatActivity extends AppCompatActivity {
private View mRoot;
@@ -58,7 +55,7 @@
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- setContentView(R.layout.window_inset_activity);
+ setContentView(R.layout.chat_activity);
setSupportActionBar(findViewById(R.id.toolbar));
@@ -71,7 +68,7 @@
mRoot.setOnTouchListener(new View.OnTouchListener() {
private final ViewConfiguration mViewConfiguration =
- ViewConfiguration.get(WindowInsetsActivity.this);
+ ViewConfiguration.get(ChatActivity.this);
WindowInsetsAnimationController mAnimationController;
WindowInsetsAnimationControlListener mCurrentRequest;
boolean mRequestedController = false;
diff --git a/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/ControllerActivity.java b/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/ControllerActivity.java
new file mode 100644
index 0000000..beb4049
--- /dev/null
+++ b/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/ControllerActivity.java
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.test.windowinsetstests;
+
+import android.app.Activity;
+import android.graphics.Insets;
+import android.os.Bundle;
+import android.view.View;
+import android.view.WindowInsets;
+import android.view.WindowInsets.Type;
+import android.view.WindowInsetsAnimationControlListener;
+import android.view.WindowInsetsAnimationController;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.CompoundButton;
+import android.widget.SeekBar;
+import android.widget.Spinner;
+import android.widget.TextView;
+import android.widget.ToggleButton;
+
+public class ControllerActivity extends Activity implements View.OnApplyWindowInsetsListener {
+
+ private ToggleButton mToggleStatus;
+ private SeekBar mSeekStatus;
+ private ToggleButton mToggleNavigation;
+ private SeekBar mSeekNavigation;
+ private ToggleButton mToggleIme;
+ private SeekBar mSeekIme;
+ private TextView mTextControllableInsets;
+ private boolean[] mNotFromUser = {false};
+ private WindowInsets mLastInsets;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.controller_activity);
+ final Spinner spinnerBehavior = findViewById(R.id.spinnerBehavior);
+ ArrayAdapter<CharSequence> adapterBehavior = ArrayAdapter.createFromResource(this,
+ R.array.behaviors, android.R.layout.simple_spinner_item);
+ adapterBehavior.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+ spinnerBehavior.setAdapter(adapterBehavior);
+ spinnerBehavior.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
+ @Override
+ public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
+ parent.getWindowInsetsController().setSystemBarsBehavior(position);
+ }
+
+ @Override
+ public void onNothingSelected(AdapterView<?> parent) { }
+ });
+ mToggleStatus = findViewById(R.id.toggleButtonStatus);
+ mToggleStatus.setTag(mNotFromUser);
+ mToggleStatus.setOnCheckedChangeListener(new ToggleListener(Type.statusBars()));
+ mSeekStatus = findViewById(R.id.seekBarStatus);
+ mSeekStatus.setOnSeekBarChangeListener(new SeekBarListener(Type.statusBars()));
+ mToggleNavigation = findViewById(R.id.toggleButtonNavigation);
+ mToggleNavigation.setTag(mNotFromUser);
+ mToggleNavigation.setOnCheckedChangeListener(new ToggleListener(Type.navigationBars()));
+ mSeekNavigation = findViewById(R.id.seekBarNavigation);
+ mSeekNavigation.setOnSeekBarChangeListener(new SeekBarListener(Type.navigationBars()));
+ mToggleIme = findViewById(R.id.toggleButtonIme);
+ mToggleIme.setTag(mNotFromUser);
+ mToggleIme.setOnCheckedChangeListener(new ToggleListener(Type.ime()));
+ mSeekIme = findViewById(R.id.seekBarIme);
+ mSeekIme.setOnSeekBarChangeListener(new SeekBarListener(Type.ime()));
+ mTextControllableInsets = findViewById(R.id.textViewControllableInsets);
+ final View contentView = findViewById(R.id.content);
+ contentView.setOnApplyWindowInsetsListener(this);
+ contentView.getWindowInsetsController().addOnControllableInsetsChangedListener(
+ (c, types) -> mTextControllableInsets.setText("ControllableInsetsTypes=" + types));
+ }
+
+ @Override
+ public WindowInsets onApplyWindowInsets(View v, WindowInsets insets) {
+ mNotFromUser[0] = true;
+ updateWidgets(insets, Type.statusBars(), mToggleStatus, mSeekStatus);
+ updateWidgets(insets, Type.navigationBars(), mToggleNavigation, mSeekNavigation);
+ updateWidgets(insets, Type.ime(), mToggleIme, mSeekIme);
+ mLastInsets = insets;
+ mNotFromUser[0] = false;
+
+ // Prevent triggering system gestures while controlling seek bars.
+ final Insets gestureInsets = insets.getInsets(Type.systemGestures());
+ v.setPadding(gestureInsets.left, 0, gestureInsets.right, 0);
+
+ return v.onApplyWindowInsets(insets);
+ }
+
+ private void updateWidgets(WindowInsets insets, int types, ToggleButton toggle, SeekBar seek) {
+ final boolean isVisible = insets.isVisible(types);
+ final boolean wasVisible = mLastInsets != null ? mLastInsets.isVisible(types) : !isVisible;
+ if (isVisible != wasVisible) {
+ toggle.setChecked(isVisible);
+ if (!seek.isPressed()) {
+ seek.setProgress(isVisible ? seek.getMax() : seek.getMin(), true /* animate*/);
+ }
+ }
+
+ }
+
+ private static class ToggleListener implements CompoundButton.OnCheckedChangeListener {
+
+ private final @Type.InsetsType int mTypes;
+
+ ToggleListener(int types) {
+ mTypes = types;
+ }
+
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ if (((boolean[]) buttonView.getTag())[0]) {
+ // not from user
+ return;
+ }
+ if (isChecked) {
+ buttonView.getWindowInsetsController().show(mTypes);
+ } else {
+ buttonView.getWindowInsetsController().hide(mTypes);
+ }
+ }
+ }
+
+ private static class SeekBarListener implements SeekBar.OnSeekBarChangeListener {
+
+ private final @Type.InsetsType int mTypes;
+
+ private WindowInsetsAnimationController mController;
+
+ SeekBarListener(int types) {
+ mTypes = types;
+ }
+
+ @Override
+ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+ if (mController != null && fromUser) {
+ final int min = seekBar.getMin();
+ final float fraction = (progress - min) / (float) (seekBar.getMax() - min);
+ final Insets shownInsets = mController.getShownStateInsets();
+ final Insets hiddenInsets = mController.getHiddenStateInsets();
+ final Insets currentInsets = Insets.of(
+ (int) (0.5f + fraction * (shownInsets.left - hiddenInsets.left)),
+ (int) (0.5f + fraction * (shownInsets.top - hiddenInsets.top)),
+ (int) (0.5f + fraction * (shownInsets.right - hiddenInsets.right)),
+ (int) (0.5f + fraction * (shownInsets.bottom - hiddenInsets.bottom)));
+ mController.setInsetsAndAlpha(currentInsets, 1f /* alpha */, fraction);
+ }
+ }
+
+ @Override
+ public void onStartTrackingTouch(SeekBar seekBar) {
+ if (mController != null) {
+ return;
+ }
+ seekBar.getWindowInsetsController().controlWindowInsetsAnimation(mTypes,
+ -1 /* durationMs */, null /* interpolator */, null /* cancellationSignal */,
+ new WindowInsetsAnimationControlListener() {
+ @Override
+ public void onReady(WindowInsetsAnimationController controller, int types) {
+ mController = controller;
+ if (!seekBar.isPressed()) {
+ onStopTrackingTouch(seekBar);
+ }
+ }
+
+ @Override
+ public void onFinished(WindowInsetsAnimationController controller) {
+ mController = null;
+ }
+
+ @Override
+ public void onCancelled(WindowInsetsAnimationController controller) {
+ mController = null;
+ }
+ });
+ }
+
+ @Override
+ public void onStopTrackingTouch(SeekBar seekBar) {
+ final int min = seekBar.getMin();
+ final int max = seekBar.getMax();
+ final boolean shown = (seekBar.getProgress() - min) * 2 > max - min;
+ seekBar.setProgress(shown ? max : min);
+ if (mController != null) {
+ mController.finish(shown);
+ }
+ }
+ }
+}
diff --git a/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/WindowInsetsTestsMainActivity.java b/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/WindowInsetsTestsMainActivity.java
new file mode 100644
index 0000000..8b77a78
--- /dev/null
+++ b/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/WindowInsetsTestsMainActivity.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.test.windowinsetstests;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+
+public class WindowInsetsTestsMainActivity extends Activity {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.main_activity);
+
+ findViewById(R.id.chat_button).setOnClickListener(
+ v -> startActivity(new Intent(this, ChatActivity.class)));
+
+ findViewById(R.id.controller_button).setOnClickListener(
+ v -> startActivity(new Intent(this, ControllerActivity.class)));
+ }
+}