diff options
-rw-r--r-- | android/src/org/rockbox/Helper/RunForegroundManager.java | 149 |
1 files changed, 62 insertions, 87 deletions
diff --git a/android/src/org/rockbox/Helper/RunForegroundManager.java b/android/src/org/rockbox/Helper/RunForegroundManager.java index 6ecb16a408..67e1ef4f8f 100644 --- a/android/src/org/rockbox/Helper/RunForegroundManager.java +++ b/android/src/org/rockbox/Helper/RunForegroundManager.java | |||
@@ -16,12 +16,8 @@ import android.widget.RemoteViews; | |||
16 | 16 | ||
17 | public class RunForegroundManager | 17 | public class RunForegroundManager |
18 | { | 18 | { |
19 | /* all below is heavily based on the examples found on | ||
20 | * http://developer.android.com/reference/android/app/Service.html#setForeground(boolean) | ||
21 | */ | ||
22 | private Notification mNotification; | 19 | private Notification mNotification; |
23 | private NotificationManager mNM; | 20 | private NotificationManager mNM; |
24 | private IRunForeground api; | ||
25 | private Service mCurrentService; | 21 | private Service mCurrentService; |
26 | private Intent mWidgetUpdate; | 22 | private Intent mWidgetUpdate; |
27 | 23 | ||
@@ -42,12 +38,7 @@ public class RunForegroundManager | |||
42 | mNotification.flags |= Notification.FLAG_ONGOING_EVENT; | 38 | mNotification.flags |= Notification.FLAG_ONGOING_EVENT; |
43 | mNotification.contentIntent = PendingIntent.getActivity(service, 0, intent, 0); | 39 | mNotification.contentIntent = PendingIntent.getActivity(service, 0, intent, 0); |
44 | 40 | ||
45 | try { | 41 | initForegroundCompat(); |
46 | api = new newForegroundApi(R.string.notification, mNotification); | ||
47 | } catch (NoSuchMethodException e) { | ||
48 | /* Fall back on the old API */ | ||
49 | api = new oldForegroundApi(); | ||
50 | } | ||
51 | } | 42 | } |
52 | 43 | ||
53 | private void LOG(CharSequence text) | 44 | private void LOG(CharSequence text) |
@@ -62,27 +53,12 @@ public class RunForegroundManager | |||
62 | 53 | ||
63 | public void startForeground() | 54 | public void startForeground() |
64 | { | 55 | { |
65 | /* | 56 | startForegroundCompat(R.string.notification, mNotification); |
66 | * Send the notification. | ||
67 | * We use a layout id because it is a unique number. | ||
68 | * We use it later to cancel. | ||
69 | */ | ||
70 | mNM.notify(R.string.notification, mNotification); | ||
71 | /* | ||
72 | * this call makes the service run as foreground, which | ||
73 | * provides enough cpu time to do music decoding in the | ||
74 | * background | ||
75 | */ | ||
76 | api.startForeground(); | ||
77 | } | 57 | } |
78 | 58 | ||
79 | public void stopForeground() | 59 | public void stopForeground() |
80 | { | 60 | { |
81 | /* Note to cancel BEFORE changing the | 61 | stopForegroundCompat(R.string.notification); |
82 | * foreground state, since we could be killed at that point. | ||
83 | */ | ||
84 | mNM.cancel(R.string.notification); | ||
85 | api.stopForeground(); | ||
86 | mWidgetUpdate = null; | 62 | mWidgetUpdate = null; |
87 | } | 63 | } |
88 | 64 | ||
@@ -118,74 +94,73 @@ public class RunForegroundManager | |||
118 | mCurrentService.sendBroadcast(widgetUpdate); | 94 | mCurrentService.sendBroadcast(widgetUpdate); |
119 | } | 95 | } |
120 | 96 | ||
121 | private interface IRunForeground | 97 | /* Loosely based on http://developer.android.com/reference/android/app/Service.html#startForeground(int, android.app.Notification) */ |
122 | { | 98 | private static final Class<?>[] mSetForegroundSignature = new Class[] {boolean.class}; |
123 | void startForeground(); | 99 | private static final Class<?>[] mStartForegroundSignature = new Class[] {int.class, Notification.class}; |
124 | void stopForeground(); | 100 | private static final Class<?>[] mStopForegroundSignature = new Class[] {boolean.class}; |
125 | } | ||
126 | 101 | ||
127 | private class newForegroundApi implements IRunForeground | 102 | private Method mSetForeground; |
128 | { | 103 | private Method mStartForeground; |
129 | Class<?>[] mStartForegroundSignature = | 104 | private Method mStopForeground; |
130 | new Class[] { int.class, Notification.class }; | ||
131 | Class<?>[] mStopForegroundSignature = | ||
132 | new Class[] { boolean.class }; | ||
133 | private Method mStartForeground; | ||
134 | private Method mStopForeground; | ||
135 | private Object[] mStartForegroundArgs = new Object[2]; | ||
136 | private Object[] mStopForegroundArgs = new Object[1]; | ||
137 | |||
138 | newForegroundApi(int id, Notification notification) | ||
139 | throws SecurityException, NoSuchMethodException | ||
140 | { | ||
141 | /* | ||
142 | * Get the new API through reflection | ||
143 | */ | ||
144 | mStartForeground = Service.class.getMethod("startForeground", | ||
145 | mStartForegroundSignature); | ||
146 | mStopForeground = Service.class.getMethod("stopForeground", | ||
147 | mStopForegroundSignature); | ||
148 | mStartForegroundArgs[0] = id; | ||
149 | mStartForegroundArgs[1] = notification; | ||
150 | mStopForegroundArgs[0] = Boolean.TRUE; | ||
151 | } | ||
152 | 105 | ||
153 | public void startForeground() | 106 | private void initForegroundCompat() { |
154 | { | 107 | Class<?> serviceClass = mCurrentService.getClass(); |
108 | try { | ||
109 | mStartForeground = serviceClass.getMethod("startForeground", mStartForegroundSignature); | ||
110 | mStopForeground = serviceClass.getMethod("stopForeground", mStopForegroundSignature); | ||
111 | } catch (NoSuchMethodException e) { | ||
112 | // Running on an older platform. | ||
113 | mStartForeground = mStopForeground = null; | ||
155 | try { | 114 | try { |
156 | mStartForeground.invoke(mCurrentService, mStartForegroundArgs); | 115 | mSetForeground = serviceClass.getMethod("setForeground", mSetForegroundSignature); |
157 | } catch (InvocationTargetException e) { | 116 | } catch (NoSuchMethodException e2) { |
158 | /* Should not happen. */ | 117 | throw new IllegalStateException("OS doesn't have Service.startForeground nor Service.setForeground!", e2); |
159 | LOG("Unable to invoke startForeground", e); | ||
160 | } catch (IllegalAccessException e) { | ||
161 | /* Should not happen. */ | ||
162 | LOG("Unable to invoke startForeground", e); | ||
163 | } | 118 | } |
164 | } | 119 | } |
120 | } | ||
165 | 121 | ||
166 | public void stopForeground() | 122 | private void invokeMethod(Method method, Object[] args) { |
167 | { | 123 | try { |
168 | try { | 124 | method.invoke(mCurrentService, args); |
169 | mStopForeground.invoke(mCurrentService, mStopForegroundArgs); | 125 | } catch (Exception e) { |
170 | } catch (InvocationTargetException e) { | 126 | // Should not happen. |
171 | /* Should not happen. */ | 127 | Log.w("Rockbox", "Unable to invoke method", e); |
172 | LOG("Unable to invoke stopForeground", e); | ||
173 | } catch (IllegalAccessException e) { | ||
174 | /* Should not happen. */ | ||
175 | LOG("Unable to invoke stopForeground", e); | ||
176 | } | ||
177 | } | 128 | } |
178 | } | 129 | } |
179 | 130 | ||
180 | private class oldForegroundApi implements IRunForeground | 131 | /** |
181 | { | 132 | * This is a wrapper around the new startForeground method, using the older |
182 | public void startForeground() | 133 | * APIs if it is not available. |
183 | { | 134 | */ |
184 | mCurrentService.setForeground(false); | 135 | private void startForegroundCompat(int id, Notification notification) { |
136 | // If we have the new startForeground API, then use it. | ||
137 | if (mStartForeground != null) { | ||
138 | Object[] startForeGroundArgs = new Object[] {Integer.valueOf(id), notification}; | ||
139 | invokeMethod(mStartForeground, startForeGroundArgs); | ||
140 | } else { | ||
141 | // Fall back on the old API. | ||
142 | Object[] setForegroundArgs = new Object[] {Boolean.TRUE}; | ||
143 | invokeMethod(mSetForeground, setForegroundArgs); | ||
144 | mNM.notify(id, notification); | ||
145 | } | ||
146 | } | ||
147 | |||
148 | /** | ||
149 | * This is a wrapper around the new stopForeground method, using the older | ||
150 | * APIs if it is not available. | ||
151 | */ | ||
152 | private void stopForegroundCompat(int id) { | ||
153 | // If we have the new stopForeground API, then use it. | ||
154 | if (mStopForeground != null) { | ||
155 | Object[] stopForegroundArgs = new Object[] {Boolean.TRUE}; | ||
156 | invokeMethod(mStopForeground, stopForegroundArgs); | ||
157 | } else { | ||
158 | // Fall back on the old API. Note to cancel BEFORE changing the | ||
159 | // foreground state, since we could be killed at that point. | ||
160 | mNM.cancel(id); | ||
161 | |||
162 | Object[] setForegroundArgs = new Object[] {Boolean.FALSE}; | ||
163 | invokeMethod(mSetForeground, setForegroundArgs); | ||
185 | } | 164 | } |
186 | public void stopForeground() | ||
187 | { | ||
188 | mCurrentService.setForeground(false); | ||
189 | } | ||
190 | } | 165 | } |
191 | } | 166 | } |