diff --git a/android/BOINC/res/values/configuration.xml b/android/BOINC/res/values/configuration.xml index 378f5b4fa7..054608b80e 100644 --- a/android/BOINC/res/values/configuration.xml +++ b/android/BOINC/res/values/configuration.xml @@ -24,7 +24,7 @@ 1000 1000 10 - 460 + 1 10 diff --git a/android/BOINC/src/edu/berkeley/boinc/BOINCActivity.java b/android/BOINC/src/edu/berkeley/boinc/BOINCActivity.java index 89065b5dc5..58c9664aab 100644 --- a/android/BOINC/src/edu/berkeley/boinc/BOINCActivity.java +++ b/android/BOINC/src/edu/berkeley/boinc/BOINCActivity.java @@ -115,6 +115,8 @@ public class BOINCActivity extends TabActivity { } private void doBindService() { + // start service to allow setForeground later on... + startService(new Intent(this, Monitor.class)); // Establish a connection with the service, onServiceConnected gets called when bindService(new Intent(this, Monitor.class), mConnection, Service.BIND_AUTO_CREATE); } diff --git a/android/BOINC/src/edu/berkeley/boinc/PrefsActivity.java b/android/BOINC/src/edu/berkeley/boinc/PrefsActivity.java index d1a4cd5af5..d90ff61494 100644 --- a/android/BOINC/src/edu/berkeley/boinc/PrefsActivity.java +++ b/android/BOINC/src/edu/berkeley/boinc/PrefsActivity.java @@ -171,8 +171,8 @@ public class PrefsActivity extends FragmentActivity { break; case R.string.prefs_show_notification_header: //app pref appPrefs.setShowNotification(isSet); + ClientNotification.getInstance(getApplicationContext()).update(); // update notification populateLayout(); // updates status text - ClientNotification.getInstance().update(getApplicationContext()); break; case R.string.prefs_show_advanced_header: //app pref appPrefs.setShowAdvanced(isSet); diff --git a/android/BOINC/src/edu/berkeley/boinc/client/ClientNotification.java b/android/BOINC/src/edu/berkeley/boinc/client/ClientNotification.java index 4b4bab575c..05c91174ce 100644 --- a/android/BOINC/src/edu/berkeley/boinc/client/ClientNotification.java +++ b/android/BOINC/src/edu/berkeley/boinc/client/ClientNotification.java @@ -7,15 +7,20 @@ import android.app.NotificationManager; import android.app.PendingIntent; import android.content.Context; import android.content.Intent; +import android.graphics.BitmapFactory; +import android.support.v4.app.NotificationCompat; +import android.util.Log; public class ClientNotification { - //private static final String TAG = "ClientNotification"; + private static final String TAG = "ClientNotification"; private static ClientNotification clientNotification = null; - public Notification notification; + private Context context; + private NotificationManager nm; + private Integer notificationId; + private PendingIntent contentIntent; - private boolean mIsEnabled = true; private int mOldComputingStatus = -1; private int mOldSuspendReason = -1; @@ -24,22 +29,30 @@ public class ClientNotification { * 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(); - + public static synchronized ClientNotification getInstance(Context ctx) { + if (clientNotification == null) { + clientNotification = new ClientNotification(ctx); + } return clientNotification; } + + public ClientNotification (Context ctx) { + this.context = ctx; + this.nm = (NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE); + notificationId = context.getResources().getInteger(R.integer.autostart_notification_id); + Intent intent = new Intent(context, BOINCActivity.class); + intent.setFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT); + contentIntent = PendingIntent.getActivity(context, 0, intent, 0); + } /** * Updates notification with client's current status - * @param context - * @param updatedStatus new status */ - public synchronized void update(Context context) { - // check whether notification is allowed in preferences + public synchronized void update() { + // check whether notification is allowed in preferences if (!Monitor.getAppPrefs().getShowNotification()) { - hide(context); + Log.d(TAG,"cancelling notification"); + nm.cancel(notificationId); return; } @@ -49,61 +62,55 @@ public class ClientNotification { // update notification if (clientNotification.mOldComputingStatus == -1 || updatedStatus.computingStatus.intValue() != clientNotification.mOldComputingStatus - || (updatedStatus.computingStatus == ClientStatus.COMPUTING_STATUS_SUSPENDED && updatedStatus.computingSuspendReason != clientNotification.mOldSuspendReason)) { - if (clientNotification.mIsEnabled) - updateNotification(context, updatedStatus.computingStatus); + || (updatedStatus.computingStatus == ClientStatus.COMPUTING_STATUS_SUSPENDED + && updatedStatus.computingSuspendReason != clientNotification.mOldSuspendReason)) { + + nm.notify(notificationId, buildNotification()); + + // save status for comparison next time clientNotification.mOldComputingStatus = updatedStatus.computingStatus; clientNotification.mOldSuspendReason = updatedStatus.computingSuspendReason; } } - private void updateNotification(Context context, int status) { - switch(status) { - case ClientStatus.COMPUTING_STATUS_NEVER: - show(context, R.drawable.ic_stat_notify_boinc_paused, BOINCActivity.class); - break; - case ClientStatus.COMPUTING_STATUS_SUSPENDED: - show(context, R.drawable.ic_stat_notify_boinc_paused, BOINCActivity.class); - break; - case ClientStatus.COMPUTING_STATUS_IDLE: - show(context, R.drawable.ic_stat_notify_boinc_paused, BOINCActivity.class); - break; - case ClientStatus.COMPUTING_STATUS_COMPUTING: - show(context, R.drawable.ic_stat_notify_boinc_normal, BOINCActivity.class); - break; - } - } - - private void show(Context context, int icon, Class launchActivity) { + private Notification buildNotification() { + // get current client computingstatus + Integer computingStatus = Monitor.getClientStatus().computingStatus; // get status string from ClientStatus String statusText = Monitor.getClientStatus().getCurrentStatusString(); - // get notification ID - Integer notificationId = context.getResources().getInteger(R.integer.autostart_notification_id); - - // Set the icon, scrolling text and time-stamp - notification = new Notification( - icon, - statusText, - 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), - statusText, pendingIntent); - notification.flags |= Notification.FLAG_NO_CLEAR; - - NotificationManager nm = (NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE); - nm.notify(notificationId, notification); + // build notification + Notification notification = new NotificationCompat.Builder(context) + .setContentTitle(context.getString(R.string.app_name)) + .setContentText(statusText) + .setSmallIcon(getIcon(computingStatus)) + .setLargeIcon(BitmapFactory.decodeResource(context.getResources(), getIcon(computingStatus))) + .setContentIntent(contentIntent) + .setOngoing(true) + .build(); + + return notification; } - private void hide(Context context) { - // get notification ID - Integer notificationId = context.getResources().getInteger(R.integer.autostart_notification_id); - - NotificationManager nm = (NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE); - nm.cancel(notificationId); + // returns resource id of icon + private int getIcon(int status) { + int icon; + switch(status) { + case ClientStatus.COMPUTING_STATUS_NEVER: + icon = R.drawable.ic_stat_notify_boinc_paused; + break; + case ClientStatus.COMPUTING_STATUS_SUSPENDED: + icon = R.drawable.ic_stat_notify_boinc_paused; + break; + case ClientStatus.COMPUTING_STATUS_IDLE: + icon = R.drawable.ic_stat_notify_boinc_paused; + break; + case ClientStatus.COMPUTING_STATUS_COMPUTING: + icon = R.drawable.ic_stat_notify_boinc_normal; + break; + default: + icon = R.drawable.ic_stat_notify_boinc_normal; + } + return icon; } } diff --git a/android/BOINC/src/edu/berkeley/boinc/client/Monitor.java b/android/BOINC/src/edu/berkeley/boinc/client/Monitor.java index 2e560feab9..3aaf2ccf00 100644 --- a/android/BOINC/src/edu/berkeley/boinc/client/Monitor.java +++ b/android/BOINC/src/edu/berkeley/boinc/client/Monitor.java @@ -503,7 +503,6 @@ public class Monitor extends Service { Log.d(TAG,"onDestroy()"); // Cancel the persistent notification. - // ((NotificationManager)getSystemService(Service.NOTIFICATION_SERVICE)).cancel(getResources().getInteger(R.integer.autostart_notification_id)); // Abort the ClientMonitorAsync thread @@ -512,12 +511,6 @@ public class Monitor extends Service { monitorThread.interrupt(); clientStatus.setWakeLock(false); // release wakeLock, if held. - - // Quit client here is not appropriate?! - // Keep Client running until explecitely killed, independently from UI - //quitClient(); - - //android.widget.Toast.makeText(this, "BOINC Monitor Service Stopped", android.widget.Toast.LENGTH_SHORT).show(); } @Override @@ -525,28 +518,13 @@ public class Monitor extends Service { //this gets called after startService(intent) (either by BootReceiver or AndroidBOINCActivity, depending on the user's autostart configuration) Log.d(TAG, "onStartCommand()"); /* - * START_NOT_STICKY is now used and replaced START_STICKY in previous implementations. - * Lifecycle events - e.g. killing apps by calling their "onDestroy" methods, or killing an app in the task manager - does not effect the non-Dalvik code like the native BOINC Client. - * Therefore, it is not necessary for the service to get reactivated. When the user navigates back to the app (BOINC Manager), the service gets re-started from scratch. - * Con: After user navigates back, it takes some time until current Client status is present. - * Pro: Saves RAM/CPU. - * + * START_STICKY causes service to stay in memory until stopSelf() is called, even if all + * Activities get destroyed by the system. Important for GUI keep-alive * For detailed service documentation see * http://android-developers.blogspot.com.au/2010/02/service-api-changes-starting-with.html */ - return START_NOT_STICKY; + return START_STICKY; } - - //sends broadcast about login (or register) result for login activity - /* - private void sendLoginResultBroadcast(Integer type, Integer result, String message) { - Intent loginResults = new Intent(); - loginResults.setAction("edu.berkeley.boinc.loginresults"); - loginResults.putExtra("type", type); - loginResults.putExtra("result", result); - loginResults.putExtra("message", message); - getApplicationContext().sendBroadcast(loginResults,null); - }*/ public void restartMonitor() { if(Monitor.monitorActive) { //monitor is already active, launch cancelled @@ -609,6 +587,9 @@ public class Monitor extends Service { // set client status to SETUP_STATUS_CLOSED to adapt layout accordingly getClientStatus().setSetupStatus(ClientStatus.SETUP_STATUS_CLOSED,true); + + //stop service, triggers onDestroy + stopSelf(); } public void setRunMode(Integer mode) { @@ -964,7 +945,7 @@ public class Monitor extends Service { if( (status != null) && (state != null) && (state.results != null) && (state.projects != null) && (transfers != null)) { Monitor.getClientStatus().setClientStatus(status, state.results, state.projects, transfers); // Update status bar notification - ClientNotification.getInstance().update(getApplicationContext()); + ClientNotification.getInstance(getApplicationContext()).update(); } else { Log.d(TAG, "client status connection problem"); }