ExactCalculator: Use MotionLayout to animate

Change-Id: Idd5745f37668f29adabfc429ba80fa313006b7fc
diff --git a/res/layout-land/activity_calculator.xml b/res/layout-land/activity_calculator.xml
index 58843f6..599bf5a 100644
--- a/res/layout-land/activity_calculator.xml
+++ b/res/layout-land/activity_calculator.xml
@@ -3,20 +3,32 @@
     SPDX-FileCopyrightText: 2023 The LineageOS Project
     SPDX-License-Identifier: Apache-2.0
 -->
-<androidx.constraintlayout.widget.ConstraintLayout
+<com.android.calculator2.DisplayMotionLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
-    xmlns:tools="http://schemas.android.com/tools"
+    android:id="@+id/main_calculator"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:orientation="vertical">
+    android:orientation="vertical"
+    app:layoutDescription="@xml/activity_calculator_scene">
+
+    <FrameLayout
+        android:id="@+id/history_frame"
+        android:layout_width="0dp"
+        android:layout_height="0dp"
+        app:layout_constraintBottom_toTopOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toTopOf="parent" />
 
     <include
         android:id="@+id/display"
         layout="@layout/display_one_line"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        app:layout_constraintBottom_toTopOf="@id/advanced_pad"
+        android:layout_width="0dp"
+        android:layout_height="0dp"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintHeight_percent="@dimen/layout_display_ratio"
+        app:layout_constraintStart_toStartOf="parent"
         app:layout_constraintTop_toTopOf="parent" />
 
     <include
@@ -24,8 +36,8 @@
         layout="@layout/advanced_pad"
         android:layout_width="0dp"
         android:layout_height="0dp"
-        app:layout_constraintBottom_toBottomOf="parent"
         app:layout_constraintEnd_toStartOf="@id/layout_vertical_guideline"
+        app:layout_constraintHeight_percent="@dimen/layout_pads_ratio"
         app:layout_constraintStart_toStartOf="parent"
         app:layout_constraintTop_toBottomOf="@id/display" />
 
@@ -34,16 +46,25 @@
         layout="@layout/input_pad"
         android:layout_width="0dp"
         android:layout_height="0dp"
-        app:layout_constraintBottom_toBottomOf="parent"
         app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintHeight_percent="@dimen/layout_pads_ratio"
         app:layout_constraintStart_toEndOf="@id/layout_vertical_guideline"
         app:layout_constraintTop_toBottomOf="@id/display" />
 
+    <FrameLayout
+        android:id="@+id/animation_helper"
+        android:layout_width="0dp"
+        android:layout_height="0dp"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintHeight_percent="@dimen/layout_pads_ratio"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toBottomOf="parent" />
+
     <androidx.constraintlayout.widget.Guideline
         android:id="@+id/layout_vertical_guideline"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:orientation="vertical"
-        app:layout_constraintGuide_percent="0.35" />
+        app:layout_constraintGuide_percent="@dimen/layout_advanced_pad_ratio" />
 
-</androidx.constraintlayout.widget.ConstraintLayout>
+</com.android.calculator2.DisplayMotionLayout>
diff --git a/res/layout/activity_calculator.xml b/res/layout/activity_calculator.xml
index 1a9b5c8..fb12379 100644
--- a/res/layout/activity_calculator.xml
+++ b/res/layout/activity_calculator.xml
@@ -15,38 +15,61 @@
   limitations under the License.
   -->
 
-<androidx.constraintlayout.widget.ConstraintLayout
+<com.android.calculator2.DisplayMotionLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
-    android:id="@+id/linearLayout"
+    android:id="@+id/main_calculator"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:orientation="vertical">
+    android:orientation="vertical"
+    app:layoutDescription="@xml/activity_calculator_scene">
+
+    <FrameLayout
+        android:id="@+id/history_frame"
+        android:layout_width="0dp"
+        android:layout_height="0dp"
+        app:layout_constraintBottom_toTopOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toTopOf="parent" />
 
     <include
         android:id="@+id/display"
         layout="@layout/display_two_line"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        app:layout_constraintBottom_toTopOf="@+id/advanced_pad"
+        android:layout_width="0dp"
+        android:layout_height="0dp"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintHeight_percent="@dimen/layout_display_ratio"
+        app:layout_constraintStart_toStartOf="parent"
         app:layout_constraintTop_toTopOf="parent" />
 
     <include
         android:id="@+id/advanced_pad"
         layout="@layout/advanced_pad"
-        android:layout_width="match_parent"
+        android:layout_width="0dp"
         android:layout_height="0dp"
-        app:layout_constraintBottom_toTopOf="@+id/input_pad"
-        app:layout_constraintTop_toBottomOf="@id/display"
-        app:layout_constraintVertical_weight="30" />
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintHeight_percent="@dimen/layout_advanced_pad_ratio"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toBottomOf="@id/display" />
 
     <include
         android:id="@+id/input_pad"
         layout="@layout/input_pad"
-        android:layout_width="match_parent"
+        android:layout_width="0dp"
         android:layout_height="0dp"
-        app:layout_constraintBottom_toBottomOf="parent"
-        app:layout_constraintTop_toBottomOf="@id/advanced_pad"
-        app:layout_constraintVertical_weight="70" />
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintHeight_percent="@dimen/layout_input_pad_ratio"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toBottomOf="@id/advanced_pad" />
 
-</androidx.constraintlayout.widget.ConstraintLayout>
+    <FrameLayout
+        android:id="@+id/animation_helper"
+        android:layout_width="0dp"
+        android:layout_height="0dp"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintHeight_percent="@dimen/layout_pads_ratio"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toBottomOf="parent" />
+
+</com.android.calculator2.DisplayMotionLayout>
diff --git a/res/layout/activity_calculator_main.xml b/res/layout/activity_calculator_main.xml
deleted file mode 100644
index 2e9d62d..0000000
--- a/res/layout/activity_calculator_main.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  Copyright (C) 2016 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.
-  -->
-
-<com.android.calculator2.DragLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/drag_layout"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent">
-
-    <include
-        android:id="@+id/main_calculator"
-        layout="@layout/activity_calculator" />
-
-    <FrameLayout
-        android:id="@+id/history_frame"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:visibility="invisible" />
-
-</com.android.calculator2.DragLayout>
diff --git a/res/layout/fragment_history.xml b/res/layout/fragment_history.xml
index 02222c8..2bc2d7a 100644
--- a/res/layout/fragment_history.xml
+++ b/res/layout/fragment_history.xml
@@ -39,8 +39,8 @@
         android:background="@color/display_background_color"
         android:clipChildren="false"
         android:clipToPadding="false"
+        android:nestedScrollingEnabled="false"
         android:paddingBottom="@dimen/history_divider_padding"
-        android:visibility="invisible"
         app:layoutManager="LinearLayoutManager"
         app:reverseLayout="true" />
 
diff --git a/res/values-land/ratios.xml b/res/values-land/ratios.xml
new file mode 100644
index 0000000..0db9819
--- /dev/null
+++ b/res/values-land/ratios.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    SPDX-FileCopyrightText: 2023 The LineageOS Project
+    SPDX-License-Identifier: Apache-2.0
+-->
+<resources>
+    <!-- layout_display_ratio + layout_pads_ratio = 1 -->
+    <item type="dimen" format="float" name="layout_display_ratio">0.4</item>
+    <item type="dimen" format="float" name="layout_pads_ratio">0.6</item>
+
+    <item type="dimen" format="float" name="layout_advanced_pad_ratio">0.33</item>
+</resources>
diff --git a/res/values/ratios.xml b/res/values/ratios.xml
new file mode 100644
index 0000000..5472928
--- /dev/null
+++ b/res/values/ratios.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    SPDX-FileCopyrightText: 2023 The LineageOS Project
+    SPDX-License-Identifier: Apache-2.0
+-->
+<resources>
+    <!-- layout_display_ratio + layout_pads_ratio = 1 -->
+    <item type="dimen" format="float" name="layout_display_ratio">0.3</item>
+    <!-- layout_pads_ratio = layout_advanced_pad_ratio + layout_input_pad_ratio -->
+    <item type="dimen" format="float" name="layout_pads_ratio">0.7</item>
+    <item type="dimen" format="float" name="layout_advanced_pad_ratio">0.2</item>
+    <item type="dimen" format="float" name="layout_input_pad_ratio">0.5</item>
+</resources>
diff --git a/res/xml-land/activity_calculator_scene.xml b/res/xml-land/activity_calculator_scene.xml
new file mode 100644
index 0000000..d65485a
--- /dev/null
+++ b/res/xml-land/activity_calculator_scene.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    SPDX-FileCopyrightText: 2023 The LineageOS Project
+    SPDX-License-Identifier: Apache-2.0
+-->
+<MotionScene xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:motion="http://schemas.android.com/apk/res-auto">
+
+    <Transition
+        motion:constraintSetEnd="@+id/end_state"
+        motion:constraintSetStart="@+id/start_state"
+        motion:duration="1000"
+        motion:layoutDuringTransition="honorRequest">
+        <OnSwipe
+            motion:dragDirection="dragDown"
+            motion:limitBoundsTo="@id/display"
+            motion:touchAnchorId="@id/display"
+            motion:touchAnchorSide="bottom" />
+    </Transition>
+
+    <ConstraintSet android:id="@+id/start_state" />
+
+    <ConstraintSet android:id="@+id/end_state">
+        <Constraint
+            android:id="@id/history_frame"
+            android:layout_width="match_parent"
+            motion:layout_constraintBottom_toTopOf="@id/display"
+            motion:layout_constraintTop_toTopOf="parent" />
+        <Constraint
+            android:id="@id/display"
+            android:layout_width="match_parent"
+            motion:layout_constraintBottom_toTopOf="@id/advanced_pad"
+            motion:layout_constraintHeight_percent="@dimen/layout_display_ratio" />
+        <Constraint
+            android:id="@id/advanced_pad"
+            motion:layout_constraintBottom_toBottomOf="@id/animation_helper"
+            motion:layout_constraintEnd_toStartOf="@id/layout_vertical_guideline"
+            motion:layout_constraintHeight_percent="@dimen/layout_pads_ratio"
+            motion:layout_constraintStart_toStartOf="parent" />
+        <Constraint
+            android:id="@id/input_pad"
+            motion:layout_constraintBottom_toBottomOf="@id/animation_helper"
+            motion:layout_constraintEnd_toEndOf="parent"
+            motion:layout_constraintHeight_percent="@dimen/layout_pads_ratio"
+            motion:layout_constraintStart_toEndOf="@id/layout_vertical_guideline" />
+    </ConstraintSet>
+</MotionScene>
diff --git a/res/xml/activity_calculator_scene.xml b/res/xml/activity_calculator_scene.xml
new file mode 100644
index 0000000..c25f933
--- /dev/null
+++ b/res/xml/activity_calculator_scene.xml
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    SPDX-FileCopyrightText: 2023 The LineageOS Project
+    SPDX-License-Identifier: Apache-2.0
+-->
+<MotionScene xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:motion="http://schemas.android.com/apk/res-auto">
+
+    <Transition
+        motion:constraintSetEnd="@+id/end_state"
+        motion:constraintSetStart="@+id/start_state"
+        motion:duration="1000"
+        motion:layoutDuringTransition="honorRequest">
+        <OnSwipe
+            motion:dragDirection="dragDown"
+            motion:limitBoundsTo="@id/display"
+            motion:touchAnchorId="@id/display"
+            motion:touchAnchorSide="bottom" />
+    </Transition>
+
+    <ConstraintSet android:id="@+id/start_state" />
+
+    <ConstraintSet android:id="@+id/end_state">
+        <Constraint
+            android:id="@id/history_frame"
+            android:layout_width="0dp"
+            android:layout_height="0dp"
+            motion:layout_constraintBottom_toTopOf="@id/display"
+            motion:layout_constraintEnd_toEndOf="parent"
+            motion:layout_constraintStart_toStartOf="parent"
+            motion:layout_constraintTop_toTopOf="parent" />
+        <Constraint
+            android:id="@+id/display"
+            android:layout_width="0dp"
+            android:layout_height="0dp"
+            motion:layout_constraintBottom_toTopOf="@id/advanced_pad"
+            motion:layout_constraintEnd_toEndOf="parent"
+            motion:layout_constraintHeight_percent="@dimen/layout_display_ratio"
+            motion:layout_constraintStart_toStartOf="parent" />
+        <Constraint
+            android:id="@id/advanced_pad"
+            android:layout_width="0dp"
+            android:layout_height="0dp"
+            motion:layout_constraintBottom_toTopOf="@id/input_pad"
+            motion:layout_constraintEnd_toEndOf="parent"
+            motion:layout_constraintHeight_percent="@dimen/layout_advanced_pad_ratio"
+            motion:layout_constraintStart_toStartOf="parent" />
+        <Constraint
+            android:id="@id/input_pad"
+            android:layout_width="0dp"
+            android:layout_height="0dp"
+            motion:layout_constraintBottom_toBottomOf="@id/animation_helper"
+            motion:layout_constraintEnd_toEndOf="parent"
+            motion:layout_constraintHeight_percent="@dimen/layout_input_pad_ratio"
+            motion:layout_constraintStart_toStartOf="parent" />
+    </ConstraintSet>
+</MotionScene>
diff --git a/src/com/android/calculator2/Calculator.java b/src/com/android/calculator2/Calculator.java
index 3fcf0af..a46b6d9 100644
--- a/src/com/android/calculator2/Calculator.java
+++ b/src/com/android/calculator2/Calculator.java
@@ -60,10 +60,10 @@
 import androidx.appcompat.app.ActionBar;
 import androidx.appcompat.app.AppCompatActivity;
 import androidx.appcompat.widget.Toolbar;
+import androidx.constraintlayout.motion.widget.MotionLayout;
 import androidx.core.content.ContextCompat;
 import androidx.fragment.app.Fragment;
 import androidx.fragment.app.FragmentManager;
-import androidx.fragment.app.FragmentTransaction;
 
 import com.android.calculator2.CalculatorFormula.OnTextSizeChangeListener;
 
@@ -78,8 +78,7 @@
 
 public class Calculator extends AppCompatActivity
         implements OnTextSizeChangeListener, AlertDialogFragment.OnClickListener,
