mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-20 22:59:54 +01:00
Don't show tap protection dialog until it's needed.
This commit is contained in:
@@ -183,15 +183,13 @@ public class ScreenFilterMonitorImpl extends BroadcastReceiver
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
@UiThread
|
@UiThread
|
||||||
public void storeAppsAsShown(Collection<String> s, boolean persistent) {
|
public void storeAppsAsShown(Collection<String> shown) {
|
||||||
HashSet<String> buf = new HashSet<>(s);
|
shownApps.addAll(shown);
|
||||||
shownApps.addAll(buf);
|
HashSet<String> buf = new HashSet<>(shown);
|
||||||
if (persistent && !s.isEmpty()) {
|
buf.addAll(getShownScreenFilterApps());
|
||||||
buf.addAll(getShownScreenFilterApps());
|
prefs.edit()
|
||||||
prefs.edit()
|
.putStringSet(PREF_SCREEN_FILTER_APPS, buf)
|
||||||
.putStringSet(PREF_SCREEN_FILTER_APPS, buf)
|
.apply();
|
||||||
.apply();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Set<String> getInstalledScreenFilterApps() {
|
private Set<String> getInstalledScreenFilterApps() {
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ public class StartupFailureActivity extends BaseActivity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void showNewScreenFilterWarning() {
|
protected void showScreenFilterWarning() {
|
||||||
// Don't show here, service might not be available
|
// Don't show here, service might not be available
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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,18 @@ public abstract class BaseActivity extends AppCompatActivity
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void showNewScreenFilterWarning() {
|
protected 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) {
|
public void rememberShownApps(ArrayList<String> s) {
|
||||||
screenFilterMonitor.storeAppsAsShown(s, permanent);
|
screenFilterMonitor.storeAppsAsShown(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -161,4 +162,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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,10 +22,11 @@ import javax.annotation.Nullable;
|
|||||||
|
|
||||||
@MethodsNotNullByDefault
|
@MethodsNotNullByDefault
|
||||||
@ParametersNotNullByDefault
|
@ParametersNotNullByDefault
|
||||||
public class SFDialogFragment extends DialogFragment {
|
public class ScreenFilterDialogFragment extends DialogFragment {
|
||||||
|
|
||||||
public static SFDialogFragment newInstance(ArrayList<String> apps) {
|
public static ScreenFilterDialogFragment newInstance(
|
||||||
SFDialogFragment frag = new SFDialogFragment();
|
ArrayList<String> apps) {
|
||||||
|
ScreenFilterDialogFragment frag = new ScreenFilterDialogFragment();
|
||||||
Bundle args = new Bundle();
|
Bundle args = new Bundle();
|
||||||
args.putStringArrayList("apps", apps);
|
args.putStringArrayList("apps", apps);
|
||||||
frag.setArguments(args);
|
frag.setArguments(args);
|
||||||
@@ -34,31 +35,25 @@ public class SFDialogFragment extends DialogFragment {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
|
public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
|
||||||
AlertDialog.Builder builder =
|
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity(),
|
||||||
new AlertDialog.Builder(
|
R.style.BriarDialogThemeNoFilter);
|
||||||
getActivity(),
|
|
||||||
R.style.BriarDialogThemeNoFilter);
|
|
||||||
builder.setTitle(R.string.screen_filter_title);
|
builder.setTitle(R.string.screen_filter_title);
|
||||||
LayoutInflater li = getActivity().getLayoutInflater();
|
LayoutInflater li = getActivity().getLayoutInflater();
|
||||||
//Pass null here because it's an AlertDialog
|
// Pass null here because it's an AlertDialog
|
||||||
View v =
|
View v = li.inflate(R.layout.alert_dialog_checkbox, null, false);
|
||||||
li.inflate(R.layout.alert_dialog_checkbox, null,
|
|
||||||
false);
|
|
||||||
TextView t = (TextView) v.findViewById(R.id.alert_dialog_text);
|
TextView t = (TextView) v.findViewById(R.id.alert_dialog_text);
|
||||||
final ArrayList<String> apps =
|
final ArrayList<String> apps =
|
||||||
getArguments().getStringArrayList("apps");
|
getArguments().getStringArrayList("apps");
|
||||||
t.setText(getString(R.string.screen_filter_body, TextUtils
|
t.setText(getString(R.string.screen_filter_body, TextUtils
|
||||||
.join("\n", apps)));
|
.join("\n", apps)));
|
||||||
final CheckBox cb =
|
final CheckBox cb = (CheckBox) v.findViewById(
|
||||||
(CheckBox) v.findViewById(
|
R.id.checkbox_dont_show_again);
|
||||||
R.id.checkBox_screen_filter_reminder);
|
|
||||||
builder.setNeutralButton(R.string.continue_button,
|
builder.setNeutralButton(R.string.continue_button,
|
||||||
new DialogInterface.OnClickListener() {
|
new DialogInterface.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(DialogInterface dialog,
|
public void onClick(DialogInterface dialog, int which) {
|
||||||
int which) {
|
if (cb.isChecked())
|
||||||
((BaseActivity) getActivity())
|
((BaseActivity) getActivity()).rememberShownApps(apps);
|
||||||
.rememberShownApps(apps, cb.isChecked());
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
builder.setView(v);
|
builder.setView(v);
|
||||||
@@ -84,7 +84,8 @@ public class SplashScreenActivity extends BaseActivity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void showNewScreenFilterWarning() {
|
protected void showScreenFilterWarning() {
|
||||||
|
// Ignore touches until the next activity is shown
|
||||||
}
|
}
|
||||||
|
|
||||||
private void enableStrictMode() {
|
private void enableStrictMode() {
|
||||||
|
|||||||
@@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -11,5 +11,5 @@ public interface ScreenFilterMonitor {
|
|||||||
Set<String> getApps();
|
Set<String> getApps();
|
||||||
|
|
||||||
@UiThread
|
@UiThread
|
||||||
void storeAppsAsShown(Collection<String> s, boolean persistent);
|
void storeAppsAsShown(Collection<String> shown);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:layout_height="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:filterTouchesWhenObscured="false"
|
android:layout_height="match_parent"
|
||||||
android:orientation="vertical">
|
android:orientation="vertical">
|
||||||
|
|
||||||
<ScrollView
|
<ScrollView
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
@@ -28,13 +28,12 @@
|
|||||||
</ScrollView>
|
</ScrollView>
|
||||||
|
|
||||||
<CheckBox
|
<CheckBox
|
||||||
android:id="@+id/checkBox_screen_filter_reminder"
|
android:id="@+id/checkbox_dont_show_again"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginLeft="15dp"
|
android:layout_marginLeft="15dp"
|
||||||
android:layout_marginStart="15dp"
|
android:layout_marginStart="15dp"
|
||||||
android:layout_weight="0"
|
android:layout_weight="0"
|
||||||
android:filterTouchesWhenObscured="false"
|
|
||||||
android:text="@string/checkbox_dont_show_again"
|
android:text="@string/checkbox_dont_show_again"
|
||||||
android:textAppearance="@style/BriarTextBody"/>
|
android:textAppearance="@style/BriarTextBody"/>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|||||||
@@ -381,9 +381,8 @@
|
|||||||
<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 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>
|
||||||
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>
|
<string name="checkbox_dont_show_again">Don\'t warn me again for these apps</string>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
Reference in New Issue
Block a user