From f309cb36d4ed1acdaf371cd5e05e2ca1cbc911c8 Mon Sep 17 00:00:00 2001 From: Torsten Grote Date: Tue, 29 Dec 2015 14:23:15 -0200 Subject: [PATCH] Create custom BriarRecyclerView and use it for the contact list. It is a common pattern to have a list with an empty view and a progress bar. This commit introduces a custom BriarRecyclerView and uses it for the contact list. No more manually hiding and showing empty views and progress bars is necessary when using the new BriarRecyclerView instead of RecyclerView. Please note that this conflicts with !44 at the moment and needs to be implemented for !36 once merged. Closes #198 --- .../res/layout/activity_contact_list.xml | 62 +++------- .../res/layout/briar_recycler_view.xml | 30 +++++ briar-android/res/values/strings.xml | 1 + .../android/contact/ContactListActivity.java | 47 +------- .../android/util/BriarRecyclerView.java | 112 ++++++++++++++++++ 5 files changed, 167 insertions(+), 85 deletions(-) create mode 100644 briar-android/res/layout/briar_recycler_view.xml create mode 100644 briar-android/src/org/briarproject/android/util/BriarRecyclerView.java diff --git a/briar-android/res/layout/activity_contact_list.xml b/briar-android/res/layout/activity_contact_list.xml index 02faeff9b..333dac203 100644 --- a/briar-android/res/layout/activity_contact_list.xml +++ b/briar-android/res/layout/activity_contact_list.xml @@ -1,54 +1,28 @@ - + android:layout_height="match_parent"> - + android:layout_height="match_parent"/> - - - - - - - + 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"/> - - - \ No newline at end of file + 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 000000000..d84769f3e --- /dev/null +++ b/briar-android/res/layout/briar_recycler_view.xml @@ -0,0 +1,30 @@ + + + + + + + + + + \ No newline at end of file diff --git a/briar-android/res/values/strings.xml b/briar-android/res/values/strings.xml index 0a7c7b1f0..db7f2bfc4 100644 --- a/briar-android/res/values/strings.xml +++ b/briar-android/res/values/strings.xml @@ -128,4 +128,5 @@ Password recovery is not possible. Do you wish to delete your user, all contacts, and re-register ? Confirm Contact Deletion Are you sure that you want to remove this contact and all messages exchanged with this contact? + No data diff --git a/briar-android/src/org/briarproject/android/contact/ContactListActivity.java b/briar-android/src/org/briarproject/android/contact/ContactListActivity.java index b3fec8962..4b39c1295 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 { @@ -154,15 +133,6 @@ public class ContactListActivity extends BriarActivity private void displayContacts(final List contacts) { runOnUiThread(new Runnable() { public void run() { - if (contacts.size() > 0) { - list.setVisibility(VISIBLE); - empty.setVisibility(GONE); - } else { - list.setVisibility(GONE); - empty.setVisibility(VISIBLE); - } - loading.setVisibility(GONE); - adapter.addAll(contacts); } }); @@ -228,11 +198,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 000000000..891d155ff --- /dev/null +++ b/briar-android/src/org/briarproject/android/util/BriarRecyclerView.java @@ -0,0 +1,112 @@ +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); + + recyclerView.setVisibility(View.INVISIBLE); + emptyView.setVisibility(View.INVISIBLE); + progressBar.setVisibility(View.VISIBLE); + + 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.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); + } + + private 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); + } + } + +}