1 /* 2 * Hedgewars for Android. An Android port of Hedgewars, a free turn based strategy game 3 * Copyright (c) 2011-2012 Richard Deurwaarder <xeli@xelification.com> 4 * 5 * This program is free software; you can redistribute it and/or 6 * modify it under the terms of the GNU General Public License 7 * as published by the Free Software Foundation; either version 2 8 * of the License, or (at your option) any later version. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program; if not, write to the Free Software 17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 18 */ 19 20 package org.hedgewars.hedgeroid.Downloader; 21 22 import java.util.LinkedList; 23 import java.util.List; 24 25 import org.hedgewars.hedgeroid.R; 26 27 import android.app.Notification; 28 import android.app.NotificationManager; 29 import android.app.PendingIntent; 30 import android.app.Service; 31 import android.content.Intent; 32 import android.os.Handler; 33 import android.os.IBinder; 34 import android.os.Message; 35 import android.os.Messenger; 36 import android.os.RemoteException; 37 import android.widget.RemoteViews; 38 39 public class DownloadService extends Service { 40 public final static String INTENT_TASKID = "taskId"; 41 public final static String INTENT_TASK = "task"; 42 43 public static final String PREF_DOWNLOADED = "downloaded"; 44 public static final int MSG_CANCEL = 0; 45 public static final int MSG_UNREGISTER_CLIENT = 2; 46 public final static int MSG_ADDTASK = 4; 47 48 public static final int NOTIFICATION_PROCESSING = 0; 49 public static final int NOTIFICATION_DONE = 1; 50 51 private DownloadAsyncTask asyncExecutor; 52 53 private DownloadHandler handler = new DownloadHandler(); 54 private final Messenger messenger = new Messenger(handler); 55 56 private NotificationManager nM; 57 private RemoteViews contentView; 58 59 private LinkedList<DownloadTask> downloadTasks = new LinkedList<DownloadTask>(); 60 private DownloadTask currentTask = null; 61 62 public class DownloadHandler extends Handler{ 63 handleMessage(Message msg)64 public void handleMessage(Message msg){ 65 if(msg.obj != null){ 66 DownloadPackage pack = (DownloadPackage) msg.obj; 67 DownloadTask task = null; 68 Messenger replyToMessenger = msg.replyTo; 69 for(DownloadTask _task : downloadTasks){ 70 if(_task.getPackage().equals(pack)){ 71 task = _task; 72 break; 73 } 74 } 75 76 switch(msg.what){ 77 case MSG_ADDTASK: 78 if(task == null){ 79 task = new DownloadTask(pack); 80 downloadTasks.add(task); 81 } 82 83 task.addClient(replyToMessenger); 84 runNextTask(); 85 return; 86 case MSG_CANCEL: 87 if(task != null && task.getPackage().equals(pack) && task.getStatus() == TASK_STATE.PENDING){ 88 downloadTasks.remove(task); 89 } 90 if(currentTask != null && currentTask.getPackage().equals(pack)){//TODO synchronization problem? 91 asyncExecutor.cancel(false); 92 } 93 return; 94 case MSG_UNREGISTER_CLIENT: 95 if(task != null){ 96 task.removeClient(replyToMessenger); 97 } 98 return; 99 } 100 } 101 } 102 } 103 onCreate()104 public void onCreate(){ 105 super.onCreate(); 106 nM = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); 107 } onBind(Intent intent)108 public IBinder onBind(Intent intent) { 109 return messenger.getBinder(); 110 } 111 runNextTask()112 private void runNextTask(){ 113 if(asyncExecutor == null){//if (task isnt running right now) ... 114 currentTask = downloadTasks.poll(); 115 if(currentTask != null){ 116 asyncExecutor = new DownloadAsyncTask(currentTask); 117 asyncExecutor.execute(currentTask.getPackage()); 118 } 119 } 120 } 121 onDestroy()122 public void onDestroy(){ 123 super.onDestroy(); 124 asyncExecutor.cancel(false); 125 } 126 127 class DownloadTask { 128 private final DownloadPackage pack; 129 private TASK_STATE status = TASK_STATE.PENDING; 130 private Notification progressNotification, doneNotification; 131 132 //I expect little to no removeClient calls that's why we go for a list rather than a map 133 private final List<Messenger> clients; 134 DownloadTask(DownloadPackage _pack)135 public DownloadTask(DownloadPackage _pack){ 136 pack = _pack; 137 clients = new LinkedList<Messenger>(); 138 } 139 addClient(Messenger messenger)140 public void addClient(Messenger messenger){ 141 clients.add(messenger); 142 } removeClient(Messenger messenger)143 public void removeClient(Messenger messenger){ 144 clients.remove(messenger); 145 } 146 getPackage()147 public DownloadPackage getPackage(){ 148 return pack; 149 } 150 getStatus()151 public TASK_STATE getStatus(){ 152 return status; 153 } 154 sendMessageToClients(Message msg)155 public void sendMessageToClients(Message msg){ 156 for(Messenger messenger : clients){ 157 try { 158 messenger.send(msg); 159 } catch (RemoteException e) { 160 e.printStackTrace(); 161 } 162 } 163 } 164 165 /* 166 * Callbacks called from the async tasks 167 */ 168 169 //Thread safe method to let clients know the processing is starting and will process int max kbytes start(int max)170 public void start(int max){ 171 progressNotification = new Notification(R.drawable.statusbar, getString(R.string.notification_title), System.currentTimeMillis()); 172 progressNotification.flags |= Notification.FLAG_ONGOING_EVENT; 173 174 contentView = new RemoteViews(getPackageName(), R.layout.notification); 175 contentView.setProgressBar(R.id.notification_progress, 100, 34, false); 176 progressNotification.contentView = contentView; 177 178 PendingIntent contentIntent = PendingIntent.getActivity(DownloadService.this, 0, new Intent(DownloadService.this, DownloadListActivity.class), Intent.FLAG_ACTIVITY_NEW_TASK); 179 progressNotification.contentIntent = contentIntent; 180 181 startForeground(NOTIFICATION_PROCESSING, progressNotification); 182 183 Message msg = Message.obtain(null, DownloadFragment.MSG_START, max, 0); 184 sendMessageToClients(msg); 185 } 186 187 //periodically gets called by the ASyncTask, we can't tell for sure when it's called update(int progress, int max, String fileName)188 public void update(int progress, int max, String fileName){ 189 progress = (progress/1024); 190 191 contentView.setProgressBar(R.id.notification_progress, max, progress, false); 192 contentView.setTextViewText(R.id.progressbar_sub, String.format("%dkb/%dkb (Compressed sizes)", progress, max)); 193 nM.notify(NOTIFICATION_PROCESSING, progressNotification); 194 195 sendMessageToClients(Message.obtain(handler, DownloadFragment.MSG_UPDATE, progress, max, fileName)); 196 } 197 198 //Call back from the ASync task when the task has either run into an error or finished otherwise done(int result)199 public void done(int result){ 200 switch(result){ 201 case DownloadAsyncTask.EXIT_SUCCESS: sendMessageToClients(Message.obtain(handler, DownloadFragment.MSG_DONE)); break; 202 case DownloadAsyncTask.EXIT_CONNERROR: sendMessageToClients(Message.obtain(handler, DownloadFragment.MSG_FAILED, DownloadAsyncTask.EXIT_CONNERROR, 0)); break; 203 case DownloadAsyncTask.EXIT_FNF: sendMessageToClients(Message.obtain(handler, DownloadFragment.MSG_FAILED, DownloadAsyncTask.EXIT_FNF, 0)); break; 204 case DownloadAsyncTask.EXIT_MD5: sendMessageToClients(Message.obtain(handler, DownloadFragment.MSG_FAILED, DownloadAsyncTask.EXIT_MD5, 0)); break; 205 case DownloadAsyncTask.EXIT_URLFAIL: sendMessageToClients(Message.obtain(handler, DownloadFragment.MSG_FAILED, DownloadAsyncTask.EXIT_URLFAIL, 0)); break; 206 case DownloadAsyncTask.EXIT_CANCELLED: sendMessageToClients(Message.obtain(handler, DownloadFragment.MSG_DONE)); break; 207 } 208 209 stopForeground(true); 210 nM.cancel(NOTIFICATION_PROCESSING); 211 212 String title = getString(R.string.notification_title); 213 214 doneNotification = new Notification(R.drawable.icon, title, System.currentTimeMillis()); 215 doneNotification.flags |= Notification.FLAG_AUTO_CANCEL; 216 PendingIntent contentIntent = PendingIntent.getActivity(DownloadService.this, 0, new Intent(DownloadService.this, DownloadListActivity.class), Intent.FLAG_ACTIVITY_NEW_TASK); 217 doneNotification.setLatestEventInfo(DownloadService.this, title, getString(R.string.notification_done) + pack, contentIntent); 218 nM.notify(pack.getId(), doneNotification); 219 220 asyncExecutor = null; 221 runNextTask();//see if there are more tasks 222 } 223 224 } 225 226 enum TASK_STATE{ 227 RUNNING, FINISHED, PENDING; 228 } 229 230 } 231