mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-12 02:39:05 +01:00
Compare commits
64 Commits
integratio
...
user-testi
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e3e0d3bf59 | ||
|
|
761647dde4 | ||
|
|
51ddd47502 | ||
|
|
eb72754d8d | ||
|
|
aeaa549d6f | ||
|
|
22fb2df3dc | ||
|
|
906ee6c735 | ||
|
|
6a81e805cc | ||
|
|
f6ccf885e6 | ||
|
|
d821696c6a | ||
|
|
cf9162b694 | ||
|
|
48f0fd0dea | ||
|
|
73bbfe3993 | ||
|
|
096249ad32 | ||
|
|
ebcc789977 | ||
|
|
4d7bc18155 | ||
|
|
7682bf9553 | ||
|
|
e8428df700 | ||
|
|
a7cec213b1 | ||
|
|
dd4afd7c39 | ||
|
|
838fc46af4 | ||
|
|
d29ade44fb | ||
|
|
76d29d4a18 | ||
|
|
04ef837307 | ||
|
|
4a73daa214 | ||
|
|
9f9d5642c2 | ||
|
|
707e7b06df | ||
|
|
b5f69b5212 | ||
|
|
a7e5924137 | ||
|
|
105dc08121 | ||
|
|
c49db25e96 | ||
|
|
34c1490a8b | ||
|
|
d7b60c5d5b | ||
|
|
a4a8fea29d | ||
|
|
396b433030 | ||
|
|
8b1badc715 | ||
|
|
b1d6e81c73 | ||
|
|
7204f8ea0b | ||
|
|
4ab0d4b24b | ||
|
|
a8a905fb87 | ||
|
|
73b0e0356f | ||
|
|
952cc9265f | ||
|
|
104587838c | ||
|
|
1a91be403b | ||
|
|
f2e1a1bf73 | ||
|
|
fe360f28fd | ||
|
|
8247e10e82 | ||
|
|
e8c029a7b4 | ||
|
|
5cdbd58ac3 | ||
|
|
51a308c6f4 | ||
|
|
62215be369 | ||
|
|
7a8f07a8c6 | ||
|
|
c3201590de | ||
|
|
f002378bbf | ||
|
|
31ca3e2cb5 | ||
|
|
3a11cb32c4 | ||
|
|
aee663fcc0 | ||
|
|
b266b78a49 | ||
|
|
02f1385ed2 | ||
|
|
c683038343 | ||
|
|
336dd8de5b | ||
|
|
7e4460b4ea | ||
|
|
152b3f1967 | ||
|
|
ae461b9878 |
15
.idea/dictionaries/briar.xml
generated
15
.idea/dictionaries/briar.xml
generated
@@ -1,15 +0,0 @@
|
||||
<component name="ProjectDictionaryState">
|
||||
<dictionary name="briar">
|
||||
<words>
|
||||
<w>briar</w>
|
||||
<w>briarproject</w>
|
||||
<w>emoji</w>
|
||||
<w>encrypter</w>
|
||||
<w>identicon</w>
|
||||
<w>introducee</w>
|
||||
<w>introducees</w>
|
||||
<w>introducer</w>
|
||||
<w>onboarding</w>
|
||||
</words>
|
||||
</dictionary>
|
||||
</component>
|
||||
@@ -11,8 +11,8 @@ android {
|
||||
defaultConfig {
|
||||
minSdkVersion 16
|
||||
targetSdkVersion 29
|
||||
versionCode 10216
|
||||
versionName "1.2.16"
|
||||
versionCode 10214
|
||||
versionName "1.2.14"
|
||||
consumerProguardFiles 'proguard-rules.txt'
|
||||
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
@@ -38,8 +38,8 @@ configurations {
|
||||
|
||||
dependencies {
|
||||
implementation project(path: ':bramble-core', configuration: 'default')
|
||||
tor 'org.briarproject:tor-android:0.3.5.13@zip'
|
||||
tor 'org.briarproject:obfs4proxy-android:0.0.12-dev-40245c4a@zip'
|
||||
tor 'org.briarproject:tor-android:0.3.5.12@zip'
|
||||
tor 'org.briarproject:obfs4proxy-android:0.0.11-2@zip'
|
||||
|
||||
annotationProcessor 'com.google.dagger:dagger-compiler:2.24'
|
||||
|
||||
|
||||
@@ -31,7 +31,6 @@ public class AndroidUtils {
|
||||
private static final String FAKE_BLUETOOTH_ADDRESS = "02:00:00:00:00:00";
|
||||
|
||||
private static final String STORED_REPORTS = "dev-reports";
|
||||
private static final String STORED_LOGCAT = "dev-logcat";
|
||||
|
||||
public static Collection<String> getSupportedArchitectures() {
|
||||
List<String> abis = new ArrayList<>();
|
||||
@@ -108,10 +107,6 @@ public class AndroidUtils {
|
||||
return ctx.getDir(STORED_REPORTS, MODE_PRIVATE);
|
||||
}
|
||||
|
||||
public static File getLogcatFile(Context ctx) {
|
||||
return new File(ctx.getFilesDir(), STORED_LOGCAT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of supported content types for image attachments.
|
||||
* GIFs can't be compressed on API < 24 so they're not supported.
|
||||
|
||||
@@ -75,8 +75,8 @@ dependencyVerification {
|
||||
'org.beanshell:bsh:1.3.0:bsh-1.3.0.jar:9b04edc75d19db54f1b4e8b5355e9364384c6cf71eb0a1b9724c159d779879f8',
|
||||
'org.bouncycastle:bcpkix-jdk15on:1.56:bcpkix-jdk15on-1.56.jar:7043dee4e9e7175e93e0b36f45b1ec1ecb893c5f755667e8b916eb8dd201c6ca',
|
||||
'org.bouncycastle:bcprov-jdk15on:1.56:bcprov-jdk15on-1.56.jar:963e1ee14f808ffb99897d848ddcdb28fa91ddda867eb18d303e82728f878349',
|
||||
'org.briarproject:obfs4proxy-android:0.0.12-dev-40245c4a:obfs4proxy-android-0.0.12-dev-40245c4a.zip:8ab05a8f8391be2cb5ab2b665c281a06d9e3a756bd0f95a40a36ca927866ea82',
|
||||
'org.briarproject:tor-android:0.3.5.13:tor-android-0.3.5.13.zip:e0978db136731dae07774b722970cdae1e462fb5adc82845dd80a7e2d87f356c',
|
||||
'org.briarproject:obfs4proxy-android:0.0.11-2:obfs4proxy-android-0.0.11-2.zip:57e55cbe87aa2aac210fdbb6cd8cdeafe15f825406a08ebf77a8b787aa2c6a8a',
|
||||
'org.briarproject:tor-android:0.3.5.12:tor-android-0.3.5.12.zip:db71fb3290acff79d572af0752570eaf6aad7c4d88c9b9aa0b4d5afe2b9ead9c',
|
||||
'org.checkerframework:checker-compat-qual:2.5.3:checker-compat-qual-2.5.3.jar:d76b9afea61c7c082908023f0cbc1427fab9abd2df915c8b8a3e7a509bccbc6d',
|
||||
'org.checkerframework:checker-qual:2.5.2:checker-qual-2.5.2.jar:64b02691c8b9d4e7700f8ee2e742dce7ea2c6e81e662b7522c9ee3bf568c040a',
|
||||
'org.checkerframework:checker-qual:2.8.1:checker-qual-2.8.1.jar:9103499008bcecd4e948da29b17864abb64304e15706444ae209d17ebe0575df',
|
||||
|
||||
@@ -9,5 +9,4 @@ public interface FeatureFlags {
|
||||
|
||||
boolean shouldEnableProfilePictures();
|
||||
|
||||
boolean shouldEnableDisappearingMessages();
|
||||
}
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
package org.briarproject.bramble.api.cleanup;
|
||||
|
||||
import org.briarproject.bramble.api.db.DatabaseComponent;
|
||||
import org.briarproject.bramble.api.db.DbException;
|
||||
import org.briarproject.bramble.api.db.Transaction;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.bramble.api.sync.GroupId;
|
||||
import org.briarproject.bramble.api.sync.MessageId;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* An interface for registering a hook with the {@link CleanupManager}
|
||||
* that will be called when a message's cleanup deadline is reached.
|
||||
*/
|
||||
@NotNullByDefault
|
||||
public interface CleanupHook {
|
||||
|
||||
/**
|
||||
* Called when the cleanup deadlines of one or more messages are reached.
|
||||
* <p>
|
||||
* The callee is not required to delete the messages, but the hook won't be
|
||||
* called again for these messages unless another cleanup timer is set (see
|
||||
* {@link DatabaseComponent#setCleanupTimerDuration(Transaction, MessageId, long)}
|
||||
* and {@link DatabaseComponent#startCleanupTimer(Transaction, MessageId)}).
|
||||
*/
|
||||
void deleteMessages(Transaction txn, GroupId g,
|
||||
Collection<MessageId> messageIds) throws DbException;
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
package org.briarproject.bramble.api.cleanup;
|
||||
|
||||
import org.briarproject.bramble.api.cleanup.event.CleanupTimerStartedEvent;
|
||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||
import org.briarproject.bramble.api.db.DatabaseComponent;
|
||||
import org.briarproject.bramble.api.db.Transaction;
|
||||
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.bramble.api.sync.ClientId;
|
||||
import org.briarproject.bramble.api.sync.MessageId;
|
||||
|
||||
/**
|
||||
* The CleanupManager is responsible for tracking the cleanup deadlines of
|
||||
* messages and passing them to their respective
|
||||
* {@link CleanupHook CleanupHooks} when the deadlines are reached.
|
||||
* <p>
|
||||
* The CleanupManager responds to
|
||||
* {@link CleanupTimerStartedEvent CleanupTimerStartedEvents} broadcast by the
|
||||
* {@link DatabaseComponent}.
|
||||
* <p>
|
||||
* See {@link DatabaseComponent#setCleanupTimerDuration(Transaction, MessageId, long)},
|
||||
* {@link DatabaseComponent#startCleanupTimer(Transaction, MessageId)},
|
||||
* {@link DatabaseComponent#stopCleanupTimer(Transaction, MessageId)}.
|
||||
*/
|
||||
@NotNullByDefault
|
||||
public interface CleanupManager {
|
||||
|
||||
/**
|
||||
* When scheduling a cleanup task we overshoot the deadline by this many
|
||||
* milliseconds to reduce the number of tasks that need to be scheduled
|
||||
* when messages have cleanup deadlines that are close together.
|
||||
*/
|
||||
long BATCH_DELAY_MS = 1000;
|
||||
|
||||
/**
|
||||
* Registers a hook to be called when messages are due for cleanup.
|
||||
* This method should be called before
|
||||
* {@link LifecycleManager#startServices(SecretKey)}.
|
||||
*/
|
||||
void registerCleanupHook(ClientId c, int majorVersion,
|
||||
CleanupHook hook);
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
package org.briarproject.bramble.api.cleanup.event;
|
||||
|
||||
import org.briarproject.bramble.api.event.Event;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.bramble.api.sync.MessageId;
|
||||
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
|
||||
/**
|
||||
* An event that is broadcast when a message's cleanup timer is started.
|
||||
*/
|
||||
@Immutable
|
||||
@NotNullByDefault
|
||||
public class CleanupTimerStartedEvent extends Event {
|
||||
|
||||
private final MessageId messageId;
|
||||
private final long cleanupDeadline;
|
||||
|
||||
public CleanupTimerStartedEvent(MessageId messageId,
|
||||
long cleanupDeadline) {
|
||||
this.messageId = messageId;
|
||||
this.cleanupDeadline = cleanupDeadline;
|
||||
}
|
||||
|
||||
public MessageId getMessageId() {
|
||||
return messageId;
|
||||
}
|
||||
|
||||
public long getCleanupDeadline() {
|
||||
return cleanupDeadline;
|
||||
}
|
||||
}
|
||||
@@ -17,7 +17,6 @@ import org.briarproject.bramble.api.sync.Message;
|
||||
import org.briarproject.bramble.api.sync.MessageId;
|
||||
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
|
||||
@NotNullByDefault
|
||||
@@ -52,11 +51,9 @@ public interface ClientHelper {
|
||||
BdfDictionary getGroupMetadataAsDictionary(Transaction txn, GroupId g)
|
||||
throws DbException, FormatException;
|
||||
|
||||
Collection<MessageId> getMessageIds(Transaction txn, GroupId g,
|
||||
BdfDictionary query) throws DbException, FormatException;
|
||||
|
||||
BdfDictionary getMessageMetadataAsDictionary(MessageId m)
|
||||
throws DbException, FormatException;
|
||||
throws DbException,
|
||||
FormatException;
|
||||
|
||||
BdfDictionary getMessageMetadataAsDictionary(Transaction txn, MessageId m)
|
||||
throws DbException, FormatException;
|
||||
@@ -128,12 +125,12 @@ public interface ClientHelper {
|
||||
* group.
|
||||
*/
|
||||
ContactId getContactId(Transaction txn, GroupId contactGroupId)
|
||||
throws DbException, FormatException;
|
||||
throws DbException, FormatException;
|
||||
|
||||
/**
|
||||
* Stores the given contact ID in the group metadata of the given contact
|
||||
* group.
|
||||
*/
|
||||
void setContactId(Transaction txn, GroupId contactGroupId, ContactId c)
|
||||
throws DbException;
|
||||
throws DbException;
|
||||
}
|
||||
|
||||
@@ -19,10 +19,4 @@ public interface StreamDecrypterFactory {
|
||||
*/
|
||||
StreamDecrypter createContactExchangeStreamDecrypter(InputStream in,
|
||||
SecretKey headerKey);
|
||||
|
||||
/**
|
||||
* Creates a {@link StreamDecrypter} for decrypting a log stream.
|
||||
*/
|
||||
StreamDecrypter createLogStreamDecrypter(InputStream in,
|
||||
SecretKey headerKey);
|
||||
}
|
||||
|
||||
@@ -17,12 +17,6 @@ public interface StreamEncrypterFactory {
|
||||
* Creates a {@link StreamEncrypter} for encrypting a contact exchange
|
||||
* stream.
|
||||
*/
|
||||
StreamEncrypter createContactExchangeStreamEncrypter(OutputStream out,
|
||||
SecretKey headerKey);
|
||||
|
||||
/**
|
||||
* Creates a {@link StreamEncrypter} for encrypting a log stream.
|
||||
*/
|
||||
StreamEncrypter createLogStreamEncrypter(OutputStream out,
|
||||
StreamEncrypter createContactExchangeStreamDecrypter(OutputStream out,
|
||||
SecretKey headerKey);
|
||||
}
|
||||
|
||||
@@ -41,18 +41,6 @@ import javax.annotation.Nullable;
|
||||
@NotNullByDefault
|
||||
public interface DatabaseComponent extends TransactionManager {
|
||||
|
||||
/**
|
||||
* Return value for {@link #getNextCleanupDeadline(Transaction)} if
|
||||
* no messages are scheduled to be deleted.
|
||||
*/
|
||||
long NO_CLEANUP_DEADLINE = -1;
|
||||
|
||||
/**
|
||||
* Return value for {@link #startCleanupTimer(Transaction, MessageId)}
|
||||
* if the cleanup timer was not started.
|
||||
*/
|
||||
long TIMER_NOT_STARTED = -1;
|
||||
|
||||
/**
|
||||
* Opens the database and returns true if the database already existed.
|
||||
*
|
||||
@@ -300,16 +288,6 @@ public interface DatabaseComponent extends TransactionManager {
|
||||
Collection<MessageId> getMessageIds(Transaction txn, GroupId g)
|
||||
throws DbException;
|
||||
|
||||
/**
|
||||
* Returns the IDs of any delivered messages in the given group with
|
||||
* metadata that matches all entries in the given query. If the query is
|
||||
* empty, the IDs of all delivered messages are returned.
|
||||
* <p/>
|
||||
* Read-only.
|
||||
*/
|
||||
Collection<MessageId> getMessageIds(Transaction txn, GroupId g,
|
||||
Metadata query) throws DbException;
|
||||
|
||||
/**
|
||||
* Returns the IDs of any messages that need to be validated.
|
||||
* <p/>
|
||||
@@ -336,15 +314,6 @@ public interface DatabaseComponent extends TransactionManager {
|
||||
Collection<MessageId> getMessagesToShare(Transaction txn)
|
||||
throws DbException;
|
||||
|
||||
/**
|
||||
* Returns the IDs of any messages of any messages that are due for
|
||||
* deletion, along with their group IDs.
|
||||
* <p/>
|
||||
* Read-only.
|
||||
*/
|
||||
Map<GroupId, Collection<MessageId>> getMessagesToDelete(Transaction txn)
|
||||
throws DbException;
|
||||
|
||||
/**
|
||||
* Returns the metadata for all delivered messages in the given group.
|
||||
* <p/>
|
||||
@@ -426,15 +395,6 @@ public interface DatabaseComponent extends TransactionManager {
|
||||
MessageStatus getMessageStatus(Transaction txn, ContactId c, MessageId m)
|
||||
throws DbException;
|
||||
|
||||
/**
|
||||
* Returns the next time (in milliseconds since the Unix epoch) when a
|
||||
* message is due to be deleted, or {@link #NO_CLEANUP_DEADLINE}
|
||||
* if no messages are scheduled to be deleted.
|
||||
* <p/>
|
||||
* Read-only.
|
||||
*/
|
||||
long getNextCleanupDeadline(Transaction txn) throws DbException;
|
||||
|
||||
/*
|
||||
* Returns the next time (in milliseconds since the Unix epoch) when a
|
||||
* message is due to be sent to the given contact. The returned value may
|
||||
@@ -575,13 +535,6 @@ public interface DatabaseComponent extends TransactionManager {
|
||||
void removeTransportKeys(Transaction txn, TransportId t, KeySetId k)
|
||||
throws DbException;
|
||||
|
||||
/**
|
||||
* Sets the cleanup timer duration for the given message. This does not
|
||||
* start the message's cleanup timer.
|
||||
*/
|
||||
void setCleanupTimerDuration(Transaction txn, MessageId m, long duration)
|
||||
throws DbException;
|
||||
|
||||
/**
|
||||
* Marks the given contact as verified.
|
||||
*/
|
||||
@@ -604,12 +557,6 @@ public interface DatabaseComponent extends TransactionManager {
|
||||
*/
|
||||
void setMessagePermanent(Transaction txn, MessageId m) throws DbException;
|
||||
|
||||
/**
|
||||
* Marks the given message as not shared. This method is only meant for
|
||||
* testing.
|
||||
*/
|
||||
void setMessageNotShared(Transaction txn, MessageId m) throws DbException;
|
||||
|
||||
/**
|
||||
* Marks the given message as shared.
|
||||
*/
|
||||
@@ -652,22 +599,6 @@ public interface DatabaseComponent extends TransactionManager {
|
||||
void setTransportKeysActive(Transaction txn, TransportId t, KeySetId k)
|
||||
throws DbException;
|
||||
|
||||
/**
|
||||
* Starts the cleanup timer for the given message, if a timer duration
|
||||
* has been set and the timer has not already been started.
|
||||
*
|
||||
* @return The cleanup deadline, or {@link #TIMER_NOT_STARTED} if no
|
||||
* timer duration has been set for this message or its timer has already
|
||||
* been started.
|
||||
*/
|
||||
long startCleanupTimer(Transaction txn, MessageId m) throws DbException;
|
||||
|
||||
/**
|
||||
* Stops the cleanup timer for the given message, if the timer has been
|
||||
* started.
|
||||
*/
|
||||
void stopCleanupTimer(Transaction txn, MessageId m) throws DbException;
|
||||
|
||||
/**
|
||||
* Stores the given transport keys, deleting any keys they have replaced.
|
||||
*/
|
||||
|
||||
@@ -13,6 +13,4 @@ public interface DevConfig {
|
||||
String getDevOnionAddress();
|
||||
|
||||
File getReportDir();
|
||||
|
||||
File getLogcatFile();
|
||||
}
|
||||
|
||||
@@ -16,13 +16,8 @@ public interface StreamReaderFactory {
|
||||
|
||||
/**
|
||||
* Creates an {@link InputStream InputStream} for reading from a contact
|
||||
* exchange stream.
|
||||
* exchangestream.
|
||||
*/
|
||||
InputStream createContactExchangeStreamReader(InputStream in,
|
||||
SecretKey headerKey);
|
||||
|
||||
/**
|
||||
* Creates an {@link InputStream} for reading from a log stream.
|
||||
*/
|
||||
InputStream createLogStreamReader(InputStream in, SecretKey headerKey);
|
||||
}
|
||||
|
||||
@@ -7,19 +7,17 @@ import java.io.OutputStream;
|
||||
|
||||
@NotNullByDefault
|
||||
public interface StreamWriterFactory {
|
||||
|
||||
/**
|
||||
* Creates a {@link StreamWriter} for writing to a transport stream.
|
||||
* Creates an {@link OutputStream OutputStream} for writing to a
|
||||
* transport stream
|
||||
*/
|
||||
StreamWriter createStreamWriter(OutputStream out, StreamContext ctx);
|
||||
|
||||
/**
|
||||
* Creates a {@link StreamWriter} for writing to a contact exchange stream.
|
||||
* Creates an {@link OutputStream OutputStream} for writing to a contact
|
||||
* exchange stream.
|
||||
*/
|
||||
StreamWriter createContactExchangeStreamWriter(OutputStream out,
|
||||
SecretKey headerKey);
|
||||
|
||||
/**
|
||||
* Creates a {@link StreamWriter} for writing to a log stream.
|
||||
*/
|
||||
StreamWriter createLogStreamWriter(OutputStream out, SecretKey headerKey);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package org.briarproject.bramble;
|
||||
|
||||
import org.briarproject.bramble.cleanup.CleanupModule;
|
||||
import org.briarproject.bramble.contact.ContactModule;
|
||||
import org.briarproject.bramble.crypto.CryptoExecutorModule;
|
||||
import org.briarproject.bramble.db.DatabaseExecutorModule;
|
||||
@@ -15,8 +14,6 @@ import org.briarproject.bramble.versioning.VersioningModule;
|
||||
|
||||
public interface BrambleCoreEagerSingletons {
|
||||
|
||||
void inject(CleanupModule.EagerSingletons init);
|
||||
|
||||
void inject(ContactModule.EagerSingletons init);
|
||||
|
||||
void inject(CryptoExecutorModule.EagerSingletons init);
|
||||
@@ -42,7 +39,6 @@ public interface BrambleCoreEagerSingletons {
|
||||
class Helper {
|
||||
|
||||
public static void injectEagerSingletons(BrambleCoreEagerSingletons c) {
|
||||
c.inject(new CleanupModule.EagerSingletons());
|
||||
c.inject(new ContactModule.EagerSingletons());
|
||||
c.inject(new CryptoExecutorModule.EagerSingletons());
|
||||
c.inject(new DatabaseExecutorModule.EagerSingletons());
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package org.briarproject.bramble;
|
||||
|
||||
import org.briarproject.bramble.cleanup.CleanupModule;
|
||||
import org.briarproject.bramble.client.ClientModule;
|
||||
import org.briarproject.bramble.connection.ConnectionModule;
|
||||
import org.briarproject.bramble.contact.ContactModule;
|
||||
@@ -28,7 +27,6 @@ import org.briarproject.bramble.versioning.VersioningModule;
|
||||
import dagger.Module;
|
||||
|
||||
@Module(includes = {
|
||||
CleanupModule.class,
|
||||
ClientModule.class,
|
||||
ConnectionModule.class,
|
||||
ContactModule.class,
|
||||
|
||||
@@ -1,159 +0,0 @@
|
||||
package org.briarproject.bramble.cleanup;
|
||||
|
||||
import org.briarproject.bramble.api.cleanup.CleanupHook;
|
||||
import org.briarproject.bramble.api.cleanup.CleanupManager;
|
||||
import org.briarproject.bramble.api.cleanup.event.CleanupTimerStartedEvent;
|
||||
import org.briarproject.bramble.api.db.DatabaseComponent;
|
||||
import org.briarproject.bramble.api.db.DatabaseExecutor;
|
||||
import org.briarproject.bramble.api.db.DbException;
|
||||
import org.briarproject.bramble.api.db.Transaction;
|
||||
import org.briarproject.bramble.api.event.Event;
|
||||
import org.briarproject.bramble.api.event.EventListener;
|
||||
import org.briarproject.bramble.api.lifecycle.Service;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.bramble.api.sync.ClientId;
|
||||
import org.briarproject.bramble.api.sync.Group;
|
||||
import org.briarproject.bramble.api.sync.GroupId;
|
||||
import org.briarproject.bramble.api.sync.MessageId;
|
||||
import org.briarproject.bramble.api.system.Clock;
|
||||
import org.briarproject.bramble.api.system.TaskScheduler;
|
||||
import org.briarproject.bramble.api.versioning.ClientMajorVersion;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.annotation.concurrent.GuardedBy;
|
||||
import javax.annotation.concurrent.ThreadSafe;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static java.lang.Math.max;
|
||||
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
||||
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.util.LogUtils.logException;
|
||||
|
||||
@ThreadSafe
|
||||
@NotNullByDefault
|
||||
class CleanupManagerImpl implements CleanupManager, Service, EventListener {
|
||||
|
||||
private static final Logger LOG =
|
||||
getLogger(CleanupManagerImpl.class.getName());
|
||||
|
||||
private final Executor dbExecutor;
|
||||
private final DatabaseComponent db;
|
||||
private final TaskScheduler taskScheduler;
|
||||
private final Clock clock;
|
||||
private final Map<ClientMajorVersion, CleanupHook> hooks =
|
||||
new ConcurrentHashMap<>();
|
||||
private final Object lock = new Object();
|
||||
|
||||
@GuardedBy("lock")
|
||||
private final Set<CleanupTask> pending = new HashSet<>();
|
||||
|
||||
@Inject
|
||||
CleanupManagerImpl(@DatabaseExecutor Executor dbExecutor,
|
||||
DatabaseComponent db, TaskScheduler taskScheduler, Clock clock) {
|
||||
this.dbExecutor = dbExecutor;
|
||||
this.db = db;
|
||||
this.taskScheduler = taskScheduler;
|
||||
this.clock = clock;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerCleanupHook(ClientId c, int majorVersion,
|
||||
CleanupHook hook) {
|
||||
hooks.put(new ClientMajorVersion(c, majorVersion), hook);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startService() {
|
||||
maybeScheduleTask(clock.currentTimeMillis());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stopService() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void eventOccurred(Event e) {
|
||||
if (e instanceof CleanupTimerStartedEvent) {
|
||||
CleanupTimerStartedEvent a = (CleanupTimerStartedEvent) e;
|
||||
maybeScheduleTask(a.getCleanupDeadline());
|
||||
}
|
||||
}
|
||||
|
||||
private void maybeScheduleTask(long deadline) {
|
||||
synchronized (lock) {
|
||||
for (CleanupTask task : pending) {
|
||||
if (task.deadline <= deadline) return;
|
||||
}
|
||||
CleanupTask task = new CleanupTask(deadline);
|
||||
pending.add(task);
|
||||
scheduleTask(task);
|
||||
}
|
||||
}
|
||||
|
||||
private void scheduleTask(CleanupTask task) {
|
||||
long now = clock.currentTimeMillis();
|
||||
long delay = max(0, task.deadline - now + BATCH_DELAY_MS);
|
||||
if (LOG.isLoggable(INFO)) {
|
||||
LOG.info("Scheduling cleanup task in " + delay + " ms");
|
||||
}
|
||||
taskScheduler.schedule(() -> deleteMessagesAndScheduleNextTask(task),
|
||||
dbExecutor, delay, MILLISECONDS);
|
||||
}
|
||||
|
||||
private void deleteMessagesAndScheduleNextTask(CleanupTask task) {
|
||||
try {
|
||||
synchronized (lock) {
|
||||
pending.remove(task);
|
||||
}
|
||||
long deadline = db.transactionWithResult(false, txn -> {
|
||||
deleteMessages(txn);
|
||||
return db.getNextCleanupDeadline(txn);
|
||||
});
|
||||
if (deadline != NO_CLEANUP_DEADLINE) {
|
||||
maybeScheduleTask(deadline);
|
||||
}
|
||||
} catch (DbException e) {
|
||||
logException(LOG, WARNING, e);
|
||||
}
|
||||
}
|
||||
|
||||
private void deleteMessages(Transaction txn) throws DbException {
|
||||
Map<GroupId, Collection<MessageId>> ids = db.getMessagesToDelete(txn);
|
||||
for (Entry<GroupId, Collection<MessageId>> e : ids.entrySet()) {
|
||||
GroupId groupId = e.getKey();
|
||||
Collection<MessageId> messageIds = e.getValue();
|
||||
if (LOG.isLoggable(INFO)) {
|
||||
LOG.info(messageIds.size() + " messages to delete");
|
||||
}
|
||||
for (MessageId m : messageIds) db.stopCleanupTimer(txn, m);
|
||||
Group group = db.getGroup(txn, groupId);
|
||||
ClientMajorVersion cv = new ClientMajorVersion(group.getClientId(),
|
||||
group.getMajorVersion());
|
||||
CleanupHook hook = hooks.get(cv);
|
||||
if (hook == null) {
|
||||
throw new IllegalStateException("No cleanup hook for " + cv);
|
||||
}
|
||||
hook.deleteMessages(txn, groupId, messageIds);
|
||||
}
|
||||
}
|
||||
|
||||
private static class CleanupTask {
|
||||
|
||||
private final long deadline;
|
||||
|
||||
private CleanupTask(long deadline) {
|
||||
this.deadline = deadline;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
package org.briarproject.bramble.cleanup;
|
||||
|
||||
import org.briarproject.bramble.api.cleanup.CleanupManager;
|
||||
import org.briarproject.bramble.api.event.EventBus;
|
||||
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
|
||||
@Module
|
||||
public class CleanupModule {
|
||||
|
||||
public static class EagerSingletons {
|
||||
@Inject
|
||||
CleanupManager cleanupManager;
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
CleanupManager provideCleanupManager(LifecycleManager lifecycleManager,
|
||||
EventBus eventBus, CleanupManagerImpl cleanupManager) {
|
||||
lifecycleManager.registerService(cleanupManager);
|
||||
eventBus.addListener(cleanupManager);
|
||||
return cleanupManager;
|
||||
}
|
||||
}
|
||||
@@ -34,7 +34,6 @@ import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
@@ -155,12 +154,6 @@ class ClientHelperImpl implements ClientHelper {
|
||||
return metadataParser.parse(metadata);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<MessageId> getMessageIds(Transaction txn, GroupId g,
|
||||
BdfDictionary query) throws DbException, FormatException {
|
||||
return db.getMessageIds(txn, g, metadataEncoder.encode(query));
|
||||
}
|
||||
|
||||
@Override
|
||||
public BdfDictionary getMessageMetadataAsDictionary(MessageId m)
|
||||
throws DbException, FormatException {
|
||||
|
||||
@@ -9,16 +9,14 @@ import java.util.logging.Logger;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static java.lang.Math.min;
|
||||
import static java.util.logging.Level.INFO;
|
||||
import static java.util.logging.Logger.getLogger;
|
||||
import static org.briarproject.bramble.util.LogUtils.logDuration;
|
||||
import static org.briarproject.bramble.util.LogUtils.now;
|
||||
|
||||
class ScryptKdf implements PasswordBasedKdf {
|
||||
|
||||
private static final Logger LOG =
|
||||
getLogger(ScryptKdf.class.getName());
|
||||
Logger.getLogger(ScryptKdf.class.getName());
|
||||
|
||||
private static final int MIN_COST = 256; // Min parameter N
|
||||
private static final int MAX_COST = 1024 * 1024; // Max parameter N
|
||||
@@ -35,20 +33,10 @@ class ScryptKdf implements PasswordBasedKdf {
|
||||
|
||||
@Override
|
||||
public int chooseCostParameter() {
|
||||
// Scrypt uses at least 128 * N * r bytes of memory. Don't use more
|
||||
// than half of the JVM's max heap size or we may run out of memory.
|
||||
// https://blog.filippo.io/the-scrypt-parameters/
|
||||
long maxMemory = Runtime.getRuntime().maxMemory();
|
||||
long maxCost = min(MAX_COST, maxMemory / BLOCK_SIZE / 256);
|
||||
if (LOG.isLoggable(INFO) && maxCost < MAX_COST) {
|
||||
LOG.info("Max cost capped at " + maxCost
|
||||
+ " due to max heap size " + maxMemory);
|
||||
}
|
||||
// Increase the cost from min to max while measuring performance
|
||||
int cost = MIN_COST;
|
||||
while (cost * 2 <= maxCost && measureDuration(cost) * 2 <= TARGET_MS) {
|
||||
while (cost * 2 <= MAX_COST && measureDuration(cost) * 2 <= TARGET_MS)
|
||||
cost *= 2;
|
||||
}
|
||||
if (LOG.isLoggable(INFO))
|
||||
LOG.info("KDF cost parameter " + cost);
|
||||
return cost;
|
||||
|
||||
@@ -36,10 +36,4 @@ class StreamDecrypterFactoryImpl implements StreamDecrypterFactory {
|
||||
SecretKey headerKey) {
|
||||
return new StreamDecrypterImpl(in, cipherProvider.get(), 0, headerKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public StreamDecrypter createLogStreamDecrypter(InputStream in,
|
||||
SecretKey headerKey) {
|
||||
return createContactExchangeStreamDecrypter(in, headerKey);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ class StreamEncrypterFactoryImpl implements StreamEncrypterFactory {
|
||||
}
|
||||
|
||||
@Override
|
||||
public StreamEncrypter createContactExchangeStreamEncrypter(
|
||||
public StreamEncrypter createContactExchangeStreamDecrypter(
|
||||
OutputStream out, SecretKey headerKey) {
|
||||
AuthenticatedCipher cipher = cipherProvider.get();
|
||||
byte[] streamHeaderNonce = new byte[STREAM_HEADER_NONCE_LENGTH];
|
||||
@@ -60,10 +60,4 @@ class StreamEncrypterFactoryImpl implements StreamEncrypterFactory {
|
||||
return new StreamEncrypterImpl(out, cipher, 0, null, streamHeaderNonce,
|
||||
headerKey, frameKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public StreamEncrypter createLogStreamEncrypter(OutputStream out,
|
||||
SecretKey headerKey) {
|
||||
return createContactExchangeStreamEncrypter(out, headerKey);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -497,25 +497,6 @@ interface Database<T> {
|
||||
*/
|
||||
Collection<MessageId> getMessagesToShare(T txn) throws DbException;
|
||||
|
||||
/**
|
||||
* Returns the IDs of any messages of any messages that are due for
|
||||
* deletion, along with their group IDs.
|
||||
* <p/>
|
||||
* Read-only.
|
||||
*/
|
||||
Map<GroupId, Collection<MessageId>> getMessagesToDelete(T txn)
|
||||
throws DbException;
|
||||
|
||||
/**
|
||||
* Returns the next time (in milliseconds since the Unix epoch) when a
|
||||
* message is due to be deleted, or
|
||||
* {@link DatabaseComponent#NO_CLEANUP_DEADLINE} if no messages are
|
||||
* scheduled to be deleted.
|
||||
* <p/>
|
||||
* Read-only.
|
||||
*/
|
||||
long getNextCleanupDeadline(T txn) throws DbException;
|
||||
|
||||
/**
|
||||
* Returns the next time (in milliseconds since the Unix epoch) when a
|
||||
* message is due to be sent to the given contact. The returned value may
|
||||
@@ -625,10 +606,8 @@ interface Database<T> {
|
||||
|
||||
/**
|
||||
* Marks a message as having been seen by the given contact.
|
||||
*
|
||||
* @return True if the message was not already marked as seen.
|
||||
*/
|
||||
boolean raiseSeenFlag(T txn, ContactId c, MessageId m) throws DbException;
|
||||
void raiseSeenFlag(T txn, ContactId c, MessageId m) throws DbException;
|
||||
|
||||
/**
|
||||
* Removes a contact from the database.
|
||||
@@ -692,13 +671,6 @@ interface Database<T> {
|
||||
*/
|
||||
void resetExpiryTime(T txn, ContactId c, MessageId m) throws DbException;
|
||||
|
||||
/**
|
||||
* Sets the cleanup timer duration for the given message. This does not
|
||||
* start the message's cleanup timer.
|
||||
*/
|
||||
void setCleanupTimerDuration(T txn, MessageId m, long duration)
|
||||
throws DbException;
|
||||
|
||||
/**
|
||||
* Marks the given contact as verified.
|
||||
*/
|
||||
@@ -729,10 +701,9 @@ interface Database<T> {
|
||||
void setMessagePermanent(T txn, MessageId m) throws DbException;
|
||||
|
||||
/**
|
||||
* Marks the given message as shared or not.
|
||||
* Marks the given message as shared.
|
||||
*/
|
||||
void setMessageShared(T txn, MessageId m, boolean shared)
|
||||
throws DbException;
|
||||
void setMessageShared(T txn, MessageId m) throws DbException;
|
||||
|
||||
/**
|
||||
* Sets the validation and delivery state of the given message.
|
||||
@@ -759,22 +730,6 @@ interface Database<T> {
|
||||
void setTransportKeysActive(T txn, TransportId t, KeySetId k)
|
||||
throws DbException;
|
||||
|
||||
/**
|
||||
* Starts the cleanup timer for the given message, if a timer duration
|
||||
* has been set and the timer has not already been started.
|
||||
*
|
||||
* @return The cleanup deadline, or
|
||||
* {@link DatabaseComponent#TIMER_NOT_STARTED} if no timer duration has
|
||||
* been set for this message or its timer has already been started.
|
||||
*/
|
||||
long startCleanupTimer(T txn, MessageId m) throws DbException;
|
||||
|
||||
/**
|
||||
* Stops the cleanup timer for the given message, if the timer has been
|
||||
* started.
|
||||
*/
|
||||
void stopCleanupTimer(T txn, MessageId m) throws DbException;
|
||||
|
||||
/**
|
||||
* Updates the transmission count, expiry time and estimated time of arrival
|
||||
* of the given message with respect to the given contact, using the latency
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package org.briarproject.bramble.db;
|
||||
|
||||
import org.briarproject.bramble.api.cleanup.event.CleanupTimerStartedEvent;
|
||||
import org.briarproject.bramble.api.contact.Contact;
|
||||
import org.briarproject.bramble.api.contact.ContactId;
|
||||
import org.briarproject.bramble.api.contact.PendingContact;
|
||||
@@ -577,15 +576,6 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
||||
return db.getMessageIds(txn, g);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<MessageId> getMessageIds(Transaction transaction,
|
||||
GroupId g, Metadata query) throws DbException {
|
||||
T txn = unbox(transaction);
|
||||
if (!db.containsGroup(txn, g))
|
||||
throw new NoSuchGroupException();
|
||||
return db.getMessageIds(txn, g, query);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<MessageId> getMessagesToValidate(Transaction transaction)
|
||||
throws DbException {
|
||||
@@ -607,13 +597,6 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
||||
return db.getMessagesToShare(txn);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<GroupId, Collection<MessageId>> getMessagesToDelete(
|
||||
Transaction transaction) throws DbException {
|
||||
T txn = unbox(transaction);
|
||||
return db.getMessagesToDelete(txn);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<MessageId, Metadata> getMessageMetadata(Transaction transaction,
|
||||
GroupId g) throws DbException {
|
||||
@@ -709,13 +692,6 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
||||
return db.getMessageDependents(txn, m);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getNextCleanupDeadline(Transaction transaction)
|
||||
throws DbException {
|
||||
T txn = unbox(transaction);
|
||||
return db.getNextCleanupDeadline(txn);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getNextSendTime(Transaction transaction, ContactId c)
|
||||
throws DbException {
|
||||
@@ -819,17 +795,8 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
||||
Collection<MessageId> acked = new ArrayList<>();
|
||||
for (MessageId m : a.getMessageIds()) {
|
||||
if (db.containsVisibleMessage(txn, c, m)) {
|
||||
if (db.raiseSeenFlag(txn, c, m)) {
|
||||
// This is the first time the message has been acked by
|
||||
// this contact. Start the cleanup timer (a no-op unless
|
||||
// a cleanup deadline has been set for this message)
|
||||
long deadline = db.startCleanupTimer(txn, m);
|
||||
if (deadline != TIMER_NOT_STARTED) {
|
||||
transaction.attach(new CleanupTimerStartedEvent(m,
|
||||
deadline));
|
||||
}
|
||||
acked.add(m);
|
||||
}
|
||||
db.raiseSeenFlag(txn, c, m);
|
||||
acked.add(m);
|
||||
}
|
||||
}
|
||||
if (acked.size() > 0) {
|
||||
@@ -985,16 +952,6 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
||||
db.removeTransportKeys(txn, t, k);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCleanupTimerDuration(Transaction transaction, MessageId m,
|
||||
long duration) throws DbException {
|
||||
if (transaction.isReadOnly()) throw new IllegalArgumentException();
|
||||
T txn = unbox(transaction);
|
||||
if (!db.containsMessage(txn, m))
|
||||
throw new NoSuchMessageException();
|
||||
db.setCleanupTimerDuration(txn, m, duration);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setContactVerified(Transaction transaction, ContactId c)
|
||||
throws DbException {
|
||||
@@ -1044,16 +1001,6 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
||||
db.setMessagePermanent(txn, m);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMessageNotShared(Transaction transaction, MessageId m)
|
||||
throws DbException {
|
||||
if (transaction.isReadOnly()) throw new IllegalArgumentException();
|
||||
T txn = unbox(transaction);
|
||||
if (!db.containsMessage(txn, m))
|
||||
throw new NoSuchMessageException();
|
||||
db.setMessageShared(txn, m, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMessageShared(Transaction transaction, MessageId m)
|
||||
throws DbException {
|
||||
@@ -1063,7 +1010,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
||||
throw new NoSuchMessageException();
|
||||
if (db.getMessageState(txn, m) != DELIVERED)
|
||||
throw new IllegalArgumentException("Shared undelivered message");
|
||||
db.setMessageShared(txn, m, true);
|
||||
db.setMessageShared(txn, m);
|
||||
transaction.attach(new MessageSharedEvent(m));
|
||||
}
|
||||
|
||||
@@ -1135,30 +1082,6 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
||||
db.setTransportKeysActive(txn, t, k);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long startCleanupTimer(Transaction transaction, MessageId m)
|
||||
throws DbException {
|
||||
if (transaction.isReadOnly()) throw new IllegalArgumentException();
|
||||
T txn = unbox(transaction);
|
||||
if (!db.containsMessage(txn, m))
|
||||
throw new NoSuchMessageException();
|
||||
long deadline = db.startCleanupTimer(txn, m);
|
||||
if (deadline != TIMER_NOT_STARTED) {
|
||||
transaction.attach(new CleanupTimerStartedEvent(m, deadline));
|
||||
}
|
||||
return deadline;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stopCleanupTimer(Transaction transaction, MessageId m)
|
||||
throws DbException {
|
||||
if (transaction.isReadOnly()) throw new IllegalArgumentException();
|
||||
T txn = unbox(transaction);
|
||||
if (!db.containsMessage(txn, m))
|
||||
throw new NoSuchMessageException();
|
||||
db.stopCleanupTimer(txn, m);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateTransportKeys(Transaction transaction,
|
||||
Collection<TransportKeySet> keys) throws DbException {
|
||||
|
||||
@@ -72,8 +72,6 @@ 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;
|
||||
@@ -100,7 +98,7 @@ import static org.briarproject.bramble.util.LogUtils.now;
|
||||
abstract class JdbcDatabase implements Database<Connection> {
|
||||
|
||||
// Package access for testing
|
||||
static final int CODE_SCHEMA_VERSION = 48;
|
||||
static final int CODE_SCHEMA_VERSION = 47;
|
||||
|
||||
// Time period offsets for incoming transport keys
|
||||
private static final int OFFSET_PREV = -1;
|
||||
@@ -182,11 +180,6 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
+ " state INT NOT NULL,"
|
||||
+ " shared BOOLEAN NOT NULL,"
|
||||
+ " temporary BOOLEAN NOT NULL,"
|
||||
// Null if no timer duration has been set
|
||||
+ " cleanupTimerDuration BIGINT,"
|
||||
// Null if no timer duration has been set or the timer
|
||||
// hasn't started
|
||||
+ " cleanupDeadline BIGINT,"
|
||||
+ " length INT NOT NULL,"
|
||||
+ " raw BLOB," // Null if message has been deleted
|
||||
+ " PRIMARY KEY (messageId),"
|
||||
@@ -343,10 +336,6 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
"CREATE INDEX IF NOT EXISTS statusesByContactIdTimestamp"
|
||||
+ " ON statuses (contactId, timestamp)";
|
||||
|
||||
private static final String INDEX_MESSAGES_BY_CLEANUP_DEADLINE =
|
||||
"CREATE INDEX IF NOT EXISTS messagesByCleanupDeadline"
|
||||
+ " ON messages (cleanupDeadline)";
|
||||
|
||||
private static final Logger LOG =
|
||||
getLogger(JdbcDatabase.class.getName());
|
||||
|
||||
@@ -474,8 +463,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
new Migration43_44(dbTypes),
|
||||
new Migration44_45(),
|
||||
new Migration45_46(),
|
||||
new Migration46_47(dbTypes),
|
||||
new Migration47_48()
|
||||
new Migration46_47(dbTypes)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -543,7 +531,6 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
s.executeUpdate(INDEX_MESSAGE_DEPENDENCIES_BY_DEPENDENCY_ID);
|
||||
s.executeUpdate(INDEX_STATUSES_BY_CONTACT_ID_GROUP_ID);
|
||||
s.executeUpdate(INDEX_STATUSES_BY_CONTACT_ID_TIMESTAMP);
|
||||
s.executeUpdate(INDEX_MESSAGES_BY_CLEANUP_DEADLINE);
|
||||
s.close();
|
||||
} catch (SQLException e) {
|
||||
tryToClose(s, LOG, WARNING);
|
||||
@@ -1303,9 +1290,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
public void deleteMessage(Connection txn, MessageId m) throws DbException {
|
||||
PreparedStatement ps = null;
|
||||
try {
|
||||
String sql = "UPDATE messages"
|
||||
+ " SET raw = NULL, cleanupDeadline = NULL"
|
||||
+ " WHERE messageId = ?";
|
||||
String sql = "UPDATE messages SET raw = NULL WHERE messageId = ?";
|
||||
ps = txn.prepareStatement(sql);
|
||||
ps.setBytes(1, m.getBytes());
|
||||
int affected = ps.executeUpdate();
|
||||
@@ -1784,6 +1769,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
// Return early if there are no matches
|
||||
if (intersection.isEmpty()) return Collections.emptySet();
|
||||
}
|
||||
if (intersection == null) throw new AssertionError();
|
||||
return intersection;
|
||||
} catch (SQLException e) {
|
||||
tryToClose(rs, LOG, WARNING);
|
||||
@@ -2240,39 +2226,6 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<GroupId, Collection<MessageId>> getMessagesToDelete(
|
||||
Connection txn) throws DbException {
|
||||
long now = clock.currentTimeMillis();
|
||||
PreparedStatement ps = null;
|
||||
ResultSet rs = null;
|
||||
try {
|
||||
String sql = "SELECT messageId, groupId FROM messages"
|
||||
+ " WHERE cleanupDeadline <= ?";
|
||||
ps = txn.prepareStatement(sql);
|
||||
ps.setLong(1, now);
|
||||
rs = ps.executeQuery();
|
||||
Map<GroupId, Collection<MessageId>> ids = new HashMap<>();
|
||||
while (rs.next()) {
|
||||
MessageId m = new MessageId(rs.getBytes(1));
|
||||
GroupId g = new GroupId(rs.getBytes(2));
|
||||
Collection<MessageId> messageIds = ids.get(g);
|
||||
if (messageIds == null) {
|
||||
messageIds = new ArrayList<>();
|
||||
ids.put(g, messageIds);
|
||||
}
|
||||
messageIds.add(m);
|
||||
}
|
||||
rs.close();
|
||||
ps.close();
|
||||
return ids;
|
||||
} catch (SQLException e) {
|
||||
tryToClose(rs, LOG, WARNING);
|
||||
tryToClose(ps, LOG, WARNING);
|
||||
throw new DbException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getNextSendTime(Connection txn, ContactId c)
|
||||
throws DbException {
|
||||
@@ -2303,31 +2256,6 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getNextCleanupDeadline(Connection txn) throws DbException {
|
||||
Statement s = null;
|
||||
ResultSet rs = null;
|
||||
try {
|
||||
String sql = "SELECT cleanupDeadline FROM messages"
|
||||
+ " WHERE cleanupDeadline IS NOT NULL"
|
||||
+ " ORDER BY cleanupDeadline LIMIT 1";
|
||||
s = txn.createStatement();
|
||||
rs = s.executeQuery(sql);
|
||||
long nextDeadline = NO_CLEANUP_DEADLINE;
|
||||
if (rs.next()) {
|
||||
nextDeadline = rs.getLong(1);
|
||||
if (rs.next()) throw new AssertionError();
|
||||
}
|
||||
rs.close();
|
||||
s.close();
|
||||
return nextDeadline;
|
||||
} catch (SQLException e) {
|
||||
tryToClose(rs, LOG, WARNING);
|
||||
tryToClose(s, LOG, WARNING);
|
||||
throw new DbException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public PendingContact getPendingContact(Connection txn, PendingContactId p)
|
||||
throws DbException {
|
||||
@@ -2848,7 +2776,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean raiseSeenFlag(Connection txn, ContactId c, MessageId m)
|
||||
public void raiseSeenFlag(Connection txn, ContactId c, MessageId m)
|
||||
throws DbException {
|
||||
PreparedStatement ps = null;
|
||||
try {
|
||||
@@ -2860,7 +2788,6 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
int affected = ps.executeUpdate();
|
||||
if (affected < 0 || affected > 1) throw new DbStateException();
|
||||
ps.close();
|
||||
return affected == 1;
|
||||
} catch (SQLException e) {
|
||||
tryToClose(ps, LOG, WARNING);
|
||||
throw new DbException(e);
|
||||
@@ -3094,25 +3021,6 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCleanupTimerDuration(Connection txn, MessageId m,
|
||||
long duration) throws DbException {
|
||||
PreparedStatement ps = null;
|
||||
try {
|
||||
String sql = "UPDATE messages SET cleanupTimerDuration = ?"
|
||||
+ " WHERE messageId = ? AND cleanupTimerDuration IS NULL";
|
||||
ps = txn.prepareStatement(sql);
|
||||
ps.setLong(1, duration);
|
||||
ps.setBytes(2, m.getBytes());
|
||||
int affected = ps.executeUpdate();
|
||||
if (affected < 0 || affected > 1) throw new DbStateException();
|
||||
ps.close();
|
||||
} catch (SQLException e) {
|
||||
tryToClose(ps, LOG, WARNING);
|
||||
throw new DbException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setContactVerified(Connection txn, ContactId c)
|
||||
throws DbException {
|
||||
@@ -3220,24 +3128,22 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMessageShared(Connection txn, MessageId m, boolean shared)
|
||||
public void setMessageShared(Connection txn, MessageId m)
|
||||
throws DbException {
|
||||
PreparedStatement ps = null;
|
||||
try {
|
||||
String sql = "UPDATE messages SET shared = ?"
|
||||
String sql = "UPDATE messages SET shared = TRUE"
|
||||
+ " WHERE messageId = ?";
|
||||
ps = txn.prepareStatement(sql);
|
||||
ps.setBoolean(1, shared);
|
||||
ps.setBytes(2, m.getBytes());
|
||||
ps.setBytes(1, m.getBytes());
|
||||
int affected = ps.executeUpdate();
|
||||
if (affected < 0 || affected > 1) throw new DbStateException();
|
||||
ps.close();
|
||||
// Update denormalised column in statuses
|
||||
sql = "UPDATE statuses SET messageShared = ?"
|
||||
sql = "UPDATE statuses SET messageShared = TRUE"
|
||||
+ " WHERE messageId = ?";
|
||||
ps = txn.prepareStatement(sql);
|
||||
ps.setBoolean(1, shared);
|
||||
ps.setBytes(2, m.getBytes());
|
||||
ps.setBytes(1, m.getBytes());
|
||||
affected = ps.executeUpdate();
|
||||
if (affected < 0) throw new DbStateException();
|
||||
ps.close();
|
||||
@@ -3366,60 +3272,6 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public long startCleanupTimer(Connection txn, MessageId m)
|
||||
throws DbException {
|
||||
long now = clock.currentTimeMillis();
|
||||
PreparedStatement ps = null;
|
||||
ResultSet rs = null;
|
||||
try {
|
||||
String sql = "UPDATE messages"
|
||||
+ " SET cleanupDeadline = ? + cleanupTimerDuration"
|
||||
+ " WHERE messageId = ?"
|
||||
+ " AND cleanupTimerDuration IS NOT NULL"
|
||||
+ " AND cleanupDeadline IS NULL";
|
||||
ps = txn.prepareStatement(sql);
|
||||
ps.setLong(1, now);
|
||||
ps.setBytes(2, m.getBytes());
|
||||
int affected = ps.executeUpdate();
|
||||
if (affected < 0 || affected > 1) throw new DbStateException();
|
||||
ps.close();
|
||||
if (affected == 0) return TIMER_NOT_STARTED;
|
||||
sql = "SELECT cleanupDeadline FROM messages WHERE messageId = ?";
|
||||
ps = txn.prepareStatement(sql);
|
||||
ps.setBytes(1, m.getBytes());
|
||||
rs = ps.executeQuery();
|
||||
if (!rs.next()) throw new DbStateException();
|
||||
long deadline = rs.getLong(1);
|
||||
if (rs.next()) throw new DbStateException();
|
||||
rs.close();
|
||||
ps.close();
|
||||
return deadline;
|
||||
} catch (SQLException e) {
|
||||
tryToClose(rs, LOG, WARNING);
|
||||
tryToClose(ps, LOG, WARNING);
|
||||
throw new DbException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stopCleanupTimer(Connection txn, MessageId m)
|
||||
throws DbException {
|
||||
PreparedStatement ps = null;
|
||||
try {
|
||||
String sql = "UPDATE messages SET cleanupDeadline = NULL"
|
||||
+ " WHERE messageId = ?";
|
||||
ps = txn.prepareStatement(sql);
|
||||
ps.setBytes(1, m.getBytes());
|
||||
int affected = ps.executeUpdate();
|
||||
if (affected < 0 || affected > 1) throw new DbStateException();
|
||||
ps.close();
|
||||
} catch (SQLException e) {
|
||||
tryToClose(ps, LOG, WARNING);
|
||||
throw new DbException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateExpiryTimeAndEta(Connection txn, ContactId c, MessageId m,
|
||||
int maxLatency) throws DbException {
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
package org.briarproject.bramble.db;
|
||||
|
||||
import org.briarproject.bramble.api.db.DbException;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import static java.util.logging.Level.WARNING;
|
||||
import static java.util.logging.Logger.getLogger;
|
||||
import static org.briarproject.bramble.db.JdbcUtils.tryToClose;
|
||||
|
||||
class Migration47_48 implements Migration<Connection> {
|
||||
|
||||
private static final Logger LOG = getLogger(Migration47_48.class.getName());
|
||||
|
||||
@Override
|
||||
public int getStartVersion() {
|
||||
return 47;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getEndVersion() {
|
||||
return 48;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void migrate(Connection txn) throws DbException {
|
||||
Statement s = null;
|
||||
try {
|
||||
s = txn.createStatement();
|
||||
// Null if no timer duration has been set
|
||||
s.execute("ALTER TABLE messages"
|
||||
+ " ADD COLUMN cleanupTimerDuration BIGINT");
|
||||
// Null if no timer duration has been set or the timer
|
||||
// hasn't started
|
||||
s.execute("ALTER TABLE messages"
|
||||
+ " ADD COLUMN cleanupDeadline BIGINT");
|
||||
s.execute("CREATE INDEX IF NOT EXISTS messagesByCleanupDeadline"
|
||||
+ " ON messages (cleanupDeadline)");
|
||||
} catch (SQLException e) {
|
||||
tryToClose(s, LOG, WARNING);
|
||||
throw new DbException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -24,21 +24,15 @@ class StreamReaderFactoryImpl implements StreamReaderFactory {
|
||||
|
||||
@Override
|
||||
public InputStream createStreamReader(InputStream in, StreamContext ctx) {
|
||||
return new StreamReaderImpl(streamDecrypterFactory
|
||||
.createStreamDecrypter(in, ctx));
|
||||
return new StreamReaderImpl(
|
||||
streamDecrypterFactory.createStreamDecrypter(in, ctx));
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream createContactExchangeStreamReader(InputStream in,
|
||||
SecretKey headerKey) {
|
||||
return new StreamReaderImpl(streamDecrypterFactory
|
||||
.createContactExchangeStreamDecrypter(in, headerKey));
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream createLogStreamReader(InputStream in,
|
||||
SecretKey headerKey) {
|
||||
return new StreamReaderImpl(streamDecrypterFactory
|
||||
.createLogStreamDecrypter(in, headerKey));
|
||||
return new StreamReaderImpl(
|
||||
streamDecrypterFactory.createContactExchangeStreamDecrypter(in,
|
||||
headerKey));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,21 +26,15 @@ class StreamWriterFactoryImpl implements StreamWriterFactory {
|
||||
@Override
|
||||
public StreamWriter createStreamWriter(OutputStream out,
|
||||
StreamContext ctx) {
|
||||
return new StreamWriterImpl(streamEncrypterFactory
|
||||
.createStreamEncrypter(out, ctx));
|
||||
return new StreamWriterImpl(
|
||||
streamEncrypterFactory.createStreamEncrypter(out, ctx));
|
||||
}
|
||||
|
||||
@Override
|
||||
public StreamWriter createContactExchangeStreamWriter(OutputStream out,
|
||||
SecretKey headerKey) {
|
||||
return new StreamWriterImpl(streamEncrypterFactory
|
||||
.createContactExchangeStreamEncrypter(out, headerKey));
|
||||
return new StreamWriterImpl(
|
||||
streamEncrypterFactory.createContactExchangeStreamDecrypter(out,
|
||||
headerKey));
|
||||
}
|
||||
|
||||
@Override
|
||||
public StreamWriter createLogStreamWriter(OutputStream out,
|
||||
SecretKey headerKey) {
|
||||
return new StreamWriterImpl(streamEncrypterFactory
|
||||
.createLogStreamEncrypter(out, headerKey));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
package org.briarproject.bramble.db;
|
||||
|
||||
import org.briarproject.bramble.api.cleanup.event.CleanupTimerStartedEvent;
|
||||
import org.briarproject.bramble.api.contact.Contact;
|
||||
import org.briarproject.bramble.api.contact.ContactId;
|
||||
import org.briarproject.bramble.api.contact.PendingContactId;
|
||||
@@ -70,8 +69,6 @@ import static java.util.Arrays.asList;
|
||||
import static java.util.Collections.emptyList;
|
||||
import static java.util.Collections.emptyMap;
|
||||
import static java.util.Collections.singletonList;
|
||||
import static java.util.concurrent.TimeUnit.HOURS;
|
||||
import static org.briarproject.bramble.api.db.DatabaseComponent.TIMER_NOT_STARTED;
|
||||
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;
|
||||
@@ -513,11 +510,11 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
||||
throws Exception {
|
||||
context.checking(new Expectations() {{
|
||||
// Check whether the group is in the DB (which it's not)
|
||||
exactly(10).of(database).startTransaction();
|
||||
exactly(8).of(database).startTransaction();
|
||||
will(returnValue(txn));
|
||||
exactly(10).of(database).containsGroup(txn, groupId);
|
||||
exactly(8).of(database).containsGroup(txn, groupId);
|
||||
will(returnValue(false));
|
||||
exactly(10).of(database).abortTransaction(txn);
|
||||
exactly(8).of(database).abortTransaction(txn);
|
||||
// Allow other checks to pass
|
||||
allowing(database).containsContact(txn, contactId);
|
||||
will(returnValue(true));
|
||||
@@ -526,7 +523,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
||||
eventExecutor, shutdownManager);
|
||||
|
||||
try {
|
||||
db.transaction(true, transaction ->
|
||||
db.transaction(false, transaction ->
|
||||
db.getGroup(transaction, groupId));
|
||||
fail();
|
||||
} catch (NoSuchGroupException expected) {
|
||||
@@ -534,7 +531,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
||||
}
|
||||
|
||||
try {
|
||||
db.transaction(true, transaction ->
|
||||
db.transaction(false, transaction ->
|
||||
db.getGroupMetadata(transaction, groupId));
|
||||
fail();
|
||||
} catch (NoSuchGroupException expected) {
|
||||
@@ -542,23 +539,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
||||
}
|
||||
|
||||
try {
|
||||
db.transaction(true, transaction ->
|
||||
db.getMessageIds(transaction, groupId));
|
||||
fail();
|
||||
} catch (NoSuchGroupException expected) {
|
||||
// Expected
|
||||
}
|
||||
|
||||
try {
|
||||
db.transaction(true, transaction ->
|
||||
db.getMessageIds(transaction, groupId, new Metadata()));
|
||||
fail();
|
||||
} catch (NoSuchGroupException expected) {
|
||||
// Expected
|
||||
}
|
||||
|
||||
try {
|
||||
db.transaction(true, transaction ->
|
||||
db.transaction(false, transaction ->
|
||||
db.getMessageMetadata(transaction, groupId));
|
||||
fail();
|
||||
} catch (NoSuchGroupException expected) {
|
||||
@@ -566,7 +547,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
||||
}
|
||||
|
||||
try {
|
||||
db.transaction(true, transaction ->
|
||||
db.transaction(false, transaction ->
|
||||
db.getMessageMetadata(transaction, groupId,
|
||||
new Metadata()));
|
||||
fail();
|
||||
@@ -575,7 +556,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
||||
}
|
||||
|
||||
try {
|
||||
db.transaction(true, transaction ->
|
||||
db.transaction(false, transaction ->
|
||||
db.getMessageStatus(transaction, contactId, groupId));
|
||||
fail();
|
||||
} catch (NoSuchGroupException expected) {
|
||||
@@ -613,11 +594,11 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
||||
throws Exception {
|
||||
context.checking(new Expectations() {{
|
||||
// Check whether the message is in the DB (which it's not)
|
||||
exactly(15).of(database).startTransaction();
|
||||
exactly(12).of(database).startTransaction();
|
||||
will(returnValue(txn));
|
||||
exactly(15).of(database).containsMessage(txn, messageId);
|
||||
exactly(12).of(database).containsMessage(txn, messageId);
|
||||
will(returnValue(false));
|
||||
exactly(15).of(database).abortTransaction(txn);
|
||||
exactly(12).of(database).abortTransaction(txn);
|
||||
// Allow other checks to pass
|
||||
allowing(database).containsContact(txn, contactId);
|
||||
will(returnValue(true));
|
||||
@@ -642,7 +623,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
||||
}
|
||||
|
||||
try {
|
||||
db.transaction(true, transaction ->
|
||||
db.transaction(false, transaction ->
|
||||
db.getMessage(transaction, messageId));
|
||||
fail();
|
||||
} catch (NoSuchMessageException expected) {
|
||||
@@ -650,7 +631,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
||||
}
|
||||
|
||||
try {
|
||||
db.transaction(true, transaction ->
|
||||
db.transaction(false, transaction ->
|
||||
db.getMessageMetadata(transaction, messageId));
|
||||
fail();
|
||||
} catch (NoSuchMessageException expected) {
|
||||
@@ -658,7 +639,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
||||
}
|
||||
|
||||
try {
|
||||
db.transaction(true, transaction ->
|
||||
db.transaction(false, transaction ->
|
||||
db.getMessageState(transaction, messageId));
|
||||
fail();
|
||||
} catch (NoSuchMessageException expected) {
|
||||
@@ -666,7 +647,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
||||
}
|
||||
|
||||
try {
|
||||
db.transaction(true, transaction ->
|
||||
db.transaction(false, transaction ->
|
||||
db.getMessageStatus(transaction, contactId, messageId));
|
||||
fail();
|
||||
} catch (NoSuchMessageException expected) {
|
||||
@@ -681,15 +662,6 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
||||
// Expected
|
||||
}
|
||||
|
||||
try {
|
||||
db.transaction(false, transaction ->
|
||||
db.setCleanupTimerDuration(transaction, message.getId(),
|
||||
HOURS.toMillis(1)));
|
||||
fail();
|
||||
} catch (NoSuchMessageException expected) {
|
||||
// Expected
|
||||
}
|
||||
|
||||
try {
|
||||
db.transaction(false, transaction ->
|
||||
db.setMessagePermanent(transaction, message.getId()));
|
||||
@@ -715,7 +687,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
||||
}
|
||||
|
||||
try {
|
||||
db.transaction(true, transaction ->
|
||||
db.transaction(false, transaction ->
|
||||
db.getMessageDependencies(transaction, messageId));
|
||||
fail();
|
||||
} catch (NoSuchMessageException expected) {
|
||||
@@ -723,28 +695,12 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
||||
}
|
||||
|
||||
try {
|
||||
db.transaction(true, transaction ->
|
||||
db.transaction(false, transaction ->
|
||||
db.getMessageDependents(transaction, messageId));
|
||||
fail();
|
||||
} catch (NoSuchMessageException expected) {
|
||||
// Expected
|
||||
}
|
||||
|
||||
try {
|
||||
db.transaction(false, transaction ->
|
||||
db.startCleanupTimer(transaction, messageId));
|
||||
fail();
|
||||
} catch (NoSuchMessageException expected) {
|
||||
// Expected
|
||||
}
|
||||
|
||||
try {
|
||||
db.transaction(false, transaction ->
|
||||
db.stopCleanupTimer(transaction, messageId));
|
||||
fail();
|
||||
} catch (NoSuchMessageException expected) {
|
||||
// Expected
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -1025,9 +981,6 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
||||
oneOf(database).containsVisibleMessage(txn, contactId, messageId);
|
||||
will(returnValue(true));
|
||||
oneOf(database).raiseSeenFlag(txn, contactId, messageId);
|
||||
will(returnValue(true));
|
||||
oneOf(database).startCleanupTimer(txn, messageId);
|
||||
will(returnValue(TIMER_NOT_STARTED)); // No cleanup duration was set
|
||||
oneOf(database).commitTransaction(txn);
|
||||
oneOf(eventBus).broadcast(with(any(MessagesAckedEvent.class)));
|
||||
}});
|
||||
@@ -1040,56 +993,6 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReceiveDuplicateAck() throws Exception {
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(database).startTransaction();
|
||||
will(returnValue(txn));
|
||||
oneOf(database).containsContact(txn, contactId);
|
||||
will(returnValue(true));
|
||||
oneOf(database).containsVisibleMessage(txn, contactId, messageId);
|
||||
will(returnValue(true));
|
||||
oneOf(database).raiseSeenFlag(txn, contactId, messageId);
|
||||
will(returnValue(false)); // Already acked
|
||||
oneOf(database).commitTransaction(txn);
|
||||
}});
|
||||
DatabaseComponent db = createDatabaseComponent(database, eventBus,
|
||||
eventExecutor, shutdownManager);
|
||||
|
||||
db.transaction(false, transaction -> {
|
||||
Ack a = new Ack(singletonList(messageId));
|
||||
db.receiveAck(transaction, contactId, a);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReceiveAckWithCleanupTimer() throws Exception {
|
||||
long deadline = System.currentTimeMillis();
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(database).startTransaction();
|
||||
will(returnValue(txn));
|
||||
oneOf(database).containsContact(txn, contactId);
|
||||
will(returnValue(true));
|
||||
oneOf(database).containsVisibleMessage(txn, contactId, messageId);
|
||||
will(returnValue(true));
|
||||
oneOf(database).raiseSeenFlag(txn, contactId, messageId);
|
||||
will(returnValue(true));
|
||||
oneOf(database).startCleanupTimer(txn, messageId);
|
||||
will(returnValue(deadline));
|
||||
oneOf(database).commitTransaction(txn);
|
||||
oneOf(eventBus).broadcast(with(any(
|
||||
CleanupTimerStartedEvent.class)));
|
||||
oneOf(eventBus).broadcast(with(any(MessagesAckedEvent.class)));
|
||||
}});
|
||||
DatabaseComponent db = createDatabaseComponent(database, eventBus,
|
||||
eventExecutor, shutdownManager);
|
||||
|
||||
db.transaction(false, transaction -> {
|
||||
Ack a = new Ack(singletonList(messageId));
|
||||
db.receiveAck(transaction, contactId, a);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReceiveMessage() throws Exception {
|
||||
context.checking(new Expectations() {{
|
||||
|
||||
@@ -53,11 +53,10 @@ import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
import static java.util.Arrays.asList;
|
||||
import static java.util.Collections.emptyList;
|
||||
import static java.util.Collections.emptyMap;
|
||||
import static java.util.Collections.singletonList;
|
||||
import static java.util.Collections.singletonMap;
|
||||
import static java.util.concurrent.TimeUnit.SECONDS;
|
||||
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.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH;
|
||||
import static org.briarproject.bramble.api.sync.Group.Visibility.INVISIBLE;
|
||||
@@ -352,7 +351,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
|
||||
assertTrue(ids.isEmpty());
|
||||
|
||||
// Sharing the message should make it sendable
|
||||
db.setMessageShared(txn, messageId, true);
|
||||
db.setMessageShared(txn, messageId);
|
||||
ids = db.getMessagesToSend(txn, contactId, ONE_MEGABYTE, MAX_LATENCY);
|
||||
assertEquals(singletonList(messageId), ids);
|
||||
ids = db.getMessagesToOffer(txn, contactId, 100, MAX_LATENCY);
|
||||
@@ -632,7 +631,8 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
|
||||
|
||||
// The group should not be visible to the contact
|
||||
assertEquals(INVISIBLE, db.getGroupVisibility(txn, contactId, groupId));
|
||||
assertTrue(db.getGroupVisibility(txn, groupId).isEmpty());
|
||||
assertEquals(emptyMap(),
|
||||
db.getGroupVisibility(txn, groupId));
|
||||
|
||||
// Make the group visible to the contact
|
||||
db.addGroupVisibility(txn, contactId, groupId, false);
|
||||
@@ -655,7 +655,8 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
|
||||
// Make the group invisible again
|
||||
db.removeGroupVisibility(txn, contactId, groupId);
|
||||
assertEquals(INVISIBLE, db.getGroupVisibility(txn, contactId, groupId));
|
||||
assertTrue(db.getGroupVisibility(txn, groupId).isEmpty());
|
||||
assertEquals(emptyMap(),
|
||||
db.getGroupVisibility(txn, groupId));
|
||||
|
||||
db.commitTransaction(txn);
|
||||
db.close();
|
||||
@@ -2039,7 +2040,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
|
||||
assertEquals(Long.MAX_VALUE, db.getNextSendTime(txn, contactId));
|
||||
|
||||
// Share the message - now it should be sendable immediately
|
||||
db.setMessageShared(txn, messageId, true);
|
||||
db.setMessageShared(txn, messageId);
|
||||
assertEquals(0, db.getNextSendTime(txn, contactId));
|
||||
|
||||
// Mark the message as requested - it should still be sendable
|
||||
@@ -2346,87 +2347,6 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
|
||||
db.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCleanupTimer() throws Exception {
|
||||
long duration = 60_000;
|
||||
long now = System.currentTimeMillis();
|
||||
AtomicLong time = new AtomicLong(now);
|
||||
Database<Connection> db =
|
||||
open(false, new TestMessageFactory(), new SettableClock(time));
|
||||
Connection txn = db.startTransaction();
|
||||
|
||||
// No messages should be due or scheduled for deletion
|
||||
assertTrue(db.getMessagesToDelete(txn).isEmpty());
|
||||
assertEquals(NO_CLEANUP_DEADLINE, db.getNextCleanupDeadline(txn));
|
||||
|
||||
// Add a group and a message
|
||||
db.addGroup(txn, group);
|
||||
db.addMessage(txn, message, DELIVERED, false, false, null);
|
||||
|
||||
// No messages should be due or scheduled for deletion
|
||||
assertTrue(db.getMessagesToDelete(txn).isEmpty());
|
||||
assertEquals(NO_CLEANUP_DEADLINE, db.getNextCleanupDeadline(txn));
|
||||
|
||||
// Set the message's cleanup timer duration
|
||||
db.setCleanupTimerDuration(txn, messageId, duration);
|
||||
|
||||
// No messages should be due or scheduled for deletion
|
||||
assertTrue(db.getMessagesToDelete(txn).isEmpty());
|
||||
assertEquals(NO_CLEANUP_DEADLINE, db.getNextCleanupDeadline(txn));
|
||||
|
||||
// Start the message's cleanup timer
|
||||
assertEquals(now + duration, db.startCleanupTimer(txn, messageId));
|
||||
|
||||
// The timer can't be started again
|
||||
assertEquals(TIMER_NOT_STARTED, db.startCleanupTimer(txn, messageId));
|
||||
|
||||
// No messages should be due for deletion, but the message should be
|
||||
// scheduled for deletion
|
||||
assertTrue(db.getMessagesToDelete(txn).isEmpty());
|
||||
assertEquals(now + duration, db.getNextCleanupDeadline(txn));
|
||||
|
||||
// Stop the timer
|
||||
db.stopCleanupTimer(txn, messageId);
|
||||
|
||||
// No messages should be due or scheduled for deletion
|
||||
assertTrue(db.getMessagesToDelete(txn).isEmpty());
|
||||
assertEquals(NO_CLEANUP_DEADLINE, db.getNextCleanupDeadline(txn));
|
||||
|
||||
// Start the timer again
|
||||
assertEquals(now + duration, db.startCleanupTimer(txn, messageId));
|
||||
|
||||
// No messages should be due for deletion, but the message should be
|
||||
// scheduled for deletion
|
||||
assertTrue(db.getMessagesToDelete(txn).isEmpty());
|
||||
assertEquals(now + duration, db.getNextCleanupDeadline(txn));
|
||||
|
||||
// 1 ms before the timer expires, no messages should be due for
|
||||
// deletion but the message should be scheduled for deletion
|
||||
time.set(now + duration - 1);
|
||||
assertTrue(db.getMessagesToDelete(txn).isEmpty());
|
||||
assertEquals(now + duration, db.getNextCleanupDeadline(txn));
|
||||
|
||||
// When the timer expires, the message should be due and scheduled for
|
||||
// deletion
|
||||
time.set(now + duration);
|
||||
assertEquals(singletonMap(groupId, singletonList(messageId)),
|
||||
db.getMessagesToDelete(txn));
|
||||
assertEquals(now + duration, db.getNextCleanupDeadline(txn));
|
||||
|
||||
// 1 ms after the timer expires, the message should be due and
|
||||
// scheduled for deletion
|
||||
time.set(now + duration + 1);
|
||||
assertEquals(singletonMap(groupId, singletonList(messageId)),
|
||||
db.getMessagesToDelete(txn));
|
||||
assertEquals(now + duration, db.getNextCleanupDeadline(txn));
|
||||
|
||||
// Once the message has been deleted, it should no longer be due
|
||||
// or scheduled for deletion
|
||||
db.deleteMessage(txn, messageId);
|
||||
assertTrue(db.getMessagesToDelete(txn).isEmpty());
|
||||
assertEquals(NO_CLEANUP_DEADLINE, db.getNextCleanupDeadline(txn));
|
||||
}
|
||||
|
||||
private Database<Connection> open(boolean resume) throws Exception {
|
||||
return open(resume, new TestMessageFactory(), new SystemClock());
|
||||
}
|
||||
|
||||
@@ -33,11 +33,6 @@ public class BrambleCoreIntegrationTestModule {
|
||||
public boolean shouldEnableProfilePictures() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldEnableDisappearingMessages() {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,8 +16,8 @@ dependencies {
|
||||
implementation fileTree(dir: 'libs', include: '*.jar')
|
||||
implementation 'net.java.dev.jna:jna:4.5.2'
|
||||
implementation 'net.java.dev.jna:jna-platform:4.5.2'
|
||||
tor 'org.briarproject:tor:0.3.5.13@zip'
|
||||
tor 'org.briarproject:obfs4proxy:0.0.12-dev-40245c4a@zip'
|
||||
tor 'org.briarproject:tor:0.3.5.12@zip'
|
||||
tor 'org.briarproject:obfs4proxy:0.0.7@zip'
|
||||
|
||||
annotationProcessor 'com.google.dagger:dagger-compiler:2.24'
|
||||
|
||||
|
||||
@@ -23,8 +23,8 @@ dependencyVerification {
|
||||
'org.apache.ant:ant-launcher:1.9.4:ant-launcher-1.9.4.jar:7bccea20b41801ca17bcbc909a78c835d0f443f12d639c77bd6ae3d05861608d',
|
||||
'org.apache.ant:ant:1.9.4:ant-1.9.4.jar:649ae0730251de07b8913f49286d46bba7b92d47c5f332610aa426c4f02161d8',
|
||||
'org.beanshell:bsh:1.3.0:bsh-1.3.0.jar:9b04edc75d19db54f1b4e8b5355e9364384c6cf71eb0a1b9724c159d779879f8',
|
||||
'org.briarproject:obfs4proxy:0.0.12-dev-40245c4a:obfs4proxy-0.0.12-dev-40245c4a.zip:172029e7058b3a83ac93ac4991a44bf76e16ce8d46f558f5836d57da3cb3a766',
|
||||
'org.briarproject:tor:0.3.5.13:tor-0.3.5.13.zip:1c5f0b821ee2aadb0ea04aa96caab3ca0a08370cce8de81c2dfe04d172f8a2a0',
|
||||
'org.briarproject:obfs4proxy:0.0.7:obfs4proxy-0.0.7.zip:5b2f693262ce43a7e130f7cc7d5d1617925330640a2eb6d71085e95df8ee0642',
|
||||
'org.briarproject:tor:0.3.5.12:tor-0.3.5.12.zip:2f542c4befd216f2226bf7c76e3b8b2d99af6f146a8cb28bf727f42014587006',
|
||||
'org.checkerframework:checker-compat-qual:2.5.3:checker-compat-qual-2.5.3.jar:d76b9afea61c7c082908023f0cbc1427fab9abd2df915c8b8a3e7a509bccbc6d',
|
||||
'org.checkerframework:checker-qual:2.5.2:checker-qual-2.5.2.jar:64b02691c8b9d4e7700f8ee2e742dce7ea2c6e81e662b7522c9ee3bf568c040a',
|
||||
'org.codehaus.mojo:animal-sniffer-annotations:1.17:animal-sniffer-annotations-1.17.jar:92654f493ecfec52082e76354f0ebf87648dc3d5cec2e3c3cdb947c016747a53',
|
||||
|
||||
@@ -22,8 +22,8 @@ android {
|
||||
defaultConfig {
|
||||
minSdkVersion 16
|
||||
targetSdkVersion 29
|
||||
versionCode 10216
|
||||
versionName "1.2.16"
|
||||
versionCode 10214
|
||||
versionName "1.2.14"
|
||||
applicationId "org.briarproject.briar.android"
|
||||
|
||||
vectorDrawables.useSupportLibrary = true
|
||||
@@ -95,7 +95,6 @@ dependencies {
|
||||
implementation project(path: ':bramble-core', configuration: 'default')
|
||||
implementation project(':bramble-android')
|
||||
|
||||
implementation 'androidx.fragment:fragment:1.3.0'
|
||||
implementation 'androidx.preference:preference:1.1.1'
|
||||
implementation 'androidx.exifinterface:exifinterface:1.3.1'
|
||||
implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
|
||||
@@ -137,7 +136,6 @@ dependencies {
|
||||
testImplementation "org.jmock:jmock:$jmockVersion"
|
||||
testImplementation "org.jmock:jmock-junit4:$jmockVersion"
|
||||
testImplementation "org.jmock:jmock-legacy:$jmockVersion"
|
||||
testAnnotationProcessor "com.google.dagger:dagger-compiler:2.24"
|
||||
|
||||
androidTestImplementation project(path: ':bramble-api', configuration: 'testOutput')
|
||||
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
|
||||
|
||||
@@ -35,7 +35,6 @@ import org.briarproject.briar.BriarCoreModule;
|
||||
import org.briarproject.briar.android.attachment.AttachmentModule;
|
||||
import org.briarproject.briar.android.attachment.media.MediaModule;
|
||||
import org.briarproject.briar.android.conversation.glide.BriarModelLoader;
|
||||
import org.briarproject.briar.android.logging.CachingLogHandler;
|
||||
import org.briarproject.briar.android.login.SignInReminderReceiver;
|
||||
import org.briarproject.briar.android.view.EmojiTextInputView;
|
||||
import org.briarproject.briar.api.android.AndroidNotificationManager;
|
||||
@@ -183,10 +182,6 @@ public interface AndroidComponent
|
||||
|
||||
AndroidWakeLockManager wakeLockManager();
|
||||
|
||||
CachingLogHandler logHandler();
|
||||
|
||||
Thread.UncaughtExceptionHandler exceptionHandler();
|
||||
|
||||
AutoDeleteManager autoDeleteManager();
|
||||
|
||||
void inject(SignInReminderReceiver briarService);
|
||||
|
||||
@@ -37,7 +37,6 @@ import org.briarproject.briar.android.splash.SplashScreenActivity;
|
||||
import org.briarproject.briar.android.util.BriarNotificationBuilder;
|
||||
import org.briarproject.briar.api.android.AndroidNotificationManager;
|
||||
import org.briarproject.briar.api.blog.event.BlogPostAddedEvent;
|
||||
import org.briarproject.briar.api.conversation.ConversationResponse;
|
||||
import org.briarproject.briar.api.conversation.event.ConversationMessageReceivedEvent;
|
||||
import org.briarproject.briar.api.forum.event.ForumPostReceivedEvent;
|
||||
import org.briarproject.briar.api.privategroup.event.GroupMessageAddedEvent;
|
||||
@@ -227,12 +226,6 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
|
||||
} else if (e instanceof ConversationMessageReceivedEvent) {
|
||||
ConversationMessageReceivedEvent<?> p =
|
||||
(ConversationMessageReceivedEvent<?>) e;
|
||||
if (p.getMessageHeader() instanceof ConversationResponse) {
|
||||
ConversationResponse r =
|
||||
(ConversationResponse) p.getMessageHeader();
|
||||
// don't show notification for own auto-decline responses
|
||||
if (r.isAutoDecline()) return;
|
||||
}
|
||||
showContactNotification(p.getContactId());
|
||||
} else if (e instanceof GroupMessageAddedEvent) {
|
||||
GroupMessageAddedEvent g = (GroupMessageAddedEvent) e;
|
||||
|
||||
@@ -33,7 +33,6 @@ import org.briarproject.briar.android.account.SetupModule;
|
||||
import org.briarproject.briar.android.contact.ContactListModule;
|
||||
import org.briarproject.briar.android.forum.ForumModule;
|
||||
import org.briarproject.briar.android.keyagreement.ContactExchangeModule;
|
||||
import org.briarproject.briar.android.logging.LoggingModule;
|
||||
import org.briarproject.briar.android.login.LoginModule;
|
||||
import org.briarproject.briar.android.navdrawer.NavDrawerModule;
|
||||
import org.briarproject.briar.android.privategroup.conversation.GroupConversationModule;
|
||||
@@ -69,13 +68,11 @@ import static java.util.Collections.singletonList;
|
||||
import static java.util.Collections.singletonMap;
|
||||
import static org.briarproject.bramble.api.reporting.ReportingConstants.DEV_ONION_ADDRESS;
|
||||
import static org.briarproject.bramble.api.reporting.ReportingConstants.DEV_PUBLIC_KEY_HEX;
|
||||
import static org.briarproject.briar.android.TestingConstants.IS_DEBUG_BUILD;
|
||||
|
||||
@Module(includes = {
|
||||
SetupModule.class,
|
||||
DozeHelperModule.class,
|
||||
ContactExchangeModule.class,
|
||||
LoggingModule.class,
|
||||
LoginModule.class,
|
||||
NavDrawerModule.class,
|
||||
ViewModelModule.class,
|
||||
@@ -194,11 +191,6 @@ public class AppModule {
|
||||
public File getReportDir() {
|
||||
return AndroidUtils.getReportDir(app.getApplicationContext());
|
||||
}
|
||||
|
||||
@Override
|
||||
public File getLogcatFile() {
|
||||
return AndroidUtils.getLogcatFile(app.getApplicationContext());
|
||||
}
|
||||
};
|
||||
return devConfig;
|
||||
}
|
||||
@@ -276,17 +268,12 @@ public class AppModule {
|
||||
|
||||
@Override
|
||||
public boolean shouldEnableImageAttachments() {
|
||||
return IS_DEBUG_BUILD;
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldEnableProfilePictures() {
|
||||
return IS_DEBUG_BUILD;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldEnableDisappearingMessages() {
|
||||
return IS_DEBUG_BUILD;
|
||||
return false;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -6,6 +6,9 @@ import android.content.SharedPreferences;
|
||||
import org.briarproject.bramble.BrambleApplication;
|
||||
import org.briarproject.briar.android.navdrawer.NavDrawerActivity;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.logging.LogRecord;
|
||||
|
||||
/**
|
||||
* This exists so that the Application object will not necessarily be cast
|
||||
* directly to the Briar application object.
|
||||
@@ -14,6 +17,8 @@ public interface BriarApplication extends BrambleApplication {
|
||||
|
||||
Class<? extends Activity> ENTRY_ACTIVITY = NavDrawerActivity.class;
|
||||
|
||||
Collection<LogRecord> getRecentLogRecords();
|
||||
|
||||
AndroidComponent getApplicationComponent();
|
||||
|
||||
SharedPreferences getDefaultSharedPreferences();
|
||||
|
||||
@@ -20,10 +20,12 @@ import org.briarproject.bramble.BrambleCoreEagerSingletons;
|
||||
import org.briarproject.briar.BriarCoreEagerSingletons;
|
||||
import org.briarproject.briar.R;
|
||||
import org.briarproject.briar.android.logging.CachingLogHandler;
|
||||
import org.briarproject.briar.android.reporting.BriarExceptionHandler;
|
||||
import org.briarproject.briar.android.util.UiUtils;
|
||||
|
||||
import java.lang.Thread.UncaughtExceptionHandler;
|
||||
import java.util.Collection;
|
||||
import java.util.logging.Handler;
|
||||
import java.util.logging.LogRecord;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
@@ -40,18 +42,22 @@ public class BriarApplicationImpl extends Application
|
||||
private static final Logger LOG =
|
||||
getLogger(BriarApplicationImpl.class.getName());
|
||||
|
||||
private final CachingLogHandler logHandler = new CachingLogHandler();
|
||||
private final BriarExceptionHandler exceptionHandler =
|
||||
new BriarExceptionHandler(this);
|
||||
|
||||
private AndroidComponent applicationComponent;
|
||||
private volatile SharedPreferences prefs;
|
||||
|
||||
@Override
|
||||
protected void attachBaseContext(Context base) {
|
||||
Thread.setDefaultUncaughtExceptionHandler(exceptionHandler);
|
||||
if (prefs == null)
|
||||
prefs = PreferenceManager.getDefaultSharedPreferences(base);
|
||||
// Loading the language needs to be done here.
|
||||
Localizer.initialize(prefs);
|
||||
super.attachBaseContext(
|
||||
Localizer.getInstance().setLocale(base));
|
||||
Localizer.getInstance().setLocale(this);
|
||||
setTheme(base, prefs);
|
||||
}
|
||||
|
||||
@@ -61,11 +67,6 @@ public class BriarApplicationImpl extends Application
|
||||
|
||||
if (IS_DEBUG_BUILD) enableStrictMode();
|
||||
|
||||
applicationComponent = createApplicationComponent();
|
||||
UncaughtExceptionHandler exceptionHandler =
|
||||
applicationComponent.exceptionHandler();
|
||||
Thread.setDefaultUncaughtExceptionHandler(exceptionHandler);
|
||||
|
||||
Logger rootLogger = getLogger("");
|
||||
Handler[] handlers = rootLogger.getHandlers();
|
||||
// Disable the Android logger for release builds
|
||||
@@ -77,12 +78,12 @@ public class BriarApplicationImpl extends Application
|
||||
// Restore the default handlers after the level raising handler
|
||||
for (Handler handler : handlers) rootLogger.addHandler(handler);
|
||||
}
|
||||
CachingLogHandler logHandler = applicationComponent.logHandler();
|
||||
rootLogger.addHandler(logHandler);
|
||||
rootLogger.setLevel(IS_DEBUG_BUILD ? FINE : INFO);
|
||||
|
||||
LOG.info("Created");
|
||||
|
||||
applicationComponent = createApplicationComponent();
|
||||
EmojiManager.install(new GoogleEmojiProvider());
|
||||
}
|
||||
|
||||
@@ -135,6 +136,11 @@ public class BriarApplicationImpl extends Application
|
||||
return applicationComponent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<LogRecord> getRecentLogRecords() {
|
||||
return logHandler.getRecentLogRecords();
|
||||
}
|
||||
|
||||
@Override
|
||||
public AndroidComponent getApplicationComponent() {
|
||||
return applicationComponent;
|
||||
|
||||
@@ -37,7 +37,7 @@ import javax.inject.Inject;
|
||||
import androidx.core.app.NotificationCompat;
|
||||
|
||||
import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
|
||||
import static android.app.NotificationManager.IMPORTANCE_LOW;
|
||||
import static android.app.NotificationManager.IMPORTANCE_NONE;
|
||||
import static android.app.PendingIntent.FLAG_UPDATE_CURRENT;
|
||||
import static android.content.Intent.ACTION_SHUTDOWN;
|
||||
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK;
|
||||
@@ -46,7 +46,6 @@ import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
|
||||
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
|
||||
import static android.content.Intent.FLAG_ACTIVITY_NO_ANIMATION;
|
||||
import static android.os.Build.VERSION.SDK_INT;
|
||||
import static android.os.Process.myPid;
|
||||
import static androidx.core.app.NotificationCompat.VISIBILITY_SECRET;
|
||||
import static java.util.logging.Level.INFO;
|
||||
import static java.util.logging.Level.WARNING;
|
||||
@@ -57,10 +56,8 @@ import static org.briarproject.briar.android.BriarApplication.ENTRY_ACTIVITY;
|
||||
import static org.briarproject.briar.api.android.AndroidNotificationManager.FAILURE_CHANNEL_ID;
|
||||
import static org.briarproject.briar.api.android.AndroidNotificationManager.FAILURE_NOTIFICATION_ID;
|
||||
import static org.briarproject.briar.api.android.AndroidNotificationManager.ONGOING_CHANNEL_ID;
|
||||
import static org.briarproject.briar.api.android.AndroidNotificationManager.ONGOING_CHANNEL_OLD_ID;
|
||||
import static org.briarproject.briar.api.android.AndroidNotificationManager.ONGOING_NOTIFICATION_ID;
|
||||
import static org.briarproject.briar.api.android.LockManager.ACTION_LOCK;
|
||||
import static org.briarproject.briar.api.android.LockManager.EXTRA_PID;
|
||||
|
||||
public class BriarService extends Service {
|
||||
|
||||
@@ -123,17 +120,11 @@ public class BriarService extends Service {
|
||||
if (SDK_INT >= 26) {
|
||||
NotificationManager nm = (NotificationManager)
|
||||
requireNonNull(getSystemService(NOTIFICATION_SERVICE));
|
||||
// Delete the old notification channel, which had
|
||||
// IMPORTANCE_NONE and showed a badge
|
||||
nm.deleteNotificationChannel(ONGOING_CHANNEL_OLD_ID);
|
||||
// Use IMPORTANCE_LOW so the system doesn't show its own
|
||||
// notification on API 26-27
|
||||
NotificationChannel ongoingChannel = new NotificationChannel(
|
||||
ONGOING_CHANNEL_ID,
|
||||
getString(R.string.ongoing_notification_title),
|
||||
IMPORTANCE_LOW);
|
||||
IMPORTANCE_NONE);
|
||||
ongoingChannel.setLockscreenVisibility(VISIBILITY_SECRET);
|
||||
ongoingChannel.setShowBadge(false);
|
||||
nm.createNotificationChannel(ongoingChannel);
|
||||
NotificationChannel failureChannel = new NotificationChannel(
|
||||
FAILURE_CHANNEL_ID,
|
||||
@@ -179,7 +170,6 @@ public class BriarService extends Service {
|
||||
@Override
|
||||
protected void attachBaseContext(Context base) {
|
||||
super.attachBaseContext(Localizer.getInstance().setLocale(base));
|
||||
Localizer.getInstance().setLocale(this);
|
||||
}
|
||||
|
||||
private void showStartupFailureNotification(StartResult result) {
|
||||
@@ -212,12 +202,7 @@ public class BriarService extends Service {
|
||||
@Override
|
||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||
if (ACTION_LOCK.equals(intent.getAction())) {
|
||||
int pid = intent.getIntExtra(EXTRA_PID, -1);
|
||||
if (pid == myPid()) lockManager.setLocked(true);
|
||||
else if (LOG.isLoggable(WARNING)) {
|
||||
LOG.warning("Tried to lock process " + pid + " but this is " +
|
||||
myPid());
|
||||
}
|
||||
lockManager.setLocked(true);
|
||||
}
|
||||
return START_NOT_STICKY; // Don't restart automatically if killed
|
||||
}
|
||||
|
||||
@@ -68,21 +68,7 @@ public class Localizer {
|
||||
return new Locale(tag);
|
||||
}
|
||||
|
||||
/*
|
||||
* Apply localization to the specified context.
|
||||
*
|
||||
* It updates the configuration of the context's resources object but can
|
||||
* also return a new context derived from the context parameter. Hence
|
||||
* make sure to work with the return value of this method instead of
|
||||
* the context you passed as a parameter.
|
||||
*
|
||||
* This method also has side-effects as it calls Locale#setDefault().
|
||||
*
|
||||
* When using this in attachBaseContext() of Application, Service or
|
||||
* Activity subclasses, it is important to not only apply this method to the
|
||||
* base Context parameter received in that method, but also apply it on the
|
||||
* class itself which also extends Context.
|
||||
*/
|
||||
// Returns the localized version of context
|
||||
public Context setLocale(Context context) {
|
||||
Resources res = context.getResources();
|
||||
Configuration conf = res.getConfiguration();
|
||||
@@ -96,7 +82,7 @@ public class Localizer {
|
||||
Locale.setDefault(locale);
|
||||
if (SDK_INT >= 17) {
|
||||
conf.setLocale(locale);
|
||||
context = context.createConfigurationContext(conf);
|
||||
context.createConfigurationContext(conf);
|
||||
} else
|
||||
conf.locale = locale;
|
||||
//noinspection deprecation
|
||||
|
||||
@@ -32,10 +32,8 @@ import androidx.lifecycle.LiveData;
|
||||
import androidx.lifecycle.MutableLiveData;
|
||||
|
||||
import static android.app.AlarmManager.ELAPSED_REALTIME;
|
||||
import static android.app.PendingIntent.FLAG_UPDATE_CURRENT;
|
||||
import static android.app.PendingIntent.getService;
|
||||
import static android.content.Context.ALARM_SERVICE;
|
||||
import static android.os.Process.myPid;
|
||||
import static android.os.SystemClock.elapsedRealtime;
|
||||
import static java.util.concurrent.TimeUnit.MINUTES;
|
||||
import static java.util.logging.Level.WARNING;
|
||||
@@ -77,25 +75,23 @@ public class LockManagerImpl implements LockManager, Service, EventListener {
|
||||
LockManagerImpl(Application app, SettingsManager settingsManager,
|
||||
AndroidNotificationManager notificationManager,
|
||||
@DatabaseExecutor Executor dbExecutor) {
|
||||
appContext = app.getApplicationContext();
|
||||
this.appContext = app.getApplicationContext();
|
||||
this.settingsManager = settingsManager;
|
||||
this.notificationManager = notificationManager;
|
||||
this.dbExecutor = dbExecutor;
|
||||
alarmManager =
|
||||
this.alarmManager =
|
||||
(AlarmManager) appContext.getSystemService(ALARM_SERVICE);
|
||||
Intent i =
|
||||
new Intent(ACTION_LOCK, null, appContext, BriarService.class);
|
||||
i.putExtra(EXTRA_PID, myPid());
|
||||
// When not using FLAG_UPDATE_CURRENT, the intent might have no extras
|
||||
lockIntent = getService(appContext, 0, i, FLAG_UPDATE_CURRENT);
|
||||
timeoutNever = Integer.parseInt(
|
||||
this.lockIntent = getService(appContext, 0, i, 0);
|
||||
this.timeoutNever = Integer.valueOf(
|
||||
appContext.getString(R.string.pref_lock_timeout_value_never));
|
||||
timeoutDefault = Integer.parseInt(
|
||||
this.timeoutDefault = Integer.valueOf(
|
||||
appContext.getString(R.string.pref_lock_timeout_value_default));
|
||||
timeoutMinutes = timeoutNever;
|
||||
this.timeoutMinutes = timeoutNever;
|
||||
|
||||
// setting this in the constructor makes #getValue() @NonNull
|
||||
lockable.setValue(false);
|
||||
this.lockable.setValue(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -152,7 +148,7 @@ public class LockManagerImpl implements LockManager, Service, EventListener {
|
||||
boolean oldValue = lockable.getValue();
|
||||
boolean newValue = hasScreenLock(appContext) && lockableSetting;
|
||||
if (oldValue != newValue) {
|
||||
lockable.setValue(newValue);
|
||||
this.lockable.setValue(newValue);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -77,7 +77,7 @@ public class UnlockActivity extends BaseActivity {
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode,
|
||||
@Nullable Intent data) {
|
||||
Intent data) {
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
if (requestCode == REQUEST_KEYGUARD_UNLOCK) {
|
||||
if (resultCode == RESULT_OK) unlock();
|
||||
|
||||
@@ -109,7 +109,6 @@ public abstract class BaseActivity extends AppCompatActivity
|
||||
protected void attachBaseContext(Context base) {
|
||||
super.attachBaseContext(
|
||||
Localizer.getInstance().setLocale(base));
|
||||
Localizer.getInstance().setLocale(this);
|
||||
}
|
||||
|
||||
public ActivityComponent getActivityComponent() {
|
||||
|
||||
@@ -34,6 +34,7 @@ import io.github.kobakei.materialfabspeeddial.FabSpeedDial;
|
||||
import io.github.kobakei.materialfabspeeddial.FabSpeedDial.OnMenuItemClickListener;
|
||||
|
||||
import static com.google.android.material.snackbar.BaseTransientBottomBar.LENGTH_INDEFINITE;
|
||||
import static org.briarproject.bramble.api.nullsafety.NullSafety.requireNonNull;
|
||||
import static org.briarproject.briar.android.conversation.ConversationActivity.CONTACT_ID;
|
||||
|
||||
@MethodsNotNullByDefault
|
||||
@@ -101,8 +102,7 @@ public class ContactListFragment extends BaseFragment
|
||||
.observe(getViewLifecycleOwner(), result -> {
|
||||
result.onError(this::handleException).onSuccess(items -> {
|
||||
adapter.submitList(items);
|
||||
// TODO remove when BriarRecyclerView was adapted
|
||||
list.showData();
|
||||
if (requireNonNull(items).size() == 0) list.showData();
|
||||
});
|
||||
});
|
||||
viewModel.getHasPendingContacts()
|
||||
|
||||
@@ -124,7 +124,7 @@ public class AddContactViewModel extends DbViewModel {
|
||||
return addContactResult;
|
||||
}
|
||||
|
||||
void updatePendingContact(String name, PendingContact p) {
|
||||
public void updatePendingContact(String name, PendingContact p) {
|
||||
runOnDbThread(() -> {
|
||||
try {
|
||||
contactManager.removePendingContact(p.getId());
|
||||
|
||||
@@ -29,11 +29,11 @@ import org.briarproject.briar.android.fragment.BaseFragment;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.StringRes;
|
||||
import androidx.appcompat.app.AlertDialog.Builder;
|
||||
import androidx.lifecycle.LifecycleOwner;
|
||||
import androidx.lifecycle.ViewModelProvider;
|
||||
import androidx.lifecycle.ViewModelProviders;
|
||||
|
||||
import static android.view.View.INVISIBLE;
|
||||
import static android.view.View.VISIBLE;
|
||||
@@ -48,7 +48,6 @@ import static org.briarproject.briar.android.util.UiUtils.getDialogIcon;
|
||||
public class NicknameFragment extends BaseFragment {
|
||||
|
||||
private static final String TAG = NicknameFragment.class.getName();
|
||||
private static final String SAVED_LINK = "savedLink";
|
||||
|
||||
@Inject
|
||||
ViewModelProvider.Factory viewModelFactory;
|
||||
@@ -68,20 +67,6 @@ public class NicknameFragment extends BaseFragment {
|
||||
@Override
|
||||
public void injectFragment(ActivityComponent component) {
|
||||
component.inject(this);
|
||||
viewModel = new ViewModelProvider(requireActivity(), viewModelFactory)
|
||||
.get(AddContactViewModel.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
if (savedInstanceState != null) {
|
||||
// When the activity (and the ViewModel) get destroyed,
|
||||
// the link will not be available anymore and needs to be restored.
|
||||
// TODO migrate to SavedStateViewModelFactory (once we can use it)
|
||||
String savedLink = savedInstanceState.getString(SAVED_LINK);
|
||||
if (savedLink != null) viewModel.setRemoteHandshakeLink(savedLink);
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -89,9 +74,14 @@ public class NicknameFragment extends BaseFragment {
|
||||
public View onCreateView(LayoutInflater inflater,
|
||||
@Nullable ViewGroup container,
|
||||
@Nullable Bundle savedInstanceState) {
|
||||
if (getActivity() == null || getContext() == null) return null;
|
||||
|
||||
View v = inflater.inflate(R.layout.fragment_nickname,
|
||||
container, false);
|
||||
|
||||
viewModel = ViewModelProviders.of(getActivity(), viewModelFactory)
|
||||
.get(AddContactViewModel.class);
|
||||
|
||||
contactNameLayout = v.findViewById(R.id.contactNameLayout);
|
||||
contactNameInput = v.findViewById(R.id.contactNameInput);
|
||||
|
||||
@@ -103,12 +93,6 @@ public class NicknameFragment extends BaseFragment {
|
||||
return v;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSaveInstanceState(@NonNull Bundle outState) {
|
||||
super.onSaveInstanceState(outState);
|
||||
outState.putString(SAVED_LINK, viewModel.getRemoteHandshakeLink());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private String getNicknameOrNull() {
|
||||
Editable text = contactNameInput.getText();
|
||||
|
||||
@@ -63,7 +63,6 @@ import org.briarproject.briar.android.view.TextSendController;
|
||||
import org.briarproject.briar.android.view.TextSendController.SendState;
|
||||
import org.briarproject.briar.api.android.AndroidNotificationManager;
|
||||
import org.briarproject.briar.api.attachment.AttachmentHeader;
|
||||
import org.briarproject.briar.api.autodelete.event.ConversationMessagesDeletedEvent;
|
||||
import org.briarproject.briar.api.blog.BlogSharingManager;
|
||||
import org.briarproject.briar.api.client.ProtocolStateException;
|
||||
import org.briarproject.briar.api.client.SessionId;
|
||||
@@ -373,14 +372,12 @@ public class ConversationActivity extends BriarActivity
|
||||
// enable alias action if available
|
||||
observeOnce(viewModel.getContactItem(), this, contact ->
|
||||
menu.findItem(R.id.action_set_alias).setEnabled(true));
|
||||
// Show auto-delete menu item if feature is enabled
|
||||
if (featureFlags.shouldEnableDisappearingMessages()) {
|
||||
// show auto-delete timer setting only, if contacts supports it
|
||||
observeOnce(viewModel.getPrivateMessageFormat(), this, format -> {
|
||||
boolean visible = format == TEXT_IMAGES_AUTO_DELETE;
|
||||
MenuItem item = menu.findItem(R.id.action_conversation_settings);
|
||||
item.setVisible(true);
|
||||
// Enable menu item only if contact supports auto-delete
|
||||
viewModel.getPrivateMessageFormat().observe(this, format ->
|
||||
item.setEnabled(format == TEXT_IMAGES_AUTO_DELETE));
|
||||
}
|
||||
item.setVisible(visible);
|
||||
});
|
||||
|
||||
return super.onCreateOptionsMenu(menu);
|
||||
}
|
||||
@@ -575,7 +572,7 @@ public class ConversationActivity extends BriarActivity
|
||||
this::showImageOnboarding);
|
||||
}
|
||||
List<ConversationItem> items = createItems(headers);
|
||||
adapter.replaceAll(items);
|
||||
adapter.addAll(items);
|
||||
list.showData();
|
||||
if (layoutManagerState == null) {
|
||||
scrollToBottom();
|
||||
@@ -674,13 +671,6 @@ public class ConversationActivity extends BriarActivity
|
||||
LOG.info("Messages acked");
|
||||
markMessages(m.getMessageIds(), true, true);
|
||||
}
|
||||
} else if (e instanceof ConversationMessagesDeletedEvent) {
|
||||
ConversationMessagesDeletedEvent m =
|
||||
(ConversationMessagesDeletedEvent) e;
|
||||
if (m.getContactId().equals(contactId)) {
|
||||
LOG.info("Messages auto-deleted");
|
||||
onConversationMessagesDeleted(m.getMessageIds());
|
||||
}
|
||||
} else if (e instanceof ContactConnectedEvent) {
|
||||
ContactConnectedEvent c = (ContactConnectedEvent) e;
|
||||
if (c.getContactId().equals(contactId)) {
|
||||
@@ -728,13 +718,6 @@ public class ConversationActivity extends BriarActivity
|
||||
}
|
||||
}
|
||||
|
||||
@UiThread
|
||||
private void onConversationMessagesDeleted(
|
||||
Collection<MessageId> messageIds) {
|
||||
adapter.incrementRevision();
|
||||
adapter.removeItems(messageIds);
|
||||
}
|
||||
|
||||
@UiThread
|
||||
private void markMessages(Collection<MessageId> messageIds, boolean sent,
|
||||
boolean seen) {
|
||||
@@ -846,6 +829,10 @@ public class ConversationActivity extends BriarActivity
|
||||
fails.add(getString(
|
||||
R.string.dialog_message_not_deleted_ongoing_invitations));
|
||||
}
|
||||
if (result.hasNotFullyDownloaded()) {
|
||||
fails.add(getString(
|
||||
R.string.dialog_message_not_deleted_partly_downloaded));
|
||||
}
|
||||
// add problems the user can resolve
|
||||
if (result.hasNotAllIntroductionSelected() &&
|
||||
result.hasNotAllInvitationSelected()) {
|
||||
|
||||
@@ -13,13 +13,10 @@ import org.briarproject.briar.R;
|
||||
import org.briarproject.briar.android.util.BriarAdapter;
|
||||
import org.briarproject.briar.android.util.ItemReturningAdapter;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import androidx.annotation.LayoutRes;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.UiThread;
|
||||
import androidx.recyclerview.selection.SelectionTracker;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView.RecycledViewPool;
|
||||
@@ -120,31 +117,15 @@ class ConversationAdapter
|
||||
}
|
||||
|
||||
@Override
|
||||
public void replaceAll(Collection<ConversationItem> itemsToReplace) {
|
||||
public void addAll(Collection<ConversationItem> itemsToAdd) {
|
||||
items.beginBatchedUpdates();
|
||||
// there can be items already in the adapter
|
||||
// SortedList takes care of duplicates and detecting changed items
|
||||
items.replaceAll(itemsToReplace);
|
||||
items.addAll(itemsToAdd);
|
||||
updateTimersInBatch();
|
||||
items.endBatchedUpdates();
|
||||
}
|
||||
|
||||
@UiThread
|
||||
void removeItems(Collection<MessageId> messageIds) {
|
||||
// Collect all items to be deleted first
|
||||
// and then delete them in one batched update.
|
||||
// Deleting them right away would cause issues
|
||||
// due to changing list positions.
|
||||
List<ConversationItem> toRemove = new ArrayList<>(messageIds.size());
|
||||
for (int i = 0; i < items.size(); i++) {
|
||||
ConversationItem item = items.get(i);
|
||||
if (messageIds.contains(item.getId())) toRemove.add(item);
|
||||
}
|
||||
items.beginBatchedUpdates();
|
||||
for (ConversationItem item : toRemove) items.remove(item);
|
||||
items.endBatchedUpdates();
|
||||
}
|
||||
|
||||
private void updateTimersInBatch() {
|
||||
long lastTimerIncoming = NO_AUTO_DELETE_TIMER;
|
||||
long lastTimerOutgoing = NO_AUTO_DELETE_TIMER;
|
||||
|
||||
@@ -18,7 +18,6 @@ import static android.view.View.GONE;
|
||||
import static android.view.View.VISIBLE;
|
||||
import static org.briarproject.bramble.util.StringUtils.trim;
|
||||
import static org.briarproject.briar.android.util.UiUtils.formatDate;
|
||||
import static org.briarproject.briar.android.util.UiUtils.formatDuration;
|
||||
import static org.briarproject.briar.api.autodelete.AutoDeleteConstants.NO_AUTO_DELETE_TIMER;
|
||||
|
||||
@UiThread
|
||||
@@ -83,23 +82,19 @@ abstract class ConversationItemViewHolder extends ViewHolder {
|
||||
Context ctx = itemView.getContext();
|
||||
topNotice.setVisibility(VISIBLE);
|
||||
boolean enabled = item.getAutoDeleteTimer() != NO_AUTO_DELETE_TIMER;
|
||||
String duration = enabled ?
|
||||
formatDuration(ctx, item.getAutoDeleteTimer()) : "";
|
||||
String tapToLearnMore = ctx.getString(R.string.tap_to_learn_more);
|
||||
String text;
|
||||
if (item.isIncoming()) {
|
||||
String name = item.getContactName().getValue();
|
||||
text = enabled ?
|
||||
ctx.getString(R.string.auto_delete_msg_contact_enabled,
|
||||
name, duration, tapToLearnMore) :
|
||||
ctx.getString(R.string.auto_delete_msg_contact_disabled,
|
||||
name, tapToLearnMore);
|
||||
int strRes = enabled ?
|
||||
R.string.auto_delete_msg_contact_enabled :
|
||||
R.string.auto_delete_msg_contact_disabled;
|
||||
text = ctx.getString(strRes, name, tapToLearnMore);
|
||||
} else {
|
||||
text = enabled ?
|
||||
ctx.getString(R.string.auto_delete_msg_you_enabled,
|
||||
duration, tapToLearnMore) :
|
||||
ctx.getString(R.string.auto_delete_msg_you_disabled,
|
||||
tapToLearnMore);
|
||||
int strRes = enabled ?
|
||||
R.string.auto_delete_msg_you_enabled :
|
||||
R.string.auto_delete_msg_you_disabled;
|
||||
text = ctx.getString(strRes, tapToLearnMore);
|
||||
}
|
||||
topNotice.setText(text);
|
||||
topNotice.setOnClickListener(
|
||||
|
||||
@@ -5,7 +5,7 @@ import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.Button;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.briarproject.bramble.api.contact.ContactId;
|
||||
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
||||
@@ -13,7 +13,6 @@ import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
||||
import org.briarproject.briar.R;
|
||||
import org.briarproject.briar.android.activity.ActivityComponent;
|
||||
import org.briarproject.briar.android.fragment.BaseFragment;
|
||||
import org.briarproject.briar.android.widget.OnboardingFullDialogFragment;
|
||||
|
||||
import java.util.logging.Logger;
|
||||
|
||||
@@ -96,7 +95,7 @@ public class ConversationSettingsDialog extends DialogFragment {
|
||||
switchDisappearingMessages.setOnCheckedChangeListener(
|
||||
(button, value) -> viewModel.setAutoDeleteTimerEnabled(value));
|
||||
|
||||
Button buttonLearnMore =
|
||||
TextView buttonLearnMore =
|
||||
view.findViewById(R.id.buttonLearnMore);
|
||||
buttonLearnMore.setOnClickListener(e -> showLearnMoreDialog());
|
||||
|
||||
@@ -114,10 +113,10 @@ public class ConversationSettingsDialog extends DialogFragment {
|
||||
}
|
||||
|
||||
private void showLearnMoreDialog() {
|
||||
OnboardingFullDialogFragment.newInstance(
|
||||
R.string.disappearing_messages_title,
|
||||
R.string.disappearing_messages_explanation_long
|
||||
).show(getChildFragmentManager(), OnboardingFullDialogFragment.TAG);
|
||||
ConversationSettingsLearnMoreDialog
|
||||
dialog = new ConversationSettingsLearnMoreDialog();
|
||||
dialog.show(getChildFragmentManager(),
|
||||
ConversationSettingsLearnMoreDialog.TAG);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
package org.briarproject.briar.android.conversation;
|
||||
|
||||
import android.app.Dialog;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
|
||||
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
||||
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
||||
import org.briarproject.briar.R;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.fragment.app.DialogFragment;
|
||||
import androidx.fragment.app.FragmentActivity;
|
||||
|
||||
@MethodsNotNullByDefault
|
||||
@ParametersNotNullByDefault
|
||||
public class ConversationSettingsLearnMoreDialog extends DialogFragment {
|
||||
|
||||
final static String TAG =
|
||||
ConversationSettingsLearnMoreDialog.class.getName();
|
||||
|
||||
@Override
|
||||
public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
|
||||
FragmentActivity activity = requireActivity();
|
||||
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(activity,
|
||||
R.style.OnboardingDialogTheme);
|
||||
|
||||
LayoutInflater inflater = LayoutInflater.from(builder.getContext());
|
||||
View view = inflater.inflate(
|
||||
R.layout.fragment_conversation_settings_learn_more, null);
|
||||
builder.setView(view);
|
||||
|
||||
builder.setTitle(R.string.disappearing_messages_title);
|
||||
builder.setPositiveButton(R.string.ok, null);
|
||||
|
||||
return builder.create();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -62,6 +62,7 @@ import androidx.lifecycle.MutableLiveData;
|
||||
|
||||
import static androidx.lifecycle.Transformations.map;
|
||||
import static java.util.Objects.requireNonNull;
|
||||
import static java.util.concurrent.TimeUnit.DAYS;
|
||||
import static java.util.logging.Level.INFO;
|
||||
import static java.util.logging.Level.WARNING;
|
||||
import static java.util.logging.Logger.getLogger;
|
||||
@@ -74,7 +75,6 @@ import static org.briarproject.briar.android.view.TextSendController.SendState.E
|
||||
import static org.briarproject.briar.android.view.TextSendController.SendState.SENT;
|
||||
import static org.briarproject.briar.android.view.TextSendController.SendState.UNEXPECTED_TIMER;
|
||||
import static org.briarproject.briar.api.autodelete.AutoDeleteConstants.NO_AUTO_DELETE_TIMER;
|
||||
import static org.briarproject.briar.api.autodelete.AutoDeleteManager.DEFAULT_TIMER_DURATION;
|
||||
import static org.briarproject.briar.api.messaging.PrivateMessageFormat.TEXT_IMAGES;
|
||||
import static org.briarproject.briar.api.messaging.PrivateMessageFormat.TEXT_ONLY;
|
||||
|
||||
@@ -393,7 +393,7 @@ public class ConversationViewModel extends DbViewModel
|
||||
}
|
||||
|
||||
void setAutoDeleteTimerEnabled(boolean enabled) {
|
||||
long timer = enabled ? DEFAULT_TIMER_DURATION : NO_AUTO_DELETE_TIMER;
|
||||
final long timer = enabled ? DAYS.toMillis(7) : NO_AUTO_DELETE_TIMER;
|
||||
// ContactId is set before menu gets inflated and UI interaction
|
||||
final ContactId c = requireNonNull(contactId);
|
||||
runOnDbThread(() -> {
|
||||
|
||||
@@ -102,10 +102,6 @@ class ConversationVisitor implements
|
||||
text = ctx.getString(
|
||||
R.string.blogs_sharing_response_accepted_sent,
|
||||
contactName.getValue());
|
||||
} else if (r.isAutoDecline()) {
|
||||
text = ctx.getString(
|
||||
R.string.blogs_sharing_response_declined_auto,
|
||||
contactName.getValue());
|
||||
} else {
|
||||
text = ctx.getString(
|
||||
R.string.blogs_sharing_response_declined_sent,
|
||||
@@ -159,10 +155,6 @@ class ConversationVisitor implements
|
||||
text = ctx.getString(
|
||||
R.string.forum_invitation_response_accepted_sent,
|
||||
contactName.getValue());
|
||||
} else if (r.isAutoDecline()) {
|
||||
text = ctx.getString(
|
||||
R.string.forum_invitation_response_declined_auto,
|
||||
contactName.getValue());
|
||||
} else {
|
||||
text = ctx.getString(
|
||||
R.string.forum_invitation_response_declined_sent,
|
||||
@@ -217,10 +209,6 @@ class ConversationVisitor implements
|
||||
text = ctx.getString(
|
||||
R.string.groups_invitations_response_accepted_sent,
|
||||
contactName.getValue());
|
||||
} else if (r.isAutoDecline()) {
|
||||
text = ctx.getString(
|
||||
R.string.groups_invitations_response_declined_auto,
|
||||
contactName.getValue());
|
||||
} else {
|
||||
text = ctx.getString(
|
||||
R.string.groups_invitations_response_declined_sent,
|
||||
@@ -289,10 +277,6 @@ class ConversationVisitor implements
|
||||
text = ctx.getString(
|
||||
R.string.introduction_response_accepted_sent,
|
||||
introducedAuthor) + suffix;
|
||||
} else if (r.isAutoDecline()) {
|
||||
text = ctx.getString(
|
||||
R.string.introduction_response_declined_auto,
|
||||
introducedAuthor);
|
||||
} else {
|
||||
text = ctx.getString(
|
||||
R.string.introduction_response_declined_sent,
|
||||
|
||||
@@ -10,8 +10,9 @@ import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
||||
import org.briarproject.briar.android.DestroyableContext;
|
||||
import org.briarproject.briar.android.activity.ActivityComponent;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import androidx.annotation.CallSuper;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.UiThread;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentActivity;
|
||||
@@ -46,11 +47,13 @@ public abstract class BaseFragment extends Fragment
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
if (item.getItemId() == android.R.id.home) {
|
||||
requireActivity().onBackPressed();
|
||||
return true;
|
||||
switch (item.getItemId()) {
|
||||
case android.R.id.home:
|
||||
listener.onBackPressed();
|
||||
return true;
|
||||
default:
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
@UiThread
|
||||
@@ -76,7 +79,6 @@ public abstract class BaseFragment extends Fragment
|
||||
void handleException(Exception e);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
@CallSuper
|
||||
@Override
|
||||
public void runOnUiThreadUnlessDestroyed(Runnable r) {
|
||||
|
||||
@@ -411,8 +411,6 @@ public abstract class KeyAgreementActivity extends BriarActivity implements
|
||||
@UiThread
|
||||
public void onRequestPermissionsResult(int requestCode,
|
||||
String[] permissions, int[] grantResults) {
|
||||
super.onRequestPermissionsResult(requestCode, permissions,
|
||||
grantResults);
|
||||
if (requestCode != REQUEST_PERMISSION_CAMERA_LOCATION)
|
||||
throw new AssertionError();
|
||||
if (gotPermission(CAMERA, permissions, grantResults)) {
|
||||
|
||||
@@ -4,7 +4,6 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
|
||||
import java.text.DateFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
import java.util.TimeZone;
|
||||
import java.util.logging.Formatter;
|
||||
@@ -18,16 +17,6 @@ import static java.util.Locale.US;
|
||||
@NotNullByDefault
|
||||
public class BriefLogFormatter extends Formatter {
|
||||
|
||||
public static String formatLog(Formatter formatter,
|
||||
Collection<LogRecord> logRecords) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (LogRecord record : logRecords) {
|
||||
String formatted = formatter.format(record);
|
||||
sb.append(formatted).append('\n');
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private final Object lock = new Object();
|
||||
private final DateFormat dateFormat; // Locking: lock
|
||||
private final Date date; // Locking: lock
|
||||
|
||||
@@ -21,10 +21,6 @@ public class CachingLogHandler extends Handler {
|
||||
// Locking: lock
|
||||
private final Queue<LogRecord> recent = new LinkedList<>();
|
||||
|
||||
// package-private constructor
|
||||
CachingLogHandler() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void publish(LogRecord record) {
|
||||
synchronized (lock) {
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
package org.briarproject.briar.android.logging;
|
||||
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.bramble.util.AndroidUtils;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
@NotNullByDefault
|
||||
public interface LogDecrypter {
|
||||
/**
|
||||
* Returns decrypted log records from {@link AndroidUtils#getLogcatFile}
|
||||
* or null if there was an error reading the logs.
|
||||
*/
|
||||
@Nullable
|
||||
String decryptLogs(@Nullable byte[] logKey);
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
package org.briarproject.briar.android.logging;
|
||||
|
||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.bramble.api.reporting.DevConfig;
|
||||
import org.briarproject.bramble.api.transport.StreamReaderFactory;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Scanner;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import static java.util.logging.Level.WARNING;
|
||||
import static java.util.logging.Logger.getLogger;
|
||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
||||
|
||||
@NotNullByDefault
|
||||
class LogDecrypterImpl implements LogDecrypter {
|
||||
|
||||
private static final Logger LOG =
|
||||
getLogger(LogDecrypterImpl.class.getName());
|
||||
|
||||
private final DevConfig devConfig;
|
||||
private final StreamReaderFactory streamReaderFactory;
|
||||
|
||||
@Inject
|
||||
LogDecrypterImpl(DevConfig devConfig,
|
||||
StreamReaderFactory streamReaderFactory) {
|
||||
this.devConfig = devConfig;
|
||||
this.streamReaderFactory = streamReaderFactory;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String decryptLogs(@Nullable byte[] logKey) {
|
||||
if (logKey == null) return null;
|
||||
SecretKey key = new SecretKey(logKey);
|
||||
File logFile = devConfig.getLogcatFile();
|
||||
try (InputStream in = new FileInputStream(logFile)) {
|
||||
InputStream reader =
|
||||
streamReaderFactory.createLogStreamReader(in, key);
|
||||
Scanner s = new Scanner(reader);
|
||||
StringBuilder sb = new StringBuilder();
|
||||
while (s.hasNextLine()) sb.append(s.nextLine()).append("\n");
|
||||
s.close();
|
||||
return sb.toString();
|
||||
} catch (IOException e) {
|
||||
logException(LOG, WARNING, e);
|
||||
return null;
|
||||
} finally {
|
||||
//noinspection ResultOfMethodCallIgnored
|
||||
logFile.delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
package org.briarproject.briar.android.logging;
|
||||
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.bramble.util.AndroidUtils;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
@NotNullByDefault
|
||||
public interface LogEncrypter {
|
||||
/**
|
||||
* Writes encrypted log records to {@link AndroidUtils#getLogcatFile}
|
||||
* and returns the encryption key if everything went fine.
|
||||
*/
|
||||
@Nullable
|
||||
byte[] encryptLogs();
|
||||
}
|
||||
@@ -1,77 +0,0 @@
|
||||
package org.briarproject.briar.android.logging;
|
||||
|
||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.bramble.api.reporting.DevConfig;
|
||||
import org.briarproject.bramble.api.transport.StreamWriter;
|
||||
import org.briarproject.bramble.api.transport.StreamWriterFactory;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.Writer;
|
||||
import java.util.logging.Formatter;
|
||||
import java.util.logging.LogRecord;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import static java.util.logging.Level.WARNING;
|
||||
import static java.util.logging.Logger.getLogger;
|
||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
||||
|
||||
@NotNullByDefault
|
||||
class LogEncrypterImpl implements LogEncrypter {
|
||||
|
||||
private static final Logger LOG =
|
||||
getLogger(LogEncrypterImpl.class.getName());
|
||||
|
||||
private final DevConfig devConfig;
|
||||
private final CachingLogHandler logHandler;
|
||||
private final CryptoComponent crypto;
|
||||
private final StreamWriterFactory streamWriterFactory;
|
||||
|
||||
@Inject
|
||||
LogEncrypterImpl(DevConfig devConfig,
|
||||
CachingLogHandler logHandler,
|
||||
CryptoComponent crypto,
|
||||
StreamWriterFactory streamWriterFactory) {
|
||||
this.devConfig = devConfig;
|
||||
this.logHandler = logHandler;
|
||||
this.crypto = crypto;
|
||||
this.streamWriterFactory = streamWriterFactory;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public byte[] encryptLogs() {
|
||||
SecretKey logKey = crypto.generateSecretKey();
|
||||
File logFile = devConfig.getLogcatFile();
|
||||
try (OutputStream out = new FileOutputStream(logFile)) {
|
||||
StreamWriter streamWriter =
|
||||
streamWriterFactory.createLogStreamWriter(out, logKey);
|
||||
Writer writer =
|
||||
new OutputStreamWriter(streamWriter.getOutputStream());
|
||||
writeLogString(writer);
|
||||
writer.close();
|
||||
return logKey.getBytes();
|
||||
} catch (IOException e) {
|
||||
logException(LOG, WARNING, e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private void writeLogString(Writer writer) throws IOException {
|
||||
Formatter formatter = new BriefLogFormatter();
|
||||
for (LogRecord record : logHandler.getRecentLogRecords()) {
|
||||
String formatted = formatter.format(record);
|
||||
writer.append(formatted).append('\n');
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
package org.briarproject.briar.android.logging;
|
||||
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
|
||||
@Module
|
||||
public class LoggingModule {
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
CachingLogHandler provideCachingLogHandler() {
|
||||
return new CachingLogHandler();
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
LogEncrypter provideLogEncrypter(LogEncrypterImpl logEncrypter) {
|
||||
return logEncrypter;
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
LogDecrypter provideLogDecrypter(LogDecrypterImpl logDecrypter) {
|
||||
return logDecrypter;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -49,12 +49,14 @@ import androidx.annotation.DrawableRes;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.StringRes;
|
||||
import androidx.appcompat.app.ActionBar;
|
||||
import androidx.appcompat.app.ActionBarDrawerToggle;
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
import androidx.core.app.ActivityCompat;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.drawerlayout.widget.DrawerLayout;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
import androidx.fragment.app.FragmentTransaction;
|
||||
import androidx.lifecycle.MutableLiveData;
|
||||
import androidx.lifecycle.ViewModelProvider;
|
||||
import androidx.lifecycle.ViewModelProviders;
|
||||
@@ -65,7 +67,8 @@ import static android.view.View.GONE;
|
||||
import static android.view.View.VISIBLE;
|
||||
import static androidx.core.view.GravityCompat.START;
|
||||
import static androidx.drawerlayout.widget.DrawerLayout.LOCK_MODE_LOCKED_CLOSED;
|
||||
import static androidx.lifecycle.Lifecycle.State.STARTED;
|
||||
import static androidx.fragment.app.FragmentManager.POP_BACK_STACK_INCLUSIVE;
|
||||
import static java.util.Objects.requireNonNull;
|
||||
import static java.util.logging.Logger.getLogger;
|
||||
import static org.briarproject.bramble.api.lifecycle.LifecycleManager.LifecycleState.RUNNING;
|
||||
import static org.briarproject.bramble.api.plugin.Plugin.State.ACTIVE;
|
||||
@@ -142,7 +145,7 @@ public class NavDrawerActivity extends BriarActivity implements
|
||||
if (ask) showDozeDialog(getString(R.string.setup_doze_intro));
|
||||
});
|
||||
|
||||
Toolbar toolbar = setUpCustomToolbar(false);
|
||||
Toolbar toolbar = findViewById(R.id.toolbar);
|
||||
drawerLayout = findViewById(R.id.drawer_layout);
|
||||
navigation = findViewById(R.id.navigation);
|
||||
GridView transportsView = findViewById(R.id.transportsView);
|
||||
@@ -152,6 +155,11 @@ public class NavDrawerActivity extends BriarActivity implements
|
||||
startActivity(new Intent(this, TransportsActivity.class));
|
||||
});
|
||||
|
||||
setSupportActionBar(toolbar);
|
||||
ActionBar actionBar = requireNonNull(getSupportActionBar());
|
||||
actionBar.setDisplayHomeAsUpEnabled(true);
|
||||
actionBar.setHomeButtonEnabled(true);
|
||||
|
||||
drawerToggle = new ActionBarDrawerToggle(this, drawerLayout, toolbar,
|
||||
R.string.nav_drawer_open_description,
|
||||
R.string.nav_drawer_close_description) {
|
||||
@@ -175,6 +183,9 @@ public class NavDrawerActivity extends BriarActivity implements
|
||||
|
||||
if (lifecycleManager.getLifecycleState().isAfter(RUNNING)) {
|
||||
showSignOutFragment();
|
||||
} else if (state == null) {
|
||||
startFragment(ContactListFragment.newInstance(),
|
||||
R.id.nav_btn_contacts);
|
||||
}
|
||||
if (state == null) {
|
||||
// do not call this again when there's existing state
|
||||
@@ -264,6 +275,7 @@ public class NavDrawerActivity extends BriarActivity implements
|
||||
@Override
|
||||
public boolean onNavigationItemSelected(@NonNull MenuItem item) {
|
||||
drawerLayout.closeDrawer(START);
|
||||
clearBackStack();
|
||||
if (item.getItemId() == R.id.nav_btn_lock) {
|
||||
lockManager.setLocked(true);
|
||||
ActivityCompat.finishAfterTransition(this);
|
||||
@@ -283,14 +295,8 @@ public class NavDrawerActivity extends BriarActivity implements
|
||||
FragmentManager fm = getSupportFragmentManager();
|
||||
if (fm.findFragmentByTag(SignOutFragment.TAG) != null) {
|
||||
finish();
|
||||
} else if (fm.getBackStackEntryCount() == 0 &&
|
||||
fm.findFragmentByTag(ContactListFragment.TAG) == null) {
|
||||
// don't start fragments in the wrong part of lifecycle (#1904)
|
||||
if (!getLifecycle().getCurrentState().isAtLeast(STARTED)) {
|
||||
LOG.warning("Tried to start contacts fragment in state " +
|
||||
getLifecycle().getCurrentState().name());
|
||||
return;
|
||||
}
|
||||
} else if (fm.getBackStackEntryCount() == 0
|
||||
&& fm.findFragmentByTag(ContactListFragment.TAG) == null) {
|
||||
/*
|
||||
* This makes sure that the first fragment (ContactListFragment) the
|
||||
* user sees is the same as the last fragment the user sees before
|
||||
@@ -333,12 +339,30 @@ public class NavDrawerActivity extends BriarActivity implements
|
||||
startFragment(fragment);
|
||||
}
|
||||
|
||||
private void startFragment(BaseFragment f) {
|
||||
getSupportFragmentManager().beginTransaction()
|
||||
.setCustomAnimations(R.anim.fade_in, R.anim.fade_out,
|
||||
R.anim.fade_in, R.anim.fade_out)
|
||||
.replace(R.id.fragmentContainer, f, f.getUniqueTag())
|
||||
.commit();
|
||||
private void startFragment(BaseFragment fragment) {
|
||||
if (getSupportFragmentManager().getBackStackEntryCount() == 0)
|
||||
startFragment(fragment, false);
|
||||
else startFragment(fragment, true);
|
||||
}
|
||||
|
||||
private void startFragment(BaseFragment fragment,
|
||||
boolean isAddedToBackStack) {
|
||||
FragmentTransaction trans =
|
||||
getSupportFragmentManager().beginTransaction()
|
||||
.setCustomAnimations(R.anim.fade_in,
|
||||
R.anim.fade_out, R.anim.fade_in,
|
||||
R.anim.fade_out)
|
||||
.replace(R.id.fragmentContainer, fragment,
|
||||
fragment.getUniqueTag());
|
||||
if (isAddedToBackStack) {
|
||||
trans.addToBackStack(fragment.getUniqueTag());
|
||||
}
|
||||
trans.commit();
|
||||
}
|
||||
|
||||
private void clearBackStack() {
|
||||
getSupportFragmentManager().popBackStackImmediate(null,
|
||||
POP_BACK_STACK_INCLUSIVE);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -79,10 +79,9 @@ public class GroupActivity extends
|
||||
|
||||
// start with group disabled and enable when not dissolved
|
||||
setGroupEnabled(false);
|
||||
viewModel.isDissolved().observe(this, dissolved -> {
|
||||
viewModel.isDissolved().observeEvent(this, dissolved -> {
|
||||
setGroupEnabled(!dissolved);
|
||||
// only show dialog when no prior state
|
||||
if (dissolved && state == null) onGroupDissolved();
|
||||
if (dissolved) onGroupDissolved();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -154,7 +153,7 @@ public class GroupActivity extends
|
||||
|
||||
@Override
|
||||
public void onReplyClick(GroupMessageItem item) {
|
||||
Boolean isDissolved = viewModel.isDissolved().getValue();
|
||||
Boolean isDissolved = viewModel.isDissolved().getLastValue();
|
||||
if (isDissolved != null && !isDissolved) super.onReplyClick(item);
|
||||
}
|
||||
|
||||
|
||||
@@ -22,6 +22,8 @@ import org.briarproject.bramble.api.system.AndroidExecutor;
|
||||
import org.briarproject.bramble.api.system.Clock;
|
||||
import org.briarproject.briar.android.sharing.SharingController;
|
||||
import org.briarproject.briar.android.threaded.ThreadListViewModel;
|
||||
import org.briarproject.briar.android.viewmodel.LiveEvent;
|
||||
import org.briarproject.briar.android.viewmodel.MutableLiveEvent;
|
||||
import org.briarproject.briar.api.android.AndroidNotificationManager;
|
||||
import org.briarproject.briar.api.client.MessageTracker;
|
||||
import org.briarproject.briar.api.client.MessageTracker.GroupCount;
|
||||
@@ -69,8 +71,8 @@ class GroupViewModel extends ThreadListViewModel<GroupMessageItem> {
|
||||
private final MutableLiveData<PrivateGroup> privateGroup =
|
||||
new MutableLiveData<>();
|
||||
private final MutableLiveData<Boolean> isCreator = new MutableLiveData<>();
|
||||
private final MutableLiveData<Boolean> isDissolved =
|
||||
new MutableLiveData<>();
|
||||
private final MutableLiveEvent<Boolean> isDissolved =
|
||||
new MutableLiveEvent<>();
|
||||
|
||||
@Inject
|
||||
GroupViewModel(Application application,
|
||||
@@ -127,7 +129,7 @@ class GroupViewModel extends ThreadListViewModel<GroupMessageItem> {
|
||||
} else if (e instanceof GroupDissolvedEvent) {
|
||||
GroupDissolvedEvent g = (GroupDissolvedEvent) e;
|
||||
if (g.getGroupId().equals(groupId)) {
|
||||
isDissolved.setValue(true);
|
||||
isDissolved.setEvent(true);
|
||||
}
|
||||
} else {
|
||||
super.eventOccurred(e);
|
||||
@@ -162,7 +164,7 @@ class GroupViewModel extends ThreadListViewModel<GroupMessageItem> {
|
||||
loadList(txn -> {
|
||||
// check first if group is dissolved
|
||||
isDissolved
|
||||
.postValue(privateGroupManager.isDissolved(txn, groupId));
|
||||
.postEvent(privateGroupManager.isDissolved(txn, groupId));
|
||||
// now continue to load the items
|
||||
long start = now();
|
||||
List<GroupMessageHeader> headers =
|
||||
@@ -280,7 +282,7 @@ class GroupViewModel extends ThreadListViewModel<GroupMessageItem> {
|
||||
return isCreator;
|
||||
}
|
||||
|
||||
LiveData<Boolean> isDissolved() {
|
||||
LiveEvent<Boolean> isDissolved() {
|
||||
return isDissolved;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,40 +1,29 @@
|
||||
package org.briarproject.briar.android.reporting;
|
||||
|
||||
import android.app.Application;
|
||||
import android.content.Context;
|
||||
import android.os.Process;
|
||||
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.briar.android.logging.LogEncrypter;
|
||||
|
||||
import java.lang.Thread.UncaughtExceptionHandler;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static org.briarproject.briar.android.util.UiUtils.startDevReportActivity;
|
||||
|
||||
@NotNullByDefault
|
||||
class BriarExceptionHandler implements UncaughtExceptionHandler {
|
||||
public class BriarExceptionHandler implements UncaughtExceptionHandler {
|
||||
|
||||
private final Application app;
|
||||
private final LogEncrypter logEncrypter;
|
||||
private final Context ctx;
|
||||
private final long appStartTime;
|
||||
|
||||
@Inject
|
||||
BriarExceptionHandler(Application app, LogEncrypter logEncrypter) {
|
||||
this.app = app;
|
||||
this.logEncrypter = logEncrypter;
|
||||
appStartTime = System.currentTimeMillis();
|
||||
public BriarExceptionHandler(Context ctx) {
|
||||
this.ctx = ctx;
|
||||
this.appStartTime = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void uncaughtException(Thread t, Throwable e) {
|
||||
// encrypt logs to disk before handing over to new process
|
||||
// the intent has limited space, so we can't reliably store them there.
|
||||
byte[] logKey = logEncrypter.encryptLogs();
|
||||
|
||||
// activity runs in its own process, so we can kill the old one
|
||||
startDevReportActivity(app.getApplicationContext(),
|
||||
CrashReportActivity.class, e, appStartTime, logKey);
|
||||
startDevReportActivity(ctx, CrashReportActivity.class, e, appStartTime);
|
||||
Process.killProcess(Process.myPid());
|
||||
System.exit(10);
|
||||
}
|
||||
|
||||
@@ -25,6 +25,8 @@ import org.briarproject.bramble.api.Pair;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.briar.BuildConfig;
|
||||
import org.briarproject.briar.R;
|
||||
import org.briarproject.briar.android.BriarApplication;
|
||||
import org.briarproject.briar.android.logging.BriefLogFormatter;
|
||||
import org.briarproject.briar.android.reporting.ReportData.MultiReportInfo;
|
||||
import org.briarproject.briar.android.reporting.ReportData.ReportItem;
|
||||
import org.briarproject.briar.android.reporting.ReportData.SingleReportInfo;
|
||||
@@ -39,6 +41,8 @@ import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.logging.Formatter;
|
||||
import java.util.logging.LogRecord;
|
||||
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
|
||||
@@ -70,8 +74,8 @@ class BriarReportCollector {
|
||||
this.ctx = ctx;
|
||||
}
|
||||
|
||||
ReportData collectReportData(@Nullable Throwable t, long appStartTime,
|
||||
String logs) {
|
||||
public ReportData collectReportData(@Nullable Throwable t,
|
||||
long appStartTime) {
|
||||
ReportData reportData = new ReportData()
|
||||
.add(getBasicInfo(t))
|
||||
.add(getDeviceInfo());
|
||||
@@ -82,7 +86,7 @@ class BriarReportCollector {
|
||||
.add(getStorage())
|
||||
.add(getConnectivity())
|
||||
.add(getBuildConfig())
|
||||
.add(getLogcat(logs))
|
||||
.add(getLogcat())
|
||||
.add(getDeviceFeatures());
|
||||
}
|
||||
|
||||
@@ -309,8 +313,15 @@ class BriarReportCollector {
|
||||
buildConfig);
|
||||
}
|
||||
|
||||
private ReportItem getLogcat(String logs) {
|
||||
return new ReportItem("Logcat", R.string.dev_report_logcat, logs);
|
||||
private ReportItem getLogcat() {
|
||||
BriarApplication app = (BriarApplication) ctx.getApplicationContext();
|
||||
StringBuilder sb = new StringBuilder();
|
||||
Formatter formatter = new BriefLogFormatter();
|
||||
for (LogRecord record : app.getRecentLogRecords()) {
|
||||
sb.append(formatter.format(record)).append('\n');
|
||||
}
|
||||
return new ReportItem("Logcat", R.string.dev_report_logcat,
|
||||
sb.toString());
|
||||
}
|
||||
|
||||
private ReportItem getDeviceFeatures() {
|
||||
|
||||
@@ -35,7 +35,6 @@ public class CrashReportActivity extends BaseActivity
|
||||
|
||||
public static final String EXTRA_THROWABLE = "throwable";
|
||||
public static final String EXTRA_APP_START_TIME = "appStartTime";
|
||||
public static final String EXTRA_APP_LOGCAT = "logcat";
|
||||
|
||||
@Inject
|
||||
ViewModelProvider.Factory viewModelFactory;
|
||||
@@ -57,8 +56,7 @@ public class CrashReportActivity extends BaseActivity
|
||||
Intent intent = getIntent();
|
||||
Throwable t = (Throwable) intent.getSerializableExtra(EXTRA_THROWABLE);
|
||||
long appStartTime = intent.getLongExtra(EXTRA_APP_START_TIME, -1);
|
||||
byte[] logKey = intent.getByteArrayExtra(EXTRA_APP_LOGCAT);
|
||||
viewModel.init(t, appStartTime, logKey);
|
||||
viewModel.init(t, appStartTime);
|
||||
viewModel.getShowReport().observeEvent(this, show -> {
|
||||
if (show) displayFragment(true);
|
||||
});
|
||||
|
||||
@@ -15,8 +15,4 @@ public abstract class DevReportModule {
|
||||
@ViewModelKey(ReportViewModel.class)
|
||||
abstract ViewModel bindReportViewModel(ReportViewModel reportViewModel);
|
||||
|
||||
@Binds
|
||||
abstract Thread.UncaughtExceptionHandler bindUncaughtExceptionHandler(
|
||||
BriarExceptionHandler handler);
|
||||
|
||||
}
|
||||
|
||||
@@ -11,9 +11,6 @@ import org.briarproject.bramble.api.plugin.TorConstants;
|
||||
import org.briarproject.bramble.api.reporting.DevReporter;
|
||||
import org.briarproject.bramble.util.AndroidUtils;
|
||||
import org.briarproject.briar.R;
|
||||
import org.briarproject.briar.android.logging.BriefLogFormatter;
|
||||
import org.briarproject.briar.android.logging.CachingLogHandler;
|
||||
import org.briarproject.briar.android.logging.LogDecrypter;
|
||||
import org.briarproject.briar.android.reporting.ReportData.MultiReportInfo;
|
||||
import org.briarproject.briar.android.viewmodel.LiveEvent;
|
||||
import org.briarproject.briar.android.viewmodel.MutableLiveEvent;
|
||||
@@ -22,7 +19,6 @@ import org.json.JSONException;
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.util.UUID;
|
||||
import java.util.logging.Formatter;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.inject.Inject;
|
||||
@@ -40,16 +36,13 @@ import static java.util.logging.Logger.getLogger;
|
||||
import static org.briarproject.bramble.api.plugin.Plugin.State.ACTIVE;
|
||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
||||
import static org.briarproject.bramble.util.StringUtils.isNullOrEmpty;
|
||||
import static org.briarproject.briar.android.logging.BriefLogFormatter.formatLog;
|
||||
|
||||
@NotNullByDefault
|
||||
class ReportViewModel extends AndroidViewModel {
|
||||
public class ReportViewModel extends AndroidViewModel {
|
||||
|
||||
private static final Logger LOG =
|
||||
getLogger(ReportViewModel.class.getName());
|
||||
|
||||
private final CachingLogHandler logHandler;
|
||||
private final LogDecrypter logDecrypter;
|
||||
private final BriarReportCollector collector;
|
||||
private final DevReporter reporter;
|
||||
private final PluginManager pluginManager;
|
||||
@@ -65,39 +58,18 @@ class ReportViewModel extends AndroidViewModel {
|
||||
private boolean isFeedback;
|
||||
|
||||
@Inject
|
||||
ReportViewModel(@NonNull Application application,
|
||||
CachingLogHandler logHandler,
|
||||
LogDecrypter logDecrypter,
|
||||
DevReporter reporter,
|
||||
PluginManager pluginManager) {
|
||||
public ReportViewModel(@NonNull Application application,
|
||||
DevReporter reporter, PluginManager pluginManager) {
|
||||
super(application);
|
||||
collector = new BriarReportCollector(application);
|
||||
this.logHandler = logHandler;
|
||||
this.logDecrypter = logDecrypter;
|
||||
this.collector = new BriarReportCollector(application);
|
||||
this.reporter = reporter;
|
||||
this.pluginManager = pluginManager;
|
||||
}
|
||||
|
||||
void init(@Nullable Throwable t, long appStartTime,
|
||||
@Nullable byte[] logKey) {
|
||||
void init(@Nullable Throwable t, long appStartTime) {
|
||||
isFeedback = t == null;
|
||||
if (reportData.getValue() == null) new SingleShotAndroidExecutor(() -> {
|
||||
String decryptedLogs;
|
||||
if (isFeedback) {
|
||||
Formatter formatter = new BriefLogFormatter();
|
||||
decryptedLogs =
|
||||
formatLog(formatter, logHandler.getRecentLogRecords());
|
||||
} else {
|
||||
decryptedLogs = logDecrypter.decryptLogs(logKey);
|
||||
if (decryptedLogs == null) {
|
||||
// error decrypting logs, get logs from this process
|
||||
Formatter formatter = new BriefLogFormatter();
|
||||
decryptedLogs = formatLog(formatter,
|
||||
logHandler.getRecentLogRecords());
|
||||
}
|
||||
}
|
||||
ReportData data =
|
||||
collector.collectReportData(t, appStartTime, decryptedLogs);
|
||||
ReportData data = collector.collectReportData(t, appStartTime);
|
||||
reportData.postValue(data);
|
||||
}).start();
|
||||
}
|
||||
@@ -138,8 +110,8 @@ class ReportViewModel extends AndroidViewModel {
|
||||
}
|
||||
|
||||
/**
|
||||
* The content of the report that will be loaded after
|
||||
* {@link #init(Throwable, long, byte[])} was called.
|
||||
* The content of the report
|
||||
* that will be loaded after {@link #init(Throwable, long)} was called.
|
||||
*/
|
||||
LiveData<ReportData> getReportData() {
|
||||
return reportData;
|
||||
|
||||
@@ -9,13 +9,10 @@ import android.view.View;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.bumptech.glide.load.engine.DiskCacheStrategy;
|
||||
|
||||
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
||||
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
||||
import org.briarproject.briar.R;
|
||||
import org.briarproject.briar.android.activity.BaseActivity;
|
||||
import org.briarproject.briar.android.conversation.glide.GlideApp;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
@@ -35,7 +32,7 @@ public class ConfirmAvatarDialogFragment extends DialogFragment {
|
||||
|
||||
@Inject
|
||||
ViewModelProvider.Factory viewModelFactory;
|
||||
private SettingsViewModel viewModel;
|
||||
private SettingsViewModel settingsViewModel;
|
||||
|
||||
private static final String ARG_URI = "uri";
|
||||
private Uri uri;
|
||||
@@ -54,9 +51,6 @@ public class ConfirmAvatarDialogFragment extends DialogFragment {
|
||||
public void onAttach(Context ctx) {
|
||||
super.onAttach(ctx);
|
||||
((BaseActivity) requireActivity()).getActivityComponent().inject(this);
|
||||
ViewModelProvider provider =
|
||||
new ViewModelProvider(requireActivity(), viewModelFactory);
|
||||
viewModel = provider.get(SettingsViewModel.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -66,34 +60,32 @@ public class ConfirmAvatarDialogFragment extends DialogFragment {
|
||||
uri = Uri.parse(argUri);
|
||||
|
||||
FragmentActivity activity = requireActivity();
|
||||
LayoutInflater inflater = LayoutInflater.from(activity);
|
||||
|
||||
ViewModelProvider provider =
|
||||
new ViewModelProvider(activity, viewModelFactory);
|
||||
settingsViewModel = provider.get(SettingsViewModel.class);
|
||||
|
||||
AlertDialog.Builder builder =
|
||||
new AlertDialog.Builder(activity, R.style.BriarDialogTheme);
|
||||
|
||||
LayoutInflater inflater = LayoutInflater.from(getContext());
|
||||
final View view =
|
||||
inflater.inflate(R.layout.fragment_confirm_avatar_dialog, null);
|
||||
builder.setView(view);
|
||||
|
||||
builder.setTitle(R.string.dialog_confirm_profile_picture_title);
|
||||
builder.setNegativeButton(R.string.cancel, null);
|
||||
builder.setPositiveButton(R.string.change,
|
||||
(dialog, id) -> settingsViewModel.setAvatar(uri));
|
||||
|
||||
ImageView imageView = view.findViewById(R.id.image);
|
||||
imageView.setImageURI(uri);
|
||||
|
||||
TextView textViewUserName = view.findViewById(R.id.username);
|
||||
settingsViewModel.getOwnIdentityInfo().observe(activity,
|
||||
us -> textViewUserName.setText(us.getLocalAuthor().getName()));
|
||||
|
||||
GlideApp.with(imageView)
|
||||
.load(uri)
|
||||
.diskCacheStrategy(DiskCacheStrategy.NONE)
|
||||
.error(R.drawable.ic_image_broken)
|
||||
.into(imageView)
|
||||
.waitForLayout();
|
||||
|
||||
// we can't use getViewLifecycleOwner() here
|
||||
// as this fragment technically doesn't have a view
|
||||
viewModel.getOwnIdentityInfo().observe(activity, us ->
|
||||
textViewUserName.setText(us.getLocalAuthor().getName())
|
||||
);
|
||||
|
||||
int theme = R.style.BriarDialogTheme;
|
||||
return new AlertDialog.Builder(activity, theme)
|
||||
.setView(view)
|
||||
.setTitle(R.string.dialog_confirm_profile_picture_title)
|
||||
.setNegativeButton(R.string.cancel, null)
|
||||
.setPositiveButton(R.string.change, (d, id) ->
|
||||
viewModel.setAvatar(uri)
|
||||
)
|
||||
.create();
|
||||
return builder.create();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -79,7 +79,7 @@ class SettingsViewModel extends AndroidViewModel {
|
||||
return ownIdentityInfo;
|
||||
}
|
||||
|
||||
LiveEvent<Boolean> getSetAvatarFailed() {
|
||||
public LiveEvent<Boolean> getSetAvatarFailed() {
|
||||
return setAvatarFailed;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package org.briarproject.briar.android.splash;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
@@ -8,7 +7,6 @@ import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
|
||||
import org.briarproject.briar.R;
|
||||
import org.briarproject.briar.android.Localizer;
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
|
||||
@@ -29,13 +27,6 @@ public class ExpiredActivity extends AppCompatActivity
|
||||
findViewById(R.id.download_briar_button).setOnClickListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void attachBaseContext(Context base) {
|
||||
super.attachBaseContext(
|
||||
Localizer.getInstance().setLocale(base));
|
||||
Localizer.getInstance().setLocale(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
Uri uri = Uri.parse("https://briarproject.org/download.html");
|
||||
|
||||
@@ -197,10 +197,6 @@ public abstract class ThreadListViewModel<I extends ThreadItem>
|
||||
*/
|
||||
@UiThread
|
||||
protected void addItem(I item, boolean scrollToItem) {
|
||||
// If items haven't loaded, we need to wait until they have.
|
||||
// Since this was a R/W DB transaction, the load will pick up this item.
|
||||
if (items.getValue() == null) return;
|
||||
|
||||
messageTree.add(item);
|
||||
if (scrollToItem) this.scrollToItem.set(item.getId());
|
||||
items.setValue(new LiveResult<>(messageTree.depthFirstOrder()));
|
||||
|
||||
@@ -6,7 +6,6 @@ import android.app.KeyguardManager;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface.OnClickListener;
|
||||
import android.content.Intent;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.net.Uri;
|
||||
import android.os.PowerManager;
|
||||
@@ -77,7 +76,6 @@ import static android.text.format.DateUtils.FORMAT_ABBREV_TIME;
|
||||
import static android.text.format.DateUtils.FORMAT_SHOW_DATE;
|
||||
import static android.text.format.DateUtils.FORMAT_SHOW_TIME;
|
||||
import static android.text.format.DateUtils.FORMAT_SHOW_YEAR;
|
||||
import static android.text.format.DateUtils.HOUR_IN_MILLIS;
|
||||
import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
|
||||
import static android.text.format.DateUtils.WEEK_IN_MILLIS;
|
||||
import static android.text.format.DateUtils.YEAR_IN_MILLIS;
|
||||
@@ -99,7 +97,6 @@ import static java.util.concurrent.TimeUnit.DAYS;
|
||||
import static org.briarproject.bramble.util.AndroidUtils.getSupportedImageContentTypes;
|
||||
import static org.briarproject.briar.BuildConfig.APPLICATION_ID;
|
||||
import static org.briarproject.briar.android.TestingConstants.EXPIRY_DATE;
|
||||
import static org.briarproject.briar.android.reporting.CrashReportActivity.EXTRA_APP_LOGCAT;
|
||||
import static org.briarproject.briar.android.reporting.CrashReportActivity.EXTRA_APP_START_TIME;
|
||||
import static org.briarproject.briar.android.reporting.CrashReportActivity.EXTRA_THROWABLE;
|
||||
|
||||
@@ -169,37 +166,6 @@ public class UiUtils {
|
||||
return DateUtils.formatDateTime(ctx, time, flags);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the given duration in a human-friendly format. For example,
|
||||
* "7 days" or "1 hour". Returns only the largest meaningful unit of time,
|
||||
* from days up to minutes.
|
||||
*/
|
||||
public static String formatDuration(Context ctx, long millis) {
|
||||
Resources r = ctx.getResources();
|
||||
if (millis >= DAY_IN_MILLIS) {
|
||||
int days = (int) (millis / DAY_IN_MILLIS);
|
||||
int rest = (int) (millis % DAY_IN_MILLIS);
|
||||
String dayStr =
|
||||
r.getQuantityString(R.plurals.duration_days, days, days);
|
||||
if (rest < HOUR_IN_MILLIS / 2) return dayStr;
|
||||
else return dayStr + " " + formatDuration(ctx, rest);
|
||||
} else if (millis >= HOUR_IN_MILLIS) {
|
||||
int hours = (int) (millis / HOUR_IN_MILLIS);
|
||||
int rest = (int) (millis % HOUR_IN_MILLIS);
|
||||
String hourStr =
|
||||
r.getQuantityString(R.plurals.duration_hours, hours, hours);
|
||||
if (rest < MINUTE_IN_MILLIS / 2) return hourStr;
|
||||
else return hourStr + " " + formatDuration(ctx, rest);
|
||||
} else {
|
||||
int minutes =
|
||||
(int) ((millis + MINUTE_IN_MILLIS / 2) / MINUTE_IN_MILLIS);
|
||||
// anything less than one minute is shown as one minute
|
||||
if (minutes < 1) minutes = 1;
|
||||
return r.getQuantityString(R.plurals.duration_minutes, minutes,
|
||||
minutes);
|
||||
}
|
||||
}
|
||||
|
||||
public static long getDaysUntilExpiry() {
|
||||
long now = System.currentTimeMillis();
|
||||
return (EXPIRY_DATE - now) / DAYS.toMillis(1);
|
||||
@@ -391,17 +357,16 @@ public class UiUtils {
|
||||
}
|
||||
|
||||
public static void triggerFeedback(Context ctx) {
|
||||
startDevReportActivity(ctx, FeedbackActivity.class, null, null, null);
|
||||
startDevReportActivity(ctx, FeedbackActivity.class, null, null);
|
||||
}
|
||||
|
||||
public static void startDevReportActivity(Context ctx,
|
||||
Class<? extends FragmentActivity> activity, @Nullable Throwable t,
|
||||
@Nullable Long appStartTime, @Nullable byte[] logKey) {
|
||||
@Nullable Long appStartTime) {
|
||||
final Intent dialogIntent = new Intent(ctx, activity);
|
||||
dialogIntent.setFlags(FLAG_ACTIVITY_NEW_TASK);
|
||||
dialogIntent.putExtra(EXTRA_THROWABLE, t);
|
||||
dialogIntent.putExtra(EXTRA_APP_START_TIME, appStartTime);
|
||||
dialogIntent.putExtra(EXTRA_APP_LOGCAT, logKey);
|
||||
ctx.startActivity(dialogIntent);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,66 +0,0 @@
|
||||
package org.briarproject.briar.android.widget;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.briar.R;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.StringRes;
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
import androidx.fragment.app.DialogFragment;
|
||||
|
||||
@NotNullByDefault
|
||||
public class OnboardingFullDialogFragment extends DialogFragment {
|
||||
|
||||
public final static String TAG =
|
||||
OnboardingFullDialogFragment.class.getName();
|
||||
|
||||
private final static String RES_TITLE = "resTitle";
|
||||
private final static String RES_CONTENT = "resContent";
|
||||
|
||||
public static OnboardingFullDialogFragment newInstance(@StringRes int title,
|
||||
@StringRes int content) {
|
||||
Bundle args = new Bundle();
|
||||
args.putInt(RES_TITLE, title);
|
||||
args.putInt(RES_CONTENT, content);
|
||||
OnboardingFullDialogFragment f = new OnboardingFullDialogFragment();
|
||||
f.setArguments(args);
|
||||
return f;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setStyle(DialogFragment.STYLE_NORMAL,
|
||||
R.style.BriarFullScreenDialogTheme);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater,
|
||||
@Nullable ViewGroup container,
|
||||
@Nullable Bundle savedInstanceState) {
|
||||
View view = inflater.inflate(R.layout.fragment_onboarding_full,
|
||||
container, false);
|
||||
|
||||
Bundle args = requireArguments();
|
||||
|
||||
Toolbar toolbar = view.findViewById(R.id.toolbar);
|
||||
toolbar.setNavigationOnClickListener(v -> dismiss());
|
||||
toolbar.setTitle(args.getInt(RES_TITLE));
|
||||
|
||||
TextView contentView = view.findViewById(R.id.contentView);
|
||||
contentView.setText(args.getInt(RES_CONTENT));
|
||||
|
||||
view.findViewById(R.id.button).setOnClickListener(v -> dismiss());
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -39,8 +39,7 @@ public interface AndroidNotificationManager {
|
||||
String BLOG_CHANNEL_ID = "blogs";
|
||||
// Channels are sorted by channel ID in the Settings app, so use IDs
|
||||
// that will sort below the main channels such as contacts
|
||||
String ONGOING_CHANNEL_OLD_ID = "zForegroundService";
|
||||
String ONGOING_CHANNEL_ID = "zForegroundService2";
|
||||
String ONGOING_CHANNEL_ID = "zForegroundService";
|
||||
String FAILURE_CHANNEL_ID = "zStartupFailure";
|
||||
String REMINDER_CHANNEL_ID = "zSignInReminder";
|
||||
|
||||
|
||||
@@ -8,7 +8,6 @@ import androidx.lifecycle.LiveData;
|
||||
public interface LockManager {
|
||||
|
||||
String ACTION_LOCK = "lock";
|
||||
String EXTRA_PID = "PID";
|
||||
|
||||
/**
|
||||
* Stops the inactivity timer when the user interacts with the app.
|
||||
|
||||
@@ -52,17 +52,16 @@
|
||||
android:layout_height="0dp"
|
||||
android:contentDescription="@string/close"
|
||||
android:scaleType="center"
|
||||
app:srcCompat="@drawable/ic_close"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:srcCompat="@drawable/ic_close"
|
||||
app:tint="@color/briar_text_tertiary_inverse" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<androidx.fragment.app.FragmentContainerView
|
||||
<FrameLayout
|
||||
android:id="@+id/fragmentContainer"
|
||||
android:name="org.briarproject.briar.android.contact.ContactListFragment"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
<ScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:paddingHorizontal="@dimen/margin_large"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
@@ -42,25 +43,24 @@
|
||||
|
||||
<androidx.appcompat.widget.SwitchCompat
|
||||
android:id="@+id/switchDisappearingMessages"
|
||||
android:layout_width="0dp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="16dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:enabled="false"
|
||||
android:text="@string/disappearing_messages_summary"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/imageViewBomb"
|
||||
tools:enabled="true" />
|
||||
app:layout_constraintTop_toBottomOf="@+id/imageViewBomb" />
|
||||
|
||||
<Button
|
||||
<TextView
|
||||
android:id="@+id/buttonLearnMore"
|
||||
style="@style/BriarButtonFlat.Positive"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="16dp"
|
||||
android:layout_marginTop="@dimen/margin_large"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:text="@string/learn_more"
|
||||
android:textAllCaps="false"
|
||||
android:textSize="14sp"
|
||||
android:textColor="@color/briar_text_link"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/switchDisappearingMessages" />
|
||||
|
||||
@@ -68,4 +68,4 @@
|
||||
|
||||
</ScrollView>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:paddingHorizontal="?dialogPreferredPadding">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/disappearing_messages_explanation_long"
|
||||
android:textColor="?android:attr/textColorSecondary"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
</ScrollView>
|
||||
@@ -112,6 +112,7 @@
|
||||
android:textColor="@color/briar_primary"
|
||||
android:textIsSelectable="true"
|
||||
android:textSize="18sp"
|
||||
android:typeface="monospace"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/yourLinkIcon"
|
||||
|
||||
@@ -139,10 +139,10 @@
|
||||
<Space
|
||||
android:id="@+id/space"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constrainedHeight="true"
|
||||
android:layout_height="0dp"
|
||||
app:layout_constraintBottom_toTopOf="@+id/addButton"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHeight_default="wrap"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/contactNameLayout" />
|
||||
|
||||
@@ -173,4 +173,4 @@
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</ScrollView>
|
||||
</ScrollView>
|
||||
@@ -1,69 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<androidx.appcompat.widget.Toolbar
|
||||
android:id="@+id/toolbar"
|
||||
style="@style/BriarToolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:elevation="4dp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:navigationIcon="@drawable/abc_ic_ab_back_material"
|
||||
tools:title="Onboarding Fullscreen Dialog" />
|
||||
|
||||
<ScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/briar_primary"
|
||||
android:fillViewport="true">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/imageView"
|
||||
android:layout_width="64dp"
|
||||
android:layout_height="64dp"
|
||||
android:layout_margin="16dp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:srcCompat="@drawable/ic_info_white"
|
||||
app:tint="@color/briar_text_secondary_inverse"
|
||||
tools:ignore="ContentDescription" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/contentView"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="16dp"
|
||||
android:textColor="@color/briar_text_secondary_inverse"
|
||||
app:layout_constraintBottom_toTopOf="@+id/button"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/imageView"
|
||||
app:layout_constraintVertical_bias="0.0"
|
||||
tools:text="@tools:sample/lorem/random" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/button"
|
||||
style="@style/BriarButtonFlat.Positive"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/got_it"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</ScrollView>
|
||||
|
||||
</LinearLayout>
|
||||
@@ -5,28 +5,27 @@
|
||||
<group android:checkableBehavior="single">
|
||||
<item
|
||||
android:id="@+id/nav_btn_contacts"
|
||||
android:checked="true"
|
||||
android:icon="@drawable/ic_contacts"
|
||||
android:title="@string/contact_list_button" />
|
||||
android:title="@string/contact_list_button"/>
|
||||
<item
|
||||
android:id="@+id/nav_btn_groups"
|
||||
android:icon="@drawable/ic_group"
|
||||
android:title="@string/groups_button" />
|
||||
android:title="@string/groups_button"/>
|
||||
<item
|
||||
android:id="@+id/nav_btn_forums"
|
||||
android:icon="@drawable/ic_forums_black_24dp"
|
||||
android:title="@string/forums_button" />
|
||||
android:title="@string/forums_button"/>
|
||||
<item
|
||||
android:id="@+id/nav_btn_blogs"
|
||||
android:icon="@drawable/blogs"
|
||||
android:title="@string/blogs_button" />
|
||||
android:title="@string/blogs_button"/>
|
||||
</group>
|
||||
|
||||
<group android:checkableBehavior="single">
|
||||
<item
|
||||
android:id="@+id/nav_btn_settings"
|
||||
android:icon="@drawable/ic_settings_black"
|
||||
android:title="@string/settings_button" />
|
||||
android:title="@string/settings_button"/>
|
||||
<item
|
||||
android:id="@+id/nav_btn_lock"
|
||||
android:icon="@drawable/startup_lock"
|
||||
@@ -36,7 +35,7 @@
|
||||
<item
|
||||
android:id="@+id/nav_btn_signout"
|
||||
android:icon="@drawable/ic_signout"
|
||||
android:title="@string/sign_out_button" />
|
||||
android:title="@string/sign_out_button"/>
|
||||
</group>
|
||||
|
||||
</menu>
|
||||
@@ -67,11 +67,8 @@
|
||||
<string name="lock_button">قفل کردن برنامه</string>
|
||||
<string name="settings_button">تنظیمات</string>
|
||||
<string name="sign_out_button">خروج</string>
|
||||
<string name="transports_onboarding_text">برای کنترل چگونگی اتصال Briar (برایر) به مخاطبین خود، اینجا را لمس کنید.</string>
|
||||
<!--Transports: Tor-->
|
||||
<string name="transport_tor">اینترنت</string>
|
||||
<string name="tor_device_status_online_wifi">تلفن شما از طریق Wi-Fi به اینترنت دسترسی دارد.</string>
|
||||
<string name="tor_device_status_online_mobile">تلفن شما از طریق دیتا سیمکارت به اینترنت دسترسی دارد.</string>
|
||||
<string name="tor_device_status_offline">تلفن شما دارای دسترسی اینترنتی نیست</string>
|
||||
<string name="tor_plugin_status_enabling">Briar در حال اتصال به اینترنت می باشد</string>
|
||||
<string name="tor_plugin_status_active">Briar به اینترنت متصل شد</string>
|
||||
@@ -465,10 +462,6 @@
|
||||
برای وارد کردن خوراک روی آیکون + ضربه بزنید</string>
|
||||
<string name="blogs_rss_feeds_manage_error">مشکلی با بارگذاری فیدهای شما وجود داشت. لطفا بعدا امتحان کنید.</string>
|
||||
<!--Settings Profile Picture-->
|
||||
<string name="change_profile_picture">برای تغییر تصویر نمایه خود اینجا را لمس کنید.</string>
|
||||
<string name="dialog_confirm_profile_picture_title">تغییر تصویر نمایه</string>
|
||||
<string name="dialog_confirm_profile_picture_remark">تنها مخاطبین شما میتوانند تصویر نمایه شما را مشاهده کنند.</string>
|
||||
<string name="change_profile_picture_failed_message">تاسفیم اما هنگام بروزرسانی تصویر نمایه شما مشکلی رخ داد.</string>
|
||||
<!--Settings Display-->
|
||||
<string name="pref_language_title">زبان و منطقه</string>
|
||||
<string name="pref_language_changed">این تنظیمات زمانی که Briar (برایر) را ری استارت کنید تاثیر خود را می گذارند. لطفا خارج شوید و Briar (برایر) را دوباره راه اندازی کنید.</string>
|
||||
@@ -578,20 +571,17 @@
|
||||
<string name="include_debug_report_feedback">قرار دادن داده های ناشناس درباره این دستگاه</string>
|
||||
<string name="dev_report_basic_info">اطلاعات پایه</string>
|
||||
<string name="dev_report_device_info">اطلاعات دستگاه</string>
|
||||
<string name="dev_report_stacktrace">Stacktrace</string>
|
||||
<string name="dev_report_time_info">اطلاعات زمانی</string>
|
||||
<string name="dev_report_memory">حافظه</string>
|
||||
<string name="dev_report_storage">حافظه</string>
|
||||
<string name="dev_report_connectivity">اتصال</string>
|
||||
<string name="dev_report_build_config">پیکربندی ساخت</string>
|
||||
<string name="dev_report_logcat">لاگ برنامه</string>
|
||||
<string name="dev_report_device_features">ویژگیهای دستگاه</string>
|
||||
<string name="send_report">ارسال گزارش</string>
|
||||
<string name="close">بستن</string>
|
||||
<string name="dev_report_sending">در حال فرستادن نظر...</string>
|
||||
<string name="dev_report_sent">بازخورد ارسال شد</string>
|
||||
<string name="dev_report_saved">گزارش ذخیره شد. دفعه بعدی که وارد Briar (برایر) شدید فرستاده خواهد شد.</string>
|
||||
<string name="dev_report_error">خطا در ارسال گزارش</string>
|
||||
<!--Sign Out-->
|
||||
<string name="progress_title_logout">خروج از Briar (برایر)...</string>
|
||||
<!--Screen Filters & Tapjacking-->
|
||||
@@ -601,9 +591,7 @@
|
||||
این برنامه ها ممکن است روی Briar (برایر) قرار گرفته باشند:
|
||||
|
||||
%1$s</string>
|
||||
<string name="screen_filter_body_api_30">برنامه دیگری بر روی برنامه Briar (برایر) قرار دارد. برای محافظت از امنیت شما، Briar (برایر) هنگامی که برنامه دیگری روی آن باز است، به لمس پاسخ نخواهد داد. \n\nبرای یافتن برنامه مذکور، برنامههای زیر را بررسی کنید.</string>
|
||||
<string name="screen_filter_allow">به این برنامه ها اجازه بده تا روی Briar (برایر) قرار بگیرند</string>
|
||||
<string name="screen_filter_review_apps">بررسی برنامهها</string>
|
||||
<!--Permission Requests-->
|
||||
<string name="permission_camera_title">دسترسی به دوربین</string>
|
||||
<string name="permission_camera_request_body">برای اسکن کردن کد کیوآر دسترسی به دوربین لازم است.</string>
|
||||
@@ -620,7 +608,6 @@ Briar (برایر) موقعیت شما را ذخیره نمیکند و آن
|
||||
<string name="permission_camera_denied_body">شما دسترسی به دوربین را رد کرده اید، اما افزودن مخاطب نیاز به دوربین دارد.
|
||||
|
||||
لطفا اجازه دسترسی را بدهید.</string>
|
||||
<string name="permission_location_denied_body">شما دسترسی به موقعیت خود را ندادهاید اما Briar (برایر) برای یافتن دستگاههای بلوتوث نیاز به این دسترسی دارد.\n\nلطفا این دسترسی را فراهم کنید.</string>
|
||||
<string name="qr_code">کد کیوآر</string>
|
||||
<string name="show_qr_code_fullscreen">نمایش کد کیوآر به صورت فول اسکرین</string>
|
||||
<!--App Locking-->
|
||||
@@ -631,7 +618,6 @@ Briar (برایر) موقعیت شما را ذخیره نمیکند و آن
|
||||
<string name="lock_is_locked">Briar (برایر) قفل می باشد</string>
|
||||
<string name="lock_tap_to_unlock">برای آنلاک کردن کلیک کنید</string>
|
||||
<!--Connections Screen-->
|
||||
<string name="transports_help_text">Briar (برایر) میتواند از طریق اینترنت، Wi-Fi و یا بلوتوث به مخاطبین شما متصل گردد.\n\nارتباط با اینترنت از طریق شبکهی تور صورت میپذیرد.\n\nاگر دسترسی به مخاطب شما از روشهای مختلفی ممکن باشد، Briar (برایر) به صورت موازی از آنها استفاده خواهد کرد.</string>
|
||||
<!--Screenshots-->
|
||||
<!--This is a name to be used in screenshots. Feel free to change it to a local name.-->
|
||||
<string name="screenshot_alice">آلیس</string>
|
||||
|
||||
@@ -425,10 +425,6 @@
|
||||
<string name="blogs_rss_feeds_manage_empty_state">Sen fontes RSS que mostrar\n\nToque na icona + para importar unha fonte</string>
|
||||
<string name="blogs_rss_feeds_manage_error">Aconteceu un problema ao cargar as súas fontes. Por favor, inténteo máis tarde.</string>
|
||||
<!--Settings Profile Picture-->
|
||||
<string name="change_profile_picture">Toca para cambiar a túa imaxe de perfil</string>
|
||||
<string name="dialog_confirm_profile_picture_title">Mudar imaxe de perfil</string>
|
||||
<string name="dialog_confirm_profile_picture_remark">Só os teus contactos poden ver a túa imaxe de perfil</string>
|
||||
<string name="change_profile_picture_failed_message">Lamentámolo, pero algo fallou cando intentamos actualizar a túa imaxe de pefil</string>
|
||||
<!--Settings Display-->
|
||||
<string name="pref_language_title">Idioma & rexión</string>
|
||||
<string name="pref_language_changed">Este axuste terá efecto cando reinicie Briar. Por favor desconecte e volte a iniciar Briar.</string>
|
||||
|
||||
@@ -449,10 +449,6 @@
|
||||
<string name="blogs_rss_feeds_manage_empty_state">אין הזנות RSS להראות\n\nהקש על הצלמית + כדי לייבא הזנה</string>
|
||||
<string name="blogs_rss_feeds_manage_error">הייתה בעיה בטעינת ההזנות שלך. אנא נסה שוב מאוחר יותר.</string>
|
||||
<!--Settings Profile Picture-->
|
||||
<string name="change_profile_picture">הקש כדי לשנות את תמונת הפרופיל שלך</string>
|
||||
<string name="dialog_confirm_profile_picture_title">שנה תמונת פרופיל</string>
|
||||
<string name="dialog_confirm_profile_picture_remark">רק אנשי הקשר שלך יכולים לראות את תמונת הפרופיל שלך</string>
|
||||
<string name="change_profile_picture_failed_message">אנו מצטערים משהו השתבש בעת עדכון תמונת הפרופיל שלך</string>
|
||||
<!--Settings Display-->
|
||||
<string name="pref_language_title">שפה ואזור</string>
|
||||
<string name="pref_language_changed">הגדרה זו תיכנס לתוקף כשתפעיל מחדש את Briar. אנא התנתק והפעל מחדש את Briar.</string>
|
||||
@@ -562,7 +558,6 @@
|
||||
<string name="include_debug_report_feedback">כלול נתונים אלמוניים לגבי מכשיר זה</string>
|
||||
<string name="dev_report_basic_info">מידע בסיסי</string>
|
||||
<string name="dev_report_device_info">מידע מכשיר</string>
|
||||
<string name="dev_report_stacktrace">מחסנית עקיבה (Stacktrace)</string>
|
||||
<string name="dev_report_time_info">מידע זמן</string>
|
||||
<string name="dev_report_memory">זיכרון</string>
|
||||
<string name="dev_report_storage">אחסון</string>
|
||||
|
||||
@@ -425,10 +425,6 @@
|
||||
<string name="blogs_rss_feeds_manage_empty_state">Engin RSS-streymi til að birta\n\nÝttu á + táknið til að flytja inn streymi</string>
|
||||
<string name="blogs_rss_feeds_manage_error">Vandamál hefur komið upp með að hlaða inn streymunum þínum. Reyndu aftur síðar.</string>
|
||||
<!--Settings Profile Picture-->
|
||||
<string name="change_profile_picture">Ýttu til að skipta um auðkennismyndina þína</string>
|
||||
<string name="dialog_confirm_profile_picture_title">Skipta um auðkennismynd</string>
|
||||
<string name="dialog_confirm_profile_picture_remark">Einungis tengiliðirnir þínir geta séð auðkennismyndina þína</string>
|
||||
<string name="change_profile_picture_failed_message">Því miður, eitthvað fór úrskeiðis við að uppfæra auðkennismyndina þína.</string>
|
||||
<!--Settings Display-->
|
||||
<string name="pref_language_title">Tungumál og landsvæði</string>
|
||||
<string name="pref_language_changed">Þessi stilling tekur gildi í næst þegar þú skráir þig inn í Briar. Skráðu þig út og endurræstu Briar.</string>
|
||||
|
||||
@@ -425,10 +425,6 @@
|
||||
<string name="blogs_rss_feeds_manage_empty_state">Nessun feed RSS da mostrare\n\nClicca l\'icona + per importare un feed</string>
|
||||
<string name="blogs_rss_feeds_manage_error">C\'è stato un problema nel caricare i tuoi feeds. Per favore riprova fra poco.</string>
|
||||
<!--Settings Profile Picture-->
|
||||
<string name="change_profile_picture">Tocca per cambiare l\'immagine del profilo</string>
|
||||
<string name="dialog_confirm_profile_picture_title">Cambia immagine profilo</string>
|
||||
<string name="dialog_confirm_profile_picture_remark">Solo i tuoi contatti possono vedere l\'immagine del profilo</string>
|
||||
<string name="change_profile_picture_failed_message">Spiacenti, qualcosa è andato storto aggiornando la tua foto del profilo.</string>
|
||||
<!--Settings Display-->
|
||||
<string name="pref_language_title">Lingua & regione</string>
|
||||
<string name="pref_language_changed">Questa impostazione avrà effetto quando riavvierai Briar. Per favore, esci e riavvia Briar.</string>
|
||||
|
||||
@@ -425,10 +425,6 @@
|
||||
<string name="blogs_rss_feeds_manage_empty_state">Geen RSS-feeds om te tonen\n\nTik op het +-icoon om een feed te importeren</string>
|
||||
<string name="blogs_rss_feeds_manage_error">Er was een probleem met het laden van je feeds. Probeer het alsjeblieft later nog een keer.</string>
|
||||
<!--Settings Profile Picture-->
|
||||
<string name="change_profile_picture">Tik om je profielfoto te wijzigen</string>
|
||||
<string name="dialog_confirm_profile_picture_title">Wijzig profielfoto</string>
|
||||
<string name="dialog_confirm_profile_picture_remark">Alleen je contacten kunnen je profielfoto zien</string>
|
||||
<string name="change_profile_picture_failed_message">Excuses, maar er is iets misgegaan met het updaten van je profielfoto</string>
|
||||
<!--Settings Display-->
|
||||
<string name="pref_language_title">Taal & regio</string>
|
||||
<string name="pref_language_changed">Deze instelling zal werken wanneer u Briar opnieuw opstart. Gelieve uit te loggen en Briar opnieuw te starten.</string>
|
||||
@@ -568,7 +564,7 @@
|
||||
<string name="permission_camera_location_title">Camera en locatie</string>
|
||||
<string name="permission_camera_location_request_body">Om de QR-code in te scannen heeft Briar toegang nodig tot de camera.\n\nOm bluetoothapparaten te ontdekken heeft Briar toestemming nodig tot je locatie.\n\nBriar slaat je locatie niet op en deelt het met niemand.</string>
|
||||
<string name="permission_camera_denied_body">Je hebt toegang tot de camera niet vrijgegeven, terwijl het toevoegen van contacten de camera nodig heeft.\n\nOverweeg alsjeblieft toegang vrij te geven.</string>
|
||||
<string name="permission_location_denied_body">Je hebt geen toegang tot je locatie gegeven, maar Briar heeft deze rechten nodig om apparaten via bluetooth te vinden.\n\nOverweeg a.u.b. deze rechten te geven.</string>
|
||||
<string name="permission_location_denied_body">Je hebt geen toegang tot je locatie gegeven, maar Briar heeft deze rechten nodig om apparaten via bleutooth te vinden.\n\nOverweeg a.u.b. deze rechten te geven.</string>
|
||||
<string name="qr_code">QR-code</string>
|
||||
<string name="show_qr_code_fullscreen">Toon QR-code op volledig scherm</string>
|
||||
<!--App Locking-->
|
||||
|
||||
@@ -435,10 +435,6 @@
|
||||
<string name="blogs_rss_feeds_manage_empty_state">Nici un flux RSS de arătat\n\nAtingeți iconița + pentru a adăuga un flux</string>
|
||||
<string name="blogs_rss_feeds_manage_error">A apărut o eroare la încărcarea fluxurilor dumneavoastră. Vă rugăm să încercați din nou mai târziu.</string>
|
||||
<!--Settings Profile Picture-->
|
||||
<string name="change_profile_picture">Atingeți pentru a vă schimba poza de profil</string>
|
||||
<string name="dialog_confirm_profile_picture_title">Schimbare poză de profil</string>
|
||||
<string name="dialog_confirm_profile_picture_remark">Doar contactele vor vedea poza de contact</string>
|
||||
<string name="change_profile_picture_failed_message">Ne pare rău, dar ceva nu a funcționat cum trebuie la actualizarea pozei de profil</string>
|
||||
<!--Settings Display-->
|
||||
<string name="pref_language_title">Limbă & Regiune</string>
|
||||
<string name="pref_language_changed">Această setare va avea efect după repornirea Briar. Vă rugăm să ieșiți din Briar și să reporniți aplicația.</string>
|
||||
|
||||
@@ -447,10 +447,6 @@
|
||||
<string name="blogs_rss_feeds_manage_empty_state">Нет RSS-лент для отображения\n\nКоснитесь значка + для импорта ленты</string>
|
||||
<string name="blogs_rss_feeds_manage_error">Ошибка при загрузке вашей ленты. Повторите попытку позже.</string>
|
||||
<!--Settings Profile Picture-->
|
||||
<string name="change_profile_picture">Нажмите, чтобы изменить изображение вашего профиля </string>
|
||||
<string name="dialog_confirm_profile_picture_title">Изменить изображение профиля</string>
|
||||
<string name="dialog_confirm_profile_picture_remark">Только ваши контакты могут видеть изображение вашего профиля</string>
|
||||
<string name="change_profile_picture_failed_message">Нам очень жаль, но что-то пошло не так во время обновления изображения вашего профиля.</string>
|
||||
<!--Settings Display-->
|
||||
<string name="pref_language_title">Язык и регион</string>
|
||||
<string name="pref_language_changed">Этот параметр вступит в силу после перезапуска Briar. Пожалуйста, выйдите и перезапустите Briar.</string>
|
||||
|
||||
@@ -168,24 +168,12 @@
|
||||
<string name="set_contact_alias">Change contact name</string>
|
||||
<string name="set_contact_alias_hint">Contact name</string>
|
||||
<string name="menu_item_disappearing_messages">Disappearing messages</string>
|
||||
<!-- The first placeholder will show a duration like "7 days". The second placeholder at the end will add "Tap to learn more." -->
|
||||
<string name="auto_delete_msg_you_enabled">Your messages will disappear after %1$s. %2$s</string>
|
||||
<!-- The placeholder at the end will add "Tap to learn more." -->
|
||||
<string name="auto_delete_msg_you_enabled">Your messages will disappear after 7 days. %1$s</string>
|
||||
<!-- The placeholder at the end will add "Tap to learn more." -->
|
||||
<string name="auto_delete_msg_you_disabled">Your messages will not disappear. %1$s</string>
|
||||
<!-- The second placeholder will show a duration like "7 days". The third placeholder at the end will add "Tap to learn more." -->
|
||||
<string name="auto_delete_msg_contact_enabled">%1$s\'s messages will disappear after %2$s. %3$s</string>
|
||||
<plurals name="duration_minutes">
|
||||
<item quantity="one">%d minute</item>
|
||||
<item quantity="other">%d minutes</item>
|
||||
</plurals>
|
||||
<plurals name="duration_hours">
|
||||
<item quantity="one">%d hour</item>
|
||||
<item quantity="other">%d hours</item>
|
||||
</plurals>
|
||||
<plurals name="duration_days">
|
||||
<item quantity="one">%d day</item>
|
||||
<item quantity="other">%d days</item>
|
||||
</plurals>
|
||||
<!-- The second placeholder at the end will add "Tap to learn more." -->
|
||||
<string name="auto_delete_msg_contact_enabled">%1$s\'s messages will disappear after 7 days. %2$s</string>
|
||||
<!-- The second placeholder at the end will add "Tap to learn more." -->
|
||||
<string name="auto_delete_msg_contact_disabled">%1$s\'s messages will not disappear. %2$s</string>
|
||||
<string name="tap_to_learn_more">Tap to learn more.</string>
|
||||
@@ -200,6 +188,7 @@
|
||||
<string name="dialog_message_not_deleted_ongoing_both">Messages related to ongoing invitations and introductions cannot be deleted until they conclude.</string>
|
||||
<string name="dialog_message_not_deleted_ongoing_introductions">Messages related to ongoing introductions cannot be deleted until they conclude.</string>
|
||||
<string name="dialog_message_not_deleted_ongoing_invitations">Messages related to ongoing invitations cannot be deleted until they conclude.</string>
|
||||
<string name="dialog_message_not_deleted_partly_downloaded">Partly downloaded messages cannot be deleted until they have finished downloading.</string>
|
||||
<string name="dialog_message_not_deleted_not_all_selected_both">To delete an invitation or introduction, you need to select the request and the response.</string>
|
||||
<string name="dialog_message_not_deleted_not_all_selected_introductions">To delete an introduction, you need to select the request and the response.</string>
|
||||
<string name="dialog_message_not_deleted_not_all_selected_invitations">To delete an invitation, you need to select the request and the response.</string>
|
||||
@@ -317,7 +306,6 @@
|
||||
<string name="introduction_response_accepted_sent">You accepted the introduction to %1$s.</string>
|
||||
<string name="introduction_response_accepted_sent_info">Before %1$s gets added to your contacts, they need to accept the introduction as well. This might take some time.</string>
|
||||
<string name="introduction_response_declined_sent">You declined the introduction to %1$s.</string>
|
||||
<string name="introduction_response_declined_auto">The introduction to %1$s was automatically declined.</string>
|
||||
<string name="introduction_response_accepted_received">%1$s accepted the introduction to %2$s.</string>
|
||||
<string name="introduction_response_declined_received">%1$s declined the introduction to %2$s.</string>
|
||||
<string name="introduction_response_declined_received_by_introducee">%1$s says that %2$s declined the introduction.</string>
|
||||
@@ -366,7 +354,6 @@
|
||||
</plurals>
|
||||
<string name="groups_invitations_response_accepted_sent">You accepted the group invitation from %s.</string>
|
||||
<string name="groups_invitations_response_declined_sent">You declined the group invitation from %s.</string>
|
||||
<string name="groups_invitations_response_declined_auto">The group invitation from %s was automatically declined.</string>
|
||||
<string name="groups_invitations_response_accepted_received">%s accepted the group invitation.</string>
|
||||
<string name="groups_invitations_response_declined_received">%s declined the group invitation.</string>
|
||||
<string name="sharing_status_groups">Only the creator can invite new members to the group. Below are all current members of the group.</string>
|
||||
@@ -420,7 +407,6 @@
|
||||
<string name="forum_invitation_already_sharing">Already sharing</string>
|
||||
<string name="forum_invitation_response_accepted_sent">You accepted the forum invitation from %s.</string>
|
||||
<string name="forum_invitation_response_declined_sent">You declined the forum invitation from %s.</string>
|
||||
<string name="forum_invitation_response_declined_auto">The forum invitation from %s was automatically declined.</string>
|
||||
<string name="forum_invitation_response_accepted_received">%s accepted the forum invitation.</string>
|
||||
<string name="forum_invitation_response_declined_received">%s declined the forum invitation.</string>
|
||||
|
||||
@@ -458,7 +444,6 @@
|
||||
<string name="blogs_sharing_snackbar">Blog shared with chosen contacts</string>
|
||||
<string name="blogs_sharing_response_accepted_sent">You accepted the blog invitation from %s.</string>
|
||||
<string name="blogs_sharing_response_declined_sent">You declined the blog invitation from %s.</string>
|
||||
<string name="blogs_sharing_response_declined_auto">The blog invitation from %s was automatically declined.</string>
|
||||
<string name="blogs_sharing_response_accepted_received">%s accepted the blog invitation.</string>
|
||||
<string name="blogs_sharing_response_declined_received">%s declined the blog invitation.</string>
|
||||
<string name="blogs_sharing_invitation_received">%1$s has shared the blog \"%2$s\" with you.</string>
|
||||
@@ -487,7 +472,7 @@
|
||||
<!-- Settings Profile Picture -->
|
||||
<string name="change_profile_picture">Tap to change your profile picture</string>
|
||||
<string name="dialog_confirm_profile_picture_title">Change profile picture</string>
|
||||
<string name="dialog_confirm_profile_picture_remark">Only your contacts can see this picture</string>
|
||||
<string name="dialog_confirm_profile_picture_remark">Only your contacts can see your profile image</string>
|
||||
<string name="change_profile_picture_failed_message">We\'re sorry, but something went wrong while updating your profile picture</string>
|
||||
|
||||
<!-- Settings Display -->
|
||||
@@ -585,16 +570,15 @@
|
||||
<!-- Conversation Settings -->
|
||||
<string name="disappearing_messages_title">Disappearing messages</string>
|
||||
<string name="disappearing_messages_explanation_long">Turning on this setting will make new
|
||||
messages in this conversation automatically disappear after 7\u00A0days.
|
||||
\n\nThe countdown for the sender\'s copy of the message starts after it has been delivered.
|
||||
The countdown starts for the recipient after they have read the message.
|
||||
messages in this conversation automatically disappear 7\u00A0days after being received.
|
||||
This applies to messages you send to your contact as well as messages your contact sends to you.
|
||||
Your contact can also change this setting for the both of you.
|
||||
\n\nMessages that will disappear are marked with a bomb icon.
|
||||
\n\nKeep in mind that recipients can still make copies of the messages you send.
|
||||
\n\nIf you change this setting, it will apply to your new messages immediately and to your
|
||||
contact\'s messages once they receive your next message.
|
||||
Your contact can also change this setting for the both of you.</string>
|
||||
\n\nIf you change this setting, it will apply to your messages immediately and to your
|
||||
contact\'s messages once they receive your next message.</string>
|
||||
<string name="learn_more">Learn more</string>
|
||||
<string name="disappearing_messages_summary">Make future messages in this conversation automatically disappear after 7\u00A0days.</string>
|
||||
<string name="disappearing_messages_summary">Make future messages in this conversation automatically disappear 7\u00A0days after being received.</string>
|
||||
|
||||
<!-- Settings Feedback -->
|
||||
<string name="feedback_settings_title">Feedback</string>
|
||||
|
||||
@@ -1,66 +0,0 @@
|
||||
package org.briarproject.briar.android.logging;
|
||||
|
||||
import org.briarproject.bramble.test.BrambleMockTestCase;
|
||||
import org.junit.ClassRule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.TemporaryFolder;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.ArrayList;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.LogRecord;
|
||||
|
||||
import static java.util.logging.Level.FINE;
|
||||
import static java.util.logging.Level.INFO;
|
||||
import static java.util.logging.Level.SEVERE;
|
||||
import static java.util.logging.Level.WARNING;
|
||||
import static org.briarproject.bramble.util.StringUtils.getRandomString;
|
||||
import static org.briarproject.briar.android.logging.BriefLogFormatter.formatLog;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
public class LogEncryptionDecryptionTest extends BrambleMockTestCase {
|
||||
|
||||
@ClassRule
|
||||
public static TemporaryFolder folder = new TemporaryFolder();
|
||||
|
||||
private final SecureRandom random;
|
||||
private final CachingLogHandler cachingLogHandler;
|
||||
private final LogEncrypter logEncrypter;
|
||||
private final LogDecrypter logDecrypter;
|
||||
private final BriefLogFormatter logFormatter = new BriefLogFormatter();
|
||||
|
||||
public LogEncryptionDecryptionTest() throws IOException {
|
||||
LoggingComponent loggingComponent = DaggerLoggingComponent.builder()
|
||||
.loggingTestModule(new LoggingTestModule(folder.newFile()))
|
||||
.build();
|
||||
random = loggingComponent.random();
|
||||
logEncrypter = loggingComponent.logEncrypter();
|
||||
logDecrypter = loggingComponent.logDecrypter();
|
||||
cachingLogHandler = loggingComponent.cachingLogHandler();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEncryptedMatchesDecrypted() {
|
||||
ArrayList<LogRecord> logRecords =
|
||||
new ArrayList<>(random.nextInt(99) + 1);
|
||||
for (int i = 0; i < logRecords.size(); i++) {
|
||||
LogRecord logRecord = getRandomLogRecord();
|
||||
cachingLogHandler.publish(logRecord);
|
||||
logRecords.add(logRecord);
|
||||
}
|
||||
byte[] logKey = logEncrypter.encryptLogs();
|
||||
assertEquals(formatLog(logFormatter, logRecords),
|
||||
logDecrypter.decryptLogs(logKey));
|
||||
}
|
||||
|
||||
private LogRecord getRandomLogRecord() {
|
||||
Level[] levels = {SEVERE, WARNING, INFO, FINE};
|
||||
Level level = levels[random.nextInt(levels.length)];
|
||||
LogRecord logRecord =
|
||||
new LogRecord(level, getRandomString(random.nextInt(128) + 1));
|
||||
logRecord.setLoggerName(getRandomString(random.nextInt(23) + 1));
|
||||
return logRecord;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
package org.briarproject.briar.android.logging;
|
||||
|
||||
import org.briarproject.bramble.BrambleCoreModule;
|
||||
import org.briarproject.bramble.system.ClockModule;
|
||||
import org.briarproject.bramble.test.TestSecureRandomModule;
|
||||
|
||||
import java.security.SecureRandom;
|
||||
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import dagger.Component;
|
||||
|
||||
@Singleton
|
||||
@Component(modules = {
|
||||
ClockModule.class,
|
||||
BrambleCoreModule.class,
|
||||
TestSecureRandomModule.class,
|
||||
LoggingModule.class,
|
||||
LoggingTestModule.class,
|
||||
})
|
||||
public interface LoggingComponent {
|
||||
|
||||
SecureRandom random();
|
||||
|
||||
CachingLogHandler cachingLogHandler();
|
||||
|
||||
LogEncrypter logEncrypter();
|
||||
|
||||
LogDecrypter logDecrypter();
|
||||
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
package org.briarproject.briar.android.logging;
|
||||
|
||||
import org.briarproject.bramble.api.crypto.PublicKey;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.bramble.api.reporting.DevConfig;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
|
||||
@Module
|
||||
class LoggingTestModule {
|
||||
|
||||
private final File logFile;
|
||||
|
||||
LoggingTestModule(File logFile) {
|
||||
this.logFile = logFile;
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
DevConfig provideDevConfig() {
|
||||
@NotNullByDefault
|
||||
DevConfig devConfig = new DevConfig() {
|
||||
@Override
|
||||
public PublicKey getDevPublicKey() {
|
||||
throw new UnsupportedOperationException();
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDevOnionAddress() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public File getReportDir() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public File getLogcatFile() {
|
||||
return logFile;
|
||||
}
|
||||
};
|
||||
return devConfig;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,168 +0,0 @@
|
||||
package org.briarproject.briar.android.util;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
|
||||
import org.briarproject.bramble.test.BrambleMockTestCase;
|
||||
import org.briarproject.briar.R;
|
||||
import org.jmock.Expectations;
|
||||
import org.jmock.lib.legacy.ClassImposteriser;
|
||||
import org.junit.Test;
|
||||
|
||||
import static java.util.concurrent.TimeUnit.DAYS;
|
||||
import static java.util.concurrent.TimeUnit.HOURS;
|
||||
import static java.util.concurrent.TimeUnit.MINUTES;
|
||||
import static java.util.concurrent.TimeUnit.SECONDS;
|
||||
import static org.briarproject.briar.android.util.UiUtils.formatDuration;
|
||||
|
||||
public class UiUtilsFormatDurationTest extends BrambleMockTestCase {
|
||||
|
||||
private final Context ctx;
|
||||
private final Resources r;
|
||||
private final int strMinutes = R.plurals.duration_minutes;
|
||||
private final int strHours = R.plurals.duration_hours;
|
||||
private final int strDays = R.plurals.duration_days;
|
||||
|
||||
public UiUtilsFormatDurationTest() {
|
||||
context.setImposteriser(ClassImposteriser.INSTANCE);
|
||||
ctx = context.mock(Context.class);
|
||||
r = context.mock(Resources.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOneMinute() {
|
||||
expectMinuteString(1);
|
||||
formatDuration(ctx, MINUTES.toMillis(1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOneHour() {
|
||||
expectHourString(1);
|
||||
formatDuration(ctx, HOURS.toMillis(1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOneDay() {
|
||||
expectDayString(1);
|
||||
formatDuration(ctx, DAYS.toMillis(1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test10Seconds() {
|
||||
// capped to 1min
|
||||
expectMinuteString(1);
|
||||
formatDuration(ctx, SECONDS.toMillis(10));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test100Seconds() {
|
||||
expectMinuteString(2);
|
||||
formatDuration(ctx, SECONDS.toMillis(100));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test2Minutes() {
|
||||
expectMinuteString(2);
|
||||
formatDuration(ctx, MINUTES.toMillis(2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test10Minutes() {
|
||||
expectMinuteString(10);
|
||||
formatDuration(ctx, MINUTES.toMillis(10));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test130Minutes() {
|
||||
expectHourString(2);
|
||||
expectMinuteString(10);
|
||||
formatDuration(ctx, MINUTES.toMillis(130));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test13Hours() {
|
||||
expectHourString(13);
|
||||
formatDuration(ctx, HOURS.toMillis(13));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSevenDays() {
|
||||
expectDayString(7);
|
||||
formatDuration(ctx, DAYS.toMillis(7));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSevenDays2Hours() {
|
||||
expectDayString(7);
|
||||
expectHourString(2);
|
||||
formatDuration(ctx, DAYS.toMillis(7) + HOURS.toMillis(2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSevenDays20Minutes() {
|
||||
expectDayString(7);
|
||||
formatDuration(ctx, DAYS.toMillis(7) + MINUTES.toMillis(20));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSevenDays40Minutes() {
|
||||
expectDayString(7);
|
||||
expectMinuteString(40);
|
||||
formatDuration(ctx, DAYS.toMillis(7) + MINUTES.toMillis(40));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTwoDays11Hours() {
|
||||
expectDayString(2);
|
||||
expectHourString(11);
|
||||
formatDuration(ctx, DAYS.toMillis(2) + HOURS.toMillis(11));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTwoDays12Hours() {
|
||||
expectDayString(2);
|
||||
expectHourString(12);
|
||||
formatDuration(ctx, DAYS.toMillis(2) + HOURS.toMillis(12));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTwoDays13Hours() {
|
||||
expectDayString(2);
|
||||
expectHourString(13);
|
||||
formatDuration(ctx, DAYS.toMillis(2) + HOURS.toMillis(13));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test7Days23Hours55Minutes() {
|
||||
expectDayString(7);
|
||||
expectHourString(23);
|
||||
expectMinuteString(55);
|
||||
formatDuration(ctx,
|
||||
DAYS.toMillis(7) + HOURS.toMillis(23) + MINUTES.toMillis(55));
|
||||
}
|
||||
|
||||
private void expectMinuteString(int minutes) {
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(ctx).getResources();
|
||||
will(returnValue(r));
|
||||
oneOf(r).getQuantityString(strMinutes, minutes, minutes);
|
||||
}});
|
||||
}
|
||||
|
||||
private void expectHourString(int hours) {
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(ctx).getResources();
|
||||
will(returnValue(r));
|
||||
oneOf(r).getQuantityString(strHours, hours, hours);
|
||||
}});
|
||||
}
|
||||
|
||||
private void expectDayString(int days) {
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(ctx).getResources();
|
||||
will(returnValue(r));
|
||||
oneOf(r).getQuantityString(strDays, days, days);
|
||||
}});
|
||||
}
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user