Model refactor
This splits up the POJOs from the app data types to make it easier to
persist in both Firebase and Syncbase.
Change-Id: I4609e1ee0fa9772952e9aa12cf362a02e6098fee
diff --git a/app/build.gradle b/app/build.gradle
index 0b7fa60..ed30d8b 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -46,5 +46,4 @@
'com.android.support:recyclerview-v7:23.1.1'
)
firebaseCompile 'com.firebase:firebase-client-android:2.5.2'
- syncbaseCompile 'com.fasterxml.jackson.core:jackson-annotations:2.7.3'
}
diff --git a/app/src/firebase/java/io/v/todos/persistence/firebase/ChildEventListenerAdapter.java b/app/src/firebase/java/io/v/todos/persistence/firebase/ChildEventListenerAdapter.java
index 282eecd..4f22548 100644
--- a/app/src/firebase/java/io/v/todos/persistence/firebase/ChildEventListenerAdapter.java
+++ b/app/src/firebase/java/io/v/todos/persistence/firebase/ChildEventListenerAdapter.java
@@ -8,37 +8,20 @@
import com.firebase.client.DataSnapshot;
import com.firebase.client.FirebaseError;
-import io.v.todos.model.KeyedData;
-import io.v.todos.persistence.ListEventListener;
-
-public class ChildEventListenerAdapter<T extends KeyedData> implements ChildEventListener {
- private final Class<T> mType;
- private final ListEventListener<T> mDelegate;
-
- public ChildEventListenerAdapter(Class<T> type, ListEventListener<T> delegate) {
- mType = type;
- mDelegate = delegate;
- }
-
- private T prepareKeyedData(DataSnapshot dataSnapshot) {
- T value = dataSnapshot.getValue(mType);
- value.setKey(dataSnapshot.getKey());
- return value;
- }
-
+/**
+ * Basic Java adapter pattern for a {@link ChildEventListener}.
+ */
+public abstract class ChildEventListenerAdapter implements ChildEventListener {
@Override
public void onChildAdded(DataSnapshot dataSnapshot, String prevKey) {
- mDelegate.onItemAdd(prepareKeyedData(dataSnapshot));
}
@Override
public void onChildChanged(DataSnapshot dataSnapshot, String prevKey) {
- mDelegate.onItemUpdate(prepareKeyedData(dataSnapshot));
}
@Override
public void onChildRemoved(DataSnapshot dataSnapshot) {
- mDelegate.onItemDelete(dataSnapshot.getKey());
}
@Override
diff --git a/app/src/firebase/java/io/v/todos/persistence/firebase/FirebaseMain.java b/app/src/firebase/java/io/v/todos/persistence/firebase/FirebaseMain.java
index 9066a0b..2df421e 100644
--- a/app/src/firebase/java/io/v/todos/persistence/firebase/FirebaseMain.java
+++ b/app/src/firebase/java/io/v/todos/persistence/firebase/FirebaseMain.java
@@ -14,10 +14,14 @@
import com.firebase.client.Transaction;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.Map;
+import java.util.Set;
import io.v.todos.model.ListMetadata;
+import io.v.todos.model.ListSpec;
import io.v.todos.model.Task;
+import io.v.todos.model.TaskSpec;
import io.v.todos.persistence.ListEventListener;
import io.v.todos.persistence.MainPersistence;
@@ -40,28 +44,25 @@
// This handler will forward events to the passed in listener after ensuring that all the
// data in the ListMetadata is set and can automatically update.
mTodoListsListener = mTodoLists.addChildEventListener(
- new ChildEventListenerAdapter<>(ListMetadata.class, new ListEventListener<ListMetadata>() {
+ new ChildEventListenerAdapter() {
@Override
- public void onItemAdd(ListMetadata item) {
- // Hook up listeners for the # completed and # tasks. Then forward the item.
- startWatchTodoListTasks(item);
- mListener.onItemAdd(item);
+ public void onChildAdded(DataSnapshot dataSnapshot, String prevKey) {
+ mListener.onItemAdd(startWatchTodoListTasks(
+ dataSnapshot.getKey(), dataSnapshot.getValue(ListSpec.class)));
}
@Override
- public void onItemUpdate(ListMetadata item) {
- // Retrieve # completed and # tasks. Then forward the item.
- setTaskCompletion(item);
- mListener.onItemUpdate(item);
+ public void onChildChanged(DataSnapshot dataSnapshot, String prevKey) {
+ mListener.onItemUpdate(updateListSpec(
+ dataSnapshot.getKey(), dataSnapshot.getValue(ListSpec.class)));
}
@Override
- public void onItemDelete(String key) {
- // Remove listeners for the # completed and # tasks. Then forward the item.
- stopWatchTodoListTasks(key);
- mListener.onItemDelete(key);
+ public void onChildRemoved(DataSnapshot dataSnapshot) {
+ stopWatchTodoListTasks(dataSnapshot.getKey());
+ mListener.onItemDelete(dataSnapshot.getKey());
}
- }));
+ });
mListener = listener;
mTodoListTaskListeners = new HashMap<>();
@@ -69,8 +70,8 @@
}
@Override
- public void addTodoList(ListMetadata listMetadata) {
- mTodoLists.push().setValue(listMetadata);
+ public void addTodoList(ListSpec listSpec) {
+ mTodoLists.push().setValue(listSpec);
}
@Override
@@ -85,44 +86,46 @@
@Override
public void completeAllTasks(final ListMetadata listMetadata) {
// Update all child tasks for this key to have done = true.
- Firebase tasksRef = getFirebase().child(FirebaseTodoList.TASKS).child(listMetadata.getKey());
+ Firebase tasksRef = getFirebase().child(FirebaseTodoList.TASKS).child(listMetadata.key);
tasksRef.runTransaction(new Transaction.Handler() {
@Override
public Transaction.Result doTransaction(MutableData mutableData) {
// Note: This is very easy to make conflicts with. It may be better to avoid doing
// this in a batch or to split up the Task into components.
- for (Task t : mTodoListTrackers.get(listMetadata.getKey()).mTasks.values()) {
- Task tCopy = t.copy();
- tCopy.setDone(true);
- mutableData.child(t.getKey()).setValue(tCopy);
+ for (MutableData taskData : mutableData.getChildren()) {
+ TaskSpec spec = taskData.getValue(TaskSpec.class);
+ spec.setDone(true);
+ taskData.setValue(spec);
}
return Transaction.success(mutableData);
}
@Override
- public void onComplete(FirebaseError firebaseError, boolean b, DataSnapshot dataSnapshot) {
+ public void onComplete(FirebaseError firebaseError, boolean b,
+ DataSnapshot dataSnapshot) {
}
});
// Further, update this todo list to set its last updated time.
- mTodoLists.child(listMetadata.getKey()).setValue(new ListMetadata(listMetadata.getName()));
+ ListSpec spec = listMetadata.toSpec();
+ spec.setUpdatedAt(System.currentTimeMillis());
+ mTodoLists.child(listMetadata.key).setValue(spec);
}
- private void setTaskCompletion(ListMetadata listMetadata) {
- TodoListTasksListener tracker = mTodoListTrackers.get(listMetadata.getKey());
- tracker.swapTodoList(listMetadata);
+ private ListMetadata updateListSpec(String key, ListSpec updatedSpec) {
+ TodoListTasksListener tracker = mTodoListTrackers.get(key);
+ tracker.listSpec = updatedSpec;
+ return tracker.getListMetadata();
}
- private void startWatchTodoListTasks(final ListMetadata listMetadata) {
- final String todoListKey = listMetadata.getKey();
-
- Firebase taskRef = getFirebase().child(FirebaseTodoList.TASKS).child(todoListKey);
- TodoListTasksListener tasksListener = new TodoListTasksListener(listMetadata);
+ private ListMetadata startWatchTodoListTasks(String key, final ListSpec listSpec) {
+ Firebase taskRef = getFirebase().child(FirebaseTodoList.TASKS).child(key);
+ TodoListTasksListener tasksListener = new TodoListTasksListener(key, listSpec);
ChildEventListener l = taskRef.addChildEventListener(
- new ChildEventListenerAdapter<>(Task.class, tasksListener)
- );
- mTodoListTrackers.put(todoListKey, tasksListener);
- mTodoListTaskListeners.put(todoListKey, l);
+ new TaskChildEventListener(tasksListener));
+ mTodoListTrackers.put(key, tasksListener);
+ mTodoListTaskListeners.put(key, l);
+ return tasksListener.getListMetadata();
}
private void stopWatchTodoListTasks(String key) {
@@ -140,30 +143,28 @@
}
private class TodoListTasksListener implements ListEventListener<Task> {
- ListMetadata mListMetadata; // The list whose numCompleted and numTasks fields will be updated.
- final Map<String, Task> mTasks;
- boolean disabled = false;
+ final String listKey;
+ ListSpec listSpec;
+ final Set<String> completedTaskKeys;
+ int numTasks;
+ boolean disabled;
- TodoListTasksListener(ListMetadata listMetadata) {
- mListMetadata = listMetadata;
- mTasks = new HashMap<>();
+ TodoListTasksListener(String listKey, ListSpec listSpec) {
+ this.listKey = listKey;
+ this.listSpec = listSpec;
+
+ completedTaskKeys = new HashSet<>();
}
// Prevent this listener from propagating any more updates.
// Note: It looks like Firebase will continue firing listeners if they have more data, so
// call this if you absolutely don't need any more events to fire.
- public void disable() {
+ void disable() {
disabled = true;
}
- public void swapTodoList(ListMetadata otherList) {
- if (disabled) {
- return;
- }
- assert mListMetadata.getKey() == otherList.getKey();
- otherList.numCompleted = mListMetadata.numCompleted;
- otherList.numTasks = mListMetadata.numTasks;
- mListMetadata = otherList;
+ ListMetadata getListMetadata() {
+ return new ListMetadata(listKey, listSpec, completedTaskKeys.size(), numTasks);
}
@Override
@@ -171,13 +172,12 @@
if (disabled) {
return;
}
- mListMetadata.numTasks++;
- if (item.getDone()) {
- mListMetadata.numCompleted++;
+ numTasks++;
+ if (item.done) {
+ completedTaskKeys.add(item.key);
}
- mTasks.put(item.getKey(), item);
- mListener.onItemUpdate(mListMetadata);
+ mListener.onItemUpdate(getListMetadata());
}
@Override
@@ -185,16 +185,14 @@
if (disabled) {
return;
}
- Task oldItem = mTasks.get(item.getKey());
- mTasks.put(item.getKey(), item);
- if (oldItem.getDone() != item.getDone()) {
- if (item.getDone()) {
- mListMetadata.numCompleted++;
- } else {
- mListMetadata.numCompleted--;
- }
- mListener.onItemUpdate(mListMetadata);
+ // Short-circuiting performs the appropriate Set update (add if done, remove if not).
+ boolean changedDone =
+ item.done && completedTaskKeys.add(item.key) ||
+ !item.done && completedTaskKeys.remove(item.key);
+
+ if (changedDone) {
+ mListener.onItemUpdate(getListMetadata());
}
}
@@ -203,13 +201,10 @@
if (disabled) {
return;
}
- mListMetadata.numTasks--;
- Task t = mTasks.remove(key);
- if (t.getDone()) {
- mListMetadata.numCompleted--;
- }
+ numTasks--;
+ completedTaskKeys.remove(key);
- mListener.onItemUpdate(mListMetadata);
+ mListener.onItemUpdate(getListMetadata());
}
}
}
diff --git a/app/src/firebase/java/io/v/todos/persistence/firebase/FirebaseTodoList.java b/app/src/firebase/java/io/v/todos/persistence/firebase/FirebaseTodoList.java
index 402cd62..393bb44 100644
--- a/app/src/firebase/java/io/v/todos/persistence/firebase/FirebaseTodoList.java
+++ b/app/src/firebase/java/io/v/todos/persistence/firebase/FirebaseTodoList.java
@@ -12,8 +12,9 @@
import com.firebase.client.FirebaseError;
import com.firebase.client.ValueEventListener;
-import io.v.todos.model.ListMetadata;
+import io.v.todos.model.ListSpec;
import io.v.todos.model.Task;
+import io.v.todos.model.TaskSpec;
import io.v.todos.persistence.TodoListListener;
import io.v.todos.persistence.TodoListPersistence;
@@ -24,7 +25,7 @@
private final ValueEventListener mTodoListListener;
private final ChildEventListener mTasksListener;
- private ListMetadata mList;
+ private ListSpec mListSpec;
public FirebaseTodoList(Context context, String todoListKey, final TodoListListener listener) {
super(context);
@@ -35,28 +36,26 @@
mTodoListListener = mTodoList.addValueEventListener(new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
- ListMetadata listMetadata = dataSnapshot.getValue(ListMetadata.class);
- if (listMetadata == null) {
+ ListSpec listSpec = dataSnapshot.getValue(ListSpec.class);
+ if (listSpec == null) {
listener.onDelete();
} else {
- mList = listMetadata;
- listener.onUpdate(listMetadata);
+ mListSpec = listSpec;
+ listener.onUpdate(listSpec);
}
}
@Override
public void onCancelled(FirebaseError firebaseError) {
-
}
});
- mTasksListener = mTasks.addChildEventListener(
- new ChildEventListenerAdapter<>(Task.class, listener));
+ mTasksListener = mTasks.addChildEventListener(new TaskChildEventListener(listener));
}
@Override
- public void updateTodoList(ListMetadata listMetadata) {
- mTodoList.setValue(listMetadata);
+ public void updateTodoList(ListSpec listSpec) {
+ mTodoList.setValue(listSpec);
}
@Override
@@ -64,22 +63,27 @@
mTodoList.removeValue();
}
+ private void updateListTimestamp() {
+ mListSpec.setUpdatedAt(System.currentTimeMillis());
+ mTodoList.setValue(mListSpec);
+ }
+
@Override
- public void addTask(Task task) {
+ public void addTask(TaskSpec task) {
mTasks.push().setValue(task);
- mTodoList.setValue(new ListMetadata(mList.getName()));
+ updateListTimestamp();
}
@Override
public void updateTask(Task task) {
- mTasks.child(task.getKey()).setValue(task);
- mTodoList.setValue(new ListMetadata(mList.getName()));
+ mTasks.child(task.key).setValue(task.toSpec());
+ updateListTimestamp();
}
@Override
public void deleteTask(String key) {
mTasks.child(key).removeValue();
- mTodoList.setValue(new ListMetadata(mList.getName()));
+ updateListTimestamp();
}
@Override
diff --git a/app/src/firebase/java/io/v/todos/persistence/firebase/TaskChildEventListener.java b/app/src/firebase/java/io/v/todos/persistence/firebase/TaskChildEventListener.java
new file mode 100644
index 0000000..0c0023b
--- /dev/null
+++ b/app/src/firebase/java/io/v/todos/persistence/firebase/TaskChildEventListener.java
@@ -0,0 +1,38 @@
+// Copyright 2016 The Vanadium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package io.v.todos.persistence.firebase;
+
+import com.firebase.client.DataSnapshot;
+
+import io.v.todos.model.Task;
+import io.v.todos.model.TaskSpec;
+import io.v.todos.persistence.ListEventListener;
+
+public class TaskChildEventListener extends ChildEventListenerAdapter {
+ private final ListEventListener<Task> mDelegate;
+
+ public TaskChildEventListener(ListEventListener<Task> delegate) {
+ mDelegate = delegate;
+ }
+
+ protected Task extractValue(DataSnapshot dataSnapshot) {
+ return new Task(dataSnapshot.getKey(), dataSnapshot.getValue(TaskSpec.class));
+ }
+
+ @Override
+ public void onChildAdded(DataSnapshot dataSnapshot, String prevKey) {
+ mDelegate.onItemAdd(extractValue(dataSnapshot));
+ }
+
+ @Override
+ public void onChildChanged(DataSnapshot dataSnapshot, String prevKey) {
+ mDelegate.onItemUpdate(extractValue(dataSnapshot));
+ }
+
+ @Override
+ public void onChildRemoved(DataSnapshot dataSnapshot) {
+ mDelegate.onItemDelete(dataSnapshot.getKey());
+ }
+}
diff --git a/app/src/main/java/io/v/todos/MainActivity.java b/app/src/main/java/io/v/todos/MainActivity.java
index 93e84b3..0b8e1c0 100644
--- a/app/src/main/java/io/v/todos/MainActivity.java
+++ b/app/src/main/java/io/v/todos/MainActivity.java
@@ -20,6 +20,7 @@
import io.v.todos.model.DataList;
import io.v.todos.model.ListMetadata;
+import io.v.todos.model.ListSpec;
import io.v.todos.persistence.ListEventListener;
import io.v.todos.persistence.MainPersistence;
import io.v.todos.persistence.PersistenceFactory;
@@ -112,7 +113,7 @@
@Override
public void onItemUpdate(ListMetadata item) {
- int start = snackoosList.findIndexByKey(item.getKey());
+ int start = snackoosList.findIndexByKey(item.key);
int end = snackoosList.updateInOrder(item);
adapter.notifyItemMoved(start, end);
@@ -143,7 +144,7 @@
.setView(todoItem)
.setPositiveButton("Add", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
- mPersistence.addTodoList(new ListMetadata(todoItem.getText().toString()));
+ mPersistence.addTodoList(new ListSpec(todoItem.getText().toString()));
}
})
.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
diff --git a/app/src/main/java/io/v/todos/TaskRecyclerAdapter.java b/app/src/main/java/io/v/todos/TaskRecyclerAdapter.java
index ffd1fa6..73533dc 100644
--- a/app/src/main/java/io/v/todos/TaskRecyclerAdapter.java
+++ b/app/src/main/java/io/v/todos/TaskRecyclerAdapter.java
@@ -49,7 +49,7 @@
private int nonDoneSize() {
for (int i = 0; i < backup.size(); i++) {
- if (backup.get(i).getDone()) {
+ if (backup.get(i).done) {
return i;
}
}
diff --git a/app/src/main/java/io/v/todos/TaskViewHolder.java b/app/src/main/java/io/v/todos/TaskViewHolder.java
index 7354ac6..2aed003 100644
--- a/app/src/main/java/io/v/todos/TaskViewHolder.java
+++ b/app/src/main/java/io/v/todos/TaskViewHolder.java
@@ -22,24 +22,24 @@
public void bindTask(Task task, View.OnClickListener listener) {
final ImageView doneMark = (ImageView) itemView.findViewById(R.id.task_done);
- doneMark.setVisibility(task.getDone() ? View.VISIBLE : View.GONE);
+ doneMark.setVisibility(task.done ? View.VISIBLE : View.GONE);
final TextView name=(TextView) itemView.findViewById(R.id.task_text);
- name.setText(task.getText());
+ name.setText(task.text);
final TextView created=(TextView) itemView.findViewById(R.id.task_time);
created.setText(computeCreated(task));
- getCardView().setCardBackgroundColor(task.getDone() ? 0xFFCCCCCC : 0xFFFFFFFF);
+ getCardView().setCardBackgroundColor(task.done ? 0xFFCCCCCC : 0xFFFFFFFF);
- itemView.setTag(task.getKey());
+ itemView.setTag(task.key);
itemView.setOnClickListener(listener);
- itemView.setVisibility(!showDone && task.getDone() ? View.GONE : View.VISIBLE);
+ itemView.setVisibility(!showDone && task.done ? View.GONE : View.VISIBLE);
}
private String computeCreated(Task task) {
- return UIUtil.computeTimeAgo("Created", task.getAddedAt());
+ return UIUtil.computeTimeAgo("Created", task.addedAt);
}
public void setShowDone(boolean showDone) {
diff --git a/app/src/main/java/io/v/todos/TodoListActivity.java b/app/src/main/java/io/v/todos/TodoListActivity.java
index 4d6a5a2..8bd6d9a 100644
--- a/app/src/main/java/io/v/todos/TodoListActivity.java
+++ b/app/src/main/java/io/v/todos/TodoListActivity.java
@@ -19,8 +19,9 @@
import android.widget.Toolbar;
import io.v.todos.model.DataList;
-import io.v.todos.model.ListMetadata;
+import io.v.todos.model.ListSpec;
import io.v.todos.model.Task;
+import io.v.todos.model.TaskSpec;
import io.v.todos.persistence.PersistenceFactory;
import io.v.todos.persistence.TodoListListener;
import io.v.todos.persistence.TodoListPersistence;
@@ -40,7 +41,7 @@
public class TodoListActivity extends Activity {
private TodoListPersistence mPersistence;
- private ListMetadata snackoo;
+ private ListSpec snackoo;
private DataList<Task> snackoosList = new DataList<Task>();
private boolean showDone = false; // TODO(alexfandrianto): Load from shared preferences...
@@ -91,7 +92,7 @@
mPersistence = PersistenceFactory.getTodoListPersistence(this, snackooKey,
new TodoListListener() {
@Override
- public void onUpdate(ListMetadata value) {
+ public void onUpdate(ListSpec value) {
snackoo = value;
getActionBar().setTitle(snackoo.getName());
}
@@ -110,7 +111,7 @@
@Override
public void onItemUpdate(Task item) {
- int start = snackoosList.findIndexByKey(item.getKey());
+ int start = snackoosList.findIndexByKey(item.key);
int end = snackoosList.updateInOrder(item);
adapter.notifyItemMoved(start, end);
adapter.notifyItemChanged(end);
@@ -133,18 +134,14 @@
}
public void addTodoItem(String todo) {
- mPersistence.addTask(new Task(todo));
+ mPersistence.addTask(new TaskSpec(todo));
}
public void updateTodoItem(String fbKey, String todo) {
- Task task = snackoosList.findByKey(fbKey).copy();
- task.setText(todo);
- mPersistence.updateTask(task);
+ mPersistence.updateTask(snackoosList.findByKey(fbKey).withText(todo));
}
public void markAsDone(String fbKey) {
- Task task = snackoosList.findByKey(fbKey).copy();
- task.setDone(!task.getDone());
- mPersistence.updateTask(task);
+ mPersistence.updateTask(snackoosList.findByKey(fbKey).withToggleDone());
}
public void deleteTodoItem(String fbKey) {
@@ -172,7 +169,7 @@
private void initiateTaskEdit(final String fbKey) {
final EditText todoItem = new EditText(this);
- todoItem.setText(snackoosList.findByKey(fbKey).getText());
+ todoItem.setText(snackoosList.findByKey(fbKey).text);
AlertDialog dialog = new AlertDialog.Builder(this)
.setTitle("Editing Task")
@@ -221,8 +218,8 @@
}
- public void updateTodoList(String todo) {
- mPersistence.updateTodoList(new ListMetadata(todo));
+ public void updateTodoList(String name) {
+ mPersistence.updateTodoList(new ListSpec(name));
}
public void deleteTodoList() {
diff --git a/app/src/main/java/io/v/todos/TodoListViewHolder.java b/app/src/main/java/io/v/todos/TodoListViewHolder.java
index b170523..5ddfd8c 100644
--- a/app/src/main/java/io/v/todos/TodoListViewHolder.java
+++ b/app/src/main/java/io/v/todos/TodoListViewHolder.java
@@ -19,7 +19,7 @@
public void bindTodoList(ListMetadata listMetadata, View.OnClickListener listener) {
final TextView name=(TextView) itemView.findViewById(R.id.todo_list_name);
- name.setText(listMetadata.getName());
+ name.setText(listMetadata.name);
final TextView completedStatus=(TextView) itemView.findViewById(R.id.todo_list_completed);
completedStatus.setText(computeCompleted(listMetadata));
@@ -27,18 +27,18 @@
final TextView timeAgo=(TextView) itemView.findViewById(R.id.todo_list_time);
timeAgo.setText(computeTimeAgo(listMetadata));
- getCardView().setCardBackgroundColor(listMetadata.getDone() ? 0xFFCCCCCC : 0xFFFFFFFF);
+ getCardView().setCardBackgroundColor(listMetadata.isDone() ? 0xFFCCCCCC : 0xFFFFFFFF);
- itemView.setTag(listMetadata.getKey());
+ itemView.setTag(listMetadata.key);
itemView.setOnClickListener(listener);
}
private String computeTimeAgo(ListMetadata listMetadata) {
- return UIUtil.computeTimeAgo("Last Updated", listMetadata.getUpdatedAt());
+ return UIUtil.computeTimeAgo("Last Updated", listMetadata.updatedAt);
}
private String computeCompleted(ListMetadata listMetadata) {
- if (listMetadata.getDone()) {
+ if (listMetadata.isDone()) {
return "Done!";
} else if (listMetadata.numTasks == 0) {
return "Needs Tasks";
diff --git a/app/src/main/java/io/v/todos/model/DataList.java b/app/src/main/java/io/v/todos/model/DataList.java
index 47fbda3..90d4a30 100644
--- a/app/src/main/java/io/v/todos/model/DataList.java
+++ b/app/src/main/java/io/v/todos/model/DataList.java
@@ -28,7 +28,7 @@
// We have to replace the old item while keeping sort order.
// It is easiest to remove and then insertInOrder.
public int updateInOrder(T item) {
- removeByKey(item.getKey());
+ removeByKey(item.key);
return insertInOrder(item);
}
@@ -43,7 +43,7 @@
public int findIndexByKey(String key) {
for (int i = 0; i < size(); i++) {
T oldItem = get(i);
- if (oldItem.getKey().equals(key)) {
+ if (oldItem.key.equals(key)) {
return i;
}
}
diff --git a/app/src/main/java/io/v/todos/model/KeyedData.java b/app/src/main/java/io/v/todos/model/KeyedData.java
index e0b2252..2fede3c 100644
--- a/app/src/main/java/io/v/todos/model/KeyedData.java
+++ b/app/src/main/java/io/v/todos/model/KeyedData.java
@@ -5,12 +5,15 @@
package io.v.todos.model;
/**
- * KeyedData represents data that has a key and is comparable.
- * Most subclasses will use this key as part of their comparison function.
+ * Represents data that has a key and is comparable. Most subclasses will use this key as part of
+ * their comparison function.
*
* @author alexfandrianto
*/
-public interface KeyedData<T> extends Comparable<T> {
- String getKey();
- void setKey(String key);
+public abstract class KeyedData<T> implements Comparable<T> {
+ public final String key;
+
+ protected KeyedData(String key) {
+ this.key = key;
+ }
}
diff --git a/app/src/main/java/io/v/todos/model/ListMetadata.java b/app/src/main/java/io/v/todos/model/ListMetadata.java
index d455f62..46b89ec 100644
--- a/app/src/main/java/io/v/todos/model/ListMetadata.java
+++ b/app/src/main/java/io/v/todos/model/ListMetadata.java
@@ -4,60 +4,69 @@
package io.v.todos.model;
-import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import android.support.annotation.NonNull;
/**
- * ListMetadata is a Firebase-compatible class that tracks information regarding a particular todo list.
- *
- * @author alexfandrianto
+ * Tracks information regarding a particular todo list.
*/
-@JsonIgnoreProperties({ "numCompleted", "numTasks", "done", "key" })
-public class ListMetadata implements KeyedData<ListMetadata> {
- private String name;
- private long updatedAt;
+public class ListMetadata extends KeyedData<ListMetadata> {
+ public final String name;
+ public final long updatedAt;
- // Not serialized.
- public int numCompleted = 0;
- public int numTasks = 0;
- private String key = null; // Usually assigned for comparison/viewing.
- //public List<String> sharedWith ??
+ public final int numCompleted;
+ public final int numTasks;
- // The default constructor is used by Firebase.
- public ListMetadata() {}
-
- // Use this constructor when creating a new Task for the first time.
- public ListMetadata(String name) {
+ public ListMetadata(String key, String name, long updatedAt, int numCompleted, int numTasks) {
+ super(key);
this.name = name;
- this.updatedAt = System.currentTimeMillis();
+ this.updatedAt = updatedAt;
+ this.numCompleted = numCompleted;
+ this.numTasks = numTasks;
}
- public String getName() {
- return name;
- }
- public long getUpdatedAt() {
- return updatedAt;
+ public ListMetadata(String key, ListSpec spec, int numCompleted, int numTasks) {
+ this(key, spec.getName(), spec.getUpdatedAt(), numCompleted, numTasks);
}
- public boolean getDone() {
+ public boolean isDone() {
return numTasks > 0 && numCompleted == numTasks;
}
- public boolean canCompleteAll() { return numCompleted < numTasks; }
- public void setKey(String key) {
- this.key = key;
- }
- public String getKey() {
- return key;
+
+ public boolean canCompleteAll() {
+ return numCompleted < numTasks;
}
@Override
- public int compareTo(ListMetadata other) {
- if (key == null && other.key != null) {
- return 1;
- } else if (key != null && other.key == null) {
- return -1;
- } else if (key == null && other.key == null) {
+ public boolean equals(Object o) {
+ return this == o ||
+ o instanceof ListMetadata &&
+ ((ListMetadata) o).canEqual(this) &&
+ compareTo((ListMetadata)o) == 0;
+ }
+
+ protected boolean canEqual(Object other) {
+ return other instanceof ListMetadata;
+ }
+
+ @Override
+ public int hashCode() {
+ return key.hashCode();
+ }
+
+ @Override
+ public int compareTo(@NonNull ListMetadata other) {
+ if (this == other) {
return 0;
+ } else if (!other.canEqual(this)) {
+ throw new ClassCastException("Cannot compare " + getClass() + " to " +
+ other.getClass());
+ } else {
+ return key.compareTo(other.key);
}
- return key.compareTo(other.key);
+ // TODO(rosswang): factor out ordering.
+ }
+
+ public ListSpec toSpec() {
+ return new ListSpec(name, updatedAt);
}
}
diff --git a/app/src/main/java/io/v/todos/model/ListSpec.java b/app/src/main/java/io/v/todos/model/ListSpec.java
new file mode 100644
index 0000000..06302d8
--- /dev/null
+++ b/app/src/main/java/io/v/todos/model/ListSpec.java
@@ -0,0 +1,43 @@
+// Copyright 2016 The Vanadium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package io.v.todos.model;
+
+/**
+ * POJO of persisted information regarding a particular todo list.
+ */
+public class ListSpec {
+ private String mName;
+ private long mUpdatedAt;
+
+ public ListSpec() {}
+
+ public ListSpec(String name, long updatedAt) {
+ mName = name;
+ mUpdatedAt = updatedAt;
+ }
+
+ /**
+ * Convenience constructor that initializes {@code updatedAt} to now.
+ */
+ public ListSpec(String name) {
+ this(name, System.currentTimeMillis());
+ }
+
+ public String getName() {
+ return mName;
+ }
+
+ public void setName(String value) {
+ mName = value;
+ }
+
+ public long getUpdatedAt() {
+ return mUpdatedAt;
+ }
+
+ public void setUpdatedAt(long value) {
+ mUpdatedAt = value;
+ }
+}
diff --git a/app/src/main/java/io/v/todos/model/Task.java b/app/src/main/java/io/v/todos/model/Task.java
index d8af531..e415048 100644
--- a/app/src/main/java/io/v/todos/model/Task.java
+++ b/app/src/main/java/io/v/todos/model/Task.java
@@ -4,78 +4,81 @@
package io.v.todos.model;
-import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import android.support.annotation.NonNull;
+
+import java.util.Objects;
/**
* Task is a Firebase-compatible class that tracks information regarding a particular task.
*
* @author alexfandrianto
*/
-@JsonIgnoreProperties({ "key" })
-public class Task implements KeyedData<Task> {
- private String text;
- private long addedAt;
- private boolean done;
-
- // Unserialized properties.
- private String key; // Usually assigned for comparison/viewing.
-
- // The default constructor is used by Firebase.
- public Task() {}
+public class Task extends KeyedData<Task> {
+ public final String text;
+ public final long addedAt;
+ public final boolean done;
// Use this constructor when creating a new Task for the first time.
- public Task(String text) {
+ public Task(String key, String text, long addedAt, boolean done) {
+ super(key);
this.text = text;
- this.addedAt = System.currentTimeMillis();
- this.done = false;
+ this.addedAt = addedAt;
+ this.done = done;
}
- public Task copy() {
- Task t = new Task();
- t.text = text;
- t.addedAt = addedAt;
- t.done = done;
- t.key = key;
- return t;
+ public Task(String key, TaskSpec spec) {
+ this(key, spec.getText(), spec.getAddedAt(), spec.getDone());
}
- public String getText() {
- return text;
- }
- public long getAddedAt() {
- return addedAt;
- }
- public boolean getDone() {
- return done;
- }
- public void setKey(String key) {
- this.key = key;
- }
- public String getKey() {
- return key;
+ public Task withText(String value) {
+ return Objects.equals(text, value) ? this : new Task(key, value, addedAt, done);
}
- public void setText(String newText) {
- text = newText;
- }
- public void setDone(boolean newDone) {
- done = newDone;
+ public Task withToggleDone() {
+ return new Task(key, text, addedAt, !done);
}
@Override
- public int compareTo(Task other) {
- if (done && !other.done) {
+ public boolean equals(Object o) {
+ return this == o ||
+ o instanceof Task &&
+ ((Task) o).canEqual(this) &&
+ compareTo((Task)o) == 0;
+ }
+
+ protected boolean canEqual(Object other) {
+ return other instanceof Task;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(addedAt, done);
+ }
+
+ @Override
+ public int compareTo(@NonNull Task other) {
+ // TODO(rosswang): factor out ordering.
+ if (other == this) {
+ return 0;
+ } else if (!other.canEqual(this)) {
+ throw new ClassCastException("Cannot compare " + getClass() + " to " +
+ other.getClass());
+ } else if (done && !other.done) {
return 1;
} else if (!done && other.done) {
return -1;
- }
- if (key == null && other.key != null) {
+ } else if (key == null && other.key != null) {
return 1;
} else if (key != null && other.key == null) {
return -1;
- } else if (key == null && other.key == null) {
+ } else if (key == null) {
return 0;
+ } else {
+ return key.compareTo(other.key);
}
- return key.compareTo(other.key);
+ }
+
+ public TaskSpec toSpec() {
+ return new TaskSpec(text, addedAt, done);
}
}
diff --git a/app/src/main/java/io/v/todos/model/TaskSpec.java b/app/src/main/java/io/v/todos/model/TaskSpec.java
new file mode 100644
index 0000000..f3f243d
--- /dev/null
+++ b/app/src/main/java/io/v/todos/model/TaskSpec.java
@@ -0,0 +1,55 @@
+// Copyright 2016 The Vanadium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package io.v.todos.model;
+
+/**
+ * POJO of persisted information regarding a particular task.
+ *
+ * @author alexfandrianto
+ */
+public class TaskSpec {
+ private String mText;
+ private long mAddedAt;
+ private boolean mDone;
+
+ public TaskSpec() {}
+
+ public TaskSpec(String text, long addedAt, boolean done) {
+ mText = text;
+ mAddedAt = addedAt;
+ mDone = done;
+ }
+
+ /**
+ * Convenience constructor that creates an undone task with an {@code addedAt} timestamp of now.
+ */
+ public TaskSpec(String text) {
+ this(text, System.currentTimeMillis(), false);
+ }
+
+ public String getText() {
+ return mText;
+ }
+
+ public void setText(String value) {
+ mText = value;
+ }
+
+ public long getAddedAt() {
+ return mAddedAt;
+ }
+
+ public void setAddedAt(long value) {
+ mAddedAt = value;
+ }
+
+ public boolean getDone() {
+ return mDone;
+ }
+
+ public void setDone(boolean value) {
+ mDone = value;
+ }
+}
diff --git a/app/src/main/java/io/v/todos/persistence/MainPersistence.java b/app/src/main/java/io/v/todos/persistence/MainPersistence.java
index 7516f3d..0d3a5a3 100644
--- a/app/src/main/java/io/v/todos/persistence/MainPersistence.java
+++ b/app/src/main/java/io/v/todos/persistence/MainPersistence.java
@@ -5,9 +5,10 @@
package io.v.todos.persistence;
import io.v.todos.model.ListMetadata;
+import io.v.todos.model.ListSpec;
public interface MainPersistence extends Persistence {
- void addTodoList(ListMetadata listMetadata);
+ void addTodoList(ListSpec listSpec);
void deleteTodoList(String key);
void completeAllTasks(ListMetadata listMetadata);
}
diff --git a/app/src/main/java/io/v/todos/persistence/TodoListListener.java b/app/src/main/java/io/v/todos/persistence/TodoListListener.java
index de06278..7ae1ebe 100644
--- a/app/src/main/java/io/v/todos/persistence/TodoListListener.java
+++ b/app/src/main/java/io/v/todos/persistence/TodoListListener.java
@@ -4,10 +4,10 @@
package io.v.todos.persistence;
+import io.v.todos.model.ListSpec;
import io.v.todos.model.Task;
-import io.v.todos.model.ListMetadata;
public interface TodoListListener extends ListEventListener<Task> {
- void onUpdate(ListMetadata value);
+ void onUpdate(ListSpec value);
void onDelete();
}
diff --git a/app/src/main/java/io/v/todos/persistence/TodoListPersistence.java b/app/src/main/java/io/v/todos/persistence/TodoListPersistence.java
index 2dcb24c..5dfb1ff 100644
--- a/app/src/main/java/io/v/todos/persistence/TodoListPersistence.java
+++ b/app/src/main/java/io/v/todos/persistence/TodoListPersistence.java
@@ -4,13 +4,14 @@
package io.v.todos.persistence;
-import io.v.todos.model.ListMetadata;
+import io.v.todos.model.ListSpec;
import io.v.todos.model.Task;
+import io.v.todos.model.TaskSpec;
public interface TodoListPersistence extends Persistence {
- void updateTodoList(ListMetadata listMetadata);
+ void updateTodoList(ListSpec listSpec);
void deleteTodoList();
- void addTask(Task task);
+ void addTask(TaskSpec task);
void updateTask(Task task);
void deleteTask(String key);
}