mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-12 10:49:06 +01:00
Merge branch '1247-pin-lock-activity-timeout' into 'master'
Screen Lock: Lock after customizable inactivity timeout See merge request briar/briar!887
This commit is contained in:
@@ -26,6 +26,7 @@ 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 org.briarproject.briar.api.android.LockManager;
|
||||
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
@@ -54,6 +55,7 @@ import static org.briarproject.briar.api.android.AndroidNotificationManager.FAIL
|
||||
import static org.briarproject.briar.api.android.AndroidNotificationManager.FAILURE_NOTIFICATION_ID;
|
||||
import static org.briarproject.briar.api.android.AndroidNotificationManager.ONGOING_CHANNEL_ID;
|
||||
import static org.briarproject.briar.api.android.AndroidNotificationManager.ONGOING_NOTIFICATION_ID;
|
||||
import static org.briarproject.briar.api.android.LockManager.ACTION_LOCK;
|
||||
|
||||
public class BriarService extends Service {
|
||||
|
||||
@@ -77,6 +79,8 @@ public class BriarService extends Service {
|
||||
AndroidNotificationManager notificationManager;
|
||||
@Inject
|
||||
AccountManager accountManager;
|
||||
@Inject
|
||||
LockManager lockManager;
|
||||
|
||||
// Fields that are accessed from background threads must be volatile
|
||||
@Inject
|
||||
@@ -189,6 +193,9 @@ public class BriarService extends Service {
|
||||
|
||||
@Override
|
||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||
if (ACTION_LOCK.equals(intent.getAction())) {
|
||||
lockManager.setLocked(true);
|
||||
}
|
||||
return START_NOT_STICKY; // Don't restart automatically if killed
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
package org.briarproject.briar.android.account;
|
||||
|
||||
import android.app.AlarmManager;
|
||||
import android.app.Application;
|
||||
import android.app.PendingIntent;
|
||||
import android.arch.lifecycle.LiveData;
|
||||
import android.arch.lifecycle.MutableLiveData;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.support.annotation.UiThread;
|
||||
|
||||
import org.briarproject.bramble.api.db.DatabaseExecutor;
|
||||
@@ -16,6 +19,8 @@ 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.R;
|
||||
import org.briarproject.briar.android.BriarService;
|
||||
import org.briarproject.briar.api.android.AndroidNotificationManager;
|
||||
import org.briarproject.briar.api.android.LockManager;
|
||||
|
||||
@@ -25,9 +30,15 @@ import java.util.logging.Logger;
|
||||
import javax.annotation.concurrent.ThreadSafe;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static android.app.AlarmManager.ELAPSED_REALTIME;
|
||||
import static android.app.PendingIntent.getService;
|
||||
import static android.content.Context.ALARM_SERVICE;
|
||||
import static android.os.SystemClock.elapsedRealtime;
|
||||
import static java.util.concurrent.TimeUnit.MINUTES;
|
||||
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.PREF_SCREEN_LOCK_TIMEOUT;
|
||||
import static org.briarproject.briar.android.settings.SettingsFragment.SETTINGS_NAMESPACE;
|
||||
import static org.briarproject.briar.android.util.UiUtils.hasScreenLock;
|
||||
|
||||
@@ -44,19 +55,39 @@ public class LockManagerImpl implements LockManager, Service, EventListener {
|
||||
private final AndroidNotificationManager notificationManager;
|
||||
@DatabaseExecutor
|
||||
private final Executor dbExecutor;
|
||||
private final AlarmManager alarmManager;
|
||||
private final PendingIntent lockIntent;
|
||||
private final int timeoutNever, timeoutDefault;
|
||||
|
||||
private volatile boolean locked = false;
|
||||
private volatile boolean lockableSetting = false;
|
||||
private volatile int timeoutMinutes;
|
||||
private int activitiesRunning = 0;
|
||||
private boolean alarmSet = false;
|
||||
// This is to ensure that we don't start unlocked after a timeout and thus
|
||||
// is set to the elapsed real time when no more activities are running.
|
||||
// Its value is only relevant as long as no activity is running.
|
||||
private long idleTime;
|
||||
private final MutableLiveData<Boolean> lockable = new MutableLiveData<>();
|
||||
|
||||
@Inject
|
||||
public LockManagerImpl(Application app, SettingsManager settingsManager,
|
||||
LockManagerImpl(Application app, SettingsManager settingsManager,
|
||||
AndroidNotificationManager notificationManager,
|
||||
@DatabaseExecutor Executor dbExecutor) {
|
||||
this.appContext = app.getApplicationContext();
|
||||
this.settingsManager = settingsManager;
|
||||
this.notificationManager = notificationManager;
|
||||
this.dbExecutor = dbExecutor;
|
||||
this.alarmManager =
|
||||
(AlarmManager) appContext.getSystemService(ALARM_SERVICE);
|
||||
Intent i =
|
||||
new Intent(ACTION_LOCK, null, appContext, BriarService.class);
|
||||
this.lockIntent = getService(appContext, 0, i, 0);
|
||||
this.timeoutNever = Integer.valueOf(
|
||||
appContext.getString(R.string.pref_lock_timeout_value_never));
|
||||
this.timeoutDefault = Integer.valueOf(
|
||||
appContext.getString(R.string.pref_lock_timeout_value_default));
|
||||
this.timeoutMinutes = timeoutNever;
|
||||
|
||||
// setting this in the constructor makes #getValue() @NonNull
|
||||
this.lockable.setValue(false);
|
||||
@@ -72,6 +103,37 @@ public class LockManagerImpl implements LockManager, Service, EventListener {
|
||||
public void stopService() {
|
||||
}
|
||||
|
||||
@UiThread
|
||||
@Override
|
||||
public void onActivityStart() {
|
||||
if (!locked && activitiesRunning == 0 && timeoutEnabled() &&
|
||||
timedOut()) {
|
||||
// lock the app in case the alarm wasn't run during sleep
|
||||
setLocked(true);
|
||||
}
|
||||
activitiesRunning++;
|
||||
if (alarmSet) {
|
||||
alarmManager.cancel(lockIntent);
|
||||
alarmSet = false;
|
||||
}
|
||||
}
|
||||
|
||||
@UiThread
|
||||
@Override
|
||||
public void onActivityStop() {
|
||||
activitiesRunning--;
|
||||
if (activitiesRunning == 0) {
|
||||
idleTime = elapsedRealtime();
|
||||
if (!locked && timeoutEnabled()) {
|
||||
if (alarmSet) alarmManager.cancel(lockIntent);
|
||||
long triggerAt =
|
||||
elapsedRealtime() + MINUTES.toMillis(timeoutMinutes);
|
||||
alarmManager.set(ELAPSED_REALTIME, triggerAt, lockIntent);
|
||||
alarmSet = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public LiveData<Boolean> isLockable() {
|
||||
return lockable;
|
||||
@@ -92,6 +154,9 @@ public class LockManagerImpl implements LockManager, Service, EventListener {
|
||||
if (locked && !hasScreenLock(appContext)) {
|
||||
lockable.postValue(false);
|
||||
locked = false;
|
||||
} else if (!locked && activitiesRunning == 0 && timeoutEnabled() &&
|
||||
timedOut()) {
|
||||
setLocked(true);
|
||||
}
|
||||
return locked;
|
||||
}
|
||||
@@ -118,9 +183,13 @@ public class LockManagerImpl implements LockManager, Service, EventListener {
|
||||
try {
|
||||
Settings settings =
|
||||
settingsManager.getSettings(SETTINGS_NAMESPACE);
|
||||
// is the app lockable?
|
||||
lockableSetting = settings.getBoolean(PREF_SCREEN_LOCK, false);
|
||||
boolean newValue = hasScreenLock(appContext) && lockableSetting;
|
||||
lockable.postValue(newValue);
|
||||
// what is the timeout in minutes?
|
||||
timeoutMinutes = settings.getInt(PREF_SCREEN_LOCK_TIMEOUT,
|
||||
timeoutDefault);
|
||||
} catch (DbException e) {
|
||||
logException(LOG, WARNING, e);
|
||||
lockableSetting = false;
|
||||
@@ -129,4 +198,12 @@ public class LockManagerImpl implements LockManager, Service, EventListener {
|
||||
});
|
||||
}
|
||||
|
||||
private boolean timeoutEnabled() {
|
||||
return timeoutMinutes != timeoutNever && lockable.getValue();
|
||||
}
|
||||
|
||||
private boolean timedOut() {
|
||||
return elapsedRealtime() - idleTime > MINUTES.toMillis(timeoutMinutes);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -53,6 +53,12 @@ public abstract class BriarActivity extends BaseActivity {
|
||||
@Inject
|
||||
protected LockManager lockManager;
|
||||
|
||||
@Override
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
lockManager.onActivityStart();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(int request, int result, Intent data) {
|
||||
super.onActivityResult(request, result, data);
|
||||
@@ -97,6 +103,12 @@ public abstract class BriarActivity extends BaseActivity {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStop() {
|
||||
super.onStop();
|
||||
lockManager.onActivityStop();
|
||||
}
|
||||
|
||||
public void setSceneTransitionAnimation() {
|
||||
if (SDK_INT < 21) return;
|
||||
// workaround for #1007
|
||||
|
||||
@@ -15,6 +15,7 @@ import android.support.v4.content.ContextCompat;
|
||||
import android.support.v4.text.TextUtilsCompat;
|
||||
import android.support.v7.preference.ListPreference;
|
||||
import android.support.v7.preference.Preference;
|
||||
import android.support.v7.preference.Preference.OnPreferenceChangeListener;
|
||||
import android.support.v7.preference.PreferenceFragmentCompat;
|
||||
import android.support.v7.preference.PreferenceGroup;
|
||||
import android.view.LayoutInflater;
|
||||
@@ -105,13 +106,15 @@ import static org.briarproject.briar.api.android.AndroidNotificationManager.PREF
|
||||
@MethodsNotNullByDefault
|
||||
@ParametersNotNullByDefault
|
||||
public class SettingsFragment extends PreferenceFragmentCompat
|
||||
implements EventListener, Preference.OnPreferenceChangeListener {
|
||||
implements EventListener, OnPreferenceChangeListener {
|
||||
|
||||
public static final String SETTINGS_NAMESPACE = "android-ui";
|
||||
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 PREF_SCREEN_LOCK_TIMEOUT =
|
||||
"pref_key_lock_timeout";
|
||||
public static final String NOTIFY_SIGN_IN = "pref_key_notify_sign_in";
|
||||
public static final String TOR_LOCATION = "pref_key_tor_location";
|
||||
|
||||
@@ -124,6 +127,7 @@ public class SettingsFragment extends PreferenceFragmentCompat
|
||||
private ListPreference torNetwork;
|
||||
private SwitchPreference torBlocked;
|
||||
private SwitchPreference screenLock;
|
||||
private ListPreference screenLockTimeout;
|
||||
private SwitchPreference notifyPrivateMessages;
|
||||
private SwitchPreference notifyGroupMessages;
|
||||
private SwitchPreference notifyForumPosts;
|
||||
@@ -166,6 +170,8 @@ public class SettingsFragment extends PreferenceFragmentCompat
|
||||
SwitchPreference notifySignIn =
|
||||
(SwitchPreference) findPreference(NOTIFY_SIGN_IN);
|
||||
screenLock = (SwitchPreference) findPreference(PREF_SCREEN_LOCK);
|
||||
screenLockTimeout =
|
||||
(ListPreference) findPreference(PREF_SCREEN_LOCK_TIMEOUT);
|
||||
notifyPrivateMessages = (SwitchPreference) findPreference(
|
||||
"pref_key_notify_private_messages");
|
||||
notifyGroupMessages = (SwitchPreference) findPreference(
|
||||
@@ -203,6 +209,7 @@ public class SettingsFragment extends PreferenceFragmentCompat
|
||||
torNetwork.setOnPreferenceChangeListener(this);
|
||||
torBlocked.setOnPreferenceChangeListener(this);
|
||||
screenLock.setOnPreferenceChangeListener(this);
|
||||
screenLockTimeout.setOnPreferenceChangeListener(this);
|
||||
if (SDK_INT >= 21) {
|
||||
notifyLockscreen.setVisible(true);
|
||||
notifyLockscreen.setOnPreferenceChangeListener(this);
|
||||
@@ -224,6 +231,7 @@ public class SettingsFragment extends PreferenceFragmentCompat
|
||||
theme.setVisible(FEATURE_FLAG_DARK_THEME);
|
||||
notifySignIn.setVisible(FEATURE_FLAG_SIGN_IN_REMINDER);
|
||||
screenLock.setVisible(FEATURE_FLAG_PIN_LOCK);
|
||||
screenLockTimeout.setVisible(FEATURE_FLAG_PIN_LOCK);
|
||||
|
||||
findPreference("pref_key_explode").setVisible(false);
|
||||
findPreference("pref_key_test_data").setVisible(false);
|
||||
@@ -419,6 +427,7 @@ public class SettingsFragment extends PreferenceFragmentCompat
|
||||
// - pref_key_notify_sign_in
|
||||
// preferences partly needed here, because they have their own logic
|
||||
// - pref_key_lock (screenLock -> displayScreenLockSetting())
|
||||
// - pref_key_lock_timeout (screenLockTimeout)
|
||||
enableBluetooth.setEnabled(enabled);
|
||||
torNetwork.setEnabled(enabled);
|
||||
torBlocked.setEnabled(enabled);
|
||||
@@ -435,6 +444,7 @@ public class SettingsFragment extends PreferenceFragmentCompat
|
||||
private void displayScreenLockSetting() {
|
||||
if (SDK_INT < 21) {
|
||||
screenLock.setVisible(false);
|
||||
screenLockTimeout.setVisible(false);
|
||||
} else {
|
||||
if (getActivity() != null && hasScreenLock(getActivity())) {
|
||||
screenLock.setEnabled(true);
|
||||
@@ -446,6 +456,24 @@ public class SettingsFragment extends PreferenceFragmentCompat
|
||||
screenLock.setChecked(false);
|
||||
screenLock.setSummary(R.string.pref_lock_disabled_summary);
|
||||
}
|
||||
// timeout depends on screenLock and gets disabled automatically
|
||||
int timeout = settings.getInt(PREF_SCREEN_LOCK_TIMEOUT,
|
||||
Integer.valueOf(getString(
|
||||
R.string.pref_lock_timeout_value_default)));
|
||||
String newValue = String.valueOf(timeout);
|
||||
screenLockTimeout.setValue(newValue);
|
||||
setScreenLockTimeoutSummary(newValue);
|
||||
}
|
||||
}
|
||||
|
||||
private void setScreenLockTimeoutSummary(String timeout) {
|
||||
String never = getString(R.string.pref_lock_timeout_value_never);
|
||||
if (timeout.equals(never)) {
|
||||
screenLockTimeout
|
||||
.setSummary(R.string.pref_lock_timeout_never_summary);
|
||||
} else {
|
||||
screenLockTimeout
|
||||
.setSummary(R.string.pref_lock_timeout_summary);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -508,6 +536,12 @@ public class SettingsFragment extends PreferenceFragmentCompat
|
||||
Settings s = new Settings();
|
||||
s.putBoolean(PREF_SCREEN_LOCK, (Boolean) newValue);
|
||||
storeSettings(s);
|
||||
} else if (preference == screenLockTimeout) {
|
||||
Settings s = new Settings();
|
||||
String value = (String) newValue;
|
||||
s.putInt(PREF_SCREEN_LOCK_TIMEOUT, Integer.valueOf(value));
|
||||
storeSettings(s);
|
||||
setScreenLockTimeoutSummary(value);
|
||||
} else if (preference == notifyPrivateMessages) {
|
||||
Settings s = new Settings();
|
||||
s.putBoolean(PREF_NOTIFY_PRIVATE, (Boolean) newValue);
|
||||
|
||||
@@ -1,10 +1,27 @@
|
||||
package org.briarproject.briar.api.android;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.arch.lifecycle.LiveData;
|
||||
import android.support.annotation.UiThread;
|
||||
|
||||
public interface LockManager {
|
||||
|
||||
String ACTION_LOCK = "lock";
|
||||
|
||||
/**
|
||||
* Stops the inactivity timer when the user interacts with the app.
|
||||
* Should typically be called by {@link Activity#onStart()}
|
||||
*/
|
||||
@UiThread
|
||||
void onActivityStart();
|
||||
|
||||
/**
|
||||
* Starts the inactivity timer which will lock the app.
|
||||
* Should typically be called by {@link Activity#onStop()}
|
||||
*/
|
||||
@UiThread
|
||||
void onActivityStop();
|
||||
|
||||
/**
|
||||
* Returns an observable LiveData to indicate whether the app can be locked.
|
||||
*/
|
||||
|
||||
@@ -67,4 +67,23 @@
|
||||
<item>@string/pref_theme_auto_value</item>
|
||||
<item>@string/pref_theme_system_value</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="pref_key_lock_timeout_entries">
|
||||
<item>@string/pref_lock_timeout_never</item>
|
||||
<item>@string/pref_lock_timeout_1</item>
|
||||
<item>@string/pref_lock_timeout_5</item>
|
||||
<item>@string/pref_lock_timeout_15</item>
|
||||
<item>@string/pref_lock_timeout_30</item>
|
||||
<item>@string/pref_lock_timeout_60</item>
|
||||
</string-array>
|
||||
<string name="pref_lock_timeout_value_default">5</string>
|
||||
<string name="pref_lock_timeout_value_never">-1</string>
|
||||
<string-array name="pref_key_lock_timeout_values">
|
||||
<item>@string/pref_lock_timeout_value_never</item>
|
||||
<item>1</item>
|
||||
<item>5</item>
|
||||
<item>15</item>
|
||||
<item>30</item>
|
||||
<item>60</item>
|
||||
</string-array>
|
||||
</resources>
|
||||
|
||||
@@ -62,6 +62,7 @@
|
||||
<string name="groups_button">Private Groups</string>
|
||||
<string name="forums_button">Forums</string>
|
||||
<string name="blogs_button">Blogs</string>
|
||||
<!-- This is part of the main menu. The app will be locked when this is tapped. -->
|
||||
<string name="lock_button">Lock App</string>
|
||||
<string name="settings_button">Settings</string>
|
||||
<string name="sign_out_button">Sign Out</string>
|
||||
@@ -357,9 +358,25 @@
|
||||
|
||||
<!-- Settings Security and Panic -->
|
||||
<string name="security_settings_title">Security</string>
|
||||
<string name="pref_lock_title">Screen Lock</string>
|
||||
<string name="pref_lock_title">Screen lock</string>
|
||||
<string name="pref_lock_summary">Use the device\'s screen lock to protect Briar while signed in</string>
|
||||
<string name="pref_lock_disabled_summary">Set up a screen lock for your device to protect Briar while signed in</string>
|
||||
<string name="pref_lock_disabled_summary">To use this feature, set up a screen lock for your device</string>
|
||||
<string name="pref_lock_timeout_title">Screen lock inactivity timeout</string>
|
||||
<!-- The %s placeholder is replaced with the following time spans, e.g. 5 Minutes, 1 Hour -->
|
||||
<string name="pref_lock_timeout_summary">When not using Briar, automatically lock it after %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 minute</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_5">5 minutes</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_15">15 minutes</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_30">30 minutes</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_60">1 hour</string>
|
||||
<string name="pref_lock_timeout_never">Never</string>
|
||||
<string name="pref_lock_timeout_never_summary">Never lock Briar automatically</string>
|
||||
|
||||
<string name="change_password">Change password</string>
|
||||
<string name="current_password">Current password</string>
|
||||
<string name="choose_new_password">New password</string>
|
||||
|
||||
@@ -60,8 +60,17 @@
|
||||
android:key="pref_key_lock"
|
||||
android:persistent="false"
|
||||
android:summary="@string/pref_lock_summary"
|
||||
android:title="@string/pref_lock_title"
|
||||
android:enabled="false"/>
|
||||
android:title="@string/pref_lock_title"/>
|
||||
|
||||
<ListPreference
|
||||
android:defaultValue="@string/pref_lock_timeout_value_default"
|
||||
android:dependency="pref_key_lock"
|
||||
android:entries="@array/pref_key_lock_timeout_entries"
|
||||
android:entryValues="@array/pref_key_lock_timeout_values"
|
||||
android:key="pref_key_lock_timeout"
|
||||
android:persistent="false"
|
||||
android:summary="@string/pref_lock_timeout_summary"
|
||||
android:title="@string/pref_lock_timeout_title"/>
|
||||
|
||||
<Preference
|
||||
android:key="pref_key_change_password"
|
||||
|
||||
Reference in New Issue
Block a user