1 package org
.rockbox
.Helper
;
3 import java
.lang
.reflect
.Method
;
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);
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 */
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()
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
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
);
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()
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
;
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
);
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));
128 views
.setImageViewResource(R
.id
.artwork
, R
.drawable
.launcher
);
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
);
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
170 Notification mNotification
;
171 NewForegroundApi(int _id
, Notification _notification
)
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
194 private Method mSetForeground
;
196 public OldForegroundApi() throws SecurityException
, NoSuchMethodException
198 mSetForeground
= getClass().getMethod("setForeground",
199 new Class
[] { boolean.class });
202 public void startForeground()
205 mSetForeground
.invoke(mCurrentService
, Boolean
.TRUE
);
206 } catch (Exception e
) {
207 Logger
.e("startForeground compat error: " + e
.getMessage());
211 public void stopForeground()
214 mSetForeground
.invoke(mCurrentService
, Boolean
.FALSE
);
215 } catch (Exception e
) {
216 Logger
.e("stopForeground compat error: " + e
.getMessage());