Making no-items view a bit friendlier

Also factoring out common activity elements to make consistent UI polish
easier to do.

Change-Id: If2d1d7907caa123e8424988ee3188ed0eef20fe1
diff --git a/app/src/androidTestMock/java/io/v/todos/MainActivityTest.java b/app/src/androidTestMock/java/io/v/todos/MainActivityTest.java
index c9b2dec..1e8956c 100644
--- a/app/src/androidTestMock/java/io/v/todos/MainActivityTest.java
+++ b/app/src/androidTestMock/java/io/v/todos/MainActivityTest.java
@@ -66,7 +66,7 @@
 
     private MainPersistence mockPersistence() {
         MainPersistence mocked = mock(MainPersistence.class);
-        mActivity.setMainPersistence(mocked);
+        mActivity.setPersistence(mocked);
         return mocked;
     }
 
diff --git a/app/src/androidTestMock/java/io/v/todos/TodoListActivityTest.java b/app/src/androidTestMock/java/io/v/todos/TodoListActivityTest.java
index afc1446..3fd8212 100644
--- a/app/src/androidTestMock/java/io/v/todos/TodoListActivityTest.java
+++ b/app/src/androidTestMock/java/io/v/todos/TodoListActivityTest.java
@@ -68,7 +68,7 @@
 
     private TodoListPersistence mockPersistence() {
         TodoListPersistence mocked = mock(TodoListPersistence.class);
-        mActivity.setTodoListPersistence(mocked);
+        mActivity.setPersistence(mocked);
         return mocked;
     }
 
diff --git a/app/src/main/java/io/v/todos/MainActivity.java b/app/src/main/java/io/v/todos/MainActivity.java
index 878f28f..7ca7d88 100644
--- a/app/src/main/java/io/v/todos/MainActivity.java
+++ b/app/src/main/java/io/v/todos/MainActivity.java
@@ -4,7 +4,6 @@
 
 package io.v.todos;
 
-import android.app.Activity;
 import android.content.Intent;
 import android.os.Bundle;
 import android.support.annotation.VisibleForTesting;
@@ -13,7 +12,6 @@
 import android.view.Menu;
 import android.view.MenuItem;
 import android.view.View;
-import android.widget.Toolbar;
 
 import io.v.todos.model.DataList;
 import io.v.todos.model.ListMetadata;
@@ -32,36 +30,20 @@
  *
  * @author alexfandrianto
  */
