mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-16 04:39:54 +01:00
Remember that app entered doze mode and inform user when returning
This commit is contained in:
@@ -3,12 +3,16 @@ package org.briarproject.briar.android;
|
|||||||
import android.app.NotificationManager;
|
import android.app.NotificationManager;
|
||||||
import android.app.PendingIntent;
|
import android.app.PendingIntent;
|
||||||
import android.app.Service;
|
import android.app.Service;
|
||||||
|
import android.content.BroadcastReceiver;
|
||||||
import android.content.ComponentName;
|
import android.content.ComponentName;
|
||||||
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
import android.content.IntentFilter;
|
||||||
import android.content.ServiceConnection;
|
import android.content.ServiceConnection;
|
||||||
import android.os.Binder;
|
import android.os.Binder;
|
||||||
import android.os.Build;
|
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
|
import android.os.PowerManager;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
import android.support.v4.app.NotificationCompat;
|
import android.support.v4.app.NotificationCompat;
|
||||||
import android.support.v4.content.ContextCompat;
|
import android.support.v4.content.ContextCompat;
|
||||||
|
|
||||||
@@ -28,12 +32,15 @@ import javax.inject.Inject;
|
|||||||
import static android.app.PendingIntent.FLAG_UPDATE_CURRENT;
|
import static android.app.PendingIntent.FLAG_UPDATE_CURRENT;
|
||||||
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP;
|
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP;
|
||||||
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
|
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
|
||||||
|
import static android.os.Build.VERSION.SDK_INT;
|
||||||
|
import static android.os.PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED;
|
||||||
import static android.support.v4.app.NotificationCompat.CATEGORY_SERVICE;
|
import static android.support.v4.app.NotificationCompat.CATEGORY_SERVICE;
|
||||||
import static android.support.v4.app.NotificationCompat.PRIORITY_MIN;
|
import static android.support.v4.app.NotificationCompat.PRIORITY_MIN;
|
||||||
import static android.support.v4.app.NotificationCompat.VISIBILITY_SECRET;
|
import static android.support.v4.app.NotificationCompat.VISIBILITY_SECRET;
|
||||||
import static java.util.logging.Level.WARNING;
|
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.ALREADY_RUNNING;
|
||||||
import static org.briarproject.bramble.api.lifecycle.LifecycleManager.StartResult.SUCCESS;
|
import static org.briarproject.bramble.api.lifecycle.LifecycleManager.StartResult.SUCCESS;
|
||||||
|
import static org.briarproject.briar.android.util.UiUtils.needsDozeWhitelisting;
|
||||||
|
|
||||||
public class BriarService extends Service {
|
public class BriarService extends Service {
|
||||||
|
|
||||||
@@ -45,6 +52,9 @@ public class BriarService extends Service {
|
|||||||
|
|
||||||
private final AtomicBoolean created = new AtomicBoolean(false);
|
private final AtomicBoolean created = new AtomicBoolean(false);
|
||||||
private final Binder binder = new BriarBinder();
|
private final Binder binder = new BriarBinder();
|
||||||
|
@Nullable
|
||||||
|
private BriarBroadcastReceiver receiver = null;
|
||||||
|
private boolean hasDozed = false;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
protected DatabaseConfig databaseConfig;
|
protected DatabaseConfig databaseConfig;
|
||||||
@@ -84,7 +94,7 @@ public class BriarService extends Service {
|
|||||||
Intent i = new Intent(this, NavDrawerActivity.class);
|
Intent i = new Intent(this, NavDrawerActivity.class);
|
||||||
i.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TOP);
|
i.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TOP);
|
||||||
b.setContentIntent(PendingIntent.getActivity(this, 0, i, 0));
|
b.setContentIntent(PendingIntent.getActivity(this, 0, i, 0));
|
||||||
if (Build.VERSION.SDK_INT >= 21) {
|
if (SDK_INT >= 21) {
|
||||||
b.setCategory(CATEGORY_SERVICE);
|
b.setCategory(CATEGORY_SERVICE);
|
||||||
b.setVisibility(VISIBILITY_SECRET);
|
b.setVisibility(VISIBILITY_SECRET);
|
||||||
}
|
}
|
||||||
@@ -109,6 +119,7 @@ public class BriarService extends Service {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}.start();
|
}.start();
|
||||||
|
registerBroadcastReceiver();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void showStartupFailureNotification(StartResult result) {
|
private void showStartupFailureNotification(StartResult result) {
|
||||||
@@ -153,6 +164,7 @@ public class BriarService extends Service {
|
|||||||
public void onDestroy() {
|
public void onDestroy() {
|
||||||
super.onDestroy();
|
super.onDestroy();
|
||||||
LOG.info("Destroyed");
|
LOG.info("Destroyed");
|
||||||
|
if (receiver != null) unregisterReceiver(receiver);
|
||||||
stopForeground(true);
|
stopForeground(true);
|
||||||
// Stop the services in a background thread
|
// Stop the services in a background thread
|
||||||
new Thread() {
|
new Thread() {
|
||||||
@@ -170,6 +182,21 @@ public class BriarService extends Service {
|
|||||||
// FIXME: Work out what to do about it
|
// FIXME: Work out what to do about it
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void registerBroadcastReceiver() {
|
||||||
|
if (SDK_INT < 23) return;
|
||||||
|
IntentFilter filter = new IntentFilter(ACTION_DEVICE_IDLE_MODE_CHANGED);
|
||||||
|
if (receiver == null) receiver = new BriarBroadcastReceiver();
|
||||||
|
registerReceiver(receiver, filter);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasDozed() {
|
||||||
|
return hasDozed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void resetDozeFlag() {
|
||||||
|
hasDozed = false;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Waits for all services to start before returning.
|
* Waits for all services to start before returning.
|
||||||
*/
|
*/
|
||||||
@@ -225,4 +252,15 @@ public class BriarService extends Service {
|
|||||||
return binder;
|
return binder;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class BriarBroadcastReceiver extends BroadcastReceiver {
|
||||||
|
@Override
|
||||||
|
public void onReceive(Context context, Intent intent) {
|
||||||
|
if (SDK_INT < 23 || !needsDozeWhitelisting(getApplicationContext()))
|
||||||
|
return;
|
||||||
|
PowerManager pm = (PowerManager) getSystemService(POWER_SERVICE);
|
||||||
|
if (pm.isDeviceIdleMode()) hasDozed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,15 +4,18 @@ import android.annotation.SuppressLint;
|
|||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.support.v7.app.ActionBar;
|
import android.support.v7.app.ActionBar;
|
||||||
|
import android.support.v7.app.AlertDialog;
|
||||||
import android.support.v7.widget.Toolbar;
|
import android.support.v7.widget.Toolbar;
|
||||||
import android.transition.Slide;
|
import android.transition.Slide;
|
||||||
import android.transition.Transition;
|
import android.transition.Transition;
|
||||||
import android.view.Gravity;
|
import android.view.Gravity;
|
||||||
import android.view.Window;
|
import android.view.Window;
|
||||||
|
import android.widget.CheckBox;
|
||||||
|
|
||||||
import org.briarproject.briar.R;
|
import org.briarproject.briar.R;
|
||||||
import org.briarproject.briar.android.controller.BriarController;
|
import org.briarproject.briar.android.controller.BriarController;
|
||||||
import org.briarproject.briar.android.controller.DbController;
|
import org.briarproject.briar.android.controller.DbController;
|
||||||
|
import org.briarproject.briar.android.controller.handler.UiResultHandler;
|
||||||
import org.briarproject.briar.android.login.PasswordActivity;
|
import org.briarproject.briar.android.login.PasswordActivity;
|
||||||
import org.briarproject.briar.android.panic.ExitActivity;
|
import org.briarproject.briar.android.panic.ExitActivity;
|
||||||
|
|
||||||
@@ -25,7 +28,9 @@ import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK;
|
|||||||
import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
|
import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
|
||||||
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
|
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
|
||||||
import static android.content.Intent.FLAG_ACTIVITY_NO_ANIMATION;
|
import static android.content.Intent.FLAG_ACTIVITY_NO_ANIMATION;
|
||||||
|
import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_DOZE_WHITELISTING;
|
||||||
import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_PASSWORD;
|
import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_PASSWORD;
|
||||||
|
import static org.briarproject.briar.android.util.UiUtils.getDozeWhitelistingIntent;
|
||||||
|
|
||||||
@SuppressLint("Registered")
|
@SuppressLint("Registered")
|
||||||
public abstract class BriarActivity extends BaseActivity {
|
public abstract class BriarActivity extends BaseActivity {
|
||||||
@@ -59,6 +64,13 @@ public abstract class BriarActivity extends BaseActivity {
|
|||||||
if (!briarController.hasEncryptionKey() && !isFinishing()) {
|
if (!briarController.hasEncryptionKey() && !isFinishing()) {
|
||||||
Intent i = new Intent(this, PasswordActivity.class);
|
Intent i = new Intent(this, PasswordActivity.class);
|
||||||
startActivityForResult(i, REQUEST_PASSWORD);
|
startActivityForResult(i, REQUEST_PASSWORD);
|
||||||
|
} else {
|
||||||
|
briarController.hasDozed(new UiResultHandler<Boolean>(this) {
|
||||||
|
@Override
|
||||||
|
public void onResultUi(Boolean result) {
|
||||||
|
if (result) showDozeDialog();
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -97,6 +109,29 @@ public abstract class BriarActivity extends BaseActivity {
|
|||||||
return toolbar;
|
return toolbar;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void showDozeDialog() {
|
||||||
|
AlertDialog.Builder b =
|
||||||
|
new AlertDialog.Builder(this, R.style.BriarDialogTheme);
|
||||||
|
b.setMessage(getString(R.string.warning_dozed,
|
||||||
|
getString(R.string.app_name)));
|
||||||
|
b.setView(R.layout.checkbox);
|
||||||
|
b.setPositiveButton(R.string.fix,
|
||||||
|
(dialog, which) -> {
|
||||||
|
Intent i = getDozeWhitelistingIntent(BriarActivity.this);
|
||||||
|
startActivityForResult(i, REQUEST_DOZE_WHITELISTING);
|
||||||
|
dialog.dismiss();
|
||||||
|
});
|
||||||
|
b.setNegativeButton(R.string.cancel,
|
||||||
|
(dialog, which) -> dialog.dismiss());
|
||||||
|
b.setOnDismissListener(dialog -> {
|
||||||
|
CheckBox checkBox = (CheckBox) ((AlertDialog) dialog)
|
||||||
|
.findViewById(R.id.checkbox);
|
||||||
|
if (checkBox.isChecked())
|
||||||
|
briarController.doNotNotifyWhenDozed();
|
||||||
|
});
|
||||||
|
b.show();
|
||||||
|
}
|
||||||
|
|
||||||
protected void signOut(boolean removeFromRecentApps) {
|
protected void signOut(boolean removeFromRecentApps) {
|
||||||
if (briarController.hasEncryptionKey()) {
|
if (briarController.hasEncryptionKey()) {
|
||||||
// Don't use UiResultHandler because we want the result even if
|
// Don't use UiResultHandler because we want the result even if
|
||||||
|
|||||||
@@ -8,5 +8,13 @@ public interface BriarController extends ActivityLifecycleController {
|
|||||||
|
|
||||||
boolean hasEncryptionKey();
|
boolean hasEncryptionKey();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true via the handler when the app has dozed
|
||||||
|
* without being white-listed.
|
||||||
|
*/
|
||||||
|
void hasDozed(ResultHandler<Boolean> handler);
|
||||||
|
|
||||||
|
void doNotNotifyWhenDozed();
|
||||||
|
|
||||||
void signOut(ResultHandler<Void> eventHandler);
|
void signOut(ResultHandler<Void> eventHandler);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,30 +6,48 @@ import android.os.IBinder;
|
|||||||
import android.support.annotation.CallSuper;
|
import android.support.annotation.CallSuper;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.db.DatabaseConfig;
|
import org.briarproject.bramble.api.db.DatabaseConfig;
|
||||||
|
import org.briarproject.bramble.api.db.DatabaseExecutor;
|
||||||
|
import org.briarproject.bramble.api.db.DbException;
|
||||||
|
import org.briarproject.bramble.api.settings.Settings;
|
||||||
|
import org.briarproject.bramble.api.settings.SettingsManager;
|
||||||
import org.briarproject.briar.android.BriarService;
|
import org.briarproject.briar.android.BriarService;
|
||||||
import org.briarproject.briar.android.BriarService.BriarServiceConnection;
|
import org.briarproject.briar.android.BriarService.BriarServiceConnection;
|
||||||
import org.briarproject.briar.android.controller.handler.ResultHandler;
|
import org.briarproject.briar.android.controller.handler.ResultHandler;
|
||||||
|
|
||||||
|
import java.util.concurrent.Executor;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import static java.util.logging.Level.WARNING;
|
||||||
|
import static org.briarproject.briar.android.settings.SettingsFragment.SETTINGS_NAMESPACE;
|
||||||
|
import static org.briarproject.briar.android.util.UiUtils.needsDozeWhitelisting;
|
||||||
|
|
||||||
public class BriarControllerImpl implements BriarController {
|
public class BriarControllerImpl implements BriarController {
|
||||||
|
|
||||||
private static final Logger LOG =
|
private static final Logger LOG =
|
||||||
Logger.getLogger(BriarControllerImpl.class.getName());
|
Logger.getLogger(BriarControllerImpl.class.getName());
|
||||||
|
|
||||||
|
private static final String HAS_DOZED_ASK_AGAIN = "hasDozedAskAgain";
|
||||||
|
|
||||||
private final BriarServiceConnection serviceConnection;
|
private final BriarServiceConnection serviceConnection;
|
||||||
private final DatabaseConfig databaseConfig;
|
private final DatabaseConfig databaseConfig;
|
||||||
|
@DatabaseExecutor
|
||||||
|
private final Executor databaseExecutor;
|
||||||
|
private final SettingsManager settingsManager;
|
||||||
private final Activity activity;
|
private final Activity activity;
|
||||||
|
|
||||||
private boolean bound = false;
|
private boolean bound = false;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
BriarControllerImpl(BriarServiceConnection serviceConnection,
|
BriarControllerImpl(BriarServiceConnection serviceConnection,
|
||||||
DatabaseConfig databaseConfig, Activity activity) {
|
DatabaseConfig databaseConfig,
|
||||||
|
@DatabaseExecutor Executor databaseExecutor,
|
||||||
|
SettingsManager settingsManager, Activity activity) {
|
||||||
this.serviceConnection = serviceConnection;
|
this.serviceConnection = serviceConnection;
|
||||||
this.databaseConfig = databaseConfig;
|
this.databaseConfig = databaseConfig;
|
||||||
|
this.databaseExecutor = databaseExecutor;
|
||||||
|
this.settingsManager = settingsManager;
|
||||||
this.activity = activity;
|
this.activity = activity;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -65,6 +83,50 @@ public class BriarControllerImpl implements BriarController {
|
|||||||
return databaseConfig.getEncryptionKey() != null;
|
return databaseConfig.getEncryptionKey() != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void hasDozed(ResultHandler<Boolean> handler) {
|
||||||
|
// check this first, to hit the DbThread only when really necessary
|
||||||
|
if (!needsDozeWhitelisting(activity)) {
|
||||||
|
handler.onResult(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
databaseExecutor.execute(() -> {
|
||||||
|
try {
|
||||||
|
Settings settings =
|
||||||
|
settingsManager.getSettings(SETTINGS_NAMESPACE);
|
||||||
|
boolean ask = settings.getBoolean(HAS_DOZED_ASK_AGAIN, true);
|
||||||
|
if (!ask) {
|
||||||
|
handler.onResult(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
IBinder binder = serviceConnection.waitForBinder();
|
||||||
|
BriarService service =
|
||||||
|
((BriarService.BriarBinder) binder).getService();
|
||||||
|
handler.onResult(service.hasDozed());
|
||||||
|
service.resetDozeFlag();
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
LOG.warning("Interrupted while waiting for service");
|
||||||
|
} catch (DbException e) {
|
||||||
|
if (LOG.isLoggable(WARNING))
|
||||||
|
LOG.log(WARNING, e.toString(), e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void doNotNotifyWhenDozed() {
|
||||||
|
databaseExecutor.execute(() -> {
|
||||||
|
try {
|
||||||
|
Settings settings = new Settings();
|
||||||
|
settings.putBoolean(HAS_DOZED_ASK_AGAIN, false);
|
||||||
|
settingsManager.mergeSettings(settings, SETTINGS_NAMESPACE);
|
||||||
|
} catch (DbException e) {
|
||||||
|
if (LOG.isLoggable(WARNING))
|
||||||
|
LOG.log(WARNING, e.toString(), e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void signOut(ResultHandler<Void> eventHandler) {
|
public void signOut(ResultHandler<Void> eventHandler) {
|
||||||
new Thread() {
|
new Thread() {
|
||||||
|
|||||||
@@ -158,42 +158,37 @@ public class NavDrawerControllerImpl extends DbControllerImpl
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void askDozeWhitelisting(final Context ctx,
|
public void askDozeWhitelisting(Context ctx,
|
||||||
final ResultHandler<Boolean> handler) {
|
ResultHandler<Boolean> handler) {
|
||||||
|
// check this first, to hit the DbThread only when really necessary
|
||||||
if (!needsDozeWhitelisting(ctx)) {
|
if (!needsDozeWhitelisting(ctx)) {
|
||||||
handler.onResult(false);
|
handler.onResult(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
runOnDbThread(new Runnable() {
|
runOnDbThread(() -> {
|
||||||
@Override
|
try {
|
||||||
public void run() {
|
Settings settings =
|
||||||
try {
|
settingsManager.getSettings(SETTINGS_NAMESPACE);
|
||||||
Settings settings =
|
boolean ask = settings.getBoolean(DOZE_ASK_AGAIN, true);
|
||||||
settingsManager.getSettings(SETTINGS_NAMESPACE);
|
handler.onResult(ask);
|
||||||
boolean ask = settings.getBoolean(DOZE_ASK_AGAIN, true);
|
} catch (DbException e) {
|
||||||
handler.onResult(ask);
|
if (LOG.isLoggable(WARNING))
|
||||||
} catch (DbException e) {
|
LOG.log(WARNING, e.toString(), e);
|
||||||
if (LOG.isLoggable(WARNING))
|
handler.onResult(true);
|
||||||
LOG.log(WARNING, e.toString(), e);
|
|
||||||
handler.onResult(true);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void doNotAskAgainForDozeWhiteListing() {
|
public void doNotAskAgainForDozeWhiteListing() {
|
||||||
runOnDbThread(new Runnable() {
|
runOnDbThread(() -> {
|
||||||
@Override
|
try {
|
||||||
public void run() {
|
Settings settings = new Settings();
|
||||||
try {
|
settings.putBoolean(DOZE_ASK_AGAIN, false);
|
||||||
Settings settings = new Settings();
|
settingsManager.mergeSettings(settings, SETTINGS_NAMESPACE);
|
||||||
settings.putBoolean(DOZE_ASK_AGAIN, false);
|
} catch (DbException e) {
|
||||||
settingsManager.mergeSettings(settings, SETTINGS_NAMESPACE);
|
if (LOG.isLoggable(WARNING))
|
||||||
} catch (DbException e) {
|
LOG.log(WARNING, e.toString(), e);
|
||||||
if (LOG.isLoggable(WARNING))
|
|
||||||
LOG.log(WARNING, e.toString(), e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -95,6 +95,8 @@
|
|||||||
<string name="ellipsis">…</string>
|
<string name="ellipsis">…</string>
|
||||||
<string name="text_too_long">The entered text is too long</string>
|
<string name="text_too_long">The entered text is too long</string>
|
||||||
<string name="show_onboarding">Show Help Dialog</string>
|
<string name="show_onboarding">Show Help Dialog</string>
|
||||||
|
<string name="warning_dozed">%s was unable to run in the background</string>
|
||||||
|
<string name="fix">Fix</string>
|
||||||
|
|
||||||
<!-- Contacts and Private Conversations-->
|
<!-- Contacts and Private Conversations-->
|
||||||
<string name="no_contacts">It seems that you are new here and have no contacts yet.\n\nTap the + icon at the top and follow the instructions to add some friends to your list.\n\nPlease remember: You can only add new contacts face-to-face to prevent anyone from impersonating you or reading your messages in the future.</string>
|
<string name="no_contacts">It seems that you are new here and have no contacts yet.\n\nTap the + icon at the top and follow the instructions to add some friends to your list.\n\nPlease remember: You can only add new contacts face-to-face to prevent anyone from impersonating you or reading your messages in the future.</string>
|
||||||
@@ -391,7 +393,7 @@
|
|||||||
<string name="screen_filter_title">Screen overlay detected</string>
|
<string name="screen_filter_title">Screen overlay detected</string>
|
||||||
<string name="screen_filter_body">Another app is drawing on top of Briar. To protect your security, Briar will not respond to touches when another app is drawing on top.\n\nTry turning off the following apps when using Briar:\n\n%1$s</string>
|
<string name="screen_filter_body">Another app is drawing on top of Briar. To protect your security, Briar will not respond to touches when another app is drawing on top.\n\nTry turning off the following apps when using Briar:\n\n%1$s</string>
|
||||||
|
|
||||||
<!-- Permission Requests and Doze Mode -->
|
<!-- Permission Requests -->
|
||||||
<string name="permission_camera_title">Camera permission</string>
|
<string name="permission_camera_title">Camera permission</string>
|
||||||
<string name="permission_camera_request_body">To scan the QR code, Briar needs access to the camera.</string>
|
<string name="permission_camera_request_body">To scan the QR code, Briar needs access to the camera.</string>
|
||||||
<string name="permission_camera_denied_body">You have denied access to the camera, but adding contacts requires using the camera.\n\nPlease consider granting access.</string>
|
<string name="permission_camera_denied_body">You have denied access to the camera, but adding contacts requires using the camera.\n\nPlease consider granting access.</string>
|
||||||
|
|||||||
Reference in New Issue
Block a user