Reorganised code and added support for low-power mode and power-saver modes.

This commit is contained in:
Mridang Agarwalla 2016-02-28 15:44:10 +02:00
parent 69710f928a
commit 12b231abbb
11 changed files with 498 additions and 410 deletions

View File

@ -65,32 +65,25 @@
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/jni" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/rs" isTestSource="true" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/assets" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/bundles" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/classes" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/coverage-instrumented-classes" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/dependency-cache" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/dex" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/dex-cache" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/support-v4/22.1.1/jars" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/support-v4/23.1.1/jars" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/jacoco" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/javaResources" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/libs" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/lint" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/manifests" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/ndk" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/pre-dexed" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/proguard" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/res" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/resources" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/rs" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/symbols" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/tmp" />
<excludeFolder url="file://$MODULE_DIR$/build/outputs" />
<excludeFolder url="file://$MODULE_DIR$/build/tmp" />
</content>
<orderEntry type="jdk" jdkName="Android API 22 Platform" jdkType="Android SDK" />
<orderEntry type="jdk" jdkName="Android API 23 Platform" jdkType="Android SDK" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" exported="" name="support-v4-22.1.1" level="project" />
<orderEntry type="library" exported="" name="support-annotations-22.1.1" level="project" />
<orderEntry type="library" exported="" name="support-v4-23.1.1" level="project" />
<orderEntry type="library" exported="" name="support-annotations-23.1.1" level="project" />
<orderEntry type="module" module-name="colorPicker" exported="" />
</component>
</module>

View File

