Miscellaneous fixes
-Fix fragment animation on exit
-Use DateUtils.RelativeTimeSpanString instead of SimpleDateFormat
-Fix RecyclerView ordering and remove arbitrary "25" pre-seeding
-Cancel evaluation for only the id corresponding to the recycled
ViewHolder
Fixes: 32918645
Fixes: 32945018
Fixes: 33000429
Change-Id: I55e3a101a02aec8fe03d43ad0b60343d1fa36940
diff --git a/src/com/android/calculator2/Calculator.java b/src/com/android/calculator2/Calculator.java
index ce80507..02047af 100644
--- a/src/com/android/calculator2/Calculator.java
+++ b/src/com/android/calculator2/Calculator.java
@@ -505,7 +505,6 @@
public void onBackPressed() {
if (!stopActionModeOrContextMenu()) {
if (mDragLayout.isOpen()) {
- // Close the layout and remove the fragment.
mDragLayout.setClosed();
popFragmentBackstack();
return;
diff --git a/src/com/android/calculator2/DragController.java b/src/com/android/calculator2/DragController.java
index b0ff122..4cc755e 100644
--- a/src/com/android/calculator2/DragController.java
+++ b/src/com/android/calculator2/DragController.java
@@ -53,10 +53,10 @@
if (evaluator != null) {
// Initialize controller
- if (isDisplayEmpty()) {
+ if (EvaluatorStateUtils.isDisplayEmpty(mEvaluator)) {
// Empty display
mAnimationController = new EmptyAnimationController();
- } else if (mEvaluator.hasResult(Evaluator.MAIN_INDEX)) {
+ } else if (isResultState()) {
// Result
mAnimationController = new ResultAnimationController();
} else {
@@ -67,14 +67,8 @@
}
}
- // There is no formula or result in the CalculatorDisplay.
- private boolean isDisplayEmpty() {
- if (mEvaluator != null) {
- final CalculatorExpr mainExpr = mEvaluator.getExpr(Evaluator.MAIN_INDEX);
- return mainExpr == null || mainExpr.isEmpty();
- } else {
- return false;
- }
+ private boolean isResultState() {
+ return mDisplayResult.getTranslationY() != 0;
}
public void setDisplayFormula(CalculatorFormula formula) {
@@ -92,7 +86,7 @@
public void animateViews(float yFraction, RecyclerView recyclerView, int itemCount) {
final HistoryAdapter.ViewHolder vh = (HistoryAdapter.ViewHolder)
recyclerView.findViewHolderForAdapterPosition(0);
- if (vh != null && !isDisplayEmpty()) {
+ if (vh != null && !EvaluatorStateUtils.isDisplayEmpty(mEvaluator)) {
final CalculatorFormula formula = vh.getFormula();
final CalculatorResult result = vh.getResult();
final TextView date = vh.getDate();
@@ -132,7 +126,7 @@
date.setTranslationY(mAnimationController.getDateTranslationY(yFraction));
}
- } else if (isDisplayEmpty()) {
+ } else if (EvaluatorStateUtils.isDisplayEmpty(mEvaluator)) {
// There is no current expression but we still need to collect information
// to translate the other viewholders.
if (!mAnimationInitialized) {
diff --git a/src/com/android/calculator2/DragLayout.java b/src/com/android/calculator2/DragLayout.java
index 101b62e..e7920ce 100644
--- a/src/com/android/calculator2/DragLayout.java
+++ b/src/com/android/calculator2/DragLayout.java
@@ -184,7 +184,6 @@
c.onClosed();
}
mDragHelper.smoothSlideViewTo(mHistoryFrame, 0, 0);
- mHistoryFrame.setVisibility(GONE);
mIsOpen = false;
}
@@ -233,6 +232,7 @@
// The view stopped moving.
if (mDraggingBorder == 0) {
setClosed();
+ mHistoryFrame.setVisibility(GONE);
} else if (mDraggingBorder == mVerticalRange) {
setOpen();
}
diff --git a/src/com/android/calculator2/EvaluatorStateUtils.java b/src/com/android/calculator2/EvaluatorStateUtils.java
new file mode 100644
index 0000000..2d8eddc
--- /dev/null
+++ b/src/com/android/calculator2/EvaluatorStateUtils.java
@@ -0,0 +1,32 @@
+/*
+ * 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;
+
+/**
+ * Utils class to get the state of the passed-in Evaluator.
+ */
+
+public class EvaluatorStateUtils {
+ public static boolean isDisplayEmpty(Evaluator evaluator) {
+ if (evaluator != null) {
+ final CalculatorExpr mainExpr = evaluator.getExpr(Evaluator.MAIN_INDEX);
+ return mainExpr == null || mainExpr.isEmpty();
+ } else {
+ return true;
+ }
+ }
+}
diff --git a/src/com/android/calculator2/HistoryAdapter.java b/src/com/android/calculator2/HistoryAdapter.java
index 169e34f..ca5509c 100644
--- a/src/com/android/calculator2/HistoryAdapter.java
+++ b/src/com/android/calculator2/HistoryAdapter.java
@@ -39,13 +39,12 @@
private List<HistoryItem> mDataSet;
- private boolean mHasCurrentExpression = true;
-
public HistoryAdapter(Calculator calculator, ArrayList<HistoryItem> dataSet,
String currentExpressionDescription) {
mEvaluator = Evaluator.getInstance(calculator);
mDataSet = dataSet;
mCurrentExpressionDescription = currentExpressionDescription;
+ setHasStableIds(true);
}
@Override
@@ -71,41 +70,44 @@
holder.mFormula.setText(item.getFormula());
// Note: HistoryItems that are not the current expression will always have interesting ops.
- holder.mResult.setEvaluator(mEvaluator, item.getId());
- if (mHasCurrentExpression && position == 0) {
+ holder.mResult.setEvaluator(mEvaluator, item.getEvaluatorIndex());
+ if (item.getEvaluatorIndex() == Evaluator.MAIN_INDEX) {
holder.mDate.setText(mCurrentExpressionDescription);
holder.mDate.setContentDescription(mCurrentExpressionDescription);
} else {
holder.mDate.setText(item.getDateString());
- holder.mDate.setContentDescription(item.getDateDescription());
}
}
- public void setHasCurrentExpression(boolean has) {
- mHasCurrentExpression = has;
- }
-
@Override
public void onViewRecycled(ViewHolder holder) {
+ if (holder.getItemViewType() == EMPTY_VIEW_TYPE) {
+ return;
+ }
+ mEvaluator.cancel(holder.getItemId(), true);
+
holder.mDate.setContentDescription(null);
holder.mDate.setText(null);
holder.mFormula.setText(null);
holder.mResult.setText(null);
- // TODO: Only cancel the calculation for the recycled view
- mEvaluator.cancelAll(true);
-
super.onViewRecycled(holder);
}
@Override
+ public long getItemId(int position) {
+ return mDataSet.get(position).getEvaluatorIndex();
+ }
+
+ @Override
public int getItemViewType(int position) {
HistoryItem item = mDataSet.get(position);
// Continue to lazy-fill the data set
if (item == null) {
- item = new HistoryItem(position, mEvaluator.getTimeStamp(position),
- mEvaluator.getExprAsSpannable(position));
+ final int evaluatorIndex = getEvaluatorIndex(position);
+ item = new HistoryItem(evaluatorIndex, mEvaluator.getTimeStamp(evaluatorIndex),
+ mEvaluator.getExprAsSpannable(evaluatorIndex));
mDataSet.set(position, item);
}
return item.isEmptyView() ? EMPTY_VIEW_TYPE : HISTORY_VIEW_TYPE;
@@ -120,6 +122,15 @@
mDataSet = dataSet;
}
+ private int getEvaluatorIndex(int position) {
+ if (EvaluatorStateUtils.isDisplayEmpty(mEvaluator)) {
+ return (int) mEvaluator.getMaxIndex() - position;
+ } else {
+ // Account for the additional "Current Expression" with the +1.
+ return (int) mEvaluator.getMaxIndex() - position + 1;
+ }
+ }
+
public static class ViewHolder extends RecyclerView.ViewHolder {
private TextView mDate;
diff --git a/src/com/android/calculator2/HistoryFragment.java b/src/com/android/calculator2/HistoryFragment.java
index aca7847..dcc7b25 100644
--- a/src/com/android/calculator2/HistoryFragment.java
+++ b/src/com/android/calculator2/HistoryFragment.java
@@ -19,7 +19,6 @@
import android.animation.Animator;
import android.animation.ObjectAnimator;
import android.app.Fragment;
-import android.app.FragmentTransaction;
import android.os.Bundle;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
@@ -145,20 +144,13 @@
final ArrayList<HistoryItem> newDataSet = new ArrayList<>();
- if (!mEvaluator.getExpr(Evaluator.MAIN_INDEX).isEmpty()) {
+ if (!EvaluatorStateUtils.isDisplayEmpty(mEvaluator)) {
// 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).
- newDataSet.add(new HistoryItem(Evaluator.MAIN_INDEX, 0 /* millis*/,
+ newDataSet.add(new HistoryItem(Evaluator.MAIN_INDEX, System.currentTimeMillis(),
mEvaluator.getExprAsSpannable(0)));
}
- // We retrieve the current expression separately, so it's excluded from this loop.
- // We lazy-fill, so just retrieve the first 25 expressions for now.
- for (long i = Math.min(maxIndex, 25); i > 0; --i) {
- final HistoryItem item = new HistoryItem(i, mEvaluator.getTimeStamp(i),
- mEvaluator.getExprAsSpannable(i));
- newDataSet.add(item);
- }
- for (long i = Math.max(maxIndex - 25, 0); i > 0; --i) {
+ for (long i = 0; i < maxIndex; ++i) {
newDataSet.add(null);
}
if (maxIndex == 0) {
@@ -179,12 +171,11 @@
public Animator onCreateAnimator(int transit, boolean enter, int nextAnim) {
final View view = getView();
final int height = getResources().getDisplayMetrics().heightPixels;
- if (!enter) {
- return ObjectAnimator.ofFloat(view, View.TRANSLATION_Y, -height);
- } else if (transit == FragmentTransaction.TRANSIT_FRAGMENT_OPEN) {
+ if (enter) {
return ObjectAnimator.ofFloat(view, View.TRANSLATION_Y, -height, 0f);
+ } else {
+ return ObjectAnimator.ofFloat(view, View.TRANSLATION_Y, -height);
}
- return null;
}
@Override
diff --git a/src/com/android/calculator2/HistoryItem.java b/src/com/android/calculator2/HistoryItem.java
index 63f46e9..5694cc7 100644
--- a/src/com/android/calculator2/HistoryItem.java
+++ b/src/com/android/calculator2/HistoryItem.java
@@ -17,33 +17,27 @@
package com.android.calculator2;
import android.text.Spannable;
-import android.text.format.DateFormat;
-
-import java.text.SimpleDateFormat;
-import java.util.Date;
-import java.util.Locale;
+import android.text.format.DateUtils;
public class HistoryItem {
- private static final String dateFormat = "EEEMMMd";
- private static final String descriptionFormat = "EEEEMMMMd";
-
- private long mId;
- private Date mDate;
+ private long mEvaluatorIndex;
+ /** Date in millis */
+ private long mTimeInMillis;
private Spannable mFormula;
- // This is true only for the "empty history" view.
+ /** This is true only for the "empty history" view. */
private final boolean mIsEmpty;
- public HistoryItem(long id, long millis, Spannable formula) {
- mId = id;
- mDate = new Date(millis);
+ public HistoryItem(long evaluatorIndex, long millis, Spannable formula) {
+ mEvaluatorIndex = evaluatorIndex;
+ mTimeInMillis = millis;
mFormula = formula;
mIsEmpty = false;
}
- public long getId() {
- return mId;
+ public long getEvaluatorIndex() {
+ return mEvaluatorIndex;
}
public HistoryItem() {
@@ -54,17 +48,13 @@
return mIsEmpty;
}
- public String getDateString() {
- // TODO: Use DateUtils?
- final Locale l = Locale.getDefault();
- final String datePattern = DateFormat.getBestDateTimePattern(l, dateFormat);
- return new SimpleDateFormat(datePattern, l).format(mDate);
- }
-
- public String getDateDescription() {
- final Locale l = Locale.getDefault();
- final String descriptionPattern = DateFormat.getBestDateTimePattern(l, descriptionFormat);
- return new SimpleDateFormat(descriptionPattern, l).format(mDate);
+ /**
+ * @return String in format "n days ago"
+ * For n > 7, the date is returned.
+ */
+ public CharSequence getDateString() {
+ return DateUtils.getRelativeTimeSpanString(mTimeInMillis, System.currentTimeMillis(),
+ DateUtils.DAY_IN_MILLIS, DateUtils.FORMAT_ABBREV_RELATIVE);
}
public Spannable getFormula() {