-public class MainActivity extends Activity {
-    private MainPersistence mPersistence;
-
+public class MainActivity extends TodosAppActivity<MainPersistence, TodoListRecyclerAdapter> {
     // Snackoos are the code name for the list of todos.
     // These todos are backed up at the SNACKOOS child of the Firebase URL.
     // We use mMainList to track a custom sorted list of the stored values.
     static final String INTENT_SNACKOO_KEY = "snackoo key";
     private DataList<ListMetadata> mMainList = new DataList<>();
 
-    // This adapter handle mirrors the firebase list values and generates the corresponding todo
-    // item View children for a list view.
-    private TodoListRecyclerAdapter mAdapter;
     private RecyclerView mRecyclerView;
 
     @Override
-    protected void onDestroy() {
-        if (mPersistence != null) {
-            mPersistence.close();
-            mPersistence = null;
-        }
-        super.onDestroy();
-    }
-
-    @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
-        setContentView(R.layout.activity_main);
-        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
-        setActionBar(toolbar);
         getActionBar().setTitle(R.string.app_name);
+        mEmptyView.setText(R.string.no_lists);
 
         // Set up the todo list adapter
         mAdapter = new TodoListRecyclerAdapter(mMainList, new View.OnClickListener() {
@@ -164,18 +146,6 @@
                 };
     }
 
-    // Allow the tests to mock out the main persistence.
-    @VisibleForTesting
-    void setMainPersistence(MainPersistence p) {
-        mPersistence = p;
-    }
-
-    // Set the visibility based on what the adapter thinks is the visible item count.
-    private void setEmptyVisiblity() {
-        View v = findViewById(R.id.empty);
-        v.setVisibility(mAdapter.getItemCount() == 0 ? View.VISIBLE : View.GONE);
-    }
-
     public void initiateItemAdd(View view) {
         UIUtil.showAddDialog(this, "New Todo List", new UIUtil.DialogResponseListener() {
             @Override
diff --git a/app/src/main/java/io/v/todos/TodoListActivity.java b/app/src/main/java/io/v/todos/TodoListActivity.java
index 454884d..e47cf53 100644
--- a/app/src/main/java/io/v/todos/TodoListActivity.java
+++ b/app/src/main/java/io/v/todos/TodoListActivity.java
@@ -4,7 +4,6 @@
 
 package io.v.todos;
 
-import android.app.Activity;
 import android.content.Intent;
 import android.os.Bundle;
 import android.support.annotation.VisibleForTesting;
@@ -13,13 +12,11 @@
 import android.view.Menu;
 import android.view.MenuItem;
 import android.view.View;
-import android.widget.Toolbar;
 
 import io.v.todos.model.DataList;
 import io.v.todos.model.ListSpec;
 import io.v.todos.model.Task;
 import io.v.todos.model.TaskSpec;
-import io.v.todos.persistence.MainPersistence;
 import io.v.todos.persistence.PersistenceFactory;
 import io.v.todos.persistence.TodoListListener;
 import io.v.todos.persistence.TodoListPersistence;
@@ -36,40 +33,22 @@
  *
  * @author alexfandrianto
  */
-public class TodoListActivity extends Activity {
-    private TodoListPersistence mPersistence;
-
+public class TodoListActivity extends TodosAppActivity<TodoListPersistence, TaskRecyclerAdapter> {
     private ListSpec snackoo;
     private DataList<Task> snackoosList = new DataList<>();
 
-    // This adapter handle mirrors the firebase list values and generates the corresponding todo
-    // item View children for a list view.
-    private TaskRecyclerAdapter adapter;
-
     // The menu item that toggles whether done items are shown or not.
     private MenuItem mShowDoneMenuItem;
 
-    @Override
-    protected void onDestroy() {
-        if (mPersistence != null) {
-            mPersistence.close();
-            mPersistence = null;
-        }
-        super.onDestroy();
-    }
-
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
-        setContentView(R.layout.activity_main);
+        mEmptyView.setText(R.string.no_tasks);
 
         Intent intent = getIntent();
         final String snackooKey = intent.getStringExtra(MainActivity.INTENT_SNACKOO_KEY);
 
-        Toolbar toolbar = (Toolbar)findViewById(R.id.toolbar);
-        setActionBar(toolbar);
-
         // Set up the todo list adapter
-        adapter = new TaskRecyclerAdapter(snackoosList, new View.OnClickListener() {
+        mAdapter = new TaskRecyclerAdapter(snackoosList, new View.OnClickListener() {
             @Override
             public void onClick(View view) {
                 String key = (String) view.getTag();
@@ -79,7 +58,7 @@
         });
 
         RecyclerView recyclerView = (RecyclerView)findViewById(R.id.recycler);
-        recyclerView.setAdapter(adapter);
+        recyclerView.setAdapter(mAdapter);
 
         new ItemTouchHelper(new SwipeableTouchHelperCallback() {
             @Override
@@ -129,13 +108,13 @@
                     mShowDoneMenuItem.setChecked(showDone);
                 }
 
-                int oldSize = adapter.getItemCount();
-                adapter.setShowDone(showDone);
-                int newSize = adapter.getItemCount();
+                int oldSize = mAdapter.getItemCount();
+                mAdapter.setShowDone(showDone);
+                int newSize = mAdapter.getItemCount();
                 if (newSize > oldSize) {
-                    adapter.notifyItemRangeInserted(oldSize, newSize - oldSize);
+                    mAdapter.notifyItemRangeInserted(oldSize, newSize - oldSize);
                 } else {
-                    adapter.notifyItemRangeRemoved(newSize, oldSize - newSize);
+                    mAdapter.notifyItemRangeRemoved(newSize, oldSize - newSize);
                 }
                 setEmptyVisiblity();
             }
@@ -143,7 +122,7 @@
             @Override
             public void onItemAdd(Task item) {
                 int position = snackoosList.insertInOrder(item);
-                adapter.notifyItemInserted(position);
+                mAdapter.notifyItemInserted(position);
                 setEmptyVisiblity();
             }
 
@@ -151,32 +130,20 @@
             public void onItemUpdate(Task item) {
                 int start = snackoosList.findIndexByKey(item.key);
                 int end = snackoosList.updateInOrder(item);
-                adapter.notifyItemMoved(start, end);
-                adapter.notifyItemChanged(end);
+                mAdapter.notifyItemMoved(start, end);
+                mAdapter.notifyItemChanged(end);
                 setEmptyVisiblity();
             }
 
             @Override
             public void onItemDelete(String key) {
                 int position = snackoosList.removeByKey(key);
-                adapter.notifyItemRemoved(position);
+                mAdapter.notifyItemRemoved(position);
                 setEmptyVisiblity();
             }
         };
     }
 
-    // Allow the tests to mock out the main persistence.
-    @VisibleForTesting
-    void setTodoListPersistence(TodoListPersistence p) {
-        mPersistence = p;
-    }
-
-    // Set the visibility based on what the adapter thinks is the visible item count.
-    private void setEmptyVisiblity() {
-        View v = findViewById(R.id.empty);
-        v.setVisibility(adapter.getItemCount() == 0 ? View.VISIBLE : View.GONE);
-    }
-
     public void initiateItemAdd(View view) {
         UIUtil.showAddDialog(this, "New Task", new UIUtil.DialogResponseListener() {
             @Override
@@ -226,7 +193,7 @@
         mShowDoneMenuItem = menu.findItem(R.id.show_done);
 
         // Since the menu item may be inflated too late, set checked to the adapter's value.
-        mShowDoneMenuItem.setChecked(adapter.getShowDone());
+        mShowDoneMenuItem.setChecked(mAdapter.getShowDone());
 
         return true;
     }
diff --git a/app/src/main/java/io/v/todos/TodosAppActivity.java b/app/src/main/java/io/v/todos/TodosAppActivity.java
new file mode 100644
index 0000000..0991262
--- /dev/null
+++ b/app/src/main/java/io/v/todos/TodosAppActivity.java
@@ -0,0 +1,57 @@
+// 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;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.support.annotation.VisibleForTesting;
+import android.support.v7.widget.RecyclerView;
+import android.view.View;
+import android.widget.TextView;
+import android.widget.Toolbar;
+
+import io.v.todos.persistence.Persistence;
+
+public class TodosAppActivity<P extends Persistence, A extends RecyclerView.Adapter<?>>
+        extends Activity {
+    protected P mPersistence;
+    protected A mAdapter;
+
+    protected TextView mEmptyView;
+
+    /**
+     * Allow tests to mock out persistence.
+     */
+    @VisibleForTesting
+    void setPersistence(P persistence) {
+        mPersistence = persistence;
+    }
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_main);
+        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
+        setActionBar(toolbar);
+
+        mEmptyView = (TextView) findViewById(R.id.empty);
+    }
+
+    @Override
+    protected void onDestroy() {
+        if (mPersistence != null) {
+            mPersistence.close();
+            mPersistence = null;
+        }
+        super.onDestroy();
+    }
+
+    /**
+     * Set the visibility based on what the adapter thinks is the visible item count.
+     */
+    protected void setEmptyVisiblity() {
+        mEmptyView.setVisibility(mAdapter.getItemCount() == 0 ? View.VISIBLE : View.GONE);
+    }
+}
diff --git a/app/src/main/res/layout/content_main.xml b/app/src/main/res/layout/content_main.xml
index f1a47c2..c014941 100644
--- a/app/src/main/res/layout/content_main.xml
+++ b/app/src/main/res/layout/content_main.xml
@@ -1,40 +1,45 @@
 <?xml version="1.0" encoding="utf-8"?>
 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:app="http://schemas.android.com/apk/res-auto"
-    xmlns:tools="http://schemas.android.com/tools"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    android:paddingBottom="@dimen/activity_vertical_margin"
-    android:paddingLeft="@dimen/activity_horizontal_margin"
-    android:paddingRight="@dimen/activity_horizontal_margin"
-    android:paddingTop="@dimen/activity_vertical_margin"
-    app:layout_behavior="@string/appbar_scrolling_view_behavior"
-    tools:context="io.v.todos.MainActivity"
-    tools:showIn="@layout/activity_main">
+                xmlns:app="http://schemas.android.com/apk/res-auto"
+                xmlns:tools="http://schemas.android.com/tools"
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"
+                android:paddingBottom="@dimen/activity_vertical_margin"
+                android:paddingLeft="@dimen/activity_horizontal_margin"
+                android:paddingRight="@dimen/activity_horizontal_margin"
+                android:paddingTop="@dimen/activity_vertical_margin"
+                app:layout_behavior="@string/appbar_scrolling_view_behavior"
+                tools:context="io.v.todos.MainActivity"
+                tools:showIn="@layout/activity_main">
 
     <LinearLayout
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:orientation="vertical">
-        <android.support.v7.widget.RecyclerView android:id="@+id/recycler"
+
+        <android.support.v7.widget.RecyclerView
+            android:id="@+id/recycler"
             android:layout_width="match_parent"
             android:layout_height="match_parent"
             android:layout_weight="1"
-            app:layoutManager="LinearLayoutManager" />
-        <TextView android:id="@+id/empty"
+            app:layoutManager="LinearLayoutManager"/>
+
+        <TextView
+            android:id="@+id/empty"
             android:layout_width="match_parent"
             android:layout_height="match_parent"
-            android:background="#FF0000"
-            android:text="No data"/>
+            android:gravity="center"/>
     </LinearLayout>
-    <android.support.design.widget.FloatingActionButton android:id="@+id/fab"
-        android:layout_height="wrap_content"
+
+    <android.support.design.widget.FloatingActionButton
+        android:id="@+id/fab"
         android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
         android:layout_alignParentBottom="true"
         android:layout_alignParentRight="true"
-        app:fabSize="normal"
-        android:src="@android:drawable/ic_input_add"
         android:layout_margin="@dimen/fab_margin"
         android:clickable="true"
-        android:onClick="initiateItemAdd"/>
+        android:onClick="initiateItemAdd"
+        android:src="@android:drawable/ic_input_add"
+        app:fabSize="normal"/>
 </RelativeLayout>
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index da00730..7cd0203 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -7,6 +7,8 @@
     <string name="set_button">Set</string>
     <string name="add_button">Add</string>
     <string name="init_persistence">Initializing&#8230;</string>
+    <string name="no_lists">No todo lists.\nPress \'+\' to add lists.</string>
+    <string name="no_tasks">No tasks.\nPress \'+\' to add tasks.</string>
     <!-- Errors -->
     <string name="err_init">Unable to initialize persistence</string>
     <string name="err_sync">Unable to sync</string>