diff options
-rw-r--r-- | android/src/org/rockbox/Helper/MediaButtonReceiver.java | 17 | ||||
-rw-r--r-- | android/src/org/rockbox/RockboxActivity.java | 31 | ||||
-rw-r--r-- | android/src/org/rockbox/RockboxFramebuffer.java | 2 | ||||
-rw-r--r-- | android/src/org/rockbox/RockboxService.java | 113 | ||||
-rw-r--r-- | apps/main.c | 1 | ||||
-rw-r--r-- | firmware/target/hosted/android/button-android.c | 8 | ||||
-rw-r--r-- | firmware/target/hosted/android/lcd-android.c | 99 |
7 files changed, 123 insertions, 148 deletions
diff --git a/android/src/org/rockbox/Helper/MediaButtonReceiver.java b/android/src/org/rockbox/Helper/MediaButtonReceiver.java index 3749cec32a..a57bdd4831 100644 --- a/android/src/org/rockbox/Helper/MediaButtonReceiver.java +++ b/android/src/org/rockbox/Helper/MediaButtonReceiver.java @@ -22,9 +22,8 @@ package org.rockbox.Helper; import java.lang.reflect.Method; - +import org.rockbox.RockboxFramebuffer; import org.rockbox.RockboxService; - import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; @@ -73,7 +72,12 @@ public class MediaButtonReceiver /* helper class for the manifest */ public static class MediaReceiver extends BroadcastReceiver - { + { + private void startService(Context c, Intent baseIntent) + { + baseIntent.setClass(c, RockboxService.class); + c.startService(baseIntent); + } @Override public void onReceive(Context context, Intent intent) { @@ -81,8 +85,11 @@ public class MediaButtonReceiver { KeyEvent key = (KeyEvent)intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT); if (key.getAction() == KeyEvent.ACTION_UP) - { /* pass the pressed key to Rockbox */ - if (RockboxService.get_instance().get_fb().dispatchKeyEvent(key)) + { /* pass the pressed key to Rockbox, starting it if needed */ + RockboxService s = RockboxService.get_instance(); + if (s == null || !s.isRockboxRunning()) + startService(context, intent); + else if (RockboxFramebuffer.buttonHandler(key.getKeyCode(), false)) abortBroadcast(); } } diff --git a/android/src/org/rockbox/RockboxActivity.java b/android/src/org/rockbox/RockboxActivity.java index 90c687f35a..65c7f92bbe 100644 --- a/android/src/org/rockbox/RockboxActivity.java +++ b/android/src/org/rockbox/RockboxActivity.java @@ -29,16 +29,12 @@ import android.os.Bundle; import android.os.Handler; import android.os.ResultReceiver; import android.util.Log; -import android.view.View; -import android.view.ViewGroup; import android.view.Window; import android.view.WindowManager; import android.widget.Toast; public class RockboxActivity extends Activity { - private RockboxService rbservice; - /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) @@ -79,9 +75,7 @@ public class RockboxActivity extends Activity loadingdialog.setProgress(resultData.getInt("value", 0)); break; case RockboxService.RESULT_SERVICE_RUNNING: - rbservice = RockboxService.get_instance(); setServiceActivity(true); - attachFramebuffer(); break; case RockboxService.RESULT_ERROR_OCCURED: Toast.makeText(RockboxActivity.this, resultData.getString("error"), Toast.LENGTH_LONG); @@ -89,38 +83,21 @@ public class RockboxActivity extends Activity } } }); + setContentView(new RockboxFramebuffer(this)); startService(intent); } private void setServiceActivity(boolean set) { - if (rbservice != null) - rbservice.set_activity(set ? this : null); - } - - private void attachFramebuffer() - { - View rbFramebuffer = null; - try { - rbFramebuffer = rbservice.get_fb(); - setContentView(rbFramebuffer); - } catch (IllegalStateException e) { - /* we are already using the View, - * need to remove it and re-attach it */ - ViewGroup g = (ViewGroup) rbFramebuffer.getParent(); - g.removeView(rbFramebuffer); - setContentView(rbFramebuffer); - } catch (NullPointerException e) { - return; - } - rbFramebuffer.requestFocus(); + RockboxService s = RockboxService.get_instance(); + if (s != null) + s.set_activity(set ? this : null); } public void onResume() { super.onResume(); setVisible(true); - attachFramebuffer(); } /* this is also called when the backlight goes off, diff --git a/android/src/org/rockbox/RockboxFramebuffer.java b/android/src/org/rockbox/RockboxFramebuffer.java index 037fd2db85..a17bc90ab6 100644 --- a/android/src/org/rockbox/RockboxFramebuffer.java +++ b/android/src/org/rockbox/RockboxFramebuffer.java @@ -148,7 +148,7 @@ public class RockboxFramebuffer extends SurfaceView } private native void touchHandler(boolean down, int x, int y); - private native static boolean buttonHandler(int keycode, boolean state); + public native static boolean buttonHandler(int keycode, boolean state); public native void surfaceCreated(SurfaceHolder holder); public native void surfaceDestroyed(SurfaceHolder holder); diff --git a/android/src/org/rockbox/RockboxService.java b/android/src/org/rockbox/RockboxService.java index 43d122a8cc..4f0caa7704 100644 --- a/android/src/org/rockbox/RockboxService.java +++ b/android/src/org/rockbox/RockboxService.java @@ -59,8 +59,7 @@ public class RockboxService extends Service private static RockboxService instance = null; /* locals needed for the c code and rockbox state */ - private RockboxFramebuffer fb = null; - private volatile boolean rockbox_running; + private static volatile boolean rockbox_running; private Activity current_activity = null; private IntentFilter itf; private BroadcastReceiver batt_monitor; @@ -69,11 +68,9 @@ public class RockboxService extends Service @SuppressWarnings("unused") private int battery_level; private ResultReceiver resultReceiver; - final private Object lock = new Object(); public static final int RESULT_INVOKING_MAIN = 0; public static final int RESULT_LIB_LOAD_PROGRESS = 1; - public static final int RESULT_FB_INITIALIZED = 2; public static final int RESULT_SERVICE_RUNNING = 3; public static final int RESULT_ERROR_OCCURED = 4; public static final int RESULT_LIB_LOADED = 5; @@ -88,14 +85,15 @@ public class RockboxService extends Service public static RockboxService get_instance() { + /* don't call the construtor here, the instances are managed by + * android, so we can't just create a new one */ return instance; } - public RockboxFramebuffer get_fb() + public boolean isRockboxRunning() { - return fb; + return rockbox_running; } - public Activity get_activity() { return current_activity; @@ -108,71 +106,48 @@ public class RockboxService extends Service private void do_start(Intent intent) { LOG("Start Service"); - if (intent != null && intent.hasExtra("callback")) resultReceiver = (ResultReceiver) intent.getParcelableExtra("callback"); - /* Display a notification about us starting. - * We put an icon in the status bar. */ - if (fg_runner == null) - { /* needs to be initialized before main() runs */ - try { - } catch (Exception e) { - e.printStackTrace(); - } - } - if (!rockbox_running) - { - synchronized(lock) - { - startservice(); - while(true) { - try { - lock.wait(); - } catch (InterruptedException e) { - continue; - } - break; - } - fb = new RockboxFramebuffer(this); - if (resultReceiver != null) - resultReceiver.send(RESULT_FB_INITIALIZED, null); - } - } + startservice(); if (resultReceiver != null) resultReceiver.send(RESULT_LIB_LOADED, null); + 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); + if (!rockbox_running) + { /* give it a bit of time so we can register button presses + * sleeping longer doesn't work here, apparently Android + * surpresses long sleeps during intent handling */ + try { + Thread.sleep(50); + } + catch (InterruptedException e) { } } - else if (intent.getAction().equals("org.rockbox.Prev")) + + if (intent.getAction().equals(Intent.ACTION_MEDIA_BUTTON)) { - if (fb != null) - fb.onKeyUp(KeyEvent.KEYCODE_MEDIA_PREVIOUS, null); + KeyEvent kev = (KeyEvent)intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT); + RockboxFramebuffer.buttonHandler(kev.getKeyCode(), kev.getAction() == KeyEvent.ACTION_DOWN); } + else if (intent.getAction().equals("org.rockbox.PlayPause")) + RockboxFramebuffer.buttonHandler(KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE, false); + else if (intent.getAction().equals("org.rockbox.Prev")) + RockboxFramebuffer.buttonHandler(KeyEvent.KEYCODE_MEDIA_PREVIOUS, false); else if (intent.getAction().equals("org.rockbox.Next")) - { - if (fb != null) - fb.onKeyUp(KeyEvent.KEYCODE_MEDIA_NEXT, null); - } + RockboxFramebuffer.buttonHandler(KeyEvent.KEYCODE_MEDIA_NEXT, false); else if (intent.getAction().equals("org.rockbox.Stop")) - { - if (fb != null) - fb.onKeyUp(KeyEvent.KEYCODE_MEDIA_STOP, null); - } + RockboxFramebuffer.buttonHandler(KeyEvent.KEYCODE_MEDIA_STOP, false); } /* (Re-)attach the media button receiver, in case it has been lost */ mMediaButtonReceiver.register(); - if (resultReceiver != null) resultReceiver.send(RESULT_SERVICE_RUNNING, null); + + rockbox_running = true; } private void LOG(CharSequence text) @@ -195,16 +170,23 @@ public class RockboxService extends Service return 1; /* old API compatibility: 1 == START_STICKY */ } - private void startservice() + private void startservice() { - final int BUFFER = 8*1024; + final Object lock = new Object(); Thread rb = new Thread(new Runnable() { public void run() { + final int BUFFER = 8*1024; String rockboxDirPath = "/data/data/org.rockbox/app_rockbox/rockbox"; File rockboxDir = new File(rockboxDirPath); + /* load library before unzipping which may take a while */ + synchronized (lock) { + System.loadLibrary("rockbox"); + lock.notify(); + } + /* the following block unzips libmisc.so, which contains the files * we ship, such as themes. It's needed to put it into a .so file * because there's no other way to ship files and have access @@ -268,13 +250,7 @@ public class RockboxService extends Service } } } - - synchronized (lock) { - System.loadLibrary("rockbox"); - lock.notify(); - } - rockbox_running = true; if (resultReceiver != null) resultReceiver.send(RESULT_INVOKING_MAIN, null); @@ -283,7 +259,20 @@ public class RockboxService extends Service } }, "Rockbox thread"); rb.setDaemon(false); - rb.start(); + /* wait at least until the library is loaded */ + synchronized (lock) + { + rb.start(); + while(true) + { + try { + lock.wait(); + } catch (InterruptedException e) { + continue; + } + break; + } + } } private native void main(); @@ -352,5 +341,7 @@ public class RockboxService extends Service mMediaButtonReceiver = null; /* Make sure our notification is gone. */ stopForeground(); + instance = null; + rockbox_running = false; } } diff --git a/apps/main.c b/apps/main.c index 3fc48be183..bd04223f97 100644 --- a/apps/main.c +++ b/apps/main.c @@ -410,7 +410,6 @@ static void init(void) #endif /* CONFIG_CODEC == SWCODEC */ audio_init(); - button_clear_queue(); /* Empty the keyboard buffer */ settings_apply_skins(); } diff --git a/firmware/target/hosted/android/button-android.c b/firmware/target/hosted/android/button-android.c index 832eef54f3..ed1e125223 100644 --- a/firmware/target/hosted/android/button-android.c +++ b/firmware/target/hosted/android/button-android.c @@ -29,6 +29,7 @@ #include "system.h" #include "touchscreen.h" +extern JNIEnv *env_ptr; static int last_y, last_x; static int last_btns; @@ -61,11 +62,11 @@ Java_org_rockbox_RockboxFramebuffer_touchHandler(JNIEnv*env, jobject this, * this writes in an interrupt-like fashion the button events that the user * generated by pressing/releasing them to a variable */ JNIEXPORT bool JNICALL -Java_org_rockbox_RockboxFramebuffer_buttonHandler(JNIEnv*env, jclass this, +Java_org_rockbox_RockboxFramebuffer_buttonHandler(JNIEnv*env, jclass class, jint keycode, jboolean state) { (void)env; - (void)this; + (void)class; unsigned button = 0; @@ -75,7 +76,10 @@ Java_org_rockbox_RockboxFramebuffer_buttonHandler(JNIEnv*env, jclass this, if (!button) button = dpad_to_button((int)keycode); if (button) + { queue_post(&button_queue, button, 0); + return true; + } } if (!button) diff --git a/firmware/target/hosted/android/lcd-android.c b/firmware/target/hosted/android/lcd-android.c index f719329819..c9bd0a1254 100644 --- a/firmware/target/hosted/android/lcd-android.c +++ b/firmware/target/hosted/android/lcd-android.c @@ -29,68 +29,63 @@ #include "button.h" extern JNIEnv *env_ptr; -extern jclass RockboxService_class; extern jobject RockboxService_instance; -static jclass RockboxFramebuffer_class; static jobject RockboxFramebuffer_instance; static jmethodID java_lcd_update; static jmethodID java_lcd_update_rect; +static jmethodID java_lcd_init; +static jobject native_buffer; static int dpi; static int scroll_threshold; static bool display_on; -void lcd_init_device(void) +/* this might actually be called before lcd_init_device() or even main(), so + * be sure to only access static storage initalized at library loading, + * and not more */ +void connect_with_java(JNIEnv* env, jobject fb_instance) { - JNIEnv e = *env_ptr; - /* get existing instance from the Service */ - jmethodID get_fb = e->GetMethodID(env_ptr, RockboxService_class, "get_fb", - "()Lorg/rockbox/RockboxFramebuffer;"); - RockboxFramebuffer_instance = e->CallObjectMethod(env_ptr, - RockboxService_instance, - get_fb); - RockboxFramebuffer_class = (*env_ptr)->GetObjectClass(env_ptr, - RockboxFramebuffer_instance); - - /* Get init function and set up what's left from the constructor */ - jmethodID java_lcd_init = (*env_ptr)->GetMethodID(env_ptr, - RockboxFramebuffer_class, - "java_lcd_init", - "(IILjava/nio/ByteBuffer;)V"); - - jobject buf = e->NewDirectByteBuffer(env_ptr, + JNIEnv e = *env; + static bool have_class; + RockboxFramebuffer_instance = fb_instance; + if (!have_class) + { + jclass fb_class = e->GetObjectClass(env, fb_instance); + /* cache update functions */ + java_lcd_update = e->GetMethodID(env, fb_class, + "java_lcd_update", + "()V"); + java_lcd_update_rect = e->GetMethodID(env, fb_class, + "java_lcd_update_rect", + "(IIII)V"); + jmethodID get_dpi = e->GetMethodID(env, fb_class, + "getDpi", "()I"); + jmethodID thresh = e->GetMethodID(env, fb_class, + "getScrollThreshold", "()I"); + /* these don't change with new instances so call them now */ + dpi = e->CallIntMethod(env, fb_instance, get_dpi); + scroll_threshold = e->CallIntMethod(env, fb_instance, thresh); + + java_lcd_init = e->GetMethodID(env, fb_class, + "java_lcd_init", + "(IILjava/nio/ByteBuffer;)V"); + + native_buffer = e->NewDirectByteBuffer(env, lcd_framebuffer, (jlong)sizeof(lcd_framebuffer)); + have_class = true; + } + /* we need to setup parts for the java object every time */ + (*env)->CallVoidMethod(env, fb_instance, java_lcd_init, + (jint)LCD_WIDTH, (jint)LCD_HEIGHT, native_buffer); +} - e->CallVoidMethod(env_ptr, RockboxFramebuffer_instance, java_lcd_init, - (jint)LCD_WIDTH, - (jint)LCD_HEIGHT, - buf); - - /* cache update functions */ - java_lcd_update = (*env_ptr)->GetMethodID(env_ptr, - RockboxFramebuffer_class, - "java_lcd_update", - "()V"); - java_lcd_update_rect = (*env_ptr)->GetMethodID(env_ptr, - RockboxFramebuffer_class, - "java_lcd_update_rect", - "(IIII)V"); - - jmethodID get_dpi = e->GetMethodID(env_ptr, - RockboxFramebuffer_class, - "getDpi", "()I"); - - jmethodID get_scroll_threshold - = e->GetMethodID(env_ptr, - RockboxFramebuffer_class, - "getScrollThreshold", "()I"); - - dpi = e->CallIntMethod(env_ptr, RockboxFramebuffer_instance, - get_dpi); - scroll_threshold = e->CallIntMethod(env_ptr, RockboxFramebuffer_instance, - get_scroll_threshold); +/* + * Do nothing here and connect with the java object later (if it isn't already) + */ +void lcd_init_device(void) +{ /* must not draw until surface is created */ display_on = false; } @@ -116,12 +111,14 @@ void lcd_update_rect(int x, int y, int width, int height) * Note this is considered interrupt context */ JNIEXPORT void JNICALL -Java_org_rockbox_RockboxFramebuffer_surfaceCreated(JNIEnv *e, jobject this, +Java_org_rockbox_RockboxFramebuffer_surfaceCreated(JNIEnv *env, jobject this, jobject surfaceholder) { - (void)e; (void)this; (void)surfaceholder; - + (void)surfaceholder; + /* possibly a new instance - reconnect */ + connect_with_java(env, this); display_on = true; + send_event(LCD_EVENT_ACTIVATION, NULL); /* Force an update, since the newly created surface is initially black * waiting for the next normal update results in a longish black screen */ |