summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--android/src/org/rockbox/Helper/MediaButtonReceiver.java17
-rw-r--r--android/src/org/rockbox/RockboxActivity.java31
-rw-r--r--android/src/org/rockbox/RockboxFramebuffer.java2
-rw-r--r--android/src/org/rockbox/RockboxService.java113
-rw-r--r--apps/main.c1
-rw-r--r--firmware/target/hosted/android/button-android.c8
-rw-r--r--firmware/target/hosted/android/lcd-android.c99
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 */