35 import android.os.Messenger; |
35 import android.os.Messenger; |
36 import android.os.RemoteException; |
36 import android.os.RemoteException; |
37 import android.widget.RemoteViews; |
37 import android.widget.RemoteViews; |
38 |
38 |
39 public class DownloadService extends Service { |
39 public class DownloadService extends Service { |
40 public final static String INTENT_TASKID = "taskId"; |
40 public final static String INTENT_TASKID = "taskId"; |
41 public final static String INTENT_TASK = "task"; |
41 public final static String INTENT_TASK = "task"; |
42 |
42 |
43 public static final String PREF_DOWNLOADED = "downloaded"; |
43 public static final String PREF_DOWNLOADED = "downloaded"; |
44 public static final int MSG_CANCEL = 0; |
44 public static final int MSG_CANCEL = 0; |
45 public static final int MSG_UNREGISTER_CLIENT = 2; |
45 public static final int MSG_UNREGISTER_CLIENT = 2; |
46 public final static int MSG_ADDTASK = 4; |
46 public final static int MSG_ADDTASK = 4; |
47 |
47 |
48 public static final int NOTIFICATION_PROCESSING = 0; |
48 public static final int NOTIFICATION_PROCESSING = 0; |
49 public static final int NOTIFICATION_DONE = 1; |
49 public static final int NOTIFICATION_DONE = 1; |
50 |
50 |
51 private DownloadAsyncTask asyncExecutor; |
51 private DownloadAsyncTask asyncExecutor; |
52 |
52 |
53 private DownloadHandler handler = new DownloadHandler(); |
53 private DownloadHandler handler = new DownloadHandler(); |
54 private final Messenger messenger = new Messenger(handler); |
54 private final Messenger messenger = new Messenger(handler); |
55 |
55 |
56 private NotificationManager nM; |
56 private NotificationManager nM; |
57 private RemoteViews contentView; |
57 private RemoteViews contentView; |
58 |
58 |
59 private LinkedList<DownloadTask> downloadTasks = new LinkedList<DownloadTask>(); |
59 private LinkedList<DownloadTask> downloadTasks = new LinkedList<DownloadTask>(); |
60 private DownloadTask currentTask = null; |
60 private DownloadTask currentTask = null; |
61 |
61 |
62 public class DownloadHandler extends Handler{ |
62 public class DownloadHandler extends Handler{ |
63 |
63 |
64 public void handleMessage(Message msg){ |
64 public void handleMessage(Message msg){ |
65 if(msg.obj != null){ |
65 if(msg.obj != null){ |
66 DownloadPackage pack = (DownloadPackage) msg.obj; |
66 DownloadPackage pack = (DownloadPackage) msg.obj; |
67 DownloadTask task = null; |
67 DownloadTask task = null; |
68 Messenger replyToMessenger = msg.replyTo; |
68 Messenger replyToMessenger = msg.replyTo; |
69 for(DownloadTask _task : downloadTasks){ |
69 for(DownloadTask _task : downloadTasks){ |
70 if(_task.getPackage().equals(pack)){ |
70 if(_task.getPackage().equals(pack)){ |
71 task = _task; |
71 task = _task; |
72 break; |
72 break; |
73 } |
73 } |
74 } |
74 } |
75 |
75 |
76 switch(msg.what){ |
76 switch(msg.what){ |
77 case MSG_ADDTASK: |
77 case MSG_ADDTASK: |
78 if(task == null){ |
78 if(task == null){ |
79 task = new DownloadTask(pack); |
79 task = new DownloadTask(pack); |
80 downloadTasks.add(task); |
80 downloadTasks.add(task); |
81 } |
81 } |
82 |
82 |
83 task.addClient(replyToMessenger); |
83 task.addClient(replyToMessenger); |
84 runNextTask(); |
84 runNextTask(); |
85 return; |
85 return; |
86 case MSG_CANCEL: |
86 case MSG_CANCEL: |
87 if(task != null && task.getPackage().equals(pack) && task.getStatus() == TASK_STATE.PENDING){ |
87 if(task != null && task.getPackage().equals(pack) && task.getStatus() == TASK_STATE.PENDING){ |
88 downloadTasks.remove(task); |
88 downloadTasks.remove(task); |
89 } |
89 } |
90 if(currentTask != null && currentTask.getPackage().equals(pack)){//TODO synchronization problem? |
90 if(currentTask != null && currentTask.getPackage().equals(pack)){//TODO synchronization problem? |
91 asyncExecutor.cancel(false); |
91 asyncExecutor.cancel(false); |
92 } |
92 } |
93 return; |
93 return; |
94 case MSG_UNREGISTER_CLIENT: |
94 case MSG_UNREGISTER_CLIENT: |
95 if(task != null){ |
95 if(task != null){ |
96 task.removeClient(replyToMessenger); |
96 task.removeClient(replyToMessenger); |
97 } |
97 } |
98 return; |
98 return; |
99 } |
99 } |
100 } |
100 } |
101 } |
101 } |
102 } |
102 } |
103 |
103 |
104 public void onCreate(){ |
104 public void onCreate(){ |
105 super.onCreate(); |
105 super.onCreate(); |
106 nM = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); |
106 nM = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); |
107 } |
107 } |
108 public IBinder onBind(Intent intent) { |
108 public IBinder onBind(Intent intent) { |
109 return messenger.getBinder(); |
109 return messenger.getBinder(); |
110 } |
110 } |
111 |
111 |
112 private void runNextTask(){ |
112 private void runNextTask(){ |
113 if(asyncExecutor == null){//if (task isnt running right now) ... |
113 if(asyncExecutor == null){//if (task isnt running right now) ... |
114 currentTask = downloadTasks.poll(); |
114 currentTask = downloadTasks.poll(); |
115 if(currentTask != null){ |
115 if(currentTask != null){ |
116 asyncExecutor = new DownloadAsyncTask(currentTask); |
116 asyncExecutor = new DownloadAsyncTask(currentTask); |
117 asyncExecutor.execute(currentTask.getPackage()); |
117 asyncExecutor.execute(currentTask.getPackage()); |
118 } |
118 } |
119 } |
119 } |
120 } |
120 } |
121 |
121 |
122 public void onDestroy(){ |
122 public void onDestroy(){ |
123 super.onDestroy(); |
123 super.onDestroy(); |
124 asyncExecutor.cancel(false); |
124 asyncExecutor.cancel(false); |
125 } |
125 } |
126 |
126 |
127 class DownloadTask { |
127 class DownloadTask { |
128 private final DownloadPackage pack; |
128 private final DownloadPackage pack; |
129 private TASK_STATE status = TASK_STATE.PENDING; |
129 private TASK_STATE status = TASK_STATE.PENDING; |
130 private Notification progressNotification, doneNotification; |
130 private Notification progressNotification, doneNotification; |
131 |
131 |
132 //I expect little to no removeClient calls that's why we go for a list rather than a map |
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; |
133 private final List<Messenger> clients; |
134 |
134 |
135 public DownloadTask(DownloadPackage _pack){ |
135 public DownloadTask(DownloadPackage _pack){ |
136 pack = _pack; |
136 pack = _pack; |
137 clients = new LinkedList<Messenger>(); |
137 clients = new LinkedList<Messenger>(); |
138 } |
138 } |
139 |
139 |
140 public void addClient(Messenger messenger){ |
140 public void addClient(Messenger messenger){ |
141 clients.add(messenger); |
141 clients.add(messenger); |
142 } |
142 } |
143 public void removeClient(Messenger messenger){ |
143 public void removeClient(Messenger messenger){ |
144 clients.remove(messenger); |
144 clients.remove(messenger); |
145 } |
145 } |
146 |
146 |
147 public DownloadPackage getPackage(){ |
147 public DownloadPackage getPackage(){ |
148 return pack; |
148 return pack; |
149 } |
149 } |
150 |
150 |
151 public TASK_STATE getStatus(){ |
151 public TASK_STATE getStatus(){ |
152 return status; |
152 return status; |
153 } |
153 } |
154 |
154 |
155 public void sendMessageToClients(Message msg){ |
155 public void sendMessageToClients(Message msg){ |
156 for(Messenger messenger : clients){ |
156 for(Messenger messenger : clients){ |
157 try { |
157 try { |
158 messenger.send(msg); |
158 messenger.send(msg); |
159 } catch (RemoteException e) { |
159 } catch (RemoteException e) { |
160 e.printStackTrace(); |
160 e.printStackTrace(); |
161 } |
161 } |
162 } |
162 } |
163 } |
163 } |
164 |
164 |
165 /* |
165 /* |
166 * Callbacks called from the async tasks |
166 * Callbacks called from the async tasks |
167 */ |
167 */ |
168 |
168 |
169 //Thread safe method to let clients know the processing is starting and will process int max kbytes |
169 //Thread safe method to let clients know the processing is starting and will process int max kbytes |
170 public void start(int max){ |
170 public void start(int max){ |
171 progressNotification = new Notification(R.drawable.statusbar, getString(R.string.notification_title), System.currentTimeMillis()); |
171 progressNotification = new Notification(R.drawable.statusbar, getString(R.string.notification_title), System.currentTimeMillis()); |
172 progressNotification.flags |= Notification.FLAG_ONGOING_EVENT; |
172 progressNotification.flags |= Notification.FLAG_ONGOING_EVENT; |
173 |
173 |
174 contentView = new RemoteViews(getPackageName(), R.layout.notification); |
174 contentView = new RemoteViews(getPackageName(), R.layout.notification); |
175 contentView.setProgressBar(R.id.notification_progress, 100, 34, false); |
175 contentView.setProgressBar(R.id.notification_progress, 100, 34, false); |
176 progressNotification.contentView = contentView; |
176 progressNotification.contentView = contentView; |
177 |
177 |
178 PendingIntent contentIntent = PendingIntent.getActivity(DownloadService.this, 0, new Intent(DownloadService.this, DownloadListActivity.class), Intent.FLAG_ACTIVITY_NEW_TASK); |
178 PendingIntent contentIntent = PendingIntent.getActivity(DownloadService.this, 0, new Intent(DownloadService.this, DownloadListActivity.class), Intent.FLAG_ACTIVITY_NEW_TASK); |
179 progressNotification.contentIntent = contentIntent; |
179 progressNotification.contentIntent = contentIntent; |
180 |
180 |
181 startForeground(NOTIFICATION_PROCESSING, progressNotification); |
181 startForeground(NOTIFICATION_PROCESSING, progressNotification); |
182 |
182 |
183 Message msg = Message.obtain(null, DownloadFragment.MSG_START, max, 0); |
183 Message msg = Message.obtain(null, DownloadFragment.MSG_START, max, 0); |
184 sendMessageToClients(msg); |
184 sendMessageToClients(msg); |
185 } |
185 } |
186 |
186 |
187 //periodically gets called by the ASyncTask, we can't tell for sure when it's called |
187 //periodically gets called by the ASyncTask, we can't tell for sure when it's called |
188 public void update(int progress, int max, String fileName){ |
188 public void update(int progress, int max, String fileName){ |
189 progress = (progress/1024); |
189 progress = (progress/1024); |
190 |
190 |
191 contentView.setProgressBar(R.id.notification_progress, max, progress, false); |
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)); |
192 contentView.setTextViewText(R.id.progressbar_sub, String.format("%dkb/%dkb (Compressed sizes)", progress, max)); |
193 nM.notify(NOTIFICATION_PROCESSING, progressNotification); |
193 nM.notify(NOTIFICATION_PROCESSING, progressNotification); |
194 |
194 |
195 sendMessageToClients(Message.obtain(handler, DownloadFragment.MSG_UPDATE, progress, max, fileName)); |
195 sendMessageToClients(Message.obtain(handler, DownloadFragment.MSG_UPDATE, progress, max, fileName)); |
196 } |
196 } |
197 |
197 |
198 //Call back from the ASync task when the task has either run into an error or finished otherwise |
198 //Call back from the ASync task when the task has either run into an error or finished otherwise |
199 public void done(int result){ |
199 public void done(int result){ |
200 switch(result){ |
200 switch(result){ |
201 case DownloadAsyncTask.EXIT_SUCCESS: sendMessageToClients(Message.obtain(handler, DownloadFragment.MSG_DONE)); break; |
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; |
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; |
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; |
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; |
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; |
206 case DownloadAsyncTask.EXIT_CANCELLED: sendMessageToClients(Message.obtain(handler, DownloadFragment.MSG_DONE)); break; |
207 } |
207 } |
208 |
208 |
209 stopForeground(true); |
209 stopForeground(true); |
210 nM.cancel(NOTIFICATION_PROCESSING); |
210 nM.cancel(NOTIFICATION_PROCESSING); |
211 |
211 |
212 String title = getString(R.string.notification_title); |
212 String title = getString(R.string.notification_title); |
213 |
213 |
214 doneNotification = new Notification(R.drawable.icon, title, System.currentTimeMillis()); |
214 doneNotification = new Notification(R.drawable.icon, title, System.currentTimeMillis()); |
215 doneNotification.flags |= Notification.FLAG_AUTO_CANCEL; |
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); |
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); |
217 doneNotification.setLatestEventInfo(DownloadService.this, title, getString(R.string.notification_done) + pack, contentIntent); |
218 nM.notify(pack.getId(), doneNotification); |
218 nM.notify(pack.getId(), doneNotification); |
219 |
219 |
220 asyncExecutor = null; |
220 asyncExecutor = null; |
221 runNextTask();//see if there are more tasks |
221 runNextTask();//see if there are more tasks |
222 } |
222 } |
223 |
223 |
224 } |
224 } |
225 |
225 |
226 enum TASK_STATE{ |
226 enum TASK_STATE{ |
227 RUNNING, FINISHED, PENDING; |
227 RUNNING, FINISHED, PENDING; |
228 } |
228 } |
229 |
229 |
230 } |
230 } |