Fix losing widget state on restart
This commit is contained in:
parent
4a8c0b7a2d
commit
ea21bbe811
@ -26,7 +26,7 @@ import android.os.Parcel;
|
|||||||
import android.os.Parcelable;
|
import android.os.Parcelable;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.view.ContextMenu;
|
import android.view.ContextMenu;
|
||||||
import jscl.math.Generic;
|
|
||||||
import org.json.JSONException;
|
import org.json.JSONException;
|
||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
import org.solovyev.android.calculator.jscl.JsclOperation;
|
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.Nonnull;
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
import jscl.math.Generic;
|
||||||
|
|
||||||
public class DisplayState implements Parcelable, ContextMenu.ContextMenuInfo {
|
public class DisplayState implements Parcelable, ContextMenu.ContextMenuInfo {
|
||||||
|
|
||||||
public static final Creator<DisplayState> CREATOR = new Creator<DisplayState>() {
|
public static final Creator<DisplayState> CREATOR = new Creator<DisplayState>() {
|
||||||
@ -143,4 +145,8 @@ public class DisplayState implements Parcelable, ContextMenu.ContextMenuInfo {
|
|||||||
dest.writeString(text);
|
dest.writeString(text);
|
||||||
dest.writeByte((byte) (valid ? 1 : 0));
|
dest.writeByte((byte) (valid ? 1 : 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isEmpty() {
|
||||||
|
return valid && TextUtils.isEmpty(text);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,8 +4,11 @@ import android.content.BroadcastReceiver;
|
|||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.os.Vibrator;
|
import android.os.Vibrator;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
|
|
||||||
import org.solovyev.android.calculator.buttons.CppButton;
|
import org.solovyev.android.calculator.buttons.CppButton;
|
||||||
|
import org.solovyev.android.calculator.history.History;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
@ -19,6 +22,8 @@ public final class WidgetReceiver extends BroadcastReceiver {
|
|||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
Keyboard keyboard;
|
Keyboard keyboard;
|
||||||
|
@Inject
|
||||||
|
History history;
|
||||||
|
|
||||||
@Nonnull
|
@Nonnull
|
||||||
public static Intent newButtonClickedIntent(@Nonnull Context context, @Nonnull CppButton button) {
|
public static Intent newButtonClickedIntent(@Nonnull Context context, @Nonnull CppButton button) {
|
||||||
@ -43,9 +48,21 @@ public final class WidgetReceiver extends BroadcastReceiver {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!keyboard.buttonPressed(button.action)) {
|
if (history.isLoaded()) {
|
||||||
return;
|
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()) {
|
if (!keyboard.isVibrateOnKeypress()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -55,4 +72,21 @@ public final class WidgetReceiver extends BroadcastReceiver {
|
|||||||
}
|
}
|
||||||
vibrator.vibrate(10);
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -77,6 +77,9 @@ public class History {
|
|||||||
private final RecentHistory recent = new RecentHistory();
|
private final RecentHistory recent = new RecentHistory();
|
||||||
@Nonnull
|
@Nonnull
|
||||||
private final List<HistoryState> saved = new ArrayList<>();
|
private final List<HistoryState> saved = new ArrayList<>();
|
||||||
|
@Nonnull
|
||||||
|
private final List<Runnable> whenLoadedRunnables = new ArrayList<>();
|
||||||
|
private boolean loaded;
|
||||||
@Inject
|
@Inject
|
||||||
Application application;
|
Application application;
|
||||||
@Inject
|
@Inject
|
||||||
@ -217,15 +220,32 @@ public class History {
|
|||||||
handler.post(new Runnable() {
|
handler.post(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
Check.isTrue(recent.isEmpty());
|
onLoaded(recentStates, savedStates);
|
||||||
Check.isTrue(saved.isEmpty());
|
|
||||||
recent.addInitial(recentStates);
|
|
||||||
saved.addAll(savedStates);
|
|
||||||
editor.onHistoryLoaded(recent);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void onLoaded(@NonNull List<HistoryState> recentStates, @NonNull List<HistoryState> 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
|
@Nonnull
|
||||||
private List<HistoryState> tryLoadStates(@NonNull File file) {
|
private List<HistoryState> tryLoadStates(@NonNull File file) {
|
||||||
try {
|
try {
|
||||||
@ -238,6 +258,10 @@ public class History {
|
|||||||
|
|
||||||
public void addRecent(@Nonnull HistoryState state) {
|
public void addRecent(@Nonnull HistoryState state) {
|
||||||
Check.isMainThread();
|
Check.isMainThread();
|
||||||
|
if (recent.isEmpty() && state.isEmpty()) {
|
||||||
|
// don't add empty states to empty history
|
||||||
|
return;
|
||||||
|
}
|
||||||
recent.add(state);
|
recent.add(state);
|
||||||
onRecentChanged(new AddedEvent(state, true));
|
onRecentChanged(new AddedEvent(state, true));
|
||||||
}
|
}
|
||||||
@ -255,15 +279,23 @@ public class History {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void onRecentChanged(@Nonnull Object event) {
|
private void onRecentChanged(@Nonnull Object event) {
|
||||||
handler.removeCallbacks(writeRecent);
|
postRecentWrite();
|
||||||
handler.postDelayed(writeRecent, 5000);
|
|
||||||
bus.post(event);
|
bus.post(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void postRecentWrite() {
|
||||||
|
handler.removeCallbacks(writeRecent);
|
||||||
|
handler.postDelayed(writeRecent, 5000);
|
||||||
|
}
|
||||||
|
|
||||||
private void onSavedChanged(@Nonnull Object event) {
|
private void onSavedChanged(@Nonnull Object event) {
|
||||||
|
postSavedWrite();
|
||||||
|
bus.post(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void postSavedWrite() {
|
||||||
handler.removeCallbacks(writeSaved);
|
handler.removeCallbacks(writeSaved);
|
||||||
handler.postDelayed(writeSaved, 500);
|
handler.postDelayed(writeSaved, 500);
|
||||||
bus.post(event);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
@Nonnull
|
||||||
@ -359,6 +391,15 @@ public class History {
|
|||||||
addRecent(HistoryState.builder(editorState, displayState).build());
|
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 static class ClearedEvent {
|
||||||
public final boolean recent;
|
public final boolean recent;
|
||||||
|
|
||||||
@ -406,6 +447,9 @@ public class History {
|
|||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
Check.isMainThread();
|
Check.isMainThread();
|
||||||
|
if (!loaded) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
// don't need to save intermediate states, thus {@link History#getRecent}
|
// don't need to save intermediate states, thus {@link History#getRecent}
|
||||||
final List<HistoryState> states = recent ? getRecent(false) : getSaved();
|
final List<HistoryState> states = recent ? getRecent(false) : getSaved();
|
||||||
backgroundThread.execute(new Runnable() {
|
backgroundThread.execute(new Runnable() {
|
||||||
|
@ -3,6 +3,7 @@ package org.solovyev.android.calculator.history;
|
|||||||
import android.os.Parcel;
|
import android.os.Parcel;
|
||||||
import android.os.Parcelable;
|
import android.os.Parcelable;
|
||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
|
||||||
import org.json.JSONException;
|
import org.json.JSONException;
|
||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
@ -15,8 +16,6 @@ import org.solovyev.android.calculator.json.Jsonable;
|
|||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
import static android.text.TextUtils.isEmpty;
|
|
||||||
|
|
||||||
public class HistoryState implements Parcelable, Jsonable {
|
public class HistoryState implements Parcelable, Jsonable {
|
||||||
|
|
||||||
public static final Creator<HistoryState> CREATOR = new Creator<HistoryState>() {
|
public static final Creator<HistoryState> CREATOR = new Creator<HistoryState>() {
|
||||||
@ -103,7 +102,7 @@ public class HistoryState implements Parcelable, Jsonable {
|
|||||||
json.put(JSON_EDITOR, editor.toJson());
|
json.put(JSON_EDITOR, editor.toJson());
|
||||||
json.put(JSON_DISPLAY, display.toJson());
|
json.put(JSON_DISPLAY, display.toJson());
|
||||||
json.put(JSON_TIME, time);
|
json.put(JSON_TIME, time);
|
||||||
if (!isEmpty(comment)) {
|
if (!TextUtils.isEmpty(comment)) {
|
||||||
json.put(JSON_COMMENT, comment);
|
json.put(JSON_COMMENT, comment);
|
||||||
}
|
}
|
||||||
return json;
|
return json;
|
||||||
@ -173,6 +172,10 @@ public class HistoryState implements Parcelable, Jsonable {
|
|||||||
dest.writeString(comment);
|
dest.writeString(comment);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isEmpty() {
|
||||||
|
return display.isEmpty() && editor.isEmpty() && TextUtils.isEmpty(comment);
|
||||||
|
}
|
||||||
|
|
||||||
public static final class Builder {
|
public static final class Builder {
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
|
Loading…
Reference in New Issue
Block a user