summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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}