@ -1,15 +1,15 @@
apply plugin: 'com.android.application'
android {
compileSdkVersion 22
buildToolsVersion "22.0.0"
compileSdkVersion 23
buildToolsVersion "23.0.1"
defaultConfig {
applicationId "com.mridang.speedo"
minSdkVersion 14
targetSdkVersion 22
versionCode 1
versionName "1.0"
minSdkVersion 16
targetSdkVersion 23
versionCode 2
versionName "1.1"
}
buildTypes {
release {
@ -21,6 +21,6 @@ android {
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:support-v4:22.1.1'
compile 'com.android.support:support-v4:23.1.1'
compile project(':colorPicker')
}
}

View File

@ -38,6 +38,21 @@
<data android:scheme="package"/>
</intent-filter>
</receiver>
<receiver
android:name=".PowerReceiver"
android:enabled="true">
<intent-filter>
<action android:name="android.os.action.POWER_SAVE_MODE_CHANGED" />
</intent-filter>
</receiver>
<receiver
android:name=".BatteryReceiver"
android:enabled="true">
<intent-filter>
<action android:name="android.intent.action.BATTERY_LOW" />
<action android:name="android.intent.action.BATTERY_OKAY" />
</intent-filter>
</receiver>
</application>
</manifest>

View File

@ -0,0 +1,32 @@
package com.mridang.speedo;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.preference.PreferenceManager;
import android.util.Log;
/**
* Broadcast receiver class to help start or stop the traffic monitoring service when the phone's
* battery level is low or okay.
*/
public class BatteryReceiver extends BroadcastReceiver {
/**
* Receiver method for the phone boot that starts the traffic monitoring service
*/
@Override
public void onReceive(Context appContext, Intent ittIntent) {
Log.v("BatteryReceiver", "Received a battery intent");
if (PreferenceManager.getDefaultSharedPreferences(appContext).getBoolean("enabled", true) &&
PreferenceManager.getDefaultSharedPreferences(appContext).getBoolean("lowpower", false)) {
if (ittIntent.getAction().equals(Intent.ACTION_BATTERY_LOW)) {
Log.i("BatteryReceiver", "Battery low. Stopping service");
appContext.stopService(new Intent(appContext, TrafficService.class));
} else {
Log.i("BatteryReceiver", "Battery okay. Starting service");
appContext.startService(new Intent(appContext, TrafficService.class));
}
}
}
}

View File

@ -6,18 +6,18 @@ import android.content.Intent;
import android.preference.PreferenceManager;
/**
Broadcast receiver class to help start the traffic monitoring service when the phone boots up only
if the service is enabled.
* Broadcast receiver class to help start the traffic monitoring service when the phone boots up only
* if the service is enabled.
*/
public class BootReceiver extends BroadcastReceiver {
/**
Receiver method for the phone boot that starts the traffic monitoring service
*/
@Override
public void onReceive(Context appContext, Intent bootIntent) {
if (PreferenceManager.getDefaultSharedPreferences(appContext).getBoolean("enabled", true)) {
appContext.startService(new Intent(appContext, TrafficService.class));
}
}
/**
* Receiver method for the phone boot that starts the traffic monitoring service
*/
@Override
public void onReceive(Context appContext, Intent bootIntent) {
if (PreferenceManager.getDefaultSharedPreferences(appContext).getBoolean("enabled", true)) {
appContext.startService(new Intent(appContext, TrafficService.class));
}
}
}

View File

@ -0,0 +1,37 @@
package com.mridang.speedo;
import android.annotation.TargetApi;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.PowerManager;
import android.preference.PreferenceManager;
import android.util.Log;
/**
* Broadcast receiver class to help start or stop the traffic monitoring service when the phone's
* battery saver mode is enabled or disabled
*/
public class PowerReceiver extends BroadcastReceiver {
/**
* Receiver method for the phone boot that starts the traffic monitoring service
*/
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
@Override
public void onReceive(Context appContext, Intent bootIntent) {
Log.v("BatteryReceiver", "Received a power intent");
if (PreferenceManager.getDefaultSharedPreferences(appContext).getBoolean("enabled", true) &&
PreferenceManager.getDefaultSharedPreferences(appContext).getBoolean("lowpower", false)) {
PowerManager mgrPower = (PowerManager) appContext.getSystemService(Context.POWER_SERVICE);
if (mgrPower.isPowerSaveMode()) {
Log.i("BatteryReceiver", "Power saving mode enabled. Stopping service");
appContext.stopService(new Intent(appContext, TrafficService.class));
} else {
Log.i("BatteryReceiver", "Power saving mode disabled. Starting service");
appContext.startService(new Intent(appContext, TrafficService.class));
}
}
}
}

View File

@ -9,106 +9,106 @@ import android.preference.Preference;
import android.preference.PreferenceActivity;
/**
Settings activity that allows the user to start or stop the service and also change the polling
interval
* Settings activity that allows the user to start or stop the service and also change the polling
* interval
*/
public class SettingsActivity extends PreferenceActivity {
private boolean isBounded;
private TrafficService backgroundService;
private boolean isBounded;
private TrafficService backgroundService;
/**
Connection class between the activity and the service to be able to invoke service methods
*/
private final ServiceConnection mConnection = new ServiceConnection() {
/**
* Connection class between the activity and the service to be able to invoke service methods
*/
private final ServiceConnection mConnection = new ServiceConnection() {
public void onServiceDisconnected(ComponentName name) {
isBounded = false;
backgroundService = null;
}
public void onServiceDisconnected(ComponentName name) {
isBounded = false;
backgroundService = null;
}
public void onServiceConnected(ComponentName name, IBinder service) {
isBounded = true;
TrafficService.LocalBinder mLocalBinder = (TrafficService.LocalBinder) service;
backgroundService = mLocalBinder.getServerInstance();
}
};
public void onServiceConnected(ComponentName name, IBinder service) {
isBounded = true;
TrafficService.LocalBinder mLocalBinder = (TrafficService.LocalBinder) service;
backgroundService = mLocalBinder.getServerInstance();
}
};
/**
OnStart method of the activity that establishes a connection with the service by binding to it
*/
@Override
protected void onStart() {
super.onStart();
Intent serviceIntent = new Intent(getApplicationContext(), TrafficService.class);
startService(serviceIntent);
bindService(serviceIntent, mConnection, BIND_AUTO_CREATE);
}
/**
* OnStart method of the activity that establishes a connection with the service by binding to it
*/
@Override
protected void onStart() {
super.onStart();
Intent serviceIntent = new Intent(getApplicationContext(), TrafficService.class);
startService(serviceIntent);
bindService(serviceIntent, mConnection, BIND_AUTO_CREATE);
}
/**
OnStop method of the activity that destroys the connection with the service by unbinding from
it
*/
@Override
protected void onStop() {
super.onStop();
if (isBounded) {
unbindService(mConnection);
isBounded = false;
}
}
/**
* OnStop method of the activity that destroys the connection with the service by unbinding from
* it
*/
@Override
protected void onStop() {
super.onStop();
if (isBounded) {
unbindService(mConnection);
isBounded = false;
}
}
/**
Post create method that bind each of the preferences to a listener so that updating the
preferences will fire the listeners to trigger updates to the notification
*/
@SuppressWarnings("deprecation")
@Override
protected void onPostCreate(Bundle savedInstanceState) {
super.onPostCreate(savedInstanceState);
addPreferencesFromResource(R.xml.preferences);
/**
* Post create method that bind each of the preferences to a listener so that updating the
* preferences will fire the listeners to trigger updates to the notification
*/
@SuppressWarnings("deprecation")
@Override
protected void onPostCreate(Bundle savedInstanceState) {
super.onPostCreate(savedInstanceState);
addPreferencesFromResource(R.xml.preferences);
findPreference("enabled").setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
findPreference("enabled").setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
/**
* Preference change listener that handles the starting and the stopping of the service
* depending on whether the the switch is toggled or not.
*/
@Override
public boolean onPreferenceChange(Preference enabledPreference, Object newValue) {
if ((Boolean) newValue) {
backgroundService.showNotification();
} else {
backgroundService.hideNotification();
}
return true;
}
});
/**
* Preference change listener that handles the starting and the stopping of the service
* depending on whether the the switch is toggled or not.
*/
@Override
public boolean onPreferenceChange(Preference enabledPreference, Object newValue) {
if ((Boolean) newValue) {
backgroundService.showNotification();
} else {
backgroundService.hideNotification();
}
return true;
}
});
findPreference("lockscreen").setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
findPreference("lockscreen").setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
/**
* Preference change listener that handles whether the notification should be displayed
* on the lockscreen depending on whether the the switch is toggled or not.
*/
@Override
public boolean onPreferenceChange(Preference enabledPreference, Object newValue) {
backgroundService.visibilityPublic((Boolean) newValue);
return true;
}
});
/**
* Preference change listener that handles whether the notification should be displayed
* on the lockscreen depending on whether the the switch is toggled or not.
*/
@Override
public boolean onPreferenceChange(Preference enabledPreference, Object newValue) {
backgroundService.visibilityPublic((Boolean) newValue);
return true;
}
});
findPreference("color").setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
findPreference("color").setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
/**
* Preference change listener that handles whether the notification should be displayed
* on the lockscreen depending on whether the the switch is toggled or not.
*/
@Override
public boolean onPreferenceChange(Preference enabledPreference, Object newValue) {
backgroundService.setColor((Integer) newValue);
return true;
}
});
}
/**
* Preference change listener that handles whether the notification should be displayed
* on the lockscreen depending on whether the the switch is toggled or not.
*/
@Override
public boolean onPreferenceChange(Preference enabledPreference, Object newValue) {
backgroundService.setColor((Integer) newValue);
return true;
}
});
}
}

