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/jni" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/rs" 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/assets" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/bundles" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/classes" /> <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/dependency-cache" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/dex" /> <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/23.1.1/jars" />
<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/incremental" /> <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/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/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/res" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/resources" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/rs" /> <excludeFolder url="file://$MODULE_DIR$/build/intermediates/rs" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/symbols" /> <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/outputs" />
<excludeFolder url="file://$MODULE_DIR$/build/tmp" /> <excludeFolder url="file://$MODULE_DIR$/build/tmp" />
</content> </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="sourceFolder" forTests="false" />
<orderEntry type="library" exported="" name="support-v4-22.1.1" level="project" /> <orderEntry type="library" exported="" name="support-v4-23.1.1" level="project" />
<orderEntry type="library" exported="" name="support-annotations-22.1.1" level="project" /> <orderEntry type="library" exported="" name="support-annotations-23.1.1" level="project" />
<orderEntry type="module" module-name="colorPicker" exported="" /> <orderEntry type="module" module-name="colorPicker" exported="" />
</component> </component>
</module> </module>

View File

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

View File

@ -38,6 +38,21 @@
<data android:scheme="package"/> <data android:scheme="package"/>
</intent-filter> </intent-filter>
</receiver> </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> </application>
</manifest> </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; import android.preference.PreferenceManager;
/** /**
Broadcast receiver class to help start the traffic monitoring service when the phone boots up only * Broadcast receiver class to help start the traffic monitoring service when the phone boots up only
if the service is enabled. * if the service is enabled.
*/ */
public class BootReceiver extends BroadcastReceiver { 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 @Override
public void onReceive(Context appContext, Intent bootIntent) { public void onReceive(Context appContext, Intent bootIntent) {
if (PreferenceManager.getDefaultSharedPreferences(appContext).getBoolean("enabled", true)) { if (PreferenceManager.getDefaultSharedPreferences(appContext).getBoolean("enabled", true)) {
appContext.startService(new Intent(appContext, TrafficService.class)); 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; import android.preference.PreferenceActivity;
/** /**
Settings activity that allows the user to start or stop the service and also change the polling * Settings activity that allows the user to start or stop the service and also change the polling
interval * interval
*/ */
public class SettingsActivity extends PreferenceActivity { public class SettingsActivity extends PreferenceActivity {
private boolean isBounded; private boolean isBounded;
private TrafficService backgroundService; 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() { private final ServiceConnection mConnection = new ServiceConnection() {
public void onServiceDisconnected(ComponentName name) { public void onServiceDisconnected(ComponentName name) {
isBounded = false; isBounded = false;
backgroundService = null; backgroundService = null;
} }
public void onServiceConnected(ComponentName name, IBinder service) { public void onServiceConnected(ComponentName name, IBinder service) {
isBounded = true; isBounded = true;
TrafficService.LocalBinder mLocalBinder = (TrafficService.LocalBinder) service; TrafficService.LocalBinder mLocalBinder = (TrafficService.LocalBinder) service;
backgroundService = mLocalBinder.getServerInstance(); backgroundService = mLocalBinder.getServerInstance();
} }
}; };
/** /**
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 @Override
protected void onStart() { protected void onStart() {
super.onStart(); super.onStart();
Intent serviceIntent = new Intent(getApplicationContext(), TrafficService.class); Intent serviceIntent = new Intent(getApplicationContext(), TrafficService.class);
startService(serviceIntent); startService(serviceIntent);
bindService(serviceIntent, mConnection, BIND_AUTO_CREATE); bindService(serviceIntent, mConnection, BIND_AUTO_CREATE);
} }
/** /**
OnStop method of the activity that destroys the connection with the service by unbinding from * OnStop method of the activity that destroys the connection with the service by unbinding from
it * it
*/ */
@Override @Override
protected void onStop() { protected void onStop() {
super.onStop(); super.onStop();
if (isBounded) { if (isBounded) {
unbindService(mConnection); unbindService(mConnection);
isBounded = false; isBounded = false;
} }
} }
/** /**
Post create method that bind each of the preferences to a listener so that updating the * 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 * preferences will fire the listeners to trigger updates to the notification
*/ */
@SuppressWarnings("deprecation") @SuppressWarnings("deprecation")
@Override @Override
protected void onPostCreate(Bundle savedInstanceState) { protected void onPostCreate(Bundle savedInstanceState) {
super.onPostCreate(savedInstanceState); super.onPostCreate(savedInstanceState);
addPreferencesFromResource(R.xml.preferences); 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 * Preference change listener that handles the starting and the stopping of the service
* depending on whether the the switch is toggled or not. * depending on whether the the switch is toggled or not.
*/ */
@Override @Override
public boolean onPreferenceChange(Preference enabledPreference, Object newValue) { public boolean onPreferenceChange(Preference enabledPreference, Object newValue) {
if ((Boolean) newValue) { if ((Boolean) newValue) {
backgroundService.showNotification(); backgroundService.showNotification();
} else { } else {
backgroundService.hideNotification(); backgroundService.hideNotification();
} }
return true; 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 * Preference change listener that handles whether the notification should be displayed
* on the lockscreen depending on whether the the switch is toggled or not. * on the lockscreen depending on whether the the switch is toggled or not.
*/ */
@Override @Override
public boolean onPreferenceChange(Preference enabledPreference, Object newValue) { public boolean onPreferenceChange(Preference enabledPreference, Object newValue) {
backgroundService.visibilityPublic((Boolean) newValue); backgroundService.visibilityPublic((Boolean) newValue);
return true; 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 * Preference change listener that handles whether the notification should be displayed
* on the lockscreen depending on whether the the switch is toggled or not. * on the lockscreen depending on whether the the switch is toggled or not.
*/ */
@Override @Override
public boolean onPreferenceChange(Preference enabledPreference, Object newValue) { public boolean onPreferenceChange(Preference enabledPreference, Object newValue) {
backgroundService.setColor((Integer) newValue); backgroundService.setColor((Integer) newValue);
return true; return true;
} }
}); });
} }
} }

View File

@ -17,9 +17,11 @@ import android.net.TrafficStats;
import android.net.wifi.WifiInfo; import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager; import android.net.wifi.WifiManager;
import android.os.Binder; import android.os.Binder;
import android.os.Build;
import android.os.Handler; import android.os.Handler;
import android.os.IBinder; import android.os.IBinder;
import android.os.Message; import android.os.Message;
import android.os.PowerManager;
import android.preference.PreferenceManager; import android.preference.PreferenceManager;
import android.support.v4.app.NotificationCompat; import android.support.v4.app.NotificationCompat;
import android.telephony.TelephonyManager; import android.telephony.TelephonyManager;
@ -27,334 +29,344 @@ import android.text.format.Formatter;
import android.util.Log; 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 { public class TrafficService extends Service {
/** /**
Custom binder class used for allowing the preference activity to bind to this service so that it * The constant defining the identifier of the notification that is to be shown
may be configured on the fly */
*/ private static final int ID = 9000;
public class LocalBinder extends Binder { /**
* 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() {
/** Log.i("HardwareService", "Creating the hardware service");
The instance of the binder class used by the activity super.onCreate();
*/ final SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
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";
/** Intent ittSettings = new Intent();
The handler class that runs every second to update the notification with the network speed. It ittSettings.setComponent(new ComponentName("com.android.settings", CMP));
also runs every minute to save the amount of data-transferred to the preferences. PendingIntent pitSettings = PendingIntent.getActivity(this, 0, ittSettings, 0);
*/ notBuilder = new NotificationCompat.Builder(this);
private static class NotificationHandler extends Handler { 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));
/** Log.d("HardwareService", "Setting up the service manager and the broadcast receiver");
The value of the amount of data transferred in the previous invocation of the method mgrConnectivity = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
*/ mgrNotifications = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
private long lngPrevious = 0L; mgrWireless = (WifiManager) getSystemService(Context.WIFI_SERVICE);
/** mgrTelephony = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
The instance of the context of the parent service hndNotifier = new NotificationHandler(getApplicationContext());
*/
private final Context ctxContext;
/** if (settings.getBoolean("enabled", true)) {
Simple constructor to initialize the initial value of the previous Log.d("HardwareService", "Screen on; showing the notification");
*/ hndNotifier.sendEmptyMessage(1);
@SuppressLint("CommitPrefEdits") }
public NotificationHandler(Context ctxContext) { recScreen = new BroadcastReceiver() {
this.ctxContext = ctxContext;
}
/** /**
Handler method that updates the notification icon with the current speed. It is a very * Handles the screen-on and the screen off intents to enable or disable the notification.
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 * We don't want to show the notification if the screen is off.
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 @Override
value indicates the b/s. However, we need to convert this value into KB/s for values under 1 public void onReceive(Context ctcContext, Intent ittIntent) {
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); if (ittIntent.getAction().equalsIgnoreCase(Intent.ACTION_SCREEN_OFF)) {
long lngCurrent = TrafficStats.getTotalRxBytes() + TrafficStats.getTotalTxBytes(); Log.d("TrafficService", "Screen off; hiding the notification");
int lngSpeed = (int) (lngCurrent - lngPrevious); hndNotifier.removeMessages(1);
lngPrevious = lngCurrent; 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) { if (ittIntent.getBooleanExtra("state", false)) {
TrafficService.notBuilder.setSmallIcon(R.drawable.wkb000);
} else if (lngSpeed < 1048576L) {
TrafficService.notBuilder.setSmallIcon(R.drawable.wkb000 + (int) (lngSpeed / 1024L)); Log.d("TrafficService", "Airplane mode; hiding the notification");
if (lngSpeed > 1022976) { hndNotifier.removeMessages(1);
TrafficService.notBuilder.setSmallIcon(R.drawable.wkb000 + 1000); hndNotifier.sendEmptyMessage(1);
} } else {
} 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(); Log.d("TrafficService", "Airplane mode; showing the notification");
String strTotal = Formatter.formatFileSize(this.ctxContext, lngTotal); connectivityUpdate();
TrafficService.notBuilder.setContentInfo(strTotal); }
TrafficService.mgrNotifications.notify(ID, TrafficService.notBuilder.build()); } else {
} catch (Exception e) {
Log.e("NotificationHandler", "Error creating notification for speed " + lngSpeed);
}
}
}
/** Log.d("TrafficService", "Connectivity change; updating the notification");
The instance of the handler that updates the notification connectivityUpdate();
*/ }
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;
/** IntentFilter ittScreen = new IntentFilter();
Initializes the service by getting instances of service managers and mainly setting up the ittScreen.addAction(Intent.ACTION_SCREEN_ON);
receiver to receive all the necessary intents that this service is supposed to handle. ittScreen.addAction(Intent.ACTION_SCREEN_OFF);
*/ ittScreen.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED);
@Override ittScreen.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
public void onCreate() { registerReceiver(recScreen, ittScreen);
Log.i("HardwareService", "Creating the hardware service"); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
super.onCreate(); IntentFilter ittSaver = new IntentFilter();
final SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()); ittScreen.addAction(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED);
registerReceiver(recSaver, ittSaver);
}
}
Intent ittSettings = new Intent(); /**
ittSettings.setComponent(new ComponentName("com.android.settings", CMP)); * Updates the notification with the new connectivity information. This method determines the type
PendingIntent pitSettings = PendingIntent.getActivity(this, 0, ittSettings, 0); * of connectivity and updates the notification with the network type and name. If there is no
notBuilder = new NotificationCompat.Builder(this); * information about the active network, this will suppress the notification.
notBuilder.setSmallIcon(R.drawable.wkb000); */
notBuilder.setContentIntent(pitSettings); private void connectivityUpdate() {
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));
Log.d("HardwareService", "Setting up the service manager and the broadcast receiver"); NetworkInfo nifNetwork = mgrConnectivity.getActiveNetworkInfo();
mgrConnectivity = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE); if (nifNetwork != null && nifNetwork.isConnectedOrConnecting()) {
mgrNotifications = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
mgrWireless = (WifiManager) getSystemService(Context.WIFI_SERVICE);
mgrTelephony = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
hndNotifier = new NotificationHandler(getApplicationContext());
if (settings.getBoolean("enabled", true)) { Log.d("TrafficService", "Network connected; showing the notification");
Log.d("HardwareService", "Screen on; showing the notification"); if (nifNetwork.getType() == ConnectivityManager.TYPE_WIFI) {
hndNotifier.sendEmptyMessage(1);
}
recScreen = new BroadcastReceiver() {
/** Log.d("TrafficService", "Connected to a wireless network");
* Handles the screen-on and the screen off intents to enable or disable the notification. WifiInfo wifInfo = mgrWireless.getConnectionInfo();
* We don't want to show the notification if the screen is off. if (wifInfo != null && !wifInfo.getSSID().trim().isEmpty()) {
*/
@Override
public void onReceive(Context ctcContext, Intent ittIntent) {
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"); Log.d("TrafficService", "Unknown network without SSID");
hndNotifier.removeMessages(1); hideNotification();
mgrNotifications.cancel(ID); }
} else if (ittIntent.getAction().equalsIgnoreCase(Intent.ACTION_SCREEN_ON)) { } else {
Log.d("TrafficService", "Screen on; showing the notification"); Log.d("TrafficService", "Connected to a cellular network");
connectivityUpdate(); if (!mgrTelephony.getNetworkOperatorName().trim().isEmpty()) {
} else if (ittIntent.getAction().equalsIgnoreCase(Intent.ACTION_AIRPLANE_MODE_CHANGED)) {
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"); Log.d("TrafficService", "Unknown network without IMSI");
hndNotifier.removeMessages(1); hideNotification();
hndNotifier.sendEmptyMessage(1); }
} else { }
} else {
Log.d("TrafficService", "Airplane mode; showing the notification"); Log.d("TrafficService", "Network disconnected; hiding the notification");
connectivityUpdate(); hideNotification();
} }
} else { }
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(); Log.d("HardwareService", "Stopping the hardware service");
ittScreen.addAction(Intent.ACTION_SCREEN_ON); unregisterReceiver(recScreen);
ittScreen.addAction(Intent.ACTION_SCREEN_OFF); unregisterReceiver(recSaver);
ittScreen.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED); hndNotifier.removeMessages(1);
ittScreen.addAction(ConnectivityManager.CONNECTIVITY_ACTION); mgrNotifications.cancel(ID);
registerReceiver(recScreen, ittScreen); }
}
/** /**
Updates the notification with the new connectivity information. This method determines the type * Helper method that shows the notification by sending the handler a message and building the
of connectivity and updates the notification with the network type and name. If there is no * notification. This is invoked when the preference is toggled.
information about the active network, this will suppress the notification. */
*/ public void showNotification() {
private void connectivityUpdate() {
NetworkInfo nifNetwork = mgrConnectivity.getActiveNetworkInfo(); Log.d("HardwareService", "Showing the notification");
if (nifNetwork != null && nifNetwork.isConnectedOrConnecting()) { 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"); Log.d("HardwareService", "Hiding the notification");
WifiInfo wifInfo = mgrWireless.getConnectionInfo(); mgrNotifications.cancel(ID);
if (wifInfo != null && !wifInfo.getSSID().trim().isEmpty()) { hndNotifier.removeMessages(1);
}
Log.d("TrafficService", wifInfo.getSSID()); /**
notBuilder.setContentTitle(getString(R.string.wireless)); * Helper method that toggles the visibility of the notification on the locksreen depending on the
notBuilder.setContentText(wifInfo.getSSID().replaceAll("^\"|\"$", "")); * value of the preference in the activity
showNotification(); *
} else { * @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(); * Helper method that sets the background color of the notification icon by parsing the RGB value
} * into an int.
} else { *
* @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)); * The handler class that runs every second to update the notification with the network speed. It
notBuilder.setContentText(mgrTelephony.getNetworkOperatorName()); * also runs every minute to save the amount of data-transferred to the preferences.
showNotification(); */
} else { private static class NotificationHandler extends Handler {
Log.d("TrafficService", "Unknown network without IMSI"); /**
hideNotification(); * The instance of the context of the parent service
} */
} private final Context ctxContext;
} else { /**
* 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 * Handler method that updates the notification icon with the current speed. It is a very
the handler, hides the notification and unregisters the receivers. * 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
@Override * the previous value, we get the delta. Since this method is invoked every second, this delta
public void onDestroy() { * 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"); TrafficService.hndNotifier.sendEmptyMessageDelayed(1, 1000L);
unregisterReceiver(recScreen);
hndNotifier.removeMessages(1);
mgrNotifications.cancel(ID);
}
/** long lngCurrent = TrafficStats.getTotalRxBytes() + TrafficStats.getTotalTxBytes();
Helper method that shows the notification by sending the handler a message and building the int lngSpeed = (int) (lngCurrent - lngPrevious);
notification. This is invoked when the preference is toggled. lngPrevious = lngCurrent;
*/
public void showNotification() {
Log.d("HardwareService", "Showing the notification"); try {
mgrNotifications.notify(ID, notBuilder.build());
hndNotifier.removeMessages(1);
hndNotifier.sendEmptyMessage(1);
}
/** if (lngSpeed < 1024) {
Helper method that hides the notification by clearing the handler messages and cancelling the TrafficService.notBuilder.setSmallIcon(R.drawable.wkb000);
notification. This is invoked when the preference is toggled. } else if (lngSpeed < 1048576L) {
*/
public void hideNotification() {
Log.d("HardwareService", "Hiding the notification"); TrafficService.notBuilder.setSmallIcon(R.drawable.wkb000 + (int) (lngSpeed / 1024L));
mgrNotifications.cancel(ID); if (lngSpeed > 1022976) {
hndNotifier.removeMessages(1); 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();
Helper method that toggles the visibility of the notification on the locksreen depending on the String strTotal = Formatter.formatFileSize(this.ctxContext, lngTotal);
value of the preference in the activity 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 * Custom binder class used for allowing the preference activity to bind to this service so that it
*/ * may be configured on the fly
public void visibilityPublic(Boolean visibility) { */
if (visibility) { public class LocalBinder extends Binder {
notBuilder.setVisibility(NotificationCompat.VISIBILITY_PUBLIC);
} else {
notBuilder.setVisibility(NotificationCompat.VISIBILITY_SECRET);
}
}
/** public TrafficService getServerInstance() {
Helper method that sets the background color of the notification icon by parsing the RGB value return TrafficService.this;
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;
}
} }

View File

@ -4,8 +4,10 @@
<string name="cellular">Cellular network connectivity</string> <string name="cellular">Cellular network connectivity</string>
<string name="enable_disable">Shows or hides the notification icon</string> <string name="enable_disable">Shows or hides the notification icon</string>
<string name="enable_switch">Enabled</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="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_summary">Background color of the notification icon</string>
<string name="color_title">Color</string> <string name="color_title">Color</string>
</resources> </resources>

View File

@ -10,6 +10,11 @@
android:key="lockscreen" android:key="lockscreen"
android:summary="@string/lockscreen_summary" android:summary="@string/lockscreen_summary"
android:title="@string/lockscreen_title"/> 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 <com.mridang.colorpicker.ColorPreference
android:defaultValue="@android:color/transparent" android:defaultValue="@android:color/transparent"
android:key="color" android:key="color"

View File

@ -68,20 +68,12 @@
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/assets" /> <excludeFolder url="file://$MODULE_DIR$/build/intermediates/assets" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/bundles" /> <excludeFolder url="file://$MODULE_DIR$/build/intermediates/bundles" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/classes" /> <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/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/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/lint" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/manifests" /> <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/res" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/resources" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/rs" /> <excludeFolder url="file://$MODULE_DIR$/build/intermediates/rs" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/symbols" /> <excludeFolder url="file://$MODULE_DIR$/build/intermediates/symbols" />
<excludeFolder url="file://$MODULE_DIR$/build/outputs" /> <excludeFolder url="file://$MODULE_DIR$/build/outputs" />