Index: android/AndroidManifest.xml
===================================================================
--- android/AndroidManifest.xml (revision 29051)
+++ android/AndroidManifest.xml (working copy)
@@ -29,6 +29,16 @@
+
+
+
+
+
+
+
+
+
Index: android/src/org/rockbox/Helper/RunForegroundManager.java
===================================================================
--- android/src/org/rockbox/Helper/RunForegroundManager.java (revision 29051)
+++ android/src/org/rockbox/Helper/RunForegroundManager.java (working copy)
@@ -5,12 +5,14 @@
import org.rockbox.R;
import org.rockbox.RockboxActivity;
+import org.rockbox.RockboxWidgetProvider;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Intent;
+import android.net.Uri;
import android.util.Log;
import android.widget.RemoteViews;
@@ -82,15 +84,31 @@
api.stopForeground();
}
- public void updateNotification(String title, String content, String ticker)
+ public void updateNotification(String title, String artist, String album)
{
RemoteViews views = mNotification.contentView;
views.setTextViewText(R.id.title, title);
- views.setTextViewText(R.id.content, content);
- mNotification.tickerText = ticker;
+ views.setTextViewText(R.id.content, artist+"\n"+album);
+ if (artist.equals(""))
+ mNotification.tickerText = title;
+ else
+ mNotification.tickerText = title+" - "+artist;
mNM.notify(R.string.notification, mNotification);
+
+ Intent widgetUpdate = new Intent("org.rockbox.TrackUpdateInfo", Uri.EMPTY, mCurrentService, RockboxWidgetProvider.class);
+ widgetUpdate.putExtra("title", title);
+ widgetUpdate.putExtra("artist", artist);
+ widgetUpdate.putExtra("album", album);
+ mCurrentService.sendBroadcast(widgetUpdate);
}
+ public void finishNotification()
+ {
+ Log.d("Rockbox", "TrackFinish");
+ Intent widgetUpdate = new Intent("org.rockbox.TrackFinish", Uri.EMPTY, mCurrentService, RockboxWidgetProvider.class);
+ mCurrentService.sendBroadcast(widgetUpdate);
+ }
+
private interface IRunForeground
{
void startForeground();
Index: android/src/org/rockbox/RockboxWidgetProvider.java
===================================================================
--- android/src/org/rockbox/RockboxWidgetProvider.java (revision 0)
+++ android/src/org/rockbox/RockboxWidgetProvider.java (revision 0)
@@ -0,0 +1,134 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2011 Antoine Cellerier
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+package org.rockbox;
+
+import android.app.PendingIntent;
+import android.appwidget.AppWidgetManager;
+import android.appwidget.AppWidgetProvider;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.net.Uri;
+import android.util.Log;
+import android.view.View;
+import android.view.KeyEvent;
+import android.widget.RemoteViews;
+
+import java.util.ArrayList;
+
+public class RockboxWidgetProvider extends AppWidgetProvider
+{
+ @Override
+ public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds)
+ {
+ final int N = appWidgetIds.length;
+ for (int i = 0; i < N; i++)
+ {
+ int appWidgetId = appWidgetIds[i];
+ updateAppWidget(context, appWidgetManager, appWidgetId, null);
+
+ }
+ }
+
+ @Override
+ public void onDeleted(Context context, int[] appWidgetIds)
+ {
+ }
+
+ @Override
+ public void onEnabled(Context context)
+ {
+ }
+
+ @Override
+ public void onDisabled(Context context)
+ {
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent)
+ {
+ String action = intent.getAction();
+ if (intent.getAction().equals("org.rockbox.TrackUpdateInfo") ||
+ intent.getAction().equals("org.rockbox.TrackFinish") ||
+ intent.getAction().equals("org.rockbox.UpdateState"))
+ {
+ AppWidgetManager gm = AppWidgetManager.getInstance(context);
+ int[] appWidgetIds = gm.getAppWidgetIds(new ComponentName(context, RockboxWidgetProvider.class));
+ final int N = appWidgetIds.length;
+ for (int i = 0; i < N; i++)
+ {
+ updateAppWidget(context, gm, appWidgetIds[i], intent);
+ }
+ }
+ else
+ {
+ super.onReceive(context, intent);
+ }
+ }
+
+ public static void updateAppWidget(Context context, AppWidgetManager appWidgetManager, int appWidgetId, Intent args)
+ {
+ RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.appwidget);
+
+ Intent intent = new Intent(context, RockboxActivity.class);
+ PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0);
+ views.setOnClickPendingIntent(R.id.infoDisplay, pendingIntent);
+
+ intent = new Intent("org.rockbox.PlayPause", Uri.EMPTY, context, RockboxService.class);
+ pendingIntent = PendingIntent.getService(context, 0, intent, 0);
+ views.setOnClickPendingIntent(R.id.playPause, pendingIntent);
+
+ intent = new Intent("org.rockbox.Next", Uri.EMPTY, context, RockboxService.class);
+ pendingIntent = PendingIntent.getService(context, 0, intent, 0);
+ views.setOnClickPendingIntent(R.id.next, pendingIntent);
+
+ if (args != null)
+ {
+ if (args.getAction().equals("org.rockbox.TrackUpdateInfo"))
+ {
+ CharSequence title = args.getCharSequenceExtra("title");
+ CharSequence artist = args.getCharSequenceExtra("artist");
+ CharSequence album = args.getCharSequenceExtra("album");
+ views.setTextViewText(R.id.infoDisplay, title+"\n"+artist+"\n"+album);
+ }
+ else if (args.getAction().equals("org.rockbox.TrackFinish"))
+ {
+ // FIXME: looks like this event is always fired earlier than
+ // the actual track change (a few seconds)
+ views.setTextViewText(R.id.infoDisplay, context.getString(R.string.appwidget_infoDisplay));
+ }
+ else if (args.getAction().equals("org.rockbox.UpdateState"))
+ {
+ CharSequence state = args.getCharSequenceExtra("state");
+ if (state.equals("play"))
+ views.setImageViewResource(R.id.playPause, R.drawable.appwidget_pause);
+ else /* pause or stop */
+ views.setImageViewResource(R.id.playPause, R.drawable.appwidget_play);
+ }
+ }
+
+ appWidgetManager.updateAppWidget(appWidgetId, views);
+ }
+}
+
Index: android/src/org/rockbox/RockboxPCM.java
===================================================================
--- android/src/org/rockbox/RockboxPCM.java (revision 29051)
+++ android/src/org/rockbox/RockboxPCM.java (working copy)
@@ -23,9 +23,11 @@
import java.util.Arrays;
+import android.content.Intent;
import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioTrack;
+import android.net.Uri;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Process;
@@ -80,10 +82,16 @@
private void play_pause(boolean pause) {
if (pause)
{
+ Intent widgetUpdate = new Intent("org.rockbox.UpdateState", Uri.EMPTY, RockboxService.get_instance(), RockboxWidgetProvider.class);
+ widgetUpdate.putExtra("state", "pause");
+ RockboxService.get_instance().sendBroadcast(widgetUpdate);
pause();
}
else
{
+ Intent widgetUpdate = new Intent("org.rockbox.UpdateState", Uri.EMPTY, RockboxService.get_instance(), RockboxWidgetProvider.class);
+ widgetUpdate.putExtra("state", "play");
+ RockboxService.get_instance().sendBroadcast(widgetUpdate);
if (getPlayState() == AudioTrack.PLAYSTATE_STOPPED)
{
RockboxService.get_instance().startForeground();
@@ -114,6 +122,9 @@
throw new IllegalStateException(e);
}
RockboxService.get_instance().stopForeground();
+ Intent widgetUpdate = new Intent("org.rockbox.UpdateState", Uri.EMPTY, RockboxService.get_instance(), RockboxWidgetProvider.class);
+ widgetUpdate.putExtra("state", "stop");
+ RockboxService.get_instance().sendBroadcast(widgetUpdate);
}
@SuppressWarnings("unused")
Index: android/src/org/rockbox/RockboxService.java
===================================================================
--- android/src/org/rockbox/RockboxService.java (revision 29051)
+++ android/src/org/rockbox/RockboxService.java (working copy)
@@ -43,6 +43,7 @@
import android.os.IBinder;
import android.os.ResultReceiver;
import android.util.Log;
+import android.view.KeyEvent;
/* This class is used as the main glue between java and c.
* All access should be done through RockboxService.get_instance() for safety.
@@ -76,7 +77,7 @@
@Override
public void onCreate()
{
- instance = this;
+ instance = this;
}
public static RockboxService get_instance()
@@ -115,6 +116,21 @@
if (!rbLibLoaded)
startservice();
+ if (intent != null && intent.getAction() != null)
+ {
+ Log.d("RockboxService", intent.getAction());
+ if (intent.getAction().equals("org.rockbox.PlayPause"))
+ {
+ if (fb != null)
+ fb.onKeyUp(KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE, null);
+ }
+ else if (intent.getAction().equals("org.rockbox.Next"))
+ {
+ if (fb != null)
+ fb.onKeyUp(KeyEvent.KEYCODE_MEDIA_NEXT, null);
+ }
+ }
+
/* Display a notification about us starting.
* We put an icon in the status bar. */
if (fg_runner == null)
Index: android/res/values/style.xml
===================================================================
--- android/res/values/style.xml (revision 0)
+++ android/res/values/style.xml (revision 0)
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
Index: android/res/values/strings.xml
===================================================================
--- android/res/values/strings.xml (revision 29051)
+++ android/res/values/strings.xml (working copy)
@@ -10,4 +10,5 @@
No
Error occured during extraction!
Rockbox is loading. Please wait...
-
\ No newline at end of file
+Rockbox\nTouch to launch app
+
Index: android/res/xml/appwidget_provider.xml
===================================================================
--- android/res/xml/appwidget_provider.xml (revision 0)
+++ android/res/xml/appwidget_provider.xml (revision 0)
@@ -0,0 +1,7 @@
+
+
+
Index: android/res/drawable/appwidget_pause.xml
===================================================================
--- android/res/drawable/appwidget_pause.xml (revision 0)
+++ android/res/drawable/appwidget_pause.xml (revision 0)
@@ -0,0 +1,6 @@
+
+
+
+
+
+
Index: android/res/drawable/appwidget_next.xml
===================================================================
--- android/res/drawable/appwidget_next.xml (revision 0)
+++ android/res/drawable/appwidget_next.xml (revision 0)
@@ -0,0 +1,6 @@
+
+
+
+
+
+
Index: android/res/drawable/appwidget_background.xml
===================================================================
--- android/res/drawable/appwidget_background.xml (revision 0)
+++ android/res/drawable/appwidget_background.xml (revision 0)
@@ -0,0 +1,6 @@
+
+
+
+
+
+
Index: android/res/drawable/appwidget_infodisplay_background.xml
===================================================================
--- android/res/drawable/appwidget_infodisplay_background.xml (revision 0)
+++ android/res/drawable/appwidget_infodisplay_background.xml (revision 0)
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
Index: android/res/drawable/appwidget_stop_normal.png
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
Property changes on: android/res/drawable/appwidget_stop_normal.png
___________________________________________________________________
Added: svn:mime-type
+ application/octet-stream
Index: android/res/drawable/appwidget_background_normal.9.png
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
Property changes on: android/res/drawable/appwidget_background_normal.9.png
___________________________________________________________________
Added: svn:mime-type
+ application/octet-stream
Index: android/res/drawable/appwidget_ff_normal.png
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
Property changes on: android/res/drawable/appwidget_ff_normal.png
___________________________________________________________________
Added: svn:mime-type
+ application/octet-stream
Index: android/res/drawable/appwidget_rew_normal.png
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
Property changes on: android/res/drawable/appwidget_rew_normal.png
___________________________________________________________________
Added: svn:mime-type
+ application/octet-stream
Index: android/res/drawable/source/appwidget_background.svg
===================================================================
--- android/res/drawable/source/appwidget_background.svg (revision 0)
+++ android/res/drawable/source/appwidget_background.svg (revision 0)
@@ -0,0 +1,119 @@
+
+
+
+
Index: android/res/drawable/appwidget_selection_clicked.9.png
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
Property changes on: android/res/drawable/appwidget_selection_clicked.9.png
___________________________________________________________________
Added: svn:mime-type
+ application/octet-stream
Index: android/res/drawable/appwidget_play_normal.png
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
Property changes on: android/res/drawable/appwidget_play_normal.png
___________________________________________________________________
Added: svn:mime-type
+ application/octet-stream
Index: android/res/drawable/appwidget_selection_transparent.9.png
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
Property changes on: android/res/drawable/appwidget_selection_transparent.9.png
___________________________________________________________________
Added: svn:mime-type
+ application/octet-stream
Index: android/res/drawable/appwidget_play.xml
===================================================================
--- android/res/drawable/appwidget_play.xml (revision 0)
+++ android/res/drawable/appwidget_play.xml (revision 0)
@@ -0,0 +1,6 @@
+
+
+
+
+
+
Index: android/res/drawable/appwidget_pause_normal.png
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
Property changes on: android/res/drawable/appwidget_pause_normal.png
___________________________________________________________________
Added: svn:mime-type
+ application/octet-stream
Index: android/res/drawable/appwidget_selection_over.9.png
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
Property changes on: android/res/drawable/appwidget_selection_over.9.png
___________________________________________________________________
Added: svn:mime-type
+ application/octet-stream
Index: android/res/layout/appwidget.xml
===================================================================
--- android/res/layout/appwidget.xml (revision 0)
+++ android/res/layout/appwidget.xml (revision 0)
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
Index: android/installApk.sh
===================================================================
--- android/installApk.sh (revision 29051)
+++ android/installApk.sh (working copy)
@@ -1,5 +1,10 @@
#!/bin/sh
ADB="$ANDROID_SDK_PATH/tools/adb"
+if [ ! -e $ADB ]
+then
+ # Starting with the gingerbread sdk, the adb location changed
+ ADB="$ANDROID_SDK_PATH/platform-tools/adb"
+fi
$ADB install -r rockbox.apk
echo 'am start -a android.intent.action.MAIN -n org.rockbox/.RockboxActivity; exit' | $ADB shell
Index: android/android.make
===================================================================
--- android/android.make (revision 29051)
+++ android/android.make (working copy)
@@ -31,6 +31,7 @@
APKBUILDER=$(ANDROID_SDK_PATH)/tools/apkbuilder
ZIPALIGN=$(ANDROID_SDK_PATH)/tools/zipalign
KEYSTORE=$(HOME)/.android/debug.keystore
+ADB=$(ANDROID_SDK_PATH)/platform-tools/adb
MANIFEST := $(ANDROID_DIR)/AndroidManifest.xml
@@ -69,7 +70,7 @@
-classpath $(ANDROID_PLATFORM)/android.jar:$(BUILDDIR)/bin \
-sourcepath $(ANDROID_DIR)/gen:$(ANDROID_DIR)/src $<
-$(BUILDDIR)/bin/$(PACKAGE_PATH)/%.class: $(ANDROID_DIR)/src/$(PACKAGE_PATH)/%.java
+$(BUILDDIR)/bin/$(PACKAGE_PATH)/%.class: $(ANDROID_DIR)/src/$(PACKAGE_PATH)/%.java $(BUILDDIR)/bin/$(PACKAGE_PATH)/R.class
$(call PRINTS,JAVAC $(subst $(ROOTDIR)/,,$<))javac -d $(BUILDDIR)/bin \
-classpath $(ANDROID_PLATFORM)/android.jar:$(BUILDDIR)/bin \
-sourcepath $(ANDROID_DIR)/gen:$(ANDROID_DIR)/src $<
@@ -112,3 +113,7 @@
dirs: $(DIRS)
apk: $(APK)
+
+install: apk
+ $(ADB) install -r $(APK)
+
Index: tools/root.make
===================================================================
--- tools/root.make (revision 29051)
+++ tools/root.make (working copy)
@@ -295,6 +295,7 @@
endif
+ifeq (,$(findstring android, $(APP_TYPE)))
bininstall: $(BUILDDIR)/$(BINARY)
@echo "Installing your rockbox binary in your '$(RBPREFIX)' dir"
$(SILENT)cp $(BINARY) "$(RBPREFIX)/.rockbox/"
@@ -310,6 +311,7 @@
symlinkinstall:
@echo "Installing a full setup with links in your '$(RBPREFIX)' dir"
$(SILENT)$(TOOLSDIR)/buildzip.pl $(VERBOSEOPT) -m "$(MODELNAME)" -i "$(TARGET_ID)" $(INSTALL) -z "zip -r0" -r "$(ROOTDIR)" --rbdir="$(RBDIR)" -f 2 $(TARGET) $(BINARY) -l
+endif
help:
@echo "A few helpful make targets"
Index: apps/hosted/notification.c
===================================================================
--- apps/hosted/notification.c (revision 29051)
+++ apps/hosted/notification.c (working copy)
@@ -30,9 +30,9 @@
extern jclass RockboxService_class;
extern jobject RockboxService_instance;
-static jmethodID updateNotification;
+static jmethodID updateNotification, finishNotification;
static jobject NotificationManager_instance;
-static jstring headline, content, ticker;
+static jstring title, artist, album;
#define NZV(a) (a && a[0])
@@ -45,40 +45,37 @@
if (id3)
{
/* passing NULL to DeleteLocalRef() is OK */
- e->DeleteLocalRef(env_ptr, headline);
- e->DeleteLocalRef(env_ptr, content);
- e->DeleteLocalRef(env_ptr, ticker);
+ e->DeleteLocalRef(env_ptr, title);
+ e->DeleteLocalRef(env_ptr, artist);
+ e->DeleteLocalRef(env_ptr, album);
char buf[200];
- const char * title = id3->title;
- if (!title)
+ const char * ptitle = id3->title;
+ if (!ptitle)
{ /* pass the filename as title if id3 info isn't available */
- title =
+ ptitle =
strip_extension(buf, sizeof(buf), strrchr(id3->path,'/') + 1);
}
- /* do text for the notification area in the scrolled-down statusbar */
- headline = e->NewStringUTF(env_ptr, title);
+ title = e->NewStringUTF(env_ptr, ptitle);
+ artist = e->NewStringUTF(env_ptr, id3->artist ?: "");
+ album = e->NewStringUTF(env_ptr, id3->album ?: "");
- /* add a \n so that android does split into two lines */
- snprintf(buf, sizeof(buf), "%s\n%s", id3->artist ?: "", id3->album ?: "");
- content = e->NewStringUTF(env_ptr, buf);
-
- /* now do the text for the notification in the statusbar itself */
- if (NZV(id3->artist))
- { /* title - artist */
- snprintf(buf, sizeof(buf), "%s - %s", title, id3->artist);
- ticker = e->NewStringUTF(env_ptr, buf);
- }
- else
- { /* title */
- ticker = e->NewStringUTF(env_ptr, title);
- }
e->CallVoidMethod(env_ptr, NotificationManager_instance,
- updateNotification, headline, content, ticker);
+ updateNotification, title, artist, album);
}
}
+/*
+ * notify about track finishing */
+static void track_finished_callback(void *param)
+{
+ (void)param;
+ JNIEnv e = *env_ptr;
+ e->CallVoidMethod(env_ptr, NotificationManager_instance,
+ finishNotification);
+}
+
void notification_init(void)
{
JNIEnv e = *env_ptr;
@@ -92,5 +89,9 @@
"(Ljava/lang/String;"
"Ljava/lang/String;"
"Ljava/lang/String;)V");
+ finishNotification = e->GetMethodID(env_ptr, class, "finishNotification",
+ "()V");
+
add_event(PLAYBACK_EVENT_TRACK_CHANGE, false, track_changed_callback);
+ add_event(PLAYBACK_EVENT_TRACK_FINISH, false, track_finished_callback);
}