summaryrefslogtreecommitdiff
path: root/android
diff options
context:
space:
mode:
authorThomas Martitz <kugel@rockbox.org>2011-02-23 01:10:54 +0000
committerThomas Martitz <kugel@rockbox.org>2011-02-23 01:10:54 +0000
commit95e24dd7a54256e8df56e347c0f43133087a1df2 (patch)
tree677d66d4e72fedfe134bca103ed98b5792da1440 /android
parentda3417706d927c7da0b59351fa8cc010d63d7928 (diff)
downloadrockbox-95e24dd7a54256e8df56e347c0f43133087a1df2.tar.gz
rockbox-95e24dd7a54256e8df56e347c0f43133087a1df2.zip
Android:
* Re-create RockboxFramebuffer instance with every time there's a new Activity. * Also, allow Rockbox to be started via multimedia buttons, immediately starting playback if wanted. We don't need to keep the fb instance around when it backround, and it makes us less depending on it and the activity (less race conditions). And this is how you usually do it in Android apps. git-svn-id: svn://svn.rockbox.org/rockbox/trunk@29384 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'android')
-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
4 files changed, 69 insertions, 94 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 @@
22package org.rockbox.Helper; 22package org.rockbox.Helper;
23 23
24import java.lang.reflect.Method; 24import java.lang.reflect.Method;
25 25import org.rockbox.RockboxFramebuffer;
26import org.rockbox.RockboxService; 26import org.rockbox.RockboxService;
27
28import android.content.BroadcastReceiver; 27import android.content.BroadcastReceiver;
29import android.content.ComponentName; 28import android.content.ComponentName;
30import android.content.Context; 29import android.content.Context;
@@ -73,7 +72,12 @@ public class MediaButtonReceiver
73 72
74 /* helper class for the manifest */ 73 /* helper class for the manifest */
75 public static class MediaReceiver extends BroadcastReceiver 74 public static class MediaReceiver extends BroadcastReceiver
76 { 75 {
76 private void startService(Context c, Intent baseIntent)
77 {
78 baseIntent.setClass(c, RockboxService.class);
79 c.startService(baseIntent);
80 }
77 @Override 81 @Override
78 public void onReceive(Context context, Intent intent) 82 public void onReceive(Context context, Intent intent)
79 { 83 {
@@ -81,8 +85,11 @@ public class MediaButtonReceiver
81 { 85 {
82 KeyEvent key = (KeyEvent)intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT); 86 KeyEvent key = (KeyEvent)intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT);
83 if (key.getAction() == KeyEvent.ACTION_UP) 87 if (key.getAction() == KeyEvent.ACTION_UP)
84 { /* pass the pressed key to Rockbox */ 88 { /* pass the pressed key to Rockbox, starting it if needed */
85 if (RockboxService.get_instance().get_fb().dispatchKeyEvent(key)) 89 RockboxService s = RockboxService.get_instance();
90 if (s == null || !s.isRockboxRunning())
91 startService(context, intent);
92 else if (RockboxFramebuffer.buttonHandler(key.getKeyCode(), false))
86 abortBroadcast(); 93 abortBroadcast();
87 } 94 }
88 } 95 }
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;
29import android.os.Handler; 29import android.os.Handler;
30import android.os.ResultReceiver; 30import android.os.ResultReceiver;
31import android.util.Log; 31import android.util.Log;
32import android.view.View;
33import android.view.ViewGroup;
34import android.view.Window; 32import android.view.Window;
35import android.view.WindowManager; 33import android.view.WindowManager;
36import android.widget.Toast; 34import android.widget.Toast;
37 35
38public class RockboxActivity extends Activity 36public class RockboxActivity extends Activity
39{ 37{
40 private RockboxService rbservice;
41
42 /** Called when the activity is first created. */ 38 /** Called when the activity is first created. */
43 @Override 39 @Override
44 public void onCreate(Bundle savedInstanceState) 40 public void onCreate(Bundle savedInstanceState)
@@ -79,9 +75,7 @@ public class RockboxActivity extends Activity
79 loadingdialog.setProgress(resultData.getInt("value", 0)); 75 loadingdialog.setProgress(resultData.getInt("value", 0));
80 break; 76 break;
81 case RockboxService.RESULT_SERVICE_RUNNING: 77 case RockboxService.RESULT_SERVICE_RUNNING:
82 rbservice = RockboxService.get_instance();
83 setServiceActivity(true); 78 setServiceActivity(true);
84 attachFramebuffer();
85 break; 79 break;
86 case RockboxService.RESULT_ERROR_OCCURED: 80 case RockboxService.RESULT_ERROR_OCCURED:
87 Toast.makeText(RockboxActivity.this, resultData.getString("error"), Toast.LENGTH_LONG); 81 Toast.makeText(RockboxActivity.this, resultData.getString("error"), Toast.LENGTH_LONG);
@@ -89,38 +83,21 @@ public class RockboxActivity extends Activity
89 } 83 }
90 } 84 }
91 }); 85 });
86 setContentView(new RockboxFramebuffer(this));
92 startService(intent); 87 startService(intent);
93 } 88 }
94 89
95 private void setServiceActivity(boolean set) 90 private void setServiceActivity(boolean set)
96 { 91 {
97 if (rbservice != null) 92 RockboxService s = RockboxService.get_instance();
98 rbservice.set_activity(set ? this : null); 93 if (s != null)
99 } 94 s.set_activity(set ? this : null);
100
101 private void attachFramebuffer()
102 {
103 View rbFramebuffer = null;
104 try {
105 rbFramebuffer = rbservice.get_fb();
106 setContentView(rbFramebuffer);
107 } catch (IllegalStateException e) {
108 /* we are already using the View,
109 * need to remove it and re-attach it */
110 ViewGroup g = (ViewGroup) rbFramebuffer.getParent();
111 g.removeView(rbFramebuffer);
112 setContentView(rbFramebuffer);
113 } catch (NullPointerException e) {
114 return;
115 }
116 rbFramebuffer.requestFocus();
117 } 95 }
118 96
119 public void onResume() 97 public void onResume()
120 { 98 {
121 super.onResume(); 99 super.onResume();
122 setVisible(true); 100 setVisible(true);
123 attachFramebuffer();
124 } 101 }
125 102
126 /* this is also called when the backlight goes off, 103 /* 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
148 } 148 }
149 149
150 private native void touchHandler(boolean down, int x, int y); 150 private native void touchHandler(boolean down, int x, int y);
151 private native static boolean buttonHandler(int keycode, boolean state); 151 public native static boolean buttonHandler(int keycode, boolean state);
152 152
153 public native void surfaceCreated(SurfaceHolder holder); 153 public native void surfaceCreated(SurfaceHolder holder);
154 public native void surfaceDestroyed(SurfaceHolder holder); 154 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
59 private static RockboxService instance = null; 59 private static RockboxService instance = null;
60 60
61 /* locals needed for the c code and rockbox state */ 61 /* locals needed for the c code and rockbox state */
62 private RockboxFramebuffer fb = null; 62 private static volatile boolean rockbox_running;
63 private volatile boolean rockbox_running;
64 private Activity current_activity = null; 63 private Activity current_activity = null;
65 private IntentFilter itf; 64 private IntentFilter itf;
66 private BroadcastReceiver batt_monitor; 65 private BroadcastReceiver batt_monitor;
@@ -69,11 +68,9 @@ public class RockboxService extends Service
69 @SuppressWarnings("unused") 68 @SuppressWarnings("unused")
70 private int battery_level; 69 private int battery_level;
71 private ResultReceiver resultReceiver; 70 private ResultReceiver resultReceiver;
72 final private Object lock = new Object();
73 71
74 public static final int RESULT_INVOKING_MAIN = 0; 72 public static final int RESULT_INVOKING_MAIN = 0;
75 public static final int RESULT_LIB_LOAD_PROGRESS = 1; 73 public static final int RESULT_LIB_LOAD_PROGRESS = 1;
76 public static final int RESULT_FB_INITIALIZED = 2;
77 public static final int RESULT_SERVICE_RUNNING = 3; 74 public static final int RESULT_SERVICE_RUNNING = 3;
78 public static final int RESULT_ERROR_OCCURED = 4; 75 public static final int RESULT_ERROR_OCCURED = 4;
79 public static final int RESULT_LIB_LOADED = 5; 76 public static final int RESULT_LIB_LOADED = 5;
@@ -88,14 +85,15 @@ public class RockboxService extends Service
88 85
89 public static RockboxService get_instance() 86 public static RockboxService get_instance()
90 { 87 {
88 /* don't call the construtor here, the instances are managed by
89 * android, so we can't just create a new one */
91 return instance; 90 return instance;
92 } 91 }
93 92
94 public RockboxFramebuffer get_fb() 93 public boolean isRockboxRunning()
95 { 94 {
96 return fb; 95 return rockbox_running;
97 } 96 }
98
99 public Activity get_activity() 97 public Activity get_activity()
100 { 98 {
101 return current_activity; 99 return current_activity;
@@ -108,71 +106,48 @@ public class RockboxService extends Service
108 private void do_start(Intent intent) 106 private void do_start(Intent intent)
109 { 107 {
110 LOG("Start Service"); 108 LOG("Start Service");
111
112 if (intent != null && intent.hasExtra("callback")) 109 if (intent != null && intent.hasExtra("callback"))
113 resultReceiver = (ResultReceiver) intent.getParcelableExtra("callback"); 110 resultReceiver = (ResultReceiver) intent.getParcelableExtra("callback");
114 111
115 /* Display a notification about us starting.
116 * We put an icon in the status bar. */
117 if (fg_runner == null)
118 { /* needs to be initialized before main() runs */
119 try {
120 } catch (Exception e) {
121 e.printStackTrace();
122 }
123 }
124
125 if (!rockbox_running) 112 if (!rockbox_running)
126 { 113 startservice();
127 synchronized(lock)
128 {
129 startservice();
130 while(true) {
131 try {
132 lock.wait();
133 } catch (InterruptedException e) {
134 continue;
135 }
136 break;
137 }
138 fb = new RockboxFramebuffer(this);
139 if (resultReceiver != null)
140 resultReceiver.send(RESULT_FB_INITIALIZED, null);
141 }
142 }
143 if (resultReceiver != null) 114 if (resultReceiver != null)
144 resultReceiver.send(RESULT_LIB_LOADED, null); 115 resultReceiver.send(RESULT_LIB_LOADED, null);
145 116
117
146 if (intent != null && intent.getAction() != null) 118 if (intent != null && intent.getAction() != null)
147 { 119 {
148 Log.d("RockboxService", intent.getAction()); 120 if (!rockbox_running)
149 if (intent.getAction().equals("org.rockbox.PlayPause")) 121 { /* give it a bit of time so we can register button presses
150 { 122 * sleeping longer doesn't work here, apparently Android
151 if (fb != null) 123 * surpresses long sleeps during intent handling */
152 fb.onKeyUp(KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE, null); 124 try {
125 Thread.sleep(50);
126 }
127 catch (InterruptedException e) { }
153 } 128 }
154 else if (intent.getAction().equals("org.rockbox.Prev")) 129
130 if (intent.getAction().equals(Intent.ACTION_MEDIA_BUTTON))
155 { 131 {
156 if (fb != null) 132 KeyEvent kev = (KeyEvent)intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT);
157 fb.onKeyUp(KeyEvent.KEYCODE_MEDIA_PREVIOUS, null); 133 RockboxFramebuffer.buttonHandler(kev.getKeyCode(), kev.getAction() == KeyEvent.ACTION_DOWN);
158 } 134 }
135 else if (intent.getAction().equals("org.rockbox.PlayPause"))
136 RockboxFramebuffer.buttonHandler(KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE, false);
137 else if (intent.getAction().equals("org.rockbox.Prev"))
138 RockboxFramebuffer.buttonHandler(KeyEvent.KEYCODE_MEDIA_PREVIOUS, false);
159 else if (intent.getAction().equals("org.rockbox.Next")) 139 else if (intent.getAction().equals("org.rockbox.Next"))
160 { 140 RockboxFramebuffer.buttonHandler(KeyEvent.KEYCODE_MEDIA_NEXT, false);
161 if (fb != null)
162 fb.onKeyUp(KeyEvent.KEYCODE_MEDIA_NEXT, null);
163 }
164 else if (intent.getAction().equals("org.rockbox.Stop")) 141 else if (intent.getAction().equals("org.rockbox.Stop"))
165 { 142 RockboxFramebuffer.buttonHandler(KeyEvent.KEYCODE_MEDIA_STOP, false);
166 if (fb != null)
167 fb.onKeyUp(KeyEvent.KEYCODE_MEDIA_STOP, null);
168 }
169 } 143 }
170 144
171 /* (Re-)attach the media button receiver, in case it has been lost */ 145 /* (Re-)attach the media button receiver, in case it has been lost */
172 mMediaButtonReceiver.register(); 146 mMediaButtonReceiver.register();
173
174 if (resultReceiver != null) 147 if (resultReceiver != null)
175 resultReceiver.send(RESULT_SERVICE_RUNNING, null); 148 resultReceiver.send(RESULT_SERVICE_RUNNING, null);
149
150 rockbox_running = true;
176 } 151 }
177 152
178 private void LOG(CharSequence text) 153 private void LOG(CharSequence text)
@@ -195,16 +170,23 @@ public class RockboxService extends Service
195 return 1; /* old API compatibility: 1 == START_STICKY */ 170 return 1; /* old API compatibility: 1 == START_STICKY */
196 } 171 }
197 172
198 private void startservice() 173 private void startservice()
199 { 174 {
200 final int BUFFER = 8*1024; 175 final Object lock = new Object();
201 Thread rb = new Thread(new Runnable() 176 Thread rb = new Thread(new Runnable()
202 { 177 {
203 public void run() 178 public void run()
204 { 179 {
180 final int BUFFER = 8*1024;
205 String rockboxDirPath = "/data/data/org.rockbox/app_rockbox/rockbox"; 181 String rockboxDirPath = "/data/data/org.rockbox/app_rockbox/rockbox";
206 File rockboxDir = new File(rockboxDirPath); 182 File rockboxDir = new File(rockboxDirPath);
207 183
184 /* load library before unzipping which may take a while */
185 synchronized (lock) {
186 System.loadLibrary("rockbox");
187 lock.notify();
188 }
189
208 /* the following block unzips libmisc.so, which contains the files 190 /* the following block unzips libmisc.so, which contains the files
209 * we ship, such as themes. It's needed to put it into a .so file 191 * we ship, such as themes. It's needed to put it into a .so file
210 * because there's no other way to ship files and have access 192 * because there's no other way to ship files and have access
@@ -268,13 +250,7 @@ public class RockboxService extends Service
268 } 250 }
269 } 251 }
270 } 252 }
271
272 synchronized (lock) {
273 System.loadLibrary("rockbox");
274 lock.notify();
275 }
276 253
277 rockbox_running = true;
278 if (resultReceiver != null) 254 if (resultReceiver != null)
279 resultReceiver.send(RESULT_INVOKING_MAIN, null); 255 resultReceiver.send(RESULT_INVOKING_MAIN, null);
280 256
@@ -283,7 +259,20 @@ public class RockboxService extends Service
283 } 259 }
284 }, "Rockbox thread"); 260 }, "Rockbox thread");
285 rb.setDaemon(false); 261 rb.setDaemon(false);
286 rb.start(); 262 /* wait at least until the library is loaded */
263 synchronized (lock)
264 {
265 rb.start();
266 while(true)
267 {
268 try {
269 lock.wait();
270 } catch (InterruptedException e) {
271 continue;
272 }
273 break;
274 }
275 }
287 } 276 }
288 277
289 private native void main(); 278 private native void main();
@@ -352,5 +341,7 @@ public class RockboxService extends Service
352 mMediaButtonReceiver = null; 341 mMediaButtonReceiver = null;
353 /* Make sure our notification is gone. */ 342 /* Make sure our notification is gone. */
354 stopForeground(); 343 stopForeground();
344 instance = null;
345 rockbox_running = false;
355 } 346 }
356} 347}