mirror of
https://github.com/emanuele-f/PCAPdroid.git
synced 2026-06-16 21:10:57 +08:00
Rework app settings in status view quick settings
This commit is contained in:
parent
94b93f3175
commit
6f009ce19a
@ -137,8 +137,8 @@ public class CaptureService extends VpnService implements Runnable {
|
||||
public_dns = Utils.getDnsServer(app_ctx);
|
||||
vpn_dns = VPN_VIRTUAL_DNS_SERVER;
|
||||
vpn_ipv4 = VPN_IP_ADDRESS;
|
||||
app_filter = settings.getString(Prefs.PREF_APP_FILTER);
|
||||
|
||||
app_filter = Prefs.getAppFilter(prefs);
|
||||
collector_address = Prefs.getCollectorIp(prefs);
|
||||
collector_port = Prefs.getCollectorPort(prefs);
|
||||
http_server_port = Prefs.getHttpServerPort(prefs);
|
||||
@ -197,7 +197,7 @@ public class CaptureService extends VpnService implements Runnable {
|
||||
.addRoute("128.0.0.0", 1)
|
||||
.addDnsServer(vpn_dns);
|
||||
|
||||
if(app_filter != null) {
|
||||
if((app_filter != null) && (!app_filter.isEmpty())) {
|
||||
Log.d(TAG, "Setting app filter: " + app_filter);
|
||||
|
||||
try {
|
||||
|
||||
@ -20,8 +20,15 @@
|
||||
package com.emanuelef.remote_capture;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.Dialog;
|
||||
import android.app.Service;
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.graphics.drawable.ScaleDrawable;
|
||||
import android.net.ConnectivityManager;
|
||||
import android.net.LinkProperties;
|
||||
import android.net.Network;
|
||||
@ -30,6 +37,7 @@ import android.net.wifi.WifiInfo;
|
||||
import android.net.wifi.WifiManager;
|
||||
import android.os.Build;
|
||||
import android.util.Log;
|
||||
import android.view.Gravity;
|
||||
import android.view.View;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
@ -249,7 +257,7 @@ public class Utils {
|
||||
Toast.makeText(context, msg, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
|
||||
public static void showAppSelectionDialog(Activity activity, List<AppDescriptor> appsData, AppsListView.OnSelectedAppListener listener) {
|
||||
public static Dialog getAppSelectionDialog(Activity activity, List<AppDescriptor> appsData, AppsListView.OnSelectedAppListener listener) {
|
||||
View dialogLayout = activity.getLayoutInflater().inflate(R.layout.apps_selector, null);
|
||||
SearchView searchView = dialogLayout.findViewById(R.id.apps_search);
|
||||
AppsListView apps = dialogLayout.findViewById(R.id.apps_list);
|
||||
@ -264,15 +272,16 @@ public class Utils {
|
||||
builder.setView(dialogLayout);
|
||||
|
||||
final AlertDialog alert = builder.create();
|
||||
alert.setCanceledOnTouchOutside(true);
|
||||
|
||||
apps.setSelectedAppListener(app -> {
|
||||
listener.onSelectedApp(app);
|
||||
|
||||
// dismiss the dialog
|
||||
alert.cancel();
|
||||
alert.dismiss();
|
||||
});
|
||||
|
||||
alert.show();
|
||||
return alert;
|
||||
}
|
||||
|
||||
public static String getUniquePcapFileName(Context context) {
|
||||
@ -280,4 +289,14 @@ public class Utils {
|
||||
final DateFormat fmt = new SimpleDateFormat("dd_MMM_HH_mm_ss", locale);
|
||||
return "PCAPdroid_" + fmt.format(new Date()) + ".pcap";
|
||||
}
|
||||
|
||||
public static Drawable scaleDrawable(Resources res, Drawable drawable, int new_x, int new_y) {
|
||||
Bitmap bitmap = Bitmap.createBitmap(new_x, new_y, Bitmap.Config.ARGB_8888);
|
||||
Canvas canvas = new Canvas(bitmap);
|
||||
|
||||
drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
|
||||
drawable.draw(canvas);
|
||||
|
||||
return new BitmapDrawable(res, bitmap);
|
||||
}
|
||||
}
|
||||
|
||||
@ -27,7 +27,6 @@ import android.content.SharedPreferences;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.database.Cursor;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.net.Uri;
|
||||
import android.net.VpnService;
|
||||
|
||||
@ -52,10 +51,7 @@ import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.emanuelef.remote_capture.AppsLoader;
|
||||
import com.emanuelef.remote_capture.interfaces.AppStateListener;
|
||||
import com.emanuelef.remote_capture.interfaces.AppsLoadListener;
|
||||
import com.emanuelef.remote_capture.model.AppDescriptor;
|
||||
import com.emanuelef.remote_capture.model.AppState;
|
||||
import com.emanuelef.remote_capture.CaptureService;
|
||||
import com.emanuelef.remote_capture.model.Prefs;
|
||||
@ -64,29 +60,19 @@ import com.emanuelef.remote_capture.Utils;
|
||||
import com.google.android.material.navigation.NavigationView;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
import cat.ereza.customactivityoncrash.config.CaocConfig;
|
||||
|
||||
public class MainActivity extends AppCompatActivity implements AppsLoadListener, NavigationView.OnNavigationItemSelectedListener {
|
||||
public class MainActivity extends AppCompatActivity implements NavigationView.OnNavigationItemSelectedListener {
|
||||
private SharedPreferences mPrefs;
|
||||
private Menu mMenu;
|
||||
private MenuItem mMenuItemStartBtn;
|
||||
private MenuItem mMenuItemAppSel;
|
||||
private MenuItem mMenuSettings;
|
||||
private Drawable mFilterIcon;
|
||||
private String mFilterApp;
|
||||
private boolean mOpenAppsWhenDone;
|
||||
private List<AppDescriptor> mInstalledApps;
|
||||
private AppState mState;
|
||||
private AppStateListener mListener;
|
||||
private AppDescriptor mNoFilterApp;
|
||||
private Uri mPcapUri;
|
||||
private BroadcastReceiver mReceiver;
|
||||
private String mPcapFname;
|
||||
|
||||
private static final String TAG = "Main";
|
||||
|
||||
@ -101,19 +87,8 @@ public class MainActivity extends AppCompatActivity implements AppsLoadListener,
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
Drawable icon = ContextCompat.getDrawable(this, android.R.color.transparent);
|
||||
mNoFilterApp = new AppDescriptor("", icon, this.getResources().getString(R.string.no_filter), -1, false, true);
|
||||
|
||||
mFilterApp = CaptureService.getAppFilter();
|
||||
mPcapUri = CaptureService.getPcapUri();
|
||||
|
||||
if((mFilterApp == null) && (savedInstanceState != null)) {
|
||||
// Possibly get the temporary filter
|
||||
mFilterApp = savedInstanceState.getString("FilterApp");
|
||||
}
|
||||
|
||||
mOpenAppsWhenDone = false;
|
||||
|
||||
CaocConfig.Builder.create()
|
||||
.errorDrawable(R.drawable.ic_app_crash)
|
||||
.apply();
|
||||
@ -122,10 +97,6 @@ public class MainActivity extends AppCompatActivity implements AppsLoadListener,
|
||||
|
||||
mPrefs = PreferenceManager.getDefaultSharedPreferences(this);
|
||||
|
||||
(new AppsLoader(this))
|
||||
.setAppsLoadListener(this)
|
||||
.loadAllApps();
|
||||
|
||||
/* Register for service status */
|
||||
mReceiver = new BroadcastReceiver() {
|
||||
@Override
|
||||
@ -143,6 +114,7 @@ public class MainActivity extends AppCompatActivity implements AppsLoadListener,
|
||||
if((mPcapUri != null) && (Prefs.getDumpMode(mPrefs) == Prefs.DumpMode.PCAP_FILE)) {
|
||||
showPcapActionDialog(mPcapUri);
|
||||
mPcapUri = null;
|
||||
mPcapFname = null;
|
||||
}
|
||||
|
||||
appStateReady();
|
||||
@ -200,13 +172,6 @@ public class MainActivity extends AppCompatActivity implements AppsLoadListener,
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSaveInstanceState(@NonNull Bundle savedInstanceState) {
|
||||
super.onSaveInstanceState(savedInstanceState);
|
||||
|
||||
savedInstanceState.putString("FilterApp", mFilterApp);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
@ -215,38 +180,6 @@ public class MainActivity extends AppCompatActivity implements AppsLoadListener,
|
||||
initAppState();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAppsInfoLoaded(Map<Integer, AppDescriptor> apps) {
|
||||
// TODO optimize: show the apps even when icon not loaded
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAppsIconsLoaded(Map<Integer, AppDescriptor> apps) {
|
||||
mInstalledApps = new ArrayList<>();
|
||||
AppDescriptor filterApp = null;
|
||||
|
||||
for (Map.Entry<Integer, AppDescriptor> pair : apps.entrySet()) {
|
||||
AppDescriptor app = pair.getValue();
|
||||
|
||||
if(!app.isVirtual()) {
|
||||
mInstalledApps.add(app);
|
||||
|
||||
if (app.getPackageName().equals(mFilterApp))
|
||||
filterApp = app;
|
||||
}
|
||||
}
|
||||
|
||||
Collections.sort(mInstalledApps);
|
||||
|
||||
if (filterApp != null) {
|
||||
/* An filter is active, try to set the corresponding app image */
|
||||
setSelectedApp(filterApp);
|
||||
}
|
||||
|
||||
if (mOpenAppsWhenDone)
|
||||
openAppSelector();
|
||||
}
|
||||
|
||||
public boolean onNavigationItemSelected(@NonNull MenuItem item) {
|
||||
int id = item.getItemId();
|
||||
|
||||
@ -300,7 +233,6 @@ public class MainActivity extends AppCompatActivity implements AppsLoadListener,
|
||||
ContextCompat.getDrawable(this, android.R.drawable.ic_media_play));
|
||||
mMenuItemStartBtn.setTitle(R.string.start_button);
|
||||
mMenuItemStartBtn.setEnabled(true);
|
||||
mMenuItemAppSel.setEnabled(true);
|
||||
mMenuSettings.setEnabled(true);
|
||||
}
|
||||
|
||||
@ -310,7 +242,6 @@ public class MainActivity extends AppCompatActivity implements AppsLoadListener,
|
||||
|
||||
mMenuItemStartBtn.setEnabled(false);
|
||||
mMenuSettings.setEnabled(false);
|
||||
mMenuItemAppSel.setEnabled(false);
|
||||
}
|
||||
|
||||
public void appStateRunning() {
|
||||
@ -322,7 +253,6 @@ public class MainActivity extends AppCompatActivity implements AppsLoadListener,
|
||||
mMenuItemStartBtn.setTitle(R.string.stop_button);
|
||||
mMenuItemStartBtn.setEnabled(true);
|
||||
mMenuSettings.setEnabled(false);
|
||||
mMenuItemAppSel.setEnabled(false);
|
||||
}
|
||||
|
||||
public void appStateStopping() {
|
||||
@ -339,11 +269,7 @@ public class MainActivity extends AppCompatActivity implements AppsLoadListener,
|
||||
|
||||
mMenu = menu;
|
||||
mMenuItemStartBtn = mMenu.findItem(R.id.action_start);
|
||||
mMenuItemAppSel = mMenu.findItem(R.id.action_show_app_filter);
|
||||
mMenuSettings = mMenu.findItem(R.id.action_settings);
|
||||
|
||||
mFilterIcon = mMenuItemAppSel.getIcon();
|
||||
|
||||
initAppState();
|
||||
|
||||
return true;
|
||||
@ -386,13 +312,6 @@ public class MainActivity extends AppCompatActivity implements AppsLoadListener,
|
||||
Intent intent = new Intent(MainActivity.this, SettingsActivity.class);
|
||||
startActivity(intent);
|
||||
return true;
|
||||
} else if (id == R.id.action_show_app_filter) {
|
||||
if(mFilterApp != null)
|
||||
setSelectedApp(null);
|
||||
else
|
||||
openAppSelector();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return super.onOptionsItemSelected(item);
|
||||
@ -410,7 +329,6 @@ public class MainActivity extends AppCompatActivity implements AppsLoadListener,
|
||||
if((mPcapUri != null) && (Prefs.getDumpMode(mPrefs) == Prefs.DumpMode.PCAP_FILE))
|
||||
bundle.putString(Prefs.PREF_PCAP_URI, mPcapUri.toString());
|
||||
|
||||
bundle.putString(Prefs.PREF_APP_FILTER, mFilterApp);
|
||||
intent.putExtra("settings", bundle);
|
||||
|
||||
Log.d(TAG, "onActivityResult -> start CaptureService");
|
||||
@ -426,6 +344,7 @@ public class MainActivity extends AppCompatActivity implements AppsLoadListener,
|
||||
} else if(requestCode == REQUEST_CODE_PCAP_FILE) {
|
||||
if(resultCode == RESULT_OK) {
|
||||
mPcapUri = data.getData();
|
||||
mPcapFname = null;
|
||||
Log.d(TAG, "PCAP to write: " + mPcapUri.toString());
|
||||
|
||||
toggleService();
|
||||
@ -496,11 +415,11 @@ public class MainActivity extends AppCompatActivity implements AppsLoadListener,
|
||||
if((cursor == null) || !cursor.moveToFirst())
|
||||
return;
|
||||
|
||||
// If file is empty, delete it
|
||||
long file_size = cursor.getLong(cursor.getColumnIndex(OpenableColumns.SIZE));
|
||||
String fname = cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME));
|
||||
cursor.close();
|
||||
|
||||
// If file is empty, delete it
|
||||
if(file_size == 0) {
|
||||
Log.d(TAG, "PCAP file is empty, deleting");
|
||||
|
||||
@ -550,40 +469,29 @@ public class MainActivity extends AppCompatActivity implements AppsLoadListener,
|
||||
return(mState);
|
||||
}
|
||||
|
||||
public void setSelectedApp(AppDescriptor app) {
|
||||
if(app == null)
|
||||
app = mNoFilterApp;
|
||||
public String getPcapFname() {
|
||||
if((mState == AppState.running) && (mPcapUri != null)) {
|
||||
if(mPcapFname != null)
|
||||
return mPcapFname;
|
||||
|
||||
Log.d(TAG, "Selected app: " + app.getUid());
|
||||
Cursor cursor;
|
||||
|
||||
if(app.getUid() != -1) {
|
||||
// an app has been selected
|
||||
mFilterApp = app.getPackageName();
|
||||
|
||||
// clone the drawable to avoid a "zoom-in" effect when clicked
|
||||
Drawable drawable = (app.getIcon() != null) ? Objects.requireNonNull(app.getIcon().getConstantState()).newDrawable() : null;
|
||||
|
||||
if(drawable != null) {
|
||||
mMenuItemAppSel.setIcon(drawable);
|
||||
mMenuItemAppSel.setTitle(R.string.remove_app_filter);
|
||||
try {
|
||||
cursor = getContentResolver().query(mPcapUri, null, null, null, null);
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
// no filter
|
||||
mFilterApp = null;
|
||||
mMenuItemAppSel.setIcon(mFilterIcon);
|
||||
mMenuItemAppSel.setTitle(R.string.set_app_filter);
|
||||
}
|
||||
}
|
||||
|
||||
private void openAppSelector() {
|
||||
if(mInstalledApps == null) {
|
||||
/* The applications loader has not finished yet. */
|
||||
mOpenAppsWhenDone = true;
|
||||
Utils.showToast(this, R.string.apps_loading_please_wait);
|
||||
return;
|
||||
if((cursor == null) || !cursor.moveToFirst())
|
||||
return null;
|
||||
|
||||
String fname = cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME));
|
||||
cursor.close();
|
||||
|
||||
mPcapFname = fname;
|
||||
return fname;
|
||||
}
|
||||
|
||||
mOpenAppsWhenDone = false;
|
||||
Utils.showAppSelectionDialog(this, mInstalledApps, this::setSelectedApp);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@ -51,10 +51,6 @@ public class SettingsActivity extends AppCompatActivity {
|
||||
}
|
||||
|
||||
public static class SettingsFragment extends PreferenceFragmentCompat {
|
||||
private EditTextPreference mRemoteCollectorIp;
|
||||
private EditTextPreference mRemoteCollectorPort;
|
||||
private EditTextPreference mHttpServerPort;
|
||||
private ListPreference mDumpModePref;
|
||||
private SwitchPreference mTlsDecryptionEnabled;
|
||||
private EditTextPreference mTlsProxyIp;
|
||||
private EditTextPreference mTlsProxyPort;
|
||||
@ -63,18 +59,11 @@ public class SettingsActivity extends AppCompatActivity {
|
||||
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
|
||||
setPreferencesFromResource(R.xml.root_preferences, rootKey);
|
||||
|
||||
mDumpModePref = findPreference(Prefs.PREF_PCAP_DUMP_MODE);
|
||||
mDumpModePref.setOnPreferenceChangeListener((preference, newValue) -> {
|
||||
dumpPrefsHideShow((String) newValue);
|
||||
return true;
|
||||
});
|
||||
|
||||
setupUdpExporterPrefs();
|
||||
setupHttpServerPrefs();
|
||||
setupTlsProxyPrefs();
|
||||
|
||||
tlsDecryptionHideShow(mTlsDecryptionEnabled.isChecked());
|
||||
dumpPrefsHideShow(mDumpModePref.getValue());
|
||||
}
|
||||
|
||||
private boolean validatePort(String value) {
|
||||
@ -88,21 +77,21 @@ public class SettingsActivity extends AppCompatActivity {
|
||||
|
||||
private void setupUdpExporterPrefs() {
|
||||
/* Collector IP validation */
|
||||
mRemoteCollectorIp = findPreference(Prefs.PREF_COLLECTOR_IP_KEY);
|
||||
EditTextPreference mRemoteCollectorIp = findPreference(Prefs.PREF_COLLECTOR_IP_KEY);
|
||||
mRemoteCollectorIp.setOnPreferenceChangeListener((preference, newValue) -> {
|
||||
Matcher matcher = Patterns.IP_ADDRESS.matcher(newValue.toString());
|
||||
return(matcher.matches());
|
||||
});
|
||||
|
||||
/* Collector port validation */
|
||||
mRemoteCollectorPort = findPreference(Prefs.PREF_COLLECTOR_PORT_KEY);
|
||||
EditTextPreference mRemoteCollectorPort = findPreference(Prefs.PREF_COLLECTOR_PORT_KEY);
|
||||
mRemoteCollectorPort.setOnBindEditTextListener(editText -> editText.setInputType(InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_FLAG_SIGNED));
|
||||
mRemoteCollectorPort.setOnPreferenceChangeListener((preference, newValue) -> validatePort(newValue.toString()));
|
||||
}
|
||||
|
||||
private void setupHttpServerPrefs() {
|
||||
/* HTTP Server port validation */
|
||||
mHttpServerPort = findPreference(Prefs.PREF_HTTP_SERVER_PORT);
|
||||
EditTextPreference mHttpServerPort = findPreference(Prefs.PREF_HTTP_SERVER_PORT);
|
||||
mHttpServerPort.setOnPreferenceChangeListener((preference, newValue) -> validatePort(newValue.toString()));
|
||||
}
|
||||
|
||||
@ -126,41 +115,6 @@ public class SettingsActivity extends AppCompatActivity {
|
||||
mTlsProxyPort.setOnPreferenceChangeListener((preference, newValue) -> validatePort(newValue.toString()));
|
||||
}
|
||||
|
||||
/* This implements a radio-button like behaviour */
|
||||
private void dumpPrefsHideShow(String dumpMode) {
|
||||
Prefs.DumpMode mode = Prefs.getDumpMode(dumpMode);
|
||||
boolean show_http_prefs = (mode == Prefs.DumpMode.HTTP_SERVER);
|
||||
boolean show_udp_prefs = (mode == Prefs.DumpMode.UDP_EXPORTER);
|
||||
|
||||
/* HTTP Server */
|
||||
mHttpServerPort.setVisible(show_http_prefs);
|
||||
|
||||
/* UDP Receiver */
|
||||
mRemoteCollectorIp.setVisible(show_udp_prefs);
|
||||
mRemoteCollectorPort.setVisible(show_udp_prefs);
|
||||
|
||||
/* Adjust label */
|
||||
int summary_id;
|
||||
|
||||
switch(mode) {
|
||||
case HTTP_SERVER:
|
||||
summary_id = R.string.http_server_info;
|
||||
break;
|
||||
case PCAP_FILE:
|
||||
summary_id = R.string.pcap_file_info;
|
||||
break;
|
||||
case UDP_EXPORTER:
|
||||
summary_id = R.string.udp_exporter_info;
|
||||
break;
|
||||
case NONE:
|
||||
default:
|
||||
summary_id = R.string.no_dump_info;
|
||||
break;
|
||||
}
|
||||
|
||||
mDumpModePref.setSummary(summary_id);
|
||||
}
|
||||
|
||||
private void tlsDecryptionHideShow(boolean decryptionEnabled) {
|
||||
mTlsProxyIp.setVisible(decryptionEnabled);
|
||||
mTlsProxyPort.setVisible(decryptionEnabled);
|
||||
|
||||
@ -76,8 +76,10 @@ public class AppsAdapter extends RecyclerView.Adapter<AppsAdapter.AppViewHolder>
|
||||
AppDescriptor app = getItem(position);
|
||||
|
||||
holder.textInListView.setText(app.getName());
|
||||
holder.imageInListView.setImageDrawable(app.getIcon());
|
||||
holder.packageInListView.setText(app.getPackageName());
|
||||
|
||||
if(app.getIcon() != null)
|
||||
holder.imageInListView.setImageDrawable(app.getIcon());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -0,0 +1,84 @@
|
||||
package com.emanuelef.remote_capture.adapters;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.BaseAdapter;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.emanuelef.remote_capture.R;
|
||||
|
||||
public class DumpModesAdapter extends BaseAdapter {
|
||||
private final Context mContext;
|
||||
private final LayoutInflater mInflater;
|
||||
private final DumpModeInfo[] mModes;
|
||||
|
||||
public class DumpModeInfo {
|
||||
public final String key;
|
||||
public final String label;
|
||||
public final String description;
|
||||
|
||||
public DumpModeInfo(String _key, String _label, String _descr) {
|
||||
key = _key;
|
||||
label = _label;
|
||||
description = _descr;
|
||||
}
|
||||
}
|
||||
|
||||
public DumpModesAdapter(Context context) {
|
||||
mContext = context;
|
||||
mInflater = LayoutInflater.from(mContext);
|
||||
|
||||
String[] keys = mContext.getResources().getStringArray(R.array.pcap_dump_modes);
|
||||
String[] labels = mContext.getResources().getStringArray(R.array.pcap_dump_modes_labels);
|
||||
String[] descriptions = mContext.getResources().getStringArray(R.array.pcap_dump_modes_descriptions);
|
||||
|
||||
assert ((keys.length == labels.length) && (keys.length == descriptions.length));
|
||||
mModes = new DumpModeInfo[keys.length];
|
||||
|
||||
for(int i=0; i<keys.length; i++) {
|
||||
mModes[i] = new DumpModeInfo(keys[i], labels[i], descriptions[i]);
|
||||
}
|
||||
}
|
||||
|
||||
public int getModePos(String key) {
|
||||
for(int i=0; i<mModes.length; i++) {
|
||||
if(key.equals(mModes[i].key))
|
||||
return i;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
return mModes.length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getItem(int position) {
|
||||
return mModes[position];
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getItemId(int position) {
|
||||
return position;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(int position, View convertView, ViewGroup parent) {
|
||||
if(convertView == null)
|
||||
convertView = mInflater.inflate(R.layout.quick_settings_item, parent, false);
|
||||
|
||||
DumpModeInfo mode = (DumpModeInfo) getItem(position);
|
||||
|
||||
TextView title =convertView.findViewById(R.id.title);
|
||||
TextView description =convertView.findViewById(R.id.description);
|
||||
|
||||
title.setText(mode.label);
|
||||
description.setText(mode.description);
|
||||
|
||||
return convertView;
|
||||
}
|
||||
}
|
||||
@ -413,7 +413,7 @@ public class ConnectionsFragment extends Fragment implements ConnectionsListener
|
||||
|
||||
Collections.sort(appsData);
|
||||
|
||||
Utils.showAppSelectionDialog(getActivity(), appsData, app -> setUidFilter(app.getUid()));
|
||||
Utils.getAppSelectionDialog(getActivity(), appsData, app -> setUidFilter(app.getUid())).show();
|
||||
}
|
||||
|
||||
private void setUidFilter(int uid) {
|
||||
|
||||
@ -20,6 +20,7 @@
|
||||
package com.emanuelef.remote_capture.fragments;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.Dialog;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
@ -33,33 +34,54 @@ import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.Spinner;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.widget.SwitchCompat;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
|
||||
import androidx.preference.PreferenceManager;
|
||||
|
||||
import com.emanuelef.remote_capture.AppsLoader;
|
||||
import com.emanuelef.remote_capture.activities.InspectorActivity;
|
||||
import com.emanuelef.remote_capture.adapters.DumpModesAdapter;
|
||||
import com.emanuelef.remote_capture.interfaces.AppsLoadListener;
|
||||
import com.emanuelef.remote_capture.model.AppDescriptor;
|
||||
import com.emanuelef.remote_capture.model.AppState;
|
||||
import com.emanuelef.remote_capture.CaptureService;
|
||||
import com.emanuelef.remote_capture.model.Prefs;
|
||||
import com.emanuelef.remote_capture.R;
|
||||
import com.emanuelef.remote_capture.Utils;
|
||||
import com.emanuelef.remote_capture.activities.MainActivity;
|
||||
import com.emanuelef.remote_capture.activities.StatsActivity;
|
||||
import com.emanuelef.remote_capture.interfaces.AppStateListener;
|
||||
import com.emanuelef.remote_capture.model.Prefs;
|
||||
import com.emanuelef.remote_capture.model.VPNStats;
|
||||
import com.emanuelef.remote_capture.views.AppsListView;
|
||||
|
||||
public class StatusFragment extends Fragment implements AppStateListener {
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class StatusFragment extends Fragment implements AppStateListener, AppsLoadListener {
|
||||
private static final String TAG = "StatusFragment";
|
||||
private TextView mCollectorInfo;
|
||||
private TextView mCaptureStatus;
|
||||
private TextView mInspectorLink;
|
||||
private View mQuickSettings;
|
||||
private MainActivity mActivity;
|
||||
private SharedPreferences mPrefs;
|
||||
private BroadcastReceiver mReceiver;
|
||||
private TextView mFilterDescription;
|
||||
private SwitchCompat mAppFilterSwitch;
|
||||
private String mAppFilter;
|
||||
private boolean mOpenAppsWhenDone;
|
||||
private List<AppDescriptor> mInstalledApps;
|
||||
AppsListView mOpenAppsList;
|
||||
|
||||
@Override
|
||||
public void onAttach(@NonNull Context context) {
|
||||
@ -87,7 +109,47 @@ public class StatusFragment extends Fragment implements AppStateListener {
|
||||
mCollectorInfo = view.findViewById(R.id.collector_info);
|
||||
mCaptureStatus = view.findViewById(R.id.status_view);
|
||||
mInspectorLink = view.findViewById(R.id.inspector_link);
|
||||
mQuickSettings = view.findViewById(R.id.quick_settings);
|
||||
mPrefs = PreferenceManager.getDefaultSharedPreferences(mActivity);
|
||||
mAppFilter = Prefs.getAppFilter(mPrefs);
|
||||
mOpenAppsWhenDone = false;
|
||||
|
||||
DumpModesAdapter dumpModeAdapter = new DumpModesAdapter(getContext());
|
||||
Spinner dumpMode = view.findViewById(R.id.dump_mode_spinner);
|
||||
dumpMode.setAdapter(dumpModeAdapter);
|
||||
int curSel = dumpModeAdapter.getModePos(mPrefs.getString(Prefs.PREF_PCAP_DUMP_MODE, Prefs.DEFAULT_DUMP_MODE));
|
||||
dumpMode.setSelection(curSel);
|
||||
|
||||
dumpMode.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
|
||||
@Override
|
||||
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
|
||||
DumpModesAdapter.DumpModeInfo mode = (DumpModesAdapter.DumpModeInfo) dumpModeAdapter.getItem(position);
|
||||
SharedPreferences.Editor editor = mPrefs.edit();
|
||||
|
||||
editor.putString(Prefs.PREF_PCAP_DUMP_MODE, mode.key);
|
||||
editor.apply();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNothingSelected(AdapterView<?> parent) {}
|
||||
});
|
||||
|
||||
mAppFilterSwitch = view.findViewById(R.id.app_filter_switch);
|
||||
View filterRow = view.findViewById(R.id.app_filter_text);
|
||||
TextView filterTitle = filterRow.findViewById(R.id.title);
|
||||
mFilterDescription = filterRow.findViewById(R.id.description);
|
||||
|
||||
filterTitle.setText(R.string.app_filter);
|
||||
|
||||
mAppFilterSwitch.setOnCheckedChangeListener((buttonView, isChecked) -> {
|
||||
if(isChecked) {
|
||||
if((mAppFilter == null) || (mAppFilter.isEmpty()))
|
||||
openAppFilterSelector();
|
||||
} else
|
||||
setAppFilter(null);
|
||||
});
|
||||
|
||||
refreshFilterInfo();
|
||||
|
||||
mCaptureStatus.setOnClickListener(v -> {
|
||||
if(mActivity.getState() == AppState.running) {
|
||||
@ -101,11 +163,6 @@ public class StatusFragment extends Fragment implements AppStateListener {
|
||||
|
||||
setupInspectorLinK();
|
||||
|
||||
mPrefs.registerOnSharedPreferenceChangeListener((sharedPreferences, s) -> {
|
||||
if((mActivity != null) && (mActivity.getState() == AppState.ready))
|
||||
refreshPcapDumpInfo();
|
||||
});
|
||||
|
||||
/* Register for stats update */
|
||||
mReceiver = new BroadcastReceiver() {
|
||||
@Override
|
||||
@ -119,6 +176,10 @@ public class StatusFragment extends Fragment implements AppStateListener {
|
||||
|
||||
/* Important: call this after all the fields have been initialized */
|
||||
mActivity.setAppStateListener(this);
|
||||
|
||||
(new AppsLoader(mActivity))
|
||||
.setAppsLoadListener(this)
|
||||
.loadAllApps();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -131,6 +192,59 @@ public class StatusFragment extends Fragment implements AppStateListener {
|
||||
}
|
||||
}
|
||||
|
||||
private AppDescriptor findAppByPackage(String pkgname) {
|
||||
if(mInstalledApps == null)
|
||||
return null;
|
||||
|
||||
for(AppDescriptor app : mInstalledApps) {
|
||||
if(pkgname.equals(app.getPackageName()))
|
||||
return app;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private void refreshFilterInfo() {
|
||||
if((mAppFilter == null) || (mAppFilter.isEmpty())) {
|
||||
mFilterDescription.setText(R.string.no_app_filter);
|
||||
mFilterDescription.setCompoundDrawablesWithIntrinsicBounds(null, null, null, null);
|
||||
mAppFilterSwitch.setChecked(false);
|
||||
return;
|
||||
}
|
||||
|
||||
mAppFilterSwitch.setChecked(true);
|
||||
|
||||
AppDescriptor app = findAppByPackage(mAppFilter);
|
||||
String description;
|
||||
|
||||
if(app == null)
|
||||
description = mAppFilter;
|
||||
else {
|
||||
description = app.getName() + " (" + app.getPackageName() + ")";
|
||||
|
||||
if(app.getIcon() != null) {
|
||||
int height = mFilterDescription.getMeasuredHeight();
|
||||
Drawable drawable = Utils.scaleDrawable(getResources(), app.getIcon(), height, height);
|
||||
|
||||
if(drawable != null)
|
||||
mFilterDescription.setCompoundDrawablesWithIntrinsicBounds(drawable, null, null, null);
|
||||
else
|
||||
mFilterDescription.setCompoundDrawablesWithIntrinsicBounds(null, null, null, null);
|
||||
}
|
||||
}
|
||||
|
||||
mFilterDescription.setText(description);
|
||||
}
|
||||
|
||||
private void setAppFilter(AppDescriptor filter) {
|
||||
SharedPreferences.Editor editor = mPrefs.edit();
|
||||
mAppFilter = (filter != null) ? filter.getPackageName() : "";
|
||||
|
||||
editor.putString(Prefs.PREF_APP_FILTER, mAppFilter);
|
||||
editor.apply();
|
||||
refreshFilterInfo();
|
||||
}
|
||||
|
||||
private void setupInspectorLinK() {
|
||||
int color = ContextCompat.getColor(mActivity, android.R.color.tab_indicator_text);
|
||||
|
||||
@ -153,41 +267,53 @@ public class StatusFragment extends Fragment implements AppStateListener {
|
||||
mCaptureStatus.setText(Utils.formatBytes(stats.bytes_sent + stats.bytes_rcvd));
|
||||
}
|
||||
|
||||
private void refreshPcapDumpInfo() {
|
||||
String info;
|
||||
String modeName;
|
||||
private void refreshPcapDumpInfo() {
|
||||
String info = "";
|
||||
|
||||
Prefs.DumpMode mode = CaptureService.isServiceActive() ? CaptureService.getDumpMode() : Prefs.getDumpMode(mPrefs);
|
||||
Prefs.DumpMode mode = CaptureService.getDumpMode();
|
||||
|
||||
switch (mode) {
|
||||
case NONE:
|
||||
info = getString(R.string.no_dump_info);
|
||||
break;
|
||||
case HTTP_SERVER:
|
||||
modeName = getResources().getString(R.string.http_server);
|
||||
info = String.format(getResources().getString(R.string.http_server_status),
|
||||
Utils.getLocalIPAddress(mActivity), CaptureService.getHTTPServerPort());
|
||||
break;
|
||||
case PCAP_FILE:
|
||||
modeName = getResources().getString(R.string.pcap_file);
|
||||
info = "";
|
||||
info = getString(R.string.pcap_file_info);
|
||||
|
||||
if(mActivity != null) {
|
||||
String fname = mActivity.getPcapFname();
|
||||
|
||||
if(fname != null)
|
||||
info = fname;
|
||||
}
|
||||
break;
|
||||
case UDP_EXPORTER:
|
||||
modeName = getResources().getString(R.string.udp_exporter);
|
||||
info = String.format(getResources().getString(R.string.collector_info),
|
||||
CaptureService.getCollectorAddress(), CaptureService.getCollectorPort());
|
||||
break;
|
||||
default:
|
||||
modeName = getResources().getString(R.string.no_dump);
|
||||
info = "";
|
||||
break;
|
||||
}
|
||||
|
||||
if(!CaptureService.isServiceActive()) {
|
||||
info = getResources().getString(R.string.dump_mode) + ": " + modeName;
|
||||
|
||||
if(Prefs.getTlsDecryptionEnabled(mPrefs))
|
||||
info += " (" + getResources().getString(R.string.with_tls_decryption) + ")";
|
||||
}
|
||||
|
||||
mCollectorInfo.setText(info);
|
||||
|
||||
// Check if a filter is set
|
||||
if(mAppFilter != null) {
|
||||
AppDescriptor app = findAppByPackage(mAppFilter);
|
||||
|
||||
if((app != null) && (app.getIcon() != null)) {
|
||||
int height = mCollectorInfo.getMeasuredHeight();
|
||||
Drawable drawable = Utils.scaleDrawable(getResources(), app.getIcon(), height, height);
|
||||
|
||||
if(drawable != null)
|
||||
mCollectorInfo.setCompoundDrawablesWithIntrinsicBounds(null, null, drawable, null);
|
||||
else
|
||||
mCollectorInfo.setCompoundDrawablesWithIntrinsicBounds(null, null, null, null);
|
||||
} else
|
||||
mCollectorInfo.setCompoundDrawablesWithIntrinsicBounds(null, null, null, null);
|
||||
} else
|
||||
mCollectorInfo.setCompoundDrawablesWithIntrinsicBounds(null, null, null, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -196,15 +322,75 @@ public class StatusFragment extends Fragment implements AppStateListener {
|
||||
case ready:
|
||||
mCaptureStatus.setText(R.string.ready);
|
||||
mInspectorLink.setVisibility(View.GONE);
|
||||
refreshPcapDumpInfo();
|
||||
mCollectorInfo.setVisibility(View.GONE);
|
||||
mQuickSettings.setVisibility(View.VISIBLE);
|
||||
break;
|
||||
case running:
|
||||
mCaptureStatus.setText(Utils.formatBytes(CaptureService.getBytes()));
|
||||
mInspectorLink.setVisibility(View.VISIBLE);
|
||||
mCollectorInfo.setVisibility(View.VISIBLE);
|
||||
mQuickSettings.setVisibility(View.GONE);
|
||||
refreshPcapDumpInfo();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void loadInstalledApps(Map<Integer, AppDescriptor> apps, boolean with_icons) {
|
||||
mInstalledApps = new ArrayList<>();
|
||||
|
||||
for(Map.Entry<Integer, AppDescriptor> pair : apps.entrySet()) {
|
||||
AppDescriptor app = pair.getValue();
|
||||
|
||||
if(!app.isVirtual())
|
||||
mInstalledApps.add(app);
|
||||
}
|
||||
|
||||
Collections.sort(mInstalledApps);
|
||||
refreshFilterInfo();
|
||||
|
||||
if(mOpenAppsWhenDone && mAppFilterSwitch.isChecked())
|
||||
openAppFilterSelector();
|
||||
}
|
||||
|
||||
private void openAppFilterSelector() {
|
||||
if(mInstalledApps == null) {
|
||||
// Applications not loaded yet
|
||||
mOpenAppsWhenDone = true;
|
||||
Utils.showToast(getContext(), R.string.apps_loading_please_wait);
|
||||
return;
|
||||
}
|
||||
|
||||
mOpenAppsWhenDone = false;
|
||||
|
||||
Dialog dialog = Utils.getAppSelectionDialog(mActivity, mInstalledApps, this::setAppFilter);
|
||||
dialog.setOnCancelListener(dialog1 -> {
|
||||
setAppFilter(null);
|
||||
});
|
||||
dialog.setOnDismissListener(dialog1 -> {
|
||||
mOpenAppsList = null;
|
||||
});
|
||||
|
||||
dialog.show();
|
||||
|
||||
// NOTE: run this after dialog.show
|
||||
mOpenAppsList = (AppsListView) dialog.findViewById(R.id.apps_list);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAppsInfoLoaded(Map<Integer, AppDescriptor> apps) {
|
||||
loadInstalledApps(apps, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAppsIconsLoaded(Map<Integer, AppDescriptor> apps) {
|
||||
loadInstalledApps(apps, true);
|
||||
|
||||
// Possibly update the icons
|
||||
if(mOpenAppsList != null) {
|
||||
Log.d(TAG, "reloading app icons in dialog");
|
||||
mOpenAppsList.setApps(mInstalledApps);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -34,6 +34,7 @@ public class Prefs {
|
||||
public static final String PREF_HTTP_SERVER_PORT = "http_server_port";
|
||||
public static final String PREF_PCAP_DUMP_MODE = "pcap_dump_mode";
|
||||
public static final String PREF_PCAP_URI = "pcap_path";
|
||||
public static final String DEFAULT_DUMP_MODE = DUMP_HTTP_SERVER;
|
||||
|
||||
public enum DumpMode {
|
||||
NONE,
|
||||
@ -56,9 +57,10 @@ public class Prefs {
|
||||
/* Prefs with defaults */
|
||||
public static String getCollectorIp(SharedPreferences p) { return(p.getString(PREF_COLLECTOR_IP_KEY, "127.0.0.1")); }
|
||||
public static int getCollectorPort(SharedPreferences p) { return(Integer.parseInt(p.getString(PREF_COLLECTOR_PORT_KEY, "1234"))); }
|
||||
public static DumpMode getDumpMode(SharedPreferences p) { return(getDumpMode(p.getString(PREF_PCAP_DUMP_MODE, DUMP_HTTP_SERVER))); }
|
||||
public static DumpMode getDumpMode(SharedPreferences p) { return(getDumpMode(p.getString(PREF_PCAP_DUMP_MODE, DEFAULT_DUMP_MODE))); }
|
||||
public static int getHttpServerPort(SharedPreferences p) { return(Integer.parseInt(p.getString(Prefs.PREF_HTTP_SERVER_PORT, "8080"))); }
|
||||
public static boolean getTlsDecryptionEnabled(SharedPreferences p) { return(p.getBoolean(PREF_TLS_DECRYPTION_ENABLED_KEY, false)); }
|
||||
public static String getTlsProxyAddress(SharedPreferences p) { return(p.getString(PREF_TLS_PROXY_IP_KEY, "0.0.0.0")); }
|
||||
public static int getTlsProxyPort(SharedPreferences p) { return(Integer.parseInt(p.getString(Prefs.PREF_TLS_PROXY_PORT_KEY, "8080"))); }
|
||||
public static String getAppFilter(SharedPreferences p) { return(p.getString(PREF_APP_FILTER, "")); }
|
||||
}
|
||||
|
||||
@ -113,8 +113,12 @@ public class AppsListView extends EmptyRecyclerView implements SearchView.OnQuer
|
||||
|
||||
public void setApps(List<AppDescriptor> installedApps) {
|
||||
mInstalledApps = installedApps;
|
||||
mAdapter = new AppsAdapter(getContext(), mInstalledApps);
|
||||
setAdapter(mAdapter);
|
||||
|
||||
if(mAdapter == null) {
|
||||
mAdapter = new AppsAdapter(getContext(), mInstalledApps);
|
||||
setAdapter(mAdapter);
|
||||
} else
|
||||
mAdapter.notifyDataSetChanged();
|
||||
}
|
||||
|
||||
public void setSelectedAppListener(final OnSelectedAppListener listener) {
|
||||
|
||||
@ -23,12 +23,17 @@
|
||||
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
<fragment
|
||||
android:layout_height="match_parent"
|
||||
android:layout_width="match_parent"
|
||||
android:id="@+id/status_fragment"
|
||||
android:name="com.emanuelef.remote_capture.fragments.StatusFragment"
|
||||
<ScrollView
|
||||
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior"
|
||||
tools:layout="@layout/status"/>
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" >
|
||||
|
||||
<fragment
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="match_parent"
|
||||
android:id="@+id/status_fragment"
|
||||
android:name="com.emanuelef.remote_capture.fragments.StatusFragment"
|
||||
tools:layout="@layout/status"/>
|
||||
</ScrollView>
|
||||
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
26
app/src/main/res/layout/quick_settings_item.xml
Normal file
26
app/src/main/res/layout/quick_settings_item.xml
Normal file
@ -0,0 +1,26 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="4dp"
|
||||
android:layout_marginTop="4dp"
|
||||
android:padding="10dp"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/title"
|
||||
android:theme="@style/QuickSettings.Title"
|
||||
tools:text="Title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_marginBottom="2dp"
|
||||
android:layout_height="wrap_content"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/description"
|
||||
tools:text="Description"
|
||||
android:drawablePadding="5dp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"/>
|
||||
</LinearLayout>
|
||||
@ -14,7 +14,8 @@
|
||||
android:layout_marginTop="60dp"
|
||||
android:gravity="center"
|
||||
android:autoLink="web"
|
||||
android:drawablePadding="5dp"
|
||||
android:drawablePadding="10dp"
|
||||
tools:text="Collector Info"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.5"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
@ -24,11 +25,12 @@
|
||||
android:id="@+id/inspector_link"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="24dp"
|
||||
android:layout_marginTop="32dp"
|
||||
android:gravity="center"
|
||||
android:text="@string/inspector"
|
||||
android:drawablePadding="5dp"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.5"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
@ -36,8 +38,8 @@
|
||||
|
||||
<TextView
|
||||
android:id="@+id/status_view"
|
||||
android:layout_width="240dp"
|
||||
android:layout_height="240dp"
|
||||
android:layout_width="220dp"
|
||||
android:layout_height="220dp"
|
||||
android:layout_marginTop="50dp"
|
||||
android:background="@drawable/rounded_bg"
|
||||
android:gravity="center"
|
||||
@ -48,4 +50,42 @@
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/quick_settings"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:layout_marginTop="32dp"
|
||||
android:padding="10dp"
|
||||
app:layout_constraintTop_toBottomOf="@id/status_view"
|
||||
app:layout_constraintStart_toStartOf="parent">
|
||||
|
||||
<androidx.appcompat.widget.AppCompatSpinner
|
||||
android:id="@+id/dump_mode_spinner"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:spinnerMode="dialog"
|
||||
android:prompt="@string/dump_mode"/>
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginTop="10dp"
|
||||
android:paddingEnd="0dp">
|
||||
|
||||
<include layout="@layout/quick_settings_item"
|
||||
android:id="@+id/app_filter_text"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="50dp" />
|
||||
|
||||
<androidx.appcompat.widget.SwitchCompat
|
||||
android:id="@+id/app_filter_switch"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerInParent="true" />
|
||||
</RelativeLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@ -9,13 +9,6 @@
|
||||
android:icon="@android:drawable/ic_media_play"
|
||||
app:showAsAction="always" />
|
||||
|
||||
<item
|
||||
android:id="@+id/action_show_app_filter"
|
||||
android:title="@string/set_app_filter"
|
||||
android:orderInCategory="50"
|
||||
android:icon="@drawable/ic_filter_alt"
|
||||
app:showAsAction="ifRoom" />
|
||||
|
||||
<item
|
||||
android:id="@+id/action_settings"
|
||||
android:title="@string/title_activity_settings"
|
||||
|
||||
@ -13,4 +13,11 @@
|
||||
<item>@string/pcap_file</item>
|
||||
<item>@string/udp_exporter</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="pcap_dump_modes_descriptions">
|
||||
<item>@string/no_dump_info</item>
|
||||
<item>@string/http_server_info</item>
|
||||
<item>@string/pcap_file_info</item>
|
||||
<item>@string/udp_exporter_info</item>
|
||||
</string-array>
|
||||
</resources>
|
||||
|
||||
@ -47,8 +47,8 @@
|
||||
<string name="http_server_info">Start an HTTP server for the PCAP download</string>
|
||||
<string name="udp_exporter_info">Sends the PCAP to a remote UDP receiver</string>
|
||||
<string name="http_server_port">HTTP Server Port</string>
|
||||
<string name="receiver_ip_address">Receiver IP Address</string>
|
||||
<string name="receiver_port">Receiver Port</string>
|
||||
<string name="receiver_ip_address">Collector IP Address</string>
|
||||
<string name="receiver_port">Collector Port</string>
|
||||
<string name="dump_mode">Dump Mode</string>
|
||||
<string name="tls_proxy_ip_address">mitmproxy IP Address</string>
|
||||
<string name="tls_proxy_port">mitmproxy Port</string>
|
||||
@ -99,5 +99,6 @@
|
||||
<string name="delete_error">Could not delete file</string>
|
||||
<string name="capture_running">Capture running</string>
|
||||
<string name="notification_msg">%1$s captured, %2$s connections</string>
|
||||
<string name="no_app_filter">No app filter set</string>
|
||||
</resources>
|
||||
|
||||
|
||||
@ -17,4 +17,10 @@
|
||||
|
||||
<style name="PopupOverlay" parent="ThemeOverlay.AppCompat.Light" />
|
||||
|
||||
<style name="QuickSettings.Title" parent="AppTheme">
|
||||
<item name="android:textSize">18sp</item>
|
||||
<item name="android:textStyle">bold</item>
|
||||
<item name="android:textColor">@android:color/tab_indicator_text</item>
|
||||
</style>
|
||||
|
||||
</resources>
|
||||
|
||||
@ -16,23 +16,16 @@
|
||||
|
||||
<PreferenceScreen xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<PreferenceCategory app:title="@string/pcap_dump" app:iconSpaceReserved="false">
|
||||
<ListPreference
|
||||
app:key="pcap_dump_mode"
|
||||
app:title="@string/dump_mode"
|
||||
app:iconSpaceReserved="false"
|
||||
app:summary="@string/no_dump_info"
|
||||
app:defaultValue="http_server"
|
||||
app:entries="@array/pcap_dump_modes_labels"
|
||||
app:entryValues="@array/pcap_dump_modes"/>
|
||||
|
||||
<PreferenceCategory app:title="@string/http_server" app:iconSpaceReserved="false">
|
||||
<EditTextPreference
|
||||
app:key="http_server_port"
|
||||
app:title="@string/http_server_port"
|
||||
app:defaultValue="8080"
|
||||
app:iconSpaceReserved="false"
|
||||
app:useSimpleSummaryProvider="true" />
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory app:title="@string/udp_exporter" app:iconSpaceReserved="false">
|
||||
<EditTextPreference
|
||||
app:key="collector_ip_address"
|
||||
app:title="@string/receiver_ip_address"
|
||||
|
||||
Loading…
Reference in New Issue
Block a user