diff --git a/app/app.iml b/app/app.iml index c123b6e..64848e5 100644 --- a/app/app.iml +++ b/app/app.iml @@ -65,32 +65,25 @@ - - - - + - - - - - - + + - + - - + + \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index 5b8dcb8..2fd34c0 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -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') -} +} \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index af83aa7..f3b54e5 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -38,6 +38,21 @@ + + + + + + + + + + + diff --git a/app/src/main/java/com/mridang/speedo/BatteryReceiver.java b/app/src/main/java/com/mridang/speedo/BatteryReceiver.java new file mode 100644 index 0000000..35a6362 --- /dev/null +++ b/app/src/main/java/com/mridang/speedo/BatteryReceiver.java @@ -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)); + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/mridang/speedo/BootReceiver.java b/app/src/main/java/com/mridang/speedo/BootReceiver.java index 904d3fe..5b997a2 100644 --- a/app/src/main/java/com/mridang/speedo/BootReceiver.java +++ b/app/src/main/java/com/mridang/speedo/BootReceiver.java @@ -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)); + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/mridang/speedo/PowerReceiver.java b/app/src/main/java/com/mridang/speedo/PowerReceiver.java new file mode 100644 index 0000000..8b16e60 --- /dev/null +++ b/app/src/main/java/com/mridang/speedo/PowerReceiver.java @@ -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)); + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/mridang/speedo/SettingsActivity.java b/app/src/main/java/com/mridang/speedo/SettingsActivity.java index aaf9e90..1b37eb2 100644 --- a/app/src/main/java/com/mridang/speedo/SettingsActivity.java +++ b/app/src/main/java/com/mridang/speedo/SettingsActivity.java @@ -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; + } + }); + } } diff --git a/app/src/main/java/com/mridang/speedo/TrafficService.java b/app/src/main/java/com/mridang/speedo/TrafficService.java index 0efc722..7bc99ac 100644 --- a/app/src/main/java/com/mridang/speedo/TrafficService.java +++ b/app/src/main/java/com/mridang/speedo/TrafficService.java @@ -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; + } + } } \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 67b19bc..7095f82 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -4,8 +4,10 @@ Cellular network connectivity Shows or hides the notification icon Enabled - Show the notification in the lockscreen when the phone is locked + Show the notification in the lockscreen when the device is locked Publicly visible + Disable on low battery + Disable the notification and the service when the device\'s battery is low or when the power saving mode is enabled Background color of the notification icon Color diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml index 6c17014..7838931 100644 --- a/app/src/main/res/xml/preferences.xml +++ b/app/src/main/res/xml/preferences.xml @@ -10,6 +10,11 @@ android:key="lockscreen" android:summary="@string/lockscreen_summary" android:title="@string/lockscreen_title"/> + - - - - - - - - - +