mirror of
https://github.com/emanuele-f/PCAPdroid.git
synced 2026-06-16 21:10:57 +08:00
Add app context menu to quickly add firewall rule
Moreover, the apps list now shows a ban icon if the app is blocked
This commit is contained in:
parent
67dae23ca2
commit
3be18fb2e5
@ -20,6 +20,7 @@
|
||||
package com.emanuelef.remote_capture.adapters;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
@ -29,29 +30,37 @@ import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.preference.PreferenceManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.emanuelef.remote_capture.CaptureService;
|
||||
import com.emanuelef.remote_capture.PCAPdroid;
|
||||
import com.emanuelef.remote_capture.R;
|
||||
import com.emanuelef.remote_capture.Utils;
|
||||
import com.emanuelef.remote_capture.model.AppDescriptor;
|
||||
import com.emanuelef.remote_capture.model.AppStats;
|
||||
import com.emanuelef.remote_capture.AppsResolver;
|
||||
import com.emanuelef.remote_capture.model.MatchList;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class AppsStatsAdapter extends RecyclerView.Adapter<AppsStatsAdapter.ViewHolder> {
|
||||
private static final String TAG = "ConnectionsAdapter";
|
||||
private static final String TAG = "AppsStatsAdapter";
|
||||
private final Context mContext;
|
||||
private final LayoutInflater mLayoutInflater;
|
||||
private final Drawable mUnknownIcon;
|
||||
private final MatchList mBlocklist;
|
||||
private View.OnClickListener mListener;
|
||||
private List<AppStats> mStats;
|
||||
private final AppsResolver mApps;
|
||||
private final SharedPreferences mPrefs;
|
||||
private int mClickedPosition;
|
||||
|
||||
public static class ViewHolder extends RecyclerView.ViewHolder {
|
||||
public class ViewHolder extends RecyclerView.ViewHolder {
|
||||
ImageView icon;
|
||||
ImageView blockedFlag;
|
||||
TextView info;
|
||||
TextView traffic;
|
||||
|
||||
@ -59,27 +68,29 @@ public class AppsStatsAdapter extends RecyclerView.Adapter<AppsStatsAdapter.View
|
||||
super(itemView);
|
||||
|
||||
icon = itemView.findViewById(R.id.icon);
|
||||
blockedFlag = itemView.findViewById(R.id.blocked);
|
||||
info = itemView.findViewById(R.id.app_info);
|
||||
traffic = itemView.findViewById(R.id.traffic);
|
||||
}
|
||||
|
||||
public void bindAppStats(Context context, AppStats stats, AppsResolver apps, Drawable unknownIcon) {
|
||||
public void bindAppStats(AppStats stats) {
|
||||
Drawable appIcon;
|
||||
|
||||
// NOTE: can be null
|
||||
AppDescriptor app = (apps != null) ? apps.get(stats.getUid(), 0) : null;
|
||||
AppDescriptor app = (mApps != null) ? mApps.get(stats.getUid(), 0) : null;
|
||||
|
||||
appIcon = ((app != null) && (app.getIcon() != null)) ? app.getIcon() : unknownIcon;
|
||||
appIcon = ((app != null) && (app.getIcon() != null)) ? app.getIcon() : mUnknownIcon;
|
||||
icon.setImageDrawable(appIcon);
|
||||
|
||||
String info_txt = (app != null) ? app.getName() : Integer.toString(stats.getUid());
|
||||
|
||||
if(stats.numConnections > 1)
|
||||
info_txt += " (" + Utils.formatNumber(context, stats.numConnections) + ")";
|
||||
info_txt += " (" + Utils.formatNumber(mContext, stats.numConnections) + ")";
|
||||
|
||||
info.setText(info_txt);
|
||||
|
||||
traffic.setText(Utils.formatBytes(stats.sentBytes + stats.rcvdBytes));
|
||||
blockedFlag.setVisibility(mBlocklist.matchesApp(stats.getUid()) ? View.VISIBLE : View.INVISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
@ -88,8 +99,10 @@ public class AppsStatsAdapter extends RecyclerView.Adapter<AppsStatsAdapter.View
|
||||
mApps = new AppsResolver(context);
|
||||
mLayoutInflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
||||
mUnknownIcon = ContextCompat.getDrawable(mContext, android.R.drawable.ic_menu_help);
|
||||
mBlocklist = PCAPdroid.getInstance().getBlocklist();
|
||||
mListener = null;
|
||||
mStats = new ArrayList<>();
|
||||
mPrefs = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
setHasStableIds(true);
|
||||
}
|
||||
|
||||
@ -106,7 +119,21 @@ public class AppsStatsAdapter extends RecyclerView.Adapter<AppsStatsAdapter.View
|
||||
if(mListener != null)
|
||||
view.setOnClickListener(mListener);
|
||||
|
||||
return new ViewHolder(view);
|
||||
ViewHolder holder = new ViewHolder(view);
|
||||
|
||||
if(CaptureService.isFirewallEnabled(mContext, mPrefs)) {
|
||||
// Enable the ability to show the context menu
|
||||
view.setLongClickable(true);
|
||||
|
||||
view.setOnLongClickListener(v -> {
|
||||
// see registerForContextMenu
|
||||
mClickedPosition = holder.getAbsoluteAdapterPosition();
|
||||
return false;
|
||||
});
|
||||
} else
|
||||
view.setLongClickable(false);
|
||||
|
||||
return holder;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -116,7 +143,7 @@ public class AppsStatsAdapter extends RecyclerView.Adapter<AppsStatsAdapter.View
|
||||
if(stats == null)
|
||||
return;
|
||||
|
||||
holder.bindAppStats(mContext, stats, mApps, mUnknownIcon);
|
||||
holder.bindAppStats(stats);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -130,6 +157,10 @@ public class AppsStatsAdapter extends RecyclerView.Adapter<AppsStatsAdapter.View
|
||||
return mStats.get(pos);
|
||||
}
|
||||
|
||||
public int getClickedItemPos() {
|
||||
return mClickedPosition;
|
||||
}
|
||||
|
||||
public String getItemPackage(int pos) {
|
||||
AppStats stats = getItem(pos);
|
||||
|
||||
|
||||
@ -199,6 +199,7 @@ public class ConnectionsAdapter extends RecyclerView.Adapter<ConnectionsAdapter.
|
||||
ViewHolder holder = new ViewHolder(view);
|
||||
|
||||
view.setOnLongClickListener(v -> {
|
||||
// see registerForContextMenu
|
||||
mClickedPosition = holder.getAbsoluteAdapterPosition();
|
||||
return false;
|
||||
});
|
||||
|
||||
@ -26,7 +26,11 @@ import android.content.IntentFilter;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.util.Log;
|
||||
import android.view.ContextMenu;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
@ -36,13 +40,17 @@ import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
|
||||
|
||||
import com.emanuelef.remote_capture.Billing;
|
||||
import com.emanuelef.remote_capture.CaptureService;
|
||||
import com.emanuelef.remote_capture.ConnectionsRegister;
|
||||
import com.emanuelef.remote_capture.PCAPdroid;
|
||||
import com.emanuelef.remote_capture.R;
|
||||
import com.emanuelef.remote_capture.activities.AppDetailsActivity;
|
||||
import com.emanuelef.remote_capture.adapters.AppsStatsAdapter;
|
||||
import com.emanuelef.remote_capture.interfaces.ConnectionsListener;
|
||||
import com.emanuelef.remote_capture.model.AppStats;
|
||||
import com.emanuelef.remote_capture.model.ConnectionDescriptor;
|
||||
import com.emanuelef.remote_capture.model.MatchList;
|
||||
import com.emanuelef.remote_capture.views.EmptyRecyclerView;
|
||||
|
||||
public class AppsFragment extends Fragment implements ConnectionsListener {
|
||||
@ -78,6 +86,7 @@ public class AppsFragment extends Fragment implements ConnectionsListener {
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
mRecyclerView = view.findViewById(R.id.apps_stats_view);
|
||||
mRecyclerView.setLayoutManager(new EmptyRecyclerView.MyLinearLayoutManager(getContext()));
|
||||
registerForContextMenu(mRecyclerView);
|
||||
|
||||
mAdapter = new AppsStatsAdapter(getContext());
|
||||
doRefreshApps();
|
||||
@ -129,6 +138,47 @@ public class AppsFragment extends Fragment implements ConnectionsListener {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreateContextMenu(@NonNull ContextMenu menu, @NonNull View v,
|
||||
@Nullable ContextMenu.ContextMenuInfo menuInfo) {
|
||||
super.onCreateContextMenu(menu, v, menuInfo);
|
||||
Log.d(TAG, "onCreateContextMenu");
|
||||
|
||||
MenuInflater inflater = requireActivity().getMenuInflater();
|
||||
inflater.inflate(R.menu.app_context_menu, menu);
|
||||
|
||||
AppStats stats = mAdapter.getItem(mAdapter.getClickedItemPos());
|
||||
if(stats == null)
|
||||
return;
|
||||
|
||||
// TODO unblock
|
||||
menu.findItem(R.id.block_app).setVisible(true);
|
||||
menu.findItem(R.id.unblock_app).setVisible(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onContextItemSelected(@NonNull MenuItem item) {
|
||||
int id = item.getItemId();
|
||||
MatchList blocklist = PCAPdroid.getInstance().getBlocklist();
|
||||
int itemPos = mAdapter.getClickedItemPos();
|
||||
AppStats app = mAdapter.getItem(itemPos);
|
||||
|
||||
if(id == R.id.block_app)
|
||||
blocklist.addApp(app.getUid());
|
||||
else
|
||||
return super.onContextItemSelected(item);
|
||||
|
||||
// refresh the blocklist
|
||||
blocklist.save();
|
||||
if(CaptureService.isServiceActive())
|
||||
CaptureService.requireInstance().reloadBlocklist();
|
||||
|
||||
// refresh the item
|
||||
mAdapter.notifyItemChanged(itemPos);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void registerConnsListener() {
|
||||
if (!listenerSet) {
|
||||
ConnectionsRegister reg = CaptureService.getConnsRegister();
|
||||
|
||||
@ -6,6 +6,7 @@
|
||||
android:orientation="horizontal"
|
||||
android:paddingHorizontal="4dp"
|
||||
android:paddingVertical="2dp"
|
||||
android:gravity="center_vertical"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<ImageView
|
||||
@ -22,9 +23,15 @@
|
||||
tools:text="Example app"
|
||||
android:maxLines="2"
|
||||
android:gravity="center_vertical"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:id="@+id/app_info"/>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/blocked"
|
||||
android:src="@drawable/ic_block"
|
||||
android:layout_marginHorizontal="4dp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="12sp" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="8sp"
|
||||
android:layout_height="fill_parent"
|
||||
|
||||
13
app/src/main/res/menu/app_context_menu.xml
Normal file
13
app/src/main/res/menu/app_context_menu.xml
Normal file
@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<item
|
||||
android:id="@+id/block_app"
|
||||
android:title="@string/action_block" />
|
||||
|
||||
<item
|
||||
android:id="@+id/unblock_app"
|
||||
android:title="@string/action_unblock" />
|
||||
</menu>
|
||||
@ -349,4 +349,6 @@
|
||||
<string name="firewall_is_disabled">Firewall is disabled</string>
|
||||
<string name="firewall_is_enabled">Firewall is enabled</string>
|
||||
<string name="app_info">App info</string>
|
||||
<string name="action_block">Block</string>
|
||||
<string name="action_unblock">Unblock</string>
|
||||
</resources>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user