summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorcyberta <cyberta@riseup.net>2026-04-13 07:42:42 +0200
committercyberta <cyberta@riseup.net>2026-04-15 23:56:05 +0000
commitd33310afc0dfa85a95419babc2f0f6c852074b70 (patch)
treeaceaba2a526a997df2828a967aa810ee6873cf11
parentba4e1500eb9813c2e88eea1d23caa432a21c019c (diff)
replace ListView with RecyclerView
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/base/fragments/ExcludeAppsFragment.java157
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/base/utils/ApplicationInfoManager.java7
-rw-r--r--app/src/main/res/layout/allowed_vpn_apps.xml4
3 files changed, 70 insertions, 98 deletions
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 57217a4f..06349cf2 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
@@ -5,6 +5,7 @@
package se.leap.bitmaskclient.base.fragments;
+import static android.view.View.VISIBLE;
import static se.leap.bitmaskclient.R.string.exclude_apps_fragment_title;
import android.app.Activity;
@@ -18,16 +19,20 @@ import android.view.Menu;
import android.view.MenuInflater;
import android.view.View;
import android.view.ViewGroup;
-import android.widget.AdapterView;
-import android.widget.BaseAdapter;
import android.widget.Filter;
import android.widget.Filterable;
import android.widget.ImageView;
-import android.widget.ListView;
+import android.widget.LinearLayout;
import android.widget.SearchView;
+import android.widget.TextView;
+import androidx.annotation.NonNull;
+import androidx.annotation.WorkerThread;
import androidx.appcompat.widget.AppCompatTextView;
+import androidx.core.content.ContextCompat;
import androidx.fragment.app.Fragment;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
import java.util.Locale;
import java.util.Set;
@@ -43,54 +48,41 @@ import se.leap.bitmaskclient.base.views.SimpleCheckBox;
/**
* Created by arne on 16.11.14.
*/
-public class ExcludeAppsFragment extends Fragment implements AdapterView.OnItemClickListener, SimpleCheckBox.OnCheckedChangeListener, View.OnClickListener {
- private ListView mListView;
+public class ExcludeAppsFragment extends Fragment implements SimpleCheckBox.OnCheckedChangeListener {
+ private static final String TAG = ExcludeAppsFragment.class.getSimpleName();
+ private RecyclerView mRecyclerView;
+ private LinearLayout emptyView;
private VpnProfile mProfile;
private PackageAdapter mListAdapter;
private Set<String> apps;
- @Override
- public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
- AppViewHolder avh = (AppViewHolder) view.getTag();
- avh.checkBox.toggle();
- }
-
- @Override
- public void onClick(View v) {
-
- }
-
- static class AppViewHolder {
+ static class AppViewHolder extends RecyclerView.ViewHolder {
public ApplicationInfo mInfo;
- public View rootView;
public AppCompatTextView appName;
public ImageView appIcon;
- //public AppCompatTextView appSize;
- //public AppCompatTextView disabled;
public SimpleCheckBox checkBox;
- static public AppViewHolder createOrRecycle(LayoutInflater inflater, View convertView, ViewGroup parent) {
- if (convertView == null) {
- convertView = inflater.inflate(R.layout.allowed_application_layout, parent, false);
-
- // Creates a ViewHolder and store references to the two children views
- // we want to bind data to.
- AppViewHolder holder = new AppViewHolder();
- holder.rootView = convertView;
- holder.appName = convertView.findViewById(R.id.app_name);
- holder.appIcon = convertView.findViewById(R.id.app_icon);
- holder.checkBox = convertView.findViewById(R.id.app_selected);
- convertView.setTag(holder);
-
- return holder;
- } else {
- // Get the ViewHolder back to get fast access to the TextView
- // and the ImageView.
- return (AppViewHolder) convertView.getTag();
- }
+ public AppViewHolder(@NonNull View itemView) {
+ super(itemView);
+ appName = itemView.findViewById(R.id.app_name);
+ appIcon = itemView.findViewById(R.id.app_icon);
+ checkBox = itemView.findViewById(R.id.app_selected);
+ itemView.setOnClickListener(v -> checkBox.toggle());
}
+ public void bind(ApplicationInfo info, ApplicationInfoManager appInfoManager, Set<String> apps, SimpleCheckBox.OnCheckedChangeListener listener) {
+ mInfo = info;
+ CharSequence appLabelText = appInfoManager.loadLabel(info);
+ if (TextUtils.isEmpty(appLabelText)) {
+ appLabelText = info.packageName;
+ }
+ appName.setText(appLabelText);
+ appIcon.setImageDrawable(appInfoManager.loadDrawable(info));
+ checkBox.setTag(info.packageName);
+ checkBox.setOnCheckedChangeListener(listener);
+ checkBox.setChecked(apps.contains(info.packageName));
+ }
}
@Override
@@ -107,7 +99,7 @@ public class ExcludeAppsFragment extends Fragment implements AdapterView.OnItemC
}
}
- class PackageAdapter extends BaseAdapter implements Filterable {
+ class PackageAdapter extends RecyclerView.Adapter<AppViewHolder> implements Filterable {
private Vector<ApplicationInfo> mPackages;
private final LayoutInflater mInflater;
private final ApplicationInfoManager mAppInfoManager;
@@ -123,7 +115,6 @@ public class ExcludeAppsFragment extends Fragment implements AdapterView.OnItemC
FilterResults results = new FilterResults();
-
int count = mPackages.size();
final Vector<ApplicationInfo> nlist = new Vector<>(count);
@@ -134,9 +125,9 @@ public class ExcludeAppsFragment extends Fragment implements AdapterView.OnItemC
if (TextUtils.isEmpty(appName))
appName = pInfo.packageName;
- if (appName instanceof String) {
+ if (appName instanceof String) {
if (((String) appName).toLowerCase(Locale.getDefault()).contains(filterString))
- nlist.add(pInfo);
+ nlist.add(pInfo);
} else {
if (appName.toString().toLowerCase(Locale.getDefault()).contains(filterString))
nlist.add(pInfo);
@@ -163,48 +154,35 @@ public class ExcludeAppsFragment extends Fragment implements AdapterView.OnItemC
mPackages = new Vector<>();
mFilteredData = mPackages;
}
-
+ @WorkerThread
private void populateList(Activity c) {
- Vector<ApplicationInfo> apps = mAppInfoManager.getApplicationInfos();
- mPackages = apps;
- mFilteredData = apps;
- c.runOnUiThread(this::notifyDataSetChanged);
+ Vector<ApplicationInfo> appsList = mAppInfoManager.getApplicationInfos();
+ mPackages = appsList;
+ mFilteredData = appsList;
+ c.runOnUiThread(() -> {
+ if (!appsList.isEmpty()) {
+ mRecyclerView.setVisibility(VISIBLE);
+ }
+ this.notifyDataSetChanged();
+ });
}
@Override
- public int getCount() {
+ public int getItemCount() {
return mFilteredData.size();
}
+ @NonNull
@Override
- public Object getItem(int position) {
- return mFilteredData.get(position);
+ public AppViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
+ View itemView = mInflater.inflate(R.layout.allowed_application_layout, parent, false);
+ return new AppViewHolder(itemView);
}
@Override
- public long getItemId(int position) {
- return mFilteredData.get(position).packageName.hashCode();
- }
-
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- AppViewHolder viewHolder = AppViewHolder.createOrRecycle(mInflater, convertView, parent);
-
- viewHolder.mInfo = mFilteredData.get(position);
- final ApplicationInfo mInfo = mFilteredData.get(position);
-
-
- CharSequence appName = mAppInfoManager.loadLabel(mInfo);
-
- if (TextUtils.isEmpty(appName))
- appName = mInfo.packageName;
- viewHolder.appName.setText(appName);
- viewHolder.appIcon.setImageDrawable(mAppInfoManager.loadDrawable(mInfo));
- viewHolder.checkBox.setTag(mInfo.packageName);
- viewHolder.checkBox.setOnCheckedChangeListener(ExcludeAppsFragment.this);
- viewHolder.checkBox.setChecked(apps.contains(mInfo.packageName));
-
- return viewHolder.rootView;
+ public void onBindViewHolder(@NonNull AppViewHolder holder, int position) {
+ ApplicationInfo mInfo = mFilteredData.get(position);
+ holder.bind(mInfo, mAppInfoManager, apps, ExcludeAppsFragment.this);
}
@Override
@@ -236,29 +214,26 @@ public class ExcludeAppsFragment extends Fragment implements AdapterView.OnItemC
@Override
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 searchView = (SearchView) menu.findItem(R.id.app_search_widget).getActionView();
if (searchView != null) {
searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
@Override
public boolean onQueryTextSubmit(String query) {
- mListView.setFilterText(query);
- mListView.setTextFilterEnabled(true);
+ mListAdapter.getFilter().filter(query);
return true;
}
@Override
public boolean onQueryTextChange(String newText) {
- mListView.setFilterText(newText);
- if (TextUtils.isEmpty(newText))
- mListView.setTextFilterEnabled(false);
- else
- mListView.setTextFilterEnabled(true);
-
+ if (TextUtils.isEmpty(newText)) {
+ mListAdapter.getFilter().filter("");
+ } else {
+ mListAdapter.getFilter().filter(newText);
+ }
return true;
}
});
searchView.setOnCloseListener(() -> {
- mListView.clearTextFilter();
mListAdapter.getFilter().filter("");
return false;
});
@@ -285,13 +260,14 @@ public class ExcludeAppsFragment extends Fragment implements AdapterView.OnItemC
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.allowed_vpn_apps, container, false);
- mListView = v.findViewById(android.R.id.list);
+ mRecyclerView = v.findViewById(R.id.list);
+ mRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
+ emptyView = v.findViewById(R.id.loading_container);
+ emptyView.setVisibility(VISIBLE);
mListAdapter = new PackageAdapter(getActivity(), mProfile);
- mListView.setAdapter(mListAdapter);
- mListView.setOnItemClickListener(this);
+ mRecyclerView.setAdapter(mListAdapter);
- mListView.setEmptyView(v.findViewById(R.id.loading_container));
ViewHelper.setActionBarSubtitle(this, exclude_apps_fragment_title);
new Thread(() -> mListAdapter.populateList(getActivity())).start();
@@ -301,8 +277,9 @@ public class ExcludeAppsFragment extends Fragment implements AdapterView.OnItemC
@Override
public void onDestroyView() {
- mListView.setOnItemClickListener(null);
+ Log.d(TAG, "onDestroyView");
+ mRecyclerView.setAdapter(null);
mListAdapter = null;
super.onDestroyView();
}
-}
+} \ No newline at end of file
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
index 1cf688fa..4ddbc92f 100644
--- a/app/src/main/java/se/leap/bitmaskclient/base/utils/ApplicationInfoManager.java
+++ b/app/src/main/java/se/leap/bitmaskclient/base/utils/ApplicationInfoManager.java
@@ -5,16 +5,13 @@ 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 {
@@ -65,21 +62,19 @@ public class ApplicationInfoManager {
public void onApplicationRemoved(String appPackage) {
Set<String> excludedApps = PreferenceHelper.getExcludedApps();
- Log.d(TAG, "remove appPackage: " + appPackage);
excludedApps.remove(appPackage);
PreferenceHelper.setExcludedApps(excludedApps);
}
+ @WorkerThread
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);
}
}
diff --git a/app/src/main/res/layout/allowed_vpn_apps.xml b/app/src/main/res/layout/allowed_vpn_apps.xml
index 1930adf2..c9e74cf3 100644
--- a/app/src/main/res/layout/allowed_vpn_apps.xml
+++ b/app/src/main/res/layout/allowed_vpn_apps.xml
@@ -30,9 +30,9 @@
android:layout_height="1dp"
android:background="?android:attr/listDivider"/>
- <ListView
+ <androidx.recyclerview.widget.RecyclerView
android:visibility="gone"
- android:id="@android:id/list"
+ android:id="@+id/list"
android:drawSelectorOnTop="false"
android:layout_width="match_parent"
android:layout_height="match_parent"