From f49c34ddec8eca36192b89de2aea24371385e7b1 Mon Sep 17 00:00:00 2001 From: emanuele-f Date: Mon, 14 Jun 2021 15:21:57 +0200 Subject: [PATCH] Rework whitelist The whitelist editor is now a separate activity. Whitelist removed from the ConnectionRegister. --- app/src/main/AndroidManifest.xml | 4 + .../remote_capture/CaptureService.java | 5 +- .../remote_capture/ConnectionsRegister.java | 28 +-- .../activities/MainActivity.java | 3 + .../activities/WhitelistActivity.java | 42 +++++ .../adapters/ConnectionsAdapter.java | 22 ++- .../adapters/WhitelistEditAdapter.java | 32 +--- .../fragments/ConnectionsFragment.java | 140 ++++----------- .../fragments/WhitelistFragment.java | 160 ++++++++++++++++++ .../model/ConnectionsMatcher.java | 6 +- .../remote_capture/model/Whitelist.java | 49 ++++++ .../views/EmptyRecyclerView.java | 2 + app/src/main/res/drawable/ic_checklist.xml | 5 + app/src/main/res/drawable/ic_select_all.xml | 5 + app/src/main/res/layout/apps_stats.xml | 2 +- .../{whitelist.xml => whitelist_activity.xml} | 7 +- .../main/res/layout/whitelist_fragment.xml | 22 +++ app/src/main/res/layout/whitelist_item.xml | 21 +-- app/src/main/res/menu/connections_menu.xml | 26 ++- app/src/main/res/menu/nav_items.xml | 4 + app/src/main/res/menu/whitelist_cab.xml | 19 +++ app/src/main/res/values-it/strings.xml | 3 + app/src/main/res/values/strings.xml | 7 +- 23 files changed, 402 insertions(+), 212 deletions(-) create mode 100644 app/src/main/java/com/emanuelef/remote_capture/activities/WhitelistActivity.java create mode 100644 app/src/main/java/com/emanuelef/remote_capture/fragments/WhitelistFragment.java create mode 100644 app/src/main/java/com/emanuelef/remote_capture/model/Whitelist.java create mode 100644 app/src/main/res/drawable/ic_checklist.xml create mode 100644 app/src/main/res/drawable/ic_select_all.xml rename app/src/main/res/layout/{whitelist.xml => whitelist_activity.xml} (69%) create mode 100644 app/src/main/res/layout/whitelist_fragment.xml create mode 100644 app/src/main/res/menu/whitelist_cab.xml 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