project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Downloader/DownloadService.java
branchhedgeroid
changeset 6350 41b0a9955c47
parent 6343 9df5a486f41e
child 6437 4ed58839b13b
equal deleted inserted replaced
6348:162fec525764 6350:41b0a9955c47
    18 
    18 
    19 
    19 
    20 package org.hedgewars.hedgeroid.Downloader;
    20 package org.hedgewars.hedgeroid.Downloader;
    21 
    21 
    22 import java.util.ArrayList;
    22 import java.util.ArrayList;
       
    23 import java.util.Deque;
    23 import java.util.LinkedList;
    24 import java.util.LinkedList;
       
    25 import java.util.List;
    24 
    26 
    25 import org.hedgewars.hedgeroid.MainActivity;
    27 import org.hedgewars.hedgeroid.MainActivity;
    26 import org.hedgewars.hedgeroid.R;
    28 import org.hedgewars.hedgeroid.R;
    27 import org.hedgewars.hedgeroid.Utils;
       
    28 
    29 
    29 import android.app.Notification;
    30 import android.app.Notification;
    30 import android.app.NotificationManager;
    31 import android.app.NotificationManager;
    31 import android.app.PendingIntent;
    32 import android.app.PendingIntent;
    32 import android.app.Service;
    33 import android.app.Service;
    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 }