summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Martitz <kugel@rockbox.org>2011-07-18 22:04:08 +0000
committerThomas Martitz <kugel@rockbox.org>2011-07-18 22:04:08 +0000
commit64b5f1c625f500e8837147f31997b7671e724462 (patch)
tree7f82ce1ee193a227b59b9878627c83f0226dca20
parent8fba4fb937332f4125d5fe6aee90e37dc9f68b0e (diff)
downloadrockbox-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.java4
-rw-r--r--android/src/org/rockbox/Helper/MediaButtonReceiver.java1
-rw-r--r--android/src/org/rockbox/Helper/RunForegroundManager.java149
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 @@
1package org.rockbox.Helper; 1package org.rockbox.Helper;
2 2
3import java.lang.reflect.Method; 3import java.lang.reflect.Method;
4
5import org.rockbox.R; 4import org.rockbox.R;
6import org.rockbox.RockboxActivity; 5import org.rockbox.RockboxActivity;
7
8import android.app.Notification; 6import android.app.Notification;
9import android.app.NotificationManager; 7import android.app.NotificationManager;
10import android.app.PendingIntent; 8import android.app.PendingIntent;
11import android.app.Service; 9import android.app.Service;
12import android.content.Intent; 10import android.content.Intent;
13import android.util.Log;
14import android.widget.RemoteViews; 11import android.widget.RemoteViews;
15 12
16public class RunForegroundManager 13public 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}