mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-12 02:39:05 +01:00
Compare commits
51 Commits
limit-in-m
...
beta-1.2.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
54893d2716 | ||
|
|
84657127b8 | ||
|
|
01a146ba71 | ||
|
|
a30e5b672e | ||
|
|
edb584dc3b | ||
|
|
12a8907c8b | ||
|
|
e0f381a973 | ||
|
|
61d3d133e8 | ||
|
|
0caa522f07 | ||
|
|
948212103c | ||
|
|
ce1a57c2b4 | ||
|
|
922a52bf83 | ||
|
|
8cbb38ee68 | ||
|
|
1c4cf7d771 | ||
|
|
090a1bd84e | ||
|
|
44f6f5d416 | ||
|
|
b88f012880 | ||
|
|
93f434e54b | ||
|
|
92f4a3a404 | ||
|
|
c017a813b0 | ||
|
|
6c6dbfd357 | ||
|
|
1f246637e2 | ||
|
|
1ac17cf859 | ||
|
|
0a3ff41feb | ||
|
|
9738dd2838 | ||
|
|
be0e21d39b | ||
|
|
6a2c2bed0f | ||
|
|
de9c6d4447 | ||
|
|
37a2d9f990 | ||
|
|
0e1fb406b5 | ||
|
|
b72e8fa490 | ||
|
|
f3157e5276 | ||
|
|
e2124ff3c9 | ||
|
|
66cc9d25e7 | ||
|
|
e9cdec95e0 | ||
|
|
63d3a78dda | ||
|
|
ccbe6d4bb8 | ||
|
|
54b852db70 | ||
|
|
8d55ea3f6f | ||
|
|
cf8241e79c | ||
|
|
61d3fe9055 | ||
|
|
bded1edb2b | ||
|
|
4d27828712 | ||
|
|
0f6f52c37a | ||
|
|
c1cf6f61b9 | ||
|
|
7c22016b81 | ||
|
|
31f42d44af | ||
|
|
a1cf485ecc | ||
|
|
b7d3cd7990 | ||
|
|
4122e0852a | ||
|
|
41411b0e2e |
14
.idea/codeStyles/Project.xml
generated
14
.idea/codeStyles/Project.xml
generated
@@ -28,6 +28,20 @@
|
|||||||
<option name="JD_ALIGN_EXCEPTION_COMMENTS" value="false" />
|
<option name="JD_ALIGN_EXCEPTION_COMMENTS" value="false" />
|
||||||
</JavaCodeStyleSettings>
|
</JavaCodeStyleSettings>
|
||||||
<JetCodeStyleSettings>
|
<JetCodeStyleSettings>
|
||||||
|
<option name="PACKAGES_TO_USE_STAR_IMPORTS">
|
||||||
|
<value />
|
||||||
|
</option>
|
||||||
|
<option name="PACKAGES_IMPORT_LAYOUT">
|
||||||
|
<value>
|
||||||
|
<package name="" alias="false" withSubpackages="true" />
|
||||||
|
<package name="java" alias="false" withSubpackages="true" />
|
||||||
|
<package name="javax" alias="false" withSubpackages="true" />
|
||||||
|
<package name="kotlin" alias="false" withSubpackages="true" />
|
||||||
|
<package name="" alias="true" withSubpackages="true" />
|
||||||
|
</value>
|
||||||
|
</option>
|
||||||
|
<option name="NAME_COUNT_TO_USE_STAR_IMPORT" value="2147483647" />
|
||||||
|
<option name="NAME_COUNT_TO_USE_STAR_IMPORT_FOR_MEMBERS" value="2147483647" />
|
||||||
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
|
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
|
||||||
</JetCodeStyleSettings>
|
</JetCodeStyleSettings>
|
||||||
<XML>
|
<XML>
|
||||||
|
|||||||
1
bramble-android/.gitignore
vendored
1
bramble-android/.gitignore
vendored
@@ -3,3 +3,4 @@ gen
|
|||||||
build
|
build
|
||||||
.settings
|
.settings
|
||||||
src/main/res/raw/*.zip
|
src/main/res/raw/*.zip
|
||||||
|
src/main/jniLibs
|
||||||
@@ -5,14 +5,14 @@ apply plugin: 'witness'
|
|||||||
apply from: 'witness.gradle'
|
apply from: 'witness.gradle'
|
||||||
|
|
||||||
android {
|
android {
|
||||||
compileSdkVersion 29
|
compileSdkVersion rootProject.ext.compileSdkVersion
|
||||||
buildToolsVersion '29.0.2'
|
buildToolsVersion rootProject.ext.buildToolsVersion
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
minSdkVersion 16
|
minSdkVersion rootProject.ext.minSdkVersion
|
||||||
targetSdkVersion 28
|
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||||
versionCode 10209
|
versionCode rootProject.ext.versionCode
|
||||||
versionName "1.2.9"
|
versionName rootProject.ext.versionName
|
||||||
consumerProguardFiles 'proguard-rules.txt'
|
consumerProguardFiles 'proguard-rules.txt'
|
||||||
|
|
||||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
@@ -53,10 +53,12 @@ dependencies {
|
|||||||
}
|
}
|
||||||
|
|
||||||
def torBinariesDir = 'src/main/res/raw'
|
def torBinariesDir = 'src/main/res/raw'
|
||||||
|
def torLibsDir = 'src/main/jniLibs'
|
||||||
|
|
||||||
task cleanTorBinaries {
|
task cleanTorBinaries {
|
||||||
doLast {
|
doLast {
|
||||||
delete fileTree(torBinariesDir) { include '*.zip' }
|
delete fileTree(torBinariesDir) { include '*.zip' }
|
||||||
|
delete fileTree(torLibsDir) { include '**/*.so' }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -67,8 +69,36 @@ task unpackTorBinaries {
|
|||||||
copy {
|
copy {
|
||||||
from configurations.tor.collect { zipTree(it) }
|
from configurations.tor.collect { zipTree(it) }
|
||||||
into torBinariesDir
|
into torBinariesDir
|
||||||
// TODO: Remove after next Tor upgrade, which won't include non-PIE binaries
|
include 'geoip.zip'
|
||||||
include 'geoip.zip', '*_pie.zip'
|
}
|
||||||
|
configurations.tor.each { outer ->
|
||||||
|
zipTree(outer).each { inner ->
|
||||||
|
if (inner.name.endsWith('_arm_pie.zip')) {
|
||||||
|
copy {
|
||||||
|
from zipTree(inner)
|
||||||
|
into torLibsDir
|
||||||
|
rename '(.*)', 'armeabi-v7a/lib$1.so'
|
||||||
|
}
|
||||||
|
} else if (inner.name.endsWith('_arm64_pie.zip')) {
|
||||||
|
copy {
|
||||||
|
from zipTree(inner)
|
||||||
|
into torLibsDir
|
||||||
|
rename '(.*)', 'arm64-v8a/lib$1.so'
|
||||||
|
}
|
||||||
|
} else if (inner.name.endsWith('_x86_pie.zip')) {
|
||||||
|
copy {
|
||||||
|
from zipTree(inner)
|
||||||
|
into torLibsDir
|
||||||
|
rename '(.*)', 'x86/lib$1.so'
|
||||||
|
}
|
||||||
|
} else if (inner.name.endsWith('_x86_64_pie.zip')) {
|
||||||
|
copy {
|
||||||
|
from zipTree(inner)
|
||||||
|
into torLibsDir
|
||||||
|
rename '(.*)', 'x86_64/lib$1.so'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
dependsOn cleanTorBinaries
|
dependsOn cleanTorBinaries
|
||||||
@@ -76,5 +106,6 @@ task unpackTorBinaries {
|
|||||||
|
|
||||||
tasks.withType(MergeResources) {
|
tasks.withType(MergeResources) {
|
||||||
inputs.dir torBinariesDir
|
inputs.dir torBinariesDir
|
||||||
|
inputs.dir torLibsDir
|
||||||
dependsOn unpackTorBinaries
|
dependsOn unpackTorBinaries
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -135,6 +135,7 @@ class AndroidBluetoothPlugin
|
|||||||
@Override
|
@Override
|
||||||
@Nullable
|
@Nullable
|
||||||
String getBluetoothAddress() {
|
String getBluetoothAddress() {
|
||||||
|
if (adapter == null) return null;
|
||||||
String address = AndroidUtils.getBluetoothAddress(app, adapter);
|
String address = AndroidUtils.getBluetoothAddress(app, adapter);
|
||||||
return address.isEmpty() ? null : address;
|
return address.isEmpty() ? null : address;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,19 +16,42 @@ import org.briarproject.bramble.api.system.AndroidWakeLockManager;
|
|||||||
import org.briarproject.bramble.api.system.Clock;
|
import org.briarproject.bramble.api.system.Clock;
|
||||||
import org.briarproject.bramble.api.system.LocationUtils;
|
import org.briarproject.bramble.api.system.LocationUtils;
|
||||||
import org.briarproject.bramble.api.system.ResourceProvider;
|
import org.briarproject.bramble.api.system.ResourceProvider;
|
||||||
|
import org.briarproject.bramble.util.AndroidUtils;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
import java.util.zip.ZipEntry;
|
||||||
|
import java.util.zip.ZipInputStream;
|
||||||
|
|
||||||
import javax.net.SocketFactory;
|
import javax.net.SocketFactory;
|
||||||
|
|
||||||
|
import static android.os.Build.VERSION.SDK_INT;
|
||||||
|
import static java.util.Arrays.asList;
|
||||||
|
import static java.util.logging.Level.INFO;
|
||||||
|
import static java.util.logging.Logger.getLogger;
|
||||||
|
|
||||||
@MethodsNotNullByDefault
|
@MethodsNotNullByDefault
|
||||||
@ParametersNotNullByDefault
|
@ParametersNotNullByDefault
|
||||||
class AndroidTorPlugin extends TorPlugin {
|
class AndroidTorPlugin extends TorPlugin {
|
||||||
|
|
||||||
|
private static final List<String> LIBRARY_ARCHITECTURES =
|
||||||
|
asList("armeabi-v7a", "arm64-v8a", "x86", "x86_64");
|
||||||
|
|
||||||
|
private static final String TOR_LIB_NAME = "libtor.so";
|
||||||
|
private static final String OBFS4_LIB_NAME = "libobfs4proxy.so";
|
||||||
|
|
||||||
|
private static final Logger LOG =
|
||||||
|
getLogger(AndroidTorPlugin.class.getName());
|
||||||
|
|
||||||
private final Application app;
|
private final Application app;
|
||||||
private final AndroidWakeLock wakeLock;
|
private final AndroidWakeLock wakeLock;
|
||||||
|
private final File torLib, obfs4Lib;
|
||||||
|
|
||||||
AndroidTorPlugin(Executor ioExecutor,
|
AndroidTorPlugin(Executor ioExecutor,
|
||||||
Executor wakefulIoExecutor,
|
Executor wakefulIoExecutor,
|
||||||
@@ -55,6 +78,9 @@ class AndroidTorPlugin extends TorPlugin {
|
|||||||
maxIdleTime, torDirectory);
|
maxIdleTime, torDirectory);
|
||||||
this.app = app;
|
this.app = app;
|
||||||
wakeLock = wakeLockManager.createWakeLock("TorPlugin");
|
wakeLock = wakeLockManager.createWakeLock("TorPlugin");
|
||||||
|
String nativeLibDir = app.getApplicationInfo().nativeLibraryDir;
|
||||||
|
torLib = new File(nativeLibDir, TOR_LIB_NAME);
|
||||||
|
obfs4Lib = new File(nativeLibDir, OBFS4_LIB_NAME);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -85,4 +111,112 @@ class AndroidTorPlugin extends TorPlugin {
|
|||||||
super.stop();
|
super.stop();
|
||||||
wakeLock.release();
|
wakeLock.release();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected File getTorExecutableFile() {
|
||||||
|
return torLib.exists() ? torLib : super.getTorExecutableFile();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected File getObfs4ExecutableFile() {
|
||||||
|
return obfs4Lib.exists() ? obfs4Lib : super.getObfs4ExecutableFile();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void installTorExecutable() throws IOException {
|
||||||
|
File extracted = super.getTorExecutableFile();
|
||||||
|
if (torLib.exists()) {
|
||||||
|
// If an older version left behind a Tor binary, delete it
|
||||||
|
if (extracted.exists()) {
|
||||||
|
if (extracted.delete()) LOG.info("Deleted Tor binary");
|
||||||
|
else LOG.info("Failed to delete Tor binary");
|
||||||
|
}
|
||||||
|
} else if (SDK_INT < 29) {
|
||||||
|
// The binary wasn't extracted at install time. Try to extract it
|
||||||
|
extractLibraryFromApk(TOR_LIB_NAME, extracted);
|
||||||
|
} else {
|
||||||
|
// No point extracting the binary, we won't be allowed to execute it
|
||||||
|
throw new FileNotFoundException(torLib.getAbsolutePath());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void installObfs4Executable() throws IOException {
|
||||||
|
File extracted = super.getObfs4ExecutableFile();
|
||||||
|
if (obfs4Lib.exists()) {
|
||||||
|
// If an older version left behind an obfs4 binary, delete it
|
||||||
|
if (extracted.exists()) {
|
||||||
|
if (extracted.delete()) LOG.info("Deleted obfs4 binary");
|
||||||
|
else LOG.info("Failed to delete obfs4 binary");
|
||||||
|
}
|
||||||
|
} else if (SDK_INT < 29) {
|
||||||
|
// The binary wasn't extracted at install time. Try to extract it
|
||||||
|
extractLibraryFromApk(OBFS4_LIB_NAME, extracted);
|
||||||
|
} else {
|
||||||
|
// No point extracting the binary, we won't be allowed to execute it
|
||||||
|
throw new FileNotFoundException(obfs4Lib.getAbsolutePath());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void extractLibraryFromApk(String libName, File dest)
|
||||||
|
throws IOException {
|
||||||
|
File sourceDir = new File(app.getApplicationInfo().sourceDir);
|
||||||
|
if (sourceDir.isFile()) {
|
||||||
|
// Look for other APK files in the same directory, if we're allowed
|
||||||
|
File parent = sourceDir.getParentFile();
|
||||||
|
if (parent != null) sourceDir = parent;
|
||||||
|
}
|
||||||
|
List<String> libPaths = getSupportedLibraryPaths(libName);
|
||||||
|
for (File apk : findApkFiles(sourceDir)) {
|
||||||
|
ZipInputStream zin = new ZipInputStream(new FileInputStream(apk));
|
||||||
|
for (ZipEntry e = zin.getNextEntry(); e != null;
|
||||||
|
e = zin.getNextEntry()) {
|
||||||
|
if (libPaths.contains(e.getName())) {
|
||||||
|
if (LOG.isLoggable(INFO)) {
|
||||||
|
LOG.info("Extracting " + e.getName()
|
||||||
|
+ " from " + apk.getAbsolutePath());
|
||||||
|
}
|
||||||
|
extract(zin, dest); // Zip input stream will be closed
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
zin.close();
|
||||||
|
}
|
||||||
|
throw new FileNotFoundException(libName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns all files with the extension .apk or .APK under the given root.
|
||||||
|
*/
|
||||||
|
private List<File> findApkFiles(File root) {
|
||||||
|
List<File> files = new ArrayList<>();
|
||||||
|
findApkFiles(root, files);
|
||||||
|
return files;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void findApkFiles(File f, List<File> files) {
|
||||||
|
if (f.isFile() && f.getName().toLowerCase().endsWith(".apk")) {
|
||||||
|
files.add(f);
|
||||||
|
} else if (f.isDirectory()) {
|
||||||
|
File[] children = f.listFiles();
|
||||||
|
if (children != null) {
|
||||||
|
for (File child : children) findApkFiles(child, files);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the paths at which libraries with the given name would be found
|
||||||
|
* inside an APK file, for all architectures supported by the device, in
|
||||||
|
* order of preference.
|
||||||
|
*/
|
||||||
|
private List<String> getSupportedLibraryPaths(String libName) {
|
||||||
|
List<String> architectures = new ArrayList<>();
|
||||||
|
for (String abi : AndroidUtils.getSupportedArchitectures()) {
|
||||||
|
if (LIBRARY_ARCHITECTURES.contains(abi)) {
|
||||||
|
architectures.add("lib/" + abi + "/" + libName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return architectures;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package org.briarproject.bramble.keyagreement;
|
package org.briarproject.bramble.keyagreement;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.Pair;
|
||||||
import org.briarproject.bramble.api.crypto.KeyAgreementCrypto;
|
import org.briarproject.bramble.api.crypto.KeyAgreementCrypto;
|
||||||
import org.briarproject.bramble.api.crypto.KeyPair;
|
import org.briarproject.bramble.api.crypto.KeyPair;
|
||||||
import org.briarproject.bramble.api.data.BdfList;
|
import org.briarproject.bramble.api.data.BdfList;
|
||||||
@@ -8,6 +9,8 @@ import org.briarproject.bramble.api.keyagreement.KeyAgreementListener;
|
|||||||
import org.briarproject.bramble.api.keyagreement.Payload;
|
import org.briarproject.bramble.api.keyagreement.Payload;
|
||||||
import org.briarproject.bramble.api.keyagreement.TransportDescriptor;
|
import org.briarproject.bramble.api.keyagreement.TransportDescriptor;
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
import org.briarproject.bramble.api.plugin.BluetoothConstants;
|
||||||
|
import org.briarproject.bramble.api.plugin.LanTcpConstants;
|
||||||
import org.briarproject.bramble.api.plugin.Plugin;
|
import org.briarproject.bramble.api.plugin.Plugin;
|
||||||
import org.briarproject.bramble.api.plugin.PluginManager;
|
import org.briarproject.bramble.api.plugin.PluginManager;
|
||||||
import org.briarproject.bramble.api.plugin.TransportId;
|
import org.briarproject.bramble.api.plugin.TransportId;
|
||||||
@@ -19,7 +22,9 @@ import org.briarproject.bramble.api.record.RecordWriterFactory;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.concurrent.Callable;
|
import java.util.concurrent.Callable;
|
||||||
import java.util.concurrent.CopyOnWriteArrayList;
|
import java.util.concurrent.CopyOnWriteArrayList;
|
||||||
import java.util.concurrent.CountDownLatch;
|
import java.util.concurrent.CountDownLatch;
|
||||||
@@ -28,8 +33,10 @@ import java.util.logging.Logger;
|
|||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
import static java.util.Arrays.asList;
|
||||||
import static java.util.logging.Level.INFO;
|
import static java.util.logging.Level.INFO;
|
||||||
import static java.util.logging.Level.WARNING;
|
import static java.util.logging.Level.WARNING;
|
||||||
|
import static java.util.logging.Logger.getLogger;
|
||||||
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.CONNECTION_TIMEOUT;
|
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.CONNECTION_TIMEOUT;
|
||||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
import static org.briarproject.bramble.util.LogUtils.logException;
|
||||||
|
|
||||||
@@ -41,7 +48,10 @@ class KeyAgreementConnector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static final Logger LOG =
|
private static final Logger LOG =
|
||||||
Logger.getLogger(KeyAgreementConnector.class.getName());
|
getLogger(KeyAgreementConnector.class.getName());
|
||||||
|
|
||||||
|
private static final List<TransportId> PREFERRED_TRANSPORTS =
|
||||||
|
asList(BluetoothConstants.ID, LanTcpConstants.ID);
|
||||||
|
|
||||||
private final Callbacks callbacks;
|
private final Callbacks callbacks;
|
||||||
private final KeyAgreementCrypto keyAgreementCrypto;
|
private final KeyAgreementCrypto keyAgreementCrypto;
|
||||||
@@ -105,24 +115,35 @@ class KeyAgreementConnector {
|
|||||||
this.alice = alice;
|
this.alice = alice;
|
||||||
aliceLatch.countDown();
|
aliceLatch.countDown();
|
||||||
|
|
||||||
// Start connecting over supported transports
|
// Start connecting over supported transports in order of preference
|
||||||
if (LOG.isLoggable(INFO)) {
|
if (LOG.isLoggable(INFO)) {
|
||||||
LOG.info("Starting outgoing BQP connections as "
|
LOG.info("Starting outgoing BQP connections as "
|
||||||
+ (alice ? "Alice" : "Bob"));
|
+ (alice ? "Alice" : "Bob"));
|
||||||
}
|
}
|
||||||
|
Map<TransportId, TransportDescriptor> descriptors = new HashMap<>();
|
||||||
for (TransportDescriptor d : remotePayload.getTransportDescriptors()) {
|
for (TransportDescriptor d : remotePayload.getTransportDescriptors()) {
|
||||||
Plugin p = pluginManager.getPlugin(d.getId());
|
descriptors.put(d.getId(), d);
|
||||||
if (p instanceof DuplexPlugin) {
|
}
|
||||||
|
List<Pair<DuplexPlugin, BdfList>> transports = new ArrayList<>();
|
||||||
|
for (TransportId id : PREFERRED_TRANSPORTS) {
|
||||||
|
TransportDescriptor d = descriptors.get(id);
|
||||||
|
Plugin p = pluginManager.getPlugin(id);
|
||||||
|
if (d != null && p instanceof DuplexPlugin) {
|
||||||
if (LOG.isLoggable(INFO))
|
if (LOG.isLoggable(INFO))
|
||||||
LOG.info("Connecting via " + d.getId());
|
LOG.info("Connecting via " + id);
|
||||||
DuplexPlugin plugin = (DuplexPlugin) p;
|
transports.add(new Pair<>((DuplexPlugin) p, d.getDescriptor()));
|
||||||
byte[] commitment = remotePayload.getCommitment();
|
|
||||||
BdfList descriptor = d.getDescriptor();
|
|
||||||
connectionChooser.submit(new ReadableTask(
|
|
||||||
new ConnectorTask(plugin, commitment, descriptor)));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: If we don't have any transports in common with the peer,
|
||||||
|
// warn the user and give up (#1224)
|
||||||
|
|
||||||
|
if (!transports.isEmpty()) {
|
||||||
|
byte[] commitment = remotePayload.getCommitment();
|
||||||
|
connectionChooser.submit(new ReadableTask(new ConnectorTask(
|
||||||
|
transports, commitment)));
|
||||||
|
}
|
||||||
|
|
||||||
// Get chosen connection
|
// Get chosen connection
|
||||||
try {
|
try {
|
||||||
KeyAgreementConnection chosen =
|
KeyAgreementConnection chosen =
|
||||||
@@ -148,15 +169,13 @@ class KeyAgreementConnector {
|
|||||||
|
|
||||||
private class ConnectorTask implements Callable<KeyAgreementConnection> {
|
private class ConnectorTask implements Callable<KeyAgreementConnection> {
|
||||||
|
|
||||||
|
private final List<Pair<DuplexPlugin, BdfList>> transports;
|
||||||
private final byte[] commitment;
|
private final byte[] commitment;
|
||||||
private final BdfList descriptor;
|
|
||||||
private final DuplexPlugin plugin;
|
|
||||||
|
|
||||||
private ConnectorTask(DuplexPlugin plugin, byte[] commitment,
|
private ConnectorTask(List<Pair<DuplexPlugin, BdfList>> transports,
|
||||||
BdfList descriptor) {
|
byte[] commitment) {
|
||||||
this.plugin = plugin;
|
this.transports = transports;
|
||||||
this.commitment = commitment;
|
this.commitment = commitment;
|
||||||
this.descriptor = descriptor;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@@ -164,13 +183,18 @@ class KeyAgreementConnector {
|
|||||||
public KeyAgreementConnection call() throws Exception {
|
public KeyAgreementConnection call() throws Exception {
|
||||||
// Repeat attempts until we connect, get stopped, or get interrupted
|
// Repeat attempts until we connect, get stopped, or get interrupted
|
||||||
while (!stopped) {
|
while (!stopped) {
|
||||||
DuplexTransportConnection conn =
|
for (Pair<DuplexPlugin, BdfList> pair : transports) {
|
||||||
plugin.createKeyAgreementConnection(commitment,
|
if (stopped) return null;
|
||||||
descriptor);
|
DuplexPlugin plugin = pair.getFirst();
|
||||||
if (conn != null) {
|
BdfList descriptor = pair.getSecond();
|
||||||
if (LOG.isLoggable(INFO))
|
DuplexTransportConnection conn =
|
||||||
LOG.info(plugin.getId() + ": Outgoing connection");
|
plugin.createKeyAgreementConnection(commitment,
|
||||||
return new KeyAgreementConnection(conn, plugin.getId());
|
descriptor);
|
||||||
|
if (conn != null) {
|
||||||
|
if (LOG.isLoggable(INFO))
|
||||||
|
LOG.info(plugin.getId() + ": Outgoing connection");
|
||||||
|
return new KeyAgreementConnection(conn, plugin.getId());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Wait 2s before retry (to circumvent transient failures)
|
// Wait 2s before retry (to circumvent transient failures)
|
||||||
Thread.sleep(2000);
|
Thread.sleep(2000);
|
||||||
|
|||||||
@@ -436,8 +436,10 @@ abstract class BluetoothPlugin<S, SS> implements DuplexPlugin, EventListener {
|
|||||||
String uuid = UUID.nameUUIDFromBytes(commitment).toString();
|
String uuid = UUID.nameUUIDFromBytes(commitment).toString();
|
||||||
DuplexTransportConnection conn;
|
DuplexTransportConnection conn;
|
||||||
if (descriptor.size() == 1) {
|
if (descriptor.size() == 1) {
|
||||||
if (LOG.isLoggable(INFO))
|
if (LOG.isLoggable(INFO)) {
|
||||||
LOG.info("Discovering address for key agreement UUID " + uuid);
|
LOG.info("Discovering address for key agreement UUID " +
|
||||||
|
uuid);
|
||||||
|
}
|
||||||
conn = discoverAndConnect(uuid);
|
conn = discoverAndConnect(uuid);
|
||||||
} else {
|
} else {
|
||||||
String address;
|
String address;
|
||||||
|
|||||||
@@ -132,7 +132,7 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
|||||||
private final CircumventionProvider circumventionProvider;
|
private final CircumventionProvider circumventionProvider;
|
||||||
private final ResourceProvider resourceProvider;
|
private final ResourceProvider resourceProvider;
|
||||||
private final int maxLatency, maxIdleTime, socketTimeout;
|
private final int maxLatency, maxIdleTime, socketTimeout;
|
||||||
private final File torDirectory, torFile, geoIpFile, obfs4File, configFile;
|
private final File torDirectory, geoIpFile, configFile;
|
||||||
private final File doneFile, cookieFile;
|
private final File doneFile, cookieFile;
|
||||||
private final AtomicBoolean used = new AtomicBoolean(false);
|
private final AtomicBoolean used = new AtomicBoolean(false);
|
||||||
|
|
||||||
@@ -181,9 +181,7 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
|||||||
socketTimeout = Integer.MAX_VALUE;
|
socketTimeout = Integer.MAX_VALUE;
|
||||||
else socketTimeout = maxIdleTime * 2;
|
else socketTimeout = maxIdleTime * 2;
|
||||||
this.torDirectory = torDirectory;
|
this.torDirectory = torDirectory;
|
||||||
torFile = new File(torDirectory, "tor");
|
|
||||||
geoIpFile = new File(torDirectory, "geoip");
|
geoIpFile = new File(torDirectory, "geoip");
|
||||||
obfs4File = new File(torDirectory, "obfs4proxy");
|
|
||||||
configFile = new File(torDirectory, "torrc");
|
configFile = new File(torDirectory, "torrc");
|
||||||
doneFile = new File(torDirectory, "done");
|
doneFile = new File(torDirectory, "done");
|
||||||
cookieFile = new File(torDirectory, ".tor/control_auth_cookie");
|
cookieFile = new File(torDirectory, ".tor/control_auth_cookie");
|
||||||
@@ -192,6 +190,14 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
|||||||
new PoliteExecutor("TorPlugin", ioExecutor, 1);
|
new PoliteExecutor("TorPlugin", ioExecutor, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected File getTorExecutableFile() {
|
||||||
|
return new File(torDirectory, "tor");
|
||||||
|
}
|
||||||
|
|
||||||
|
protected File getObfs4ExecutableFile() {
|
||||||
|
return new File(torDirectory, "obfs4proxy");
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public TransportId getId() {
|
public TransportId getId() {
|
||||||
return TorConstants.ID;
|
return TorConstants.ID;
|
||||||
@@ -224,6 +230,7 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
|||||||
LOG.warning("Old auth cookie not deleted");
|
LOG.warning("Old auth cookie not deleted");
|
||||||
// Start a new Tor process
|
// Start a new Tor process
|
||||||
LOG.info("Starting Tor");
|
LOG.info("Starting Tor");
|
||||||
|
File torFile = getTorExecutableFile();
|
||||||
String torPath = torFile.getAbsolutePath();
|
String torPath = torFile.getAbsolutePath();
|
||||||
String configPath = configFile.getAbsolutePath();
|
String configPath = configFile.getAbsolutePath();
|
||||||
String pid = String.valueOf(getProcessId());
|
String pid = String.valueOf(getProcessId());
|
||||||
@@ -322,44 +329,43 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void installAssets() throws PluginException {
|
private void installAssets() throws PluginException {
|
||||||
InputStream in = null;
|
|
||||||
OutputStream out = null;
|
|
||||||
try {
|
try {
|
||||||
// The done file may already exist from a previous installation
|
// The done file may already exist from a previous installation
|
||||||
//noinspection ResultOfMethodCallIgnored
|
//noinspection ResultOfMethodCallIgnored
|
||||||
doneFile.delete();
|
doneFile.delete();
|
||||||
// Unzip the Tor binary to the filesystem
|
installTorExecutable();
|
||||||
in = getTorInputStream();
|
installObfs4Executable();
|
||||||
out = new FileOutputStream(torFile);
|
extract(getGeoIpInputStream(), geoIpFile);
|
||||||
copyAndClose(in, out);
|
extract(getConfigInputStream(), configFile);
|
||||||
// Make the Tor binary executable
|
|
||||||
if (!torFile.setExecutable(true, true)) throw new IOException();
|
|
||||||
// Unzip the GeoIP database to the filesystem
|
|
||||||
in = getGeoIpInputStream();
|
|
||||||
out = new FileOutputStream(geoIpFile);
|
|
||||||
copyAndClose(in, out);
|
|
||||||
// Unzip the Obfs4 proxy to the filesystem
|
|
||||||
in = getObfs4InputStream();
|
|
||||||
out = new FileOutputStream(obfs4File);
|
|
||||||
copyAndClose(in, out);
|
|
||||||
// Make the Obfs4 proxy executable
|
|
||||||
if (!obfs4File.setExecutable(true, true)) throw new IOException();
|
|
||||||
// Copy the config file to the filesystem
|
|
||||||
in = getConfigInputStream();
|
|
||||||
out = new FileOutputStream(configFile);
|
|
||||||
copyAndClose(in, out);
|
|
||||||
if (!doneFile.createNewFile())
|
if (!doneFile.createNewFile())
|
||||||
LOG.warning("Failed to create done file");
|
LOG.warning("Failed to create done file");
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
tryToClose(in, LOG, WARNING);
|
|
||||||
tryToClose(out, LOG, WARNING);
|
|
||||||
throw new PluginException(e);
|
throw new PluginException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private InputStream getTorInputStream() throws IOException {
|
protected void extract(InputStream in, File dest) throws IOException {
|
||||||
|
OutputStream out = new FileOutputStream(dest);
|
||||||
|
copyAndClose(in, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void installTorExecutable() throws IOException {
|
||||||
if (LOG.isLoggable(INFO))
|
if (LOG.isLoggable(INFO))
|
||||||
LOG.info("Installing Tor binary for " + architecture);
|
LOG.info("Installing Tor binary for " + architecture);
|
||||||
|
File torFile = getTorExecutableFile();
|
||||||
|
extract(getTorInputStream(), torFile);
|
||||||
|
if (!torFile.setExecutable(true, true)) throw new IOException();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void installObfs4Executable() throws IOException {
|
||||||
|
if (LOG.isLoggable(INFO))
|
||||||
|
LOG.info("Installing obfs4proxy binary for " + architecture);
|
||||||
|
File obfs4File = getObfs4ExecutableFile();
|
||||||
|
extract(getObfs4InputStream(), obfs4File);
|
||||||
|
if (!obfs4File.setExecutable(true, true)) throw new IOException();
|
||||||
|
}
|
||||||
|
|
||||||
|
private InputStream getTorInputStream() throws IOException {
|
||||||
InputStream in = resourceProvider
|
InputStream in = resourceProvider
|
||||||
.getResourceInputStream("tor_" + architecture, ".zip");
|
.getResourceInputStream("tor_" + architecture, ".zip");
|
||||||
ZipInputStream zin = new ZipInputStream(in);
|
ZipInputStream zin = new ZipInputStream(in);
|
||||||
@@ -376,8 +382,6 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private InputStream getObfs4InputStream() throws IOException {
|
private InputStream getObfs4InputStream() throws IOException {
|
||||||
if (LOG.isLoggable(INFO))
|
|
||||||
LOG.info("Installing obfs4proxy binary for " + architecture);
|
|
||||||
InputStream in = resourceProvider
|
InputStream in = resourceProvider
|
||||||
.getResourceInputStream("obfs4proxy_" + architecture, ".zip");
|
.getResourceInputStream("obfs4proxy_" + architecture, ".zip");
|
||||||
ZipInputStream zin = new ZipInputStream(in);
|
ZipInputStream zin = new ZipInputStream(in);
|
||||||
@@ -569,6 +573,7 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
|||||||
if (enable) {
|
if (enable) {
|
||||||
Collection<String> conf = new ArrayList<>();
|
Collection<String> conf = new ArrayList<>();
|
||||||
conf.add("UseBridges 1");
|
conf.add("UseBridges 1");
|
||||||
|
File obfs4File = getObfs4ExecutableFile();
|
||||||
if (needsMeek) {
|
if (needsMeek) {
|
||||||
conf.add("ClientTransportPlugin meek_lite exec " +
|
conf.add("ClientTransportPlugin meek_lite exec " +
|
||||||
obfs4File.getAbsolutePath());
|
obfs4File.getAbsolutePath());
|
||||||
|
|||||||
@@ -16,14 +16,14 @@ def getStdout = { command, defaultValue ->
|
|||||||
}
|
}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
compileSdkVersion 29
|
compileSdkVersion rootProject.ext.compileSdkVersion
|
||||||
buildToolsVersion '29.0.2'
|
buildToolsVersion rootProject.ext.buildToolsVersion
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
minSdkVersion 16
|
minSdkVersion rootProject.ext.minSdkVersion
|
||||||
targetSdkVersion 28
|
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||||
versionCode 10209
|
versionCode rootProject.ext.versionCode
|
||||||
versionName "1.2.9"
|
versionName rootProject.ext.versionName
|
||||||
applicationId "org.briarproject.briar.android"
|
applicationId "org.briarproject.briar.android"
|
||||||
buildConfigField "String", "GitHash",
|
buildConfigField "String", "GitHash",
|
||||||
"\"${getStdout(['git', 'rev-parse', '--short=7', 'HEAD'], 'No commit hash')}\""
|
"\"${getStdout(['git', 'rev-parse', '--short=7', 'HEAD'], 'No commit hash')}\""
|
||||||
|
|||||||
@@ -13,8 +13,9 @@ import java.util.Random;
|
|||||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
|
|
||||||
import static androidx.test.InstrumentationRegistry.getContext;
|
import static androidx.test.InstrumentationRegistry.getContext;
|
||||||
|
import static org.briarproject.briar.android.attachment.AttachmentItem.State.AVAILABLE;
|
||||||
|
import static org.briarproject.briar.android.attachment.AttachmentItem.State.ERROR;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertFalse;
|
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
@RunWith(AndroidJUnit4.class)
|
@RunWith(AndroidJUnit4.class)
|
||||||
@@ -27,7 +28,7 @@ public class AttachmentRetrieverIntegrationTest {
|
|||||||
|
|
||||||
private final ImageHelper imageHelper = new ImageHelperImpl();
|
private final ImageHelper imageHelper = new ImageHelperImpl();
|
||||||
private final AttachmentRetriever retriever =
|
private final AttachmentRetriever retriever =
|
||||||
new AttachmentRetrieverImpl(null, dimensions, imageHelper,
|
new AttachmentRetrieverImpl(null, null, dimensions, imageHelper,
|
||||||
new ImageSizeCalculator(imageHelper));
|
new ImageSizeCalculator(imageHelper));
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -35,7 +36,7 @@ public class AttachmentRetrieverIntegrationTest {
|
|||||||
AttachmentHeader h = new AttachmentHeader(msgId, "image/jpeg");
|
AttachmentHeader h = new AttachmentHeader(msgId, "image/jpeg");
|
||||||
InputStream is = getAssetInputStream("kitten_small.jpg");
|
InputStream is = getAssetInputStream("kitten_small.jpg");
|
||||||
Attachment a = new Attachment(h, is);
|
Attachment a = new Attachment(h, is);
|
||||||
AttachmentItem item = retriever.getAttachmentItem(a, true);
|
AttachmentItem item = retriever.createAttachmentItem(a, true);
|
||||||
assertEquals(msgId, item.getMessageId());
|
assertEquals(msgId, item.getMessageId());
|
||||||
assertEquals(160, item.getWidth());
|
assertEquals(160, item.getWidth());
|
||||||
assertEquals(240, item.getHeight());
|
assertEquals(240, item.getHeight());
|
||||||
@@ -43,7 +44,7 @@ public class AttachmentRetrieverIntegrationTest {
|
|||||||
assertEquals(240, item.getThumbnailHeight());
|
assertEquals(240, item.getThumbnailHeight());
|
||||||
assertEquals("image/jpeg", item.getMimeType());
|
assertEquals("image/jpeg", item.getMimeType());
|
||||||
assertJpgOrJpeg(item.getExtension());
|
assertJpgOrJpeg(item.getExtension());
|
||||||
assertFalse(item.hasError());
|
assertEquals(AVAILABLE, item.getState());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -51,7 +52,7 @@ public class AttachmentRetrieverIntegrationTest {
|
|||||||
AttachmentHeader h = new AttachmentHeader(msgId, "image/jpeg");
|
AttachmentHeader h = new AttachmentHeader(msgId, "image/jpeg");
|
||||||
InputStream is = getAssetInputStream("kitten_original.jpg");
|
InputStream is = getAssetInputStream("kitten_original.jpg");
|
||||||
Attachment a = new Attachment(h, is);
|
Attachment a = new Attachment(h, is);
|
||||||
AttachmentItem item = retriever.getAttachmentItem(a, true);
|
AttachmentItem item = retriever.createAttachmentItem(a, true);
|
||||||
assertEquals(msgId, item.getMessageId());
|
assertEquals(msgId, item.getMessageId());
|
||||||
assertEquals(1728, item.getWidth());
|
assertEquals(1728, item.getWidth());
|
||||||
assertEquals(2592, item.getHeight());
|
assertEquals(2592, item.getHeight());
|
||||||
@@ -59,7 +60,7 @@ public class AttachmentRetrieverIntegrationTest {
|
|||||||
assertEquals(dimensions.maxHeight, item.getThumbnailHeight());
|
assertEquals(dimensions.maxHeight, item.getThumbnailHeight());
|
||||||
assertEquals("image/jpeg", item.getMimeType());
|
assertEquals("image/jpeg", item.getMimeType());
|
||||||
assertJpgOrJpeg(item.getExtension());
|
assertJpgOrJpeg(item.getExtension());
|
||||||
assertFalse(item.hasError());
|
assertEquals(AVAILABLE, item.getState());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -67,7 +68,7 @@ public class AttachmentRetrieverIntegrationTest {
|
|||||||
AttachmentHeader h = new AttachmentHeader(msgId, "image/png");
|
AttachmentHeader h = new AttachmentHeader(msgId, "image/png");
|
||||||
InputStream is = getAssetInputStream("kitten.png");
|
InputStream is = getAssetInputStream("kitten.png");
|
||||||
Attachment a = new Attachment(h, is);
|
Attachment a = new Attachment(h, is);
|
||||||
AttachmentItem item = retriever.getAttachmentItem(a, true);
|
AttachmentItem item = retriever.createAttachmentItem(a, true);
|
||||||
assertEquals(msgId, item.getMessageId());
|
assertEquals(msgId, item.getMessageId());
|
||||||
assertEquals(737, item.getWidth());
|
assertEquals(737, item.getWidth());
|
||||||
assertEquals(510, item.getHeight());
|
assertEquals(510, item.getHeight());
|
||||||
@@ -75,7 +76,7 @@ public class AttachmentRetrieverIntegrationTest {
|
|||||||
assertEquals(138, item.getThumbnailHeight());
|
assertEquals(138, item.getThumbnailHeight());
|
||||||
assertEquals("image/png", item.getMimeType());
|
assertEquals("image/png", item.getMimeType());
|
||||||
assertEquals("png", item.getExtension());
|
assertEquals("png", item.getExtension());
|
||||||
assertFalse(item.hasError());
|
assertEquals(AVAILABLE, item.getState());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -83,14 +84,14 @@ public class AttachmentRetrieverIntegrationTest {
|
|||||||
AttachmentHeader h = new AttachmentHeader(msgId, "image/gif");
|
AttachmentHeader h = new AttachmentHeader(msgId, "image/gif");
|
||||||
InputStream is = getAssetInputStream("uber.gif");
|
InputStream is = getAssetInputStream("uber.gif");
|
||||||
Attachment a = new Attachment(h, is);
|
Attachment a = new Attachment(h, is);
|
||||||
AttachmentItem item = retriever.getAttachmentItem(a, true);
|
AttachmentItem item = retriever.createAttachmentItem(a, true);
|
||||||
assertEquals(1, item.getWidth());
|
assertEquals(1, item.getWidth());
|
||||||
assertEquals(1, item.getHeight());
|
assertEquals(1, item.getHeight());
|
||||||
assertEquals(dimensions.minHeight, item.getThumbnailWidth());
|
assertEquals(dimensions.minHeight, item.getThumbnailWidth());
|
||||||
assertEquals(dimensions.minHeight, item.getThumbnailHeight());
|
assertEquals(dimensions.minHeight, item.getThumbnailHeight());
|
||||||
assertEquals("image/gif", item.getMimeType());
|
assertEquals("image/gif", item.getMimeType());
|
||||||
assertEquals("gif", item.getExtension());
|
assertEquals("gif", item.getExtension());
|
||||||
assertFalse(item.hasError());
|
assertEquals(AVAILABLE, item.getState());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -98,14 +99,14 @@ public class AttachmentRetrieverIntegrationTest {
|
|||||||
AttachmentHeader h = new AttachmentHeader(msgId, "image/jpeg");
|
AttachmentHeader h = new AttachmentHeader(msgId, "image/jpeg");
|
||||||
InputStream is = getAssetInputStream("lottapixel.jpg");
|
InputStream is = getAssetInputStream("lottapixel.jpg");
|
||||||
Attachment a = new Attachment(h, is);
|
Attachment a = new Attachment(h, is);
|
||||||
AttachmentItem item = retriever.getAttachmentItem(a, true);
|
AttachmentItem item = retriever.createAttachmentItem(a, true);
|
||||||
assertEquals(64250, item.getWidth());
|
assertEquals(64250, item.getWidth());
|
||||||
assertEquals(64250, item.getHeight());
|
assertEquals(64250, item.getHeight());
|
||||||
assertEquals(dimensions.maxWidth, item.getThumbnailWidth());
|
assertEquals(dimensions.maxWidth, item.getThumbnailWidth());
|
||||||
assertEquals(dimensions.maxWidth, item.getThumbnailHeight());
|
assertEquals(dimensions.maxWidth, item.getThumbnailHeight());
|
||||||
assertEquals("image/jpeg", item.getMimeType());
|
assertEquals("image/jpeg", item.getMimeType());
|
||||||
assertJpgOrJpeg(item.getExtension());
|
assertJpgOrJpeg(item.getExtension());
|
||||||
assertFalse(item.hasError());
|
assertEquals(AVAILABLE, item.getState());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -113,14 +114,14 @@ public class AttachmentRetrieverIntegrationTest {
|
|||||||
AttachmentHeader h = new AttachmentHeader(msgId, "image/png");
|
AttachmentHeader h = new AttachmentHeader(msgId, "image/png");
|
||||||
InputStream is = getAssetInputStream("image_io_crash.png");
|
InputStream is = getAssetInputStream("image_io_crash.png");
|
||||||
Attachment a = new Attachment(h, is);
|
Attachment a = new Attachment(h, is);
|
||||||
AttachmentItem item = retriever.getAttachmentItem(a, true);
|
AttachmentItem item = retriever.createAttachmentItem(a, true);
|
||||||
assertEquals(1184, item.getWidth());
|
assertEquals(1184, item.getWidth());
|
||||||
assertEquals(448, item.getHeight());
|
assertEquals(448, item.getHeight());
|
||||||
assertEquals(dimensions.maxWidth, item.getThumbnailWidth());
|
assertEquals(dimensions.maxWidth, item.getThumbnailWidth());
|
||||||
assertEquals(dimensions.minHeight, item.getThumbnailHeight());
|
assertEquals(dimensions.minHeight, item.getThumbnailHeight());
|
||||||
assertEquals("image/png", item.getMimeType());
|
assertEquals("image/png", item.getMimeType());
|
||||||
assertEquals("png", item.getExtension());
|
assertEquals("png", item.getExtension());
|
||||||
assertFalse(item.hasError());
|
assertEquals(AVAILABLE, item.getState());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -128,14 +129,14 @@ public class AttachmentRetrieverIntegrationTest {
|
|||||||
AttachmentHeader h = new AttachmentHeader(msgId, "image/gif");
|
AttachmentHeader h = new AttachmentHeader(msgId, "image/gif");
|
||||||
InputStream is = getAssetInputStream("gimp_crash.gif");
|
InputStream is = getAssetInputStream("gimp_crash.gif");
|
||||||
Attachment a = new Attachment(h, is);
|
Attachment a = new Attachment(h, is);
|
||||||
AttachmentItem item = retriever.getAttachmentItem(a, true);
|
AttachmentItem item = retriever.createAttachmentItem(a, true);
|
||||||
assertEquals(1, item.getWidth());
|
assertEquals(1, item.getWidth());
|
||||||
assertEquals(1, item.getHeight());
|
assertEquals(1, item.getHeight());
|
||||||
assertEquals(dimensions.minHeight, item.getThumbnailWidth());
|
assertEquals(dimensions.minHeight, item.getThumbnailWidth());
|
||||||
assertEquals(dimensions.minHeight, item.getThumbnailHeight());
|
assertEquals(dimensions.minHeight, item.getThumbnailHeight());
|
||||||
assertEquals("image/gif", item.getMimeType());
|
assertEquals("image/gif", item.getMimeType());
|
||||||
assertEquals("gif", item.getExtension());
|
assertEquals("gif", item.getExtension());
|
||||||
assertFalse(item.hasError());
|
assertEquals(AVAILABLE, item.getState());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -143,14 +144,14 @@ public class AttachmentRetrieverIntegrationTest {
|
|||||||
AttachmentHeader h = new AttachmentHeader(msgId, "image/gif");
|
AttachmentHeader h = new AttachmentHeader(msgId, "image/gif");
|
||||||
InputStream is = getAssetInputStream("opti_png_afl.gif");
|
InputStream is = getAssetInputStream("opti_png_afl.gif");
|
||||||
Attachment a = new Attachment(h, is);
|
Attachment a = new Attachment(h, is);
|
||||||
AttachmentItem item = retriever.getAttachmentItem(a, true);
|
AttachmentItem item = retriever.createAttachmentItem(a, true);
|
||||||
assertEquals(32, item.getWidth());
|
assertEquals(32, item.getWidth());
|
||||||
assertEquals(32, item.getHeight());
|
assertEquals(32, item.getHeight());
|
||||||
assertEquals(dimensions.minHeight, item.getThumbnailWidth());
|
assertEquals(dimensions.minHeight, item.getThumbnailWidth());
|
||||||
assertEquals(dimensions.minHeight, item.getThumbnailHeight());
|
assertEquals(dimensions.minHeight, item.getThumbnailHeight());
|
||||||
assertEquals("image/gif", item.getMimeType());
|
assertEquals("image/gif", item.getMimeType());
|
||||||
assertEquals("gif", item.getExtension());
|
assertEquals("gif", item.getExtension());
|
||||||
assertFalse(item.hasError());
|
assertEquals(AVAILABLE, item.getState());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -158,8 +159,8 @@ public class AttachmentRetrieverIntegrationTest {
|
|||||||
AttachmentHeader h = new AttachmentHeader(msgId, "image/jpeg");
|
AttachmentHeader h = new AttachmentHeader(msgId, "image/jpeg");
|
||||||
InputStream is = getAssetInputStream("libraw_error.jpg");
|
InputStream is = getAssetInputStream("libraw_error.jpg");
|
||||||
Attachment a = new Attachment(h, is);
|
Attachment a = new Attachment(h, is);
|
||||||
AttachmentItem item = retriever.getAttachmentItem(a, true);
|
AttachmentItem item = retriever.createAttachmentItem(a, true);
|
||||||
assertTrue(item.hasError());
|
assertEquals(ERROR, item.getState());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -167,14 +168,14 @@ public class AttachmentRetrieverIntegrationTest {
|
|||||||
AttachmentHeader h = new AttachmentHeader(msgId, "image/gif");
|
AttachmentHeader h = new AttachmentHeader(msgId, "image/gif");
|
||||||
InputStream is = getAssetInputStream("animated.gif");
|
InputStream is = getAssetInputStream("animated.gif");
|
||||||
Attachment a = new Attachment(h, is);
|
Attachment a = new Attachment(h, is);
|
||||||
AttachmentItem item = retriever.getAttachmentItem(a, true);
|
AttachmentItem item = retriever.createAttachmentItem(a, true);
|
||||||
assertEquals(65535, item.getWidth());
|
assertEquals(65535, item.getWidth());
|
||||||
assertEquals(65535, item.getHeight());
|
assertEquals(65535, item.getHeight());
|
||||||
assertEquals(dimensions.maxWidth, item.getThumbnailWidth());
|
assertEquals(dimensions.maxWidth, item.getThumbnailWidth());
|
||||||
assertEquals(dimensions.maxWidth, item.getThumbnailHeight());
|
assertEquals(dimensions.maxWidth, item.getThumbnailHeight());
|
||||||
assertEquals("image/gif", item.getMimeType());
|
assertEquals("image/gif", item.getMimeType());
|
||||||
assertEquals("gif", item.getExtension());
|
assertEquals("gif", item.getExtension());
|
||||||
assertFalse(item.hasError());
|
assertEquals(AVAILABLE, item.getState());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -182,14 +183,14 @@ public class AttachmentRetrieverIntegrationTest {
|
|||||||
AttachmentHeader h = new AttachmentHeader(msgId, "image/gif");
|
AttachmentHeader h = new AttachmentHeader(msgId, "image/gif");
|
||||||
InputStream is = getAssetInputStream("animated2.gif");
|
InputStream is = getAssetInputStream("animated2.gif");
|
||||||
Attachment a = new Attachment(h, is);
|
Attachment a = new Attachment(h, is);
|
||||||
AttachmentItem item = retriever.getAttachmentItem(a, true);
|
AttachmentItem item = retriever.createAttachmentItem(a, true);
|
||||||
assertEquals(10000, item.getWidth());
|
assertEquals(10000, item.getWidth());
|
||||||
assertEquals(10000, item.getHeight());
|
assertEquals(10000, item.getHeight());
|
||||||
assertEquals(dimensions.maxWidth, item.getThumbnailWidth());
|
assertEquals(dimensions.maxWidth, item.getThumbnailWidth());
|
||||||
assertEquals(dimensions.maxWidth, item.getThumbnailHeight());
|
assertEquals(dimensions.maxWidth, item.getThumbnailHeight());
|
||||||
assertEquals("image/gif", item.getMimeType());
|
assertEquals("image/gif", item.getMimeType());
|
||||||
assertEquals("gif", item.getExtension());
|
assertEquals("gif", item.getExtension());
|
||||||
assertFalse(item.hasError());
|
assertEquals(AVAILABLE, item.getState());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -197,14 +198,14 @@ public class AttachmentRetrieverIntegrationTest {
|
|||||||
AttachmentHeader h = new AttachmentHeader(msgId, "image/gif");
|
AttachmentHeader h = new AttachmentHeader(msgId, "image/gif");
|
||||||
InputStream is = getAssetInputStream("error_large.gif");
|
InputStream is = getAssetInputStream("error_large.gif");
|
||||||
Attachment a = new Attachment(h, is);
|
Attachment a = new Attachment(h, is);
|
||||||
AttachmentItem item = retriever.getAttachmentItem(a, true);
|
AttachmentItem item = retriever.createAttachmentItem(a, true);
|
||||||
assertEquals(16384, item.getWidth());
|
assertEquals(16384, item.getWidth());
|
||||||
assertEquals(16384, item.getHeight());
|
assertEquals(16384, item.getHeight());
|
||||||
assertEquals(dimensions.maxWidth, item.getThumbnailWidth());
|
assertEquals(dimensions.maxWidth, item.getThumbnailWidth());
|
||||||
assertEquals(dimensions.maxWidth, item.getThumbnailHeight());
|
assertEquals(dimensions.maxWidth, item.getThumbnailHeight());
|
||||||
assertEquals("image/gif", item.getMimeType());
|
assertEquals("image/gif", item.getMimeType());
|
||||||
assertEquals("gif", item.getExtension());
|
assertEquals("gif", item.getExtension());
|
||||||
assertFalse(item.hasError());
|
assertEquals(AVAILABLE, item.getState());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -212,14 +213,14 @@ public class AttachmentRetrieverIntegrationTest {
|
|||||||
AttachmentHeader h = new AttachmentHeader(msgId, "image/jpeg");
|
AttachmentHeader h = new AttachmentHeader(msgId, "image/jpeg");
|
||||||
InputStream is = getAssetInputStream("error_high.jpg");
|
InputStream is = getAssetInputStream("error_high.jpg");
|
||||||
Attachment a = new Attachment(h, is);
|
Attachment a = new Attachment(h, is);
|
||||||
AttachmentItem item = retriever.getAttachmentItem(a, true);
|
AttachmentItem item = retriever.createAttachmentItem(a, true);
|
||||||
assertEquals(1, item.getWidth());
|
assertEquals(1, item.getWidth());
|
||||||
assertEquals(10000, item.getHeight());
|
assertEquals(10000, item.getHeight());
|
||||||
assertEquals(dimensions.minWidth, item.getThumbnailWidth());
|
assertEquals(dimensions.minWidth, item.getThumbnailWidth());
|
||||||
assertEquals(dimensions.maxHeight, item.getThumbnailHeight());
|
assertEquals(dimensions.maxHeight, item.getThumbnailHeight());
|
||||||
assertEquals("image/jpeg", item.getMimeType());
|
assertEquals("image/jpeg", item.getMimeType());
|
||||||
assertJpgOrJpeg(item.getExtension());
|
assertJpgOrJpeg(item.getExtension());
|
||||||
assertFalse(item.hasError());
|
assertEquals(AVAILABLE, item.getState());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -227,14 +228,14 @@ public class AttachmentRetrieverIntegrationTest {
|
|||||||
AttachmentHeader h = new AttachmentHeader(msgId, "image/jpeg");
|
AttachmentHeader h = new AttachmentHeader(msgId, "image/jpeg");
|
||||||
InputStream is = getAssetInputStream("error_wide.jpg");
|
InputStream is = getAssetInputStream("error_wide.jpg");
|
||||||
Attachment a = new Attachment(h, is);
|
Attachment a = new Attachment(h, is);
|
||||||
AttachmentItem item = retriever.getAttachmentItem(a, true);
|
AttachmentItem item = retriever.createAttachmentItem(a, true);
|
||||||
assertEquals(1920, item.getWidth());
|
assertEquals(1920, item.getWidth());
|
||||||
assertEquals(1, item.getHeight());
|
assertEquals(1, item.getHeight());
|
||||||
assertEquals(dimensions.maxWidth, item.getThumbnailWidth());
|
assertEquals(dimensions.maxWidth, item.getThumbnailWidth());
|
||||||
assertEquals(dimensions.minHeight, item.getThumbnailHeight());
|
assertEquals(dimensions.minHeight, item.getThumbnailHeight());
|
||||||
assertEquals("image/jpeg", item.getMimeType());
|
assertEquals("image/jpeg", item.getMimeType());
|
||||||
assertJpgOrJpeg(item.getExtension());
|
assertJpgOrJpeg(item.getExtension());
|
||||||
assertFalse(item.hasError());
|
assertEquals(AVAILABLE, item.getState());
|
||||||
}
|
}
|
||||||
|
|
||||||
private InputStream getAssetInputStream(String name) throws Exception {
|
private InputStream getAssetInputStream(String name) throws Exception {
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ import androidx.lifecycle.MutableLiveData;
|
|||||||
import static java.util.logging.Level.WARNING;
|
import static java.util.logging.Level.WARNING;
|
||||||
import static java.util.logging.Logger.getLogger;
|
import static java.util.logging.Logger.getLogger;
|
||||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
import static org.briarproject.bramble.util.LogUtils.logException;
|
||||||
|
import static org.briarproject.briar.android.attachment.AttachmentItem.State.ERROR;
|
||||||
import static org.briarproject.briar.android.util.UiUtils.observeForeverOnce;
|
import static org.briarproject.briar.android.util.UiUtils.observeForeverOnce;
|
||||||
import static org.briarproject.briar.api.messaging.MessagingConstants.MAX_IMAGE_SIZE;
|
import static org.briarproject.briar.api.messaging.MessagingConstants.MAX_IMAGE_SIZE;
|
||||||
|
|
||||||
@@ -75,8 +76,12 @@ class AttachmentCreatorImpl implements AttachmentCreator {
|
|||||||
@UiThread
|
@UiThread
|
||||||
public LiveData<AttachmentResult> storeAttachments(
|
public LiveData<AttachmentResult> storeAttachments(
|
||||||
LiveData<GroupId> groupId, Collection<Uri> newUris) {
|
LiveData<GroupId> groupId, Collection<Uri> newUris) {
|
||||||
if (task != null || result != null || !uris.isEmpty())
|
if (task != null || result != null || !uris.isEmpty()) {
|
||||||
|
if (task != null) LOG.warning("Task already exists!");
|
||||||
|
if (result != null) LOG.warning("Result already exists!");
|
||||||
|
if (!uris.isEmpty()) LOG.warning("Uris available: " + uris);
|
||||||
throw new IllegalStateException();
|
throw new IllegalStateException();
|
||||||
|
}
|
||||||
MutableLiveData<AttachmentResult> result = new MutableLiveData<>();
|
MutableLiveData<AttachmentResult> result = new MutableLiveData<>();
|
||||||
this.result = result;
|
this.result = result;
|
||||||
uris.addAll(newUris);
|
uris.addAll(newUris);
|
||||||
@@ -95,8 +100,12 @@ class AttachmentCreatorImpl implements AttachmentCreator {
|
|||||||
@UiThread
|
@UiThread
|
||||||
public LiveData<AttachmentResult> getLiveAttachments() {
|
public LiveData<AttachmentResult> getLiveAttachments() {
|
||||||
MutableLiveData<AttachmentResult> result = this.result;
|
MutableLiveData<AttachmentResult> result = this.result;
|
||||||
if (task == null || result == null || uris.isEmpty())
|
if (task == null || result == null || uris.isEmpty()) {
|
||||||
|
if (task == null) LOG.warning("No Task!");
|
||||||
|
if (result == null) LOG.warning("No Result!");
|
||||||
|
if (uris.isEmpty()) LOG.warning("Uris empty!");
|
||||||
throw new IllegalStateException();
|
throw new IllegalStateException();
|
||||||
|
}
|
||||||
// A task is already running. It will update the result LiveData.
|
// A task is already running. It will update the result LiveData.
|
||||||
// So nothing more to do here.
|
// So nothing more to do here.
|
||||||
return result;
|
return result;
|
||||||
@@ -109,8 +118,8 @@ class AttachmentCreatorImpl implements AttachmentCreator {
|
|||||||
// get and cache AttachmentItem for ImagePreview
|
// get and cache AttachmentItem for ImagePreview
|
||||||
try {
|
try {
|
||||||
Attachment a = retriever.getMessageAttachment(h);
|
Attachment a = retriever.getMessageAttachment(h);
|
||||||
AttachmentItem item = retriever.getAttachmentItem(a, needsSize);
|
AttachmentItem item = retriever.createAttachmentItem(a, needsSize);
|
||||||
if (item.hasError()) throw new IOException();
|
if (item.getState() == ERROR) throw new IOException();
|
||||||
AttachmentItemResult itemResult =
|
AttachmentItemResult itemResult =
|
||||||
new AttachmentItemResult(uri, item);
|
new AttachmentItemResult(uri, item);
|
||||||
itemResults.add(itemResult);
|
itemResults.add(itemResult);
|
||||||
@@ -167,21 +176,13 @@ class AttachmentCreatorImpl implements AttachmentCreator {
|
|||||||
@Override
|
@Override
|
||||||
@UiThread
|
@UiThread
|
||||||
public void onAttachmentsSent(MessageId id) {
|
public void onAttachmentsSent(MessageId id) {
|
||||||
List<AttachmentItem> items = new ArrayList<>(itemResults.size());
|
|
||||||
for (AttachmentItemResult itemResult : itemResults) {
|
|
||||||
// check if we are trying to send attachment items with errors
|
|
||||||
if (itemResult.getItem() == null) throw new IllegalStateException();
|
|
||||||
items.add(itemResult.getItem());
|
|
||||||
}
|
|
||||||
retriever.cachePut(id, items);
|
|
||||||
resetState();
|
resetState();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@UiThread
|
@UiThread
|
||||||
public void cancel() {
|
public void cancel() {
|
||||||
if (task == null) throw new AssertionError();
|
if (task != null) task.cancel();
|
||||||
task.cancel();
|
|
||||||
deleteUnsentAttachments();
|
deleteUnsentAttachments();
|
||||||
resetState();
|
resetState();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,24 +7,33 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|||||||
import org.briarproject.bramble.api.sync.MessageId;
|
import org.briarproject.bramble.api.sync.MessageId;
|
||||||
import org.briarproject.briar.api.messaging.AttachmentHeader;
|
import org.briarproject.briar.api.messaging.AttachmentHeader;
|
||||||
|
|
||||||
import java.util.concurrent.atomic.AtomicLong;
|
|
||||||
|
|
||||||
import javax.annotation.concurrent.Immutable;
|
import javax.annotation.concurrent.Immutable;
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
|
import static java.lang.System.arraycopy;
|
||||||
import static java.util.Objects.requireNonNull;
|
import static java.util.Objects.requireNonNull;
|
||||||
|
import static org.briarproject.bramble.util.StringUtils.toHexString;
|
||||||
|
import static org.briarproject.briar.android.attachment.AttachmentItem.State.LOADING;
|
||||||
|
import static org.briarproject.briar.android.attachment.AttachmentItem.State.MISSING;
|
||||||
|
|
||||||
@Immutable
|
@Immutable
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
public class AttachmentItem implements Parcelable {
|
public class AttachmentItem implements Parcelable {
|
||||||
|
|
||||||
|
public enum State {
|
||||||
|
LOADING, MISSING, AVAILABLE, ERROR;
|
||||||
|
|
||||||
|
public boolean isFinal() {
|
||||||
|
return this == AVAILABLE || this == ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private final AttachmentHeader header;
|
private final AttachmentHeader header;
|
||||||
private final int width, height;
|
private final int width, height;
|
||||||
private final String extension;
|
private final String extension;
|
||||||
private final int thumbnailWidth, thumbnailHeight;
|
private final int thumbnailWidth, thumbnailHeight;
|
||||||
private final boolean hasError;
|
private final State state;
|
||||||
private final long instanceId;
|
|
||||||
|
|
||||||
public static final Creator<AttachmentItem> CREATOR =
|
public static final Creator<AttachmentItem> CREATOR =
|
||||||
new Creator<AttachmentItem>() {
|
new Creator<AttachmentItem>() {
|
||||||
@@ -39,19 +48,33 @@ public class AttachmentItem implements Parcelable {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private static final AtomicLong NEXT_INSTANCE_ID = new AtomicLong(0);
|
|
||||||
|
|
||||||
AttachmentItem(AttachmentHeader header, int width, int height,
|
AttachmentItem(AttachmentHeader header, int width, int height,
|
||||||
String extension, int thumbnailWidth, int thumbnailHeight,
|
String extension, int thumbnailWidth, int thumbnailHeight,
|
||||||
boolean hasError) {
|
State state) {
|
||||||
this.header = header;
|
this.header = header;
|
||||||
this.width = width;
|
this.width = width;
|
||||||
this.height = height;
|
this.height = height;
|
||||||
this.extension = extension;
|
this.extension = extension;
|
||||||
this.thumbnailWidth = thumbnailWidth;
|
this.thumbnailWidth = thumbnailWidth;
|
||||||
this.thumbnailHeight = thumbnailHeight;
|
this.thumbnailHeight = thumbnailHeight;
|
||||||
this.hasError = hasError;
|
this.state = state;
|
||||||
instanceId = NEXT_INSTANCE_ID.getAndIncrement();
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use only for {@link State MISSING} or {@link State LOADING} items.
|
||||||
|
*/
|
||||||
|
AttachmentItem(AttachmentHeader header, int width, int height,
|
||||||
|
State state) {
|
||||||
|
this(header, width, height, "", width, height, state);
|
||||||
|
if (state != MISSING && state != LOADING)
|
||||||
|
throw new IllegalArgumentException();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use when the item does not need a size.
|
||||||
|
*/
|
||||||
|
AttachmentItem(AttachmentHeader header, String extension, State state) {
|
||||||
|
this(header, 0, 0, extension, 0, 0, state);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected AttachmentItem(Parcel in) {
|
protected AttachmentItem(Parcel in) {
|
||||||
@@ -64,8 +87,7 @@ public class AttachmentItem implements Parcelable {
|
|||||||
extension = requireNonNull(in.readString());
|
extension = requireNonNull(in.readString());
|
||||||
thumbnailWidth = in.readInt();
|
thumbnailWidth = in.readInt();
|
||||||
thumbnailHeight = in.readInt();
|
thumbnailHeight = in.readInt();
|
||||||
hasError = in.readByte() != 0;
|
state = State.valueOf(requireNonNull(in.readString()));
|
||||||
instanceId = in.readLong();
|
|
||||||
header = new AttachmentHeader(messageId, mimeType);
|
header = new AttachmentHeader(messageId, mimeType);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -101,12 +123,16 @@ public class AttachmentItem implements Parcelable {
|
|||||||
return thumbnailHeight;
|
return thumbnailHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean hasError() {
|
public State getState() {
|
||||||
return hasError;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getTransitionName() {
|
public String getTransitionName(MessageId conversationItemId) {
|
||||||
return String.valueOf(instanceId);
|
int len = MessageId.LENGTH;
|
||||||
|
byte[] instanceId = new byte[len * 2];
|
||||||
|
arraycopy(header.getMessageId().getBytes(), 0, instanceId, 0, len);
|
||||||
|
arraycopy(conversationItemId.getBytes(), 0, instanceId, len, len);
|
||||||
|
return toHexString(instanceId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -123,14 +149,23 @@ public class AttachmentItem implements Parcelable {
|
|||||||
dest.writeString(extension);
|
dest.writeString(extension);
|
||||||
dest.writeInt(thumbnailWidth);
|
dest.writeInt(thumbnailWidth);
|
||||||
dest.writeInt(thumbnailHeight);
|
dest.writeInt(thumbnailHeight);
|
||||||
dest.writeByte((byte) (hasError ? 1 : 0));
|
dest.writeString(state.name());
|
||||||
dest.writeLong(instanceId);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is used to identity if two items are the same,
|
||||||
|
* irrespective of their state or size.
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(@Nullable Object o) {
|
public boolean equals(@Nullable Object o) {
|
||||||
return o instanceof AttachmentItem &&
|
return o instanceof AttachmentItem &&
|
||||||
instanceId == ((AttachmentItem) o).instanceId;
|
header.getMessageId().equals(
|
||||||
|
((AttachmentItem) o).header.getMessageId()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return header.getMessageId().hashCode();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,29 +1,63 @@
|
|||||||
package org.briarproject.briar.android.attachment;
|
package org.briarproject.briar.android.attachment;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.db.DatabaseExecutor;
|
||||||
import org.briarproject.bramble.api.db.DbException;
|
import org.briarproject.bramble.api.db.DbException;
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.bramble.api.sync.MessageId;
|
import org.briarproject.bramble.api.sync.MessageId;
|
||||||
import org.briarproject.briar.api.messaging.Attachment;
|
import org.briarproject.briar.api.messaging.Attachment;
|
||||||
import org.briarproject.briar.api.messaging.AttachmentHeader;
|
import org.briarproject.briar.api.messaging.AttachmentHeader;
|
||||||
|
import org.briarproject.briar.api.messaging.PrivateMessageHeader;
|
||||||
|
import org.briarproject.briar.api.messaging.event.AttachmentReceivedEvent;
|
||||||
|
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
import androidx.lifecycle.LiveData;
|
||||||
|
|
||||||
|
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
public interface AttachmentRetriever {
|
public interface AttachmentRetriever {
|
||||||
|
|
||||||
void cachePut(MessageId messageId, List<AttachmentItem> attachments);
|
@DatabaseExecutor
|
||||||
|
|
||||||
@Nullable
|
|
||||||
List<AttachmentItem> cacheGet(MessageId messageId);
|
|
||||||
|
|
||||||
Attachment getMessageAttachment(AttachmentHeader h) throws DbException;
|
Attachment getMessageAttachment(AttachmentHeader h) throws DbException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a list of observable {@link LiveData}
|
||||||
|
* that get updated as the state of their {@link AttachmentItem}s changes.
|
||||||
|
*/
|
||||||
|
List<LiveData<AttachmentItem>> getAttachmentItems(
|
||||||
|
PrivateMessageHeader messageHeader);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves item size and adds the item to the cache, if available.
|
||||||
|
* <p>
|
||||||
|
* Use this to eagerly load the attachment size before it gets displayed.
|
||||||
|
* This is needed for messages containing a single attachment.
|
||||||
|
* Messages with more than one attachment use a standard size.
|
||||||
|
*/
|
||||||
|
@DatabaseExecutor
|
||||||
|
void cacheAttachmentItemWithSize(MessageId conversationMessageId,
|
||||||
|
AttachmentHeader h) throws DbException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an {@link AttachmentItem} from the {@link Attachment}'s
|
* Creates an {@link AttachmentItem} from the {@link Attachment}'s
|
||||||
* {@link InputStream} which will be closed when this method returns.
|
* {@link InputStream} which will be closed when this method returns.
|
||||||
*/
|
*/
|
||||||
AttachmentItem getAttachmentItem(Attachment a, boolean needsSize);
|
AttachmentItem createAttachmentItem(Attachment a, boolean needsSize);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads an {@link AttachmentItem}
|
||||||
|
* that arrived via an {@link AttachmentReceivedEvent}
|
||||||
|
* and notifies the associated {@link LiveData}.
|
||||||
|
*
|
||||||
|
* Note that you need to call {@link #getAttachmentItems(PrivateMessageHeader)}
|
||||||
|
* first to get the LiveData.
|
||||||
|
*
|
||||||
|
* It is possible that no LiveData is available,
|
||||||
|
* because the message of the AttachmentItem did not arrive, yet.
|
||||||
|
* In this case, the load wil be deferred until the message arrives.
|
||||||
|
*/
|
||||||
|
@DatabaseExecutor
|
||||||
|
void loadAttachmentItem(MessageId attachmentId);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,25 +1,39 @@
|
|||||||
package org.briarproject.briar.android.attachment;
|
package org.briarproject.briar.android.attachment;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.db.DatabaseExecutor;
|
||||||
import org.briarproject.bramble.api.db.DbException;
|
import org.briarproject.bramble.api.db.DbException;
|
||||||
|
import org.briarproject.bramble.api.db.NoSuchMessageException;
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.bramble.api.sync.MessageId;
|
import org.briarproject.bramble.api.sync.MessageId;
|
||||||
|
import org.briarproject.briar.android.attachment.AttachmentItem.State;
|
||||||
import org.briarproject.briar.api.messaging.Attachment;
|
import org.briarproject.briar.api.messaging.Attachment;
|
||||||
import org.briarproject.briar.api.messaging.AttachmentHeader;
|
import org.briarproject.briar.api.messaging.AttachmentHeader;
|
||||||
import org.briarproject.briar.api.messaging.MessagingManager;
|
import org.briarproject.briar.api.messaging.MessagingManager;
|
||||||
|
import org.briarproject.briar.api.messaging.PrivateMessageHeader;
|
||||||
|
|
||||||
import java.io.BufferedInputStream;
|
import java.io.BufferedInputStream;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.concurrent.ConcurrentMap;
|
||||||
|
import java.util.concurrent.Executor;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
import androidx.lifecycle.LiveData;
|
||||||
|
import androidx.lifecycle.MutableLiveData;
|
||||||
|
|
||||||
|
import static java.util.Objects.requireNonNull;
|
||||||
import static java.util.logging.Level.WARNING;
|
import static java.util.logging.Level.WARNING;
|
||||||
import static java.util.logging.Logger.getLogger;
|
import static java.util.logging.Logger.getLogger;
|
||||||
|
import static org.briarproject.bramble.util.IoUtils.tryToClose;
|
||||||
|
import static org.briarproject.bramble.util.LogUtils.logException;
|
||||||
|
import static org.briarproject.briar.android.attachment.AttachmentItem.State.AVAILABLE;
|
||||||
|
import static org.briarproject.briar.android.attachment.AttachmentItem.State.ERROR;
|
||||||
|
import static org.briarproject.briar.android.attachment.AttachmentItem.State.LOADING;
|
||||||
|
import static org.briarproject.briar.android.attachment.AttachmentItem.State.MISSING;
|
||||||
|
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
class AttachmentRetrieverImpl implements AttachmentRetriever {
|
class AttachmentRetrieverImpl implements AttachmentRetriever {
|
||||||
@@ -27,6 +41,8 @@ class AttachmentRetrieverImpl implements AttachmentRetriever {
|
|||||||
private static final Logger LOG =
|
private static final Logger LOG =
|
||||||
getLogger(AttachmentRetrieverImpl.class.getName());
|
getLogger(AttachmentRetrieverImpl.class.getName());
|
||||||
|
|
||||||
|
@DatabaseExecutor
|
||||||
|
private final Executor dbExecutor;
|
||||||
private final MessagingManager messagingManager;
|
private final MessagingManager messagingManager;
|
||||||
private final ImageHelper imageHelper;
|
private final ImageHelper imageHelper;
|
||||||
private final ImageSizeCalculator imageSizeCalculator;
|
private final ImageSizeCalculator imageSizeCalculator;
|
||||||
@@ -34,13 +50,17 @@ class AttachmentRetrieverImpl implements AttachmentRetriever {
|
|||||||
private final int minWidth, maxWidth;
|
private final int minWidth, maxWidth;
|
||||||
private final int minHeight, maxHeight;
|
private final int minHeight, maxHeight;
|
||||||
|
|
||||||
private final Map<MessageId, List<AttachmentItem>> attachmentCache =
|
private final ConcurrentMap<MessageId, MutableLiveData<AttachmentItem>>
|
||||||
new ConcurrentHashMap<>();
|
itemsWithSize = new ConcurrentHashMap<>();
|
||||||
|
private final ConcurrentMap<MessageId, MutableLiveData<AttachmentItem>>
|
||||||
|
itemsWithoutSize = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
AttachmentRetrieverImpl(MessagingManager messagingManager,
|
AttachmentRetrieverImpl(@DatabaseExecutor Executor dbExecutor,
|
||||||
|
MessagingManager messagingManager,
|
||||||
AttachmentDimensions dimensions, ImageHelper imageHelper,
|
AttachmentDimensions dimensions, ImageHelper imageHelper,
|
||||||
ImageSizeCalculator imageSizeCalculator) {
|
ImageSizeCalculator imageSizeCalculator) {
|
||||||
|
this.dbExecutor = dbExecutor;
|
||||||
this.messagingManager = messagingManager;
|
this.messagingManager = messagingManager;
|
||||||
this.imageHelper = imageHelper;
|
this.imageHelper = imageHelper;
|
||||||
this.imageSizeCalculator = imageSizeCalculator;
|
this.imageSizeCalculator = imageSizeCalculator;
|
||||||
@@ -52,40 +72,143 @@ class AttachmentRetrieverImpl implements AttachmentRetriever {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void cachePut(MessageId messageId,
|
@DatabaseExecutor
|
||||||
List<AttachmentItem> attachments) {
|
|
||||||
attachmentCache.put(messageId, attachments);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@Nullable
|
|
||||||
public List<AttachmentItem> cacheGet(MessageId messageId) {
|
|
||||||
return attachmentCache.get(messageId);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Attachment getMessageAttachment(AttachmentHeader h)
|
public Attachment getMessageAttachment(AttachmentHeader h)
|
||||||
throws DbException {
|
throws DbException {
|
||||||
return messagingManager.getAttachment(h);
|
return messagingManager.getAttachment(h);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AttachmentItem getAttachmentItem(Attachment a, boolean needsSize) {
|
public List<LiveData<AttachmentItem>> getAttachmentItems(
|
||||||
AttachmentHeader h = a.getHeader();
|
PrivateMessageHeader messageHeader) {
|
||||||
if (!needsSize) {
|
List<AttachmentHeader> headers = messageHeader.getAttachmentHeaders();
|
||||||
String extension =
|
List<LiveData<AttachmentItem>> items = new ArrayList<>(headers.size());
|
||||||
imageHelper.getExtensionFromMimeType(h.getContentType());
|
boolean needsSize = headers.size() == 1;
|
||||||
boolean hasError = false;
|
for (AttachmentHeader h : headers) {
|
||||||
if (extension == null) {
|
// try cache for existing item live data
|
||||||
extension = "";
|
MutableLiveData<AttachmentItem> liveData;
|
||||||
hasError = true;
|
if (needsSize) liveData = itemsWithSize.get(h.getMessageId());
|
||||||
|
else {
|
||||||
|
// try items with size first, as they work as well
|
||||||
|
liveData = itemsWithSize.get(h.getMessageId());
|
||||||
|
if (liveData == null)
|
||||||
|
liveData = itemsWithoutSize.get(h.getMessageId());
|
||||||
}
|
}
|
||||||
return new AttachmentItem(h, 0, 0, extension, 0, 0, hasError);
|
|
||||||
|
// create new live data with LOADING item if cache miss
|
||||||
|
if (liveData == null) {
|
||||||
|
AttachmentItem item = new AttachmentItem(h,
|
||||||
|
defaultSize, defaultSize, LOADING);
|
||||||
|
liveData = new MutableLiveData<>(item);
|
||||||
|
// add new LiveData to cache, checking for concurrent updates
|
||||||
|
MutableLiveData<AttachmentItem> oldLiveData;
|
||||||
|
if (needsSize) {
|
||||||
|
oldLiveData = itemsWithSize.putIfAbsent(h.getMessageId(),
|
||||||
|
liveData);
|
||||||
|
} else {
|
||||||
|
oldLiveData = itemsWithoutSize.putIfAbsent(h.getMessageId(),
|
||||||
|
liveData);
|
||||||
|
}
|
||||||
|
if (oldLiveData == null) {
|
||||||
|
// kick-off loading of attachment, will post to live data
|
||||||
|
MutableLiveData<AttachmentItem> finalLiveData = liveData;
|
||||||
|
dbExecutor.execute(() ->
|
||||||
|
loadAttachmentItem(h, needsSize, finalLiveData));
|
||||||
|
} else {
|
||||||
|
// Concurrent cache update - use the existing live data
|
||||||
|
liveData = oldLiveData;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
items.add(liveData);
|
||||||
|
}
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@DatabaseExecutor
|
||||||
|
public void cacheAttachmentItemWithSize(MessageId conversationMessageId,
|
||||||
|
AttachmentHeader h) throws DbException {
|
||||||
|
// If a live data is already cached we don't need to do anything
|
||||||
|
if (itemsWithSize.containsKey(h.getMessageId())) return;
|
||||||
|
try {
|
||||||
|
Attachment a = messagingManager.getAttachment(h);
|
||||||
|
AttachmentItem item = createAttachmentItem(a, true);
|
||||||
|
MutableLiveData<AttachmentItem> liveData =
|
||||||
|
new MutableLiveData<>(item);
|
||||||
|
// If a live data was concurrently cached, don't replace it
|
||||||
|
itemsWithSize.putIfAbsent(h.getMessageId(), liveData);
|
||||||
|
} catch (NoSuchMessageException e) {
|
||||||
|
LOG.info("Attachment not received yet");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@DatabaseExecutor
|
||||||
|
public void loadAttachmentItem(MessageId attachmentId) {
|
||||||
|
// try to find LiveData for attachment in both caches
|
||||||
|
MutableLiveData<AttachmentItem> liveData;
|
||||||
|
boolean needsSize = true;
|
||||||
|
liveData = itemsWithSize.get(attachmentId);
|
||||||
|
if (liveData == null) {
|
||||||
|
needsSize = false;
|
||||||
|
liveData = itemsWithoutSize.get(attachmentId);
|
||||||
}
|
}
|
||||||
|
|
||||||
InputStream is = new BufferedInputStream(a.getStream());
|
// If no LiveData for the attachment exists,
|
||||||
Size size = imageSizeCalculator.getSize(is, h.getContentType());
|
// its message did not yet arrive and we can ignore it for now.
|
||||||
|
if (liveData == null) return;
|
||||||
|
|
||||||
|
// actually load the attachment item
|
||||||
|
AttachmentHeader h = requireNonNull(liveData.getValue()).getHeader();
|
||||||
|
loadAttachmentItem(h, needsSize, liveData);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads an {@link AttachmentItem} from the database
|
||||||
|
* and notifies the given {@link LiveData}.
|
||||||
|
*/
|
||||||
|
@DatabaseExecutor
|
||||||
|
private void loadAttachmentItem(AttachmentHeader h, boolean needsSize,
|
||||||
|
MutableLiveData<AttachmentItem> liveData) {
|
||||||
|
Attachment a;
|
||||||
|
AttachmentItem item;
|
||||||
|
try {
|
||||||
|
a = messagingManager.getAttachment(h);
|
||||||
|
item = createAttachmentItem(a, needsSize);
|
||||||
|
} catch (NoSuchMessageException e) {
|
||||||
|
LOG.info("Attachment not received yet");
|
||||||
|
item = new AttachmentItem(h, defaultSize, defaultSize, MISSING);
|
||||||
|
} catch (DbException e) {
|
||||||
|
logException(LOG, WARNING, e);
|
||||||
|
item = new AttachmentItem(h, "", ERROR);
|
||||||
|
}
|
||||||
|
liveData.postValue(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AttachmentItem createAttachmentItem(Attachment a,
|
||||||
|
boolean needsSize) {
|
||||||
|
AttachmentItem item;
|
||||||
|
AttachmentHeader h = a.getHeader();
|
||||||
|
if (needsSize) {
|
||||||
|
InputStream is = new BufferedInputStream(a.getStream());
|
||||||
|
Size size = imageSizeCalculator.getSize(is, h.getContentType());
|
||||||
|
tryToClose(is, LOG, WARNING);
|
||||||
|
item = createAttachmentItem(h, size);
|
||||||
|
} else {
|
||||||
|
String extension =
|
||||||
|
imageHelper.getExtensionFromMimeType(h.getContentType());
|
||||||
|
State state = AVAILABLE;
|
||||||
|
if (extension == null) {
|
||||||
|
extension = "";
|
||||||
|
state = ERROR;
|
||||||
|
}
|
||||||
|
item = new AttachmentItem(h, extension, state);
|
||||||
|
}
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
private AttachmentItem createAttachmentItem(AttachmentHeader h, Size size) {
|
||||||
// calculate thumbnail size
|
// calculate thumbnail size
|
||||||
Size thumbnailSize = new Size(defaultSize, defaultSize, size.mimeType);
|
Size thumbnailSize = new Size(defaultSize, defaultSize, size.mimeType);
|
||||||
if (!size.error) {
|
if (!size.error) {
|
||||||
@@ -104,8 +227,9 @@ class AttachmentRetrieverImpl implements AttachmentRetriever {
|
|||||||
hasError = true;
|
hasError = true;
|
||||||
}
|
}
|
||||||
if (extension == null) extension = "";
|
if (extension == null) extension = "";
|
||||||
return new AttachmentItem(h, size.width, size.height, extension,
|
State state = hasError ? ERROR : AVAILABLE;
|
||||||
thumbnailSize.width, thumbnailSize.height, hasError);
|
return new AttachmentItem(h, size.width, size.height,
|
||||||
|
extension, thumbnailSize.width, thumbnailSize.height, state);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Size getThumbnailSize(int width, int height, String mimeType) {
|
private Size getThumbnailSize(int width, int height, String mimeType) {
|
||||||
|
|||||||
@@ -28,7 +28,6 @@ import org.briarproject.bramble.api.contact.event.ContactRemovedEvent;
|
|||||||
import org.briarproject.bramble.api.db.DatabaseExecutor;
|
import org.briarproject.bramble.api.db.DatabaseExecutor;
|
||||||
import org.briarproject.bramble.api.db.DbException;
|
import org.briarproject.bramble.api.db.DbException;
|
||||||
import org.briarproject.bramble.api.db.NoSuchContactException;
|
import org.briarproject.bramble.api.db.NoSuchContactException;
|
||||||
import org.briarproject.bramble.api.db.NoSuchMessageException;
|
|
||||||
import org.briarproject.bramble.api.event.Event;
|
import org.briarproject.bramble.api.event.Event;
|
||||||
import org.briarproject.bramble.api.event.EventBus;
|
import org.briarproject.bramble.api.event.EventBus;
|
||||||
import org.briarproject.bramble.api.event.EventListener;
|
import org.briarproject.bramble.api.event.EventListener;
|
||||||
@@ -65,17 +64,16 @@ import org.briarproject.briar.api.client.ProtocolStateException;
|
|||||||
import org.briarproject.briar.api.client.SessionId;
|
import org.briarproject.briar.api.client.SessionId;
|
||||||
import org.briarproject.briar.api.conversation.ConversationManager;
|
import org.briarproject.briar.api.conversation.ConversationManager;
|
||||||
import org.briarproject.briar.api.conversation.ConversationMessageHeader;
|
import org.briarproject.briar.api.conversation.ConversationMessageHeader;
|
||||||
|
import org.briarproject.briar.api.conversation.ConversationMessageVisitor;
|
||||||
import org.briarproject.briar.api.conversation.ConversationRequest;
|
import org.briarproject.briar.api.conversation.ConversationRequest;
|
||||||
import org.briarproject.briar.api.conversation.ConversationResponse;
|
import org.briarproject.briar.api.conversation.ConversationResponse;
|
||||||
import org.briarproject.briar.api.conversation.DeletionResult;
|
import org.briarproject.briar.api.conversation.DeletionResult;
|
||||||
import org.briarproject.briar.api.conversation.event.ConversationMessageReceivedEvent;
|
import org.briarproject.briar.api.conversation.event.ConversationMessageReceivedEvent;
|
||||||
import org.briarproject.briar.api.forum.ForumSharingManager;
|
import org.briarproject.briar.api.forum.ForumSharingManager;
|
||||||
import org.briarproject.briar.api.introduction.IntroductionManager;
|
import org.briarproject.briar.api.introduction.IntroductionManager;
|
||||||
import org.briarproject.briar.api.messaging.Attachment;
|
|
||||||
import org.briarproject.briar.api.messaging.AttachmentHeader;
|
import org.briarproject.briar.api.messaging.AttachmentHeader;
|
||||||
import org.briarproject.briar.api.messaging.MessagingManager;
|
import org.briarproject.briar.api.messaging.MessagingManager;
|
||||||
import org.briarproject.briar.api.messaging.PrivateMessageHeader;
|
import org.briarproject.briar.api.messaging.PrivateMessageHeader;
|
||||||
import org.briarproject.briar.api.messaging.event.AttachmentReceivedEvent;
|
|
||||||
import org.briarproject.briar.api.privategroup.invitation.GroupInvitationManager;
|
import org.briarproject.briar.api.privategroup.invitation.GroupInvitationManager;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@@ -97,6 +95,7 @@ import androidx.appcompat.widget.Toolbar;
|
|||||||
import androidx.core.app.ActivityCompat;
|
import androidx.core.app.ActivityCompat;
|
||||||
import androidx.core.app.ActivityOptionsCompat;
|
import androidx.core.app.ActivityOptionsCompat;
|
||||||
import androidx.core.content.ContextCompat;
|
import androidx.core.content.ContextCompat;
|
||||||
|
import androidx.lifecycle.LiveData;
|
||||||
import androidx.lifecycle.Observer;
|
import androidx.lifecycle.Observer;
|
||||||
import androidx.lifecycle.ViewModelProvider;
|
import androidx.lifecycle.ViewModelProvider;
|
||||||
import androidx.lifecycle.ViewModelProviders;
|
import androidx.lifecycle.ViewModelProviders;
|
||||||
@@ -118,8 +117,6 @@ import static androidx.core.app.ActivityOptionsCompat.makeSceneTransitionAnimati
|
|||||||
import static androidx.core.view.ViewCompat.setTransitionName;
|
import static androidx.core.view.ViewCompat.setTransitionName;
|
||||||
import static androidx.lifecycle.Lifecycle.State.STARTED;
|
import static androidx.lifecycle.Lifecycle.State.STARTED;
|
||||||
import static androidx.recyclerview.widget.SortedList.INVALID_POSITION;
|
import static androidx.recyclerview.widget.SortedList.INVALID_POSITION;
|
||||||
import static java.util.Collections.emptyList;
|
|
||||||
import static java.util.Collections.singletonList;
|
|
||||||
import static java.util.Collections.sort;
|
import static java.util.Collections.sort;
|
||||||
import static java.util.Objects.requireNonNull;
|
import static java.util.Objects.requireNonNull;
|
||||||
import static java.util.logging.Level.INFO;
|
import static java.util.logging.Level.INFO;
|
||||||
@@ -136,6 +133,7 @@ import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_INTRO
|
|||||||
import static org.briarproject.briar.android.conversation.ImageActivity.ATTACHMENTS;
|
import static org.briarproject.briar.android.conversation.ImageActivity.ATTACHMENTS;
|
||||||
import static org.briarproject.briar.android.conversation.ImageActivity.ATTACHMENT_POSITION;
|
import static org.briarproject.briar.android.conversation.ImageActivity.ATTACHMENT_POSITION;
|
||||||
import static org.briarproject.briar.android.conversation.ImageActivity.DATE;
|
import static org.briarproject.briar.android.conversation.ImageActivity.DATE;
|
||||||
|
import static org.briarproject.briar.android.conversation.ImageActivity.ITEM_ID;
|
||||||
import static org.briarproject.briar.android.conversation.ImageActivity.NAME;
|
import static org.briarproject.briar.android.conversation.ImageActivity.NAME;
|
||||||
import static org.briarproject.briar.android.util.UiUtils.getAvatarTransitionName;
|
import static org.briarproject.briar.android.util.UiUtils.getAvatarTransitionName;
|
||||||
import static org.briarproject.briar.android.util.UiUtils.getBulbTransitionName;
|
import static org.briarproject.briar.android.util.UiUtils.getBulbTransitionName;
|
||||||
@@ -185,8 +183,6 @@ public class ConversationActivity extends BriarActivity
|
|||||||
volatile GroupInvitationManager groupInvitationManager;
|
volatile GroupInvitationManager groupInvitationManager;
|
||||||
|
|
||||||
private final Map<MessageId, String> textCache = new ConcurrentHashMap<>();
|
private final Map<MessageId, String> textCache = new ConcurrentHashMap<>();
|
||||||
private final Map<MessageId, PrivateMessageHeader> missingAttachments =
|
|
||||||
new ConcurrentHashMap<>();
|
|
||||||
private final Observer<String> contactNameObserver = name -> {
|
private final Observer<String> contactNameObserver = name -> {
|
||||||
requireNonNull(name);
|
requireNonNull(name);
|
||||||
loadMessages();
|
loadMessages();
|
||||||
@@ -540,6 +536,7 @@ public class ConversationActivity extends BriarActivity
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@DatabaseExecutor
|
||||||
private void eagerlyLoadMessageSize(PrivateMessageHeader h) {
|
private void eagerlyLoadMessageSize(PrivateMessageHeader h) {
|
||||||
try {
|
try {
|
||||||
MessageId id = h.getId();
|
MessageId id = h.getId();
|
||||||
@@ -556,21 +553,11 @@ public class ConversationActivity extends BriarActivity
|
|||||||
// images we use a grid so the size is fixed
|
// images we use a grid so the size is fixed
|
||||||
List<AttachmentHeader> headers = h.getAttachmentHeaders();
|
List<AttachmentHeader> headers = h.getAttachmentHeaders();
|
||||||
if (headers.size() == 1) {
|
if (headers.size() == 1) {
|
||||||
List<AttachmentItem> items = attachmentRetriever.cacheGet(id);
|
LOG.info("Eagerly loading image size for latest message");
|
||||||
if (items == null) {
|
AttachmentHeader header = headers.get(0);
|
||||||
LOG.info("Eagerly loading image size for latest message");
|
// get the item to retrieve its size
|
||||||
AttachmentHeader header = headers.get(0);
|
attachmentRetriever
|
||||||
try {
|
.cacheAttachmentItemWithSize(h.getId(), header);
|
||||||
Attachment a = attachmentRetriever
|
|
||||||
.getMessageAttachment(header);
|
|
||||||
AttachmentItem item =
|
|
||||||
attachmentRetriever.getAttachmentItem(a, true);
|
|
||||||
attachmentRetriever.cachePut(id, singletonList(item));
|
|
||||||
} catch (NoSuchMessageException e) {
|
|
||||||
LOG.info("Attachment not received yet");
|
|
||||||
missingAttachments.put(header.getMessageId(), h);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} catch (DbException e) {
|
} catch (DbException e) {
|
||||||
logException(LOG, WARNING, e);
|
logException(LOG, WARNING, e);
|
||||||
@@ -651,59 +638,18 @@ public class ConversationActivity extends BriarActivity
|
|||||||
&& adapter.isScrolledToBottom(layoutManager);
|
&& adapter.isScrolledToBottom(layoutManager);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void loadMessageAttachments(PrivateMessageHeader h) {
|
@UiThread
|
||||||
// TODO: Use placeholders for missing/invalid attachments
|
private void updateMessageAttachment(MessageId m, AttachmentItem item) {
|
||||||
runOnDbThread(() -> {
|
Pair<Integer, ConversationMessageItem> pair = adapter.getMessageItem(m);
|
||||||
try {
|
if (pair != null && pair.getSecond().updateAttachments(item)) {
|
||||||
// TODO move getting the items off to IoExecutor, if size == 1
|
boolean scroll = shouldScrollWhenUpdatingMessage();
|
||||||
List<AttachmentHeader> headers = h.getAttachmentHeaders();
|
adapter.notifyItemChanged(pair.getFirst());
|
||||||
boolean needsSize = headers.size() == 1;
|
if (scroll) scrollToBottom();
|
||||||
List<AttachmentItem> items = new ArrayList<>(headers.size());
|
}
|
||||||
for (AttachmentHeader header : headers) {
|
|
||||||
try {
|
|
||||||
Attachment a = attachmentRetriever
|
|
||||||
.getMessageAttachment(header);
|
|
||||||
AttachmentItem item = attachmentRetriever
|
|
||||||
.getAttachmentItem(a, needsSize);
|
|
||||||
items.add(item);
|
|
||||||
} catch (NoSuchMessageException e) {
|
|
||||||
LOG.info("Attachment not received yet");
|
|
||||||
missingAttachments.put(header.getMessageId(), h);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Don't cache items unless all are present and valid
|
|
||||||
attachmentRetriever.cachePut(h.getId(), items);
|
|
||||||
displayMessageAttachments(h.getId(), items);
|
|
||||||
} catch (DbException e) {
|
|
||||||
logException(LOG, WARNING, e);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void displayMessageAttachments(MessageId m,
|
|
||||||
List<AttachmentItem> items) {
|
|
||||||
runOnUiThreadUnlessDestroyed(() -> {
|
|
||||||
Pair<Integer, ConversationMessageItem> pair =
|
|
||||||
adapter.getMessageItem(m);
|
|
||||||
if (pair != null) {
|
|
||||||
boolean scroll = shouldScrollWhenUpdatingMessage();
|
|
||||||
pair.getSecond().setAttachments(items);
|
|
||||||
adapter.notifyItemChanged(pair.getFirst());
|
|
||||||
if (scroll) scrollToBottom();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void eventOccurred(Event e) {
|
public void eventOccurred(Event e) {
|
||||||
if (e instanceof AttachmentReceivedEvent) {
|
|
||||||
AttachmentReceivedEvent a = (AttachmentReceivedEvent) e;
|
|
||||||
if (a.getContactId().equals(contactId)) {
|
|
||||||
LOG.info("Attachment received");
|
|
||||||
onAttachmentReceived(a.getMessageId());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (e instanceof ContactRemovedEvent) {
|
if (e instanceof ContactRemovedEvent) {
|
||||||
ContactRemovedEvent c = (ContactRemovedEvent) e;
|
ContactRemovedEvent c = (ContactRemovedEvent) e;
|
||||||
if (c.getContactId().equals(contactId)) {
|
if (c.getContactId().equals(contactId)) {
|
||||||
@@ -763,15 +709,6 @@ public class ConversationActivity extends BriarActivity
|
|||||||
scrollToBottom();
|
scrollToBottom();
|
||||||
}
|
}
|
||||||
|
|
||||||
@UiThread
|
|
||||||
private void onAttachmentReceived(MessageId attachmentId) {
|
|
||||||
PrivateMessageHeader h = missingAttachments.remove(attachmentId);
|
|
||||||
if (h != null) {
|
|
||||||
LOG.info("Missing attachment received");
|
|
||||||
loadMessageAttachments(h);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@UiThread
|
@UiThread
|
||||||
private void onNewConversationMessage(ConversationMessageHeader h) {
|
private void onNewConversationMessage(ConversationMessageHeader h) {
|
||||||
if (h instanceof ConversationRequest ||
|
if (h instanceof ConversationRequest ||
|
||||||
@@ -780,7 +717,7 @@ public class ConversationActivity extends BriarActivity
|
|||||||
observeOnce(viewModel.getContactDisplayName(), this,
|
observeOnce(viewModel.getContactDisplayName(), this,
|
||||||
name -> addConversationItem(h.accept(visitor)));
|
name -> addConversationItem(h.accept(visitor)));
|
||||||
} else {
|
} else {
|
||||||
// visitor also loads message text (if existing)
|
// visitor also loads message text and attachments (if existing)
|
||||||
addConversationItem(h.accept(visitor));
|
addConversationItem(h.accept(visitor));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1107,8 +1044,9 @@ public class ConversationActivity extends BriarActivity
|
|||||||
i.putExtra(ATTACHMENT_POSITION, attachments.indexOf(item));
|
i.putExtra(ATTACHMENT_POSITION, attachments.indexOf(item));
|
||||||
i.putExtra(NAME, name);
|
i.putExtra(NAME, name);
|
||||||
i.putExtra(DATE, messageItem.getTime());
|
i.putExtra(DATE, messageItem.getTime());
|
||||||
|
i.putExtra(ITEM_ID, messageItem.getId().getBytes());
|
||||||
// restoring list position should not trigger android bug #224270
|
// restoring list position should not trigger android bug #224270
|
||||||
String transitionName = item.getTransitionName();
|
String transitionName = item.getTransitionName(messageItem.getId());
|
||||||
ActivityOptionsCompat options =
|
ActivityOptionsCompat options =
|
||||||
makeSceneTransitionAnimation(this, view, transitionName);
|
makeSceneTransitionAnimation(this, view, transitionName);
|
||||||
ActivityCompat.startActivity(this, i, options.toBundle());
|
ActivityCompat.startActivity(this, i, options.toBundle());
|
||||||
@@ -1147,15 +1085,41 @@ public class ConversationActivity extends BriarActivity
|
|||||||
return text;
|
return text;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called by {@link PrivateMessageHeader#accept(ConversationMessageVisitor)}
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public List<AttachmentItem> getAttachmentItems(PrivateMessageHeader h) {
|
public List<AttachmentItem> getAttachmentItems(PrivateMessageHeader h) {
|
||||||
List<AttachmentItem> attachments =
|
List<LiveData<AttachmentItem>> liveDataList =
|
||||||
attachmentRetriever.cacheGet(h.getId());
|
attachmentRetriever.getAttachmentItems(h);
|
||||||
if (attachments == null) {
|
List<AttachmentItem> items = new ArrayList<>(liveDataList.size());
|
||||||
loadMessageAttachments(h);
|
for (LiveData<AttachmentItem> liveData : liveDataList) {
|
||||||
return emptyList();
|
// first remove all our observers to avoid having more than one
|
||||||
|
// in case we reload the conversation, e.g. after deleting messages
|
||||||
|
liveData.removeObservers(this);
|
||||||
|
// add a new observer
|
||||||
|
liveData.observe(this, new AttachmentObserver(h.getId(), liveData));
|
||||||
|
items.add(requireNonNull(liveData.getValue()));
|
||||||
|
}
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
|
||||||
|
private class AttachmentObserver implements Observer<AttachmentItem> {
|
||||||
|
private final MessageId conversationMessageId;
|
||||||
|
private final LiveData<AttachmentItem> liveData;
|
||||||
|
|
||||||
|
private AttachmentObserver(MessageId conversationMessageId,
|
||||||
|
LiveData<AttachmentItem> liveData) {
|
||||||
|
this.conversationMessageId = conversationMessageId;
|
||||||
|
this.liveData = liveData;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onChanged(AttachmentItem attachmentItem) {
|
||||||
|
updateMessageAttachment(conversationMessageId, attachmentItem);
|
||||||
|
if (attachmentItem.getState().isFinal())
|
||||||
|
liveData.removeObserver(this);
|
||||||
}
|
}
|
||||||
return attachments;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,12 +9,13 @@ import java.util.List;
|
|||||||
import javax.annotation.concurrent.NotThreadSafe;
|
import javax.annotation.concurrent.NotThreadSafe;
|
||||||
|
|
||||||
import androidx.annotation.LayoutRes;
|
import androidx.annotation.LayoutRes;
|
||||||
|
import androidx.annotation.UiThread;
|
||||||
|
|
||||||
@NotThreadSafe
|
@NotThreadSafe
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
class ConversationMessageItem extends ConversationItem {
|
class ConversationMessageItem extends ConversationItem {
|
||||||
|
|
||||||
private List<AttachmentItem> attachments;
|
private final List<AttachmentItem> attachments;
|
||||||
|
|
||||||
ConversationMessageItem(@LayoutRes int layoutRes, PrivateMessageHeader h,
|
ConversationMessageItem(@LayoutRes int layoutRes, PrivateMessageHeader h,
|
||||||
List<AttachmentItem> attachments) {
|
List<AttachmentItem> attachments) {
|
||||||
@@ -26,8 +27,14 @@ class ConversationMessageItem extends ConversationItem {
|
|||||||
return attachments;
|
return attachments;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setAttachments(List<AttachmentItem> attachments) {
|
@UiThread
|
||||||
this.attachments = attachments;
|
boolean updateAttachments(AttachmentItem item) {
|
||||||
|
int pos = attachments.indexOf(item);
|
||||||
|
if (pos != -1 && attachments.get(pos).getState() != item.getState()) {
|
||||||
|
attachments.set(pos, item);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,9 @@ import org.briarproject.bramble.api.db.DatabaseExecutor;
|
|||||||
import org.briarproject.bramble.api.db.DbException;
|
import org.briarproject.bramble.api.db.DbException;
|
||||||
import org.briarproject.bramble.api.db.NoSuchContactException;
|
import org.briarproject.bramble.api.db.NoSuchContactException;
|
||||||
import org.briarproject.bramble.api.db.TransactionManager;
|
import org.briarproject.bramble.api.db.TransactionManager;
|
||||||
|
import org.briarproject.bramble.api.event.Event;
|
||||||
|
import org.briarproject.bramble.api.event.EventBus;
|
||||||
|
import org.briarproject.bramble.api.event.EventListener;
|
||||||
import org.briarproject.bramble.api.identity.AuthorId;
|
import org.briarproject.bramble.api.identity.AuthorId;
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.bramble.api.settings.Settings;
|
import org.briarproject.bramble.api.settings.Settings;
|
||||||
@@ -30,6 +33,7 @@ import org.briarproject.briar.api.messaging.MessagingManager;
|
|||||||
import org.briarproject.briar.api.messaging.PrivateMessage;
|
import org.briarproject.briar.api.messaging.PrivateMessage;
|
||||||
import org.briarproject.briar.api.messaging.PrivateMessageFactory;
|
import org.briarproject.briar.api.messaging.PrivateMessageFactory;
|
||||||
import org.briarproject.briar.api.messaging.PrivateMessageHeader;
|
import org.briarproject.briar.api.messaging.PrivateMessageHeader;
|
||||||
|
import org.briarproject.briar.api.messaging.event.AttachmentReceivedEvent;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -56,7 +60,7 @@ import static org.briarproject.briar.android.util.UiUtils.observeForeverOnce;
|
|||||||
|
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
public class ConversationViewModel extends AndroidViewModel
|
public class ConversationViewModel extends AndroidViewModel
|
||||||
implements AttachmentManager {
|
implements EventListener, AttachmentManager {
|
||||||
|
|
||||||
private static Logger LOG =
|
private static Logger LOG =
|
||||||
getLogger(ConversationViewModel.class.getName());
|
getLogger(ConversationViewModel.class.getName());
|
||||||
@@ -69,6 +73,7 @@ public class ConversationViewModel extends AndroidViewModel
|
|||||||
@DatabaseExecutor
|
@DatabaseExecutor
|
||||||
private final Executor dbExecutor;
|
private final Executor dbExecutor;
|
||||||
private final TransactionManager db;
|
private final TransactionManager db;
|
||||||
|
private final EventBus eventBus;
|
||||||
private final MessagingManager messagingManager;
|
private final MessagingManager messagingManager;
|
||||||
private final ContactManager contactManager;
|
private final ContactManager contactManager;
|
||||||
private final SettingsManager settingsManager;
|
private final SettingsManager settingsManager;
|
||||||
@@ -101,6 +106,7 @@ public class ConversationViewModel extends AndroidViewModel
|
|||||||
ConversationViewModel(Application application,
|
ConversationViewModel(Application application,
|
||||||
@DatabaseExecutor Executor dbExecutor,
|
@DatabaseExecutor Executor dbExecutor,
|
||||||
TransactionManager db,
|
TransactionManager db,
|
||||||
|
EventBus eventBus,
|
||||||
MessagingManager messagingManager,
|
MessagingManager messagingManager,
|
||||||
ContactManager contactManager,
|
ContactManager contactManager,
|
||||||
SettingsManager settingsManager,
|
SettingsManager settingsManager,
|
||||||
@@ -110,6 +116,7 @@ public class ConversationViewModel extends AndroidViewModel
|
|||||||
super(application);
|
super(application);
|
||||||
this.dbExecutor = dbExecutor;
|
this.dbExecutor = dbExecutor;
|
||||||
this.db = db;
|
this.db = db;
|
||||||
|
this.eventBus = eventBus;
|
||||||
this.messagingManager = messagingManager;
|
this.messagingManager = messagingManager;
|
||||||
this.contactManager = contactManager;
|
this.contactManager = contactManager;
|
||||||
this.settingsManager = settingsManager;
|
this.settingsManager = settingsManager;
|
||||||
@@ -119,12 +126,27 @@ public class ConversationViewModel extends AndroidViewModel
|
|||||||
messagingGroupId = Transformations
|
messagingGroupId = Transformations
|
||||||
.map(contact, c -> messagingManager.getContactGroup(c).getId());
|
.map(contact, c -> messagingManager.getContactGroup(c).getId());
|
||||||
contactDeleted.setValue(false);
|
contactDeleted.setValue(false);
|
||||||
|
|
||||||
|
eventBus.addListener(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCleared() {
|
protected void onCleared() {
|
||||||
super.onCleared();
|
super.onCleared();
|
||||||
attachmentCreator.deleteUnsentAttachments();
|
attachmentCreator.cancel(); // also deletes unsent attachments
|
||||||
|
eventBus.removeListener(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void eventOccurred(Event e) {
|
||||||
|
if (e instanceof AttachmentReceivedEvent) {
|
||||||
|
AttachmentReceivedEvent a = (AttachmentReceivedEvent) e;
|
||||||
|
if (a.getContactId().equals(contactId)) {
|
||||||
|
LOG.info("Attachment received");
|
||||||
|
dbExecutor.execute(() -> attachmentRetriever
|
||||||
|
.loadAttachmentItem(a.getMessageId()));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -252,6 +274,7 @@ public class ConversationViewModel extends AndroidViewModel
|
|||||||
settingsManager.mergeSettings(settings, SETTINGS_NAMESPACE);
|
settingsManager.mergeSettings(settings, SETTINGS_NAMESPACE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@UiThread
|
||||||
private void createMessage(GroupId groupId, @Nullable String text,
|
private void createMessage(GroupId groupId, @Nullable String text,
|
||||||
List<AttachmentHeader> headers, long timestamp,
|
List<AttachmentHeader> headers, long timestamp,
|
||||||
boolean hasImageSupport) {
|
boolean hasImageSupport) {
|
||||||
@@ -270,6 +293,7 @@ public class ConversationViewModel extends AndroidViewModel
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@UiThread
|
||||||
private void storeMessage(PrivateMessage m) {
|
private void storeMessage(PrivateMessage m) {
|
||||||
attachmentCreator.onAttachmentsSent(m.getMessage().getId());
|
attachmentCreator.onAttachmentsSent(m.getMessage().getId());
|
||||||
dbExecutor.execute(() -> {
|
dbExecutor.execute(() -> {
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import com.google.android.material.appbar.AppBarLayout;
|
|||||||
|
|
||||||
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
||||||
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
||||||
|
import org.briarproject.bramble.api.sync.MessageId;
|
||||||
import org.briarproject.briar.R;
|
import org.briarproject.briar.R;
|
||||||
import org.briarproject.briar.android.activity.ActivityComponent;
|
import org.briarproject.briar.android.activity.ActivityComponent;
|
||||||
import org.briarproject.briar.android.activity.BriarActivity;
|
import org.briarproject.briar.android.activity.BriarActivity;
|
||||||
@@ -67,6 +68,7 @@ public class ImageActivity extends BriarActivity
|
|||||||
final static String ATTACHMENT_POSITION = "position";
|
final static String ATTACHMENT_POSITION = "position";
|
||||||
final static String NAME = "name";
|
final static String NAME = "name";
|
||||||
final static String DATE = "date";
|
final static String DATE = "date";
|
||||||
|
final static String ITEM_ID = "itemId";
|
||||||
|
|
||||||
@RequiresApi(api = 16)
|
@RequiresApi(api = 16)
|
||||||
private final static int UI_FLAGS_DEFAULT =
|
private final static int UI_FLAGS_DEFAULT =
|
||||||
@@ -80,6 +82,7 @@ public class ImageActivity extends BriarActivity
|
|||||||
private AppBarLayout appBarLayout;
|
private AppBarLayout appBarLayout;
|
||||||
private ViewPager viewPager;
|
private ViewPager viewPager;
|
||||||
private List<AttachmentItem> attachments;
|
private List<AttachmentItem> attachments;
|
||||||
|
private MessageId conversationMessageId;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void injectActivity(ActivityComponent component) {
|
public void injectActivity(ActivityComponent component) {
|
||||||
@@ -98,9 +101,20 @@ public class ImageActivity extends BriarActivity
|
|||||||
setSceneTransitionAnimation(transition, null, transition);
|
setSceneTransitionAnimation(transition, null, transition);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Intent Extras
|
||||||
|
Intent i = getIntent();
|
||||||
|
attachments =
|
||||||
|
requireNonNull(i.getParcelableArrayListExtra(ATTACHMENTS));
|
||||||
|
int position = i.getIntExtra(ATTACHMENT_POSITION, -1);
|
||||||
|
if (position == -1) throw new IllegalStateException();
|
||||||
|
String name = i.getStringExtra(NAME);
|
||||||
|
long time = i.getLongExtra(DATE, 0);
|
||||||
|
byte[] messageIdBytes = requireNonNull(i.getByteArrayExtra(ITEM_ID));
|
||||||
|
|
||||||
// get View Model
|
// get View Model
|
||||||
viewModel = ViewModelProviders.of(this, viewModelFactory)
|
viewModel = ViewModelProviders.of(this, viewModelFactory)
|
||||||
.get(ImageViewModel.class);
|
.get(ImageViewModel.class);
|
||||||
|
viewModel.expectAttachments(attachments);
|
||||||
viewModel.getSaveState().observeEvent(this,
|
viewModel.getSaveState().observeEvent(this,
|
||||||
this::onImageSaveStateChanged);
|
this::onImageSaveStateChanged);
|
||||||
|
|
||||||
@@ -124,16 +138,11 @@ public class ImageActivity extends BriarActivity
|
|||||||
TextView contactName = toolbar.findViewById(R.id.contactName);
|
TextView contactName = toolbar.findViewById(R.id.contactName);
|
||||||
TextView dateView = toolbar.findViewById(R.id.dateView);
|
TextView dateView = toolbar.findViewById(R.id.dateView);
|
||||||
|
|
||||||
// Intent Extras
|
// Set contact name and message time
|
||||||
Intent i = getIntent();
|
|
||||||
attachments = i.getParcelableArrayListExtra(ATTACHMENTS);
|
|
||||||
int position = i.getIntExtra(ATTACHMENT_POSITION, -1);
|
|
||||||
if (position == -1) throw new IllegalStateException();
|
|
||||||
String name = i.getStringExtra(NAME);
|
|
||||||
long time = i.getLongExtra(DATE, 0);
|
|
||||||
String date = formatDateAbsolute(this, time);
|
String date = formatDateAbsolute(this, time);
|
||||||
contactName.setText(name);
|
contactName.setText(name);
|
||||||
dateView.setText(date);
|
dateView.setText(date);
|
||||||
|
conversationMessageId = new MessageId(messageIdBytes);
|
||||||
|
|
||||||
// Set up image ViewPager
|
// Set up image ViewPager
|
||||||
viewPager = findViewById(R.id.viewPager);
|
viewPager = findViewById(R.id.viewPager);
|
||||||
@@ -320,8 +329,8 @@ public class ImageActivity extends BriarActivity
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Fragment getItem(int position) {
|
public Fragment getItem(int position) {
|
||||||
Fragment f = ImageFragment
|
Fragment f = ImageFragment.newInstance(
|
||||||
.newInstance(attachments.get(position), isFirst);
|
attachments.get(position), conversationMessageId, isFirst);
|
||||||
isFirst = false;
|
isFirst = false;
|
||||||
return f;
|
return f;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,7 +49,8 @@ class ImageAdapter extends Adapter<ImageViewHolder> {
|
|||||||
public ImageViewHolder onCreateViewHolder(ViewGroup viewGroup, int type) {
|
public ImageViewHolder onCreateViewHolder(ViewGroup viewGroup, int type) {
|
||||||
View v = LayoutInflater.from(viewGroup.getContext()).inflate(
|
View v = LayoutInflater.from(viewGroup.getContext()).inflate(
|
||||||
R.layout.list_item_image, viewGroup, false);
|
R.layout.list_item_image, viewGroup, false);
|
||||||
return new ImageViewHolder(v, imageSize);
|
requireNonNull(conversationItem);
|
||||||
|
return new ImageViewHolder(v, imageSize, conversationItem.getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import com.bumptech.glide.request.target.Target;
|
|||||||
import com.github.chrisbanes.photoview.PhotoView;
|
import com.github.chrisbanes.photoview.PhotoView;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
||||||
|
import org.briarproject.bramble.api.sync.MessageId;
|
||||||
import org.briarproject.briar.R;
|
import org.briarproject.briar.R;
|
||||||
import org.briarproject.briar.android.activity.BaseActivity;
|
import org.briarproject.briar.android.activity.BaseActivity;
|
||||||
import org.briarproject.briar.android.attachment.AttachmentItem;
|
import org.briarproject.briar.android.attachment.AttachmentItem;
|
||||||
@@ -23,6 +24,7 @@ import org.briarproject.briar.android.conversation.glide.GlideApp;
|
|||||||
import javax.annotation.ParametersAreNonnullByDefault;
|
import javax.annotation.ParametersAreNonnullByDefault;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import androidx.annotation.DrawableRes;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.fragment.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
import androidx.lifecycle.ViewModelProvider;
|
import androidx.lifecycle.ViewModelProvider;
|
||||||
@@ -32,27 +34,36 @@ import static android.os.Build.VERSION.SDK_INT;
|
|||||||
import static android.widget.ImageView.ScaleType.FIT_START;
|
import static android.widget.ImageView.ScaleType.FIT_START;
|
||||||
import static com.bumptech.glide.load.engine.DiskCacheStrategy.NONE;
|
import static com.bumptech.glide.load.engine.DiskCacheStrategy.NONE;
|
||||||
import static org.briarproject.bramble.api.nullsafety.NullSafety.requireNonNull;
|
import static org.briarproject.bramble.api.nullsafety.NullSafety.requireNonNull;
|
||||||
|
import static org.briarproject.briar.android.attachment.AttachmentItem.State.AVAILABLE;
|
||||||
|
import static org.briarproject.briar.android.attachment.AttachmentItem.State.ERROR;
|
||||||
import static org.briarproject.briar.android.conversation.ImageActivity.ATTACHMENT_POSITION;
|
import static org.briarproject.briar.android.conversation.ImageActivity.ATTACHMENT_POSITION;
|
||||||
|
import static org.briarproject.briar.android.conversation.ImageActivity.ITEM_ID;
|
||||||
|
|
||||||
@MethodsNotNullByDefault
|
@MethodsNotNullByDefault
|
||||||
@ParametersAreNonnullByDefault
|
@ParametersAreNonnullByDefault
|
||||||
public class ImageFragment extends Fragment {
|
public class ImageFragment extends Fragment
|
||||||
|
implements RequestListener<Drawable> {
|
||||||
|
|
||||||
private final static String IS_FIRST = "isFirst";
|
private final static String IS_FIRST = "isFirst";
|
||||||
|
@DrawableRes
|
||||||
|
private static final int ERROR_RES = R.drawable.ic_image_broken;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
ViewModelProvider.Factory viewModelFactory;
|
ViewModelProvider.Factory viewModelFactory;
|
||||||
|
|
||||||
private AttachmentItem attachment;
|
private AttachmentItem attachment;
|
||||||
private boolean isFirst;
|
private boolean isFirst;
|
||||||
|
private MessageId conversationItemId;
|
||||||
private ImageViewModel viewModel;
|
private ImageViewModel viewModel;
|
||||||
private PhotoView photoView;
|
private PhotoView photoView;
|
||||||
|
|
||||||
static ImageFragment newInstance(AttachmentItem a, boolean isFirst) {
|
static ImageFragment newInstance(AttachmentItem a,
|
||||||
|
MessageId conversationMessageId, boolean isFirst) {
|
||||||
ImageFragment f = new ImageFragment();
|
ImageFragment f = new ImageFragment();
|
||||||
Bundle args = new Bundle();
|
Bundle args = new Bundle();
|
||||||
args.putParcelable(ATTACHMENT_POSITION, a);
|
args.putParcelable(ATTACHMENT_POSITION, a);
|
||||||
args.putBoolean(IS_FIRST, isFirst);
|
args.putBoolean(IS_FIRST, isFirst);
|
||||||
|
args.putByteArray(ITEM_ID, conversationMessageId.getBytes());
|
||||||
f.setArguments(args);
|
f.setArguments(args);
|
||||||
return f;
|
return f;
|
||||||
}
|
}
|
||||||
@@ -70,6 +81,8 @@ public class ImageFragment extends Fragment {
|
|||||||
Bundle args = requireNonNull(getArguments());
|
Bundle args = requireNonNull(getArguments());
|
||||||
attachment = requireNonNull(args.getParcelable(ATTACHMENT_POSITION));
|
attachment = requireNonNull(args.getParcelable(ATTACHMENT_POSITION));
|
||||||
isFirst = args.getBoolean(IS_FIRST);
|
isFirst = args.getBoolean(IS_FIRST);
|
||||||
|
conversationItemId =
|
||||||
|
new MessageId(requireNonNull(args.getByteArray(ITEM_ID)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@@ -87,50 +100,68 @@ public class ImageFragment extends Fragment {
|
|||||||
photoView.setScaleLevels(1, 2, 4);
|
photoView.setScaleLevels(1, 2, 4);
|
||||||
photoView.setOnClickListener(view -> viewModel.clickImage());
|
photoView.setOnClickListener(view -> viewModel.clickImage());
|
||||||
|
|
||||||
// Request Listener
|
if (attachment.getState() == AVAILABLE) {
|
||||||
RequestListener<Drawable> listener = new RequestListener<Drawable>() {
|
loadImage();
|
||||||
|
// postponed transition will be started when Image was loaded
|
||||||
@Override
|
} else if (attachment.getState() == ERROR) {
|
||||||
public boolean onLoadFailed(@Nullable GlideException e,
|
photoView.setImageResource(ERROR_RES);
|
||||||
Object model, Target<Drawable> target,
|
startPostponedTransition();
|
||||||
boolean isFirstResource) {
|
} else {
|
||||||
if (getActivity() != null && isFirst)
|
photoView.setImageResource(R.drawable.ic_image_missing);
|
||||||
getActivity().supportStartPostponedEnterTransition();
|
startPostponedTransition();
|
||||||
return false;
|
// state is not final, so observe state changes
|
||||||
}
|
viewModel.getOnAttachmentReceived(attachment.getMessageId())
|
||||||
|
.observeEvent(this, this::onAttachmentReceived);
|
||||||
@Override
|
}
|
||||||
public boolean onResourceReady(Drawable resource, Object model,
|
|
||||||
Target<Drawable> target, DataSource dataSource,
|
|
||||||
boolean isFirstResource) {
|
|
||||||
if (SDK_INT >= 21 && !(resource instanceof Animatable)) {
|
|
||||||
// set transition name only when not animatable,
|
|
||||||
// because the animation won't start otherwise
|
|
||||||
photoView.setTransitionName(
|
|
||||||
attachment.getTransitionName());
|
|
||||||
}
|
|
||||||
// Move image to the top if overlapping toolbar
|
|
||||||
if (viewModel.isOverlappingToolbar(photoView, resource)) {
|
|
||||||
photoView.setScaleType(FIT_START);
|
|
||||||
}
|
|
||||||
if (getActivity() != null && isFirst) {
|
|
||||||
getActivity().supportStartPostponedEnterTransition();
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Load Image
|
|
||||||
GlideApp.with(this)
|
|
||||||
.load(attachment)
|
|
||||||
// TODO allow if size < maxTextureSize ?
|
|
||||||
// .override(SIZE_ORIGINAL)
|
|
||||||
.diskCacheStrategy(NONE)
|
|
||||||
.error(R.drawable.ic_image_broken)
|
|
||||||
.addListener(listener)
|
|
||||||
.into(photoView);
|
|
||||||
|
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void loadImage() {
|
||||||
|
GlideApp.with(this)
|
||||||
|
.load(attachment)
|
||||||
|
// TODO allow if size < maxTextureSize ?
|
||||||
|
// .override(SIZE_ORIGINAL)
|
||||||
|
.diskCacheStrategy(NONE)
|
||||||
|
.error(ERROR_RES)
|
||||||
|
.addListener(this)
|
||||||
|
.into(photoView);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onAttachmentReceived(Boolean received) {
|
||||||
|
if (received) loadImage();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onLoadFailed(@Nullable GlideException e,
|
||||||
|
Object model, Target<Drawable> target,
|
||||||
|
boolean isFirstResource) {
|
||||||
|
startPostponedTransition();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onResourceReady(Drawable resource, Object model,
|
||||||
|
Target<Drawable> target, DataSource dataSource,
|
||||||
|
boolean isFirstResource) {
|
||||||
|
if (SDK_INT >= 21 && !(resource instanceof Animatable)) {
|
||||||
|
// set transition name only when not animatable,
|
||||||
|
// because the animation won't start otherwise
|
||||||
|
photoView.setTransitionName(
|
||||||
|
attachment.getTransitionName(conversationItemId));
|
||||||
|
}
|
||||||
|
// Move image to the top if overlapping toolbar
|
||||||
|
if (viewModel.isOverlappingToolbar(photoView, resource)) {
|
||||||
|
photoView.setScaleType(FIT_START);
|
||||||
|
}
|
||||||
|
startPostponedTransition();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void startPostponedTransition() {
|
||||||
|
if (getActivity() != null && isFirst) {
|
||||||
|
getActivity().supportStartPostponedEnterTransition();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import android.widget.ImageView;
|
|||||||
import com.bumptech.glide.load.Transformation;
|
import com.bumptech.glide.load.Transformation;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
import org.briarproject.bramble.api.sync.MessageId;
|
||||||
import org.briarproject.briar.R;
|
import org.briarproject.briar.R;
|
||||||
import org.briarproject.briar.android.attachment.AttachmentItem;
|
import org.briarproject.briar.android.attachment.AttachmentItem;
|
||||||
import org.briarproject.briar.android.conversation.glide.BriarImageTransformation;
|
import org.briarproject.briar.android.conversation.glide.BriarImageTransformation;
|
||||||
@@ -18,8 +19,12 @@ import androidx.recyclerview.widget.RecyclerView.ViewHolder;
|
|||||||
import androidx.recyclerview.widget.StaggeredGridLayoutManager.LayoutParams;
|
import androidx.recyclerview.widget.StaggeredGridLayoutManager.LayoutParams;
|
||||||
|
|
||||||
import static android.os.Build.VERSION.SDK_INT;
|
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;
|
import static com.bumptech.glide.load.engine.DiskCacheStrategy.NONE;
|
||||||
import static com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions.withCrossFade;
|
import static com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions.withCrossFade;
|
||||||
|
import static org.briarproject.briar.android.attachment.AttachmentItem.State.AVAILABLE;
|
||||||
|
import static org.briarproject.briar.android.attachment.AttachmentItem.State.ERROR;
|
||||||
|
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
class ImageViewHolder extends ViewHolder {
|
class ImageViewHolder extends ViewHolder {
|
||||||
@@ -29,25 +34,33 @@ class ImageViewHolder extends ViewHolder {
|
|||||||
|
|
||||||
protected final ImageView imageView;
|
protected final ImageView imageView;
|
||||||
private final int imageSize;
|
private final int imageSize;
|
||||||
|
private final MessageId conversationItemId;
|
||||||
|
|
||||||
ImageViewHolder(View v, int imageSize) {
|
ImageViewHolder(View v, int imageSize, MessageId conversationItemId) {
|
||||||
super(v);
|
super(v);
|
||||||
imageView = v.findViewById(R.id.imageView);
|
imageView = v.findViewById(R.id.imageView);
|
||||||
this.imageSize = imageSize;
|
this.imageSize = imageSize;
|
||||||
|
this.conversationItemId = conversationItemId;
|
||||||
}
|
}
|
||||||
|
|
||||||
void bind(AttachmentItem attachment, Radii r, boolean single,
|
void bind(AttachmentItem attachment, Radii r, boolean single,
|
||||||
boolean needsStretch) {
|
boolean needsStretch) {
|
||||||
if (attachment.hasError()) {
|
setImageViewDimensions(attachment, single, needsStretch);
|
||||||
GlideApp.with(imageView)
|
if (attachment.getState() != AVAILABLE) {
|
||||||
.clear(imageView);
|
GlideApp.with(imageView).clear(imageView);
|
||||||
imageView.setImageResource(ERROR_RES);
|
if (attachment.getState() == ERROR) {
|
||||||
} else {
|
imageView.setImageResource(ERROR_RES);
|
||||||
setImageViewDimensions(attachment, single, needsStretch);
|
} else {
|
||||||
loadImage(attachment, r);
|
imageView.setImageResource(R.drawable.ic_image_missing);
|
||||||
if (SDK_INT >= 21) {
|
|
||||||
imageView.setTransitionName(attachment.getTransitionName());
|
|
||||||
}
|
}
|
||||||
|
imageView.setScaleType(FIT_CENTER);
|
||||||
|
} else {
|
||||||
|
loadImage(attachment, r);
|
||||||
|
imageView.setScaleType(CENTER_CROP);
|
||||||
|
}
|
||||||
|
if (SDK_INT >= 21) {
|
||||||
|
imageView.setTransitionName(
|
||||||
|
attachment.getTransitionName(conversationItemId));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,13 +7,18 @@ import android.view.View;
|
|||||||
|
|
||||||
import org.briarproject.bramble.api.db.DatabaseExecutor;
|
import org.briarproject.bramble.api.db.DatabaseExecutor;
|
||||||
import org.briarproject.bramble.api.db.DbException;
|
import org.briarproject.bramble.api.db.DbException;
|
||||||
|
import org.briarproject.bramble.api.event.Event;
|
||||||
|
import org.briarproject.bramble.api.event.EventBus;
|
||||||
|
import org.briarproject.bramble.api.event.EventListener;
|
||||||
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
import org.briarproject.bramble.api.sync.MessageId;
|
||||||
import org.briarproject.briar.android.attachment.AttachmentItem;
|
import org.briarproject.briar.android.attachment.AttachmentItem;
|
||||||
import org.briarproject.briar.android.viewmodel.LiveEvent;
|
import org.briarproject.briar.android.viewmodel.LiveEvent;
|
||||||
import org.briarproject.briar.android.viewmodel.MutableLiveEvent;
|
import org.briarproject.briar.android.viewmodel.MutableLiveEvent;
|
||||||
import org.briarproject.briar.api.messaging.Attachment;
|
import org.briarproject.briar.api.messaging.Attachment;
|
||||||
import org.briarproject.briar.api.messaging.MessagingManager;
|
import org.briarproject.briar.api.messaging.MessagingManager;
|
||||||
|
import org.briarproject.briar.api.messaging.event.AttachmentReceivedEvent;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
@@ -22,6 +27,8 @@ import java.io.InputStream;
|
|||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
@@ -35,22 +42,28 @@ import androidx.lifecycle.AndroidViewModel;
|
|||||||
import static android.media.MediaScannerConnection.scanFile;
|
import static android.media.MediaScannerConnection.scanFile;
|
||||||
import static android.os.Environment.DIRECTORY_PICTURES;
|
import static android.os.Environment.DIRECTORY_PICTURES;
|
||||||
import static android.os.Environment.getExternalStoragePublicDirectory;
|
import static android.os.Environment.getExternalStoragePublicDirectory;
|
||||||
|
import static java.util.Objects.requireNonNull;
|
||||||
import static java.util.logging.Level.WARNING;
|
import static java.util.logging.Level.WARNING;
|
||||||
import static java.util.logging.Logger.getLogger;
|
import static java.util.logging.Logger.getLogger;
|
||||||
import static org.briarproject.bramble.util.IoUtils.copyAndClose;
|
import static org.briarproject.bramble.util.IoUtils.copyAndClose;
|
||||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
import static org.briarproject.bramble.util.LogUtils.logException;
|
||||||
|
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
public class ImageViewModel extends AndroidViewModel {
|
public class ImageViewModel extends AndroidViewModel implements EventListener {
|
||||||
|
|
||||||
private static Logger LOG = getLogger(ImageViewModel.class.getName());
|
private static Logger LOG = getLogger(ImageViewModel.class.getName());
|
||||||
|
|
||||||
private final MessagingManager messagingManager;
|
private final MessagingManager messagingManager;
|
||||||
|
private final EventBus eventBus;
|
||||||
@DatabaseExecutor
|
@DatabaseExecutor
|
||||||
private final Executor dbExecutor;
|
private final Executor dbExecutor;
|
||||||
@IoExecutor
|
@IoExecutor
|
||||||
private final Executor ioExecutor;
|
private final Executor ioExecutor;
|
||||||
|
|
||||||
|
private boolean receivedAttachmentsInitialized = false;
|
||||||
|
private HashMap<MessageId, MutableLiveEvent<Boolean>> receivedAttachments =
|
||||||
|
new HashMap<>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* true means there was an error saving the image, false if image was saved.
|
* true means there was an error saving the image, false if image was saved.
|
||||||
*/
|
*/
|
||||||
@@ -62,13 +75,60 @@ public class ImageViewModel extends AndroidViewModel {
|
|||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
ImageViewModel(Application application,
|
ImageViewModel(Application application,
|
||||||
MessagingManager messagingManager,
|
MessagingManager messagingManager, EventBus eventBus,
|
||||||
@DatabaseExecutor Executor dbExecutor,
|
@DatabaseExecutor Executor dbExecutor,
|
||||||
@IoExecutor Executor ioExecutor) {
|
@IoExecutor Executor ioExecutor) {
|
||||||
super(application);
|
super(application);
|
||||||
this.messagingManager = messagingManager;
|
this.messagingManager = messagingManager;
|
||||||
|
this.eventBus = eventBus;
|
||||||
this.dbExecutor = dbExecutor;
|
this.dbExecutor = dbExecutor;
|
||||||
this.ioExecutor = ioExecutor;
|
this.ioExecutor = ioExecutor;
|
||||||
|
|
||||||
|
eventBus.addListener(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCleared() {
|
||||||
|
super.onCleared();
|
||||||
|
eventBus.removeListener(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@UiThread
|
||||||
|
@Override
|
||||||
|
public void eventOccurred(Event e) {
|
||||||
|
if (e instanceof AttachmentReceivedEvent) {
|
||||||
|
MessageId id = ((AttachmentReceivedEvent) e).getMessageId();
|
||||||
|
MutableLiveEvent<Boolean> oldEvent;
|
||||||
|
if (receivedAttachmentsInitialized) {
|
||||||
|
oldEvent = receivedAttachments.get(id);
|
||||||
|
if (oldEvent != null) oldEvent.postEvent(true);
|
||||||
|
} else {
|
||||||
|
receivedAttachments.put(id, new MutableLiveEvent<>(true));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@UiThread
|
||||||
|
public void expectAttachments(List<AttachmentItem> attachments) {
|
||||||
|
for (AttachmentItem item : attachments) {
|
||||||
|
// no need to track items that are in a final state already
|
||||||
|
if (item.getState().isFinal()) continue;
|
||||||
|
// add new live events, if not already added by eventOccurred()
|
||||||
|
MessageId id = item.getMessageId();
|
||||||
|
if (!receivedAttachments.containsKey(id)) {
|
||||||
|
receivedAttachments.put(id, new MutableLiveEvent<>());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
receivedAttachmentsInitialized = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a LiveData for attachments in a non-final state.
|
||||||
|
* Note that you need to call {@link #expectAttachments(List)} first.
|
||||||
|
*/
|
||||||
|
@UiThread
|
||||||
|
LiveEvent<Boolean> getOnAttachmentReceived(MessageId messageId) {
|
||||||
|
return requireNonNull(receivedAttachments.get(messageId));
|
||||||
}
|
}
|
||||||
|
|
||||||
void clickImage() {
|
void clickImage() {
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
|||||||
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
||||||
import org.briarproject.briar.R;
|
import org.briarproject.briar.R;
|
||||||
import org.briarproject.briar.android.activity.ActivityComponent;
|
import org.briarproject.briar.android.activity.ActivityComponent;
|
||||||
import org.briarproject.briar.android.fragment.BaseFragment;
|
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
@@ -114,9 +113,7 @@ public class ContactExchangeActivity extends KeyAgreementActivity {
|
|||||||
return getString(R.string.exchanging_contact_details);
|
return getString(R.string.exchanging_contact_details);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void showErrorFragment() {
|
private void showErrorFragment() {
|
||||||
String errorMsg = getString(R.string.connection_error_explanation);
|
showNextFragment(new ContactExchangeErrorFragment());
|
||||||
BaseFragment f = ContactExchangeErrorFragment.newInstance(errorMsg);
|
|
||||||
showNextFragment(f);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package org.briarproject.briar.android.keyagreement;
|
package org.briarproject.briar.android.keyagreement;
|
||||||
|
|
||||||
|
import android.content.Intent;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
@@ -18,7 +19,10 @@ import org.briarproject.briar.android.util.UiUtils;
|
|||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.fragment.app.FragmentActivity;
|
||||||
|
|
||||||
|
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP;
|
||||||
|
import static android.view.View.GONE;
|
||||||
import static org.briarproject.briar.android.util.UiUtils.onSingleLinkClick;
|
import static org.briarproject.briar.android.util.UiUtils.onSingleLinkClick;
|
||||||
|
|
||||||
@MethodsNotNullByDefault
|
@MethodsNotNullByDefault
|
||||||
@@ -58,13 +62,12 @@ public class ContactExchangeErrorFragment extends BaseFragment {
|
|||||||
View v = inflater.inflate(R.layout.fragment_error_contact_exchange,
|
View v = inflater.inflate(R.layout.fragment_error_contact_exchange,
|
||||||
container, false);
|
container, false);
|
||||||
|
|
||||||
// set humanized error message
|
// set optional error message
|
||||||
TextView explanation = v.findViewById(R.id.errorMessage);
|
TextView explanation = v.findViewById(R.id.errorMessage);
|
||||||
Bundle args = getArguments();
|
Bundle args = getArguments();
|
||||||
if (args == null) {
|
String errorMessage = args == null ? null : args.getString(ERROR_MSG);
|
||||||
throw new IllegalArgumentException("Use newInstance()");
|
if (errorMessage == null) explanation.setVisibility(GONE);
|
||||||
}
|
else explanation.setText(args.getString(ERROR_MSG));
|
||||||
explanation.setText(args.getString(ERROR_MSG));
|
|
||||||
|
|
||||||
// make feedback link clickable
|
// make feedback link clickable
|
||||||
TextView sendFeedback = v.findViewById(R.id.sendFeedback);
|
TextView sendFeedback = v.findViewById(R.id.sendFeedback);
|
||||||
@@ -73,7 +76,11 @@ public class ContactExchangeErrorFragment extends BaseFragment {
|
|||||||
// buttons
|
// buttons
|
||||||
Button tryAgain = v.findViewById(R.id.tryAgainButton);
|
Button tryAgain = v.findViewById(R.id.tryAgainButton);
|
||||||
tryAgain.setOnClickListener(view -> {
|
tryAgain.setOnClickListener(view -> {
|
||||||
if (getActivity() != null) getActivity().onBackPressed();
|
// Recreate the activity so we return to the intro fragment
|
||||||
|
FragmentActivity activity = requireActivity();
|
||||||
|
Intent i = new Intent(activity, ContactExchangeActivity.class);
|
||||||
|
i.setFlags(FLAG_ACTIVITY_CLEAR_TOP);
|
||||||
|
activity.startActivity(i);
|
||||||
});
|
});
|
||||||
Button cancel = v.findViewById(R.id.cancelButton);
|
Button cancel = v.findViewById(R.id.cancelButton);
|
||||||
cancel.setOnClickListener(view -> finish());
|
cancel.setOnClickListener(view -> finish());
|
||||||
|
|||||||
@@ -26,7 +26,6 @@ import org.briarproject.briar.android.fragment.BaseFragment;
|
|||||||
import org.briarproject.briar.android.fragment.BaseFragment.BaseFragmentListener;
|
import org.briarproject.briar.android.fragment.BaseFragment.BaseFragmentListener;
|
||||||
import org.briarproject.briar.android.keyagreement.IntroFragment.IntroScreenSeenListener;
|
import org.briarproject.briar.android.keyagreement.IntroFragment.IntroScreenSeenListener;
|
||||||
import org.briarproject.briar.android.keyagreement.KeyAgreementFragment.KeyAgreementEventListener;
|
import org.briarproject.briar.android.keyagreement.KeyAgreementFragment.KeyAgreementEventListener;
|
||||||
import org.briarproject.briar.android.util.UiUtils;
|
|
||||||
|
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
@@ -46,6 +45,7 @@ import static android.bluetooth.BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE;
|
|||||||
import static android.bluetooth.BluetoothAdapter.ACTION_SCAN_MODE_CHANGED;
|
import static android.bluetooth.BluetoothAdapter.ACTION_SCAN_MODE_CHANGED;
|
||||||
import static android.bluetooth.BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE;
|
import static android.bluetooth.BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE;
|
||||||
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
|
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
|
||||||
|
import static android.os.Build.VERSION.SDK_INT;
|
||||||
import static java.util.logging.Level.INFO;
|
import static java.util.logging.Level.INFO;
|
||||||
import static java.util.logging.Logger.getLogger;
|
import static java.util.logging.Logger.getLogger;
|
||||||
import static org.briarproject.bramble.api.nullsafety.NullSafety.requireNonNull;
|
import static org.briarproject.bramble.api.nullsafety.NullSafety.requireNonNull;
|
||||||
@@ -55,6 +55,7 @@ import static org.briarproject.bramble.api.plugin.Plugin.State.INACTIVE;
|
|||||||
import static org.briarproject.bramble.api.plugin.Plugin.State.STARTING_STOPPING;
|
import static org.briarproject.bramble.api.plugin.Plugin.State.STARTING_STOPPING;
|
||||||
import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_BLUETOOTH_DISCOVERABLE;
|
import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_BLUETOOTH_DISCOVERABLE;
|
||||||
import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_PERMISSION_CAMERA_LOCATION;
|
import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_PERMISSION_CAMERA_LOCATION;
|
||||||
|
import static org.briarproject.briar.android.util.UiUtils.getGoToSettingsListener;
|
||||||
|
|
||||||
@MethodsNotNullByDefault
|
@MethodsNotNullByDefault
|
||||||
@ParametersNotNullByDefault
|
@ParametersNotNullByDefault
|
||||||
@@ -133,6 +134,8 @@ public abstract class KeyAgreementActivity extends BriarActivity implements
|
|||||||
private Permission locationPermission = Permission.UNKNOWN;
|
private Permission locationPermission = Permission.UNKNOWN;
|
||||||
private BluetoothDecision bluetoothDecision = BluetoothDecision.UNKNOWN;
|
private BluetoothDecision bluetoothDecision = BluetoothDecision.UNKNOWN;
|
||||||
private BroadcastReceiver bluetoothReceiver = null;
|
private BroadcastReceiver bluetoothReceiver = null;
|
||||||
|
private Plugin wifiPlugin = null, bluetoothPlugin = null;
|
||||||
|
private BluetoothAdapter bt = null;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void injectActivity(ActivityComponent component) {
|
public void injectActivity(ActivityComponent component) {
|
||||||
@@ -152,6 +155,9 @@ public abstract class KeyAgreementActivity extends BriarActivity implements
|
|||||||
IntentFilter filter = new IntentFilter(ACTION_SCAN_MODE_CHANGED);
|
IntentFilter filter = new IntentFilter(ACTION_SCAN_MODE_CHANGED);
|
||||||
bluetoothReceiver = new BluetoothStateReceiver();
|
bluetoothReceiver = new BluetoothStateReceiver();
|
||||||
registerReceiver(bluetoothReceiver, filter);
|
registerReceiver(bluetoothReceiver, filter);
|
||||||
|
wifiPlugin = pluginManager.getPlugin(LanTcpConstants.ID);
|
||||||
|
bluetoothPlugin = pluginManager.getPlugin(BluetoothConstants.ID);
|
||||||
|
bt = BluetoothAdapter.getDefaultAdapter();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -187,6 +193,7 @@ public abstract class KeyAgreementActivity extends BriarActivity implements
|
|||||||
showQrCodeFragmentIfAllowed();
|
showQrCodeFragmentIfAllowed();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("StatementWithEmptyBody")
|
||||||
private void showQrCodeFragmentIfAllowed() {
|
private void showQrCodeFragmentIfAllowed() {
|
||||||
if (isResumed && continueClicked && areEssentialPermissionsGranted()) {
|
if (isResumed && continueClicked && areEssentialPermissionsGranted()) {
|
||||||
if (isWifiReady() && isBluetoothReady()) {
|
if (isWifiReady() && isBluetoothReady()) {
|
||||||
@@ -200,6 +207,8 @@ public abstract class KeyAgreementActivity extends BriarActivity implements
|
|||||||
}
|
}
|
||||||
if (bluetoothDecision == BluetoothDecision.UNKNOWN) {
|
if (bluetoothDecision == BluetoothDecision.UNKNOWN) {
|
||||||
requestBluetoothDiscoverable();
|
requestBluetoothDiscoverable();
|
||||||
|
} else if (bluetoothDecision == BluetoothDecision.REFUSED) {
|
||||||
|
// Ask again when the user clicks "continue"
|
||||||
} else if (shouldEnableBluetooth()) {
|
} else if (shouldEnableBluetooth()) {
|
||||||
LOG.info("Enabling Bluetooth plugin");
|
LOG.info("Enabling Bluetooth plugin");
|
||||||
hasEnabledBluetooth = true;
|
hasEnabledBluetooth = true;
|
||||||
@@ -210,55 +219,50 @@ public abstract class KeyAgreementActivity extends BriarActivity implements
|
|||||||
}
|
}
|
||||||
|
|
||||||
private boolean areEssentialPermissionsGranted() {
|
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 &&
|
return cameraPermission == Permission.GRANTED &&
|
||||||
(locationPermission == Permission.GRANTED ||
|
(SDK_INT < 23 || locationPermission == Permission.GRANTED ||
|
||||||
locationPermission == Permission.PERMANENTLY_DENIED);
|
!isBluetoothSupported());
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isBluetoothSupported() {
|
||||||
|
return bt != null && bluetoothPlugin != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isWifiReady() {
|
private boolean isWifiReady() {
|
||||||
Plugin p = pluginManager.getPlugin(LanTcpConstants.ID);
|
if (wifiPlugin == null) return true; // Continue without wifi
|
||||||
if (p == null) return true; // Continue without wifi
|
State state = wifiPlugin.getState();
|
||||||
State state = p.getState();
|
|
||||||
// Wait for plugin to become enabled
|
// Wait for plugin to become enabled
|
||||||
return state == ACTIVE || state == INACTIVE;
|
return state == ACTIVE || state == INACTIVE;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isBluetoothReady() {
|
private boolean isBluetoothReady() {
|
||||||
if (bluetoothDecision == BluetoothDecision.UNKNOWN ||
|
if (!isBluetoothSupported()) {
|
||||||
bluetoothDecision == BluetoothDecision.WAITING) {
|
|
||||||
// Wait for decision
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (bluetoothDecision == BluetoothDecision.NO_ADAPTER
|
|
||||||
|| bluetoothDecision == BluetoothDecision.REFUSED) {
|
|
||||||
// Continue without Bluetooth
|
// Continue without Bluetooth
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
BluetoothAdapter bt = BluetoothAdapter.getDefaultAdapter();
|
if (bluetoothDecision == BluetoothDecision.UNKNOWN ||
|
||||||
if (bt == null) return true; // Continue without Bluetooth
|
bluetoothDecision == BluetoothDecision.WAITING ||
|
||||||
|
bluetoothDecision == BluetoothDecision.REFUSED) {
|
||||||
|
// Wait for user to accept
|
||||||
|
return false;
|
||||||
|
}
|
||||||
if (bt.getScanMode() != SCAN_MODE_CONNECTABLE_DISCOVERABLE) {
|
if (bt.getScanMode() != SCAN_MODE_CONNECTABLE_DISCOVERABLE) {
|
||||||
// Wait for adapter to become discoverable
|
// Wait for adapter to become discoverable
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
Plugin p = pluginManager.getPlugin(BluetoothConstants.ID);
|
|
||||||
if (p == null) return true; // Continue without Bluetooth
|
|
||||||
// Wait for plugin to become active
|
// Wait for plugin to become active
|
||||||
return p.getState() == ACTIVE;
|
return bluetoothPlugin.getState() == ACTIVE;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean shouldEnableWifi() {
|
private boolean shouldEnableWifi() {
|
||||||
if (hasEnabledWifi) return false;
|
if (hasEnabledWifi) return false;
|
||||||
Plugin p = pluginManager.getPlugin(LanTcpConstants.ID);
|
if (wifiPlugin == null) return false;
|
||||||
if (p == null) return false;
|
State state = wifiPlugin.getState();
|
||||||
State state = p.getState();
|
|
||||||
return state == STARTING_STOPPING || state == DISABLED;
|
return state == STARTING_STOPPING || state == DISABLED;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void requestBluetoothDiscoverable() {
|
private void requestBluetoothDiscoverable() {
|
||||||
BluetoothAdapter bt = BluetoothAdapter.getDefaultAdapter();
|
if (!isBluetoothSupported()) {
|
||||||
if (bt == null) {
|
|
||||||
bluetoothDecision = BluetoothDecision.NO_ADAPTER;
|
bluetoothDecision = BluetoothDecision.NO_ADAPTER;
|
||||||
showQrCodeFragmentIfAllowed();
|
showQrCodeFragmentIfAllowed();
|
||||||
} else {
|
} else {
|
||||||
@@ -277,9 +281,8 @@ public abstract class KeyAgreementActivity extends BriarActivity implements
|
|||||||
private boolean shouldEnableBluetooth() {
|
private boolean shouldEnableBluetooth() {
|
||||||
if (bluetoothDecision != BluetoothDecision.ACCEPTED) return false;
|
if (bluetoothDecision != BluetoothDecision.ACCEPTED) return false;
|
||||||
if (hasEnabledBluetooth) return false;
|
if (hasEnabledBluetooth) return false;
|
||||||
Plugin p = pluginManager.getPlugin(BluetoothConstants.ID);
|
if (!isBluetoothSupported()) return false;
|
||||||
if (p == null) return false;
|
State state = bluetoothPlugin.getState();
|
||||||
State state = p.getState();
|
|
||||||
return state == STARTING_STOPPING || state == DISABLED;
|
return state == STARTING_STOPPING || state == DISABLED;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -298,6 +301,9 @@ public abstract class KeyAgreementActivity extends BriarActivity implements
|
|||||||
@Override
|
@Override
|
||||||
public void showNextScreen() {
|
public void showNextScreen() {
|
||||||
continueClicked = true;
|
continueClicked = true;
|
||||||
|
if (bluetoothDecision == BluetoothDecision.REFUSED) {
|
||||||
|
bluetoothDecision = BluetoothDecision.UNKNOWN; // Ask again
|
||||||
|
}
|
||||||
if (checkPermissions()) showQrCodeFragmentIfAllowed();
|
if (checkPermissions()) showQrCodeFragmentIfAllowed();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -341,17 +347,17 @@ public abstract class KeyAgreementActivity extends BriarActivity implements
|
|||||||
|
|
||||||
private boolean checkPermissions() {
|
private boolean checkPermissions() {
|
||||||
if (areEssentialPermissionsGranted()) return true;
|
if (areEssentialPermissionsGranted()) return true;
|
||||||
// If the camera permission has been permanently denied, ask the
|
// If an essential permission has been permanently denied, ask the
|
||||||
// user to change the setting
|
// user to change the setting
|
||||||
if (cameraPermission == Permission.PERMANENTLY_DENIED) {
|
if (cameraPermission == Permission.PERMANENTLY_DENIED) {
|
||||||
Builder builder = new Builder(this, R.style.BriarDialogTheme);
|
showDenialDialog(R.string.permission_camera_title,
|
||||||
builder.setTitle(R.string.permission_camera_title);
|
R.string.permission_camera_denied_body);
|
||||||
builder.setMessage(R.string.permission_camera_denied_body);
|
return false;
|
||||||
builder.setPositiveButton(R.string.ok,
|
}
|
||||||
UiUtils.getGoToSettingsListener(this));
|
if (isBluetoothSupported() &&
|
||||||
builder.setNegativeButton(R.string.cancel,
|
locationPermission == Permission.PERMANENTLY_DENIED) {
|
||||||
(dialog, which) -> supportFinishAfterTransition());
|
showDenialDialog(R.string.permission_location_title,
|
||||||
builder.show();
|
R.string.permission_location_denied_body);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// Should we show the rationale for one or both permissions?
|
// Should we show the rationale for one or both permissions?
|
||||||
@@ -371,6 +377,16 @@ public abstract class KeyAgreementActivity extends BriarActivity implements
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void showDenialDialog(@StringRes int title, @StringRes int body) {
|
||||||
|
Builder builder = new Builder(this, R.style.BriarDialogTheme);
|
||||||
|
builder.setTitle(title);
|
||||||
|
builder.setMessage(body);
|
||||||
|
builder.setPositiveButton(R.string.ok, getGoToSettingsListener(this));
|
||||||
|
builder.setNegativeButton(R.string.cancel,
|
||||||
|
(dialog, which) -> supportFinishAfterTransition());
|
||||||
|
builder.show();
|
||||||
|
}
|
||||||
|
|
||||||
private void showRationale(@StringRes int title, @StringRes int body) {
|
private void showRationale(@StringRes int title, @StringRes int body) {
|
||||||
Builder builder = new Builder(this, R.style.BriarDialogTheme);
|
Builder builder = new Builder(this, R.style.BriarDialogTheme);
|
||||||
builder.setTitle(title);
|
builder.setTitle(title);
|
||||||
@@ -381,8 +397,13 @@ public abstract class KeyAgreementActivity extends BriarActivity implements
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void requestPermissions() {
|
private void requestPermissions() {
|
||||||
ActivityCompat.requestPermissions(this,
|
String[] permissions;
|
||||||
new String[] {CAMERA, ACCESS_FINE_LOCATION},
|
if (isBluetoothSupported()) {
|
||||||
|
permissions = new String[] {CAMERA, ACCESS_FINE_LOCATION};
|
||||||
|
} else {
|
||||||
|
permissions = new String[] {CAMERA};
|
||||||
|
}
|
||||||
|
ActivityCompat.requestPermissions(this, permissions,
|
||||||
REQUEST_PERMISSION_CAMERA_LOCATION);
|
REQUEST_PERMISSION_CAMERA_LOCATION);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -399,12 +420,15 @@ public abstract class KeyAgreementActivity extends BriarActivity implements
|
|||||||
} else {
|
} else {
|
||||||
cameraPermission = Permission.PERMANENTLY_DENIED;
|
cameraPermission = Permission.PERMANENTLY_DENIED;
|
||||||
}
|
}
|
||||||
if (gotPermission(ACCESS_FINE_LOCATION, permissions, grantResults)) {
|
if (isBluetoothSupported()) {
|
||||||
locationPermission = Permission.GRANTED;
|
if (gotPermission(ACCESS_FINE_LOCATION, permissions,
|
||||||
} else if (shouldShowRationale(ACCESS_FINE_LOCATION)) {
|
grantResults)) {
|
||||||
locationPermission = Permission.SHOW_RATIONALE;
|
locationPermission = Permission.GRANTED;
|
||||||
} else {
|
} else if (shouldShowRationale(ACCESS_FINE_LOCATION)) {
|
||||||
locationPermission = Permission.PERMANENTLY_DENIED;
|
locationPermission = Permission.SHOW_RATIONALE;
|
||||||
|
} else {
|
||||||
|
locationPermission = Permission.PERMANENTLY_DENIED;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// If a permission dialog has been shown, showing the QR code fragment
|
// If a permission dialog has been shown, showing the QR code fragment
|
||||||
// on this call path would cause a crash due to
|
// on this call path would cause a crash due to
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ public class ImagePreview extends ConstraintLayout {
|
|||||||
((ImagePreviewAdapter) imageList.getAdapter());
|
((ImagePreviewAdapter) imageList.getAdapter());
|
||||||
int pos = requireNonNull(adapter).loadItemPreview(result);
|
int pos = requireNonNull(adapter).loadItemPreview(result);
|
||||||
if (pos != NO_POSITION) {
|
if (pos != NO_POSITION) {
|
||||||
imageList.smoothScrollToPosition(pos);
|
imageList.scrollToPosition(pos);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,22 @@ import androidx.lifecycle.Observer;
|
|||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
public class LiveEvent<T> extends LiveData<LiveEvent.ConsumableEvent<T>> {
|
public class LiveEvent<T> extends LiveData<LiveEvent.ConsumableEvent<T>> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a LiveEvent initialized with the given {@code value}.
|
||||||
|
*
|
||||||
|
* @param value initial value
|
||||||
|
*/
|
||||||
|
public LiveEvent(T value) {
|
||||||
|
super(new ConsumableEvent<>(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a LiveEvent with no value assigned to it.
|
||||||
|
*/
|
||||||
|
public LiveEvent() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
public void observeEvent(LifecycleOwner owner,
|
public void observeEvent(LifecycleOwner owner,
|
||||||
LiveEventHandler<T> handler) {
|
LiveEventHandler<T> handler) {
|
||||||
LiveEventObserver<T> observer = new LiveEventObserver<>(handler);
|
LiveEventObserver<T> observer = new LiveEventObserver<>(handler);
|
||||||
|
|||||||
@@ -5,6 +5,22 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
public class MutableLiveEvent<T> extends LiveEvent<T> {
|
public class MutableLiveEvent<T> extends LiveEvent<T> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a MutableLiveEvent initialized with the given {@code value}.
|
||||||
|
*
|
||||||
|
* @param value initial value
|
||||||
|
*/
|
||||||
|
public MutableLiveEvent(T value) {
|
||||||
|
super(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a MutableLiveEvent with no value assigned to it.
|
||||||
|
*/
|
||||||
|
public MutableLiveEvent() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
public void postEvent(T value) {
|
public void postEvent(T value) {
|
||||||
super.postValue(new ConsumableEvent<>(value));
|
super.postValue(new ConsumableEvent<>(value));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:width="200dp"
|
android:width="115dp"
|
||||||
android:height="200dp"
|
android:height="115dp"
|
||||||
android:viewportHeight="24.0"
|
android:viewportHeight="24.0"
|
||||||
android:viewportWidth="24.0">
|
android:viewportWidth="24.0">
|
||||||
<path
|
<path
|
||||||
|
|||||||
10
briar-android/src/main/res/drawable/ic_image_missing.xml
Normal file
10
briar-android/src/main/res/drawable/ic_image_missing.xml
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<vector
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="115dp"
|
||||||
|
android:height="115dp"
|
||||||
|
android:viewportHeight="24.0"
|
||||||
|
android:viewportWidth="24.0">
|
||||||
|
<path
|
||||||
|
android:fillColor="#808080"
|
||||||
|
android:pathData="M21,19V5c0,-1.1 -0.9,-2 -2,-2H5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2zM8.5,13.5l2.5,3.01L14.5,12l4.5,6H5l3.5,-4.5z"/>
|
||||||
|
</vector>
|
||||||
@@ -24,6 +24,7 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="4dp"
|
android:layout_marginTop="4dp"
|
||||||
android:textColor="?android:attr/textColorSecondary"
|
android:textColor="?android:attr/textColorSecondary"
|
||||||
|
android:textIsSelectable="true"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toBottomOf="@+id/include_in_report"
|
app:layout_constraintTop_toBottomOf="@+id/include_in_report"
|
||||||
|
|||||||
@@ -27,6 +27,7 @@
|
|||||||
<string name="enter_password">كلمة السّر</string>
|
<string name="enter_password">كلمة السّر</string>
|
||||||
<string name="try_again">كلمة السرّ خاطئة, الرجاء المحاولة مجدّدا</string>
|
<string name="try_again">كلمة السرّ خاطئة, الرجاء المحاولة مجدّدا</string>
|
||||||
<string name="dialog_title_cannot_check_password">لا يمكن التحقق من كلمة السر</string>
|
<string name="dialog_title_cannot_check_password">لا يمكن التحقق من كلمة السر</string>
|
||||||
|
<string name="dialog_message_cannot_check_password">Briar لم يتمكن من التحقق من كلمة المرور. الرجاء إعادة تشغيل جهازك من أجل جل المشكلة</string>
|
||||||
<string name="sign_in_button">تسجيل الدخول</string>
|
<string name="sign_in_button">تسجيل الدخول</string>
|
||||||
<string name="forgotten_password">نسيتُ كلمة السر</string>
|
<string name="forgotten_password">نسيتُ كلمة السر</string>
|
||||||
<string name="dialog_title_lost_password">فقدت كلمة السر</string>
|
<string name="dialog_title_lost_password">فقدت كلمة السر</string>
|
||||||
@@ -64,12 +65,36 @@
|
|||||||
<string name="lock_button">قفل التطبيق</string>
|
<string name="lock_button">قفل التطبيق</string>
|
||||||
<string name="settings_button">الإعدادات</string>
|
<string name="settings_button">الإعدادات</string>
|
||||||
<string name="sign_out_button">تسجيل الخروج</string>
|
<string name="sign_out_button">تسجيل الخروج</string>
|
||||||
|
<string name="transports_onboarding_text">إلمس هنا من أجل التحكم بطريقةالربط مع جهات الاتصال.</string>
|
||||||
<!--Transports: Tor-->
|
<!--Transports: Tor-->
|
||||||
<string name="transport_tor">إنترنت</string>
|
<string name="transport_tor">إنترنت</string>
|
||||||
|
<string name="tor_device_status_online_wifi">جهازك لديه ولوج لشبكة الانترنت عبر ال Wi-Fi </string>
|
||||||
|
<string name="tor_device_status_online_mobile">جهازك لديه ولوج لشبكة الانترنت عبر بيانات الهاتف</string>
|
||||||
|
<string name="tor_device_status_offline">جهازك ليس متصل بالانترنت</string>
|
||||||
|
<string name="tor_plugin_status_enabling">جاري اتصال Briar بالانترنت</string>
|
||||||
|
<string name="tor_plugin_status_active">Briar متصل بالانترنت</string>
|
||||||
|
<string name="tor_plugin_status_inactive">Briar لم يتمكن من الاتصال بالانترنت </string>
|
||||||
|
<string name="tor_plugin_status_disabled">إعدادات Briar لاتسمح بالاتصال بالانترنت</string>
|
||||||
|
<string name="tor_plugin_status_disabled_mobile_data">إعدادات Briar لاتسمح بالاتصال عن طريق بيانات الهاتف</string>
|
||||||
|
<string name="tor_plugin_status_disabled_battery">إعدادات Briar لاتسمح بالاتصال بالانترنت عند استخدام بطارية الهاتف</string>
|
||||||
|
<string name="tor_plugin_status_disabled_country_blocked">لايمكن استخدام Briar في هذا البلد</string>
|
||||||
<!--Transports: Wi-Fi-->
|
<!--Transports: Wi-Fi-->
|
||||||
<string name="transport_lan">واي-فاي</string>
|
<string name="transport_lan">واي-فاي</string>
|
||||||
|
<string name="transport_lan_long">نفس شبكة الWi-Fi </string>
|
||||||
|
<string name="lan_device_status_on">جهازك متصل بشبكة الWi-Fi</string>
|
||||||
|
<string name="lan_device_status_off">جهازك ليس متصل بشبكة الWi-Fi</string>
|
||||||
|
<string name="lan_plugin_status_enabling">جاري ايصال Briar بشبكة الWi-Fi</string>
|
||||||
|
<string name="lan_plugin_status_active"> Briar متصل بشبكة الWi-Fi</string>
|
||||||
|
<string name="lan_plugin_status_inactive">لم يتمكن Briar من الاتصال بشبكة ال Wi-Fi</string>
|
||||||
|
<string name="lan_plugin_status_disabled">إعدادات Briar لاتسمح بالاتصال بشبكة ال Wi-Fi</string>
|
||||||
<!--Transports: Bluetooth-->
|
<!--Transports: Bluetooth-->
|
||||||
<string name="transport_bt">بلوتوث</string>
|
<string name="transport_bt">بلوتوث</string>
|
||||||
|
<string name="bt_device_status_on"> البلوتوث مفعّل </string>
|
||||||
|
<string name="bt_device_status_off">البلوتوث مفعّل</string>
|
||||||
|
<string name="bt_plugin_status_enabling">جاري اتصال Briar بالبلوتوث</string>
|
||||||
|
<string name="bt_plugin_status_active">Briar متصل بالبلوتوث</string>
|
||||||
|
<string name="bt_plugin_status_inactive">لم يتمكن Briar من الاتصال بالانترنت</string>
|
||||||
|
<string name="bt_plugin_status_disabled">إعدادات Briar لاتسمح بالاتصال بالبلوتوث</string>
|
||||||
<!--Notifications-->
|
<!--Notifications-->
|
||||||
<string name="reminder_notification_title">تم تسجيل الخروج من Briar (براير)</string>
|
<string name="reminder_notification_title">تم تسجيل الخروج من Briar (براير)</string>
|
||||||
<string name="reminder_notification_text">الرجاء اللمس لإعادة الدخول</string>
|
<string name="reminder_notification_text">الرجاء اللمس لإعادة الدخول</string>
|
||||||
@@ -190,7 +215,6 @@
|
|||||||
<string name="connecting_to_device">يتم الإتصال بالجهاز\u2026</string>
|
<string name="connecting_to_device">يتم الإتصال بالجهاز\u2026</string>
|
||||||
<string name="authenticating_with_device">يتم التوثيق مع الجهاز\u2026</string>
|
<string name="authenticating_with_device">يتم التوثيق مع الجهاز\u2026</string>
|
||||||
<string name="connection_error_title">لم يمكن الإتصال بجهة إتصالك</string>
|
<string name="connection_error_title">لم يمكن الإتصال بجهة إتصالك</string>
|
||||||
<string name="connection_error_explanation">رجاءًا التأكد أن كليكما متصل بنفس شبكة الواي فاي.</string>
|
|
||||||
<string name="connection_error_feedback">إذا إستمرت المشكلة، رجاءًا <a href="feedback">أرسل تقرير </a> لمساعدتنا على تحسين التطبيق.</string>
|
<string name="connection_error_feedback">إذا إستمرت المشكلة، رجاءًا <a href="feedback">أرسل تقرير </a> لمساعدتنا على تحسين التطبيق.</string>
|
||||||
<!--Adding Contacts Remotely-->
|
<!--Adding Contacts Remotely-->
|
||||||
<string name="add_contact_remotely_title_case">إضافة جهة اتصال عن بعد </string>
|
<string name="add_contact_remotely_title_case">إضافة جهة اتصال عن بعد </string>
|
||||||
@@ -459,6 +483,8 @@
|
|||||||
<string name="tor_enable_summary">كل جهات الاتصال تمر عبر شبكة تور من أجل الخصوصية</string>
|
<string name="tor_enable_summary">كل جهات الاتصال تمر عبر شبكة تور من أجل الخصوصية</string>
|
||||||
<string name="tor_network_setting">وسيلة الاتصال لشبكة تور</string>
|
<string name="tor_network_setting">وسيلة الاتصال لشبكة تور</string>
|
||||||
<string name="tor_network_setting_automatic">تلقائيًا حسب الموقع</string>
|
<string name="tor_network_setting_automatic">تلقائيًا حسب الموقع</string>
|
||||||
|
<string name="tor_network_setting_without_bridges">استخدام شبكة تور من دون جسور</string>
|
||||||
|
<string name="tor_network_setting_with_bridges">استخدام شبكة تور مع جسور</string>
|
||||||
<string name="tor_network_setting_never">لا يمكن الاتصال بالإنترنت</string>
|
<string name="tor_network_setting_never">لا يمكن الاتصال بالإنترنت</string>
|
||||||
<!--How and when Briar will connect to Tor: E.g. "Don't connect to the Internet (in China)" or "Use Tor network with bridges (in Belarus)"-->
|
<!--How and when Briar will connect to Tor: E.g. "Don't connect to the Internet (in China)" or "Use Tor network with bridges (in Belarus)"-->
|
||||||
<string name="tor_network_setting_summary">تلقائيا: %1$s (في %2$s)</string>
|
<string name="tor_network_setting_summary">تلقائيا: %1$s (في %2$s)</string>
|
||||||
@@ -574,6 +600,7 @@
|
|||||||
<string name="lock_is_locked">Briar (براير) مقفل</string>
|
<string name="lock_is_locked">Briar (براير) مقفل</string>
|
||||||
<string name="lock_tap_to_unlock">الرجاء اللمس لفك القفل</string>
|
<string name="lock_tap_to_unlock">الرجاء اللمس لفك القفل</string>
|
||||||
<!--Connections Screen-->
|
<!--Connections Screen-->
|
||||||
|
<string name="transports_help_text">يمكن ل Briar التواصل مع جهات الاتصال عن طريق الانترنت, شكبة ال Wi-Fi أو البلوتوث.n\n\كل وسائل الاتصال عن طريق الانترنت تمر عبر شبكة تور من أجل الخصوصية.n\n\إذا كان من الممكن الوصول إلى شبكة إتصال بعدة طرق فإن Briar سوف يستعملهم جميعاً بالتوازي.</string>
|
||||||
<!--Screenshots-->
|
<!--Screenshots-->
|
||||||
<!--This is a name to be used in screenshots. Feel free to change it to a local name.-->
|
<!--This is a name to be used in screenshots. Feel free to change it to a local name.-->
|
||||||
<string name="screenshot_alice">آليس</string>
|
<string name="screenshot_alice">آليس</string>
|
||||||
|
|||||||
@@ -162,7 +162,6 @@
|
|||||||
<string name="connecting_to_device">Cihaza qoşulma /2026</string>
|
<string name="connecting_to_device">Cihaza qoşulma /2026</string>
|
||||||
<string name="authenticating_with_device">Cihazla təsdiqlənir \u2026</string>
|
<string name="authenticating_with_device">Cihazla təsdiqlənir \u2026</string>
|
||||||
<string name="connection_error_title">Kontaktınıza qoşula bilmədi</string>
|
<string name="connection_error_title">Kontaktınıza qoşula bilmədi</string>
|
||||||
<string name="connection_error_explanation">Həmin Wi-Fi şəbəkəsinə qoşulduğunuzu yoxlayın.</string>
|
|
||||||
<string name="connection_error_feedback"><a href="feedback">Bu problem davam edərsə, tətbiqin təkmilləşdirilməsinə kömək etmək üçün rəy göndərin.</a></string>
|
<string name="connection_error_feedback"><a href="feedback">Bu problem davam edərsə, tətbiqin təkmilləşdirilməsinə kömək etmək üçün rəy göndərin.</a></string>
|
||||||
<!--Adding Contacts Remotely-->
|
<!--Adding Contacts Remotely-->
|
||||||
<string name="add_contact_remotely_title_case">Məsafədə kontakt əlavə etmək </string>
|
<string name="add_contact_remotely_title_case">Məsafədə kontakt əlavə etmək </string>
|
||||||
|
|||||||
@@ -134,7 +134,6 @@
|
|||||||
<string name="connecting_to_device">Povezujem se sa uređajem\u2026</string>
|
<string name="connecting_to_device">Povezujem se sa uređajem\u2026</string>
|
||||||
<string name="authenticating_with_device">Autentikacija sa uređajem\u2026</string>
|
<string name="authenticating_with_device">Autentikacija sa uređajem\u2026</string>
|
||||||
<string name="connection_error_title">Nije moguće povezivanje sa vašim kontaktom</string>
|
<string name="connection_error_title">Nije moguće povezivanje sa vašim kontaktom</string>
|
||||||
<string name="connection_error_explanation">Provjerite jeste li oboje povezani na ist Wi-Fi mrežu.</string>
|
|
||||||
<string name="connection_error_feedback">Ako se problem ponavlja, Molimo vas <a href="feedback">pošaljite povratne informacije</a> kako bi pomogli poboljšanju aplikacije.</string>
|
<string name="connection_error_feedback">Ako se problem ponavlja, Molimo vas <a href="feedback">pošaljite povratne informacije</a> kako bi pomogli poboljšanju aplikacije.</string>
|
||||||
<!--Adding Contacts Remotely-->
|
<!--Adding Contacts Remotely-->
|
||||||
<string name="add_contact_remotely_title_case">Dodaj udaljeni konktakt</string>
|
<string name="add_contact_remotely_title_case">Dodaj udaljeni konktakt</string>
|
||||||
|
|||||||
@@ -27,7 +27,7 @@
|
|||||||
<string name="enter_password">Contrasenya</string>
|
<string name="enter_password">Contrasenya</string>
|
||||||
<string name="try_again">La contrasenya és incorrecta, torneu a escriure-la</string>
|
<string name="try_again">La contrasenya és incorrecta, torneu a escriure-la</string>
|
||||||
<string name="dialog_title_cannot_check_password">No es pot verificar la contrasenya</string>
|
<string name="dialog_title_cannot_check_password">No es pot verificar la contrasenya</string>
|
||||||
<string name="dialog_message_cannot_check_password">El Briar no pot verificar la contrasenya. Proveu de reiniciar el vostre aparell per a solucionar aquest problema.</string>
|
<string name="dialog_message_cannot_check_password">Briar no pot verificar la contrasenya. Proveu de reiniciar el vostre aparell per a solucionar aquest problema.</string>
|
||||||
<string name="sign_in_button">Inicia la sessió</string>
|
<string name="sign_in_button">Inicia la sessió</string>
|
||||||
<string name="forgotten_password">No recordo la contrasenya</string>
|
<string name="forgotten_password">No recordo la contrasenya</string>
|
||||||
<string name="dialog_title_lost_password">Contrasenya perduda</string>
|
<string name="dialog_title_lost_password">Contrasenya perduda</string>
|
||||||
@@ -44,9 +44,9 @@
|
|||||||
<item quantity="other">Aquesta és una versió de prova de Briar. El vostre compte caducarà en %d dies i no es podrà 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>
|
||||||
<string name="expiry_date_reached">Aquesta versió de Briar ha caducat.\nGràcies per haver-lo provat!</string>
|
<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 el Briar, baixeu la darrera versió.</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>
|
<string name="create_new_account">Haureu de crear un compte nou, però podeu utilitzar el mateix sobrenom.</string>
|
||||||
<string name="download_briar_button">Baixa l\'última versió</string>
|
<string name="download_briar_button">Descarrega la darrera versió</string>
|
||||||
<string name="startup_open_database">S\'està desxifrant la base de dades...</string>
|
<string name="startup_open_database">S\'està desxifrant la base de dades...</string>
|
||||||
<string name="startup_migrate_database">S\'està actualitzant la base de dades...</string>
|
<string name="startup_migrate_database">S\'està actualitzant la base de dades...</string>
|
||||||
<string name="startup_compact_database">S\'està compactant la base de dades...</string>
|
<string name="startup_compact_database">S\'està compactant la base de dades...</string>
|
||||||
@@ -61,12 +61,36 @@
|
|||||||
<string name="lock_button">Bloqueja l\'aplicació</string>
|
<string name="lock_button">Bloqueja l\'aplicació</string>
|
||||||
<string name="settings_button">Configuració</string>
|
<string name="settings_button">Configuració</string>
|
||||||
<string name="sign_out_button">Tanca la sessió </string>
|
<string name="sign_out_button">Tanca la sessió </string>
|
||||||
|
<string name="transports_onboarding_text">Toqueu per decidir com es connecta Briar als vostres contactes.</string>
|
||||||
<!--Transports: Tor-->
|
<!--Transports: Tor-->
|
||||||
<string name="transport_tor">Internet</string>
|
<string name="transport_tor">Internet</string>
|
||||||
|
<string name="tor_device_status_online_wifi">El mòbil té accés a Internet via WiFi</string>
|
||||||
|
<string name="tor_device_status_online_mobile">El mòbil té accés a Internet via dades</string>
|
||||||
|
<string name="tor_device_status_offline">El mòbil no té accés a Internet</string>
|
||||||
|
<string name="tor_plugin_status_enabling">Briar s\'està connectant a Internet</string>
|
||||||
|
<string name="tor_plugin_status_active">Briar està connectat a Internet</string>
|
||||||
|
<string name="tor_plugin_status_inactive">Briar no pot connectar-se a Internet</string>
|
||||||
|
<string name="tor_plugin_status_disabled">Briar està configurat per a no emprar Internet</string>
|
||||||
|
<string name="tor_plugin_status_disabled_mobile_data">Briar està configurat per a no emprar dades mòbils</string>
|
||||||
|
<string name="tor_plugin_status_disabled_battery">Briar està configurat per a no usar Internet mentre funciona amb bateria</string>
|
||||||
|
<string name="tor_plugin_status_disabled_country_blocked">Briar està configurat per a no emprar Internet en aquest país</string>
|
||||||
<!--Transports: Wi-Fi-->
|
<!--Transports: Wi-Fi-->
|
||||||
<string name="transport_lan">Wi-Fi</string>
|
<string name="transport_lan">Wi-Fi</string>
|
||||||
|
<string name="transport_lan_long">La mateixa xarxa WiFi</string>
|
||||||
|
<string name="lan_device_status_on">El mòbil està connectat a WiFi</string>
|
||||||
|
<string name="lan_device_status_off">El mòbil no està connectat a WiFi</string>
|
||||||
|
<string name="lan_plugin_status_enabling">Briar s\'està connectant a la xarxa WiFi</string>
|
||||||
|
<string name="lan_plugin_status_active">Briar està connectat a la xarxa WiFi</string>
|
||||||
|
<string name="lan_plugin_status_inactive">Briar no pot connectar-se a la xarxa WiFi</string>
|
||||||
|
<string name="lan_plugin_status_disabled">Briar està configurat per a no emprar la xarxa WiFi</string>
|
||||||
<!--Transports: Bluetooth-->
|
<!--Transports: Bluetooth-->
|
||||||
<string name="transport_bt">Bluetooth</string>
|
<string name="transport_bt">Bluetooth</string>
|
||||||
|
<string name="bt_device_status_on">El Bluetooth del mòbil està apagat</string>
|
||||||
|
<string name="bt_device_status_off">El Bluetooth del mòbil està engegat</string>
|
||||||
|
<string name="bt_plugin_status_enabling">Briar s\'està connectant a Bluetooth</string>
|
||||||
|
<string name="bt_plugin_status_active">Briar està connectat a Bluetooth</string>
|
||||||
|
<string name="bt_plugin_status_inactive">Briar no pot connectar-se a Bluetooth</string>
|
||||||
|
<string name="bt_plugin_status_disabled">Briar està configurat per a no emprar Bluetooth</string>
|
||||||
<!--Notifications-->
|
<!--Notifications-->
|
||||||
<string name="reminder_notification_title">Heu sortit de Briar</string>
|
<string name="reminder_notification_title">Heu sortit de Briar</string>
|
||||||
<string name="reminder_notification_text">Toqueu per a reiniciar la sessió.</string>
|
<string name="reminder_notification_text">Toqueu per a reiniciar la sessió.</string>
|
||||||
@@ -112,7 +136,7 @@
|
|||||||
<string name="fix">Corregeix</string>
|
<string name="fix">Corregeix</string>
|
||||||
<string name="help">Ajuda</string>
|
<string name="help">Ajuda</string>
|
||||||
<string name="sorry">Ens sap greu</string>
|
<string name="sorry">Ens sap greu</string>
|
||||||
<string name="error_start_activity">No disponible al vostre sistema</string>
|
<string name="error_start_activity">No està disponible en el vostre sistema</string>
|
||||||
<string name="status_heading">Estat</string>
|
<string name="status_heading">Estat</string>
|
||||||
<!--Contacts and Private Conversations-->
|
<!--Contacts and Private Conversations-->
|
||||||
<string name="no_contacts">No hi ha cap contacte per mostrar</string>
|
<string name="no_contacts">No hi ha cap contacte per mostrar</string>
|
||||||
@@ -128,16 +152,16 @@
|
|||||||
<string name="set_contact_alias">Canvia el nom del contacte</string>
|
<string name="set_contact_alias">Canvia el nom del contacte</string>
|
||||||
<string name="set_contact_alias_hint">Nom del contacte</string>
|
<string name="set_contact_alias_hint">Nom del contacte</string>
|
||||||
<string name="set_alias_button">Canvia</string>
|
<string name="set_alias_button">Canvia</string>
|
||||||
<string name="delete_all_messages">Suprimeix tots els missatges</string>
|
<string name="delete_all_messages">Esborra tots els missatges</string>
|
||||||
<string name="dialog_title_delete_all_messages">Confirmeu la supressió dels missatges</string>
|
<string name="dialog_title_delete_all_messages">Confirmeu l\'esborrat dels missatges</string>
|
||||||
<string name="dialog_message_delete_all_messages">Esteu segur que voleu suprimir tots els missatges?</string>
|
<string name="dialog_message_delete_all_messages">Segur que voleu esborrar tots els missatges?</string>
|
||||||
<string name="dialog_title_not_all_messages_deleted">No s\'ha pogut suprimir tots els missatges</string>
|
<string name="dialog_title_not_all_messages_deleted">No s\'han pogut esborrar tots els missatges</string>
|
||||||
<string name="dialog_message_not_deleted_ongoing_both">Els missatges relacionats amb introduccions i invitacions pendents no es poden suprimir fins que es finalitzin.</string>
|
<string name="dialog_message_not_deleted_ongoing_both">Els missatges relacionats amb presentacions i invitacions pendents no es poden suprimir fins que finalitzen.</string>
|
||||||
<string name="dialog_message_not_deleted_ongoing_introductions">Els missatges relacionats amb introduccions pendents no es poden suprimir fins que es finalitzin.</string>
|
<string name="dialog_message_not_deleted_ongoing_introductions">Els missatges relacionats amb presentacions pendents no es poden suprimir fins que finalitzen.</string>
|
||||||
<string name="dialog_message_not_deleted_ongoing_invitations">Els missatges relacionats amb invitacions pendents no es poden suprimir fins que es finalitzin.</string>
|
<string name="dialog_message_not_deleted_ongoing_invitations">Els missatges relacionats amb invitacions pendents no es poden suprimir fins que finalitzen.</string>
|
||||||
<string name="dialog_message_not_deleted_partly_downloaded">Els missatges baixats parcialment no es poden suprimir fins que s\'acabin de baixar.</string>
|
<string name="dialog_message_not_deleted_partly_downloaded">Els missatges baixats parcialment no es poden suprimir fins que s\'acabin de baixar.</string>
|
||||||
<string name="dialog_message_not_deleted_not_all_selected_both">Per a suprimir una invitació o una introducció, cal que seleccioneu la sol·licitdu i la resposta.</string>
|
<string name="dialog_message_not_deleted_not_all_selected_both">Per a suprimir una invitació o una presentació, cal que seleccioneu la sol·licitud i la resposta.</string>
|
||||||
<string name="dialog_message_not_deleted_not_all_selected_introductions">Per a suprimir una introducció, cal que seleccioneu la sol·licitud i la resposta.</string>
|
<string name="dialog_message_not_deleted_not_all_selected_introductions">Per a suprimir una presentació, cal que seleccioneu la sol·licitud i la resposta.</string>
|
||||||
<string name="dialog_message_not_deleted_not_all_selected_invitations">Per a suprimir una invitació, cal que seleccioneu la sol·licitud i la resposta.</string>
|
<string name="dialog_message_not_deleted_not_all_selected_invitations">Per a suprimir una invitació, cal que seleccioneu la sol·licitud i la resposta.</string>
|
||||||
<string name="delete_contact">Suprimeix aquest contacte</string>
|
<string name="delete_contact">Suprimeix aquest contacte</string>
|
||||||
<string name="dialog_title_delete_contact">Confirmeu la supressió del contacte</string>
|
<string name="dialog_title_delete_contact">Confirmeu la supressió del contacte</string>
|
||||||
@@ -172,7 +196,6 @@ Així que l\'actualitzi li veureu una icona diferent .</string>
|
|||||||
<string name="connecting_to_device">Connectant-se al dispositiu\u2026</string>
|
<string name="connecting_to_device">Connectant-se al dispositiu\u2026</string>
|
||||||
<string name="authenticating_with_device">Autenticant-se amb el dispositiu\u2026</string>
|
<string name="authenticating_with_device">Autenticant-se amb el dispositiu\u2026</string>
|
||||||
<string name="connection_error_title">No ha pogut connectar-se al vostre contacte</string>
|
<string name="connection_error_title">No ha pogut connectar-se al vostre contacte</string>
|
||||||
<string name="connection_error_explanation">Comproveu que esteu connectats a la mateixa xarxa Wi-Fi.</string>
|
|
||||||
<string name="connection_error_feedback">Si aquest problema persisteix, <a href="feedback">envieu-nos un comentari</a> per ajudar-nos a millorar l\'aplicació.</string>
|
<string name="connection_error_feedback">Si aquest problema persisteix, <a href="feedback">envieu-nos un comentari</a> per ajudar-nos a millorar l\'aplicació.</string>
|
||||||
<!--Adding Contacts Remotely-->
|
<!--Adding Contacts Remotely-->
|
||||||
<string name="add_contact_remotely_title_case">Afegeix un contacte llunyà</string>
|
<string name="add_contact_remotely_title_case">Afegeix un contacte llunyà</string>
|
||||||
@@ -413,10 +436,20 @@ Així que l\'actualitzi li veureu una icona diferent .</string>
|
|||||||
<string name="pref_theme_auto">Automàtic (segons l\'hora)</string>
|
<string name="pref_theme_auto">Automàtic (segons l\'hora)</string>
|
||||||
<string name="pref_theme_system">Valor per defecte del sistema</string>
|
<string name="pref_theme_system">Valor per defecte del sistema</string>
|
||||||
<!--Settings Connections-->
|
<!--Settings Connections-->
|
||||||
|
<string name="network_settings_title">Connexions</string>
|
||||||
|
<string name="bluetooth_setting">Connectat als contactes via Bluetooth</string>
|
||||||
|
<string name="wifi_setting">Connectat als contactes en la mateixa xarxa WiFi</string>
|
||||||
|
<string name="tor_enable_title">Connectat als contactes via Internet</string>
|
||||||
|
<string name="tor_enable_summary">Totes les connexions van via la xarxa Tor per augmentar privacitat</string>
|
||||||
|
<string name="tor_network_setting">Mètode de connexió per a la xarxa Tor</string>
|
||||||
<string name="tor_network_setting_automatic">Automàtic, basat en la posició</string>
|
<string name="tor_network_setting_automatic">Automàtic, basat en la posició</string>
|
||||||
|
<string name="tor_network_setting_without_bridges">Usa la xarxa Tor sense ponts</string>
|
||||||
|
<string name="tor_network_setting_with_bridges">Usa la xarxa Tor amb ponts</string>
|
||||||
|
<string name="tor_network_setting_never">No connectis a Internet</string>
|
||||||
<!--How and when Briar will connect to Tor: E.g. "Don't connect to the Internet (in China)" or "Use Tor network with bridges (in Belarus)"-->
|
<!--How and when Briar will connect to Tor: E.g. "Don't connect to the Internet (in China)" or "Use Tor network with bridges (in Belarus)"-->
|
||||||
<string name="tor_network_setting_summary">Automàtic: %1$s (a %2$s)</string>
|
<string name="tor_network_setting_summary">Automàtic: %1$s (a %2$s)</string>
|
||||||
<string name="tor_mobile_data_title">Usa dades mòbils</string>
|
<string name="tor_mobile_data_title">Usa dades mòbils</string>
|
||||||
|
<string name="tor_only_when_charging_title">Només connecta\'t a Internet mentre s\'està carregant </string>
|
||||||
<string name="tor_only_when_charging_summary">Desactiva la connexió a Internet quan el dispositiu estigui funcionant amb la bateria</string>
|
<string name="tor_only_when_charging_summary">Desactiva la connexió a Internet quan el dispositiu estigui funcionant amb la bateria</string>
|
||||||
<!--Settings Security and Panic-->
|
<!--Settings Security and Panic-->
|
||||||
<string name="security_settings_title">Seguretat</string>
|
<string name="security_settings_title">Seguretat</string>
|
||||||
@@ -527,6 +560,7 @@ Així que l\'actualitzi li veureu una icona diferent .</string>
|
|||||||
<string name="lock_is_locked">Briar està bloquejat</string>
|
<string name="lock_is_locked">Briar està bloquejat</string>
|
||||||
<string name="lock_tap_to_unlock">Toqueu per desbloquejar-lo</string>
|
<string name="lock_tap_to_unlock">Toqueu per desbloquejar-lo</string>
|
||||||
<!--Connections Screen-->
|
<!--Connections Screen-->
|
||||||
|
<string name="transports_help_text">Briar pot contactar els vostres contactes via Internet, WiFi o Bluetooth.\n\nTotes les connexions d\'Internet van via la xarxa Tor per privacitat.\n\nSi es pot arribar a un contacte per diversos mètodes, Briar els usa tots simultàniament.</string>
|
||||||
<!--Screenshots-->
|
<!--Screenshots-->
|
||||||
<!--This is a name to be used in screenshots. Feel free to change it to a local name.-->
|
<!--This is a name to be used in screenshots. Feel free to change it to a local name.-->
|
||||||
<string name="screenshot_alice">Alba</string>
|
<string name="screenshot_alice">Alba</string>
|
||||||
|
|||||||
@@ -195,7 +195,6 @@
|
|||||||
<string name="connecting_to_device">Verbinde mit Gerät\u2026</string>
|
<string name="connecting_to_device">Verbinde mit Gerät\u2026</string>
|
||||||
<string name="authenticating_with_device">Authentifiziere Gerät\u2026</string>
|
<string name="authenticating_with_device">Authentifiziere Gerät\u2026</string>
|
||||||
<string name="connection_error_title">Keine Verbindung zum Kontakt</string>
|
<string name="connection_error_title">Keine Verbindung zum Kontakt</string>
|
||||||
<string name="connection_error_explanation">Überprüfe, ob ihr beide mit demselben WLAN-Netzwerk verbunden seid.</string>
|
|
||||||
<string name="connection_error_feedback">Wenn das Problem weiterbesteht, hilf uns die App zu verbessern und <a href="feedback">schicke Feedback</a>.</string>
|
<string name="connection_error_feedback">Wenn das Problem weiterbesteht, hilf uns die App zu verbessern und <a href="feedback">schicke Feedback</a>.</string>
|
||||||
<!--Adding Contacts Remotely-->
|
<!--Adding Contacts Remotely-->
|
||||||
<string name="add_contact_remotely_title_case">Kontakt aus der Ferne hinzufügen</string>
|
<string name="add_contact_remotely_title_case">Kontakt aus der Ferne hinzufügen</string>
|
||||||
@@ -211,7 +210,7 @@
|
|||||||
<string name="add_contact_choose_nickname">Wähle einen Spitznamen</string>
|
<string name="add_contact_choose_nickname">Wähle einen Spitznamen</string>
|
||||||
<string name="add_contact_choose_a_nickname">Gib einen Spitznamen ein</string>
|
<string name="add_contact_choose_a_nickname">Gib einen Spitznamen ein</string>
|
||||||
<string name="nickname_intro">Gib deinem Kontakt einen Spitznamen. Nur du kannst ihn sehen.</string>
|
<string name="nickname_intro">Gib deinem Kontakt einen Spitznamen. Nur du kannst ihn sehen.</string>
|
||||||
<string name="your_link">Gebe diesen Link dem Kontakt, den du hinzufügen möchtest:</string>
|
<string name="your_link">Gib diesen Link dem Kontakt, den du hinzufügen möchtest</string>
|
||||||
<string name="link_clip_label">Briar Link</string>
|
<string name="link_clip_label">Briar Link</string>
|
||||||
<string name="link_copied_toast">Link kopiert</string>
|
<string name="link_copied_toast">Link kopiert</string>
|
||||||
<string name="adding_contact_error">Es gab einen Fehler beim Hinzufügen des Kontaktes.</string>
|
<string name="adding_contact_error">Es gab einen Fehler beim Hinzufügen des Kontaktes.</string>
|
||||||
@@ -550,6 +549,7 @@
|
|||||||
<string name="permission_camera_location_title">Kamera und Standort</string>
|
<string name="permission_camera_location_title">Kamera und Standort</string>
|
||||||
<string name="permission_camera_location_request_body">Um den QR-Code zu scannen, braucht Briar Zugriff auf die Kamera.\n\nUm Bluetooth-Geräte zu finden, braucht Briar Zugriff auf deinen Standort.\n\nBriar speichert weder deinen Standort noch gibt es ihn an andere weiter.</string>
|
<string name="permission_camera_location_request_body">Um den QR-Code zu scannen, braucht Briar Zugriff auf die Kamera.\n\nUm Bluetooth-Geräte zu finden, braucht Briar Zugriff auf deinen Standort.\n\nBriar speichert weder deinen Standort noch gibt es ihn an andere weiter.</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_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_location_denied_body">Du hast den Zugriff auf deinen Standort verweigert, aber Briar benötigt diese Berechtigung, um Bluetooth-Geräte zu erkennen.\n\nBitte gewähre den Zugriff.</string>
|
||||||
<string name="qr_code">QR-Code</string>
|
<string name="qr_code">QR-Code</string>
|
||||||
<string name="show_qr_code_fullscreen">QR-Code im Vollbildmodus anzeigen</string>
|
<string name="show_qr_code_fullscreen">QR-Code im Vollbildmodus anzeigen</string>
|
||||||
<!--App Locking-->
|
<!--App Locking-->
|
||||||
|
|||||||
@@ -157,12 +157,12 @@
|
|||||||
<string name="dialog_message_delete_all_messages">¿Estás seguro de que deseas eliminar todos los mensajes?</string>
|
<string name="dialog_message_delete_all_messages">¿Estás seguro de que deseas eliminar todos los mensajes?</string>
|
||||||
<string name="dialog_title_not_all_messages_deleted">No se pudieron eliminar todos los mensajes.</string>
|
<string name="dialog_title_not_all_messages_deleted">No se pudieron eliminar todos los mensajes.</string>
|
||||||
<string name="dialog_message_not_deleted_ongoing_both">Los mensajes relacionados con presentaciones o invitaciones en curso no se pueden eliminar hasta que finalicen.</string>
|
<string name="dialog_message_not_deleted_ongoing_both">Los mensajes relacionados con presentaciones o invitaciones en curso no se pueden eliminar hasta que finalicen.</string>
|
||||||
<string name="dialog_message_not_deleted_ongoing_introductions">Los mensajes relacionados con presentaciones o invitaciones en curso no se pueden eliminar hasta que finalicen.</string>
|
<string name="dialog_message_not_deleted_ongoing_introductions">Los mensajes relacionados con presentaciones no se pueden eliminar hasta que finalicen.</string>
|
||||||
<string name="dialog_message_not_deleted_ongoing_invitations">Los mensajes relacionados a invitaciones en curso no pueden ser borrados hasta su conclusión.</string>
|
<string name="dialog_message_not_deleted_ongoing_invitations">Los mensajes relacionados a invitaciones en curso no pueden ser borrados hasta su conclusión.</string>
|
||||||
<string name="dialog_message_not_deleted_partly_downloaded">Los mensajes parcialmente descargados no se pueden eliminar hasta que haya finalizado la descarga.</string>
|
<string name="dialog_message_not_deleted_partly_downloaded">Los mensajes parcialmente descargados no se pueden eliminar hasta que haya finalizado la descarga.</string>
|
||||||
<string name="dialog_message_not_deleted_not_all_selected_both">Para borrar una invitación o presentación, debes seleccionar la petición y la respuesta.</string>
|
<string name="dialog_message_not_deleted_not_all_selected_both">Para borrar una invitación o presentación, debes seleccionar la petición y la respuesta.</string>
|
||||||
<string name="dialog_message_not_deleted_not_all_selected_introductions">Para eliminar una introducción, debe seleccionar la solicitud y la respuesta.</string>
|
<string name="dialog_message_not_deleted_not_all_selected_introductions">Para eliminar una introducción, debes seleccionar la solicitud y la respuesta.</string>
|
||||||
<string name="dialog_message_not_deleted_not_all_selected_invitations">Para eliminar una invitación, debe seleccionar la solicitud y la respuesta.</string>
|
<string name="dialog_message_not_deleted_not_all_selected_invitations">Para eliminar una invitación, debes seleccionar la solicitud y la respuesta.</string>
|
||||||
<string name="delete_contact">Eliminar contacto</string>
|
<string name="delete_contact">Eliminar contacto</string>
|
||||||
<string name="dialog_title_delete_contact">Confirmar eliminación de contacto</string>
|
<string name="dialog_title_delete_contact">Confirmar eliminación de contacto</string>
|
||||||
<string name="dialog_message_delete_contact">¿Seguro que quieres eliminar este contacto y todos los mensajes intercambiados entre vosotros?</string>
|
<string name="dialog_message_delete_contact">¿Seguro que quieres eliminar este contacto y todos los mensajes intercambiados entre vosotros?</string>
|
||||||
@@ -195,7 +195,6 @@
|
|||||||
<string name="connecting_to_device">Conectando al dispositivo\u2026</string>
|
<string name="connecting_to_device">Conectando al dispositivo\u2026</string>
|
||||||
<string name="authenticating_with_device">Autentificándose con el dispositivo\u2026</string>
|
<string name="authenticating_with_device">Autentificándose con el dispositivo\u2026</string>
|
||||||
<string name="connection_error_title">No se pudo conectar a tu contacto</string>
|
<string name="connection_error_title">No se pudo conectar a tu contacto</string>
|
||||||
<string name="connection_error_explanation">Por favor comprobar que ambos estén conectados a la misma red Wi-Fi.</string>
|
|
||||||
<string name="connection_error_feedback">Si este problema persiste, por favor <a href="feedback">envía tus comentarios</a> para ayudarnos a mejorar la aplicación.</string>
|
<string name="connection_error_feedback">Si este problema persiste, por favor <a href="feedback">envía tus comentarios</a> para ayudarnos a mejorar la aplicación.</string>
|
||||||
<!--Adding Contacts Remotely-->
|
<!--Adding Contacts Remotely-->
|
||||||
<string name="add_contact_remotely_title_case">Añadir un Contacto a Distancia</string>
|
<string name="add_contact_remotely_title_case">Añadir un Contacto a Distancia</string>
|
||||||
@@ -550,6 +549,7 @@
|
|||||||
<string name="permission_camera_location_title">Cámara y ubicación</string>
|
<string name="permission_camera_location_title">Cámara y ubicación</string>
|
||||||
<string name="permission_camera_location_request_body">Para escanear el código QR, Briar necesita acceso a la cámara.\n\nPara descubrir dispositivos Bluetooth, Briar necesita permiso para acceder tu ubicación.\n\nBriar no la almacena o la comparte con nadie.</string>
|
<string name="permission_camera_location_request_body">Para escanear el código QR, Briar necesita acceso a la cámara.\n\nPara descubrir dispositivos Bluetooth, Briar necesita permiso para acceder tu ubicación.\n\nBriar no la almacena o la comparte con nadie.</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_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_location_denied_body">Has denegado el acceso a tu ubicación, pero Briar necesita este permiso para descubrir dispositivos Bluetooth.\n\nPor favor considera la posibilidad de conceder el acceso.</string>
|
||||||
<string name="qr_code">Código QR</string>
|
<string name="qr_code">Código QR</string>
|
||||||
<string name="show_qr_code_fullscreen">Mostrar código QR a pantalla completa</string>
|
<string name="show_qr_code_fullscreen">Mostrar código QR a pantalla completa</string>
|
||||||
<!--App Locking-->
|
<!--App Locking-->
|
||||||
|
|||||||
@@ -171,7 +171,6 @@
|
|||||||
<string name="connecting_to_device">Gailura konektatzen\u2026</string>
|
<string name="connecting_to_device">Gailura konektatzen\u2026</string>
|
||||||
<string name="authenticating_with_device">Gailuarekin autentifikatzen\u2026</string>
|
<string name="authenticating_with_device">Gailuarekin autentifikatzen\u2026</string>
|
||||||
<string name="connection_error_title">Ezin izan da zure kontaktuarekin konektatu</string>
|
<string name="connection_error_title">Ezin izan da zure kontaktuarekin konektatu</string>
|
||||||
<string name="connection_error_explanation">Egiaztatu biak Wi-Fi sare berera konektatuta zaudetela.</string>
|
|
||||||
<string name="connection_error_feedback">Arazoa mantentzen bada, mesedez <a href="feedback">bidali iruzkin bat</a> aplikazioa hobetzen laguntzeko.</string>
|
<string name="connection_error_feedback">Arazoa mantentzen bada, mesedez <a href="feedback">bidali iruzkin bat</a> aplikazioa hobetzen laguntzeko.</string>
|
||||||
<!--Adding Contacts Remotely-->
|
<!--Adding Contacts Remotely-->
|
||||||
<string name="add_contact_remotely_title_case">Gehitu urruneko kontaktua</string>
|
<string name="add_contact_remotely_title_case">Gehitu urruneko kontaktua</string>
|
||||||
|
|||||||
@@ -69,10 +69,21 @@
|
|||||||
<string name="sign_out_button">خروج</string>
|
<string name="sign_out_button">خروج</string>
|
||||||
<!--Transports: Tor-->
|
<!--Transports: Tor-->
|
||||||
<string name="transport_tor">اینترنت</string>
|
<string name="transport_tor">اینترنت</string>
|
||||||
|
<string name="tor_plugin_status_enabling">Briar در حال اتصال به اینترنت می باشد</string>
|
||||||
|
<string name="tor_plugin_status_active">Briar به اینترنت متصل شد</string>
|
||||||
|
<string name="tor_plugin_status_inactive">Briar نمی تواند به اینترنت متصل شود</string>
|
||||||
<!--Transports: Wi-Fi-->
|
<!--Transports: Wi-Fi-->
|
||||||
<string name="transport_lan">وای فای</string>
|
<string name="transport_lan">وای فای</string>
|
||||||
|
<string name="transport_lan_long">همان شبکه وای-فای</string>
|
||||||
|
<string name="lan_device_status_on">موبایل شما به وای-فای وصل می باشد</string>
|
||||||
|
<string name="lan_device_status_off">موبایل شما به وای-فای وصل نیست</string>
|
||||||
|
<string name="lan_plugin_status_enabling">Briar در حال اتصال به شبکه وای-فای می باشد</string>
|
||||||
<!--Transports: Bluetooth-->
|
<!--Transports: Bluetooth-->
|
||||||
<string name="transport_bt">بلوتوث</string>
|
<string name="transport_bt">بلوتوث</string>
|
||||||
|
<string name="bt_device_status_on">بلوتوث موبایل شما روشن می باشد</string>
|
||||||
|
<string name="bt_device_status_off">بلوتوث موبایل شما خاموش می باشد</string>
|
||||||
|
<string name="bt_plugin_status_inactive">Briar نمی تواند به بلوتوث وصل شود</string>
|
||||||
|
<string name="bt_plugin_status_disabled">Briar طوری پیکربندی شده که از بلوتوث استفاده نکند</string>
|
||||||
<!--Notifications-->
|
<!--Notifications-->
|
||||||
<string name="reminder_notification_title">از Briar (برایر) خارج شد</string>
|
<string name="reminder_notification_title">از Briar (برایر) خارج شد</string>
|
||||||
<string name="reminder_notification_text">برای وارد شدن دوباره ضربه بزنید.</string>
|
<string name="reminder_notification_text">برای وارد شدن دوباره ضربه بزنید.</string>
|
||||||
@@ -185,7 +196,6 @@
|
|||||||
<string name="connecting_to_device">اتصال به دستگاهu2026\</string>
|
<string name="connecting_to_device">اتصال به دستگاهu2026\</string>
|
||||||
<string name="authenticating_with_device">تصدیق سازی با دستگاه u2026\</string>
|
<string name="authenticating_with_device">تصدیق سازی با دستگاه u2026\</string>
|
||||||
<string name="connection_error_title">اتصال به مخاطب شما برقرار نشد</string>
|
<string name="connection_error_title">اتصال به مخاطب شما برقرار نشد</string>
|
||||||
<string name="connection_error_explanation">لطفا مطمئن شوید که هر دو شما به شبکه وای فای یکسان متصل هستید.</string>
|
|
||||||
<string name="connection_error_feedback">اگر این مشکل ادامه پیدا کرد، لطفا <a href="feedback">بازخورد ارسال کنید</a> تا به ما در بهبود برنامه کمک کنید.</string>
|
<string name="connection_error_feedback">اگر این مشکل ادامه پیدا کرد، لطفا <a href="feedback">بازخورد ارسال کنید</a> تا به ما در بهبود برنامه کمک کنید.</string>
|
||||||
<!--Adding Contacts Remotely-->
|
<!--Adding Contacts Remotely-->
|
||||||
<string name="add_contact_remotely_title_case">افزودن مخاطب از دور</string>
|
<string name="add_contact_remotely_title_case">افزودن مخاطب از دور</string>
|
||||||
|
|||||||
@@ -31,7 +31,7 @@
|
|||||||
<string name="sign_in_button">Connexion</string>
|
<string name="sign_in_button">Connexion</string>
|
||||||
<string name="forgotten_password">J’ai oublié mon mot de passe</string>
|
<string name="forgotten_password">J’ai oublié mon mot de passe</string>
|
||||||
<string name="dialog_title_lost_password">Mot de passe oublié</string>
|
<string name="dialog_title_lost_password">Mot de passe oublié</string>
|
||||||
<string name="dialog_message_lost_password">Votre compte Briar est enregistré chiffré sur votre appareil, pas dans le nuage, et nous ne pouvons donc pas réinitialiser votre mot de passe. Souhaitez-vous supprimer votre compte et recommencer ?\n\nAttention : vos identités, contacts et messages seront perdus irrémédiablement.</string>
|
<string name="dialog_message_lost_password">Votre compte Briar est enregistré chiffré sur votre appareil, pas dans le nuage, et nous ne pouvons donc pas réinitialiser votre mot de passe. Voulez-vous supprimer votre compte et recommencer ?\n\nAttention : vos identités, contacts et messages seront perdus irrémédiablement.</string>
|
||||||
<string name="startup_failed_notification_title">Impossible de démarrer Briar</string>
|
<string name="startup_failed_notification_title">Impossible de démarrer Briar</string>
|
||||||
<string name="startup_failed_notification_text">Toucher pour plus d’informations.</string>
|
<string name="startup_failed_notification_text">Toucher pour plus d’informations.</string>
|
||||||
<string name="startup_failed_activity_title">Échec de démarrage de Briar</string>
|
<string name="startup_failed_activity_title">Échec de démarrage de Briar</string>
|
||||||
@@ -143,7 +143,7 @@
|
|||||||
<string name="no_contacts_action">Touchez l’icône + pour ajouter un contact</string>
|
<string name="no_contacts_action">Touchez l’icône + pour ajouter un contact</string>
|
||||||
<string name="date_no_private_messages">Aucun message.</string>
|
<string name="date_no_private_messages">Aucun message.</string>
|
||||||
<string name="no_private_messages">Aucun message à afficher</string>
|
<string name="no_private_messages">Aucun message à afficher</string>
|
||||||
<string name="message_hint">Rédiger le message</string>
|
<string name="message_hint">Rédigez un message</string>
|
||||||
<string name="image_caption_hint">Ajouter une légende (facultatif)</string>
|
<string name="image_caption_hint">Ajouter une légende (facultatif)</string>
|
||||||
<string name="image_attach">Joindre une image</string>
|
<string name="image_attach">Joindre une image</string>
|
||||||
<string name="image_attach_error">Impossible de joindre une ou des images</string>
|
<string name="image_attach_error">Impossible de joindre une ou des images</string>
|
||||||
@@ -154,7 +154,7 @@
|
|||||||
<string name="set_alias_button">Modifier</string>
|
<string name="set_alias_button">Modifier</string>
|
||||||
<string name="delete_all_messages">Supprimer tous les messages</string>
|
<string name="delete_all_messages">Supprimer tous les messages</string>
|
||||||
<string name="dialog_title_delete_all_messages">Confirmer la suppression des messages</string>
|
<string name="dialog_title_delete_all_messages">Confirmer la suppression des messages</string>
|
||||||
<string name="dialog_message_delete_all_messages">Souhaitez-vous vraiment supprimer tous les messages ?</string>
|
<string name="dialog_message_delete_all_messages">Voulez-vous vraiment supprimer tous les messages ?</string>
|
||||||
<string name="dialog_title_not_all_messages_deleted">Impossible de supprimer tous les messages</string>
|
<string name="dialog_title_not_all_messages_deleted">Impossible de supprimer tous les messages</string>
|
||||||
<string name="dialog_message_not_deleted_ongoing_both">Les messages relatifs aux invitations et présentations en cours ne peuvent pas être supprimés jusqu’à leur conclusion.</string>
|
<string name="dialog_message_not_deleted_ongoing_both">Les messages relatifs aux invitations et présentations en cours ne peuvent pas être supprimés jusqu’à leur conclusion.</string>
|
||||||
<string name="dialog_message_not_deleted_ongoing_introductions">Les messages relatifs aux présentations en cours ne peuvent pas être supprimés jusqu’à leur conclusion.</string>
|
<string name="dialog_message_not_deleted_ongoing_introductions">Les messages relatifs aux présentations en cours ne peuvent pas être supprimés jusqu’à leur conclusion.</string>
|
||||||
@@ -165,13 +165,13 @@
|
|||||||
<string name="dialog_message_not_deleted_not_all_selected_invitations">Pour supprimer une invitation, vous devez sélectionner la demande et la réponse.</string>
|
<string name="dialog_message_not_deleted_not_all_selected_invitations">Pour supprimer une invitation, vous devez sélectionner la demande et la réponse.</string>
|
||||||
<string name="delete_contact">Supprimer le contact</string>
|
<string name="delete_contact">Supprimer le contact</string>
|
||||||
<string name="dialog_title_delete_contact">Confirmer la suppression du contact</string>
|
<string name="dialog_title_delete_contact">Confirmer la suppression du contact</string>
|
||||||
<string name="dialog_message_delete_contact">Souhaitez-vous vraiment supprimer ce contact et tous les messages associés ?</string>
|
<string name="dialog_message_delete_contact">Voulez-vous vraiment supprimer ce contact et tous les messages associés ?</string>
|
||||||
<string name="contact_deleted_toast">Le contact a été supprimé</string>
|
<string name="contact_deleted_toast">Le contact a été supprimé</string>
|
||||||
<!--This is shown in the action bar when opening an image in fullscreen that the user sent-->
|
<!--This is shown in the action bar when opening an image in fullscreen that the user sent-->
|
||||||
<string name="you">Vous</string>
|
<string name="you">Vous</string>
|
||||||
<string name="save_image">Enregistrer l’image</string>
|
<string name="save_image">Enregistrer l’image</string>
|
||||||
<string name="dialog_title_save_image">Enregistrer l’image ?</string>
|
<string name="dialog_title_save_image">Enregistrer l’image ?</string>
|
||||||
<string name="dialog_message_save_image">L’enregistrement de cette image permettra aux autres applis d’y accéder.\n\n Souhaitez-vous vraiment l’enregistrer ?</string>
|
<string name="dialog_message_save_image">L’enregistrement de cette image permettra aux autres applis d’y accéder.\n\n Voulez-vous vraiment l’enregistrer ?</string>
|
||||||
<string name="save_image_success">L’image a été enregistrée</string>
|
<string name="save_image_success">L’image a été enregistrée</string>
|
||||||
<string name="save_image_error">Impossible d’enregistrer l’image</string>
|
<string name="save_image_error">Impossible d’enregistrer l’image</string>
|
||||||
<string name="dialog_title_no_image_support">Les images ne sont pas disponibles</string>
|
<string name="dialog_title_no_image_support">Les images ne sont pas disponibles</string>
|
||||||
@@ -195,7 +195,6 @@
|
|||||||
<string name="connecting_to_device">Connexion à l’appareil\u2026</string>
|
<string name="connecting_to_device">Connexion à l’appareil\u2026</string>
|
||||||
<string name="authenticating_with_device">Autentification avec 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>
|
<string name="connection_error_title">Impossible de se connecter à votre contact</string>
|
||||||
<string name="connection_error_explanation">Veuillez vérifier que vous êtes connecté au même réseau Wi-Fi.</string>
|
|
||||||
<string name="connection_error_feedback">Si le problème persiste, veuillez nous <a href="feedback">envoyer une rétroaction</a> pour nous aider à améliorer l\'appli.</string>
|
<string name="connection_error_feedback">Si le problème persiste, veuillez nous <a href="feedback">envoyer une rétroaction</a> pour nous aider à améliorer l\'appli.</string>
|
||||||
<!--Adding Contacts Remotely-->
|
<!--Adding Contacts Remotely-->
|
||||||
<string name="add_contact_remotely_title_case">Ajouter un contact éloigné</string>
|
<string name="add_contact_remotely_title_case">Ajouter un contact éloigné</string>
|
||||||
@@ -266,7 +265,7 @@
|
|||||||
<string name="introduction_sent">Votre présentation a été envoyée.</string>
|
<string name="introduction_sent">Votre présentation a été envoyée.</string>
|
||||||
<string name="introduction_error">Une erreur est survenue lors de la présentation.</string>
|
<string name="introduction_error">Une erreur est survenue lors de la présentation.</string>
|
||||||
<string name="introduction_request_sent">Vous avez demandé de présenter %1$s à %2$s.</string>
|
<string name="introduction_request_sent">Vous avez demandé de présenter %1$s à %2$s.</string>
|
||||||
<string name="introduction_request_received">%1$s a demandé de vous présenter à %2$s. Souhaitez-vous ajouter %2$s à votre liste de contacts ?</string>
|
<string name="introduction_request_received">%1$s a demandé de vous présenter à %2$s. Voulez-vous ajouter %2$s à votre liste de contacts ?</string>
|
||||||
<string name="introduction_request_exists_received">%1$s a demandé de vous présenter à %2$s, mais %2$s est déjà dans votre liste de contacts. Puisque %1$s pourrait ne pas le savoir, vous pouvez tout de même répondre :</string>
|
<string name="introduction_request_exists_received">%1$s a demandé de vous présenter à %2$s, mais %2$s est déjà dans votre liste de contacts. Puisque %1$s pourrait ne pas le savoir, vous pouvez tout de même répondre :</string>
|
||||||
<string name="introduction_request_answered_received">%1$s a demandé de vous présenter à %2$s.</string>
|
<string name="introduction_request_answered_received">%1$s a demandé de vous présenter à %2$s.</string>
|
||||||
<string name="introduction_response_accepted_sent">Vous avez accepté d’être présenté à %1$s.</string>
|
<string name="introduction_response_accepted_sent">Vous avez accepté d’être présenté à %1$s.</string>
|
||||||
@@ -299,10 +298,10 @@
|
|||||||
<string name="groups_member_joined">%s s’est joint au groupe</string>
|
<string name="groups_member_joined">%s s’est joint au groupe</string>
|
||||||
<string name="groups_leave">Quitter le groupe</string>
|
<string name="groups_leave">Quitter le groupe</string>
|
||||||
<string name="groups_leave_dialog_title">Confirmer la sortie du groupe</string>
|
<string name="groups_leave_dialog_title">Confirmer la sortie du groupe</string>
|
||||||
<string name="groups_leave_dialog_message">Souhaitez-vous vraiment quitter ce groupe ?</string>
|
<string name="groups_leave_dialog_message">Voulez-vous vraiment quitter ce groupe ?</string>
|
||||||
<string name="groups_dissolve">Dissoudre le groupe</string>
|
<string name="groups_dissolve">Dissoudre le groupe</string>
|
||||||
<string name="groups_dissolve_dialog_title">Confirmer la dissolution du groupe</string>
|
<string name="groups_dissolve_dialog_title">Confirmer la dissolution du groupe</string>
|
||||||
<string name="groups_dissolve_dialog_message">Souhaitez-vous vraiment dissoudre ce groupe ?\n\nLes autres participants ne pourront pas poursuivre leur conversation et ne recevront peut-être pas les derniers messages.</string>
|
<string name="groups_dissolve_dialog_message">Voulez-vous vraiment dissoudre ce groupe ?\n\nLes autres participants ne pourront pas poursuivre leur conversation et ne recevront peut-être pas les derniers messages.</string>
|
||||||
<string name="groups_dissolve_button">Dissoudre</string>
|
<string name="groups_dissolve_button">Dissoudre</string>
|
||||||
<string name="groups_dissolved_dialog_title">Le groupe a été dissous</string>
|
<string name="groups_dissolved_dialog_title">Le groupe a été dissous</string>
|
||||||
<string name="groups_dissolved_dialog_message">Le créateur de ce groupe l’a dissous.\n\nVous ne pouvez plus écrire de messages au groupe et ne recevrez peut-être pas tous ceux qui y ont été publiés.</string>
|
<string name="groups_dissolved_dialog_message">Le créateur de ce groupe l’a dissous.\n\nVous ne pouvez plus écrire de messages au groupe et ne recevrez peut-être pas tous ceux qui y ont été publiés.</string>
|
||||||
@@ -346,7 +345,7 @@
|
|||||||
<string name="btn_reply">Répondre</string>
|
<string name="btn_reply">Répondre</string>
|
||||||
<string name="forum_leave">Quitter le forum</string>
|
<string name="forum_leave">Quitter le forum</string>
|
||||||
<string name="dialog_title_leave_forum">Confirmer la sortie du forum</string>
|
<string name="dialog_title_leave_forum">Confirmer la sortie du forum</string>
|
||||||
<string name="dialog_message_leave_forum">Souhaitez-vous vraiment quitter ce forum ?\n\nLes contacts avec qui vous l’avez partagé pourraient ne plus en recevoir les mises à jour.</string>
|
<string name="dialog_message_leave_forum">Voulez-vous vraiment quitter ce forum ?\n\nLes contacts avec qui vous l’avez partagé pourraient ne plus en recevoir les mises à jour.</string>
|
||||||
<string name="dialog_button_leave">Quitter</string>
|
<string name="dialog_button_leave">Quitter</string>
|
||||||
<string name="forum_left_toast">À quitté le forum</string>
|
<string name="forum_left_toast">À quitté le forum</string>
|
||||||
<!--Forum Sharing-->
|
<!--Forum Sharing-->
|
||||||
@@ -390,7 +389,7 @@
|
|||||||
<string name="blogs_feed_empty_state">Aucun billet à afficher</string>
|
<string name="blogs_feed_empty_state">Aucun billet à afficher</string>
|
||||||
<string name="blogs_feed_empty_state_action">Les billets de vos contacts et les blogues auxquels vous vous abonnez apparaîtront ici.\n\nTouchez l’icône de crayon pour rédiger un billet</string>
|
<string name="blogs_feed_empty_state_action">Les billets de vos contacts et les blogues auxquels vous vous abonnez apparaîtront ici.\n\nTouchez l’icône de crayon pour rédiger un billet</string>
|
||||||
<string name="blogs_remove_blog">Supprimer le blogue</string>
|
<string name="blogs_remove_blog">Supprimer le blogue</string>
|
||||||
<string name="blogs_remove_blog_dialog_message">Souhaitez-vous vraiment supprimer ce blogue ?\nLes billets seront supprimés de votre appareil mais pas des appareils d’autrui.\n\nLes contacts avec qui vous avez partagé ce blogue pourraient ne plus en recevoir les mises à jour.</string>
|
<string name="blogs_remove_blog_dialog_message">Voulez-vous vraiment supprimer ce blogue ?\nLes billets seront supprimés de votre appareil mais pas des appareils d’autrui.\n\nLes contacts avec qui vous avez partagé ce blogue pourraient ne plus en recevoir les mises à jour.</string>
|
||||||
<string name="blogs_remove_blog_ok">Supprimer</string>
|
<string name="blogs_remove_blog_ok">Supprimer</string>
|
||||||
<string name="blogs_blog_removed">Le blogue a été supprimé</string>
|
<string name="blogs_blog_removed">Le blogue a été supprimé</string>
|
||||||
<string name="blogs_reblog_comment_hint">Ajouter un commentaire (facultatif)</string>
|
<string name="blogs_reblog_comment_hint">Ajouter un commentaire (facultatif)</string>
|
||||||
@@ -420,7 +419,7 @@
|
|||||||
<string name="blogs_rss_feeds_manage_author">Auteur :</string>
|
<string name="blogs_rss_feeds_manage_author">Auteur :</string>
|
||||||
<string name="blogs_rss_feeds_manage_updated">Dernière mise à jour :</string>
|
<string name="blogs_rss_feeds_manage_updated">Dernière mise à jour :</string>
|
||||||
<string name="blogs_rss_remove_feed">Supprimer le fil</string>
|
<string name="blogs_rss_remove_feed">Supprimer le fil</string>
|
||||||
<string name="blogs_rss_remove_feed_dialog_message">Souhaitez-vous vraiment supprimer ce fil ?\nLes billets seront supprimés de votre appareil mais pas des appareils d’autrui.\n\nLes contacts avec qui vous avez partagé ce fil pourraient ne plus en recevoir les mises à jour.</string>
|
<string name="blogs_rss_remove_feed_dialog_message">Voulez-vous vraiment supprimer ce fil ?\nLes billets seront supprimés de votre appareil mais pas des appareils d’autrui.\n\nLes contacts avec qui vous avez partagé ce fil pourraient ne plus en recevoir les mises à jour.</string>
|
||||||
<string name="blogs_rss_remove_feed_ok">Supprimer</string>
|
<string name="blogs_rss_remove_feed_ok">Supprimer</string>
|
||||||
<string name="blogs_rss_feeds_manage_delete_error">Impossible de supprimer le fil !</string>
|
<string name="blogs_rss_feeds_manage_delete_error">Impossible de supprimer le fil !</string>
|
||||||
<string name="blogs_rss_feeds_manage_empty_state">Aucun fil RSS à afficher\n\nTouchez l’icône + pour importer un fil</string>
|
<string name="blogs_rss_feeds_manage_empty_state">Aucun fil RSS à afficher\n\nTouchez l’icône + pour importer un fil</string>
|
||||||
@@ -484,7 +483,7 @@
|
|||||||
<string name="panic_app_setting_summary">Aucune appli n’a été définie</string>
|
<string name="panic_app_setting_summary">Aucune appli n’a été définie</string>
|
||||||
<string name="panic_app_setting_none">Aucune</string>
|
<string name="panic_app_setting_none">Aucune</string>
|
||||||
<string name="dialog_title_connect_panic_app">Confirmer l’application d’urgence</string>
|
<string name="dialog_title_connect_panic_app">Confirmer l’application d’urgence</string>
|
||||||
<string name="dialog_message_connect_panic_app">Souhaitez-vous vraiment autoriser %1$s à déclencher les actions destructrices du bouton d’urgence ?</string>
|
<string name="dialog_message_connect_panic_app">Voulez-vous vraiment autoriser %1$s à déclencher les actions destructrices du bouton d’urgence ?</string>
|
||||||
<string name="panic_setting_destructive_action">Actions destructrices</string>
|
<string name="panic_setting_destructive_action">Actions destructrices</string>
|
||||||
<string name="panic_setting_signout_title">Déconnexion</string>
|
<string name="panic_setting_signout_title">Déconnexion</string>
|
||||||
<string name="panic_setting_signout_summary">Se déconnecter de Briar si l’on appuie sur un bouton d’urgence</string>
|
<string name="panic_setting_signout_summary">Se déconnecter de Briar si l’on appuie sur un bouton d’urgence</string>
|
||||||
@@ -550,6 +549,7 @@
|
|||||||
<string name="permission_camera_location_title">Appareil photo et position</string>
|
<string name="permission_camera_location_title">Appareil photo et position</string>
|
||||||
<string name="permission_camera_location_request_body">Afin de balayer 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_location_request_body">Afin de balayer 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_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_location_denied_body">Vous avez refusé l’accès à votre position géographique, mais Briar en a besoin pour découvrir les appareils Bluetooth.\n\nVeuillez envisager d’y accorder l’accès.</string>
|
||||||
<string name="qr_code">Code QR</string>
|
<string name="qr_code">Code QR</string>
|
||||||
<string name="show_qr_code_fullscreen">Afficher le code QR en plein écran</string>
|
<string name="show_qr_code_fullscreen">Afficher le code QR en plein écran</string>
|
||||||
<!--App Locking-->
|
<!--App Locking-->
|
||||||
|
|||||||
@@ -61,12 +61,36 @@
|
|||||||
<string name="lock_button">Bloquear App</string>
|
<string name="lock_button">Bloquear App</string>
|
||||||
<string name="settings_button">Axustes</string>
|
<string name="settings_button">Axustes</string>
|
||||||
<string name="sign_out_button">Finalizar sesión</string>
|
<string name="sign_out_button">Finalizar sesión</string>
|
||||||
|
<string name="transports_onboarding_text">Toca aquí para controlar o xeito en que Briar conecta cos teus contactos.</string>
|
||||||
<!--Transports: Tor-->
|
<!--Transports: Tor-->
|
||||||
<string name="transport_tor">Internet</string>
|
<string name="transport_tor">Internet</string>
|
||||||
|
<string name="tor_device_status_online_wifi">O teu móbil ten acceso a internet por Wi-Fi</string>
|
||||||
|
<string name="tor_device_status_online_mobile">O teu móbil ten acceso a internet por datos móbiles</string>
|
||||||
|
<string name="tor_device_status_offline">O teu móbil non ten acceso a internet</string>
|
||||||
|
<string name="tor_plugin_status_enabling">Briar estase conectando a internet</string>
|
||||||
|
<string name="tor_plugin_status_active">Briar está conectada a internet</string>
|
||||||
|
<string name="tor_plugin_status_inactive">Briar non pode conectarse a internet</string>
|
||||||
|
<string name="tor_plugin_status_disabled">Briar está configurada para non usar internet</string>
|
||||||
|
<string name="tor_plugin_status_disabled_mobile_data">Briar está configurada para non usar datos móbilies</string>
|
||||||
|
<string name="tor_plugin_status_disabled_battery">Briar está configurada para non usar internet se está usando batería</string>
|
||||||
|
<string name="tor_plugin_status_disabled_country_blocked">Briar está configurada para non usar internet neste país</string>
|
||||||
<!--Transports: Wi-Fi-->
|
<!--Transports: Wi-Fi-->
|
||||||
<string name="transport_lan">Wi-Fi</string>
|
<string name="transport_lan">Wi-Fi</string>
|
||||||
|
<string name="transport_lan_long">Mesma rede Wi-Fi</string>
|
||||||
|
<string name="lan_device_status_on">O móbil está conectado á Wi-Fi</string>
|
||||||
|
<string name="lan_device_status_off">O móbil non está conectado á Wi-Fi</string>
|
||||||
|
<string name="lan_plugin_status_enabling">Briar está conectando á rede Wi-Fi</string>
|
||||||
|
<string name="lan_plugin_status_active">Briar está conectada á rede Wi-Fi</string>
|
||||||
|
<string name="lan_plugin_status_inactive">Briar non pode conectar coa rede Wi-Fi</string>
|
||||||
|
<string name="lan_plugin_status_disabled">Briar está configurada para non usar a rede Wi-Fi</string>
|
||||||
<!--Transports: Bluetooth-->
|
<!--Transports: Bluetooth-->
|
||||||
<string name="transport_bt">Bluetooth</string>
|
<string name="transport_bt">Bluetooth</string>
|
||||||
|
<string name="bt_device_status_on">O móbil ten o Bluetooth activado</string>
|
||||||
|
<string name="bt_device_status_off">O móbil ten o Bluetooth desactivado</string>
|
||||||
|
<string name="bt_plugin_status_enabling">Briar está conectando por Bluetooth</string>
|
||||||
|
<string name="bt_plugin_status_active">Briar está conectada ó Bluetooth</string>
|
||||||
|
<string name="bt_plugin_status_inactive">Briar non pode conectar por Bluetooth</string>
|
||||||
|
<string name="bt_plugin_status_disabled">Briar está configurada para non usar Bluetooth</string>
|
||||||
<!--Notifications-->
|
<!--Notifications-->
|
||||||
<string name="reminder_notification_title">Desconectou de Briar</string>
|
<string name="reminder_notification_title">Desconectou de Briar</string>
|
||||||
<string name="reminder_notification_text">Toque para voltar a conectar</string>
|
<string name="reminder_notification_text">Toque para voltar a conectar</string>
|
||||||
@@ -171,7 +195,6 @@
|
|||||||
<string name="connecting_to_device">Conectando co dispositivo\u2026</string>
|
<string name="connecting_to_device">Conectando co dispositivo\u2026</string>
|
||||||
<string name="authenticating_with_device">Autenticándose co dispositivo\u2026</string>
|
<string name="authenticating_with_device">Autenticándose co dispositivo\u2026</string>
|
||||||
<string name="connection_error_title">Non se puido conectar co contacto</string>
|
<string name="connection_error_title">Non se puido conectar co contacto</string>
|
||||||
<string name="connection_error_explanation">Por favor, comprobe que ambas están conectadas a mesma rede Wi-Fi.</string>
|
|
||||||
<string name="connection_error_feedback">Si persiste o problema,<a href="feedback">envíe un informe</a> para axudarnos a mellorar a app.</string>
|
<string name="connection_error_feedback">Si persiste o problema,<a href="feedback">envíe un informe</a> para axudarnos a mellorar a app.</string>
|
||||||
<!--Adding Contacts Remotely-->
|
<!--Adding Contacts Remotely-->
|
||||||
<string name="add_contact_remotely_title_case">Engadir contacto a distancia</string>
|
<string name="add_contact_remotely_title_case">Engadir contacto a distancia</string>
|
||||||
@@ -526,6 +549,7 @@
|
|||||||
<string name="permission_camera_location_title">Cámara e localización</string>
|
<string name="permission_camera_location_title">Cámara e localización</string>
|
||||||
<string name="permission_camera_location_request_body">Para escanear o código QR, Briar precisa acceso a cámara.\n\nPara descubrir dispositivos Bluetooth, Briar precisa permiso a súa localización.\n\nBriar non garda a súa localización nin a comparte con ninguén.</string>
|
<string name="permission_camera_location_request_body">Para escanear o código QR, Briar precisa acceso a cámara.\n\nPara descubrir dispositivos Bluetooth, Briar precisa permiso a súa localización.\n\nBriar non garda a súa localización nin a comparte con ninguén.</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_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_location_denied_body">Denegache o acceso á localización, mais Briar precisa este permiso para descubrir dispositivos Bluetooth.\n\nConsidera conceder o permiso.</string>
|
||||||
<string name="qr_code">Código QR</string>
|
<string name="qr_code">Código QR</string>
|
||||||
<string name="show_qr_code_fullscreen">Amosar o código QR a pantalla completa</string>
|
<string name="show_qr_code_fullscreen">Amosar o código QR a pantalla completa</string>
|
||||||
<!--App Locking-->
|
<!--App Locking-->
|
||||||
@@ -536,6 +560,7 @@
|
|||||||
<string name="lock_is_locked">Briar está bloqueada</string>
|
<string name="lock_is_locked">Briar está bloqueada</string>
|
||||||
<string name="lock_tap_to_unlock">Toque para desbloquear</string>
|
<string name="lock_tap_to_unlock">Toque para desbloquear</string>
|
||||||
<!--Connections Screen-->
|
<!--Connections Screen-->
|
||||||
|
<string name="transports_help_text">Briar pode conectar cos teus contactos a través de Internet, Wi-Fi ou Bluetooth.\n\nTodas as conexións a internet pasan a través da rede Tor para máis privacidade.\n\nSe un contacto é accesible de múltiples xeitos, Briar usaráos en paralelo.</string>
|
||||||
<!--Screenshots-->
|
<!--Screenshots-->
|
||||||
<!--This is a name to be used in screenshots. Feel free to change it to a local name.-->
|
<!--This is a name to be used in screenshots. Feel free to change it to a local name.-->
|
||||||
<string name="screenshot_alice">Alice</string>
|
<string name="screenshot_alice">Alice</string>
|
||||||
|
|||||||
@@ -63,12 +63,36 @@
|
|||||||
<string name="lock_button">נעל יישום</string>
|
<string name="lock_button">נעל יישום</string>
|
||||||
<string name="settings_button">הגדרות</string>
|
<string name="settings_button">הגדרות</string>
|
||||||
<string name="sign_out_button">התנתק</string>
|
<string name="sign_out_button">התנתק</string>
|
||||||
|
<string name="transports_onboarding_text">הקש כאן כדי לשלוט איך Briar מתחבר אל אנשי הקשר שלך.</string>
|
||||||
<!--Transports: Tor-->
|
<!--Transports: Tor-->
|
||||||
<string name="transport_tor">אינטרנט</string>
|
<string name="transport_tor">אינטרנט</string>
|
||||||
|
<string name="tor_device_status_online_wifi">לטלפון שלך יש גישת אינטרנט באמצעות Wi-Fi</string>
|
||||||
|
<string name="tor_device_status_online_mobile">לטלפון שלך יש חיבור אינטרנט באמצעות נתונים סלולריים</string>
|
||||||
|
<string name="tor_device_status_offline">לטלפון שלך אין גישת אינטרנט</string>
|
||||||
|
<string name="tor_plugin_status_enabling">Briar מתחבר אל האינטרנט</string>
|
||||||
|
<string name="tor_plugin_status_active">Briar מחובר אל האינטרנט</string>
|
||||||
|
<string name="tor_plugin_status_inactive">Briar אינו יכול להתחבר אל האינטרנט</string>
|
||||||
|
<string name="tor_plugin_status_disabled">Briar מתוצר לא להשתמש באינטרנט</string>
|
||||||
|
<string name="tor_plugin_status_disabled_mobile_data">Briar מתוצר לא להשתמש בנתונים סלולריים</string>
|
||||||
|
<string name="tor_plugin_status_disabled_battery">Briar מתוצר לא להשתמש באינטרנט בעת הרצה על סוללה</string>
|
||||||
|
<string name="tor_plugin_status_disabled_country_blocked">Briar מתוצר לא להשתמש באינטרנט במדינה זו</string>
|
||||||
<!--Transports: Wi-Fi-->
|
<!--Transports: Wi-Fi-->
|
||||||
<string name="transport_lan">Wi-Fi</string>
|
<string name="transport_lan">Wi-Fi</string>
|
||||||
|
<string name="transport_lan_long">אותה רשת Wi-Fi</string>
|
||||||
|
<string name="lan_device_status_on">הטלפון שלך מחובר אל Wi-Fi</string>
|
||||||
|
<string name="lan_device_status_off">הטלפון שלך אינו מחובר אל Wi-Fi</string>
|
||||||
|
<string name="lan_plugin_status_enabling">Briar מתחבר אל רשת ה־Wi-Fi</string>
|
||||||
|
<string name="lan_plugin_status_active">Briar מחובר אל רשת ה־Wi-Fi</string>
|
||||||
|
<string name="lan_plugin_status_inactive">Briar אינו יכול להתחבר אל רשת ה־Wi-Fi</string>
|
||||||
|
<string name="lan_plugin_status_disabled">Briar מתוצר לא להשתמש ברשת ה־Wi-Fi</string>
|
||||||
<!--Transports: Bluetooth-->
|
<!--Transports: Bluetooth-->
|
||||||
<string name="transport_bt">שן כחולה</string>
|
<string name="transport_bt">Bluetooth</string>
|
||||||
|
<string name="bt_device_status_on">Bluetooth של הטלפון שלך מופעל</string>
|
||||||
|
<string name="bt_device_status_off">Bluetooth של הטלפון שלך מכובה</string>
|
||||||
|
<string name="bt_plugin_status_enabling">Briar מתחבר אל Bluetooth</string>
|
||||||
|
<string name="bt_plugin_status_active">Briar מחובר אל Bluetooth</string>
|
||||||
|
<string name="bt_plugin_status_inactive">Briar אינו יכול להתחבר אל Bluetooth</string>
|
||||||
|
<string name="bt_plugin_status_disabled">Briar מתוצר לא להשתמש ב־Bluetooth</string>
|
||||||
<!--Notifications-->
|
<!--Notifications-->
|
||||||
<string name="reminder_notification_title">נותקת מן Briar</string>
|
<string name="reminder_notification_title">נותקת מן Briar</string>
|
||||||
<string name="reminder_notification_text">הקש כדי להתחבר חזרה.</string>
|
<string name="reminder_notification_text">הקש כדי להתחבר חזרה.</string>
|
||||||
@@ -181,7 +205,6 @@
|
|||||||
<string name="connecting_to_device">מתחבר אל מכשיר\u2026</string>
|
<string name="connecting_to_device">מתחבר אל מכשיר\u2026</string>
|
||||||
<string name="authenticating_with_device">מזדהה מול המכשיר\u2026</string>
|
<string name="authenticating_with_device">מזדהה מול המכשיר\u2026</string>
|
||||||
<string name="connection_error_title">לא היה ניתן להתחבר אל איש הקשר שלך</string>
|
<string name="connection_error_title">לא היה ניתן להתחבר אל איש הקשר שלך</string>
|
||||||
<string name="connection_error_explanation">אנא בדוק ששניכם מחוברים אל אותה רשת Wi-Fi.</string>
|
|
||||||
<string name="connection_error_feedback">אם בעיה נמשכת, אנא <a href="feedback">שלח משוב</a> כדי לעזור לנו לשפר את היישום.</string>
|
<string name="connection_error_feedback">אם בעיה נמשכת, אנא <a href="feedback">שלח משוב</a> כדי לעזור לנו לשפר את היישום.</string>
|
||||||
<!--Adding Contacts Remotely-->
|
<!--Adding Contacts Remotely-->
|
||||||
<string name="add_contact_remotely_title_case">הוסף איש קשר בקרבה</string>
|
<string name="add_contact_remotely_title_case">הוסף איש קשר בקרבה</string>
|
||||||
@@ -436,10 +459,20 @@
|
|||||||
<string name="pref_theme_auto">אוטומטי (שעות יום)</string>
|
<string name="pref_theme_auto">אוטומטי (שעות יום)</string>
|
||||||
<string name="pref_theme_system">ברירת מחדל</string>
|
<string name="pref_theme_system">ברירת מחדל</string>
|
||||||
<!--Settings Connections-->
|
<!--Settings Connections-->
|
||||||
|
<string name="network_settings_title">חיבורים</string>
|
||||||
|
<string name="bluetooth_setting">התחבר אל אנשי קשר באמצעות Bluetooth</string>
|
||||||
|
<string name="wifi_setting">התחבר אל אנשי קשר באותה רשת Wi-Fi</string>
|
||||||
|
<string name="tor_enable_title">התחבר אל אנשי קשר באמצעות האינטרנט</string>
|
||||||
|
<string name="tor_enable_summary">כל החיבורים עוברים דרך רשת Tor למען פרטיות</string>
|
||||||
|
<string name="tor_network_setting">שיטת חיבור עבור רשת Tor</string>
|
||||||
<string name="tor_network_setting_automatic">אוטומטי על סמך מיקום</string>
|
<string name="tor_network_setting_automatic">אוטומטי על סמך מיקום</string>
|
||||||
|
<string name="tor_network_setting_without_bridges">השתמש ברשת Tor בלי גשרים</string>
|
||||||
|
<string name="tor_network_setting_with_bridges">השתמש ברשת Tor עם גשרים</string>
|
||||||
|
<string name="tor_network_setting_never">אל תתחבר אל האינטרנט</string>
|
||||||
<!--How and when Briar will connect to Tor: E.g. "Don't connect to the Internet (in China)" or "Use Tor network with bridges (in Belarus)"-->
|
<!--How and when Briar will connect to Tor: E.g. "Don't connect to the Internet (in China)" or "Use Tor network with bridges (in Belarus)"-->
|
||||||
<string name="tor_network_setting_summary">אוטומטי: %1$s (תוך %2$s)</string>
|
<string name="tor_network_setting_summary">אוטומטי: %1$s (תוך %2$s)</string>
|
||||||
<string name="tor_mobile_data_title">השתמש בנתונים סלולריים</string>
|
<string name="tor_mobile_data_title">השתמש בנתונים סלולריים</string>
|
||||||
|
<string name="tor_only_when_charging_title">התחבר אל האינטרנט רק בעת טעינה</string>
|
||||||
<string name="tor_only_when_charging_summary">משבית חיבור אינטרנט כשהמכשיר עובד על סוללה</string>
|
<string name="tor_only_when_charging_summary">משבית חיבור אינטרנט כשהמכשיר עובד על סוללה</string>
|
||||||
<!--Settings Security and Panic-->
|
<!--Settings Security and Panic-->
|
||||||
<string name="security_settings_title">אבטחה</string>
|
<string name="security_settings_title">אבטחה</string>
|
||||||
@@ -536,7 +569,7 @@
|
|||||||
<string name="permission_camera_title">הרשאת מצלמה</string>
|
<string name="permission_camera_title">הרשאת מצלמה</string>
|
||||||
<string name="permission_camera_request_body">כדי לסרוק את קוד ה־QR, היישום Briar צריך גישה אל המצלמה.</string>
|
<string name="permission_camera_request_body">כדי לסרוק את קוד ה־QR, היישום Briar צריך גישה אל המצלמה.</string>
|
||||||
<string name="permission_location_title">הרשאת מיקום</string>
|
<string name="permission_location_title">הרשאת מיקום</string>
|
||||||
<string name="permission_location_request_body">כדי לגלות מכשירי שן־כחולה, Briar צריך הרשאה להשיג גישה אל מיקומך.\n\nBriar אינו מאחסן את מיקומך או משתף אותו עם אף אחד.</string>
|
<string name="permission_location_request_body">כדי לגלות מכשירי Bluetooth, היישום Briar צריך הרשאה להשיג גישה אל מיקומך.\n\nBriar אינו מאחסן את מיקומך או משתף אותו עם אף אחד.</string>
|
||||||
<string name="permission_camera_location_title">מצלמה ומיקום</string>
|
<string name="permission_camera_location_title">מצלמה ומיקום</string>
|
||||||
<string name="permission_camera_location_request_body">כדי לסרוק את קוד ה־QR, היישום Briar צריך הרשאה אל המצלמה.\n\nכדי לגלות מכשירי שן־כחולה, Briar צריך הרשאה להשיג גישה אל מיקומך.\n\nBriar אינו מאחסן את מיקומך או משתף אותו עם אף אחד.</string>
|
<string name="permission_camera_location_request_body">כדי לסרוק את קוד ה־QR, היישום Briar צריך הרשאה אל המצלמה.\n\nכדי לגלות מכשירי שן־כחולה, Briar צריך הרשאה להשיג גישה אל מיקומך.\n\nBriar אינו מאחסן את מיקומך או משתף אותו עם אף אחד.</string>
|
||||||
<string name="permission_camera_denied_body">דחית גישה אל המצלמה, אבל הוספת אנשי קשר דורשת שימוש במצלמה.\n\nאנא שקול הענקת גישה.</string>
|
<string name="permission_camera_denied_body">דחית גישה אל המצלמה, אבל הוספת אנשי קשר דורשת שימוש במצלמה.\n\nאנא שקול הענקת גישה.</string>
|
||||||
@@ -550,6 +583,7 @@
|
|||||||
<string name="lock_is_locked">Briar נעול</string>
|
<string name="lock_is_locked">Briar נעול</string>
|
||||||
<string name="lock_tap_to_unlock">הקש כדי לבטל נעילה</string>
|
<string name="lock_tap_to_unlock">הקש כדי לבטל נעילה</string>
|
||||||
<!--Connections Screen-->
|
<!--Connections Screen-->
|
||||||
|
<string name="transports_help_text">Briar יכול להתחבר אל אנשי הקשר שלך באמצעות האינטרנט, Wi-Fi או Bluetooth.\n\nכל חיבורי האינטרנט עוברים דרך רשת Tor למען פרטיות.\n\nאם איש קשר ניתן להשגה באמצעות שיטות רבות, Briar משתמש בהן במקביל.</string>
|
||||||
<!--Screenshots-->
|
<!--Screenshots-->
|
||||||
<!--This is a name to be used in screenshots. Feel free to change it to a local name.-->
|
<!--This is a name to be used in screenshots. Feel free to change it to a local name.-->
|
||||||
<string name="screenshot_alice">נועה</string>
|
<string name="screenshot_alice">נועה</string>
|
||||||
|
|||||||
@@ -154,7 +154,6 @@
|
|||||||
<string name="connecting_to_device">उपकरण \ u2026 से कनेक्ट हो रहा है</string>
|
<string name="connecting_to_device">उपकरण \ u2026 से कनेक्ट हो रहा है</string>
|
||||||
<string name="authenticating_with_device">डिवाइस के साथ प्रमाणीकरण \ u2026</string>
|
<string name="authenticating_with_device">डिवाइस के साथ प्रमाणीकरण \ u2026</string>
|
||||||
<string name="connection_error_title">आपके संपर्क से कनेक्ट नहीं हो सका</string>
|
<string name="connection_error_title">आपके संपर्क से कनेक्ट नहीं हो सका</string>
|
||||||
<string name="connection_error_explanation">कृपया जांचें कि आप दोनों एक ही वाई-फाई नेटवर्क से जुड़े हुए हैं।</string>
|
|
||||||
<string name="connection_error_feedback">यदि यह समस्या बनी रहती है, तो कृपया ऐप को बेहतर बनाने में हमारी सहायता के लिए <a href="feedback">फ़ीडबैक भेजें</a>।</string>
|
<string name="connection_error_feedback">यदि यह समस्या बनी रहती है, तो कृपया ऐप को बेहतर बनाने में हमारी सहायता के लिए <a href="feedback">फ़ीडबैक भेजें</a>।</string>
|
||||||
<!--Adding Contacts Remotely-->
|
<!--Adding Contacts Remotely-->
|
||||||
<string name="add_contact_remotely_title_case">दूरी वाले संपर्क जोड़ें</string>
|
<string name="add_contact_remotely_title_case">दूरी वाले संपर्क जोड़ें</string>
|
||||||
|
|||||||
@@ -61,12 +61,36 @@
|
|||||||
<string name="lock_button">App zárolása</string>
|
<string name="lock_button">App zárolása</string>
|
||||||
<string name="settings_button">Beállítások</string>
|
<string name="settings_button">Beállítások</string>
|
||||||
<string name="sign_out_button">Kijelentkezés</string>
|
<string name="sign_out_button">Kijelentkezés</string>
|
||||||
|
<string name="transports_onboarding_text">Érintse meg itt, hogy beállíthassa, hogyan csatlakozzon a Tor kapcsolataihoz.</string>
|
||||||
<!--Transports: Tor-->
|
<!--Transports: Tor-->
|
||||||
<string name="transport_tor">Internet</string>
|
<string name="transport_tor">Internet</string>
|
||||||
|
<string name="tor_device_status_online_wifi">A telefonja Internet hozzáféréssel rendelkezik Wi-Fi-n keresztül</string>
|
||||||
|
<string name="tor_device_status_online_mobile">A telefonja Internet hozzáféréssel rendelkezik mobil hálózaton keresztül</string>
|
||||||
|
<string name="tor_device_status_offline">A telefonja nem rendelkezik Internet hozzáféréssel</string>
|
||||||
|
<string name="tor_plugin_status_enabling">A Briar csatlakozik az Internethez</string>
|
||||||
|
<string name="tor_plugin_status_active">A Briar csatlakoztatva az Internethez</string>
|
||||||
|
<string name="tor_plugin_status_inactive">A Briar nem tud csatlakozni az Internethez</string>
|
||||||
|
<string name="tor_plugin_status_disabled">A Briar Internet nélküli használatra van beállítva</string>
|
||||||
|
<string name="tor_plugin_status_disabled_mobile_data">A Briar mobil hálózat nélküli használatra van beállítva</string>
|
||||||
|
<string name="tor_plugin_status_disabled_battery">A Briar úgy van beállítva, hogy ne használjon Internetet, ha akkumulátorról megy a telefon</string>
|
||||||
|
<string name="tor_plugin_status_disabled_country_blocked">A Briar úgy van beállítva, hogy ne használjon Internetet ebben az országban</string>
|
||||||
<!--Transports: Wi-Fi-->
|
<!--Transports: Wi-Fi-->
|
||||||
<string name="transport_lan">Wi-Fi</string>
|
<string name="transport_lan">Wi-Fi</string>
|
||||||
|
<string name="transport_lan_long">Azonos Wi-Fi hálózat</string>
|
||||||
|
<string name="lan_device_status_on">A telefonja Wi-Fi-hez csatlakoztatott.</string>
|
||||||
|
<string name="lan_device_status_off">A telefonja Wi-Fi-hez nem csatlakoztatott.</string>
|
||||||
|
<string name="lan_plugin_status_enabling">A Briar csatlakozik a Wi-Fi hálózathoz</string>
|
||||||
|
<string name="lan_plugin_status_active">A Briar csatlakoztatva a Wi-Fi hálózathoz</string>
|
||||||
|
<string name="lan_plugin_status_inactive">A Briar nem tud csatlakozni a Wi-Fi hálózathoz</string>
|
||||||
|
<string name="lan_plugin_status_disabled">A Briar Wi-Fi nélküli használatra van beállítva</string>
|
||||||
<!--Transports: Bluetooth-->
|
<!--Transports: Bluetooth-->
|
||||||
<string name="transport_bt">Bluetooth</string>
|
<string name="transport_bt">Bluetooth</string>
|
||||||
|
<string name="bt_device_status_on">A telefonja Bluetooth-ja bekapcsolva</string>
|
||||||
|
<string name="bt_device_status_off">A telefonja Bluetooth-ja kikapcsolva</string>
|
||||||
|
<string name="bt_plugin_status_enabling">A Briar csatlakozik a Bluetooth-hoz</string>
|
||||||
|
<string name="bt_plugin_status_active">A Briar csatlakoztatva a Bluetooth-hoz</string>
|
||||||
|
<string name="bt_plugin_status_inactive">A Briar nem tud csatlakozni a Bluetooth-hoz</string>
|
||||||
|
<string name="bt_plugin_status_disabled">A Briar Bluetooth nélküli használatra van beállítva</string>
|
||||||
<!--Notifications-->
|
<!--Notifications-->
|
||||||
<string name="reminder_notification_title">Kilépve a Briar-ból</string>
|
<string name="reminder_notification_title">Kilépve a Briar-ból</string>
|
||||||
<string name="reminder_notification_text">Érintse meg az újra belépéshez.</string>
|
<string name="reminder_notification_text">Érintse meg az újra belépéshez.</string>
|
||||||
@@ -173,7 +197,6 @@ Biztosan szeretné menteni?</string>
|
|||||||
<string name="connecting_to_device">Csatlakozás az eszközhöz\u2026</string>
|
<string name="connecting_to_device">Csatlakozás az eszközhöz\u2026</string>
|
||||||
<string name="authenticating_with_device">Azonosítás az eszközzel\u2026</string>
|
<string name="authenticating_with_device">Azonosítás az eszközzel\u2026</string>
|
||||||
<string name="connection_error_title">Nem sikerült csatlakozni a kapcsolatához</string>
|
<string name="connection_error_title">Nem sikerült csatlakozni a kapcsolatához</string>
|
||||||
<string name="connection_error_explanation">Ellenőrizzék, hogy mindketten ugyanahhoz a Wi-Fi hálózathoz vannak-e csatlakozva.</string>
|
|
||||||
<string name="connection_error_feedback">Ha ez a probléma tartósan fennáll, kérjük <a href="feedback">küldjön visszajelzést</a> nekünk, hogy segítsen fejleszteni az appot.</string>
|
<string name="connection_error_feedback">Ha ez a probléma tartósan fennáll, kérjük <a href="feedback">küldjön visszajelzést</a> nekünk, hogy segítsen fejleszteni az appot.</string>
|
||||||
<!--Adding Contacts Remotely-->
|
<!--Adding Contacts Remotely-->
|
||||||
<string name="add_contact_remotely_title_case">Távoli kapcsolat hozzá adása</string>
|
<string name="add_contact_remotely_title_case">Távoli kapcsolat hozzá adása</string>
|
||||||
@@ -421,10 +444,19 @@ Kapcsolatai, akivel megosztotta ezt a blogot, lehet nem kapnak többé frissít
|
|||||||
<string name="pref_theme_system">Rendszer alapértelmezett</string>
|
<string name="pref_theme_system">Rendszer alapértelmezett</string>
|
||||||
<!--Settings Connections-->
|
<!--Settings Connections-->
|
||||||
<string name="network_settings_title">Kapcsolatok</string>
|
<string name="network_settings_title">Kapcsolatok</string>
|
||||||
|
<string name="bluetooth_setting">Csatlakozás a kapcsolatokhoz Bluetooth-on</string>
|
||||||
|
<string name="wifi_setting">Csatlakozás az egy Wi-Fi hálózaton lévő kapcsolatokhoz</string>
|
||||||
|
<string name="tor_enable_title">Csatlakozás a kapcsolatokhoz Interneten</string>
|
||||||
|
<string name="tor_enable_summary">Minden kapcsolat átmegy a Tor hálózaton az adatvédelem érdekében</string>
|
||||||
|
<string name="tor_network_setting">Kapcsolódási mód a Tor hálózathoz</string>
|
||||||
<string name="tor_network_setting_automatic">Automatikusan hely alapján</string>
|
<string name="tor_network_setting_automatic">Automatikusan hely alapján</string>
|
||||||
|
<string name="tor_network_setting_without_bridges">Tor hálózat használata hidak nélkül</string>
|
||||||
|
<string name="tor_network_setting_with_bridges">Tor hálózat használata hidakkal</string>
|
||||||
|
<string name="tor_network_setting_never">Ne csatlakozzon az internethez</string>
|
||||||
<!--How and when Briar will connect to Tor: E.g. "Don't connect to the Internet (in China)" or "Use Tor network with bridges (in Belarus)"-->
|
<!--How and when Briar will connect to Tor: E.g. "Don't connect to the Internet (in China)" or "Use Tor network with bridges (in Belarus)"-->
|
||||||
<string name="tor_network_setting_summary">Automatikus: %1$s ( %2$s)</string>
|
<string name="tor_network_setting_summary">Automatikus: %1$s ( %2$s)</string>
|
||||||
<string name="tor_mobile_data_title">Mobil adat használata</string>
|
<string name="tor_mobile_data_title">Mobil adat használata</string>
|
||||||
|
<string name="tor_only_when_charging_title">Csatlakozás az internethez csak töltés alatt</string>
|
||||||
<string name="tor_only_when_charging_summary">Letiltja az internet kapcsolatot, amikor elemről fut az eszköz</string>
|
<string name="tor_only_when_charging_summary">Letiltja az internet kapcsolatot, amikor elemről fut az eszköz</string>
|
||||||
<!--Settings Security and Panic-->
|
<!--Settings Security and Panic-->
|
||||||
<string name="security_settings_title">Biztonság</string>
|
<string name="security_settings_title">Biztonság</string>
|
||||||
@@ -536,6 +568,7 @@ Vigyázat: Ez végleg törli az identitásait, kapcsolatait és üzeneteit</stri
|
|||||||
<string name="lock_is_locked">A Briar zárolt</string>
|
<string name="lock_is_locked">A Briar zárolt</string>
|
||||||
<string name="lock_tap_to_unlock">Érintse meg a zárolás feloldásához</string>
|
<string name="lock_tap_to_unlock">Érintse meg a zárolás feloldásához</string>
|
||||||
<!--Connections Screen-->
|
<!--Connections Screen-->
|
||||||
|
<string name="transports_help_text">A Briar Interneten, Wi-Fi-n vagy Bluetooth-on keresztül csatlakozhat kapcsolataihoz.\n\nAz összes internetkapcsolat a Tor hálózaton megy keresztül megy az adatvédelem érdekében.\n\nHa egy kapcsolatot több módszerrel is el lehet érni, Briar párhuzamosan használja azokat.</string>
|
||||||
<!--Screenshots-->
|
<!--Screenshots-->
|
||||||
<!--This is a name to be used in screenshots. Feel free to change it to a local name.-->
|
<!--This is a name to be used in screenshots. Feel free to change it to a local name.-->
|
||||||
<string name="screenshot_alice">Alice</string>
|
<string name="screenshot_alice">Alice</string>
|
||||||
|
|||||||
@@ -79,8 +79,18 @@
|
|||||||
<string name="transport_lan_long">Sama þráðlausa Wi-Fi netkerfið</string>
|
<string name="transport_lan_long">Sama þráðlausa Wi-Fi netkerfið</string>
|
||||||
<string name="lan_device_status_on">Síminn þinn er tengdur við Wi-Fi</string>
|
<string name="lan_device_status_on">Síminn þinn er tengdur við Wi-Fi</string>
|
||||||
<string name="lan_device_status_off">Síminn þinn er ekki tengdur við Wi-Fi</string>
|
<string name="lan_device_status_off">Síminn þinn er ekki tengdur við Wi-Fi</string>
|
||||||
|
<string name="lan_plugin_status_enabling">Briar er að tengjast við Wi-Fi-netið</string>
|
||||||
|
<string name="lan_plugin_status_active">Briar er tengt við við Wi-Fi-netið</string>
|
||||||
|
<string name="lan_plugin_status_inactive">Briar getur ekki tengst við Wi-Fi-netið</string>
|
||||||
|
<string name="lan_plugin_status_disabled">Briar er sett upp til að nota ekki Wi-Fi-netið</string>
|
||||||
<!--Transports: Bluetooth-->
|
<!--Transports: Bluetooth-->
|
||||||
<string name="transport_bt">Bluetooth</string>
|
<string name="transport_bt">Bluetooth</string>
|
||||||
|
<string name="bt_device_status_on">Kveikt er á Bluetooth-kerfi símans</string>
|
||||||
|
<string name="bt_device_status_off">Slökkt er á Bluetooth-kerfi símans</string>
|
||||||
|
<string name="bt_plugin_status_enabling">Briar er að tengjast við Bluetooth</string>
|
||||||
|
<string name="bt_plugin_status_active">Briar er tengt við Bluetooth</string>
|
||||||
|
<string name="bt_plugin_status_inactive">Briar getur ekki tengst við Bluetooth</string>
|
||||||
|
<string name="bt_plugin_status_disabled">Briar er sett upp til að nota ekki Bluetooth</string>
|
||||||
<!--Notifications-->
|
<!--Notifications-->
|
||||||
<string name="reminder_notification_title">Skráð út úr Briar</string>
|
<string name="reminder_notification_title">Skráð út úr Briar</string>
|
||||||
<string name="reminder_notification_text">Ýttu til að skrá þig aftur inn.</string>
|
<string name="reminder_notification_text">Ýttu til að skrá þig aftur inn.</string>
|
||||||
@@ -185,7 +195,6 @@
|
|||||||
<string name="connecting_to_device">Tengist við tæki\u2026</string>
|
<string name="connecting_to_device">Tengist við tæki\u2026</string>
|
||||||
<string name="authenticating_with_device">Auðkenni við tæki\u2026</string>
|
<string name="authenticating_with_device">Auðkenni við tæki\u2026</string>
|
||||||
<string name="connection_error_title">Gat ekki tengst við tengiliðinn þinn</string>
|
<string name="connection_error_title">Gat ekki tengst við tengiliðinn þinn</string>
|
||||||
<string name="connection_error_explanation">Athugaðu hvort þið séuð ekki báðir tengdir við sama þráðlausa Wi-Fi netið.</string>
|
|
||||||
<string name="connection_error_feedback">Ef þetta vandamál er viðvarandi, ættirðu að <a href="feedback">senda umsögn</a> til að hjálpa okkur að bæta forritið.</string>
|
<string name="connection_error_feedback">Ef þetta vandamál er viðvarandi, ættirðu að <a href="feedback">senda umsögn</a> til að hjálpa okkur að bæta forritið.</string>
|
||||||
<!--Adding Contacts Remotely-->
|
<!--Adding Contacts Remotely-->
|
||||||
<string name="add_contact_remotely_title_case">Bæta við fjarlægum tengilið</string>
|
<string name="add_contact_remotely_title_case">Bæta við fjarlægum tengilið</string>
|
||||||
@@ -426,19 +435,29 @@
|
|||||||
<string name="pref_theme_auto">Sjálfvirkt (eftir tíma dags)</string>
|
<string name="pref_theme_auto">Sjálfvirkt (eftir tíma dags)</string>
|
||||||
<string name="pref_theme_system">Sjálfgefið í kerfinu</string>
|
<string name="pref_theme_system">Sjálfgefið í kerfinu</string>
|
||||||
<!--Settings Connections-->
|
<!--Settings Connections-->
|
||||||
|
<string name="network_settings_title">Tengingar</string>
|
||||||
|
<string name="bluetooth_setting">Tengjast tengiliðum í gegnum Bluetooth</string>
|
||||||
|
<string name="wifi_setting">Tengjast tengiliðum á sama þráðlausa Wi-Fi neti</string>
|
||||||
|
<string name="tor_enable_title">Tengjast tengiliðum í gegnum internetið</string>
|
||||||
|
<string name="tor_enable_summary">Allar tengingar fara í gegnum Tor-netið til að vernda persónuupplýsingar</string>
|
||||||
|
<string name="tor_network_setting">Aðferðir til tengingar við Tor-netið</string>
|
||||||
<string name="tor_network_setting_automatic">Sjálfvirkt byggt á staðsetningu</string>
|
<string name="tor_network_setting_automatic">Sjálfvirkt byggt á staðsetningu</string>
|
||||||
|
<string name="tor_network_setting_without_bridges">Nota Tor-netkerfið án brúa</string>
|
||||||
|
<string name="tor_network_setting_with_bridges">Nota Tor-netkerfið með brúm</string>
|
||||||
|
<string name="tor_network_setting_never">Ekki tengjast við internetið</string>
|
||||||
<!--How and when Briar will connect to Tor: E.g. "Don't connect to the Internet (in China)" or "Use Tor network with bridges (in Belarus)"-->
|
<!--How and when Briar will connect to Tor: E.g. "Don't connect to the Internet (in China)" or "Use Tor network with bridges (in Belarus)"-->
|
||||||
<string name="tor_network_setting_summary">Sjálfvirkt: %1$s (eftir %2$s)</string>
|
<string name="tor_network_setting_summary">Sjálfvirkt: %1$s (eftir %2$s)</string>
|
||||||
<string name="tor_mobile_data_title">Nota farsímagagnasamband</string>
|
<string name="tor_mobile_data_title">Nota farsímagagnasamband</string>
|
||||||
|
<string name="tor_only_when_charging_title">Tengjast í gegnum internetið aðeins þegar verið er í hleðslu</string>
|
||||||
<string name="tor_only_when_charging_summary">Gerir internettengingu óvirka þegar tækið keyrir á rafhlöðu</string>
|
<string name="tor_only_when_charging_summary">Gerir internettengingu óvirka þegar tækið keyrir á rafhlöðu</string>
|
||||||
<!--Settings Security and Panic-->
|
<!--Settings Security and Panic-->
|
||||||
<string name="security_settings_title">Öryggi</string>
|
<string name="security_settings_title">Öryggi</string>
|
||||||
<string name="pref_lock_title">Forritslæsing</string>
|
<string name="pref_lock_title">Forritslæsing</string>
|
||||||
<string name="pref_lock_summary">Notaðu skjálæsingu tækisins til að vernda Briar á meðan þú ert skráð/ur inn</string>
|
<string name="pref_lock_summary">Notaðu skjálæsingu tækisins til að vernda Briar á meðan þú ert skráð/ur inn</string>
|
||||||
<string name="pref_lock_disabled_summary">Til að nota þetta þarftu að setja upp skjálæsingu fyrir tækið þitt</string>
|
<string name="pref_lock_disabled_summary">Til að nota þetta þarftu að setja upp skjálæsingu fyrir tækið þitt</string>
|
||||||
<string name="pref_lock_timeout_title">Tímamörk forritslæsingar við aðgerðaleysi</string>
|
<string name="pref_lock_timeout_title">Tímamörk læsingar forrits við aðgerðaleysi</string>
|
||||||
<!--The %s placeholder is replaced with the following time spans, e.g. 5 Minutes, 1 Hour-->
|
<!--The %s placeholder is replaced with the following time spans, e.g. 5 Minutes, 1 Hour-->
|
||||||
<string name="pref_lock_timeout_summary">Þehar ekki er verið að nota Briar, læsa því sjálfvirkt eftir %s</string>
|
<string name="pref_lock_timeout_summary">Þegar ekki er verið að nota Briar, læsa því sjálfvirkt eftir %s</string>
|
||||||
<!--Will be shown in a list of lock times. Should fit into the %s of "automatically lock it after %s"-->
|
<!--Will be shown in a list of lock times. Should fit into the %s of "automatically lock it after %s"-->
|
||||||
<string name="pref_lock_timeout_1">1 mínúta</string>
|
<string name="pref_lock_timeout_1">1 mínúta</string>
|
||||||
<!--Will be shown in a list of lock times. Should fit into the %s of "automatically lock it after %s"-->
|
<!--Will be shown in a list of lock times. Should fit into the %s of "automatically lock it after %s"-->
|
||||||
@@ -540,6 +559,7 @@
|
|||||||
<string name="lock_is_locked">Briar er læst</string>
|
<string name="lock_is_locked">Briar er læst</string>
|
||||||
<string name="lock_tap_to_unlock">Ýttu til að aflæsa</string>
|
<string name="lock_tap_to_unlock">Ýttu til að aflæsa</string>
|
||||||
<!--Connections Screen-->
|
<!--Connections Screen-->
|
||||||
|
<string name="transports_help_text">Briar getur tengst við tengiliðina þína í gegnum internet, Wi-Fi eða Bluetooth.\n\nAllar internettengingar fara í gegnum Tor-netkerfið til að gæta gagnaleyndar.\n\nEf hægt er að nálgast tengilið með mörgum leiðum, notar Briar þær samhliða.</string>
|
||||||
<!--Screenshots-->
|
<!--Screenshots-->
|
||||||
<!--This is a name to be used in screenshots. Feel free to change it to a local name.-->
|
<!--This is a name to be used in screenshots. Feel free to change it to a local name.-->
|
||||||
<string name="screenshot_alice">Lísa</string>
|
<string name="screenshot_alice">Lísa</string>
|
||||||
|
|||||||
@@ -195,7 +195,6 @@
|
|||||||
<string name="connecting_to_device">Connessione al dispositivo\u2026</string>
|
<string name="connecting_to_device">Connessione al dispositivo\u2026</string>
|
||||||
<string name="authenticating_with_device">Autenticazione con il dispositivo\u2026</string>
|
<string name="authenticating_with_device">Autenticazione con il dispositivo\u2026</string>
|
||||||
<string name="connection_error_title">Impossibile connettersi al tuo contatto</string>
|
<string name="connection_error_title">Impossibile connettersi al tuo contatto</string>
|
||||||
<string name="connection_error_explanation">Verifica di essere entrambi connessi alla stessa rete Wi-Fi.</string>
|
|
||||||
<string name="connection_error_feedback">Se il problema persiste, <a href="feedback">invia un feedback</a> per aiutarci a migliorare l\'app.</string>
|
<string name="connection_error_feedback">Se il problema persiste, <a href="feedback">invia un feedback</a> per aiutarci a migliorare l\'app.</string>
|
||||||
<!--Adding Contacts Remotely-->
|
<!--Adding Contacts Remotely-->
|
||||||
<string name="add_contact_remotely_title_case">Aggiungi contatto distante</string>
|
<string name="add_contact_remotely_title_case">Aggiungi contatto distante</string>
|
||||||
|
|||||||
@@ -149,7 +149,6 @@
|
|||||||
<string name="connecting_to_device">デバイスに接続中\u2026</string>
|
<string name="connecting_to_device">デバイスに接続中\u2026</string>
|
||||||
<string name="authenticating_with_device">デバイス同士での認証中\u2026</string>
|
<string name="authenticating_with_device">デバイス同士での認証中\u2026</string>
|
||||||
<string name="connection_error_title">連絡先に接続できませんでした</string>
|
<string name="connection_error_title">連絡先に接続できませんでした</string>
|
||||||
<string name="connection_error_explanation">あなたと連絡相手、両方が同じWi-Fiネットワークに接続していることを確認してください。</string>
|
|
||||||
<string name="connection_error_feedback">この問題が解決しない場合、アプリを改善するために<a href="feedback">フィードバック</a>を送信してください。</string>
|
<string name="connection_error_feedback">この問題が解決しない場合、アプリを改善するために<a href="feedback">フィードバック</a>を送信してください。</string>
|
||||||
<!--Adding Contacts Remotely-->
|
<!--Adding Contacts Remotely-->
|
||||||
<string name="add_contact_remotely_title_case">離れた場所にいる相手を連絡先に追加</string>
|
<string name="add_contact_remotely_title_case">離れた場所にいる相手を連絡先に追加</string>
|
||||||
|
|||||||
@@ -26,6 +26,8 @@
|
|||||||
<!--Login-->
|
<!--Login-->
|
||||||
<string name="enter_password">비밀번호</string>
|
<string name="enter_password">비밀번호</string>
|
||||||
<string name="try_again">잘못된 비밀번호입니다. 다시 시도하세요.</string>
|
<string name="try_again">잘못된 비밀번호입니다. 다시 시도하세요.</string>
|
||||||
|
<string name="dialog_title_cannot_check_password">비밀번호를 확인할 수 없습니다</string>
|
||||||
|
<string name="dialog_message_cannot_check_password">비밀번호를 확인할 수 없었습니다. 이 문제를 해결하기 위해 기기를 재시작 해 보세요.</string>
|
||||||
<string name="sign_in_button">로그인</string>
|
<string name="sign_in_button">로그인</string>
|
||||||
<string name="forgotten_password">비밀번호를 잊어버렸습니다</string>
|
<string name="forgotten_password">비밀번호를 잊어버렸습니다</string>
|
||||||
<string name="dialog_title_lost_password">비밀번호 분실</string>
|
<string name="dialog_title_lost_password">비밀번호 분실</string>
|
||||||
@@ -41,7 +43,9 @@
|
|||||||
<item quantity="other">이 Briar는 테스트용입니다. %d일 내에 계정이 만료되고 다시 되돌리지 못합니다.</item>
|
<item quantity="other">이 Briar는 테스트용입니다. %d일 내에 계정이 만료되고 다시 되돌리지 못합니다.</item>
|
||||||
</plurals>
|
</plurals>
|
||||||
<string name="expiry_date_reached">이 소프트웨어는 만료되었습니다. \ n 테스트 해 주셔서 감사합니다!</string>
|
<string name="expiry_date_reached">이 소프트웨어는 만료되었습니다. \ n 테스트 해 주셔서 감사합니다!</string>
|
||||||
|
<string name="download_briar">Briar를 계속 이용하기 위해서는 최신 버전을 다운로드 해 주세요.</string>
|
||||||
<string name="create_new_account">새로운 계정을 만들어야 하지만, 같은 별명을 사용할 수 있습니다.</string>
|
<string name="create_new_account">새로운 계정을 만들어야 하지만, 같은 별명을 사용할 수 있습니다.</string>
|
||||||
|
<string name="download_briar_button">최신 버전 다운로드 받기</string>
|
||||||
<string name="startup_open_database">데이터베이스를 복호화하고 있습니다....</string>
|
<string name="startup_open_database">데이터베이스를 복호화하고 있습니다....</string>
|
||||||
<string name="startup_migrate_database">데이터베이스를 업그레이드 하고 있습니다...</string>
|
<string name="startup_migrate_database">데이터베이스를 업그레이드 하고 있습니다...</string>
|
||||||
<string name="startup_compact_database">데이터베이스를 작게 만들고 있습니다...</string>
|
<string name="startup_compact_database">데이터베이스를 작게 만들고 있습니다...</string>
|
||||||
@@ -56,12 +60,36 @@
|
|||||||
<string name="lock_button">앱 잠그기</string>
|
<string name="lock_button">앱 잠그기</string>
|
||||||
<string name="settings_button">설정</string>
|
<string name="settings_button">설정</string>
|
||||||
<string name="sign_out_button">로그아웃</string>
|
<string name="sign_out_button">로그아웃</string>
|
||||||
|
<string name="transports_onboarding_text">여기를 눌러 Briar가 연락처와 연결하는 방법을 설정하세요.</string>
|
||||||
<!--Transports: Tor-->
|
<!--Transports: Tor-->
|
||||||
<string name="transport_tor">인터넷</string>
|
<string name="transport_tor">인터넷</string>
|
||||||
|
<string name="tor_device_status_online_wifi">와이파이를 통해 인터넷에 연결하고 있습니다</string>
|
||||||
|
<string name="tor_device_status_online_mobile">모바일 데이터로 인터넷에 연결하고 있습니다</string>
|
||||||
|
<string name="tor_device_status_offline">인터넷에 연결돼 있지 않습니다</string>
|
||||||
|
<string name="tor_plugin_status_enabling">인터넷에 연결하고 있습니다</string>
|
||||||
|
<string name="tor_plugin_status_active">인터넷에 연결했습니다</string>
|
||||||
|
<string name="tor_plugin_status_inactive">인터넷에 연결하지 못했습니다</string>
|
||||||
|
<string name="tor_plugin_status_disabled">인터넷을 사용하지 않도록 설정돼 있습니다</string>
|
||||||
|
<string name="tor_plugin_status_disabled_mobile_data">모바일 데이터를 사용하지 않도록 설정돼 있습니다</string>
|
||||||
|
<string name="tor_plugin_status_disabled_battery">충전하지 않고 있을 때에는 인터넷을 사용하지 않도록 설정돼 있습니다</string>
|
||||||
|
<string name="tor_plugin_status_disabled_country_blocked">이 국가에서는 인터넷을 사용하지 않도록 설정돼 있습니다</string>
|
||||||
<!--Transports: Wi-Fi-->
|
<!--Transports: Wi-Fi-->
|
||||||
<string name="transport_lan">Wi-Fi</string>
|
<string name="transport_lan">Wi-Fi</string>
|
||||||
|
<string name="transport_lan_long">동일한 와이파이 네트워크입니다</string>
|
||||||
|
<string name="lan_device_status_on">와이파이에 연결돼 있습니다</string>
|
||||||
|
<string name="lan_device_status_off">와이파이에 연결돼 있지 않습니다</string>
|
||||||
|
<string name="lan_plugin_status_enabling">와이파이에 연결 중입니다</string>
|
||||||
|
<string name="lan_plugin_status_active">와이파이에 연결됐습니다</string>
|
||||||
|
<string name="lan_plugin_status_inactive">와이파이에 연결하지 못했습니다</string>
|
||||||
|
<string name="lan_plugin_status_disabled">와이파이를 사용하지 않도록 설정돼 있습니다</string>
|
||||||
<!--Transports: Bluetooth-->
|
<!--Transports: Bluetooth-->
|
||||||
<string name="transport_bt">블루투스</string>
|
<string name="transport_bt">블루투스</string>
|
||||||
|
<string name="bt_device_status_on">블루투스가 켜져 있습니다</string>
|
||||||
|
<string name="bt_device_status_off">블루투스가 꺼져 있습니다</string>
|
||||||
|
<string name="bt_plugin_status_enabling">블루투스에 연결 중입니다</string>
|
||||||
|
<string name="bt_plugin_status_active">블루투스에 연결했습니다</string>
|
||||||
|
<string name="bt_plugin_status_inactive">블루투스에 연결하지 못했습니다</string>
|
||||||
|
<string name="bt_plugin_status_disabled">블루투스를 사용하지 않도록 설정돼 있습니다</string>
|
||||||
<!--Notifications-->
|
<!--Notifications-->
|
||||||
<string name="reminder_notification_title">Briar에서 로그아웃 됨</string>
|
<string name="reminder_notification_title">Briar에서 로그아웃 됨</string>
|
||||||
<string name="reminder_notification_text">눌러서 다시 로그인하세요.</string>
|
<string name="reminder_notification_text">눌러서 다시 로그인하세요.</string>
|
||||||
@@ -103,6 +131,7 @@
|
|||||||
<string name="fix">고치기</string>
|
<string name="fix">고치기</string>
|
||||||
<string name="help">도움</string>
|
<string name="help">도움</string>
|
||||||
<string name="sorry">죄송합니다</string>
|
<string name="sorry">죄송합니다</string>
|
||||||
|
<string name="error_start_activity">이 시스템에서는 지원되지 않습니다</string>
|
||||||
<string name="status_heading">상태</string>
|
<string name="status_heading">상태</string>
|
||||||
<!--Contacts and Private Conversations-->
|
<!--Contacts and Private Conversations-->
|
||||||
<string name="no_contacts">저장된 연락처가 없습니다</string>
|
<string name="no_contacts">저장된 연락처가 없습니다</string>
|
||||||
@@ -118,6 +147,17 @@
|
|||||||
<string name="set_contact_alias">연락처 이름 바꾸기</string>
|
<string name="set_contact_alias">연락처 이름 바꾸기</string>
|
||||||
<string name="set_contact_alias_hint">연락처 이름</string>
|
<string name="set_contact_alias_hint">연락처 이름</string>
|
||||||
<string name="set_alias_button">바꾸기</string>
|
<string name="set_alias_button">바꾸기</string>
|
||||||
|
<string name="delete_all_messages">모든 메시지 지우기</string>
|
||||||
|
<string name="dialog_title_delete_all_messages">메시지 삭제 확인</string>
|
||||||
|
<string name="dialog_message_delete_all_messages">정말로 모든 메시지를 지우려고 하시나요?</string>
|
||||||
|
<string name="dialog_title_not_all_messages_deleted">모든 메시지를 지울 수는 없었습니다</string>
|
||||||
|
<string name="dialog_message_not_deleted_ongoing_both">진행 중인 초대와 소개와 관련된 메시지는 완료되기 전에는 지울 수 없습니다.</string>
|
||||||
|
<string name="dialog_message_not_deleted_ongoing_introductions">진행 중인 소개와 관련된 메시지는 완료되기 전에는 지울 수 없습니다.</string>
|
||||||
|
<string name="dialog_message_not_deleted_ongoing_invitations">진행 중인 초대와 관련된 메시지는 완료되기 전에는 지울 수 없습니다.</string>
|
||||||
|
<string name="dialog_message_not_deleted_partly_downloaded">부분적으로 다운로드 된 메시지는 다운로드가 완료되기 전까지는 지울 수 없습니다.</string>
|
||||||
|
<string name="dialog_message_not_deleted_not_all_selected_both">초대나 소개를 지우기 위해서는 요청을 선택하고 답을 주셔야 합니다.</string>
|
||||||
|
<string name="dialog_message_not_deleted_not_all_selected_introductions">소개를 지우기 위해서는 요청을 선택하고 답을 주셔야 합니다.</string>
|
||||||
|
<string name="dialog_message_not_deleted_not_all_selected_invitations">초대를 지우기 위해서는 요청을 선택하고 답을 주셔야 합니다.</string>
|
||||||
<string name="delete_contact">연락처 삭제하기</string>
|
<string name="delete_contact">연락처 삭제하기</string>
|
||||||
<string name="dialog_title_delete_contact">연락처 삭제 확인</string>
|
<string name="dialog_title_delete_contact">연락처 삭제 확인</string>
|
||||||
<string name="dialog_message_delete_contact">정말로 이 연락처, 그리고 이 연락처와의 메시지를 모두 제거하시겠어요?</string>
|
<string name="dialog_message_delete_contact">정말로 이 연락처, 그리고 이 연락처와의 메시지를 모두 제거하시겠어요?</string>
|
||||||
@@ -133,8 +173,9 @@
|
|||||||
<string name="dialog_message_no_image_support">지인 분의 Briar가 이미지 첨부를 지원하지 않습니다. 이 분이 업그레이드하면 다른 상징을 볼 수 있습니다.</string>
|
<string name="dialog_message_no_image_support">지인 분의 Briar가 이미지 첨부를 지원하지 않습니다. 이 분이 업그레이드하면 다른 상징을 볼 수 있습니다.</string>
|
||||||
<string name="dialog_title_image_support">이제 이 분에게 이미지를 보낼 수 있습니다</string>
|
<string name="dialog_title_image_support">이제 이 분에게 이미지를 보낼 수 있습니다</string>
|
||||||
<string name="dialog_message_image_support">이 상징을 눌러 이미지를 첨부하세요.</string>
|
<string name="dialog_message_image_support">이 상징을 눌러 이미지를 첨부하세요.</string>
|
||||||
|
<string name="messaging_too_many_attachments_toast">첫 %d개의 이미지만 보내질 것입니다</string>
|
||||||
<!--Adding Contacts-->
|
<!--Adding Contacts-->
|
||||||
<string name="add_contact_title">주위의 지인 추가하기</string>
|
<string name="add_contact_title">근처의 연락처 추가하기</string>
|
||||||
<string name="face_to_face">연락처를 추가하려는 사람과 먼저 만나야 합니다.\n\n나중에 누군가 당신인 척 하거나 메시지를 훔쳐보는 것을 방지할 수 있습니다.</string>
|
<string name="face_to_face">연락처를 추가하려는 사람과 먼저 만나야 합니다.\n\n나중에 누군가 당신인 척 하거나 메시지를 훔쳐보는 것을 방지할 수 있습니다.</string>
|
||||||
<string name="continue_button">계속하기</string>
|
<string name="continue_button">계속하기</string>
|
||||||
<string name="try_again_button">다시 시도하기</string>
|
<string name="try_again_button">다시 시도하기</string>
|
||||||
@@ -149,12 +190,11 @@
|
|||||||
<string name="connecting_to_device">\u2026 기기에 연결하고 있습니다</string>
|
<string name="connecting_to_device">\u2026 기기에 연결하고 있습니다</string>
|
||||||
<string name="authenticating_with_device">\u2026 기기와 인증하고 있습니다</string>
|
<string name="authenticating_with_device">\u2026 기기와 인증하고 있습니다</string>
|
||||||
<string name="connection_error_title">연락처에 연결하지 못했습니다</string>
|
<string name="connection_error_title">연락처에 연결하지 못했습니다</string>
|
||||||
<string name="connection_error_explanation">둘 다 같은 Wi-Fi 네트워크에 연결돼 있는지 확인해 보세요.</string>
|
|
||||||
<string name="connection_error_feedback">문제가 계속된다면, 부디 <a href="feedback">피드백을 남겨서</a> 앱이 좋아지게 도와주세요.</string>
|
<string name="connection_error_feedback">문제가 계속된다면, 부디 <a href="feedback">피드백을 남겨서</a> 앱이 좋아지게 도와주세요.</string>
|
||||||
<!--Adding Contacts Remotely-->
|
<!--Adding Contacts Remotely-->
|
||||||
<string name="add_contact_remotely_title_case">멀리서 지인 추가하기</string>
|
<string name="add_contact_remotely_title_case">원거리에서 연락처 추가하기</string>
|
||||||
<string name="add_contact_nearby_title">주위의 지인 추가하기</string>
|
<string name="add_contact_nearby_title">근처의 연락처 추가하기</string>
|
||||||
<string name="add_contact_remotely_title">멀리서 지인 추가하기</string>
|
<string name="add_contact_remotely_title">원거리에서 연락처 추가하기</string>
|
||||||
<string name="contact_link_intro">지인 분에게서 받은 링크를 여기에 입력하세요</string>
|
<string name="contact_link_intro">지인 분에게서 받은 링크를 여기에 입력하세요</string>
|
||||||
<string name="contact_link_hint">지인 분의 링크</string>
|
<string name="contact_link_hint">지인 분의 링크</string>
|
||||||
<string name="paste_button">붙여 넣기</string>
|
<string name="paste_button">붙여 넣기</string>
|
||||||
@@ -194,8 +234,9 @@
|
|||||||
<string name="offline_state">인터넷에 연결되지 않음</string>
|
<string name="offline_state">인터넷에 연결되지 않음</string>
|
||||||
<string name="duplicate_link_dialog_title">중복되는 링크입니다</string>
|
<string name="duplicate_link_dialog_title">중복되는 링크입니다</string>
|
||||||
<string name="duplicate_link_dialog_text_1">%s: 이미 이 링크를 통한 연락 요청이 있습니다.</string>
|
<string name="duplicate_link_dialog_text_1">%s: 이미 이 링크를 통한 연락 요청이 있습니다.</string>
|
||||||
|
<string name="duplicate_link_dialog_text_1_contact">%s: 이 링크의 연락처가 이미 있습니다.</string>
|
||||||
<!--This is a question asking whether two nicknames refer to the same person-->
|
<!--This is a question asking whether two nicknames refer to the same person-->
|
||||||
<string name="duplicate_link_dialog_text_2">%s과(와) %s이(가) 같은 사람인가요?</string>
|
<string name="duplicate_link_dialog_text_2">%s 님과 %s 님이 같은 사람인가요?</string>
|
||||||
<!--This is a button for answering that two nicknames do indeed refer to the same person. This
|
<!--This is a button for answering that two nicknames do indeed refer to the same person. This
|
||||||
string will be used in a dialog button, so if the translation of this string is longer than 20
|
string will be used in a dialog button, so if the translation of this string is longer than 20
|
||||||
characters, please use "Yes" instead, and use "No" for the "Different Person" button-->
|
characters, please use "Yes" instead, and use "No" for the "Different Person" button-->
|
||||||
@@ -204,7 +245,7 @@
|
|||||||
will be used in a dialog button, so if the translation of this string longer than 20 characters,
|
will be used in a dialog button, so if the translation of this string longer than 20 characters,
|
||||||
please use "No" instead, and use "Yes" for the "Same Person" button-->
|
please use "No" instead, and use "Yes" for the "Same Person" button-->
|
||||||
<string name="different_person_button">다른 사람</string>
|
<string name="different_person_button">다른 사람</string>
|
||||||
<string name="duplicate_link_dialog_text_3">%s과(와) %s이(가) 같은 링크를 보냈습니다.\n\n둘 중 하나가 당신의 지인이 누군지 알아내려고 하는 것일 수도 있습니다.\n\n둘 중 누구에게라도 다른 사람에게서 같은 링크를 받았다고 알리지 마십시오.</string>
|
<string name="duplicate_link_dialog_text_3">%s 님과 %s 님이 동일한 링크를 보냈습니다.\n\n누군가 당신의 연락처를 찾으려고 하는 것일 수도 있습니다.\n\n둘 중 누구에게라도 다른 사람에게서 동일한 링크를 받았다고 알리지 마십시오.</string>
|
||||||
<string name="pending_contact_updated_toast">남은 연락 요청이 있는지 확인했습니다</string>
|
<string name="pending_contact_updated_toast">남은 연락 요청이 있는지 확인했습니다</string>
|
||||||
<!--Introductions-->
|
<!--Introductions-->
|
||||||
<string name="introduction_onboarding_title">지인 소개하기</string>
|
<string name="introduction_onboarding_title">지인 소개하기</string>
|
||||||
@@ -218,19 +259,19 @@
|
|||||||
<string name="introduction_sent">소개시켰습니다.</string>
|
<string name="introduction_sent">소개시켰습니다.</string>
|
||||||
<string name="introduction_error">소개시키는 과정에서 문제가 있었습니다.</string>
|
<string name="introduction_error">소개시키는 과정에서 문제가 있었습니다.</string>
|
||||||
<string name="introduction_request_sent">%2$s에게 %1$s을(를) 소개하고 싶다고 전했습니다.</string>
|
<string name="introduction_request_sent">%2$s에게 %1$s을(를) 소개하고 싶다고 전했습니다.</string>
|
||||||
<string name="introduction_request_received">%1$s이(가) %2$s에게 소개하고 싶다고 합니다. %2$s을(를) 연락처 목록에 추가하고 싶으십니까?</string>
|
<string name="introduction_request_received">%1$s 님이 %2$s 님에게 소개하고 싶다고 합니다. %2$s 님을 연락처 목록에 추가하고 싶으십니까?</string>
|
||||||
<string name="introduction_request_exists_received">%1$s이(가) %2$s에게 소개하고 싶다고 물었지만, %2$s은(는) 이미 연락처 목록에 있습니다. %1$s이(가) 아직 이 부분을 모르고 있을 수 있으니, 아직 응할 수 있습니다:</string>
|
<string name="introduction_request_exists_received">%1$s 님이 당신을 %2$s 님에게 소개하고 싶다고 하지만, %2$s 님은 이미 연락처 목록에 있습니다. %1$s 님이 아직 모르고 있을 수 있으니, 아직 응할 수 있습니다:</string>
|
||||||
<string name="introduction_request_answered_received">%1$s이(가) %2$s에게 소개시키고 싶다고 물었습니다.</string>
|
<string name="introduction_request_answered_received">%1$s 님이 당신을 %2$s 님에게 소개시키고 싶다고 합니다.</string>
|
||||||
<string name="introduction_response_accepted_sent">%1$s에게 소개되고 싶다고 했습니다.</string>
|
<string name="introduction_response_accepted_sent">%1$s에게 소개되고 싶다고 했습니다.</string>
|
||||||
<string name="introduction_response_accepted_sent_info">%1$s이(가) 당신의 연락처를 받기 전에, 그 쪽에서도 소개를 승락해야 합니다. 여기서 시간이 좀 걸릴 수 있습니다.</string>
|
<string name="introduction_response_accepted_sent_info">%1$s 님이 소개를 승락해야 연락처에 추가될 수 있습니다. 여기서 시간이 좀 걸릴 수 있습니다.</string>
|
||||||
<string name="introduction_response_declined_sent">%1$s에게 소개되고 싶지 않다고 했습니다.</string>
|
<string name="introduction_response_declined_sent">%1$s에게 소개되고 싶지 않다고 했습니다.</string>
|
||||||
<string name="introduction_response_accepted_received">%1$s이(가) %2$s에게 소개해도 괜찮다고 했습니다.</string>
|
<string name="introduction_response_accepted_received">%1$s 님이 %2$s 님과의 소개를 승락했습니다.</string>
|
||||||
<string name="introduction_response_declined_received">%1$s이(가) %2$s에게 소개되고 싶지 않다고 했습니다.</string>
|
<string name="introduction_response_declined_received">%1$s 님이 %2$s 님과의 소개를 거절했습니다.</string>
|
||||||
<string name="introduction_response_declined_received_by_introducee">%1$s이(가) %2$s이(가) 소개되고 싶지 않다고 합니다.</string>
|
<string name="introduction_response_declined_received_by_introducee">%1$s 님은 %2$s 님이 소개를 거절했다고 합니다.</string>
|
||||||
<!--Private Groups-->
|
<!--Private Groups-->
|
||||||
<string name="groups_list_empty">모임이 없습니다.</string>
|
<string name="groups_list_empty">모임이 없습니다.</string>
|
||||||
<string name="groups_list_empty_action">+ 상징을 눌러 모임을 만들거나, 연락하는 분에게 모임을 공유해 달라고 물어보세요</string>
|
<string name="groups_list_empty_action">+ 상징을 눌러 모임을 만들거나, 연락하는 분에게 모임을 공유해 달라고 여쭤보세요</string>
|
||||||
<string name="groups_created_by">%s이(가) 만듦</string>
|
<string name="groups_created_by">%s 님이 만듦</string>
|
||||||
<plurals name="messages">
|
<plurals name="messages">
|
||||||
<item quantity="other">메시지 %d개</item>
|
<item quantity="other">메시지 %d개</item>
|
||||||
</plurals>
|
</plurals>
|
||||||
@@ -245,9 +286,9 @@
|
|||||||
<string name="groups_member_list">구성원 목록</string>
|
<string name="groups_member_list">구성원 목록</string>
|
||||||
<string name="groups_invite_members">구성원 초대하기</string>
|
<string name="groups_invite_members">구성원 초대하기</string>
|
||||||
<string name="groups_member_created_you">모임을 만들었습니다</string>
|
<string name="groups_member_created_you">모임을 만들었습니다</string>
|
||||||
<string name="groups_member_created">%s이(가) 모임을 만들었습니다</string>
|
<string name="groups_member_created">%s 님이 모임을 만들었습니다</string>
|
||||||
<string name="groups_member_joined_you">모임에 참가했습니다</string>
|
<string name="groups_member_joined_you">모임에 참가했습니다</string>
|
||||||
<string name="groups_member_joined">%s이(가) 모임에 참가했습니다</string>
|
<string name="groups_member_joined">%s 님이 모임에 참가했습니다</string>
|
||||||
<string name="groups_leave">모임 나가기</string>
|
<string name="groups_leave">모임 나가기</string>
|
||||||
<string name="groups_leave_dialog_title">모임 떠나기 확인</string>
|
<string name="groups_leave_dialog_title">모임 떠나기 확인</string>
|
||||||
<string name="groups_leave_dialog_message">정말로 이 모임을 나가고 싶으신가요?</string>
|
<string name="groups_leave_dialog_message">정말로 이 모임을 나가고 싶으신가요?</string>
|
||||||
@@ -260,7 +301,7 @@
|
|||||||
<!--Private Group Invitations-->
|
<!--Private Group Invitations-->
|
||||||
<string name="groups_invitations_title">모임 초대장</string>
|
<string name="groups_invitations_title">모임 초대장</string>
|
||||||
<string name="groups_invitations_invitation_sent">%1$s을(를) \"%2$s\" 모임에 참가하도록 초대했습니다.</string>
|
<string name="groups_invitations_invitation_sent">%1$s을(를) \"%2$s\" 모임에 참가하도록 초대했습니다.</string>
|
||||||
<string name="groups_invitations_invitation_received">%1$s이(가) \"%2$s\" 모임에 참가하도록 초대했습니다.</string>
|
<string name="groups_invitations_invitation_received">%1$s 님이 \"%2$s\" 모임에 참가하도록 초대했습니다.</string>
|
||||||
<string name="groups_invitations_joined">모임에 참가했습니다</string>
|
<string name="groups_invitations_joined">모임에 참가했습니다</string>
|
||||||
<string name="groups_invitations_declined">모임 초대장 거절함</string>
|
<string name="groups_invitations_declined">모임 초대장 거절함</string>
|
||||||
<plurals name="groups_invitations_open">
|
<plurals name="groups_invitations_open">
|
||||||
@@ -268,19 +309,19 @@
|
|||||||
</plurals>
|
</plurals>
|
||||||
<string name="groups_invitations_response_accepted_sent">%s(으)로부터 받은 초대장을 승락했습니다.</string>
|
<string name="groups_invitations_response_accepted_sent">%s(으)로부터 받은 초대장을 승락했습니다.</string>
|
||||||
<string name="groups_invitations_response_declined_sent">%s(으)로부터의 모임 초대장을 거절했습니다.</string>
|
<string name="groups_invitations_response_declined_sent">%s(으)로부터의 모임 초대장을 거절했습니다.</string>
|
||||||
<string name="groups_invitations_response_accepted_received">%s이(가) 모임 초대장을 승락했습니다.</string>
|
<string name="groups_invitations_response_accepted_received">%s 님이 모임 초대장을 승락했습니다.</string>
|
||||||
<string name="groups_invitations_response_declined_received">%s이(가) 모임 초대장을 거절했습니다.</string>
|
<string name="groups_invitations_response_declined_received">%s 님이 모임 초대장을 거절했습니다.</string>
|
||||||
<string name="sharing_status_groups">모임을 만든 분만 새로운 구성원을 초대할 수 있습니다. 다음은 모임의 모든 현재 구성원입니다.</string>
|
<string name="sharing_status_groups">모임을 만든 분만 새로운 구성원을 초대할 수 있습니다. 다음은 모임의 모든 현재 구성원입니다.</string>
|
||||||
<!--Private Groups Revealing Contacts-->
|
<!--Private Groups Revealing Contacts-->
|
||||||
<string name="groups_reveal_contacts">연락처 보이기</string>
|
<string name="groups_reveal_contacts">연락처 공개하기</string>
|
||||||
<string name="groups_reveal_dialog_message">이 모임의 지금과 앞으로 들어올 구성원에게 연락처를 공개할지를 정할 수 있습니다.\n\n연락처를 공개하면, 모임을 만든 사람이 오프라인이어도 공개된 연락처와 연결할 수 있기 때문에 모임에 더 빠르고 안정적으로 연결할 수 있습니다.</string>
|
<string name="groups_reveal_dialog_message">이 모임의 지금과 앞으로 들어올 구성원에게 연락처를 공개할지를 정할 수 있습니다.\n\n연락처를 공개하면, 모임을 만든 사람이 오프라인이어도 공개된 연락처와 연결할 수 있기 때문에 모임에 더 빠르고 안정적으로 연결할 수 있습니다.</string>
|
||||||
<string name="groups_reveal_visible">연락처 관계가 모임에서 보입니다</string>
|
<string name="groups_reveal_visible">연락처 관계가 모임에서 보입니다</string>
|
||||||
<string name="groups_reveal_visible_revealed_by_us">연락처 관계가 모임에서 보입니다(본인이 공개)</string>
|
<string name="groups_reveal_visible_revealed_by_us">(당신이) 모임에게 지인 관계 공개합니다</string>
|
||||||
<string name="groups_reveal_visible_revealed_by_contact">연락처 관계가 모임에서 보입니다(%s이(가) 공개)</string>
|
<string name="groups_reveal_visible_revealed_by_contact">(%s님이) 모임에게 지인 관계를 공개합니다</string>
|
||||||
<string name="groups_reveal_invisible">연락처 관계가 모임에서 보이지 않습니다</string>
|
<string name="groups_reveal_invisible">모임에게 지인 관계를 공개하지 않습니다</string>
|
||||||
<!--Forums-->
|
<!--Forums-->
|
||||||
<string name="no_forums">보여드릴 포럼이 없습니다</string>
|
<string name="no_forums">보여드릴 포럼이 없습니다</string>
|
||||||
<string name="no_forums_action">+ 상징을 눌러 포럼을 만들거나, 연락하는 분에게 포럼을 공유해 달라고 물어보세요 </string>
|
<string name="no_forums_action">+ 상징을 눌러 포럼을 만들거나, 연락하는 분에게 포럼을 공유해 달라고 여쭤보세요</string>
|
||||||
<string name="create_forum_title">포럼 만들기</string>
|
<string name="create_forum_title">포럼 만들기</string>
|
||||||
<string name="choose_forum_hint">만드실 포럼 이름을 정해주세요</string>
|
<string name="choose_forum_hint">만드실 포럼 이름을 정해주세요</string>
|
||||||
<string name="create_forum_button">포럼 만들기</string>
|
<string name="create_forum_button">포럼 만들기</string>
|
||||||
@@ -295,7 +336,7 @@
|
|||||||
<string name="btn_reply">답장</string>
|
<string name="btn_reply">답장</string>
|
||||||
<string name="forum_leave">포럼 떠나기</string>
|
<string name="forum_leave">포럼 떠나기</string>
|
||||||
<string name="dialog_title_leave_forum">포럼 떠나기 확인</string>
|
<string name="dialog_title_leave_forum">포럼 떠나기 확인</string>
|
||||||
<string name="dialog_message_leave_forum">정말로 이 포럼을 떠나려고 하세요?\n\n이 포럼과 공유한 연락처에서 업데이트가 되지 않을 수 있습니다.</string>
|
<string name="dialog_message_leave_forum">정말로 이 포럼을 떠나려고 하세요?\n\n이 포럼과 공유한 지인이 업데이트를 받지 못할 수 있습니다.</string>
|
||||||
<string name="dialog_button_leave">떠나기</string>
|
<string name="dialog_button_leave">떠나기</string>
|
||||||
<string name="forum_left_toast">포럼을 떠났습니다</string>
|
<string name="forum_left_toast">포럼을 떠났습니다</string>
|
||||||
<!--Forum Sharing-->
|
<!--Forum Sharing-->
|
||||||
@@ -303,11 +344,11 @@
|
|||||||
<string name="contacts_selected">선택한 연락처</string>
|
<string name="contacts_selected">선택한 연락처</string>
|
||||||
<string name="activity_share_toolbar_header">연락처 선택하기</string>
|
<string name="activity_share_toolbar_header">연락처 선택하기</string>
|
||||||
<string name="no_contacts_selector">저장된 연락처가 없습니다</string>
|
<string name="no_contacts_selector">저장된 연락처가 없습니다</string>
|
||||||
<string name="no_contacts_selector_action">연락처를 추가한 후에 돌아오길 바랍니다</string>
|
<string name="no_contacts_selector_action">연락처를 추가한 후에 돌아오세요</string>
|
||||||
<string name="forum_shared_snackbar">선택한 지인 분과 공유하는 포럼</string>
|
<string name="forum_shared_snackbar">선택한 지인과 공유하는 포럼</string>
|
||||||
<string name="forum_share_message">메시지 추가하기(선택 사항)</string>
|
<string name="forum_share_message">메시지 추가하기(선택 사항)</string>
|
||||||
<string name="forum_share_error">포럼을 공유하는 과정에서 문제가 있었습니다</string>
|
<string name="forum_share_error">포럼을 공유하는 과정에서 문제가 있었습니다</string>
|
||||||
<string name="forum_invitation_received">%1$s이(가) \"%2$s\" 포럼을 공유했습니다.</string>
|
<string name="forum_invitation_received">%1$s님이 \"%2$s\" 포럼을 공유했습니다.</string>
|
||||||
<string name="forum_invitation_sent">\"%1$s\" 포럼을 %2$s과(와) 공유했습니다.</string>
|
<string name="forum_invitation_sent">\"%1$s\" 포럼을 %2$s과(와) 공유했습니다.</string>
|
||||||
<string name="forum_invitations_title">포럼 초대장</string>
|
<string name="forum_invitations_title">포럼 초대장</string>
|
||||||
<string name="forum_invitation_exists">이미 이 포럼에 초대를 승락했습니다.\n\n초대를 더 많이 승락할수록 더 빠르고 안정적으로 포럼에 연결할 수 있습니다.</string>
|
<string name="forum_invitation_exists">이미 이 포럼에 초대를 승락했습니다.\n\n초대를 더 많이 승락할수록 더 빠르고 안정적으로 포럼에 연결할 수 있습니다.</string>
|
||||||
@@ -317,10 +358,10 @@
|
|||||||
<string name="forum_invitation_already_sharing">이미 공유하고 있습니다</string>
|
<string name="forum_invitation_already_sharing">이미 공유하고 있습니다</string>
|
||||||
<string name="forum_invitation_response_accepted_sent">%s(으)로부터의 포럼 초대를 승락했습니다.</string>
|
<string name="forum_invitation_response_accepted_sent">%s(으)로부터의 포럼 초대를 승락했습니다.</string>
|
||||||
<string name="forum_invitation_response_declined_sent">%s(으)로부터의 포럼 초대를 거절했습니다.</string>
|
<string name="forum_invitation_response_declined_sent">%s(으)로부터의 포럼 초대를 거절했습니다.</string>
|
||||||
<string name="forum_invitation_response_accepted_received">%s이(가) 포럼 초대장을 승락했습니다.</string>
|
<string name="forum_invitation_response_accepted_received">%s님이 포럼 초대장을 승락했습니다.</string>
|
||||||
<string name="forum_invitation_response_declined_received">%s이(가) 포럼 초대장을 거절했습니다.</string>
|
<string name="forum_invitation_response_declined_received">%s 님이 포럼 초대장을 거절했습니다.</string>
|
||||||
<string name="sharing_status">공유 상태</string>
|
<string name="sharing_status">공유 상태</string>
|
||||||
<string name="sharing_status_forum">포럼은 참가한 누구나 지인과 공유할 수 있습니다. 다음 연락처와 포럼을 공유하고 있습니다. 이외 다른 구성원이 더 있을 수 있습니다.</string>
|
<string name="sharing_status_forum">포럼은 참가한 누구나 자신의 지인과 공유할 수 있습니다. 현재 다음 연락처와 포럼을 공유하고 있습니다. 이외 다른 구성원이 더 있을 수 있습니다.</string>
|
||||||
<string name="shared_with">와(과) 공유하고 있습니다.(%1$d %2$d명 온라인)</string>
|
<string name="shared_with">와(과) 공유하고 있습니다.(%1$d %2$d명 온라인)</string>
|
||||||
<plurals name="forums_shared">
|
<plurals name="forums_shared">
|
||||||
<item quantity="other">지인과 공유한 %d개의 포럼</item>
|
<item quantity="other">지인과 공유한 %d개의 포럼</item>
|
||||||
@@ -336,9 +377,9 @@
|
|||||||
<string name="blogs_blog_post_received">새로운 블로그 게시물을 받았습니다</string>
|
<string name="blogs_blog_post_received">새로운 블로그 게시물을 받았습니다</string>
|
||||||
<string name="blogs_blog_post_scroll_to">스크롤</string>
|
<string name="blogs_blog_post_scroll_to">스크롤</string>
|
||||||
<string name="blogs_feed_empty_state">게시물이 없습니다</string>
|
<string name="blogs_feed_empty_state">게시물이 없습니다</string>
|
||||||
<string name="blogs_feed_empty_state_action">연락처와 구독한 블로그의 게시물이 여기에 나타납니다.\n\n게시물을 쓰려면 펜 상징을 누르세요</string>
|
<string name="blogs_feed_empty_state_action">지인이 올린 글과 구독한 블로그의 게시물이 여기에 올라옵니다.\n\n게시물을 작성하려면 펜 상징을 누르세요</string>
|
||||||
<string name="blogs_remove_blog">블로그 제거하기</string>
|
<string name="blogs_remove_blog">블로그 제거하기</string>
|
||||||
<string name="blogs_remove_blog_dialog_message">정말로 이 블로그를 제거하시겠어요?\n\n기기에서 게시물은 제거되지만 다른 사람의 기기에서는 제거되지 않습니다.\n\n이 블로그를 공유하던 연락처에서 업데이트가 되지 않을 수 있습니다. </string>
|
<string name="blogs_remove_blog_dialog_message">정말로 이 블로그를 제거하시겠어요?\n\n게시물이 이 기기에서는 제거되지만 다른 사람의 기기에서는 제거되지 않습니다.\n\n이 블로그를 공유하던 지인이 업데이트를 받지 못하게 될 수 있습니다. </string>
|
||||||
<string name="blogs_remove_blog_ok">제거하기</string>
|
<string name="blogs_remove_blog_ok">제거하기</string>
|
||||||
<string name="blogs_blog_removed">블로그를 제거했습니다</string>
|
<string name="blogs_blog_removed">블로그를 제거했습니다</string>
|
||||||
<string name="blogs_reblog_comment_hint">글 추가하기(선택 사항)</string>
|
<string name="blogs_reblog_comment_hint">글 추가하기(선택 사항)</string>
|
||||||
@@ -347,17 +388,17 @@
|
|||||||
<string name="blogs_sharing_share">블로그 공유하기</string>
|
<string name="blogs_sharing_share">블로그 공유하기</string>
|
||||||
<string name="blogs_sharing_error">블로그를 공유하는 과정에서 문제가 있었습니다.</string>
|
<string name="blogs_sharing_error">블로그를 공유하는 과정에서 문제가 있었습니다.</string>
|
||||||
<string name="blogs_sharing_button">블로그 공유하기</string>
|
<string name="blogs_sharing_button">블로그 공유하기</string>
|
||||||
<string name="blogs_sharing_snackbar">선택한 지인 분과 블로그 공유함</string>
|
<string name="blogs_sharing_snackbar">선택한 지인과 블로그 공유함</string>
|
||||||
<string name="blogs_sharing_response_accepted_sent">%s(으)로부터 받은 블로그 초대장을 승락했습니다.</string>
|
<string name="blogs_sharing_response_accepted_sent">%s(으)로부터 받은 블로그 초대장을 승락했습니다.</string>
|
||||||
<string name="blogs_sharing_response_declined_sent">%s(으)로부터의 블로그 초대장을 거절했습니다.</string>
|
<string name="blogs_sharing_response_declined_sent">%s(으)로부터의 블로그 초대장을 거절했습니다.</string>
|
||||||
<string name="blogs_sharing_response_accepted_received">%s이(가) 블로그 초대를 승락했습니다.</string>
|
<string name="blogs_sharing_response_accepted_received">%s 님이 블로그 초대를 승락했습니다.</string>
|
||||||
<string name="blogs_sharing_response_declined_received">%s이(가) 블로그 초대장을 거절했습니다.</string>
|
<string name="blogs_sharing_response_declined_received">%s 님이 블로그 초대장을 거절했습니다.</string>
|
||||||
<string name="blogs_sharing_invitation_received">%1$s이(가) \"%2$s\" 블로그를 공유했습니다.</string>
|
<string name="blogs_sharing_invitation_received">%1$s 님이 \"%2$s\" 블로그를 공유했습니다.</string>
|
||||||
<string name="blogs_sharing_invitation_sent">%2$s와(과) \"%1$s\" 블로그를 공유했습니다.</string>
|
<string name="blogs_sharing_invitation_sent">%2$s와(과) \"%1$s\" 블로그를 공유했습니다.</string>
|
||||||
<string name="blogs_sharing_invitations_title">블로그 초대장</string>
|
<string name="blogs_sharing_invitations_title">블로그 초대장</string>
|
||||||
<string name="blogs_sharing_joined_toast">블로그에 구독함</string>
|
<string name="blogs_sharing_joined_toast">블로그에 구독함</string>
|
||||||
<string name="blogs_sharing_declined_toast">모임 초대장 거절함</string>
|
<string name="blogs_sharing_declined_toast">모임 초대장 거절함</string>
|
||||||
<string name="sharing_status_blog">블로그는 구독한 누구나 지인과 공유할 수 있습니다. 다음 지인 분과 이 블로그를 공유하고 있습니다. 이외 다른 구독자가 더 있을 수 있습니다.</string>
|
<string name="sharing_status_blog">블로그는 구독한 누구나 자신의 지인과 공유할 수 있습니다. 현재 다음 연락처와 이 블로그를 공유하고 있습니다. 이외 다른 구독자가 더 있을 수 있습니다.</string>
|
||||||
<!--RSS Feeds-->
|
<!--RSS Feeds-->
|
||||||
<string name="blogs_rss_feeds_import">RSS 피드 불러오기</string>
|
<string name="blogs_rss_feeds_import">RSS 피드 불러오기</string>
|
||||||
<string name="blogs_rss_feeds_import_button">가져오기</string>
|
<string name="blogs_rss_feeds_import_button">가져오기</string>
|
||||||
@@ -368,13 +409,13 @@
|
|||||||
<string name="blogs_rss_feeds_manage_author">작성자:</string>
|
<string name="blogs_rss_feeds_manage_author">작성자:</string>
|
||||||
<string name="blogs_rss_feeds_manage_updated">최종 업데이트:</string>
|
<string name="blogs_rss_feeds_manage_updated">최종 업데이트:</string>
|
||||||
<string name="blogs_rss_remove_feed">피드 제거하기</string>
|
<string name="blogs_rss_remove_feed">피드 제거하기</string>
|
||||||
<string name="blogs_rss_remove_feed_dialog_message">정말로 이 피드를 제거하시겠어요?\n\n기기에서 게시물은 제거되지만 다른 사람의 기기에서는 제거되지 않습니다.\n\n이 피드를 공유하던 연락처에서 업데이트가 되지 않을 수 있습니다. </string>
|
<string name="blogs_rss_remove_feed_dialog_message">정말로 이 피드를 제거하시겠어요?\n\n기기에서 게시물은 제거되지만 다른 사람의 기기에서는 제거되지 않습니다.\n\n이 피드를 공유하던 지인이 업데이트를 받지 못하게 될 수 있습니다. </string>
|
||||||
<string name="blogs_rss_remove_feed_ok">제거하기</string>
|
<string name="blogs_rss_remove_feed_ok">제거하기</string>
|
||||||
<string name="blogs_rss_feeds_manage_delete_error">피드를 삭제할 수 없었습니다!</string>
|
<string name="blogs_rss_feeds_manage_delete_error">피드를 삭제할 수 없었습니다!</string>
|
||||||
<string name="blogs_rss_feeds_manage_empty_state">보여드릴 RSS 피드가 없습니다\n\n+ 상징을 눌러 피드를 불러오세요</string>
|
<string name="blogs_rss_feeds_manage_empty_state">보여드릴 RSS 피드가 없습니다\n\n+ 상징을 눌러 피드를 불러오세요</string>
|
||||||
<string name="blogs_rss_feeds_manage_error">피드를 불러오는 과정에서 문제가 있었습니다. 나중에 다시 시도해 주세요.</string>
|
<string name="blogs_rss_feeds_manage_error">피드를 불러오는 과정에서 문제가 있었습니다. 나중에 다시 시도해 주세요.</string>
|
||||||
<!--Settings Display-->
|
<!--Settings Display-->
|
||||||
<string name="pref_language_title">언어 & 지역</string>
|
<string name="pref_language_title">Language & region</string>
|
||||||
<string name="pref_language_changed">Briar를 다시 시작한 후에 설정이 적용됩니다. 부디 로그아웃하고 Briar를 다시 시작해 주세요.</string>
|
<string name="pref_language_changed">Briar를 다시 시작한 후에 설정이 적용됩니다. 부디 로그아웃하고 Briar를 다시 시작해 주세요.</string>
|
||||||
<string name="pref_language_default">기본 설정</string>
|
<string name="pref_language_default">기본 설정</string>
|
||||||
<string name="display_settings_title">화면</string>
|
<string name="display_settings_title">화면</string>
|
||||||
@@ -384,10 +425,20 @@
|
|||||||
<string name="pref_theme_auto">자동(시간에 따라)</string>
|
<string name="pref_theme_auto">자동(시간에 따라)</string>
|
||||||
<string name="pref_theme_system">기본 설정</string>
|
<string name="pref_theme_system">기본 설정</string>
|
||||||
<!--Settings Connections-->
|
<!--Settings Connections-->
|
||||||
|
<string name="network_settings_title">연결</string>
|
||||||
|
<string name="bluetooth_setting">블루투스로 지인과 연결하기</string>
|
||||||
|
<string name="wifi_setting">같은 와이파이 네트워크에 연결된 지인과 연결하기</string>
|
||||||
|
<string name="tor_enable_title">인터넷을 통해 지인과 연결하기</string>
|
||||||
|
<string name="tor_enable_summary">프라이버시를 위해 모든 연결은 Tor 네트워크를 거칩니다</string>
|
||||||
|
<string name="tor_network_setting">Tor 네트워크 연결 방식</string>
|
||||||
<string name="tor_network_setting_automatic">장소에 따라 자동으로</string>
|
<string name="tor_network_setting_automatic">장소에 따라 자동으로</string>
|
||||||
|
<string name="tor_network_setting_without_bridges">브리지 없이 Tor 네트워크 사용하기</string>
|
||||||
|
<string name="tor_network_setting_with_bridges">브지를 통해 Tor 네트워크 사용하기</string>
|
||||||
|
<string name="tor_network_setting_never">인터넷에 연결하지 않기</string>
|
||||||
<!--How and when Briar will connect to Tor: E.g. "Don't connect to the Internet (in China)" or "Use Tor network with bridges (in Belarus)"-->
|
<!--How and when Briar will connect to Tor: E.g. "Don't connect to the Internet (in China)" or "Use Tor network with bridges (in Belarus)"-->
|
||||||
<string name="tor_network_setting_summary">자동: %1$s(%2$s에서)</string>
|
<string name="tor_network_setting_summary">자동: %1$s(%2$s에서)</string>
|
||||||
<string name="tor_mobile_data_title">모바일 데이터 사용하기</string>
|
<string name="tor_mobile_data_title">모바일 데이터 사용하기</string>
|
||||||
|
<string name="tor_only_when_charging_title">충전할 때만 인터넷에 연결하기</string>
|
||||||
<string name="tor_only_when_charging_summary">충전 중이지 않을 때에는 인터넷 연결을 비활성화</string>
|
<string name="tor_only_when_charging_summary">충전 중이지 않을 때에는 인터넷 연결을 비활성화</string>
|
||||||
<!--Settings Security and Panic-->
|
<!--Settings Security and Panic-->
|
||||||
<string name="security_settings_title"> 보안</string>
|
<string name="security_settings_title"> 보안</string>
|
||||||
@@ -422,12 +473,12 @@
|
|||||||
<string name="panic_app_setting_summary">설정된 앱이 없습니다</string>
|
<string name="panic_app_setting_summary">설정된 앱이 없습니다</string>
|
||||||
<string name="panic_app_setting_none">없음</string>
|
<string name="panic_app_setting_none">없음</string>
|
||||||
<string name="dialog_title_connect_panic_app">패닉 앱 확인</string>
|
<string name="dialog_title_connect_panic_app">패닉 앱 확인</string>
|
||||||
<string name="dialog_message_connect_panic_app">%1$s이(가) 파기 권한을 지닌 패닉 버튼 동작을 작동시킬 수 있게 허용하시겠어요?</string>
|
<string name="dialog_message_connect_panic_app">%1$s 님이 파기 권한을 지닌 패닉 버튼 동작을 작동시킬 수 있게 허용하시겠어요?</string>
|
||||||
<string name="panic_setting_destructive_action">파기하기</string>
|
<string name="panic_setting_destructive_action">파기하기</string>
|
||||||
<string name="panic_setting_signout_title">로그아웃</string>
|
<string name="panic_setting_signout_title">로그아웃</string>
|
||||||
<string name="panic_setting_signout_summary">패닉 버튼이 눌리면 Briar에서 로그아웃하기</string>
|
<string name="panic_setting_signout_summary">패닉 버튼이 눌리면 Briar에서 로그아웃하기</string>
|
||||||
<string name="purge_setting_title">계정 삭제하기</string>
|
<string name="purge_setting_title">계정 삭제하기</string>
|
||||||
<string name="purge_setting_summary">패닉 버튼이 눌리면 Briar 계정을 삭제하기 (주의: 계정, 연락처와 메시지가 영구적으로 지워집니다)</string>
|
<string name="purge_setting_summary">패닉 버튼이 눌리면 Briar 계정을 삭제하기. 주의: 계정, 연락처와 메시지가 영구적으로 지워집니다</string>
|
||||||
<!--Settings Notifications-->
|
<!--Settings Notifications-->
|
||||||
<string name="notification_settings_title">알림</string>
|
<string name="notification_settings_title">알림</string>
|
||||||
<string name="notify_sign_in_title">로그인 알려주기</string>
|
<string name="notify_sign_in_title">로그인 알려주기</string>
|
||||||
@@ -498,6 +549,7 @@
|
|||||||
<string name="lock_is_locked">Briar가 잠겼습니다</string>
|
<string name="lock_is_locked">Briar가 잠겼습니다</string>
|
||||||
<string name="lock_tap_to_unlock">눌러 잠금해제</string>
|
<string name="lock_tap_to_unlock">눌러 잠금해제</string>
|
||||||
<!--Connections Screen-->
|
<!--Connections Screen-->
|
||||||
|
<string name="transports_help_text">Briar는 인터넷이나 Wi-Fi, 블루투스를 통해 지인과 연결할 수 있습니다.\n\n인터넷에 연결할 때는 프라이버시를 위해 언제나 Tor 네트워크를 거칩니다.\n\nBriar는 가능한 연락처에 닿을 수 있는 여러 방식을 병용합니다.</string>
|
||||||
<!--Screenshots-->
|
<!--Screenshots-->
|
||||||
<!--This is a name to be used in screenshots. Feel free to change it to a local name.-->
|
<!--This is a name to be used in screenshots. Feel free to change it to a local name.-->
|
||||||
<string name="screenshot_alice">영희</string>
|
<string name="screenshot_alice">영희</string>
|
||||||
|
|||||||
@@ -31,7 +31,7 @@
|
|||||||
<string name="sign_in_button">Prisijungti</string>
|
<string name="sign_in_button">Prisijungti</string>
|
||||||
<string name="forgotten_password">Aš pamiršau savo slaptažodį</string>
|
<string name="forgotten_password">Aš pamiršau savo slaptažodį</string>
|
||||||
<string name="dialog_title_lost_password">Prarastas slaptažodis</string>
|
<string name="dialog_title_lost_password">Prarastas slaptažodis</string>
|
||||||
<string name="dialog_message_lost_password">Jūsų Briar paskyra yra saugoma šifruotu pavidalu jūsų įrenginyje, o ne debesijoje, taigi, negalime atstatyti jūsų slaptažodžio Ar norėtumėte ištrinti savo paskyrą ir pradėti iš naujo?\n\nDėmesio: Jūsų tapatybės, žinutės ir adresatai bus prarasti visiems laikams.</string>
|
<string name="dialog_message_lost_password">Jūsų Briar paskyra yra saugoma šifruotu pavidalu jūsų įrenginyje, o ne debesijoje, taigi, negalime atstatyti jūsų slaptažodžio. Ar norėtumėte ištrinti savo paskyrą ir pradėti iš naujo?\n\nDėmesio: Jūsų tapatybės, žinutės ir adresatai bus prarasti visiems laikams.</string>
|
||||||
<string name="startup_failed_notification_title">Nepavyko paleisti Briar</string>
|
<string name="startup_failed_notification_title">Nepavyko paleisti Briar</string>
|
||||||
<string name="startup_failed_notification_text">Bakstelėkite išsamesnei informacijai.</string>
|
<string name="startup_failed_notification_text">Bakstelėkite išsamesnei informacijai.</string>
|
||||||
<string name="startup_failed_activity_title">Briar paleidimo nesėkmė</string>
|
<string name="startup_failed_activity_title">Briar paleidimo nesėkmė</string>
|
||||||
@@ -191,7 +191,7 @@
|
|||||||
<string name="messaging_too_many_attachments_toast">Bus išsiųsti tik %d pirmi paveikslai</string>
|
<string name="messaging_too_many_attachments_toast">Bus išsiųsti tik %d pirmi paveikslai</string>
|
||||||
<!--Adding Contacts-->
|
<!--Adding Contacts-->
|
||||||
<string name="add_contact_title">Pridėti šalia esantį adresatą</string>
|
<string name="add_contact_title">Pridėti šalia esantį adresatą</string>
|
||||||
<string name="face_to_face">Jūs privalote susitikti gyvai su asmeniu, kurį norite pridėti kaip adresatą.\n\nTai neleis bet kam apsimetinėti jumis ar ateityje skaityti jūsų žinutes.</string>
|
<string name="face_to_face">Jūs privalote susitikti gyvai su asmeniu, kurį norite pridėti kaip adresatą.\n\nTai neleis bet kam apsimetinėti jumis ar ateityje skaityti jūsų žinučių.</string>
|
||||||
<string name="continue_button">Tęsti</string>
|
<string name="continue_button">Tęsti</string>
|
||||||
<string name="try_again_button">Bandyti dar kartą</string>
|
<string name="try_again_button">Bandyti dar kartą</string>
|
||||||
<string name="waiting_for_contact_to_scan">Laukiama, kol adresatas nuskenuos ir prisijungs\u2026</string>
|
<string name="waiting_for_contact_to_scan">Laukiama, kol adresatas nuskenuos ir prisijungs\u2026</string>
|
||||||
@@ -205,7 +205,6 @@
|
|||||||
<string name="connecting_to_device">Jungiamasi prie įrenginio\u2026</string>
|
<string name="connecting_to_device">Jungiamasi prie įrenginio\u2026</string>
|
||||||
<string name="authenticating_with_device">Tapatybės nustatymas su įrenginiu\u2026</string>
|
<string name="authenticating_with_device">Tapatybės nustatymas su įrenginiu\u2026</string>
|
||||||
<string name="connection_error_title">Nepavyko prisijungti prie jūsų adresato</string>
|
<string name="connection_error_title">Nepavyko prisijungti prie jūsų adresato</string>
|
||||||
<string name="connection_error_explanation">Įsitikinkite, kad abu esate prisijungę prie to paties belaidžio (Wi-Fi) tinklo.</string>
|
|
||||||
<string name="connection_error_feedback">Jei ši problema išlieka, <a href="feedback">atsiųskite mums atsiliepimą</a>, kad padėtumėte mums patobulinti programėlę.</string>
|
<string name="connection_error_feedback">Jei ši problema išlieka, <a href="feedback">atsiųskite mums atsiliepimą</a>, kad padėtumėte mums patobulinti programėlę.</string>
|
||||||
<!--Adding Contacts Remotely-->
|
<!--Adding Contacts Remotely-->
|
||||||
<string name="add_contact_remotely_title_case">Pridėti adresatą per atstumą</string>
|
<string name="add_contact_remotely_title_case">Pridėti adresatą per atstumą</string>
|
||||||
@@ -570,6 +569,7 @@
|
|||||||
<string name="permission_camera_location_title">Kamera ir įrenginio vietovė</string>
|
<string name="permission_camera_location_title">Kamera ir įrenginio vietovė</string>
|
||||||
<string name="permission_camera_location_request_body">Tam, kad galėtų nuskenuoti QR kodą, Briar reikia gauti prieigą prie jūsų kameros.\n\nTam, kad galėtų atrasti Bluetooth įrenginius, Briar reikia gauti prieigą prie jūsų įrenginio vietovės.\n\nBriar nesaugo jūsų įrenginio vietovės ir su niekuo jos nebendrina.</string>
|
<string name="permission_camera_location_request_body">Tam, kad galėtų nuskenuoti QR kodą, Briar reikia gauti prieigą prie jūsų kameros.\n\nTam, kad galėtų atrasti Bluetooth įrenginius, Briar reikia gauti prieigą prie jūsų įrenginio vietovės.\n\nBriar nesaugo jūsų įrenginio vietovės ir su niekuo jos nebendrina.</string>
|
||||||
<string name="permission_camera_denied_body">Jūs uždraudėte prieigą prie kameros, tačiau norint pridėti adresatus, reikia naudoti kamerą.\n\nApsvarstykite galimybę sutekti prieigą prie kameros.</string>
|
<string name="permission_camera_denied_body">Jūs uždraudėte prieigą prie kameros, tačiau norint pridėti adresatus, reikia naudoti kamerą.\n\nApsvarstykite galimybę sutekti prieigą prie kameros.</string>
|
||||||
|
<string name="permission_location_denied_body">Jūs uždraudėte prieigą prie įrenginio vietovės, tačiau norint atrasti Bluetooth įrenginius, Briar reikia šio leidimo.\n\nApsvarstykite galimybę sutekti prieigą prie įrenginio vietovės.</string>
|
||||||
<string name="qr_code">QR kodas</string>
|
<string name="qr_code">QR kodas</string>
|
||||||
<string name="show_qr_code_fullscreen">Rodyti QR kodą visame ekrane</string>
|
<string name="show_qr_code_fullscreen">Rodyti QR kodą visame ekrane</string>
|
||||||
<!--App Locking-->
|
<!--App Locking-->
|
||||||
|
|||||||
@@ -61,12 +61,36 @@
|
|||||||
<string name="lock_button">Заклучи ја апликацијата</string>
|
<string name="lock_button">Заклучи ја апликацијата</string>
|
||||||
<string name="settings_button">Поставки</string>
|
<string name="settings_button">Поставки</string>
|
||||||
<string name="sign_out_button">Одјави се</string>
|
<string name="sign_out_button">Одјави се</string>
|
||||||
|
<string name="transports_onboarding_text">Допрете овде за да контролирате како Briar се поврзува со вашите контакти.</string>
|
||||||
<!--Transports: Tor-->
|
<!--Transports: Tor-->
|
||||||
<string name="transport_tor">Интернет</string>
|
<string name="transport_tor">Интернет</string>
|
||||||
|
<string name="tor_device_status_online_wifi">Вашиот телефон има интернет пристап преку Wi-Fi</string>
|
||||||
|
<string name="tor_device_status_online_mobile">Вашиот телефон има интернет пристап преку мобилни податоци</string>
|
||||||
|
<string name="tor_device_status_offline">Вашиот телефон нема интернет пристап</string>
|
||||||
|
<string name="tor_plugin_status_enabling">Briar се поврзува на Интернет</string>
|
||||||
|
<string name="tor_plugin_status_active">Briar е поврзан на Интернет</string>
|
||||||
|
<string name="tor_plugin_status_inactive">Briar не може да се поврзе на Интернет</string>
|
||||||
|
<string name="tor_plugin_status_disabled">Briar е конфигуриран да не користи Интернет</string>
|
||||||
|
<string name="tor_plugin_status_disabled_mobile_data">Briar е конфигуриран да не користи мобилни податоци</string>
|
||||||
|
<string name="tor_plugin_status_disabled_battery">Briar е конфигуриран да не користи Интернет додека е работи на батерија</string>
|
||||||
|
<string name="tor_plugin_status_disabled_country_blocked">Briar е конфигуриран да не користи Интернет во оваа држава</string>
|
||||||
<!--Transports: Wi-Fi-->
|
<!--Transports: Wi-Fi-->
|
||||||
<string name="transport_lan">Wi-Fi</string>
|
<string name="transport_lan">Wi-Fi</string>
|
||||||
|
<string name="transport_lan_long">Иста Wi-Fi мрежа </string>
|
||||||
|
<string name="lan_device_status_on">Вашиот телефон е поврзан на Wi-Fi</string>
|
||||||
|
<string name="lan_device_status_off">Вашиот телефон не е поврзан на Wi-Fi</string>
|
||||||
|
<string name="lan_plugin_status_enabling">Briar се поврзува на Wi-Fi мрежа</string>
|
||||||
|
<string name="lan_plugin_status_active">Briar е поврзан на Wi-Fi мрежата</string>
|
||||||
|
<string name="lan_plugin_status_inactive">Briar сне може да се поврзе на Wi-Fi мрежата</string>
|
||||||
|
<string name="lan_plugin_status_disabled">Briar е конфигуриран да не ја користи Wi-Fi мрежата</string>
|
||||||
<!--Transports: Bluetooth-->
|
<!--Transports: Bluetooth-->
|
||||||
<string name="transport_bt">Bluetooth</string>
|
<string name="transport_bt">Bluetooth</string>
|
||||||
|
<string name="bt_device_status_on">Bluetooth-от на вашиот телефон е вклучен</string>
|
||||||
|
<string name="bt_device_status_off">Bluetooth-от на вашиот телефон е исклучен</string>
|
||||||
|
<string name="bt_plugin_status_enabling">Briar се поврзува со Bluetooth</string>
|
||||||
|
<string name="bt_plugin_status_active">Briar е поврзан со Bluetooth</string>
|
||||||
|
<string name="bt_plugin_status_inactive">Briar не може да се поврзе со Bluetooth</string>
|
||||||
|
<string name="bt_plugin_status_disabled">Briar е конфигуриран да не користи Bluetooth</string>
|
||||||
<!--Notifications-->
|
<!--Notifications-->
|
||||||
<string name="reminder_notification_title">Одјави се од Briar</string>
|
<string name="reminder_notification_title">Одјави се од Briar</string>
|
||||||
<string name="reminder_notification_text">Допрете за да се најавите.</string>
|
<string name="reminder_notification_text">Допрете за да се најавите.</string>
|
||||||
@@ -113,6 +137,7 @@
|
|||||||
<string name="help">Помош</string>
|
<string name="help">Помош</string>
|
||||||
<string name="sorry">Жал ни е</string>
|
<string name="sorry">Жал ни е</string>
|
||||||
<string name="error_start_activity">Недостапно на вашиот систем</string>
|
<string name="error_start_activity">Недостапно на вашиот систем</string>
|
||||||
|
<string name="status_heading">Статус:</string>
|
||||||
<!--Contacts and Private Conversations-->
|
<!--Contacts and Private Conversations-->
|
||||||
<string name="no_contacts">Нема контакти за прикажување</string>
|
<string name="no_contacts">Нема контакти за прикажување</string>
|
||||||
<string name="no_contacts_action">Допрете ја + иконата за да додадете контакт</string>
|
<string name="no_contacts_action">Допрете ја + иконата за да додадете контакт</string>
|
||||||
@@ -170,7 +195,6 @@
|
|||||||
<string name="connecting_to_device">Поврзување со уред\u2026</string>
|
<string name="connecting_to_device">Поврзување со уред\u2026</string>
|
||||||
<string name="authenticating_with_device">Автентикација со уред\u2026</string>
|
<string name="authenticating_with_device">Автентикација со уред\u2026</string>
|
||||||
<string name="connection_error_title">Не може да се поврзе со твојот контакт</string>
|
<string name="connection_error_title">Не може да се поврзе со твојот контакт</string>
|
||||||
<string name="connection_error_explanation">Ве молиме проверете дека и двајцата сте поврзани на истата Wi-Fi мрежа.</string>
|
|
||||||
<string name="connection_error_feedback">Ако овој проблем останува, ве молиме <a href="feedback">испратете повратна информација</a> за да ни помогнете да ја подобриме апликацијата.</string>
|
<string name="connection_error_feedback">Ако овој проблем останува, ве молиме <a href="feedback">испратете повратна информација</a> за да ни помогнете да ја подобриме апликацијата.</string>
|
||||||
<!--Adding Contacts Remotely-->
|
<!--Adding Contacts Remotely-->
|
||||||
<string name="add_contact_remotely_title_case">Додај контакт НА РАСТОЈАНИЕ</string>
|
<string name="add_contact_remotely_title_case">Додај контакт НА РАСТОЈАНИЕ</string>
|
||||||
@@ -411,10 +435,20 @@
|
|||||||
<string name="pref_theme_auto">Автоматски (Дневно)</string>
|
<string name="pref_theme_auto">Автоматски (Дневно)</string>
|
||||||
<string name="pref_theme_system">Систем стандардно поставен</string>
|
<string name="pref_theme_system">Систем стандардно поставен</string>
|
||||||
<!--Settings Connections-->
|
<!--Settings Connections-->
|
||||||
|
<string name="network_settings_title">Поврзувања</string>
|
||||||
|
<string name="bluetooth_setting">Поврзете се контактите преку Bluetooth</string>
|
||||||
|
<string name="wifi_setting">Поврзете се со контактите на истата Wi-Fi мрежа</string>
|
||||||
|
<string name="tor_enable_title">Поврзете се контактите преку Интернет</string>
|
||||||
|
<string name="tor_enable_summary">Сите поврзувања одат преку Tor мрежата поради приватност</string>
|
||||||
|
<string name="tor_network_setting">Метод на поврзување на Tor мрежата</string>
|
||||||
<string name="tor_network_setting_automatic">Автоматски базирано на локација</string>
|
<string name="tor_network_setting_automatic">Автоматски базирано на локација</string>
|
||||||
|
<string name="tor_network_setting_without_bridges">Користете Tor мрежа без мостови</string>
|
||||||
|
<string name="tor_network_setting_with_bridges">Користете Tor мрежа со мостови</string>
|
||||||
|
<string name="tor_network_setting_never">Не се поврзувај на Интернет</string>
|
||||||
<!--How and when Briar will connect to Tor: E.g. "Don't connect to the Internet (in China)" or "Use Tor network with bridges (in Belarus)"-->
|
<!--How and when Briar will connect to Tor: E.g. "Don't connect to the Internet (in China)" or "Use Tor network with bridges (in Belarus)"-->
|
||||||
<string name="tor_network_setting_summary">Автоматски: %1$s (во %2$s)</string>
|
<string name="tor_network_setting_summary">Автоматски: %1$s (во %2$s)</string>
|
||||||
<string name="tor_mobile_data_title">Користи мобилни податоци</string>
|
<string name="tor_mobile_data_title">Користи мобилни податоци</string>
|
||||||
|
<string name="tor_only_when_charging_title">Поврзи на Интернет само за време на полнење</string>
|
||||||
<string name="tor_only_when_charging_summary">Оневозможи го Интернет поврзувањето кога уредот не е приклучен на полнач</string>
|
<string name="tor_only_when_charging_summary">Оневозможи го Интернет поврзувањето кога уредот не е приклучен на полнач</string>
|
||||||
<!--Settings Security and Panic-->
|
<!--Settings Security and Panic-->
|
||||||
<string name="security_settings_title">Безбедност</string>
|
<string name="security_settings_title">Безбедност</string>
|
||||||
@@ -514,7 +548,8 @@
|
|||||||
<string name="permission_location_request_body">За да открие Bluetooth уреди, на Briar му е потребна дозвола за пристап до вашата локација.\n\nBriar не ја зачувува вашата локација или не ја споделува со никого.</string>
|
<string name="permission_location_request_body">За да открие Bluetooth уреди, на Briar му е потребна дозвола за пристап до вашата локација.\n\nBriar не ја зачувува вашата локација или не ја споделува со никого.</string>
|
||||||
<string name="permission_camera_location_title">Камера и локација</string>
|
<string name="permission_camera_location_title">Камера и локација</string>
|
||||||
<string name="permission_camera_location_request_body">За да го скенира QR кодот, на Briar му е потребен пристап до камерата.\n\nЗа да открие Bluetooth уреди, на Briar му е потребна дозвола за вашата локација.\n\nBriar не ја зачувува вашата локација или не ја споделува со никого.</string>
|
<string name="permission_camera_location_request_body">За да го скенира QR кодот, на Briar му е потребен пристап до камерата.\n\nЗа да открие Bluetooth уреди, на Briar му е потребна дозвола за вашата локација.\n\nBriar не ја зачувува вашата локација или не ја споделува со никого.</string>
|
||||||
<string name="permission_camera_denied_body">Го одбивте пристапот до камерата, но за додавање на контакти потребно е користење на камерата.\n\nВе молиме размислете за давање дозвола.</string>
|
<string name="permission_camera_denied_body">Го одбивте пристапот до камерата, но за додавање на контакти потребно е користење на камерата.\n\nВе молиме размислете за давање пристап.</string>
|
||||||
|
<string name="permission_location_denied_body">Го одбивте пристапот до вашата локација, но на Briar му е потребна оваа дозвола за да ги открие Bluetooth уредите.\n\nВе молиме размислете за давање пристап.</string>
|
||||||
<string name="qr_code">QR код</string>
|
<string name="qr_code">QR код</string>
|
||||||
<string name="show_qr_code_fullscreen">Покажи го QR кодот на цел екран</string>
|
<string name="show_qr_code_fullscreen">Покажи го QR кодот на цел екран</string>
|
||||||
<!--App Locking-->
|
<!--App Locking-->
|
||||||
@@ -525,6 +560,7 @@
|
|||||||
<string name="lock_is_locked">Briar е заклучен</string>
|
<string name="lock_is_locked">Briar е заклучен</string>
|
||||||
<string name="lock_tap_to_unlock">Допрете за отклучување</string>
|
<string name="lock_tap_to_unlock">Допрете за отклучување</string>
|
||||||
<!--Connections Screen-->
|
<!--Connections Screen-->
|
||||||
|
<string name="transports_help_text">Briar може да се поврзе со вашите контакти преку Интернет, Wi-Fi или Bluetooth.\n\nСите Интернет поврзувања одат преку Tor Мрежата поради приватност.\n\nАко контактот може да биде достапен преку повеќе начини, Briar ќе ги користи паралелно. </string>
|
||||||
<!--Screenshots-->
|
<!--Screenshots-->
|
||||||
<!--This is a name to be used in screenshots. Feel free to change it to a local name.-->
|
<!--This is a name to be used in screenshots. Feel free to change it to a local name.-->
|
||||||
<string name="screenshot_alice">Жарко</string>
|
<string name="screenshot_alice">Жарко</string>
|
||||||
|
|||||||
@@ -61,12 +61,36 @@
|
|||||||
<string name="lock_button">App vergrendelen</string>
|
<string name="lock_button">App vergrendelen</string>
|
||||||
<string name="settings_button">Instellingen</string>
|
<string name="settings_button">Instellingen</string>
|
||||||
<string name="sign_out_button">Log uit</string>
|
<string name="sign_out_button">Log uit</string>
|
||||||
|
<string name="transports_onboarding_text">Tik hier om in te stellen hoe Briar een verbinding met je contacten opzet.</string>
|
||||||
<!--Transports: Tor-->
|
<!--Transports: Tor-->
|
||||||
<string name="transport_tor">Internet</string>
|
<string name="transport_tor">Internet</string>
|
||||||
|
<string name="tor_device_status_online_wifi">Je apparaat heeft internettoegang via wifi</string>
|
||||||
|
<string name="tor_device_status_online_mobile">Je apparaat heeft internettoegang via mobiele dataverbinding</string>
|
||||||
|
<string name="tor_device_status_offline">Je apparaat heeft geen internettoegang</string>
|
||||||
|
<string name="tor_plugin_status_enabling">Briar is aan het verbinden met internet</string>
|
||||||
|
<string name="tor_plugin_status_active">Briar is verbonden met het internet</string>
|
||||||
|
<string name="tor_plugin_status_inactive">Briar kan geen verbinding maken met het internet</string>
|
||||||
|
<string name="tor_plugin_status_disabled">Briar is geconfigureerd om internet niet te gebruiken</string>
|
||||||
|
<string name="tor_plugin_status_disabled_mobile_data">Briar is geconfigureerd om mobiele dataverbinding niet te gebruiken</string>
|
||||||
|
<string name="tor_plugin_status_disabled_battery">Briar is geconfigureerd om het internet niet te gebruiken als apparaat op baterij loopt</string>
|
||||||
|
<string name="tor_plugin_status_disabled_country_blocked">Briar is geconfigureerd om internet niet te gebruiken in dit land</string>
|
||||||
<!--Transports: Wi-Fi-->
|
<!--Transports: Wi-Fi-->
|
||||||
<string name="transport_lan">Wifi</string>
|
<string name="transport_lan">Wifi</string>
|
||||||
|
<string name="transport_lan_long">Hetzelfde wifinetwerk</string>
|
||||||
|
<string name="lan_device_status_on">Je apparaat is verbonden met wifi</string>
|
||||||
|
<string name="lan_device_status_off">Je apparaat is niet verbonden met wifi</string>
|
||||||
|
<string name="lan_plugin_status_enabling">Briar is verbinding aan het maken met het wifinetwerk</string>
|
||||||
|
<string name="lan_plugin_status_active">Briar is verbonden met het wifinetwerk</string>
|
||||||
|
<string name="lan_plugin_status_inactive">Briar kan geen verbinding maken met het wifinetwerk</string>
|
||||||
|
<string name="lan_plugin_status_disabled">Briar is geconfigureerd om het wifinetwerk niet te gebruiken</string>
|
||||||
<!--Transports: Bluetooth-->
|
<!--Transports: Bluetooth-->
|
||||||
<string name="transport_bt">Bluetooth</string>
|
<string name="transport_bt">Bluetooth</string>
|
||||||
|
<string name="bt_device_status_on">Bluetooth op je apparaat staat aan</string>
|
||||||
|
<string name="bt_device_status_off">Bluetooth op je apparaat staat uit</string>
|
||||||
|
<string name="bt_plugin_status_enabling">Briar is aan het verbinden met bluetooth</string>
|
||||||
|
<string name="bt_plugin_status_active">Briar is verbonden met bluetooth</string>
|
||||||
|
<string name="bt_plugin_status_inactive">Briar kan geen verbinding maken met bluetooth</string>
|
||||||
|
<string name="bt_plugin_status_disabled">Briar is geconfigureerd om bluetooth niet te gebruiken</string>
|
||||||
<!--Notifications-->
|
<!--Notifications-->
|
||||||
<string name="reminder_notification_title">Uitgelogd van Briar</string>
|
<string name="reminder_notification_title">Uitgelogd van Briar</string>
|
||||||
<string name="reminder_notification_text">Tik om opnieuw in te loggen.</string>
|
<string name="reminder_notification_text">Tik om opnieuw in te loggen.</string>
|
||||||
@@ -171,7 +195,6 @@
|
|||||||
<string name="connecting_to_device">Aan het verbinden met apparaat\u2026</string>
|
<string name="connecting_to_device">Aan het verbinden met apparaat\u2026</string>
|
||||||
<string name="authenticating_with_device">Aan het authenticeren met apparaat\u2026</string>
|
<string name="authenticating_with_device">Aan het authenticeren met apparaat\u2026</string>
|
||||||
<string name="connection_error_title">Kon geen verbinding maken met je contact</string>
|
<string name="connection_error_title">Kon geen verbinding maken met je contact</string>
|
||||||
<string name="connection_error_explanation">Controleer alsjeblieft dat jullie beiden met hetzelfde wifinetwerk zijn verbonden.</string>
|
|
||||||
<string name="connection_error_feedback">Als dit probleem aanhoudt, <a href="feedback">stuur feedback</a> alsjeblieft om ons te helpen de app te verbeteren.</string>
|
<string name="connection_error_feedback">Als dit probleem aanhoudt, <a href="feedback">stuur feedback</a> alsjeblieft om ons te helpen de app te verbeteren.</string>
|
||||||
<!--Adding Contacts Remotely-->
|
<!--Adding Contacts Remotely-->
|
||||||
<string name="add_contact_remotely_title_case">Voeg een contact ver weg toe</string>
|
<string name="add_contact_remotely_title_case">Voeg een contact ver weg toe</string>
|
||||||
@@ -536,6 +559,7 @@
|
|||||||
<string name="lock_is_locked">Briar is vergrendeld</string>
|
<string name="lock_is_locked">Briar is vergrendeld</string>
|
||||||
<string name="lock_tap_to_unlock">Tik om te ontgrendelen</string>
|
<string name="lock_tap_to_unlock">Tik om te ontgrendelen</string>
|
||||||
<!--Connections Screen-->
|
<!--Connections Screen-->
|
||||||
|
<string name="transports_help_text">Briar kan met je contacten verbinden via het internet, wifi of bluetooth.\n\nAlle internetverbindingen gaan voor privacy door het Tornetwerk.\n\nAls een contact via meerdere methoden te bereiken is, zal Briar die parallel gebruiken.</string>
|
||||||
<!--Screenshots-->
|
<!--Screenshots-->
|
||||||
<!--This is a name to be used in screenshots. Feel free to change it to a local name.-->
|
<!--This is a name to be used in screenshots. Feel free to change it to a local name.-->
|
||||||
<string name="screenshot_alice">Veerle</string>
|
<string name="screenshot_alice">Veerle</string>
|
||||||
|
|||||||
@@ -169,7 +169,6 @@ Volètz suprimir vòstre compte e ne crear un nòu ?\n
|
|||||||
<string name="connecting_to_device">Connexion a l’aparelh\u2026</string>
|
<string name="connecting_to_device">Connexion a l’aparelh\u2026</string>
|
||||||
<string name="authenticating_with_device">Autentificacion amb l’aparelh\u2026</string>
|
<string name="authenticating_with_device">Autentificacion amb l’aparelh\u2026</string>
|
||||||
<string name="connection_error_title">Connexion impossibla al contacte</string>
|
<string name="connection_error_title">Connexion impossibla al contacte</string>
|
||||||
<string name="connection_error_explanation">Verificatz que sètz los dos connectats al meteis ret wifi.</string>
|
|
||||||
<string name="connection_error_feedback">S’aqueste problèma dura, mercés <a href="feedback"> d’enviar un comentari</a> per nos ajudar a melhorar l’aplicacion.</string>
|
<string name="connection_error_feedback">S’aqueste problèma dura, mercés <a href="feedback"> d’enviar un comentari</a> per nos ajudar a melhorar l’aplicacion.</string>
|
||||||
<!--Adding Contacts Remotely-->
|
<!--Adding Contacts Remotely-->
|
||||||
<string name="add_contact_remotely_title_case">Ajustar un contacte a distància</string>
|
<string name="add_contact_remotely_title_case">Ajustar un contacte a distància</string>
|
||||||
|
|||||||
@@ -165,7 +165,6 @@
|
|||||||
<string name="connecting_to_device">Łączenie z urządzeniem\u2026</string>
|
<string name="connecting_to_device">Łączenie z urządzeniem\u2026</string>
|
||||||
<string name="authenticating_with_device">Autoryzowanie z urządzeniem\u2026</string>
|
<string name="authenticating_with_device">Autoryzowanie z urządzeniem\u2026</string>
|
||||||
<string name="connection_error_title">Nie udało się połączyć z kontaktem</string>
|
<string name="connection_error_title">Nie udało się połączyć z kontaktem</string>
|
||||||
<string name="connection_error_explanation">Sprawdź czy obaj jesteście połączeni z tą samą siecią Wi-Fi.</string>
|
|
||||||
<string name="connection_error_feedback">Jeśli problem będzie występować dalej, proszę <a href="feedback">wysłać zgłoszenie</a> aby pomóc nam ulepszyć aplikację.</string>
|
<string name="connection_error_feedback">Jeśli problem będzie występować dalej, proszę <a href="feedback">wysłać zgłoszenie</a> aby pomóc nam ulepszyć aplikację.</string>
|
||||||
<!--Adding Contacts Remotely-->
|
<!--Adding Contacts Remotely-->
|
||||||
<string name="add_contact_remotely_title_case">Dodaj Kontakt na odległość</string>
|
<string name="add_contact_remotely_title_case">Dodaj Kontakt na odległość</string>
|
||||||
|
|||||||
@@ -61,6 +61,7 @@
|
|||||||
<string name="lock_button">Bloquear Aplicativo</string>
|
<string name="lock_button">Bloquear Aplicativo</string>
|
||||||
<string name="settings_button">Configurações</string>
|
<string name="settings_button">Configurações</string>
|
||||||
<string name="sign_out_button">Sair</string>
|
<string name="sign_out_button">Sair</string>
|
||||||
|
<string name="transports_onboarding_text">Clique aqui para controlar como o Briar se conecta aos seus contatos.</string>
|
||||||
<!--Transports: Tor-->
|
<!--Transports: Tor-->
|
||||||
<string name="transport_tor">Internet</string>
|
<string name="transport_tor">Internet</string>
|
||||||
<!--Transports: Wi-Fi-->
|
<!--Transports: Wi-Fi-->
|
||||||
@@ -171,7 +172,6 @@
|
|||||||
<string name="connecting_to_device">Conectando a device\u2026</string>
|
<string name="connecting_to_device">Conectando a device\u2026</string>
|
||||||
<string name="authenticating_with_device">Autenticando com o dispositivo\u2026</string>
|
<string name="authenticating_with_device">Autenticando com o dispositivo\u2026</string>
|
||||||
<string name="connection_error_title">Não foi possível conectar-se ao seu contato</string>
|
<string name="connection_error_title">Não foi possível conectar-se ao seu contato</string>
|
||||||
<string name="connection_error_explanation">Por favor, certifique se ambos estão conectados na mesma rede Wi-Fi.</string>
|
|
||||||
<string name="connection_error_feedback">Se o problema persistir, por favor <a href="feedback">envie um feedback</a> para nos ajudar a melhorar o app.</string>
|
<string name="connection_error_feedback">Se o problema persistir, por favor <a href="feedback">envie um feedback</a> para nos ajudar a melhorar o app.</string>
|
||||||
<!--Adding Contacts Remotely-->
|
<!--Adding Contacts Remotely-->
|
||||||
<string name="add_contact_remotely_title_case">Adicionar contato à distância</string>
|
<string name="add_contact_remotely_title_case">Adicionar contato à distância</string>
|
||||||
|
|||||||
@@ -200,7 +200,6 @@
|
|||||||
<string name="connecting_to_device">Conectare la dispozitiv\u2026</string>
|
<string name="connecting_to_device">Conectare la dispozitiv\u2026</string>
|
||||||
<string name="authenticating_with_device">Autentificare cu dispozitivul\u2026</string>
|
<string name="authenticating_with_device">Autentificare cu dispozitivul\u2026</string>
|
||||||
<string name="connection_error_title">Nu s-a putut face conexiunea la contactul dumneavoastră</string>
|
<string name="connection_error_title">Nu s-a putut face conexiunea la contactul dumneavoastră</string>
|
||||||
<string name="connection_error_explanation">Verificați să fiți conectați la aceeași rețea Wi-Fi.</string>
|
|
||||||
<string name="connection_error_feedback">Dacă problema persistă, vă rugăm să <a href="feedback">trimiteți feedback</a> pentru a ne ajuta să îmbunătățim aplicația.</string>
|
<string name="connection_error_feedback">Dacă problema persistă, vă rugăm să <a href="feedback">trimiteți feedback</a> pentru a ne ajuta să îmbunătățim aplicația.</string>
|
||||||
<!--Adding Contacts Remotely-->
|
<!--Adding Contacts Remotely-->
|
||||||
<string name="add_contact_remotely_title_case">Adaugă un contact la distanță</string>
|
<string name="add_contact_remotely_title_case">Adaugă un contact la distanță</string>
|
||||||
@@ -560,6 +559,7 @@
|
|||||||
<string name="permission_camera_location_title">Cameră foto și locație</string>
|
<string name="permission_camera_location_title">Cameră foto și locație</string>
|
||||||
<string name="permission_camera_location_request_body">Pentru a scana codul QR, Briar are nevoie să acceseze camera foto.\n\nPentru a putea descoperi dispozitive Bluetooth, Briar are nevoie de permisiunea de a vă accesa locația.\n\nBriar nu vă stochează locația și nici nu o partajează cu nimeni. </string>
|
<string name="permission_camera_location_request_body">Pentru a scana codul QR, Briar are nevoie să acceseze camera foto.\n\nPentru a putea descoperi dispozitive Bluetooth, Briar are nevoie de permisiunea de a vă accesa locația.\n\nBriar nu vă stochează locația și nici nu o partajează cu nimeni. </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_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_location_denied_body">Ați refuzat accesul la locație, dar Briar are nevoie de această permisiune pentru a descoperi dispozitive Bluetooth.\n\nVă rugăm să luați în considerare acordarea accesului.</string>
|
||||||
<string name="qr_code">Cod QR</string>
|
<string name="qr_code">Cod QR</string>
|
||||||
<string name="show_qr_code_fullscreen">Arată codul QR pe tot ecranul</string>
|
<string name="show_qr_code_fullscreen">Arată codul QR pe tot ecranul</string>
|
||||||
<!--App Locking-->
|
<!--App Locking-->
|
||||||
|
|||||||
@@ -65,12 +65,36 @@
|
|||||||
<string name="lock_button">Заблокировать приложение</string>
|
<string name="lock_button">Заблокировать приложение</string>
|
||||||
<string name="settings_button">Настройки</string>
|
<string name="settings_button">Настройки</string>
|
||||||
<string name="sign_out_button">Выйти</string>
|
<string name="sign_out_button">Выйти</string>
|
||||||
|
<string name="transports_onboarding_text">Нажмите здесь, чтобы проверить, как Briar подключается к вашим контактам.</string>
|
||||||
<!--Transports: Tor-->
|
<!--Transports: Tor-->
|
||||||
<string name="transport_tor">Интернет</string>
|
<string name="transport_tor">Интернет</string>
|
||||||
|
<string name="tor_device_status_online_wifi">Ваш телефон имеет доступ в интернет через Wi-Fi</string>
|
||||||
|
<string name="tor_device_status_online_mobile">Ваш телефон имеет доступ в интернет через мобильную сеть.</string>
|
||||||
|
<string name="tor_device_status_offline">Ваш телефон не имеет доступа в интернет</string>
|
||||||
|
<string name="tor_plugin_status_enabling">Briar подключается к интернету</string>
|
||||||
|
<string name="tor_plugin_status_active">Briar подключен к интернету</string>
|
||||||
|
<string name="tor_plugin_status_inactive">Briar не может подключиться к интернету.</string>
|
||||||
|
<string name="tor_plugin_status_disabled">Briar настроен не использовать интернет</string>
|
||||||
|
<string name="tor_plugin_status_disabled_mobile_data">Briar настроен не использовать мобильную сеть</string>
|
||||||
|
<string name="tor_plugin_status_disabled_battery">Briar настроен не использовать интернет при работе от батареи</string>
|
||||||
|
<string name="tor_plugin_status_disabled_country_blocked">Briar настроен не использовать интернет в этой стране</string>
|
||||||
<!--Transports: Wi-Fi-->
|
<!--Transports: Wi-Fi-->
|
||||||
<string name="transport_lan">Wi-Fi</string>
|
<string name="transport_lan">Wi-Fi</string>
|
||||||
|
<string name="transport_lan_long">Та же сеть Wi-Fi</string>
|
||||||
|
<string name="lan_device_status_on">Ваш телефон подключен к Wi-Fi</string>
|
||||||
|
<string name="lan_device_status_off">Телефон не подключен к Wi-Fi</string>
|
||||||
|
<string name="lan_plugin_status_enabling">Briar подключается к сети Wi-Fi</string>
|
||||||
|
<string name="lan_plugin_status_active">Briar подключен к сети Wi-Fi</string>
|
||||||
|
<string name="lan_plugin_status_inactive">Briar не может подключиться к сети Wi-Fi.</string>
|
||||||
|
<string name="lan_plugin_status_disabled">Briar настроен не использовать сеть Wi-Fi.</string>
|
||||||
<!--Transports: Bluetooth-->
|
<!--Transports: Bluetooth-->
|
||||||
<string name="transport_bt">Bluetooth</string>
|
<string name="transport_bt">Bluetooth</string>
|
||||||
|
<string name="bt_device_status_on">Bluetooth вашего телефона включен.</string>
|
||||||
|
<string name="bt_device_status_off">Bluetooth вашего телефона отключен.</string>
|
||||||
|
<string name="bt_plugin_status_enabling">Briar подключается к Bluetooth</string>
|
||||||
|
<string name="bt_plugin_status_active">Briar подключен к Bluetooth</string>
|
||||||
|
<string name="bt_plugin_status_inactive">Briar не может подключиться к Bluetooth.</string>
|
||||||
|
<string name="bt_plugin_status_disabled">Briar настроен не использовать Bluetooth</string>
|
||||||
<!--Notifications-->
|
<!--Notifications-->
|
||||||
<string name="reminder_notification_title">Вы не авторизованы в Briar</string>
|
<string name="reminder_notification_title">Вы не авторизованы в Briar</string>
|
||||||
<string name="reminder_notification_text">Нажмите для входа</string>
|
<string name="reminder_notification_text">Нажмите для входа</string>
|
||||||
@@ -183,7 +207,6 @@
|
|||||||
<string name="connecting_to_device">Подключение к устройству\u2026</string>
|
<string name="connecting_to_device">Подключение к устройству\u2026</string>
|
||||||
<string name="authenticating_with_device">Аутентификация с устройством\u2026</string>
|
<string name="authenticating_with_device">Аутентификация с устройством\u2026</string>
|
||||||
<string name="connection_error_title">Не удалось подключиться к контакту</string>
|
<string name="connection_error_title">Не удалось подключиться к контакту</string>
|
||||||
<string name="connection_error_explanation">Убедитесь, что вы оба подключены к той же сети Wi-Fi.</string>
|
|
||||||
<string name="connection_error_feedback">Если эта проблема сохраняется, пожалуйста <a href="feedback">отправьте отзыв</a>, чтобы помочь нам улучшить приложение.</string>
|
<string name="connection_error_feedback">Если эта проблема сохраняется, пожалуйста <a href="feedback">отправьте отзыв</a>, чтобы помочь нам улучшить приложение.</string>
|
||||||
<!--Adding Contacts Remotely-->
|
<!--Adding Contacts Remotely-->
|
||||||
<string name="add_contact_remotely_title_case">Добавление контакта на расстоянии</string>
|
<string name="add_contact_remotely_title_case">Добавление контакта на расстоянии</string>
|
||||||
@@ -199,7 +222,7 @@
|
|||||||
<string name="add_contact_choose_nickname">Выберите ник</string>
|
<string name="add_contact_choose_nickname">Выберите ник</string>
|
||||||
<string name="add_contact_choose_a_nickname">Введите ник</string>
|
<string name="add_contact_choose_a_nickname">Введите ник</string>
|
||||||
<string name="nickname_intro">Дайте вашему контакту ник. Увидеть его сможете только вы.</string>
|
<string name="nickname_intro">Дайте вашему контакту ник. Увидеть его сможете только вы.</string>
|
||||||
<string name="your_link">Передайте эту ссылку контакту, которого вы хотите добавить.</string>
|
<string name="your_link">Передайте эту ссылку контакту, который вы хотите добавить.</string>
|
||||||
<string name="link_clip_label">Ссылка Briar</string>
|
<string name="link_clip_label">Ссылка Briar</string>
|
||||||
<string name="link_copied_toast">Ссылка скопирована</string>
|
<string name="link_copied_toast">Ссылка скопирована</string>
|
||||||
<string name="adding_contact_error">При добавлении контакта произошла ошибка.</string>
|
<string name="adding_contact_error">При добавлении контакта произошла ошибка.</string>
|
||||||
@@ -558,6 +581,7 @@
|
|||||||
<string name="lock_is_locked">Briar заблокирован</string>
|
<string name="lock_is_locked">Briar заблокирован</string>
|
||||||
<string name="lock_tap_to_unlock">Нажмите для разблокировки</string>
|
<string name="lock_tap_to_unlock">Нажмите для разблокировки</string>
|
||||||
<!--Connections Screen-->
|
<!--Connections Screen-->
|
||||||
|
<string name="transports_help_text">Briar может подключаться к контактам через интернет, Wi-Fi или Bluetooth.\n\nВсе интернет-соединения проходят через сеть Tor для обеспечения конфиденциальности.\n\nЕсли с контактом можно связаться несколькими способами, Briar использует их параллельно.</string>
|
||||||
<!--Screenshots-->
|
<!--Screenshots-->
|
||||||
<!--This is a name to be used in screenshots. Feel free to change it to a local name.-->
|
<!--This is a name to be used in screenshots. Feel free to change it to a local name.-->
|
||||||
<string name="screenshot_alice">Бузова</string>
|
<string name="screenshot_alice">Бузова</string>
|
||||||
|
|||||||
@@ -195,7 +195,6 @@
|
|||||||
<string name="connecting_to_device">Po lidhet me pajisjen\u2026</string>
|
<string name="connecting_to_device">Po lidhet me pajisjen\u2026</string>
|
||||||
<string name="authenticating_with_device">Po bëhet mirëfilltësimi me pajisjen\u2026</string>
|
<string name="authenticating_with_device">Po bëhet mirëfilltësimi me pajisjen\u2026</string>
|
||||||
<string name="connection_error_title">S’u lidh dot te kontakti juaj</string>
|
<string name="connection_error_title">S’u lidh dot te kontakti juaj</string>
|
||||||
<string name="connection_error_explanation">Ju lutemi, kontrolloni se jeni të dy të lidhur në të njëjtin rrjet Wi-Fi.</string>
|
|
||||||
<string name="connection_error_feedback">Nëse problemi vazhdon, ju lutemi, <a href="feedback">na njoftoni</a>, që të mund të përmirësojmë aplikacionin.</string>
|
<string name="connection_error_feedback">Nëse problemi vazhdon, ju lutemi, <a href="feedback">na njoftoni</a>, që të mund të përmirësojmë aplikacionin.</string>
|
||||||
<!--Adding Contacts Remotely-->
|
<!--Adding Contacts Remotely-->
|
||||||
<string name="add_contact_remotely_title_case">Shtoni Kontakt në Largësi</string>
|
<string name="add_contact_remotely_title_case">Shtoni Kontakt në Largësi</string>
|
||||||
@@ -550,6 +549,7 @@
|
|||||||
<string name="permission_camera_location_title">Kamera dhe vend</string>
|
<string name="permission_camera_location_title">Kamera dhe vend</string>
|
||||||
<string name="permission_camera_location_request_body">Që të skanojë kodin QR, Briar-it i duhet të përdorë kamerën.\n\nQë të pikasë pajisje Bluetooth, Briar-i lyp leje të njohë vendin tuaj.\n\nBriar-i nuk e depoziton vendin tuaj, as e ndan me dikë.</string>
|
<string name="permission_camera_location_request_body">Që të skanojë kodin QR, Briar-it i duhet të përdorë kamerën.\n\nQë të pikasë pajisje Bluetooth, Briar-i lyp leje të njohë vendin tuaj.\n\nBriar-i nuk e depoziton vendin tuaj, as e ndan me dikë.</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_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_location_denied_body">Keni mohuar hyrje te vendndodhja juaj, por Briar-i ka nevojë për këtë leje, që të mund të zbulojë pajisje Bluetooth.\n\nJu lutemi, shihni mundësinë e akordimit të hyrjes.</string>
|
||||||
<string name="qr_code">Kod QR</string>
|
<string name="qr_code">Kod QR</string>
|
||||||
<string name="show_qr_code_fullscreen">Shfaqe kodin QR sa tërë ekrani</string>
|
<string name="show_qr_code_fullscreen">Shfaqe kodin QR sa tërë ekrani</string>
|
||||||
<!--App Locking-->
|
<!--App Locking-->
|
||||||
|
|||||||
@@ -61,12 +61,36 @@
|
|||||||
<string name="lock_button">Lås appen</string>
|
<string name="lock_button">Lås appen</string>
|
||||||
<string name="settings_button">Inställningar</string>
|
<string name="settings_button">Inställningar</string>
|
||||||
<string name="sign_out_button">Logga ut</string>
|
<string name="sign_out_button">Logga ut</string>
|
||||||
|
<string name="transports_onboarding_text">Klicka här för att styra hur Briar ansluter till dina kontakter.</string>
|
||||||
<!--Transports: Tor-->
|
<!--Transports: Tor-->
|
||||||
<string name="transport_tor">Internet</string>
|
<string name="transport_tor">Internet</string>
|
||||||
|
<string name="tor_device_status_online_wifi">Din telefon har internetåtkomst via Wi-Fi</string>
|
||||||
|
<string name="tor_device_status_online_mobile">Din telefon har internetåtkomst via mobildata</string>
|
||||||
|
<string name="tor_device_status_offline">Din telefon har inte internetåtkomst</string>
|
||||||
|
<string name="tor_plugin_status_enabling">Briar ansluter till internet</string>
|
||||||
|
<string name="tor_plugin_status_active">Briar är anslutet till internet</string>
|
||||||
|
<string name="tor_plugin_status_inactive">Briar kan inte ansluta till internet</string>
|
||||||
|
<string name="tor_plugin_status_disabled">Briar är inte konfigurerat för att använda internet</string>
|
||||||
|
<string name="tor_plugin_status_disabled_mobile_data">Briar är konfigurerat så att de inte använder mobildata</string>
|
||||||
|
<string name="tor_plugin_status_disabled_battery">Briar är konfigurerat att inte använda internet vid batterianvändning</string>
|
||||||
|
<string name="tor_plugin_status_disabled_country_blocked">Briar är konfigurerat att inte använda internet i detta landet</string>
|
||||||
<!--Transports: Wi-Fi-->
|
<!--Transports: Wi-Fi-->
|
||||||
<string name="transport_lan">Wi-Fi</string>
|
<string name="transport_lan">Wi-Fi</string>
|
||||||
|
<string name="transport_lan_long">Samma Wi-Fi-nätverk</string>
|
||||||
|
<string name="lan_device_status_on">Din telefon är ansluten till Wi-Fi</string>
|
||||||
|
<string name="lan_device_status_off">Din telefon är inte ansluten till Wi-Fi</string>
|
||||||
|
<string name="lan_plugin_status_enabling">Briar ansluter till Wi-Fi-nätverket</string>
|
||||||
|
<string name="lan_plugin_status_active">Briar är anslutet till Wi-Fi nätverket</string>
|
||||||
|
<string name="lan_plugin_status_inactive">Briar kan inte ansluta till Wi-Fi-nätverket</string>
|
||||||
|
<string name="lan_plugin_status_disabled">Briar är konfigurerat för att inte använda Wi-Fi-nätverket</string>
|
||||||
<!--Transports: Bluetooth-->
|
<!--Transports: Bluetooth-->
|
||||||
<string name="transport_bt">Bluetooth</string>
|
<string name="transport_bt">Bluetooth</string>
|
||||||
|
<string name="bt_device_status_on">Din telefons Bluetooth är aktiverad</string>
|
||||||
|
<string name="bt_device_status_off">Din telefons Bluetooth är inaktiverad</string>
|
||||||
|
<string name="bt_plugin_status_enabling">Briar ansluter till Bluetooth</string>
|
||||||
|
<string name="bt_plugin_status_active">Briar är ansluten till Bluetooth</string>
|
||||||
|
<string name="bt_plugin_status_inactive">Briar kan inte ansluta till Bluetooth</string>
|
||||||
|
<string name="bt_plugin_status_disabled">Briar är konfigurerat för att inte använda Bluetooth</string>
|
||||||
<!--Notifications-->
|
<!--Notifications-->
|
||||||
<string name="reminder_notification_title">Utloggad från Briar</string>
|
<string name="reminder_notification_title">Utloggad från Briar</string>
|
||||||
<string name="reminder_notification_text">Tryck för att logga in igen.</string>
|
<string name="reminder_notification_text">Tryck för att logga in igen.</string>
|
||||||
@@ -171,7 +195,6 @@
|
|||||||
<string name="connecting_to_device">Ansluter till enhet\u2026</string>
|
<string name="connecting_to_device">Ansluter till enhet\u2026</string>
|
||||||
<string name="authenticating_with_device">Autentiserar med enhet\u2026</string>
|
<string name="authenticating_with_device">Autentiserar med enhet\u2026</string>
|
||||||
<string name="connection_error_title">Kunde ej ansluta till din kontakt</string>
|
<string name="connection_error_title">Kunde ej ansluta till din kontakt</string>
|
||||||
<string name="connection_error_explanation">Kontrollera att ni båda är anslutna på samma Wi-Fi-nätverk.</string>
|
|
||||||
<string name="connection_error_feedback">Om det här problemet kvarstår, vänligen lämna <a href="feedback">synpunkter</a> så vi kan förbättra appen.</string>
|
<string name="connection_error_feedback">Om det här problemet kvarstår, vänligen lämna <a href="feedback">synpunkter</a> så vi kan förbättra appen.</string>
|
||||||
<!--Adding Contacts Remotely-->
|
<!--Adding Contacts Remotely-->
|
||||||
<string name="add_contact_remotely_title_case">Lägg till en kontakt på avstånd</string>
|
<string name="add_contact_remotely_title_case">Lägg till en kontakt på avstånd</string>
|
||||||
@@ -412,10 +435,20 @@
|
|||||||
<string name="pref_theme_auto">Automatisk (dagtid)</string>
|
<string name="pref_theme_auto">Automatisk (dagtid)</string>
|
||||||
<string name="pref_theme_system">Systemets förval</string>
|
<string name="pref_theme_system">Systemets förval</string>
|
||||||
<!--Settings Connections-->
|
<!--Settings Connections-->
|
||||||
|
<string name="network_settings_title">Anslutningar</string>
|
||||||
|
<string name="bluetooth_setting">Anslut till kontakter via Bluetooth</string>
|
||||||
|
<string name="wifi_setting">Anslut till kontakter på samma Wi-Fi-nätverk</string>
|
||||||
|
<string name="tor_enable_title">Anslut till kontakter via internet</string>
|
||||||
|
<string name="tor_enable_summary">Alla anslutningar gå via Tor-nätverket av integritetsskäl</string>
|
||||||
|
<string name="tor_network_setting">Anslutningsmetod för Tor-nätverket</string>
|
||||||
<string name="tor_network_setting_automatic">Automatisk, baserad på position</string>
|
<string name="tor_network_setting_automatic">Automatisk, baserad på position</string>
|
||||||
|
<string name="tor_network_setting_without_bridges">Använd Tor-nätverket utan bryggor</string>
|
||||||
|
<string name="tor_network_setting_with_bridges">Anvädn Tor-nätverket med bryggor</string>
|
||||||
|
<string name="tor_network_setting_never">Anslut inte till internet</string>
|
||||||
<!--How and when Briar will connect to Tor: E.g. "Don't connect to the Internet (in China)" or "Use Tor network with bridges (in Belarus)"-->
|
<!--How and when Briar will connect to Tor: E.g. "Don't connect to the Internet (in China)" or "Use Tor network with bridges (in Belarus)"-->
|
||||||
<string name="tor_network_setting_summary">Automatisk: %1$s (i %2$s)</string>
|
<string name="tor_network_setting_summary">Automatisk: %1$s (i %2$s)</string>
|
||||||
<string name="tor_mobile_data_title">Använd mobildata</string>
|
<string name="tor_mobile_data_title">Använd mobildata</string>
|
||||||
|
<string name="tor_only_when_charging_title">Anslut till internet endast vid laddning</string>
|
||||||
<string name="tor_only_when_charging_summary">Avaktiverar anslutning över Internet när enheten går på batteri</string>
|
<string name="tor_only_when_charging_summary">Avaktiverar anslutning över Internet när enheten går på batteri</string>
|
||||||
<!--Settings Security and Panic-->
|
<!--Settings Security and Panic-->
|
||||||
<string name="security_settings_title">Säkerhet</string>
|
<string name="security_settings_title">Säkerhet</string>
|
||||||
@@ -526,6 +559,7 @@
|
|||||||
<string name="lock_is_locked">Briar är låst</string>
|
<string name="lock_is_locked">Briar är låst</string>
|
||||||
<string name="lock_tap_to_unlock">Tryck för att låsa upp</string>
|
<string name="lock_tap_to_unlock">Tryck för att låsa upp</string>
|
||||||
<!--Connections Screen-->
|
<!--Connections Screen-->
|
||||||
|
<string name="transports_help_text">Briar kan ansluta till dina kontakter via internet, Wi-Fi eller Bluetooth.\n\nAlla internetanslutningar går via Tor-nätverket av integritetsskäl.\n\nOm en kontakt kan nås via flera metoder kommer Briar att använda dem parallellt.</string>
|
||||||
<!--Screenshots-->
|
<!--Screenshots-->
|
||||||
<!--This is a name to be used in screenshots. Feel free to change it to a local name.-->
|
<!--This is a name to be used in screenshots. Feel free to change it to a local name.-->
|
||||||
<string name="screenshot_alice">Alice</string>
|
<string name="screenshot_alice">Alice</string>
|
||||||
|
|||||||
@@ -187,7 +187,6 @@ Umepoteza nenosiri.</string>
|
|||||||
<string name="connecting_to_device">Unganisha kwenye kifaa\u2026</string>
|
<string name="connecting_to_device">Unganisha kwenye kifaa\u2026</string>
|
||||||
<string name="authenticating_with_device">Uhalisishaji wa kifaa\u2026</string>
|
<string name="authenticating_with_device">Uhalisishaji wa kifaa\u2026</string>
|
||||||
<string name="connection_error_title">Imeshindwa unganishwa kwenye mawasiliano </string>
|
<string name="connection_error_title">Imeshindwa unganishwa kwenye mawasiliano </string>
|
||||||
<string name="connection_error_explanation">Tafathali Angalia kama wote Mmeunganishwa kwenye Mtandao wa WI-FI mmoja .</string>
|
|
||||||
<string name="connection_error_feedback">Kama hilo tatizo linaendela , Tafathali <a href="feedback"> tutumie maoni</a>ilituboreshe Programu .</string>
|
<string name="connection_error_feedback">Kama hilo tatizo linaendela , Tafathali <a href="feedback"> tutumie maoni</a>ilituboreshe Programu .</string>
|
||||||
<!--Adding Contacts Remotely-->
|
<!--Adding Contacts Remotely-->
|
||||||
<string name="add_contact_remotely_title_case">Ongeza mawasiliano kwambali </string>
|
<string name="add_contact_remotely_title_case">Ongeza mawasiliano kwambali </string>
|
||||||
|
|||||||
@@ -61,12 +61,36 @@
|
|||||||
<string name="lock_button">Uygulamayı Kilitle</string>
|
<string name="lock_button">Uygulamayı Kilitle</string>
|
||||||
<string name="settings_button">Ayarlar</string>
|
<string name="settings_button">Ayarlar</string>
|
||||||
<string name="sign_out_button">Oturumu Kapat</string>
|
<string name="sign_out_button">Oturumu Kapat</string>
|
||||||
|
<string name="transports_onboarding_text">Briar\'ın kişilerinizle nasıl bağlanacağını kontrol etmek için buraya tıklayın</string>
|
||||||
<!--Transports: Tor-->
|
<!--Transports: Tor-->
|
||||||
<string name="transport_tor">İnternet</string>
|
<string name="transport_tor">İnternet</string>
|
||||||
|
<string name="tor_device_status_online_wifi">Telefonunuzun Wi-Fi aracılığıyla İnternet erişimi var</string>
|
||||||
|
<string name="tor_device_status_online_mobile">Telefonunuzun mobil veri aracılığıyla İnternet erişimi var</string>
|
||||||
|
<string name="tor_device_status_offline">Telefonunuzun İnternet erişimi yok</string>
|
||||||
|
<string name="tor_plugin_status_enabling">Briar İnternet\'e bağlanıyor</string>
|
||||||
|
<string name="tor_plugin_status_active">Briar İnternet\'e bağlandı</string>
|
||||||
|
<string name="tor_plugin_status_inactive">Briar İnternet\'e bağlanamıyor</string>
|
||||||
|
<string name="tor_plugin_status_disabled">Briar İnternet kullanmamak üzere yapılandırılmış</string>
|
||||||
|
<string name="tor_plugin_status_disabled_mobile_data">Briar mobil veri kullanmamak üzere yapılandırılmış</string>
|
||||||
|
<string name="tor_plugin_status_disabled_battery">Briar pille çalışırken İnternet kullanmamak üzere yapılandırılmış</string>
|
||||||
|
<string name="tor_plugin_status_disabled_country_blocked">Briar bu ülkede İnternet kullanmamak üzere yapılandırılmış</string>
|
||||||
<!--Transports: Wi-Fi-->
|
<!--Transports: Wi-Fi-->
|
||||||
<string name="transport_lan">Wi-Fi</string>
|
<string name="transport_lan">Wi-Fi</string>
|
||||||
|
<string name="transport_lan_long">Aynı Wi-Fi Ağı</string>
|
||||||
|
<string name="lan_device_status_on">Telefonunuz Wi-Fi\'ye bağlı</string>
|
||||||
|
<string name="lan_device_status_off">Telefonunuz Wi-Fi\'ye bağlı değil</string>
|
||||||
|
<string name="lan_plugin_status_enabling">Briar Wi-Fi ağına bağlanıyor</string>
|
||||||
|
<string name="lan_plugin_status_active">Briar Wi-Fi ağına bağlandı</string>
|
||||||
|
<string name="lan_plugin_status_inactive">Briar Wi-Fi ağına bağlanamıyor</string>
|
||||||
|
<string name="lan_plugin_status_disabled">Briar Wi-Fi ağını kullanmamak üzere yapılandırılmış</string>
|
||||||
<!--Transports: Bluetooth-->
|
<!--Transports: Bluetooth-->
|
||||||
<string name="transport_bt">Bluetooth</string>
|
<string name="transport_bt">Bluetooth</string>
|
||||||
|
<string name="bt_device_status_on">Telefonunuzun Bluetooth\'u açık</string>
|
||||||
|
<string name="bt_device_status_off">Telefonunuzun Bluetooth\'u kapalı</string>
|
||||||
|
<string name="bt_plugin_status_enabling">Briar Bluetooth\'a bağlanıyor</string>
|
||||||
|
<string name="bt_plugin_status_active">Briar Bluetooth\'a bağlandı</string>
|
||||||
|
<string name="bt_plugin_status_inactive">Briar Bluetooth\'a bağlanamıyor</string>
|
||||||
|
<string name="bt_plugin_status_disabled">Briar Bluetooth kullanmamak üzere yapılandırılmış</string>
|
||||||
<!--Notifications-->
|
<!--Notifications-->
|
||||||
<string name="reminder_notification_title">Briar oturumu kapatıldı</string>
|
<string name="reminder_notification_title">Briar oturumu kapatıldı</string>
|
||||||
<string name="reminder_notification_text">Tekrar oturum açmak için dokunun</string>
|
<string name="reminder_notification_text">Tekrar oturum açmak için dokunun</string>
|
||||||
@@ -171,7 +195,6 @@
|
|||||||
<string name="connecting_to_device">Aygıta bağlanıyor\u2026</string>
|
<string name="connecting_to_device">Aygıta bağlanıyor\u2026</string>
|
||||||
<string name="authenticating_with_device">Aygıtla kimlik doğrulama\u2026</string>
|
<string name="authenticating_with_device">Aygıtla kimlik doğrulama\u2026</string>
|
||||||
<string name="connection_error_title">Kişinizle bağlantı kurulamadı</string>
|
<string name="connection_error_title">Kişinizle bağlantı kurulamadı</string>
|
||||||
<string name="connection_error_explanation">Lütfen her ikinizin de aynı Wi-Fi ağına bağlı olup olmadığınızı kontrol edin.</string>
|
|
||||||
<string name="connection_error_feedback">Sorun devam ederse, uygulamayı geliştirmemize yardımcı olmak için <a href="feedback">geri bildirim gönderin</a>.</string>
|
<string name="connection_error_feedback">Sorun devam ederse, uygulamayı geliştirmemize yardımcı olmak için <a href="feedback">geri bildirim gönderin</a>.</string>
|
||||||
<!--Adding Contacts Remotely-->
|
<!--Adding Contacts Remotely-->
|
||||||
<string name="add_contact_remotely_title_case">Uzaktaki Kişiyi Ekle</string>
|
<string name="add_contact_remotely_title_case">Uzaktaki Kişiyi Ekle</string>
|
||||||
@@ -536,6 +559,7 @@
|
|||||||
<string name="lock_is_locked">Briar kilitli</string>
|
<string name="lock_is_locked">Briar kilitli</string>
|
||||||
<string name="lock_tap_to_unlock">Kilidi açmak için dokunun</string>
|
<string name="lock_tap_to_unlock">Kilidi açmak için dokunun</string>
|
||||||
<!--Connections Screen-->
|
<!--Connections Screen-->
|
||||||
|
<string name="transports_help_text">Briar kişilerinizle İnternet, Wi-Fi veya Bluetooth ile bağlanabilir.\n\nBütün İnternet bağlantıları gizliliğiniz için Tor Ağı üzerinden yapılıyor.\n\nEğer bir kişiniz birçok yöntemle erişilebiliyorsa, Briar bu yöntemleri paralel olarak kullanır.</string>
|
||||||
<!--Screenshots-->
|
<!--Screenshots-->
|
||||||
<!--This is a name to be used in screenshots. Feel free to change it to a local name.-->
|
<!--This is a name to be used in screenshots. Feel free to change it to a local name.-->
|
||||||
<string name="screenshot_alice">Alice</string>
|
<string name="screenshot_alice">Alice</string>
|
||||||
|
|||||||
@@ -165,7 +165,6 @@
|
|||||||
<string name="connecting_to_device">З\'єднання з пристроєм\u2026</string>
|
<string name="connecting_to_device">З\'єднання з пристроєм\u2026</string>
|
||||||
<string name="authenticating_with_device">Автентифікація з пристроєм\u2026</string>
|
<string name="authenticating_with_device">Автентифікація з пристроєм\u2026</string>
|
||||||
<string name="connection_error_title">Неможливо з\'єднатися з вашим контактом</string>
|
<string name="connection_error_title">Неможливо з\'єднатися з вашим контактом</string>
|
||||||
<string name="connection_error_explanation">Будь ласка, впевніться, що ви обоє під\'єднані до однієї Wi-Fi-мережі.</string>
|
|
||||||
<string name="connection_error_feedback">Якщо ця проблема збережеться, будь ласка, <a href="feedback">надішліть відгук</a>, щоб допомогти нам вдосконалити цей додаток.</string>
|
<string name="connection_error_feedback">Якщо ця проблема збережеться, будь ласка, <a href="feedback">надішліть відгук</a>, щоб допомогти нам вдосконалити цей додаток.</string>
|
||||||
<!--Adding Contacts Remotely-->
|
<!--Adding Contacts Remotely-->
|
||||||
<string name="add_contact_remotely_title_case">Додати контакт віддалено</string>
|
<string name="add_contact_remotely_title_case">Додати контакт віддалено</string>
|
||||||
|
|||||||
@@ -60,12 +60,36 @@
|
|||||||
<string name="lock_button">锁定应用</string>
|
<string name="lock_button">锁定应用</string>
|
||||||
<string name="settings_button">设置</string>
|
<string name="settings_button">设置</string>
|
||||||
<string name="sign_out_button">登出</string>
|
<string name="sign_out_button">登出</string>
|
||||||
|
<string name="transports_onboarding_text">点击这里来控制Briar如何连接到您的联系人</string>
|
||||||
<!--Transports: Tor-->
|
<!--Transports: Tor-->
|
||||||
<string name="transport_tor">互联网</string>
|
<string name="transport_tor">互联网</string>
|
||||||
|
<string name="tor_device_status_online_wifi">你的手机通过Wi-Fi上网</string>
|
||||||
|
<string name="tor_device_status_online_mobile">您的手机通过移动数据访问互联网</string>
|
||||||
|
<string name="tor_device_status_offline">你的手机不能上网</string>
|
||||||
|
<string name="tor_plugin_status_enabling">Briar正在连接互联网</string>
|
||||||
|
<string name="tor_plugin_status_active">Briar 已联网</string>
|
||||||
|
<string name="tor_plugin_status_inactive">Briar 无法联网</string>
|
||||||
|
<string name="tor_plugin_status_disabled">Briar 配置为不使用互联网</string>
|
||||||
|
<string name="tor_plugin_status_disabled_mobile_data">Briar 配置为不使用移动数据</string>
|
||||||
|
<string name="tor_plugin_status_disabled_battery">Briar配置为在用电池运行时不使用互联网</string>
|
||||||
|
<string name="tor_plugin_status_disabled_country_blocked">Briar配置为在这个国家不使用互联网</string>
|
||||||
<!--Transports: Wi-Fi-->
|
<!--Transports: Wi-Fi-->
|
||||||
<string name="transport_lan">无线局域网</string>
|
<string name="transport_lan">无线局域网</string>
|
||||||
|
<string name="transport_lan_long">同一 Wi-Fi 网络</string>
|
||||||
|
<string name="lan_device_status_on">你的手机连接到Wi-Fi</string>
|
||||||
|
<string name="lan_device_status_off">你的手机未连接到Wi-Fi</string>
|
||||||
|
<string name="lan_plugin_status_enabling">Briar正在连接Wi-Fi网络</string>
|
||||||
|
<string name="lan_plugin_status_active">Briar已连接到此Wi-Fi网络</string>
|
||||||
|
<string name="lan_plugin_status_inactive">Briar不能连接到此Wi-Fi网络</string>
|
||||||
|
<string name="lan_plugin_status_disabled">Briar 配置为不使用Wi-Fi 网络</string>
|
||||||
<!--Transports: Bluetooth-->
|
<!--Transports: Bluetooth-->
|
||||||
<string name="transport_bt">蓝牙</string>
|
<string name="transport_bt">蓝牙</string>
|
||||||
|
<string name="bt_device_status_on">你手机的蓝牙已打开</string>
|
||||||
|
<string name="bt_device_status_off">你手机额蓝牙已关闭</string>
|
||||||
|
<string name="bt_plugin_status_enabling">Briar 正连接蓝牙</string>
|
||||||
|
<string name="bt_plugin_status_active">Briar 已连接蓝牙</string>
|
||||||
|
<string name="bt_plugin_status_inactive">Briar 不能连接蓝牙</string>
|
||||||
|
<string name="bt_plugin_status_disabled">Briar 配置为不使用蓝牙</string>
|
||||||
<!--Notifications-->
|
<!--Notifications-->
|
||||||
<string name="reminder_notification_title">已登出 Briar</string>
|
<string name="reminder_notification_title">已登出 Briar</string>
|
||||||
<string name="reminder_notification_text">轻按以重新登录。</string>
|
<string name="reminder_notification_text">轻按以重新登录。</string>
|
||||||
@@ -108,6 +132,7 @@
|
|||||||
<string name="help">帮助</string>
|
<string name="help">帮助</string>
|
||||||
<string name="sorry">抱歉</string>
|
<string name="sorry">抱歉</string>
|
||||||
<string name="error_start_activity">在您的系统上不可用</string>
|
<string name="error_start_activity">在您的系统上不可用</string>
|
||||||
|
<string name="status_heading">状态:</string>
|
||||||
<!--Contacts and Private Conversations-->
|
<!--Contacts and Private Conversations-->
|
||||||
<string name="no_contacts">尚无联系人可供显示</string>
|
<string name="no_contacts">尚无联系人可供显示</string>
|
||||||
<string name="no_contacts_action">轻按 + 号即可添加联系人</string>
|
<string name="no_contacts_action">轻按 + 号即可添加联系人</string>
|
||||||
@@ -165,7 +190,6 @@
|
|||||||
<string name="connecting_to_device">正在连接至设备\u2026</string>
|
<string name="connecting_to_device">正在连接至设备\u2026</string>
|
||||||
<string name="authenticating_with_device">正在验证设备\u2026</string>
|
<string name="authenticating_with_device">正在验证设备\u2026</string>
|
||||||
<string name="connection_error_title">无法连接到您的联系人</string>
|
<string name="connection_error_title">无法连接到您的联系人</string>
|
||||||
<string name="connection_error_explanation">请确保你们连接到了同一个无线局域网中。</string>
|
|
||||||
<string name="connection_error_feedback">如果该问题仍存在,请 <a href="feedback">发送反馈</a> 帮助我们改善应用。</string>
|
<string name="connection_error_feedback">如果该问题仍存在,请 <a href="feedback">发送反馈</a> 帮助我们改善应用。</string>
|
||||||
<!--Adding Contacts Remotely-->
|
<!--Adding Contacts Remotely-->
|
||||||
<string name="add_contact_remotely_title_case">添加远处的联系人</string>
|
<string name="add_contact_remotely_title_case">添加远处的联系人</string>
|
||||||
@@ -414,6 +438,7 @@
|
|||||||
<!--How and when Briar will connect to Tor: E.g. "Don't connect to the Internet (in China)" or "Use Tor network with bridges (in Belarus)"-->
|
<!--How and when Briar will connect to Tor: E.g. "Don't connect to the Internet (in China)" or "Use Tor network with bridges (in Belarus)"-->
|
||||||
<string name="tor_network_setting_summary">自动选择:%1$s(在 %2$s)</string>
|
<string name="tor_network_setting_summary">自动选择:%1$s(在 %2$s)</string>
|
||||||
<string name="tor_mobile_data_title">使用移动数据</string>
|
<string name="tor_mobile_data_title">使用移动数据</string>
|
||||||
|
<string name="tor_only_when_charging_title">仅在充电时联网</string>
|
||||||
<string name="tor_only_when_charging_summary">当设备使用电池电量时关闭网络连接</string>
|
<string name="tor_only_when_charging_summary">当设备使用电池电量时关闭网络连接</string>
|
||||||
<!--Settings Security and Panic-->
|
<!--Settings Security and Panic-->
|
||||||
<string name="security_settings_title">安全</string>
|
<string name="security_settings_title">安全</string>
|
||||||
@@ -514,6 +539,7 @@
|
|||||||
<string name="permission_camera_location_title">相机和位置</string>
|
<string name="permission_camera_location_title">相机和位置</string>
|
||||||
<string name="permission_camera_location_request_body"> Briar 需要相机权限以扫描二维码。\n\nBriar 需要位置信息权限以发现蓝牙设备。\n\nBriar 不会存储您的位置或将它分享给任何人。</string>
|
<string name="permission_camera_location_request_body"> Briar 需要相机权限以扫描二维码。\n\nBriar 需要位置信息权限以发现蓝牙设备。\n\nBriar 不会存储您的位置或将它分享给任何人。</string>
|
||||||
<string name="permission_camera_denied_body">您已拒绝相机权限,而添加联系人需要使用相机。\n\n请考虑授予相机权限。</string>
|
<string name="permission_camera_denied_body">您已拒绝相机权限,而添加联系人需要使用相机。\n\n请考虑授予相机权限。</string>
|
||||||
|
<string name="permission_location_denied_body">您已拒绝访问您的位置,但Briar需要此权限才能发现蓝牙设备。\n\n请考虑授予访问权限。</string>
|
||||||
<string name="qr_code">二维码</string>
|
<string name="qr_code">二维码</string>
|
||||||
<string name="show_qr_code_fullscreen">全屏显示二维码</string>
|
<string name="show_qr_code_fullscreen">全屏显示二维码</string>
|
||||||
<!--App Locking-->
|
<!--App Locking-->
|
||||||
@@ -524,6 +550,7 @@
|
|||||||
<string name="lock_is_locked">Briar 已锁定</string>
|
<string name="lock_is_locked">Briar 已锁定</string>
|
||||||
<string name="lock_tap_to_unlock">轻按以解锁</string>
|
<string name="lock_tap_to_unlock">轻按以解锁</string>
|
||||||
<!--Connections Screen-->
|
<!--Connections Screen-->
|
||||||
|
<string name="transports_help_text">Briar可以通过互联网、Wi-Fi或蓝牙连接到您的联系人。\n\n为了保护隐私,所有的互联网连接都通过Tor网络。\n\n如果一个联系人可以通过多种方法联系到,Briar会并行地使用它们。</string>
|
||||||
<!--Screenshots-->
|
<!--Screenshots-->
|
||||||
<!--This is a name to be used in screenshots. Feel free to change it to a local name.-->
|
<!--This is a name to be used in screenshots. Feel free to change it to a local name.-->
|
||||||
<string name="screenshot_alice">韩梅梅</string>
|
<string name="screenshot_alice">韩梅梅</string>
|
||||||
|
|||||||
@@ -149,7 +149,6 @@
|
|||||||
<string name="connecting_to_device">正在連接至裝置\u2026</string>
|
<string name="connecting_to_device">正在連接至裝置\u2026</string>
|
||||||
<string name="authenticating_with_device">正在驗證裝置\u2026</string>
|
<string name="authenticating_with_device">正在驗證裝置\u2026</string>
|
||||||
<string name="connection_error_title">無法連接到您的聯絡人</string>
|
<string name="connection_error_title">無法連接到您的聯絡人</string>
|
||||||
<string name="connection_error_explanation">請確保您們連接到了同一個 Wi-Fi中。</string>
|
|
||||||
<string name="connection_error_feedback">如果該問題仍存在,請 <a href="feedback">發送反饋</a> 幫助我們改善程式。</string>
|
<string name="connection_error_feedback">如果該問題仍存在,請 <a href="feedback">發送反饋</a> 幫助我們改善程式。</string>
|
||||||
<!--Adding Contacts Remotely-->
|
<!--Adding Contacts Remotely-->
|
||||||
<string name="add_contact_remotely_title_case">添加遠處的聯絡人</string>
|
<string name="add_contact_remotely_title_case">添加遠處的聯絡人</string>
|
||||||
|
|||||||
@@ -210,7 +210,6 @@
|
|||||||
<string name="connecting_to_device">Connecting to device\u2026</string>
|
<string name="connecting_to_device">Connecting to device\u2026</string>
|
||||||
<string name="authenticating_with_device">Authenticating with device\u2026</string>
|
<string name="authenticating_with_device">Authenticating with device\u2026</string>
|
||||||
<string name="connection_error_title">Could not connect to your contact</string>
|
<string name="connection_error_title">Could not connect to your contact</string>
|
||||||
<string name="connection_error_explanation">Please check that you\'re both connected to the same Wi-Fi network.</string>
|
|
||||||
<string name="connection_error_feedback">If this problem persists, please <a href="feedback">send feedback</a> to help us improve the app.</string>
|
<string name="connection_error_feedback">If this problem persists, please <a href="feedback">send feedback</a> to help us improve the app.</string>
|
||||||
|
|
||||||
<!-- Adding Contacts Remotely -->
|
<!-- Adding Contacts Remotely -->
|
||||||
@@ -589,6 +588,7 @@
|
|||||||
<string name="permission_camera_location_title">Camera and location</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_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_body">You have denied access to the camera, but adding contacts requires using the camera.\n\nPlease consider granting access.</string>
|
||||||
|
<string name="permission_location_denied_body">You have denied access to your location, but Briar needs this permission to discover Bluetooth devices.\n\nPlease consider granting access.</string>
|
||||||
<string name="qr_code">QR code</string>
|
<string name="qr_code">QR code</string>
|
||||||
<string name="show_qr_code_fullscreen">Show QR code fullscreen</string>
|
<string name="show_qr_code_fullscreen">Show QR code fullscreen</string>
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package org.briarproject.briar.android.attachment;
|
|||||||
|
|
||||||
import org.briarproject.bramble.api.sync.MessageId;
|
import org.briarproject.bramble.api.sync.MessageId;
|
||||||
import org.briarproject.bramble.test.BrambleMockTestCase;
|
import org.briarproject.bramble.test.BrambleMockTestCase;
|
||||||
|
import org.briarproject.bramble.test.ImmediateExecutor;
|
||||||
import org.briarproject.briar.api.messaging.Attachment;
|
import org.briarproject.briar.api.messaging.Attachment;
|
||||||
import org.briarproject.briar.api.messaging.AttachmentHeader;
|
import org.briarproject.briar.api.messaging.AttachmentHeader;
|
||||||
import org.briarproject.briar.api.messaging.MessagingManager;
|
import org.briarproject.briar.api.messaging.MessagingManager;
|
||||||
@@ -11,12 +12,13 @@ import org.junit.Test;
|
|||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.util.concurrent.Executor;
|
||||||
|
|
||||||
import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
|
import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
|
||||||
import static org.briarproject.bramble.test.TestUtils.getRandomId;
|
import static org.briarproject.bramble.test.TestUtils.getRandomId;
|
||||||
|
import static org.briarproject.briar.android.attachment.AttachmentItem.State.AVAILABLE;
|
||||||
|
import static org.briarproject.briar.android.attachment.AttachmentItem.State.ERROR;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertFalse;
|
|
||||||
import static org.junit.Assert.assertTrue;
|
|
||||||
|
|
||||||
public class AttachmentRetrieverTest extends BrambleMockTestCase {
|
public class AttachmentRetrieverTest extends BrambleMockTestCase {
|
||||||
|
|
||||||
@@ -33,8 +35,9 @@ public class AttachmentRetrieverTest extends BrambleMockTestCase {
|
|||||||
MessagingManager messagingManager =
|
MessagingManager messagingManager =
|
||||||
context.mock(MessagingManager.class);
|
context.mock(MessagingManager.class);
|
||||||
imageSizeCalculator = context.mock(ImageSizeCalculator.class);
|
imageSizeCalculator = context.mock(ImageSizeCalculator.class);
|
||||||
retriever = new AttachmentRetrieverImpl(messagingManager, dimensions,
|
Executor dbExecutor = new ImmediateExecutor();
|
||||||
imageHelper, imageSizeCalculator);
|
retriever = new AttachmentRetrieverImpl(dbExecutor, messagingManager,
|
||||||
|
dimensions, imageHelper, imageSizeCalculator);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -47,10 +50,10 @@ public class AttachmentRetrieverTest extends BrambleMockTestCase {
|
|||||||
will(returnValue("jpg"));
|
will(returnValue("jpg"));
|
||||||
}});
|
}});
|
||||||
|
|
||||||
AttachmentItem item = retriever.getAttachmentItem(attachment, false);
|
AttachmentItem item = retriever.createAttachmentItem(attachment, false);
|
||||||
assertEquals(mimeType, item.getMimeType());
|
assertEquals(mimeType, item.getMimeType());
|
||||||
assertEquals("jpg", item.getExtension());
|
assertEquals("jpg", item.getExtension());
|
||||||
assertFalse(item.hasError());
|
assertEquals(AVAILABLE, item.getState());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -63,8 +66,8 @@ public class AttachmentRetrieverTest extends BrambleMockTestCase {
|
|||||||
will(returnValue(null));
|
will(returnValue(null));
|
||||||
}});
|
}});
|
||||||
|
|
||||||
AttachmentItem item = retriever.getAttachmentItem(attachment, false);
|
AttachmentItem item = retriever.createAttachmentItem(attachment, false);
|
||||||
assertTrue(item.hasError());
|
assertEquals(ERROR, item.getState());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -80,7 +83,7 @@ public class AttachmentRetrieverTest extends BrambleMockTestCase {
|
|||||||
will(returnValue("jpg"));
|
will(returnValue("jpg"));
|
||||||
}});
|
}});
|
||||||
|
|
||||||
AttachmentItem item = retriever.getAttachmentItem(attachment, true);
|
AttachmentItem item = retriever.createAttachmentItem(attachment, true);
|
||||||
assertEquals(msgId, item.getMessageId());
|
assertEquals(msgId, item.getMessageId());
|
||||||
assertEquals(160, item.getWidth());
|
assertEquals(160, item.getWidth());
|
||||||
assertEquals(240, item.getHeight());
|
assertEquals(240, item.getHeight());
|
||||||
@@ -88,7 +91,7 @@ public class AttachmentRetrieverTest extends BrambleMockTestCase {
|
|||||||
assertEquals(240, item.getThumbnailHeight());
|
assertEquals(240, item.getThumbnailHeight());
|
||||||
assertEquals(mimeType, item.getMimeType());
|
assertEquals(mimeType, item.getMimeType());
|
||||||
assertEquals("jpg", item.getExtension());
|
assertEquals("jpg", item.getExtension());
|
||||||
assertFalse(item.hasError());
|
assertEquals(AVAILABLE, item.getState());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -104,12 +107,12 @@ public class AttachmentRetrieverTest extends BrambleMockTestCase {
|
|||||||
will(returnValue("jpg"));
|
will(returnValue("jpg"));
|
||||||
}});
|
}});
|
||||||
|
|
||||||
AttachmentItem item = retriever.getAttachmentItem(attachment, true);
|
AttachmentItem item = retriever.createAttachmentItem(attachment, true);
|
||||||
assertEquals(1728, item.getWidth());
|
assertEquals(1728, item.getWidth());
|
||||||
assertEquals(2592, item.getHeight());
|
assertEquals(2592, item.getHeight());
|
||||||
assertEquals(dimensions.maxWidth, item.getThumbnailWidth());
|
assertEquals(dimensions.maxWidth, item.getThumbnailWidth());
|
||||||
assertEquals(dimensions.maxHeight, item.getThumbnailHeight());
|
assertEquals(dimensions.maxHeight, item.getThumbnailHeight());
|
||||||
assertFalse(item.hasError());
|
assertEquals(AVAILABLE, item.getState());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -125,8 +128,8 @@ public class AttachmentRetrieverTest extends BrambleMockTestCase {
|
|||||||
will(returnValue(null));
|
will(returnValue(null));
|
||||||
}});
|
}});
|
||||||
|
|
||||||
AttachmentItem item = retriever.getAttachmentItem(attachment, true);
|
AttachmentItem item = retriever.createAttachmentItem(attachment, true);
|
||||||
assertTrue(item.hasError());
|
assertEquals(ERROR, item.getState());
|
||||||
}
|
}
|
||||||
|
|
||||||
private Attachment getAttachment(String contentType) {
|
private Attachment getAttachment(String contentType) {
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ public interface MessagingManager extends ConversationClient {
|
|||||||
String getMessageText(MessageId m) throws DbException;
|
String getMessageText(MessageId m) throws DbException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the attachment with the given message ID and content type.
|
* Returns the attachment with the given attachment header.
|
||||||
*
|
*
|
||||||
* @throws InvalidAttachmentException If the header refers to a message
|
* @throws InvalidAttachmentException If the header refers to a message
|
||||||
* that is not an attachment, or to an attachment that does not have the
|
* that is not an attachment, or to an attachment that does not have the
|
||||||
|
|||||||
@@ -69,7 +69,8 @@ Returns a JSON array of contacts:
|
|||||||
"handshakePublicKey": "XnYRd7a7E4CTqgAvh4hCxh/YZ0EPscxknB9ZcEOpSzY=",
|
"handshakePublicKey": "XnYRd7a7E4CTqgAvh4hCxh/YZ0EPscxknB9ZcEOpSzY=",
|
||||||
"verified": true,
|
"verified": true,
|
||||||
"lastChatActivity": 1557838312175,
|
"lastChatActivity": 1557838312175,
|
||||||
"connected": false
|
"connected": false,
|
||||||
|
"unreadCount": 7
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -182,6 +183,18 @@ Note that it's also possible to add contacts nearby via Bluetooth/Wifi or
|
|||||||
introductions. In these cases contacts omit the `pendingContact` state and
|
introductions. In these cases contacts omit the `pendingContact` state and
|
||||||
directly become `contact`s.
|
directly become `contact`s.
|
||||||
|
|
||||||
|
### Changing alias of a contact
|
||||||
|
|
||||||
|
`PUT /v1/contacts/{contactId}/alias`
|
||||||
|
|
||||||
|
The alias should be posted as a JSON object:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"alias": "A nickname for the new contact"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
### Removing a contact
|
### Removing a contact
|
||||||
|
|
||||||
`DELETE /v1/contacts/{contactId}`
|
`DELETE /v1/contacts/{contactId}`
|
||||||
@@ -233,6 +246,25 @@ The text of the message should be posted as JSON:
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Marking private messages as read
|
||||||
|
|
||||||
|
`POST /v1/messages/{contactId}/read`
|
||||||
|
|
||||||
|
The `messageId` of the message to be marked as read
|
||||||
|
needs to be provided in the request body as follows:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"messageId": "+AIMMgOCPFF8HDEhiEHYjbfKrg7v0G94inKxjvjYzA8="
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Deleting all private messages
|
||||||
|
|
||||||
|
`DELETE /v1/messages/{contactId}/all`
|
||||||
|
|
||||||
|
It returns with a status code `200`, if removal was successful.
|
||||||
|
|
||||||
### Listing blog posts
|
### Listing blog posts
|
||||||
|
|
||||||
`GET /v1/blogs/posts`
|
`GET /v1/blogs/posts`
|
||||||
@@ -409,3 +441,39 @@ When the last connection is lost (the contact goes offline), it sends a `Contact
|
|||||||
"type": "event"
|
"type": "event"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### A message was sent
|
||||||
|
|
||||||
|
When Briar sent a message to a contact, it sends a `MessagesSentEvent`. This is indicated in Briar
|
||||||
|
by showing one tick next to the message.
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"contactId": 1,
|
||||||
|
"messageIds": [
|
||||||
|
"+AIMMgOCPFF8HDEhiEHYjbfKrg7v0G94inKxjvjYzA8="
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"name": "MessagesSentEvent",
|
||||||
|
"type": "event"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### A message was acknowledged
|
||||||
|
|
||||||
|
When a contact acknowledges that they received a message, Briar sends a `MessagesAckedEvent`.
|
||||||
|
This is indicated in Briar by showing two ticks next to the message.
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"contactId": 1,
|
||||||
|
"messageIds": [
|
||||||
|
"+AIMMgOCPFF8HDEhiEHYjbfKrg7v0G94inKxjvjYzA8="
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"name": "MessagesAckedEvent",
|
||||||
|
"type": "event"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|||||||
@@ -81,11 +81,20 @@ constructor(
|
|||||||
path("/:contactId") {
|
path("/:contactId") {
|
||||||
delete { ctx -> contactController.delete(ctx) }
|
delete { ctx -> contactController.delete(ctx) }
|
||||||
}
|
}
|
||||||
|
path("/:contactId/alias") {
|
||||||
|
put { ctx -> contactController.setContactAlias(ctx) }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
path("/messages/:contactId") {
|
path("/messages/:contactId") {
|
||||||
get { ctx -> messagingController.list(ctx) }
|
get { ctx -> messagingController.list(ctx) }
|
||||||
post { ctx -> messagingController.write(ctx) }
|
post { ctx -> messagingController.write(ctx) }
|
||||||
}
|
}
|
||||||
|
path("/messages/:contactId/read") {
|
||||||
|
post { ctx -> messagingController.markMessageRead(ctx) }
|
||||||
|
}
|
||||||
|
path("/messages/:contactId/all") {
|
||||||
|
delete { ctx -> messagingController.deleteAllMessages(ctx) }
|
||||||
|
}
|
||||||
path("/forums") {
|
path("/forums") {
|
||||||
get { ctx -> forumController.list(ctx) }
|
get { ctx -> forumController.list(ctx) }
|
||||||
post { ctx -> forumController.create(ctx) }
|
post { ctx -> forumController.create(ctx) }
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ interface ContactController {
|
|||||||
fun addPendingContact(ctx: Context): Context
|
fun addPendingContact(ctx: Context): Context
|
||||||
fun listPendingContacts(ctx: Context): Context
|
fun listPendingContacts(ctx: Context): Context
|
||||||
fun removePendingContact(ctx: Context): Context
|
fun removePendingContact(ctx: Context): Context
|
||||||
|
fun setContactAlias(ctx: Context): Context
|
||||||
fun delete(ctx: Context): Context
|
fun delete(ctx: Context): Context
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -77,7 +77,8 @@ constructor(
|
|||||||
val contacts = contactManager.contacts.map { contact ->
|
val contacts = contactManager.contacts.map { contact ->
|
||||||
val latestMsgTime = conversationManager.getGroupCount(contact.id).latestMsgTime
|
val latestMsgTime = conversationManager.getGroupCount(contact.id).latestMsgTime
|
||||||
val connected = connectionRegistry.isConnected(contact.id)
|
val connected = connectionRegistry.isConnected(contact.id)
|
||||||
contact.output(latestMsgTime, connected)
|
val unreadCount = conversationManager.getGroupCount(contact.id).unreadCount
|
||||||
|
contact.output(latestMsgTime, connected, unreadCount)
|
||||||
}
|
}
|
||||||
return ctx.json(contacts)
|
return ctx.json(contacts)
|
||||||
}
|
}
|
||||||
@@ -91,9 +92,7 @@ constructor(
|
|||||||
val link = ctx.getFromJson(objectMapper, "link")
|
val link = ctx.getFromJson(objectMapper, "link")
|
||||||
val alias = ctx.getFromJson(objectMapper, "alias")
|
val alias = ctx.getFromJson(objectMapper, "alias")
|
||||||
if (!LINK_REGEX.matcher(link).find()) throw BadRequestResponse("Invalid Link")
|
if (!LINK_REGEX.matcher(link).find()) throw BadRequestResponse("Invalid Link")
|
||||||
val aliasUtf8 = toUtf8(alias)
|
checkAliasLength(alias)
|
||||||
if (aliasUtf8.isEmpty() || aliasUtf8.size > MAX_AUTHOR_NAME_LENGTH)
|
|
||||||
throw BadRequestResponse("Invalid Alias")
|
|
||||||
val pendingContact = contactManager.addPendingContact(link, alias)
|
val pendingContact = contactManager.addPendingContact(link, alias)
|
||||||
return ctx.json(pendingContact.output())
|
return ctx.json(pendingContact.output())
|
||||||
}
|
}
|
||||||
@@ -124,6 +123,18 @@ constructor(
|
|||||||
return ctx
|
return ctx
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun setContactAlias(ctx: Context): Context {
|
||||||
|
val contactId = ctx.getContactIdFromPathParam()
|
||||||
|
val alias = ctx.getFromJson(objectMapper, "alias")
|
||||||
|
checkAliasLength(alias)
|
||||||
|
try {
|
||||||
|
contactManager.setContactAlias(contactId, alias)
|
||||||
|
} catch (e: NoSuchContactException) {
|
||||||
|
throw NotFoundResponse()
|
||||||
|
}
|
||||||
|
return ctx
|
||||||
|
}
|
||||||
|
|
||||||
override fun delete(ctx: Context): Context {
|
override fun delete(ctx: Context): Context {
|
||||||
val contactId = ctx.getContactIdFromPathParam()
|
val contactId = ctx.getContactIdFromPathParam()
|
||||||
try {
|
try {
|
||||||
@@ -134,4 +145,10 @@ constructor(
|
|||||||
return ctx
|
return ctx
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun checkAliasLength(alias: String) {
|
||||||
|
val aliasUtf8 = toUtf8(alias)
|
||||||
|
if (aliasUtf8.isEmpty() || aliasUtf8.size > MAX_AUTHOR_NAME_LENGTH)
|
||||||
|
throw BadRequestResponse("Invalid Alias")
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,12 +7,13 @@ import org.briarproject.bramble.api.plugin.event.ContactDisconnectedEvent
|
|||||||
import org.briarproject.bramble.identity.output
|
import org.briarproject.bramble.identity.output
|
||||||
import org.briarproject.briar.headless.json.JsonDict
|
import org.briarproject.briar.headless.json.JsonDict
|
||||||
|
|
||||||
internal fun Contact.output(latestMsgTime: Long, connected: Boolean) = JsonDict(
|
internal fun Contact.output(latestMsgTime: Long, connected: Boolean, unreadCount: Int) = JsonDict(
|
||||||
"contactId" to id.int,
|
"contactId" to id.int,
|
||||||
"author" to author.output(),
|
"author" to author.output(),
|
||||||
"verified" to isVerified,
|
"verified" to isVerified,
|
||||||
"lastChatActivity" to latestMsgTime,
|
"lastChatActivity" to latestMsgTime,
|
||||||
"connected" to connected
|
"connected" to connected,
|
||||||
|
"unreadCount" to unreadCount
|
||||||
).apply {
|
).apply {
|
||||||
alias?.let { put("alias", it) }
|
alias?.let { put("alias", it) }
|
||||||
handshakePublicKey?.let { put("handshakePublicKey", it.encoded) }
|
handshakePublicKey?.let { put("handshakePublicKey", it.encoded) }
|
||||||
|
|||||||
@@ -8,4 +8,8 @@ interface MessagingController {
|
|||||||
|
|
||||||
fun write(ctx: Context): Context
|
fun write(ctx: Context): Context
|
||||||
|
|
||||||
|
fun markMessageRead(ctx: Context): Context
|
||||||
|
|
||||||
|
fun deleteAllMessages(ctx: Context): Context
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,9 @@ import org.briarproject.bramble.api.db.DatabaseExecutor
|
|||||||
import org.briarproject.bramble.api.db.NoSuchContactException
|
import org.briarproject.bramble.api.db.NoSuchContactException
|
||||||
import org.briarproject.bramble.api.event.Event
|
import org.briarproject.bramble.api.event.Event
|
||||||
import org.briarproject.bramble.api.event.EventListener
|
import org.briarproject.bramble.api.event.EventListener
|
||||||
|
import org.briarproject.bramble.api.sync.event.MessagesAckedEvent
|
||||||
|
import org.briarproject.bramble.api.sync.event.MessagesSentEvent
|
||||||
|
import org.briarproject.bramble.api.sync.MessageId
|
||||||
import org.briarproject.bramble.api.system.Clock
|
import org.briarproject.bramble.api.system.Clock
|
||||||
import org.briarproject.bramble.util.StringUtils.utf8IsTooLong
|
import org.briarproject.bramble.util.StringUtils.utf8IsTooLong
|
||||||
import org.briarproject.briar.api.blog.BlogInvitationRequest
|
import org.briarproject.briar.api.blog.BlogInvitationRequest
|
||||||
@@ -33,12 +36,16 @@ import org.briarproject.briar.headless.event.output
|
|||||||
import org.briarproject.briar.headless.getContactIdFromPathParam
|
import org.briarproject.briar.headless.getContactIdFromPathParam
|
||||||
import org.briarproject.briar.headless.getFromJson
|
import org.briarproject.briar.headless.getFromJson
|
||||||
import org.briarproject.briar.headless.json.JsonDict
|
import org.briarproject.briar.headless.json.JsonDict
|
||||||
|
import org.spongycastle.util.encoders.Base64
|
||||||
|
import org.spongycastle.util.encoders.DecoderException
|
||||||
import java.util.concurrent.Executor
|
import java.util.concurrent.Executor
|
||||||
import javax.annotation.concurrent.Immutable
|
import javax.annotation.concurrent.Immutable
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
|
|
||||||
internal const val EVENT_CONVERSATION_MESSAGE = "ConversationMessageReceivedEvent"
|
internal const val EVENT_CONVERSATION_MESSAGE = "ConversationMessageReceivedEvent"
|
||||||
|
internal const val EVENT_MESSAGES_ACKED = "MessagesAckedEvent"
|
||||||
|
internal const val EVENT_MESSAGES_SENT = "MessagesSentEvent"
|
||||||
|
|
||||||
@Immutable
|
@Immutable
|
||||||
@Singleton
|
@Singleton
|
||||||
@@ -79,6 +86,36 @@ constructor(
|
|||||||
return ctx.json(m.output(contact.id, text))
|
return ctx.json(m.output(contact.id, text))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun markMessageRead(ctx: Context): Context {
|
||||||
|
val contact = getContact(ctx)
|
||||||
|
val groupId = messagingManager.getContactGroup(contact).id
|
||||||
|
|
||||||
|
val messageIdString = ctx.getFromJson(objectMapper, "messageId")
|
||||||
|
val messageId = deserializeMessageId(messageIdString)
|
||||||
|
messagingManager.setReadFlag(groupId, messageId, true)
|
||||||
|
return ctx.json(messageIdString)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun deserializeMessageId(idString: String): MessageId {
|
||||||
|
val idBytes = try {
|
||||||
|
Base64.decode(idString)
|
||||||
|
} catch (e: DecoderException) {
|
||||||
|
throw NotFoundResponse()
|
||||||
|
}
|
||||||
|
if (idBytes.size != MessageId.LENGTH) throw NotFoundResponse()
|
||||||
|
return MessageId(idBytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun deleteAllMessages(ctx: Context): Context {
|
||||||
|
val contactId = ctx.getContactIdFromPathParam()
|
||||||
|
try {
|
||||||
|
val result = conversationManager.deleteAllMessages(contactId)
|
||||||
|
return ctx.json(result.output())
|
||||||
|
} catch (e: NoSuchContactException) {
|
||||||
|
throw NotFoundResponse()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun eventOccurred(e: Event) {
|
override fun eventOccurred(e: Event) {
|
||||||
when (e) {
|
when (e) {
|
||||||
is ConversationMessageReceivedEvent<*> -> {
|
is ConversationMessageReceivedEvent<*> -> {
|
||||||
@@ -90,6 +127,12 @@ constructor(
|
|||||||
webSocketController.sendEvent(EVENT_CONVERSATION_MESSAGE, e.output())
|
webSocketController.sendEvent(EVENT_CONVERSATION_MESSAGE, e.output())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
is MessagesSentEvent -> {
|
||||||
|
webSocketController.sendEvent(EVENT_MESSAGES_SENT, e.output())
|
||||||
|
}
|
||||||
|
is MessagesAckedEvent -> {
|
||||||
|
webSocketController.sendEvent(EVENT_MESSAGES_ACKED, e.output())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,11 @@
|
|||||||
package org.briarproject.briar.headless.messaging
|
package org.briarproject.briar.headless.messaging
|
||||||
|
|
||||||
import org.briarproject.bramble.api.contact.ContactId
|
import org.briarproject.bramble.api.contact.ContactId
|
||||||
|
import org.briarproject.bramble.api.sync.MessageId
|
||||||
|
import org.briarproject.bramble.api.sync.event.MessagesAckedEvent
|
||||||
|
import org.briarproject.bramble.api.sync.event.MessagesSentEvent
|
||||||
import org.briarproject.briar.api.conversation.ConversationMessageHeader
|
import org.briarproject.briar.api.conversation.ConversationMessageHeader
|
||||||
|
import org.briarproject.briar.api.conversation.DeletionResult
|
||||||
import org.briarproject.briar.api.messaging.PrivateMessage
|
import org.briarproject.briar.api.messaging.PrivateMessage
|
||||||
import org.briarproject.briar.api.messaging.PrivateMessageHeader
|
import org.briarproject.briar.api.messaging.PrivateMessageHeader
|
||||||
import org.briarproject.briar.headless.json.JsonDict
|
import org.briarproject.briar.headless.json.JsonDict
|
||||||
@@ -43,3 +47,24 @@ internal fun PrivateMessage.output(contactId: ContactId, text: String) = JsonDic
|
|||||||
"groupId" to message.groupId.bytes,
|
"groupId" to message.groupId.bytes,
|
||||||
"text" to text
|
"text" to text
|
||||||
)
|
)
|
||||||
|
|
||||||
|
internal fun DeletionResult.output() = JsonDict(
|
||||||
|
"allDeleted" to allDeleted(),
|
||||||
|
"hasIntroductionSessionInProgress" to hasIntroductionSessionInProgress(),
|
||||||
|
"hasInvitationSessionInProgress" to hasInvitationSessionInProgress(),
|
||||||
|
"hasNotAllIntroductionSelected" to hasNotAllIntroductionSelected(),
|
||||||
|
"hasNotAllInvitationSelected" to hasNotAllInvitationSelected(),
|
||||||
|
"hasNotFullyDownloaded" to hasNotFullyDownloaded()
|
||||||
|
)
|
||||||
|
|
||||||
|
internal fun MessagesAckedEvent.output() = JsonDict(
|
||||||
|
"contactId" to contactId.int,
|
||||||
|
"messageIds" to messageIds.toJson()
|
||||||
|
)
|
||||||
|
|
||||||
|
internal fun MessagesSentEvent.output() = JsonDict(
|
||||||
|
"contactId" to contactId.int,
|
||||||
|
"messageIds" to messageIds.toJson()
|
||||||
|
)
|
||||||
|
|
||||||
|
internal fun Collection<MessageId>.toJson() = map { it.bytes }
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ abstract class ControllerTest {
|
|||||||
protected val message: Message = getMessage(group.id)
|
protected val message: Message = getMessage(group.id)
|
||||||
protected val text: String = getRandomString(5)
|
protected val text: String = getRandomString(5)
|
||||||
protected val timestamp = 42L
|
protected val timestamp = 42L
|
||||||
|
protected val unreadCount = 42
|
||||||
|
|
||||||
protected fun assertJsonEquals(json: String, obj: Any) {
|
protected fun assertJsonEquals(json: String, obj: Any) {
|
||||||
assertEquals(json, outputCtx.json(obj).resultString(), STRICT)
|
assertEquals(json, outputCtx.json(obj).resultString(), STRICT)
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import io.javalin.plugin.json.JavalinJson.toJson
|
|||||||
import io.mockk.Runs
|
import io.mockk.Runs
|
||||||
import io.mockk.every
|
import io.mockk.every
|
||||||
import io.mockk.just
|
import io.mockk.just
|
||||||
|
import io.mockk.mockkStatic
|
||||||
import io.mockk.runs
|
import io.mockk.runs
|
||||||
import org.briarproject.bramble.api.Pair
|
import org.briarproject.bramble.api.Pair
|
||||||
import org.briarproject.bramble.api.contact.Contact
|
import org.briarproject.bramble.api.contact.Contact
|
||||||
@@ -27,6 +28,7 @@ import org.briarproject.bramble.test.TestUtils.getPendingContact
|
|||||||
import org.briarproject.bramble.test.TestUtils.getRandomBytes
|
import org.briarproject.bramble.test.TestUtils.getRandomBytes
|
||||||
import org.briarproject.bramble.util.StringUtils.getRandomString
|
import org.briarproject.bramble.util.StringUtils.getRandomString
|
||||||
import org.briarproject.briar.headless.ControllerTest
|
import org.briarproject.briar.headless.ControllerTest
|
||||||
|
import org.briarproject.briar.headless.getFromJson
|
||||||
import org.briarproject.briar.headless.json.JsonDict
|
import org.briarproject.briar.headless.json.JsonDict
|
||||||
import org.junit.jupiter.api.Assertions.assertNotNull
|
import org.junit.jupiter.api.Assertions.assertNotNull
|
||||||
import org.junit.jupiter.api.Assertions.assertThrows
|
import org.junit.jupiter.api.Assertions.assertThrows
|
||||||
@@ -58,7 +60,8 @@ internal class ContactControllerTest : ControllerTest() {
|
|||||||
every { contactManager.contacts } returns listOf(contact)
|
every { contactManager.contacts } returns listOf(contact)
|
||||||
every { conversationManager.getGroupCount(contact.id).latestMsgTime } returns timestamp
|
every { conversationManager.getGroupCount(contact.id).latestMsgTime } returns timestamp
|
||||||
every { connectionRegistry.isConnected(contact.id) } returns connected
|
every { connectionRegistry.isConnected(contact.id) } returns connected
|
||||||
every { ctx.json(listOf(contact.output(timestamp, connected))) } returns ctx
|
every { conversationManager.getGroupCount(contact.id).unreadCount } returns unreadCount
|
||||||
|
every { ctx.json(listOf(contact.output(timestamp, connected, unreadCount))) } returns ctx
|
||||||
controller.list(ctx)
|
controller.list(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -193,6 +196,66 @@ internal class ContactControllerTest : ControllerTest() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testSetContactAlias() {
|
||||||
|
mockkStatic("org.briarproject.briar.headless.RouterKt")
|
||||||
|
every { ctx.pathParam("contactId") } returns "1"
|
||||||
|
every { ctx.getFromJson(objectMapper, "alias") } returns "foo"
|
||||||
|
every { contactManager.setContactAlias(ContactId(1), "foo") } just Runs
|
||||||
|
controller.setContactAlias(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testSetContactAliasInvalidId() {
|
||||||
|
mockkStatic("org.briarproject.briar.headless.RouterKt")
|
||||||
|
every { ctx.pathParam("contactId") } returns "foo"
|
||||||
|
every { ctx.getFromJson(objectMapper, "alias") } returns "bar"
|
||||||
|
assertThrows(NotFoundResponse::class.java) {
|
||||||
|
controller.setContactAlias(ctx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testSetContactAliasNonexistentId() {
|
||||||
|
mockkStatic("org.briarproject.briar.headless.RouterKt")
|
||||||
|
every { ctx.pathParam("contactId") } returns "1"
|
||||||
|
every { ctx.getFromJson(objectMapper, "alias") } returns "foo"
|
||||||
|
every { contactManager.setContactAlias(ContactId(1), "foo") } throws NotFoundResponse()
|
||||||
|
assertThrows(NotFoundResponse::class.java) {
|
||||||
|
controller.setContactAlias(ctx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testSetContactAliasInvalid() {
|
||||||
|
mockkStatic("org.briarproject.briar.headless.RouterKt")
|
||||||
|
every { ctx.pathParam("contactId") } returns "1"
|
||||||
|
every { ctx.getFromJson(objectMapper, "alias") } returns getRandomString(MAX_AUTHOR_NAME_LENGTH + 1)
|
||||||
|
assertThrows(BadRequestResponse::class.java) {
|
||||||
|
controller.setContactAlias(ctx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testSetContactAliasEmpty() {
|
||||||
|
mockkStatic("org.briarproject.briar.headless.RouterKt")
|
||||||
|
every { ctx.pathParam("contactId") } returns "1"
|
||||||
|
every { ctx.getFromJson(objectMapper, "alias") } returns ""
|
||||||
|
assertThrows(BadRequestResponse::class.java) {
|
||||||
|
controller.setContactAlias(ctx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testSetContactAliasMissing() {
|
||||||
|
mockkStatic("org.briarproject.briar.headless.RouterKt")
|
||||||
|
every { ctx.pathParam("contactId") } returns "1"
|
||||||
|
every { ctx.body() } returns ""
|
||||||
|
assertThrows(BadRequestResponse::class.java) {
|
||||||
|
controller.setContactAlias(ctx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testDelete() {
|
fun testDelete() {
|
||||||
every { ctx.pathParam("contactId") } returns "1"
|
every { ctx.pathParam("contactId") } returns "1"
|
||||||
@@ -313,10 +376,11 @@ internal class ContactControllerTest : ControllerTest() {
|
|||||||
"handshakePublicKey": ${toJson(contact.handshakePublicKey!!.encoded)},
|
"handshakePublicKey": ${toJson(contact.handshakePublicKey!!.encoded)},
|
||||||
"verified": ${contact.isVerified},
|
"verified": ${contact.isVerified},
|
||||||
"lastChatActivity": $timestamp,
|
"lastChatActivity": $timestamp,
|
||||||
"connected": $connected
|
"connected": $connected,
|
||||||
|
"unreadCount": $unreadCount
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
assertJsonEquals(json, contact.output(timestamp, connected))
|
assertJsonEquals(json, contact.output(timestamp, connected, unreadCount))
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
@@ -10,11 +10,14 @@ import org.briarproject.bramble.api.db.NoSuchContactException
|
|||||||
import org.briarproject.bramble.api.identity.AuthorInfo
|
import org.briarproject.bramble.api.identity.AuthorInfo
|
||||||
import org.briarproject.bramble.api.identity.AuthorInfo.Status.UNVERIFIED
|
import org.briarproject.bramble.api.identity.AuthorInfo.Status.UNVERIFIED
|
||||||
import org.briarproject.bramble.api.identity.AuthorInfo.Status.VERIFIED
|
import org.briarproject.bramble.api.identity.AuthorInfo.Status.VERIFIED
|
||||||
|
import org.briarproject.bramble.api.sync.MessageId
|
||||||
|
import org.briarproject.bramble.api.sync.event.MessagesAckedEvent
|
||||||
|
import org.briarproject.bramble.api.sync.event.MessagesSentEvent
|
||||||
import org.briarproject.bramble.test.ImmediateExecutor
|
import org.briarproject.bramble.test.ImmediateExecutor
|
||||||
import org.briarproject.bramble.test.TestUtils.getRandomId
|
import org.briarproject.bramble.test.TestUtils.getRandomId
|
||||||
import org.briarproject.bramble.util.StringUtils.getRandomString
|
import org.briarproject.bramble.util.StringUtils.getRandomString
|
||||||
import org.briarproject.briar.api.client.SessionId
|
import org.briarproject.briar.api.client.SessionId
|
||||||
import org.briarproject.briar.api.conversation.ConversationManager
|
import org.briarproject.briar.api.conversation.DeletionResult
|
||||||
import org.briarproject.briar.api.introduction.IntroductionRequest
|
import org.briarproject.briar.api.introduction.IntroductionRequest
|
||||||
import org.briarproject.briar.api.messaging.MessagingConstants.MAX_PRIVATE_MESSAGE_TEXT_LENGTH
|
import org.briarproject.briar.api.messaging.MessagingConstants.MAX_PRIVATE_MESSAGE_TEXT_LENGTH
|
||||||
import org.briarproject.briar.api.messaging.MessagingManager
|
import org.briarproject.briar.api.messaging.MessagingManager
|
||||||
@@ -24,10 +27,13 @@ import org.briarproject.briar.api.messaging.PrivateMessageHeader
|
|||||||
import org.briarproject.briar.api.messaging.event.PrivateMessageReceivedEvent
|
import org.briarproject.briar.api.messaging.event.PrivateMessageReceivedEvent
|
||||||
import org.briarproject.briar.headless.ControllerTest
|
import org.briarproject.briar.headless.ControllerTest
|
||||||
import org.briarproject.briar.headless.event.output
|
import org.briarproject.briar.headless.event.output
|
||||||
|
import org.briarproject.briar.headless.getFromJson
|
||||||
import org.briarproject.briar.headless.json.JsonDict
|
import org.briarproject.briar.headless.json.JsonDict
|
||||||
import org.junit.jupiter.api.Assertions.assertEquals
|
import org.junit.jupiter.api.Assertions.assertEquals
|
||||||
import org.junit.jupiter.api.Assertions.assertThrows
|
import org.junit.jupiter.api.Assertions.assertThrows
|
||||||
import org.junit.jupiter.api.Test
|
import org.junit.jupiter.api.Test
|
||||||
|
import org.spongycastle.util.encoders.Base64
|
||||||
|
import kotlin.random.Random
|
||||||
|
|
||||||
internal class MessagingControllerImplTest : ControllerTest() {
|
internal class MessagingControllerImplTest : ControllerTest() {
|
||||||
|
|
||||||
@@ -100,6 +106,40 @@ internal class MessagingControllerImplTest : ControllerTest() {
|
|||||||
testInvalidContactId { controller.list(ctx) }
|
testInvalidContactId { controller.list(ctx) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testMessagesAckedEvent() {
|
||||||
|
val messageId1 = MessageId(getRandomId())
|
||||||
|
val messageId2 = MessageId(getRandomId())
|
||||||
|
val messageIds = listOf(messageId1, messageId2)
|
||||||
|
val event = MessagesAckedEvent(contact.id, messageIds)
|
||||||
|
|
||||||
|
every {
|
||||||
|
webSocketController.sendEvent(
|
||||||
|
EVENT_MESSAGES_ACKED,
|
||||||
|
event.output()
|
||||||
|
)
|
||||||
|
} just runs
|
||||||
|
|
||||||
|
controller.eventOccurred(event)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testMessagesSentEvent() {
|
||||||
|
val messageId1 = MessageId(getRandomId())
|
||||||
|
val messageId2 = MessageId(getRandomId())
|
||||||
|
val messageIds = listOf(messageId1, messageId2)
|
||||||
|
val event = MessagesSentEvent(contact.id, messageIds)
|
||||||
|
|
||||||
|
every {
|
||||||
|
webSocketController.sendEvent(
|
||||||
|
EVENT_MESSAGES_SENT,
|
||||||
|
event.output()
|
||||||
|
)
|
||||||
|
} just runs
|
||||||
|
|
||||||
|
controller.eventOccurred(event)
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun listNonexistentContactId() {
|
fun listNonexistentContactId() {
|
||||||
testNonexistentContactId { controller.list(ctx) }
|
testNonexistentContactId { controller.list(ctx) }
|
||||||
@@ -162,6 +202,32 @@ internal class MessagingControllerImplTest : ControllerTest() {
|
|||||||
assertThrows(BadRequestResponse::class.java) { controller.write(ctx) }
|
assertThrows(BadRequestResponse::class.java) { controller.write(ctx) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun markMessageRead() {
|
||||||
|
mockkStatic("org.briarproject.briar.headless.RouterKt")
|
||||||
|
mockkStatic("org.spongycastle.util.encoders.Base64")
|
||||||
|
expectGetContact()
|
||||||
|
|
||||||
|
val messageIdString = message.id.bytes.toString()
|
||||||
|
every { messagingManager.getContactGroup(contact).id } returns group.id
|
||||||
|
every { ctx.getFromJson(objectMapper, "messageId") } returns messageIdString
|
||||||
|
every { Base64.decode(messageIdString) } returns message.id.bytes
|
||||||
|
every { messagingManager.setReadFlag(group.id, message.id, true) } just Runs
|
||||||
|
every { ctx.json(messageIdString) } returns ctx
|
||||||
|
|
||||||
|
controller.markMessageRead(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun markMessageReadInvalidContactId() {
|
||||||
|
testInvalidContactId { controller.markMessageRead(ctx) }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun markMessageReadNonexistentId() {
|
||||||
|
testNonexistentContactId { controller.markMessageRead(ctx) }
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun privateMessageEvent() {
|
fun privateMessageEvent() {
|
||||||
val event = PrivateMessageReceivedEvent(header, contact.id)
|
val event = PrivateMessageReceivedEvent(header, contact.id)
|
||||||
@@ -177,6 +243,43 @@ internal class MessagingControllerImplTest : ControllerTest() {
|
|||||||
controller.eventOccurred(event)
|
controller.eventOccurred(event)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testOutputMessagesAckedEvent() {
|
||||||
|
val messageId1 = MessageId(getRandomId())
|
||||||
|
val messageId2 = MessageId(getRandomId())
|
||||||
|
val messageIds = listOf(messageId1, messageId2)
|
||||||
|
val event = MessagesAckedEvent(contact.id, messageIds)
|
||||||
|
val json = """
|
||||||
|
{
|
||||||
|
"contactId": ${contact.id.int},
|
||||||
|
"messageIds": [
|
||||||
|
${toJson(messageId1.bytes)},
|
||||||
|
${toJson(messageId2.bytes)}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
assertJsonEquals(json, event.output())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testOutputMessagesSentEvent() {
|
||||||
|
val messageId1 = MessageId(getRandomId())
|
||||||
|
val messageId2 = MessageId(getRandomId())
|
||||||
|
val messageIds = listOf(messageId1, messageId2)
|
||||||
|
val event = MessagesSentEvent(contact.id, messageIds)
|
||||||
|
|
||||||
|
val json = """
|
||||||
|
{
|
||||||
|
"contactId": ${contact.id.int},
|
||||||
|
"messageIds": [
|
||||||
|
${toJson(messageId1.bytes)},
|
||||||
|
${toJson(messageId2.bytes)}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
assertJsonEquals(json, event.output())
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testOutputPrivateMessageHeader() {
|
fun testOutputPrivateMessageHeader() {
|
||||||
val json = """
|
val json = """
|
||||||
@@ -242,6 +345,53 @@ internal class MessagingControllerImplTest : ControllerTest() {
|
|||||||
assertJsonEquals(json, request.output(contact.id))
|
assertJsonEquals(json, request.output(contact.id))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testDeleteAllMessages() {
|
||||||
|
val result = DeletionResult()
|
||||||
|
every { ctx.pathParam("contactId") } returns "1"
|
||||||
|
every { conversationManager.deleteAllMessages(ContactId(1)) } returns result
|
||||||
|
every { ctx.json(result.output()) } returns ctx
|
||||||
|
controller.deleteAllMessages(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testDeleteAllMessagesInvalidContactId() {
|
||||||
|
every { ctx.pathParam("contactId") } returns "foo"
|
||||||
|
assertThrows(NotFoundResponse::class.java) {
|
||||||
|
controller.deleteAllMessages(ctx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testDeleteAllMessagesNonexistentContactId() {
|
||||||
|
every { ctx.pathParam("contactId") } returns "1"
|
||||||
|
every { conversationManager.deleteAllMessages(ContactId(1)) } throws NoSuchContactException()
|
||||||
|
assertThrows(NotFoundResponse::class.java) {
|
||||||
|
controller.deleteAllMessages(ctx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testOutputDeletionResult() {
|
||||||
|
val result = DeletionResult()
|
||||||
|
if (Random.nextBoolean()) result.addInvitationNotAllSelected()
|
||||||
|
if (Random.nextBoolean()) result.addInvitationSessionInProgress()
|
||||||
|
if (Random.nextBoolean()) result.addIntroductionNotAllSelected()
|
||||||
|
if (Random.nextBoolean()) result.addIntroductionSessionInProgress()
|
||||||
|
if (Random.nextBoolean()) result.addNotFullyDownloaded()
|
||||||
|
val json = """
|
||||||
|
{
|
||||||
|
"allDeleted": ${result.allDeleted()},
|
||||||
|
"hasIntroductionSessionInProgress": ${result.hasIntroductionSessionInProgress()},
|
||||||
|
"hasInvitationSessionInProgress": ${result.hasInvitationSessionInProgress()},
|
||||||
|
"hasNotAllIntroductionSelected": ${result.hasNotAllIntroductionSelected()},
|
||||||
|
"hasNotAllInvitationSelected": ${result.hasNotAllInvitationSelected()},
|
||||||
|
"hasNotFullyDownloaded": ${result.hasNotFullyDownloaded()}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
assertJsonEquals(json, result.output())
|
||||||
|
}
|
||||||
|
|
||||||
private fun expectGetContact() {
|
private fun expectGetContact() {
|
||||||
every { ctx.pathParam("contactId") } returns contact.id.int.toString()
|
every { ctx.pathParam("contactId") } returns contact.id.int.toString()
|
||||||
every { contactManager.getContact(contact.id) } returns contact
|
every { contactManager.getContact(contact.id) } returns contact
|
||||||
|
|||||||
@@ -33,3 +33,12 @@ buildscript {
|
|||||||
classpath files('libs/gradle-witness.jar')
|
classpath files('libs/gradle-witness.jar')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
project.ext {
|
||||||
|
buildToolsVersion = '30.0.2'
|
||||||
|
compileSdkVersion = 30
|
||||||
|
minSdkVersion = 16
|
||||||
|
targetSdkVersion = 29
|
||||||
|
versionCode = 10212
|
||||||
|
versionName = '1.2.12'
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user