diff options
-rw-r--r-- | android/src/org/rockbox/Helper/Logger.java | 4 | ||||
-rw-r--r-- | android/src/org/rockbox/Helper/MediaButtonReceiver.java | 1 | ||||
-rw-r--r-- | android/src/org/rockbox/Helper/RunForegroundManager.java | 149 |
3 files changed, 88 insertions, 66 deletions
diff --git a/android/src/org/rockbox/Helper/Logger.java b/android/src/org/rockbox/Helper/Logger.java index c0ef5f64ad..0620175ef9 100644 --- a/android/src/org/rockbox/Helper/Logger.java +++ b/android/src/org/rockbox/Helper/Logger.java | |||
@@ -36,6 +36,10 @@ public class Logger | |||
36 | { | 36 | { |
37 | Log.d(TAG, s); | 37 | Log.d(TAG, s); |
38 | } | 38 | } |
39 | public static void d(String s, Throwable t) | ||
40 | { | ||
41 | Log.d(TAG, s, t); | ||
42 | } | ||
39 | 43 | ||
40 | public static void e(String s) | 44 | public static void e(String s) |
41 | { | 45 | { |
diff --git a/android/src/org/rockbox/Helper/MediaButtonReceiver.java b/android/src/org/rockbox/Helper/MediaButtonReceiver.java index eeeeef22cc..e74ec5c8c2 100644 --- a/android/src/org/rockbox/Helper/MediaButtonReceiver.java +++ b/android/src/org/rockbox/Helper/MediaButtonReceiver.java | |||
@@ -58,6 +58,7 @@ public class MediaButtonReceiver | |||
58 | /* Throwable includes Exception and the expected | 58 | /* Throwable includes Exception and the expected |
59 | * NoClassDefFoundError */ | 59 | * NoClassDefFoundError */ |
60 | api = new OldApi(c); | 60 | api = new OldApi(c); |
61 | Logger.i("MediaButtonReceiver: Falling back to compatibility API"); | ||
61 | } | 62 | } |
62 | } | 63 | } |
63 | 64 | ||
diff --git a/android/src/org/rockbox/Helper/RunForegroundManager.java b/android/src/org/rockbox/Helper/RunForegroundManager.java index 7677444374..fbb6040005 100644 --- a/android/src/org/rockbox/Helper/RunForegroundManager.java +++ b/android/src/org/rockbox/Helper/RunForegroundManager.java | |||
@@ -1,22 +1,23 @@ | |||
1 | package org.rockbox.Helper; | 1 | package org.rockbox.Helper; |
2 | 2 | ||
3 | import java.lang.reflect.Method; | 3 | import java.lang.reflect.Method; |
4 | |||
5 | import org.rockbox.R; | 4 | import org.rockbox.R; |
6 | import org.rockbox.RockboxActivity; | 5 | import org.rockbox.RockboxActivity; |
7 | |||
8 | import android.app.Notification; | 6 | import android.app.Notification; |
9 | import android.app.NotificationManager; | 7 | import android.app.NotificationManager; |
10 | import android.app.PendingIntent; | 8 | import android.app.PendingIntent; |
11 | import android.app.Service; | 9 | import android.app.Service; |
12 | import android.content.Intent; | 10 | import android.content.Intent; |
13 | import android.util.Log; | ||
14 | import android.widget.RemoteViews; | 11 | import android.widget.RemoteViews; |
15 | 12 | ||
16 | public class RunForegroundManager | 13 | public class RunForegroundManager |
17 | { | 14 | { |
15 | /* all below is heavily based on the examples found on | ||
16 | * http://developer.android.com/reference/android/app/Service.html#setForeground(boolean) | ||
17 | */ | ||
18 | private Notification mNotification; | 18 | private Notification mNotification; |
19 | private NotificationManager mNM; | 19 | private NotificationManager mNM; |
20 | private IRunForeground api; | ||
20 | private Service mCurrentService; | 21 | private Service mCurrentService; |
21 | private Intent mWidgetUpdate; | 22 | private Intent mWidgetUpdate; |
22 | 23 | ||
@@ -37,17 +38,43 @@ public class RunForegroundManager | |||
37 | mNotification.flags |= Notification.FLAG_ONGOING_EVENT; | 38 | mNotification.flags |= Notification.FLAG_ONGOING_EVENT; |
38 | mNotification.contentIntent = PendingIntent.getActivity(service, 0, intent, 0); | 39 | mNotification.contentIntent = PendingIntent.getActivity(service, 0, intent, 0); |
39 | 40 | ||
40 | initForegroundCompat(); | 41 | try { |
42 | api = new NewForegroundApi(R.string.notification, mNotification); | ||
43 | } catch (Throwable t) { | ||
44 | /* Throwable includes Exception and the expected | ||
45 | * NoClassDefFoundError for Android 1.x */ | ||
46 | try { | ||
47 | api = new OldForegroundApi(); | ||
48 | Logger.i("RunForegroundManager: Falling back to compatibility API"); | ||
49 | } catch (Exception e) { | ||
50 | Logger.e("Cannot run in foreground: No available API"); | ||
51 | } | ||
52 | } | ||
41 | } | 53 | } |
42 | 54 | ||
43 | public void startForeground() | 55 | public void startForeground() |
44 | { | 56 | { |
45 | startForegroundCompat(R.string.notification, mNotification); | 57 | /* |
58 | * Send the notification. | ||
59 | * We use a layout id because it is a unique number. | ||
60 | * We use it later to cancel. | ||
61 | */ | ||
62 | mNM.notify(R.string.notification, mNotification); | ||
63 | /* | ||
64 | * this call makes the service run as foreground, which | ||
65 | * provides enough cpu time to do music decoding in the | ||
66 | * background | ||
67 | */ | ||
68 | api.startForeground(); | ||
46 | } | 69 | } |
47 | 70 | ||
48 | public void stopForeground() | 71 | public void stopForeground() |
49 | { | 72 | { |
50 | stopForegroundCompat(R.string.notification); | 73 | /* Note to cancel BEFORE changing the |
74 | * foreground state, since we could be killed at that point. | ||
75 | */ | ||
76 | mNM.cancel(R.string.notification); | ||
77 | api.stopForeground(); | ||
51 | mWidgetUpdate = null; | 78 | mWidgetUpdate = null; |
52 | } | 79 | } |
53 | 80 | ||
@@ -78,78 +105,68 @@ public class RunForegroundManager | |||
78 | 105 | ||
79 | public void finishNotification() | 106 | public void finishNotification() |
80 | { | 107 | { |
81 | Log.d("Rockbox", "TrackFinish"); | 108 | Logger.d("TrackFinish"); |
82 | Intent widgetUpdate = new Intent("org.rockbox.TrackFinish"); | 109 | Intent widgetUpdate = new Intent("org.rockbox.TrackFinish"); |
83 | mCurrentService.sendBroadcast(widgetUpdate); | 110 | mCurrentService.sendBroadcast(widgetUpdate); |
84 | } | 111 | } |
85 | 112 | ||
86 | /* Loosely based on http://developer.android.com/reference/android/app/Service.html#startForeground(int, android.app.Notification) */ | 113 | private interface IRunForeground |
87 | private static final Class<?>[] mSetForegroundSignature = new Class[] {boolean.class}; | 114 | { |
88 | private static final Class<?>[] mStartForegroundSignature = new Class[] {int.class, Notification.class}; | 115 | void startForeground(); |
89 | private static final Class<?>[] mStopForegroundSignature = new Class[] {boolean.class}; | 116 | void stopForeground(); |
90 | 117 | } | |
91 | private Method mSetForeground; | ||
92 | private Method mStartForeground; | ||
93 | private Method mStopForeground; | ||
94 | 118 | ||
95 | private void initForegroundCompat() { | 119 | private class NewForegroundApi implements IRunForeground |
96 | Class<?> serviceClass = mCurrentService.getClass(); | 120 | { |
97 | try { | 121 | int id; |
98 | mStartForeground = serviceClass.getMethod("startForeground", mStartForegroundSignature); | 122 | Notification mNotification; |
99 | mStopForeground = serviceClass.getMethod("stopForeground", mStopForegroundSignature); | 123 | NewForegroundApi(int _id, Notification _notification) |
100 | } catch (NoSuchMethodException e) { | 124 | { |
101 | // Running on an older platform. | 125 | id = _id; |
102 | mStartForeground = mStopForeground = null; | 126 | mNotification = _notification; |
103 | try { | ||
104 | mSetForeground = serviceClass.getMethod("setForeground", mSetForegroundSignature); | ||
105 | } catch (NoSuchMethodException e2) { | ||
106 | throw new IllegalStateException("OS doesn't have Service.startForeground nor Service.setForeground!", e2); | ||
107 | } | ||
108 | } | 127 | } |
109 | } | ||
110 | 128 | ||
111 | private void invokeMethod(Method method, Object[] args) { | 129 | public void startForeground() |
112 | try { | 130 | { |
113 | method.invoke(mCurrentService, args); | 131 | mCurrentService.startForeground(id, mNotification); |
114 | } catch (Exception e) { | ||
115 | // Should not happen. | ||
116 | Log.w("Rockbox", "Unable to invoke method", e); | ||
117 | } | 132 | } |
118 | } | ||
119 | 133 | ||
120 | /** | 134 | public void stopForeground() |
121 | * This is a wrapper around the new startForeground method, using the older | 135 | { |
122 | * APIs if it is not available. | 136 | mCurrentService.stopForeground(true); |
123 | */ | ||
124 | private void startForegroundCompat(int id, Notification notification) { | ||
125 | // If we have the new startForeground API, then use it. | ||
126 | if (mStartForeground != null) { | ||
127 | Object[] startForeGroundArgs = new Object[] {Integer.valueOf(id), notification}; | ||
128 | invokeMethod(mStartForeground, startForeGroundArgs); | ||
129 | } else { | ||
130 | // Fall back on the old API. | ||
131 | Object[] setForegroundArgs = new Object[] {Boolean.TRUE}; | ||
132 | invokeMethod(mSetForeground, setForegroundArgs); | ||
133 | mNM.notify(id, notification); | ||
134 | } | 137 | } |
135 | } | 138 | } |
139 | |||
140 | private class OldForegroundApi implements IRunForeground | ||
141 | { | ||
142 | /* | ||
143 | * Get the new API through reflection because it's unavailable | ||
144 | * in honeycomb | ||
145 | */ | ||
146 | private Method mSetForeground; | ||
147 | |||
148 | public OldForegroundApi() throws SecurityException, NoSuchMethodException | ||
149 | { | ||
150 | mSetForeground = getClass().getMethod("setForeground", | ||
151 | new Class[] { boolean.class }); | ||
152 | } | ||
136 | 153 | ||
137 | /** | 154 | public void startForeground() |
138 | * This is a wrapper around the new stopForeground method, using the older | 155 | { |
139 | * APIs if it is not available. | 156 | try { |
140 | */ | 157 | mSetForeground.invoke(mCurrentService, Boolean.TRUE); |
141 | private void stopForegroundCompat(int id) { | 158 | } catch (Exception e) { |
142 | // If we have the new stopForeground API, then use it. | 159 | Logger.e("startForeground compat error: " + e.getMessage()); |
143 | if (mStopForeground != null) { | 160 | e.printStackTrace(); |
144 | Object[] stopForegroundArgs = new Object[] {Boolean.TRUE}; | 161 | } |
145 | invokeMethod(mStopForeground, stopForegroundArgs); | ||
146 | } else { | ||
147 | // Fall back on the old API. Note to cancel BEFORE changing the | ||
148 | // foreground state, since we could be killed at that point. | ||
149 | mNM.cancel(id); | ||
150 | |||
151 | Object[] setForegroundArgs = new Object[] {Boolean.FALSE}; | ||
152 | invokeMethod(mSetForeground, setForegroundArgs); | ||
153 | } | 162 | } |
163 | public void stopForeground() | ||
164 | { | ||
165 | try { | ||
166 | mSetForeground.invoke(mCurrentService, Boolean.FALSE); | ||
167 | } catch (Exception e) { | ||
168 | Logger.e("stopForeground compat error: " + e.getMessage()); | ||
169 | } | ||
170 | } | ||
154 | } | 171 | } |
155 | } | 172 | } |