diff --git a/android/res/drawable-hdpi/rb.png b/android/res/drawable-hdpi/rb.png new file mode 100644 index 0000000000..6d21c98bf1 Binary files /dev/null and b/android/res/drawable-hdpi/rb.png differ diff --git a/android/res/drawable-ldpi/rb.png b/android/res/drawable-ldpi/rb.png new file mode 100644 index 0000000000..6d21c98bf1 Binary files /dev/null and b/android/res/drawable-ldpi/rb.png differ diff --git a/android/res/drawable-mdpi/rb.png b/android/res/drawable-mdpi/rb.png new file mode 100644 index 0000000000..6d21c98bf1 Binary files /dev/null and b/android/res/drawable-mdpi/rb.png differ diff --git a/android/src/org/rockbox/RockboxActivity.java b/android/src/org/rockbox/RockboxActivity.java index 2bafb6688d..3be9e3ed47 100644 --- a/android/src/org/rockbox/RockboxActivity.java +++ b/android/src/org/rockbox/RockboxActivity.java @@ -53,24 +53,21 @@ public class RockboxActivity extends Activity { new Thread(new Runnable() { public void run() { - while (RockboxService.fb == null) - { - try { - Thread.sleep(250); - } catch (InterruptedException e) { - } catch (Exception e) { - LOG(e.toString()); + try { + while (RockboxService.fb == null) + Thread.sleep(250); + } catch (InterruptedException e) { + } catch (Exception e) { + LOG(e.toString()); + } + /* drawing needs to happen in ui thread */ + runOnUiThread(new Runnable() + { @Override + public void run() { + setContentView(RockboxService.fb); + RockboxService.fb.invalidate(); } - /* drawing needs to happen in ui thread */ - runOnUiThread(new Runnable() - { @Override - public void run() { - setContentView(RockboxService.fb); - RockboxService.fb.invalidate(); - } - }); - } - + }); } }).start(); } diff --git a/android/src/org/rockbox/RockboxService.java b/android/src/org/rockbox/RockboxService.java new file mode 100644 index 0000000000..5c74d19ebf --- /dev/null +++ b/android/src/org/rockbox/RockboxService.java @@ -0,0 +1,173 @@ +package org.rockbox; + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.util.Enumeration; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; + +import android.app.Notification; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.app.Service; +import android.content.Intent; +import android.os.Binder; +import android.os.IBinder; +import android.util.Log; + +public class RockboxService extends Service +{ + public static RockboxFramebuffer fb = null; + @Override + public void onCreate() + { + mNM = (NotificationManager)getSystemService(NOTIFICATION_SERVICE); + startservice(); + } + + private void do_start(Intent intent) + { + LOG("Start Service"); + } + + private void LOG(CharSequence text) + { + Log.d("Rockbox", (String) text); + } + + @Override + public void onStart(Intent intent, int startId) { + do_start(intent); + } + + @Override + public int onStartCommand(Intent intent, int flags, int startId) + { + do_start(intent); + /* Display a notification about us starting. We put an icon in the status bar. */ + showNotification(); + return START_STICKY; + } + + private void startservice() + { + fb = new RockboxFramebuffer(this); + final int BUFFER = 2048; + /* 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 + * to them from native code + */ + try + { + BufferedOutputStream dest = null; + BufferedInputStream is = null; + ZipEntry entry; + File file = new File("/data/data/org.rockbox/lib/libmisc.so"); + /* use arbitary file to determine whether extracting is needed */ + File file2 = new File("/data/data/org.rockbox/app_rockbox/rockbox/codecs/mpa.codec"); + if (!file2.exists() || (file.lastModified() > file2.lastModified())) + { + ZipFile zipfile = new ZipFile(file); + Enumeration e = zipfile.entries(); + File folder; + while(e.hasMoreElements()) { + entry = (ZipEntry) e.nextElement(); + LOG("Extracting: " +entry); + if (entry.isDirectory()) + { + folder = new File(entry.getName()); + LOG("mkdir "+ entry); + try { + folder.mkdirs(); + } catch (SecurityException ex){ + LOG(ex.getMessage()); + } + continue; + } + is = new BufferedInputStream(zipfile.getInputStream(entry)); + int count; + byte data[] = new byte[BUFFER]; + folder = new File(new File(entry.getName()).getParent()); + LOG("" + folder.getAbsolutePath()); + if (!folder.exists()) + folder.mkdirs(); + FileOutputStream fos = new FileOutputStream(entry.getName()); + dest = new BufferedOutputStream(fos, BUFFER); + while ((count = is.read(data, 0, BUFFER)) != -1) { + dest.write(data, 0, count); + } + dest.flush(); + dest.close(); + is.close(); + } + } + } catch(Exception e) { + e.printStackTrace(); + } + + System.loadLibrary("rockbox"); + + Thread rb = new Thread(new Runnable() + { + public void run() + { + main(); + } + },"Rockbox thread"); + rb.setDaemon(false); + rb.start(); + } + + private native void main(); + @Override + public IBinder onBind(Intent intent) { + // TODO Auto-generated method stub + return null; + } + private NotificationManager mNM; + + /** + * Class for clients to access. Because we know this service always + * runs in the same process as its clients, we don't need to deal with + * IPC. + */ + public class LocalBinder extends Binder { + RockboxService getService() { + return RockboxService.this; + } + } + + /* heavily based on the example found on + * http://developer.android.com/reference/android/app/Service.html + */ + public void showNotification() { + // In this sample, we'll use the same text for the ticker and the expanded notification + CharSequence text = getText(R.string.notification); + + // Set the icon, scrolling text and timestamp + Notification notification = new Notification(R.drawable.rb, text, + System.currentTimeMillis()); + + // The PendingIntent to launch our activity if the user selects this notification + Intent intent = new Intent(this, RockboxActivity.class); + PendingIntent contentIntent = PendingIntent.getActivity(this, 0, intent, 0); + + + // Set the info for the views that show in the notification panel. + notification.setLatestEventInfo(this, getText(R.string.notification), text, contentIntent); + + // Send the notification. + // We use a layout id because it is a unique number. We use it later to cancel. + mNM.notify(R.string.notification, notification); + + /* + * this call makes the service run as foreground, which + * provides enough cpu time to do music decoding in the + * background + */ + startForeground(R.string.notification, notification); + } +} diff --git a/firmware/target/hosted/android/button-android.c b/firmware/target/hosted/android/button-android.c index 67e8ca1f89..1172880908 100644 --- a/firmware/target/hosted/android/button-android.c +++ b/firmware/target/hosted/android/button-android.c @@ -27,7 +27,6 @@ #include "system.h" #include "touchscreen.h" -static long last_touch; static int last_y, last_x; static enum { @@ -48,7 +47,6 @@ Java_org_rockbox_RockboxFramebuffer_pixelHandler(JNIEnv*env, jobject this, (void)this; last_x = x; last_y = y; - last_touch = current_tick; } /* @@ -68,20 +66,11 @@ Java_org_rockbox_RockboxFramebuffer_touchHandler(JNIEnv*env, jobject this, void button_init_device(void) { - last_touch = current_tick; } int button_read_device(int *data) { /* get grid button/coordinates based on the current touchscreen mode */ int btn = touchscreen_to_pixels(last_x, last_y, data); - if (last_state == STATE_DOWN) - { - return btn; - } - else - { - *data = last_x = last_y = 0; - return 0; - } + return (last_state == STATE_DOWN ? btn : 0); }