View File

@ -17,9 +17,11 @@ import android.net.TrafficStats;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.os.Binder;
import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.PowerManager;
import android.preference.PreferenceManager;
import android.support.v4.app.NotificationCompat;
import android.telephony.TelephonyManager;
@ -27,334 +29,344 @@ import android.text.format.Formatter;
import android.util.Log;
/**
Main service class that monitors the network speed and updates the notification every second
* Main service class that monitors the network speed and updates the notification every second
*/
public class TrafficService extends Service {
/**
Custom binder class used for allowing the preference activity to bind to this service so that it
may be configured on the fly
*/
public class LocalBinder extends Binder {
/**
* The constant defining the identifier of the notification that is to be shown
*/
private static final int ID = 9000;
/**
* The identifier if the component that open from the settings activity
*/
private static final String CMP = "com.android.settings.Settings$DataUsageSummaryActivity";
/**
* The instance of the handler that updates the notification
*/
private static NotificationHandler hndNotifier;
/**
* The instance of the manager of the connectivity services
*/
private static ConnectivityManager mgrConnectivity;
/**
* The instance of the manager of the notification services
*/
private static NotificationManager mgrNotifications;
/**
* The instance of the manager of the wireless services
*/
private static WifiManager mgrWireless;
/**
* The instance of the manager of the telephony services
*/
private static TelephonyManager mgrTelephony;
/**
* The instance of the notification builder to rebuild the notification
*/
private static NotificationCompat.Builder notBuilder;
/**
* The instance of the binder class used by the activity
*/
private final IBinder mBinder = new LocalBinder();
/**
* The instance of the broadcast receiver to handle intents
*/
private BroadcastReceiver recScreen;
/**
* The instance of the broadcast receiver to handle power saver mode intents
*/
private final BroadcastReceiver recSaver = new PowerReceiver();
public TrafficService getServerInstance() {
return TrafficService.this;
}
}
/**
* Initializes the service by getting instances of service managers and mainly setting up the
* receiver to receive all the necessary intents that this service is supposed to handle.
*/
@Override
public void onCreate() {
/**
The instance of the binder class used by the activity
*/
private final IBinder mBinder = new LocalBinder();
/**
The constant defining the identifier of the notification that is to be shown
*/
private static final int ID = 9000;
/**
The identifier if the component that open from the settings activity
*/
private static final String CMP = "com.android.settings.Settings$DataUsageSummaryActivity";
Log.i("HardwareService", "Creating the hardware service");
super.onCreate();
final SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
/**
The handler class that runs every second to update the notification with the network speed. It
also runs every minute to save the amount of data-transferred to the preferences.
*/
private static class NotificationHandler extends Handler {
Intent ittSettings = new Intent();
ittSettings.setComponent(new ComponentName("com.android.settings", CMP));
PendingIntent pitSettings = PendingIntent.getActivity(this, 0, ittSettings, 0);
notBuilder = new NotificationCompat.Builder(this);
notBuilder.setSmallIcon(R.drawable.wkb000);
notBuilder.setContentIntent(pitSettings);
notBuilder.setOngoing(true);
notBuilder.setWhen(0);
notBuilder.setOnlyAlertOnce(true);
notBuilder.setPriority(Integer.MAX_VALUE);
notBuilder.setCategory(NotificationCompat.CATEGORY_SERVICE);
setColor(settings.getInt("color", Color.TRANSPARENT));
visibilityPublic(settings.getBoolean("lockscreen", true));
/**
The value of the amount of data transferred in the previous invocation of the method
*/
private long lngPrevious = 0L;
/**
The instance of the context of the parent service
*/
private final Context ctxContext;
Log.d("HardwareService", "Setting up the service manager and the broadcast receiver");
mgrConnectivity = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
mgrNotifications = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
mgrWireless = (WifiManager) getSystemService(Context.WIFI_SERVICE);
mgrTelephony = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
hndNotifier = new NotificationHandler(getApplicationContext());
/**
Simple constructor to initialize the initial value of the previous
*/
@SuppressLint("CommitPrefEdits")
public NotificationHandler(Context ctxContext) {
this.ctxContext = ctxContext;
}
if (settings.getBoolean("enabled", true)) {
Log.d("HardwareService", "Screen on; showing the notification");
hndNotifier.sendEmptyMessage(1);
}
recScreen = new BroadcastReceiver() {
/**
Handler method that updates the notification icon with the current speed. It is a very
hackish method. We have icons for 1 KB/s to 999 KB/s and 1.0 MB/s to 99.9 MB/s. Every time
the method is invoked, we get the amount of data transferred. By subtracting this value with
the previous value, we get the delta. Since this method is invoked every second, this delta
value indicates the b/s. However, we need to convert this value into KB/s for values under 1
MB/s and we need to convert the value to MB/s for values over 1 MB/s. Since all our icon
images are numbered sequentially we can assume that the R class generated will contain the
integer references of the drawables in the sequential order.
*/
@Override
public void handleMessage(Message msgMessage) {
/**
* Handles the screen-on and the screen off intents to enable or disable the notification.
* We don't want to show the notification if the screen is off.
*/
@Override
public void onReceive(Context ctcContext, Intent ittIntent) {
TrafficService.hndNotifier.sendEmptyMessageDelayed(1, 1000L);
if (ittIntent.getAction().equalsIgnoreCase(Intent.ACTION_SCREEN_OFF)) {
long lngCurrent = TrafficStats.getTotalRxBytes() + TrafficStats.getTotalTxBytes();
int lngSpeed = (int) (lngCurrent - lngPrevious);
lngPrevious = lngCurrent;
Log.d("TrafficService", "Screen off; hiding the notification");
hndNotifier.removeMessages(1);
mgrNotifications.cancel(ID);
} else if (ittIntent.getAction().equalsIgnoreCase(Intent.ACTION_SCREEN_ON)) {
try {
Log.d("TrafficService", "Screen on; showing the notification");
connectivityUpdate();
} else if (ittIntent.getAction().equalsIgnoreCase(Intent.ACTION_AIRPLANE_MODE_CHANGED)) {
if (lngSpeed < 1024) {
TrafficService.notBuilder.setSmallIcon(R.drawable.wkb000);
} else if (lngSpeed < 1048576L) {
if (ittIntent.getBooleanExtra("state", false)) {
TrafficService.notBuilder.setSmallIcon(R.drawable.wkb000 + (int) (lngSpeed / 1024L));
if (lngSpeed > 1022976) {
TrafficService.notBuilder.setSmallIcon(R.drawable.wkb000 + 1000);
}
} else if (lngSpeed <= 10485760) {
TrafficService.notBuilder.setSmallIcon(990 + R.drawable.wkb000
+ (int) (0.5D + (double) (10F * ((float) lngSpeed / 1048576F))));
} else if (lngSpeed <= 103809024) {
TrafficService.notBuilder.setSmallIcon(1080 + R.drawable.wkb000
+ (int) (0.5D + (double) ((float) lngSpeed / 1048576F)));
} else {
TrafficService.notBuilder.setSmallIcon(1180 + R.drawable.wkb000);
}
Log.d("TrafficService", "Airplane mode; hiding the notification");
hndNotifier.removeMessages(1);
hndNotifier.sendEmptyMessage(1);
} else {
Long lngTotal = TrafficStats.getTotalRxBytes() + TrafficStats.getTotalTxBytes();
String strTotal = Formatter.formatFileSize(this.ctxContext, lngTotal);
TrafficService.notBuilder.setContentInfo(strTotal);
TrafficService.mgrNotifications.notify(ID, TrafficService.notBuilder.build());
} catch (Exception e) {
Log.e("NotificationHandler", "Error creating notification for speed " + lngSpeed);
}
}
}
Log.d("TrafficService", "Airplane mode; showing the notification");
connectivityUpdate();
}
} else {
/**
The instance of the handler that updates the notification
*/
private static NotificationHandler hndNotifier;
/**
The instance of the manager of the connectivity services
*/
private static ConnectivityManager mgrConnectivity;
/**
The instance of the manager of the notification services
*/
private static NotificationManager mgrNotifications;
/**
The instance of the manager of the wireless services
*/
private static WifiManager mgrWireless;
/**
The instance of the manager of the telephony services
*/
private static TelephonyManager mgrTelephony;
/**
The instance of the notification builder to rebuild the notification
*/
private static NotificationCompat.Builder notBuilder;
/**
The instance of the broadcast receiver to handle intents
*/
private BroadcastReceiver recScreen;
Log.d("TrafficService", "Connectivity change; updating the notification");
connectivityUpdate();
}
}
};
/**
Initializes the service by getting instances of service managers and mainly setting up the
receiver to receive all the necessary intents that this service is supposed to handle.
*/
@Override
public void onCreate() {
IntentFilter ittScreen = new IntentFilter();
ittScreen.addAction(Intent.ACTION_SCREEN_ON);
ittScreen.addAction(Intent.ACTION_SCREEN_OFF);
ittScreen.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED);
ittScreen.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
registerReceiver(recScreen, ittScreen);
Log.i("HardwareService", "Creating the hardware service");
super.onCreate();
final SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
IntentFilter ittSaver = new IntentFilter();
ittScreen.addAction(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED);
registerReceiver(recSaver, ittSaver);
}
}
Intent ittSettings = new Intent();
ittSettings.setComponent(new ComponentName("com.android.settings", CMP));
PendingIntent pitSettings = PendingIntent.getActivity(this, 0, ittSettings, 0);
notBuilder = new NotificationCompat.Builder(this);
notBuilder.setSmallIcon(R.drawable.wkb000);
notBuilder.setContentIntent(pitSettings);
notBuilder.setOngoing(true);
notBuilder.setWhen(0);
notBuilder.setOnlyAlertOnce(true);
notBuilder.setPriority(Integer.MAX_VALUE);
notBuilder.setCategory(NotificationCompat.CATEGORY_SERVICE);
setColor(settings.getInt("color", Color.TRANSPARENT));
visibilityPublic(settings.getBoolean("lockscreen", true));
/**
* Updates the notification with the new connectivity information. This method determines the type
* of connectivity and updates the notification with the network type and name. If there is no
* information about the active network, this will suppress the notification.
*/
private void connectivityUpdate() {
Log.d("HardwareService", "Setting up the service manager and the broadcast receiver");
mgrConnectivity = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
mgrNotifications = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
mgrWireless = (WifiManager) getSystemService(Context.WIFI_SERVICE);
mgrTelephony = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
hndNotifier = new NotificationHandler(getApplicationContext());
NetworkInfo nifNetwork = mgrConnectivity.getActiveNetworkInfo();
if (nifNetwork != null && nifNetwork.isConnectedOrConnecting()) {
if (settings.getBoolean("enabled", true)) {
Log.d("HardwareService", "Screen on; showing the notification");
hndNotifier.sendEmptyMessage(1);
}
recScreen = new BroadcastReceiver() {
Log.d("TrafficService", "Network connected; showing the notification");
if (nifNetwork.getType() == ConnectivityManager.TYPE_WIFI) {
/**
* Handles the screen-on and the screen off intents to enable or disable the notification.
* We don't want to show the notification if the screen is off.
*/
@Override
public void onReceive(Context ctcContext, Intent ittIntent) {
Log.d("TrafficService", "Connected to a wireless network");
WifiInfo wifInfo = mgrWireless.getConnectionInfo();
if (wifInfo != null && !wifInfo.getSSID().trim().isEmpty()) {
if (ittIntent.getAction().equalsIgnoreCase(Intent.ACTION_SCREEN_OFF)) {
Log.d("TrafficService", wifInfo.getSSID());
notBuilder.setContentTitle(getString(R.string.wireless));
notBuilder.setContentText(wifInfo.getSSID().replaceAll("^\"|\"$", ""));
showNotification();
} else {
Log.d("TrafficService", "Screen off; hiding the notification");
hndNotifier.removeMessages(1);
mgrNotifications.cancel(ID);
} else if (ittIntent.getAction().equalsIgnoreCase(Intent.ACTION_SCREEN_ON)) {
Log.d("TrafficService", "Unknown network without SSID");
hideNotification();
}
} else {
Log.d("TrafficService", "Screen on; showing the notification");
connectivityUpdate();
} else if (ittIntent.getAction().equalsIgnoreCase(Intent.ACTION_AIRPLANE_MODE_CHANGED)) {
Log.d("TrafficService", "Connected to a cellular network");
if (!mgrTelephony.getNetworkOperatorName().trim().isEmpty()) {
if (ittIntent.getBooleanExtra("state", false)) {
Log.d("TrafficService", mgrTelephony.getNetworkOperatorName());
notBuilder.setContentTitle(getString(R.string.cellular));
notBuilder.setContentText(mgrTelephony.getNetworkOperatorName());
showNotification();
} else {
Log.d("TrafficService", "Airplane mode; hiding the notification");
hndNotifier.removeMessages(1);
hndNotifier.sendEmptyMessage(1);
} else {
Log.d("TrafficService", "Unknown network without IMSI");
hideNotification();
}
}
} else {
Log.d("TrafficService", "Airplane mode; showing the notification");
connectivityUpdate();
}
} else {
Log.d("TrafficService", "Network disconnected; hiding the notification");
hideNotification();
}
}
Log.d("TrafficService", "Connectivity change; updating the notification");
connectivityUpdate();
}
}
};
/**
* Called when the service is being stopped. It doesn't do much except clear the message queue of
* the handler, hides the notification and unregisters the receivers.
*/
@Override
public void onDestroy() {
IntentFilter ittScreen = new IntentFilter();
ittScreen.addAction(Intent.ACTION_SCREEN_ON);
ittScreen.addAction(Intent.ACTION_SCREEN_OFF);
ittScreen.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED);
ittScreen.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
registerReceiver(recScreen, ittScreen);
}
Log.d("HardwareService", "Stopping the hardware service");
unregisterReceiver(recScreen);
unregisterReceiver(recSaver);
hndNotifier.removeMessages(1);
mgrNotifications.cancel(ID);
}
/**
Updates the notification with the new connectivity information. This method determines the type
of connectivity and updates the notification with the network type and name. If there is no
information about the active network, this will suppress the notification.
*/
private void connectivityUpdate() {
/**
* Helper method that shows the notification by sending the handler a message and building the
* notification. This is invoked when the preference is toggled.
*/
public void showNotification() {
NetworkInfo nifNetwork = mgrConnectivity.getActiveNetworkInfo();
if (nifNetwork != null && nifNetwork.isConnectedOrConnecting()) {
Log.d("HardwareService", "Showing the notification");
mgrNotifications.notify(ID, notBuilder.build());
hndNotifier.removeMessages(1);
hndNotifier.sendEmptyMessage(1);
}
Log.d("TrafficService", "Network connected; showing the notification");
if (nifNetwork.getType() == ConnectivityManager.TYPE_WIFI) {
/**
* Helper method that hides the notification by clearing the handler messages and cancelling the
* notification. This is invoked when the preference is toggled.
*/
public void hideNotification() {
Log.d("TrafficService", "Connected to a wireless network");
WifiInfo wifInfo = mgrWireless.getConnectionInfo();
if (wifInfo != null && !wifInfo.getSSID().trim().isEmpty()) {
Log.d("HardwareService", "Hiding the notification");
mgrNotifications.cancel(ID);
hndNotifier.removeMessages(1);
}
Log.d("TrafficService", wifInfo.getSSID());
notBuilder.setContentTitle(getString(R.string.wireless));
notBuilder.setContentText(wifInfo.getSSID().replaceAll("^\"|\"$", ""));
showNotification();
} else {
/**
* Helper method that toggles the visibility of the notification on the locksreen depending on the
* value of the preference in the activity
*
* @param visibility A boolean value indicating whether the notification should be visible on the
* lockscreen
*/
public void visibilityPublic(Boolean visibility) {
if (visibility) {
notBuilder.setVisibility(NotificationCompat.VISIBILITY_PUBLIC);
} else {
notBuilder.setVisibility(NotificationCompat.VISIBILITY_SECRET);
}
}
Log.d("TrafficService", "Unknown network without SSID");
hideNotification();
}
} else {
/**
* Helper method that sets the background color of the notification icon by parsing the RGB value
* into an int.
*
* @param color The internal int representation of the RGB color to set as the background colour
*/
public void setColor(Integer color) {
notBuilder.setColor(color);
}
Log.d("TrafficService", "Connected to a cellular network");
if (!mgrTelephony.getNetworkOperatorName().trim().isEmpty()) {
/**
* Binder method to allow the settings activity to bind to the service so the notification can be
* configured and updated while the activity is being toggles.
*
* @see android.app.Service#onBind(android.content.Intent)
*/
@Override
public IBinder onBind(Intent intReason) {
return mBinder;
}
Log.d("TrafficService", mgrTelephony.getNetworkOperatorName());
notBuilder.setContentTitle(getString(R.string.cellular));
notBuilder.setContentText(mgrTelephony.getNetworkOperatorName());
showNotification();
} else {
/**
* The handler class that runs every second to update the notification with the network speed. It
* also runs every minute to save the amount of data-transferred to the preferences.
*/
private static class NotificationHandler extends Handler {
Log.d("TrafficService", "Unknown network without IMSI");
hideNotification();
}
}
} else {
/**
* The instance of the context of the parent service
*/
private final Context ctxContext;
/**
* The value of the amount of data transferred in the previous invocation of the method
*/
private long lngPrevious = 0L;
Log.d("TrafficService", "Network disconnected; hiding the notification");
hideNotification();
}
}
/**
* Simple constructor to initialize the initial value of the previous
*/
@SuppressLint("CommitPrefEdits")
public NotificationHandler(Context ctxContext) {
this.ctxContext = ctxContext;
}
/**
Called when the service is being stopped. It doesn't do much except clear the message queue of
the handler, hides the notification and unregisters the receivers.
*/
@Override
public void onDestroy() {
/**
* Handler method that updates the notification icon with the current speed. It is a very
* hackish method. We have icons for 1 KB/s to 999 KB/s and 1.0 MB/s to 99.9 MB/s. Every time
* the method is invoked, we get the amount of data transferred. By subtracting this value with
* the previous value, we get the delta. Since this method is invoked every second, this delta
* value indicates the b/s. However, we need to convert this value into KB/s for values under 1
* MB/s and we need to convert the value to MB/s for values over 1 MB/s. Since all our icon
* images are numbered sequentially we can assume that the R class generated will contain the
* integer references of the drawables in the sequential order.
*/
@Override
public void handleMessage(Message msgMessage) {
Log.d("HardwareService", "Stopping the hardware service");
unregisterReceiver(recScreen);
hndNotifier.removeMessages(1);
mgrNotifications.cancel(ID);
}
TrafficService.hndNotifier.sendEmptyMessageDelayed(1, 1000L);
/**
Helper method that shows the notification by sending the handler a message and building the
notification. This is invoked when the preference is toggled.
*/
public void showNotification() {
long lngCurrent = TrafficStats.getTotalRxBytes() + TrafficStats.getTotalTxBytes();
int lngSpeed = (int) (lngCurrent - lngPrevious);
lngPrevious = lngCurrent;
Log.d("HardwareService", "Showing the notification");
mgrNotifications.notify(ID, notBuilder.build());
hndNotifier.removeMessages(1);
hndNotifier.sendEmptyMessage(1);
}
try {
/**
Helper method that hides the notification by clearing the handler messages and cancelling the
notification. This is invoked when the preference is toggled.
*/
public void hideNotification() {
if (lngSpeed < 1024) {
TrafficService.notBuilder.setSmallIcon(R.drawable.wkb000);
} else if (lngSpeed < 1048576L) {
Log.d("HardwareService", "Hiding the notification");
mgrNotifications.cancel(ID);
hndNotifier.removeMessages(1);
}
TrafficService.notBuilder.setSmallIcon(R.drawable.wkb000 + (int) (lngSpeed / 1024L));
if (lngSpeed > 1022976) {
TrafficService.notBuilder.setSmallIcon(R.drawable.wkb000 + 1000);
}
} else if (lngSpeed <= 10485760) {
TrafficService.notBuilder.setSmallIcon(990 + R.drawable.wkb000
+ (int) (0.5D + (double) (10F * ((float) lngSpeed / 1048576F))));
} else if (lngSpeed <= 103809024) {
TrafficService.notBuilder.setSmallIcon(1080 + R.drawable.wkb000
+ (int) (0.5D + (double) ((float) lngSpeed / 1048576F)));
} else {
TrafficService.notBuilder.setSmallIcon(1180 + R.drawable.wkb000);
}
/**
Helper method that toggles the visibility of the notification on the locksreen depending on the
value of the preference in the activity
Long lngTotal = TrafficStats.getTotalRxBytes() + TrafficStats.getTotalTxBytes();
String strTotal = Formatter.formatFileSize(this.ctxContext, lngTotal);
TrafficService.notBuilder.setContentInfo(strTotal);
TrafficService.mgrNotifications.notify(ID, TrafficService.notBuilder.build());
} catch (Exception e) {
Log.e("NotificationHandler", "Error creating notification for speed " + lngSpeed);
}
}
}
@param visibility A boolean value indicating whether the notification should be visible on the
lockscreen
*/
public void visibilityPublic(Boolean visibility) {
if (visibility) {
notBuilder.setVisibility(NotificationCompat.VISIBILITY_PUBLIC);
} else {
notBuilder.setVisibility(NotificationCompat.VISIBILITY_SECRET);
}
}
/**
* Custom binder class used for allowing the preference activity to bind to this service so that it
* may be configured on the fly
*/
public class LocalBinder extends Binder {
/**
Helper method that sets the background color of the notification icon by parsing the RGB value
into an int.
@param color The internal int representation of the RGB color to set as the background colour
*/
public void setColor(Integer color) {
notBuilder.setColor(color);
}
/**
Binder method to allow the settings activity to bind to the service so the notification can be
configured and updated while the activity is being toggles.
@see android.app.Service#onBind(android.content.Intent)
*/
@Override
public IBinder onBind(Intent intReason) {
return mBinder;
}
public TrafficService getServerInstance() {
return TrafficService.this;
}
}
}

View File

@ -4,8 +4,10 @@
<string name="cellular">Cellular network connectivity</string>
<string name="enable_disable">Shows or hides the notification icon</string>
<string name="enable_switch">Enabled</string>
<string name="lockscreen_summary">Show the notification in the lockscreen when the phone is locked</string>
<string name="lockscreen_summary">Show the notification in the lockscreen when the device is locked</string>
<string name="lockscreen_title">Publicly visible</string>
<string name="lowpower_title">Disable on low battery</string>
<string name="lowpower_summary">Disable the notification and the service when the device\'s battery is low or when the power saving mode is enabled</string>
<string name="color_summary">Background color of the notification icon</string>
<string name="color_title">Color</string>
</resources>

View File

@ -10,6 +10,11 @@
android:key="lockscreen"
android:summary="@string/lockscreen_summary"
android:title="@string/lockscreen_title"/>
<SwitchPreference
android:defaultValue="false"
android:key="lowpower"
android:summary="@string/lowpower_summary"
android:title="@string/lowpower_title"/>
<com.mridang.colorpicker.ColorPreference
android:defaultValue="@android:color/transparent"
android:key="color"

View File

@ -68,20 +68,12 @@
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/assets" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/bundles" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/classes" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/coverage-instrumented-classes" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/dependency-cache" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/dex" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/dex-cache" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/jacoco" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/javaResources" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/libs" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/lint" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/manifests" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/ndk" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/pre-dexed" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/proguard" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/res" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/resources" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/rs" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/symbols" />
<excludeFolder url="file://$MODULE_DIR$/build/outputs" />