mirror of
https://github.com/emanuele-f/PCAPdroid.git
synced 2026-06-19 21:05:25 +08:00
Add HTTP scroll to request/reply button
This commit is contained in:
parent
64721ea64c
commit
00c4d08deb
@ -20,6 +20,7 @@
|
||||
package com.emanuelef.remote_capture.adapters;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
@ -28,6 +29,7 @@ import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.emanuelef.remote_capture.CaptureService;
|
||||
@ -38,6 +40,7 @@ import com.emanuelef.remote_capture.model.ConnectionDescriptor;
|
||||
import com.emanuelef.remote_capture.model.PayloadChunk;
|
||||
import com.emanuelef.remote_capture.model.PayloadChunk.ChunkType;
|
||||
import com.emanuelef.remote_capture.model.Prefs;
|
||||
import com.google.android.material.button.MaterialButton;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.text.SimpleDateFormat;
|
||||
@ -50,6 +53,7 @@ import java.util.Locale;
|
||||
* Since the text of a chunk can be very long (hundreds of KB) and rendering it would freeze the UI,
|
||||
* it is split into pages of VISUAL_PAGE_SIZE. */
|
||||
public class PayloadAdapter extends RecyclerView.Adapter<PayloadAdapter.PayloadViewHolder> implements HTTPReassembly.ReassemblyListener {
|
||||
private static final String TAG = "PayloadAdapter";
|
||||
public static final int COLLAPSE_CHUNK_SIZE = 1500;
|
||||
public static final int VISUAL_PAGE_SIZE = 4020; // must be a multiple of 67 to avoid splitting the hexdump
|
||||
private final LayoutInflater mLayoutInflater;
|
||||
@ -57,12 +61,15 @@ public class PayloadAdapter extends RecyclerView.Adapter<PayloadAdapter.PayloadV
|
||||
private final Context mContext;
|
||||
private final ChunkType mMode;
|
||||
private int mHandledChunks;
|
||||
private AdapterChunk mUnmatchedHttpReq = null;
|
||||
private final ArrayList<AdapterChunk> mChunks = new ArrayList<>();
|
||||
private final LinearLayoutManager mLinearLayout;
|
||||
private final HTTPReassembly mHttpReq;
|
||||
private final HTTPReassembly mHttpRes;
|
||||
|
||||
public PayloadAdapter(Context context, ConnectionDescriptor conn, ChunkType mode) {
|
||||
public PayloadAdapter(Context context, LinearLayoutManager linearLayout, ConnectionDescriptor conn, ChunkType mode) {
|
||||
mLayoutInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
||||
mLinearLayout = linearLayout;
|
||||
mConn = conn;
|
||||
mContext = context;
|
||||
mMode = mode;
|
||||
@ -83,6 +90,7 @@ public class PayloadAdapter extends RecyclerView.Adapter<PayloadAdapter.PayloadV
|
||||
private boolean mIsExpanded;
|
||||
private int mNumPages = 1;
|
||||
public final int originalPos;
|
||||
public AdapterChunk peer = null;
|
||||
|
||||
AdapterChunk(PayloadChunk _chunk, int pos) {
|
||||
mChunk = _chunk;
|
||||
@ -186,6 +194,7 @@ public class PayloadAdapter extends RecyclerView.Adapter<PayloadAdapter.PayloadV
|
||||
TextView header;
|
||||
TextView dump;
|
||||
ImageView expandButton;
|
||||
MaterialButton jumpToReply;
|
||||
|
||||
public PayloadViewHolder(View view) {
|
||||
super(view);
|
||||
@ -193,6 +202,7 @@ public class PayloadAdapter extends RecyclerView.Adapter<PayloadAdapter.PayloadV
|
||||
header = view.findViewById(R.id.header);
|
||||
dump = view.findViewById(R.id.dump);
|
||||
expandButton = view.findViewById(R.id.expand_button);
|
||||
jumpToReply = view.findViewById(R.id.jumpToReply);
|
||||
}
|
||||
}
|
||||
|
||||
@ -219,6 +229,16 @@ public class PayloadAdapter extends RecyclerView.Adapter<PayloadAdapter.PayloadV
|
||||
}
|
||||
});
|
||||
|
||||
holder.jumpToReply.setOnClickListener(v -> {
|
||||
int pos = holder.getAbsoluteAdapterPosition();
|
||||
Page page = getItem(pos);
|
||||
assert(page.adaptChunk.peer != null);
|
||||
|
||||
int jumpPos = getAdapterPosition(page.adaptChunk.peer);
|
||||
Log.d(TAG, "jump to " + jumpPos + " (orig_pos=" + page.adaptChunk.peer.originalPos + ")");
|
||||
mLinearLayout.scrollToPosition(jumpPos);
|
||||
});
|
||||
|
||||
return holder;
|
||||
}
|
||||
|
||||
@ -252,6 +272,8 @@ public class PayloadAdapter extends RecyclerView.Adapter<PayloadAdapter.PayloadV
|
||||
} else
|
||||
holder.expandButton.setVisibility(View.GONE);
|
||||
|
||||
holder.jumpToReply.setVisibility((page.adaptChunk.peer != null) ? View.VISIBLE : View.GONE);
|
||||
|
||||
holder.dump.setText(page.getText());
|
||||
|
||||
if(chunk.is_sent) {
|
||||
@ -298,6 +320,21 @@ public class PayloadAdapter extends RecyclerView.Adapter<PayloadAdapter.PayloadV
|
||||
return mChunks.get(i).getPage(pageIdx);
|
||||
}
|
||||
|
||||
private int getAdapterPosition(AdapterChunk chunk) {
|
||||
int i;
|
||||
int count = 0;
|
||||
|
||||
for(i=0; i < mChunks.size(); i++) {
|
||||
AdapterChunk aChunk = mChunks.get(i);
|
||||
if(aChunk == chunk)
|
||||
break;
|
||||
|
||||
count += aChunk.getNumPages();
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
public void handleChunksAdded(int tot_chunks) {
|
||||
for(int i = mHandledChunks; i<tot_chunks; i++) {
|
||||
PayloadChunk chunk = mConn.getPayloadChunk(i);
|
||||
@ -324,8 +361,29 @@ public class PayloadAdapter extends RecyclerView.Adapter<PayloadAdapter.PayloadV
|
||||
|
||||
@Override
|
||||
public void onChunkReassembled(PayloadChunk chunk) {
|
||||
AdapterChunk adapterChunk = new AdapterChunk(chunk, mChunks.size());
|
||||
|
||||
if(!chunk.is_sent && (mUnmatchedHttpReq != null)) {
|
||||
// HTTP reply
|
||||
adapterChunk.peer = mUnmatchedHttpReq;
|
||||
mUnmatchedHttpReq.peer = adapterChunk;
|
||||
notifyItemChanged(getAdapterPosition(mUnmatchedHttpReq));
|
||||
mUnmatchedHttpReq = null;
|
||||
|
||||
// Possibly find next un-replied HTTP request
|
||||
for(int i=adapterChunk.peer.originalPos + 1; i<mChunks.size(); i++) {
|
||||
AdapterChunk cur = mChunks.get(i);
|
||||
|
||||
if(cur.mChunk.is_sent) {
|
||||
mUnmatchedHttpReq = cur;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if((chunk.is_sent) && (mUnmatchedHttpReq == null))
|
||||
mUnmatchedHttpReq = adapterChunk;
|
||||
|
||||
int insert_pos = getItemCount();
|
||||
mChunks.add(new AdapterChunk(chunk, mChunks.size()));
|
||||
mChunks.add(adapterChunk);
|
||||
notifyItemInserted(insert_pos);
|
||||
}
|
||||
}
|
||||
|
||||
@ -91,7 +91,7 @@ public class ConnectionPayload extends Fragment implements ConnectionDetailsActi
|
||||
if(mConn.isPayloadTruncated())
|
||||
mTruncatedWarning.setVisibility(View.VISIBLE);
|
||||
|
||||
mAdapter = new PayloadAdapter(requireContext(), mConn, mode);
|
||||
mAdapter = new PayloadAdapter(requireContext(), layoutMan, mConn, mode);
|
||||
mCurChunks = mConn.getNumPayloadChunks();
|
||||
recyclerView.setAdapter(mAdapter);
|
||||
}
|
||||
|
||||
5
app/src/main/res/drawable/ic_subdirectory_arrow_left.xml
Normal file
5
app/src/main/res/drawable/ic_subdirectory_arrow_left.xml
Normal file
@ -0,0 +1,5 @@
|
||||
<vector android:height="18dp" android:tint="#FFFFFF"
|
||||
android:viewportHeight="24" android:viewportWidth="24"
|
||||
android:width="18dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="@android:color/white" android:pathData="M11,9l1.42,1.42L8.83,14H18V4h2v12H8.83l3.59,3.58L11,21l-6,-6 6,-6z"/>
|
||||
</vector>
|
||||
@ -3,19 +3,43 @@
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:orientation="vertical"
|
||||
android:paddingHorizontal="3sp"
|
||||
android:paddingVertical="10dp">
|
||||
android:paddingVertical="8dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/header"
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="monospace"
|
||||
android:textSize="11sp"
|
||||
android:layout_marginBottom="12dp"
|
||||
android:textStyle="bold"
|
||||
tools:text="#1 [TX] 11:02:03.154 — 120 B" />
|
||||
android:gravity="center_vertical"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/header"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:fontFamily="monospace"
|
||||
android:textSize="11sp"
|
||||
android:textStyle="bold"
|
||||
tools:text="#1 [TX] 11:02:03.154 — 120 B" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/jumpToReply"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
style="@style/Widget.MaterialComponents.Button.OutlinedButton"
|
||||
android:insetTop="0dp"
|
||||
android:insetBottom="0dp"
|
||||
android:minWidth="0dp"
|
||||
android:minHeight="0dp"
|
||||
android:paddingVertical="0dp"
|
||||
android:paddingHorizontal="0dp"
|
||||
android:padding="4dp"
|
||||
app:icon="@drawable/ic_subdirectory_arrow_left"
|
||||
app:iconTint="@color/colorTabText" />
|
||||
</LinearLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/dump"
|
||||
|
||||
Loading…
Reference in New Issue
Block a user