summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorcyberta <cyberta@riseup.net>2026-04-06 00:42:22 +0200
committercyberta <cyberta@riseup.net>2026-04-15 23:56:05 +0000
commitba4e1500eb9813c2e88eea1d23caa432a21c019c (patch)
treebfe6f4d7b221104c4723c6cabc2b52dbfe202e3b
parent428a0bf3c4048e97b6fb96277672d1ddcad045fc (diff)
update excluded apps list on launch and dynamically after an application gets removed while the app is running. This fixes a wrong counter of excluded apps in the UI
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/base/AppQueryReceiver.java36
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/base/BitmaskApp.java7
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/base/fragments/ExcludeAppsFragment.java109
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/base/fragments/SettingsFragment.java4
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/base/utils/ApplicationInfoManager.java85
5 files changed, 172 insertions, 69 deletions
diff --git a/app/src/main/java/se/leap/bitmaskclient/base/AppQueryReceiver.java b/app/src/main/java/se/leap/bitmaskclient/base/AppQueryReceiver.java
new file mode 100644
index 00000000..d5788345
--- /dev/null
+++ b/app/src/main/java/se/leap/bitmaskclient/base/AppQueryReceiver.java
@@ -0,0 +1,36 @@
+package se.leap.bitmaskclient.base;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+
+import se.leap.bitmaskclient.base.utils.ApplicationInfoManager;
+
+public class AppQueryReceiver extends BroadcastReceiver {
+
+ private static final String TAG = AppQueryReceiver.class.getSimpleName();
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (context == null || intent == null ||
+ intent.getAction() == null || intent.getData() == null ||
+ !intent.getAction().equals(Intent.ACTION_PACKAGE_REMOVED) ||
+ intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
+ return;
+ }
+
+ String packageName = intent.getData().getEncodedSchemeSpecificPart();
+ if (packageName != null) {
+ ApplicationInfoManager appManager = new ApplicationInfoManager(context);
+ appManager.onApplicationRemoved(packageName);
+ }
+ }
+
+ public static void register(Context context) {
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+ filter.addDataScheme("package");
+ context.registerReceiver(new AppQueryReceiver(), filter);
+ }
+} \ No newline at end of file
diff --git a/app/src/main/java/se/leap/bitmaskclient/base/BitmaskApp.java b/app/src/main/java/se/leap/bitmaskclient/base/BitmaskApp.java
index cbe954db..ae4d7e08 100644
--- a/app/src/main/java/se/leap/bitmaskclient/base/BitmaskApp.java
+++ b/app/src/main/java/se/leap/bitmaskclient/base/BitmaskApp.java
@@ -45,6 +45,7 @@ import java.security.Security;
import se.leap.bitmaskclient.BuildConfig;
import se.leap.bitmaskclient.appUpdate.DownloadBroadcastReceiver;
import se.leap.bitmaskclient.base.models.ProviderObservable;
+import se.leap.bitmaskclient.base.utils.ApplicationInfoManager;
import se.leap.bitmaskclient.base.utils.ClientTransportPluginProvider;
import se.leap.bitmaskclient.base.utils.PRNGFixes;
import se.leap.bitmaskclient.base.utils.PreferenceHelper;
@@ -103,6 +104,12 @@ public class BitmaskApp extends MultiDexApplication implements DefaultLifecycleO
intentFilter.addCategory(CATEGORY_DEFAULT);
LocalBroadcastManager.getInstance(this.getApplicationContext()).registerReceiver(downloadBroadcastReceiver, intentFilter);
}
+ AppQueryReceiver.register(this);
+ new Thread(() -> {
+ ApplicationInfoManager applicationInfoManager = new ApplicationInfoManager(BitmaskApp.this);
+ applicationInfoManager.updateExcludedApps();
+ }).start();
+
}
@Override
diff --git a/app/src/main/java/se/leap/bitmaskclient/base/fragments/ExcludeAppsFragment.java b/app/src/main/java/se/leap/bitmaskclient/base/fragments/ExcludeAppsFragment.java
index 07608211..57217a4f 100644
--- a/app/src/main/java/se/leap/bitmaskclient/base/fragments/ExcludeAppsFragment.java
+++ b/app/src/main/java/se/leap/bitmaskclient/base/fragments/ExcludeAppsFragment.java
@@ -7,11 +7,9 @@ package se.leap.bitmaskclient.base.fragments;
import static se.leap.bitmaskclient.R.string.exclude_apps_fragment_title;
-import android.Manifest;
import android.app.Activity;
import android.content.Context;
import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
import android.os.Bundle;
import android.text.TextUtils;
import android.util.Log;
@@ -31,14 +29,13 @@ import android.widget.SearchView;
import androidx.appcompat.widget.AppCompatTextView;
import androidx.fragment.app.Fragment;
-import java.util.Collections;
-import java.util.List;
import java.util.Locale;
import java.util.Set;
import java.util.Vector;
import de.blinkt.openvpn.VpnProfile;
import se.leap.bitmaskclient.R;
+import se.leap.bitmaskclient.base.utils.ApplicationInfoManager;
import se.leap.bitmaskclient.base.utils.PreferenceHelper;
import se.leap.bitmaskclient.base.utils.ViewHelper;
import se.leap.bitmaskclient.base.views.SimpleCheckBox;
@@ -113,8 +110,8 @@ public class ExcludeAppsFragment extends Fragment implements AdapterView.OnItemC
class PackageAdapter extends BaseAdapter implements Filterable {
private Vector<ApplicationInfo> mPackages;
private final LayoutInflater mInflater;
- private final PackageManager mPm;
- private ItemFilter mFilter = new ItemFilter();
+ private final ApplicationInfoManager mAppInfoManager;
+ private final ItemFilter mFilter = new ItemFilter();
private Vector<ApplicationInfo> mFilteredData;
@@ -132,7 +129,7 @@ public class ExcludeAppsFragment extends Fragment implements AdapterView.OnItemC
for (int i = 0; i < count; i++) {
ApplicationInfo pInfo = mPackages.get(i);
- CharSequence appName = pInfo.loadLabel(mPm);
+ CharSequence appName = mAppInfoManager.loadLabel(pInfo);
if (TextUtils.isEmpty(appName))
appName = pInfo.packageName;
@@ -156,12 +153,10 @@ public class ExcludeAppsFragment extends Fragment implements AdapterView.OnItemC
mFilteredData = (Vector<ApplicationInfo>) results.values;
notifyDataSetChanged();
}
-
}
-
PackageAdapter(Context c, VpnProfile vp) {
- mPm = c.getPackageManager();
+ mAppInfoManager = new ApplicationInfoManager(c);
mProfile = vp;
mInflater = LayoutInflater.from(c);
@@ -170,40 +165,10 @@ public class ExcludeAppsFragment extends Fragment implements AdapterView.OnItemC
}
private void populateList(Activity c) {
- List<ApplicationInfo> installedPackages = mPm.getInstalledApplications(PackageManager.GET_META_DATA);
-
- // Remove apps not using Internet
-
- int androidSystemUid = 0;
- ApplicationInfo system = null;
- Vector<ApplicationInfo> apps = new Vector<ApplicationInfo>();
-
- try {
- system = mPm.getApplicationInfo("android", PackageManager.GET_META_DATA);
- androidSystemUid = system.uid;
- apps.add(system);
- } catch (PackageManager.NameNotFoundException e) {
- }
-
-
- for (ApplicationInfo app : installedPackages) {
-
- if (mPm.checkPermission(Manifest.permission.INTERNET, app.packageName) == PackageManager.PERMISSION_GRANTED &&
- app.uid != androidSystemUid) {
-
- apps.add(app);
- }
- }
-
- Collections.sort(apps, new ApplicationInfo.DisplayNameComparator(mPm));
+ Vector<ApplicationInfo> apps = mAppInfoManager.getApplicationInfos();
mPackages = apps;
mFilteredData = apps;
- c.runOnUiThread(new Runnable() {
- @Override
- public void run() {
- notifyDataSetChanged();
- }
- });
+ c.runOnUiThread(this::notifyDataSetChanged);
}
@Override
@@ -229,12 +194,12 @@ public class ExcludeAppsFragment extends Fragment implements AdapterView.OnItemC
final ApplicationInfo mInfo = mFilteredData.get(position);
- CharSequence appName = mInfo.loadLabel(mPm);
+ CharSequence appName = mAppInfoManager.loadLabel(mInfo);
if (TextUtils.isEmpty(appName))
appName = mInfo.packageName;
viewHolder.appName.setText(appName);
- viewHolder.appIcon.setImageDrawable(mInfo.loadIcon(mPm));
+ viewHolder.appIcon.setImageDrawable(mAppInfoManager.loadDrawable(mInfo));
viewHolder.checkBox.setTag(mInfo.packageName);
viewHolder.checkBox.setOnCheckedChangeListener(ExcludeAppsFragment.this);
viewHolder.checkBox.setChecked(apps.contains(mInfo.packageName));
@@ -272,32 +237,34 @@ public class ExcludeAppsFragment extends Fragment implements AdapterView.OnItemC
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.allowed_apps, menu);
SearchView searchView = (SearchView) menu.findItem( R.id.app_search_widget ).getActionView();
- searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
- @Override
- public boolean onQueryTextSubmit(String query) {
- mListView.setFilterText(query);
- mListView.setTextFilterEnabled(true);
- return true;
- }
-
- @Override
- public boolean onQueryTextChange(String newText) {
- mListView.setFilterText(newText);
- if (TextUtils.isEmpty(newText))
- mListView.setTextFilterEnabled(false);
- else
+ if (searchView != null) {
+ searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
+ @Override
+ public boolean onQueryTextSubmit(String query) {
+ mListView.setFilterText(query);
mListView.setTextFilterEnabled(true);
+ return true;
+ }
- return true;
- }
- });
- searchView.setOnCloseListener(() -> {
- mListView.clearTextFilter();
- mListAdapter.getFilter().filter("");
- return false;
- });
-
- tintSearchViewChild(searchView);
+ @Override
+ public boolean onQueryTextChange(String newText) {
+ mListView.setFilterText(newText);
+ if (TextUtils.isEmpty(newText))
+ mListView.setTextFilterEnabled(false);
+ else
+ mListView.setTextFilterEnabled(true);
+
+ return true;
+ }
+ });
+ searchView.setOnCloseListener(() -> {
+ mListView.clearTextFilter();
+ mListAdapter.getFilter().filter("");
+ return false;
+ });
+
+ tintSearchViewChild(searchView);
+ }
super.onCreateOptionsMenu(menu, inflater);
}
@@ -332,4 +299,10 @@ public class ExcludeAppsFragment extends Fragment implements AdapterView.OnItemC
return v;
}
+ @Override
+ public void onDestroyView() {
+ mListView.setOnItemClickListener(null);
+ mListAdapter = null;
+ super.onDestroyView();
+ }
}
diff --git a/app/src/main/java/se/leap/bitmaskclient/base/fragments/SettingsFragment.java b/app/src/main/java/se/leap/bitmaskclient/base/fragments/SettingsFragment.java
index d4d72812..c8d60b90 100644
--- a/app/src/main/java/se/leap/bitmaskclient/base/fragments/SettingsFragment.java
+++ b/app/src/main/java/se/leap/bitmaskclient/base/fragments/SettingsFragment.java
@@ -4,6 +4,7 @@ import static android.view.View.GONE;
import static android.view.View.VISIBLE;
import static se.leap.bitmaskclient.R.string.advanced_settings;
import static se.leap.bitmaskclient.base.fragments.CensorshipCircumventionFragment.TUNNELING_AUTOMATICALLY;
+import static se.leap.bitmaskclient.base.models.Constants.EXCLUDED_APPS;
import static se.leap.bitmaskclient.base.models.Constants.GATEWAY_PINNING;
import static se.leap.bitmaskclient.base.models.Constants.PREFER_UDP;
import static se.leap.bitmaskclient.base.models.Constants.USE_BRIDGES;
@@ -409,8 +410,9 @@ public class SettingsFragment extends Fragment implements SharedPreferences.OnSh
initFirewallEntry(getView());
} else if (key.equals(GATEWAY_PINNING)) {
initGatewayPinningEntry(rootView);
+ } else if (key.equals(EXCLUDED_APPS)) {
+ initExcludeAppsEntry(rootView);
}
-
if (key.equals(USE_OBFUSCATION_PINNING) || key.equals(USE_BRIDGES)) {
initObfuscationPinningEntry(rootView);
}
diff --git a/app/src/main/java/se/leap/bitmaskclient/base/utils/ApplicationInfoManager.java b/app/src/main/java/se/leap/bitmaskclient/base/utils/ApplicationInfoManager.java
new file mode 100644
index 00000000..1cf688fa
--- /dev/null
+++ b/app/src/main/java/se/leap/bitmaskclient/base/utils/ApplicationInfoManager.java
@@ -0,0 +1,85 @@
+package se.leap.bitmaskclient.base.utils;
+
+import android.Manifest;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.graphics.drawable.Drawable;
+import android.util.Log;
+
+import androidx.annotation.WorkerThread;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.Vector;
+import java.util.function.Predicate;
+
+public class ApplicationInfoManager {
+
+ public final static String TAG = ApplicationInfoManager.class.getSimpleName();
+ final PackageManager packageManager;
+ public ApplicationInfoManager(Context context) {
+ packageManager = context.getPackageManager();
+ }
+
+ @WorkerThread
+ public Vector<ApplicationInfo> getApplicationInfos() {
+ List<ApplicationInfo> installedPackages = packageManager.getInstalledApplications(PackageManager.GET_META_DATA);
+
+ // Remove apps not using Internet
+
+ int androidSystemUid = 0;
+ ApplicationInfo system = null;
+ Vector<ApplicationInfo> apps = new Vector<>();
+
+ try {
+ system = packageManager.getApplicationInfo("android", PackageManager.GET_META_DATA);
+ androidSystemUid = system.uid;
+ apps.add(system);
+ } catch (PackageManager.NameNotFoundException e) {
+ }
+
+
+ for (ApplicationInfo app : installedPackages) {
+
+ if (packageManager.checkPermission(Manifest.permission.INTERNET, app.packageName) == PackageManager.PERMISSION_GRANTED &&
+ app.uid != androidSystemUid) {
+
+ apps.add(app);
+ }
+ }
+
+ Collections.sort(apps, new ApplicationInfo.DisplayNameComparator(packageManager));
+ return apps;
+ }
+
+ public CharSequence loadLabel(ApplicationInfo appInfo) {
+ return appInfo.loadLabel(packageManager);
+ }
+
+ public Drawable loadDrawable(ApplicationInfo appInfo) {
+ return appInfo.loadIcon(packageManager);
+ }
+
+ public void onApplicationRemoved(String appPackage) {
+ Set<String> excludedApps = PreferenceHelper.getExcludedApps();
+ Log.d(TAG, "remove appPackage: " + appPackage);
+ excludedApps.remove(appPackage);
+ PreferenceHelper.setExcludedApps(excludedApps);
+ }
+
+ public void updateExcludedApps() {
+ Set<String> excludedApps = PreferenceHelper.getExcludedApps();
+ Log.d(TAG, "update excluded apps before: " + excludedApps.toString());
+ List<ApplicationInfo> appInfos = getApplicationInfos();
+ excludedApps.removeIf(s ->
+ appInfos.stream().noneMatch(
+ applicationInfo -> s.equals(applicationInfo.packageName)
+ )
+ );
+ Log.d(TAG, "update excluded apps after: " + excludedApps.toString());
+ PreferenceHelper.setExcludedApps(excludedApps);
+ }
+}