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

This commit is contained in:
akwizgran
2017-06-29 18:18:39 +01:00
parent f5dc6f24b9
commit 8b32f82566
10 changed files with 172 additions and 63 deletions

View File

@@ -183,15 +183,13 @@ public class ScreenFilterMonitorImpl extends BroadcastReceiver
@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();
}
public void storeAppsAsShown(Collection<String> shown) {
shownApps.addAll(shown);
HashSet<String> buf = new HashSet<>(shown);
buf.addAll(getShownScreenFilterApps());
prefs.edit()
.putStringSet(PREF_SCREEN_FILTER_APPS, buf)
.apply();
}
private Set<String> getInstalledScreenFilterApps() {

View File

@@ -27,7 +27,7 @@ public class StartupFailureActivity extends BaseActivity {
}
@Override
protected void showNewScreenFilterWarning() {
protected void showScreenFilterWarning() {
// Don't show here, service might not be available
}

View File

@@ -2,9 +2,13 @@ package org.briarproject.briar.android.activity;
import android.os.Bundle;
import android.os.IBinder;
import android.support.annotation.LayoutRes;
import android.support.annotation.UiThread;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.view.inputmethod.InputMethodManager;
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.controller.ActivityLifecycleController;
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 java.util.ArrayList;
@@ -23,21 +29,23 @@ import java.util.Set;
import javax.annotation.Nullable;
import javax.inject.Inject;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.WindowManager.LayoutParams.FLAG_SECURE;
import static android.view.inputmethod.InputMethodManager.SHOW_IMPLICIT;
import static org.briarproject.briar.android.TestingConstants.PREVENT_SCREENSHOTS;
public abstract class BaseActivity extends AppCompatActivity
implements DestroyableContext {
implements DestroyableContext, OnTapFilteredListener {
@Inject
protected ScreenFilterMonitor screenFilterMonitor;
protected ActivityComponent activityComponent;
private final List<ActivityLifecycleController> lifecycleControllers =
new ArrayList<>();
private boolean destroyed = false;
@Inject
protected ScreenFilterMonitor screenFilterMonitor;
private SFDialogFragment dialogFrag;
private ScreenFilterDialogFragment dialogFrag;
public abstract void injectActivity(ActivityComponent component);
@@ -65,7 +73,6 @@ public abstract class BaseActivity extends AppCompatActivity
for (ActivityLifecycleController alc : lifecycleControllers) {
alc.onActivityCreate(this);
}
}
public ActivityComponent getActivityComponent() {
@@ -97,12 +104,6 @@ public abstract class BaseActivity extends AppCompatActivity
}
}
@Override
protected void onPostResume() {
super.onPostResume();
showNewScreenFilterWarning();
}
@Override
protected void onPause() {
super.onPause();
@@ -112,18 +113,18 @@ public abstract class BaseActivity extends AppCompatActivity
}
}
protected void showNewScreenFilterWarning() {
final Set<String> apps = screenFilterMonitor.getApps();
if (apps.isEmpty()) {
return;
}
dialogFrag = SFDialogFragment.newInstance(new ArrayList<>(apps));
protected void showScreenFilterWarning() {
if (dialogFrag != null && dialogFrag.isVisible()) return;
Set<String> apps = screenFilterMonitor.getApps();
if (apps.isEmpty()) return;
dialogFrag =
ScreenFilterDialogFragment.newInstance(new ArrayList<>(apps));
dialogFrag.setCancelable(false);
dialogFrag.show(getSupportFragmentManager(), "SFDialog");
dialogFrag.show(getSupportFragmentManager(), dialogFrag.getTag());
}
public void rememberShownApps(ArrayList<String> s, boolean permanent) {
screenFilterMonitor.storeAppsAsShown(s, permanent);
public void rememberShownApps(ArrayList<String> s) {
screenFilterMonitor.storeAppsAsShown(s);
}
@Override
@@ -161,4 +162,70 @@ public abstract class BaseActivity extends AppCompatActivity
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

@@ -22,10 +22,11 @@ import javax.annotation.Nullable;
@MethodsNotNullByDefault
@ParametersNotNullByDefault
public class SFDialogFragment extends DialogFragment {
public class ScreenFilterDialogFragment extends DialogFragment {
public static SFDialogFragment newInstance(ArrayList<String> apps) {
SFDialogFragment frag = new SFDialogFragment();
public static ScreenFilterDialogFragment newInstance(
ArrayList<String> apps) {
ScreenFilterDialogFragment frag = new ScreenFilterDialogFragment();
Bundle args = new Bundle();
args.putStringArrayList("apps", apps);
frag.setArguments(args);
@@ -34,31 +35,25 @@ public class SFDialogFragment extends DialogFragment {
@Override
public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
AlertDialog.Builder builder =
new AlertDialog.Builder(
getActivity(),
R.style.BriarDialogThemeNoFilter);
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);
// 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);
final CheckBox cb = (CheckBox) v.findViewById(
R.id.checkbox_dont_show_again);
builder.setNeutralButton(R.string.continue_button,
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog,
int which) {
((BaseActivity) getActivity())
.rememberShownApps(apps, cb.isChecked());
public void onClick(DialogInterface dialog, int which) {
if (cb.isChecked())
((BaseActivity) getActivity()).rememberShownApps(apps);
}
});
builder.setView(v);

View File

@@ -84,7 +84,8 @@ public class SplashScreenActivity extends BaseActivity {
}
@Override
protected void showNewScreenFilterWarning() {
protected void showScreenFilterWarning() {
// Ignore touches until the next activity is shown
}
private void enableStrictMode() {

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

@@ -11,5 +11,5 @@ public interface ScreenFilterMonitor {
Set<String> getApps();
@UiThread
void storeAppsAsShown(Collection<String> s, boolean persistent);
void storeAppsAsShown(Collection<String> shown);
}

View File

@@ -1,9 +1,9 @@
<?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">
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<ScrollView
android:layout_width="match_parent"
@@ -28,13 +28,12 @@
</ScrollView>
<CheckBox
android:id="@+id/checkBox_screen_filter_reminder"
android:id="@+id/checkbox_dont_show_again"
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,8 @@
<string name="progress_title_logout">Signing out of Briar…</string>
<!-- Screen Filters & Tapjacking -->
<string name="screen_filter_title">Screen filter 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.
If you experience any problems, try turning off these apps when using Briar.\n</string>
<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 while another app is drawing on top.\n\nThe following apps have permission to draw on top:\n\n%1$s \n\nTry 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>

View File

@@ -8,7 +8,6 @@
<item name="android:textColorLink">@color/briar_text_link</item>
<item name="android:windowBackground">@color/window_background</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 -->
<item name="preferenceTheme">@style/PreferenceThemeOverlay.v14.Material</item>