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,13 +6,13 @@ 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
* Receiver method for the phone boot that starts the traffic monitoring service
*/
@Override
public void onReceive(Context appContext, Intent bootIntent) {

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,8 +9,8 @@ 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 {
@ -18,7 +18,7 @@ public class SettingsActivity extends PreferenceActivity {
private TrafficService backgroundService;
/**
Connection class between the activity and the service to be able to invoke service methods
* Connection class between the activity and the service to be able to invoke service methods
*/
private final ServiceConnection mConnection = new ServiceConnection() {
@ -35,7 +35,7 @@ public class SettingsActivity extends PreferenceActivity {
};
/**
OnStart method of the activity that establishes a connection with the service by binding to it
* OnStart method of the activity that establishes a connection with the service by binding to it
*/
@Override
protected void onStart() {
@ -46,8 +46,8 @@ public class SettingsActivity extends PreferenceActivity {
}
/**
OnStop method of the activity that destroys the connection with the service by unbinding from
it
* OnStop method of the activity that destroys the connection with the service by unbinding from
* it
*/
@Override
protected void onStop() {
@ -59,8 +59,8 @@ public class SettingsActivity extends PreferenceActivity {
}
/**
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
* 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

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,138 +29,58 @@ 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 {
public TrafficService getServerInstance() {
return TrafficService.this;
}
}
/**
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
* 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
* The identifier if the component that open from the settings activity
*/
private static final String CMP = "com.android.settings.Settings$DataUsageSummaryActivity";
/**
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 {
/**
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;
/**
Simple constructor to initialize the initial value of the previous
*/
@SuppressLint("CommitPrefEdits")
public NotificationHandler(Context ctxContext) {
this.ctxContext = ctxContext;
}
/**
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) {
TrafficService.hndNotifier.sendEmptyMessageDelayed(1, 1000L);
long lngCurrent = TrafficStats.getTotalRxBytes() + TrafficStats.getTotalTxBytes();
int lngSpeed = (int) (lngCurrent - lngPrevious);
lngPrevious = lngCurrent;
try {
if (lngSpeed < 1024) {
TrafficService.notBuilder.setSmallIcon(R.drawable.wkb000);
} else if (lngSpeed < 1048576L) {
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);
}
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);
}
}
}
/**
The instance of the handler that updates the notification
* The instance of the handler that updates the notification
*/
private static NotificationHandler hndNotifier;
/**
The instance of the manager of the connectivity services
* The instance of the manager of the connectivity services
*/
private static ConnectivityManager mgrConnectivity;
/**
The instance of the manager of the notification services
* The instance of the manager of the notification services
*/
private static NotificationManager mgrNotifications;
/**
The instance of the manager of the wireless services
* The instance of the manager of the wireless services
*/
private static WifiManager mgrWireless;
/**
The instance of the manager of the telephony services
* The instance of the manager of the telephony services
*/
private static TelephonyManager mgrTelephony;
/**
The instance of the notification builder to rebuild the notification
* The instance of the notification builder to rebuild the notification
*/
private static NotificationCompat.Builder notBuilder;
/**
The instance of the broadcast receiver to handle intents
* 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();
/**
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.
* 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() {
@ -236,12 +158,18 @@ public class TrafficService extends Service {
ittScreen.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED);
ittScreen.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
registerReceiver(recScreen, ittScreen);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
IntentFilter ittSaver = new IntentFilter();
ittScreen.addAction(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED);
registerReceiver(recSaver, ittSaver);
}
}
/**
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.
* 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() {
@ -287,21 +215,22 @@ public class TrafficService extends Service {
}
/**
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.
* 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() {
Log.d("HardwareService", "Stopping the hardware service");
unregisterReceiver(recScreen);
unregisterReceiver(recSaver);
hndNotifier.removeMessages(1);
mgrNotifications.cancel(ID);
}
/**
Helper method that shows the notification by sending the handler a message and building the
notification. This is invoked when the preference is toggled.
* 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() {
@ -312,8 +241,8 @@ public class TrafficService extends Service {
}
/**
Helper method that hides the notification by clearing the handler messages and cancelling the
notification. This is invoked when the preference is toggled.
* 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() {
@ -323,11 +252,11 @@ public class TrafficService extends Service {
}
/**
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
* 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) {
@ -338,23 +267,106 @@ public class TrafficService extends Service {
}
/**
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
* 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)
* 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;
}
/**
* 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 {
/**
* 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;
/**
* Simple constructor to initialize the initial value of the previous
*/
@SuppressLint("CommitPrefEdits")
public NotificationHandler(Context ctxContext) {
this.ctxContext = ctxContext;
}
/**
* 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) {
TrafficService.hndNotifier.sendEmptyMessageDelayed(1, 1000L);
long lngCurrent = TrafficStats.getTotalRxBytes() + TrafficStats.getTotalTxBytes();
int lngSpeed = (int) (lngCurrent - lngPrevious);
lngPrevious = lngCurrent;
try {
if (lngSpeed < 1024) {
TrafficService.notBuilder.setSmallIcon(R.drawable.wkb000);
} else if (lngSpeed < 1048576L) {
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);
}
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);
}
}
}
/**
* 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 {
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" />