Android: Rework notification and change icon sizes to better meet the systems' standard.
[kugel-rb.git] / android / src / org / rockbox / RockboxService.java
blob23086eae290cc2114b4cb93106330dadfe62ba3d
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.FileOutputStream;
28 import java.util.Enumeration;
29 import java.util.Timer;
30 import java.util.TimerTask;
31 import java.util.zip.ZipEntry;
32 import java.util.zip.ZipFile;
34 import org.rockbox.Helper.RunForegroundManager;
36 import android.app.Activity;
37 import android.app.Service;
38 import android.content.BroadcastReceiver;
39 import android.content.Context;
40 import android.content.Intent;
41 import android.content.IntentFilter;
42 import android.os.Bundle;
43 import android.os.IBinder;
44 import android.os.ResultReceiver;
45 import android.util.Log;
47 /* This class is used as the main glue between java and c.
48 * All access should be done through RockboxService.get_instance() for safety.
51 public class RockboxService extends Service
53 /* this Service is really a singleton class - well almost.
54 * To do it properly this line should be instance = new RockboxService()
55 * but apparently that doesnt work with the way android Services are created.
57 private static RockboxService instance = null;
59 /* locals needed for the c code and rockbox state */
60 private RockboxFramebuffer fb = null;
61 private boolean mRockboxRunning = false;
62 private volatile boolean rbLibLoaded;
63 private Activity current_activity = null;
64 private IntentFilter itf;
65 private BroadcastReceiver batt_monitor;
66 private RunForegroundManager fg_runner;
67 @SuppressWarnings("unused")
68 private int battery_level;
69 private ResultReceiver resultReceiver;
71 public static final int RESULT_LIB_LOADED = 0;
72 public static final int RESULT_LIB_LOAD_PROGRESS = 1;
73 public static final int RESULT_FB_INITIALIZED = 2;
74 public static final int RESULT_ERROR_OCCURED = 3;
76 @Override
77 public void onCreate()
79 instance = this;
82 public static RockboxService get_instance()
84 return instance;
87 public RockboxFramebuffer get_fb()
89 return fb;
91 /* framebuffer is initialised by the native code(!) so this is needed */
92 public void set_fb(RockboxFramebuffer newfb)
94 fb = newfb;
95 mRockboxRunning = true;
96 if (resultReceiver != null)
97 resultReceiver.send(RESULT_FB_INITIALIZED, null);
100 public Activity get_activity()
102 return current_activity;
104 public void set_activity(Activity a)
106 current_activity = a;
109 private void do_start(Intent intent)
111 LOG("Start Service");
113 if (intent != null && intent.hasExtra("callback"))
114 resultReceiver = (ResultReceiver) intent.getParcelableExtra("callback");
115 if (!rbLibLoaded)
116 startservice();
118 /* Display a notification about us starting.
119 * We put an icon in the status bar. */
120 if (fg_runner == null)
122 try {
123 fg_runner = new RunForegroundManager(this);
124 } catch (Exception e) {
125 e.printStackTrace();
130 private void LOG(CharSequence text)
132 Log.d("Rockbox", (String) text);
135 private void LOG(CharSequence text, Throwable tr)
137 Log.d("Rockbox", (String) text, tr);
140 public void onStart(Intent intent, int startId) {
141 do_start(intent);
144 public int onStartCommand(Intent intent, int flags, int startId)
146 do_start(intent);
147 return 1; /* old API compatibility: 1 == START_STICKY */
150 private void startservice()
152 final int BUFFER = 8*1024;
153 Thread rb = new Thread(new Runnable()
155 public void run()
157 String rockboxDirPath = "/data/data/org.rockbox/app_rockbox/rockbox";
158 File rockboxDir = new File(rockboxDirPath);
160 /* the following block unzips libmisc.so, which contains the files
161 * we ship, such as themes. It's needed to put it into a .so file
162 * because there's no other way to ship files and have access
163 * to them from native code
165 File libMisc = new File("/data/data/org.rockbox/lib/libmisc.so");
166 /* use arbitrary file to determine whether extracting is needed */
167 File arbitraryFile = new File(rockboxDir, "viewers.config");
168 if (!arbitraryFile.exists() || (libMisc.lastModified() > arbitraryFile.lastModified()))
172 Bundle progressData = new Bundle();
173 byte data[] = new byte[BUFFER];
174 ZipFile zipfile = new ZipFile(libMisc);
175 Enumeration<? extends ZipEntry> e = zipfile.entries();
176 progressData.putInt("max", zipfile.size());
178 while(e.hasMoreElements())
180 ZipEntry entry = (ZipEntry) e.nextElement();
181 File file;
182 /* strip off /.rockbox when extracting */
183 String fileName = entry.getName();
184 int slashIndex = fileName.indexOf('/', 1);
185 file = new File(rockboxDirPath + fileName.substring(slashIndex));
187 if (!entry.isDirectory())
189 /* Create the parent folders if necessary */
190 File folder = new File(file.getParent());
191 if (!folder.exists())
192 folder.mkdirs();
194 /* Extract file */
195 BufferedInputStream is = new BufferedInputStream(zipfile.getInputStream(entry), BUFFER);
196 FileOutputStream fos = new FileOutputStream(file);
197 BufferedOutputStream dest = new BufferedOutputStream(fos, BUFFER);
199 int count;
200 while ((count = is.read(data, 0, BUFFER)) != -1)
201 dest.write(data, 0, count);
203 dest.flush();
204 dest.close();
205 is.close();
208 if (resultReceiver != null) {
209 progressData.putInt("value", progressData.getInt("value", 0) + 1);
210 resultReceiver.send(RESULT_LIB_LOAD_PROGRESS, progressData);
213 } catch(Exception e) {
214 LOG("Exception when unzipping", e);
215 e.printStackTrace();
216 if (resultReceiver != null) {
217 Bundle bundle = new Bundle();
218 bundle.putString("error", getString(R.string.error_extraction));
219 resultReceiver.send(RESULT_ERROR_OCCURED, bundle);
224 System.loadLibrary("rockbox");
225 rbLibLoaded = true;
226 if (resultReceiver != null)
227 resultReceiver.send(RESULT_LIB_LOADED, null);
229 main();
230 throw new IllegalStateException("native main() returned!");
232 }, "Rockbox thread");
233 rb.setDaemon(false);
234 rb.start();
236 private native void main();
238 /* returns true once rockbox is up and running.
239 * This is considered done once the framebuffer is initialised
241 public boolean isRockboxRunning()
243 return mRockboxRunning;
246 @Override
247 public IBinder onBind(Intent intent)
249 // TODO Auto-generated method stub
250 return null;
254 @SuppressWarnings("unused")
256 * Sets up the battery monitor which receives the battery level
257 * about each 30 seconds
259 private void initBatteryMonitor()
261 itf = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
262 batt_monitor = new BroadcastReceiver()
264 @Override
265 public void onReceive(Context context, Intent intent)
267 /* we get literally spammed with battery statuses
268 * if we don't delay the re-attaching
270 TimerTask tk = new TimerTask()
272 public void run()
274 registerReceiver(batt_monitor, itf);
277 Timer t = new Timer();
278 context.unregisterReceiver(this);
279 int rawlevel = intent.getIntExtra("level", -1);
280 int scale = intent.getIntExtra("scale", -1);
281 if (rawlevel >= 0 && scale > 0)
282 battery_level = (rawlevel * 100) / scale;
283 else
284 battery_level = -1;
285 /* query every 30s should be sufficient */
286 t.schedule(tk, 30000);
289 registerReceiver(batt_monitor, itf);
292 public void startForeground()
294 fg_runner.startForeground();
297 public void stopForeground()
299 fg_runner.stopForeground();
302 @Override
303 public void onDestroy()
305 super.onDestroy();
306 fb.destroy();
307 /* Make sure our notification is gone. */
308 stopForeground();