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");
}