From 64b5f1c625f500e8837147f31997b7671e724462 Mon Sep 17 00:00:00 2001 From: Thomas Martitz Date: Mon, 18 Jul 2011 22:04:08 +0000 Subject: [PATCH] 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 --- android/src/org/rockbox/Helper/Logger.java | 4 + .../rockbox/Helper/MediaButtonReceiver.java | 1 + .../rockbox/Helper/RunForegroundManager.java | 157 ++++++++++-------- 3 files changed, 92 insertions(+), 70 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 { Log.d(TAG, s); } + public static void d(String s, Throwable t) + { + Log.d(TAG, s, t); + } public static void e(String s) { 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 /* Throwable includes Exception and the expected * NoClassDefFoundError */ api = new OldApi(c); + Logger.i("MediaButtonReceiver: Falling back to compatibility API"); } } 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 @@ package org.rockbox.Helper; import java.lang.reflect.Method; - import org.rockbox.R; import org.rockbox.RockboxActivity; - import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; import android.app.Service; import android.content.Intent; -import android.util.Log; import android.widget.RemoteViews; public class RunForegroundManager { + /* all below is heavily based on the examples found on + * http://developer.android.com/reference/android/app/Service.html#setForeground(boolean) + */ private Notification mNotification; private NotificationManager mNM; + private IRunForeground api; private Service mCurrentService; private Intent mWidgetUpdate; @@ -37,17 +38,43 @@ public class RunForegroundManager mNotification.flags |= Notification.FLAG_ONGOING_EVENT; mNotification.contentIntent = PendingIntent.getActivity(service, 0, intent, 0); - initForegroundCompat(); + try { + api = new NewForegroundApi(R.string.notification, mNotification); + } catch (Throwable t) { + /* Throwable includes Exception and the expected + * NoClassDefFoundError for Android 1.x */ + try { + api = new OldForegroundApi(); + Logger.i("RunForegroundManager: Falling back to compatibility API"); + } catch (Exception e) { + Logger.e("Cannot run in foreground: No available API"); + } + } } public void startForeground() { - startForegroundCompat(R.string.notification, mNotification); + /* + * Send the notification. + * We use a layout id because it is a unique number. + * We use it later to cancel. + */ + mNM.notify(R.string.notification, mNotification); + /* + * this call makes the service run as foreground, which + * provides enough cpu time to do music decoding in the + * background + */ + api.startForeground(); } public void stopForeground() { - stopForegroundCompat(R.string.notification); + /* Note to cancel BEFORE changing the + * foreground state, since we could be killed at that point. + */ + mNM.cancel(R.string.notification); + api.stopForeground(); mWidgetUpdate = null; } @@ -78,78 +105,68 @@ public class RunForegroundManager public void finishNotification() { - Log.d("Rockbox", "TrackFinish"); + Logger.d("TrackFinish"); Intent widgetUpdate = new Intent("org.rockbox.TrackFinish"); mCurrentService.sendBroadcast(widgetUpdate); } - /* Loosely based on http://developer.android.com/reference/android/app/Service.html#startForeground(int, android.app.Notification) */ - private static final Class[] mSetForegroundSignature = new Class[] {boolean.class}; - private static final Class[] mStartForegroundSignature = new Class[] {int.class, Notification.class}; - private static final Class[] mStopForegroundSignature = new Class[] {boolean.class}; + private interface IRunForeground + { + void startForeground(); + void stopForeground(); + } - private Method mSetForeground; - private Method mStartForeground; - private Method mStopForeground; + private class NewForegroundApi implements IRunForeground + { + int id; + Notification mNotification; + NewForegroundApi(int _id, Notification _notification) + { + id = _id; + mNotification = _notification; + } - private void initForegroundCompat() { - Class serviceClass = mCurrentService.getClass(); - try { - mStartForeground = serviceClass.getMethod("startForeground", mStartForegroundSignature); - mStopForeground = serviceClass.getMethod("stopForeground", mStopForegroundSignature); - } catch (NoSuchMethodException e) { - // Running on an older platform. - mStartForeground = mStopForeground = null; + public void startForeground() + { + mCurrentService.startForeground(id, mNotification); + } + + public void stopForeground() + { + mCurrentService.stopForeground(true); + } + } + + private class OldForegroundApi implements IRunForeground + { + /* + * Get the new API through reflection because it's unavailable + * in honeycomb + */ + private Method mSetForeground; + + public OldForegroundApi() throws SecurityException, NoSuchMethodException + { + mSetForeground = getClass().getMethod("setForeground", + new Class[] { boolean.class }); + } + + public void startForeground() + { try { - mSetForeground = serviceClass.getMethod("setForeground", mSetForegroundSignature); - } catch (NoSuchMethodException e2) { - throw new IllegalStateException("OS doesn't have Service.startForeground nor Service.setForeground!", e2); + mSetForeground.invoke(mCurrentService, Boolean.TRUE); + } catch (Exception e) { + Logger.e("startForeground compat error: " + e.getMessage()); + e.printStackTrace(); } } - } - - private void invokeMethod(Method method, Object[] args) { - try { - method.invoke(mCurrentService, args); - } catch (Exception e) { - // Should not happen. - Log.w("Rockbox", "Unable to invoke method", e); - } - } - - /** - * This is a wrapper around the new startForeground method, using the older - * APIs if it is not available. - */ - private void startForegroundCompat(int id, Notification notification) { - // If we have the new startForeground API, then use it. - if (mStartForeground != null) { - Object[] startForeGroundArgs = new Object[] {Integer.valueOf(id), notification}; - invokeMethod(mStartForeground, startForeGroundArgs); - } else { - // Fall back on the old API. - Object[] setForegroundArgs = new Object[] {Boolean.TRUE}; - invokeMethod(mSetForeground, setForegroundArgs); - mNM.notify(id, notification); - } - } - - /** - * This is a wrapper around the new stopForeground method, using the older - * APIs if it is not available. - */ - private void stopForegroundCompat(int id) { - // If we have the new stopForeground API, then use it. - if (mStopForeground != null) { - Object[] stopForegroundArgs = new Object[] {Boolean.TRUE}; - invokeMethod(mStopForeground, stopForegroundArgs); - } else { - // Fall back on the old API. Note to cancel BEFORE changing the - // foreground state, since we could be killed at that point. - mNM.cancel(id); - - Object[] setForegroundArgs = new Object[] {Boolean.FALSE}; - invokeMethod(mSetForeground, setForegroundArgs); - } + public void stopForeground() + { + try { + mSetForeground.invoke(mCurrentService, Boolean.FALSE); + } catch (Exception e) { + Logger.e("stopForeground compat error: " + e.getMessage()); + } + } } }