Skip to content

Instantly share code, notes, and snippets.

@wubydax
Created August 6, 2016 10:58
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save wubydax/77de99188c07f3a16b42a229e8dadecb to your computer and use it in GitHub Desktop.
Save wubydax/77de99188c07f3a16b42a229e8dadecb to your computer and use it in GitHub Desktop.
Custom network traffic view for status bar or notification panel. Originally created by @temasec here https://github.com/temasek/android_frameworks_base/blob/cm-13.0/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkTraffic.java and upgraded and reworked for more features by @wubydax
package com.android.wubydax;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.Resources;
import android.database.ContentObserver;
import android.graphics.Color;
import android.graphics.PorterDuff;
import android.graphics.Typeface;
import android.graphics.drawable.Drawable;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.TrafficStats;
import android.net.Uri;
import android.os.Handler;
import android.os.Message;
import android.os.SystemClock;
import android.provider.Settings;
import android.util.AttributeSet;
import android.util.Log;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.View;
import android.widget.TextView;
import java.text.DecimalFormat;
/*
*
* Seeing how an Integer object in java requires at least 16 Bytes, it seemed awfully wasteful
* to only use it for a single boolean. 32-bits is plenty of room for what we need it to do.
*
*/
public class NetworkTraffic extends TextView {
public static final int MASK_DOWN = 2; // Second least valuable bit
public static final int MASK_UP = 1; // Least valuable bit
private static final int KILOBIT = 1000;
private static final int KILOBYTE = 1024;
private static String NETWORK_TRAFFIC_STATE = "network_traffic_state";
private static String NETWORK_TRAFFIC_COLOR = "network_traffic_color";
private static String NETWORK_TRAFFIC_AUTOHIDE = "network_traffic_autohide";
private static String NETWORK_TRAFFIC_SHOW_ARROWS = "network_traffic_show_arrows";
private static String NETWORK_TRAFFIC_AUTOHIDE_THRESHOLD = "network_traffic_threshold";
private static String NETWORK_TRAFFIC_UNIT = "network_traffic_unit";
private static String NETWORK_TRAFFIC_ARROW_COLOR = "network_traffic_arrow_color";
private static String NETWORK_TRAFFIC_GLOBAL_COLOR = "network_traffic_global_color";
private static String NETWORK_TRAFFIC_ARROWS_POSITION = "network_traffic_arrows_position";
private static String NETWORK_TRAFFIC_SCALE = "network_traffic_scale";
private static String NETWORK_TRAFFIC_TEXT_STYLE = "network_traffic_text_style";
private static DecimalFormat decimalFormat = new DecimalFormat("##0.#");
static {
decimalFormat.setMaximumIntegerDigits(3);
decimalFormat.setMaximumFractionDigits(1);
}
private int mState = 0;
private long totalRxBytes;
private long totalTxBytes;
private long lastUpdateTime;
private int txtSizeSingle;
private int txtSizeMulti;
private int KB = KILOBIT;
private int MB = KB * KB;
private int GB = MB * KB;
private boolean mAutoHide;
private boolean mShowArrows, mIsGlobalColor, mIsToStart;
private int mAutoHideThreshold;
private int mNetworkTrafficArrowColor, mNetworkTrafficColor, mTextStyle;
private Context mContext;
private SettingsObserver mSettingsObserver;
private Handler mTrafficHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
long timeDelta = SystemClock.elapsedRealtime() - lastUpdateTime;
if (timeDelta < getInterval(mState) * .95) {
if (msg.what != 1) {
// we just updated the view, nothing further to do
return;
}
if (timeDelta < 1) {
// Can't div by 0 so make sure the value displayed is minimal
timeDelta = Long.MAX_VALUE;
}
}
lastUpdateTime = SystemClock.elapsedRealtime();
// Calculate the data rate from the change in total bytes and time
long newTotalRxBytes = TrafficStats.getTotalRxBytes();
long newTotalTxBytes = TrafficStats.getTotalTxBytes();
long rxData = newTotalRxBytes - totalRxBytes;
long txData = newTotalTxBytes - totalTxBytes;
if (shouldHide(rxData, timeDelta)) {
setText("");
setVisibility(View.GONE);
} else if (!getConnectAvailable()) {
clearHandlerCallbacks();
setVisibility(View.GONE);
} else {
// If bit/s convert from Bytes to bits
String symbol;
if (KB == KILOBYTE) {
symbol = "B/s";
} else {
symbol = "b/s";
rxData = rxData * 8;
txData = txData * 8;
}
// Get information for uplink ready so the line return can be added
String output = "";
if (isSet(mState, MASK_UP)) {
output = formatOutput(timeDelta, txData, symbol);
}
// Ensure text size is where it needs to be
int textSize;
if (isSet(mState, MASK_UP + MASK_DOWN)) {
output += "\n";
textSize = txtSizeMulti;
} else {
textSize = txtSizeSingle;
}
// Add information for downlink if it's called for
if (isSet(mState, MASK_DOWN)) {
output += formatOutput(timeDelta, rxData, symbol);
}
// Update view if there's anything new to show
if (!output.contentEquals(getText())) {
setTextSize(TypedValue.COMPLEX_UNIT_PX, (float) textSize);
setText(output);
}
setVisibility(View.VISIBLE);
}
// Post delayed message to refresh in ~1000ms
totalRxBytes = newTotalRxBytes;
totalTxBytes = newTotalTxBytes;
clearHandlerCallbacks();
mTrafficHandler.postDelayed(mRunnable, getInterval(mState));
}
private String formatOutput(long timeDelta, long data, String symbol) {
long speed = (long) (data / (timeDelta / 1000F));
if (speed < KB) {
return decimalFormat.format(speed) + symbol;
} else if (speed < MB) {
return decimalFormat.format(speed / (float) KB) + 'K' + symbol;
} else if (speed < GB) {
return decimalFormat.format(speed / (float) MB) + 'M' + symbol;
}
return decimalFormat.format(speed / (float) GB) + 'G' + symbol;
}
private boolean shouldHide(long rxData, long timeDelta) {
long speedRxKB = (long) (rxData / (timeDelta / 1000f)) / KILOBYTE;
return mAutoHide && speedRxKB <= mAutoHideThreshold;
}
};
private Runnable mRunnable = new Runnable() {
@Override
public void run() {
mTrafficHandler.sendEmptyMessage(0);
}
};
private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action != null && action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
updateSettings();
}
}
};
public NetworkTraffic(Context context, AttributeSet attrs) {
super(context, attrs);
mContext = context;
final Resources resources = getResources();
txtSizeSingle = resources.getDimensionPixelSize(resources.getIdentifier("net_traffic_single_text_size", "dimen", context.getPackageName()));
txtSizeMulti = resources.getDimensionPixelSize(resources.getIdentifier("net_traffic_multi_text_size", "dimen", context.getPackageName()));
Handler mHandler = new Handler();
mSettingsObserver = new SettingsObserver(mHandler);
updateSettings();
setGravity(Gravity.CENTER_VERTICAL);
}
private static boolean isSet(int intState, int intMask) {
return (intState & intMask) == intMask;
}
private static int getInterval(int intState) {
int intInterval = intState >>> 16;
return (intInterval >= 250 && intInterval <= 32750) ? intInterval : 1000;
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
try {
mSettingsObserver.observe();
IntentFilter filter = new IntentFilter();
filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
mContext.registerReceiver(mIntentReceiver, filter, null, getHandler());
} catch (IllegalArgumentException e) {
Log.d("wubydax", "onAttachedToWindow receiver still registered");
}
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
try {
mSettingsObserver.unObserve();
mContext.unregisterReceiver(mIntentReceiver);
} catch (IllegalArgumentException e) {
Log.d("wubydax", "onDetachedFromWindow receiver not registered");
}
}
private boolean getConnectAvailable() {
ConnectivityManager connManager =
(ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo network = (connManager != null) ? connManager.getActiveNetworkInfo() : null;
return network != null && network.isConnected();
}
private void updateSettings() {
ContentResolver resolver = mContext.getContentResolver();
mAutoHide = Settings.System.getInt(resolver,
NETWORK_TRAFFIC_AUTOHIDE, 0) == 1;
mShowArrows = Settings.System.getInt(resolver,
NETWORK_TRAFFIC_SHOW_ARROWS, 1) == 1;
mAutoHideThreshold = Settings.System.getInt(resolver,
NETWORK_TRAFFIC_AUTOHIDE_THRESHOLD, 10);
mIsGlobalColor = Settings.System.getInt(resolver, NETWORK_TRAFFIC_GLOBAL_COLOR, 1) == 0;
mState = Settings.System.getInt(resolver, NETWORK_TRAFFIC_STATE, 0);
mNetworkTrafficArrowColor = Settings.System.getInt(resolver, NETWORK_TRAFFIC_ARROW_COLOR, Color.WHITE);
boolean isBytes = Settings.System.getInt(resolver, NETWORK_TRAFFIC_UNIT, 0) == 1;
mIsToStart = Settings.System.getInt(resolver, NETWORK_TRAFFIC_ARROWS_POSITION, 0) == 1;
mNetworkTrafficColor = Settings.System.getInt(resolver,
NETWORK_TRAFFIC_COLOR, Color.WHITE);
float scale = (float) Settings.System.getInt(resolver, NETWORK_TRAFFIC_SCALE, 10) / 10;
setScaleX(scale);
setScaleY(scale);
mTextStyle = Settings.System.getInt(resolver, NETWORK_TRAFFIC_TEXT_STYLE, 0);
setUpStyle();
setTextColor(mNetworkTrafficColor);
updateTrafficDrawable();
if (isBytes) {
KB = KILOBYTE;
} else {
KB = KILOBIT;
}
MB = KB * KB;
GB = MB * KB;
if (isSet(mState, MASK_UP) || isSet(mState, MASK_DOWN)) {
if (getConnectAvailable()) {
totalRxBytes = TrafficStats.getTotalRxBytes();
lastUpdateTime = SystemClock.elapsedRealtime();
mTrafficHandler.sendEmptyMessage(1);
setVisibility(View.VISIBLE);
updateTrafficDrawable();
return;
}
} else {
clearHandlerCallbacks();
}
setVisibility(View.GONE);
}
private void setUpStyle() {
switch (mTextStyle) {
case 1:
setTypeface(null, Typeface.BOLD);
break;
case 2:
setTypeface(null, Typeface.ITALIC);
break;
case 3:
setTypeface(null, Typeface.BOLD_ITALIC);
break;
default:
setTypeface(null, Typeface.NORMAL);
}
}
private void clearHandlerCallbacks() {
mTrafficHandler.removeCallbacks(mRunnable);
mTrafficHandler.removeMessages(0);
mTrafficHandler.removeMessages(1);
}
@SuppressWarnings("deprecation")
private void updateTrafficDrawable() {
int intTrafficDrawable;
if (mShowArrows) {
Drawable drw;
if (isSet(mState, MASK_UP + MASK_DOWN)) {
intTrafficDrawable = mContext.getResources().getIdentifier("stat_sys_network_traffic_updown", "drawable", mContext.getPackageName());
} else if (isSet(mState, MASK_UP)) {
intTrafficDrawable = mContext.getResources().getIdentifier("stat_sys_network_traffic_up", "drawable", mContext.getPackageName());
} else if (isSet(mState, MASK_DOWN)) {
intTrafficDrawable = mContext.getResources().getIdentifier("stat_sys_network_traffic_down", "drawable", mContext.getPackageName());
} else {
intTrafficDrawable = 0;
}
if (intTrafficDrawable != 0) {
drw = getContext().getResources().getDrawable(intTrafficDrawable);
if (drw != null) {
drw.setColorFilter(mIsGlobalColor ? mNetworkTrafficColor : mNetworkTrafficArrowColor, PorterDuff.Mode.SRC_ATOP);
if (mIsToStart) {
setCompoundDrawablesRelativeWithIntrinsicBounds(drw, null, null, null);
} else {
setCompoundDrawablesWithIntrinsicBounds(null, null, drw, null);
}
}
}
} else {
setCompoundDrawablesWithIntrinsicBounds(null, null, null, null);
}
}
class SettingsObserver extends ContentObserver {
SettingsObserver(Handler handler) {
super(handler);
}
void observe() {
ContentResolver resolver = mContext.getContentResolver();
Uri uri = Settings.System.getUriFor(NETWORK_TRAFFIC_STATE);
resolver.registerContentObserver(uri, false,
this);
resolver.registerContentObserver(Settings.System
.getUriFor(NETWORK_TRAFFIC_AUTOHIDE), false,
this);
resolver.registerContentObserver(Settings.System
.getUriFor(NETWORK_TRAFFIC_SHOW_ARROWS), false,
this);
resolver.registerContentObserver(Settings.System
.getUriFor(NETWORK_TRAFFIC_AUTOHIDE_THRESHOLD), false,
this);
resolver.registerContentObserver(Settings.System
.getUriFor(NETWORK_TRAFFIC_COLOR), false,
this);
resolver.registerContentObserver(Settings.System
.getUriFor(NETWORK_TRAFFIC_UNIT), false,
this);
resolver.registerContentObserver(Settings.System
.getUriFor(NETWORK_TRAFFIC_ARROW_COLOR), false,
this);
resolver.registerContentObserver(Settings.System
.getUriFor(NETWORK_TRAFFIC_GLOBAL_COLOR), false,
this);
resolver.registerContentObserver(Settings.System
.getUriFor(NETWORK_TRAFFIC_ARROWS_POSITION), false,
this);
resolver.registerContentObserver(Settings.System
.getUriFor(NETWORK_TRAFFIC_TEXT_STYLE), false,
this);
resolver.registerContentObserver(Settings.System
.getUriFor(NETWORK_TRAFFIC_SCALE), false,
this);
}
/*
* @hide
*/
@Override
public void onChange(boolean selfChange) {
updateSettings();
}
void unObserve() {
mContext.getContentResolver().unregisterContentObserver(this);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment