mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-11 18:29:05 +01:00
Compare commits
37 Commits
alpha-1.4.
...
alpha-1.4.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7536f16c61 | ||
|
|
ab628c1921 | ||
|
|
85e53479f2 | ||
|
|
116ee97056 | ||
|
|
78938f1ac6 | ||
|
|
afff66eaff | ||
|
|
a8624cd507 | ||
|
|
e7fc37d81e | ||
|
|
7bd220f18d | ||
|
|
dea05c85a2 | ||
|
|
174ca3cfb8 | ||
|
|
961af66c8e | ||
|
|
a86ea454d0 | ||
|
|
a7877bf7ee | ||
|
|
62ae0f745b | ||
|
|
f83abbe63d | ||
|
|
e0b6b8435d | ||
|
|
d3c7832245 | ||
|
|
a043e8b1cf | ||
|
|
bc013296f6 | ||
|
|
c1fabcd46b | ||
|
|
3c08e86822 | ||
|
|
de2c9670d5 | ||
|
|
9632754274 | ||
|
|
b275a0ffff | ||
|
|
74a3f54d28 | ||
|
|
edcb234b93 | ||
|
|
dae00c7e4e | ||
|
|
29b16c4d74 | ||
|
|
40d58a9359 | ||
|
|
60a1a4d2d1 | ||
|
|
238aeb3abd | ||
|
|
62c16fad09 | ||
|
|
68e57bda0d | ||
|
|
0df73dbf0a | ||
|
|
5b648cbd35 | ||
|
|
5e7891d78a |
@@ -123,5 +123,6 @@ pre_release_tests:
|
||||
extends: .optional_tests
|
||||
script:
|
||||
- OPTIONAL_TESTS=org.briarproject.bramble.plugin.tor.BridgeTest ./gradlew --info bramble-java:test --tests BridgeTest
|
||||
timeout: 3h
|
||||
only:
|
||||
- tags
|
||||
|
||||
@@ -15,8 +15,8 @@ android {
|
||||
defaultConfig {
|
||||
minSdkVersion 16
|
||||
targetSdkVersion 30
|
||||
versionCode 10406
|
||||
versionName "1.4.6"
|
||||
versionCode 10407
|
||||
versionName "1.4.7"
|
||||
consumerProguardFiles 'proguard-rules.txt'
|
||||
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
|
||||
@@ -11,6 +11,8 @@ import android.net.LinkAddress;
|
||||
import android.net.LinkProperties;
|
||||
import android.net.Network;
|
||||
import android.net.NetworkInfo;
|
||||
import android.net.wifi.WifiInfo;
|
||||
import android.net.wifi.WifiManager;
|
||||
|
||||
import org.briarproject.bramble.api.event.EventBus;
|
||||
import org.briarproject.bramble.api.event.EventExecutor;
|
||||
@@ -38,6 +40,7 @@ import javax.annotation.Nullable;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static android.content.Context.CONNECTIVITY_SERVICE;
|
||||
import static android.content.Context.WIFI_SERVICE;
|
||||
import static android.content.Intent.ACTION_SCREEN_OFF;
|
||||
import static android.content.Intent.ACTION_SCREEN_ON;
|
||||
import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
|
||||
@@ -111,15 +114,37 @@ class AndroidNetworkManager implements NetworkManager, Service {
|
||||
|
||||
@Override
|
||||
public NetworkStatus getNetworkStatus() {
|
||||
NetworkInfo net = connectivityManager.getActiveNetworkInfo();
|
||||
boolean connected = net != null && net.isConnected();
|
||||
boolean wifi = false, ipv6Only = false;
|
||||
if (connected) {
|
||||
wifi = net.getType() == TYPE_WIFI;
|
||||
if (SDK_INT >= 23) ipv6Only = isActiveNetworkIpv6Only();
|
||||
else ipv6Only = areAllAvailableNetworksIpv6Only();
|
||||
// https://issuetracker.google.com/issues/175055271
|
||||
try {
|
||||
NetworkInfo net = connectivityManager.getActiveNetworkInfo();
|
||||
boolean connected = net != null && net.isConnected();
|
||||
boolean wifi = false, ipv6Only = false;
|
||||
if (connected) {
|
||||
wifi = net.getType() == TYPE_WIFI;
|
||||
if (SDK_INT >= 23) ipv6Only = isActiveNetworkIpv6Only();
|
||||
else ipv6Only = areAllAvailableNetworksIpv6Only();
|
||||
}
|
||||
return new NetworkStatus(connected, wifi, ipv6Only);
|
||||
} catch (SecurityException e) {
|
||||
logException(LOG, WARNING, e);
|
||||
// Without the ConnectivityManager we can't detect whether we have
|
||||
// internet access. Assume we do, which is probably less harmful
|
||||
// than assuming we don't. Likewise, assume the connection is
|
||||
// IPv6-only. Fall back to the WifiManager to detect whether we
|
||||
// have a wifi connection.
|
||||
LOG.info("ConnectivityManager is broken, guessing connectivity");
|
||||
boolean connected = true, wifi = false, ipv6Only = true;
|
||||
WifiManager wm = (WifiManager) app.getSystemService(WIFI_SERVICE);
|
||||
if (wm != null) {
|
||||
WifiInfo info = wm.getConnectionInfo();
|
||||
if (info != null && info.getIpAddress() != 0) {
|
||||
LOG.info("Connected to wifi");
|
||||
wifi = true;
|
||||
ipv6Only = false;
|
||||
}
|
||||
}
|
||||
return new NetworkStatus(connected, wifi, ipv6Only);
|
||||
}
|
||||
return new NetworkStatus(connected, wifi, ipv6Only);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -130,23 +155,29 @@ class AndroidNetworkManager implements NetworkManager, Service {
|
||||
*/
|
||||
@TargetApi(23)
|
||||
private boolean isActiveNetworkIpv6Only() {
|
||||
Network net = connectivityManager.getActiveNetwork();
|
||||
if (net == null) {
|
||||
LOG.info("No active network");
|
||||
// https://issuetracker.google.com/issues/175055271
|
||||
try {
|
||||
Network net = connectivityManager.getActiveNetwork();
|
||||
if (net == null) {
|
||||
LOG.info("No active network");
|
||||
return false;
|
||||
}
|
||||
LinkProperties props = connectivityManager.getLinkProperties(net);
|
||||
if (props == null) {
|
||||
LOG.info("No link properties for active network");
|
||||
return false;
|
||||
}
|
||||
boolean hasIpv6Unicast = false;
|
||||
for (LinkAddress linkAddress : props.getLinkAddresses()) {
|
||||
InetAddress addr = linkAddress.getAddress();
|
||||
if (addr instanceof Inet4Address) return false;
|
||||
if (!addr.isMulticastAddress()) hasIpv6Unicast = true;
|
||||
}
|
||||
return hasIpv6Unicast;
|
||||
} catch (SecurityException e) {
|
||||
logException(LOG, WARNING, e);
|
||||
return false;
|
||||
}
|
||||
LinkProperties props = connectivityManager.getLinkProperties(net);
|
||||
if (props == null) {
|
||||
LOG.info("No link properties for active network");
|
||||
return false;
|
||||
}
|
||||
boolean hasIpv6Unicast = false;
|
||||
for (LinkAddress linkAddress : props.getLinkAddresses()) {
|
||||
InetAddress addr = linkAddress.getAddress();
|
||||
if (addr instanceof Inet4Address) return false;
|
||||
if (!addr.isMulticastAddress()) hasIpv6Unicast = true;
|
||||
}
|
||||
return hasIpv6Unicast;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -32,13 +32,22 @@ class AndroidRemovableDrivePlugin extends RemovableDrivePlugin {
|
||||
InputStream openInputStream(TransportProperties p) throws IOException {
|
||||
String uri = p.get(PROP_URI);
|
||||
if (isNullOrEmpty(uri)) throw new IllegalArgumentException();
|
||||
return app.getContentResolver().openInputStream(Uri.parse(uri));
|
||||
try {
|
||||
return app.getContentResolver().openInputStream(Uri.parse(uri));
|
||||
} catch (SecurityException e) {
|
||||
throw new IOException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
OutputStream openOutputStream(TransportProperties p) throws IOException {
|
||||
String uri = p.get(PROP_URI);
|
||||
if (isNullOrEmpty(uri)) throw new IllegalArgumentException();
|
||||
return app.getContentResolver().openOutputStream(Uri.parse(uri), "wt");
|
||||
try {
|
||||
return app.getContentResolver()
|
||||
.openOutputStream(Uri.parse(uri), "wt");
|
||||
} catch (SecurityException e) {
|
||||
throw new IOException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -175,16 +175,24 @@ class AndroidLanTcpPlugin extends LanTcpPlugin {
|
||||
@TargetApi(21)
|
||||
@Nullable
|
||||
private InetAddress getWifiClientIpv6Address() {
|
||||
for (Network net : connectivityManager.getAllNetworks()) {
|
||||
NetworkCapabilities caps =
|
||||
connectivityManager.getNetworkCapabilities(net);
|
||||
if (caps == null || !caps.hasTransport(TRANSPORT_WIFI)) continue;
|
||||
LinkProperties props = connectivityManager.getLinkProperties(net);
|
||||
if (props == null) continue;
|
||||
for (LinkAddress linkAddress : props.getLinkAddresses()) {
|
||||
InetAddress addr = linkAddress.getAddress();
|
||||
if (isIpv6LinkLocalAddress(addr)) return addr;
|
||||
// https://issuetracker.google.com/issues/175055271
|
||||
try {
|
||||
for (Network net : connectivityManager.getAllNetworks()) {
|
||||
NetworkCapabilities caps =
|
||||
connectivityManager.getNetworkCapabilities(net);
|
||||
if (caps == null || !caps.hasTransport(TRANSPORT_WIFI)) {
|
||||
continue;
|
||||
}
|
||||
LinkProperties props =
|
||||
connectivityManager.getLinkProperties(net);
|
||||
if (props == null) continue;
|
||||
for (LinkAddress linkAddress : props.getLinkAddresses()) {
|
||||
InetAddress addr = linkAddress.getAddress();
|
||||
if (isIpv6LinkLocalAddress(addr)) return addr;
|
||||
}
|
||||
}
|
||||
} catch (SecurityException e) {
|
||||
logException(LOG, WARNING, e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -227,12 +235,17 @@ class AndroidLanTcpPlugin extends LanTcpPlugin {
|
||||
// network's socket factory may try to connect via another network
|
||||
private SocketFactory getSocketFactory() {
|
||||
if (SDK_INT < 21) return SocketFactory.getDefault();
|
||||
for (Network net : connectivityManager.getAllNetworks()) {
|
||||
NetworkCapabilities caps =
|
||||
connectivityManager.getNetworkCapabilities(net);
|
||||
if (caps != null && caps.hasTransport(TRANSPORT_WIFI)) {
|
||||
return net.getSocketFactory();
|
||||
// https://issuetracker.google.com/issues/175055271
|
||||
try {
|
||||
for (Network net : connectivityManager.getAllNetworks()) {
|
||||
NetworkCapabilities caps =
|
||||
connectivityManager.getNetworkCapabilities(net);
|
||||
if (caps != null && caps.hasTransport(TRANSPORT_WIFI)) {
|
||||
return net.getSocketFactory();
|
||||
}
|
||||
}
|
||||
} catch (SecurityException e) {
|
||||
logException(LOG, WARNING, e);
|
||||
}
|
||||
LOG.warning("Could not find suitable socket factory");
|
||||
return SocketFactory.getDefault();
|
||||
|
||||
@@ -116,10 +116,12 @@ class AndroidTaskScheduler implements TaskScheduler, Service, AlarmListener {
|
||||
long dueMillis = now + MILLISECONDS.convert(delay, unit);
|
||||
Runnable wakeful = () ->
|
||||
wakeLockManager.executeWakefully(task, executor, "TaskHandoff");
|
||||
Future<?> check = scheduleCheckForDueTasks(delay, unit);
|
||||
ScheduledTask s = new ScheduledTask(wakeful, dueMillis, check,
|
||||
cancelled);
|
||||
// Acquire the lock before scheduling the check to ensure the check
|
||||
// doesn't access the task queue before the task has been added
|
||||
ScheduledTask s;
|
||||
synchronized (lock) {
|
||||
Future<?> check = scheduleCheckForDueTasks(delay, unit);
|
||||
s = new ScheduledTask(wakeful, dueMillis, check, cancelled);
|
||||
tasks.add(s);
|
||||
}
|
||||
return s;
|
||||
@@ -136,6 +138,7 @@ class AndroidTaskScheduler implements TaskScheduler, Service, AlarmListener {
|
||||
return schedule(wrapped, executor, delay, unit, cancelled);
|
||||
}
|
||||
|
||||
@GuardedBy("lock")
|
||||
private Future<?> scheduleCheckForDueTasks(long delay, TimeUnit unit) {
|
||||
Runnable wakeful = () -> wakeLockManager.runWakefully(
|
||||
this::runDueTasks, "TaskScheduler");
|
||||
@@ -206,7 +209,7 @@ class AndroidTaskScheduler implements TaskScheduler, Service, AlarmListener {
|
||||
private final Future<?> check;
|
||||
private final AtomicBoolean cancelled;
|
||||
|
||||
public ScheduledTask(Runnable task, long dueMillis,
|
||||
private ScheduledTask(Runnable task, long dueMillis,
|
||||
Future<?> check, AtomicBoolean cancelled) {
|
||||
this.task = task;
|
||||
this.dueMillis = dueMillis;
|
||||
|
||||
@@ -4,6 +4,7 @@ import android.annotation.SuppressLint;
|
||||
import android.bluetooth.BluetoothAdapter;
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
import android.os.Looper;
|
||||
import android.provider.Settings;
|
||||
|
||||
import org.briarproject.bramble.api.Pair;
|
||||
@@ -134,4 +135,8 @@ public class AndroidUtils {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isUiThread() {
|
||||
return Looper.myLooper() == Looper.getMainLooper();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,4 +32,13 @@ public interface MailboxManager {
|
||||
*/
|
||||
MailboxPairingTask startPairingTask(String qrCodePayload);
|
||||
|
||||
/**
|
||||
* Can be used by the UI to test the mailbox connection.
|
||||
*
|
||||
* @return true (success) or false (error).
|
||||
* A {@link OwnMailboxConnectionStatusEvent} might be broadcast with a new
|
||||
* {@link MailboxStatus}.
|
||||
*/
|
||||
boolean checkConnection();
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package org.briarproject.bramble.api.plugin;
|
||||
|
||||
import static java.util.concurrent.TimeUnit.SECONDS;
|
||||
|
||||
public interface TorConstants {
|
||||
|
||||
TransportId ID = new TransportId("org.briarproject.bramble.tor");
|
||||
@@ -10,8 +12,9 @@ public interface TorConstants {
|
||||
int DEFAULT_SOCKS_PORT = 59050;
|
||||
int DEFAULT_CONTROL_PORT = 59051;
|
||||
|
||||
int CONNECT_TO_PROXY_TIMEOUT = 5000; // Milliseconds
|
||||
int EXTRA_SOCKET_TIMEOUT = 30000; // Milliseconds
|
||||
int CONNECT_TO_PROXY_TIMEOUT = (int) SECONDS.toMillis(5);
|
||||
int EXTRA_CONNECT_TIMEOUT = (int) SECONDS.toMillis(120);
|
||||
int EXTRA_SOCKET_TIMEOUT = (int) SECONDS.toMillis(30);
|
||||
|
||||
// Local settings (not shared with contacts)
|
||||
String PREF_TOR_NETWORK = "network2";
|
||||
|
||||
@@ -2,27 +2,42 @@ package org.briarproject.bramble.mailbox;
|
||||
|
||||
import org.briarproject.bramble.api.db.DbException;
|
||||
import org.briarproject.bramble.api.db.Transaction;
|
||||
import org.briarproject.bramble.api.db.TransactionManager;
|
||||
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxManager;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxPairingTask;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxProperties;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxSettingsManager;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxStatus;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.bramble.api.system.Clock;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.annotation.concurrent.GuardedBy;
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static java.util.logging.Level.WARNING;
|
||||
import static java.util.logging.Logger.getLogger;
|
||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
||||
|
||||
@Immutable
|
||||
@NotNullByDefault
|
||||
class MailboxManagerImpl implements MailboxManager {
|
||||
|
||||
private static final String TAG = MailboxManagerImpl.class.getName();
|
||||
private final static Logger LOG = getLogger(TAG);
|
||||
|
||||
private final Executor ioExecutor;
|
||||
private final MailboxApi api;
|
||||
private final TransactionManager db;
|
||||
private final MailboxSettingsManager mailboxSettingsManager;
|
||||
private final MailboxPairingTaskFactory pairingTaskFactory;
|
||||
private final Clock clock;
|
||||
private final Object lock = new Object();
|
||||
|
||||
@Nullable
|
||||
@@ -32,11 +47,17 @@ class MailboxManagerImpl implements MailboxManager {
|
||||
@Inject
|
||||
MailboxManagerImpl(
|
||||
@IoExecutor Executor ioExecutor,
|
||||
MailboxApi api,
|
||||
TransactionManager db,
|
||||
MailboxSettingsManager mailboxSettingsManager,
|
||||
MailboxPairingTaskFactory pairingTaskFactory) {
|
||||
MailboxPairingTaskFactory pairingTaskFactory,
|
||||
Clock clock) {
|
||||
this.ioExecutor = ioExecutor;
|
||||
this.api = api;
|
||||
this.db = db;
|
||||
this.mailboxSettingsManager = mailboxSettingsManager;
|
||||
this.pairingTaskFactory = pairingTaskFactory;
|
||||
this.clock = clock;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -75,4 +96,39 @@ class MailboxManagerImpl implements MailboxManager {
|
||||
return created;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkConnection() {
|
||||
boolean success;
|
||||
try {
|
||||
MailboxProperties props = db.transactionWithNullableResult(true,
|
||||
mailboxSettingsManager::getOwnMailboxProperties);
|
||||
success = api.checkStatus(props);
|
||||
} catch (DbException e) {
|
||||
logException(LOG, WARNING, e);
|
||||
// we don't treat this is a failure to record
|
||||
return false;
|
||||
} catch (IOException | MailboxApi.ApiException e) {
|
||||
// we record this as a failure
|
||||
success = false;
|
||||
logException(LOG, WARNING, e);
|
||||
}
|
||||
try {
|
||||
recordCheckResult(success);
|
||||
} catch (DbException e) {
|
||||
logException(LOG, WARNING, e);
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
private void recordCheckResult(boolean success) throws DbException {
|
||||
long now = clock.currentTimeMillis();
|
||||
db.transaction(false, txn -> {
|
||||
if (success) {
|
||||
mailboxSettingsManager.recordSuccessfulConnection(txn, now);
|
||||
} else {
|
||||
mailboxSettingsManager.recordFailedConnectionAttempt(txn, now);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -158,6 +158,12 @@ class MailboxPropertyManagerImpl implements MailboxPropertyManager,
|
||||
BdfList body = clientHelper.getMessageAsList(txn, m.getId());
|
||||
MailboxPropertiesUpdate p = parseProperties(body);
|
||||
txn.attach(new RemoteMailboxPropertiesUpdateEvent(c, p));
|
||||
// Reset message retransmission timers for the contact. Avoiding
|
||||
// messages getting stranded:
|
||||
// - on our mailbox, if they now have a mailbox but didn't before
|
||||
// - on the contact's old mailbox, if they removed their mailbox
|
||||
// - on the contact's old mailbox, if they replaced their mailbox
|
||||
db.resetUnackedMessagesToSend(txn, c);
|
||||
} catch (FormatException e) {
|
||||
throw new InvalidMessageException(e);
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import dagger.Module;
|
||||
import dagger.Provides;
|
||||
|
||||
import static org.briarproject.bramble.api.plugin.TorConstants.CONNECT_TO_PROXY_TIMEOUT;
|
||||
import static org.briarproject.bramble.api.plugin.TorConstants.EXTRA_CONNECT_TIMEOUT;
|
||||
import static org.briarproject.bramble.api.plugin.TorConstants.EXTRA_SOCKET_TIMEOUT;
|
||||
|
||||
@Module
|
||||
@@ -20,6 +21,6 @@ public class SocksModule {
|
||||
InetSocketAddress proxy = new InetSocketAddress("127.0.0.1",
|
||||
torSocksPort);
|
||||
return new SocksSocketFactory(proxy, CONNECT_TO_PROXY_TIMEOUT,
|
||||
EXTRA_SOCKET_TIMEOUT);
|
||||
EXTRA_CONNECT_TIMEOUT, EXTRA_SOCKET_TIMEOUT);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,15 +26,18 @@ class SocksSocket extends Socket {
|
||||
"Address type not supported"
|
||||
};
|
||||
|
||||
@SuppressWarnings("MismatchedReadAndWriteOfArray")
|
||||
private static final byte[] UNSPECIFIED_ADDRESS = new byte[4];
|
||||
|
||||
private final SocketAddress proxy;
|
||||
private final int connectToProxyTimeout, extraSocketTimeout;
|
||||
private final int connectToProxyTimeout;
|
||||
private final int extraConnectTimeout, extraSocketTimeout;
|
||||
|
||||
SocksSocket(SocketAddress proxy, int connectToProxyTimeout,
|
||||
int extraSocketTimeout) {
|
||||
int extraConnectTimeout, int extraSocketTimeout) {
|
||||
this.proxy = proxy;
|
||||
this.connectToProxyTimeout = connectToProxyTimeout;
|
||||
this.extraConnectTimeout = extraConnectTimeout;
|
||||
this.extraSocketTimeout = extraSocketTimeout;
|
||||
}
|
||||
|
||||
@@ -66,7 +69,7 @@ class SocksSocket extends Socket {
|
||||
|
||||
// Use the supplied timeout temporarily, plus any configured extra
|
||||
int oldTimeout = getSoTimeout();
|
||||
setSoTimeout(timeout + extraSocketTimeout);
|
||||
setSoTimeout(timeout + extraConnectTimeout);
|
||||
|
||||
// Connect to the endpoint via the proxy
|
||||
sendConnectRequest(out, host, port);
|
||||
|
||||
@@ -11,18 +11,21 @@ import javax.net.SocketFactory;
|
||||
class SocksSocketFactory extends SocketFactory {
|
||||
|
||||
private final SocketAddress proxy;
|
||||
private final int connectToProxyTimeout, extraSocketTimeout;
|
||||
private final int connectToProxyTimeout;
|
||||
private final int extraConnectTimeout, extraSocketTimeout;
|
||||
|
||||
SocksSocketFactory(SocketAddress proxy, int connectToProxyTimeout,
|
||||
int extraSocketTimeout) {
|
||||
int extraConnectTimeout, int extraSocketTimeout) {
|
||||
this.proxy = proxy;
|
||||
this.connectToProxyTimeout = connectToProxyTimeout;
|
||||
this.extraConnectTimeout = extraConnectTimeout;
|
||||
this.extraSocketTimeout = extraSocketTimeout;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Socket createSocket() {
|
||||
return new SocksSocket(proxy, connectToProxyTimeout, extraSocketTimeout);
|
||||
return new SocksSocket(proxy, connectToProxyTimeout,
|
||||
extraConnectTimeout, extraSocketTimeout);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -281,6 +281,7 @@ public class MailboxPropertyManagerImplTest extends BrambleMockTestCase {
|
||||
public void testDoesNotDeleteAnythingWhenFirstUpdateIsDelivered()
|
||||
throws Exception {
|
||||
Transaction txn = new Transaction(null, false);
|
||||
Contact contact = getContact();
|
||||
GroupId contactGroupId = new GroupId(getRandomId());
|
||||
Message message = getMessage(contactGroupId);
|
||||
BdfList body = BdfList.of(1, propsDict);
|
||||
@@ -304,11 +305,13 @@ public class MailboxPropertyManagerImplTest extends BrambleMockTestCase {
|
||||
contactGroupId);
|
||||
will(returnValue(messageMetadata));
|
||||
oneOf(clientHelper).getContactId(txn, contactGroupId);
|
||||
will(returnValue(contact.getId()));
|
||||
oneOf(clientHelper).getMessageAsList(txn, message.getId());
|
||||
will(returnValue(body));
|
||||
oneOf(clientHelper).parseAndValidateMailboxPropertiesUpdate(
|
||||
propsDict);
|
||||
will(returnValue(props));
|
||||
oneOf(db).resetUnackedMessagesToSend(txn, contact.getId());
|
||||
}});
|
||||
|
||||
MailboxPropertyManagerImpl t = createInstance();
|
||||
@@ -321,6 +324,7 @@ public class MailboxPropertyManagerImplTest extends BrambleMockTestCase {
|
||||
public void testDeletesOlderUpdateWhenUpdateIsDelivered()
|
||||
throws Exception {
|
||||
Transaction txn = new Transaction(null, false);
|
||||
Contact contact = getContact();
|
||||
GroupId contactGroupId = new GroupId(getRandomId());
|
||||
Message message = getMessage(contactGroupId);
|
||||
BdfList body = BdfList.of(1, propsDict);
|
||||
@@ -352,11 +356,13 @@ public class MailboxPropertyManagerImplTest extends BrambleMockTestCase {
|
||||
oneOf(db).deleteMessage(txn, updateId);
|
||||
oneOf(db).deleteMessageMetadata(txn, updateId);
|
||||
oneOf(clientHelper).getContactId(txn, contactGroupId);
|
||||
will(returnValue(contact.getId()));
|
||||
oneOf(clientHelper).getMessageAsList(txn, message.getId());
|
||||
will(returnValue(body));
|
||||
oneOf(clientHelper).parseAndValidateMailboxPropertiesUpdate(
|
||||
propsDict);
|
||||
will(returnValue(props));
|
||||
oneOf(db).resetUnackedMessagesToSend(txn, contact.getId());
|
||||
}});
|
||||
|
||||
MailboxPropertyManagerImpl t = createInstance();
|
||||
|
||||
@@ -26,8 +26,8 @@ android {
|
||||
defaultConfig {
|
||||
minSdkVersion 16
|
||||
targetSdkVersion 30
|
||||
versionCode 10406
|
||||
versionName "1.4.6"
|
||||
versionCode 10407
|
||||
versionName "1.4.7"
|
||||
applicationId "org.briarproject.briar.android"
|
||||
|
||||
vectorDrawables.useSupportLibrary = true
|
||||
|
||||
@@ -21,6 +21,7 @@ import org.briarproject.bramble.api.lifecycle.LifecycleManager;
|
||||
import org.briarproject.bramble.api.lifecycle.LifecycleManager.StartResult;
|
||||
import org.briarproject.bramble.api.system.AndroidExecutor;
|
||||
import org.briarproject.bramble.api.system.AndroidWakeLockManager;
|
||||
import org.briarproject.bramble.api.system.Clock;
|
||||
import org.briarproject.briar.R;
|
||||
import org.briarproject.briar.android.logout.HideUiActivity;
|
||||
import org.briarproject.briar.api.android.AndroidNotificationManager;
|
||||
@@ -33,6 +34,8 @@ import java.util.logging.Logger;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import androidx.annotation.UiThread;
|
||||
|
||||
import static android.app.NotificationManager.IMPORTANCE_LOW;
|
||||
import static android.content.Intent.ACTION_SHUTDOWN;
|
||||
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK;
|
||||
@@ -48,6 +51,7 @@ import static java.util.logging.Level.WARNING;
|
||||
import static org.briarproject.bramble.api.lifecycle.LifecycleManager.StartResult.ALREADY_RUNNING;
|
||||
import static org.briarproject.bramble.api.lifecycle.LifecycleManager.StartResult.SUCCESS;
|
||||
import static org.briarproject.bramble.api.nullsafety.NullSafety.requireNonNull;
|
||||
import static org.briarproject.bramble.util.AndroidUtils.isUiThread;
|
||||
import static org.briarproject.briar.android.BriarApplication.ENTRY_ACTIVITY;
|
||||
import static org.briarproject.briar.api.android.AndroidNotificationManager.FAILURE_CHANNEL_ID;
|
||||
import static org.briarproject.briar.api.android.AndroidNotificationManager.ONGOING_CHANNEL_ID;
|
||||
@@ -66,6 +70,12 @@ public class BriarService extends Service {
|
||||
private static final Logger LOG =
|
||||
Logger.getLogger(BriarService.class.getName());
|
||||
|
||||
/**
|
||||
* Don't clear the Glide cache repeatedly if low memory warnings arrive
|
||||
* in quick succession.
|
||||
*/
|
||||
private static final long MIN_GLIDE_CACHE_CLEAR_INTERVAL_MS = 5000;
|
||||
|
||||
private final AtomicBoolean created = new AtomicBoolean(false);
|
||||
private final Binder binder = new BriarBinder();
|
||||
|
||||
@@ -87,7 +97,11 @@ public class BriarService extends Service {
|
||||
volatile LifecycleManager lifecycleManager;
|
||||
@Inject
|
||||
volatile AndroidExecutor androidExecutor;
|
||||
@Inject
|
||||
volatile Clock clock;
|
||||
|
||||
private volatile boolean started = false;
|
||||
private volatile long glideCacheCleared = 0;
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
@@ -221,6 +235,7 @@ public class BriarService extends Service {
|
||||
public void onLowMemory() {
|
||||
super.onLowMemory();
|
||||
LOG.warning("Memory is low");
|
||||
maybeClearGlideCache();
|
||||
// If we're not in the foreground, clear the UI to save memory
|
||||
if (app.isRunningInBackground()) hideUi();
|
||||
}
|
||||
@@ -238,13 +253,13 @@ public class BriarService extends Service {
|
||||
LOG.info("Trim memory: near end of LRU list");
|
||||
} else if (level == TRIM_MEMORY_RUNNING_MODERATE) {
|
||||
LOG.info("Trim memory: running moderately low");
|
||||
Glide.get(getApplicationContext()).clearMemory();
|
||||
maybeClearGlideCache();
|
||||
} else if (level == TRIM_MEMORY_RUNNING_LOW) {
|
||||
LOG.info("Trim memory: running low");
|
||||
// TODO investigate if we can clear Glide cache here as well
|
||||
maybeClearGlideCache();
|
||||
} else if (level == TRIM_MEMORY_RUNNING_CRITICAL) {
|
||||
LOG.warning("Trim memory: running critically low");
|
||||
// TODO investigate if we can clear Glide cache here as well
|
||||
maybeClearGlideCache();
|
||||
// If we're not in the foreground, clear the UI to save memory
|
||||
if (app.isRunningInBackground()) hideUi();
|
||||
} else if (LOG.isLoggable(INFO)) {
|
||||
@@ -252,6 +267,25 @@ public class BriarService extends Service {
|
||||
}
|
||||
}
|
||||
|
||||
private void maybeClearGlideCache() {
|
||||
if (isUiThread()) {
|
||||
maybeClearGlideCacheUiThread();
|
||||
} else {
|
||||
LOG.warning("Low memory callback was not called on main thread");
|
||||
androidExecutor.runOnUiThread(this::maybeClearGlideCacheUiThread);
|
||||
}
|
||||
}
|
||||
|
||||
@UiThread
|
||||
private void maybeClearGlideCacheUiThread() {
|
||||
long now = clock.currentTimeMillis();
|
||||
if (now - glideCacheCleared >= MIN_GLIDE_CACHE_CLEAR_INTERVAL_MS) {
|
||||
LOG.info("Clearing Glide cache");
|
||||
Glide.get(getApplicationContext()).clearMemory();
|
||||
glideCacheCleared = now;
|
||||
}
|
||||
}
|
||||
|
||||
private void hideUi() {
|
||||
Intent i = new Intent(this, HideUiActivity.class);
|
||||
i.addFlags(FLAG_ACTIVITY_NEW_TASK
|
||||
|
||||
@@ -26,6 +26,7 @@ import static org.briarproject.bramble.util.IoUtils.tryToClose;
|
||||
import static org.briarproject.bramble.util.LogUtils.logDuration;
|
||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
||||
import static org.briarproject.bramble.util.LogUtils.now;
|
||||
import static org.briarproject.briar.android.attachment.media.ImageCompressor.MIME_TYPE;
|
||||
|
||||
@NotNullByDefault
|
||||
class AttachmentCreationTask {
|
||||
@@ -100,14 +101,17 @@ class AttachmentCreationTask {
|
||||
if (!asList(getSupportedImageContentTypes()).contains(contentType)) {
|
||||
throw new UnsupportedMimeTypeException(contentType, uri);
|
||||
}
|
||||
InputStream is = contentResolver.openInputStream(uri);
|
||||
if (is == null) throw new IOException();
|
||||
is = imageCompressor
|
||||
.compressImage(is, contentType);
|
||||
InputStream is;
|
||||
try {
|
||||
is = contentResolver.openInputStream(uri);
|
||||
if (is == null) throw new IOException();
|
||||
} catch (SecurityException e) {
|
||||
throw new IOException(e);
|
||||
}
|
||||
is = imageCompressor.compressImage(is, contentType);
|
||||
long timestamp = System.currentTimeMillis();
|
||||
AttachmentHeader h = messagingManager
|
||||
.addLocalAttachment(groupId, timestamp,
|
||||
ImageCompressor.MIME_TYPE, is);
|
||||
AttachmentHeader h = messagingManager.addLocalAttachment(groupId,
|
||||
timestamp, MIME_TYPE, is);
|
||||
tryToClose(is, LOG, WARNING);
|
||||
logDuration(LOG, "Storing attachment", start);
|
||||
return h;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package org.briarproject.briar.android.conversation;
|
||||
|
||||
import android.content.ActivityNotFoundException;
|
||||
import android.content.DialogInterface.OnClickListener;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
@@ -263,7 +264,11 @@ public class ImageActivity extends BriarActivity
|
||||
if (SDK_INT >= 19) {
|
||||
String name = viewModel.getFileName() + "." +
|
||||
getVisibleAttachment().getExtension();
|
||||
launcher.launch(name);
|
||||
try {
|
||||
launcher.launch(name);
|
||||
} catch (ActivityNotFoundException e) {
|
||||
viewModel.onSaveImageError();
|
||||
}
|
||||
} else {
|
||||
viewModel.saveImage(getVisibleAttachment());
|
||||
}
|
||||
|
||||
@@ -180,12 +180,17 @@ public class ImageViewModel extends DbViewModel implements EventListener {
|
||||
@UiThread
|
||||
void saveImage(AttachmentItem attachment, @Nullable Uri uri) {
|
||||
if (uri == null) {
|
||||
saveState.setEvent(true);
|
||||
onSaveImageError();
|
||||
} else {
|
||||
saveImage(attachment, () -> getOutputStream(uri), null);
|
||||
}
|
||||
}
|
||||
|
||||
@UiThread
|
||||
void onSaveImageError() {
|
||||
saveState.setEvent(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the attachment on external storage,
|
||||
* assuming the permission was granted during install time.
|
||||
|
||||
@@ -47,11 +47,11 @@ public class MailboxActivity extends BriarActivity {
|
||||
setContentView(R.layout.activity_mailbox);
|
||||
|
||||
progressBar = findViewById(R.id.progressBar);
|
||||
if (viewModel.getState().getValue() == null) {
|
||||
if (viewModel.getPairingState().getValue() == null) {
|
||||
progressBar.setVisibility(VISIBLE);
|
||||
}
|
||||
|
||||
viewModel.getState().observeEvent(this, state -> {
|
||||
viewModel.getPairingState().observeEvent(this, state -> {
|
||||
if (state instanceof MailboxState.NotSetup) {
|
||||
onNotSetup();
|
||||
} else if (state instanceof MailboxState.ShowDownload) {
|
||||
@@ -67,7 +67,7 @@ public class MailboxActivity extends BriarActivity {
|
||||
} else if (state instanceof MailboxState.CameraError) {
|
||||
onCameraError();
|
||||
} else if (state instanceof MailboxState.IsPaired) {
|
||||
onIsPaired();
|
||||
onIsPaired(((MailboxState.IsPaired) state).isOnline);
|
||||
} else {
|
||||
throw new AssertionError("Unknown state: " + state);
|
||||
}
|
||||
@@ -85,7 +85,7 @@ public class MailboxActivity extends BriarActivity {
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
MailboxState s = viewModel.getState().getLastValue();
|
||||
MailboxState s = viewModel.getPairingState().getLastValue();
|
||||
if (s instanceof MailboxState.Pairing) {
|
||||
// don't go back in the flow if we are already pairing
|
||||
// with the mailbox. We provide a try-again button instead.
|
||||
@@ -181,10 +181,13 @@ public class MailboxActivity extends BriarActivity {
|
||||
showFragment(getSupportFragmentManager(), f, ErrorFragment.TAG);
|
||||
}
|
||||
|
||||
private void onIsPaired() {
|
||||
private void onIsPaired(boolean isOnline) {
|
||||
progressBar.setVisibility(INVISIBLE);
|
||||
showFragment(getSupportFragmentManager(), new MailboxStatusFragment(),
|
||||
MailboxStatusFragment.TAG, false);
|
||||
Fragment f = isOnline ?
|
||||
new MailboxStatusFragment() : new OfflineStatusFragment();
|
||||
String tag = isOnline ?
|
||||
MailboxStatusFragment.TAG : OfflineStatusFragment.TAG;
|
||||
showFragment(getSupportFragmentManager(), f, tag, false);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package org.briarproject.briar.android.mailbox;
|
||||
|
||||
import org.briarproject.bramble.api.mailbox.MailboxPairingState;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxStatus;
|
||||
|
||||
class MailboxState {
|
||||
|
||||
@@ -29,10 +28,10 @@ class MailboxState {
|
||||
}
|
||||
|
||||
static class IsPaired extends MailboxState {
|
||||
final MailboxStatus mailboxStatus;
|
||||
final boolean isOnline;
|
||||
|
||||
IsPaired(MailboxStatus mailboxStatus) {
|
||||
this.mailboxStatus = mailboxStatus;
|
||||
IsPaired(boolean isOnline) {
|
||||
this.isOnline = isOnline;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,37 +1,62 @@
|
||||
package org.briarproject.briar.android.mailbox;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.ColorStateList;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.Button;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.briarproject.bramble.api.mailbox.MailboxStatus;
|
||||
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
||||
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
||||
import org.briarproject.briar.R;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import androidx.annotation.ColorRes;
|
||||
import androidx.annotation.DrawableRes;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.UiThread;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentActivity;
|
||||
import androidx.lifecycle.ViewModelProvider;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
import static android.view.View.INVISIBLE;
|
||||
import static android.view.View.VISIBLE;
|
||||
import static androidx.core.content.ContextCompat.getColor;
|
||||
import static androidx.core.widget.ImageViewCompat.setImageTintList;
|
||||
import static androidx.transition.TransitionManager.beginDelayedTransition;
|
||||
import static org.briarproject.briar.android.AppModule.getAndroidComponent;
|
||||
import static org.briarproject.briar.android.util.UiUtils.MIN_DATE_RESOLUTION;
|
||||
import static org.briarproject.briar.android.util.UiUtils.formatDate;
|
||||
import static org.briarproject.briar.android.util.UiUtils.observeOnce;
|
||||
|
||||
@MethodsNotNullByDefault
|
||||
@ParametersNotNullByDefault
|
||||
public class MailboxStatusFragment extends Fragment {
|
||||
|
||||
static final String TAG = MailboxStatusFragment.class.getName();
|
||||
private static final int NUM_FAILURES = 4;
|
||||
|
||||
@Inject
|
||||
ViewModelProvider.Factory viewModelFactory;
|
||||
|
||||
private MailboxViewModel viewModel;
|
||||
private final Handler handler = new Handler(Looper.getMainLooper());
|
||||
@Nullable // UiThread
|
||||
private Runnable refresher = null;
|
||||
|
||||
private ImageView imageView;
|
||||
private TextView statusTitleView;
|
||||
private TextView statusInfoView;
|
||||
|
||||
@Override
|
||||
public void onAttach(Context context) {
|
||||
@@ -54,11 +79,72 @@ public class MailboxStatusFragment extends Fragment {
|
||||
@Override
|
||||
public void onViewCreated(View v, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(v, savedInstanceState);
|
||||
MailboxState.IsPaired state =
|
||||
(MailboxState.IsPaired) viewModel.getState().getLastValue();
|
||||
requireNonNull(state); // TODO check assumption
|
||||
TextView statusInfoView = v.findViewById(R.id.statusInfoView);
|
||||
long lastSuccess = state.mailboxStatus.getTimeOfLastSuccess();
|
||||
|
||||
Button checkButton = v.findViewById(R.id.checkButton);
|
||||
ProgressBar checkProgress = v.findViewById(R.id.checkProgress);
|
||||
checkButton.setOnClickListener(view -> {
|
||||
beginDelayedTransition((ViewGroup) v);
|
||||
checkButton.setVisibility(INVISIBLE);
|
||||
checkProgress.setVisibility(VISIBLE);
|
||||
observeOnce(viewModel.checkConnection(), this, result -> {
|
||||
beginDelayedTransition((ViewGroup) v);
|
||||
checkButton.setVisibility(VISIBLE);
|
||||
checkProgress.setVisibility(INVISIBLE);
|
||||
});
|
||||
});
|
||||
|
||||
imageView = v.findViewById(R.id.imageView);
|
||||
statusTitleView = v.findViewById(R.id.statusTitleView);
|
||||
statusInfoView = v.findViewById(R.id.statusInfoView);
|
||||
viewModel.getStatus()
|
||||
.observe(getViewLifecycleOwner(), this::onMailboxStateChanged);
|
||||
|
||||
// TODO
|
||||
// * Implement UI for warning user when mailbox is unreachable #2175
|
||||
// * add "Unlink" button confirmation dialog and functionality #2173
|
||||
Button unlinkButton = v.findViewById(R.id.unlinkButton);
|
||||
unlinkButton.setOnClickListener(view -> Toast.makeText(requireContext(),
|
||||
"NOT IMPLEMENTED", Toast.LENGTH_SHORT).show());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
requireActivity().setTitle(R.string.mailbox_status_title);
|
||||
refresher = this::refreshLastConnection;
|
||||
handler.postDelayed(refresher, MIN_DATE_RESOLUTION);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStop() {
|
||||
super.onStop();
|
||||
handler.removeCallbacks(refresher);
|
||||
refresher = null;
|
||||
}
|
||||
|
||||
private void onMailboxStateChanged(MailboxStatus status) {
|
||||
@ColorRes int tintRes;
|
||||
@DrawableRes int iconRes;
|
||||
String title;
|
||||
if (status.getAttemptsSinceSuccess() == 0) {
|
||||
iconRes = R.drawable.ic_check_circle_outline;
|
||||
title = getString(R.string.mailbox_status_connected_title);
|
||||
tintRes = R.color.briar_brand_green;
|
||||
} else if (status.getAttemptsSinceSuccess() < NUM_FAILURES) {
|
||||
iconRes = R.drawable.ic_help_outline_white;
|
||||
title = getString(R.string.mailbox_status_problem_title);
|
||||
tintRes = R.color.briar_orange_500;
|
||||
} else {
|
||||
tintRes = R.color.briar_red_500;
|
||||
title = getString(R.string.mailbox_status_failure_title);
|
||||
iconRes = R.drawable.alerts_and_states_error;
|
||||
}
|
||||
imageView.setImageResource(iconRes);
|
||||
int color = getColor(requireContext(), tintRes);
|
||||
setImageTintList(imageView, ColorStateList.valueOf(color));
|
||||
statusTitleView.setText(title);
|
||||
|
||||
long lastSuccess = status.getTimeOfLastSuccess();
|
||||
String lastConnectionText;
|
||||
if (lastSuccess < 0) {
|
||||
lastConnectionText =
|
||||
@@ -66,21 +152,19 @@ public class MailboxStatusFragment extends Fragment {
|
||||
} else {
|
||||
lastConnectionText = formatDate(requireContext(), lastSuccess);
|
||||
}
|
||||
String statusInfoText = getString(
|
||||
R.string.mailbox_status_connected_info, lastConnectionText);
|
||||
String statusInfoText =
|
||||
getString(R.string.mailbox_status_connected_info,
|
||||
lastConnectionText);
|
||||
statusInfoView.setText(statusInfoText);
|
||||
// TODO
|
||||
// * react to status changes
|
||||
// * detect problems and show them
|
||||
// * update connection time periodically like conversation timestamps
|
||||
// * add "Check connection" button
|
||||
// * add "Unlink" button with confirmation dialog
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
requireActivity().setTitle(R.string.mailbox_status_title);
|
||||
@UiThread
|
||||
private void refreshLastConnection() {
|
||||
MailboxStatus status = viewModel.getStatus().getValue();
|
||||
if (status != null) onMailboxStateChanged(status);
|
||||
if (refresher != null) {
|
||||
handler.postDelayed(refresher, MIN_DATE_RESOLUTION);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -7,16 +7,22 @@ import com.google.zxing.Result;
|
||||
import org.briarproject.bramble.api.Consumer;
|
||||
import org.briarproject.bramble.api.db.DatabaseExecutor;
|
||||
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.lifecycle.IoExecutor;
|
||||
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxManager;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxPairingState;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxPairingTask;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxStatus;
|
||||
import org.briarproject.bramble.api.mailbox.OwnMailboxConnectionStatusEvent;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.bramble.api.plugin.Plugin;
|
||||
import org.briarproject.bramble.api.plugin.PluginManager;
|
||||
import org.briarproject.bramble.api.plugin.TorConstants;
|
||||
import org.briarproject.bramble.api.plugin.TransportId;
|
||||
import org.briarproject.bramble.api.plugin.event.TransportInactiveEvent;
|
||||
import org.briarproject.bramble.api.system.AndroidExecutor;
|
||||
import org.briarproject.briar.android.mailbox.MailboxState.NotSetup;
|
||||
import org.briarproject.briar.android.qrcode.QrCodeDecoder;
|
||||
@@ -32,6 +38,8 @@ import javax.inject.Inject;
|
||||
import androidx.annotation.AnyThread;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.UiThread;
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.lifecycle.MutableLiveData;
|
||||
|
||||
import static java.util.logging.Level.INFO;
|
||||
import static java.util.logging.Logger.getLogger;
|
||||
@@ -39,17 +47,22 @@ import static org.briarproject.bramble.api.plugin.Plugin.State.ACTIVE;
|
||||
|
||||
@NotNullByDefault
|
||||
class MailboxViewModel extends DbViewModel
|
||||
implements QrCodeDecoder.ResultCallback, Consumer<MailboxPairingState> {
|
||||
implements QrCodeDecoder.ResultCallback, Consumer<MailboxPairingState>,
|
||||
EventListener {
|
||||
|
||||
private static final Logger LOG =
|
||||
getLogger(MailboxViewModel.class.getName());
|
||||
|
||||
private final EventBus eventBus;
|
||||
private final Executor ioExecutor;
|
||||
private final QrCodeDecoder qrCodeDecoder;
|
||||
private final PluginManager pluginManager;
|
||||
private final MailboxManager mailboxManager;
|
||||
|
||||
private final MutableLiveEvent<MailboxState> state =
|
||||
private final MutableLiveEvent<MailboxState> pairingState =
|
||||
new MutableLiveEvent<>();
|
||||
private final MutableLiveData<MailboxStatus> status =
|
||||
new MutableLiveData<>();
|
||||
@Nullable
|
||||
private MailboxPairingTask pairingTask = null;
|
||||
|
||||
@@ -60,19 +73,24 @@ class MailboxViewModel extends DbViewModel
|
||||
LifecycleManager lifecycleManager,
|
||||
TransactionManager db,
|
||||
AndroidExecutor androidExecutor,
|
||||
EventBus eventBus,
|
||||
@IoExecutor Executor ioExecutor,
|
||||
PluginManager pluginManager,
|
||||
MailboxManager mailboxManager) {
|
||||
super(app, dbExecutor, lifecycleManager, db, androidExecutor);
|
||||
this.eventBus = eventBus;
|
||||
this.ioExecutor = ioExecutor;
|
||||
this.pluginManager = pluginManager;
|
||||
this.mailboxManager = mailboxManager;
|
||||
qrCodeDecoder = new QrCodeDecoder(androidExecutor, ioExecutor, this);
|
||||
eventBus.addListener(this);
|
||||
checkIfSetup();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCleared() {
|
||||
super.onCleared();
|
||||
eventBus.removeListener(this);
|
||||
MailboxPairingTask task = pairingTask;
|
||||
if (task != null) {
|
||||
task.removeObserver(this);
|
||||
@@ -89,9 +107,11 @@ class MailboxViewModel extends DbViewModel
|
||||
if (isPaired) {
|
||||
MailboxStatus mailboxStatus =
|
||||
mailboxManager.getMailboxStatus(txn);
|
||||
state.postEvent(new MailboxState.IsPaired(mailboxStatus));
|
||||
boolean isOnline = isTorActive();
|
||||
pairingState.postEvent(new MailboxState.IsPaired(isOnline));
|
||||
status.postValue(mailboxStatus);
|
||||
} else {
|
||||
state.postEvent(new NotSetup());
|
||||
pairingState.postEvent(new NotSetup());
|
||||
}
|
||||
}, this::handleException);
|
||||
} else {
|
||||
@@ -100,18 +120,37 @@ class MailboxViewModel extends DbViewModel
|
||||
}
|
||||
}
|
||||
|
||||
@UiThread
|
||||
@Override
|
||||
public void eventOccurred(Event e) {
|
||||
if (e instanceof OwnMailboxConnectionStatusEvent) {
|
||||
MailboxStatus status =
|
||||
((OwnMailboxConnectionStatusEvent) e).getStatus();
|
||||
this.status.setValue(status);
|
||||
} else if (e instanceof TransportInactiveEvent) {
|
||||
TransportId id = ((TransportInactiveEvent) e).getTransportId();
|
||||
if (!TorConstants.ID.equals(id)) return;
|
||||
MailboxState lastState = pairingState.getLastValue();
|
||||
if (lastState instanceof MailboxState.IsPaired) {
|
||||
pairingState.setEvent(new MailboxState.IsPaired(false));
|
||||
} else if (lastState != null) {
|
||||
pairingState.setEvent(new MailboxState.OfflineWhenPairing());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@UiThread
|
||||
void onScanButtonClicked() {
|
||||
if (isTorActive()) {
|
||||
state.setEvent(new MailboxState.ScanningQrCode());
|
||||
pairingState.setEvent(new MailboxState.ScanningQrCode());
|
||||
} else {
|
||||
state.setEvent(new MailboxState.OfflineWhenPairing());
|
||||
pairingState.setEvent(new MailboxState.OfflineWhenPairing());
|
||||
}
|
||||
}
|
||||
|
||||
@UiThread
|
||||
void onCameraError() {
|
||||
state.setEvent(new MailboxState.CameraError());
|
||||
pairingState.setEvent(new MailboxState.CameraError());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -127,7 +166,7 @@ class MailboxViewModel extends DbViewModel
|
||||
pairingTask = mailboxManager.startPairingTask(qrCodePayload);
|
||||
pairingTask.addObserver(this);
|
||||
} else {
|
||||
state.postEvent(new MailboxState.OfflineWhenPairing());
|
||||
pairingState.postEvent(new MailboxState.OfflineWhenPairing());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -138,7 +177,7 @@ class MailboxViewModel extends DbViewModel
|
||||
LOG.info("New pairing state: " +
|
||||
mailboxPairingState.getClass().getSimpleName());
|
||||
}
|
||||
state.setEvent(new MailboxState.Pairing(mailboxPairingState));
|
||||
pairingState.setEvent(new MailboxState.Pairing(mailboxPairingState));
|
||||
}
|
||||
|
||||
private boolean isTorActive() {
|
||||
@@ -148,7 +187,7 @@ class MailboxViewModel extends DbViewModel
|
||||
|
||||
@UiThread
|
||||
void showDownloadFragment() {
|
||||
state.setEvent(new MailboxState.ShowDownload());
|
||||
pairingState.setEvent(new MailboxState.ShowDownload());
|
||||
}
|
||||
|
||||
@UiThread
|
||||
@@ -157,7 +196,37 @@ class MailboxViewModel extends DbViewModel
|
||||
}
|
||||
|
||||
@UiThread
|
||||
LiveEvent<MailboxState> getState() {
|
||||
return state;
|
||||
void checkIfOnlineWhenPaired() {
|
||||
boolean isOnline = isTorActive();
|
||||
pairingState.setEvent(new MailboxState.IsPaired(isOnline));
|
||||
}
|
||||
|
||||
LiveData<Boolean> checkConnection() {
|
||||
MutableLiveData<Boolean> liveData = new MutableLiveData<>();
|
||||
ioExecutor.execute(() -> {
|
||||
boolean success = mailboxManager.checkConnection();
|
||||
if (LOG.isLoggable(INFO)) {
|
||||
LOG.info("Got result from connection check: " + success);
|
||||
}
|
||||
liveData.postValue(success);
|
||||
if (!success) { // force failure screen
|
||||
MailboxStatus lastStatus = status.getValue();
|
||||
long lastSuccess = lastStatus == null ?
|
||||
-1 : lastStatus.getTimeOfLastSuccess();
|
||||
long now = System.currentTimeMillis();
|
||||
status.postValue(new MailboxStatus(now, lastSuccess, 999));
|
||||
}
|
||||
});
|
||||
return liveData;
|
||||
}
|
||||
|
||||
@UiThread
|
||||
LiveEvent<MailboxState> getPairingState() {
|
||||
return pairingState;
|
||||
}
|
||||
|
||||
@UiThread
|
||||
LiveData<MailboxStatus> getStatus() {
|
||||
return status;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,10 +33,9 @@ public class OfflineFragment extends Fragment {
|
||||
@Inject
|
||||
ViewModelProvider.Factory viewModelFactory;
|
||||
|
||||
private MailboxViewModel viewModel;
|
||||
protected MailboxViewModel viewModel;
|
||||
|
||||
private NestedScrollView scrollView;
|
||||
protected Button buttonView;
|
||||
|
||||
@Override
|
||||
public void onAttach(Context context) {
|
||||
@@ -61,8 +60,8 @@ public class OfflineFragment extends Fragment {
|
||||
Intent i = new Intent(requireContext(), TransportsActivity.class);
|
||||
startActivity(i);
|
||||
});
|
||||
buttonView = v.findViewById(R.id.button);
|
||||
buttonView.setOnClickListener(view -> viewModel.showDownloadFragment());
|
||||
Button buttonView = v.findViewById(R.id.button);
|
||||
buttonView.setOnClickListener(view -> onTryAgainClicked());
|
||||
|
||||
return v;
|
||||
}
|
||||
@@ -74,4 +73,8 @@ public class OfflineFragment extends Fragment {
|
||||
scrollView.post(() -> scrollView.fullScroll(FOCUS_DOWN));
|
||||
}
|
||||
|
||||
protected void onTryAgainClicked() {
|
||||
viewModel.showDownloadFragment();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
package org.briarproject.briar.android.mailbox;
|
||||
|
||||
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
||||
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
||||
|
||||
@MethodsNotNullByDefault
|
||||
@ParametersNotNullByDefault
|
||||
public class OfflineStatusFragment extends OfflineFragment {
|
||||
|
||||
public static final String TAG = OfflineStatusFragment.class.getName();
|
||||
|
||||
@Override
|
||||
protected void onTryAgainClicked() {
|
||||
viewModel.checkIfOnlineWhenPaired();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -103,7 +103,8 @@ public class ReceiveFragment extends Fragment {
|
||||
// to prevent duplicates on the back stack.
|
||||
getParentFragmentManager().popBackStack();
|
||||
// Start again (picks up existing task or allows to start a new one)
|
||||
viewModel.startReceiveData();
|
||||
// unless the activity was recreated after the app was killed
|
||||
if (viewModel.isAccountSignedIn()) viewModel.startReceiveData();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import android.app.Application;
|
||||
import android.net.Uri;
|
||||
|
||||
import org.briarproject.bramble.api.Consumer;
|
||||
import org.briarproject.bramble.api.account.AccountManager;
|
||||
import org.briarproject.bramble.api.contact.ContactId;
|
||||
import org.briarproject.bramble.api.db.DatabaseExecutor;
|
||||
import org.briarproject.bramble.api.db.DbException;
|
||||
@@ -32,6 +33,7 @@ import androidx.lifecycle.MutableLiveData;
|
||||
|
||||
import static java.util.Locale.US;
|
||||
import static java.util.Objects.requireNonNull;
|
||||
import static org.briarproject.bramble.api.lifecycle.LifecycleManager.LifecycleState.STARTING_SERVICES;
|
||||
import static org.briarproject.bramble.api.plugin.file.RemovableDriveConstants.PROP_URI;
|
||||
|
||||
@UiThread
|
||||
@@ -40,6 +42,7 @@ class RemovableDriveViewModel extends DbViewModel {
|
||||
|
||||
enum Action {SEND, RECEIVE}
|
||||
|
||||
private final AccountManager accountManager;
|
||||
private final RemovableDriveManager manager;
|
||||
|
||||
private final MutableLiveEvent<Action> action = new MutableLiveEvent<>();
|
||||
@@ -61,8 +64,10 @@ class RemovableDriveViewModel extends DbViewModel {
|
||||
LifecycleManager lifecycleManager,
|
||||
TransactionManager db,
|
||||
AndroidExecutor androidExecutor,
|
||||
AccountManager accountManager,
|
||||
RemovableDriveManager removableDriveManager) {
|
||||
super(app, dbExecutor, lifecycleManager, db, androidExecutor);
|
||||
this.accountManager = accountManager;
|
||||
this.manager = removableDriveManager;
|
||||
}
|
||||
|
||||
@@ -193,4 +198,8 @@ class RemovableDriveViewModel extends DbViewModel {
|
||||
return state;
|
||||
}
|
||||
|
||||
boolean isAccountSignedIn() {
|
||||
return accountManager.hasDatabaseKey() &&
|
||||
lifecycleManager.getLifecycleState().isAfter(STARTING_SERVICES);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -109,7 +109,8 @@ public class SendFragment extends Fragment {
|
||||
// to prevent duplicates on the back stack.
|
||||
getParentFragmentManager().popBackStack();
|
||||
// Start again (picks up existing task or allows to start a new one)
|
||||
viewModel.startSendData();
|
||||
// unless the activity was recreated after the app was killed
|
||||
if (viewModel.isAccountSignedIn()) viewModel.startSendData();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -198,38 +198,47 @@ class BriarReportCollector {
|
||||
private ReportItem getConnectivity() {
|
||||
MultiReportInfo connectivityInfo = new MultiReportInfo();
|
||||
|
||||
// Is mobile data available?
|
||||
ConnectivityManager cm = requireNonNull(
|
||||
getSystemService(ctx, ConnectivityManager.class));
|
||||
NetworkInfo mobile = cm.getNetworkInfo(TYPE_MOBILE);
|
||||
boolean mobileAvailable = mobile != null && mobile.isAvailable();
|
||||
connectivityInfo.add("MobileDataAvailable", mobileAvailable);
|
||||
|
||||
// Is mobile data enabled?
|
||||
boolean mobileEnabled = false;
|
||||
// https://issuetracker.google.com/issues/175055271
|
||||
try {
|
||||
Class<?> clazz = Class.forName(cm.getClass().getName());
|
||||
Method method = clazz.getDeclaredMethod("getMobileDataEnabled");
|
||||
method.setAccessible(true);
|
||||
mobileEnabled = (Boolean) requireNonNull(method.invoke(cm));
|
||||
} catch (ClassNotFoundException
|
||||
| NoSuchMethodException
|
||||
| IllegalArgumentException
|
||||
| InvocationTargetException
|
||||
| IllegalAccessException e) {
|
||||
connectivityInfo
|
||||
.add("MobileDataReflectionException", e.toString());
|
||||
// Is mobile data available?
|
||||
ConnectivityManager cm = requireNonNull(
|
||||
getSystemService(ctx, ConnectivityManager.class));
|
||||
NetworkInfo mobile = cm.getNetworkInfo(TYPE_MOBILE);
|
||||
boolean mobileAvailable = mobile != null && mobile.isAvailable();
|
||||
connectivityInfo.add("MobileDataAvailable", mobileAvailable);
|
||||
|
||||
// Is mobile data enabled?
|
||||
boolean mobileEnabled = false;
|
||||
try {
|
||||
Class<?> clazz = Class.forName(cm.getClass().getName());
|
||||
Method method = clazz.getDeclaredMethod("getMobileDataEnabled");
|
||||
method.setAccessible(true);
|
||||
mobileEnabled = (Boolean) requireNonNull(method.invoke(cm));
|
||||
} catch (ClassNotFoundException
|
||||
| NoSuchMethodException
|
||||
| IllegalArgumentException
|
||||
| InvocationTargetException
|
||||
| IllegalAccessException e) {
|
||||
connectivityInfo
|
||||
.add("MobileDataReflectionException", e.toString());
|
||||
}
|
||||
connectivityInfo.add("MobileDataEnabled", mobileEnabled);
|
||||
|
||||
// Is mobile data connected ?
|
||||
boolean mobileConnected = mobile != null && mobile.isConnected();
|
||||
connectivityInfo.add("MobileDataConnected", mobileConnected);
|
||||
|
||||
// Is wifi available?
|
||||
NetworkInfo wifi = cm.getNetworkInfo(TYPE_WIFI);
|
||||
boolean wifiAvailable = wifi != null && wifi.isAvailable();
|
||||
connectivityInfo.add("WifiAvailable", wifiAvailable);
|
||||
|
||||
// Is wifi connected?
|
||||
boolean wifiConnected = wifi != null && wifi.isConnected();
|
||||
connectivityInfo.add("WifiConnected", wifiConnected);
|
||||
} catch (SecurityException e) {
|
||||
connectivityInfo.add("ConnectivityManagerException", e.toString());
|
||||
}
|
||||
connectivityInfo.add("MobileDataEnabled", mobileEnabled);
|
||||
|
||||
// Is mobile data connected ?
|
||||
boolean mobileConnected = mobile != null && mobile.isConnected();
|
||||
connectivityInfo.add("MobileDataConnected", mobileConnected);
|
||||
|
||||
// Is wifi available?
|
||||
NetworkInfo wifi = cm.getNetworkInfo(TYPE_WIFI);
|
||||
boolean wifiAvailable = wifi != null && wifi.isAvailable();
|
||||
connectivityInfo.add("WifiAvailable", wifiAvailable);
|
||||
|
||||
// Is wifi enabled?
|
||||
WifiManager wm = getSystemService(ctx, WifiManager.class);
|
||||
@@ -237,10 +246,6 @@ class BriarReportCollector {
|
||||
wm.getWifiState() == WIFI_STATE_ENABLED;
|
||||
connectivityInfo.add("WifiEnabled", wifiEnabled);
|
||||
|
||||
// Is wifi connected?
|
||||
boolean wifiConnected = wifi != null && wifi.isConnected();
|
||||
connectivityInfo.add("WifiConnected", wifiConnected);
|
||||
|
||||
// Is wifi direct supported?
|
||||
boolean wifiDirect = ctx.getSystemService(WIFI_P2P_SERVICE) != null;
|
||||
connectivityInfo.add("WiFiDirectSupported", wifiDirect);
|
||||
|
||||
@@ -231,9 +231,14 @@ class SettingsViewModel extends DbViewModel implements EventListener {
|
||||
if (!asList(getSupportedImageContentTypes()).contains(contentType)) {
|
||||
throw new UnsupportedMimeTypeException(contentType, uri);
|
||||
}
|
||||
InputStream is = contentResolver.openInputStream(uri);
|
||||
if (is == null) throw new IOException(
|
||||
"ContentResolver returned null when opening InputStream");
|
||||
InputStream is;
|
||||
try {
|
||||
is = contentResolver.openInputStream(uri);
|
||||
if (is == null) throw new IOException(
|
||||
"ContentResolver returned null when opening InputStream");
|
||||
} catch (SecurityException e) {
|
||||
throw new IOException(e);
|
||||
}
|
||||
InputStream compressed = imageCompressor.compressImage(is, contentType);
|
||||
|
||||
runOnDbThread(() -> {
|
||||
|
||||
@@ -44,7 +44,7 @@ public abstract class DbViewModel extends AndroidViewModel {
|
||||
|
||||
@DatabaseExecutor
|
||||
private final Executor dbExecutor;
|
||||
private final LifecycleManager lifecycleManager;
|
||||
protected final LifecycleManager lifecycleManager;
|
||||
private final TransactionManager db;
|
||||
protected final AndroidExecutor androidExecutor;
|
||||
|
||||
|
||||
@@ -10,30 +10,55 @@
|
||||
android:id="@+id/imageView"
|
||||
android:layout_width="32dp"
|
||||
android:layout_height="32dp"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginLeft="16dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@+id/statusTitleView"
|
||||
android:layout_marginHorizontal="16dp"
|
||||
app:layout_constraintBottom_toTopOf="@+id/statusTitleView"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.5"
|
||||
app:layout_constraintHorizontal_chainStyle="packed"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintVertical_bias="0.25"
|
||||
app:layout_constraintVertical_chainStyle="packed"
|
||||
app:srcCompat="@drawable/ic_check_circle_outline"
|
||||
app:tint="@color/briar_brand_green"
|
||||
tools:ignore="ContentDescription" />
|
||||
tools:ignore="ContentDescription"
|
||||
tools:srcCompat="@drawable/ic_help_outline_white"
|
||||
tools:tint="@color/briar_orange_500" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/statusTitleView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="16dp"
|
||||
android:text="@string/mailbox_status_connected_title"
|
||||
android:layout_margin="16dp"
|
||||
android:gravity="center"
|
||||
android:textAppearance="@style/TextAppearance.MaterialComponents.Headline6"
|
||||
app:layout_constrainedWidth="true"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/imageView"
|
||||
app:layout_constraintBottom_toTopOf="@+id/checkButton"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@+id/imageView"
|
||||
app:layout_constraintTop_toTopOf="@+id/imageView" />
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/imageView"
|
||||
tools:text="@string/mailbox_status_problem_title" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/checkButton"
|
||||
style="@style/BriarButtonFlat.Neutral"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="16dp"
|
||||
android:text="@string/mailbox_status_check_button"
|
||||
app:layout_constraintBottom_toTopOf="@+id/statusInfoView"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/statusTitleView" />
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/checkProgress"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="invisible"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/checkButton"
|
||||
app:layout_constraintEnd_toEndOf="@+id/checkButton"
|
||||
app:layout_constraintStart_toStartOf="@+id/checkButton"
|
||||
app:layout_constraintTop_toTopOf="@+id/checkButton"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/statusInfoView"
|
||||
@@ -41,11 +66,21 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="16dp"
|
||||
android:gravity="center"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintBottom_toTopOf="@+id/unlinkButton"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/statusTitleView"
|
||||
app:layout_constraintVertical_bias="0.0"
|
||||
app:layout_constraintTop_toBottomOf="@+id/checkButton"
|
||||
tools:text="@string/mailbox_status_connected_info" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/unlinkButton"
|
||||
style="@style/BriarButtonFlat.Negative"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="16dp"
|
||||
android:text="@string/mailbox_status_unlink_button"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
<string name="setup_huawei_app_launch_text">لطفا روی دکمه زیر زده، صفحهی \"راه اندازی برنامه\" را باز کرده و از این که برایر (Briar) بر روی \"مدیریت دستی\" تنظیم شده باشد، اطمینان حاصل کنید.</string>
|
||||
<string name="setup_huawei_app_launch_button">باز کردن تنظیمات باتری</string>
|
||||
<string name="setup_huawei_app_launch_help">اگر در صفحه \"راه اندازی برنامه\"، برایر (Briar) بر روی گزینه \"مدیریت دستی\" تنظیم نشده باشد، برنامه قادر به فعالیت در پسزمینه نخواهد بود.</string>
|
||||
<string name="setup_huawei_app_launch_error_toast">تنظیمات باتری باز نشد</string>
|
||||
<string name="setup_xiaomi_text">برای اجرا در پسزمینه، لازم است برایر (Briar) در لیست اپهای اخیر قفل شود.</string>
|
||||
<string name="setup_xiaomi_button">حفاظت از Briar (برایر)</string>
|
||||
<string name="setup_xiaomi_help">اگر برایر (Briar) در لیست اپهای اخیر قفل نشود، قادر با اجرا در پسزمینه نخواهد بود.</string>
|
||||
@@ -314,7 +315,7 @@
|
||||
<string name="pending_contact_updated_toast">مخاطب معلق به روز رسانی شد</string>
|
||||
<!--Introductions-->
|
||||
<string name="introduction_onboarding_title">معرفی مخاطبان</string>
|
||||
<string name="introduction_onboarding_text">شما می توانید مخاطبان خود را به یکدیگر معرفی کنید، در این صورت دیگر نیازی به دیدار حضوری برای اتصال روی Briar (برایر) نمی باشد.</string>
|
||||
<string name="introduction_onboarding_text">مخاطبین خود را به یکدیگر معرفی کنید تا بتوانند در Briar متصل شوند.</string>
|
||||
<string name="introduction_menu_item">معرفی کردن</string>
|
||||
<string name="introduction_activity_title">انتخاب مخاطب</string>
|
||||
<string name="introduction_not_possible">شما همین الان یک معرفی در مرحله انجام با این مخاطبان دارید. لطفا اجازه دهید تا این معرفی به پایان برسد. اگر شما و یا مخاطبانتان به ندرت آنلاین هستید، ممکن است کمی زمان ببرد.</string>
|
||||
@@ -623,10 +624,31 @@
|
||||
<string name="permission_camera_qr_denied_body">شما دسترسی به دوربین را رد کردهاید، اما اسکن کد QR مستلزم استفاده از دوربین است.\n\nلطفا دسترسی به دوربین را بدهید.</string>
|
||||
<string name="mailbox_setup_connecting">در حال اتصال...</string>
|
||||
<string name="mailbox_setup_qr_code_wrong_title">کد QR اشتباه است</string>
|
||||
<string name="mailbox_setup_qr_code_wrong_description">کد اسکن شده نامعتبر است. لطفا برنامه Briar Mailbox را در دستگاه صندوق پستی خود باز کنید و کد QR ارائه شده را اسکن کنید.</string>
|
||||
<string name="mailbox_setup_qr_code_wrong_description">کد اسکنشده نامعتبر است. لطفا برنامه Briar Mailbox را در دستگاه Mailbox خود باز کنید و کد QR ارائهشده را اسکن کنید.</string>
|
||||
<string name="mailbox_setup_already_paired_title">صندوقپستی قبلا متصل شده است</string>
|
||||
<string name="mailbox_setup_already_paired_description">اتصال صندوقپستی دستگاه دیگرتان را لغو کنید و دوباره امتحان کنید.</string>
|
||||
<string name="mailbox_setup_io_error_title">متصل نشد</string>
|
||||
<string name="mailbox_setup_io_error_description">مطمئن شوید که هر دو دستگاه به اینترنت متصل هستند و دوباره امتحان کنید.</string>
|
||||
<string name="mailbox_setup_assertion_error_title">خطای Mailbox</string>
|
||||
<string name="mailbox_setup_assertion_error_description">لطفا در صورت تداوم مشکل، بازخورد (با دادههای ناشناس) را از طریق برنامه Briar ارسال کنید.</string>
|
||||
<string name="mailbox_setup_camera_error_description">دسترسی به دوربین امکان پذیر نیست. لطفا بعد از راهاندازی مجدد دستگاه، دوباره امتحان کنید.</string>
|
||||
<string name="mailbox_setup_paired_title">متصل</string>
|
||||
<string name="mailbox_setup_paired_description">صندوق پستی شما با موفقیت با Briar وصل شد.\n
|
||||
\nصندوق پستی خود را به برق و Wi-Fi متصل نگه دارید تا همیشه آنلاین باشد.</string>
|
||||
<string name="tor_offline_title">آفلاین</string>
|
||||
<string name="tor_offline_description">مطمئن شوید که این دستگاه آنلاین است و اجازه اتصال به اینترنت را دارد.\n\nسپس منتظر بمانید تا نماد کره زمین در تنظیمات اتصال سبز شود.</string>
|
||||
<string name="tor_offline_description">مطمئن شوید که این دستگاه آنلاین است و اتصال به اینترنت مجاز است.\n
|
||||
\nپس از آن، منتظر بمانید تا نماد کره زمین در صفحه تنظیمات اتصال سبز شود.</string>
|
||||
<string name="tor_offline_button_check">تنظیمات اتصال را بررسی کنید</string>
|
||||
<string name="mailbox_status_title">وضعیت Mailbox</string>
|
||||
<string name="mailbox_status_connected_title">Mailbox در حال اجراست</string>
|
||||
<string name="mailbox_status_problem_title">ما در اتصال به صندوق پستی به مشکلی برخوردیم</string>
|
||||
<string name="mailbox_status_failure_title">صندوق پستی در دسترس نیست</string>
|
||||
<string name="mailbox_status_check_button">اتصال را بررسی کنید</string>
|
||||
<!--Example for string substitution: Last connection: 3min ago-->
|
||||
<string name="mailbox_status_connected_info">آخرین اتصال: %s</string>
|
||||
<!--Indicates that there never was a connection to the mailbox. Last connection: Never-->
|
||||
<string name="mailbox_status_connected_never">هیچ وقت</string>
|
||||
<string name="mailbox_status_unlink_button">لغو پیوند</string>
|
||||
<!--Conversation Settings-->
|
||||
<string name="disappearing_messages_title">پیامهای ناپدید شونده</string>
|
||||
<string name="disappearing_messages_explanation_long">روشن کردن این تنظیمات موجب خواهد شد تا پیامهای جدید
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
<string name="setup_huawei_app_launch_text">Bakstelėkite mygtuką žemiau, atverkite langą „Programų paleidimas (angl. App launch)“ ir įsitikinkite, kad Briar yra nustatyta į „Tvarkyti rankiniu būdu (angl. Manage manually)“.</string>
|
||||
<string name="setup_huawei_app_launch_button">Atverti akumuliatoriaus nustatymus</string>
|
||||
<string name="setup_huawei_app_launch_help">Jeigu „Programų paleidimo (angl. App launch)“ lange Briar nėra nustatyta į „Tvarkyti rankiniu būdu (angl. Manage manually)“, tuomet programėlė negalės veikti fone.</string>
|
||||
<string name="setup_huawei_app_launch_error_toast">Nepavyko atverti akumuliatoriaus nustatymų</string>
|
||||
<string name="setup_xiaomi_text">Tam, kad galėtų veikti fone, Briar turi būti prirakinta prie paskiausiųjų programėlių sąrašo.</string>
|
||||
<string name="setup_xiaomi_button">Apsaugoti Briar</string>
|
||||
<string name="setup_xiaomi_help">Jei Briar nebus prirakinta prie paskiausiųjų programėlių sąrašo, ji negalės veikti fone.</string>
|
||||
@@ -320,7 +321,6 @@
|
||||
<string name="pending_contact_updated_toast">Laukiantis adresatas atnaujintas</string>
|
||||
<!--Introductions-->
|
||||
<string name="introduction_onboarding_title">Supažindinkite savo adresatus</string>
|
||||
<string name="introduction_onboarding_text">Galite supažindinti savo adresatus vieną su kitu, kad jiems nereikėtų susitikti gyvai ir jie galėtų užmegzti ryšį, naudodami Briar.</string>
|
||||
<string name="introduction_menu_item">Supažindinti</string>
|
||||
<string name="introduction_activity_title">Pasirinkite adresatą</string>
|
||||
<string name="introduction_not_possible">Jau vyksta vienas supažindinimas tarp šių adresatų. Iš pradžių, leiskite, kad jis būtų užbaigtas. Jeigu jūs ar jūsų adresatai retai būna prisijungę, tuomet tai gali šiek tiek užtrukti.</string>
|
||||
@@ -604,8 +604,31 @@
|
||||
<string name="choose_ringtone_title">Pasirinkite skambėjimo melodiją</string>
|
||||
<string name="cannot_load_ringtone">Nepavyksta įkelti skambučio melodijos</string>
|
||||
<!--Mailbox-->
|
||||
<string name="mailbox_settings_title">Pašto dėžutė</string>
|
||||
<string name="mailbox_setup_title">Pašto dėžutės sąranka</string>
|
||||
<string name="mailbox_setup_intro">Pašto dėžutės leidžia adresatams siųsti jums žinutes, kol esate neprisijungę prie interneto. Žinutės bus siunčiamos į jūsų pašto dėžutę ir saugomos ten tol, kol prisijungsite prie interneto.\n
|
||||
\nGalite įsidiegti Briar pašto dėžutę laisvame atsarginiame įrenginyje. Palikite įrenginį prijungtą prie maitinimo šaltinio ir belaidžio (Wi-Fi) ryšio, kad jis būtų pastoviai prijungtas prie interneto.</string>
|
||||
<string name="mailbox_setup_download_link">Bendrinti atsisiuntimo nuorodą</string>
|
||||
<string name="mailbox_setup_button_scan">Skenuoti pašto dėžutės QR kodą</string>
|
||||
<string name="mailbox_setup_connecting">Jungiamasi…</string>
|
||||
<string name="mailbox_setup_qr_code_wrong_title">Neteisingas QR kodas</string>
|
||||
<string name="mailbox_setup_already_paired_title">Pašto dėžutė jau susieta</string>
|
||||
<string name="mailbox_setup_io_error_title">Nepavyko prisijungti</string>
|
||||
<string name="mailbox_setup_io_error_description">Įsitikinkite, kad abu įrenginiai yra prisijungę prie interneto ir bandykite dar kartą.</string>
|
||||
<string name="mailbox_setup_assertion_error_title">Pašto dėžutės klaida</string>
|
||||
<string name="mailbox_setup_assertion_error_description">Jei problema išlieka, išsiųskite atsiliepimą (su anoniminiais duomenimis) per Briar programėlę.</string>
|
||||
<string name="mailbox_setup_camera_error_description">Nepavyko gauti prieigos prie kameros. Bandykite dar kartą, galbūt po to, kai paleisite įrenginį iš naujo.</string>
|
||||
<string name="mailbox_setup_paired_title">Prisijungta</string>
|
||||
<string name="tor_offline_title">Nepasiekiama</string>
|
||||
<string name="tor_offline_button_check">Tikrinti ryšio nustatymus</string>
|
||||
<string name="mailbox_status_title">Pašto dėžutės būsena</string>
|
||||
<string name="mailbox_status_connected_title">Pašto dėžutė veikia</string>
|
||||
<string name="mailbox_status_check_button">Tikrinti ryšį</string>
|
||||
<!--Example for string substitution: Last connection: 3min ago-->
|
||||
<string name="mailbox_status_connected_info">Paskutinis prisijungimas: %s</string>
|
||||
<!--Indicates that there never was a connection to the mailbox. Last connection: Never-->
|
||||
<string name="mailbox_status_connected_never">Niekada</string>
|
||||
<string name="mailbox_status_unlink_button">Atsieti</string>
|
||||
<!--Conversation Settings-->
|
||||
<string name="disappearing_messages_title">Išnykstančios žinutės</string>
|
||||
<string name="disappearing_messages_explanation_long">Įjungus šį nustatymą, naujos žinutės
|
||||
|
||||
@@ -22,7 +22,15 @@
|
||||
<string name="setup_huawei_text">Toque no botão abaixo e verifique se o Briar está protegido na tela \"Aplicativos Protegidos\"</string>
|
||||
<string name="setup_huawei_button">Proteger o Briar</string>
|
||||
<string name="setup_huawei_help">Se o Briar não for adicionado à lista de aplicativos protegidos ele não poderá ser executado em segundo plano.</string>
|
||||
<string name="setup_huawei_app_launch_text">Por favor, toque no botão abaixo, abra a tela de \"Lançamento de apps\" e certifique que o Briar está definido para \"Gerenciar manualmente\".</string>
|
||||
<string name="setup_huawei_app_launch_button">Abrir Configurações de Bateria</string>
|
||||
<string name="setup_huawei_app_launch_help">Se o Briar não estiver definido como \"Gerenciar manualmente\" na tela de \"Lançamento de apps\", ele não poderá ser executado em segundo plano.</string>
|
||||
<string name="setup_huawei_app_launch_error_toast">Não pôde abrir configurações de bateria</string>
|
||||
<string name="setup_xiaomi_text">Para ser executado em segundo plano, o Briar deve estar fixado à lista de apps recentes.</string>
|
||||
<string name="setup_xiaomi_button">Proteger o Briar</string>
|
||||
<string name="setup_xiaomi_help">Se o Briar não estiver fixado na lista de aplicativos recentes, ele não será capaz de rodar em segundo plano.</string>
|
||||
<string name="setup_xiaomi_dialog_body_old">1. Abra a lista de aplicativos recentes (também chamada de alternador de aplicativos)\n\n2. Deslize para baixo na imagem do Briar para mostrar o ícone de cadeado\n\n3. Se o cadeado não estiver trancado, toque para trancá-lo</string>
|
||||
<string name="setup_xiaomi_dialog_body_new">1. Abra a lista de aplicativos recentes (também chamada de alternador de aplicativos)\n\n2. Pressione e segure na imagem do Briar para mostrar o ícone de cadeado\n\n3. Se o cadeado não estiver trancado, toque para trancá-lo</string>
|
||||
<string name="warning_dozed">%s não pôde ser executado em segundo plano</string>
|
||||
<!--Login-->
|
||||
<string name="enter_password">Senha</string>
|
||||
@@ -34,14 +42,25 @@
|
||||
<string name="dialog_title_lost_password">Perdeu a senha</string>
|
||||
<string name="dialog_message_lost_password">Sua conta Briar é armazenada em seu dispositivo e criptografada, não na Nuvem, assim não podemos recuperar a senha. Você quer deletar sua conta e começar de novo?\n\nAtenção: Isso irá apagar permanentemente suas identidades, contatos e mensagens</string>
|
||||
<string name="startup_failed_activity_title">Inicialização do Briar falhou</string>
|
||||
<string name="startup_failed_clock_error">Briar não iniciou porquê o horário do seu dispositivo está incorreto.\n\nPor favor, ajuste o horário do seu dispositivo e tente novamente.</string>
|
||||
<string name="startup_failed_db_error">Briar não foi capaz de abrir o banco de dados que contém sua conta, seus contatos e suas mensagens.\n\nPor favor, faça a atualização do aplicativo para a versão mais recente e tente novamente, ou crie uma nova conta clicando em \'Eu esqueci minha senha\'.</string>
|
||||
<string name="startup_failed_data_too_old_error">Sua conta foi criada em uma versão mais antiga desse aplicativo e não pode ser aberta com a versão atual.\n\nVocê deve reinstalar a versão antiga do aplicativo ou criar uma nova conta clicando em \'Eu esqueci minha senha\'.</string>
|
||||
<string name="startup_failed_data_too_new_error">Sua conta foi criada com uma versão mais recente deste aplicativo e não pode ser aberta com esta versão.\n\nPor favor, atualize para a versão mais recente e tente novamente. </string>
|
||||
<string name="startup_failed_service_error">Briar não foi capaz de inicializar um componente necessário.\n\nPor favor, atualize o aplicativo para uma versão mais recente e tente novamente.</string>
|
||||
<plurals name="expiry_warning">
|
||||
<item quantity="one">Esta é uma versão de teste do Briar. Sua conta irá expirar em %d dia e não poderá ser renovada.</item>
|
||||
<item quantity="other">Esta é uma versão de teste do Briar. Sua conta irá expirar em %d dias e não poderá ser renovada.</item>
|
||||
</plurals>
|
||||
<plurals name="old_android_expiry_warning">
|
||||
<item quantity="one">A versão 4 do Android já não é mais compatível. Briar deixará de funcionar em %s (dentro de %d dias). Por favor, instale Briar em um novo aparelho e crie uma nova conta.</item>
|
||||
<item quantity="other">A versão 4 do Android já não é mais compatível. Briar deixará de funcionar em %s (dentro de %d dias). Por favor, instale Briar em um novo dispositivo e crie uma nova conta.</item>
|
||||
</plurals>
|
||||
<string name="expiry_date_reached">Este software expirou.\nObrigado por testar!</string>
|
||||
<string name="download_briar">Para continuar usando o Briar, por favor baixe a versão mais recente.</string>
|
||||
<string name="create_new_account">Você precisará criar uma nova conta, mas poderá usar o mesmo nome de usuária.</string>
|
||||
<string name="create_new_account">Você precisará criar uma nova conta, mas poderá usar o mesmo apelido.</string>
|
||||
<string name="download_briar_button">Baixar a versão mais recente</string>
|
||||
<string name="old_android_expiry_date_reached">Briar já não é mais compatível com a versão 4 do Android.\nPor favor, instale Briar em um dispositivo com uma versão mais recente do Android. </string>
|
||||
<string name="old_android_delete_account">Você pode tocar no botão abaixo para deletar sua conta desse dispositivo.</string>
|
||||
<string name="delete_account_button">Apagar a Conta</string>
|
||||
<string name="startup_open_database">Descriptografando Banco de Dados...</string>
|
||||
<string name="startup_migrate_database">Atualizando Banco de Dados...</string>
|
||||
@@ -126,7 +145,7 @@
|
||||
<string name="allow">Permitir</string>
|
||||
<string name="open">Abrir</string>
|
||||
<string name="change">Alterar</string>
|
||||
<string name="start">Início</string>
|
||||
<string name="start">Iniciar</string>
|
||||
<string name="finish">Terminar</string>
|
||||
<string name="no_data">Nenhum dado</string>
|
||||
<string name="ellipsis">…</string>
|
||||
@@ -142,8 +161,10 @@
|
||||
<string name="no_contacts">Sem contatos para exibir</string>
|
||||
<string name="no_contacts_action">Toque no ícone + para adicionar um contato</string>
|
||||
<string name="date_no_private_messages">Sem Mensagens</string>
|
||||
<string name="no_private_messages">Nenhuma mensagem para ser exibida</string>
|
||||
<string name="no_private_messages">Sem mensagens para exibir</string>
|
||||
<string name="message_hint">Nova mensagem</string>
|
||||
<string name="message_hint_auto_delete">Nova mensagem efêmera</string>
|
||||
<string name="message_error">Erro ao enviar mensagem</string>
|
||||
<string name="image_caption_hint">Adicione uma legenda (opcional)</string>
|
||||
<string name="image_attach">Anexar imagem</string>
|
||||
<string name="image_attach_error">Não foi possível anexar imagem(s)</string>
|
||||
@@ -151,10 +172,32 @@
|
||||
<string name="image_attach_error_invalid_mime_type">Formato da imagem não suportado: %s</string>
|
||||
<string name="set_contact_alias">Alterar nome do contato</string>
|
||||
<string name="set_contact_alias_hint">Nome de contato</string>
|
||||
<string name="menu_item_disappearing_messages">Mensagens efêmeras</string>
|
||||
<!--The first placeholder will show a duration like "7 days". The second placeholder at the end will add "Tap to learn more."-->
|
||||
<string name="auto_delete_msg_you_enabled">Suas mensagens irão desaparecer após %1$s. %2$s</string>
|
||||
<!--The placeholder at the end will add "Tap to learn more."-->
|
||||
<string name="auto_delete_msg_you_disabled">Suas mensagens não vão desaparecer. %1$s</string>
|
||||
<!--The first placeholder will show a contact's name. The second placeholder will show a duration like "7 days". The third placeholder at the end will add "Tap to learn more."-->
|
||||
<string name="auto_delete_msg_contact_enabled">As mensagens de %1$s vão desaparecer em %2$s. %3$s</string>
|
||||
<plurals name="duration_minutes">
|
||||
<item quantity="one">%d minuto</item>
|
||||
<item quantity="other">%d minutos</item>
|
||||
</plurals>
|
||||
<plurals name="duration_hours">
|
||||
<item quantity="one">%d hora</item>
|
||||
<item quantity="other">%d horas</item>
|
||||
</plurals>
|
||||
<plurals name="duration_days">
|
||||
<item quantity="one">%d dia</item>
|
||||
<item quantity="other">%d dias</item>
|
||||
</plurals>
|
||||
<!--The first placeholder will show a contact's name. The second placeholder at the end will add "Tap to learn more."-->
|
||||
<string name="auto_delete_msg_contact_disabled">As mensagens de %1$s não vão desaparecer. %2$s</string>
|
||||
<string name="tap_to_learn_more">Toque para saber mais.</string>
|
||||
<string name="auto_delete_changed_warning_title">As mensagens efêmeras mudaram</string>
|
||||
<string name="auto_delete_changed_warning_message_enabled">Após você começar a escrever sua mensagem, as mensagens efêmeras foram ativadas.</string>
|
||||
<string name="auto_delete_changed_warning_message_disabled">Após você começar a escrever sua mensagem, as mensagens efêmeras foram desativadas.</string>
|
||||
<string name="auto_delete_changed_warning_send">Mandar mesmo assim</string>
|
||||
<string name="delete_all_messages">Deletar todas as mensagens</string>
|
||||
<string name="dialog_title_delete_all_messages">Confirmar exclusão de mensagem</string>
|
||||
<string name="dialog_message_delete_all_messages">Você tem certeza que deseja excluir todas as mensagens?</string>
|
||||
@@ -177,7 +220,7 @@
|
||||
<string name="save_image_success">Imagem salva</string>
|
||||
<string name="save_image_error">Não foi possível salvar imagem</string>
|
||||
<string name="dialog_title_no_image_support">Imagens indisponíveis</string>
|
||||
<string name="dialog_message_no_image_support">Seu contato do Briar ainda não suporta anexo de imagens. Quando ele atualizar o aplicativo, você verá um ícone diferente.</string>
|
||||
<string name="dialog_message_no_image_support">O Briar do seu contato ainda não suporta o anexo de imagens. Quando ele estiver atualizado, você verá um ícone diferente.</string>
|
||||
<string name="dialog_title_image_support">Você agora pode enviar imagens para esse contato</string>
|
||||
<string name="dialog_message_image_support">Toque nesse ícone para anexar imagens.</string>
|
||||
<string name="messaging_too_many_attachments_toast">Apenas as primeiras %d imagens serão enviadas</string>
|
||||
@@ -209,8 +252,8 @@
|
||||
<string name="add_contact_button">Adicionar contato</string>
|
||||
<string name="copy_button">Copiar</string>
|
||||
<string name="share_button">Compartilhar</string>
|
||||
<string name="send_link_title">Passem os links</string>
|
||||
<string name="add_contact_choose_nickname">Escolha um apelido</string>
|
||||
<string name="send_link_title">Troquem os links</string>
|
||||
<string name="add_contact_choose_nickname">Escolha um Apelido</string>
|
||||
<string name="add_contact_choose_a_nickname">Insira um apelido</string>
|
||||
<string name="nickname_intro">Dê um apelido ao contato. Somente você pode ver esse apelido. </string>
|
||||
<string name="your_link">Passe esse link para o contato que você quer adicionar</string>
|
||||
@@ -227,7 +270,7 @@
|
||||
<string name="dialog_title_remove_pending_contact">Confirme a remoção </string>
|
||||
<string name="dialog_message_remove_pending_contact">Esse contato ainda está sendo adicionado. Se você removê-lo agora, o mesmo não será adicionado</string>
|
||||
<string name="own_link_error">Insira o link do seu contato, não o seu próprio </string>
|
||||
<string name="nickname_missing">Por favor insira um apelido</string>
|
||||
<string name="nickname_missing">Por favor, insira um apelido</string>
|
||||
<string name="invalid_link">Link invalido</string>
|
||||
<string name="unsupported_link">Esse link veio de uma versão mais recente do Briar. Por favor atualize para a versão mais recente e tente novamente </string>
|
||||
<string name="intent_own_link">Você abriu seu próprio link. Utilize o link do contato que quer adicionar!</string>
|
||||
@@ -245,6 +288,7 @@
|
||||
<string name="duplicate_link_dialog_text_1">Você já tem um contato pendente com esse link: %s </string>
|
||||
<string name="duplicate_link_dialog_text_1_contact">Você já possui um contato com esse link: %s</string>
|
||||
<!--This is a question asking whether two nicknames refer to the same person-->
|
||||
<string name="duplicate_link_dialog_text_2">%1$s e %2$s são a mesma pessoa?</string>
|
||||
<!--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
|
||||
characters, please use "Yes" instead, and use "No" for the "Different Person" button-->
|
||||
@@ -253,10 +297,11 @@
|
||||
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-->
|
||||
<string name="different_person_button">Pessoa diferente</string>
|
||||
<string name="duplicate_link_dialog_text_3">%1$s e %2$s enviaram o mesmo link para você.\n\nUm deles pode estar tentando descobrir quem são seus contatos.\n\nNão diga a eles que você recebeu o mesmo link de outra pessoa.</string>
|
||||
<string name="pending_contact_updated_toast">Contato pendente atualizado</string>
|
||||
<!--Introductions-->
|
||||
<string name="introduction_onboarding_title">Apresente seus contatos</string>
|
||||
<string name="introduction_onboarding_text">Você pode apresentar seus contatos entre si, assim eles não precisam se encontrar pessoalmente para se comunicar no Briar.</string>
|
||||
<string name="introduction_onboarding_text">Apresente seus contatos entre si para que eles possam se conectar no Briar.</string>
|
||||
<string name="introduction_menu_item">Fazer apresentação</string>
|
||||
<string name="introduction_activity_title">Selecionar contato</string>
|
||||
<string name="introduction_not_possible">Você já tem uma apresentação em andamento com esses contatos. Por favor, permita que isso termine primeiro. Se você ou seus contatos raramente ficam on-line, isso pode levar algum tempo.</string>
|
||||
@@ -272,15 +317,23 @@
|
||||
<string name="introduction_response_accepted_sent">Você aceitou ser apresentado a %1$s.</string>
|
||||
<string name="introduction_response_accepted_sent_info">Antes de %1$sser adicionado nos seus contatos, ele também precisa aceitar a introdução. Isso pode demorar algum tempo.</string>
|
||||
<string name="introduction_response_declined_sent">Você recusou ser apresentado a %1$s.</string>
|
||||
<string name="introduction_response_declined_auto">A apresentação à %1$s foi automaticamente recusada.</string>
|
||||
<string name="introduction_response_accepted_received">%1$s aceitou ser apresentado a %2$s.</string>
|
||||
<string name="introduction_response_declined_received">%1$s recusou ser apresentado a %2$s.</string>
|
||||
<string name="introduction_response_declined_received_by_introducee">%1$s disse que %2$s recusou a apresentação.</string>
|
||||
<!--Connect via Bluetooth-->
|
||||
<string name="menu_item_connect_via_bluetooth">Conectar via Bluetooth</string>
|
||||
<string name="connect_via_bluetooth_title">Conectar via Bluetooth</string>
|
||||
<string name="connect_via_bluetooth_intro">Caso as conexões Bluetooth não funcionem automaticamente, você pode usar essa tela para se conectar manualmente.\n\nSeu contato precisa estar próximo para isso dar certo.\n\nVocê e seu contato devem apertar \"Iniciar\" ao mesmo tempo.</string>
|
||||
<string name="connect_via_bluetooth_already_discovering">Tentando se conectar via Bluetooth no momento. Por favor, tente novamente em breve.</string>
|
||||
<string name="connect_via_bluetooth_no_location_permission">Não há como continuar sem a permissão de localização</string>
|
||||
<string name="connect_via_bluetooth_start">Conectando-se via Bluetooth...</string>
|
||||
<string name="connect_via_bluetooth_success">Conectado com sucesso via Bluetooth</string>
|
||||
<string name="connect_via_bluetooth_error">Não pôde se conectar via Bluetooth.</string>
|
||||
<string name="connect_via_bluetooth_error_not_supported">Bluetooth não suportado pelo dispositivo.</string>
|
||||
<!--Private Groups-->
|
||||
<string name="groups_list_empty">Sem grupos para exibir</string>
|
||||
<string name="groups_list_empty_action">Toque no ícone + para criar um grupo, ou peça a seus contatos para compartilhar grupos com você</string>
|
||||
<string name="groups_list_empty_action">Toque no ícone + para criar um grupo, ou peça para seus contatos compartilharem grupos com você</string>
|
||||
<string name="groups_created_by">Criado por %s.</string>
|
||||
<plurals name="messages">
|
||||
<item quantity="one">%d mensagem</item>
|
||||
@@ -321,6 +374,7 @@
|
||||
</plurals>
|
||||
<string name="groups_invitations_response_accepted_sent">Você aceitou o convite do Grupo de %s.</string>
|
||||
<string name="groups_invitations_response_declined_sent">Você recusou o convite do Grupo de %s.</string>
|
||||
<string name="groups_invitations_response_declined_auto">O convite ao grupo de %s foi automaticamente recusado.</string>
|
||||
<string name="groups_invitations_response_accepted_received">%s aceitou o convite do Grupo.</string>
|
||||
<string name="groups_invitations_response_declined_received">%s recusou o convite do Grupo.</string>
|
||||
<string name="sharing_status_groups">Apenas o criador pode convidar novas pessoas para o grupo. Abaixo estão os atuais membros do grupo.</string>
|
||||
@@ -332,19 +386,19 @@
|
||||
<string name="groups_reveal_visible_revealed_by_contact">Relação de contatos está visível para o Grupo (revelado por %s)</string>
|
||||
<string name="groups_reveal_invisible">Relação de contatos não visível para o Grupo </string>
|
||||
<!--Forums-->
|
||||
<string name="no_forums">Sem Fóruns para exibir </string>
|
||||
<string name="no_forums_action">Toque no ícone + para criar um forum, ou peça para seus contatos compartilhar forums com você </string>
|
||||
<string name="no_forums">Sem fóruns para exibir </string>
|
||||
<string name="no_forums_action">Toque no ícone + para criar um fórum, ou peça para seus contatos compartilharem fóruns com você </string>
|
||||
<string name="create_forum_title">Criar Fórum</string>
|
||||
<string name="choose_forum_hint">Escolha um nome para o seu fórum</string>
|
||||
<string name="create_forum_button">Criar fórum</string>
|
||||
<string name="forum_created_toast">Fórum criado</string>
|
||||
<string name="no_forum_posts">Nenhum post para ser exibido</string>
|
||||
<string name="no_forum_posts">Sem posts para exibir</string>
|
||||
<string name="no_posts">Sem Posts</string>
|
||||
<plurals name="posts">
|
||||
<item quantity="one">%d Post</item>
|
||||
<item quantity="other">%d Posts</item>
|
||||
</plurals>
|
||||
<string name="forum_new_message_hint">Nova postagem</string>
|
||||
<string name="forum_new_message_hint">Novo Post</string>
|
||||
<string name="forum_message_reply_hint">Nova resposta</string>
|
||||
<string name="btn_reply">Responder</string>
|
||||
<string name="forum_leave">Sair do fórum</string>
|
||||
@@ -371,6 +425,7 @@
|
||||
<string name="forum_invitation_already_sharing">Já compartilhado</string>
|
||||
<string name="forum_invitation_response_accepted_sent">Você aceitou o convite do fórum do %s.</string>
|
||||
<string name="forum_invitation_response_declined_sent">Você recusou o convite de Fórum de %s.</string>
|
||||
<string name="forum_invitation_response_declined_auto">O convite ao fórum de %s foi automaticamente recusado.</string>
|
||||
<string name="forum_invitation_response_accepted_received">%s aceitou o convite de fórum.</string>
|
||||
<string name="forum_invitation_response_declined_received">%s recusou o convite de fórum.</string>
|
||||
<string name="sharing_status">Status de compartilhamento</string>
|
||||
@@ -382,7 +437,7 @@
|
||||
</plurals>
|
||||
<string name="nobody">Ninguém</string>
|
||||
<!--Blogs-->
|
||||
<string name="blogs_other_blog_empty_state">Nenhum post para ser exibido</string>
|
||||
<string name="blogs_other_blog_empty_state">Sem posts para exibir</string>
|
||||
<string name="read_more">leia mais</string>
|
||||
<string name="blogs_write_blog_post">Escrever Post do Blog</string>
|
||||
<string name="blogs_write_blog_post_body_hint">Digite seu post</string>
|
||||
@@ -390,8 +445,8 @@
|
||||
<string name="blogs_blog_post_created">Post do Blog criado</string>
|
||||
<string name="blogs_blog_post_received">Novo Post de Blog recebido</string>
|
||||
<string name="blogs_blog_post_scroll_to">Role Para</string>
|
||||
<string name="blogs_feed_empty_state">Nenhum post para ser exibido</string>
|
||||
<string name="blogs_feed_empty_state_action">Posts de seus contatos e blogs que você está inscrito vão aparecer aqui\n\n Toque no ícone da caneta para escrever um post </string>
|
||||
<string name="blogs_feed_empty_state">Sem posts para exibir</string>
|
||||
<string name="blogs_feed_empty_state_action">Posts de seus contatos e de blogs em que você está inscrito aparecerão aqui\n\nToque no ícone da caneta para escrever um post </string>
|
||||
<string name="blogs_remove_blog">Remover Blog</string>
|
||||
<string name="blogs_remove_blog_dialog_message">Você tem certeza que quer remover este blog?\n\nOs posts serão removidos do seu dispositivo mas não dos dispositivos das outras pessoas.\n\nContatos com quem você tenha compartilhado este blog vão parar de receber atualizações dele.</string>
|
||||
<string name="blogs_remove_blog_ok">Remover</string>
|
||||
@@ -405,6 +460,7 @@
|
||||
<string name="blogs_sharing_snackbar">Blog compartilhado com os contatos escolhidos</string>
|
||||
<string name="blogs_sharing_response_accepted_sent">Você aceitou o convite do Blog do %s.</string>
|
||||
<string name="blogs_sharing_response_declined_sent">Você recusou o convite de Blog de %s.</string>
|
||||
<string name="blogs_sharing_response_declined_auto">O convite ao blog de %s foi automaticamente recusado.</string>
|
||||
<string name="blogs_sharing_response_accepted_received">%s aceitou o convite de Blog.</string>
|
||||
<string name="blogs_sharing_response_declined_received">%s recusou o convite de Fórum.</string>
|
||||
<string name="blogs_sharing_invitation_received">%1$s compartilhou o blog \"%2$s\" com você.</string>
|
||||
@@ -418,6 +474,7 @@
|
||||
<string name="blogs_rss_feeds_import_button">Importar</string>
|
||||
<string name="blogs_rss_feeds_import_hint">Entre a URL do feed RSS</string>
|
||||
<string name="blogs_rss_feeds_import_error">Nós lamentamos! Houve um erro ao importar seu Feed.</string>
|
||||
<string name="blogs_rss_feeds_import_exists">Esse feed já foi importado.</string>
|
||||
<string name="blogs_rss_feeds">Feeds RSS</string>
|
||||
<string name="blogs_rss_feeds_manage_imported">Importado:</string>
|
||||
<string name="blogs_rss_feeds_manage_author">Autor:</string>
|
||||
@@ -425,14 +482,18 @@
|
||||
<string name="blogs_rss_remove_feed">Remover Feed</string>
|
||||
<string name="blogs_rss_remove_feed_dialog_message">Você tem certeza que deseja remover este feed?\n\nOs posts serão removidos do seus dispositivo mas não dos dispositivos de outras pessoas.\n\nContatos com quem você tenha compartilhado este feed vão parar de receber atualizações dele.</string>
|
||||
<string name="blogs_rss_remove_feed_ok">Remover</string>
|
||||
<string name="blogs_rss_feeds_manage_empty_state">Nenhum feed RSS para ser exibido\n\nPressione o ícone + para importar um feed</string>
|
||||
<string name="blogs_rss_feeds_manage_empty_state">Sem feeds RSS para exibir\n\nToque no ícone + para importar um feed</string>
|
||||
<string name="blogs_rss_feeds_manage_error">Houve um problema ao carregar seus Feeds. Por favor tente novamente.</string>
|
||||
<!--Settings Profile Picture-->
|
||||
<string name="change_profile_picture">Toque para mudar sua foto de perfil</string>
|
||||
<string name="dialog_confirm_profile_picture_title">Mudar foto de perfil</string>
|
||||
<string name="dialog_confirm_profile_picture_remark">Só seus contatos podem ver essa foto</string>
|
||||
<string name="change_profile_picture_failed_message">Sentimos muito, mas algo deu errado ao atualizar sua foto de perfil</string>
|
||||
<!--Settings Display-->
|
||||
<string name="pref_language_title">Linguagem & região</string>
|
||||
<string name="pref_language_changed">Essa configuração vai tomar efeito quando você reiniciar o Briar. Por favor saia e reinicie o Briar.</string>
|
||||
<string name="pref_language_changed">Essa definição vai valer quando você reiniciar o Briar. Por favor saia e reinicie o Briar.</string>
|
||||
<string name="pref_language_default">Padrão do sistema</string>
|
||||
<string name="display_settings_title">Visualização</string>
|
||||
<string name="display_settings_title">Aparência</string>
|
||||
<string name="pref_theme_title">Tema</string>
|
||||
<string name="pref_theme_light">Claro</string>
|
||||
<string name="pref_theme_dark">Escuro</string>
|
||||
@@ -451,17 +512,17 @@
|
||||
<string name="tor_network_setting_never">Não se conectar à 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)"-->
|
||||
<string name="tor_network_setting_summary">Automático: %1$s (em %2$s) </string>
|
||||
<string name="tor_mobile_data_title">Usar dados moveis</string>
|
||||
<string name="tor_only_when_charging_title">Conectar à Internet somente durante carregamento de bateria</string>
|
||||
<string name="tor_mobile_data_title">Usar dados móveis</string>
|
||||
<string name="tor_only_when_charging_title">Conectar à Internet apenas durante o carregamento</string>
|
||||
<string name="tor_only_when_charging_summary">Desabilita conexão à Internet quando seu dispositivo estiver somente na bateria</string>
|
||||
<!--Settings Security and Panic-->
|
||||
<string name="security_settings_title">Segurança</string>
|
||||
<string name="pref_lock_title">Bloqueio de app</string>
|
||||
<string name="pref_lock_summary">Use o bloqueio de tela do dispositivo para proteger o Briar enquanto estiver conectado</string>
|
||||
<string name="pref_lock_disabled_summary">Para usar esse recurso, configure um bloqueio de tela para seu dispositivo</string>
|
||||
<string name="pref_lock_timeout_title">Bloqueio de app por tempo de inatividade</string>
|
||||
<string name="pref_lock_timeout_title">Tempo de inatividade para bloqueio do app</string>
|
||||
<!--The %s placeholder is replaced with the following time spans, e.g. 5 Minutes, 1 Hour-->
|
||||
<string name="pref_lock_timeout_summary">Quando o Briar não estiver sendo usando, bloquear automaticamente após %s</string>
|
||||
<string name="pref_lock_timeout_summary">Bloquear o Briar automaticamente ao não usá-lo por %s</string>
|
||||
<!--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 minuto</string>
|
||||
<!--Will be shown in a list of lock times. Should fit into the %s of "automatically lock it after %s"-->
|
||||
@@ -496,7 +557,7 @@
|
||||
<!--Settings Notifications-->
|
||||
<string name="notification_settings_title">Notificações</string>
|
||||
<string name="notify_sign_in_title">Lembre-me de fazer login</string>
|
||||
<string name="notify_sign_in_summary">Mostrar um lembrete quando o telefone é iniciado ou o aplicativo for atualizado</string>
|
||||
<string name="notify_sign_in_summary">Mostrar um lembrete quando o celular for ligado ou o app atualizado</string>
|
||||
<string name="notify_private_messages_setting_title">Mensagens privadas</string>
|
||||
<string name="notify_private_messages_setting_summary">Mostrar alertas para mensagens privadas</string>
|
||||
<string name="notify_private_messages_setting_summary_26">Configurar alertas para mensagens privadas</string>
|
||||
@@ -515,8 +576,56 @@
|
||||
<string name="notify_sound_setting_disabled">Nenhum</string>
|
||||
<string name="choose_ringtone_title">Escolher toque</string>
|
||||
<string name="cannot_load_ringtone">Não foi possível carregar o toque</string>
|
||||
<!--Mailbox-->
|
||||
<string name="mailbox_settings_title">Mailbox</string>
|
||||
<string name="mailbox_setup_title">Configuração do Mailbox</string>
|
||||
<string name="mailbox_setup_intro">Um Mailbox permite que seus contatos enviem mensagens a você enquanto você está offline. O Mailbox irá receber suas mensagens e as armazenar até que você esteja online.\n
|
||||
\nVocê pode instalar o app Briar Mailbox em um dispositivo sobrando. Mantenha-o conectado à energia e ao Wi-Fi para que sempre esteja online.</string>
|
||||
<string name="mailbox_setup_download">Primeiro, instale o app Mailbox em outro dispositivo buscando por \"Briar Mailbox\" no Google Play ou no local onde você baixou o Briar.\n
|
||||
\nDepois, vincule seu Mailbox com o Briar escaneando o código QR mostrado pelo app Mailbox.</string>
|
||||
<string name="mailbox_setup_download_link">Compartilhar Link de Download</string>
|
||||
<string name="mailbox_setup_button_scan">Escanear código QR do Mailbox</string>
|
||||
<string name="permission_camera_qr_denied_body">Você negou acesso à câmera, mas escanear um código QR requer o uso da câmera.\n\nPor favor, considere conceder acesso.</string>
|
||||
<string name="mailbox_setup_connecting">Conectando...</string>
|
||||
<string name="mailbox_setup_qr_code_wrong_title">Código QR errado</string>
|
||||
<string name="mailbox_setup_qr_code_wrong_description">O código escaneado é inválido. Por favor, abra o app Briar Mailbox no seu dispositivo Mailbox e escaneie o código QR apresentado.</string>
|
||||
<string name="mailbox_setup_already_paired_title">Mailbox já vinculado</string>
|
||||
<string name="mailbox_setup_already_paired_description">Desvincule o Mailbox no seu outro dispositivo e tente de novo.</string>
|
||||
<string name="mailbox_setup_io_error_title">Não foi possível conectar</string>
|
||||
<string name="mailbox_setup_io_error_description">Garanta que ambos os dispositivo estão conectados à Internet e tente de novo.</string>
|
||||
<string name="mailbox_setup_assertion_error_title">Erro no Mailbox</string>
|
||||
<string name="mailbox_setup_assertion_error_description">Por favor, envie feedback (com dados anônimos) pelo app Briar se o problema persistir.</string>
|
||||
<string name="mailbox_setup_camera_error_description">Não foi possível acessar a câmera. Tente de novo, talvez após reiniciar o dispositivo.</string>
|
||||
<string name="mailbox_setup_paired_title">Conectado</string>
|
||||
<string name="mailbox_setup_paired_description">Seu Mailbox foi vinculado ao Briar com sucesso.\n
|
||||
\nMantenha seu Mailbox conectado à energia e ao Wi-Fi para que sempre esteja online.</string>
|
||||
<string name="tor_offline_title">Offline</string>
|
||||
<string name="tor_offline_description">Garanta que esse dispositivo está online e que conexões à Internet são permitidas.\n
|
||||
\nEm seguida, aguarde o ícone de globo na tela das configurações de conexão ficar verde.</string>
|
||||
<string name="tor_offline_button_check">Cheque as configurações de conexão</string>
|
||||
<string name="mailbox_status_title">Status do Mailbox</string>
|
||||
<string name="mailbox_status_connected_title">Mailbox está em execução</string>
|
||||
<string name="mailbox_status_problem_title">Estamos tendo dificuldades ao conectar ao Mailbox</string>
|
||||
<string name="mailbox_status_failure_title">Mailbox está indisponível</string>
|
||||
<string name="mailbox_status_check_button">Verificar Conexão</string>
|
||||
<!--Example for string substitution: Last connection: 3min ago-->
|
||||
<string name="mailbox_status_connected_info">Última conexão: %s</string>
|
||||
<!--Indicates that there never was a connection to the mailbox. Last connection: Never-->
|
||||
<string name="mailbox_status_connected_never">Nunca</string>
|
||||
<string name="mailbox_status_unlink_button">Desvincular</string>
|
||||
<!--Conversation Settings-->
|
||||
<string name="disappearing_messages_title">Mensagens efêmeras</string>
|
||||
<string name="disappearing_messages_explanation_long">Ativar essa opção fará novas
|
||||
mensagens nessa conversa desaparecerem automaticamente após 7\u00A0dias.
|
||||
\n\nO temporizador para a cópia do remetente da mensagem começa após ela ter sido entregue.
|
||||
O temporizador para o destinatário começa após ele ter lido a mensagem.
|
||||
\n\nMensagens efêmeras são marcadas com um ícone de bomba.
|
||||
\n\nNote que destinatários ainda podem fazer cópias das mensagens que você envia.
|
||||
\n\nSe você alterar essa opção, ela se aplicará à novas mensagens imediatamente e para
|
||||
mensagens de contatos após receberem sua próxima mensagem.
|
||||
Seu contato tambem pode alterar essa opção por vocês.</string>
|
||||
<string name="learn_more">Saiba Mais</string>
|
||||
<string name="disappearing_messages_summary">Faça as futuras mensagens nessa conversa desaparecerem automaticamente após 7\u00A0dias.</string>
|
||||
<!--Settings Actions-->
|
||||
<string name="pref_category_actions">Ações</string>
|
||||
<string name="send_feedback">Enviar feedback</string>
|
||||
@@ -527,6 +636,7 @@
|
||||
<string name="link_warning_open_link">Abrir Link</string>
|
||||
<!--Crash Reporter-->
|
||||
<string name="crash_report_title">Relatório de falhas do Briar</string>
|
||||
<string name="briar_crashed">Desculpe, o Briar fechou</string>
|
||||
<string name="not_your_fault">Isso não é sua culpa.</string>
|
||||
<string name="please_send_report">Nós ajude a construir um Briar melhor enviando um relatório de falhas.</string>
|
||||
<string name="report_is_encrypted">Nós prometemos que o relatório é criptografado e enviado de forma segura.</string>
|
||||
@@ -536,6 +646,7 @@
|
||||
<string name="optional_contact_email">E-mail de contato (opcional)</string>
|
||||
<string name="include_debug_report_crash">Incluir informações anonimas sobre o </string>
|
||||
<string name="include_debug_report_feedback">Incluir dados anônimos sobre esse dispositivo </string>
|
||||
<string name="dev_report_user_info">Informações do usuário</string>
|
||||
<string name="dev_report_basic_info">Informação Básica</string>
|
||||
<string name="dev_report_device_info">Informações do dispositivo</string>
|
||||
<string name="dev_report_stacktrace">Rastreamento de pilha</string>
|
||||
@@ -543,6 +654,7 @@
|
||||
<string name="dev_report_memory">Memória</string>
|
||||
<string name="dev_report_storage">Armazenamento</string>
|
||||
<string name="dev_report_connectivity">Conectividade</string>
|
||||
<string name="dev_report_network_usage">Uso da rede</string>
|
||||
<string name="dev_report_build_config">Configuração de execução</string>
|
||||
<string name="dev_report_logcat">Log do App</string>
|
||||
<string name="dev_report_device_features">Características do dispositivo</string>
|
||||
@@ -569,30 +681,104 @@
|
||||
<string name="permission_camera_location_request_body">Para escanear o código QR, O Briar precisa de acesso à câmera.\n\nPara encontrar dispositivos Bluetooth, O Briar precisa de permissão para acessar a localização.\n\nO Briar não armazena seu localização ou a compartilhar com qualquer pessoa.</string>
|
||||
<string name="permission_camera_denied_body">Você negou acesso à câmera, mas para adicionar contatos você precisa da câmera.\n\nPor favor, pense em liberar o acesso a ela.</string>
|
||||
<string name="permission_location_denied_body">Você desabilitou o acesso à sua localização, no entanto o Briar precisa da permissão para saber se há dispositivos Bluetooth.\n\nPor favor, considere liberar o acesso.</string>
|
||||
<string name="permission_location_setting_title">Configuração de localização</string>
|
||||
<string name="permission_location_setting_body">A opção de localização de seu dispositivo deve estar ativada para achar outros dispositivos via Bluetooth. Por favor, ative a localização para continuar. Você pode desativá-la em seguida.</string>
|
||||
<string name="permission_location_setting_button">Ativar localização</string>
|
||||
<string name="qr_code">Código QR</string>
|
||||
<string name="show_qr_code_fullscreen">Mostrar o código QR na tela cheia</string>
|
||||
<string name="show_qr_code_fullscreen">Mostrar código QR em tela cheia</string>
|
||||
<!--App Locking-->
|
||||
<string name="lock_unlock">Desbloquear o Briar</string>
|
||||
<string name="lock_unlock_verbose">Digite o PIN, padrão ou senha do seu dispositivo para desbloquear o Briar</string>
|
||||
<string name="lock_unlock_verbose">Insira o PIN, padrão ou senha do seu dispositivo para desbloquear o Briar</string>
|
||||
<string name="lock_unlock_fingerprint_description">Encoste no leitor de impressão digital com o dedo cadastrado para continuar</string>
|
||||
<string name="lock_unlock_password">Usar senha</string>
|
||||
<string name="lock_is_locked">O Briar está desbloqueado</string>
|
||||
<string name="lock_is_locked">O Briar está bloqueado</string>
|
||||
<string name="lock_tap_to_unlock">Toque para desbloquear</string>
|
||||
<!--Connections Screen-->
|
||||
<string name="transports_help_text">Briar pode se conectar aos seus contatos via Internet, Wi-Fi ou Bluetooth.\n\nTodas as conexões com a Internet acontecem via rede do Tor, por privacidade.\n\nSe um contato pode ser alcançado de várias formas, Briar as utilizará em paralelo.</string>
|
||||
<!--Share app offline-->
|
||||
<string name="hotspot_title">Compartilhar esse app offline</string>
|
||||
<string name="hotspot_intro">Compartilhe esse app com alguém próximo sem uma conexão à Internet usando o Wi-Fi do seu celular
|
||||
\n\nSeu celular vai iniciar um hotspot Wi-Fi. Pessoas próximas podem se conectar ao hotspot e baixar o app Briar pelo seu celular.</string>
|
||||
<string name="hotspot_button_start_sharing">Iniciar hotspot</string>
|
||||
<string name="hotspot_button_stop_sharing">Parar hotspot</string>
|
||||
<string name="hotspot_progress_text_start">Preparando hotspot...</string>
|
||||
<string name="hotspot_notification_channel_title">Hotspot Wi-Fi</string>
|
||||
<string name="hotspot_notification_title">Compartilhando Briar offline</string>
|
||||
<string name="hotspot_button_connected">Próximo</string>
|
||||
<string name="permission_hotspot_location_request_body">Para criar um hotspot Wi-Fi, o Briar precisa de permissão para acessar sua localização.\n\nO Briar não armazena sua localização nem a compartilha.</string>
|
||||
<string name="permission_hotspot_location_denied_body">Você negou acesso à sua localização, mas o Briar precisa dessa permissão para criar um hotspot Wi-Fi.\n\nPor favor, considere conceder acesso.</string>
|
||||
<string name="wifi_settings_title">Configurações de Wi-Fi</string>
|
||||
<string name="wifi_settings_request_enable_body">Para criar um hotspot Wi-Fi, o Briar precisa usar o Wi-Fi. Por favor, ative ele.</string>
|
||||
<string name="hotspot_tab_manual">Manual</string>
|
||||
<!--The placeholder to be inserted into the string 'hotspot_manual_wifi': People can connect by %s-->
|
||||
<string name="hotspot_scanning_a_qr_code">escaneando um código QR</string>
|
||||
<!--Wi-Fi setup-->
|
||||
<!--The %s placeholder will be replaced with the translation of 'hotspot_scanning_a_qr_code'-->
|
||||
<string name="hotspot_manual_wifi">Seu celular está fornecendo um hotspot Wi-Fi. Pessoas que quiserem baixar o Briar podem se conectar ao hotspot ao adicioná-lo nas configurações de Wi-Fi de seus dispositivos usando os detalhes abaixo ou %s. Quando estiverem conectadas ao hotspot, pressione \'Próximo\'.</string>
|
||||
<string name="hotspot_manual_wifi_ssid">Nome da rede</string>
|
||||
<string name="hotspot_qr_wifi">Seu celular está fornecendo um hotspot Wi-Fi. Pessoas que quiserem baixar o Briar podem se conectar ao hotspot escaneando esse código QR. Quando estiverem conectadas ao hotspot, pressione \'Próximo\'.</string>
|
||||
<string name="hotspot_no_peers_connected">Sem dispositivos conectados</string>
|
||||
<plurals name="hotspot_peers_connected">
|
||||
<item quantity="one">%s dispositivo conectado</item>
|
||||
<item quantity="other">%s dispositivos conectados</item>
|
||||
</plurals>
|
||||
<!--Download link-->
|
||||
<!--The %s placeholder will be replaced with the translation of 'hotspot_scanning_a_qr_code'-->
|
||||
<string name="hotspot_manual_site">Seu celular está fornecendo um hotspot Wi-Fi. Pessoas que estão conectadas ao hotspot podem baixar o Briar inserindo o seguinte link em um navegador de internet ou %s.</string>
|
||||
<string name="hotspot_manual_site_address">Endereço (URL)</string>
|
||||
<string name="hotspot_qr_site">Seu celular está fornecendo um hotspot Wi-Fi. Pessoas que estão conectadas ao hotspot podem baixar o Briar escaneando esse código QR. </string>
|
||||
<!--e.g. Download Briar 1.2.20-->
|
||||
<string name="website_download_title">Baixar %s</string>
|
||||
<string name="website_download_intro">Alguém próximo compartilhou %s com você.</string>
|
||||
<string name="website_download_outro">Após baixar o arquivo, abra-o e instale-o.</string>
|
||||
<string name="website_troubleshooting_title">Solução de Problemas</string>
|
||||
<string name="website_troubleshooting_1">Se você não conseguir baixar o app, tente em um app de navegador de internet diferente.</string>
|
||||
<string name="website_troubleshooting_2_old">Para instalar o app baixado, você talvez precise permitir a instalação de apps de \"Fontes desconhecidas\" nas configurações do sistema. Após isso, você pode precisar baixar o app novamente. Recomendamos desativar a permissão de \"Fontes desconhecidas\" após instalar o app.</string>
|
||||
<string name="website_troubleshooting_2_new">Para instalar o app baixado, você talvez precise permitir que seu navegador instale apps desconhecidos. Após instalar o app, recomendamos remover a permissão do navegador de instalar apps desconhecidos.</string>
|
||||
<string name="hotspot_help_wifi_title">Problemas ao conectar ao Wi-Fi:</string>
|
||||
<string name="hotspot_help_wifi_1">Tente desativar e reativar o Wi-Fi nos dois celulares e tente de novo.</string>
|
||||
<string name="hotspot_help_wifi_2">Se seu celular reclama que o Wi-Fi não tem Internet, diga que você quer ficar conectado mesmo assim.</string>
|
||||
<string name="hotspot_help_wifi_3">Reinicie o celular que está rodando o hotspot Wi-Fi, depois abra o Briar e tente compartilhar de novo.</string>
|
||||
<string name="hotspot_help_site_title">Problemas visitando o website local:</string>
|
||||
<string name="hotspot_help_site_1">Verifique se você inseriu o endereço exatamente como mostrado. Um pequeno erro pode fazê-lo falhar.</string>
|
||||
<string name="hotspot_help_site_2">Garanta que seu celular ainda está conectado ao Wi-Fi correto (veja acima) quando você tentar acessar o site.</string>
|
||||
<string name="hotspot_help_site_3">Se você tem um app de firewall, verifique se ele não está bloqueando o acesso.</string>
|
||||
<string name="hotspot_help_site_4">Se você consegue visitar o site, mas não baixar o app Briar, tente em um app navegador de internet diferente.</string>
|
||||
<string name="hotspot_help_fallback_title">Nada funciona?</string>
|
||||
<string name="hotspot_help_fallback_intro">Você pode tentar salvar o app como um arquivo .apk para compartilhar de outra forma. Após o arquivo ser transferido para o outro dispositivo, ele pode ser usado para instalar o Briar.
|
||||
\n\nDica: Para transferir via Bluetooth, você talvez precise renomear o arquivo para finalizar com .zip primeiro.</string>
|
||||
<string name="hotspot_help_fallback_button">Salvar app</string>
|
||||
<!--error handling-->
|
||||
<string name="hotspot_error_intro">Algo deu errado ao tentar compartilhar o app via Wi-Fi:</string>
|
||||
<string name="hotspot_error_no_wifi_direct">Dispositivo não suporta Wi-Fi Direct</string>
|
||||
<string name="hotspot_error_start_callback_failed">Hotspot falhou ao iniciar: erro %s</string>
|
||||
<string name="hotspot_error_start_callback_failed_unknown">Hotspot falhou ao iniciar, com um erro desconhecido, razão %d</string>
|
||||
<string name="hotspot_error_start_callback_no_group_info">Hotspot falhou ao iniciar: sem info de grupo</string>
|
||||
<string name="hotspot_error_web_server_start">Erro ao iniciar servidor web</string>
|
||||
<string name="hotspot_error_web_server_serve">Erro ao apresentar website.\n\nPor favor envie feedback (com dados anônimos) pelo app Briar se o problema persistir.</string>
|
||||
<string name="hotspot_flag_test">Aviso: Esse app foi instalado com Android Studio e NÃO pode ser instalado em outro dispositivo.</string>
|
||||
<string name="hotspot_error_framework_busy">Não pôde iniciar o hotspot.\n\nSe você tem outro hotspot rodando ou está compartilhando sua conexão à Internet por Wi-Fi, tente parar isso e tentar de novo em seguida.</string>
|
||||
<!--Transfer Data via Removable Drives-->
|
||||
<string name="removable_drive_menu_title">Conectar via Unidade Removível</string>
|
||||
<string name="removable_drive_intro">Se você não puder se conectar ao seu contato via Internet, Wi-Fi ou Bluetooth, o Briar tambem pode transferir mensagens em uma unidade removível como um pendrive USB ou cartão SD.</string>
|
||||
<string name="removable_drive_explanation">Se você não pode se conectar ao seu contato por Internet, Wi-Fi ou Bluetooth, o Briar tambem pode transferir mensagens em uma unidade removível como um pendrive USB ou um cartão SD.\n\nQuando você usar o botão \"Enviar Dados\", qualquer dado que esteja aguardando envio ao seu contato será gravado na unidade removível. Isso inclui mensagens privadas, anexos, blogs, fóruns e grupos privados.\n\nTudo será criptografado antes de ser gravado na unidade removível.\n\nQuando seu contato receber a unidade removível, poderá usar o botão \"Receber Dados\" para importar as mensagens ao Briar.</string>
|
||||
<string name="removable_drive_title_send">Enviar dados</string>
|
||||
<string name="removable_drive_title_receive">Receber dados</string>
|
||||
<string name="removable_drive_send_intro">Toque no botão abaixo para criar um novo arquivo contendo as mensagens criptografadas. Você pode escolher onde o arquivo será salvo.\n\nSe você quer salvar o arquivo numa unidade removível, insira a unidade agora.</string>
|
||||
<string name="removable_drive_send_no_data">Atualmente não há mensagens aguardando envio para esse contato.</string>
|
||||
<string name="removable_drive_send_not_supported">Esse contato está usando uma versão antiga do Briar ou um dispositivo antigo que não suporta esse recurso.</string>
|
||||
<string name="removable_drive_send_button">Escolher arquivo para exportar</string>
|
||||
<string name="removable_drive_ongoing">Por favor, aguarde a tarefa em curso ser concluída</string>
|
||||
<string name="removable_drive_receive_intro">Toque no botão abaixo para selecionar o arquivo que seu contato enviou para você.\n\nSe o arquivo está numa unidade removível, insira a unidade agora.</string>
|
||||
<string name="removable_drive_receive_button">Escolher arquivo para importar</string>
|
||||
<string name="removable_drive_success_send_title">Exportação bem sucedida</string>
|
||||
<string name="removable_drive_success_send_text">Dados exportados com sucesso. Você agora tem 28 dias para transportar o arquivo para seu contato.\n\nSe o arquivo está em uma unidade removível, use a notificação na barra de status para ejetar a unidade antes de removê-la.</string>
|
||||
<string name="removable_drive_success_receive_title">Importação bem sucedida</string>
|
||||
<string name="removable_drive_success_receive_text">Todas as mensagens criptografadas contidas nesse arquivo foram recebidas.</string>
|
||||
<string name="removable_drive_error_send_title">Erro ao exportar dados</string>
|
||||
<string name="removable_drive_error_send_text">Houve um erro ao gravar dados no arquivo. \n\nSe você está usando uma unidade removível, garanta que está propriamente inserida e tente de novo.\n\nSe o erro persistir, por favor envie feedback para que a equipe do Briar saiba do problema.</string>
|
||||
<string name="removable_drive_error_receive_title">Erro ao importar dados</string>
|
||||
<string name="removable_drive_error_receive_text">O arquivo selecionado não contém nada que o Briar possa reconhecer. \n\nPor favor, cheque que você selecionou o arquivo correto.\n\nSe seu contato criou o arquivo há mais de 28 dias, o Briar não será capaz de reconhecê-lo.</string>
|
||||
<!--Screenshots-->
|
||||
<!--This is a name to be used in screenshots. Feel free to change it to a local name.-->
|
||||
<string name="screenshot_alice">Alice</string>
|
||||
|
||||
@@ -320,7 +320,7 @@
|
||||
<!-- Introductions -->
|
||||
|
||||
<string name="introduction_onboarding_title">Introduce your contacts</string>
|
||||
<string name="introduction_onboarding_text">You can introduce your contacts to each other, so they don\'t need to meet in person to connect on Briar.</string>
|
||||
<string name="introduction_onboarding_text">Introduce your contacts to each other so they can connect on Briar.</string>
|
||||
<string name="introduction_menu_item">Make Introduction</string>
|
||||
<string name="introduction_activity_title">Select Contact</string>
|
||||
<string name="introduction_not_possible">You already have one introduction in progress with these contacts. Please allow for this to finish first. If you or your contacts are rarely online, this can take some time.</string>
|
||||
@@ -643,10 +643,14 @@
|
||||
<string name="tor_offline_button_check">Check connection settings</string>
|
||||
<string name="mailbox_status_title">Mailbox status</string>
|
||||
<string name="mailbox_status_connected_title">Mailbox is running</string>
|
||||
<string name="mailbox_status_problem_title">Briar is having trouble connecting to the Mailbox</string>
|
||||
<string name="mailbox_status_failure_title">Mailbox is unavailable</string>
|
||||
<string name="mailbox_status_check_button">Check Connection</string>
|
||||
<!-- Example for string substitution: Last connection: 3min ago-->
|
||||
<string name="mailbox_status_connected_info">Last connection: %s</string>
|
||||
<!-- Indicates that there never was a connection to the mailbox. Last connection: Never -->
|
||||
<string name="mailbox_status_connected_never">Never</string>
|
||||
<string name="mailbox_status_unlink_button">Unlink</string>
|
||||
|
||||
<!-- Conversation Settings -->
|
||||
<string name="disappearing_messages_title">Disappearing messages</string>
|
||||
|
||||
Reference in New Issue
Block a user