Added option to show network speed graph in status bar notification

This commit is contained in:
M66B 2015-12-09 14:57:14 +01:00
parent f83a6ef682
commit fbf6d0b06c
10 changed files with 230 additions and 15 deletions

View File

@ -29,7 +29,13 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.net.ConnectivityManager;
import android.net.TrafficStats;
import android.net.VpnService;
import android.os.Build;
import android.os.Handler;
@ -38,6 +44,7 @@ import android.os.Looper;
import android.os.Message;
import android.os.ParcelFileDescriptor;
import android.os.PowerManager;
import android.os.SystemClock;
import android.preference.PreferenceManager;
import android.support.v4.app.NotificationCompat;
import android.support.v4.app.NotificationManagerCompat;
@ -48,12 +55,15 @@ import android.telephony.ServiceState;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.util.Log;
import android.widget.RemoteViews;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.List;
public class SinkholeService extends VpnService {
private static final String TAG = "NetGuard.Service";
@ -71,10 +81,16 @@ public class SinkholeService extends VpnService {
private static final int NOTIFY_FOREGROUND = 1;
private static final int NOTIFY_DISABLED = 2;
private static final int NOTIFY_TRAFFIC = 3;
private static final String EXTRA_COMMAND = "Command";
private static final String EXTRA_REASON = "Reason";
private static final int MSG_SERVICE_INTENT = 0;
private static final int MSG_STATS_START = 1;
private static final int MSG_STATS_STOP = 2;
private static final int MSG_STATS_UPDATE = 3;
private enum Command {start, reload, stop}
private static volatile PowerManager.WakeLock wlInstance = null;
@ -96,22 +112,39 @@ public class SinkholeService extends VpnService {
@Override
public void handleMessage(Message msg) {
try {
handleIntent((Intent) msg.obj);
switch (msg.what) {
case MSG_SERVICE_INTENT:
handleIntent((Intent) msg.obj);
break;
case MSG_STATS_START:
startStats();
break;
case MSG_STATS_STOP:
stopStats();
break;
case MSG_STATS_UPDATE:
updateStats();
break;
}
} catch (Throwable ex) {
Log.e(TAG, ex.toString() + "\n" + Log.getStackTraceString(ex));
Util.sendCrashReport(ex, SinkholeService.this);
} finally {
try {
PowerManager.WakeLock wl = getLock(SinkholeService.this);
if (wl.isHeld())
wl.release();
else
Log.w(TAG, "Wakelock under-locked");
Log.i(TAG, "Messages=" + hasMessages(0) + " wakelock=" + wlInstance.isHeld());
} catch (Exception ex) {
Log.e(TAG, ex.toString() + "\n" + Log.getStackTraceString(ex));
Util.sendCrashReport(ex, SinkholeService.this);
}
if (msg.what == MSG_SERVICE_INTENT)
try {
PowerManager.WakeLock wl = getLock(SinkholeService.this);
if (wl.isHeld())
wl.release();
else
Log.w(TAG, "Wakelock under-locked");
Log.i(TAG, "Messages=" + hasMessages(0) + " wakelock=" + wlInstance.isHeld());
} catch (Exception ex) {
Log.e(TAG, ex.toString() + "\n" + Log.getStackTraceString(ex));
Util.sendCrashReport(ex, SinkholeService.this);
}
}
}
@ -209,6 +242,151 @@ public class SinkholeService extends VpnService {
Util.sendCrashReport(ex, SinkholeService.this);
}
}
private boolean stats = false;
private long t = -1;
private long tx = -1;
private long rx = -1;
private List<Long> gt = new ArrayList<>();
private List<Float> gtx = new ArrayList<>();
private List<Float> grx = new ArrayList<>();
private void startStats() {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(SinkholeService.this);
boolean enabled = prefs.getBoolean("show_stats", false);
Log.i(TAG, "Stats start enabled=" + enabled);
if (enabled) {
t = -1;
tx = -1;
rx = -1;
gt.clear();
gtx.clear();
grx.clear();
stats = true;
updateStats();
}
}
private void stopStats() {
Log.i(TAG, "Stats stop");
stats = false;
NotificationManagerCompat.from(SinkholeService.this).cancel(NOTIFY_TRAFFIC);
}
private void updateStats() {
if (!stats)
return;
// Schedule next update
Message msg = new Message();
msg.what = MSG_STATS_UPDATE;
mServiceHandler.sendMessageDelayed(msg, 1000);
// Cleanup
while (gt.size() >= 100) {
gt.remove(0);
gtx.remove(0);
grx.remove(0);
}
// Calculate stats
float txsec = 0;
float rxsec = 0;
long ct = SystemClock.elapsedRealtime();
long ctx = TrafficStats.getTotalTxBytes();
long rtx = TrafficStats.getTotalRxBytes();
if (ct > 0 && (rx > 0 || tx > 0)) {
float dt = (ct - t) / 1000f;
txsec = (ctx - tx) / dt;
rxsec = (rtx - rx) / dt;
Log.i(TAG, "tx=" + txsec + " rx=" + rxsec);
gt.add(ct);
gtx.add(txsec);
grx.add(rxsec);
}
t = ct;
tx = ctx;
rx = rtx;
// Create bitmap
int height = Util.dips2pixels(96 - 2 * 4, SinkholeService.this);
int width = Util.dips2pixels(96 * 5, SinkholeService.this);
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
// Create canvas
Canvas canvas = new Canvas(bitmap);
canvas.drawColor(Color.TRANSPARENT);
// Determine max
long xmax = 0;
float ymax = 1024;
for (int i = 0; i < gt.size(); i++) {
long t = gt.get(i);
float tx = gtx.get(i);
float rx = grx.get(i);
if (t > xmax)
xmax = t;
if (tx > ymax)
ymax = tx;
if (rx > ymax)
ymax = rx;
}
// Build paths
Path ptx = new Path();
Path prx = new Path();
for (int i = 0; i < gtx.size(); i++) {
float x = width * (xmax - gt.get(i)) / 1000f / 100f;
float ytx = height - height * gtx.get(i) / ymax;
float yrx = height - height * grx.get(i) / ymax;
if (i == 0) {
ptx.moveTo(x, ytx);
prx.moveTo(x, yrx);
} else {
ptx.lineTo(x, ytx);
prx.lineTo(x, yrx);
}
}
// Build paint
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(Util.dips2pixels(3, SinkholeService.this));
// Draw paths
paint.setColor(Color.MAGENTA);
canvas.drawPath(ptx, paint);
paint.setColor(Color.BLUE);
canvas.drawPath(prx, paint);
// Draw 1 KiB line
paint.setStrokeWidth(0);
paint.setColor(Color.GRAY);
float y = height - height * 1024f / ymax;
canvas.drawLine(0, y, width, y, paint);
// Update remote view
RemoteViews remoteViews = new RemoteViews(getPackageName(), R.layout.traffic);
remoteViews.setImageViewBitmap(R.id.ivTraffic, bitmap);
remoteViews.setTextViewText(R.id.tvTraffic, String.format("%.0f/%.0f B/sec", txsec, rxsec));
// Show notification
Intent main = new Intent(SinkholeService.this, ActivityMain.class);
PendingIntent pi = PendingIntent.getActivity(SinkholeService.this, 0, main, PendingIntent.FLAG_UPDATE_CURRENT);
NotificationCompat.Builder builder = new NotificationCompat.Builder(SinkholeService.this)
.setSmallIcon(R.drawable.ic_equalizer_white_24dp)
.setContent(remoteViews)
.setContentIntent(pi)
.setCategory(Notification.CATEGORY_STATUS)
.setVisibility(Notification.VISIBILITY_SECRET)
.setPriority(Notification.PRIORITY_DEFAULT)
.setColor(ContextCompat.getColor(SinkholeService.this, R.color.colorPrimary))
.setAutoCancel(true);
NotificationManagerCompat.from(SinkholeService.this).notify(NOTIFY_TRAFFIC, builder.build());
}
}
private ParcelFileDescriptor startVPN() {
@ -400,6 +578,12 @@ public class SinkholeService extends VpnService {
Log.i(TAG, "Received " + intent);
Util.logExtras(intent);
reload(null, "interactive state changed", SinkholeService.this);
// Start/stop stats
PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
Message msg = new Message();
msg.what = pm.isInteractive() ? MSG_STATS_START : MSG_STATS_STOP;
mServiceHandler.sendMessage(msg);
}
};
@ -546,7 +730,7 @@ public class SinkholeService extends VpnService {
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
msg.obj = intent;
msg.what = 0;
msg.what = MSG_SERVICE_INTENT;
mServiceHandler.sendMessage(msg);
return START_STICKY;
@ -613,8 +797,7 @@ public class SinkholeService extends VpnService {
.setCategory(Notification.CATEGORY_STATUS)
.setVisibility(Notification.VISIBILITY_SECRET)
.setPriority(Notification.PRIORITY_MIN)
.setColor(ContextCompat.getColor(this, R.color.colorPrimary))
.setAutoCancel(true);
.setColor(ContextCompat.getColor(this, R.color.colorPrimary));
if (allowed > 0 || blocked > 0) {
NotificationCompat.BigTextStyle notification = new NotificationCompat.BigTextStyle(builder);

View File

@ -300,6 +300,10 @@ public class Util {
}
}
public static int dips2pixels(int dips, Context context) {
return Math.round(dips * context.getResources().getDisplayMetrics().density + 0.5f);
}
public static void logExtras(Intent intent) {
if (intent != null)
logBundle(intent.getExtras());

Binary file not shown.

After

Width:  |  Height:  |  Size: 106 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 88 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 99 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 B

View File

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="4dp">
<ImageView
android:id="@+id/ivTraffic"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true" />
<TextView
android:id="@+id/tvTraffic"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:gravity="center_horizontal"
android:textAppearance="@style/TextAppearance.StatusBar.EventContent" />
</RelativeLayout>

View File

@ -24,6 +24,7 @@ These issues are caused by bugs in Android, or in the software provided by the m
<string name="setting_options">Options</string>
<string name="setting_system">Manage system applications</string>
<string name="setting_stats">Show speed notification</string>
<string name="setting_auto">Auto enable after %1$s minutes</string>
<string name="setting_dark">Use dark theme</string>
<string name="setting_wifi_home">Wi-Fi home network: %1$s</string>
@ -40,6 +41,7 @@ These issues are caused by bugs in Android, or in the software provided by the m
<string name="setting_technical">Technical information</string>
<string name="summary_system">Define rules for system applications (for experts)</string>
<string name="summary_stats">Show network speed graph in status bar notification</string>
<string name="summary_auto">After disabling using the widget, automatically enable NetGuard again after the selected number of minutes\nEnter zero to disable this option</string>
<string name="summary_wifi_home">Apply Wi-Fi network rules for selected network only (apply mobile network rules for other Wi-Fi networks)</string>
<string name="summary_metered">Apply mobile network rules to metered (paid, tethered) Wi-Fi networks</string>

View File

@ -32,6 +32,11 @@
android:key="manage_system"
android:summary="@string/summary_system"
android:title="@string/setting_system" />
<SwitchPreference
android:defaultValue="false"
android:key="show_stats"
android:summary="@string/summary_stats"
android:title="@string/setting_stats" />
<EditTextPreference
android:defaultValue="0"
android:inputType="number"