diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/ConnectionRegistry.java b/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/ConnectionRegistry.java index 7f0adec36..1bfdf6d8d 100644 --- a/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/ConnectionRegistry.java +++ b/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/ConnectionRegistry.java @@ -5,8 +5,7 @@ import org.briarproject.bramble.api.contact.PendingContactId; import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.plugin.event.ConnectionClosedEvent; import org.briarproject.bramble.api.plugin.event.ConnectionOpenedEvent; -import org.briarproject.bramble.api.plugin.event.ContactConnectedEvent; -import org.briarproject.bramble.api.plugin.event.ContactDisconnectedEvent; +import org.briarproject.bramble.api.plugin.event.ConnectionStatusChangedEvent; import org.briarproject.bramble.api.rendezvous.event.RendezvousConnectionClosedEvent; import org.briarproject.bramble.api.rendezvous.event.RendezvousConnectionOpenedEvent; @@ -21,15 +20,15 @@ public interface ConnectionRegistry { /** * Registers a connection with the given contact over the given transport. * Broadcasts {@link ConnectionOpenedEvent}. Also broadcasts - * {@link ContactConnectedEvent} if this is the only connection with the - * contact. + * {@link ConnectionStatusChangedEvent} if this is the only connection with + * the contact. */ void registerConnection(ContactId c, TransportId t, boolean incoming); /** * Unregisters a connection with the given contact over the given transport. * 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. */ void unregisterConnection(ContactId c, TransportId t, boolean incoming); @@ -45,9 +44,9 @@ public interface ConnectionRegistry { 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 diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/ConnectionStatus.java b/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/ConnectionStatus.java new file mode 100644 index 000000000..42b09b5db --- /dev/null +++ b/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/ConnectionStatus.java @@ -0,0 +1,5 @@ +package org.briarproject.bramble.api.plugin; + +public enum ConnectionStatus { + CONNECTED, RECENTLY_CONNECTED, DISCONNECTED +} diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/event/ContactConnectedEvent.java b/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/event/ConnectionStatusChangedEvent.java similarity index 51% rename from bramble-api/src/main/java/org/briarproject/bramble/api/plugin/event/ContactConnectedEvent.java rename to bramble-api/src/main/java/org/briarproject/bramble/api/plugin/event/ConnectionStatusChangedEvent.java index 46f6b46bc..7c71ce904 100644 --- a/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/event/ContactConnectedEvent.java +++ b/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/event/ConnectionStatusChangedEvent.java @@ -3,24 +3,31 @@ 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 org.briarproject.bramble.api.plugin.ConnectionStatus; import javax.annotation.concurrent.Immutable; /** - * An event that is broadcast when a contact connects that was not previously - * connected via any transport. + * An event that is broadcast when a contact's connection status changes. */ @Immutable @NotNullByDefault -public class ContactConnectedEvent extends Event { +public class ConnectionStatusChangedEvent extends Event { private final ContactId contactId; + private final ConnectionStatus status; - public ContactConnectedEvent(ContactId contactId) { + public ConnectionStatusChangedEvent(ContactId contactId, + ConnectionStatus status) { this.contactId = contactId; + this.status = status; } public ContactId getContactId() { return contactId; } + + public ConnectionStatus getConnectionStatus() { + return status; + } } diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/event/ContactDisconnectedEvent.java b/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/event/ContactDisconnectedEvent.java deleted file mode 100644 index 77b176484..000000000 --- a/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/event/ContactDisconnectedEvent.java +++ /dev/null @@ -1,26 +0,0 @@ -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; - } -} diff --git a/bramble-core/src/main/java/org/briarproject/bramble/plugin/ConnectionRegistryImpl.java b/bramble-core/src/main/java/org/briarproject/bramble/plugin/ConnectionRegistryImpl.java index 06056f960..11857d1d4 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/plugin/ConnectionRegistryImpl.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/plugin/ConnectionRegistryImpl.java @@ -6,30 +6,41 @@ import org.briarproject.bramble.api.contact.PendingContactId; import org.briarproject.bramble.api.event.EventBus; import org.briarproject.bramble.api.nullsafety.NotNullByDefault; 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.event.ConnectionClosedEvent; import org.briarproject.bramble.api.plugin.event.ConnectionOpenedEvent; -import org.briarproject.bramble.api.plugin.event.ContactConnectedEvent; -import org.briarproject.bramble.api.plugin.event.ContactDisconnectedEvent; +import org.briarproject.bramble.api.plugin.event.ConnectionStatusChangedEvent; import org.briarproject.bramble.api.rendezvous.event.RendezvousConnectionClosedEvent; 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.Collection; -import java.util.Collections; import java.util.HashMap; import java.util.HashSet; +import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Map.Entry; import java.util.Set; +import java.util.concurrent.ScheduledExecutorService; import java.util.logging.Logger; import javax.annotation.concurrent.GuardedBy; import javax.annotation.concurrent.ThreadSafe; 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.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 @NotNullByDefault @@ -38,22 +49,30 @@ class ConnectionRegistryImpl implements ConnectionRegistry { private static final Logger LOG = 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 Clock clock; private final Object lock = new Object(); @GuardedBy("lock") private final Map> contactConnections; @GuardedBy("lock") - private final Multiset contactCounts; + private final Map contactCounts; @GuardedBy("lock") private final Set connectedPendingContacts; @Inject - ConnectionRegistryImpl(EventBus eventBus) { + ConnectionRegistryImpl(EventBus eventBus, Clock clock, + @Scheduler ScheduledExecutorService scheduler) { this.eventBus = eventBus; + this.clock = clock; contactConnections = new HashMap<>(); - contactCounts = new Multiset<>(); + contactCounts = new HashMap<>(); connectedPendingContacts = new HashSet<>(); + scheduler.scheduleWithFixedDelay(this::expireRecentConnections, + EXPIRY_INTERVAL_MS, EXPIRY_INTERVAL_MS, MILLISECONDS); } @Override @@ -71,12 +90,22 @@ class ConnectionRegistryImpl implements ConnectionRegistry { contactConnections.put(t, m); } 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)); if (firstConnection) { LOG.info("Contact connected"); - eventBus.broadcast(new ContactConnectedEvent(c)); + eventBus.broadcast(new ConnectionStatusChangedEvent(c, CONNECTED)); } } @@ -93,12 +122,22 @@ class ConnectionRegistryImpl implements ConnectionRegistry { if (m == null || !m.contains(c)) throw new IllegalArgumentException(); 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)); if (lastConnection) { LOG.info("Contact disconnected"); - eventBus.broadcast(new ContactDisconnectedEvent(c)); + eventBus.broadcast( + new ConnectionStatusChangedEvent(c, RECENTLY_CONNECTED)); } } @@ -106,7 +145,7 @@ class ConnectionRegistryImpl implements ConnectionRegistry { public Collection getConnectedContacts(TransportId t) { synchronized (lock) { Multiset m = contactConnections.get(t); - if (m == null) return Collections.emptyList(); + if (m == null) return emptyList(); List ids = new ArrayList<>(m.keySet()); if (LOG.isLoggable(INFO)) LOG.info(ids.size() + " contacts connected: " + t); @@ -123,9 +162,11 @@ class ConnectionRegistryImpl implements ConnectionRegistry { } @Override - public boolean isConnected(ContactId c) { + public ConnectionStatus getConnectionStatus(ContactId c) { 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 { } eventBus.broadcast(new RendezvousConnectionClosedEvent(p, success)); } + + @Scheduler + private void expireRecentConnections() { + long now = clock.currentTimeMillis(); + List disconnected = new ArrayList<>(); + synchronized (lock) { + Iterator> it = + contactCounts.entrySet().iterator(); + while (it.hasNext()) { + Entry 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; + } + } } diff --git a/bramble-core/src/test/java/org/briarproject/bramble/plugin/ConnectionRegistryImplTest.java b/bramble-core/src/test/java/org/briarproject/bramble/plugin/ConnectionRegistryImplTest.java index 13c0466e8..6524364dc 100644 --- a/bramble-core/src/test/java/org/briarproject/bramble/plugin/ConnectionRegistryImplTest.java +++ b/bramble-core/src/test/java/org/briarproject/bramble/plugin/ConnectionRegistryImplTest.java @@ -7,18 +7,20 @@ import org.briarproject.bramble.api.plugin.ConnectionRegistry; import org.briarproject.bramble.api.plugin.TransportId; import org.briarproject.bramble.api.plugin.event.ConnectionClosedEvent; import org.briarproject.bramble.api.plugin.event.ConnectionOpenedEvent; -import org.briarproject.bramble.api.plugin.event.ContactConnectedEvent; -import org.briarproject.bramble.api.plugin.event.ContactDisconnectedEvent; +import org.briarproject.bramble.api.plugin.event.ConnectionStatusChangedEvent; import org.briarproject.bramble.api.rendezvous.event.RendezvousConnectionClosedEvent; import org.briarproject.bramble.api.rendezvous.event.RendezvousConnectionOpenedEvent; +import org.briarproject.bramble.api.system.Clock; import org.briarproject.bramble.test.BrambleMockTestCase; import org.jmock.Expectations; import org.junit.Test; import java.util.Collection; +import java.util.concurrent.ScheduledExecutorService; import static java.util.Collections.emptyList; 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.getRandomId; import static org.briarproject.bramble.test.TestUtils.getTransportId; @@ -30,6 +32,9 @@ import static org.junit.Assert.fail; public class ConnectionRegistryImplTest extends BrambleMockTestCase { 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 contactId1 = getContactId(); @@ -40,17 +45,25 @@ public class ConnectionRegistryImplTest extends BrambleMockTestCase { @Test 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 assertEquals(emptyList(), c.getConnectedContacts(transportId)); assertEquals(emptyList(), c.getConnectedContacts(transportId1)); // 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() {{ 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); assertEquals(singletonList(contactId), @@ -81,11 +94,13 @@ public class ConnectionRegistryImplTest extends BrambleMockTestCase { context.assertIsSatisfied(); // Unregister the other connection - this should broadcast a - // ConnectionClosedEvent and a ContactDisconnectedEvent + // ConnectionClosedEvent and a ConnectionStatusChangedEvent context.checking(new Expectations() {{ + oneOf(clock).currentTimeMillis(); + will(returnValue(System.currentTimeMillis())); oneOf(eventBus).broadcast(with(any(ConnectionClosedEvent.class))); oneOf(eventBus).broadcast(with(any( - ContactDisconnectedEvent.class))); + ConnectionStatusChangedEvent.class))); }}); c.unregisterConnection(contactId, transportId, true); assertEquals(emptyList(), c.getConnectedContacts(transportId)); @@ -102,12 +117,12 @@ public class ConnectionRegistryImplTest extends BrambleMockTestCase { // Register both contacts with one transport, one contact with both - // this should broadcast three ConnectionOpenedEvents and two - // ContactConnectedEvents + // ConnectionStatusChangedEvents context.checking(new Expectations() {{ exactly(3).of(eventBus).broadcast(with(any( ConnectionOpenedEvent.class))); exactly(2).of(eventBus).broadcast(with(any( - ContactConnectedEvent.class))); + ConnectionStatusChangedEvent.class))); }}); c.registerConnection(contactId, transportId, true); c.registerConnection(contactId1, transportId, true); @@ -122,7 +137,14 @@ public class ConnectionRegistryImplTest extends BrambleMockTestCase { @Test 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() {{ oneOf(eventBus).broadcast(with(any( diff --git a/briar-android/src/main/java/org/briarproject/briar/android/contact/ContactItem.java b/briar-android/src/main/java/org/briarproject/briar/android/contact/ContactItem.java index e2e5f0351..3e9552f73 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/contact/ContactItem.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/contact/ContactItem.java @@ -2,35 +2,38 @@ package org.briarproject.briar.android.contact; import org.briarproject.bramble.api.contact.Contact; import org.briarproject.bramble.api.nullsafety.NotNullByDefault; +import org.briarproject.bramble.api.plugin.ConnectionStatus; import javax.annotation.concurrent.NotThreadSafe; +import static org.briarproject.bramble.api.plugin.ConnectionStatus.DISCONNECTED; + @NotThreadSafe @NotNullByDefault public class ContactItem { private final Contact contact; - private boolean connected; + private ConnectionStatus status; 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.connected = connected; + this.status = status; } public Contact getContact() { return contact; } - boolean isConnected() { - return connected; + ConnectionStatus getConnectionStatus() { + return status; } - void setConnected(boolean connected) { - this.connected = connected; + void setConnectionStatus(ConnectionStatus status) { + this.status = status; } } diff --git a/briar-android/src/main/java/org/briarproject/briar/android/contact/ContactItemViewHolder.java b/briar-android/src/main/java/org/briarproject/briar/android/contact/ContactItemViewHolder.java index 154287f47..37afa289f 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/contact/ContactItemViewHolder.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/contact/ContactItemViewHolder.java @@ -7,6 +7,7 @@ import android.widget.TextView; import org.briarproject.bramble.api.identity.Author; import org.briarproject.bramble.api.nullsafety.NotNullByDefault; +import org.briarproject.bramble.api.plugin.ConnectionStatus; import org.briarproject.briar.R; import org.briarproject.briar.android.contact.BaseContactListAdapter.OnContactClickListener; @@ -16,6 +17,8 @@ import androidx.annotation.UiThread; import androidx.recyclerview.widget.RecyclerView; 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; @UiThread @@ -27,7 +30,7 @@ public class ContactItemViewHolder protected final ImageView avatar; protected final TextView name; @Nullable - protected final ImageView bulb; + private final ImageView bulb; public ContactItemViewHolder(View v) { super(v); @@ -47,10 +50,13 @@ public class ContactItemViewHolder if (bulb != null) { // online/offline - if (item.isConnected()) { - bulb.setImageResource(R.drawable.contact_connected); + ConnectionStatus status = item.getConnectionStatus(); + if (status == CONNECTED) { + bulb.setImageResource(R.drawable.ic_connected); + } else if (status == RECENTLY_CONNECTED) { + bulb.setImageResource(R.drawable.ic_recently_connected); } else { - bulb.setImageResource(R.drawable.contact_disconnected); + bulb.setImageResource(R.drawable.ic_disconnected); } } diff --git a/briar-android/src/main/java/org/briarproject/briar/android/contact/ContactListAdapter.java b/briar-android/src/main/java/org/briarproject/briar/android/contact/ContactListAdapter.java index 0c8cf5e12..6cc18160b 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/contact/ContactListAdapter.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/contact/ContactListAdapter.java @@ -39,7 +39,7 @@ public class ContactListAdapter extends if (c1.getTimestamp() != c2.getTimestamp()) { return false; } - return c1.isConnected() == c2.isConnected(); + return c1.getConnectionStatus() == c2.getConnectionStatus(); } @Override diff --git a/briar-android/src/main/java/org/briarproject/briar/android/contact/ContactListFragment.java b/briar-android/src/main/java/org/briarproject/briar/android/contact/ContactListFragment.java index a2adf44de..e19cedff7 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/contact/ContactListFragment.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/contact/ContactListFragment.java @@ -25,8 +25,8 @@ import org.briarproject.bramble.api.event.EventListener; 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.plugin.event.ContactConnectedEvent; -import org.briarproject.bramble.api.plugin.event.ContactDisconnectedEvent; +import org.briarproject.bramble.api.plugin.ConnectionStatus; +import org.briarproject.bramble.api.plugin.event.ConnectionStatusChangedEvent; import org.briarproject.briar.R; import org.briarproject.briar.android.activity.ActivityComponent; import org.briarproject.briar.android.contact.BaseContactListAdapter.OnContactClickListener; @@ -53,7 +53,6 @@ import javax.inject.Inject; import androidx.annotation.UiThread; import androidx.core.app.ActivityCompat; import androidx.core.app.ActivityOptionsCompat; -import androidx.core.util.Pair; import androidx.recyclerview.widget.LinearLayoutManager; import io.github.kobakei.materialfabspeeddial.FabSpeedDial; import io.github.kobakei.materialfabspeeddial.FabSpeedDial.OnMenuItemClickListener; @@ -137,26 +136,15 @@ public class ContactListFragment extends BaseFragment implements EventListener, ContactId contactId = item.getContact().getId(); i.putExtra(CONTACT_ID, contactId.getInt()); + Bundle options = null; + // work-around for android bug #224270 if (SDK_INT >= 23 && !isSamsung7()) { - ContactListItemViewHolder holder = - (ContactListItemViewHolder) list - .getRecyclerView() - .findViewHolderForAdapterPosition( - adapter.findItemPosition(item)); - Pair avatar = - Pair.create(holder.avatar, - getTransitionName(holder.avatar)); - Pair 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 + options = makeTransitionOptions(view); + } + if (options == null) { startActivity(i); + } else { + ActivityCompat.startActivity(getActivity(), i, options); } }; adapter = new ContactListAdapter(requireContext(), @@ -171,6 +159,15 @@ public class ContactListFragment extends BaseFragment implements EventListener, 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 public void onMenuItemClick(FloatingActionButton fab, @Nullable TextView v, int itemId) { @@ -232,9 +229,9 @@ public class ContactListFragment extends BaseFragment implements EventListener, ContactId id = c.getId(); GroupCount count = conversationManager.getGroupCount(id); - boolean connected = - connectionRegistry.isConnected(c.getId()); - contacts.add(new ContactListItem(c, connected, count)); + ConnectionStatus status = connectionRegistry + .getConnectionStatus(c.getId()); + contacts.add(new ContactListItem(c, status, count)); } catch (NoSuchContactException e) { // Continue } @@ -265,10 +262,9 @@ public class ContactListFragment extends BaseFragment implements EventListener, if (e instanceof ContactAddedEvent) { LOG.info("Contact added, reloading"); loadContacts(); - } else if (e instanceof ContactConnectedEvent) { - setConnected(((ContactConnectedEvent) e).getContactId(), true); - } else if (e instanceof ContactDisconnectedEvent) { - setConnected(((ContactDisconnectedEvent) e).getContactId(), false); + } else if (e instanceof ConnectionStatusChangedEvent) { + ConnectionStatusChangedEvent c = (ConnectionStatusChangedEvent) e; + setConnectionStatus(c.getContactId(), c.getConnectionStatus()); } else if (e instanceof ContactRemovedEvent) { LOG.info("Contact removed, removing item"); removeItem(((ContactRemovedEvent) e).getContactId()); @@ -304,12 +300,12 @@ public class ContactListFragment extends BaseFragment implements EventListener, } @UiThread - private void setConnected(ContactId c, boolean connected) { + private void setConnectionStatus(ContactId c, ConnectionStatus status) { adapter.incrementRevision(); int position = adapter.findItemPosition(c); ContactListItem item = adapter.getItemAt(position); if (item != null) { - item.setConnected(connected); + item.setConnectionStatus(status); adapter.updateItemAt(position, item); } } diff --git a/briar-android/src/main/java/org/briarproject/briar/android/contact/ContactListItem.java b/briar-android/src/main/java/org/briarproject/briar/android/contact/ContactListItem.java index 7b0fc0328..f657ae4a7 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/contact/ContactListItem.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/contact/ContactListItem.java @@ -2,6 +2,7 @@ package org.briarproject.briar.android.contact; import org.briarproject.bramble.api.contact.Contact; 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.conversation.ConversationMessageHeader; @@ -15,9 +16,9 @@ public class ContactListItem extends ContactItem { private long timestamp; private int unread; - public ContactListItem(Contact contact, boolean connected, + public ContactListItem(Contact contact, ConnectionStatus status, GroupCount count) { - super(contact, connected); + super(contact, status); this.empty = count.getMsgCount() == 0; this.unread = count.getUnreadCount(); this.timestamp = count.getLatestMsgTime(); diff --git a/briar-android/src/main/java/org/briarproject/briar/android/contact/ContactListItemViewHolder.java b/briar-android/src/main/java/org/briarproject/briar/android/contact/ContactListItemViewHolder.java index 9b109d66f..43aadca54 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/contact/ContactListItemViewHolder.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/contact/ContactListItemViewHolder.java @@ -7,7 +7,6 @@ import org.briarproject.bramble.api.contact.ContactId; import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.briar.R; import org.briarproject.briar.android.contact.BaseContactListAdapter.OnContactClickListener; -import org.briarproject.briar.android.util.UiUtils; import java.util.Locale; @@ -15,8 +14,11 @@ import javax.annotation.Nullable; 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 org.briarproject.briar.android.util.UiUtils.formatDate; +import static org.briarproject.briar.android.util.UiUtils.getAvatarTransitionName; @UiThread @NotNullByDefault @@ -39,10 +41,11 @@ class ContactListItemViewHolder extends ContactItemViewHolder { // unread count int unreadCount = item.getUnreadCount(); if (unreadCount > 0) { - unread.setText(String.format(Locale.getDefault(), "%d", unreadCount)); - unread.setVisibility(View.VISIBLE); + unread.setText(String.format(Locale.getDefault(), "%d", + unreadCount)); + unread.setVisibility(VISIBLE); } else { - unread.setVisibility(View.INVISIBLE); + unread.setVisibility(INVISIBLE); } // date of last message @@ -54,8 +57,7 @@ class ContactListItemViewHolder extends ContactItemViewHolder { } ContactId c = item.getContact().getId(); - setTransitionName(avatar, UiUtils.getAvatarTransitionName(c)); - setTransitionName(bulb, UiUtils.getBulbTransitionName(c)); + setTransitionName(avatar, getAvatarTransitionName(c)); } } diff --git a/briar-android/src/main/java/org/briarproject/briar/android/controller/SharingControllerImpl.java b/briar-android/src/main/java/org/briarproject/briar/android/controller/SharingControllerImpl.java index 2d22db901..c246c297e 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/controller/SharingControllerImpl.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/controller/SharingControllerImpl.java @@ -6,8 +6,7 @@ import org.briarproject.bramble.api.event.EventBus; import org.briarproject.bramble.api.event.EventListener; import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.plugin.ConnectionRegistry; -import org.briarproject.bramble.api.plugin.event.ContactConnectedEvent; -import org.briarproject.bramble.api.plugin.event.ContactDisconnectedEvent; +import org.briarproject.bramble.api.plugin.event.ConnectionStatusChangedEvent; import java.util.Collection; import java.util.HashSet; @@ -18,6 +17,8 @@ import javax.inject.Inject; import androidx.annotation.UiThread; +import static org.briarproject.bramble.api.plugin.ConnectionStatus.CONNECTED; + @NotNullByDefault public class SharingControllerImpl implements SharingController, EventListener { @@ -60,15 +61,14 @@ public class SharingControllerImpl implements SharingController, EventListener { @Override public void eventOccurred(Event e) { - if (e instanceof ContactConnectedEvent) { - setConnected(((ContactConnectedEvent) e).getContactId()); - } else if (e instanceof ContactDisconnectedEvent) { - setConnected(((ContactDisconnectedEvent) e).getContactId()); + if (e instanceof ConnectionStatusChangedEvent) { + ConnectionStatusChangedEvent c = (ConnectionStatusChangedEvent) e; + setConnectionStatus(c.getContactId()); } } @UiThread - private void setConnected(ContactId c) { + private void setConnectionStatus(ContactId c) { if (listener == null) throw new IllegalStateException(); if (contacts.contains(c)) { int online = getOnlineCount(); @@ -95,7 +95,9 @@ public class SharingControllerImpl implements SharingController, EventListener { public int getOnlineCount() { int online = 0; for (ContactId c : contacts) { - if (connectionRegistry.isConnected(c)) online++; + if (connectionRegistry.getConnectionStatus(c) == CONNECTED) { + online++; + } } return online; } diff --git a/briar-android/src/main/java/org/briarproject/briar/android/conversation/ConversationActivity.java b/briar-android/src/main/java/org/briarproject/briar/android/conversation/ConversationActivity.java index 6aa6986c7..85bde78ac 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/conversation/ConversationActivity.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/conversation/ConversationActivity.java @@ -13,7 +13,6 @@ import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; -import android.widget.ImageView; import android.widget.TextView; import android.widget.Toast; @@ -34,8 +33,8 @@ import org.briarproject.bramble.api.event.EventListener; 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.plugin.event.ContactConnectedEvent; -import org.briarproject.bramble.api.plugin.event.ContactDisconnectedEvent; +import org.briarproject.bramble.api.plugin.ConnectionStatus; +import org.briarproject.bramble.api.plugin.event.ConnectionStatusChangedEvent; import org.briarproject.bramble.api.sync.ClientId; import org.briarproject.bramble.api.sync.MessageId; import org.briarproject.bramble.api.sync.event.MessagesAckedEvent; @@ -125,6 +124,8 @@ import static java.util.Objects.requireNonNull; import static java.util.logging.Level.INFO; import static java.util.logging.Level.WARNING; 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.logException; import static org.briarproject.bramble.util.LogUtils.now; @@ -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.NAME; 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.api.messaging.MessagingConstants.MAX_ATTACHMENTS_PER_MESSAGE; import static org.briarproject.briar.api.messaging.MessagingConstants.MAX_PRIVATE_MESSAGE_TEXT_LENGTH; @@ -198,8 +198,8 @@ public class ConversationActivity extends BriarActivity private ConversationAdapter adapter; private Toolbar toolbar; private CircleImageView toolbarAvatar; - private ImageView toolbarStatus; private TextView toolbarTitle; + private TextView toolbarStatus; private BriarRecyclerView list; private LinearLayoutManager layoutManager; private TextInputView textInputView; @@ -237,8 +237,8 @@ public class ConversationActivity extends BriarActivity // Custom Toolbar toolbar = requireNonNull(setUpCustomToolbar(true)); toolbarAvatar = toolbar.findViewById(R.id.contactAvatar); - toolbarStatus = toolbar.findViewById(R.id.contactStatus); toolbarTitle = toolbar.findViewById(R.id.contactName); + toolbarStatus = toolbar.findViewById(R.id.contactStatus); observeOnce(viewModel.getContactAuthorId(), this, authorId -> { requireNonNull(authorId); @@ -257,7 +257,6 @@ public class ConversationActivity extends BriarActivity this::onAddedPrivateMessage); setTransitionName(toolbarAvatar, getAvatarTransitionName(contactId)); - setTransitionName(toolbarStatus, getBulbTransitionName(contactId)); visitor = new ConversationVisitor(this, this, this, viewModel.getContactDisplayName()); @@ -499,14 +498,14 @@ public class ConversationActivity extends BriarActivity @UiThread private void displayContactOnlineStatus() { - if (connectionRegistry.isConnected(contactId)) { - toolbarStatus.setImageDrawable(ContextCompat.getDrawable( - ConversationActivity.this, R.drawable.contact_online)); - toolbarStatus.setContentDescription(getString(R.string.online)); + ConnectionStatus status = + connectionRegistry.getConnectionStatus(contactId); + if (status == CONNECTED) { + toolbarStatus.setText(R.string.online); + } else if (status == RECENTLY_CONNECTED) { + toolbarStatus.setText(R.string.recently_online); } else { - toolbarStatus.setImageDrawable(ContextCompat.getDrawable( - ConversationActivity.this, R.drawable.contact_offline)); - toolbarStatus.setContentDescription(getString(R.string.offline)); + toolbarStatus.setText(R.string.offline); } } @@ -729,16 +728,10 @@ public class ConversationActivity extends BriarActivity LOG.info("Messages acked"); markMessages(m.getMessageIds(), true, true); } - } else if (e instanceof ContactConnectedEvent) { - ContactConnectedEvent c = (ContactConnectedEvent) e; + } else if (e instanceof ConnectionStatusChangedEvent) { + 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)) { - LOG.info("Contact disconnected"); + LOG.info("Connection status changed"); displayContactOnlineStatus(); } } else if (e instanceof ClientVersionUpdatedEvent) { diff --git a/briar-android/src/main/java/org/briarproject/briar/android/introduction/ContactChooserFragment.java b/briar-android/src/main/java/org/briarproject/briar/android/introduction/ContactChooserFragment.java index 19c982029..9012e1a16 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/introduction/ContactChooserFragment.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/introduction/ContactChooserFragment.java @@ -12,6 +12,7 @@ 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.plugin.ConnectionStatus; import org.briarproject.briar.R; import org.briarproject.briar.android.activity.ActivityComponent; import org.briarproject.briar.android.contact.BaseContactListAdapter.OnContactClickListener; @@ -73,7 +74,8 @@ public class ContactChooserFragment extends BaseFragment { } @Override - public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, + public View onCreateView(LayoutInflater inflater, + @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { View contentView = inflater.inflate(R.layout.list, container, false); @@ -127,9 +129,9 @@ public class ContactChooserFragment extends BaseFragment { ContactId id = c.getId(); GroupCount count = conversationManager.getGroupCount(id); - boolean connected = - connectionRegistry.isConnected(c.getId()); - contacts.add(new ContactListItem(c, connected, count)); + ConnectionStatus status = connectionRegistry + .getConnectionStatus(c.getId()); + contacts.add(new ContactListItem(c, status, count)); } } displayContacts(contacts); diff --git a/briar-android/src/main/java/org/briarproject/briar/android/privategroup/memberlist/GroupMemberListActivity.java b/briar-android/src/main/java/org/briarproject/briar/android/privategroup/memberlist/GroupMemberListActivity.java index 6a6fe5a7a..12dfe6ab1 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/privategroup/memberlist/GroupMemberListActivity.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/privategroup/memberlist/GroupMemberListActivity.java @@ -99,7 +99,7 @@ public class GroupMemberListActivity extends BriarActivity supportFinishAfterTransition(); } } - // TODO ContactConnectedEvent and ContactDisconnectedEvent + // TODO ConnectionStatusChangedEvent } @Override diff --git a/briar-android/src/main/java/org/briarproject/briar/android/privategroup/memberlist/GroupMemberListControllerImpl.java b/briar-android/src/main/java/org/briarproject/briar/android/privategroup/memberlist/GroupMemberListControllerImpl.java index 92f6343ef..bb18676a5 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/privategroup/memberlist/GroupMemberListControllerImpl.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/privategroup/memberlist/GroupMemberListControllerImpl.java @@ -5,6 +5,7 @@ import org.briarproject.bramble.api.db.DatabaseExecutor; import org.briarproject.bramble.api.db.DbException; import org.briarproject.bramble.api.lifecycle.LifecycleManager; import org.briarproject.bramble.api.plugin.ConnectionRegistry; +import org.briarproject.bramble.api.plugin.ConnectionStatus; import org.briarproject.bramble.api.sync.GroupId; import org.briarproject.briar.android.controller.DbControllerImpl; import org.briarproject.briar.android.controller.handler.ResultExceptionHandler; @@ -19,6 +20,7 @@ import java.util.logging.Logger; import javax.inject.Inject; import static java.util.logging.Level.WARNING; +import static org.briarproject.bramble.api.plugin.ConnectionStatus.DISCONNECTED; import static org.briarproject.bramble.util.LogUtils.logException; class GroupMemberListControllerImpl extends DbControllerImpl @@ -50,10 +52,11 @@ class GroupMemberListControllerImpl extends DbControllerImpl privateGroupManager.getMembers(groupId); for (GroupMember m : members) { ContactId c = m.getContactId(); - boolean online = false; - if (c != null) - online = connectionRegistry.isConnected(c); - items.add(new MemberListItem(m, online)); + ConnectionStatus status = DISCONNECTED; + if (c != null) { + status = connectionRegistry.getConnectionStatus(c); + } + items.add(new MemberListItem(m, status)); } handler.onResult(items); } catch (DbException e) { diff --git a/briar-android/src/main/java/org/briarproject/briar/android/privategroup/memberlist/MemberListAdapter.java b/briar-android/src/main/java/org/briarproject/briar/android/privategroup/memberlist/MemberListAdapter.java index cec23eea1..c2031e7c2 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/privategroup/memberlist/MemberListAdapter.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/privategroup/memberlist/MemberListAdapter.java @@ -45,7 +45,7 @@ class MemberListAdapter extends @Override 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.getStatus() != m2.getStatus()) return false; return true; diff --git a/briar-android/src/main/java/org/briarproject/briar/android/privategroup/memberlist/MemberListItem.java b/briar-android/src/main/java/org/briarproject/briar/android/privategroup/memberlist/MemberListItem.java index a83c2c9a3..f0cde615e 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/privategroup/memberlist/MemberListItem.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/privategroup/memberlist/MemberListItem.java @@ -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.Status; import org.briarproject.bramble.api.nullsafety.NotNullByDefault; +import org.briarproject.bramble.api.plugin.ConnectionStatus; import org.briarproject.briar.api.privategroup.GroupMember; import javax.annotation.Nullable; @@ -15,11 +16,11 @@ import javax.annotation.concurrent.NotThreadSafe; class MemberListItem { private final GroupMember groupMember; - private boolean online; + private ConnectionStatus status; - MemberListItem(GroupMember groupMember, boolean online) { + MemberListItem(GroupMember groupMember, ConnectionStatus status) { this.groupMember = groupMember; - this.online = online; + this.status = status; } Author getMember() { @@ -43,12 +44,12 @@ class MemberListItem { return groupMember.getContactId(); } - boolean isOnline() { - return online; + ConnectionStatus getConnectionStatus() { + return status; } - void setOnline(boolean online) { - this.online = online; + void setConnectionStatus(ConnectionStatus status) { + this.status = status; } } diff --git a/briar-android/src/main/java/org/briarproject/briar/android/privategroup/memberlist/MemberListItemHolder.java b/briar-android/src/main/java/org/briarproject/briar/android/privategroup/memberlist/MemberListItemHolder.java index c0713a0cd..22b60a6b6 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/privategroup/memberlist/MemberListItemHolder.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/privategroup/memberlist/MemberListItemHolder.java @@ -5,6 +5,7 @@ import android.widget.ImageView; import android.widget.TextView; import org.briarproject.bramble.api.nullsafety.NotNullByDefault; +import org.briarproject.bramble.api.plugin.ConnectionStatus; import org.briarproject.briar.R; import org.briarproject.briar.android.view.AuthorView; @@ -14,6 +15,8 @@ import androidx.recyclerview.widget.RecyclerView; import static android.view.View.GONE; import static android.view.View.VISIBLE; 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; @UiThread @@ -38,10 +41,13 @@ class MemberListItemHolder extends RecyclerView.ViewHolder { // online status of visible contacts if (item.getContactId() != null) { bulb.setVisibility(VISIBLE); - if (item.isOnline()) { - bulb.setImageResource(R.drawable.contact_connected); + ConnectionStatus status = item.getConnectionStatus(); + if (status == CONNECTED) { + bulb.setImageResource(R.drawable.ic_connected); + } else if (status == RECENTLY_CONNECTED) { + bulb.setImageResource(R.drawable.ic_recently_connected); } else { - bulb.setImageResource(R.drawable.contact_disconnected); + bulb.setImageResource(R.drawable.ic_disconnected); } } else { bulb.setVisibility(GONE); diff --git a/briar-android/src/main/java/org/briarproject/briar/android/sharing/SharingStatusActivity.java b/briar-android/src/main/java/org/briarproject/briar/android/sharing/SharingStatusActivity.java index a6dcd9516..4b06f75dd 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/sharing/SharingStatusActivity.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/sharing/SharingStatusActivity.java @@ -14,6 +14,7 @@ import org.briarproject.bramble.api.event.EventListener; 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.plugin.ConnectionStatus; import org.briarproject.bramble.api.sync.GroupId; import org.briarproject.bramble.api.sync.event.GroupRemovedEvent; import org.briarproject.briar.R; @@ -104,7 +105,7 @@ abstract class SharingStatusActivity extends BriarActivity supportFinishAfterTransition(); } } - // TODO ContactConnectedEvent and ContactDisconnectedEvent + // TODO ConnectionStatusChangedEvent } @Override @@ -134,8 +135,9 @@ abstract class SharingStatusActivity extends BriarActivity try { List contactItems = new ArrayList<>(); for (Contact c : getSharedWith()) { - boolean online = connectionRegistry.isConnected(c.getId()); - ContactItem item = new ContactItem(c, online); + ConnectionStatus status = + connectionRegistry.getConnectionStatus(c.getId()); + ContactItem item = new ContactItem(c, status); contactItems.add(item); } displaySharedWith(contactItems); diff --git a/briar-android/src/main/java/org/briarproject/briar/android/util/UiUtils.java b/briar-android/src/main/java/org/briarproject/briar/android/util/UiUtils.java index f4bdb966a..1bea467ba 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/util/UiUtils.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/util/UiUtils.java @@ -238,10 +238,6 @@ public class UiUtils { return "avatar" + c.getInt(); } - public static String getBulbTransitionName(ContactId c) { - return "bulb" + c.getInt(); - } - public static OnClickListener getGoToSettingsListener(Context context) { return (dialog, which) -> { Intent i = new Intent(); diff --git a/briar-android/src/main/res/drawable-night/contact_connected.xml b/briar-android/src/main/res/drawable-night/contact_connected.xml deleted file mode 100644 index e4e45826a..000000000 --- a/briar-android/src/main/res/drawable-night/contact_connected.xml +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/briar-android/src/main/res/drawable-night/contact_disconnected.xml b/briar-android/src/main/res/drawable-night/contact_disconnected.xml deleted file mode 100644 index 1dd34144f..000000000 --- a/briar-android/src/main/res/drawable-night/contact_disconnected.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/briar-android/src/main/res/drawable-night/ic_connected.xml b/briar-android/src/main/res/drawable-night/ic_connected.xml new file mode 100644 index 000000000..57cdead99 --- /dev/null +++ b/briar-android/src/main/res/drawable-night/ic_connected.xml @@ -0,0 +1,13 @@ + + + diff --git a/briar-android/src/main/res/drawable-night/ic_disconnected.xml b/briar-android/src/main/res/drawable-night/ic_disconnected.xml new file mode 100644 index 000000000..070d7396b --- /dev/null +++ b/briar-android/src/main/res/drawable-night/ic_disconnected.xml @@ -0,0 +1,14 @@ + + + diff --git a/briar-android/src/main/res/drawable-night/ic_recently_connected.xml b/briar-android/src/main/res/drawable-night/ic_recently_connected.xml new file mode 100644 index 000000000..266a79397 --- /dev/null +++ b/briar-android/src/main/res/drawable-night/ic_recently_connected.xml @@ -0,0 +1,20 @@ + + + + diff --git a/briar-android/src/main/res/drawable/contact_connected.xml b/briar-android/src/main/res/drawable/contact_connected.xml deleted file mode 100644 index a53142509..000000000 --- a/briar-android/src/main/res/drawable/contact_connected.xml +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - diff --git a/briar-android/src/main/res/drawable/contact_disconnected.xml b/briar-android/src/main/res/drawable/contact_disconnected.xml deleted file mode 100644 index c1a088235..000000000 --- a/briar-android/src/main/res/drawable/contact_disconnected.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - diff --git a/briar-android/src/main/res/drawable/ic_connected.xml b/briar-android/src/main/res/drawable/ic_connected.xml new file mode 100644 index 000000000..ba6ab0ce9 --- /dev/null +++ b/briar-android/src/main/res/drawable/ic_connected.xml @@ -0,0 +1,13 @@ + + + diff --git a/briar-android/src/main/res/drawable/ic_disconnected.xml b/briar-android/src/main/res/drawable/ic_disconnected.xml new file mode 100644 index 000000000..695c80c5e --- /dev/null +++ b/briar-android/src/main/res/drawable/ic_disconnected.xml @@ -0,0 +1,14 @@ + + + diff --git a/briar-android/src/main/res/drawable/ic_recently_connected.xml b/briar-android/src/main/res/drawable/ic_recently_connected.xml new file mode 100644 index 000000000..bb31e1d78 --- /dev/null +++ b/briar-android/src/main/res/drawable/ic_recently_connected.xml @@ -0,0 +1,20 @@ + + + + diff --git a/briar-android/src/main/res/layout/activity_conversation.xml b/briar-android/src/main/res/layout/activity_conversation.xml index b2908bcf6..d48d0a7a2 100644 --- a/briar-android/src/main/res/layout/activity_conversation.xml +++ b/briar-android/src/main/res/layout/activity_conversation.xml @@ -18,27 +18,50 @@ android:layout_width="match_parent" android:layout_height="wrap_content"> - + - + - + + + diff --git a/briar-android/src/main/res/layout/contact_avatar_status.xml b/briar-android/src/main/res/layout/contact_avatar_status.xml deleted file mode 100644 index f499e4ba0..000000000 --- a/briar-android/src/main/res/layout/contact_avatar_status.xml +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/briar-android/src/main/res/layout/list_item_contact.xml b/briar-android/src/main/res/layout/list_item_contact.xml index a59a0e8d2..e3cc36d50 100644 --- a/briar-android/src/main/res/layout/list_item_contact.xml +++ b/briar-android/src/main/res/layout/list_item_contact.xml @@ -90,7 +90,7 @@ app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toTopOf="parent" tools:ignore="ContentDescription" - tools:src="@drawable/contact_connected" /> + tools:src="@drawable/ic_recently_connected" /> + tools:src="@drawable/ic_connected" /> diff --git a/briar-android/src/main/res/layout/list_item_group_member.xml b/briar-android/src/main/res/layout/list_item_group_member.xml index 21946a4f0..6fb81ee60 100644 --- a/briar-android/src/main/res/layout/list_item_group_member.xml +++ b/briar-android/src/main/res/layout/list_item_group_member.xml @@ -24,7 +24,7 @@ app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toTopOf="parent" tools:ignore="ContentDescription" - tools:src="@drawable/contact_connected" /> + tools:src="@drawable/ic_connected" /> Decline Options Online + Recently online Offline Send Allow