mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-12 02:39:05 +01:00
Compare commits
23 Commits
1233-remot
...
release-1.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6565172e10 | ||
|
|
7447468ce5 | ||
|
|
22eed91019 | ||
|
|
fcb88ed58c | ||
|
|
0d940fc7d7 | ||
|
|
53da13794f | ||
|
|
2ab03f48cc | ||
|
|
436f45554d | ||
|
|
51209b5eec | ||
|
|
822597b4c6 | ||
|
|
7c01bc59c0 | ||
|
|
825d342f9b | ||
|
|
34955fecbb | ||
|
|
5c28b60a6b | ||
|
|
9c4fb4fd34 | ||
|
|
3d6a336f6d | ||
|
|
4b7a81177c | ||
|
|
9515e93857 | ||
|
|
efe15df940 | ||
|
|
de611857cf | ||
|
|
8935ec2c2e | ||
|
|
bd00fb1c04 | ||
|
|
3192015cfd |
@@ -9,8 +9,8 @@ android {
|
||||
defaultConfig {
|
||||
minSdkVersion 14
|
||||
targetSdkVersion 26
|
||||
versionCode 10103
|
||||
versionName "1.1.3"
|
||||
versionCode 10104
|
||||
versionName "1.1.4"
|
||||
consumerProguardFiles 'proguard-rules.txt'
|
||||
|
||||
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
||||
|
||||
@@ -16,18 +16,27 @@ import org.briarproject.bramble.api.plugin.PluginException;
|
||||
import org.briarproject.bramble.api.plugin.duplex.DuplexPluginCallback;
|
||||
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
|
||||
import org.briarproject.bramble.api.system.AndroidExecutor;
|
||||
import org.briarproject.bramble.api.system.Clock;
|
||||
import org.briarproject.bramble.util.AndroidUtils;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import static android.bluetooth.BluetoothAdapter.ACTION_DISCOVERY_FINISHED;
|
||||
import static android.bluetooth.BluetoothAdapter.ACTION_DISCOVERY_STARTED;
|
||||
import static android.bluetooth.BluetoothAdapter.ACTION_SCAN_MODE_CHANGED;
|
||||
import static android.bluetooth.BluetoothAdapter.ACTION_STATE_CHANGED;
|
||||
import static android.bluetooth.BluetoothAdapter.EXTRA_SCAN_MODE;
|
||||
@@ -37,8 +46,13 @@ import static android.bluetooth.BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERA
|
||||
import static android.bluetooth.BluetoothAdapter.SCAN_MODE_NONE;
|
||||
import static android.bluetooth.BluetoothAdapter.STATE_OFF;
|
||||
import static android.bluetooth.BluetoothAdapter.STATE_ON;
|
||||
import static android.bluetooth.BluetoothDevice.ACTION_FOUND;
|
||||
import static android.bluetooth.BluetoothDevice.EXTRA_DEVICE;
|
||||
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
||||
import static java.util.logging.Level.INFO;
|
||||
import static java.util.logging.Level.WARNING;
|
||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
||||
import static org.briarproject.bramble.util.PrivacyUtils.scrubMacAddress;
|
||||
|
||||
@MethodsNotNullByDefault
|
||||
@ParametersNotNullByDefault
|
||||
@@ -47,8 +61,11 @@ class AndroidBluetoothPlugin extends BluetoothPlugin<BluetoothServerSocket> {
|
||||
private static final Logger LOG =
|
||||
Logger.getLogger(AndroidBluetoothPlugin.class.getName());
|
||||
|
||||
private static final int MAX_DISCOVERY_MS = 10_000;
|
||||
|
||||
private final AndroidExecutor androidExecutor;
|
||||
private final Context appContext;
|
||||
private final Clock clock;
|
||||
|
||||
private volatile boolean wasEnabledByUs = false;
|
||||
private volatile BluetoothStateReceiver receiver = null;
|
||||
@@ -58,12 +75,13 @@ class AndroidBluetoothPlugin extends BluetoothPlugin<BluetoothServerSocket> {
|
||||
|
||||
AndroidBluetoothPlugin(BluetoothConnectionLimiter connectionLimiter,
|
||||
Executor ioExecutor, AndroidExecutor androidExecutor,
|
||||
Context appContext, SecureRandom secureRandom, Backoff backoff,
|
||||
DuplexPluginCallback callback, int maxLatency) {
|
||||
Context appContext, SecureRandom secureRandom, Clock clock,
|
||||
Backoff backoff, DuplexPluginCallback callback, int maxLatency) {
|
||||
super(connectionLimiter, ioExecutor, secureRandom, backoff, callback,
|
||||
maxLatency);
|
||||
this.androidExecutor = androidExecutor;
|
||||
this.appContext = appContext;
|
||||
this.clock = clock;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -182,6 +200,74 @@ class AndroidBluetoothPlugin extends BluetoothPlugin<BluetoothServerSocket> {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
DuplexTransportConnection discoverAndConnect(String uuid) {
|
||||
if (adapter == null) return null;
|
||||
for (String address : discoverDevices()) {
|
||||
try {
|
||||
if (LOG.isLoggable(INFO))
|
||||
LOG.info("Connecting to " + scrubMacAddress(address));
|
||||
return connectTo(address, uuid);
|
||||
} catch (IOException e) {
|
||||
if (LOG.isLoggable(INFO)) {
|
||||
LOG.info("Could not connect to "
|
||||
+ scrubMacAddress(address));
|
||||
}
|
||||
}
|
||||
}
|
||||
LOG.info("Could not connect to any devices");
|
||||
return null;
|
||||
}
|
||||
|
||||
private Collection<String> discoverDevices() {
|
||||
List<String> addresses = new ArrayList<>();
|
||||
BlockingQueue<Intent> intents = new LinkedBlockingQueue<>();
|
||||
DiscoveryReceiver receiver = new DiscoveryReceiver(intents);
|
||||
IntentFilter filter = new IntentFilter();
|
||||
filter.addAction(ACTION_DISCOVERY_STARTED);
|
||||
filter.addAction(ACTION_DISCOVERY_FINISHED);
|
||||
filter.addAction(ACTION_FOUND);
|
||||
appContext.registerReceiver(receiver, filter);
|
||||
try {
|
||||
if (adapter.startDiscovery()) {
|
||||
long now = clock.currentTimeMillis();
|
||||
long end = now + MAX_DISCOVERY_MS;
|
||||
while (now < end) {
|
||||
Intent i = intents.poll(end - now, MILLISECONDS);
|
||||
if (i == null) break;
|
||||
String action = i.getAction();
|
||||
if (ACTION_DISCOVERY_STARTED.equals(action)) {
|
||||
LOG.info("Discovery started");
|
||||
} else if (ACTION_DISCOVERY_FINISHED.equals(action)) {
|
||||
LOG.info("Discovery finished");
|
||||
break;
|
||||
} else if (ACTION_FOUND.equals(action)) {
|
||||
BluetoothDevice d = i.getParcelableExtra(EXTRA_DEVICE);
|
||||
String address = d.getAddress();
|
||||
if (LOG.isLoggable(INFO))
|
||||
LOG.info("Discovered " + scrubMacAddress(address));
|
||||
if (!addresses.contains(address))
|
||||
addresses.add(address);
|
||||
}
|
||||
now = clock.currentTimeMillis();
|
||||
}
|
||||
} else {
|
||||
LOG.info("Could not start discovery");
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
LOG.info("Interrupted while discovering devices");
|
||||
Thread.currentThread().interrupt();
|
||||
} finally {
|
||||
LOG.info("Cancelling discovery");
|
||||
adapter.cancelDiscovery();
|
||||
appContext.unregisterReceiver(receiver);
|
||||
}
|
||||
// Shuffle the addresses so we don't always try the same one first
|
||||
Collections.shuffle(addresses);
|
||||
return addresses;
|
||||
}
|
||||
|
||||
private void tryToClose(@Nullable Closeable c) {
|
||||
try {
|
||||
if (c != null) c.close();
|
||||
@@ -207,4 +293,18 @@ class AndroidBluetoothPlugin extends BluetoothPlugin<BluetoothServerSocket> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class DiscoveryReceiver extends BroadcastReceiver {
|
||||
|
||||
private final BlockingQueue<Intent> intents;
|
||||
|
||||
private DiscoveryReceiver(BlockingQueue<Intent> intents) {
|
||||
this.intents = intents;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReceive(Context ctx, Intent intent) {
|
||||
intents.add(intent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin;
|
||||
import org.briarproject.bramble.api.plugin.duplex.DuplexPluginCallback;
|
||||
import org.briarproject.bramble.api.plugin.duplex.DuplexPluginFactory;
|
||||
import org.briarproject.bramble.api.system.AndroidExecutor;
|
||||
import org.briarproject.bramble.api.system.Clock;
|
||||
|
||||
import java.security.SecureRandom;
|
||||
import java.util.concurrent.Executor;
|
||||
@@ -33,17 +34,19 @@ public class AndroidBluetoothPluginFactory implements DuplexPluginFactory {
|
||||
private final Context appContext;
|
||||
private final SecureRandom secureRandom;
|
||||
private final EventBus eventBus;
|
||||
private final Clock clock;
|
||||
private final BackoffFactory backoffFactory;
|
||||
|
||||
public AndroidBluetoothPluginFactory(Executor ioExecutor,
|
||||
AndroidExecutor androidExecutor, Context appContext,
|
||||
SecureRandom secureRandom, EventBus eventBus,
|
||||
SecureRandom secureRandom, EventBus eventBus, Clock clock,
|
||||
BackoffFactory backoffFactory) {
|
||||
this.ioExecutor = ioExecutor;
|
||||
this.androidExecutor = androidExecutor;
|
||||
this.appContext = appContext;
|
||||
this.secureRandom = secureRandom;
|
||||
this.eventBus = eventBus;
|
||||
this.clock = clock;
|
||||
this.backoffFactory = backoffFactory;
|
||||
}
|
||||
|
||||
@@ -65,7 +68,7 @@ public class AndroidBluetoothPluginFactory implements DuplexPluginFactory {
|
||||
MAX_POLLING_INTERVAL, BACKOFF_BASE);
|
||||
AndroidBluetoothPlugin plugin = new AndroidBluetoothPlugin(
|
||||
connectionLimiter, ioExecutor, androidExecutor, appContext,
|
||||
secureRandom, backoff, callback, MAX_LATENCY);
|
||||
secureRandom, clock, backoff, callback, MAX_LATENCY);
|
||||
eventBus.addListener(plugin);
|
||||
return plugin;
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ public interface KeyAgreementConstants {
|
||||
/**
|
||||
* The connection timeout in milliseconds.
|
||||
*/
|
||||
long CONNECTION_TIMEOUT = 20 * 1000;
|
||||
long CONNECTION_TIMEOUT = 60_000;
|
||||
|
||||
/**
|
||||
* The transport identifier for Bluetooth.
|
||||
|
||||
@@ -23,7 +23,6 @@ import org.briarproject.bramble.api.plugin.event.EnableBluetoothEvent;
|
||||
import org.briarproject.bramble.api.properties.TransportProperties;
|
||||
import org.briarproject.bramble.api.settings.Settings;
|
||||
import org.briarproject.bramble.api.settings.event.SettingsUpdatedEvent;
|
||||
import org.briarproject.bramble.util.StringUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.security.SecureRandom;
|
||||
@@ -46,6 +45,9 @@ import static org.briarproject.bramble.api.plugin.BluetoothConstants.PROP_UUID;
|
||||
import static org.briarproject.bramble.api.plugin.BluetoothConstants.UUID_BYTES;
|
||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
||||
import static org.briarproject.bramble.util.PrivacyUtils.scrubMacAddress;
|
||||
import static org.briarproject.bramble.util.StringUtils.isNullOrEmpty;
|
||||
import static org.briarproject.bramble.util.StringUtils.macToBytes;
|
||||
import static org.briarproject.bramble.util.StringUtils.macToString;
|
||||
|
||||
@MethodsNotNullByDefault
|
||||
@ParametersNotNullByDefault
|
||||
@@ -96,6 +98,9 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
|
||||
abstract DuplexTransportConnection connectTo(String address, String uuid)
|
||||
throws IOException;
|
||||
|
||||
@Nullable
|
||||
abstract DuplexTransportConnection discoverAndConnect(String uuid);
|
||||
|
||||
BluetoothPlugin(BluetoothConnectionLimiter connectionLimiter,
|
||||
Executor ioExecutor, SecureRandom secureRandom,
|
||||
Backoff backoff, DuplexPluginCallback callback, int maxLatency) {
|
||||
@@ -193,7 +198,7 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
|
||||
address = getBluetoothAddress();
|
||||
if (LOG.isLoggable(INFO))
|
||||
LOG.info("Local address " + scrubMacAddress(address));
|
||||
if (!StringUtils.isNullOrEmpty(address)) {
|
||||
if (!isNullOrEmpty(address)) {
|
||||
p.put(PROP_ADDRESS, address);
|
||||
changed = true;
|
||||
}
|
||||
@@ -256,9 +261,9 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
|
||||
// Try to connect to known devices in parallel
|
||||
for (Entry<ContactId, TransportProperties> e : contacts.entrySet()) {
|
||||
String address = e.getValue().get(PROP_ADDRESS);
|
||||
if (StringUtils.isNullOrEmpty(address)) continue;
|
||||
if (isNullOrEmpty(address)) continue;
|
||||
String uuid = e.getValue().get(PROP_UUID);
|
||||
if (StringUtils.isNullOrEmpty(uuid)) continue;
|
||||
if (isNullOrEmpty(uuid)) continue;
|
||||
ContactId c = e.getKey();
|
||||
ioExecutor.execute(() -> {
|
||||
if (!isRunning() || !shouldAllowContactConnections()) return;
|
||||
@@ -309,9 +314,9 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
|
||||
if (!isRunning() || !shouldAllowContactConnections()) return null;
|
||||
if (!connectionLimiter.canOpenContactConnection()) return null;
|
||||
String address = p.get(PROP_ADDRESS);
|
||||
if (StringUtils.isNullOrEmpty(address)) return null;
|
||||
if (isNullOrEmpty(address)) return null;
|
||||
String uuid = p.get(PROP_UUID);
|
||||
if (StringUtils.isNullOrEmpty(uuid)) return null;
|
||||
if (isNullOrEmpty(uuid)) return null;
|
||||
DuplexTransportConnection conn = connect(address, uuid);
|
||||
if (conn == null) return null;
|
||||
// TODO: Why don't we reset the backoff here?
|
||||
@@ -326,9 +331,6 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
|
||||
@Override
|
||||
public KeyAgreementListener createKeyAgreementListener(byte[] commitment) {
|
||||
if (!isRunning()) return null;
|
||||
// There's no point listening if we can't discover our own address
|
||||
String address = getBluetoothAddress();
|
||||
if (address == null) return null;
|
||||
// No truncation necessary because COMMIT_LENGTH = 16
|
||||
String uuid = UUID.nameUUIDFromBytes(commitment).toString();
|
||||
if (LOG.isLoggable(INFO)) LOG.info("Key agreement UUID " + uuid);
|
||||
@@ -346,7 +348,8 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
|
||||
}
|
||||
BdfList descriptor = new BdfList();
|
||||
descriptor.add(TRANSPORT_ID_BLUETOOTH);
|
||||
descriptor.add(StringUtils.macToBytes(address));
|
||||
String address = getBluetoothAddress();
|
||||
if (address != null) descriptor.add(macToBytes(address));
|
||||
return new BluetoothKeyAgreementListener(descriptor, ss);
|
||||
}
|
||||
|
||||
@@ -354,18 +357,25 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
|
||||
public DuplexTransportConnection createKeyAgreementConnection(
|
||||
byte[] commitment, BdfList descriptor) {
|
||||
if (!isRunning()) return null;
|
||||
String address;
|
||||
try {
|
||||
address = parseAddress(descriptor);
|
||||
} catch (FormatException e) {
|
||||
LOG.info("Invalid address in key agreement descriptor");
|
||||
return null;
|
||||
}
|
||||
// No truncation necessary because COMMIT_LENGTH = 16
|
||||
String uuid = UUID.nameUUIDFromBytes(commitment).toString();
|
||||
if (LOG.isLoggable(INFO))
|
||||
LOG.info("Connecting to key agreement UUID " + uuid);
|
||||
DuplexTransportConnection conn = connect(address, uuid);
|
||||
DuplexTransportConnection conn;
|
||||
if (descriptor.size() == 1) {
|
||||
if (LOG.isLoggable(INFO))
|
||||
LOG.info("Discovering address for key agreement UUID " + uuid);
|
||||
conn = discoverAndConnect(uuid);
|
||||
} else {
|
||||
String address;
|
||||
try {
|
||||
address = parseAddress(descriptor);
|
||||
} catch (FormatException e) {
|
||||
LOG.info("Invalid address in key agreement descriptor");
|
||||
return null;
|
||||
}
|
||||
if (LOG.isLoggable(INFO))
|
||||
LOG.info("Connecting to key agreement UUID " + uuid);
|
||||
conn = connect(address, uuid);
|
||||
}
|
||||
if (conn != null) connectionLimiter.keyAgreementConnectionOpened(conn);
|
||||
return conn;
|
||||
}
|
||||
@@ -373,7 +383,7 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
|
||||
private String parseAddress(BdfList descriptor) throws FormatException {
|
||||
byte[] mac = descriptor.getRaw(1);
|
||||
if (mac.length != 6) throw new FormatException();
|
||||
return StringUtils.macToString(mac);
|
||||
return macToString(mac);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -108,6 +108,12 @@ class JavaBluetoothPlugin extends BluetoothPlugin<StreamConnectionNotifier> {
|
||||
return wrapSocket((StreamConnection) Connector.open(url));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
DuplexTransportConnection discoverAndConnect(String uuid) {
|
||||
return null; // TODO
|
||||
}
|
||||
|
||||
private String makeUrl(String address, String uuid) {
|
||||
return "btspp://" + address + ":" + uuid + ";name=RFCOMM";
|
||||
}
|
||||
|
||||
@@ -22,8 +22,8 @@ android {
|
||||
defaultConfig {
|
||||
minSdkVersion 15
|
||||
targetSdkVersion 26
|
||||
versionCode 10103
|
||||
versionName "1.1.3"
|
||||
versionCode 10104
|
||||
versionName "1.1.4"
|
||||
applicationId "org.briarproject.briar.android"
|
||||
buildConfigField "String", "GitHash",
|
||||
"\"${getStdout(['git', 'rev-parse', '--short=7', 'HEAD'], 'No commit hash')}\""
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
<uses-feature android:name="android.hardware.camera" android:required="false"/>
|
||||
<uses-feature android:name="android.hardware.touchscreen" android:required="false" />
|
||||
|
||||
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
|
||||
<uses-permission android:name="android.permission.BLUETOOTH" />
|
||||
|
||||
@@ -107,7 +107,7 @@ public class AppModule {
|
||||
Context appContext = app.getApplicationContext();
|
||||
DuplexPluginFactory bluetooth =
|
||||
new AndroidBluetoothPluginFactory(ioExecutor, androidExecutor,
|
||||
appContext, random, eventBus, backoffFactory);
|
||||
appContext, random, eventBus, clock, backoffFactory);
|
||||
DuplexPluginFactory tor = new AndroidTorPluginFactory(ioExecutor,
|
||||
scheduler, appContext, networkManager, locationUtils, eventBus,
|
||||
torSocketFactory, backoffFactory, resourceProvider,
|
||||
|
||||
@@ -34,6 +34,7 @@ import org.briarproject.briar.api.android.ScreenFilterMonitor.AppDetails;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.inject.Inject;
|
||||
@@ -42,6 +43,8 @@ import static android.arch.lifecycle.Lifecycle.State.STARTED;
|
||||
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
|
||||
import static android.view.WindowManager.LayoutParams.FLAG_SECURE;
|
||||
import static android.view.inputmethod.InputMethodManager.SHOW_IMPLICIT;
|
||||
import static java.util.logging.Level.INFO;
|
||||
import static java.util.logging.Logger.getLogger;
|
||||
import static org.briarproject.briar.android.TestingConstants.PREVENT_SCREENSHOTS;
|
||||
|
||||
/**
|
||||
@@ -51,6 +54,8 @@ import static org.briarproject.briar.android.TestingConstants.PREVENT_SCREENSHOT
|
||||
public abstract class BaseActivity extends AppCompatActivity
|
||||
implements DestroyableContext, OnTapFilteredListener {
|
||||
|
||||
private final static Logger LOG = getLogger(BaseActivity.class.getName());
|
||||
|
||||
@Inject
|
||||
protected ScreenFilterMonitor screenFilterMonitor;
|
||||
|
||||
@@ -119,6 +124,8 @@ public abstract class BaseActivity extends AppCompatActivity
|
||||
@Override
|
||||
protected void onStart() {
|
||||
super.onStart();
|
||||
if (LOG.isLoggable(INFO))
|
||||
LOG.info("Starting " + this.getClass().getSimpleName());
|
||||
for (ActivityLifecycleController alc : lifecycleControllers) {
|
||||
alc.onActivityStart();
|
||||
}
|
||||
@@ -137,6 +144,8 @@ public abstract class BaseActivity extends AppCompatActivity
|
||||
@Override
|
||||
protected void onStop() {
|
||||
super.onStop();
|
||||
if (LOG.isLoggable(INFO))
|
||||
LOG.info("Stopping " + this.getClass().getSimpleName());
|
||||
for (ActivityLifecycleController alc : lifecycleControllers) {
|
||||
alc.onActivityStop();
|
||||
}
|
||||
|
||||
@@ -9,9 +9,9 @@ public interface RequestCodes {
|
||||
int REQUEST_WRITE_BLOG_POST = 5;
|
||||
int REQUEST_SHARE_BLOG = 6;
|
||||
int REQUEST_RINGTONE = 7;
|
||||
int REQUEST_PERMISSION_CAMERA = 8;
|
||||
int REQUEST_PERMISSION_CAMERA_LOCATION = 8;
|
||||
int REQUEST_DOZE_WHITELISTING = 9;
|
||||
int REQUEST_ENABLE_BLUETOOTH = 10;
|
||||
int REQUEST_BLUETOOTH_DISCOVERABLE = 10;
|
||||
int REQUEST_UNLOCK = 11;
|
||||
int REQUEST_KEYGUARD_UNLOCK = 12;
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@ package org.briarproject.briar.android.keyagreement;
|
||||
import android.bluetooth.BluetoothAdapter;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface.OnClickListener;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.os.Bundle;
|
||||
@@ -11,19 +10,15 @@ import android.support.annotation.StringRes;
|
||||
import android.support.annotation.UiThread;
|
||||
import android.support.v4.app.ActivityCompat;
|
||||
import android.support.v4.app.FragmentManager;
|
||||
import android.support.v4.content.ContextCompat;
|
||||
import android.support.v7.app.AlertDialog.Builder;
|
||||
import android.support.v7.widget.Toolbar;
|
||||
import android.view.MenuItem;
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.briarproject.bramble.api.event.EventBus;
|
||||
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
||||
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
||||
import org.briarproject.bramble.api.plugin.event.BluetoothEnabledEvent;
|
||||
import org.briarproject.briar.R;
|
||||
import org.briarproject.briar.R.string;
|
||||
import org.briarproject.briar.R.style;
|
||||
import org.briarproject.briar.android.activity.ActivityComponent;
|
||||
import org.briarproject.briar.android.activity.BriarActivity;
|
||||
import org.briarproject.briar.android.fragment.BaseFragment;
|
||||
@@ -37,16 +32,19 @@ import java.util.logging.Logger;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static android.Manifest.permission.ACCESS_COARSE_LOCATION;
|
||||
import static android.Manifest.permission.CAMERA;
|
||||
import static android.bluetooth.BluetoothAdapter.ACTION_REQUEST_ENABLE;
|
||||
import static android.bluetooth.BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE;
|
||||
import static android.bluetooth.BluetoothAdapter.ACTION_SCAN_MODE_CHANGED;
|
||||
import static android.bluetooth.BluetoothAdapter.ACTION_STATE_CHANGED;
|
||||
import static android.bluetooth.BluetoothAdapter.EXTRA_SCAN_MODE;
|
||||
import static android.bluetooth.BluetoothAdapter.EXTRA_STATE;
|
||||
import static android.bluetooth.BluetoothAdapter.SCAN_MODE_CONNECTABLE;
|
||||
import static android.bluetooth.BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE;
|
||||
import static android.bluetooth.BluetoothAdapter.STATE_ON;
|
||||
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
|
||||
import static android.os.Build.VERSION.SDK_INT;
|
||||
import static android.widget.Toast.LENGTH_LONG;
|
||||
import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_ENABLE_BLUETOOTH;
|
||||
import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_PERMISSION_CAMERA;
|
||||
import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_BLUETOOTH_DISCOVERABLE;
|
||||
import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_PERMISSION_CAMERA_LOCATION;
|
||||
|
||||
@MethodsNotNullByDefault
|
||||
@ParametersNotNullByDefault
|
||||
@@ -55,7 +53,11 @@ public abstract class KeyAgreementActivity extends BriarActivity implements
|
||||
KeyAgreementEventListener {
|
||||
|
||||
private enum BluetoothState {
|
||||
UNKNOWN, NO_ADAPTER, WAITING, REFUSED, ENABLED
|
||||
UNKNOWN, NO_ADAPTER, WAITING, REFUSED, ENABLED, DISCOVERABLE
|
||||
}
|
||||
|
||||
private enum Permission {
|
||||
UNKNOWN, GRANTED, SHOW_RATIONALE, PERMANENTLY_DENIED
|
||||
}
|
||||
|
||||
private static final Logger LOG =
|
||||
@@ -64,8 +66,27 @@ public abstract class KeyAgreementActivity extends BriarActivity implements
|
||||
@Inject
|
||||
EventBus eventBus;
|
||||
|
||||
private boolean isResumed = false, enableWasRequested = false;
|
||||
private boolean continueClicked, gotCameraPermission;
|
||||
/**
|
||||
* Set to true in onPostResume() and false in onPause(). This prevents the
|
||||
* QR code fragment from being shown if onRequestPermissionsResult() is
|
||||
* called while the activity is paused, which could cause a crash due to
|
||||
* https://issuetracker.google.com/issues/37067655.
|
||||
*/
|
||||
private boolean isResumed = false;
|
||||
/**
|
||||
* Set to true when the continue button is clicked, and false when the QR
|
||||
* code fragment is shown. This prevents the QR code fragment from being
|
||||
* shown automatically before the continue button has been clicked.
|
||||
*/
|
||||
private boolean continueClicked = false;
|
||||
/**
|
||||
* Records whether the Bluetooth adapter was already enabled before we
|
||||
* asked for Bluetooth discoverability, so we know whether to broadcast a
|
||||
* {@link BluetoothEnabledEvent}.
|
||||
*/
|
||||
private boolean wasAdapterEnabled = false;
|
||||
private Permission cameraPermission = Permission.UNKNOWN;
|
||||
private Permission locationPermission = Permission.UNKNOWN;
|
||||
private BluetoothState bluetoothState = BluetoothState.UNKNOWN;
|
||||
private BroadcastReceiver bluetoothReceiver = null;
|
||||
|
||||
@@ -85,7 +106,9 @@ public abstract class KeyAgreementActivity extends BriarActivity implements
|
||||
if (state == null) {
|
||||
showInitialFragment(IntroFragment.newInstance());
|
||||
}
|
||||
IntentFilter filter = new IntentFilter(ACTION_STATE_CHANGED);
|
||||
IntentFilter filter = new IntentFilter();
|
||||
filter.addAction(ACTION_STATE_CHANGED);
|
||||
filter.addAction(ACTION_SCAN_MODE_CHANGED);
|
||||
bluetoothReceiver = new BluetoothStateReceiver();
|
||||
registerReceiver(bluetoothReceiver, filter);
|
||||
}
|
||||
@@ -107,20 +130,40 @@ public abstract class KeyAgreementActivity extends BriarActivity implements
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
// Permissions may have been granted manually while we were stopped
|
||||
cameraPermission = Permission.UNKNOWN;
|
||||
locationPermission = Permission.UNKNOWN;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostResume() {
|
||||
super.onPostResume();
|
||||
isResumed = true;
|
||||
// Workaround for
|
||||
// https://code.google.com/p/android/issues/detail?id=190966
|
||||
if (canShowQrCodeFragment()) showQrCodeFragment();
|
||||
showQrCodeFragmentIfAllowed();
|
||||
}
|
||||
|
||||
private boolean canShowQrCodeFragment() {
|
||||
return isResumed && continueClicked
|
||||
&& (SDK_INT < 23 || gotCameraPermission)
|
||||
&& bluetoothState != BluetoothState.UNKNOWN
|
||||
&& bluetoothState != BluetoothState.WAITING;
|
||||
private void showQrCodeFragmentIfAllowed() {
|
||||
if (isResumed && continueClicked && areEssentialPermissionsGranted()) {
|
||||
if (bluetoothState == BluetoothState.UNKNOWN ||
|
||||
bluetoothState == BluetoothState.ENABLED) {
|
||||
requestBluetoothDiscoverable();
|
||||
} else if (bluetoothState != BluetoothState.WAITING) {
|
||||
showQrCodeFragment();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean areEssentialPermissionsGranted() {
|
||||
// If the camera permission has been granted, and the location
|
||||
// permission has been granted or permanently denied, we can continue
|
||||
return cameraPermission == Permission.GRANTED &&
|
||||
(locationPermission == Permission.GRANTED ||
|
||||
locationPermission == Permission.PERMANENTLY_DENIED);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -132,50 +175,54 @@ public abstract class KeyAgreementActivity extends BriarActivity implements
|
||||
@Override
|
||||
public void showNextScreen() {
|
||||
continueClicked = true;
|
||||
if (checkPermissions()) {
|
||||
if (shouldRequestEnableBluetooth()) requestEnableBluetooth();
|
||||
else if (canShowQrCodeFragment()) showQrCodeFragment();
|
||||
}
|
||||
if (checkPermissions()) showQrCodeFragmentIfAllowed();
|
||||
}
|
||||
|
||||
private boolean shouldRequestEnableBluetooth() {
|
||||
return bluetoothState == BluetoothState.UNKNOWN
|
||||
|| bluetoothState == BluetoothState.REFUSED;
|
||||
}
|
||||
|
||||
private void requestEnableBluetooth() {
|
||||
private void requestBluetoothDiscoverable() {
|
||||
BluetoothAdapter bt = BluetoothAdapter.getDefaultAdapter();
|
||||
if (bt == null) {
|
||||
setBluetoothState(BluetoothState.NO_ADAPTER);
|
||||
} else if (bt.isEnabled()) {
|
||||
setBluetoothState(BluetoothState.ENABLED);
|
||||
} else {
|
||||
enableWasRequested = true;
|
||||
setBluetoothState(BluetoothState.WAITING);
|
||||
Intent i = new Intent(ACTION_REQUEST_ENABLE);
|
||||
startActivityForResult(i, REQUEST_ENABLE_BLUETOOTH);
|
||||
wasAdapterEnabled = bt.isEnabled();
|
||||
Intent i = new Intent(ACTION_REQUEST_DISCOVERABLE);
|
||||
startActivityForResult(i, REQUEST_BLUETOOTH_DISCOVERABLE);
|
||||
}
|
||||
}
|
||||
|
||||
private void setBluetoothState(BluetoothState bluetoothState) {
|
||||
LOG.info("Setting Bluetooth state to " + bluetoothState);
|
||||
this.bluetoothState = bluetoothState;
|
||||
if (enableWasRequested && bluetoothState == BluetoothState.ENABLED) {
|
||||
if (!wasAdapterEnabled && bluetoothState == BluetoothState.ENABLED) {
|
||||
eventBus.broadcast(new BluetoothEnabledEvent());
|
||||
enableWasRequested = false;
|
||||
wasAdapterEnabled = true;
|
||||
}
|
||||
if (canShowQrCodeFragment()) showQrCodeFragment();
|
||||
showQrCodeFragmentIfAllowed();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityResult(int request, int result, Intent data) {
|
||||
// If the request was granted we'll catch the state change event
|
||||
if (request == REQUEST_ENABLE_BLUETOOTH && result == RESULT_CANCELED)
|
||||
setBluetoothState(BluetoothState.REFUSED);
|
||||
if (request == REQUEST_BLUETOOTH_DISCOVERABLE) {
|
||||
if (result == RESULT_CANCELED) {
|
||||
setBluetoothState(BluetoothState.REFUSED);
|
||||
} else {
|
||||
// If Bluetooth is already discoverable, show the QR code -
|
||||
// otherwise wait for the state or scan mode to change
|
||||
BluetoothAdapter bt = BluetoothAdapter.getDefaultAdapter();
|
||||
if (bt == null) throw new AssertionError();
|
||||
if (bt.getScanMode() == SCAN_MODE_CONNECTABLE_DISCOVERABLE)
|
||||
setBluetoothState(BluetoothState.DISCOVERABLE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void showQrCodeFragment() {
|
||||
// If we return to the intro fragment, the continue button needs to be
|
||||
// clicked again before showing the QR code fragment
|
||||
continueClicked = false;
|
||||
// If we return to the intro fragment, ask for Bluetooth
|
||||
// discoverability again before showing the QR code fragment
|
||||
bluetoothState = BluetoothState.UNKNOWN;
|
||||
// FIXME #824
|
||||
FragmentManager fm = getSupportFragmentManager();
|
||||
if (fm.findFragmentByTag(KeyAgreementFragment.TAG) == null) {
|
||||
@@ -194,74 +241,113 @@ public abstract class KeyAgreementActivity extends BriarActivity implements
|
||||
}
|
||||
|
||||
private boolean checkPermissions() {
|
||||
if (ContextCompat.checkSelfPermission(this, CAMERA) !=
|
||||
PERMISSION_GRANTED) {
|
||||
// Should we show an explanation?
|
||||
if (ActivityCompat.shouldShowRequestPermissionRationale(this,
|
||||
CAMERA)) {
|
||||
OnClickListener continueListener =
|
||||
(dialog, which) -> requestPermission();
|
||||
Builder builder = new Builder(this, style.BriarDialogTheme);
|
||||
builder.setTitle(string.permission_camera_title);
|
||||
builder.setMessage(string.permission_camera_request_body);
|
||||
builder.setNeutralButton(string.continue_button,
|
||||
continueListener);
|
||||
builder.show();
|
||||
} else {
|
||||
requestPermission();
|
||||
}
|
||||
gotCameraPermission = false;
|
||||
if (areEssentialPermissionsGranted()) return true;
|
||||
// If the camera permission has been permanently denied, ask the
|
||||
// user to change the setting
|
||||
if (cameraPermission == Permission.PERMANENTLY_DENIED) {
|
||||
Builder builder = new Builder(this, R.style.BriarDialogTheme);
|
||||
builder.setTitle(R.string.permission_camera_title);
|
||||
builder.setMessage(R.string.permission_camera_denied_body);
|
||||
builder.setPositiveButton(R.string.ok,
|
||||
UiUtils.getGoToSettingsListener(this));
|
||||
builder.setNegativeButton(R.string.cancel,
|
||||
(dialog, which) -> supportFinishAfterTransition());
|
||||
builder.show();
|
||||
return false;
|
||||
} else {
|
||||
gotCameraPermission = true;
|
||||
return true;
|
||||
}
|
||||
// Should we show the rationale for one or both permissions?
|
||||
if (cameraPermission == Permission.SHOW_RATIONALE &&
|
||||
locationPermission == Permission.SHOW_RATIONALE) {
|
||||
showRationale(R.string.permission_camera_location_title,
|
||||
R.string.permission_camera_location_request_body);
|
||||
} else if (cameraPermission == Permission.SHOW_RATIONALE) {
|
||||
showRationale(R.string.permission_camera_title,
|
||||
R.string.permission_camera_request_body);
|
||||
} else if (locationPermission == Permission.SHOW_RATIONALE) {
|
||||
showRationale(R.string.permission_location_title,
|
||||
R.string.permission_location_request_body);
|
||||
} else {
|
||||
requestPermissions();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void requestPermission() {
|
||||
ActivityCompat.requestPermissions(this, new String[] {CAMERA},
|
||||
REQUEST_PERMISSION_CAMERA);
|
||||
private void showRationale(@StringRes int title, @StringRes int body) {
|
||||
Builder builder = new Builder(this, R.style.BriarDialogTheme);
|
||||
builder.setTitle(title);
|
||||
builder.setMessage(body);
|
||||
builder.setNeutralButton(R.string.continue_button,
|
||||
(dialog, which) -> requestPermissions());
|
||||
builder.show();
|
||||
}
|
||||
|
||||
private void requestPermissions() {
|
||||
ActivityCompat.requestPermissions(this,
|
||||
new String[] {CAMERA, ACCESS_COARSE_LOCATION},
|
||||
REQUEST_PERMISSION_CAMERA_LOCATION);
|
||||
}
|
||||
|
||||
@Override
|
||||
@UiThread
|
||||
public void onRequestPermissionsResult(int requestCode,
|
||||
String permissions[], int[] grantResults) {
|
||||
if (requestCode == REQUEST_PERMISSION_CAMERA) {
|
||||
// If request is cancelled, the result arrays are empty.
|
||||
if (grantResults.length > 0 &&
|
||||
grantResults[0] == PERMISSION_GRANTED) {
|
||||
gotCameraPermission = true;
|
||||
showNextScreen();
|
||||
} else {
|
||||
if (!ActivityCompat.shouldShowRequestPermissionRationale(this,
|
||||
CAMERA)) {
|
||||
// The user has permanently denied the request
|
||||
OnClickListener cancelListener =
|
||||
(dialog, which) -> supportFinishAfterTransition();
|
||||
Builder builder = new Builder(this, style.BriarDialogTheme);
|
||||
builder.setTitle(string.permission_camera_title);
|
||||
builder.setMessage(string.permission_camera_denied_body);
|
||||
builder.setPositiveButton(string.ok,
|
||||
UiUtils.getGoToSettingsListener(this));
|
||||
builder.setNegativeButton(string.cancel, cancelListener);
|
||||
builder.show();
|
||||
} else {
|
||||
Toast.makeText(this, string.permission_camera_denied_toast,
|
||||
LENGTH_LONG).show();
|
||||
supportFinishAfterTransition();
|
||||
}
|
||||
}
|
||||
String[] permissions, int[] grantResults) {
|
||||
if (requestCode != REQUEST_PERMISSION_CAMERA_LOCATION)
|
||||
throw new AssertionError();
|
||||
if (gotPermission(CAMERA, permissions, grantResults)) {
|
||||
cameraPermission = Permission.GRANTED;
|
||||
} else if (shouldShowRationale(CAMERA)) {
|
||||
cameraPermission = Permission.SHOW_RATIONALE;
|
||||
} else {
|
||||
cameraPermission = Permission.PERMANENTLY_DENIED;
|
||||
}
|
||||
if (gotPermission(ACCESS_COARSE_LOCATION, permissions, grantResults)) {
|
||||
locationPermission = Permission.GRANTED;
|
||||
} else if (shouldShowRationale(ACCESS_COARSE_LOCATION)) {
|
||||
locationPermission = Permission.SHOW_RATIONALE;
|
||||
} else {
|
||||
locationPermission = Permission.PERMANENTLY_DENIED;
|
||||
}
|
||||
// If a permission dialog has been shown, showing the QR code fragment
|
||||
// on this call path would cause a crash due to
|
||||
// https://code.google.com/p/android/issues/detail?id=190966.
|
||||
// In that case the isResumed flag prevents the fragment from being
|
||||
// shown here, and showQrCodeFragmentIfAllowed() will be called again
|
||||
// from onPostResume().
|
||||
if (checkPermissions()) showQrCodeFragmentIfAllowed();
|
||||
}
|
||||
|
||||
private boolean gotPermission(String permission, String[] permissions,
|
||||
int[] grantResults) {
|
||||
for (int i = 0; i < permissions.length; i++) {
|
||||
if (permission.equals(permissions[i]))
|
||||
return grantResults[i] == PERMISSION_GRANTED;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean shouldShowRationale(String permission) {
|
||||
return ActivityCompat.shouldShowRequestPermissionRationale(this,
|
||||
permission);
|
||||
}
|
||||
|
||||
private class BluetoothStateReceiver extends BroadcastReceiver {
|
||||
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
int state = intent.getIntExtra(EXTRA_STATE, 0);
|
||||
if (state == STATE_ON) setBluetoothState(BluetoothState.ENABLED);
|
||||
else setBluetoothState(BluetoothState.UNKNOWN);
|
||||
String action = intent.getAction();
|
||||
if (ACTION_STATE_CHANGED.equals(action)) {
|
||||
int state = intent.getIntExtra(EXTRA_STATE, 0);
|
||||
if (state == STATE_ON)
|
||||
setBluetoothState(BluetoothState.ENABLED);
|
||||
else setBluetoothState(BluetoothState.UNKNOWN);
|
||||
} else if (ACTION_SCAN_MODE_CHANGED.equals(action)) {
|
||||
int scanMode = intent.getIntExtra(EXTRA_SCAN_MODE, 0);
|
||||
if (scanMode == SCAN_MODE_CONNECTABLE_DISCOVERABLE)
|
||||
setBluetoothState(BluetoothState.DISCOVERABLE);
|
||||
else if (scanMode == SCAN_MODE_CONNECTABLE)
|
||||
setBluetoothState(BluetoothState.ENABLED);
|
||||
else setBluetoothState(BluetoothState.UNKNOWN);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,6 +44,7 @@ import org.briarproject.briar.android.navdrawer.NavDrawerActivity;
|
||||
import org.briarproject.briar.android.util.UiUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.logging.Logger;
|
||||
@@ -210,6 +211,13 @@ public class SettingsFragment extends PreferenceFragmentCompat
|
||||
return true;
|
||||
});
|
||||
|
||||
if (SDK_INT < 27) {
|
||||
// remove System Default Theme option
|
||||
List<CharSequence> entries =
|
||||
new ArrayList<>(Arrays.asList(theme.getEntries()));
|
||||
entries.remove(getString(R.string.pref_theme_system));
|
||||
theme.setEntries(entries.toArray(new CharSequence[0]));
|
||||
}
|
||||
if (IS_DEBUG_BUILD) {
|
||||
findPreference("pref_key_explode").setOnPreferenceClickListener(
|
||||
preference -> {
|
||||
|
||||
@@ -20,17 +20,12 @@ import android.widget.TextView;
|
||||
|
||||
import org.briarproject.briar.R;
|
||||
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import static org.briarproject.briar.android.util.UiUtils.MIN_DATE_RESOLUTION;
|
||||
|
||||
public class BriarRecyclerView extends FrameLayout {
|
||||
|
||||
private static final Logger LOG =
|
||||
Logger.getLogger(BriarRecyclerView.class.getName());
|
||||
|
||||
private final Handler handler = new Handler(Looper.getMainLooper());
|
||||
|
||||
private RecyclerView recyclerView;
|
||||
@@ -39,6 +34,7 @@ public class BriarRecyclerView extends FrameLayout {
|
||||
private TextView emptyText, emptyAction;
|
||||
private ProgressBar progressBar;
|
||||
private RecyclerView.AdapterDataObserver emptyObserver;
|
||||
@Nullable
|
||||
private Runnable refresher = null;
|
||||
private boolean isScrollingToEnd = false;
|
||||
|
||||
@@ -217,18 +213,15 @@ public class BriarRecyclerView extends FrameLayout {
|
||||
throw new IllegalStateException("Need to call setAdapter() first!");
|
||||
}
|
||||
refresher = () -> {
|
||||
LOG.info("Updating Content...");
|
||||
Adapter adapter = recyclerView.getAdapter();
|
||||
adapter.notifyItemRangeChanged(0, adapter.getItemCount());
|
||||
handler.postDelayed(refresher, MIN_DATE_RESOLUTION);
|
||||
};
|
||||
LOG.info("Adding Handler Callback");
|
||||
handler.postDelayed(refresher, MIN_DATE_RESOLUTION);
|
||||
}
|
||||
|
||||
public void stopPeriodicUpdate() {
|
||||
if (refresher != null) {
|
||||
LOG.info("Removing Handler Callback");
|
||||
handler.removeCallbacks(refresher);
|
||||
refresher = null;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportHeight="24.0"
|
||||
android:viewportWidth="24.0">
|
||||
<!-- Workaround for toolbar icons failing to apply proper layout direction -->
|
||||
<group
|
||||
android:pivotX="12"
|
||||
android:pivotY="12"
|
||||
android:rotation="180">
|
||||
<path
|
||||
android:fillColor="#FFFFFFFF"
|
||||
android:pathData="M2.01,21L23,12 2.01,3 2,10l15,2 -15,2z"/>
|
||||
</group>
|
||||
</vector>
|
||||
@@ -1,7 +1,6 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:autoMirrored="true"
|
||||
android:viewportHeight="24.0"
|
||||
android:viewportWidth="24.0">
|
||||
<path
|
||||
|
||||
@@ -471,7 +471,6 @@
|
||||
<string name="permission_camera_title">إذن الكاميرا</string>
|
||||
<string name="permission_camera_request_body">للتمكن من مسح رمز QR، يحتاج Briar (براير) إلى إستعمال الكاميرا.</string>
|
||||
<string name="permission_camera_denied_body">قد رفضت إعطاء إذن الكاميرا، لكن إضافة جهات إتصال يتطلب إستعمال الكاميرا.\n\nالرجاء منح الإذن.</string>
|
||||
<string name="permission_camera_denied_toast">لم يتم منح الإذن بإستعمال الكاميرا</string>
|
||||
<string name="qr_code">رمز QR</string>
|
||||
<string name="show_qr_code_fullscreen">اظهار رمز QR بوضع ملء الشاشة</string>
|
||||
<!--App Locking-->
|
||||
|
||||
@@ -430,7 +430,6 @@
|
||||
<string name="permission_camera_title">Permís de la càmera</string>
|
||||
<string name="permission_camera_request_body">Per escanejar el codi QR, Briar necessita accedir a la càmera.</string>
|
||||
<string name="permission_camera_denied_body">Heu denegat l\'accés a la càmera però per afegir contactes cal utilitzar la càmera.\n\nRecomanem que permeteu l\'accés a la càmera.</string>
|
||||
<string name="permission_camera_denied_toast">No s\'ha concedit el permís per accedir a la càmera</string>
|
||||
<string name="qr_code">Codi QR</string>
|
||||
<string name="show_qr_code_fullscreen">Mostra el codi QR a pantalla completa</string>
|
||||
<!--App Locking-->
|
||||
|
||||
@@ -403,7 +403,6 @@
|
||||
<string name="permission_camera_title">Oprávnění pro přístup k fotoaparátu</string>
|
||||
<string name="permission_camera_request_body">Pro scan QR kódu, Briar vyžaduje přístup k fotoaparátu.</string>
|
||||
<string name="permission_camera_denied_body">Odmítli jste udělit oprávnění přístupu k fotoaparátu, avšak pro přidání kontaktů je nutné použití fotoaparátu.\n\nZvažte prosím, opětovné udělení přístupu.</string>
|
||||
<string name="permission_camera_denied_toast">Oprávnění pro přístup k fotoaparátu nebylo uděleno</string>
|
||||
<string name="qr_code">QR kód</string>
|
||||
<string name="show_qr_code_fullscreen">Zobrazit QR kód na celou obrazovku</string>
|
||||
<!--App Locking-->
|
||||
|
||||
@@ -430,7 +430,6 @@
|
||||
<string name="permission_camera_title">Berechtigung Kamera</string>
|
||||
<string name="permission_camera_request_body">Um den QR-Code zu scannen, benötigt Briar Zugriff auf die Kamera.</string>
|
||||
<string name="permission_camera_denied_body">Du hast den Zugriff auf die Kamera verweigert, aber das Hinzufügen von Kontakten erfordert die Verwendung der Kamera.\n\nBitte gewähre den Zugriff.</string>
|
||||
<string name="permission_camera_denied_toast">Berechtigung für Kamera wurde nicht gewährt</string>
|
||||
<string name="qr_code">QR-Code</string>
|
||||
<string name="show_qr_code_fullscreen">QR-Code im Vollbildmodus anzeigen</string>
|
||||
<!--App Locking-->
|
||||
|
||||
@@ -48,6 +48,7 @@
|
||||
<string name="download_briar_button">Descargar Briar 1.0</string>
|
||||
<string name="startup_open_database">Descifrando la base de datos...</string>
|
||||
<string name="startup_migrate_database">Actualizando la base de datos...</string>
|
||||
<string name="startup_compact_database">Compactando base de datos...</string>
|
||||
<!--Navigation Drawer-->
|
||||
<string name="nav_drawer_open_description">Abre el panel de navegación</string>
|
||||
<string name="nav_drawer_close_description">Cierra el panel de navegación</string>
|
||||
@@ -435,7 +436,6 @@
|
||||
<string name="permission_camera_title">Permiso de cámara</string>
|
||||
<string name="permission_camera_request_body">Para escanear el código QR, Briar necesita acceso a la cámara.</string>
|
||||
<string name="permission_camera_denied_body">Has denegado el acceso a la cámara, pero para añadir contactos se requiere el uso de la cámara.\n\nPor favor considera la posibilidad de conceder el acceso.</string>
|
||||
<string name="permission_camera_denied_toast">No se concedió el permiso de cámara</string>
|
||||
<string name="qr_code">Código QR</string>
|
||||
<string name="show_qr_code_fullscreen">Mostrar código QR a pantalla completa</string>
|
||||
<!--App Locking-->
|
||||
|
||||
@@ -48,6 +48,7 @@
|
||||
<string name="download_briar_button">Deskargatu Briar 1.0</string>
|
||||
<string name="startup_open_database">Datu-basea deszifratzen...</string>
|
||||
<string name="startup_migrate_database">Datu-basea eguneratzen...</string>
|
||||
<string name="startup_compact_database">Datu-basea trinkotzen…</string>
|
||||
<!--Navigation Drawer-->
|
||||
<string name="nav_drawer_open_description">Ireki nabigazio-tiradera</string>
|
||||
<string name="nav_drawer_close_description">Itxi nabigazio-tiradera</string>
|
||||
@@ -435,7 +436,6 @@
|
||||
<string name="permission_camera_title">Kamera erabiltzeko baimena</string>
|
||||
<string name="permission_camera_request_body">QR kodea eskaneatzeko Briar-ek kamera atzitu behar du.</string>
|
||||
<string name="permission_camera_denied_body">Kamera atzitzeko baimena ukatu duzu, baina kontaktuak gehitzeko kamera erabili behar da.\n\nMesedez baimendu kamera atzitzea.</string>
|
||||
<string name="permission_camera_denied_toast">Ez da kamera erabiltzeko baimenik eman</string>
|
||||
<string name="qr_code">QR kodea</string>
|
||||
<string name="show_qr_code_fullscreen">Erakutsi QR kodea pantaila osoan</string>
|
||||
<!--App Locking-->
|
||||
|
||||
@@ -474,7 +474,6 @@
|
||||
<string name="permission_camera_denied_body">شما دسترسی به دوربین را رد کرده اید، اما افزودن مخاطب نیاز به دوربین دارد.
|
||||
|
||||
لطفا اجازه دسترسی را بدهید.</string>
|
||||
<string name="permission_camera_denied_toast">اجازه دسترسی به دوربین پذیرفته نشد</string>
|
||||
<string name="qr_code">کد QR</string>
|
||||
<string name="show_qr_code_fullscreen">نمایش کد QR به صورت فول اسکرین</string>
|
||||
<!--App Locking-->
|
||||
|
||||
@@ -430,7 +430,6 @@
|
||||
<string name="permission_camera_title">Kameran käyttöoikeus</string>
|
||||
<string name="permission_camera_request_body">Skannatakseen QR koodin, Briar tarvitsee luvan käyttää kameraa.</string>
|
||||
<string name="permission_camera_denied_body">Olet kieltänyt käyttämästä kameraa, mutta yhteyshenkilöiden lisääminen vaatii kameran käyttöä.\n\nOle hyvä ja harkitse kameraluvan myöntämistä.</string>
|
||||
<string name="permission_camera_denied_toast">Kameran käyttöoikeutta ei myönnetty</string>
|
||||
<string name="qr_code">QR-koodi</string>
|
||||
<string name="show_qr_code_fullscreen">Näytä QR-koodi koko näytöllä</string>
|
||||
<!--App Locking-->
|
||||
|
||||
@@ -132,7 +132,7 @@
|
||||
<string name="qr_code_invalid">Le code QR est invalide</string>
|
||||
<string name="qr_code_too_old">Le code QR que vous avez lu provient d’une version plus ancienne de %s.\n\nVeuillez demander à votre contact de passer à la version la plus récente, puis ressayez.</string>
|
||||
<string name="qr_code_too_new">Le code QR que vous avez lu provient d’une version plus récente de %s.\n\nVeuillez passer à la version la plus récente, puis ressayez.</string>
|
||||
<string name="camera_error">Erreur de la caméra</string>
|
||||
<string name="camera_error">Erreur de l’appareil photo</string>
|
||||
<string name="connecting_to_device">Connexion à l’appareil\u2026</string>
|
||||
<string name="authenticating_with_device">Autentification avec l’appareil\u2026</string>
|
||||
<string name="connection_error_title">Impossible de se connecter à votre contact</string>
|
||||
@@ -433,10 +433,13 @@
|
||||
<string name="screen_filter_body">Une autre appli s’affiche par-dessus Briar. Pour protéger votre sécurité, Briar ne répondra pas au toucher si une autre appli s’affiche par-dessus.\n\nLes applis suivantes pourraient s’afficher par-dessus Briar : \n\n%1$s</string>
|
||||
<string name="screen_filter_allow">Permettre à ces applis de s’afficher par-dessus</string>
|
||||
<!--Permission Requests-->
|
||||
<string name="permission_camera_title">Accès à la caméra</string>
|
||||
<string name="permission_camera_request_body">Pour lire le code QR, Briar doit accéder à la caméra.</string>
|
||||
<string name="permission_camera_title">Accès à l’appareil photo</string>
|
||||
<string name="permission_camera_request_body">Afin de lire le code QR, Briar doit accéder à l’appareil photo.</string>
|
||||
<string name="permission_location_title">Autorisation d’accès à la position</string>
|
||||
<string name="permission_location_request_body">Afin de découvrir des périphériques Bluetooth, Briar doit accéder à votre position.\n\nBriar n’enregistre pas votre position et ne la partage avec personne.</string>
|
||||
<string name="permission_camera_location_title">Appareil photo et position</string>
|
||||
<string name="permission_camera_location_request_body">Afin de lire le code QR, Briar doit accéder à l’appareil photo.\n\nAfin de découvrir des périphériques Bluetooth, Briar doit accéder à votre position.\n\nBriar n’enregistre pas votre position et ne la partage avec personne.</string>
|
||||
<string name="permission_camera_denied_body">Vous avez refusé l’accès à la caméra, mais l’ajout de contacts exige l’utilisation de celle-ci.\n\nVeuillez envisager d’y accorder l’accès.</string>
|
||||
<string name="permission_camera_denied_toast">L’accès à la caméra n’a pas été accordé</string>
|
||||
<string name="qr_code">Code QR</string>
|
||||
<string name="show_qr_code_fullscreen">Afficher le code QR en plein écran</string>
|
||||
<!--App Locking-->
|
||||
|
||||
@@ -436,7 +436,6 @@
|
||||
<string name="permission_camera_title">Permiso da cámara</string>
|
||||
<string name="permission_camera_request_body">Para escanear códigos QR, Briar precisa acceso a cámara.</string>
|
||||
<string name="permission_camera_denied_body">Denegou o permiso de acceso a cámara, pero é necesario para engadir contactos.\n\nPor favor, considere conceder o permiso.</string>
|
||||
<string name="permission_camera_denied_toast">Non concedeu o acceso a cámara</string>
|
||||
<string name="qr_code">Código QR</string>
|
||||
<string name="show_qr_code_fullscreen">Amosar o código QR a pantalla completa</string>
|
||||
<!--App Locking-->
|
||||
|
||||
@@ -53,6 +53,7 @@
|
||||
<string name="download_briar_button">הורד את Briar 1.0</string>
|
||||
<string name="startup_open_database">מפענח מסד נתונים...</string>
|
||||
<string name="startup_migrate_database">משדרג מסד נתונים...</string>
|
||||
<string name="startup_compact_database">מכווץ מסד נתונים...</string>
|
||||
<!--Navigation Drawer-->
|
||||
<string name="nav_drawer_open_description">פתח את מגירת הניווט</string>
|
||||
<string name="nav_drawer_close_description">סגור את מגירת הניווט</string>
|
||||
@@ -317,6 +318,7 @@
|
||||
<string name="blogs_remove_blog_ok">הסר</string>
|
||||
<string name="blogs_blog_removed">בלוג הוסר</string>
|
||||
<string name="blogs_reblog_comment_hint">הוסף תגובה (רשותי)</string>
|
||||
<string name="blogs_reblog_button">פרסם מחדש</string>
|
||||
<!--Blog Sharing-->
|
||||
<string name="blogs_sharing_share">שתף בלוג</string>
|
||||
<string name="blogs_sharing_error">הייתה שגיאה בשיתוף בלוג זה.</string>
|
||||
@@ -461,11 +463,12 @@
|
||||
<string name="progress_title_logout">מתנתק מן Briar...</string>
|
||||
<!--Screen Filters & Tapjacking-->
|
||||
<string name="screen_filter_title">ציפוי מסך התגלה</string>
|
||||
<string name="screen_filter_body">יישום אחר מציירת מעל Briar. כדי להגן על אבטחתך, Briar לא יגיב לנגיעות כאשר יישום אחר מצייר מעל.\n\nהיישומים הבאים יכולים לצייר מעל:\n\n%1$s</string>
|
||||
<string name="screen_filter_allow">התר ליישומים אלו לצייר מעל</string>
|
||||
<!--Permission Requests-->
|
||||
<string name="permission_camera_title">הרשאת מצלמה</string>
|
||||
<string name="permission_camera_request_body">כדי לסרוק את קוד ה־QR, היישום Briar צריך גישה אל המצלמה.</string>
|
||||
<string name="permission_camera_denied_body">דחית גישה אל המצלמה, אבל הוספת אנשי קשר דורשת שימוש במצלמה.\n\nאנא שקול הענקת גישה.</string>
|
||||
<string name="permission_camera_denied_toast">הרשאת מצלמה לא הוענקה</string>
|
||||
<string name="qr_code">קוד QR</string>
|
||||
<string name="show_qr_code_fullscreen">הראה קוד QR במסך מלא</string>
|
||||
<!--App Locking-->
|
||||
|
||||
@@ -430,7 +430,6 @@
|
||||
<string name="permission_camera_title">कैमरा अनुमति</string>
|
||||
<string name="permission_camera_request_body">QR कोड को स्कैन करने के लिए, Briar को कैमरे तक पहुंच की आवश्यकता है।</string>
|
||||
<string name="permission_camera_denied_body">आपने कैमरे तक पहुंच से वंचित किया है, लेकिन संपर्क जोड़ने के लिए कैमरे का उपयोग करने की आवश्यकता है। \ N \ n कृपया पहुंच प्रदान करने पर विचार करें।</string>
|
||||
<string name="permission_camera_denied_toast">कैमरा अनुमति नहीं दी गई थी</string>
|
||||
<string name="qr_code">क्यूआर कोड</string>
|
||||
<string name="show_qr_code_fullscreen">क्यूआर कोड पूर्णस्क्रीन दिखाएं</string>
|
||||
<!--App Locking-->
|
||||
|
||||
@@ -433,7 +433,6 @@ Vigyázat: Ez végleg törli az identitásait, kapcsolatait és üzeneteit</stri
|
||||
<string name="permission_camera_title">Kamera jogosultságok</string>
|
||||
<string name="permission_camera_request_body">A QR kód olvasáshoz a Briar-nak szüksége van kamera hozzáférésre.</string>
|
||||
<string name="permission_camera_denied_body">Megtiltotta hozzáférést a kamerához, de a kapcsolatok hozzáadásához szükséges a kamera.\n\nKérjük gondolja meg a jog megadását.</string>
|
||||
<string name="permission_camera_denied_toast">A kamera jogosultság nincs megadva</string>
|
||||
<string name="qr_code">QR kód</string>
|
||||
<string name="show_qr_code_fullscreen">A QR kód teljes képernyősen</string>
|
||||
<!--App Locking-->
|
||||
|
||||
@@ -48,6 +48,7 @@
|
||||
<string name="download_briar_button">Scarica Briar 1.0</string>
|
||||
<string name="startup_open_database">Decrittazione database...</string>
|
||||
<string name="startup_migrate_database">Aggiornamento database...</string>
|
||||
<string name="startup_compact_database">Compattazione database…</string>
|
||||
<!--Navigation Drawer-->
|
||||
<string name="nav_drawer_open_description">Apri la barra di navigazione</string>
|
||||
<string name="nav_drawer_close_description">Chiudi la barra di navigazione</string>
|
||||
@@ -435,7 +436,6 @@
|
||||
<string name="permission_camera_title">Autorizzazione fotocamera</string>
|
||||
<string name="permission_camera_request_body">Per scansionare il codice QR, Briar deve accedere alla fotocamera.</string>
|
||||
<string name="permission_camera_denied_body">Hai negato l\'accesso alla fotocamera, ma questa serve per aggiungere i contatti.\n\nConsidera la possibilità di concedere l\'accesso.</string>
|
||||
<string name="permission_camera_denied_toast">Autorizzazione fotocamera non concessa</string>
|
||||
<string name="qr_code">Codice QR</string>
|
||||
<string name="show_qr_code_fullscreen">Mostra codice QR a tutto schermo</string>
|
||||
<!--App Locking-->
|
||||
|
||||
@@ -430,7 +430,6 @@
|
||||
<string name="permission_camera_title">Cameratoestemming</string>
|
||||
<string name="permission_camera_request_body">Om de QR-code te scannen moet Briar toegang hebben tot de camera.</string>
|
||||
<string name="permission_camera_denied_body">Je hebt toegang tot de camera niet vrijgegeven, terwijl het toevoegen van contacten de camera nodig heeft.\n\nOverweeg alsjeblieft toegang vrij te geven.</string>
|
||||
<string name="permission_camera_denied_toast">Cameratoegang niet vrijgegeven</string>
|
||||
<string name="qr_code">QR-code</string>
|
||||
<string name="show_qr_code_fullscreen">Toon QR-code op volledig scherm</string>
|
||||
<!--App Locking-->
|
||||
|
||||
@@ -440,7 +440,6 @@ contactes qu’avètz partejat aqueste flux quitaràn benlèu de recebre las mes
|
||||
<string name="permission_camera_title">Permission de la camèra</string>
|
||||
<string name="permission_camera_request_body">Per numerizar lo còdi QR cal que Briar aja l’accès a la camèra.</string>
|
||||
<string name="permission_camera_denied_body">Avètz regetat l’accès a la camèra, mas per ajustar de contacte cal utilizar la camèra.\n\n Mercés de li donar l’accès</string>
|
||||
<string name="permission_camera_denied_toast">La permission a la camèra es pas estada donada</string>
|
||||
<string name="qr_code">Còdi QR</string>
|
||||
<string name="show_qr_code_fullscreen">Mostrar lo còdi QR en ecran complèt</string>
|
||||
<!--App Locking-->
|
||||
|
||||
@@ -450,7 +450,6 @@
|
||||
<string name="permission_camera_title">Dostęp do aparatu</string>
|
||||
<string name="permission_camera_request_body">Aby zeskanować kod QR, Briar potrzebuje mieć dostęp do aparatu.</string>
|
||||
<string name="permission_camera_denied_body">Odmówiłeś dostępu do kamery, lecz dodawanie kontaktów tego wymaga.\n\nProszę udzielić dostępu.</string>
|
||||
<string name="permission_camera_denied_toast">Dostęp do aparatu nie został przyznany</string>
|
||||
<string name="qr_code">Kod QR</string>
|
||||
<string name="show_qr_code_fullscreen">Pokaż QR na pełnym ekranie</string>
|
||||
<!--App Locking-->
|
||||
|
||||
@@ -430,7 +430,6 @@
|
||||
<string name="permission_camera_title">Permissão da câmera</string>
|
||||
<string name="permission_camera_request_body">O Briar precisa acessar a câmera para pode escanear o código QR.</string>
|
||||
<string name="permission_camera_denied_body">Você negou acesso à câmera, mas para adicionar contatos você precisa da câmera.\n\nPor favor, pense em liberar o acesso a ela.</string>
|
||||
<string name="permission_camera_denied_toast">A permissão da câmera não foi concedida</string>
|
||||
<string name="qr_code">Código QR</string>
|
||||
<string name="show_qr_code_fullscreen">Mostrar o código QR na tela cheia</string>
|
||||
<!--App Locking-->
|
||||
|
||||
@@ -446,7 +446,6 @@
|
||||
<string name="permission_camera_title">Permisiune de acces la camera foto</string>
|
||||
<string name="permission_camera_request_body">Pentru a scana codul QR, Briar are nevoie să acceseze camera foto.</string>
|
||||
<string name="permission_camera_denied_body">Ați refuzat accesul la camera foto, dar pentru a adăuga contacte este necesară folosirea camerei foto.\n\nVă rugăm să luați în considerare acordarea accesului.</string>
|
||||
<string name="permission_camera_denied_toast">Permisiunea de acces la camera foto nu a fost acordată</string>
|
||||
<string name="qr_code">Cod QR</string>
|
||||
<string name="show_qr_code_fullscreen">Arată codul QR pe tot ecranul</string>
|
||||
<!--App Locking-->
|
||||
|
||||
@@ -452,7 +452,6 @@
|
||||
<string name="permission_camera_title">Доступ к камере</string>
|
||||
<string name="permission_camera_request_body">Для сканирования QR-кода Briar необходим доступ к камере.</string>
|
||||
<string name="permission_camera_denied_body">Вы отказали в доступе к камере. Для добавления контактов необходимо использовать камеру.\n\nРассмотрите возможность предоставления доступа.</string>
|
||||
<string name="permission_camera_denied_toast">Доступ к камере предоставлен не был</string>
|
||||
<string name="qr_code">QR-код</string>
|
||||
<string name="show_qr_code_fullscreen">Показать QR-код во весь экран</string>
|
||||
<!--App Locking-->
|
||||
|
||||
@@ -436,7 +436,6 @@
|
||||
<string name="permission_camera_title">Leje mbi kamerën</string>
|
||||
<string name="permission_camera_request_body">Që të skanojë kodin QR, Briar-i lypset të hyjë te kamera.</string>
|
||||
<string name="permission_camera_denied_body">Keni mohuar hyrjen në kamera, por shtimi i kontakteve lyp përdorimin e kamerës.\n\nJu lutemi, shihni mundësinë e akordimit të hyrjes.</string>
|
||||
<string name="permission_camera_denied_toast">S\’u akorduan leje mbi kamerën</string>
|
||||
<string name="qr_code">Kod QR</string>
|
||||
<string name="show_qr_code_fullscreen">Shfaqe kodin QR sa tërë ekrani</string>
|
||||
<!--App Locking-->
|
||||
|
||||
@@ -466,7 +466,6 @@ Slijedeće aplikacije mogu precrtavati preko:
|
||||
<string name="permission_camera_denied_body">Uskratili ste pristup kameri, ali dodavanje kontakata zahtijeva pristup kameri.
|
||||
|
||||
Molim vas da uzmete u obzir odobravanja pristupa.</string>
|
||||
<string name="permission_camera_denied_toast">Dozvola kameri nije odobrena</string>
|
||||
<string name="qr_code">QR kod</string>
|
||||
<string name="show_qr_code_fullscreen">Prikazi QR kod na puni ekran</string>
|
||||
<!--App Locking-->
|
||||
|
||||
@@ -420,7 +420,6 @@
|
||||
<string name="permission_camera_title">相机权限</string>
|
||||
<string name="permission_camera_request_body">Briar 需要获得相机权限以扫描二维码。</string>
|
||||
<string name="permission_camera_denied_body">您已拒绝相机权限,而添加联系人需要使用相机。\n\n请考虑授予相机权限。</string>
|
||||
<string name="permission_camera_denied_toast">未授予相机权限</string>
|
||||
<string name="qr_code">二维码</string>
|
||||
<string name="show_qr_code_fullscreen">全屏显示二维码</string>
|
||||
<!--App Locking-->
|
||||
|
||||
@@ -63,10 +63,10 @@
|
||||
<item>@string/pref_theme_auto</item>
|
||||
<item>@string/pref_theme_system</item>
|
||||
</string-array>
|
||||
<string name="pref_theme_light_value">light</string>
|
||||
<string name="pref_theme_dark_value">dark</string>
|
||||
<string name="pref_theme_auto_value">auto</string>
|
||||
<string name="pref_theme_system_value">system</string>
|
||||
<string name="pref_theme_light_value" translatable="false">light</string>
|
||||
<string name="pref_theme_dark_value" translatable="false">dark</string>
|
||||
<string name="pref_theme_auto_value" translatable="false">auto</string>
|
||||
<string name="pref_theme_system_value" translatable="false">system</string>
|
||||
<string-array name="pref_theme_values">
|
||||
<item>@string/pref_theme_light_value</item>
|
||||
<item>@string/pref_theme_dark_value</item>
|
||||
@@ -82,8 +82,8 @@
|
||||
<item>@string/pref_lock_timeout_30</item>
|
||||
<item>@string/pref_lock_timeout_60</item>
|
||||
</string-array>
|
||||
<string name="pref_lock_timeout_value_default">5</string>
|
||||
<string name="pref_lock_timeout_value_never">-1</string>
|
||||
<string name="pref_lock_timeout_value_default" translatable="false">5</string>
|
||||
<string name="pref_lock_timeout_value_never" translatable="false">-1</string>
|
||||
<string-array name="pref_key_lock_timeout_values">
|
||||
<item>@string/pref_lock_timeout_value_never</item>
|
||||
<item>1</item>
|
||||
|
||||
@@ -468,8 +468,11 @@
|
||||
<!-- Permission Requests -->
|
||||
<string name="permission_camera_title">Camera permission</string>
|
||||
<string name="permission_camera_request_body">To scan the QR code, Briar needs access to the camera.</string>
|
||||
<string name="permission_location_title">Location permission</string>
|
||||
<string name="permission_location_request_body">To discover Bluetooth devices, Briar needs permission to access your location.\n\nBriar does not store your location or share it with anyone.</string>
|
||||
<string name="permission_camera_location_title">Camera and location</string>
|
||||
<string name="permission_camera_location_request_body">To scan the QR code, Briar needs access to the camera.\n\nTo discover Bluetooth devices, Briar needs permission to access your location.\n\nBriar does not store your location or share it with anyone.</string>
|
||||
<string name="permission_camera_denied_body">You have denied access to the camera, but adding contacts requires using the camera.\n\nPlease consider granting access.</string>
|
||||
<string name="permission_camera_denied_toast">Camera permission was not granted</string>
|
||||
<string name="qr_code">QR code</string>
|
||||
<string name="show_qr_code_fullscreen">Show QR code fullscreen</string>
|
||||
|
||||
|
||||
@@ -72,9 +72,16 @@ Returns a JSON array of contacts:
|
||||
The only workaround is to add a contact to the Briar app running on a rooted Android phone
|
||||
and then move its database (and key files) to the headless peer.
|
||||
|
||||
### Removing a contact
|
||||
|
||||
`DELETE /v1/contacts/{contactId}`
|
||||
|
||||
The `{contactId}` is the `contactId` of the contact (`1` in the example above).
|
||||
It returns with a status code `200`, if removal was successful.
|
||||
|
||||
### Listing all private messages
|
||||
|
||||
`GET /messages/{contactId}`
|
||||
`GET /v1/messages/{contactId}`
|
||||
|
||||
The `{contactId}` is the `contactId` of the contact (`1` in the example above).
|
||||
It returns a JSON array of private messages:
|
||||
@@ -100,7 +107,7 @@ Attention: There can messages of other `type`s where the message `text` is `null
|
||||
|
||||
### Writing a private message
|
||||
|
||||
`POST /messages/{contactId}`
|
||||
`POST /v1/messages/{contactId}`
|
||||
|
||||
The text of the message should be posted as JSON:
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
plugins {
|
||||
id 'java'
|
||||
id 'idea'
|
||||
id 'org.jetbrains.kotlin.jvm' version '1.2.70'
|
||||
id 'org.jetbrains.kotlin.kapt' version '1.2.70'
|
||||
id 'org.jetbrains.kotlin.jvm' version '1.2.71'
|
||||
id 'org.jetbrains.kotlin.kapt' version '1.2.71'
|
||||
id 'witness'
|
||||
}
|
||||
apply from: 'witness.gradle'
|
||||
@@ -14,9 +14,9 @@ dependencies {
|
||||
implementation project(path: ':briar-core', configuration: 'default')
|
||||
implementation project(path: ':bramble-java', configuration: 'default')
|
||||
|
||||
implementation 'io.javalin:javalin:2.2.0'
|
||||
implementation 'io.javalin:javalin:2.3.0'
|
||||
implementation 'org.slf4j:slf4j-simple:1.7.25'
|
||||
implementation 'com.fasterxml.jackson.core:jackson-databind:2.9.6'
|
||||
implementation 'com.fasterxml.jackson.core:jackson-databind:2.9.7'
|
||||
implementation 'com.github.ajalt:clikt:1.5.0'
|
||||
|
||||
kapt 'com.google.dagger:dagger-compiler:2.0.2'
|
||||
@@ -24,11 +24,11 @@ dependencies {
|
||||
testImplementation project(path: ':bramble-api', configuration: 'testOutput')
|
||||
testImplementation project(path: ':bramble-core', configuration: 'testOutput')
|
||||
|
||||
def junitVersion = '5.2.0'
|
||||
def junitVersion = '5.3.1'
|
||||
testImplementation "org.junit.jupiter:junit-jupiter-api:$junitVersion"
|
||||
testImplementation "org.junit.jupiter:junit-jupiter-params:$junitVersion"
|
||||
testRuntime "org.junit.jupiter:junit-jupiter-engine:$junitVersion"
|
||||
testImplementation "io.mockk:mockk:1.8.6"
|
||||
testImplementation "io.mockk:mockk:1.8.9"
|
||||
testImplementation "org.skyscreamer:jsonassert:1.5.0"
|
||||
}
|
||||
|
||||
|
||||
@@ -7,9 +7,11 @@ import io.javalin.Context
|
||||
import io.javalin.Javalin
|
||||
import io.javalin.JavalinEvent.SERVER_START_FAILED
|
||||
import io.javalin.JavalinEvent.SERVER_STOPPED
|
||||
import io.javalin.NotFoundResponse
|
||||
import io.javalin.apibuilder.ApiBuilder.*
|
||||
import io.javalin.core.util.ContextUtil
|
||||
import io.javalin.core.util.Header.AUTHORIZATION
|
||||
import org.briarproject.bramble.api.contact.ContactId
|
||||
import org.briarproject.briar.headless.blogs.BlogController
|
||||
import org.briarproject.briar.headless.contact.ContactController
|
||||
import org.briarproject.briar.headless.event.WebSocketController
|
||||
@@ -50,7 +52,6 @@ constructor(
|
||||
.event(SERVER_START_FAILED) {serverStopped() }
|
||||
.event(SERVER_STOPPED) { serverStopped() }
|
||||
if (debug) app.enableDebugLogging()
|
||||
app.start()
|
||||
|
||||
app.accessManager { handler, ctx, _ ->
|
||||
if (ctx.header(AUTHORIZATION) == "Bearer $authToken") {
|
||||
@@ -63,6 +64,9 @@ constructor(
|
||||
path("/v1") {
|
||||
path("/contacts") {
|
||||
get { ctx -> contactController.list(ctx) }
|
||||
path("/:contactId") {
|
||||
delete { ctx -> contactController.delete(ctx) }
|
||||
}
|
||||
}
|
||||
path("/messages/:contactId") {
|
||||
get { ctx -> messagingController.list(ctx) }
|
||||
@@ -97,6 +101,7 @@ constructor(
|
||||
webSocketController.sessions.remove(session)
|
||||
}
|
||||
}
|
||||
app.start()
|
||||
}
|
||||
|
||||
private fun serverStopped() {
|
||||
@@ -112,6 +117,21 @@ constructor(
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a [ContactId] from the "contactId" path parameter.
|
||||
*
|
||||
* @throws NotFoundResponse when contactId is not a number.
|
||||
*/
|
||||
fun Context.getContactIdFromPathParam(): ContactId {
|
||||
val contactString = pathParam("contactId")
|
||||
val contactInt = try {
|
||||
Integer.parseInt(contactString)
|
||||
} catch (e: NumberFormatException) {
|
||||
throw NotFoundResponse()
|
||||
}
|
||||
return ContactId(contactInt)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a String from the JSON field or throws [BadRequestResponse] if null or empty.
|
||||
*/
|
||||
|
||||
@@ -5,5 +5,6 @@ import io.javalin.Context
|
||||
interface ContactController {
|
||||
|
||||
fun list(ctx: Context): Context
|
||||
fun delete(ctx: Context): Context
|
||||
|
||||
}
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
package org.briarproject.briar.headless.contact
|
||||
|
||||
import io.javalin.Context
|
||||
import io.javalin.NotFoundResponse
|
||||
import org.briarproject.bramble.api.contact.ContactManager
|
||||
import org.briarproject.bramble.api.db.NoSuchContactException
|
||||
import org.briarproject.briar.headless.getContactIdFromPathParam
|
||||
import javax.annotation.concurrent.Immutable
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
@@ -19,4 +22,14 @@ constructor(private val contactManager: ContactManager) : ContactController {
|
||||
return ctx.json(contacts)
|
||||
}
|
||||
|
||||
override fun delete(ctx: Context): Context {
|
||||
val contactId = ctx.getContactIdFromPathParam()
|
||||
try {
|
||||
contactManager.removeContact(contactId)
|
||||
} catch (e: NoSuchContactException) {
|
||||
throw NotFoundResponse()
|
||||
}
|
||||
return ctx
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ import org.briarproject.briar.api.privategroup.invitation.GroupInvitationRequest
|
||||
import org.briarproject.briar.api.privategroup.invitation.GroupInvitationResponse
|
||||
import org.briarproject.briar.headless.event.WebSocketController
|
||||
import org.briarproject.briar.headless.event.output
|
||||
import org.briarproject.briar.headless.getContactIdFromPathParam
|
||||
import org.briarproject.briar.headless.getFromJson
|
||||
import org.briarproject.briar.headless.json.JsonDict
|
||||
import java.util.concurrent.Executor
|
||||
@@ -84,13 +85,7 @@ constructor(
|
||||
}
|
||||
|
||||
private fun getContact(ctx: Context): Contact {
|
||||
val contactString = ctx.pathParam("contactId")
|
||||
val contactInt = try {
|
||||
Integer.parseInt(contactString)
|
||||
} catch (e: NumberFormatException) {
|
||||
throw NotFoundResponse()
|
||||
}
|
||||
val contactId = ContactId(contactInt)
|
||||
val contactId = ctx.getContactIdFromPathParam()
|
||||
return try {
|
||||
contactManager.getContact(contactId)
|
||||
} catch (e: NoSuchContactException) {
|
||||
|
||||
@@ -1,10 +1,16 @@
|
||||
package org.briarproject.briar.headless.contact
|
||||
|
||||
import io.javalin.NotFoundResponse
|
||||
import io.javalin.json.JavalinJson.toJson
|
||||
import io.mockk.Runs
|
||||
import io.mockk.every
|
||||
import io.mockk.just
|
||||
import org.briarproject.bramble.api.contact.Contact
|
||||
import org.briarproject.bramble.api.contact.ContactId
|
||||
import org.briarproject.bramble.api.db.NoSuchContactException
|
||||
import org.briarproject.bramble.identity.output
|
||||
import org.briarproject.briar.headless.ControllerTest
|
||||
import org.junit.jupiter.api.Assertions.assertThrows
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
internal class ContactControllerTest : ControllerTest() {
|
||||
@@ -25,6 +31,30 @@ internal class ContactControllerTest : ControllerTest() {
|
||||
controller.list(ctx)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testDelete() {
|
||||
every { ctx.pathParam("contactId") } returns "1"
|
||||
every { contactManager.removeContact(ContactId(1)) } just Runs
|
||||
controller.delete(ctx)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testDeleteInvalidContactId() {
|
||||
every { ctx.pathParam("contactId") } returns "foo"
|
||||
assertThrows(NotFoundResponse::class.java) {
|
||||
controller.delete(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testDeleteNonexistentContactId() {
|
||||
every { ctx.pathParam("contactId") } returns "1"
|
||||
every { contactManager.removeContact(ContactId(1)) } throws NoSuchContactException()
|
||||
assertThrows(NotFoundResponse::class.java) {
|
||||
controller.delete(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testOutputContact() {
|
||||
val json = """
|
||||
|
||||
@@ -1,26 +1,26 @@
|
||||
dependencyVerification {
|
||||
verify = [
|
||||
'com.fasterxml.jackson.core:jackson-annotations:2.9.0:jackson-annotations-2.9.0.jar:45d32ac61ef8a744b464c54c2b3414be571016dd46bfc2bec226761cf7ae457a',
|
||||
'com.fasterxml.jackson.core:jackson-core:2.9.6:jackson-core-2.9.6.jar:fab8746aedd6427788ee390ea04d438ec141bff7eb3476f8bdd5d9110fb2718a',
|
||||
'com.fasterxml.jackson.core:jackson-databind:2.9.6:jackson-databind-2.9.6.jar:657e3e979446d61f88432b9c50f0ccd9c1fe4f1c822d533f5572e4c0d172a125',
|
||||
'com.fasterxml.jackson.core:jackson-core:2.9.7:jackson-core-2.9.7.jar:9e5bc0efabd9f0cac5c1fdd9ae35b16332ed22a0ee19a356de370a18a8cb6c84',
|
||||
'com.fasterxml.jackson.core:jackson-databind:2.9.7:jackson-databind-2.9.7.jar:675376decfc070b039d2be773a97002f1ee1e1346d95bd99feee0d56683a92bf',
|
||||
'com.github.ajalt:clikt:1.5.0:clikt-1.5.0.jar:f13ab614cb0372229f6bb1e19aa98ee6f4ac96f253d0e72d482ee4f5fd2a13a9',
|
||||
'com.google.dagger:dagger-compiler:2.0.2:dagger-compiler-2.0.2.jar:b74bc9de063dd4c6400b232231f2ef5056145b8fbecbf5382012007dd1c071b3',
|
||||
'com.google.dagger:dagger-producers:2.0-beta:dagger-producers-2.0-beta.jar:99ec15e8a0507ba569e7655bc1165ee5e5ca5aa914b3c8f7e2c2458f724edd6b',
|
||||
'com.google.dagger:dagger:2.0.2:dagger-2.0.2.jar:84c0282ed8be73a29e0475d639da030b55dee72369e58dd35ae7d4fe6243dcf9',
|
||||
'com.google.guava:guava:18.0:guava-18.0.jar:d664fbfc03d2e5ce9cab2a44fb01f1d0bf9dfebeccc1a473b1f9ea31f79f6f99',
|
||||
'com.vaadin.external.google:android-json:0.0.20131108.vaadin1:android-json-0.0.20131108.vaadin1.jar:dfb7bae2f404cfe0b72b4d23944698cb716b7665171812a0a4d0f5926c0fac79',
|
||||
'io.javalin:javalin:2.2.0:javalin-2.2.0.jar:f7298fa281400559e92f000477a631c75aca9e01776962fd4b392fdb3b714190',
|
||||
'io.mockk:mockk-agent-api:1.8.6:mockk-agent-api-1.8.6.jar:613512c66538e6349e03df641a868f4ee324f13e2e1dbd67a0ed388aa664a444',
|
||||
'io.mockk:mockk-agent-common:1.8.6:mockk-agent-common-1.8.6.jar:cb7cb26fae5bfd3c89090858548990f311b27f673b9efa9d0c94f97c463b2863',
|
||||
'io.mockk:mockk-agent-jvm:1.8.6:mockk-agent-jvm-1.8.6.jar:3f30b98d23ada8b5a44d75b43cd58fc03252fcb96939ff31e7ad659818af1e5d',
|
||||
'io.mockk:mockk-common:1.8.6:mockk-common-1.8.6.jar:a04b0e2fc7d583807cf89f3bbf5c7501808725e49e385d95486e1008d8ab2ba8',
|
||||
'io.mockk:mockk-dsl-jvm:1.8.6:mockk-dsl-jvm-1.8.6.jar:c2c5df747ff04d1a3e02212b7b43f9ba4233597ae278928598275d7a7bb26d73',
|
||||
'io.mockk:mockk-dsl:1.8.6:mockk-dsl-1.8.6.jar:f6014265fe88ef1290c936741bdd0a7c3d9ceba9ee3bd2a153d65b05e1fc7946',
|
||||
'io.mockk:mockk:1.8.6:mockk-1.8.6.jar:0a200d71bab11facfe50637b1980f53c07a21bfa4dd9eb021ac8e8cc693924b2',
|
||||
'io.javalin:javalin:2.3.0:javalin-2.3.0.jar:3571e83863e1f163854f1b2ee3cbfc1336fcbdfa595ec9c2ed8ab8bfa792e5f4',
|
||||
'io.mockk:mockk-agent-api:1.8.9:mockk-agent-api-1.8.9.jar:8cbc0adc0f349f891d44ffdaba55c68271790ce9c33cc437d2be86a5af572d6c',
|
||||
'io.mockk:mockk-agent-common:1.8.9:mockk-agent-common-1.8.9.jar:a67b8bb696f91023e4446e2c8594c652924a350422047c96f31031be2a8de54a',
|
||||
'io.mockk:mockk-agent-jvm:1.8.9:mockk-agent-jvm-1.8.9.jar:4a8b83501e0fda6f79601e073c9e419dc649eeb8d18645ae0d60c64f1e671076',
|
||||
'io.mockk:mockk-common:1.8.9:mockk-common-1.8.9.jar:a3fb4d3fa776dde4eab2cc49e5df68fa394064df47431e2add9fc2100f3a145f',
|
||||
'io.mockk:mockk-dsl-jvm:1.8.9:mockk-dsl-jvm-1.8.9.jar:d6cfda3ba94b3fba2826c4554a33866a5559df9326fb28b559078fcb4508e60d',
|
||||
'io.mockk:mockk-dsl:1.8.9:mockk-dsl-1.8.9.jar:e24f0de13e12227544a68dae13f4fae4dd376f5d1eac796d3f4594989c0f2e68',
|
||||
'io.mockk:mockk:1.8.9:mockk-1.8.9.jar:9ead145f12b086af1d15eb4bf0fa5ccee02781b69937be6f77f47ac03100f526',
|
||||
'javax.inject:javax.inject:1:javax.inject-1.jar:91c77044a50c481636c32d916fd89c9118a72195390452c81065080f957de7ff',
|
||||
'javax.servlet:javax.servlet-api:3.1.0:javax.servlet-api-3.1.0.jar:af456b2dd41c4e82cf54f3e743bc678973d9fe35bd4d3071fa05c7e5333b8482',
|
||||
'net.bytebuddy:byte-buddy-agent:1.8.8:byte-buddy-agent-1.8.8.jar:dc1a2dcefe72731fa89ae84e32231c74d545ccf8216c79865096e546f20c57e8',
|
||||
'net.bytebuddy:byte-buddy:1.8.8:byte-buddy-1.8.8.jar:30aed1ae2ee5261b1d2f0e98ec3fcb40755c3f61b378089fb65d56098df1f16b',
|
||||
'net.bytebuddy:byte-buddy-agent:1.8.22:byte-buddy-agent-1.8.22.jar:ebc20e83fbb13e7911e4c704c9548a4166d7e83922f80de700ae5c5c983943d5',
|
||||
'net.bytebuddy:byte-buddy:1.8.22:byte-buddy-1.8.22.jar:d330d2ef290a2852bbaf06eab03bc93d24501599c8a836da9d946f82c48e276c',
|
||||
'org.apiguardian:apiguardian-api:1.0.0:apiguardian-api-1.0.0.jar:1f58b77470d8d147a0538d515347dd322f49a83b9e884b8970051160464b65b3',
|
||||
'org.eclipse.jetty.websocket:websocket-api:9.4.12.v20180830:websocket-api-9.4.12.v20180830.jar:6f7ecb42601058ffe4a6c19c5340cac3ebf0f83e2e252b457558f104238278e3',
|
||||
'org.eclipse.jetty.websocket:websocket-client:9.4.12.v20180830:websocket-client-9.4.12.v20180830.jar:97c6882c858a75776773eaccc01739757c4e9f60a51613878c1f2b2ba03d91af',
|
||||
@@ -36,31 +36,30 @@ dependencyVerification {
|
||||
'org.eclipse.jetty:jetty-util:9.4.12.v20180830:jetty-util-9.4.12.v20180830.jar:60ad53e118a3e7d10418b155b9944d90b2e4e4c732e53ef4f419473288d3f48c',
|
||||
'org.eclipse.jetty:jetty-webapp:9.4.12.v20180830:jetty-webapp-9.4.12.v20180830.jar:5301e412a32bf7dddcfad458d952179597c61f8fd531c265873562725c3d4646',
|
||||
'org.eclipse.jetty:jetty-xml:9.4.12.v20180830:jetty-xml-9.4.12.v20180830.jar:5b8298ab3d43ddaf0941d41f51b82c8ae23a247da055fa161b752ab9495155ed',
|
||||
'org.jetbrains.kotlin:kotlin-android-extensions:1.2.70:kotlin-android-extensions-1.2.70.jar:534601f4b10bd175268a611ea370051f75db3377da11e9400430f01db7c70bd0',
|
||||
'org.jetbrains.kotlin:kotlin-annotation-processing-gradle:1.2.70:kotlin-annotation-processing-gradle-1.2.70.jar:820da7e3637066c14eb3d54dc29cd6d4dc4a041ff603d0b15844403de47b7d12',
|
||||
'org.jetbrains.kotlin:kotlin-build-common:1.2.70:kotlin-build-common-1.2.70.jar:6756d98108512b8a42013e453912e868777a20f05cac3f40af891e4058e94da6',
|
||||
'org.jetbrains.kotlin:kotlin-compiler-embeddable:1.2.70:kotlin-compiler-embeddable-1.2.70.jar:8958d6f6ce4e49a6cecaaa9a1711a6b03df793fe066a74d88cf4958f20b0f10d',
|
||||
'org.jetbrains.kotlin:kotlin-compiler-runner:1.2.70:kotlin-compiler-runner-1.2.70.jar:44654c5f86e2b36222cb8b09e5eeb0b252db1e83d258b27fb31c670c339e65e1',
|
||||
'org.jetbrains.kotlin:kotlin-daemon-client:1.2.70:kotlin-daemon-client-1.2.70.jar:92b73c45670324ae6b81ccaa123a38eddd53adfe3a2b58938421d4d17a53f9e3',
|
||||
'org.jetbrains.kotlin:kotlin-gradle-plugin-api:1.2.70:kotlin-gradle-plugin-api-1.2.70.jar:a60b71d564795461e023956d8b3dd03d606d51ad1eb79e7c002dbce62a24378c',
|
||||
'org.jetbrains.kotlin:kotlin-gradle-plugin-model:1.2.70:kotlin-gradle-plugin-model-1.2.70.jar:a7fdb4b35e7537ad417e6b19595a1df7d676e8e9fa5a8a96dbcf1a40b577493b',
|
||||
'org.jetbrains.kotlin:kotlin-gradle-plugin:1.2.70:kotlin-gradle-plugin-1.2.70.jar:b9a96b3d39215b7fdcc8013476e70f86b8010277dd3639e58b42e6ffde1f3cab',
|
||||
'org.jetbrains.kotlin:kotlin-reflect:1.2.41:kotlin-reflect-1.2.41.jar:1bab75771dfa2bb5949cd383ceaedf6f8d354fa0d677804fc5a39e320bab70d3',
|
||||
'org.jetbrains.kotlin:kotlin-reflect:1.2.70:kotlin-reflect-1.2.70.jar:89ef46a458c5ee58b8460d0d22b0bb484eea0589ccffd59d650ef38bcb60e806',
|
||||
'org.jetbrains.kotlin:kotlin-script-runtime:1.2.70:kotlin-script-runtime-1.2.70.jar:0124dfcf890e39250c3a4481cdd27f038d8321e93ce983730c4cbc10143eadc2',
|
||||
'org.jetbrains.kotlin:kotlin-scripting-compiler-embeddable:1.2.70:kotlin-scripting-compiler-embeddable-1.2.70.jar:cbd88e1cae3f8f2baeb7021d3b76323b30e82663e7b4222a214f33ee314b3653',
|
||||
'org.jetbrains.kotlin:kotlin-stdlib-common:1.2.70:kotlin-stdlib-common-1.2.70.jar:bb6898bef18e1de5d416d5135ca70dcac6645718c7d84bbddcfeb76ed1c199a1',
|
||||
'org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.2.70:kotlin-stdlib-jdk7-1.2.70.jar:b4ace315288134b52fddb58b4a92636faafb2ab5eb46bad97d3bce7623a8e213',
|
||||
'org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.2.70:kotlin-stdlib-jdk8-1.2.70.jar:88d0c29f4065b6ad34261fb4a04d39f9813051c6942428d718b649378d057ad1',
|
||||
'org.jetbrains.kotlin:kotlin-stdlib:1.2.70:kotlin-stdlib-1.2.70.jar:7d20d0a56dd0ea6176137759a6aad331bbfae67436b45e5f0a4d8dafb6985c81',
|
||||
'org.jetbrains.kotlin:kotlin-android-extensions:1.2.71:kotlin-android-extensions-1.2.71.jar:6c3f52bbb7c25ddb102cfbe1e91e3f1ee45805f842310fb92496668dbf1366de',
|
||||
'org.jetbrains.kotlin:kotlin-annotation-processing-gradle:1.2.71:kotlin-annotation-processing-gradle-1.2.71.jar:107070d5bc10530a6823907487609899ac4d8753f730b8abf1f158971d93802e',
|
||||
'org.jetbrains.kotlin:kotlin-build-common:1.2.71:kotlin-build-common-1.2.71.jar:f3c6874442409c5fac7142dc5a4f7a0148147896a6d7f9e0c3cb140690090cb1',
|
||||
'org.jetbrains.kotlin:kotlin-compiler-embeddable:1.2.71:kotlin-compiler-embeddable-1.2.71.jar:623e546310d3da89ed2bc0cf9f7fedd78fadd9cd65d2ff798fa894c14e527665',
|
||||
'org.jetbrains.kotlin:kotlin-compiler-runner:1.2.71:kotlin-compiler-runner-1.2.71.jar:7259a24562ba880f821bc7c9ae51f724d66e8ea00f6e3143d4427a161ae6e998',
|
||||
'org.jetbrains.kotlin:kotlin-daemon-client:1.2.71:kotlin-daemon-client-1.2.71.jar:2ad669e3c2cedd3a2406f3c2c4739dba537acf373d605c207ed5058630e66462',
|
||||
'org.jetbrains.kotlin:kotlin-gradle-plugin-api:1.2.71:kotlin-gradle-plugin-api-1.2.71.jar:a141e6b9740ebf88ce53d258e40cb59f54ee8e2fafdff4c4c10b0abdceb57cba',
|
||||
'org.jetbrains.kotlin:kotlin-gradle-plugin-model:1.2.71:kotlin-gradle-plugin-model-1.2.71.jar:7082e9858b3ead143bcb48383007751a776fd46e8ce4cec1a1d4a873db5e7ec1',
|
||||
'org.jetbrains.kotlin:kotlin-gradle-plugin:1.2.71:kotlin-gradle-plugin-1.2.71.jar:ab88dab9bd2a3c31c4e6aaa0eb4168d13f126e9076561bb42f5f3fe80c902a78',
|
||||
'org.jetbrains.kotlin:kotlin-reflect:1.2.71:kotlin-reflect-1.2.71.jar:1f3e10abd68d0b0816bddab7314f61269e01d8aa2ca1cbd120c12d3b4dc94b0f',
|
||||
'org.jetbrains.kotlin:kotlin-script-runtime:1.2.71:kotlin-script-runtime-1.2.71.jar:307d0a56734458a5e57e3ea788c15b22591912ba39f81b2cc8b0a090944012bb',
|
||||
'org.jetbrains.kotlin:kotlin-scripting-compiler-embeddable:1.2.71:kotlin-scripting-compiler-embeddable-1.2.71.jar:97a5d94241d13719ea756a908262a10c638db7a8caaa23b28537b1687f894f0c',
|
||||
'org.jetbrains.kotlin:kotlin-stdlib-common:1.2.71:kotlin-stdlib-common-1.2.71.jar:63999687ff2fce8a592dd180ffbbf8f1d21c26b4044c55cdc74ff3cf3b3cf328',
|
||||
'org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.2.71:kotlin-stdlib-jdk7-1.2.71.jar:b136bd61b240e07d4d92ce00d3bd1dbf584400a7bf5f220c2f3cd22446858082',
|
||||
'org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.2.71:kotlin-stdlib-jdk8-1.2.71.jar:ac3c8abf47790b64b4f7e2509a53f0c145e061ac1612a597520535d199946ea9',
|
||||
'org.jetbrains.kotlin:kotlin-stdlib:1.2.71:kotlin-stdlib-1.2.71.jar:4c895c270b87f5fec2a2796e1d89c15407ee821de961527c28588bb46afbc68b',
|
||||
'org.jetbrains:annotations:13.0:annotations-13.0.jar:ace2a10dc8e2d5fd34925ecac03e4988b2c0f851650c94b8cef49ba1bd111478',
|
||||
'org.junit.jupiter:junit-jupiter-api:5.2.0:junit-jupiter-api-5.2.0.jar:47f7d71b35dc331210b9ab219bbb00d54332981aa12eb5effe817de17e1ae7b3',
|
||||
'org.junit.jupiter:junit-jupiter-engine:5.2.0:junit-jupiter-engine-5.2.0.jar:8f994f4094790e246dc84de86a1ff4194ca85e8b13bedaca0207f727ebfbc813',
|
||||
'org.junit.jupiter:junit-jupiter-params:5.2.0:junit-jupiter-params-5.2.0.jar:34ce02519044ef68217002f640a83e267c4001ce53b68270218d49d00449a836',
|
||||
'org.junit.platform:junit-platform-commons:1.2.0:junit-platform-commons-1.2.0.jar:7771af2f797d1d0ccce9920eb3cd826fb8fd7659ccb4d8877e76d9412be72cc2',
|
||||
'org.junit.platform:junit-platform-engine:1.2.0:junit-platform-engine-1.2.0.jar:60b102e94ea01556fdc8c041950a05450edc188e3708f032a6bfb1a50ba0bc22',
|
||||
'org.junit.jupiter:junit-jupiter-api:5.3.1:junit-jupiter-api-5.3.1.jar:7923e21f030a9964d70a0e48007ca873280c66ddf0f0620b2d969852c23d5653',
|
||||
'org.junit.jupiter:junit-jupiter-engine:5.3.1:junit-jupiter-engine-5.3.1.jar:04f4354548a30827e126bdf6fcbe3640789ad8335a6f3f0762bf7f9f74e51fbf',
|
||||
'org.junit.jupiter:junit-jupiter-params:5.3.1:junit-jupiter-params-5.3.1.jar:72fe344712d4cd88dd0cb4bfa304322d512d2cb27173ed64cb5036a573d29f4c',
|
||||
'org.junit.platform:junit-platform-commons:1.3.1:junit-platform-commons-1.3.1.jar:457d8e1c0c80d1e320a792ec35e7c180694ba05182d7ecf7dabdb4e5a8a12fe2',
|
||||
'org.junit.platform:junit-platform-engine:1.3.1:junit-platform-engine-1.3.1.jar:303d0546c3e950cc3beaca00dfcbf2632d4eca530e4e446391bf193cbc2a71a3',
|
||||
'org.objenesis:objenesis:2.6:objenesis-2.6.jar:5e168368fbc250af3c79aa5fef0c3467a2d64e5a7bd74005f25d8399aeb0708d',
|
||||
'org.opentest4j:opentest4j:1.1.0:opentest4j-1.1.0.jar:65a5fd7380f53aac708bcee3091dbe2dba73a9a2e7645b66e70e0804fc36ee3b',
|
||||
'org.opentest4j:opentest4j:1.1.1:opentest4j-1.1.1.jar:f106351abd941110226745ed103c85863b3f04e9fa82ddea1084639ae0c5336c',
|
||||
'org.skyscreamer:jsonassert:1.5.0:jsonassert-1.5.0.jar:a310bc79c3f4744e2b2e993702fcebaf3696fec0063643ffdc6b49a8fb03ef39',
|
||||
'org.slf4j:slf4j-api:1.7.25:slf4j-api-1.7.25.jar:18c4a0095d5c1da6b817592e767bb23d29dd2f560ad74df75ff3961dbde25b79',
|
||||
'org.slf4j:slf4j-simple:1.7.25:slf4j-simple-1.7.25.jar:0966e86fffa5be52d3d9e7b89dd674d98a03eed0a454fbaf7c1bd9493bd9d874',
|
||||
|
||||
Reference in New Issue
Block a user