summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--android/src/org/rockbox/RockboxActivity.java45
-rw-r--r--android/src/org/rockbox/RockboxFramebuffer.java101
-rw-r--r--android/src/org/rockbox/RockboxService.java45
-rw-r--r--apps/keymaps/keymap-android.c3
-rw-r--r--firmware/target/hosted/android/app/button-target.h2
-rw-r--r--firmware/target/hosted/android/lcd-android.c140
6 files changed, 158 insertions, 178 deletions
diff --git a/android/src/org/rockbox/RockboxActivity.java b/android/src/org/rockbox/RockboxActivity.java
index 76535687f9..f3f0d2151c 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 05d2b11184..037fd2db85 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 de90999783..4f5df62280 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 df187e7ac8..c822a64fc2 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 6106b612f9..3b6028739c 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 92e8f5b96f..f719329819 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 */
/**