From 753068288f6d6a76d5d709dff2855e745ad5899f Mon Sep 17 00:00:00 2001 From: akwizgran Date: Thu, 4 Jan 2018 13:19:49 +0000 Subject: [PATCH 1/7] Allow filtered taps if all overlay apps are whitelisted. --- .../briar/android/AndroidComponent.java | 4 + .../briarproject/briar/android/AppModule.java | 6 ++ .../android/ScreenFilterMonitorImpl.java | 78 +++++++++++++------ .../android/activity/ActivityComponent.java | 4 + .../android/activity/ActivityModule.java | 8 -- .../briar/android/activity/BaseActivity.java | 27 ++++--- .../fragment/ScreenFilterDialogFragment.java | 66 +++++++++++++--- .../android/widget/TapSafeFrameLayout.java | 8 +- .../api/android/ScreenFilterMonitor.java | 31 +++++++- .../main/res/layout/dialog_screen_filter.xml | 26 +++++++ briar-android/src/main/res/values/strings.xml | 1 + 11 files changed, 200 insertions(+), 59 deletions(-) create mode 100644 briar-android/src/main/res/layout/dialog_screen_filter.xml diff --git a/briar-android/src/main/java/org/briarproject/briar/android/AndroidComponent.java b/briar-android/src/main/java/org/briarproject/briar/android/AndroidComponent.java index 1c6cc9d86..02a5dc0ff 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/AndroidComponent.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/AndroidComponent.java @@ -1,5 +1,7 @@ package org.briarproject.briar.android; +import android.content.SharedPreferences; + import org.briarproject.bramble.BrambleAndroidModule; import org.briarproject.bramble.BrambleCoreEagerSingletons; import org.briarproject.bramble.BrambleCoreModule; @@ -89,6 +91,8 @@ public interface AndroidComponent AndroidNotificationManager androidNotificationManager(); + SharedPreferences sharedPreferences(); + ScreenFilterMonitor screenFilterMonitor(); ConnectionRegistry connectionRegistry(); diff --git a/briar-android/src/main/java/org/briarproject/briar/android/AppModule.java b/briar-android/src/main/java/org/briarproject/briar/android/AppModule.java index c0e068976..947d86076 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/AppModule.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/AppModule.java @@ -1,6 +1,7 @@ package org.briarproject.briar.android; import android.app.Application; +import android.content.SharedPreferences; import org.briarproject.bramble.api.crypto.CryptoComponent; import org.briarproject.bramble.api.crypto.PublicKey; @@ -157,6 +158,11 @@ public class AppModule { return devConfig; } + @Provides + SharedPreferences provideSharedPreferences(Application app) { + return app.getSharedPreferences("db", MODE_PRIVATE); + } + @Provides @Singleton ReferenceManager provideReferenceManager() { diff --git a/briar-android/src/main/java/org/briarproject/briar/android/ScreenFilterMonitorImpl.java b/briar-android/src/main/java/org/briarproject/briar/android/ScreenFilterMonitorImpl.java index e1a88b592..d9c5ac65e 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/ScreenFilterMonitorImpl.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/ScreenFilterMonitorImpl.java @@ -1,6 +1,8 @@ package org.briarproject.briar.android; +import android.annotation.SuppressLint; import android.app.Application; +import android.content.SharedPreferences; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; @@ -16,19 +18,23 @@ import java.io.InputStream; import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; import java.util.List; import java.util.Set; -import java.util.TreeSet; import java.util.logging.Logger; -import javax.annotation.Nullable; import javax.inject.Inject; import static android.Manifest.permission.SYSTEM_ALERT_WINDOW; import static android.content.pm.ApplicationInfo.FLAG_SYSTEM; import static android.content.pm.ApplicationInfo.FLAG_UPDATED_SYSTEM_APP; +import static android.content.pm.PackageInfo.REQUESTED_PERMISSION_GRANTED; import static android.content.pm.PackageManager.GET_PERMISSIONS; import static android.content.pm.PackageManager.GET_SIGNATURES; +import static android.os.Build.VERSION.SDK_INT; import static java.util.logging.Level.WARNING; @NotNullByDefault @@ -56,54 +62,75 @@ class ScreenFilterMonitorImpl implements ScreenFilterMonitor { "82BA35E003C1B4B10DD244A8EE24FFFD333872AB5221985EDAB0FC0D" + "0B145B6AA192858E79020103"; + private static final String PREF_KEY_ALLOWED = "allowedOverlayApps"; + private final PackageManager pm; + private final SharedPreferences prefs; @Inject - ScreenFilterMonitorImpl(Application app) { + ScreenFilterMonitorImpl(Application app, SharedPreferences prefs) { pm = app.getPackageManager(); + this.prefs = prefs; } @Override @UiThread - public Set getApps() { - Set screenFilterApps = new TreeSet<>(); + public Collection getApps() { + Set allowed = prefs.getStringSet(PREF_KEY_ALLOWED, + Collections.emptySet()); + List apps = new ArrayList<>(); List packageInfos = pm.getInstalledPackages(GET_PERMISSIONS); for (PackageInfo packageInfo : packageInfos) { - if (isOverlayApp(packageInfo)) { - String name = pkgToString(packageInfo); - if (name != null) { - screenFilterApps.add(name); - } + if (!allowed.contains(packageInfo.packageName) + && isOverlayApp(packageInfo)) { + String name = getAppName(packageInfo); + apps.add(new AppDetails(name, packageInfo.packageName)); } } - return screenFilterApps; + Collections.sort(apps, (a, b) -> a.name.compareTo(b.name)); + return apps; } - // Fetches the application name for a given package. - @Nullable - private String pkgToString(PackageInfo pkgInfo) { + @Override + public void allowApps(Collection packageNames) { + Set allowed = prefs.getStringSet(PREF_KEY_ALLOWED, + Collections.emptySet()); + Set merged = new HashSet<>(allowed); + merged.addAll(packageNames); + prefs.edit().putStringSet(PREF_KEY_ALLOWED, merged).apply(); + } + + // Returns the application name for a given package, or the package name + // if no application name is available + private String getAppName(PackageInfo pkgInfo) { CharSequence seq = pm.getApplicationLabel(pkgInfo.applicationInfo); - if (seq != null) { - return seq.toString(); - } - return null; + return seq == null ? pkgInfo.packageName : seq.toString(); } // Checks if an installed package is a user app using the permission. private boolean isOverlayApp(PackageInfo packageInfo) { int mask = FLAG_SYSTEM | FLAG_UPDATED_SYSTEM_APP; // Ignore system apps - if ((packageInfo.applicationInfo.flags & mask) != 0) { - return false; - } + if ((packageInfo.applicationInfo.flags & mask) != 0) return false; // Ignore Play Services, it's effectively a system app - if (isPlayServices(packageInfo.packageName)) { - return false; - } + if (isPlayServices(packageInfo.packageName)) return false; // Get permissions String[] requestedPermissions = packageInfo.requestedPermissions; - if (requestedPermissions != null) { + if (requestedPermissions == null) return false; + if (SDK_INT >= 16 && SDK_INT < 23) { + // Check whether the permission has been requested and granted + int[] flags = packageInfo.requestedPermissionsFlags; + if (flags == null || flags.length != requestedPermissions.length) + throw new AssertionError(); + for (int i = 0; i < requestedPermissions.length; i++) { + if (requestedPermissions[i].equals(SYSTEM_ALERT_WINDOW) + && (flags[i] & REQUESTED_PERMISSION_GRANTED) != 0) { + return true; + } + } + } else { + // Check whether the permission has been requested for (String requestedPermission : requestedPermissions) { if (requestedPermission.equals(SYSTEM_ALERT_WINDOW)) { return true; @@ -113,6 +140,7 @@ class ScreenFilterMonitorImpl implements ScreenFilterMonitor { return false; } + @SuppressLint("PackageManagerGetSignatures") private boolean isPlayServices(String pkg) { if (!PLAY_SERVICES_PACKAGE.equals(pkg)) return false; try { diff --git a/briar-android/src/main/java/org/briarproject/briar/android/activity/ActivityComponent.java b/briar-android/src/main/java/org/briarproject/briar/android/activity/ActivityComponent.java index f02774ae5..4066c384a 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/activity/ActivityComponent.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/activity/ActivityComponent.java @@ -21,6 +21,7 @@ import org.briarproject.briar.android.forum.CreateForumActivity; import org.briarproject.briar.android.forum.ForumActivity; import org.briarproject.briar.android.forum.ForumListFragment; import org.briarproject.briar.android.forum.ForumModule; +import org.briarproject.briar.android.fragment.ScreenFilterDialogFragment; import org.briarproject.briar.android.introduction.ContactChooserFragment; import org.briarproject.briar.android.introduction.IntroductionActivity; import org.briarproject.briar.android.introduction.IntroductionMessageFragment; @@ -152,7 +153,9 @@ public interface ActivityComponent { // Fragments void inject(AuthorNameFragment fragment); + void inject(PasswordFragment fragment); + void inject(DozeFragment fragment); void inject(ContactListFragment fragment); @@ -189,4 +192,5 @@ public interface ActivityComponent { void inject(SettingsFragment fragment); + void inject(ScreenFilterDialogFragment fragment); } diff --git a/briar-android/src/main/java/org/briarproject/briar/android/activity/ActivityModule.java b/briar-android/src/main/java/org/briarproject/briar/android/activity/ActivityModule.java index 2a00be5cc..2651833f1 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/activity/ActivityModule.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/activity/ActivityModule.java @@ -1,7 +1,6 @@ package org.briarproject.briar.android.activity; import android.app.Activity; -import android.content.SharedPreferences; import org.briarproject.briar.android.controller.BriarController; import org.briarproject.briar.android.controller.BriarControllerImpl; @@ -19,7 +18,6 @@ import org.briarproject.briar.android.navdrawer.NavDrawerControllerImpl; import dagger.Module; import dagger.Provides; -import static android.content.Context.MODE_PRIVATE; import static org.briarproject.briar.android.BriarService.BriarServiceConnection; @Module @@ -57,12 +55,6 @@ public class ActivityModule { return configController; } - @ActivityScope - @Provides - SharedPreferences provideSharedPreferences(Activity activity) { - return activity.getSharedPreferences("db", MODE_PRIVATE); - } - @ActivityScope @Provides PasswordController providePasswordController( diff --git a/briar-android/src/main/java/org/briarproject/briar/android/activity/BaseActivity.java b/briar-android/src/main/java/org/briarproject/briar/android/activity/BaseActivity.java index 88e6302ec..59e0cca70 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/activity/BaseActivity.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/activity/BaseActivity.java @@ -24,10 +24,11 @@ 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.AppDetails; import java.util.ArrayList; +import java.util.Collection; import java.util.List; -import java.util.Set; import javax.annotation.Nullable; import javax.inject.Inject; @@ -132,16 +133,19 @@ public abstract class BaseActivity extends AppCompatActivity .commit(); } - private void showScreenFilterWarning() { - if (dialogFrag != null && dialogFrag.isVisible()) return; - Set apps = screenFilterMonitor.getApps(); - if (apps.isEmpty()) return; - dialogFrag = - ScreenFilterDialogFragment.newInstance(new ArrayList<>(apps)); + private boolean showScreenFilterWarning() { + // If the dialog is already visible, filter the tap + if (dialogFrag != null && dialogFrag.isVisible()) return false; + Collection apps = screenFilterMonitor.getApps(); + // If all overlay apps are allowed or system apps, allow the tap + if (apps.isEmpty()) return true; + dialogFrag = ScreenFilterDialogFragment.newInstance(apps); dialogFrag.setCancelable(false); // Show dialog unless onSaveInstanceState() has been called, see #1112 FragmentManager fm = getSupportFragmentManager(); if (!fm.isStateSaved()) dialogFrag.show(fm, dialogFrag.getTag()); + // Filter the tap + return false; } @Override @@ -198,7 +202,10 @@ public abstract class BaseActivity extends AppCompatActivity View decorView = getWindow().getDecorView(); if (decorView instanceof ViewGroup) { Toolbar toolbar = findToolbar((ViewGroup) decorView); - if (toolbar != null) toolbar.setFilterTouchesWhenObscured(true); + if (toolbar != null) { + boolean filter = !screenFilterMonitor.getApps().isEmpty(); + toolbar.setFilterTouchesWhenObscured(filter); + } } } @@ -239,7 +246,7 @@ public abstract class BaseActivity extends AppCompatActivity } @Override - public void onTapFiltered() { - showScreenFilterWarning(); + public boolean shouldAllowTap() { + return showScreenFilterWarning(); } } diff --git a/briar-android/src/main/java/org/briarproject/briar/android/fragment/ScreenFilterDialogFragment.java b/briar-android/src/main/java/org/briarproject/briar/android/fragment/ScreenFilterDialogFragment.java index b1e644e96..037438b50 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/fragment/ScreenFilterDialogFragment.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/fragment/ScreenFilterDialogFragment.java @@ -1,40 +1,86 @@ package org.briarproject.briar.android.fragment; +import android.annotation.SuppressLint; +import android.app.Activity; import android.app.Dialog; 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.NotNullByDefault; +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 org.briarproject.briar.api.android.ScreenFilterMonitor; +import org.briarproject.briar.api.android.ScreenFilterMonitor.AppDetails; import java.util.ArrayList; +import java.util.Collection; import javax.annotation.Nullable; +import javax.inject.Inject; -@NotNullByDefault +@MethodsNotNullByDefault +@ParametersNotNullByDefault public class ScreenFilterDialogFragment extends DialogFragment { + @Inject + ScreenFilterMonitor screenFilterMonitor; + public static ScreenFilterDialogFragment newInstance( - ArrayList apps) { + Collection apps) { ScreenFilterDialogFragment frag = new ScreenFilterDialogFragment(); Bundle args = new Bundle(); - args.putStringArrayList("apps", apps); + ArrayList appNames = new ArrayList<>(); + for (AppDetails a : apps) appNames.add(a.name); + args.putStringArrayList("appNames", appNames); + ArrayList packageNames = new ArrayList<>(); + for (AppDetails a : apps) packageNames.add(a.packageName); + args.putStringArrayList("packageNames", packageNames); frag.setArguments(args); return frag; } + @Override + public void onActivityCreated(@Nullable Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + Activity activity = getActivity(); + if (activity == null) throw new IllegalStateException(); + ((BaseActivity) activity).getActivityComponent().inject(this); + } + @Override public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) { - AlertDialog.Builder builder = new AlertDialog.Builder(getActivity(), + Activity activity = getActivity(); + if (activity == null) throw new IllegalStateException(); + AlertDialog.Builder builder = new AlertDialog.Builder(activity, R.style.BriarDialogThemeNoFilter); builder.setTitle(R.string.screen_filter_title); - ArrayList apps = getArguments().getStringArrayList("apps"); - builder.setMessage(getString(R.string.screen_filter_body, - TextUtils.join("\n", apps))); - builder.setNeutralButton(R.string.continue_button, - (dialog, which) -> dialog.dismiss()); + Bundle args = getArguments(); + if (args == null) throw new IllegalStateException(); + ArrayList appNames = args.getStringArrayList("appNames"); + ArrayList packageNames = + args.getStringArrayList("packageNames"); + if (appNames == null || packageNames == null) + throw new IllegalStateException(); + LayoutInflater inflater = activity.getLayoutInflater(); + // See https://stackoverflow.com/a/24720976/6314875 + @SuppressLint("InflateParams") + View dialogView = inflater.inflate(R.layout.dialog_screen_filter, null); + builder.setView(dialogView); + TextView message = dialogView.findViewById(R.id.screen_filter_message); + message.setText(getString(R.string.screen_filter_body, + TextUtils.join("\n", appNames))); + CheckBox allow = dialogView.findViewById(R.id.screen_filter_checkbox); + builder.setNeutralButton(R.string.continue_button, (dialog, which) -> { + if (allow.isChecked()) screenFilterMonitor.allowApps(packageNames); + dialog.dismiss(); + }); return builder.create(); } } diff --git a/briar-android/src/main/java/org/briarproject/briar/android/widget/TapSafeFrameLayout.java b/briar-android/src/main/java/org/briarproject/briar/android/widget/TapSafeFrameLayout.java index 840e68b08..701406e1f 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/widget/TapSafeFrameLayout.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/widget/TapSafeFrameLayout.java @@ -40,12 +40,12 @@ public class TapSafeFrameLayout extends FrameLayout { @Override public boolean onFilterTouchEventForSecurity(MotionEvent e) { - boolean filter = (e.getFlags() & FLAG_WINDOW_IS_OBSCURED) != 0; - if (filter && listener != null) listener.onTapFiltered(); - return !filter; + boolean obscured = (e.getFlags() & FLAG_WINDOW_IS_OBSCURED) != 0; + if (obscured && listener != null) return listener.shouldAllowTap(); + else return !obscured; } public interface OnTapFilteredListener { - void onTapFiltered(); + boolean shouldAllowTap(); } } diff --git a/briar-android/src/main/java/org/briarproject/briar/api/android/ScreenFilterMonitor.java b/briar-android/src/main/java/org/briarproject/briar/api/android/ScreenFilterMonitor.java index 9df5add2f..a14b3b078 100644 --- a/briar-android/src/main/java/org/briarproject/briar/api/android/ScreenFilterMonitor.java +++ b/briar-android/src/main/java/org/briarproject/briar/api/android/ScreenFilterMonitor.java @@ -2,10 +2,37 @@ package org.briarproject.briar.api.android; import android.support.annotation.UiThread; -import java.util.Set; +import org.briarproject.bramble.api.nullsafety.NotNullByDefault; +import java.util.Collection; + +@NotNullByDefault public interface ScreenFilterMonitor { + /** + * Returns the details of all apps that have requested the + * SYSTEM_ALERT_WINDOW permission, excluding system apps, Google Play + * Services, and any apps that have been allowed by calling + * {@link #allowApps(Collection)}. + */ @UiThread - Set getApps(); + Collection getApps(); + + /** + * Allows the apps with the given package names to use overlay windows. + * They will not be returned by future calls to {@link #getApps()}. + */ + @UiThread + void allowApps(Collection packageNames); + + class AppDetails { + + public final String name; + public final String packageName; + + public AppDetails(String name, String packageName) { + this.name = name; + this.packageName = packageName; + } + } } diff --git a/briar-android/src/main/res/layout/dialog_screen_filter.xml b/briar-android/src/main/res/layout/dialog_screen_filter.xml new file mode 100644 index 000000000..c09d0dea7 --- /dev/null +++ b/briar-android/src/main/res/layout/dialog_screen_filter.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/briar-android/src/main/res/values/strings.xml b/briar-android/src/main/res/values/strings.xml index 97da612ac..7f27eeada 100644 --- a/briar-android/src/main/res/values/strings.xml +++ b/briar-android/src/main/res/values/strings.xml @@ -397,6 +397,7 @@ Screen overlay detected 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 + Allow these apps to draw on top Camera permission From 863c90826799a30d53bb257e333ea8b59a8ad054 Mon Sep 17 00:00:00 2001 From: akwizgran Date: Fri, 5 Jan 2018 11:58:22 +0000 Subject: [PATCH 2/7] Cache the list of overlay apps. --- .../briarproject/briar/android/AppModule.java | 3 + .../android/ScreenFilterMonitorImpl.java | 77 +++++++++++++++++-- .../briar/android/activity/BaseActivity.java | 40 +++++++--- .../fragment/ScreenFilterDialogFragment.java | 17 ++++ 4 files changed, 118 insertions(+), 19 deletions(-) diff --git a/briar-android/src/main/java/org/briarproject/briar/android/AppModule.java b/briar-android/src/main/java/org/briarproject/briar/android/AppModule.java index 947d86076..eba5d57ee 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/AppModule.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/AppModule.java @@ -180,8 +180,11 @@ public class AppModule { } @Provides + @Singleton ScreenFilterMonitor provideScreenFilterMonitor( + LifecycleManager lifecycleManager, ScreenFilterMonitorImpl screenFilterMonitor) { + lifecycleManager.registerService(screenFilterMonitor); return screenFilterMonitor; } diff --git a/briar-android/src/main/java/org/briarproject/briar/android/ScreenFilterMonitorImpl.java b/briar-android/src/main/java/org/briarproject/briar/android/ScreenFilterMonitorImpl.java index d9c5ac65e..fa9122c45 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/ScreenFilterMonitorImpl.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/ScreenFilterMonitorImpl.java @@ -2,6 +2,10 @@ package org.briarproject.briar.android; import android.annotation.SuppressLint; 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.PackageManager; @@ -9,7 +13,10 @@ import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.Signature; import android.support.annotation.UiThread; +import org.briarproject.bramble.api.lifecycle.Service; +import org.briarproject.bramble.api.lifecycle.ServiceException; import org.briarproject.bramble.api.nullsafety.NotNullByDefault; +import org.briarproject.bramble.api.system.AndroidExecutor; import org.briarproject.bramble.util.StringUtils; import org.briarproject.briar.api.android.ScreenFilterMonitor; @@ -24,11 +31,17 @@ import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.logging.Logger; +import javax.annotation.Nullable; import javax.inject.Inject; import static android.Manifest.permission.SYSTEM_ALERT_WINDOW; +import static android.content.Intent.ACTION_PACKAGE_ADDED; +import static android.content.Intent.ACTION_PACKAGE_CHANGED; +import static android.content.Intent.ACTION_PACKAGE_REMOVED; +import static android.content.Intent.ACTION_PACKAGE_REPLACED; import static android.content.pm.ApplicationInfo.FLAG_SYSTEM; import static android.content.pm.ApplicationInfo.FLAG_UPDATED_SYSTEM_APP; import static android.content.pm.PackageInfo.REQUESTED_PERMISSION_GRANTED; @@ -38,7 +51,7 @@ import static android.os.Build.VERSION.SDK_INT; import static java.util.logging.Level.WARNING; @NotNullByDefault -class ScreenFilterMonitorImpl implements ScreenFilterMonitor { +class ScreenFilterMonitorImpl implements ScreenFilterMonitor, Service { private static final Logger LOG = Logger.getLogger(ScreenFilterMonitorImpl.class.getName()); @@ -65,17 +78,32 @@ class ScreenFilterMonitorImpl implements ScreenFilterMonitor { private static final String PREF_KEY_ALLOWED = "allowedOverlayApps"; private final PackageManager pm; + private final Application app; + private final AndroidExecutor androidExecutor; private final SharedPreferences prefs; + private final AtomicBoolean used = new AtomicBoolean(false); + + // UiThread + @Nullable + private BroadcastReceiver receiver = null; + + // UiThread + @Nullable + private Collection cachedApps = null; @Inject - ScreenFilterMonitorImpl(Application app, SharedPreferences prefs) { + ScreenFilterMonitorImpl(Application app, AndroidExecutor androidExecutor, + SharedPreferences prefs) { pm = app.getPackageManager(); + this.app = app; + this.androidExecutor = androidExecutor; this.prefs = prefs; } @Override @UiThread public Collection getApps() { + if (cachedApps != null) return cachedApps; Set allowed = prefs.getStringSet(PREF_KEY_ALLOWED, Collections.emptySet()); List apps = new ArrayList<>(); @@ -89,11 +117,15 @@ class ScreenFilterMonitorImpl implements ScreenFilterMonitor { } } Collections.sort(apps, (a, b) -> a.name.compareTo(b.name)); + apps = Collections.unmodifiableList(apps); + cachedApps = apps; return apps; } @Override + @UiThread public void allowApps(Collection packageNames) { + cachedApps = null; Set allowed = prefs.getStringSet(PREF_KEY_ALLOWED, Collections.emptySet()); Set merged = new HashSet<>(allowed); @@ -121,12 +153,11 @@ class ScreenFilterMonitorImpl implements ScreenFilterMonitor { if (SDK_INT >= 16 && SDK_INT < 23) { // Check whether the permission has been requested and granted int[] flags = packageInfo.requestedPermissionsFlags; - if (flags == null || flags.length != requestedPermissions.length) - throw new AssertionError(); for (int i = 0; i < requestedPermissions.length; i++) { - if (requestedPermissions[i].equals(SYSTEM_ALERT_WINDOW) - && (flags[i] & REQUESTED_PERMISSION_GRANTED) != 0) { - return true; + if (requestedPermissions[i].equals(SYSTEM_ALERT_WINDOW)) { + // 'flags' may be null on Robolectric + return flags == null || + (flags[i] & REQUESTED_PERMISSION_GRANTED) != 0; } } } else { @@ -163,4 +194,36 @@ class ScreenFilterMonitorImpl implements ScreenFilterMonitor { return false; } } + + @Override + public void startService() throws ServiceException { + if (used.getAndSet(true)) throw new IllegalStateException(); + androidExecutor.runOnUiThread(() -> { + IntentFilter filter = new IntentFilter(); + filter.addAction(ACTION_PACKAGE_ADDED); + filter.addAction(ACTION_PACKAGE_CHANGED); + filter.addAction(ACTION_PACKAGE_REMOVED); + filter.addAction(ACTION_PACKAGE_REPLACED); + filter.addDataScheme("package"); + receiver = new PackageBroadcastReceiver(); + app.registerReceiver(receiver, filter); + cachedApps = null; + }); + } + + @Override + public void stopService() throws ServiceException { + androidExecutor.runOnUiThread(() -> { + if (receiver != null) app.unregisterReceiver(receiver); + }); + } + + private class PackageBroadcastReceiver extends BroadcastReceiver { + + @Override + @UiThread + public void onReceive(Context context, Intent intent) { + cachedApps = null; + } + } } diff --git a/briar-android/src/main/java/org/briarproject/briar/android/activity/BaseActivity.java b/briar-android/src/main/java/org/briarproject/briar/android/activity/BaseActivity.java index 59e0cca70..dd684b7d3 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/activity/BaseActivity.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/activity/BaseActivity.java @@ -49,7 +49,10 @@ public abstract class BaseActivity extends AppCompatActivity private final List lifecycleControllers = new ArrayList<>(); private boolean destroyed = false; + private ScreenFilterDialogFragment dialogFrag; + private Toolbar toolbar = null; + private boolean searchedForToolbar = false; public abstract void injectActivity(ActivityComponent component); @@ -108,6 +111,12 @@ public abstract class BaseActivity extends AppCompatActivity } } + @Override + protected void onResume() { + super.onResume(); + protectToolbar(); + } + @Override protected void onPause() { super.onPause(); @@ -137,13 +146,18 @@ public abstract class BaseActivity extends AppCompatActivity // If the dialog is already visible, filter the tap if (dialogFrag != null && dialogFrag.isVisible()) return false; Collection apps = screenFilterMonitor.getApps(); - // If all overlay apps are allowed or system apps, allow the tap + // If there are no overlay apps that haven't been allowed, allow the tap if (apps.isEmpty()) return true; + // Create dialog dialogFrag = ScreenFilterDialogFragment.newInstance(apps); dialogFrag.setCancelable(false); // Show dialog unless onSaveInstanceState() has been called, see #1112 FragmentManager fm = getSupportFragmentManager(); - if (!fm.isStateSaved()) dialogFrag.show(fm, dialogFrag.getTag()); + if (!fm.isStateSaved()) { + // When dialog is dismissed, update protection of toolbar + dialogFrag.setDismissListener(this::protectToolbar); + dialogFrag.show(fm, dialogFrag.getTag()); + } // Filter the tap return false; } @@ -199,16 +213,21 @@ public abstract class BaseActivity extends AppCompatActivity * is outside the wrapper. */ private void protectToolbar() { - View decorView = getWindow().getDecorView(); - if (decorView instanceof ViewGroup) { - Toolbar toolbar = findToolbar((ViewGroup) decorView); - if (toolbar != null) { - boolean filter = !screenFilterMonitor.getApps().isEmpty(); - toolbar.setFilterTouchesWhenObscured(filter); - } + findToolbar(); + if (toolbar != null) { + boolean filter = !screenFilterMonitor.getApps().isEmpty(); + toolbar.setFilterTouchesWhenObscured(filter); } } + private void findToolbar() { + if (searchedForToolbar) return; + View decorView = getWindow().getDecorView(); + if (decorView instanceof ViewGroup) + toolbar = findToolbar((ViewGroup) decorView); + searchedForToolbar = true; + } + @Nullable private Toolbar findToolbar(ViewGroup vg) { for (int i = 0, len = vg.getChildCount(); i < len; i++) { @@ -230,19 +249,16 @@ public abstract class BaseActivity extends AppCompatActivity @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 diff --git a/briar-android/src/main/java/org/briarproject/briar/android/fragment/ScreenFilterDialogFragment.java b/briar-android/src/main/java/org/briarproject/briar/android/fragment/ScreenFilterDialogFragment.java index 037438b50..0539497c7 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/fragment/ScreenFilterDialogFragment.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/fragment/ScreenFilterDialogFragment.java @@ -3,6 +3,7 @@ package org.briarproject.briar.android.fragment; import android.annotation.SuppressLint; import android.app.Activity; import android.app.Dialog; +import android.content.DialogInterface; import android.os.Bundle; import android.support.v4.app.DialogFragment; import android.support.v7.app.AlertDialog; @@ -32,6 +33,8 @@ public class ScreenFilterDialogFragment extends DialogFragment { @Inject ScreenFilterMonitor screenFilterMonitor; + DismissListener dismissListener = null; + public static ScreenFilterDialogFragment newInstance( Collection apps) { ScreenFilterDialogFragment frag = new ScreenFilterDialogFragment(); @@ -46,6 +49,10 @@ public class ScreenFilterDialogFragment extends DialogFragment { return frag; } + public void setDismissListener(DismissListener dismissListener) { + this.dismissListener = dismissListener; + } + @Override public void onActivityCreated(@Nullable Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); @@ -83,4 +90,14 @@ public class ScreenFilterDialogFragment extends DialogFragment { }); return builder.create(); } + + @Override + public void onDismiss(DialogInterface dialog) { + super.onDismiss(dialog); + if (dismissListener != null) dismissListener.onDialogDismissed(); + } + + public interface DismissListener { + void onDialogDismissed(); + } } From 60d38b034d68d34e3e4722473e4f4ac0ae4fe11e Mon Sep 17 00:00:00 2001 From: akwizgran Date: Tue, 16 Jan 2018 14:00:59 +0000 Subject: [PATCH 3/7] Set layout weight so checkbox is visible. --- briar-android/src/main/res/layout/dialog_screen_filter.xml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/briar-android/src/main/res/layout/dialog_screen_filter.xml b/briar-android/src/main/res/layout/dialog_screen_filter.xml index c09d0dea7..b883d16e7 100644 --- a/briar-android/src/main/res/layout/dialog_screen_filter.xml +++ b/briar-android/src/main/res/layout/dialog_screen_filter.xml @@ -7,7 +7,8 @@ + android:layout_height="wrap_content" + android:layout_weight="1"> From dae8e6d759295cafe8fdc8f549dd55716b3cfdbb Mon Sep 17 00:00:00 2001 From: akwizgran Date: Tue, 16 Jan 2018 13:52:58 +0000 Subject: [PATCH 4/7] Re-show dialog when activity resumes or is recreated. --- .../briar/android/activity/BaseActivity.java | 47 +++++++++++++++---- .../fragment/ScreenFilterDialogFragment.java | 1 + 2 files changed, 38 insertions(+), 10 deletions(-) diff --git a/briar-android/src/main/java/org/briarproject/briar/android/activity/BaseActivity.java b/briar-android/src/main/java/org/briarproject/briar/android/activity/BaseActivity.java index dd684b7d3..b4352e71f 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/activity/BaseActivity.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/activity/BaseActivity.java @@ -41,6 +41,8 @@ import static org.briarproject.briar.android.TestingConstants.PREVENT_SCREENSHOT public abstract class BaseActivity extends AppCompatActivity implements DestroyableContext, OnTapFilteredListener { + private static final String STATE_SHOULD_SHOW_DIALOG = "shouldShowDialog"; + @Inject protected ScreenFilterMonitor screenFilterMonitor; @@ -50,7 +52,11 @@ public abstract class BaseActivity extends AppCompatActivity new ArrayList<>(); private boolean destroyed = false; - private ScreenFilterDialogFragment dialogFrag; + @Nullable + private ScreenFilterDialogFragment dialogFrag = null; + private boolean shouldShowDialog = false; + + @Nullable private Toolbar toolbar = null; private boolean searchedForToolbar = false; @@ -61,8 +67,8 @@ public abstract class BaseActivity extends AppCompatActivity } @Override - public void onCreate(@Nullable Bundle savedInstanceState) { - super.onCreate(savedInstanceState); + public void onCreate(@Nullable Bundle state) { + super.onCreate(state); if (PREVENT_SCREENSHOTS) getWindow().addFlags(FLAG_SECURE); @@ -80,6 +86,15 @@ public abstract class BaseActivity extends AppCompatActivity for (ActivityLifecycleController alc : lifecycleControllers) { alc.onActivityCreate(this); } + + if (state != null) + shouldShowDialog = state.getBoolean(STATE_SHOULD_SHOW_DIALOG); + } + + @Override + protected void onSaveInstanceState(Bundle state) { + super.onSaveInstanceState(state); + state.putBoolean(STATE_SHOULD_SHOW_DIALOG, shouldShowDialog); } public ActivityComponent getActivityComponent() { @@ -115,6 +130,7 @@ public abstract class BaseActivity extends AppCompatActivity protected void onResume() { super.onResume(); protectToolbar(); + if (shouldShowDialog) showScreenFilterWarning(); } @Override @@ -123,6 +139,7 @@ public abstract class BaseActivity extends AppCompatActivity if (dialogFrag != null) { dialogFrag.dismiss(); dialogFrag = null; + shouldShowDialog = true; // Show dialog again on resume } } @@ -144,18 +161,28 @@ public abstract class BaseActivity extends AppCompatActivity private boolean showScreenFilterWarning() { // If the dialog is already visible, filter the tap - if (dialogFrag != null && dialogFrag.isVisible()) return false; + if (dialogFrag != null) return false; Collection apps = screenFilterMonitor.getApps(); - // If there are no overlay apps that haven't been allowed, allow the tap - if (apps.isEmpty()) return true; - // Create dialog - dialogFrag = ScreenFilterDialogFragment.newInstance(apps); - dialogFrag.setCancelable(false); + // If all overlay apps have been allowed, allow the tap + if (apps.isEmpty()) { + shouldShowDialog = false; // May have been true when state was saved + return true; + } // Show dialog unless onSaveInstanceState() has been called, see #1112 FragmentManager fm = getSupportFragmentManager(); if (!fm.isStateSaved()) { + // Create dialog + dialogFrag = ScreenFilterDialogFragment.newInstance(apps); + shouldShowDialog = true; // When dialog is dismissed, update protection of toolbar - dialogFrag.setDismissListener(this::protectToolbar); + dialogFrag.setDismissListener(() -> { + dialogFrag = null; + shouldShowDialog = false; + protectToolbar(); + }); + // Hide soft keyboard when (re)showing dialog + View focus = getCurrentFocus(); + if (focus != null) hideSoftKeyboard(focus); dialogFrag.show(fm, dialogFrag.getTag()); } // Filter the tap diff --git a/briar-android/src/main/java/org/briarproject/briar/android/fragment/ScreenFilterDialogFragment.java b/briar-android/src/main/java/org/briarproject/briar/android/fragment/ScreenFilterDialogFragment.java index 0539497c7..810f61f39 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/fragment/ScreenFilterDialogFragment.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/fragment/ScreenFilterDialogFragment.java @@ -88,6 +88,7 @@ public class ScreenFilterDialogFragment extends DialogFragment { if (allow.isChecked()) screenFilterMonitor.allowApps(packageNames); dialog.dismiss(); }); + builder.setCancelable(false); return builder.create(); } From dab9a3e73d47e5ad8370fb59bcb5de36c21fe167 Mon Sep 17 00:00:00 2001 From: akwizgran Date: Fri, 19 Jan 2018 09:49:16 +0000 Subject: [PATCH 5/7] Update screen overlay warning text. --- briar-android/src/main/res/values/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/briar-android/src/main/res/values/strings.xml b/briar-android/src/main/res/values/strings.xml index 7f27eeada..a990a022f 100644 --- a/briar-android/src/main/res/values/strings.xml +++ b/briar-android/src/main/res/values/strings.xml @@ -396,7 +396,7 @@ Screen overlay detected - 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 + 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\nThe following apps might be drawing on top:\n\n%1$s Allow these apps to draw on top From a50ded2d50b9016875166066ca42ed9026c186f1 Mon Sep 17 00:00:00 2001 From: akwizgran Date: Mon, 22 Jan 2018 17:22:04 +0000 Subject: [PATCH 6/7] Simplify dialog handling, work around Android bug. --- .../briar/android/activity/BaseActivity.java | 67 ++++++------------- .../fragment/ScreenFilterDialogFragment.java | 2 + .../briar/android/util/UiUtils.java | 10 ++- .../android/widget/TapSafeFrameLayout.java | 7 +- 4 files changed, 35 insertions(+), 51 deletions(-) diff --git a/briar-android/src/main/java/org/briarproject/briar/android/activity/BaseActivity.java b/briar-android/src/main/java/org/briarproject/briar/android/activity/BaseActivity.java index b4352e71f..7c8f5061a 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/activity/BaseActivity.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/activity/BaseActivity.java @@ -4,6 +4,7 @@ import android.os.Bundle; import android.os.IBinder; import android.support.annotation.LayoutRes; import android.support.annotation.UiThread; +import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.Toolbar; @@ -21,6 +22,7 @@ import org.briarproject.briar.android.controller.ActivityLifecycleController; import org.briarproject.briar.android.forum.ForumModule; import org.briarproject.briar.android.fragment.BaseFragment; import org.briarproject.briar.android.fragment.ScreenFilterDialogFragment; +import org.briarproject.briar.android.util.UiUtils; import org.briarproject.briar.android.widget.TapSafeFrameLayout; import org.briarproject.briar.android.widget.TapSafeFrameLayout.OnTapFilteredListener; import org.briarproject.briar.api.android.ScreenFilterMonitor; @@ -41,8 +43,6 @@ import static org.briarproject.briar.android.TestingConstants.PREVENT_SCREENSHOT public abstract class BaseActivity extends AppCompatActivity implements DestroyableContext, OnTapFilteredListener { - private static final String STATE_SHOULD_SHOW_DIALOG = "shouldShowDialog"; - @Inject protected ScreenFilterMonitor screenFilterMonitor; @@ -52,10 +52,6 @@ public abstract class BaseActivity extends AppCompatActivity new ArrayList<>(); private boolean destroyed = false; - @Nullable - private ScreenFilterDialogFragment dialogFrag = null; - private boolean shouldShowDialog = false; - @Nullable private Toolbar toolbar = null; private boolean searchedForToolbar = false; @@ -86,15 +82,6 @@ public abstract class BaseActivity extends AppCompatActivity for (ActivityLifecycleController alc : lifecycleControllers) { alc.onActivityCreate(this); } - - if (state != null) - shouldShowDialog = state.getBoolean(STATE_SHOULD_SHOW_DIALOG); - } - - @Override - protected void onSaveInstanceState(Bundle state) { - super.onSaveInstanceState(state); - state.putBoolean(STATE_SHOULD_SHOW_DIALOG, shouldShowDialog); } public ActivityComponent getActivityComponent() { @@ -116,6 +103,16 @@ public abstract class BaseActivity extends AppCompatActivity for (ActivityLifecycleController alc : lifecycleControllers) { alc.onActivityStart(); } + protectToolbar(); + ScreenFilterDialogFragment f = findDialogFragment(); + if (f != null) f.setDismissListener(this::protectToolbar); + } + + @Nullable + private ScreenFilterDialogFragment findDialogFragment() { + Fragment f = getSupportFragmentManager().findFragmentByTag( + ScreenFilterDialogFragment.TAG); + return (ScreenFilterDialogFragment) f; } @Override @@ -126,23 +123,6 @@ public abstract class BaseActivity extends AppCompatActivity } } - @Override - protected void onResume() { - super.onResume(); - protectToolbar(); - if (shouldShowDialog) showScreenFilterWarning(); - } - - @Override - protected void onPause() { - super.onPause(); - if (dialogFrag != null) { - dialogFrag.dismiss(); - dialogFrag = null; - shouldShowDialog = true; // Show dialog again on resume - } - } - protected void showInitialFragment(BaseFragment f) { getSupportFragmentManager().beginTransaction() .replace(R.id.fragmentContainer, f, f.getUniqueTag()) @@ -161,29 +141,22 @@ public abstract class BaseActivity extends AppCompatActivity private boolean showScreenFilterWarning() { // If the dialog is already visible, filter the tap - if (dialogFrag != null) return false; + ScreenFilterDialogFragment f = findDialogFragment(); + if (f != null && f.isVisible()) return false; Collection apps = screenFilterMonitor.getApps(); // If all overlay apps have been allowed, allow the tap - if (apps.isEmpty()) { - shouldShowDialog = false; // May have been true when state was saved - return true; - } + if (apps.isEmpty()) return true; // Show dialog unless onSaveInstanceState() has been called, see #1112 FragmentManager fm = getSupportFragmentManager(); if (!fm.isStateSaved()) { // Create dialog - dialogFrag = ScreenFilterDialogFragment.newInstance(apps); - shouldShowDialog = true; + f = ScreenFilterDialogFragment.newInstance(apps); // When dialog is dismissed, update protection of toolbar - dialogFrag.setDismissListener(() -> { - dialogFrag = null; - shouldShowDialog = false; - protectToolbar(); - }); + f.setDismissListener(this::protectToolbar); // Hide soft keyboard when (re)showing dialog View focus = getCurrentFocus(); if (focus != null) hideSoftKeyboard(focus); - dialogFrag.show(fm, dialogFrag.getTag()); + f.show(fm, ScreenFilterDialogFragment.TAG); } // Filter the tap return false; @@ -243,7 +216,7 @@ public abstract class BaseActivity extends AppCompatActivity findToolbar(); if (toolbar != null) { boolean filter = !screenFilterMonitor.getApps().isEmpty(); - toolbar.setFilterTouchesWhenObscured(filter); + UiUtils.setFilterTouchesWhenObscured(toolbar, filter); } } @@ -257,6 +230,8 @@ public abstract class BaseActivity extends AppCompatActivity @Nullable private Toolbar findToolbar(ViewGroup vg) { + // Views inside tap-safe layouts are already protected + if (vg instanceof TapSafeFrameLayout) return null; for (int i = 0, len = vg.getChildCount(); i < len; i++) { View child = vg.getChildAt(i); if (child instanceof Toolbar) return (Toolbar) child; diff --git a/briar-android/src/main/java/org/briarproject/briar/android/fragment/ScreenFilterDialogFragment.java b/briar-android/src/main/java/org/briarproject/briar/android/fragment/ScreenFilterDialogFragment.java index 810f61f39..b7c611665 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/fragment/ScreenFilterDialogFragment.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/fragment/ScreenFilterDialogFragment.java @@ -30,6 +30,8 @@ import javax.inject.Inject; @ParametersNotNullByDefault public class ScreenFilterDialogFragment extends DialogFragment { + public static final String TAG = ScreenFilterDialogFragment.class.getName(); + @Inject ScreenFilterMonitor screenFilterMonitor; diff --git a/briar-android/src/main/java/org/briarproject/briar/android/util/UiUtils.java b/briar-android/src/main/java/org/briarproject/briar/android/util/UiUtils.java index 7e31452fe..d6ef54126 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/util/UiUtils.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/util/UiUtils.java @@ -6,7 +6,6 @@ import android.content.Context; import android.content.DialogInterface.OnClickListener; import android.content.Intent; import android.net.Uri; -import android.os.Build; import android.os.PowerManager; import android.support.design.widget.TextInputLayout; import android.support.v4.app.FragmentManager; @@ -162,7 +161,7 @@ public class UiUtils { } public static boolean needsDozeWhitelisting(Context ctx) { - if (Build.VERSION.SDK_INT < 23) return false; + if (SDK_INT < 23) return false; PowerManager pm = (PowerManager) ctx.getSystemService(POWER_SERVICE); String packageName = ctx.getPackageName(); if (pm == null) throw new AssertionError(); @@ -182,4 +181,11 @@ public class UiUtils { return SDK_INT == 24 && MANUFACTURER.equalsIgnoreCase("Samsung"); } + public static void setFilterTouchesWhenObscured(View v, boolean filter) { + v.setFilterTouchesWhenObscured(filter); + // Workaround for Android bug #13530806, see + // https://android.googlesource.com/platform/frameworks/base/+/aba566589e0011c4b973c0d4f77be4e9ee176089%5E%21/core/java/android/view/View.java + if (v.getFilterTouchesWhenObscured() != filter) + v.setFilterTouchesWhenObscured(!filter); + } } diff --git a/briar-android/src/main/java/org/briarproject/briar/android/widget/TapSafeFrameLayout.java b/briar-android/src/main/java/org/briarproject/briar/android/widget/TapSafeFrameLayout.java index 701406e1f..6d285682f 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/widget/TapSafeFrameLayout.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/widget/TapSafeFrameLayout.java @@ -7,6 +7,7 @@ import android.view.MotionEvent; import android.widget.FrameLayout; import org.briarproject.bramble.api.nullsafety.NotNullByDefault; +import org.briarproject.briar.android.util.UiUtils; import javax.annotation.Nullable; @@ -20,18 +21,18 @@ public class TapSafeFrameLayout extends FrameLayout { public TapSafeFrameLayout(Context context) { super(context); - setFilterTouchesWhenObscured(false); + UiUtils.setFilterTouchesWhenObscured(this, false); } public TapSafeFrameLayout(Context context, @Nullable AttributeSet attrs) { super(context, attrs); - setFilterTouchesWhenObscured(false); + UiUtils.setFilterTouchesWhenObscured(this, false); } public TapSafeFrameLayout(Context context, @Nullable AttributeSet attrs, @AttrRes int defStyleAttr) { super(context, attrs, defStyleAttr); - setFilterTouchesWhenObscured(false); + UiUtils.setFilterTouchesWhenObscured(this, false); } public void setOnTapFilteredListener(OnTapFilteredListener listener) { From e0a67d1eb90a9d78905289a6c5d369c0ed4b74ba Mon Sep 17 00:00:00 2001 From: akwizgran Date: Thu, 1 Feb 2018 10:39:26 +0000 Subject: [PATCH 7/7] Remove unused argument. --- .../org/briarproject/briar/android/activity/BriarActivity.java | 2 +- .../org/briarproject/briar/android/blog/BlogPostViewHolder.java | 2 +- .../main/java/org/briarproject/briar/android/util/UiUtils.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/briar-android/src/main/java/org/briarproject/briar/android/activity/BriarActivity.java b/briar-android/src/main/java/org/briarproject/briar/android/activity/BriarActivity.java index 37e8ed140..499696182 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/activity/BriarActivity.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/activity/BriarActivity.java @@ -80,7 +80,7 @@ public abstract class BriarActivity extends BaseActivity { public void setSceneTransitionAnimation() { if (SDK_INT < 21) return; // workaround for #1007 - if (isSamsung7(this)) { + if (isSamsung7()) { return; } Transition slide = new Slide(Gravity.RIGHT); diff --git a/briar-android/src/main/java/org/briarproject/briar/android/blog/BlogPostViewHolder.java b/briar-android/src/main/java/org/briarproject/briar/android/blog/BlogPostViewHolder.java index 3126d8704..e579aea13 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/blog/BlogPostViewHolder.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/blog/BlogPostViewHolder.java @@ -131,7 +131,7 @@ class BlogPostViewHolder extends RecyclerView.ViewHolder { i.putExtra(GROUP_ID, item.getGroupId().getBytes()); i.putExtra(POST_ID, item.getId().getBytes()); - if (Build.VERSION.SDK_INT >= 23 && !isSamsung7(ctx)) { + if (Build.VERSION.SDK_INT >= 23 && !isSamsung7()) { ActivityOptionsCompat options = makeSceneTransitionAnimation((Activity) ctx, layout, getTransitionName(item.getId())); diff --git a/briar-android/src/main/java/org/briarproject/briar/android/util/UiUtils.java b/briar-android/src/main/java/org/briarproject/briar/android/util/UiUtils.java index d6ef54126..26e61f37f 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/util/UiUtils.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/util/UiUtils.java @@ -177,7 +177,7 @@ public class UiUtils { return i; } - public static boolean isSamsung7(Context context) { + public static boolean isSamsung7() { return SDK_INT == 24 && MANUFACTURER.equalsIgnoreCase("Samsung"); }