WindowInsetsTests: More compelling IME transition sample
Test: make WindowInsetsTests && adb install ...
Change-Id: I4320871bdc8184fac38921616e1a1322f8dbc804
diff --git a/tests/WindowInsetsTests/Android.bp b/tests/WindowInsetsTests/Android.bp
index 12395e7..7272152 100644
--- a/tests/WindowInsetsTests/Android.bp
+++ b/tests/WindowInsetsTests/Android.bp
@@ -18,5 +18,10 @@
resource_dirs: ["res"],
certificate: "platform",
platform_apis: true,
+ static_libs: [
+ "androidx.core_core",
+ "androidx.appcompat_appcompat",
+ "com.google.android.material_material",
+ ],
}
diff --git a/tests/WindowInsetsTests/AndroidManifest.xml b/tests/WindowInsetsTests/AndroidManifest.xml
index 8d33f70..0f6282e 100644
--- a/tests/WindowInsetsTests/AndroidManifest.xml
+++ b/tests/WindowInsetsTests/AndroidManifest.xml
@@ -20,7 +20,7 @@
<application android:label="@string/activity_title">
<activity android:name=".WindowInsetsActivity"
- android:theme="@android:style/Theme.Material"
+ android:theme="@style/appTheme"
android:windowSoftInputMode="adjustResize">
<intent-filter>
diff --git a/tests/WindowInsetsTests/res/drawable/bubble.xml b/tests/WindowInsetsTests/res/drawable/bubble.xml
new file mode 100644
index 0000000..26deb1e
--- /dev/null
+++ b/tests/WindowInsetsTests/res/drawable/bubble.xml
@@ -0,0 +1,23 @@
+<?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.
+ -->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+ <solid android:color="@color/bubble" />
+ <corners android:radius="@dimen/bubble_corner" />
+ <padding android:left="@dimen/bubble_padding_side" android:top="@dimen/bubble_padding"
+ android:right="@dimen/bubble_padding_side" android:bottom="@dimen/bubble_padding" />
+</shape>
\ No newline at end of file
diff --git a/tests/WindowInsetsTests/res/drawable/bubble_self.xml b/tests/WindowInsetsTests/res/drawable/bubble_self.xml
new file mode 100644
index 0000000..5f098a2
--- /dev/null
+++ b/tests/WindowInsetsTests/res/drawable/bubble_self.xml
@@ -0,0 +1,23 @@
+<?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.
+ -->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+ <solid android:color="@color/bubble_self" />
+ <corners android:radius="@dimen/bubble_corner" />
+ <padding android:left="@dimen/bubble_padding_side" android:top="@dimen/bubble_padding"
+ android:right="@dimen/bubble_padding_side" android:bottom="@dimen/bubble_padding" />
+</shape>
\ No newline at end of file
diff --git a/tests/WindowInsetsTests/res/drawable/ic_send.xml b/tests/WindowInsetsTests/res/drawable/ic_send.xml
new file mode 100644
index 0000000..15bc411
--- /dev/null
+++ b/tests/WindowInsetsTests/res/drawable/ic_send.xml
@@ -0,0 +1,27 @@
+<?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.
+ -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:autoMirrored="true"
+ android:width="24.0dp"
+ android:height="24.0dp"
+ android:viewportWidth="48.0"
+ android:viewportHeight="48.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M4.02,42.0L46.0,24.0 4.02,6.0 4.0,20.0l30.0,4.0 -30.0,4.0z"/>
+</vector>
diff --git a/tests/WindowInsetsTests/res/layout/message.xml b/tests/WindowInsetsTests/res/layout/message.xml
new file mode 100644
index 0000000..d6b29c3
--- /dev/null
+++ b/tests/WindowInsetsTests/res/layout/message.xml
@@ -0,0 +1,26 @@
+<?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.
+ -->
+
+<TextView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:background="@drawable/bubble"
+ android:textSize="32sp"
+ android:text="Hello World">
+
+</TextView>
\ No newline at end of file
diff --git a/tests/WindowInsetsTests/res/layout/message_self.xml b/tests/WindowInsetsTests/res/layout/message_self.xml
new file mode 100644
index 0000000..de34e48
--- /dev/null
+++ b/tests/WindowInsetsTests/res/layout/message_self.xml
@@ -0,0 +1,30 @@
+<?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.
+ -->
+
+<merge>
+ <include
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:aapt="http://schemas.android.com/aapt"
+ layout="@layout/message">
+ <aapt:attr name="android:theme">
+ <style>
+ <item name="android:layout_gravity">end</item>
+ <item name="bubbleBackground">@color/bubble_self</item>
+ </style>
+ </aapt:attr>
+ </include>
+</merge>
diff --git a/tests/WindowInsetsTests/res/layout/window_inset_activity.xml b/tests/WindowInsetsTests/res/layout/window_inset_activity.xml
index 38e0029..1b51c4f 100644
--- a/tests/WindowInsetsTests/res/layout/window_inset_activity.xml
+++ b/tests/WindowInsetsTests/res/layout/window_inset_activity.xml
@@ -17,17 +17,82 @@
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:gravity="center"
+ android:clipToPadding="false"
android:id="@+id/root">
- <Button
- android:id="@+id/button"
- android:layout_width="wrap_content"
- android:layout_height="48dp"
- android:text="Hello insets" />
+ <androidx.appcompat.widget.Toolbar
+ android:id="@+id/toolbar"
+ android:layout_width="match_parent"
+ android:layout_height="?attr/actionBarSize"
+ />
+ <FrameLayout
+ android:id="@+id/scrollView"
+ android:layout_height="0dp"
+ android:layout_width="match_parent"
+ android:paddingStart="8dp"
+ android:paddingEnd="8dp"
+ android:layout_weight="1">
+
+ <LinearLayout
+ android:orientation="vertical"
+ android:layout_gravity="bottom"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ style="@style/bubble"
+ android:text="Hey, look at this buttery smooth animation!" />
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ style="@style/bubble_self"
+ android:text="Wow, that's pretty neat, how does this work?" />
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ style="@style/bubble"
+ android:text="Using the new WindowInsets animation system of course!" />
+
+ </LinearLayout>
+
+ </FrameLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingStart="8dp"
+ android:paddingEnd="8dp"
+ android:id="@+id/editText">
+
+ <com.google.android.material.textfield.TextInputLayout
+ style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1">
+
+ <com.google.android.material.textfield.TextInputEditText
+ android:hint="Text message"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"/>
+
+ </com.google.android.material.textfield.TextInputLayout>
+
+ <com.google.android.material.floatingactionbutton.FloatingActionButton
+ android:id="@+id/floating_action_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ app:elevation="0dp"
+ app:fabSize="mini"
+ app:srcCompat="@drawable/ic_send"/>
+
+ </LinearLayout>
</LinearLayout>
diff --git a/tests/WindowInsetsTests/res/values/strings.xml b/tests/WindowInsetsTests/res/values/strings.xml
index 242823d..2b8e5f3d 100644
--- a/tests/WindowInsetsTests/res/values/strings.xml
+++ b/tests/WindowInsetsTests/res/values/strings.xml
@@ -16,5 +16,5 @@
-->
<resources>
- <string name="activity_title">Window Insets Tests</string>
+ <string name="activity_title">New Insets Chat</string>
</resources>
diff --git a/tests/WindowInsetsTests/res/values/styles.xml b/tests/WindowInsetsTests/res/values/styles.xml
new file mode 100644
index 0000000..220671f
--- /dev/null
+++ b/tests/WindowInsetsTests/res/values/styles.xml
@@ -0,0 +1,67 @@
+<?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.
+ -->
+
+<resources>
+
+ <style name="appTheme" parent="@style/Theme.MaterialComponents.Light">
+ <item name="windowActionBar">false</item>
+ <item name="windowNoTitle">true</item>
+
+ <item name="colorPrimary">@color/primaryColor</item>
+ <item name="colorPrimaryDark">@color/primaryDarkColor</item>
+ <item name="colorSecondary">?attr/colorPrimary</item>
+ <item name="colorOnSecondary">@color/primaryTextColor</item>
+
+ <!-- Window decor -->
+ <item name="android:statusBarColor">#ffffff</item>
+ <item name="android:windowLightStatusBar">true</item>
+ <item name="android:windowLightNavigationBar">true</item>
+ <item name="android:navigationBarColor">#ffffff</item>
+
+ </style>
+
+ <style name="bubble_base" parent="">
+ <item name="android:textSize">20sp</item>
+ <item name="android:layout_marginBottom">16dp</item>
+ </style>
+
+ <style name="bubble" parent="@style/bubble_base">
+ <item name="android:layout_marginEnd">56dp</item>
+ <item name="android:background">@drawable/bubble</item>
+ <item name="android:layout_gravity">start</item>
+ </style>
+
+ <style name="bubble_self" parent="@style/bubble_base">
+ <item name="android:layout_marginStart">56dp</item>
+ <item name="android:background">@drawable/bubble_self</item>
+ <item name="android:layout_gravity">end</item>
+ </style>
+
+ <color name="primaryColor">#1c3fef</color>
+ <color name="primaryLightColor">#6f6bff</color>
+ <color name="primaryDarkColor">#0016bb</color>
+ <color name="primaryTextColor">#ffffff</color>
+
+ <color name="bubble">#eeeeee</color>
+ <color name="bubble_self">#D8DCF0</color>
+
+ <dimen name="bubble_corner">16dp</dimen>
+ <dimen name="bubble_padding">8dp</dimen>
+ <dimen name="bubble_padding_side">16dp</dimen>
+
+
+</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/WindowInsetsActivity.java
index b9f5ac0..8e6f198 100644
--- a/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/WindowInsetsActivity.java
+++ b/tests/WindowInsetsTests/src/com/google/android/test/windowinsetstests/WindowInsetsActivity.java
@@ -18,134 +18,216 @@
import static android.view.WindowInsetsAnimation.Callback.DISPATCH_MODE_STOP;
-import android.animation.ObjectAnimator;
-import android.animation.TypeEvaluator;
-import android.animation.ValueAnimator;
-import android.app.Activity;
+import static java.lang.Math.max;
+import static java.lang.Math.min;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
import android.graphics.Insets;
import android.os.Bundle;
-import android.util.Property;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
import android.view.View;
+import android.view.ViewConfiguration;
+import android.view.Window;
import android.view.WindowInsets;
import android.view.WindowInsets.Type;
import android.view.WindowInsetsAnimation;
+import android.view.WindowInsetsAnimation.Callback;
import android.view.WindowInsetsAnimationControlListener;
import android.view.WindowInsetsAnimationController;
+import android.view.animation.LinearInterpolator;
+import android.widget.LinearLayout;
-import com.google.android.test.windowinsetstests.R;
+import androidx.appcompat.app.AppCompatActivity;
+import java.util.ArrayList;
import java.util.List;
-public class WindowInsetsActivity extends Activity {
+public class WindowInsetsActivity extends AppCompatActivity {
private View mRoot;
- private View mButton;
- private static class InsetsProperty extends Property<WindowInsetsAnimationController, Insets> {
-
- private final View mViewToAnimate;
- private final Insets mShowingInsets;
-
- public InsetsProperty(View viewToAnimate, Insets showingInsets) {
- super(Insets.class, "Insets");
- mViewToAnimate = viewToAnimate;
- mShowingInsets = showingInsets;
- }
-
- @Override
- public Insets get(WindowInsetsAnimationController object) {
- return object.getCurrentInsets();
- }
-
- @Override
- public void set(WindowInsetsAnimationController object, Insets value) {
- object.setInsetsAndAlpha(value, 1.0f, 0.5f);
- if (mShowingInsets.bottom != 0) {
- mViewToAnimate.setTranslationY(mShowingInsets.bottom - value.bottom);
- } else if (mShowingInsets.right != 0) {
- mViewToAnimate.setTranslationX(mShowingInsets.right - value.right);
- } else if (mShowingInsets.left != 0) {
- mViewToAnimate.setTranslationX(value.left - mShowingInsets.left);
- }
- }
- };
-
- float startY;
- float endY;
- WindowInsetsAnimation imeAnim;
+ final ArrayList<Transition> mTransitions = new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.window_inset_activity);
+
+ setSupportActionBar(findViewById(R.id.toolbar));
+
mRoot = findViewById(R.id.root);
- mButton = findViewById(R.id.button);
- mButton.setOnClickListener(v -> {
- if (!v.getRootWindowInsets().isVisible(Type.ime())) {
- v.getWindowInsetsController().show(Type.ime());
- } else {
- v.getWindowInsetsController().hide(Type.ime());
+ mRoot.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
+ | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
+
+ mTransitions.add(new Transition(findViewById(R.id.scrollView)));
+ mTransitions.add(new Transition(findViewById(R.id.editText)));
+
+ mRoot.setOnTouchListener(new View.OnTouchListener() {
+ private final ViewConfiguration mViewConfiguration =
+ ViewConfiguration.get(WindowInsetsActivity.this);
+ WindowInsetsAnimationController mAnimationController;
+ WindowInsetsAnimationControlListener mCurrentRequest;
+ boolean mRequestedController = false;
+ float mDown = 0;
+ float mCurrent = 0;
+ Insets mDownInsets = Insets.NONE;
+ boolean mShownAtDown;
+
+ @Override
+ public boolean onTouch(View v, MotionEvent event) {
+ mCurrent = event.getY();
+ switch (event.getAction()) {
+ case MotionEvent.ACTION_DOWN:
+ mDown = event.getY();
+ mDownInsets = v.getRootWindowInsets().getInsets(Type.ime());
+ mShownAtDown = v.getRootWindowInsets().isVisible(Type.ime());
+ mRequestedController = false;
+ mCurrentRequest = null;
+ break;
+ case MotionEvent.ACTION_MOVE:
+ if (mAnimationController != null) {
+ updateInset();
+ } else if (Math.abs(mDown - event.getY())
+ > mViewConfiguration.getScaledTouchSlop()
+ && !mRequestedController) {
+ mRequestedController = true;
+ v.getWindowInsetsController().controlWindowInsetsAnimation(Type.ime(),
+ 1000, new LinearInterpolator(),
+ mCurrentRequest = new WindowInsetsAnimationControlListener() {
+ @Override
+ public void onReady(
+ @NonNull WindowInsetsAnimationController controller,
+ int types) {
+ if (mCurrentRequest == this) {
+ mAnimationController = controller;
+ updateInset();
+ } else {
+ controller.finish(mShownAtDown);
+ }
+ }
+
+ @Override
+ public void onCancelled() {
+ mAnimationController = null;
+ }
+ });
+ }
+ break;
+ case MotionEvent.ACTION_UP:
+ case MotionEvent.ACTION_CANCEL:
+ if (mAnimationController != null) {
+ boolean isCancel = event.getAction() == MotionEvent.ACTION_CANCEL;
+ mAnimationController.finish(isCancel ? mShownAtDown : !mShownAtDown);
+ mAnimationController = null;
+ }
+ mRequestedController = false;
+ mCurrentRequest = null;
+ break;
+ }
+ return true;
+ }
+
+ private void updateInset() {
+ int inset = (int) (mDownInsets.bottom + (mDown - mCurrent));
+ final int hidden = mAnimationController.getHiddenStateInsets().bottom;
+ final int shown = mAnimationController.getShownStateInsets().bottom;
+ final int start = mShownAtDown ? shown : hidden;
+ final int end = mShownAtDown ? hidden : shown;
+ inset = max(inset, hidden);
+ inset = min(inset, shown);
+ mAnimationController.setInsetsAndAlpha(
+ Insets.of(0, 0, 0, inset),
+ 1f, (inset - start) / (float)(end - start));
}
});
- mRoot.setWindowInsetsAnimationCallback(new WindowInsetsAnimation.Callback(
- DISPATCH_MODE_STOP) {
+
+ mRoot.setOnApplyWindowInsetsListener(new View.OnApplyWindowInsetsListener() {
+ @Override
+ public WindowInsets onApplyWindowInsets(View v, WindowInsets insets) {
+ mRoot.setPadding(insets.getSystemWindowInsetLeft(),
+ insets.getSystemWindowInsetTop(), insets.getSystemWindowInsetRight(),
+ insets.getSystemWindowInsetBottom());
+ return WindowInsets.CONSUMED;
+ }
+ });
+
+ mRoot.setWindowInsetsAnimationCallback(new Callback(DISPATCH_MODE_STOP) {
@Override
public void onPrepare(WindowInsetsAnimation animation) {
- if ((animation.getTypeMask() & Type.ime()) != 0) {
- imeAnim = animation;
- }
- startY = mButton.getTop();
+ mTransitions.forEach(it -> it.onPrepare(animation));
}
@Override
public WindowInsets onProgress(WindowInsets insets,
- List<WindowInsetsAnimation> runningAnimations) {
- mButton.setY(startY + (endY - startY) * imeAnim.getInterpolatedFraction());
+ @NonNull List<WindowInsetsAnimation> runningAnimations) {
+ mTransitions.forEach(it -> it.onProgress(insets));
return insets;
}
@Override
public WindowInsetsAnimation.Bounds onStart(WindowInsetsAnimation animation,
WindowInsetsAnimation.Bounds bounds) {
- endY = mButton.getTop();
+ mTransitions.forEach(Transition::onStart);
return bounds;
}
@Override
public void onEnd(WindowInsetsAnimation animation) {
- imeAnim = null;
+ mTransitions.forEach(it -> it.onFinish(animation));
}
});
}
@Override
- public void onAttachedToWindow() {
- super.onAttachedToWindow();
+ public void onResume() {
+ super.onResume();
+ // TODO: move this to onCreate once setDecorFitsSystemWindows can be safely called there.
+ getWindow().getDecorView().post(() -> getWindow().setDecorFitsSystemWindows(false));
+ }
- TypeEvaluator<Insets> evaluator = (fraction, startValue, endValue) -> Insets.of(
- (int)(startValue.left + fraction * (endValue.left - startValue.left)),
- (int)(startValue.top + fraction * (endValue.top - startValue.top)),
- (int)(startValue.right + fraction * (endValue.right - startValue.right)),
- (int)(startValue.bottom + fraction * (endValue.bottom - startValue.bottom)));
+ static class Transition {
+ private int mEndBottom;
+ private int mStartBottom;
+ private final View mView;
+ private WindowInsetsAnimation mInsetsAnimation;
- WindowInsetsAnimationControlListener listener = new WindowInsetsAnimationControlListener() {
- @Override
- public void onReady(WindowInsetsAnimationController controller, int types) {
- ObjectAnimator animator = ObjectAnimator.ofObject(controller,
- new InsetsProperty(findViewById(R.id.button),
- controller.getShownStateInsets()),
- evaluator, controller.getShownStateInsets(),
- controller.getHiddenStateInsets());
- animator.setRepeatCount(ValueAnimator.INFINITE);
- animator.setRepeatMode(ValueAnimator.REVERSE);
- animator.start();
+ Transition(View root) {
+ mView = root;
+ }
+
+ void onPrepare(WindowInsetsAnimation animation) {
+ if ((animation.getTypeMask() & Type.ime()) != 0) {
+ mInsetsAnimation = animation;
}
+ mStartBottom = mView.getBottom();
+ }
- @Override
- public void onCancelled() {
+ void onProgress(WindowInsets insets) {
+ mView.setY(mStartBottom + (mEndBottom - mStartBottom)
+ * mInsetsAnimation.getInterpolatedFraction()
+ - mView.getHeight());
+ }
+ void onStart() {
+ mEndBottom = mView.getBottom();
+ }
+
+ void onFinish(WindowInsetsAnimation animation) {
+ if (mInsetsAnimation == animation) {
+ mInsetsAnimation = null;
}
- };
+ }
+ }
+
+ static class ImeLinearLayout extends LinearLayout {
+
+ public ImeLinearLayout(Context context,
+ @Nullable AttributeSet attrs) {
+ super(context, attrs);
+ }
}
}