This commit is contained in:
serso
2016-01-16 15:32:21 +01:00
parent 4323c688c5
commit 45a71817c8
35 changed files with 384 additions and 354 deletions

View File

@@ -50,8 +50,8 @@ public enum CalculatorFragmentType {
editor(CalculatorEditorFragment.class, R.layout.cpp_app_editor, R.string.editor),
//display(CalculatorHistoryFragment.class, "history", R.layout.fragment_history, R.string.c_history),
//keyboard(CalculatorHistoryFragment.class, "history", R.layout.fragment_history, R.string.c_history),
history(RecentHistoryFragment.class, R.layout.fragment_history, R.string.c_history),
saved_history(SavedHistoryFragment.class, R.layout.fragment_history, R.string.c_saved_history),
history(RecentHistoryFragment.class, R.layout.fragment_history, R.string.cpp_history_tab_recent),
saved_history(SavedHistoryFragment.class, R.layout.fragment_history, R.string.cpp_history_tab_saved),
variables(CalculatorVarsFragment.class, R.layout.vars_fragment, R.string.c_vars),
functions(CalculatorFunctionsFragment.class, R.layout.math_entities_fragment, R.string.c_functions),
operators(CalculatorOperatorsFragment.class, R.layout.math_entities_fragment, R.string.c_operators),

View File

@@ -26,36 +26,53 @@ import android.app.Activity;
import android.content.Context;
import android.content.DialogInterface;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.StringRes;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.ListFragment;
import android.support.v7.app.AlertDialog;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.text.ClipboardManager;
import android.text.format.DateUtils;
import android.view.*;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.TextView;
import butterknife.Bind;
import butterknife.ButterKnife;
import com.melnykov.fab.FloatingActionButton;
import com.squareup.otto.Bus;
import com.squareup.otto.Subscribe;
import org.solovyev.android.Check;
import org.solovyev.android.calculator.*;
import org.solovyev.android.calculator.jscl.JsclOperation;
import org.solovyev.android.views.llm.DividerItemDecoration;
import org.solovyev.common.text.Strings;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.inject.Inject;
import java.util.ArrayList;
import java.util.List;
public abstract class BaseHistoryFragment extends ListFragment {
import static android.view.Menu.NONE;
import static android.view.View.GONE;
import static android.view.View.VISIBLE;
public abstract class BaseHistoryFragment extends Fragment {
private final boolean recentHistory;
@Inject
History history;
@Inject
Bus bus;
private HistoryArrayAdapter adapter;
@Bind(R.id.history_recyclerview)
RecyclerView recyclerView;
@Bind(R.id.history_fab)
FloatingActionButton fab;
@Nonnull
private FragmentUi ui;
private HistoryAdapter adapter;
protected BaseHistoryFragment(@Nonnull CalculatorFragmentType type) {
recentHistory = type == CalculatorFragmentType.history;
ui = new FragmentUi(type.getDefaultLayoutId(), type.getDefaultTitleResId(), false);
}
@@ -81,46 +98,33 @@ public abstract class BaseHistoryFragment extends ListFragment {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
((CalculatorApplication) getActivity().getApplication()).getComponent().inject(this);
bus.register(this);
ui.onCreate(this);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return ui.onCreateView(this, inflater, container);
}
@Override
public void onViewCreated(View root, Bundle savedInstanceState) {
super.onViewCreated(root, savedInstanceState);
ui.onViewCreated(this, root);
adapter = new HistoryArrayAdapter(this.getActivity(), getItemLayoutId(), R.id.history_item, new ArrayList<HistoryState>());
setListAdapter(adapter);
final ListView lv = getListView();
lv.setTextFilterEnabled(true);
final FloatingActionButton fab = (FloatingActionButton) root.findViewById(R.id.fab);
fab.attachToListView(lv);
final View view = ui.onCreateView(this, inflater, container);
ButterKnife.bind(this, view);
final Context context = inflater.getContext();
adapter = new HistoryAdapter(context);
bus.register(adapter);
recyclerView.setLayoutManager(new LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false));
recyclerView.setAdapter(adapter);
recyclerView.addItemDecoration(new DividerItemDecoration(context, null));
fab.attachToRecyclerView(recyclerView);
fab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
showClearHistoryDialog();
}
});
return view;
}
lv.setOnItemClickListener(new AdapterView.OnItemClickListener() {
public void onItemClick(final AdapterView<?> parent,
final View view,
final int position,
final long id) {
useState((HistoryState) parent.getItemAtPosition(position));
}
});
registerForContextMenu(lv);
@Override
public void onViewCreated(View root, Bundle savedInstanceState) {
super.onViewCreated(root, savedInstanceState);
ui.onViewCreated(this, root);
}
private void showClearHistoryDialog() {
@@ -130,7 +134,11 @@ public abstract class BaseHistoryFragment extends ListFragment {
.setPositiveButton(R.string.cpp_clear_history, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
clearHistory();
if (recentHistory) {
history.clearRecent();
} else {
history.clearSaved();
}
}
})
.setNegativeButton(R.string.c_cancel, null)
@@ -142,33 +150,8 @@ public abstract class BaseHistoryFragment extends ListFragment {
public void onResume() {
super.onResume();
ui.onResume(this);
updateAdapter();
}
@Override
public final void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
super.onCreateContextMenu(menu, v, menuInfo);
final AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo;
final HistoryState state = (HistoryState) getListView().getItemAtPosition(info.position);
onCreateContextMenu(menu, state);
}
protected abstract void onCreateContextMenu(@Nonnull ContextMenu menu, @Nonnull HistoryState state);
@Override
public final boolean onContextItemSelected(MenuItem item) {
final AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo();
final HistoryState state = (HistoryState) getListView().getItemAtPosition(info.position);
if (onContextItemSelected(item, state)) {
return true;
}
return super.onContextItemSelected(item);
}
protected abstract boolean onContextItemSelected(@Nonnull MenuItem item, @Nonnull HistoryState state);
@SuppressWarnings("deprecation")
protected final void copyResult(@Nonnull HistoryState state) {
final Context context = getActivity();
@@ -203,53 +186,188 @@ public abstract class BaseHistoryFragment extends ListFragment {
@Override
public void onDestroyView() {
bus.unregister(adapter);
ui.onDestroyView(this);
super.onDestroyView();
}
@Override
public void onDestroy() {
bus.unregister(this);
ui.onDestroy(this);
super.onDestroy();
}
protected abstract int getItemLayoutId();
public class HistoryViewHolder extends RecyclerView.ViewHolder implements View.OnCreateContextMenuListener, View.OnClickListener, MenuItem.OnMenuItemClickListener {
private void updateAdapter() {
final List<HistoryState> historyList = getHistoryItems();
private static final int DATETIME_FORMAT = DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_ABBREV_MONTH | DateUtils.FORMAT_ABBREV_TIME;
@Bind(R.id.history_item_value)
TextView valueView;
@Bind(R.id.history_item_comment)
TextView commentView;
@Bind(R.id.history_item_time)
TextView timeView;
@Nullable
private HistoryState state;
final ArrayAdapter<HistoryState> adapter = getAdapter();
try {
adapter.setNotifyOnChange(false);
adapter.clear();
for (HistoryState historyState : historyList) {
adapter.add(historyState);
public HistoryViewHolder(View view) {
super(view);
ButterKnife.bind(this, view);
view.setOnCreateContextMenuListener(this);
view.setOnClickListener(this);
}
void bind(@Nonnull HistoryState state) {
this.state = state;
valueView.setText(BaseHistoryFragment.getHistoryText(state));
timeView.setText(DateUtils.formatDateTime(getContext(), state.getTime(), DATETIME_FORMAT));
final String comment = state.getComment();
if (!Strings.isEmpty(comment)) {
commentView.setText(comment);
commentView.setVisibility(VISIBLE);
} else {
commentView.setText(null);
commentView.setVisibility(GONE);
}
} finally {
adapter.setNotifyOnChange(true);
}
adapter.notifyDataSetChanged();
}
@Nonnull
protected abstract List<HistoryState> getHistoryItems();
protected abstract void clearHistory();
@Nonnull
protected HistoryArrayAdapter getAdapter() {
return adapter;
}
@Subscribe
void onHistoryChanged(@Nonnull History.ChangedEvent e) {
if (e.recent != isRecentHistory()) {
return;
@Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
Check.isNotNull(state);
if (recentHistory) {
addMenu(menu, R.string.c_use);
addMenu(menu, R.string.c_copy_expression);
if (shouldHaveCopyResult(state)) {
addMenu(menu, R.string.c_copy_result);
}
addMenu(menu, R.string.c_save);
} else {
addMenu(menu, R.string.c_use);
addMenu(menu, R.string.c_copy_expression);
if (shouldHaveCopyResult(state)) {
addMenu(menu, R.string.c_copy_result);
}
addMenu(menu, R.string.c_edit);
addMenu(menu, R.string.c_remove);
}
}
@Nonnull
private MenuItem addMenu(@Nonnull ContextMenu menu, @StringRes int label) {
return menu.add(NONE, label, NONE, label).setOnMenuItemClickListener(this);
}
@Override
public void onClick(View v) {
Check.isNotNull(state);
useState(state);
}
@Override
public boolean onMenuItemClick(MenuItem item) {
Check.isNotNull(state);
switch (item.getItemId()) {
case R.string.c_use:
useState(state);
return true;
case R.string.c_copy_expression:
copyExpression(state);
return true;
case R.string.c_copy_result:
copyResult(state);
return true;
case R.string.c_edit:
EditHistoryFragment.show(state, false, getFragmentManager());
return true;
case R.string.c_save:
EditHistoryFragment.show(state, true, getFragmentManager());
return true;
case R.string.c_remove:
history.removeSaved(state);
return true;
}
return false;
}
updateAdapter();
}
protected abstract boolean isRecentHistory();
public class HistoryAdapter extends RecyclerView.Adapter<HistoryViewHolder> {
@NonNull
private final LayoutInflater inflater;
@NonNull
private final List<HistoryState> list;
public HistoryAdapter(@NonNull Context context) {
inflater = LayoutInflater.from(context);
list = loadHistory();
setHasStableIds(true);
}
@NonNull
private List<HistoryState> loadHistory() {
return recentHistory ? history.getRecent() : history.getSaved();
}
@Override
public HistoryViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
return new HistoryViewHolder(inflater.inflate(R.layout.fragment_history_item, parent, false));
}
@Override
public void onBindViewHolder(HistoryViewHolder holder, int position) {
holder.bind(list.get(position));
}
@Override
public long getItemId(int position) {
return list.get(position).hashCode();
}
@Override
public int getItemCount() {
return list.size();
}
@Subscribe
public void onHistoryCleared(@Nonnull History.ClearedEvent e) {
if (e.recent != recentHistory) {
return;
}
list.clear();
notifyDataSetChanged();
}
@Subscribe
public void onHistoryAdded(@Nonnull History.AddedEvent e) {
if (e.recent != recentHistory) {
return;
}
list.add(e.state);
notifyItemInserted(0);
}
@Subscribe
public void onHistoryUpdated(@Nonnull History.UpdatedEvent e) {
if (e.recent != recentHistory) {
return;
}
final int i = list.indexOf(e.state);
if (i >= 0) {
list.set(i, e.state);
notifyItemChanged(i);
}
}
@Subscribe
public void onHistoryRemoved(@Nonnull History.RemovedEvent e) {
if (e.recent != recentHistory) {
return;
}
final int i = list.indexOf(e.state);
if (i >= 0) {
list.remove(i);
notifyItemRemoved(i);
}
}
}
}

View File

@@ -78,11 +78,8 @@ public class EditHistoryFragment extends BaseDialogFragment {
builder.setPositiveButton(R.string.c_save, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
final HistoryState.Builder b = HistoryState.builder(state)
final HistoryState.Builder b = HistoryState.builder(state, newState)
.withComment(commentView.getText().toString());
if (newState) {
b.withNowTime();
}
history.updateSaved(b.build());
}
});

View File

@@ -27,25 +27,22 @@ 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.Display;
import org.solovyev.android.calculator.DisplayState;
import org.solovyev.android.calculator.Editor;
import org.solovyev.android.calculator.EditorState;
import org.solovyev.android.calculator.ErrorReporter;
import org.solovyev.android.calculator.*;
import org.solovyev.android.calculator.model.AndroidCalculatorEngine.Preferences;
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 javax.inject.Singleton;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
@@ -54,12 +51,6 @@ 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 javax.inject.Singleton;
import static android.text.TextUtils.isEmpty;
@Singleton
@@ -67,8 +58,8 @@ public class History {
public static final String TAG = App.subTag("History");
public static final String OLD_HISTORY_PREFS_KEY = "org.solovyev.android.calculator.CalculatorModel_history";
private static final ChangedEvent CHANGED_EVENT_RECENT = new ChangedEvent(true);
private static final ChangedEvent CHANGED_EVENT_SAVED = new ChangedEvent(false);
private static final ClearedEvent CLEARED_EVENT_RECENT = new ClearedEvent(true);
private static final ClearedEvent CLEARED_EVENT_SAVED = new ClearedEvent(false);
private static final int MAX_INTERMEDIATE_STREAK = 5;
@NonNull
private final Runnable writeRecent = new WriteTask(true);
@@ -246,7 +237,7 @@ public class History {
public void addRecent(@Nonnull HistoryState state) {
Check.isMainThread();
recent.add(state);
onRecentChanged();
onRecentChanged(new AddedEvent(state, true));
}
public void updateSaved(@Nonnull HistoryState state) {
@@ -254,22 +245,23 @@ public class History {
final int i = saved.indexOf(state);
if(i >= 0) {
saved.set(i, state);
onSavedChanged(new UpdatedEvent(state, false));
} else {
saved.add(state);
onSavedChanged(new AddedEvent(state, false));
}
onSavedChanged();
}
private void onRecentChanged() {
private void onRecentChanged(@Nonnull Object event) {
handler.removeCallbacks(writeRecent);
handler.postDelayed(writeRecent, 500);
bus.post(CHANGED_EVENT_RECENT);
bus.post(event);
}
private void onSavedChanged() {
private void onSavedChanged(@Nonnull Object event) {
handler.removeCallbacks(writeSaved);
handler.postDelayed(writeSaved, 500);
bus.post(CHANGED_EVENT_SAVED);
bus.post(event);
}
@Nonnull
@@ -314,13 +306,13 @@ public class History {
public void clearRecent() {
Check.isMainThread();
recent.clear();
onRecentChanged();
onRecentChanged(CLEARED_EVENT_RECENT);
}
public void clearSaved() {
Check.isMainThread();
saved.clear();
onSavedChanged();
onSavedChanged(CLEARED_EVENT_SAVED);
}
public void undo() {
@@ -347,13 +339,13 @@ public class History {
public void removeSaved(@Nonnull HistoryState state) {
Check.isMainThread();
saved.remove(state);
onSavedChanged();
onSavedChanged(new RemovedEvent(state, false));
}
public void removeRecent(@Nonnull HistoryState state) {
Check.isMainThread();
recent.remove(state);
onRecentChanged();
onSavedChanged(new RemovedEvent(state, true));
}
@Subscribe
@@ -366,10 +358,39 @@ public class History {
addRecent(HistoryState.builder(editorState, displayState).build());
}
public static class ChangedEvent {
public static class ClearedEvent {
public final boolean recent;
public ChangedEvent(boolean recent) {
ClearedEvent(boolean recent) {
this.recent = recent;
}
}
public static class RemovedEvent extends StateEvent {
RemovedEvent(@Nonnull HistoryState state, boolean recent) {
super(state, recent);
}
}
public static class AddedEvent extends StateEvent {
AddedEvent(@Nonnull HistoryState state, boolean recent) {
super(state, recent);
}
}
public static class UpdatedEvent extends StateEvent {
UpdatedEvent(@Nonnull HistoryState state, boolean recent) {
super(state, recent);
}
}
public abstract static class StateEvent {
@Nonnull
public final HistoryState state;
public final boolean recent;
protected StateEvent(@Nonnull HistoryState state, boolean recent) {
this.state = state;
this.recent = recent;
}
}

View File

@@ -51,10 +51,10 @@ public class HistoryArrayAdapter extends ArrayAdapter<HistoryState> {
final HistoryState state = getItem(position);
final TextView time = (TextView) result.findViewById(R.id.history_time);
final TextView time = (TextView) result.findViewById(R.id.history_item_time);
time.setText(DateUtils.formatDateTime(getContext(), state.getTime(), DATETIME_FORMAT));
final TextView editor = (TextView) result.findViewById(R.id.history_item);
final TextView editor = (TextView) result.findViewById(R.id.history_item_value);
editor.setText(BaseHistoryFragment.getHistoryText(state));
final TextView commentView = (TextView) result.findViewById(R.id.history_item_comment);

View File

@@ -3,7 +3,6 @@ package org.solovyev.android.calculator.history;
import android.annotation.SuppressLint;
import android.os.Parcel;
import android.os.Parcelable;
import org.json.JSONException;
import org.json.JSONObject;
import org.solovyev.android.Check;
@@ -37,7 +36,7 @@ public class HistoryState implements Parcelable {
public final EditorState editor;
@Nonnull
public final DisplayState display;
protected long time;
protected long time = now();
@Nonnull
protected String comment = "";
@@ -47,8 +46,8 @@ public class HistoryState implements Parcelable {
this.display = display;
}
private HistoryState(@Nonnull HistoryState state) {
this.id = state.id;
private HistoryState(@Nonnull HistoryState state, boolean newState) {
this.id = newState ? System.identityHashCode(this) : state.id;
this.editor = state.editor;
this.display = state.display;
this.time = state.time;
@@ -75,8 +74,8 @@ public class HistoryState implements Parcelable {
}
@Nonnull
public static Builder builder(@Nonnull HistoryState state) {
return new Builder(state);
public static Builder builder(@Nonnull HistoryState state, boolean newState) {
return new Builder(state, newState);
}
@Nonnull
@@ -84,6 +83,10 @@ public class HistoryState implements Parcelable {
return new HistoryState(json);
}
private static long now() {
return System.currentTimeMillis();
}
@Nonnull
public JSONObject toJson() throws JSONException {
final JSONObject json = new JSONObject();
@@ -122,7 +125,7 @@ public class HistoryState implements Parcelable {
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
if (o == null || !(o instanceof HistoryState)) return false;
final HistoryState that = (HistoryState) o;
@@ -167,16 +170,13 @@ public class HistoryState implements Parcelable {
private Builder(@Nonnull EditorState editor, @Nonnull DisplayState display) {
super(editor, display);
withNowTime();
}
private Builder(@Nonnull HistoryState state) {
super(state);
}
@Nonnull
public Builder withNowTime() {
return withTime(System.currentTimeMillis());
private Builder(@Nonnull HistoryState state, boolean newState) {
super(state, newState);
if (newState) {
withTime(now());
}
}
@Nonnull

View File

@@ -37,53 +37,4 @@ public class RecentHistoryFragment extends BaseHistoryFragment {
public RecentHistoryFragment() {
super(CalculatorFragmentType.history);
}
@Override
protected int getItemLayoutId() {
return R.layout.fragment_history_item;
}
@Nonnull
@Override
protected List<HistoryState> getHistoryItems() {
return history.getRecent();
}
@Override
protected void clearHistory() {
history.clearRecent();
getAdapter().clear();
}
@Override
protected boolean isRecentHistory() {
return true;
}
protected void onCreateContextMenu(@Nonnull ContextMenu menu, @Nonnull HistoryState state) {
menu.add(NONE, R.string.c_use, NONE, R.string.c_use);
menu.add(NONE, R.string.c_copy_expression, NONE, R.string.c_copy_expression);
if (shouldHaveCopyResult(state)) {
menu.add(NONE, R.string.c_copy_result, NONE, R.string.c_copy_result);
}
menu.add(NONE, R.string.c_save, NONE, R.string.c_save);
}
protected boolean onContextItemSelected(@Nonnull MenuItem item, @Nonnull HistoryState state) {
switch (item.getItemId()) {
case R.string.c_use:
useState(state);
return true;
case R.string.c_copy_expression:
copyExpression(state);
return true;
case R.string.c_copy_result:
copyResult(state);
return true;
case R.string.c_save:
EditHistoryFragment.show(state, true, getFragmentManager());
return true;
}
return false;
}
}

View File

@@ -22,73 +22,11 @@
package org.solovyev.android.calculator.history;
import android.view.ContextMenu;
import android.view.MenuItem;
import org.solovyev.android.calculator.CalculatorFragmentType;
import org.solovyev.android.calculator.R;
import javax.annotation.Nonnull;
import java.util.List;
import static android.view.Menu.NONE;
public class SavedHistoryFragment extends BaseHistoryFragment {
public SavedHistoryFragment() {
super(CalculatorFragmentType.saved_history);
}
@Override
protected int getItemLayoutId() {
return R.layout.saved_history_item;
}
@Nonnull
@Override
protected List<HistoryState> getHistoryItems() {
return history.getSaved();
}
@Override
protected void clearHistory() {
history.clearSaved();
getAdapter().clear();
}
@Override
protected boolean isRecentHistory() {
return false;
}
protected void onCreateContextMenu(@Nonnull ContextMenu menu, @Nonnull HistoryState state) {
menu.add(NONE, R.string.c_use, NONE, R.string.c_use);
menu.add(NONE, R.string.c_copy_expression, NONE, R.string.c_copy_expression);
if (shouldHaveCopyResult(state)) {
menu.add(NONE, R.string.c_copy_result, NONE, R.string.c_copy_result);
}
menu.add(NONE, R.string.c_edit, NONE, R.string.c_edit);
menu.add(NONE, R.string.c_remove, NONE, R.string.c_remove);
}
protected boolean onContextItemSelected(@Nonnull MenuItem item, @Nonnull HistoryState state) {
switch (item.getItemId()) {
case R.string.c_use:
useState(state);
return true;
case R.string.c_copy_expression:
copyExpression(state);
return true;
case R.string.c_copy_result:
copyResult(state);
return true;
case R.string.c_edit:
EditHistoryFragment.show(state, false, getFragmentManager());
return true;
case R.string.c_remove:
history.removeSaved(state);
return true;
}
return false;
}
}