diff --git a/android/BOINC/res/drawable-hdpi/ic_stat_notify_boinc_normal.png b/android/BOINC/res/drawable-hdpi/ic_stat_notify_boinc_normal.png new file mode 100644 index 0000000000..b87ea6f965 Binary files /dev/null and b/android/BOINC/res/drawable-hdpi/ic_stat_notify_boinc_normal.png differ diff --git a/android/BOINC/res/drawable-hdpi/ic_stat_notify_boinc_paused.png b/android/BOINC/res/drawable-hdpi/ic_stat_notify_boinc_paused.png new file mode 100644 index 0000000000..33780f9ba9 Binary files /dev/null and b/android/BOINC/res/drawable-hdpi/ic_stat_notify_boinc_paused.png differ diff --git a/android/BOINC/res/drawable-ldpi/ic_stat_notify_boinc_normal.png b/android/BOINC/res/drawable-ldpi/ic_stat_notify_boinc_normal.png new file mode 100644 index 0000000000..42dd3fcc9f Binary files /dev/null and b/android/BOINC/res/drawable-ldpi/ic_stat_notify_boinc_normal.png differ diff --git a/android/BOINC/res/drawable-ldpi/ic_stat_notify_boinc_paused.png b/android/BOINC/res/drawable-ldpi/ic_stat_notify_boinc_paused.png new file mode 100644 index 0000000000..b168b24e1e Binary files /dev/null and b/android/BOINC/res/drawable-ldpi/ic_stat_notify_boinc_paused.png differ diff --git a/android/BOINC/res/drawable-mdpi/ic_stat_notify_boinc_normal.png b/android/BOINC/res/drawable-mdpi/ic_stat_notify_boinc_normal.png new file mode 100644 index 0000000000..9acfe941d5 Binary files /dev/null and b/android/BOINC/res/drawable-mdpi/ic_stat_notify_boinc_normal.png differ diff --git a/android/BOINC/res/drawable-mdpi/ic_stat_notify_boinc_paused.png b/android/BOINC/res/drawable-mdpi/ic_stat_notify_boinc_paused.png new file mode 100644 index 0000000000..cf57611d96 Binary files /dev/null and b/android/BOINC/res/drawable-mdpi/ic_stat_notify_boinc_paused.png differ diff --git a/android/BOINC/res/values/strings.xml b/android/BOINC/res/values/strings.xml index eb21d708ec..4b605a7b7c 100644 --- a/android/BOINC/res/values/strings.xml +++ b/android/BOINC/res/values/strings.xml @@ -157,6 +157,9 @@ Autostart BOINC starts on boot up Start BOINC manually + Notification + Shown when client is working + Hidden Used CPU cores (in %) Pause when other CPU usage exceeds… (in %) CPU limit (in %) diff --git a/android/BOINC/src/edu/berkeley/boinc/AppPreferences.java b/android/BOINC/src/edu/berkeley/boinc/AppPreferences.java index e8d48b5ad8..ce72bae2df 100644 --- a/android/BOINC/src/edu/berkeley/boinc/AppPreferences.java +++ b/android/BOINC/src/edu/berkeley/boinc/AppPreferences.java @@ -18,6 +18,7 @@ ******************************************************************************/ package edu.berkeley.boinc; +import edu.berkeley.boinc.client.ClientNotification; import android.content.Context; import android.content.SharedPreferences; import android.util.Log; @@ -29,6 +30,7 @@ public class AppPreferences { private SharedPreferences prefs; private Boolean autostart; + private Boolean showNotification; private Boolean showAdvanced; public void readPrefs (Context ctx) { @@ -37,6 +39,7 @@ public class AppPreferences { } //second parameter of reading function is the initial value after installation. autostart = prefs.getBoolean("autostart", false); + showNotification = prefs.getBoolean("showNotification", true); showAdvanced = prefs.getBoolean("showAdvanced", false); Log.d(TAG, "appPrefs read successful." + autostart + showAdvanced); @@ -52,6 +55,17 @@ public class AppPreferences { public Boolean getAutostart () { return this.autostart; } + + public void setShowNotification(Boolean as) { + SharedPreferences.Editor editor = prefs.edit(); + editor.putBoolean("showNotification", as); + editor.commit(); + this.showNotification = as; + } + + public Boolean getShowNotification() { + return this.showNotification; + } public void setShowAdvanced(Boolean as) { SharedPreferences.Editor editor = prefs.edit(); diff --git a/android/BOINC/src/edu/berkeley/boinc/PrefsActivity.java b/android/BOINC/src/edu/berkeley/boinc/PrefsActivity.java index a7db189cd1..770fc79479 100644 --- a/android/BOINC/src/edu/berkeley/boinc/PrefsActivity.java +++ b/android/BOINC/src/edu/berkeley/boinc/PrefsActivity.java @@ -24,6 +24,7 @@ import edu.berkeley.boinc.adapter.PrefsListAdapter; import edu.berkeley.boinc.adapter.PrefsListItemWrapper; import edu.berkeley.boinc.adapter.PrefsListItemWrapperBool; import edu.berkeley.boinc.adapter.PrefsListItemWrapperDouble; +import edu.berkeley.boinc.client.ClientNotification; import edu.berkeley.boinc.client.Monitor; import edu.berkeley.boinc.rpc.GlobalPreferences; import android.app.AlertDialog; @@ -130,7 +131,8 @@ public class PrefsActivity extends FragmentActivity { Boolean advanced = appPrefs.getShowAdvanced(); data.add(new PrefsListItemWrapper(this,R.string.prefs_category_general,true)); - data.add(new PrefsListItemWrapperBool(this,R.string.prefs_autostart_header,R.string.prefs_category_general,appPrefs.getAutostart())); + data.add(new PrefsListItemWrapperBool(this,R.string.prefs_autostart_header,R.string.prefs_category_general,appPrefs.getAutostart())); + data.add(new PrefsListItemWrapperBool(this,R.string.prefs_show_notification_header,R.string.prefs_category_general,appPrefs.getShowNotification())); data.add(new PrefsListItemWrapperBool(this,R.string.prefs_show_advanced_header,R.string.prefs_category_general,appPrefs.getShowAdvanced())); data.add(new PrefsListItemWrapper(this,R.string.prefs_category_network,true)); data.add(new PrefsListItemWrapperBool(this,R.string.prefs_network_wifi_only_header,R.string.prefs_category_network,clientPrefs.network_wifi_only)); @@ -167,6 +169,12 @@ public class PrefsActivity extends FragmentActivity { appPrefs.setAutostart(isSet); populateLayout(); break; + case R.string.prefs_show_notification_header: //app pref + appPrefs.setShowNotification(isSet); + //TODO: needs to be checked against app design. Is does not look to be at the right place + // Enable/disable notification instantly + ClientNotification.getInstance().enable(getApplicationContext(), isSet); + break; case R.string.prefs_show_advanced_header: //app pref appPrefs.setShowAdvanced(isSet); // call reload of list directly, whithout detour via setDataOutdated and waiting for event. diff --git a/android/BOINC/src/edu/berkeley/boinc/adapter/PrefsListItemWrapperBool.java b/android/BOINC/src/edu/berkeley/boinc/adapter/PrefsListItemWrapperBool.java index 3a294ce0d3..4d2dab497d 100644 --- a/android/BOINC/src/edu/berkeley/boinc/adapter/PrefsListItemWrapperBool.java +++ b/android/BOINC/src/edu/berkeley/boinc/adapter/PrefsListItemWrapperBool.java @@ -46,6 +46,11 @@ public class PrefsListItemWrapperBool extends PrefsListItemWrapper { status_true = ctx.getString(R.string.prefs_autostart_true); status_false = ctx.getString(R.string.prefs_autostart_false); break; + case R.string.prefs_show_notification_header: + header = ctx.getString(R.string.prefs_show_notification_header); + status_true = ctx.getString(R.string.prefs_show_notification_true); + status_false = ctx.getString(R.string.prefs_show_notification_false); + break; case R.string.prefs_run_on_battery_header: header = ctx.getString(R.string.prefs_run_on_battery_header); status_true = ctx.getString(R.string.prefs_run_on_battery_true); diff --git a/android/BOINC/src/edu/berkeley/boinc/client/ClientNotification.java b/android/BOINC/src/edu/berkeley/boinc/client/ClientNotification.java new file mode 100644 index 0000000000..11972fbfe0 --- /dev/null +++ b/android/BOINC/src/edu/berkeley/boinc/client/ClientNotification.java @@ -0,0 +1,105 @@ +package edu.berkeley.boinc.client; + +import edu.berkeley.boinc.BOINCActivity; +import edu.berkeley.boinc.R; +import android.app.Notification; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.content.Context; +import android.content.Intent; + +public class ClientNotification { + private static final String TAG = "ClientNotification"; + + private static final int NOTIFICATION_ID = 460; + + private static ClientNotification clientNotification = null; + + private boolean mIsEnabled = true; + private int mOldComputingStatus = -1; + + private ClientNotification() { + + } + + /** + * Returns a reference to a singleton ClientNotification object. + * Constructs a new instance of the ClientNotification if not already constructed. + * @return ClientNotification static instance + */ + public static synchronized ClientNotification getInstance() { + if (clientNotification == null) + clientNotification = new ClientNotification(); + + return clientNotification; + } + + /** + * Updates notification with client's current status + * @param context + * @param updatedStatus new status + */ + public synchronized void update(Context context, ClientStatus updatedStatus) { + if (clientNotification.mOldComputingStatus == -1 + || updatedStatus.computingStatus.intValue() != clientNotification.mOldComputingStatus) { + if (clientNotification.mIsEnabled) + updateNotification(context, updatedStatus.computingStatus); + clientNotification.mOldComputingStatus = updatedStatus.computingStatus; + } + } + + /** + * Set notification enabled/disabled + * @param context + * @param enabled + */ + public synchronized void enable(Context context, boolean enabled) { + clientNotification.mIsEnabled = enabled; + if (clientNotification.mIsEnabled) { + if (clientNotification.mOldComputingStatus != -1) + updateNotification(context, clientNotification.mOldComputingStatus); + } else { + hide(context); + } + } + + private void updateNotification(Context context, int status) { + switch(status) { + case ClientStatus.COMPUTING_STATUS_NEVER: +// hide(context); +// break; + case ClientStatus.COMPUTING_STATUS_SUSPENDED: + case ClientStatus.COMPUTING_STATUS_IDLE: + show(context, R.drawable.ic_stat_notify_boinc_paused, R.string.status_idle, BOINCActivity.class); + break; + case ClientStatus.COMPUTING_STATUS_COMPUTING: + show(context, R.drawable.ic_stat_notify_boinc_normal, R.string.status_running, BOINCActivity.class); + break; + } + } + + private void show(Context context, int icon, int message, Class launchActivity) { + + // Set the icon, scrolling text and time-stamp + Notification notification = new Notification( + icon, + context.getText(message), + System.currentTimeMillis()); + // The PendingIntent to launch activity + PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, + new Intent(context, launchActivity), 0); + + // Set the info for the views that show in the notification panel. + notification.setLatestEventInfo(context, context.getText(R.string.app_name), + context.getText(message), pendingIntent); + notification.flags |= Notification.FLAG_NO_CLEAR; + + NotificationManager nm = (NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE); + nm.notify(NOTIFICATION_ID, notification); + } + + private void hide(Context context) { + NotificationManager nm = (NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE); + nm.cancel(NOTIFICATION_ID); + } +} diff --git a/android/BOINC/src/edu/berkeley/boinc/client/Monitor.java b/android/BOINC/src/edu/berkeley/boinc/client/Monitor.java index cb83dd7fbe..6726f35f8e 100644 --- a/android/BOINC/src/edu/berkeley/boinc/client/Monitor.java +++ b/android/BOINC/src/edu/berkeley/boinc/client/Monitor.java @@ -922,6 +922,8 @@ public class Monitor extends Service { if( (status != null) && (results != null) && (projects != null) && (transfers != null)) { Monitor.getClientStatus().setClientStatus(status, results, projects, transfers); + // Update status bar notification + ClientNotification.getInstance().update(getApplicationContext(), getClientStatus()); } else { Log.d(TAG, "client status connection problem"); }