diff options
author | Thomas Martitz <kugel@rockbox.org> | 2011-07-18 22:04:08 +0000 |
---|---|---|
committer | Thomas Martitz <kugel@rockbox.org> | 2011-07-18 22:04:08 +0000 |
commit | 64b5f1c625f500e8837147f31997b7671e724462 (patch) | |
tree | 7f82ce1ee193a227b59b9878627c83f0226dca20 | |
parent | 8fba4fb937332f4125d5fe6aee90e37dc9f68b0e (diff) | |
download | rockbox-64b5f1c625f500e8837147f31997b7671e724462.tar.gz rockbox-64b5f1c625f500e8837147f31997b7671e724462.zip |
Android: Rework RunForegroundManager (again)
Restore pre-r29562 way of doing compatibility and adapt
it to what MediaButtonReceiver looks like. I.e. assume the new API is there,
and fall back to the old API if an exception is raised because the API isn't there.
The old API still needs to be discovered through reflection because it's removed
entirely from Honeycomb.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@30165 a1c6a512-1295-4272-9138-f99709370657
-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 | } |