From 2ea2bb70069af468b6b0dc658dad4aa151b1f419 Mon Sep 17 00:00:00 2001 From: Jon Haus Date: Thu, 5 Jan 2017 00:20:06 +0100 Subject: [PATCH] base: Statusbar Network Traffic [1/2] ----- abc ezio84: use O darkintensity api use arrows vectors from aosp net arrows commit (thanks @Mazda-- for this find!): https://github.com/ezio84/abc_frameworks_base/commit/ad71ae1fd09f9a7392ab2eba7b766c8b088319b4 fix visual glitch on statusbar expand (arrows showing and quickly hiding again) don't show arrows when no data remove unit settings (KB is good) remove interval (set to 1sec by def) and single arrow options don't get settings values again when not needed fix padding between arrows and speed values and expose the padding value code cleanup ----- Change-Id: I026f67caec69a09c0c1969a09fbe99aa6b6f0510 Signed-off-by: Adithya R --- core/java/android/provider/Settings.java | 12 + .../stat_sys_network_traffic_updown.xml | 29 ++ packages/SystemUI/res/layout/status_bar.xml | 12 +- .../SystemUI/res/values/custom_dimens.xml | 5 + .../phone/PhoneStatusBarTransitions.java | 5 +- .../statusbar/policy/NetworkTraffic.java | 298 ++++++++++++++++++ 6 files changed, 359 insertions(+), 2 deletions(-) create mode 100644 packages/SystemUI/res/drawable/stat_sys_network_traffic_updown.xml create mode 100644 packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkTraffic.java diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 8b2fed102189..d51e2ba75bf7 100755 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -4703,6 +4703,18 @@ public static void setShowGTalkServiceStatusForUser(ContentResolver cr, boolean */ public static final String FOD_ANIM = "fod_recognizing_animation_list"; + + /** + * Wheter to show network traffic indicator in statusbar + * @hide + */ + public static final String NETWORK_TRAFFIC_STATE = "network_traffic_state"; + /** + * Network traffic inactivity threshold (default is 1 kBs) + * @hide + */ + public static final String NETWORK_TRAFFIC_AUTOHIDE_THRESHOLD = "network_traffic_autohide_threshold"; + /** * Show pointer location on screen? * 0 = no diff --git a/packages/SystemUI/res/drawable/stat_sys_network_traffic_updown.xml b/packages/SystemUI/res/drawable/stat_sys_network_traffic_updown.xml new file mode 100644 index 000000000000..4e0b63ceffe4 --- /dev/null +++ b/packages/SystemUI/res/drawable/stat_sys_network_traffic_updown.xml @@ -0,0 +1,29 @@ + + + + + + diff --git a/packages/SystemUI/res/layout/status_bar.xml b/packages/SystemUI/res/layout/status_bar.xml index 37d3091ddc97..e6ab710c642d 100644 --- a/packages/SystemUI/res/layout/status_bar.xml +++ b/packages/SystemUI/res/layout/status_bar.xml @@ -112,13 +112,23 @@ android:gravity="center_horizontal|center_vertical"/> + + diff --git a/packages/SystemUI/res/values/custom_dimens.xml b/packages/SystemUI/res/values/custom_dimens.xml index 1ced556bc64c..30e71ba4ce0a 100644 --- a/packages/SystemUI/res/values/custom_dimens.xml +++ b/packages/SystemUI/res/values/custom_dimens.xml @@ -106,4 +106,9 @@ 13.5dp 20dp + + + 8dp + 2dp + diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarTransitions.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarTransitions.java index 8f20034a17c3..366bf00dfcc3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarTransitions.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarTransitions.java @@ -31,7 +31,7 @@ public final class PhoneStatusBarTransitions extends BarTransitions { private final float mIconAlphaWhenOpaque; - private View mLeftSide, mStatusIcons, mBattery, mClock, mCenterClock, mRightClock; + private View mLeftSide, mStatusIcons, mBattery, mClock, mCenterClock, mRightClock, mNetworkTraffic; private Animator mCurrentAnimation; @@ -48,6 +48,7 @@ public PhoneStatusBarTransitions(PhoneStatusBarView statusBarView, View backgrou mClock = statusBarView.findViewById(R.id.clock); mCenterClock = statusBarView.findViewById(R.id.center_clock); mRightClock = statusBarView.findViewById(R.id.right_clock); + mNetworkTraffic = statusBarView.findViewById(R.id.networkTraffic); applyModeBackground(-1, getMode(), false /*animate*/); applyMode(getMode(), false /*animate*/); } @@ -94,6 +95,7 @@ private void applyMode(int mode, boolean animate) { animateTransitionTo(mClock, newAlphaBC), animateTransitionTo(mCenterClock, newAlphaBC), animateTransitionTo(mRightClock, newAlphaBC) + animateTransitionTo(mNetworkTraffic, newAlpha) ); if (isLightsOut(mode)) { anims.setDuration(LIGHTS_OUT_DURATION); @@ -107,6 +109,7 @@ private void applyMode(int mode, boolean animate) { mClock.setAlpha(newAlphaBC); mCenterClock.setAlpha(newAlphaBC); mRightClock.setAlpha(newAlphaBC); + mNetworkTraffic.setAlpha(newAlpha); } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkTraffic.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkTraffic.java new file mode 100644 index 000000000000..165d248d7bdd --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkTraffic.java @@ -0,0 +1,298 @@ +package com.android.systemui.statusbar.policy; + +import java.text.DecimalFormat; + +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.drawable.Drawable; +import android.graphics.PorterDuff.Mode; +import android.graphics.Rect; +import android.net.ConnectivityManager; +import android.net.NetworkInfo; +import android.net.TrafficStats; +import android.net.Uri; +import android.os.Handler; +import android.os.UserHandle; +import android.os.Message; +import android.os.SystemClock; +import android.provider.Settings; +import android.util.AttributeSet; +import android.util.TypedValue; +import android.view.View; +import android.widget.TextView; + +import com.android.systemui.Dependency; +import com.android.systemui.R; +import com.android.systemui.statusbar.policy.DarkIconDispatcher.DarkReceiver; +/* +* +* 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 implements DarkReceiver { + + private static final int INTERVAL = 1500; //ms + private static final int KB = 1024; + private static final int MB = KB * KB; + private static final int GB = MB * KB; + private static final String symbol = "B/s"; + + private static DecimalFormat decimalFormat = new DecimalFormat("##0.#"); + static { + decimalFormat.setMaximumIntegerDigits(3); + decimalFormat.setMaximumFractionDigits(1); + } + + private boolean mIsEnabled; + private boolean mAttached; + private long totalRxBytes; + private long totalTxBytes; + private long lastUpdateTime; + private int txtSize; + private int txtImgPadding; + private int mAutoHideThreshold; + private int mTintColor; + + private Handler mTrafficHandler = new Handler() { + @Override + public void handleMessage(Message msg) { + long timeDelta = SystemClock.elapsedRealtime() - lastUpdateTime; + + if (timeDelta < INTERVAL * .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, txData, timeDelta)) { + setText(""); + setVisibility(View.GONE); + } else { + // Get information for uplink ready so the line return can be added + String output = formatOutput(timeDelta, txData, symbol); + // Ensure text size is where it needs to be + output += "\n"; + // Add information for downlink if it's called for + output += formatOutput(timeDelta, rxData, symbol); + + // Update view if there's anything new to show + if (! output.contentEquals(getText())) { + setTextSize(TypedValue.COMPLEX_UNIT_PX, (float)txtSize); + setText(output); + } + setVisibility(View.VISIBLE); + } + + // Post delayed message to refresh in ~1000ms + totalRxBytes = newTotalRxBytes; + totalTxBytes = newTotalTxBytes; + clearHandlerCallbacks(); + mTrafficHandler.postDelayed(mRunnable, INTERVAL); + } + + 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 txData, long timeDelta) { + long speedTxKB = (long)(txData / (timeDelta / 1000f)) / KB; + long speedRxKB = (long)(rxData / (timeDelta / 1000f)) / KB; + return !getConnectAvailable() || + (speedRxKB < mAutoHideThreshold && + speedTxKB < mAutoHideThreshold); + } + }; + + private Runnable mRunnable = new Runnable() { + @Override + public void run() { + mTrafficHandler.sendEmptyMessage(0); + } + }; + + class SettingsObserver extends ContentObserver { + SettingsObserver(Handler handler) { + super(handler); + } + + void observe() { + ContentResolver resolver = mContext.getContentResolver(); + resolver.registerContentObserver(Settings.System + .getUriFor(Settings.System.NETWORK_TRAFFIC_STATE), false, + this, UserHandle.USER_ALL); + resolver.registerContentObserver(Settings.System + .getUriFor(Settings.System.NETWORK_TRAFFIC_AUTOHIDE_THRESHOLD), false, + this, UserHandle.USER_ALL); + } + + /* + * @hide + */ + @Override + public void onChange(boolean selfChange) { + setMode(); + updateSettings(); + } + } + + /* + * @hide + */ + public NetworkTraffic(Context context) { + this(context, null); + } + + /* + * @hide + */ + public NetworkTraffic(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + /* + * @hide + */ + public NetworkTraffic(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + final Resources resources = getResources(); + txtSize = resources.getDimensionPixelSize(R.dimen.net_traffic_multi_text_size); + txtImgPadding = resources.getDimensionPixelSize(R.dimen.net_traffic_txt_img_padding); + mTintColor = resources.getColor(android.R.color.white); + Handler mHandler = new Handler(); + SettingsObserver settingsObserver = new SettingsObserver(mHandler); + settingsObserver.observe(); + setMode(); + updateSettings(); + } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + if (!mAttached) { + mAttached = true; + IntentFilter filter = new IntentFilter(); + filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION); + mContext.registerReceiver(mIntentReceiver, filter, null, getHandler()); + } + Dependency.get(DarkIconDispatcher.class).addDarkReceiver(this); + updateSettings(); + } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + if (mAttached) { + mContext.unregisterReceiver(mIntentReceiver); + mAttached = false; + } + Dependency.get(DarkIconDispatcher.class).removeDarkReceiver(this); + } + + 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(); + } + } + }; + + private boolean getConnectAvailable() { + ConnectivityManager connManager = + (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE); + NetworkInfo network = (connManager != null) ? connManager.getActiveNetworkInfo() : null; + return network != null; + } + + private void updateSettings() { + if (mIsEnabled) { + if (getConnectAvailable()) { + if (mAttached) { + totalRxBytes = TrafficStats.getTotalRxBytes(); + lastUpdateTime = SystemClock.elapsedRealtime(); + mTrafficHandler.sendEmptyMessage(1); + } + updateTrafficDrawable(); + return; + } + } else { + clearHandlerCallbacks(); + } + setVisibility(View.GONE); + } + + private void setMode() { + ContentResolver resolver = mContext.getContentResolver(); + mIsEnabled = Settings.System.getIntForUser(resolver, + Settings.System.NETWORK_TRAFFIC_STATE, 1, + UserHandle.USER_CURRENT) == 1; + mAutoHideThreshold = Settings.System.getIntForUser(resolver, + Settings.System.NETWORK_TRAFFIC_AUTOHIDE_THRESHOLD, 1, + UserHandle.USER_CURRENT); + } + + private void clearHandlerCallbacks() { + mTrafficHandler.removeCallbacks(mRunnable); + mTrafficHandler.removeMessages(0); + mTrafficHandler.removeMessages(1); + } + + private void updateTrafficDrawable() { + int intTrafficDrawable; + if (mIsEnabled) { + intTrafficDrawable = R.drawable.stat_sys_network_traffic_updown; + } else { + intTrafficDrawable = 0; + } + if (intTrafficDrawable != 0) { + Drawable d = getContext().getDrawable(intTrafficDrawable); + d.setColorFilter(mTintColor, Mode.SRC_ATOP); + setCompoundDrawablePadding(txtImgPadding); + setCompoundDrawablesWithIntrinsicBounds(null, null, d, null); + } else { + setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0); + } + } + + public void onDensityOrFontScaleChanged() { + final Resources resources = getResources(); + txtSize = resources.getDimensionPixelSize(R.dimen.net_traffic_multi_text_size); + txtImgPadding = resources.getDimensionPixelSize(R.dimen.net_traffic_multi_text_size); + setTextSize(TypedValue.COMPLEX_UNIT_PX, (float)txtSize); + setCompoundDrawablePadding(txtImgPadding); + } + + @Override + public void onDarkChanged(Rect area, float darkIntensity, int tint) { + mTintColor = DarkIconDispatcher.getTint(area, this, tint); + setTextColor(mTintColor); + updateTrafficDrawable(); + } +}