mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-12 10:49:06 +01:00
Compare commits
13 Commits
alpha-1.4.
...
545-log-db
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
63ae199be3 | ||
|
|
74a3f54d28 | ||
|
|
edcb234b93 | ||
|
|
dae00c7e4e | ||
|
|
29b16c4d74 | ||
|
|
40d58a9359 | ||
|
|
60a1a4d2d1 | ||
|
|
238aeb3abd | ||
|
|
62c16fad09 | ||
|
|
68e57bda0d | ||
|
|
0df73dbf0a | ||
|
|
5b648cbd35 | ||
|
|
5e7891d78a |
@@ -123,5 +123,6 @@ pre_release_tests:
|
||||
extends: .optional_tests
|
||||
script:
|
||||
- OPTIONAL_TESTS=org.briarproject.bramble.plugin.tor.BridgeTest ./gradlew --info bramble-java:test --tests BridgeTest
|
||||
timeout: 3h
|
||||
only:
|
||||
- tags
|
||||
|
||||
@@ -32,4 +32,13 @@ public interface MailboxManager {
|
||||
*/
|
||||
MailboxPairingTask startPairingTask(String qrCodePayload);
|
||||
|
||||
/**
|
||||
* Can be used by the UI to test the mailbox connection.
|
||||
*
|
||||
* @return true (success) or false (error).
|
||||
* A {@link OwnMailboxConnectionStatusEvent} might be broadcast with a new
|
||||
* {@link MailboxStatus}.
|
||||
*/
|
||||
boolean checkConnection();
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,32 @@
|
||||
package org.briarproject.bramble.db;
|
||||
|
||||
import static org.briarproject.bramble.api.db.DatabaseComponent.NO_CLEANUP_DEADLINE;
|
||||
import static org.briarproject.bramble.api.db.DatabaseComponent.TIMER_NOT_STARTED;
|
||||
import static org.briarproject.bramble.api.db.Metadata.REMOVE;
|
||||
import static org.briarproject.bramble.api.sync.Group.Visibility.INVISIBLE;
|
||||
import static org.briarproject.bramble.api.sync.Group.Visibility.SHARED;
|
||||
import static org.briarproject.bramble.api.sync.Group.Visibility.VISIBLE;
|
||||
import static org.briarproject.bramble.api.sync.SyncConstants.MESSAGE_HEADER_LENGTH;
|
||||
import static org.briarproject.bramble.api.sync.validation.MessageState.DELIVERED;
|
||||
import static org.briarproject.bramble.api.sync.validation.MessageState.PENDING;
|
||||
import static org.briarproject.bramble.api.sync.validation.MessageState.UNKNOWN;
|
||||
import static org.briarproject.bramble.db.DatabaseConstants.DB_SETTINGS_NAMESPACE;
|
||||
import static org.briarproject.bramble.db.DatabaseConstants.DIRTY_KEY;
|
||||
import static org.briarproject.bramble.db.DatabaseConstants.SCHEMA_VERSION_KEY;
|
||||
import static org.briarproject.bramble.db.ExponentialBackoff.calculateExpiry;
|
||||
import static org.briarproject.bramble.db.JdbcUtils.tryToClose;
|
||||
import static org.briarproject.bramble.util.LogUtils.logDuration;
|
||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
||||
import static org.briarproject.bramble.util.LogUtils.now;
|
||||
import static java.sql.Types.BINARY;
|
||||
import static java.sql.Types.BOOLEAN;
|
||||
import static java.sql.Types.INTEGER;
|
||||
import static java.sql.Types.VARCHAR;
|
||||
import static java.util.Arrays.asList;
|
||||
import static java.util.logging.Level.INFO;
|
||||
import static java.util.logging.Level.WARNING;
|
||||
import static java.util.logging.Logger.getLogger;
|
||||
|
||||
import org.briarproject.bramble.api.contact.Contact;
|
||||
import org.briarproject.bramble.api.contact.ContactId;
|
||||
import org.briarproject.bramble.api.contact.PendingContact;
|
||||
@@ -65,33 +92,6 @@ import java.util.logging.Logger;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.annotation.concurrent.GuardedBy;
|
||||
|
||||
import static java.sql.Types.BINARY;
|
||||
import static java.sql.Types.BOOLEAN;
|
||||
import static java.sql.Types.INTEGER;
|
||||
import static java.sql.Types.VARCHAR;
|
||||
import static java.util.Arrays.asList;
|
||||
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.db.DatabaseComponent.NO_CLEANUP_DEADLINE;
|
||||
import static org.briarproject.bramble.api.db.DatabaseComponent.TIMER_NOT_STARTED;
|
||||
import static org.briarproject.bramble.api.db.Metadata.REMOVE;
|
||||
import static org.briarproject.bramble.api.sync.Group.Visibility.INVISIBLE;
|
||||
import static org.briarproject.bramble.api.sync.Group.Visibility.SHARED;
|
||||
import static org.briarproject.bramble.api.sync.Group.Visibility.VISIBLE;
|
||||
import static org.briarproject.bramble.api.sync.SyncConstants.MESSAGE_HEADER_LENGTH;
|
||||
import static org.briarproject.bramble.api.sync.validation.MessageState.DELIVERED;
|
||||
import static org.briarproject.bramble.api.sync.validation.MessageState.PENDING;
|
||||
import static org.briarproject.bramble.api.sync.validation.MessageState.UNKNOWN;
|
||||
import static org.briarproject.bramble.db.DatabaseConstants.DB_SETTINGS_NAMESPACE;
|
||||
import static org.briarproject.bramble.db.DatabaseConstants.DIRTY_KEY;
|
||||
import static org.briarproject.bramble.db.DatabaseConstants.SCHEMA_VERSION_KEY;
|
||||
import static org.briarproject.bramble.db.ExponentialBackoff.calculateExpiry;
|
||||
import static org.briarproject.bramble.db.JdbcUtils.tryToClose;
|
||||
import static org.briarproject.bramble.util.LogUtils.logDuration;
|
||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
||||
import static org.briarproject.bramble.util.LogUtils.now;
|
||||
|
||||
/**
|
||||
* A generic database implementation that can be used with any JDBC-compatible
|
||||
* database library.
|
||||
@@ -415,6 +415,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
}
|
||||
createIndexes(txn);
|
||||
setDirty(txn, true);
|
||||
if (LOG.isLoggable(INFO)) countAndLogRows(txn);
|
||||
commitTransaction(txn);
|
||||
} catch (DbException e) {
|
||||
abortTransaction(txn);
|
||||
@@ -565,6 +566,40 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
}
|
||||
}
|
||||
|
||||
private void countAndLogRows(Connection txn) throws DbException {
|
||||
Statement s = null;
|
||||
try {
|
||||
s = txn.createStatement();
|
||||
countAndLogRows(s, "settings");
|
||||
countAndLogRows(s, "localAuthors");
|
||||
countAndLogRows(s, "contacts");
|
||||
countAndLogRows(s, "groups");
|
||||
countAndLogRows(s, "groupMetadata");
|
||||
countAndLogRows(s, "groupVisibilities");
|
||||
countAndLogRows(s, "messages");
|
||||
countAndLogRows(s, "messageMetadata");
|
||||
countAndLogRows(s, "messageDependencies");
|
||||
countAndLogRows(s, "offers");
|
||||
countAndLogRows(s, "statuses");
|
||||
countAndLogRows(s, "transports");
|
||||
countAndLogRows(s, "incomingKeys");
|
||||
countAndLogRows(s, "outgoingKeys");
|
||||
s.close();
|
||||
} catch (SQLException e) {
|
||||
tryToClose(s, LOG, WARNING);
|
||||
throw new DbException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void countAndLogRows(Statement s, String tableName)
|
||||
throws SQLException {
|
||||
ResultSet rs = s.executeQuery("SELECT COUNT (*) FROM " + tableName);
|
||||
if (rs.next())
|
||||
LOG.info("Table " + tableName + ": " + rs.getInt(1) + " rows");
|
||||
else LOG.warning("Table " + tableName + " could not be counted");
|
||||
rs.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Connection startTransaction() throws DbException {
|
||||
Connection txn;
|
||||
|
||||
@@ -2,27 +2,42 @@ package org.briarproject.bramble.mailbox;
|
||||
|
||||
import org.briarproject.bramble.api.db.DbException;
|
||||
import org.briarproject.bramble.api.db.Transaction;
|
||||
import org.briarproject.bramble.api.db.TransactionManager;
|
||||
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxManager;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxPairingTask;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxProperties;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxSettingsManager;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxStatus;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.bramble.api.system.Clock;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.annotation.concurrent.GuardedBy;
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static java.util.logging.Level.WARNING;
|
||||
import static java.util.logging.Logger.getLogger;
|
||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
||||
|
||||
@Immutable
|
||||
@NotNullByDefault
|
||||
class MailboxManagerImpl implements MailboxManager {
|
||||
|
||||
private static final String TAG = MailboxManagerImpl.class.getName();
|
||||
private final static Logger LOG = getLogger(TAG);
|
||||
|
||||
private final Executor ioExecutor;
|
||||
private final MailboxApi api;
|
||||
private final TransactionManager db;
|
||||
private final MailboxSettingsManager mailboxSettingsManager;
|
||||
private final MailboxPairingTaskFactory pairingTaskFactory;
|
||||
private final Clock clock;
|
||||
private final Object lock = new Object();
|
||||
|
||||
@Nullable
|
||||
@@ -32,11 +47,17 @@ class MailboxManagerImpl implements MailboxManager {
|
||||
@Inject
|
||||
MailboxManagerImpl(
|
||||
@IoExecutor Executor ioExecutor,
|
||||
MailboxApi api,
|
||||
TransactionManager db,
|
||||
MailboxSettingsManager mailboxSettingsManager,
|
||||
MailboxPairingTaskFactory pairingTaskFactory) {
|
||||
MailboxPairingTaskFactory pairingTaskFactory,
|
||||
Clock clock) {
|
||||
this.ioExecutor = ioExecutor;
|
||||
this.api = api;
|
||||
this.db = db;
|
||||
this.mailboxSettingsManager = mailboxSettingsManager;
|
||||
this.pairingTaskFactory = pairingTaskFactory;
|
||||
this.clock = clock;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -75,4 +96,29 @@ class MailboxManagerImpl implements MailboxManager {
|
||||
return created;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkConnection() {
|
||||
boolean success;
|
||||
try {
|
||||
MailboxProperties props = db.transactionWithNullableResult(true,
|
||||
mailboxSettingsManager::getOwnMailboxProperties);
|
||||
success = api.checkStatus(props);
|
||||
} catch (DbException | IOException | MailboxApi.ApiException e) {
|
||||
success = false;
|
||||
logException(LOG, WARNING, e);
|
||||
}
|
||||
if (success) {
|
||||
try {
|
||||
// we are only recording successful connections here
|
||||
// as those update the UI and failures might be false negatives
|
||||
db.transaction(false, txn ->
|
||||
mailboxSettingsManager.recordSuccessfulConnection(txn,
|
||||
clock.currentTimeMillis()));
|
||||
} catch (DbException e) {
|
||||
logException(LOG, WARNING, e);
|
||||
}
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -158,6 +158,12 @@ class MailboxPropertyManagerImpl implements MailboxPropertyManager,
|
||||
BdfList body = clientHelper.getMessageAsList(txn, m.getId());
|
||||
MailboxPropertiesUpdate p = parseProperties(body);
|
||||
txn.attach(new RemoteMailboxPropertiesUpdateEvent(c, p));
|
||||
// Reset message retransmission timers for the contact. Avoiding
|
||||
// messages getting stranded:
|
||||
// - on our mailbox, if they now have a mailbox but didn't before
|
||||
// - on the contact's old mailbox, if they removed their mailbox
|
||||
// - on the contact's old mailbox, if they replaced their mailbox
|
||||
db.resetUnackedMessagesToSend(txn, c);
|
||||
} catch (FormatException e) {
|
||||
throw new InvalidMessageException(e);
|
||||
}
|
||||
|
||||
@@ -281,6 +281,7 @@ public class MailboxPropertyManagerImplTest extends BrambleMockTestCase {
|
||||
public void testDoesNotDeleteAnythingWhenFirstUpdateIsDelivered()
|
||||
throws Exception {
|
||||
Transaction txn = new Transaction(null, false);
|
||||
Contact contact = getContact();
|
||||
GroupId contactGroupId = new GroupId(getRandomId());
|
||||
Message message = getMessage(contactGroupId);
|
||||
BdfList body = BdfList.of(1, propsDict);
|
||||
@@ -304,11 +305,13 @@ public class MailboxPropertyManagerImplTest extends BrambleMockTestCase {
|
||||
contactGroupId);
|
||||
will(returnValue(messageMetadata));
|
||||
oneOf(clientHelper).getContactId(txn, contactGroupId);
|
||||
will(returnValue(contact.getId()));
|
||||
oneOf(clientHelper).getMessageAsList(txn, message.getId());
|
||||
will(returnValue(body));
|
||||
oneOf(clientHelper).parseAndValidateMailboxPropertiesUpdate(
|
||||
propsDict);
|
||||
will(returnValue(props));
|
||||
oneOf(db).resetUnackedMessagesToSend(txn, contact.getId());
|
||||
}});
|
||||
|
||||
MailboxPropertyManagerImpl t = createInstance();
|
||||
@@ -321,6 +324,7 @@ public class MailboxPropertyManagerImplTest extends BrambleMockTestCase {
|
||||
public void testDeletesOlderUpdateWhenUpdateIsDelivered()
|
||||
throws Exception {
|
||||
Transaction txn = new Transaction(null, false);
|
||||
Contact contact = getContact();
|
||||
GroupId contactGroupId = new GroupId(getRandomId());
|
||||
Message message = getMessage(contactGroupId);
|
||||
BdfList body = BdfList.of(1, propsDict);
|
||||
@@ -352,11 +356,13 @@ public class MailboxPropertyManagerImplTest extends BrambleMockTestCase {
|
||||
oneOf(db).deleteMessage(txn, updateId);
|
||||
oneOf(db).deleteMessageMetadata(txn, updateId);
|
||||
oneOf(clientHelper).getContactId(txn, contactGroupId);
|
||||
will(returnValue(contact.getId()));
|
||||
oneOf(clientHelper).getMessageAsList(txn, message.getId());
|
||||
will(returnValue(body));
|
||||
oneOf(clientHelper).parseAndValidateMailboxPropertiesUpdate(
|
||||
propsDict);
|
||||
will(returnValue(props));
|
||||
oneOf(db).resetUnackedMessagesToSend(txn, contact.getId());
|
||||
}});
|
||||
|
||||
MailboxPropertyManagerImpl t = createInstance();
|
||||
|
||||
@@ -47,11 +47,11 @@ public class MailboxActivity extends BriarActivity {
|
||||
setContentView(R.layout.activity_mailbox);
|
||||
|
||||
progressBar = findViewById(R.id.progressBar);
|
||||
if (viewModel.getState().getValue() == null) {
|
||||
if (viewModel.getPairingState().getValue() == null) {
|
||||
progressBar.setVisibility(VISIBLE);
|
||||
}
|
||||
|
||||
viewModel.getState().observeEvent(this, state -> {
|
||||
viewModel.getPairingState().observeEvent(this, state -> {
|
||||
if (state instanceof MailboxState.NotSetup) {
|
||||
onNotSetup();
|
||||
} else if (state instanceof MailboxState.ShowDownload) {
|
||||
@@ -67,7 +67,7 @@ public class MailboxActivity extends BriarActivity {
|
||||
} else if (state instanceof MailboxState.CameraError) {
|
||||
onCameraError();
|
||||
} else if (state instanceof MailboxState.IsPaired) {
|
||||
onIsPaired();
|
||||
onIsPaired(((MailboxState.IsPaired) state).isOnline);
|
||||
} else {
|
||||
throw new AssertionError("Unknown state: " + state);
|
||||
}
|
||||
@@ -85,7 +85,7 @@ public class MailboxActivity extends BriarActivity {
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
MailboxState s = viewModel.getState().getLastValue();
|
||||
MailboxState s = viewModel.getPairingState().getLastValue();
|
||||
if (s instanceof MailboxState.Pairing) {
|
||||
// don't go back in the flow if we are already pairing
|
||||
// with the mailbox. We provide a try-again button instead.
|
||||
@@ -181,10 +181,13 @@ public class MailboxActivity extends BriarActivity {
|
||||
showFragment(getSupportFragmentManager(), f, ErrorFragment.TAG);
|
||||
}
|
||||
|
||||
private void onIsPaired() {
|
||||
private void onIsPaired(boolean isOnline) {
|
||||
progressBar.setVisibility(INVISIBLE);
|
||||
showFragment(getSupportFragmentManager(), new MailboxStatusFragment(),
|
||||
MailboxStatusFragment.TAG, false);
|
||||
Fragment f = isOnline ?
|
||||
new MailboxStatusFragment() : new OfflineStatusFragment();
|
||||
String tag = isOnline ?
|
||||
MailboxStatusFragment.TAG : OfflineStatusFragment.TAG;
|
||||
showFragment(getSupportFragmentManager(), f, tag, false);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package org.briarproject.briar.android.mailbox;
|
||||
|
||||
import org.briarproject.bramble.api.mailbox.MailboxPairingState;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxStatus;
|
||||
|
||||
class MailboxState {
|
||||
|
||||
@@ -29,10 +28,10 @@ class MailboxState {
|
||||
}
|
||||
|
||||
static class IsPaired extends MailboxState {
|
||||
final MailboxStatus mailboxStatus;
|
||||
final boolean isOnline;
|
||||
|
||||
IsPaired(MailboxStatus mailboxStatus) {
|
||||
this.mailboxStatus = mailboxStatus;
|
||||
IsPaired(boolean isOnline) {
|
||||
this.isOnline = isOnline;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,37 +1,62 @@
|
||||
package org.briarproject.briar.android.mailbox;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.ColorStateList;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.Button;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.briarproject.bramble.api.mailbox.MailboxStatus;
|
||||
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
||||
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
||||
import org.briarproject.briar.R;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import androidx.annotation.ColorRes;
|
||||
import androidx.annotation.DrawableRes;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.UiThread;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentActivity;
|
||||
import androidx.lifecycle.ViewModelProvider;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
import static android.view.View.INVISIBLE;
|
||||
import static android.view.View.VISIBLE;
|
||||
import static androidx.core.content.ContextCompat.getColor;
|
||||
import static androidx.core.widget.ImageViewCompat.setImageTintList;
|
||||
import static androidx.transition.TransitionManager.beginDelayedTransition;
|
||||
import static org.briarproject.briar.android.AppModule.getAndroidComponent;
|
||||
import static org.briarproject.briar.android.util.UiUtils.MIN_DATE_RESOLUTION;
|
||||
import static org.briarproject.briar.android.util.UiUtils.formatDate;
|
||||
import static org.briarproject.briar.android.util.UiUtils.observeOnce;
|
||||
|
||||
@MethodsNotNullByDefault
|
||||
@ParametersNotNullByDefault
|
||||
public class MailboxStatusFragment extends Fragment {
|
||||
|
||||
static final String TAG = MailboxStatusFragment.class.getName();
|
||||
private static final int NUM_FAILURES = 4;
|
||||
|
||||
@Inject
|
||||
ViewModelProvider.Factory viewModelFactory;
|
||||
|
||||
private MailboxViewModel viewModel;
|
||||
private final Handler handler = new Handler(Looper.getMainLooper());
|
||||
@Nullable // UiThread
|
||||
private Runnable refresher = null;
|
||||
|
||||
private ImageView imageView;
|
||||
private TextView statusTitleView;
|
||||
private TextView statusInfoView;
|
||||
|
||||
@Override
|
||||
public void onAttach(Context context) {
|
||||
@@ -54,11 +79,72 @@ public class MailboxStatusFragment extends Fragment {
|
||||
@Override
|
||||
public void onViewCreated(View v, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(v, savedInstanceState);
|
||||
MailboxState.IsPaired state =
|
||||
(MailboxState.IsPaired) viewModel.getState().getLastValue();
|
||||
requireNonNull(state); // TODO check assumption
|
||||
TextView statusInfoView = v.findViewById(R.id.statusInfoView);
|
||||
long lastSuccess = state.mailboxStatus.getTimeOfLastSuccess();
|
||||
|
||||
Button checkButton = v.findViewById(R.id.checkButton);
|
||||
ProgressBar checkProgress = v.findViewById(R.id.checkProgress);
|
||||
checkButton.setOnClickListener(view -> {
|
||||
beginDelayedTransition((ViewGroup) v);
|
||||
checkButton.setVisibility(INVISIBLE);
|
||||
checkProgress.setVisibility(VISIBLE);
|
||||
observeOnce(viewModel.checkConnection(), this, result -> {
|
||||
beginDelayedTransition((ViewGroup) v);
|
||||
checkButton.setVisibility(VISIBLE);
|
||||
checkProgress.setVisibility(INVISIBLE);
|
||||
});
|
||||
});
|
||||
|
||||
imageView = v.findViewById(R.id.imageView);
|
||||
statusTitleView = v.findViewById(R.id.statusTitleView);
|
||||
statusInfoView = v.findViewById(R.id.statusInfoView);
|
||||
viewModel.getStatus()
|
||||
.observe(getViewLifecycleOwner(), this::onMailboxStateChanged);
|
||||
|
||||
// TODO
|
||||
// * detect problems and show them #2175
|
||||
// * add "Unlink" button confirmation dialog and functionality #2173
|
||||
Button unlinkButton = v.findViewById(R.id.unlinkButton);
|
||||
unlinkButton.setOnClickListener(view -> Toast.makeText(requireContext(),
|
||||
"NOT IMPLEMENTED", Toast.LENGTH_SHORT).show());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
requireActivity().setTitle(R.string.mailbox_status_title);
|
||||
refresher = this::refreshLastConnection;
|
||||
handler.postDelayed(refresher, MIN_DATE_RESOLUTION);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStop() {
|
||||
super.onStop();
|
||||
handler.removeCallbacks(refresher);
|
||||
refresher = null;
|
||||
}
|
||||
|
||||
private void onMailboxStateChanged(MailboxStatus status) {
|
||||
@ColorRes int tintRes;
|
||||
@DrawableRes int iconRes;
|
||||
String title;
|
||||
if (status.getAttemptsSinceSuccess() == 0) {
|
||||
iconRes = R.drawable.ic_check_circle_outline;
|
||||
title = getString(R.string.mailbox_status_connected_title);
|
||||
tintRes = R.color.briar_brand_green;
|
||||
} else if (status.getAttemptsSinceSuccess() < NUM_FAILURES) {
|
||||
iconRes = R.drawable.ic_help_outline_white;
|
||||
title = getString(R.string.mailbox_status_problem_title);
|
||||
tintRes = R.color.briar_orange_500;
|
||||
} else {
|
||||
tintRes = R.color.briar_red_500;
|
||||
title = getString(R.string.mailbox_status_failure_title);
|
||||
iconRes = R.drawable.alerts_and_states_error;
|
||||
}
|
||||
imageView.setImageResource(iconRes);
|
||||
int color = getColor(requireContext(), tintRes);
|
||||
setImageTintList(imageView, ColorStateList.valueOf(color));
|
||||
statusTitleView.setText(title);
|
||||
|
||||
long lastSuccess = status.getTimeOfLastSuccess();
|
||||
String lastConnectionText;
|
||||
if (lastSuccess < 0) {
|
||||
lastConnectionText =
|
||||
@@ -66,21 +152,19 @@ public class MailboxStatusFragment extends Fragment {
|
||||
} else {
|
||||
lastConnectionText = formatDate(requireContext(), lastSuccess);
|
||||
}
|
||||
String statusInfoText = getString(
|
||||
R.string.mailbox_status_connected_info, lastConnectionText);
|
||||
String statusInfoText =
|
||||
getString(R.string.mailbox_status_connected_info,
|
||||
lastConnectionText);
|
||||
statusInfoView.setText(statusInfoText);
|
||||
// TODO
|
||||
// * react to status changes
|
||||
// * detect problems and show them
|
||||
// * update connection time periodically like conversation timestamps
|
||||
// * add "Check connection" button
|
||||
// * add "Unlink" button with confirmation dialog
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
requireActivity().setTitle(R.string.mailbox_status_title);
|
||||
@UiThread
|
||||
private void refreshLastConnection() {
|
||||
MailboxStatus status = viewModel.getStatus().getValue();
|
||||
if (status != null) onMailboxStateChanged(status);
|
||||
if (refresher != null) {
|
||||
handler.postDelayed(refresher, MIN_DATE_RESOLUTION);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -7,16 +7,22 @@ import com.google.zxing.Result;
|
||||
import org.briarproject.bramble.api.Consumer;
|
||||
import org.briarproject.bramble.api.db.DatabaseExecutor;
|
||||
import org.briarproject.bramble.api.db.TransactionManager;
|
||||
import org.briarproject.bramble.api.event.Event;
|
||||
import org.briarproject.bramble.api.event.EventBus;
|
||||
import org.briarproject.bramble.api.event.EventListener;
|
||||
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
||||
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxManager;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxPairingState;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxPairingTask;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxStatus;
|
||||
import org.briarproject.bramble.api.mailbox.OwnMailboxConnectionStatusEvent;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.bramble.api.plugin.Plugin;
|
||||
import org.briarproject.bramble.api.plugin.PluginManager;
|
||||
import org.briarproject.bramble.api.plugin.TorConstants;
|
||||
import org.briarproject.bramble.api.plugin.TransportId;
|
||||
import org.briarproject.bramble.api.plugin.event.TransportInactiveEvent;
|
||||
import org.briarproject.bramble.api.system.AndroidExecutor;
|
||||
import org.briarproject.briar.android.mailbox.MailboxState.NotSetup;
|
||||
import org.briarproject.briar.android.qrcode.QrCodeDecoder;
|
||||
@@ -32,6 +38,8 @@ import javax.inject.Inject;
|
||||
import androidx.annotation.AnyThread;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.UiThread;
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.lifecycle.MutableLiveData;
|
||||
|
||||
import static java.util.logging.Level.INFO;
|
||||
import static java.util.logging.Logger.getLogger;
|
||||
@@ -39,17 +47,22 @@ import static org.briarproject.bramble.api.plugin.Plugin.State.ACTIVE;
|
||||
|
||||
@NotNullByDefault
|
||||
class MailboxViewModel extends DbViewModel
|
||||
implements QrCodeDecoder.ResultCallback, Consumer<MailboxPairingState> {
|
||||
implements QrCodeDecoder.ResultCallback, Consumer<MailboxPairingState>,
|
||||
EventListener {
|
||||
|
||||
private static final Logger LOG =
|
||||
getLogger(MailboxViewModel.class.getName());
|
||||
|
||||
private final EventBus eventBus;
|
||||
private final Executor ioExecutor;
|
||||
private final QrCodeDecoder qrCodeDecoder;
|
||||
private final PluginManager pluginManager;
|
||||
private final MailboxManager mailboxManager;
|
||||
|
||||
private final MutableLiveEvent<MailboxState> state =
|
||||
private final MutableLiveEvent<MailboxState> pairingState =
|
||||
new MutableLiveEvent<>();
|
||||
private final MutableLiveData<MailboxStatus> status =
|
||||
new MutableLiveData<>();
|
||||
@Nullable
|
||||
private MailboxPairingTask pairingTask = null;
|
||||
|
||||
@@ -60,19 +73,24 @@ class MailboxViewModel extends DbViewModel
|
||||
LifecycleManager lifecycleManager,
|
||||
TransactionManager db,
|
||||
AndroidExecutor androidExecutor,
|
||||
EventBus eventBus,
|
||||
@IoExecutor Executor ioExecutor,
|
||||
PluginManager pluginManager,
|
||||
MailboxManager mailboxManager) {
|
||||
super(app, dbExecutor, lifecycleManager, db, androidExecutor);
|
||||
this.eventBus = eventBus;
|
||||
this.ioExecutor = ioExecutor;
|
||||
this.pluginManager = pluginManager;
|
||||
this.mailboxManager = mailboxManager;
|
||||
qrCodeDecoder = new QrCodeDecoder(androidExecutor, ioExecutor, this);
|
||||
eventBus.addListener(this);
|
||||
checkIfSetup();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCleared() {
|
||||
super.onCleared();
|
||||
eventBus.removeListener(this);
|
||||
MailboxPairingTask task = pairingTask;
|
||||
if (task != null) {
|
||||
task.removeObserver(this);
|
||||
@@ -89,9 +107,11 @@ class MailboxViewModel extends DbViewModel
|
||||
if (isPaired) {
|
||||
MailboxStatus mailboxStatus =
|
||||
mailboxManager.getMailboxStatus(txn);
|
||||
state.postEvent(new MailboxState.IsPaired(mailboxStatus));
|
||||
boolean isOnline = isTorActive();
|
||||
pairingState.postEvent(new MailboxState.IsPaired(isOnline));
|
||||
status.postValue(mailboxStatus);
|
||||
} else {
|
||||
state.postEvent(new NotSetup());
|
||||
pairingState.postEvent(new NotSetup());
|
||||
}
|
||||
}, this::handleException);
|
||||
} else {
|
||||
@@ -100,18 +120,37 @@ class MailboxViewModel extends DbViewModel
|
||||
}
|
||||
}
|
||||
|
||||
@UiThread
|
||||
@Override
|
||||
public void eventOccurred(Event e) {
|
||||
if (e instanceof OwnMailboxConnectionStatusEvent) {
|
||||
MailboxStatus status =
|
||||
((OwnMailboxConnectionStatusEvent) e).getStatus();
|
||||
this.status.setValue(status);
|
||||
} else if (e instanceof TransportInactiveEvent) {
|
||||
TransportId id = ((TransportInactiveEvent) e).getTransportId();
|
||||
if (!TorConstants.ID.equals(id)) return;
|
||||
MailboxState lastState = pairingState.getLastValue();
|
||||
if (lastState instanceof MailboxState.IsPaired) {
|
||||
pairingState.setEvent(new MailboxState.IsPaired(false));
|
||||
} else if (lastState != null) {
|
||||
pairingState.setEvent(new MailboxState.OfflineWhenPairing());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@UiThread
|
||||
void onScanButtonClicked() {
|
||||
if (isTorActive()) {
|
||||
state.setEvent(new MailboxState.ScanningQrCode());
|
||||
pairingState.setEvent(new MailboxState.ScanningQrCode());
|
||||
} else {
|
||||
state.setEvent(new MailboxState.OfflineWhenPairing());
|
||||
pairingState.setEvent(new MailboxState.OfflineWhenPairing());
|
||||
}
|
||||
}
|
||||
|
||||
@UiThread
|
||||
void onCameraError() {
|
||||
state.setEvent(new MailboxState.CameraError());
|
||||
pairingState.setEvent(new MailboxState.CameraError());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -127,7 +166,7 @@ class MailboxViewModel extends DbViewModel
|
||||
pairingTask = mailboxManager.startPairingTask(qrCodePayload);
|
||||
pairingTask.addObserver(this);
|
||||
} else {
|
||||
state.postEvent(new MailboxState.OfflineWhenPairing());
|
||||
pairingState.postEvent(new MailboxState.OfflineWhenPairing());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -138,7 +177,7 @@ class MailboxViewModel extends DbViewModel
|
||||
LOG.info("New pairing state: " +
|
||||
mailboxPairingState.getClass().getSimpleName());
|
||||
}
|
||||
state.setEvent(new MailboxState.Pairing(mailboxPairingState));
|
||||
pairingState.setEvent(new MailboxState.Pairing(mailboxPairingState));
|
||||
}
|
||||
|
||||
private boolean isTorActive() {
|
||||
@@ -148,7 +187,7 @@ class MailboxViewModel extends DbViewModel
|
||||
|
||||
@UiThread
|
||||
void showDownloadFragment() {
|
||||
state.setEvent(new MailboxState.ShowDownload());
|
||||
pairingState.setEvent(new MailboxState.ShowDownload());
|
||||
}
|
||||
|
||||
@UiThread
|
||||
@@ -157,7 +196,37 @@ class MailboxViewModel extends DbViewModel
|
||||
}
|
||||
|
||||
@UiThread
|
||||
LiveEvent<MailboxState> getState() {
|
||||
return state;
|
||||
void checkIfOnlineWhenPaired() {
|
||||
boolean isOnline = isTorActive();
|
||||
pairingState.setEvent(new MailboxState.IsPaired(isOnline));
|
||||
}
|
||||
|
||||
LiveData<Boolean> checkConnection() {
|
||||
MutableLiveData<Boolean> liveData = new MutableLiveData<>();
|
||||
ioExecutor.execute(() -> {
|
||||
boolean success = mailboxManager.checkConnection();
|
||||
if (LOG.isLoggable(INFO)) {
|
||||
LOG.info("Got result from connection check: " + success);
|
||||
}
|
||||
liveData.postValue(success);
|
||||
if (!success) { // force failure screen
|
||||
MailboxStatus lastStatus = status.getValue();
|
||||
long lastSuccess = lastStatus == null ?
|
||||
-1 : lastStatus.getTimeOfLastSuccess();
|
||||
long now = System.currentTimeMillis();
|
||||
status.postValue(new MailboxStatus(now, lastSuccess, 999));
|
||||
}
|
||||
});
|
||||
return liveData;
|
||||
}
|
||||
|
||||
@UiThread
|
||||
LiveEvent<MailboxState> getPairingState() {
|
||||
return pairingState;
|
||||
}
|
||||
|
||||
@UiThread
|
||||
LiveData<MailboxStatus> getStatus() {
|
||||
return status;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,10 +33,9 @@ public class OfflineFragment extends Fragment {
|
||||
@Inject
|
||||
ViewModelProvider.Factory viewModelFactory;
|
||||
|
||||
private MailboxViewModel viewModel;
|
||||
protected MailboxViewModel viewModel;
|
||||
|
||||
private NestedScrollView scrollView;
|
||||
protected Button buttonView;
|
||||
|
||||
@Override
|
||||
public void onAttach(Context context) {
|
||||
@@ -61,8 +60,8 @@ public class OfflineFragment extends Fragment {
|
||||
Intent i = new Intent(requireContext(), TransportsActivity.class);
|
||||
startActivity(i);
|
||||
});
|
||||
buttonView = v.findViewById(R.id.button);
|
||||
buttonView.setOnClickListener(view -> viewModel.showDownloadFragment());
|
||||
Button buttonView = v.findViewById(R.id.button);
|
||||
buttonView.setOnClickListener(view -> onTryAgainClicked());
|
||||
|
||||
return v;
|
||||
}
|
||||
@@ -74,4 +73,8 @@ public class OfflineFragment extends Fragment {
|
||||
scrollView.post(() -> scrollView.fullScroll(FOCUS_DOWN));
|
||||
}
|
||||
|
||||
protected void onTryAgainClicked() {
|
||||
viewModel.showDownloadFragment();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
package org.briarproject.briar.android.mailbox;
|
||||
|
||||
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
||||
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
||||
|
||||
@MethodsNotNullByDefault
|
||||
@ParametersNotNullByDefault
|
||||
public class OfflineStatusFragment extends OfflineFragment {
|
||||
|
||||
public static final String TAG = OfflineStatusFragment.class.getName();
|
||||
|
||||
@Override
|
||||
protected void onTryAgainClicked() {
|
||||
viewModel.checkIfOnlineWhenPaired();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -10,13 +10,14 @@
|
||||
android:id="@+id/imageView"
|
||||
android:layout_width="32dp"
|
||||
android:layout_height="32dp"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginLeft="16dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@+id/statusTitleView"
|
||||
android:layout_marginHorizontal="16dp"
|
||||
app:layout_constraintBottom_toTopOf="@+id/statusTitleView"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.5"
|
||||
app:layout_constraintHorizontal_chainStyle="packed"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintVertical_bias="0.25"
|
||||
app:layout_constraintVertical_chainStyle="packed"
|
||||
app:srcCompat="@drawable/ic_check_circle_outline"
|
||||
app:tint="@color/briar_brand_green"
|
||||
@@ -26,14 +27,37 @@
|
||||
android:id="@+id/statusTitleView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="16dp"
|
||||
android:layout_margin="16dp"
|
||||
android:text="@string/mailbox_status_connected_title"
|
||||
android:textAppearance="@style/TextAppearance.MaterialComponents.Headline6"
|
||||
app:layout_constrainedWidth="true"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/imageView"
|
||||
app:layout_constraintBottom_toTopOf="@+id/checkButton"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@+id/imageView"
|
||||
app:layout_constraintTop_toTopOf="@+id/imageView" />
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/imageView" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/checkButton"
|
||||
style="@style/BriarButtonFlat.Neutral"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="16dp"
|
||||
android:text="@string/mailbox_status_check_button"
|
||||
app:layout_constraintBottom_toTopOf="@+id/statusInfoView"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/statusTitleView" />
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/checkProgress"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="invisible"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/checkButton"
|
||||
app:layout_constraintEnd_toEndOf="@+id/checkButton"
|
||||
app:layout_constraintStart_toStartOf="@+id/checkButton"
|
||||
app:layout_constraintTop_toTopOf="@+id/checkButton"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/statusInfoView"
|
||||
@@ -41,11 +65,21 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="16dp"
|
||||
android:gravity="center"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintBottom_toTopOf="@+id/unlinkButton"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/statusTitleView"
|
||||
app:layout_constraintVertical_bias="0.0"
|
||||
app:layout_constraintTop_toBottomOf="@+id/checkButton"
|
||||
tools:text="@string/mailbox_status_connected_info" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/unlinkButton"
|
||||
style="@style/BriarButtonFlat.Negative"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="16dp"
|
||||
android:text="@string/mailbox_status_unlink_button"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
@@ -643,10 +643,14 @@
|
||||
<string name="tor_offline_button_check">Check connection settings</string>
|
||||
<string name="mailbox_status_title">Mailbox status</string>
|
||||
<string name="mailbox_status_connected_title">Mailbox is running</string>
|
||||
<string name="mailbox_status_problem_title">We are having trouble connecting to the mailbox</string>
|
||||
<string name="mailbox_status_failure_title">Mailbox is unavailable</string>
|
||||
<string name="mailbox_status_check_button">Check Connection</string>
|
||||
<!-- Example for string substitution: Last connection: 3min ago-->
|
||||
<string name="mailbox_status_connected_info">Last connection: %s</string>
|
||||
<!-- Indicates that there never was a connection to the mailbox. Last connection: Never -->
|
||||
<string name="mailbox_status_connected_never">Never</string>
|
||||
<string name="mailbox_status_unlink_button">Unlink</string>
|
||||
|
||||
<!-- Conversation Settings -->
|
||||
<string name="disappearing_messages_title">Disappearing messages</string>
|
||||
|
||||
Reference in New Issue
Block a user