diff --git a/briar-android/res/layout/activity_contact_list.xml b/briar-android/res/layout/activity_contact_list.xml index 02faeff9bc82ed922eb43829e452a3ff175431eb..333dac2035f20f4006695966a4c3890c68c6373b 100644 --- a/briar-android/res/layout/activity_contact_list.xml +++ b/briar-android/res/layout/activity_contact_list.xml @@ -1,54 +1,28 @@ <?xml version="1.0" encoding="utf-8"?> -<LinearLayout +<android.support.design.widget.CoordinatorLayout 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:id="@+id/coordinatorLayout" android:layout_width="match_parent" - android:layout_height="match_parent" - android:orientation="vertical" - android:gravity="center"> + android:layout_height="match_parent"> - <android.support.design.widget.CoordinatorLayout - android:id="@+id/coordinatorLayout" + <org.briarproject.android.util.BriarRecyclerView + xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/contactList" android:layout_width="match_parent" - android:layout_height="match_parent"> + android:layout_height="match_parent"/> - <android.support.v7.widget.RecyclerView - android:id="@+id/contactList" - android:scrollbars="vertical" - android:layout_width="match_parent" - android:layout_height="match_parent" - tools:listitem="@layout/list_item_contact"/> - - <android.support.design.widget.FloatingActionButton - android:id="@+id/addContactFAB" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_gravity="bottom|end" - android:layout_margin="@dimen/margin_activity_horizontal" - android:src="@drawable/ic_add_white" - app:fabSize="normal" - app:elevation="4dp" - app:layout_anchor="@id/contactList" - app:layout_anchorGravity="bottom|right|end" - app:layout_behavior="org.briarproject.android.util.HideFabOnScrollBehavior"/> - - </android.support.design.widget.CoordinatorLayout> - - <ProgressBar - android:id="@+id/progressBar" - style="?android:attr/progressBarStyleLarge" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:indeterminate="true" - android:visibility="gone"/> - - <TextView - android:id="@+id/emptyView" + <android.support.design.widget.FloatingActionButton + android:id="@+id/addContactFAB" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:textSize="@dimen/text_size_large" - android:text="@string/no_contacts" - android:visibility="gone"/> + android:layout_gravity="bottom|end" + android:layout_margin="@dimen/margin_activity_horizontal" + android:src="@drawable/ic_add_white" + app:fabSize="normal" + app:elevation="4dp" + app:layout_anchor="@id/contactList" + app:layout_anchorGravity="bottom|right|end" + app:layout_behavior="org.briarproject.android.util.HideFabOnScrollBehavior"/> -</LinearLayout> \ No newline at end of file +</android.support.design.widget.CoordinatorLayout> diff --git a/briar-android/res/layout/briar_recycler_view.xml b/briar-android/res/layout/briar_recycler_view.xml new file mode 100644 index 0000000000000000000000000000000000000000..d84769f3e15ce47b19f380d2973da34039cf3ac0 --- /dev/null +++ b/briar-android/res/layout/briar_recycler_view.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8"?> +<RelativeLayout + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <android.support.v7.widget.RecyclerView + android:id="@+id/recyclerView" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:scrollbars="vertical" + tools:listitem="@layout/list_item_contact"/> + + <ProgressBar + android:id="@+id/progressBar" + style="?android:attr/progressBarStyleLarge" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_centerInParent="true"/> + + <TextView + android:id="@+id/emptyView" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_centerInParent="true" + android:textSize="@dimen/text_size_large" + android:text="@string/no_data"/> + +</RelativeLayout> \ No newline at end of file diff --git a/briar-android/res/values/strings.xml b/briar-android/res/values/strings.xml index 0a7c7b1f0e5f97faa642e5295c5634c8dd28b43a..db7f2bfc4c1be306c8c2a197a441f760a8cdb141 100644 --- a/briar-android/res/values/strings.xml +++ b/briar-android/res/values/strings.xml @@ -128,4 +128,5 @@ <string name="dialog_message_lost_password">Password recovery is not possible. Do you wish to delete your user, all contacts, and re-register ?</string> <string name="dialog_title_delete_contact">Confirm Contact Deletion</string> <string name="dialog_message_delete_contact">Are you sure that you want to remove this contact and all messages exchanged with this contact?</string> + <string name="no_data">No data</string> </resources> diff --git a/briar-android/src/org/briarproject/android/contact/ContactListActivity.java b/briar-android/src/org/briarproject/android/contact/ContactListActivity.java index b3fec8962a6a67a2ea8821b88be40a3752e64c72..79188325459bfcc0ae347f82f233eb40e653cacd 100644 --- a/briar-android/src/org/briarproject/android/contact/ContactListActivity.java +++ b/briar-android/src/org/briarproject/android/contact/ContactListActivity.java @@ -4,15 +4,12 @@ import android.content.Intent; import android.os.Bundle; import android.support.design.widget.FloatingActionButton; import android.support.v7.widget.LinearLayoutManager; -import android.support.v7.widget.RecyclerView; import android.view.View; -import android.view.View.OnCreateContextMenuListener; -import android.widget.ProgressBar; -import android.widget.TextView; import org.briarproject.R; import org.briarproject.android.BriarActivity; import org.briarproject.android.invitation.AddContactActivity; +import org.briarproject.android.util.BriarRecyclerView; import org.briarproject.api.contact.Contact; import org.briarproject.api.contact.ContactId; import org.briarproject.api.contact.ContactManager; @@ -38,22 +35,18 @@ import java.util.logging.Logger; import javax.inject.Inject; -import static android.view.View.GONE; -import static android.view.View.VISIBLE; import static java.util.logging.Level.INFO; import static java.util.logging.Level.WARNING; public class ContactListActivity extends BriarActivity - implements OnCreateContextMenuListener, EventListener { + implements EventListener { private static final Logger LOG = Logger.getLogger(ContactListActivity.class.getName()); @Inject private ConnectionRegistry connectionRegistry; - private TextView empty = null; private ContactListAdapter adapter = null; - private RecyclerView list = null; - private ProgressBar loading = null; + private BriarRecyclerView list = null; // Fields that are accessed from background threads must be volatile @Inject private volatile ContactManager contactManager; @@ -67,18 +60,10 @@ public class ContactListActivity extends BriarActivity setContentView(R.layout.activity_contact_list); adapter = new ContactListAdapter(this); - list = (RecyclerView) findViewById(R.id.contactList); + list = (BriarRecyclerView) findViewById(R.id.contactList); list.setLayoutManager(new LinearLayoutManager(this)); list.setAdapter(adapter); - list.setOnCreateContextMenuListener(this); - list.setVisibility(GONE); - - // Show a notice when there are no contacts - empty = (TextView) findViewById(R.id.emptyView); - - // Show a progress bar while the list is loading - loading = (ProgressBar) findViewById(R.id.progressBar); - loading.setVisibility(VISIBLE); + list.setEmptyText(getString(R.string.no_contacts)); // Show a floating action button FloatingActionButton fab = (FloatingActionButton) findViewById( @@ -104,18 +89,12 @@ public class ContactListActivity extends BriarActivity public void onResume() { super.onResume(); eventBus.addListener(this); + loadContacts(); } private void loadContacts() { - runOnUiThread(new Runnable() { - public void run() { - empty.setVisibility(GONE); - list.setVisibility(GONE); - loading.setVisibility(VISIBLE); - } - }); runOnDbThread(new Runnable() { public void run() { try { @@ -155,15 +134,11 @@ public class ContactListActivity extends BriarActivity runOnUiThread(new Runnable() { public void run() { if (contacts.size() > 0) { - list.setVisibility(VISIBLE); - empty.setVisibility(GONE); + adapter.addAll(contacts); } else { - list.setVisibility(GONE); - empty.setVisibility(VISIBLE); + // no contacts to display, make sure progress bar is hidden + list.showData(); } - loading.setVisibility(GONE); - - adapter.addAll(contacts); } }); } @@ -228,11 +203,6 @@ public class ContactListActivity extends BriarActivity ContactListItem item = adapter.findItem(c); if (item != null) { adapter.remove(item); - - if (adapter.isEmpty()) { - empty.setVisibility(VISIBLE); - list.setVisibility(GONE); - } } } }); diff --git a/briar-android/src/org/briarproject/android/util/BriarRecyclerView.java b/briar-android/src/org/briarproject/android/util/BriarRecyclerView.java new file mode 100644 index 0000000000000000000000000000000000000000..4e50c1d5d53a55ff8cca34f58cf66d02200ecab9 --- /dev/null +++ b/briar-android/src/org/briarproject/android/util/BriarRecyclerView.java @@ -0,0 +1,119 @@ +package org.briarproject.android.util; + +import android.content.Context; +import android.support.v7.widget.RecyclerView; +import android.util.AttributeSet; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.FrameLayout; +import android.widget.ProgressBar; +import android.widget.TextView; + +import org.briarproject.R; + +public class BriarRecyclerView extends FrameLayout { + + private RecyclerView recyclerView; + private TextView emptyView; + private ProgressBar progressBar; + private RecyclerView.AdapterDataObserver emptyObserver; + + public BriarRecyclerView(Context context) { + super(context); + + initViews(); + } + + public BriarRecyclerView(Context context, AttributeSet attrs) { + super(context, attrs); + + initViews(); + } + + public BriarRecyclerView(Context context, AttributeSet attrs, + int defStyle) { + super(context, attrs, defStyle); + + initViews(); + } + + private void initViews() { + if (isInEditMode()) { + return; + } + + View v = LayoutInflater.from(getContext()).inflate( + R.layout.briar_recycler_view, this, true); + + recyclerView = (RecyclerView) v.findViewById(R.id.recyclerView); + emptyView = (TextView) v.findViewById(R.id.emptyView); + progressBar = (ProgressBar) v.findViewById(R.id.progressBar); + + showProgressBar(); + + emptyObserver = new RecyclerView.AdapterDataObserver() { + @Override + public void onChanged() { + showData(); + } + + @Override + public void onItemRangeInserted(int positionStart, int itemCount) { + super.onItemRangeInserted(positionStart, itemCount); + onChanged(); + } + }; + } + + public void setLayoutManager(RecyclerView.LayoutManager layout) { + recyclerView.setLayoutManager(layout); + } + + public void setAdapter(RecyclerView.Adapter adapter) { + RecyclerView.Adapter oldAdapter = recyclerView.getAdapter(); + if (oldAdapter != null) { + oldAdapter.unregisterAdapterDataObserver(emptyObserver); + } + + recyclerView.setAdapter(adapter); + + if (adapter != null) { + adapter.registerAdapterDataObserver(emptyObserver); + + if (adapter.getItemCount() > 0) { + // only show data if adapter has data already + // otherwise progress bar is shown + emptyObserver.onChanged(); + } + } + } + + public void setEmptyText(String text) { + emptyView.setText(text); + } + + public void showProgressBar() { + recyclerView.setVisibility(View.INVISIBLE); + emptyView.setVisibility(View.INVISIBLE); + progressBar.setVisibility(View.VISIBLE); + } + + public void showData() { + RecyclerView.Adapter<?> adapter = recyclerView.getAdapter(); + if (adapter != null) { + if (adapter.getItemCount() == 0) { + emptyView.setVisibility(View.VISIBLE); + recyclerView.setVisibility(View.INVISIBLE); + } else { + emptyView.setVisibility(View.INVISIBLE); + recyclerView.setVisibility(View.VISIBLE); + } + progressBar.setVisibility(View.INVISIBLE); + } + } + + public RecyclerView getRecyclerView() { + return recyclerView; + } + +}