diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 9f2b78cb..107763a1 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -62,6 +62,10 @@
android:name=".activities.LogviewActivity"
android:launchMode="singleTop"
android:parentActivityName=".activities.MainActivity" />
+
mAppsStats;
private final ArrayList mListeners;
- private final SharedPreferences mPrefs;
- public final ConnectionsMatcher mWhitelist;
- public boolean mWhitelistEnabled;
- public ConnectionsRegister(int _size, Context context, SharedPreferences prefs) {
+ public ConnectionsRegister(int _size) {
mTail = 0;
mNumItems = 0;
mUntrackedItems = 0;
@@ -61,15 +54,6 @@ public class ConnectionsRegister {
mItemsRing = new ConnectionDescriptor[mSize];
mListeners = new ArrayList<>();
mAppsStats = new HashMap<>(); // uid -> AppStats
- mWhitelistEnabled = true;
- mPrefs = prefs;
- mWhitelist = new ConnectionsMatcher(context);
-
- // Try to restore the whitelist
- String serialized = prefs.getString(Prefs.PREF_WHITELIST, "");
-
- if(!serialized.isEmpty())
- mWhitelist.fromJson(serialized);
}
private int firstPos() {
@@ -220,10 +204,6 @@ public class ConnectionsRegister {
return mUntrackedItems;
}
- public boolean hasWhitelistFilter() {
- return(mWhitelistEnabled && !mWhitelist.isEmpty());
- }
-
public @Nullable ConnectionDescriptor getConn(int i) {
if(i >= mNumItems)
return null;
@@ -269,10 +249,4 @@ public class ConnectionsRegister {
return rv;
}
-
- public void saveWhitelist() {
- mPrefs.edit()
- .putString(Prefs.PREF_WHITELIST, mWhitelist.toJson())
- .apply();
- }
}
diff --git a/app/src/main/java/com/emanuelef/remote_capture/activities/MainActivity.java b/app/src/main/java/com/emanuelef/remote_capture/activities/MainActivity.java
index bba650ba..c2bab15b 100644
--- a/app/src/main/java/com/emanuelef/remote_capture/activities/MainActivity.java
+++ b/app/src/main/java/com/emanuelef/remote_capture/activities/MainActivity.java
@@ -347,6 +347,9 @@ public class MainActivity extends BaseActivity implements NavigationView.OnNavig
startActivity(intent);
} else
Utils.showToast(this, R.string.capture_not_started);
+ } else if(id == R.id.edit_whitelist) {
+ Intent intent = new Intent(MainActivity.this, WhitelistActivity.class);
+ startActivity(intent);
} else if(id == R.id.open_root_log) {
Intent intent = new Intent(MainActivity.this, LogviewActivity.class);
startActivity(intent);
diff --git a/app/src/main/java/com/emanuelef/remote_capture/activities/WhitelistActivity.java b/app/src/main/java/com/emanuelef/remote_capture/activities/WhitelistActivity.java
new file mode 100644
index 00000000..674f0ac7
--- /dev/null
+++ b/app/src/main/java/com/emanuelef/remote_capture/activities/WhitelistActivity.java
@@ -0,0 +1,42 @@
+/*
+ * This file is part of PCAPdroid.
+ *
+ * PCAPdroid is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * PCAPdroid is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with PCAPdroid. If not, see .
+ *
+ * Copyright 2020-21 - Emanuele Faranda
+ */
+
+package com.emanuelef.remote_capture.activities;
+
+import android.os.Bundle;
+
+import com.emanuelef.remote_capture.R;
+import com.emanuelef.remote_capture.fragments.AppsFragment;
+import com.emanuelef.remote_capture.fragments.WhitelistFragment;
+
+public class WhitelistActivity extends BaseActivity {
+ private static final String TAG = "WhitelistActivity";
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ setTitle(R.string.whitelist);
+ setContentView(R.layout.whitelist_activity);
+
+ getSupportFragmentManager().beginTransaction()
+ .replace(R.id.whitelist_fragment, new WhitelistFragment())
+ .commit();
+ }
+}
diff --git a/app/src/main/java/com/emanuelef/remote_capture/adapters/ConnectionsAdapter.java b/app/src/main/java/com/emanuelef/remote_capture/adapters/ConnectionsAdapter.java
index 398da0a5..464b7992 100644
--- a/app/src/main/java/com/emanuelef/remote_capture/adapters/ConnectionsAdapter.java
+++ b/app/src/main/java/com/emanuelef/remote_capture/adapters/ConnectionsAdapter.java
@@ -40,6 +40,7 @@ import com.emanuelef.remote_capture.model.ConnectionDescriptor;
import com.emanuelef.remote_capture.ConnectionsRegister;
import com.emanuelef.remote_capture.R;
import com.emanuelef.remote_capture.Utils;
+import com.emanuelef.remote_capture.model.Whitelist;
import java.util.ArrayList;
import java.util.HashMap;
@@ -57,8 +58,10 @@ public class ConnectionsAdapter extends RecyclerView.Adapter mIdToFilteredPos;
private ArrayList mFilteredConn;
+ public final Whitelist mWhitelist;
public static class ViewHolder extends RecyclerView.ViewHolder {
ImageView icon;
@@ -135,7 +138,11 @@ public class ConnectionsAdapter extends RecyclerView.Adapter();
mUidFilter = Utils.UID_NO_FILTER;
+ mWhitelistEnabled = true;
+ mWhitelist = new Whitelist(context);
setHasStableIds(true);
+
+ mWhitelist.reload();
}
@Override
@@ -181,10 +188,10 @@ public class ConnectionsAdapter extends RecyclerView.Adapter();
for(int i=0; i {
private final LayoutInflater mLayoutInflater;
- private boolean mShowTrash;
- private final int mResId;
- public WhitelistEditAdapter(Context context, int res, Iterator items) {
- super(context, res);
-
- mResId = res;
- mShowTrash = true;
+ public WhitelistEditAdapter(Context context, Iterator items) {
+ super(context, R.layout.whitelist_item);
mLayoutInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
while(items.hasNext()) {
ConnectionsMatcher.Item item = items.next();
add(item);
}
-
- recheckSize();
}
@NonNull
@Override
public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
if(convertView == null)
- convertView = mLayoutInflater.inflate(mResId, parent, false);
+ convertView = mLayoutInflater.inflate(R.layout.whitelist_item, parent, false);
ConnectionsMatcher.Item item = getItem(position);
((TextView)convertView.findViewById(R.id.item_label)).setText(item.getLabel());
- convertView.findViewById(R.id.item_icon).setVisibility(mShowTrash ? View.VISIBLE : View.INVISIBLE);
return convertView;
}
-
- @Override
- public void remove(@Nullable ConnectionsMatcher.Item object) {
- super.remove(object);
-
- recheckSize();
- }
-
- private void recheckSize() {
- if(getCount() == 1) {
- // Prevent an empty view
- mShowTrash = false;
- notifyDataSetChanged();
- } else if(!mShowTrash) {
- mShowTrash = true;
- notifyDataSetChanged();
- }
- }
}
diff --git a/app/src/main/java/com/emanuelef/remote_capture/fragments/ConnectionsFragment.java b/app/src/main/java/com/emanuelef/remote_capture/fragments/ConnectionsFragment.java
index fce400dd..7015ccd9 100644
--- a/app/src/main/java/com/emanuelef/remote_capture/fragments/ConnectionsFragment.java
+++ b/app/src/main/java/com/emanuelef/remote_capture/fragments/ConnectionsFragment.java
@@ -38,7 +38,6 @@ import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
-import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
@@ -47,7 +46,6 @@ import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import androidx.appcompat.app.AlertDialog;
import androidx.core.content.ContextCompat;
import androidx.fragment.app.Fragment;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
@@ -61,7 +59,6 @@ import com.emanuelef.remote_capture.ConnectionsRegister;
import com.emanuelef.remote_capture.R;
import com.emanuelef.remote_capture.Utils;
import com.emanuelef.remote_capture.activities.MainActivity;
-import com.emanuelef.remote_capture.adapters.WhitelistEditAdapter;
import com.emanuelef.remote_capture.model.AppDescriptor;
import com.emanuelef.remote_capture.model.AppState;
import com.emanuelef.remote_capture.model.ConnectionDescriptor;
@@ -77,7 +74,6 @@ import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Collections;
-import java.util.Iterator;
import java.util.Objects;
import java.util.Set;
@@ -92,7 +88,6 @@ public class ConnectionsFragment extends Fragment implements ConnectionsListener
private boolean autoScroll;
private boolean listenerSet;
private MenuItem mMenuItemAppSel;
- private MenuItem mMenuItemWhitelist;
private MenuItem mMenuItemEnableWhitelist;
private MenuItem mMenuItemDisableWhitelist;
private MenuItem mSave;
@@ -110,11 +105,11 @@ public class ConnectionsFragment extends Fragment implements ConnectionsListener
public void onResume() {
super.onResume();
- registerConnsListener();
+ // Reload the whitelist as it could modified in WhitelistActivity
+ mAdapter.mWhitelist.reload();
- // reg.mWhitelistEnabled may have changed (e.g. when filtering from the AppsActivity
- if(mMenuItemWhitelist != null)
- refreshWhitelistMenu();
+ registerConnsListener();
+ refreshMenuIcons();
}
@Override
@@ -129,6 +124,7 @@ public class ConnectionsFragment extends Fragment implements ConnectionsListener
super.onSaveInstanceState(outState);
outState.putInt("uidFilter", mAdapter.getUidFilter());
+ outState.putBoolean("whitelistEnabled", mAdapter.mWhitelistEnabled);
}
@Override
@@ -232,22 +228,23 @@ public class ConnectionsFragment extends Fragment implements ConnectionsListener
if(uidFilter != Utils.UID_NO_FILTER) {
// "consume" it
intent.removeExtra(MainActivity.UID_FILTER_EXTRA);
-
- // disable the whitelist to prevent an empty view
- ConnectionsRegister reg = CaptureService.getConnsRegister();
-
- if(reg != null)
- reg.mWhitelistEnabled = false;
}
}
- if ((uidFilter == Utils.UID_NO_FILTER) && (savedInstanceState != null)) {
- uidFilter = savedInstanceState.getInt("uidFilter", Utils.UID_NO_FILTER);
+ if(savedInstanceState != null) {
+ if(uidFilter == Utils.UID_NO_FILTER)
+ uidFilter = savedInstanceState.getInt("uidFilter", Utils.UID_NO_FILTER);
+
+ mAdapter.mWhitelistEnabled = savedInstanceState.getBoolean("whitelistEnabled", true);
}
- if(uidFilter != Utils.UID_NO_FILTER)
+ if(uidFilter != Utils.UID_NO_FILTER) {
setUidFilter(uidFilter);
+ // Avoid hiding the interesting items
+ mAdapter.mWhitelistEnabled = false;
+ }
+
// Register for service status
mReceiver = new BroadcastReceiver() {
@Override
@@ -330,29 +327,29 @@ public class ConnectionsFragment extends Fragment implements ConnectionsListener
@Override
public boolean onContextItemSelected(@NonNull MenuItem item) {
- ConnectionsRegister reg = CaptureService.getConnsRegister();
ConnectionDescriptor conn = mAdapter.getClickedItem();
- if((reg == null) || (conn == null))
+ if(conn == null)
return super.onContextItemSelected(item);
int id = item.getItemId();
String label = item.getTitle().toString();
if(id == R.id.exclude_app)
- reg.mWhitelist.addApp(conn.uid, label);
+ mAdapter.mWhitelist.addApp(conn.uid, label);
else if(id == R.id.exclude_host)
- reg.mWhitelist.addHost(conn.info, label);
+ mAdapter.mWhitelist.addHost(conn.info, label);
else if(id == R.id.exclude_ip)
- reg.mWhitelist.addIp(conn.dst_ip, label);
+ mAdapter.mWhitelist.addIp(conn.dst_ip, label);
else if(id == R.id.exclude_proto)
- reg.mWhitelist.addProto(conn.l7proto, label);
+ mAdapter.mWhitelist.addProto(conn.l7proto, label);
else if(id == R.id.exclude_root_domain)
- reg.mWhitelist.addRootDomain(Utils.getRootDomain(conn.info), label);
+ mAdapter.mWhitelist.addRootDomain(Utils.getRootDomain(conn.info), label);
else
return super.onContextItemSelected(item);
- reg.mWhitelistEnabled = true;
+ mAdapter.mWhitelist.save();
+ mAdapter.mWhitelistEnabled = true;
refreshFilteredConnections();
return true;
}
@@ -402,7 +399,7 @@ public class ConnectionsFragment extends Fragment implements ConnectionsListener
// This performs an unoptimized adapter refresh
private void refreshFilteredConnections() {
mAdapter.refreshFilteredConnections();
- refreshWhitelistMenu();
+ refreshMenuIcons();
recheckScroll();
}
@@ -465,14 +462,12 @@ public class ConnectionsFragment extends Fragment implements ConnectionsListener
mSave = menu.findItem(R.id.save);
mMenuItemAppSel = menu.findItem(R.id.action_show_app_filter);
- mMenuItemWhitelist = menu.findItem(R.id.whitelist);
- mMenuItemEnableWhitelist = menu.findItem(R.id.enable_whitelist);
- mMenuItemDisableWhitelist = menu.findItem(R.id.disable_whitelist);
+ mMenuItemEnableWhitelist = menu.findItem(R.id.hide_whitelist);
+ mMenuItemDisableWhitelist = menu.findItem(R.id.show_whitelist);
mFilterIcon = mMenuItemAppSel.getIcon();
refreshFilterIcon();
refreshMenuIcons();
- refreshWhitelistMenu();
}
@Override
@@ -489,15 +484,12 @@ public class ConnectionsFragment extends Fragment implements ConnectionsListener
} else if(id == R.id.save) {
openFileSelector();
return true;
- } else if(id == R.id.edit_whitelist) {
- showWhitelistEditor();
- return true;
- } else if((id == R.id.enable_whitelist) || (id == R.id.disable_whitelist)) {
+ } else if((id == R.id.hide_whitelist) || (id == R.id.show_whitelist)) {
ConnectionsRegister reg = CaptureService.getConnsRegister();
if(reg == null)
return false;
- reg.mWhitelistEnabled = !reg.mWhitelistEnabled;
+ mAdapter.mWhitelistEnabled = !mAdapter.mWhitelistEnabled;
// Delay the refresh to wait for the menu to be closed
(new Handler(requireActivity().getMainLooper())).postDelayed(this::refreshFilteredConnections, 50);
@@ -576,25 +568,14 @@ public class ConnectionsFragment extends Fragment implements ConnectionsListener
mMenuItemAppSel.setEnabled(is_enabled);
mSave.setEnabled(is_enabled);
- }
- private void refreshWhitelistMenu() {
- ConnectionsRegister reg = CaptureService.getConnsRegister();
-
- if(reg == null) {
- mMenuItemWhitelist.setVisible(false);
- return;
+ if((mAdapter == null) || mAdapter.mWhitelist.isEmpty()) {
+ mMenuItemDisableWhitelist.setVisible(false);
+ mMenuItemEnableWhitelist.setVisible(false);
+ } else {
+ mMenuItemDisableWhitelist.setVisible(mAdapter.mWhitelistEnabled);
+ mMenuItemEnableWhitelist.setVisible(!mAdapter.mWhitelistEnabled);
}
-
- // Update the icon only if something changed
- // NOTE: getApplicationContext required to properly style the tint
- mMenuItemWhitelist.setIcon(
- ContextCompat.getDrawable(requireContext().getApplicationContext(),
- reg.mWhitelistEnabled ? R.drawable.ic_eye_slash : R.drawable.ic_eye));
-
- mMenuItemWhitelist.setVisible(!reg.mWhitelist.isEmpty());
- mMenuItemDisableWhitelist.setVisible(reg.mWhitelistEnabled);
- mMenuItemEnableWhitelist.setVisible(!reg.mWhitelistEnabled);
}
private void dumpCsv() {
@@ -663,59 +644,6 @@ public class ConnectionsFragment extends Fragment implements ConnectionsListener
}
}
- private void showWhitelistEditor() {
- ConnectionsRegister reg = CaptureService.getConnsRegister();
-
- if(reg == null)
- return;
-
- AlertDialog.Builder builder = new AlertDialog.Builder(requireActivity());
- WhitelistEditAdapter adapter = new WhitelistEditAdapter(requireContext(),
- R.layout.whitelist_item, reg.mWhitelist.iterItems());
- View exclListView = requireActivity().getLayoutInflater().inflate(R.layout.whitelist, null);
-
- ListView whitelist = exclListView.findViewById(R.id.list);
- whitelist.setAdapter(adapter);
- whitelist.setOnItemClickListener((parent, view, position, id) -> {
- if(adapter.getCount() > 1)
- adapter.remove(adapter.getItem(position));
- });
-
- builder.setTitle(R.string.edit_whitelist);
- builder.setView(exclListView);
- builder.setPositiveButton(R.string.ok, (dialog, which) -> updateWhitelist(adapter));
- builder.setNeutralButton(R.string.cancel, (dialog, which) -> dialog.dismiss());
-
- final AlertDialog alert = builder.create();
- alert.setCanceledOnTouchOutside(true);
-
- alert.show();
- }
-
- private void updateWhitelist(WhitelistEditAdapter adapter) {
- ConnectionsRegister reg = CaptureService.getConnsRegister();
- ArrayList toRemove = new ArrayList<>();
-
- if(reg == null)
- return;
-
- Iterator iter = reg.mWhitelist.iterItems();
- boolean changed = false;
-
- // Remove the whitelisted items which are not in the adapter dataset
- while(iter.hasNext()) {
- ConnectionsMatcher.Item item = iter.next();
-
- if(adapter.getPosition(item) < 0)
- toRemove.add(item);
- }
-
- if(toRemove.size() > 0) {
- reg.mWhitelist.removeItems(toRemove);
- refreshFilteredConnections();
- }
- }
-
private void csvFileResult(final ActivityResult result) {
if (result.getResultCode() == Activity.RESULT_OK && result.getData() != null) {
mCsvFname = result.getData().getData();
diff --git a/app/src/main/java/com/emanuelef/remote_capture/fragments/WhitelistFragment.java b/app/src/main/java/com/emanuelef/remote_capture/fragments/WhitelistFragment.java
new file mode 100644
index 00000000..b47d6dbe
--- /dev/null
+++ b/app/src/main/java/com/emanuelef/remote_capture/fragments/WhitelistFragment.java
@@ -0,0 +1,160 @@
+/*
+ * This file is part of PCAPdroid.
+ *
+ * PCAPdroid is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * PCAPdroid is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with PCAPdroid. If not, see .
+ *
+ * Copyright 2020-21 - Emanuele Faranda
+ */
+
+package com.emanuelef.remote_capture.fragments;
+
+import android.app.AlertDialog;
+import android.os.Bundle;
+import android.view.ActionMode;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AbsListView;
+import android.widget.ListView;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.fragment.app.Fragment;
+
+import com.emanuelef.remote_capture.R;
+import com.emanuelef.remote_capture.adapters.WhitelistEditAdapter;
+import com.emanuelef.remote_capture.model.ConnectionsMatcher;
+import com.emanuelef.remote_capture.model.Whitelist;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+
+public class WhitelistFragment extends Fragment {
+ private WhitelistEditAdapter mAdapter;
+ private TextView mEmptyText;
+ private ArrayList mSelected = new ArrayList<>();
+ private Whitelist mWhitelist;
+ private ListView mWhitelistView;
+ private static final String TAG = "WhitelistFragment";
+
+ @Override
+ public View onCreateView(LayoutInflater inflater,
+ ViewGroup container, Bundle savedInstanceState) {
+ return inflater.inflate(R.layout.whitelist_fragment, container, false);
+ }
+
+ @Override
+ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
+ mWhitelistView = view.findViewById(R.id.whitelist);
+ mEmptyText = view.findViewById(R.id.whitelist_empty);
+ mWhitelist = new Whitelist(view.getContext());
+ mWhitelist.reload();
+
+ mAdapter = new WhitelistEditAdapter(requireContext(), mWhitelist.iterItems());
+ mWhitelistView.setAdapter(mAdapter);
+ mWhitelistView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL);
+ mWhitelistView.setMultiChoiceModeListener(new AbsListView.MultiChoiceModeListener() {
+ @Override
+ public void onItemCheckedStateChanged(ActionMode mode, int position, long id, boolean checked) {
+ ConnectionsMatcher.Item item = mAdapter.getItem(position);
+
+ if(checked)
+ mSelected.add(item);
+ else
+ mSelected.remove(item);
+
+ mode.setTitle(getString(R.string.n_selected, mSelected.size()));
+ }
+
+ @Override
+ public boolean onCreateActionMode(ActionMode mode, Menu menu) {
+ MenuInflater inflater = requireActivity().getMenuInflater();
+ inflater.inflate(R.menu.whitelist_cab, menu);
+ return true;
+ }
+
+ @Override
+ public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
+ return false;
+ }
+
+ @Override
+ public boolean onActionItemClicked(ActionMode mode, MenuItem menuItem) {
+ int id = menuItem.getItemId();
+
+ if(id == R.id.delete_entry) {
+ if(mSelected.size() >= mAdapter.getCount()) {
+ mAdapter.clear();
+ mWhitelist.clear();
+ mWhitelist.save();
+ } else {
+ for(ConnectionsMatcher.Item item : mSelected)
+ mAdapter.remove(item);
+ updateWhitelist();
+ }
+
+ mode.finish();
+ recheckWhitelistSize();
+ return true;
+ } else if(id == R.id.select_all) {
+ if(mSelected.size() >= mAdapter.getCount())
+ mode.finish();
+ else {
+ for(int i=0; i();
+ }
+ });
+
+ recheckWhitelistSize();
+ }
+
+ private void recheckWhitelistSize() {
+ mEmptyText.setVisibility((mAdapter.getCount() == 0) ? View.VISIBLE : View.GONE);
+ }
+
+ private void updateWhitelist() {
+ ArrayList toRemove = new ArrayList<>();
+
+ Iterator iter = mWhitelist.iterItems();
+
+ // Remove the whitelisted items which are not in the adapter dataset
+ while(iter.hasNext()) {
+ ConnectionsMatcher.Item item = iter.next();
+
+ if (mAdapter.getPosition(item) < 0)
+ toRemove.add(item);
+ }
+
+ if(toRemove.size() > 0) {
+ mWhitelist.removeItems(toRemove);
+ mWhitelist.save();
+ }
+ }
+}
diff --git a/app/src/main/java/com/emanuelef/remote_capture/model/ConnectionsMatcher.java b/app/src/main/java/com/emanuelef/remote_capture/model/ConnectionsMatcher.java
index 9a48fd7d..34c5718e 100644
--- a/app/src/main/java/com/emanuelef/remote_capture/model/ConnectionsMatcher.java
+++ b/app/src/main/java/com/emanuelef/remote_capture/model/ConnectionsMatcher.java
@@ -22,7 +22,6 @@ package com.emanuelef.remote_capture.model;
import android.content.Context;
import android.graphics.Typeface;
import android.text.style.StyleSpan;
-import android.util.Log;
import androidx.annotation.Nullable;
@@ -135,6 +134,8 @@ public class ConnectionsMatcher {
private void deserialize(JsonObject object) {
mItems = new ArrayList<>();
+ mMatches.clear();
+
JsonArray itemArray = object.getAsJsonArray("items");
AppsResolver resolver = new AppsResolver(mContext);
@@ -176,7 +177,6 @@ public class ConnectionsMatcher {
private void addItem(Item item) {
String key = matchKey(item.getType(), item.getValue().toString());
- Log.d(TAG, key);
if(!mMatches.containsKey(key)) {
mItems.add(item);
@@ -217,7 +217,7 @@ public class ConnectionsMatcher {
}
public String toJson() {
- Gson gson = new GsonBuilder().registerTypeAdapter(ConnectionsMatcher.class, new Serializer())
+ Gson gson = new GsonBuilder().registerTypeAdapter(getClass(), new Serializer())
.create();
String serialized = gson.toJson(this);
diff --git a/app/src/main/java/com/emanuelef/remote_capture/model/Whitelist.java b/app/src/main/java/com/emanuelef/remote_capture/model/Whitelist.java
new file mode 100644
index 00000000..ff0c9ab0
--- /dev/null
+++ b/app/src/main/java/com/emanuelef/remote_capture/model/Whitelist.java
@@ -0,0 +1,49 @@
+/*
+ * This file is part of PCAPdroid.
+ *
+ * PCAPdroid is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * PCAPdroid is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with PCAPdroid. If not, see .
+ *
+ * Copyright 2020-21 - Emanuele Faranda
+ */
+
+package com.emanuelef.remote_capture.model;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+
+import androidx.preference.PreferenceManager;
+
+public class Whitelist extends ConnectionsMatcher {
+ private final SharedPreferences mPrefs;
+
+ public Whitelist(Context ctx) {
+ super(ctx);
+
+ mPrefs = PreferenceManager.getDefaultSharedPreferences(ctx);
+ }
+
+ public void reload() {
+ // Try to restore the whitelist
+ String serialized = mPrefs.getString(Prefs.PREF_WHITELIST, "");
+
+ if(!serialized.isEmpty())
+ fromJson(serialized);
+ }
+
+ public void save() {
+ mPrefs.edit()
+ .putString(Prefs.PREF_WHITELIST, toJson())
+ .apply();
+ }
+}
diff --git a/app/src/main/java/com/emanuelef/remote_capture/views/EmptyRecyclerView.java b/app/src/main/java/com/emanuelef/remote_capture/views/EmptyRecyclerView.java
index 70731142..8ddc0a96 100644
--- a/app/src/main/java/com/emanuelef/remote_capture/views/EmptyRecyclerView.java
+++ b/app/src/main/java/com/emanuelef/remote_capture/views/EmptyRecyclerView.java
@@ -83,6 +83,8 @@ public class EmptyRecyclerView extends RecyclerView {
if (adapter != null) {
adapter.registerAdapterDataObserver(observer);
}
+
+ initEmptyView();
}
public void setEmptyView(View view) {
diff --git a/app/src/main/res/drawable/ic_checklist.xml b/app/src/main/res/drawable/ic_checklist.xml
new file mode 100644
index 00000000..1ab66953
--- /dev/null
+++ b/app/src/main/res/drawable/ic_checklist.xml
@@ -0,0 +1,5 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_select_all.xml b/app/src/main/res/drawable/ic_select_all.xml
new file mode 100644
index 00000000..c70e5ac8
--- /dev/null
+++ b/app/src/main/res/drawable/ic_select_all.xml
@@ -0,0 +1,5 @@
+
+
+
diff --git a/app/src/main/res/layout/apps_stats.xml b/app/src/main/res/layout/apps_stats.xml
index 2889fbd2..02222a30 100644
--- a/app/src/main/res/layout/apps_stats.xml
+++ b/app/src/main/res/layout/apps_stats.xml
@@ -4,11 +4,11 @@
android:layout_height="match_parent">
+ android:orientation="vertical">
-
diff --git a/app/src/main/res/layout/whitelist_fragment.xml b/app/src/main/res/layout/whitelist_fragment.xml
new file mode 100644
index 00000000..6a37a93e
--- /dev/null
+++ b/app/src/main/res/layout/whitelist_fragment.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/whitelist_item.xml b/app/src/main/res/layout/whitelist_item.xml
index 73e6bf91..afffaa6d 100644
--- a/app/src/main/res/layout/whitelist_item.xml
+++ b/app/src/main/res/layout/whitelist_item.xml
@@ -1,25 +1,16 @@
-
-
+ android:background="?android:attr/activatedBackgroundIndicator"
+ android:orientation="vertical"
+ android:padding="15sp">
-
-
-
+ android:gravity="center_vertical" />
+
diff --git a/app/src/main/res/menu/connections_menu.xml b/app/src/main/res/menu/connections_menu.xml
index d026e87e..cfe7955f 100644
--- a/app/src/main/res/menu/connections_menu.xml
+++ b/app/src/main/res/menu/connections_menu.xml
@@ -11,23 +11,19 @@
app:showAsAction="ifRoom" />
+
+ -
-
-
+ android:orderInCategory="20"
+ app:showAsAction="ifRoom"
+ android:visible="false" />
+
-
+
\ No newline at end of file
diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml
index c5d0ef9b..b2027c5c 100644
--- a/app/src/main/res/values-it/strings.xml
+++ b/app/src/main/res/values-it/strings.xml
@@ -135,5 +135,8 @@
Esclusioni
Modifica Esclusioni
Escludi …
+ Lista esclusioni vuota.\nPuoi escludere una connessione tenendo premuto su di essa.
+ %1$d selezionati
+ Seleziona tutto
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index ea1b0e22..45bcc661 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -139,10 +139,13 @@
Cancel
Edit
Delete all
- Show whitelisted
- Hide whitelisted
+ Show whitelisted connections
+ Hide whitelisted connections
Whitelist
Edit Whitelist
Whitelist …
+ The whitelist is empty.\nYou can long press a connection to whitelist it.
+ %1$d selected
+ Select all