Merge "Add folder UI animation." into ub-launcher3-master
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index cacd3fb..2e36bc0 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -88,6 +88,7 @@
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewGroup;
+import android.view.WindowManager;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.animation.OvershootInterpolator;
 import android.widget.Toast;
@@ -466,6 +467,10 @@
 
         mUserChangedCallbackCloseable = UserCache.INSTANCE.get(this).addUserChangeListener(
                 () -> getStateManager().goToState(NORMAL));
+
+        if (Utilities.ATLEAST_R) {
+            getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING);
+        }
     }
 
     protected LauncherOverlayManager getDefaultOverlay() {
diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java
index f1606ea..c841b7b 100644
--- a/src/com/android/launcher3/folder/Folder.java
+++ b/src/com/android/launcher3/folder/Folder.java
@@ -17,6 +17,7 @@
 package com.android.launcher3.folder;
 
 import static android.text.TextUtils.isEmpty;
+import static android.view.WindowInsetsAnimation.Callback.DISPATCH_MODE_STOP;
 
 import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_EXIT_DELAY;
 import static com.android.launcher3.LauncherState.NORMAL;
@@ -34,8 +35,10 @@
 import android.appwidget.AppWidgetHostView;
 import android.content.Context;
 import android.graphics.Canvas;
+import android.graphics.Insets;
 import android.graphics.Path;
 import android.graphics.Rect;
+import android.os.Build;
 import android.text.InputType;
 import android.text.Selection;
 import android.text.TextUtils;
@@ -47,11 +50,16 @@
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewDebug;
+import android.view.WindowInsets;
+import android.view.WindowInsetsAnimation;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.animation.AnimationUtils;
 import android.view.inputmethod.EditorInfo;
 import android.widget.TextView;
 
+import androidx.annotation.NonNull;
+import androidx.annotation.RequiresApi;
+
 import com.android.launcher3.AbstractFloatingView;
 import com.android.launcher3.Alarm;
 import com.android.launcher3.BubbleTextView;
@@ -252,6 +260,13 @@
         int measureSpec = MeasureSpec.UNSPECIFIED;
         mFooter.measure(measureSpec, measureSpec);
         mFooterHeight = mFooter.getMeasuredHeight();
+
+        if (Utilities.ATLEAST_R) {
+            WindowInsetsAnimation.Callback cb = new FolderWindowInsetsAnimationCallback(
+                    DISPATCH_MODE_STOP, this);
+
+            setWindowInsetsAnimationCallback(cb);
+        }
     }
 
     public boolean onLongClick(View v) {
@@ -373,6 +388,26 @@
         return false;
     }
 
+    @Override
+    public WindowInsets onApplyWindowInsets(WindowInsets windowInsets) {
+        if (Utilities.ATLEAST_R) {
+            this.setTranslationY(0);
+
+            if (windowInsets.isVisible(WindowInsets.Type.ime())) {
+                Insets keyboardInsets = windowInsets.getInsets(WindowInsets.Type.ime());
+                int folderHeightFromBottom = getHeightFromBottom();
+
+                if (keyboardInsets.bottom > folderHeightFromBottom) {
+                    // Translate this folder above the keyboard, then add the folder name's padding
+                    this.setTranslationY(folderHeightFromBottom - keyboardInsets.bottom
+                            - mFolderName.getPaddingBottom());
+                }
+            }
+        }
+
+        return windowInsets;
+    }
+
     public FolderIcon getFolderIcon() {
         return mFolderIcon;
     }
@@ -1646,4 +1681,64 @@
         mLauncher.getUserEventDispatcher()
                 .logLauncherEvent(mInfo.getFolderLabelStateLauncherEvent(fromState, toState));
     }
+
+    /** Returns the height of the current folder's bottom edge from the bottom of the screen. */
+    private int getHeightFromBottom() {
+        DragLayer.LayoutParams layoutParams = (DragLayer.LayoutParams) getLayoutParams();
+        int folderBottomPx = layoutParams.y + layoutParams.height;
+        int windowBottomPx = mLauncher.getDeviceProfile().heightPx;
+
+        return windowBottomPx - folderBottomPx;
+    }
+
+    /** Callback that animates a folder sliding up above the ime. */
+    @RequiresApi(api = Build.VERSION_CODES.R)
+    private static class FolderWindowInsetsAnimationCallback
+            extends WindowInsetsAnimation.Callback {
+
+        private final Folder mFolder;
+        float mFolderTranslationStart;
+        float mFolderTranslationEnd;
+
+        FolderWindowInsetsAnimationCallback(int dispatchMode, Folder folder) {
+            super(dispatchMode);
+
+            mFolder = folder;
+        }
+
+        @Override
+        public void onPrepare(@NonNull WindowInsetsAnimation animation) {
+            mFolderTranslationStart = mFolder.getTranslationY();
+        }
+
+        @NonNull
+        @Override
+        public WindowInsetsAnimation.Bounds onStart(
+                @NonNull WindowInsetsAnimation animation,
+                @NonNull WindowInsetsAnimation.Bounds bounds) {
+            mFolderTranslationEnd = mFolder.getTranslationY();
+
+            mFolder.setTranslationY(mFolderTranslationStart);
+
+            return super.onStart(animation, bounds);
+        }
+
+        @NonNull
+        @Override
+        public WindowInsets onProgress(@NonNull WindowInsets windowInsets,
+                @NonNull List<WindowInsetsAnimation> list) {
+            if (list.size() == 0) {
+                mFolder.setTranslationY(0);
+
+                return windowInsets;
+            }
+            float progress = list.get(0).getInterpolatedFraction();
+
+            mFolder.setTranslationY(
+                    Utilities.mapRange(progress, mFolderTranslationStart, mFolderTranslationEnd));
+
+            return windowInsets;
+        }
+
+    }
 }