From 76b8901a4efa7829c6ac8e546d2ebee7eff28e82 Mon Sep 17 00:00:00 2001 From: serso Date: Fri, 15 Apr 2016 22:29:04 +0200 Subject: [PATCH 1/8] Support libraries update --- app/build.gradle | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 8be66346..ac367c1b 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -66,9 +66,9 @@ android { dependencies { compile fileTree(include: ['*.jar'], dir: 'libs') - compile 'com.android.support:support-v4:23.2.1' - compile 'com.android.support:appcompat-v7:23.2.1' - compile 'com.android.support:design:23.2.1' + compile 'com.android.support:support-v4:23.3.0' + compile 'com.android.support:appcompat-v7:23.3.0' + compile 'com.android.support:design:23.3.0' compile('ch.acra:acra:4.7.0') { exclude group: 'org.json' } @@ -105,7 +105,7 @@ dependencies { testCompile 'org.skyscreamer:jsonassert:1.2.3' testCompile(name: 'org.apache.http.legacy', ext: 'jar') - androidTestCompile 'com.android.support:support-annotations:23.2.1' + androidTestCompile 'com.android.support:support-annotations:23.3.0' androidTestCompile 'com.android.support.test:runner:0.4.1' androidTestCompile 'com.android.support.test:rules:0.4.1' androidTestCompile 'org.hamcrest:hamcrest-library:1.3' From 28a573c777e22b5e57fbcfeff3bdbdeda364d0b2 Mon Sep 17 00:00:00 2001 From: serso Date: Fri, 15 Apr 2016 22:53:44 +0200 Subject: [PATCH 2/8] Exponent can't be first in number + tests for NumberInputFilter --- .../text/method/NumberInputFilter.java | 5 +- .../text/method/NumberInputFilterTest.java | 112 ++++++++++++++++++ 2 files changed, 116 insertions(+), 1 deletion(-) create mode 100644 app/src/test/java/org/solovyev/android/text/method/NumberInputFilterTest.java diff --git a/app/src/main/java/org/solovyev/android/text/method/NumberInputFilter.java b/app/src/main/java/org/solovyev/android/text/method/NumberInputFilter.java index 5600c096..f7114173 100644 --- a/app/src/main/java/org/solovyev/android/text/method/NumberInputFilter.java +++ b/app/src/main/java/org/solovyev/android/text/method/NumberInputFilter.java @@ -61,7 +61,7 @@ public class NumberInputFilter implements InputFilter { findChars(dest, dend, dest.length(), end - start, CHARS); SpannableStringBuilder filtered = null; - for (int i = end - 1; i >= start; i--) { + for (int i = start; i < end; i++) { final char c = source.charAt(i); boolean filter = false; @@ -93,6 +93,9 @@ public class NumberInputFilter implements InputFilter { } else if (CHARS[CHAR_POINT] >= 0 && CHARS[CHAR_POINT] > i + dstart) { // no exponent before decimal point filter = true; + } else if (i + dstart == 0) { + // exponent can't be first + filter = true; } else { CHARS[CHAR_EXP] = i + dstart; } diff --git a/app/src/test/java/org/solovyev/android/text/method/NumberInputFilterTest.java b/app/src/test/java/org/solovyev/android/text/method/NumberInputFilterTest.java new file mode 100644 index 00000000..f32c0282 --- /dev/null +++ b/app/src/test/java/org/solovyev/android/text/method/NumberInputFilterTest.java @@ -0,0 +1,112 @@ +package org.solovyev.android.text.method; + +import android.os.Build; +import android.text.Editable; +import android.text.InputFilter; +import android.text.SpannableStringBuilder; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricGradleTestRunner; +import org.robolectric.annotation.Config; +import org.solovyev.android.calculator.BuildConfig; + +import static org.junit.Assert.assertEquals; + +@Config(constants = BuildConfig.class, sdk = Build.VERSION_CODES.LOLLIPOP) +@RunWith(RobolectricGradleTestRunner.class) +public class NumberInputFilterTest { + + private Editable editable; + + @Before + public void setUp() throws Exception { + editable = new SpannableStringBuilder(); + editable.setFilters(new InputFilter[]{new NumberInputFilter()}); + } + + @Test + public void testShouldNotInsertExponentInTheBeginning() throws Exception { + editable.insert(0, "E"); + assertEquals("", editable.toString()); + } + + @Test + public void testShouldInsertExponentAtTheEnd() throws Exception { + editable.insert(0, "1"); + editable.insert(1, "E"); + assertEquals("1E", editable.toString()); + } + + @Test + public void testShouldNotInsertSecondMinusSign() throws Exception { + editable.insert(0, "-"); + editable.insert(1, "-"); + assertEquals("-", editable.toString()); + } + + @Test + public void testShouldNotInsertTwoMinusSigns() throws Exception { + editable.insert(0, "--"); + assertEquals("-", editable.toString()); + } + + @Test + public void testShouldInsertSecondMinusSignAfterExponent() throws Exception { + editable.insert(0, "-"); + editable.insert(1, "E"); + editable.insert(2, "-"); + assertEquals("-E-", editable.toString()); + } + + @Test + public void testShouldInsertSecondMinusSignAlongWithExponent() throws Exception { + editable.insert(0, "-"); + editable.insert(1, "E-"); + assertEquals("-E-", editable.toString()); + } + + @Test + public void testShouldNotInsertMinusSignBeforeExistingMinusSIgn() throws Exception { + editable.insert(0, "-"); + editable.insert(0, "-"); + assertEquals("-", editable.toString()); + } + + @Test + public void testShouldNotInsertSecondDecimalPoint() throws Exception { + editable.insert(0, "0.2"); + editable.insert(3, "."); + assertEquals("0.2", editable.toString()); + } + + @Test + public void testShouldNotInsertTwoDecimalPoints() throws Exception { + editable.insert(0, ".."); + assertEquals(".", editable.toString()); + } + + @Test + public void testShouldNotInsertDecimalPointAfterExponent() throws Exception { + editable.insert(0, "2E"); + editable.insert(2, "."); + assertEquals("2E", editable.toString()); + + editable.clear(); + editable.insert(0, "2E."); + assertEquals("2E", editable.toString()); + } + + @Test + public void testShouldNotInsertTwoExcponents() throws Exception { + editable.insert(0, "2EE"); + assertEquals("2E", editable.toString()); + } + + @Test + public void testShouldNotInsertExponentBeforeDecimalPoint() throws Exception { + editable.insert(0, "0.2"); + editable.insert(0, "E"); + assertEquals("0.2", editable.toString()); + } +} \ No newline at end of file From ba7207d0e6787634193f64b471e90a5407d9b874 Mon Sep 17 00:00:00 2001 From: serso Date: Sat, 16 Apr 2016 20:51:18 +0200 Subject: [PATCH 3/8] AMOLED-friendly theme added --- .../android/calculator/Preferences.java | 26 +++++- .../android/calculator/language/Language.java | 15 +++- .../preferences/PreferenceEntry.java | 12 +++ .../preferences/PreferencesFragment.java | 45 ++++++++--- .../wizard/ChooseThemeWizardStep.java | 16 ++-- .../drawable-v21/material_button_black.xml | 32 ++++++++ .../material_button_black_lighter.xml | 32 ++++++++ .../material_button_deep_blue.xml | 32 ++++++++ .../material_button_deep_blue_lighter.xml | 32 ++++++++ .../res/drawable/material_button_black.xml | 35 ++++++++ .../material_button_black_lighter.xml | 35 ++++++++ .../drawable/material_button_deep_blue.xml | 39 +++++++++ .../material_button_deep_blue_lighter.xml | 39 +++++++++ app/src/main/res/values/arrays.xml | 15 ---- app/src/main/res/values/colors.xml | 5 ++ .../main/res/values/text_non_translatable.xml | 1 + .../main/res/values/theme_material_black.xml | 79 +++++++++++++++++++ .../main/res/xml/preferences_appearance.xml | 2 - 18 files changed, 447 insertions(+), 45 deletions(-) create mode 100644 app/src/main/java/org/solovyev/android/calculator/preferences/PreferenceEntry.java create mode 100644 app/src/main/res/drawable-v21/material_button_black.xml create mode 100644 app/src/main/res/drawable-v21/material_button_black_lighter.xml create mode 100644 app/src/main/res/drawable-v21/material_button_deep_blue.xml create mode 100644 app/src/main/res/drawable-v21/material_button_deep_blue_lighter.xml create mode 100644 app/src/main/res/drawable/material_button_black.xml create mode 100644 app/src/main/res/drawable/material_button_black_lighter.xml create mode 100644 app/src/main/res/drawable/material_button_deep_blue.xml create mode 100644 app/src/main/res/drawable/material_button_deep_blue_lighter.xml create mode 100644 app/src/main/res/values/theme_material_black.xml diff --git a/app/src/main/java/org/solovyev/android/calculator/Preferences.java b/app/src/main/java/org/solovyev/android/calculator/Preferences.java index 95a4c794..0622d50e 100644 --- a/app/src/main/java/org/solovyev/android/calculator/Preferences.java +++ b/app/src/main/java/org/solovyev/android/calculator/Preferences.java @@ -33,8 +33,6 @@ import android.support.annotation.*; import android.support.v7.view.ContextThemeWrapper; import android.text.TextUtils; import android.util.SparseArray; -import jscl.AngleUnit; -import jscl.NumeralBase; import org.solovyev.android.Check; import org.solovyev.android.calculator.about.AboutActivity; import org.solovyev.android.calculator.functions.FunctionsActivity; @@ -42,6 +40,7 @@ import org.solovyev.android.calculator.history.HistoryActivity; import org.solovyev.android.calculator.language.Languages; import org.solovyev.android.calculator.math.MathType; import org.solovyev.android.calculator.operators.OperatorsActivity; +import org.solovyev.android.calculator.preferences.PreferenceEntry; import org.solovyev.android.calculator.preferences.PreferencesActivity; import org.solovyev.android.calculator.variables.VariablesActivity; import org.solovyev.android.calculator.wizard.WizardActivity; @@ -285,7 +284,7 @@ public final class Preferences { return mode.getPreferenceNoError(preferences); } - public enum Theme { + public enum Theme implements PreferenceEntry { default_theme(R.style.Cpp_Theme_Gray), violet_theme(R.style.Cpp_Theme_Violet), @@ -294,12 +293,19 @@ public final class Preferences { metro_purple_theme(R.string.p_metro_purple_theme, R.style.Cpp_Theme_Metro_Purple, R.style.Cpp_Theme_Metro_Purple_Calculator, R.style.Cpp_Theme_Wizard, R.style.Cpp_Theme_Metro_Purple_Dialog, R.style.Cpp_Theme_Material_Dialog_Alert), metro_green_theme(R.string.p_metro_green_theme, R.style.Cpp_Theme_Metro_Green, R.style.Cpp_Theme_Metro_Green_Calculator, R.style.Cpp_Theme_Wizard, R.style.Cpp_Theme_Metro_Green_Dialog, R.style.Cpp_Theme_Material_Dialog_Alert), material_theme(R.string.cpp_theme_dark, R.style.Cpp_Theme_Material, R.style.Cpp_Theme_Material_Calculator), + material_black_theme(R.string.cpp_theme_black, R.style.Cpp_Theme_Material_Black, R.style.Cpp_Theme_Material_Black_Calculator) { + @NonNull + @Override + public String getName(@NonNull Context context) { + return context.getString(name, material_theme.getName(context)); + } + }, material_light_theme(R.string.cpp_theme_light, R.style.Cpp_Theme_Material_Light, R.style.Cpp_Theme_Material_Light_Calculator, R.style.Cpp_Theme_Wizard_Light, R.style.Cpp_Theme_Material_Light_Dialog, R.style.Cpp_Theme_Material_Light_Dialog_Alert); private static final SparseArray textColors = new SparseArray<>(); @StringRes - public final int name; + protected final int name; @StyleRes public final int theme; @StyleRes @@ -373,6 +379,18 @@ public final class Preferences { } return textColor; } + + @Override + @NonNull + public String getName(@NonNull Context context) { + return context.getString(name); + } + + @NonNull + @Override + public CharSequence getId() { + return name(); + } } public enum Mode { diff --git a/app/src/main/java/org/solovyev/android/calculator/language/Language.java b/app/src/main/java/org/solovyev/android/calculator/language/Language.java index dab1dfb9..21d0a2a0 100644 --- a/app/src/main/java/org/solovyev/android/calculator/language/Language.java +++ b/app/src/main/java/org/solovyev/android/calculator/language/Language.java @@ -1,15 +1,15 @@ package org.solovyev.android.calculator.language; import android.content.Context; +import android.support.annotation.NonNull; import android.text.TextUtils; - import org.solovyev.android.calculator.R; - -import java.util.Locale; +import org.solovyev.android.calculator.preferences.PreferenceEntry; import javax.annotation.Nonnull; +import java.util.Locale; -public final class Language { +public final class Language implements PreferenceEntry{ @Nonnull public final String code; @@ -39,6 +39,7 @@ public final class Language { return locale.getDisplayName(locale); } + @Override @Nonnull public String getName(@Nonnull Context context) { if (!isSystem()) { @@ -48,6 +49,12 @@ public final class Language { } } + @NonNull + @Override + public CharSequence getId() { + return code; + } + public boolean isSystem() { return code.equals(Languages.SYSTEM_LANGUAGE_CODE); } diff --git a/app/src/main/java/org/solovyev/android/calculator/preferences/PreferenceEntry.java b/app/src/main/java/org/solovyev/android/calculator/preferences/PreferenceEntry.java new file mode 100644 index 00000000..21aebcb2 --- /dev/null +++ b/app/src/main/java/org/solovyev/android/calculator/preferences/PreferenceEntry.java @@ -0,0 +1,12 @@ +package org.solovyev.android.calculator.preferences; + +import android.content.Context; +import android.support.annotation.NonNull; + +public interface PreferenceEntry { + @NonNull + CharSequence getName(@NonNull Context context); + + @NonNull + CharSequence getId(); +} diff --git a/app/src/main/java/org/solovyev/android/calculator/preferences/PreferencesFragment.java b/app/src/main/java/org/solovyev/android/calculator/preferences/PreferencesFragment.java index c40ae3f3..e18bd7cf 100644 --- a/app/src/main/java/org/solovyev/android/calculator/preferences/PreferencesFragment.java +++ b/app/src/main/java/org/solovyev/android/calculator/preferences/PreferencesFragment.java @@ -1,5 +1,6 @@ package org.solovyev.android.calculator.preferences; +import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.os.Build; @@ -13,6 +14,7 @@ import android.widget.ListView; import org.solovyev.android.calculator.AdView; import org.solovyev.android.calculator.Engine; import org.solovyev.android.calculator.Preferences; +import org.solovyev.android.calculator.Preferences.Gui.Theme; import org.solovyev.android.calculator.R; import org.solovyev.android.calculator.language.Language; import org.solovyev.android.calculator.language.Languages; @@ -25,6 +27,7 @@ import org.solovyev.android.wizard.Wizards; import javax.annotation.Nonnull; import javax.annotation.Nullable; import javax.inject.Inject; +import java.util.Arrays; import java.util.List; import static org.solovyev.android.calculator.App.cast; @@ -154,32 +157,50 @@ public class PreferencesFragment extends org.solovyev.android.material.preferenc return; } final ListPreference theme = (ListPreference) preferenceManager.findPreference(Preferences.Gui.theme.getKey()); - theme.setSummary(Preferences.Gui.getTheme(preferences).name); + final FragmentActivity context = getActivity(); + populate(theme, + Theme.material_theme, + Theme.material_black_theme, + Theme.material_light_theme, + Theme.metro_blue_theme, + Theme.metro_green_theme, + Theme.metro_purple_theme); + theme.setSummary(Preferences.Gui.getTheme(preferences).getName(context)); theme.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { @Override public boolean onPreferenceChange(Preference preference, Object newValue) { - theme.setSummary(Preferences.Gui.Theme.valueOf((String) newValue).name); + final Theme newTheme = Theme.valueOf((String) newValue); + theme.setSummary(newTheme.getName(context)); return true; } }); } + private static void populate(@Nonnull ListPreference preference, @Nonnull PreferenceEntry... entries) { + populate(preference, Arrays.asList(entries)); + } + + private static void populate(@Nonnull ListPreference preference, @Nonnull List entries) { + final int size = entries.size(); + final CharSequence[] e = new CharSequence[size]; + final CharSequence[] v = new CharSequence[size]; + final Context context = preference.getContext(); + for (int i = 0; i < size; i++) { + final PreferenceEntry entry = entries.get(i); + e[i] = entry.getName(context); + v[i] = entry.getId(); + } + preference.setEntries(e); + preference.setEntryValues(v); + } + private void prepareLanguagePreference(int preference) { if (preference != R.xml.preferences_appearance) { return; } final ListPreference language = (ListPreference) preferenceManager.findPreference(Preferences.Gui.language.getKey()); - final List languagesList = languages.getList(); - final CharSequence[] entries = new CharSequence[languagesList.size()]; - final CharSequence[] entryValues = new CharSequence[languagesList.size()]; - for (int i = 0; i < languagesList.size(); i++) { - final Language l = languagesList.get(i); - entries[i] = l.getName(getActivity()); - entryValues[i] = l.code; - } - language.setEntries(entries); - language.setEntryValues(entryValues); + populate(language, languages.getList()); language.setSummary(languages.getCurrent().getName(getActivity())); language.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { @Override diff --git a/app/src/main/java/org/solovyev/android/calculator/wizard/ChooseThemeWizardStep.java b/app/src/main/java/org/solovyev/android/calculator/wizard/ChooseThemeWizardStep.java index f29670a8..78244e8b 100644 --- a/app/src/main/java/org/solovyev/android/calculator/wizard/ChooseThemeWizardStep.java +++ b/app/src/main/java/org/solovyev/android/calculator/wizard/ChooseThemeWizardStep.java @@ -23,7 +23,6 @@ package org.solovyev.android.calculator.wizard; import android.os.Bundle; -import android.support.annotation.StringRes; import android.support.v7.view.ContextThemeWrapper; import android.view.LayoutInflater; import android.view.View; @@ -58,11 +57,12 @@ public class ChooseThemeWizardStep extends WizardFragment implements AdapterView final Preferences.Gui.Theme theme = Preferences.Gui.getTheme(preferences); final Spinner spinner = (Spinner) root.findViewById(R.id.wizard_theme_spinner); themes.clear(); - themes.add(new ThemeUi(Preferences.Gui.Theme.material_theme, R.string.cpp_theme_dark)); - themes.add(new ThemeUi(Preferences.Gui.Theme.material_light_theme, R.string.cpp_theme_light)); - themes.add(new ThemeUi(Preferences.Gui.Theme.metro_blue_theme, R.string.p_metro_blue_theme)); - themes.add(new ThemeUi(Preferences.Gui.Theme.metro_green_theme, R.string.p_metro_green_theme)); - themes.add(new ThemeUi(Preferences.Gui.Theme.metro_purple_theme, R.string.p_metro_purple_theme)); + themes.add(new ThemeUi(Preferences.Gui.Theme.material_theme)); + themes.add(new ThemeUi(Preferences.Gui.Theme.material_black_theme)); + themes.add(new ThemeUi(Preferences.Gui.Theme.material_light_theme)); + themes.add(new ThemeUi(Preferences.Gui.Theme.metro_blue_theme)); + themes.add(new ThemeUi(Preferences.Gui.Theme.metro_green_theme)); + themes.add(new ThemeUi(Preferences.Gui.Theme.metro_purple_theme)); adapter = new WizardArrayAdapter<>(getActivity(), themes); spinner.setAdapter(adapter); spinner.setSelection(findPosition(theme)); @@ -111,9 +111,9 @@ public class ChooseThemeWizardStep extends WizardFragment implements AdapterView @Nonnull final String name; - public ThemeUi(@Nonnull Preferences.Gui.Theme theme, @StringRes int name) { + public ThemeUi(@Nonnull Preferences.Gui.Theme theme) { this.theme = theme; - this.name = getString(name); + this.name = theme.getName(getActivity()); } @Override diff --git a/app/src/main/res/drawable-v21/material_button_black.xml b/app/src/main/res/drawable-v21/material_button_black.xml new file mode 100644 index 00000000..360413b6 --- /dev/null +++ b/app/src/main/res/drawable-v21/material_button_black.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable-v21/material_button_black_lighter.xml b/app/src/main/res/drawable-v21/material_button_black_lighter.xml new file mode 100644 index 00000000..d7fc0204 --- /dev/null +++ b/app/src/main/res/drawable-v21/material_button_black_lighter.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable-v21/material_button_deep_blue.xml b/app/src/main/res/drawable-v21/material_button_deep_blue.xml new file mode 100644 index 00000000..68efa4b2 --- /dev/null +++ b/app/src/main/res/drawable-v21/material_button_deep_blue.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + diff --git a/app/src/main/res/drawable-v21/material_button_deep_blue_lighter.xml b/app/src/main/res/drawable-v21/material_button_deep_blue_lighter.xml new file mode 100644 index 00000000..e4679c5b --- /dev/null +++ b/app/src/main/res/drawable-v21/material_button_deep_blue_lighter.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + diff --git a/app/src/main/res/drawable/material_button_black.xml b/app/src/main/res/drawable/material_button_black.xml new file mode 100644 index 00000000..5d7dd74b --- /dev/null +++ b/app/src/main/res/drawable/material_button_black.xml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/material_button_black_lighter.xml b/app/src/main/res/drawable/material_button_black_lighter.xml new file mode 100644 index 00000000..610a3db0 --- /dev/null +++ b/app/src/main/res/drawable/material_button_black_lighter.xml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/material_button_deep_blue.xml b/app/src/main/res/drawable/material_button_deep_blue.xml new file mode 100644 index 00000000..5880c0de --- /dev/null +++ b/app/src/main/res/drawable/material_button_deep_blue.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/material_button_deep_blue_lighter.xml b/app/src/main/res/drawable/material_button_deep_blue_lighter.xml new file mode 100644 index 00000000..000bc4a6 --- /dev/null +++ b/app/src/main/res/drawable/material_button_deep_blue_lighter.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/arrays.xml b/app/src/main/res/values/arrays.xml index 225b9e4f..fb4c2f95 100644 --- a/app/src/main/res/values/arrays.xml +++ b/app/src/main/res/values/arrays.xml @@ -21,21 +21,6 @@ --> - - @string/cpp_theme_dark - @string/cpp_theme_light - @string/p_metro_blue_theme - @string/p_metro_green_theme - @string/p_metro_purple_theme - - - material_theme - material_light_theme - metro_blue_theme - metro_green_theme - metro_purple_theme - - @string/p_use_app_theme @string/cpp_theme_dark diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index 14a03463..2efa67f2 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -47,6 +47,11 @@ #313131 #212121 #101010 + #0a0a0a + + #06224d + #082a5e + #0a3980 #0D47A1 #1565C0 diff --git a/app/src/main/res/values/text_non_translatable.xml b/app/src/main/res/values/text_non_translatable.xml index 9e49f240..c7026931 100644 --- a/app/src/main/res/values/text_non_translatable.xml +++ b/app/src/main/res/values/text_non_translatable.xml @@ -16,6 +16,7 @@ f(x, y) αβγ E + %1$s (AMOLED) 0 1 diff --git a/app/src/main/res/values/theme_material_black.xml b/app/src/main/res/values/theme_material_black.xml new file mode 100644 index 00000000..4fcb3932 --- /dev/null +++ b/app/src/main/res/values/theme_material_black.xml @@ -0,0 +1,79 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/xml/preferences_appearance.xml b/app/src/main/res/xml/preferences_appearance.xml index 9c1fa08e..687d40cf 100644 --- a/app/src/main/res/xml/preferences_appearance.xml +++ b/app/src/main/res/xml/preferences_appearance.xml @@ -35,8 +35,6 @@ a:title="@string/cpp_prefs_vibrate_on_keypress" /> From f7e2229687b278905c22ec378a98e05a2ddddf79 Mon Sep 17 00:00:00 2001 From: serso Date: Sat, 16 Apr 2016 21:04:44 +0200 Subject: [PATCH 4/8] bin/hex numbers should always use space as a grouping separator --- jscl/src/main/java/jscl/JsclMathEngine.java | 16 +++++++++++---- .../test/java/jscl/JsclMathEngineTest.java | 20 +++++++++++++++++++ 2 files changed, 32 insertions(+), 4 deletions(-) diff --git a/jscl/src/main/java/jscl/JsclMathEngine.java b/jscl/src/main/java/jscl/JsclMathEngine.java index f58333f0..8a59b2fe 100644 --- a/jscl/src/main/java/jscl/JsclMathEngine.java +++ b/jscl/src/main/java/jscl/JsclMathEngine.java @@ -9,7 +9,6 @@ import jscl.math.operator.Percent; import jscl.math.operator.Rand; import jscl.math.operator.matrix.OperatorsRegistry; import jscl.text.ParseException; -import midpcalc.Real; import org.solovyev.common.NumberFormatter; import org.solovyev.common.math.MathRegistry; import org.solovyev.common.msg.MessageRegistry; @@ -168,10 +167,10 @@ public class JsclMathEngine implements MathEngine { } } final NumberFormatter nf = numberFormatter.get(); - nf.setGroupingSeparator(useGroupingSeparator ? groupingSeparator : NumberFormatter.NO_GROUPING); + nf.setGroupingSeparator(useGroupingSeparator ? getGroupingSeparatorChar(nb) : NumberFormatter.NO_GROUPING); nf.setPrecision(roundResult ? precision : NumberFormatter.NO_ROUNDING); switch (numberFormat) { - case Real.NumberFormat.FSE_ENG: + case FSE_ENG: nf.useEngineeringFormat(NumberFormatter.DEFAULT_MAGNITUDE); break; case FSE_SCI: @@ -251,7 +250,7 @@ public class JsclMathEngine implements MathEngine { @Nonnull public String addGroupingSeparators(@Nonnull NumeralBase nb, @Nonnull String ungroupedDoubleValue) { if (useGroupingSeparator) { - final String groupingSeparator = nb == NumeralBase.dec ? String.valueOf(this.groupingSeparator) : " "; + final String groupingSeparator = getGroupingSeparator(nb); final int dotIndex = ungroupedDoubleValue.indexOf("."); @@ -277,6 +276,15 @@ public class JsclMathEngine implements MathEngine { } } + @Nonnull + private String getGroupingSeparator(@Nonnull NumeralBase nb) { + return nb == NumeralBase.dec ? String.valueOf(groupingSeparator) : " "; + } + + private char getGroupingSeparatorChar(@Nonnull NumeralBase nb) { + return nb == NumeralBase.dec ? groupingSeparator : ' '; + } + @Nonnull private StringBuilder insertSeparators(@Nonnull NumeralBase nb, @Nonnull String groupingSeparator, diff --git a/jscl/src/test/java/jscl/JsclMathEngineTest.java b/jscl/src/test/java/jscl/JsclMathEngineTest.java index 96043c89..92b271fa 100644 --- a/jscl/src/test/java/jscl/JsclMathEngineTest.java +++ b/jscl/src/test/java/jscl/JsclMathEngineTest.java @@ -77,12 +77,31 @@ public class JsclMathEngineTest { assertEquals("13D", me.format(317d, NumeralBase.hex)); } + @Test public void testPiComputation() throws Exception { final JsclMathEngine me = JsclMathEngine.getInstance(); assertEquals("-1+0.0000000000000001*i", me.evaluate("exp(√(-1)*Π)")); } + @Test + public void testBinShouldAlwaysUseSpaceAsGroupingSeparator() throws Exception { + final JsclMathEngine me = new JsclMathEngine(); + me.setGroupingSeparator('\''); + me.setUseGroupingSeparator(true); + + assertEquals("100 0000 0000", me.format(1024d, NumeralBase.bin)); + } + + @Test + public void testHexShouldAlwaysUseSpaceAsGroupingSeparator() throws Exception { + final JsclMathEngine me = new JsclMathEngine(); + me.setGroupingSeparator('\''); + me.setUseGroupingSeparator(true); + + assertEquals("4 00", me.format(1024d, NumeralBase.hex)); + } + @Test public void testEngineeringNotationWithRounding() throws Exception { final JsclMathEngine me = JsclMathEngine.getInstance(); @@ -114,6 +133,7 @@ public class JsclMathEngineTest { assertEquals("-999", me.format(-999d)); assertEquals("-999.99", me.format(-999.99d)); assertEquals("-0.1", me.format(-0.1d)); + assertEquals("-0.12", me.format(-0.12d)); assertEquals("-0.123", me.format(-0.123d)); assertEquals("-0.1234", me.format(-0.1234d)); From 9e9e856c24b5643110bd46bd9969fa95e392362a Mon Sep 17 00:00:00 2001 From: serso Date: Sat, 16 Apr 2016 21:09:47 +0200 Subject: [PATCH 5/8] Use primitive types in number formatting --- jscl/src/main/java/jscl/JsclMathEngine.java | 17 ++++++++++------- jscl/src/main/java/jscl/MathContext.java | 4 ++-- .../main/java/jscl/math/numeric/Numeric.java | 2 +- 3 files changed, 13 insertions(+), 10 deletions(-) diff --git a/jscl/src/main/java/jscl/JsclMathEngine.java b/jscl/src/main/java/jscl/JsclMathEngine.java index 8a59b2fe..5b7ddee5 100644 --- a/jscl/src/main/java/jscl/JsclMathEngine.java +++ b/jscl/src/main/java/jscl/JsclMathEngine.java @@ -143,16 +143,16 @@ public class JsclMathEngine implements MathEngine { } @Nonnull - public String format(@Nonnull Double value) throws NumeralBaseException { + public String format(double value) throws NumeralBaseException { return format(value, numeralBase); } @Nonnull - public String format(@Nonnull Double value, @Nonnull NumeralBase nb) throws NumeralBaseException { - if (value.isInfinite()) { + public String format(double value, @Nonnull NumeralBase nb) throws NumeralBaseException { + if (Double.isInfinite(value)) { return formatInfinity(value); } - if (value.isNaN()) { + if (Double.isNaN(value)) { // return "NaN" return String.valueOf(value); } @@ -184,14 +184,17 @@ public class JsclMathEngine implements MathEngine { } @Nullable - private IConstant findConstant(@Nonnull Double value) { + private IConstant findConstant(double value) { final IConstant constant = findConstant(constantsRegistry.getSystemEntities(), value); if (constant != null) { return constant; } final IConstant piInv = constantsRegistry.get(Constants.PI_INV.getName()); - if (piInv != null && value.equals(piInv.getDoubleValue())) { - return piInv; + if (piInv != null) { + final Double piInvValue = piInv.getDoubleValue(); + if (piInvValue != null && piInvValue == value) { + return piInv; + } } return null; } diff --git a/jscl/src/main/java/jscl/MathContext.java b/jscl/src/main/java/jscl/MathContext.java index 30400a29..45d9e330 100644 --- a/jscl/src/main/java/jscl/MathContext.java +++ b/jscl/src/main/java/jscl/MathContext.java @@ -44,10 +44,10 @@ public interface MathContext { void setGroupingSeparator(char groupingSeparator); @Nonnull - String format(@Nonnull Double value) throws NumeralBaseException; + String format(double value) throws NumeralBaseException; @Nonnull - String format(@Nonnull Double value, @Nonnull NumeralBase nb) throws NumeralBaseException; + String format(double value, @Nonnull NumeralBase nb) throws NumeralBaseException; @Nonnull String addGroupingSeparators(@Nonnull NumeralBase nb, @Nonnull String ungroupedIntValue); diff --git a/jscl/src/main/java/jscl/math/numeric/Numeric.java b/jscl/src/main/java/jscl/math/numeric/Numeric.java index e30d75af..3ef0a3c1 100644 --- a/jscl/src/main/java/jscl/math/numeric/Numeric.java +++ b/jscl/src/main/java/jscl/math/numeric/Numeric.java @@ -324,7 +324,7 @@ public abstract class Numeric implements Arithmetic, INumeric, @Nonnull protected String toString(final double value) { - return JsclMathEngine.getInstance().format(value, JsclMathEngine.getInstance().getNumeralBase()); + return JsclMathEngine.getInstance().format(value); } public BigInteger toBigInteger() { From 85b9cdc56a5d2974a33cf49fa3069b80ca68de57 Mon Sep 17 00:00:00 2001 From: serso Date: Sat, 16 Apr 2016 21:40:49 +0200 Subject: [PATCH 6/8] Format BigInteger directly --- jscl/src/main/java/jscl/JsclMathEngine.java | 22 +++++- jscl/src/main/java/jscl/MathContext.java | 3 + jscl/src/main/java/jscl/math/JsclInteger.java | 3 +- .../org/solovyev/common/NumberFormatter.java | 79 ++++++++++++++++--- .../solovyev/common/NumberFormatterTest.java | 37 +++++++++ 5 files changed, 130 insertions(+), 14 deletions(-) diff --git a/jscl/src/main/java/jscl/JsclMathEngine.java b/jscl/src/main/java/jscl/JsclMathEngine.java index 5b7ddee5..66508e4f 100644 --- a/jscl/src/main/java/jscl/JsclMathEngine.java +++ b/jscl/src/main/java/jscl/JsclMathEngine.java @@ -17,6 +17,7 @@ import org.solovyev.common.msg.Messages; import javax.annotation.Nonnull; import javax.annotation.Nullable; import java.math.BigDecimal; +import java.math.BigInteger; import java.util.List; import static midpcalc.Real.NumberFormat.*; @@ -166,6 +167,10 @@ public class JsclMathEngine implements MathEngine { return constant.getName(); } } + return prepareNumberFormatter(nb).format(value, nb.radix).toString(); + } + + private NumberFormatter prepareNumberFormatter(@Nonnull NumeralBase nb) { final NumberFormatter nf = numberFormatter.get(); nf.setGroupingSeparator(useGroupingSeparator ? getGroupingSeparatorChar(nb) : NumberFormatter.NO_GROUPING); nf.setPrecision(roundResult ? precision : NumberFormatter.NO_ROUNDING); @@ -180,7 +185,22 @@ public class JsclMathEngine implements MathEngine { nf.useSimpleFormat(); break; } - return nf.format(value, nb.radix).toString(); + return nf; + } + + @Override + public String format(@Nonnull BigInteger value) throws NumeralBaseException { + return format(value, numeralBase); + } + + @Nonnull + public String format(@Nonnull BigInteger value, @Nonnull NumeralBase nb) throws NumeralBaseException { + if (nb == NumeralBase.dec) { + if (BigInteger.ZERO.equals(value)) { + return "0"; + } + } + return prepareNumberFormatter(nb).format(value, nb.radix).toString(); } @Nullable diff --git a/jscl/src/main/java/jscl/MathContext.java b/jscl/src/main/java/jscl/MathContext.java index 45d9e330..c4ef9cc7 100644 --- a/jscl/src/main/java/jscl/MathContext.java +++ b/jscl/src/main/java/jscl/MathContext.java @@ -6,6 +6,7 @@ import jscl.math.operator.Operator; import org.solovyev.common.math.MathRegistry; import javax.annotation.Nonnull; +import java.math.BigInteger; public interface MathContext { @@ -46,6 +47,8 @@ public interface MathContext { @Nonnull String format(double value) throws NumeralBaseException; + String format(@Nonnull BigInteger value) throws NumeralBaseException; + @Nonnull String format(double value, @Nonnull NumeralBase nb) throws NumeralBaseException; diff --git a/jscl/src/main/java/jscl/math/JsclInteger.java b/jscl/src/main/java/jscl/math/JsclInteger.java index a8fc3efa..c365d053 100644 --- a/jscl/src/main/java/jscl/math/JsclInteger.java +++ b/jscl/src/main/java/jscl/math/JsclInteger.java @@ -352,8 +352,7 @@ public final class JsclInteger extends Generic { } public String toString() { - // todo serso: actually better way is to provide custom format() method for integers and not to convert integer to double - return JsclMathEngine.getInstance().format(this.content.doubleValue()); + return JsclMathEngine.getInstance().format(content); } public String toJava() { diff --git a/jscl/src/main/java/org/solovyev/common/NumberFormatter.java b/jscl/src/main/java/org/solovyev/common/NumberFormatter.java index 5eb44bfa..b875774d 100644 --- a/jscl/src/main/java/org/solovyev/common/NumberFormatter.java +++ b/jscl/src/main/java/org/solovyev/common/NumberFormatter.java @@ -1,16 +1,13 @@ package org.solovyev.common; -import java.math.BigDecimal; - -import javax.annotation.Nonnull; - import midpcalc.Real; +import javax.annotation.Nonnull; +import java.math.BigDecimal; +import java.math.BigInteger; + import static java.lang.Math.pow; -import static midpcalc.Real.NumberFormat.FSE_ENG; -import static midpcalc.Real.NumberFormat.FSE_FIX; -import static midpcalc.Real.NumberFormat.FSE_NONE; -import static midpcalc.Real.NumberFormat.FSE_SCI; +import static midpcalc.Real.NumberFormat.*; public class NumberFormatter { @@ -54,11 +51,14 @@ public class NumberFormatter { return format(value, 10); } + @Nonnull + public CharSequence format(@Nonnull BigInteger value) { + return format(value, 10); + } + @Nonnull public CharSequence format(double value, int radix) { - if (radix != 2 && radix != 8 && radix != 10 && radix != 16) { - throw new IllegalArgumentException("Unsupported radix: " + radix); - } + checkRadix(radix); double absValue = Math.abs(value); final boolean simpleFormat = useSimpleFormat(radix, absValue); @@ -88,6 +88,39 @@ public class NumberFormatter { return prepare(value); } + @Nonnull + public CharSequence format(@Nonnull BigInteger value, int radix) { + checkRadix(radix); + final BigInteger absValue = value.abs(); + final boolean simpleFormat = useSimpleFormat(radix, absValue); + + final int effectivePrecision = precision == NO_ROUNDING ? MAX_PRECISION : precision; + if (simpleFormat) { + numberFormat.fse = FSE_FIX; + } else if (format == FSE_NONE) { + // originally, a simple format was requested but we have to use something more appropriate, f.e. scientific + // format + numberFormat.fse = FSE_SCI; + } else { + numberFormat.fse = format; + } + numberFormat.thousand = groupingSeparator; + numberFormat.precision = effectivePrecision; + numberFormat.base = radix; + numberFormat.maxwidth = simpleFormat ? 100 : 30; + + if (radix == 2 && value.compareTo(BigInteger.ZERO) < 0) { + return "-" + prepare(absValue); + } + return prepare(value); + } + + private void checkRadix(int radix) { + if (radix != 2 && radix != 8 && radix != 10 && radix != 16) { + throw new IllegalArgumentException("Unsupported radix: " + radix); + } + } + private boolean useSimpleFormat(int radix, double absValue) { if (radix != 10) { return true; @@ -103,17 +136,41 @@ public class NumberFormatter { return false; } + private boolean useSimpleFormat(int radix, @Nonnull BigInteger absValue) { + if (radix != 10) { + return true; + } + if (format == FSE_NONE) { + return true; + } + if (absValue.compareTo(BigInteger.valueOf((long) pow(10, simpleFormatMagnitude))) < 0) { + return true; + } + return false; + } + @Nonnull private CharSequence prepare(double value) { return stripZeros(realFormat(value)).replace('e', 'E'); } + @Nonnull + private CharSequence prepare(@Nonnull BigInteger value) { + return stripZeros(realFormat(value)).replace('e', 'E'); + } + @Nonnull private String realFormat(double value) { real.assign(Double.toString(value)); return real.toString(numberFormat); } + @Nonnull + private String realFormat(@Nonnull BigInteger value) { + real.assign(value.toString()); + return real.toString(numberFormat); + } + @Nonnull private String stripZeros(@Nonnull String s) { int dot = -1; diff --git a/jscl/src/test/java/org/solovyev/common/NumberFormatterTest.java b/jscl/src/test/java/org/solovyev/common/NumberFormatterTest.java index 45ce11f1..e7847319 100644 --- a/jscl/src/test/java/org/solovyev/common/NumberFormatterTest.java +++ b/jscl/src/test/java/org/solovyev/common/NumberFormatterTest.java @@ -3,7 +3,10 @@ package org.solovyev.common; import org.junit.Before; import org.junit.Test; +import java.math.BigInteger; + import static java.lang.Math.pow; +import static java.math.BigInteger.TEN; import static org.junit.Assert.assertEquals; import static org.solovyev.common.NumberFormatter.DEFAULT_MAGNITUDE; import static org.solovyev.common.NumberFormatter.NO_ROUNDING; @@ -99,32 +102,66 @@ public class NumberFormatterTest { // testing simple format with and without rounding private void testSimpleFormat() { assertEquals("0.00001", numberFormatter.format(pow(10, -5))); + assertEquals("100", numberFormatter.format(pow(10, 2))); + assertEquals("1", numberFormatter.format(BigInteger.ONE)); + assertEquals("1000", numberFormatter.format(BigInteger.valueOf(1000))); + assertEquals("1000000000000000000", numberFormatter.format(pow(10, 18))); + assertEquals("1000000000000000000", numberFormatter.format(BigInteger.valueOf(10).pow(18))); + assertEquals("1E19", numberFormatter.format(pow(10, 19))); + assertEquals("1E19", numberFormatter.format(BigInteger.valueOf(10).pow(19))); + assertEquals("1E20", numberFormatter.format(pow(10, 20))); + assertEquals("1E20", numberFormatter.format(BigInteger.valueOf(10).pow(20))); + assertEquals("1E100", numberFormatter.format(pow(10, 100))); + assertEquals("1E100", numberFormatter.format(BigInteger.valueOf(10).pow(100))); assertEquals("0.01", numberFormatter.format(pow(10, -2))); assertEquals("5000000000000000000", numberFormatter.format(5000000000000000000d)); + assertEquals("5000000000000000000", numberFormatter.format(BigInteger.valueOf(5000000000000000000L))); + assertEquals("5000000000000000000", numberFormatter.format(5000000000000000001d)); + assertEquals("5000000000000000001", numberFormatter.format(BigInteger.valueOf(5000000000000000001L))); + assertEquals("5999999999999994900", numberFormatter.format(5999999999999994999d)); + assertEquals("5999999999999994999", numberFormatter.format(BigInteger.valueOf(5999999999999994999L))); + assertEquals("5E19", numberFormatter.format(50000000000000000000d)); + assertEquals("5E19", numberFormatter.format(BigInteger.valueOf(5L).multiply(TEN.pow(19)))); + assertEquals("5E40", numberFormatter.format(50000000000000000000000000000000000000000d)); + assertEquals("5E40", numberFormatter.format(BigInteger.valueOf(5L).multiply(TEN.pow(40)))); } // testing scientific format with and without rounding private void testScientificFormat() { assertEquals("0.00001", numberFormatter.format(pow(10, -5))); assertEquals("1E-6", numberFormatter.format(pow(10, -6))); + assertEquals("100", numberFormatter.format(pow(10, 2))); + assertEquals("100", numberFormatter.format(TEN.pow(2))); + assertEquals("10000", numberFormatter.format(pow(10, 4))); + assertEquals("10000", numberFormatter.format(TEN.pow(4))); + assertEquals("1E5", numberFormatter.format(pow(10, 5))); + assertEquals("1E5", numberFormatter.format(TEN.pow(5))); + assertEquals("1E18", numberFormatter.format(pow(10, 18))); + assertEquals("1E18", numberFormatter.format(TEN.pow(18))); + assertEquals("1E19", numberFormatter.format(pow(10, 19))); + assertEquals("1E19", numberFormatter.format(TEN.pow( 19))); + assertEquals("1E20", numberFormatter.format(pow(10, 20))); + assertEquals("1E20", numberFormatter.format(TEN.pow(20))); + assertEquals("1E100", numberFormatter.format(pow(10, 100))); + assertEquals("1E100", numberFormatter.format(TEN.pow(100))); assertEquals("0.01", numberFormatter.format(pow(10, -2))); assertEquals("1E-17", numberFormatter.format(pow(10, -17))); From a0c45a383c8ac060ad364874d4ab4cacb681272e Mon Sep 17 00:00:00 2001 From: serso Date: Sat, 16 Apr 2016 23:32:05 +0200 Subject: [PATCH 7/8] Copy/paste actions --- app/src/main/assets/fonts/Roboto-Regular.ttf | Bin 126072 -> 115204 bytes .../solovyev/android/calculator/Keyboard.java | 55 +++++++++--------- .../calculator/buttons/CppSpecialButton.java | 2 + .../calculator/keyboard/BaseKeyboardUi.java | 7 ++- .../calculator/keyboard/KeyboardUi.java | 2 +- .../keyboard/PartialKeyboardUi.java | 6 +- .../main/res/layout/cpp_app_button_left.xml | 1 + .../main/res/layout/cpp_app_button_right.xml | 1 + .../main/res/values/text_non_translatable.xml | 2 + 9 files changed, 40 insertions(+), 36 deletions(-) diff --git a/app/src/main/assets/fonts/Roboto-Regular.ttf b/app/src/main/assets/fonts/Roboto-Regular.ttf index 0e58508a64bf413a58c24bf60a0e8a90d663f304..bca461d0ad1191eafa7b69302d6cea8315228a18 100644 GIT binary patch delta 49272 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%0D3fZyC7zNdLAN{QSFqH^F@v zOx`e-ytneyJC08OMuC5^y#J@qzucP$|6+}Aoa*!O-_7@2+2(L)A(B|10~y zcb)-M<@g>q*6wj%P5rw!8{8*`)hc%}JlGwtTQjVd-&1hA4U)gNKXO}tSK;oAePQ)~ zy869{Vg7qRuM_!;tKYOs#W!yEY3!e2k^sccX#TK&M@4YGZNtiyuXN0psA8zPwzEC?t^ zazt=a5miI*){aPx5PT3CBlsdTLGVLps-2Pi5t<=1M`(c%gwO_|EkZB?7?9d&r=|7? zp$K6J;aZv00U-kAcSJdz@a&9d7d*S-8HGG5#3g}i38IqvAmFblNdpidHvF;4Gtwgn zLlK4}j6`@8VVwP{q~iHF!V?G!(5ed&79lJJ;bjQ_LU;+`WrTkttkKG3XKje=hTyB6 zk^QyPGAg6WEs+p_mp}l6@C>zImBF#x11;Z6J0kZ6ye~q3gaN<>V=~%NjzvfTUMg_1 z0GojKJUkymn2O&skoE-9X5#rIo=*e*48mN5g$RofmT2l38Ml?mMF=Yp)&RH;8D2$( z^?1I9XEC0yf_ z@&U<9`xe%G0M>i})_g!(rJa>l1GZNCR<4QQjnEjOhju_7jz5=!zaApDki7-UO9 zwiIMbLADfROF_06q)I{R97vUdR4GW6f>bF;6@yeMZWU^Yu|<3Bywm`}1EC>;C&D;{ zB?yHGDsJYHiV#*HtkllSp$HccE+KOd_&!%`P`bjYl)c@9_YGz64Q22PW$+1Q@Cjw`31uviTv-x=6(I#7RV7#0cNy%v4E9|H z`!0ihm%%p6BoKvZmcca3V47tx%`%u~8BDVbrdbBlEQ4v5!8FTYnq@G}GMH8wET#`dURsO~*}mJ5U|ge3_{AOWGQS{_uzt!fnzK@dc(C|cB3MCoE% zX)PiXL_U@0Qmsp>&n2R-0?M}h78QyEb?soxbJ(;O{!4a zPd_i`^P4#{cb4=2pZ|JhGKZU@B}PM%Xh;%mNTLl%?cB}ccVI232JeFR!3Q7)J_a?Q z7I>fqw1NOip(>F6o;^ZECvu?>gE2nR6&%6)wj`SJdXJg+7 z`(^iw4>(frQc-EqrvtATxTSbWap;t&k`qdo4c<0*Z&^qA&&nSwe`m-H<^i38be;#8 z;0K2LFx-dXJ`DF^xDUg981BPxABOud+=t;l4EJHU55s*J?!#Lj-um#?hqpew_2I1# zZ+&>{E5?6)nCrt_ALjZn*N3@2%=KZe4|9E(>%&|h=K3(#hq*q?^xuKYjS=!%rW6`tZ|-pS}Y_eHiM)P+tthTlqK`AN%fO-+k=6 zkA3&C@3a*x2P-%lTCkWtmeI$W`B*X^yWwLueC&pg-SDv+K6b;$Zur;=pRY{xN#B9b zyaU&C2d?Q3T$6JLzKNYEi;uGSD2tD>_$Z5yviK;AkFxkEi?5ylRe p({SR!aQ}r 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 z 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 diff --git a/app/src/main/java/org/solovyev/android/calculator/Keyboard.java b/app/src/main/java/org/solovyev/android/calculator/Keyboard.java index 7d610c20..ab314896 100644 --- a/app/src/main/java/org/solovyev/android/calculator/Keyboard.java +++ b/app/src/main/java/org/solovyev/android/calculator/Keyboard.java @@ -43,6 +43,10 @@ public class Keyboard implements SharedPreferences.OnSharedPreferenceChangeListe @Nonnull private final MathType.Result mathType = new MathType.Result(); + @Nonnull + private static final String GLYPH_PASTE = "\uE000"; + @Nonnull + private static final String GLYPH_COPY = "\uE001"; @Inject Editor editor; @@ -74,10 +78,17 @@ public class Keyboard implements SharedPreferences.OnSharedPreferenceChangeListe return vibrateOnKeypress; } - public boolean buttonPressed(@Nullable final String text) { + public boolean buttonPressed(@Nullable String text) { if (TextUtils.isEmpty(text)) { return false; } + + if (text.equals(GLYPH_COPY)) { + text = CppSpecialButton.copy.action; + } else if (text.equals(GLYPH_PASTE)) { + text = CppSpecialButton.paste.action; + } + ga.onButtonPressed(text); if (!processSpecialAction(text)) { processText(prepareText(text)); @@ -137,10 +148,16 @@ public class Keyboard implements SharedPreferences.OnSharedPreferenceChangeListe launcher.showHistory(); break; case cursor_right: - moveCursorRight(); + editor.moveCursorRight(); + break; + case cursor_to_end: + editor.setCursorOnEnd(); break; case cursor_left: - moveCursorLeft(); + editor.moveCursorLeft(); + break; + case cursor_to_start: + editor.setCursorOnStart(); break; case settings: launcher.showSettings(); @@ -158,16 +175,19 @@ public class Keyboard implements SharedPreferences.OnSharedPreferenceChangeListe editor.erase(); break; case paste: - pasteButtonPressed(); + final String text = clipboard.get().getText(); + if (!TextUtils.isEmpty(text)) { + editor.insert(text); + } break; case copy: - copyButtonPressed(); + bus.get().post(new Display.CopyOperation()); break; case equals: equalsButtonPressed(); break; case clear: - clearButtonPressed(); + editor.clear(); break; case functions: launcher.showFunctions(); @@ -209,29 +229,6 @@ public class Keyboard implements SharedPreferences.OnSharedPreferenceChangeListe editor.setText("(" + oldText.subSequence(0, cursorPosition) + ")" + oldText.subSequence(cursorPosition, oldText.length()), cursorPosition + 2); } - public void pasteButtonPressed() { - final String text = clipboard.get().getText(); - if (!TextUtils.isEmpty(text)) { - editor.insert(text); - } - } - - public void clearButtonPressed() { - editor.clear(); - } - - public void copyButtonPressed() { - bus.get().post(new Display.CopyOperation()); - } - - public void moveCursorLeft() { - editor.moveCursorLeft(); - } - - public void moveCursorRight() { - editor.moveCursorRight(); - } - @Override public void onSharedPreferenceChanged(SharedPreferences preferences, String key) { if (Preferences.Gui.vibrateOnKeypress.isSameKey(key)) { diff --git a/app/src/main/java/org/solovyev/android/calculator/buttons/CppSpecialButton.java b/app/src/main/java/org/solovyev/android/calculator/buttons/CppSpecialButton.java index b999dc15..875431dc 100644 --- a/app/src/main/java/org/solovyev/android/calculator/buttons/CppSpecialButton.java +++ b/app/src/main/java/org/solovyev/android/calculator/buttons/CppSpecialButton.java @@ -33,7 +33,9 @@ public enum CppSpecialButton { history("history"), cursor_right("▷"), + cursor_to_end(">>"), cursor_left("◁"), + cursor_to_start("<<"), settings("settings"), settings_widget("settings_widget"), like("like"), diff --git a/app/src/main/java/org/solovyev/android/calculator/keyboard/BaseKeyboardUi.java b/app/src/main/java/org/solovyev/android/calculator/keyboard/BaseKeyboardUi.java index c9adcd43..c8098c7c 100644 --- a/app/src/main/java/org/solovyev/android/calculator/keyboard/BaseKeyboardUi.java +++ b/app/src/main/java/org/solovyev/android/calculator/keyboard/BaseKeyboardUi.java @@ -65,7 +65,10 @@ public abstract class BaseKeyboardUi implements SharedPreferences.OnSharedPrefer listener = new DirectionDragListener(application) { @Override protected boolean onDrag(@NonNull View view, @NonNull DragEvent event, @NonNull DragDirection direction) { - return Drag.hasDirectionText(view, direction) && BaseKeyboardUi.this.onDrag(view, direction); + if (!Drag.hasDirectionText(view, direction)) { + return false; + } + return BaseKeyboardUi.this.onDrag(view, direction, ((DirectionDragView) view).getText(direction).getValue()); } }; textScale = getTextScale(application); @@ -82,7 +85,7 @@ public abstract class BaseKeyboardUi implements SharedPreferences.OnSharedPrefer } } - protected abstract boolean onDrag(@NonNull View view, @NonNull DragDirection direction); + protected abstract boolean onDrag(@NonNull View view, @NonNull DragDirection direction, @Nonnull String value); public void onCreateView(@Nonnull Activity activity, @Nonnull View view) { cast(activity.getApplication()).getComponent().inject(this); diff --git a/app/src/main/java/org/solovyev/android/calculator/keyboard/KeyboardUi.java b/app/src/main/java/org/solovyev/android/calculator/keyboard/KeyboardUi.java index 735a8cba..5990bf86 100644 --- a/app/src/main/java/org/solovyev/android/calculator/keyboard/KeyboardUi.java +++ b/app/src/main/java/org/solovyev/android/calculator/keyboard/KeyboardUi.java @@ -246,7 +246,7 @@ public class KeyboardUi extends BaseKeyboardUi { } @Override - protected boolean onDrag(@NonNull View view, @NonNull DragDirection direction) { + protected boolean onDrag(@NonNull View view, @NonNull DragDirection direction, @Nonnull String value) { switch (view.getId()) { case R.id.cpp_button_functions: if (direction == up) { diff --git a/app/src/main/java/org/solovyev/android/calculator/keyboard/PartialKeyboardUi.java b/app/src/main/java/org/solovyev/android/calculator/keyboard/PartialKeyboardUi.java index e9889b76..dd503ded 100644 --- a/app/src/main/java/org/solovyev/android/calculator/keyboard/PartialKeyboardUi.java +++ b/app/src/main/java/org/solovyev/android/calculator/keyboard/PartialKeyboardUi.java @@ -75,13 +75,11 @@ public class PartialKeyboardUi extends BaseKeyboardUi { } @Override - protected boolean onDrag(@NonNull View view, @NonNull DragDirection direction) { + protected boolean onDrag(@NonNull View view, @NonNull DragDirection direction, @Nonnull String value) { switch (view.getId()) { case R.id.cpp_button_right: - editor.setCursorOnEnd(); - return true; case R.id.cpp_button_left: - editor.setCursorOnStart(); + keyboard.buttonPressed(value); return true; case R.id.cpp_button_clear: if(direction == up) { diff --git a/app/src/main/res/layout/cpp_app_button_left.xml b/app/src/main/res/layout/cpp_app_button_left.xml index f616ebbd..845176fa 100644 --- a/app/src/main/res/layout/cpp_app_button_left.xml +++ b/app/src/main/res/layout/cpp_app_button_left.xml @@ -29,4 +29,5 @@ xmlns:tools="http://schemas.android.com/tools" a:src="@drawable/ic_keyboard_arrow_left_white_48dp" c:directionTextUp="<<" + c:directionTextDown="@string/cpp_glyph_copy" tools:ignore="HardcodedText" /> \ No newline at end of file diff --git a/app/src/main/res/layout/cpp_app_button_right.xml b/app/src/main/res/layout/cpp_app_button_right.xml index 6983f4ce..f5d9d2c3 100644 --- a/app/src/main/res/layout/cpp_app_button_right.xml +++ b/app/src/main/res/layout/cpp_app_button_right.xml @@ -29,4 +29,5 @@ xmlns:tools="http://schemas.android.com/tools" a:src="@drawable/ic_keyboard_arrow_right_white_48dp" c:directionTextUp=">>" + c:directionTextDown="@string/cpp_glyph_paste" tools:ignore="HardcodedText" /> \ No newline at end of file diff --git a/app/src/main/res/values/text_non_translatable.xml b/app/src/main/res/values/text_non_translatable.xml index c7026931..6144e7a8 100644 --- a/app/src/main/res/values/text_non_translatable.xml +++ b/app/src/main/res/values/text_non_translatable.xml @@ -17,6 +17,8 @@ αβγ E %1$s (AMOLED) + "\ue000" + "\ue001" 0 1 From f7015ce3fd7d8965c49f875081cf071fd277be10 Mon Sep 17 00:00:00 2001 From: serso Date: Mon, 18 Apr 2016 00:23:21 +0200 Subject: [PATCH 8/8] Buttons changes Special glyphs in the built-in font were introduced to be used instead of images --- app/build.gradle | 5 - app/src/main/assets/fonts/Roboto-Regular.ttf | Bin 115204 -> 134436 bytes .../solovyev/android/calculator/Keyboard.java | 91 +++++++++-- .../android/calculator/buttons/CppButton.java | 3 +- .../calculator/buttons/CppSpecialButton.java | 65 +++++++- .../calculator/keyboard/BaseKeyboardUi.java | 28 +++- .../calculator/keyboard/KeyboardUi.java | 150 +----------------- .../keyboard/PartialKeyboardUi.java | 51 ------ .../calculator/view/AngleUnitsButton.java | 68 -------- .../calculator/view/NumeralBasesButton.java | 68 -------- .../android/views/dragbutton/DragView.java | 1 + .../ic_keyboard_arrow_left_white_48dp.png | Bin 226 -> 0 bytes .../ic_keyboard_arrow_right_white_48dp.png | Bin 227 -> 0 bytes .../ic_keyboard_arrow_left_white_48dp.png | Bin 164 -> 0 bytes .../ic_keyboard_arrow_right_white_48dp.png | Bin 163 -> 0 bytes .../ic_keyboard_arrow_left_white_48dp.png | Bin 203 -> 0 bytes .../ic_keyboard_arrow_right_white_48dp.png | Bin 233 -> 0 bytes .../ic_keyboard_arrow_left_white_48dp.png | Bin 285 -> 0 bytes .../ic_keyboard_arrow_right_white_48dp.png | Bin 286 -> 0 bytes .../ic_keyboard_arrow_left_white_48dp.png | Bin 349 -> 0 bytes .../ic_keyboard_arrow_right_white_48dp.png | Bin 354 -> 0 bytes ...4dp.xml => ic_chevron_left_white_24dp.xml} | 10 +- .../ic_favorite_border_white_48dp.xml | 9 ++ .../ic_keyboard_arrow_left_white_48dp.xml | 9 ++ .../ic_keyboard_arrow_right_white_48dp.xml | 9 ++ .../main/res/layout-land/cpp_app_keyboard.xml | 2 +- .../main/res/layout/cpp_app_button_copy.xml | 2 +- .../layout/cpp_app_button_equals_no_bg.xml | 2 +- .../res/layout/cpp_app_button_history.xml | 4 +- .../main/res/layout/cpp_app_button_like.xml | 29 ++++ .../main/res/layout/cpp_app_button_paste.xml | 2 +- app/src/main/res/menu/main.xml | 6 +- app/src/main/res/values/text_glyphs.xml | 13 ++ .../main/res/values/text_non_translatable.xml | 2 - .../buttons/CppSpecialButtonTest.java | 20 +++ .../calculator/view/AngleUnitsButtonTest.java | 61 ------- .../view/NumeralBasesButtonTest.java | 61 ------- 37 files changed, 266 insertions(+), 505 deletions(-) delete mode 100644 app/src/main/java/org/solovyev/android/calculator/view/AngleUnitsButton.java delete mode 100644 app/src/main/java/org/solovyev/android/calculator/view/NumeralBasesButton.java delete mode 100644 app/src/main/res/drawable-hdpi/ic_keyboard_arrow_left_white_48dp.png delete mode 100644 app/src/main/res/drawable-hdpi/ic_keyboard_arrow_right_white_48dp.png delete mode 100644 app/src/main/res/drawable-mdpi/ic_keyboard_arrow_left_white_48dp.png delete mode 100644 app/src/main/res/drawable-mdpi/ic_keyboard_arrow_right_white_48dp.png delete mode 100644 app/src/main/res/drawable-xhdpi/ic_keyboard_arrow_left_white_48dp.png delete mode 100644 app/src/main/res/drawable-xhdpi/ic_keyboard_arrow_right_white_48dp.png delete mode 100644 app/src/main/res/drawable-xxhdpi/ic_keyboard_arrow_left_white_48dp.png delete mode 100644 app/src/main/res/drawable-xxhdpi/ic_keyboard_arrow_right_white_48dp.png delete mode 100644 app/src/main/res/drawable-xxxhdpi/ic_keyboard_arrow_left_white_48dp.png delete mode 100644 app/src/main/res/drawable-xxxhdpi/ic_keyboard_arrow_right_white_48dp.png rename app/src/main/res/drawable/{ic_chevron_left_24dp.xml => ic_chevron_left_white_24dp.xml} (55%) create mode 100644 app/src/main/res/drawable/ic_favorite_border_white_48dp.xml create mode 100644 app/src/main/res/drawable/ic_keyboard_arrow_left_white_48dp.xml create mode 100644 app/src/main/res/drawable/ic_keyboard_arrow_right_white_48dp.xml create mode 100644 app/src/main/res/layout/cpp_app_button_like.xml create mode 100644 app/src/main/res/values/text_glyphs.xml create mode 100644 app/src/test/java/org/solovyev/android/calculator/buttons/CppSpecialButtonTest.java delete mode 100644 app/src/test/java/org/solovyev/android/calculator/view/AngleUnitsButtonTest.java delete mode 100644 app/src/test/java/org/solovyev/android/calculator/view/NumeralBasesButtonTest.java diff --git a/app/build.gradle b/app/build.gradle index ac367c1b..87dd0323 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -34,7 +34,6 @@ android { versionCode 148 versionName '2.2.1' testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" - generatedDensities = [] } buildTypes { release { @@ -58,10 +57,6 @@ android { sourceCompatibility JavaVersion.VERSION_1_7 targetCompatibility JavaVersion.VERSION_1_7 } - // This is handled for you by the 2.0+ Gradle Plugin - aaptOptions { - additionalParameters "--no-version-vectors" - } } dependencies { diff --git a/app/src/main/assets/fonts/Roboto-Regular.ttf b/app/src/main/assets/fonts/Roboto-Regular.ttf index bca461d0ad1191eafa7b69302d6cea8315228a18..cae96938114d963e7e3b42fb0a8d86660affb431 100644 GIT binary patch delta 36497 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 diff --git a/app/src/main/java/org/solovyev/android/calculator/Keyboard.java b/app/src/main/java/org/solovyev/android/calculator/Keyboard.java index ab314896..4b37ea06 100644 --- a/app/src/main/java/org/solovyev/android/calculator/Keyboard.java +++ b/app/src/main/java/org/solovyev/android/calculator/Keyboard.java @@ -25,11 +25,15 @@ package org.solovyev.android.calculator; import android.content.SharedPreferences; import android.support.annotation.NonNull; import android.text.TextUtils; +import android.util.Log; import com.squareup.otto.Bus; import dagger.Lazy; +import jscl.math.Expression; +import jscl.math.Generic; import org.solovyev.android.Check; import org.solovyev.android.calculator.buttons.CppSpecialButton; import org.solovyev.android.calculator.ga.Ga; +import org.solovyev.android.calculator.history.History; import org.solovyev.android.calculator.math.MathType; import org.solovyev.android.calculator.memory.Memory; @@ -43,16 +47,14 @@ public class Keyboard implements SharedPreferences.OnSharedPreferenceChangeListe @Nonnull private final MathType.Result mathType = new MathType.Result(); - @Nonnull - private static final String GLYPH_PASTE = "\uE000"; - @Nonnull - private static final String GLYPH_COPY = "\uE001"; @Inject Editor editor; @Inject Display display; @Inject + History history; + @Inject Lazy memory; @Inject Calculator calculator; @@ -83,10 +85,14 @@ public class Keyboard implements SharedPreferences.OnSharedPreferenceChangeListe return false; } - if (text.equals(GLYPH_COPY)) { - text = CppSpecialButton.copy.action; - } else if (text.equals(GLYPH_PASTE)) { - text = CppSpecialButton.paste.action; + if (text.length() == 1) { + final char glyph = text.charAt(0); + final CppSpecialButton button = CppSpecialButton.getByGlyph(glyph); + if (button != null) { + ga.onButtonPressed(button.action); + handleSpecialAction(button); + return true; + } } ga.onButtonPressed(text); @@ -138,15 +144,21 @@ public class Keyboard implements SharedPreferences.OnSharedPreferenceChangeListe if (button == null) { return false; } - onSpecialButtonPressed(button); + handleSpecialAction(button); return true; } - private void onSpecialButtonPressed(@NonNull CppSpecialButton button) { + private void handleSpecialAction(@NonNull CppSpecialButton button) { switch (button) { case history: launcher.showHistory(); break; + case history_undo: + history.undo(); + break; + case history_redo: + history.redo(); + break; case cursor_right: editor.moveCursorRight(); break; @@ -171,6 +183,15 @@ public class Keyboard implements SharedPreferences.OnSharedPreferenceChangeListe case memory: memory.get().requestValue(); break; + case memory_plus: + handleMemoryButton(true); + break; + case memory_minus: + handleMemoryButton(false); + break; + case memory_clear: + memory.get().clear(); + break; case erase: editor.erase(); break; @@ -183,6 +204,9 @@ public class Keyboard implements SharedPreferences.OnSharedPreferenceChangeListe case copy: bus.get().post(new Display.CopyOperation()); break; + case brackets_wrap: + handleBracketsWrap(); + break; case equals: equalsButtonPressed(); break; @@ -192,6 +216,15 @@ public class Keyboard implements SharedPreferences.OnSharedPreferenceChangeListe case functions: launcher.showFunctions(); break; + case function_add: + launcher.showFunctionEditor(); + break; + case var_add: + launcher.showConstantEditor(); + break; + case plot_add: + launcher.plotDisplayedExpression(); + break; case open_app: launcher.openApp(); break; @@ -201,6 +234,9 @@ public class Keyboard implements SharedPreferences.OnSharedPreferenceChangeListe case operators: launcher.showOperators(); break; + case simplify: + calculator.simplify(); + break; default: Check.shouldNotHappen(); } @@ -220,15 +256,38 @@ public class Keyboard implements SharedPreferences.OnSharedPreferenceChangeListe editor.setText(state.text); } - public void roundBracketsButtonPressed() { - EditorState viewState = editor.getState(); - - final int cursorPosition = viewState.selection; - final CharSequence oldText = viewState.text; - + public void handleBracketsWrap() { + final EditorState state = editor.getState(); + final int cursorPosition = state.selection; + final CharSequence oldText = state.text; editor.setText("(" + oldText.subSequence(0, cursorPosition) + ")" + oldText.subSequence(cursorPosition, oldText.length()), cursorPosition + 2); } + private boolean handleMemoryButton(boolean plus) { + final DisplayState state = display.getState(); + if (!state.valid) { + return false; + } + Generic value = state.getResult(); + if (value == null) { + try { + value = Expression.valueOf(state.text); + } catch (jscl.text.ParseException e) { + Log.w(App.TAG, e.getMessage(), e); + } + } + if (value == null) { + memory.get().requestShow(); + return false; + } + if (plus) { + memory.get().add(value); + } else { + memory.get().subtract(value); + } + return true; + } + @Override public void onSharedPreferenceChanged(SharedPreferences preferences, String key) { if (Preferences.Gui.vibrateOnKeypress.isSameKey(key)) { diff --git a/app/src/main/java/org/solovyev/android/calculator/buttons/CppButton.java b/app/src/main/java/org/solovyev/android/calculator/buttons/CppButton.java index 2a5c9cef..a8e57189 100644 --- a/app/src/main/java/org/solovyev/android/calculator/buttons/CppButton.java +++ b/app/src/main/java/org/solovyev/android/calculator/buttons/CppButton.java @@ -51,6 +51,7 @@ public enum CppButton { period(R.id.cpp_button_period, "."), brackets(R.id.cpp_button_round_brackets, "()"), + memory(R.id.cpp_button_memory, CppSpecialButton.memory), settings(R.id.cpp_button_settings, CppSpecialButton.settings), settings_widget(R.id.cpp_button_settings_widget, CppSpecialButton.settings_widget), like(R.id.cpp_button_like, CppSpecialButton.like), @@ -65,7 +66,7 @@ public enum CppButton { history(R.id.cpp_button_history, CppSpecialButton.history), /*operations*/ - multiplication(R.id.cpp_button_multiplication, "*"), + multiplication(R.id.cpp_button_multiplication, "×"), division(R.id.cpp_button_division, "/"), plus(R.id.cpp_button_plus, "+"), subtraction(R.id.cpp_button_subtraction, "−"), diff --git a/app/src/main/java/org/solovyev/android/calculator/buttons/CppSpecialButton.java b/app/src/main/java/org/solovyev/android/calculator/buttons/CppSpecialButton.java index 875431dc..ae1c4324 100644 --- a/app/src/main/java/org/solovyev/android/calculator/buttons/CppSpecialButton.java +++ b/app/src/main/java/org/solovyev/android/calculator/buttons/CppSpecialButton.java @@ -29,9 +29,12 @@ import javax.annotation.Nullable; import java.util.HashMap; import java.util.Map; +// see values/text_glyphs.xml for glyph constants public enum CppSpecialButton { history("history"), + history_undo("↶", '\ue007'), + history_redo("↷", '\ue008'), cursor_right("▷"), cursor_to_end(">>"), cursor_left("◁"), @@ -40,39 +43,85 @@ public enum CppSpecialButton { settings_widget("settings_widget"), like("like"), memory("memory"), + memory_plus("M+"), + memory_minus("M-"), + memory_clear("MC"), erase("erase"), - paste("paste"), - copy("copy"), + paste("paste", '\uE000'), + copy("copy", '\uE001'), + brackets_wrap("(…)"), equals("="), clear("clear"), functions("functions"), + function_add("+ƒ"), + var_add("+π"), + plot_add("+plot", '\uE009'), open_app("open_app"), vars("vars"), - operators("operators"); + operators("operators"), + simplify("≡"); @Nonnull - private static Map buttonsByActions = new HashMap<>(); + private static final Map buttonsByActions = new HashMap<>(); + @Nonnull + private static final CppSpecialButton[] buttonsByGlyphs = new CppSpecialButton[values().length]; + private static final char FIRST_GLYPH = '\uE000'; @Nonnull public final String action; + public final char glyph; CppSpecialButton(@Nonnull String action) { + this(action, (char) 0); + } + + CppSpecialButton(@Nonnull String action, char glyph) { this.action = action; + this.glyph = glyph; } @Nullable public static CppSpecialButton getByAction(@Nonnull String action) { - initButtonsByActionsMap(); + initButtonsByActions(); return buttonsByActions.get(action); } - private static void initButtonsByActionsMap() { + private static void initButtonsByActions() { Check.isMainThread(); if (!buttonsByActions.isEmpty()) { return; } - for (CppSpecialButton specialButton : values()) { - buttonsByActions.put(specialButton.action, specialButton); + for (CppSpecialButton button : values()) { + buttonsByActions.put(button.action, button); + } + } + + @Nullable + public static CppSpecialButton getByGlyph(char glyph) { + initButtonsByGlyphs(); + final int position = glyphToPosition(glyph); + if (position < 0 || position >= buttonsByGlyphs.length) { + return null; + } + return buttonsByGlyphs[position]; + } + + private static int glyphToPosition(char glyph) { + return glyph - FIRST_GLYPH; + } + + private static void initButtonsByGlyphs() { + Check.isMainThread(); + if (buttonsByGlyphs[0] != null) { + return; + } + for (CppSpecialButton button : values()) { + if(button.glyph == 0) { + continue; + } + final int position = glyphToPosition(button.glyph); + Check.isNull(buttonsByGlyphs[position], "Glyph is already taken, glyph=" + button.glyph); + buttonsByGlyphs[position] = button; } } diff --git a/app/src/main/java/org/solovyev/android/calculator/keyboard/BaseKeyboardUi.java b/app/src/main/java/org/solovyev/android/calculator/keyboard/BaseKeyboardUi.java index c8098c7c..9e71605d 100644 --- a/app/src/main/java/org/solovyev/android/calculator/keyboard/BaseKeyboardUi.java +++ b/app/src/main/java/org/solovyev/android/calculator/keyboard/BaseKeyboardUi.java @@ -7,14 +7,16 @@ import android.content.SharedPreferences; import android.graphics.Typeface; import android.support.annotation.NonNull; import android.support.annotation.Nullable; +import android.text.TextUtils; import android.util.DisplayMetrics; import android.util.TypedValue; import android.view.View; import android.widget.ImageView; import android.widget.TextView; import dagger.Lazy; +import org.solovyev.android.Check; import org.solovyev.android.calculator.*; -import org.solovyev.android.calculator.buttons.CppSpecialButton; +import org.solovyev.android.calculator.buttons.CppButton; import org.solovyev.android.calculator.memory.Memory; import org.solovyev.android.views.Adjuster; import org.solovyev.android.views.dragbutton.*; @@ -68,7 +70,15 @@ public abstract class BaseKeyboardUi implements SharedPreferences.OnSharedPrefer if (!Drag.hasDirectionText(view, direction)) { return false; } - return BaseKeyboardUi.this.onDrag(view, direction, ((DirectionDragView) view).getText(direction).getValue()); + final DirectionDragView dragView = (DirectionDragView) view; + final String text = dragView.getText(direction).getValue(); + if (TextUtils.isEmpty(text)) { + // hasDirectionText should return false for empty text + Check.shouldNotHappen(); + return false; + } + keyboard.buttonPressed(text); + return true; } }; textScale = getTextScale(application); @@ -85,7 +95,15 @@ public abstract class BaseKeyboardUi implements SharedPreferences.OnSharedPrefer } } - protected abstract boolean onDrag(@NonNull View view, @NonNull DragDirection direction, @Nonnull String value); + @Override + public void onClick(View v) { + final CppButton button = CppButton.getById(v.getId()); + if (button == null) { + Check.shouldNotHappen(); + return; + } + onClick(v, button.action); + } public void onCreateView(@Nonnull Activity activity, @Nonnull View view) { cast(activity.getApplication()).getComponent().inject(this); @@ -197,10 +215,6 @@ public abstract class BaseKeyboardUi implements SharedPreferences.OnSharedPrefer v.performHapticFeedback(KEYBOARD_TAP, FLAG_IGNORE_GLOBAL_SETTING | FLAG_IGNORE_VIEW_SETTING); } - protected final void onClick(@Nonnull View v, @Nonnull CppSpecialButton b) { - onClick(v, b.action); - } - private static class AdjusterHelper implements Adjuster.Helper { public static AdjusterHelper instance = new AdjusterHelper(); diff --git a/app/src/main/java/org/solovyev/android/calculator/keyboard/KeyboardUi.java b/app/src/main/java/org/solovyev/android/calculator/keyboard/KeyboardUi.java index 5990bf86..7c45b836 100644 --- a/app/src/main/java/org/solovyev/android/calculator/keyboard/KeyboardUi.java +++ b/app/src/main/java/org/solovyev/android/calculator/keyboard/KeyboardUi.java @@ -3,31 +3,23 @@ package org.solovyev.android.calculator.keyboard; import android.app.Activity; import android.app.Application; import android.content.SharedPreferences; -import android.support.annotation.NonNull; import android.support.annotation.Nullable; -import android.util.Log; import android.view.View; -import android.widget.Button; import android.widget.ImageButton; import butterknife.Bind; import butterknife.ButterKnife; import jscl.NumeralBase; -import jscl.math.Expression; -import jscl.math.Generic; -import org.solovyev.android.calculator.*; -import org.solovyev.android.calculator.buttons.CppSpecialButton; -import org.solovyev.android.calculator.history.History; -import org.solovyev.android.calculator.view.AngleUnitsButton; -import org.solovyev.android.calculator.view.NumeralBasesButton; +import org.solovyev.android.calculator.Display; +import org.solovyev.android.calculator.Engine; +import org.solovyev.android.calculator.R; import org.solovyev.android.views.dragbutton.DirectionDragButton; import org.solovyev.android.views.dragbutton.DirectionDragImageButton; -import org.solovyev.android.views.dragbutton.DirectionDragView; -import org.solovyev.android.views.dragbutton.DragDirection; import javax.annotation.Nonnull; import javax.inject.Inject; -import static org.solovyev.android.calculator.Engine.Preferences.*; +import static org.solovyev.android.calculator.Engine.Preferences.multiplicationSign; +import static org.solovyev.android.calculator.Engine.Preferences.numeralBase; import static org.solovyev.android.views.dragbutton.DragDirection.*; public class KeyboardUi extends BaseKeyboardUi { @@ -53,10 +45,6 @@ public class KeyboardUi extends BaseKeyboardUi { @Bind(R.id.cpp_button_9) public DirectionDragButton button9; @Inject - History history; - @Inject - ActivityLauncher launcher; - @Inject Engine engine; @Inject Display display; @@ -85,10 +73,10 @@ public class KeyboardUi extends BaseKeyboardUi { DirectionDragButton bracketsButton; @Nullable @Bind(R.id.cpp_button_copy) - NumeralBasesButton copyButton; + DirectionDragImageButton copyButton; @Nullable @Bind(R.id.cpp_button_paste) - AngleUnitsButton pasteButton; + DirectionDragImageButton pasteButton; @Nullable @Bind(R.id.cpp_button_like) ImageButton likeButton; @@ -146,11 +134,9 @@ public class KeyboardUi extends BaseKeyboardUi { if (copyButton != null) { prepareButton(copyButton); - copyButton.setNumeralBase(numeralBase.getPreference(preferences)); } if (pasteButton != null) { prepareButton(pasteButton); - pasteButton.setAngleUnit(angleUnit.getPreference(preferences)); } prepareButton(likeButton); prepareButton(memoryButton); @@ -182,133 +168,11 @@ public class KeyboardUi extends BaseKeyboardUi { @Override public void onSharedPreferenceChanged(SharedPreferences preferences, String key) { super.onSharedPreferenceChanged(preferences, key); - if (angleUnit.isSameKey(key) && pasteButton != null) { - pasteButton.setAngleUnit(angleUnit.getPreference(preferences)); - } if (numeralBase.isSameKey(key)) { toggleNumericDigits(); - if (copyButton != null) { - copyButton.setNumeralBase(numeralBase.getPreference(preferences)); - } } if (multiplicationSign.isSameKey(key)) { multiplicationButton.setText(multiplicationSign.getPreference(preferences)); } } - - @Override - public void onClick(View v) { - switch (v.getId()) { - case R.id.cpp_button_0: - case R.id.cpp_button_1: - case R.id.cpp_button_2: - case R.id.cpp_button_3: - case R.id.cpp_button_4: - case R.id.cpp_button_5: - case R.id.cpp_button_6: - case R.id.cpp_button_7: - case R.id.cpp_button_8: - case R.id.cpp_button_9: - case R.id.cpp_button_division: - case R.id.cpp_button_period: - case R.id.cpp_button_percent: - case R.id.cpp_button_subtraction: - case R.id.cpp_button_multiplication: - case R.id.cpp_button_plus: - case R.id.cpp_button_round_brackets: - onClick(v, ((Button) v).getText().toString()); - break; - case R.id.cpp_button_functions: - onClick(v, CppSpecialButton.functions); - break; - case R.id.cpp_button_history: - onClick(v, CppSpecialButton.history); - break; - case R.id.cpp_button_paste: - onClick(v, CppSpecialButton.paste); - break; - case R.id.cpp_button_copy: - onClick(v, CppSpecialButton.copy); - break; - case R.id.cpp_button_like: - onClick(v, CppSpecialButton.like); - break; - case R.id.cpp_button_memory: - onClick(v, CppSpecialButton.memory); - break; - case R.id.cpp_button_operators: - onClick(v, CppSpecialButton.operators); - break; - case R.id.cpp_button_vars: - onClick(v, CppSpecialButton.vars); - break; - } - } - - @Override - protected boolean onDrag(@NonNull View view, @NonNull DragDirection direction, @Nonnull String value) { - switch (view.getId()) { - case R.id.cpp_button_functions: - if (direction == up) { - launcher.showFunctionEditor(); - return true; - } else if (direction == down) { - launcher.showConstantEditor(); - return true; - } - return false; - case R.id.cpp_button_history: - if (direction == up) { - history.undo(); - return true; - } else if (direction == down) { - history.redo(); - return true; - } - return false; - case R.id.cpp_button_memory: - return processMemoryButton(direction); - case R.id.cpp_button_round_brackets: - if (direction == left) { - keyboard.roundBracketsButtonPressed(); - return true; - } - return processDefault(direction, (DirectionDragView) view); - default: - return processDefault(direction, (DirectionDragView) view); - } - } - - private boolean processMemoryButton(@NonNull DragDirection direction) { - final DisplayState state = display.getState(); - if (!state.valid) { - return false; - } - Generic value = state.getResult(); - if (value == null) { - try { - value = Expression.valueOf(state.text); - } catch (jscl.text.ParseException e) { - Log.w(App.TAG, e.getMessage(), e); - } - } - if (value == null) { - memory.get().requestShow(); - return false; - } - switch (direction) { - case up: - memory.get().add(value); - return true; - case down: - memory.get().subtract(value); - return true; - } - return false; - } - - private boolean processDefault(@Nonnull DragDirection direction, @Nonnull DirectionDragView button) { - final String text = button.getText(direction).getValue(); - return keyboard.buttonPressed(text); - } } \ No newline at end of file diff --git a/app/src/main/java/org/solovyev/android/calculator/keyboard/PartialKeyboardUi.java b/app/src/main/java/org/solovyev/android/calculator/keyboard/PartialKeyboardUi.java index dd503ded..94a4c58a 100644 --- a/app/src/main/java/org/solovyev/android/calculator/keyboard/PartialKeyboardUi.java +++ b/app/src/main/java/org/solovyev/android/calculator/keyboard/PartialKeyboardUi.java @@ -10,18 +10,15 @@ import android.widget.ImageButton; import butterknife.Bind; import butterknife.ButterKnife; import org.solovyev.android.calculator.R; -import org.solovyev.android.calculator.buttons.CppSpecialButton; import org.solovyev.android.calculator.view.EditorLongClickEraser; import org.solovyev.android.views.dragbutton.DirectionDragButton; import org.solovyev.android.views.dragbutton.DirectionDragImageButton; -import org.solovyev.android.views.dragbutton.DragDirection; import javax.annotation.Nonnull; import javax.inject.Inject; import static org.solovyev.android.calculator.Preferences.Gui.vibrateOnKeypress; import static org.solovyev.android.views.dragbutton.DragDirection.down; -import static org.solovyev.android.views.dragbutton.DragDirection.up; public class PartialKeyboardUi extends BaseKeyboardUi { @@ -73,52 +70,4 @@ public class PartialKeyboardUi extends BaseKeyboardUi { longClickEraser.setVibrateOnKeypress(vibrateOnKeypress.getPreference(preferences)); } } - - @Override - protected boolean onDrag(@NonNull View view, @NonNull DragDirection direction, @Nonnull String value) { - switch (view.getId()) { - case R.id.cpp_button_right: - case R.id.cpp_button_left: - keyboard.buttonPressed(value); - return true; - case R.id.cpp_button_clear: - if(direction == up) { - memory.get().clear(); - return true; - } - return false; - case R.id.cpp_button_equals: - if (direction == down) { - launcher.plotDisplayedExpression(); - return true; - } else if (direction == up) { - calculator.simplify(); - return true; - } - - return false; - } - return false; - } - - @Override - public void onClick(View v) { - switch (v.getId()) { - case R.id.cpp_button_left: - onClick(v, CppSpecialButton.cursor_left); - break; - case R.id.cpp_button_right: - onClick(v, CppSpecialButton.cursor_right); - break; - case R.id.cpp_button_clear: - onClick(v, CppSpecialButton.clear); - break; - case R.id.cpp_button_erase: - onClick(v, CppSpecialButton.erase); - break; - case R.id.cpp_button_equals: - onClick(v, CppSpecialButton.equals); - break; - } - } } diff --git a/app/src/main/java/org/solovyev/android/calculator/view/AngleUnitsButton.java b/app/src/main/java/org/solovyev/android/calculator/view/AngleUnitsButton.java deleted file mode 100644 index 12769efc..00000000 --- a/app/src/main/java/org/solovyev/android/calculator/view/AngleUnitsButton.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright 2013 serso aka se.solovyev - * - * 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. - * - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * Contact details - * - * Email: se.solovyev@gmail.com - * Site: http://se.solovyev.org - */ - -package org.solovyev.android.calculator.view; - -import android.content.Context; -import android.support.v4.content.ContextCompat; -import android.util.AttributeSet; -import jscl.AngleUnit; -import org.solovyev.android.calculator.R; -import org.solovyev.android.views.dragbutton.DirectionDragImageButton; -import org.solovyev.android.views.dragbutton.DirectionTextView; -import org.solovyev.android.views.dragbutton.DragDirection; - -import javax.annotation.Nonnull; - -public class AngleUnitsButton extends DirectionDragImageButton { - - @Nonnull - private AngleUnit angleUnit = AngleUnit.deg; - - public AngleUnitsButton(Context context, @Nonnull AttributeSet attrs) { - super(context, attrs); - updateDirectionColors(); - } - - boolean isCurrentAngleUnits(@Nonnull String directionText) { - return angleUnit.name().equals(directionText); - } - - public void setAngleUnit(@Nonnull AngleUnit angleUnit) { - if (this.angleUnit == angleUnit) { - return; - } - this.angleUnit = angleUnit; - updateDirectionColors(); - } - - private void updateDirectionColors() { - for (DragDirection direction : DragDirection.values()) { - final DirectionTextView.Text text = getText(direction); - if (isCurrentAngleUnits(text.getValue())) { - text.setColor(ContextCompat.getColor(getContext(), R.color.yellow_100), 1f); - } else { - text.setColor(ContextCompat.getColor(getContext(), R.color.cpp_text), DirectionTextView.DEF_ALPHA); - } - } - } -} diff --git a/app/src/main/java/org/solovyev/android/calculator/view/NumeralBasesButton.java b/app/src/main/java/org/solovyev/android/calculator/view/NumeralBasesButton.java deleted file mode 100644 index 5c489004..00000000 --- a/app/src/main/java/org/solovyev/android/calculator/view/NumeralBasesButton.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright 2013 serso aka se.solovyev - * - * 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. - * - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * Contact details - * - * Email: se.solovyev@gmail.com - * Site: http://se.solovyev.org - */ - -package org.solovyev.android.calculator.view; - -import android.content.Context; -import android.support.v4.content.ContextCompat; -import android.util.AttributeSet; -import jscl.NumeralBase; -import org.solovyev.android.calculator.R; -import org.solovyev.android.views.dragbutton.DirectionDragImageButton; -import org.solovyev.android.views.dragbutton.DirectionTextView; -import org.solovyev.android.views.dragbutton.DragDirection; - -import javax.annotation.Nonnull; - -public class NumeralBasesButton extends DirectionDragImageButton { - - @Nonnull - private NumeralBase numeralBase = NumeralBase.dec; - - public NumeralBasesButton(Context context, @Nonnull AttributeSet attrs) { - super(context, attrs); - updateDirectionColors(); - } - - boolean isCurrentNumberBase(@Nonnull String directionText) { - return numeralBase.name().equals(directionText); - } - - public void setNumeralBase(@Nonnull NumeralBase numeralBase) { - if (this.numeralBase == numeralBase) { - return; - } - this.numeralBase = numeralBase; - updateDirectionColors(); - } - - private void updateDirectionColors() { - for (DragDirection direction : DragDirection.values()) { - final DirectionTextView.Text text = getText(direction); - if (isCurrentNumberBase(text.getValue())) { - text.setColor(ContextCompat.getColor(getContext(), R.color.yellow_100), 1f); - } else { - text.setColor(ContextCompat.getColor(getContext(), R.color.cpp_text), DirectionTextView.DEF_ALPHA); - } - } - } -} diff --git a/app/src/main/java/org/solovyev/android/views/dragbutton/DragView.java b/app/src/main/java/org/solovyev/android/views/dragbutton/DragView.java index a7a139d5..06c3813a 100644 --- a/app/src/main/java/org/solovyev/android/views/dragbutton/DragView.java +++ b/app/src/main/java/org/solovyev/android/views/dragbutton/DragView.java @@ -3,6 +3,7 @@ package org.solovyev.android.views.dragbutton; import android.support.annotation.Nullable; public interface DragView { + int getId(); void setOnDragListener(@Nullable DragListener listener); void setVibrateOnDrag(boolean vibrateOnDrag); } diff --git a/app/src/main/res/drawable-hdpi/ic_keyboard_arrow_left_white_48dp.png b/app/src/main/res/drawable-hdpi/ic_keyboard_arrow_left_white_48dp.png deleted file mode 100644 index d8af01c85ab1fbed7f641baf3b0fee50b79b74ff..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 226 zcmeAS@N?(olHy`uVBq!ia0vp^9w5xY0wn)GsXhawR(iTPhGg7(d*du`lLG^5BG(4C zyFz~*1$EAL_iB`U+B0zlXPgPoNgvkxncv@rO`Qn@e|Jm~5lcVOptv-}Lx>~uiGhdy z9F>=wl?o^6c5j|+eDLOG3wE*PX@z|{=Q@vOM$Fkfhtu!$qms|}UT!x(H`UB&5_kWV zj~;7Penf7O2~y^?b80ZowOkT)Dg09Hwe5e|UrscA>D+cu(zDh~bh(Jg@iRd1X1^_? Y`On}(7bdyL0UgcY>FVdQ&MBb@0B}oQssI20 diff --git a/app/src/main/res/drawable-hdpi/ic_keyboard_arrow_right_white_48dp.png b/app/src/main/res/drawable-hdpi/ic_keyboard_arrow_right_white_48dp.png deleted file mode 100644 index e9bc3889e0263bff935d25ad2368f91bb64e9f7b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 227 zcmeAS@N?(olHy`uVBq!ia0vp^9w5xY0wn)GsXhawR(ZNOhGg7(dv&*9Qvd^NpwY>k zlly;YUXu*eJeb8Jx+wdQ1=At!`=RC97B|&61%cqN*Da0WM>(M@w0!STHO+!Ab}~bR;9J?n6v1p z=9EK)Ph%eN6r3xlvQ(b?poU+)-S~i|RH0ptlHPLXuJf4_Y%KIA@h?}iEbIV+7yeaD YJEW3&7S3+&0lJ#O)78&qol`;+06LLbwEzGB diff --git a/app/src/main/res/drawable-mdpi/ic_keyboard_arrow_left_white_48dp.png b/app/src/main/res/drawable-mdpi/ic_keyboard_arrow_left_white_48dp.png deleted file mode 100644 index 2b4d614a17813189eca5158c7df64b8b06613bc1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 164 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA0wn)(8}b0DY)==*kch)?FYo4Ta1dyD=y=fR z3)eNJ8kT8^s*9%flq-mHvd4y3NO#GZ>y8IVpF+_r<68M;MGZ8_3C3C?EM^dG{gE OS_V&7KbLh*2~7Zs%S0#u diff --git a/app/src/main/res/drawable-mdpi/ic_keyboard_arrow_right_white_48dp.png b/app/src/main/res/drawable-mdpi/ic_keyboard_arrow_right_white_48dp.png deleted file mode 100644 index aa55964e73db339eb94e30b751b8ee3cf183eecd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 163 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA0wn)(8}b0DEKe85kch)?&mQD;aA07)XuF6Z z>Q8%2hTMq11H*W9PAo@e;k7hy~$2rUL@F^m<{vOd#UNM6)g~c{ps;SM_e~nt4 z(-nV#)9M|z!p%%CKG_NG{r*q=ci1leMh~HP#tU!HZz*~|r}z<{Vv9`Vfyhe=KwB9+ MUHx3vIVCg!0BGzwyZ`_I diff --git a/app/src/main/res/drawable-xhdpi/ic_keyboard_arrow_left_white_48dp.png b/app/src/main/res/drawable-xhdpi/ic_keyboard_arrow_left_white_48dp.png deleted file mode 100644 index 36cfbc9a904ad0126a126ea5752d73d08faa89fe..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 203 zcmeAS@N?(olHy`uVBq!ia0vp^2_VeD0wg^q?%xcgCV9FzhE&{odwsW{BcnviMSJ)D zFAod7ofoW~%JEKO;)z+sS~v9XNCH(d)WuKJx%+wY6}3IjwyUjqX0Ep9Y`*so1A9-t zyPx0RoRz=tXYXh8XS4s`{WJULv)T78{%7s``K+AHW>a?k9w8Nv2?~x)ESw<5hvS^T xXFh9rTvF+6;X6On>G7Y?LzPvjCsRSTG^n?DGDja<L(SB+ zeJ%wTrqxZ^bj5KVcW29Po1go*?$zI(JJS%ToZ-jbHAbfA=NF$0nmxgCx{9Xni8Pw?JTE4_s7GB%;HgcAmz;|# z|Ds9kfhU_+JhAMUSFvf@j>iX^+4<#cWIxt?cmQFBKic^a!m3zz=<_kKbj61+%qH$v zSNGnL=l*q!Sy0KPLqO4~g##DEYIa7+f+h}suPu*Gc6?akQW3PM^o952NiW*fl@A3A iJe-K^P!&(9<+68Pd|fzEZZQYYzYLzPelF{r5}E+)er+EB diff --git a/app/src/main/res/drawable-xxhdpi/ic_keyboard_arrow_right_white_48dp.png b/app/src/main/res/drawable-xxhdpi/ic_keyboard_arrow_right_white_48dp.png deleted file mode 100644 index 25503d1b49f1382d10f694205e1a785012ca7d80..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 286 zcmeAS@N?(olHy`uVBq!ia0vp^6F``Q1xWh(YZ(Knr=Bj3Ar-gYUf9jay@0VeDybd z+pb-4jeei zAnMR$V<*hxc*vP0a#oKu=i5y{)u>>??9wpNQ-6$;9cSNfvf0mCS;zh9kJyu+x+gwI zPk6pv;e5X1JfOt?Gu3=uQ-A7roz%Dgbh`h~C*}Qra%TQtGXu;z0Tg_1^WVGf=TrB( zpA+Njo=l&g`+2^|WDrXpB3Qqj*{(SB3=_A=<^xPebv8H{a_a<;&VBG+Tt-w|{LU3E z@e}j)CfLUogs+HEsNd!AujHWf)S|{eFPVN`X7yJ+#rlaE)u%w-0seh%i&Af_Uv?o1 OBK4 diff --git a/app/src/main/res/drawable-xxxhdpi/ic_keyboard_arrow_right_white_48dp.png b/app/src/main/res/drawable-xxxhdpi/ic_keyboard_arrow_right_white_48dp.png deleted file mode 100644 index 034a3eff4dc443ac589699f86d6d697bf20c480a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 354 zcmeAS@N?(olHy`uVBq!ia0vp^2SAvG8AvYpRA>UE3<7*YT!Hj|Qh>EDivds%e@T#E zFhj#fi}xQ)7>!Mvn_6TVdoHF;14>=;ba4!+hlL4CeQr``(An{)ZV)7>9CXF|cvg=|qj^Bcd-?G!qty7?f( zlz?+HH6ChLPi0xqGT&!Pz`CC+5*em_n0``dTJ)@fNf#z7B>D?KyR6_bVZ&igY0mE| z9uW^(9#?Y+0U3vxH(!0-&B7s|;LyOp$OL8_(C6uBWclJIc7fBM!7Ibj^gx5(3x;U{ o%sFpBWNywuvnw+k(qO=RW9^jKb0LbQK;JQVy85}Sb4q9e0D^I%=l}o! diff --git a/app/src/main/res/drawable/ic_chevron_left_24dp.xml b/app/src/main/res/drawable/ic_chevron_left_white_24dp.xml similarity index 55% rename from app/src/main/res/drawable/ic_chevron_left_24dp.xml rename to app/src/main/res/drawable/ic_chevron_left_white_24dp.xml index c339eccc..a32c74ac 100644 --- a/app/src/main/res/drawable/ic_chevron_left_24dp.xml +++ b/app/src/main/res/drawable/ic_chevron_left_white_24dp.xml @@ -1,9 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportHeight="24.0" + android:viewportWidth="24.0"> + android:pathData="M15.41,7.41L14,6l-6,6 6,6 1.41,-1.41L10.83,12z" /> diff --git a/app/src/main/res/drawable/ic_favorite_border_white_48dp.xml b/app/src/main/res/drawable/ic_favorite_border_white_48dp.xml new file mode 100644 index 00000000..534260f9 --- /dev/null +++ b/app/src/main/res/drawable/ic_favorite_border_white_48dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_keyboard_arrow_left_white_48dp.xml b/app/src/main/res/drawable/ic_keyboard_arrow_left_white_48dp.xml new file mode 100644 index 00000000..b215b841 --- /dev/null +++ b/app/src/main/res/drawable/ic_keyboard_arrow_left_white_48dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_keyboard_arrow_right_white_48dp.xml b/app/src/main/res/drawable/ic_keyboard_arrow_right_white_48dp.xml new file mode 100644 index 00000000..7c99a11f --- /dev/null +++ b/app/src/main/res/drawable/ic_keyboard_arrow_right_white_48dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/layout-land/cpp_app_keyboard.xml b/app/src/main/res/layout-land/cpp_app_keyboard.xml index 8b210807..e40d246f 100644 --- a/app/src/main/res/layout-land/cpp_app_keyboard.xml +++ b/app/src/main/res/layout-land/cpp_app_keyboard.xml @@ -84,7 +84,7 @@ a:baselineAligned="false" a:orientation="horizontal"> - + diff --git a/app/src/main/res/layout/cpp_app_button_copy.xml b/app/src/main/res/layout/cpp_app_button_copy.xml index 5a26e8b6..abf9433b 100644 --- a/app/src/main/res/layout/cpp_app_button_copy.xml +++ b/app/src/main/res/layout/cpp_app_button_copy.xml @@ -22,7 +22,7 @@ ~ Site: http://se.solovyev.org --> - \ No newline at end of file diff --git a/app/src/main/res/layout/cpp_app_button_history.xml b/app/src/main/res/layout/cpp_app_button_history.xml index 1b29e5fc..a7291251 100644 --- a/app/src/main/res/layout/cpp_app_button_history.xml +++ b/app/src/main/res/layout/cpp_app_button_history.xml @@ -28,5 +28,5 @@ style="?attr/cpp_button_style_control" a:src="@drawable/ic_history_white_48dp" app:directionTextScale="0.5" - app:directionTextDown="@string/cpp_kb_redo" - app:directionTextUp="@string/cpp_kb_undo" /> \ No newline at end of file + app:directionTextDown="@string/cpp_glyph_redo" + app:directionTextUp="@string/cpp_glyph_undo" /> \ No newline at end of file diff --git a/app/src/main/res/layout/cpp_app_button_like.xml b/app/src/main/res/layout/cpp_app_button_like.xml new file mode 100644 index 00000000..88497724 --- /dev/null +++ b/app/src/main/res/layout/cpp_app_button_like.xml @@ -0,0 +1,29 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/cpp_app_button_paste.xml b/app/src/main/res/layout/cpp_app_button_paste.xml index b995e86f..718e80dc 100644 --- a/app/src/main/res/layout/cpp_app_button_paste.xml +++ b/app/src/main/res/layout/cpp_app_button_paste.xml @@ -22,7 +22,7 @@ ~ Site: http://se.solovyev.org --> - + + "\ue000" + "\ue001" + "\ue002" + "\ue003" + "\ue004" + "\ue005" + "\ue006" + "\ue007" + "\ue008" + "\ue009" + \ No newline at end of file diff --git a/app/src/main/res/values/text_non_translatable.xml b/app/src/main/res/values/text_non_translatable.xml index 6144e7a8..c7026931 100644 --- a/app/src/main/res/values/text_non_translatable.xml +++ b/app/src/main/res/values/text_non_translatable.xml @@ -17,8 +17,6 @@ αβγ E %1$s (AMOLED) - "\ue000" - "\ue001" 0 1 diff --git a/app/src/test/java/org/solovyev/android/calculator/buttons/CppSpecialButtonTest.java b/app/src/test/java/org/solovyev/android/calculator/buttons/CppSpecialButtonTest.java new file mode 100644 index 00000000..e26e7818 --- /dev/null +++ b/app/src/test/java/org/solovyev/android/calculator/buttons/CppSpecialButtonTest.java @@ -0,0 +1,20 @@ +package org.solovyev.android.calculator.buttons; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +public class CppSpecialButtonTest { + + @Test + public void testShouldReturnButtonByGlyph() throws Exception { + assertEquals(CppSpecialButton.copy, CppSpecialButton.getByGlyph(CppSpecialButton.copy.glyph)); + assertEquals(CppSpecialButton.paste, CppSpecialButton.getByGlyph(CppSpecialButton.paste.glyph)); + } + + @Test + public void testShouldReturnNullForButtonWithoutGlyph() throws Exception { + assertNull(CppSpecialButton.getByGlyph(CppSpecialButton.brackets_wrap.glyph)); + } +} \ No newline at end of file diff --git a/app/src/test/java/org/solovyev/android/calculator/view/AngleUnitsButtonTest.java b/app/src/test/java/org/solovyev/android/calculator/view/AngleUnitsButtonTest.java deleted file mode 100644 index fac85081..00000000 --- a/app/src/test/java/org/solovyev/android/calculator/view/AngleUnitsButtonTest.java +++ /dev/null @@ -1,61 +0,0 @@ -package org.solovyev.android.calculator.view; - -import android.app.Activity; -import android.os.Build; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mockito; -import org.robolectric.Robolectric; -import org.robolectric.RobolectricGradleTestRunner; -import org.robolectric.Shadows; -import org.robolectric.annotation.Config; -import org.robolectric.res.Attribute; -import org.robolectric.shadows.ShadowActivity; -import org.solovyev.android.calculator.BuildConfig; - -import java.util.ArrayList; - -import static jscl.AngleUnit.deg; -import static jscl.AngleUnit.grad; -import static jscl.AngleUnit.rad; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; - -@Config(constants = BuildConfig.class, sdk = Build.VERSION_CODES.LOLLIPOP) -@RunWith(RobolectricGradleTestRunner.class) -public class AngleUnitsButtonTest { - - private AngleUnitsButton button; - - @Before - public void setUp() throws Exception { - final Activity context = Robolectric.buildActivity(Activity.class).create().get(); - final ShadowActivity activity = Shadows.shadowOf(context); - button = new AngleUnitsButton(context, activity.createAttributeSet(new ArrayList(), AngleUnitsButton.class)); - } - - @Test - public void testIsCurrentAngleUnits() throws Exception { - button.setAngleUnit(rad); - assertTrue(button.isCurrentAngleUnits(rad.name())); - assertFalse(button.isCurrentAngleUnits(deg.name())); - assertFalse(button.isCurrentAngleUnits(grad.name())); - } - - @Test - public void testInvalidateShouldBeCalledOnlyWhenChangeIsDone() throws Exception { - button.setAngleUnit(rad); - - button = Mockito.spy(button); - - button.setAngleUnit(deg); - verify(button, times(1)).invalidate(); - - button.setAngleUnit(deg); - verify(button, times(1)).invalidate(); - } -} diff --git a/app/src/test/java/org/solovyev/android/calculator/view/NumeralBasesButtonTest.java b/app/src/test/java/org/solovyev/android/calculator/view/NumeralBasesButtonTest.java deleted file mode 100644 index 41d91c70..00000000 --- a/app/src/test/java/org/solovyev/android/calculator/view/NumeralBasesButtonTest.java +++ /dev/null @@ -1,61 +0,0 @@ -package org.solovyev.android.calculator.view; - -import android.app.Activity; -import android.os.Build; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mockito; -import org.robolectric.Robolectric; -import org.robolectric.RobolectricGradleTestRunner; -import org.robolectric.Shadows; -import org.robolectric.annotation.Config; -import org.robolectric.res.Attribute; -import org.robolectric.shadows.ShadowActivity; -import org.solovyev.android.calculator.BuildConfig; - -import java.util.ArrayList; - -import static jscl.NumeralBase.bin; -import static jscl.NumeralBase.dec; -import static jscl.NumeralBase.hex; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; - -@Config(constants = BuildConfig.class, sdk = Build.VERSION_CODES.LOLLIPOP) -@RunWith(RobolectricGradleTestRunner.class) -public class NumeralBasesButtonTest { - - private NumeralBasesButton button; - - @Before - public void setUp() throws Exception { - final Activity context = Robolectric.buildActivity(Activity.class).create().get(); - final ShadowActivity activity = Shadows.shadowOf(context); - button = new NumeralBasesButton(context, activity.createAttributeSet(new ArrayList(), NumeralBasesButton.class)); - } - - @Test - public void testIsCurrentNumeralBase() throws Exception { - button.setNumeralBase(dec); - assertTrue(button.isCurrentNumberBase(dec.name())); - assertFalse(button.isCurrentNumberBase(hex.name())); - assertFalse(button.isCurrentNumberBase(bin.name())); - } - - @Test - public void testInvalidateShouldBeCalledOnlyWhenChangeIsDone() throws Exception { - button.setNumeralBase(dec); - - button = Mockito.spy(button); - - button.setNumeralBase(hex); - verify(button, times(1)).invalidate(); - - button.setNumeralBase(hex); - verify(button, times(1)).invalidate(); - } -}