mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-17 13:19:52 +01:00
Compare commits
100 Commits
user-testi
...
new-or-rec
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d91d2e0070 | ||
|
|
58f803a48a | ||
|
|
ac9c71f7eb | ||
|
|
b3292f86ab | ||
|
|
28d2697e38 | ||
|
|
8e4b309a12 | ||
|
|
8a1333e8f2 | ||
|
|
bd2a671f9f | ||
|
|
845be86113 | ||
|
|
25bbb5aa36 | ||
|
|
57605d55ce | ||
|
|
6c079e172a | ||
|
|
a101229f73 | ||
|
|
3f7f53774b | ||
|
|
9beb4d7b81 | ||
|
|
378112c00c | ||
|
|
451a3238be | ||
|
|
bf6dd0d924 | ||
|
|
085e25cc14 | ||
|
|
033c9f4d59 | ||
|
|
5f7bc4a143 | ||
|
|
4972c554dc | ||
|
|
44e33e3d1a | ||
|
|
5212bb7a01 | ||
|
|
83aad185cd | ||
|
|
c318dcfb5f | ||
|
|
10610930c0 | ||
|
|
2af236b733 | ||
|
|
d46a513208 | ||
|
|
022357fb4c | ||
|
|
a576d7abf8 | ||
|
|
008877a9da | ||
|
|
01bc94c241 | ||
|
|
03c1f9c99a | ||
|
|
0b9e4915dc | ||
|
|
55e5600214 | ||
|
|
4c357fe87a | ||
|
|
6a7ceb4a68 | ||
|
|
d917e9d642 | ||
|
|
c7f6270b2a | ||
|
|
681b151c8b | ||
|
|
b86b0f5fbc | ||
|
|
dc138c713f | ||
|
|
7da49ae6df | ||
|
|
3c61f499d9 | ||
|
|
fbe839d9ca | ||
|
|
f2638c9db2 | ||
|
|
808166931e | ||
|
|
77d0c16530 | ||
|
|
c991cfb926 | ||
|
|
dcda13db64 | ||
|
|
ff4640b789 | ||
|
|
a2426e3b2a | ||
|
|
0bc4bf232f | ||
|
|
2ed44aa2a8 | ||
|
|
8496ab0a6a | ||
|
|
b57d811b4a | ||
|
|
4077e28999 | ||
|
|
292fb6d3b1 | ||
|
|
4ead7cd4a1 | ||
|
|
513e696238 | ||
|
|
f160efb0e7 | ||
|
|
a6c2000d81 | ||
|
|
a38a3139d9 | ||
|
|
4c8adaa02b | ||
|
|
8a534b4503 | ||
|
|
e5b2275c82 | ||
|
|
5159593825 | ||
|
|
a546fecc01 | ||
|
|
3e7e37f5f6 | ||
|
|
d095ba0b15 | ||
|
|
7fab97d26c | ||
|
|
6fbc82ee27 | ||
|
|
885b03cfd7 | ||
|
|
f81bfcafeb | ||
|
|
f36f1cf3d4 | ||
|
|
7d6a63d866 | ||
|
|
15ebdf8dd5 | ||
|
|
db2c235283 | ||
|
|
e5bd43469e | ||
|
|
9366c184d8 | ||
|
|
73d2c964d4 | ||
|
|
fb2b4209cf | ||
|
|
a04b512497 | ||
|
|
3d9515e308 | ||
|
|
1b19b331b1 | ||
|
|
d151a2d7f7 | ||
|
|
9712a4b849 | ||
|
|
cf1ac5e3e5 | ||
|
|
cb859e998d | ||
|
|
0b9345f867 | ||
|
|
12988120d1 | ||
|
|
8d6c866e62 | ||
|
|
8f82cf3c73 | ||
|
|
21112ce092 | ||
|
|
21ee3ea00d | ||
|
|
bb964101b3 | ||
|
|
d796eff0f6 | ||
|
|
700ea2b387 | ||
|
|
e4a66615a7 |
14
.idea/dictionaries/briar.xml
generated
Normal file
14
.idea/dictionaries/briar.xml
generated
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
<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>introducer</w>
|
||||||
|
<w>onboarding</w>
|
||||||
|
</words>
|
||||||
|
</dictionary>
|
||||||
|
</component>
|
||||||
@@ -11,8 +11,8 @@ android {
|
|||||||
defaultConfig {
|
defaultConfig {
|
||||||
minSdkVersion 16
|
minSdkVersion 16
|
||||||
targetSdkVersion 29
|
targetSdkVersion 29
|
||||||
versionCode 10214
|
versionCode 10216
|
||||||
versionName "1.2.14"
|
versionName "1.2.16"
|
||||||
consumerProguardFiles 'proguard-rules.txt'
|
consumerProguardFiles 'proguard-rules.txt'
|
||||||
|
|
||||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
@@ -38,8 +38,8 @@ configurations {
|
|||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation project(path: ':bramble-core', configuration: 'default')
|
implementation project(path: ':bramble-core', configuration: 'default')
|
||||||
tor 'org.briarproject:tor-android:0.3.5.12@zip'
|
tor 'org.briarproject:tor-android:0.3.5.13@zip'
|
||||||
tor 'org.briarproject:obfs4proxy-android:0.0.11-2@zip'
|
tor 'org.briarproject:obfs4proxy-android:0.0.12-dev-40245c4a@zip'
|
||||||
|
|
||||||
annotationProcessor 'com.google.dagger:dagger-compiler:2.24'
|
annotationProcessor 'com.google.dagger:dagger-compiler:2.24'
|
||||||
|
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ public class AndroidUtils {
|
|||||||
private static final String FAKE_BLUETOOTH_ADDRESS = "02:00:00:00:00:00";
|
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_REPORTS = "dev-reports";
|
||||||
|
private static final String STORED_LOGCAT = "dev-logcat";
|
||||||
|
|
||||||
public static Collection<String> getSupportedArchitectures() {
|
public static Collection<String> getSupportedArchitectures() {
|
||||||
List<String> abis = new ArrayList<>();
|
List<String> abis = new ArrayList<>();
|
||||||
@@ -107,6 +108,10 @@ public class AndroidUtils {
|
|||||||
return ctx.getDir(STORED_REPORTS, MODE_PRIVATE);
|
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.
|
* Returns an array of supported content types for image attachments.
|
||||||
* GIFs can't be compressed on API < 24 so they're not supported.
|
* 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.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:bcpkix-jdk15on:1.56:bcpkix-jdk15on-1.56.jar:7043dee4e9e7175e93e0b36f45b1ec1ecb893c5f755667e8b916eb8dd201c6ca',
|
||||||
'org.bouncycastle:bcprov-jdk15on:1.56:bcprov-jdk15on-1.56.jar:963e1ee14f808ffb99897d848ddcdb28fa91ddda867eb18d303e82728f878349',
|
'org.bouncycastle:bcprov-jdk15on:1.56:bcprov-jdk15on-1.56.jar:963e1ee14f808ffb99897d848ddcdb28fa91ddda867eb18d303e82728f878349',
|
||||||
'org.briarproject:obfs4proxy-android:0.0.11-2:obfs4proxy-android-0.0.11-2.zip:57e55cbe87aa2aac210fdbb6cd8cdeafe15f825406a08ebf77a8b787aa2c6a8a',
|
'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.12:tor-android-0.3.5.12.zip:db71fb3290acff79d572af0752570eaf6aad7c4d88c9b9aa0b4d5afe2b9ead9c',
|
'org.briarproject:tor-android:0.3.5.13:tor-android-0.3.5.13.zip:e0978db136731dae07774b722970cdae1e462fb5adc82845dd80a7e2d87f356c',
|
||||||
'org.checkerframework:checker-compat-qual:2.5.3:checker-compat-qual-2.5.3.jar:d76b9afea61c7c082908023f0cbc1427fab9abd2df915c8b8a3e7a509bccbc6d',
|
'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.5.2:checker-qual-2.5.2.jar:64b02691c8b9d4e7700f8ee2e742dce7ea2c6e81e662b7522c9ee3bf568c040a',
|
||||||
'org.checkerframework:checker-qual:2.8.1:checker-qual-2.8.1.jar:9103499008bcecd4e948da29b17864abb64304e15706444ae209d17ebe0575df',
|
'org.checkerframework:checker-qual:2.8.1:checker-qual-2.8.1.jar:9103499008bcecd4e948da29b17864abb64304e15706444ae209d17ebe0575df',
|
||||||
|
|||||||
@@ -1,12 +1,11 @@
|
|||||||
package org.briarproject.bramble.crypto;
|
package org.briarproject.bramble.api.crypto;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
|
||||||
import java.security.GeneralSecurityException;
|
import java.security.GeneralSecurityException;
|
||||||
|
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
interface AuthenticatedCipher {
|
public interface AuthenticatedCipher {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes this cipher for encryption or decryption with a key and an
|
* Initializes this cipher for encryption or decryption with a key and an
|
||||||
@@ -19,4 +19,10 @@ public interface StreamDecrypterFactory {
|
|||||||
*/
|
*/
|
||||||
StreamDecrypter createContactExchangeStreamDecrypter(InputStream in,
|
StreamDecrypter createContactExchangeStreamDecrypter(InputStream in,
|
||||||
SecretKey headerKey);
|
SecretKey headerKey);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a {@link StreamDecrypter} for decrypting a log stream.
|
||||||
|
*/
|
||||||
|
StreamDecrypter createLogStreamDecrypter(InputStream in,
|
||||||
|
SecretKey headerKey);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,12 @@ public interface StreamEncrypterFactory {
|
|||||||
* Creates a {@link StreamEncrypter} for encrypting a contact exchange
|
* Creates a {@link StreamEncrypter} for encrypting a contact exchange
|
||||||
* stream.
|
* stream.
|
||||||
*/
|
*/
|
||||||
StreamEncrypter createContactExchangeStreamDecrypter(OutputStream out,
|
StreamEncrypter createContactExchangeStreamEncrypter(OutputStream out,
|
||||||
|
SecretKey headerKey);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a {@link StreamEncrypter} for encrypting a log stream.
|
||||||
|
*/
|
||||||
|
StreamEncrypter createLogStreamEncrypter(OutputStream out,
|
||||||
SecretKey headerKey);
|
SecretKey headerKey);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,6 +26,11 @@ public interface IdentityManager {
|
|||||||
*/
|
*/
|
||||||
void registerIdentity(Identity i);
|
void registerIdentity(Identity i);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the cached local identity or loads it from the database.
|
||||||
|
*/
|
||||||
|
Identity getIdentity(Transaction txn) throws DbException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the cached local identity or loads it from the database.
|
* Returns the cached local identity or loads it from the database.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -74,6 +74,13 @@ public interface TransportPropertyManager {
|
|||||||
TransportProperties getRemoteProperties(ContactId c, TransportId t)
|
TransportProperties getRemoteProperties(ContactId c, TransportId t)
|
||||||
throws DbException;
|
throws DbException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the remote transport properties for the given contact and
|
||||||
|
* transport.
|
||||||
|
*/
|
||||||
|
TransportProperties getRemoteProperties(Transaction txn, ContactId c,
|
||||||
|
TransportId t) throws DbException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Merges the given properties with the existing local properties for the
|
* Merges the given properties with the existing local properties for the
|
||||||
* given transport.
|
* given transport.
|
||||||
|
|||||||
@@ -13,4 +13,6 @@ public interface DevConfig {
|
|||||||
String getDevOnionAddress();
|
String getDevOnionAddress();
|
||||||
|
|
||||||
File getReportDir();
|
File getReportDir();
|
||||||
|
|
||||||
|
File getLogcatFile();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,8 +16,13 @@ public interface StreamReaderFactory {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an {@link InputStream InputStream} for reading from a contact
|
* Creates an {@link InputStream InputStream} for reading from a contact
|
||||||
* exchangestream.
|
* exchange stream.
|
||||||
*/
|
*/
|
||||||
InputStream createContactExchangeStreamReader(InputStream in,
|
InputStream createContactExchangeStreamReader(InputStream in,
|
||||||
SecretKey headerKey);
|
SecretKey headerKey);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an {@link InputStream} for reading from a log stream.
|
||||||
|
*/
|
||||||
|
InputStream createLogStreamReader(InputStream in, SecretKey headerKey);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,17 +7,19 @@ import java.io.OutputStream;
|
|||||||
|
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
public interface StreamWriterFactory {
|
public interface StreamWriterFactory {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an {@link OutputStream OutputStream} for writing to a
|
* Creates a {@link StreamWriter} for writing to a transport stream.
|
||||||
* transport stream
|
|
||||||
*/
|
*/
|
||||||
StreamWriter createStreamWriter(OutputStream out, StreamContext ctx);
|
StreamWriter createStreamWriter(OutputStream out, StreamContext ctx);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an {@link OutputStream OutputStream} for writing to a contact
|
* Creates a {@link StreamWriter} for writing to a contact exchange stream.
|
||||||
* exchange stream.
|
|
||||||
*/
|
*/
|
||||||
StreamWriter createContactExchangeStreamWriter(OutputStream out,
|
StreamWriter createContactExchangeStreamWriter(OutputStream out,
|
||||||
SecretKey headerKey);
|
SecretKey headerKey);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a {@link StreamWriter} for writing to a log stream.
|
||||||
|
*/
|
||||||
|
StreamWriter createLogStreamWriter(OutputStream out, SecretKey headerKey);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import net.i2p.crypto.eddsa.KeyPairGenerator;
|
|||||||
|
|
||||||
import org.briarproject.bramble.api.crypto.AgreementPrivateKey;
|
import org.briarproject.bramble.api.crypto.AgreementPrivateKey;
|
||||||
import org.briarproject.bramble.api.crypto.AgreementPublicKey;
|
import org.briarproject.bramble.api.crypto.AgreementPublicKey;
|
||||||
|
import org.briarproject.bramble.api.crypto.AuthenticatedCipher;
|
||||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||||
import org.briarproject.bramble.api.crypto.DecryptionException;
|
import org.briarproject.bramble.api.crypto.DecryptionException;
|
||||||
import org.briarproject.bramble.api.crypto.KeyPair;
|
import org.briarproject.bramble.api.crypto.KeyPair;
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package org.briarproject.bramble.crypto;
|
package org.briarproject.bramble.crypto;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.crypto.AuthenticatedCipher;
|
||||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||||
import org.briarproject.bramble.api.crypto.KeyAgreementCrypto;
|
import org.briarproject.bramble.api.crypto.KeyAgreementCrypto;
|
||||||
import org.briarproject.bramble.api.crypto.PasswordStrengthEstimator;
|
import org.briarproject.bramble.api.crypto.PasswordStrengthEstimator;
|
||||||
|
|||||||
@@ -9,14 +9,16 @@ import java.util.logging.Logger;
|
|||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import static java.lang.Math.min;
|
||||||
import static java.util.logging.Level.INFO;
|
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.logDuration;
|
||||||
import static org.briarproject.bramble.util.LogUtils.now;
|
import static org.briarproject.bramble.util.LogUtils.now;
|
||||||
|
|
||||||
class ScryptKdf implements PasswordBasedKdf {
|
class ScryptKdf implements PasswordBasedKdf {
|
||||||
|
|
||||||
private static final Logger LOG =
|
private static final Logger LOG =
|
||||||
Logger.getLogger(ScryptKdf.class.getName());
|
getLogger(ScryptKdf.class.getName());
|
||||||
|
|
||||||
private static final int MIN_COST = 256; // Min parameter N
|
private static final int MIN_COST = 256; // Min parameter N
|
||||||
private static final int MAX_COST = 1024 * 1024; // Max parameter N
|
private static final int MAX_COST = 1024 * 1024; // Max parameter N
|
||||||
@@ -33,10 +35,20 @@ class ScryptKdf implements PasswordBasedKdf {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int chooseCostParameter() {
|
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
|
// Increase the cost from min to max while measuring performance
|
||||||
int cost = MIN_COST;
|
int cost = MIN_COST;
|
||||||
while (cost * 2 <= MAX_COST && measureDuration(cost) * 2 <= TARGET_MS)
|
while (cost * 2 <= maxCost && measureDuration(cost) * 2 <= TARGET_MS) {
|
||||||
cost *= 2;
|
cost *= 2;
|
||||||
|
}
|
||||||
if (LOG.isLoggable(INFO))
|
if (LOG.isLoggable(INFO))
|
||||||
LOG.info("KDF cost parameter " + cost);
|
LOG.info("KDF cost parameter " + cost);
|
||||||
return cost;
|
return cost;
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package org.briarproject.bramble.crypto;
|
package org.briarproject.bramble.crypto;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.crypto.AuthenticatedCipher;
|
||||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||||
import org.briarproject.bramble.api.crypto.StreamDecrypter;
|
import org.briarproject.bramble.api.crypto.StreamDecrypter;
|
||||||
import org.briarproject.bramble.api.crypto.StreamDecrypterFactory;
|
import org.briarproject.bramble.api.crypto.StreamDecrypterFactory;
|
||||||
@@ -36,4 +37,10 @@ class StreamDecrypterFactoryImpl implements StreamDecrypterFactory {
|
|||||||
SecretKey headerKey) {
|
SecretKey headerKey) {
|
||||||
return new StreamDecrypterImpl(in, cipherProvider.get(), 0, headerKey);
|
return new StreamDecrypterImpl(in, cipherProvider.get(), 0, headerKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public StreamDecrypter createLogStreamDecrypter(InputStream in,
|
||||||
|
SecretKey headerKey) {
|
||||||
|
return createContactExchangeStreamDecrypter(in, headerKey);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package org.briarproject.bramble.crypto;
|
package org.briarproject.bramble.crypto;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.FormatException;
|
import org.briarproject.bramble.api.FormatException;
|
||||||
|
import org.briarproject.bramble.api.crypto.AuthenticatedCipher;
|
||||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||||
import org.briarproject.bramble.api.crypto.StreamDecrypter;
|
import org.briarproject.bramble.api.crypto.StreamDecrypter;
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package org.briarproject.bramble.crypto;
|
package org.briarproject.bramble.crypto;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.crypto.AuthenticatedCipher;
|
||||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||||
import org.briarproject.bramble.api.crypto.StreamEncrypter;
|
import org.briarproject.bramble.api.crypto.StreamEncrypter;
|
||||||
@@ -51,7 +52,7 @@ class StreamEncrypterFactoryImpl implements StreamEncrypterFactory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public StreamEncrypter createContactExchangeStreamDecrypter(
|
public StreamEncrypter createContactExchangeStreamEncrypter(
|
||||||
OutputStream out, SecretKey headerKey) {
|
OutputStream out, SecretKey headerKey) {
|
||||||
AuthenticatedCipher cipher = cipherProvider.get();
|
AuthenticatedCipher cipher = cipherProvider.get();
|
||||||
byte[] streamHeaderNonce = new byte[STREAM_HEADER_NONCE_LENGTH];
|
byte[] streamHeaderNonce = new byte[STREAM_HEADER_NONCE_LENGTH];
|
||||||
@@ -60,4 +61,10 @@ class StreamEncrypterFactoryImpl implements StreamEncrypterFactory {
|
|||||||
return new StreamEncrypterImpl(out, cipher, 0, null, streamHeaderNonce,
|
return new StreamEncrypterImpl(out, cipher, 0, null, streamHeaderNonce,
|
||||||
headerKey, frameKey);
|
headerKey, frameKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public StreamEncrypter createLogStreamEncrypter(OutputStream out,
|
||||||
|
SecretKey headerKey) {
|
||||||
|
return createContactExchangeStreamEncrypter(out, headerKey);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package org.briarproject.bramble.crypto;
|
package org.briarproject.bramble.crypto;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.crypto.AuthenticatedCipher;
|
||||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||||
import org.briarproject.bramble.api.crypto.StreamEncrypter;
|
import org.briarproject.bramble.api.crypto.StreamEncrypter;
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package org.briarproject.bramble.crypto;
|
package org.briarproject.bramble.crypto;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.crypto.AuthenticatedCipher;
|
||||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
import org.spongycastle.crypto.DataLengthException;
|
import org.spongycastle.crypto.DataLengthException;
|
||||||
|
|||||||
@@ -118,6 +118,11 @@ class IdentityManagerImpl implements IdentityManager, OpenDatabaseHook {
|
|||||||
return cached.getLocalAuthor();
|
return cached.getLocalAuthor();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Identity getIdentity(Transaction txn) throws DbException {
|
||||||
|
return getCachedIdentity(txn);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public LocalAuthor getLocalAuthor(Transaction txn) throws DbException {
|
public LocalAuthor getLocalAuthor(Transaction txn) throws DbException {
|
||||||
return getCachedIdentity(txn).getLocalAuthor();
|
return getCachedIdentity(txn).getLocalAuthor();
|
||||||
|
|||||||
@@ -294,7 +294,13 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
|
|||||||
public TransportProperties getRemoteProperties(ContactId c, TransportId t)
|
public TransportProperties getRemoteProperties(ContactId c, TransportId t)
|
||||||
throws DbException {
|
throws DbException {
|
||||||
return db.transactionWithResult(true, txn ->
|
return db.transactionWithResult(true, txn ->
|
||||||
getRemoteProperties(txn, db.getContact(txn, c), t));
|
getRemoteProperties(txn, c, t));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TransportProperties getRemoteProperties(Transaction txn,
|
||||||
|
ContactId c, TransportId t) throws DbException {
|
||||||
|
return getRemoteProperties(txn, db.getContact(txn, c), t);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -24,15 +24,21 @@ class StreamReaderFactoryImpl implements StreamReaderFactory {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public InputStream createStreamReader(InputStream in, StreamContext ctx) {
|
public InputStream createStreamReader(InputStream in, StreamContext ctx) {
|
||||||
return new StreamReaderImpl(
|
return new StreamReaderImpl(streamDecrypterFactory
|
||||||
streamDecrypterFactory.createStreamDecrypter(in, ctx));
|
.createStreamDecrypter(in, ctx));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public InputStream createContactExchangeStreamReader(InputStream in,
|
public InputStream createContactExchangeStreamReader(InputStream in,
|
||||||
SecretKey headerKey) {
|
SecretKey headerKey) {
|
||||||
return new StreamReaderImpl(
|
return new StreamReaderImpl(streamDecrypterFactory
|
||||||
streamDecrypterFactory.createContactExchangeStreamDecrypter(in,
|
.createContactExchangeStreamDecrypter(in, headerKey));
|
||||||
headerKey));
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InputStream createLogStreamReader(InputStream in,
|
||||||
|
SecretKey headerKey) {
|
||||||
|
return new StreamReaderImpl(streamDecrypterFactory
|
||||||
|
.createLogStreamDecrypter(in, headerKey));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,15 +26,21 @@ class StreamWriterFactoryImpl implements StreamWriterFactory {
|
|||||||
@Override
|
@Override
|
||||||
public StreamWriter createStreamWriter(OutputStream out,
|
public StreamWriter createStreamWriter(OutputStream out,
|
||||||
StreamContext ctx) {
|
StreamContext ctx) {
|
||||||
return new StreamWriterImpl(
|
return new StreamWriterImpl(streamEncrypterFactory
|
||||||
streamEncrypterFactory.createStreamEncrypter(out, ctx));
|
.createStreamEncrypter(out, ctx));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public StreamWriter createContactExchangeStreamWriter(OutputStream out,
|
public StreamWriter createContactExchangeStreamWriter(OutputStream out,
|
||||||
SecretKey headerKey) {
|
SecretKey headerKey) {
|
||||||
return new StreamWriterImpl(
|
return new StreamWriterImpl(streamEncrypterFactory
|
||||||
streamEncrypterFactory.createContactExchangeStreamDecrypter(out,
|
.createContactExchangeStreamEncrypter(out, headerKey));
|
||||||
headerKey));
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
@Override
|
||||||
|
public StreamWriter createLogStreamWriter(OutputStream out,
|
||||||
|
SecretKey headerKey) {
|
||||||
|
return new StreamWriterImpl(streamEncrypterFactory
|
||||||
|
.createLogStreamEncrypter(out, headerKey));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package org.briarproject.bramble.crypto;
|
package org.briarproject.bramble.crypto;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.crypto.AuthenticatedCipher;
|
||||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||||
import org.briarproject.bramble.test.BrambleTestCase;
|
import org.briarproject.bramble.test.BrambleTestCase;
|
||||||
import org.briarproject.bramble.test.TestUtils;
|
import org.briarproject.bramble.test.TestUtils;
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package org.briarproject.bramble.crypto;
|
package org.briarproject.bramble.crypto;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.crypto.AuthenticatedCipher;
|
||||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||||
import org.briarproject.bramble.test.BrambleTestCase;
|
import org.briarproject.bramble.test.BrambleTestCase;
|
||||||
import org.briarproject.bramble.test.TestUtils;
|
import org.briarproject.bramble.test.TestUtils;
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package org.briarproject.bramble.crypto;
|
package org.briarproject.bramble.crypto;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.crypto.AuthenticatedCipher;
|
||||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package org.briarproject.bramble.crypto;
|
package org.briarproject.bramble.crypto;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.crypto.AuthenticatedCipher;
|
||||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||||
import org.briarproject.bramble.test.BrambleTestCase;
|
import org.briarproject.bramble.test.BrambleTestCase;
|
||||||
import org.briarproject.bramble.util.StringUtils;
|
import org.briarproject.bramble.util.StringUtils;
|
||||||
|
|||||||
@@ -16,8 +16,8 @@ dependencies {
|
|||||||
implementation fileTree(dir: 'libs', include: '*.jar')
|
implementation fileTree(dir: 'libs', include: '*.jar')
|
||||||
implementation 'net.java.dev.jna:jna:4.5.2'
|
implementation 'net.java.dev.jna:jna:4.5.2'
|
||||||
implementation 'net.java.dev.jna:jna-platform:4.5.2'
|
implementation 'net.java.dev.jna:jna-platform:4.5.2'
|
||||||
tor 'org.briarproject:tor:0.3.5.12@zip'
|
tor 'org.briarproject:tor:0.3.5.13@zip'
|
||||||
tor 'org.briarproject:obfs4proxy:0.0.7@zip'
|
tor 'org.briarproject:obfs4proxy:0.0.12-dev-40245c4a@zip'
|
||||||
|
|
||||||
annotationProcessor 'com.google.dagger:dagger-compiler:2.24'
|
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-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.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.beanshell:bsh:1.3.0:bsh-1.3.0.jar:9b04edc75d19db54f1b4e8b5355e9364384c6cf71eb0a1b9724c159d779879f8',
|
||||||
'org.briarproject:obfs4proxy:0.0.7:obfs4proxy-0.0.7.zip:5b2f693262ce43a7e130f7cc7d5d1617925330640a2eb6d71085e95df8ee0642',
|
'org.briarproject:obfs4proxy:0.0.12-dev-40245c4a:obfs4proxy-0.0.12-dev-40245c4a.zip:172029e7058b3a83ac93ac4991a44bf76e16ce8d46f558f5836d57da3cb3a766',
|
||||||
'org.briarproject:tor:0.3.5.12:tor-0.3.5.12.zip:2f542c4befd216f2226bf7c76e3b8b2d99af6f146a8cb28bf727f42014587006',
|
'org.briarproject:tor:0.3.5.13:tor-0.3.5.13.zip:1c5f0b821ee2aadb0ea04aa96caab3ca0a08370cce8de81c2dfe04d172f8a2a0',
|
||||||
'org.checkerframework:checker-compat-qual:2.5.3:checker-compat-qual-2.5.3.jar:d76b9afea61c7c082908023f0cbc1427fab9abd2df915c8b8a3e7a509bccbc6d',
|
'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.5.2:checker-qual-2.5.2.jar:64b02691c8b9d4e7700f8ee2e742dce7ea2c6e81e662b7522c9ee3bf568c040a',
|
||||||
'org.codehaus.mojo:animal-sniffer-annotations:1.17:animal-sniffer-annotations-1.17.jar:92654f493ecfec52082e76354f0ebf87648dc3d5cec2e3c3cdb947c016747a53',
|
'org.codehaus.mojo:animal-sniffer-annotations:1.17:animal-sniffer-annotations-1.17.jar:92654f493ecfec52082e76354f0ebf87648dc3d5cec2e3c3cdb947c016747a53',
|
||||||
|
|||||||
@@ -22,8 +22,8 @@ android {
|
|||||||
defaultConfig {
|
defaultConfig {
|
||||||
minSdkVersion 16
|
minSdkVersion 16
|
||||||
targetSdkVersion 29
|
targetSdkVersion 29
|
||||||
versionCode 10214
|
versionCode 10216
|
||||||
versionName "1.2.14"
|
versionName "1.2.16"
|
||||||
applicationId "org.briarproject.briar.android"
|
applicationId "org.briarproject.briar.android"
|
||||||
|
|
||||||
vectorDrawables.useSupportLibrary = true
|
vectorDrawables.useSupportLibrary = true
|
||||||
@@ -101,6 +101,7 @@ dependencies {
|
|||||||
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
|
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
|
||||||
implementation 'com.google.android.material:material:1.2.1'
|
implementation 'com.google.android.material:material:1.2.1'
|
||||||
implementation 'androidx.recyclerview:recyclerview-selection:1.1.0-rc03'
|
implementation 'androidx.recyclerview:recyclerview-selection:1.1.0-rc03'
|
||||||
|
implementation 'org.magmacollective.darkcrystal:dark-crystal-secret-sharing-wrapper:1.1.0'
|
||||||
|
|
||||||
implementation 'info.guardianproject.panic:panic:1.0'
|
implementation 'info.guardianproject.panic:panic:1.0'
|
||||||
implementation 'info.guardianproject.trustedintents:trustedintents:0.2'
|
implementation 'info.guardianproject.trustedintents:trustedintents:0.2'
|
||||||
@@ -136,6 +137,7 @@ dependencies {
|
|||||||
testImplementation "org.jmock:jmock:$jmockVersion"
|
testImplementation "org.jmock:jmock:$jmockVersion"
|
||||||
testImplementation "org.jmock:jmock-junit4:$jmockVersion"
|
testImplementation "org.jmock:jmock-junit4:$jmockVersion"
|
||||||
testImplementation "org.jmock:jmock-legacy:$jmockVersion"
|
testImplementation "org.jmock:jmock-legacy:$jmockVersion"
|
||||||
|
testAnnotationProcessor "com.google.dagger:dagger-compiler:2.24"
|
||||||
|
|
||||||
androidTestImplementation project(path: ':bramble-api', configuration: 'testOutput')
|
androidTestImplementation project(path: ':bramble-api', configuration: 'testOutput')
|
||||||
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
|
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
|
||||||
|
|||||||
@@ -134,6 +134,34 @@
|
|||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
|
<activity
|
||||||
|
android:name="org.briarproject.briar.android.account.NewOrRecoverActivity"
|
||||||
|
android:label="@string/activity_name_new_or_recover_account"
|
||||||
|
android:parentActivityName="org.briarproject.briar.android.login.StartupActivity">
|
||||||
|
<meta-data
|
||||||
|
android:name="android.support.PARENT_ACTIVITY"
|
||||||
|
android:value="org.briarproject.briar.android.login.StartupActivity" />
|
||||||
|
</activity>
|
||||||
|
|
||||||
|
<activity
|
||||||
|
android:name="org.briarproject.briar.android.socialbackup.DistributedBackupActivity"
|
||||||
|
android:label="@string/activity_name_distributed_backup"
|
||||||
|
android:parentActivityName="org.briarproject.briar.android.settings.SettingsActivity">
|
||||||
|
<meta-data
|
||||||
|
android:name="android.support.PARENT_ACTIVITY"
|
||||||
|
android:value="org.briarproject.briar.android.settings.SettingsActivity" />
|
||||||
|
</activity>
|
||||||
|
|
||||||
|
<activity
|
||||||
|
android:name="org.briarproject.briar.android.socialbackup.OldDistributedBackupActivity"
|
||||||
|
android:label="@string/activity_name_old_distributed_backup"
|
||||||
|
android:parentActivityName="org.briarproject.briar.android.settings.SettingsActivity">
|
||||||
|
<meta-data
|
||||||
|
android:name="android.support.PARENT_ACTIVITY"
|
||||||
|
android:value="org.briarproject.briar.android.settings.SettingsActivity" />
|
||||||
|
</activity>
|
||||||
|
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name="org.briarproject.briar.android.conversation.ConversationActivity"
|
android:name="org.briarproject.briar.android.conversation.ConversationActivity"
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import org.briarproject.bramble.api.contact.ContactExchangeManager;
|
|||||||
import org.briarproject.bramble.api.contact.ContactManager;
|
import org.briarproject.bramble.api.contact.ContactManager;
|
||||||
import org.briarproject.bramble.api.crypto.CryptoExecutor;
|
import org.briarproject.bramble.api.crypto.CryptoExecutor;
|
||||||
import org.briarproject.bramble.api.crypto.PasswordStrengthEstimator;
|
import org.briarproject.bramble.api.crypto.PasswordStrengthEstimator;
|
||||||
|
import org.briarproject.bramble.api.db.DatabaseComponent;
|
||||||
import org.briarproject.bramble.api.db.DatabaseExecutor;
|
import org.briarproject.bramble.api.db.DatabaseExecutor;
|
||||||
import org.briarproject.bramble.api.db.TransactionManager;
|
import org.briarproject.bramble.api.db.TransactionManager;
|
||||||
import org.briarproject.bramble.api.event.EventBus;
|
import org.briarproject.bramble.api.event.EventBus;
|
||||||
@@ -34,6 +35,7 @@ import org.briarproject.briar.BriarCoreModule;
|
|||||||
import org.briarproject.briar.android.attachment.AttachmentModule;
|
import org.briarproject.briar.android.attachment.AttachmentModule;
|
||||||
import org.briarproject.briar.android.attachment.media.MediaModule;
|
import org.briarproject.briar.android.attachment.media.MediaModule;
|
||||||
import org.briarproject.briar.android.conversation.glide.BriarModelLoader;
|
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.login.SignInReminderReceiver;
|
||||||
import org.briarproject.briar.android.view.EmojiTextInputView;
|
import org.briarproject.briar.android.view.EmojiTextInputView;
|
||||||
import org.briarproject.briar.api.android.AndroidNotificationManager;
|
import org.briarproject.briar.api.android.AndroidNotificationManager;
|
||||||
@@ -58,7 +60,9 @@ import org.briarproject.briar.api.privategroup.PrivateGroupFactory;
|
|||||||
import org.briarproject.briar.api.privategroup.PrivateGroupManager;
|
import org.briarproject.briar.api.privategroup.PrivateGroupManager;
|
||||||
import org.briarproject.briar.api.privategroup.invitation.GroupInvitationFactory;
|
import org.briarproject.briar.api.privategroup.invitation.GroupInvitationFactory;
|
||||||
import org.briarproject.briar.api.privategroup.invitation.GroupInvitationManager;
|
import org.briarproject.briar.api.privategroup.invitation.GroupInvitationManager;
|
||||||
|
import org.briarproject.briar.api.socialbackup.SocialBackupManager;
|
||||||
import org.briarproject.briar.api.test.TestDataCreator;
|
import org.briarproject.briar.api.test.TestDataCreator;
|
||||||
|
import org.briarproject.briar.socialbackup.SocialBackupManagerImpl_Factory;
|
||||||
|
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
|
|
||||||
@@ -179,6 +183,13 @@ public interface AndroidComponent
|
|||||||
|
|
||||||
AndroidWakeLockManager wakeLockManager();
|
AndroidWakeLockManager wakeLockManager();
|
||||||
|
|
||||||
|
CachingLogHandler logHandler();
|
||||||
|
|
||||||
|
Thread.UncaughtExceptionHandler exceptionHandler();
|
||||||
|
|
||||||
|
SocialBackupManager socialBackupManager();
|
||||||
|
DatabaseComponent databaseComponent();
|
||||||
|
|
||||||
void inject(SignInReminderReceiver briarService);
|
void inject(SignInReminderReceiver briarService);
|
||||||
|
|
||||||
void inject(BriarService briarService);
|
void inject(BriarService briarService);
|
||||||
|
|||||||
@@ -33,12 +33,13 @@ import org.briarproject.briar.android.account.SetupModule;
|
|||||||
import org.briarproject.briar.android.contact.ContactListModule;
|
import org.briarproject.briar.android.contact.ContactListModule;
|
||||||
import org.briarproject.briar.android.forum.ForumModule;
|
import org.briarproject.briar.android.forum.ForumModule;
|
||||||
import org.briarproject.briar.android.keyagreement.ContactExchangeModule;
|
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.login.LoginModule;
|
||||||
import org.briarproject.briar.android.navdrawer.NavDrawerModule;
|
import org.briarproject.briar.android.navdrawer.NavDrawerModule;
|
||||||
import org.briarproject.briar.android.privategroup.conversation.GroupConversationModule;
|
import org.briarproject.briar.android.privategroup.conversation.GroupConversationModule;
|
||||||
import org.briarproject.briar.android.settings.SettingsModule;
|
|
||||||
import org.briarproject.briar.android.privategroup.list.GroupListModule;
|
import org.briarproject.briar.android.privategroup.list.GroupListModule;
|
||||||
import org.briarproject.briar.android.reporting.DevReportModule;
|
import org.briarproject.briar.android.reporting.DevReportModule;
|
||||||
|
import org.briarproject.briar.android.settings.SettingsModule;
|
||||||
import org.briarproject.briar.android.sharing.SharingModule;
|
import org.briarproject.briar.android.sharing.SharingModule;
|
||||||
import org.briarproject.briar.android.test.TestAvatarCreatorImpl;
|
import org.briarproject.briar.android.test.TestAvatarCreatorImpl;
|
||||||
import org.briarproject.briar.android.viewmodel.ViewModelModule;
|
import org.briarproject.briar.android.viewmodel.ViewModelModule;
|
||||||
@@ -74,6 +75,7 @@ import static org.briarproject.briar.android.TestingConstants.IS_DEBUG_BUILD;
|
|||||||
SetupModule.class,
|
SetupModule.class,
|
||||||
DozeHelperModule.class,
|
DozeHelperModule.class,
|
||||||
ContactExchangeModule.class,
|
ContactExchangeModule.class,
|
||||||
|
LoggingModule.class,
|
||||||
LoginModule.class,
|
LoginModule.class,
|
||||||
NavDrawerModule.class,
|
NavDrawerModule.class,
|
||||||
ViewModelModule.class,
|
ViewModelModule.class,
|
||||||
@@ -192,6 +194,11 @@ public class AppModule {
|
|||||||
public File getReportDir() {
|
public File getReportDir() {
|
||||||
return AndroidUtils.getReportDir(app.getApplicationContext());
|
return AndroidUtils.getReportDir(app.getApplicationContext());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public File getLogcatFile() {
|
||||||
|
return AndroidUtils.getLogcatFile(app.getApplicationContext());
|
||||||
|
}
|
||||||
};
|
};
|
||||||
return devConfig;
|
return devConfig;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,9 +6,6 @@ import android.content.SharedPreferences;
|
|||||||
import org.briarproject.bramble.BrambleApplication;
|
import org.briarproject.bramble.BrambleApplication;
|
||||||
import org.briarproject.briar.android.navdrawer.NavDrawerActivity;
|
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
|
* This exists so that the Application object will not necessarily be cast
|
||||||
* directly to the Briar application object.
|
* directly to the Briar application object.
|
||||||
@@ -17,8 +14,6 @@ public interface BriarApplication extends BrambleApplication {
|
|||||||
|
|
||||||
Class<? extends Activity> ENTRY_ACTIVITY = NavDrawerActivity.class;
|
Class<? extends Activity> ENTRY_ACTIVITY = NavDrawerActivity.class;
|
||||||
|
|
||||||
Collection<LogRecord> getRecentLogRecords();
|
|
||||||
|
|
||||||
AndroidComponent getApplicationComponent();
|
AndroidComponent getApplicationComponent();
|
||||||
|
|
||||||
SharedPreferences getDefaultSharedPreferences();
|
SharedPreferences getDefaultSharedPreferences();
|
||||||
|
|||||||
@@ -20,12 +20,10 @@ import org.briarproject.bramble.BrambleCoreEagerSingletons;
|
|||||||
import org.briarproject.briar.BriarCoreEagerSingletons;
|
import org.briarproject.briar.BriarCoreEagerSingletons;
|
||||||
import org.briarproject.briar.R;
|
import org.briarproject.briar.R;
|
||||||
import org.briarproject.briar.android.logging.CachingLogHandler;
|
import org.briarproject.briar.android.logging.CachingLogHandler;
|
||||||
import org.briarproject.briar.android.reporting.BriarExceptionHandler;
|
|
||||||
import org.briarproject.briar.android.util.UiUtils;
|
import org.briarproject.briar.android.util.UiUtils;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.lang.Thread.UncaughtExceptionHandler;
|
||||||
import java.util.logging.Handler;
|
import java.util.logging.Handler;
|
||||||
import java.util.logging.LogRecord;
|
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
@@ -42,22 +40,18 @@ public class BriarApplicationImpl extends Application
|
|||||||
private static final Logger LOG =
|
private static final Logger LOG =
|
||||||
getLogger(BriarApplicationImpl.class.getName());
|
getLogger(BriarApplicationImpl.class.getName());
|
||||||
|
|
||||||
private final CachingLogHandler logHandler = new CachingLogHandler();
|
|
||||||
private final BriarExceptionHandler exceptionHandler =
|
|
||||||
new BriarExceptionHandler(this);
|
|
||||||
|
|
||||||
private AndroidComponent applicationComponent;
|
private AndroidComponent applicationComponent;
|
||||||
private volatile SharedPreferences prefs;
|
private volatile SharedPreferences prefs;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void attachBaseContext(Context base) {
|
protected void attachBaseContext(Context base) {
|
||||||
Thread.setDefaultUncaughtExceptionHandler(exceptionHandler);
|
|
||||||
if (prefs == null)
|
if (prefs == null)
|
||||||
prefs = PreferenceManager.getDefaultSharedPreferences(base);
|
prefs = PreferenceManager.getDefaultSharedPreferences(base);
|
||||||
// Loading the language needs to be done here.
|
// Loading the language needs to be done here.
|
||||||
Localizer.initialize(prefs);
|
Localizer.initialize(prefs);
|
||||||
super.attachBaseContext(
|
super.attachBaseContext(
|
||||||
Localizer.getInstance().setLocale(base));
|
Localizer.getInstance().setLocale(base));
|
||||||
|
Localizer.getInstance().setLocale(this);
|
||||||
setTheme(base, prefs);
|
setTheme(base, prefs);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -67,6 +61,11 @@ public class BriarApplicationImpl extends Application
|
|||||||
|
|
||||||
if (IS_DEBUG_BUILD) enableStrictMode();
|
if (IS_DEBUG_BUILD) enableStrictMode();
|
||||||
|
|
||||||
|
applicationComponent = createApplicationComponent();
|
||||||
|
UncaughtExceptionHandler exceptionHandler =
|
||||||
|
applicationComponent.exceptionHandler();
|
||||||
|
Thread.setDefaultUncaughtExceptionHandler(exceptionHandler);
|
||||||
|
|
||||||
Logger rootLogger = getLogger("");
|
Logger rootLogger = getLogger("");
|
||||||
Handler[] handlers = rootLogger.getHandlers();
|
Handler[] handlers = rootLogger.getHandlers();
|
||||||
// Disable the Android logger for release builds
|
// Disable the Android logger for release builds
|
||||||
@@ -78,12 +77,12 @@ public class BriarApplicationImpl extends Application
|
|||||||
// Restore the default handlers after the level raising handler
|
// Restore the default handlers after the level raising handler
|
||||||
for (Handler handler : handlers) rootLogger.addHandler(handler);
|
for (Handler handler : handlers) rootLogger.addHandler(handler);
|
||||||
}
|
}
|
||||||
|
CachingLogHandler logHandler = applicationComponent.logHandler();
|
||||||
rootLogger.addHandler(logHandler);
|
rootLogger.addHandler(logHandler);
|
||||||
rootLogger.setLevel(IS_DEBUG_BUILD ? FINE : INFO);
|
rootLogger.setLevel(IS_DEBUG_BUILD ? FINE : INFO);
|
||||||
|
|
||||||
LOG.info("Created");
|
LOG.info("Created");
|
||||||
|
|
||||||
applicationComponent = createApplicationComponent();
|
|
||||||
EmojiManager.install(new GoogleEmojiProvider());
|
EmojiManager.install(new GoogleEmojiProvider());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -136,11 +135,6 @@ public class BriarApplicationImpl extends Application
|
|||||||
return applicationComponent;
|
return applicationComponent;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public Collection<LogRecord> getRecentLogRecords() {
|
|
||||||
return logHandler.getRecentLogRecords();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AndroidComponent getApplicationComponent() {
|
public AndroidComponent getApplicationComponent() {
|
||||||
return applicationComponent;
|
return applicationComponent;
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ import javax.inject.Inject;
|
|||||||
import androidx.core.app.NotificationCompat;
|
import androidx.core.app.NotificationCompat;
|
||||||
|
|
||||||
import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
|
import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
|
||||||
import static android.app.NotificationManager.IMPORTANCE_NONE;
|
import static android.app.NotificationManager.IMPORTANCE_LOW;
|
||||||
import static android.app.PendingIntent.FLAG_UPDATE_CURRENT;
|
import static android.app.PendingIntent.FLAG_UPDATE_CURRENT;
|
||||||
import static android.content.Intent.ACTION_SHUTDOWN;
|
import static android.content.Intent.ACTION_SHUTDOWN;
|
||||||
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK;
|
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK;
|
||||||
@@ -46,6 +46,7 @@ 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_NEW_TASK;
|
||||||
import static android.content.Intent.FLAG_ACTIVITY_NO_ANIMATION;
|
import static android.content.Intent.FLAG_ACTIVITY_NO_ANIMATION;
|
||||||
import static android.os.Build.VERSION.SDK_INT;
|
import static android.os.Build.VERSION.SDK_INT;
|
||||||
|
import static android.os.Process.myPid;
|
||||||
import static androidx.core.app.NotificationCompat.VISIBILITY_SECRET;
|
import static androidx.core.app.NotificationCompat.VISIBILITY_SECRET;
|
||||||
import static java.util.logging.Level.INFO;
|
import static java.util.logging.Level.INFO;
|
||||||
import static java.util.logging.Level.WARNING;
|
import static java.util.logging.Level.WARNING;
|
||||||
@@ -56,8 +57,10 @@ 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_CHANNEL_ID;
|
||||||
import static org.briarproject.briar.api.android.AndroidNotificationManager.FAILURE_NOTIFICATION_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_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.AndroidNotificationManager.ONGOING_NOTIFICATION_ID;
|
||||||
import static org.briarproject.briar.api.android.LockManager.ACTION_LOCK;
|
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 {
|
public class BriarService extends Service {
|
||||||
|
|
||||||
@@ -120,11 +123,17 @@ public class BriarService extends Service {
|
|||||||
if (SDK_INT >= 26) {
|
if (SDK_INT >= 26) {
|
||||||
NotificationManager nm = (NotificationManager)
|
NotificationManager nm = (NotificationManager)
|
||||||
requireNonNull(getSystemService(NOTIFICATION_SERVICE));
|
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(
|
NotificationChannel ongoingChannel = new NotificationChannel(
|
||||||
ONGOING_CHANNEL_ID,
|
ONGOING_CHANNEL_ID,
|
||||||
getString(R.string.ongoing_notification_title),
|
getString(R.string.ongoing_notification_title),
|
||||||
IMPORTANCE_NONE);
|
IMPORTANCE_LOW);
|
||||||
ongoingChannel.setLockscreenVisibility(VISIBILITY_SECRET);
|
ongoingChannel.setLockscreenVisibility(VISIBILITY_SECRET);
|
||||||
|
ongoingChannel.setShowBadge(false);
|
||||||
nm.createNotificationChannel(ongoingChannel);
|
nm.createNotificationChannel(ongoingChannel);
|
||||||
NotificationChannel failureChannel = new NotificationChannel(
|
NotificationChannel failureChannel = new NotificationChannel(
|
||||||
FAILURE_CHANNEL_ID,
|
FAILURE_CHANNEL_ID,
|
||||||
@@ -170,6 +179,7 @@ public class BriarService extends Service {
|
|||||||
@Override
|
@Override
|
||||||
protected void attachBaseContext(Context base) {
|
protected void attachBaseContext(Context base) {
|
||||||
super.attachBaseContext(Localizer.getInstance().setLocale(base));
|
super.attachBaseContext(Localizer.getInstance().setLocale(base));
|
||||||
|
Localizer.getInstance().setLocale(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void showStartupFailureNotification(StartResult result) {
|
private void showStartupFailureNotification(StartResult result) {
|
||||||
@@ -202,7 +212,12 @@ public class BriarService extends Service {
|
|||||||
@Override
|
@Override
|
||||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||||
if (ACTION_LOCK.equals(intent.getAction())) {
|
if (ACTION_LOCK.equals(intent.getAction())) {
|
||||||
lockManager.setLocked(true);
|
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());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return START_NOT_STICKY; // Don't restart automatically if killed
|
return START_NOT_STICKY; // Don't restart automatically if killed
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -68,7 +68,21 @@ public class Localizer {
|
|||||||
return new Locale(tag);
|
return new Locale(tag);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns the localized version of context
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
public Context setLocale(Context context) {
|
public Context setLocale(Context context) {
|
||||||
Resources res = context.getResources();
|
Resources res = context.getResources();
|
||||||
Configuration conf = res.getConfiguration();
|
Configuration conf = res.getConfiguration();
|
||||||
@@ -82,7 +96,7 @@ public class Localizer {
|
|||||||
Locale.setDefault(locale);
|
Locale.setDefault(locale);
|
||||||
if (SDK_INT >= 17) {
|
if (SDK_INT >= 17) {
|
||||||
conf.setLocale(locale);
|
conf.setLocale(locale);
|
||||||
context.createConfigurationContext(conf);
|
context = context.createConfigurationContext(conf);
|
||||||
} else
|
} else
|
||||||
conf.locale = locale;
|
conf.locale = locale;
|
||||||
//noinspection deprecation
|
//noinspection deprecation
|
||||||
|
|||||||
@@ -32,8 +32,10 @@ import androidx.lifecycle.LiveData;
|
|||||||
import androidx.lifecycle.MutableLiveData;
|
import androidx.lifecycle.MutableLiveData;
|
||||||
|
|
||||||
import static android.app.AlarmManager.ELAPSED_REALTIME;
|
import static android.app.AlarmManager.ELAPSED_REALTIME;
|
||||||
|
import static android.app.PendingIntent.FLAG_UPDATE_CURRENT;
|
||||||
import static android.app.PendingIntent.getService;
|
import static android.app.PendingIntent.getService;
|
||||||
import static android.content.Context.ALARM_SERVICE;
|
import static android.content.Context.ALARM_SERVICE;
|
||||||
|
import static android.os.Process.myPid;
|
||||||
import static android.os.SystemClock.elapsedRealtime;
|
import static android.os.SystemClock.elapsedRealtime;
|
||||||
import static java.util.concurrent.TimeUnit.MINUTES;
|
import static java.util.concurrent.TimeUnit.MINUTES;
|
||||||
import static java.util.logging.Level.WARNING;
|
import static java.util.logging.Level.WARNING;
|
||||||
@@ -75,23 +77,25 @@ public class LockManagerImpl implements LockManager, Service, EventListener {
|
|||||||
LockManagerImpl(Application app, SettingsManager settingsManager,
|
LockManagerImpl(Application app, SettingsManager settingsManager,
|
||||||
AndroidNotificationManager notificationManager,
|
AndroidNotificationManager notificationManager,
|
||||||
@DatabaseExecutor Executor dbExecutor) {
|
@DatabaseExecutor Executor dbExecutor) {
|
||||||
this.appContext = app.getApplicationContext();
|
appContext = app.getApplicationContext();
|
||||||
this.settingsManager = settingsManager;
|
this.settingsManager = settingsManager;
|
||||||
this.notificationManager = notificationManager;
|
this.notificationManager = notificationManager;
|
||||||
this.dbExecutor = dbExecutor;
|
this.dbExecutor = dbExecutor;
|
||||||
this.alarmManager =
|
alarmManager =
|
||||||
(AlarmManager) appContext.getSystemService(ALARM_SERVICE);
|
(AlarmManager) appContext.getSystemService(ALARM_SERVICE);
|
||||||
Intent i =
|
Intent i =
|
||||||
new Intent(ACTION_LOCK, null, appContext, BriarService.class);
|
new Intent(ACTION_LOCK, null, appContext, BriarService.class);
|
||||||
this.lockIntent = getService(appContext, 0, i, 0);
|
i.putExtra(EXTRA_PID, myPid());
|
||||||
this.timeoutNever = Integer.valueOf(
|
// When not using FLAG_UPDATE_CURRENT, the intent might have no extras
|
||||||
|
lockIntent = getService(appContext, 0, i, FLAG_UPDATE_CURRENT);
|
||||||
|
timeoutNever = Integer.parseInt(
|
||||||
appContext.getString(R.string.pref_lock_timeout_value_never));
|
appContext.getString(R.string.pref_lock_timeout_value_never));
|
||||||
this.timeoutDefault = Integer.valueOf(
|
timeoutDefault = Integer.parseInt(
|
||||||
appContext.getString(R.string.pref_lock_timeout_value_default));
|
appContext.getString(R.string.pref_lock_timeout_value_default));
|
||||||
this.timeoutMinutes = timeoutNever;
|
timeoutMinutes = timeoutNever;
|
||||||
|
|
||||||
// setting this in the constructor makes #getValue() @NonNull
|
// setting this in the constructor makes #getValue() @NonNull
|
||||||
this.lockable.setValue(false);
|
lockable.setValue(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -148,7 +152,7 @@ public class LockManagerImpl implements LockManager, Service, EventListener {
|
|||||||
boolean oldValue = lockable.getValue();
|
boolean oldValue = lockable.getValue();
|
||||||
boolean newValue = hasScreenLock(appContext) && lockableSetting;
|
boolean newValue = hasScreenLock(appContext) && lockableSetting;
|
||||||
if (oldValue != newValue) {
|
if (oldValue != newValue) {
|
||||||
this.lockable.setValue(newValue);
|
lockable.setValue(newValue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,47 @@
|
|||||||
|
package org.briarproject.briar.android.account;
|
||||||
|
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.os.Bundle;
|
||||||
|
|
||||||
|
import org.briarproject.briar.R;
|
||||||
|
import org.briarproject.briar.android.activity.ActivityComponent;
|
||||||
|
import org.briarproject.briar.android.activity.BaseActivity;
|
||||||
|
import org.briarproject.briar.android.fragment.BaseFragment;
|
||||||
|
|
||||||
|
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK;
|
||||||
|
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP;
|
||||||
|
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
|
||||||
|
import static android.content.Intent.FLAG_ACTIVITY_TASK_ON_HOME;
|
||||||
|
|
||||||
|
public class NewOrRecoverActivity extends BaseActivity implements
|
||||||
|
BaseFragment.BaseFragmentListener, SetupNewAccountChosenListener {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void injectActivity(ActivityComponent component) {
|
||||||
|
component.inject(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(Bundle state) {
|
||||||
|
super.onCreate(state);
|
||||||
|
// fade-in after splash screen instead of default animation
|
||||||
|
overridePendingTransition(R.anim.fade_in, R.anim.fade_out);
|
||||||
|
setContentView(R.layout.activity_fragment_container);
|
||||||
|
NewOrRecoverFragment fragment = NewOrRecoverFragment.newInstance();
|
||||||
|
showInitialFragment(fragment);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setupNewAccountChosen () {
|
||||||
|
finish();
|
||||||
|
Intent i = new Intent(this, SetupActivity.class);
|
||||||
|
i.addFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TOP |
|
||||||
|
FLAG_ACTIVITY_CLEAR_TASK | FLAG_ACTIVITY_TASK_ON_HOME);
|
||||||
|
startActivity(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void runOnDbThread(Runnable runnable) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
package org.briarproject.briar.android.account;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.Button;
|
||||||
|
|
||||||
|
import org.briarproject.briar.R;
|
||||||
|
import org.briarproject.briar.android.activity.ActivityComponent;
|
||||||
|
import org.briarproject.briar.android.fragment.BaseFragment;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
|
public class NewOrRecoverFragment extends BaseFragment {
|
||||||
|
|
||||||
|
public static final String TAG = NewOrRecoverFragment.class.getName();
|
||||||
|
|
||||||
|
protected SetupNewAccountChosenListener listener;
|
||||||
|
|
||||||
|
public static NewOrRecoverFragment newInstance() {
|
||||||
|
Bundle bundle = new Bundle();
|
||||||
|
NewOrRecoverFragment fragment = new NewOrRecoverFragment();
|
||||||
|
fragment.setArguments(bundle);
|
||||||
|
return fragment;
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
requireActivity().setTitle(R.string.setup_title);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable
|
||||||
|
ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||||
|
View view = inflater.inflate(R.layout.fragment_new_or_recover,
|
||||||
|
container, false);
|
||||||
|
Button button = view.findViewById(R.id.buttonSetupNewAccount);
|
||||||
|
button.setOnClickListener(e -> {
|
||||||
|
listener.setupNewAccountChosen();
|
||||||
|
});
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAttach(Context context) {
|
||||||
|
super.onAttach(context);
|
||||||
|
listener = (SetupNewAccountChosenListener) context;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getUniqueTag() {
|
||||||
|
return TAG;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void injectFragment(ActivityComponent component) {
|
||||||
|
component.inject(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
package org.briarproject.briar.android.account;
|
||||||
|
|
||||||
|
import androidx.annotation.UiThread;
|
||||||
|
|
||||||
|
public interface SetupNewAccountChosenListener {
|
||||||
|
@UiThread
|
||||||
|
void setupNewAccountChosen();
|
||||||
|
}
|
||||||
@@ -77,7 +77,7 @@ public class UnlockActivity extends BaseActivity {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onActivityResult(int requestCode, int resultCode,
|
protected void onActivityResult(int requestCode, int resultCode,
|
||||||
Intent data) {
|
@Nullable Intent data) {
|
||||||
super.onActivityResult(requestCode, resultCode, data);
|
super.onActivityResult(requestCode, resultCode, data);
|
||||||
if (requestCode == REQUEST_KEYGUARD_UNLOCK) {
|
if (requestCode == REQUEST_KEYGUARD_UNLOCK) {
|
||||||
if (resultCode == RESULT_OK) unlock();
|
if (resultCode == RESULT_OK) unlock();
|
||||||
|
|||||||
@@ -2,10 +2,13 @@ package org.briarproject.briar.android.activity;
|
|||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.db.DatabaseComponent;
|
||||||
import org.briarproject.briar.android.AndroidComponent;
|
import org.briarproject.briar.android.AndroidComponent;
|
||||||
import org.briarproject.briar.android.StartupFailureActivity;
|
import org.briarproject.briar.android.StartupFailureActivity;
|
||||||
import org.briarproject.briar.android.account.AuthorNameFragment;
|
import org.briarproject.briar.android.account.AuthorNameFragment;
|
||||||
import org.briarproject.briar.android.account.DozeFragment;
|
import org.briarproject.briar.android.account.DozeFragment;
|
||||||
|
import org.briarproject.briar.android.account.NewOrRecoverActivity;
|
||||||
|
import org.briarproject.briar.android.account.NewOrRecoverFragment;
|
||||||
import org.briarproject.briar.android.account.SetPasswordFragment;
|
import org.briarproject.briar.android.account.SetPasswordFragment;
|
||||||
import org.briarproject.briar.android.account.SetupActivity;
|
import org.briarproject.briar.android.account.SetupActivity;
|
||||||
import org.briarproject.briar.android.account.UnlockActivity;
|
import org.briarproject.briar.android.account.UnlockActivity;
|
||||||
@@ -77,20 +80,31 @@ import org.briarproject.briar.android.sharing.ShareBlogFragment;
|
|||||||
import org.briarproject.briar.android.sharing.ShareForumActivity;
|
import org.briarproject.briar.android.sharing.ShareForumActivity;
|
||||||
import org.briarproject.briar.android.sharing.ShareForumFragment;
|
import org.briarproject.briar.android.sharing.ShareForumFragment;
|
||||||
import org.briarproject.briar.android.sharing.SharingModule;
|
import org.briarproject.briar.android.sharing.SharingModule;
|
||||||
|
//import org.briarproject.briar.android.socialbackup.CustodianDisplayFragment;
|
||||||
|
import org.briarproject.briar.android.socialbackup.CustodianSelectorFragment;
|
||||||
|
import org.briarproject.briar.android.socialbackup.DistributedBackupActivity;
|
||||||
|
import org.briarproject.briar.android.socialbackup.ExistingBackupFragment;
|
||||||
|
import org.briarproject.briar.android.socialbackup.OldDistributedBackupActivity;
|
||||||
|
import org.briarproject.briar.android.socialbackup.ShardsSentFragment;
|
||||||
|
import org.briarproject.briar.android.socialbackup.ThresholdSelectorFragment;
|
||||||
|
import org.briarproject.briar.android.socialbackup.creation.CreateBackupController;
|
||||||
|
import org.briarproject.briar.android.socialbackup.creation.CreateBackupModule;
|
||||||
import org.briarproject.briar.android.splash.SplashScreenActivity;
|
import org.briarproject.briar.android.splash.SplashScreenActivity;
|
||||||
import org.briarproject.briar.android.test.TestDataActivity;
|
import org.briarproject.briar.android.test.TestDataActivity;
|
||||||
|
import org.h2.util.New;
|
||||||
|
|
||||||
import dagger.Component;
|
import dagger.Component;
|
||||||
|
|
||||||
@ActivityScope
|
@ActivityScope
|
||||||
@Component(modules = {
|
@Component(modules ={
|
||||||
ActivityModule.class,
|
ActivityModule.class,
|
||||||
BlogModule.class,
|
BlogModule.class,
|
||||||
CreateGroupModule.class,
|
CreateGroupModule.class,
|
||||||
GroupInvitationModule.class,
|
GroupInvitationModule.class,
|
||||||
GroupMemberModule.class,
|
GroupMemberModule.class,
|
||||||
GroupRevealModule.class,
|
GroupRevealModule.class,
|
||||||
SharingModule.SharingLegacyModule.class
|
SharingModule.SharingLegacyModule.class,
|
||||||
|
CreateBackupModule.class
|
||||||
}, dependencies = AndroidComponent.class)
|
}, dependencies = AndroidComponent.class)
|
||||||
public interface ActivityComponent {
|
public interface ActivityComponent {
|
||||||
|
|
||||||
@@ -182,6 +196,8 @@ public interface ActivityComponent {
|
|||||||
|
|
||||||
void inject(CrashReportActivity crashReportActivity);
|
void inject(CrashReportActivity crashReportActivity);
|
||||||
|
|
||||||
|
void inject(NewOrRecoverActivity newOrRecoverActivity);
|
||||||
|
|
||||||
// Fragments
|
// Fragments
|
||||||
|
|
||||||
void inject(AuthorNameFragment fragment);
|
void inject(AuthorNameFragment fragment);
|
||||||
@@ -238,4 +254,21 @@ public interface ActivityComponent {
|
|||||||
|
|
||||||
void inject(ConfirmAvatarDialogFragment fragment);
|
void inject(ConfirmAvatarDialogFragment fragment);
|
||||||
|
|
||||||
|
void inject(ThresholdSelectorFragment thresholdSelectorFragment);
|
||||||
|
|
||||||
|
void inject(DistributedBackupActivity distributedBackupActivity);
|
||||||
|
// void inject(CreateBackupController createBackupController);
|
||||||
|
// void inject(CustodianDisplayFragment custodianDisplayFragment);
|
||||||
|
|
||||||
|
void inject(DatabaseComponent databaseComponent);
|
||||||
|
|
||||||
|
void inject(CustodianSelectorFragment custodianSelectorFragment);
|
||||||
|
|
||||||
|
void inject(ShardsSentFragment shardsSentFragment);
|
||||||
|
|
||||||
|
void inject(ExistingBackupFragment existingBackupFragment);
|
||||||
|
|
||||||
|
void inject(NewOrRecoverFragment newOrRecoverFragment);
|
||||||
|
|
||||||
|
void inject(OldDistributedBackupActivity oldDistributedBackupActivity);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -109,6 +109,7 @@ public abstract class BaseActivity extends AppCompatActivity
|
|||||||
protected void attachBaseContext(Context base) {
|
protected void attachBaseContext(Context base) {
|
||||||
super.attachBaseContext(
|
super.attachBaseContext(
|
||||||
Localizer.getInstance().setLocale(base));
|
Localizer.getInstance().setLocale(base));
|
||||||
|
Localizer.getInstance().setLocale(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ActivityComponent getActivityComponent() {
|
public ActivityComponent getActivityComponent() {
|
||||||
|
|||||||
@@ -124,7 +124,7 @@ public class AddContactViewModel extends DbViewModel {
|
|||||||
return addContactResult;
|
return addContactResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void updatePendingContact(String name, PendingContact p) {
|
void updatePendingContact(String name, PendingContact p) {
|
||||||
runOnDbThread(() -> {
|
runOnDbThread(() -> {
|
||||||
try {
|
try {
|
||||||
contactManager.removePendingContact(p.getId());
|
contactManager.removePendingContact(p.getId());
|
||||||
|
|||||||
@@ -29,11 +29,11 @@ import org.briarproject.briar.android.fragment.BaseFragment;
|
|||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.StringRes;
|
import androidx.annotation.StringRes;
|
||||||
import androidx.appcompat.app.AlertDialog.Builder;
|
import androidx.appcompat.app.AlertDialog.Builder;
|
||||||
import androidx.lifecycle.LifecycleOwner;
|
import androidx.lifecycle.LifecycleOwner;
|
||||||
import androidx.lifecycle.ViewModelProvider;
|
import androidx.lifecycle.ViewModelProvider;
|
||||||
import androidx.lifecycle.ViewModelProviders;
|
|
||||||
|
|
||||||
import static android.view.View.INVISIBLE;
|
import static android.view.View.INVISIBLE;
|
||||||
import static android.view.View.VISIBLE;
|
import static android.view.View.VISIBLE;
|
||||||
@@ -48,6 +48,7 @@ import static org.briarproject.briar.android.util.UiUtils.getDialogIcon;
|
|||||||
public class NicknameFragment extends BaseFragment {
|
public class NicknameFragment extends BaseFragment {
|
||||||
|
|
||||||
private static final String TAG = NicknameFragment.class.getName();
|
private static final String TAG = NicknameFragment.class.getName();
|
||||||
|
private static final String SAVED_LINK = "savedLink";
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
ViewModelProvider.Factory viewModelFactory;
|
ViewModelProvider.Factory viewModelFactory;
|
||||||
@@ -67,6 +68,20 @@ public class NicknameFragment extends BaseFragment {
|
|||||||
@Override
|
@Override
|
||||||
public void injectFragment(ActivityComponent component) {
|
public void injectFragment(ActivityComponent component) {
|
||||||
component.inject(this);
|
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
|
@Nullable
|
||||||
@@ -74,14 +89,9 @@ public class NicknameFragment extends BaseFragment {
|
|||||||
public View onCreateView(LayoutInflater inflater,
|
public View onCreateView(LayoutInflater inflater,
|
||||||
@Nullable ViewGroup container,
|
@Nullable ViewGroup container,
|
||||||
@Nullable Bundle savedInstanceState) {
|
@Nullable Bundle savedInstanceState) {
|
||||||
if (getActivity() == null || getContext() == null) return null;
|
|
||||||
|
|
||||||
View v = inflater.inflate(R.layout.fragment_nickname,
|
View v = inflater.inflate(R.layout.fragment_nickname,
|
||||||
container, false);
|
container, false);
|
||||||
|
|
||||||
viewModel = ViewModelProviders.of(getActivity(), viewModelFactory)
|
|
||||||
.get(AddContactViewModel.class);
|
|
||||||
|
|
||||||
contactNameLayout = v.findViewById(R.id.contactNameLayout);
|
contactNameLayout = v.findViewById(R.id.contactNameLayout);
|
||||||
contactNameInput = v.findViewById(R.id.contactNameInput);
|
contactNameInput = v.findViewById(R.id.contactNameInput);
|
||||||
|
|
||||||
@@ -93,6 +103,12 @@ public class NicknameFragment extends BaseFragment {
|
|||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSaveInstanceState(@NonNull Bundle outState) {
|
||||||
|
super.onSaveInstanceState(outState);
|
||||||
|
outState.putString(SAVED_LINK, viewModel.getRemoteHandshakeLink());
|
||||||
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private String getNicknameOrNull() {
|
private String getNicknameOrNull() {
|
||||||
Editable text = contactNameInput.getText();
|
Editable text = contactNameInput.getText();
|
||||||
|
|||||||
@@ -56,8 +56,11 @@ public abstract class BaseContactSelectorFragment<I extends SelectableContactIte
|
|||||||
|
|
||||||
Bundle args = requireArguments();
|
Bundle args = requireArguments();
|
||||||
byte[] b = args.getByteArray(GROUP_ID);
|
byte[] b = args.getByteArray(GROUP_ID);
|
||||||
if (b == null) throw new IllegalStateException("No GroupId");
|
// if (b == null) throw new IllegalStateException("No GroupId");
|
||||||
groupId = new GroupId(b);
|
// TODO find what the groupId should be when selecting custodians
|
||||||
|
if (b != null) {
|
||||||
|
groupId = new GroupId(b);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ public abstract class ContactSelectorFragment extends
|
|||||||
|
|
||||||
public static final String TAG = ContactSelectorFragment.class.getName();
|
public static final String TAG = ContactSelectorFragment.class.getName();
|
||||||
|
|
||||||
private Menu menu;
|
protected Menu menu;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected ContactSelectorAdapter getAdapter(Context context,
|
protected ContactSelectorAdapter getAdapter(Context context,
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|||||||
|
|
||||||
import java.text.DateFormat;
|
import java.text.DateFormat;
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.TimeZone;
|
import java.util.TimeZone;
|
||||||
import java.util.logging.Formatter;
|
import java.util.logging.Formatter;
|
||||||
@@ -17,6 +18,16 @@ import static java.util.Locale.US;
|
|||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
public class BriefLogFormatter extends Formatter {
|
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 Object lock = new Object();
|
||||||
private final DateFormat dateFormat; // Locking: lock
|
private final DateFormat dateFormat; // Locking: lock
|
||||||
private final Date date; // Locking: lock
|
private final Date date; // Locking: lock
|
||||||
|
|||||||
@@ -21,6 +21,10 @@ public class CachingLogHandler extends Handler {
|
|||||||
// Locking: lock
|
// Locking: lock
|
||||||
private final Queue<LogRecord> recent = new LinkedList<>();
|
private final Queue<LogRecord> recent = new LinkedList<>();
|
||||||
|
|
||||||
|
// package-private constructor
|
||||||
|
CachingLogHandler() {
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void publish(LogRecord record) {
|
public void publish(LogRecord record) {
|
||||||
synchronized (lock) {
|
synchronized (lock) {
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
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);
|
||||||
|
}
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
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();
|
||||||
|
}
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
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');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -7,6 +7,7 @@ import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
|||||||
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
||||||
import org.briarproject.briar.R;
|
import org.briarproject.briar.R;
|
||||||
import org.briarproject.briar.android.BriarService;
|
import org.briarproject.briar.android.BriarService;
|
||||||
|
import org.briarproject.briar.android.account.NewOrRecoverActivity;
|
||||||
import org.briarproject.briar.android.account.SetupActivity;
|
import org.briarproject.briar.android.account.SetupActivity;
|
||||||
import org.briarproject.briar.android.activity.ActivityComponent;
|
import org.briarproject.briar.android.activity.ActivityComponent;
|
||||||
import org.briarproject.briar.android.activity.BaseActivity;
|
import org.briarproject.briar.android.activity.BaseActivity;
|
||||||
@@ -107,7 +108,7 @@ public class StartupActivity extends BaseActivity implements
|
|||||||
private void onAccountDeleted() {
|
private void onAccountDeleted() {
|
||||||
setResult(RESULT_CANCELED);
|
setResult(RESULT_CANCELED);
|
||||||
finish();
|
finish();
|
||||||
Intent i = new Intent(this, SetupActivity.class);
|
Intent i = new Intent(this, NewOrRecoverActivity.class);
|
||||||
i.addFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TOP |
|
i.addFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TOP |
|
||||||
FLAG_ACTIVITY_CLEAR_TASK | FLAG_ACTIVITY_TASK_ON_HOME);
|
FLAG_ACTIVITY_CLEAR_TASK | FLAG_ACTIVITY_TASK_ON_HOME);
|
||||||
startActivity(i);
|
startActivity(i);
|
||||||
|
|||||||
@@ -68,6 +68,7 @@ import static android.view.View.VISIBLE;
|
|||||||
import static androidx.core.view.GravityCompat.START;
|
import static androidx.core.view.GravityCompat.START;
|
||||||
import static androidx.drawerlayout.widget.DrawerLayout.LOCK_MODE_LOCKED_CLOSED;
|
import static androidx.drawerlayout.widget.DrawerLayout.LOCK_MODE_LOCKED_CLOSED;
|
||||||
import static androidx.fragment.app.FragmentManager.POP_BACK_STACK_INCLUSIVE;
|
import static androidx.fragment.app.FragmentManager.POP_BACK_STACK_INCLUSIVE;
|
||||||
|
import static androidx.lifecycle.Lifecycle.State.STARTED;
|
||||||
import static java.util.Objects.requireNonNull;
|
import static java.util.Objects.requireNonNull;
|
||||||
import static java.util.logging.Logger.getLogger;
|
import static java.util.logging.Logger.getLogger;
|
||||||
import static org.briarproject.bramble.api.lifecycle.LifecycleManager.LifecycleState.RUNNING;
|
import static org.briarproject.bramble.api.lifecycle.LifecycleManager.LifecycleState.RUNNING;
|
||||||
@@ -297,6 +298,12 @@ public class NavDrawerActivity extends BriarActivity implements
|
|||||||
finish();
|
finish();
|
||||||
} else if (fm.getBackStackEntryCount() == 0
|
} else if (fm.getBackStackEntryCount() == 0
|
||||||
&& fm.findFragmentByTag(ContactListFragment.TAG) == null) {
|
&& 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;
|
||||||
|
}
|
||||||
/*
|
/*
|
||||||
* This makes sure that the first fragment (ContactListFragment) the
|
* This makes sure that the first fragment (ContactListFragment) the
|
||||||
* user sees is the same as the last fragment the user sees before
|
* user sees is the same as the last fragment the user sees before
|
||||||
|
|||||||
@@ -79,9 +79,10 @@ public class GroupActivity extends
|
|||||||
|
|
||||||
// start with group disabled and enable when not dissolved
|
// start with group disabled and enable when not dissolved
|
||||||
setGroupEnabled(false);
|
setGroupEnabled(false);
|
||||||
viewModel.isDissolved().observeEvent(this, dissolved -> {
|
viewModel.isDissolved().observe(this, dissolved -> {
|
||||||
setGroupEnabled(!dissolved);
|
setGroupEnabled(!dissolved);
|
||||||
if (dissolved) onGroupDissolved();
|
// only show dialog when no prior state
|
||||||
|
if (dissolved && state == null) onGroupDissolved();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -153,7 +154,7 @@ public class GroupActivity extends
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onReplyClick(GroupMessageItem item) {
|
public void onReplyClick(GroupMessageItem item) {
|
||||||
Boolean isDissolved = viewModel.isDissolved().getLastValue();
|
Boolean isDissolved = viewModel.isDissolved().getValue();
|
||||||
if (isDissolved != null && !isDissolved) super.onReplyClick(item);
|
if (isDissolved != null && !isDissolved) super.onReplyClick(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,8 +22,6 @@ import org.briarproject.bramble.api.system.AndroidExecutor;
|
|||||||
import org.briarproject.bramble.api.system.Clock;
|
import org.briarproject.bramble.api.system.Clock;
|
||||||
import org.briarproject.briar.android.sharing.SharingController;
|
import org.briarproject.briar.android.sharing.SharingController;
|
||||||
import org.briarproject.briar.android.threaded.ThreadListViewModel;
|
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.android.AndroidNotificationManager;
|
||||||
import org.briarproject.briar.api.client.MessageTracker;
|
import org.briarproject.briar.api.client.MessageTracker;
|
||||||
import org.briarproject.briar.api.client.MessageTracker.GroupCount;
|
import org.briarproject.briar.api.client.MessageTracker.GroupCount;
|
||||||
@@ -71,8 +69,8 @@ class GroupViewModel extends ThreadListViewModel<GroupMessageItem> {
|
|||||||
private final MutableLiveData<PrivateGroup> privateGroup =
|
private final MutableLiveData<PrivateGroup> privateGroup =
|
||||||
new MutableLiveData<>();
|
new MutableLiveData<>();
|
||||||
private final MutableLiveData<Boolean> isCreator = new MutableLiveData<>();
|
private final MutableLiveData<Boolean> isCreator = new MutableLiveData<>();
|
||||||
private final MutableLiveEvent<Boolean> isDissolved =
|
private final MutableLiveData<Boolean> isDissolved =
|
||||||
new MutableLiveEvent<>();
|
new MutableLiveData<>();
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
GroupViewModel(Application application,
|
GroupViewModel(Application application,
|
||||||
@@ -129,7 +127,7 @@ class GroupViewModel extends ThreadListViewModel<GroupMessageItem> {
|
|||||||
} else if (e instanceof GroupDissolvedEvent) {
|
} else if (e instanceof GroupDissolvedEvent) {
|
||||||
GroupDissolvedEvent g = (GroupDissolvedEvent) e;
|
GroupDissolvedEvent g = (GroupDissolvedEvent) e;
|
||||||
if (g.getGroupId().equals(groupId)) {
|
if (g.getGroupId().equals(groupId)) {
|
||||||
isDissolved.setEvent(true);
|
isDissolved.setValue(true);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
super.eventOccurred(e);
|
super.eventOccurred(e);
|
||||||
@@ -164,7 +162,7 @@ class GroupViewModel extends ThreadListViewModel<GroupMessageItem> {
|
|||||||
loadList(txn -> {
|
loadList(txn -> {
|
||||||
// check first if group is dissolved
|
// check first if group is dissolved
|
||||||
isDissolved
|
isDissolved
|
||||||
.postEvent(privateGroupManager.isDissolved(txn, groupId));
|
.postValue(privateGroupManager.isDissolved(txn, groupId));
|
||||||
// now continue to load the items
|
// now continue to load the items
|
||||||
long start = now();
|
long start = now();
|
||||||
List<GroupMessageHeader> headers =
|
List<GroupMessageHeader> headers =
|
||||||
@@ -282,7 +280,7 @@ class GroupViewModel extends ThreadListViewModel<GroupMessageItem> {
|
|||||||
return isCreator;
|
return isCreator;
|
||||||
}
|
}
|
||||||
|
|
||||||
LiveEvent<Boolean> isDissolved() {
|
LiveData<Boolean> isDissolved() {
|
||||||
return isDissolved;
|
return isDissolved;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,29 +1,40 @@
|
|||||||
package org.briarproject.briar.android.reporting;
|
package org.briarproject.briar.android.reporting;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.app.Application;
|
||||||
import android.os.Process;
|
import android.os.Process;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
import org.briarproject.briar.android.logging.LogEncrypter;
|
||||||
|
|
||||||
import java.lang.Thread.UncaughtExceptionHandler;
|
import java.lang.Thread.UncaughtExceptionHandler;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
import static org.briarproject.briar.android.util.UiUtils.startDevReportActivity;
|
import static org.briarproject.briar.android.util.UiUtils.startDevReportActivity;
|
||||||
|
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
public class BriarExceptionHandler implements UncaughtExceptionHandler {
|
class BriarExceptionHandler implements UncaughtExceptionHandler {
|
||||||
|
|
||||||
private final Context ctx;
|
private final Application app;
|
||||||
|
private final LogEncrypter logEncrypter;
|
||||||
private final long appStartTime;
|
private final long appStartTime;
|
||||||
|
|
||||||
public BriarExceptionHandler(Context ctx) {
|
@Inject
|
||||||
this.ctx = ctx;
|
BriarExceptionHandler(Application app, LogEncrypter logEncrypter) {
|
||||||
this.appStartTime = System.currentTimeMillis();
|
this.app = app;
|
||||||
|
this.logEncrypter = logEncrypter;
|
||||||
|
appStartTime = System.currentTimeMillis();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void uncaughtException(Thread t, Throwable e) {
|
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
|
// activity runs in its own process, so we can kill the old one
|
||||||
startDevReportActivity(ctx, CrashReportActivity.class, e, appStartTime);
|
startDevReportActivity(app.getApplicationContext(),
|
||||||
|
CrashReportActivity.class, e, appStartTime, logKey);
|
||||||
Process.killProcess(Process.myPid());
|
Process.killProcess(Process.myPid());
|
||||||
System.exit(10);
|
System.exit(10);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,8 +25,6 @@ import org.briarproject.bramble.api.Pair;
|
|||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.briar.BuildConfig;
|
import org.briarproject.briar.BuildConfig;
|
||||||
import org.briarproject.briar.R;
|
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.MultiReportInfo;
|
||||||
import org.briarproject.briar.android.reporting.ReportData.ReportItem;
|
import org.briarproject.briar.android.reporting.ReportData.ReportItem;
|
||||||
import org.briarproject.briar.android.reporting.ReportData.SingleReportInfo;
|
import org.briarproject.briar.android.reporting.ReportData.SingleReportInfo;
|
||||||
@@ -41,8 +39,6 @@ import java.net.InetAddress;
|
|||||||
import java.net.UnknownHostException;
|
import java.net.UnknownHostException;
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.logging.Formatter;
|
|
||||||
import java.util.logging.LogRecord;
|
|
||||||
|
|
||||||
import javax.annotation.concurrent.Immutable;
|
import javax.annotation.concurrent.Immutable;
|
||||||
|
|
||||||
@@ -74,8 +70,8 @@ class BriarReportCollector {
|
|||||||
this.ctx = ctx;
|
this.ctx = ctx;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ReportData collectReportData(@Nullable Throwable t,
|
ReportData collectReportData(@Nullable Throwable t, long appStartTime,
|
||||||
long appStartTime) {
|
String logs) {
|
||||||
ReportData reportData = new ReportData()
|
ReportData reportData = new ReportData()
|
||||||
.add(getBasicInfo(t))
|
.add(getBasicInfo(t))
|
||||||
.add(getDeviceInfo());
|
.add(getDeviceInfo());
|
||||||
@@ -86,7 +82,7 @@ class BriarReportCollector {
|
|||||||
.add(getStorage())
|
.add(getStorage())
|
||||||
.add(getConnectivity())
|
.add(getConnectivity())
|
||||||
.add(getBuildConfig())
|
.add(getBuildConfig())
|
||||||
.add(getLogcat())
|
.add(getLogcat(logs))
|
||||||
.add(getDeviceFeatures());
|
.add(getDeviceFeatures());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -313,15 +309,8 @@ class BriarReportCollector {
|
|||||||
buildConfig);
|
buildConfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ReportItem getLogcat() {
|
private ReportItem getLogcat(String logs) {
|
||||||
BriarApplication app = (BriarApplication) ctx.getApplicationContext();
|
return new ReportItem("Logcat", R.string.dev_report_logcat, logs);
|
||||||
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() {
|
private ReportItem getDeviceFeatures() {
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ public class CrashReportActivity extends BaseActivity
|
|||||||
|
|
||||||
public static final String EXTRA_THROWABLE = "throwable";
|
public static final String EXTRA_THROWABLE = "throwable";
|
||||||
public static final String EXTRA_APP_START_TIME = "appStartTime";
|
public static final String EXTRA_APP_START_TIME = "appStartTime";
|
||||||
|
public static final String EXTRA_APP_LOGCAT = "logcat";
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
ViewModelProvider.Factory viewModelFactory;
|
ViewModelProvider.Factory viewModelFactory;
|
||||||
@@ -56,7 +57,8 @@ public class CrashReportActivity extends BaseActivity
|
|||||||
Intent intent = getIntent();
|
Intent intent = getIntent();
|
||||||
Throwable t = (Throwable) intent.getSerializableExtra(EXTRA_THROWABLE);
|
Throwable t = (Throwable) intent.getSerializableExtra(EXTRA_THROWABLE);
|
||||||
long appStartTime = intent.getLongExtra(EXTRA_APP_START_TIME, -1);
|
long appStartTime = intent.getLongExtra(EXTRA_APP_START_TIME, -1);
|
||||||
viewModel.init(t, appStartTime);
|
byte[] logKey = intent.getByteArrayExtra(EXTRA_APP_LOGCAT);
|
||||||
|
viewModel.init(t, appStartTime, logKey);
|
||||||
viewModel.getShowReport().observeEvent(this, show -> {
|
viewModel.getShowReport().observeEvent(this, show -> {
|
||||||
if (show) displayFragment(true);
|
if (show) displayFragment(true);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -15,4 +15,8 @@ public abstract class DevReportModule {
|
|||||||
@ViewModelKey(ReportViewModel.class)
|
@ViewModelKey(ReportViewModel.class)
|
||||||
abstract ViewModel bindReportViewModel(ReportViewModel reportViewModel);
|
abstract ViewModel bindReportViewModel(ReportViewModel reportViewModel);
|
||||||
|
|
||||||
|
@Binds
|
||||||
|
abstract Thread.UncaughtExceptionHandler bindUncaughtExceptionHandler(
|
||||||
|
BriarExceptionHandler handler);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,9 @@ import org.briarproject.bramble.api.plugin.TorConstants;
|
|||||||
import org.briarproject.bramble.api.reporting.DevReporter;
|
import org.briarproject.bramble.api.reporting.DevReporter;
|
||||||
import org.briarproject.bramble.util.AndroidUtils;
|
import org.briarproject.bramble.util.AndroidUtils;
|
||||||
import org.briarproject.briar.R;
|
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.reporting.ReportData.MultiReportInfo;
|
||||||
import org.briarproject.briar.android.viewmodel.LiveEvent;
|
import org.briarproject.briar.android.viewmodel.LiveEvent;
|
||||||
import org.briarproject.briar.android.viewmodel.MutableLiveEvent;
|
import org.briarproject.briar.android.viewmodel.MutableLiveEvent;
|
||||||
@@ -19,6 +22,7 @@ import org.json.JSONException;
|
|||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
import java.util.logging.Formatter;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
@@ -36,13 +40,16 @@ import static java.util.logging.Logger.getLogger;
|
|||||||
import static org.briarproject.bramble.api.plugin.Plugin.State.ACTIVE;
|
import static org.briarproject.bramble.api.plugin.Plugin.State.ACTIVE;
|
||||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
import static org.briarproject.bramble.util.LogUtils.logException;
|
||||||
import static org.briarproject.bramble.util.StringUtils.isNullOrEmpty;
|
import static org.briarproject.bramble.util.StringUtils.isNullOrEmpty;
|
||||||
|
import static org.briarproject.briar.android.logging.BriefLogFormatter.formatLog;
|
||||||
|
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
public class ReportViewModel extends AndroidViewModel {
|
class ReportViewModel extends AndroidViewModel {
|
||||||
|
|
||||||
private static final Logger LOG =
|
private static final Logger LOG =
|
||||||
getLogger(ReportViewModel.class.getName());
|
getLogger(ReportViewModel.class.getName());
|
||||||
|
|
||||||
|
private final CachingLogHandler logHandler;
|
||||||
|
private final LogDecrypter logDecrypter;
|
||||||
private final BriarReportCollector collector;
|
private final BriarReportCollector collector;
|
||||||
private final DevReporter reporter;
|
private final DevReporter reporter;
|
||||||
private final PluginManager pluginManager;
|
private final PluginManager pluginManager;
|
||||||
@@ -58,18 +65,39 @@ public class ReportViewModel extends AndroidViewModel {
|
|||||||
private boolean isFeedback;
|
private boolean isFeedback;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public ReportViewModel(@NonNull Application application,
|
ReportViewModel(@NonNull Application application,
|
||||||
DevReporter reporter, PluginManager pluginManager) {
|
CachingLogHandler logHandler,
|
||||||
|
LogDecrypter logDecrypter,
|
||||||
|
DevReporter reporter,
|
||||||
|
PluginManager pluginManager) {
|
||||||
super(application);
|
super(application);
|
||||||
this.collector = new BriarReportCollector(application);
|
collector = new BriarReportCollector(application);
|
||||||
|
this.logHandler = logHandler;
|
||||||
|
this.logDecrypter = logDecrypter;
|
||||||
this.reporter = reporter;
|
this.reporter = reporter;
|
||||||
this.pluginManager = pluginManager;
|
this.pluginManager = pluginManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
void init(@Nullable Throwable t, long appStartTime) {
|
void init(@Nullable Throwable t, long appStartTime,
|
||||||
|
@Nullable byte[] logKey) {
|
||||||
isFeedback = t == null;
|
isFeedback = t == null;
|
||||||
if (reportData.getValue() == null) new SingleShotAndroidExecutor(() -> {
|
if (reportData.getValue() == null) new SingleShotAndroidExecutor(() -> {
|
||||||
ReportData data = collector.collectReportData(t, appStartTime);
|
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.postValue(data);
|
reportData.postValue(data);
|
||||||
}).start();
|
}).start();
|
||||||
}
|
}
|
||||||
@@ -110,8 +138,8 @@ public class ReportViewModel extends AndroidViewModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The content of the report
|
* The content of the report that will be loaded after
|
||||||
* that will be loaded after {@link #init(Throwable, long)} was called.
|
* {@link #init(Throwable, long, byte[])} was called.
|
||||||
*/
|
*/
|
||||||
LiveData<ReportData> getReportData() {
|
LiveData<ReportData> getReportData() {
|
||||||
return reportData;
|
return reportData;
|
||||||
|
|||||||
@@ -0,0 +1,93 @@
|
|||||||
|
package org.briarproject.briar.android.socialbackup;
|
||||||
|
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.view.MenuItem;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.contact.ContactId;
|
||||||
|
import org.briarproject.bramble.api.db.DbException;
|
||||||
|
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
||||||
|
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
||||||
|
import org.briarproject.bramble.api.sync.GroupId;
|
||||||
|
import org.briarproject.briar.R;
|
||||||
|
import org.briarproject.briar.android.activity.ActivityComponent;
|
||||||
|
import org.briarproject.briar.android.socialbackup.creation.CreateBackupController;
|
||||||
|
import org.briarproject.briar.android.contactselection.BaseContactSelectorAdapter;
|
||||||
|
import org.briarproject.briar.android.contactselection.ContactSelectorController;
|
||||||
|
import org.briarproject.briar.android.contactselection.ContactSelectorFragment;
|
||||||
|
import org.briarproject.briar.android.contactselection.SelectableContactItem;
|
||||||
|
import org.briarproject.briar.android.controller.handler.ResultExceptionHandler;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
|
import static java.util.Objects.requireNonNull;
|
||||||
|
|
||||||
|
@MethodsNotNullByDefault
|
||||||
|
@ParametersNotNullByDefault
|
||||||
|
public class CustodianSelectorFragment extends ContactSelectorFragment {
|
||||||
|
|
||||||
|
public static final String TAG = CustodianSelectorFragment.class.getName();
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
CreateBackupController controller;
|
||||||
|
|
||||||
|
public static CustodianSelectorFragment newInstance() {
|
||||||
|
Bundle args = new Bundle();
|
||||||
|
|
||||||
|
CustodianSelectorFragment fragment = new CustodianSelectorFragment();
|
||||||
|
fragment.setArguments(args);
|
||||||
|
|
||||||
|
return fragment;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void injectFragment(ActivityComponent component) {
|
||||||
|
component.inject(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
|
requireActivity().setTitle(R.string.title_select_custodians);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ContactSelectorController<SelectableContactItem> getController() {
|
||||||
|
return controller;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getUniqueTag() {
|
||||||
|
return TAG;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onSelectionChanged() {
|
||||||
|
super.onSelectionChanged();
|
||||||
|
if (menu == null) return;
|
||||||
|
MenuItem item = menu.findItem(R.id.action_contacts_selected);
|
||||||
|
if (item == null) return;
|
||||||
|
|
||||||
|
BaseContactSelectorAdapter a = adapter;
|
||||||
|
selectedContacts = a.getSelectedContactIds();
|
||||||
|
|
||||||
|
int n = selectedContacts.size();
|
||||||
|
int min = 2;
|
||||||
|
boolean enough = n >= min;
|
||||||
|
|
||||||
|
item.setVisible(enough);
|
||||||
|
if (n == 0) {
|
||||||
|
Toast.makeText(getContext(), String.format(getString(R.string.select_at_least_n_contacts), min),
|
||||||
|
Toast.LENGTH_SHORT).show();
|
||||||
|
} else if (n < min) {
|
||||||
|
Toast.makeText(getContext(), String.format(getString(R.string.select_at_least_n_more_contacts), min - n),
|
||||||
|
Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
package org.briarproject.briar.android.socialbackup;
|
||||||
|
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||||
|
import org.briarproject.briar.api.socialbackup.Shard;
|
||||||
|
import org.briarproject.briar.api.socialbackup.DarkCrystal;
|
||||||
|
import org.magmacollective.darkcrystal.secretsharingwrapper.SecretSharingWrapper;
|
||||||
|
|
||||||
|
import java.security.GeneralSecurityException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
import static org.briarproject.briar.socialbackup.SocialBackupConstants.SECRET_ID_BYTES;
|
||||||
|
|
||||||
|
public class DarkCrystalImpl implements DarkCrystal {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Shard> createShards(SecretKey secret, int numShards,
|
||||||
|
int threshold) {
|
||||||
|
Random random = new Random();
|
||||||
|
byte[] secretId = new byte[SECRET_ID_BYTES];
|
||||||
|
random.nextBytes(secretId);
|
||||||
|
List<byte[]> shardsBytes = SecretSharingWrapper.share(secret.getBytes(), numShards, threshold);
|
||||||
|
List<Shard> shards = new ArrayList<>(numShards);
|
||||||
|
for (byte[] shardBytes : shardsBytes) {
|
||||||
|
shards.add(new Shard(secretId, shardBytes));
|
||||||
|
}
|
||||||
|
return shards;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SecretKey combineShards(List<Shard> shards) throws
|
||||||
|
GeneralSecurityException {
|
||||||
|
// Check each shard has the same secret Id
|
||||||
|
byte[] secretId = shards.get(0).getSecretId();
|
||||||
|
for (Shard shard : shards) {
|
||||||
|
if (!Arrays.equals(shard.getSecretId(), secretId)) throw new GeneralSecurityException();
|
||||||
|
}
|
||||||
|
List<byte[]> shardsBytes = new ArrayList<>(shards.size());
|
||||||
|
for (Shard shard : shards) {
|
||||||
|
shardsBytes.add(shard.getShard());
|
||||||
|
}
|
||||||
|
byte[] secretBytes = SecretSharingWrapper.combine(shardsBytes);
|
||||||
|
return new SecretKey(secretBytes);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
package org.briarproject.briar.android.socialbackup;
|
||||||
|
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.contact.ContactId;
|
||||||
|
import org.briarproject.bramble.api.db.DatabaseComponent;
|
||||||
|
import org.briarproject.bramble.api.db.DbException;
|
||||||
|
import org.briarproject.briar.R;
|
||||||
|
import org.briarproject.briar.android.activity.ActivityComponent;
|
||||||
|
import org.briarproject.briar.android.activity.BriarActivity;
|
||||||
|
import org.briarproject.briar.android.contactselection.ContactSelectorListener;
|
||||||
|
import org.briarproject.briar.android.fragment.BaseFragment;
|
||||||
|
import org.briarproject.briar.api.socialbackup.BackupMetadata;
|
||||||
|
import org.briarproject.briar.api.socialbackup.SocialBackupManager;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
public class DistributedBackupActivity extends BriarActivity implements
|
||||||
|
BaseFragment.BaseFragmentListener, ContactSelectorListener,
|
||||||
|
ThresholdDefinedListener, ShardsSentDismissedListener {
|
||||||
|
|
||||||
|
private Collection<ContactId> custodians;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public SocialBackupManager socialBackupManager;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public DatabaseComponent db;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void injectActivity(ActivityComponent component) {
|
||||||
|
component.inject(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
setContentView(R.layout.activity_distributed_backup);
|
||||||
|
// TODO here we should check if we already have a backup
|
||||||
|
|
||||||
|
try {
|
||||||
|
db.transaction(false, txn -> {
|
||||||
|
BackupMetadata backupMetadata = socialBackupManager.getBackupMetadata(txn);
|
||||||
|
if (backupMetadata == null) {
|
||||||
|
CustodianSelectorFragment fragment =
|
||||||
|
CustodianSelectorFragment.newInstance();
|
||||||
|
showInitialFragment(fragment);
|
||||||
|
} else {
|
||||||
|
ExistingBackupFragment fragment = ExistingBackupFragment.newInstance(backupMetadata);
|
||||||
|
showInitialFragment(fragment);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (DbException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void contactsSelected(Collection<ContactId> contacts) {
|
||||||
|
Toast.makeText(this,
|
||||||
|
String.format("selected %d contacts", contacts.size()),
|
||||||
|
Toast.LENGTH_SHORT).show();
|
||||||
|
custodians = contacts;
|
||||||
|
ThresholdSelectorFragment fragment = ThresholdSelectorFragment.newInstance(contacts.size());
|
||||||
|
showNextFragment(fragment);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void thresholdDefined(int threshold) throws DbException {
|
||||||
|
db.transaction(false, txn -> {
|
||||||
|
socialBackupManager.createBackup(txn, (List<ContactId>) custodians, threshold);
|
||||||
|
ShardsSentFragment fragment = new ShardsSentFragment();
|
||||||
|
showNextFragment(fragment);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void shardsSentDismissed() {
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
package org.briarproject.briar.android.socialbackup;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
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.identity.Author;
|
||||||
|
import org.briarproject.briar.R;
|
||||||
|
import org.briarproject.briar.android.activity.ActivityComponent;
|
||||||
|
import org.briarproject.briar.android.fragment.BaseFragment;
|
||||||
|
import org.briarproject.briar.api.socialbackup.BackupMetadata;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
|
public class ExistingBackupFragment extends BaseFragment {
|
||||||
|
|
||||||
|
private static final String THRESHOLD = "threshold";
|
||||||
|
private static final String CUSTODIANS = "custodians";
|
||||||
|
public static final String TAG = ExistingBackupFragment.class.getName();
|
||||||
|
|
||||||
|
public static ExistingBackupFragment newInstance(
|
||||||
|
BackupMetadata backupMetadata) {
|
||||||
|
Bundle bundle = new Bundle();
|
||||||
|
List<Author> custodians = backupMetadata.getCustodians();
|
||||||
|
ArrayList custodianNames = new ArrayList();
|
||||||
|
for (Author custodian : custodians) {
|
||||||
|
custodianNames.add(custodian.getName());
|
||||||
|
}
|
||||||
|
bundle.putStringArrayList(CUSTODIANS, custodianNames);
|
||||||
|
bundle.putInt(THRESHOLD, backupMetadata.getThreshold());
|
||||||
|
ExistingBackupFragment fragment = new ExistingBackupFragment();
|
||||||
|
fragment.setArguments(bundle);
|
||||||
|
return fragment;
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
requireActivity().setTitle(R.string.title_distributed_backup);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable
|
||||||
|
ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||||
|
View view = inflater.inflate(R.layout.fragment_existing_backup,
|
||||||
|
container, false);
|
||||||
|
Bundle args = requireArguments();
|
||||||
|
ArrayList<String> custodianNames = args.getStringArrayList(CUSTODIANS);
|
||||||
|
String custodianNamesString = "";
|
||||||
|
for (String custodianName : custodianNames) {
|
||||||
|
custodianNamesString += custodianName + ", ";
|
||||||
|
}
|
||||||
|
TextView textViewThreshold = view.findViewById(R.id.textViewThreshold);
|
||||||
|
textViewThreshold.setText(String.format("%d of %d contacts needed to restore account", args.getInt(THRESHOLD), custodianNames.size()));
|
||||||
|
TextView textViewCustodians = view.findViewById(R.id.textViewCustodians);
|
||||||
|
textViewCustodians.setText(custodianNamesString);
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAttach(Context context) {
|
||||||
|
super.onAttach(context);
|
||||||
|
// listener = (ShardsSentDismissedListener) context;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getUniqueTag() {
|
||||||
|
return TAG;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void injectFragment(ActivityComponent component) {
|
||||||
|
component.inject(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
package org.briarproject.briar.android.socialbackup;
|
||||||
|
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.contact.ContactId;
|
||||||
|
import org.briarproject.briar.R;
|
||||||
|
import org.briarproject.briar.android.activity.ActivityComponent;
|
||||||
|
import org.briarproject.briar.android.activity.BriarActivity;
|
||||||
|
import org.briarproject.briar.android.contactselection.ContactSelectorListener;
|
||||||
|
import org.briarproject.briar.android.fragment.BaseFragment;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
public class OldDistributedBackupActivity extends BriarActivity
|
||||||
|
implements BaseFragment.BaseFragmentListener, ContactSelectorListener {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void injectActivity(ActivityComponent component) {
|
||||||
|
component.inject(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
setContentView(R.layout.activity_distributed_backup);
|
||||||
|
|
||||||
|
// CustodianDisplayFragment fragment =
|
||||||
|
// CustodianDisplayFragment.newInstance();
|
||||||
|
//
|
||||||
|
// showInitialFragment(fragment);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void contactsSelected(Collection<ContactId> contacts) {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
package org.briarproject.briar.android.socialbackup;
|
||||||
|
|
||||||
|
import androidx.annotation.UiThread;
|
||||||
|
|
||||||
|
public interface ShardsSentDismissedListener {
|
||||||
|
|
||||||
|
@UiThread
|
||||||
|
void shardsSentDismissed();
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
package org.briarproject.briar.android.socialbackup;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.Button;
|
||||||
|
|
||||||
|
import org.briarproject.briar.R;
|
||||||
|
import org.briarproject.briar.android.activity.ActivityComponent;
|
||||||
|
import org.briarproject.briar.android.fragment.BaseFragment;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
|
public class ShardsSentFragment extends BaseFragment {
|
||||||
|
|
||||||
|
public static final String TAG = ShardsSentFragment.class.getName();
|
||||||
|
|
||||||
|
protected ShardsSentDismissedListener listener;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
requireActivity().setTitle(R.string.title_distributed_backup);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||||
|
View view = inflater.inflate(R.layout.fragment_shards_sent,
|
||||||
|
container, false);
|
||||||
|
|
||||||
|
Button button = view.findViewById(R.id.button);
|
||||||
|
button.setOnClickListener(e -> {
|
||||||
|
listener.shardsSentDismissed();
|
||||||
|
});
|
||||||
|
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAttach(Context context) {
|
||||||
|
super.onAttach(context);
|
||||||
|
listener = (ShardsSentDismissedListener) context;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getUniqueTag() {
|
||||||
|
return TAG;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void injectFragment(ActivityComponent component) {
|
||||||
|
component.inject(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
package org.briarproject.briar.android.socialbackup;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.db.DbException;
|
||||||
|
|
||||||
|
import androidx.annotation.UiThread;
|
||||||
|
|
||||||
|
public interface ThresholdDefinedListener {
|
||||||
|
|
||||||
|
@UiThread
|
||||||
|
void thresholdDefined(int threshold) throws DbException;
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,169 @@
|
|||||||
|
package org.briarproject.briar.android.socialbackup;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.Menu;
|
||||||
|
import android.view.MenuInflater;
|
||||||
|
import android.view.MenuItem;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.ImageView;
|
||||||
|
import android.widget.SeekBar;
|
||||||
|
import android.widget.TextView;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.db.DbException;
|
||||||
|
import org.briarproject.briar.R;
|
||||||
|
import org.briarproject.briar.android.activity.ActivityComponent;
|
||||||
|
import org.briarproject.briar.android.contactselection.ContactSelectorListener;
|
||||||
|
import org.briarproject.briar.android.fragment.BaseFragment;
|
||||||
|
import org.magmacollective.darkcrystal.secretsharingwrapper.SecretSharingWrapper;
|
||||||
|
|
||||||
|
import static java.util.Objects.requireNonNull;
|
||||||
|
|
||||||
|
public class ThresholdSelectorFragment extends BaseFragment {
|
||||||
|
|
||||||
|
public static final String TAG = ThresholdSelectorFragment.class.getName();
|
||||||
|
private static final String NUMBER_CUSTODIANS = "numberCustodians";
|
||||||
|
|
||||||
|
protected ThresholdDefinedListener listener;
|
||||||
|
|
||||||
|
// TODO this should be the actual number of custodians
|
||||||
|
private int numberOfCustodians;
|
||||||
|
private int threshold;
|
||||||
|
private int recommendedThreshold;
|
||||||
|
private SeekBar seekBar;
|
||||||
|
private TextView thresholdRepresentation;
|
||||||
|
private TextView message;
|
||||||
|
private TextView mOfn;
|
||||||
|
|
||||||
|
public static ThresholdSelectorFragment newInstance(int numberCustodians) {
|
||||||
|
Bundle bundle = new Bundle();
|
||||||
|
bundle.putInt(NUMBER_CUSTODIANS, numberCustodians);
|
||||||
|
ThresholdSelectorFragment fragment = new ThresholdSelectorFragment();
|
||||||
|
fragment.setArguments(bundle);
|
||||||
|
return fragment;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
requireActivity().setTitle(R.string.title_define_threshold);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||||
|
View view = inflater.inflate(R.layout.fragment_select_threshold,
|
||||||
|
container, false);
|
||||||
|
Bundle args = requireArguments();
|
||||||
|
numberOfCustodians = args.getInt(NUMBER_CUSTODIANS);
|
||||||
|
|
||||||
|
seekBar = view.findViewById(R.id.seekBar);
|
||||||
|
thresholdRepresentation = view.findViewById(R.id.textViewThresholdRepresentation);
|
||||||
|
message = view.findViewById(R.id.textViewMessage);
|
||||||
|
mOfn = view.findViewById(R.id.textViewmOfn);
|
||||||
|
int max = numberOfCustodians - 3;
|
||||||
|
seekBar.setMax(max);
|
||||||
|
seekBar.setOnSeekBarChangeListener(new SeekBarListener());
|
||||||
|
recommendedThreshold = SecretSharingWrapper.defaultThreshold(numberOfCustodians);
|
||||||
|
threshold = recommendedThreshold;
|
||||||
|
seekBar.setProgress(threshold - 2);
|
||||||
|
|
||||||
|
thresholdRepresentation.setText(buildThresholdRepresentationString());
|
||||||
|
setmOfnText();
|
||||||
|
return view;
|
||||||
|
// return super.onCreateView(inflater, container, savedInstanceState);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setmOfnText() {
|
||||||
|
mOfn.setText(String.format("%d of %d contacts needed to recover your account", threshold, numberOfCustodians));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAttach(Context context) {
|
||||||
|
super.onAttach(context);
|
||||||
|
listener = (ThresholdDefinedListener) context;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getUniqueTag() {
|
||||||
|
return TAG;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void injectFragment(ActivityComponent component) {
|
||||||
|
component.inject(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
||||||
|
inflater.inflate(R.menu.define_threshold_actions, menu);
|
||||||
|
super.onCreateOptionsMenu(menu, inflater);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onOptionsItemSelected(MenuItem item) {
|
||||||
|
switch (item.getItemId()) {
|
||||||
|
case R.id.action_threshold_defined:
|
||||||
|
try {
|
||||||
|
listener.thresholdDefined(threshold);
|
||||||
|
} catch (DbException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return super.onOptionsItemSelected(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String buildThresholdRepresentationString () {
|
||||||
|
String thresholdRepresentationText = "";
|
||||||
|
for (int i = 0; i < threshold; i++) {
|
||||||
|
thresholdRepresentationText += getString(R.string.filled_bullet);
|
||||||
|
}
|
||||||
|
for (int i = 0; i < (numberOfCustodians - threshold); i++) {
|
||||||
|
thresholdRepresentationText += getString(R.string.linear_bullet);
|
||||||
|
}
|
||||||
|
return thresholdRepresentationText;
|
||||||
|
}
|
||||||
|
|
||||||
|
private class SeekBarListener implements SeekBar.OnSeekBarChangeListener {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onProgressChanged(SeekBar seekBar, int progress,
|
||||||
|
boolean fromUser) {
|
||||||
|
threshold = progress + 2;
|
||||||
|
|
||||||
|
thresholdRepresentation.setText(
|
||||||
|
buildThresholdRepresentationString()
|
||||||
|
);
|
||||||
|
setmOfnText();
|
||||||
|
|
||||||
|
int sanityLevel = SecretSharingWrapper.thresholdSanity(threshold, numberOfCustodians);
|
||||||
|
int text = R.string.threshold_secure;
|
||||||
|
if (threshold == recommendedThreshold) text = R.string.threshold_recommended;
|
||||||
|
if (sanityLevel < -1) text = R.string.threshold_low_insecure;
|
||||||
|
if (sanityLevel > 0) text = R.string.threshold_high_insecure;
|
||||||
|
message.setText(text);
|
||||||
|
// TODO change colour of thresholdRepresentation to green/red based on sanityLevel
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStartTrackingTouch(SeekBar seekBar) {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStopTrackingTouch(SeekBar seekBar) {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
package org.briarproject.briar.android.socialbackup.creation;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.contact.ContactId;
|
||||||
|
import org.briarproject.bramble.api.db.DbException;
|
||||||
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
import org.briarproject.bramble.api.sync.GroupId;
|
||||||
|
import org.briarproject.briar.android.contactselection.ContactSelectorController;
|
||||||
|
import org.briarproject.briar.android.contactselection.SelectableContactItem;
|
||||||
|
import org.briarproject.briar.android.controller.handler.ResultExceptionHandler;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
|
@NotNullByDefault
|
||||||
|
public interface CreateBackupController
|
||||||
|
extends ContactSelectorController<SelectableContactItem> {
|
||||||
|
|
||||||
|
void createGroup(String name,
|
||||||
|
ResultExceptionHandler<GroupId, DbException> result);
|
||||||
|
|
||||||
|
void sendInvitation(GroupId g, Collection<ContactId> contacts,
|
||||||
|
@Nullable String text,
|
||||||
|
ResultExceptionHandler<Void, DbException> result);
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,206 @@
|
|||||||
|
package org.briarproject.briar.android.socialbackup.creation;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.contact.Contact;
|
||||||
|
import org.briarproject.bramble.api.contact.ContactId;
|
||||||
|
import org.briarproject.bramble.api.contact.ContactManager;
|
||||||
|
import org.briarproject.bramble.api.crypto.CryptoExecutor;
|
||||||
|
import org.briarproject.bramble.api.db.DatabaseExecutor;
|
||||||
|
import org.briarproject.bramble.api.db.DbException;
|
||||||
|
import org.briarproject.bramble.api.db.NoSuchContactException;
|
||||||
|
import org.briarproject.bramble.api.identity.IdentityManager;
|
||||||
|
import org.briarproject.bramble.api.identity.LocalAuthor;
|
||||||
|
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
|
||||||
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
import org.briarproject.bramble.api.sync.GroupId;
|
||||||
|
import org.briarproject.bramble.api.system.Clock;
|
||||||
|
import org.briarproject.briar.android.contactselection.ContactSelectorControllerImpl;
|
||||||
|
import org.briarproject.briar.android.controller.handler.ResultExceptionHandler;
|
||||||
|
import org.briarproject.briar.api.identity.AuthorManager;
|
||||||
|
import org.briarproject.briar.api.privategroup.GroupMessage;
|
||||||
|
import org.briarproject.briar.api.privategroup.GroupMessageFactory;
|
||||||
|
import org.briarproject.briar.api.privategroup.PrivateGroup;
|
||||||
|
import org.briarproject.briar.api.privategroup.PrivateGroupFactory;
|
||||||
|
import org.briarproject.briar.api.privategroup.PrivateGroupManager;
|
||||||
|
import org.briarproject.briar.api.privategroup.invitation.GroupInvitationFactory;
|
||||||
|
import org.briarproject.briar.api.privategroup.invitation.GroupInvitationManager;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.Executor;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
import javax.annotation.concurrent.Immutable;
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
|
import static java.util.logging.Level.WARNING;
|
||||||
|
import static org.briarproject.bramble.util.LogUtils.logException;
|
||||||
|
|
||||||
|
@Immutable
|
||||||
|
@NotNullByDefault
|
||||||
|
/**
|
||||||
|
* Derived from {@link org.briarproject.briar.android.privategroup.invitation.GroupInvitationControllerImpl}
|
||||||
|
*/
|
||||||
|
class CreateBackupControllerImpl extends ContactSelectorControllerImpl
|
||||||
|
implements CreateBackupController {
|
||||||
|
|
||||||
|
private static final Logger LOG =
|
||||||
|
Logger.getLogger(
|
||||||
|
CreateBackupControllerImpl.class.getName());
|
||||||
|
|
||||||
|
private final Executor cryptoExecutor;
|
||||||
|
private final ContactManager contactManager;
|
||||||
|
private final IdentityManager identityManager;
|
||||||
|
private final PrivateGroupFactory groupFactory;
|
||||||
|
private final GroupMessageFactory groupMessageFactory;
|
||||||
|
private final PrivateGroupManager groupManager;
|
||||||
|
private final GroupInvitationFactory groupInvitationFactory;
|
||||||
|
private final GroupInvitationManager groupInvitationManager;
|
||||||
|
private final Clock clock;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
CreateBackupControllerImpl(@DatabaseExecutor Executor dbExecutor,
|
||||||
|
@CryptoExecutor Executor cryptoExecutor,
|
||||||
|
LifecycleManager lifecycleManager, ContactManager contactManager,
|
||||||
|
IdentityManager identityManager, PrivateGroupFactory groupFactory,
|
||||||
|
GroupMessageFactory groupMessageFactory,
|
||||||
|
PrivateGroupManager groupManager,
|
||||||
|
GroupInvitationFactory groupInvitationFactory,
|
||||||
|
GroupInvitationManager groupInvitationManager, Clock clock, AuthorManager authorManager) {
|
||||||
|
super(dbExecutor, lifecycleManager, contactManager, authorManager);
|
||||||
|
this.cryptoExecutor = cryptoExecutor;
|
||||||
|
this.contactManager = contactManager;
|
||||||
|
this.identityManager = identityManager;
|
||||||
|
this.groupFactory = groupFactory;
|
||||||
|
this.groupMessageFactory = groupMessageFactory;
|
||||||
|
this.groupManager = groupManager;
|
||||||
|
this.groupInvitationFactory = groupInvitationFactory;
|
||||||
|
this.groupInvitationManager = groupInvitationManager;
|
||||||
|
this.clock = clock;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void createGroup(String name,
|
||||||
|
ResultExceptionHandler<GroupId, DbException> handler) {
|
||||||
|
runOnDbThread(() -> {
|
||||||
|
try {
|
||||||
|
LocalAuthor author = identityManager.getLocalAuthor();
|
||||||
|
createGroupAndMessages(author, name, handler);
|
||||||
|
} catch (DbException e) {
|
||||||
|
logException(LOG, WARNING, e);
|
||||||
|
handler.onException(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createGroupAndMessages(LocalAuthor author, String name,
|
||||||
|
ResultExceptionHandler<GroupId, DbException> handler) {
|
||||||
|
cryptoExecutor.execute(() -> {
|
||||||
|
LOG.info("Creating group...");
|
||||||
|
PrivateGroup group =
|
||||||
|
groupFactory.createPrivateGroup(name, author);
|
||||||
|
LOG.info("Creating new join announcement...");
|
||||||
|
GroupMessage joinMsg =
|
||||||
|
groupMessageFactory.createJoinMessage(group.getId(),
|
||||||
|
clock.currentTimeMillis(), author);
|
||||||
|
storeGroup(group, joinMsg, handler);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void storeGroup(PrivateGroup group, GroupMessage joinMsg,
|
||||||
|
ResultExceptionHandler<GroupId, DbException> handler) {
|
||||||
|
runOnDbThread(() -> {
|
||||||
|
LOG.info("Adding group to database...");
|
||||||
|
try {
|
||||||
|
groupManager.addPrivateGroup(group, joinMsg, true);
|
||||||
|
handler.onResult(group.getId());
|
||||||
|
} catch (DbException e) {
|
||||||
|
logException(LOG, WARNING, e);
|
||||||
|
handler.onException(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean isDisabled(GroupId g, Contact c) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void sendInvitation(GroupId g, Collection<ContactId> contactIds,
|
||||||
|
@Nullable String text,
|
||||||
|
ResultExceptionHandler<Void, DbException> handler) {
|
||||||
|
runOnDbThread(() -> {
|
||||||
|
try {
|
||||||
|
LocalAuthor localAuthor = identityManager.getLocalAuthor();
|
||||||
|
List<Contact> contacts = new ArrayList<>();
|
||||||
|
for (ContactId c : contactIds) {
|
||||||
|
try {
|
||||||
|
contacts.add(contactManager.getContact(c));
|
||||||
|
} catch (NoSuchContactException e) {
|
||||||
|
// Continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
signInvitations(g, localAuthor, contacts, text, handler);
|
||||||
|
} catch (DbException e) {
|
||||||
|
logException(LOG, WARNING, e);
|
||||||
|
handler.onException(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void signInvitations(GroupId g, LocalAuthor localAuthor,
|
||||||
|
Collection<Contact> contacts, @Nullable String text,
|
||||||
|
ResultExceptionHandler<Void, DbException> handler) {
|
||||||
|
cryptoExecutor.execute(() -> {
|
||||||
|
long timestamp = clock.currentTimeMillis();
|
||||||
|
List<InvitationContext> contexts = new ArrayList<>();
|
||||||
|
for (Contact c : contacts) {
|
||||||
|
byte[] signature = groupInvitationFactory.signInvitation(c, g,
|
||||||
|
timestamp, localAuthor.getPrivateKey());
|
||||||
|
contexts.add(new InvitationContext(c.getId(), timestamp,
|
||||||
|
signature));
|
||||||
|
}
|
||||||
|
sendInvitations(g, contexts, text, handler);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sendInvitations(GroupId g,
|
||||||
|
Collection<InvitationContext> contexts, @Nullable String text,
|
||||||
|
ResultExceptionHandler<Void, DbException> handler) {
|
||||||
|
runOnDbThread(() -> {
|
||||||
|
try {
|
||||||
|
for (InvitationContext context : contexts) {
|
||||||
|
try {
|
||||||
|
groupInvitationManager.sendInvitation(g,
|
||||||
|
context.contactId, text, context.timestamp,
|
||||||
|
context.signature);
|
||||||
|
} catch (NoSuchContactException e) {
|
||||||
|
// Continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//noinspection ConstantConditions
|
||||||
|
handler.onResult(null);
|
||||||
|
} catch (DbException e) {
|
||||||
|
logException(LOG, WARNING, e);
|
||||||
|
handler.onException(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class InvitationContext {
|
||||||
|
|
||||||
|
private final ContactId contactId;
|
||||||
|
private final long timestamp;
|
||||||
|
private final byte[] signature;
|
||||||
|
|
||||||
|
private InvitationContext(ContactId contactId, long timestamp,
|
||||||
|
byte[] signature) {
|
||||||
|
this.contactId = contactId;
|
||||||
|
this.timestamp = timestamp;
|
||||||
|
this.signature = signature;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package org.briarproject.briar.android.socialbackup.creation;
|
||||||
|
|
||||||
|
import org.briarproject.briar.android.activity.ActivityScope;
|
||||||
|
|
||||||
|
import dagger.Module;
|
||||||
|
import dagger.Provides;
|
||||||
|
|
||||||
|
@Module
|
||||||
|
public class CreateBackupModule {
|
||||||
|
|
||||||
|
@ActivityScope
|
||||||
|
@Provides
|
||||||
|
CreateBackupController provideCreateGroupController(
|
||||||
|
CreateBackupControllerImpl createBackupController) {
|
||||||
|
return createBackupController;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
package org.briarproject.briar.android.splash;
|
package org.briarproject.briar.android.splash;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
@@ -7,6 +8,7 @@ import android.view.View;
|
|||||||
import android.view.View.OnClickListener;
|
import android.view.View.OnClickListener;
|
||||||
|
|
||||||
import org.briarproject.briar.R;
|
import org.briarproject.briar.R;
|
||||||
|
import org.briarproject.briar.android.Localizer;
|
||||||
|
|
||||||
import androidx.appcompat.app.AppCompatActivity;
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
|
|
||||||
@@ -27,6 +29,13 @@ public class ExpiredActivity extends AppCompatActivity
|
|||||||
findViewById(R.id.download_briar_button).setOnClickListener(this);
|
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
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
Uri uri = Uri.parse("https://briarproject.org/download.html");
|
Uri uri = Uri.parse("https://briarproject.org/download.html");
|
||||||
|
|||||||
@@ -197,6 +197,10 @@ public abstract class ThreadListViewModel<I extends ThreadItem>
|
|||||||
*/
|
*/
|
||||||
@UiThread
|
@UiThread
|
||||||
protected void addItem(I item, boolean scrollToItem) {
|
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);
|
messageTree.add(item);
|
||||||
if (scrollToItem) this.scrollToItem.set(item.getId());
|
if (scrollToItem) this.scrollToItem.set(item.getId());
|
||||||
items.setValue(new LiveResult<>(messageTree.depthFirstOrder()));
|
items.setValue(new LiveResult<>(messageTree.depthFirstOrder()));
|
||||||
|
|||||||
@@ -97,6 +97,7 @@ import static java.util.concurrent.TimeUnit.DAYS;
|
|||||||
import static org.briarproject.bramble.util.AndroidUtils.getSupportedImageContentTypes;
|
import static org.briarproject.bramble.util.AndroidUtils.getSupportedImageContentTypes;
|
||||||
import static org.briarproject.briar.BuildConfig.APPLICATION_ID;
|
import static org.briarproject.briar.BuildConfig.APPLICATION_ID;
|
||||||
import static org.briarproject.briar.android.TestingConstants.EXPIRY_DATE;
|
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_APP_START_TIME;
|
||||||
import static org.briarproject.briar.android.reporting.CrashReportActivity.EXTRA_THROWABLE;
|
import static org.briarproject.briar.android.reporting.CrashReportActivity.EXTRA_THROWABLE;
|
||||||
|
|
||||||
@@ -357,16 +358,17 @@ public class UiUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static void triggerFeedback(Context ctx) {
|
public static void triggerFeedback(Context ctx) {
|
||||||
startDevReportActivity(ctx, FeedbackActivity.class, null, null);
|
startDevReportActivity(ctx, FeedbackActivity.class, null, null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void startDevReportActivity(Context ctx,
|
public static void startDevReportActivity(Context ctx,
|
||||||
Class<? extends FragmentActivity> activity, @Nullable Throwable t,
|
Class<? extends FragmentActivity> activity, @Nullable Throwable t,
|
||||||
@Nullable Long appStartTime) {
|
@Nullable Long appStartTime, @Nullable byte[] logKey) {
|
||||||
final Intent dialogIntent = new Intent(ctx, activity);
|
final Intent dialogIntent = new Intent(ctx, activity);
|
||||||
dialogIntent.setFlags(FLAG_ACTIVITY_NEW_TASK);
|
dialogIntent.setFlags(FLAG_ACTIVITY_NEW_TASK);
|
||||||
dialogIntent.putExtra(EXTRA_THROWABLE, t);
|
dialogIntent.putExtra(EXTRA_THROWABLE, t);
|
||||||
dialogIntent.putExtra(EXTRA_APP_START_TIME, appStartTime);
|
dialogIntent.putExtra(EXTRA_APP_START_TIME, appStartTime);
|
||||||
|
dialogIntent.putExtra(EXTRA_APP_LOGCAT, logKey);
|
||||||
ctx.startActivity(dialogIntent);
|
ctx.startActivity(dialogIntent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -39,7 +39,8 @@ public interface AndroidNotificationManager {
|
|||||||
String BLOG_CHANNEL_ID = "blogs";
|
String BLOG_CHANNEL_ID = "blogs";
|
||||||
// Channels are sorted by channel ID in the Settings app, so use IDs
|
// Channels are sorted by channel ID in the Settings app, so use IDs
|
||||||
// that will sort below the main channels such as contacts
|
// that will sort below the main channels such as contacts
|
||||||
String ONGOING_CHANNEL_ID = "zForegroundService";
|
String ONGOING_CHANNEL_OLD_ID = "zForegroundService";
|
||||||
|
String ONGOING_CHANNEL_ID = "zForegroundService2";
|
||||||
String FAILURE_CHANNEL_ID = "zStartupFailure";
|
String FAILURE_CHANNEL_ID = "zStartupFailure";
|
||||||
String REMINDER_CHANNEL_ID = "zSignInReminder";
|
String REMINDER_CHANNEL_ID = "zSignInReminder";
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import androidx.lifecycle.LiveData;
|
|||||||
public interface LockManager {
|
public interface LockManager {
|
||||||
|
|
||||||
String ACTION_LOCK = "lock";
|
String ACTION_LOCK = "lock";
|
||||||
|
String EXTRA_PID = "PID";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stops the inactivity timer when the user interacts with the app.
|
* Stops the inactivity timer when the user interacts with the app.
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
<vector android:height="24dp" android:tint="#29C400"
|
||||||
|
android:viewportHeight="24" android:viewportWidth="24"
|
||||||
|
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<path android:fillColor="@android:color/white" android:pathData="M19.77,5.03l1.4,1.4L8.43,19.17l-5.6,-5.6 1.4,-1.4 4.2,4.2L19.77,5.03m0,-2.83L8.43,13.54l-4.2,-4.2L0,13.57 8.43,22 24,6.43 19.77,2.2z"/>
|
||||||
|
</vector>
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:id="@+id/fragmentContainer"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
<?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"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:fitsSystemWindows="true"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.Toolbar
|
||||||
|
android:id="@+id/toolbar"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="?attr/colorPrimary"
|
||||||
|
android:minHeight="?attr/actionBarSize"
|
||||||
|
app:title="@string/title_help_recover"
|
||||||
|
app:titleTextColor="@android:color/white" />
|
||||||
|
|
||||||
|
<include layout="@layout/fragment_recovery_custodian_explainer" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
<?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"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:fitsSystemWindows="true"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.Toolbar
|
||||||
|
android:id="@+id/toolbar"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="?attr/colorPrimary"
|
||||||
|
android:minHeight="?attr/actionBarSize"
|
||||||
|
app:title="@string/title_help_recover"
|
||||||
|
app:titleTextColor="@android:color/white" />
|
||||||
|
|
||||||
|
<include layout="@layout/fragment_recovery_custodian_error_explainer" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
<?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"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:fitsSystemWindows="true"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.Toolbar
|
||||||
|
android:id="@+id/toolbar"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="?attr/colorPrimary"
|
||||||
|
android:minHeight="?attr/actionBarSize"
|
||||||
|
app:title="@string/title_recovery_mode"
|
||||||
|
app:titleTextColor="@android:color/white" />
|
||||||
|
|
||||||
|
<include layout="@layout/fragment_recovery_owner_explainer" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
<?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"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:fitsSystemWindows="true"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.Toolbar
|
||||||
|
android:id="@+id/toolbar"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="?attr/colorPrimary"
|
||||||
|
android:minHeight="?attr/actionBarSize"
|
||||||
|
app:title="@string/title_recovery_mode"
|
||||||
|
app:titleTextColor="@android:color/white" />
|
||||||
|
|
||||||
|
<include layout="@layout/fragment_recovery_owner_error_explainer" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
<?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"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:fitsSystemWindows="true"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.Toolbar
|
||||||
|
android:id="@+id/toolbar"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="?attr/colorPrimary"
|
||||||
|
android:minHeight="?attr/actionBarSize"
|
||||||
|
app:title="@string/title_recovery_mode"
|
||||||
|
app:titleTextColor="@android:color/white" />
|
||||||
|
|
||||||
|
<include layout="@layout/fragment_recovery_owner_main" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
<?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"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:fitsSystemWindows="true"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.Toolbar
|
||||||
|
android:id="@+id/toolbar"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="?attr/colorPrimary"
|
||||||
|
android:minHeight="?attr/actionBarSize"
|
||||||
|
app:title="@string/title_recovery_mode"
|
||||||
|
app:titleTextColor="@android:color/white" />
|
||||||
|
|
||||||
|
<include layout="@layout/fragment_recovery_owner_recovering" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
<?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"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:fitsSystemWindows="true"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.Toolbar
|
||||||
|
android:id="@+id/toolbar"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="?attr/colorPrimary"
|
||||||
|
android:minHeight="?attr/actionBarSize"
|
||||||
|
app:title="@string/setup_title"
|
||||||
|
app:titleTextColor="@android:color/white" />
|
||||||
|
|
||||||
|
<include layout="@layout/fragment_start" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
19
briar-android/src/main/res/layout/activity_welcome.xml
Normal file
19
briar-android/src/main/res/layout/activity_welcome.xml
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
<?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"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:fitsSystemWindows="true"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.Toolbar
|
||||||
|
android:id="@+id/toolbar"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="?attr/colorPrimary"
|
||||||
|
android:minHeight="?attr/actionBarSize"
|
||||||
|
app:title="@string/setup_title"
|
||||||
|
app:titleTextColor="@android:color/white" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout 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:id="@+id/linearLayout"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:paddingLeft="@dimen/margin_large"
|
||||||
|
android:paddingTop="@dimen/margin_medium"
|
||||||
|
android:paddingRight="@dimen/margin_large"
|
||||||
|
android:paddingBottom="@dimen/margin_medium">
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/button"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="32dp"
|
||||||
|
android:text="@string/backup_done_dismiss"
|
||||||
|
android:textSize="24sp"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/textView2" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/textView2"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:gravity="center"
|
||||||
|
android:text="@string/recovery_account_recovered"
|
||||||
|
android:textSize="30sp"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/imageView2" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/imageView2"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:layout_marginStart="32dp"
|
||||||
|
android:layout_marginLeft="32dp"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:layout_marginEnd="32dp"
|
||||||
|
android:layout_marginRight="32dp"
|
||||||
|
app:layout_constraintDimensionRatio="1:1"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:srcCompat="@drawable/ic_baseline_done_outline_24" />
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
|
||||||
|
android:paddingLeft="@dimen/margin_large"
|
||||||
|
android:paddingTop="@dimen/margin_large"
|
||||||
|
android:paddingRight="@dimen/margin_large"
|
||||||
|
android:paddingBottom="@dimen/margin_medium"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/textViewThreshold"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="center"
|
||||||
|
android:text="placeholder threshold"
|
||||||
|
android:textSize="16sp"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/textViewCustodians"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="center"
|
||||||
|
android:text="@string/backup_done_info"
|
||||||
|
android:textSize="16sp"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/textViewThreshold" />
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
@@ -112,7 +112,6 @@
|
|||||||
android:textColor="@color/briar_primary"
|
android:textColor="@color/briar_primary"
|
||||||
android:textIsSelectable="true"
|
android:textIsSelectable="true"
|
||||||
android:textSize="18sp"
|
android:textSize="18sp"
|
||||||
android:typeface="monospace"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toBottomOf="@+id/yourLinkIcon"
|
app:layout_constraintTop_toBottomOf="@+id/yourLinkIcon"
|
||||||
|
|||||||
130
briar-android/src/main/res/layout/fragment_mockups.xml
Normal file
130
briar-android/src/main/res/layout/fragment_mockups.xml
Normal file
@@ -0,0 +1,130 @@
|
|||||||
|
<?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"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/linearLayout"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:paddingLeft="@dimen/margin_large"
|
||||||
|
android:paddingTop="@dimen/margin_medium"
|
||||||
|
android:paddingRight="@dimen/margin_large"
|
||||||
|
android:paddingBottom="@dimen/margin_medium">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Backup creation" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/buttonSelectThreshold"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:drawableLeft="@drawable/ic_security"
|
||||||
|
android:text="Select Threshold" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/buttonShardsSent"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:drawableLeft="@drawable/ic_security"
|
||||||
|
android:text="Shards sent" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Setup" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/buttonWelcome"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:drawableLeft="@drawable/ic_security"
|
||||||
|
android:text="Welcome" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Recovery from secrect owner's point of view" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/buttonOwnerRecoveryExplainer"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:drawableLeft="@drawable/ic_security"
|
||||||
|
android:text="Explainer" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/buttonOwnerRecoveryReceivedShard"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:drawableLeft="@drawable/ic_security"
|
||||||
|
android:text="Shard received" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/buttonOwnerRecoveryMain1"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:drawableLeft="@drawable/ic_security"
|
||||||
|
android:text="Main (0 shards)" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/buttonOwnerRecoveryMain2"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:drawableLeft="@drawable/ic_security"
|
||||||
|
android:text="Main (1 shard)" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/buttonOwnerRecoveryMain3"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:drawableLeft="@drawable/ic_security"
|
||||||
|
android:text="Recovering account…" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/buttonOwnerRecoveryAccountRecovered"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:drawableLeft="@drawable/ic_security"
|
||||||
|
android:text="Account recovered" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/buttonOwnerRecoveryErrorExplainer"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:drawableLeft="@drawable/ic_security"
|
||||||
|
android:text="Error" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Recovery from custodian's point of view" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/buttonCustodianRecoveryExplainer"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:drawableLeft="@drawable/ic_security"
|
||||||
|
android:text="Explainer" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/buttonCustodianRecoveryErrorExplainer"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:drawableLeft="@drawable/ic_security"
|
||||||
|
android:text="Error" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/buttonCustodianRecoveryDone"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:drawableLeft="@drawable/ic_security"
|
||||||
|
android:text="Done" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
</ScrollView>
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout 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:id="@+id/linearLayout"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:paddingLeft="@dimen/margin_large"
|
||||||
|
android:paddingTop="@dimen/margin_medium"
|
||||||
|
android:paddingRight="@dimen/margin_large"
|
||||||
|
android:paddingBottom="@dimen/margin_medium"
|
||||||
|
tools:showIn="@layout/activity_preview_welcome">
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/buttonSetupNewAccount"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:drawableLeft="@drawable/ic_contacts"
|
||||||
|
android:text="@string/setup_new_account"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/buttonRestoreAccount"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:drawableLeft="@drawable/ic_repeat"
|
||||||
|
android:text="@string/setup_restore_account"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/buttonSetupNewAccount" />
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
@@ -139,10 +139,10 @@
|
|||||||
<Space
|
<Space
|
||||||
android:id="@+id/space"
|
android:id="@+id/space"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="0dp"
|
android:layout_height="wrap_content"
|
||||||
|
app:layout_constrainedHeight="true"
|
||||||
app:layout_constraintBottom_toTopOf="@+id/addButton"
|
app:layout_constraintBottom_toTopOf="@+id/addButton"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintHeight_default="wrap"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toBottomOf="@+id/contactNameLayout" />
|
app:layout_constraintTop_toBottomOf="@+id/contactNameLayout" />
|
||||||
|
|
||||||
@@ -173,4 +173,4 @@
|
|||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
|
|||||||
@@ -0,0 +1,39 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout 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:id="@+id/linearLayout"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:paddingLeft="@dimen/margin_large"
|
||||||
|
android:paddingTop="@dimen/margin_medium"
|
||||||
|
android:paddingRight="@dimen/margin_large"
|
||||||
|
android:paddingBottom="@dimen/margin_medium">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/textView2"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:gravity="center"
|
||||||
|
android:text="@string/custodian_shard_sent"
|
||||||
|
android:textSize="30sp"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/imageView2" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/imageView2"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:layout_marginStart="32dp"
|
||||||
|
android:layout_marginLeft="32dp"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:layout_marginEnd="32dp"
|
||||||
|
android:layout_marginRight="32dp"
|
||||||
|
app:layout_constraintDimensionRatio="1:1"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:srcCompat="@drawable/ic_baseline_done_outline_24" />
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout 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:id="@+id/linearLayout"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:paddingLeft="@dimen/margin_large"
|
||||||
|
android:paddingTop="@dimen/margin_medium"
|
||||||
|
android:paddingRight="@dimen/margin_large"
|
||||||
|
android:paddingBottom="@dimen/margin_medium"
|
||||||
|
tools:ignore="VectorDrawableCompat"
|
||||||
|
tools:showIn="@layout/activity_preview_recovery_custodian2">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/textView1"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="24dp"
|
||||||
|
android:gravity="center"
|
||||||
|
android:text="@string/custodian_recovery_failed_to_send"
|
||||||
|
android:textSize="24sp"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
tools:layout_editor_absoluteX="16dp" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/imageView"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="24dp"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/textView1"
|
||||||
|
app:srcCompat="@drawable/qr_code_error" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/textView2"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="24dp"
|
||||||
|
android:gravity="center"
|
||||||
|
android:text="@string/recovery_helpful_suggestions"
|
||||||
|
android:textSize="24sp"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintHorizontal_bias="0.0"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/imageView" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/button"
|
||||||
|
android:layout_marginTop="24dp"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/recovery_retry"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintHorizontal_bias="0.0"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/textView2" />
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user