From ea21bbe81117eeb191117b10db5632e06dc29e96 Mon Sep 17 00:00:00 2001 From: serso Date: Tue, 1 Mar 2016 14:01:59 +0100 Subject: [PATCH] Fix losing widget state on restart --- .../android/calculator/DisplayState.java | 8 ++- .../android/calculator/WidgetReceiver.java | 38 +++++++++++- .../android/calculator/history/History.java | 60 ++++++++++++++++--- .../calculator/history/HistoryState.java | 9 ++- 4 files changed, 101 insertions(+), 14 deletions(-) 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 2a2297f7..213dfe39 100644 --- a/app/src/main/java/org/solovyev/android/calculator/DisplayState.java +++ b/app/src/main/java/org/solovyev/android/calculator/DisplayState.java @@ -26,7 +26,7 @@ import android.os.Parcel; import android.os.Parcelable; import android.text.TextUtils; import android.view.ContextMenu; -import jscl.math.Generic; + import org.json.JSONException; import org.json.JSONObject; import org.solovyev.android.calculator.jscl.JsclOperation; @@ -34,6 +34,8 @@ import org.solovyev.android.calculator.jscl.JsclOperation; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import jscl.math.Generic; + public class DisplayState implements Parcelable, ContextMenu.ContextMenuInfo { public static final Creator CREATOR = new Creator() { @@ -143,4 +145,8 @@ public class DisplayState implements Parcelable, ContextMenu.ContextMenuInfo { dest.writeString(text); dest.writeByte((byte) (valid ? 1 : 0)); } + + public boolean isEmpty() { + return valid && TextUtils.isEmpty(text); + } } diff --git a/app/src/main/java/org/solovyev/android/calculator/WidgetReceiver.java b/app/src/main/java/org/solovyev/android/calculator/WidgetReceiver.java index 46c275c6..8c7ab933 100644 --- a/app/src/main/java/org/solovyev/android/calculator/WidgetReceiver.java +++ b/app/src/main/java/org/solovyev/android/calculator/WidgetReceiver.java @@ -4,8 +4,11 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.os.Vibrator; +import android.support.annotation.NonNull; import android.text.TextUtils; + import org.solovyev.android.calculator.buttons.CppButton; +import org.solovyev.android.calculator.history.History; import javax.annotation.Nonnull; import javax.inject.Inject; @@ -19,6 +22,8 @@ public final class WidgetReceiver extends BroadcastReceiver { @Inject Keyboard keyboard; + @Inject + History history; @Nonnull public static Intent newButtonClickedIntent(@Nonnull Context context, @Nonnull CppButton button) { @@ -43,9 +48,21 @@ public final class WidgetReceiver extends BroadcastReceiver { return; } - if (!keyboard.buttonPressed(button.action)) { - return; + if (history.isLoaded()) { + if (!keyboard.buttonPressed(button.action)) { + // prevent vibrate + return; + } + } else { + // if app has been killed we need first to restore the state and only after doing this + // to apply actions. Otherwise, we will apply actions on the empty editor + history.runWhenLoaded(new MyRunnable(keyboard, button.action)); } + + vibrate(context); + } + + private void vibrate(@NonNull Context context) { if (!keyboard.isVibrateOnKeypress()) { return; } @@ -55,4 +72,21 @@ public final class WidgetReceiver extends BroadcastReceiver { } vibrator.vibrate(10); } + + private static class MyRunnable implements Runnable { + @NonNull + private final Keyboard keyboard; + @NonNull + private final String action; + + public MyRunnable(@NonNull Keyboard keyboard, @NonNull String action) { + this.keyboard = keyboard; + this.action = action; + } + + @Override + public void run() { + keyboard.buttonPressed(action); + } + } } 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 95375dff..9bd614b8 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 @@ -77,6 +77,9 @@ public class History { private final RecentHistory recent = new RecentHistory(); @Nonnull private final List saved = new ArrayList<>(); + @Nonnull + private final List whenLoadedRunnables = new ArrayList<>(); + private boolean loaded; @Inject Application application; @Inject @@ -217,15 +220,32 @@ public class History { handler.post(new Runnable() { @Override public void run() { - Check.isTrue(recent.isEmpty()); - Check.isTrue(saved.isEmpty()); - recent.addInitial(recentStates); - saved.addAll(savedStates); - editor.onHistoryLoaded(recent); + onLoaded(recentStates, savedStates); } }); } + private void onLoaded(@NonNull List recentStates, @NonNull List savedStates) { + Check.isTrue(saved.isEmpty()); + Check.isMainThread(); + final boolean wasEmpty = recent.isEmpty(); + recent.addInitial(recentStates); + saved.addAll(savedStates); + if (wasEmpty) { + // user has typed nothing while we were loading, let's use recent history to restore + // editor state + editor.onHistoryLoaded(recent); + } else { + // user has types something => we should schedule save + postRecentWrite(); + } + loaded = true; + for (Runnable runnable : whenLoadedRunnables) { + runnable.run(); + } + whenLoadedRunnables.clear(); + } + @Nonnull private List tryLoadStates(@NonNull File file) { try { @@ -238,6 +258,10 @@ public class History { public void addRecent(@Nonnull HistoryState state) { Check.isMainThread(); + if (recent.isEmpty() && state.isEmpty()) { + // don't add empty states to empty history + return; + } recent.add(state); onRecentChanged(new AddedEvent(state, true)); } @@ -255,15 +279,23 @@ public class History { } private void onRecentChanged(@Nonnull Object event) { - handler.removeCallbacks(writeRecent); - handler.postDelayed(writeRecent, 5000); + postRecentWrite(); bus.post(event); } + private void postRecentWrite() { + handler.removeCallbacks(writeRecent); + handler.postDelayed(writeRecent, 5000); + } + private void onSavedChanged(@Nonnull Object event) { + postSavedWrite(); + bus.post(event); + } + + private void postSavedWrite() { handler.removeCallbacks(writeSaved); handler.postDelayed(writeSaved, 500); - bus.post(event); } @Nonnull @@ -359,6 +391,15 @@ public class History { addRecent(HistoryState.builder(editorState, displayState).build()); } + public boolean isLoaded() { + return loaded; + } + + public void runWhenLoaded(@NonNull Runnable runnable) { + Check.isTrue(!loaded); + whenLoadedRunnables.add(runnable); + } + public static class ClearedEvent { public final boolean recent; @@ -406,6 +447,9 @@ public class History { @Override public void run() { Check.isMainThread(); + if (!loaded) { + return; + } // don't need to save intermediate states, thus {@link History#getRecent} final List states = recent ? getRecent(false) : getSaved(); backgroundThread.execute(new Runnable() { 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 77724eb1..9124d9a5 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 @@ -3,6 +3,7 @@ package org.solovyev.android.calculator.history; import android.os.Parcel; import android.os.Parcelable; import android.support.annotation.NonNull; +import android.text.TextUtils; import org.json.JSONException; import org.json.JSONObject; @@ -15,8 +16,6 @@ import org.solovyev.android.calculator.json.Jsonable; import javax.annotation.Nonnull; import javax.annotation.Nullable; -import static android.text.TextUtils.isEmpty; - public class HistoryState implements Parcelable, Jsonable { public static final Creator CREATOR = new Creator() { @@ -103,7 +102,7 @@ public class HistoryState implements Parcelable, Jsonable { json.put(JSON_EDITOR, editor.toJson()); json.put(JSON_DISPLAY, display.toJson()); json.put(JSON_TIME, time); - if (!isEmpty(comment)) { + if (!TextUtils.isEmpty(comment)) { json.put(JSON_COMMENT, comment); } return json; @@ -173,6 +172,10 @@ public class HistoryState implements Parcelable, Jsonable { dest.writeString(comment); } + public boolean isEmpty() { + return display.isEmpty() && editor.isEmpty() && TextUtils.isEmpty(comment); + } + public static final class Builder { @NonNull