Merge branch '810-fix-sharing-status-screens' into 'master'

Fix Sharing Status screens

* Remove distinction between "shared with" and "shared by"
* Show all contacts a blog is shared with
* Show online status of contacts in sharing screen

![device-2016-12-05-142949](/uploads/703fbd2d52815374e57edd89f754bf6c/device-2016-12-05-142949.png)

Closes #810

See merge request !445
This commit is contained in:
Torsten Grote
2016-12-07 16:52:29 +00:00
18 changed files with 234 additions and 213 deletions

View File

@@ -43,6 +43,24 @@ public interface ContactManager {
*/ */
Contact getContact(ContactId c) throws DbException; Contact getContact(ContactId c) throws DbException;
/**
* Returns the contact with the given remoteAuthorId
* that was added by the LocalAuthor with the given localAuthorId
*
* @throws org.briarproject.bramble.api.db.NoSuchContactException
*/
Contact getContact(AuthorId remoteAuthorId, AuthorId localAuthorId)
throws DbException;
/**
* Returns the contact with the given remoteAuthorId
* that was added by the LocalAuthor with the given localAuthorId
*
* @throws org.briarproject.bramble.api.db.NoSuchContactException
*/
Contact getContact(Transaction txn, AuthorId remoteAuthorId,
AuthorId localAuthorId) throws DbException;
/** /**
* Returns all active contacts. * Returns all active contacts.
*/ */

View File

@@ -6,6 +6,7 @@ import org.briarproject.bramble.api.contact.ContactManager;
import org.briarproject.bramble.api.crypto.SecretKey; import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.db.DatabaseComponent; import org.briarproject.bramble.api.db.DatabaseComponent;
import org.briarproject.bramble.api.db.DbException; import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.NoSuchContactException;
import org.briarproject.bramble.api.db.Transaction; import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.identity.Author; import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.identity.AuthorId; import org.briarproject.bramble.api.identity.AuthorId;
@@ -88,6 +89,32 @@ class ContactManagerImpl implements ContactManager {
return contact; return contact;
} }
@Override
public Contact getContact(AuthorId remoteAuthorId, AuthorId localAuthorId)
throws DbException {
Transaction txn = db.startTransaction(true);
try {
Contact c = getContact(txn, remoteAuthorId, localAuthorId);
db.commitTransaction(txn);
return c;
} finally {
db.endTransaction(txn);
}
}
@Override
public Contact getContact(Transaction txn, AuthorId remoteAuthorId,
AuthorId localAuthorId) throws DbException {
Collection<Contact> contacts =
db.getContactsByAuthorId(txn, remoteAuthorId);
for (Contact c : contacts) {
if (c.getLocalAuthorId().equals(localAuthorId)) {
return c;
}
}
throw new NoSuchContactException();
}
@Override @Override
public Collection<Contact> getActiveContacts() throws DbException { public Collection<Contact> getActiveContacts() throws DbException {
Collection<Contact> contacts; Collection<Contact> contacts;

View File

@@ -4,6 +4,7 @@ import net.jodah.concurrentunit.Waiter;
import org.briarproject.bramble.api.contact.Contact; import org.briarproject.bramble.api.contact.Contact;
import org.briarproject.bramble.api.db.DbException; import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.NoSuchGroupException;
import org.briarproject.bramble.api.event.Event; import org.briarproject.bramble.api.event.Event;
import org.briarproject.bramble.api.event.EventListener; import org.briarproject.bramble.api.event.EventListener;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
@@ -30,6 +31,7 @@ import static org.briarproject.briar.api.blog.BlogSharingManager.CLIENT_ID;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
public class BlogSharingIntegrationTest public class BlogSharingIntegrationTest
extends BriarIntegrationTest<BriarIntegrationTestComponent> { extends BriarIntegrationTest<BriarIntegrationTestComponent> {
@@ -266,7 +268,7 @@ public class BlogSharingIntegrationTest
assertTrue(blogSharingManager0.getSharedWith(blog2.getId()) assertTrue(blogSharingManager0.getSharedWith(blog2.getId())
.contains(contact1From0)); .contains(contact1From0));
// invitee gets blog shared by sharer // invitee gets blog shared by sharer
assertTrue(blogSharingManager1.getSharedBy(blog2.getId()) assertTrue(blogSharingManager1.getSharedWith(blog2.getId())
.contains(contact0From1)); .contains(contact0From1));
// invitee un-subscribes from blog // invitee un-subscribes from blog
@@ -282,9 +284,13 @@ public class BlogSharingIntegrationTest
// sharer no longer shares blog with invitee // sharer no longer shares blog with invitee
assertFalse(blogSharingManager0.getSharedWith(blog2.getId()) assertFalse(blogSharingManager0.getSharedWith(blog2.getId())
.contains(contact1From0)); .contains(contact1From0));
// invitee no longer gets blog shared by sharer // invitee no longer has blog shared by sharer
assertFalse(blogSharingManager1.getSharedBy(blog2.getId()) try {
.contains(contact0From1)); blogSharingManager1.getSharedWith(blog2.getId());
fail();
} catch (NoSuchGroupException e) {
// expected
}
// blog can be shared again // blog can be shared again
assertTrue( assertTrue(
blogSharingManager0.canBeShared(blog2.getId(), contact1From0)); blogSharingManager0.canBeShared(blog2.getId(), contact1From0));
@@ -310,10 +316,10 @@ public class BlogSharingIntegrationTest
eventWaiter.await(TIMEOUT, 1); eventWaiter.await(TIMEOUT, 1);
assertTrue(listener1.requestReceived); assertTrue(listener1.requestReceived);
// make sure blog2 is shared by 0 // make sure blog2 is shared by 0 and 2
Collection<Contact> contacts = Collection<Contact> contacts =
blogSharingManager1.getSharedBy(blog2.getId()); blogSharingManager1.getSharedWith(blog2.getId());
assertEquals(1, contacts.size()); assertEquals(2, contacts.size());
assertTrue(contacts.contains(contact0From1)); assertTrue(contacts.contains(contact0From1));
// make sure 1 knows that they have blog2 already // make sure 1 knows that they have blog2 already
@@ -355,10 +361,11 @@ public class BlogSharingIntegrationTest
assertEquals(3, blogManager1.getBlogs().size()); assertEquals(3, blogManager1.getBlogs().size());
Collection<Contact> sharedWith = Collection<Contact> sharedWith =
blogSharingManager0.getSharedWith(blog2.getId()); blogSharingManager0.getSharedWith(blog2.getId());
assertEquals(1, sharedWith.size()); assertEquals(2, sharedWith.size());
assertEquals(contact1From0, sharedWith.iterator().next()); assertTrue(sharedWith.contains(contact1From0));
assertTrue(sharedWith.contains(contact2From0));
Collection<Contact> sharedBy = Collection<Contact> sharedBy =
blogSharingManager1.getSharedBy(blog2.getId()); blogSharingManager1.getSharedWith(blog2.getId());
assertEquals(1, sharedBy.size()); assertEquals(1, sharedBy.size());
assertEquals(contact0From1, sharedBy.iterator().next()); assertEquals(contact0From1, sharedBy.iterator().next());
@@ -374,7 +381,8 @@ public class BlogSharingIntegrationTest
// sharer does not share this blog anymore with invitee // sharer does not share this blog anymore with invitee
sharedWith = sharedWith =
blogSharingManager0.getSharedWith(blog2.getId()); blogSharingManager0.getSharedWith(blog2.getId());
assertEquals(0, sharedWith.size()); assertEquals(1, sharedWith.size());
assertTrue(sharedWith.contains(contact2From0));
} }
@Test @Test
@@ -396,7 +404,7 @@ public class BlogSharingIntegrationTest
// make sure blog2 is shared by 0 // make sure blog2 is shared by 0
Collection<Contact> contacts = Collection<Contact> contacts =
blogSharingManager1.getSharedBy(blog2.getId()); blogSharingManager1.getSharedWith(blog2.getId());
assertEquals(1, contacts.size()); assertEquals(1, contacts.size());
assertTrue(contacts.contains(contact0From1)); assertTrue(contacts.contains(contact0From1));

View File

@@ -244,7 +244,7 @@ public class ForumSharingIntegrationTest
.contains(c1)); .contains(c1));
// invitee gets forum shared by sharer // invitee gets forum shared by sharer
Contact contact0 = contactManager1.getContact(contactId1From0); Contact contact0 = contactManager1.getContact(contactId1From0);
assertTrue(forumSharingManager1.getSharedBy(forum0.getId()) assertTrue(forumSharingManager1.getSharedWith(forum0.getId())
.contains(contact0)); .contains(contact0));
// invitee un-subscribes from forum // invitee un-subscribes from forum
@@ -261,7 +261,7 @@ public class ForumSharingIntegrationTest
assertFalse(forumSharingManager0.getSharedWith(forum0.getId()) assertFalse(forumSharingManager0.getSharedWith(forum0.getId())
.contains(c1)); .contains(c1));
// invitee no longer gets forum shared by sharer // invitee no longer gets forum shared by sharer
assertFalse(forumSharingManager1.getSharedBy(forum0.getId()) assertFalse(forumSharingManager1.getSharedWith(forum0.getId())
.contains(contact0)); .contains(contact0));
// forum can be shared again // forum can be shared again
assertTrue(forumSharingManager0.canBeShared(forum0.getId(), c1)); assertTrue(forumSharingManager0.canBeShared(forum0.getId(), c1));
@@ -299,7 +299,7 @@ public class ForumSharingIntegrationTest
.contains(c1)); .contains(c1));
// invitee gets forum shared by sharer // invitee gets forum shared by sharer
Contact contact0 = contactManager1.getContact(contactId1From0); Contact contact0 = contactManager1.getContact(contactId1From0);
assertTrue(forumSharingManager1.getSharedBy(forum0.getId()) assertTrue(forumSharingManager1.getSharedWith(forum0.getId())
.contains(contact0)); .contains(contact0));
// sharer un-subscribes from forum // sharer un-subscribes from forum
@@ -317,7 +317,7 @@ public class ForumSharingIntegrationTest
assertFalse(forumSharingManager1.getSharedWith(forum0.getId()) assertFalse(forumSharingManager1.getSharedWith(forum0.getId())
.contains(c0)); .contains(c0));
// sharer no longer gets forum shared by invitee // sharer no longer gets forum shared by invitee
assertFalse(forumSharingManager1.getSharedBy(forum0.getId()) assertFalse(forumSharingManager1.getSharedWith(forum0.getId())
.contains(contact0)); .contains(contact0));
// forum can be shared again // forum can be shared again
assertTrue(forumSharingManager1.canBeShared(forum0.getId(), c0)); assertTrue(forumSharingManager1.canBeShared(forum0.getId(), c0));
@@ -647,11 +647,11 @@ public class ForumSharingIntegrationTest
assertEquals(2, forums.iterator().next().getNewSharers().size()); assertEquals(2, forums.iterator().next().getNewSharers().size());
assertEquals(forum0, forums.iterator().next().getShareable()); assertEquals(forum0, forums.iterator().next().getShareable());
assertEquals(2, assertEquals(2,
forumSharingManager1.getSharedBy(forum0.getId()).size()); forumSharingManager1.getSharedWith(forum0.getId()).size());
// make sure both sharers actually share the forum // make sure both sharers actually share the forum
Collection<Contact> contacts = Collection<Contact> contacts =
forumSharingManager1.getSharedBy(forum0.getId()); forumSharingManager1.getSharedWith(forum0.getId());
assertEquals(2, contacts.size()); assertEquals(2, contacts.size());
// answer second request // answer second request

