TBR: TODOs: Correctly notify the adapter of changes to the data

adapter.notifyDataSetChanged() is a bad idea. It makes it hard for
the RecyclerView to animate any of its View Holders.

This CL swaps these notify calls out for the more specific kind.
This makes the list items appear to move as they are modified. It is
quite nice.

Closes https://github.com/vanadium/issues/issues/1298

Change-Id: Ifcd9662bcc96a4074b67db126c1f2e160375bdc1
diff --git a/projects/todos/app/src/main/java/io/v/todos/DataList.java b/projects/todos/app/src/main/java/io/v/todos/DataList.java
index fd59a13..494fe72 100644
--- a/projects/todos/app/src/main/java/io/v/todos/DataList.java
+++ b/projects/todos/app/src/main/java/io/v/todos/DataList.java
@@ -16,28 +16,31 @@
  * @author alexfandrianto
  */
 public class DataList<T extends KeyedData<T>> extends ArrayList<T> {
-    public void insertInOrder(T item) {
+    public int insertInOrder(T item) {
         // Note: binarySearch returns -|correct insert index| - 1 if it fails to find a match.
         // For Java ints, this is the bitwise complement of the "correct" insertion index.
-        int insertIndex = Collections.binarySearch(this, item);
-        add(insertIndex < 0 ? ~insertIndex : insertIndex, item);
+        int searchIndex = Collections.binarySearch(this, item);
+        int insertIndex = searchIndex < 0 ? ~searchIndex : searchIndex;
+        add(insertIndex, item);
+        return insertIndex;
     }
 
     // We have to replace the old item while keeping sort order.
     // It is easiest to remove and then insertInOrder.
-    public void updateInOrder(T item) {
+    public int updateInOrder(T item) {
         removeByKey(item.getKey());
-        insertInOrder(item);
+        return insertInOrder(item);
     }
 
-    public void removeByKey(String key) {
+    public int removeByKey(String key) {
         int index = findIndexByKey(key);
         if (index != -1) {
             remove(index);
         }
+        return index;
     }
 
-    private int findIndexByKey(String key) {
+    public int findIndexByKey(String key) {
         for (int i = 0; i < size(); i++) {
             T oldItem = get(i);
             if (oldItem.getKey().equals(key)) {
diff --git a/projects/todos/app/src/main/java/io/v/todos/MainActivity.java b/projects/todos/app/src/main/java/io/v/todos/MainActivity.java
index 708ffff..a9a2e49 100644
--- a/projects/todos/app/src/main/java/io/v/todos/MainActivity.java
+++ b/projects/todos/app/src/main/java/io/v/todos/MainActivity.java
@@ -79,15 +79,20 @@
             public void onSwiped(final RecyclerView.ViewHolder viewHolder, final int direction) {
                 String todoListKey = (String)viewHolder.itemView.getTag();
                 if (direction == ItemTouchHelper.RIGHT) {
-                    TodoList l = snackoosList.findByKey(todoListKey);
-                    if (l != null && l.canCompleteAll()) {
-                        mPersistence.completeAllTasks(l);
-                    } else {
-                        // TODO(alexfandrianto): Can we remove this? The bug when we don't have it
-                        // here is that the swiped card doesn't return even though no data may have
-                        // been affected.
-                        adapter.notifyDataSetChanged();
+                    int position = snackoosList.findIndexByKey(todoListKey);
+                    if (position == -1) {
+                        return;
                     }
+                    TodoList l = snackoosList.get(position);
+                    if (l.canCompleteAll()) {
+                        mPersistence.completeAllTasks(l);
+                    }
+                    // TODO(alexfandrianto): Can we remove this? The bug when we don't have it
+                    // here is that the swiped card doesn't return from being swiped to the right
+                    // whether or not is it marking tasks as done or not.
+                    // Doing this causes a little bit of flicker when marking all tasks as done and
+                    // appears natural in the no-op case.
+                    adapter.notifyItemChanged(position);
                 } else if (direction == ItemTouchHelper.LEFT) {
                     mPersistence.deleteTodoList(todoListKey);
                 }
@@ -97,24 +102,26 @@
         mPersistence = PersistenceFactory.getMainPersistence(this, new ListEventListener<TodoList>() {
             @Override
             public void onItemAdd(TodoList item) {
-                snackoosList.insertInOrder(item);
+                int position = snackoosList.insertInOrder(item);
 
-                adapter.notifyDataSetChanged();
+                adapter.notifyItemInserted(position);
                 setEmptyVisiblity();
             }
 
             @Override
             public void onItemUpdate(TodoList item) {
-                snackoosList.updateInOrder(item);
+                int start = snackoosList.findIndexByKey(item.getKey());
+                int end = snackoosList.updateInOrder(item);
 
-                adapter.notifyDataSetChanged();
+                adapter.notifyItemMoved(start, end);
+                adapter.notifyItemChanged(end);
             }
 
             @Override
             public void onItemDelete(String key) {
-                snackoosList.removeByKey(key);
+                int position = snackoosList.removeByKey(key);
 
-                adapter.notifyDataSetChanged();
+                adapter.notifyItemRemoved(position);
                 setEmptyVisiblity();
             }
         });
diff --git a/projects/todos/app/src/main/java/io/v/todos/TodoListActivity.java b/projects/todos/app/src/main/java/io/v/todos/TodoListActivity.java
index 0789c43..8c74fb9 100644
--- a/projects/todos/app/src/main/java/io/v/todos/TodoListActivity.java
+++ b/projects/todos/app/src/main/java/io/v/todos/TodoListActivity.java
@@ -100,22 +100,24 @@
 
             @Override
             public void onItemAdd(Task item) {
-                snackoosList.insertInOrder(item);
-                adapter.notifyDataSetChanged();
+                int position = snackoosList.insertInOrder(item);
+                adapter.notifyItemInserted(position);
                 setEmptyVisiblity();
             }
 
             @Override
             public void onItemUpdate(Task item) {
-                snackoosList.updateInOrder(item);
-                adapter.notifyDataSetChanged();
+                int start = snackoosList.findIndexByKey(item.getKey());
+                int end = snackoosList.updateInOrder(item);
+                adapter.notifyItemMoved(start, end);
+                adapter.notifyItemChanged(end);
                 setEmptyVisiblity();
             }
 
             @Override
             public void onItemDelete(String key) {
-                snackoosList.removeByKey(key);
-                adapter.notifyDataSetChanged();
+                int position = snackoosList.removeByKey(key);
+                adapter.notifyItemRemoved(position);
                 setEmptyVisiblity();
             }
         });
diff --git a/projects/todos/app/src/main/res/layout/todo_list_row.xml b/projects/todos/app/src/main/res/layout/todo_list_row.xml
index c76ce42..ef299e5 100644
--- a/projects/todos/app/src/main/res/layout/todo_list_row.xml
+++ b/projects/todos/app/src/main/res/layout/todo_list_row.xml
@@ -61,8 +61,8 @@
                 android:layout_weight = "1">
 
                 <TextView android:id="@+id/todo_list_completed"
-                    android:layout_width="fill_parent"
-                    android:layout_weight = "1"
+                    android:layout_width="0dp"
+                    android:layout_weight="1"
                     android:layout_height="wrap_content"
                     android:gravity="center_vertical"
                     android:textSize="12sp"
@@ -70,8 +70,8 @@
                     android:layout_margin="5dp" />
 
                 <TextView android:id="@+id/todo_list_time"
-                    android:layout_width="fill_parent"
-                    android:layout_weight = "1"
+                    android:layout_width="0dp"
+                    android:layout_weight="2"
                     android:layout_height="wrap_content"
                     android:gravity="center_vertical"
                     android:textSize="12sp"