mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-12 10:49:06 +01:00
Compare commits
27 Commits
alpha-1.5.
...
release-1.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1f1a97f62d | ||
|
|
7a33d26533 | ||
|
|
f2c85f37be | ||
|
|
8e3fa872fd | ||
|
|
0d1e81ebdb | ||
|
|
bded4e7bc8 | ||
|
|
bf1a5cf218 | ||
|
|
dd7a638984 | ||
|
|
942222131e | ||
|
|
643757e407 | ||
|
|
7c530ad7a3 | ||
|
|
23b2dfa4a8 | ||
|
|
ce10e6770f | ||
|
|
b88dbee881 | ||
|
|
0ca21ad4c0 | ||
|
|
a14f62dcc3 | ||
|
|
f0c1ebcc1b | ||
|
|
4a4b04bec3 | ||
|
|
6f57ec8281 | ||
|
|
0eb0bbdc99 | ||
|
|
76344344d2 | ||
|
|
fbc32830bd | ||
|
|
145117a1dc | ||
|
|
6ed55bcd7d | ||
|
|
f8015272f4 | ||
|
|
462f57c966 | ||
|
|
8d20c5d8b8 |
@@ -11,10 +11,10 @@ android {
|
||||
}
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion 16
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 31
|
||||
versionCode 10501
|
||||
versionName "1.5.1"
|
||||
versionCode 10503
|
||||
versionName "1.5.3"
|
||||
consumerProguardFiles 'proguard-rules.txt'
|
||||
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
@@ -40,7 +40,7 @@ configurations {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
api 'org.briarproject:dont-kill-me-lib:0.2.6'
|
||||
api 'org.briarproject:dont-kill-me-lib:0.2.7'
|
||||
|
||||
// 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,
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
package org.briarproject.android.dontkillmelib.wakelock;
|
||||
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
|
||||
@Module
|
||||
public class AndroidWakeLockModule {
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
AndroidWakeLockManager provideWakeLockManager(
|
||||
AndroidWakeLockManagerImpl wakeLockManager) {
|
||||
return wakeLockManager;
|
||||
}
|
||||
}
|
||||
@@ -1,16 +1,16 @@
|
||||
package org.briarproject.bramble;
|
||||
|
||||
import org.briarproject.android.dontkillmelib.wakelock.AndroidWakeLockModule;
|
||||
import org.briarproject.bramble.battery.AndroidBatteryModule;
|
||||
import org.briarproject.bramble.io.DnsModule;
|
||||
import org.briarproject.bramble.network.AndroidNetworkModule;
|
||||
import org.briarproject.bramble.plugin.tor.CircumventionModule;
|
||||
import org.briarproject.bramble.reporting.ReportingModule;
|
||||
import org.briarproject.bramble.socks.SocksModule;
|
||||
import org.briarproject.bramble.system.AndroidSystemModule;
|
||||
import org.briarproject.bramble.system.AndroidTaskSchedulerModule;
|
||||
import org.briarproject.bramble.system.AndroidWakeLockModule;
|
||||
import org.briarproject.bramble.system.AndroidWakefulIoExecutorModule;
|
||||
import org.briarproject.bramble.system.DefaultThreadFactoryModule;
|
||||
import org.briarproject.onionwrapper.CircumventionModule;
|
||||
|
||||
import dagger.Module;
|
||||
|
||||
|
||||
@@ -20,7 +20,6 @@ import javax.annotation.Nullable;
|
||||
import javax.annotation.concurrent.GuardedBy;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static android.os.Build.VERSION.SDK_INT;
|
||||
import static java.util.Arrays.asList;
|
||||
import static java.util.logging.Level.INFO;
|
||||
import static org.briarproject.bramble.util.IoUtils.deleteFileOrDir;
|
||||
@@ -105,15 +104,11 @@ class AndroidAccountManager extends AccountManagerImpl
|
||||
}
|
||||
files.add(appContext.getFilesDir());
|
||||
addIfNotNull(files, appContext.getExternalCacheDir());
|
||||
if (SDK_INT >= 19) {
|
||||
for (File file : appContext.getExternalCacheDirs()) {
|
||||
addIfNotNull(files, file);
|
||||
}
|
||||
for (File file : appContext.getExternalCacheDirs()) {
|
||||
addIfNotNull(files, file);
|
||||
}
|
||||
if (SDK_INT >= 21) {
|
||||
for (File file : appContext.getExternalMediaDirs()) {
|
||||
addIfNotNull(files, file);
|
||||
}
|
||||
for (File file : appContext.getExternalMediaDirs()) {
|
||||
addIfNotNull(files, file);
|
||||
}
|
||||
// Clear the cache directory but don't delete it
|
||||
File cacheDir = appContext.getCacheDir();
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package org.briarproject.bramble.plugin.bluetooth;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.Application;
|
||||
import android.bluetooth.BluetoothAdapter;
|
||||
import android.bluetooth.BluetoothDevice;
|
||||
@@ -49,7 +50,6 @@ import static android.bluetooth.BluetoothAdapter.STATE_ON;
|
||||
import static android.bluetooth.BluetoothDevice.ACTION_FOUND;
|
||||
import static android.bluetooth.BluetoothDevice.DEVICE_TYPE_LE;
|
||||
import static android.bluetooth.BluetoothDevice.EXTRA_DEVICE;
|
||||
import static android.os.Build.VERSION.SDK_INT;
|
||||
import static java.util.Collections.shuffle;
|
||||
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
||||
import static java.util.logging.Level.INFO;
|
||||
@@ -60,6 +60,7 @@ import static org.briarproject.bramble.util.PrivacyUtils.scrubMacAddress;
|
||||
|
||||
@MethodsNotNullByDefault
|
||||
@ParametersNotNullByDefault
|
||||
@SuppressLint("MissingPermission")
|
||||
class AndroidBluetoothPlugin extends
|
||||
AbstractBluetoothPlugin<BluetoothSocket, BluetoothServerSocket> {
|
||||
|
||||
@@ -253,7 +254,7 @@ class AndroidBluetoothPlugin extends
|
||||
} else if (ACTION_FOUND.equals(action)) {
|
||||
BluetoothDevice d = i.getParcelableExtra(EXTRA_DEVICE);
|
||||
// Ignore Bluetooth LE devices
|
||||
if (SDK_INT < 18 || d.getType() != DEVICE_TYPE_LE) {
|
||||
if (d.getType() != DEVICE_TYPE_LE) {
|
||||
String address = d.getAddress();
|
||||
if (LOG.isLoggable(INFO))
|
||||
LOG.info("Discovered " +
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package org.briarproject.bramble.plugin.tcp;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.app.Application;
|
||||
import android.net.ConnectivityManager;
|
||||
import android.net.LinkAddress;
|
||||
@@ -37,7 +36,6 @@ import javax.net.SocketFactory;
|
||||
import static android.content.Context.CONNECTIVITY_SERVICE;
|
||||
import static android.content.Context.WIFI_SERVICE;
|
||||
import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
|
||||
import static android.os.Build.VERSION.SDK_INT;
|
||||
import static java.util.Collections.emptyList;
|
||||
import static java.util.Collections.list;
|
||||
import static java.util.Collections.singletonList;
|
||||
@@ -118,7 +116,7 @@ class AndroidLanTcpPlugin extends LanTcpPlugin {
|
||||
// If there's no wifi IPv4 address, we might be a client on an
|
||||
// IPv6-only wifi network. We can only detect this on API 21+
|
||||
if (wifi == null) {
|
||||
return SDK_INT >= 21 ? getWifiClientIpv6Address() : null;
|
||||
return getWifiClientIpv6Address();
|
||||
}
|
||||
// Use the wifi IPv4 address to determine which interface's IPv6
|
||||
// address we should return (if the interface has a suitable address)
|
||||
@@ -172,7 +170,6 @@ class AndroidLanTcpPlugin extends LanTcpPlugin {
|
||||
* Returns a link-local IPv6 address for the wifi client interface, or null
|
||||
* if there's no such interface or it doesn't have a suitable address.
|
||||
*/
|
||||
@TargetApi(21)
|
||||
@Nullable
|
||||
private InetAddress getWifiClientIpv6Address() {
|
||||
// https://issuetracker.google.com/issues/175055271
|
||||
@@ -234,7 +231,6 @@ class AndroidLanTcpPlugin extends LanTcpPlugin {
|
||||
// On API 21 and later, a socket that is not created with the wifi
|
||||
// network's socket factory may try to connect via another network
|
||||
private SocketFactory getSocketFactory() {
|
||||
if (SDK_INT < 21) return SocketFactory.getDefault();
|
||||
// https://issuetracker.google.com/issues/175055271
|
||||
try {
|
||||
for (Network net : connectivityManager.getAllNetworks()) {
|
||||
@@ -302,7 +298,7 @@ class AndroidLanTcpPlugin extends LanTcpPlugin {
|
||||
Pair<InetAddress, Boolean> wifi = getWifiIpv4Address();
|
||||
// If there's no wifi IPv4 address, we might be a client on an
|
||||
// IPv6-only wifi network. We can only detect this on API 21+
|
||||
if (wifi == null && SDK_INT >= 21) {
|
||||
if (wifi == null) {
|
||||
InetAddress ipv6 = getWifiClientIpv6Address();
|
||||
if (ipv6 != null) return new Pair<>(ipv6, false);
|
||||
}
|
||||
|
||||
@@ -16,11 +16,11 @@ import org.briarproject.bramble.api.plugin.TorControlPort;
|
||||
import org.briarproject.bramble.api.plugin.TorDirectory;
|
||||
import org.briarproject.bramble.api.plugin.TorSocksPort;
|
||||
import org.briarproject.bramble.api.system.Clock;
|
||||
import org.briarproject.bramble.api.system.LocationUtils;
|
||||
import org.briarproject.bramble.api.system.WakefulIoExecutor;
|
||||
import org.briarproject.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.onionwrapper.AndroidTorWrapper;
|
||||
import org.briarproject.onionwrapper.CircumventionProvider;
|
||||
import org.briarproject.onionwrapper.LocationUtils;
|
||||
import org.briarproject.onionwrapper.TorWrapper;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
@@ -1,72 +0,0 @@
|
||||
package org.briarproject.bramble.system;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.Application;
|
||||
import android.content.Context;
|
||||
import android.telephony.TelephonyManager;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import org.briarproject.bramble.api.system.LocationUtils;
|
||||
import org.briarproject.nullsafety.NotNullByDefault;
|
||||
|
||||
import java.util.Locale;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static android.content.Context.TELEPHONY_SERVICE;
|
||||
|
||||
@NotNullByDefault
|
||||
class AndroidLocationUtils implements LocationUtils {
|
||||
|
||||
private static final Logger LOG =
|
||||
Logger.getLogger(AndroidLocationUtils.class.getName());
|
||||
|
||||
private final Context appContext;
|
||||
|
||||
@Inject
|
||||
AndroidLocationUtils(Application app) {
|
||||
appContext = app.getApplicationContext();
|
||||
}
|
||||
|
||||
/**
|
||||
* This guesses the current country from the first of these sources that
|
||||
* succeeds (also in order of likelihood of being correct):
|
||||
*
|
||||
* <ul>
|
||||
* <li>Phone network. This works even when no SIM card is inserted, or a
|
||||
* foreign SIM card is inserted.</li>
|
||||
* <li>SIM card. This is only an heuristic and assumes the user is not
|
||||
* roaming.</li>
|
||||
* <li>User locale. This is an even worse heuristic.</li>
|
||||
* </ul>
|
||||
*
|
||||
* Note: this is very similar to <a href="https://android.googlesource.com/platform/frameworks/base/+/cd92588%5E/location/java/android/location/CountryDetector.java">
|
||||
* this API</a> except it seems that Google doesn't want us to use it for
|
||||
* some reason - both that class and {@code Context.COUNTRY_CODE} are
|
||||
* annotated {@code @hide}.
|
||||
*/
|
||||
@Override
|
||||
@SuppressLint("DefaultLocale")
|
||||
public String getCurrentCountry() {
|
||||
String countryCode = getCountryFromPhoneNetwork();
|
||||
if (!TextUtils.isEmpty(countryCode)) return countryCode.toUpperCase();
|
||||
LOG.info("Falling back to SIM card country");
|
||||
countryCode = getCountryFromSimCard();
|
||||
if (!TextUtils.isEmpty(countryCode)) return countryCode.toUpperCase();
|
||||
LOG.info("Falling back to user-defined locale");
|
||||
return Locale.getDefault().getCountry();
|
||||
}
|
||||
|
||||
private String getCountryFromPhoneNetwork() {
|
||||
Object o = appContext.getSystemService(TELEPHONY_SERVICE);
|
||||
TelephonyManager tm = (TelephonyManager) o;
|
||||
return tm == null ? "" : tm.getNetworkCountryIso();
|
||||
}
|
||||
|
||||
private String getCountryFromSimCard() {
|
||||
Object o = appContext.getSystemService(TELEPHONY_SERVICE);
|
||||
TelephonyManager tm = (TelephonyManager) o;
|
||||
return tm == null ? "" : tm.getSimCountryIso();
|
||||
}
|
||||
}
|
||||
@@ -31,8 +31,6 @@ import static android.provider.Settings.Secure.ANDROID_ID;
|
||||
@NotNullByDefault
|
||||
class AndroidSecureRandomProvider extends UnixSecureRandomProvider {
|
||||
|
||||
private static final int SEED_LENGTH = 32;
|
||||
|
||||
private final Context appContext;
|
||||
|
||||
@Inject
|
||||
@@ -72,27 +70,6 @@ class AndroidSecureRandomProvider extends UnixSecureRandomProvider {
|
||||
// Silence strict mode
|
||||
StrictMode.ThreadPolicy tp = StrictMode.allowThreadDiskWrites();
|
||||
super.writeSeed();
|
||||
if (SDK_INT <= 18) applyOpenSslFix();
|
||||
StrictMode.setThreadPolicy(tp);
|
||||
}
|
||||
|
||||
// Based on https://android-developers.googleblog.com/2013/08/some-securerandom-thoughts.html
|
||||
private void applyOpenSslFix() {
|
||||
byte[] seed = new UnixSecureRandomSpi().engineGenerateSeed(
|
||||
SEED_LENGTH);
|
||||
try {
|
||||
// Seed the OpenSSL PRNG
|
||||
Class.forName("org.apache.harmony.xnet.provider.jsse.NativeCrypto")
|
||||
.getMethod("RAND_seed", byte[].class)
|
||||
.invoke(null, (Object) seed);
|
||||
// Mix the output of the Linux PRNG into the OpenSSL PRNG
|
||||
int bytesRead = (Integer) Class.forName(
|
||||
"org.apache.harmony.xnet.provider.jsse.NativeCrypto")
|
||||
.getMethod("RAND_load_file", String.class, long.class)
|
||||
.invoke(null, "/dev/urandom", 1024);
|
||||
if (bytesRead != 1024) throw new IOException();
|
||||
} catch (Exception e) {
|
||||
throw new SecurityException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
package org.briarproject.bramble.system;
|
||||
|
||||
import android.app.Application;
|
||||
|
||||
import org.briarproject.bramble.api.event.EventExecutor;
|
||||
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
|
||||
import org.briarproject.bramble.api.system.AndroidExecutor;
|
||||
import org.briarproject.bramble.api.system.LocationUtils;
|
||||
import org.briarproject.bramble.api.system.ResourceProvider;
|
||||
import org.briarproject.bramble.api.system.SecureRandomProvider;
|
||||
import org.briarproject.onionwrapper.AndroidLocationUtilsFactory;
|
||||
import org.briarproject.onionwrapper.LocationUtils;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.RejectedExecutionHandler;
|
||||
@@ -45,8 +48,9 @@ public class AndroidSystemModule {
|
||||
}
|
||||
|
||||
@Provides
|
||||
LocationUtils provideLocationUtils(AndroidLocationUtils locationUtils) {
|
||||
return locationUtils;
|
||||
@Singleton
|
||||
LocationUtils provideLocationUtils(Application app) {
|
||||
return AndroidLocationUtilsFactory.createAndroidLocationUtils(app);
|
||||
}
|
||||
|
||||
@Provides
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
package org.briarproject.bramble.system;
|
||||
|
||||
import android.app.Application;
|
||||
|
||||
import org.briarproject.android.dontkillmelib.wakelock.AndroidWakeLockManager;
|
||||
import org.briarproject.android.dontkillmelib.wakelock.AndroidWakeLockManagerFactory;
|
||||
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
|
||||
@Module
|
||||
public class AndroidWakeLockModule {
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
AndroidWakeLockManager provideWakeLockManager(Application app,
|
||||
ScheduledExecutorService scheduledExecutorService) {
|
||||
return AndroidWakeLockManagerFactory.createAndroidWakeLockManager(app,
|
||||
scheduledExecutorService);
|
||||
}
|
||||
}
|
||||
@@ -11,14 +11,10 @@ import org.briarproject.bramble.api.Pair;
|
||||
import org.briarproject.nullsafety.NotNullByDefault;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Scanner;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
@@ -29,7 +25,6 @@ import static android.content.pm.PackageManager.PERMISSION_GRANTED;
|
||||
import static android.os.Build.VERSION.SDK_INT;
|
||||
import static android.os.Process.myPid;
|
||||
import static android.os.Process.myUid;
|
||||
import static java.lang.Runtime.getRuntime;
|
||||
import static java.util.Arrays.asList;
|
||||
import static org.briarproject.nullsafety.NullSafety.requireNonNull;
|
||||
|
||||
@@ -43,14 +38,7 @@ public class AndroidUtils {
|
||||
private static final String STORED_LOGCAT = "dev-logcat";
|
||||
|
||||
public static Collection<String> getSupportedArchitectures() {
|
||||
List<String> abis = new ArrayList<>();
|
||||
if (SDK_INT >= 21) {
|
||||
abis.addAll(asList(Build.SUPPORTED_ABIS));
|
||||
} else {
|
||||
abis.add(Build.CPU_ABI);
|
||||
if (Build.CPU_ABI2 != null) abis.add(Build.CPU_ABI2);
|
||||
}
|
||||
return abis;
|
||||
return asList(Build.SUPPORTED_ABIS);
|
||||
}
|
||||
|
||||
public static boolean hasBtConnectPermission(Context ctx) {
|
||||
@@ -136,19 +124,6 @@ public class AndroidUtils {
|
||||
return new String[] {"image/jpeg", "image/png", "image/gif"};
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static String getSystemProperty(String propName) {
|
||||
try {
|
||||
Process p = getRuntime().exec("getprop " + propName);
|
||||
Scanner s = new Scanner(p.getInputStream());
|
||||
String line = s.nextLine();
|
||||
s.close();
|
||||
return line;
|
||||
} catch (SecurityException | IOException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isUiThread() {
|
||||
return Looper.myLooper() == Looper.getMainLooper();
|
||||
}
|
||||
|
||||
@@ -87,6 +87,10 @@ public class AndroidAccountManagerTest extends BrambleMockTestCase {
|
||||
File potatoFile = new File(potatoDir, "file");
|
||||
File filesDir = new File(testDir, "filesDir");
|
||||
File externalCacheDir = new File(testDir, "externalCacheDir");
|
||||
File externalCacheDir1 = new File(testDir, "externalCacheDir1");
|
||||
File externalCacheDir2 = new File(testDir, "externalCacheDir2");
|
||||
File externalMediaDir1 = new File(testDir, "externalMediaDir1");
|
||||
File externalMediaDir2 = new File(testDir, "externalMediaDir2");
|
||||
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(prefs).edit();
|
||||
@@ -109,6 +113,12 @@ public class AndroidAccountManagerTest extends BrambleMockTestCase {
|
||||
will(returnValue(cacheDir));
|
||||
oneOf(app).getExternalCacheDir();
|
||||
will(returnValue(externalCacheDir));
|
||||
oneOf(app).getExternalCacheDirs();
|
||||
will(returnValue(
|
||||
new File[] {externalCacheDir1, externalCacheDir2}));
|
||||
oneOf(app).getExternalMediaDirs();
|
||||
will(returnValue(
|
||||
new File[] {externalMediaDir1, externalMediaDir2}));
|
||||
}});
|
||||
|
||||
assertTrue(dbDir.mkdirs());
|
||||
@@ -125,6 +135,10 @@ public class AndroidAccountManagerTest extends BrambleMockTestCase {
|
||||
assertTrue(potatoFile.createNewFile());
|
||||
assertTrue(filesDir.mkdirs());
|
||||
assertTrue(externalCacheDir.mkdirs());
|
||||
assertTrue(externalCacheDir1.mkdirs());
|
||||
assertTrue(externalCacheDir2.mkdirs());
|
||||
assertTrue(externalMediaDir1.mkdirs());
|
||||
assertTrue(externalMediaDir2.mkdirs());
|
||||
|
||||
accountManager.deleteAccount();
|
||||
|
||||
@@ -142,6 +156,10 @@ public class AndroidAccountManagerTest extends BrambleMockTestCase {
|
||||
assertFalse(potatoFile.exists());
|
||||
assertFalse(filesDir.exists());
|
||||
assertFalse(externalCacheDir.exists());
|
||||
assertFalse(externalCacheDir1.exists());
|
||||
assertFalse(externalCacheDir2.exists());
|
||||
assertFalse(externalMediaDir1.exists());
|
||||
assertFalse(externalMediaDir2.exists());
|
||||
}
|
||||
|
||||
@After
|
||||
|
||||
@@ -4,10 +4,10 @@ dependencyVerification {
|
||||
'cglib:cglib:3.2.8:cglib-3.2.8.jar:3f64de999ecc5595dc84ca8ff0879d8a34c8623f9ef3c517a53ed59023fcb9db',
|
||||
'com.google.code.findbugs:annotations:3.0.1:annotations-3.0.1.jar:6b47ff0a6de0ce17cbedc3abb0828ca5bce3009d53ea47b3723ff023c4742f79',
|
||||
'com.google.code.findbugs:jsr305:3.0.2:jsr305-3.0.2.jar:766ad2a0783f2687962c8ad74ceecc38a28b9f72a2d085ee438b7813e928d0c7',
|
||||
'com.google.dagger:dagger-compiler:2.43.2:dagger-compiler-2.43.2.jar:298c020ee6ed2f4cc651ebbfdb7f8de329b07c44b618d65be117846a850e2a03',
|
||||
'com.google.dagger:dagger-producers:2.43.2:dagger-producers-2.43.2.jar:e7f5d9ffc85d48a49c8e22e02833d418f7ccad5d7512f529964db5127ab915ff',
|
||||
'com.google.dagger:dagger-spi:2.43.2:dagger-spi-2.43.2.jar:3bae8d9dadeaaa5927da6f094389a560c12c05fec3d2711d2fa79292c7a7d7ad',
|
||||
'com.google.dagger:dagger:2.43.2:dagger-2.43.2.jar:c89681f7cbbf8c527bf4ac2748515d617fdb54a1d425c08d914fdc28192b5fe4',
|
||||
'com.google.dagger:dagger-compiler:2.45:dagger-compiler-2.45.jar:5617dfb994537dba5b41f3744a6dd13ec3cd99789c065e0d5c6fa9f21cf7ca25',
|
||||
'com.google.dagger:dagger-producers:2.45:dagger-producers-2.45.jar:a05abb4c3ccf6bb0f056bdcb5ef973898ecf172952ab5948a824aeea6c86ecaa',
|
||||
'com.google.dagger:dagger-spi:2.45:dagger-spi-2.45.jar:7cd6f0b09d88e64a9c97bc80e544ab8ac8fdee9301754413585a74cf64222b27',
|
||||
'com.google.dagger:dagger:2.45:dagger-2.45.jar:f011cae7d2c0fb7ea17c34e05bc10e768b1081a5892ad019cf1fdb0e125c49c1',
|
||||
'com.google.devtools.ksp:symbol-processing-api:1.7.0-1.0.6:symbol-processing-api-1.7.0-1.0.6.jar:adc29417be5ca9ff42118105fea4e36d9ef44987abfc41432309371a60198941',
|
||||
'com.google.errorprone:error_prone_annotations:2.7.1:error_prone_annotations-2.7.1.jar:cd5257c08a246cf8628817ae71cb822be192ef91f6881ca4a3fcff4f1de1cff3',
|
||||
'com.google.errorprone:javac-shaded:9-dev-r4023-3:javac-shaded-9-dev-r4023-3.jar:65bfccf60986c47fbc17c9ebab0be626afc41741e0a6ec7109e0768817a36f30',
|
||||
@@ -17,6 +17,7 @@ dependencyVerification {
|
||||
'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.squareup:javapoet:1.13.0:javapoet-1.13.0.jar:4c7517e848a71b36d069d12bb3bf46a70fd4cda3105d822b0ed2e19c00b69291',
|
||||
'com.squareup:kotlinpoet:1.11.0:kotlinpoet-1.11.0.jar:2887ada1ca03dd83baa2758640d87e840d1907564db0ef88d2289c868a980492',
|
||||
'javax.annotation:jsr250-api:1.0:jsr250-api-1.0.jar:a1a922d0d9b6d183ed3800dfac01d1e1eb159f0e8c6f94736931c1def54a941f',
|
||||
'javax.inject:javax.inject:1:javax.inject-1.jar:91c77044a50c481636c32d916fd89c9118a72195390452c81065080f957de7ff',
|
||||
'junit:junit:4.13.2:junit-4.13.2.jar:8e495b634469d64fb8acfa3495a065cbacc8a0fff55ce1e31007be4c16dc57d3',
|
||||
@@ -24,12 +25,12 @@ dependencyVerification {
|
||||
'net.jcip:jcip-annotations:1.0:jcip-annotations-1.0.jar:be5805392060c71474bf6c9a67a099471274d30b83eef84bfc4e0889a4f1dcc0',
|
||||
'net.ltgt.gradle.incap:incap:0.2:incap-0.2.jar:b625b9806b0f1e4bc7a2e3457119488de3cd57ea20feedd513db070a573a4ffd',
|
||||
'org.apache-extras.beanshell:bsh:2.0b6:bsh-2.0b6.jar:a17955976070c0573235ee662f2794a78082758b61accffce8d3f8aedcd91047',
|
||||
'org.briarproject:dont-kill-me-lib:0.2.6:dont-kill-me-lib-0.2.6.aar:8a4cc201143227c0865c2edfba035f71109bf02e1ab26444fa3e42d3c569960f',
|
||||
'org.briarproject:dont-kill-me-lib:0.2.7:dont-kill-me-lib-0.2.7.aar:8a9540941fd927e1c127096a7a9b4aa61ce2f2965d2e24f849be92f9e57213c4',
|
||||
'org.briarproject:jtorctl:0.5:jtorctl-0.5.jar:43f8c7d390169772b9a2c82ab806c8414c136a2a8636c555e22754bb7260793b',
|
||||
'org.briarproject:null-safety:0.1:null-safety-0.1.jar:161760de5e838cb982bafa973df820675d4397098e9a91637a36a306d43ba011',
|
||||
'org.briarproject:obfs4proxy-android:0.0.14-tor2:obfs4proxy-android-0.0.14-tor2.jar:a0a93770d6760ce57d9dbd31cc7177687374e00c3361dac22ab75e3b6e0f289e',
|
||||
'org.briarproject:onionwrapper-android:0.0.1:onionwrapper-android-0.0.1.aar:959115946586daa090f057645cf75992407a59025e221c3bf88d2aa930ef3919',
|
||||
'org.briarproject:onionwrapper-core:0.0.1:onionwrapper-core-0.0.1.jar:a1937506b00ee6620e909a500e5d004be81f94a6f7d7c898e1a9e841a8ae8a2a',
|
||||
'org.briarproject:onionwrapper-android:0.0.2:onionwrapper-android-0.0.2.aar:d196f1fe5d8b750866ea09d80509716aae7645d39b2c85433994718dbebeb4d1',
|
||||
'org.briarproject:onionwrapper-core:0.0.2:onionwrapper-core-0.0.2.jar:7038e960c9e59803f0e2c19444dbb5214cd99e5a7463c0a01c45318e07a0eb80',
|
||||
'org.briarproject:snowflake-android:2.5.1:snowflake-android-2.5.1.jar:88ec81c17b1b6fa884d06839dec0330e328b45c89f88c970a213ce91ca8eac87',
|
||||
'org.briarproject:tor-android:0.4.7.13-2:tor-android-0.4.7.13-2.jar:453fd463b234a2104edd7f0d02d0649cbb5c5efbe47a76df3828f55a3f90f8b5',
|
||||
'org.checkerframework:checker-compat-qual:2.5.5:checker-compat-qual-2.5.5.jar:11d134b245e9cacc474514d2d66b5b8618f8039a1465cdc55bbc0b34e0008b7a',
|
||||
@@ -41,6 +42,7 @@ dependencyVerification {
|
||||
'org.jacoco:org.jacoco.ant:0.8.7:org.jacoco.ant-0.8.7.jar:97ca96a382c3f23a44d8eb4c4e6c3742a30cb8005774a76ced0fc4806ce49605',
|
||||
'org.jacoco:org.jacoco.core:0.8.7:org.jacoco.core-0.8.7.jar:ad7739b5fb5969aa1a8aead3d74ed54dc82ed012f1f10f336bd1b96e71c1a13c',
|
||||
'org.jacoco:org.jacoco.report:0.8.7:org.jacoco.report-0.8.7.jar:cc89258623700a6c932592153cb528785876b6da183d5431f97efbba6f020e5b',
|
||||
'org.jetbrains.kotlin:kotlin-reflect:1.6.10:kotlin-reflect-1.6.10.jar:3277ac102ae17aad10a55abec75ff5696c8d109790396434b496e75087854203',
|
||||
'org.jetbrains.kotlin:kotlin-stdlib-common:1.7.0:kotlin-stdlib-common-1.7.0.jar:59c6ff64fe9a6604afce03e8aaa75f83586c6030ac71fb0b34ee7cdefed3618f',
|
||||
'org.jetbrains.kotlin:kotlin-stdlib-common:1.8.0:kotlin-stdlib-common-1.8.0.jar:78ef93b59e603cc0fe51def9bd4c037b07cbace3b3b7806d1a490a42bc1f4cb2',
|
||||
'org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.7.0:kotlin-stdlib-jdk7-1.7.0.jar:07e91be9b2ca20672d2bdb7e181b766e73453a2da13492b5ddaee8fa47aea239',
|
||||
|
||||
@@ -54,6 +54,38 @@ public interface CryptoComponent {
|
||||
KeyPair ourKeyPair, byte[]... inputs)
|
||||
throws GeneralSecurityException;
|
||||
|
||||
/**
|
||||
* Derives a shared secret from two static and two ephemeral key pairs.
|
||||
* <p>
|
||||
* Do not use this method for new protocols. The shared secret can be
|
||||
* re-derived using the ephemeral public keys and both static private
|
||||
* keys, so keys derived from the shared secret should not be used if
|
||||
* forward secrecy is required. Use {@link #deriveSharedSecret(String,
|
||||
* PublicKey, PublicKey, KeyPair, KeyPair, boolean, byte[]...)} instead.
|
||||
* <p>
|
||||
* TODO: Remove this after a reasonable migration period (added 2023-03-10).
|
||||
* <p>
|
||||
*
|
||||
* @param label A namespaced label indicating the purpose of this shared
|
||||
* secret, to prevent it from being repurposed or colliding with a shared
|
||||
* secret derived for another purpose
|
||||
* @param theirStaticPublicKey The static public key of the remote party
|
||||
* @param theirEphemeralPublicKey The ephemeral public key of the remote
|
||||
* party
|
||||
* @param ourStaticKeyPair The static key pair of the local party
|
||||
* @param ourEphemeralKeyPair The ephemeral key pair of the local party
|
||||
* @param alice True if the local party is Alice
|
||||
* @param inputs Additional inputs that will be included in the
|
||||
* derivation of the shared secret
|
||||
* @return The shared secret
|
||||
*/
|
||||
@Deprecated
|
||||
SecretKey deriveSharedSecretBadly(String label,
|
||||
PublicKey theirStaticPublicKey, PublicKey theirEphemeralPublicKey,
|
||||
KeyPair ourStaticKeyPair, KeyPair ourEphemeralKeyPair,
|
||||
boolean alice, byte[]... inputs)
|
||||
throws GeneralSecurityException;
|
||||
|
||||
/**
|
||||
* Derives a shared secret from two static and two ephemeral key pairs.
|
||||
*
|
||||
|
||||
@@ -32,8 +32,15 @@ public interface RecordReader {
|
||||
* 'accept' or 'ignore' predicates
|
||||
*/
|
||||
@Nullable
|
||||
Record readRecord(Predicate<Record> accept, Predicate<Record> ignore)
|
||||
Record readRecord(RecordPredicate accept, RecordPredicate ignore)
|
||||
throws IOException;
|
||||
|
||||
void close() throws IOException;
|
||||
|
||||
/**
|
||||
* Interface that reifies the generic interface {@code Predicate<Record>}
|
||||
* for easier testing.
|
||||
*/
|
||||
interface RecordPredicate extends Predicate<Record> {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
package org.briarproject.bramble.api.system;
|
||||
|
||||
import org.briarproject.nullsafety.NotNullByDefault;
|
||||
|
||||
@NotNullByDefault
|
||||
public interface LocationUtils {
|
||||
|
||||
/**
|
||||
* Get the country the device is currently located in, or "" if it cannot
|
||||
* be determined.
|
||||
* <p>
|
||||
* The country codes are formatted upper-case and as per <a href="
|
||||
* https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2">ISO 3166-1 alpha 2</a>.
|
||||
*/
|
||||
String getCurrentCountry();
|
||||
}
|
||||
@@ -4,7 +4,7 @@ dependencyVerification {
|
||||
'com.fasterxml.jackson.core:jackson-annotations:2.13.4:jackson-annotations-2.13.4.jar:ac5b27a634942391ca113850ee7db01df1499a240174021263501c05fc653b44',
|
||||
'com.google.code.findbugs:annotations:3.0.1:annotations-3.0.1.jar:6b47ff0a6de0ce17cbedc3abb0828ca5bce3009d53ea47b3723ff023c4742f79',
|
||||
'com.google.code.findbugs:jsr305:3.0.2:jsr305-3.0.2.jar:766ad2a0783f2687962c8ad74ceecc38a28b9f72a2d085ee438b7813e928d0c7',
|
||||
'com.google.dagger:dagger:2.43.2:dagger-2.43.2.jar:c89681f7cbbf8c527bf4ac2748515d617fdb54a1d425c08d914fdc28192b5fe4',
|
||||
'com.google.dagger:dagger:2.45:dagger-2.45.jar:f011cae7d2c0fb7ea17c34e05bc10e768b1081a5892ad019cf1fdb0e125c49c1',
|
||||
'javax.inject:javax.inject:1:javax.inject-1.jar:91c77044a50c481636c32d916fd89c9118a72195390452c81065080f957de7ff',
|
||||
'junit:junit:4.13.2:junit-4.13.2.jar:8e495b634469d64fb8acfa3495a065cbacc8a0fff55ce1e31007be4c16dc57d3',
|
||||
'net.bytebuddy:byte-buddy:1.9.12:byte-buddy-1.9.12.jar:3688c3d434bebc3edc5516296a2ed0f47b65e451071b4afecad84f902f0efc11',
|
||||
@@ -12,8 +12,8 @@ dependencyVerification {
|
||||
'org.apache-extras.beanshell:bsh:2.0b6:bsh-2.0b6.jar:a17955976070c0573235ee662f2794a78082758b61accffce8d3f8aedcd91047',
|
||||
'org.briarproject:null-safety:0.1:null-safety-0.1.jar:161760de5e838cb982bafa973df820675d4397098e9a91637a36a306d43ba011',
|
||||
'org.codehaus.mojo.signature:java16:1.1:java16-1.1.signature:53799223a2c98dba2d0add810bed76315460df285c69e4f397ae6098f87dd619',
|
||||
'org.codehaus.mojo:animal-sniffer-ant-tasks:1.20:animal-sniffer-ant-tasks-1.20.jar:bb7d2498144118311d968bb08ff6fae3fc535fb1cb9cca8b8e9ea65b189422ac',
|
||||
'org.codehaus.mojo:animal-sniffer:1.20:animal-sniffer-1.20.jar:80c422523c38db91260c6d78e5ee4b012862ab61cc55020c9e243dd7b5c62249',
|
||||
'org.codehaus.mojo:animal-sniffer-ant-tasks:1.22:animal-sniffer-ant-tasks-1.22.jar:3f6afeb3e09301d2d7179ed1db21e3ad8846c1e38415ad832a395138ae3f4218',
|
||||
'org.codehaus.mojo:animal-sniffer:1.22:animal-sniffer-1.22.jar:f18c11a25bdd8b520b9c6a28cbb6f33007c812ab0051b6be3f0778e660aa501c',
|
||||
'org.hamcrest:hamcrest-core:2.1:hamcrest-core-2.1.jar:e09109e54a289d88506b9bfec987ddd199f4217c9464132668351b9a4f00bee9',
|
||||
'org.hamcrest:hamcrest-library:2.1:hamcrest-library-2.1.jar:b7e2b6895b3b679f0e47b6380fda391b225e9b78505db9d8bdde8d3cc8d52a21',
|
||||
'org.hamcrest:hamcrest:2.1:hamcrest-2.1.jar:ba93b2e3a562322ba432f0a1b53addcc55cb188253319a020ed77f824e692050',
|
||||
@@ -24,6 +24,6 @@ dependencyVerification {
|
||||
'org.jmock:jmock:2.12.0:jmock-2.12.0.jar:266d07314c0cd343c46ff8a55601272de8cf406807caf55e6f313295f83d10be',
|
||||
'org.objenesis:objenesis:3.0.1:objenesis-3.0.1.jar:7a8ff780b9ff48415d7c705f60030b0acaa616e7f823c98eede3b63508d4e984',
|
||||
'org.ow2.asm:asm:7.1:asm-7.1.jar:4ab2fa2b6d2cc9ccb1eaa05ea329c407b47b13ed2915f62f8c4b8cc96258d4de',
|
||||
'org.ow2.asm:asm:9.1:asm-9.1.jar:cda4de455fab48ff0bcb7c48b4639447d4de859a7afc30a094a986f0936beba2',
|
||||
'org.ow2.asm:asm:9.3:asm-9.3.jar:1263369b59e29c943918de11d6d6152e2ec6085ce63e5710516f8c67d368e4bc',
|
||||
]
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ dependencies {
|
||||
testImplementation project(path: ':bramble-api', configuration: 'testOutput')
|
||||
|
||||
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.6'
|
||||
testImplementation "junit:junit:$junit_version"
|
||||
testImplementation "org.jmock:jmock:$jmock_version"
|
||||
testImplementation "org.jmock:jmock-junit4:$jmock_version"
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package org.briarproject.bramble.contact;
|
||||
|
||||
import org.briarproject.bramble.api.FormatException;
|
||||
import org.briarproject.bramble.api.Predicate;
|
||||
import org.briarproject.bramble.api.client.ClientHelper;
|
||||
import org.briarproject.bramble.api.contact.Contact;
|
||||
import org.briarproject.bramble.api.contact.ContactExchangeManager;
|
||||
@@ -24,6 +23,7 @@ import org.briarproject.bramble.api.properties.TransportProperties;
|
||||
import org.briarproject.bramble.api.properties.TransportPropertyManager;
|
||||
import org.briarproject.bramble.api.record.Record;
|
||||
import org.briarproject.bramble.api.record.RecordReader;
|
||||
import org.briarproject.bramble.api.record.RecordReader.RecordPredicate;
|
||||
import org.briarproject.bramble.api.record.RecordReaderFactory;
|
||||
import org.briarproject.bramble.api.record.RecordWriter;
|
||||
import org.briarproject.bramble.api.record.RecordWriterFactory;
|
||||
@@ -61,12 +61,12 @@ class ContactExchangeManagerImpl implements ContactExchangeManager {
|
||||
getLogger(ContactExchangeManagerImpl.class.getName());
|
||||
|
||||
// Accept records with current protocol version, known record type
|
||||
private static final Predicate<Record> ACCEPT = r ->
|
||||
private static final RecordPredicate ACCEPT = r ->
|
||||
r.getProtocolVersion() == PROTOCOL_VERSION &&
|
||||
isKnownRecordType(r.getRecordType());
|
||||
|
||||
// Ignore records with current protocol version, unknown record type
|
||||
private static final Predicate<Record> IGNORE = r ->
|
||||
private static final RecordPredicate IGNORE = r ->
|
||||
r.getProtocolVersion() == PROTOCOL_VERSION &&
|
||||
!isKnownRecordType(r.getRecordType());
|
||||
|
||||
|
||||
@@ -5,14 +5,31 @@ import static org.briarproject.bramble.api.crypto.CryptoConstants.MAC_BYTES;
|
||||
interface HandshakeConstants {
|
||||
|
||||
/**
|
||||
* The current version of the handshake protocol.
|
||||
* The current major version of the handshake protocol.
|
||||
*/
|
||||
byte PROTOCOL_VERSION = 0;
|
||||
byte PROTOCOL_MAJOR_VERSION = 0;
|
||||
|
||||
/**
|
||||
* Label for deriving the master key.
|
||||
* The current minor version of the handshake protocol.
|
||||
*/
|
||||
String MASTER_KEY_LABEL = "org.briarproject.bramble.handshake/MASTER_KEY";
|
||||
byte PROTOCOL_MINOR_VERSION = 1;
|
||||
|
||||
/**
|
||||
* Label for deriving the master key when using the deprecated v0.0 key
|
||||
* derivation method.
|
||||
* <p>
|
||||
* TODO: Remove this after a reasonable migration period (added 2023-03-10).
|
||||
*/
|
||||
@Deprecated
|
||||
String MASTER_KEY_LABEL_0_0 =
|
||||
"org.briarproject.bramble.handshake/MASTER_KEY";
|
||||
|
||||
/**
|
||||
* Label for deriving the master key when using the v0.1 key derivation
|
||||
* method.
|
||||
*/
|
||||
String MASTER_KEY_LABEL_0_1 =
|
||||
"org.briarproject.bramble.handshake/MASTER_KEY_0_1";
|
||||
|
||||
/**
|
||||
* Label for deriving Alice's proof of ownership from the master key.
|
||||
|
||||
@@ -13,11 +13,26 @@ interface HandshakeCrypto {
|
||||
KeyPair generateEphemeralKeyPair();
|
||||
|
||||
/**
|
||||
* Derives the master key from the given static and ephemeral keys.
|
||||
* Derives the master key from the given static and ephemeral keys using
|
||||
* the deprecated v0.0 key derivation method.
|
||||
* <p>
|
||||
* TODO: Remove this after a reasonable migration period (added 2023-03-10).
|
||||
*
|
||||
* @param alice Whether the local peer is Alice
|
||||
*/
|
||||
SecretKey deriveMasterKey(PublicKey theirStaticPublicKey,
|
||||
@Deprecated
|
||||
SecretKey deriveMasterKey_0_0(PublicKey theirStaticPublicKey,
|
||||
PublicKey theirEphemeralPublicKey, KeyPair ourStaticKeyPair,
|
||||
KeyPair ourEphemeralKeyPair, boolean alice)
|
||||
throws GeneralSecurityException;
|
||||
|
||||
/**
|
||||
* Derives the master key from the given static and ephemeral keys using
|
||||
* the v0.1 key derivation method.
|
||||
*
|
||||
* @param alice Whether the local peer is Alice
|
||||
*/
|
||||
SecretKey deriveMasterKey_0_1(PublicKey theirStaticPublicKey,
|
||||
PublicKey theirEphemeralPublicKey, KeyPair ourStaticKeyPair,
|
||||
KeyPair ourEphemeralKeyPair, boolean alice)
|
||||
throws GeneralSecurityException;
|
||||
|
||||
@@ -13,7 +13,8 @@ import javax.inject.Inject;
|
||||
|
||||
import static org.briarproject.bramble.contact.HandshakeConstants.ALICE_PROOF_LABEL;
|
||||
import static org.briarproject.bramble.contact.HandshakeConstants.BOB_PROOF_LABEL;
|
||||
import static org.briarproject.bramble.contact.HandshakeConstants.MASTER_KEY_LABEL;
|
||||
import static org.briarproject.bramble.contact.HandshakeConstants.MASTER_KEY_LABEL_0_0;
|
||||
import static org.briarproject.bramble.contact.HandshakeConstants.MASTER_KEY_LABEL_0_1;
|
||||
|
||||
@Immutable
|
||||
@NotNullByDefault
|
||||
@@ -32,7 +33,8 @@ class HandshakeCryptoImpl implements HandshakeCrypto {
|
||||
}
|
||||
|
||||
@Override
|
||||
public SecretKey deriveMasterKey(PublicKey theirStaticPublicKey,
|
||||
@Deprecated
|
||||
public SecretKey deriveMasterKey_0_0(PublicKey theirStaticPublicKey,
|
||||
PublicKey theirEphemeralPublicKey, KeyPair ourStaticKeyPair,
|
||||
KeyPair ourEphemeralKeyPair, boolean alice) throws
|
||||
GeneralSecurityException {
|
||||
@@ -46,9 +48,29 @@ class HandshakeCryptoImpl implements HandshakeCrypto {
|
||||
alice ? ourEphemeral : theirEphemeral,
|
||||
alice ? theirEphemeral : ourEphemeral
|
||||
};
|
||||
return crypto.deriveSharedSecret(MASTER_KEY_LABEL, theirStaticPublicKey,
|
||||
theirEphemeralPublicKey, ourStaticKeyPair, ourEphemeralKeyPair,
|
||||
alice, inputs);
|
||||
return crypto.deriveSharedSecretBadly(MASTER_KEY_LABEL_0_0,
|
||||
theirStaticPublicKey, theirEphemeralPublicKey,
|
||||
ourStaticKeyPair, ourEphemeralKeyPair, alice, inputs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SecretKey deriveMasterKey_0_1(PublicKey theirStaticPublicKey,
|
||||
PublicKey theirEphemeralPublicKey, KeyPair ourStaticKeyPair,
|
||||
KeyPair ourEphemeralKeyPair, boolean alice) throws
|
||||
GeneralSecurityException {
|
||||
byte[] theirStatic = theirStaticPublicKey.getEncoded();
|
||||
byte[] theirEphemeral = theirEphemeralPublicKey.getEncoded();
|
||||
byte[] ourStatic = ourStaticKeyPair.getPublic().getEncoded();
|
||||
byte[] ourEphemeral = ourEphemeralKeyPair.getPublic().getEncoded();
|
||||
byte[][] inputs = {
|
||||
alice ? ourStatic : theirStatic,
|
||||
alice ? theirStatic : ourStatic,
|
||||
alice ? ourEphemeral : theirEphemeral,
|
||||
alice ? theirEphemeral : ourEphemeral
|
||||
};
|
||||
return crypto.deriveSharedSecret(MASTER_KEY_LABEL_0_1,
|
||||
theirStaticPublicKey, theirEphemeralPublicKey,
|
||||
ourStaticKeyPair, ourEphemeralKeyPair, alice, inputs);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -2,7 +2,6 @@ package org.briarproject.bramble.contact;
|
||||
|
||||
import org.briarproject.bramble.api.FormatException;
|
||||
import org.briarproject.bramble.api.Pair;
|
||||
import org.briarproject.bramble.api.Predicate;
|
||||
import org.briarproject.bramble.api.contact.ContactManager;
|
||||
import org.briarproject.bramble.api.contact.HandshakeManager;
|
||||
import org.briarproject.bramble.api.contact.PendingContact;
|
||||
@@ -12,12 +11,12 @@ import org.briarproject.bramble.api.crypto.KeyPair;
|
||||
import org.briarproject.bramble.api.crypto.PublicKey;
|
||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||
import org.briarproject.bramble.api.crypto.TransportCrypto;
|
||||
import org.briarproject.bramble.api.db.DatabaseComponent;
|
||||
import org.briarproject.bramble.api.db.DbException;
|
||||
import org.briarproject.bramble.api.db.TransactionManager;
|
||||
import org.briarproject.bramble.api.identity.IdentityManager;
|
||||
import org.briarproject.bramble.api.record.Record;
|
||||
import org.briarproject.bramble.api.record.RecordReader;
|
||||
import org.briarproject.bramble.api.record.RecordReader.RecordPredicate;
|
||||
import org.briarproject.bramble.api.record.RecordReaderFactory;
|
||||
import org.briarproject.bramble.api.record.RecordWriter;
|
||||
import org.briarproject.bramble.api.record.RecordWriterFactory;
|
||||
@@ -28,15 +27,20 @@ import java.io.EOFException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.util.List;
|
||||
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static java.util.Arrays.asList;
|
||||
import static java.util.Collections.singletonList;
|
||||
import static org.briarproject.bramble.api.crypto.CryptoConstants.MAX_AGREEMENT_PUBLIC_KEY_BYTES;
|
||||
import static org.briarproject.bramble.contact.HandshakeConstants.PROOF_BYTES;
|
||||
import static org.briarproject.bramble.contact.HandshakeConstants.PROTOCOL_VERSION;
|
||||
import static org.briarproject.bramble.contact.HandshakeRecordTypes.EPHEMERAL_PUBLIC_KEY;
|
||||
import static org.briarproject.bramble.contact.HandshakeRecordTypes.PROOF_OF_OWNERSHIP;
|
||||
import static org.briarproject.bramble.contact.HandshakeConstants.PROTOCOL_MAJOR_VERSION;
|
||||
import static org.briarproject.bramble.contact.HandshakeConstants.PROTOCOL_MINOR_VERSION;
|
||||
import static org.briarproject.bramble.contact.HandshakeRecordTypes.RECORD_TYPE_EPHEMERAL_PUBLIC_KEY;
|
||||
import static org.briarproject.bramble.contact.HandshakeRecordTypes.RECORD_TYPE_MINOR_VERSION;
|
||||
import static org.briarproject.bramble.contact.HandshakeRecordTypes.RECORD_TYPE_PROOF_OF_OWNERSHIP;
|
||||
import static org.briarproject.bramble.util.ValidationUtils.checkLength;
|
||||
|
||||
@Immutable
|
||||
@@ -44,12 +48,14 @@ import static org.briarproject.bramble.util.ValidationUtils.checkLength;
|
||||
class HandshakeManagerImpl implements HandshakeManager {
|
||||
|
||||
// Ignore records with current protocol version, unknown record type
|
||||
private static final Predicate<Record> IGNORE = r ->
|
||||
r.getProtocolVersion() == PROTOCOL_VERSION &&
|
||||
private static final RecordPredicate IGNORE = r ->
|
||||
r.getProtocolVersion() == PROTOCOL_MAJOR_VERSION &&
|
||||
!isKnownRecordType(r.getRecordType());
|
||||
|
||||
private static boolean isKnownRecordType(byte type) {
|
||||
return type == EPHEMERAL_PUBLIC_KEY || type == PROOF_OF_OWNERSHIP;
|
||||
return type == RECORD_TYPE_EPHEMERAL_PUBLIC_KEY ||
|
||||
type == RECORD_TYPE_PROOF_OF_OWNERSHIP ||
|
||||
type == RECORD_TYPE_MINOR_VERSION;
|
||||
}
|
||||
|
||||
private final TransactionManager db;
|
||||
@@ -61,7 +67,7 @@ class HandshakeManagerImpl implements HandshakeManager {
|
||||
private final RecordWriterFactory recordWriterFactory;
|
||||
|
||||
@Inject
|
||||
HandshakeManagerImpl(DatabaseComponent db,
|
||||
HandshakeManagerImpl(TransactionManager db,
|
||||
IdentityManager identityManager,
|
||||
ContactManager contactManager,
|
||||
TransportCrypto transportCrypto,
|
||||
@@ -95,19 +101,31 @@ class HandshakeManagerImpl implements HandshakeManager {
|
||||
.createRecordWriter(out.getOutputStream());
|
||||
KeyPair ourEphemeralKeyPair =
|
||||
handshakeCrypto.generateEphemeralKeyPair();
|
||||
PublicKey theirEphemeralPublicKey;
|
||||
Pair<Byte, PublicKey> theirMinorVersionAndKey;
|
||||
if (alice) {
|
||||
sendMinorVersion(recordWriter);
|
||||
sendPublicKey(recordWriter, ourEphemeralKeyPair.getPublic());
|
||||
theirEphemeralPublicKey = receivePublicKey(recordReader);
|
||||
theirMinorVersionAndKey = receiveMinorVersionAndKey(recordReader);
|
||||
} else {
|
||||
theirEphemeralPublicKey = receivePublicKey(recordReader);
|
||||
theirMinorVersionAndKey = receiveMinorVersionAndKey(recordReader);
|
||||
sendMinorVersion(recordWriter);
|
||||
sendPublicKey(recordWriter, ourEphemeralKeyPair.getPublic());
|
||||
}
|
||||
byte theirMinorVersion = theirMinorVersionAndKey.getFirst();
|
||||
PublicKey theirEphemeralPublicKey = theirMinorVersionAndKey.getSecond();
|
||||
SecretKey masterKey;
|
||||
try {
|
||||
masterKey = handshakeCrypto.deriveMasterKey(theirStaticPublicKey,
|
||||
theirEphemeralPublicKey, ourStaticKeyPair,
|
||||
ourEphemeralKeyPair, alice);
|
||||
if (theirMinorVersion > 0) {
|
||||
masterKey = handshakeCrypto.deriveMasterKey_0_1(
|
||||
theirStaticPublicKey, theirEphemeralPublicKey,
|
||||
ourStaticKeyPair, ourEphemeralKeyPair, alice);
|
||||
} else {
|
||||
// TODO: Remove this branch after a reasonable migration
|
||||
// period (added 2023-03-10).
|
||||
masterKey = handshakeCrypto.deriveMasterKey_0_0(
|
||||
theirStaticPublicKey, theirEphemeralPublicKey,
|
||||
ourStaticKeyPair, ourEphemeralKeyPair, alice);
|
||||
}
|
||||
} catch (GeneralSecurityException e) {
|
||||
throw new FormatException();
|
||||
}
|
||||
@@ -128,34 +146,91 @@ class HandshakeManagerImpl implements HandshakeManager {
|
||||
}
|
||||
|
||||
private void sendPublicKey(RecordWriter w, PublicKey k) throws IOException {
|
||||
w.writeRecord(new Record(PROTOCOL_VERSION, EPHEMERAL_PUBLIC_KEY,
|
||||
k.getEncoded()));
|
||||
w.writeRecord(new Record(PROTOCOL_MAJOR_VERSION,
|
||||
RECORD_TYPE_EPHEMERAL_PUBLIC_KEY, k.getEncoded()));
|
||||
w.flush();
|
||||
}
|
||||
|
||||
private PublicKey receivePublicKey(RecordReader r) throws IOException {
|
||||
byte[] key = readRecord(r, EPHEMERAL_PUBLIC_KEY).getPayload();
|
||||
/**
|
||||
* Receives the remote peer's protocol minor version and ephemeral public
|
||||
* key.
|
||||
* <p>
|
||||
* In version 0.1 of the protocol, each peer sends a minor version record
|
||||
* followed by an ephemeral public key record.
|
||||
* <p>
|
||||
* In version 0.0 of the protocol, each peer sends an ephemeral public key
|
||||
* record without a preceding minor version record.
|
||||
* <p>
|
||||
* Therefore the remote peer's minor version must be non-zero if a minor
|
||||
* version record is received, and is assumed to be zero if no minor
|
||||
* version record is received.
|
||||
*/
|
||||
private Pair<Byte, PublicKey> receiveMinorVersionAndKey(RecordReader r)
|
||||
throws IOException {
|
||||
byte theirMinorVersion;
|
||||
PublicKey theirEphemeralPublicKey;
|
||||
// The first record can be either a minor version record or an
|
||||
// ephemeral public key record
|
||||
Record first = readRecord(r, asList(RECORD_TYPE_MINOR_VERSION,
|
||||
RECORD_TYPE_EPHEMERAL_PUBLIC_KEY));
|
||||
if (first.getRecordType() == RECORD_TYPE_MINOR_VERSION) {
|
||||
// The payload must be a single byte giving the remote peer's
|
||||
// protocol minor version, which must be non-zero
|
||||
byte[] payload = first.getPayload();
|
||||
checkLength(payload, 1);
|
||||
theirMinorVersion = payload[0];
|
||||
if (theirMinorVersion == 0) throw new FormatException();
|
||||
// The second record must be an ephemeral public key record
|
||||
Record second = readRecord(r,
|
||||
singletonList(RECORD_TYPE_EPHEMERAL_PUBLIC_KEY));
|
||||
theirEphemeralPublicKey = parsePublicKey(second);
|
||||
} else {
|
||||
// The remote peer did not send a minor version record, so the
|
||||
// remote peer's protocol minor version is assumed to be zero
|
||||
// TODO: Remove this branch after a reasonable migration period
|
||||
// (added 2023-03-10).
|
||||
theirMinorVersion = 0;
|
||||
theirEphemeralPublicKey = parsePublicKey(first);
|
||||
}
|
||||
return new Pair<>(theirMinorVersion, theirEphemeralPublicKey);
|
||||
}
|
||||
|
||||
private PublicKey parsePublicKey(Record rec) throws IOException {
|
||||
if (rec.getRecordType() != RECORD_TYPE_EPHEMERAL_PUBLIC_KEY) {
|
||||
throw new AssertionError();
|
||||
}
|
||||
byte[] key = rec.getPayload();
|
||||
checkLength(key, 1, MAX_AGREEMENT_PUBLIC_KEY_BYTES);
|
||||
return new AgreementPublicKey(key);
|
||||
}
|
||||
|
||||
private void sendProof(RecordWriter w, byte[] proof) throws IOException {
|
||||
w.writeRecord(new Record(PROTOCOL_VERSION, PROOF_OF_OWNERSHIP, proof));
|
||||
w.writeRecord(new Record(PROTOCOL_MAJOR_VERSION,
|
||||
RECORD_TYPE_PROOF_OF_OWNERSHIP, proof));
|
||||
w.flush();
|
||||
}
|
||||
|
||||
private byte[] receiveProof(RecordReader r) throws IOException {
|
||||
byte[] proof = readRecord(r, PROOF_OF_OWNERSHIP).getPayload();
|
||||
Record rec = readRecord(r,
|
||||
singletonList(RECORD_TYPE_PROOF_OF_OWNERSHIP));
|
||||
byte[] proof = rec.getPayload();
|
||||
checkLength(proof, PROOF_BYTES, PROOF_BYTES);
|
||||
return proof;
|
||||
}
|
||||
|
||||
private Record readRecord(RecordReader r, byte expectedType)
|
||||
private void sendMinorVersion(RecordWriter w) throws IOException {
|
||||
w.writeRecord(new Record(PROTOCOL_MAJOR_VERSION,
|
||||
RECORD_TYPE_MINOR_VERSION,
|
||||
new byte[] {PROTOCOL_MINOR_VERSION}));
|
||||
w.flush();
|
||||
}
|
||||
|
||||
private Record readRecord(RecordReader r, List<Byte> expectedTypes)
|
||||
throws IOException {
|
||||
// Accept records with current protocol version, expected type only
|
||||
Predicate<Record> accept = rec ->
|
||||
rec.getProtocolVersion() == PROTOCOL_VERSION &&
|
||||
rec.getRecordType() == expectedType;
|
||||
// Accept records with current protocol version, expected types only
|
||||
RecordPredicate accept = rec ->
|
||||
rec.getProtocolVersion() == PROTOCOL_MAJOR_VERSION &&
|
||||
expectedTypes.contains(rec.getRecordType());
|
||||
Record rec = r.readRecord(accept, IGNORE);
|
||||
if (rec == null) throw new EOFException();
|
||||
return rec;
|
||||
|
||||
@@ -5,7 +5,9 @@ package org.briarproject.bramble.contact;
|
||||
*/
|
||||
interface HandshakeRecordTypes {
|
||||
|
||||
byte EPHEMERAL_PUBLIC_KEY = 0;
|
||||
byte RECORD_TYPE_EPHEMERAL_PUBLIC_KEY = 0;
|
||||
|
||||
byte PROOF_OF_OWNERSHIP = 1;
|
||||
byte RECORD_TYPE_PROOF_OF_OWNERSHIP = 1;
|
||||
|
||||
byte RECORD_TYPE_MINOR_VERSION = 2;
|
||||
}
|
||||
|
||||
@@ -222,7 +222,8 @@ class CryptoComponentImpl implements CryptoComponent {
|
||||
}
|
||||
|
||||
@Override
|
||||
public SecretKey deriveSharedSecret(String label,
|
||||
@Deprecated
|
||||
public SecretKey deriveSharedSecretBadly(String label,
|
||||
PublicKey theirStaticPublicKey, PublicKey theirEphemeralPublicKey,
|
||||
KeyPair ourStaticKeyPair, KeyPair ourEphemeralKeyPair,
|
||||
boolean alice, byte[]... inputs) throws GeneralSecurityException {
|
||||
@@ -250,6 +251,35 @@ class CryptoComponentImpl implements CryptoComponent {
|
||||
return new SecretKey(hash);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SecretKey deriveSharedSecret(String label,
|
||||
PublicKey theirStaticPublicKey, PublicKey theirEphemeralPublicKey,
|
||||
KeyPair ourStaticKeyPair, KeyPair ourEphemeralKeyPair,
|
||||
boolean alice, byte[]... inputs) throws GeneralSecurityException {
|
||||
PrivateKey ourStaticPrivateKey = ourStaticKeyPair.getPrivate();
|
||||
PrivateKey ourEphemeralPrivateKey = ourEphemeralKeyPair.getPrivate();
|
||||
byte[][] hashInputs = new byte[inputs.length + 3][];
|
||||
// Alice ephemeral/Bob ephemeral
|
||||
hashInputs[0] = performRawKeyAgreement(ourEphemeralPrivateKey,
|
||||
theirEphemeralPublicKey);
|
||||
// Alice static/Bob ephemeral, Bob static/Alice ephemeral
|
||||
if (alice) {
|
||||
hashInputs[1] = performRawKeyAgreement(ourStaticPrivateKey,
|
||||
theirEphemeralPublicKey);
|
||||
hashInputs[2] = performRawKeyAgreement(ourEphemeralPrivateKey,
|
||||
theirStaticPublicKey);
|
||||
} else {
|
||||
hashInputs[1] = performRawKeyAgreement(ourEphemeralPrivateKey,
|
||||
theirStaticPublicKey);
|
||||
hashInputs[2] = performRawKeyAgreement(ourStaticPrivateKey,
|
||||
theirEphemeralPublicKey);
|
||||
}
|
||||
arraycopy(inputs, 0, hashInputs, 3, inputs.length);
|
||||
byte[] hash = hash(label, hashInputs);
|
||||
if (hash.length != SecretKey.LENGTH) throw new IllegalStateException();
|
||||
return new SecretKey(hash);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] sign(String label, byte[] toSign, PrivateKey privateKey)
|
||||
throws GeneralSecurityException {
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
package org.briarproject.bramble.keyagreement;
|
||||
|
||||
import org.briarproject.bramble.api.Predicate;
|
||||
import org.briarproject.bramble.api.keyagreement.KeyAgreementConnection;
|
||||
import org.briarproject.bramble.api.plugin.TransportId;
|
||||
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
|
||||
import org.briarproject.bramble.api.record.Record;
|
||||
import org.briarproject.bramble.api.record.RecordReader;
|
||||
import org.briarproject.bramble.api.record.RecordReader.RecordPredicate;
|
||||
import org.briarproject.bramble.api.record.RecordReaderFactory;
|
||||
import org.briarproject.bramble.api.record.RecordWriter;
|
||||
import org.briarproject.bramble.api.record.RecordWriterFactory;
|
||||
@@ -34,12 +34,12 @@ class KeyAgreementTransport {
|
||||
Logger.getLogger(KeyAgreementTransport.class.getName());
|
||||
|
||||
// Accept records with current protocol version, known record type
|
||||
private static final Predicate<Record> ACCEPT = r ->
|
||||
private static final RecordPredicate ACCEPT = r ->
|
||||
r.getProtocolVersion() == PROTOCOL_VERSION &&
|
||||
isKnownRecordType(r.getRecordType());
|
||||
|
||||
// Ignore records with current protocol version, unknown record type
|
||||
private static final Predicate<Record> IGNORE = r ->
|
||||
private static final RecordPredicate IGNORE = r ->
|
||||
r.getProtocolVersion() == PROTOCOL_VERSION &&
|
||||
!isKnownRecordType(r.getRecordType());
|
||||
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
package org.briarproject.bramble.plugin.tor;
|
||||
|
||||
import org.briarproject.onionwrapper.CircumventionProvider;
|
||||
import org.briarproject.onionwrapper.CircumventionProviderFactory;
|
||||
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
|
||||
@Module
|
||||
public class CircumventionModule {
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
CircumventionProvider provideCircumventionProvider() {
|
||||
return CircumventionProviderFactory.createCircumventionProvider();
|
||||
}
|
||||
}
|
||||
@@ -24,11 +24,11 @@ import org.briarproject.bramble.api.rendezvous.KeyMaterialSource;
|
||||
import org.briarproject.bramble.api.rendezvous.RendezvousEndpoint;
|
||||
import org.briarproject.bramble.api.settings.Settings;
|
||||
import org.briarproject.bramble.api.settings.event.SettingsUpdatedEvent;
|
||||
import org.briarproject.bramble.api.system.LocationUtils;
|
||||
import org.briarproject.nullsafety.InterfaceNotNullByDefault;
|
||||
import org.briarproject.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.onionwrapper.CircumventionProvider;
|
||||
import org.briarproject.onionwrapper.CircumventionProvider.BridgeType;
|
||||
import org.briarproject.onionwrapper.LocationUtils;
|
||||
import org.briarproject.onionwrapper.TorWrapper;
|
||||
import org.briarproject.onionwrapper.TorWrapper.HiddenServiceProperties;
|
||||
import org.briarproject.onionwrapper.TorWrapper.Observer;
|
||||
|
||||
@@ -17,10 +17,10 @@ import org.briarproject.bramble.api.plugin.TransportId;
|
||||
import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin;
|
||||
import org.briarproject.bramble.api.plugin.duplex.DuplexPluginFactory;
|
||||
import org.briarproject.bramble.api.system.Clock;
|
||||
import org.briarproject.bramble.api.system.LocationUtils;
|
||||
import org.briarproject.bramble.api.system.WakefulIoExecutor;
|
||||
import org.briarproject.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.onionwrapper.CircumventionProvider;
|
||||
import org.briarproject.onionwrapper.LocationUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package org.briarproject.bramble.record;
|
||||
|
||||
import org.briarproject.bramble.api.FormatException;
|
||||
import org.briarproject.bramble.api.Predicate;
|
||||
import org.briarproject.bramble.api.record.Record;
|
||||
import org.briarproject.bramble.api.record.RecordReader;
|
||||
import org.briarproject.bramble.util.ByteUtils;
|
||||
@@ -45,7 +44,7 @@ class RecordReaderImpl implements RecordReader {
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Record readRecord(Predicate<Record> accept, Predicate<Record> ignore)
|
||||
public Record readRecord(RecordPredicate accept, RecordPredicate ignore)
|
||||
throws IOException {
|
||||
while (true) {
|
||||
if (eof()) return null;
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
package org.briarproject.bramble.sync;
|
||||
|
||||
import org.briarproject.bramble.api.FormatException;
|
||||
import org.briarproject.bramble.api.Predicate;
|
||||
import org.briarproject.bramble.api.UniqueId;
|
||||
import org.briarproject.bramble.api.record.Record;
|
||||
import org.briarproject.bramble.api.record.RecordReader;
|
||||
import org.briarproject.bramble.api.record.RecordReader.RecordPredicate;
|
||||
import org.briarproject.bramble.api.sync.Ack;
|
||||
import org.briarproject.bramble.api.sync.Message;
|
||||
import org.briarproject.bramble.api.sync.MessageFactory;
|
||||
@@ -41,12 +41,12 @@ import static org.briarproject.bramble.api.sync.SyncConstants.PROTOCOL_VERSION;
|
||||
class SyncRecordReaderImpl implements SyncRecordReader {
|
||||
|
||||
// Accept records with current protocol version, known record type
|
||||
private static final Predicate<Record> ACCEPT = r ->
|
||||
private static final RecordPredicate ACCEPT = r ->
|
||||
r.getProtocolVersion() == PROTOCOL_VERSION &&
|
||||
isKnownRecordType(r.getRecordType());
|
||||
|
||||
// Ignore records with current protocol version, unknown record type
|
||||
private static final Predicate<Record> IGNORE = r ->
|
||||
private static final RecordPredicate IGNORE = r ->
|
||||
r.getProtocolVersion() == PROTOCOL_VERSION &&
|
||||
!isKnownRecordType(r.getRecordType());
|
||||
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
package org.briarproject.onionwrapper;
|
||||
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
|
||||
@Module
|
||||
public class CircumventionModule {
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
CircumventionProvider provideCircumventionProvider(
|
||||
CircumventionProviderImpl provider) {
|
||||
return provider;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,316 @@
|
||||
package org.briarproject.bramble.contact;
|
||||
|
||||
import org.briarproject.bramble.api.FormatException;
|
||||
import org.briarproject.bramble.api.contact.ContactManager;
|
||||
import org.briarproject.bramble.api.contact.HandshakeManager.HandshakeResult;
|
||||
import org.briarproject.bramble.api.contact.PendingContact;
|
||||
import org.briarproject.bramble.api.crypto.KeyPair;
|
||||
import org.briarproject.bramble.api.crypto.PrivateKey;
|
||||
import org.briarproject.bramble.api.crypto.PublicKey;
|
||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||
import org.briarproject.bramble.api.crypto.TransportCrypto;
|
||||
import org.briarproject.bramble.api.db.Transaction;
|
||||
import org.briarproject.bramble.api.db.TransactionManager;
|
||||
import org.briarproject.bramble.api.identity.IdentityManager;
|
||||
import org.briarproject.bramble.api.record.Record;
|
||||
import org.briarproject.bramble.api.record.RecordReader;
|
||||
import org.briarproject.bramble.api.record.RecordReader.RecordPredicate;
|
||||
import org.briarproject.bramble.api.record.RecordReaderFactory;
|
||||
import org.briarproject.bramble.api.record.RecordWriter;
|
||||
import org.briarproject.bramble.api.record.RecordWriterFactory;
|
||||
import org.briarproject.bramble.api.transport.StreamWriter;
|
||||
import org.briarproject.bramble.test.BrambleMockTestCase;
|
||||
import org.briarproject.bramble.test.DbExpectations;
|
||||
import org.briarproject.bramble.test.PredicateMatcher;
|
||||
import org.jmock.Expectations;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.Arrays;
|
||||
|
||||
import static org.briarproject.bramble.contact.HandshakeConstants.PROOF_BYTES;
|
||||
import static org.briarproject.bramble.contact.HandshakeConstants.PROTOCOL_MAJOR_VERSION;
|
||||
import static org.briarproject.bramble.contact.HandshakeConstants.PROTOCOL_MINOR_VERSION;
|
||||
import static org.briarproject.bramble.contact.HandshakeRecordTypes.RECORD_TYPE_EPHEMERAL_PUBLIC_KEY;
|
||||
import static org.briarproject.bramble.contact.HandshakeRecordTypes.RECORD_TYPE_MINOR_VERSION;
|
||||
import static org.briarproject.bramble.contact.HandshakeRecordTypes.RECORD_TYPE_PROOF_OF_OWNERSHIP;
|
||||
import static org.briarproject.bramble.test.TestUtils.getAgreementPrivateKey;
|
||||
import static org.briarproject.bramble.test.TestUtils.getAgreementPublicKey;
|
||||
import static org.briarproject.bramble.test.TestUtils.getPendingContact;
|
||||
import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
|
||||
import static org.briarproject.bramble.test.TestUtils.getSecretKey;
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
public class HandshakeManagerImplTest extends BrambleMockTestCase {
|
||||
|
||||
private final TransactionManager db =
|
||||
context.mock(TransactionManager.class);
|
||||
private final IdentityManager identityManager =
|
||||
context.mock(IdentityManager.class);
|
||||
private final ContactManager contactManager =
|
||||
context.mock(ContactManager.class);
|
||||
private final TransportCrypto transportCrypto =
|
||||
context.mock(TransportCrypto.class);
|
||||
private final HandshakeCrypto handshakeCrypto =
|
||||
context.mock(HandshakeCrypto.class);
|
||||
private final RecordReaderFactory recordReaderFactory =
|
||||
context.mock(RecordReaderFactory.class);
|
||||
private final RecordWriterFactory recordWriterFactory =
|
||||
context.mock(RecordWriterFactory.class);
|
||||
private final RecordReader recordReader = context.mock(RecordReader.class);
|
||||
private final RecordWriter recordWriter = context.mock(RecordWriter.class);
|
||||
private final StreamWriter streamWriter = context.mock(StreamWriter.class);
|
||||
|
||||
private final PendingContact pendingContact = getPendingContact();
|
||||
private final PublicKey theirStaticPublicKey =
|
||||
pendingContact.getPublicKey();
|
||||
private final PublicKey ourStaticPublicKey = getAgreementPublicKey();
|
||||
private final PrivateKey ourStaticPrivateKey = getAgreementPrivateKey();
|
||||
private final KeyPair ourStaticKeyPair =
|
||||
new KeyPair(ourStaticPublicKey, ourStaticPrivateKey);
|
||||
private final PublicKey theirEphemeralPublicKey = getAgreementPublicKey();
|
||||
private final PublicKey ourEphemeralPublicKey = getAgreementPublicKey();
|
||||
private final PrivateKey ourEphemeralPrivateKey = getAgreementPrivateKey();
|
||||
private final KeyPair ourEphemeralKeyPair =
|
||||
new KeyPair(ourEphemeralPublicKey, ourEphemeralPrivateKey);
|
||||
private final SecretKey masterKey = getSecretKey();
|
||||
private final byte[] ourProof = getRandomBytes(PROOF_BYTES);
|
||||
private final byte[] theirProof = getRandomBytes(PROOF_BYTES);
|
||||
|
||||
private final InputStream in = new ByteArrayInputStream(new byte[0]);
|
||||
private final OutputStream out = new ByteArrayOutputStream(0);
|
||||
|
||||
private final HandshakeManagerImpl handshakeManager =
|
||||
new HandshakeManagerImpl(db, identityManager, contactManager,
|
||||
transportCrypto, handshakeCrypto, recordReaderFactory,
|
||||
recordWriterFactory);
|
||||
|
||||
@Test
|
||||
public void testHandshakeAsAliceWithPeerVersion_0_1() throws Exception {
|
||||
testHandshakeWithPeerVersion_0_1(true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHandshakeAsBobWithPeerVersion_0_1() throws Exception {
|
||||
testHandshakeWithPeerVersion_0_1(false);
|
||||
}
|
||||
|
||||
private void testHandshakeWithPeerVersion_0_1(boolean alice)
|
||||
throws Exception {
|
||||
expectPrepareForHandshake(alice);
|
||||
expectSendMinorVersion();
|
||||
expectSendKey();
|
||||
// Remote peer sends minor version, so use new key derivation
|
||||
expectReceiveMinorVersion();
|
||||
expectReceiveKey();
|
||||
expectDeriveMasterKey_0_1(alice);
|
||||
expectDeriveProof(alice);
|
||||
expectSendProof();
|
||||
expectReceiveProof();
|
||||
expectSendEof();
|
||||
expectReceiveEof();
|
||||
expectVerifyOwnership(alice, true);
|
||||
|
||||
HandshakeResult result = handshakeManager.handshake(
|
||||
pendingContact.getId(), in, streamWriter);
|
||||
|
||||
assertArrayEquals(masterKey.getBytes(),
|
||||
result.getMasterKey().getBytes());
|
||||
assertEquals(alice, result.isAlice());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHandshakeAsAliceWithPeerVersion_0_0() throws Exception {
|
||||
testHandshakeWithPeerVersion_0_0(true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHandshakeAsBobWithPeerVersion_0_0() throws Exception {
|
||||
testHandshakeWithPeerVersion_0_0(false);
|
||||
}
|
||||
|
||||
private void testHandshakeWithPeerVersion_0_0(boolean alice)
|
||||
throws Exception {
|
||||
expectPrepareForHandshake(alice);
|
||||
expectSendMinorVersion();
|
||||
expectSendKey();
|
||||
// Remote peer does not send minor version, so use old key derivation
|
||||
expectReceiveKey();
|
||||
expectDeriveMasterKey_0_0(alice);
|
||||
expectDeriveProof(alice);
|
||||
expectSendProof();
|
||||
expectReceiveProof();
|
||||
expectSendEof();
|
||||
expectReceiveEof();
|
||||
expectVerifyOwnership(alice, true);
|
||||
|
||||
HandshakeResult result = handshakeManager.handshake(
|
||||
pendingContact.getId(), in, streamWriter);
|
||||
|
||||
assertArrayEquals(masterKey.getBytes(),
|
||||
result.getMasterKey().getBytes());
|
||||
assertEquals(alice, result.isAlice());
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testProofOfOwnershipNotVerifiedAsAlice() throws Exception {
|
||||
testProofOfOwnershipNotVerified(true);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testProofOfOwnershipNotVerifiedAsBob() throws Exception {
|
||||
testProofOfOwnershipNotVerified(false);
|
||||
}
|
||||
|
||||
private void testProofOfOwnershipNotVerified(boolean alice)
|
||||
throws Exception {
|
||||
expectPrepareForHandshake(alice);
|
||||
expectSendMinorVersion();
|
||||
expectSendKey();
|
||||
expectReceiveMinorVersion();
|
||||
expectReceiveKey();
|
||||
expectDeriveMasterKey_0_1(alice);
|
||||
expectDeriveProof(alice);
|
||||
expectSendProof();
|
||||
expectReceiveProof();
|
||||
expectSendEof();
|
||||
expectReceiveEof();
|
||||
expectVerifyOwnership(alice, false);
|
||||
|
||||
handshakeManager.handshake(pendingContact.getId(), in, streamWriter);
|
||||
}
|
||||
|
||||
private void expectPrepareForHandshake(boolean alice) throws Exception {
|
||||
Transaction txn = new Transaction(null, true);
|
||||
|
||||
context.checking(new DbExpectations() {{
|
||||
oneOf(db).transactionWithResult(with(true), withDbCallable(txn));
|
||||
oneOf(contactManager).getPendingContact(txn,
|
||||
pendingContact.getId());
|
||||
will(returnValue(pendingContact));
|
||||
oneOf(identityManager).getHandshakeKeys(txn);
|
||||
will(returnValue(ourStaticKeyPair));
|
||||
oneOf(transportCrypto).isAlice(theirStaticPublicKey,
|
||||
ourStaticKeyPair);
|
||||
will(returnValue(alice));
|
||||
oneOf(recordReaderFactory).createRecordReader(in);
|
||||
will(returnValue(recordReader));
|
||||
oneOf(streamWriter).getOutputStream();
|
||||
will(returnValue(out));
|
||||
oneOf(recordWriterFactory).createRecordWriter(out);
|
||||
will(returnValue(recordWriter));
|
||||
oneOf(handshakeCrypto).generateEphemeralKeyPair();
|
||||
will(returnValue(ourEphemeralKeyPair));
|
||||
}});
|
||||
}
|
||||
|
||||
private void expectSendMinorVersion() throws Exception {
|
||||
expectWriteRecord(new Record(PROTOCOL_MAJOR_VERSION,
|
||||
RECORD_TYPE_MINOR_VERSION,
|
||||
new byte[] {PROTOCOL_MINOR_VERSION}));
|
||||
}
|
||||
|
||||
private void expectReceiveMinorVersion() throws Exception {
|
||||
expectReadRecord(new Record(PROTOCOL_MAJOR_VERSION,
|
||||
RECORD_TYPE_MINOR_VERSION,
|
||||
new byte[] {PROTOCOL_MINOR_VERSION}));
|
||||
}
|
||||
|
||||
private void expectSendKey() throws Exception {
|
||||
expectWriteRecord(new Record(PROTOCOL_MAJOR_VERSION,
|
||||
RECORD_TYPE_EPHEMERAL_PUBLIC_KEY,
|
||||
ourEphemeralPublicKey.getEncoded()));
|
||||
}
|
||||
|
||||
private void expectReceiveKey() throws Exception {
|
||||
expectReadRecord(new Record(PROTOCOL_MAJOR_VERSION,
|
||||
RECORD_TYPE_EPHEMERAL_PUBLIC_KEY,
|
||||
theirEphemeralPublicKey.getEncoded()));
|
||||
}
|
||||
|
||||
private void expectDeriveMasterKey_0_1(boolean alice) throws Exception {
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(handshakeCrypto).deriveMasterKey_0_1(theirStaticPublicKey,
|
||||
theirEphemeralPublicKey, ourStaticKeyPair,
|
||||
ourEphemeralKeyPair, alice);
|
||||
will(returnValue(masterKey));
|
||||
}});
|
||||
}
|
||||
|
||||
private void expectDeriveMasterKey_0_0(boolean alice) throws Exception {
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(handshakeCrypto).deriveMasterKey_0_0(theirStaticPublicKey,
|
||||
theirEphemeralPublicKey, ourStaticKeyPair,
|
||||
ourEphemeralKeyPair, alice);
|
||||
will(returnValue(masterKey));
|
||||
}});
|
||||
}
|
||||
|
||||
private void expectDeriveProof(boolean alice) {
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(handshakeCrypto).proveOwnership(masterKey, alice);
|
||||
will(returnValue(ourProof));
|
||||
}});
|
||||
}
|
||||
|
||||
private void expectSendProof() throws Exception {
|
||||
expectWriteRecord(new Record(PROTOCOL_MAJOR_VERSION,
|
||||
RECORD_TYPE_PROOF_OF_OWNERSHIP, ourProof));
|
||||
}
|
||||
|
||||
private void expectReceiveProof() throws Exception {
|
||||
expectReadRecord(new Record(PROTOCOL_MAJOR_VERSION,
|
||||
RECORD_TYPE_PROOF_OF_OWNERSHIP, theirProof));
|
||||
}
|
||||
|
||||
private void expectSendEof() throws Exception {
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(streamWriter).sendEndOfStream();
|
||||
}});
|
||||
}
|
||||
|
||||
private void expectReceiveEof() throws Exception {
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(recordReader).readRecord(with(any(RecordPredicate.class)),
|
||||
with(any(RecordPredicate.class)));
|
||||
will(returnValue(null));
|
||||
}});
|
||||
}
|
||||
|
||||
private void expectVerifyOwnership(boolean alice, boolean verified) {
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(handshakeCrypto).verifyOwnership(masterKey, !alice,
|
||||
theirProof);
|
||||
will(returnValue(verified));
|
||||
}});
|
||||
}
|
||||
|
||||
private void expectWriteRecord(Record record) throws Exception {
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(recordWriter).writeRecord(with(new PredicateMatcher<>(
|
||||
Record.class, r -> recordEquals(record, r))));
|
||||
oneOf(recordWriter).flush();
|
||||
}});
|
||||
}
|
||||
|
||||
private boolean recordEquals(Record expected, Record actual) {
|
||||
return expected.getProtocolVersion() == actual.getProtocolVersion() &&
|
||||
expected.getRecordType() == actual.getRecordType() &&
|
||||
Arrays.equals(expected.getPayload(), actual.getPayload());
|
||||
}
|
||||
|
||||
private void expectReadRecord(Record record) throws Exception {
|
||||
context.checking(new Expectations() {{
|
||||
// Test that the `accept` predicate passed to the reader would
|
||||
// accept the expected record
|
||||
oneOf(recordReader).readRecord(with(new PredicateMatcher<>(
|
||||
RecordPredicate.class, rp -> rp.test(record))),
|
||||
with(any(RecordPredicate.class)));
|
||||
will(returnValue(record));
|
||||
}});
|
||||
}
|
||||
}
|
||||
@@ -60,6 +60,22 @@ public class KeyAgreementTest extends BrambleTestCase {
|
||||
assertArrayEquals(aShared.getBytes(), bShared.getBytes());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDerivesStaticEphemeralSharedSecretBadly() throws Exception {
|
||||
String label = getRandomString(123);
|
||||
KeyPair aStatic = crypto.generateAgreementKeyPair();
|
||||
KeyPair aEphemeral = crypto.generateAgreementKeyPair();
|
||||
KeyPair bStatic = crypto.generateAgreementKeyPair();
|
||||
KeyPair bEphemeral = crypto.generateAgreementKeyPair();
|
||||
SecretKey aShared = crypto.deriveSharedSecretBadly(label,
|
||||
bStatic.getPublic(), bEphemeral.getPublic(), aStatic,
|
||||
aEphemeral, true, inputs);
|
||||
SecretKey bShared = crypto.deriveSharedSecretBadly(label,
|
||||
aStatic.getPublic(), aEphemeral.getPublic(), bStatic,
|
||||
bEphemeral, false, inputs);
|
||||
assertArrayEquals(aShared.getBytes(), bShared.getBytes());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDerivesStaticEphemeralSharedSecret() throws Exception {
|
||||
String label = getRandomString(123);
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package org.briarproject.bramble.keyagreement;
|
||||
|
||||
import org.briarproject.bramble.api.Predicate;
|
||||
import org.briarproject.bramble.api.keyagreement.KeyAgreementConnection;
|
||||
import org.briarproject.bramble.api.plugin.TransportConnectionReader;
|
||||
import org.briarproject.bramble.api.plugin.TransportConnectionWriter;
|
||||
@@ -8,11 +7,13 @@ import org.briarproject.bramble.api.plugin.TransportId;
|
||||
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
|
||||
import org.briarproject.bramble.api.record.Record;
|
||||
import org.briarproject.bramble.api.record.RecordReader;
|
||||
import org.briarproject.bramble.api.record.RecordReader.RecordPredicate;
|
||||
import org.briarproject.bramble.api.record.RecordReaderFactory;
|
||||
import org.briarproject.bramble.api.record.RecordWriter;
|
||||
import org.briarproject.bramble.api.record.RecordWriterFactory;
|
||||
import org.briarproject.bramble.test.BrambleMockTestCase;
|
||||
import org.briarproject.bramble.test.CaptureArgumentAction;
|
||||
import org.briarproject.bramble.test.PredicateMatcher;
|
||||
import org.jmock.Expectations;
|
||||
import org.jmock.imposters.ByteBuddyClassImposteriser;
|
||||
import org.junit.Test;
|
||||
@@ -21,8 +22,6 @@ import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.PROTOCOL_VERSION;
|
||||
import static org.briarproject.bramble.api.keyagreement.RecordTypes.ABORT;
|
||||
import static org.briarproject.bramble.api.keyagreement.RecordTypes.CONFIRM;
|
||||
@@ -119,7 +118,7 @@ public class KeyAgreementTransportTest extends BrambleMockTestCase {
|
||||
public void testReceiveKeyThrowsExceptionIfAtEndOfStream()
|
||||
throws Exception {
|
||||
setup();
|
||||
expectReadRecord(null);
|
||||
expectReadEof();
|
||||
|
||||
kat.receiveKey();
|
||||
}
|
||||
@@ -148,7 +147,7 @@ public class KeyAgreementTransportTest extends BrambleMockTestCase {
|
||||
public void testReceiveConfirmThrowsExceptionIfAtEndOfStream()
|
||||
throws Exception {
|
||||
setup();
|
||||
expectReadRecord(null);
|
||||
expectReadEof();
|
||||
|
||||
kat.receiveConfirm();
|
||||
}
|
||||
@@ -209,12 +208,22 @@ public class KeyAgreementTransportTest extends BrambleMockTestCase {
|
||||
assertArrayEquals(expectedPayload, actual.getPayload());
|
||||
}
|
||||
|
||||
private void expectReadRecord(@Nullable Record record) throws Exception {
|
||||
private void expectReadRecord(Record record) throws Exception {
|
||||
context.checking(new Expectations() {{
|
||||
//noinspection unchecked
|
||||
oneOf(recordReader).readRecord(with(any(Predicate.class)),
|
||||
with(any(Predicate.class)));
|
||||
// Test that the `accept` predicate passed to the reader would
|
||||
// accept the expected record
|
||||
oneOf(recordReader).readRecord(with(new PredicateMatcher<>(
|
||||
RecordPredicate.class, rp -> rp.test(record))),
|
||||
with(any(RecordPredicate.class)));
|
||||
will(returnValue(record));
|
||||
}});
|
||||
}
|
||||
|
||||
private void expectReadEof() throws Exception {
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(recordReader).readRecord(with(any(RecordPredicate.class)),
|
||||
with(any(RecordPredicate.class)));
|
||||
will(returnValue(null));
|
||||
}});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package org.briarproject.bramble.record;
|
||||
|
||||
import org.briarproject.bramble.api.FormatException;
|
||||
import org.briarproject.bramble.api.Predicate;
|
||||
import org.briarproject.bramble.api.record.Record;
|
||||
import org.briarproject.bramble.api.record.RecordReader;
|
||||
import org.briarproject.bramble.api.record.RecordReader.RecordPredicate;
|
||||
import org.briarproject.bramble.test.BrambleTestCase;
|
||||
import org.briarproject.bramble.util.ByteUtils;
|
||||
import org.junit.Test;
|
||||
@@ -128,12 +128,12 @@ public class RecordReaderImplTest extends BrambleTestCase {
|
||||
RecordReader reader = new RecordReaderImpl(in);
|
||||
|
||||
// Accept records with version 0, type 0 or 1
|
||||
Predicate<Record> accept = r -> {
|
||||
RecordPredicate accept = r -> {
|
||||
byte version = r.getProtocolVersion(), type = r.getRecordType();
|
||||
return version == 0 && (type == 0 || type == 1);
|
||||
};
|
||||
// Ignore records with version 0, any other type
|
||||
Predicate<Record> ignore = r -> {
|
||||
RecordPredicate ignore = r -> {
|
||||
byte version = r.getProtocolVersion(), type = r.getRecordType();
|
||||
return version == 0 && !(type == 0 || type == 1);
|
||||
};
|
||||
@@ -183,12 +183,12 @@ public class RecordReaderImplTest extends BrambleTestCase {
|
||||
RecordReader reader = new RecordReaderImpl(in);
|
||||
|
||||
// Accept records with version 0, type 0 or 1
|
||||
Predicate<Record> accept = r -> {
|
||||
RecordPredicate accept = r -> {
|
||||
byte version = r.getProtocolVersion(), type = r.getRecordType();
|
||||
return version == 0 && (type == 0 || type == 1);
|
||||
};
|
||||
// Ignore records with version 0, any other type
|
||||
Predicate<Record> ignore = r -> {
|
||||
RecordPredicate ignore = r -> {
|
||||
byte version = r.getProtocolVersion(), type = r.getRecordType();
|
||||
return version == 0 && !(type == 0 || type == 1);
|
||||
};
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
package org.briarproject.bramble.sync;
|
||||
|
||||
import org.briarproject.bramble.api.FormatException;
|
||||
import org.briarproject.bramble.api.Predicate;
|
||||
import org.briarproject.bramble.api.UniqueId;
|
||||
import org.briarproject.bramble.api.record.Record;
|
||||
import org.briarproject.bramble.api.record.RecordReader;
|
||||
import org.briarproject.bramble.api.record.RecordReader.RecordPredicate;
|
||||
import org.briarproject.bramble.api.sync.Ack;
|
||||
import org.briarproject.bramble.api.sync.GroupId;
|
||||
import org.briarproject.bramble.api.sync.Message;
|
||||
@@ -24,8 +24,6 @@ import org.junit.Test;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.util.List;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import static org.briarproject.bramble.api.record.Record.MAX_RECORD_PAYLOAD_BYTES;
|
||||
import static org.briarproject.bramble.api.sync.RecordTypes.ACK;
|
||||
import static org.briarproject.bramble.api.sync.RecordTypes.MESSAGE;
|
||||
@@ -186,7 +184,7 @@ public class SyncRecordReaderImplTest extends BrambleMockTestCase {
|
||||
@Test
|
||||
public void testEofReturnsTrueWhenAtEndOfStream() throws Exception {
|
||||
expectReadRecord(createAck());
|
||||
expectReadRecord(null);
|
||||
expectReadEof();
|
||||
|
||||
SyncRecordReader reader =
|
||||
new SyncRecordReaderImpl(messageFactory, recordReader);
|
||||
@@ -212,15 +210,25 @@ public class SyncRecordReaderImplTest extends BrambleMockTestCase {
|
||||
}});
|
||||
}
|
||||
|
||||
private void expectReadRecord(@Nullable Record record) throws Exception {
|
||||
private void expectReadRecord(Record record) throws Exception {
|
||||
context.checking(new Expectations() {{
|
||||
//noinspection unchecked
|
||||
oneOf(recordReader).readRecord(with(any(Predicate.class)),
|
||||
with(any(Predicate.class)));
|
||||
// Test that the `accept` predicate passed to the reader would
|
||||
// accept the expected record
|
||||
oneOf(recordReader).readRecord(with(new PredicateMatcher<>(
|
||||
RecordPredicate.class, rp -> rp.test(record))),
|
||||
with(any(RecordPredicate.class)));
|
||||
will(returnValue(record));
|
||||
}});
|
||||
}
|
||||
|
||||
private void expectReadEof() throws Exception {
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(recordReader).readRecord(with(any(RecordPredicate.class)),
|
||||
with(any(RecordPredicate.class)));
|
||||
will(returnValue(null));
|
||||
}});
|
||||
}
|
||||
|
||||
private Record createMessage(int payloadLength) {
|
||||
return new Record(PROTOCOL_VERSION, MESSAGE, new byte[payloadLength]);
|
||||
}
|
||||
|
||||
@@ -234,12 +234,12 @@ public abstract class BrambleIntegrationTest<C extends BrambleIntegrationTestCom
|
||||
}
|
||||
|
||||
protected void awaitPendingMessageDelivery(int num)
|
||||
throws TimeoutException {
|
||||
throws TimeoutException, InterruptedException {
|
||||
awaitPendingMessageDelivery(num, TIMEOUT);
|
||||
}
|
||||
|
||||
protected void awaitPendingMessageDelivery(int num, long timeout)
|
||||
throws TimeoutException {
|
||||
throws TimeoutException, InterruptedException {
|
||||
deliveryWaiter.await(timeout, num);
|
||||
assertEquals("Messages delivered", num, deliveryCounter.getAndSet(0));
|
||||
|
||||
|
||||
@@ -6,10 +6,10 @@ dependencyVerification {
|
||||
'com.fasterxml.jackson.core:jackson-databind:2.13.4:jackson-databind-2.13.4.jar:c9faff420d9e2c7e1e4711dbeebec2506a32c9942027211c5c293d8d87807eb6',
|
||||
'com.google.code.findbugs:annotations:3.0.1:annotations-3.0.1.jar:6b47ff0a6de0ce17cbedc3abb0828ca5bce3009d53ea47b3723ff023c4742f79',
|
||||
'com.google.code.findbugs:jsr305:3.0.2:jsr305-3.0.2.jar:766ad2a0783f2687962c8ad74ceecc38a28b9f72a2d085ee438b7813e928d0c7',
|
||||
'com.google.dagger:dagger-compiler:2.43.2:dagger-compiler-2.43.2.jar:298c020ee6ed2f4cc651ebbfdb7f8de329b07c44b618d65be117846a850e2a03',
|
||||
'com.google.dagger:dagger-producers:2.43.2:dagger-producers-2.43.2.jar:e7f5d9ffc85d48a49c8e22e02833d418f7ccad5d7512f529964db5127ab915ff',
|
||||
'com.google.dagger:dagger-spi:2.43.2:dagger-spi-2.43.2.jar:3bae8d9dadeaaa5927da6f094389a560c12c05fec3d2711d2fa79292c7a7d7ad',
|
||||
'com.google.dagger:dagger:2.43.2:dagger-2.43.2.jar:c89681f7cbbf8c527bf4ac2748515d617fdb54a1d425c08d914fdc28192b5fe4',
|
||||
'com.google.dagger:dagger-compiler:2.45:dagger-compiler-2.45.jar:5617dfb994537dba5b41f3744a6dd13ec3cd99789c065e0d5c6fa9f21cf7ca25',
|
||||
'com.google.dagger:dagger-producers:2.45:dagger-producers-2.45.jar:a05abb4c3ccf6bb0f056bdcb5ef973898ecf172952ab5948a824aeea6c86ecaa',
|
||||
'com.google.dagger:dagger-spi:2.45:dagger-spi-2.45.jar:7cd6f0b09d88e64a9c97bc80e544ab8ac8fdee9301754413585a74cf64222b27',
|
||||
'com.google.dagger:dagger:2.45:dagger-2.45.jar:f011cae7d2c0fb7ea17c34e05bc10e768b1081a5892ad019cf1fdb0e125c49c1',
|
||||
'com.google.devtools.ksp:symbol-processing-api:1.7.0-1.0.6:symbol-processing-api-1.7.0-1.0.6.jar:adc29417be5ca9ff42118105fea4e36d9ef44987abfc41432309371a60198941',
|
||||
'com.google.errorprone:error_prone_annotations:2.7.1:error_prone_annotations-2.7.1.jar:cd5257c08a246cf8628817ae71cb822be192ef91f6881ca4a3fcff4f1de1cff3',
|
||||
'com.google.errorprone:javac-shaded:9-dev-r4023-3:javac-shaded-9-dev-r4023-3.jar:65bfccf60986c47fbc17c9ebab0be626afc41741e0a6ec7109e0768817a36f30',
|
||||
@@ -19,42 +19,44 @@ dependencyVerification {
|
||||
'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.h2database:h2:1.4.192:h2-1.4.192.jar:225b22e9857235c46c93861410b60b8c81c10dc8985f4faf188985ba5445126c',
|
||||
'com.squareup.okhttp3:mockwebserver:4.9.3:mockwebserver-4.9.3.jar:9c8c581c29f22f877a35d11380462f75bb24bf1886204fe835ee695594a2784e',
|
||||
'com.squareup.okhttp3:okhttp:3.12.13:okhttp-3.12.13.jar:508234e024ef7e270ab1a6d5b356f5b98e786511239ca986d684fd1e2cf7bc82',
|
||||
'com.squareup.okhttp3:okhttp:4.9.3:okhttp-4.9.3.jar:93ecd6cba19d87dccfe566ec848d91aae799e3cf16c00709358ea69bd9227219',
|
||||
'com.squareup.okio:okio:1.15.0:okio-1.15.0.jar:693fa319a7e8843300602b204023b7674f106ebcb577f2dd5807212b66118bd2',
|
||||
'com.squareup.okio:okio:2.8.0:okio-jvm-2.8.0.jar:4496b06e73982fcdd8a5393f46e5df2ce2fa4465df5895454cac68a32f09bbc8',
|
||||
'com.squareup.okhttp3:mockwebserver:4.10.0:mockwebserver-4.10.0.jar:af29da234e63159d6e0dea43bf8288eea97d71cdf1651a5ee2d6c0d0d4adbf8f',
|
||||
'com.squareup.okhttp3:okhttp:4.10.0:okhttp-4.10.0.jar:7580f14fa1691206e37081ad3f92063b1603b328da0bb316f2fef02e0562e7ec',
|
||||
'com.squareup.okio:okio-jvm:3.0.0:okio-jvm-3.0.0.jar:be64a0cc1f28ea9cd5c970dd7e7557af72c808d738c495b397bf897c9921e907',
|
||||
'com.squareup:javapoet:1.13.0:javapoet-1.13.0.jar:4c7517e848a71b36d069d12bb3bf46a70fd4cda3105d822b0ed2e19c00b69291',
|
||||
'com.squareup:kotlinpoet:1.11.0:kotlinpoet-1.11.0.jar:2887ada1ca03dd83baa2758640d87e840d1907564db0ef88d2289c868a980492',
|
||||
'javax.inject:javax.inject:1:javax.inject-1.jar:91c77044a50c481636c32d916fd89c9118a72195390452c81065080f957de7ff',
|
||||
'junit:junit:4.13.2:junit-4.13.2.jar:8e495b634469d64fb8acfa3495a065cbacc8a0fff55ce1e31007be4c16dc57d3',
|
||||
'net.bytebuddy:byte-buddy:1.9.12:byte-buddy-1.9.12.jar:3688c3d434bebc3edc5516296a2ed0f47b65e451071b4afecad84f902f0efc11',
|
||||
'net.i2p.crypto:eddsa:0.2.0:eddsa-0.2.0.jar:a7cb1b85c16e2f0730b9204106929a1d9aaae1df728adc7041a8b8b605692140',
|
||||
'net.jcip:jcip-annotations:1.0:jcip-annotations-1.0.jar:be5805392060c71474bf6c9a67a099471274d30b83eef84bfc4e0889a4f1dcc0',
|
||||
'net.jodah:concurrentunit:0.4.2:concurrentunit-0.4.2.jar:5583078e1acf91734939e985bc9e7ee947b0e93a8eef679da6bb07bbeb47ced3',
|
||||
'net.jodah:concurrentunit:0.4.6:concurrentunit-0.4.6.jar:760e6d4ab7801484de09da621b61141f3b2c2432949da9eb13f076e5d9a5e0a5',
|
||||
'net.ltgt.gradle.incap:incap:0.2:incap-0.2.jar:b625b9806b0f1e4bc7a2e3457119488de3cd57ea20feedd513db070a573a4ffd',
|
||||
'org.apache-extras.beanshell:bsh:2.0b6:bsh-2.0b6.jar:a17955976070c0573235ee662f2794a78082758b61accffce8d3f8aedcd91047',
|
||||
'org.bitlet:weupnp:0.1.4:weupnp-0.1.4.jar:88df7e6504929d00bdb832863761385c68ab92af945b04f0770b126270a444fb',
|
||||
'org.bouncycastle:bcprov-jdk15to18:1.71:bcprov-jdk15to18-1.71.jar:143aaa4a40edd5fc2a18db7900059f6c16f4d931b94b94b20f7e2238e6662886',
|
||||
'org.briarproject:jtorctl:0.5:jtorctl-0.5.jar:43f8c7d390169772b9a2c82ab806c8414c136a2a8636c555e22754bb7260793b',
|
||||
'org.briarproject:null-safety:0.1:null-safety-0.1.jar:161760de5e838cb982bafa973df820675d4397098e9a91637a36a306d43ba011',
|
||||
'org.briarproject:onionwrapper-core:0.0.1:onionwrapper-core-0.0.1.jar:a1937506b00ee6620e909a500e5d004be81f94a6f7d7c898e1a9e841a8ae8a2a',
|
||||
'org.briarproject:onionwrapper-core:0.0.2:onionwrapper-core-0.0.2.jar:7038e960c9e59803f0e2c19444dbb5214cd99e5a7463c0a01c45318e07a0eb80',
|
||||
'org.briarproject:socks-socket:0.1:socks-socket-0.1.jar:e5898822d10f5390363c5dddb945891648c92cf93ba50709e07f0d173ec0eb4b',
|
||||
'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.codehaus.mojo.signature:java16:1.1:java16-1.1.signature:53799223a2c98dba2d0add810bed76315460df285c69e4f397ae6098f87dd619',
|
||||
'org.codehaus.mojo:animal-sniffer-ant-tasks:1.20:animal-sniffer-ant-tasks-1.20.jar:bb7d2498144118311d968bb08ff6fae3fc535fb1cb9cca8b8e9ea65b189422ac',
|
||||
'org.codehaus.mojo:animal-sniffer:1.20:animal-sniffer-1.20.jar:80c422523c38db91260c6d78e5ee4b012862ab61cc55020c9e243dd7b5c62249',
|
||||
'org.codehaus.mojo:animal-sniffer-ant-tasks:1.22:animal-sniffer-ant-tasks-1.22.jar:3f6afeb3e09301d2d7179ed1db21e3ad8846c1e38415ad832a395138ae3f4218',
|
||||
'org.codehaus.mojo:animal-sniffer:1.22:animal-sniffer-1.22.jar:f18c11a25bdd8b520b9c6a28cbb6f33007c812ab0051b6be3f0778e660aa501c',
|
||||
'org.hamcrest:hamcrest-core:2.1:hamcrest-core-2.1.jar:e09109e54a289d88506b9bfec987ddd199f4217c9464132668351b9a4f00bee9',
|
||||
'org.hamcrest:hamcrest-library:2.1:hamcrest-library-2.1.jar:b7e2b6895b3b679f0e47b6380fda391b225e9b78505db9d8bdde8d3cc8d52a21',
|
||||
'org.hamcrest:hamcrest:2.1:hamcrest-2.1.jar:ba93b2e3a562322ba432f0a1b53addcc55cb188253319a020ed77f824e692050',
|
||||
'org.hsqldb:hsqldb:2.3.5:hsqldb-2.3.5.jar:6676a6977ac98997a80f827ddbd3fe8ca1e0853dad1492512135fd1a222ccfad',
|
||||
'org.jetbrains.kotlin:kotlin-stdlib-common:1.4.10:kotlin-stdlib-common-1.4.10.jar:4681f2d436a68c7523595d84ed5758e1382f9da0f67c91e6a848690d711274fe',
|
||||
'org.jetbrains.kotlin:kotlin-reflect:1.6.10:kotlin-reflect-1.6.10.jar:3277ac102ae17aad10a55abec75ff5696c8d109790396434b496e75087854203',
|
||||
'org.jetbrains.kotlin:kotlin-stdlib-common:1.6.20:kotlin-stdlib-common-1.6.20.jar:8da40a2520d30dcb1012176fe93d24e82d08a3e346c37e0343b0fb6f64f6be01',
|
||||
'org.jetbrains.kotlin:kotlin-stdlib-common:1.7.0:kotlin-stdlib-common-1.7.0.jar:59c6ff64fe9a6604afce03e8aaa75f83586c6030ac71fb0b34ee7cdefed3618f',
|
||||
'org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.4.10:kotlin-stdlib-jdk7-1.4.10.jar:f9566380c08722c780ce33ceee23e98ddf765ca98fabd3e2fabae7975c8d232b',
|
||||
'org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.5.31:kotlin-stdlib-jdk7-1.5.31.jar:a25bf47353ce899d843cbddee516d621a73473e7fba97f8d0301e7b4aed7c15f',
|
||||
'org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.6.10:kotlin-stdlib-jdk7-1.6.10.jar:2aedcdc6b69b33bdf5cc235bcea88e7cf6601146bb6bcdffdb312bbacd7be261',
|
||||
'org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.7.0:kotlin-stdlib-jdk7-1.7.0.jar:07e91be9b2ca20672d2bdb7e181b766e73453a2da13492b5ddaee8fa47aea239',
|
||||
'org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.4.10:kotlin-stdlib-jdk8-1.4.10.jar:39b7a9442d7a3865e0f4a732c56c1d5da0e11ffb3bb82a461d32deb0c0ca7673',
|
||||
'org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.5.31:kotlin-stdlib-jdk8-1.5.31.jar:b548f7767aacf029d2417e47440742bd6d3ebede19b60386e23554ce5c4c5fdc',
|
||||
'org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.6.10:kotlin-stdlib-jdk8-1.6.10.jar:1456d82d039ea30d8485b032901f52bbf07e7cdbe8bb1f8708ad32a8574c41ce',
|
||||
'org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.7.0:kotlin-stdlib-jdk8-1.7.0.jar:cf058e11db1dfc9944680c8c61b95ac689aaaa8a3eb30bced028100f038f030b',
|
||||
'org.jetbrains.kotlin:kotlin-stdlib:1.4.10:kotlin-stdlib-1.4.10.jar:01ecb09782c042b931c1839acf21a188340b295d05400afd6e3415d4475b8daa',
|
||||
'org.jetbrains.kotlin:kotlin-stdlib:1.6.20:kotlin-stdlib-1.6.20.jar:eeb51c2b67b26233fd81d0bc4f8044ec849718890905763ceffd84a31e2cb799',
|
||||
'org.jetbrains.kotlin:kotlin-stdlib:1.7.0:kotlin-stdlib-1.7.0.jar:aa88e9625577957f3249a46cb6e166ee09b369e600f7a11d148d16b0a6d87f05',
|
||||
'org.jetbrains.kotlinx:kotlinx-metadata-jvm:0.5.0:kotlinx-metadata-jvm-0.5.0.jar:ca063a96639b08b9eaa0de4d65e899480740a6efbe28ab9a8681a2ced03055a4',
|
||||
'org.jetbrains:annotations:13.0:annotations-13.0.jar:ace2a10dc8e2d5fd34925ecac03e4988b2c0f851650c94b8cef49ba1bd111478',
|
||||
@@ -65,7 +67,7 @@ dependencyVerification {
|
||||
'org.jmock:jmock:2.12.0:jmock-2.12.0.jar:266d07314c0cd343c46ff8a55601272de8cf406807caf55e6f313295f83d10be',
|
||||
'org.objenesis:objenesis:3.0.1:objenesis-3.0.1.jar:7a8ff780b9ff48415d7c705f60030b0acaa616e7f823c98eede3b63508d4e984',
|
||||
'org.ow2.asm:asm:7.1:asm-7.1.jar:4ab2fa2b6d2cc9ccb1eaa05ea329c407b47b13ed2915f62f8c4b8cc96258d4de',
|
||||
'org.ow2.asm:asm:9.1:asm-9.1.jar:cda4de455fab48ff0bcb7c48b4639447d4de859a7afc30a094a986f0936beba2',
|
||||
'org.ow2.asm:asm:9.3:asm-9.3.jar:1263369b59e29c943918de11d6d6152e2ec6085ce63e5710516f8c67d368e4bc',
|
||||
'org.whispersystems:curve25519-java:0.5.0:curve25519-java-0.5.0.jar:0aadd43cf01d11e9b58f867b3c4f25c3194e8b0623d1953d32dfbfbee009e38d',
|
||||
]
|
||||
}
|
||||
|
||||
@@ -3,9 +3,9 @@ package org.briarproject.bramble;
|
||||
import org.briarproject.bramble.io.DnsModule;
|
||||
import org.briarproject.bramble.mailbox.ModularMailboxModule;
|
||||
import org.briarproject.bramble.network.JavaNetworkModule;
|
||||
import org.briarproject.bramble.plugin.tor.CircumventionModule;
|
||||
import org.briarproject.bramble.socks.SocksModule;
|
||||
import org.briarproject.bramble.system.JavaSystemModule;
|
||||
import org.briarproject.onionwrapper.CircumventionModule;
|
||||
|
||||
import dagger.Module;
|
||||
|
||||
|
||||
@@ -13,10 +13,10 @@ import org.briarproject.bramble.api.plugin.TorControlPort;
|
||||
import org.briarproject.bramble.api.plugin.TorDirectory;
|
||||
import org.briarproject.bramble.api.plugin.TorSocksPort;
|
||||
import org.briarproject.bramble.api.system.Clock;
|
||||
import org.briarproject.bramble.api.system.LocationUtils;
|
||||
import org.briarproject.bramble.api.system.WakefulIoExecutor;
|
||||
import org.briarproject.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.onionwrapper.CircumventionProvider;
|
||||
import org.briarproject.onionwrapper.LocationUtils;
|
||||
import org.briarproject.onionwrapper.TorWrapper;
|
||||
import org.briarproject.onionwrapper.UnixTorWrapper;
|
||||
|
||||
|
||||
@@ -13,10 +13,10 @@ import org.briarproject.bramble.api.plugin.TorControlPort;
|
||||
import org.briarproject.bramble.api.plugin.TorDirectory;
|
||||
import org.briarproject.bramble.api.plugin.TorSocksPort;
|
||||
import org.briarproject.bramble.api.system.Clock;
|
||||
import org.briarproject.bramble.api.system.LocationUtils;
|
||||
import org.briarproject.bramble.api.system.WakefulIoExecutor;
|
||||
import org.briarproject.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.onionwrapper.CircumventionProvider;
|
||||
import org.briarproject.onionwrapper.LocationUtils;
|
||||
import org.briarproject.onionwrapper.TorWrapper;
|
||||
import org.briarproject.onionwrapper.WindowsTorWrapper;
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ public class DesktopSecureRandomModule {
|
||||
@Provides
|
||||
@Singleton
|
||||
SecureRandomProvider provideSecureRandomProvider() {
|
||||
if (isLinux() || isMac()) return new UnixSecureRandomProvider();
|
||||
if (isLinux()) return new UnixSecureRandomProvider();
|
||||
return () -> null; // Use system default
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
package org.briarproject.bramble.system;
|
||||
|
||||
import org.briarproject.bramble.api.system.LocationUtils;
|
||||
import org.briarproject.nullsafety.NotNullByDefault;
|
||||
|
||||
import java.util.Locale;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
@NotNullByDefault
|
||||
class JavaLocationUtils implements LocationUtils {
|
||||
|
||||
private static final Logger LOG =
|
||||
Logger.getLogger(JavaLocationUtils.class.getName());
|
||||
|
||||
@Inject
|
||||
JavaLocationUtils() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCurrentCountry() {
|
||||
LOG.info("Using user-defined locale");
|
||||
return Locale.getDefault().getCountry();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,7 +1,8 @@
|
||||
package org.briarproject.bramble.system;
|
||||
|
||||
import org.briarproject.bramble.api.system.LocationUtils;
|
||||
import org.briarproject.bramble.api.system.ResourceProvider;
|
||||
import org.briarproject.onionwrapper.JavaLocationUtilsFactory;
|
||||
import org.briarproject.onionwrapper.LocationUtils;
|
||||
|
||||
import javax.inject.Singleton;
|
||||
|
||||
@@ -13,8 +14,8 @@ public class JavaSystemModule {
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
LocationUtils provideLocationUtils(JavaLocationUtils locationUtils) {
|
||||
return locationUtils;
|
||||
LocationUtils provideLocationUtils() {
|
||||
return JavaLocationUtilsFactory.createJavaLocationUtils();
|
||||
}
|
||||
|
||||
@Provides
|
||||
|
||||
@@ -3,10 +3,10 @@ dependencyVerification {
|
||||
'cglib:cglib:3.2.8:cglib-3.2.8.jar:3f64de999ecc5595dc84ca8ff0879d8a34c8623f9ef3c517a53ed59023fcb9db',
|
||||
'com.google.code.findbugs:annotations:3.0.1:annotations-3.0.1.jar:6b47ff0a6de0ce17cbedc3abb0828ca5bce3009d53ea47b3723ff023c4742f79',
|
||||
'com.google.code.findbugs:jsr305:3.0.2:jsr305-3.0.2.jar:766ad2a0783f2687962c8ad74ceecc38a28b9f72a2d085ee438b7813e928d0c7',
|
||||
'com.google.dagger:dagger-compiler:2.43.2:dagger-compiler-2.43.2.jar:298c020ee6ed2f4cc651ebbfdb7f8de329b07c44b618d65be117846a850e2a03',
|
||||
'com.google.dagger:dagger-producers:2.43.2:dagger-producers-2.43.2.jar:e7f5d9ffc85d48a49c8e22e02833d418f7ccad5d7512f529964db5127ab915ff',
|
||||
'com.google.dagger:dagger-spi:2.43.2:dagger-spi-2.43.2.jar:3bae8d9dadeaaa5927da6f094389a560c12c05fec3d2711d2fa79292c7a7d7ad',
|
||||
'com.google.dagger:dagger:2.43.2:dagger-2.43.2.jar:c89681f7cbbf8c527bf4ac2748515d617fdb54a1d425c08d914fdc28192b5fe4',
|
||||
'com.google.dagger:dagger-compiler:2.45:dagger-compiler-2.45.jar:5617dfb994537dba5b41f3744a6dd13ec3cd99789c065e0d5c6fa9f21cf7ca25',
|
||||
'com.google.dagger:dagger-producers:2.45:dagger-producers-2.45.jar:a05abb4c3ccf6bb0f056bdcb5ef973898ecf172952ab5948a824aeea6c86ecaa',
|
||||
'com.google.dagger:dagger-spi:2.45:dagger-spi-2.45.jar:7cd6f0b09d88e64a9c97bc80e544ab8ac8fdee9301754413585a74cf64222b27',
|
||||
'com.google.dagger:dagger:2.45:dagger-2.45.jar:f011cae7d2c0fb7ea17c34e05bc10e768b1081a5892ad019cf1fdb0e125c49c1',
|
||||
'com.google.devtools.ksp:symbol-processing-api:1.7.0-1.0.6:symbol-processing-api-1.7.0-1.0.6.jar:adc29417be5ca9ff42118105fea4e36d9ef44987abfc41432309371a60198941',
|
||||
'com.google.errorprone:error_prone_annotations:2.7.1:error_prone_annotations-2.7.1.jar:cd5257c08a246cf8628817ae71cb822be192ef91f6881ca4a3fcff4f1de1cff3',
|
||||
'com.google.errorprone:javac-shaded:9-dev-r4023-3:javac-shaded-9-dev-r4023-3.jar:65bfccf60986c47fbc17c9ebab0be626afc41741e0a6ec7109e0768817a36f30',
|
||||
@@ -16,6 +16,7 @@ dependencyVerification {
|
||||
'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.squareup:javapoet:1.13.0:javapoet-1.13.0.jar:4c7517e848a71b36d069d12bb3bf46a70fd4cda3105d822b0ed2e19c00b69291',
|
||||
'com.squareup:kotlinpoet:1.11.0:kotlinpoet-1.11.0.jar:2887ada1ca03dd83baa2758640d87e840d1907564db0ef88d2289c868a980492',
|
||||
'javax.inject:javax.inject:1:javax.inject-1.jar:91c77044a50c481636c32d916fd89c9118a72195390452c81065080f957de7ff',
|
||||
'junit:junit:4.13.2:junit-4.13.2.jar:8e495b634469d64fb8acfa3495a065cbacc8a0fff55ce1e31007be4c16dc57d3',
|
||||
'net.bytebuddy:byte-buddy:1.9.12:byte-buddy-1.9.12.jar:3688c3d434bebc3edc5516296a2ed0f47b65e451071b4afecad84f902f0efc11',
|
||||
@@ -26,13 +27,14 @@ dependencyVerification {
|
||||
'org.apache-extras.beanshell:bsh:2.0b6:bsh-2.0b6.jar:a17955976070c0573235ee662f2794a78082758b61accffce8d3f8aedcd91047',
|
||||
'org.briarproject:jtorctl:0.5:jtorctl-0.5.jar:43f8c7d390169772b9a2c82ab806c8414c136a2a8636c555e22754bb7260793b',
|
||||
'org.briarproject:null-safety:0.1:null-safety-0.1.jar:161760de5e838cb982bafa973df820675d4397098e9a91637a36a306d43ba011',
|
||||
'org.briarproject:onionwrapper-core:0.0.1:onionwrapper-core-0.0.1.jar:a1937506b00ee6620e909a500e5d004be81f94a6f7d7c898e1a9e841a8ae8a2a',
|
||||
'org.briarproject:onionwrapper-java:0.0.1:onionwrapper-java-0.0.1.jar:102ccea934d02b13702fd28e890e27e342db8b669a4c84bb54a3783cb8926552',
|
||||
'org.briarproject:onionwrapper-core:0.0.2:onionwrapper-core-0.0.2.jar:7038e960c9e59803f0e2c19444dbb5214cd99e5a7463c0a01c45318e07a0eb80',
|
||||
'org.briarproject:onionwrapper-java:0.0.2:onionwrapper-java-0.0.2.jar:87a3f4082174dbbd32c4f5f062b46af1d3fedd8cfa1ec84f6ce6ccb6e3674fb6',
|
||||
'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.hamcrest:hamcrest-core:2.1:hamcrest-core-2.1.jar:e09109e54a289d88506b9bfec987ddd199f4217c9464132668351b9a4f00bee9',
|
||||
'org.hamcrest:hamcrest-library:2.1:hamcrest-library-2.1.jar:b7e2b6895b3b679f0e47b6380fda391b225e9b78505db9d8bdde8d3cc8d52a21',
|
||||
'org.hamcrest:hamcrest:2.1:hamcrest-2.1.jar:ba93b2e3a562322ba432f0a1b53addcc55cb188253319a020ed77f824e692050',
|
||||
'org.jetbrains.kotlin:kotlin-reflect:1.6.10:kotlin-reflect-1.6.10.jar:3277ac102ae17aad10a55abec75ff5696c8d109790396434b496e75087854203',
|
||||
'org.jetbrains.kotlin:kotlin-stdlib-common:1.7.0:kotlin-stdlib-common-1.7.0.jar:59c6ff64fe9a6604afce03e8aaa75f83586c6030ac71fb0b34ee7cdefed3618f',
|
||||
'org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.7.0:kotlin-stdlib-jdk7-1.7.0.jar:07e91be9b2ca20672d2bdb7e181b766e73453a2da13492b5ddaee8fa47aea239',
|
||||
'org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.7.0:kotlin-stdlib-jdk8-1.7.0.jar:cf058e11db1dfc9944680c8c61b95ac689aaaa8a3eb30bced028100f038f030b',
|
||||
|
||||
@@ -24,10 +24,10 @@ android {
|
||||
}
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion 16
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 31
|
||||
versionCode 10501
|
||||
versionName "1.5.1"
|
||||
versionCode 10503
|
||||
versionName "1.5.3"
|
||||
applicationId "org.briarproject.briar.android"
|
||||
buildConfigField "String", "TorVersion", "\"$tor_version\""
|
||||
|
||||
@@ -62,7 +62,6 @@ android {
|
||||
productFlavors {
|
||||
screenshot {
|
||||
dimension "version"
|
||||
minSdkVersion 21
|
||||
applicationIdSuffix ".screenshot" // = org.briarproject.briar.android.screenshot.debug
|
||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt', 'proguard-test.txt'
|
||||
}
|
||||
@@ -90,6 +89,7 @@ android {
|
||||
|
||||
lintOptions {
|
||||
warning 'MissingTranslation'
|
||||
warning 'MissingDefaultResource'
|
||||
warning 'ImpliedQuantity'
|
||||
warning 'ExtraTranslation'
|
||||
// FIXME
|
||||
@@ -108,24 +108,30 @@ dependencies {
|
||||
implementation project(':bramble-android')
|
||||
implementation project(':briar-core')
|
||||
|
||||
implementation 'androidx.fragment:fragment:1.3.4'
|
||||
implementation 'androidx.preference:preference:1.1.1'
|
||||
implementation 'androidx.exifinterface:exifinterface:1.3.3'
|
||||
implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
|
||||
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
|
||||
implementation 'com.google.android.material:material:1.3.0'
|
||||
implementation 'androidx.fragment:fragment:1.5.5'
|
||||
implementation 'androidx.preference:preference:1.2.0'
|
||||
implementation 'androidx.exifinterface:exifinterface:1.3.6'
|
||||
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.1'
|
||||
implementation 'androidx.lifecycle:lifecycle-livedata:2.5.1'
|
||||
|
||||
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
|
||||
// check https://github.com/material-components/material-components-android/issues/3191
|
||||
// before upgrading material library
|
||||
implementation 'com.google.android.material:material:1.7.0'
|
||||
implementation 'androidx.recyclerview:recyclerview-selection:1.1.0'
|
||||
|
||||
// force kotlin standard lib to latest version to prevent jetifier issues
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
|
||||
implementation "com.squareup.okhttp3:okhttp:$okhttp_version"
|
||||
implementation "org.jsoup:jsoup:$jsoup_version"
|
||||
implementation 'info.guardianproject.panic:panic:1.0'
|
||||
implementation 'de.hdodenhof:circleimageview:3.1.0'
|
||||
implementation 'com.google.zxing:core:3.3.3' // newer version need minSdk 24
|
||||
implementation 'uk.co.samuelwall:material-tap-target-prompt:3.3.0'
|
||||
implementation 'com.vanniktech:emoji-google:0.7.0' // newer versions need minSdk 21
|
||||
implementation 'uk.co.samuelwall:material-tap-target-prompt:3.3.2'
|
||||
implementation 'com.vanniktech:emoji-google:0.9.0' // newer versions are more work to adapt
|
||||
implementation 'com.github.kobakei:MaterialFabSpeedDial:1.2.1'
|
||||
implementation 'com.github.chrisbanes:PhotoView:2.3.0'
|
||||
def glideVersion = '4.12.0'
|
||||
def glideVersion = '4.14.2'
|
||||
implementation("com.github.bumptech.glide:glide:$glideVersion") {
|
||||
exclude group: 'com.android.support'
|
||||
exclude module: 'disklrucache' // when there's no disk cache, we can't accidentally use it
|
||||
@@ -142,12 +148,12 @@ dependencies {
|
||||
|
||||
def espressoVersion = '3.3.0'
|
||||
testImplementation 'androidx.test:runner:1.4.0'
|
||||
testImplementation 'androidx.test.ext:junit:1.1.3'
|
||||
testImplementation 'androidx.test.ext:junit:1.1.5'
|
||||
testImplementation 'androidx.fragment:fragment-testing:1.4.0'
|
||||
testImplementation "androidx.arch.core:core-testing:2.1.0"
|
||||
testImplementation "androidx.test.espresso:espresso-core:$espressoVersion"
|
||||
testImplementation 'org.robolectric:robolectric:4.4'
|
||||
testImplementation 'org.mockito:mockito-core:3.9.0'
|
||||
testImplementation "androidx.test.espresso:espresso-core:3.5.0"
|
||||
testImplementation 'org.robolectric:robolectric:4.8.2' // newer versions have SecureRandom issue
|
||||
testImplementation 'org.mockito:mockito-core:5.1.1'
|
||||
testImplementation "junit:junit:$junit_version"
|
||||
testImplementation "org.jmock:jmock:$jmock_version"
|
||||
testImplementation "org.jmock:jmock-junit4:$jmock_version"
|
||||
|
||||
@@ -99,11 +99,6 @@
|
||||
android:exported="false"
|
||||
android:label="@string/app_name" />
|
||||
|
||||
<activity
|
||||
android:name="org.briarproject.briar.android.splash.ExpiredOldAndroidActivity"
|
||||
android:exported="false"
|
||||
android:label="@string/app_name" />
|
||||
|
||||
<activity
|
||||
android:name="org.briarproject.briar.android.login.StartupActivity"
|
||||
android:exported="false"
|
||||
|
||||
@@ -27,7 +27,6 @@ import org.briarproject.bramble.api.plugin.PluginManager;
|
||||
import org.briarproject.bramble.api.settings.SettingsManager;
|
||||
import org.briarproject.bramble.api.system.AndroidExecutor;
|
||||
import org.briarproject.bramble.api.system.Clock;
|
||||
import org.briarproject.bramble.api.system.LocationUtils;
|
||||
import org.briarproject.bramble.mailbox.ModularMailboxModule;
|
||||
import org.briarproject.bramble.plugin.file.RemovableDriveModule;
|
||||
import org.briarproject.bramble.system.ClockModule;
|
||||
@@ -84,6 +83,7 @@ import org.briarproject.briar.api.privategroup.invitation.GroupInvitationFactory
|
||||
import org.briarproject.briar.api.privategroup.invitation.GroupInvitationManager;
|
||||
import org.briarproject.briar.api.test.TestDataCreator;
|
||||
import org.briarproject.onionwrapper.CircumventionProvider;
|
||||
import org.briarproject.onionwrapper.LocationUtils;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
|
||||
@@ -294,10 +294,8 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
|
||||
b.setOngoing(true);
|
||||
Intent i = new Intent(appContext, SplashScreenActivity.class);
|
||||
b.setContentIntent(getActivity(appContext, 0, i, getImmutableFlags(0)));
|
||||
if (SDK_INT >= 21) {
|
||||
b.setCategory(CATEGORY_SERVICE);
|
||||
b.setVisibility(VISIBILITY_SECRET);
|
||||
}
|
||||
b.setCategory(CATEGORY_SERVICE);
|
||||
b.setVisibility(VISIBILITY_SECRET);
|
||||
b.setPriority(PRIORITY_MIN);
|
||||
return b.build();
|
||||
}
|
||||
@@ -773,8 +771,7 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
|
||||
i.setAction(ACTION_STOP_HOTSPOT);
|
||||
PendingIntent actionIntent =
|
||||
getActivity(appContext, 0, i, getImmutableFlags(0));
|
||||
int icon = SDK_INT >= 21 ? R.drawable.ic_portable_wifi_off : 0;
|
||||
b.addAction(icon, actionTitle, actionIntent);
|
||||
b.addAction(R.drawable.ic_portable_wifi_off, actionTitle, actionIntent);
|
||||
notificationManager.notify(HOTSPOT_NOTIFICATION_ID, b.build());
|
||||
}
|
||||
|
||||
|
||||
@@ -212,7 +212,7 @@ public class AppModule {
|
||||
public Collection<SimplexPluginFactory> getSimplexFactories() {
|
||||
List<SimplexPluginFactory> simplex = new ArrayList<>();
|
||||
simplex.add(mailbox);
|
||||
if (SDK_INT >= 19) simplex.add(drive);
|
||||
simplex.add(drive);
|
||||
return simplex;
|
||||
}
|
||||
|
||||
|
||||
@@ -56,16 +56,8 @@ public class Localizer {
|
||||
// Get Locale from BCP-47 tag
|
||||
@Nullable
|
||||
public static Locale getLocaleFromTag(String tag) {
|
||||
if (tag.equals("default"))
|
||||
return null;
|
||||
if (SDK_INT >= 21) {
|
||||
return Locale.forLanguageTag(tag);
|
||||
}
|
||||
if (tag.contains("-")) {
|
||||
String[] langArray = tag.split("-");
|
||||
return new Locale(langArray[0], langArray[1]);
|
||||
} else
|
||||
return new Locale(tag);
|
||||
if (tag.equals("default")) return null;
|
||||
return Locale.forLanguageTag(tag);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -94,12 +86,8 @@ public class Localizer {
|
||||
if (locale.equals(currentLocale))
|
||||
return context;
|
||||
Locale.setDefault(locale);
|
||||
if (SDK_INT >= 17) {
|
||||
conf.setLocale(locale);
|
||||
context = context.createConfigurationContext(conf);
|
||||
} else
|
||||
conf.locale = locale;
|
||||
//noinspection deprecation
|
||||
conf.setLocale(locale);
|
||||
context = context.createConfigurationContext(conf);
|
||||
res.updateConfiguration(conf, res.getDisplayMetrics());
|
||||
return context;
|
||||
}
|
||||
|
||||
@@ -2,8 +2,8 @@ package org.briarproject.briar.android;
|
||||
|
||||
import org.briarproject.briar.BuildConfig;
|
||||
|
||||
import static android.os.Build.VERSION.SDK_INT;
|
||||
import static java.util.concurrent.TimeUnit.DAYS;
|
||||
import static org.briarproject.briar.BuildConfig.BuildTimestamp;
|
||||
|
||||
public interface TestingConstants {
|
||||
|
||||
@@ -20,15 +20,9 @@ public interface TestingConstants {
|
||||
*/
|
||||
boolean PREVENT_SCREENSHOTS = !IS_DEBUG_BUILD;
|
||||
|
||||
boolean IS_OLD_ANDROID = SDK_INT <= 19;
|
||||
long OLD_ANDROID_WARN_DATE = 1659225600_000L; // 2022-07-31
|
||||
long OLD_ANDROID_EXPIRY_DATE = 1675123200_000L; // 2023-01-31
|
||||
|
||||
/**
|
||||
* Debug builds expire after 90 days. Release builds running on Android 4
|
||||
* expire at a set date, otherwise they expire after 292 million years.
|
||||
* Debug builds expire after 90 days.
|
||||
*/
|
||||
long EXPIRY_DATE = IS_DEBUG_BUILD ?
|
||||
BuildConfig.BuildTimestamp + DAYS.toMillis(90)
|
||||
: (IS_OLD_ANDROID ? OLD_ANDROID_EXPIRY_DATE : Long.MAX_VALUE);
|
||||
BuildTimestamp + DAYS.toMillis(90) : Long.MAX_VALUE;
|
||||
}
|
||||
|
||||
@@ -32,7 +32,6 @@ import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_KEYGU
|
||||
import static org.briarproject.briar.android.util.UiUtils.hasKeyguardLock;
|
||||
import static org.briarproject.briar.android.util.UiUtils.hasUsableFingerprint;
|
||||
|
||||
@RequiresApi(21)
|
||||
@MethodsNotNullByDefault
|
||||
@ParametersNotNullByDefault
|
||||
public class UnlockActivity extends BaseActivity {
|
||||
|
||||
@@ -81,7 +81,6 @@ import org.briarproject.briar.android.sharing.ShareBlogFragment;
|
||||
import org.briarproject.briar.android.sharing.ShareForumActivity;
|
||||
import org.briarproject.briar.android.sharing.ShareForumFragment;
|
||||
import org.briarproject.briar.android.sharing.SharingModule;
|
||||
import org.briarproject.briar.android.splash.ExpiredOldAndroidActivity;
|
||||
import org.briarproject.briar.android.splash.SplashScreenActivity;
|
||||
import org.briarproject.briar.android.test.TestDataActivity;
|
||||
|
||||
@@ -184,8 +183,6 @@ public interface ActivityComponent {
|
||||
|
||||
void inject(RemovableDriveActivity activity);
|
||||
|
||||
void inject(ExpiredOldAndroidActivity activity);
|
||||
|
||||
// Fragments
|
||||
|
||||
void inject(SetupFragment fragment);
|
||||
|
||||
@@ -24,7 +24,6 @@ import java.util.logging.Logger;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import androidx.annotation.RequiresApi;
|
||||
import androidx.annotation.StringRes;
|
||||
import androidx.appcompat.app.ActionBar;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
@@ -105,7 +104,7 @@ public abstract class BriarActivity extends BaseActivity {
|
||||
LOG.info("Not signed in, launching StartupActivity");
|
||||
Intent i = new Intent(this, StartupActivity.class);
|
||||
startActivityForResult(i, REQUEST_PASSWORD);
|
||||
} else if (SDK_INT >= 21 && lockManager.isLocked() && !isFinishing()) {
|
||||
} else if (lockManager.isLocked() && !isFinishing()) {
|
||||
// Also check that the activity isn't finishing already.
|
||||
// This is possible if we finished in onActivityResult().
|
||||
// Launching another UnlockActivity would cause a loop.
|
||||
@@ -135,8 +134,7 @@ public abstract class BriarActivity extends BaseActivity {
|
||||
* @param exitTransition used to move views out when starting a <b>new</b> activity.
|
||||
* @param returnTransition used when window is closing, because the activity is finishing.
|
||||
*/
|
||||
@RequiresApi(api = 21)
|
||||
public void setSceneTransitionAnimation(
|
||||
protected void setSceneTransitionAnimation(
|
||||
@Nullable Transition enterTransition,
|
||||
@Nullable Transition exitTransition,
|
||||
@Nullable Transition returnTransition) {
|
||||
@@ -232,8 +230,7 @@ public abstract class BriarActivity extends BaseActivity {
|
||||
|
||||
@Wakeful
|
||||
private void finishAndExit() {
|
||||
if (SDK_INT >= 21) finishAndRemoveTask();
|
||||
else supportFinishAfterTransition();
|
||||
finishAndRemoveTask();
|
||||
LOG.info("Exiting");
|
||||
BriarApplication app = (BriarApplication) getApplication();
|
||||
if (!app.isInstrumentationTest()) System.exit(0);
|
||||
|
||||
@@ -21,7 +21,6 @@ import androidx.annotation.UiThread;
|
||||
import androidx.core.view.ViewCompat;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import static android.os.Build.VERSION.SDK_INT;
|
||||
import static android.view.View.GONE;
|
||||
import static android.view.View.VISIBLE;
|
||||
import static org.briarproject.briar.android.activity.BriarActivity.GROUP_ID;
|
||||
@@ -134,12 +133,6 @@ class BlogPostViewHolder extends RecyclerView.ViewHolder {
|
||||
} else {
|
||||
reblogger.setVisibility(GONE);
|
||||
}
|
||||
|
||||
// Apply Android 4 padding fix after setting up author/reblogger views
|
||||
if (SDK_INT < 21) {
|
||||
reblogger.setPadding(padding, padding, padding, padding);
|
||||
author.setPadding(padding, padding, padding, padding);
|
||||
}
|
||||
}
|
||||
|
||||
private void onBindComment(BlogCommentItem item, boolean authorClickable) {
|
||||
|
||||
@@ -27,11 +27,9 @@ import javax.annotation.Nullable;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import androidx.activity.result.ActivityResultLauncher;
|
||||
import androidx.annotation.RequiresApi;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.lifecycle.ViewModelProvider;
|
||||
|
||||
import static android.os.Build.VERSION.SDK_INT;
|
||||
import static android.view.View.GONE;
|
||||
import static android.view.View.VISIBLE;
|
||||
import static android.view.inputmethod.EditorInfo.IME_ACTION_DONE;
|
||||
@@ -52,7 +50,6 @@ public class RssFeedImportFragment extends BaseFragment {
|
||||
private Button importButton;
|
||||
private ProgressBar progressBar;
|
||||
|
||||
@RequiresApi(19)
|
||||
private final ActivityResultLauncher<String[]> docLauncher =
|
||||
registerForActivityResult(new OpenDocumentAdvanced(),
|
||||
this::onFileChosen);
|
||||
@@ -74,7 +71,7 @@ public class RssFeedImportFragment extends BaseFragment {
|
||||
@Nullable ViewGroup container,
|
||||
@Nullable Bundle savedInstanceState) {
|
||||
requireActivity().setTitle(getString(R.string.blogs_rss_feeds_import));
|
||||
if (SDK_INT >= 19) setHasOptionsMenu(true);
|
||||
setHasOptionsMenu(true);
|
||||
View v = inflater.inflate(R.layout.fragment_rss_feed_import,
|
||||
container, false);
|
||||
|
||||
@@ -117,15 +114,13 @@ public class RssFeedImportFragment extends BaseFragment {
|
||||
|
||||
@Override
|
||||
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
||||
if (SDK_INT >= 19) {
|
||||
inflater.inflate(R.menu.rss_feed_import_actions, menu);
|
||||
}
|
||||
inflater.inflate(R.menu.rss_feed_import_actions, menu);
|
||||
super.onCreateOptionsMenu(menu, inflater);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
if (item.getItemId() == R.id.action_import_file && SDK_INT >= 19) {
|
||||
if (item.getItemId() == R.id.action_import_file) {
|
||||
launchActivityToOpenFile(requireContext(), docLauncher,
|
||||
contentLauncher, "*/*");
|
||||
return true;
|
||||
|
||||
@@ -55,7 +55,6 @@ import org.briarproject.briar.android.fragment.BaseFragment.BaseFragmentListener
|
||||
import org.briarproject.briar.android.introduction.IntroductionActivity;
|
||||
import org.briarproject.briar.android.privategroup.conversation.GroupActivity;
|
||||
import org.briarproject.briar.android.removabledrive.RemovableDriveActivity;
|
||||
import org.briarproject.briar.android.util.ActivityLaunchers.GetImageAdvanced;
|
||||
import org.briarproject.briar.android.util.ActivityLaunchers.GetMultipleImagesAdvanced;
|
||||
import org.briarproject.briar.android.util.ActivityLaunchers.OpenMultipleImageDocumentsAdvanced;
|
||||
import org.briarproject.briar.android.util.BriarSnackbarBuilder;
|
||||
@@ -122,13 +121,11 @@ import androidx.vectordrawable.graphics.drawable.VectorDrawableCompat;
|
||||
import de.hdodenhof.circleimageview.CircleImageView;
|
||||
import uk.co.samuelwall.materialtaptargetprompt.MaterialTapTargetPrompt;
|
||||
|
||||
import static android.os.Build.VERSION.SDK_INT;
|
||||
import static android.view.Gravity.RIGHT;
|
||||
import static android.widget.Toast.LENGTH_SHORT;
|
||||
import static androidx.core.app.ActivityOptionsCompat.makeSceneTransitionAnimation;
|
||||
import static androidx.lifecycle.Lifecycle.State.STARTED;
|
||||
import static androidx.recyclerview.widget.SortedList.INVALID_POSITION;
|
||||
import static java.util.Collections.singletonList;
|
||||
import static java.util.Collections.sort;
|
||||
import static java.util.Objects.requireNonNull;
|
||||
import static java.util.logging.Level.INFO;
|
||||
@@ -200,18 +197,13 @@ public class ConversationActivity extends BriarActivity
|
||||
requireNonNull(name);
|
||||
loadMessages();
|
||||
};
|
||||
@Nullable
|
||||
private final ActivityResultLauncher<String[]> docLauncher = SDK_INT >= 19 ?
|
||||
|
||||
private final ActivityResultLauncher<String[]> docLauncher =
|
||||
registerForActivityResult(new OpenMultipleImageDocumentsAdvanced(),
|
||||
this::onImagesChosen) :
|
||||
null;
|
||||
this::onImagesChosen);
|
||||
private final ActivityResultLauncher<String> contentLauncher =
|
||||
SDK_INT >= 18 ?
|
||||
registerForActivityResult(new GetMultipleImagesAdvanced(),
|
||||
this::onImagesChosen) :
|
||||
registerForActivityResult(new GetImageAdvanced(), uri -> {
|
||||
if (uri != null) onImagesChosen(singletonList(uri));
|
||||
});
|
||||
registerForActivityResult(new GetMultipleImagesAdvanced(),
|
||||
this::onImagesChosen);
|
||||
|
||||
private AttachmentRetriever attachmentRetriever;
|
||||
private ConversationViewModel viewModel;
|
||||
@@ -242,13 +234,11 @@ public class ConversationActivity extends BriarActivity
|
||||
|
||||
@Override
|
||||
public void onCreate(@Nullable Bundle state) {
|
||||
if (SDK_INT >= 21) {
|
||||
// Spurious lint warning - using END causes a crash
|
||||
@SuppressLint("RtlHardcoded")
|
||||
Transition slide = new Slide(RIGHT);
|
||||
slide.setDuration(TRANSITION_DURATION_MS);
|
||||
setSceneTransitionAnimation(slide, null, slide);
|
||||
}
|
||||
// Spurious lint warning - using END causes a crash
|
||||
@SuppressLint("RtlHardcoded")
|
||||
Transition slide = new Slide(RIGHT);
|
||||
slide.setDuration(TRANSITION_DURATION_MS);
|
||||
setSceneTransitionAnimation(slide, null, slide);
|
||||
super.onCreate(state);
|
||||
|
||||
Intent i = getIntent();
|
||||
@@ -389,10 +379,6 @@ public class ConversationActivity extends BriarActivity
|
||||
this::showIntroductionOnboarding);
|
||||
}
|
||||
});
|
||||
// Transfer Data feature only supported on API 19+
|
||||
if (SDK_INT >= 19) {
|
||||
menu.findItem(R.id.action_transfer_data).setVisible(true);
|
||||
}
|
||||
// enable alias and bluetooth action once available
|
||||
observeOnce(viewModel.getContactItem(), this, contact -> {
|
||||
menu.findItem(R.id.action_set_alias).setEnabled(true);
|
||||
@@ -434,11 +420,9 @@ public class ConversationActivity extends BriarActivity
|
||||
startActivity(intent);
|
||||
return true;
|
||||
} else if (itemId == R.id.action_transfer_data) {
|
||||
if (SDK_INT >= 19) {
|
||||
Intent intent = new Intent(this, RemovableDriveActivity.class);
|
||||
intent.putExtra(CONTACT_ID, contactId.getInt());
|
||||
startActivity(intent);
|
||||
}
|
||||
Intent intent = new Intent(this, RemovableDriveActivity.class);
|
||||
intent.putExtra(CONTACT_ID, contactId.getInt());
|
||||
startActivity(intent);
|
||||
return true;
|
||||
} else if (itemId == R.id.action_delete_all_messages) {
|
||||
askToDeleteAllMessages();
|
||||
@@ -955,14 +939,10 @@ public class ConversationActivity extends BriarActivity
|
||||
|
||||
private void showImageOnboarding(Boolean show) {
|
||||
if (!show) return;
|
||||
if (SDK_INT >= 21) {
|
||||
// show onboarding only after the enter transition has ended
|
||||
// otherwise the tap target animation won't play
|
||||
textInputView.postDelayed(this::showImageOnboarding,
|
||||
TRANSITION_DURATION_MS + ONBOARDING_DELAY_MS);
|
||||
} else {
|
||||
showImageOnboarding();
|
||||
}
|
||||
// show onboarding only after the enter transition has ended
|
||||
// otherwise the tap target animation won't play
|
||||
textInputView.postDelayed(this::showImageOnboarding,
|
||||
TRANSITION_DURATION_MS + ONBOARDING_DELAY_MS);
|
||||
}
|
||||
|
||||
private void showImageOnboarding() {
|
||||
@@ -973,14 +953,10 @@ public class ConversationActivity extends BriarActivity
|
||||
|
||||
private void showIntroductionOnboarding(@Nullable Boolean show) {
|
||||
if (show == null || !show) return;
|
||||
if (SDK_INT >= 21) {
|
||||
// show onboarding only after the enter transition has ended
|
||||
// otherwise the tap target animation won't play
|
||||
textInputView.postDelayed(this::showIntroductionOnboarding,
|
||||
TRANSITION_DURATION_MS + ONBOARDING_DELAY_MS);
|
||||
} else {
|
||||
showIntroductionOnboarding();
|
||||
}
|
||||
// show onboarding only after the enter transition has ended
|
||||
// otherwise the tap target animation won't play
|
||||
textInputView.postDelayed(this::showIntroductionOnboarding,
|
||||
TRANSITION_DURATION_MS + ONBOARDING_DELAY_MS);
|
||||
}
|
||||
|
||||
private void showIntroductionOnboarding() {
|
||||
|
||||
@@ -26,6 +26,7 @@ import org.briarproject.briar.android.util.BriarSnackbarBuilder;
|
||||
import org.briarproject.briar.android.view.PullDownLayout;
|
||||
import org.briarproject.nullsafety.MethodsNotNullByDefault;
|
||||
import org.briarproject.nullsafety.ParametersNotNullByDefault;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -41,13 +42,11 @@ import androidx.viewpager2.adapter.FragmentStateAdapter;
|
||||
import androidx.viewpager2.widget.ViewPager2;
|
||||
|
||||
import static android.graphics.Color.TRANSPARENT;
|
||||
import static android.os.Build.VERSION.SDK_INT;
|
||||
import static android.view.View.GONE;
|
||||
import static android.view.View.SYSTEM_UI_FLAG_FULLSCREEN;
|
||||
import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
|
||||
import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
|
||||
import static android.view.View.VISIBLE;
|
||||
import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS;
|
||||
import static com.google.android.material.snackbar.Snackbar.LENGTH_LONG;
|
||||
import static java.util.Objects.requireNonNull;
|
||||
import static org.briarproject.briar.android.util.UiUtils.formatDateAbsolute;
|
||||
@@ -77,11 +76,9 @@ public class ImageActivity extends BriarActivity
|
||||
private List<AttachmentItem> attachments;
|
||||
private MessageId conversationMessageId;
|
||||
|
||||
@Nullable
|
||||
private final ActivityResultLauncher<String> launcher = SDK_INT >= 19 ?
|
||||
private final ActivityResultLauncher<String> launcher =
|
||||
registerForActivityResult(new CreateDocumentAdvanced(),
|
||||
this::onImageUriSelected) :
|
||||
null;
|
||||
this::onImageUriSelected);
|
||||
|
||||
@Override
|
||||
public void injectActivity(ActivityComponent component) {
|
||||
@@ -97,10 +94,8 @@ public class ImageActivity extends BriarActivity
|
||||
// Transitions
|
||||
if (state == null) supportPostponeEnterTransition();
|
||||
Window window = getWindow();
|
||||
if (SDK_INT >= 21) {
|
||||
Transition transition = new Fade();
|
||||
setSceneTransitionAnimation(transition, null, transition);
|
||||
}
|
||||
Transition transition = new Fade();
|
||||
setSceneTransitionAnimation(transition, null, transition);
|
||||
|
||||
// Intent Extras
|
||||
Intent i = getIntent();
|
||||
@@ -124,12 +119,7 @@ public class ImageActivity extends BriarActivity
|
||||
layout.getViewTreeObserver().addOnGlobalLayoutListener(this);
|
||||
|
||||
// Status Bar
|
||||
if (SDK_INT >= 21) {
|
||||
window.setStatusBarColor(TRANSPARENT);
|
||||
} else if (SDK_INT >= 19) {
|
||||
// we can't make the status bar transparent, but translucent
|
||||
window.addFlags(FLAG_TRANSLUCENT_STATUS);
|
||||
}
|
||||
window.setStatusBarColor(TRANSPARENT);
|
||||
|
||||
// Toolbar
|
||||
appBarLayout = findViewById(R.id.appBarLayout);
|
||||
@@ -257,16 +247,12 @@ public class ImageActivity extends BriarActivity
|
||||
|
||||
private void showSaveImageDialog() {
|
||||
OnClickListener okListener = (dialog, which) -> {
|
||||
if (SDK_INT >= 19) {
|
||||
String name = viewModel.getFileName() + "." +
|
||||
getVisibleAttachment().getExtension();
|
||||
try {
|
||||
requireNonNull(launcher).launch(name);
|
||||
} catch (ActivityNotFoundException e) {
|
||||
viewModel.onSaveImageError();
|
||||
}
|
||||
} else {
|
||||
viewModel.saveImage(getVisibleAttachment());
|
||||
String name = viewModel.getFileName() + "." +
|
||||
getVisibleAttachment().getExtension();
|
||||
try {
|
||||
launcher.launch(name);
|
||||
} catch (ActivityNotFoundException e) {
|
||||
viewModel.onSaveImageError();
|
||||
}
|
||||
};
|
||||
Builder builder = new Builder(this, R.style.BriarDialogTheme);
|
||||
@@ -295,7 +281,7 @@ public class ImageActivity extends BriarActivity
|
||||
.show();
|
||||
}
|
||||
|
||||
AttachmentItem getVisibleAttachment() {
|
||||
private AttachmentItem getVisibleAttachment() {
|
||||
return attachments.get(viewPager.getCurrentItem());
|
||||
}
|
||||
|
||||
@@ -307,6 +293,7 @@ public class ImageActivity extends BriarActivity
|
||||
super(ImageActivity.this);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Fragment createFragment(int position) {
|
||||
Fragment f = ImageFragment.newInstance(
|
||||
|
||||
@@ -31,7 +31,6 @@ import androidx.fragment.app.Fragment;
|
||||
import androidx.lifecycle.LifecycleOwner;
|
||||
import androidx.lifecycle.ViewModelProvider;
|
||||
|
||||
import static android.os.Build.VERSION.SDK_INT;
|
||||
import static android.widget.ImageView.ScaleType.FIT_START;
|
||||
import static com.bumptech.glide.load.engine.DiskCacheStrategy.NONE;
|
||||
import static org.briarproject.briar.android.attachment.AttachmentItem.State.AVAILABLE;
|
||||
@@ -150,7 +149,7 @@ public class ImageFragment extends Fragment
|
||||
public boolean onResourceReady(Drawable resource, Object model,
|
||||
Target<Drawable> target, DataSource dataSource,
|
||||
boolean isFirstResource) {
|
||||
if (SDK_INT >= 21 && !(resource instanceof Animatable)) {
|
||||
if (!(resource instanceof Animatable)) {
|
||||
// set transition name only when not animatable,
|
||||
// because the animation won't start otherwise
|
||||
photoView.setTransitionName(
|
||||
|
||||
@@ -18,7 +18,6 @@ import androidx.annotation.DrawableRes;
|
||||
import androidx.recyclerview.widget.RecyclerView.ViewHolder;
|
||||
import androidx.recyclerview.widget.StaggeredGridLayoutManager.LayoutParams;
|
||||
|
||||
import static android.os.Build.VERSION.SDK_INT;
|
||||
import static android.widget.ImageView.ScaleType.CENTER_CROP;
|
||||
import static android.widget.ImageView.ScaleType.FIT_CENTER;
|
||||
import static com.bumptech.glide.load.engine.DiskCacheStrategy.NONE;
|
||||
@@ -58,10 +57,8 @@ class ImageViewHolder extends ViewHolder {
|
||||
loadImage(attachment, r);
|
||||
imageView.setScaleType(CENTER_CROP);
|
||||
}
|
||||
if (SDK_INT >= 21) {
|
||||
imageView.setTransitionName(
|
||||
attachment.getTransitionName(conversationItemId));
|
||||
}
|
||||
imageView.setTransitionName(
|
||||
attachment.getTransitionName(conversationItemId));
|
||||
}
|
||||
|
||||
private void setImageViewDimensions(AttachmentItem a, boolean single,
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
package org.briarproject.briar.android.forum;
|
||||
|
||||
import org.briarproject.bramble.api.identity.Author;
|
||||
import org.briarproject.briar.api.identity.AuthorInfo;
|
||||
import org.briarproject.bramble.api.sync.MessageId;
|
||||
import org.briarproject.briar.android.threaded.ThreadItem;
|
||||
import org.briarproject.briar.api.forum.ForumPostHeader;
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@ package org.briarproject.briar.android.hotspot;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
@@ -17,8 +16,6 @@ import org.briarproject.briar.android.util.ActivityLaunchers.CreateDocumentAdvan
|
||||
import org.briarproject.nullsafety.MethodsNotNullByDefault;
|
||||
import org.briarproject.nullsafety.ParametersNotNullByDefault;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import androidx.activity.result.ActivityResultLauncher;
|
||||
@@ -29,14 +26,11 @@ import androidx.lifecycle.ViewModelProvider;
|
||||
import static android.content.Intent.ACTION_SEND;
|
||||
import static android.content.Intent.EXTRA_STREAM;
|
||||
import static android.content.Intent.FLAG_GRANT_READ_URI_PERMISSION;
|
||||
import static android.content.pm.PackageManager.MATCH_DEFAULT_ONLY;
|
||||
import static android.os.Build.VERSION.SDK_INT;
|
||||
import static android.view.View.INVISIBLE;
|
||||
import static android.view.View.VISIBLE;
|
||||
import static androidx.transition.TransitionManager.beginDelayedTransition;
|
||||
import static org.briarproject.briar.android.AppModule.getAndroidComponent;
|
||||
import static org.briarproject.briar.android.hotspot.HotspotViewModel.getApkFileName;
|
||||
import static org.briarproject.nullsafety.NullSafety.requireNonNull;
|
||||
|
||||
@MethodsNotNullByDefault
|
||||
@ParametersNotNullByDefault
|
||||
@@ -48,11 +42,9 @@ public class FallbackFragment extends BaseFragment {
|
||||
ViewModelProvider.Factory viewModelFactory;
|
||||
|
||||
private HotspotViewModel viewModel;
|
||||
@Nullable
|
||||
private final ActivityResultLauncher<String> launcher = SDK_INT >= 19 ?
|
||||
private final ActivityResultLauncher<String> launcher =
|
||||
registerForActivityResult(new CreateDocumentAdvanced(),
|
||||
this::onDocumentCreated) :
|
||||
null;
|
||||
this::onDocumentCreated);
|
||||
private Button fallbackButton;
|
||||
private ProgressBar progressBar;
|
||||
|
||||
@@ -89,12 +81,7 @@ public class FallbackFragment extends BaseFragment {
|
||||
beginDelayedTransition((ViewGroup) v);
|
||||
fallbackButton.setVisibility(INVISIBLE);
|
||||
progressBar.setVisibility(VISIBLE);
|
||||
|
||||
if (SDK_INT >= 19) {
|
||||
requireNonNull(launcher).launch(getApkFileName());
|
||||
} else {
|
||||
viewModel.exportApk();
|
||||
}
|
||||
launcher.launch(getApkFileName());
|
||||
});
|
||||
viewModel.getSavedApkToUri().observeEvent(this, this::shareUri);
|
||||
}
|
||||
@@ -110,23 +97,11 @@ public class FallbackFragment extends BaseFragment {
|
||||
progressBar.setVisibility(INVISIBLE);
|
||||
}
|
||||
|
||||
void shareUri(Uri uri) {
|
||||
private void shareUri(Uri uri) {
|
||||
Intent i = new Intent(ACTION_SEND);
|
||||
i.putExtra(EXTRA_STREAM, uri);
|
||||
i.setType("*/*"); // gives us all sharing options
|
||||
i.addFlags(FLAG_GRANT_READ_URI_PERMISSION);
|
||||
Context ctx = requireContext();
|
||||
if (SDK_INT <= 19) {
|
||||
// Workaround for Android bug:
|
||||
// ctx.grantUriPermission also needed for Android 4
|
||||
List<ResolveInfo> resInfoList = ctx.getPackageManager()
|
||||
.queryIntentActivities(i, MATCH_DEFAULT_ONLY);
|
||||
for (ResolveInfo resolveInfo : resInfoList) {
|
||||
String packageName = resolveInfo.activityInfo.packageName;
|
||||
ctx.grantUriPermission(packageName, uri,
|
||||
FLAG_GRANT_READ_URI_PERMISSION);
|
||||
}
|
||||
}
|
||||
startActivity(Intent.createChooser(i, null));
|
||||
}
|
||||
|
||||
|
||||
@@ -25,7 +25,6 @@ import org.briarproject.nullsafety.NotNullByDefault;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.concurrent.Executor;
|
||||
@@ -38,9 +37,6 @@ import androidx.annotation.UiThread;
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.lifecycle.MutableLiveData;
|
||||
|
||||
import static android.os.Build.VERSION.SDK_INT;
|
||||
import static android.os.Environment.DIRECTORY_DOWNLOADS;
|
||||
import static android.os.Environment.getExternalStoragePublicDirectory;
|
||||
import static java.util.Objects.requireNonNull;
|
||||
import static java.util.logging.Level.WARNING;
|
||||
import static java.util.logging.Logger.getLogger;
|
||||
@@ -168,7 +164,6 @@ class HotspotViewModel extends DbViewModel
|
||||
}
|
||||
|
||||
void exportApk(Uri uri) {
|
||||
if (SDK_INT < 19) throw new IllegalStateException();
|
||||
try {
|
||||
OutputStream out = getApplication().getContentResolver()
|
||||
.openOutputStream(uri, "wt");
|
||||
@@ -178,20 +173,6 @@ class HotspotViewModel extends DbViewModel
|
||||
}
|
||||
}
|
||||
|
||||
void exportApk() {
|
||||
if (SDK_INT >= 19) throw new IllegalStateException();
|
||||
File path = getExternalStoragePublicDirectory(DIRECTORY_DOWNLOADS);
|
||||
//noinspection ResultOfMethodCallIgnored
|
||||
path.mkdirs();
|
||||
File file = new File(path, getApkFileName());
|
||||
try {
|
||||
OutputStream out = new FileOutputStream(file);
|
||||
writeApk(out, Uri.fromFile(file));
|
||||
} catch (FileNotFoundException e) {
|
||||
handleException(e);
|
||||
}
|
||||
}
|
||||
|
||||
static String getApkFileName() {
|
||||
return "briar" + (DEBUG ? "-debug-" : "-") + VERSION_NAME + ".apk";
|
||||
}
|
||||
|
||||
@@ -5,8 +5,6 @@ import android.os.Bundle;
|
||||
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import static android.os.Build.VERSION.SDK_INT;
|
||||
|
||||
public class ExitActivity extends Activity {
|
||||
|
||||
private static final Logger LOG =
|
||||
@@ -15,8 +13,7 @@ public class ExitActivity extends Activity {
|
||||
@Override
|
||||
public void onCreate(Bundle state) {
|
||||
super.onCreate(state);
|
||||
if (SDK_INT >= 21) finishAndRemoveTask();
|
||||
else finish();
|
||||
finishAndRemoveTask();
|
||||
LOG.info("Exiting");
|
||||
System.exit(0);
|
||||
}
|
||||
|
||||
@@ -75,15 +75,12 @@ import static org.briarproject.bramble.api.plugin.Plugin.State.ENABLING;
|
||||
import static org.briarproject.bramble.api.plugin.Plugin.State.STARTING_STOPPING;
|
||||
import static org.briarproject.briar.android.BriarService.EXTRA_STARTUP_FAILED;
|
||||
import static org.briarproject.briar.android.BriarService.EXTRA_START_RESULT;
|
||||
import static org.briarproject.briar.android.TestingConstants.EXPIRY_DATE;
|
||||
import static org.briarproject.briar.android.TestingConstants.IS_DEBUG_BUILD;
|
||||
import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_PASSWORD;
|
||||
import static org.briarproject.briar.android.navdrawer.IntentRouter.handleExternalIntent;
|
||||
import static org.briarproject.briar.android.util.UiUtils.formatDateFull;
|
||||
import static org.briarproject.briar.android.util.UiUtils.getDaysUntilExpiry;
|
||||
import static org.briarproject.briar.android.util.UiUtils.observeOnce;
|
||||
import static org.briarproject.briar.android.util.UiUtils.resolveColorAttribute;
|
||||
import static org.briarproject.briar.android.util.UiUtils.shouldWarnOldAndroidExpiry;
|
||||
|
||||
@MethodsNotNullByDefault
|
||||
@ParametersNotNullByDefault
|
||||
@@ -140,11 +137,9 @@ public class NavDrawerActivity extends BriarActivity implements
|
||||
setContentView(R.layout.activity_nav_drawer);
|
||||
|
||||
BriarApplication app = (BriarApplication) getApplication();
|
||||
if (!app.isInstrumentationTest()) {
|
||||
if (IS_DEBUG_BUILD || shouldWarnOldAndroidExpiry()) {
|
||||
navDrawerViewModel.showExpiryWarning()
|
||||
.observe(this, this::showExpiryWarning);
|
||||
}
|
||||
if (IS_DEBUG_BUILD && !app.isInstrumentationTest()) {
|
||||
navDrawerViewModel.showExpiryWarning()
|
||||
.observe(this, this::showExpiryWarning);
|
||||
}
|
||||
navDrawerViewModel.shouldAskForDozeWhitelisting().observe(this, ask -> {
|
||||
if (ask) showDozeDialog(R.string.dnkm_doze_intro);
|
||||
@@ -212,7 +207,7 @@ public class NavDrawerActivity extends BriarActivity implements
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
lockManager.checkIfLockable();
|
||||
if (IS_DEBUG_BUILD || shouldWarnOldAndroidExpiry()) {
|
||||
if (IS_DEBUG_BUILD) {
|
||||
navDrawerViewModel.checkExpiryWarning();
|
||||
}
|
||||
}
|
||||
@@ -384,23 +379,14 @@ public class NavDrawerActivity extends BriarActivity implements
|
||||
return;
|
||||
}
|
||||
|
||||
String text;
|
||||
if (IS_DEBUG_BUILD) {
|
||||
text = getResources().getQuantityString(
|
||||
R.plurals.expiry_warning, (int) daysUntilExpiry,
|
||||
(int) daysUntilExpiry);
|
||||
} else {
|
||||
text = getResources().getQuantityString(
|
||||
R.plurals.old_android_expiry_warning, (int) daysUntilExpiry,
|
||||
formatDateFull(this, EXPIRY_DATE),
|
||||
(int) daysUntilExpiry);
|
||||
}
|
||||
|
||||
ViewGroup expiryWarning = findViewById(R.id.expiryWarning);
|
||||
if (show) {
|
||||
// show expiry warning text
|
||||
TextView expiryWarningText =
|
||||
expiryWarning.findViewById(R.id.expiryWarningText);
|
||||
String text = getResources().getQuantityString(
|
||||
R.plurals.expiry_warning, (int) daysUntilExpiry,
|
||||
(int) daysUntilExpiry);
|
||||
expiryWarningText.setText(text);
|
||||
// make close button functional
|
||||
ImageView expiryWarningClose =
|
||||
@@ -432,7 +418,7 @@ public class NavDrawerActivity extends BriarActivity implements
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(int position, View convertView,
|
||||
public View getView(int position, @Nullable View convertView,
|
||||
ViewGroup parent) {
|
||||
View view;
|
||||
if (convertView != null) {
|
||||
|
||||
@@ -19,7 +19,6 @@ import info.guardianproject.panic.Panic;
|
||||
import info.guardianproject.panic.PanicResponder;
|
||||
import info.guardianproject.trustedintents.TrustedIntents;
|
||||
|
||||
import static android.os.Build.VERSION.SDK_INT;
|
||||
import static java.util.logging.Logger.getLogger;
|
||||
import static org.briarproject.briar.android.panic.PanicPreferencesFragment.KEY_LOCK;
|
||||
import static org.briarproject.briar.android.panic.PanicPreferencesFragment.KEY_PURGE;
|
||||
@@ -73,12 +72,7 @@ public class PanicResponderActivity extends BriarActivity {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (SDK_INT >= 21) {
|
||||
finishAndRemoveTask();
|
||||
} else {
|
||||
finish();
|
||||
}
|
||||
finishAndRemoveTask();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -20,7 +20,6 @@ import javax.inject.Inject;
|
||||
|
||||
import androidx.activity.result.ActivityResultLauncher;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.RequiresApi;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentActivity;
|
||||
import androidx.lifecycle.ViewModelProvider;
|
||||
@@ -31,7 +30,6 @@ import static org.briarproject.briar.android.AppModule.getAndroidComponent;
|
||||
import static org.briarproject.briar.android.util.UiUtils.hideViewOnSmallScreen;
|
||||
import static org.briarproject.briar.android.util.UiUtils.launchActivityToOpenFile;
|
||||
|
||||
@RequiresApi(19)
|
||||
@MethodsNotNullByDefault
|
||||
@ParametersNotNullByDefault
|
||||
public class ReceiveFragment extends Fragment {
|
||||
|
||||
@@ -19,7 +19,6 @@ import javax.inject.Inject;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.RequiresApi;
|
||||
import androidx.annotation.StringRes;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
@@ -29,7 +28,6 @@ import static java.util.Objects.requireNonNull;
|
||||
import static org.briarproject.briar.android.conversation.ConversationActivity.CONTACT_ID;
|
||||
import static org.briarproject.briar.android.util.UiUtils.showFragment;
|
||||
|
||||
@RequiresApi(19)
|
||||
@MethodsNotNullByDefault
|
||||
@ParametersNotNullByDefault
|
||||
public class RemovableDriveActivity extends BriarActivity {
|
||||
|
||||
@@ -24,7 +24,6 @@ import javax.inject.Inject;
|
||||
|
||||
import androidx.activity.result.ActivityResultLauncher;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.RequiresApi;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentActivity;
|
||||
import androidx.lifecycle.ViewModelProvider;
|
||||
@@ -38,7 +37,6 @@ import static org.briarproject.bramble.util.LogUtils.logException;
|
||||
import static org.briarproject.briar.android.AppModule.getAndroidComponent;
|
||||
import static org.briarproject.briar.android.util.UiUtils.hideViewOnSmallScreen;
|
||||
|
||||
@RequiresApi(19)
|
||||
@MethodsNotNullByDefault
|
||||
@ParametersNotNullByDefault
|
||||
public class SendFragment extends Fragment {
|
||||
|
||||
@@ -220,10 +220,10 @@ class BriarReportCollector {
|
||||
method.setAccessible(true);
|
||||
mobileEnabled = (Boolean) requireNonNull(method.invoke(cm));
|
||||
} catch (ClassNotFoundException
|
||||
| NoSuchMethodException
|
||||
| IllegalArgumentException
|
||||
| InvocationTargetException
|
||||
| IllegalAccessException e) {
|
||||
| NoSuchMethodException
|
||||
| IllegalArgumentException
|
||||
| InvocationTargetException
|
||||
| IllegalAccessException e) {
|
||||
connectivityInfo
|
||||
.add("MobileDataReflectionException", e.toString());
|
||||
}
|
||||
@@ -300,15 +300,12 @@ class BriarReportCollector {
|
||||
scanMode == SCAN_MODE_CONNECTABLE_DISCOVERABLE;
|
||||
connectivityInfo.add("BluetoothDiscoverable", btDiscoverable);
|
||||
|
||||
if (SDK_INT >= 21) {
|
||||
// Is Bluetooth LE scanning and advertising supported?
|
||||
boolean btLeScan = bt.getBluetoothLeScanner() != null;
|
||||
connectivityInfo.add("BluetoothLeScanningSupported", btLeScan);
|
||||
boolean btLeAdvertise =
|
||||
bt.getBluetoothLeAdvertiser() != null;
|
||||
connectivityInfo.add("BluetoothLeAdvertisingSupported",
|
||||
btLeAdvertise);
|
||||
}
|
||||
// Is Bluetooth LE scanning and advertising supported?
|
||||
boolean btLeScan = bt.getBluetoothLeScanner() != null;
|
||||
connectivityInfo.add("BluetoothLeScanningSupported", btLeScan);
|
||||
boolean btLeAdvertise = bt.getBluetoothLeAdvertiser() != null;
|
||||
connectivityInfo.add("BluetoothLeAdvertisingSupported",
|
||||
btLeAdvertise);
|
||||
|
||||
Pair<String, String> p = getBluetoothAddressAndMethod(ctx, bt);
|
||||
String address = p.getFirst();
|
||||
|
||||
@@ -27,7 +27,6 @@ import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
|
||||
import static android.os.Build.VERSION.SDK_INT;
|
||||
import static androidx.core.view.ViewCompat.LAYOUT_DIRECTION_LTR;
|
||||
import static java.util.Objects.requireNonNull;
|
||||
import static java.util.logging.Level.INFO;
|
||||
import static java.util.logging.Logger.getLogger;
|
||||
import static org.briarproject.briar.android.BriarApplication.ENTRY_ACTIVITY;
|
||||
import static org.briarproject.briar.android.navdrawer.NavDrawerActivity.SIGN_OUT_URI;
|
||||
@@ -75,12 +74,6 @@ public class DisplayFragment extends PreferenceFragmentCompat {
|
||||
Locale locale = Localizer.getLocaleFromTag(tag);
|
||||
if (locale == null)
|
||||
throw new IllegalStateException();
|
||||
// Exclude RTL locales on API < 17, they won't be laid out correctly
|
||||
if (SDK_INT < 17 && !isLeftToRight(locale)) {
|
||||
if (LOG.isLoggable(INFO))
|
||||
LOG.info("Skipping RTL locale " + tag);
|
||||
continue;
|
||||
}
|
||||
String nativeName = locale.getDisplayName(locale);
|
||||
// Fallback to English if the name is unknown in both native and
|
||||
// current locale.
|
||||
|
||||
@@ -18,7 +18,6 @@ import androidx.preference.ListPreference;
|
||||
import androidx.preference.PreferenceFragmentCompat;
|
||||
import androidx.preference.SwitchPreferenceCompat;
|
||||
|
||||
import static android.os.Build.VERSION.SDK_INT;
|
||||
import static java.util.Objects.requireNonNull;
|
||||
import static org.briarproject.briar.android.AppModule.getAndroidComponent;
|
||||
import static org.briarproject.briar.android.settings.SettingsActivity.enableAndPersist;
|
||||
@@ -71,17 +70,12 @@ public class SecurityFragment extends PreferenceFragmentCompat {
|
||||
@Override
|
||||
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
if (SDK_INT < 21) {
|
||||
screenLock.setVisible(false);
|
||||
screenLockTimeout.setVisible(false);
|
||||
} else {
|
||||
// timeout depends on screenLock and gets disabled automatically
|
||||
LifecycleOwner lifecycleOwner = getViewLifecycleOwner();
|
||||
viewModel.getScreenLockTimeout().observe(lifecycleOwner, value -> {
|
||||
screenLockTimeout.setValue(value);
|
||||
enableAndPersist(screenLockTimeout);
|
||||
});
|
||||
}
|
||||
// timeout depends on screenLock and gets disabled automatically
|
||||
LifecycleOwner lifecycleOwner = getViewLifecycleOwner();
|
||||
viewModel.getScreenLockTimeout().observe(lifecycleOwner, value -> {
|
||||
screenLockTimeout.setValue(value);
|
||||
enableAndPersist(screenLockTimeout);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -92,7 +86,6 @@ public class SecurityFragment extends PreferenceFragmentCompat {
|
||||
}
|
||||
|
||||
private void checkScreenLock() {
|
||||
if (SDK_INT < 21) return;
|
||||
LifecycleOwner lifecycleOwner = getViewLifecycleOwner();
|
||||
viewModel.getScreenLockEnabled().removeObservers(lifecycleOwner);
|
||||
if (hasScreenLock(requireActivity())) {
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
package org.briarproject.briar.android.settings;
|
||||
|
||||
import android.content.ActivityNotFoundException;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.briarproject.briar.R;
|
||||
import org.briarproject.briar.android.mailbox.MailboxActivity;
|
||||
@@ -24,7 +26,9 @@ import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceFragmentCompat;
|
||||
import androidx.preference.PreferenceGroup;
|
||||
|
||||
import static android.os.Build.VERSION.SDK_INT;
|
||||
import static android.content.Intent.ACTION_SEND;
|
||||
import static android.content.Intent.EXTRA_TEXT;
|
||||
import static android.widget.Toast.LENGTH_LONG;
|
||||
import static java.util.Objects.requireNonNull;
|
||||
import static org.briarproject.briar.android.AppModule.getAndroidComponent;
|
||||
import static org.briarproject.briar.android.TestingConstants.IS_DEBUG_BUILD;
|
||||
@@ -38,22 +42,23 @@ public class SettingsFragment extends PreferenceFragmentCompat {
|
||||
public static final String SETTINGS_NAMESPACE = "android-ui";
|
||||
|
||||
private static final String PREF_KEY_AVATAR = "pref_key_avatar";
|
||||
private static final String PREF_KEY_SHARE_LINK = "pref_key_share_app_link";
|
||||
private static final String PREF_KEY_FEEDBACK = "pref_key_send_feedback";
|
||||
private static final String PREF_KEY_DEV = "pref_key_dev";
|
||||
private static final String PREF_KEY_EXPLODE = "pref_key_explode";
|
||||
private static final String PREF_KEY_MAILBOX = "pref_key_mailbox";
|
||||
|
||||
private static final String DOWNLOAD_URL = "https://briarproject.org/download/";
|
||||
|
||||
@Inject
|
||||
ViewModelProvider.Factory viewModelFactory;
|
||||
|
||||
private SettingsViewModel viewModel;
|
||||
private AvatarPreference prefAvatar;
|
||||
|
||||
@Nullable
|
||||
private final ActivityResultLauncher<String[]> docLauncher = SDK_INT >= 19 ?
|
||||
private final ActivityResultLauncher<String[]> docLauncher =
|
||||
registerForActivityResult(new OpenImageDocumentAdvanced(),
|
||||
this::onImageSelected) :
|
||||
null;
|
||||
this::onImageSelected);
|
||||
private final ActivityResultLauncher<String> contentLauncher =
|
||||
registerForActivityResult(new GetImageAdvanced(),
|
||||
this::onImageSelected);
|
||||
@@ -89,6 +94,21 @@ public class SettingsFragment extends PreferenceFragmentCompat {
|
||||
return true;
|
||||
});
|
||||
|
||||
Preference prefShareLink =
|
||||
requireNonNull(findPreference(PREF_KEY_SHARE_LINK));
|
||||
prefShareLink.setOnPreferenceClickListener(preference -> {
|
||||
String text = getString(R.string.share_app_link_text, DOWNLOAD_URL);
|
||||
Intent sendIntent = new Intent(ACTION_SEND);
|
||||
sendIntent.putExtra(EXTRA_TEXT, text);
|
||||
sendIntent.setType("text/plain");
|
||||
try {
|
||||
startActivity(Intent.createChooser(sendIntent, null));
|
||||
} catch (ActivityNotFoundException e) {
|
||||
Toast.makeText(requireContext(),
|
||||
R.string.error_start_activity, LENGTH_LONG).show();
|
||||
}
|
||||
return true;
|
||||
});
|
||||
Preference prefFeedback =
|
||||
requireNonNull(findPreference(PREF_KEY_FEEDBACK));
|
||||
prefFeedback.setOnPreferenceClickListener(preference -> {
|
||||
|
||||
@@ -23,7 +23,6 @@ import org.briarproject.bramble.api.settings.Settings;
|
||||
import org.briarproject.bramble.api.settings.SettingsManager;
|
||||
import org.briarproject.bramble.api.settings.event.SettingsUpdatedEvent;
|
||||
import org.briarproject.bramble.api.system.AndroidExecutor;
|
||||
import org.briarproject.bramble.api.system.LocationUtils;
|
||||
import org.briarproject.briar.R;
|
||||
import org.briarproject.briar.android.attachment.UnsupportedMimeTypeException;
|
||||
import org.briarproject.briar.android.attachment.media.ImageCompressor;
|
||||
@@ -34,6 +33,7 @@ import org.briarproject.briar.api.identity.AuthorManager;
|
||||
import org.briarproject.nullsafety.MethodsNotNullByDefault;
|
||||
import org.briarproject.nullsafety.ParametersNotNullByDefault;
|
||||
import org.briarproject.onionwrapper.CircumventionProvider;
|
||||
import org.briarproject.onionwrapper.LocationUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
@@ -2,10 +2,10 @@ package org.briarproject.briar.android.settings;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import org.briarproject.bramble.api.system.LocationUtils;
|
||||
import org.briarproject.briar.R;
|
||||
import org.briarproject.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.onionwrapper.CircumventionProvider;
|
||||
import org.briarproject.onionwrapper.LocationUtils;
|
||||
|
||||
import androidx.preference.ListPreference;
|
||||
import androidx.preference.Preference.SummaryProvider;
|
||||
|
||||
@@ -1,57 +0,0 @@
|
||||
package org.briarproject.briar.android.splash;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
|
||||
import org.briarproject.android.dontkillmelib.wakelock.AndroidWakeLockManager;
|
||||
import org.briarproject.briar.R;
|
||||
import org.briarproject.briar.android.activity.ActivityComponent;
|
||||
import org.briarproject.briar.android.activity.BaseActivity;
|
||||
import org.briarproject.briar.android.controller.BriarController;
|
||||
import org.briarproject.briar.android.logout.ExitActivity;
|
||||
import org.briarproject.nullsafety.MethodsNotNullByDefault;
|
||||
import org.briarproject.nullsafety.ParametersNotNullByDefault;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK;
|
||||
import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
|
||||
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
|
||||
import static android.content.Intent.FLAG_ACTIVITY_NO_ANIMATION;
|
||||
|
||||
@MethodsNotNullByDefault
|
||||
@ParametersNotNullByDefault
|
||||
public class ExpiredOldAndroidActivity extends BaseActivity {
|
||||
|
||||
@Inject
|
||||
BriarController briarController;
|
||||
@Inject
|
||||
AndroidWakeLockManager wakeLockManager;
|
||||
|
||||
@Override
|
||||
public void onCreate(@Nullable Bundle state) {
|
||||
super.onCreate(state);
|
||||
|
||||
setContentView(R.layout.activity_expired_old_android);
|
||||
findViewById(R.id.delete_account_button).setOnClickListener(v -> {
|
||||
// Hold a wake lock to ensure we exit before the device goes to sleep
|
||||
wakeLockManager.runWakefully(() -> {
|
||||
// we're not signed in, just go ahead and delete
|
||||
briarController.deleteAccount();
|
||||
// remove from recent apps
|
||||
Intent i = new Intent(this, ExitActivity.class);
|
||||
i.addFlags(FLAG_ACTIVITY_NEW_TASK
|
||||
| FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
|
||||
| FLAG_ACTIVITY_NO_ANIMATION
|
||||
| FLAG_ACTIVITY_CLEAR_TASK);
|
||||
startActivity(i);
|
||||
}, "DeleteAccount");
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void injectActivity(ActivityComponent component) {
|
||||
component.inject(this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,6 @@ import javax.inject.Inject;
|
||||
|
||||
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP;
|
||||
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
|
||||
import static android.os.Build.VERSION.SDK_INT;
|
||||
import static androidx.preference.PreferenceManager.setDefaultValues;
|
||||
import static java.lang.System.currentTimeMillis;
|
||||
import static java.util.logging.Logger.getLogger;
|
||||
@@ -50,12 +49,8 @@ public class SplashScreenActivity extends BaseActivity {
|
||||
public void onCreate(@Nullable Bundle state) {
|
||||
super.onCreate(state);
|
||||
|
||||
if (SDK_INT >= 21) {
|
||||
getWindow().setExitTransition(new Fade());
|
||||
}
|
||||
|
||||
getWindow().setExitTransition(new Fade());
|
||||
setPreferencesDefaults();
|
||||
|
||||
setContentView(R.layout.splash);
|
||||
|
||||
if (accountManager.hasDatabaseKey()) {
|
||||
@@ -65,14 +60,9 @@ public class SplashScreenActivity extends BaseActivity {
|
||||
int duration =
|
||||
getResources().getInteger(R.integer.splashScreenDuration);
|
||||
new Handler().postDelayed(() -> {
|
||||
if (currentTimeMillis() >= EXPIRY_DATE) {
|
||||
if (IS_DEBUG_BUILD) {
|
||||
LOG.info("Expired: debug build");
|
||||
startNextActivity(ExpiredActivity.class);
|
||||
} else {
|
||||
LOG.info("Expired: running on old Android");
|
||||
startNextActivity(ExpiredOldAndroidActivity.class);
|
||||
}
|
||||
if (IS_DEBUG_BUILD && currentTimeMillis() >= EXPIRY_DATE) {
|
||||
LOG.info("Expired");
|
||||
startNextActivity(ExpiredActivity.class);
|
||||
} else {
|
||||
startNextActivity(ENTRY_ACTIVITY);
|
||||
}
|
||||
|
||||
@@ -24,9 +24,10 @@ public class TestDataActivity extends BriarActivity {
|
||||
@Inject
|
||||
TestDataCreator testDataCreator;
|
||||
|
||||
private TextView contactsTextView, forumsTextView;
|
||||
private TextView contactsTextView, forumsTextView, privateGroupsTextView;
|
||||
private SeekBar contactsSeekBar, messagesSeekBar, avatarsSeekBar,
|
||||
blogPostsSeekBar, forumsSeekBar, forumPostsSeekBar;
|
||||
blogPostsSeekBar, forumsSeekBar, forumPostsSeekBar,
|
||||
privateGroupsSeekBar, privateGroupPostsSeekBar;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle bundle) {
|
||||
@@ -46,28 +47,26 @@ public class TestDataActivity extends BriarActivity {
|
||||
forumsTextView = findViewById(R.id.TextViewForumsSb);
|
||||
TextView forumPostsTextView =
|
||||
findViewById(R.id.TextViewForumMessagesSb);
|
||||
privateGroupsTextView = findViewById(R.id.TextViewPrivateGroupsSb);
|
||||
TextView privateGroupPostsTextView =
|
||||
findViewById(R.id.TextViewPrivateGroupMessagesSb);
|
||||
contactsSeekBar = findViewById(R.id.seekBarContacts);
|
||||
messagesSeekBar = findViewById(R.id.seekBarMessages);
|
||||
avatarsSeekBar = findViewById(R.id.seekBarAvatars);
|
||||
blogPostsSeekBar = findViewById(R.id.seekBarBlogPosts);
|
||||
forumsSeekBar = findViewById(R.id.seekBarForums);
|
||||
forumPostsSeekBar = findViewById(R.id.seekBarForumMessages);
|
||||
privateGroupsSeekBar = findViewById(R.id.seekBarPrivateGroups);
|
||||
privateGroupPostsSeekBar =
|
||||
findViewById(R.id.seekBarPrivateGroupMessages);
|
||||
|
||||
contactsSeekBar
|
||||
.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
|
||||
contactsSeekBar.setOnSeekBarChangeListener(
|
||||
new AbstractOnSeekBarChangeListener() {
|
||||
@Override
|
||||
public void onProgressChanged(SeekBar seekBar,
|
||||
int progress, boolean fromUser) {
|
||||
public void onProgressChanged(SeekBar seekBar, int progress,
|
||||
boolean fromUser) {
|
||||
contactsTextView.setText(String.valueOf(progress + 1));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStartTrackingTouch(SeekBar seekBar) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStopTrackingTouch(SeekBar seekBar) {
|
||||
}
|
||||
});
|
||||
|
||||
messagesSeekBar.setOnSeekBarChangeListener(
|
||||
@@ -76,35 +75,39 @@ public class TestDataActivity extends BriarActivity {
|
||||
new OnSeekBarChangeUpdateProgress(avatarsTextView));
|
||||
blogPostsSeekBar.setOnSeekBarChangeListener(
|
||||
new OnSeekBarChangeUpdateProgress(blogPostsTextView));
|
||||
forumsSeekBar
|
||||
.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
|
||||
forumsSeekBar.setOnSeekBarChangeListener(
|
||||
new AbstractOnSeekBarChangeListener() {
|
||||
@Override
|
||||
public void onProgressChanged(SeekBar seekBar,
|
||||
int progress, boolean fromUser) {
|
||||
public void onProgressChanged(SeekBar seekBar, int progress,
|
||||
boolean fromUser) {
|
||||
forumsTextView.setText(String.valueOf(progress));
|
||||
forumPostsSeekBar.setEnabled(progress > 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStartTrackingTouch(SeekBar seekBar) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStopTrackingTouch(SeekBar seekBar) {
|
||||
}
|
||||
});
|
||||
forumPostsSeekBar.setOnSeekBarChangeListener(
|
||||
new OnSeekBarChangeUpdateProgress(forumPostsTextView));
|
||||
|
||||
findViewById(R.id.buttonZeroValues).setOnClickListener(
|
||||
v -> {
|
||||
contactsSeekBar.setProgress(0);
|
||||
messagesSeekBar.setProgress(0);
|
||||
avatarsSeekBar.setProgress(0);
|
||||
blogPostsSeekBar.setProgress(0);
|
||||
forumsSeekBar.setProgress(0);
|
||||
forumPostsSeekBar.setProgress(0);
|
||||
privateGroupsSeekBar.setOnSeekBarChangeListener(
|
||||
new AbstractOnSeekBarChangeListener() {
|
||||
@Override
|
||||
public void onProgressChanged(SeekBar seekBar, int progress,
|
||||
boolean fromUser) {
|
||||
privateGroupsTextView.setText(String.valueOf(progress));
|
||||
privateGroupPostsSeekBar.setEnabled(progress > 0);
|
||||
}
|
||||
});
|
||||
privateGroupPostsSeekBar.setOnSeekBarChangeListener(
|
||||
new OnSeekBarChangeUpdateProgress(privateGroupPostsTextView));
|
||||
|
||||
findViewById(R.id.buttonZeroValues).setOnClickListener(v -> {
|
||||
contactsSeekBar.setProgress(0);
|
||||
messagesSeekBar.setProgress(0);
|
||||
avatarsSeekBar.setProgress(0);
|
||||
blogPostsSeekBar.setProgress(0);
|
||||
forumsSeekBar.setProgress(0);
|
||||
forumPostsSeekBar.setProgress(0);
|
||||
privateGroupsSeekBar.setProgress(0);
|
||||
privateGroupPostsSeekBar.setProgress(0);
|
||||
});
|
||||
|
||||
findViewById(R.id.buttonCreateTestData).setOnClickListener(
|
||||
v -> createTestData());
|
||||
@@ -114,7 +117,9 @@ public class TestDataActivity extends BriarActivity {
|
||||
testDataCreator.createTestData(contactsSeekBar.getProgress() + 1,
|
||||
messagesSeekBar.getProgress(), avatarsSeekBar.getProgress(),
|
||||
blogPostsSeekBar.getProgress(), forumsSeekBar.getProgress(),
|
||||
forumPostsSeekBar.getProgress());
|
||||
forumPostsSeekBar.getProgress(),
|
||||
privateGroupsSeekBar.getProgress(),
|
||||
privateGroupPostsSeekBar.getProgress());
|
||||
Intent intent = new Intent(this, ENTRY_ACTIVITY);
|
||||
intent.addFlags(FLAG_ACTIVITY_CLEAR_TOP);
|
||||
startActivity(intent);
|
||||
@@ -136,7 +141,7 @@ public class TestDataActivity extends BriarActivity {
|
||||
}
|
||||
|
||||
private static class OnSeekBarChangeUpdateProgress
|
||||
implements OnSeekBarChangeListener {
|
||||
extends AbstractOnSeekBarChangeListener {
|
||||
private final TextView textView;
|
||||
|
||||
private OnSeekBarChangeUpdateProgress(TextView textView) {
|
||||
@@ -148,7 +153,10 @@ public class TestDataActivity extends BriarActivity {
|
||||
boolean fromUser) {
|
||||
textView.setText(String.valueOf(progress));
|
||||
}
|
||||
}
|
||||
|
||||
private abstract static class AbstractOnSeekBarChangeListener
|
||||
implements OnSeekBarChangeListener {
|
||||
@Override
|
||||
public void onStartTrackingTouch(SeekBar seekBar) {
|
||||
}
|
||||
@@ -157,5 +165,4 @@ public class TestDataActivity extends BriarActivity {
|
||||
public void onStopTrackingTouch(SeekBar seekBar) {
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -13,7 +13,6 @@ import androidx.activity.result.contract.ActivityResultContracts.OpenDocument;
|
||||
import androidx.activity.result.contract.ActivityResultContracts.OpenMultipleDocuments;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.RequiresApi;
|
||||
|
||||
import static android.app.Activity.RESULT_CANCELED;
|
||||
import static android.bluetooth.BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE;
|
||||
@@ -26,7 +25,6 @@ import static org.briarproject.bramble.util.AndroidUtils.getSupportedImageConten
|
||||
@NotNullByDefault
|
||||
public class ActivityLaunchers {
|
||||
|
||||
@RequiresApi(19)
|
||||
public static class CreateDocumentAdvanced extends CreateDocument {
|
||||
@NonNull
|
||||
@Override
|
||||
@@ -48,7 +46,6 @@ public class ActivityLaunchers {
|
||||
}
|
||||
}
|
||||
|
||||
@RequiresApi(19)
|
||||
public static class OpenDocumentAdvanced extends OpenDocument {
|
||||
@NonNull
|
||||
@Override
|
||||
@@ -69,13 +66,11 @@ public class ActivityLaunchers {
|
||||
putShowAdvancedExtra(i);
|
||||
i.setType("image/*");
|
||||
i.addFlags(FLAG_GRANT_READ_URI_PERMISSION);
|
||||
if (SDK_INT >= 19)
|
||||
i.putExtra(EXTRA_MIME_TYPES, getSupportedImageContentTypes());
|
||||
i.putExtra(EXTRA_MIME_TYPES, getSupportedImageContentTypes());
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
@RequiresApi(18)
|
||||
public static class GetMultipleImagesAdvanced extends GetMultipleContents {
|
||||
@NonNull
|
||||
@Override
|
||||
@@ -84,13 +79,11 @@ public class ActivityLaunchers {
|
||||
putShowAdvancedExtra(i);
|
||||
i.setType("image/*");
|
||||
i.addFlags(FLAG_GRANT_READ_URI_PERMISSION);
|
||||
if (SDK_INT >= 19)
|
||||
i.putExtra(EXTRA_MIME_TYPES, getSupportedImageContentTypes());
|
||||
i.putExtra(EXTRA_MIME_TYPES, getSupportedImageContentTypes());
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
@RequiresApi(19)
|
||||
public static class OpenImageDocumentAdvanced extends OpenDocument {
|
||||
@NonNull
|
||||
@Override
|
||||
@@ -99,13 +92,11 @@ public class ActivityLaunchers {
|
||||
putShowAdvancedExtra(i);
|
||||
i.setType("image/*");
|
||||
i.addFlags(FLAG_GRANT_READ_URI_PERMISSION);
|
||||
if (SDK_INT >= 19)
|
||||
i.putExtra(EXTRA_MIME_TYPES, getSupportedImageContentTypes());
|
||||
i.putExtra(EXTRA_MIME_TYPES, getSupportedImageContentTypes());
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
@RequiresApi(19)
|
||||
public static class OpenMultipleImageDocumentsAdvanced
|
||||
extends OpenMultipleDocuments {
|
||||
@NonNull
|
||||
@@ -115,8 +106,7 @@ public class ActivityLaunchers {
|
||||
putShowAdvancedExtra(i);
|
||||
i.setType("image/*");
|
||||
i.addFlags(FLAG_GRANT_READ_URI_PERMISSION);
|
||||
if (SDK_INT >= 19)
|
||||
i.putExtra(EXTRA_MIME_TYPES, getSupportedImageContentTypes());
|
||||
i.putExtra(EXTRA_MIME_TYPES, getSupportedImageContentTypes());
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ import androidx.annotation.ColorRes;
|
||||
import androidx.core.app.NotificationCompat;
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
import static android.os.Build.VERSION.SDK_INT;
|
||||
import static androidx.core.app.NotificationCompat.VISIBILITY_PRIVATE;
|
||||
|
||||
public class BriarNotificationBuilder extends NotificationCompat.Builder {
|
||||
@@ -24,7 +23,7 @@ public class BriarNotificationBuilder extends NotificationCompat.Builder {
|
||||
|
||||
setLights(ContextCompat.getColor(context, R.color.briar_lime_400),
|
||||
750, 500);
|
||||
if (SDK_INT >= 21) setVisibility(VISIBILITY_PRIVATE);
|
||||
setVisibility(VISIBILITY_PRIVATE);
|
||||
}
|
||||
|
||||
public BriarNotificationBuilder setColorRes(@ColorRes int res) {
|
||||
@@ -33,7 +32,7 @@ public class BriarNotificationBuilder extends NotificationCompat.Builder {
|
||||
}
|
||||
|
||||
public BriarNotificationBuilder setNotificationCategory(String category) {
|
||||
if (SDK_INT >= 21) setCategory(category);
|
||||
setCategory(category);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@ import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
|
||||
import com.google.android.material.snackbar.Snackbar;
|
||||
import com.google.android.material.snackbar.Snackbar.Callback;
|
||||
|
||||
import org.briarproject.briar.R;
|
||||
import org.briarproject.nullsafety.NotNullByDefault;
|
||||
@@ -13,11 +12,7 @@ import androidx.annotation.ColorRes;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.StringRes;
|
||||
|
||||
import static android.os.Build.VERSION.SDK_INT;
|
||||
import static android.view.View.INVISIBLE;
|
||||
import static android.view.View.VISIBLE;
|
||||
import static androidx.core.content.ContextCompat.getColor;
|
||||
import static com.google.android.material.snackbar.Snackbar.LENGTH_INDEFINITE;
|
||||
|
||||
@NotNullByDefault
|
||||
public class BriarSnackbarBuilder {
|
||||
@@ -37,24 +32,6 @@ public class BriarSnackbarBuilder {
|
||||
R.color.briar_button_text_positive));
|
||||
s.setAction(actionResId, onClickListener);
|
||||
}
|
||||
// Workaround for https://issuetracker.google.com/issues/64285517
|
||||
if (duration == LENGTH_INDEFINITE && SDK_INT < 21) {
|
||||
// Hide snackbar while it's opening to make bouncing less noticeable
|
||||
s.getView().setVisibility(INVISIBLE);
|
||||
s.addCallback(new Callback() {
|
||||
@Override
|
||||
public void onShown(Snackbar snackbar) {
|
||||
snackbar.getView().setVisibility(VISIBLE);
|
||||
// Request layout again in case snackbar is in wrong place
|
||||
snackbar.getView().requestLayout();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDismissed(Snackbar snackbar, int event) {
|
||||
snackbar.getView().setVisibility(INVISIBLE);
|
||||
}
|
||||
});
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
@@ -51,7 +51,6 @@ import androidx.annotation.ColorInt;
|
||||
import androidx.annotation.ColorRes;
|
||||
import androidx.annotation.DrawableRes;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.RequiresApi;
|
||||
import androidx.annotation.UiThread;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.core.content.ContextCompat;
|
||||
@@ -108,8 +107,6 @@ import static java.util.logging.Level.WARNING;
|
||||
import static java.util.logging.Logger.getLogger;
|
||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
||||
import static org.briarproject.briar.android.TestingConstants.EXPIRY_DATE;
|
||||
import static org.briarproject.briar.android.TestingConstants.IS_OLD_ANDROID;
|
||||
import static org.briarproject.briar.android.TestingConstants.OLD_ANDROID_WARN_DATE;
|
||||
import static org.briarproject.briar.android.reporting.CrashReportActivity.EXTRA_APP_LOGCAT;
|
||||
import static org.briarproject.briar.android.reporting.CrashReportActivity.EXTRA_APP_START_TIME;
|
||||
import static org.briarproject.briar.android.reporting.CrashReportActivity.EXTRA_INITIAL_COMMENT;
|
||||
@@ -244,11 +241,6 @@ public class UiUtils {
|
||||
return (EXPIRY_DATE - now) / DAYS.toMillis(1);
|
||||
}
|
||||
|
||||
public static boolean shouldWarnOldAndroidExpiry() {
|
||||
return IS_OLD_ANDROID &&
|
||||
System.currentTimeMillis() >= OLD_ANDROID_WARN_DATE;
|
||||
}
|
||||
|
||||
public static SpannableStringBuilder getTeaser(Context ctx, Spanned text) {
|
||||
if (text.length() < TEASER_LENGTH)
|
||||
throw new IllegalArgumentException(
|
||||
@@ -376,7 +368,6 @@ public class UiUtils {
|
||||
}
|
||||
|
||||
public static boolean hasKeyguardLock(Context ctx) {
|
||||
if (SDK_INT < 21) return false;
|
||||
KeyguardManager keyguardManager =
|
||||
(KeyguardManager) ctx.getSystemService(KEYGUARD_SERVICE);
|
||||
if (keyguardManager == null) return false;
|
||||
@@ -438,7 +429,6 @@ public class UiUtils {
|
||||
keyEvent.getKeyCode() == KEYCODE_ENTER;
|
||||
}
|
||||
|
||||
@RequiresApi(api = 21)
|
||||
public static void excludeSystemUi(Transition transition) {
|
||||
transition.excludeTarget(android.R.id.statusBarBackground, true);
|
||||
transition.excludeTarget(android.R.id.navigationBarBackground, true);
|
||||
@@ -480,7 +470,6 @@ public class UiUtils {
|
||||
}
|
||||
|
||||
public static boolean isRtl(Context ctx) {
|
||||
if (SDK_INT < 17) return false;
|
||||
return ctx.getResources().getConfiguration().getLayoutDirection() ==
|
||||
LAYOUT_DIRECTION_RTL;
|
||||
}
|
||||
@@ -507,7 +496,7 @@ public class UiUtils {
|
||||
view.setVisibility(small ? GONE : VISIBLE);
|
||||
}
|
||||
|
||||
public static boolean isSmallScreenRelativeToFontSize(Context ctx) {
|
||||
private static boolean isSmallScreenRelativeToFontSize(Context ctx) {
|
||||
Configuration config = ctx.getResources().getConfiguration();
|
||||
if (config.fontScale == 0f) return true;
|
||||
return config.screenHeightDp / config.fontScale < 600;
|
||||
@@ -546,7 +535,7 @@ public class UiUtils {
|
||||
}
|
||||
|
||||
public static void launchActivityToOpenFile(Context ctx,
|
||||
@Nullable ActivityResultLauncher<String[]> docLauncher,
|
||||
ActivityResultLauncher<String[]> docLauncher,
|
||||
ActivityResultLauncher<String> contentLauncher,
|
||||
String contentType) {
|
||||
// Try GET_CONTENT, fall back to OPEN_DOCUMENT if available
|
||||
@@ -556,13 +545,11 @@ public class UiUtils {
|
||||
} catch (ActivityNotFoundException e) {
|
||||
logException(LOG, WARNING, e);
|
||||
}
|
||||
if (docLauncher != null) {
|
||||
try {
|
||||
docLauncher.launch(new String[] {contentType});
|
||||
return;
|
||||
} catch (ActivityNotFoundException e) {
|
||||
logException(LOG, WARNING, e);
|
||||
}
|
||||
try {
|
||||
docLauncher.launch(new String[] {contentType});
|
||||
return;
|
||||
} catch (ActivityNotFoundException e) {
|
||||
logException(LOG, WARNING, e);
|
||||
}
|
||||
Toast.makeText(ctx, R.string.error_start_activity, LENGTH_LONG).show();
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package org.briarproject.briar.android.widget;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.MotionEvent;
|
||||
@@ -27,7 +26,6 @@ public class TouchInterceptingLinearLayout extends LinearLayout {
|
||||
super(context, attrs, defStyleAttr);
|
||||
}
|
||||
|
||||
@TargetApi(21)
|
||||
public TouchInterceptingLinearLayout(Context context, AttributeSet attrs,
|
||||
int defStyleAttr, int defStyleRes) {
|
||||
super(context, attrs, defStyleAttr, defStyleRes);
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:tint="?android:attr/textColorPrimary"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#FFFFFFFF"
|
||||
android:pathData="M21,12L14,5V9C7,10 4,15 3,20C5.5,16.5 9,14.9 14,14.9V19L21,12Z" />
|
||||
</vector>
|
||||
@@ -191,6 +191,66 @@
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/textViewForumMessages" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textViewPrivateGroups"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="@dimen/margin_medium"
|
||||
android:text="Number of private groups"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/seekBarForumMessages" />
|
||||
|
||||
<SeekBar
|
||||
android:id="@+id/seekBarPrivateGroups"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:max="50"
|
||||
android:paddingTop="5dp"
|
||||
android:progress="5"
|
||||
app:layout_constraintEnd_toStartOf="@+id/TextViewPrivateGroupsSb"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/textViewPrivateGroups" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/TextViewPrivateGroupsSb"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:ems="2"
|
||||
android:text="5"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/seekBarPrivateGroups"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/textViewPrivateGroups" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textViewPrivateGroupMessages"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="@dimen/margin_medium"
|
||||
android:text="Number of private group messages"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/seekBarPrivateGroups" />
|
||||
|
||||
<SeekBar
|
||||
android:id="@+id/seekBarPrivateGroupMessages"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:max="50"
|
||||
android:paddingTop="5dp"
|
||||
android:progress="20"
|
||||
app:layout_constraintEnd_toStartOf="@+id/TextViewPrivateGroupMessagesSb"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/textViewPrivateGroupMessages" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/TextViewPrivateGroupMessagesSb"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:ems="2"
|
||||
android:text="20"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/seekBarPrivateGroupMessages"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/textViewPrivateGroupMessages" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/buttonZeroValues"
|
||||
style="@style/BriarButton"
|
||||
@@ -201,7 +261,7 @@
|
||||
android:text="Zero values"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/seekBarForumMessages"
|
||||
app:layout_constraintTop_toBottomOf="@+id/seekBarPrivateGroupMessages"
|
||||
app:layout_constraintVertical_bias="1.0" />
|
||||
|
||||
<Button
|
||||
|
||||
@@ -52,10 +52,10 @@
|
||||
android:layout_height="24dp"
|
||||
android:layout_margin="@dimen/margin_medium"
|
||||
android:contentDescription="@string/help"
|
||||
android:tint="@color/briar_button_text_positive"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/button"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@+id/button"
|
||||
app:srcCompat="@drawable/ic_help_outline_white" />
|
||||
app:srcCompat="@drawable/ic_help_outline_white"
|
||||
app:tint="@color/briar_button_text_positive" />
|
||||
|
||||
</merge>
|
||||
@@ -35,7 +35,6 @@
|
||||
<item
|
||||
android:id="@+id/action_transfer_data"
|
||||
android:title="@string/removable_drive_menu_title"
|
||||
android:visible="false"
|
||||
app:showAsAction="never" />
|
||||
</menu>
|
||||
</item>
|
||||
|
||||
@@ -27,6 +27,8 @@
|
||||
<string name="dnkm_xiaomi_help">Ако Briar не е заключен в списъка с последно използваните приложения, няма да работи на заден план.</string>
|
||||
<string name="dnkm_xiaomi_dialog_body_old">1. Отворете списъка с отворени приложения (списък за превключване на приложения)\n\n2. Плъзнете надолу върху изображението на Briar докато се покаже икона на катинар\n\n3. Ако катинарът е отключен го докоснете, за да го заключите</string>
|
||||
<string name="dnkm_xiaomi_dialog_body_new">1. Отворете списъка с последните приложения\n\n2. Ако до името на Briar има значка на катинарче, не е необходимо да правите нищо\n\n3. Ако няма – натиснете и задръжте изображението на Briar, докато се появи бутон за катинарче, след което го докоснете</string>
|
||||
<string name="dnkm_xiaomi_lock_apps_text">Моля, докоснете бутона по-долу, за да отворите настройките за сигурност. Натиснете бутона \"Ускори\", след това натиснете \"Заключени приложения\" и се уверете, че Briar е \"Заключен\"</string>
|
||||
<string name="dnkm_xiaomi_lock_apps_help">Ако Briar не е \"Заключен\" в менюто \"Заключени приложения\", няма да може да работи на заден план.</string>
|
||||
<string name="dnkm_warning_dozed_1">Briar не може да работи във фонов режим</string>
|
||||
<!--Login-->
|
||||
<string name="enter_password">Парола</string>
|
||||
@@ -47,10 +49,6 @@
|
||||
<item quantity="one">Това е тестова версия на Briar. Вашият акаунт ще бъде изтече след %d ден и не може да бъде подновена.</item>
|
||||
<item quantity="other">Това е изпитателно издание на Briar. Валидността на профила ще изтече след %d дена и не може да бъде подновена.</item>
|
||||
</plurals>
|
||||
<plurals name="old_android_expiry_warning">
|
||||
<item quantity="one">Android 4 не се поддържа. Briar ще спре да работи на %s (след %d ден). Инсталирайте Briar на друго устройство и създайте нов профил.</item>
|
||||
<item quantity="other">Android 4 не се поддържа. Briar ще спре да работи на %s (след %d дена). Инсталирайте Briar на друго устройство и създайте нов профил.</item>
|
||||
</plurals>
|
||||
<string name="expiry_date_reached">Софтуерът е с изтекъл срок.\nБлагодарим за изпитването!</string>
|
||||
<string name="download_briar">За да продължите да използвате Briar изтеглете последното издание.</string>
|
||||
<string name="create_new_account">Ще трябва да създадете нов профил, но ще можете да използвате същия прякор.</string>
|
||||
|
||||
@@ -49,10 +49,6 @@
|
||||
<item quantity="one">Aquesta és una versió de prova de Briar. El vostre compte expira en %d dia i no es pot renovar.</item>
|
||||
<item quantity="other">Aquesta és una versió de prova de Briar. El vostre compte caducarà en %d dies i no es podrà renovar.</item>
|
||||
</plurals>
|
||||
<plurals name="old_android_expiry_warning">
|
||||
<item quantity="one">L\'Android 4 ja no és compatible. El Briar deixarà de funcionar a %s (d\'aquí a %d dia). Instal·leu el Briar en un dispositiu més nou i creeu un compte nou.</item>
|
||||
<item quantity="other">L\'Android 4 ja no és compatible. El Briar deixarà de funcionar a %s (d\'aquí a %d dies). Instal·leu el Briar en un dispositiu més nou i creeu un compte nou.</item>
|
||||
</plurals>
|
||||
<string name="expiry_date_reached">Aquesta versió de Briar ha caducat.\nGràcies per haver-lo provat!</string>
|
||||
<string name="download_briar">Per continuar utilitzant Briar, baixeu la darrera versió.</string>
|
||||
<string name="create_new_account">Haureu de crear un compte nou, però podeu utilitzar el mateix sobrenom.</string>
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
<string name="dialog_title_lost_password">Passwort vergessen</string>
|
||||
<string name="dialog_message_lost_password">Dein Briar-Konto ist verschlüsselt auf deinem Gerät und nicht in der Cloud gespeichert, deshalb kannst du dein Passwort nicht zurücksetzen. Willst du dein Konto löschen und neu beginnen?\n\nAchtung: Deine bestehenden Identitäten, Kontakte und Nachrichten gehen dann für immer verloren.</string>
|
||||
<string name="startup_failed_activity_title">Fehler beim Starten von Briar</string>
|
||||
<string name="startup_failed_clock_error">Briar konnte nicht gestartet werden, weil die Uhr deines Geräts falsch eingestellt ist.\n\Bitte stelle die Uhr deines Geräts auf die richtige Zeit ein und versuche es erneut.</string>
|
||||
<string name="startup_failed_clock_error">Briar konnte nicht gestartet werden, weil die Uhr deines Geräts falsch eingestellt ist.\n\nBitte stelle die Uhr deines Geräts auf die richtige Zeit ein und versuche es erneut.</string>
|
||||
<string name="startup_failed_db_error">Briar konnte die Datenbank mit deinem Konto, deinen Kontakten und deinen Nachrichten nicht öffnen.\n\nBitte aktualisiere auf die neueste Version der App und versuche es erneut, oder richte ein neues Konto ein, indem du bei der Passwortabfrage \"Ich habe mein Passwort vergessen\" wählst.</string>
|
||||
<string name="startup_failed_data_too_old_error">Dein Konto wurde mit einer alten Version dieser App erstellt und kann mit dieser Version nicht geöffnet werden.\n\nDu musst entweder die alte Version neu installieren oder ein neues Konto einrichten, indem du bei der Passwortabfrage \"Ich habe mein Passwort vergessen\" wählst.</string>
|
||||
<string name="startup_failed_data_too_new_error">Dein Konto wurde mit einer neueren Version dieser App erstellt und kann mit dieser Version nicht geöffnet werden.\n\nBitte aktualisiere auf die neueste Version und versuche es erneut.</string>
|
||||
@@ -49,10 +49,6 @@
|
||||
<item quantity="one">Dies ist eine Testversion von Briar. Dein Konto läuft in %d Tag ab und kann nicht verlängert werden.</item>
|
||||
<item quantity="other">Dies ist eine Testversion von Briar. Dein Konto läuft in %d Tagen ab und kann nicht verlängert werden.</item>
|
||||
</plurals>
|
||||
<plurals name="old_android_expiry_warning">
|
||||
<item quantity="one">Android 4 wird nicht mehr unterstützt. Briar wird nicht mehr auf %s funktionieren (in %d Tag). Bitte installiere es auf einem neueren Gerät und erstelle ein neues Konto.</item>
|
||||
<item quantity="other">Android 4 wird nicht mehr unterstützt. Briar wird nicht mehr auf %s funktionieren (in %dTagen). Bitte installiere es auf einem neueren Gerät und erstelle ein neues Konto.</item>
|
||||
</plurals>
|
||||
<string name="expiry_date_reached">Diese Software ist abgelaufen.\nDanke, dass du Briar getestet hast!</string>
|
||||
<string name="download_briar">Bitte lade die aktuelle Version von Briar herunter, um es weiter zu benutzen.</string>
|
||||
<string name="create_new_account">Du wirst ein neues Konto erstellen müssen, wobei du jedoch wieder denselben Spitznamen verwenden kannst.</string>
|
||||
|
||||
@@ -50,11 +50,6 @@
|
||||
<item quantity="many">Esta es una versión de prueba de Briar. Tu cuenta expirará en %d días y no podrá ser renovada.</item>
|
||||
<item quantity="other">Esta es una versión de prueba de Briar. Tu cuenta expirará en %d días y no podrá ser renovada.</item>
|
||||
</plurals>
|
||||
<plurals name="old_android_expiry_warning">
|
||||
<item quantity="one">Android 4 ya no es soportado. Briar dejará de funcionar sobre %s (en %d días). Por favor, instala Briar en un dispositivo más nuevo y crea una cuenta nueva.</item>
|
||||
<item quantity="many">Android 4 ya no es soportado. Briar dejará de funcionar sobre %s (en %d días). Por favor, instala Briar en un dispositivo más nuevo y crea una cuenta nueva.</item>
|
||||
<item quantity="other">Android 4 ya no es soportado. Briar dejará de funcionar sobre %s (en %d días). Por favor, instala Briar en un dispositivo más nuevo y crea una cuenta nueva.</item>
|
||||
</plurals>
|
||||
<string name="expiry_date_reached">Este programa ha caducado.\n¡Gracias por probarlo!</string>
|
||||
<string name="download_briar">Para continuar usando Briar, por favor descarga la versión mas reciente.</string>
|
||||
<string name="create_new_account">Necesitarás crear una nueva cuenta, pero puedes usar el mismo nombre de usuario.</string>
|
||||
|
||||
@@ -53,10 +53,6 @@
|
||||
<item quantity="one">این یک نسخه آزمایشی از Briar (برایر) می باشد. حساب کاربری شما در %d روز آینده به پایان می رسد و امکان تمدید آن وجود نخواهد داشت.</item>
|
||||
<item quantity="other">این یک نسخه آزمایشی از Briar (برایر) می باشد. حساب کاربری شما در %d روز آینده به پایان می رسد و امکان تمدید آن وجود نخواهد داشت.</item>
|
||||
</plurals>
|
||||
<plurals name="old_android_expiry_warning">
|
||||
<item quantity="one">اندروید 4 دیگر پشتیبانی نمیشود. Briar (در عرض %d روز) کار بر روی %s را متوقف خواهد کرد. لطفا Briar را در دستگاه جدیدتر نصب کنید و یک حساب کاربری جدید ایجاد کنید.</item>
|
||||
<item quantity="other">اندروید 4 دیگر پشتیبانی نمیشود. Briar (در عرض %d روز) کار بر روی %s را متوقف خواهد کرد. لطفا Briar را در دستگاه جدیدتر نصب کنید و یک حساب کاربری جدید ایجاد کنید.</item>
|
||||
</plurals>
|
||||
<string name="expiry_date_reached">این نرم افزار منقضی شده است.
|
||||
|
||||
بابت تست از شما سپاسگزاریم.</string>
|
||||
@@ -700,7 +696,7 @@
|
||||
<string name="mailbox_error_wizard_info2">لطفا هنگامی که به دستگاه دسترسی دارید به این صفحه بازگردید.</string>
|
||||
<string name="mailbox_error_wizard_info3">لطفا با استفاده از دکمه زیر، پیوند Mailbox خود را لغو کنید.\n\nپس از لغو پیوند Mailbox قدیمی، میتوانید هر زمان که خواستید یک Mailbox جدید راهاندازی کنید.</string>
|
||||
<!--About-->
|
||||
<string name="about_title">دربارهی Psiphon</string>
|
||||
<string name="about_title">دربارهی Briar</string>
|
||||
<string name="briar_version">نسخه Briar: %s</string>
|
||||
<string name="tor_version">نسخه Tor: %s</string>
|
||||
<string name="links">لینک ها</string>
|
||||
|
||||
@@ -46,11 +46,6 @@
|
||||
<item quantity="many">Ceci est une version d’essai de Briar. Votre compte arrivera à expiration dans %d jours et ne pourra pas être renouvelé.</item>
|
||||
<item quantity="other">Ceci est une version d’essai de Briar. Votre compte arrivera à expiration dans %d jours et ne pourra pas être renouvelé.</item>
|
||||
</plurals>
|
||||
<plurals name="old_android_expiry_warning">
|
||||
<item quantity="one">Android 4 n’est plus pris en charge. Briar cessera de fonctionner le %s (dans %d jour). Veuillez installer Briar sur un appareil plus récent et créer un nouveau compte.</item>
|
||||
<item quantity="many">Android 4 n’est plus pris en charge. Briar cessera de fonctionner le %s (dans %d jours). Veuillez installer Briar sur un appareil plus récent et créer un nouveau compte.</item>
|
||||
<item quantity="other">Android 4 n’est plus pris en charge. Briar cessera de fonctionner le %s (dans %d jours). Veuillez installer Briar sur un appareil plus récent et créer un nouveau compte.</item>
|
||||
</plurals>
|
||||
<string name="expiry_date_reached">Ce logiciel est arrivé à expiration.\nMerci de l’avoir testé !</string>
|
||||
<string name="download_briar">Pour continuer à utiliser Briar, veuillez télécharger la dernière version.</string>
|
||||
<string name="create_new_account">Vous devrez créer un nouveau compte, mais vous pouvez utiliser le même pseudonyme.</string>
|
||||
|
||||
@@ -45,10 +45,6 @@
|
||||
<item quantity="one">Þetta er prufuútgáfa af Briar. Notandaaðgangurinn þinn mun renna út eftir %d dag og er ekki hægt að endurnýja hann.</item>
|
||||
<item quantity="other">Þetta er prufuútgáfa af Briar. Notandaaðgangurinn þinn mun renna út eftir %d daga og er ekki hægt að endurnýja hann.</item>
|
||||
</plurals>
|
||||
<plurals name="old_android_expiry_warning">
|
||||
<item quantity="one">Android 4 er ekki lengur stutt. Briar mun hætta að virka á %s (eftir %d dag). Settu Briar upp á nýrra tæki og útbúðu nýjan aðgang.</item>
|
||||
<item quantity="other">Android 4 er ekki lengur stutt. Briar mun hætta að virka á %s (eftir %d daga). Settu Briar upp á nýrra tæki og útbúðu nýjan aðgang.</item>
|
||||
</plurals>
|
||||
<string name="expiry_date_reached">Þessi hugbúnaður er úreltur.\nTakk fyrir að hafa tekið þátt í prófunum!</string>
|
||||
<string name="download_briar">Til að halda áfram að nota Briar, ættirðu að sækja nýjustu útgáfuna.</string>
|
||||
<string name="create_new_account">Þú þarft að búa til nýjan notandaaðgang, en þú getur notað áfram sama stuttnefni.</string>
|
||||
|
||||
@@ -49,10 +49,6 @@
|
||||
<item quantity="one">ესაა საცდელი ვერსიის Briar. თქვენს ანგარიშს %d დღეში გაუვა ვადა და ვეღარ გახანგრძლივდება.</item>
|
||||
<item quantity="other">ეს არის Briar-ის საცდელი ვერსია, რომლის ვადა %d დღეში ამოიწურება და მისი განახლება შეუძლებელია.</item>
|
||||
</plurals>
|
||||
<plurals name="old_android_expiry_warning">
|
||||
<item quantity="one">Android 4 აღარაა მხარდაჭერილი. Briar-ის გაუქმების ვადაა %s (%d დღეში). გთხოვთ, Briar გამოიყენოთ უფრო ახალ მოწყობილობაზე და ახალი ანგარიში შექმნათ.</item>
|
||||
<item quantity="other">Android 4 აღარაა მხარდაჭერილი. Briar შეწყვეტს მუშაობას %s (%d დღეში). გთხოვთ, დააინსტალიროთ Briar უფრო ახალ მოწყობილობაზე და ახალი ანგარიში შექმნათ.</item>
|
||||
</plurals>
|
||||
<string name="expiry_date_reached">პროგრამული უზრუნველყოფის ვადა ამოიწურა.\nგმადლობთ ტესტირებისთვის!</string>
|
||||
<string name="download_briar">თუ გსურთ, კვლავ გამოიყენოთ Briar, ჩამოტვირთეთ ბოლო გამოშვება.</string>
|
||||
<string name="create_new_account">დაგჭირდებათ ახალი ანგარიშის შექმნა, თუმცა იმავე მეტსახელის გამოყენება შეგეძლებათ.</string>
|
||||
|
||||
@@ -47,12 +47,6 @@
|
||||
<item quantity="many">Tai yra bandomoji Briar versija. Jūsų paskyros galiojimas pasibaigs po %d dienų ir negalės būti pratęstas.</item>
|
||||
<item quantity="other">Tai yra bandomoji Briar versija. Jūsų paskyros galiojimas pasibaigs po %d dienos ir negalės būti pratęstas.</item>
|
||||
</plurals>
|
||||
<plurals name="old_android_expiry_warning">
|
||||
<item quantity="one">„Android“ 4 yra daugiau nebepalaikoma. Briar nustos veikti ties %s (po %d dienos). Įsidiekite Briar naujesniame įrenginyje ir susikurkite naują paskyrą.</item>
|
||||
<item quantity="few">„Android“ 4 yra daugiau nebepalaikoma. Briar nustos veikti ties %s (po %d dienų). Įsidiekite Briar naujesniame įrenginyje ir susikurkite naują paskyrą.</item>
|
||||
<item quantity="many">„Android“ 4 yra daugiau nebepalaikoma. Briar nustos veikti ties %s (po %d dienų). Įsidiekite Briar naujesniame įrenginyje ir susikurkite naują paskyrą.</item>
|
||||
<item quantity="other">„Android“ 4 yra daugiau nebepalaikoma. Briar nustos veikti ties %s (po %d dienos). Įsidiekite Briar naujesniame įrenginyje ir susikurkite naują paskyrą.</item>
|
||||
</plurals>
|
||||
<string name="expiry_date_reached">Programinės įrangos galiojimas pasibaigė.\nDėkojame, kad išbandėte!</string>
|
||||
<string name="download_briar">Norėdami ir toliau naudotis Briar, atsisiųskite naujausią programos laidą.</string>
|
||||
<string name="create_new_account">Jūs turėsite susikurti paskyrą, tačiau galėsite naudoti tą patį slapyvardį.</string>
|
||||
@@ -780,6 +774,7 @@
|
||||
<string name="hotspot_qr_site">Jūsų telefonas teikia belaidį (Wi-Fi) prieigos tašką. Žmonės, prisijungę prie prieigos taško, gali atsisiųsti Briar, skenuodami šį QR kodą.</string>
|
||||
<!--e.g. Download Briar 1.2.20-->
|
||||
<string name="website_download_title_1">Atsisiųsti Briar %s</string>
|
||||
<string name="website_download_intro_1">Kažkas iš šalia esančių pradėjo bendrinti su jumis Briar.</string>
|
||||
<string name="website_download_button">Atsisiųsti Briar</string>
|
||||
<string name="website_download_outro">Kai atsisiuntimas pasibaigs, atverkite atsisiųstą failą ir jį įdiekite.</string>
|
||||
<string name="website_troubleshooting_title">Nesklandumų šalinimas</string>
|
||||
|
||||
@@ -44,9 +44,6 @@
|
||||
<plurals name="expiry_warning">
|
||||
<item quantity="other">ဤအရာသည် Briar ၏ စမ်းသပ်ဆဲဗားရှင်းဖြစ်ပါသည်။ သင့်အကောင့်သည် %d ရက်ကြာလျှင် သက်တမ်းကုန်ဆုံးမည်ဖြစ်ပြီး သက်တမ်းတိုး၍မရနိုင်ပါ။</item>
|
||||
</plurals>
|
||||
<plurals name="old_android_expiry_warning">
|
||||
<item quantity="other">အန်းဒရိုက် ၄ ကို မပံ့ပိုးတော့ပါ။ Briar သည် %s(ရက်%dတွင်) အလုပ် မလုပ်တော့ပါ။ ကျေးဇူးပြု၍ Briar ကို စက်ပစ္စည်းအသစ်တွင် ထည့်သွင်းပြီး အကောင့်အသစ်တစ်ခု ဖန်တီးပါ။</item>
|
||||
</plurals>
|
||||
<string name="expiry_date_reached">ဤဆော့ဖ်ဝဲသည် သက်တမ်းကုန်သွားပါပြီ။ \nစမ်းသပ်အသုံးပြုခြင်းအတွက် ကျေးဇူးတင်ပါသည်။</string>
|
||||
<string name="download_briar">Briar ကို ဆက်လက်အသုံးပြုရန် နောက်ဆုံးထွက်ထားသည်ကို ဒေါင်းလုဒ်လုပ်ပါ။</string>
|
||||
<string name="create_new_account">အကောင့်အသစ်ဖွင့်ရန် လိုအပ်သော်လည်း သုံးလက်စနာမည်ပြောင်ကို ဆက်သုံးနိုင်ပါသည်။</string>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user