Merge branch '678-contact-list-refactoring' into 'master'

Refactor contact lists, their adapters and items

This was supposed to be a preparation for #678 to make the contacts lists cleaner and easier to re-use for different use-cases. Turns out #678 can't use this work, but it is probably nice to have anyway.

During this work, support for multiple identities has been removed from the various contact lists.

See merge request !363
This commit is contained in:
akwizgran
2016-10-31 15:39:48 +00:00
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);
}
}