Number format preference
This commit is contained in:
parent
9e4a798c1b
commit
5a9bcdede7
@ -56,7 +56,11 @@
|
|||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".preferences.PreferencesActivity"
|
android:name=".preferences.PreferencesActivity"
|
||||||
android:label="@string/cpp_settings" />
|
android:label="@string/cpp_settings">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.MAIN"/>
|
||||||
|
</intent-filter>
|
||||||
|
</activity>
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".preferences.PreferencesActivity$Dialog"
|
android:name=".preferences.PreferencesActivity$Dialog"
|
||||||
|
@ -25,7 +25,11 @@ package org.solovyev.android.calculator;
|
|||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.app.Application;
|
import android.app.Application;
|
||||||
import android.app.Dialog;
|
import android.app.Dialog;
|
||||||
import android.content.*;
|
import android.content.ActivityNotFoundException;
|
||||||
|
import android.content.ComponentName;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
import android.content.res.Configuration;
|
import android.content.res.Configuration;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
@ -48,14 +52,17 @@ import android.util.TypedValue;
|
|||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.view.inputmethod.InputMethodManager;
|
import android.view.inputmethod.InputMethodManager;
|
||||||
|
import android.widget.ArrayAdapter;
|
||||||
|
|
||||||
import org.solovyev.android.Check;
|
import org.solovyev.android.Check;
|
||||||
import org.solovyev.android.calculator.floating.FloatingCalculatorService;
|
import org.solovyev.android.calculator.floating.FloatingCalculatorService;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
public final class App {
|
public final class App {
|
||||||
|
|
||||||
public static final String TAG = "C++";
|
public static final String TAG = "C++";
|
||||||
@ -276,6 +283,11 @@ public final class App {
|
|||||||
processViewsOfType0(view, viewClass, viewProcessor);
|
processViewsOfType0(view, viewClass, viewProcessor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
public static <T> ArrayAdapter<T> makeSimpleSpinnerAdapter(@NonNull Context context) {
|
||||||
|
return new ArrayAdapter<>(context, R.layout.support_simple_spinner_dropdown_item);
|
||||||
|
}
|
||||||
|
|
||||||
public interface ViewProcessor<V> {
|
public interface ViewProcessor<V> {
|
||||||
void process(@Nonnull V view);
|
void process(@Nonnull V view);
|
||||||
}
|
}
|
||||||
|
@ -23,15 +23,11 @@
|
|||||||
package org.solovyev.android.calculator;
|
package org.solovyev.android.calculator;
|
||||||
|
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
|
import android.support.annotation.StringRes;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
|
|
||||||
import com.squareup.otto.Bus;
|
import com.squareup.otto.Bus;
|
||||||
import jscl.AngleUnit;
|
|
||||||
import jscl.JsclMathEngine;
|
|
||||||
import jscl.MathEngine;
|
|
||||||
import jscl.NumeralBase;
|
|
||||||
import jscl.math.operator.Operator;
|
|
||||||
import jscl.text.Identifier;
|
|
||||||
import jscl.text.Parser;
|
|
||||||
import org.solovyev.android.Check;
|
import org.solovyev.android.Check;
|
||||||
import org.solovyev.android.calculator.functions.FunctionsRegistry;
|
import org.solovyev.android.calculator.functions.FunctionsRegistry;
|
||||||
import org.solovyev.android.calculator.operators.OperatorsRegistry;
|
import org.solovyev.android.calculator.operators.OperatorsRegistry;
|
||||||
@ -43,15 +39,25 @@ import org.solovyev.android.prefs.StringPreference;
|
|||||||
import org.solovyev.common.text.EnumMapper;
|
import org.solovyev.common.text.EnumMapper;
|
||||||
import org.solovyev.common.text.NumberMapper;
|
import org.solovyev.common.text.NumberMapper;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
import javax.inject.Inject;
|
|
||||||
import javax.inject.Singleton;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javax.inject.Singleton;
|
||||||
|
|
||||||
|
import jscl.AngleUnit;
|
||||||
|
import jscl.JsclMathEngine;
|
||||||
|
import jscl.MathEngine;
|
||||||
|
import jscl.NumeralBase;
|
||||||
|
import jscl.math.operator.Operator;
|
||||||
|
import jscl.text.Identifier;
|
||||||
|
import jscl.text.Parser;
|
||||||
|
import midpcalc.Real;
|
||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
public class Engine implements SharedPreferences.OnSharedPreferenceChangeListener {
|
public class Engine implements SharedPreferences.OnSharedPreferenceChangeListener {
|
||||||
|
|
||||||
@ -231,8 +237,24 @@ public class Engine implements SharedPreferences.OnSharedPreferenceChangeListene
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public enum Notation {
|
||||||
|
simple(Real.NumberFormat.FSE_NONE, R.string.cpp_number_format_simple),
|
||||||
|
eng(Real.NumberFormat.FSE_ENG, R.string.cpp_number_format_eng),
|
||||||
|
sci(Real.NumberFormat.FSE_SCI, R.string.cpp_number_format_sci);
|
||||||
|
|
||||||
|
public final int id;
|
||||||
|
@StringRes
|
||||||
|
public final int name;
|
||||||
|
|
||||||
|
Notation(int id, @StringRes int name) {
|
||||||
|
this.id = id;
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static class ChangedEvent {
|
public static class ChangedEvent {
|
||||||
static final ChangedEvent INSTANCE = new ChangedEvent();
|
static final ChangedEvent INSTANCE = new ChangedEvent();
|
||||||
|
|
||||||
private ChangedEvent() {
|
private ChangedEvent() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -264,6 +286,7 @@ public class Engine implements SharedPreferences.OnSharedPreferenceChangeListene
|
|||||||
public static final StringPreference<Integer> precision = StringPreference.ofTypedValue("engine.output.precision", "5", NumberMapper.of(Integer.class));
|
public static final StringPreference<Integer> precision = StringPreference.ofTypedValue("engine.output.precision", "5", NumberMapper.of(Integer.class));
|
||||||
public static final BooleanPreference scientificNotation = BooleanPreference.of("engine.output.scientificNotation", false);
|
public static final BooleanPreference scientificNotation = BooleanPreference.of("engine.output.scientificNotation", false);
|
||||||
public static final BooleanPreference round = BooleanPreference.of("engine.output.round", true);
|
public static final BooleanPreference round = BooleanPreference.of("engine.output.round", true);
|
||||||
|
public static final StringPreference<Notation> notation = StringPreference.ofEnum("engine.output.notation", Notation.simple, Notation.class);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,21 +21,38 @@ import android.view.View;
|
|||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.view.inputmethod.EditorInfo;
|
import android.view.inputmethod.EditorInfo;
|
||||||
import android.view.inputmethod.InputMethodManager;
|
import android.view.inputmethod.InputMethodManager;
|
||||||
import android.widget.*;
|
import android.widget.AdapterView;
|
||||||
import butterknife.Bind;
|
import android.widget.ArrayAdapter;
|
||||||
import butterknife.ButterKnife;
|
import android.widget.EditText;
|
||||||
import org.solovyev.android.calculator.*;
|
import android.widget.ImageButton;
|
||||||
|
import android.widget.Spinner;
|
||||||
|
import android.widget.TextView;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import org.solovyev.android.calculator.App;
|
||||||
|
import org.solovyev.android.calculator.AppComponent;
|
||||||
|
import org.solovyev.android.calculator.AppModule;
|
||||||
|
import org.solovyev.android.calculator.BaseDialogFragment;
|
||||||
|
import org.solovyev.android.calculator.Clipboard;
|
||||||
|
import org.solovyev.android.calculator.Editor;
|
||||||
|
import org.solovyev.android.calculator.Keyboard;
|
||||||
|
import org.solovyev.android.calculator.R;
|
||||||
import org.solovyev.android.calculator.keyboard.FloatingKeyboard;
|
import org.solovyev.android.calculator.keyboard.FloatingKeyboard;
|
||||||
import org.solovyev.android.calculator.keyboard.FloatingKeyboardWindow;
|
import org.solovyev.android.calculator.keyboard.FloatingKeyboardWindow;
|
||||||
import org.solovyev.android.calculator.keyboard.FloatingNumberKeyboard;
|
import org.solovyev.android.calculator.keyboard.FloatingNumberKeyboard;
|
||||||
import org.solovyev.android.calculator.math.MathUtils;
|
import org.solovyev.android.calculator.math.MathUtils;
|
||||||
|
import org.solovyev.android.calculator.text.NaturalComparator;
|
||||||
import org.solovyev.android.calculator.view.EditTextCompat;
|
import org.solovyev.android.calculator.view.EditTextCompat;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import java.util.Comparator;
|
|
||||||
|
|
||||||
import static org.solovyev.android.calculator.UiPreferences.Converter.*;
|
import butterknife.Bind;
|
||||||
|
import butterknife.ButterKnife;
|
||||||
|
|
||||||
|
import static org.solovyev.android.calculator.UiPreferences.Converter.lastDimension;
|
||||||
|
import static org.solovyev.android.calculator.UiPreferences.Converter.lastUnitsFrom;
|
||||||
|
import static org.solovyev.android.calculator.UiPreferences.Converter.lastUnitsTo;
|
||||||
|
|
||||||
public class ConverterFragment extends BaseDialogFragment
|
public class ConverterFragment extends BaseDialogFragment
|
||||||
implements AdapterView.OnItemSelectedListener, View.OnFocusChangeListener, TextView.OnEditorActionListener, View.OnClickListener, TextWatcher {
|
implements AdapterView.OnItemSelectedListener, View.OnFocusChangeListener, TextView.OnEditorActionListener, View.OnClickListener, TextWatcher {
|
||||||
@ -44,7 +61,6 @@ public class ConverterFragment extends BaseDialogFragment
|
|||||||
private static final String STATE_SELECTION_FROM = "selection.from";
|
private static final String STATE_SELECTION_FROM = "selection.from";
|
||||||
private static final String STATE_SELECTION_TO = "selection.to";
|
private static final String STATE_SELECTION_TO = "selection.to";
|
||||||
private static final String EXTRA_VALUE = "value";
|
private static final String EXTRA_VALUE = "value";
|
||||||
private static final NamedItemComparator COMPARATOR = new NamedItemComparator();
|
|
||||||
public static final int NONE = -1;
|
public static final int NONE = -1;
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
@ -96,11 +112,6 @@ public class ConverterFragment extends BaseDialogFragment
|
|||||||
App.showDialog(fragment, "converter", activity.getSupportFragmentManager());
|
App.showDialog(fragment, "converter", activity.getSupportFragmentManager());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
|
||||||
private static <T> ArrayAdapter<T> makeAdapter(@NonNull Context context) {
|
|
||||||
return new ArrayAdapter<>(context, R.layout.support_simple_spinner_dropdown_item);
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
public AlertDialog onCreateDialog(@Nullable Bundle savedInstanceState) {
|
public AlertDialog onCreateDialog(@Nullable Bundle savedInstanceState) {
|
||||||
@ -130,13 +141,13 @@ public class ConverterFragment extends BaseDialogFragment
|
|||||||
final View view = inflater.inflate(R.layout.cpp_unit_converter, null);
|
final View view = inflater.inflate(R.layout.cpp_unit_converter, null);
|
||||||
ButterKnife.bind(this, view);
|
ButterKnife.bind(this, view);
|
||||||
|
|
||||||
dimensionsAdapter = makeAdapter(context);
|
dimensionsAdapter = App.makeSimpleSpinnerAdapter(context);
|
||||||
for (ConvertibleDimension dimension : UnitDimension.values()) {
|
for (ConvertibleDimension dimension : UnitDimension.values()) {
|
||||||
dimensionsAdapter.add(dimension.named(context));
|
dimensionsAdapter.add(dimension.named(context));
|
||||||
}
|
}
|
||||||
dimensionsAdapter.add(NumeralBaseDimension.get().named(context));
|
dimensionsAdapter.add(NumeralBaseDimension.get().named(context));
|
||||||
adapterFrom = makeAdapter(context);
|
adapterFrom = App.makeSimpleSpinnerAdapter(context);
|
||||||
adapterTo = makeAdapter(context);
|
adapterTo = App.makeSimpleSpinnerAdapter(context);
|
||||||
|
|
||||||
dimensionsSpinner.setAdapter(dimensionsAdapter);
|
dimensionsSpinner.setAdapter(dimensionsAdapter);
|
||||||
spinnerFrom.setAdapter(adapterFrom);
|
spinnerFrom.setAdapter(adapterFrom);
|
||||||
@ -235,7 +246,7 @@ public class ConverterFragment extends BaseDialogFragment
|
|||||||
for (Convertible unit : dimension.getUnits()) {
|
for (Convertible unit : dimension.getUnits()) {
|
||||||
adapterFrom.add(unit.named(getActivity()));
|
adapterFrom.add(unit.named(getActivity()));
|
||||||
}
|
}
|
||||||
adapterFrom.sort(COMPARATOR);
|
adapterFrom.sort(NaturalComparator.INSTANCE);
|
||||||
adapterFrom.setNotifyOnChange(true);
|
adapterFrom.setNotifyOnChange(true);
|
||||||
adapterFrom.notifyDataSetChanged();
|
adapterFrom.notifyDataSetChanged();
|
||||||
if (pendingFromSelection != NONE) {
|
if (pendingFromSelection != NONE) {
|
||||||
@ -261,7 +272,7 @@ public class ConverterFragment extends BaseDialogFragment
|
|||||||
adapterTo.add(unit.named(getActivity()));
|
adapterTo.add(unit.named(getActivity()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
adapterTo.sort(COMPARATOR);
|
adapterTo.sort(NaturalComparator.INSTANCE);
|
||||||
adapterTo.setNotifyOnChange(true);
|
adapterTo.setNotifyOnChange(true);
|
||||||
adapterTo.notifyDataSetChanged();
|
adapterTo.notifyDataSetChanged();
|
||||||
if (selectedUnit != null && !except.equals(selectedUnit)) {
|
if (selectedUnit != null && !except.equals(selectedUnit)) {
|
||||||
@ -431,13 +442,6 @@ public class ConverterFragment extends BaseDialogFragment
|
|||||||
super.dismiss();
|
super.dismiss();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class NamedItemComparator implements Comparator<Named<Convertible>> {
|
|
||||||
@Override
|
|
||||||
public int compare(Named<Convertible> lhs, Named<Convertible> rhs) {
|
|
||||||
return lhs.toString().compareTo(rhs.toString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class KeyboardUser implements FloatingKeyboard.User {
|
private class KeyboardUser implements FloatingKeyboard.User {
|
||||||
@NonNull
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
|
@ -0,0 +1,100 @@
|
|||||||
|
package org.solovyev.android.calculator.preferences;
|
||||||
|
|
||||||
|
import android.annotation.TargetApi;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.preference.DialogPreference;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.ArrayAdapter;
|
||||||
|
import android.widget.SeekBar;
|
||||||
|
import android.widget.Spinner;
|
||||||
|
|
||||||
|
import org.solovyev.android.calculator.App;
|
||||||
|
import org.solovyev.android.calculator.Engine;
|
||||||
|
import org.solovyev.android.calculator.R;
|
||||||
|
import org.solovyev.android.calculator.text.NaturalComparator;
|
||||||
|
|
||||||
|
import butterknife.Bind;
|
||||||
|
import butterknife.ButterKnife;
|
||||||
|
|
||||||
|
public class NumberFormatPreference extends DialogPreference {
|
||||||
|
@Bind(R.id.nf_notation_spinner)
|
||||||
|
Spinner notationSpinner;
|
||||||
|
@Bind(R.id.nf_precision_seekbar)
|
||||||
|
SeekBar precisionSeekBar;
|
||||||
|
|
||||||
|
{
|
||||||
|
setPersistent(false);
|
||||||
|
setDialogLayoutResource(R.layout.preference_number_format);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
|
||||||
|
public NumberFormatPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
|
||||||
|
super(context, attrs, defStyleAttr, defStyleRes);
|
||||||
|
}
|
||||||
|
|
||||||
|
public NumberFormatPreference(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||||
|
super(context, attrs, defStyleAttr);
|
||||||
|
}
|
||||||
|
|
||||||
|
public NumberFormatPreference(Context context, AttributeSet attrs) {
|
||||||
|
super(context, attrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
|
||||||
|
public NumberFormatPreference(Context context) {
|
||||||
|
super(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onBindDialogView(View view) {
|
||||||
|
super.onBindDialogView(view);
|
||||||
|
ButterKnife.bind(this, view);
|
||||||
|
|
||||||
|
final SharedPreferences preferences = getSharedPreferences();
|
||||||
|
precisionSeekBar.setMax(15);
|
||||||
|
precisionSeekBar.setProgress(Math.max(0, Math.min(15, Engine.Preferences.Output.precision.getPreference(preferences))));
|
||||||
|
final ArrayAdapter<NotationItem> adapter = makeNumberFormatAdapter();
|
||||||
|
notationSpinner.setAdapter(adapter);
|
||||||
|
notationSpinner.setSelection(indexOf(adapter, Engine.Preferences.Output.notation.getPreference(preferences)));
|
||||||
|
}
|
||||||
|
|
||||||
|
private int indexOf(ArrayAdapter<NotationItem> adapter, Engine.Notation notation) {
|
||||||
|
for (int i = 0; i < adapter.getCount(); i++) {
|
||||||
|
if (adapter.getItem(i).notation == notation) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
private ArrayAdapter<NotationItem> makeNumberFormatAdapter() {
|
||||||
|
final ArrayAdapter<NotationItem> adapter = App.makeSimpleSpinnerAdapter(getContext());
|
||||||
|
for (Engine.Notation format : Engine.Notation.values()) {
|
||||||
|
adapter.add(new NotationItem(format));
|
||||||
|
}
|
||||||
|
adapter.sort(NaturalComparator.INSTANCE);
|
||||||
|
return adapter;
|
||||||
|
}
|
||||||
|
|
||||||
|
private final class NotationItem {
|
||||||
|
@NonNull
|
||||||
|
final Engine.Notation notation;
|
||||||
|
@NonNull
|
||||||
|
final String name;
|
||||||
|
|
||||||
|
private NotationItem(@NonNull Engine.Notation notation) {
|
||||||
|
this.notation = notation;
|
||||||
|
this.name = getContext().getString(notation.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -11,6 +11,7 @@ import android.support.v4.app.FragmentActivity;
|
|||||||
import android.util.SparseArray;
|
import android.util.SparseArray;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.ListView;
|
import android.widget.ListView;
|
||||||
|
|
||||||
import org.solovyev.android.calculator.AdView;
|
import org.solovyev.android.calculator.AdView;
|
||||||
import org.solovyev.android.calculator.Engine;
|
import org.solovyev.android.calculator.Engine;
|
||||||
import org.solovyev.android.calculator.Preferences;
|
import org.solovyev.android.calculator.Preferences;
|
||||||
@ -24,11 +25,12 @@ import org.solovyev.android.checkout.ProductTypes;
|
|||||||
import org.solovyev.android.checkout.RequestListener;
|
import org.solovyev.android.checkout.RequestListener;
|
||||||
import org.solovyev.android.wizard.Wizards;
|
import org.solovyev.android.wizard.Wizards;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import static org.solovyev.android.calculator.App.cast;
|
import static org.solovyev.android.calculator.App.cast;
|
||||||
import static org.solovyev.android.calculator.wizard.CalculatorWizards.DEFAULT_WIZARD_FLOW;
|
import static org.solovyev.android.calculator.wizard.CalculatorWizards.DEFAULT_WIZARD_FLOW;
|
||||||
|
@ -0,0 +1,12 @@
|
|||||||
|
package org.solovyev.android.calculator.text;
|
||||||
|
|
||||||
|
import java.util.Comparator;
|
||||||
|
|
||||||
|
public class NaturalComparator implements Comparator<Object> {
|
||||||
|
public static final NaturalComparator INSTANCE = new NaturalComparator();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compare(Object lhs, Object rhs) {
|
||||||
|
return lhs.toString().compareTo(rhs.toString());
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,224 @@
|
|||||||
|
package org.solovyev.android.views;
|
||||||
|
|
||||||
|
import android.animation.ObjectAnimator;
|
||||||
|
import android.annotation.TargetApi;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.res.ColorStateList;
|
||||||
|
import android.content.res.TypedArray;
|
||||||
|
import android.graphics.Canvas;
|
||||||
|
import android.graphics.Color;
|
||||||
|
import android.graphics.Paint;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
import android.view.animation.DecelerateInterpolator;
|
||||||
|
import android.widget.SeekBar;
|
||||||
|
|
||||||
|
import org.solovyev.android.Check;
|
||||||
|
import org.solovyev.android.calculator.R;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SeekBar for discrete values with a label displayed underneath the active tick
|
||||||
|
*/
|
||||||
|
public class DiscreteSeekBar extends SeekBar {
|
||||||
|
// Duration of how quick the SeekBar thumb should snap to its destination value
|
||||||
|
private static final int THUMB_SNAP_DURATION_TIME = 100;
|
||||||
|
private final Paint mPaint = new Paint();
|
||||||
|
private ObjectAnimator mObjectAnimator;
|
||||||
|
private OnChangeListener mOnChangeListener;
|
||||||
|
private int mCurrentTick = 0;
|
||||||
|
private CharSequence[] mTickLabels;
|
||||||
|
private ColorStateList mLabelColor;
|
||||||
|
|
||||||
|
public DiscreteSeekBar(Context context) {
|
||||||
|
super(context);
|
||||||
|
init(context, null, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public DiscreteSeekBar(Context context, AttributeSet attrs) {
|
||||||
|
super(context, attrs);
|
||||||
|
init(context, attrs, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public DiscreteSeekBar(Context context, AttributeSet attrs, int defStyle) {
|
||||||
|
super(context, attrs, defStyle);
|
||||||
|
init(context, attrs, defStyle);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void init(Context context, AttributeSet attrs, int defStyle) {
|
||||||
|
final TypedArray a = context.obtainStyledAttributes(
|
||||||
|
attrs, R.styleable.DiscreteSeekBar, defStyle, 0);
|
||||||
|
mTickLabels = a.getTextArray(R.styleable.DiscreteSeekBar_values);
|
||||||
|
final int labelsSize = a.getDimensionPixelSize(R.styleable.DiscreteSeekBar_labelsSize, 0);
|
||||||
|
final ColorStateList labelsColor = a.getColorStateList(R.styleable.DiscreteSeekBar_labelsColor);
|
||||||
|
a.recycle();
|
||||||
|
|
||||||
|
Check.isNotNull(mTickLabels);
|
||||||
|
Check.isTrue(mTickLabels.length > 0);
|
||||||
|
Check.isTrue(labelsSize > 0);
|
||||||
|
|
||||||
|
mPaint.setStyle(Paint.Style.FILL);
|
||||||
|
mPaint.setFlags(Paint.ANTI_ALIAS_FLAG);
|
||||||
|
mPaint.setTextSize(labelsSize);
|
||||||
|
|
||||||
|
if (labelsColor != null) {
|
||||||
|
setLabelColor(labelsColor);
|
||||||
|
} else {
|
||||||
|
mPaint.setColor(Color.BLACK);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extend the bottom padding to include tick label height (including descent in order to not
|
||||||
|
// clip glyphs that extends below the baseline).
|
||||||
|
Paint.FontMetricsInt fi = mPaint.getFontMetricsInt();
|
||||||
|
setPadding(getPaddingLeft(), getPaddingTop(), getPaddingRight(),
|
||||||
|
getPaddingBottom() + labelsSize + fi.descent);
|
||||||
|
|
||||||
|
super.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
|
||||||
|
@Override
|
||||||
|
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStartTrackingTouch(SeekBar seekBar) {
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
|
||||||
|
cancelAnimator();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
|
||||||
|
private void cancelAnimator() {
|
||||||
|
if (mObjectAnimator != null) {
|
||||||
|
mObjectAnimator.cancel();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStopTrackingTouch(SeekBar seekBar) {
|
||||||
|
mCurrentTick = getClosestTick(seekBar.getProgress());
|
||||||
|
final int endProgress = getProgressForTick(mCurrentTick);
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
|
||||||
|
startAnimator(seekBar, endProgress);
|
||||||
|
} else {
|
||||||
|
seekBar.setProgress(endProgress);
|
||||||
|
}
|
||||||
|
if (mOnChangeListener != null) {
|
||||||
|
mOnChangeListener.onValueChanged(mCurrentTick);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
|
||||||
|
private void startAnimator(SeekBar seekBar, int endProgress) {
|
||||||
|
mObjectAnimator = ObjectAnimator.ofInt(
|
||||||
|
seekBar, "progress", seekBar.getProgress(), endProgress);
|
||||||
|
mObjectAnimator.setInterpolator(new DecelerateInterpolator());
|
||||||
|
mObjectAnimator.setDuration(THUMB_SNAP_DURATION_TIME);
|
||||||
|
mObjectAnimator.start();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getClosestTick(int progress) {
|
||||||
|
float normalizedValue = (float) progress / getMax();
|
||||||
|
return Math.round(normalizedValue * getMaxTick());
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getProgressForTick(int tick) {
|
||||||
|
return (getMax() / getMaxTick()) * tick;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setOnSeekBarChangeListener(OnSeekBarChangeListener seekBarChangeListener) {
|
||||||
|
// It doesn't make sense to expose the interface for listening to intermediate changes.
|
||||||
|
Check.isTrue(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the largest tick value the SeekBar can represent
|
||||||
|
*
|
||||||
|
* @return maximum tick value
|
||||||
|
*/
|
||||||
|
public int getMaxTick() {
|
||||||
|
return mTickLabels.length - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set listener for observing value changes
|
||||||
|
*
|
||||||
|
* @param onChangeListener listener that should receive updates
|
||||||
|
*/
|
||||||
|
public void setOnChangeListener(OnChangeListener onChangeListener) {
|
||||||
|
mOnChangeListener = onChangeListener;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set tick value
|
||||||
|
*
|
||||||
|
* @param tickValue tick value in range [0, maxTick]
|
||||||
|
*/
|
||||||
|
public void setTickValue(int tickValue) {
|
||||||
|
Check.isTrue(tickValue >= 0);
|
||||||
|
Check.isTrue(tickValue <= getMaxTick());
|
||||||
|
mCurrentTick = tickValue;
|
||||||
|
setProgress(getProgressForTick(mCurrentTick));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLabelColor(int color) {
|
||||||
|
mLabelColor = ColorStateList.valueOf(color);
|
||||||
|
updateLabelColor();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLabelColor(ColorStateList colors) {
|
||||||
|
mLabelColor = colors;
|
||||||
|
updateLabelColor();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateLabelColor() {
|
||||||
|
int color = mLabelColor.getColorForState(getDrawableState(), Color.BLACK);
|
||||||
|
if (color != mPaint.getColor()) {
|
||||||
|
mPaint.setColor(color);
|
||||||
|
invalidate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void drawableStateChanged() {
|
||||||
|
super.drawableStateChanged();
|
||||||
|
updateLabelColor();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDraw(Canvas canvas) {
|
||||||
|
super.onDraw(canvas);
|
||||||
|
|
||||||
|
final float sliderWidth = getWidth() - getPaddingRight() - getPaddingLeft();
|
||||||
|
final float sliderStepSize = sliderWidth / getMaxTick();
|
||||||
|
int closestTick = getClosestTick(getProgress());
|
||||||
|
String text = mTickLabels[closestTick].toString();
|
||||||
|
final float startOffset = getPaddingLeft();
|
||||||
|
final float tickLabelWidth = mPaint.measureText(text);
|
||||||
|
final float tickPos = sliderStepSize * closestTick;
|
||||||
|
final float labelOffset;
|
||||||
|
// First step description text should be anchored with its left edge just
|
||||||
|
// below the slider start tick. The last step description should be anchored
|
||||||
|
// to the right just under the end tick. Tick labels in between are centered below
|
||||||
|
// each tick.
|
||||||
|
if (closestTick == 0) {
|
||||||
|
labelOffset = startOffset;
|
||||||
|
} else if (closestTick == getMaxTick()) {
|
||||||
|
labelOffset = startOffset + sliderWidth - tickLabelWidth;
|
||||||
|
} else {
|
||||||
|
labelOffset = startOffset + tickPos - tickLabelWidth / 2;
|
||||||
|
}
|
||||||
|
// Text position is drawn from bottom left, with bottom at the font baseline. We need to
|
||||||
|
// offset by the descent to cover e.g 'g' that extends below the baseline.
|
||||||
|
final Paint.FontMetricsInt m = mPaint.getFontMetricsInt();
|
||||||
|
final int lowestPosForFullGlyphCoverage = getHeight() - m.descent;
|
||||||
|
canvas.drawText(text, labelOffset, lowestPosForFullGlyphCoverage, mPaint);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Listener for observing tick changes
|
||||||
|
*/
|
||||||
|
public interface OnChangeListener {
|
||||||
|
void onValueChanged(int selectedTick);
|
||||||
|
}
|
||||||
|
}
|
@ -59,11 +59,9 @@
|
|||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/fn_linewidth_label"
|
android:id="@+id/fn_linewidth_label"
|
||||||
style="@style/TextAppearance.AppCompat.Caption"
|
style="@style/CppLabel"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:paddingStart="4dp"
|
|
||||||
android:paddingLeft="4dp"
|
|
||||||
android:text="@string/cpp_plot_function_line_width"
|
android:text="@string/cpp_plot_function_line_width"
|
||||||
tools:ignore="RtlSymmetry"/>
|
tools:ignore="RtlSymmetry"/>
|
||||||
|
|
||||||
@ -74,11 +72,9 @@
|
|||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/fn_color_label"
|
android:id="@+id/fn_color_label"
|
||||||
style="@style/TextAppearance.AppCompat.Caption"
|
style="@style/CppLabel"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:paddingStart="4dp"
|
|
||||||
android:paddingLeft="4dp"
|
|
||||||
android:text="@string/cpp_plot_function_line_color"
|
android:text="@string/cpp_plot_function_line_color"
|
||||||
tools:ignore="RtlSymmetry"/>
|
tools:ignore="RtlSymmetry"/>
|
||||||
|
|
||||||
|
34
app/src/main/res/layout/preference_number_format.xml
Normal file
34
app/src/main/res/layout/preference_number_format.xml
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="@dimen/cpp_dialog_spacing">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
style="@style/CppLabel"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Notation"/>
|
||||||
|
|
||||||
|
<Spinner
|
||||||
|
android:id="@+id/nf_notation_spinner"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
style="@style/CppLabel"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Precision"/>
|
||||||
|
|
||||||
|
<org.solovyev.android.views.DiscreteSeekBar
|
||||||
|
android:id="@+id/nf_precision_seekbar"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:labelsColor="?android:attr/textColorSecondary"
|
||||||
|
app:labelsSize="12sp"
|
||||||
|
app:values="@array/cpp_prefs_precisions"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
8
app/src/main/res/values/attrs_dsb.xml
Normal file
8
app/src/main/res/values/attrs_dsb.xml
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<declare-styleable name="DiscreteSeekBar">
|
||||||
|
<attr name="values" format="reference"/>
|
||||||
|
<attr name="labelsSize" format="dimension|reference"/>
|
||||||
|
<attr name="labelsColor" format="color|reference"/>
|
||||||
|
</declare-styleable>
|
||||||
|
</resources>
|
@ -319,6 +319,11 @@
|
|||||||
<item name="android:visibility">gone</item>
|
<item name="android:visibility">gone</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
<style name="CppLabel" parent="TextAppearance.AppCompat.Caption">
|
||||||
|
<item name="android:paddingLeft">4dp</item>
|
||||||
|
<item name="android:paddingRight">4dp</item>
|
||||||
|
</style>
|
||||||
|
|
||||||
<dimen name="list_item_text_size">16sp</dimen>
|
<dimen name="list_item_text_size">16sp</dimen>
|
||||||
<dimen name="list_item_text_size_small">14sp</dimen>
|
<dimen name="list_item_text_size_small">14sp</dimen>
|
||||||
</resources>
|
</resources>
|
@ -34,6 +34,5 @@
|
|||||||
<item>13</item>
|
<item>13</item>
|
||||||
<item>14</item>
|
<item>14</item>
|
||||||
<item>15</item>
|
<item>15</item>
|
||||||
<item>16</item>
|
|
||||||
</string-array>
|
</string-array>
|
||||||
</resources>
|
</resources>
|
@ -124,4 +124,7 @@
|
|||||||
<string name="cpp_angles">Angles</string>
|
<string name="cpp_angles">Angles</string>
|
||||||
<string name="cpp_radix">Radix</string>
|
<string name="cpp_radix">Radix</string>
|
||||||
<string name="cpp_numeral_system">Numeral system</string>
|
<string name="cpp_numeral_system">Numeral system</string>
|
||||||
|
<string name="cpp_number_format_simple">Simple</string>
|
||||||
|
<string name="cpp_number_format_eng">Engineering</string>
|
||||||
|
<string name="cpp_number_format_sci">Scientific</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -24,11 +24,9 @@
|
|||||||
|
|
||||||
<PreferenceScreen xmlns:a="http://schemas.android.com/apk/res/android">
|
<PreferenceScreen xmlns:a="http://schemas.android.com/apk/res/android">
|
||||||
|
|
||||||
<android.preference.CheckBoxPreference
|
<org.solovyev.android.calculator.preferences.NumberFormatPreference
|
||||||
a:defaultValue="true"
|
a:key="engine.output.numberFormat"
|
||||||
a:key="engine.output.round"
|
a:title="Number format" />
|
||||||
a:summary="@string/c_calc_round_result_summary"
|
|
||||||
a:title="@string/c_calc_round_result_title" />
|
|
||||||
|
|
||||||
<ListPreference
|
<ListPreference
|
||||||
a:entries="@array/cpp_prefs_precisions"
|
a:entries="@array/cpp_prefs_precisions"
|
||||||
|
Loading…
Reference in New Issue
Block a user