Refactor contact lists, their adapters and items

This commit is contained in:
Torsten Grote
2016-10-25 12:50:49 -02:00
parent 1e36f21cc8
commit 7191967092
19 changed files with 373 additions and 472 deletions

View File

@@ -1,7 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
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="wrap_content"
@@ -12,8 +11,7 @@
android:layout_height="wrap_content"
android:background="?attr/selectableItemBackground"
android:paddingBottom="@dimen/listitem_horizontal_margin"
android:paddingTop="@dimen/listitem_horizontal_margin"
>
android:paddingTop="@dimen/listitem_horizontal_margin">
<FrameLayout
android:id="@+id/avatarFrameView"
@@ -76,15 +74,6 @@
android:textSize="@dimen/text_size_small"
tools:text="Dec 24"/>
<TextView
android:id="@+id/identityView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@android:color/tertiary_text_light"
android:textSize="@dimen/text_size_tiny"
android:visibility="gone"
tools:text="My Identity"/>
</LinearLayout>
<ImageView

View File

@@ -127,8 +127,6 @@
<string name="introduction_sent">Your introduction has been sent.</string>
<string name="introduction_error">There was an error making the introduction.</string>
<string name="introduction_response_error">Error when responding to introduction</string>
<string name="introduction_warn_different_identities_title">Warning: Different Identities</string>
<string name="introduction_warn_different_identities_text">You are trying to introduce two contacts that you have added with different identities. This might reveal that both identities are yours.</string>
<string name="dialog_button_introduce">Introduce</string>
<string name="introduction_request_sent">You have asked to introduce %1$s to %2$s.</string>
<string name="introduction_request_received">%1$s has asked to introduce you to %2$s. Do you want to add %2$s to your contact list?</string>

View File

@@ -2,129 +2,61 @@ package org.briarproject.android.contact;
import android.content.Context;
import android.support.annotation.Nullable;
import android.support.v4.view.ViewCompat;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import org.briarproject.R;
import org.briarproject.android.util.BriarAdapter;
import org.briarproject.api.contact.ContactId;
import org.briarproject.api.identity.Author;
import org.briarproject.util.StringUtils;
import im.delight.android.identicons.IdenticonDrawable;
import static android.support.v7.util.SortedList.INVALID_POSITION;
public abstract class BaseContactListAdapter<VH extends BaseContactListAdapter.BaseContactHolder>
extends BriarAdapter<ContactListItem, VH> {
public abstract class BaseContactListAdapter<I extends ContactItem, VH extends ContactItemViewHolder<I>>
extends BriarAdapter<I, VH> {
@Nullable
protected final OnItemClickListener listener;
protected final OnContactClickListener<I> listener;
public BaseContactListAdapter(Context ctx,
@Nullable OnItemClickListener listener) {
super(ctx, ContactListItem.class);
public BaseContactListAdapter(Context ctx, Class<I> c,
@Nullable OnContactClickListener<I> listener) {
super(ctx, c);
this.listener = listener;
}
@Override
public void onBindViewHolder(final VH ui, int position) {
final ContactListItem item = getItemAt(position);
if (item == null) return;
Author author = item.getContact().getAuthor();
ui.avatar.setImageDrawable(
new IdenticonDrawable(author.getId().getBytes()));
String contactName = author.getName();
ui.name.setText(contactName);
ui.layout.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (listener != null) listener.onItemClick(ui.avatar, item);
}
});
ViewCompat.setTransitionName(ui.avatar, "avatar" +
StringUtils.toHexString(item.getGroupId().getBytes()));
I item = items.get(position);
ui.bind(item, listener);
}
@Override
public int compare(ContactListItem c1, ContactListItem c2) {
return compareByName(c1, c2);
public int compare(I c1, I c2) {
return c1.getContact().getAuthor().getName()
.compareTo(c2.getContact().getAuthor().getName());
}
@Override
public boolean areItemsTheSame(ContactListItem c1, ContactListItem c2) {
public boolean areItemsTheSame(I c1, I c2) {
return c1.getContact().getId().equals(c2.getContact().getId());
}
@Override
public boolean areContentsTheSame(ContactListItem c1, ContactListItem c2) {
public boolean areContentsTheSame(I c1, I c2) {
// check for all properties that influence visual
// representation of contact
if (c1.isConnected() != c2.isConnected()) {
return false;
}
if (c1.getUnreadCount() != c2.getUnreadCount()) {
return false;
}
if (c1.getTimestamp() != c2.getTimestamp()) {
return false;
}
return true;
return c1.isConnected() == c2.isConnected();
}
int findItemPosition(ContactId c) {
int count = getItemCount();
for (int i = 0; i < count; i++) {
ContactListItem item = getItemAt(i);
I item = getItemAt(i);
if (item != null && item.getContact().getId().equals(c))
return i;
}
return INVALID_POSITION; // Not found
}
public static class BaseContactHolder extends RecyclerView.ViewHolder {
public final ViewGroup layout;
public final ImageView avatar;
public final TextView name;
public BaseContactHolder(View v) {
super(v);
layout = (ViewGroup) v;
avatar = (ImageView) v.findViewById(R.id.avatarView);
name = (TextView) v.findViewById(R.id.nameView);
}
}
protected int compareByName(ContactListItem c1, ContactListItem c2) {
int authorCompare = c1.getLocalAuthor().getName()
.compareTo(c2.getLocalAuthor().getName());
if (authorCompare == 0) {
// if names are equal, compare by time instead
return compareByTime(c1, c2);
} else {
return authorCompare;
}
}
protected int compareByTime(ContactListItem c1, ContactListItem c2) {
long time1 = c1.getTimestamp();
long time2 = c2.getTimestamp();
if (time1 < time2) return 1;
if (time1 > time2) return -1;
return 0;
}
public interface OnItemClickListener {
void onItemClick(View view, ContactListItem item);
public interface OnContactClickListener<I> {
void onItemClick(View view, I item);
}
}

View File

@@ -0,0 +1,32 @@
package org.briarproject.android.contact;
import org.briarproject.api.contact.Contact;
import org.briarproject.api.nullsafety.NotNullByDefault;
import javax.annotation.concurrent.NotThreadSafe;
@NotThreadSafe
@NotNullByDefault
public class ContactItem {
private final Contact contact;
private boolean connected;
public ContactItem(Contact contact, boolean connected) {
this.contact = contact;
this.connected = connected;
}
public Contact getContact() {
return contact;
}
boolean isConnected() {
return connected;
}
void setConnected(boolean connected) {
this.connected = connected;
}
}

View File

@@ -0,0 +1,51 @@
package org.briarproject.android.contact;
import android.support.annotation.UiThread;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import org.briarproject.R;
import org.briarproject.android.contact.BaseContactListAdapter.OnContactClickListener;
import org.briarproject.api.identity.Author;
import org.briarproject.api.nullsafety.NotNullByDefault;
import org.jetbrains.annotations.Nullable;
import im.delight.android.identicons.IdenticonDrawable;
@UiThread
@NotNullByDefault
public class ContactItemViewHolder<I extends ContactItem>
extends RecyclerView.ViewHolder {
protected final ViewGroup layout;
protected final ImageView avatar;
protected final TextView name;
public ContactItemViewHolder(View v) {
super(v);
layout = (ViewGroup) v;
avatar = (ImageView) v.findViewById(R.id.avatarView);
name = (TextView) v.findViewById(R.id.nameView);
}
protected void bind(final I item,
@Nullable final OnContactClickListener<I> listener) {
Author author = item.getContact().getAuthor();
avatar.setImageDrawable(
new IdenticonDrawable(author.getId().getBytes()));
String contactName = author.getName();
name.setText(contactName);
layout.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (listener != null) listener.onItemClick(avatar, item);
}
});
}
}

