diff options
author | Justin Klaassen <justinklaassen@google.com> | 2015-05-28 17:55:20 -0700 |
---|---|---|
committer | Justin Klaassen <justinklaassen@google.com> | 2015-05-29 16:19:59 -0700 |
commit | 4459516a2c116ddf80725d6a96a69186ccddc329 (patch) | |
tree | 7c606670ae5740cb05d08f4150a5ba2d44a28f05 | |
parent | 721ec84263a26b859c57eb9fb4eb66939fe94272 (diff) | |
download | android_packages_apps_ExactCalculator-4459516a2c116ddf80725d6a96a69186ccddc329.tar.gz android_packages_apps_ExactCalculator-4459516a2c116ddf80725d6a96a69186ccddc329.tar.bz2 android_packages_apps_ExactCalculator-4459516a2c116ddf80725d6a96a69186ccddc329.zip |
Polish display and evaluate animation
Bug: 20915670
Bug: 21489377
- Adjust font metrics across all supported device configurations to
support font scaling and min touch size requirements.
- Support proper font scaling for non-scrollable results when performing
the evaluate animation.
- Remove restriction for only using 4/5 of the width of the result
display (NOTE: the result's textSize must match the formula's
minTextSize).
- Add AlignedTextView base class to ensure formula/result padding is
based on the displayed text's ascent/baseline.
Change-Id: Id53e9bdc6e699fb05fdf331a6a472ecc170edf38
-rw-r--r-- | res/layout/display.xml | 4 | ||||
-rw-r--r-- | res/values-land/styles.xml | 25 | ||||
-rw-r--r-- | res/values-port/styles.xml | 9 | ||||
-rw-r--r-- | res/values-sw400dp-land/styles.xml | 61 | ||||
-rw-r--r-- | res/values-sw400dp-port/styles.xml | 63 | ||||
-rw-r--r-- | res/values-sw600dp-land/styles.xml | 23 | ||||
-rw-r--r-- | res/values-sw600dp-port/styles.xml | 27 | ||||
-rw-r--r-- | res/values-sw768dp-land/styles.xml (renamed from res/values-sw800dp-land/styles.xml) | 54 | ||||
-rw-r--r-- | res/values-sw768dp-port/styles.xml | 63 | ||||
-rw-r--r-- | res/values-sw800dp-port/styles.xml | 109 | ||||
-rw-r--r-- | src/com/android/calculator2/AlignedTextView.java | 86 | ||||
-rw-r--r-- | src/com/android/calculator2/Calculator.java | 167 | ||||
-rw-r--r-- | src/com/android/calculator2/CalculatorResult.java | 81 | ||||
-rw-r--r-- | src/com/android/calculator2/CalculatorText.java | 59 | ||||
-rw-r--r-- | src/com/android/calculator2/Evaluator.java | 25 |
15 files changed, 463 insertions, 393 deletions
diff --git a/res/layout/display.xml b/res/layout/display.xml index 56b0e6b..52182a4 100644 --- a/res/layout/display.xml +++ b/res/layout/display.xml @@ -51,10 +51,6 @@ android:textColor="@color/display_formula_text_color" android:textIsSelectable="false" /> - <!-- - We lay the result out to full width, but are careful to use only - 4/5 of the space, so that we have room when we expand. - --> <com.android.calculator2.CalculatorResult android:id="@+id/result" style="@style/DisplayTextStyle.Result" diff --git a/res/values-land/styles.xml b/res/values-land/styles.xml index bbda604..afd278e 100644 --- a/res/values-land/styles.xml +++ b/res/values-land/styles.xml @@ -19,26 +19,22 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android"> <style name="DisplayTextStyle.Formula"> - <item name="android:paddingTop">0dip</item> - <item name="android:paddingBottom">12dip</item> - <item name="android:paddingStart">32dip</item> - <item name="android:paddingEnd">32dip</item> - <item name="android:textSize">30sp</item> + <item name="android:paddingTop">2dip</item> + <item name="android:paddingBottom">6dip</item> + <item name="android:paddingStart">36dip</item> + <item name="android:paddingEnd">36dip</item> + <item name="android:textSize">28sp</item> </style> <style name="DisplayTextStyle.Result"> - <item name="android:paddingTop">0dip</item> + <item name="android:paddingTop">6dip</item> <item name="android:paddingBottom">12dip</item> - <item name="android:paddingStart">32dip</item> - <item name="android:paddingEnd">32dip</item> + <item name="android:paddingStart">36dip</item> + <item name="android:paddingEnd">36dip</item> <item name="android:textSize">28sp</item> </style> <style name="PadButtonStyle.Advanced"> - <item name="android:layout_marginTop">4dip</item> - <item name="android:layout_marginBottom">4dip</item> - <item name="android:layout_marginStart">8dip</item> - <item name="android:layout_marginEnd">8dip</item> <item name="android:background">@drawable/pad_button_advanced_background</item> <item name="android:textColor">@color/pad_button_advanced_text_color</item> <item name="android:textSize">15sp</item> @@ -50,7 +46,6 @@ </style> <style name="PadButtonStyle.Numeric"> - <item name="android:layout_margin">4dip</item> <item name="android:textSize">23sp</item> </style> @@ -59,10 +54,6 @@ </style> <style name="PadButtonStyle.Operator"> - <item name="android:layout_marginTop">4dip</item> - <item name="android:layout_marginBottom">4dip</item> - <item name="android:layout_marginStart">8dip</item> - <item name="android:layout_marginEnd">8dip</item> <item name="android:textSize">20sp</item> </style> diff --git a/res/values-port/styles.xml b/res/values-port/styles.xml index a3b1867..8cdf8c4 100644 --- a/res/values-port/styles.xml +++ b/res/values-port/styles.xml @@ -23,8 +23,8 @@ <item name="android:paddingBottom">24dip</item> <item name="android:paddingStart">16dip</item> <item name="android:paddingEnd">16dip</item> - <item name="minTextSize">36sp</item> - <item name="maxTextSize">64sp</item> + <item name="minTextSize">32sp</item> + <item name="maxTextSize">56sp</item> <item name="stepTextSize">8sp</item> </style> @@ -33,11 +33,10 @@ <item name="android:paddingBottom">32dip</item> <item name="android:paddingStart">16dip</item> <item name="android:paddingEnd">16dip</item> - <item name="android:textSize">28sp</item> + <item name="android:textSize">32sp</item> </style> <style name="PadButtonStyle.Advanced"> - <item name="android:layout_margin">4dip</item> <item name="android:background">@drawable/pad_button_advanced_background</item> <item name="android:textColor">@color/pad_button_advanced_text_color</item> <item name="android:textSize">20sp</item> @@ -49,7 +48,6 @@ </style> <style name="PadButtonStyle.Numeric"> - <item name="android:layout_margin">4dip</item> <item name="android:textSize">32sp</item> </style> @@ -58,7 +56,6 @@ </style> <style name="PadButtonStyle.Operator"> - <item name="android:layout_margin">8dip</item> <item name="android:textSize">23sp</item> </style> diff --git a/res/values-sw400dp-land/styles.xml b/res/values-sw400dp-land/styles.xml new file mode 100644 index 0000000..bf863c2 --- /dev/null +++ b/res/values-sw400dp-land/styles.xml @@ -0,0 +1,61 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2014 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. + --> + +<!-- Styles for landscape phone (e.g. Nexus 4/5). --> +<resources xmlns:android="http://schemas.android.com/apk/res/android"> + + <style name="DisplayTextStyle.Formula"> + <item name="android:paddingTop">2dip</item> + <item name="android:paddingBottom">10dip</item> + <item name="android:paddingStart">36dip</item> + <item name="android:paddingEnd">36dip</item> + <item name="android:textSize">36sp</item> + </style> + + <style name="DisplayTextStyle.Result"> + <item name="android:paddingTop">8dip</item> + <item name="android:paddingBottom">18dip</item> + <item name="android:paddingStart">36dip</item> + <item name="android:paddingEnd">36dip</item> + <item name="android:textSize">36sp</item> + </style> + + <style name="PadButtonStyle.Advanced"> + <item name="android:background">@drawable/pad_button_advanced_background</item> + <item name="android:textColor">@color/pad_button_advanced_text_color</item> + <item name="android:textSize">17sp</item> + </style> + + <style name="PadButtonStyle.Advanced.Text"> + <item name="android:textAllCaps">true</item> + <item name="android:textSize">15sp</item> + </style> + + <style name="PadButtonStyle.Numeric"> + <item name="android:textSize">27sp</item> + </style> + + <style name="PadButtonStyle.Operator"> + <item name="android:textSize">24sp</item> + </style> + + <style name="PadButtonStyle.Operator.Text"> + <item name="android:textAllCaps">true</item> + <item name="android:textSize">15sp</item> + </style> + +</resources> diff --git a/res/values-sw400dp-port/styles.xml b/res/values-sw400dp-port/styles.xml new file mode 100644 index 0000000..c715d52 --- /dev/null +++ b/res/values-sw400dp-port/styles.xml @@ -0,0 +1,63 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2014 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. + --> + +<!-- Styles for portrait phone (e.g. Nexus 4/5). --> +<resources xmlns:android="http://schemas.android.com/apk/res/android"> + + <style name="DisplayTextStyle.Formula"> + <item name="android:paddingTop">16dip</item> + <item name="android:paddingBottom">28dip</item> + <item name="android:paddingStart">16dip</item> + <item name="android:paddingEnd">16dip</item> + <item name="minTextSize">42sp</item> + <item name="maxTextSize">74sp</item> + <item name="stepTextSize">8sp</item> + </style> + + <style name="DisplayTextStyle.Result"> + <item name="android:paddingTop">16dip</item> + <item name="android:paddingBottom">42dip</item> + <item name="android:paddingStart">16dip</item> + <item name="android:paddingEnd">16dip</item> + <item name="android:textSize">42sp</item> + </style> + + <style name="PadButtonStyle.Advanced"> + <item name="android:background">@drawable/pad_button_advanced_background</item> + <item name="android:textColor">@color/pad_button_advanced_text_color</item> + <item name="android:textSize">23sp</item> + </style> + + <style name="PadButtonStyle.Advanced.Text"> + <item name="android:textAllCaps">true</item> + <item name="android:textSize">20sp</item> + </style> + + <style name="PadButtonStyle.Numeric"> + <item name="android:textSize">36sp</item> + </style> + + <style name="PadButtonStyle.Operator"> + <item name="android:textSize">27sp</item> + </style> + + <style name="PadButtonStyle.Operator.Text"> + <item name="android:textAllCaps">true</item> + <item name="android:textSize">20sp</item> + </style> + +</resources> diff --git a/res/values-sw600dp-land/styles.xml b/res/values-sw600dp-land/styles.xml index 603a392..0b0a10f 100644 --- a/res/values-sw600dp-land/styles.xml +++ b/res/values-sw600dp-land/styles.xml @@ -19,23 +19,22 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android"> <style name="DisplayTextStyle.Formula"> - <item name="android:paddingTop">0dip</item> - <item name="android:paddingBottom">52dip</item> + <item name="android:paddingTop">4dip</item> + <item name="android:paddingBottom">16dip</item> <item name="android:paddingStart">44dip</item> <item name="android:paddingEnd">44dip</item> <item name="android:textSize">48sp</item> </style> <style name="DisplayTextStyle.Result"> - <item name="android:paddingTop">0dip</item> - <item name="android:paddingBottom">46dip</item> + <item name="android:paddingTop">16dip</item> + <item name="android:paddingBottom">32dip</item> <item name="android:paddingStart">44dip</item> <item name="android:paddingEnd">44dip</item> - <item name="android:textSize">36sp</item> + <item name="android:textSize">48sp</item> </style> <style name="PadButtonStyle.Advanced"> - <item name="android:layout_margin">6dip</item> <item name="android:background">@drawable/pad_button_advanced_background</item> <item name="android:textColor">@color/pad_button_advanced_text_color</item> <item name="android:textSize">27sp</item> @@ -47,7 +46,6 @@ </style> <style name="PadButtonStyle.Numeric"> - <item name="android:layout_margin">6dip</item> <item name="android:textSize">36sp</item> </style> @@ -56,7 +54,6 @@ </style> <style name="PadButtonStyle.Operator"> - <item name="android:layout_margin">6dip</item> <item name="android:textSize">36sp</item> </style> @@ -67,25 +64,25 @@ <style name="PadLayoutStyle.Advanced"> <item name="android:layout_width">0dip</item> - <item name="android:layout_weight">508</item> + <item name="android:layout_weight">500</item> <item name="android:paddingTop">10dip</item> <item name="android:paddingBottom">10dip</item> <item name="android:paddingStart">18dip</item> - <item name="android:paddingEnd">22dip</item> + <item name="android:paddingEnd">18dip</item> </style> <style name="PadLayoutStyle.Numeric"> <item name="android:layout_width">0dip</item> - <item name="android:layout_weight">532</item> + <item name="android:layout_weight">500</item> <item name="android:paddingTop">10dip</item> <item name="android:paddingBottom">10dip</item> - <item name="android:paddingStart">22dip</item> + <item name="android:paddingStart">18dip</item> <item name="android:paddingEnd">18dip</item> </style> <style name="PadLayoutStyle.Operator"> <item name="android:layout_width">0dip</item> - <item name="android:layout_weight">240</item> + <item name="android:layout_weight">280</item> <item name="android:paddingTop">10dip</item> <item name="android:paddingBottom">10dip</item> <item name="android:paddingStart">18dip</item> diff --git a/res/values-sw600dp-port/styles.xml b/res/values-sw600dp-port/styles.xml index efb1033..254880b 100644 --- a/res/values-sw600dp-port/styles.xml +++ b/res/values-sw600dp-port/styles.xml @@ -21,27 +21,23 @@ <style name="DisplayTextStyle.Formula"> <item name="android:paddingTop">16dip</item> - <item name="android:paddingBottom">48dip</item> + <item name="android:paddingBottom">36dip</item> <item name="android:paddingStart">44dip</item> <item name="android:paddingEnd">44dip</item> <item name="minTextSize">48sp</item> - <item name="maxTextSize">80sp</item> + <item name="maxTextSize">72sp</item> <item name="stepTextSize">8sp</item> </style> <style name="DisplayTextStyle.Result"> <item name="android:paddingTop">16dip</item> - <item name="android:paddingBottom">90dip</item> + <item name="android:paddingBottom">48dip</item> <item name="android:paddingStart">44dip</item> <item name="android:paddingEnd">44dip</item> - <item name="android:textSize">36sp</item> + <item name="android:textSize">48sp</item> </style> <style name="PadButtonStyle.Advanced"> - <item name="android:layout_marginTop">8dip</item> - <item name="android:layout_marginBottom">8dip</item> - <item name="android:layout_marginStart">12dip</item> - <item name="android:layout_marginEnd">12dip</item> <item name="android:background">@drawable/pad_button_advanced_background</item> <item name="android:textColor">@color/pad_button_advanced_text_color</item> <item name="android:textSize">27sp</item> @@ -53,7 +49,6 @@ </style> <style name="PadButtonStyle.Numeric"> - <item name="android:layout_margin">8dip</item> <item name="android:textSize">36sp</item> </style> @@ -62,10 +57,6 @@ </style> <style name="PadButtonStyle.Operator"> - <item name="android:layout_marginTop">8dip</item> - <item name="android:layout_marginBottom">8dip</item> - <item name="android:layout_marginStart">16dip</item> - <item name="android:layout_marginEnd">16dip</item> <item name="android:textSize">36sp</item> </style> @@ -76,7 +67,7 @@ <style name="PadLayoutStyle.Advanced"> <item name="android:layout_height">0dip</item> - <item name="android:layout_weight">256</item> + <item name="android:layout_weight">264</item> <item name="android:paddingTop">8dip</item> <item name="android:paddingBottom">8dip</item> <item name="android:paddingStart">16dip</item> @@ -85,7 +76,7 @@ <style name="PadLayoutStyle.Numeric"> <item name="android:layout_width">0dip</item> - <item name="android:layout_weight">532</item> + <item name="android:layout_weight">500</item> <item name="android:paddingTop">8dip</item> <item name="android:paddingBottom">8dip</item> <item name="android:paddingStart">16dip</item> @@ -94,17 +85,17 @@ <style name="PadLayoutStyle.Operator"> <item name="android:layout_width">0dip</item> - <item name="android:layout_weight">240</item> + <item name="android:layout_weight">264</item> <item name="android:paddingTop">8dip</item> <item name="android:paddingBottom">8dip</item> - <item name="android:paddingStart">0dip</item> + <item name="android:paddingStart">8dip</item> <item name="android:paddingEnd">8dip</item> </style> <style name="PadLinearLayoutStyle"> <item name="android:layout_width">match_parent</item> <item name="android:layout_height">0dip</item> - <item name="android:layout_weight">508</item> + <item name="android:layout_weight">500</item> </style> </resources> diff --git a/res/values-sw800dp-land/styles.xml b/res/values-sw768dp-land/styles.xml index 807f568..22b3f08 100644 --- a/res/values-sw800dp-land/styles.xml +++ b/res/values-sw768dp-land/styles.xml @@ -20,78 +20,44 @@ <style name="DisplayTextStyle.Formula"> <item name="android:paddingTop">16dip</item> - <item name="android:paddingBottom">42dip</item> + <item name="android:paddingBottom">32dip</item> <item name="android:paddingStart">44dip</item> <item name="android:paddingEnd">44dip</item> <item name="minTextSize">56sp</item> - <item name="maxTextSize">96sp</item> + <item name="maxTextSize">72sp</item> <item name="stepTextSize">8sp</item> </style> <style name="DisplayTextStyle.Result"> - <item name="android:paddingTop">10dip</item> - <item name="android:paddingBottom">46dip</item> + <item name="android:paddingTop">16dip</item> + <item name="android:paddingBottom">48dip</item> <item name="android:paddingStart">44dip</item> <item name="android:paddingEnd">44dip</item> - <item name="android:textSize">40sp</item> + <item name="android:textSize">56sp</item> </style> <style name="PadButtonStyle.Advanced"> - <item name="android:layout_margin">6dip</item> <item name="android:background">@drawable/pad_button_advanced_background</item> <item name="android:textColor">@color/pad_button_advanced_text_color</item> - <item name="android:textSize">36sp</item> + <item name="android:textSize">30sp</item> </style> <style name="PadButtonStyle.Advanced.Text"> <item name="android:textAllCaps">true</item> - <item name="android:textSize">32sp</item> + <item name="android:textSize">26sp</item> </style> <style name="PadButtonStyle.Numeric"> - <item name="android:layout_margin">6dip</item> - <item name="android:textSize">48sp</item> - </style> - - <style name="PadButtonStyle.Numeric.Equals"> - <item name="android:visibility">gone</item> + <item name="android:textSize">36sp</item> </style> <style name="PadButtonStyle.Operator"> - <item name="android:layout_margin">6dip</item> - <item name="android:textSize">48sp</item> + <item name="android:textSize">36sp</item> </style> <style name="PadButtonStyle.Operator.Text"> <item name="android:textAllCaps">true</item> - <item name="android:textSize">32sp</item> - </style> - - <style name="PadLayoutStyle.Advanced"> - <item name="android:layout_width">0dip</item> - <item name="android:layout_weight">508</item> - <item name="android:paddingTop">10dip</item> - <item name="android:paddingBottom">10dip</item> - <item name="android:paddingStart">18dip</item> - <item name="android:paddingEnd">22dip</item> - </style> - - <style name="PadLayoutStyle.Numeric"> - <item name="android:layout_width">0dip</item> - <item name="android:layout_weight">532</item> - <item name="android:paddingTop">10dip</item> - <item name="android:paddingBottom">10dip</item> - <item name="android:paddingStart">22dip</item> - <item name="android:paddingEnd">18dip</item> - </style> - - <style name="PadLayoutStyle.Operator"> - <item name="android:layout_width">0dip</item> - <item name="android:layout_weight">240</item> - <item name="android:paddingTop">10dip</item> - <item name="android:paddingBottom">10dip</item> - <item name="android:paddingStart">18dip</item> - <item name="android:paddingEnd">18dip</item> + <item name="android:textSize">26sp</item> </style> </resources> diff --git a/res/values-sw768dp-port/styles.xml b/res/values-sw768dp-port/styles.xml new file mode 100644 index 0000000..c9bceba --- /dev/null +++ b/res/values-sw768dp-port/styles.xml @@ -0,0 +1,63 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2014 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. + --> + +<!-- Styles for portrait 800dip-wide tablet (e.g. Nexus 10). --> +<resources xmlns:android="http://schemas.android.com/apk/res/android"> + + <style name="DisplayTextStyle.Formula"> + <item name="android:paddingTop">24dip</item> + <item name="android:paddingBottom">32dip</item> + <item name="android:paddingStart">44dip</item> + <item name="android:paddingEnd">44dip</item> + <item name="minTextSize">56sp</item> + <item name="maxTextSize">80sp</item> + <item name="stepTextSize">8sp</item> + </style> + + <style name="DisplayTextStyle.Result"> + <item name="android:paddingTop">24dip</item> + <item name="android:paddingBottom">56dip</item> + <item name="android:paddingStart">44dip</item> + <item name="android:paddingEnd">44dip</item> + <item name="android:textSize">56sp</item> + </style> + + <style name="PadButtonStyle.Advanced"> + <item name="android:background">@drawable/pad_button_advanced_background</item> + <item name="android:textColor">@color/pad_button_advanced_text_color</item> + <item name="android:textSize">32sp</item> + </style> + + <style name="PadButtonStyle.Advanced.Text"> + <item name="android:textAllCaps">true</item>s + <item name="android:textSize">28sp</item> + </style> + + <style name="PadButtonStyle.Numeric"> + <item name="android:textSize">38sp</item> + </style> + + <style name="PadButtonStyle.Operator"> + <item name="android:textSize">38sp</item> + </style> + + <style name="PadButtonStyle.Operator.Text"> + <item name="android:textAllCaps">true</item> + <item name="android:textSize">28sp</item> + </style> + +</resources> diff --git a/res/values-sw800dp-port/styles.xml b/res/values-sw800dp-port/styles.xml deleted file mode 100644 index 8ce7a12..0000000 --- a/res/values-sw800dp-port/styles.xml +++ /dev/null @@ -1,109 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - Copyright (C) 2014 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. - --> - -<!-- Styles for portrait 800dip-wide tablet (e.g. Nexus 10). --> -<resources xmlns:android="http://schemas.android.com/apk/res/android"> - - <style name="DisplayTextStyle.Formula"> - <item name="android:paddingTop">16dip</item> - <item name="android:paddingBottom">68dip</item> - <item name="android:paddingStart">44dip</item> - <item name="android:paddingEnd">44dip</item> - <item name="minTextSize">56sp</item> - <item name="maxTextSize">96sp</item> - <item name="stepTextSize">8sp</item> - </style> - - <style name="DisplayTextStyle.Result"> - <item name="android:paddingTop">6dip</item> - <item name="android:paddingBottom">80dip</item> - <item name="android:paddingStart">44dip</item> - <item name="android:paddingEnd">44dip</item> - <item name="android:textSize">40sp</item> - </style> - - <style name="PadButtonStyle.Advanced"> - <item name="android:layout_marginTop">8dip</item> - <item name="android:layout_marginBottom">8dip</item> - <item name="android:layout_marginStart">12dip</item> - <item name="android:layout_marginEnd">12dip</item> - <item name="android:background">@drawable/pad_button_advanced_background</item> - <item name="android:textColor">@color/pad_button_advanced_text_color</item> - <item name="android:textSize">36sp</item> - </style> - - <style name="PadButtonStyle.Advanced.Text"> - <item name="android:textAllCaps">true</item> - <item name="android:textSize">32sp</item> - </style> - - <style name="PadButtonStyle.Numeric"> - <item name="android:layout_margin">8dip</item> - <item name="android:textSize">48sp</item> - </style> - - <style name="PadButtonStyle.Numeric.Equals"> - <item name="android:visibility">gone</item> - </style> - - <style name="PadButtonStyle.Operator"> - <item name="android:layout_marginTop">8dip</item> - <item name="android:layout_marginBottom">8dip</item> - <item name="android:layout_marginStart">16dip</item> - <item name="android:layout_marginEnd">16dip</item> - <item name="android:textSize">48sp</item> - </style> - - <style name="PadButtonStyle.Operator.Text"> - <item name="android:textAllCaps">true</item> - <item name="android:textSize">32sp</item> - </style> - - <style name="PadLayoutStyle.Advanced"> - <item name="android:layout_height">0dip</item> - <item name="android:layout_weight">256</item> - <item name="android:paddingTop">8dip</item> - <item name="android:paddingBottom">8dip</item> - <item name="android:paddingStart">16dip</item> - <item name="android:paddingEnd">16dip</item> - </style> - - <style name="PadLayoutStyle.Numeric"> - <item name="android:layout_width">0dip</item> - <item name="android:layout_weight">532</item> - <item name="android:paddingTop">8dip</item> - <item name="android:paddingBottom">8dip</item> - <item name="android:paddingStart">16dip</item> - <item name="android:paddingEnd">16dip</item> - </style> - - <style name="PadLayoutStyle.Operator"> - <item name="android:layout_width">0dip</item> - <item name="android:layout_weight">240</item> - <item name="android:paddingTop">8dip</item> - <item name="android:paddingBottom">8dip</item> - <item name="android:paddingStart">0dip</item> - <item name="android:paddingEnd">8dip</item> - </style> - - <style name="PadLinearLayoutStyle"> - <item name="android:layout_width">match_parent</item> - <item name="android:layout_height">0dip</item> - <item name="android:layout_weight">508</item> - </style> - -</resources> diff --git a/src/com/android/calculator2/AlignedTextView.java b/src/com/android/calculator2/AlignedTextView.java new file mode 100644 index 0000000..1c4b78f --- /dev/null +++ b/src/com/android/calculator2/AlignedTextView.java @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2015 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.content.Context; +import android.graphics.Paint; +import android.graphics.Rect; +import android.text.TextUtils; +import android.util.AttributeSet; +import android.widget.TextView; + +import java.nio.charset.Charset; +import java.nio.charset.CharsetEncoder; + +/** + * Extended {@link TextView} that supports ascent/baseline alignment. + */ +public class AlignedTextView extends TextView { + + private static final String LATIN_CAPITAL_LETTER = "H"; + private static final CharsetEncoder LATIN_CHARSET_ENCODER = + Charset.forName("ISO-8859-1").newEncoder(); + + // temporary rect for use during layout + private final Rect mTempRect = new Rect(); + + private int mTopPaddingOffset; + private int mBottomPaddingOffset; + + public AlignedTextView(Context context) { + this(context, null /* attrs */); + } + + public AlignedTextView(Context context, AttributeSet attrs) { + this(context, attrs, android.R.attr.textViewStyle); + } + + public AlignedTextView(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + + // Disable any included font padding by default. + setIncludeFontPadding(false); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + CharSequence text = getText(); + if (TextUtils.isEmpty(text) || LATIN_CHARSET_ENCODER.canEncode(text)) { + // For latin text align to the default capital letter height. + text = LATIN_CAPITAL_LETTER; + } + getPaint().getTextBounds(text.toString(), 0, text.length(), mTempRect); + + final Paint textPaint = getPaint(); + mTopPaddingOffset = Math.min(getPaddingTop(), + (int) Math.floor(mTempRect.top - textPaint.ascent())); + mBottomPaddingOffset = Math.min(getPaddingBottom(), + (int) Math.floor(textPaint.descent())); + + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + } + + @Override + public int getCompoundPaddingTop() { + return super.getCompoundPaddingTop() - mTopPaddingOffset; + } + + @Override + public int getCompoundPaddingBottom() { + return super.getCompoundPaddingBottom() - mBottomPaddingOffset; + } +} diff --git a/src/com/android/calculator2/Calculator.java b/src/com/android/calculator2/Calculator.java index 3a4ac6d..0970174 100644 --- a/src/com/android/calculator2/Calculator.java +++ b/src/com/android/calculator2/Calculator.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2014 The Android Open Source Project + * Copyright (C) 2015 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. @@ -17,8 +17,6 @@ // FIXME: Menu handling, particularly for cut/paste, is very ugly // and not the way it was intended. // Other menus are not handled brilliantly either. -// TODO: Revisit handling of "Help" menu, so that it's more consistent -// with our conventions. // TODO: Better indication of when the result is known to be exact. // TODO: Check and possibly fix accessability issues. // TODO: Copy & more general paste in formula? Note that this requires @@ -37,6 +35,7 @@ import android.animation.Animator.AnimatorListener; import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; import android.animation.ObjectAnimator; +import android.animation.PropertyValuesHolder; import android.app.Activity; import android.app.AlertDialog; import android.content.DialogInterface; @@ -51,6 +50,7 @@ import android.support.v4.view.ViewPager; import android.text.SpannableString; import android.text.Spanned; import android.text.style.ForegroundColorSpan; +import android.util.Property; import android.view.KeyCharacterMap; import android.view.KeyEvent; import android.view.Menu; @@ -110,10 +110,21 @@ public class Calculator extends Activity // TODO: Possibly save a bit more information, e.g. its initial display string // or most significant digit position, to speed up restart. + private final Property<TextView, Integer> TEXT_COLOR = + new Property<TextView, Integer>(Integer.class, "textColor") { + @Override + public Integer get(TextView textView) { + return textView.getCurrentTextColor(); + } + + @Override + public void set(TextView textView, Integer textColor) { + textView.setTextColor(textColor); + } + }; + // We currently assume that the formula does not change out from under us in // any way. We explicitly handle all input to the formula here. - // TODO: Perhaps the formula should not be editable at all? - private final OnKeyListener mFormulaOnKeyListener = new OnKeyListener() { @Override public boolean onKey(View view, int keyCode, KeyEvent keyEvent) { @@ -132,17 +143,17 @@ public class Calculator extends Activity return true; default: final int raw = keyEvent.getKeyCharacterMap() - .get(keyCode, keyEvent.getMetaState()); + .get(keyCode, keyEvent.getMetaState()); if ((raw & KeyCharacterMap.COMBINING_ACCENT) != 0) { return true; // discard } // Try to discard non-printing characters and the like. // The user will have to explicitly delete other junk that gets past us. if (Character.isIdentifierIgnorable(raw) - || Character.isWhitespace(raw)) { + || Character.isWhitespace(raw)) { return true; } - char c = (char)raw; + char c = (char) raw; if (c == '=') { mCurrentButton = mEqualButton; onEquals(); @@ -168,7 +179,7 @@ public class Calculator extends Activity private View mDisplayView; private TextView mModeView; private CalculatorText mFormulaText; - private CalculatorResult mResult; + private CalculatorResult mResultText; private ViewPager mPadViewPager; private View mDeleteButton; @@ -200,7 +211,7 @@ public class Calculator extends Activity mDisplayView = findViewById(R.id.display); mModeView = (TextView) findViewById(R.id.mode); mFormulaText = (CalculatorText) findViewById(R.id.formula); - mResult = (CalculatorResult) findViewById(R.id.result); + mResultText = (CalculatorResult) findViewById(R.id.result); mPadViewPager = (ViewPager) findViewById(R.id.pad_pager); mDeleteButton = findViewById(R.id.del); @@ -224,8 +235,8 @@ public class Calculator extends Activity findViewById(R.id.fun_arctan) }; - mEvaluator = new Evaluator(this, mResult); - mResult.setEvaluator(mEvaluator); + mEvaluator = new Evaluator(this, mResultText); + mResultText.setEvaluator(mEvaluator); KeyMaps.setActivity(this); if (savedInstanceState != null) { @@ -313,17 +324,14 @@ public class Calculator extends Activity } if (mCurrentState == CalculatorState.ERROR) { - final int errorColor = getResources().getColor(R.color.calculator_error_color); + final int errorColor = getColor(R.color.calculator_error_color); mFormulaText.setTextColor(errorColor); - mResult.setTextColor(errorColor); + mResultText.setTextColor(errorColor); getWindow().setStatusBarColor(errorColor); - } else { - mFormulaText.setTextColor( - getResources().getColor(R.color.display_formula_text_color)); - mResult.setTextColor( - getResources().getColor(R.color.display_result_text_color)); - getWindow().setStatusBarColor( - getResources().getColor(R.color.calculator_accent_color)); + } else if (mCurrentState != CalculatorState.RESULT) { + mFormulaText.setTextColor(getColor(R.color.display_formula_text_color)); + mResultText.setTextColor(getColor(R.color.display_result_text_color)); + getWindow().setStatusBarColor(getColor(R.color.calculator_accent_color)); } invalidateOptionsMenu(); @@ -332,7 +340,7 @@ public class Calculator extends Activity // Stop any active ActionMode. Return true if there was one. private boolean stopActionMode() { - if (mResult.stopActionMode()) { + if (mResultText.stopActionMode()) { return true; } if (mFormulaText.stopActionMode()) { @@ -437,7 +445,7 @@ public class Calculator extends Activity if (mEvaluator.getExpr().hasInterestingOps()) { mEvaluator.evaluateAndShowResult(); } else { - mResult.clear(); + mResultText.clear(); } } @@ -482,7 +490,7 @@ public class Calculator extends Activity onModeChanged(mode); setState(CalculatorState.INPUT); - mResult.clear(); + mResultText.clear(); if (mEvaluator.getExpr().hasInterestingOps()) { mEvaluator.evaluateAndShowResult(); } @@ -525,7 +533,7 @@ public class Calculator extends Activity // Invalidate any options that may depend on the current result. invalidateOptionsMenu(); - mResult.displayResult(initDisplayPrec, leastDigPos, truncatedWholeNumber); + mResultText.displayResult(initDisplayPrec, leastDigPos, truncatedWholeNumber); if (mCurrentState != CalculatorState.INPUT) { // in EVALUATE or INIT state onResult(mCurrentState != CalculatorState.INIT); } @@ -535,13 +543,13 @@ public class Calculator extends Activity // We should be in EVALUATE state. // Display is still in input state. setState(CalculatorState.INPUT); - mResult.clear(); + mResultText.clear(); } // Reevaluation completed; ask result to redisplay current value. public void onReevaluate() { - mResult.redisplay(); + mResultText.redisplay(); } @Override @@ -660,7 +668,7 @@ public class Calculator extends Activity @Override public void onAnimationEnd(Animator animation) { mUnprocessedChars = null; - mResult.clear(); + mResultText.clear(); mEvaluator.clear(); setState(CalculatorState.INPUT); redisplayFormula(); @@ -677,14 +685,14 @@ public class Calculator extends Activity @Override public void onAnimationEnd(Animator animation) { setState(CalculatorState.ERROR); - mResult.displayError(errorResourceId); + mResultText.displayError(errorResourceId); } }); } else if (mCurrentState == CalculatorState.INIT) { setState(CalculatorState.ERROR); - mResult.displayError(errorResourceId); + mResultText.displayError(errorResourceId); } else { - mResult.clear(); + mResultText.clear(); } } @@ -692,50 +700,49 @@ public class Calculator extends Activity // Animate movement of result into the top formula slot. // Result window now remains translated in the top slot while the result is displayed. // (We convert it back to formula use only when the user provides new input.) - // Historical note: In the Lollipop version, this invisibly and instantaeously moved + // Historical note: In the Lollipop version, this invisibly and instantaneously moved // formula and result displays back at the end of the animation. We no longer do that, // so that we can continue to properly support scrolling of the result. // We assume the result already contains the text to be expanded. private void onResult(boolean animate) { - // Calculate the values needed to perform the scale and translation animations. - // The nominal font size in the result display is fixed. But the magnification we - // use when the user hits "=" is variable, with a scrollable result always getting - // minimum magnification. - // Display.xml is designed to ensure that a 5/4 increase is always possible. - // More is possible if the display is not fully occupied. - // Pivot the result around the bottom of the text. - final float resultScale = (float)Math.min(1.25f / mResult.getOccupancy(), 2.0); - // Keep the right end of text fixed as we scale. - mResult.setPivotX(mResult.getWidth() - mResult.getPaddingRight()); - // Move result up to take place of formula. Scale around top of formula. - mResult.setPivotY(mResult.getPaddingTop()); - float resultTranslationY = -mFormulaText.getHeight(); - // Move formula off screen. + // Calculate the textSize that would be used to display the result in the formula. + // For scrollable results just use the minimum textSize to maximize the number of digits + // that are visible on screen. + float textSize = mFormulaText.getMinimumTextSize(); + if (!mResultText.isScrollable()) { + textSize = mFormulaText.getVariableTextSize(mResultText.getText().toString()); + } + + // Scale the result to match the calculated textSize, minimizing the jump-cut transition + // when a result is reused in a subsequent expression. + final float resultScale = textSize / mResultText.getTextSize(); + + // Set the result's pivot to match its gravity. + mResultText.setPivotX(mResultText.getWidth() - mResultText.getPaddingRight()); + mResultText.setPivotY(mResultText.getHeight() - mResultText.getPaddingBottom()); + + // Calculate the necessary translations so the result takes the place of the formula and + // the formula moves off the top of the screen. + final float resultTranslationY = (mFormulaText.getBottom() - mResultText.getBottom()) + - (mFormulaText.getPaddingBottom() - mResultText.getPaddingBottom()); final float formulaTranslationY = -mFormulaText.getBottom(); - // TODO: Reintroduce textColorAnimator? - // The initial and final colors seemed to be the same in L. - // With the new model, the result logically changes back to a formula - // only when we switch back to INPUT state, so it's unclear that animating - // a color change here makes sense. + // Change the result's textColor to match the formula. + final int formulaTextColor = mFormulaText.getCurrentTextColor(); + if (animate) { final AnimatorSet animatorSet = new AnimatorSet(); animatorSet.playTogether( - ObjectAnimator.ofFloat(mResult, View.SCALE_X, resultScale), - ObjectAnimator.ofFloat(mResult, View.SCALE_Y, resultScale), - ObjectAnimator.ofFloat(mResult, View.TRANSLATION_Y, resultTranslationY), - ObjectAnimator.ofFloat(mFormulaText, View.TRANSLATION_Y, - formulaTranslationY)); - animatorSet.setDuration( - getResources().getInteger(android.R.integer.config_longAnimTime)); - animatorSet.setInterpolator(new AccelerateDecelerateInterpolator()); + ObjectAnimator.ofPropertyValuesHolder(mResultText, + PropertyValuesHolder.ofFloat(View.SCALE_X, resultScale), + PropertyValuesHolder.ofFloat(View.SCALE_Y, resultScale), + PropertyValuesHolder.ofFloat(View.TRANSLATION_Y, resultTranslationY)), + ObjectAnimator.ofArgb(mResultText, TEXT_COLOR, formulaTextColor), + ObjectAnimator.ofFloat(mFormulaText, View.TRANSLATION_Y, formulaTranslationY)); + animatorSet.setDuration(getResources().getInteger( + android.R.integer.config_longAnimTime)); animatorSet.addListener(new AnimatorListenerAdapter() { @Override - public void onAnimationStart(Animator animation) { - // Result should already be displayed; no need to do anything. - } - - @Override public void onAnimationEnd(Animator animation) { setState(CalculatorState.RESULT); mCurrentAnimator = null; @@ -745,9 +752,10 @@ public class Calculator extends Activity mCurrentAnimator = animatorSet; animatorSet.start(); } else /* No animation desired; get there fast, e.g. when restarting */ { - mResult.setScaleX(resultScale); - mResult.setScaleY(resultScale); - mResult.setTranslationY(resultTranslationY); + mResultText.setScaleX(resultScale); + mResultText.setScaleY(resultScale); + mResultText.setTranslationY(resultTranslationY); + mResultText.setTextColor(formulaTextColor); mFormulaText.setTranslationY(formulaTranslationY); setState(CalculatorState.RESULT); } @@ -757,12 +765,12 @@ public class Calculator extends Activity // pre-animation state. private void restoreDisplayPositions() { // Clear result. - mResult.setText(""); + mResultText.setText(""); // Reset all of the values modified during the animation. - mResult.setScaleX(1.0f); - mResult.setScaleY(1.0f); - mResult.setTranslationX(0.0f); - mResult.setTranslationY(0.0f); + mResultText.setScaleX(1.0f); + mResultText.setScaleY(1.0f); + mResultText.setTranslationX(0.0f); + mResultText.setTranslationY(0.0f); mFormulaText.setTranslationY(0.0f); mFormulaText.requestFocus(); @@ -808,13 +816,10 @@ public class Calculator extends Activity } private void displayMessage(String s) { - AlertDialog.Builder builder = new AlertDialog.Builder(this); - builder.setMessage(s) - .setNegativeButton(R.string.dismiss, - new DialogInterface.OnClickListener() { - public void onClick(DialogInterface d, int which) { } - }) - .show(); + new AlertDialog.Builder(this) + .setMessage(s) + .setNegativeButton(R.string.dismiss, null /* listener */) + .show(); } private void displayFraction() { @@ -825,8 +830,8 @@ public class Calculator extends Activity // Display full result to currently evaluated precision private void displayFull() { Resources res = getResources(); - String msg = mResult.getFullText() + " "; - if (mResult.fullTextIsExact()) { + String msg = mResultText.getFullText() + " "; + if (mResultText.fullTextIsExact()) { msg += res.getString(R.string.exact); } else { msg += res.getString(R.string.approximate); diff --git a/src/com/android/calculator2/CalculatorResult.java b/src/com/android/calculator2/CalculatorResult.java index a916c30..5b4fb86 100644 --- a/src/com/android/calculator2/CalculatorResult.java +++ b/src/com/android/calculator2/CalculatorResult.java @@ -16,23 +16,16 @@ package com.android.calculator2; -import android.content.ClipboardManager; import android.content.ClipData; import android.content.ClipDescription; +import android.content.ClipboardManager; import android.content.Context; -import android.graphics.Typeface; -import android.graphics.Paint; -import android.graphics.Rect; -import android.graphics.Color; -import android.net.Uri; -import android.widget.TextView; -import android.widget.OverScroller; -import android.text.Editable; +import android.text.Layout; import android.text.SpannableString; import android.text.Spanned; +import android.text.TextPaint; import android.text.style.ForegroundColorSpan; import android.util.AttributeSet; -import android.util.Log; import android.view.ActionMode; import android.view.GestureDetector; import android.view.Menu; @@ -40,14 +33,12 @@ import android.view.MenuInflater; import android.view.MenuItem; import android.view.MotionEvent; import android.view.View; +import android.widget.OverScroller; import android.widget.Toast; -import android.support.v4.view.ViewCompat; - - // A text widget that is "infinitely" scrollable to the right, // and obtains the text to display via a callback to Logic. -public class CalculatorResult extends TextView { +public class CalculatorResult extends AlignedTextView { static final int MAX_RIGHT_SCROLL = 10000000; static final int INVALID = MAX_RIGHT_SCROLL + 10000; // A larger value is unlikely to avoid running out of space @@ -56,8 +47,7 @@ public class CalculatorResult extends TextView { class MyTouchListener implements View.OnTouchListener { @Override public boolean onTouch(View v, MotionEvent event) { - boolean res = mGestureDetector.onTouchEvent(event); - return res; + return mGestureDetector.onTouchEvent(event); } } final MyTouchListener mTouchListener = new MyTouchListener(); @@ -75,11 +65,11 @@ public class CalculatorResult extends TextView { private int mMinPos; // Minimum position before all digits disappear off the right. Pixels. private int mMaxPos; // Maximum position before we start displaying the infinite // sequence of trailing zeroes on the right. Pixels. - private Object mWidthLock = new Object(); + private final Object mWidthLock = new Object(); // Protects the next two fields. private int mWidthConstraint = -1; // Our total width in pixels. - private int mCharWidth = 1; + private float mCharWidth = 1; // Maximum character width. For now we pretend that all characters // have this width. // TODO: We're not really using a fixed width font. But it appears @@ -112,7 +102,7 @@ public class CalculatorResult extends TextView { if (!mScrollable) return true; mScroller.fling(mCurrentPos, 0, - (int) velocityX, 0 /* horizontal only */, mMinPos, mMaxPos, 0, 0); - ViewCompat.postInvalidateOnAnimation(CalculatorResult.this); + postInvalidateOnAnimation(); return true; } @Override @@ -134,7 +124,7 @@ public class CalculatorResult extends TextView { int duration = (int)(e2.getEventTime() - e1.getEventTime()); if (duration < 1 || duration > 100) duration = 10; mScroller.startScroll(mCurrentPos, 0, distance, 0, (int)duration); - ViewCompat.postInvalidateOnAnimation(CalculatorResult.this); + postInvalidateOnAnimation(); return true; } @Override @@ -163,21 +153,11 @@ public class CalculatorResult extends TextView { protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); - char testChar = KeyMaps.translateResult("5").charAt(0); - // TODO: Redo on Locale change? Doesn't seem to matter? - // We try to determine the maximal size of a digit plus corresponding inter-character - // space. We assume that "5" has maximal width. Since any string includes one fewer - // inter-character space than characters, me measure one that's longer than any real - // display string, and then divide by the number of characters. This should bound - // the per-character space we need for any real string. - StringBuilder sb = new StringBuilder(MAX_WIDTH); - for (int i = 0; i < MAX_WIDTH; ++i) { - sb.append(testChar); - } - final int newWidthConstraint = - MeasureSpec.getSize(widthMeasureSpec) - getPaddingLeft() - getPaddingRight(); - final int newCharWidth = - (int)Math.ceil(getPaint().measureText(sb.toString()) / MAX_WIDTH); + final TextPaint paint = getPaint(); + final int newWidthConstraint = MeasureSpec.getSize(widthMeasureSpec) + - (getPaddingLeft() + getPaddingRight()) + - (int) Math.ceil(Layout.getDesiredWidth(KeyMaps.ELLIPSIS, paint)); + final float newCharWidth = Layout.getDesiredWidth("\u2007", paint); synchronized(mWidthLock) { mWidthConstraint = newWidthConstraint; mCharWidth = newCharWidth; @@ -208,15 +188,16 @@ public class CalculatorResult extends TextView { void displayResult(int initPrec, int leastDigPos, String truncatedWholePart) { mLastPos = INVALID; synchronized(mWidthLock) { - mCurrentPos = initPrec * mCharWidth; + mCurrentPos = (int) Math.ceil(initPrec * mCharWidth); } // Should logically be // mMinPos = - (int) Math.ceil(getPaint().measureText(truncatedWholePart)), but // we eventually transalate to a character position by dividing by mCharWidth. // To avoid rounding issues, we use the analogous computation here. - mMinPos = - truncatedWholePart.length() * mCharWidth; + mMinPos = - (int) Math.ceil(truncatedWholePart.length() * mCharWidth); if (leastDigPos < MAX_RIGHT_SCROLL) { - mMaxPos = Math.min(addExpSpace(leastDigPos) * mCharWidth, MAX_RIGHT_SCROLL); + mMaxPos = Math.min((int) Math.ceil(addExpSpace(leastDigPos) * mCharWidth), + MAX_RIGHT_SCROLL); } else { mMaxPos = MAX_RIGHT_SCROLL; } @@ -350,11 +331,9 @@ public class CalculatorResult extends TextView { * May be called asynchronously from non-UI thread. */ int getMaxChars() { - // We only use 4/5 of the available space, since at least the left 4/5 of the result - // is not visible when it is shown in large size. int result; synchronized(mWidthLock) { - result = 4 * mWidthConstraint / (5 * mCharWidth); + result = (int) Math.floor(mWidthConstraint / mCharWidth); // We can apparently finish evaluating before onMeasure in CalculatorText has been // called, in which case we get 0 or -1 as the width constraint. } @@ -362,26 +341,22 @@ public class CalculatorResult extends TextView { // Return something conservatively big, to force sufficient evaluation. return MAX_WIDTH; } else { - return result; + // Always allow for the ellipsis character which already accounted for in the width + // constraint. + return result + 1; } } /** - * Return the fraction of the available character space occupied by the - * current result. - * Should be called only with a valid result displayed. + * @return {@code true} if the currently displayed result is scrollable */ - float getOccupancy() { - if (mScrollable) { - return 1.0f; - } else { - return (float)getText().length() / getMaxChars(); - } + public boolean isScrollable() { + return mScrollable; } int getCurrentCharPos() { synchronized(mWidthLock) { - return mCurrentPos/mCharWidth; + return (int) Math.ceil(mCurrentPos / mCharWidth); } } @@ -419,7 +394,7 @@ public class CalculatorResult extends TextView { redisplay(); } if (!mScroller.isFinished()) { - ViewCompat.postInvalidateOnAnimation(this); + postInvalidateOnAnimation(); } } } diff --git a/src/com/android/calculator2/CalculatorText.java b/src/com/android/calculator2/CalculatorText.java index 1b16bca..c6f38ae 100644 --- a/src/com/android/calculator2/CalculatorText.java +++ b/src/com/android/calculator2/CalculatorText.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2014 The Android Open Source Project + * Copyright (C) 2015 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. @@ -16,39 +16,31 @@ package com.android.calculator2; -import android.content.ClipboardManager; import android.content.ClipData; +import android.content.ClipboardManager; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Paint; -import android.graphics.Paint.FontMetricsInt; -import android.graphics.Rect; import android.net.Uri; -import android.os.Parcelable; -import android.text.method.ScrollingMovementMethod; import android.text.TextPaint; +import android.text.method.ScrollingMovementMethod; import android.util.AttributeSet; -import android.util.Log; import android.util.TypedValue; import android.view.ActionMode; -import android.view.GestureDetector; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; -import android.view.MotionEvent; import android.view.View; import android.widget.TextView; /** * TextView adapted for Calculator display. */ - -public class CalculatorText extends TextView implements View.OnLongClickListener{ +public class CalculatorText extends AlignedTextView implements View.OnLongClickListener { private ActionMode mActionMode; - private final ActionMode.Callback mPasteActionModeCallback = - new ActionMode.Callback() { + private final ActionMode.Callback mPasteActionModeCallback = new ActionMode.Callback() { @Override public boolean onActionItemClicked(ActionMode mode, MenuItem item) { switch (item.getItemId()) { @@ -109,9 +101,8 @@ public class CalculatorText extends TextView implements View.OnLongClickListener private final float mMinimumTextSize; private final float mStepTextSize; - // Temporary objects for use in layout methods. + // Temporary paint for use in layout methods. private final Paint mTempPaint = new TextPaint(); - private final Rect mTempRect = new Rect(); private int mWidthConstraint = -1; private OnTextSizeChangeListener mOnTextSizeChangeListener; @@ -146,7 +137,6 @@ public class CalculatorText extends TextView implements View.OnLongClickListener setMovementMethod(ScrollingMovementMethod.getInstance()); setTextSize(TypedValue.COMPLEX_UNIT_PX, mMaximumTextSize); - setMinHeight(getLineHeight() + getCompoundPaddingBottom() + getCompoundPaddingTop()); } @Override @@ -159,8 +149,13 @@ public class CalculatorText extends TextView implements View.OnLongClickListener protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); - mWidthConstraint = - MeasureSpec.getSize(widthMeasureSpec) - getPaddingLeft() - getPaddingRight(); + // Prevent shrinking/resizing with our variable textSize. + if (!isLaidOut()) { + setMinHeight(getLineHeight() + getCompoundPaddingBottom() + getCompoundPaddingTop()); + } + + mWidthConstraint = MeasureSpec.getSize(widthMeasureSpec) + - getPaddingLeft() - getPaddingRight(); setTextSize(TypedValue.COMPLEX_UNIT_PX, getVariableTextSize(getText().toString())); } @@ -170,7 +165,6 @@ public class CalculatorText extends TextView implements View.OnLongClickListener protected void onTextChanged(CharSequence text, int start, int lengthBefore, int lengthAfter) { super.onTextChanged(text, start, lengthBefore, lengthAfter); - final int textLength = text.length(); setTextSize(TypedValue.COMPLEX_UNIT_PX, getVariableTextSize(text.toString())); } @@ -188,6 +182,14 @@ public class CalculatorText extends TextView implements View.OnLongClickListener mOnTextSizeChangeListener = listener; } + public float getMinimumTextSize() { + return mMinimumTextSize; + } + + public float getMaximumTextSize() { + return mMaximumTextSize; + } + public float getVariableTextSize(String text) { if (mWidthConstraint < 0 || mMaximumTextSize <= mMinimumTextSize) { // Not measured, bail early. @@ -212,25 +214,6 @@ public class CalculatorText extends TextView implements View.OnLongClickListener return lastFitTextSize; } - @Override - public int getCompoundPaddingTop() { - // Measure the top padding from the capital letter height of the text instead of the top, - // but don't remove more than the available top padding otherwise clipping may occur. - getPaint().getTextBounds("H", 0, 1, mTempRect); - - final FontMetricsInt fontMetrics = getPaint().getFontMetricsInt(); - final int paddingOffset = -(fontMetrics.ascent + mTempRect.height()); - return super.getCompoundPaddingTop() - Math.min(getPaddingTop(), paddingOffset); - } - - @Override - public int getCompoundPaddingBottom() { - // Measure the bottom padding from the baseline of the text instead of the bottom, but don't - // remove more than the available bottom padding otherwise clipping may occur. - final FontMetricsInt fontMetrics = getPaint().getFontMetricsInt(); - return super.getCompoundPaddingBottom() - Math.min(getPaddingBottom(), fontMetrics.descent); - } - public boolean stopActionMode() { if (mActionMode != null) { mActionMode.finish(); diff --git a/src/com/android/calculator2/Evaluator.java b/src/com/android/calculator2/Evaluator.java index 6362efe..4ddaf15 100644 --- a/src/com/android/calculator2/Evaluator.java +++ b/src/com/android/calculator2/Evaluator.java @@ -796,6 +796,14 @@ class Evaluator { return mDegreeMode; } + /** + * @return the {@link CalculatorExpr} representation of the current result + */ + CalculatorExpr getResultExpr() { + final BigInteger intVal = BoundedRational.asBigInteger(mRatVal); + return mExpr.abbreviate(mVal, mRatVal, mDegreeMode, getShortString(mCache, intVal)); + } + // Abbreviate the current expression to a pre-evaluated // expression node, which will display as a short number. // This should not be called unless the expression was @@ -805,11 +813,8 @@ class Evaluator { // diverges. Subsequent re-evaluation will also not diverge, // though it may generate errors of various kinds. // E.g. sqrt(-10^-1000) - void collapse () { - BigInteger intVal = BoundedRational.asBigInteger(mRatVal); - CalculatorExpr abbrvExpr = mExpr.abbreviate( - mVal, mRatVal, mDegreeMode, - getShortString(mCache, intVal)); + void collapse() { + final CalculatorExpr abbrvExpr = getResultExpr(); clear(); mExpr.append(abbrvExpr); } @@ -817,11 +822,11 @@ class Evaluator { // Same as above, but put result in mSaved, leaving mExpr alone. // Return false if result is unavailable. boolean collapseToSaved() { - if (mCache == null) return false; - BigInteger intVal = BoundedRational.asBigInteger(mRatVal); - CalculatorExpr abbrvExpr = mExpr.abbreviate( - mVal, mRatVal, mDegreeMode, - getShortString(mCache, intVal)); + if (mCache == null) { + return false; + } + + final CalculatorExpr abbrvExpr = getResultExpr(); mSaved.clear(); mSaved.append(abbrvExpr); return true; |