mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-13 19:29:06 +01:00
Merge branch 'master' into 'preference-switches'
# Conflicts: # briar-android/src/main/java/org/briarproject/briar/android/settings/SettingsFragment.java
This commit is contained in:
@@ -28,6 +28,7 @@ import org.briarproject.briar.android.login.SignInReminderReceiver;
|
||||
import org.briarproject.briar.android.reporting.BriarReportSender;
|
||||
import org.briarproject.briar.api.android.AndroidNotificationManager;
|
||||
import org.briarproject.briar.api.android.DozeWatchdog;
|
||||
import org.briarproject.briar.api.android.LockManager;
|
||||
import org.briarproject.briar.api.android.ScreenFilterMonitor;
|
||||
import org.briarproject.briar.api.blog.BlogManager;
|
||||
import org.briarproject.briar.api.blog.BlogPostFactory;
|
||||
@@ -146,6 +147,8 @@ public interface AndroidComponent
|
||||
|
||||
AccountManager accountManager();
|
||||
|
||||
LockManager lockManager();
|
||||
|
||||
void inject(SignInReminderReceiver briarService);
|
||||
|
||||
void inject(BriarService briarService);
|
||||
|
||||
@@ -2,6 +2,7 @@ package org.briarproject.briar.android;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.app.Application;
|
||||
import android.app.Notification;
|
||||
import android.app.NotificationChannel;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
@@ -12,7 +13,6 @@ import android.support.annotation.StringRes;
|
||||
import android.support.annotation.UiThread;
|
||||
import android.support.v4.app.NotificationCompat;
|
||||
import android.support.v4.app.TaskStackBuilder;
|
||||
import android.support.v4.content.ContextCompat;
|
||||
|
||||
import org.briarproject.bramble.api.Multiset;
|
||||
import org.briarproject.bramble.api.contact.ContactId;
|
||||
@@ -65,7 +65,6 @@ import javax.inject.Inject;
|
||||
import static android.app.Notification.DEFAULT_LIGHTS;
|
||||
import static android.app.Notification.DEFAULT_SOUND;
|
||||
import static android.app.Notification.DEFAULT_VIBRATE;
|
||||
import static android.app.Notification.VISIBILITY_SECRET;
|
||||
import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
|
||||
import static android.app.NotificationManager.IMPORTANCE_LOW;
|
||||
import static android.content.Context.NOTIFICATION_SERVICE;
|
||||
@@ -73,8 +72,12 @@ import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP;
|
||||
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
|
||||
import static android.os.Build.VERSION.SDK_INT;
|
||||
import static android.support.v4.app.NotificationCompat.CATEGORY_MESSAGE;
|
||||
import static android.support.v4.app.NotificationCompat.CATEGORY_SERVICE;
|
||||
import static android.support.v4.app.NotificationCompat.CATEGORY_SOCIAL;
|
||||
import static android.support.v4.app.NotificationCompat.PRIORITY_LOW;
|
||||
import static android.support.v4.app.NotificationCompat.PRIORITY_MIN;
|
||||
import static android.support.v4.app.NotificationCompat.VISIBILITY_SECRET;
|
||||
import static android.support.v4.content.ContextCompat.getColor;
|
||||
import static java.util.logging.Level.WARNING;
|
||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
||||
import static org.briarproject.briar.android.activity.BriarActivity.GROUP_ID;
|
||||
@@ -173,8 +176,7 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
|
||||
nc.setLockscreenVisibility(VISIBILITY_SECRET);
|
||||
nc.enableVibration(true);
|
||||
nc.enableLights(true);
|
||||
nc.setLightColor(
|
||||
ContextCompat.getColor(appContext, R.color.briar_green_light));
|
||||
nc.setLightColor(getColor(appContext, R.color.briar_green_light));
|
||||
notificationManager.createNotificationChannel(nc);
|
||||
}
|
||||
|
||||
@@ -271,6 +273,47 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
|
||||
});
|
||||
}
|
||||
|
||||
@UiThread
|
||||
@Override
|
||||
public Notification getForegroundNotification() {
|
||||
return getForegroundNotification(false);
|
||||
}
|
||||
|
||||
@UiThread
|
||||
private Notification getForegroundNotification(boolean locked) {
|
||||
int title = locked ? R.string.lock_is_locked :
|
||||
R.string.ongoing_notification_title;
|
||||
int text = locked ? R.string.lock_tap_to_unlock :
|
||||
R.string.ongoing_notification_text;
|
||||
int icon = locked ? R.drawable.startup_lock :
|
||||
R.drawable.notification_ongoing;
|
||||
// Ongoing foreground notification that shows BriarService is running
|
||||
NotificationCompat.Builder b =
|
||||
new NotificationCompat.Builder(appContext, ONGOING_CHANNEL_ID);
|
||||
b.setSmallIcon(icon);
|
||||
b.setColor(getColor(appContext, R.color.briar_primary));
|
||||
b.setContentTitle(appContext.getText(title));
|
||||
b.setContentText(appContext.getText(text));
|
||||
b.setWhen(0); // Don't show the time
|
||||
b.setOngoing(true);
|
||||
Intent i = new Intent(appContext, NavDrawerActivity.class);
|
||||
i.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TOP);
|
||||
b.setContentIntent(PendingIntent.getActivity(appContext, 0, i, 0));
|
||||
if (SDK_INT >= 21) {
|
||||
b.setCategory(CATEGORY_SERVICE);
|
||||
b.setVisibility(VISIBILITY_SECRET);
|
||||
}
|
||||
b.setPriority(PRIORITY_MIN);
|
||||
return b.build();
|
||||
}
|
||||
|
||||
@UiThread
|
||||
@Override
|
||||
public void updateForegroundNotification(boolean locked) {
|
||||
Notification n = getForegroundNotification(locked);
|
||||
notificationManager.notify(ONGOING_NOTIFICATION_ID, n);
|
||||
}
|
||||
|
||||
private void showContactNotification(ContactId c) {
|
||||
androidExecutor.runOnUiThread(() -> {
|
||||
if (blockContacts) return;
|
||||
@@ -636,7 +679,7 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
|
||||
NotificationCompat.Builder b =
|
||||
new NotificationCompat.Builder(appContext, REMINDER_CHANNEL_ID);
|
||||
b.setSmallIcon(R.drawable.ic_signout);
|
||||
b.setColor(ContextCompat.getColor(appContext, R.color.briar_primary));
|
||||
b.setColor(getColor(appContext, R.color.briar_primary));
|
||||
b.setContentTitle(
|
||||
appContext.getText(R.string.reminder_notification_title));
|
||||
b.setContentText(
|
||||
|
||||
@@ -29,8 +29,10 @@ import org.briarproject.bramble.plugin.tor.AndroidTorPluginFactory;
|
||||
import org.briarproject.bramble.plugin.tor.CircumventionProvider;
|
||||
import org.briarproject.bramble.util.AndroidUtils;
|
||||
import org.briarproject.bramble.util.StringUtils;
|
||||
import org.briarproject.briar.android.account.LockManagerImpl;
|
||||
import org.briarproject.briar.api.android.AndroidNotificationManager;
|
||||
import org.briarproject.briar.api.android.DozeWatchdog;
|
||||
import org.briarproject.briar.api.android.LockManager;
|
||||
import org.briarproject.briar.api.android.ScreenFilterMonitor;
|
||||
|
||||
import java.io.File;
|
||||
@@ -200,4 +202,13 @@ public class AppModule {
|
||||
return dozeWatchdog;
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
LockManager provideLockManager(LifecycleManager lifecycleManager,
|
||||
EventBus eventBus, LockManagerImpl lockManager) {
|
||||
lifecycleManager.registerService(lockManager);
|
||||
eventBus.addListener(lockManager);
|
||||
return lockManager;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package org.briarproject.briar.android;
|
||||
|
||||
import android.app.ActivityManager;
|
||||
import android.app.ActivityManager.RunningAppProcessInfo;
|
||||
import android.app.Notification;
|
||||
import android.app.NotificationChannel;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
@@ -15,7 +16,6 @@ import android.content.ServiceConnection;
|
||||
import android.os.Binder;
|
||||
import android.os.IBinder;
|
||||
import android.support.v4.app.NotificationCompat;
|
||||
import android.support.v4.content.ContextCompat;
|
||||
|
||||
import org.briarproject.bramble.api.account.AccountManager;
|
||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||
@@ -25,6 +25,7 @@ import org.briarproject.bramble.api.system.AndroidExecutor;
|
||||
import org.briarproject.briar.R;
|
||||
import org.briarproject.briar.android.logout.HideUiActivity;
|
||||
import org.briarproject.briar.android.navdrawer.NavDrawerActivity;
|
||||
import org.briarproject.briar.api.android.AndroidNotificationManager;
|
||||
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
@@ -44,8 +45,6 @@ import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
|
||||
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
|
||||
import static android.content.Intent.FLAG_ACTIVITY_NO_ANIMATION;
|
||||
import static android.os.Build.VERSION.SDK_INT;
|
||||
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.VISIBILITY_SECRET;
|
||||
import static java.util.logging.Level.INFO;
|
||||
import static java.util.logging.Level.WARNING;
|
||||
@@ -74,6 +73,8 @@ public class BriarService extends Service {
|
||||
@Nullable
|
||||
private BroadcastReceiver receiver = null;
|
||||
|
||||
@Inject
|
||||
AndroidNotificationManager notificationManager;
|
||||
@Inject
|
||||
AccountManager accountManager;
|
||||
|
||||
@@ -121,24 +122,9 @@ public class BriarService extends Service {
|
||||
failureChannel.setLockscreenVisibility(VISIBILITY_SECRET);
|
||||
nm.createNotificationChannel(failureChannel);
|
||||
}
|
||||
// Show an ongoing notification that the service is running
|
||||
NotificationCompat.Builder b =
|
||||
new NotificationCompat.Builder(this, ONGOING_CHANNEL_ID);
|
||||
b.setSmallIcon(R.drawable.notification_ongoing);
|
||||
b.setColor(ContextCompat.getColor(this, R.color.briar_primary));
|
||||
b.setContentTitle(getText(R.string.ongoing_notification_title));
|
||||
b.setContentText(getText(R.string.ongoing_notification_text));
|
||||
b.setWhen(0); // Don't show the time
|
||||
b.setOngoing(true);
|
||||
Intent i = new Intent(this, NavDrawerActivity.class);
|
||||
i.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TOP);
|
||||
b.setContentIntent(PendingIntent.getActivity(this, 0, i, 0));
|
||||
if (SDK_INT >= 21) {
|
||||
b.setCategory(CATEGORY_SERVICE);
|
||||
b.setVisibility(VISIBILITY_SECRET);
|
||||
}
|
||||
b.setPriority(PRIORITY_MIN);
|
||||
startForeground(ONGOING_NOTIFICATION_ID, b.build());
|
||||
Notification foregroundNotification =
|
||||
notificationManager.getForegroundNotification();
|
||||
startForeground(ONGOING_NOTIFICATION_ID, foregroundNotification);
|
||||
// Start the services in a background thread
|
||||
new Thread(() -> {
|
||||
StartResult result = lifecycleManager.startServices(dbKey);
|
||||
|
||||
@@ -40,4 +40,9 @@ public interface TestingConstants {
|
||||
* Feature flag for enabling the sign-in reminder in release builds.
|
||||
*/
|
||||
boolean FEATURE_FLAG_SIGN_IN_REMINDER = IS_DEBUG_BUILD;
|
||||
|
||||
/**
|
||||
* Feature flag for enabling the PIN lock in release builds.
|
||||
*/
|
||||
boolean FEATURE_FLAG_PIN_LOCK = IS_DEBUG_BUILD;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,132 @@
|
||||
package org.briarproject.briar.android.account;
|
||||
|
||||
import android.app.Application;
|
||||
import android.arch.lifecycle.LiveData;
|
||||
import android.arch.lifecycle.MutableLiveData;
|
||||
import android.content.Context;
|
||||
import android.support.annotation.UiThread;
|
||||
|
||||
import org.briarproject.bramble.api.db.DatabaseExecutor;
|
||||
import org.briarproject.bramble.api.db.DbException;
|
||||
import org.briarproject.bramble.api.event.Event;
|
||||
import org.briarproject.bramble.api.event.EventListener;
|
||||
import org.briarproject.bramble.api.lifecycle.Service;
|
||||
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
||||
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
||||
import org.briarproject.bramble.api.settings.Settings;
|
||||
import org.briarproject.bramble.api.settings.SettingsManager;
|
||||
import org.briarproject.bramble.api.settings.event.SettingsUpdatedEvent;
|
||||
import org.briarproject.briar.api.android.AndroidNotificationManager;
|
||||
import org.briarproject.briar.api.android.LockManager;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.annotation.concurrent.ThreadSafe;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static java.util.logging.Level.WARNING;
|
||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
||||
import static org.briarproject.briar.android.settings.SettingsFragment.PREF_SCREEN_LOCK;
|
||||
import static org.briarproject.briar.android.settings.SettingsFragment.SETTINGS_NAMESPACE;
|
||||
import static org.briarproject.briar.android.util.UiUtils.hasScreenLock;
|
||||
|
||||
@ThreadSafe
|
||||
@MethodsNotNullByDefault
|
||||
@ParametersNotNullByDefault
|
||||
public class LockManagerImpl implements LockManager, Service, EventListener {
|
||||
|
||||
private static final Logger LOG =
|
||||
Logger.getLogger(LockManagerImpl.class.getName());
|
||||
|
||||
private final Context appContext;
|
||||
private final SettingsManager settingsManager;
|
||||
private final AndroidNotificationManager notificationManager;
|
||||
@DatabaseExecutor
|
||||
private final Executor dbExecutor;
|
||||
|
||||
private volatile boolean locked = false;
|
||||
private volatile boolean lockableSetting = false;
|
||||
private final MutableLiveData<Boolean> lockable = new MutableLiveData<>();
|
||||
|
||||
@Inject
|
||||
public LockManagerImpl(Application app, SettingsManager settingsManager,
|
||||
AndroidNotificationManager notificationManager,
|
||||
@DatabaseExecutor Executor dbExecutor) {
|
||||
this.appContext = app.getApplicationContext();
|
||||
this.settingsManager = settingsManager;
|
||||
this.notificationManager = notificationManager;
|
||||
this.dbExecutor = dbExecutor;
|
||||
|
||||
// setting this in the constructor makes #getValue() @NonNull
|
||||
this.lockable.setValue(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startService() {
|
||||
// only load the setting here, because database isn't open before
|
||||
loadLockableSetting();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stopService() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public LiveData<Boolean> isLockable() {
|
||||
return lockable;
|
||||
}
|
||||
|
||||
@UiThread
|
||||
@Override
|
||||
public void checkIfLockable() {
|
||||
boolean oldValue = lockable.getValue();
|
||||
boolean newValue = hasScreenLock(appContext) && lockableSetting;
|
||||
if (oldValue != newValue) {
|
||||
this.lockable.setValue(newValue);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLocked() {
|
||||
if (locked && !hasScreenLock(appContext)) {
|
||||
lockable.postValue(false);
|
||||
locked = false;
|
||||
}
|
||||
return locked;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLocked(boolean locked) {
|
||||
this.locked = locked;
|
||||
notificationManager.updateForegroundNotification(locked);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void eventOccurred(Event event) {
|
||||
if (event instanceof SettingsUpdatedEvent) {
|
||||
SettingsUpdatedEvent e = (SettingsUpdatedEvent) event;
|
||||
String namespace = e.getNamespace();
|
||||
if (namespace.equals(SETTINGS_NAMESPACE)) {
|
||||
loadLockableSetting();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void loadLockableSetting() {
|
||||
dbExecutor.execute(() -> {
|
||||
try {
|
||||
Settings settings =
|
||||
settingsManager.getSettings(SETTINGS_NAMESPACE);
|
||||
lockableSetting = settings.getBoolean(PREF_SCREEN_LOCK, false);
|
||||
boolean newValue = hasScreenLock(appContext) && lockableSetting;
|
||||
lockable.postValue(newValue);
|
||||
} catch (DbException e) {
|
||||
logException(LOG, WARNING, e);
|
||||
lockableSetting = false;
|
||||
lockable.postValue(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -37,6 +37,7 @@ import org.briarproject.briar.android.login.OpenDatabaseActivity;
|
||||
import org.briarproject.briar.android.login.PasswordActivity;
|
||||
import org.briarproject.briar.android.login.PasswordFragment;
|
||||
import org.briarproject.briar.android.login.SetupActivity;
|
||||
import org.briarproject.briar.android.login.UnlockActivity;
|
||||
import org.briarproject.briar.android.navdrawer.NavDrawerActivity;
|
||||
import org.briarproject.briar.android.panic.PanicPreferencesActivity;
|
||||
import org.briarproject.briar.android.panic.PanicResponderActivity;
|
||||
@@ -163,6 +164,8 @@ public interface ActivityComponent {
|
||||
|
||||
void inject(StartupFailureActivity activity);
|
||||
|
||||
void inject(UnlockActivity activity);
|
||||
|
||||
// Fragments
|
||||
void inject(AuthorNameFragment fragment);
|
||||
|
||||
|
||||
@@ -16,7 +16,9 @@ import org.briarproject.briar.android.controller.BriarController;
|
||||
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.UnlockActivity;
|
||||
import org.briarproject.briar.android.logout.ExitActivity;
|
||||
import org.briarproject.briar.api.android.LockManager;
|
||||
|
||||
import java.util.logging.Logger;
|
||||
|
||||
@@ -30,6 +32,7 @@ import static android.content.Intent.FLAG_ACTIVITY_NO_ANIMATION;
|
||||
import static android.os.Build.VERSION.SDK_INT;
|
||||
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_UNLOCK;
|
||||
import static org.briarproject.briar.android.util.UiUtils.getDozeWhitelistingIntent;
|
||||
import static org.briarproject.briar.android.util.UiUtils.isSamsung7;
|
||||
|
||||
@@ -44,26 +47,43 @@ public abstract class BriarActivity extends BaseActivity {
|
||||
|
||||
@Inject
|
||||
BriarController briarController;
|
||||
|
||||
@Deprecated
|
||||
@Inject
|
||||
DbController dbController;
|
||||
@Inject
|
||||
protected LockManager lockManager;
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(int request, int result, Intent data) {
|
||||
super.onActivityResult(request, result, data);
|
||||
if (request == REQUEST_PASSWORD) {
|
||||
if (result == RESULT_OK) briarController.startAndBindService();
|
||||
else supportFinishAfterTransition();
|
||||
if (request == REQUEST_PASSWORD && result == RESULT_OK) {
|
||||
// PasswordActivity finishes when password was entered correctly.
|
||||
// When back button is pressed there, it will bring itself back,
|
||||
// so that we never arrive here with a result that is not OK.
|
||||
briarController.startAndBindService();
|
||||
} else if (request == REQUEST_UNLOCK && result != RESULT_OK) {
|
||||
// We arrive here, if the user presses 'back'
|
||||
// in the Keyguard unlock screen, because UnlockActivity finishes.
|
||||
// If we don't finish here, isFinishing will be false in onResume()
|
||||
// and we launch a new UnlockActivity causing a loop.
|
||||
supportFinishAfterTransition();
|
||||
// If the result is OK, we don't need to do anything here
|
||||
// and can resume normally.
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
if (!briarController.accountSignedIn() && !isFinishing()) {
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
if (!briarController.accountSignedIn()) {
|
||||
Intent i = new Intent(this, PasswordActivity.class);
|
||||
startActivityForResult(i, REQUEST_PASSWORD);
|
||||
} else if (lockManager.isLocked() && !isFinishing()) {
|
||||
// Also check that the activity isn't finishing already.
|
||||
// This is possible if finishing in onActivityResult().
|
||||
// Failure to do this check would cause an UnlockActivity loop.
|
||||
Intent i = new Intent(this, UnlockActivity.class);
|
||||
startActivityForResult(i, REQUEST_UNLOCK);
|
||||
} else if (SDK_INT >= 23) {
|
||||
briarController.hasDozed(new UiResultHandler<Boolean>(this) {
|
||||
@Override
|
||||
|
||||
@@ -12,5 +12,7 @@ public interface RequestCodes {
|
||||
int REQUEST_PERMISSION_CAMERA = 8;
|
||||
int REQUEST_DOZE_WHITELISTING = 9;
|
||||
int REQUEST_ENABLE_BLUETOOTH = 10;
|
||||
int REQUEST_UNLOCK = 11;
|
||||
int REQUEST_KEYGUARD_UNLOCK = 12;
|
||||
|
||||
}
|
||||
|
||||
@@ -23,8 +23,6 @@ import org.briarproject.briar.api.android.AndroidNotificationManager;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static android.content.Intent.ACTION_MAIN;
|
||||
import static android.content.Intent.CATEGORY_HOME;
|
||||
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK;
|
||||
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
|
||||
import static android.view.View.INVISIBLE;
|
||||
@@ -109,10 +107,9 @@ public class PasswordActivity extends BaseActivity {
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
// Show the home screen rather than another password prompt
|
||||
Intent intent = new Intent(ACTION_MAIN);
|
||||
intent.addCategory(CATEGORY_HOME);
|
||||
startActivity(intent);
|
||||
// Move task and activity to the background instead of showing another
|
||||
// password prompt. onActivityResult() won't be called in BriarActivity
|
||||
moveTaskToBack(true);
|
||||
}
|
||||
|
||||
private void deleteAccount() {
|
||||
|
||||
@@ -0,0 +1,123 @@
|
||||
package org.briarproject.briar.android.login;
|
||||
|
||||
import android.app.KeyguardManager;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.annotation.RequiresApi;
|
||||
import android.widget.Button;
|
||||
|
||||
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
||||
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
||||
import org.briarproject.briar.R;
|
||||
import org.briarproject.briar.android.activity.ActivityComponent;
|
||||
import org.briarproject.briar.android.activity.BaseActivity;
|
||||
import org.briarproject.briar.api.android.LockManager;
|
||||
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static android.os.Build.VERSION.SDK_INT;
|
||||
import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_KEYGUARD_UNLOCK;
|
||||
|
||||
@RequiresApi(21)
|
||||
@MethodsNotNullByDefault
|
||||
@ParametersNotNullByDefault
|
||||
public class UnlockActivity extends BaseActivity {
|
||||
|
||||
private static final Logger LOG =
|
||||
Logger.getLogger(UnlockActivity.class.getName());
|
||||
private static final String KEYGUARD_SHOWN = "keyguardShown";
|
||||
|
||||
@Inject
|
||||
LockManager lockManager;
|
||||
|
||||
private boolean keyguardShown = false;
|
||||
|
||||
@Override
|
||||
public void injectActivity(ActivityComponent component) {
|
||||
component.inject(this);
|
||||
}
|
||||
|
||||
public void onCreate(@Nullable Bundle state) {
|
||||
super.onCreate(state);
|
||||
overridePendingTransition(0, 0);
|
||||
setContentView(R.layout.activity_unlock);
|
||||
|
||||
Button button = findViewById(R.id.unlock);
|
||||
button.setOnClickListener(view -> requestKeyguardUnlock());
|
||||
|
||||
keyguardShown = state != null && state.getBoolean(KEYGUARD_SHOWN);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSaveInstanceState(Bundle outState) {
|
||||
super.onSaveInstanceState(outState);
|
||||
// Saving whether we've shown the keyguard already is necessary
|
||||
// for Android 6 when this activity gets destroyed.
|
||||
//
|
||||
// This will not help Android 5.
|
||||
// There the system will show the keyguard once again.
|
||||
// So if this activity was destroyed, the user needs to enter PIN twice.
|
||||
outState.putBoolean(KEYGUARD_SHOWN, keyguardShown);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode,
|
||||
Intent data) {
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
if (requestCode == REQUEST_KEYGUARD_UNLOCK) {
|
||||
if (resultCode == RESULT_OK) unlock();
|
||||
else {
|
||||
finish();
|
||||
overridePendingTransition(0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
// Show keyguard after onActivityResult() as been called.
|
||||
// Check if app is still locked, lockable
|
||||
// and not finishing (which is possible if recreated)
|
||||
if (!keyguardShown && lockManager.isLocked() && !isFinishing()) {
|
||||
requestKeyguardUnlock();
|
||||
} else if (!lockManager.isLocked()) {
|
||||
setResult(RESULT_OK);
|
||||
finish();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
moveTaskToBack(true);
|
||||
}
|
||||
|
||||
private void requestKeyguardUnlock() {
|
||||
KeyguardManager keyguardManager =
|
||||
(KeyguardManager) getSystemService(KEYGUARD_SERVICE);
|
||||
if (keyguardManager == null) throw new AssertionError();
|
||||
Intent intent = keyguardManager.createConfirmDeviceCredentialIntent(
|
||||
SDK_INT < 23 ? getString(R.string.lock_unlock_verbose) :
|
||||
getString(R.string.lock_unlock), null);
|
||||
if (intent == null) {
|
||||
// the user must have removed the screen lock since locked
|
||||
LOG.warning("Unlocking without keyguard");
|
||||
unlock();
|
||||
} else {
|
||||
keyguardShown = true;
|
||||
startActivityForResult(intent, REQUEST_KEYGUARD_UNLOCK);
|
||||
overridePendingTransition(0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
private void unlock() {
|
||||
lockManager.setLocked(false);
|
||||
setResult(RESULT_OK);
|
||||
finish();
|
||||
overridePendingTransition(0, 0);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -7,6 +7,7 @@ import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.design.widget.NavigationView;
|
||||
import android.support.design.widget.NavigationView.OnNavigationItemSelectedListener;
|
||||
import android.support.v4.app.ActivityCompat;
|
||||
import android.support.v4.app.FragmentManager;
|
||||
import android.support.v4.app.FragmentTransaction;
|
||||
import android.support.v4.content.ContextCompat;
|
||||
@@ -136,6 +137,8 @@ public class NavDrawerActivity extends BriarActivity implements
|
||||
initializeTransports(getLayoutInflater());
|
||||
transportsView.setAdapter(transportsAdapter);
|
||||
|
||||
lockManager.isLockable().observe(this, this::setLockVisible);
|
||||
|
||||
if (lifecycleManager.getLifecycleState().isAfter(RUNNING)) {
|
||||
showSignOutFragment();
|
||||
} else if (state == null) {
|
||||
@@ -152,6 +155,7 @@ public class NavDrawerActivity extends BriarActivity implements
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
updateTransports();
|
||||
lockManager.checkIfLockable();
|
||||
controller.showExpiryWarning(new UiResultHandler<ExpiryWarning>(this) {
|
||||
@Override
|
||||
public void onResultUi(ExpiryWarning expiry) {
|
||||
@@ -213,9 +217,15 @@ public class NavDrawerActivity extends BriarActivity implements
|
||||
public boolean onNavigationItemSelected(@NonNull MenuItem item) {
|
||||
drawerLayout.closeDrawer(START);
|
||||
clearBackStack();
|
||||
loadFragment(item.getItemId());
|
||||
// Don't display the Settings item as checked
|
||||
return item.getItemId() != R.id.nav_btn_settings;
|
||||
if (item.getItemId() == R.id.nav_btn_lock) {
|
||||
lockManager.setLocked(true);
|
||||
ActivityCompat.finishAfterTransition(this);
|
||||
return false;
|
||||
} else {
|
||||
loadFragment(item.getItemId());
|
||||
// Don't display the Settings item as checked
|
||||
return item.getItemId() != R.id.nav_btn_settings;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -301,6 +311,11 @@ public class NavDrawerActivity extends BriarActivity implements
|
||||
// Do nothing for now
|
||||
}
|
||||
|
||||
private void setLockVisible(boolean visible) {
|
||||
MenuItem item = navigation.getMenu().findItem(R.id.nav_btn_lock);
|
||||
if (item != null) item.setVisible(visible);
|
||||
}
|
||||
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
private void showExpiryWarning(ExpiryWarning expiry) {
|
||||
int daysUntilExpiry = getDaysUntilExpiry();
|
||||
|
||||
@@ -82,10 +82,12 @@ import static org.briarproject.bramble.util.LogUtils.logException;
|
||||
import static org.briarproject.bramble.util.LogUtils.now;
|
||||
import static org.briarproject.bramble.util.StringUtils.join;
|
||||
import static org.briarproject.briar.android.TestingConstants.FEATURE_FLAG_DARK_THEME;
|
||||
import static org.briarproject.briar.android.TestingConstants.FEATURE_FLAG_PIN_LOCK;
|
||||
import static org.briarproject.briar.android.TestingConstants.FEATURE_FLAG_SIGN_IN_REMINDER;
|
||||
import static org.briarproject.briar.android.TestingConstants.IS_DEBUG_BUILD;
|
||||
import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_RINGTONE;
|
||||
import static org.briarproject.briar.android.navdrawer.NavDrawerActivity.INTENT_SIGN_OUT;
|
||||
import static org.briarproject.briar.android.util.UiUtils.hasScreenLock;
|
||||
import static org.briarproject.briar.api.android.AndroidNotificationManager.BLOG_CHANNEL_ID;
|
||||
import static org.briarproject.briar.api.android.AndroidNotificationManager.CONTACT_CHANNEL_ID;
|
||||
import static org.briarproject.briar.api.android.AndroidNotificationManager.FORUM_CHANNEL_ID;
|
||||
@@ -109,6 +111,7 @@ public class SettingsFragment extends PreferenceFragmentCompat
|
||||
public static final String BT_NAMESPACE = BluetoothConstants.ID.getString();
|
||||
public static final String TOR_NAMESPACE = TorConstants.ID.getString();
|
||||
public static final String LANGUAGE = "pref_key_language";
|
||||
public static final String PREF_SCREEN_LOCK = "pref_key_lock";
|
||||
public static final String NOTIFY_SIGN_IN = "pref_key_notify_sign_in";
|
||||
public static final String TOR_LOCATION = "pref_key_tor_location";
|
||||
|
||||
@@ -120,13 +123,13 @@ public class SettingsFragment extends PreferenceFragmentCompat
|
||||
private ListPreference enableBluetooth;
|
||||
private ListPreference torNetwork;
|
||||
private SwitchPreference torBlocked;
|
||||
private SwitchPreference screenLock;
|
||||
private SwitchPreference notifyPrivateMessages;
|
||||
private SwitchPreference notifyGroupMessages;
|
||||
private SwitchPreference notifyForumPosts;
|
||||
private SwitchPreference notifyBlogPosts;
|
||||
private SwitchPreference notifyVibration;
|
||||
private SwitchPreference notifyLockscreen;
|
||||
|
||||
private Preference notifySound;
|
||||
|
||||
// Fields that are accessed from background threads must be volatile
|
||||
@@ -162,6 +165,7 @@ public class SettingsFragment extends PreferenceFragmentCompat
|
||||
setBlockedCountries();
|
||||
SwitchPreference notifySignIn =
|
||||
(SwitchPreference) findPreference(NOTIFY_SIGN_IN);
|
||||
screenLock = (SwitchPreference) findPreference(PREF_SCREEN_LOCK);
|
||||
notifyPrivateMessages = (SwitchPreference) findPreference(
|
||||
"pref_key_notify_private_messages");
|
||||
notifyGroupMessages = (SwitchPreference) findPreference(
|
||||
@@ -176,8 +180,6 @@ public class SettingsFragment extends PreferenceFragmentCompat
|
||||
"pref_key_notify_lock_screen");
|
||||
notifySound = findPreference("pref_key_notify_sound");
|
||||
|
||||
setSettingsEnabled(false);
|
||||
|
||||
language.setOnPreferenceChangeListener(this);
|
||||
theme.setOnPreferenceChangeListener((preference, newValue) -> {
|
||||
if (getActivity() != null) {
|
||||
@@ -200,6 +202,7 @@ public class SettingsFragment extends PreferenceFragmentCompat
|
||||
enableBluetooth.setOnPreferenceChangeListener(this);
|
||||
torNetwork.setOnPreferenceChangeListener(this);
|
||||
torBlocked.setOnPreferenceChangeListener(this);
|
||||
screenLock.setOnPreferenceChangeListener(this);
|
||||
if (SDK_INT >= 21) {
|
||||
notifyLockscreen.setVisible(true);
|
||||
notifyLockscreen.setOnPreferenceChangeListener(this);
|
||||
@@ -220,6 +223,7 @@ public class SettingsFragment extends PreferenceFragmentCompat
|
||||
} else {
|
||||
theme.setVisible(FEATURE_FLAG_DARK_THEME);
|
||||
notifySignIn.setVisible(FEATURE_FLAG_SIGN_IN_REMINDER);
|
||||
screenLock.setVisible(FEATURE_FLAG_PIN_LOCK);
|
||||
|
||||
findPreference("pref_key_explode").setVisible(false);
|
||||
findPreference("pref_key_test_data").setVisible(false);
|
||||
@@ -229,7 +233,6 @@ public class SettingsFragment extends PreferenceFragmentCompat
|
||||
testing.setVisible(false);
|
||||
}
|
||||
|
||||
loadSettings();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -246,6 +249,8 @@ public class SettingsFragment extends PreferenceFragmentCompat
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
eventBus.addListener(this);
|
||||
setSettingsEnabled(false);
|
||||
loadSettings();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -354,6 +359,7 @@ public class SettingsFragment extends PreferenceFragmentCompat
|
||||
enableBluetooth.setValue(Boolean.toString(btSetting));
|
||||
torNetwork.setValue(Integer.toString(torNetworkSetting));
|
||||
torBlocked.setChecked(torBlockedSetting);
|
||||
displayScreenLockSetting();
|
||||
|
||||
if (SDK_INT < 26) {
|
||||
notifyPrivateMessages.setChecked(settings.getBoolean(
|
||||
@@ -411,9 +417,12 @@ public class SettingsFragment extends PreferenceFragmentCompat
|
||||
// preferences not needed here, because handled by SharedPreferences:
|
||||
// - pref_key_theme
|
||||
// - pref_key_notify_sign_in
|
||||
// preferences partly needed here, because they have their own logic
|
||||
// - pref_key_lock (screenLock -> displayScreenLockSetting())
|
||||
enableBluetooth.setEnabled(enabled);
|
||||
torNetwork.setEnabled(enabled);
|
||||
torBlocked.setEnabled(enabled);
|
||||
if (!enabled) screenLock.setEnabled(false);
|
||||
notifyPrivateMessages.setEnabled(enabled);
|
||||
notifyGroupMessages.setEnabled(enabled);
|
||||
notifyForumPosts.setEnabled(enabled);
|
||||
@@ -423,6 +432,23 @@ public class SettingsFragment extends PreferenceFragmentCompat
|
||||
notifySound.setEnabled(enabled);
|
||||
}
|
||||
|
||||
private void displayScreenLockSetting() {
|
||||
if (SDK_INT < 21) {
|
||||
screenLock.setVisible(false);
|
||||
} else {
|
||||
if (getActivity() != null && hasScreenLock(getActivity())) {
|
||||
screenLock.setEnabled(true);
|
||||
screenLock.setChecked(
|
||||
settings.getBoolean(PREF_SCREEN_LOCK, false));
|
||||
screenLock.setSummary(R.string.pref_lock_summary);
|
||||
} else {
|
||||
screenLock.setEnabled(false);
|
||||
screenLock.setChecked(false);
|
||||
screenLock.setSummary(R.string.pref_lock_disabled_summary);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@TargetApi(26)
|
||||
private void setupNotificationPreference(SwitchPreference pref,
|
||||
String channelId, @StringRes int summary) {
|
||||
@@ -478,6 +504,10 @@ public class SettingsFragment extends PreferenceFragmentCompat
|
||||
} else if (preference == torBlocked) {
|
||||
boolean torBlockedSetting = (Boolean) newValue;
|
||||
storeTorBlockedSetting(torBlockedSetting);
|
||||
} else if (preference == screenLock) {
|
||||
Settings s = new Settings();
|
||||
s.putBoolean(PREF_SCREEN_LOCK, (Boolean) newValue);
|
||||
storeSettings(s);
|
||||
} else if (preference == notifyPrivateMessages) {
|
||||
Settings s = new Settings();
|
||||
s.putBoolean(PREF_NOTIFY_PRIVATE, (Boolean) newValue);
|
||||
|
||||
@@ -14,6 +14,8 @@ import org.briarproject.briar.android.activity.ActivityComponent;
|
||||
import org.briarproject.briar.android.activity.BaseActivity;
|
||||
import org.briarproject.briar.android.login.OpenDatabaseActivity;
|
||||
import org.briarproject.briar.android.login.SetupActivity;
|
||||
import org.briarproject.briar.android.navdrawer.NavDrawerActivity;
|
||||
import org.briarproject.briar.api.android.LockManager;
|
||||
|
||||
import java.util.logging.Logger;
|
||||
|
||||
@@ -29,6 +31,8 @@ public class SplashScreenActivity extends BaseActivity {
|
||||
@Inject
|
||||
protected AccountManager accountManager;
|
||||
@Inject
|
||||
protected LockManager lockManager;
|
||||
@Inject
|
||||
protected AndroidExecutor androidExecutor;
|
||||
|
||||
@Override
|
||||
@@ -44,7 +48,16 @@ public class SplashScreenActivity extends BaseActivity {
|
||||
setContentView(R.layout.splash);
|
||||
|
||||
if (accountManager.hasDatabaseKey()) {
|
||||
startActivity(new Intent(this, OpenDatabaseActivity.class));
|
||||
Intent i;
|
||||
if (lockManager.isLocked()) {
|
||||
// The database needs to be opened for the app to be locked.
|
||||
// Start main activity right away. It will open UnlockActivity.
|
||||
// Otherwise, we would end up with two screen unlock inputs.
|
||||
i = new Intent(this, NavDrawerActivity.class);
|
||||
} else {
|
||||
i = new Intent(this, OpenDatabaseActivity.class);
|
||||
}
|
||||
startActivity(i);
|
||||
finish();
|
||||
} else {
|
||||
new Handler().postDelayed(() -> {
|
||||
|
||||
@@ -2,6 +2,7 @@ package org.briarproject.briar.android.util;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.annotation.TargetApi;
|
||||
import android.app.KeyguardManager;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface.OnClickListener;
|
||||
import android.content.Intent;
|
||||
@@ -36,6 +37,7 @@ import org.briarproject.briar.android.widget.LinkDialogFragment;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import static android.content.Context.KEYGUARD_SERVICE;
|
||||
import static android.content.Context.POWER_SERVICE;
|
||||
import static android.content.Intent.CATEGORY_DEFAULT;
|
||||
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
|
||||
@@ -229,4 +231,15 @@ public class UiUtils {
|
||||
return ContextCompat.getColor(ctx, color);
|
||||
}
|
||||
|
||||
public static boolean hasScreenLock(Context ctx) {
|
||||
if (SDK_INT < 21) return false;
|
||||
KeyguardManager keyguardManager =
|
||||
(KeyguardManager) ctx.getSystemService(KEYGUARD_SERVICE);
|
||||
if (keyguardManager == null) return false;
|
||||
// check if there's a lock mechanism we can use
|
||||
// first one is true if SIM card is locked, so use second if available
|
||||
return (SDK_INT < 23 && keyguardManager.isKeyguardSecure()) ||
|
||||
(SDK_INT >= 23 && keyguardManager.isDeviceSecure());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package org.briarproject.briar.api.android;
|
||||
|
||||
import android.app.Notification;
|
||||
|
||||
import org.briarproject.bramble.api.contact.ContactId;
|
||||
import org.briarproject.bramble.api.sync.GroupId;
|
||||
|
||||
@@ -52,6 +54,10 @@ public interface AndroidNotificationManager {
|
||||
// Actions for pending intents
|
||||
String ACTION_DISMISS_REMINDER = "dismissReminder";
|
||||
|
||||
Notification getForegroundNotification();
|
||||
|
||||
void updateForegroundNotification(boolean locked);
|
||||
|
||||
void clearContactNotification(ContactId c);
|
||||
|
||||
void clearAllContactNotifications();
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
package org.briarproject.briar.api.android;
|
||||
|
||||
import android.arch.lifecycle.LiveData;
|
||||
import android.support.annotation.UiThread;
|
||||
|
||||
public interface LockManager {
|
||||
|
||||
/**
|
||||
* Returns an observable LiveData to indicate whether the app can be locked.
|
||||
*/
|
||||
LiveData<Boolean> isLockable();
|
||||
|
||||
/**
|
||||
* Updates the LiveData returned by {@link #isLockable()}.
|
||||
* It checks whether a device screen lock is available and
|
||||
* whether the app setting is checked.
|
||||
*/
|
||||
@UiThread
|
||||
void checkIfLockable();
|
||||
|
||||
/**
|
||||
* Returns true if app is currently locked, false otherwise.
|
||||
* If the device's screen lock was removed while the app was locked,
|
||||
* calling this will unlock the app automatically.
|
||||
*/
|
||||
boolean isLocked();
|
||||
|
||||
/**
|
||||
* Locks the app if true is passed, otherwise unlocks the app.
|
||||
*/
|
||||
void setLocked(boolean locked);
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user