summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--android/AndroidManifest.xml11
-rw-r--r--android/src/org/rockbox/Helper/MediaButtonReceiver.java164
-rw-r--r--android/src/org/rockbox/RockboxFramebuffer.java11
-rw-r--r--android/src/org/rockbox/RockboxService.java2
-rw-r--r--apps/action.c5
-rw-r--r--apps/gui/wps.c14
-rw-r--r--apps/menu.c16
-rw-r--r--apps/misc.c37
-rw-r--r--firmware/export/button.h15
-rw-r--r--firmware/export/config/application.h1
-rw-r--r--firmware/target/hosted/android/app/button-application.c21
-rw-r--r--firmware/target/hosted/android/app/button-target.h1
-rw-r--r--firmware/target/hosted/android/button-android.c19
13 files changed, 300 insertions, 17 deletions
diff --git a/android/AndroidManifest.xml b/android/AndroidManifest.xml
index c52d83fd2c..06a13ddb40 100644
--- a/android/AndroidManifest.xml
+++ b/android/AndroidManifest.xml
@@ -16,10 +16,21 @@
16 </intent-filter> 16 </intent-filter>
17 </activity> 17 </activity>
18 <service android:name=".RockboxService"/> 18 <service android:name=".RockboxService"/>
19 <receiver android:name=".Helper.MediaButtonReceiver$MediaReceiver"
20 android:enabled="true"
21 android:exported="true">
22 <intent-filter>
23 <action android:name="android.intent.action.MEDIA_BUTTON" />
24 </intent-filter>
25 </receiver>
19 26
20 <activity android:name="KeyboardActivity"></activity> 27 <activity android:name="KeyboardActivity"></activity>
21 <activity android:name="YesnoActivity"></activity> 28 <activity android:name="YesnoActivity"></activity>
22</application> 29</application>
23<uses-sdk android:minSdkVersion="4" /> 30<uses-sdk android:minSdkVersion="4" />
24<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission> 31<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission>
32
33
34
35
25</manifest> 36</manifest>
diff --git a/android/src/org/rockbox/Helper/MediaButtonReceiver.java b/android/src/org/rockbox/Helper/MediaButtonReceiver.java
new file mode 100644
index 0000000000..3749cec32a
--- /dev/null
+++ b/android/src/org/rockbox/Helper/MediaButtonReceiver.java
@@ -0,0 +1,164 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2010 Thomas Martitz
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21
22package org.rockbox.Helper;
23
24import java.lang.reflect.Method;
25
26import org.rockbox.RockboxService;
27
28import android.content.BroadcastReceiver;
29import android.content.ComponentName;
30import android.content.Context;
31import android.content.Intent;
32import android.content.IntentFilter;
33import android.media.AudioManager;
34import android.view.KeyEvent;
35
36public class MediaButtonReceiver
37{
38 /* A note on the API being used. 2.2 introduces a new and sane API
39 * for handling multimedia button presses
40 * http://android-developers.blogspot.com/2010/06/allowing-applications-to-play-nicer.html
41 *
42 * the old API is flawed. It doesn't have management for
43 * concurrent media apps
44 *
45 * if multiple media apps are running
46 * probably all of them want to respond to media keys
47 *
48 * it's not clear which app wins, it depends on the
49 * priority set for the IntentFilter (see below)
50 *
51 * so this all might or might not work on < 2.2 */
52
53 IMultiMediaReceiver api;
54
55 public MediaButtonReceiver(Context c)
56 {
57 try {
58 api = new NewApi(c);
59 } catch (Exception e) {
60 api = new OldApi(c);
61 }
62 }
63
64 public void register()
65 {
66 api.register();
67 }
68
69 public void unregister()
70 {
71 api.register();
72 }
73
74 /* helper class for the manifest */
75 public static class MediaReceiver extends BroadcastReceiver
76 {
77 @Override
78 public void onReceive(Context context, Intent intent)
79 {
80 if (Intent.ACTION_MEDIA_BUTTON.equals(intent.getAction()))
81 {
82 KeyEvent key = (KeyEvent)intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT);
83 if (key.getAction() == KeyEvent.ACTION_UP)
84 { /* pass the pressed key to Rockbox */
85 if (RockboxService.get_instance().get_fb().dispatchKeyEvent(key))
86 abortBroadcast();
87 }
88 }
89 }
90 }
91
92 private interface IMultiMediaReceiver
93 {
94 void register();
95 void unregister();
96 }
97
98 private static class NewApi implements IMultiMediaReceiver
99 {
100 private Method register_method;
101 private Method unregister_method;
102 private AudioManager audio_manager;
103 private ComponentName receiver_name;
104 /* the constructor gets the methods through reflection so that
105 * this compiles on pre-2.2 devices */
106 NewApi(Context c) throws SecurityException, NoSuchMethodException
107 {
108 register_method = AudioManager.class.getMethod(
109 "registerMediaButtonEventReceiver",
110 new Class[] { ComponentName.class } );
111 unregister_method = AudioManager.class.getMethod(
112 "unregisterMediaButtonEventReceiver",
113 new Class[] { ComponentName.class } );
114
115 audio_manager = (AudioManager)c.getSystemService(Context.AUDIO_SERVICE);
116 receiver_name = new ComponentName(c, MediaReceiver.class);
117 }
118 public void register()
119 {
120 try {
121 register_method.invoke(audio_manager, receiver_name);
122 } catch (Exception e) {
123 // Nothing
124 e.printStackTrace();
125 }
126 }
127
128 public void unregister()
129 {
130 try
131 {
132 unregister_method.invoke(audio_manager, receiver_name);
133 } catch (Exception e) {
134 // Nothing
135 e.printStackTrace();
136 }
137 }
138
139 }
140
141 private static class OldApi implements IMultiMediaReceiver
142 {
143 private static final IntentFilter filter = new IntentFilter(Intent.ACTION_MEDIA_BUTTON);
144 private MediaReceiver receiver;
145 private Context context;
146 OldApi(Context c)
147 {
148 filter.setPriority(1); /* 1 higher than the built-in media player */
149 receiver = new MediaReceiver();
150 context = c;
151 }
152
153 public void register()
154 {
155 context.registerReceiver(receiver, filter);
156 }
157
158 public void unregister()
159 {
160 context.unregisterReceiver(receiver);
161 }
162
163 }
164}
diff --git a/android/src/org/rockbox/RockboxFramebuffer.java b/android/src/org/rockbox/RockboxFramebuffer.java
index 0daeffe265..84974d627a 100644
--- a/android/src/org/rockbox/RockboxFramebuffer.java
+++ b/android/src/org/rockbox/RockboxFramebuffer.java
@@ -23,6 +23,8 @@ package org.rockbox;
23 23
24import java.nio.ByteBuffer; 24import java.nio.ByteBuffer;
25 25
26import org.rockbox.Helper.MediaButtonReceiver;
27
26import android.content.Context; 28import android.content.Context;
27import android.graphics.Bitmap; 29import android.graphics.Bitmap;
28import android.graphics.Canvas; 30import android.graphics.Canvas;
@@ -35,6 +37,7 @@ public class RockboxFramebuffer extends View
35{ 37{
36 private Bitmap btm; 38 private Bitmap btm;
37 private ByteBuffer native_buf; 39 private ByteBuffer native_buf;
40 private MediaButtonReceiver media_monitor;
38 41
39 public RockboxFramebuffer(Context c, int lcd_width, 42 public RockboxFramebuffer(Context c, int lcd_width,
40 int lcd_height, ByteBuffer native_fb) 43 int lcd_height, ByteBuffer native_fb)
@@ -47,6 +50,8 @@ public class RockboxFramebuffer extends View
47 btm = Bitmap.createBitmap(lcd_width, lcd_height, Bitmap.Config.RGB_565); 50 btm = Bitmap.createBitmap(lcd_width, lcd_height, Bitmap.Config.RGB_565);
48 native_buf = native_fb; 51 native_buf = native_fb;
49 requestFocus(); 52 requestFocus();
53 media_monitor = new MediaButtonReceiver(c);
54 media_monitor.register();
50 /* the service needs to know the about us */ 55 /* the service needs to know the about us */
51 ((RockboxService)c).set_fb(this); 56 ((RockboxService)c).set_fb(this);
52 } 57 }
@@ -122,6 +127,12 @@ public class RockboxFramebuffer extends View
122 set_lcd_active(1); 127 set_lcd_active(1);
123 } 128 }
124 129
130 public void destroy()
131 {
132 suspend();
133 media_monitor.unregister();
134 }
135
125 public native void set_lcd_active(int active); 136 public native void set_lcd_active(int active);
126 public native void touchHandler(boolean down, int x, int y); 137 public native void touchHandler(boolean down, int x, int y);
127 public native boolean buttonHandler(int keycode, boolean state); 138 public native boolean buttonHandler(int keycode, boolean state);
diff --git a/android/src/org/rockbox/RockboxService.java b/android/src/org/rockbox/RockboxService.java
index b841bc5d7f..8989271b9f 100644
--- a/android/src/org/rockbox/RockboxService.java
+++ b/android/src/org/rockbox/RockboxService.java
@@ -139,6 +139,7 @@ public class RockboxService extends Service
139 { 139 {
140 public void run() 140 public void run()
141 { 141 {
142 LOG("main");
142 /* the following block unzips libmisc.so, which contains the files 143 /* the following block unzips libmisc.so, which contains the files
143 * we ship, such as themes. It's needed to put it into a .so file 144 * we ship, such as themes. It's needed to put it into a .so file
144 * because there's no other way to ship files and have access 145 * because there's no other way to ship files and have access
@@ -276,6 +277,7 @@ public class RockboxService extends Service
276 public void onDestroy() 277 public void onDestroy()
277 { 278 {
278 super.onDestroy(); 279 super.onDestroy();
280 fb.destroy();
279 /* Make sure our notification is gone. */ 281 /* Make sure our notification is gone. */
280 stopForeground(); 282 stopForeground();
281 } 283 }
diff --git a/apps/action.c b/apps/action.c
index 8f427c8d68..d61930a08c 100644
--- a/apps/action.c
+++ b/apps/action.c
@@ -183,8 +183,9 @@ static int get_action_worker(int context, int timeout,
183 else 183 else
184 button = button_get_w_tmo(timeout); 184 button = button_get_w_tmo(timeout);
185 185
186 /* Data from sys events can be pulled with button_get_data */ 186 /* Data from sys events can be pulled with button_get_data
187 if (button == BUTTON_NONE || button & SYS_EVENT) 187 * multimedia button presses don't go through the action system */
188 if (button == BUTTON_NONE || button & (SYS_EVENT|BUTTON_MULTIMEDIA))
188 return button; 189 return button;
189 /* Don't send any buttons through untill we see the release event */ 190 /* Don't send any buttons through untill we see the release event */
190 if (wait_for_release) 191 if (wait_for_release)
diff --git a/apps/gui/wps.c b/apps/gui/wps.c
index a5fe304d21..7d633ad4e8 100644
--- a/apps/gui/wps.c
+++ b/apps/gui/wps.c
@@ -1045,18 +1045,18 @@ long gui_wps_show(void)
1045 exit = true; 1045 exit = true;
1046 break; 1046 break;
1047#endif 1047#endif
1048 case SYS_POWEROFF:
1049 default_event_handler(SYS_POWEROFF);
1050 break;
1051 case ACTION_WPS_VIEW_PLAYLIST: 1048 case ACTION_WPS_VIEW_PLAYLIST:
1052 gwps_leave_wps(); 1049 gwps_leave_wps();
1053 return GO_TO_PLAYLIST_VIEWER; 1050 return GO_TO_PLAYLIST_VIEWER;
1054 break; 1051 break;
1055 default: 1052 default:
1056 if(default_event_handler(button) == SYS_USB_CONNECTED) 1053 switch(default_event_handler(button))
1057 { 1054 { /* music has been stopped by the default handler */
1058 gwps_leave_wps(); 1055 case SYS_USB_CONNECTED:
1059 return GO_TO_ROOT; 1056 case SYS_CALL_INCOMING:
1057 case BUTTON_MULTIMEDIA_STOP:
1058 gwps_leave_wps();
1059 return GO_TO_ROOT;
1060 } 1060 }
1061 update = true; 1061 update = true;
1062 break; 1062 break;
diff --git a/apps/menu.c b/apps/menu.c
index 9d67c7b03e..5839a51c21 100644
--- a/apps/menu.c
+++ b/apps/menu.c
@@ -650,10 +650,20 @@ int do_menu(const struct menu_item_ex *start_menu, int *start_selected,
650 } 650 }
651#endif 651#endif
652 } 652 }
653 else if(default_event_handler(action) == SYS_USB_CONNECTED) 653 else
654 { 654 {
655 ret = MENU_ATTACHED_USB; 655 switch(default_event_handler(action))
656 done = true; 656 {
657 case SYS_USB_CONNECTED:
658 ret = MENU_ATTACHED_USB;
659 done = true;
660 break;
661 case SYS_CALL_HUNG_UP:
662 case BUTTON_MULTIMEDIA_PLAYPAUSE:
663 /* remove splash from playlist_resume() */
664 redraw_lists = true;
665 break;
666 }
657 } 667 }
658 668
659 if (redraw_lists && !done) 669 if (redraw_lists && !done)
diff --git a/apps/misc.c b/apps/misc.c
index 8d0ca7922f..c41f63456c 100644
--- a/apps/misc.c
+++ b/apps/misc.c
@@ -529,6 +529,7 @@ long default_event_handler_ex(long event, void (*callback)(void *), void *parame
529#if CONFIG_PLATFORM & PLATFORM_ANDROID 529#if CONFIG_PLATFORM & PLATFORM_ANDROID
530 static bool resume = false; 530 static bool resume = false;
531#endif 531#endif
532
532 switch(event) 533 switch(event)
533 { 534 {
534 case SYS_BATTERY_UPDATE: 535 case SYS_BATTERY_UPDATE:
@@ -629,11 +630,45 @@ long default_event_handler_ex(long event, void (*callback)(void *), void *parame
629 if (resume && playlist_resume() != -1) 630 if (resume && playlist_resume() != -1)
630 { 631 {
631 playlist_start(global_status.resume_index, 632 playlist_start(global_status.resume_index,
632 global_status.resume_offset); 633 global_status.resume_offset);
633 } 634 }
634 resume = false; 635 resume = false;
635 return SYS_CALL_HUNG_UP; 636 return SYS_CALL_HUNG_UP;
636#endif 637#endif
638#ifdef HAVE_MULTIMEDIA_KEYS
639 /* multimedia keys on keyboards, headsets */
640 case BUTTON_MULTIMEDIA_PLAYPAUSE:
641 {
642 int status = audio_status();
643 if (status & AUDIO_STATUS_PLAY)
644 {
645 if (status & AUDIO_STATUS_PAUSE)
646 audio_resume();
647 else
648 audio_pause();
649 }
650 else
651 if (playlist_resume() != -1)
652 {
653 playlist_start(global_status.resume_index,
654 global_status.resume_offset);
655 }
656 return event;
657 }
658 case BUTTON_MULTIMEDIA_NEXT:
659 audio_next();
660 return event;
661 case BUTTON_MULTIMEDIA_PREV:
662 audio_prev();
663 return event;
664 case BUTTON_MULTIMEDIA_STOP:
665 list_stop_handler();
666 return event;
667 case BUTTON_MULTIMEDIA_REW:
668 case BUTTON_MULTIMEDIA_FFWD:
669 /* not supported yet, needs to be done in the WPS */
670 return 0;
671#endif
637 } 672 }
638 return 0; 673 return 0;
639} 674}
diff --git a/firmware/export/button.h b/firmware/export/button.h
index 097f50a9d5..3847d2ac9f 100644
--- a/firmware/export/button.h
+++ b/firmware/export/button.h
@@ -69,6 +69,21 @@ int button_apply_acceleration(const unsigned int data);
69#define BUTTON_REL 0x02000000 69#define BUTTON_REL 0x02000000
70#define BUTTON_REPEAT 0x04000000 70#define BUTTON_REPEAT 0x04000000
71#define BUTTON_TOUCHSCREEN 0x08000000 71#define BUTTON_TOUCHSCREEN 0x08000000
72#define BUTTON_MULTIMEDIA 0x10000000
73
74#define BUTTON_MULTIMEDIA_PLAYPAUSE (BUTTON_MULTIMEDIA|0x01)
75#define BUTTON_MULTIMEDIA_STOP (BUTTON_MULTIMEDIA|0x02)
76#define BUTTON_MULTIMEDIA_PREV (BUTTON_MULTIMEDIA|0x04)
77#define BUTTON_MULTIMEDIA_NEXT (BUTTON_MULTIMEDIA|0x08)
78#define BUTTON_MULTIMEDIA_REW (BUTTON_MULTIMEDIA|0x10)
79#define BUTTON_MULTIMEDIA_FFWD (BUTTON_MULTIMEDIA|0x20)
80
81#define BUTTON_MULTIMEDIA_ALL (BUTTON_MULTIMEDIA_PLAYPAUSE| \
82 BUTTON_MULTIMEDIA_STOP| \
83 BUTTON_MULTIMEDIA_PREV| \
84 BUTTON_MULTIMEDIA_NEXT| \
85 BUTTON_MULTIMEDIA_REW | \
86 BUTTON_MULTIMEDIA_FFWD)
72 87
73#ifdef HAVE_TOUCHSCREEN 88#ifdef HAVE_TOUCHSCREEN
74int touchscreen_last_touch(void); 89int touchscreen_last_touch(void);
diff --git a/firmware/export/config/application.h b/firmware/export/config/application.h
index 4dc34c14fa..b731f0cf76 100644
--- a/firmware/export/config/application.h
+++ b/firmware/export/config/application.h
@@ -77,6 +77,7 @@
77 77
78#if (CONFIG_PLATFORM & PLATFORM_ANDROID) 78#if (CONFIG_PLATFORM & PLATFORM_ANDROID)
79#define CONFIG_KEYPAD ANDROID_PAD 79#define CONFIG_KEYPAD ANDROID_PAD
80#define HAVE_MULTIMEDIA_KEYS
80#elif (CONFIG_PLATFORM & PLATFORM_SDL) 81#elif (CONFIG_PLATFORM & PLATFORM_SDL)
81#define HAVE_SCROLLWHEEL 82#define HAVE_SCROLLWHEEL
82#define CONFIG_KEYPAD SDL_PAD 83#define CONFIG_KEYPAD SDL_PAD
diff --git a/firmware/target/hosted/android/app/button-application.c b/firmware/target/hosted/android/app/button-application.c
index 47798a6096..a7d75ef172 100644
--- a/firmware/target/hosted/android/app/button-application.c
+++ b/firmware/target/hosted/android/app/button-application.c
@@ -45,3 +45,24 @@ int key_to_button(int keyboard_key)
45 return BUTTON_MENU; 45 return BUTTON_MENU;
46 } 46 }
47} 47}
48
49unsigned multimedia_to_button(int keyboard_key)
50{
51 switch (keyboard_key)
52 {
53 case KEYCODE_MEDIA_PLAY_PAUSE:
54 return BUTTON_MULTIMEDIA_PLAYPAUSE;
55 case KEYCODE_MEDIA_STOP:
56 return BUTTON_MULTIMEDIA_STOP;
57 case KEYCODE_MEDIA_NEXT:
58 return BUTTON_MULTIMEDIA_NEXT;
59 case KEYCODE_MEDIA_PREVIOUS:
60 return BUTTON_MULTIMEDIA_PREV;
61 case KEYCODE_MEDIA_REWIND:
62 return BUTTON_MULTIMEDIA_REW;
63 case KEYCODE_MEDIA_FAST_FORWARD:
64 return BUTTON_MULTIMEDIA_FFWD;
65 default:
66 return 0;
67 }
68}
diff --git a/firmware/target/hosted/android/app/button-target.h b/firmware/target/hosted/android/app/button-target.h
index 6c7bd271e9..ca306d4fef 100644
--- a/firmware/target/hosted/android/app/button-target.h
+++ b/firmware/target/hosted/android/app/button-target.h
@@ -28,6 +28,7 @@
28#undef button_init_device 28#undef button_init_device
29void button_init_device(void); 29void button_init_device(void);
30int button_read_device(int *data); 30int button_read_device(int *data);
31unsigned multimedia_to_button(int keyboard_key);
31 32
32/* Main unit's buttons */ 33/* Main unit's buttons */
33#define BUTTON_MENU 0x00000001 34#define BUTTON_MENU 0x00000001
diff --git a/firmware/target/hosted/android/button-android.c b/firmware/target/hosted/android/button-android.c
index c072e3e38b..9bf15c25a2 100644
--- a/firmware/target/hosted/android/button-android.c
+++ b/firmware/target/hosted/android/button-android.c
@@ -28,7 +28,7 @@
28#include "kernel.h" 28#include "kernel.h"
29#include "system.h" 29#include "system.h"
30#include "touchscreen.h" 30#include "touchscreen.h"
31 31#include "debug.h"
32static int last_y, last_x; 32static int last_y, last_x;
33static int last_btns; 33static int last_btns;
34static long last_button_tick; 34static long last_button_tick;
@@ -44,7 +44,7 @@ static enum {
44 * began or stopped the touch action + where (pixel coordinates) */ 44 * began or stopped the touch action + where (pixel coordinates) */
45JNIEXPORT void JNICALL 45JNIEXPORT void JNICALL
46Java_org_rockbox_RockboxFramebuffer_touchHandler(JNIEnv*env, jobject this, 46Java_org_rockbox_RockboxFramebuffer_touchHandler(JNIEnv*env, jobject this,
47 bool down, int x, int y) 47 jboolean down, jint x, jint y)
48{ 48{
49 (void)env; 49 (void)env;
50 (void)this; 50 (void)this;
@@ -63,12 +63,23 @@ Java_org_rockbox_RockboxFramebuffer_touchHandler(JNIEnv*env, jobject this,
63 * generated by pressing/releasing them to a variable */ 63 * generated by pressing/releasing them to a variable */
64JNIEXPORT bool JNICALL 64JNIEXPORT bool JNICALL
65Java_org_rockbox_RockboxFramebuffer_buttonHandler(JNIEnv*env, jobject this, 65Java_org_rockbox_RockboxFramebuffer_buttonHandler(JNIEnv*env, jobject this,
66 int keycode, bool state) 66 jint keycode, jboolean state)
67{ 67{
68 (void)env; 68 (void)env;
69 (void)this; 69 (void)this;
70 70
71 int button = key_to_button(keycode); 71 unsigned button = 0;
72
73 if (!state)
74 button = multimedia_to_button((int)keycode);
75
76 if (button)
77 { /* multimeida buttons are handled differently */
78 queue_post(&button_queue, button, 0);
79 return true;
80 }
81
82 button = key_to_button(keycode);
72 83
73 if (button == BUTTON_NONE) 84 if (button == BUTTON_NONE)
74 return false; 85 return false;