Created
August 6, 2016 10:58
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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