-        Evaluator.EvaluationListener /* for main result */, DragLayout.CloseCallback,
-        DragLayout.DragCallback {
+        Evaluator.EvaluationListener /* for main result */ {
 
     private static final String TAG = "Calculator";
     /**
@@ -228,9 +227,7 @@
     private CalculatorFormula mFormulaText;
     private CalculatorResult mResultText;
     private HorizontalScrollView mFormulaContainer;
-    private DragLayout mDragLayout;
-
-    private View mMainCalculator;
+    private MotionLayout mMainCalculator;
 
     private TextView mInverseToggle;
     private TextView mModeToggle;
@@ -325,7 +322,7 @@
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
 
-        setContentView(R.layout.activity_calculator_main);
+        setContentView(R.layout.activity_calculator);
         setSupportActionBar((Toolbar) findViewById(R.id.toolbar));
 
         // Hide all default options in the ActionBar.
@@ -375,10 +372,31 @@
                 findViewById(R.id.op_sqr)
         };
 
-        mDragLayout = (DragLayout) findViewById(R.id.drag_layout);
-        mDragLayout.removeDragCallback(this);
-        mDragLayout.addDragCallback(this);
-        mDragLayout.setCloseCallback(this);
+        mMainCalculator.setTransitionListener(new MotionLayout.TransitionListener() {
+            @Override
+            public void onTransitionStarted(MotionLayout motionLayout, int startId, int endId) {
+                if (startId == R.id.start_state) {
+                    showHistoryFragment();
+                }
+            }
+
+            @Override
+            public void onTransitionChange(MotionLayout motionLayout, int startId, int endId,
+                                           float progress) {
+            }
+
+            @Override
+            public void onTransitionCompleted(MotionLayout motionLayout, int currentId) {
+                if (currentId == R.id.start_state) {
+                    removeHistoryFragment();
+                }
+            }
+
+            @Override
+            public void onTransitionTrigger(MotionLayout motionLayout, int triggerId,
+                                            boolean positive, float progress) {
+            }
+        });
 
         mFormulaText.setOnContextMenuClickListener(mOnFormulaContextMenuClickListener);
         mFormulaText.setOnDisplayMemoryOperationsListener(mOnDisplayMemoryOperationsListener);
@@ -409,7 +427,8 @@
         // If we did not do this, it would be possible to traverse to main Calculator elements from
         // HistoryFragment.
         mMainCalculator.setImportantForAccessibility(
-                mDragLayout.isOpen() ? View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
+                mMainCalculator.getCurrentState() == R.id.end_state
+                        ? View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
                         : View.IMPORTANT_FOR_ACCESSIBILITY_AUTO);
     }
 
@@ -487,12 +506,6 @@
         return mIsOneLine;
     }
 
-    @Override
-    protected void onDestroy() {
-        mDragLayout.removeDragCallback(this);
-        super.onDestroy();
-    }
-
     /**
      * Destroy the evaluator and close the underlying database.
      */
@@ -523,7 +536,8 @@
             stopActionModeOrContextMenu();
 
             final HistoryFragment historyFragment = getHistoryFragment();
-            if (mDragLayout.isOpen() && historyFragment != null) {
+            if (mMainCalculator.getCurrentState() == R.id.end_state
+                && historyFragment != null) {
                 historyFragment.stopActionModeOrContextMenu();
             }
         }
@@ -534,10 +548,9 @@
     public void onBackPressed() {
         if (!stopActionModeOrContextMenu()) {
             final HistoryFragment historyFragment = getHistoryFragment();
-            if (mDragLayout.isOpen() && historyFragment != null) {
-                if (!historyFragment.stopActionModeOrContextMenu()) {
-                    removeHistoryFragment();
-                }
+            if (mMainCalculator.getCurrentState() == R.id.end_state
+                && historyFragment != null) {
+                mMainCalculator.transitionToStart();
                 return;
             }
         }
@@ -1096,7 +1109,7 @@
     public boolean onOptionsItemSelected(MenuItem item) {
         int itemId = item.getItemId();
         if (itemId == R.id.menu_history) {
-            showHistoryFragment();
+            mMainCalculator.transitionToEnd();
             return true;
         } else if (itemId == R.id.menu_leading) {
             displayFull();
@@ -1111,43 +1124,6 @@
         return super.onOptionsItemSelected(item);
     }
 
-    /* Begin override CloseCallback method. */
-
-    @Override
-    public void onClose() {
-        removeHistoryFragment();
-    }
-
-    /* End override CloseCallback method. */
-
-    /* Begin override DragCallback methods */
-
-    public void onStartDraggingOpen() {
-        mDisplayView.hideToolbar();
-        showHistoryFragment();
-    }
-
-    @Override
-    public void onInstanceStateRestored(boolean isOpen) {
-    }
-
-    @Override
-    public void whileDragging(float yFraction) {
-    }
-
-    @Override
-    public boolean shouldCaptureView(View view, int x, int y) {
-        return view.getId() == R.id.history_frame
-            && (mDragLayout.isMoving() || mDragLayout.isViewUnder(view, x, y));
-    }
-
-    @Override
-    public int getDisplayHeight() {
-        return mDisplayView.getMeasuredHeight();
-    }
-
-    /* End override DragCallback methods */
-
     /**
      * Change evaluation state to one that's friendly to the history fragment.
      * Return false if that was not easily possible.
@@ -1186,15 +1162,12 @@
 
         final FragmentManager manager = getSupportFragmentManager();
         if (manager == null || manager.isDestroyed() || !prepareForHistory()) {
-            // If the history fragment can not be shown, close the draglayout.
-            mDragLayout.setClosed();
             return;
         }
 
         stopActionModeOrContextMenu();
         manager.beginTransaction()
                 .replace(R.id.history_frame, new HistoryFragment(), HistoryFragment.TAG)
-                .setTransition(FragmentTransaction.TRANSIT_NONE)
                 .addToBackStack(HistoryFragment.TAG)
                 .commit();
 
diff --git a/src/com/android/calculator2/DisplayMotionLayout.java b/src/com/android/calculator2/DisplayMotionLayout.java
new file mode 100644
index 0000000..77554dc
--- /dev/null
+++ b/src/com/android/calculator2/DisplayMotionLayout.java
@@ -0,0 +1,102 @@
+/*
+ * SPDX-FileCopyrightText: 2023 The LineageOS Project
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package com.android.calculator2;
+
+import android.content.Context;
+import android.graphics.PointF;
+import android.graphics.Rect;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.view.ViewConfiguration;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.constraintlayout.motion.widget.MotionLayout;
+
+public class DisplayMotionLayout extends MotionLayout {
+    private int mPointerId;
+    private boolean mIsScrolling;
+    private PointF mPreviousPoint;
+    private MotionEvent mPreviousEvent;
+    private final int mTouchSlop;
+    private boolean mOutOfBounds;
+
+    public DisplayMotionLayout(@NonNull Context context) {
+        this(context, null);
+    }
+
+    public DisplayMotionLayout(@NonNull Context context, @Nullable AttributeSet attributeSet) {
+        this(context, attributeSet, 0);
+    }
+
+    public DisplayMotionLayout(@NonNull Context context, @Nullable AttributeSet attributeSet,
+                               int i) {
+        super(context, attributeSet, i);
+        mPointerId = -1;
+        mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
+    }
+
+    @Override
+    public final boolean onInterceptTouchEvent(@NonNull MotionEvent motionEvent) {
+        switch (motionEvent.getActionMasked()) {
+            case MotionEvent.ACTION_DOWN:
+            case MotionEvent.ACTION_POINTER_DOWN:
+                Rect hitRect = new Rect();
+
+                findViewById(R.id.display).getHitRect(hitRect);
+                if (hitRect.contains((int) motionEvent.getX(), (int) motionEvent.getY())) {
+                    mPreviousPoint = new PointF(motionEvent.getX(),motionEvent.getY());
+                    mPointerId = motionEvent.getPointerId(0);
+                    mIsScrolling = false;
+                    mOutOfBounds = false;
+                    saveLastMotion(motionEvent);
+                } else {
+                    mPointerId = -1;
+                    mIsScrolling = false;
+                    mOutOfBounds = true;
+                    clearLastMotion();
+                }
+                break;
+            case MotionEvent.ACTION_UP:
+            case MotionEvent.ACTION_CANCEL:
+            case MotionEvent.ACTION_POINTER_UP:
+                mIsScrolling = false;
+                mOutOfBounds = false;
+                mPointerId = -1;
+                clearLastMotion();
+                break;
+            case MotionEvent.ACTION_MOVE:
+                int pointerIndex = motionEvent.findPointerIndex(mPointerId);
+                if (mPointerId != -1 && pointerIndex != -1 && !mOutOfBounds) {
+                    float y = Math.abs(motionEvent.getY(pointerIndex) - mPreviousPoint.y);
+                    if (y > mTouchSlop) {
+                        mIsScrolling = true;
+                        onTouchEvent(mPreviousEvent);
+                    }
+                }
+                break;
+        }
+        if (super.onInterceptTouchEvent(motionEvent)) {
+            return true;
+        }
+        return mIsScrolling & !mOutOfBounds;
+    }
+
+    private void saveLastMotion(@NonNull MotionEvent motionEvent) {
+        if (mPreviousEvent != null) {
+            mPreviousEvent.recycle();
+        }
+        mPreviousEvent = MotionEvent.obtain(motionEvent);
+    }
+
+    private void clearLastMotion()
+    {
+        if (mPreviousEvent != null) {
+            mPreviousEvent.recycle();
+            mPreviousEvent = null;
+        }
+    }
+}
diff --git a/src/com/android/calculator2/DragController.java b/src/com/android/calculator2/DragController.java
deleted file mode 100644
index 5b9b1c6..0000000
--- a/src/com/android/calculator2/DragController.java
+++ /dev/null
@@ -1,482 +0,0 @@
-/*
- * Copyright (C) 2016 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.android.calculator2;
-
-import android.animation.ArgbEvaluator;
-import androidx.recyclerview.widget.RecyclerView;
-import android.view.View;
-import android.widget.TextView;
-
-/**
- * Contains the logic for animating the recyclerview elements on drag.
- */
-public final class DragController {
-
-    private static final String TAG = "DragController";
-
-    private static final ArgbEvaluator mColorEvaluator = new ArgbEvaluator();
-
-    // References to views from the Calculator Display.
-    private CalculatorFormula mDisplayFormula;
-    private CalculatorResult mDisplayResult;
-    private View mToolbar;
-
-    private int mFormulaTranslationY;
-    private int mFormulaTranslationX;
-    private float mFormulaScale;
-    private float mResultScale;
-
-    private float mResultTranslationY;
-    private int mResultTranslationX;
-
-    private int mDisplayHeight;
-
-    private int mFormulaStartColor;
-    private int mFormulaEndColor;
-
-    private int mResultStartColor;
-    private int mResultEndColor;
-
-    // The padding at the bottom of the RecyclerView itself.
-    private int mBottomPaddingHeight;
-
-    private boolean mAnimationInitialized;
-
-    private boolean mOneLine;
-    private boolean mIsDisplayEmpty;
-
-    private AnimationController mAnimationController;
-
-    private Evaluator mEvaluator;
-
-    public void setEvaluator(Evaluator evaluator) {
-        mEvaluator = evaluator;
-    }
-
-    public void initializeController(boolean isResult, boolean oneLine, boolean isDisplayEmpty) {
-        mOneLine = oneLine;
-        mIsDisplayEmpty = isDisplayEmpty;
-        if (mIsDisplayEmpty) {
-            // Empty display
-            mAnimationController = new EmptyAnimationController();
-        } else if (isResult) {
-            // Result
-            mAnimationController = new ResultAnimationController();
-        } else {
-            // There is something in the formula field. There may or may not be
-            // a quick result.
-            mAnimationController = new AnimationController();
-        }
-    }
-
-    public void setDisplayFormula(CalculatorFormula formula) {
-        mDisplayFormula = formula;
-    }
-
-    public void setDisplayResult(CalculatorResult result) {
-        mDisplayResult = result;
-    }
-
-    public void setToolbar(View toolbar) {
-        mToolbar = toolbar;
-    }
-
-    public void animateViews(float yFraction, RecyclerView recyclerView) {
-        if (mDisplayFormula == null
-                || mDisplayResult == null
-                || mToolbar == null
-                || mEvaluator == null) {
-            // Bail if we aren't yet initialized.
-            return;
-        }
-
-        final HistoryAdapter.ViewHolder vh =
-                (HistoryAdapter.ViewHolder) recyclerView.findViewHolderForAdapterPosition(0);
-        if (yFraction > 0 && vh != null) {
-            recyclerView.setVisibility(View.VISIBLE);
-        }
-        if (vh != null && !mIsDisplayEmpty
-                && vh.getItemViewType() == HistoryAdapter.HISTORY_VIEW_TYPE) {
-            final AlignedTextView formula = vh.getFormula();
-            final CalculatorResult result = vh.getResult();
-            final TextView date = vh.getDate();
-            final View divider = vh.getDivider();
-
-            if (!mAnimationInitialized) {
-                mBottomPaddingHeight = recyclerView.getPaddingBottom();
-
-                mAnimationController.initializeScales(formula, result);
-
-                mAnimationController.initializeColorAnimators(formula, result);
-
-                mAnimationController.initializeFormulaTranslationX(formula);
-
-                mAnimationController.initializeFormulaTranslationY(formula, result);
-
-                mAnimationController.initializeResultTranslationX(result);
-
-                mAnimationController.initializeResultTranslationY(result);
-
-                mAnimationInitialized = true;
-            }
-
-            result.setScaleX(mAnimationController.getResultScale(yFraction));
-            result.setScaleY(mAnimationController.getResultScale(yFraction));
-
-            formula.setScaleX(mAnimationController.getFormulaScale(yFraction));
-            formula.setScaleY(mAnimationController.getFormulaScale(yFraction));
-
-            formula.setPivotX(formula.getWidth() - formula.getPaddingEnd());
-            formula.setPivotY(formula.getHeight() - formula.getPaddingBottom());
-
-            result.setPivotX(result.getWidth() - result.getPaddingEnd());
-            result.setPivotY(result.getHeight() - result.getPaddingBottom());
-
-            formula.setTranslationX(mAnimationController.getFormulaTranslationX(yFraction));
-            formula.setTranslationY(mAnimationController.getFormulaTranslationY(yFraction));
-
-            result.setTranslationX(mAnimationController.getResultTranslationX(yFraction));
-            result.setTranslationY(mAnimationController.getResultTranslationY(yFraction));
-
-            formula.setTextColor((int) mColorEvaluator.evaluate(yFraction, mFormulaStartColor,
-                    mFormulaEndColor));
-
-            result.setTextColor((int) mColorEvaluator.evaluate(yFraction, mResultStartColor,
-                    mResultEndColor));
-
-            date.setTranslationY(mAnimationController.getDateTranslationY(yFraction));
-            divider.setTranslationY(mAnimationController.getDateTranslationY(yFraction));
-        } else if (mIsDisplayEmpty) {
-            // There is no current expression but we still need to collect information
-            // to translate the other viewholders.
-            if (!mAnimationInitialized) {
-                mAnimationController.initializeDisplayHeight();
-                mAnimationInitialized = true;
-            }
-        }
-
-        // Move up all ViewHolders above the current expression; if there is no current expression,
-        // we're translating all the viewholders.
-        for (int i = recyclerView.getChildCount() - 1;
-             i >= mAnimationController.getFirstTranslatedViewHolderIndex();
-             --i) {
-            final RecyclerView.ViewHolder vh2 =
-                    recyclerView.getChildViewHolder(recyclerView.getChildAt(i));
-            if (vh2 != null) {
-                final View view = vh2.itemView;
-                if (view != null) {
-                    view.setTranslationY(
-                        mAnimationController.getHistoryElementTranslationY(yFraction));
-                }
-            }
-        }
-    }
-
-    /**
-     * Reset all initialized values.
-     */
-    public void initializeAnimation(boolean isResult, boolean oneLine, boolean isDisplayEmpty) {
-        mAnimationInitialized = false;
-        initializeController(isResult, oneLine, isDisplayEmpty);
-    }
-
-    public interface AnimateTextInterface {
-
-        void initializeDisplayHeight();
-
-        void initializeColorAnimators(AlignedTextView formula, CalculatorResult result);
-
-        void initializeScales(AlignedTextView formula, CalculatorResult result);
-
-        void initializeFormulaTranslationX(AlignedTextView formula);
-
-        void initializeFormulaTranslationY(AlignedTextView formula, CalculatorResult result);
-
-        void initializeResultTranslationX(CalculatorResult result);
-
-        void initializeResultTranslationY(CalculatorResult result);
-
-        float getResultTranslationX(float yFraction);
-
-        float getResultTranslationY(float yFraction);
-
-        float getResultScale(float yFraction);
-
-        float getFormulaScale(float yFraction);
-
-        float getFormulaTranslationX(float yFraction);
-
-        float getFormulaTranslationY(float yFraction);
-
-        float getDateTranslationY(float yFraction);
-
-        float getHistoryElementTranslationY(float yFraction);
-
-        // Return the lowest index of the first Viewholder to be translated upwards.
-        // If there is no current expression, we translate all the viewholders; otherwise,
-        // we start at index 1.
-        int getFirstTranslatedViewHolderIndex();
-    }
-
-    // The default AnimationController when Display is in INPUT state and DisplayFormula is not
-    // empty. There may or may not be a quick result.
-    public class AnimationController implements DragController.AnimateTextInterface {
-
-        public void initializeDisplayHeight() {
-            // no-op
-        }
-
-        public void initializeColorAnimators(AlignedTextView formula, CalculatorResult result) {
-            mFormulaStartColor = mDisplayFormula.getCurrentTextColor();
-            mFormulaEndColor = formula.getCurrentTextColor();
-
-            mResultStartColor = mDisplayResult.getCurrentTextColor();
-            mResultEndColor = result.getCurrentTextColor();
-        }
-
-        public void initializeScales(AlignedTextView formula, CalculatorResult result) {
-            // Calculate the scale for the text
-            mFormulaScale = mDisplayFormula.getTextSize() / formula.getTextSize();
-        }
-
-        public void initializeFormulaTranslationY(AlignedTextView formula,
-                CalculatorResult result) {
-            if (mOneLine) {
-                // Disregard result since we set it to GONE in the one-line case.
-                mFormulaTranslationY =
-                        mDisplayFormula.getPaddingBottom() - formula.getPaddingBottom()
-                        - mBottomPaddingHeight;
-            } else {
-                // Baseline of formula moves by the difference in formula bottom padding and the
-                // difference in result height.
-                mFormulaTranslationY =
-                        mDisplayFormula.getPaddingBottom() - formula.getPaddingBottom()
-                                + mDisplayResult.getHeight() - result.getHeight()
-                                - mBottomPaddingHeight;
-            }
-        }
-
-        public void initializeFormulaTranslationX(AlignedTextView formula) {
-            // Right border of formula moves by the difference in formula end padding.
-            mFormulaTranslationX = mDisplayFormula.getPaddingEnd() - formula.getPaddingEnd();
-        }
-
-        public void initializeResultTranslationY(CalculatorResult result) {
-            // Baseline of result moves by the difference in result bottom padding.
-            mResultTranslationY = mDisplayResult.getPaddingBottom() - result.getPaddingBottom()
-            - mBottomPaddingHeight;
-        }
-
-        public void initializeResultTranslationX(CalculatorResult result) {
-            mResultTranslationX = mDisplayResult.getPaddingEnd() - result.getPaddingEnd();
-        }
-
-        public float getResultTranslationX(float yFraction) {
-            return mResultTranslationX * (yFraction - 1f);
-        }
-
-        public float getResultTranslationY(float yFraction) {
-            return mResultTranslationY * (yFraction - 1f);
-        }
-
-        public float getResultScale(float yFraction) {
-            return 1f;
-        }
-
-        public float getFormulaScale(float yFraction) {
-            return mFormulaScale + (1f - mFormulaScale) * yFraction;
-        }
-
-        public float getFormulaTranslationX(float yFraction) {
-            return mFormulaTranslationX * (yFraction - 1f);
-        }
-
-        public float getFormulaTranslationY(float yFraction) {
-            // Scale linearly between -FormulaTranslationY and 0.
-            return mFormulaTranslationY * (yFraction - 1f);
-        }
-
-        public float getDateTranslationY(float yFraction) {
-            // We also want the date to start out above the visible screen with
-            // this distance decreasing as it's pulled down.
-            // Account for the scaled formula height.
-            return -mToolbar.getHeight() * (1f - yFraction)
-                    + getFormulaTranslationY(yFraction)
-                    - mDisplayFormula.getHeight() /getFormulaScale(yFraction) * (1f - yFraction);
-        }
-
-        public float getHistoryElementTranslationY(float yFraction) {
-            return getDateTranslationY(yFraction);
-        }
-
-        public int getFirstTranslatedViewHolderIndex() {
-            return 1;
-        }
-    }
-
-    // The default AnimationController when Display is in RESULT state.
-    public class ResultAnimationController extends AnimationController
-            implements DragController.AnimateTextInterface {
-        @Override
-        public void initializeScales(AlignedTextView formula, CalculatorResult result) {
-            final float textSize = mDisplayResult.getTextSize() * mDisplayResult.getScaleX();
-            mResultScale = textSize / result.getTextSize();
-            mFormulaScale = 1f;
-        }
-
-        @Override
-        public void initializeFormulaTranslationY(AlignedTextView formula,
-                CalculatorResult result) {
-            // Baseline of formula moves by the difference in formula bottom padding and the
-            // difference in the result height.
-            mFormulaTranslationY = mDisplayFormula.getPaddingBottom() - formula.getPaddingBottom()
-                            + mDisplayResult.getHeight() - result.getHeight()
-                            - mBottomPaddingHeight;
-        }
-
-        @Override
-        public void initializeFormulaTranslationX(AlignedTextView formula) {
-            // Right border of formula moves by the difference in formula end padding.
-            mFormulaTranslationX = mDisplayFormula.getPaddingEnd() - formula.getPaddingEnd();
-        }
-
-        @Override
-        public void initializeResultTranslationY(CalculatorResult result) {
-            // Baseline of result moves by the difference in result bottom padding.
-            mResultTranslationY =  mDisplayResult.getPaddingBottom() - result.getPaddingBottom()
-                    - mDisplayResult.getTranslationY()
-                    - mBottomPaddingHeight;
-        }
-
-        @Override
-        public void initializeResultTranslationX(CalculatorResult result) {
-            mResultTranslationX = mDisplayResult.getPaddingEnd() - result.getPaddingEnd();
-        }
-
-        @Override
-        public float getResultTranslationX(float yFraction) {
-            return (mResultTranslationX * yFraction) - mResultTranslationX;
-        }
-
-        @Override
-        public float getResultTranslationY(float yFraction) {
-            return (mResultTranslationY * yFraction) - mResultTranslationY;
-        }
-
-        @Override
-        public float getFormulaTranslationX(float yFraction) {
-            return (mFormulaTranslationX * yFraction) -
-                    mFormulaTranslationX;
-        }
-
-        @Override
-        public float getFormulaTranslationY(float yFraction) {
-            return getDateTranslationY(yFraction);
-        }
-
-        @Override
-        public float getResultScale(float yFraction) {
-            return mResultScale - (mResultScale * yFraction) + yFraction;
-        }
-
-        @Override
-        public float getFormulaScale(float yFraction) {
-            return 1f;
-        }
-
-        @Override
-        public float getDateTranslationY(float yFraction) {
-            // We also want the date to start out above the visible screen with
-            // this distance decreasing as it's pulled down.
-            return -mToolbar.getHeight() * (1f - yFraction)
-                    + (mResultTranslationY * yFraction) - mResultTranslationY
-                    - mDisplayFormula.getPaddingTop() +
-                    (mDisplayFormula.getPaddingTop() * yFraction);
-        }
-
-        @Override
-        public int getFirstTranslatedViewHolderIndex() {
-            return 1;
-        }
-    }
-
-    // The default AnimationController when Display is completely empty.
-    public class EmptyAnimationController extends AnimationController
-            implements DragController.AnimateTextInterface {
-        @Override
-        public void initializeDisplayHeight() {
-            mDisplayHeight = mToolbar.getHeight() + mDisplayResult.getHeight()
-                    + mDisplayFormula.getHeight();
-        }
-
-        @Override
-        public void initializeScales(AlignedTextView formula, CalculatorResult result) {
-            // no-op
-        }
-
-        @Override
-        public void initializeFormulaTranslationY(AlignedTextView formula,
-                CalculatorResult result) {
-            // no-op
-        }
-
-        @Override
-        public void initializeFormulaTranslationX(AlignedTextView formula) {
-            // no-op
-        }
-
-        @Override
-        public void initializeResultTranslationY(CalculatorResult result) {
-            // no-op
-        }
-
-        @Override
-        public void initializeResultTranslationX(CalculatorResult result) {
-            // no-op
-        }
-
-        @Override
-        public float getResultTranslationX(float yFraction) {
-            return 0f;
-        }
-
-        @Override
-        public float getResultTranslationY(float yFraction) {
-            return 0f;
-        }
-
-        @Override
-        public float getFormulaScale(float yFraction) {
-            return 1f;
-        }
-
-        @Override
-        public float getDateTranslationY(float yFraction) {
-            return 0f;
-        }
-
-        @Override
-        public float getHistoryElementTranslationY(float yFraction) {
-            return -mDisplayHeight * (1f - yFraction) - mBottomPaddingHeight;
-        }
-
-        @Override
-        public int getFirstTranslatedViewHolderIndex() {
-            return 0;
-        }
-    }
-}
diff --git a/src/com/android/calculator2/DragLayout.java b/src/com/android/calculator2/DragLayout.java
deleted file mode 100644
index 4948f56..0000000
--- a/src/com/android/calculator2/DragLayout.java
+++ /dev/null
@@ -1,375 +0,0 @@
-/*
- * Copyright (C) 2016 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.android.calculator2;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ValueAnimator;
-import android.content.Context;
-import android.graphics.Canvas;
-import android.graphics.PointF;
-import android.graphics.Rect;
-import android.os.Bundle;
-import android.os.Parcelable;
-import androidx.core.view.ViewCompat;
-import androidx.customview.widget.ViewDragHelper;
-import android.util.AttributeSet;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.FrameLayout;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.CopyOnWriteArrayList;
-
-public class DragLayout extends ViewGroup {
-
-    private static final double AUTO_OPEN_SPEED_LIMIT = 600.0;
-    private static final String KEY_IS_OPEN = "IS_OPEN";
-    private static final String KEY_SUPER_STATE = "SUPER_STATE";
-
-    private FrameLayout mHistoryFrame;
-    private ViewDragHelper mDragHelper;
-
-    // No concurrency; allow modifications while iterating.
-    private final List<DragCallback> mDragCallbacks = new CopyOnWriteArrayList<>();
-    private CloseCallback mCloseCallback;
-
-    private final Map<Integer, PointF> mLastMotionPoints = new HashMap<>();
-    private final Rect mHitRect = new Rect();
-    private final List<Rect> mExclusionRects = new ArrayList<>();
-
-    private int mVerticalRange;
-    private boolean mIsOpen;
-
-    public DragLayout(Context context, AttributeSet attrs) {
-        super(context, attrs);
-    }
-
-    @Override
-    protected void onFinishInflate() {
-        mDragHelper = ViewDragHelper.create(this, 1.0f, new DragHelperCallback());
-        mHistoryFrame = (FrameLayout) findViewById(R.id.history_frame);
-        super.onFinishInflate();
-    }
-
-    @Override
-    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-        measureChildren(widthMeasureSpec, heightMeasureSpec);
-    }
-
-    @Override
-    protected void onLayout(boolean changed, int l, int t, int r, int b) {
-        int displayHeight = 0;
-        for (DragCallback c : mDragCallbacks) {
-            displayHeight = Math.max(displayHeight, c.getDisplayHeight());
-        }
-        mVerticalRange = getHeight() - displayHeight;
-
-        final int childCount = getChildCount();
-        for (int i = 0; i < childCount; ++i) {
-            final View child = getChildAt(i);
-
-            int top = 0;
-            if (child == mHistoryFrame) {
-                if (mDragHelper.getCapturedView() == mHistoryFrame
-                        && mDragHelper.getViewDragState() != ViewDragHelper.STATE_IDLE) {
-                    top = child.getTop();
-                } else {
-                    top = mIsOpen ? 0 : -mVerticalRange;
-                }
-            }
-            child.layout(0, top, child.getMeasuredWidth(), top + child.getMeasuredHeight());
-        }
-
-        if (changed) {
-            updateExclusionRects();
-        }
-    }
-
-    @Override
-    public void onDraw(Canvas canvas) {
-        super.onDraw(canvas);
-        updateExclusionRects();
-    }
-
-    @Override
-    protected Parcelable onSaveInstanceState() {
-        final Bundle bundle = new Bundle();
-        bundle.putParcelable(KEY_SUPER_STATE, super.onSaveInstanceState());
-        bundle.putBoolean(KEY_IS_OPEN, mIsOpen);
-        return bundle;
-    }
-
-    @Override
-    protected void onRestoreInstanceState(Parcelable state) {
-        if (state instanceof Bundle) {
-            final Bundle bundle = (Bundle) state;
-            mIsOpen = bundle.getBoolean(KEY_IS_OPEN);
-            mHistoryFrame.setVisibility(mIsOpen ? View.VISIBLE : View.INVISIBLE);
-            for (DragCallback c : mDragCallbacks) {
-                c.onInstanceStateRestored(mIsOpen);
-            }
-
-            state = bundle.getParcelable(KEY_SUPER_STATE);
-        }
-        super.onRestoreInstanceState(state);
-    }
-
-    private void saveLastMotion(MotionEvent event) {
-        final int action = event.getActionMasked();
-        switch (action) {
-            case MotionEvent.ACTION_DOWN:
-            case MotionEvent.ACTION_POINTER_DOWN: {
-                final int actionIndex = event.getActionIndex();
-                final int pointerId = event.getPointerId(actionIndex);
-                final PointF point = new PointF(event.getX(actionIndex), event.getY(actionIndex));
-                mLastMotionPoints.put(pointerId, point);
-                break;
-            }
-            case MotionEvent.ACTION_MOVE: {
-                for (int i = event.getPointerCount() - 1; i >= 0; --i) {
-                    final int pointerId = event.getPointerId(i);
-                    final PointF point = mLastMotionPoints.get(pointerId);
-                    if (point != null) {
-                        point.set(event.getX(i), event.getY(i));
-                    }
-                }
-                break;
-            }
-            case MotionEvent.ACTION_POINTER_UP: {
-                final int actionIndex = event.getActionIndex();
-                final int pointerId = event.getPointerId(actionIndex);
-                mLastMotionPoints.remove(pointerId);
-                break;
-            }
-            case MotionEvent.ACTION_UP:
-            case MotionEvent.ACTION_CANCEL: {
-                mLastMotionPoints.clear();
-                break;
-            }
-        }
-    }
-
-    @Override
-    public boolean onInterceptTouchEvent(MotionEvent event) {
-        saveLastMotion(event);
-        return mDragHelper.shouldInterceptTouchEvent(event);
-    }
-
-    @Override
-    public boolean onTouchEvent(MotionEvent event) {
-        // Workaround: do not process the error case where multi-touch would cause a crash.
-        if (event.getActionMasked() == MotionEvent.ACTION_MOVE
-                && mDragHelper.getViewDragState() == ViewDragHelper.STATE_DRAGGING
-                && mDragHelper.getActivePointerId() != ViewDragHelper.INVALID_POINTER
-                && event.findPointerIndex(mDragHelper.getActivePointerId()) == -1) {
-            mDragHelper.cancel();
-            return false;
-        }
-
-        saveLastMotion(event);
-
-        mDragHelper.processTouchEvent(event);
-        return true;
-    }
-
-    @Override
-    public void computeScroll() {
-        if (mDragHelper.continueSettling(true)) {
-            ViewCompat.postInvalidateOnAnimation(this);
-        }
-    }
-
-    private void onStartDragging() {
-        for (DragCallback c : mDragCallbacks) {
-            c.onStartDraggingOpen();
-        }
-        mHistoryFrame.setVisibility(VISIBLE);
-    }
-
-    public boolean isViewUnder(View view, int x, int y) {
-        view.getHitRect(mHitRect);
-        offsetDescendantRectToMyCoords((View) view.getParent(), mHitRect);
-        return mHitRect.contains(x, y);
-    }
-
-    public boolean isMoving() {
-        final int draggingState = mDragHelper.getViewDragState();
-        return draggingState == ViewDragHelper.STATE_DRAGGING
-                || draggingState == ViewDragHelper.STATE_SETTLING;
-    }
-
-    public boolean isOpen() {
-        return mIsOpen;
-    }
-
-    public void setClosed() {
-        mIsOpen = false;
-        mHistoryFrame.setVisibility(View.INVISIBLE);
-        if (mCloseCallback != null) {
-            mCloseCallback.onClose();
-        }
-    }
-
-    public Animator createAnimator(boolean toOpen) {
-        if (mIsOpen == toOpen) {
-            return ValueAnimator.ofFloat(0f, 1f).setDuration(0L);
-        }
-
-        mIsOpen = toOpen;
-        mHistoryFrame.setVisibility(VISIBLE);
-
-        final ValueAnimator animator = ValueAnimator.ofFloat(0f, 1f);
-        animator.addListener(new AnimatorListenerAdapter() {
-            @Override
-            public void onAnimationStart(Animator animation) {
-                mDragHelper.cancel();
-                mDragHelper.smoothSlideViewTo(mHistoryFrame, 0, mIsOpen ? 0 : -mVerticalRange);
-            }
-        });
-
-        return animator;
-    }
-
-    public void setCloseCallback(CloseCallback callback) {
-        mCloseCallback = callback;
-    }
-
-    public void addDragCallback(DragCallback callback) {
-        mDragCallbacks.add(callback);
-    }
-
-    public void removeDragCallback(DragCallback callback) {
-        mDragCallbacks.remove(callback);
-    }
-
-    private void updateExclusionRects() {
-        mExclusionRects.add(0, mHitRect);
-        setSystemGestureExclusionRects(mExclusionRects);
-    }
-
-    /**
-     * Callback when the layout is closed.
-     * We use this to pop the HistoryFragment off the backstack.
-     * We can't use a method in DragCallback because we get ConcurrentModificationExceptions on
-     * mDragCallbacks when executePendingTransactions() is called for popping the fragment off the
-     * backstack.
-     */
-    public interface CloseCallback {
-        void onClose();
-    }
-
-    /**
-     * Callbacks for coordinating with the RecyclerView or HistoryFragment.
-     */
-    public interface DragCallback {
-        // Callback when a drag to open begins.
-        void onStartDraggingOpen();
-
-        // Callback in onRestoreInstanceState.
-        void onInstanceStateRestored(boolean isOpen);
-
-        // Animate the RecyclerView text.
-        void whileDragging(float yFraction);
-
-        // Whether we should allow the view to be dragged.
-        boolean shouldCaptureView(View view, int x, int y);
-
-        int getDisplayHeight();
-    }
-
-    public class DragHelperCallback extends ViewDragHelper.Callback {
-        @Override
-        public void onViewDragStateChanged(int state) {
-            // The view stopped moving.
-            if (state == ViewDragHelper.STATE_IDLE
-                    && mDragHelper.getCapturedView().getTop() < -(mVerticalRange / 2)) {
-                setClosed();
-            }
-        }
-
-        @Override
-        public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
-            for (DragCallback c : mDragCallbacks) {
-                // Top is between [-mVerticalRange, 0].
-                c.whileDragging(1f + (float) top / mVerticalRange);
-            }
-        }
-
-        @Override
-        public int getViewVerticalDragRange(View child) {
-            return mVerticalRange;
-        }
-
-        @Override
-        public boolean tryCaptureView(View view, int pointerId) {
-            final PointF point = mLastMotionPoints.get(pointerId);
-            if (point == null) {
-                return false;
-            }
-
-            final int x = (int) point.x;
-            final int y = (int) point.y;
-
-            for (DragCallback c : mDragCallbacks) {
-                if (!c.shouldCaptureView(view, x, y)) {
-                    return false;
-                }
-            }
-            return true;
-        }
-
-        @Override
-        public int clampViewPositionVertical(View child, int top, int dy) {
-            return Math.max(Math.min(top, 0), -mVerticalRange);
-        }
-
-        @Override
-        public void onViewCaptured(View capturedChild, int activePointerId) {
-            super.onViewCaptured(capturedChild, activePointerId);
-
-            if (!mIsOpen) {
-                mIsOpen = true;
-                onStartDragging();
-            }
-        }
-
-        @Override
-        public void onViewReleased(View releasedChild, float xvel, float yvel) {
-            final boolean settleToOpen;
-            if (yvel > AUTO_OPEN_SPEED_LIMIT) {
-                // Speed has priority over position.
-                settleToOpen = true;
-            } else if (yvel < -AUTO_OPEN_SPEED_LIMIT) {
-                settleToOpen = false;
-            } else {
-                settleToOpen = releasedChild.getTop() > -(mVerticalRange / 2);
-            }
-
-            // If the view is not visible, then settle it closed, not open.
-            if (mDragHelper.settleCapturedViewAt(0, settleToOpen && mIsOpen ? 0
-                    : -mVerticalRange)) {
-                ViewCompat.postInvalidateOnAnimation(DragLayout.this);
-            }
-        }
-    }
-}
diff --git a/src/com/android/calculator2/HistoryAdapter.java b/src/com/android/calculator2/HistoryAdapter.java
index be50050..9eb6b42 100644
--- a/src/com/android/calculator2/HistoryAdapter.java
+++ b/src/com/android/calculator2/HistoryAdapter.java
@@ -75,10 +75,7 @@
         holder.mFormula.setText(item.getFormula());
         // Note: HistoryItems that are not the current expression will always have interesting ops.
         holder.mResult.setEvaluator(mEvaluator, item.getEvaluatorIndex());
-        if (item.getEvaluatorIndex() == Evaluator.HISTORY_MAIN_INDEX) {
-            holder.mDate.setText(R.string.title_current_expression);
-            holder.mResult.setVisibility(mIsOneLine ? View.GONE : View.VISIBLE);
-        } else {
+        if (item.getEvaluatorIndex() != Evaluator.HISTORY_MAIN_INDEX) {
             // If the previous item occurred on the same date, the current item does not need
             // a date header.
             if (shouldShowHeader(position, item)) {
@@ -145,12 +142,7 @@
     }
 
     private int getEvaluatorIndex(int position) {
-        if (mIsDisplayEmpty || mIsResultLayout) {
-            return (int) (mEvaluator.getMaxIndex() - position);
-        } else {
-            // Account for the additional "Current Expression" with the +1.
-            return (int) (mEvaluator.getMaxIndex() - position + 1);
-        }
+        return (int) (mEvaluator.getMaxIndex() - position);
     }
 
     private boolean shouldShowHeader(int position, HistoryItem item) {
diff --git a/src/com/android/calculator2/HistoryFragment.java b/src/com/android/calculator2/HistoryFragment.java
index a09bb53..9b0f4c0 100644
--- a/src/com/android/calculator2/HistoryFragment.java
+++ b/src/com/android/calculator2/HistoryFragment.java
@@ -18,13 +18,14 @@
 
 import static androidx.recyclerview.widget.RecyclerView.SCROLL_STATE_DRAGGING;
 
-import android.animation.Animator;
 import android.os.Bundle;
 import android.view.LayoutInflater;
 import android.view.MenuItem;
 import android.view.View;
 import android.view.ViewGroup;
 
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 import androidx.appcompat.widget.Toolbar;
 import androidx.core.content.ContextCompat;
 import androidx.fragment.app.Fragment;
@@ -32,16 +33,13 @@
 
 import java.util.ArrayList;
 
-public class HistoryFragment extends Fragment implements DragLayout.DragCallback {
+public class HistoryFragment extends Fragment {
 
     public static final String TAG = "HistoryFragment";
     public static final String CLEAR_DIALOG_TAG = "clear";
 
-    private final DragController mDragController = new DragController();
-
     private RecyclerView mRecyclerView;
     private HistoryAdapter mAdapter;
-    private DragLayout mDragLayout;
 
     private Evaluator mEvaluator;
 
@@ -61,9 +59,6 @@
         final View view = inflater.inflate(
                 R.layout.fragment_history, container, false /* attachToRoot */);
 
-        mDragLayout = (DragLayout) container.getRootView().findViewById(R.id.drag_layout);
-        mDragLayout.addDragCallback(this);
-
         mRecyclerView = (RecyclerView) view.findViewById(R.id.history_recycler_view);
         mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
             @Override
@@ -105,15 +100,14 @@
     }
 
     @Override
-    public void onActivityCreated(Bundle savedInstanceState) {
-        super.onActivityCreated(savedInstanceState);
+    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
+        super.onViewCreated(view, savedInstanceState);
 
         final Calculator activity = (Calculator) getActivity();
         mEvaluator = Evaluator.getInstance(activity);
         mAdapter.setEvaluator(mEvaluator);
 
         final boolean isResultLayout = activity.isResultLayout();
-        final boolean isOneLine = activity.isOneLine();
 
         // Snapshot display state here. For the rest of the lifecycle of this current
         // HistoryFragment, this is what we will consider the display state.
@@ -121,22 +115,10 @@
         final CalculatorExpr mainExpr = mEvaluator.getExpr(Evaluator.MAIN_INDEX);
         mIsDisplayEmpty = mainExpr == null || mainExpr.isEmpty();
 
-        initializeController(isResultLayout, isOneLine, mIsDisplayEmpty);
-
         final long maxIndex = mEvaluator.getMaxIndex();
 
         final ArrayList<HistoryItem> newDataSet = new ArrayList<>();
 
-        if (!mIsDisplayEmpty && !isResultLayout) {
-            // Add the current expression as the first element in the list (the layout is
-            // reversed and we want the current expression to be the last one in the
-            // RecyclerView).
-            // If we are in the result state, the result will animate to the last history
-            // element in the list and there will be no "Current Expression."
-            mEvaluator.copyMainToHistory();
-            newDataSet.add(new HistoryItem(Evaluator.HISTORY_MAIN_INDEX,
-                    System.currentTimeMillis(), mEvaluator.getExprAsSpannable(0)));
-        }
         for (long i = 0; i < maxIndex; ++i) {
             newDataSet.add(null);
         }
@@ -155,27 +137,9 @@
     }
 
     @Override
-    public void onStart() {
-        super.onStart();
-
-        final Calculator activity = (Calculator) getActivity();
-        mDragController.initializeAnimation(activity.isResultLayout(), activity.isOneLine(),
-                mIsDisplayEmpty);
-    }
-
-    @Override
-    public Animator onCreateAnimator(int transit, boolean enter, int nextAnim) {
-        return mDragLayout.createAnimator(enter);
-    }
-
-    @Override
     public void onDestroy() {
         super.onDestroy();
 
-        if (mDragLayout != null) {
-            mDragLayout.removeDragCallback(this);
-        }
-
         if (mEvaluator != null) {
             // Note that the view is destroyed when the fragment backstack is popped, so
             // these are essentially called when the DragLayout is closed.
@@ -183,16 +147,6 @@
         }
     }
 
-    private void initializeController(boolean isResult, boolean isOneLine, boolean isDisplayEmpty) {
-        mDragController.setDisplayFormula(
-                (CalculatorFormula) getActivity().findViewById(R.id.formula));
-        mDragController.setDisplayResult(
-                (CalculatorResult) getActivity().findViewById(R.id.result));
-        mDragController.setToolbar(getActivity().findViewById(R.id.toolbar));
-        mDragController.setEvaluator(mEvaluator);
-        mDragController.initializeController(isResult, isOneLine, isDisplayEmpty);
-    }
-
     public boolean stopActionModeOrContextMenu() {
         if (mRecyclerView == null) {
             return false;
@@ -208,37 +162,4 @@
         }
         return false;
     }
-
-    /* Begin override DragCallback methods. */
-
-    @Override
-    public void onStartDraggingOpen() {
-        // no-op
-    }
-
-    @Override
-    public void onInstanceStateRestored(boolean isOpen) {
-        if (isOpen) {
-            mRecyclerView.setVisibility(View.VISIBLE);
-        }
-    }
-
-    @Override
-    public void whileDragging(float yFraction) {
-        if (isVisible() || isRemoving()) {
-            mDragController.animateViews(yFraction, mRecyclerView);
-        }
-    }
-
-    @Override
-    public boolean shouldCaptureView(View view, int x, int y) {
-        return !mRecyclerView.canScrollVertically(1 /* scrolling down */);
-    }
-
-    @Override
-    public int getDisplayHeight() {
-        return 0;
-    }
-
-    /* End override DragCallback methods. */
 }