mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-17 13:19:52 +01:00
Cache the list of overlay apps.
This commit is contained in:
@@ -180,8 +180,11 @@ public class AppModule {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
|
@Singleton
|
||||||
ScreenFilterMonitor provideScreenFilterMonitor(
|
ScreenFilterMonitor provideScreenFilterMonitor(
|
||||||
|
LifecycleManager lifecycleManager,
|
||||||
ScreenFilterMonitorImpl screenFilterMonitor) {
|
ScreenFilterMonitorImpl screenFilterMonitor) {
|
||||||
|
lifecycleManager.registerService(screenFilterMonitor);
|
||||||
return screenFilterMonitor;
|
return screenFilterMonitor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,10 @@ package org.briarproject.briar.android;
|
|||||||
|
|
||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
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.SharedPreferences;
|
||||||
import android.content.pm.PackageInfo;
|
import android.content.pm.PackageInfo;
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
@@ -9,7 +13,10 @@ 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 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.nullsafety.NotNullByDefault;
|
||||||
|
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;
|
||||||
|
|
||||||
@@ -24,11 +31,17 @@ import java.util.Collections;
|
|||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
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.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_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.PackageInfo.REQUESTED_PERMISSION_GRANTED;
|
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;
|
import static java.util.logging.Level.WARNING;
|
||||||
|
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
class ScreenFilterMonitorImpl implements ScreenFilterMonitor {
|
class ScreenFilterMonitorImpl implements ScreenFilterMonitor, Service {
|
||||||
|
|
||||||
private static final Logger LOG =
|
private static final Logger LOG =
|
||||||
Logger.getLogger(ScreenFilterMonitorImpl.class.getName());
|
Logger.getLogger(ScreenFilterMonitorImpl.class.getName());
|
||||||
@@ -65,17 +78,32 @@ class ScreenFilterMonitorImpl implements ScreenFilterMonitor {
|
|||||||
private static final String PREF_KEY_ALLOWED = "allowedOverlayApps";
|
private static final String PREF_KEY_ALLOWED = "allowedOverlayApps";
|
||||||
|
|
||||||
private final PackageManager pm;
|
private final PackageManager pm;
|
||||||
|
private final Application app;
|
||||||
|
private final AndroidExecutor androidExecutor;
|
||||||
private final SharedPreferences prefs;
|
private final SharedPreferences prefs;
|
||||||
|
private final AtomicBoolean used = new AtomicBoolean(false);
|
||||||
|
|
||||||
|
// UiThread
|
||||||
|
@Nullable
|
||||||
|
private BroadcastReceiver receiver = null;
|
||||||
|
|
||||||
|
// UiThread
|
||||||
|
@Nullable
|
||||||
|
private Collection<AppDetails> cachedApps = null;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
ScreenFilterMonitorImpl(Application app, SharedPreferences prefs) {
|
ScreenFilterMonitorImpl(Application app, AndroidExecutor androidExecutor,
|
||||||
|
SharedPreferences prefs) {
|
||||||
pm = app.getPackageManager();
|
pm = app.getPackageManager();
|
||||||
|
this.app = app;
|
||||||
|
this.androidExecutor = androidExecutor;
|
||||||
this.prefs = prefs;
|
this.prefs = prefs;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@UiThread
|
@UiThread
|
||||||
public Collection<AppDetails> getApps() {
|
public Collection<AppDetails> getApps() {
|
||||||
|
if (cachedApps != null) return cachedApps;
|
||||||
Set<String> allowed = prefs.getStringSet(PREF_KEY_ALLOWED,
|
Set<String> allowed = prefs.getStringSet(PREF_KEY_ALLOWED,
|
||||||
Collections.emptySet());
|
Collections.emptySet());
|
||||||
List<AppDetails> apps = new ArrayList<>();
|
List<AppDetails> apps = new ArrayList<>();
|
||||||
@@ -89,11 +117,15 @@ class ScreenFilterMonitorImpl implements ScreenFilterMonitor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Collections.sort(apps, (a, b) -> a.name.compareTo(b.name));
|
Collections.sort(apps, (a, b) -> a.name.compareTo(b.name));
|
||||||
|
apps = Collections.unmodifiableList(apps);
|
||||||
|
cachedApps = apps;
|
||||||
return apps;
|
return apps;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@UiThread
|
||||||
public void allowApps(Collection<String> packageNames) {
|
public void allowApps(Collection<String> packageNames) {
|
||||||
|
cachedApps = null;
|
||||||
Set<String> allowed = prefs.getStringSet(PREF_KEY_ALLOWED,
|
Set<String> allowed = prefs.getStringSet(PREF_KEY_ALLOWED,
|
||||||
Collections.emptySet());
|
Collections.emptySet());
|
||||||
Set<String> merged = new HashSet<>(allowed);
|
Set<String> merged = new HashSet<>(allowed);
|
||||||
@@ -121,12 +153,11 @@ class ScreenFilterMonitorImpl implements ScreenFilterMonitor {
|
|||||||
if (SDK_INT >= 16 && SDK_INT < 23) {
|
if (SDK_INT >= 16 && SDK_INT < 23) {
|
||||||
// Check whether the permission has been requested and granted
|
// Check whether the permission has been requested and granted
|
||||||
int[] flags = packageInfo.requestedPermissionsFlags;
|
int[] flags = packageInfo.requestedPermissionsFlags;
|
||||||
if (flags == null || flags.length != requestedPermissions.length)
|
|
||||||
throw new AssertionError();
|
|
||||||
for (int i = 0; i < requestedPermissions.length; i++) {
|
for (int i = 0; i < requestedPermissions.length; i++) {
|
||||||
if (requestedPermissions[i].equals(SYSTEM_ALERT_WINDOW)
|
if (requestedPermissions[i].equals(SYSTEM_ALERT_WINDOW)) {
|
||||||
&& (flags[i] & REQUESTED_PERMISSION_GRANTED) != 0) {
|
// 'flags' may be null on Robolectric
|
||||||
return true;
|
return flags == null ||
|
||||||
|
(flags[i] & REQUESTED_PERMISSION_GRANTED) != 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -163,4 +194,36 @@ class ScreenFilterMonitorImpl implements ScreenFilterMonitor {
|
|||||||
return false;
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,7 +49,10 @@ public abstract class BaseActivity extends AppCompatActivity
|
|||||||
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;
|
private ScreenFilterDialogFragment dialogFrag;
|
||||||
|
private Toolbar toolbar = null;
|
||||||
|
private boolean searchedForToolbar = false;
|
||||||
|
|
||||||
public abstract void injectActivity(ActivityComponent component);
|
public abstract void injectActivity(ActivityComponent component);
|
||||||
|
|
||||||
@@ -108,6 +111,12 @@ public abstract class BaseActivity extends AppCompatActivity
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onResume() {
|
||||||
|
super.onResume();
|
||||||
|
protectToolbar();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onPause() {
|
protected void onPause() {
|
||||||
super.onPause();
|
super.onPause();
|
||||||
@@ -137,13 +146,18 @@ public abstract class BaseActivity extends AppCompatActivity
|
|||||||
// If the dialog is already visible, filter the tap
|
// If the dialog is already visible, filter the tap
|
||||||
if (dialogFrag != null && dialogFrag.isVisible()) return false;
|
if (dialogFrag != null && dialogFrag.isVisible()) return false;
|
||||||
Collection<AppDetails> apps = screenFilterMonitor.getApps();
|
Collection<AppDetails> 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;
|
if (apps.isEmpty()) return true;
|
||||||
|
// Create dialog
|
||||||
dialogFrag = ScreenFilterDialogFragment.newInstance(apps);
|
dialogFrag = ScreenFilterDialogFragment.newInstance(apps);
|
||||||
dialogFrag.setCancelable(false);
|
dialogFrag.setCancelable(false);
|
||||||
// Show dialog unless onSaveInstanceState() has been called, see #1112
|
// Show dialog unless onSaveInstanceState() has been called, see #1112
|
||||||
FragmentManager fm = getSupportFragmentManager();
|
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
|
// Filter the tap
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -199,16 +213,21 @@ public abstract class BaseActivity extends AppCompatActivity
|
|||||||
* is outside the wrapper.
|
* is outside the wrapper.
|
||||||
*/
|
*/
|
||||||
private void protectToolbar() {
|
private void protectToolbar() {
|
||||||
View decorView = getWindow().getDecorView();
|
findToolbar();
|
||||||
if (decorView instanceof ViewGroup) {
|
if (toolbar != null) {
|
||||||
Toolbar toolbar = findToolbar((ViewGroup) decorView);
|
boolean filter = !screenFilterMonitor.getApps().isEmpty();
|
||||||
if (toolbar != null) {
|
toolbar.setFilterTouchesWhenObscured(filter);
|
||||||
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
|
@Nullable
|
||||||
private Toolbar findToolbar(ViewGroup vg) {
|
private Toolbar findToolbar(ViewGroup vg) {
|
||||||
for (int i = 0, len = vg.getChildCount(); i < len; i++) {
|
for (int i = 0, len = vg.getChildCount(); i < len; i++) {
|
||||||
@@ -230,19 +249,16 @@ public abstract class BaseActivity extends AppCompatActivity
|
|||||||
@Override
|
@Override
|
||||||
public void setContentView(View v) {
|
public void setContentView(View v) {
|
||||||
super.setContentView(makeTapSafeWrapper(v));
|
super.setContentView(makeTapSafeWrapper(v));
|
||||||
protectToolbar();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setContentView(View v, LayoutParams layoutParams) {
|
public void setContentView(View v, LayoutParams layoutParams) {
|
||||||
super.setContentView(makeTapSafeWrapper(v), layoutParams);
|
super.setContentView(makeTapSafeWrapper(v), layoutParams);
|
||||||
protectToolbar();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addContentView(View v, LayoutParams layoutParams) {
|
public void addContentView(View v, LayoutParams layoutParams) {
|
||||||
super.addContentView(makeTapSafeWrapper(v), layoutParams);
|
super.addContentView(makeTapSafeWrapper(v), layoutParams);
|
||||||
protectToolbar();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package org.briarproject.briar.android.fragment;
|
|||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.app.Dialog;
|
import android.app.Dialog;
|
||||||
|
import android.content.DialogInterface;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.v4.app.DialogFragment;
|
import android.support.v4.app.DialogFragment;
|
||||||
import android.support.v7.app.AlertDialog;
|
import android.support.v7.app.AlertDialog;
|
||||||
@@ -32,6 +33,8 @@ public class ScreenFilterDialogFragment extends DialogFragment {
|
|||||||
@Inject
|
@Inject
|
||||||
ScreenFilterMonitor screenFilterMonitor;
|
ScreenFilterMonitor screenFilterMonitor;
|
||||||
|
|
||||||
|
DismissListener dismissListener = null;
|
||||||
|
|
||||||
public static ScreenFilterDialogFragment newInstance(
|
public static ScreenFilterDialogFragment newInstance(
|
||||||
Collection<AppDetails> apps) {
|
Collection<AppDetails> apps) {
|
||||||
ScreenFilterDialogFragment frag = new ScreenFilterDialogFragment();
|
ScreenFilterDialogFragment frag = new ScreenFilterDialogFragment();
|
||||||
@@ -46,6 +49,10 @@ public class ScreenFilterDialogFragment extends DialogFragment {
|
|||||||
return frag;
|
return frag;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setDismissListener(DismissListener dismissListener) {
|
||||||
|
this.dismissListener = dismissListener;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
|
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
|
||||||
super.onActivityCreated(savedInstanceState);
|
super.onActivityCreated(savedInstanceState);
|
||||||
@@ -83,4 +90,14 @@ public class ScreenFilterDialogFragment extends DialogFragment {
|
|||||||
});
|
});
|
||||||
return builder.create();
|
return builder.create();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDismiss(DialogInterface dialog) {
|
||||||
|
super.onDismiss(dialog);
|
||||||
|
if (dismissListener != null) dismissListener.onDialogDismissed();
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface DismissListener {
|
||||||
|
void onDialogDismissed();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user