UI for memory and functionality for M+ and M-
Bug: 31686717
Test: manual - long-press on result and formula and proper context
menus appear, as well as on results in history.
Change-Id: I88b25ed9d0402e03b420ab984a6b086dff6859e5
diff --git a/res/layout/pad_operator_two_col.xml b/res/layout/pad_operator_two_col.xml
index 0d9308d..e056ba5 100644
--- a/res/layout/pad_operator_two_col.xml
+++ b/res/layout/pad_operator_two_col.xml
@@ -58,14 +58,6 @@
app:layout_column="0" />
<Button
- android:id="@+id/memory_store"
- style="@style/PadButtonStyle.Operator.Text"
- android:contentDescription="@string/desc_memory_store"
- android:text="@string/memory_store"
- app:layout_row="1"
- app:layout_column="1" />
-
- <Button
android:id="@+id/op_sub"
style="@style/PadButtonStyle.Operator"
android:contentDescription="@string/desc_op_sub"
@@ -74,14 +66,6 @@
app:layout_column="0" />
<Button
- android:id="@+id/memory_recall"
- style="@style/PadButtonStyle.Operator.Text"
- android:contentDescription="@string/desc_memory_recall"
- android:text="@string/memory_recall"
- app:layout_row="2"
- app:layout_column="1" />
-
- <Button
android:id="@+id/op_add"
style="@style/PadButtonStyle.Operator"
android:contentDescription="@string/desc_op_add"
diff --git a/res/menu/copy.xml b/res/menu/copy.xml
deleted file mode 100644
index 5897f88..0000000
--- a/res/menu/copy.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
- * Copyright (C) 2011, 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.
- */
--->
-
-<menu xmlns:android="http://schemas.android.com/apk/res/android">
-
- <item android:id="@+id/menu_copy"
- android:title="@android:string/copy"/>
-
-</menu>
diff --git a/res/menu/paste.xml b/res/menu/menu_formula.xml
similarity index 84%
rename from res/menu/paste.xml
rename to res/menu/menu_formula.xml
index 964be0d..8882c8a 100644
--- a/res/menu/paste.xml
+++ b/res/menu/menu_formula.xml
@@ -19,7 +19,10 @@
<menu xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:id="@+id/memory_recall"
+ android:title="@string/memory_recall"/>
+
<item android:id="@+id/menu_paste"
- android:title="@android:string/paste"/>
+ android:title="@android:string/paste"/>
</menu>
diff --git a/res/menu/paste.xml b/res/menu/menu_result.xml
similarity index 65%
copy from res/menu/paste.xml
copy to res/menu/menu_result.xml
index 964be0d..15e76cd 100644
--- a/res/menu/paste.xml
+++ b/res/menu/menu_result.xml
@@ -19,7 +19,20 @@
<menu xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:id="@+id/menu_paste"
- android:title="@android:string/paste"/>
+ <item
+ android:id="@+id/memory_store"
+ android:title="@string/memory_store" />
-</menu>
+ <item
+ android:id="@+id/memory_add"
+ android:title="@string/memory_add" />
+
+ <item
+ android:id="@+id/memory_subtract"
+ android:title="@string/memory_subtract" />
+
+ <item
+ android:id="@+id/menu_copy"
+ android:title="@android:string/copy" />
+
+</menu>
\ No newline at end of file
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 24ef68c..e4345cd 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -127,10 +127,21 @@
<!-- Toggle button to show/hide inverse functions. [CHAR_LIMIT=4] -->
<string name="inv" translatable="false">inv</string>
- <!-- Memory store button. [CHAR_LIMIT=2] -->
- <string name="memory_store">MS</string>
- <!-- Memory recall button. [CHAR_LIMIT=2] -->
- <string name="memory_recall">MR</string>
+ <!--
+ Item on Formula context menu used to paste from the Memory into the edit field. [CHAR_LIMIT=2]
+ -->
+ <string name="memory_recall" translatable="false">MR</string>
+ <!-- Item on Result context menu used to store the result in memory. [CHAR_LIMIT=2]
+ -->
+ <string name="memory_store" translatable="false">MS</string>
+ <!-- Item on Result context menu, which subtracts the current result from the number in memory.
+ [CHAR_LIMIT=2]
+ -->
+ <string name="memory_subtract" translatable="false">M-</string>
+ <!-- Item on Result context menu, which adds the current result to the number in memory.
+ [CHAR_LIMIT=2]
+ -->
+ <string name="memory_add" translatable="false">M+</string>
<!-- Content description for 'e' button. [CHAR_LIMIT=NONE] -->
<string name="desc_const_e">Euler\'s number</string>
@@ -218,11 +229,6 @@
<!-- Content description for "inv" button to hide inverse functions. [CHAR_LIMIT=NONE] -->
<string name="desc_inv_on">hide inverse functions</string>
- <!-- Content description for memory store button. [CHAR_LIMIT=NONE] -->
- <string name="desc_memory_store">memory store</string>
- <!-- Content description for memory recall button. [CHAR_LIMIT=NONE] -->
- <string name="desc_memory_recall">memory recall</string>
-
<!-- Content description for formula field when it is empty. [CHAR_LIMIT=NONE] -->
<string name="desc_formula">No formula</string>
<!-- Content description for result field when it is empty. [CHAR_LIMIT=NONE] -->
diff --git a/src/com/android/calculator2/Calculator.java b/src/com/android/calculator2/Calculator.java
index f41d87e..ea1d445 100644
--- a/src/com/android/calculator2/Calculator.java
+++ b/src/com/android/calculator2/Calculator.java
@@ -80,8 +80,10 @@
import java.io.ObjectOutput;
import java.io.ObjectOutputStream;
+import static com.android.calculator2.CalculatorFormula.OnFormulaContextMenuClickListener;
+
public class Calculator extends Activity
- implements OnTextSizeChangeListener, OnLongClickListener, CalculatorFormula.OnPasteListener,
+ implements OnTextSizeChangeListener, OnLongClickListener,
AlertDialogFragment.OnClickListener, Evaluator.EvaluationListener /* for main result */ {
/**
@@ -158,6 +160,48 @@
}
};
+ public final OnDisplayMemoryOperationsListener mOnDisplayMemoryOperationsListener =
+ new OnDisplayMemoryOperationsListener() {
+ @Override
+ public boolean shouldDisplayMemory() {
+ return mEvaluator.getMemoryIndex() != 0;
+ }
+ };
+
+ public final OnFormulaContextMenuClickListener mOnFormulaContextMenuClickListener =
+ new OnFormulaContextMenuClickListener() {
+ @Override
+ public boolean onPaste(ClipData clip) {
+ final ClipData.Item item = clip.getItemCount() == 0 ? null : clip.getItemAt(0);
+ if (item == null) {
+ // nothing to paste, bail early...
+ return false;
+ }
+
+ // Check if the item is a previously copied result, otherwise paste as raw text.
+ final Uri uri = item.getUri();
+ if (uri != null && mEvaluator.isLastSaved(uri)) {
+ clearIfNotInputState();
+ mEvaluator.appendExpr(mEvaluator.getSavedIndex());
+ redisplayAfterFormulaChange();
+ } else {
+ addChars(item.coerceToText(Calculator.this).toString(), false);
+ }
+ return true;
+ }
+
+ @Override
+ public void onMemoryRecall() {
+ clearIfNotInputState();
+ long memoryIndex = mEvaluator.getMemoryIndex();
+ if (memoryIndex != 0) {
+ mEvaluator.appendExpr(mEvaluator.getMemoryIndex());
+ redisplayAfterFormulaChange();
+ } // FIXME: Avoid the 0 case, e.g. by graying out button when memory is unavailable.
+ }
+ };
+
+
private final TextWatcher mFormulaTextWatcher = new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence charSequence, int start, int count, int after) {
@@ -340,8 +384,10 @@
mEvaluator.clearMain();
}
+ mFormulaText.setOnContextMenuClickListener(mOnFormulaContextMenuClickListener);
+ mFormulaText.setOnDisplayMemoryOperationsListener(mOnDisplayMemoryOperationsListener);
+
mFormulaText.setOnTextSizeChangeListener(this);
- mFormulaText.setOnPasteListener(this);
mFormulaText.addTextChangedListener(mFormulaTextWatcher);
mDeleteButton.setOnLongClickListener(this);
@@ -765,12 +811,6 @@
mEvaluator.evaluateAndNotify(mEvaluator.MAIN_INDEX, this, mResultText);
}
return;
- case R.id.memory_store:
- mResultText.onMemoryStore();
- return;
- case R.id.memory_recall:
- onMemoryRecall();
- return;
default:
cancelIfEvaluating(false);
if (haveUnprocessed()) {
@@ -920,15 +960,6 @@
redisplayAfterFormulaChange();
}
- private void onMemoryRecall() {
- clearIfNotInputState();
- long memoryIndex = mEvaluator.getMemoryIndex();
- if (memoryIndex != 0) {
- mEvaluator.appendExpr(mEvaluator.getMemoryIndex());
- redisplayAfterFormulaChange();
- } // FIXME: Avoid the 0 case, e.g. by graying out button when memory is unavailable.
- }
-
private void reveal(View sourceView, int colorRes, AnimatorListener listener) {
final ViewGroupOverlay groupOverlay =
(ViewGroupOverlay) getWindow().getDecorView().getOverlay();
@@ -1319,26 +1350,6 @@
return mHitRect.contains((int) event.getX(), (int) event.getY());
}
- @Override
- public boolean onPaste(ClipData clip) {
- final ClipData.Item item = clip.getItemCount() == 0 ? null : clip.getItemAt(0);
- if (item == null) {
- // nothing to paste, bail early...
- return false;
- }
-
- // Check if the item is a previously copied result, otherwise paste as raw text.
- final Uri uri = item.getUri();
- if (uri != null && mEvaluator.isLastSaved(uri)) {
- clearIfNotInputState();
- mEvaluator.appendExpr(mEvaluator.getSavedIndex());
- redisplayAfterFormulaChange();
- } else {
- addChars(item.coerceToText(this).toString(), false);
- }
- return true;
- }
-
/**
* Clean up animation for context menu.
*/
@@ -1346,4 +1357,8 @@
public void onContextMenuClosed(Menu menu) {
stopActionModeOrContextMenu();
}
+
+ public interface OnDisplayMemoryOperationsListener {
+ boolean shouldDisplayMemory();
+ }
}
diff --git a/src/com/android/calculator2/CalculatorFormula.java b/src/com/android/calculator2/CalculatorFormula.java
index 8a7e4c5..210372c 100644
--- a/src/com/android/calculator2/CalculatorFormula.java
+++ b/src/com/android/calculator2/CalculatorFormula.java
@@ -58,8 +58,9 @@
private ActionMode mActionMode;
private ActionMode.Callback mPasteActionModeCallback;
private ContextMenu mContextMenu;
- private OnPasteListener mOnPasteListener;
private OnTextSizeChangeListener mOnTextSizeChangeListener;
+ private OnFormulaContextMenuClickListener mOnContextMenuClickListener;
+ private Calculator.OnDisplayMemoryOperationsListener mOnDisplayMemoryOperationsListener;
public CalculatorFormula(Context context) {
this(context, null /* attrs */);
@@ -243,8 +244,13 @@
mOnTextSizeChangeListener = listener;
}
- public void setOnPasteListener(OnPasteListener listener) {
- mOnPasteListener = listener;
+ public void setOnContextMenuClickListener(OnFormulaContextMenuClickListener listener) {
+ mOnContextMenuClickListener = listener;
+ }
+
+ public void setOnDisplayMemoryOperationsListener(
+ Calculator.OnDisplayMemoryOperationsListener listener) {
+ mOnDisplayMemoryOperationsListener = listener;
}
/**
@@ -268,7 +274,7 @@
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
mode.setTag(TAG_ACTION_MODE);
final MenuInflater inflater = mode.getMenuInflater();
- return createPasteMenu(inflater, menu);
+ return createContextMenu(inflater, menu);
}
@Override
@@ -309,7 +315,7 @@
public void onCreateContextMenu(ContextMenu contextMenu, View view,
ContextMenu.ContextMenuInfo contextMenuInfo) {
final MenuInflater inflater = new MenuInflater(getContext());
- createPasteMenu(inflater, contextMenu);
+ createContextMenu(inflater, contextMenu);
mContextMenu = contextMenu;
for(int i = 0; i < contextMenu.size(); i++) {
contextMenu.getItem(i).setOnMenuItemClickListener(CalculatorFormula.this);
@@ -324,41 +330,52 @@
});
}
- private boolean createPasteMenu(MenuInflater inflater, Menu menu) {
+ private boolean createContextMenu(MenuInflater inflater, Menu menu) {
final ClipboardManager clipboard = (ClipboardManager) getContext()
.getSystemService(Context.CLIPBOARD_SERVICE);
- if (clipboard.hasPrimaryClip()) {
- bringPointIntoView(length());
- inflater.inflate(R.menu.paste, menu);
- return true;
+ final boolean isPasteEnabled = clipboard.hasPrimaryClip();
+ final boolean isMemoryEnabled = isMemoryEnabled();
+ if (!isPasteEnabled && !isMemoryEnabled) {
+ return false;
}
- // Prevents the selection action mode on double tap.
- return false;
+
+ bringPointIntoView(length());
+ inflater.inflate(R.menu.menu_formula, menu);
+ final MenuItem pasteItem = menu.findItem(R.id.menu_paste);
+ final MenuItem memoryRecallItem = menu.findItem(R.id.memory_recall);
+ pasteItem.setEnabled(isPasteEnabled);
+ memoryRecallItem.setEnabled(isMemoryEnabled);
+ return true;
}
private void paste() {
final ClipboardManager clipboard = (ClipboardManager) getContext()
.getSystemService(Context.CLIPBOARD_SERVICE);
final ClipData primaryClip = clipboard.getPrimaryClip();
- if (primaryClip != null && mOnPasteListener != null) {
- mOnPasteListener.onPaste(primaryClip);
+ if (primaryClip != null && mOnContextMenuClickListener != null) {
+ mOnContextMenuClickListener.onPaste(primaryClip);
}
}
@Override
public boolean onMenuItemClick(MenuItem item) {
- if (item.getItemId() == R.id.menu_paste) {
- paste();
- return true;
+ switch (item.getItemId()) {
+ case R.id.memory_recall:
+ mOnContextMenuClickListener.onMemoryRecall();
+ return true;
+ case R.id.menu_paste:
+ paste();
+ return true;
+ default:
+ return false;
}
- return false;
}
@Override
public void onPrimaryClipChanged() {
final ClipData clip = mClipboardManager.getPrimaryClip();
if (clip == null || clip.getItemCount() == 0) {
- setLongClickable(false);
+ setLongClickable(isMemoryEnabled());
return;
}
CharSequence clipText = null;
@@ -367,14 +384,20 @@
} catch (Exception e) {
Log.i("Calculator", "Error reading clipboard:", e);
}
- setLongClickable(!TextUtils.isEmpty(clipText));
+ setLongClickable(!TextUtils.isEmpty(clipText) || isMemoryEnabled());
+ }
+
+ private boolean isMemoryEnabled() {
+ return !(mOnDisplayMemoryOperationsListener == null || mOnContextMenuClickListener == null)
+ && mOnDisplayMemoryOperationsListener.shouldDisplayMemory();
}
public interface OnTextSizeChangeListener {
void onTextSizeChanged(TextView textView, float oldSize);
}
- public interface OnPasteListener {
+ public interface OnFormulaContextMenuClickListener {
boolean onPaste(ClipData clip);
+ void onMemoryRecall();
}
}
diff --git a/src/com/android/calculator2/CalculatorResult.java b/src/com/android/calculator2/CalculatorResult.java
index e001bee..0b399c4 100644
--- a/src/com/android/calculator2/CalculatorResult.java
+++ b/src/com/android/calculator2/CalculatorResult.java
@@ -410,6 +410,20 @@
}
/**
+ * Add the result to the value currently in memory.
+ */
+ public void onMemoryAdd() {
+ mEvaluator.addToMemory(Evaluator.MAIN_INDEX);
+ }
+
+ /**
+ * Subtract the result from the value currently in memory.
+ */
+ public void onMemorySubtract() {
+ mEvaluator.subtractFromMemory(Evaluator.MAIN_INDEX);
+ }
+
+ /**
* Set up scroll bounds (mMinPos, mMaxPos, etc.) and determine whether the result is
* scrollable, based on the supplied information about the result.
* This is unfortunately complicated because we need to predict whether trailing digits
@@ -920,7 +934,7 @@
}
/**
- * Use ActionMode for copy support on M and higher.
+ * Use ActionMode for copy/memory support on M and higher.
*/
@TargetApi(Build.VERSION_CODES.M)
private void setupActionMode() {
@@ -995,7 +1009,7 @@
}
/**
- * Use ContextMenu for copy support on L and lower.
+ * Use ContextMenu for copy/memory support on L and lower.
*/
private void setupContextMenu() {
setOnCreateContextMenuListener(new OnCreateContextMenuListener() {
@@ -1022,7 +1036,12 @@
}
private boolean createCopyMenu(MenuInflater inflater, Menu menu) {
- inflater.inflate(R.menu.copy, menu);
+ inflater.inflate(R.menu.menu_result, menu);
+ final boolean displayMemory = mEvaluator.getMemoryIndex() != 0;
+ final MenuItem memoryAddItem = menu.findItem(R.id.memory_add);
+ final MenuItem memorySubtractItem = menu.findItem(R.id.memory_subtract);
+ memoryAddItem.setEnabled(displayMemory);
+ memorySubtractItem.setEnabled(displayMemory);
highlightResult();
return true;
}
@@ -1072,6 +1091,15 @@
@Override
public boolean onMenuItemClick(MenuItem item) {
switch (item.getItemId()) {
+ case R.id.memory_add:
+ onMemoryAdd();
+ return true;
+ case R.id.memory_subtract:
+ onMemorySubtract();
+ return true;
+ case R.id.memory_store:
+ onMemoryStore();
+ return true;
case R.id.menu_copy:
if (mEvaluator.evaluationInProgress(mIndex)) {
// Refuse to copy placeholder characters.
@@ -1085,4 +1113,4 @@
return false;
}
}
-}
+}
\ No newline at end of file
diff --git a/src/com/android/calculator2/Evaluator.java b/src/com/android/calculator2/Evaluator.java
index 82a4d86..77c762d 100644
--- a/src/com/android/calculator2/Evaluator.java
+++ b/src/com/android/calculator2/Evaluator.java
@@ -1362,6 +1362,22 @@
}
/**
+ * Return an ExprInfo corresponding to the subtraction of the value at the subtrahend index
+ * from value at the minuend index (minuend - subtrahend = result). Both are presumed to have
+ * been previously evaluated. The result is unevaluated.
+ */
+ private ExprInfo difference(long minuendIndex, long subtrahendIndex) {
+ final CalculatorExpr resultExpr = new CalculatorExpr();
+ resultExpr.append(getCollapsedExpr(minuendIndex));
+ resultExpr.add(R.id.op_sub);
+ resultExpr.append(getCollapsedExpr(subtrahendIndex));
+ final ExprInfo result = new ExprInfo(resultExpr, false /* angular measure irrelevant */);
+ result.mLongTimeout = mExprs.get(minuendIndex).mLongTimeout
+ || mExprs.get(subtrahendIndex).mLongTimeout;
+ return result;
+ }
+
+ /**
* Add the expression described by the argument to the database.
* Returns the new row id in the database.
* If in_history is true, add it with a positive index, so it will appear in the history.
@@ -1576,6 +1592,17 @@
}
/**
+ * Save an an expression representing the subtraction of the expression with the given index
+ * from "memory." Make mMemoryIndex point to it when we complete evaluating.
+ */
+ public void subtractFromMemory(long index) {
+ ExprInfo newEi = difference(mMemoryIndex, index);
+ long newIndex = addToDB(false, newEi);
+ mMemoryIndex = 0; // Invalidate while we're evaluating.
+ setMemoryIndexWhenEvaluated(newIndex, true /* persist */);
+ }
+
+ /**
* Return index of "saved" expression, or 0.
*/
public long getSavedIndex() {