mirror of
https://github.com/emanuele-f/PCAPdroid.git
synced 2026-06-16 21:10:57 +08:00
Migrate connections view to RecyclerView
This commit is contained in:
parent
c43f2e8f6c
commit
9722b221ce
@ -44,7 +44,8 @@ dependencies {
|
||||
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
|
||||
implementation 'androidx.localbroadcastmanager:localbroadcastmanager:1.0.0'
|
||||
implementation 'androidx.preference:preference:1.1.1'
|
||||
implementation 'com.google.android.material:material:1.2.1'
|
||||
implementation 'androidx.recyclerview:recyclerview:1.1.0'
|
||||
implementation 'com.google.android.material:material:1.3.0'
|
||||
|
||||
// Third-party
|
||||
implementation 'cat.ereza:customactivityoncrash:2.3.0'
|
||||
|
||||
@ -21,81 +21,99 @@ package com.emanuelef.remote_capture;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Handler;
|
||||
import android.telecom.ConnectionRequest;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.BaseAdapter;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Objects;
|
||||
|
||||
public class ConnectionsAdapter extends BaseAdapter {
|
||||
private static final String TAG = "ConnectionsAdapter";
|
||||
private final MainActivity mActivity;
|
||||
private final Drawable mUnknownIcon;
|
||||
class ViewHolder extends RecyclerView.ViewHolder {
|
||||
ImageView icon;
|
||||
TextView remote;
|
||||
TextView l7proto;
|
||||
TextView traffic;
|
||||
|
||||
ConnectionsAdapter(MainActivity context) {
|
||||
mActivity = context;
|
||||
mUnknownIcon = ContextCompat.getDrawable(mActivity, android.R.drawable.ic_menu_help);
|
||||
ViewHolder(View itemView) {
|
||||
super(itemView);
|
||||
|
||||
icon = itemView.findViewById(R.id.icon);
|
||||
remote = itemView.findViewById(R.id.remote);
|
||||
l7proto = itemView.findViewById(R.id.l7proto);
|
||||
traffic = itemView.findViewById(R.id.traffic);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public View getView(int position, View convertView, @NonNull ViewGroup parent) {
|
||||
ConnDescriptor conn = getItem(position);
|
||||
|
||||
if(convertView == null) {
|
||||
LayoutInflater inflater = (LayoutInflater) mActivity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
||||
assert inflater != null;
|
||||
convertView = inflater.inflate(R.layout.connection_item, parent, false);
|
||||
}
|
||||
|
||||
assert conn != null;
|
||||
ImageView icon = convertView.findViewById(R.id.icon);
|
||||
TextView remote = convertView.findViewById(R.id.remote);
|
||||
TextView l7proto = convertView.findViewById(R.id.l7proto);
|
||||
TextView traffic = convertView.findViewById(R.id.traffic);
|
||||
AppDescriptor app = mActivity.findAppByUid(conn.uid);
|
||||
public void bindConn(MainActivity activity, ConnDescriptor conn, Drawable unknownIcon) {
|
||||
AppDescriptor app = activity.findAppByUid(conn.uid);
|
||||
Drawable appIcon;
|
||||
|
||||
appIcon = (app != null) ? Objects.requireNonNull(app.getIcon().getConstantState()).newDrawable() : mUnknownIcon;
|
||||
appIcon = (app != null) ? Objects.requireNonNull(app.getIcon().getConstantState()).newDrawable() : unknownIcon;
|
||||
icon.setImageDrawable(appIcon);
|
||||
|
||||
if(conn.info.length() > 0)
|
||||
remote.setText(conn.info);
|
||||
else
|
||||
remote.setText(String.format(mActivity.getResources().getString(R.string.ip_and_port),
|
||||
remote.setText(String.format(activity.getResources().getString(R.string.ip_and_port),
|
||||
conn.dst_ip, conn.dst_port));
|
||||
|
||||
l7proto.setText(conn.l7proto);
|
||||
traffic.setText(Utils.formatBytes(conn.sent_bytes + conn.rcvd_bytes));
|
||||
}
|
||||
}
|
||||
|
||||
return(convertView);
|
||||
public class ConnectionsAdapter extends RecyclerView.Adapter<ViewHolder> {
|
||||
private static final String TAG = "ConnectionsAdapter";
|
||||
private final MainActivity mActivity;
|
||||
private final Drawable mUnknownIcon;
|
||||
private View.OnClickListener mListener;
|
||||
|
||||
ConnectionsAdapter(MainActivity context) {
|
||||
mActivity = context;
|
||||
mUnknownIcon = ContextCompat.getDrawable(mActivity, android.R.drawable.ic_menu_help);
|
||||
mListener = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
public int getItemCount() {
|
||||
ConnectionsRegister reg = CaptureService.getConnsRegister();
|
||||
|
||||
return (reg != null) ? reg.getConnCount() : 0;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
LayoutInflater inflater = (LayoutInflater) mActivity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
||||
assert inflater != null;
|
||||
|
||||
View view = inflater.inflate(R.layout.connection_item, parent, false);
|
||||
|
||||
if(mListener != null)
|
||||
view.setOnClickListener(mListener);
|
||||
|
||||
return new ViewHolder(view);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
|
||||
ConnDescriptor conn = getItem(position);
|
||||
|
||||
if(conn == null)
|
||||
return;
|
||||
|
||||
holder.bindConn(mActivity, conn, mUnknownIcon);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getItemId(int pos) {
|
||||
ConnectionsRegister reg = CaptureService.getConnsRegister();
|
||||
|
||||
if((pos < 0) || (pos >= getCount()) || (reg == null))
|
||||
if((pos < 0) || (pos >= getItemCount()) || (reg == null))
|
||||
return -1;
|
||||
|
||||
ConnDescriptor conn = reg.getConn(pos);
|
||||
@ -103,14 +121,17 @@ public class ConnectionsAdapter extends BaseAdapter {
|
||||
return ((conn != null) ? conn.incr_id : -1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConnDescriptor getItem(int pos) {
|
||||
ConnectionsRegister reg = CaptureService.getConnsRegister();
|
||||
|
||||
/* Prevent indexOutOfBounds exception in updateView() */
|
||||
if((pos < 0) || (pos >= getCount()) || (reg == null))
|
||||
if((pos < 0) || (pos >= getItemCount()) || (reg == null))
|
||||
return null;
|
||||
|
||||
return reg.getConn(pos);
|
||||
}
|
||||
|
||||
public void setClickListener(View.OnClickListener listener) {
|
||||
mListener = listener;
|
||||
}
|
||||
}
|
||||
|
||||
@ -26,13 +26,12 @@ import android.os.Handler;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.ListView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
|
||||
public class ConnectionsFragment extends Fragment implements AppStateListener, ConnectionsListener {
|
||||
private static final String TAG = "ConnectionsFragment";
|
||||
@ -67,36 +66,37 @@ public class ConnectionsFragment extends Fragment implements AppStateListener, C
|
||||
|
||||
@Override
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
ListView connList = view.findViewById(R.id.connections_view);
|
||||
EmptyRecyclerView connList = view.findViewById(R.id.connections_view);
|
||||
connList.setLayoutManager(new LinearLayoutManager(mActivity));
|
||||
|
||||
TextView emptyText = view.findViewById(R.id.no_connections);
|
||||
connList.setEmptyView(emptyText);
|
||||
|
||||
mAdapter = new ConnectionsAdapter(mActivity);
|
||||
connList.setAdapter(mAdapter);
|
||||
connList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
|
||||
@Override
|
||||
public void onItemClick(AdapterView<?> adapterView, View view, int pos, long id) {
|
||||
ConnDescriptor item = (ConnDescriptor) adapterView.getItemAtPosition(pos);
|
||||
|
||||
if(item != null) {
|
||||
Intent intent = new Intent(getContext(), ConnectionDetails.class);
|
||||
AppDescriptor app = mActivity.findAppByUid(item.uid);
|
||||
String app_name = null;//;1051
|
||||
mAdapter.setClickListener(v -> {
|
||||
int pos = connList.getChildLayoutPosition(v);
|
||||
ConnDescriptor item = mAdapter.getItem(pos);
|
||||
|
||||
if(app != null)
|
||||
app_name = app.getName();
|
||||
else if(item.uid == 1000)
|
||||
app_name = "system";
|
||||
else if(item.uid == 1051)
|
||||
app_name = "netd";
|
||||
if(item != null) {
|
||||
Intent intent = new Intent(getContext(), ConnectionDetails.class);
|
||||
AppDescriptor app = mActivity.findAppByUid(item.uid);
|
||||
String app_name = null;//;1051
|
||||
|
||||
intent.putExtra(ConnectionDetails.CONN_EXTRA_KEY, item);
|
||||
if(app != null)
|
||||
app_name = app.getName();
|
||||
else if(item.uid == 1000)
|
||||
app_name = "system";
|
||||
else if(item.uid == 1051)
|
||||
app_name = "netd";
|
||||
|
||||
if(app_name != null)
|
||||
intent.putExtra(ConnectionDetails.APP_NAME_EXTRA_KEY, app_name);
|
||||
intent.putExtra(ConnectionDetails.CONN_EXTRA_KEY, item);
|
||||
|
||||
startActivity(intent);
|
||||
}
|
||||
if(app_name != null)
|
||||
intent.putExtra(ConnectionDetails.APP_NAME_EXTRA_KEY, app_name);
|
||||
|
||||
startActivity(intent);
|
||||
}
|
||||
});
|
||||
|
||||
@ -127,17 +127,12 @@ public class ConnectionsFragment extends Fragment implements AppStateListener, C
|
||||
|
||||
@Override
|
||||
public void appsLoaded() {
|
||||
// Refresh the adapter to load the apps
|
||||
// Refresh the adapter to load the apps icons
|
||||
mAdapter.notifyDataSetChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void connectionsChanges() {
|
||||
mHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
mAdapter.notifyDataSetChanged();
|
||||
}
|
||||
});
|
||||
mHandler.post(() -> mAdapter.notifyDataSetChanged());
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,73 @@
|
||||
package com.emanuelef.remote_capture;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
// Adapter from https://gist.github.com/AlexZhukovich/537eaa1e3c82ef9f5d5cd22efdc80c54#file-emptyrecyclerview-java
|
||||
public class EmptyRecyclerView extends RecyclerView {
|
||||
private View mEmptyView;
|
||||
|
||||
public EmptyRecyclerView(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
public EmptyRecyclerView(Context context, @Nullable AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
public EmptyRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) {
|
||||
super(context, attrs, defStyle);
|
||||
}
|
||||
|
||||
private void initEmptyView() {
|
||||
if (mEmptyView != null) {
|
||||
mEmptyView.setVisibility(
|
||||
getAdapter() == null || getAdapter().getItemCount() == 0 ? VISIBLE : GONE);
|
||||
EmptyRecyclerView.this.setVisibility(
|
||||
getAdapter() == null || getAdapter().getItemCount() == 0 ? GONE : VISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
final AdapterDataObserver observer = new AdapterDataObserver() {
|
||||
@Override
|
||||
public void onChanged() {
|
||||
super.onChanged();
|
||||
initEmptyView();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onItemRangeInserted(int positionStart, int itemCount) {
|
||||
super.onItemRangeInserted(positionStart, itemCount);
|
||||
initEmptyView();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onItemRangeRemoved(int positionStart, int itemCount) {
|
||||
super.onItemRangeRemoved(positionStart, itemCount);
|
||||
initEmptyView();
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public void setAdapter(Adapter adapter) {
|
||||
Adapter oldAdapter = getAdapter();
|
||||
super.setAdapter(adapter);
|
||||
|
||||
if (oldAdapter != null) {
|
||||
oldAdapter.unregisterAdapterDataObserver(observer);
|
||||
}
|
||||
|
||||
if (adapter != null) {
|
||||
adapter.registerAdapterDataObserver(observer);
|
||||
}
|
||||
}
|
||||
|
||||
public void setEmptyView(View view) {
|
||||
this.mEmptyView = view;
|
||||
initEmptyView();
|
||||
}
|
||||
}
|
||||
@ -1,6 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:background="?attr/selectableItemBackground"
|
||||
android:paddingHorizontal="5dp"
|
||||
android:layout_height="40dp">
|
||||
|
||||
|
||||
@ -4,11 +4,11 @@
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<ListView
|
||||
<com.emanuelef.remote_capture.EmptyRecyclerView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:id="@+id/connections_view"
|
||||
></ListView>
|
||||
></com.emanuelef.remote_capture.EmptyRecyclerView>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/no_connections"
|
||||
|
||||
Loading…
Reference in New Issue
Block a user