first part of the new downloader implementation hedgeroid
authorXeli
Mon, 14 Nov 2011 18:03:31 +0100
branchhedgeroid
changeset 6343 9df5a486f41e
parent 6340 9dd921c0c7e7
child 6346 db58a42bb5e7
first part of the new downloader implementation
hedgewars/uAI.pas
project_files/Android-build/SDL-android-project/AndroidManifest.xml
project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Downloader/DownloadActivity.java
project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Downloader/DownloadAsyncTask.java
project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Downloader/DownloadListActivity.java
project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Downloader/DownloadService.java
project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Downloader/DownloadTask.java
project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/MainActivity.java
--- a/hedgewars/uAI.pas	Mon Nov 14 17:59:26 2011 +0100
+++ b/hedgewars/uAI.pas	Mon Nov 14 18:03:31 2011 +0100
@@ -325,6 +325,7 @@
 {$ELSE}
 ThinkThread := SDL_CreateThread(@Think, Me);
 {$ENDIF}
+{$ENDIF}
 AddFileLog('Thread started');
 end;
 
--- a/project_files/Android-build/SDL-android-project/AndroidManifest.xml	Mon Nov 14 17:59:26 2011 +0100
+++ b/project_files/Android-build/SDL-android-project/AndroidManifest.xml	Mon Nov 14 18:03:31 2011 +0100
@@ -28,6 +28,13 @@
  		  		  android:launchMode="singleTask">
         </activity>
         
+        <activity android:name=".Downloader.DownloadListActivity"
+                  android:label="@string/app_name"
+				  android:theme="@android:style/Theme.NoTitleBar.Fullscreen">
+        </activity>
+        
+        <service android:name=".Downloader.DownloadService"/>
+        
         <activity android:name="StartGameActivity"
                   android:label="@string/app_name"
 				  android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
@@ -38,12 +45,10 @@
 				  android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
 				  android:screenOrientation='landscape'>
         </activity>
-                <activity android:name="TeamCreatorActivity"
+        <activity android:name="TeamCreatorActivity"
                   android:label="@string/app_name"
 				  android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
 				  android:screenOrientation='landscape'>
         </activity>
-        
-        <service android:name=".Downloader.DownloadService"/>
     </application>
 </manifest> 
--- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Downloader/DownloadActivity.java	Mon Nov 14 17:59:26 2011 +0100
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Downloader/DownloadActivity.java	Mon Nov 14 18:03:31 2011 +0100
@@ -114,7 +114,7 @@
 	private OnClickListener cancelClicker = new OnClickListener(){
 		public void onClick(View v){
 			Intent i = new Intent(getApplicationContext(), DownloadService.class);
-			i.putExtra("taskID", DownloadService.TASKID_CANCEL);
+			i.putExtra(DownloadService.INTENT_TASKID, DownloadService.TASKID_CANCEL);
 			startService(i);
 			finish();
 		}
@@ -128,13 +128,13 @@
 	
 	private OnClickListener tryAgainClicker = new OnClickListener(){
 		public void onClick(View v){
-			bindToService(DownloadService.TASKID_RETRY);
+			bindToService(DownloadService.TASKID_ADDTASK);
 		}
 	};
 	
 	public void onStart(){
 		super.onStart();
-		bindToService(DownloadService.TASKID_START);
+		bindToService(DownloadService.TASKID_SETUP);
 	}
 	
 	public void onStop(){
@@ -163,7 +163,7 @@
 	
 	private void bindToService(int taskId){
 		Intent i = new Intent(getApplicationContext(), DownloadService.class);
-		i.putExtra("taskID", taskId);
+		i.putExtra(DownloadService.INTENT_TASKID, taskId);
 		startService(i);
 		bindService(new Intent(getApplicationContext(), DownloadService.class), connection, Context.BIND_AUTO_CREATE);
 		boundToService = true;
--- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Downloader/DownloadAsyncTask.java	Mon Nov 14 17:59:26 2011 +0100
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Downloader/DownloadAsyncTask.java	Mon Nov 14 18:03:31 2011 +0100
@@ -32,7 +32,6 @@
 import java.util.zip.ZipInputStream;
 
 import android.os.AsyncTask;
-import android.util.Log;
 /**
  * This is an AsyncTask which will download a zip from an URL and unzip it to a specified path
  * 
@@ -40,13 +39,12 @@
  * @author Xeli
  *
  */
-public class DownloadAsyncTask extends AsyncTask<String, Object, Long> {
+public class DownloadAsyncTask extends AsyncTask<DownloadTask, Object, Long> {
 
-	private final static String URL_WITHOUT_SUFFIX = "http://hedgewars.googlecode.com/files/data_5631.";
 	//private final static String URL_WITHOUT_SUFFIX = "http://www.xelification.com/tmp/firebutton.";
-	private final static String URL_ZIP_SUFFIX = "zip";
-	private final static String URL_HASH_SUFFIX = "hash";
-
+	private final static String URL_ZIP_SUFFIX = ".zip";
+	private final static String URL_HASH_SUFFIX = ".hash";
+	
 	private DownloadService service;
 	private long lastUpdateMillis = 0;
 
@@ -56,18 +54,20 @@
 
 	/**
 	 * 
-	 * @param params - 2 Strings, first is the path where the unzipped files will be stored, second is the URL to download from
+	 * @param params - A {@link}DownloadTask which gives information about where to download from and store the files to 
 	 */
-	protected Long doInBackground(String... params) {
+	protected Long doInBackground(DownloadTask...tasks) {
+		DownloadTask task = tasks[0];//just use one task per execute call for now
+		
 		HttpURLConnection conn = null;
 		MessageDigest digester = null;
-		String rootZipDest = params[0];
+		String rootZipDest = task.getPathToStore();
 
 		File rootDest = new File(rootZipDest);//TODO check for nullpointer, it hints to the absence of an sdcard
 		rootDest.mkdir();
 
 		try {
-			URL url = new URL(URL_WITHOUT_SUFFIX + URL_ZIP_SUFFIX);
+			URL url = new URL(task.getURL() + URL_ZIP_SUFFIX);
 			conn = (HttpURLConnection)url.openConnection();
 		} catch (IOException e) {
 			e.printStackTrace();
@@ -161,7 +161,7 @@
 
 		if(conn != null) conn.disconnect();
 
-		if(checkMD5(digester))return 0l;
+		if(checkMD5(digester, task))return 0l;
 		else return -1l;
 	}
 
@@ -174,12 +174,12 @@
 		service.update((Integer)objects[0], (Integer)objects[1], (String)objects[2]);
 	}
 
-	private boolean checkMD5(MessageDigest digester){
+	private boolean checkMD5(MessageDigest digester, DownloadTask task){
 		if(digester != null) {
 			byte[] messageDigest = digester.digest();
 
 			try {
-				URL url = new URL(URL_WITHOUT_SUFFIX + URL_HASH_SUFFIX);
+				URL url = new URL(task.getURL() + URL_HASH_SUFFIX);
 				HttpURLConnection conn = (HttpURLConnection)url.openConnection();
 
 				byte[] buffer = new byte[1024];//size is large enough to hold the entire hash
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Downloader/DownloadListActivity.java	Mon Nov 14 18:03:31 2011 +0100
@@ -0,0 +1,36 @@
+package org.hedgewars.hedgeroid.Downloader;
+
+import android.app.ListActivity;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemClickListener;
+import android.widget.ArrayAdapter;
+
+public class DownloadListActivity extends ListActivity implements OnItemClickListener{
+
+	
+	public void onCreate(Bundle savedInstanceState){
+		super.onCreate(savedInstanceState);
+		
+		DownloadTask[] tasks = new DownloadTask[3];
+		tasks[0] = new DownloadTask("url1", "/home/path/1", 1, "entry 1");
+		tasks[1] = new DownloadTask("url2", "/home/path/2", 1, "entry 2");
+		tasks[2] = new DownloadTask("url3", "/home/path/3", 1, "entry 3");
+		
+		ArrayAdapter<DownloadTask> adapter = new ArrayAdapter<DownloadTask>(this, android.R.layout.simple_list_item_1, tasks);
+		this.setListAdapter(adapter);
+		this.getListView().setOnItemClickListener(this);
+		
+	}
+	
+	public void onItemClick(AdapterView<?> arg0, View arg1, int position, long arg3) {
+		DownloadTask task = (DownloadTask)arg0.getAdapter().getItem(position);
+	}
+	
+//	public static class Dialog extends DialogFragment{
+		
+//	}
+	
+}
+
--- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Downloader/DownloadService.java	Mon Nov 14 17:59:26 2011 +0100
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Downloader/DownloadService.java	Mon Nov 14 18:03:31 2011 +0100
@@ -20,6 +20,7 @@
 package org.hedgewars.hedgeroid.Downloader;
 
 import java.util.ArrayList;
+import java.util.LinkedList;
 
 import org.hedgewars.hedgeroid.MainActivity;
 import org.hedgewars.hedgeroid.R;
@@ -30,6 +31,7 @@
 import android.app.PendingIntent;
 import android.app.Service;
 import android.content.Intent;
+import android.os.AsyncTask;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Message;
@@ -41,6 +43,13 @@
 
 public class DownloadService extends Service {
 
+	public final static int TASKID_SETUP = 0;
+	public final static int TASKID_CANCEL = 1;
+	public final static int TASKID_ADDTASK = 2;
+
+	public final static String INTENT_TASKID = "taskId";
+	public final static String INTENT_TASK = "task";
+
 	public static final String PREF_DOWNLOADED = "downloaded";
 	public static final int MSG_CANCEL = 0;
 	public static final int MSG_REGISTER_CLIENT = 1;
@@ -49,12 +58,13 @@
 	public static final int NOTIFICATION_PROCESSING = 0;
 	public static final int NOTIFICATION_DONE = 1;
 
-	private DownloadAsyncTask downloadTask;
+	private DownloadAsyncTask asyncExecutor;
 	private final Messenger messenger = new Messenger(new DownloadHandler());
 	private NotificationManager nM;
 	private RemoteViews contentView;
 	private Notification notification;
 
+	private LinkedList<DownloadTask> downloadTasks = new LinkedList<DownloadTask>();
 	private ArrayList<Messenger> clientList = new ArrayList<Messenger>();
 	private Message onRegisterMessage = null;
 
@@ -64,7 +74,7 @@
 		public void handleMessage(Message msg){
 			switch(msg.what){
 			case MSG_CANCEL:
-				downloadTask.cancel(false);
+				asyncExecutor.cancel(false);
 				break;
 			case MSG_REGISTER_CLIENT:
 				clientList.add(msg.replyTo);
@@ -82,23 +92,19 @@
 			}
 		}
 	}
+	public IBinder onBind(Intent intent) {
+		return messenger.getBinder();
+	}
 
-	public final static int TASKID_START = 0;
-	public final static int TASKID_CANCEL = 1;
-	public final static int TASKID_RETRY = 2;
-	
+	/**
+	 * This is the main method which controls how DownloadService and DownloadAsyncTask are running
+	 */
 	public int onStartCommand(Intent intent, int flags, int startId){
-		switch(intent.getIntExtra("taskID", TASKID_START)){
-		case TASKID_RETRY:
-			if(downloadTask != null){
-				downloadTask.cancel(false);
-				downloadTask = null;
-			}
-		case TASKID_START:
+		switch(intent.getIntExtra("taskID", TASKID_SETUP)){
+		case TASKID_SETUP:
 			nM = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
 
 			notification = new Notification(R.drawable.statusbar, getString(R.string.notification_title), System.currentTimeMillis());
-			//notification.flags |= Notification.FLAG_ONGOING_EVENT;// | Notification.FLAG_NO_CLEAR | Notification.FLAG_FOREGROUND_SERVICE;
 			notification.flags |= Notification.FLAG_ONGOING_EVENT;
 
 			contentView = new RemoteViews(getPackageName(), R.layout.notification);
@@ -108,67 +114,66 @@
 			PendingIntent contentIntent = PendingIntent.getActivity(this, 0, new Intent(this, DownloadActivity.class), Intent.FLAG_ACTIVITY_NEW_TASK);
 			notification.contentIntent = contentIntent;
 
-			//nM.notify(NOTIFICATION_PROCESSING, notification);
-			startForeground(NOTIFICATION_PROCESSING, notification);
-
-			if(downloadTask == null){
-				downloadTask = new DownloadAsyncTask(this);
-				downloadTask.execute(Utils.getDownloadPath(this));
-			}	
+			asyncExecutor = new DownloadAsyncTask(this);
+			break;
+		case TASKID_ADDTASK:
+			//Add downloadtask to the queue
+			DownloadTask task = intent.getParcelableExtra(DownloadService.INTENT_TASK);
+			downloadTasks.offer(task);
+			runNextTask();
 			break;
 		case TASKID_CANCEL:
-			downloadTask.cancel(false);
-			stopService();
+			asyncExecutor.cancel(false);
 			break;
 		}		
 		return 0;
 	}
 
-	public void onDestroy(){
-		Log.e("bla", "onDestroy");
-		downloadTask.cancel(false);	
+	private synchronized void runNextTask(){
+		if(!asyncExecutor.getStatus().equals(AsyncTask.Status.RUNNING)){//if the task isnt running right now...
+			DownloadTask task = downloadTasks.poll();
+			if(task == null){
+				startForeground(NOTIFICATION_PROCESSING, notification);
+				asyncExecutor.execute(task);
+			}
+		}
 	}
 
-	public IBinder onBind(Intent intent) {
-		return messenger.getBinder();
+	public void onDestroy(){
+		super.onDestroy();
+		asyncExecutor.cancel(false);	
 	}
 
 	/*
-	 * Thread safe method to let clients know the processing is starting and will process int max kbytes
+	 * Callbacks called from the async tasks
 	 */
+
+	//Thread safe method to let clients know the processing is starting and will process int max kbytes
 	public void start(int max){
 		onRegisterMessage = Message.obtain(null, DownloadActivity.MSG_START, max, -1);
 		sendMessageToClients(onRegisterMessage);
 	}
 
-	/*
-	 * periodically gets called by the ASyncTask, we can't tell for sure when it's called
-	 */
+	//periodically gets called by the ASyncTask, we can't tell for sure when it's called
 	public void update(int progress, int max, String fileName){
 		progress = (progress/1024);
 		updateNotification(progress, max, fileName);
 
 		sendMessageToClients(Message.obtain(null, DownloadActivity.MSG_UPDATE, progress, max, fileName));
 	}
-	
-	/*
-	 * Call back from the ASync task when the task has either run into an error or finished otherwise
-	 */
+
+	//Call back from the ASync task when the task has either run into an error or finished otherwise
 	public void done(boolean succesful){
 		if(succesful){
-			PreferenceManager.getDefaultSharedPreferences(this).edit().putBoolean(DownloadService.PREF_DOWNLOADED, true).commit();
 			sendMessageToClients(Message.obtain(null, DownloadActivity.MSG_DONE));
 		}else sendMessageToClients(Message.obtain(null, DownloadActivity.MSG_FAILED));
-		stopService();//stopService clears all notifications and thus must be called before we show the ready notification
+		nM.cancel(NOTIFICATION_PROCESSING);
+		stopForeground(true);
 		showDoneNotification();
+		runNextTask();//see if there are more tasks
 	}
 
-	private void stopService(){
-		nM.cancelAll();
-		stopForeground(true);
-		stopSelf();
-	}
-	
+
 	private void updateNotification(int progress, int max, String fileName){
 
 		contentView.setProgressBar(R.id.notification_progress, max, progress, false);
@@ -177,9 +182,6 @@
 	}
 
 	private void showDoneNotification(){
-		nM.cancelAll();
-		stopForeground(true);
-
 		String title = getString(R.string.notification_title);
 
 		notification = new Notification(R.drawable.icon, title, System.currentTimeMillis());
@@ -187,7 +189,8 @@
 		PendingIntent contentIntent = PendingIntent.getActivity(this, 0, new Intent(this, MainActivity.class), Intent.FLAG_ACTIVITY_NEW_TASK);
 		notification.setLatestEventInfo(this, title, getString(R.string.notification_done), contentIntent);
 		nM.notify(NOTIFICATION_DONE, notification);
-	}	
+	}
+
 	private void sendMessageToClients(Message msg){
 		for(Messenger m : clientList){
 			try {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Downloader/DownloadTask.java	Mon Nov 14 18:03:31 2011 +0100
@@ -0,0 +1,158 @@
+/*
+ * Hedgewars for Android. An Android port of Hedgewars, a free turn based strategy game
+ * Copyright (c) 2011 Richard Deurwaarder <xeli@xelification.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+package org.hedgewars.hedgeroid.Downloader;
+
+import java.io.IOException;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+public class DownloadTask implements Parcelable{
+
+	private String url_without_suffix;
+	private String pathToStore;
+	private String representation;
+	private int attempts;
+	private int versionNumber;
+	
+	
+	public DownloadTask(Parcel in){
+		readFromParcel(in);
+	}
+	
+	public DownloadTask(String _url_without_suffix, String path, int version, String _representation){
+		url_without_suffix = _url_without_suffix;
+		pathToStore = path;
+		representation = _representation;
+		versionNumber = version;
+		attempts = 0;
+	}
+	
+	public int getAttempts(){
+		return attempts;
+	}
+	
+	public String getURL(){
+		return url_without_suffix;
+	}
+	
+	public String getPathToStore(){
+		return pathToStore;
+	}
+	
+	public void incrementAttempts(){
+		attempts++;
+	}
+	
+	public String toString(){
+		return representation;
+	}
+	
+	public int describeContents() {
+		return 0;
+	}
+
+	public void writeToParcel(Parcel dest, int flags) {
+		dest.writeString(url_without_suffix);
+		dest.writeString(pathToStore);
+		dest.writeString(representation);
+		dest.writeInt(versionNumber);
+		dest.writeInt(attempts);
+	}
+	
+	private void readFromParcel(Parcel src){
+		url_without_suffix = src.readString();
+		pathToStore = src.readString();
+		representation = src.readString();
+		versionNumber = src.readInt();
+		attempts = src.readInt();
+	}
+	
+	public static final Parcelable.Creator<DownloadTask> CREATOR = new Parcelable.Creator<DownloadTask>() {
+		public DownloadTask createFromParcel(Parcel source) {
+			return new DownloadTask(source);
+		}
+		public DownloadTask[] newArray(int size) {
+			return new DownloadTask[size];
+		}
+	};
+	
+	/*
+	 * We enter with a XmlPullParser.Start_tag with name "task"
+	 */
+	public static DownloadTask getTaskFromXML(XmlPullParser xmlPuller) throws XmlPullParserException, IOException{
+		String url = null;
+		String path = null;
+		String representation = null;
+		int version = -1;
+		
+		int eventType = xmlPuller.getEventType();//get the next token, should be a start tag
+		while(eventType != XmlPullParser.END_DOCUMENT){
+			switch(eventType){
+			case XmlPullParser.START_TAG:
+				String name = xmlPuller.getName().toLowerCase();
+				if(name.equals("url")){
+					if(xmlPuller.getEventType() == XmlPullParser.TEXT){
+						url = xmlPuller.getText();
+					}
+				}else if(name.equals("version")){
+					if(xmlPuller.getEventType() == XmlPullParser.TEXT){
+						version = Integer.parseInt(xmlPuller.getText());
+					}
+				}else if(name.equals("path")){
+					if(xmlPuller.getEventType() == XmlPullParser.TEXT){
+						path = xmlPuller.getText();
+					}
+				}else if(name.equals("representation")){
+					if(xmlPuller.getEventType() == XmlPullParser.TEXT){
+						representation = xmlPuller.getText();
+					}
+				}
+				
+				xmlPuller.getEventType();//endtag
+				break;
+			case XmlPullParser.END_TAG:
+				if(xmlPuller.getName().toLowerCase().equals("task") && url != null && path != null && version != -1 && representation != null){
+					return new DownloadTask(url, path, version, representation);
+				}else{
+					throw new XmlPullParserException(null);
+				}
+			default:
+				throw new XmlPullParserException(null);
+			}
+			eventType = getEventType(xmlPuller);
+		}
+		
+		throw new XmlPullParserException(null);
+	}
+	
+	/**
+	 * Skips whitespaces..
+	 */
+	private static int getEventType(XmlPullParser xmlPuller)throws XmlPullParserException, IOException{
+		int eventType = xmlPuller.next();
+		while(eventType == XmlPullParser.TEXT && xmlPuller.isWhitespace()){
+			eventType = xmlPuller.next();
+		}
+		return eventType;
+	}
+}
--- a/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/MainActivity.java	Mon Nov 14 17:59:26 2011 +0100
+++ b/project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/MainActivity.java	Mon Nov 14 18:03:31 2011 +0100
@@ -19,6 +19,7 @@
 package org.hedgewars.hedgeroid;
 
 import org.hedgewars.hedgeroid.Downloader.DownloadActivity;
+import org.hedgewars.hedgeroid.Downloader.DownloadListActivity;
 import org.hedgewars.hedgeroid.Downloader.DownloadService;
 
 import android.app.Activity;
@@ -49,7 +50,8 @@
 	
 	private OnClickListener downloadClicker = new OnClickListener(){
 		public void onClick(View v){
-			startActivityForResult(new Intent(getApplicationContext(), DownloadActivity.class), 0);
+			//startActivityForResult(new Intent(getApplicationContext(), DownloadActivity.class), 0);
+			startActivityForResult(new Intent(getApplicationContext(), DownloadListActivity.class), 0);
 		}
 	};