Merge branch '1103-dont-ask-again-doze' into 'master'

Show Doze Mode Warning with Don't Ask Again Option

Closes #1103

See merge request !625
This commit is contained in:
akwizgran
2017-11-23 16:23:39 +00:00
13 changed files with 253 additions and 26 deletions

View File

@@ -26,6 +26,7 @@ import org.briarproject.briar.BriarCoreEagerSingletons;
import org.briarproject.briar.BriarCoreModule;
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.ScreenFilterMonitor;
import org.briarproject.briar.api.blog.BlogManager;
import org.briarproject.briar.api.blog.BlogPostFactory;
@@ -140,6 +141,8 @@ public interface AndroidComponent
TestDataCreator testDataCreator();
DozeWatchdog dozeWatchdog();
@IoExecutor
Executor ioExecutor();

View File

@@ -15,6 +15,7 @@ import org.briarproject.bramble.api.reporting.DevConfig;
import org.briarproject.bramble.api.ui.UiCallback;
import org.briarproject.bramble.util.StringUtils;
import org.briarproject.briar.api.android.AndroidNotificationManager;
import org.briarproject.briar.api.android.DozeWatchdog;
import org.briarproject.briar.api.android.ReferenceManager;
import org.briarproject.briar.api.android.ScreenFilterMonitor;
@@ -40,6 +41,8 @@ public class AppModule {
AndroidNotificationManager androidNotificationManager;
@Inject
NetworkUsageLogger networkUsageLogger;
@Inject
DozeWatchdog dozeWatchdog;
}
private final Application application;
@@ -183,4 +186,12 @@ public class AppModule {
lifecycleManager.registerService(networkUsageLogger);
return networkUsageLogger;
}
@Provides
@Singleton
DozeWatchdog provideDozeWatchdog(LifecycleManager lifecycleManager) {
DozeWatchdogImpl dozeWatchdog = new DozeWatchdogImpl(application);
lifecycleManager.registerService(dozeWatchdog);
return dozeWatchdog;
}
}

View File

@@ -0,0 +1,57 @@
package org.briarproject.briar.android;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.PowerManager;
import org.briarproject.bramble.api.lifecycle.Service;
import org.briarproject.bramble.api.lifecycle.ServiceException;
import org.briarproject.briar.api.android.DozeWatchdog;
import java.util.concurrent.atomic.AtomicBoolean;
import static android.content.Context.POWER_SERVICE;
import static android.os.Build.VERSION.SDK_INT;
import static android.os.PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED;
class DozeWatchdogImpl implements DozeWatchdog, Service {
private final Context appContext;
private final AtomicBoolean dozed = new AtomicBoolean(false);
private final BroadcastReceiver receiver = new DozeBroadcastReceiver();
DozeWatchdogImpl(Context appContext) {
this.appContext = appContext;
}
@Override
public boolean getAndResetDozeFlag() {
return dozed.getAndSet(false);
}
@Override
public void startService() throws ServiceException {
if (SDK_INT < 23) return;
IntentFilter filter = new IntentFilter(ACTION_DEVICE_IDLE_MODE_CHANGED);
appContext.registerReceiver(receiver, filter);
}
@Override
public void stopService() throws ServiceException {
if (SDK_INT < 23) return;
appContext.unregisterReceiver(receiver);
}
private class DozeBroadcastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if (SDK_INT < 23) return;
PowerManager pm =
(PowerManager) appContext.getSystemService(POWER_SERVICE);
if (pm.isDeviceIdleMode()) dozed.set(true);
}
}
}

View File

