History tests

This commit is contained in:
serso
2016-01-13 23:09:07 +01:00
parent 1e8be31ab5
commit 6734bfeaa4
14 changed files with 295 additions and 337 deletions

View File

@@ -95,7 +95,6 @@ public final class App {
private static volatile Application application;
@Nonnull
private static Executor uiThreadExecutor;
private static volatile boolean initialized;
@Nonnull
private static SharedPreferences preferences;
@Nonnull
@@ -132,9 +131,6 @@ public final class App {
public static void init(@Nonnull CalculatorApplication application,
@Nonnull Languages languages) {
if (initialized) {
throw new IllegalStateException("Already initialized!");
}
App.application = application;
App.preferences = PreferenceManager.getDefaultSharedPreferences(application);
App.uiThreadExecutor = application.uiThread;
@@ -163,14 +159,6 @@ public final class App {
App.editor = application.editor;
App.display = application.display;
App.bus = application.bus;
App.initialized = true;
}
private static void checkInit() {
if (!initialized) {
throw new IllegalStateException("App should be initialized!");
}
}
/**

View File

@@ -1,11 +1,11 @@
package org.solovyev.android.calculator;
import dagger.Component;
import org.solovyev.android.calculator.history.BaseHistoryFragment;
import org.solovyev.android.calculator.onscreen.CalculatorOnscreenService;
import javax.inject.Singleton;
import dagger.Component;
@Singleton
@Component(modules = AppModule.class)
public interface AppComponent {
@@ -13,4 +13,5 @@ public interface AppComponent {
void inject(CalculatorEditorFragment fragment);
void inject(BaseUi ui);
void inject(CalculatorOnscreenService service);
void inject(BaseHistoryFragment fragment);
}

View File

@@ -6,21 +6,17 @@ import android.os.Handler;
import android.os.Looper;
import android.preference.PreferenceManager;
import android.support.annotation.NonNull;
import com.squareup.otto.Bus;
import dagger.Module;
import dagger.Provides;
import org.solovyev.android.UiThreadExecutor;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import javax.annotation.Nonnull;
import javax.inject.Named;
import javax.inject.Singleton;
import dagger.Module;
import dagger.Provides;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
@Module
public class AppModule {
@@ -43,6 +39,12 @@ public class AppModule {
return new Handler(Looper.getMainLooper());
}
@Provides
@Singleton
Application provideApplication() {
return application;
}
@Provides
@Singleton
Bus provideBus(Handler handler) {

View File

@@ -80,19 +80,6 @@ public enum CalculatorEventType {
engine_preferences_changed,
/*
**********************************************************************
*
* HISTORY
*
**********************************************************************
*/
// @Nonnull CalculatorHistoryState
history_state_added,
clear_history_requested,
/*
**********************************************************************
*

View File

@@ -30,43 +30,24 @@ import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.ListFragment;
import android.text.ClipboardManager;
import android.view.ContextMenu;
import android.view.LayoutInflater;
import android.view.MenuItem;
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 android.view.*;
import android.widget.*;
import com.melnykov.fab.FloatingActionButton;
import org.solovyev.android.calculator.App;
import org.solovyev.android.calculator.CalculatorActivity;
import org.solovyev.android.calculator.CalculatorEventData;
import org.solovyev.android.calculator.CalculatorEventListener;
import org.solovyev.android.calculator.CalculatorEventType;
import org.solovyev.android.calculator.CalculatorFragmentType;
import org.solovyev.android.calculator.FragmentUi;
import org.solovyev.android.calculator.Locator;
import org.solovyev.android.calculator.R;
import com.squareup.otto.Bus;
import com.squareup.otto.Subscribe;
import org.solovyev.android.calculator.*;
import org.solovyev.android.calculator.jscl.JsclOperation;
import org.solovyev.common.text.Strings;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.inject.Inject;
import java.util.ArrayList;
import java.util.List;
import static android.view.Menu.NONE;
import static org.solovyev.android.calculator.CalculatorEventType.clear_history_requested;
public abstract class BaseHistoryFragment extends ListFragment implements CalculatorEventListener {
public abstract class BaseHistoryFragment extends ListFragment {
@Nonnull
private final DialogInterface.OnClickListener clearDialogListener = new DialogInterface.OnClickListener() {
@@ -79,14 +60,16 @@ public abstract class BaseHistoryFragment extends ListFragment implements Calcul
}
}
};
@Inject
History history;
@Inject
Bus bus;
@Nonnull
private HistoryArrayAdapter adapter;
@Nonnull
private FragmentUi ui;
@Nullable
private AlertDialog clearDialog;
@Inject
History history;
protected BaseHistoryFragment(@Nonnull CalculatorFragmentType fragmentType) {
ui = new FragmentUi(fragmentType.getDefaultLayoutId(), fragmentType.getDefaultTitleResId(), false);
@@ -114,6 +97,8 @@ public abstract class BaseHistoryFragment extends ListFragment implements Calcul
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
((CalculatorApplication) getActivity().getApplication()).getComponent().inject(this);
bus.register(this);
ui.onCreate(this);
}
@@ -139,7 +124,12 @@ public abstract class BaseHistoryFragment extends ListFragment implements Calcul
fab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Locator.getInstance().getCalculator().fireCalculatorEvent(clear_history_requested, null);
clearDialog = new AlertDialog.Builder(getActivity()).setTitle(R.string.cpp_clear_history_title)
.setMessage(R.string.cpp_clear_history_message)
.setPositiveButton(R.string.cpp_clear_history, clearDialogListener)
.setNegativeButton(R.string.c_cancel, clearDialogListener)
.create();
clearDialog.show();
}
});
@@ -159,7 +149,7 @@ public abstract class BaseHistoryFragment extends ListFragment implements Calcul
public void onResume() {
super.onResume();
this.ui.onResume(this);
ui.onResume(this);
updateAdapter();
}
@@ -285,6 +275,7 @@ public abstract class BaseHistoryFragment extends ListFragment implements Calcul
@Override
public void onDestroy() {
bus.unregister(this);
if (clearDialog != null) {
clearDialog.dismiss();
clearDialog = null;
@@ -323,31 +314,13 @@ public abstract class BaseHistoryFragment extends ListFragment implements Calcul
return adapter;
}
@Override
public void onCalculatorEvent(@Nonnull CalculatorEventData calculatorEventData, @Nonnull CalculatorEventType calculatorEventType, @Nullable Object data) {
switch (calculatorEventType) {
case history_state_added:
getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
updateAdapter();
}
});
break;
case clear_history_requested:
getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
clearDialog = new AlertDialog.Builder(getActivity()).setTitle(R.string.cpp_clear_history_title)
.setMessage(R.string.cpp_clear_history_message)
.setPositiveButton(R.string.cpp_clear_history, clearDialogListener)
.setNegativeButton(R.string.c_cancel, clearDialogListener)
.create();
clearDialog.show();
}
});
break;
@Subscribe
void onHistoryChanged(@Nonnull History.ChangedEvent e) {
if (e.recent != isRecentHistory()) {
return;
}
updateAdapter();
}
protected abstract boolean isRecentHistory();
}

View File

@@ -22,30 +22,24 @@
package org.solovyev.android.calculator.history;
import android.app.Application;
import android.content.SharedPreferences;
import android.os.Handler;
import android.support.annotation.NonNull;
import android.text.TextUtils;
import com.google.common.base.Strings;
import com.squareup.otto.Bus;
import com.squareup.otto.Subscribe;
import org.json.JSONArray;
import org.json.JSONException;
import org.solovyev.android.Check;
import org.solovyev.android.calculator.App;
import org.solovyev.android.calculator.AppModule;
import org.solovyev.android.calculator.CalculatorEventType;
import org.solovyev.android.calculator.Display;
import org.solovyev.android.calculator.DisplayState;
import org.solovyev.android.calculator.Editor;
import org.solovyev.android.calculator.EditorState;
import org.solovyev.android.calculator.Locator;
import org.solovyev.android.calculator.model.AndroidCalculatorEngine;
import org.solovyev.android.calculator.*;
import org.solovyev.android.io.FileLoader;
import org.solovyev.android.io.FileSaver;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.inject.Inject;
import javax.inject.Named;
import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
@@ -53,16 +47,13 @@ import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.Executor;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.inject.Inject;
import javax.inject.Named;
import static java.lang.Character.isDigit;
import static android.text.TextUtils.isEmpty;
public class History {
public static final String TAG = App.subTag("History");
private static final ChangedEvent CHANGED_EVENT_RECENT = new ChangedEvent(true);
private static final ChangedEvent CHANGED_EVENT_SAVED = new ChangedEvent(false);
@NonNull
private final Runnable writeRecent = new WriteTask(true);
@NonNull
@@ -71,17 +62,22 @@ public class History {
private final RecentHistory recent = new RecentHistory();
@Nonnull
private final List<HistoryState> saved = new ArrayList<>();
@Nonnull
private final Bus bus;
@Inject
Handler handler;
@Inject
SharedPreferences preferences;
@Nullable
private EditorState lastEditorState;
@Inject
Editor editor;
@Inject
Application application;
private boolean initialized;
@Inject
public History(Bus bus, @Named(AppModule.THREAD_INIT) Executor initThread) {
bus.register(this);
public History(@NonNull Bus bus, @Nonnull @Named(AppModule.THREAD_INIT) Executor initThread) {
this.bus = bus;
this.bus.register(this);
initThread.execute(new Runnable() {
@Override
public void run() {
@@ -90,23 +86,6 @@ public class History {
});
}
private void migrateOldHistory() {
try {
final String xml = preferences.getString("org.solovyev.android.calculator.CalculatorModel_history", null);
if (TextUtils.isEmpty(xml)) {
return;
}
final List<HistoryState> states = convertOldHistory(xml);
if (states == null) {
return;
}
final JSONArray json = RecentHistory.toJson(states);
FileSaver.save(getSavedHistoryFile(), json.toString());
} catch (Exception e) {
Locator.getInstance().getLogger().error(TAG, e.getMessage(), e);
}
}
@Nullable
static List<HistoryState> convertOldHistory(@NonNull String xml) {
final OldHistory history = OldHistory.fromXml(xml);
@@ -129,13 +108,13 @@ public class History {
}
@NonNull
private static File getSavedHistoryFile() {
return new File(App.getApplication().getFilesDir(), "history-saved.json");
private File getSavedHistoryFile() {
return new File(application.getFilesDir(), "history-saved.json");
}
@NonNull
private static File getRecentHistoryFile() {
return new File(App.getApplication().getFilesDir(), "history-recent.json");
private File getRecentHistoryFile() {
return new File(application.getFilesDir(), "history-recent.json");
}
@Nonnull
@@ -144,7 +123,7 @@ public class History {
return Collections.emptyList();
}
final CharSequence json = FileLoader.load(file);
if (TextUtils.isEmpty(json)) {
if (isEmpty(json)) {
return Collections.emptyList();
}
try {
@@ -155,6 +134,44 @@ public class History {
return Collections.emptyList();
}
private static boolean isIntermediate(@Nonnull String olderText,
@Nonnull String newerText) {
if (isEmpty(olderText)) {
return true;
}
if (isEmpty(newerText)) {
return false;
}
final int diff = newerText.length() - olderText.length();
if (diff >= 1) {
return newerText.startsWith(olderText);
} else if (diff <= 1) {
return olderText.startsWith(newerText);
} else if (diff == 0) {
return olderText.equals(newerText);
}
return false;
}
private void migrateOldHistory() {
try {
final String xml = preferences.getString("org.solovyev.android.calculator.CalculatorModel_history", null);
if (isEmpty(xml)) {
return;
}
final List<HistoryState> states = convertOldHistory(xml);
if (states == null) {
return;
}
final JSONArray json = RecentHistory.toJson(states);
FileSaver.save(getSavedHistoryFile(), json.toString());
} catch (Exception e) {
Locator.getInstance().getLogger().error(TAG, e.getMessage(), e);
}
}
private void init() {
Check.isNotMainThread();
migrateOldHistory();
@@ -175,7 +192,6 @@ public class History {
public void addRecent(@Nonnull HistoryState state) {
Check.isMainThread();
recent.add(state);
Locator.getInstance().getCalculator().fireCalculatorEvent(CalculatorEventType.history_state_added, state);
onRecentChanged();
}
@@ -188,11 +204,13 @@ public class History {
private void onRecentChanged() {
handler.removeCallbacks(writeRecent);
handler.postDelayed(writeRecent, 500);
bus.post(CHANGED_EVENT_RECENT);
}
private void onSavedChanged() {
handler.removeCallbacks(writeSaved);
handler.postDelayed(writeSaved, 500);
bus.post(CHANGED_EVENT_SAVED);
}
@Nonnull
@@ -201,8 +219,6 @@ public class History {
final List<HistoryState> result = new LinkedList<>();
final String groupingSeparator = AndroidCalculatorEngine.Preferences.groupingSeparator.getPreference(App.getPreferences());
final List<HistoryState> states = recent.asList();
final int statesCount = states.size();
for (int i = 1; i < statesCount; i++) {
@@ -210,13 +226,14 @@ public class History {
final HistoryState newerState = states.get(i);
final String olderText = olderState.editor.getTextString();
final String newerText = newerState.editor.getTextString();
if (!isIntermediate(olderText, newerText, groupingSeparator)) {
if (!isIntermediate(olderText, newerText)) {
result.add(0, olderState);
}
}
if (statesCount > 0) {
// try add last state if not empty
final HistoryState state = states.get(statesCount - 1);
if (!TextUtils.isEmpty(state.editor.getTextString())) {
if (!isEmpty(state.editor.getTextString())) {
result.add(0, state);
}
}
@@ -229,53 +246,6 @@ public class History {
return new ArrayList<>(saved);
}
private static boolean isIntermediate(@Nonnull String olderText,
@Nonnull String newerText,
@NonNull String groupingSeparator) {
if (TextUtils.isEmpty(olderText)) {
return true;
}
if (TextUtils.isEmpty(newerText)) {
return false;
}
olderText = trimGroupingSeparators(olderText, groupingSeparator);
newerText = trimGroupingSeparators(newerText, groupingSeparator);
final int diff = newerText.length() - olderText.length();
if (diff >= 1) {
return newerText.startsWith(olderText);
} else if (diff <= 1) {
return olderText.startsWith(newerText);
} else if (diff == 0) {
return olderText.equals(newerText);
}
return false;
}
@NonNull
private static String trimGroupingSeparators(@NonNull String text, @NonNull String groupingSeparator) {
if (TextUtils.isEmpty(groupingSeparator)) {
return text;
}
Check.isTrue(groupingSeparator.length() == 1);
final StringBuilder sb = new StringBuilder(text.length());
for (int i = 0; i < text.length(); i++) {
if (i == 0 || i == text.length() - 1) {
// grouping separator can't be the first and the last character
sb.append(text.charAt(i));
continue;
}
if (isDigit(text.charAt(i - 1)) && text.charAt(i) == groupingSeparator.charAt(0) && isDigit(text.charAt(i + 1))) {
// grouping separator => skip
continue;
}
sb.append(text.charAt(i));
}
return sb.toString();
}
public void clearRecent() {
Check.isMainThread();
recent.clear();
@@ -321,27 +291,25 @@ public class History {
onRecentChanged();
}
@Subscribe
public void onEditorChanged(@Nonnull Editor.ChangedEvent e) {
if (!initialized) {
return;
}
lastEditorState = e.newState;
}
@Subscribe
public void onDisplayChanged(@Nonnull Display.ChangedEvent e) {
if (!initialized) {
return;
}
if (lastEditorState == null) {
final EditorState editorState = editor.getState();
final DisplayState displayState = e.newState;
if (editorState.sequence != displayState.sequence) {
return;
}
if (lastEditorState.sequence != e.newState.sequence) {
return;
addRecent(HistoryState.newBuilder(editorState, displayState).build());
}
public static class ChangedEvent {
public final boolean recent;
public ChangedEvent(boolean recent) {
this.recent = recent;
}
addRecent(HistoryState.newBuilder(lastEditorState, e.newState).build());
lastEditorState = null;
}
private class WriteTask implements Runnable {

View File

@@ -25,9 +25,8 @@ package org.solovyev.android.calculator.history;
import org.solovyev.android.calculator.CalculatorFragmentType;
import org.solovyev.android.calculator.R;
import java.util.List;
import javax.annotation.Nonnull;
import java.util.List;
public class RecentHistoryFragment extends BaseHistoryFragment {
@@ -51,4 +50,9 @@ public class RecentHistoryFragment extends BaseHistoryFragment {
history.clearRecent();
getAdapter().clear();
}
@Override
protected boolean isRecentHistory() {
return true;
}
}

View File

@@ -25,9 +25,8 @@ package org.solovyev.android.calculator.history;
import org.solovyev.android.calculator.CalculatorFragmentType;
import org.solovyev.android.calculator.R;
import java.util.List;
import javax.annotation.Nonnull;
import java.util.List;
public class SavedHistoryFragment extends BaseHistoryFragment {
@@ -51,4 +50,9 @@ public class SavedHistoryFragment extends BaseHistoryFragment {
history.clearSaved();
getAdapter().clear();
}
@Override
protected boolean isRecentHistory() {
return false;
}
}