Initialize (instantiate) RockboxFramebuffer from the C code like with the othe java...
[maemo-rb.git] / android / src / org / rockbox / RockboxService.java
blobbe02342768ac62911cd8a89e1627a84125a0436d
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2010 Thomas Martitz
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
20 ****************************************************************************/
22 package org.rockbox;
24 import java.io.BufferedInputStream;
25 import java.io.BufferedOutputStream;
26 import java.io.File;
27 import java.io.FileNotFoundException;
28 import java.io.FileOutputStream;
29 import java.io.IOException;
30 import java.lang.reflect.InvocationTargetException;
31 import java.lang.reflect.Method;
32 import java.util.Enumeration;
33 import java.util.Timer;
34 import java.util.TimerTask;
35 import java.util.zip.ZipEntry;
36 import java.util.zip.ZipFile;
38 import android.app.Notification;
39 import android.app.NotificationManager;
40 import android.app.PendingIntent;
41 import android.app.Service;
42 import android.content.BroadcastReceiver;
43 import android.content.Context;
44 import android.content.Intent;
45 import android.content.IntentFilter;
46 import android.os.IBinder;
47 import android.util.Log;
49 public class RockboxService extends Service
51 /* this Service is really a singleton class */
52 public static RockboxFramebuffer fb = null;
53 private static RockboxService instance;
54 private Notification notification;
55 private static final Class<?>[] mStartForegroundSignature =
56 new Class[] { int.class, Notification.class };
57 private static final Class<?>[] mStopForegroundSignature =
58 new Class[] { boolean.class };
60 private NotificationManager mNM;
61 private Method mStartForeground;
62 private Method mStopForeground;
63 private Object[] mStartForegroundArgs = new Object[2];
64 private Object[] mStopForegroundArgs = new Object[1];
65 private IntentFilter itf;
66 private BroadcastReceiver batt_monitor;
67 @SuppressWarnings("unused")
68 private int battery_level;
70 @Override
71 public void onCreate()
73 mNM = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
74 try
76 mStartForeground = getClass().getMethod("startForeground",
77 mStartForegroundSignature);
78 mStopForeground = getClass().getMethod("stopForeground",
79 mStopForegroundSignature);
81 catch (NoSuchMethodException e)
83 /* Running on an older platform: fall back to old API */
84 mStartForeground = mStopForeground = null;
86 instance = this;
87 startservice();
90 private void do_start(Intent intent)
92 LOG("Start Service");
94 /* Display a notification about us starting.
95 * We put an icon in the status bar. */
96 create_notification();
99 private void LOG(CharSequence text)
101 Log.d("Rockbox", (String) text);
104 private void LOG(CharSequence text, Throwable tr)
106 Log.d("Rockbox", (String) text, tr);
109 public void onStart(Intent intent, int startId) {
110 do_start(intent);
113 public int onStartCommand(Intent intent, int flags, int startId)
115 do_start(intent);
116 return 1; /* old API compatibility: 1 == START_STICKY */
119 private void startservice()
121 final int BUFFER = 8*1024;
122 Thread rb = new Thread(new Runnable()
124 public void run()
126 /* the following block unzips libmisc.so, which contains the files
127 * we ship, such as themes. It's needed to put it into a .so file
128 * because there's no other way to ship files and have access
129 * to them from native code
133 BufferedOutputStream dest = null;
134 BufferedInputStream is = null;
135 ZipEntry entry;
136 File file = new File("/data/data/org.rockbox/" +
137 "lib/libmisc.so");
138 /* use arbitrary file to determine whether extracting is needed */
139 File file2 = new File("/data/data/org.rockbox/" +
140 "app_rockbox/rockbox/codecs/mpa.codec");
141 if (!file2.exists() || (file.lastModified() > file2.lastModified()))
143 ZipFile zipfile = new ZipFile(file);
144 Enumeration<? extends ZipEntry> e = zipfile.entries();
145 File folder;
146 while(e.hasMoreElements())
148 entry = (ZipEntry) e.nextElement();
149 LOG("Extracting: " +entry);
150 if (entry.isDirectory())
152 folder = new File(entry.getName());
153 LOG("mkdir "+ entry);
154 try {
155 folder.mkdirs();
156 } catch (SecurityException ex) {
157 LOG(ex.getMessage());
159 continue;
161 is = new BufferedInputStream(zipfile.getInputStream(entry),
162 BUFFER);
163 int count;
164 byte data[] = new byte[BUFFER];
165 folder = new File(new File(entry.getName()).getParent());
166 LOG("" + folder.getAbsolutePath());
167 if (!folder.exists())
168 folder.mkdirs();
169 FileOutputStream fos = new FileOutputStream(entry.getName());
170 dest = new BufferedOutputStream(fos, BUFFER);
171 while ((count = is.read(data, 0, BUFFER)) != -1)
172 dest.write(data, 0, count);
173 dest.flush();
174 dest.close();
175 is.close();
178 } catch(FileNotFoundException e) {
179 LOG("FileNotFoundException when unzipping", e);
180 e.printStackTrace();
181 } catch(IOException e) {
182 LOG("IOException when unzipping", e);
183 e.printStackTrace();
186 System.loadLibrary("rockbox");
187 main();
189 },"Rockbox thread");
190 rb.setDaemon(false);
191 rb.start();
194 private native void main();
195 @Override
196 public IBinder onBind(Intent intent)
198 // TODO Auto-generated method stub
199 return null;
203 @SuppressWarnings("unused")
205 * Sets up the battery monitor which receives the battery level
206 * about each 30 seconds
208 private void initBatteryMonitor()
210 itf = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
211 batt_monitor = new BroadcastReceiver()
213 @Override
214 public void onReceive(Context context, Intent intent)
216 /* we get literally spammed with battery statuses
217 * if we don't delay the re-attaching
219 TimerTask tk = new TimerTask()
221 public void run()
223 registerReceiver(batt_monitor, itf);
226 Timer t = new Timer();
227 context.unregisterReceiver(this);
228 int rawlevel = intent.getIntExtra("level", -1);
229 int scale = intent.getIntExtra("scale", -1);
230 if (rawlevel >= 0 && scale > 0)
231 battery_level = (rawlevel * 100) / scale;
232 else
233 battery_level = -1;
234 /* query every 30s should be sufficient */
235 t.schedule(tk, 30000);
238 registerReceiver(batt_monitor, itf);
241 /* all below is heavily based on the examples found on
242 * http://developer.android.com/reference/android/app/Service.html
245 private void create_notification()
247 /* For now we'll use the same text for the ticker and the
248 * expanded notification */
249 CharSequence text = getText(R.string.notification);
250 /* Set the icon, scrolling text and timestamp */
251 notification = new Notification(R.drawable.icon, text,
252 System.currentTimeMillis());
254 /* The PendingIntent to launch our activity if the user selects
255 * this notification */
256 Intent intent = new Intent(this, RockboxActivity.class);
257 PendingIntent contentIntent =
258 PendingIntent.getActivity(this, 0, intent, 0);
260 /* Set the info for the views that show in the notification panel. */
261 notification.setLatestEventInfo(this,
262 getText(R.string.notification), text, contentIntent);
265 public static void startForeground()
267 if (instance != null)
270 * Send the notification.
271 * We use a layout id because it is a unique number.
272 * We use it later to cancel.
274 instance.mNM.notify(R.string.notification, instance.notification);
276 * this call makes the service run as foreground, which
277 * provides enough cpu time to do music decoding in the
278 * background
280 instance.startForegroundCompat(R.string.notification,
281 instance.notification);
285 public static void stopForeground()
287 if (instance.notification != null)
289 instance.stopForegroundCompat(R.string.notification);
290 instance.mNM.cancel(R.string.notification);
295 * This is a wrapper around the new startForeground method, using the older
296 * APIs if it is not available.
298 void startForegroundCompat(int id, Notification notification)
300 if (mStartForeground != null) {
301 mStartForegroundArgs[0] = Integer.valueOf(id);
302 mStartForegroundArgs[1] = notification;
303 try {
304 mStartForeground.invoke(this, mStartForegroundArgs);
305 } catch (InvocationTargetException e) {
306 /* Should not happen. */
307 LOG("Unable to invoke startForeground", e);
308 } catch (IllegalAccessException e) {
309 /* Should not happen. */
310 LOG("Unable to invoke startForeground", e);
312 return;
315 /* Fall back on the old API.*/
316 setForeground(true);
317 mNM.notify(id, notification);
321 * This is a wrapper around the new stopForeground method, using the older
322 * APIs if it is not available.
324 void stopForegroundCompat(int id)
326 if (mStopForeground != null) {
327 mStopForegroundArgs[0] = Boolean.TRUE;
328 try {
329 mStopForeground.invoke(this, mStopForegroundArgs);
330 } catch (InvocationTargetException e) {
331 /* Should not happen. */
332 LOG("Unable to invoke stopForeground", e);
333 } catch (IllegalAccessException e) {
334 /* Should not happen. */
335 LOG("Unable to invoke stopForeground", e);
337 return;
340 /* Fall back on the old API. Note to cancel BEFORE changing the
341 * foreground state, since we could be killed at that point. */
342 mNM.cancel(id);
343 setForeground(false);
346 @Override
347 public void onDestroy()
349 super.onDestroy();
350 /* Make sure our notification is gone. */
351 stopForegroundCompat(R.string.notification);