Skip to content
Snippets Groups Projects
Verified Commit d1cda4fb authored by akwizgran's avatar akwizgran
Browse files

Add recently connected state to core and UI.

parent 8fd9a40f
No related tags found
No related merge requests found
Pipeline #4348 passed
Showing
with 257 additions and 162 deletions
...@@ -5,8 +5,7 @@ import org.briarproject.bramble.api.contact.PendingContactId; ...@@ -5,8 +5,7 @@ import org.briarproject.bramble.api.contact.PendingContactId;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.event.ConnectionClosedEvent; import org.briarproject.bramble.api.plugin.event.ConnectionClosedEvent;
import org.briarproject.bramble.api.plugin.event.ConnectionOpenedEvent; import org.briarproject.bramble.api.plugin.event.ConnectionOpenedEvent;
import org.briarproject.bramble.api.plugin.event.ContactConnectedEvent; import org.briarproject.bramble.api.plugin.event.ConnectionStatusChangedEvent;
import org.briarproject.bramble.api.plugin.event.ContactDisconnectedEvent;
import org.briarproject.bramble.api.rendezvous.event.RendezvousConnectionClosedEvent; import org.briarproject.bramble.api.rendezvous.event.RendezvousConnectionClosedEvent;
import org.briarproject.bramble.api.rendezvous.event.RendezvousConnectionOpenedEvent; import org.briarproject.bramble.api.rendezvous.event.RendezvousConnectionOpenedEvent;
...@@ -21,15 +20,15 @@ public interface ConnectionRegistry { ...@@ -21,15 +20,15 @@ public interface ConnectionRegistry {
/** /**
* Registers a connection with the given contact over the given transport. * Registers a connection with the given contact over the given transport.
* Broadcasts {@link ConnectionOpenedEvent}. Also broadcasts * Broadcasts {@link ConnectionOpenedEvent}. Also broadcasts
* {@link ContactConnectedEvent} if this is the only connection with the * {@link ConnectionStatusChangedEvent} if this is the only connection with
* contact. * the contact.
*/ */
void registerConnection(ContactId c, TransportId t, boolean incoming); void registerConnection(ContactId c, TransportId t, boolean incoming);
/** /**
* Unregisters a connection with the given contact over the given transport. * Unregisters a connection with the given contact over the given transport.
* Broadcasts {@link ConnectionClosedEvent}. Also broadcasts * Broadcasts {@link ConnectionClosedEvent}. Also broadcasts
* {@link ContactDisconnectedEvent} if this is the only connection with * {@link ConnectionStatusChangedEvent} if this is the only connection with
* the contact. * the contact.
*/ */
void unregisterConnection(ContactId c, TransportId t, boolean incoming); void unregisterConnection(ContactId c, TransportId t, boolean incoming);
...@@ -45,9 +44,9 @@ public interface ConnectionRegistry { ...@@ -45,9 +44,9 @@ public interface ConnectionRegistry {
boolean isConnected(ContactId c, TransportId t); boolean isConnected(ContactId c, TransportId t);
/** /**
* Returns true if the given contact is connected via any transport. * Returns the connection status of the given contact via all transports.
*/ */
boolean isConnected(ContactId c); ConnectionStatus getConnectionStatus(ContactId c);
/** /**
* Registers a connection with the given pending contact. Broadcasts * Registers a connection with the given pending contact. Broadcasts
......
package org.briarproject.bramble.api.plugin;
public enum ConnectionStatus {
CONNECTED, RECENTLY_CONNECTED, DISCONNECTED
}
...@@ -3,24 +3,31 @@ package org.briarproject.bramble.api.plugin.event; ...@@ -3,24 +3,31 @@ package org.briarproject.bramble.api.plugin.event;
import org.briarproject.bramble.api.contact.ContactId; import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.event.Event; import org.briarproject.bramble.api.event.Event;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.ConnectionStatus;
import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.Immutable;
/** /**
* An event that is broadcast when a contact connects that was not previously * An event that is broadcast when a contact's connection status changes.
* connected via any transport.
*/ */
@Immutable @Immutable
@NotNullByDefault @NotNullByDefault
public class ContactConnectedEvent extends Event { public class ConnectionStatusChangedEvent extends Event {
private final ContactId contactId; private final ContactId contactId;
private final ConnectionStatus status;
public ContactConnectedEvent(ContactId contactId) { public ConnectionStatusChangedEvent(ContactId contactId,
ConnectionStatus status) {
this.contactId = contactId; this.contactId = contactId;
this.status = status;
} }
public ContactId getContactId() { public ContactId getContactId() {
return contactId; return contactId;
} }
public ConnectionStatus getConnectionStatus() {
return status;
}
} }
package org.briarproject.bramble.api.plugin.event;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.event.Event;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.concurrent.Immutable;
/**
* An event that is broadcast when a contact disconnects and is no longer
* connected via any transport.
*/
@Immutable
@NotNullByDefault
public class ContactDisconnectedEvent extends Event {
private final ContactId contactId;
public ContactDisconnectedEvent(ContactId contactId) {
this.contactId = contactId;
}
public ContactId getContactId() {
return contactId;
}
}
...@@ -6,30 +6,41 @@ import org.briarproject.bramble.api.contact.PendingContactId; ...@@ -6,30 +6,41 @@ import org.briarproject.bramble.api.contact.PendingContactId;
import org.briarproject.bramble.api.event.EventBus; import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.ConnectionRegistry; import org.briarproject.bramble.api.plugin.ConnectionRegistry;
import org.briarproject.bramble.api.plugin.ConnectionStatus;
import org.briarproject.bramble.api.plugin.TransportId; import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.plugin.event.ConnectionClosedEvent; import org.briarproject.bramble.api.plugin.event.ConnectionClosedEvent;
import org.briarproject.bramble.api.plugin.event.ConnectionOpenedEvent; import org.briarproject.bramble.api.plugin.event.ConnectionOpenedEvent;
import org.briarproject.bramble.api.plugin.event.ContactConnectedEvent; import org.briarproject.bramble.api.plugin.event.ConnectionStatusChangedEvent;
import org.briarproject.bramble.api.plugin.event.ContactDisconnectedEvent;
import org.briarproject.bramble.api.rendezvous.event.RendezvousConnectionClosedEvent; import org.briarproject.bramble.api.rendezvous.event.RendezvousConnectionClosedEvent;
import org.briarproject.bramble.api.rendezvous.event.RendezvousConnectionOpenedEvent; import org.briarproject.bramble.api.rendezvous.event.RendezvousConnectionOpenedEvent;
import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.api.system.Scheduler;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry;
import java.util.Set; import java.util.Set;
import java.util.concurrent.ScheduledExecutorService;
import java.util.logging.Logger; import java.util.logging.Logger;
import javax.annotation.concurrent.GuardedBy; import javax.annotation.concurrent.GuardedBy;
import javax.annotation.concurrent.ThreadSafe; import javax.annotation.concurrent.ThreadSafe;
import javax.inject.Inject; import javax.inject.Inject;
import static java.util.Collections.emptyList;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static java.util.concurrent.TimeUnit.MINUTES;
import static java.util.concurrent.TimeUnit.SECONDS;
import static java.util.logging.Level.INFO; import static java.util.logging.Level.INFO;
import static java.util.logging.Logger.getLogger; import static java.util.logging.Logger.getLogger;
import static org.briarproject.bramble.api.plugin.ConnectionStatus.CONNECTED;
import static org.briarproject.bramble.api.plugin.ConnectionStatus.DISCONNECTED;
import static org.briarproject.bramble.api.plugin.ConnectionStatus.RECENTLY_CONNECTED;
@ThreadSafe @ThreadSafe
@NotNullByDefault @NotNullByDefault
...@@ -38,22 +49,30 @@ class ConnectionRegistryImpl implements ConnectionRegistry { ...@@ -38,22 +49,30 @@ class ConnectionRegistryImpl implements ConnectionRegistry {
private static final Logger LOG = private static final Logger LOG =
getLogger(ConnectionRegistryImpl.class.getName()); getLogger(ConnectionRegistryImpl.class.getName());
private static final long RECENTLY_CONNECTED_MS = MINUTES.toMillis(1);
private static final long EXPIRY_INTERVAL_MS = SECONDS.toMillis(10);
private final EventBus eventBus; private final EventBus eventBus;
private final Clock clock;
private final Object lock = new Object(); private final Object lock = new Object();
@GuardedBy("lock") @GuardedBy("lock")
private final Map<TransportId, Multiset<ContactId>> contactConnections; private final Map<TransportId, Multiset<ContactId>> contactConnections;
@GuardedBy("lock") @GuardedBy("lock")
private final Multiset<ContactId> contactCounts; private final Map<ContactId, Counter> contactCounts;
@GuardedBy("lock") @GuardedBy("lock")
private final Set<PendingContactId> connectedPendingContacts; private final Set<PendingContactId> connectedPendingContacts;
@Inject @Inject
ConnectionRegistryImpl(EventBus eventBus) { ConnectionRegistryImpl(EventBus eventBus, Clock clock,
@Scheduler ScheduledExecutorService scheduler) {
this.eventBus = eventBus; this.eventBus = eventBus;
this.clock = clock;
contactConnections = new HashMap<>(); contactConnections = new HashMap<>();
contactCounts = new Multiset<>(); contactCounts = new HashMap<>();
connectedPendingContacts = new HashSet<>(); connectedPendingContacts = new HashSet<>();
scheduler.scheduleWithFixedDelay(this::expireRecentConnections,
EXPIRY_INTERVAL_MS, EXPIRY_INTERVAL_MS, MILLISECONDS);
} }
@Override @Override
...@@ -71,12 +90,22 @@ class ConnectionRegistryImpl implements ConnectionRegistry { ...@@ -71,12 +90,22 @@ class ConnectionRegistryImpl implements ConnectionRegistry {
contactConnections.put(t, m); contactConnections.put(t, m);
} }
m.add(c); m.add(c);
if (contactCounts.add(c) == 1) firstConnection = true;
Counter counter = contactCounts.get(c);
if (counter == null) {
counter = new Counter();
contactCounts.put(c, counter);
}
if (counter.connections == 0) {
counter.disconnectedTime = 0;
firstConnection = true;
}
counter.connections++;
} }
eventBus.broadcast(new ConnectionOpenedEvent(c, t, incoming)); eventBus.broadcast(new ConnectionOpenedEvent(c, t, incoming));
if (firstConnection) { if (firstConnection) {
LOG.info("Contact connected"); LOG.info("Contact connected");
eventBus.broadcast(new ContactConnectedEvent(c)); eventBus.broadcast(new ConnectionStatusChangedEvent(c, CONNECTED));
} }
} }
...@@ -93,12 +122,22 @@ class ConnectionRegistryImpl implements ConnectionRegistry { ...@@ -93,12 +122,22 @@ class ConnectionRegistryImpl implements ConnectionRegistry {
if (m == null || !m.contains(c)) if (m == null || !m.contains(c))
throw new IllegalArgumentException(); throw new IllegalArgumentException();
m.remove(c); m.remove(c);
if (contactCounts.remove(c) == 0) lastConnection = true;
Counter counter = contactCounts.get(c);
if (counter == null || counter.connections == 0) {
throw new IllegalArgumentException();
}
counter.connections--;
if (counter.connections == 0) {
counter.disconnectedTime = clock.currentTimeMillis();
lastConnection = true;
}
} }
eventBus.broadcast(new ConnectionClosedEvent(c, t, incoming)); eventBus.broadcast(new ConnectionClosedEvent(c, t, incoming));
if (lastConnection) { if (lastConnection) {
LOG.info("Contact disconnected"); LOG.info("Contact disconnected");
eventBus.broadcast(new ContactDisconnectedEvent(c)); eventBus.broadcast(
new ConnectionStatusChangedEvent(c, RECENTLY_CONNECTED));
} }
} }
...@@ -106,7 +145,7 @@ class ConnectionRegistryImpl implements ConnectionRegistry { ...@@ -106,7 +145,7 @@ class ConnectionRegistryImpl implements ConnectionRegistry {
public Collection<ContactId> getConnectedContacts(TransportId t) { public Collection<ContactId> getConnectedContacts(TransportId t) {
synchronized (lock) { synchronized (lock) {
Multiset<ContactId> m = contactConnections.get(t); Multiset<ContactId> m = contactConnections.get(t);
if (m == null) return Collections.emptyList(); if (m == null) return emptyList();
List<ContactId> ids = new ArrayList<>(m.keySet()); List<ContactId> ids = new ArrayList<>(m.keySet());
if (LOG.isLoggable(INFO)) if (LOG.isLoggable(INFO))
LOG.info(ids.size() + " contacts connected: " + t); LOG.info(ids.size() + " contacts connected: " + t);
...@@ -123,9 +162,11 @@ class ConnectionRegistryImpl implements ConnectionRegistry { ...@@ -123,9 +162,11 @@ class ConnectionRegistryImpl implements ConnectionRegistry {
} }
@Override @Override
public boolean isConnected(ContactId c) { public ConnectionStatus getConnectionStatus(ContactId c) {
synchronized (lock) { synchronized (lock) {
return contactCounts.contains(c); Counter counter = contactCounts.get(c);
if (counter == null) return DISCONNECTED;
return counter.connections > 0 ? CONNECTED : RECENTLY_CONNECTED;
} }
} }
...@@ -147,4 +188,36 @@ class ConnectionRegistryImpl implements ConnectionRegistry { ...@@ -147,4 +188,36 @@ class ConnectionRegistryImpl implements ConnectionRegistry {
} }
eventBus.broadcast(new RendezvousConnectionClosedEvent(p, success)); eventBus.broadcast(new RendezvousConnectionClosedEvent(p, success));
} }
@Scheduler
private void expireRecentConnections() {
long now = clock.currentTimeMillis();
List<ContactId> disconnected = new ArrayList<>();
synchronized (lock) {
Iterator<Entry<ContactId, Counter>> it =
contactCounts.entrySet().iterator();
while (it.hasNext()) {
Entry<ContactId, Counter> e = it.next();
if (e.getValue().isExpired(now)) {
disconnected.add(e.getKey());
it.remove();
}
}
}
for (ContactId c : disconnected) {
eventBus.broadcast(
new ConnectionStatusChangedEvent(c, DISCONNECTED));
}
}
private static class Counter {
private int connections = 0;
private long disconnectedTime = 0;
private boolean isExpired(long now) {
return connections == 0 &&
now - disconnectedTime > RECENTLY_CONNECTED_MS;
}
}
} }
...@@ -7,18 +7,20 @@ import org.briarproject.bramble.api.plugin.ConnectionRegistry; ...@@ -7,18 +7,20 @@ import org.briarproject.bramble.api.plugin.ConnectionRegistry;
import org.briarproject.bramble.api.plugin.TransportId; import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.plugin.event.ConnectionClosedEvent; import org.briarproject.bramble.api.plugin.event.ConnectionClosedEvent;
import org.briarproject.bramble.api.plugin.event.ConnectionOpenedEvent; import org.briarproject.bramble.api.plugin.event.ConnectionOpenedEvent;
import org.briarproject.bramble.api.plugin.event.ContactConnectedEvent; import org.briarproject.bramble.api.plugin.event.ConnectionStatusChangedEvent;
import org.briarproject.bramble.api.plugin.event.ContactDisconnectedEvent;
import org.briarproject.bramble.api.rendezvous.event.RendezvousConnectionClosedEvent; import org.briarproject.bramble.api.rendezvous.event.RendezvousConnectionClosedEvent;
import org.briarproject.bramble.api.rendezvous.event.RendezvousConnectionOpenedEvent; import org.briarproject.bramble.api.rendezvous.event.RendezvousConnectionOpenedEvent;
import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.test.BrambleMockTestCase; import org.briarproject.bramble.test.BrambleMockTestCase;
import org.jmock.Expectations; import org.jmock.Expectations;
import org.junit.Test; import org.junit.Test;
import java.util.Collection; import java.util.Collection;
import java.util.concurrent.ScheduledExecutorService;
import static java.util.Collections.emptyList; import static java.util.Collections.emptyList;
import static java.util.Collections.singletonList; import static java.util.Collections.singletonList;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static org.briarproject.bramble.test.TestUtils.getContactId; import static org.briarproject.bramble.test.TestUtils.getContactId;
import static org.briarproject.bramble.test.TestUtils.getRandomId; import static org.briarproject.bramble.test.TestUtils.getRandomId;
import static org.briarproject.bramble.test.TestUtils.getTransportId; import static org.briarproject.bramble.test.TestUtils.getTransportId;
...@@ -30,6 +32,9 @@ import static org.junit.Assert.fail; ...@@ -30,6 +32,9 @@ import static org.junit.Assert.fail;
public class ConnectionRegistryImplTest extends BrambleMockTestCase { public class ConnectionRegistryImplTest extends BrambleMockTestCase {
private final EventBus eventBus = context.mock(EventBus.class); private final EventBus eventBus = context.mock(EventBus.class);
private final Clock clock = context.mock(Clock.class);
private final ScheduledExecutorService scheduler =
context.mock(ScheduledExecutorService.class);
private final ContactId contactId = getContactId(); private final ContactId contactId = getContactId();
private final ContactId contactId1 = getContactId(); private final ContactId contactId1 = getContactId();
...@@ -40,17 +45,25 @@ public class ConnectionRegistryImplTest extends BrambleMockTestCase { ...@@ -40,17 +45,25 @@ public class ConnectionRegistryImplTest extends BrambleMockTestCase {
@Test @Test
public void testRegisterAndUnregister() { public void testRegisterAndUnregister() {
ConnectionRegistry c = new ConnectionRegistryImpl(eventBus); context.checking(new Expectations() {{
oneOf(scheduler).scheduleWithFixedDelay(with(any(Runnable.class)),
with(10_000L), with(10_000L), with(MILLISECONDS));
}});
ConnectionRegistry c = new ConnectionRegistryImpl(eventBus, clock,
scheduler);
context.assertIsSatisfied();
// The registry should be empty // The registry should be empty
assertEquals(emptyList(), c.getConnectedContacts(transportId)); assertEquals(emptyList(), c.getConnectedContacts(transportId));
assertEquals(emptyList(), c.getConnectedContacts(transportId1)); assertEquals(emptyList(), c.getConnectedContacts(transportId1));
// Check that a registered connection shows up - this should // Check that a registered connection shows up - this should
// broadcast a ConnectionOpenedEvent and a ContactConnectedEvent // broadcast a ConnectionOpenedEvent and a ConnectionStatusChangedEvent
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(eventBus).broadcast(with(any(ConnectionOpenedEvent.class))); oneOf(eventBus).broadcast(with(any(ConnectionOpenedEvent.class)));
oneOf(eventBus).broadcast(with(any(ContactConnectedEvent.class))); oneOf(eventBus).broadcast(with(any(
ConnectionStatusChangedEvent.class)));
}}); }});
c.registerConnection(contactId, transportId, true); c.registerConnection(contactId, transportId, true);
assertEquals(singletonList(contactId), assertEquals(singletonList(contactId),
...@@ -81,11 +94,13 @@ public class ConnectionRegistryImplTest extends BrambleMockTestCase { ...@@ -81,11 +94,13 @@ public class ConnectionRegistryImplTest extends BrambleMockTestCase {
context.assertIsSatisfied(); context.assertIsSatisfied();
// Unregister the other connection - this should broadcast a // Unregister the other connection - this should broadcast a
// ConnectionClosedEvent and a ContactDisconnectedEvent // ConnectionClosedEvent and a ConnectionStatusChangedEvent
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(clock).currentTimeMillis();
will(returnValue(System.currentTimeMillis()));
oneOf(eventBus).broadcast(with(any(ConnectionClosedEvent.class))); oneOf(eventBus).broadcast(with(any(ConnectionClosedEvent.class)));
oneOf(eventBus).broadcast(with(any( oneOf(eventBus).broadcast(with(any(
ContactDisconnectedEvent.class))); ConnectionStatusChangedEvent.class)));
}}); }});
c.unregisterConnection(contactId, transportId, true); c.unregisterConnection(contactId, transportId, true);
assertEquals(emptyList(), c.getConnectedContacts(transportId)); assertEquals(emptyList(), c.getConnectedContacts(transportId));
...@@ -102,12 +117,12 @@ public class ConnectionRegistryImplTest extends BrambleMockTestCase { ...@@ -102,12 +117,12 @@ public class ConnectionRegistryImplTest extends BrambleMockTestCase {
// Register both contacts with one transport, one contact with both - // Register both contacts with one transport, one contact with both -
// this should broadcast three ConnectionOpenedEvents and two // this should broadcast three ConnectionOpenedEvents and two
// ContactConnectedEvents // ConnectionStatusChangedEvents
context.checking(new Expectations() {{ context.checking(new Expectations() {{
exactly(3).of(eventBus).broadcast(with(any( exactly(3).of(eventBus).broadcast(with(any(
ConnectionOpenedEvent.class))); ConnectionOpenedEvent.class)));
exactly(2).of(eventBus).broadcast(with(any( exactly(2).of(eventBus).broadcast(with(any(
ContactConnectedEvent.class))); ConnectionStatusChangedEvent.class)));
}}); }});
c.registerConnection(contactId, transportId, true); c.registerConnection(contactId, transportId, true);
c.registerConnection(contactId1, transportId, true); c.registerConnection(contactId1, transportId, true);
...@@ -122,7 +137,14 @@ public class ConnectionRegistryImplTest extends BrambleMockTestCase { ...@@ -122,7 +137,14 @@ public class ConnectionRegistryImplTest extends BrambleMockTestCase {
@Test @Test
public void testRegisterAndUnregisterPendingContacts() { public void testRegisterAndUnregisterPendingContacts() {
ConnectionRegistry c = new ConnectionRegistryImpl(eventBus); context.checking(new Expectations() {{
oneOf(scheduler).scheduleWithFixedDelay(with(any(Runnable.class)),
with(10_000L), with(10_000L), with(MILLISECONDS));
}});
ConnectionRegistry c = new ConnectionRegistryImpl(eventBus, clock,
scheduler);
context.assertIsSatisfied();
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(eventBus).broadcast(with(any( oneOf(eventBus).broadcast(with(any(
......
...@@ -2,35 +2,38 @@ package org.briarproject.briar.android.contact; ...@@ -2,35 +2,38 @@ package org.briarproject.briar.android.contact;
import org.briarproject.bramble.api.contact.Contact; import org.briarproject.bramble.api.contact.Contact;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.ConnectionStatus;
import javax.annotation.concurrent.NotThreadSafe; import javax.annotation.concurrent.NotThreadSafe;
import static org.briarproject.bramble.api.plugin.ConnectionStatus.DISCONNECTED;
@NotThreadSafe @NotThreadSafe
@NotNullByDefault @NotNullByDefault
public class ContactItem { public class ContactItem {
private final Contact contact; private final Contact contact;
private boolean connected; private ConnectionStatus status;
public ContactItem(Contact contact) { public ContactItem(Contact contact) {
this(contact, false); this(contact, DISCONNECTED);
} }
public ContactItem(Contact contact, boolean connected) { public ContactItem(Contact contact, ConnectionStatus status) {
this.contact = contact; this.contact = contact;
this.connected = connected; this.status = status;
} }
public Contact getContact() { public Contact getContact() {
return contact; return contact;
} }
boolean isConnected() { ConnectionStatus getConnectionStatus() {
return connected; return status;
} }
void setConnected(boolean connected) { void setConnectionStatus(ConnectionStatus status) {
this.connected = connected; this.status = status;
} }
} }
...@@ -7,6 +7,7 @@ import android.widget.TextView; ...@@ -7,6 +7,7 @@ import android.widget.TextView;
import org.briarproject.bramble.api.identity.Author; import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.ConnectionStatus;
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;
...@@ -16,6 +17,8 @@ import androidx.annotation.UiThread; ...@@ -16,6 +17,8 @@ import androidx.annotation.UiThread;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
import im.delight.android.identicons.IdenticonDrawable; import im.delight.android.identicons.IdenticonDrawable;
import static org.briarproject.bramble.api.plugin.ConnectionStatus.CONNECTED;
import static org.briarproject.bramble.api.plugin.ConnectionStatus.RECENTLY_CONNECTED;
import static org.briarproject.briar.android.util.UiUtils.getContactDisplayName; import static org.briarproject.briar.android.util.UiUtils.getContactDisplayName;
@UiThread @UiThread
...@@ -27,7 +30,7 @@ public class ContactItemViewHolder<I extends ContactItem> ...@@ -27,7 +30,7 @@ public class ContactItemViewHolder<I extends ContactItem>
protected final ImageView avatar; protected final ImageView avatar;
protected final TextView name; protected final TextView name;
@Nullable @Nullable
protected final ImageView bulb; private final ImageView bulb;
public ContactItemViewHolder(View v) { public ContactItemViewHolder(View v) {
super(v); super(v);
...@@ -47,10 +50,13 @@ public class ContactItemViewHolder<I extends ContactItem> ...@@ -47,10 +50,13 @@ public class ContactItemViewHolder<I extends ContactItem>
if (bulb != null) { if (bulb != null) {
// online/offline // online/offline
if (item.isConnected()) { ConnectionStatus status = item.getConnectionStatus();
bulb.setImageResource(R.drawable.contact_connected); if (status == CONNECTED) {
bulb.setImageResource(R.drawable.ic_connected);
} else if (status == RECENTLY_CONNECTED) {
bulb.setImageResource(R.drawable.ic_recently_connected);
} else { } else {
bulb.setImageResource(R.drawable.contact_disconnected); bulb.setImageResource(R.drawable.ic_disconnected);
} }
} }
......
...@@ -39,7 +39,7 @@ public class ContactListAdapter extends ...@@ -39,7 +39,7 @@ public class ContactListAdapter extends
if (c1.getTimestamp() != c2.getTimestamp()) { if (c1.getTimestamp() != c2.getTimestamp()) {
return false; return false;
} }
return c1.isConnected() == c2.isConnected(); return c1.getConnectionStatus() == c2.getConnectionStatus();
} }
@Override @Override
......
...@@ -25,8 +25,8 @@ import org.briarproject.bramble.api.event.EventListener; ...@@ -25,8 +25,8 @@ import org.briarproject.bramble.api.event.EventListener;
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault; import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault; import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
import org.briarproject.bramble.api.plugin.ConnectionRegistry; import org.briarproject.bramble.api.plugin.ConnectionRegistry;
import org.briarproject.bramble.api.plugin.event.ContactConnectedEvent; import org.briarproject.bramble.api.plugin.ConnectionStatus;
import org.briarproject.bramble.api.plugin.event.ContactDisconnectedEvent; import org.briarproject.bramble.api.plugin.event.ConnectionStatusChangedEvent;
import org.briarproject.briar.R; import org.briarproject.briar.R;
import org.briarproject.briar.android.activity.ActivityComponent; import org.briarproject.briar.android.activity.ActivityComponent;
import org.briarproject.briar.android.contact.BaseContactListAdapter.OnContactClickListener; import org.briarproject.briar.android.contact.BaseContactListAdapter.OnContactClickListener;
...@@ -53,7 +53,6 @@ import javax.inject.Inject; ...@@ -53,7 +53,6 @@ import javax.inject.Inject;
import androidx.annotation.UiThread; import androidx.annotation.UiThread;
import androidx.core.app.ActivityCompat; import androidx.core.app.ActivityCompat;
import androidx.core.app.ActivityOptionsCompat; import androidx.core.app.ActivityOptionsCompat;
import androidx.core.util.Pair;
import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.LinearLayoutManager;
import io.github.kobakei.materialfabspeeddial.FabSpeedDial; import io.github.kobakei.materialfabspeeddial.FabSpeedDial;
import io.github.kobakei.materialfabspeeddial.FabSpeedDial.OnMenuItemClickListener; import io.github.kobakei.materialfabspeeddial.FabSpeedDial.OnMenuItemClickListener;
...@@ -137,26 +136,15 @@ public class ContactListFragment extends BaseFragment implements EventListener, ...@@ -137,26 +136,15 @@ public class ContactListFragment extends BaseFragment implements EventListener,
ContactId contactId = item.getContact().getId(); ContactId contactId = item.getContact().getId();
i.putExtra(CONTACT_ID, contactId.getInt()); i.putExtra(CONTACT_ID, contactId.getInt());
Bundle options = null;
// work-around for android bug #224270
if (SDK_INT >= 23 && !isSamsung7()) { if (SDK_INT >= 23 && !isSamsung7()) {
ContactListItemViewHolder holder = options = makeTransitionOptions(view);
(ContactListItemViewHolder) list }
.getRecyclerView() if (options == null) {
.findViewHolderForAdapterPosition(
adapter.findItemPosition(item));
Pair<View, String> avatar =
Pair.create(holder.avatar,
getTransitionName(holder.avatar));
Pair<View, String> bulb =
Pair.create(holder.bulb,
getTransitionName(holder.bulb));
ActivityOptionsCompat options =
makeSceneTransitionAnimation(getActivity(),
avatar, bulb);
ActivityCompat.startActivity(getActivity(), i,
options.toBundle());
} else {
// work-around for android bug #224270
startActivity(i); startActivity(i);
} else {
ActivityCompat.startActivity(getActivity(), i, options);
} }
}; };
adapter = new ContactListAdapter(requireContext(), adapter = new ContactListAdapter(requireContext(),
...@@ -171,6 +159,15 @@ public class ContactListFragment extends BaseFragment implements EventListener, ...@@ -171,6 +159,15 @@ public class ContactListFragment extends BaseFragment implements EventListener,
return contentView; return contentView;
} }
@Nullable
private Bundle makeTransitionOptions(View view) {
View avatar = view.findViewById(R.id.avatarView);
String name = requireNonNull(getTransitionName(avatar));
ActivityOptionsCompat options = makeSceneTransitionAnimation(
requireActivity(), view, name);
return options.toBundle();
}
@Override @Override
public void onMenuItemClick(FloatingActionButton fab, @Nullable TextView v, public void onMenuItemClick(FloatingActionButton fab, @Nullable TextView v,
int itemId) { int itemId) {
...@@ -232,9 +229,9 @@ public class ContactListFragment extends BaseFragment implements EventListener, ...@@ -232,9 +229,9 @@ public class ContactListFragment extends BaseFragment implements EventListener,
ContactId id = c.getId(); ContactId id = c.getId();
GroupCount count = GroupCount count =
conversationManager.getGroupCount(id); conversationManager.getGroupCount(id);
boolean connected = ConnectionStatus status = connectionRegistry
connectionRegistry.isConnected(c.getId()); .getConnectionStatus(c.getId());
contacts.add(new ContactListItem(c, connected, count)); contacts.add(new ContactListItem(c, status, count));
} catch (NoSuchContactException e) { } catch (NoSuchContactException e) {
// Continue // Continue
} }
...@@ -265,10 +262,9 @@ public class ContactListFragment extends BaseFragment implements EventListener, ...@@ -265,10 +262,9 @@ public class ContactListFragment extends BaseFragment implements EventListener,
if (e instanceof ContactAddedEvent) { if (e instanceof ContactAddedEvent) {
LOG.info("Contact added, reloading"); LOG.info("Contact added, reloading");
loadContacts(); loadContacts();
} else if (e instanceof ContactConnectedEvent) { } else if (e instanceof ConnectionStatusChangedEvent) {
setConnected(((ContactConnectedEvent) e).getContactId(), true); ConnectionStatusChangedEvent c = (ConnectionStatusChangedEvent) e;
} else if (e instanceof ContactDisconnectedEvent) { setConnectionStatus(c.getContactId(), c.getConnectionStatus());
setConnected(((ContactDisconnectedEvent) e).getContactId(), false);
} else if (e instanceof ContactRemovedEvent) { } else if (e instanceof ContactRemovedEvent) {
LOG.info("Contact removed, removing item"); LOG.info("Contact removed, removing item");
removeItem(((ContactRemovedEvent) e).getContactId()); removeItem(((ContactRemovedEvent) e).getContactId());
...@@ -304,12 +300,12 @@ public class ContactListFragment extends BaseFragment implements EventListener, ...@@ -304,12 +300,12 @@ public class ContactListFragment extends BaseFragment implements EventListener,
} }
@UiThread @UiThread
private void setConnected(ContactId c, boolean connected) { private void setConnectionStatus(ContactId c, ConnectionStatus status) {
adapter.incrementRevision(); adapter.incrementRevision();
int position = adapter.findItemPosition(c); int position = adapter.findItemPosition(c);
ContactListItem item = adapter.getItemAt(position); ContactListItem item = adapter.getItemAt(position);
if (item != null) { if (item != null) {
item.setConnected(connected); item.setConnectionStatus(status);
adapter.updateItemAt(position, item); adapter.updateItemAt(position, item);
} }
} }
......
...@@ -2,6 +2,7 @@ package org.briarproject.briar.android.contact; ...@@ -2,6 +2,7 @@ package org.briarproject.briar.android.contact;
import org.briarproject.bramble.api.contact.Contact; import org.briarproject.bramble.api.contact.Contact;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.ConnectionStatus;
import org.briarproject.briar.api.client.MessageTracker.GroupCount; import org.briarproject.briar.api.client.MessageTracker.GroupCount;
import org.briarproject.briar.api.conversation.ConversationMessageHeader; import org.briarproject.briar.api.conversation.ConversationMessageHeader;
...@@ -15,9 +16,9 @@ public class ContactListItem extends ContactItem { ...@@ -15,9 +16,9 @@ public class ContactListItem extends ContactItem {
private long timestamp; private long timestamp;
private int unread; private int unread;
public ContactListItem(Contact contact, boolean connected, public ContactListItem(Contact contact, ConnectionStatus status,
GroupCount count) { GroupCount count) {
super(contact, connected); super(contact, status);
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();
......
...@@ -7,7 +7,6 @@ import org.briarproject.bramble.api.contact.ContactId; ...@@ -7,7 +7,6 @@ import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; 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 org.briarproject.briar.android.util.UiUtils;
import java.util.Locale; import java.util.Locale;
...@@ -15,8 +14,11 @@ import javax.annotation.Nullable; ...@@ -15,8 +14,11 @@ import javax.annotation.Nullable;
import androidx.annotation.UiThread; import androidx.annotation.UiThread;
import static android.view.View.INVISIBLE;
import static android.view.View.VISIBLE;
import static androidx.core.view.ViewCompat.setTransitionName; import static androidx.core.view.ViewCompat.setTransitionName;
import static org.briarproject.briar.android.util.UiUtils.formatDate; import static org.briarproject.briar.android.util.UiUtils.formatDate;
import static org.briarproject.briar.android.util.UiUtils.getAvatarTransitionName;
@UiThread @UiThread
@NotNullByDefault @NotNullByDefault
...@@ -39,10 +41,11 @@ class ContactListItemViewHolder extends ContactItemViewHolder<ContactListItem> { ...@@ -39,10 +41,11 @@ class ContactListItemViewHolder extends ContactItemViewHolder<ContactListItem> {
// unread count // unread count
int unreadCount = item.getUnreadCount(); int unreadCount = item.getUnreadCount();
if (unreadCount > 0) { if (unreadCount > 0) {
unread.setText(String.format(Locale.getDefault(), "%d", unreadCount)); unread.setText(String.format(Locale.getDefault(), "%d",
unread.setVisibility(View.VISIBLE); unreadCount));
unread.setVisibility(VISIBLE);
} else { } else {
unread.setVisibility(View.INVISIBLE); unread.setVisibility(INVISIBLE);
} }
// date of last message // date of last message
...@@ -54,8 +57,7 @@ class ContactListItemViewHolder extends ContactItemViewHolder<ContactListItem> { ...@@ -54,8 +57,7 @@ class ContactListItemViewHolder extends ContactItemViewHolder<ContactListItem> {
} }
ContactId c = item.getContact().getId(); ContactId c = item.getContact().getId();
setTransitionName(avatar, UiUtils.getAvatarTransitionName(c)); setTransitionName(avatar, getAvatarTransitionName(c));
setTransitionName(bulb, UiUtils.getBulbTransitionName(c));
} }
} }
...@@ -6,8 +6,7 @@ import org.briarproject.bramble.api.event.EventBus; ...@@ -6,8 +6,7 @@ import org.briarproject.bramble.api.event.EventBus;
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;
import org.briarproject.bramble.api.plugin.ConnectionRegistry; import org.briarproject.bramble.api.plugin.ConnectionRegistry;
import org.briarproject.bramble.api.plugin.event.ContactConnectedEvent; import org.briarproject.bramble.api.plugin.event.ConnectionStatusChangedEvent;
import org.briarproject.bramble.api.plugin.event.ContactDisconnectedEvent;
import java.util.Collection; import java.util.Collection;
import java.util.HashSet; import java.util.HashSet;
...@@ -18,6 +17,8 @@ import javax.inject.Inject; ...@@ -18,6 +17,8 @@ import javax.inject.Inject;
import androidx.annotation.UiThread; import androidx.annotation.UiThread;
import static org.briarproject.bramble.api.plugin.ConnectionStatus.CONNECTED;
@NotNullByDefault @NotNullByDefault
public class SharingControllerImpl implements SharingController, EventListener { public class SharingControllerImpl implements SharingController, EventListener {
...@@ -60,15 +61,14 @@ public class SharingControllerImpl implements SharingController, EventListener { ...@@ -60,15 +61,14 @@ public class SharingControllerImpl implements SharingController, EventListener {
@Override @Override
public void eventOccurred(Event e) { public void eventOccurred(Event e) {
if (e instanceof ContactConnectedEvent) { if (e instanceof ConnectionStatusChangedEvent) {
setConnected(((ContactConnectedEvent) e).getContactId()); ConnectionStatusChangedEvent c = (ConnectionStatusChangedEvent) e;
} else if (e instanceof ContactDisconnectedEvent) { setConnectionStatus(c.getContactId());
setConnected(((ContactDisconnectedEvent) e).getContactId());
} }
} }
@UiThread @UiThread
private void setConnected(ContactId c) { private void setConnectionStatus(ContactId c) {
if (listener == null) throw new IllegalStateException(); if (listener == null) throw new IllegalStateException();
if (contacts.contains(c)) { if (contacts.contains(c)) {
int online = getOnlineCount(); int online = getOnlineCount();
...@@ -95,7 +95,9 @@ public class SharingControllerImpl implements SharingController, EventListener { ...@@ -95,7 +95,9 @@ public class SharingControllerImpl implements SharingController, EventListener {
public int getOnlineCount() { public int getOnlineCount() {
int online = 0; int online = 0;
for (ContactId c : contacts) { for (ContactId c : contacts) {
if (connectionRegistry.isConnected(c)) online++; if (connectionRegistry.getConnectionStatus(c) == CONNECTED) {
online++;
}
} }
return online; return online;
} }
......
...@@ -13,7 +13,6 @@ import android.view.Menu; ...@@ -13,7 +13,6 @@ import android.view.Menu;
import android.view.MenuInflater; import android.view.MenuInflater;
import android.view.MenuItem; import android.view.MenuItem;
import android.view.View; import android.view.View;
import android.widget.ImageView;
import android.widget.TextView; import android.widget.TextView;
import android.widget.Toast; import android.widget.Toast;
...@@ -34,8 +33,8 @@ import org.briarproject.bramble.api.event.EventListener; ...@@ -34,8 +33,8 @@ import org.briarproject.bramble.api.event.EventListener;
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault; import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault; import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
import org.briarproject.bramble.api.plugin.ConnectionRegistry; import org.briarproject.bramble.api.plugin.ConnectionRegistry;
import org.briarproject.bramble.api.plugin.event.ContactConnectedEvent; import org.briarproject.bramble.api.plugin.ConnectionStatus;
import org.briarproject.bramble.api.plugin.event.ContactDisconnectedEvent; import org.briarproject.bramble.api.plugin.event.ConnectionStatusChangedEvent;
import org.briarproject.bramble.api.sync.ClientId; import org.briarproject.bramble.api.sync.ClientId;
import org.briarproject.bramble.api.sync.MessageId; import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.bramble.api.sync.event.MessagesAckedEvent; import org.briarproject.bramble.api.sync.event.MessagesAckedEvent;
...@@ -125,6 +124,8 @@ import static java.util.Objects.requireNonNull; ...@@ -125,6 +124,8 @@ import static java.util.Objects.requireNonNull;
import static java.util.logging.Level.INFO; import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING; import static java.util.logging.Level.WARNING;
import static java.util.logging.Logger.getLogger; import static java.util.logging.Logger.getLogger;
import static org.briarproject.bramble.api.plugin.ConnectionStatus.CONNECTED;
import static org.briarproject.bramble.api.plugin.ConnectionStatus.RECENTLY_CONNECTED;
import static org.briarproject.bramble.util.LogUtils.logDuration; import static org.briarproject.bramble.util.LogUtils.logDuration;
import static org.briarproject.bramble.util.LogUtils.logException; import static org.briarproject.bramble.util.LogUtils.logException;
import static org.briarproject.bramble.util.LogUtils.now; import static org.briarproject.bramble.util.LogUtils.now;
...@@ -138,7 +139,6 @@ import static org.briarproject.briar.android.conversation.ImageActivity.ATTACHME ...@@ -138,7 +139,6 @@ import static org.briarproject.briar.android.conversation.ImageActivity.ATTACHME
import static org.briarproject.briar.android.conversation.ImageActivity.DATE; import static org.briarproject.briar.android.conversation.ImageActivity.DATE;
import static org.briarproject.briar.android.conversation.ImageActivity.NAME; import static org.briarproject.briar.android.conversation.ImageActivity.NAME;
import static org.briarproject.briar.android.util.UiUtils.getAvatarTransitionName; import static org.briarproject.briar.android.util.UiUtils.getAvatarTransitionName;
import static org.briarproject.briar.android.util.UiUtils.getBulbTransitionName;
import static org.briarproject.briar.android.util.UiUtils.observeOnce; import static org.briarproject.briar.android.util.UiUtils.observeOnce;
import static org.briarproject.briar.api.messaging.MessagingConstants.MAX_ATTACHMENTS_PER_MESSAGE; import static org.briarproject.briar.api.messaging.MessagingConstants.MAX_ATTACHMENTS_PER_MESSAGE;
import static org.briarproject.briar.api.messaging.MessagingConstants.MAX_PRIVATE_MESSAGE_TEXT_LENGTH; import static org.briarproject.briar.api.messaging.MessagingConstants.MAX_PRIVATE_MESSAGE_TEXT_LENGTH;
...@@ -198,8 +198,8 @@ public class ConversationActivity extends BriarActivity ...@@ -198,8 +198,8 @@ public class ConversationActivity extends BriarActivity
private ConversationAdapter adapter; private ConversationAdapter adapter;
private Toolbar toolbar; private Toolbar toolbar;
private CircleImageView toolbarAvatar; private CircleImageView toolbarAvatar;
private ImageView toolbarStatus;
private TextView toolbarTitle; private TextView toolbarTitle;
private TextView toolbarStatus;
private BriarRecyclerView list; private BriarRecyclerView list;
private LinearLayoutManager layoutManager; private LinearLayoutManager layoutManager;
private TextInputView textInputView; private TextInputView textInputView;
...@@ -237,8 +237,8 @@ public class ConversationActivity extends BriarActivity ...@@ -237,8 +237,8 @@ public class ConversationActivity extends BriarActivity
// Custom Toolbar // Custom Toolbar
toolbar = requireNonNull(setUpCustomToolbar(true)); toolbar = requireNonNull(setUpCustomToolbar(true));
toolbarAvatar = toolbar.findViewById(R.id.contactAvatar); toolbarAvatar = toolbar.findViewById(R.id.contactAvatar);
toolbarStatus = toolbar.findViewById(R.id.contactStatus);
toolbarTitle = toolbar.findViewById(R.id.contactName); toolbarTitle = toolbar.findViewById(R.id.contactName);
toolbarStatus = toolbar.findViewById(R.id.contactStatus);
observeOnce(viewModel.getContactAuthorId(), this, authorId -> { observeOnce(viewModel.getContactAuthorId(), this, authorId -> {
requireNonNull(authorId); requireNonNull(authorId);
...@@ -257,7 +257,6 @@ public class ConversationActivity extends BriarActivity ...@@ -257,7 +257,6 @@ public class ConversationActivity extends BriarActivity
this::onAddedPrivateMessage); this::onAddedPrivateMessage);
setTransitionName(toolbarAvatar, getAvatarTransitionName(contactId)); setTransitionName(toolbarAvatar, getAvatarTransitionName(contactId));
setTransitionName(toolbarStatus, getBulbTransitionName(contactId));
visitor = new ConversationVisitor(this, this, this, visitor = new ConversationVisitor(this, this, this,
viewModel.getContactDisplayName()); viewModel.getContactDisplayName());
...@@ -499,14 +498,14 @@ public class ConversationActivity extends BriarActivity ...@@ -499,14 +498,14 @@ public class ConversationActivity extends BriarActivity
@UiThread @UiThread
private void displayContactOnlineStatus() { private void displayContactOnlineStatus() {
if (connectionRegistry.isConnected(contactId)) { ConnectionStatus status =
toolbarStatus.setImageDrawable(ContextCompat.getDrawable( connectionRegistry.getConnectionStatus(contactId);
ConversationActivity.this, R.drawable.contact_online)); if (status == CONNECTED) {
toolbarStatus.setContentDescription(getString(R.string.online)); toolbarStatus.setText(R.string.online);
} else if (status == RECENTLY_CONNECTED) {
toolbarStatus.setText(R.string.recently_online);
} else { } else {
toolbarStatus.setImageDrawable(ContextCompat.getDrawable( toolbarStatus.setText(R.string.offline);
ConversationActivity.this, R.drawable.contact_offline));
toolbarStatus.setContentDescription(getString(R.string.offline));
} }
} }
...@@ -729,16 +728,10 @@ public class ConversationActivity extends BriarActivity ...@@ -729,16 +728,10 @@ public class ConversationActivity extends BriarActivity
LOG.info("Messages acked"); LOG.info("Messages acked");
markMessages(m.getMessageIds(), true, true); markMessages(m.getMessageIds(), true, true);
} }
} else if (e instanceof ContactConnectedEvent) { } else if (e instanceof ConnectionStatusChangedEvent) {
ContactConnectedEvent c = (ContactConnectedEvent) e; ConnectionStatusChangedEvent c = (ConnectionStatusChangedEvent) e;
if (c.getContactId().equals(contactId)) {
LOG.info("Contact connected");
displayContactOnlineStatus();
}
} else if (e instanceof ContactDisconnectedEvent) {
ContactDisconnectedEvent c = (ContactDisconnectedEvent) e;
if (c.getContactId().equals(contactId)) { if (c.getContactId().equals(contactId)) {
LOG.info("Contact disconnected"); LOG.info("Connection status changed");
displayContactOnlineStatus(); displayContactOnlineStatus();
} }
} else if (e instanceof ClientVersionUpdatedEvent) { } else if (e instanceof ClientVersionUpdatedEvent) {
......
...@@ -12,6 +12,7 @@ import org.briarproject.bramble.api.db.DbException; ...@@ -12,6 +12,7 @@ import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault; import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault; import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
import org.briarproject.bramble.api.plugin.ConnectionRegistry; import org.briarproject.bramble.api.plugin.ConnectionRegistry;
import org.briarproject.bramble.api.plugin.ConnectionStatus;
import org.briarproject.briar.R; import org.briarproject.briar.R;
import org.briarproject.briar.android.activity.ActivityComponent; import org.briarproject.briar.android.activity.ActivityComponent;
import org.briarproject.briar.android.contact.BaseContactListAdapter.OnContactClickListener; import org.briarproject.briar.android.contact.BaseContactListAdapter.OnContactClickListener;
...@@ -73,7 +74,8 @@ public class ContactChooserFragment extends BaseFragment { ...@@ -73,7 +74,8 @@ public class ContactChooserFragment extends BaseFragment {
} }
@Override @Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, public View onCreateView(LayoutInflater inflater,
@Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) { @Nullable Bundle savedInstanceState) {
View contentView = inflater.inflate(R.layout.list, container, false); View contentView = inflater.inflate(R.layout.list, container, false);
...@@ -127,9 +129,9 @@ public class ContactChooserFragment extends BaseFragment { ...@@ -127,9 +129,9 @@ public class ContactChooserFragment extends BaseFragment {
ContactId id = c.getId(); ContactId id = c.getId();
GroupCount count = GroupCount count =
conversationManager.getGroupCount(id); conversationManager.getGroupCount(id);
boolean connected = ConnectionStatus status = connectionRegistry
connectionRegistry.isConnected(c.getId()); .getConnectionStatus(c.getId());
contacts.add(new ContactListItem(c, connected, count)); contacts.add(new ContactListItem(c, status, count));
} }
} }
displayContacts(contacts); displayContacts(contacts);
......
...@@ -99,7 +99,7 @@ public class GroupMemberListActivity extends BriarActivity ...@@ -99,7 +99,7 @@ public class GroupMemberListActivity extends BriarActivity
supportFinishAfterTransition(); supportFinishAfterTransition();
} }
} }
// TODO ContactConnectedEvent and ContactDisconnectedEvent // TODO ConnectionStatusChangedEvent
} }
@Override @Override
......
...@@ -5,6 +5,7 @@ import org.briarproject.bramble.api.db.DatabaseExecutor; ...@@ -5,6 +5,7 @@ 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.lifecycle.LifecycleManager; import org.briarproject.bramble.api.lifecycle.LifecycleManager;
import org.briarproject.bramble.api.plugin.ConnectionRegistry; import org.briarproject.bramble.api.plugin.ConnectionRegistry;
import org.briarproject.bramble.api.plugin.ConnectionStatus;
import org.briarproject.bramble.api.sync.GroupId; import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.briar.android.controller.DbControllerImpl; import org.briarproject.briar.android.controller.DbControllerImpl;
import org.briarproject.briar.android.controller.handler.ResultExceptionHandler; import org.briarproject.briar.android.controller.handler.ResultExceptionHandler;
...@@ -19,6 +20,7 @@ import java.util.logging.Logger; ...@@ -19,6 +20,7 @@ import java.util.logging.Logger;
import javax.inject.Inject; import javax.inject.Inject;
import static java.util.logging.Level.WARNING; import static java.util.logging.Level.WARNING;
import static org.briarproject.bramble.api.plugin.ConnectionStatus.DISCONNECTED;
import static org.briarproject.bramble.util.LogUtils.logException; import static org.briarproject.bramble.util.LogUtils.logException;
class GroupMemberListControllerImpl extends DbControllerImpl class GroupMemberListControllerImpl extends DbControllerImpl
...@@ -50,10 +52,11 @@ class GroupMemberListControllerImpl extends DbControllerImpl ...@@ -50,10 +52,11 @@ class GroupMemberListControllerImpl extends DbControllerImpl
privateGroupManager.getMembers(groupId); privateGroupManager.getMembers(groupId);
for (GroupMember m : members) { for (GroupMember m : members) {
ContactId c = m.getContactId(); ContactId c = m.getContactId();
boolean online = false; ConnectionStatus status = DISCONNECTED;
if (c != null) if (c != null) {
online = connectionRegistry.isConnected(c); status = connectionRegistry.getConnectionStatus(c);
items.add(new MemberListItem(m, online)); }
items.add(new MemberListItem(m, status));
} }
handler.onResult(items); handler.onResult(items);
} catch (DbException e) { } catch (DbException e) {
......
...@@ -45,7 +45,7 @@ class MemberListAdapter extends ...@@ -45,7 +45,7 @@ class MemberListAdapter extends
@Override @Override
public boolean areContentsTheSame(MemberListItem m1, MemberListItem m2) { public boolean areContentsTheSame(MemberListItem m1, MemberListItem m2) {
if (m1.isOnline() != m2.isOnline()) return false; if (m1.getConnectionStatus() != m2.getConnectionStatus()) return false;
if (m1.getContactId() != m2.getContactId()) return false; if (m1.getContactId() != m2.getContactId()) return false;
if (m1.getStatus() != m2.getStatus()) return false; if (m1.getStatus() != m2.getStatus()) return false;
return true; return true;
......
...@@ -5,6 +5,7 @@ import org.briarproject.bramble.api.identity.Author; ...@@ -5,6 +5,7 @@ import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.identity.AuthorInfo; import org.briarproject.bramble.api.identity.AuthorInfo;
import org.briarproject.bramble.api.identity.AuthorInfo.Status; import org.briarproject.bramble.api.identity.AuthorInfo.Status;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.ConnectionStatus;
import org.briarproject.briar.api.privategroup.GroupMember; import org.briarproject.briar.api.privategroup.GroupMember;
import javax.annotation.Nullable; import javax.annotation.Nullable;
...@@ -15,11 +16,11 @@ import javax.annotation.concurrent.NotThreadSafe; ...@@ -15,11 +16,11 @@ import javax.annotation.concurrent.NotThreadSafe;
class MemberListItem { class MemberListItem {
private final GroupMember groupMember; private final GroupMember groupMember;
private boolean online; private ConnectionStatus status;
MemberListItem(GroupMember groupMember, boolean online) { MemberListItem(GroupMember groupMember, ConnectionStatus status) {
this.groupMember = groupMember; this.groupMember = groupMember;
this.online = online; this.status = status;
} }
Author getMember() { Author getMember() {
...@@ -43,12 +44,12 @@ class MemberListItem { ...@@ -43,12 +44,12 @@ class MemberListItem {
return groupMember.getContactId(); return groupMember.getContactId();
} }
boolean isOnline() { ConnectionStatus getConnectionStatus() {
return online; return status;
} }
void setOnline(boolean online) { void setConnectionStatus(ConnectionStatus status) {
this.online = online; this.status = status;
} }
} }
...@@ -5,6 +5,7 @@ import android.widget.ImageView; ...@@ -5,6 +5,7 @@ import android.widget.ImageView;
import android.widget.TextView; import android.widget.TextView;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.ConnectionStatus;
import org.briarproject.briar.R; import org.briarproject.briar.R;
import org.briarproject.briar.android.view.AuthorView; import org.briarproject.briar.android.view.AuthorView;
...@@ -14,6 +15,8 @@ import androidx.recyclerview.widget.RecyclerView; ...@@ -14,6 +15,8 @@ import androidx.recyclerview.widget.RecyclerView;
import static android.view.View.GONE; import static android.view.View.GONE;
import static android.view.View.VISIBLE; import static android.view.View.VISIBLE;
import static org.briarproject.bramble.api.identity.AuthorInfo.Status.OURSELVES; import static org.briarproject.bramble.api.identity.AuthorInfo.Status.OURSELVES;
import static org.briarproject.bramble.api.plugin.ConnectionStatus.CONNECTED;
import static org.briarproject.bramble.api.plugin.ConnectionStatus.RECENTLY_CONNECTED;
import static org.briarproject.briar.android.util.UiUtils.getContactDisplayName; import static org.briarproject.briar.android.util.UiUtils.getContactDisplayName;
@UiThread @UiThread
...@@ -38,10 +41,13 @@ class MemberListItemHolder extends RecyclerView.ViewHolder { ...@@ -38,10 +41,13 @@ class MemberListItemHolder extends RecyclerView.ViewHolder {
// online status of visible contacts // online status of visible contacts
if (item.getContactId() != null) { if (item.getContactId() != null) {
bulb.setVisibility(VISIBLE); bulb.setVisibility(VISIBLE);
if (item.isOnline()) { ConnectionStatus status = item.getConnectionStatus();
bulb.setImageResource(R.drawable.contact_connected); if (status == CONNECTED) {
bulb.setImageResource(R.drawable.ic_connected);
} else if (status == RECENTLY_CONNECTED) {
bulb.setImageResource(R.drawable.ic_recently_connected);
} else { } else {
bulb.setImageResource(R.drawable.contact_disconnected); bulb.setImageResource(R.drawable.ic_disconnected);
} }
} else { } else {
bulb.setVisibility(GONE); bulb.setVisibility(GONE);
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment