mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-13 11:19:04 +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.PendingIntent;
|
||||
import android.app.Service;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.ServiceConnection;
|
||||
import android.os.Binder;
|
||||
import android.os.Build;
|
||||
import android.os.IBinder;
|
||||
import android.os.PowerManager;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v4.app.NotificationCompat;
|
||||
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.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.os.PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED;
|
||||
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.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.briar.android.util.UiUtils.needsDozeWhitelisting;
|
||||
|
||||
public class BriarService extends Service {
|
||||
|
||||
@@ -45,6 +52,9 @@ public class BriarService extends Service {
|
||||
|
||||
private final AtomicBoolean created = new AtomicBoolean(false);
|
||||
private final Binder binder = new BriarBinder();
|
||||
@Nullable
|
||||
private BriarBroadcastReceiver receiver = null;
|
||||
private boolean hasDozed = false;
|
||||
|
||||
@Inject
|
||||
protected DatabaseConfig databaseConfig;
|
||||
@@ -84,7 +94,7 @@ public class BriarService extends Service {
|
||||
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 (Build.VERSION.SDK_INT >= 21) {
|
||||
if (SDK_INT >= 21) {
|
||||
b.setCategory(CATEGORY_SERVICE);
|
||||
b.setVisibility(VISIBILITY_SECRET);
|
||||
}
|
||||
@@ -109,6 +119,7 @@ public class BriarService extends Service {
|
||||
}
|
||||
}
|
||||
}.start();
|
||||
registerBroadcastReceiver();
|
||||
}
|
||||
|
||||
private void showStartupFailureNotification(StartResult result) {
|
||||
@@ -153,6 +164,7 @@ public class BriarService extends Service {
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
LOG.info("Destroyed");
|
||||
if (receiver != null) unregisterReceiver(receiver);
|
||||
stopForeground(true);
|
||||
// Stop the services in a background thread
|
||||
new Thread() {
|
||||
@@ -170,6 +182,21 @@ public class BriarService extends Service {
|
||||
// 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.
|
||||
*/
|
||||
@@ -225,4 +252,15 @@ public class BriarService extends Service {
|
||||
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.os.Build;
|
||||
import android.support.v7.app.ActionBar;
|
||||
import android.support.v7.app.AlertDialog;
|
||||
import android.support.v7.widget.Toolbar;
|
||||
import android.transition.Slide;
|
||||
import android.transition.Transition;
|
||||
import android.view.Gravity;
|
||||
import android.view.Window;
|
||||
import android.widget.CheckBox;
|
||||
|
||||
import org.briarproject.briar.R;
|
||||
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.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_NEW_TASK;
|
||||
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.util.UiUtils.getDozeWhitelistingIntent;
|
||||
|
||||
@SuppressLint("Registered")
|
||||
public abstract class BriarActivity extends BaseActivity {
|
||||
@@ -59,6 +64,13 @@ public abstract class BriarActivity extends BaseActivity {
|
||||
if (!briarController.hasEncryptionKey() && !isFinishing()) {
|
||||
Intent i = new Intent(this, PasswordActivity.class);
|
||||
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;
|
||||
}
|
||||
|
||||
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) {
|
||||
if (briarController.hasEncryptionKey()) {
|
||||
// Don't use UiResultHandler because we want the result even if
|
||||
|
||||
@@ -8,5 +8,13 @@ public interface BriarController extends ActivityLifecycleController {
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -6,30 +6,48 @@ import android.os.IBinder;
|
||||
import android.support.annotation.CallSuper;
|
||||
|
||||
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.BriarServiceConnection;
|
||||
import org.briarproject.briar.android.controller.handler.ResultHandler;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
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 {
|
||||
|
||||
private static final Logger LOG =
|
||||
Logger.getLogger(BriarControllerImpl.class.getName());
|
||||
|
||||
private static final String HAS_DOZED_ASK_AGAIN = "hasDozedAskAgain";
|
||||
|
||||
private final BriarServiceConnection serviceConnection;
|
||||
private final DatabaseConfig databaseConfig;
|
||||
@DatabaseExecutor
|
||||
private final Executor databaseExecutor;
|
||||
private final SettingsManager settingsManager;
|
||||
private final Activity activity;
|
||||
|
||||
private boolean bound = false;
|
||||
|
||||
@Inject
|
||||
BriarControllerImpl(BriarServiceConnection serviceConnection,
|
||||
DatabaseConfig databaseConfig, Activity activity) {
|
||||
DatabaseConfig databaseConfig,
|
||||
@DatabaseExecutor Executor databaseExecutor,
|
||||
SettingsManager settingsManager, Activity activity) {
|
||||
this.serviceConnection = serviceConnection;
|
||||
this.databaseConfig = databaseConfig;
|
||||
this.databaseExecutor = databaseExecutor;
|
||||
this.settingsManager = settingsManager;
|
||||
this.activity = activity;
|
||||
}
|
||||
|
||||
@@ -65,6 +83,50 @@ public class BriarControllerImpl implements BriarController {
|
||||
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
|
||||
public void signOut(ResultHandler<Void> eventHandler) {
|
||||
new Thread() {
|
||||
|
||||
@@ -158,42 +158,37 @@ public class NavDrawerControllerImpl extends DbControllerImpl
|
||||
}
|
||||
|
||||
@Override
|
||||
public void askDozeWhitelisting(final Context ctx,
|
||||
final ResultHandler<Boolean> handler) {
|
||||
public void askDozeWhitelisting(Context ctx,
|
||||
ResultHandler<Boolean> handler) {
|
||||
// check this first, to hit the DbThread only when really necessary
|
||||
if (!needsDozeWhitelisting(ctx)) {
|
||||
handler.onResult(false);
|
||||
return;
|
||||
}
|
||||
runOnDbThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
Settings settings =
|
||||
settingsManager.getSettings(SETTINGS_NAMESPACE);
|
||||
boolean ask = settings.getBoolean(DOZE_ASK_AGAIN, true);
|
||||
handler.onResult(ask);
|
||||
} catch (DbException e) {
|
||||
if (LOG.isLoggable(WARNING))
|
||||
LOG.log(WARNING, e.toString(), e);
|
||||
handler.onResult(true);
|
||||
}
|
||||
runOnDbThread(() -> {
|
||||
try {
|
||||
Settings settings =
|
||||
settingsManager.getSettings(SETTINGS_NAMESPACE);
|
||||
boolean ask = settings.getBoolean(DOZE_ASK_AGAIN, true);
|
||||
handler.onResult(ask);
|
||||
} catch (DbException e) {
|
||||
if (LOG.isLoggable(WARNING))
|
||||
LOG.log(WARNING, e.toString(), e);
|
||||
handler.onResult(true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doNotAskAgainForDozeWhiteListing() {
|
||||
runOnDbThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
Settings settings = new Settings();
|
||||
settings.putBoolean(DOZE_ASK_AGAIN, false);
|
||||
settingsManager.mergeSettings(settings, SETTINGS_NAMESPACE);
|
||||
} catch (DbException e) {
|
||||
if (LOG.isLoggable(WARNING))
|
||||
LOG.log(WARNING, e.toString(), e);
|
||||
}
|
||||
runOnDbThread(() -> {
|
||||
try {
|
||||
Settings settings = new Settings();
|
||||
settings.putBoolean(DOZE_ASK_AGAIN, false);
|
||||
settingsManager.mergeSettings(settings, SETTINGS_NAMESPACE);
|
||||
} catch (DbException e) {
|
||||
if (LOG.isLoggable(WARNING))
|
||||
LOG.log(WARNING, e.toString(), e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user