Save history states

This commit is contained in:
serso 2016-01-12 14:26:29 +01:00
parent 5813f41f4c
commit 5c663e7b24
6 changed files with 90 additions and 13 deletions

View File

@ -70,6 +70,7 @@ import java.util.Arrays;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory; import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicInteger;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import javax.annotation.Nullable; import javax.annotation.Nullable;
@ -136,6 +137,15 @@ public final class App {
return new Thread(r, "Init"); return new Thread(r, "Init");
} }
}); });
@Nonnull
private static final Executor background = Executors.newFixedThreadPool(5, new ThreadFactory() {
@NonNull
private final AtomicInteger counter = new AtomicInteger();
@Override
public Thread newThread(@Nonnull Runnable r) {
return new Thread(r, "Background #" + counter.getAndIncrement());
}
});
private App() { private App() {
throw new AssertionError(); throw new AssertionError();
@ -271,6 +281,11 @@ public final class App {
return initThread; return initThread;
} }
@Nonnull
public static Executor getBackground() {
return background;
}
@Nonnull @Nonnull
public static Preferences.Gui.Theme getThemeFor(@Nonnull Context context) { public static Preferences.Gui.Theme getThemeFor(@Nonnull Context context) {
if (context instanceof CalculatorOnscreenService) { if (context instanceof CalculatorOnscreenService) {

View File

@ -105,4 +105,13 @@ public class DisplayState {
json.put(JSON_TEXT, text); json.put(JSON_TEXT, text);
return json; return json;
} }
@Override
public String toString() {
return "DisplayState{" +
"valid=" + valid +
", sequence=" + sequence +
", operation=" + operation +
'}';
}
} }

View File

@ -23,9 +23,11 @@
package org.solovyev.android.calculator; package org.solovyev.android.calculator;
import android.text.TextUtils; import android.text.TextUtils;
import org.json.JSONException; import org.json.JSONException;
import org.json.JSONObject; import org.json.JSONObject;
import org.solovyev.android.Check;
import java.util.concurrent.atomic.AtomicLong;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import javax.annotation.Nullable; import javax.annotation.Nullable;
@ -35,7 +37,7 @@ public class EditorState {
public static final long NO_SEQUENCE = -1; public static final long NO_SEQUENCE = -1;
private static final String JSON_TEXT = "t"; private static final String JSON_TEXT = "t";
private static final String JSON_SELECTION = "s"; private static final String JSON_SELECTION = "s";
private static long counter = NO_SEQUENCE + 1; private static AtomicLong counter = new AtomicLong(NO_SEQUENCE + 1);
public final long sequence; public final long sequence;
@Nonnull @Nonnull
@ -49,8 +51,7 @@ public class EditorState {
} }
private EditorState(@Nonnull CharSequence text, int selection) { private EditorState(@Nonnull CharSequence text, int selection) {
Check.isMainThread(); this.sequence = counter.getAndIncrement();
this.sequence = counter++;
this.text = text; this.text = text;
this.selection = selection; this.selection = selection;
} }
@ -91,11 +92,20 @@ public class EditorState {
return TextUtils.equals(text, that.text) && selection == that.selection; return TextUtils.equals(text, that.text) && selection == that.selection;
} }
@Override
public String toString() {
return "EditorState{" +
"sequence=" + sequence +
", text=" + text +
", selection=" + selection +
'}';
}
@Nonnull @Nonnull
public String toJson() throws JSONException { public JSONObject toJson() throws JSONException {
final JSONObject json = new JSONObject(); final JSONObject json = new JSONObject();
json.put(JSON_TEXT, getTextString()); json.put(JSON_TEXT, getTextString());
json.put(JSON_SELECTION, selection); json.put(JSON_SELECTION, selection);
return json.toString(); return json;
} }
} }

View File

