Use a RecyclerView for the Contact List
@@ -7,6 +7,7 @@ dependencies {
|
||||
compile project(':briar-core')
|
||||
compile fileTree(dir: 'libs', include: '*.jar')
|
||||
compile "com.android.support:appcompat-v7:23.1.1"
|
||||
compile 'com.android.support:recyclerview-v7:23.1.1'
|
||||
}
|
||||
|
||||
android {
|
||||
|
||||
|
Before Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 1.0 KiB |
|
Before Width: | Height: | Size: 484 B |
|
Before Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 2.1 KiB |
26
briar-android/res/drawable/contact_connected.xml
Normal file
@@ -0,0 +1,26 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24"
|
||||
android:alpha="0.56">
|
||||
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M12,2 C6.48,2,2,6.48,2,12 S6.48,22,12,22 S22,17.52,22,12 S17.52,2,12,2 Z M12,20
|
||||
C7.58,20,4,16.42,4,12 S7.58,4,12,4 S20,7.58,20,12 S16.42,20,12,20 Z" />
|
||||
<path
|
||||
android:pathData="M0,0 L24,0 L24,24 L0,24 Z" />
|
||||
<path
|
||||
android:fillColor="#95d220"
|
||||
android:strokeWidth="0.76779664"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeLineCap="round"
|
||||
android:pathData="M10.8972,19.9503 C6.5514,19.3493,3.43091,15.2154,4.0625,10.896
|
||||
C4.55452,7.53099,7.09451,4.8236,10.394,4.14714
|
||||
C14.2569,3.35517,18.1698,5.54347,19.5236,9.25295
|
||||
C20.0698,10.7495,20.1616,12.4612,19.777,13.9758
|
||||
C19.5457,14.8864,18.8106,16.3388,18.2072,17.0771
|
||||
C16.4904,19.1779,13.581,20.3215,10.8973,19.9503 Z" />
|
||||
</vector>
|
||||
5
briar-android/res/drawable/contact_disconnected.xml
Normal file
@@ -0,0 +1,5 @@
|
||||
<vector android:alpha="0.56" android:height="24dp"
|
||||
android:viewportHeight="24.0" android:viewportWidth="24.0"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="#FF000000" android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zm0,18c-4.42,0 -8,-3.58 -8,-8s3.58,-8 8,-8 8,3.58 8,8 -3.58,8 -8,8z"/>
|
||||
</vector>
|
||||
5
briar-android/res/drawable/social_add_person.xml
Normal file
@@ -0,0 +1,5 @@
|
||||
<vector android:alpha="0.56" android:height="24dp"
|
||||
android:viewportHeight="24.0" android:viewportWidth="24.0"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="#FF000000" android:pathData="M15,12c2.21,0 4,-1.79 4,-4s-1.79,-4 -4,-4 -4,1.79 -4,4 1.79,4 4,4zm-9,-2V7H4v3H1v2h3v3h2v-3h3v-2H6zm9,4c-2.67,0 -8,1.34 -8,4v2h16v-2c0,-2.66 -5.33,-4 -8,-4z"/>
|
||||
</vector>
|
||||
5
briar-android/res/drawable/social_remove_person.xml
Normal file
@@ -0,0 +1,5 @@
|
||||
<vector android:alpha="0.56" android:height="24dp"
|
||||
android:viewportHeight="24.0" android:viewportWidth="24.0"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="#FF000000" android:pathData="M6,19c0,1.1 0.9,2 2,2h8c1.1,0 2,-0.9 2,-2V7H6v12zM19,4h-3.5l-1,-1h-5l-1,1H5v2h14V4z"/>
|
||||
</vector>
|
||||
33
briar-android/res/layout/activity_contact_list.xml
Normal file
@@ -0,0 +1,33 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout
|
||||
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:orientation="vertical"
|
||||
android:gravity="center">
|
||||
|
||||
<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"/>
|
||||
|
||||
<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:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="@dimen/text_size_large"
|
||||
android:text="@string/no_contacts"
|
||||
android:visibility="gone"/>
|
||||
|
||||
</LinearLayout>
|
||||
51
briar-android/res/layout/list_item_contact.xml
Normal file
@@ -0,0 +1,51 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?attr/selectableItemBackground"
|
||||
android:padding="12dp">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/bulbView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginRight="@dimen/margin_medium"
|
||||
android:layout_marginEnd="@dimen/margin_medium"
|
||||
android:layout_gravity="center_vertical"
|
||||
tools:src="@drawable/contact_disconnected"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/nameView"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginRight="@dimen/margin_small"
|
||||
android:layout_marginEnd="@dimen/margin_small"
|
||||
android:textSize="@dimen/text_size_medium"
|
||||
android:gravity="center_vertical"
|
||||
tools:text="This is a name of a contact. It can be quite long."/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/dateView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginRight="@dimen/margin_small"
|
||||
android:layout_marginEnd="@dimen/margin_small"
|
||||
android:gravity="center_vertical"
|
||||
android:textColor="@color/no_private_messages"
|
||||
tools:text="Dec 24"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<View style="@style/Divider.Horizontal"/>
|
||||
|
||||
</LinearLayout>
|
||||
@@ -5,10 +5,7 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1px"
|
||||
android:background="@color/horizontal_border"/>
|
||||
<View style="@style/Divider.Horizontal"/>
|
||||
|
||||
<GridView
|
||||
android:id="@+id/transportsView"
|
||||
|
||||
12
briar-android/res/menu/contact_actions.xml
Normal file
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<item
|
||||
android:id="@+id/action_social_remove_person"
|
||||
android:icon="@drawable/social_remove_person"
|
||||
app:showAsAction="always"
|
||||
android:title="@string/delete_contact"/>
|
||||
|
||||
</menu>
|
||||
12
briar-android/res/menu/contact_list_actions.xml
Normal file
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<item
|
||||
android:id="@+id/action_social_add_person"
|
||||
android:icon="@drawable/social_add_person"
|
||||
app:showAsAction="always"
|
||||
android:title="@string/add_contact_title"/>
|
||||
|
||||
</menu>
|
||||
@@ -126,4 +126,6 @@
|
||||
<!-- Dialogs -->
|
||||
<string name="dialog_title_lost_password">Lost password</string>
|
||||
<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>
|
||||
</resources>
|
||||
|
||||
@@ -33,4 +33,13 @@
|
||||
<item name="android:textSize">@dimen/text_size_small</item>
|
||||
<item name="android:textColor">@android:color/primary_text_light</item>
|
||||
</style>
|
||||
|
||||
<style name="Divider">
|
||||
<item name="android:background">?android:attr/listDivider</item>
|
||||
</style>
|
||||
|
||||
<style name="Divider.Horizontal">
|
||||
<item name="android:layout_width">match_parent</item>
|
||||
<item name="android:layout_height">1px</item>
|
||||
</style>
|
||||
</resources>
|
||||
@@ -1,28 +1,20 @@
|
||||
package org.briarproject.android.contact;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.os.Bundle;
|
||||
import android.view.ContextMenu;
|
||||
import android.view.ContextMenu.ContextMenuInfo;
|
||||
import android.support.v7.widget.LinearLayoutManager;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.view.View.OnCreateContextMenuListener;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.AdapterView.AdapterContextMenuInfo;
|
||||
import android.widget.AdapterView.OnItemClickListener;
|
||||
import android.widget.ImageButton;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.ListView;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.briarproject.R;
|
||||
import org.briarproject.android.BriarActivity;
|
||||
import org.briarproject.android.invitation.AddContactActivity;
|
||||
import org.briarproject.android.util.HorizontalBorder;
|
||||
import org.briarproject.android.util.ListLoadingProgressBar;
|
||||
import org.briarproject.api.contact.Contact;
|
||||
import org.briarproject.api.contact.ContactId;
|
||||
import org.briarproject.api.contact.ContactManager;
|
||||
@@ -36,43 +28,34 @@ import org.briarproject.api.event.Event;
|
||||
import org.briarproject.api.event.EventBus;
|
||||
import org.briarproject.api.event.EventListener;
|
||||
import org.briarproject.api.event.MessageAddedEvent;
|
||||
import org.briarproject.api.identity.AuthorId;
|
||||
import org.briarproject.api.messaging.MessagingManager;
|
||||
import org.briarproject.api.messaging.PrivateMessageHeader;
|
||||
import org.briarproject.api.plugins.ConnectionRegistry;
|
||||
import org.briarproject.api.sync.GroupId;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static android.view.Gravity.CENTER;
|
||||
import static android.view.Gravity.CENTER_HORIZONTAL;
|
||||
import static android.view.Menu.NONE;
|
||||
import static android.view.View.GONE;
|
||||
import static android.view.View.VISIBLE;
|
||||
import static android.widget.LinearLayout.VERTICAL;
|
||||
import static android.widget.Toast.LENGTH_SHORT;
|
||||
import static java.util.logging.Level.INFO;
|
||||
import static java.util.logging.Level.WARNING;
|
||||
import static org.briarproject.android.util.CommonLayoutParams.MATCH_MATCH;
|
||||
import static org.briarproject.android.util.CommonLayoutParams.MATCH_WRAP;
|
||||
import static org.briarproject.android.util.CommonLayoutParams.MATCH_WRAP_1;
|
||||
|
||||
public class ContactListActivity extends BriarActivity
|
||||
implements OnClickListener, OnItemClickListener, OnCreateContextMenuListener,
|
||||
EventListener {
|
||||
implements OnCreateContextMenuListener, EventListener {
|
||||
|
||||
private static final int MENU_ITEM_DELETE = 1;
|
||||
private static final Logger LOG =
|
||||
Logger.getLogger(ContactListActivity.class.getName());
|
||||
|
||||
@Inject private ConnectionRegistry connectionRegistry;
|
||||
private TextView empty = null;
|
||||
private ContactListAdapter adapter = null;
|
||||
private ListView list = null;
|
||||
private ListLoadingProgressBar loading = null;
|
||||
private RecyclerView list = null;
|
||||
private ProgressBar loading = null;
|
||||
|
||||
// Fields that are accessed from background threads must be volatile
|
||||
@Inject private volatile ContactManager contactManager;
|
||||
@@ -82,47 +65,28 @@ EventListener {
|
||||
@Override
|
||||
public void onCreate(Bundle state) {
|
||||
super.onCreate(state);
|
||||
LinearLayout layout = new LinearLayout(this);
|
||||
layout.setLayoutParams(MATCH_MATCH);
|
||||
layout.setOrientation(VERTICAL);
|
||||
layout.setGravity(CENTER_HORIZONTAL);
|
||||
|
||||
empty = new TextView(this);
|
||||
empty.setLayoutParams(MATCH_WRAP_1);
|
||||
empty.setGravity(CENTER);
|
||||
empty.setTextSize(18);
|
||||
empty.setText(R.string.no_contacts);
|
||||
empty.setVisibility(GONE);
|
||||
layout.addView(empty);
|
||||
setContentView(R.layout.activity_contact_list);
|
||||
|
||||
adapter = new ContactListAdapter(this);
|
||||
list = new ListView(this);
|
||||
list.setLayoutParams(MATCH_WRAP_1);
|
||||
list = (RecyclerView) findViewById(R.id.contactList);
|
||||
list.setLayoutManager(new LinearLayoutManager(this));
|
||||
list.setAdapter(adapter);
|
||||
list.setOnItemClickListener(this);
|
||||
list.setOnCreateContextMenuListener(this);
|
||||
list.setVisibility(GONE);
|
||||
layout.addView(list);
|
||||
|
||||
// Show a notice when there are no contacts
|
||||
empty = (TextView) findViewById(R.id.emptyView);
|
||||
|
||||
// Show a progress bar while the list is loading
|
||||
loading = new ListLoadingProgressBar(this);
|
||||
layout.addView(loading);
|
||||
loading = (ProgressBar) findViewById(R.id.progressBar);
|
||||
loading.setVisibility(VISIBLE);
|
||||
}
|
||||
|
||||
layout.addView(new HorizontalBorder(this));
|
||||
|
||||
LinearLayout footer = new LinearLayout(this);
|
||||
footer.setLayoutParams(MATCH_WRAP);
|
||||
footer.setGravity(CENTER);
|
||||
Resources res = getResources();
|
||||
footer.setBackgroundColor(res.getColor(R.color.button_bar_background));
|
||||
ImageButton addContactButton = new ImageButton(this);
|
||||
addContactButton.setBackgroundResource(0);
|
||||
addContactButton.setImageResource(R.drawable.social_add_person);
|
||||
addContactButton.setOnClickListener(this);
|
||||
footer.addView(addContactButton);
|
||||
layout.addView(footer);
|
||||
|
||||
setContentView(layout);
|
||||
@Override
|
||||
public void onPause() {
|
||||
super.onPause();
|
||||
eventBus.removeListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -132,12 +96,47 @@ EventListener {
|
||||
loadContacts();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
// Inflate the menu items for use in the action bar
|
||||
MenuInflater inflater = getMenuInflater();
|
||||
inflater.inflate(R.menu.contact_list_actions, menu);
|
||||
|
||||
// adapt icon color to dark action bar
|
||||
menu.findItem(R.id.action_social_add_person).getIcon().setColorFilter(
|
||||
getResources().getColor(R.color.action_bar_text),
|
||||
PorterDuff.Mode.SRC_IN);
|
||||
|
||||
return super.onCreateOptionsMenu(menu);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(final MenuItem item) {
|
||||
// Handle presses on the action bar items
|
||||
switch (item.getItemId()) {
|
||||
case R.id.action_social_add_person:
|
||||
startActivity(new Intent(this, AddContactActivity.class));
|
||||
return true;
|
||||
default:
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void loadContacts() {
|
||||
clearContacts();
|
||||
runOnUiThread(new Runnable() {
|
||||
public void run() {
|
||||
empty.setVisibility(GONE);
|
||||
list.setVisibility(GONE);
|
||||
loading.setVisibility(VISIBLE);
|
||||
}
|
||||
});
|
||||
runOnDbThread(new Runnable() {
|
||||
public void run() {
|
||||
try {
|
||||
long now = System.currentTimeMillis();
|
||||
List<ContactListItem> contacts =
|
||||
new ArrayList<ContactListItem>();
|
||||
for (Contact c : contactManager.getContacts()) {
|
||||
try {
|
||||
ContactId id = c.getId();
|
||||
@@ -145,15 +144,20 @@ EventListener {
|
||||
messagingManager.getConversationId(id);
|
||||
Collection<PrivateMessageHeader> headers =
|
||||
messagingManager.getMessageHeaders(id);
|
||||
displayContact(c, conversation, headers);
|
||||
|
||||
boolean connected =
|
||||
connectionRegistry.isConnected(c.getId());
|
||||
contacts.add(new ContactListItem(c, connected,
|
||||
conversation,
|
||||
headers));
|
||||
} catch (NoSuchContactException e) {
|
||||
// Continue
|
||||
}
|
||||
}
|
||||
displayContacts(contacts);
|
||||
long duration = System.currentTimeMillis() - now;
|
||||
if (LOG.isLoggable(INFO))
|
||||
LOG.info("Full load took " + duration + " ms");
|
||||
hideProgressBar();
|
||||
} catch (DbException e) {
|
||||
if (LOG.isLoggable(WARNING))
|
||||
LOG.log(WARNING, e.toString(), e);
|
||||
@@ -162,110 +166,19 @@ EventListener {
|
||||
});
|
||||
}
|
||||
|
||||
private void clearContacts() {
|
||||
private void displayContacts(final List<ContactListItem> contacts) {
|
||||
runOnUiThread(new Runnable() {
|
||||
public void run() {
|
||||
empty.setVisibility(GONE);
|
||||
list.setVisibility(GONE);
|
||||
loading.setVisibility(VISIBLE);
|
||||
adapter.clear();
|
||||
adapter.notifyDataSetChanged();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void displayContact(final Contact c, final GroupId conversation,
|
||||
final Collection<PrivateMessageHeader> headers) {
|
||||
runOnUiThread(new Runnable() {
|
||||
public void run() {
|
||||
list.setVisibility(VISIBLE);
|
||||
loading.setVisibility(GONE);
|
||||
boolean connected = connectionRegistry.isConnected(c.getId());
|
||||
// Remove the old item, if any
|
||||
ContactListItem item = findItem(c.getId());
|
||||
if (item != null) adapter.remove(item);
|
||||
// Add a new item
|
||||
adapter.add(new ContactListItem(c, connected, conversation,
|
||||
headers));
|
||||
adapter.sort(ContactListItemComparator.INSTANCE);
|
||||
adapter.notifyDataSetChanged();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void hideProgressBar() {
|
||||
runOnUiThread(new Runnable() {
|
||||
public void run() {
|
||||
if (adapter.isEmpty()) empty.setVisibility(VISIBLE);
|
||||
else list.setVisibility(VISIBLE);
|
||||
loading.setVisibility(GONE);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private ContactListItem findItem(ContactId c) {
|
||||
int count = adapter.getCount();
|
||||
for (int i = 0; i < count; i++) {
|
||||
ContactListItem item = adapter.getItem(i);
|
||||
if (item.getContact().getId().equals(c)) return item;
|
||||
}
|
||||
return null; // Not found
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
super.onPause();
|
||||
eventBus.removeListener(this);
|
||||
}
|
||||
|
||||
public void onClick(View view) {
|
||||
startActivity(new Intent(this, AddContactActivity.class));
|
||||
}
|
||||
|
||||
public void onItemClick(AdapterView<?> parent, View view, int position,
|
||||
long id) {
|
||||
ContactListItem item = adapter.getItem(position);
|
||||
ContactId contactId = item.getContact().getId();
|
||||
String contactName = item.getContact().getAuthor().getName();
|
||||
GroupId groupId = item.getConversationId();
|
||||
AuthorId localAuthorId = item.getContact().getLocalAuthorId();
|
||||
Intent i = new Intent(this, ConversationActivity.class);
|
||||
i.putExtra("briar.CONTACT_ID", contactId.getInt());
|
||||
i.putExtra("briar.CONTACT_NAME", contactName);
|
||||
i.putExtra("briar.GROUP_ID", groupId.getBytes());
|
||||
i.putExtra("briar.LOCAL_AUTHOR_ID", localAuthorId.getBytes());
|
||||
startActivity(i);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreateContextMenu(ContextMenu menu, View view,
|
||||
ContextMenu.ContextMenuInfo info) {
|
||||
String delete = getString(R.string.delete_contact);
|
||||
menu.add(NONE, MENU_ITEM_DELETE, NONE, delete);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onContextItemSelected(MenuItem menuItem) {
|
||||
if (menuItem.getItemId() == MENU_ITEM_DELETE) {
|
||||
ContextMenuInfo info = menuItem.getMenuInfo();
|
||||
int position = ((AdapterContextMenuInfo) info).position;
|
||||
ContactListItem item = adapter.getItem(position);
|
||||
removeContact(item.getContact().getId());
|
||||
String deleted = getString(R.string.contact_deleted_toast);
|
||||
Toast.makeText(this, deleted, LENGTH_SHORT).show();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void removeContact(final ContactId c) {
|
||||
runOnDbThread(new Runnable() {
|
||||
public void run() {
|
||||
try {
|
||||
contactManager.removeContact(c);
|
||||
} catch (DbException e) {
|
||||
if (LOG.isLoggable(WARNING))
|
||||
LOG.log(WARNING, e.toString(), e);
|
||||
if(contacts.size() > 0) {
|
||||
list.setVisibility(VISIBLE);
|
||||
empty.setVisibility(GONE);
|
||||
} else {
|
||||
list.setVisibility(GONE);
|
||||
empty.setVisibility(VISIBLE);
|
||||
}
|
||||
loading.setVisibility(GONE);
|
||||
|
||||
adapter.addAll(contacts);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -314,7 +227,7 @@ EventListener {
|
||||
final Collection<PrivateMessageHeader> headers) {
|
||||
runOnUiThread(new Runnable() {
|
||||
public void run() {
|
||||
ContactListItem item = findItem(c);
|
||||
ContactListItem item = adapter.findItem(c);
|
||||
if (item != null) {
|
||||
item.setHeaders(headers);
|
||||
adapter.notifyDataSetChanged();
|
||||
@@ -326,10 +239,10 @@ EventListener {
|
||||
private void removeItem(final ContactId c) {
|
||||
runOnUiThread(new Runnable() {
|
||||
public void run() {
|
||||
ContactListItem item = findItem(c);
|
||||
ContactListItem item = adapter.findItem(c);
|
||||
if (item != null) {
|
||||
adapter.remove(item);
|
||||
adapter.notifyDataSetChanged();
|
||||
|
||||
if (adapter.isEmpty()) {
|
||||
empty.setVisibility(VISIBLE);
|
||||
list.setVisibility(GONE);
|
||||
@@ -342,7 +255,7 @@ EventListener {
|
||||
private void setConnected(final ContactId c, final boolean connected) {
|
||||
runOnUiThread(new Runnable() {
|
||||
public void run() {
|
||||
ContactListItem item = findItem(c);
|
||||
ContactListItem item = adapter.findItem(c);
|
||||
if (item != null) {
|
||||
item.setConnected(connected);
|
||||
adapter.notifyDataSetChanged();
|
||||
|
||||
@@ -1,80 +1,194 @@
|
||||
package org.briarproject.android.contact;
|
||||
|
||||
import static android.text.TextUtils.TruncateAt.END;
|
||||
import static android.view.Gravity.CENTER_VERTICAL;
|
||||
import static android.widget.LinearLayout.HORIZONTAL;
|
||||
import static org.briarproject.android.util.CommonLayoutParams.WRAP_WRAP_1;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import org.briarproject.R;
|
||||
import org.briarproject.android.util.LayoutUtils;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.res.Resources;
|
||||
import android.support.v7.util.SortedList;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.text.format.DateUtils;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
class ContactListAdapter extends ArrayAdapter<ContactListItem> {
|
||||
import org.briarproject.R;
|
||||
import org.briarproject.api.contact.ContactId;
|
||||
import org.briarproject.api.identity.AuthorId;
|
||||
import org.briarproject.api.sync.GroupId;
|
||||
|
||||
private final int pad;
|
||||
import java.util.List;
|
||||
|
||||
ContactListAdapter(Context ctx) {
|
||||
super(ctx, android.R.layout.simple_expandable_list_item_1,
|
||||
new ArrayList<ContactListItem>());
|
||||
pad = LayoutUtils.getPadding(ctx);
|
||||
public class ContactListAdapter
|
||||
extends RecyclerView.Adapter<ContactListAdapter.ContactHolder> {
|
||||
|
||||
private SortedList<ContactListItem> contacts =
|
||||
new SortedList<ContactListItem>(ContactListItem.class,
|
||||
new SortedList.Callback<ContactListItem>() {
|
||||
@Override
|
||||
public void onInserted(int position, int count) {
|
||||
notifyItemRangeInserted(position, count);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChanged(int position, int count) {
|
||||
notifyItemRangeChanged(position, count);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMoved(int fromPosition, int toPosition) {
|
||||
notifyItemMoved(fromPosition, toPosition);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRemoved(int position, int count) {
|
||||
notifyItemRangeRemoved(position, count);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compare(ContactListItem c1,
|
||||
ContactListItem c2) {
|
||||
return (int) (c1.getTimestamp() -
|
||||
c2.getTimestamp());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean areItemsTheSame(ContactListItem c1,
|
||||
ContactListItem c2) {
|
||||
return c1.getContact().getId().equals(c2.getContact().getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean areContentsTheSame(ContactListItem c1,
|
||||
ContactListItem c2) {
|
||||
return c1.equals(c2);
|
||||
}
|
||||
});
|
||||
private Context ctx;
|
||||
|
||||
public ContactListAdapter(Context context) {
|
||||
ctx = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(int position, View convertView, ViewGroup parent) {
|
||||
ContactListItem item = getItem(position);
|
||||
Context ctx = getContext();
|
||||
public ContactHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
|
||||
View v = LayoutInflater.from(viewGroup.getContext())
|
||||
.inflate(R.layout.list_item_contact, viewGroup, false);
|
||||
|
||||
return new ContactHolder(v);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(final ContactHolder ui, final int position) {
|
||||
final ContactListItem item = getItem(position);
|
||||
Resources res = ctx.getResources();
|
||||
|
||||
LinearLayout layout = new LinearLayout(ctx);
|
||||
layout.setOrientation(HORIZONTAL);
|
||||
layout.setGravity(CENTER_VERTICAL);
|
||||
int unread = item.getUnreadCount();
|
||||
if (unread > 0)
|
||||
layout.setBackgroundColor(res.getColor(R.color.unread_background));
|
||||
|
||||
ImageView bulb = new ImageView(ctx);
|
||||
bulb.setPadding(pad, pad, pad, pad);
|
||||
if (item.isConnected())
|
||||
bulb.setImageResource(R.drawable.contact_connected);
|
||||
else bulb.setImageResource(R.drawable.contact_disconnected);
|
||||
layout.addView(bulb);
|
||||
|
||||
TextView name = new TextView(ctx);
|
||||
name.setLayoutParams(WRAP_WRAP_1);
|
||||
name.setTextSize(18);
|
||||
name.setSingleLine();
|
||||
name.setEllipsize(END);
|
||||
name.setPadding(0, pad, pad, pad);
|
||||
String contactName = item.getContact().getAuthor().getName();
|
||||
if (unread > 0) name.setText(contactName + " (" + unread + ")");
|
||||
else name.setText(contactName);
|
||||
layout.addView(name);
|
||||
|
||||
if (item.isEmpty()) {
|
||||
TextView noMessages = new TextView(ctx);
|
||||
noMessages.setPadding(pad, pad, pad, pad);
|
||||
noMessages.setTextColor(res.getColor(R.color.no_private_messages));
|
||||
noMessages.setText(R.string.no_private_messages);
|
||||
layout.addView(noMessages);
|
||||
} else {
|
||||
TextView date = new TextView(ctx);
|
||||
date.setPadding(pad, pad, pad, pad);
|
||||
long timestamp = item.getTimestamp();
|
||||
date.setText(DateUtils.getRelativeTimeSpanString(ctx, timestamp));
|
||||
layout.addView(date);
|
||||
if (unread > 0) {
|
||||
ui.layout.setBackgroundColor(
|
||||
res.getColor(R.color.unread_background));
|
||||
}
|
||||
|
||||
return layout;
|
||||
if (item.isConnected()) {
|
||||
ui.bulb.setImageResource(R.drawable.contact_connected);
|
||||
} else {
|
||||
ui.bulb.setImageResource(R.drawable.contact_disconnected);
|
||||
}
|
||||
|
||||
String contactName = item.getContact().getAuthor().getName();
|
||||
if (unread > 0) {
|
||||
ui.name.setText(contactName + " (" + unread + ")");
|
||||
} else {
|
||||
ui.name.setText(contactName);
|
||||
}
|
||||
|
||||
if (item.isEmpty()) {
|
||||
ui.date.setText(R.string.no_private_messages);
|
||||
} else {
|
||||
long timestamp = item.getTimestamp();
|
||||
ui.date.setText(
|
||||
DateUtils.getRelativeTimeSpanString(ctx, timestamp));
|
||||
}
|
||||
|
||||
ui.layout.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
ContactId contactId = item.getContact().getId();
|
||||
String contactName = item.getContact().getAuthor().getName();
|
||||
GroupId groupId = item.getConversationId();
|
||||
AuthorId localAuthorId = item.getContact().getLocalAuthorId();
|
||||
|
||||
Intent i = new Intent(ctx, ConversationActivity.class);
|
||||
i.putExtra("briar.CONTACT_ID", contactId.getInt());
|
||||
i.putExtra("briar.CONTACT_NAME", contactName);
|
||||
i.putExtra("briar.GROUP_ID", groupId.getBytes());
|
||||
i.putExtra("briar.LOCAL_AUTHOR_ID", localAuthorId.getBytes());
|
||||
|
||||
ctx.startActivity(i);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return contacts == null ? 0 : contacts.size();
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return contacts == null || contacts.size() == 0;
|
||||
}
|
||||
|
||||
public ContactListItem getItem(int position) {
|
||||
if (position == -1 || contacts.size() <= position) {
|
||||
return null; // Not found
|
||||
}
|
||||
return contacts.get(position);
|
||||
}
|
||||
|
||||
public ContactListItem findItem(ContactId c) {
|
||||
int count = getItemCount();
|
||||
for (int i = 0; i < count; i++) {
|
||||
ContactListItem item = getItem(i);
|
||||
if (item.getContact().getId().equals(c)) return item;
|
||||
}
|
||||
return null; // Not found
|
||||
}
|
||||
|
||||
public void addAll(final List<ContactListItem> contacts) {
|
||||
this.contacts.addAll(contacts);
|
||||
}
|
||||
|
||||
public void add(final ContactListItem contact) {
|
||||
this.contacts.add(contact);
|
||||
}
|
||||
|
||||
public void remove(final ContactListItem contact) {
|
||||
this.contacts.remove(contact);
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
contacts.beginBatchedUpdates();
|
||||
|
||||
while(contacts.size() != 0) {
|
||||
contacts.removeItemAt(0);
|
||||
}
|
||||
|
||||
contacts.endBatchedUpdates();
|
||||
}
|
||||
|
||||
public static class ContactHolder extends RecyclerView.ViewHolder {
|
||||
public ViewGroup layout;
|
||||
public ImageView bulb;
|
||||
public TextView name;
|
||||
public TextView date;
|
||||
|
||||
public ContactHolder(View v) {
|
||||
super(v);
|
||||
|
||||
layout = (ViewGroup) v;
|
||||
bulb = (ImageView) v.findViewById(R.id.bulbView);
|
||||
name = (TextView) v.findViewById(R.id.nameView);
|
||||
date = (TextView) v.findViewById(R.id.dateView);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,16 @@
|
||||
package org.briarproject.android.contact;
|
||||
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
import android.os.Bundle;
|
||||
import android.support.v7.app.ActionBar;
|
||||
import android.support.v7.app.AlertDialog;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.view.ViewGroup;
|
||||
@@ -15,6 +21,7 @@ import android.widget.ImageButton;
|
||||
import android.widget.ListView;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.briarproject.R;
|
||||
import org.briarproject.android.BriarActivity;
|
||||
@@ -66,6 +73,7 @@ import javax.inject.Inject;
|
||||
|
||||
import static android.view.View.GONE;
|
||||
import static android.view.View.VISIBLE;
|
||||
import static android.widget.Toast.LENGTH_SHORT;
|
||||
import static java.util.logging.Level.INFO;
|
||||
import static java.util.logging.Level.WARNING;
|
||||
import static org.briarproject.android.contact.ReadPrivateMessageActivity.RESULT_PREV_NEXT;
|
||||
@@ -161,6 +169,33 @@ implements EventListener, OnClickListener, OnItemClickListener {
|
||||
loadHeaders();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
// Inflate the menu items for use in the action bar
|
||||
MenuInflater inflater = getMenuInflater();
|
||||
inflater.inflate(R.menu.contact_actions, menu);
|
||||
|
||||
// adapt icon color to dark action bar
|
||||
menu.findItem(R.id.action_social_remove_person).getIcon().setColorFilter(
|
||||
getResources().getColor(R.color.action_bar_text),
|
||||
PorterDuff.Mode.SRC_IN);
|
||||
|
||||
return super.onCreateOptionsMenu(menu);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(final MenuItem item) {
|
||||
// Handle presses on the action bar items
|
||||
switch (item.getItemId()) {
|
||||
case R.id.action_social_remove_person:
|
||||
askToRemoveContact();
|
||||
|
||||
return true;
|
||||
default:
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
}
|
||||
|
||||
private void loadContactAndGroup() {
|
||||
runOnDbThread(new Runnable() {
|
||||
public void run() {
|
||||
@@ -478,4 +513,59 @@ implements EventListener, OnClickListener, OnItemClickListener {
|
||||
i.putExtra("briar.POSITION", position);
|
||||
startActivityForResult(i, REQUEST_READ);
|
||||
}
|
||||
|
||||
private void askToRemoveContact() {
|
||||
runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
DialogInterface.OnClickListener okListener =
|
||||
new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog,
|
||||
int which) {
|
||||
removeContact();
|
||||
}
|
||||
};
|
||||
|
||||
AlertDialog.Builder builder =
|
||||
new AlertDialog.Builder(ConversationActivity.this);
|
||||
builder.setTitle(
|
||||
getString(R.string.dialog_title_delete_contact));
|
||||
builder.setMessage(
|
||||
getString(R.string.dialog_message_delete_contact));
|
||||
builder.setPositiveButton(android.R.string.ok, okListener);
|
||||
builder.setNegativeButton(android.R.string.cancel, null);
|
||||
builder.show();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void removeContact() {
|
||||
runOnDbThread(new Runnable() {
|
||||
public void run() {
|
||||
try {
|
||||
contactManager.removeContact(contactId);
|
||||
} catch (DbException e) {
|
||||
if (LOG.isLoggable(WARNING))
|
||||
LOG.log(WARNING, e.toString(), e);
|
||||
} finally {
|
||||
finishAfterContactRemoved();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void finishAfterContactRemoved() {
|
||||
runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
String deleted = getString(R.string.contact_deleted_toast);
|
||||
Toast.makeText(ConversationActivity.this, deleted, LENGTH_SHORT)
|
||||
.show();
|
||||
|
||||
finish();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||