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. */
}