Merge branch '956-tap-protection-ux' into 'master'

Don't show tap protection dialog until it's needed

Closes #956

See merge request !548
This commit is contained in:
akwizgran
2017-07-03 09:22:31 +00:00
12 changed files with 194 additions and 305 deletions

View File

@@ -38,8 +38,6 @@ public class AppModule {
static class EagerSingletons { static class EagerSingletons {
@Inject @Inject
AndroidNotificationManager androidNotificationManager; AndroidNotificationManager androidNotificationManager;
@Inject
ScreenFilterMonitor screenFilterMonitor;
} }
private final Application application; private final Application application;
@@ -171,10 +169,8 @@ public class AppModule {
} }
@Provides @Provides
@Singleton
ScreenFilterMonitor provideScreenFilterMonitor( ScreenFilterMonitor provideScreenFilterMonitor(
LifecycleManager lifecycleManager, ScreenFilterMonitorImpl sfm) { ScreenFilterMonitorImpl screenFilterMonitor) {
lifecycleManager.registerService(sfm); return screenFilterMonitor;
return sfm;
} }
} }

View File

@@ -1,23 +1,13 @@
package org.briarproject.briar.android; package org.briarproject.briar.android;
import android.app.Application; import android.app.Application;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.content.pm.PackageInfo; import android.content.pm.PackageInfo;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.Signature; import android.content.pm.Signature;
import android.support.annotation.UiThread; import android.support.annotation.UiThread;
import android.support.v7.preference.PreferenceManager;
import org.briarproject.bramble.api.lifecycle.Service; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.lifecycle.ServiceException;
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
import org.briarproject.bramble.api.system.AndroidExecutor;
import org.briarproject.bramble.util.StringUtils; import org.briarproject.bramble.util.StringUtils;
import org.briarproject.briar.api.android.ScreenFilterMonitor; import org.briarproject.briar.api.android.ScreenFilterMonitor;
@@ -26,38 +16,26 @@ import java.io.InputStream;
import java.security.cert.CertificateException; import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory; import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate; import java.security.cert.X509Certificate;
import java.util.Collection;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.TreeSet; import java.util.TreeSet;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Logger; import java.util.logging.Logger;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import javax.inject.Inject; import javax.inject.Inject;
import static android.Manifest.permission.SYSTEM_ALERT_WINDOW; import static android.Manifest.permission.SYSTEM_ALERT_WINDOW;
import static android.content.Intent.ACTION_PACKAGE_ADDED;
import static android.content.Intent.EXTRA_REPLACING;
import static android.content.pm.ApplicationInfo.FLAG_SYSTEM; import static android.content.pm.ApplicationInfo.FLAG_SYSTEM;
import static android.content.pm.ApplicationInfo.FLAG_UPDATED_SYSTEM_APP; import static android.content.pm.ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
import static android.content.pm.PackageManager.GET_PERMISSIONS; import static android.content.pm.PackageManager.GET_PERMISSIONS;
import static android.content.pm.PackageManager.GET_SIGNATURES; import static android.content.pm.PackageManager.GET_SIGNATURES;
import static java.util.logging.Level.WARNING; import static java.util.logging.Level.WARNING;
@MethodsNotNullByDefault @NotNullByDefault
@ParametersNotNullByDefault class ScreenFilterMonitorImpl implements ScreenFilterMonitor {
public class ScreenFilterMonitorImpl extends BroadcastReceiver
implements Service, ScreenFilterMonitor {
private static final Logger LOG = private static final Logger LOG =
Logger.getLogger(ScreenFilterMonitorImpl.class.getName()); Logger.getLogger(ScreenFilterMonitorImpl.class.getName());
private static final String PREF_SCREEN_FILTER_APPS =
"shownScreenFilterApps";
/* /*
* Ignore Play Services if it uses this package name and public key - it's * Ignore Play Services if it uses this package name and public key - it's
@@ -78,124 +56,17 @@ public class ScreenFilterMonitorImpl extends BroadcastReceiver
"82BA35E003C1B4B10DD244A8EE24FFFD333872AB5221985EDAB0FC0D" + "82BA35E003C1B4B10DD244A8EE24FFFD333872AB5221985EDAB0FC0D" +
"0B145B6AA192858E79020103"; "0B145B6AA192858E79020103";
private final Context appContext;
private final AndroidExecutor androidExecutor;
private final PackageManager pm; private final PackageManager pm;
private final SharedPreferences prefs;
private final AtomicBoolean used = new AtomicBoolean(false);
// The following must only be accessed on the UI thread
private final Set<String> apps = new HashSet<>();
private final Set<String> shownApps;
private boolean serviceStarted = false;
@Inject @Inject
ScreenFilterMonitorImpl(AndroidExecutor executor, Application app) { ScreenFilterMonitorImpl(Application app) {
this.androidExecutor = executor; pm = app.getPackageManager();
this.appContext = app;
pm = appContext.getPackageManager();
prefs = PreferenceManager.getDefaultSharedPreferences(appContext);
shownApps = getShownScreenFilterApps();
}
@Override
public void startService() throws ServiceException {
if (used.getAndSet(true)) throw new IllegalStateException();
Future<Void> f = androidExecutor.runOnUiThread(new Callable<Void>() {
@Override
public Void call() {
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(ACTION_PACKAGE_ADDED);
intentFilter.addDataScheme("package");
appContext.registerReceiver(ScreenFilterMonitorImpl.this,
intentFilter);
apps.addAll(getInstalledScreenFilterApps());
serviceStarted = true;
return null;
}
});
try {
f.get();
} catch (InterruptedException | ExecutionException e) {
throw new ServiceException(e);
}
}
@Override
public void stopService() throws ServiceException {
Future<Void> f = androidExecutor.runOnUiThread(new Callable<Void>() {
@Override
public Void call() {
serviceStarted = false;
appContext.unregisterReceiver(ScreenFilterMonitorImpl.this);
return null;
}
});
try {
f.get();
} catch (InterruptedException | ExecutionException e) {
throw new ServiceException(e);
}
}
private Set<String> getShownScreenFilterApps() {
// Result must not be modified
Set<String> s = prefs.getStringSet(PREF_SCREEN_FILTER_APPS, null);
HashSet<String> result = new HashSet<>();
if (s != null) {
result.addAll(s);
}
return result;
}
@Override
public void onReceive(Context context, Intent intent) {
if (!intent.getBooleanExtra(EXTRA_REPLACING, false)) {
final String packageName =
intent.getData().getEncodedSchemeSpecificPart();
androidExecutor.runOnUiThread(new Runnable() {
@Override
public void run() {
String pkg = isOverlayApp(packageName);
if (pkg == null) {
return;
}
apps.add(pkg);
}
});
}
} }
@Override @Override
@UiThread @UiThread
public Set<String> getApps() { public Set<String> getApps() {
if (!serviceStarted) { Set<String> screenFilterApps = new TreeSet<>();
apps.addAll(getInstalledScreenFilterApps());
}
TreeSet<String> buf = new TreeSet<>();
if (apps.isEmpty()) {
return buf;
}
buf.addAll(apps);
buf.removeAll(shownApps);
return buf;
}
@Override
@UiThread
public void storeAppsAsShown(Collection<String> s, boolean persistent) {
HashSet<String> buf = new HashSet<>(s);
shownApps.addAll(buf);
if (persistent && !s.isEmpty()) {
buf.addAll(getShownScreenFilterApps());
prefs.edit()
.putStringSet(PREF_SCREEN_FILTER_APPS, buf)
.apply();
}
}
private Set<String> getInstalledScreenFilterApps() {
HashSet<String> screenFilterApps = new HashSet<>();
List<PackageInfo> packageInfos = List<PackageInfo> packageInfos =
pm.getInstalledPackages(GET_PERMISSIONS); pm.getInstalledPackages(GET_PERMISSIONS);
for (PackageInfo packageInfo : packageInfos) { for (PackageInfo packageInfo : packageInfos) {
@@ -209,21 +80,6 @@ public class ScreenFilterMonitorImpl extends BroadcastReceiver
return screenFilterApps; return screenFilterApps;
} }
// Checks if a package uses the SYSTEM_ALERT_WINDOW permission and if so
// returns the app name.
@Nullable
private String isOverlayApp(String pkg) {
try {
PackageInfo pkgInfo = pm.getPackageInfo(pkg, GET_PERMISSIONS);
if (isOverlayApp(pkgInfo)) {
return pkgToString(pkgInfo);
}
} catch (NameNotFoundException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
}
return null;
}
// Fetches the application name for a given package. // Fetches the application name for a given package.
@Nullable @Nullable
private String pkgToString(PackageInfo pkgInfo) { private String pkgToString(PackageInfo pkgInfo) {

View File

@@ -26,11 +26,6 @@ public class StartupFailureActivity extends BaseActivity {
} }
@Override
protected void showNewScreenFilterWarning() {
// Don't show here, service might not be available
}
private void handleIntent(Intent i) { private void handleIntent(Intent i) {
StartResult result = (StartResult) i.getSerializableExtra("briar.START_RESULT"); StartResult result = (StartResult) i.getSerializableExtra("briar.START_RESULT");
int notificationId = i.getIntExtra("briar.FAILURE_NOTIFICATION_ID", -1); int notificationId = i.getIntExtra("briar.FAILURE_NOTIFICATION_ID", -1);

View File

@@ -2,9 +2,13 @@ package org.briarproject.briar.android.activity;
import android.os.Bundle; import android.os.Bundle;
import android.os.IBinder; import android.os.IBinder;
import android.support.annotation.LayoutRes;
import android.support.annotation.UiThread; import android.support.annotation.UiThread;
import android.support.v7.app.AppCompatActivity; import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.View; import android.view.View;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.view.inputmethod.InputMethodManager; import android.view.inputmethod.InputMethodManager;
import org.briarproject.bramble.api.db.DbException; import org.briarproject.bramble.api.db.DbException;
@@ -13,7 +17,9 @@ import org.briarproject.briar.android.BriarApplication;
import org.briarproject.briar.android.DestroyableContext; import org.briarproject.briar.android.DestroyableContext;
import org.briarproject.briar.android.controller.ActivityLifecycleController; import org.briarproject.briar.android.controller.ActivityLifecycleController;
import org.briarproject.briar.android.forum.ForumModule; import org.briarproject.briar.android.forum.ForumModule;
import org.briarproject.briar.android.fragment.SFDialogFragment; import org.briarproject.briar.android.fragment.ScreenFilterDialogFragment;
import org.briarproject.briar.android.widget.TapSafeFrameLayout;
import org.briarproject.briar.android.widget.TapSafeFrameLayout.OnTapFilteredListener;
import org.briarproject.briar.api.android.ScreenFilterMonitor; import org.briarproject.briar.api.android.ScreenFilterMonitor;
import java.util.ArrayList; import java.util.ArrayList;
@@ -23,21 +29,23 @@ import java.util.Set;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import javax.inject.Inject; import javax.inject.Inject;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.WindowManager.LayoutParams.FLAG_SECURE; import static android.view.WindowManager.LayoutParams.FLAG_SECURE;
import static android.view.inputmethod.InputMethodManager.SHOW_IMPLICIT; import static android.view.inputmethod.InputMethodManager.SHOW_IMPLICIT;
import static org.briarproject.briar.android.TestingConstants.PREVENT_SCREENSHOTS; import static org.briarproject.briar.android.TestingConstants.PREVENT_SCREENSHOTS;
public abstract class BaseActivity extends AppCompatActivity public abstract class BaseActivity extends AppCompatActivity
implements DestroyableContext { implements DestroyableContext, OnTapFilteredListener {
@Inject
protected ScreenFilterMonitor screenFilterMonitor;
protected ActivityComponent activityComponent; protected ActivityComponent activityComponent;
private final List<ActivityLifecycleController> lifecycleControllers = private final List<ActivityLifecycleController> lifecycleControllers =
new ArrayList<>(); new ArrayList<>();
private boolean destroyed = false; private boolean destroyed = false;
private ScreenFilterDialogFragment dialogFrag;
@Inject
protected ScreenFilterMonitor screenFilterMonitor;
private SFDialogFragment dialogFrag;
public abstract void injectActivity(ActivityComponent component); public abstract void injectActivity(ActivityComponent component);
@@ -65,7 +73,6 @@ public abstract class BaseActivity extends AppCompatActivity
for (ActivityLifecycleController alc : lifecycleControllers) { for (ActivityLifecycleController alc : lifecycleControllers) {
alc.onActivityCreate(this); alc.onActivityCreate(this);
} }
} }
public ActivityComponent getActivityComponent() { public ActivityComponent getActivityComponent() {
@@ -97,12 +104,6 @@ public abstract class BaseActivity extends AppCompatActivity
} }
} }
@Override
protected void onPostResume() {
super.onPostResume();
showNewScreenFilterWarning();
}
@Override @Override
protected void onPause() { protected void onPause() {
super.onPause(); super.onPause();
@@ -112,18 +113,14 @@ public abstract class BaseActivity extends AppCompatActivity
} }
} }
protected void showNewScreenFilterWarning() { private void showScreenFilterWarning() {
final Set<String> apps = screenFilterMonitor.getApps(); if (dialogFrag != null && dialogFrag.isVisible()) return;
if (apps.isEmpty()) { Set<String> apps = screenFilterMonitor.getApps();
return; if (apps.isEmpty()) return;
} dialogFrag =
dialogFrag = SFDialogFragment.newInstance(new ArrayList<>(apps)); ScreenFilterDialogFragment.newInstance(new ArrayList<>(apps));
dialogFrag.setCancelable(false); dialogFrag.setCancelable(false);
dialogFrag.show(getSupportFragmentManager(), "SFDialog"); dialogFrag.show(getSupportFragmentManager(), dialogFrag.getTag());
}
public void rememberShownApps(ArrayList<String> s, boolean permanent) {
screenFilterMonitor.storeAppsAsShown(s, permanent);
} }
@Override @Override
@@ -161,4 +158,70 @@ public abstract class BaseActivity extends AppCompatActivity
supportFinishAfterTransition(); supportFinishAfterTransition();
} }
/*
* Wraps the given view in a wrapper that notifies this activity when an
* obscured touch has been filtered, and returns the wrapper.
*/
private View makeTapSafeWrapper(View v) {
TapSafeFrameLayout wrapper = new TapSafeFrameLayout(this);
wrapper.setLayoutParams(new LayoutParams(MATCH_PARENT, MATCH_PARENT));
wrapper.setOnTapFilteredListener(this);
wrapper.addView(v);
return wrapper;
}
/*
* Finds the AppCompat toolbar, if any, and configures it to filter
* obscured touches. If a custom toolbar is used, it will be part of the
* content view and thus protected by the wrapper. But the default toolbar
* is outside the wrapper.
*/
private void protectToolbar() {
View decorView = getWindow().getDecorView();
if (decorView instanceof ViewGroup) {
Toolbar toolbar = findToolbar((ViewGroup) decorView);
if (toolbar != null) toolbar.setFilterTouchesWhenObscured(true);
}
}
@Nullable
private Toolbar findToolbar(ViewGroup vg) {
for (int i = 0, len = vg.getChildCount(); i < len; i++) {
View child = vg.getChildAt(i);
if (child instanceof Toolbar) return (Toolbar) child;
if (child instanceof ViewGroup) {
Toolbar toolbar = findToolbar((ViewGroup) child);
if (toolbar != null) return toolbar;
}
}
return null;
}
@Override
public void setContentView(@LayoutRes int layoutRes) {
setContentView(getLayoutInflater().inflate(layoutRes, null));
}
@Override
public void setContentView(View v) {
super.setContentView(makeTapSafeWrapper(v));
protectToolbar();
}
@Override
public void setContentView(View v, LayoutParams layoutParams) {
super.setContentView(makeTapSafeWrapper(v), layoutParams);
protectToolbar();
}
@Override
public void addContentView(View v, LayoutParams layoutParams) {
super.addContentView(makeTapSafeWrapper(v), layoutParams);
protectToolbar();
}
@Override
public void onTapFiltered() {
showScreenFilterWarning();
}
} }

View File

@@ -1,67 +0,0 @@
package org.briarproject.briar.android.fragment;
import android.app.Dialog;
import android.content.DialogInterface;
import android.os.Bundle;
import android.support.v4.app.DialogFragment;
import android.support.v7.app.AlertDialog;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.CheckBox;
import android.widget.TextView;
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.BaseActivity;
import java.util.ArrayList;
import javax.annotation.Nullable;
@MethodsNotNullByDefault
@ParametersNotNullByDefault
public class SFDialogFragment extends DialogFragment {
public static SFDialogFragment newInstance(ArrayList<String> apps) {
SFDialogFragment frag = new SFDialogFragment();
Bundle args = new Bundle();
args.putStringArrayList("apps", apps);
frag.setArguments(args);
return frag;
}
@Override
public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
AlertDialog.Builder builder =
new AlertDialog.Builder(
getActivity(),
R.style.BriarDialogThemeNoFilter);
builder.setTitle(R.string.screen_filter_title);
LayoutInflater li = getActivity().getLayoutInflater();
//Pass null here because it's an AlertDialog
View v =
li.inflate(R.layout.alert_dialog_checkbox, null,
false);
TextView t = (TextView) v.findViewById(R.id.alert_dialog_text);
final ArrayList<String> apps =
getArguments().getStringArrayList("apps");
t.setText(getString(R.string.screen_filter_body, TextUtils
.join("\n", apps)));
final CheckBox cb =
(CheckBox) v.findViewById(
R.id.checkBox_screen_filter_reminder);
builder.setNeutralButton(R.string.continue_button,
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog,
int which) {
((BaseActivity) getActivity())
.rememberShownApps(apps, cb.isChecked());
}
});
builder.setView(v);
return builder.create();
}
}

