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