@@ -2,17 +2,19 @@ package org.briarproject.briar.android.activity;
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 +27,10 @@ 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 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.util.UiUtils.getDozeWhitelistingIntent;
@SuppressLint("Registered")
public abstract class BriarActivity extends BaseActivity {
@@ -59,11 +64,21 @@ public abstract class BriarActivity extends BaseActivity {
if (!briarController.hasEncryptionKey() && !isFinishing()) {
Intent i = new Intent(this, PasswordActivity.class);
startActivityForResult(i, REQUEST_PASSWORD);
} else if (SDK_INT >= 23) {
briarController.hasDozed(new UiResultHandler<Boolean>(this) {
@Override
public void onResultUi(Boolean result) {
if (result) {
showDozeDialog(getString(R.string.warning_dozed,
getString(R.string.app_name)));
}
}
});
}
}
public void setSceneTransitionAnimation() {
if (Build.VERSION.SDK_INT < 21) return;
if (SDK_INT < 21) return;
Transition slide = new Slide(Gravity.RIGHT);
slide.excludeTarget(android.R.id.statusBarBackground, true);
slide.excludeTarget(android.R.id.navigationBarBackground, true);
@@ -97,6 +112,28 @@ public abstract class BriarActivity extends BaseActivity {
return toolbar;
}
protected void showDozeDialog(String message) {
AlertDialog.Builder b =
new AlertDialog.Builder(this, R.style.BriarDialogTheme);
b.setMessage(message);
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.doNotAskAgainForDozeWhiteListing();
});
b.show();
}
protected void signOut(boolean removeFromRecentApps) {
if (briarController.hasEncryptionKey()) {
// Don't use UiResultHandler because we want the result even if
@@ -123,7 +160,7 @@ public abstract class BriarActivity extends BaseActivity {
}
private void finishAndExit() {
if (Build.VERSION.SDK_INT >= 21) finishAndRemoveTask();
if (SDK_INT >= 21) finishAndRemoveTask();
else supportFinishAfterTransition();
LOG.info("Exiting");
System.exit(0);

View File

@@ -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 doNotAskAgainForDozeWhiteListing();
void signOut(ResultHandler<Void> eventHandler);
}

View File

@@ -6,30 +6,52 @@ 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 org.briarproject.briar.api.android.DozeWatchdog;
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());
public static final String DOZE_ASK_AGAIN = "dozeAskAgain";
private final BriarServiceConnection serviceConnection;
private final DatabaseConfig databaseConfig;
@DatabaseExecutor
private final Executor databaseExecutor;
private final SettingsManager settingsManager;
private final DozeWatchdog dozeWatchdog;
private final Activity activity;
private boolean bound = false;
@Inject
BriarControllerImpl(BriarServiceConnection serviceConnection,
DatabaseConfig databaseConfig, Activity activity) {
DatabaseConfig databaseConfig,
@DatabaseExecutor Executor databaseExecutor,
SettingsManager settingsManager, DozeWatchdog dozeWatchdog,
Activity activity) {
this.serviceConnection = serviceConnection;
this.databaseConfig = databaseConfig;
this.databaseExecutor = databaseExecutor;
this.settingsManager = settingsManager;
this.dozeWatchdog = dozeWatchdog;
this.activity = activity;
}
@@ -65,6 +87,40 @@ public class BriarControllerImpl implements BriarController {
return databaseConfig.getEncryptionKey() != null;
}
@Override
public void hasDozed(ResultHandler<Boolean> handler) {
if (!dozeWatchdog.getAndResetDozeFlag()
|| !needsDozeWhitelisting(activity)) {
handler.onResult(false);
return;
}
databaseExecutor.execute(() -> {
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);
}
});
}
@Override
public void doNotAskAgainForDozeWhiteListing() {
databaseExecutor.execute(() -> {
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);
}
});
}
@Override
public void signOut(ResultHandler<Void> eventHandler) {
new Thread() {

View File

@@ -26,6 +26,7 @@ public class DozeFragment extends SetupFragment {
private Button dozeButton;
private ProgressBar progressBar;
private boolean secondAttempt = false;
public static DozeFragment newInstance() {
return new DozeFragment();
@@ -64,10 +65,11 @@ public class DozeFragment extends SetupFragment {
public void onActivityResult(int request, int result, Intent data) {
super.onActivityResult(request, result, data);
if (request == REQUEST_DOZE_WHITELISTING) {
if (!setupController.needsDozeWhitelisting()) {
if (!setupController.needsDozeWhitelisting() || secondAttempt) {
dozeButton.setEnabled(false);
onClick(dozeButton);
} else {
secondAttempt = true;
showOnboardingDialog(getContext(), getHelpText());
}
}

View File

@@ -1,7 +1,6 @@
package org.briarproject.briar.android.navdrawer;
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.content.Intent;
import android.content.res.Configuration;
import android.os.Bundle;
@@ -12,7 +11,6 @@ import android.support.v4.app.FragmentTransaction;
import android.support.v4.content.ContextCompat;
import android.support.v4.widget.DrawerLayout;
import android.support.v7.app.ActionBarDrawerToggle;
import android.support.v7.app.AlertDialog;
import android.support.v7.widget.Toolbar;
import android.view.LayoutInflater;
import android.view.MenuItem;
@@ -53,12 +51,10 @@ import static android.support.v4.view.GravityCompat.START;
import static android.support.v4.widget.DrawerLayout.LOCK_MODE_LOCKED_CLOSED;
import static android.view.View.GONE;
import static android.view.View.VISIBLE;
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.navdrawer.NavDrawerController.ExpiryWarning.NO;
import static org.briarproject.briar.android.navdrawer.NavDrawerController.ExpiryWarning.UPDATE;
import static org.briarproject.briar.android.util.UiUtils.getDaysUntilExpiry;
import static org.briarproject.briar.android.util.UiUtils.getDozeWhitelistingIntent;
import static org.briarproject.briar.android.util.UiUtils.needsDozeWhitelisting;
public class NavDrawerActivity extends BriarActivity implements
BaseFragmentListener, TransportStateListener,
@@ -151,7 +147,23 @@ public class NavDrawerActivity extends BriarActivity implements
if (expiry != NO) showExpiryWarning(expiry);
}
});
if (needsDozeWhitelisting(this)) requestDozeWhitelisting();
}
@Override
protected void onActivityResult(int request, int result, Intent data) {
super.onActivityResult(request, result, data);
if (request == REQUEST_PASSWORD && result == RESULT_OK) {
controller.shouldAskForDozeWhitelisting(this,
new UiResultHandler<Boolean>(this) {
@Override
public void onResultUi(Boolean ask) {
if (ask) {
showDozeDialog(
getString(R.string.setup_doze_intro));
}
}
});
}
}
private void exitIfStartupFailed(Intent intent) {
@@ -313,20 +325,6 @@ public class NavDrawerActivity extends BriarActivity implements
expiryWarning.setVisibility(VISIBLE);
}
@TargetApi(23)
private void requestDozeWhitelisting() {
new AlertDialog.Builder(this, R.style.BriarDialogTheme)
.setMessage(R.string.setup_doze_intro)
.setPositiveButton(R.string.ok,
(dialog, which) -> {
Intent i = getDozeWhitelistingIntent(
NavDrawerActivity.this);
startActivityForResult(i,
REQUEST_DOZE_WHITELISTING);
})
.show();
}
private void initializeTransports(LayoutInflater inflater) {
transports = new ArrayList<>(3);

View File

@@ -1,5 +1,7 @@
package org.briarproject.briar.android.navdrawer;
import android.content.Context;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.briar.android.controller.ActivityLifecycleController;
@@ -16,4 +18,7 @@ public interface NavDrawerController extends ActivityLifecycleController {
void expiryWarningDismissed();
void shouldAskForDozeWhitelisting(Context ctx,
ResultHandler<Boolean> handler);
}

View File

@@ -1,6 +1,7 @@
package org.briarproject.briar.android.navdrawer;
import android.app.Activity;
import android.content.Context;
import org.briarproject.bramble.api.db.DatabaseExecutor;
import org.briarproject.bramble.api.db.DbException;
@@ -28,10 +29,12 @@ import javax.inject.Inject;
import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING;
import static org.briarproject.briar.android.BriarApplication.EXPIRY_DATE;
import static org.briarproject.briar.android.controller.BriarControllerImpl.DOZE_ASK_AGAIN;
import static org.briarproject.briar.android.navdrawer.NavDrawerController.ExpiryWarning.NO;
import static org.briarproject.briar.android.navdrawer.NavDrawerController.ExpiryWarning.SHOW;
import static org.briarproject.briar.android.navdrawer.NavDrawerController.ExpiryWarning.UPDATE;
import static org.briarproject.briar.android.settings.SettingsFragment.SETTINGS_NAMESPACE;
import static org.briarproject.briar.android.util.UiUtils.needsDozeWhitelisting;
@MethodsNotNullByDefault
@ParametersNotNullByDefault
@@ -154,6 +157,28 @@ public class NavDrawerControllerImpl extends DbControllerImpl
});
}
@Override
public void shouldAskForDozeWhitelisting(Context ctx,
ResultHandler<Boolean> handler) {
// check this first, to hit the DbThread only when really necessary
if (!needsDozeWhitelisting(ctx)) {
handler.onResult(false);
return;
}
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 boolean isTransportRunning(TransportId transportId) {
Plugin plugin = pluginManager.getPlugin(transportId);

View File

@@ -0,0 +1,6 @@
package org.briarproject.briar.api.android;
public interface DozeWatchdog {
boolean getAndResetDozeFlag();
}

View File

@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<CheckBox
android:id="@+id/checkbox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/margin_activity_horizontal"
android:layout_marginTop="@dimen/margin_activity_vertical"
android:checked="false"
android:text="@string/don_t_ask_again"/>
</FrameLayout>

View File

@@ -19,6 +19,7 @@
<string name="passwords_do_not_match">Passwords do not match</string>
<string name="create_account_button">Create Account</string>
<string name="more_info">More Information</string>
<string name="don_t_ask_again">Don\'t ask again</string>
<!-- Login -->
<string name="enter_password">Enter your password:</string>
@@ -94,6 +95,8 @@
<string name="ellipsis"></string>
<string name="text_too_long">The entered text is too long</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-->
<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>
@@ -390,7 +393,7 @@
<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>
<!-- Permission Requests and Doze Mode -->
<!-- Permission Requests -->
<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_denied_body">You have denied access to the camera, but adding contacts requires using the camera.\n\nPlease consider granting access.</string>