View File

@@ -1,87 +1,49 @@
package org.briarproject.android.contact;
import android.content.Context;
import android.support.v4.view.ViewCompat;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import org.briarproject.R;
import org.briarproject.android.util.AndroidUtils;
import org.briarproject.util.StringUtils;
public class ContactListAdapter
extends BaseContactListAdapter<ContactListAdapter.ContactHolder> {
public class ContactListAdapter extends
BaseContactListAdapter<ContactListItem, ContactListItemViewHolder> {
public ContactListAdapter(Context context, OnItemClickListener listener) {
super(context, listener);
public ContactListAdapter(Context context,
OnContactClickListener<ContactListItem> listener) {
super(context, ContactListItem.class, listener);
}
@Override
public ContactHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
public ContactListItemViewHolder onCreateViewHolder(ViewGroup viewGroup,
int i) {
View v = LayoutInflater.from(viewGroup.getContext()).inflate(
R.layout.list_item_contact, viewGroup, false);
return new ContactHolder(v);
return new ContactListItemViewHolder(v);
}
@Override
public void onBindViewHolder(ContactHolder ui, int position) {
super.onBindViewHolder(ui, position);
ContactListItem item = getItemAt(position);
if (item == null) return;
// unread count
int unread = item.getUnreadCount();
if (unread > 0) {
ui.unread.setText(String.valueOf(unread));
ui.unread.setVisibility(View.VISIBLE);
} else {
ui.unread.setVisibility(View.INVISIBLE);
public boolean areContentsTheSame(ContactListItem c1, ContactListItem c2) {
// check for all properties that influence visual
// representation of contact
if (c1.getUnreadCount() != c2.getUnreadCount()) {
return false;
}
// date of last message
if (item.isEmpty()) {
ui.date.setText(R.string.date_no_private_messages);
} else {
long timestamp = item.getTimestamp();
ui.date.setText(AndroidUtils.formatDate(ctx, timestamp));
}
// online/offline
if (item.isConnected()) {
ui.bulb.setImageResource(R.drawable.contact_connected);
} else {
ui.bulb.setImageResource(R.drawable.contact_disconnected);
}
ViewCompat.setTransitionName(ui.bulb,
"bulb" + StringUtils.toHexString(item.getGroupId().getBytes()));
}
protected static class ContactHolder
extends BaseContactListAdapter.BaseContactHolder {
public final ImageView bulb;
private final TextView unread;
public final TextView date;
public final TextView identity;
private ContactHolder(View v) {
super(v);
bulb = (ImageView) v.findViewById(R.id.bulbView);
unread = (TextView) v.findViewById(R.id.unreadCountView);
date = (TextView) v.findViewById(R.id.dateView);
identity = (TextView) v.findViewById(R.id.identityView);
if (c1.getTimestamp() != c2.getTimestamp()) {
return false;
}
return super.areContentsTheSame(c1, c2);
}
@Override
public int compare(ContactListItem c1, ContactListItem c2) {
return compareByTime(c1, c2);
long time1 = c1.getTimestamp();
long time2 = c2.getTimestamp();
if (time1 < time2) return 1;
if (time1 > time2) return -1;
return 0;
}
}

View File

@@ -7,7 +7,6 @@ import android.support.annotation.Nullable;
import android.support.v4.app.ActivityCompat;
import android.support.v4.app.ActivityOptionsCompat;
import android.support.v4.util.Pair;
import android.support.v4.view.ViewCompat;
import android.support.v7.widget.LinearLayoutManager;
import android.view.LayoutInflater;
import android.view.Menu;
@@ -19,6 +18,7 @@ import android.view.ViewGroup;
import org.briarproject.R;
import org.briarproject.android.ActivityComponent;
import org.briarproject.android.api.AndroidNotificationManager;
import org.briarproject.android.contact.BaseContactListAdapter.OnContactClickListener;
import org.briarproject.android.fragment.BaseFragment;
import org.briarproject.android.keyagreement.KeyAgreementActivity;
import org.briarproject.android.view.BriarRecyclerView;
@@ -41,8 +41,6 @@ import org.briarproject.api.event.IntroductionResponseReceivedEvent;
import org.briarproject.api.event.InvitationRequestReceivedEvent;
import org.briarproject.api.event.InvitationResponseReceivedEvent;
import org.briarproject.api.event.PrivateMessageReceivedEvent;
import org.briarproject.api.identity.IdentityManager;
import org.briarproject.api.identity.LocalAuthor;
import org.briarproject.api.introduction.IntroductionRequest;
import org.briarproject.api.introduction.IntroductionResponse;
import org.briarproject.api.messaging.ConversationManager;
@@ -59,6 +57,7 @@ import java.util.logging.Logger;
import javax.inject.Inject;
import static android.support.v4.app.ActivityOptionsCompat.makeSceneTransitionAnimation;
import static android.support.v4.view.ViewCompat.getTransitionName;
import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING;
import static org.briarproject.android.BriarActivity.GROUP_ID;
@@ -82,8 +81,6 @@ public class ContactListFragment extends BaseFragment implements EventListener {
@Inject
volatile ContactManager contactManager;
@Inject
volatile IdentityManager identityManager;
@Inject
volatile ConversationManager conversationManager;
public static ContactListFragment newInstance() {
@@ -108,12 +105,10 @@ public class ContactListFragment extends BaseFragment implements EventListener {
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View contentView =
inflater.inflate(R.layout.list, container,
false);
View contentView = inflater.inflate(R.layout.list, container, false);
BaseContactListAdapter.OnItemClickListener onItemClickListener =
new ContactListAdapter.OnItemClickListener() {
OnContactClickListener<ContactListItem> onContactClickListener =
new OnContactClickListener<ContactListItem>() {
@Override
public void onItemClick(View view, ContactListItem item) {
GroupId groupId = item.getGroupId();
@@ -123,17 +118,17 @@ public class ContactListFragment extends BaseFragment implements EventListener {
// work-around for android bug #224270
if (Build.VERSION.SDK_INT >= 23) {
ContactListAdapter.ContactHolder holder =
(ContactListAdapter.ContactHolder) list
ContactListItemViewHolder holder =
(ContactListItemViewHolder) list
.getRecyclerView()
.findViewHolderForAdapterPosition(
adapter.findItemPosition(
item));
Pair<View, String> avatar =
Pair.create((View) holder.avatar, ViewCompat
.getTransitionName(holder.avatar));
Pair.create((View) holder.avatar,
getTransitionName(holder.avatar));
Pair<View, String> bulb =
Pair.create((View) holder.bulb, ViewCompat.
Pair.create((View) holder.bulb,
getTransitionName(holder.bulb));
ActivityOptionsCompat options =
makeSceneTransitionAnimation(getActivity(),
@@ -145,8 +140,7 @@ public class ContactListFragment extends BaseFragment implements EventListener {
}
}
};
adapter = new ContactListAdapter(getContext(), onItemClickListener);
adapter = new ContactListAdapter(getContext(), onContactClickListener);
list = (BriarRecyclerView) contentView.findViewById(R.id.list);
list.setLayoutManager(new LinearLayoutManager(getContext()));
list.setAdapter(adapter);
@@ -212,10 +206,8 @@ public class ContactListFragment extends BaseFragment implements EventListener {
conversationManager.getGroupCount(id);
boolean connected =
connectionRegistry.isConnected(c.getId());
LocalAuthor localAuthor = identityManager
.getLocalAuthor(c.getLocalAuthorId());
contacts.add(new ContactListItem(c, localAuthor,
connected, groupId, count));
contacts.add(new ContactListItem(c, connected,
groupId, count));
} catch (NoSuchContactException e) {
// Continue
}

View File

@@ -2,63 +2,40 @@ package org.briarproject.android.contact;
import org.briarproject.api.clients.MessageTracker.GroupCount;
import org.briarproject.api.contact.Contact;
import org.briarproject.api.identity.LocalAuthor;
import org.briarproject.api.nullsafety.NotNullByDefault;
import org.briarproject.api.sync.GroupId;
import org.jetbrains.annotations.NotNull;
import javax.annotation.concurrent.NotThreadSafe;
@NotThreadSafe
public class ContactListItem {
@NotNullByDefault
public class ContactListItem extends ContactItem {
private final Contact contact;
private final LocalAuthor localAuthor;
private final GroupId groupId;
private boolean connected, empty;
private boolean empty;
private long timestamp;
private int unread;
public ContactListItem(@NotNull Contact contact,
@NotNull LocalAuthor localAuthor, boolean connected,
@NotNull GroupId groupId, @NotNull GroupCount count) {
this.contact = contact;
this.localAuthor = localAuthor;
public ContactListItem(Contact contact, boolean connected, GroupId groupId,
GroupCount count) {
super(contact, connected);
this.groupId = groupId;
this.connected = connected;
this.empty = count.getMsgCount() == 0;
this.unread = count.getUnreadCount();
this.timestamp = count.getLatestMsgTime();
}
void addMessage(ConversationItem message) {
empty = empty && message == null;
if (message != null) {
if (message.getTime() > timestamp) timestamp = message.getTime();
if (!message.isRead())
unread++;
}
}
public Contact getContact() {
return contact;
}
public LocalAuthor getLocalAuthor() {
return localAuthor;
empty = false;
if (message.getTime() > timestamp) timestamp = message.getTime();
if (!message.isRead())
unread++;
}
GroupId getGroupId() {
return groupId;
}
boolean isConnected() {
return connected;
}
void setConnected(boolean connected) {
this.connected = connected;
}
boolean isEmpty() {
return empty;
}
@@ -70,4 +47,5 @@ public class ContactListItem {
int getUnreadCount() {
return unread;
}
}
}

View File

@@ -0,0 +1,74 @@
package org.briarproject.android.contact;
import android.support.annotation.UiThread;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
import org.briarproject.R;
import org.briarproject.android.contact.BaseContactListAdapter.OnContactClickListener;
import org.briarproject.api.nullsafety.NotNullByDefault;
import org.briarproject.api.sync.GroupId;
import org.jetbrains.annotations.Nullable;
import static android.support.v4.view.ViewCompat.setTransitionName;
import static org.briarproject.android.util.AndroidUtils.formatDate;
import static org.briarproject.util.StringUtils.toHexString;
@UiThread
@NotNullByDefault
class ContactListItemViewHolder extends ContactItemViewHolder<ContactListItem> {
protected final ImageView bulb;
private final TextView unread;
private final TextView date;
public ContactListItemViewHolder(View v) {
super(v);
bulb = (ImageView) v.findViewById(R.id.bulbView);
unread = (TextView) v.findViewById(R.id.unreadCountView);
date = (TextView) v.findViewById(R.id.dateView);
}
@Override
protected void bind(ContactListItem item, @Nullable
OnContactClickListener<ContactListItem> listener) {
super.bind(item, listener);
// unread count
int unreadCount = item.getUnreadCount();
if (unreadCount > 0) {
unread.setText(String.valueOf(unreadCount));
unread.setVisibility(View.VISIBLE);
} else {
unread.setVisibility(View.INVISIBLE);
}
// date of last message
if (item.isEmpty()) {
date.setText(R.string.date_no_private_messages);
} else {
long timestamp = item.getTimestamp();
date.setText(formatDate(date.getContext(), timestamp));
}
// online/offline
if (item.isConnected()) {
bulb.setImageResource(R.drawable.contact_connected);
} else {
bulb.setImageResource(R.drawable.contact_disconnected);
}
setTransitionName(avatar, getAvatarTransitionName(item.getGroupId()));
setTransitionName(bulb, getBulbTransitionName(item.getGroupId()));
}
private String getAvatarTransitionName(GroupId g) {
return "avatar" + toHexString(g.getBytes());
}
private String getBulbTransitionName(GroupId g) {
return "bulb" + toHexString(g.getBytes());
}
}

View File

@@ -1,63 +0,0 @@
package org.briarproject.android.introduction;
import android.content.Context;
import android.support.annotation.UiThread;
import android.view.View;
import org.briarproject.android.contact.ContactListAdapter;
import org.briarproject.android.contact.ContactListItem;
import org.briarproject.api.identity.AuthorId;
@UiThread
class ContactChooserAdapter extends ContactListAdapter {
private AuthorId localAuthorId;
ContactChooserAdapter(Context context, OnItemClickListener listener) {
super(context, listener);
}
@Override
public void onBindViewHolder(final ContactHolder ui, final int position) {
super.onBindViewHolder(ui, position);
final ContactListItem item = getItemAt(position);
if (item == null) return;
ui.name.setText(item.getContact().getAuthor().getName());
ui.identity.setText(item.getLocalAuthor().getName());
ui.identity.setVisibility(View.VISIBLE);
if (!item.getLocalAuthor().getId().equals(localAuthorId)) {
grayOutItem(ui);
}
}
@Override
public int compare(ContactListItem c1, ContactListItem c2) {
return compareByName(c1, c2);
}
/**
* Set the identity from whose perspective the contact shall be chosen.
* Contacts that belong to a different author will be shown grayed out,
* but are still clickable.
*
* @param authorId The ID of the local Author
*/
void setLocalAuthor(AuthorId authorId) {
localAuthorId = authorId;
notifyDataSetChanged();
}
private void grayOutItem(final ContactHolder ui) {
float alpha = 0.25f;
ui.bulb.setAlpha(alpha);
ui.avatar.setAlpha(alpha);
ui.name.setAlpha(alpha);
ui.date.setAlpha(alpha);
ui.identity.setAlpha(alpha);
}
}

View File

@@ -1,10 +1,8 @@
package org.briarproject.android.introduction;
import android.content.Context;
import android.content.DialogInterface;
import android.os.Build;
import android.os.Bundle;
import android.support.v7.app.AlertDialog;
import android.support.v7.widget.LinearLayoutManager;
import android.transition.Fade;
import android.view.LayoutInflater;
@@ -13,6 +11,7 @@ import android.view.ViewGroup;
import org.briarproject.R;
import org.briarproject.android.ActivityComponent;
import org.briarproject.android.contact.BaseContactListAdapter.OnContactClickListener;
import org.briarproject.android.contact.ContactListAdapter;
import org.briarproject.android.contact.ContactListItem;
import org.briarproject.android.fragment.BaseFragment;
@@ -22,9 +21,6 @@ import org.briarproject.api.contact.Contact;
import org.briarproject.api.contact.ContactId;
import org.briarproject.api.contact.ContactManager;
import org.briarproject.api.db.DbException;
import org.briarproject.api.identity.AuthorId;
import org.briarproject.api.identity.IdentityManager;
import org.briarproject.api.identity.LocalAuthor;
import org.briarproject.api.messaging.ConversationManager;
import org.briarproject.api.plugins.ConnectionRegistry;
import org.briarproject.api.sync.GroupId;
@@ -44,7 +40,7 @@ public class ContactChooserFragment extends BaseFragment {
private IntroductionActivity introductionActivity;
private BriarRecyclerView list;
private ContactChooserAdapter adapter;
private ContactListAdapter adapter;
private ContactId contactId;
// Fields that are accessed from background threads must be volatile
@@ -52,8 +48,6 @@ public class ContactChooserFragment extends BaseFragment {
@Inject
volatile ContactManager contactManager;
@Inject
volatile IdentityManager identityManager;
@Inject
volatile ConversationManager conversationManager;
@Inject
volatile ConnectionRegistry connectionRegistry;
@@ -83,22 +77,16 @@ public class ContactChooserFragment extends BaseFragment {
setExitTransition(new Fade());
}
ContactListAdapter.OnItemClickListener onItemClickListener =
new ContactListAdapter.OnItemClickListener() {
OnContactClickListener<ContactListItem> onContactClickListener =
new OnContactClickListener<ContactListItem>() {
@Override
public void onItemClick(View view, ContactListItem item) {
if (c1 == null) throw new IllegalStateException();
Contact c2 = item.getContact();
if (!c1.getLocalAuthorId()
.equals(c2.getLocalAuthorId())) {
warnAboutDifferentIdentities(view, c1, c2);
} else {
introductionActivity.showMessageScreen(view, c1,
c2);
}
introductionActivity.showMessageScreen(view, c1, c2);
}
};
adapter = new ContactChooserAdapter(getActivity(), onItemClickListener);
adapter = new ContactListAdapter(getActivity(), onContactClickListener);
list = (BriarRecyclerView) contentView.findViewById(R.id.list);
list.setLayoutManager(new LinearLayoutManager(getActivity()));
@@ -139,8 +127,6 @@ public class ContactChooserFragment extends BaseFragment {
public void run() {
try {
List<ContactListItem> contacts = new ArrayList<>();
AuthorId localAuthorId =
identityManager.getLocalAuthor().getId();
for (Contact c : contactManager.getActiveContacts()) {
if (c.getId().equals(contactId)) {
c1 = c;
@@ -152,13 +138,11 @@ public class ContactChooserFragment extends BaseFragment {
conversationManager.getGroupCount(id);
boolean connected =
connectionRegistry.isConnected(c.getId());
LocalAuthor localAuthor = identityManager
.getLocalAuthor(c.getLocalAuthorId());
contacts.add(new ContactListItem(c, localAuthor,
connected, groupId, count));
contacts.add(new ContactListItem(c, connected,
groupId, count));
}
}
displayContacts(localAuthorId, contacts);
displayContacts(contacts);
} catch (DbException e) {
if (LOG.isLoggable(WARNING))
LOG.log(WARNING, e.toString(), e);
@@ -167,37 +151,14 @@ public class ContactChooserFragment extends BaseFragment {
});
}
private void displayContacts(final AuthorId localAuthorId,
final List<ContactListItem> contacts) {
private void displayContacts(final List<ContactListItem> contacts) {
introductionActivity.runOnUiThreadUnlessDestroyed(new Runnable() {
@Override
public void run() {
adapter.setLocalAuthor(localAuthorId);
if (contacts.isEmpty()) list.showData();
else adapter.addAll(contacts);
}
});
}
private void warnAboutDifferentIdentities(final View view, final Contact c1,
final Contact c2) {
DialogInterface.OnClickListener okListener =
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
introductionActivity.showMessageScreen(view, c1, c2);
}
};
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity(),
R.style.BriarDialogTheme);
builder.setTitle(getString(
R.string.introduction_warn_different_identities_title));
builder.setMessage(getString(
R.string.introduction_warn_different_identities_text));
builder.setPositiveButton(R.string.dialog_button_introduce, okListener);
builder.setNegativeButton(android.R.string.cancel, null);
builder.show();
}
}

View File

@@ -13,12 +13,12 @@ import org.briarproject.R;
import org.briarproject.android.ActivityComponent;
import org.briarproject.android.BriarActivity;
import org.briarproject.android.fragment.BaseFragment;
import org.briarproject.android.fragment.BaseFragment.BaseFragmentListener;
import org.briarproject.api.contact.Contact;
import org.briarproject.api.contact.ContactId;
// TODO extend the BriarFragmentActivity ?
public class IntroductionActivity extends BriarActivity implements
BaseFragment.BaseFragmentListener {
public class IntroductionActivity extends BriarActivity
implements BaseFragmentListener {
public static final String CONTACT_ID = "briar.CONTACT_ID";

View File

@@ -4,8 +4,6 @@ import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CheckBox;
import android.widget.TextView;
import org.briarproject.R;
import org.briarproject.android.contact.BaseContactListAdapter;
@@ -14,16 +12,12 @@ import org.briarproject.api.contact.ContactId;
import java.util.ArrayList;
import java.util.Collection;
import static android.view.View.GONE;
import static android.view.View.VISIBLE;
class ContactSelectorAdapter
extends BaseContactListAdapter<ContactSelectorAdapter.SelectableContactHolder> {
class ContactSelectorAdapter extends
BaseContactListAdapter<SelectableContactItem, SelectableContactHolder> {
ContactSelectorAdapter(Context context,
OnItemClickListener listener) {
super(context, listener);
OnContactClickListener<SelectableContactItem> listener) {
super(context, SelectableContactItem.class, listener);
}
@Override
@@ -31,66 +25,17 @@ class ContactSelectorAdapter
int i) {
View v = LayoutInflater.from(ctx).inflate(
R.layout.list_item_selectable_contact, viewGroup, false);
return new SelectableContactHolder(v);
}
@Override
public void onBindViewHolder(SelectableContactHolder ui, int position) {
super.onBindViewHolder(ui, position);
SelectableContactListItem item =
(SelectableContactListItem) getItemAt(position);
if (item == null) return;
if (item.isSelected()) {
ui.checkBox.setChecked(true);
} else {
ui.checkBox.setChecked(false);
}
if (item.isDisabled()) {
// we share this forum already with that contact
ui.layout.setEnabled(false);
ui.shared.setVisibility(VISIBLE);
grayOutItem(ui, true);
} else {
ui.layout.setEnabled(true);
ui.shared.setVisibility(GONE);
grayOutItem(ui, false);
}
}
Collection<ContactId> getSelectedContactIds() {
Collection<ContactId> selected = new ArrayList<>();
for (int i = 0; i < items.size(); i++) {
SelectableContactListItem item =
(SelectableContactListItem) items.get(i);
SelectableContactItem item = items.get(i);
if (item.isSelected()) selected.add(item.getContact().getId());
}
return selected;
}
static class SelectableContactHolder
extends BaseContactListAdapter.BaseContactHolder {
private final CheckBox checkBox;
private final TextView shared;
private SelectableContactHolder(View v) {
super(v);
checkBox = (CheckBox) v.findViewById(R.id.checkBox);
shared = (TextView) v.findViewById(R.id.infoView);
}
}
private void grayOutItem(SelectableContactHolder ui, boolean gray) {
float alpha = gray ? 0.25f : 1f;
ui.avatar.setAlpha(alpha);
ui.name.setAlpha(alpha);
ui.checkBox.setAlpha(alpha);
}
}

View File

@@ -15,16 +15,14 @@ import android.view.ViewGroup;
import org.briarproject.R;
import org.briarproject.android.ActivityComponent;
import org.briarproject.android.contact.BaseContactListAdapter;
import org.briarproject.android.contact.ContactListItem;
import org.briarproject.android.contact.BaseContactListAdapter.OnContactClickListener;
import org.briarproject.android.fragment.BaseFragment;
import org.briarproject.android.view.BriarRecyclerView;
import org.briarproject.api.contact.Contact;
import org.briarproject.api.contact.ContactId;
import org.briarproject.api.contact.ContactManager;
import org.briarproject.api.db.DbException;
import org.briarproject.api.identity.IdentityManager;
import org.briarproject.api.identity.LocalAuthor;
import org.briarproject.api.plugins.ConnectionRegistry;
import org.briarproject.api.sync.GroupId;
import java.util.ArrayList;
@@ -41,8 +39,8 @@ import static org.briarproject.android.sharing.ShareActivity.getContactsFromIds;
import static org.briarproject.android.sharing.ShareActivity.getContactsFromIntegers;
import static org.briarproject.api.sharing.SharingConstants.GROUP_ID;
public class ContactSelectorFragment extends BaseFragment implements
BaseContactListAdapter.OnItemClickListener {
public class ContactSelectorFragment extends BaseFragment
implements OnContactClickListener<SelectableContactItem> {
public static final String TAG = ContactSelectorFragment.class.getName();
private static final Logger LOG = Logger.getLogger(TAG);
@@ -56,7 +54,7 @@ public class ContactSelectorFragment extends BaseFragment implements
@Inject
volatile ContactManager contactManager;
@Inject
volatile IdentityManager identityManager;
volatile ConnectionRegistry connectionRegistry;
private volatile GroupId groupId;
private volatile ContactSelectorListener listener;
@@ -115,7 +113,6 @@ public class ContactSelectorFragment extends BaseFragment implements
selectedContacts = getContactsFromIntegers(intContacts);
}
}
return contentView;
}
@@ -170,8 +167,8 @@ public class ContactSelectorFragment extends BaseFragment implements
}
@Override
public void onItemClick(View view, ContactListItem item) {
((SelectableContactListItem) item).toggleSelected();
public void onItemClick(View view, SelectableContactItem item) {
item.toggleSelected();
adapter.notifyItemChanged(adapter.findItemPosition(item), item);
updateMenuItem();
@@ -183,18 +180,19 @@ public class ContactSelectorFragment extends BaseFragment implements
public void run() {
try {
long now = System.currentTimeMillis();
List<ContactListItem> contacts = new ArrayList<>();
List<SelectableContactItem> contacts =
new ArrayList<>();
for (Contact c : contactManager.getActiveContacts()) {
LocalAuthor localAuthor = identityManager
.getLocalAuthor(c.getLocalAuthorId());
// is this contact online?
boolean connected =
connectionRegistry.isConnected(c.getId());
// was this contact already selected?
boolean selected = selection != null &&
selection.contains(c.getId());
// do we have already some sharing with that contact?
boolean disabled = listener.isDisabled(groupId, c);
contacts.add(new SelectableContactListItem(c,
localAuthor, groupId, selected, disabled));
contacts.add(new SelectableContactItem(c, connected,
selected, disabled));
}
long duration = System.currentTimeMillis() - now;
if (LOG.isLoggable(INFO))
@@ -208,7 +206,8 @@ public class ContactSelectorFragment extends BaseFragment implements
});
}
private void displayContacts(final List<ContactListItem> contacts) {
private void displayContacts(
final List<SelectableContactItem> contacts) {
listener.runOnUiThreadUnlessDestroyed(new Runnable() {
@Override
public void run() {

View File

@@ -0,0 +1,61 @@
package org.briarproject.android.sharing;
import android.support.annotation.UiThread;
import android.view.View;
import android.widget.CheckBox;
import android.widget.TextView;
import org.briarproject.R;
import org.briarproject.android.contact.BaseContactListAdapter.OnContactClickListener;
import org.briarproject.android.contact.ContactItemViewHolder;
import org.briarproject.api.nullsafety.NotNullByDefault;
import org.jetbrains.annotations.Nullable;
import static android.view.View.GONE;
import static android.view.View.VISIBLE;
@UiThread
@NotNullByDefault
class SelectableContactHolder
extends ContactItemViewHolder<SelectableContactItem> {
private final CheckBox checkBox;
private final TextView shared;
SelectableContactHolder(View v) {
super(v);
checkBox = (CheckBox) v.findViewById(R.id.checkBox);
shared = (TextView) v.findViewById(R.id.infoView);
}
@Override
protected void bind(SelectableContactItem item, @Nullable
OnContactClickListener<SelectableContactItem> listener) {
super.bind(item, listener);
if (item.isSelected()) {
checkBox.setChecked(true);
} else {
checkBox.setChecked(false);
}
if (item.isDisabled()) {
// we share this forum already with that contact
layout.setEnabled(false);
shared.setVisibility(VISIBLE);
grayOutItem(true);
} else {
layout.setEnabled(true);
shared.setVisibility(GONE);
grayOutItem(false);
}
}
private void grayOutItem(boolean gray) {
float alpha = gray ? 0.25f : 1f;
avatar.setAlpha(alpha);
name.setAlpha(alpha);
checkBox.setAlpha(alpha);
}
}

View File

@@ -0,0 +1,34 @@
package org.briarproject.android.sharing;
import org.briarproject.android.contact.ContactItem;
import org.briarproject.api.contact.Contact;
import org.briarproject.api.nullsafety.NotNullByDefault;
import javax.annotation.concurrent.NotThreadSafe;
@NotThreadSafe
@NotNullByDefault
public class SelectableContactItem extends ContactItem {
private boolean selected, disabled;
public SelectableContactItem(Contact contact, boolean connected,
boolean selected, boolean disabled) {
super(contact, connected);
this.selected = selected;
this.disabled = disabled;
}
public boolean isSelected() {
return selected;
}
public void toggleSelected() {
selected = !selected;
}
public boolean isDisabled() {
return disabled;
}
}

View File

@@ -1,40 +0,0 @@
package org.briarproject.android.sharing;
import android.support.annotation.UiThread;
import org.briarproject.android.contact.ContactListItem;
import org.briarproject.api.clients.MessageTracker.GroupCount;
import org.briarproject.api.contact.Contact;
import org.briarproject.api.identity.LocalAuthor;
import org.briarproject.api.sync.GroupId;
// This class is NOT thread-safe
public class SelectableContactListItem extends ContactListItem {
private boolean selected, disabled;
public SelectableContactListItem(Contact contact, LocalAuthor localAuthor,
GroupId groupId, boolean selected, boolean disabled) {
super(contact, localAuthor, false, groupId, new GroupCount(0, 0, 0));
this.selected = selected;
this.disabled = disabled;
}
public void setSelected(boolean selected) {
this.selected = selected;
}
public boolean isSelected() {
return selected;
}
public void toggleSelected() {
selected = !selected;
}
public boolean isDisabled() {
return disabled;
}
}

View File

@@ -7,13 +7,11 @@ import android.view.MenuItem;
import org.briarproject.R;
import org.briarproject.android.BriarActivity;
import org.briarproject.android.contact.ContactListItem;
import org.briarproject.android.contact.ContactItem;
import org.briarproject.android.view.BriarRecyclerView;
import org.briarproject.api.clients.MessageTracker.GroupCount;
import org.briarproject.api.contact.Contact;
import org.briarproject.api.db.DbException;
import org.briarproject.api.identity.IdentityManager;
import org.briarproject.api.identity.LocalAuthor;
import org.briarproject.api.plugins.ConnectionRegistry;
import org.briarproject.api.sync.GroupId;
import java.util.ArrayList;
@@ -36,7 +34,7 @@ abstract class SharingStatusActivity extends BriarActivity {
// Fields that are accessed from background threads must be volatile
@Inject
volatile IdentityManager identityManager;
volatile ConnectionRegistry connectionRegistry;
@Override
public void onCreate(Bundle savedInstanceState) {
@@ -109,13 +107,11 @@ abstract class SharingStatusActivity extends BriarActivity {
@Override
public void run() {
try {
List<ContactListItem> contactItems = new ArrayList<>();
List<ContactItem> contactItems = new ArrayList<>();
for (Contact c : getSharedBy()) {
LocalAuthor localAuthor = identityManager
.getLocalAuthor(c.getLocalAuthorId());
ContactListItem item =
new ContactListItem(c, localAuthor, false,
groupId, new GroupCount(0, 0, 0));
boolean isConnected =
connectionRegistry.isConnected(c.getId());
ContactItem item = new ContactItem(c, isConnected);
contactItems.add(item);
}
displaySharedBy(contactItems);
@@ -127,7 +123,7 @@ abstract class SharingStatusActivity extends BriarActivity {
});
}
private void displaySharedBy(final List<ContactListItem> contacts) {
private void displaySharedBy(final List<ContactItem> contacts) {
runOnUiThreadUnlessDestroyed(new Runnable() {
@Override
public void run() {
@@ -142,13 +138,11 @@ abstract class SharingStatusActivity extends BriarActivity {
@Override
public void run() {
try {
List<ContactListItem> contactItems = new ArrayList<>();
List<ContactItem> contactItems = new ArrayList<>();
for (Contact c : getSharedWith()) {
LocalAuthor localAuthor = identityManager
.getLocalAuthor(c.getLocalAuthorId());
ContactListItem item =
new ContactListItem(c, localAuthor, false,
groupId, new GroupCount(0, 0, 0));
boolean isConnected =
connectionRegistry.isConnected(c.getId());
ContactItem item = new ContactItem(c, isConnected);
contactItems.add(item);
}
displaySharedWith(contactItems);
@@ -160,7 +154,7 @@ abstract class SharingStatusActivity extends BriarActivity {
});
}
private void displaySharedWith(final List<ContactListItem> contacts) {
private void displaySharedWith(final List<ContactItem> contacts) {
runOnUiThreadUnlessDestroyed(new Runnable() {
@Override
public void run() {

View File

@@ -7,20 +7,22 @@ import android.view.ViewGroup;
import org.briarproject.R;
import org.briarproject.android.contact.BaseContactListAdapter;
import org.briarproject.android.contact.ContactItem;
import org.briarproject.android.contact.ContactItemViewHolder;
class SharingStatusAdapter
extends BaseContactListAdapter<BaseContactListAdapter.BaseContactHolder> {
class SharingStatusAdapter extends
BaseContactListAdapter<ContactItem, ContactItemViewHolder<ContactItem>> {
SharingStatusAdapter(Context context) {
super(context, null);
super(context, ContactItem.class, null);
}
@Override
public BaseContactHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
public ContactItemViewHolder<ContactItem> onCreateViewHolder(
ViewGroup viewGroup, int i) {
View v = LayoutInflater.from(viewGroup.getContext()).inflate(
R.layout.list_item_contact_small, viewGroup, false);
return new BaseContactHolder(v);
return new ContactItemViewHolder<>(v);
}
}