android: service sticky for GUI keep alive

This commit is contained in:
Joachim Fritzsch 2013-05-18 11:14:22 +02:00
parent 6131a3c6bb
commit 985d91489b
5 changed files with 76 additions and 86 deletions

View File

@ -24,7 +24,7 @@
<integer name="monitor_refresh_rate_ms">1000</integer>
<integer name="monitor_setup_connection_retry_rate_ms">1000</integer>
<integer name="monitor_setup_connection_retry_attempts">10</integer>
<integer name="autostart_notification_id">460</integer>
<integer name="autostart_notification_id">1</integer>
<!-- Device status -->
<integer name="minimum_device_status_refresh_rate_in_monitor_loops">10</integer>
<!-- configuration on tab layout -->

View File

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

View File

@ -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);

View File

@ -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;
}
}

View File

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