Merge branch '198-briar-recycler-view' into 'master'

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

See merge request !45
This commit is contained in:
akwizgran
2015-12-31 11:32:58 +00:00
5 changed files with 177 additions and 83 deletions

View File

@@ -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.support.design.widget.FloatingActionButton
android:id="@+id/addContactFAB"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:indeterminate="true"
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"/>
<TextView
android:id="@+id/emptyView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="@dimen/text_size_large"
android:text="@string/no_contacts"
android:visibility="gone"/>
</LinearLayout>
</android.support.design.widget.CoordinatorLayout>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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);
}
}
}
});

View File

@@ -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;
}
}