View File

@@ -10,13 +10,27 @@ import javax.annotation.concurrent.NotThreadSafe;
public class ContactItem { public class ContactItem {
private final Contact contact; private final Contact contact;
private boolean connected;
public ContactItem(Contact contact) { public ContactItem(Contact contact) {
this(contact, false);
}
public ContactItem(Contact contact, boolean connected) {
this.contact = contact; this.contact = contact;
this.connected = connected;
} }
public Contact getContact() { public Contact getContact() {
return contact; return contact;
} }
boolean isConnected() {
return connected;
}
void setConnected(boolean connected) {
this.connected = connected;
}
} }

View File

@@ -1,5 +1,6 @@
package org.briarproject.briar.android.contact; package org.briarproject.briar.android.contact;
import android.support.annotation.Nullable;
import android.support.annotation.UiThread; import android.support.annotation.UiThread;
import android.support.v7.widget.RecyclerView; import android.support.v7.widget.RecyclerView;
import android.view.View; import android.view.View;
@@ -12,8 +13,6 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.briar.R; import org.briarproject.briar.R;
import org.briarproject.briar.android.contact.BaseContactListAdapter.OnContactClickListener; import org.briarproject.briar.android.contact.BaseContactListAdapter.OnContactClickListener;
import javax.annotation.Nullable;
import im.delight.android.identicons.IdenticonDrawable; import im.delight.android.identicons.IdenticonDrawable;
@UiThread @UiThread
@@ -24,6 +23,8 @@ public class ContactItemViewHolder<I extends ContactItem>
protected final ViewGroup layout; protected final ViewGroup layout;
protected final ImageView avatar; protected final ImageView avatar;
protected final TextView name; protected final TextView name;
@Nullable
protected final ImageView bulb;
public ContactItemViewHolder(View v) { public ContactItemViewHolder(View v) {
super(v); super(v);
@@ -31,6 +32,8 @@ public class ContactItemViewHolder<I extends ContactItem>
layout = (ViewGroup) v; layout = (ViewGroup) v;
avatar = (ImageView) v.findViewById(R.id.avatarView); avatar = (ImageView) v.findViewById(R.id.avatarView);
name = (TextView) v.findViewById(R.id.nameView); name = (TextView) v.findViewById(R.id.nameView);
// this can be null as not all layouts that use this ViewHolder have it
bulb = (ImageView) v.findViewById(R.id.bulbView);
} }
protected void bind(final I item, protected void bind(final I item,
@@ -41,6 +44,15 @@ public class ContactItemViewHolder<I extends ContactItem>
String contactName = author.getName(); String contactName = author.getName();
name.setText(contactName); name.setText(contactName);
if (bulb != null) {
// online/offline
if (item.isConnected()) {
bulb.setImageResource(R.drawable.contact_connected);
} else {
bulb.setImageResource(R.drawable.contact_disconnected);
}
}
layout.setOnClickListener(new View.OnClickListener() { layout.setOnClickListener(new View.OnClickListener() {
@Override @Override
public void onClick(View v) { public void onClick(View v) {

View File

@@ -10,14 +10,13 @@ import javax.annotation.concurrent.NotThreadSafe;
@NotNullByDefault @NotNullByDefault
public class ContactListItem extends ContactItem { public class ContactListItem extends ContactItem {
private boolean connected, empty; private boolean empty;
private long timestamp; private long timestamp;
private int unread; private int unread;
public ContactListItem(Contact contact, boolean connected, public ContactListItem(Contact contact, boolean connected,
GroupCount count) { GroupCount count) {
super(contact); super(contact, connected);
this.connected = connected;
this.empty = count.getMsgCount() == 0; this.empty = count.getMsgCount() == 0;
this.unread = count.getUnreadCount(); this.unread = count.getUnreadCount();
this.timestamp = count.getLatestMsgTime(); this.timestamp = count.getLatestMsgTime();
@@ -30,14 +29,6 @@ public class ContactListItem extends ContactItem {
unread++; unread++;
} }
boolean isConnected() {
return connected;
}
void setConnected(boolean connected) {
this.connected = connected;
}
boolean isEmpty() { boolean isEmpty() {
return empty; return empty;
} }

View File

@@ -2,7 +2,6 @@ package org.briarproject.briar.android.contact;
import android.support.annotation.UiThread; import android.support.annotation.UiThread;
import android.view.View; import android.view.View;
import android.widget.ImageView;
import android.widget.TextView; import android.widget.TextView;
import org.briarproject.bramble.api.contact.ContactId; import org.briarproject.bramble.api.contact.ContactId;
@@ -20,13 +19,11 @@ import static org.briarproject.briar.android.util.UiUtils.formatDate;
@NotNullByDefault @NotNullByDefault
class ContactListItemViewHolder extends ContactItemViewHolder<ContactListItem> { class ContactListItemViewHolder extends ContactItemViewHolder<ContactListItem> {
protected final ImageView bulb;
private final TextView unread; private final TextView unread;
private final TextView date; private final TextView date;
ContactListItemViewHolder(View v) { ContactListItemViewHolder(View v) {
super(v); super(v);
bulb = (ImageView) v.findViewById(R.id.bulbView);
unread = (TextView) v.findViewById(R.id.unreadCountView); unread = (TextView) v.findViewById(R.id.unreadCountView);
date = (TextView) v.findViewById(R.id.dateView); date = (TextView) v.findViewById(R.id.dateView);
} }
@@ -53,13 +50,6 @@ class ContactListItemViewHolder extends ContactItemViewHolder<ContactListItem> {
date.setText(formatDate(date.getContext(), timestamp)); date.setText(formatDate(date.getContext(), timestamp));
} }
// online/offline
if (item.isConnected()) {
bulb.setImageResource(R.drawable.contact_connected);
} else {
bulb.setImageResource(R.drawable.contact_disconnected);
}
ContactId c = item.getContact().getId(); ContactId c = item.getContact().getId();
setTransitionName(avatar, UiUtils.getAvatarTransitionName(c)); setTransitionName(avatar, UiUtils.getAvatarTransitionName(c));
setTransitionName(bulb, UiUtils.getBulbTransitionName(c)); setTransitionName(bulb, UiUtils.getBulbTransitionName(c));

View File

@@ -3,6 +3,8 @@ package org.briarproject.briar.android.sharing;
import org.briarproject.bramble.api.contact.Contact; import org.briarproject.bramble.api.contact.Contact;
import org.briarproject.bramble.api.db.DatabaseExecutor; import org.briarproject.bramble.api.db.DatabaseExecutor;
import org.briarproject.bramble.api.db.DbException; import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
import org.briarproject.briar.android.activity.ActivityComponent; import org.briarproject.briar.android.activity.ActivityComponent;
import org.briarproject.briar.api.blog.BlogSharingManager; import org.briarproject.briar.api.blog.BlogSharingManager;
@@ -10,6 +12,8 @@ import java.util.Collection;
import javax.inject.Inject; import javax.inject.Inject;
@MethodsNotNullByDefault
@ParametersNotNullByDefault
public class BlogSharingStatusActivity extends SharingStatusActivity { public class BlogSharingStatusActivity extends SharingStatusActivity {
// Fields that are accessed from background threads must be volatile // Fields that are accessed from background threads must be volatile
@@ -21,16 +25,10 @@ public class BlogSharingStatusActivity extends SharingStatusActivity {
component.inject(this); component.inject(this);
} }
@DatabaseExecutor
@Override @Override
@DatabaseExecutor
protected Collection<Contact> getSharedWith() throws DbException { protected Collection<Contact> getSharedWith() throws DbException {
return blogSharingManager.getSharedWith(getGroupId()); return blogSharingManager.getSharedWith(getGroupId());
} }
@DatabaseExecutor
@Override
protected Collection<Contact> getSharedBy() throws DbException {
return blogSharingManager.getSharedBy(getGroupId());
}
} }

View File

@@ -3,13 +3,18 @@ package org.briarproject.briar.android.sharing;
import org.briarproject.bramble.api.contact.Contact; import org.briarproject.bramble.api.contact.Contact;
import org.briarproject.bramble.api.db.DatabaseExecutor; import org.briarproject.bramble.api.db.DatabaseExecutor;
import org.briarproject.bramble.api.db.DbException; import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
import org.briarproject.briar.android.activity.ActivityComponent; import org.briarproject.briar.android.activity.ActivityComponent;
import org.briarproject.briar.api.forum.ForumSharingManager; import org.briarproject.briar.api.forum.ForumSharingManager;
import java.util.Collection; import java.util.Collection;
import java.util.HashSet;
import javax.inject.Inject; import javax.inject.Inject;
@MethodsNotNullByDefault
@ParametersNotNullByDefault
public class ForumSharingStatusActivity extends SharingStatusActivity { public class ForumSharingStatusActivity extends SharingStatusActivity {
// Fields that are accessed from background threads must be volatile // Fields that are accessed from background threads must be volatile
@@ -21,16 +26,10 @@ public class ForumSharingStatusActivity extends SharingStatusActivity {
component.inject(this); component.inject(this);
} }
@DatabaseExecutor
@Override @Override
@DatabaseExecutor
protected Collection<Contact> getSharedWith() throws DbException { protected Collection<Contact> getSharedWith() throws DbException {
return forumSharingManager.getSharedWith(getGroupId()); return forumSharingManager.getSharedWith(getGroupId());
} }
@DatabaseExecutor
@Override
protected Collection<Contact> getSharedBy() throws DbException {
return forumSharingManager.getSharedBy(getGroupId());
}
} }

View File

@@ -2,11 +2,16 @@ package org.briarproject.briar.android.sharing;
import android.content.Intent; import android.content.Intent;
import android.os.Bundle; import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.LinearLayoutManager;
import android.view.MenuItem; import android.view.MenuItem;
import org.briarproject.bramble.api.contact.Contact; import org.briarproject.bramble.api.contact.Contact;
import org.briarproject.bramble.api.db.DatabaseExecutor;
import org.briarproject.bramble.api.db.DbException; import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
import org.briarproject.bramble.api.plugin.ConnectionRegistry;
import org.briarproject.bramble.api.sync.GroupId; import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.briar.R; import org.briarproject.briar.R;
import org.briarproject.briar.android.activity.BriarActivity; import org.briarproject.briar.android.activity.BriarActivity;
@@ -18,55 +23,53 @@ import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.logging.Logger; import java.util.logging.Logger;
import javax.inject.Inject;
import static java.util.logging.Level.WARNING; import static java.util.logging.Level.WARNING;
@MethodsNotNullByDefault
@ParametersNotNullByDefault
abstract class SharingStatusActivity extends BriarActivity { abstract class SharingStatusActivity extends BriarActivity {
@Inject
ConnectionRegistry connectionRegistry;
private static final Logger LOG = private static final Logger LOG =
Logger.getLogger(SharingStatusActivity.class.getName()); Logger.getLogger(SharingStatusActivity.class.getName());
private GroupId groupId; private GroupId groupId;
private BriarRecyclerView sharedByList, sharedWithList; private BriarRecyclerView list;
private SharingStatusAdapter sharedByAdapter, sharedWithAdapter; private SharingStatusAdapter adapter;
@Override @Override
public void onCreate(Bundle savedInstanceState) { public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
setContentView(R.layout.activity_sharing_status); setContentView(R.layout.list);
Intent i = getIntent(); Intent i = getIntent();
byte[] b = i.getByteArrayExtra(GROUP_ID); byte[] b = i.getByteArrayExtra(GROUP_ID);
if (b == null) throw new IllegalStateException("No GroupId"); if (b == null) throw new IllegalStateException("No GroupId");
groupId = new GroupId(b); groupId = new GroupId(b);
sharedByList = (BriarRecyclerView) findViewById(R.id.sharedByView); list = (BriarRecyclerView) findViewById(R.id.list);
sharedByAdapter = new SharingStatusAdapter(this); adapter = new SharingStatusAdapter(this);
sharedByList.setLayoutManager(new LinearLayoutManager(this)); list.setLayoutManager(new LinearLayoutManager(this));
sharedByList.setAdapter(sharedByAdapter); list.setAdapter(adapter);
sharedByList.setEmptyText(getString(R.string.nobody)); list.setEmptyText(getString(R.string.nobody));
sharedWithList = (BriarRecyclerView) findViewById(R.id.sharedWithView);
sharedWithAdapter = new SharingStatusAdapter(this);
sharedWithList.setLayoutManager(new LinearLayoutManager(this));
sharedWithList.setAdapter(sharedWithAdapter);
sharedWithList.setEmptyText(getString(R.string.nobody));
} }
@Override @Override
public void onStart() { public void onStart() {
super.onStart(); super.onStart();
loadSharedBy();
loadSharedWith(); loadSharedWith();
} }
@Override @Override
public void onStop() { public void onStop() {
super.onStop(); super.onStop();
sharedByAdapter.clear(); adapter.clear();
sharedByList.showProgressBar(); list.showProgressBar();
sharedWithAdapter.clear();
sharedWithList.showProgressBar();
} }
@Override @Override
@@ -81,49 +84,13 @@ abstract class SharingStatusActivity extends BriarActivity {
} }
} }
/** @DatabaseExecutor
* This must only be called from the DbThread
*/
abstract protected Collection<Contact> getSharedWith() throws DbException; abstract protected Collection<Contact> getSharedWith() throws DbException;
/**
* This must only be called from the DbThread
*/
abstract protected Collection<Contact> getSharedBy() throws DbException;
protected GroupId getGroupId() { protected GroupId getGroupId() {
return groupId; return groupId;
} }
private void loadSharedBy() {
runOnDbThread(new Runnable() {
@Override
public void run() {
try {
List<ContactItem> contactItems = new ArrayList<>();
for (Contact c : getSharedBy()) {
ContactItem item = new ContactItem(c);
contactItems.add(item);
}
displaySharedBy(contactItems);
} catch (DbException e) {
if (LOG.isLoggable(WARNING))
LOG.log(WARNING, e.toString(), e);
}
}
});
}
private void displaySharedBy(final List<ContactItem> contacts) {
runOnUiThreadUnlessDestroyed(new Runnable() {
@Override
public void run() {
if (contacts.isEmpty()) sharedByList.showData();
else sharedByAdapter.addAll(contacts);
}
});
}
private void loadSharedWith() { private void loadSharedWith() {
runOnDbThread(new Runnable() { runOnDbThread(new Runnable() {
@Override @Override
@@ -131,7 +98,9 @@ abstract class SharingStatusActivity extends BriarActivity {
try { try {
List<ContactItem> contactItems = new ArrayList<>(); List<ContactItem> contactItems = new ArrayList<>();
for (Contact c : getSharedWith()) { for (Contact c : getSharedWith()) {
ContactItem item = new ContactItem(c); boolean online =
connectionRegistry.isConnected(c.getId());
ContactItem item = new ContactItem(c, online);
contactItems.add(item); contactItems.add(item);
} }
displaySharedWith(contactItems); displaySharedWith(contactItems);
@@ -147,8 +116,8 @@ abstract class SharingStatusActivity extends BriarActivity {
runOnUiThreadUnlessDestroyed(new Runnable() { runOnUiThreadUnlessDestroyed(new Runnable() {
@Override @Override
public void run() { public void run() {
if (contacts.isEmpty()) sharedWithList.showData(); if (contacts.isEmpty()) list.showData();
else sharedWithAdapter.addAll(contacts); else adapter.addAll(contacts);
} }
}); });
} }

View File

@@ -1,48 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<ScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/default_separator_inverted"
android:padding="@dimen/margin_medium"
android:text="@string/forum_shared_by"
android:textSize="@dimen/text_size_large"/>
<View style="@style/Divider.ForumList"/>
<org.briarproject.briar.android.view.BriarRecyclerView
android:id="@+id/sharedByView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="@dimen/margin_medium"
android:paddingTop="@dimen/margin_medium"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/default_separator_inverted"
android:padding="@dimen/margin_medium"
android:text="@string/forum_shared_with"
android:textSize="@dimen/text_size_large"/>
<View style="@style/Divider.ForumList"/>
<org.briarproject.briar.android.view.BriarRecyclerView
android:id="@+id/sharedWithView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="@dimen/margin_medium"
android:paddingTop="@dimen/margin_medium"/>
</LinearLayout>
</ScrollView>

View File

@@ -5,28 +5,38 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="horizontal" android:orientation="horizontal"
android:paddingBottom="@dimen/margin_small" android:paddingBottom="@dimen/margin_medium"
android:paddingTop="@dimen/margin_small"> android:paddingTop="@dimen/margin_medium">
<de.hdodenhof.circleimageview.CircleImageView <de.hdodenhof.circleimageview.CircleImageView
android:id="@+id/avatarView" android:id="@+id/avatarView"
style="@style/BriarAvatar" style="@style/BriarAvatar"
android:layout_width="@dimen/listitem_picture_size_small" android:layout_width="@dimen/listitem_picture_size_small"
android:layout_height="@dimen/listitem_picture_size_small" android:layout_height="@dimen/listitem_picture_size_small"
android:layout_gravity="center_vertical"
android:layout_marginLeft="@dimen/listitem_horizontal_margin" android:layout_marginLeft="@dimen/listitem_horizontal_margin"
android:layout_marginStart="@dimen/listitem_horizontal_margin" android:layout_marginStart="@dimen/listitem_horizontal_margin"
tools:src="@drawable/ic_launcher"/> tools:src="@drawable/ic_launcher"/>
<org.thoughtcrime.securesms.components.emoji.EmojiTextView <org.thoughtcrime.securesms.components.emoji.EmojiTextView
android:id="@+id/nameView" android:id="@+id/nameView"
android:layout_width="wrap_content" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center_vertical" android:layout_gravity="center_vertical"
android:layout_marginLeft="@dimen/listitem_horizontal_margin" android:layout_marginLeft="@dimen/margin_medium"
android:layout_marginStart="@dimen/listitem_horizontal_margin" android:layout_marginStart="@dimen/margin_medium"
android:layout_weight="1"
android:maxLines="2" android:maxLines="2"
android:textColor="@color/briar_text_primary" android:textColor="@color/briar_text_primary"
android:textSize="@dimen/text_size_medium" android:textSize="@dimen/text_size_medium"
tools:text="This is a name of a contact"/> tools:text="This is a name of a contact"/>
</LinearLayout> <ImageView
android:id="@+id/bulbView"
android:layout_width="@dimen/listitem_horizontal_margin"
android:layout_height="@dimen/listitem_horizontal_margin"
android:layout_gravity="center_vertical"
android:layout_marginRight="@dimen/listitem_horizontal_margin"
tools:src="@drawable/contact_connected"/>
</LinearLayout>

View File

@@ -263,8 +263,6 @@
<item quantity="one">%d forum shared by contacts</item> <item quantity="one">%d forum shared by contacts</item>
<item quantity="other">%d forums shared by contacts</item> <item quantity="other">%d forums shared by contacts</item>
</plurals> </plurals>
<string name="forum_shared_by">Shared by</string>
<string name="forum_shared_with">Shared with</string>
<string name="nobody">Nobody</string> <string name="nobody">Nobody</string>
<!-- Blogs --> <!-- Blogs -->

View File

@@ -47,11 +47,6 @@ public interface SharingManager<S extends Shareable>
*/ */
Collection<SharingInvitationItem> getInvitations() throws DbException; Collection<SharingInvitationItem> getInvitations() throws DbException;
/**
* Returns all contacts who are sharing the given group with us.
*/
Collection<Contact> getSharedBy(GroupId g) throws DbException;
/** /**
* Returns all contacts with whom the given group is shared. * Returns all contacts with whom the given group is shared.
*/ */

View File

@@ -5,6 +5,7 @@ import org.briarproject.bramble.api.client.ClientHelper;
import org.briarproject.bramble.api.client.ContactGroupFactory; import org.briarproject.bramble.api.client.ContactGroupFactory;
import org.briarproject.bramble.api.contact.Contact; import org.briarproject.bramble.api.contact.Contact;
import org.briarproject.bramble.api.contact.ContactId; import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.contact.ContactManager;
import org.briarproject.bramble.api.data.BdfDictionary; import org.briarproject.bramble.api.data.BdfDictionary;
import org.briarproject.bramble.api.data.BdfList; import org.briarproject.bramble.api.data.BdfList;
import org.briarproject.bramble.api.data.MetadataEncoder; import org.briarproject.bramble.api.data.MetadataEncoder;
@@ -37,6 +38,7 @@ import org.briarproject.briar.api.client.SessionId;
import org.briarproject.briar.api.sharing.InvitationMessage; import org.briarproject.briar.api.sharing.InvitationMessage;
import java.security.SecureRandom; import java.security.SecureRandom;
import java.util.Collection;
import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.Immutable;
import javax.inject.Inject; import javax.inject.Inject;
@@ -52,6 +54,7 @@ class BlogSharingManagerImpl extends
SharingManagerImpl<Blog, BlogInvitation, BlogInviteeSessionState, BlogSharerSessionState, BlogInvitationRequestReceivedEvent, BlogInvitationResponseReceivedEvent> SharingManagerImpl<Blog, BlogInvitation, BlogInviteeSessionState, BlogSharerSessionState, BlogInvitationRequestReceivedEvent, BlogInvitationResponseReceivedEvent>
implements BlogSharingManager, RemoveBlogHook { implements BlogSharingManager, RemoveBlogHook {
private final ContactManager contactManager;
private final IdentityManager identityManager; private final IdentityManager identityManager;
private final BlogManager blogManager; private final BlogManager blogManager;
@@ -68,13 +71,15 @@ class BlogSharingManagerImpl extends
DatabaseComponent db, MessageQueueManager messageQueueManager, DatabaseComponent db, MessageQueueManager messageQueueManager,
MetadataEncoder metadataEncoder, MetadataParser metadataParser, MetadataEncoder metadataEncoder, MetadataParser metadataParser,
ContactGroupFactory contactGroupFactory, SecureRandom random, ContactGroupFactory contactGroupFactory, SecureRandom random,
IdentityManager identityManager, MessageTracker messageTracker) { ContactManager contactManager, IdentityManager identityManager,
MessageTracker messageTracker) {
super(db, messageQueueManager, clientHelper, metadataParser, super(db, messageQueueManager, clientHelper, metadataParser,
metadataEncoder, random, contactGroupFactory, messageTracker, metadataEncoder, random, contactGroupFactory, messageTracker,
clock); clock);
this.blogManager = blogManager; this.blogManager = blogManager;
this.contactManager = contactManager;
this.identityManager = identityManager; this.identityManager = identityManager;
sFactory = new SFactory(authorFactory, blogFactory, blogManager); sFactory = new SFactory(authorFactory, blogFactory, blogManager);
iFactory = new IFactory(); iFactory = new IFactory();
@@ -105,6 +110,27 @@ class BlogSharingManagerImpl extends
return super.canBeShared(txn, g, c); return super.canBeShared(txn, g, c);
} }
@Override
public Collection<Contact> getSharedWith(GroupId g) throws DbException {
Blog blog = blogManager.getBlog(g);
LocalAuthor author = identityManager.getLocalAuthor();
if (blog.getAuthor().equals(author)) {
// This is our personal blog. It is shared with all our contacts
return contactManager.getActiveContacts();
} else {
// This is someone else's blog. Look up who it is shared with
Collection<Contact> shared = super.getSharedWith(g);
// If the blog author is our contact, also add her to the list
boolean isContact = contactManager
.contactExists(blog.getAuthor().getId(), author.getId());
if (isContact) {
shared.add(contactManager
.getContact(blog.getAuthor().getId(), author.getId()));
}
return shared;
}
}
@Override @Override
protected InvitationMessage createInvitationRequest(MessageId id, protected InvitationMessage createInvitationRequest(MessageId id,
BlogInvitation msg, ContactId contactId, boolean available, BlogInvitation msg, ContactId contactId, boolean available,

View File

@@ -514,34 +514,6 @@ abstract class SharingManagerImpl<S extends Shareable, I extends Invitation, IS
return invited; return invited;
} }
@Override
public Collection<Contact> getSharedBy(GroupId g) throws DbException {
List<Contact> subscribers;
Transaction txn = db.startTransaction(true);
try {
subscribers = getSharedBy(txn, g);
db.commitTransaction(txn);
} finally {
db.endTransaction(txn);
}
return subscribers;
}
private List<Contact> getSharedBy(Transaction txn, GroupId g)
throws DbException {
try {
List<Contact> subscribers = new ArrayList<Contact>();
for (Contact c : db.getContacts(txn)) {
GroupId contactGroup = getContactGroup(c).getId();
if (listContains(txn, contactGroup, g, SHARED_WITH_US))
subscribers.add(c);
}
return subscribers;
} catch (IOException e) {
throw new DbException(e);
}
}
@Override @Override
public Collection<Contact> getSharedWith(GroupId g) throws DbException { public Collection<Contact> getSharedWith(GroupId g) throws DbException {
try { try {
@@ -552,6 +524,8 @@ abstract class SharingManagerImpl<S extends Shareable, I extends Invitation, IS
GroupId contactGroup = getContactGroup(c).getId(); GroupId contactGroup = getContactGroup(c).getId();
if (listContains(txn, contactGroup, g, SHARED_BY_US)) if (listContains(txn, contactGroup, g, SHARED_BY_US))
shared.add(c); shared.add(c);
else if (listContains(txn, contactGroup, g, SHARED_WITH_US))
shared.add(c);
} }
db.commitTransaction(txn); db.commitTransaction(txn);
} finally { } finally {

View File

@@ -1,11 +1,12 @@
package org.briarproject.bramble.contact; package org.briarproject.bramble.contact;
import org.briarproject.BriarTestCase; import org.briarproject.BriarMockTestCase;
import org.briarproject.bramble.api.contact.Contact; import org.briarproject.bramble.api.contact.Contact;
import org.briarproject.bramble.api.contact.ContactId; import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.contact.ContactManager; import org.briarproject.bramble.api.contact.ContactManager;
import org.briarproject.bramble.api.crypto.SecretKey; import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.db.DatabaseComponent; import org.briarproject.bramble.api.db.DatabaseComponent;
import org.briarproject.bramble.api.db.NoSuchContactException;
import org.briarproject.bramble.api.db.Transaction; import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.identity.Author; import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.identity.AuthorId; import org.briarproject.bramble.api.identity.AuthorId;
@@ -24,7 +25,7 @@ import static org.briarproject.TestUtils.getSecretKey;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
public class ContactManagerImplTest extends BriarTestCase { public class ContactManagerImplTest extends BriarMockTestCase {
private final Mockery context = new Mockery(); private final Mockery context = new Mockery();
private final DatabaseComponent db = context.mock(DatabaseComponent.class); private final DatabaseComponent db = context.mock(DatabaseComponent.class);
@@ -66,7 +67,6 @@ public class ContactManagerImplTest extends BriarTestCase {
assertEquals(contactId, contactManager assertEquals(contactId, contactManager
.addContact(remote, local, master, timestamp, alice, verified, .addContact(remote, local, master, timestamp, alice, verified,
active)); active));
context.assertIsSatisfied();
} }
@Test @Test
@@ -82,7 +82,51 @@ public class ContactManagerImplTest extends BriarTestCase {
}}); }});
assertEquals(contact, contactManager.getContact(contactId)); assertEquals(contact, contactManager.getContact(contactId));
context.assertIsSatisfied(); }
@Test
public void testGetContactByAuthor() throws Exception {
final Transaction txn = new Transaction(null, true);
final Collection<Contact> contacts = Collections.singleton(contact);
context.checking(new Expectations() {{
oneOf(db).startTransaction(true);
will(returnValue(txn));
oneOf(db).getContactsByAuthorId(txn, remote.getId());
will(returnValue(contacts));
oneOf(db).commitTransaction(txn);
oneOf(db).endTransaction(txn);
}});
assertEquals(contact, contactManager.getContact(remote.getId(), local));
}
@Test(expected = NoSuchContactException.class)
public void testGetContactByUnknownAuthor() throws Exception {
final Transaction txn = new Transaction(null, true);
context.checking(new Expectations() {{
oneOf(db).startTransaction(true);
will(returnValue(txn));
oneOf(db).getContactsByAuthorId(txn, remote.getId());
will(returnValue(Collections.emptyList()));
oneOf(db).endTransaction(txn);
}});
contactManager.getContact(remote.getId(), local);
}
@Test(expected = NoSuchContactException.class)
public void testGetContactByUnknownLocalAuthor() throws Exception {
final Transaction txn = new Transaction(null, true);
final Collection<Contact> contacts = Collections.singleton(contact);
context.checking(new Expectations() {{
oneOf(db).startTransaction(true);
will(returnValue(txn));
oneOf(db).getContactsByAuthorId(txn, remote.getId());
will(returnValue(contacts));
oneOf(db).endTransaction(txn);
}});
contactManager.getContact(remote.getId(), new AuthorId(getRandomId()));
} }
@Test @Test
@@ -101,7 +145,6 @@ public class ContactManagerImplTest extends BriarTestCase {
}}); }});
assertEquals(activeContacts, contactManager.getActiveContacts()); assertEquals(activeContacts, contactManager.getActiveContacts());
context.assertIsSatisfied();
} }
@Test @Test
@@ -118,7 +161,6 @@ public class ContactManagerImplTest extends BriarTestCase {
}}); }});
contactManager.removeContact(contactId); contactManager.removeContact(contactId);
context.assertIsSatisfied();
} }
@Test @Test
@@ -129,7 +171,6 @@ public class ContactManagerImplTest extends BriarTestCase {
}}); }});
contactManager.setContactActive(txn, contactId, active); contactManager.setContactActive(txn, contactId, active);
context.assertIsSatisfied();
} }
@Test @Test
@@ -145,7 +186,6 @@ public class ContactManagerImplTest extends BriarTestCase {
}}); }});
assertTrue(contactManager.contactExists(remote.getId(), local)); assertTrue(contactManager.contactExists(remote.getId(), local));
context.assertIsSatisfied();
} }
} }