/* SDL - Simple DirectMedia Layer Copyright (C) 1997-2011 Sam Lantinga Java source code (C) 2009-2011 Sergii Pylypenko This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. */ package net.sourceforge.clonekeenplus; import android.app.Activity; import android.content.Context; import android.os.Bundle; import android.view.MotionEvent; import android.view.KeyEvent; import android.view.Window; import android.view.WindowManager; import android.os.Environment; import android.widget.TextView; import org.apache.http.client.methods.*; import org.apache.http.*; import org.apache.http.params.BasicHttpParams; import org.apache.http.conn.*; import org.apache.http.conn.params.*; import org.apache.http.conn.scheme.*; import org.apache.http.conn.ssl.*; import org.apache.http.impl.*; import org.apache.http.impl.client.*; import org.apache.http.impl.conn.SingleClientConnManager; import java.security.cert.*; import java.security.SecureRandom; import javax.net.ssl.HostnameVerifier; import javax.net.ssl.HttpsURLConnection; import java.util.zip.*; import java.io.*; import android.util.Log; import java.io.BufferedInputStream; import java.io.IOException; import java.io.InputStream; import android.content.Context; import android.content.res.Resources; import java.lang.String; import android.text.SpannedString; class CountingInputStream extends BufferedInputStream { private long bytesReadMark = 0; private long bytesRead = 0; public CountingInputStream(InputStream in, int size) { super(in, size); } public CountingInputStream(InputStream in) { super(in); } public long getBytesRead() { return bytesRead; } public synchronized int read() throws IOException { int read = super.read(); if (read >= 0) { bytesRead++; } return read; } public synchronized int read(byte[] b, int off, int len) throws IOException { int read = super.read(b, off, len); if (read >= 0) { bytesRead += read; } return read; } public synchronized long skip(long n) throws IOException { long skipped = super.skip(n); if (skipped >= 0) { bytesRead += skipped; } return skipped; } public synchronized void mark(int readlimit) { super.mark(readlimit); bytesReadMark = bytesRead; } public synchronized void reset() throws IOException { super.reset(); bytesRead = bytesReadMark; } } class DataDownloader extends Thread { class StatusWriter { private TextView Status; private MainActivity Parent; private SpannedString oldText = new SpannedString(""); public StatusWriter( TextView _Status, MainActivity _Parent ) { Status = _Status; Parent = _Parent; } public void setParent( TextView _Status, MainActivity _Parent ) { synchronized(DataDownloader.this) { Status = _Status; Parent = _Parent; setText( oldText.toString() ); } } public void setText(final String str) { class Callback implements Runnable { public TextView Status; public SpannedString text; public void run() { Status.setText(text); } } synchronized(DataDownloader.this) { Callback cb = new Callback(); oldText = new SpannedString(str); cb.text = new SpannedString(str); cb.Status = Status; if( Parent != null && Status != null ) Parent.runOnUiThread(cb); } } } public DataDownloader( MainActivity _Parent, TextView _Status ) { Parent = _Parent; Status = new StatusWriter( _Status, _Parent ); //Status.setText( "Connecting to " + Globals.DataDownloadUrl ); outFilesDir = Globals.DataDir; DownloadComplete = false; this.start(); } public void setStatusField(TextView _Status) { synchronized(this) { Status.setParent( _Status, Parent ); } } @Override public void run() { String [] downloadFiles = Globals.DataDownloadUrl.split("\\^"); for( int i = 0; i < downloadFiles.length; i++ ) { if( downloadFiles[i].length() > 0 && ( Globals.OptionalDataDownload.length > i && Globals.OptionalDataDownload[i] ) || ( Globals.OptionalDataDownload.length <= i && downloadFiles[i].indexOf("!") == 0 ) ) if( ! DownloadDataFile(downloadFiles[i], "libsdl-DownloadFinished-" + String.valueOf(i) + ".flag") ) { DownloadFailed = true; return; } } DownloadComplete = true; initParent(); } public boolean DownloadDataFile(final String DataDownloadUrl, final String DownloadFlagFileName) { String [] downloadUrls = DataDownloadUrl.split("[|]"); if( downloadUrls.length < 2 ) return false; Resources res = Parent.getResources(); String path = getOutFilePath(DownloadFlagFileName); InputStream checkFile = null; try { checkFile = new FileInputStream( path ); } catch( FileNotFoundException e ) { } catch( SecurityException e ) { }; if( checkFile != null ) { try { byte b[] = new byte[ Globals.DataDownloadUrl.getBytes("UTF-8").length + 1 ]; int readed = checkFile.read(b); String compare = new String( b, 0, readed, "UTF-8" ); boolean matched = false; //System.out.println("Read URL: '" + compare + "'"); for( int i = 1; i < downloadUrls.length; i++ ) { //System.out.println("Comparing: '" + downloadUrls[i] + "'"); if( compare.compareTo(downloadUrls[i]) == 0 ) matched = true; } //System.out.println("Matched: " + String.valueOf(matched)); if( ! matched ) throw new IOException(); Status.setText( res.getString(R.string.download_unneeded) ); return true; } catch ( IOException e ) {}; } checkFile = null; // Create output directory (not necessary for phone storage) System.out.println("Downloading data to: '" + outFilesDir + "'"); try { File outDir = new File( outFilesDir ); if( !(outDir.exists() && outDir.isDirectory()) ) outDir.mkdirs(); OutputStream out = new FileOutputStream( getOutFilePath(".nomedia") ); out.flush(); out.close(); } catch( SecurityException e ) {} catch( FileNotFoundException e ) {} catch( IOException e ) {}; HttpResponse response = null; HttpGet request; long totalLen = 0; CountingInputStream stream; byte[] buf = new byte[16384]; boolean DoNotUnzip = false; boolean FileInAssets = false; String url = ""; int downloadUrlIndex = 1; while( downloadUrlIndex < downloadUrls.length ) { System.out.println("Processing download " + downloadUrls[downloadUrlIndex]); url = new String(downloadUrls[downloadUrlIndex]); DoNotUnzip = false; if(url.indexOf(":") == 0) { url = url.substring( url.indexOf(":", 1) + 1 ); DoNotUnzip = true; } Status.setText( res.getString(R.string.connecting_to, url) ); if( url.indexOf("http://") == -1 && url.indexOf("https://") == -1 ) // File inside assets { System.out.println("Fetching file from assets: " + url); FileInAssets = true; break; } else { System.out.println("Connecting to: " + url); request = new HttpGet(url); request.addHeader("Accept", "*/*"); try { DefaultHttpClient client = HttpWithDisabledSslCertCheck(); client.getParams().setBooleanParameter("http.protocol.handle-redirects", true); response = client.execute(request); } catch (IOException e) { System.out.println("Failed to connect to " + url); downloadUrlIndex++; }; if( response != null ) { if( response.getStatusLine().getStatusCode() != 200 ) { response = null; System.out.println("Failed to connect to " + url); downloadUrlIndex++; } else break; } } } if( FileInAssets ) { System.out.println("Unpacking from assets: '" + url + "'"); try { System.out.println("Unpacking from assets: '" + url + "' 1"); stream = new CountingInputStream(Parent.getAssets().open(url), 8192); System.out.println("Unpacking from assets: '" + url + "' 2"); while( stream.skip(65536) > 0 ) { }; System.out.println("Unpacking from assets: '" + url + "' 3"); totalLen = stream.getBytesRead(); System.out.println("Unpacking from assets: '" + url + "' 4 totalLen = " + String.valueOf(totalLen)); stream.close(); System.out.println("Unpacking from assets: '" + url + "' 5"); stream = new CountingInputStream(Parent.getAssets().open(url), 8192); System.out.println("Unpacking from assets: '" + url + "' 6"); } catch( IOException e ) { System.out.println("Unpacking from assets '" + url + "' - error: " + e.toString()); Status.setText( res.getString(R.string.error_dl_from, url) ); return false; } } else { if( response == null ) { System.out.println("Error connecting to " + url); Status.setText( res.getString(R.string.failed_connecting_to, url) ); return false; } Status.setText( res.getString(R.string.dl_from, url) ); totalLen = response.getEntity().getContentLength(); try { stream = new CountingInputStream(response.getEntity().getContent(), 8192); } catch( java.io.IOException e ) { Status.setText( res.getString(R.string.error_dl_from, url) ); return false; } } if(DoNotUnzip) { path = getOutFilePath(downloadUrls[downloadUrlIndex].substring( 1, downloadUrls[downloadUrlIndex].indexOf(":", 1) )); System.out.println("Saving file '" + path + "'"); OutputStream out = null; try { try { File outDir = new File( path.substring(0, path.lastIndexOf("/") )); if( !(outDir.exists() && outDir.isDirectory()) ) outDir.mkdirs(); } catch( SecurityException e ) { }; out = new FileOutputStream( path ); } catch( FileNotFoundException e ) { System.out.println("Saving file '" + path + "' - error creating output file: " + e.toString()); } catch( SecurityException e ) { System.out.println("Saving file '" + path + "' - error creating output file: " + e.toString()); }; if( out == null ) { Status.setText( res.getString(R.string.error_write, path) ); System.out.println("Saving file '" + path + "' - error creating output file"); return false; } try { int len = stream.read(buf); while (len >= 0) { if(len > 0) out.write(buf, 0, len); len = stream.read(buf); float percent = 0.0f; if( totalLen > 0 ) percent = stream.getBytesRead() * 100.0f / totalLen; Status.setText( res.getString(R.string.dl_progress, percent, path) ); } out.flush(); out.close(); out = null; } catch( java.io.IOException e ) { Status.setText( res.getString(R.string.error_write, path) ); System.out.println("Saving file '" + path + "' - error writing: " + e.toString()); return false; } System.out.println("Saving file '" + path + "' done"); } else { System.out.println("Reading from zip file '" + url + "'"); ZipInputStream zip = new ZipInputStream(stream); while(true) { ZipEntry entry = null; try { entry = zip.getNextEntry(); if( entry != null ) System.out.println("Reading from zip file '" + url + "' entry '" + entry.getName() + "'"); } catch( java.io.IOException e ) { Status.setText( res.getString(R.string.error_dl_from, url) ); System.out.println("Error reading from zip file '" + url + "': " + e.toString()); return false; } if( entry == null ) { System.out.println("Reading from zip file '" + url + "' finished"); break; } if( entry.isDirectory() ) { System.out.println("Creating dir '" + getOutFilePath(entry.getName()) + "'"); try { File outDir = new File( getOutFilePath(entry.getName()) ); if( !(outDir.exists() && outDir.isDirectory()) ) outDir.mkdirs(); } catch( SecurityException e ) { }; continue; } OutputStream out = null; path = getOutFilePath(entry.getName()); System.out.println("Saving file '" + path + "'"); try { File outDir = new File( path.substring(0, path.lastIndexOf("/") )); if( !(outDir.exists() && outDir.isDirectory()) ) outDir.mkdirs(); } catch( SecurityException e ) { }; try { CheckedInputStream check = new CheckedInputStream( new FileInputStream(path), new CRC32() ); while( check.read(buf, 0, buf.length) > 0 ) {}; check.close(); if( check.getChecksum().getValue() != entry.getCrc() ) { File ff = new File(path); ff.delete(); throw new Exception(); } System.out.println("File '" + path + "' exists and passed CRC check - not overwriting it"); continue; } catch( Exception e ) { } try { out = new FileOutputStream( path ); } catch( FileNotFoundException e ) { System.out.println("Saving file '" + path + "' - cannot create file: " + e.toString()); } catch( SecurityException e ) { System.out.println("Saving file '" + path + "' - cannot create file: " + e.toString()); }; if( out == null ) { Status.setText( res.getString(R.string.error_write, path) ); System.out.println("Saving file '" + path + "' - cannot create file"); return false; } float percent = 0.0f; if( totalLen > 0 ) percent = stream.getBytesRead() * 100.0f / totalLen; Status.setText( res.getString(R.string.dl_progress, percent, path) ); try { int len = zip.read(buf); while (len >= 0) { if(len > 0) out.write(buf, 0, len); len = zip.read(buf); percent = 0.0f; if( totalLen > 0 ) percent = stream.getBytesRead() * 100.0f / totalLen; Status.setText( res.getString(R.string.dl_progress, percent, path) ); } out.flush(); out.close(); out = null; } catch( java.io.IOException e ) { Status.setText( res.getString(R.string.error_write, path) ); System.out.println("Saving file '" + path + "' - error writing or downloading: " + e.toString()); return false; } try { CheckedInputStream check = new CheckedInputStream( new FileInputStream(path), new CRC32() ); while( check.read(buf, 0, buf.length) > 0 ) {}; check.close(); if( check.getChecksum().getValue() != entry.getCrc() ) { File ff = new File(path); ff.delete(); throw new Exception(); } } catch( Exception e ) { Status.setText( res.getString(R.string.error_write, path) ); System.out.println("Saving file '" + path + "' - CRC check failed"); return false; } System.out.println("Saving file '" + path + "' done"); } }; OutputStream out = null; path = getOutFilePath(DownloadFlagFileName); try { out = new FileOutputStream( path ); out.write(downloadUrls[downloadUrlIndex].getBytes("UTF-8")); out.flush(); out.close(); } catch( FileNotFoundException e ) { } catch( SecurityException e ) { } catch( java.io.IOException e ) { Status.setText( res.getString(R.string.error_write, path) ); return false; }; Status.setText( res.getString(R.string.dl_finished) ); try { stream.close(); } catch( java.io.IOException e ) { }; return true; }; private void initParent() { class Callback implements Runnable { public MainActivity Parent; public void run() { Parent.initSDL(); } } Callback cb = new Callback(); synchronized(this) { cb.Parent = Parent; if(Parent != null) Parent.runOnUiThread(cb); } } private String getOutFilePath(final String filename) { return outFilesDir + "/" + filename; }; private static DefaultHttpClient HttpWithDisabledSslCertCheck() { /* HostnameVerifier hostnameVerifier = org.apache.http.conn.ssl.SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER; DefaultHttpClient client = new DefaultHttpClient(); SchemeRegistry registry = new SchemeRegistry(); SSLSocketFactory socketFactory = SSLSocketFactory.getSocketFactory(); socketFactory.setHostnameVerifier((X509HostnameVerifier) hostnameVerifier); registry.register(new Scheme("https", socketFactory, 443)); SingleClientConnManager mgr = new SingleClientConnManager(client.getParams(), registry); DefaultHttpClient http = new DefaultHttpClient(mgr, client.getParams()); HttpsURLConnection.setDefaultHostnameVerifier(hostnameVerifier); return http; */ return new DefaultHttpClient(); } public StatusWriter Status; public boolean DownloadComplete = false; public boolean DownloadFailed = false; private MainActivity Parent; private String outFilesDir = null; }