# HG changeset patch # User Xeli # Date 1321290211 -3600 # Node ID 9df5a486f41e0bdcab6eb0f0265dc07bb13244b0 # Parent 9dd921c0c7e714698576a09d0cc3b0f9cf0e6f71 first part of the new downloader implementation diff -r 9dd921c0c7e7 -r 9df5a486f41e hedgewars/uAI.pas --- 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; diff -r 9dd921c0c7e7 -r 9df5a486f41e project_files/Android-build/SDL-android-project/AndroidManifest.xml --- 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"> + + + + + - - - diff -r 9dd921c0c7e7 -r 9df5a486f41e project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Downloader/DownloadActivity.java --- 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; diff -r 9dd921c0c7e7 -r 9df5a486f41e project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Downloader/DownloadAsyncTask.java --- 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 { +public class DownloadAsyncTask extends AsyncTask { - 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 diff -r 9dd921c0c7e7 -r 9df5a486f41e project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Downloader/DownloadListActivity.java --- /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 adapter = new ArrayAdapter(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{ + +// } + +} + diff -r 9dd921c0c7e7 -r 9df5a486f41e project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Downloader/DownloadService.java --- 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 downloadTasks = new LinkedList(); private ArrayList clientList = new ArrayList(); 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 { diff -r 9dd921c0c7e7 -r 9df5a486f41e project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/Downloader/DownloadTask.java --- /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 + * + * 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 CREATOR = new Parcelable.Creator() { + 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; + } +} diff -r 9dd921c0c7e7 -r 9df5a486f41e project_files/Android-build/SDL-android-project/src/org/hedgewars/hedgeroid/MainActivity.java --- 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); } };