mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-12 10:49:06 +01:00
Compare commits
84 Commits
beta-1.4.1
...
feature-fl
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
934997ed91 | ||
|
|
0b94814620 | ||
|
|
e82e11acfa | ||
|
|
795461d9a8 | ||
|
|
7b8d01cfe0 | ||
|
|
abd04ee7f5 | ||
|
|
cc5365eaf0 | ||
|
|
6b20b03698 | ||
|
|
9da7fbf4f6 | ||
|
|
f64f442fcf | ||
|
|
6eda2f6d13 | ||
|
|
6faa095dfb | ||
|
|
4007fca668 | ||
|
|
28a747f7f3 | ||
|
|
fd2d5c9173 | ||
|
|
8f7bb9d26b | ||
|
|
dc220200b6 | ||
|
|
0cea137d75 | ||
|
|
2eef34f424 | ||
|
|
a68fff9dd2 | ||
|
|
ddc8f4a7d7 | ||
|
|
f961b6a80b | ||
|
|
93439d9c17 | ||
|
|
f3ee884816 | ||
|
|
8ca22043cf | ||
|
|
9353b78da8 | ||
|
|
429bbe1275 | ||
|
|
c5fb1416bd | ||
|
|
e52cbd896e | ||
|
|
ab1b8784b7 | ||
|
|
55a4daa92f | ||
|
|
e52250f1e4 | ||
|
|
33d01aac8c | ||
|
|
b920382e44 | ||
|
|
1a2f85f701 | ||
|
|
186bcc0b47 | ||
|
|
8b9140f477 | ||
|
|
f959c32935 | ||
|
|
1c060bc6db | ||
|
|
5e44e4d308 | ||
|
|
75d5dec45f | ||
|
|
d825227eb5 | ||
|
|
967dd1f18d | ||
|
|
4a4147b563 | ||
|
|
08b72af647 | ||
|
|
528e090c6f | ||
|
|
652f9e5705 | ||
|
|
6a91ec7a6b | ||
|
|
c3a9eff96b | ||
|
|
bd05d893eb | ||
|
|
6965bc0acd | ||
|
|
c6e9554026 | ||
|
|
ab8734e373 | ||
|
|
267956b36c | ||
|
|
ec84ddb38b | ||
|
|
ba2db48d8e | ||
|
|
186f61f771 | ||
|
|
47971517cd | ||
|
|
8db182d7e5 | ||
|
|
d44a609d0c | ||
|
|
0a1892d39f | ||
|
|
9b092da37a | ||
|
|
7a3ffcbae6 | ||
|
|
852e2c29e3 | ||
|
|
1b087d59d4 | ||
|
|
30ce8651b5 | ||
|
|
80a8ee4de9 | ||
|
|
354f3bc1cf | ||
|
|
1e6b018ff4 | ||
|
|
eba489bb98 | ||
|
|
2bfdcaaa42 | ||
|
|
c2e71ef52f | ||
|
|
9ee8fe74ba | ||
|
|
95d8783852 | ||
|
|
b4f3604584 | ||
|
|
badccac90c | ||
|
|
1b8d1a5a8d | ||
|
|
2fe57d2597 | ||
|
|
904d5b2ce2 | ||
|
|
1911b3dd97 | ||
|
|
bd430a1009 | ||
|
|
c16d0e8f45 | ||
|
|
847273c558 | ||
|
|
b9bac8b6a5 |
@@ -13,8 +13,8 @@ android {
|
|||||||
defaultConfig {
|
defaultConfig {
|
||||||
minSdkVersion 16
|
minSdkVersion 16
|
||||||
targetSdkVersion 31
|
targetSdkVersion 31
|
||||||
versionCode 10417
|
versionCode 10420
|
||||||
versionName "1.4.17"
|
versionName "1.4.20"
|
||||||
consumerProguardFiles 'proguard-rules.txt'
|
consumerProguardFiles 'proguard-rules.txt'
|
||||||
|
|
||||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
@@ -40,8 +40,16 @@ configurations {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation project(path: ':bramble-core', configuration: 'default')
|
// In theory this dependency shouldn't be needed, but without it Android Studio's linter will
|
||||||
|
// complain about unresolved symbols for bramble-api test classes in bramble-android tests,
|
||||||
|
// even though the bramble-api test classes are provided by the testImplementation dependency
|
||||||
|
// below and the compiler can find them
|
||||||
|
implementation project(':bramble-api')
|
||||||
|
|
||||||
|
implementation project(':bramble-core')
|
||||||
|
|
||||||
implementation 'androidx.annotation:annotation:1.5.0'
|
implementation 'androidx.annotation:annotation:1.5.0'
|
||||||
|
|
||||||
tor "org.briarproject:tor-android:$tor_version"
|
tor "org.briarproject:tor-android:$tor_version"
|
||||||
tor "org.briarproject:obfs4proxy-android:$obfs4proxy_version"
|
tor "org.briarproject:obfs4proxy-android:$obfs4proxy_version"
|
||||||
tor "org.briarproject:snowflake-android:$snowflake_version"
|
tor "org.briarproject:snowflake-android:$snowflake_version"
|
||||||
@@ -51,6 +59,7 @@ dependencies {
|
|||||||
compileOnly 'javax.annotation:jsr250-api:1.0'
|
compileOnly 'javax.annotation:jsr250-api:1.0'
|
||||||
|
|
||||||
testImplementation project(path: ':bramble-api', configuration: 'testOutput')
|
testImplementation project(path: ':bramble-api', configuration: 'testOutput')
|
||||||
|
|
||||||
testImplementation "junit:junit:$junit_version"
|
testImplementation "junit:junit:$junit_version"
|
||||||
testImplementation "org.jmock:jmock:$jmock_version"
|
testImplementation "org.jmock:jmock:$jmock_version"
|
||||||
testImplementation "org.jmock:jmock-junit4:$jmock_version"
|
testImplementation "org.jmock:jmock-junit4:$jmock_version"
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
package="org.briarproject.bramble">
|
package="org.briarproject.bramble">
|
||||||
|
|
||||||
<uses-feature
|
<uses-feature
|
||||||
@@ -7,15 +8,17 @@
|
|||||||
|
|
||||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||||
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
|
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
|
||||||
|
<!-- The BLUETOOTH permission was supposed to be removed in API 31 but is still needed on some Xiaomi/Redmi/POCO devices running API 31 -->
|
||||||
<uses-permission
|
<uses-permission
|
||||||
android:name="android.permission.BLUETOOTH"
|
android:name="android.permission.BLUETOOTH"
|
||||||
android:maxSdkVersion="30" />
|
android:maxSdkVersion="31" />
|
||||||
<uses-permission
|
<uses-permission
|
||||||
android:name="android.permission.BLUETOOTH_ADMIN"
|
android:name="android.permission.BLUETOOTH_ADMIN"
|
||||||
android:maxSdkVersion="30" />
|
android:maxSdkVersion="30" />
|
||||||
<uses-permission
|
<uses-permission
|
||||||
android:name="android.permission.BLUETOOTH_SCAN"
|
android:name="android.permission.BLUETOOTH_SCAN"
|
||||||
android:usesPermissionFlags="neverForLocation" />
|
android:usesPermissionFlags="neverForLocation"
|
||||||
|
tools:targetApi="31" />
|
||||||
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
|
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
|
||||||
<uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" />
|
<uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" />
|
||||||
<uses-permission android:name="android.permission.INTERNET" />
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
|
|||||||
@@ -118,6 +118,11 @@ class AndroidNetworkManager implements NetworkManager, Service {
|
|||||||
try {
|
try {
|
||||||
NetworkInfo net = connectivityManager.getActiveNetworkInfo();
|
NetworkInfo net = connectivityManager.getActiveNetworkInfo();
|
||||||
boolean connected = net != null && net.isConnected();
|
boolean connected = net != null && net.isConnected();
|
||||||
|
// Research into Android's behavior to check network connectivity
|
||||||
|
// (https://code.briarproject.org/briar/public-mesh-research/-/issues/19)
|
||||||
|
// has shown that NetworkInfo#isConnected() returns true if the device
|
||||||
|
// is connected to any Wifi, independent of whether any specific IP
|
||||||
|
// address can be reached using it or any domain names can be resolved.
|
||||||
boolean wifi = false, ipv6Only = false;
|
boolean wifi = false, ipv6Only = false;
|
||||||
if (connected) {
|
if (connected) {
|
||||||
wifi = net.getType() == TYPE_WIFI;
|
wifi = net.getType() == TYPE_WIFI;
|
||||||
|
|||||||
@@ -26,7 +26,6 @@ import static android.os.Process.myPid;
|
|||||||
import static android.os.Process.myTid;
|
import static android.os.Process.myTid;
|
||||||
import static android.os.Process.myUid;
|
import static android.os.Process.myUid;
|
||||||
import static android.provider.Settings.Secure.ANDROID_ID;
|
import static android.provider.Settings.Secure.ANDROID_ID;
|
||||||
import static org.briarproject.bramble.util.AndroidUtils.hasBtConnectPermission;
|
|
||||||
|
|
||||||
@Immutable
|
@Immutable
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
@@ -53,8 +52,8 @@ class AndroidSecureRandomProvider extends UnixSecureRandomProvider {
|
|||||||
ContentResolver contentResolver = appContext.getContentResolver();
|
ContentResolver contentResolver = appContext.getContentResolver();
|
||||||
String id = Settings.Secure.getString(contentResolver, ANDROID_ID);
|
String id = Settings.Secure.getString(contentResolver, ANDROID_ID);
|
||||||
if (id != null) out.writeUTF(id);
|
if (id != null) out.writeUTF(id);
|
||||||
// use bluetooth paired devices as well, if allowed
|
// On API 31 and higher we need permission to access bonded devices
|
||||||
if (hasBtConnectPermission(appContext)) {
|
if (SDK_INT < 31) {
|
||||||
Parcel parcel = Parcel.obtain();
|
Parcel parcel = Parcel.obtain();
|
||||||
BluetoothAdapter bt = BluetoothAdapter.getDefaultAdapter();
|
BluetoothAdapter bt = BluetoothAdapter.getDefaultAdapter();
|
||||||
if (bt != null) {
|
if (bt != null) {
|
||||||
|
|||||||
@@ -65,6 +65,9 @@ public class AndroidUtils {
|
|||||||
|
|
||||||
public static Pair<String, String> getBluetoothAddressAndMethod(Context ctx,
|
public static Pair<String, String> getBluetoothAddressAndMethod(Context ctx,
|
||||||
BluetoothAdapter adapter) {
|
BluetoothAdapter adapter) {
|
||||||
|
// If we don't have permission to access the adapter's address, let
|
||||||
|
// the caller know we can't find it
|
||||||
|
if (!hasBtConnectPermission(ctx)) return new Pair<>("", "");
|
||||||
// Return the adapter's address if it's valid and not fake
|
// Return the adapter's address if it's valid and not fake
|
||||||
@SuppressLint("HardwareIds")
|
@SuppressLint("HardwareIds")
|
||||||
String address = adapter.getAddress();
|
String address = adapter.getAddress();
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ dependencyVerification {
|
|||||||
'org.apache-extras.beanshell:bsh:2.0b6:bsh-2.0b6.jar:a17955976070c0573235ee662f2794a78082758b61accffce8d3f8aedcd91047',
|
'org.apache-extras.beanshell:bsh:2.0b6:bsh-2.0b6.jar:a17955976070c0573235ee662f2794a78082758b61accffce8d3f8aedcd91047',
|
||||||
'org.briarproject:obfs4proxy-android:0.0.14-tor1:obfs4proxy-android-0.0.14-tor1.jar:8b08068778b133484b17956d8f7a7710739c33f671a26a68156f4d34e6f28c30',
|
'org.briarproject:obfs4proxy-android:0.0.14-tor1:obfs4proxy-android-0.0.14-tor1.jar:8b08068778b133484b17956d8f7a7710739c33f671a26a68156f4d34e6f28c30',
|
||||||
'org.briarproject:snowflake-android:2.3.1:snowflake-android-2.3.1.jar:1f83c9a070f87b7074af13627709a8b5aced5460104be7166af736b1bb73c293',
|
'org.briarproject:snowflake-android:2.3.1:snowflake-android-2.3.1.jar:1f83c9a070f87b7074af13627709a8b5aced5460104be7166af736b1bb73c293',
|
||||||
'org.briarproject:tor-android:0.4.5.14:tor-android-0.4.5.14.jar:7cf1beaa6c1db51fc8fac263aba9624ef289c3db29772509efcbc59f7057330a',
|
'org.briarproject:tor-android:0.4.7.13:tor-android-0.4.7.13.jar:7852aab7d2298b80878c7719f34ce665725b494d673ecf2e6f9e697564638cc6',
|
||||||
'org.checkerframework:checker-compat-qual:2.5.5:checker-compat-qual-2.5.5.jar:11d134b245e9cacc474514d2d66b5b8618f8039a1465cdc55bbc0b34e0008b7a',
|
'org.checkerframework:checker-compat-qual:2.5.5:checker-compat-qual-2.5.5.jar:11d134b245e9cacc474514d2d66b5b8618f8039a1465cdc55bbc0b34e0008b7a',
|
||||||
'org.checkerframework:checker-qual:3.12.0:checker-qual-3.12.0.jar:ff10785ac2a357ec5de9c293cb982a2cbb605c0309ea4cc1cb9b9bc6dbe7f3cb',
|
'org.checkerframework:checker-qual:3.12.0:checker-qual-3.12.0.jar:ff10785ac2a357ec5de9c293cb982a2cbb605c0309ea4cc1cb9b9bc6dbe7f3cb',
|
||||||
'org.hamcrest:hamcrest-core:2.1:hamcrest-core-2.1.jar:e09109e54a289d88506b9bfec987ddd199f4217c9464132668351b9a4f00bee9',
|
'org.hamcrest:hamcrest-core:2.1:hamcrest-core-2.1.jar:e09109e54a289d88506b9bfec987ddd199f4217c9464132668351b9a4f00bee9',
|
||||||
|
|||||||
@@ -8,9 +8,10 @@ apply from: 'witness.gradle'
|
|||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
api 'org.briarproject:null-safety:0.1'
|
api 'org.briarproject:null-safety:0.1'
|
||||||
|
api 'com.google.code.findbugs:jsr305:3.0.2'
|
||||||
|
api 'javax.inject:javax.inject:1'
|
||||||
|
api "com.google.dagger:dagger:$dagger_version"
|
||||||
|
|
||||||
implementation "com.google.dagger:dagger:$dagger_version"
|
|
||||||
implementation 'com.google.code.findbugs:jsr305:3.0.2'
|
|
||||||
implementation "com.fasterxml.jackson.core:jackson-annotations:$jackson_version"
|
implementation "com.fasterxml.jackson.core:jackson-annotations:$jackson_version"
|
||||||
|
|
||||||
testImplementation "junit:junit:$junit_version"
|
testImplementation "junit:junit:$junit_version"
|
||||||
@@ -25,7 +26,7 @@ configurations {
|
|||||||
testOutput.extendsFrom(testCompile)
|
testOutput.extendsFrom(testCompile)
|
||||||
}
|
}
|
||||||
task jarTest(type: Jar, dependsOn: testClasses) {
|
task jarTest(type: Jar, dependsOn: testClasses) {
|
||||||
from sourceSets.test.output
|
from sourceSets.test.output, sourceSets.main.output
|
||||||
classifier = 'test'
|
classifier = 'test'
|
||||||
}
|
}
|
||||||
artifacts {
|
artifacts {
|
||||||
|
|||||||
@@ -1,18 +1,26 @@
|
|||||||
package org.briarproject.bramble.api.keyagreement;
|
package org.briarproject.bramble.api.keyagreement;
|
||||||
|
|
||||||
public interface KeyAgreementConstants {
|
import org.briarproject.bramble.api.mailbox.MailboxConstants;
|
||||||
|
|
||||||
/**
|
public interface KeyAgreementConstants {
|
||||||
* The version of the BQP protocol used in beta releases. This version
|
|
||||||
* number is reserved.
|
|
||||||
*/
|
|
||||||
byte BETA_PROTOCOL_VERSION = 89;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The current version of the BQP protocol.
|
* The current version of the BQP protocol.
|
||||||
*/
|
*/
|
||||||
byte PROTOCOL_VERSION = 4;
|
byte PROTOCOL_VERSION = 4;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The QR code format identifier, used to distinguish BQP QR codes from
|
||||||
|
* QR codes used for other purposes. See
|
||||||
|
* {@link MailboxConstants#QR_FORMAT_ID}.
|
||||||
|
*/
|
||||||
|
byte QR_FORMAT_ID = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The QR code format version.
|
||||||
|
*/
|
||||||
|
byte QR_FORMAT_VERSION = PROTOCOL_VERSION;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The length of the BQP key commitment in bytes.
|
* The length of the BQP key commitment in bytes.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -7,5 +7,5 @@ import java.io.IOException;
|
|||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
public interface PayloadParser {
|
public interface PayloadParser {
|
||||||
|
|
||||||
Payload parse(byte[] raw) throws IOException;
|
Payload parse(String payload) throws IOException;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package org.briarproject.bramble.api.mailbox;
|
package org.briarproject.bramble.api.mailbox;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.keyagreement.KeyAgreementConstants;
|
||||||
import org.briarproject.bramble.api.plugin.TransportId;
|
import org.briarproject.bramble.api.plugin.TransportId;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -19,6 +20,18 @@ public interface MailboxConstants {
|
|||||||
*/
|
*/
|
||||||
TransportId ID = new TransportId("org.briarproject.bramble.mailbox");
|
TransportId ID = new TransportId("org.briarproject.bramble.mailbox");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The QR code format identifier, used to distinguish mailbox QR codes
|
||||||
|
* from QR codes used for other purposes. See
|
||||||
|
* {@link KeyAgreementConstants#QR_FORMAT_ID};
|
||||||
|
*/
|
||||||
|
byte QR_FORMAT_ID = 1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The QR code format version.
|
||||||
|
*/
|
||||||
|
byte QR_FORMAT_VERSION = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Mailbox API versions that we support as a client. This is reported to our
|
* Mailbox API versions that we support as a client. This is reported to our
|
||||||
* contacts by {@link MailboxUpdateManager}.
|
* contacts by {@link MailboxUpdateManager}.
|
||||||
|
|||||||
@@ -1,17 +1,44 @@
|
|||||||
package org.briarproject.bramble.api.mailbox;
|
package org.briarproject.bramble.api.mailbox;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.qrcode.QrCodeClassifier.QrCodeType;
|
||||||
|
|
||||||
public abstract class MailboxPairingState {
|
public abstract class MailboxPairingState {
|
||||||
|
|
||||||
public static class QrCodeReceived extends MailboxPairingState {
|
public abstract static class Pending extends MailboxPairingState {
|
||||||
|
|
||||||
|
public final long timeStarted;
|
||||||
|
|
||||||
|
private Pending(long timeStarted) {
|
||||||
|
this.timeStarted = timeStarted;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class Pairing extends MailboxPairingState {
|
public static class QrCodeReceived extends Pending {
|
||||||
|
|
||||||
|
public QrCodeReceived(long timeStarted) {
|
||||||
|
super(timeStarted);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Pairing extends Pending {
|
||||||
|
|
||||||
|
public Pairing(long timeStarted) {
|
||||||
|
super(timeStarted);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class Paired extends MailboxPairingState {
|
public static class Paired extends MailboxPairingState {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class InvalidQrCode extends MailboxPairingState {
|
public static class InvalidQrCode extends MailboxPairingState {
|
||||||
|
|
||||||
|
public final QrCodeType qrCodeType;
|
||||||
|
public final int formatVersion;
|
||||||
|
|
||||||
|
public InvalidQrCode(QrCodeType qrCodeType, int formatVersion) {
|
||||||
|
this.qrCodeType = qrCodeType;
|
||||||
|
this.formatVersion = formatVersion;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class MailboxAlreadyPaired extends MailboxPairingState {
|
public static class MailboxAlreadyPaired extends MailboxPairingState {
|
||||||
|
|||||||
@@ -27,8 +27,6 @@ public interface TorConstants {
|
|||||||
int PREF_TOR_NETWORK_AUTOMATIC = 0;
|
int PREF_TOR_NETWORK_AUTOMATIC = 0;
|
||||||
int PREF_TOR_NETWORK_WITHOUT_BRIDGES = 1;
|
int PREF_TOR_NETWORK_WITHOUT_BRIDGES = 1;
|
||||||
int PREF_TOR_NETWORK_WITH_BRIDGES = 2;
|
int PREF_TOR_NETWORK_WITH_BRIDGES = 2;
|
||||||
// TODO: Remove when settings migration code is removed
|
|
||||||
int PREF_TOR_NETWORK_NEVER = 3;
|
|
||||||
|
|
||||||
// Default values for local settings
|
// Default values for local settings
|
||||||
boolean DEFAULT_PREF_PLUGIN_ENABLE = true;
|
boolean DEFAULT_PREF_PLUGIN_ENABLE = true;
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
package org.briarproject.bramble.api.qrcode;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.Pair;
|
||||||
|
import org.briarproject.nullsafety.NotNullByDefault;
|
||||||
|
|
||||||
|
@NotNullByDefault
|
||||||
|
public interface QrCodeClassifier {
|
||||||
|
|
||||||
|
enum QrCodeType {
|
||||||
|
BQP,
|
||||||
|
MAILBOX,
|
||||||
|
UNKNOWN
|
||||||
|
}
|
||||||
|
|
||||||
|
Pair<QrCodeType, Integer> classifyQrCode(String payload);
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
package org.briarproject.bramble.api.qrcode;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.FormatException;
|
||||||
|
import org.briarproject.bramble.api.qrcode.QrCodeClassifier.QrCodeType;
|
||||||
|
import org.briarproject.nullsafety.NotNullByDefault;
|
||||||
|
|
||||||
|
import javax.annotation.concurrent.Immutable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Thrown when a QR code that has been scanned does not have the expected type.
|
||||||
|
*/
|
||||||
|
@Immutable
|
||||||
|
@NotNullByDefault
|
||||||
|
public class WrongQrCodeTypeException extends FormatException {
|
||||||
|
|
||||||
|
private final QrCodeType qrCodeType;
|
||||||
|
|
||||||
|
public WrongQrCodeTypeException(QrCodeType qrCodeType) {
|
||||||
|
this.qrCodeType = qrCodeType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public QrCodeType getQrCodeType() {
|
||||||
|
return qrCodeType;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,7 +3,6 @@ package org.briarproject.bramble.util;
|
|||||||
import org.briarproject.bramble.api.FormatException;
|
import org.briarproject.bramble.api.FormatException;
|
||||||
import org.briarproject.nullsafety.NotNullByDefault;
|
import org.briarproject.nullsafety.NotNullByDefault;
|
||||||
|
|
||||||
import java.io.UnsupportedEncodingException;
|
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.nio.charset.CharacterCodingException;
|
import java.nio.charset.CharacterCodingException;
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
@@ -17,13 +16,18 @@ import javax.annotation.Nullable;
|
|||||||
import static java.nio.charset.CodingErrorAction.IGNORE;
|
import static java.nio.charset.CodingErrorAction.IGNORE;
|
||||||
import static java.util.regex.Pattern.CASE_INSENSITIVE;
|
import static java.util.regex.Pattern.CASE_INSENSITIVE;
|
||||||
|
|
||||||
|
@SuppressWarnings("CharsetObjectCanBeUsed")
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
public class StringUtils {
|
public class StringUtils {
|
||||||
|
|
||||||
private static final Charset UTF_8 = Charset.forName("UTF-8");
|
public static final Charset UTF_8 = Charset.forName("UTF-8");
|
||||||
private static Pattern MAC = Pattern.compile("[0-9a-f]{2}:[0-9a-f]{2}:" +
|
public static final Charset US_ASCII = Charset.forName("US-ASCII");
|
||||||
"[0-9a-f]{2}:[0-9a-f]{2}:[0-9a-f]{2}:[0-9a-f]{2}",
|
public static final Charset ISO_8859_1 = Charset.forName("ISO-8859-1");
|
||||||
CASE_INSENSITIVE);
|
|
||||||
|
private static final Pattern MAC =
|
||||||
|
Pattern.compile("[0-9a-f]{2}:[0-9a-f]{2}:" +
|
||||||
|
"[0-9a-f]{2}:[0-9a-f]{2}:[0-9a-f]{2}:[0-9a-f]{2}",
|
||||||
|
CASE_INSENSITIVE);
|
||||||
|
|
||||||
private static final char[] HEX = new char[] {
|
private static final char[] HEX = new char[] {
|
||||||
'0', '1', '2', '3', '4', '5', '6', '7',
|
'0', '1', '2', '3', '4', '5', '6', '7',
|
||||||
@@ -45,11 +49,7 @@ public class StringUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static byte[] toUtf8(String s) {
|
public static byte[] toUtf8(String s) {
|
||||||
try {
|
return s.getBytes(UTF_8);
|
||||||
return s.getBytes("UTF-8");
|
|
||||||
} catch (UnsupportedEncodingException e) {
|
|
||||||
throw new AssertionError(e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String fromUtf8(byte[] bytes) {
|
public static String fromUtf8(byte[] bytes) {
|
||||||
|
|||||||
@@ -9,30 +9,31 @@ apply from: 'witness.gradle'
|
|||||||
apply from: '../dagger.gradle'
|
apply from: '../dagger.gradle'
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation project(path: ':bramble-api', configuration: 'default')
|
api project(':bramble-api')
|
||||||
implementation 'org.bouncycastle:bcprov-jdk15to18:1.71'
|
|
||||||
|
api 'org.briarproject:jtorctl:0.5'
|
||||||
|
|
||||||
|
implementation "org.bouncycastle:bcprov-jdk15to18:$bouncy_castle_version"
|
||||||
//noinspection GradleDependency
|
//noinspection GradleDependency
|
||||||
implementation 'com.h2database:h2:1.4.192' // The last version that supports Java 1.6
|
implementation 'com.h2database:h2:1.4.192' // The last version that supports Java 1.6
|
||||||
implementation 'org.bitlet:weupnp:0.1.4'
|
implementation 'org.bitlet:weupnp:0.1.4'
|
||||||
implementation 'net.i2p.crypto:eddsa:0.2.0'
|
implementation 'net.i2p.crypto:eddsa:0.2.0'
|
||||||
implementation 'org.whispersystems:curve25519-java:0.5.0'
|
implementation 'org.whispersystems:curve25519-java:0.5.0'
|
||||||
implementation 'org.briarproject:jtorctl:0.5'
|
|
||||||
implementation 'org.briarproject:socks-socket:0.1'
|
implementation 'org.briarproject:socks-socket:0.1'
|
||||||
|
|
||||||
//noinspection GradleDependency
|
|
||||||
implementation "com.squareup.okhttp3:okhttp:$okhttp_version"
|
implementation "com.squareup.okhttp3:okhttp:$okhttp_version"
|
||||||
implementation "com.fasterxml.jackson.core:jackson-databind:$jackson_version"
|
implementation "com.fasterxml.jackson.core:jackson-databind:$jackson_version"
|
||||||
|
|
||||||
annotationProcessor "com.google.dagger:dagger-compiler:$dagger_version"
|
annotationProcessor "com.google.dagger:dagger-compiler:$dagger_version"
|
||||||
|
|
||||||
testImplementation project(path: ':bramble-api', configuration: 'testOutput')
|
testImplementation project(path: ':bramble-api', configuration: 'testOutput')
|
||||||
|
|
||||||
testImplementation 'org.hsqldb:hsqldb:2.3.5' // The last version that supports Java 1.6
|
testImplementation 'org.hsqldb:hsqldb:2.3.5' // The last version that supports Java 1.6
|
||||||
testImplementation 'net.jodah:concurrentunit:0.4.2'
|
testImplementation 'net.jodah:concurrentunit:0.4.2'
|
||||||
testImplementation "junit:junit:$junit_version"
|
testImplementation "junit:junit:$junit_version"
|
||||||
testImplementation "org.jmock:jmock:$jmock_version"
|
testImplementation "org.jmock:jmock:$jmock_version"
|
||||||
testImplementation "org.jmock:jmock-junit4:$jmock_version"
|
testImplementation "org.jmock:jmock-junit4:$jmock_version"
|
||||||
testImplementation "org.jmock:jmock-imposters:$jmock_version"
|
testImplementation "org.jmock:jmock-imposters:$jmock_version"
|
||||||
testImplementation "com.squareup.okhttp3:mockwebserver:4.9.3"
|
testImplementation "com.squareup.okhttp3:mockwebserver:$mockwebserver_version"
|
||||||
|
|
||||||
testAnnotationProcessor "com.google.dagger:dagger-compiler:$dagger_version"
|
testAnnotationProcessor "com.google.dagger:dagger-compiler:$dagger_version"
|
||||||
|
|
||||||
@@ -52,7 +53,7 @@ configurations {
|
|||||||
testOutput.extendsFrom(testCompile)
|
testOutput.extendsFrom(testCompile)
|
||||||
}
|
}
|
||||||
task jarTest(type: Jar, dependsOn: testClasses) {
|
task jarTest(type: Jar, dependsOn: testClasses) {
|
||||||
from sourceSets.test.output
|
from sourceSets.test.output, sourceSets.main.output
|
||||||
classifier = 'test'
|
classifier = 'test'
|
||||||
}
|
}
|
||||||
artifacts {
|
artifacts {
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import org.briarproject.bramble.lifecycle.LifecycleModule;
|
|||||||
import org.briarproject.bramble.mailbox.MailboxModule;
|
import org.briarproject.bramble.mailbox.MailboxModule;
|
||||||
import org.briarproject.bramble.plugin.PluginModule;
|
import org.briarproject.bramble.plugin.PluginModule;
|
||||||
import org.briarproject.bramble.properties.PropertiesModule;
|
import org.briarproject.bramble.properties.PropertiesModule;
|
||||||
|
import org.briarproject.bramble.qrcode.QrCodeModule;
|
||||||
import org.briarproject.bramble.record.RecordModule;
|
import org.briarproject.bramble.record.RecordModule;
|
||||||
import org.briarproject.bramble.reliability.ReliabilityModule;
|
import org.briarproject.bramble.reliability.ReliabilityModule;
|
||||||
import org.briarproject.bramble.rendezvous.RendezvousModule;
|
import org.briarproject.bramble.rendezvous.RendezvousModule;
|
||||||
@@ -47,6 +48,7 @@ import dagger.Module;
|
|||||||
MailboxModule.class,
|
MailboxModule.class,
|
||||||
PluginModule.class,
|
PluginModule.class,
|
||||||
PropertiesModule.class,
|
PropertiesModule.class,
|
||||||
|
QrCodeModule.class,
|
||||||
RecordModule.class,
|
RecordModule.class,
|
||||||
ReliabilityModule.class,
|
ReliabilityModule.class,
|
||||||
RendezvousModule.class,
|
RendezvousModule.class,
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ import java.io.FileInputStream;
|
|||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
import java.nio.charset.Charset;
|
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
@@ -29,6 +28,7 @@ import javax.inject.Inject;
|
|||||||
import static java.util.logging.Level.WARNING;
|
import static java.util.logging.Level.WARNING;
|
||||||
import static org.briarproject.bramble.api.crypto.DecryptionResult.INVALID_CIPHERTEXT;
|
import static org.briarproject.bramble.api.crypto.DecryptionResult.INVALID_CIPHERTEXT;
|
||||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
import static org.briarproject.bramble.util.LogUtils.logException;
|
||||||
|
import static org.briarproject.bramble.util.StringUtils.UTF_8;
|
||||||
import static org.briarproject.bramble.util.StringUtils.fromHexString;
|
import static org.briarproject.bramble.util.StringUtils.fromHexString;
|
||||||
import static org.briarproject.bramble.util.StringUtils.toHexString;
|
import static org.briarproject.bramble.util.StringUtils.toHexString;
|
||||||
|
|
||||||
@@ -99,7 +99,7 @@ class AccountManagerImpl implements AccountManager {
|
|||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
BufferedReader reader = new BufferedReader(new InputStreamReader(
|
BufferedReader reader = new BufferedReader(new InputStreamReader(
|
||||||
new FileInputStream(f), Charset.forName("UTF-8")));
|
new FileInputStream(f), UTF_8));
|
||||||
String key = reader.readLine();
|
String key = reader.readLine();
|
||||||
reader.close();
|
reader.close();
|
||||||
return key;
|
return key;
|
||||||
@@ -151,7 +151,7 @@ class AccountManagerImpl implements AccountManager {
|
|||||||
@GuardedBy("stateChangeLock")
|
@GuardedBy("stateChangeLock")
|
||||||
private void writeDbKeyToFile(String key, File f) throws IOException {
|
private void writeDbKeyToFile(String key, File f) throws IOException {
|
||||||
FileOutputStream out = new FileOutputStream(f);
|
FileOutputStream out = new FileOutputStream(f);
|
||||||
out.write(key.getBytes(Charset.forName("UTF-8")));
|
out.write(key.getBytes(UTF_8));
|
||||||
out.flush();
|
out.flush();
|
||||||
out.close();
|
out.close();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,7 +29,6 @@ import org.briarproject.nullsafety.NotNullByDefault;
|
|||||||
import org.whispersystems.curve25519.Curve25519;
|
import org.whispersystems.curve25519.Curve25519;
|
||||||
import org.whispersystems.curve25519.Curve25519KeyPair;
|
import org.whispersystems.curve25519.Curve25519KeyPair;
|
||||||
|
|
||||||
import java.nio.charset.Charset;
|
|
||||||
import java.security.GeneralSecurityException;
|
import java.security.GeneralSecurityException;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.security.Provider;
|
import java.security.Provider;
|
||||||
@@ -51,6 +50,7 @@ import static org.briarproject.bramble.api.crypto.DecryptionResult.KEY_STRENGTHE
|
|||||||
import static org.briarproject.bramble.util.ByteUtils.INT_32_BYTES;
|
import static org.briarproject.bramble.util.ByteUtils.INT_32_BYTES;
|
||||||
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;
|
||||||
|
import static org.briarproject.bramble.util.StringUtils.US_ASCII;
|
||||||
|
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
class CryptoComponentImpl implements CryptoComponent {
|
class CryptoComponentImpl implements CryptoComponent {
|
||||||
@@ -460,7 +460,7 @@ class CryptoComponentImpl implements CryptoComponent {
|
|||||||
@Override
|
@Override
|
||||||
public String encodeOnion(byte[] publicKey) {
|
public String encodeOnion(byte[] publicKey) {
|
||||||
Digest digest = new SHA3Digest(256);
|
Digest digest = new SHA3Digest(256);
|
||||||
byte[] label = ".onion checksum".getBytes(Charset.forName("US-ASCII"));
|
byte[] label = ".onion checksum".getBytes(US_ASCII);
|
||||||
digest.update(label, 0, label.length);
|
digest.update(label, 0, label.length);
|
||||||
digest.update(publicKey, 0, publicKey.length);
|
digest.update(publicKey, 0, publicKey.length);
|
||||||
digest.update(ONION_HS_PROTOCOL_VERSION);
|
digest.update(ONION_HS_PROTOCOL_VERSION);
|
||||||
|
|||||||
@@ -39,12 +39,13 @@ import java.io.FileOutputStream;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.PrintStream;
|
import java.io.PrintStream;
|
||||||
import java.nio.charset.Charset;
|
|
||||||
import java.security.SecureRandom;
|
import java.security.SecureRandom;
|
||||||
import java.util.Scanner;
|
import java.util.Scanner;
|
||||||
|
|
||||||
import javax.annotation.concurrent.Immutable;
|
import javax.annotation.concurrent.Immutable;
|
||||||
|
|
||||||
|
import static org.briarproject.bramble.util.StringUtils.UTF_8;
|
||||||
|
|
||||||
@Immutable
|
@Immutable
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
public class MessageEncrypter {
|
public class MessageEncrypter {
|
||||||
@@ -228,7 +229,7 @@ public class MessageEncrypter {
|
|||||||
PublicKey publicKey =
|
PublicKey publicKey =
|
||||||
encrypter.getKeyParser().parsePublicKey(keyBytes);
|
encrypter.getKeyParser().parsePublicKey(keyBytes);
|
||||||
String message = readFully(System.in);
|
String message = readFully(System.in);
|
||||||
byte[] plaintext = message.getBytes(Charset.forName("UTF-8"));
|
byte[] plaintext = message.getBytes(UTF_8);
|
||||||
byte[] ciphertext = encrypter.encrypt(publicKey, plaintext);
|
byte[] ciphertext = encrypter.encrypt(publicKey, plaintext);
|
||||||
System.out.println(AsciiArmour.wrap(ciphertext, LINE_LENGTH));
|
System.out.println(AsciiArmour.wrap(ciphertext, LINE_LENGTH));
|
||||||
}
|
}
|
||||||
@@ -242,7 +243,7 @@ public class MessageEncrypter {
|
|||||||
encrypter.getKeyParser().parsePrivateKey(keyBytes);
|
encrypter.getKeyParser().parsePrivateKey(keyBytes);
|
||||||
byte[] ciphertext = AsciiArmour.unwrap(readFully(System.in));
|
byte[] ciphertext = AsciiArmour.unwrap(readFully(System.in));
|
||||||
byte[] plaintext = encrypter.decrypt(privateKey, ciphertext);
|
byte[] plaintext = encrypter.decrypt(privateKey, ciphertext);
|
||||||
System.out.println(new String(plaintext, Charset.forName("UTF-8")));
|
System.out.println(new String(plaintext, UTF_8));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String readFully(InputStream in) throws IOException {
|
private static String readFully(InputStream in) throws IOException {
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
package org.briarproject.bramble.keyagreement;
|
package org.briarproject.bramble.keyagreement;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.data.BdfReaderFactory;
|
|
||||||
import org.briarproject.bramble.api.data.BdfWriterFactory;
|
|
||||||
import org.briarproject.bramble.api.keyagreement.KeyAgreementTask;
|
import org.briarproject.bramble.api.keyagreement.KeyAgreementTask;
|
||||||
import org.briarproject.bramble.api.keyagreement.PayloadEncoder;
|
import org.briarproject.bramble.api.keyagreement.PayloadEncoder;
|
||||||
import org.briarproject.bramble.api.keyagreement.PayloadParser;
|
import org.briarproject.bramble.api.keyagreement.PayloadParser;
|
||||||
@@ -19,13 +17,13 @@ public class KeyAgreementModule {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
PayloadEncoder providePayloadEncoder(BdfWriterFactory bdfWriterFactory) {
|
PayloadEncoder providePayloadEncoder(PayloadEncoderImpl payloadEncoder) {
|
||||||
return new PayloadEncoderImpl(bdfWriterFactory);
|
return payloadEncoder;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
PayloadParser providePayloadParser(BdfReaderFactory bdfReaderFactory) {
|
PayloadParser providePayloadParser(PayloadParserImpl payloadParser) {
|
||||||
return new PayloadParserImpl(bdfReaderFactory);
|
return payloadParser;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
|
|||||||
@@ -13,7 +13,8 @@ import java.io.IOException;
|
|||||||
import javax.annotation.concurrent.Immutable;
|
import javax.annotation.concurrent.Immutable;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.PROTOCOL_VERSION;
|
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.QR_FORMAT_ID;
|
||||||
|
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.QR_FORMAT_VERSION;
|
||||||
|
|
||||||
@Immutable
|
@Immutable
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
@@ -29,7 +30,8 @@ class PayloadEncoderImpl implements PayloadEncoder {
|
|||||||
@Override
|
@Override
|
||||||
public byte[] encode(Payload p) {
|
public byte[] encode(Payload p) {
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
out.write(PROTOCOL_VERSION);
|
int formatIdAndVersion = (QR_FORMAT_ID << 5) | QR_FORMAT_VERSION;
|
||||||
|
out.write(formatIdAndVersion);
|
||||||
BdfWriter w = bdfWriterFactory.createWriter(out);
|
BdfWriter w = bdfWriterFactory.createWriter(out);
|
||||||
try {
|
try {
|
||||||
w.writeListStart(); // Payload start
|
w.writeListStart(); // Payload start
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package org.briarproject.bramble.keyagreement;
|
package org.briarproject.bramble.keyagreement;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.FormatException;
|
import org.briarproject.bramble.api.FormatException;
|
||||||
|
import org.briarproject.bramble.api.Pair;
|
||||||
import org.briarproject.bramble.api.UnsupportedVersionException;
|
import org.briarproject.bramble.api.UnsupportedVersionException;
|
||||||
import org.briarproject.bramble.api.data.BdfList;
|
import org.briarproject.bramble.api.data.BdfList;
|
||||||
import org.briarproject.bramble.api.data.BdfReader;
|
import org.briarproject.bramble.api.data.BdfReader;
|
||||||
@@ -11,6 +12,9 @@ import org.briarproject.bramble.api.keyagreement.TransportDescriptor;
|
|||||||
import org.briarproject.bramble.api.plugin.BluetoothConstants;
|
import org.briarproject.bramble.api.plugin.BluetoothConstants;
|
||||||
import org.briarproject.bramble.api.plugin.LanTcpConstants;
|
import org.briarproject.bramble.api.plugin.LanTcpConstants;
|
||||||
import org.briarproject.bramble.api.plugin.TransportId;
|
import org.briarproject.bramble.api.plugin.TransportId;
|
||||||
|
import org.briarproject.bramble.api.qrcode.QrCodeClassifier;
|
||||||
|
import org.briarproject.bramble.api.qrcode.QrCodeClassifier.QrCodeType;
|
||||||
|
import org.briarproject.bramble.api.qrcode.WrongQrCodeTypeException;
|
||||||
import org.briarproject.nullsafety.NotNullByDefault;
|
import org.briarproject.nullsafety.NotNullByDefault;
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
@@ -21,34 +25,42 @@ import java.util.List;
|
|||||||
import javax.annotation.concurrent.Immutable;
|
import javax.annotation.concurrent.Immutable;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.BETA_PROTOCOL_VERSION;
|
|
||||||
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.COMMIT_LENGTH;
|
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.COMMIT_LENGTH;
|
||||||
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.PROTOCOL_VERSION;
|
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.QR_FORMAT_VERSION;
|
||||||
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.TRANSPORT_ID_BLUETOOTH;
|
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.TRANSPORT_ID_BLUETOOTH;
|
||||||
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.TRANSPORT_ID_LAN;
|
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.TRANSPORT_ID_LAN;
|
||||||
|
import static org.briarproject.bramble.api.qrcode.QrCodeClassifier.QrCodeType.BQP;
|
||||||
|
import static org.briarproject.bramble.util.StringUtils.ISO_8859_1;
|
||||||
|
|
||||||
@Immutable
|
@Immutable
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
class PayloadParserImpl implements PayloadParser {
|
class PayloadParserImpl implements PayloadParser {
|
||||||
|
|
||||||
private final BdfReaderFactory bdfReaderFactory;
|
private final BdfReaderFactory bdfReaderFactory;
|
||||||
|
private final QrCodeClassifier qrCodeClassifier;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
PayloadParserImpl(BdfReaderFactory bdfReaderFactory) {
|
PayloadParserImpl(BdfReaderFactory bdfReaderFactory,
|
||||||
|
QrCodeClassifier qrCodeClassifier) {
|
||||||
this.bdfReaderFactory = bdfReaderFactory;
|
this.bdfReaderFactory = bdfReaderFactory;
|
||||||
|
this.qrCodeClassifier = qrCodeClassifier;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Payload parse(byte[] raw) throws IOException {
|
public Payload parse(String payloadString) throws IOException {
|
||||||
ByteArrayInputStream in = new ByteArrayInputStream(raw);
|
Pair<QrCodeType, Integer> typeAndVersion =
|
||||||
// First byte: the protocol version
|
qrCodeClassifier.classifyQrCode(payloadString);
|
||||||
int protocolVersion = in.read();
|
QrCodeType qrCodeType = typeAndVersion.getFirst();
|
||||||
if (protocolVersion == -1) throw new FormatException();
|
if (qrCodeType != BQP) throw new WrongQrCodeTypeException(qrCodeType);
|
||||||
if (protocolVersion != PROTOCOL_VERSION) {
|
int formatVersion = typeAndVersion.getSecond();
|
||||||
boolean tooOld = protocolVersion < PROTOCOL_VERSION ||
|
if (formatVersion != QR_FORMAT_VERSION) {
|
||||||
protocolVersion == BETA_PROTOCOL_VERSION;
|
boolean tooOld = formatVersion < QR_FORMAT_VERSION;
|
||||||
throw new UnsupportedVersionException(tooOld);
|
throw new UnsupportedVersionException(tooOld);
|
||||||
}
|
}
|
||||||
|
byte[] raw = payloadString.getBytes(ISO_8859_1);
|
||||||
|
ByteArrayInputStream in = new ByteArrayInputStream(raw);
|
||||||
|
// First byte: the format identifier and version (already parsed)
|
||||||
|
if (in.read() == -1) throw new AssertionError();
|
||||||
// The rest of the payload is a BDF list with one or more elements
|
// The rest of the payload is a BDF list with one or more elements
|
||||||
BdfReader r = bdfReaderFactory.createReader(in);
|
BdfReader r = bdfReaderFactory.createReader(in);
|
||||||
BdfList payload = r.readList();
|
BdfList payload = r.readList();
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import org.briarproject.bramble.api.event.EventExecutor;
|
|||||||
import org.briarproject.bramble.api.mailbox.MailboxPairingTask;
|
import org.briarproject.bramble.api.mailbox.MailboxPairingTask;
|
||||||
import org.briarproject.bramble.api.mailbox.MailboxSettingsManager;
|
import org.briarproject.bramble.api.mailbox.MailboxSettingsManager;
|
||||||
import org.briarproject.bramble.api.mailbox.MailboxUpdateManager;
|
import org.briarproject.bramble.api.mailbox.MailboxUpdateManager;
|
||||||
|
import org.briarproject.bramble.api.qrcode.QrCodeClassifier;
|
||||||
import org.briarproject.bramble.api.system.Clock;
|
import org.briarproject.bramble.api.system.Clock;
|
||||||
import org.briarproject.nullsafety.NotNullByDefault;
|
import org.briarproject.nullsafety.NotNullByDefault;
|
||||||
|
|
||||||
@@ -25,6 +26,7 @@ class MailboxPairingTaskFactoryImpl implements MailboxPairingTaskFactory {
|
|||||||
private final MailboxApi api;
|
private final MailboxApi api;
|
||||||
private final MailboxSettingsManager mailboxSettingsManager;
|
private final MailboxSettingsManager mailboxSettingsManager;
|
||||||
private final MailboxUpdateManager mailboxUpdateManager;
|
private final MailboxUpdateManager mailboxUpdateManager;
|
||||||
|
private final QrCodeClassifier qrCodeClassifier;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
MailboxPairingTaskFactoryImpl(
|
MailboxPairingTaskFactoryImpl(
|
||||||
@@ -34,7 +36,8 @@ class MailboxPairingTaskFactoryImpl implements MailboxPairingTaskFactory {
|
|||||||
Clock clock,
|
Clock clock,
|
||||||
MailboxApi api,
|
MailboxApi api,
|
||||||
MailboxSettingsManager mailboxSettingsManager,
|
MailboxSettingsManager mailboxSettingsManager,
|
||||||
MailboxUpdateManager mailboxUpdateManager) {
|
MailboxUpdateManager mailboxUpdateManager,
|
||||||
|
QrCodeClassifier qrCodeClassifier) {
|
||||||
this.eventExecutor = eventExecutor;
|
this.eventExecutor = eventExecutor;
|
||||||
this.db = db;
|
this.db = db;
|
||||||
this.crypto = crypto;
|
this.crypto = crypto;
|
||||||
@@ -42,12 +45,13 @@ class MailboxPairingTaskFactoryImpl implements MailboxPairingTaskFactory {
|
|||||||
this.api = api;
|
this.api = api;
|
||||||
this.mailboxSettingsManager = mailboxSettingsManager;
|
this.mailboxSettingsManager = mailboxSettingsManager;
|
||||||
this.mailboxUpdateManager = mailboxUpdateManager;
|
this.mailboxUpdateManager = mailboxUpdateManager;
|
||||||
|
this.qrCodeClassifier = qrCodeClassifier;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public MailboxPairingTask createPairingTask(String qrCodePayload) {
|
public MailboxPairingTask createPairingTask(String qrCodePayload) {
|
||||||
return new MailboxPairingTaskImpl(qrCodePayload, eventExecutor, db,
|
return new MailboxPairingTaskImpl(qrCodePayload, eventExecutor, db,
|
||||||
crypto, clock, api, mailboxSettingsManager,
|
crypto, clock, api, mailboxSettingsManager,
|
||||||
mailboxUpdateManager);
|
mailboxUpdateManager, qrCodeClassifier);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package org.briarproject.bramble.mailbox;
|
|||||||
|
|
||||||
import org.briarproject.bramble.api.Consumer;
|
import org.briarproject.bramble.api.Consumer;
|
||||||
import org.briarproject.bramble.api.FormatException;
|
import org.briarproject.bramble.api.FormatException;
|
||||||
|
import org.briarproject.bramble.api.Pair;
|
||||||
import org.briarproject.bramble.api.contact.Contact;
|
import org.briarproject.bramble.api.contact.Contact;
|
||||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||||
import org.briarproject.bramble.api.db.DatabaseComponent;
|
import org.briarproject.bramble.api.db.DatabaseComponent;
|
||||||
@@ -9,18 +10,26 @@ import org.briarproject.bramble.api.db.DbException;
|
|||||||
import org.briarproject.bramble.api.event.EventExecutor;
|
import org.briarproject.bramble.api.event.EventExecutor;
|
||||||
import org.briarproject.bramble.api.mailbox.MailboxAuthToken;
|
import org.briarproject.bramble.api.mailbox.MailboxAuthToken;
|
||||||
import org.briarproject.bramble.api.mailbox.MailboxPairingState;
|
import org.briarproject.bramble.api.mailbox.MailboxPairingState;
|
||||||
|
import org.briarproject.bramble.api.mailbox.MailboxPairingState.ConnectionError;
|
||||||
|
import org.briarproject.bramble.api.mailbox.MailboxPairingState.InvalidQrCode;
|
||||||
|
import org.briarproject.bramble.api.mailbox.MailboxPairingState.MailboxAlreadyPaired;
|
||||||
|
import org.briarproject.bramble.api.mailbox.MailboxPairingState.Paired;
|
||||||
|
import org.briarproject.bramble.api.mailbox.MailboxPairingState.Pairing;
|
||||||
|
import org.briarproject.bramble.api.mailbox.MailboxPairingState.QrCodeReceived;
|
||||||
|
import org.briarproject.bramble.api.mailbox.MailboxPairingState.UnexpectedError;
|
||||||
import org.briarproject.bramble.api.mailbox.MailboxPairingTask;
|
import org.briarproject.bramble.api.mailbox.MailboxPairingTask;
|
||||||
import org.briarproject.bramble.api.mailbox.MailboxProperties;
|
import org.briarproject.bramble.api.mailbox.MailboxProperties;
|
||||||
import org.briarproject.bramble.api.mailbox.MailboxSettingsManager;
|
import org.briarproject.bramble.api.mailbox.MailboxSettingsManager;
|
||||||
import org.briarproject.bramble.api.mailbox.MailboxUpdate;
|
import org.briarproject.bramble.api.mailbox.MailboxUpdate;
|
||||||
import org.briarproject.bramble.api.mailbox.MailboxUpdateManager;
|
import org.briarproject.bramble.api.mailbox.MailboxUpdateManager;
|
||||||
|
import org.briarproject.bramble.api.qrcode.QrCodeClassifier;
|
||||||
|
import org.briarproject.bramble.api.qrcode.QrCodeClassifier.QrCodeType;
|
||||||
import org.briarproject.bramble.api.system.Clock;
|
import org.briarproject.bramble.api.system.Clock;
|
||||||
import org.briarproject.bramble.mailbox.MailboxApi.ApiException;
|
import org.briarproject.bramble.mailbox.MailboxApi.ApiException;
|
||||||
import org.briarproject.bramble.mailbox.MailboxApi.MailboxAlreadyPairedException;
|
import org.briarproject.bramble.mailbox.MailboxApi.MailboxAlreadyPairedException;
|
||||||
import org.briarproject.nullsafety.NotNullByDefault;
|
import org.briarproject.nullsafety.NotNullByDefault;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.charset.Charset;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -32,7 +41,10 @@ import javax.annotation.concurrent.ThreadSafe;
|
|||||||
|
|
||||||
import static java.util.logging.Level.WARNING;
|
import static java.util.logging.Level.WARNING;
|
||||||
import static java.util.logging.Logger.getLogger;
|
import static java.util.logging.Logger.getLogger;
|
||||||
|
import static org.briarproject.bramble.api.mailbox.MailboxConstants.QR_FORMAT_VERSION;
|
||||||
|
import static org.briarproject.bramble.api.qrcode.QrCodeClassifier.QrCodeType.MAILBOX;
|
||||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
import static org.briarproject.bramble.util.LogUtils.logException;
|
||||||
|
import static org.briarproject.bramble.util.StringUtils.ISO_8859_1;
|
||||||
|
|
||||||
@ThreadSafe
|
@ThreadSafe
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
@@ -40,9 +52,6 @@ class MailboxPairingTaskImpl implements MailboxPairingTask {
|
|||||||
|
|
||||||
private final static Logger LOG =
|
private final static Logger LOG =
|
||||||
getLogger(MailboxPairingTaskImpl.class.getName());
|
getLogger(MailboxPairingTaskImpl.class.getName());
|
||||||
@SuppressWarnings("CharsetObjectCanBeUsed") // Requires minSdkVersion >= 19
|
|
||||||
private static final Charset ISO_8859_1 = Charset.forName("ISO-8859-1");
|
|
||||||
private static final int VERSION_REQUIRED = 32;
|
|
||||||
|
|
||||||
private final String payload;
|
private final String payload;
|
||||||
private final Executor eventExecutor;
|
private final Executor eventExecutor;
|
||||||
@@ -52,6 +61,8 @@ class MailboxPairingTaskImpl implements MailboxPairingTask {
|
|||||||
private final MailboxApi api;
|
private final MailboxApi api;
|
||||||
private final MailboxSettingsManager mailboxSettingsManager;
|
private final MailboxSettingsManager mailboxSettingsManager;
|
||||||
private final MailboxUpdateManager mailboxUpdateManager;
|
private final MailboxUpdateManager mailboxUpdateManager;
|
||||||
|
private final QrCodeClassifier qrCodeClassifier;
|
||||||
|
private final long timeStarted;
|
||||||
|
|
||||||
private final Object lock = new Object();
|
private final Object lock = new Object();
|
||||||
@GuardedBy("lock")
|
@GuardedBy("lock")
|
||||||
@@ -68,7 +79,8 @@ class MailboxPairingTaskImpl implements MailboxPairingTask {
|
|||||||
Clock clock,
|
Clock clock,
|
||||||
MailboxApi api,
|
MailboxApi api,
|
||||||
MailboxSettingsManager mailboxSettingsManager,
|
MailboxSettingsManager mailboxSettingsManager,
|
||||||
MailboxUpdateManager mailboxUpdateManager) {
|
MailboxUpdateManager mailboxUpdateManager,
|
||||||
|
QrCodeClassifier qrCodeClassifier) {
|
||||||
this.payload = payload;
|
this.payload = payload;
|
||||||
this.eventExecutor = eventExecutor;
|
this.eventExecutor = eventExecutor;
|
||||||
this.db = db;
|
this.db = db;
|
||||||
@@ -77,7 +89,9 @@ class MailboxPairingTaskImpl implements MailboxPairingTask {
|
|||||||
this.api = api;
|
this.api = api;
|
||||||
this.mailboxSettingsManager = mailboxSettingsManager;
|
this.mailboxSettingsManager = mailboxSettingsManager;
|
||||||
this.mailboxUpdateManager = mailboxUpdateManager;
|
this.mailboxUpdateManager = mailboxUpdateManager;
|
||||||
state = new MailboxPairingState.QrCodeReceived();
|
this.qrCodeClassifier = qrCodeClassifier;
|
||||||
|
timeStarted = clock.currentTimeMillis();
|
||||||
|
state = new QrCodeReceived(timeStarted);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -99,22 +113,30 @@ class MailboxPairingTaskImpl implements MailboxPairingTask {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
|
Pair<QrCodeType, Integer> typeAndVersion =
|
||||||
|
qrCodeClassifier.classifyQrCode(payload);
|
||||||
|
QrCodeType qrCodeType = typeAndVersion.getFirst();
|
||||||
|
int formatVersion = typeAndVersion.getSecond();
|
||||||
|
if (qrCodeType != MAILBOX || formatVersion != QR_FORMAT_VERSION) {
|
||||||
|
setState(new InvalidQrCode(qrCodeType, formatVersion));
|
||||||
|
return;
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
pairMailbox();
|
pairMailbox();
|
||||||
} catch (FormatException e) {
|
} catch (FormatException e) {
|
||||||
onMailboxError(e, new MailboxPairingState.InvalidQrCode());
|
onMailboxError(e, new InvalidQrCode(qrCodeType, formatVersion));
|
||||||
} catch (MailboxAlreadyPairedException e) {
|
} catch (MailboxAlreadyPairedException e) {
|
||||||
onMailboxError(e, new MailboxPairingState.MailboxAlreadyPaired());
|
onMailboxError(e, new MailboxAlreadyPaired());
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
onMailboxError(e, new MailboxPairingState.ConnectionError());
|
onMailboxError(e, new ConnectionError());
|
||||||
} catch (ApiException | DbException e) {
|
} catch (ApiException | DbException e) {
|
||||||
onMailboxError(e, new MailboxPairingState.UnexpectedError());
|
onMailboxError(e, new UnexpectedError());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void pairMailbox() throws IOException, ApiException, DbException {
|
private void pairMailbox() throws IOException, ApiException, DbException {
|
||||||
MailboxProperties mailboxProperties = decodeQrCodePayload(payload);
|
MailboxProperties mailboxProperties = decodeQrCodePayload(payload);
|
||||||
setState(new MailboxPairingState.Pairing());
|
setState(new Pairing(timeStarted));
|
||||||
MailboxProperties ownerProperties = api.setup(mailboxProperties);
|
MailboxProperties ownerProperties = api.setup(mailboxProperties);
|
||||||
long time = clock.currentTimeMillis();
|
long time = clock.currentTimeMillis();
|
||||||
db.transaction(false, txn -> {
|
db.transaction(false, txn -> {
|
||||||
@@ -133,7 +155,7 @@ class MailboxPairingTaskImpl implements MailboxPairingTask {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
setState(new MailboxPairingState.Paired());
|
setState(new Paired());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onMailboxError(Exception e, MailboxPairingState state) {
|
private void onMailboxError(Exception e, MailboxPairingState state) {
|
||||||
@@ -167,14 +189,6 @@ class MailboxPairingTaskImpl implements MailboxPairingTask {
|
|||||||
}
|
}
|
||||||
throw new FormatException();
|
throw new FormatException();
|
||||||
}
|
}
|
||||||
int version = bytes[0] & 0xFF;
|
|
||||||
if (version != VERSION_REQUIRED) {
|
|
||||||
if (LOG.isLoggable(WARNING)) {
|
|
||||||
LOG.warning("QR code has not version " + VERSION_REQUIRED +
|
|
||||||
": " + version);
|
|
||||||
}
|
|
||||||
throw new FormatException();
|
|
||||||
}
|
|
||||||
LOG.info("QR code is valid");
|
LOG.info("QR code is valid");
|
||||||
byte[] onionPubKey = Arrays.copyOfRange(bytes, 1, 33);
|
byte[] onionPubKey = Arrays.copyOfRange(bytes, 1, 33);
|
||||||
String onion = crypto.encodeOnion(onionPubKey);
|
String onion = crypto.encodeOnion(onionPubKey);
|
||||||
|
|||||||
@@ -46,7 +46,6 @@ import java.io.OutputStream;
|
|||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
import java.net.ServerSocket;
|
import java.net.ServerSocket;
|
||||||
import java.net.Socket;
|
import java.net.Socket;
|
||||||
import java.nio.charset.Charset;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -100,6 +99,7 @@ import static org.briarproject.bramble.util.IoUtils.copyAndClose;
|
|||||||
import static org.briarproject.bramble.util.IoUtils.tryToClose;
|
import static org.briarproject.bramble.util.IoUtils.tryToClose;
|
||||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
import static org.briarproject.bramble.util.LogUtils.logException;
|
||||||
import static org.briarproject.bramble.util.PrivacyUtils.scrubOnion;
|
import static org.briarproject.bramble.util.PrivacyUtils.scrubOnion;
|
||||||
|
import static org.briarproject.bramble.util.StringUtils.UTF_8;
|
||||||
import static org.briarproject.bramble.util.StringUtils.isNullOrEmpty;
|
import static org.briarproject.bramble.util.StringUtils.isNullOrEmpty;
|
||||||
|
|
||||||
@MethodsNotNullByDefault
|
@MethodsNotNullByDefault
|
||||||
@@ -140,7 +140,8 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
|||||||
private final long maxLatency;
|
private final long maxLatency;
|
||||||
private final int maxIdleTime;
|
private final int maxIdleTime;
|
||||||
private final int socketTimeout;
|
private final int socketTimeout;
|
||||||
private final File torDirectory, geoIpFile, configFile;
|
private final File torDirectory;
|
||||||
|
private final File configFile;
|
||||||
private final int torSocksPort;
|
private final int torSocksPort;
|
||||||
private final int torControlPort;
|
private final int torControlPort;
|
||||||
private final File doneFile, cookieFile;
|
private final File doneFile, cookieFile;
|
||||||
@@ -195,7 +196,6 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
|||||||
this.torDirectory = torDirectory;
|
this.torDirectory = torDirectory;
|
||||||
this.torSocksPort = torSocksPort;
|
this.torSocksPort = torSocksPort;
|
||||||
this.torControlPort = torControlPort;
|
this.torControlPort = torControlPort;
|
||||||
geoIpFile = new File(torDirectory, "geoip");
|
|
||||||
configFile = new File(torDirectory, "torrc");
|
configFile = new File(torDirectory, "torrc");
|
||||||
doneFile = new File(torDirectory, "done");
|
doneFile = new File(torDirectory, "done");
|
||||||
cookieFile = new File(torDirectory, ".tor/control_auth_cookie");
|
cookieFile = new File(torDirectory, ".tor/control_auth_cookie");
|
||||||
@@ -332,12 +332,6 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
|||||||
// The done file may already exist from a previous installation
|
// The done file may already exist from a previous installation
|
||||||
//noinspection ResultOfMethodCallIgnored
|
//noinspection ResultOfMethodCallIgnored
|
||||||
doneFile.delete();
|
doneFile.delete();
|
||||||
// The GeoIP file may exist from a previous installation - we can
|
|
||||||
// save some space by deleting it.
|
|
||||||
// TODO: Remove after a reasonable migration period
|
|
||||||
// (added 2022-03-29)
|
|
||||||
//noinspection ResultOfMethodCallIgnored
|
|
||||||
geoIpFile.delete();
|
|
||||||
installTorExecutable();
|
installTorExecutable();
|
||||||
installObfs4Executable();
|
installObfs4Executable();
|
||||||
installSnowflakeExecutable();
|
installSnowflakeExecutable();
|
||||||
@@ -419,9 +413,7 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
|||||||
append(strb, "ClientTransportPlugin meek_lite exec", obfs4Path);
|
append(strb, "ClientTransportPlugin meek_lite exec", obfs4Path);
|
||||||
String snowflakePath = getSnowflakeExecutableFile().getAbsolutePath();
|
String snowflakePath = getSnowflakeExecutableFile().getAbsolutePath();
|
||||||
append(strb, "ClientTransportPlugin snowflake exec", snowflakePath);
|
append(strb, "ClientTransportPlugin snowflake exec", snowflakePath);
|
||||||
//noinspection CharsetObjectCanBeUsed
|
return new ByteArrayInputStream(strb.toString().getBytes(UTF_8));
|
||||||
return new ByteArrayInputStream(
|
|
||||||
strb.toString().getBytes(Charset.forName("UTF-8")));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void listFiles(File f) {
|
private void listFiles(File f) {
|
||||||
@@ -811,7 +803,8 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
|||||||
@Override
|
@Override
|
||||||
public void controlConnectionClosed() {
|
public void controlConnectionClosed() {
|
||||||
if (state.isTorRunning()) {
|
if (state.isTorRunning()) {
|
||||||
throw new RuntimeException("Control connection closed");
|
// TODO: Restart the Tor process
|
||||||
|
LOG.warning("Control connection closed");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import net.i2p.crypto.eddsa.spec.EdDSAPrivateKeySpec;
|
|||||||
import org.bouncycastle.util.encoders.Base64;
|
import org.bouncycastle.util.encoders.Base64;
|
||||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||||
|
|
||||||
import java.nio.charset.Charset;
|
import static org.briarproject.bramble.util.StringUtils.US_ASCII;
|
||||||
|
|
||||||
class TorRendezvousCryptoImpl implements TorRendezvousCrypto {
|
class TorRendezvousCryptoImpl implements TorRendezvousCrypto {
|
||||||
|
|
||||||
@@ -31,6 +31,6 @@ class TorRendezvousCryptoImpl implements TorRendezvousCrypto {
|
|||||||
EdDSAPrivateKeySpec spec = new EdDSAPrivateKeySpec(seed, CURVE_SPEC);
|
EdDSAPrivateKeySpec spec = new EdDSAPrivateKeySpec(seed, CURVE_SPEC);
|
||||||
byte[] hash = spec.getH();
|
byte[] hash = spec.getH();
|
||||||
byte[] base64 = Base64.encode(hash);
|
byte[] base64 = Base64.encode(hash);
|
||||||
return "ED25519-V3:" + new String(base64, Charset.forName("US-ASCII"));
|
return "ED25519-V3:" + new String(base64, US_ASCII);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,42 @@
|
|||||||
|
package org.briarproject.bramble.qrcode;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.Pair;
|
||||||
|
import org.briarproject.bramble.api.keyagreement.KeyAgreementConstants;
|
||||||
|
import org.briarproject.bramble.api.mailbox.MailboxConstants;
|
||||||
|
import org.briarproject.bramble.api.qrcode.QrCodeClassifier;
|
||||||
|
import org.briarproject.nullsafety.NotNullByDefault;
|
||||||
|
|
||||||
|
import javax.annotation.concurrent.Immutable;
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import static org.briarproject.bramble.api.qrcode.QrCodeClassifier.QrCodeType.BQP;
|
||||||
|
import static org.briarproject.bramble.api.qrcode.QrCodeClassifier.QrCodeType.MAILBOX;
|
||||||
|
import static org.briarproject.bramble.api.qrcode.QrCodeClassifier.QrCodeType.UNKNOWN;
|
||||||
|
import static org.briarproject.bramble.util.StringUtils.ISO_8859_1;
|
||||||
|
|
||||||
|
@Immutable
|
||||||
|
@NotNullByDefault
|
||||||
|
class QrCodeClassifierImpl implements QrCodeClassifier {
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
QrCodeClassifierImpl() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Pair<QrCodeType, Integer> classifyQrCode(String payload) {
|
||||||
|
byte[] bytes = payload.getBytes(ISO_8859_1);
|
||||||
|
if (bytes.length == 0) return new Pair<>(UNKNOWN, 0);
|
||||||
|
// If this is a Bramble QR code then the first byte encodes the
|
||||||
|
// format ID (3 bits) and version (5 bits)
|
||||||
|
int formatIdAndVersion = bytes[0] & 0xFF;
|
||||||
|
int formatId = formatIdAndVersion >> 5;
|
||||||
|
int formatVersion = formatIdAndVersion & 0x1F;
|
||||||
|
if (formatId == KeyAgreementConstants.QR_FORMAT_ID) {
|
||||||
|
return new Pair<>(BQP, formatVersion);
|
||||||
|
}
|
||||||
|
if (formatId == MailboxConstants.QR_FORMAT_ID) {
|
||||||
|
return new Pair<>(MAILBOX, formatVersion);
|
||||||
|
}
|
||||||
|
return new Pair<>(UNKNOWN, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
package org.briarproject.bramble.qrcode;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.qrcode.QrCodeClassifier;
|
||||||
|
|
||||||
|
import dagger.Module;
|
||||||
|
import dagger.Provides;
|
||||||
|
|
||||||
|
@Module
|
||||||
|
public class QrCodeModule {
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
QrCodeClassifier provideQrCodeClassifier(
|
||||||
|
QrCodeClassifierImpl qrCodeClassifier) {
|
||||||
|
return qrCodeClassifier;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -21,11 +21,11 @@ n Bridge obfs4 185.103.252.72:443 75F15E9339FF572F88F5588D429FEA379744BC53 cert=
|
|||||||
n Bridge obfs4 76.255.201.112:8888 96CF36C2ECCFB7376AB6BE905BECD2C2AE8AEFCD cert=+q0pjaiM0JMqHL/BKqCRD+pjflaw/S406eUDF7CnFgamvQW3l2HVLJhQ6uX9P8zff0PLGg iat-mode=0
|
n Bridge obfs4 76.255.201.112:8888 96CF36C2ECCFB7376AB6BE905BECD2C2AE8AEFCD cert=+q0pjaiM0JMqHL/BKqCRD+pjflaw/S406eUDF7CnFgamvQW3l2HVLJhQ6uX9P8zff0PLGg iat-mode=0
|
||||||
n Bridge obfs4 94.142.246.132:8088 135C158527AA9FE9A2F26EC515EB6999D813D347 cert=wTUz0/5FhAZRkitil5MprGbSF3JzjxjxI1kAmxAdSeDy98NgcLr11f/qUXWDC76Y97RiSg iat-mode=0
|
n Bridge obfs4 94.142.246.132:8088 135C158527AA9FE9A2F26EC515EB6999D813D347 cert=wTUz0/5FhAZRkitil5MprGbSF3JzjxjxI1kAmxAdSeDy98NgcLr11f/qUXWDC76Y97RiSg iat-mode=0
|
||||||
n Bridge obfs4 20.102.79.78:22022 B5705F7E616DAB0F477E3E1ADC23E40413F683FE cert=1Cc/hwPtPjzFKGHVOP0j/qmBgnvquRx8+im35/u5TIYjDQ3FlMfA5VvTrQ/hbX8BZZooLQ iat-mode=0
|
n Bridge obfs4 20.102.79.78:22022 B5705F7E616DAB0F477E3E1ADC23E40413F683FE cert=1Cc/hwPtPjzFKGHVOP0j/qmBgnvquRx8+im35/u5TIYjDQ3FlMfA5VvTrQ/hbX8BZZooLQ iat-mode=0
|
||||||
n Bridge obfs4 207.154.242.137:80 8E67A1B2A342652EE27376BD61BECF5806700E7F cert=qUrR9fan3XPNGNOwn9WGlXLJNZZx0grXH4AZXR+yoBbtbbj5Ak1n4a7TtjYgXcWcs/gHXw iat-mode=0
|
|
||||||
n Bridge obfs4 152.70.180.20:1993 3327C43587E66AD5F874C4234A1D72C938AD7318 cert=s7xLRUO2psaX7TMUP2YhXdxItR4U6K7D+E3gQaS/+yWUppevtazIibq4dN1g5Reu6dD2QQ iat-mode=0
|
n Bridge obfs4 152.70.180.20:1993 3327C43587E66AD5F874C4234A1D72C938AD7318 cert=s7xLRUO2psaX7TMUP2YhXdxItR4U6K7D+E3gQaS/+yWUppevtazIibq4dN1g5Reu6dD2QQ iat-mode=0
|
||||||
n Bridge obfs4 144.202.12.254:10002 4E220F45CD404C8A3082A36326A5ED19BB8D4404 cert=iLz5YYWO4pUw7U7MRNOSvE0qO+IVeE4kVfFVWPO3coH3FmZtrkvlaTklfXxHZaCcXWBgaA iat-mode=0
|
n Bridge obfs4 144.202.12.254:10002 4E220F45CD404C8A3082A36326A5ED19BB8D4404 cert=iLz5YYWO4pUw7U7MRNOSvE0qO+IVeE4kVfFVWPO3coH3FmZtrkvlaTklfXxHZaCcXWBgaA iat-mode=0
|
||||||
n Bridge obfs4 15.235.47.204:42058 869133925B3CD07683BDF01805C36448D090CE88 cert=PFwh4mzZlSTUdcEskpe20t998n5jbr81s+XoX7gmazqzUGHNhkendK5K1j2gOxesz9AkBw iat-mode=0
|
n Bridge obfs4 109.14.168.159:5082 BFE1416DEFFE969581F016A4A319A87FFB26BA91 cert=n3X1CDdKBPXPIzfKh83p3ydfMzb0AD9gKC+/gIpHb7+xjjAnYO9x3LT+T/MvOIfAXxYySg iat-mode=0
|
||||||
n Bridge obfs4 51.75.93.136:45532 8402B84833527BC249B21AC885134197E624FB5A cert=LwXEf/Dgo0tKdMJByXdlvWiJqyyPw4T284Cg5qygDuIJJNFuz3ED9UhGil6H4Of3gM7wSg iat-mode=0
|
n Bridge obfs4 45.150.172.16:80 82849E69CBB25EA7F479155F7DCD89D85717FF47 cert=+Krdu1jmVQOxWkMj0mqJHgwbQV49eyD6mZDS+mRExssWNHosa60g4P5Gp81sBJKzN8NrSw iat-mode=0
|
||||||
|
n Bridge obfs4 185.177.207.132:8443 4FB781F7A9DD39DA53A7996907817FC479874D19 cert=UL2gCAXWW5kEWY4TQ0lNeu6OAmzh40bXYVhMnTWVG8USnyy/zEKGSIPgmwTDMumWr9c1Pg iat-mode=0
|
||||||
v Bridge 92.243.15.235:9001 477EAD3C04036B48235F1F27FC91420A286A4B7F
|
v Bridge 92.243.15.235:9001 477EAD3C04036B48235F1F27FC91420A286A4B7F
|
||||||
v Bridge 213.108.108.145:17674 A39C0FE47963B6E8CFE9815549864DE544935A31
|
v Bridge 213.108.108.145:17674 A39C0FE47963B6E8CFE9815549864DE544935A31
|
||||||
v Bridge 213.196.191.96:9060 05E222E2A8C234234FE0CEB58B08A93B8FC360DB
|
v Bridge 213.196.191.96:9060 05E222E2A8C234234FE0CEB58B08A93B8FC360DB
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ import java.io.FileInputStream;
|
|||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
import java.nio.charset.Charset;
|
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
@@ -34,6 +33,7 @@ import static org.briarproject.bramble.test.TestUtils.getIdentity;
|
|||||||
import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
|
import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
|
||||||
import static org.briarproject.bramble.test.TestUtils.getSecretKey;
|
import static org.briarproject.bramble.test.TestUtils.getSecretKey;
|
||||||
import static org.briarproject.bramble.test.TestUtils.getTestDirectory;
|
import static org.briarproject.bramble.test.TestUtils.getTestDirectory;
|
||||||
|
import static org.briarproject.bramble.util.StringUtils.UTF_8;
|
||||||
import static org.briarproject.bramble.util.StringUtils.getRandomString;
|
import static org.briarproject.bramble.util.StringUtils.getRandomString;
|
||||||
import static org.briarproject.bramble.util.StringUtils.toHexString;
|
import static org.briarproject.bramble.util.StringUtils.toHexString;
|
||||||
import static org.junit.Assert.assertArrayEquals;
|
import static org.junit.Assert.assertArrayEquals;
|
||||||
@@ -342,7 +342,7 @@ public class AccountManagerImplTest extends BrambleMockTestCase {
|
|||||||
private void storeDatabaseKey(File f, String hex) throws IOException {
|
private void storeDatabaseKey(File f, String hex) throws IOException {
|
||||||
f.getParentFile().mkdirs();
|
f.getParentFile().mkdirs();
|
||||||
FileOutputStream out = new FileOutputStream(f);
|
FileOutputStream out = new FileOutputStream(f);
|
||||||
out.write(hex.getBytes(Charset.forName("UTF-8")));
|
out.write(hex.getBytes(UTF_8));
|
||||||
out.flush();
|
out.flush();
|
||||||
out.close();
|
out.close();
|
||||||
}
|
}
|
||||||
@@ -350,7 +350,7 @@ public class AccountManagerImplTest extends BrambleMockTestCase {
|
|||||||
@Nullable
|
@Nullable
|
||||||
private String loadDatabaseKey(File f) throws IOException {
|
private String loadDatabaseKey(File f) throws IOException {
|
||||||
BufferedReader reader = new BufferedReader(new InputStreamReader(
|
BufferedReader reader = new BufferedReader(new InputStreamReader(
|
||||||
new FileInputStream(f), Charset.forName("UTF-8")));
|
new FileInputStream(f), UTF_8));
|
||||||
String hex = reader.readLine();
|
String hex = reader.readLine();
|
||||||
reader.close();
|
reader.close();
|
||||||
return hex;
|
return hex;
|
||||||
|
|||||||
@@ -2,21 +2,27 @@ package org.briarproject.bramble.keyagreement;
|
|||||||
|
|
||||||
import org.briarproject.bramble.api.Bytes;
|
import org.briarproject.bramble.api.Bytes;
|
||||||
import org.briarproject.bramble.api.FormatException;
|
import org.briarproject.bramble.api.FormatException;
|
||||||
|
import org.briarproject.bramble.api.Pair;
|
||||||
import org.briarproject.bramble.api.UnsupportedVersionException;
|
import org.briarproject.bramble.api.UnsupportedVersionException;
|
||||||
import org.briarproject.bramble.api.data.BdfList;
|
import org.briarproject.bramble.api.data.BdfList;
|
||||||
import org.briarproject.bramble.api.data.BdfReader;
|
import org.briarproject.bramble.api.data.BdfReader;
|
||||||
import org.briarproject.bramble.api.data.BdfReaderFactory;
|
import org.briarproject.bramble.api.data.BdfReaderFactory;
|
||||||
import org.briarproject.bramble.api.keyagreement.Payload;
|
import org.briarproject.bramble.api.keyagreement.Payload;
|
||||||
|
import org.briarproject.bramble.api.qrcode.QrCodeClassifier;
|
||||||
|
import org.briarproject.bramble.api.qrcode.QrCodeClassifier.QrCodeType;
|
||||||
|
import org.briarproject.bramble.api.qrcode.WrongQrCodeTypeException;
|
||||||
import org.briarproject.bramble.test.BrambleMockTestCase;
|
import org.briarproject.bramble.test.BrambleMockTestCase;
|
||||||
import org.jmock.Expectations;
|
import org.jmock.Expectations;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
|
|
||||||
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.BETA_PROTOCOL_VERSION;
|
|
||||||
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.COMMIT_LENGTH;
|
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.COMMIT_LENGTH;
|
||||||
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.PROTOCOL_VERSION;
|
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.QR_FORMAT_VERSION;
|
||||||
|
import static org.briarproject.bramble.api.qrcode.QrCodeClassifier.QrCodeType.BQP;
|
||||||
|
import static org.briarproject.bramble.api.qrcode.QrCodeClassifier.QrCodeType.MAILBOX;
|
||||||
import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
|
import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
|
||||||
|
import static org.briarproject.bramble.util.StringUtils.getRandomString;
|
||||||
import static org.junit.Assert.assertArrayEquals;
|
import static org.junit.Assert.assertArrayEquals;
|
||||||
import static org.junit.Assert.assertFalse;
|
import static org.junit.Assert.assertFalse;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
@@ -26,32 +32,29 @@ public class PayloadParserImplTest extends BrambleMockTestCase {
|
|||||||
|
|
||||||
private final BdfReaderFactory bdfReaderFactory =
|
private final BdfReaderFactory bdfReaderFactory =
|
||||||
context.mock(BdfReaderFactory.class);
|
context.mock(BdfReaderFactory.class);
|
||||||
|
private final QrCodeClassifier qrCodeClassifier =
|
||||||
|
context.mock(QrCodeClassifier.class);
|
||||||
private final BdfReader bdfReader = context.mock(BdfReader.class);
|
private final BdfReader bdfReader = context.mock(BdfReader.class);
|
||||||
|
|
||||||
private final PayloadParserImpl payloadParser =
|
private final String payload = getRandomString(123);
|
||||||
new PayloadParserImpl(bdfReaderFactory);
|
|
||||||
|
|
||||||
@Test(expected = FormatException.class)
|
private final PayloadParserImpl payloadParser =
|
||||||
public void testThrowsFormatExceptionIfPayloadIsEmpty() throws Exception {
|
new PayloadParserImpl(bdfReaderFactory, qrCodeClassifier);
|
||||||
payloadParser.parse(new byte[0]);
|
|
||||||
|
@Test(expected = WrongQrCodeTypeException.class)
|
||||||
|
public void testThrowsExceptionForWrongQrCodeType() throws Exception {
|
||||||
|
expectClassifyQrCode(payload, MAILBOX, QR_FORMAT_VERSION);
|
||||||
|
|
||||||
|
payloadParser.parse(payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testThrowsUnsupportedVersionExceptionForOldVersion()
|
public void testThrowsUnsupportedVersionExceptionForOldVersion()
|
||||||
throws Exception {
|
throws Exception {
|
||||||
try {
|
expectClassifyQrCode(payload, BQP, QR_FORMAT_VERSION - 1);
|
||||||
payloadParser.parse(new byte[] {PROTOCOL_VERSION - 1});
|
|
||||||
fail();
|
|
||||||
} catch (UnsupportedVersionException e) {
|
|
||||||
assertTrue(e.isTooOld());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testThrowsUnsupportedVersionExceptionForBetaVersion()
|
|
||||||
throws Exception {
|
|
||||||
try {
|
try {
|
||||||
payloadParser.parse(new byte[] {BETA_PROTOCOL_VERSION});
|
payloadParser.parse(payload);
|
||||||
fail();
|
fail();
|
||||||
} catch (UnsupportedVersionException e) {
|
} catch (UnsupportedVersionException e) {
|
||||||
assertTrue(e.isTooOld());
|
assertTrue(e.isTooOld());
|
||||||
@@ -61,8 +64,10 @@ public class PayloadParserImplTest extends BrambleMockTestCase {
|
|||||||
@Test
|
@Test
|
||||||
public void testThrowsUnsupportedVersionExceptionForNewVersion()
|
public void testThrowsUnsupportedVersionExceptionForNewVersion()
|
||||||
throws Exception {
|
throws Exception {
|
||||||
|
expectClassifyQrCode(payload, BQP, QR_FORMAT_VERSION + 1);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
payloadParser.parse(new byte[] {PROTOCOL_VERSION + 1});
|
payloadParser.parse(payload);
|
||||||
fail();
|
fail();
|
||||||
} catch (UnsupportedVersionException e) {
|
} catch (UnsupportedVersionException e) {
|
||||||
assertFalse(e.isTooOld());
|
assertFalse(e.isTooOld());
|
||||||
@@ -71,6 +76,8 @@ public class PayloadParserImplTest extends BrambleMockTestCase {
|
|||||||
|
|
||||||
@Test(expected = FormatException.class)
|
@Test(expected = FormatException.class)
|
||||||
public void testThrowsFormatExceptionForEmptyList() throws Exception {
|
public void testThrowsFormatExceptionForEmptyList() throws Exception {
|
||||||
|
expectClassifyQrCode(payload, BQP, QR_FORMAT_VERSION);
|
||||||
|
|
||||||
context.checking(new Expectations() {{
|
context.checking(new Expectations() {{
|
||||||
oneOf(bdfReaderFactory).createReader(
|
oneOf(bdfReaderFactory).createReader(
|
||||||
with(any(ByteArrayInputStream.class)));
|
with(any(ByteArrayInputStream.class)));
|
||||||
@@ -79,7 +86,7 @@ public class PayloadParserImplTest extends BrambleMockTestCase {
|
|||||||
will(returnValue(new BdfList()));
|
will(returnValue(new BdfList()));
|
||||||
}});
|
}});
|
||||||
|
|
||||||
payloadParser.parse(new byte[] {PROTOCOL_VERSION});
|
payloadParser.parse(payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(expected = FormatException.class)
|
@Test(expected = FormatException.class)
|
||||||
@@ -87,6 +94,8 @@ public class PayloadParserImplTest extends BrambleMockTestCase {
|
|||||||
throws Exception {
|
throws Exception {
|
||||||
byte[] commitment = getRandomBytes(COMMIT_LENGTH);
|
byte[] commitment = getRandomBytes(COMMIT_LENGTH);
|
||||||
|
|
||||||
|
expectClassifyQrCode(payload, BQP, QR_FORMAT_VERSION);
|
||||||
|
|
||||||
context.checking(new Expectations() {{
|
context.checking(new Expectations() {{
|
||||||
oneOf(bdfReaderFactory).createReader(
|
oneOf(bdfReaderFactory).createReader(
|
||||||
with(any(ByteArrayInputStream.class)));
|
with(any(ByteArrayInputStream.class)));
|
||||||
@@ -97,7 +106,7 @@ public class PayloadParserImplTest extends BrambleMockTestCase {
|
|||||||
will(returnValue(false));
|
will(returnValue(false));
|
||||||
}});
|
}});
|
||||||
|
|
||||||
payloadParser.parse(new byte[] {PROTOCOL_VERSION});
|
payloadParser.parse(payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(expected = FormatException.class)
|
@Test(expected = FormatException.class)
|
||||||
@@ -105,6 +114,7 @@ public class PayloadParserImplTest extends BrambleMockTestCase {
|
|||||||
throws Exception {
|
throws Exception {
|
||||||
byte[] commitment = getRandomBytes(COMMIT_LENGTH - 1);
|
byte[] commitment = getRandomBytes(COMMIT_LENGTH - 1);
|
||||||
|
|
||||||
|
expectClassifyQrCode(payload, BQP, QR_FORMAT_VERSION);
|
||||||
context.checking(new Expectations() {{
|
context.checking(new Expectations() {{
|
||||||
oneOf(bdfReaderFactory).createReader(
|
oneOf(bdfReaderFactory).createReader(
|
||||||
with(any(ByteArrayInputStream.class)));
|
with(any(ByteArrayInputStream.class)));
|
||||||
@@ -115,7 +125,7 @@ public class PayloadParserImplTest extends BrambleMockTestCase {
|
|||||||
will(returnValue(true));
|
will(returnValue(true));
|
||||||
}});
|
}});
|
||||||
|
|
||||||
payloadParser.parse(new byte[] {PROTOCOL_VERSION});
|
payloadParser.parse(payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(expected = FormatException.class)
|
@Test(expected = FormatException.class)
|
||||||
@@ -123,6 +133,7 @@ public class PayloadParserImplTest extends BrambleMockTestCase {
|
|||||||
throws Exception {
|
throws Exception {
|
||||||
byte[] commitment = getRandomBytes(COMMIT_LENGTH + 1);
|
byte[] commitment = getRandomBytes(COMMIT_LENGTH + 1);
|
||||||
|
|
||||||
|
expectClassifyQrCode(payload, BQP, QR_FORMAT_VERSION);
|
||||||
context.checking(new Expectations() {{
|
context.checking(new Expectations() {{
|
||||||
oneOf(bdfReaderFactory).createReader(
|
oneOf(bdfReaderFactory).createReader(
|
||||||
with(any(ByteArrayInputStream.class)));
|
with(any(ByteArrayInputStream.class)));
|
||||||
@@ -133,12 +144,14 @@ public class PayloadParserImplTest extends BrambleMockTestCase {
|
|||||||
will(returnValue(true));
|
will(returnValue(true));
|
||||||
}});
|
}});
|
||||||
|
|
||||||
payloadParser.parse(new byte[] {PROTOCOL_VERSION});
|
payloadParser.parse(payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testAcceptsPayloadWithNoDescriptors() throws Exception {
|
public void testAcceptsPayloadWithNoDescriptors() throws Exception {
|
||||||
byte[] commitment = getRandomBytes(COMMIT_LENGTH);
|
byte[] commitment = getRandomBytes(COMMIT_LENGTH);
|
||||||
|
|
||||||
|
expectClassifyQrCode(payload, BQP, QR_FORMAT_VERSION);
|
||||||
context.checking(new Expectations() {{
|
context.checking(new Expectations() {{
|
||||||
oneOf(bdfReaderFactory).createReader(
|
oneOf(bdfReaderFactory).createReader(
|
||||||
with(any(ByteArrayInputStream.class)));
|
with(any(ByteArrayInputStream.class)));
|
||||||
@@ -149,8 +162,16 @@ public class PayloadParserImplTest extends BrambleMockTestCase {
|
|||||||
will(returnValue(true));
|
will(returnValue(true));
|
||||||
}});
|
}});
|
||||||
|
|
||||||
Payload p = payloadParser.parse(new byte[] {PROTOCOL_VERSION});
|
Payload p = payloadParser.parse(payload);
|
||||||
assertArrayEquals(commitment, p.getCommitment());
|
assertArrayEquals(commitment, p.getCommitment());
|
||||||
assertTrue(p.getTransportDescriptors().isEmpty());
|
assertTrue(p.getTransportDescriptors().isEmpty());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void expectClassifyQrCode(String payload, QrCodeType qrCodeType,
|
||||||
|
int formatVersion) {
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
oneOf(qrCodeClassifier).classifyQrCode(payload);
|
||||||
|
will(returnValue(new Pair<>(qrCodeType, formatVersion)));
|
||||||
|
}});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package org.briarproject.bramble.mailbox;
|
package org.briarproject.bramble.mailbox;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.Pair;
|
||||||
import org.briarproject.bramble.api.contact.Contact;
|
import org.briarproject.bramble.api.contact.Contact;
|
||||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||||
import org.briarproject.bramble.api.db.DatabaseComponent;
|
import org.briarproject.bramble.api.db.DatabaseComponent;
|
||||||
@@ -7,13 +8,24 @@ import org.briarproject.bramble.api.db.DbException;
|
|||||||
import org.briarproject.bramble.api.db.Transaction;
|
import org.briarproject.bramble.api.db.Transaction;
|
||||||
import org.briarproject.bramble.api.mailbox.MailboxAuthToken;
|
import org.briarproject.bramble.api.mailbox.MailboxAuthToken;
|
||||||
import org.briarproject.bramble.api.mailbox.MailboxPairingState;
|
import org.briarproject.bramble.api.mailbox.MailboxPairingState;
|
||||||
|
import org.briarproject.bramble.api.mailbox.MailboxPairingState.ConnectionError;
|
||||||
|
import org.briarproject.bramble.api.mailbox.MailboxPairingState.InvalidQrCode;
|
||||||
|
import org.briarproject.bramble.api.mailbox.MailboxPairingState.MailboxAlreadyPaired;
|
||||||
|
import org.briarproject.bramble.api.mailbox.MailboxPairingState.Paired;
|
||||||
|
import org.briarproject.bramble.api.mailbox.MailboxPairingState.Pairing;
|
||||||
|
import org.briarproject.bramble.api.mailbox.MailboxPairingState.QrCodeReceived;
|
||||||
|
import org.briarproject.bramble.api.mailbox.MailboxPairingState.UnexpectedError;
|
||||||
import org.briarproject.bramble.api.mailbox.MailboxPairingTask;
|
import org.briarproject.bramble.api.mailbox.MailboxPairingTask;
|
||||||
import org.briarproject.bramble.api.mailbox.MailboxProperties;
|
import org.briarproject.bramble.api.mailbox.MailboxProperties;
|
||||||
import org.briarproject.bramble.api.mailbox.MailboxSettingsManager;
|
import org.briarproject.bramble.api.mailbox.MailboxSettingsManager;
|
||||||
import org.briarproject.bramble.api.mailbox.MailboxUpdate;
|
import org.briarproject.bramble.api.mailbox.MailboxUpdate;
|
||||||
import org.briarproject.bramble.api.mailbox.MailboxUpdateManager;
|
import org.briarproject.bramble.api.mailbox.MailboxUpdateManager;
|
||||||
import org.briarproject.bramble.api.mailbox.MailboxVersion;
|
import org.briarproject.bramble.api.mailbox.MailboxVersion;
|
||||||
|
import org.briarproject.bramble.api.qrcode.QrCodeClassifier;
|
||||||
|
import org.briarproject.bramble.api.qrcode.QrCodeClassifier.QrCodeType;
|
||||||
import org.briarproject.bramble.api.system.Clock;
|
import org.briarproject.bramble.api.system.Clock;
|
||||||
|
import org.briarproject.bramble.mailbox.MailboxApi.ApiException;
|
||||||
|
import org.briarproject.bramble.mailbox.MailboxApi.MailboxAlreadyPairedException;
|
||||||
import org.briarproject.bramble.test.BrambleMockTestCase;
|
import org.briarproject.bramble.test.BrambleMockTestCase;
|
||||||
import org.briarproject.bramble.test.DbExpectations;
|
import org.briarproject.bramble.test.DbExpectations;
|
||||||
import org.briarproject.bramble.test.ImmediateExecutor;
|
import org.briarproject.bramble.test.ImmediateExecutor;
|
||||||
@@ -27,6 +39,9 @@ import java.util.concurrent.Executor;
|
|||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
import static java.util.Collections.singletonList;
|
import static java.util.Collections.singletonList;
|
||||||
|
import static org.briarproject.bramble.api.mailbox.MailboxConstants.QR_FORMAT_VERSION;
|
||||||
|
import static org.briarproject.bramble.api.qrcode.QrCodeClassifier.QrCodeType.BQP;
|
||||||
|
import static org.briarproject.bramble.api.qrcode.QrCodeClassifier.QrCodeType.MAILBOX;
|
||||||
import static org.briarproject.bramble.mailbox.MailboxTestUtils.getQrCodePayload;
|
import static org.briarproject.bramble.mailbox.MailboxTestUtils.getQrCodePayload;
|
||||||
import static org.briarproject.bramble.test.TestUtils.getContact;
|
import static org.briarproject.bramble.test.TestUtils.getContact;
|
||||||
import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
|
import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
|
||||||
@@ -48,9 +63,8 @@ public class MailboxPairingTaskImplTest extends BrambleMockTestCase {
|
|||||||
context.mock(MailboxSettingsManager.class);
|
context.mock(MailboxSettingsManager.class);
|
||||||
private final MailboxUpdateManager mailboxUpdateManager =
|
private final MailboxUpdateManager mailboxUpdateManager =
|
||||||
context.mock(MailboxUpdateManager.class);
|
context.mock(MailboxUpdateManager.class);
|
||||||
private final MailboxPairingTaskFactory factory =
|
private final QrCodeClassifier qrCodeClassifier =
|
||||||
new MailboxPairingTaskFactoryImpl(executor, db, crypto, clock, api,
|
context.mock(QrCodeClassifier.class);
|
||||||
mailboxSettingsManager, mailboxUpdateManager);
|
|
||||||
|
|
||||||
private final String onion = getRandomString(56);
|
private final String onion = getRandomString(56);
|
||||||
private final byte[] onionBytes = getRandomBytes(32);
|
private final byte[] onionBytes = getRandomBytes(32);
|
||||||
@@ -68,32 +82,50 @@ public class MailboxPairingTaskImplTest extends BrambleMockTestCase {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testInitialQrCodeReceivedState() {
|
public void testInitialQrCodeReceivedState() {
|
||||||
MailboxPairingTask task =
|
MailboxPairingTask task = createPairingTask(getRandomString(42));
|
||||||
factory.createPairingTask(getRandomString(42));
|
|
||||||
task.addObserver(state ->
|
task.addObserver(state ->
|
||||||
assertTrue(state instanceof MailboxPairingState.QrCodeReceived)
|
assertTrue(state instanceof QrCodeReceived));
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testInvalidQrCode() {
|
public void testInvalidQrCodeType() {
|
||||||
MailboxPairingTask task1 =
|
String payload = getRandomString(65);
|
||||||
factory.createPairingTask(getRandomString(42));
|
MailboxPairingTask task = createPairingTask(payload);
|
||||||
task1.run();
|
|
||||||
task1.addObserver(state ->
|
|
||||||
assertTrue(state instanceof MailboxPairingState.InvalidQrCode)
|
|
||||||
);
|
|
||||||
|
|
||||||
String goodLength = "00" + getRandomString(63);
|
expectClassifyQrCode(payload, BQP, QR_FORMAT_VERSION);
|
||||||
MailboxPairingTask task2 = factory.createPairingTask(goodLength);
|
|
||||||
task2.run();
|
task.run();
|
||||||
task2.addObserver(state ->
|
task.addObserver(state ->
|
||||||
assertTrue(state instanceof MailboxPairingState.InvalidQrCode)
|
assertTrue(state instanceof InvalidQrCode));
|
||||||
);
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testInvalidQrCodeVersion() {
|
||||||
|
String payload = getRandomString(65);
|
||||||
|
MailboxPairingTask task = createPairingTask(payload);
|
||||||
|
|
||||||
|
expectClassifyQrCode(payload, MAILBOX, QR_FORMAT_VERSION + 1);
|
||||||
|
|
||||||
|
task.run();
|
||||||
|
task.addObserver(state ->
|
||||||
|
assertTrue(state instanceof InvalidQrCode));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testInvalidQrCodeLength() {
|
||||||
|
String payload = getRandomString(42);
|
||||||
|
MailboxPairingTask task = createPairingTask(payload);
|
||||||
|
|
||||||
|
expectClassifyQrCode(payload, MAILBOX, QR_FORMAT_VERSION);
|
||||||
|
|
||||||
|
task.run();
|
||||||
|
task.addObserver(state ->
|
||||||
|
assertTrue(state instanceof InvalidQrCode));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSuccessfulPairing() throws Exception {
|
public void testSuccessfulPairing() throws Exception {
|
||||||
|
expectClassifyQrCode(validPayload, MAILBOX, QR_FORMAT_VERSION);
|
||||||
context.checking(new Expectations() {{
|
context.checking(new Expectations() {{
|
||||||
oneOf(crypto).encodeOnion(onionBytes);
|
oneOf(crypto).encodeOnion(onionBytes);
|
||||||
will(returnValue(onion));
|
will(returnValue(onion));
|
||||||
@@ -121,17 +153,14 @@ public class MailboxPairingTaskImplTest extends BrambleMockTestCase {
|
|||||||
}});
|
}});
|
||||||
|
|
||||||
AtomicInteger i = new AtomicInteger(0);
|
AtomicInteger i = new AtomicInteger(0);
|
||||||
MailboxPairingTask task = factory.createPairingTask(validPayload);
|
MailboxPairingTask task = createPairingTask(validPayload);
|
||||||
task.addObserver(state -> {
|
task.addObserver(state -> {
|
||||||
if (i.get() == 0) {
|
if (i.get() == 0) {
|
||||||
assertEquals(MailboxPairingState.QrCodeReceived.class,
|
assertEquals(QrCodeReceived.class, state.getClass());
|
||||||
state.getClass());
|
|
||||||
} else if (i.get() == 1) {
|
} else if (i.get() == 1) {
|
||||||
assertEquals(MailboxPairingState.Pairing.class,
|
assertEquals(Pairing.class, state.getClass());
|
||||||
state.getClass());
|
|
||||||
} else if (i.get() == 2) {
|
} else if (i.get() == 2) {
|
||||||
assertEquals(MailboxPairingState.Paired.class,
|
assertEquals(Paired.class, state.getClass());
|
||||||
state.getClass());
|
|
||||||
} else fail("Unexpected change of state " + state.getClass());
|
} else fail("Unexpected change of state " + state.getClass());
|
||||||
i.getAndIncrement();
|
i.getAndIncrement();
|
||||||
});
|
});
|
||||||
@@ -140,24 +169,23 @@ public class MailboxPairingTaskImplTest extends BrambleMockTestCase {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testAlreadyPaired() throws Exception {
|
public void testAlreadyPaired() throws Exception {
|
||||||
testApiException(new MailboxApi.MailboxAlreadyPairedException(),
|
testApiException(new MailboxAlreadyPairedException(),
|
||||||
MailboxPairingState.MailboxAlreadyPaired.class);
|
MailboxAlreadyPaired.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testMailboxApiException() throws Exception {
|
public void testMailboxApiException() throws Exception {
|
||||||
testApiException(new MailboxApi.ApiException(),
|
testApiException(new ApiException(), UnexpectedError.class);
|
||||||
MailboxPairingState.UnexpectedError.class);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testApiIOException() throws Exception {
|
public void testApiIOException() throws Exception {
|
||||||
testApiException(new IOException(),
|
testApiException(new IOException(), ConnectionError.class);
|
||||||
MailboxPairingState.ConnectionError.class);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void testApiException(Exception e,
|
private void testApiException(Exception e,
|
||||||
Class<? extends MailboxPairingState> s) throws Exception {
|
Class<? extends MailboxPairingState> s) throws Exception {
|
||||||
|
expectClassifyQrCode(validPayload, MAILBOX, QR_FORMAT_VERSION);
|
||||||
context.checking(new Expectations() {{
|
context.checking(new Expectations() {{
|
||||||
oneOf(crypto).encodeOnion(onionBytes);
|
oneOf(crypto).encodeOnion(onionBytes);
|
||||||
will(returnValue(onion));
|
will(returnValue(onion));
|
||||||
@@ -165,13 +193,14 @@ public class MailboxPairingTaskImplTest extends BrambleMockTestCase {
|
|||||||
will(throwException(e));
|
will(throwException(e));
|
||||||
}});
|
}});
|
||||||
|
|
||||||
MailboxPairingTask task = factory.createPairingTask(validPayload);
|
MailboxPairingTask task = createPairingTask(validPayload);
|
||||||
task.run();
|
task.run();
|
||||||
task.addObserver(state -> assertEquals(state.getClass(), s));
|
task.addObserver(state -> assertEquals(state.getClass(), s));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testDbException() throws Exception {
|
public void testDbException() throws Exception {
|
||||||
|
expectClassifyQrCode(validPayload, MAILBOX, QR_FORMAT_VERSION);
|
||||||
context.checking(new Expectations() {{
|
context.checking(new Expectations() {{
|
||||||
oneOf(crypto).encodeOnion(onionBytes);
|
oneOf(crypto).encodeOnion(onionBytes);
|
||||||
will(returnValue(onion));
|
will(returnValue(onion));
|
||||||
@@ -188,10 +217,10 @@ public class MailboxPairingTaskImplTest extends BrambleMockTestCase {
|
|||||||
will(throwException(new DbException()));
|
will(throwException(new DbException()));
|
||||||
}});
|
}});
|
||||||
|
|
||||||
MailboxPairingTask task = factory.createPairingTask(validPayload);
|
MailboxPairingTask task = createPairingTask(validPayload);
|
||||||
task.run();
|
task.run();
|
||||||
task.addObserver(state -> assertEquals(state.getClass(),
|
task.addObserver(state ->
|
||||||
MailboxPairingState.UnexpectedError.class));
|
assertEquals(state.getClass(), UnexpectedError.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
private PredicateMatcher<MailboxProperties> matches(MailboxProperties p2) {
|
private PredicateMatcher<MailboxProperties> matches(MailboxProperties p2) {
|
||||||
@@ -202,4 +231,22 @@ public class MailboxPairingTaskImplTest extends BrambleMockTestCase {
|
|||||||
p1.getServerSupports().equals(p2.getServerSupports()));
|
p1.getServerSupports().equals(p2.getServerSupports()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private MailboxPairingTask createPairingTask(String qrCodePayload) {
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
oneOf(clock).currentTimeMillis();
|
||||||
|
will(returnValue(time));
|
||||||
|
}});
|
||||||
|
|
||||||
|
return new MailboxPairingTaskImpl(qrCodePayload, executor, db,
|
||||||
|
crypto, clock, api, mailboxSettingsManager,
|
||||||
|
mailboxUpdateManager, qrCodeClassifier);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void expectClassifyQrCode(String payload, QrCodeType qrCodeType,
|
||||||
|
int formatVersion) {
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
oneOf(qrCodeClassifier).classifyQrCode(payload);
|
||||||
|
will(returnValue(new Pair<>(qrCodeType, formatVersion)));
|
||||||
|
}});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ package org.briarproject.bramble.mailbox;
|
|||||||
import org.briarproject.bramble.api.WeakSingletonProvider;
|
import org.briarproject.bramble.api.WeakSingletonProvider;
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.nio.charset.Charset;
|
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
import javax.net.SocketFactory;
|
import javax.net.SocketFactory;
|
||||||
@@ -11,20 +10,24 @@ import javax.net.SocketFactory;
|
|||||||
import okhttp3.OkHttpClient;
|
import okhttp3.OkHttpClient;
|
||||||
|
|
||||||
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
||||||
|
import static org.briarproject.bramble.api.mailbox.MailboxConstants.QR_FORMAT_ID;
|
||||||
|
import static org.briarproject.bramble.api.mailbox.MailboxConstants.QR_FORMAT_VERSION;
|
||||||
import static org.briarproject.bramble.test.TestUtils.getRandomId;
|
import static org.briarproject.bramble.test.TestUtils.getRandomId;
|
||||||
|
import static org.briarproject.bramble.util.StringUtils.ISO_8859_1;
|
||||||
|
|
||||||
class MailboxTestUtils {
|
class MailboxTestUtils {
|
||||||
|
|
||||||
static String getQrCodePayload(byte[] onionBytes, byte[] setupToken) {
|
static String getQrCodePayload(byte[] onionBytes, byte[] setupToken) {
|
||||||
|
int formatIdAndVersion = (QR_FORMAT_ID << 5) | QR_FORMAT_VERSION;
|
||||||
byte[] payloadBytes = ByteBuffer.allocate(65)
|
byte[] payloadBytes = ByteBuffer.allocate(65)
|
||||||
.put((byte) 32) // 1
|
.put((byte) formatIdAndVersion) // 1
|
||||||
.put(onionBytes) // 32
|
.put(onionBytes) // 32
|
||||||
.put(setupToken) // 32
|
.put(setupToken) // 32
|
||||||
.array();
|
.array();
|
||||||
//noinspection CharsetObjectCanBeUsed
|
return new String(payloadBytes, ISO_8859_1);
|
||||||
return new String(payloadBytes, Charset.forName("ISO-8859-1"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Used by mailbox integration tests
|
||||||
static String getQrCodePayload(byte[] setupToken) {
|
static String getQrCodePayload(byte[] setupToken) {
|
||||||
return getQrCodePayload(getRandomId(), setupToken);
|
return getQrCodePayload(getRandomId(), setupToken);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,70 @@
|
|||||||
|
package org.briarproject.bramble.qrcode;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.Pair;
|
||||||
|
import org.briarproject.bramble.api.keyagreement.KeyAgreementConstants;
|
||||||
|
import org.briarproject.bramble.api.mailbox.MailboxConstants;
|
||||||
|
import org.briarproject.bramble.api.qrcode.QrCodeClassifier;
|
||||||
|
import org.briarproject.bramble.api.qrcode.QrCodeClassifier.QrCodeType;
|
||||||
|
import org.briarproject.bramble.test.BrambleTestCase;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import static org.briarproject.bramble.api.qrcode.QrCodeClassifier.QrCodeType.BQP;
|
||||||
|
import static org.briarproject.bramble.api.qrcode.QrCodeClassifier.QrCodeType.MAILBOX;
|
||||||
|
import static org.briarproject.bramble.api.qrcode.QrCodeClassifier.QrCodeType.UNKNOWN;
|
||||||
|
import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
|
||||||
|
import static org.briarproject.bramble.util.StringUtils.ISO_8859_1;
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
public class QrCodeClassifierImplTest extends BrambleTestCase {
|
||||||
|
|
||||||
|
private final QrCodeClassifier classifier = new QrCodeClassifierImpl();
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testClassifiesEmptyStringAsUnknown() {
|
||||||
|
Pair<QrCodeType, Integer> result = classifier.classifyQrCode("");
|
||||||
|
assertEquals(UNKNOWN, result.getFirst());
|
||||||
|
assertEquals(0, result.getSecond().intValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testClassifiesKeyAgreement() {
|
||||||
|
byte[] payloadBytes = getRandomBytes(123);
|
||||||
|
for (int version = 0; version < 32; version++) {
|
||||||
|
int typeAndVersion =
|
||||||
|
(KeyAgreementConstants.QR_FORMAT_ID << 5) | version;
|
||||||
|
payloadBytes[0] = (byte) typeAndVersion;
|
||||||
|
String payload = new String(payloadBytes, ISO_8859_1);
|
||||||
|
Pair<QrCodeType, Integer> result =
|
||||||
|
classifier.classifyQrCode(payload);
|
||||||
|
assertEquals(BQP, result.getFirst());
|
||||||
|
assertEquals(version, result.getSecond().intValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testClassifiesMailbox() {
|
||||||
|
byte[] payloadBytes = getRandomBytes(123);
|
||||||
|
for (int version = 0; version < 32; version++) {
|
||||||
|
int typeAndVersion =
|
||||||
|
(MailboxConstants.QR_FORMAT_ID << 5) | version;
|
||||||
|
payloadBytes[0] = (byte) typeAndVersion;
|
||||||
|
String payload = new String(payloadBytes, ISO_8859_1);
|
||||||
|
Pair<QrCodeType, Integer> result =
|
||||||
|
classifier.classifyQrCode(payload);
|
||||||
|
assertEquals(MAILBOX, result.getFirst());
|
||||||
|
assertEquals(version, result.getSecond().intValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testClassifiesUnknownFormatIdAsUnknown() {
|
||||||
|
byte[] payloadBytes = getRandomBytes(123);
|
||||||
|
int unknownFormatId = MailboxConstants.QR_FORMAT_ID + 1;
|
||||||
|
int typeAndVersion = unknownFormatId << 5;
|
||||||
|
payloadBytes[0] = (byte) typeAndVersion;
|
||||||
|
String payload = new String(payloadBytes, ISO_8859_1);
|
||||||
|
Pair<QrCodeType, Integer> result = classifier.classifyQrCode(payload);
|
||||||
|
assertEquals(UNKNOWN, result.getFirst());
|
||||||
|
assertEquals(0, result.getSecond().intValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -12,11 +12,13 @@ configurations {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation project(path: ':bramble-core', configuration: 'default')
|
implementation project(':bramble-core')
|
||||||
|
|
||||||
implementation fileTree(dir: 'libs', include: '*.jar')
|
implementation fileTree(dir: 'libs', include: '*.jar')
|
||||||
def jna_version = '4.5.2'
|
def jna_version = '4.5.2'
|
||||||
implementation "net.java.dev.jna:jna:$jna_version"
|
implementation "net.java.dev.jna:jna:$jna_version"
|
||||||
implementation "net.java.dev.jna:jna-platform:$jna_version"
|
implementation "net.java.dev.jna:jna-platform:$jna_version"
|
||||||
|
|
||||||
tor "org.briarproject:tor-linux:$tor_version"
|
tor "org.briarproject:tor-linux:$tor_version"
|
||||||
tor "org.briarproject:tor-windows:$tor_version"
|
tor "org.briarproject:tor-windows:$tor_version"
|
||||||
tor "org.briarproject:obfs4proxy-linux:$obfs4proxy_version"
|
tor "org.briarproject:obfs4proxy-linux:$obfs4proxy_version"
|
||||||
@@ -28,9 +30,11 @@ dependencies {
|
|||||||
|
|
||||||
testImplementation project(path: ':bramble-api', configuration: 'testOutput')
|
testImplementation project(path: ':bramble-api', configuration: 'testOutput')
|
||||||
testImplementation project(path: ':bramble-core', configuration: 'testOutput')
|
testImplementation project(path: ':bramble-core', configuration: 'testOutput')
|
||||||
|
|
||||||
testImplementation "junit:junit:$junit_version"
|
testImplementation "junit:junit:$junit_version"
|
||||||
testImplementation "org.jmock:jmock:$jmock_version"
|
testImplementation "org.jmock:jmock:$jmock_version"
|
||||||
testImplementation "org.jmock:jmock-junit4:$jmock_version"
|
testImplementation "org.jmock:jmock-junit4:$jmock_version"
|
||||||
|
testImplementation "com.squareup.okhttp3:okhttp:$okhttp_version"
|
||||||
|
|
||||||
testAnnotationProcessor "com.google.dagger:dagger-compiler:$dagger_version"
|
testAnnotationProcessor "com.google.dagger:dagger-compiler:$dagger_version"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ import java.io.InputStream;
|
|||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.nio.charset.CharacterCodingException;
|
import java.nio.charset.CharacterCodingException;
|
||||||
import java.nio.charset.Charset;
|
|
||||||
import java.nio.charset.CharsetDecoder;
|
import java.nio.charset.CharsetDecoder;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
import java.util.concurrent.Semaphore;
|
import java.util.concurrent.Semaphore;
|
||||||
@@ -33,6 +32,7 @@ import static java.util.logging.Level.WARNING;
|
|||||||
import static jssc.SerialPort.PURGE_RXCLEAR;
|
import static jssc.SerialPort.PURGE_RXCLEAR;
|
||||||
import static jssc.SerialPort.PURGE_TXCLEAR;
|
import static jssc.SerialPort.PURGE_TXCLEAR;
|
||||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
import static org.briarproject.bramble.util.LogUtils.logException;
|
||||||
|
import static org.briarproject.bramble.util.StringUtils.US_ASCII;
|
||||||
|
|
||||||
@MethodsNotNullByDefault
|
@MethodsNotNullByDefault
|
||||||
@ParametersNotNullByDefault
|
@ParametersNotNullByDefault
|
||||||
@@ -41,7 +41,6 @@ class ModemImpl implements Modem, WriteHandler, SerialPortEventListener {
|
|||||||
private static final Logger LOG =
|
private static final Logger LOG =
|
||||||
Logger.getLogger(ModemImpl.class.getName());
|
Logger.getLogger(ModemImpl.class.getName());
|
||||||
|
|
||||||
private static final Charset US_ASCII = Charset.forName("US-ASCII");
|
|
||||||
private static final int MAX_LINE_LENGTH = 256;
|
private static final int MAX_LINE_LENGTH = 256;
|
||||||
private static final int[] BAUD_RATES = {
|
private static final int[] BAUD_RATES = {
|
||||||
256000, 128000, 115200, 57600, 38400, 19200, 14400, 9600, 4800, 1200
|
256000, 128000, 115200, 57600, 38400, 19200, 14400, 9600, 4800, 1200
|
||||||
|
|||||||
@@ -15,6 +15,8 @@ dependencyVerification {
|
|||||||
'com.google.guava:guava:31.0.1-jre:guava-31.0.1-jre.jar:d5be94d65e87bd219fb3193ad1517baa55a3b88fc91d21cf735826ab5af087b9',
|
'com.google.guava:guava:31.0.1-jre:guava-31.0.1-jre.jar:d5be94d65e87bd219fb3193ad1517baa55a3b88fc91d21cf735826ab5af087b9',
|
||||||
'com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava:listenablefuture-9999.0-empty-to-avoid-conflict-with-guava.jar:b372a037d4230aa57fbeffdef30fd6123f9c0c2db85d0aced00c91b974f33f99',
|
'com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava:listenablefuture-9999.0-empty-to-avoid-conflict-with-guava.jar:b372a037d4230aa57fbeffdef30fd6123f9c0c2db85d0aced00c91b974f33f99',
|
||||||
'com.google.j2objc:j2objc-annotations:1.3:j2objc-annotations-1.3.jar:21af30c92267bd6122c0e0b4d20cccb6641a37eaf956c6540ec471d584e64a7b',
|
'com.google.j2objc:j2objc-annotations:1.3:j2objc-annotations-1.3.jar:21af30c92267bd6122c0e0b4d20cccb6641a37eaf956c6540ec471d584e64a7b',
|
||||||
|
'com.squareup.okhttp3:okhttp:3.12.13:okhttp-3.12.13.jar:508234e024ef7e270ab1a6d5b356f5b98e786511239ca986d684fd1e2cf7bc82',
|
||||||
|
'com.squareup.okio:okio:1.15.0:okio-1.15.0.jar:693fa319a7e8843300602b204023b7674f106ebcb577f2dd5807212b66118bd2',
|
||||||
'com.squareup:javapoet:1.13.0:javapoet-1.13.0.jar:4c7517e848a71b36d069d12bb3bf46a70fd4cda3105d822b0ed2e19c00b69291',
|
'com.squareup:javapoet:1.13.0:javapoet-1.13.0.jar:4c7517e848a71b36d069d12bb3bf46a70fd4cda3105d822b0ed2e19c00b69291',
|
||||||
'javax.inject:javax.inject:1:javax.inject-1.jar:91c77044a50c481636c32d916fd89c9118a72195390452c81065080f957de7ff',
|
'javax.inject:javax.inject:1:javax.inject-1.jar:91c77044a50c481636c32d916fd89c9118a72195390452c81065080f957de7ff',
|
||||||
'junit:junit:4.13.2:junit-4.13.2.jar:8e495b634469d64fb8acfa3495a065cbacc8a0fff55ce1e31007be4c16dc57d3',
|
'junit:junit:4.13.2:junit-4.13.2.jar:8e495b634469d64fb8acfa3495a065cbacc8a0fff55ce1e31007be4c16dc57d3',
|
||||||
@@ -28,8 +30,8 @@ dependencyVerification {
|
|||||||
'org.briarproject:obfs4proxy-windows:0.0.14-tor1:obfs4proxy-windows-0.0.14-tor1.jar:9dd122b31b3cd1616f168091dcdb01de049d1e052fe5c089b7627618a8a2694b',
|
'org.briarproject:obfs4proxy-windows:0.0.14-tor1:obfs4proxy-windows-0.0.14-tor1.jar:9dd122b31b3cd1616f168091dcdb01de049d1e052fe5c089b7627618a8a2694b',
|
||||||
'org.briarproject:snowflake-linux:2.3.1:snowflake-linux-2.3.1.jar:99ecf4546d8f79eb8408168c09380fec596558ac934554bf7d4247ea7ef2c9f3',
|
'org.briarproject:snowflake-linux:2.3.1:snowflake-linux-2.3.1.jar:99ecf4546d8f79eb8408168c09380fec596558ac934554bf7d4247ea7ef2c9f3',
|
||||||
'org.briarproject:snowflake-windows:2.3.1:snowflake-windows-2.3.1.jar:d011f1a72c00a221f56380c19aad8ff11db8c2bb1adb0784125572d80b4d275a',
|
'org.briarproject:snowflake-windows:2.3.1:snowflake-windows-2.3.1.jar:d011f1a72c00a221f56380c19aad8ff11db8c2bb1adb0784125572d80b4d275a',
|
||||||
'org.briarproject:tor-linux:0.4.5.14:tor-linux-0.4.5.14.jar:1844e54cf6df0c85cec219381a3364c759ae444a6b63f7558b757becb7d41d08',
|
'org.briarproject:tor-linux:0.4.7.13:tor-linux-0.4.7.13.jar:9819ee973cbcdc133f7d04aef9d4b957a35087627a790e532142d15412a9636f',
|
||||||
'org.briarproject:tor-windows:0.4.5.14:tor-windows-0.4.5.14.jar:d337afa1043f0cfa7e6e8c2473d682a5663a2c8052bb97a770450893c78c9b4f',
|
'org.briarproject:tor-windows:0.4.7.13:tor-windows-0.4.7.13.jar:853d2769665614e26703cbe02e43b218b064c04a0bcd120fdc459cda45bd2606',
|
||||||
'org.checkerframework:checker-compat-qual:2.5.5:checker-compat-qual-2.5.5.jar:11d134b245e9cacc474514d2d66b5b8618f8039a1465cdc55bbc0b34e0008b7a',
|
'org.checkerframework:checker-compat-qual:2.5.5:checker-compat-qual-2.5.5.jar:11d134b245e9cacc474514d2d66b5b8618f8039a1465cdc55bbc0b34e0008b7a',
|
||||||
'org.checkerframework:checker-qual:3.12.0:checker-qual-3.12.0.jar:ff10785ac2a357ec5de9c293cb982a2cbb605c0309ea4cc1cb9b9bc6dbe7f3cb',
|
'org.checkerframework:checker-qual:3.12.0:checker-qual-3.12.0.jar:ff10785ac2a357ec5de9c293cb982a2cbb605c0309ea4cc1cb9b9bc6dbe7f3cb',
|
||||||
'org.hamcrest:hamcrest-core:2.1:hamcrest-core-2.1.jar:e09109e54a289d88506b9bfec987ddd199f4217c9464132668351b9a4f00bee9',
|
'org.hamcrest:hamcrest-core:2.1:hamcrest-core-2.1.jar:e09109e54a289d88506b9bfec987ddd199f4217c9464132668351b9a4f00bee9',
|
||||||
|
|||||||
@@ -26,8 +26,8 @@ android {
|
|||||||
defaultConfig {
|
defaultConfig {
|
||||||
minSdkVersion 16
|
minSdkVersion 16
|
||||||
targetSdkVersion 31
|
targetSdkVersion 31
|
||||||
versionCode 10417
|
versionCode 10420
|
||||||
versionName "1.4.17"
|
versionName "1.4.20"
|
||||||
applicationId "org.briarproject.briar.android"
|
applicationId "org.briarproject.briar.android"
|
||||||
buildConfigField "String", "TorVersion", "\"$tor_version\""
|
buildConfigField "String", "TorVersion", "\"$tor_version\""
|
||||||
|
|
||||||
@@ -98,10 +98,15 @@ android {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation project(path: ':briar-core', configuration: 'default')
|
// In theory this dependency shouldn't be needed, but without it Android Studio's linter will
|
||||||
implementation project(path: ':bramble-core', configuration: 'default')
|
// complain about unresolved symbols for bramble-api test classes in briar-android tests,
|
||||||
|
// even though the bramble-api test classes are provided by the testImplementation dependency
|
||||||
|
// below and the compiler can find them
|
||||||
|
implementation project(':bramble-api')
|
||||||
|
|
||||||
|
implementation project(':bramble-core')
|
||||||
implementation project(':bramble-android')
|
implementation project(':bramble-android')
|
||||||
implementation 'org.briarproject:dont-kill-me-lib:0.2.5'
|
implementation project(':briar-core')
|
||||||
|
|
||||||
implementation 'androidx.fragment:fragment:1.3.4'
|
implementation 'androidx.fragment:fragment:1.3.4'
|
||||||
implementation 'androidx.preference:preference:1.1.1'
|
implementation 'androidx.preference:preference:1.1.1'
|
||||||
@@ -111,6 +116,9 @@ dependencies {
|
|||||||
implementation 'com.google.android.material:material:1.3.0'
|
implementation 'com.google.android.material:material:1.3.0'
|
||||||
implementation 'androidx.recyclerview:recyclerview-selection:1.1.0'
|
implementation 'androidx.recyclerview:recyclerview-selection:1.1.0'
|
||||||
|
|
||||||
|
implementation 'org.briarproject:dont-kill-me-lib:0.2.5'
|
||||||
|
implementation "com.squareup.okhttp3:okhttp:$okhttp_version"
|
||||||
|
implementation "org.jsoup:jsoup:$jsoup_version"
|
||||||
implementation 'info.guardianproject.panic:panic:1.0'
|
implementation 'info.guardianproject.panic:panic:1.0'
|
||||||
implementation 'de.hdodenhof:circleimageview:3.1.0'
|
implementation 'de.hdodenhof:circleimageview:3.1.0'
|
||||||
implementation 'com.google.zxing:core:3.3.3' // newer version need minSdk 24
|
implementation 'com.google.zxing:core:3.3.3' // newer version need minSdk 24
|
||||||
@@ -130,9 +138,10 @@ dependencies {
|
|||||||
|
|
||||||
compileOnly 'javax.annotation:jsr250-api:1.0'
|
compileOnly 'javax.annotation:jsr250-api:1.0'
|
||||||
|
|
||||||
def espressoVersion = '3.3.0'
|
|
||||||
testImplementation project(path: ':bramble-api', configuration: 'testOutput')
|
testImplementation project(path: ':bramble-api', configuration: 'testOutput')
|
||||||
testImplementation project(path: ':bramble-core', configuration: 'testOutput')
|
testImplementation project(path: ':bramble-core', configuration: 'testOutput')
|
||||||
|
|
||||||
|
def espressoVersion = '3.3.0'
|
||||||
testImplementation 'androidx.test:runner:1.4.0'
|
testImplementation 'androidx.test:runner:1.4.0'
|
||||||
testImplementation 'androidx.test.ext:junit:1.1.3'
|
testImplementation 'androidx.test.ext:junit:1.1.3'
|
||||||
testImplementation 'androidx.fragment:fragment-testing:1.4.0'
|
testImplementation 'androidx.fragment:fragment-testing:1.4.0'
|
||||||
@@ -144,18 +153,24 @@ dependencies {
|
|||||||
testImplementation "org.jmock:jmock:$jmock_version"
|
testImplementation "org.jmock:jmock:$jmock_version"
|
||||||
testImplementation "org.jmock:jmock-junit4:$jmock_version"
|
testImplementation "org.jmock:jmock-junit4:$jmock_version"
|
||||||
testImplementation "org.jmock:jmock-imposters:$jmock_version"
|
testImplementation "org.jmock:jmock-imposters:$jmock_version"
|
||||||
|
|
||||||
testAnnotationProcessor "com.google.dagger:dagger-compiler:$dagger_version"
|
testAnnotationProcessor "com.google.dagger:dagger-compiler:$dagger_version"
|
||||||
|
|
||||||
androidTestImplementation project(path: ':bramble-api', configuration: 'testOutput')
|
androidTestImplementation project(path: ':bramble-api', configuration: 'testOutput')
|
||||||
|
|
||||||
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
|
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
|
||||||
androidTestImplementation "androidx.test.espresso:espresso-core:$espressoVersion"
|
androidTestImplementation "androidx.test.espresso:espresso-core:$espressoVersion"
|
||||||
androidTestImplementation "androidx.test.espresso:espresso-contrib:$espressoVersion"
|
androidTestImplementation "androidx.test.espresso:espresso-contrib:$espressoVersion"
|
||||||
androidTestImplementation "androidx.test.espresso:espresso-intents:$espressoVersion"
|
androidTestImplementation "androidx.test.espresso:espresso-intents:$espressoVersion"
|
||||||
androidTestImplementation 'androidx.test:runner:1.3.0'
|
androidTestImplementation 'androidx.test:runner:1.3.0'
|
||||||
androidTestUtil 'androidx.test:orchestrator:1.3.0'
|
|
||||||
androidTestAnnotationProcessor "com.google.dagger:dagger-compiler:$dagger_version"
|
|
||||||
androidTestCompileOnly 'javax.annotation:jsr250-api:1.0'
|
|
||||||
androidTestImplementation "junit:junit:$junit_version"
|
androidTestImplementation "junit:junit:$junit_version"
|
||||||
|
|
||||||
|
androidTestUtil 'androidx.test:orchestrator:1.3.0'
|
||||||
|
|
||||||
|
androidTestAnnotationProcessor "com.google.dagger:dagger-compiler:$dagger_version"
|
||||||
|
|
||||||
|
androidTestCompileOnly 'javax.annotation:jsr250-api:1.0'
|
||||||
|
|
||||||
androidTestScreenshotImplementation 'tools.fastlane:screengrab:2.1.1'
|
androidTestScreenshotImplementation 'tools.fastlane:screengrab:2.1.1'
|
||||||
androidTestScreenshotImplementation 'com.jraska:falcon:2.2.0'
|
androidTestScreenshotImplementation 'com.jraska:falcon:2.2.0'
|
||||||
androidTestScreenshotImplementation 'androidx.test.uiautomator:uiautomator:2.2.0'
|
androidTestScreenshotImplementation 'androidx.test.uiautomator:uiautomator:2.2.0'
|
||||||
|
|||||||
@@ -52,7 +52,6 @@ import static org.briarproject.bramble.api.lifecycle.LifecycleManager.StartResul
|
|||||||
import static org.briarproject.bramble.api.lifecycle.LifecycleManager.StartResult.SUCCESS;
|
import static org.briarproject.bramble.api.lifecycle.LifecycleManager.StartResult.SUCCESS;
|
||||||
import static org.briarproject.bramble.util.AndroidUtils.isUiThread;
|
import static org.briarproject.bramble.util.AndroidUtils.isUiThread;
|
||||||
import static org.briarproject.briar.android.BriarApplication.ENTRY_ACTIVITY;
|
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.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_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;
|
||||||
@@ -141,11 +140,6 @@ public class BriarService extends Service {
|
|||||||
ongoingChannel.setLockscreenVisibility(VISIBILITY_SECRET);
|
ongoingChannel.setLockscreenVisibility(VISIBILITY_SECRET);
|
||||||
ongoingChannel.setShowBadge(false);
|
ongoingChannel.setShowBadge(false);
|
||||||
nm.createNotificationChannel(ongoingChannel);
|
nm.createNotificationChannel(ongoingChannel);
|
||||||
// Delete the unused channel previously used for startup
|
|
||||||
// failure notifications
|
|
||||||
// TODO: Remove this ID after a reasonable upgrade period
|
|
||||||
// (added 2021-07-12)
|
|
||||||
nm.deleteNotificationChannel(FAILURE_CHANNEL_ID);
|
|
||||||
}
|
}
|
||||||
Notification foregroundNotification =
|
Notification foregroundNotification =
|
||||||
notificationManager.getForegroundNotification();
|
notificationManager.getForegroundNotification();
|
||||||
|
|||||||
@@ -41,7 +41,6 @@ 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.logException;
|
import static org.briarproject.bramble.util.LogUtils.logException;
|
||||||
import static org.briarproject.bramble.util.LogUtils.now;
|
import static org.briarproject.bramble.util.LogUtils.now;
|
||||||
import static org.briarproject.briar.util.HtmlUtils.ARTICLE;
|
|
||||||
|
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
abstract class BaseViewModel extends DbViewModel implements EventListener {
|
abstract class BaseViewModel extends DbViewModel implements EventListener {
|
||||||
@@ -115,7 +114,7 @@ abstract class BaseViewModel extends DbViewModel implements EventListener {
|
|||||||
@DatabaseExecutor
|
@DatabaseExecutor
|
||||||
private String getPostText(Transaction txn, MessageId m)
|
private String getPostText(Transaction txn, MessageId m)
|
||||||
throws DbException {
|
throws DbException {
|
||||||
return HtmlUtils.clean(blogManager.getPostText(txn, m), ARTICLE);
|
return HtmlUtils.cleanArticle(blogManager.getPostText(txn, m));
|
||||||
}
|
}
|
||||||
|
|
||||||
LiveData<LiveResult<BlogPostItem>> loadBlogPost(GroupId g, MessageId m) {
|
LiveData<LiveResult<BlogPostItem>> loadBlogPost(GroupId g, MessageId m) {
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package org.briarproject.briar.android.blog;
|
|||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.text.Spanned;
|
import android.text.Spanned;
|
||||||
|
import android.text.util.Linkify;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
@@ -170,7 +171,12 @@ class BlogPostViewHolder extends RecyclerView.ViewHolder {
|
|||||||
// TODO make author clickable #624
|
// TODO make author clickable #624
|
||||||
|
|
||||||
text.setText(c.getComment());
|
text.setText(c.getComment());
|
||||||
if (fullText) text.setTextIsSelectable(true);
|
Linkify.addLinks(text, Linkify.WEB_URLS);
|
||||||
|
text.setMovementMethod(null);
|
||||||
|
if (fullText) {
|
||||||
|
text.setTextIsSelectable(true);
|
||||||
|
makeLinksClickable(text, listener::onLinkClick);
|
||||||
|
}
|
||||||
|
|
||||||
commentContainer.addView(v);
|
commentContainer.addView(v);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package org.briarproject.briar.android.blog;
|
package org.briarproject.briar.android.blog;
|
||||||
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.widget.Toast;
|
|
||||||
|
|
||||||
import org.briarproject.briar.R;
|
import org.briarproject.briar.R;
|
||||||
import org.briarproject.briar.android.activity.ActivityComponent;
|
import org.briarproject.briar.android.activity.ActivityComponent;
|
||||||
@@ -16,10 +15,6 @@ import androidx.annotation.Nullable;
|
|||||||
import androidx.fragment.app.FragmentManager;
|
import androidx.fragment.app.FragmentManager;
|
||||||
import androidx.lifecycle.ViewModelProvider;
|
import androidx.lifecycle.ViewModelProvider;
|
||||||
|
|
||||||
import static org.briarproject.briar.android.blog.RssFeedViewModel.ImportResult.EXISTS;
|
|
||||||
import static org.briarproject.briar.android.blog.RssFeedViewModel.ImportResult.FAILED;
|
|
||||||
import static org.briarproject.briar.android.blog.RssFeedViewModel.ImportResult.IMPORTED;
|
|
||||||
|
|
||||||
@MethodsNotNullByDefault
|
@MethodsNotNullByDefault
|
||||||
@ParametersNotNullByDefault
|
@ParametersNotNullByDefault
|
||||||
public class RssFeedActivity extends BriarActivity
|
public class RssFeedActivity extends BriarActivity
|
||||||
@@ -50,13 +45,13 @@ public class RssFeedActivity extends BriarActivity
|
|||||||
viewModel.getImportResult().observeEvent(this, this::onImportResult);
|
viewModel.getImportResult().observeEvent(this, this::onImportResult);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onImportResult(RssFeedViewModel.ImportResult result) {
|
private void onImportResult(boolean result) {
|
||||||
if (result == IMPORTED) {
|
if (result) {
|
||||||
FragmentManager fm = getSupportFragmentManager();
|
FragmentManager fm = getSupportFragmentManager();
|
||||||
if (fm.findFragmentByTag(RssFeedImportFragment.TAG) != null) {
|
if (fm.findFragmentByTag(RssFeedImportFragment.TAG) != null) {
|
||||||
onBackPressed();
|
onBackPressed();
|
||||||
}
|
}
|
||||||
} else if (result == FAILED) {
|
} else {
|
||||||
String url = viewModel.getUrlFailedImport();
|
String url = viewModel.getUrlFailedImport();
|
||||||
if (url == null) {
|
if (url == null) {
|
||||||
throw new AssertionError();
|
throw new AssertionError();
|
||||||
@@ -65,9 +60,6 @@ public class RssFeedActivity extends BriarActivity
|
|||||||
RssFeedImportFailedDialogFragment.newInstance(url);
|
RssFeedImportFailedDialogFragment.newInstance(url);
|
||||||
dialog.show(getSupportFragmentManager(),
|
dialog.show(getSupportFragmentManager(),
|
||||||
RssFeedImportFailedDialogFragment.TAG);
|
RssFeedImportFailedDialogFragment.TAG);
|
||||||
} else if (result == EXISTS) {
|
|
||||||
Toast.makeText(this, R.string.blogs_rss_feeds_import_exists,
|
|
||||||
Toast.LENGTH_LONG).show();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,8 +28,7 @@ class RssFeedAdapter extends ListAdapter<Feed, RssFeedAdapter.FeedViewHolder> {
|
|||||||
super(new DiffUtil.ItemCallback<Feed>() {
|
super(new DiffUtil.ItemCallback<Feed>() {
|
||||||
@Override
|
@Override
|
||||||
public boolean areItemsTheSame(Feed a, Feed b) {
|
public boolean areItemsTheSame(Feed a, Feed b) {
|
||||||
return a.getUrl().equals(b.getUrl()) &&
|
return a.getBlogId().equals(b.getBlogId()) &&
|
||||||
a.getBlogId().equals(b.getBlogId()) &&
|
|
||||||
a.getAdded() == b.getAdded();
|
a.getAdded() == b.getAdded();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -86,8 +85,8 @@ class RssFeedAdapter extends ListAdapter<Feed, RssFeedAdapter.FeedViewHolder> {
|
|||||||
delete.setOnClickListener(v -> listener.onDeleteClick(item));
|
delete.setOnClickListener(v -> listener.onDeleteClick(item));
|
||||||
|
|
||||||
// Author
|
// Author
|
||||||
if (item.getRssAuthor() != null) {
|
if (item.getProperties().getAuthor() != null) {
|
||||||
author.setText(item.getRssAuthor());
|
author.setText(item.getProperties().getAuthor());
|
||||||
author.setVisibility(VISIBLE);
|
author.setVisibility(VISIBLE);
|
||||||
authorLabel.setVisibility(VISIBLE);
|
authorLabel.setVisibility(VISIBLE);
|
||||||
} else {
|
} else {
|
||||||
@@ -100,8 +99,8 @@ class RssFeedAdapter extends ListAdapter<Feed, RssFeedAdapter.FeedViewHolder> {
|
|||||||
updated.setText(formatDate(ctx, item.getUpdated()));
|
updated.setText(formatDate(ctx, item.getUpdated()));
|
||||||
|
|
||||||
// Description
|
// Description
|
||||||
if (item.getDescription() != null) {
|
if (item.getProperties().getDescription() != null) {
|
||||||
description.setText(item.getDescription());
|
description.setText(item.getProperties().getDescription());
|
||||||
description.setVisibility(VISIBLE);
|
description.setVisibility(VISIBLE);
|
||||||
} else {
|
} else {
|
||||||
description.setVisibility(GONE);
|
description.setVisibility(GONE);
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ import org.briarproject.nullsafety.NotNullByDefault;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.MalformedURLException;
|
import java.net.MalformedURLException;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.util.Collections;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
@@ -38,13 +38,9 @@ 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.logException;
|
import static org.briarproject.bramble.util.LogUtils.logException;
|
||||||
import static org.briarproject.bramble.util.LogUtils.now;
|
import static org.briarproject.bramble.util.LogUtils.now;
|
||||||
import static org.briarproject.briar.android.blog.RssFeedViewModel.ImportResult.EXISTS;
|
|
||||||
import static org.briarproject.briar.android.blog.RssFeedViewModel.ImportResult.FAILED;
|
|
||||||
import static org.briarproject.briar.android.blog.RssFeedViewModel.ImportResult.IMPORTED;
|
|
||||||
|
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
class RssFeedViewModel extends DbViewModel {
|
class RssFeedViewModel extends DbViewModel {
|
||||||
enum ImportResult {IMPORTED, FAILED, EXISTS}
|
|
||||||
|
|
||||||
private static final Logger LOG =
|
private static final Logger LOG =
|
||||||
getLogger(RssFeedViewModel.class.getName());
|
getLogger(RssFeedViewModel.class.getName());
|
||||||
@@ -60,7 +56,7 @@ class RssFeedViewModel extends DbViewModel {
|
|||||||
private volatile String urlFailedImport = null;
|
private volatile String urlFailedImport = null;
|
||||||
private final MutableLiveData<Boolean> isImporting =
|
private final MutableLiveData<Boolean> isImporting =
|
||||||
new MutableLiveData<>(false);
|
new MutableLiveData<>(false);
|
||||||
private final MutableLiveEvent<ImportResult> importResult =
|
private final MutableLiveEvent<Boolean> importResult =
|
||||||
new MutableLiveEvent<>();
|
new MutableLiveEvent<>();
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
@@ -101,7 +97,6 @@ class RssFeedViewModel extends DbViewModel {
|
|||||||
private List<Feed> loadFeeds(Transaction txn) throws DbException {
|
private List<Feed> loadFeeds(Transaction txn) throws DbException {
|
||||||
long start = now();
|
long start = now();
|
||||||
List<Feed> feeds = feedManager.getFeeds(txn);
|
List<Feed> feeds = feedManager.getFeeds(txn);
|
||||||
Collections.sort(feeds);
|
|
||||||
logDuration(LOG, "Loading feeds", start);
|
logDuration(LOG, "Loading feeds", start);
|
||||||
return feeds;
|
return feeds;
|
||||||
}
|
}
|
||||||
@@ -125,7 +120,7 @@ class RssFeedViewModel extends DbViewModel {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
LiveEvent<ImportResult> getImportResult() {
|
LiveEvent<Boolean> getImportResult() {
|
||||||
return importResult;
|
return importResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -138,21 +133,23 @@ class RssFeedViewModel extends DbViewModel {
|
|||||||
urlFailedImport = null;
|
urlFailedImport = null;
|
||||||
ioExecutor.execute(() -> {
|
ioExecutor.execute(() -> {
|
||||||
try {
|
try {
|
||||||
if (exists(url)) {
|
|
||||||
importResult.postEvent(EXISTS);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Feed feed = feedManager.addFeed(url);
|
Feed feed = feedManager.addFeed(url);
|
||||||
List<Feed> updated = addListItem(getList(feeds), feed);
|
// Update the feed if it was already present
|
||||||
if (updated != null) {
|
List<Feed> feedList = getList(feeds);
|
||||||
Collections.sort(updated);
|
if (feedList == null) feedList = new ArrayList<>();
|
||||||
feeds.postValue(new LiveResult<>(updated));
|
List<Feed> updated = updateListItems(feedList,
|
||||||
|
f -> f.equals(feed), f -> feed);
|
||||||
|
// Add the feed if it wasn't already present
|
||||||
|
if (updated == null) {
|
||||||
|
feedList.add(feed);
|
||||||
|
updated = feedList;
|
||||||
}
|
}
|
||||||
importResult.postEvent(IMPORTED);
|
feeds.postValue(new LiveResult<>(updated));
|
||||||
|
importResult.postEvent(true);
|
||||||
} catch (DbException | IOException e) {
|
} catch (DbException | IOException e) {
|
||||||
logException(LOG, WARNING, e);
|
logException(LOG, WARNING, e);
|
||||||
urlFailedImport = url;
|
urlFailedImport = url;
|
||||||
importResult.postEvent(FAILED);
|
importResult.postEvent(false);
|
||||||
} finally {
|
} finally {
|
||||||
isImporting.postValue(false);
|
isImporting.postValue(false);
|
||||||
}
|
}
|
||||||
@@ -163,16 +160,4 @@ class RssFeedViewModel extends DbViewModel {
|
|||||||
String getUrlFailedImport() {
|
String getUrlFailedImport() {
|
||||||
return urlFailedImport;
|
return urlFailedImport;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean exists(String url) {
|
|
||||||
List<Feed> list = getList(feeds);
|
|
||||||
if (list != null) {
|
|
||||||
for (Feed feed : list) {
|
|
||||||
if (url.equals(feed.getUrl())) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,12 +3,14 @@ package org.briarproject.briar.android.contact.add.nearby;
|
|||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.identity.Author;
|
import org.briarproject.bramble.api.identity.Author;
|
||||||
|
import org.briarproject.bramble.api.qrcode.QrCodeClassifier.QrCodeType;
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
abstract class AddContactState {
|
abstract class AddContactState {
|
||||||
|
|
||||||
static class KeyAgreementListening extends AddContactState {
|
static class KeyAgreementListening extends AddContactState {
|
||||||
|
|
||||||
final Bitmap qrCode;
|
final Bitmap qrCode;
|
||||||
|
|
||||||
KeyAgreementListening(Bitmap qrCode) {
|
KeyAgreementListening(Bitmap qrCode) {
|
||||||
@@ -29,6 +31,7 @@ abstract class AddContactState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static class ContactExchangeFinished extends AddContactState {
|
static class ContactExchangeFinished extends AddContactState {
|
||||||
|
|
||||||
final ContactExchangeResult result;
|
final ContactExchangeResult result;
|
||||||
|
|
||||||
ContactExchangeFinished(ContactExchangeResult result) {
|
ContactExchangeFinished(ContactExchangeResult result) {
|
||||||
@@ -37,25 +40,34 @@ abstract class AddContactState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static class Failed extends AddContactState {
|
static class Failed extends AddContactState {
|
||||||
/**
|
|
||||||
* Non-null if failed due to the scanned QR code version.
|
|
||||||
* True if the app producing the code is too old.
|
|
||||||
* False if the scanning app is too old.
|
|
||||||
*/
|
|
||||||
@Nullable
|
|
||||||
final Boolean qrCodeTooOld;
|
|
||||||
|
|
||||||
Failed(@Nullable Boolean qrCodeTooOld) {
|
static class WrongQrCodeType extends Failed {
|
||||||
this.qrCodeTooOld = qrCodeTooOld;
|
|
||||||
|
final QrCodeType qrCodeType;
|
||||||
|
|
||||||
|
WrongQrCodeType(QrCodeType qrCodeType) {
|
||||||
|
this.qrCodeType = qrCodeType;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Failed() {
|
static class WrongQrCodeVersion extends Failed {
|
||||||
this(null);
|
|
||||||
|
/**
|
||||||
|
* True if the app producing the code is too old.
|
||||||
|
* False if the scanning app is too old.
|
||||||
|
*/
|
||||||
|
final boolean qrCodeTooOld;
|
||||||
|
|
||||||
|
WrongQrCodeVersion(boolean qrCodeTooOld) {
|
||||||
|
this.qrCodeTooOld = qrCodeTooOld;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract static class ContactExchangeResult {
|
abstract static class ContactExchangeResult {
|
||||||
|
|
||||||
static class Success extends ContactExchangeResult {
|
static class Success extends ContactExchangeResult {
|
||||||
|
|
||||||
final Author remoteAuthor;
|
final Author remoteAuthor;
|
||||||
|
|
||||||
Success(Author remoteAuthor) {
|
Success(Author remoteAuthor) {
|
||||||
@@ -64,6 +76,7 @@ abstract class AddContactState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static class Error extends ContactExchangeResult {
|
static class Error extends ContactExchangeResult {
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
final Author duplicateAuthor;
|
final Author duplicateAuthor;
|
||||||
|
|
||||||
|
|||||||
@@ -6,12 +6,15 @@ import android.view.MenuItem;
|
|||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.identity.Author;
|
import org.briarproject.bramble.api.identity.Author;
|
||||||
|
import org.briarproject.bramble.api.qrcode.QrCodeClassifier.QrCodeType;
|
||||||
import org.briarproject.briar.R;
|
import org.briarproject.briar.R;
|
||||||
import org.briarproject.briar.android.activity.ActivityComponent;
|
import org.briarproject.briar.android.activity.ActivityComponent;
|
||||||
import org.briarproject.briar.android.activity.BriarActivity;
|
import org.briarproject.briar.android.activity.BriarActivity;
|
||||||
import org.briarproject.briar.android.contact.add.nearby.AddContactState.ContactExchangeFinished;
|
import org.briarproject.briar.android.contact.add.nearby.AddContactState.ContactExchangeFinished;
|
||||||
import org.briarproject.briar.android.contact.add.nearby.AddContactState.ContactExchangeResult;
|
import org.briarproject.briar.android.contact.add.nearby.AddContactState.ContactExchangeResult;
|
||||||
import org.briarproject.briar.android.contact.add.nearby.AddContactState.Failed;
|
import org.briarproject.briar.android.contact.add.nearby.AddContactState.Failed;
|
||||||
|
import org.briarproject.briar.android.contact.add.nearby.AddContactState.Failed.WrongQrCodeType;
|
||||||
|
import org.briarproject.briar.android.contact.add.nearby.AddContactState.Failed.WrongQrCodeVersion;
|
||||||
import org.briarproject.briar.android.contact.add.nearby.AddNearbyContactViewModel.BluetoothDecision;
|
import org.briarproject.briar.android.contact.add.nearby.AddNearbyContactViewModel.BluetoothDecision;
|
||||||
import org.briarproject.briar.android.fragment.BaseFragment;
|
import org.briarproject.briar.android.fragment.BaseFragment;
|
||||||
import org.briarproject.briar.android.fragment.BaseFragment.BaseFragmentListener;
|
import org.briarproject.briar.android.fragment.BaseFragment.BaseFragmentListener;
|
||||||
@@ -34,6 +37,7 @@ import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP;
|
|||||||
import static android.widget.Toast.LENGTH_LONG;
|
import static android.widget.Toast.LENGTH_LONG;
|
||||||
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.qrcode.QrCodeClassifier.QrCodeType.MAILBOX;
|
||||||
import static org.briarproject.briar.android.contact.add.nearby.AddNearbyContactViewModel.BluetoothDecision.ACCEPTED;
|
import static org.briarproject.briar.android.contact.add.nearby.AddNearbyContactViewModel.BluetoothDecision.ACCEPTED;
|
||||||
import static org.briarproject.briar.android.contact.add.nearby.AddNearbyContactViewModel.BluetoothDecision.REFUSED;
|
import static org.briarproject.briar.android.contact.add.nearby.AddNearbyContactViewModel.BluetoothDecision.REFUSED;
|
||||||
|
|
||||||
@@ -141,9 +145,15 @@ public class AddNearbyContactActivity extends BriarActivity
|
|||||||
ContactExchangeResult result =
|
ContactExchangeResult result =
|
||||||
((ContactExchangeFinished) state).result;
|
((ContactExchangeFinished) state).result;
|
||||||
onContactExchangeResult(result);
|
onContactExchangeResult(result);
|
||||||
|
} else if (state instanceof WrongQrCodeType) {
|
||||||
|
QrCodeType qrCodeType = ((WrongQrCodeType) state).qrCodeType;
|
||||||
|
if (qrCodeType == MAILBOX) onMailboxQrCodeScanned();
|
||||||
|
else onWrongQrCodeType();
|
||||||
|
} else if (state instanceof WrongQrCodeVersion) {
|
||||||
|
boolean qrCodeTooOld = ((WrongQrCodeVersion) state).qrCodeTooOld;
|
||||||
|
onWrongQrCodeVersion(qrCodeTooOld);
|
||||||
} else if (state instanceof Failed) {
|
} else if (state instanceof Failed) {
|
||||||
Boolean qrCodeTooOld = ((Failed) state).qrCodeTooOld;
|
showErrorFragment();
|
||||||
onAddingContactFailed(qrCodeTooOld);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -170,15 +180,27 @@ public class AddNearbyContactActivity extends BriarActivity
|
|||||||
} else throw new AssertionError();
|
} else throw new AssertionError();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onAddingContactFailed(@Nullable Boolean qrCodeTooOld) {
|
private void onMailboxQrCodeScanned() {
|
||||||
if (qrCodeTooOld == null) {
|
String title = getString(R.string.qr_code_invalid);
|
||||||
showErrorFragment();
|
String msg = getString(R.string.mailbox_qr_code_for_contact);
|
||||||
} else {
|
showNextFragment(
|
||||||
String msg;
|
AddNearbyContactErrorFragment.newInstance(title, msg, false));
|
||||||
if (qrCodeTooOld) msg = getString(R.string.qr_code_too_old_1);
|
}
|
||||||
else msg = getString(R.string.qr_code_too_new_1);
|
|
||||||
showNextFragment(AddNearbyContactErrorFragment.newInstance(msg));
|
private void onWrongQrCodeType() {
|
||||||
}
|
String title = getString(R.string.qr_code_invalid);
|
||||||
|
String msg = getString(R.string.qr_code_format_unknown);
|
||||||
|
showNextFragment(
|
||||||
|
AddNearbyContactErrorFragment.newInstance(title, msg, false));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onWrongQrCodeVersion(boolean qrCodeTooOld) {
|
||||||
|
String title = getString(R.string.qr_code_invalid);
|
||||||
|
String msg;
|
||||||
|
if (qrCodeTooOld) msg = getString(R.string.qr_code_too_old_1);
|
||||||
|
else msg = getString(R.string.qr_code_too_new_1);
|
||||||
|
showNextFragment(
|
||||||
|
AddNearbyContactErrorFragment.newInstance(title, msg, false));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void showErrorFragment() {
|
private void showErrorFragment() {
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import androidx.fragment.app.FragmentActivity;
|
|||||||
import androidx.lifecycle.ViewModelProvider;
|
import androidx.lifecycle.ViewModelProvider;
|
||||||
|
|
||||||
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP;
|
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP;
|
||||||
|
import static android.view.View.GONE;
|
||||||
import static org.briarproject.briar.android.util.UiUtils.hideViewOnSmallScreen;
|
import static org.briarproject.briar.android.util.UiUtils.hideViewOnSmallScreen;
|
||||||
import static org.briarproject.briar.android.util.UiUtils.onSingleLinkClick;
|
import static org.briarproject.briar.android.util.UiUtils.onSingleLinkClick;
|
||||||
|
|
||||||
@@ -31,17 +32,22 @@ public class AddNearbyContactErrorFragment extends BaseFragment {
|
|||||||
|
|
||||||
public static final String TAG =
|
public static final String TAG =
|
||||||
AddNearbyContactErrorFragment.class.getName();
|
AddNearbyContactErrorFragment.class.getName();
|
||||||
private static final String ERROR_MSG = "errorMessage";
|
private static final String ARG_TITLE = "title";
|
||||||
|
private static final String ARG_ERROR_MSG = "message";
|
||||||
|
private static final String ARG_FEEDBACK = "feedback";
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
ViewModelProvider.Factory viewModelFactory;
|
ViewModelProvider.Factory viewModelFactory;
|
||||||
|
|
||||||
private AddNearbyContactViewModel viewModel;
|
private AddNearbyContactViewModel viewModel;
|
||||||
|
|
||||||
public static AddNearbyContactErrorFragment newInstance(String errorMsg) {
|
public static AddNearbyContactErrorFragment newInstance(String title,
|
||||||
|
String errorMessage, boolean feedback) {
|
||||||
AddNearbyContactErrorFragment f = new AddNearbyContactErrorFragment();
|
AddNearbyContactErrorFragment f = new AddNearbyContactErrorFragment();
|
||||||
Bundle args = new Bundle();
|
Bundle args = new Bundle();
|
||||||
args.putString(ERROR_MSG, errorMsg);
|
args.putString(ARG_TITLE, title);
|
||||||
|
args.putString(ARG_ERROR_MSG, errorMessage);
|
||||||
|
args.putBoolean(ARG_FEEDBACK, feedback);
|
||||||
f.setArguments(args);
|
f.setArguments(args);
|
||||||
return f;
|
return f;
|
||||||
}
|
}
|
||||||
@@ -66,19 +72,32 @@ public class AddNearbyContactErrorFragment extends BaseFragment {
|
|||||||
View v = inflater.inflate(R.layout.fragment_error_contact_exchange,
|
View v = inflater.inflate(R.layout.fragment_error_contact_exchange,
|
||||||
container, false);
|
container, false);
|
||||||
|
|
||||||
// set optional error message
|
String title = null, errorMessage = null;
|
||||||
TextView explanation = v.findViewById(R.id.errorMessage);
|
boolean feedback = true;
|
||||||
Bundle args = getArguments();
|
Bundle args = getArguments();
|
||||||
String errorMessage = args == null ? null : args.getString(ERROR_MSG);
|
if (args != null) {
|
||||||
if (errorMessage == null) {
|
title = args.getString(ARG_TITLE);
|
||||||
explanation.setText(getString(R.string.add_contact_error_two_way));
|
errorMessage = args.getString(ARG_ERROR_MSG);
|
||||||
} else {
|
feedback = args.getBoolean(ARG_FEEDBACK, true);
|
||||||
explanation.setText(args.getString(ERROR_MSG));
|
}
|
||||||
|
|
||||||
|
if (title != null) {
|
||||||
|
TextView titleView = v.findViewById(R.id.errorTitle);
|
||||||
|
titleView.setText(title);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (errorMessage != null) {
|
||||||
|
TextView messageView = v.findViewById(R.id.errorMessage);
|
||||||
|
messageView.setText(errorMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
// make feedback link clickable
|
|
||||||
TextView sendFeedback = v.findViewById(R.id.sendFeedback);
|
TextView sendFeedback = v.findViewById(R.id.sendFeedback);
|
||||||
onSingleLinkClick(sendFeedback, this::triggerFeedback);
|
if (feedback) {
|
||||||
|
// make feedback link clickable
|
||||||
|
onSingleLinkClick(sendFeedback, this::triggerFeedback);
|
||||||
|
} else {
|
||||||
|
sendFeedback.setVisibility(GONE);
|
||||||
|
}
|
||||||
|
|
||||||
// buttons
|
// buttons
|
||||||
Button tryAgain = v.findViewById(R.id.tryAgainButton);
|
Button tryAgain = v.findViewById(R.id.tryAgainButton);
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ import org.briarproject.bramble.api.plugin.PluginManager;
|
|||||||
import org.briarproject.bramble.api.plugin.TransportId;
|
import org.briarproject.bramble.api.plugin.TransportId;
|
||||||
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
|
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
|
||||||
import org.briarproject.bramble.api.plugin.event.TransportStateEvent;
|
import org.briarproject.bramble.api.plugin.event.TransportStateEvent;
|
||||||
|
import org.briarproject.bramble.api.qrcode.WrongQrCodeTypeException;
|
||||||
import org.briarproject.bramble.api.system.AndroidExecutor;
|
import org.briarproject.bramble.api.system.AndroidExecutor;
|
||||||
import org.briarproject.bramble.plugin.bluetooth.BluetoothPlugin;
|
import org.briarproject.bramble.plugin.bluetooth.BluetoothPlugin;
|
||||||
import org.briarproject.briar.R;
|
import org.briarproject.briar.R;
|
||||||
@@ -50,9 +51,13 @@ import org.briarproject.briar.android.contact.add.nearby.AddContactState.Contact
|
|||||||
import org.briarproject.briar.android.contact.add.nearby.AddContactState.ContactExchangeResult.Error;
|
import org.briarproject.briar.android.contact.add.nearby.AddContactState.ContactExchangeResult.Error;
|
||||||
import org.briarproject.briar.android.contact.add.nearby.AddContactState.ContactExchangeResult.Success;
|
import org.briarproject.briar.android.contact.add.nearby.AddContactState.ContactExchangeResult.Success;
|
||||||
import org.briarproject.briar.android.contact.add.nearby.AddContactState.ContactExchangeStarted;
|
import org.briarproject.briar.android.contact.add.nearby.AddContactState.ContactExchangeStarted;
|
||||||
|
import org.briarproject.briar.android.contact.add.nearby.AddContactState.Failed;
|
||||||
|
import org.briarproject.briar.android.contact.add.nearby.AddContactState.Failed.WrongQrCodeType;
|
||||||
|
import org.briarproject.briar.android.contact.add.nearby.AddContactState.Failed.WrongQrCodeVersion;
|
||||||
import org.briarproject.briar.android.contact.add.nearby.AddContactState.KeyAgreementListening;
|
import org.briarproject.briar.android.contact.add.nearby.AddContactState.KeyAgreementListening;
|
||||||
import org.briarproject.briar.android.contact.add.nearby.AddContactState.KeyAgreementStarted;
|
import org.briarproject.briar.android.contact.add.nearby.AddContactState.KeyAgreementStarted;
|
||||||
import org.briarproject.briar.android.contact.add.nearby.AddContactState.KeyAgreementWaiting;
|
import org.briarproject.briar.android.contact.add.nearby.AddContactState.KeyAgreementWaiting;
|
||||||
|
import org.briarproject.briar.android.contact.add.nearby.AddContactState.QrCodeScanned;
|
||||||
import org.briarproject.briar.android.qrcode.QrCodeDecoder;
|
import org.briarproject.briar.android.qrcode.QrCodeDecoder;
|
||||||
import org.briarproject.briar.android.qrcode.QrCodeUtils;
|
import org.briarproject.briar.android.qrcode.QrCodeUtils;
|
||||||
import org.briarproject.briar.android.viewmodel.LiveEvent;
|
import org.briarproject.briar.android.viewmodel.LiveEvent;
|
||||||
@@ -60,7 +65,6 @@ import org.briarproject.briar.android.viewmodel.MutableLiveEvent;
|
|||||||
import org.briarproject.nullsafety.NotNullByDefault;
|
import org.briarproject.nullsafety.NotNullByDefault;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.charset.Charset;
|
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
@@ -85,6 +89,7 @@ import static org.briarproject.bramble.api.plugin.Plugin.State.DISABLED;
|
|||||||
import static org.briarproject.bramble.api.plugin.Plugin.State.INACTIVE;
|
import static org.briarproject.bramble.api.plugin.Plugin.State.INACTIVE;
|
||||||
import static org.briarproject.bramble.api.plugin.Plugin.State.STARTING_STOPPING;
|
import static org.briarproject.bramble.api.plugin.Plugin.State.STARTING_STOPPING;
|
||||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
import static org.briarproject.bramble.util.LogUtils.logException;
|
||||||
|
import static org.briarproject.bramble.util.StringUtils.ISO_8859_1;
|
||||||
import static org.briarproject.briar.android.contact.add.nearby.AddNearbyContactPermissionManager.areEssentialPermissionsGranted;
|
import static org.briarproject.briar.android.contact.add.nearby.AddNearbyContactPermissionManager.areEssentialPermissionsGranted;
|
||||||
import static org.briarproject.briar.android.contact.add.nearby.AddNearbyContactViewModel.BluetoothDecision.NO_ADAPTER;
|
import static org.briarproject.briar.android.contact.add.nearby.AddNearbyContactViewModel.BluetoothDecision.NO_ADAPTER;
|
||||||
import static org.briarproject.briar.android.contact.add.nearby.AddNearbyContactViewModel.BluetoothDecision.REFUSED;
|
import static org.briarproject.briar.android.contact.add.nearby.AddNearbyContactViewModel.BluetoothDecision.REFUSED;
|
||||||
@@ -126,9 +131,6 @@ class AddNearbyContactViewModel extends AndroidViewModel
|
|||||||
REFUSED
|
REFUSED
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("CharsetObjectCanBeUsed") // Requires minSdkVersion >= 19
|
|
||||||
private static final Charset ISO_8859_1 = Charset.forName("ISO-8859-1");
|
|
||||||
|
|
||||||
private final EventBus eventBus;
|
private final EventBus eventBus;
|
||||||
private final AndroidExecutor androidExecutor;
|
private final AndroidExecutor androidExecutor;
|
||||||
private final Executor ioExecutor;
|
private final Executor ioExecutor;
|
||||||
@@ -376,11 +378,11 @@ class AddNearbyContactViewModel extends AndroidViewModel
|
|||||||
} else if (e instanceof KeyAgreementAbortedEvent) {
|
} else if (e instanceof KeyAgreementAbortedEvent) {
|
||||||
LOG.info("KeyAgreementAbortedEvent received");
|
LOG.info("KeyAgreementAbortedEvent received");
|
||||||
resetPayloadFlags();
|
resetPayloadFlags();
|
||||||
state.setValue(new AddContactState.Failed());
|
state.setValue(new Failed());
|
||||||
} else if (e instanceof KeyAgreementFailedEvent) {
|
} else if (e instanceof KeyAgreementFailedEvent) {
|
||||||
LOG.info("KeyAgreementFailedEvent received");
|
LOG.info("KeyAgreementFailedEvent received");
|
||||||
resetPayloadFlags();
|
resetPayloadFlags();
|
||||||
state.setValue(new AddContactState.Failed());
|
state.setValue(new Failed());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -446,22 +448,22 @@ class AddNearbyContactViewModel extends AndroidViewModel
|
|||||||
// Ignore results until the KeyAgreementTask is ready
|
// Ignore results until the KeyAgreementTask is ready
|
||||||
if (!gotLocalPayload || gotRemotePayload || currentTask == null) return;
|
if (!gotLocalPayload || gotRemotePayload || currentTask == null) return;
|
||||||
try {
|
try {
|
||||||
byte[] payloadBytes = result.getText().getBytes(ISO_8859_1);
|
Payload remotePayload = payloadParser.parse(result.getText());
|
||||||
if (LOG.isLoggable(INFO))
|
|
||||||
LOG.info("Remote payload is " + payloadBytes.length + " bytes");
|
|
||||||
Payload remotePayload = payloadParser.parse(payloadBytes);
|
|
||||||
gotRemotePayload = true;
|
gotRemotePayload = true;
|
||||||
currentTask.connectAndRunProtocol(remotePayload);
|
currentTask.connectAndRunProtocol(remotePayload);
|
||||||
state.postValue(new AddContactState.QrCodeScanned());
|
state.postValue(new QrCodeScanned());
|
||||||
|
} catch (WrongQrCodeTypeException e) {
|
||||||
|
resetPayloadFlags();
|
||||||
|
state.postValue(new WrongQrCodeType(e.getQrCodeType()));
|
||||||
} catch (UnsupportedVersionException e) {
|
} catch (UnsupportedVersionException e) {
|
||||||
resetPayloadFlags();
|
resetPayloadFlags();
|
||||||
state.postValue(new AddContactState.Failed(e.isTooOld()));
|
state.postValue(new WrongQrCodeVersion(e.isTooOld()));
|
||||||
} catch (IOException | IllegalArgumentException e) {
|
} catch (IOException | IllegalArgumentException e) {
|
||||||
LOG.log(WARNING, "QR Code Invalid", e);
|
LOG.log(WARNING, "QR Code Invalid", e);
|
||||||
androidExecutor.runOnUiThread(() -> Toast.makeText(getApplication(),
|
androidExecutor.runOnUiThread(() -> Toast.makeText(getApplication(),
|
||||||
R.string.qr_code_invalid, LENGTH_LONG).show());
|
R.string.qr_code_invalid, LENGTH_LONG).show());
|
||||||
resetPayloadFlags();
|
resetPayloadFlags();
|
||||||
state.postValue(new AddContactState.Failed());
|
state.postValue(new Failed());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import java.util.ArrayList;
|
|||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
public abstract class BaseContactSelectorAdapter<I extends SelectableContactItem, H extends ContactItemViewHolder<I>>
|
public abstract class BaseContactSelectorAdapter<I extends BaseSelectableContactItem, H extends ContactItemViewHolder<I>>
|
||||||
extends BaseContactListAdapter<I, H> {
|
extends BaseContactListAdapter<I, H> {
|
||||||
|
|
||||||
public BaseContactSelectorAdapter(Context context, Class<I> c,
|
public BaseContactSelectorAdapter(Context context, Class<I> c,
|
||||||
@@ -24,7 +24,7 @@ public abstract class BaseContactSelectorAdapter<I extends SelectableContactItem
|
|||||||
Collection<ContactId> selected = new ArrayList<>();
|
Collection<ContactId> selected = new ArrayList<>();
|
||||||
|
|
||||||
for (int i = 0; i < items.size(); i++) {
|
for (int i = 0; i < items.size(); i++) {
|
||||||
SelectableContactItem item = items.get(i);
|
BaseSelectableContactItem item = items.get(i);
|
||||||
if (item.isSelected()) selected.add(item.getContact().getId());
|
if (item.isSelected()) selected.add(item.getContact().getId());
|
||||||
}
|
}
|
||||||
return selected;
|
return selected;
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ import static org.briarproject.briar.android.contactselection.ContactSelectorAct
|
|||||||
|
|
||||||
@MethodsNotNullByDefault
|
@MethodsNotNullByDefault
|
||||||
@ParametersNotNullByDefault
|
@ParametersNotNullByDefault
|
||||||
public abstract class BaseContactSelectorFragment<I extends SelectableContactItem, A extends BaseContactSelectorAdapter<I, ? extends ContactItemViewHolder<I>>>
|
public abstract class BaseContactSelectorFragment<I extends BaseSelectableContactItem, A extends BaseContactSelectorAdapter<I, ? extends ContactItemViewHolder<I>>>
|
||||||
extends BaseFragment
|
extends BaseFragment
|
||||||
implements OnContactClickListener<I> {
|
implements OnContactClickListener<I> {
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ import static org.briarproject.briar.android.util.UiUtils.GREY_OUT;
|
|||||||
|
|
||||||
@UiThread
|
@UiThread
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
public class BaseSelectableContactHolder<I extends SelectableContactItem>
|
public abstract class BaseSelectableContactHolder<I extends BaseSelectableContactItem>
|
||||||
extends ContactItemViewHolder<I> {
|
extends ContactItemViewHolder<I> {
|
||||||
|
|
||||||
private final CheckBox checkBox;
|
private final CheckBox checkBox;
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
package org.briarproject.briar.android.contactselection;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.contact.Contact;
|
||||||
|
import org.briarproject.briar.android.contact.ContactItem;
|
||||||
|
import org.briarproject.briar.api.identity.AuthorInfo;
|
||||||
|
import org.briarproject.nullsafety.NotNullByDefault;
|
||||||
|
|
||||||
|
import javax.annotation.concurrent.NotThreadSafe;
|
||||||
|
|
||||||
|
@NotThreadSafe
|
||||||
|
@NotNullByDefault
|
||||||
|
public abstract class BaseSelectableContactItem extends ContactItem {
|
||||||
|
|
||||||
|
private boolean selected;
|
||||||
|
|
||||||
|
public BaseSelectableContactItem(Contact contact, AuthorInfo authorInfo,
|
||||||
|
boolean selected) {
|
||||||
|
super(contact, authorInfo);
|
||||||
|
this.selected = selected;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean isSelected() {
|
||||||
|
return selected;
|
||||||
|
}
|
||||||
|
|
||||||
|
void toggleSelected() {
|
||||||
|
selected = !selected;
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract boolean isDisabled();
|
||||||
|
|
||||||
|
}
|
||||||
@@ -10,7 +10,7 @@ import org.briarproject.nullsafety.NotNullByDefault;
|
|||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
public interface ContactSelectorController<I extends SelectableContactItem>
|
public interface ContactSelectorController<I extends BaseSelectableContactItem>
|
||||||
extends DbController {
|
extends DbController {
|
||||||
|
|
||||||
void loadContacts(GroupId g, Collection<ContactId> selection,
|
void loadContacts(GroupId g, Collection<ContactId> selection,
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import org.briarproject.briar.android.controller.DbControllerImpl;
|
|||||||
import org.briarproject.briar.android.controller.handler.ResultExceptionHandler;
|
import org.briarproject.briar.android.controller.handler.ResultExceptionHandler;
|
||||||
import org.briarproject.briar.api.identity.AuthorInfo;
|
import org.briarproject.briar.api.identity.AuthorInfo;
|
||||||
import org.briarproject.briar.api.identity.AuthorManager;
|
import org.briarproject.briar.api.identity.AuthorManager;
|
||||||
|
import org.briarproject.briar.api.sharing.SharingManager.SharingStatus;
|
||||||
import org.briarproject.nullsafety.NotNullByDefault;
|
import org.briarproject.nullsafety.NotNullByDefault;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@@ -53,10 +54,8 @@ public abstract class ContactSelectorControllerImpl
|
|||||||
AuthorInfo authorInfo = authorManager.getAuthorInfo(c);
|
AuthorInfo authorInfo = authorManager.getAuthorInfo(c);
|
||||||
// was this contact already selected?
|
// was this contact already selected?
|
||||||
boolean selected = selection.contains(c.getId());
|
boolean selected = selection.contains(c.getId());
|
||||||
// can this contact be selected?
|
|
||||||
boolean disabled = isDisabled(g, c);
|
|
||||||
contacts.add(new SelectableContactItem(c, authorInfo,
|
contacts.add(new SelectableContactItem(c, authorInfo,
|
||||||
selected, disabled));
|
selected, getSharingStatus(g, c)));
|
||||||
}
|
}
|
||||||
handler.onResult(contacts);
|
handler.onResult(contacts);
|
||||||
} catch (DbException e) {
|
} catch (DbException e) {
|
||||||
@@ -67,7 +66,7 @@ public abstract class ContactSelectorControllerImpl
|
|||||||
}
|
}
|
||||||
|
|
||||||
@DatabaseExecutor
|
@DatabaseExecutor
|
||||||
protected abstract boolean isDisabled(GroupId g, Contact c)
|
protected abstract SharingStatus getSharingStatus(GroupId g, Contact c)
|
||||||
throws DbException;
|
throws DbException;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,15 +2,22 @@ package org.briarproject.briar.android.contactselection;
|
|||||||
|
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
|
||||||
|
import org.briarproject.briar.R;
|
||||||
import org.briarproject.briar.android.contact.OnContactClickListener;
|
import org.briarproject.briar.android.contact.OnContactClickListener;
|
||||||
import org.briarproject.nullsafety.NotNullByDefault;
|
import org.briarproject.nullsafety.NotNullByDefault;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
import androidx.annotation.StringRes;
|
||||||
import androidx.annotation.UiThread;
|
import androidx.annotation.UiThread;
|
||||||
|
|
||||||
import static android.view.View.GONE;
|
import static android.view.View.GONE;
|
||||||
import static android.view.View.VISIBLE;
|
import static android.view.View.VISIBLE;
|
||||||
|
import static org.briarproject.briar.api.sharing.SharingManager.SharingStatus.ERROR;
|
||||||
|
import static org.briarproject.briar.api.sharing.SharingManager.SharingStatus.INVITE_RECEIVED;
|
||||||
|
import static org.briarproject.briar.api.sharing.SharingManager.SharingStatus.INVITE_SENT;
|
||||||
|
import static org.briarproject.briar.api.sharing.SharingManager.SharingStatus.NOT_SUPPORTED;
|
||||||
|
import static org.briarproject.briar.api.sharing.SharingManager.SharingStatus.SHARING;
|
||||||
|
|
||||||
@UiThread
|
@UiThread
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
@@ -27,6 +34,19 @@ class SelectableContactHolder
|
|||||||
super.bind(item, listener);
|
super.bind(item, listener);
|
||||||
|
|
||||||
if (item.isDisabled()) {
|
if (item.isDisabled()) {
|
||||||
|
@StringRes int strRes;
|
||||||
|
if (item.getSharingStatus() == SHARING) {
|
||||||
|
strRes = R.string.forum_invitation_already_sharing;
|
||||||
|
} else if (item.getSharingStatus() == INVITE_SENT) {
|
||||||
|
strRes = R.string.forum_invitation_already_invited;
|
||||||
|
} else if (item.getSharingStatus() == INVITE_RECEIVED) {
|
||||||
|
strRes = R.string.forum_invitation_invite_received;
|
||||||
|
} else if (item.getSharingStatus() == NOT_SUPPORTED) {
|
||||||
|
strRes = R.string.forum_invitation_not_supported;
|
||||||
|
} else if (item.getSharingStatus() == ERROR) {
|
||||||
|
strRes = R.string.forum_invitation_error;
|
||||||
|
} else throw new AssertionError("Unhandled SharingStatus");
|
||||||
|
info.setText(strRes);
|
||||||
info.setVisibility(VISIBLE);
|
info.setVisibility(VISIBLE);
|
||||||
} else {
|
} else {
|
||||||
info.setVisibility(GONE);
|
info.setVisibility(GONE);
|
||||||
|
|||||||
@@ -1,36 +1,33 @@
|
|||||||
package org.briarproject.briar.android.contactselection;
|
package org.briarproject.briar.android.contactselection;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.contact.Contact;
|
import org.briarproject.bramble.api.contact.Contact;
|
||||||
import org.briarproject.briar.android.contact.ContactItem;
|
|
||||||
import org.briarproject.briar.api.identity.AuthorInfo;
|
import org.briarproject.briar.api.identity.AuthorInfo;
|
||||||
|
import org.briarproject.briar.api.sharing.SharingManager.SharingStatus;
|
||||||
import org.briarproject.nullsafety.NotNullByDefault;
|
import org.briarproject.nullsafety.NotNullByDefault;
|
||||||
|
|
||||||
import javax.annotation.concurrent.NotThreadSafe;
|
import javax.annotation.concurrent.NotThreadSafe;
|
||||||
|
|
||||||
|
import static org.briarproject.briar.api.sharing.SharingManager.SharingStatus.SHAREABLE;
|
||||||
|
|
||||||
@NotThreadSafe
|
@NotThreadSafe
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
public class SelectableContactItem extends ContactItem {
|
public class SelectableContactItem extends BaseSelectableContactItem {
|
||||||
|
|
||||||
private boolean selected;
|
private final SharingStatus sharingStatus;
|
||||||
private final boolean disabled;
|
|
||||||
|
|
||||||
public SelectableContactItem(Contact contact, AuthorInfo authorInfo,
|
public SelectableContactItem(Contact contact, AuthorInfo authorInfo,
|
||||||
boolean selected, boolean disabled) {
|
boolean selected, SharingStatus sharingStatus) {
|
||||||
super(contact, authorInfo);
|
super(contact, authorInfo, selected);
|
||||||
this.selected = selected;
|
this.sharingStatus = sharingStatus;
|
||||||
this.disabled = disabled;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean isSelected() {
|
public SharingStatus getSharingStatus() {
|
||||||
return selected;
|
return sharingStatus;
|
||||||
}
|
|
||||||
|
|
||||||
void toggleSelected() {
|
|
||||||
selected = !selected;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public boolean isDisabled() {
|
public boolean isDisabled() {
|
||||||
return disabled;
|
return sharingStatus != SHAREABLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -66,6 +66,7 @@ import org.briarproject.briar.android.view.TextAttachmentController.AttachmentLi
|
|||||||
import org.briarproject.briar.android.view.TextInputView;
|
import org.briarproject.briar.android.view.TextInputView;
|
||||||
import org.briarproject.briar.android.view.TextSendController;
|
import org.briarproject.briar.android.view.TextSendController;
|
||||||
import org.briarproject.briar.android.view.TextSendController.SendState;
|
import org.briarproject.briar.android.view.TextSendController.SendState;
|
||||||
|
import org.briarproject.briar.android.widget.LinkDialogFragment;
|
||||||
import org.briarproject.briar.api.android.AndroidNotificationManager;
|
import org.briarproject.briar.api.android.AndroidNotificationManager;
|
||||||
import org.briarproject.briar.api.attachment.AttachmentHeader;
|
import org.briarproject.briar.api.attachment.AttachmentHeader;
|
||||||
import org.briarproject.briar.api.autodelete.event.ConversationMessagesDeletedEvent;
|
import org.briarproject.briar.api.autodelete.event.ConversationMessagesDeletedEvent;
|
||||||
@@ -476,6 +477,12 @@ public class ConversationActivity extends BriarActivity
|
|||||||
actionMode = null;
|
actionMode = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onLinkClick(String url) {
|
||||||
|
LinkDialogFragment f = LinkDialogFragment.newInstance(url);
|
||||||
|
f.show(getSupportFragmentManager(), f.getUniqueTag());
|
||||||
|
}
|
||||||
|
|
||||||
private void addSelectionTracker() {
|
private void addSelectionTracker() {
|
||||||
RecyclerView recyclerView = list.getRecyclerView();
|
RecyclerView recyclerView = list.getRecyclerView();
|
||||||
if (recyclerView.getAdapter() != adapter)
|
if (recyclerView.getAdapter() != adapter)
|
||||||
@@ -925,6 +932,7 @@ public class ConversationActivity extends BriarActivity
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void removeContact() {
|
private void removeContact() {
|
||||||
|
list.showProgressBar();
|
||||||
runOnDbThread(() -> {
|
runOnDbThread(() -> {
|
||||||
try {
|
try {
|
||||||
contactManager.removeContact(contactId);
|
contactManager.removeContact(contactId);
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package org.briarproject.briar.android.conversation;
|
package org.briarproject.briar.android.conversation;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.text.util.Linkify;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
@@ -19,6 +20,7 @@ import static android.view.View.VISIBLE;
|
|||||||
import static org.briarproject.bramble.util.StringUtils.trim;
|
import static org.briarproject.bramble.util.StringUtils.trim;
|
||||||
import static org.briarproject.briar.android.util.UiUtils.formatDate;
|
import static org.briarproject.briar.android.util.UiUtils.formatDate;
|
||||||
import static org.briarproject.briar.android.util.UiUtils.formatDuration;
|
import static org.briarproject.briar.android.util.UiUtils.formatDuration;
|
||||||
|
import static org.briarproject.briar.android.util.UiUtils.makeLinksClickable;
|
||||||
import static org.briarproject.briar.api.autodelete.AutoDeleteConstants.NO_AUTO_DELETE_TIMER;
|
import static org.briarproject.briar.api.autodelete.AutoDeleteConstants.NO_AUTO_DELETE_TIMER;
|
||||||
|
|
||||||
@UiThread
|
@UiThread
|
||||||
@@ -58,6 +60,8 @@ abstract class ConversationItemViewHolder extends ViewHolder {
|
|||||||
|
|
||||||
if (item.getText() != null) {
|
if (item.getText() != null) {
|
||||||
text.setText(trim(item.getText()));
|
text.setText(trim(item.getText()));
|
||||||
|
Linkify.addLinks(text, Linkify.WEB_URLS);
|
||||||
|
makeLinksClickable(text, listener::onLinkClick);
|
||||||
}
|
}
|
||||||
|
|
||||||
long timestamp = item.getTime();
|
long timestamp = item.getTime();
|
||||||
|
|||||||
@@ -20,4 +20,6 @@ interface ConversationListener {
|
|||||||
|
|
||||||
void onAutoDeleteTimerNoticeClicked();
|
void onAutoDeleteTimerNoticeClicked();
|
||||||
|
|
||||||
|
void onLinkClick(String url);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package org.briarproject.briar.android.conversation;
|
package org.briarproject.briar.android.conversation;
|
||||||
|
|
||||||
|
import android.text.util.Linkify;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
@@ -13,6 +14,7 @@ import static android.view.View.GONE;
|
|||||||
import static android.view.View.VISIBLE;
|
import static android.view.View.VISIBLE;
|
||||||
import static org.briarproject.bramble.util.StringUtils.isNullOrEmpty;
|
import static org.briarproject.bramble.util.StringUtils.isNullOrEmpty;
|
||||||
import static org.briarproject.bramble.util.StringUtils.trim;
|
import static org.briarproject.bramble.util.StringUtils.trim;
|
||||||
|
import static org.briarproject.briar.android.util.UiUtils.makeLinksClickable;
|
||||||
|
|
||||||
@UiThread
|
@UiThread
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
@@ -40,6 +42,8 @@ class ConversationNoticeViewHolder extends ConversationItemViewHolder {
|
|||||||
} else {
|
} else {
|
||||||
msgText.setVisibility(VISIBLE);
|
msgText.setVisibility(VISIBLE);
|
||||||
msgText.setText(trim(text));
|
msgText.setText(trim(text));
|
||||||
|
Linkify.addLinks(msgText, Linkify.WEB_URLS);
|
||||||
|
makeLinksClickable(msgText, listener::onLinkClick);
|
||||||
layout.setBackgroundResource(isIncoming() ?
|
layout.setBackgroundResource(isIncoming() ?
|
||||||
R.drawable.notice_in_bottom : R.drawable.notice_out_bottom);
|
R.drawable.notice_in_bottom : R.drawable.notice_out_bottom);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -436,7 +436,7 @@ class HotspotManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static String createWifiLoginString(String ssid, String password) {
|
private static String createWifiLoginString(String ssid, String password) {
|
||||||
// https://en.wikipedia.org/wiki/QR_code#WiFi_network_login
|
// https://en.wikipedia.org/wiki/QR_code#Joining_a_Wi%E2%80%91Fi_network
|
||||||
// do not remove the dangling ';', it can cause problems to omit it
|
// do not remove the dangling ';', it can cause problems to omit it
|
||||||
return "WIFI:S:" + ssid + ";T:WPA;P:" + password + ";;";
|
return "WIFI:S:" + ssid + ";T:WPA;P:" + password + ";;";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,10 +6,24 @@ import android.widget.ProgressBar;
|
|||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.mailbox.MailboxPairingState;
|
import org.briarproject.bramble.api.mailbox.MailboxPairingState;
|
||||||
|
import org.briarproject.bramble.api.mailbox.MailboxPairingState.ConnectionError;
|
||||||
|
import org.briarproject.bramble.api.mailbox.MailboxPairingState.InvalidQrCode;
|
||||||
|
import org.briarproject.bramble.api.mailbox.MailboxPairingState.MailboxAlreadyPaired;
|
||||||
|
import org.briarproject.bramble.api.mailbox.MailboxPairingState.Paired;
|
||||||
|
import org.briarproject.bramble.api.mailbox.MailboxPairingState.Pending;
|
||||||
|
import org.briarproject.bramble.api.mailbox.MailboxPairingState.UnexpectedError;
|
||||||
import org.briarproject.briar.R;
|
import org.briarproject.briar.R;
|
||||||
import org.briarproject.briar.android.activity.ActivityComponent;
|
import org.briarproject.briar.android.activity.ActivityComponent;
|
||||||
import org.briarproject.briar.android.activity.BriarActivity;
|
import org.briarproject.briar.android.activity.BriarActivity;
|
||||||
import org.briarproject.briar.android.fragment.FinalFragment;
|
import org.briarproject.briar.android.fragment.FinalFragment;
|
||||||
|
import org.briarproject.briar.android.mailbox.MailboxState.CameraError;
|
||||||
|
import org.briarproject.briar.android.mailbox.MailboxState.IsPaired;
|
||||||
|
import org.briarproject.briar.android.mailbox.MailboxState.NotSetup;
|
||||||
|
import org.briarproject.briar.android.mailbox.MailboxState.OfflineWhenPairing;
|
||||||
|
import org.briarproject.briar.android.mailbox.MailboxState.Pairing;
|
||||||
|
import org.briarproject.briar.android.mailbox.MailboxState.ScanningQrCode;
|
||||||
|
import org.briarproject.briar.android.mailbox.MailboxState.ShowDownload;
|
||||||
|
import org.briarproject.briar.android.mailbox.MailboxState.WasUnpaired;
|
||||||
import org.briarproject.briar.android.view.BlankFragment;
|
import org.briarproject.briar.android.view.BlankFragment;
|
||||||
import org.briarproject.nullsafety.MethodsNotNullByDefault;
|
import org.briarproject.nullsafety.MethodsNotNullByDefault;
|
||||||
import org.briarproject.nullsafety.ParametersNotNullByDefault;
|
import org.briarproject.nullsafety.ParametersNotNullByDefault;
|
||||||
@@ -25,6 +39,9 @@ import androidx.lifecycle.ViewModelProvider;
|
|||||||
import static android.view.View.INVISIBLE;
|
import static android.view.View.INVISIBLE;
|
||||||
import static android.view.View.VISIBLE;
|
import static android.view.View.VISIBLE;
|
||||||
import static android.widget.Toast.LENGTH_LONG;
|
import static android.widget.Toast.LENGTH_LONG;
|
||||||
|
import static org.briarproject.bramble.api.mailbox.MailboxConstants.QR_FORMAT_VERSION;
|
||||||
|
import static org.briarproject.bramble.api.qrcode.QrCodeClassifier.QrCodeType.BQP;
|
||||||
|
import static org.briarproject.bramble.api.qrcode.QrCodeClassifier.QrCodeType.MAILBOX;
|
||||||
import static org.briarproject.briar.android.util.UiUtils.showFragment;
|
import static org.briarproject.briar.android.util.UiUtils.showFragment;
|
||||||
|
|
||||||
@MethodsNotNullByDefault
|
@MethodsNotNullByDefault
|
||||||
@@ -56,24 +73,23 @@ public class MailboxActivity extends BriarActivity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
viewModel.getPairingState().observeEvent(this, state -> {
|
viewModel.getPairingState().observeEvent(this, state -> {
|
||||||
if (state instanceof MailboxState.NotSetup) {
|
if (state instanceof NotSetup) {
|
||||||
onNotSetup();
|
onNotSetup();
|
||||||
} else if (state instanceof MailboxState.ShowDownload) {
|
} else if (state instanceof ShowDownload) {
|
||||||
onShowDownload();
|
onShowDownload();
|
||||||
} else if (state instanceof MailboxState.ScanningQrCode) {
|
} else if (state instanceof ScanningQrCode) {
|
||||||
onScanningQrCode();
|
onScanningQrCode();
|
||||||
} else if (state instanceof MailboxState.Pairing) {
|
} else if (state instanceof Pairing) {
|
||||||
MailboxPairingState s =
|
MailboxPairingState s = ((Pairing) state).pairingState;
|
||||||
((MailboxState.Pairing) state).pairingState;
|
|
||||||
onMailboxPairingStateChanged(s);
|
onMailboxPairingStateChanged(s);
|
||||||
} else if (state instanceof MailboxState.OfflineWhenPairing) {
|
} else if (state instanceof OfflineWhenPairing) {
|
||||||
onOffline();
|
onOffline();
|
||||||
} else if (state instanceof MailboxState.CameraError) {
|
} else if (state instanceof CameraError) {
|
||||||
onCameraError();
|
onCameraError();
|
||||||
} else if (state instanceof MailboxState.IsPaired) {
|
} else if (state instanceof IsPaired) {
|
||||||
onIsPaired(((MailboxState.IsPaired) state).isOnline);
|
onIsPaired(((IsPaired) state).isOnline);
|
||||||
} else if (state instanceof MailboxState.WasUnpaired) {
|
} else if (state instanceof WasUnpaired) {
|
||||||
MailboxState.WasUnpaired s = (MailboxState.WasUnpaired) state;
|
WasUnpaired s = (WasUnpaired) state;
|
||||||
onUnPaired(s.tellUserToWipeMailbox);
|
onUnPaired(s.tellUserToWipeMailbox);
|
||||||
} else {
|
} else {
|
||||||
throw new AssertionError("Unknown state: " + state);
|
throw new AssertionError("Unknown state: " + state);
|
||||||
@@ -104,7 +120,7 @@ public class MailboxActivity extends BriarActivity {
|
|||||||
@Override
|
@Override
|
||||||
public void onBackPressed() {
|
public void onBackPressed() {
|
||||||
MailboxState s = viewModel.getPairingState().getLastValue();
|
MailboxState s = viewModel.getPairingState().getLastValue();
|
||||||
if (s instanceof MailboxState.Pairing) {
|
if (s instanceof Pairing) {
|
||||||
// don't go back in the flow if we are already pairing
|
// don't go back in the flow if we are already pairing
|
||||||
// with the mailbox. We provide a try-again button instead.
|
// with the mailbox. We provide a try-again button instead.
|
||||||
supportFinishAfterTransition();
|
supportFinishAfterTransition();
|
||||||
@@ -158,33 +174,44 @@ public class MailboxActivity extends BriarActivity {
|
|||||||
}
|
}
|
||||||
Fragment f;
|
Fragment f;
|
||||||
String tag;
|
String tag;
|
||||||
if (s instanceof MailboxPairingState.QrCodeReceived) {
|
if (s instanceof Pending) {
|
||||||
f = new MailboxConnectingFragment();
|
long timeStarted = ((Pending) s).timeStarted;
|
||||||
|
f = MailboxConnectingFragment.newInstance(timeStarted);
|
||||||
tag = MailboxConnectingFragment.TAG;
|
tag = MailboxConnectingFragment.TAG;
|
||||||
} else if (s instanceof MailboxPairingState.Pairing) {
|
} else if (s instanceof InvalidQrCode) {
|
||||||
f = new MailboxConnectingFragment();
|
InvalidQrCode i = (InvalidQrCode) s;
|
||||||
tag = MailboxConnectingFragment.TAG;
|
int errorRes;
|
||||||
} else if (s instanceof MailboxPairingState.InvalidQrCode) {
|
if (i.qrCodeType == MAILBOX) {
|
||||||
f = ErrorFragment.newInstance(
|
if (i.formatVersion < QR_FORMAT_VERSION) {
|
||||||
R.string.mailbox_setup_qr_code_wrong_title,
|
errorRes = R.string.mailbox_qr_code_too_old;
|
||||||
R.string.mailbox_setup_qr_code_wrong_description);
|
} else if (i.formatVersion > QR_FORMAT_VERSION) {
|
||||||
|
errorRes = R.string.mailbox_qr_code_too_new;
|
||||||
|
} else {
|
||||||
|
errorRes = R.string.mailbox_setup_qr_code_wrong_description;
|
||||||
|
}
|
||||||
|
} else if (i.qrCodeType == BQP) {
|
||||||
|
errorRes = R.string.contact_qr_code_for_mailbox;
|
||||||
|
} else {
|
||||||
|
errorRes = R.string.mailbox_setup_qr_code_wrong_description;
|
||||||
|
}
|
||||||
|
f = ErrorFragment.newInstance(R.string.qr_code_invalid, errorRes);
|
||||||
tag = ErrorFragment.TAG;
|
tag = ErrorFragment.TAG;
|
||||||
} else if (s instanceof MailboxPairingState.MailboxAlreadyPaired) {
|
} else if (s instanceof MailboxAlreadyPaired) {
|
||||||
f = ErrorFragment.newInstance(
|
f = ErrorFragment.newInstance(
|
||||||
R.string.mailbox_setup_already_paired_title,
|
R.string.mailbox_setup_already_paired_title,
|
||||||
R.string.mailbox_setup_already_paired_description);
|
R.string.mailbox_setup_already_paired_description);
|
||||||
tag = ErrorFragment.TAG;
|
tag = ErrorFragment.TAG;
|
||||||
} else if (s instanceof MailboxPairingState.ConnectionError) {
|
} else if (s instanceof ConnectionError) {
|
||||||
f = ErrorFragment.newInstance(
|
f = ErrorFragment.newInstance(
|
||||||
R.string.mailbox_setup_io_error_title,
|
R.string.mailbox_setup_io_error_title,
|
||||||
R.string.mailbox_setup_io_error_description);
|
R.string.mailbox_setup_io_error_description);
|
||||||
tag = ErrorFragment.TAG;
|
tag = ErrorFragment.TAG;
|
||||||
} else if (s instanceof MailboxPairingState.UnexpectedError) {
|
} else if (s instanceof UnexpectedError) {
|
||||||
f = ErrorFragment.newInstance(
|
f = ErrorFragment.newInstance(
|
||||||
R.string.mailbox_setup_assertion_error_title,
|
R.string.mailbox_setup_assertion_error_title,
|
||||||
R.string.mailbox_setup_assertion_error_description);
|
R.string.mailbox_setup_assertion_error_description);
|
||||||
tag = ErrorFragment.TAG;
|
tag = ErrorFragment.TAG;
|
||||||
} else if (s instanceof MailboxPairingState.Paired) {
|
} else if (s instanceof Paired) {
|
||||||
f = FinalFragment.newInstance(R.string.mailbox_setup_paired_title,
|
f = FinalFragment.newInstance(R.string.mailbox_setup_paired_title,
|
||||||
R.drawable.ic_check_circle_outline,
|
R.drawable.ic_check_circle_outline,
|
||||||
R.color.briar_brand_green,
|
R.color.briar_brand_green,
|
||||||
@@ -218,6 +245,7 @@ public class MailboxActivity extends BriarActivity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void onUnPaired(boolean tellUserToWipeMailbox) {
|
private void onUnPaired(boolean tellUserToWipeMailbox) {
|
||||||
|
viewModel.clearProblemNotification();
|
||||||
if (tellUserToWipeMailbox) {
|
if (tellUserToWipeMailbox) {
|
||||||
showFragment(getSupportFragmentManager(), new BlankFragment(),
|
showFragment(getSupportFragmentManager(), new BlankFragment(),
|
||||||
BlankFragment.TAG);
|
BlankFragment.TAG);
|
||||||
|
|||||||
@@ -1,10 +1,15 @@
|
|||||||
package org.briarproject.briar.android.mailbox;
|
package org.briarproject.briar.android.mailbox;
|
||||||
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.os.Looper;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.ProgressBar;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.plugin.TorConstants;
|
||||||
import org.briarproject.briar.R;
|
import org.briarproject.briar.R;
|
||||||
import org.briarproject.nullsafety.MethodsNotNullByDefault;
|
import org.briarproject.nullsafety.MethodsNotNullByDefault;
|
||||||
import org.briarproject.nullsafety.ParametersNotNullByDefault;
|
import org.briarproject.nullsafety.ParametersNotNullByDefault;
|
||||||
@@ -12,25 +17,71 @@ import org.briarproject.nullsafety.ParametersNotNullByDefault;
|
|||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.fragment.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
|
|
||||||
|
import static org.briarproject.briar.android.util.UiUtils.formatDuration;
|
||||||
|
|
||||||
@MethodsNotNullByDefault
|
@MethodsNotNullByDefault
|
||||||
@ParametersNotNullByDefault
|
@ParametersNotNullByDefault
|
||||||
public class MailboxConnectingFragment extends Fragment {
|
public class MailboxConnectingFragment extends Fragment {
|
||||||
|
|
||||||
static final String TAG = MailboxConnectingFragment.class.getName();
|
static final String TAG = MailboxConnectingFragment.class.getName();
|
||||||
|
|
||||||
|
private static final String ARG_STARTED = "started";
|
||||||
|
private static final long TIMEOUT_MS = TorConstants.EXTRA_CONNECT_TIMEOUT;
|
||||||
|
private static final long REFRESH_INTERVAL_MS = 1_000;
|
||||||
|
|
||||||
|
private final Handler handler = new Handler(Looper.getMainLooper());
|
||||||
|
// Capture a method reference so we use the same reference for posting
|
||||||
|
// and removing
|
||||||
|
private final Runnable refresher = this::updateProgressBar;
|
||||||
|
|
||||||
|
private ProgressBar progressBar;
|
||||||
|
private long timeStarted;
|
||||||
|
|
||||||
|
public static MailboxConnectingFragment newInstance(long timeStarted) {
|
||||||
|
MailboxConnectingFragment f = new MailboxConnectingFragment();
|
||||||
|
Bundle args = new Bundle();
|
||||||
|
args.putLong(ARG_STARTED, timeStarted);
|
||||||
|
f.setArguments(args);
|
||||||
|
return f;
|
||||||
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@Override
|
@Override
|
||||||
public View onCreateView(LayoutInflater inflater,
|
public View onCreateView(LayoutInflater inflater,
|
||||||
@Nullable ViewGroup container,
|
@Nullable ViewGroup container,
|
||||||
@Nullable Bundle savedInstanceState) {
|
@Nullable Bundle savedInstanceState) {
|
||||||
return inflater.inflate(R.layout.fragment_mailbox_connecting,
|
View v = inflater.inflate(R.layout.fragment_mailbox_connecting,
|
||||||
container, false);
|
container, false);
|
||||||
|
|
||||||
|
progressBar = v.findViewById(R.id.progressBar);
|
||||||
|
TextView info = v.findViewById(R.id.info);
|
||||||
|
String duration = formatDuration(requireContext(), TIMEOUT_MS);
|
||||||
|
info.setText(getString(R.string.mailbox_setup_connecting_info,
|
||||||
|
duration));
|
||||||
|
|
||||||
|
timeStarted = requireArguments().getLong(ARG_STARTED);
|
||||||
|
|
||||||
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onStart() {
|
public void onStart() {
|
||||||
super.onStart();
|
super.onStart();
|
||||||
requireActivity().setTitle(R.string.mailbox_setup_title);
|
requireActivity().setTitle(R.string.mailbox_setup_title);
|
||||||
|
updateProgressBar();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStop() {
|
||||||
|
super.onStop();
|
||||||
|
handler.removeCallbacks(refresher);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateProgressBar() {
|
||||||
|
long elapsedMs = System.currentTimeMillis() - timeStarted;
|
||||||
|
int percent = (int) (elapsedMs * 100 / TIMEOUT_MS);
|
||||||
|
percent = Math.min(Math.max(percent, 0), 100);
|
||||||
|
progressBar.setProgress(percent);
|
||||||
|
handler.postDelayed(refresher, REFRESH_INTERVAL_MS);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
|||||||
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
|
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
|
||||||
import org.briarproject.bramble.api.mailbox.MailboxManager;
|
import org.briarproject.bramble.api.mailbox.MailboxManager;
|
||||||
import org.briarproject.bramble.api.mailbox.MailboxPairingState;
|
import org.briarproject.bramble.api.mailbox.MailboxPairingState;
|
||||||
|
import org.briarproject.bramble.api.mailbox.MailboxPairingState.Paired;
|
||||||
import org.briarproject.bramble.api.mailbox.MailboxPairingTask;
|
import org.briarproject.bramble.api.mailbox.MailboxPairingTask;
|
||||||
import org.briarproject.bramble.api.mailbox.MailboxStatus;
|
import org.briarproject.bramble.api.mailbox.MailboxStatus;
|
||||||
import org.briarproject.bramble.api.mailbox.event.OwnMailboxConnectionStatusEvent;
|
import org.briarproject.bramble.api.mailbox.event.OwnMailboxConnectionStatusEvent;
|
||||||
@@ -24,7 +25,14 @@ import org.briarproject.bramble.api.plugin.TorConstants;
|
|||||||
import org.briarproject.bramble.api.plugin.TransportId;
|
import org.briarproject.bramble.api.plugin.TransportId;
|
||||||
import org.briarproject.bramble.api.plugin.event.TransportInactiveEvent;
|
import org.briarproject.bramble.api.plugin.event.TransportInactiveEvent;
|
||||||
import org.briarproject.bramble.api.system.AndroidExecutor;
|
import org.briarproject.bramble.api.system.AndroidExecutor;
|
||||||
|
import org.briarproject.briar.android.mailbox.MailboxState.CameraError;
|
||||||
|
import org.briarproject.briar.android.mailbox.MailboxState.IsPaired;
|
||||||
import org.briarproject.briar.android.mailbox.MailboxState.NotSetup;
|
import org.briarproject.briar.android.mailbox.MailboxState.NotSetup;
|
||||||
|
import org.briarproject.briar.android.mailbox.MailboxState.OfflineWhenPairing;
|
||||||
|
import org.briarproject.briar.android.mailbox.MailboxState.Pairing;
|
||||||
|
import org.briarproject.briar.android.mailbox.MailboxState.ScanningQrCode;
|
||||||
|
import org.briarproject.briar.android.mailbox.MailboxState.ShowDownload;
|
||||||
|
import org.briarproject.briar.android.mailbox.MailboxState.WasUnpaired;
|
||||||
import org.briarproject.briar.android.qrcode.QrCodeDecoder;
|
import org.briarproject.briar.android.qrcode.QrCodeDecoder;
|
||||||
import org.briarproject.briar.android.viewmodel.DbViewModel;
|
import org.briarproject.briar.android.viewmodel.DbViewModel;
|
||||||
import org.briarproject.briar.android.viewmodel.LiveEvent;
|
import org.briarproject.briar.android.viewmodel.LiveEvent;
|
||||||
@@ -113,7 +121,7 @@ class MailboxViewModel extends DbViewModel
|
|||||||
MailboxStatus mailboxStatus =
|
MailboxStatus mailboxStatus =
|
||||||
mailboxManager.getMailboxStatus(txn);
|
mailboxManager.getMailboxStatus(txn);
|
||||||
boolean isOnline = isTorActive();
|
boolean isOnline = isTorActive();
|
||||||
pairingState.postEvent(new MailboxState.IsPaired(isOnline));
|
pairingState.postEvent(new IsPaired(isOnline));
|
||||||
status.postValue(mailboxStatus);
|
status.postValue(mailboxStatus);
|
||||||
} else {
|
} else {
|
||||||
pairingState.postEvent(new NotSetup());
|
pairingState.postEvent(new NotSetup());
|
||||||
@@ -142,14 +150,14 @@ class MailboxViewModel extends DbViewModel
|
|||||||
@UiThread
|
@UiThread
|
||||||
private void onTorInactive() {
|
private void onTorInactive() {
|
||||||
MailboxState lastState = pairingState.getLastValue();
|
MailboxState lastState = pairingState.getLastValue();
|
||||||
if (lastState instanceof MailboxState.IsPaired) {
|
if (lastState instanceof IsPaired) {
|
||||||
// we are already paired, so use IsPaired state
|
// we are already paired, so use IsPaired state
|
||||||
pairingState.setEvent(new MailboxState.IsPaired(false));
|
pairingState.setEvent(new IsPaired(false));
|
||||||
} else if (lastState instanceof MailboxState.Pairing) {
|
} else if (lastState instanceof Pairing) {
|
||||||
MailboxState.Pairing p = (MailboxState.Pairing) lastState;
|
Pairing p = (Pairing) lastState;
|
||||||
// check that we not just finished pairing (showing success screen)
|
// check that we not just finished pairing (showing success screen)
|
||||||
if (!(p.pairingState instanceof MailboxPairingState.Paired)) {
|
if (!(p.pairingState instanceof Paired)) {
|
||||||
pairingState.setEvent(new MailboxState.OfflineWhenPairing());
|
pairingState.setEvent(new OfflineWhenPairing());
|
||||||
}
|
}
|
||||||
// else ignore offline event as user will be leaving UI flow anyway
|
// else ignore offline event as user will be leaving UI flow anyway
|
||||||
}
|
}
|
||||||
@@ -158,15 +166,15 @@ class MailboxViewModel extends DbViewModel
|
|||||||
@UiThread
|
@UiThread
|
||||||
void onScanButtonClicked() {
|
void onScanButtonClicked() {
|
||||||
if (isTorActive()) {
|
if (isTorActive()) {
|
||||||
pairingState.setEvent(new MailboxState.ScanningQrCode());
|
pairingState.setEvent(new ScanningQrCode());
|
||||||
} else {
|
} else {
|
||||||
pairingState.setEvent(new MailboxState.OfflineWhenPairing());
|
pairingState.setEvent(new OfflineWhenPairing());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@UiThread
|
@UiThread
|
||||||
void onCameraError() {
|
void onCameraError() {
|
||||||
pairingState.setEvent(new MailboxState.CameraError());
|
pairingState.setEvent(new CameraError());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -182,7 +190,7 @@ class MailboxViewModel extends DbViewModel
|
|||||||
pairingTask = mailboxManager.startPairingTask(qrCodePayload);
|
pairingTask = mailboxManager.startPairingTask(qrCodePayload);
|
||||||
pairingTask.addObserver(this);
|
pairingTask.addObserver(this);
|
||||||
} else {
|
} else {
|
||||||
pairingState.postEvent(new MailboxState.OfflineWhenPairing());
|
pairingState.postEvent(new OfflineWhenPairing());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -193,7 +201,7 @@ class MailboxViewModel extends DbViewModel
|
|||||||
LOG.info("New pairing state: " +
|
LOG.info("New pairing state: " +
|
||||||
mailboxPairingState.getClass().getSimpleName());
|
mailboxPairingState.getClass().getSimpleName());
|
||||||
}
|
}
|
||||||
pairingState.setEvent(new MailboxState.Pairing(mailboxPairingState));
|
pairingState.setEvent(new Pairing(mailboxPairingState));
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isTorActive() {
|
private boolean isTorActive() {
|
||||||
@@ -203,7 +211,7 @@ class MailboxViewModel extends DbViewModel
|
|||||||
|
|
||||||
@UiThread
|
@UiThread
|
||||||
void showDownloadFragment() {
|
void showDownloadFragment() {
|
||||||
pairingState.setEvent(new MailboxState.ShowDownload());
|
pairingState.setEvent(new ShowDownload());
|
||||||
}
|
}
|
||||||
|
|
||||||
@UiThread
|
@UiThread
|
||||||
@@ -214,7 +222,7 @@ class MailboxViewModel extends DbViewModel
|
|||||||
@UiThread
|
@UiThread
|
||||||
void checkIfOnlineWhenPaired() {
|
void checkIfOnlineWhenPaired() {
|
||||||
boolean isOnline = isTorActive();
|
boolean isOnline = isTorActive();
|
||||||
pairingState.setEvent(new MailboxState.IsPaired(isOnline));
|
pairingState.setEvent(new IsPaired(isOnline));
|
||||||
}
|
}
|
||||||
|
|
||||||
LiveData<Boolean> checkConnection() {
|
LiveData<Boolean> checkConnection() {
|
||||||
@@ -227,7 +235,7 @@ class MailboxViewModel extends DbViewModel
|
|||||||
checkConnection(success -> {
|
checkConnection(success -> {
|
||||||
boolean isOnline = isTorActive();
|
boolean isOnline = isTorActive();
|
||||||
// make UI move back to status fragment by changing pairingState
|
// make UI move back to status fragment by changing pairingState
|
||||||
pairingState.postEvent(new MailboxState.IsPaired(isOnline));
|
pairingState.postEvent(new IsPaired(isOnline));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -246,7 +254,7 @@ class MailboxViewModel extends DbViewModel
|
|||||||
ioExecutor.execute(() -> {
|
ioExecutor.execute(() -> {
|
||||||
try {
|
try {
|
||||||
boolean wasWiped = mailboxManager.unPair();
|
boolean wasWiped = mailboxManager.unPair();
|
||||||
pairingState.postEvent(new MailboxState.WasUnpaired(!wasWiped));
|
pairingState.postEvent(new WasUnpaired(!wasWiped));
|
||||||
} catch (DbException e) {
|
} catch (DbException e) {
|
||||||
handleException(e);
|
handleException(e);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,13 +16,12 @@ import org.briarproject.nullsafety.ParametersNotNullByDefault;
|
|||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.core.widget.NestedScrollView;
|
|
||||||
import androidx.fragment.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
import androidx.fragment.app.FragmentActivity;
|
import androidx.fragment.app.FragmentActivity;
|
||||||
import androidx.lifecycle.ViewModelProvider;
|
import androidx.lifecycle.ViewModelProvider;
|
||||||
|
|
||||||
import static android.view.View.FOCUS_DOWN;
|
|
||||||
import static org.briarproject.briar.android.AppModule.getAndroidComponent;
|
import static org.briarproject.briar.android.AppModule.getAndroidComponent;
|
||||||
|
import static org.briarproject.briar.android.util.UiUtils.hideViewOnSmallScreen;
|
||||||
|
|
||||||
@MethodsNotNullByDefault
|
@MethodsNotNullByDefault
|
||||||
@ParametersNotNullByDefault
|
@ParametersNotNullByDefault
|
||||||
@@ -35,8 +34,6 @@ public class OfflineFragment extends Fragment {
|
|||||||
|
|
||||||
protected MailboxViewModel viewModel;
|
protected MailboxViewModel viewModel;
|
||||||
|
|
||||||
private NestedScrollView scrollView;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onAttach(Context context) {
|
public void onAttach(Context context) {
|
||||||
super.onAttach(context);
|
super.onAttach(context);
|
||||||
@@ -54,7 +51,6 @@ public class OfflineFragment extends Fragment {
|
|||||||
View v = inflater
|
View v = inflater
|
||||||
.inflate(R.layout.fragment_offline, container, false);
|
.inflate(R.layout.fragment_offline, container, false);
|
||||||
|
|
||||||
scrollView = (NestedScrollView) v;
|
|
||||||
Button checkButton = v.findViewById(R.id.checkButton);
|
Button checkButton = v.findViewById(R.id.checkButton);
|
||||||
checkButton.setOnClickListener(view -> {
|
checkButton.setOnClickListener(view -> {
|
||||||
Intent i = new Intent(requireContext(), TransportsActivity.class);
|
Intent i = new Intent(requireContext(), TransportsActivity.class);
|
||||||
@@ -69,8 +65,7 @@ public class OfflineFragment extends Fragment {
|
|||||||
@Override
|
@Override
|
||||||
public void onStart() {
|
public void onStart() {
|
||||||
super.onStart();
|
super.onStart();
|
||||||
// Scroll down in case the screen is small, so the button is visible
|
hideViewOnSmallScreen(requireView().findViewById(R.id.iconView));
|
||||||
scrollView.post(() -> scrollView.fullScroll(FOCUS_DOWN));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void onTryAgainClicked() {
|
protected void onTryAgainClicked() {
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import org.briarproject.briar.android.privategroup.memberlist.GroupMemberListAct
|
|||||||
import org.briarproject.briar.android.privategroup.reveal.RevealContactsActivity;
|
import org.briarproject.briar.android.privategroup.reveal.RevealContactsActivity;
|
||||||
import org.briarproject.briar.android.threaded.ThreadListActivity;
|
import org.briarproject.briar.android.threaded.ThreadListActivity;
|
||||||
import org.briarproject.briar.android.threaded.ThreadListViewModel;
|
import org.briarproject.briar.android.threaded.ThreadListViewModel;
|
||||||
|
import org.briarproject.briar.android.widget.LinkDialogFragment;
|
||||||
import org.briarproject.nullsafety.MethodsNotNullByDefault;
|
import org.briarproject.nullsafety.MethodsNotNullByDefault;
|
||||||
import org.briarproject.nullsafety.ParametersNotNullByDefault;
|
import org.briarproject.nullsafety.ParametersNotNullByDefault;
|
||||||
|
|
||||||
@@ -158,6 +159,12 @@ public class GroupActivity extends
|
|||||||
if (isDissolved != null && !isDissolved) super.onReplyClick(item);
|
if (isDissolved != null && !isDissolved) super.onReplyClick(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onLinkClick(String url){
|
||||||
|
LinkDialogFragment f = LinkDialogFragment.newInstance(url);
|
||||||
|
f.show(getSupportFragmentManager(), f.getUniqueTag());
|
||||||
|
}
|
||||||
|
|
||||||
private void setGroupEnabled(boolean enabled) {
|
private void setGroupEnabled(boolean enabled) {
|
||||||
sendController.setReady(enabled);
|
sendController.setReady(enabled);
|
||||||
list.getRecyclerView().setAlpha(enabled ? 1f : 0.5f);
|
list.getRecyclerView().setAlpha(enabled ? 1f : 0.5f);
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ 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.sharing.SharingManager.SharingStatus;
|
||||||
import org.briarproject.nullsafety.NotNullByDefault;
|
import org.briarproject.nullsafety.NotNullByDefault;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@@ -140,8 +141,8 @@ class CreateGroupControllerImpl extends ContactSelectorControllerImpl
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean isDisabled(GroupId g, Contact c) throws DbException {
|
protected SharingStatus getSharingStatus(GroupId g, Contact c) throws DbException {
|
||||||
return !groupInvitationManager.isInvitationAllowed(c, g);
|
return groupInvitationManager.getSharingStatus(c, g);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -92,7 +92,7 @@ class RevealContactsControllerImpl extends DbControllerImpl
|
|||||||
boolean selected =
|
boolean selected =
|
||||||
disabled || selection.contains(c.getId());
|
disabled || selection.contains(c.getId());
|
||||||
items.add(new RevealableContactItem(c, authorInfo, selected,
|
items.add(new RevealableContactItem(c, authorInfo, selected,
|
||||||
disabled, m.getVisibility()));
|
m.getVisibility()));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,22 +1,24 @@
|
|||||||
package org.briarproject.briar.android.privategroup.reveal;
|
package org.briarproject.briar.android.privategroup.reveal;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.contact.Contact;
|
import org.briarproject.bramble.api.contact.Contact;
|
||||||
import org.briarproject.briar.android.contactselection.SelectableContactItem;
|
import org.briarproject.briar.android.contactselection.BaseSelectableContactItem;
|
||||||
import org.briarproject.briar.api.identity.AuthorInfo;
|
import org.briarproject.briar.api.identity.AuthorInfo;
|
||||||
import org.briarproject.briar.api.privategroup.Visibility;
|
import org.briarproject.briar.api.privategroup.Visibility;
|
||||||
import org.briarproject.nullsafety.NotNullByDefault;
|
import org.briarproject.nullsafety.NotNullByDefault;
|
||||||
|
|
||||||
import javax.annotation.concurrent.NotThreadSafe;
|
import javax.annotation.concurrent.NotThreadSafe;
|
||||||
|
|
||||||
|
import static org.briarproject.briar.api.privategroup.Visibility.INVISIBLE;
|
||||||
|
|
||||||
@NotThreadSafe
|
@NotThreadSafe
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
class RevealableContactItem extends SelectableContactItem {
|
class RevealableContactItem extends BaseSelectableContactItem {
|
||||||
|
|
||||||
private final Visibility visibility;
|
private final Visibility visibility;
|
||||||
|
|
||||||
RevealableContactItem(Contact contact, AuthorInfo authorInfo,
|
RevealableContactItem(Contact contact, AuthorInfo authorInfo,
|
||||||
boolean selected, boolean disabled, Visibility visibility) {
|
boolean selected, Visibility visibility) {
|
||||||
super(contact, authorInfo, selected, disabled);
|
super(contact, authorInfo, selected);
|
||||||
this.visibility = visibility;
|
this.visibility = visibility;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -24,4 +26,8 @@ class RevealableContactItem extends SelectableContactItem {
|
|||||||
return visibility;
|
return visibility;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isDisabled() {
|
||||||
|
return visibility != INVISIBLE;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,7 +41,6 @@ public class QrCodeDecoder implements PreviewConsumer, PreviewCallback {
|
|||||||
private final ResultCallback callback;
|
private final ResultCallback callback;
|
||||||
|
|
||||||
private Camera camera = null;
|
private Camera camera = null;
|
||||||
private int cameraIndex = 0;
|
|
||||||
|
|
||||||
public QrCodeDecoder(AndroidExecutor androidExecutor,
|
public QrCodeDecoder(AndroidExecutor androidExecutor,
|
||||||
@IoExecutor Executor ioExecutor, ResultCallback callback) {
|
@IoExecutor Executor ioExecutor, ResultCallback callback) {
|
||||||
@@ -53,14 +52,12 @@ public class QrCodeDecoder implements PreviewConsumer, PreviewCallback {
|
|||||||
@Override
|
@Override
|
||||||
public void start(Camera camera, int cameraIndex) {
|
public void start(Camera camera, int cameraIndex) {
|
||||||
this.camera = camera;
|
this.camera = camera;
|
||||||
this.cameraIndex = cameraIndex;
|
|
||||||
askForPreviewFrame();
|
askForPreviewFrame();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void stop() {
|
public void stop() {
|
||||||
camera = null;
|
camera = null;
|
||||||
cameraIndex = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@UiThread
|
@UiThread
|
||||||
|
|||||||
@@ -310,14 +310,12 @@ class BriarReportCollector {
|
|||||||
btLeAdvertise);
|
btLeAdvertise);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasBtConnectPermission(ctx)) {
|
Pair<String, String> p = getBluetoothAddressAndMethod(ctx, bt);
|
||||||
Pair<String, String> p = getBluetoothAddressAndMethod(ctx, bt);
|
String address = p.getFirst();
|
||||||
String address = p.getFirst();
|
String method = p.getSecond();
|
||||||
String method = p.getSecond();
|
connectivityInfo.add("BluetoothAddress",
|
||||||
connectivityInfo.add("BluetoothAddress",
|
scrubMacAddress(address));
|
||||||
scrubMacAddress(address));
|
connectivityInfo.add("BluetoothAddressMethod", method);
|
||||||
connectivityInfo.add("BluetoothAddressMethod", method);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return new ReportItem("Connectivity", R.string.dev_report_connectivity,
|
return new ReportItem("Connectivity", R.string.dev_report_connectivity,
|
||||||
connectivityInfo);
|
connectivityInfo);
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ import static org.briarproject.bramble.api.plugin.TorConstants.DEFAULT_PREF_TOR_
|
|||||||
import static org.briarproject.bramble.api.plugin.TorConstants.DEFAULT_PREF_TOR_ONLY_WHEN_CHARGING;
|
import static org.briarproject.bramble.api.plugin.TorConstants.DEFAULT_PREF_TOR_ONLY_WHEN_CHARGING;
|
||||||
import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_MOBILE;
|
import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_MOBILE;
|
||||||
import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_NETWORK;
|
import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_NETWORK;
|
||||||
import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_NETWORK_NEVER;
|
|
||||||
import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_ONLY_WHEN_CHARGING;
|
import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_ONLY_WHEN_CHARGING;
|
||||||
import static org.briarproject.briar.android.settings.SettingsViewModel.BT_NAMESPACE;
|
import static org.briarproject.briar.android.settings.SettingsViewModel.BT_NAMESPACE;
|
||||||
import static org.briarproject.briar.android.settings.SettingsViewModel.TOR_NAMESPACE;
|
import static org.briarproject.briar.android.settings.SettingsViewModel.TOR_NAMESPACE;
|
||||||
@@ -61,32 +60,18 @@ class ConnectionsManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void updateTorSettings(Settings settings) {
|
void updateTorSettings(Settings settings) {
|
||||||
Settings torSettings = migrateTorSettings(settings);
|
torEnabled.postValue(settings.getBoolean(PREF_PLUGIN_ENABLE,
|
||||||
torEnabled.postValue(torSettings.getBoolean(PREF_PLUGIN_ENABLE,
|
|
||||||
TorConstants.DEFAULT_PREF_PLUGIN_ENABLE));
|
TorConstants.DEFAULT_PREF_PLUGIN_ENABLE));
|
||||||
|
|
||||||
int torNetworkSetting = torSettings.getInt(PREF_TOR_NETWORK,
|
int torNetworkSetting = settings.getInt(PREF_TOR_NETWORK,
|
||||||
DEFAULT_PREF_TOR_NETWORK);
|
DEFAULT_PREF_TOR_NETWORK);
|
||||||
torNetwork.postValue(Integer.toString(torNetworkSetting));
|
torNetwork.postValue(Integer.toString(torNetworkSetting));
|
||||||
|
|
||||||
torMobile.postValue(torSettings.getBoolean(PREF_TOR_MOBILE,
|
torMobile.postValue(settings.getBoolean(PREF_TOR_MOBILE,
|
||||||
DEFAULT_PREF_TOR_MOBILE));
|
DEFAULT_PREF_TOR_MOBILE));
|
||||||
torCharging
|
|
||||||
.postValue(torSettings.getBoolean(PREF_TOR_ONLY_WHEN_CHARGING,
|
|
||||||
DEFAULT_PREF_TOR_ONLY_WHEN_CHARGING));
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Remove after a reasonable migration period (added 2020-06-25)
|
torCharging.postValue(settings.getBoolean(PREF_TOR_ONLY_WHEN_CHARGING,
|
||||||
private Settings migrateTorSettings(Settings s) {
|
DEFAULT_PREF_TOR_ONLY_WHEN_CHARGING));
|
||||||
int network = s.getInt(PREF_TOR_NETWORK, DEFAULT_PREF_TOR_NETWORK);
|
|
||||||
if (network == PREF_TOR_NETWORK_NEVER) {
|
|
||||||
s.putInt(PREF_TOR_NETWORK, DEFAULT_PREF_TOR_NETWORK);
|
|
||||||
s.putBoolean(PREF_PLUGIN_ENABLE, false);
|
|
||||||
// We don't need to save the migrated settings - the Tor plugin is
|
|
||||||
// responsible for that. This code just handles the case where the
|
|
||||||
// settings are loaded before the plugin migrates them.
|
|
||||||
}
|
|
||||||
return s;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
LiveData<Boolean> btEnabled() {
|
LiveData<Boolean> btEnabled() {
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import org.briarproject.briar.android.contactselection.ContactSelectorController
|
|||||||
import org.briarproject.briar.android.controller.handler.ExceptionHandler;
|
import org.briarproject.briar.android.controller.handler.ExceptionHandler;
|
||||||
import org.briarproject.briar.api.blog.BlogSharingManager;
|
import org.briarproject.briar.api.blog.BlogSharingManager;
|
||||||
import org.briarproject.briar.api.identity.AuthorManager;
|
import org.briarproject.briar.api.identity.AuthorManager;
|
||||||
|
import org.briarproject.briar.api.sharing.SharingManager.SharingStatus;
|
||||||
import org.briarproject.nullsafety.NotNullByDefault;
|
import org.briarproject.nullsafety.NotNullByDefault;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
@@ -47,8 +48,9 @@ class ShareBlogControllerImpl extends ContactSelectorControllerImpl
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean isDisabled(GroupId g, Contact c) throws DbException {
|
protected SharingStatus getSharingStatus(GroupId g, Contact c)
|
||||||
return !blogSharingManager.canBeShared(g, c);
|
throws DbException {
|
||||||
|
return blogSharingManager.getSharingStatus(g, c);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import org.briarproject.briar.android.contactselection.ContactSelectorController
|
|||||||
import org.briarproject.briar.android.controller.handler.ExceptionHandler;
|
import org.briarproject.briar.android.controller.handler.ExceptionHandler;
|
||||||
import org.briarproject.briar.api.forum.ForumSharingManager;
|
import org.briarproject.briar.api.forum.ForumSharingManager;
|
||||||
import org.briarproject.briar.api.identity.AuthorManager;
|
import org.briarproject.briar.api.identity.AuthorManager;
|
||||||
|
import org.briarproject.briar.api.sharing.SharingManager.SharingStatus;
|
||||||
import org.briarproject.nullsafety.NotNullByDefault;
|
import org.briarproject.nullsafety.NotNullByDefault;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
@@ -47,8 +48,9 @@ class ShareForumControllerImpl extends ContactSelectorControllerImpl
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean isDisabled(GroupId g, Contact c) throws DbException {
|
protected SharingStatus getSharingStatus(GroupId g, Contact c)
|
||||||
return !forumSharingManager.canBeShared(g, c);
|
throws DbException {
|
||||||
|
return forumSharingManager.getSharingStatus(g, c);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import android.animation.Animator;
|
|||||||
import android.animation.ArgbEvaluator;
|
import android.animation.ArgbEvaluator;
|
||||||
import android.animation.ValueAnimator;
|
import android.animation.ValueAnimator;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.text.util.Linkify;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.view.animation.AccelerateInterpolator;
|
import android.view.animation.AccelerateInterpolator;
|
||||||
@@ -20,6 +21,7 @@ import androidx.annotation.UiThread;
|
|||||||
import androidx.recyclerview.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
import static androidx.core.content.ContextCompat.getColor;
|
import static androidx.core.content.ContextCompat.getColor;
|
||||||
|
import static org.briarproject.briar.android.util.UiUtils.makeLinksClickable;
|
||||||
|
|
||||||
@UiThread
|
@UiThread
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
@@ -43,6 +45,8 @@ public abstract class BaseThreadItemViewHolder<I extends ThreadItem>
|
|||||||
@CallSuper
|
@CallSuper
|
||||||
public void bind(I item, ThreadItemListener<I> listener) {
|
public void bind(I item, ThreadItemListener<I> listener) {
|
||||||
textView.setText(StringUtils.trim(item.getText()));
|
textView.setText(StringUtils.trim(item.getText()));
|
||||||
|
Linkify.addLinks(textView, Linkify.WEB_URLS);
|
||||||
|
makeLinksClickable(textView, listener::onLinkClick);
|
||||||
|
|
||||||
author.setAuthor(item.getAuthor(), item.getAuthorInfo());
|
author.setAuthor(item.getAuthor(), item.getAuthorInfo());
|
||||||
author.setDate(item.getTimestamp());
|
author.setDate(item.getTimestamp());
|
||||||
|
|||||||
@@ -137,6 +137,7 @@ public class ThreadItemAdapter<I extends ThreadItem>
|
|||||||
|
|
||||||
public interface ThreadItemListener<I> {
|
public interface ThreadItemListener<I> {
|
||||||
void onReplyClick(I item);
|
void onReplyClick(I item);
|
||||||
|
void onLinkClick(String url);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import org.briarproject.briar.android.view.TextSendController;
|
|||||||
import org.briarproject.briar.android.view.TextSendController.SendListener;
|
import org.briarproject.briar.android.view.TextSendController.SendListener;
|
||||||
import org.briarproject.briar.android.view.TextSendController.SendState;
|
import org.briarproject.briar.android.view.TextSendController.SendState;
|
||||||
import org.briarproject.briar.android.view.UnreadMessageButton;
|
import org.briarproject.briar.android.view.UnreadMessageButton;
|
||||||
|
import org.briarproject.briar.android.widget.LinkDialogFragment;
|
||||||
import org.briarproject.briar.api.attachment.AttachmentHeader;
|
import org.briarproject.briar.api.attachment.AttachmentHeader;
|
||||||
import org.briarproject.nullsafety.MethodsNotNullByDefault;
|
import org.briarproject.nullsafety.MethodsNotNullByDefault;
|
||||||
import org.briarproject.nullsafety.ParametersNotNullByDefault;
|
import org.briarproject.nullsafety.ParametersNotNullByDefault;
|
||||||
@@ -202,6 +203,12 @@ public abstract class ThreadListActivity<I extends ThreadItem, A extends ThreadI
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onLinkClick(String url) {
|
||||||
|
LinkDialogFragment f = LinkDialogFragment.newInstance(url);
|
||||||
|
f.show(getSupportFragmentManager(), f.getUniqueTag());
|
||||||
|
}
|
||||||
|
|
||||||
protected void setToolbarSubTitle(SharingInfo sharingInfo) {
|
protected void setToolbarSubTitle(SharingInfo sharingInfo) {
|
||||||
ActionBar actionBar = getSupportActionBar();
|
ActionBar actionBar = getSupportActionBar();
|
||||||
if (actionBar != null) {
|
if (actionBar != null) {
|
||||||
|
|||||||
@@ -24,7 +24,6 @@ public interface AndroidNotificationManager {
|
|||||||
|
|
||||||
// Notification IDs
|
// Notification IDs
|
||||||
int ONGOING_NOTIFICATION_ID = 1;
|
int ONGOING_NOTIFICATION_ID = 1;
|
||||||
int FAILURE_NOTIFICATION_ID = 2;
|
|
||||||
int REMINDER_NOTIFICATION_ID = 3;
|
int REMINDER_NOTIFICATION_ID = 3;
|
||||||
int PRIVATE_MESSAGE_NOTIFICATION_ID = 4;
|
int PRIVATE_MESSAGE_NOTIFICATION_ID = 4;
|
||||||
int GROUP_MESSAGE_NOTIFICATION_ID = 5;
|
int GROUP_MESSAGE_NOTIFICATION_ID = 5;
|
||||||
@@ -47,10 +46,6 @@ public interface AndroidNotificationManager {
|
|||||||
String HOTSPOT_CHANNEL_ID = "zHotspot";
|
String HOTSPOT_CHANNEL_ID = "zHotspot";
|
||||||
String MAILBOX_PROBLEM_CHANNEL_ID = "zMailboxProblem";
|
String MAILBOX_PROBLEM_CHANNEL_ID = "zMailboxProblem";
|
||||||
|
|
||||||
// This channel is no longer used - keep the ID so we can remove the
|
|
||||||
// channel from existing installations
|
|
||||||
String FAILURE_CHANNEL_ID = "zStartupFailure";
|
|
||||||
|
|
||||||
// Actions for pending intents
|
// Actions for pending intents
|
||||||
String ACTION_DISMISS_REMINDER = "dismissReminder";
|
String ACTION_DISMISS_REMINDER = "dismissReminder";
|
||||||
String ACTION_STOP_HOTSPOT = "stopHotspot";
|
String ACTION_STOP_HOTSPOT = "stopHotspot";
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:color="?attr/colorControlHighlight">
|
||||||
|
<item
|
||||||
|
android:id="@android:id/mask"
|
||||||
|
android:drawable="@drawable/button_outline_mask" />
|
||||||
|
<item android:drawable="@drawable/button_outline_background" />
|
||||||
|
</ripple>
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
<inset xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:insetLeft="4dp"
|
||||||
|
android:insetTop="6dp"
|
||||||
|
android:insetRight="4dp"
|
||||||
|
android:insetBottom="6dp">
|
||||||
|
<shape
|
||||||
|
android:shape="rectangle">
|
||||||
|
<corners android:radius="2dp" />
|
||||||
|
<solid android:color="@android:color/transparent" />
|
||||||
|
<stroke
|
||||||
|
android:width="1dp"
|
||||||
|
android:color="@color/briar_button_outline" />
|
||||||
|
<padding
|
||||||
|
android:bottom="4dp"
|
||||||
|
android:left="8dp"
|
||||||
|
android:right="8dp"
|
||||||
|
android:top="4dp" />
|
||||||
|
</shape>
|
||||||
|
</inset>
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
<inset xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:insetLeft="4dp"
|
||||||
|
android:insetTop="6dp"
|
||||||
|
android:insetRight="4dp"
|
||||||
|
android:insetBottom="6dp">
|
||||||
|
<shape
|
||||||
|
android:shape="rectangle">
|
||||||
|
<corners android:radius="2dp" />
|
||||||
|
<solid android:color="@android:color/white" />
|
||||||
|
<stroke
|
||||||
|
android:width="1dp"
|
||||||
|
android:color="@android:color/white" />
|
||||||
|
<padding
|
||||||
|
android:bottom="4dp"
|
||||||
|
android:left="8dp"
|
||||||
|
android:right="8dp"
|
||||||
|
android:top="4dp" />
|
||||||
|
</shape>
|
||||||
|
</inset>
|
||||||
16
briar-android/src/main/res/drawable/button_outline.xml
Normal file
16
briar-android/src/main/res/drawable/button_outline.xml
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item
|
||||||
|
android:bottom="6dp"
|
||||||
|
android:left="4dp"
|
||||||
|
android:right="4dp"
|
||||||
|
android:top="6dp">
|
||||||
|
<shape android:shape="rectangle">
|
||||||
|
<corners android:radius="2dp" />
|
||||||
|
<solid android:color="@android:color/transparent" />
|
||||||
|
<stroke
|
||||||
|
android:width="1dp"
|
||||||
|
android:color="@color/briar_button_outline" />
|
||||||
|
</shape>
|
||||||
|
</item>
|
||||||
|
</layer-list>
|
||||||
@@ -51,12 +51,12 @@
|
|||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="@dimen/margin_xlarge"
|
android:layout_marginTop="@dimen/margin_xlarge"
|
||||||
|
android:text="@string/add_contact_error_two_way"
|
||||||
android:textAppearance="@style/TextAppearance.MaterialComponents.Body1"
|
android:textAppearance="@style/TextAppearance.MaterialComponents.Body1"
|
||||||
app:layout_constraintBottom_toTopOf="@+id/sendFeedback"
|
app:layout_constraintBottom_toTopOf="@+id/sendFeedback"
|
||||||
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/errorTitle"
|
app:layout_constraintTop_toBottomOf="@+id/errorTitle" />
|
||||||
tools:text="error explanation" />
|
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/sendFeedback"
|
android:id="@+id/sendFeedback"
|
||||||
|
|||||||
@@ -56,9 +56,7 @@
|
|||||||
|
|
||||||
<Button
|
<Button
|
||||||
android:id="@+id/feedbackButton"
|
android:id="@+id/feedbackButton"
|
||||||
style="@style/BriarButtonFlat.Positive"
|
style="@style/BriarButtonOutline.Neutral"
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="16dp"
|
android:layout_marginTop="16dp"
|
||||||
android:text="@string/send_feedback"
|
android:text="@string/send_feedback"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
|||||||
@@ -30,9 +30,7 @@
|
|||||||
|
|
||||||
<Button
|
<Button
|
||||||
android:id="@+id/fallbackButton"
|
android:id="@+id/fallbackButton"
|
||||||
style="@style/BriarButtonFlat.Positive"
|
style="@style/BriarButtonOutline.Neutral"
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="16dp"
|
android:layout_marginTop="16dp"
|
||||||
android:text="@string/hotspot_help_fallback_button"
|
android:text="@string/hotspot_help_fallback_button"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
|||||||
@@ -62,13 +62,12 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginHorizontal="16dp"
|
android:layout_marginHorizontal="16dp"
|
||||||
android:layout_marginTop="6dp"
|
android:layout_marginTop="6dp"
|
||||||
android:layout_marginBottom="1dp"
|
|
||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
android:text="@string/hotspot_no_peers_connected"
|
android:text="@string/hotspot_no_peers_connected"
|
||||||
app:layout_constraintTop_toBottomOf="@+id/coordinatorLayout"
|
|
||||||
app:layout_constraintBottom_toTopOf="@+id/connectedButton"
|
app:layout_constraintBottom_toTopOf="@+id/connectedButton"
|
||||||
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/coordinatorLayout" />
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
android:id="@+id/connectedButton"
|
android:id="@+id/connectedButton"
|
||||||
@@ -76,8 +75,8 @@
|
|||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginHorizontal="16dp"
|
android:layout_marginHorizontal="16dp"
|
||||||
android:layout_marginTop="1dp"
|
android:layout_marginTop="6dp"
|
||||||
android:layout_marginBottom="1dp"
|
android:drawablePadding="8dp"
|
||||||
android:text="@string/hotspot_button_connected"
|
android:text="@string/hotspot_button_connected"
|
||||||
app:drawableLeftCompat="@drawable/ic_check_white"
|
app:drawableLeftCompat="@drawable/ic_check_white"
|
||||||
app:drawableStartCompat="@drawable/ic_check_white"
|
app:drawableStartCompat="@drawable/ic_check_white"
|
||||||
@@ -85,16 +84,16 @@
|
|||||||
app:layout_constraintBottom_toTopOf="@+id/stopButton"
|
app:layout_constraintBottom_toTopOf="@+id/stopButton"
|
||||||
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/connectedView"
|
||||||
tools:visibility="visible" />
|
tools:visibility="visible" />
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
android:id="@+id/stopButton"
|
android:id="@+id/stopButton"
|
||||||
style="@style/BriarButtonFlat.Negative"
|
style="@style/BriarButtonOutline.Negative"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginHorizontal="16dp"
|
android:layout_marginHorizontal="16dp"
|
||||||
android:layout_marginTop="6dp"
|
android:layout_marginTop="6dp"
|
||||||
android:layout_marginBottom="2dp"
|
|
||||||
android:drawablePadding="8dp"
|
android:drawablePadding="8dp"
|
||||||
android:text="@string/hotspot_button_stop_sharing"
|
android:text="@string/hotspot_button_stop_sharing"
|
||||||
app:drawableLeftCompat="@drawable/ic_portable_wifi_off"
|
app:drawableLeftCompat="@drawable/ic_portable_wifi_off"
|
||||||
@@ -103,6 +102,7 @@
|
|||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
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/connectedButton"
|
||||||
tools:visibility="visible" />
|
tools:visibility="visible" />
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|||||||
@@ -1,31 +1,51 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent">
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
<ProgressBar
|
<TextView
|
||||||
android:id="@+id/progressBar"
|
android:id="@+id/title"
|
||||||
style="?android:attr/progressBarStyleLarge"
|
style="@style/TextAppearance.AppCompat.Large"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_margin="16dp"
|
android:layout_marginHorizontal="@dimen/margin_xlarge"
|
||||||
app:layout_constraintBottom_toTopOf="@+id/textView"
|
android:layout_marginTop="@dimen/margin_xlarge"
|
||||||
|
android:gravity="center"
|
||||||
|
android:text="@string/mailbox_setup_connecting"
|
||||||
|
app:layout_constraintBottom_toTopOf="@+id/progressBar"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
app:layout_constraintVertical_chainStyle="packed" />
|
app:layout_constraintVertical_chainStyle="packed" />
|
||||||
|
|
||||||
<TextView
|
<ProgressBar
|
||||||
android:id="@+id/textView"
|
android:id="@+id/progressBar"
|
||||||
style="@style/TextAppearance.AppCompat.Large"
|
style="?android:attr/progressBarStyleHorizontal"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_margin="16dp"
|
android:layout_marginHorizontal="@dimen/margin_xlarge"
|
||||||
android:text="@string/mailbox_setup_connecting"
|
android:layout_marginTop="@dimen/margin_xlarge"
|
||||||
|
android:max="100"
|
||||||
|
app:layout_constraintBottom_toTopOf="@+id/info"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/title" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/info"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginHorizontal="@dimen/margin_xlarge"
|
||||||
|
android:layout_marginTop="@dimen/margin_xlarge"
|
||||||
|
android:layout_marginBottom="@dimen/margin_xlarge"
|
||||||
|
android:gravity="center"
|
||||||
|
android:textAppearance="@style/TextAppearance.MaterialComponents.Subtitle1"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
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/progressBar" />
|
app:layout_constraintTop_toBottomOf="@+id/progressBar"
|
||||||
|
tools:text="This may take up to 2 minutes" />
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|||||||
@@ -79,10 +79,11 @@
|
|||||||
|
|
||||||
<org.briarproject.briar.android.view.BriarButton
|
<org.briarproject.briar.android.view.BriarButton
|
||||||
android:id="@+id/button3"
|
android:id="@+id/button3"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_gravity="center"
|
android:layout_gravity="center"
|
||||||
app:buttonStyle="@style/BriarButtonFlat.Negative"
|
android:layout_marginHorizontal="16dp"
|
||||||
|
app:buttonStyle="@style/BriarButtonOutline.Negative"
|
||||||
app:text="@string/mailbox_status_unlink_button" />
|
app:text="@string/mailbox_status_unlink_button" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
tools:context=".android.mailbox.MailboxActivity">
|
tools:context=".android.mailbox.MailboxActivity">
|
||||||
|
|
||||||
@@ -75,10 +75,11 @@
|
|||||||
|
|
||||||
<org.briarproject.briar.android.view.BriarButton
|
<org.briarproject.briar.android.view.BriarButton
|
||||||
android:id="@+id/button1_1_1"
|
android:id="@+id/button1_1_1"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_gravity="center"
|
android:layout_gravity="center"
|
||||||
app:buttonStyle="@style/BriarButtonFlat.Negative"
|
android:layout_marginHorizontal="16dp"
|
||||||
|
app:buttonStyle="@style/BriarButtonOutline.Negative"
|
||||||
app:text="@string/mailbox_status_unlink_button" />
|
app:text="@string/mailbox_status_unlink_button" />
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
@@ -100,10 +101,11 @@
|
|||||||
|
|
||||||
<org.briarproject.briar.android.view.BriarButton
|
<org.briarproject.briar.android.view.BriarButton
|
||||||
android:id="@+id/button1_1_2"
|
android:id="@+id/button1_1_2"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_gravity="center"
|
android:layout_gravity="center"
|
||||||
app:buttonStyle="@style/BriarButtonFlat.Negative"
|
android:layout_marginHorizontal="16dp"
|
||||||
|
app:buttonStyle="@style/BriarButtonOutline.Negative"
|
||||||
app:text="@string/mailbox_status_unlink_button" />
|
app:text="@string/mailbox_status_unlink_button" />
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
@@ -125,10 +127,11 @@
|
|||||||
|
|
||||||
<org.briarproject.briar.android.view.BriarButton
|
<org.briarproject.briar.android.view.BriarButton
|
||||||
android:id="@+id/button1_1_3"
|
android:id="@+id/button1_1_3"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_gravity="center"
|
android:layout_gravity="center"
|
||||||
app:buttonStyle="@style/BriarButtonFlat.Positive"
|
android:layout_marginHorizontal="16dp"
|
||||||
|
app:buttonStyle="@style/BriarButtonOutline.Positive"
|
||||||
app:text="@string/mailbox_status_check_button" />
|
app:text="@string/mailbox_status_check_button" />
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|||||||
@@ -32,7 +32,8 @@
|
|||||||
android:id="@+id/statusTitleView"
|
android:id="@+id/statusTitleView"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_margin="16dp"
|
android:layout_marginHorizontal="16dp"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
android:textAppearance="@style/TextAppearance.MaterialComponents.Headline6"
|
android:textAppearance="@style/TextAppearance.MaterialComponents.Headline6"
|
||||||
app:layout_constrainedWidth="true"
|
app:layout_constrainedWidth="true"
|
||||||
@@ -47,62 +48,65 @@
|
|||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginHorizontal="16dp"
|
android:layout_marginHorizontal="16dp"
|
||||||
android:layout_marginBottom="16dp"
|
android:layout_marginTop="16dp"
|
||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
android:textAppearance="@style/TextAppearance.MaterialComponents.Body1"
|
android:textAppearance="@style/TextAppearance.MaterialComponents.Body1"
|
||||||
android:visibility="gone"
|
android:visibility="gone"
|
||||||
app:layout_constrainedWidth="true"
|
app:layout_constrainedWidth="true"
|
||||||
app:layout_constraintBottom_toTopOf="@+id/checkButton"
|
app:layout_constraintBottom_toTopOf="@+id/statusInfoView"
|
||||||
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/statusTitleView"
|
app:layout_constraintTop_toBottomOf="@+id/statusTitleView"
|
||||||
tools:text="@string/mailbox_status_mailbox_too_old_message"
|
tools:text="@string/mailbox_status_mailbox_too_old_message"
|
||||||
tools:visibility="visible" />
|
tools:visibility="visible" />
|
||||||
|
|
||||||
<org.briarproject.briar.android.view.BriarButton
|
|
||||||
android:id="@+id/checkButton"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginHorizontal="16dp"
|
|
||||||
app:buttonStyle="@style/BriarButtonFlat.Neutral"
|
|
||||||
app:layout_constraintBottom_toTopOf="@+id/statusInfoView"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@+id/statusMessageView"
|
|
||||||
app:text="@string/mailbox_status_check_button" />
|
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/statusInfoView"
|
android:id="@+id/statusInfoView"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_margin="16dp"
|
android:layout_marginHorizontal="16dp"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
app:layout_constraintBottom_toTopOf="@+id/unlinkButton"
|
app:layout_constraintBottom_toTopOf="@+id/checkButton"
|
||||||
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/checkButton"
|
app:layout_constraintTop_toBottomOf="@+id/statusMessageView"
|
||||||
tools:text="@string/mailbox_status_connected_info" />
|
tools:text="@string/mailbox_status_connected_info" />
|
||||||
|
|
||||||
|
<org.briarproject.briar.android.view.BriarButton
|
||||||
|
android:id="@+id/checkButton"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginHorizontal="16dp"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
app:buttonStyle="@style/BriarButtonOutline.Neutral"
|
||||||
|
app:layout_constraintBottom_toTopOf="@+id/wizardButton"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/statusInfoView"
|
||||||
|
app:text="@string/mailbox_status_check_button" />
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
android:id="@+id/wizardButton"
|
android:id="@+id/wizardButton"
|
||||||
style="@style/BriarButtonFlat.Positive"
|
style="@style/BriarButtonOutline.Neutral"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_margin="16dp"
|
android:layout_marginHorizontal="16dp"
|
||||||
|
android:layout_marginTop="6dp"
|
||||||
android:text="@string/mailbox_error_wizard_button"
|
android:text="@string/mailbox_error_wizard_button"
|
||||||
android:visibility="gone"
|
android:visibility="gone"
|
||||||
app:drawableTint="@color/briar_button_text_positive"
|
app:drawableTint="@color/briar_button_text_positive"
|
||||||
app:layout_constraintBottom_toTopOf="@+id/unlinkButton"
|
app:layout_constraintBottom_toTopOf="@+id/unlinkButton"
|
||||||
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/statusInfoView"
|
app:layout_constraintTop_toBottomOf="@+id/checkButton"
|
||||||
app:layout_constraintVertical_bias="0.0"
|
app:layout_constraintVertical_bias="0.0"
|
||||||
tools:visibility="visible" />
|
tools:visibility="visible" />
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
android:id="@+id/unlinkButton"
|
android:id="@+id/unlinkButton"
|
||||||
style="@style/BriarButtonFlat.Negative"
|
style="@style/BriarButtonOutline.Negative"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_margin="16dp"
|
android:layout_margin="16dp"
|
||||||
android:text="@string/mailbox_status_unlink_button"
|
android:text="@string/mailbox_status_unlink_button"
|
||||||
|
|||||||
@@ -1,81 +1,82 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<androidx.core.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:fillViewport="true">
|
android:orientation="vertical">
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
<ScrollView
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content">
|
android:layout_height="0dp"
|
||||||
|
android:layout_weight="1">
|
||||||
|
|
||||||
<ImageView
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
android:id="@+id/iconView"
|
android:layout_width="match_parent"
|
||||||
android:layout_width="@dimen/hero_square"
|
android:layout_height="wrap_content">
|
||||||
android:layout_height="@dimen/hero_square"
|
|
||||||
android:layout_marginHorizontal="@dimen/margin_xlarge"
|
|
||||||
android:layout_marginTop="@dimen/margin_xlarge"
|
|
||||||
app:layout_constraintBottom_toTopOf="@+id/titleView"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
|
||||||
app:layout_constraintVertical_bias="0.25"
|
|
||||||
app:layout_constraintVertical_chainStyle="packed"
|
|
||||||
app:srcCompat="@drawable/transport_tor"
|
|
||||||
app:tint="@color/briar_red_500"
|
|
||||||
tools:ignore="ContentDescription" />
|
|
||||||
|
|
||||||
<TextView
|
<ImageView
|
||||||
android:id="@+id/titleView"
|
android:id="@+id/iconView"
|
||||||
android:layout_width="0dp"
|
android:layout_width="@dimen/hero_square"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="@dimen/hero_square"
|
||||||
android:layout_marginHorizontal="@dimen/margin_xlarge"
|
android:layout_marginHorizontal="@dimen/margin_xlarge"
|
||||||
android:layout_marginTop="@dimen/margin_xlarge"
|
android:layout_marginTop="@dimen/margin_xlarge"
|
||||||
android:gravity="center"
|
app:layout_constraintBottom_toTopOf="@+id/titleView"
|
||||||
android:text="@string/offline"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
android:textAppearance="@style/TextAppearance.MaterialComponents.Headline5"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintBottom_toTopOf="@+id/textView"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintVertical_bias="0.25"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintVertical_chainStyle="packed"
|
||||||
app:layout_constraintTop_toBottomOf="@+id/iconView" />
|
app:srcCompat="@drawable/transport_tor"
|
||||||
|
app:tint="@color/briar_red_500"
|
||||||
|
tools:ignore="ContentDescription" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/textView"
|
android:id="@+id/titleView"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginHorizontal="@dimen/margin_xlarge"
|
android:layout_marginHorizontal="@dimen/margin_xlarge"
|
||||||
android:layout_marginTop="@dimen/margin_xlarge"
|
android:layout_marginTop="@dimen/margin_xlarge"
|
||||||
android:layout_marginBottom="@dimen/margin_large"
|
android:gravity="center"
|
||||||
android:text="@string/tor_offline_description"
|
android:text="@string/offline"
|
||||||
android:textAppearance="@style/TextAppearance.MaterialComponents.Body1"
|
android:textAppearance="@style/TextAppearance.MaterialComponents.Headline5"
|
||||||
app:layout_constraintBottom_toTopOf="@+id/checkButton"
|
app:layout_constraintBottom_toTopOf="@+id/textView"
|
||||||
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/titleView" />
|
app:layout_constraintTop_toBottomOf="@+id/iconView" />
|
||||||
|
|
||||||
<Button
|
<TextView
|
||||||
android:id="@+id/checkButton"
|
android:id="@+id/textView"
|
||||||
style="@style/BriarButtonFlat.Neutral"
|
android:layout_width="0dp"
|
||||||
android:layout_width="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_margin="@dimen/margin_large"
|
||||||
android:layout_margin="@dimen/margin_large"
|
android:layout_marginHorizontal="@dimen/margin_xlarge"
|
||||||
android:text="@string/tor_offline_button_check"
|
android:text="@string/tor_offline_description"
|
||||||
app:layout_constraintBottom_toTopOf="@+id/button"
|
android:textAppearance="@style/TextAppearance.MaterialComponents.Body1"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent" />
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/titleView" />
|
||||||
|
|
||||||
<Button
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
android:id="@+id/button"
|
|
||||||
style="@style/BriarButton"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_margin="@dimen/margin_large"
|
|
||||||
android:text="@string/try_again_button"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent" />
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</ScrollView>
|
||||||
|
|
||||||
</androidx.core.widget.NestedScrollView>
|
<Button
|
||||||
|
android:id="@+id/checkButton"
|
||||||
|
style="@style/BriarButtonOutline.Neutral"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginHorizontal="16dp"
|
||||||
|
android:text="@string/tor_offline_button_check" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/button"
|
||||||
|
style="@style/BriarButton"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginHorizontal="16dp"
|
||||||
|
android:layout_marginTop="6dp"
|
||||||
|
android:text="@string/try_again_button" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|||||||
@@ -33,6 +33,7 @@
|
|||||||
android:paddingBottom="@dimen/listitem_vertical_margin"
|
android:paddingBottom="@dimen/listitem_vertical_margin"
|
||||||
android:textColor="?android:attr/textColorSecondary"
|
android:textColor="?android:attr/textColorSecondary"
|
||||||
android:textSize="@dimen/text_size_small"
|
android:textSize="@dimen/text_size_small"
|
||||||
|
android:textColorLink="@color/briar_text_link"
|
||||||
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/authorView"
|
app:layout_constraintTop_toBottomOf="@+id/authorView"
|
||||||
|
|||||||
@@ -41,6 +41,7 @@
|
|||||||
android:layout_marginRight="@dimen/message_bubble_padding_sides_inner"
|
android:layout_marginRight="@dimen/message_bubble_padding_sides_inner"
|
||||||
android:layout_marginBottom="@dimen/message_bubble_padding_bottom_inner"
|
android:layout_marginBottom="@dimen/message_bubble_padding_bottom_inner"
|
||||||
android:textColor="?android:attr/textColorPrimary"
|
android:textColor="?android:attr/textColorPrimary"
|
||||||
|
android:textColorLink="@color/briar_text_link"
|
||||||
app:layout_constrainedWidth="true"
|
app:layout_constrainedWidth="true"
|
||||||
app:layout_constraintBottom_toTopOf="@+id/statusLayout"
|
app:layout_constraintBottom_toTopOf="@+id/statusLayout"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
|||||||
@@ -49,6 +49,7 @@
|
|||||||
android:layout_marginRight="@dimen/message_bubble_padding_sides_inner"
|
android:layout_marginRight="@dimen/message_bubble_padding_sides_inner"
|
||||||
android:layout_marginBottom="@dimen/message_bubble_padding_bottom_inner"
|
android:layout_marginBottom="@dimen/message_bubble_padding_bottom_inner"
|
||||||
android:textColor="@color/briar_text_primary_inverse"
|
android:textColor="@color/briar_text_primary_inverse"
|
||||||
|
android:textColorLink="@color/briar_text_link_inverse"
|
||||||
app:layout_constrainedWidth="true"
|
app:layout_constrainedWidth="true"
|
||||||
app:layout_constraintBottom_toTopOf="@+id/statusLayout"
|
app:layout_constraintBottom_toTopOf="@+id/statusLayout"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
|||||||
@@ -21,6 +21,7 @@
|
|||||||
android:layout_marginRight="@dimen/message_bubble_margin_non_tail"
|
android:layout_marginRight="@dimen/message_bubble_margin_non_tail"
|
||||||
android:background="@drawable/msg_in_top"
|
android:background="@drawable/msg_in_top"
|
||||||
android:elevation="@dimen/message_bubble_elevation"
|
android:elevation="@dimen/message_bubble_elevation"
|
||||||
|
android:textColorLink="@color/briar_text_link"
|
||||||
tools:text="Short message"
|
tools:text="Short message"
|
||||||
tools:visibility="visible" />
|
tools:visibility="visible" />
|
||||||
|
|
||||||
|
|||||||
@@ -23,6 +23,7 @@
|
|||||||
android:background="@drawable/msg_out_top"
|
android:background="@drawable/msg_out_top"
|
||||||
android:elevation="@dimen/message_bubble_elevation"
|
android:elevation="@dimen/message_bubble_elevation"
|
||||||
android:textColor="@color/briar_text_primary_inverse"
|
android:textColor="@color/briar_text_primary_inverse"
|
||||||
|
android:textColorLink="@color/briar_text_link_inverse"
|
||||||
tools:text="This is a long long long message that spans over several lines.\n\nIt ends here."
|
tools:text="This is a long long long message that spans over several lines.\n\nIt ends here."
|
||||||
tools:visibility="visible" />
|
tools:visibility="visible" />
|
||||||
|
|
||||||
|
|||||||
@@ -22,6 +22,7 @@
|
|||||||
android:background="@drawable/msg_in_top"
|
android:background="@drawable/msg_in_top"
|
||||||
android:elevation="@dimen/message_bubble_elevation"
|
android:elevation="@dimen/message_bubble_elevation"
|
||||||
android:textColor="?android:attr/textColorPrimary"
|
android:textColor="?android:attr/textColorPrimary"
|
||||||
|
android:textColorLink="@color/briar_text_link"
|
||||||
tools:text="Short message"
|
tools:text="Short message"
|
||||||
tools:visibility="visible" />
|
tools:visibility="visible" />
|
||||||
|
|
||||||
|
|||||||
@@ -114,6 +114,7 @@
|
|||||||
android:textColor="?android:attr/textColorPrimary"
|
android:textColor="?android:attr/textColorPrimary"
|
||||||
android:textIsSelectable="true"
|
android:textIsSelectable="true"
|
||||||
android:textSize="@dimen/text_size_medium"
|
android:textSize="@dimen/text_size_medium"
|
||||||
|
android:textColorLink="@color/briar_text_link"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
|||||||
@@ -224,6 +224,7 @@
|
|||||||
<string name="menu_contact">Контакт</string>
|
<string name="menu_contact">Контакт</string>
|
||||||
<!--Adding Contacts-->
|
<!--Adding Contacts-->
|
||||||
<string name="add_contact_title">Добавяне на контакт на живо</string>
|
<string name="add_contact_title">Добавяне на контакт на живо</string>
|
||||||
|
<string name="add_contact_error_two_way">Сканирахте ли взаимно кодовете си за QR?</string>
|
||||||
<string name="face_to_face">Трябва да се срещнете лично с човека, чиито контакт искате да добавите.\n\nТака никой не може да се представи за вас или да чете съобщенията ви в бъдеще.</string>
|
<string name="face_to_face">Трябва да се срещнете лично с човека, чиито контакт искате да добавите.\n\nТака никой не може да се представи за вас или да чете съобщенията ви в бъдеще.</string>
|
||||||
<string name="continue_button">Напред</string>
|
<string name="continue_button">Напред</string>
|
||||||
<string name="try_again_button">Нов опит</string>
|
<string name="try_again_button">Нов опит</string>
|
||||||
@@ -239,6 +240,7 @@
|
|||||||
<string name="authenticating_with_device">Удостоверяване с устройство\u2026</string>
|
<string name="authenticating_with_device">Удостоверяване с устройство\u2026</string>
|
||||||
<string name="connection_error_title">Не може да бъде установена връзка с контакта</string>
|
<string name="connection_error_title">Не може да бъде установена връзка с контакта</string>
|
||||||
<string name="connection_error_feedback">Ако проблемът продължава, <a href="feedback">изпратете обратна връзка</a>, за да ни помогнете да подобрим приложението.</string>
|
<string name="connection_error_feedback">Ако проблемът продължава, <a href="feedback">изпратете обратна връзка</a>, за да ни помогнете да подобрим приложението.</string>
|
||||||
|
<string name="info_both_must_scan">Трябва взаимно да сканирате кодовете си за QR</string>
|
||||||
<!--Adding Contacts Remotely-->
|
<!--Adding Contacts Remotely-->
|
||||||
<string name="add_contact_remotely_title_case">Добавяне на контакт отдалечено</string>
|
<string name="add_contact_remotely_title_case">Добавяне на контакт отдалечено</string>
|
||||||
<string name="add_contact_nearby_title">Добавяне на контакт на живо</string>
|
<string name="add_contact_nearby_title">Добавяне на контакт на живо</string>
|
||||||
@@ -296,6 +298,7 @@
|
|||||||
<string name="different_person_button">Не</string>
|
<string name="different_person_button">Не</string>
|
||||||
<string name="duplicate_link_dialog_text_3">%1$s и %2$s са изпратили еднакви препратки.\n\nЕдиният от двамата вероятно се опитва да разбере кои са контактите ви.\n\nНе им споделяйте, че сте получили същата препратка от друг човек.</string>
|
<string name="duplicate_link_dialog_text_3">%1$s и %2$s са изпратили еднакви препратки.\n\nЕдиният от двамата вероятно се опитва да разбере кои са контактите ви.\n\nНе им споделяйте, че сте получили същата препратка от друг човек.</string>
|
||||||
<string name="pending_contact_updated_toast">Обновена чакаща заявка за контакт</string>
|
<string name="pending_contact_updated_toast">Обновена чакаща заявка за контакт</string>
|
||||||
|
<string name="info_both_must_enter_links">Трябва взаимно да добавите препратките си</string>
|
||||||
<!--Peer trust levels-->
|
<!--Peer trust levels-->
|
||||||
<string name="peer_trust_level_unverified">Непроверен контакт</string>
|
<string name="peer_trust_level_unverified">Непроверен контакт</string>
|
||||||
<string name="peer_trust_level_verified">Проверен контакт</string>
|
<string name="peer_trust_level_verified">Проверен контакт</string>
|
||||||
@@ -329,6 +332,7 @@
|
|||||||
<string name="connect_via_bluetooth_intro">В случай, че връзка чрез Bluetooth не се установява автоматично можете да използвате този екран, за да се свържете ръчно.\n\nВие и контакта трябва да бъдете близо един до друг.\n\nДвамата трябва да докоснете бутона „Старт“ едновременно.</string>
|
<string name="connect_via_bluetooth_intro">В случай, че връзка чрез Bluetooth не се установява автоматично можете да използвате този екран, за да се свържете ръчно.\n\nВие и контакта трябва да бъдете близо един до друг.\n\nДвамата трябва да докоснете бутона „Старт“ едновременно.</string>
|
||||||
<string name="connect_via_bluetooth_already_discovering">Осъществява св връзка през Bluetooth. По-късно опитайте отново.</string>
|
<string name="connect_via_bluetooth_already_discovering">Осъществява св връзка през Bluetooth. По-късно опитайте отново.</string>
|
||||||
<string name="connect_via_bluetooth_no_location_permission">Не може да продължи без разрешение за местоположение</string>
|
<string name="connect_via_bluetooth_no_location_permission">Не може да продължи без разрешение за местоположение</string>
|
||||||
|
<string name="connect_via_bluetooth_no_bluetooth_permission">Не може да продължи без разрешение за устройства наблизо</string>
|
||||||
<string name="connect_via_bluetooth_start">Свързване чрез Bluetooth…</string>
|
<string name="connect_via_bluetooth_start">Свързване чрез Bluetooth…</string>
|
||||||
<string name="connect_via_bluetooth_success">Успешно е създaдена връзка чрез Bluetooth</string>
|
<string name="connect_via_bluetooth_success">Успешно е създaдена връзка чрез Bluetooth</string>
|
||||||
<string name="connect_via_bluetooth_error">Не може да бъде установена връзка чрез Bluetooth.</string>
|
<string name="connect_via_bluetooth_error">Не може да бъде установена връзка чрез Bluetooth.</string>
|
||||||
@@ -586,10 +590,8 @@
|
|||||||
\nСлед това свържете Mailbox с Briar чрез сканиране на кода за QR от приложението Mailbox.</string>
|
\nСлед това свържете Mailbox с Briar чрез сканиране на кода за QR от приложението Mailbox.</string>
|
||||||
<string name="mailbox_setup_download_link">Споделяне на препратка за изтегляне</string>
|
<string name="mailbox_setup_download_link">Споделяне на препратка за изтегляне</string>
|
||||||
<string name="mailbox_setup_button_scan">Сканиране на кода за QR на пощенска кутия</string>
|
<string name="mailbox_setup_button_scan">Сканиране на кода за QR на пощенска кутия</string>
|
||||||
<string name="permission_camera_qr_denied_body">Отказахте достъп до камерата, но тя е необходима за добавянето на контакти.\n\nОбмислете дали да не дадете разрешение.</string>
|
<string name="permission_camera_qr_denied_body">Отказахте достъп до камерата, но достъп е необходим за сканиране на кодове за QR.\n\nОбмислете дали да не дадете разрешение.</string>
|
||||||
<string name="mailbox_setup_connecting">Свързване…</string>
|
<!--This string is shown when connecting to a Mailbox for the first time. The placeholder will be replaced with a duration, e.g. "2 minutes".-->
|
||||||
<string name="mailbox_setup_qr_code_wrong_title">Грешен код на QR</string>
|
|
||||||
<string name="mailbox_setup_qr_code_wrong_description">Сканираният код е недействителен. Отворете приложението Briar Malibox на устройството, на което е инсталирано и сканирайте кода, който то предостави.</string>
|
|
||||||
<string name="mailbox_setup_already_paired_title">Пощенската кутия е вече свързана</string>
|
<string name="mailbox_setup_already_paired_title">Пощенската кутия е вече свързана</string>
|
||||||
<string name="mailbox_setup_already_paired_description">Прекъснете връзката с пощенската кутия от другото устройство и опитайте отново.</string>
|
<string name="mailbox_setup_already_paired_description">Прекъснете връзката с пощенската кутия от другото устройство и опитайте отново.</string>
|
||||||
<string name="mailbox_setup_io_error_title">Грешка при свързване</string>
|
<string name="mailbox_setup_io_error_title">Грешка при свързване</string>
|
||||||
@@ -718,14 +720,20 @@
|
|||||||
<string name="permission_camera_title">Разрешение за камера</string>
|
<string name="permission_camera_title">Разрешение за камера</string>
|
||||||
<string name="permission_camera_request_body">За да сканира кода за QR, Briar трябва да използва камерата.</string>
|
<string name="permission_camera_request_body">За да сканира кода за QR, Briar трябва да използва камерата.</string>
|
||||||
<string name="permission_location_title">Разрешение за местоположение</string>
|
<string name="permission_location_title">Разрешение за местоположение</string>
|
||||||
<string name="permission_location_request_body">За да открива устройства чрез Bluetooth, на Briar му е необходимо разрешение за достъп до местоположението.\n\nBriar не го пази и не го споделя с никого.</string>
|
<string name="permission_location_request_body">За да открива устройства чрез Bluetooth, Briar се нуждае от разрешение за достъп до местоположението.\n\nBriar не го пази и не го споделя с никого.</string>
|
||||||
<string name="permission_camera_location_title">Камера и местоположение</string>
|
<string name="permission_camera_location_title">Камера и местоположение</string>
|
||||||
<string name="permission_camera_location_request_body">За да сканира кодове за QR, на Briar му е необходимо разрешение за достъп до камерата.\n\nЗа да открива устройства чрез Bluetooth, на Briar му е необходимо разрешение за достъп до местоположението.\n\nBriar не го пази и не го споделя с никого.</string>
|
<string name="permission_camera_location_request_body">За да сканира кода за QR, Briar трябва да използва камерата.\n\nЗа да открива устройства чрез Bluetooth, Briar трябва да има достъп до местоположението.\n\nBriar не го пази и не го споделя с никого.</string>
|
||||||
<string name="permission_camera_denied_body">Отказахте достъп до камерата, но тя е необходима за добавянето на контакти.\n\nОбмислете дали да не дадете разрешение.</string>
|
<string name="permission_camera_bluetooth_title">Камера и устройства наблизо</string>
|
||||||
<string name="permission_location_denied_body">Отказахте достъп до местоположението, но то е необходимо за откриване на устройства чрез Bluetooth.\n\nОбмислете дали да не дадете разрешение.</string>
|
<string name="permission_camera_bluetooth_request_body">За да сканира кода за QR, Briar трябва да използва камерата.\n\nЗа да открива устройства чрез Bluetooth, Briar се нуждае от достъп до местоположението. Briar се нуждае от разрешение да намира и да се свързва с устройства наблизо.</string>
|
||||||
|
<string name="permission_camera_denied_body">Отказахте достъп до камерата, но достъп е необходим за добавяне на контакти.\n\nОбмислете дали да не дадете разрешение.</string>
|
||||||
|
<string name="permission_location_denied_body">Отказахте достъп до местоположението, но достъп е необходим за откриване на устройства чрез Bluetooth.\n\nОбмислете дали да не дадете разрешение.</string>
|
||||||
<string name="permission_location_setting_title">Настройки на местоположението</string>
|
<string name="permission_location_setting_title">Настройки на местоположението</string>
|
||||||
<string name="permission_location_setting_body">Местоположението на устройството ви трябва да е включено, за да бъдат откривани устройства чрез Bluetooth. За да продължите включете местоположението. След това можете да го изключите.</string>
|
<string name="permission_location_setting_body">За да бъдат откривани устройства чрез Bluetooth местоположението на устройството ви трябва да е включено. За да продължите включете местоположението. След това можете да го изключите.</string>
|
||||||
|
<string name="permission_location_setting_hotspot_body">За да създаде безжична точка за достъп местоположението на устройството ви трябва да е включено. За да продължите включете местоположението. След това можете да го изключите.</string>
|
||||||
<string name="permission_location_setting_button">Включване на местеположение</string>
|
<string name="permission_location_setting_button">Включване на местеположение</string>
|
||||||
|
<string name="permission_bluetooth_title">Разрешение за устройства наблизо</string>
|
||||||
|
<string name="permission_bluetooth_body">За да извършва разговори чрез Bluetooth, Briar се нуждае от разрешение да намира и да се свързва с устройства наблизо.</string>
|
||||||
|
<string name="permission_bluetooth_denied_body">Отказахте достъп до устройства наблизо, но достъп е необходим за използване на Bluetooth.\n\nОбмислете дали да не дадете разрешение.</string>
|
||||||
<string name="qr_code">Код за QR</string>
|
<string name="qr_code">Код за QR</string>
|
||||||
<string name="show_qr_code_fullscreen">Код за QR на цял екран</string>
|
<string name="show_qr_code_fullscreen">Код за QR на цял екран</string>
|
||||||
<!--App Locking-->
|
<!--App Locking-->
|
||||||
@@ -747,8 +755,10 @@
|
|||||||
<string name="hotspot_notification_channel_title">Безжична точка за достъп</string>
|
<string name="hotspot_notification_channel_title">Безжична точка за достъп</string>
|
||||||
<string name="hotspot_notification_title">Споделяне на Briar извън мрежа</string>
|
<string name="hotspot_notification_title">Споделяне на Briar извън мрежа</string>
|
||||||
<string name="hotspot_button_connected">Напред</string>
|
<string name="hotspot_button_connected">Напред</string>
|
||||||
<string name="permission_hotspot_location_request_body">За да създаде безжична точка за достъп, на Briar му е необходимо разрешение за достъп до местоположението.\n\nBriar не го пази и не го споделя с никого.</string>
|
<string name="permission_hotspot_location_request_body">За да създаде безжична точка за достъп, Briar се нуждае от разрешение за достъп до местоположението.\n\nBriar не го пази и не го споделя с никого.</string>
|
||||||
<string name="permission_hotspot_location_denied_body">Отказахте достъп до местоположението, но то е необходимо за създаване на безжична точка за достъп.\n\nОбмислете дали да не дадете разрешение.</string>
|
<string name="permission_hotspot_location_request_precise_body">За да създаде безжична точка за достъп, Briar се нуждае от разрешение за достъп до точното местоположение.\n\nBriar не го пази и не го споделя с никого.</string>
|
||||||
|
<string name="permission_hotspot_location_denied_body">Отказахте достъп до местоположението, но достъп е необходим за създаване на безжична точка за достъп.\n\nОбмислете дали да не дадете разрешение.</string>
|
||||||
|
<string name="permission_hotspot_location_denied_precise_body">Отказахте достъп до точното местоположение, но достъп е необходим за създаване на безжична точка за достъп.\n\nОбмислете дали да не дадете разрешение.</string>
|
||||||
<string name="wifi_settings_title">Настройки на Wi-Fi</string>
|
<string name="wifi_settings_title">Настройки на Wi-Fi</string>
|
||||||
<string name="wifi_settings_request_enable_body">За да създаде безжична точка за достъп, Briar се нуждае от безжична мрежа. Включете Wi-Fi.</string>
|
<string name="wifi_settings_request_enable_body">За да създаде безжична точка за достъп, Briar се нуждае от безжична мрежа. Включете Wi-Fi.</string>
|
||||||
<string name="hotspot_tab_manual">Ръчно</string>
|
<string name="hotspot_tab_manual">Ръчно</string>
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user