View File

@@ -0,0 +1,46 @@
package org.briarproject.briar.android.fragment;
import android.app.Dialog;
import android.content.DialogInterface;
import android.os.Bundle;
import android.support.v4.app.DialogFragment;
import android.support.v7.app.AlertDialog;
import android.text.TextUtils;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.briar.R;
import java.util.ArrayList;
import javax.annotation.Nullable;
@NotNullByDefault
public class ScreenFilterDialogFragment extends DialogFragment {
public static ScreenFilterDialogFragment newInstance(
ArrayList<String> apps) {
ScreenFilterDialogFragment frag = new ScreenFilterDialogFragment();
Bundle args = new Bundle();
args.putStringArrayList("apps", apps);
frag.setArguments(args);
return frag;
}
@Override
public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity(),
R.style.BriarDialogThemeNoFilter);
builder.setTitle(R.string.screen_filter_title);
ArrayList<String> apps = getArguments().getStringArrayList("apps");
builder.setMessage(getString(R.string.screen_filter_body,
TextUtils.join("\n", apps)));
builder.setNeutralButton(R.string.continue_button,
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
});
return builder.create();
}
}

View File

@@ -83,10 +83,6 @@ public class SplashScreenActivity extends BaseActivity {
} }
} }
@Override
protected void showNewScreenFilterWarning() {
}
private void enableStrictMode() { private void enableStrictMode() {
if (TESTING) { if (TESTING) {
ThreadPolicy.Builder threadPolicy = new ThreadPolicy.Builder(); ThreadPolicy.Builder threadPolicy = new ThreadPolicy.Builder();

View File

@@ -0,0 +1,51 @@
package org.briarproject.briar.android.widget;
import android.content.Context;
import android.support.annotation.AttrRes;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.widget.FrameLayout;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.Nullable;
import static android.view.MotionEvent.FLAG_WINDOW_IS_OBSCURED;
@NotNullByDefault
public class TapSafeFrameLayout extends FrameLayout {
@Nullable
private OnTapFilteredListener listener;
public TapSafeFrameLayout(Context context) {
super(context);
setFilterTouchesWhenObscured(false);
}
public TapSafeFrameLayout(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
setFilterTouchesWhenObscured(false);
}
public TapSafeFrameLayout(Context context, @Nullable AttributeSet attrs,
@AttrRes int defStyleAttr) {
super(context, attrs, defStyleAttr);
setFilterTouchesWhenObscured(false);
}
public void setOnTapFilteredListener(OnTapFilteredListener listener) {
this.listener = listener;
}
@Override
public boolean onFilterTouchEventForSecurity(MotionEvent e) {
boolean filter = (e.getFlags() & FLAG_WINDOW_IS_OBSCURED) != 0;
if (filter && listener != null) listener.onTapFiltered();
return !filter;
}
public interface OnTapFilteredListener {
void onTapFiltered();
}
}

View File

@@ -2,14 +2,10 @@ package org.briarproject.briar.api.android;
import android.support.annotation.UiThread; import android.support.annotation.UiThread;
import java.util.Collection;
import java.util.Set; import java.util.Set;
public interface ScreenFilterMonitor { public interface ScreenFilterMonitor {
@UiThread @UiThread
Set<String> getApps(); Set<String> getApps();
@UiThread
void storeAppsAsShown(Collection<String> s, boolean persistent);
} }

View File

@@ -1,40 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:filterTouchesWhenObscured="false"
android:orientation="vertical">
<ScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:fadeScrollbars="false"
android:paddingLeft="20dp"
android:paddingRight="20dp"
android:paddingStart="20dp"
android:paddingTop="20dp"
android:theme="@style/BriarTheme">
<TextView
android:id="@+id/alert_dialog_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingLeft="3dp"
android:paddingRight="6dp"
android:text="TextView"
android:textAppearance="@style/BriarTextBody"
android:theme="@+theme/BriarDialogTheme"/>
</ScrollView>
<CheckBox
android:id="@+id/checkBox_screen_filter_reminder"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="15dp"
android:layout_marginStart="15dp"
android:layout_weight="0"
android:filterTouchesWhenObscured="false"
android:text="@string/checkbox_dont_show_again"
android:textAppearance="@style/BriarTextBody"/>
</LinearLayout>

View File

@@ -381,9 +381,7 @@
<string name="progress_title_logout">Signing out of Briar…</string> <string name="progress_title_logout">Signing out of Briar…</string>
<!-- Screen Filters & Tapjacking --> <!-- Screen Filters & Tapjacking -->
<string name="screen_filter_title">Screen filter detected</string> <string name="screen_filter_title">Screen overlay detected</string>
<string name="screen_filter_body">The following apps have permission to draw over other apps:\n\n%1$s \n\nBriar will not respond to touches when another app is drawing over it. <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>
If you experience any problems, try turning off these apps when using Briar.\n</string>
<string name="checkbox_dont_show_again">Don\'t warn me again for these apps</string>
</resources> </resources>

View File

@@ -8,7 +8,6 @@
<item name="android:textColorLink">@color/briar_text_link</item> <item name="android:textColorLink">@color/briar_text_link</item>
<item name="android:windowBackground">@color/window_background</item> <item name="android:windowBackground">@color/window_background</item>
<item name="android:windowAnimationStyle">@style/ActivityAnimation</item> <item name="android:windowAnimationStyle">@style/ActivityAnimation</item>
<item name="android:filterTouchesWhenObscured">true</item>
<!-- These fix a long-standing UI bug in the support preference library --> <!-- These fix a long-standing UI bug in the support preference library -->
<item name="preferenceTheme">@style/PreferenceThemeOverlay.v14.Material</item> <item name="preferenceTheme">@style/PreferenceThemeOverlay.v14.Material</item>