From 7ea84eb2f97fb1bf162cbd2d346f0f6e55561dea Mon Sep 17 00:00:00 2001 From: Jon Haus Date: Sat, 6 Dec 2014 17:19:23 +0100 Subject: [PATCH] lineage-sdk: Add Network Traffic [2/3] Changes made since the original implementation from older branches: *) Forward-ported to Oreo and adapted to Lineage SDK (bgcngm). *) Implement LineageStatusBarItem interfaces to receive visibility and color tint information (bgcngm / sam3000). *) Move from fw/b to lineage-sdk (sam3000). *) Increase refresh interval from 1s to 2s (sam3000). *) Don't generate messages when the statusbar isn't visible (sam3000). *) Allow for choice of unit to be kb/s or Mb/s (and kB/s and MB/s). This deprecates threshold selection for autohide (sam3000). *) Add an option for whether units should be shown in the statusbar (sam3000). *) Various other simplifications (sam3000). *) Added vector drawables (courtesy of kover). Change-Id: Ia5aadc3f03a7b434a047accbd7d53f4aa44c77fb --- .../stat_sys_network_traffic_down.xml | 33 ++ .../drawable/stat_sys_network_traffic_up.xml | 33 ++ .../stat_sys_network_traffic_updown.xml | 33 ++ lineage/res/res/values/dimens.xml | 21 + lineage/res/res/values/strings.xml | 6 + lineage/res/res/values/symbols.xml | 11 + .../lineageos/providers/LineageSettings.java | 48 ++- .../internal/statusbar/NetworkTraffic.java | 371 ++++++++++++++++++ 8 files changed, 555 insertions(+), 1 deletion(-) create mode 100644 lineage/res/res/drawable/stat_sys_network_traffic_down.xml create mode 100644 lineage/res/res/drawable/stat_sys_network_traffic_up.xml create mode 100644 lineage/res/res/drawable/stat_sys_network_traffic_updown.xml create mode 100644 lineage/res/res/values/dimens.xml create mode 100644 sdk/src/java/org/lineageos/internal/statusbar/NetworkTraffic.java diff --git a/lineage/res/res/drawable/stat_sys_network_traffic_down.xml b/lineage/res/res/drawable/stat_sys_network_traffic_down.xml new file mode 100644 index 00000000..4e0efe6e --- /dev/null +++ b/lineage/res/res/drawable/stat_sys_network_traffic_down.xml @@ -0,0 +1,33 @@ + + + + + + + + + diff --git a/lineage/res/res/drawable/stat_sys_network_traffic_up.xml b/lineage/res/res/drawable/stat_sys_network_traffic_up.xml new file mode 100644 index 00000000..12091f3b --- /dev/null +++ b/lineage/res/res/drawable/stat_sys_network_traffic_up.xml @@ -0,0 +1,33 @@ + + + + + + + + + diff --git a/lineage/res/res/drawable/stat_sys_network_traffic_updown.xml b/lineage/res/res/drawable/stat_sys_network_traffic_updown.xml new file mode 100644 index 00000000..80336ab4 --- /dev/null +++ b/lineage/res/res/drawable/stat_sys_network_traffic_updown.xml @@ -0,0 +1,33 @@ + + + + + + + + + diff --git a/lineage/res/res/values/dimens.xml b/lineage/res/res/values/dimens.xml new file mode 100644 index 00000000..b10b58fa --- /dev/null +++ b/lineage/res/res/values/dimens.xml @@ -0,0 +1,21 @@ + + + + 14sp + 8sp + diff --git a/lineage/res/res/values/strings.xml b/lineage/res/res/values/strings.xml index a128891f..0e74a2ca 100644 --- a/lineage/res/res/values/strings.xml +++ b/lineage/res/res/values/strings.xml @@ -151,4 +151,10 @@ Application killed + + + kb/s + Mb/s + kB/s + MB/s diff --git a/lineage/res/res/values/symbols.xml b/lineage/res/res/values/symbols.xml index 813b5f80..bd0995a6 100644 --- a/lineage/res/res/values/symbols.xml +++ b/lineage/res/res/values/symbols.xml @@ -139,4 +139,15 @@ + + + + + + + + + + + diff --git a/sdk/src/java/lineageos/providers/LineageSettings.java b/sdk/src/java/lineageos/providers/LineageSettings.java index 315e37ce..069b7f9d 100644 --- a/sdk/src/java/lineageos/providers/LineageSettings.java +++ b/sdk/src/java/lineageos/providers/LineageSettings.java @@ -1,5 +1,6 @@ /** - * Copyright (c) 2015, The CyanogenMod Project + * Copyright (C) 2015-2016 The CyanogenMod Project + * Copyright (C) 2017-2018 The LineageOS Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -2910,6 +2911,47 @@ public final class LineageSettings { */ public static final String LOCK_SCREEN_WEATHER_ENABLED = "lock_screen_weather_enabled"; + /** + * Network traffic indicator mode + * 0 = Don't show network traffic indicator + * 1 = Display up-stream traffic only + * 2 = Display down-stream traffic only + * 3 = Display both up- and down-stream traffic + * @hide + */ + public static final String NETWORK_TRAFFIC_MODE = "network_traffic_mode"; + + /** @hide */ + public static final Validator NETWORK_TRAFFIC_MODE_VALIDATOR = + new InclusiveIntegerRangeValidator(0, 3); + + /** + * Whether or not to hide the network traffic indicator when there is no activity + * @hide + */ + public static final String NETWORK_TRAFFIC_AUTOHIDE = "network_traffic_autohide"; + + /** @hide */ + public static final Validator NETWORK_TRAFFIC_AUTOHIDE_VALIDATOR = sBooleanValidator; + + /** + * Measurement unit preference for network traffic + * @hide + */ + public static final String NETWORK_TRAFFIC_UNITS = "network_traffic_units"; + + /** @hide */ + public static final Validator NETWORK_TRAFFIC_UNITS_VALIDATOR = + new InclusiveIntegerRangeValidator(0, 3); + + /** + * Whether or not to show measurement units in the network traffic indiciator + * @hide + */ + public static final String NETWORK_TRAFFIC_SHOW_UNITS = "network_traffic_show_units"; + + /** @hide */ + public static final Validator NETWORK_TRAFFIC_SHOW_UNITS_VALIDATOR = sBooleanValidator; // endregion @@ -3016,6 +3058,10 @@ public final class LineageSettings { static { VALIDATORS.put(PROTECTED_COMPONENTS, PROTECTED_COMPONENTS_VALIDATOR); VALIDATORS.put(PROTECTED_COMPONENT_MANAGERS, PROTECTED_COMPONENTS_MANAGER_VALIDATOR); + VALIDATORS.put(NETWORK_TRAFFIC_MODE, NETWORK_TRAFFIC_MODE_VALIDATOR); + VALIDATORS.put(NETWORK_TRAFFIC_AUTOHIDE, NETWORK_TRAFFIC_AUTOHIDE_VALIDATOR); + VALIDATORS.put(NETWORK_TRAFFIC_UNITS, NETWORK_TRAFFIC_UNITS_VALIDATOR); + VALIDATORS.put(NETWORK_TRAFFIC_SHOW_UNITS, NETWORK_TRAFFIC_SHOW_UNITS_VALIDATOR); } /** diff --git a/sdk/src/java/org/lineageos/internal/statusbar/NetworkTraffic.java b/sdk/src/java/org/lineageos/internal/statusbar/NetworkTraffic.java new file mode 100644 index 00000000..8bed2f25 --- /dev/null +++ b/sdk/src/java/org/lineageos/internal/statusbar/NetworkTraffic.java @@ -0,0 +1,371 @@ +/** + * Copyright (C) 2017-2018 The LineageOS project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.lineageos.internal.statusbar; + +import android.animation.ArgbEvaluator; +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.drawable.Drawable; +import android.graphics.PorterDuff; +import android.graphics.Rect; +import android.net.ConnectivityManager; +import android.net.TrafficStats; +import android.os.Handler; +import android.os.UserHandle; +import android.os.Message; +import android.os.SystemClock; +import android.util.AttributeSet; +import android.util.Log; +import android.util.TypedValue; +import android.view.View; +import android.widget.TextView; + +import lineageos.providers.LineageSettings; + +import org.lineageos.platform.internal.R; + +public class NetworkTraffic extends TextView { + private static final String TAG = "NetworkTraffic"; + + private static final int MODE_DISABLED = 0; + private static final int MODE_UPSTREAM_ONLY = 1; + private static final int MODE_DOWNSTREAM_ONLY = 2; + private static final int MODE_UPSTREAM_AND_DOWNSTREAM = 3; + + private static final int MESSAGE_TYPE_PERIODIC_REFRESH = 0; + private static final int MESSAGE_TYPE_UPDATE_VIEW = 1; + + private static final int REFRESH_INTERVAL = 2000; + + private static final int UNITS_KILOBITS = 0; + private static final int UNITS_MEGABITS = 1; + private static final int UNITS_KILOBYTES = 2; + private static final int UNITS_MEGABYTES = 3; + + // Thresholds themselves are always defined in kbps + private static final long AUTOHIDE_THRESHOLD_KILOBITS = 10; + private static final long AUTOHIDE_THRESHOLD_MEGABITS = 100; + private static final long AUTOHIDE_THRESHOLD_KILOBYTES = 8; + private static final long AUTOHIDE_THRESHOLD_MEGABYTES = 80; + + private int mMode = MODE_DISABLED; + private boolean mNetworkTrafficIsVisible; + private long mTxKbps; + private long mRxKbps; + private long mLastTxBytesTotal; + private long mLastRxBytesTotal; + private long mLastUpdateTime; + private int mTextSizeSingle; + private int mTextSizeMulti; + private boolean mAutoHide; + private long mAutoHideThreshold; + private int mUnits; + private boolean mShowUnits; + private int mDarkModeFillColor; + private int mLightModeFillColor; + private int mIconTint = Color.WHITE; + private SettingsObserver mObserver; + private Drawable mDrawable; + + public NetworkTraffic(Context context) { + this(context, null); + } + + public NetworkTraffic(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public NetworkTraffic(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + + final Resources resources = getResources(); + mTextSizeSingle = resources.getDimensionPixelSize(R.dimen.net_traffic_single_text_size); + mTextSizeMulti = resources.getDimensionPixelSize(R.dimen.net_traffic_multi_text_size); + + mNetworkTrafficIsVisible = false; + + mObserver = new SettingsObserver(mTrafficHandler); + } + + private LineageStatusBarItem.DarkReceiver mDarkReceiver = + new LineageStatusBarItem.DarkReceiver() { + public void onDarkChanged(Rect area, float darkIntensity, int tint) { + mIconTint = (int) ArgbEvaluator.getInstance().evaluate(darkIntensity, + mLightModeFillColor, mDarkModeFillColor); + setTextColor(mIconTint); + updateTrafficDrawableColor(); + } + public void setFillColors(int darkColor, int lightColor) { + mDarkModeFillColor = darkColor; + mLightModeFillColor = lightColor; + } + }; + + private LineageStatusBarItem.VisibilityReceiver mVisibilityReceiver = + new LineageStatusBarItem.VisibilityReceiver() { + public void onVisibilityChanged(boolean isVisible) { + if (mNetworkTrafficIsVisible != isVisible) { + mNetworkTrafficIsVisible = isVisible; + updateViewState(); + } + } + }; + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + + LineageStatusBarItem.Manager manager = + LineageStatusBarItem.findManager((View) this); + manager.addDarkReceiver(mDarkReceiver); + manager.addVisibilityReceiver(mVisibilityReceiver); + + mContext.registerReceiver(mIntentReceiver, + new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION)); + mObserver.observe(); + updateSettings(); + } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + mContext.unregisterReceiver(mIntentReceiver); + mObserver.unobserve(); + } + + private Handler mTrafficHandler = new Handler() { + @Override + public void handleMessage(Message msg) { + long now = SystemClock.elapsedRealtime(); + long timeDelta = now - mLastUpdateTime; + if (msg.what == MESSAGE_TYPE_PERIODIC_REFRESH + && timeDelta >= REFRESH_INTERVAL * 0.95f) { + // Update counters + mLastUpdateTime = now; + long txBytes = TrafficStats.getTotalTxBytes() - mLastTxBytesTotal; + long rxBytes = TrafficStats.getTotalRxBytes() - mLastRxBytesTotal; + mTxKbps = (long) (txBytes * 8f / (timeDelta / 1000f) / 1000f); + mRxKbps = (long) (rxBytes * 8f / (timeDelta / 1000f) / 1000f); + mLastTxBytesTotal += txBytes; + mLastRxBytesTotal += rxBytes; + } + + final boolean enabled = mMode != MODE_DISABLED && isConnectionAvailable(); + final boolean showUpstream = + mMode == MODE_UPSTREAM_ONLY || mMode == MODE_UPSTREAM_AND_DOWNSTREAM; + final boolean showDownstream = + mMode == MODE_DOWNSTREAM_ONLY || mMode == MODE_UPSTREAM_AND_DOWNSTREAM; + final boolean shouldHide = mAutoHide && (!showUpstream || mTxKbps < mAutoHideThreshold) + && (!showDownstream || mRxKbps < mAutoHideThreshold); + + if (!enabled || shouldHide) { + setText(""); + setVisibility(GONE); + } else { + // Get information for uplink ready so the line return can be added + StringBuilder output = new StringBuilder(); + if (showUpstream) { + output.append(formatOutput(mTxKbps)); + } + + // Ensure text size is where it needs to be + int textSize; + if (showUpstream && showDownstream) { + output.append("\n"); + textSize = mTextSizeMulti; + } else { + textSize = mTextSizeSingle; + } + + // Add information for downlink if it's called for + if (showDownstream) { + output.append(formatOutput(mRxKbps)); + } + + // Update view if there's anything new to show + if (!output.toString().contentEquals(getText())) { + setTextSize(TypedValue.COMPLEX_UNIT_PX, (float) textSize); + setText(output.toString()); + } + setVisibility(VISIBLE); + } + + // Schedule periodic refresh + mTrafficHandler.removeMessages(MESSAGE_TYPE_PERIODIC_REFRESH); + if (enabled && mNetworkTrafficIsVisible) { + mTrafficHandler.sendEmptyMessageDelayed(MESSAGE_TYPE_PERIODIC_REFRESH, + REFRESH_INTERVAL); + } + } + + private String formatOutput(long kbps) { + final String value; + final String unit; + switch (mUnits) { + case UNITS_KILOBITS: + value = String.format("%d", kbps); + unit = mContext.getString(R.string.kilobitspersecond_short); + break; + case UNITS_MEGABITS: + value = String.format("%.1f", (float) kbps / 1000); + unit = mContext.getString(R.string.megabitspersecond_short); + break; + case UNITS_KILOBYTES: + value = String.format("%d", kbps / 8); + unit = mContext.getString(R.string.kilobytespersecond_short); + break; + case UNITS_MEGABYTES: + value = String.format("%.2f", (float) kbps / 8000); + unit = mContext.getString(R.string.megabytespersecond_short); + break; + default: + value = "unknown"; + unit = "unknown"; + break; + } + + if (mShowUnits) { + return value + " " + unit; + } else { + return value; + } + } + }; + + private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); + if (ConnectivityManager.CONNECTIVITY_ACTION.equals(action)) { + updateViewState(); + } + } + }; + + class SettingsObserver extends ContentObserver { + SettingsObserver(Handler handler) { + super(handler); + } + + void observe() { + ContentResolver resolver = mContext.getContentResolver(); + resolver.registerContentObserver(LineageSettings.Secure.getUriFor( + LineageSettings.Secure.NETWORK_TRAFFIC_MODE), + false, this, UserHandle.USER_ALL); + resolver.registerContentObserver(LineageSettings.Secure.getUriFor( + LineageSettings.Secure.NETWORK_TRAFFIC_AUTOHIDE), + false, this, UserHandle.USER_ALL); + resolver.registerContentObserver(LineageSettings.Secure.getUriFor( + LineageSettings.Secure.NETWORK_TRAFFIC_UNITS), + false, this, UserHandle.USER_ALL); + resolver.registerContentObserver(LineageSettings.Secure.getUriFor( + LineageSettings.Secure.NETWORK_TRAFFIC_SHOW_UNITS), + false, this, UserHandle.USER_ALL); + } + + void unobserve() { + mContext.getContentResolver().unregisterContentObserver(this); + } + + @Override + public void onChange(boolean selfChange) { + updateSettings(); + } + } + + private boolean isConnectionAvailable() { + ConnectivityManager cm = + (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE); + return cm.getActiveNetworkInfo() != null; + } + + private void updateSettings() { + ContentResolver resolver = mContext.getContentResolver(); + + mMode = LineageSettings.Secure.getIntForUser(resolver, + LineageSettings.Secure.NETWORK_TRAFFIC_MODE, 0, UserHandle.USER_CURRENT); + mAutoHide = LineageSettings.Secure.getIntForUser(resolver, + LineageSettings.Secure.NETWORK_TRAFFIC_AUTOHIDE, 0, UserHandle.USER_CURRENT) == 1; + mUnits = LineageSettings.Secure.getIntForUser(resolver, + LineageSettings.Secure.NETWORK_TRAFFIC_UNITS, /* Mbps */ 1, + UserHandle.USER_CURRENT); + + switch (mUnits) { + case UNITS_KILOBITS: + mAutoHideThreshold = AUTOHIDE_THRESHOLD_KILOBITS; + break; + case UNITS_MEGABITS: + mAutoHideThreshold = AUTOHIDE_THRESHOLD_MEGABITS; + break; + case UNITS_KILOBYTES: + mAutoHideThreshold = AUTOHIDE_THRESHOLD_KILOBYTES; + break; + case UNITS_MEGABYTES: + mAutoHideThreshold = AUTOHIDE_THRESHOLD_MEGABYTES; + break; + default: + mAutoHideThreshold = 0; + break; + } + + mShowUnits = LineageSettings.Secure.getIntForUser(resolver, + LineageSettings.Secure.NETWORK_TRAFFIC_SHOW_UNITS, 1, + UserHandle.USER_CURRENT) == 1; + + if (mMode != MODE_DISABLED) { + updateTrafficDrawable(); + } + updateViewState(); + } + + private void updateViewState() { + mTrafficHandler.sendEmptyMessage(MESSAGE_TYPE_UPDATE_VIEW); + } + + private void clearHandlerCallbacks() { + mTrafficHandler.removeMessages(MESSAGE_TYPE_PERIODIC_REFRESH); + mTrafficHandler.removeMessages(MESSAGE_TYPE_UPDATE_VIEW); + } + + private void updateTrafficDrawable() { + final int drawableResId; + if (mMode == MODE_UPSTREAM_AND_DOWNSTREAM) { + drawableResId = R.drawable.stat_sys_network_traffic_updown; + } else if (mMode == MODE_UPSTREAM_ONLY) { + drawableResId = R.drawable.stat_sys_network_traffic_up; + } else if (mMode == MODE_DOWNSTREAM_ONLY) { + drawableResId = R.drawable.stat_sys_network_traffic_down; + } else { + drawableResId = 0; + } + mDrawable = drawableResId != 0 ? getResources().getDrawable(drawableResId) : null; + setCompoundDrawablesWithIntrinsicBounds(null, null, mDrawable, null); + updateTrafficDrawableColor(); + } + + private void updateTrafficDrawableColor() { + if (mDrawable != null) { + mDrawable.setColorFilter(mIconTint, PorterDuff.Mode.SRC_ATOP); + } + } +}