diff options
| -rw-r--r-- | android/src/org/rockbox/RockboxActivity.java | 45 | ||||
| -rw-r--r-- | android/src/org/rockbox/RockboxFramebuffer.java | 101 | ||||
| -rw-r--r-- | android/src/org/rockbox/RockboxService.java | 45 | ||||
| -rw-r--r-- | apps/keymaps/keymap-android.c | 3 | ||||
| -rw-r--r-- | firmware/target/hosted/android/app/button-target.h | 2 | ||||
| -rw-r--r-- | firmware/target/hosted/android/lcd-android.c | 140 |
6 files changed, 158 insertions, 178 deletions
diff --git a/android/src/org/rockbox/RockboxActivity.java b/android/src/org/rockbox/RockboxActivity.java index 7653568..f3f0d21 100644 --- a/android/src/org/rockbox/RockboxActivity.java +++ b/android/src/org/rockbox/RockboxActivity.java @@ -66,10 +66,9 @@ public class RockboxActivity extends Activity protected void onReceiveResult(final int resultCode, final Bundle resultData) { switch (resultCode) { - case RockboxService.RESULT_LIB_LOADED: - rbservice = RockboxService.get_instance(); + case RockboxService.RESULT_INVOKING_MAIN: if (loadingdialog != null) - loadingdialog.setIndeterminate(true); + loadingdialog.dismiss(); break; case RockboxService.RESULT_LIB_LOAD_PROGRESS: if (loadingdialog == null) @@ -79,10 +78,10 @@ public class RockboxActivity extends Activity loadingdialog.setMax(resultData.getInt("max", 100)); loadingdialog.setProgress(resultData.getInt("value", 0)); break; - case RockboxService.RESULT_FB_INITIALIZED: + case RockboxService.RESULT_SERVICE_RUNNING: + rbservice = RockboxService.get_instance(); + setServiceActivity(true); attachFramebuffer(); - if (loadingdialog != null) - loadingdialog.dismiss(); break; case RockboxService.RESULT_ERROR_OCCURED: Toast.makeText(RockboxActivity.this, resultData.getString("error"), Toast.LENGTH_LONG); @@ -93,17 +92,17 @@ public class RockboxActivity extends Activity startService(intent); } - private boolean isRockboxRunning() + private void setServiceActivity(boolean set) { - if (rbservice == null) - rbservice = RockboxService.get_instance(); - return (rbservice!= null && rbservice.isRockboxRunning() == true); + if (rbservice != null) + rbservice.set_activity(this); } private void attachFramebuffer() { - View rbFramebuffer = rbservice.get_fb(); + View rbFramebuffer = null; try { + rbFramebuffer = rbservice.get_fb(); setContentView(rbFramebuffer); } catch (IllegalStateException e) { /* we are already using the View, @@ -111,17 +110,17 @@ public class RockboxActivity extends Activity ViewGroup g = (ViewGroup) rbFramebuffer.getParent(); g.removeView(rbFramebuffer); setContentView(rbFramebuffer); - } finally { - rbFramebuffer.requestFocus(); - rbservice.set_activity(this); + } catch (NullPointerException e) { + return; } + rbFramebuffer.requestFocus(); } public void onResume() { super.onResume(); - if (isRockboxRunning()) - attachFramebuffer(); + setVisible(true); + attachFramebuffer(); } /* this is also called when the backlight goes off, @@ -131,27 +130,23 @@ public class RockboxActivity extends Activity protected void onPause() { super.onPause(); - if (rbservice != null) - { - rbservice.set_activity(null); - rbservice.get_fb().dispatchWindowVisibilityChanged(View.INVISIBLE); - } + /* this will cause the framebuffer's Surface to be destroyed, enabling + * us to disable drawing */ + setVisible(false); } @Override protected void onStop() { super.onStop(); - if (rbservice != null) - rbservice.set_activity(null); + setServiceActivity(false); } @Override protected void onDestroy() { super.onDestroy(); - if (rbservice != null) - rbservice.set_activity(null); + setServiceActivity(false); } private void LOG(CharSequence text) diff --git a/android/src/org/rockbox/RockboxFramebuffer.java b/android/src/org/rockbox/RockboxFramebuffer.java index 05d2b11..037fd2d 100644 --- a/android/src/org/rockbox/RockboxFramebuffer.java +++ b/android/src/org/rockbox/RockboxFramebuffer.java @@ -23,8 +23,6 @@ package org.rockbox; import java.nio.ByteBuffer; -import org.rockbox.Helper.MediaButtonReceiver; - import android.content.Context; import android.graphics.Bitmap; import android.graphics.Canvas; @@ -33,45 +31,72 @@ import android.util.DisplayMetrics; import android.util.Log; import android.view.KeyEvent; import android.view.MotionEvent; -import android.view.View; +import android.view.SurfaceHolder; +import android.view.SurfaceView; import android.view.ViewConfiguration; -public class RockboxFramebuffer extends View +public class RockboxFramebuffer extends SurfaceView + implements SurfaceHolder.Callback { - private Bitmap btm; - private Rect rect; - private ByteBuffer native_buf; - private MediaButtonReceiver media_monitor; private final DisplayMetrics metrics; private final ViewConfiguration view_config; + private ByteBuffer native_buf; + private Bitmap btm; - public RockboxFramebuffer(Context c, int lcd_width, - int lcd_height, ByteBuffer native_fb) + /* first stage init; needs to run from a thread that has a Looper + * setup stuff that needs a Context */ + public RockboxFramebuffer(Context c) { super(c); + + metrics = c.getResources().getDisplayMetrics(); + view_config = ViewConfiguration.get(c); + getHolder().addCallback(this); /* Needed so we can catch KeyEvents */ setFocusable(true); setFocusableInTouchMode(true); setClickable(true); + /* don't draw until native is ready (2nd stage) */ + setEnabled(false); + } + + /* second stage init; called from Rockbox with information about the + * display framebuffer */ + @SuppressWarnings("unused") + private void java_lcd_init(int lcd_width, int lcd_height, ByteBuffer native_fb) + { btm = Bitmap.createBitmap(lcd_width, lcd_height, Bitmap.Config.RGB_565); - rect = new Rect(); native_buf = native_fb; - media_monitor = new MediaButtonReceiver(c); - media_monitor.register(); - /* the service needs to know the about us */ - ((RockboxService)c).set_fb(this); - - metrics = c.getResources().getDisplayMetrics(); - view_config = ViewConfiguration.get(c); + setEnabled(true); } - public void onDraw(Canvas c) + @SuppressWarnings("unused") + private void java_lcd_update() + { + SurfaceHolder holder = getHolder(); + Canvas c = holder.lockCanvas(null); + btm.copyPixelsFromBuffer(native_buf); + synchronized (holder) + { /* draw */ + c.drawBitmap(btm, 0.0f, 0.0f, null); + } + holder.unlockCanvasAndPost(c); + } + + @SuppressWarnings("unused") + private void java_lcd_update_rect(int x, int y, int width, int height) { - /* can't copy a partial buffer :( */ + SurfaceHolder holder = getHolder(); + Rect dirty = new Rect(x, y, x+width, y+height); + Canvas c = holder.lockCanvas(dirty); + /* can't copy a partial buffer, + * but it doesn't make a noticeable difference anyway */ btm.copyPixelsFromBuffer(native_buf); - c.getClipBounds(rect); - c.drawBitmap(btm, rect, rect, null); - post_update_done(); + synchronized (holder) + { /* draw */ + c.drawBitmap(btm, dirty, dirty, null); + } + holder.unlockCanvasAndPost(c); } @SuppressWarnings("unused") @@ -109,28 +134,6 @@ public class RockboxFramebuffer extends View { return buttonHandler(keyCode, false); } - - public void destroy() - { - set_lcd_active(0); - media_monitor.unregister(); - } - - @Override - protected void onWindowVisibilityChanged(int visibility) - { - super.onWindowVisibilityChanged(visibility); - - switch (visibility) { - case VISIBLE: - set_lcd_active(1); - break; - case GONE: - case INVISIBLE: - set_lcd_active(0); - break; - } - } @SuppressWarnings("unused") private int getDpi() @@ -144,8 +147,12 @@ public class RockboxFramebuffer extends View return view_config.getScaledTouchSlop(); } - private native void post_update_done(); - private native void set_lcd_active(int active); private native void touchHandler(boolean down, int x, int y); private native static boolean buttonHandler(int keycode, boolean state); + + public native void surfaceCreated(SurfaceHolder holder); + public native void surfaceDestroyed(SurfaceHolder holder); + public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) + { + } } diff --git a/android/src/org/rockbox/RockboxService.java b/android/src/org/rockbox/RockboxService.java index de90999..4f5df62 100644 --- a/android/src/org/rockbox/RockboxService.java +++ b/android/src/org/rockbox/RockboxService.java @@ -31,6 +31,7 @@ import java.util.TimerTask; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; +import org.rockbox.Helper.MediaButtonReceiver; import org.rockbox.Helper.RunForegroundManager; import android.app.Activity; @@ -59,20 +60,21 @@ public class RockboxService extends Service /* locals needed for the c code and rockbox state */ private RockboxFramebuffer fb = null; - private boolean mRockboxRunning = false; - private volatile boolean rbLibLoaded; + private volatile boolean rockbox_running; private Activity current_activity = null; private IntentFilter itf; private BroadcastReceiver batt_monitor; private RunForegroundManager fg_runner; + private MediaButtonReceiver mMediaButtonReceiver; @SuppressWarnings("unused") private int battery_level; private ResultReceiver resultReceiver; - public static final int RESULT_LIB_LOADED = 0; + 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_ERROR_OCCURED = 3; + public static final int RESULT_SERVICE_RUNNING = 3; + public static final int RESULT_ERROR_OCCURED = 4; @Override public void onCreate() @@ -89,14 +91,6 @@ public class RockboxService extends Service { return fb; } - /* framebuffer is initialised by the native code(!) so this is needed */ - public void set_fb(RockboxFramebuffer newfb) - { - fb = newfb; - mRockboxRunning = true; - if (resultReceiver != null) - resultReceiver.send(RESULT_FB_INITIALIZED, null); - } public Activity get_activity() { @@ -113,7 +107,7 @@ public class RockboxService extends Service if (intent != null && intent.hasExtra("callback")) resultReceiver = (ResultReceiver) intent.getParcelableExtra("callback"); - if (!rbLibLoaded) + if (!rockbox_running) startservice(); if (intent != null && intent.getAction() != null) @@ -151,6 +145,8 @@ public class RockboxService extends Service e.printStackTrace(); } } + if (resultReceiver != null) + resultReceiver.send(RESULT_SERVICE_RUNNING, null); } private void LOG(CharSequence text) @@ -176,6 +172,11 @@ public class RockboxService extends Service private void startservice() { final int BUFFER = 8*1024; + fb = new RockboxFramebuffer(this); + if (resultReceiver != null) + resultReceiver.send(RESULT_FB_INITIALIZED, null); + mMediaButtonReceiver = new MediaButtonReceiver(this); + mMediaButtonReceiver.register(); Thread rb = new Thread(new Runnable() { public void run() @@ -247,10 +248,11 @@ public class RockboxService extends Service } } - System.loadLibrary("rockbox"); - rbLibLoaded = true; + System.loadLibrary("rockbox"); + + rockbox_running = true; if (resultReceiver != null) - resultReceiver.send(RESULT_LIB_LOADED, null); + resultReceiver.send(RESULT_INVOKING_MAIN, null); main(); throw new IllegalStateException("native main() returned!"); @@ -259,15 +261,8 @@ public class RockboxService extends Service rb.setDaemon(false); rb.start(); } + private native void main(); - - /* returns true once rockbox is up and running. - * This is considered done once the framebuffer is initialised - */ - public boolean isRockboxRunning() - { - return mRockboxRunning; - } @Override public IBinder onBind(Intent intent) @@ -329,7 +324,7 @@ public class RockboxService extends Service public void onDestroy() { super.onDestroy(); - fb.destroy(); + mMediaButtonReceiver.unregister(); /* Make sure our notification is gone. */ stopForeground(); } diff --git a/apps/keymaps/keymap-android.c b/apps/keymaps/keymap-android.c index df187e7..c822a64 100644 --- a/apps/keymaps/keymap-android.c +++ b/apps/keymaps/keymap-android.c @@ -50,6 +50,9 @@ static const struct button_mapping button_context_standard[] = { { ACTION_STD_MENU, BUTTON_MENU|BUTTON_REL, BUTTON_MENU }, { ACTION_STD_CONTEXT, BUTTON_MENU|BUTTON_REPEAT, BUTTON_MENU }, + /* special hack to get a redraw on activity resume, see lcd-android.c */ + { ACTION_REDRAW, BUTTON_FORCE_REDRAW, BUTTON_NONE }, + LAST_ITEM_IN_LIST }; /* button_context_standard */ diff --git a/firmware/target/hosted/android/app/button-target.h b/firmware/target/hosted/android/app/button-target.h index 6106b61..3b60287 100644 --- a/firmware/target/hosted/android/app/button-target.h +++ b/firmware/target/hosted/android/app/button-target.h @@ -58,6 +58,8 @@ void android_ignore_back_button(bool yes); #define BUTTON_BOTTOMMIDDLE 0x00080000 #define BUTTON_BOTTOMRIGHT 0x00100000 +#define BUTTON_FORCE_REDRAW 0x00200000 + /* No remote */ #define BUTTON_REMOTE 0 diff --git a/firmware/target/hosted/android/lcd-android.c b/firmware/target/hosted/android/lcd-android.c index 92e8f5b..f719329 100644 --- a/firmware/target/hosted/android/lcd-android.c +++ b/firmware/target/hosted/android/lcd-android.c @@ -21,10 +21,12 @@ #include <jni.h> +#include <string.h> #include "config.h" #include "system.h" #include "kernel.h" #include "lcd.h" +#include "button.h" extern JNIEnv *env_ptr; extern jclass RockboxService_class; @@ -32,57 +34,48 @@ extern jobject RockboxService_instance; static jclass RockboxFramebuffer_class; static jobject RockboxFramebuffer_instance; -static jmethodID postInvalidate1; -static jmethodID postInvalidate2; +static jmethodID java_lcd_update; +static jmethodID java_lcd_update_rect; -static bool display_on; static int dpi; static int scroll_threshold; -static struct wakeup lcd_wakeup; -static struct mutex lcd_mtx; +static bool display_on; void lcd_init_device(void) { JNIEnv e = *env_ptr; - wakeup_init(&lcd_wakeup); - mutex_init(&lcd_mtx); - RockboxFramebuffer_class = e->FindClass(env_ptr, - "org/rockbox/RockboxFramebuffer"); - /* instantiate a RockboxFramebuffer instance - * - * Pass lcd width and height and our framebuffer so the java layer - * can create a Bitmap which directly maps to it - **/ - - /* map the framebuffer to a ByteBuffer, this way lcd updates will - * be directly feched from the framebuffer */ + /* 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, lcd_framebuffer, (jlong)sizeof(lcd_framebuffer)); - jmethodID constructor = e->GetMethodID(env_ptr, - RockboxFramebuffer_class, - "<init>", - "(Landroid/content/Context;" /* Service */ - "II" /* lcd width/height */ - "Ljava/nio/ByteBuffer;)V"); /* ByteBuffer */ - - RockboxFramebuffer_instance = e->NewObject(env_ptr, - RockboxFramebuffer_class, - constructor, - RockboxService_instance, + e->CallVoidMethod(env_ptr, RockboxFramebuffer_instance, java_lcd_init, (jint)LCD_WIDTH, (jint)LCD_HEIGHT, buf); /* cache update functions */ - postInvalidate1 = (*env_ptr)->GetMethodID(env_ptr, + java_lcd_update = (*env_ptr)->GetMethodID(env_ptr, RockboxFramebuffer_class, - "postInvalidate", + "java_lcd_update", "()V"); - postInvalidate2 = (*env_ptr)->GetMethodID(env_ptr, + java_lcd_update_rect = (*env_ptr)->GetMethodID(env_ptr, RockboxFramebuffer_class, - "postInvalidate", + "java_lcd_update_rect", "(IIII)V"); jmethodID get_dpi = e->GetMethodID(env_ptr, @@ -98,49 +91,54 @@ void lcd_init_device(void) get_dpi); scroll_threshold = e->CallIntMethod(env_ptr, RockboxFramebuffer_instance, get_scroll_threshold); - display_on = true; + /* must not draw until surface is created */ + display_on = false; } -/* the update mechanism is asynchronous since - * onDraw() must be called from the UI thread - * - * The Rockbox thread calling lcd_update() has to wait - * for the update to complete, so that it's synchronous, - * and we need to notify it (we could wait in the java layer, but - * that'd block the other Rockbox threads too) - * - * That should give more smoonth animations - */ void lcd_update(void) { - /* tell the system we're ready for drawing */ if (display_on) - { - mutex_lock(&lcd_mtx); - (*env_ptr)->CallVoidMethod(env_ptr, RockboxFramebuffer_instance, postInvalidate1); - wakeup_wait(&lcd_wakeup, TIMEOUT_BLOCK); - mutex_unlock(&lcd_mtx); - } + (*env_ptr)->CallVoidMethod(env_ptr, RockboxFramebuffer_instance, + java_lcd_update); } void lcd_update_rect(int x, int y, int width, int height) { if (display_on) - { - mutex_lock(&lcd_mtx); - (*env_ptr)->CallVoidMethod(env_ptr, RockboxFramebuffer_instance, postInvalidate2, - (jint)x, (jint)y, (jint)x+width, (jint)y+height); - wakeup_wait(&lcd_wakeup, TIMEOUT_BLOCK); - mutex_unlock(&lcd_mtx); - } + (*env_ptr)->CallVoidMethod(env_ptr, RockboxFramebuffer_instance, + java_lcd_update_rect, x, y, width, height); +} + +/* + * this is called when the surface is created, which called is everytime + * the activity is brought in front and the RockboxFramebuffer gains focus + * + * Note this is considered interrupt context + */ +JNIEXPORT void JNICALL +Java_org_rockbox_RockboxFramebuffer_surfaceCreated(JNIEnv *e, jobject this, + jobject surfaceholder) +{ + (void)e; (void)this; (void)surfaceholder; + + 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 */ + queue_post(&button_queue, BUTTON_FORCE_REDRAW, 0); } +/* + * the surface is destroyed everytime the RockboxFramebuffer loses focus and + * goes invisible + */ JNIEXPORT void JNICALL -Java_org_rockbox_RockboxFramebuffer_post_1update_1done(JNIEnv *e, jobject this) +Java_org_rockbox_RockboxFramebuffer_surfaceDestroyed(JNIEnv *e, jobject this, + jobject surfaceholder) { - (void)e; - (void)this; - wakeup_signal(&lcd_wakeup); + (void)e; (void)this; (void)surfaceholder; + + display_on = false; } bool lcd_active(void) @@ -158,26 +156,6 @@ int touchscreen_get_scroll_threshold(void) return scroll_threshold; } -/* - * (un)block lcd updates. - * - * Notice: This is called from the activity thread, so take it - * as interrupt context and take care what the event callback does - * (it shouldn't block in particular - * - * the 1s are needed due to strange naming conventions... - **/ -JNIEXPORT void JNICALL -Java_org_rockbox_RockboxFramebuffer_set_1lcd_1active(JNIEnv *e, - jobject this, - jint active) -{ - (void)e; - (void)this; - display_on = active != 0; - if (active) - send_event(LCD_EVENT_ACTIVATION, NULL); -} /* below is a plain copy from lcd-sdl.c */ /** |