diff --git a/app/build.gradle b/app/build.gradle index 9974f5fa..b0394592 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -62,6 +62,7 @@ dependencies { compile 'org.solovyev:common-security:1.0.7' compile 'com.android.support:support-v4:23.1.1' compile 'com.android.support:appcompat-v7:23.1.1' + compile 'com.android.support:design:23.1.1' compile('ch.acra:acra:4.7.0') { exclude group: 'org.json' } diff --git a/app/proguard-debug.cfg b/app/proguard-debug.cfg index 6fbaac11..b1803fd1 100644 --- a/app/proguard-debug.cfg +++ b/app/proguard-debug.cfg @@ -84,13 +84,7 @@ -keep interface android.support.v7.** { *; } -# -#********************************************************************* -# -# ACRA -# -#********************************************************************* -# +## ACRA # keep this class so that logging will show 'ACRA' and not a obfuscated name like 'a'. # Note: if you are removing log messages elsewhere in this file then this isn't necessary @@ -121,4 +115,26 @@ # keep this otherwise it is removed by ProGuard -keep public class org.acra.ErrorReporter { public void handleSilentException(java.lang.Throwable); +} + +## BUTTERKNIFE + +-keep class butterknife.** { *; } +-dontwarn butterknife.internal.** +-keep class **$$ViewBinder { *; } + +-keepclasseswithmembernames class * { + @butterknife.* ; +} + +-keepclasseswithmembernames class * { + @butterknife.* ; +} + +## OTTO BUS + +-keepattributes *Annotation* +-keepclassmembers class ** { + @com.squareup.otto.Subscribe public *; + @com.squareup.otto.Produce public *; } \ No newline at end of file diff --git a/app/proguard.cfg b/app/proguard.cfg index 311ff605..dc2ca4e3 100644 --- a/app/proguard.cfg +++ b/app/proguard.cfg @@ -82,13 +82,7 @@ -keep class !android.support.v7.internal.view.menu.MenuBuilder, !android.support.v7.internal.view.menu.SubMenuBuilder, android.support.v7.** { *; } -keep interface android.support.v7.** { *; } -# -#********************************************************************* -# -# LOGS -# -#********************************************************************* -# +## LOGS -assumenosideeffects class android.util.Log { public static int v(...); @@ -98,23 +92,13 @@ public static int e(...); } +## ASSERTIONS + -assumenosideeffects class org.solovyev.android.Check { *; } -# -#********************************************************************* -# -# ACRA -# -#********************************************************************* -# - -# keep this class so that logging will show 'ACRA' and not a obfuscated name like 'a'. -# Note: if you are removing log messages elsewhere in this file then this isn't necessary --keep class org.acra.ACRA { - *; -} +## ACRA # keep this around for some enums that ACRA needs -keep class org.acra.ReportingInteractionMode { @@ -139,4 +123,26 @@ # keep this otherwise it is removed by ProGuard -keep public class org.acra.ErrorReporter { public void handleSilentException(java.lang.Throwable); +} + +## BUTTERKNIFE + +-keep class butterknife.** { *; } +-dontwarn butterknife.internal.** +-keep class **$$ViewBinder { *; } + +-keepclasseswithmembernames class * { + @butterknife.* ; +} + +-keepclasseswithmembernames class * { + @butterknife.* ; +} + +## OTTO BUS + +-keepattributes *Annotation* +-keepclassmembers class ** { + @com.squareup.otto.Subscribe public *; + @com.squareup.otto.Produce public *; } \ No newline at end of file diff --git a/app/src/main/java/org/solovyev/android/calculator/AppComponent.java b/app/src/main/java/org/solovyev/android/calculator/AppComponent.java index 22fd07d9..678eb5e1 100644 --- a/app/src/main/java/org/solovyev/android/calculator/AppComponent.java +++ b/app/src/main/java/org/solovyev/android/calculator/AppComponent.java @@ -1,6 +1,7 @@ package org.solovyev.android.calculator; import org.solovyev.android.calculator.history.BaseHistoryFragment; +import org.solovyev.android.calculator.history.EditHistoryFragment; import org.solovyev.android.calculator.onscreen.CalculatorOnscreenService; import javax.inject.Singleton; @@ -16,4 +17,5 @@ public interface AppComponent { void inject(CalculatorOnscreenService service); void inject(BaseHistoryFragment fragment); void inject(BaseDialogFragment fragment); + void inject(EditHistoryFragment fragment); } diff --git a/app/src/main/java/org/solovyev/android/calculator/BaseDialogFragment.java b/app/src/main/java/org/solovyev/android/calculator/BaseDialogFragment.java index 451515c7..8c47fd32 100644 --- a/app/src/main/java/org/solovyev/android/calculator/BaseDialogFragment.java +++ b/app/src/main/java/org/solovyev/android/calculator/BaseDialogFragment.java @@ -20,7 +20,11 @@ public abstract class BaseDialogFragment extends DialogFragment { @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); - ((CalculatorApplication) getActivity().getApplication()).getComponent().inject(this); + inject(((CalculatorApplication) getActivity().getApplication()).getComponent()); + } + + protected void inject(@NonNull AppComponent component) { + component.inject(this); } @NonNull diff --git a/app/src/main/java/org/solovyev/android/calculator/CalculatorApplication.java b/app/src/main/java/org/solovyev/android/calculator/CalculatorApplication.java index 59bd795c..e7c17cd2 100644 --- a/app/src/main/java/org/solovyev/android/calculator/CalculatorApplication.java +++ b/app/src/main/java/org/solovyev/android/calculator/CalculatorApplication.java @@ -107,6 +107,7 @@ public class CalculatorApplication extends android.app.Application implements Sh .build(); component.inject(this); history.init(initThread); + display.init(); onPostCreate(preferences, languages); } diff --git a/app/src/main/java/org/solovyev/android/calculator/Display.java b/app/src/main/java/org/solovyev/android/calculator/Display.java index 81d6a011..a9f1318f 100644 --- a/app/src/main/java/org/solovyev/android/calculator/Display.java +++ b/app/src/main/java/org/solovyev/android/calculator/Display.java @@ -40,30 +40,15 @@ import static org.solovyev.android.calculator.CalculatorEventType.conversion_res @Singleton public class Display implements CalculatorEventListener { - public static class ChangedEvent { - - @Nonnull - public final DisplayState oldState; - - @Nonnull - public final DisplayState newState; - - public ChangedEvent(@Nonnull DisplayState oldState, @Nonnull DisplayState newState) { - this.oldState = oldState; - this.newState = newState; - } - } - @Nonnull private final CalculatorEventHolder lastEvent; + @Inject + Bus bus; @Nullable private DisplayView view; @Nonnull private DisplayState state = DisplayState.empty(); - @Inject - Bus bus; - @Inject public Display(@Nonnull Calculator calculator) { lastEvent = new CalculatorEventHolder(CalculatorUtils.createFirstEventDataId()); @@ -175,4 +160,18 @@ public class Display implements CalculatorEventListener { final DisplayState displayState = calculatorEventData.getDisplayState(); setState(DisplayState.createValid(displayState.getOperation(), displayState.getResult(), result, calculatorEventData.getSequenceId())); } + + public static class ChangedEvent { + + @Nonnull + public final DisplayState oldState; + + @Nonnull + public final DisplayState newState; + + public ChangedEvent(@Nonnull DisplayState oldState, @Nonnull DisplayState newState) { + this.oldState = oldState; + this.newState = newState; + } + } } diff --git a/app/src/main/java/org/solovyev/android/calculator/DisplayState.java b/app/src/main/java/org/solovyev/android/calculator/DisplayState.java index 41a0238f..beaca75a 100644 --- a/app/src/main/java/org/solovyev/android/calculator/DisplayState.java +++ b/app/src/main/java/org/solovyev/android/calculator/DisplayState.java @@ -71,7 +71,7 @@ public class DisplayState implements Parcelable { private DisplayState(Parcel in) { text = in.readString(); valid = in.readByte() != 0; - sequence = in.readLong(); + sequence = EditorState.NO_SEQUENCE; } @Nonnull @@ -143,6 +143,5 @@ public class DisplayState implements Parcelable { public void writeToParcel(Parcel dest, int flags) { dest.writeString(text); dest.writeByte((byte) (valid ? 1 : 0)); - dest.writeLong(sequence); } } diff --git a/app/src/main/java/org/solovyev/android/calculator/EditorState.java b/app/src/main/java/org/solovyev/android/calculator/EditorState.java index 4967dddd..0368f65b 100644 --- a/app/src/main/java/org/solovyev/android/calculator/EditorState.java +++ b/app/src/main/java/org/solovyev/android/calculator/EditorState.java @@ -73,7 +73,7 @@ public class EditorState implements Parcelable { } private EditorState(Parcel in) { - sequence = in.readLong(); + sequence = NO_SEQUENCE; selection = in.readInt(); textString = in.readString(); text = textString; @@ -135,7 +135,6 @@ public class EditorState implements Parcelable { @Override public void writeToParcel(Parcel dest, int flags) { - dest.writeLong(sequence); dest.writeInt(selection); dest.writeString(textString); } diff --git a/app/src/main/java/org/solovyev/android/calculator/history/BaseHistoryFragment.java b/app/src/main/java/org/solovyev/android/calculator/history/BaseHistoryFragment.java index 629eeefb..7c267d65 100644 --- a/app/src/main/java/org/solovyev/android/calculator/history/BaseHistoryFragment.java +++ b/app/src/main/java/org/solovyev/android/calculator/history/BaseHistoryFragment.java @@ -37,9 +37,7 @@ import android.view.View; import android.view.ViewGroup; import android.widget.AdapterView; import android.widget.ArrayAdapter; -import android.widget.EditText; import android.widget.ListView; -import android.widget.TextView; import android.widget.Toast; import com.melnykov.fab.FloatingActionButton; @@ -81,7 +79,6 @@ public abstract class BaseHistoryFragment extends ListFragment { History history; @Inject Bus bus; - @Nonnull private HistoryArrayAdapter adapter; @Nonnull private FragmentUi ui; @@ -165,9 +162,7 @@ public abstract class BaseHistoryFragment extends ListFragment { @Override public void onResume() { super.onResume(); - ui.onResume(this); - updateAdapter(); } @@ -222,10 +217,10 @@ public abstract class BaseHistoryFragment extends ListFragment { } return true; case R.string.c_save: - EditHistoryFragment.show(state, getFragmentManager()); + EditHistoryFragment.show(state, true, getFragmentManager()); return true; case R.string.c_edit: - createEditHistoryDialog(state, context, false); + EditHistoryFragment.show(state, false, getFragmentManager()); return true; case R.string.c_remove: getAdapter().remove(state); @@ -238,41 +233,6 @@ public abstract class BaseHistoryFragment extends ListFragment { return super.onContextItemSelected(item); } - private void createEditHistoryDialog(@Nonnull final HistoryState state, @Nonnull final Context context, final boolean save) { - final LayoutInflater layoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); - final View editView = layoutInflater.inflate(R.layout.history_edit, null); - final TextView historyExpression = (TextView) editView.findViewById(R.id.history_edit_expression); - historyExpression.setText(BaseHistoryFragment.getHistoryText(state)); - - final EditText comment = (EditText) editView.findViewById(R.id.history_edit_comment); - comment.setText(state.getComment()); - - final AlertDialog.Builder builder = new AlertDialog.Builder(context) - .setTitle(save ? R.string.c_save_history : R.string.c_edit_history) - .setCancelable(true) - .setNegativeButton(R.string.c_cancel, null) - .setPositiveButton(R.string.c_save, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - /*if (save) { - final HistoryState savedHistoryItem = Locator.getInstance().getHistory().addSaved(state); - savedHistoryItem.setComment(comment.getText().toString()); - Locator.getInstance().getHistory().save(); - // we don't need to add element to the adapter as adapter of another activity must be updated and not this - //data.getAdapter().add(savedHistoryItem); - } else { - state.setComment(comment.getText().toString()); - Locator.getInstance().getHistory().save(); - } - getAdapter().notifyDataSetChanged();*/ - Toast.makeText(context, context.getText(R.string.c_history_saved), Toast.LENGTH_LONG).show(); - } - }) - .setView(editView); - - builder.create().show(); - } - private boolean shouldHaveCopyResult(@Nonnull HistoryState state) { return !state.display.valid || !Strings.isEmpty(state.display.text); } diff --git a/app/src/main/java/org/solovyev/android/calculator/history/EditHistoryFragment.java b/app/src/main/java/org/solovyev/android/calculator/history/EditHistoryFragment.java index 610e8e04..26ad3c49 100644 --- a/app/src/main/java/org/solovyev/android/calculator/history/EditHistoryFragment.java +++ b/app/src/main/java/org/solovyev/android/calculator/history/EditHistoryFragment.java @@ -1,9 +1,11 @@ package org.solovyev.android.calculator.history; import android.content.Context; +import android.content.DialogInterface; import android.os.Bundle; import android.support.annotation.NonNull; import android.support.annotation.Nullable; +import android.support.design.widget.TextInputLayout; import android.support.v4.app.FragmentManager; import android.support.v7.app.AlertDialog; import android.view.LayoutInflater; @@ -11,27 +13,41 @@ import android.view.View; import android.widget.EditText; import android.widget.TextView; +import org.solovyev.android.calculator.AppComponent; import org.solovyev.android.calculator.BaseDialogFragment; import org.solovyev.android.calculator.R; +import javax.inject.Inject; + import butterknife.Bind; import butterknife.ButterKnife; public class EditHistoryFragment extends BaseDialogFragment { public static final String ARG_STATE = "state"; + public static final String ARG_NEW = "new"; - @Bind(R.id.history_edit_expression) + @Inject + History history; + + HistoryState state; + + boolean newState; + + @Bind(R.id.history_expression) TextView expressionView; - @Bind(R.id.history_edit_comment) + @Bind(R.id.history_comment) EditText commentView; - private HistoryState state; - public static void show(@NonNull HistoryState state, @NonNull FragmentManager fm) { + @Bind(R.id.history_comment_label) + TextInputLayout commentLabel; + + public static void show(@NonNull HistoryState state, boolean newState, @NonNull FragmentManager fm) { final EditHistoryFragment fragment = new EditHistoryFragment(); final Bundle args = new Bundle(); args.putParcelable(ARG_STATE, state); + args.putBoolean(ARG_NEW, newState); fragment.setArguments(args); fragment.show(fm, "edit-history-fragment"); } @@ -39,12 +55,40 @@ public class EditHistoryFragment extends BaseDialogFragment { @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); - state = getArguments().getParcelable(ARG_STATE); + final Bundle arguments = getArguments(); + state = arguments.getParcelable(ARG_STATE); + newState = arguments.getBoolean(ARG_NEW); + } + + @Override + protected void inject(@NonNull AppComponent component) { + super.inject(component); + component.inject(this); + } + + @NonNull + @Override + public AlertDialog onCreateDialog(Bundle savedInstanceState) { + final AlertDialog dialog = super.onCreateDialog(savedInstanceState); + dialog.setCanceledOnTouchOutside(false); + return dialog; } @Override protected void onPrepareDialog(@NonNull AlertDialog.Builder builder) { - + builder.setTitle(newState ? R.string.c_save_history : R.string.c_edit_history); + builder.setNegativeButton(R.string.c_cancel, null); + builder.setPositiveButton(R.string.c_save, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + final HistoryState.Builder b = HistoryState.builder(state) + .withComment(commentView.getText().toString()); + if (newState) { + b.withNowTime(); + } + history.updateSaved(b.build()); + } + }); } @NonNull diff --git a/app/src/main/java/org/solovyev/android/calculator/history/History.java b/app/src/main/java/org/solovyev/android/calculator/history/History.java index 46e67d2b..500bc194 100644 --- a/app/src/main/java/org/solovyev/android/calculator/history/History.java +++ b/app/src/main/java/org/solovyev/android/calculator/history/History.java @@ -110,7 +110,7 @@ public class History { final String editorText = oldEditor.getText(); final EditorState editor = EditorState.create(Strings.nullToEmpty(editorText), oldEditor.getCursorPosition()); final DisplayState display = DisplayState.createValid(oldDisplay.getJsclOperation(), null, Strings.nullToEmpty(oldDisplay.getEditorState().getText()), EditorState.NO_SEQUENCE); - states.add(HistoryState.newBuilder(editor, display).withTime(state.getTime()).withComment(state.getComment()).build()); + states.add(HistoryState.builder(editor, display).withTime(state.getTime()).withComment(state.getComment()).build()); } return states; } @@ -249,9 +249,14 @@ public class History { onRecentChanged(); } - public void addSaved(@Nonnull HistoryState state) { + public void updateSaved(@Nonnull HistoryState state) { Check.isMainThread(); - saved.add(state); + final int i = saved.indexOf(state); + if(i >= 0) { + saved.set(i, state); + } else { + saved.add(state); + } onSavedChanged(); } @@ -358,7 +363,7 @@ public class History { if (editorState.sequence != displayState.sequence) { return; } - addRecent(HistoryState.newBuilder(editorState, displayState).build()); + addRecent(HistoryState.builder(editorState, displayState).build()); } public static class ChangedEvent { diff --git a/app/src/main/java/org/solovyev/android/calculator/history/HistoryState.java b/app/src/main/java/org/solovyev/android/calculator/history/HistoryState.java index a6aeeceb..59022780 100644 --- a/app/src/main/java/org/solovyev/android/calculator/history/HistoryState.java +++ b/app/src/main/java/org/solovyev/android/calculator/history/HistoryState.java @@ -32,6 +32,7 @@ public class HistoryState implements Parcelable { private static final String JSON_DISPLAY = "d"; private static final String JSON_TIME = "t"; private static final String JSON_COMMENT = "c"; + public final int id; @Nonnull public final EditorState editor; @Nonnull @@ -41,10 +42,19 @@ public class HistoryState implements Parcelable { protected String comment = ""; private HistoryState(@Nonnull EditorState editor, @Nonnull DisplayState display) { + this.id = System.identityHashCode(this); this.editor = editor; this.display = display; } + private HistoryState(@Nonnull HistoryState state) { + this.id = state.id; + this.editor = state.editor; + this.display = state.display; + this.time = state.time; + this.comment = state.comment; + } + private HistoryState(@Nonnull JSONObject json) throws JSONException { this(EditorState.create(json.getJSONObject(JSON_EDITOR)), DisplayState.create(json.getJSONObject(JSON_DISPLAY))); this.time = json.optLong(JSON_TIME, 0L); @@ -52,6 +62,7 @@ public class HistoryState implements Parcelable { } private HistoryState(Parcel in) { + id = in.readInt(); editor = in.readParcelable(EditorState.class.getClassLoader()); display = in.readParcelable(DisplayState.class.getClassLoader()); time = in.readLong(); @@ -59,10 +70,15 @@ public class HistoryState implements Parcelable { } @Nonnull - public static Builder newBuilder(@Nonnull EditorState editor, @Nonnull DisplayState display) { + public static Builder builder(@Nonnull EditorState editor, @Nonnull DisplayState display) { return new Builder(editor, display); } + @Nonnull + public static Builder builder(@Nonnull HistoryState state) { + return new Builder(state); + } + @Nonnull public static HistoryState create(@Nonnull JSONObject json) throws JSONException { return new HistoryState(json); @@ -103,10 +119,27 @@ public class HistoryState implements Parcelable { return this.editor.same(that.editor) && this.display.same(that.display); } + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + final HistoryState that = (HistoryState) o; + + return id == that.id; + + } + + @Override + public int hashCode() { + return id; + } + @Override public String toString() { return "HistoryState{" + - "editor=" + editor + + "id=" + id + + ", editor=" + editor + ", display=" + display + ", time=" + time + ", comment='" + comment + '\'' + @@ -120,6 +153,7 @@ public class HistoryState implements Parcelable { @Override public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(id); dest.writeParcelable(editor, flags); dest.writeParcelable(display, flags); dest.writeLong(time); @@ -133,7 +167,16 @@ public class HistoryState implements Parcelable { private Builder(@Nonnull EditorState editor, @Nonnull DisplayState display) { super(editor, display); - withTime(System.currentTimeMillis()); + withNowTime(); + } + + private Builder(@Nonnull HistoryState state) { + super(state); + } + + @Nonnull + public Builder withNowTime() { + return withTime(System.currentTimeMillis()); } @Nonnull diff --git a/app/src/main/res/layout/history_edit.xml b/app/src/main/res/layout/history_edit.xml index 50dfad3d..79206315 100644 --- a/app/src/main/res/layout/history_edit.xml +++ b/app/src/main/res/layout/history_edit.xml @@ -22,45 +22,41 @@ ~ Site: http://se.solovyev.org --> - + a:layout_height="wrap_content" + a:orientation="vertical"> - - - - - - - + a:layout_height="wrap_content"> + + + + + + a:minLines="4" /> + - - - + diff --git a/app/src/main/res/values/theme.xml b/app/src/main/res/values/theme.xml index 55897b2c..d7d76ee6 100644 --- a/app/src/main/res/values/theme.xml +++ b/app/src/main/res/values/theme.xml @@ -24,18 +24,12 @@ @@ -171,7 +123,6 @@ true true false - false @color/cpp_wizard_button_selector_light diff --git a/app/src/test/java/org/solovyev/android/calculator/history/HistoryTest.java b/app/src/test/java/org/solovyev/android/calculator/history/HistoryTest.java index e95e700e..31b33034 100644 --- a/app/src/test/java/org/solovyev/android/calculator/history/HistoryTest.java +++ b/app/src/test/java/org/solovyev/android/calculator/history/HistoryTest.java @@ -143,7 +143,7 @@ public class HistoryTest { } private void addState(@Nonnull String text) { - history.addRecent(HistoryState.newBuilder(EditorState.create(text, 3), DisplayState.empty())); + history.addRecent(HistoryState.builder(EditorState.create(text, 3), DisplayState.empty())); } private static final String oldXml1 = "\n" +