221 lines
7.9 KiB
Java
221 lines
7.9 KiB
Java
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.content.res.Resources;
|
|
import android.graphics.Bitmap;
|
|
import android.graphics.BitmapFactory;
|
|
import android.graphics.drawable.Drawable;
|
|
import android.os.Handler;
|
|
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 Handler mServiceHandler;
|
|
private Intent mWidgetUpdate;
|
|
private int iconheight;
|
|
|
|
public RunForegroundManager(Service service)
|
|
{
|
|
mCurrentService = service;
|
|
mNM = (NotificationManager)
|
|
service.getSystemService(Service.NOTIFICATION_SERVICE);
|
|
RemoteViews views = new RemoteViews(service.getPackageName(), R.layout.statusbar);
|
|
/* create Intent for clicking on the expanded notifcation area */
|
|
Intent intent = new Intent(service, RockboxActivity.class);
|
|
intent = intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
|
|
|
/* retrieve height of launcher icon. Used to scale down album art. */
|
|
Resources resources = service.getResources();
|
|
Drawable draw = resources.getDrawable(R.drawable.launcher);
|
|
iconheight = draw.getIntrinsicHeight();
|
|
|
|
mNotification = new Notification();
|
|
mNotification.tickerText = service.getString(R.string.notification);
|
|
mNotification.icon = R.drawable.notification;
|
|
mNotification.contentView = views;
|
|
mNotification.flags |= Notification.FLAG_ONGOING_EVENT;
|
|
mNotification.contentIntent = PendingIntent.getActivity(service, 0, intent, 0);
|
|
|
|
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");
|
|
}
|
|
}
|
|
mServiceHandler = new Handler(service.getMainLooper());
|
|
}
|
|
|
|
public void startForeground()
|
|
{
|
|
/*
|
|
* 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()
|
|
{
|
|
/* 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;
|
|
}
|
|
|
|
public void updateNotification(final String title, final String artist, final String album, final String albumart)
|
|
{
|
|
/* do this on the main thread for 2 reasons
|
|
* 1) Don't delay track switching with possibly costly albumart
|
|
* loading (i.e. off-load from the Rockbox thread)
|
|
* 2) Work around a bug in Android where decodeFile() fails outside
|
|
* of the main thread (http://stackoverflow.com/q/7228633)
|
|
*/
|
|
mServiceHandler.post(new Runnable()
|
|
{
|
|
@Override
|
|
public void run()
|
|
{
|
|
final RemoteViews views = mNotification.contentView;
|
|
views.setTextViewText(R.id.title, title);
|
|
views.setTextViewText(R.id.content, artist+"\n"+album);
|
|
if (artist.equals(""))
|
|
mNotification.tickerText = title;
|
|
else
|
|
mNotification.tickerText = title+" - "+artist;
|
|
|
|
if (albumart != null) {
|
|
/* The notification area doesn't have permissions to access the SD card.
|
|
* Push the data as Bitmap instead of Uri. Scale down to size of
|
|
* launcher icon -- broadcasting the unscaled image may yield in
|
|
* too much data, causing UI hangs of Rockbox. */
|
|
Bitmap b = BitmapFactory.decodeFile(albumart);
|
|
if(b != null) {
|
|
/* scale width to keep aspect ratio -- height is the constraint */
|
|
int scaledwidth = Math.round(iconheight*((float)b.getWidth()/b.getHeight()));
|
|
views.setImageViewBitmap(R.id.artwork,
|
|
Bitmap.createScaledBitmap(b, scaledwidth, iconheight, false));
|
|
}
|
|
else {
|
|
views.setImageViewResource(R.id.artwork, R.drawable.launcher);
|
|
}
|
|
}
|
|
else {
|
|
views.setImageViewResource(R.id.artwork, R.drawable.launcher);
|
|
}
|
|
mWidgetUpdate = new Intent("org.rockbox.TrackUpdateInfo");
|
|
mWidgetUpdate.putExtra("title", title);
|
|
mWidgetUpdate.putExtra("artist", artist);
|
|
mWidgetUpdate.putExtra("album", album);
|
|
mWidgetUpdate.putExtra("albumart", albumart);
|
|
mCurrentService.sendBroadcast(mWidgetUpdate);
|
|
|
|
/* notify in this runnable to make sure the notification
|
|
* has the correct albumart */
|
|
mNM.notify(R.string.notification, mNotification);
|
|
}
|
|
});
|
|
}
|
|
|
|
public void resendUpdateNotification()
|
|
{
|
|
if (mWidgetUpdate != null)
|
|
mCurrentService.sendBroadcast(mWidgetUpdate);
|
|
}
|
|
|
|
public void finishNotification()
|
|
{
|
|
Logger.d("TrackFinish");
|
|
Intent widgetUpdate = new Intent("org.rockbox.TrackFinish");
|
|
mCurrentService.sendBroadcast(widgetUpdate);
|
|
}
|
|
|
|
private interface IRunForeground
|
|
{
|
|
void startForeground();
|
|
void stopForeground();
|
|
}
|
|
|
|
private class NewForegroundApi implements IRunForeground
|
|
{
|
|
int id;
|
|
Notification mNotification;
|
|
NewForegroundApi(int _id, Notification _notification)
|
|
{
|
|
id = _id;
|
|
mNotification = _notification;
|
|
}
|
|
|
|
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.invoke(mCurrentService, Boolean.TRUE);
|
|
} catch (Exception e) {
|
|
Logger.e("startForeground compat error: " + e.getMessage());
|
|
e.printStackTrace();
|
|
}
|
|
}
|
|
public void stopForeground()
|
|
{
|
|
try {
|
|
mSetForeground.invoke(mCurrentService, Boolean.FALSE);
|
|
} catch (Exception e) {
|
|
Logger.e("stopForeground compat error: " + e.getMessage());
|
|
}
|
|
}
|
|
}
|
|
}
|