Prepare new maemo release
[maemo-rb.git] / android / src / org / rockbox / Helper / RunForegroundManager.java
blob5e3f38eb8455c7c435229ce922c6bb9eddf4fe11
1 package org.rockbox.Helper;
3 import java.lang.reflect.Method;
4 import org.rockbox.R;
5 import org.rockbox.RockboxActivity;
6 import android.app.Notification;
7 import android.app.NotificationManager;
8 import android.app.PendingIntent;
9 import android.app.Service;
10 import android.content.Intent;
11 import android.content.res.Resources;
12 import android.graphics.Bitmap;
13 import android.graphics.BitmapFactory;
14 import android.graphics.drawable.Drawable;
15 import android.os.Handler;
16 import android.widget.RemoteViews;
18 public class RunForegroundManager
20 /* all below is heavily based on the examples found on
21 * http://developer.android.com/reference/android/app/Service.html#setForeground(boolean)
23 private Notification mNotification;
24 private NotificationManager mNM;
25 private IRunForeground api;
26 private Service mCurrentService;
27 private Handler mServiceHandler;
28 private Intent mWidgetUpdate;
29 private int iconheight;
31 public RunForegroundManager(Service service)
33 mCurrentService = service;
34 mNM = (NotificationManager)
35 service.getSystemService(Service.NOTIFICATION_SERVICE);
36 RemoteViews views = new RemoteViews(service.getPackageName(), R.layout.statusbar);
37 /* create Intent for clicking on the expanded notifcation area */
38 Intent intent = new Intent(service, RockboxActivity.class);
39 intent = intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
41 /* retrieve height of launcher icon. Used to scale down album art. */
42 Resources resources = service.getResources();
43 Drawable draw = resources.getDrawable(R.drawable.launcher);
44 iconheight = draw.getIntrinsicHeight();
46 mNotification = new Notification();
47 mNotification.tickerText = service.getString(R.string.notification);
48 mNotification.icon = R.drawable.notification;
49 mNotification.contentView = views;
50 mNotification.flags |= Notification.FLAG_ONGOING_EVENT;
51 mNotification.contentIntent = PendingIntent.getActivity(service, 0, intent, 0);
53 try {
54 api = new NewForegroundApi(R.string.notification, mNotification);
55 } catch (Throwable t) {
56 /* Throwable includes Exception and the expected
57 * NoClassDefFoundError for Android 1.x */
58 try {
59 api = new OldForegroundApi();
60 Logger.i("RunForegroundManager: Falling back to compatibility API");
61 } catch (Exception e) {
62 Logger.e("Cannot run in foreground: No available API");
65 mServiceHandler = new Handler(service.getMainLooper());
68 public void startForeground()
70 /*
71 * Send the notification.
72 * We use a layout id because it is a unique number.
73 * We use it later to cancel.
75 mNM.notify(R.string.notification, mNotification);
77 * this call makes the service run as foreground, which
78 * provides enough cpu time to do music decoding in the
79 * background
81 api.startForeground();
84 public void stopForeground()
86 /* Note to cancel BEFORE changing the
87 * foreground state, since we could be killed at that point.
89 mNM.cancel(R.string.notification);
90 api.stopForeground();
91 mWidgetUpdate = null;
94 public void updateNotification(final String title, final String artist, final String album, final String albumart)
96 /* do this on the main thread for 2 reasons
97 * 1) Don't delay track switching with possibly costly albumart
98 * loading (i.e. off-load from the Rockbox thread)
99 * 2) Work around a bug in Android where decodeFile() fails outside
100 * of the main thread (http://stackoverflow.com/q/7228633)
102 mServiceHandler.post(new Runnable()
104 @Override
105 public void run()
107 final RemoteViews views = mNotification.contentView;
108 views.setTextViewText(R.id.title, title);
109 views.setTextViewText(R.id.content, artist+"\n"+album);
110 if (artist.equals(""))
111 mNotification.tickerText = title;
112 else
113 mNotification.tickerText = title+" - "+artist;
115 if (albumart != null) {
116 /* The notification area doesn't have permissions to access the SD card.
117 * Push the data as Bitmap instead of Uri. Scale down to size of
118 * launcher icon -- broadcasting the unscaled image may yield in
119 * too much data, causing UI hangs of Rockbox. */
120 Bitmap b = BitmapFactory.decodeFile(albumart);
121 if(b != null) {
122 /* scale width to keep aspect ratio -- height is the constraint */
123 int scaledwidth = Math.round(iconheight*((float)b.getWidth()/b.getHeight()));
124 views.setImageViewBitmap(R.id.artwork,
125 Bitmap.createScaledBitmap(b, scaledwidth, iconheight, false));
127 else {
128 views.setImageViewResource(R.id.artwork, R.drawable.launcher);
131 else {
132 views.setImageViewResource(R.id.artwork, R.drawable.launcher);
134 mWidgetUpdate = new Intent("org.rockbox.TrackUpdateInfo");
135 mWidgetUpdate.putExtra("title", title);
136 mWidgetUpdate.putExtra("artist", artist);
137 mWidgetUpdate.putExtra("album", album);
138 mWidgetUpdate.putExtra("albumart", albumart);
139 mCurrentService.sendBroadcast(mWidgetUpdate);
141 /* notify in this runnable to make sure the notification
142 * has the correct albumart */
143 mNM.notify(R.string.notification, mNotification);
145 });
148 public void resendUpdateNotification()
150 if (mWidgetUpdate != null)
151 mCurrentService.sendBroadcast(mWidgetUpdate);
154 public void finishNotification()
156 Logger.d("TrackFinish");
157 Intent widgetUpdate = new Intent("org.rockbox.TrackFinish");
158 mCurrentService.sendBroadcast(widgetUpdate);
161 private interface IRunForeground
163 void startForeground();
164 void stopForeground();
167 private class NewForegroundApi implements IRunForeground
169 int id;
170 Notification mNotification;
171 NewForegroundApi(int _id, Notification _notification)
173 id = _id;
174 mNotification = _notification;
177 public void startForeground()
179 mCurrentService.startForeground(id, mNotification);
182 public void stopForeground()
184 mCurrentService.stopForeground(true);
188 private class OldForegroundApi implements IRunForeground
191 * Get the new API through reflection because it's unavailable
192 * in honeycomb
194 private Method mSetForeground;
196 public OldForegroundApi() throws SecurityException, NoSuchMethodException
198 mSetForeground = getClass().getMethod("setForeground",
199 new Class[] { boolean.class });
202 public void startForeground()
204 try {
205 mSetForeground.invoke(mCurrentService, Boolean.TRUE);
206 } catch (Exception e) {
207 Logger.e("startForeground compat error: " + e.getMessage());
208 e.printStackTrace();
211 public void stopForeground()
213 try {
214 mSetForeground.invoke(mCurrentService, Boolean.FALSE);
215 } catch (Exception e) {
216 Logger.e("stopForeground compat error: " + e.getMessage());