mirror of
https://github.com/emanuele-f/PCAPdroid.git
synced 2026-07-03 21:21:12 +08:00
Apps in VPN exceptions are now searchable
This commit is contained in:
parent
86d29732d4
commit
83c9cff3a8
@ -47,7 +47,9 @@ public class AppsTogglesAdapter extends RecyclerView.Adapter<AppsTogglesAdapter.
|
||||
private final LayoutInflater mLayoutInflater;
|
||||
private final Set<String> mCheckedItems;
|
||||
private AppToggleListener mListener;
|
||||
private String mFilter = "";
|
||||
private List<AppDescriptor> mApps = new ArrayList<>();
|
||||
private final List<AppDescriptor> mFilteredApps = new ArrayList<>();
|
||||
private @Nullable RecyclerView mRecyclerView;
|
||||
|
||||
public AppsTogglesAdapter(Context context, Set<String> checkedItems) {
|
||||
@ -125,16 +127,23 @@ public class AppsTogglesAdapter extends RecyclerView.Adapter<AppsTogglesAdapter.
|
||||
holder.icon.setImageDrawable(app.getIcon());
|
||||
}
|
||||
|
||||
private List<AppDescriptor> getApps() {
|
||||
if(mFilter.isEmpty())
|
||||
return mApps;
|
||||
else
|
||||
return mFilteredApps;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return mApps.size();
|
||||
return getApps().size();
|
||||
}
|
||||
|
||||
public AppDescriptor getItem(int pos) {
|
||||
if((pos < 0) || (pos > mApps.size()))
|
||||
if((pos < 0) || (pos > getItemCount()))
|
||||
return null;
|
||||
|
||||
return mApps.get(pos);
|
||||
return getApps().get(pos);
|
||||
}
|
||||
|
||||
private void handleToggle(int old_pos, boolean checked) {
|
||||
@ -152,10 +161,12 @@ public class AppsTogglesAdapter extends RecyclerView.Adapter<AppsTogglesAdapter.
|
||||
if(mListener != null)
|
||||
mListener.onAppToggled(app, checked);
|
||||
|
||||
List<AppDescriptor> apps = getApps();
|
||||
|
||||
// determine the new item position
|
||||
int new_pos = old_pos;
|
||||
for(int i=0; i<mApps.size(); i++) {
|
||||
AppDescriptor other = mApps.get(i);
|
||||
for(int i=0; i<apps.size(); i++) {
|
||||
AppDescriptor other = apps.get(i);
|
||||
|
||||
if((i != old_pos) && compareCheckedFirst(app, other) <= 0) {
|
||||
new_pos = i;
|
||||
@ -170,8 +181,8 @@ public class AppsTogglesAdapter extends RecyclerView.Adapter<AppsTogglesAdapter.
|
||||
notifyItemChanged(old_pos);
|
||||
|
||||
if(new_pos != old_pos) {
|
||||
mApps.remove(old_pos);
|
||||
mApps.add(new_pos, app);
|
||||
apps.remove(old_pos);
|
||||
apps.add(new_pos, app);
|
||||
notifyItemMoved(old_pos, new_pos);
|
||||
|
||||
if(mRecyclerView != null) {
|
||||
@ -196,11 +207,28 @@ public class AppsTogglesAdapter extends RecyclerView.Adapter<AppsTogglesAdapter.
|
||||
}
|
||||
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
private void refreshedFiteredApps() {
|
||||
mFilteredApps.clear();
|
||||
|
||||
if(!mFilter.isEmpty()) {
|
||||
for(AppDescriptor app: mApps) {
|
||||
if(app.matches(mFilter, false))
|
||||
mFilteredApps.add(app);
|
||||
}
|
||||
}
|
||||
|
||||
Collections.sort(getApps(), this::compareCheckedFirst);
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
public void setApps(List<AppDescriptor> apps) {
|
||||
mApps = apps;
|
||||
Collections.sort(mApps, this::compareCheckedFirst);
|
||||
refreshedFiteredApps();
|
||||
}
|
||||
|
||||
notifyDataSetChanged();
|
||||
public void setFilter(String text) {
|
||||
mFilter = text;
|
||||
refreshedFiteredApps();
|
||||
}
|
||||
|
||||
public void setAppToggleListener(final AppToggleListener listener) {
|
||||
|
||||
@ -19,6 +19,7 @@
|
||||
|
||||
package com.emanuelef.remote_capture.adapters;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.util.Log;
|
||||
@ -370,6 +371,7 @@ public class ConnectionsAdapter extends RecyclerView.Adapter<ConnectionsAdapter.
|
||||
fixFilteredPositions(first_removed_pos);
|
||||
}
|
||||
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
public void refreshFilteredConnections() {
|
||||
final ConnectionsRegister reg = CaptureService.getConnsRegister();
|
||||
if(reg == null)
|
||||
|
||||
@ -21,6 +21,8 @@ package com.emanuelef.remote_capture.fragments;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
@ -28,6 +30,7 @@ import android.widget.TextView;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.appcompat.widget.SearchView;
|
||||
import androidx.fragment.app.Fragment;
|
||||
|
||||
import com.emanuelef.remote_capture.AppsLoader;
|
||||
@ -42,8 +45,11 @@ import java.util.Set;
|
||||
|
||||
import kotlin.NotImplementedError;
|
||||
|
||||
public abstract class AppsToggles extends Fragment implements AppsLoadListener, AppsTogglesAdapter.AppToggleListener {
|
||||
public abstract class AppsToggles extends Fragment implements AppsLoadListener,
|
||||
AppsTogglesAdapter.AppToggleListener, SearchView.OnQueryTextListener {
|
||||
private AppsTogglesAdapter mAdapter;
|
||||
private SearchView mSearchView;
|
||||
private TextView mEmptyText;
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater,
|
||||
@ -61,18 +67,35 @@ public abstract class AppsToggles extends Fragment implements AppsLoadListener,
|
||||
recyclerView.setAdapter(mAdapter);
|
||||
mAdapter.setAppToggleListener(this);
|
||||
|
||||
TextView emptyAppsView = view.findViewById(R.id.no_apps);
|
||||
emptyAppsView.setText(R.string.loading_apps);
|
||||
recyclerView.setEmptyView(emptyAppsView);
|
||||
mEmptyText = view.findViewById(R.id.no_apps);
|
||||
mEmptyText.setText(R.string.loading_apps);
|
||||
recyclerView.setEmptyView(mEmptyText);
|
||||
|
||||
(new AppsLoader((AppCompatActivity) requireActivity()))
|
||||
.setAppsLoadListener(this)
|
||||
.loadAllApps();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreateOptionsMenu(@NonNull Menu menu, MenuInflater menuInflater) {
|
||||
menuInflater.inflate(R.menu.search_menu, menu);
|
||||
mSearchView = (SearchView) menu.findItem(R.id.search).getActionView();
|
||||
mSearchView.setOnQueryTextListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onQueryTextSubmit(String query) { return true; }
|
||||
|
||||
@Override
|
||||
public boolean onQueryTextChange(String newText) {
|
||||
mAdapter.setFilter(newText);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAppsInfoLoaded(List<AppDescriptor> apps) {
|
||||
mAdapter.setApps(apps);
|
||||
mEmptyText.setText(R.string.no_matches_found);
|
||||
}
|
||||
|
||||
// Must be implemented in sub-classes
|
||||
|
||||
@ -107,10 +107,7 @@ public class ConnectionsFragment extends Fragment implements ConnectionsListener
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
|
||||
if((CaptureService.getConnsRegister() != null) || CaptureService.isServiceActive())
|
||||
mEmptyText.setText(R.string.no_connections);
|
||||
else
|
||||
mEmptyText.setText(R.string.capture_not_running_status);
|
||||
refreshEmptyText();
|
||||
|
||||
registerConnsListener();
|
||||
mRecyclerView.setEmptyView(mEmptyText); // after registerConnsListener, when the adapter is populated
|
||||
@ -146,6 +143,13 @@ public class ConnectionsFragment extends Fragment implements ConnectionsListener
|
||||
return inflater.inflate(R.layout.connections, container, false);
|
||||
}
|
||||
|
||||
private void refreshEmptyText() {
|
||||
if((CaptureService.getConnsRegister() != null) || CaptureService.isServiceActive())
|
||||
mEmptyText.setText(mAdapter.hasFilter() ? R.string.no_matches_found : R.string.no_connections);
|
||||
else
|
||||
mEmptyText.setText(R.string.capture_not_running_status);
|
||||
}
|
||||
|
||||
private void registerConnsListener() {
|
||||
if (!listenerSet) {
|
||||
ConnectionsRegister reg = CaptureService.getConnsRegister();
|
||||
@ -810,6 +814,7 @@ public class ConnectionsFragment extends Fragment implements ConnectionsListener
|
||||
public boolean onQueryTextChange(String newText) {
|
||||
mAdapter.setSearch(newText);
|
||||
recheckScroll();
|
||||
refreshEmptyText();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@ -131,4 +131,10 @@ public class AppDescriptor implements Comparable<AppDescriptor>, Serializable {
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
public boolean matches(String filter, boolean exactPackage) {
|
||||
return getName().toLowerCase().contains(filter) ||
|
||||
(exactPackage && getPackageName().equals(filter)) ||
|
||||
(!exactPackage && getPackageName().contains(filter));
|
||||
}
|
||||
}
|
||||
|
||||
@ -235,8 +235,7 @@ public class ConnectionDescriptor {
|
||||
Integer.toString(uid).equals(filter) ||
|
||||
Integer.toString(dst_port).contains(filter) ||
|
||||
Integer.toString(src_port).equals(filter) ||
|
||||
((app != null) && (app.getName().toLowerCase().contains(filter) ||
|
||||
app.getPackageName().equals(filter)))
|
||||
((app != null) && (app.matches(filter, true)))
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
15
app/src/main/res/menu/search_menu.xml
Normal file
15
app/src/main/res/menu/search_menu.xml
Normal file
@ -0,0 +1,15 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:tools="http://schemas.android.com/tools"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<!-- NOTE: "always|collapseActionView" must be set to prevent the search button from
|
||||
disappearing and to avoid hiding the other elements. -->
|
||||
<item
|
||||
android:id="@+id/search"
|
||||
android:title="@string/search"
|
||||
android:icon="@drawable/ic_search"
|
||||
app:actionViewClass="androidx.appcompat.widget.SearchView"
|
||||
app:showAsAction="always|collapseActionView"
|
||||
tools:ignore="AlwaysShowAction" />
|
||||
</menu>
|
||||
@ -374,4 +374,5 @@
|
||||
<string name="notifications_notice">The app uses notifications to send alerts in case of anomalous events. Grant it the permission to send notifications in the next screen</string>
|
||||
<string name="vpn_exceptions">VPN Exceptions</string>
|
||||
<string name="vpn_exceptions_summary">Exclude some apps from the VPN connection. Their traffic will not be monitored</string>
|
||||
<string name="no_matches_found">No matches</string>
|
||||
</resources>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user