@ -23,6 +23,7 @@
package org.solovyev.android.calculator.history; package org.solovyev.android.calculator.history;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.os.Handler;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.text.TextUtils; import android.text.TextUtils;
@ -54,10 +55,16 @@ import javax.annotation.Nullable;
public class History { public class History {
public static final String TAG = App.subTag("History"); public static final String TAG = App.subTag("History");
@NonNull
private final Runnable writeCurrent = new WriteTask(true);
@NonNull
private final Runnable writeSaved = new WriteTask(false);
@Nonnull @Nonnull
private final HistoryList current = new HistoryList(); private final HistoryList current = new HistoryList();
@Nonnull @Nonnull
private final List<HistoryState> saved = new ArrayList<>(); private final List<HistoryState> saved = new ArrayList<>();
@Nonnull
private final Handler handler = App.getHandler();
@Nullable @Nullable
private EditorState lastEditorState; private EditorState lastEditorState;
private boolean initialized; private boolean initialized;
@ -131,9 +138,11 @@ public class History {
migrateOldHistory(); migrateOldHistory();
final List<HistoryState> currentStates = loadStates(getCurrentHistoryFile()); final List<HistoryState> currentStates = loadStates(getCurrentHistoryFile());
final List<HistoryState> savedStates = loadStates(getSavedHistoryFile()); final List<HistoryState> savedStates = loadStates(getSavedHistoryFile());
App.getHandler().post(new Runnable() { handler.post(new Runnable() {
@Override @Override
public void run() { public void run() {
Check.isTrue(current.isEmpty());
Check.isTrue(saved.isEmpty());
current.addAll(currentStates); current.addAll(currentStates);
saved.addAll(savedStates); saved.addAll(savedStates);
initialized = true; initialized = true;
@ -155,11 +164,13 @@ public class History {
} }
private void onCurrentChanged() { private void onCurrentChanged() {
// todo serso: implement handler.removeCallbacks(writeCurrent);
handler.postDelayed(writeCurrent, 500);
} }
private void onSavedChanged() { private void onSavedChanged() {
// todo serso: implement handler.removeCallbacks(writeSaved);
handler.postDelayed(writeSaved, 500);
} }
@Nonnull @Nonnull
@ -184,10 +195,10 @@ public class History {
@Nonnull @Nonnull
public List<HistoryState> getSaved() { public List<HistoryState> getSaved() {
Check.isMainThread(); Check.isMainThread();
return Collections.unmodifiableList(saved); return new ArrayList<>(saved);
} }
private boolean isIntermediate(@Nonnull String newerText, private static boolean isIntermediate(@Nonnull String newerText,
@Nonnull String olderText) { @Nonnull String olderText) {
final int diff = newerText.length() - olderText.length(); final int diff = newerText.length() - olderText.length();
if (diff == 1) { if (diff == 1) {
@ -268,4 +279,27 @@ public class History {
addCurrent(HistoryState.newBuilder(lastEditorState, e.newState).build()); addCurrent(HistoryState.newBuilder(lastEditorState, e.newState).build());
lastEditorState = null; lastEditorState = null;
} }
private class WriteTask implements Runnable {
private final boolean current;
public WriteTask(boolean current) {
this.current = current;
}
@Override
public void run() {
Check.isMainThread();
// don't need to save intermediate states, thus {@link History#getCurrent}
final List<HistoryState> states = current ? getCurrent() : getSaved();
App.getBackground().execute(new Runnable() {
@Override
public void run() {
final File file = current ? getCurrentHistoryFile() : getSavedHistoryFile();
final JSONArray array = HistoryList.toJson(states);
FileSaver.save(file, array.toString());
}
});
}
}
} }

View File

@ -81,6 +81,16 @@ public class HistoryState {
return this.editor.same(that.editor) && this.display.same(that.display); return this.editor.same(that.editor) && this.display.same(that.display);
} }
@Override
public String toString() {
return "HistoryState{" +
"editor=" + editor +
", display=" + display +
", time=" + time +
", comment='" + comment + '\'' +
'}';
}
public static final class Builder extends HistoryState { public static final class Builder extends HistoryState {
private boolean built; private boolean built;

View File

@ -26,7 +26,6 @@ import org.solovyev.android.calculator.CalculatorFragmentType;
import org.solovyev.android.calculator.Locator; import org.solovyev.android.calculator.Locator;
import org.solovyev.android.calculator.R; import org.solovyev.android.calculator.R;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
@ -45,7 +44,7 @@ public class SavedHistoryFragment extends BaseHistoryFragment {
@Nonnull @Nonnull
@Override @Override
protected List<HistoryState> getHistoryItems() { protected List<HistoryState> getHistoryItems() {
return new ArrayList<>(Locator.getInstance().getHistory().getSaved()); return Locator.getInstance().getHistory().getSaved();
} }
@Override @Override