package org.libsdl.app; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; import java.util.Enumeration; import android.os.StrictMode; import java.io.BufferedInputStream; import java.io.File; import java.io.FileOutputStream; import java.io.FileReader; import java.io.FileWriter; import java.io.InputStream; import java.io.IOException; import java.io.OutputStream; import java.net.URL; import java.net.URLConnection; import java.util.ArrayList; import java.util.Scanner; import javax.microedition.khronos.egl.EGL10; import javax.microedition.khronos.egl.EGLConfig; import javax.microedition.khronos.egl.EGLContext; import javax.microedition.khronos.egl.EGLDisplay; import javax.microedition.khronos.egl.EGLSurface; import net.wagic.app.R; import net.wagic.utils.StorageOptions; import net.wagic.utils.DeckImporter; import net.wagic.utils.ImgDownloader; import android.app.Activity; import android.app.AlertDialog; import android.app.Dialog; import android.app.ProgressDialog; import android.content.Context; import android.content.DialogInterface; import android.content.SharedPreferences; import android.content.pm.PackageManager.NameNotFoundException; import android.content.res.Configuration; import android.graphics.Canvas; import android.graphics.PixelFormat; import android.hardware.Sensor; import android.hardware.SensorEvent; import android.hardware.SensorEventListener; import android.hardware.SensorManager; import android.media.AudioFormat; import android.media.AudioManager; import android.media.AudioTrack; import android.os.AsyncTask; import android.os.Bundle; import android.os.Environment; import android.os.Handler; import android.os.Message; import android.util.Log; import android.view.Gravity; import android.view.KeyEvent; import android.view.Menu; import android.view.MenuItem; import android.view.MotionEvent; import android.view.SubMenu; import android.view.SurfaceHolder; import android.view.SurfaceView; import android.view.VelocityTracker; import android.view.View; import android.view.View.OnKeyListener; import android.widget.FrameLayout; import android.widget.FrameLayout.LayoutParams; /** * SDL Activity */ public class SDLActivity extends Activity implements OnKeyListener { private static final String TAG = SDLActivity.class.getCanonicalName(); //import deck globals public ArrayList myresult = new ArrayList(); public String myclickedItem = ""; // Main components private static SDLActivity mSingleton; private static SDLSurface mSurface; // Audio private static Thread mAudioThread; private static AudioTrack mAudioTrack; // Resource download public static final int DIALOG_DOWNLOAD_PROGRESS = 0; public static final int DIALOG_DOWNLOAD_ERROR = 1; private ProgressDialog mProgressDialog; private AlertDialog mErrorDialog; public String mErrorMessage = ""; public Boolean mErrorHappened = false; public final static String RES_FOLDER = Environment.getExternalStorageDirectory().getPath() + "/Wagic/Res/"; public static String RES_FILENAME = "core_0211.zip"; public static String RES_URL = "https://github.com/Vitty85/wagic/releases/download/wagic-v0.21.1/core_0211.zip"; public String systemFolder = Environment.getExternalStorageDirectory().getPath() + "/Wagic/Res/"; private String userFolder = Environment.getExternalStorageDirectory().getPath() + "/Wagic/User/"; // path to the onboard sd card that is not removable (typically /mnt/sdcard ) private String internalPath = ""; // path to removable sd card (on motorala devices /mnt/sdcard-ext, samsung devices: /mnt/sdcard/external_sd ) private String sdcardPath = ""; // Android only supports internal memory and internal sdcard. removable media is not currently accessible via API // using StorageOptions for now gives us a temporary interface to scan all available mounted drives. private Context mContext; // Preferences public static final String kWagicSharedPreferencesKey = "net.wagic.app.preferences.wagic"; public static final String kStoreDataOnRemovableSdCardPreference = "StoreDataOnRemovableStorage"; public static final String kSaveDataPathPreference = "StorageDataLocation"; public static final String kWagicDataStorageOptionsKey = "dataStorageOptions"; public static final int kStorageDataOptionsMenuId = 2000; public static final int kOtherOptionsMenuId = 3000; // Accessors public String getSystemStorageLocation() { return systemFolder; } public String getUserStorageLocation() { return userFolder; } // setters public void updateStorageLocations() { boolean usesInternalSdCard = (!getSharedPreferences(kWagicSharedPreferencesKey, MODE_PRIVATE).getBoolean(kStoreDataOnRemovableSdCardPreference, false)) && Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()); systemFolder = (usesInternalSdCard ? internalPath : sdcardPath) + "/Res/"; userFolder = (usesInternalSdCard ? internalPath : sdcardPath) + "/User/"; } /** * checks to see if the device has a memory card to write to that is in a valid state. * * @return true if the device can write to the sdcard, false if not. */ public boolean checkStorageState() { SharedPreferences settings = getSharedPreferences(kWagicSharedPreferencesKey, MODE_PRIVATE); boolean mExternalStorageAvailable = false; boolean mExternalStorageWriteable = false; String state = Environment.getExternalStorageState(); boolean useSdCard = (!settings.getBoolean(kStoreDataOnRemovableSdCardPreference, false)) && mExternalStorageWriteable; String systemStoragePath = getSystemStorageLocation(); if (useSdCard && (systemStoragePath.indexOf(sdcardPath) != -1)) { Log.i(TAG, "Data will be written to sdcard."); return true; } if (!useSdCard && (systemStoragePath.indexOf(internalPath) != -1)) { Log.i(TAG, "Data will be written to internal storage."); return true; } if (Environment.MEDIA_MOUNTED.equals(state)) { // We can read and write the media mExternalStorageAvailable = mExternalStorageWriteable = true; } else if (Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) { // We can only read the media mExternalStorageAvailable = true; mExternalStorageWriteable = false; } else { // Something else is wrong. It may be one of many other states, but all we need // to know is we can neither read nor write mExternalStorageAvailable = mExternalStorageWriteable = false; } return (mExternalStorageAvailable && mExternalStorageWriteable); } private boolean getRemovableMediaStorageState() { for (String extMediaPath : StorageOptions.paths) { File mediaPath = new File(extMediaPath); if (mediaPath.canWrite()) return true; } return false; } private void displayStorageOptions() { AlertDialog.Builder setStorage = new AlertDialog.Builder(this); setStorage.setTitle("Where would you like to store your data? On your removable SD Card or the built-in memory?"); StorageOptions.determineStorageOptions(); setStorage.setSingleChoiceItems(StorageOptions.labels, -1, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int item) { savePathPreference(item); } }); setStorage.setPositiveButton("OK", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { initStorage(); if (mSurface == null) mSingleton.initializeGame(); } }); setStorage.create().show(); } private void importDeckOptions() { AlertDialog.Builder importDeck = new AlertDialog.Builder(this); importDeck.setTitle("Choose Deck to Import:"); File root = new File(System.getenv("EXTERNAL_STORAGE")+"/Download"); File[] files = root.listFiles(); for( File f : files) { if( !myresult.contains(f.toString()) && (f.toString().contains(".txt")||f.toString().contains(".dck")||f.toString().contains(".dec"))) myresult.add(f.toString()); } //get first item? if(!myresult.isEmpty()) myclickedItem = myresult.get(0).toString(); importDeck.setSingleChoiceItems(myresult.toArray(new String[myresult.size()]), 0, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int item) { myclickedItem = myresult.get(item).toString(); } }); importDeck.setPositiveButton("Import Deck", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { processSelectedDeck( myclickedItem ); if (mSurface == null) mSingleton.initializeGame(); } }); importDeck.create().show(); } private void processSelectedDeck(String mypath) { AlertDialog.Builder infoDialog = new AlertDialog.Builder(this); infoDialog.setTitle("Imported Deck:"); String activePath = sdcardPath; if(activePath == ""){ activePath = internalPath; } File f = new File(mypath); //Call the deck importer.... String state = DeckImporter.importDeck(f, mypath, activePath); infoDialog.setMessage(state); infoDialog.show(); } private void checkStorageLocationPreference() { SharedPreferences settings = getSharedPreferences(kWagicSharedPreferencesKey, MODE_PRIVATE); final SharedPreferences.Editor prefsEditor = settings.edit(); boolean hasRemovableMediaMounted = getRemovableMediaStorageState(); if (!settings.contains(kStoreDataOnRemovableSdCardPreference)) { if (hasRemovableMediaMounted) { displayStorageOptions(); } else { prefsEditor.putBoolean(kStoreDataOnRemovableSdCardPreference, false); prefsEditor.commit(); initStorage(); mSingleton.initializeGame(); } } else { boolean storeOnRemovableMedia = settings.getBoolean(kStoreDataOnRemovableSdCardPreference, false); if (storeOnRemovableMedia && !hasRemovableMediaMounted) { AlertDialog setStorage = new AlertDialog.Builder(this).create(); setStorage.setTitle("Storage Preference"); setStorage.setMessage("Removable Sd Card not detected. Saving data to internal memory."); prefsEditor.putBoolean(kStoreDataOnRemovableSdCardPreference, false); prefsEditor.commit(); initStorage(); mSingleton.initializeGame(); setStorage.show(); } else { initStorage(); mSingleton.initializeGame(); } } } private void initStorage() { // check the state of the external storage to ensure we can even write to it. // we are going to assume that if an external location exists, and can be written to, use it. // Otherwise use internal storage try { // // initialize where all the files are going to be stored. // File wagicMediaPath = null; // String packageName = mContext.getPackageName(); // possibly use this to differentiate between different mods of Wagic. File externalFilesDir = Environment.getExternalStorageDirectory(); if (externalFilesDir != null) { internalPath = externalFilesDir.getAbsolutePath() + "/Wagic"; } String state = Environment.getExternalStorageState(); if (Environment.MEDIA_MOUNTED.equals(state)) { wagicMediaPath = new File(internalPath); if (wagicMediaPath.canWrite()) wagicMediaPath.mkdirs(); } // initialize the external mount SharedPreferences settings = getSharedPreferences(kWagicSharedPreferencesKey, MODE_PRIVATE); String selectedRemovableCardPath = settings.getString(kSaveDataPathPreference, internalPath); if (selectedRemovableCardPath != null && !internalPath.equalsIgnoreCase(selectedRemovableCardPath)) { wagicMediaPath = new File(selectedRemovableCardPath); if (!wagicMediaPath.exists() || !wagicMediaPath.canWrite()) { Log.e(TAG, "Error in initializing system folder: " + selectedRemovableCardPath); } else { // found a removable media location sdcardPath = selectedRemovableCardPath + "/Wagic"; } } updateStorageLocations(); } catch (Exception ioex) { Log.e(TAG, "An error occurred in setting up the storage locations."); } } private void savePathPreference(int selectedOption) { SharedPreferences settings = getSharedPreferences(kWagicSharedPreferencesKey, MODE_PRIVATE); String selectedMediaPath = StorageOptions.paths[selectedOption]; final SharedPreferences.Editor prefsEditor = settings.edit(); boolean saveToRemovableMedia = !"/mnt/sdcard".equalsIgnoreCase(selectedMediaPath); prefsEditor.putBoolean(kStoreDataOnRemovableSdCardPreference, saveToRemovableMedia); prefsEditor.putString(kSaveDataPathPreference, selectedMediaPath); prefsEditor.commit(); } private void startDownload() { //String url = getResourceUrl(); String url = RES_URL; if (!checkStorageState()) { Log.e(TAG, "Error in initializing storage space."); mSingleton.downloadError("Failed to initialize storage space for game. Please verify that your sdcard or internal memory is mounted properly."); } new DownloadFileAsync().execute(url); } public void downloadError(String errorMessage) { mErrorHappened = true; mErrorMessage = errorMessage; } private void buildStorageOptionsMenu(Menu menu) { StorageOptions.determineStorageOptions(); for (int idx = 0; idx < StorageOptions.count; idx++) { menu.add(kStorageDataOptionsMenuId, kStorageDataOptionsMenuId + idx, idx, StorageOptions.labels[idx]); } } String set = ""; String [] availableSets; Integer totalset = 0; private void downloadCardImages() { AlertDialog.Builder cardDownloader = new AlertDialog.Builder(this); cardDownloader.setTitle("Which Set would you like to download?"); File baseFolder = new File(getSystemStorageLocation()); File[] listOfFiles = baseFolder.listFiles(); ArrayList sets = new ArrayList(); ZipFile zipFile = null; try { zipFile = new ZipFile(baseFolder + "/" + listOfFiles[0].getName()); Enumeration e = zipFile.entries(); while (e.hasMoreElements()) { ZipEntry entry = e.nextElement(); String entryName = entry.getName(); if(entryName.contains("sets/")){ if(!entryName.equalsIgnoreCase("sets/") && !entryName.contains("primitives") && !entryName.contains(".")){ String[] names = entryName.split("/"); sets.add(names[1]); } } } } catch (IOException ioe) { System.out.println("Error opening zip file" + ioe); } finally { try { if (zipFile!=null) { zipFile.close(); } } catch (IOException ioe) { System.out.println("Error while closing zip file" + ioe); } } availableSets = new String[sets.size() + 1]; availableSets[0] = "*.* - All Wagic sets (thousands of cards)"; for (int i = 1; i < availableSets.length; i++){ availableSets[i] = sets.get(i-1) + " - " + ImgDownloader.getSetInfo(sets.get(i-1), true, getSystemStorageLocation()); } cardDownloader.setSingleChoiceItems(availableSets, -1, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int item) { set = availableSets[item].split(" - ")[0]; downloadCardStarting(set); } }); cardDownloader.setPositiveButton("OK", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { boolean error = false; String res = ""; try{ res = ImgDownloader.DownloadCardImages(set, availableSets, "HI", getSystemStorageLocation(), getUserStorageLocation() + "sets/"); } catch(Exception e) { res = e.getMessage(); error = true; } downloadCardCompleted(error, res, set); } }); cardDownloader.create().show(); } private void downloadCardStarting(String set){ AlertDialog.Builder infoDialog = new AlertDialog.Builder(this); infoDialog.setTitle("You choose to Download: " + set); infoDialog.setMessage("After you press OK don't turn off phone or wi-fi/data connection and don't close Wagic.\nThe download process can take several minutes according to the number of images contained in the selected set (NOTE: if you choose *.* you will have to wait several hours!!!)"); infoDialog.create().show(); } private void downloadCardCompleted(boolean error, String res, String set){ AlertDialog.Builder infoDialog = new AlertDialog.Builder(this); if(!error){ infoDialog.setTitle("Download Completed: " + set); if(!res.isEmpty()) infoDialog.setMessage("Following cards could not be downloaded:\n" + res); else infoDialog.setMessage("All the cards have been successfully downloaded"); } else { infoDialog.setTitle("Error downloading: " + set); infoDialog.setMessage(res); } infoDialog.create().show(); } @Override public boolean onCreateOptionsMenu(Menu menu) { SubMenu settingsMenu = menu.addSubMenu(Menu.NONE, 1, 1, "Settings"); menu.add(Menu.NONE, 2, 2, "Import Decks"); menu.add(Menu.NONE, 3, 3, "Download Cards"); menu.add(Menu.NONE, 4, 4, "About"); settingsMenu.add(kStorageDataOptionsMenuId, kStorageDataOptionsMenuId, Menu.NONE, "Storage Data Options"); // buildStorageOptionsMenu(settingsMenu); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { // Handle item selection int itemId = item.getItemId(); if (itemId == kStorageDataOptionsMenuId) { displayStorageOptions(); } else if (itemId == 2) { importDeckOptions(); } else if (itemId == 3) { downloadCardImages(); } else if (itemId == 4) { // display some info about the app AlertDialog.Builder infoDialog = new AlertDialog.Builder(this); infoDialog.setTitle("Wagic Info"); infoDialog.setMessage("Version " + getResources().getString(R.string.app_version)); infoDialog.show(); } else return super.onOptionsItemSelected(item); return true; } @Override protected Dialog onCreateDialog(int id) { switch (id) { case DIALOG_DOWNLOAD_PROGRESS: mProgressDialog = new ProgressDialog(this); mProgressDialog.setMessage("Downloading resource files (" + RES_FILENAME + ")"); mProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL); mProgressDialog.setCancelable(false); mProgressDialog.show(); return mProgressDialog; case DIALOG_DOWNLOAD_ERROR: // prepare alertDialog AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setMessage(mErrorMessage).setCancelable(false).setPositiveButton("Exit", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { System.exit(0); } }); mErrorDialog = builder.create(); mErrorDialog.show(); return mErrorDialog; default: return null; } } // Load the .so static { System.loadLibrary("SDL"); // System.loadLibrary("SDL_image"); // System.loadLibrary("SDL_mixer"); // System.loadLibrary("SDL_ttf"); System.loadLibrary("main"); } // create main application public void mainDisplay() { FrameLayout _videoLayout = new FrameLayout(this); // mGLView = new DemoGLSurfaceView(this); // Set up the surface mSurface = new SDLSurface(getApplication()); // setContentView(mSurface); SurfaceHolder holder = mSurface.getHolder(); holder.setType(SurfaceHolder.SURFACE_TYPE_GPU); _videoLayout.addView(mSurface, new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT)); // mGLView.setFocusableInTouchMode(true); // mGLView.setFocusable(true); // adView.requestFreshAd(); setContentView(_videoLayout, new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT)); mSurface.requestFocus(); } // Setup @Override protected void onCreate(Bundle savedInstanceState) { //Log.d(TAG, "onCreate()"); super.onCreate(savedInstanceState); StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build(); StrictMode.setThreadPolicy(policy); // So we can call stuff from static callbacks mSingleton = this; mContext = this.getApplicationContext(); //RES_FILENAME = getResourceName(); StorageOptions.determineStorageOptions(); checkStorageLocationPreference(); } public void initializeGame() { String coreFileLocation = getSystemStorageLocation() + RES_FILENAME; File file = new File(coreFileLocation); if (file.exists()) { mainDisplay(); } else { FrameLayout _videoLayout = new FrameLayout(this); setContentView(_videoLayout, new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT)); startDownload(); } } // Events @Override protected void onPause() { // Log.d(TAG, "onPause()"); super.onPause(); SDLActivity.nativePause(); } @Override protected void onResume() { // Log.d(TAG, "onResume()"); super.onResume(); SDLActivity.nativeResume(); } @Override public void onDestroy() { // Log.d(TAG, "onDestroy()"); super.onDestroy(); mSurface.onDestroy(); } // Handler for Messages coming from JGE // Suggested syntax for JGE messages is a string separated by the ":" symbol protected void processJGEMsg(String command) { if (null == command) { return; } } // Messages from the SDLMain thread static int COMMAND_CHANGE_TITLE = 1; static int COMMAND_JGE_MSG = 2; // Handler for the messages Handler commandHandler = new Handler() { public void handleMessage(Message msg) { if (msg.arg1 == COMMAND_CHANGE_TITLE) { setTitle((String) msg.obj); } else if (msg.arg1 == COMMAND_JGE_MSG) { processJGEMsg((String) msg.obj); } } }; // Send a message from the SDLMain thread void sendCommand(int command, Object data) { Message msg = commandHandler.obtainMessage(); msg.arg1 = command; msg.obj = data; commandHandler.sendMessage(msg); } // C functions we call public static native String getResourceUrl(); public static native String getResourceName(); public static native void nativeInit(); public static native void nativeQuit(); public static native void nativePause(); public static native void nativeResume(); public static native void onNativeResize(int x, int y, int format); public static native void onNativeKeyDown(int keycode); public static native void onNativeKeyUp(int keycode); public static native void onNativeTouch(int index, int action, float x, float y, float p); public static native void onNativeFlickGesture(float xVelocity, float yVelocity); public static native void onNativeAccel(float x, float y, float z); public static native void nativeRunAudioThread(); // Java functions called from C // Receive a message from the SDLMain thread public static String getSystemFolderPath() { return mSingleton.getSystemStorageLocation(); } public static String getUserFolderPath() { return mSingleton.getUserStorageLocation(); } public static void jgeSendCommand(String command) { mSingleton.sendCommand(COMMAND_JGE_MSG, command); } public static boolean createGLContext(int majorVersion, int minorVersion) { return mSurface.initEGL(majorVersion, minorVersion); } public static void flipBuffers() { mSurface.flipEGL(); } public static void setActivityTitle(String title) { // Called from SDLMain() thread and can't directly affect the view mSingleton.sendCommand(COMMAND_CHANGE_TITLE, title); } // Audio private static Object buf; public static Object audioInit(int sampleRate, boolean is16Bit, boolean isStereo, int desiredFrames) { int channelConfig = isStereo ? AudioFormat.CHANNEL_CONFIGURATION_STEREO : AudioFormat.CHANNEL_CONFIGURATION_MONO; int audioFormat = is16Bit ? AudioFormat.ENCODING_PCM_16BIT : AudioFormat.ENCODING_PCM_8BIT; int frameSize = (isStereo ? 2 : 1) * (is16Bit ? 2 : 1); // Log.d(TAG, "SDL audio: wanted " + (isStereo ? "stereo" : "mono") + " " + (is16Bit ? "16-bit" : "8-bit") + " " + ((float)sampleRate / 1000f) + "kHz, " + desiredFrames + " frames buffer"); // Let the user pick a larger buffer if they really want -- but ye // gods they probably shouldn't, the minimums are horrifyingly high // latency already desiredFrames = Math.max(desiredFrames, (AudioTrack.getMinBufferSize(sampleRate, channelConfig, audioFormat) + frameSize - 1) / frameSize); mAudioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, sampleRate, channelConfig, audioFormat, desiredFrames * frameSize, AudioTrack.MODE_STREAM); audioStartThread(); // Log.d(TAG, "SDL audio: got " + ((mAudioTrack.getChannelCount() >= 2) ? "stereo" : "mono") + " " + ((mAudioTrack.getAudioFormat() == AudioFormat.ENCODING_PCM_16BIT) ? "16-bit" : "8-bit") + " " + ((float)mAudioTrack.getSampleRate() / 1000f) + // "kHz, " + desiredFrames + " frames buffer"); if (is16Bit) { buf = new short[desiredFrames * (isStereo ? 2 : 1)]; } else { buf = new byte[desiredFrames * (isStereo ? 2 : 1)]; } return buf; } public static void audioStartThread() { mAudioThread = new Thread(new Runnable() { public void run() { mAudioTrack.play(); nativeRunAudioThread(); } }); // I'd take REALTIME if I could get it! mAudioThread.setPriority(Thread.MAX_PRIORITY); mAudioThread.start(); } public static void audioWriteShortBuffer(short[] buffer) { for (int i = 0; i < buffer.length;) { int result = mAudioTrack.write(buffer, i, buffer.length - i); if (result > 0) { i += result; } else if (result == 0) { try { Thread.sleep(1); } catch (InterruptedException e) { // Nom nom } } else { Log.w(TAG, "SDL audio: error return from write(short)"); return; } } } public static void audioWriteByteBuffer(byte[] buffer) { for (int i = 0; i < buffer.length;) { int result = mAudioTrack.write(buffer, i, buffer.length - i); if (result > 0) { i += result; } else if (result == 0) { try { Thread.sleep(1); } catch (InterruptedException e) { // Nom nom } } else { Log.w(TAG, "SDL audio: error return from write(short)"); return; } } } public static void audioQuit() { if (mAudioThread != null) { try { mAudioThread.join(); } catch (Exception e) { Log.e(TAG, "Problem stopping audio thread: " + e); } mAudioThread = null; // Log.d(TAG, "Finished waiting for audio thread"); } if (mAudioTrack != null) { mAudioTrack.stop(); mAudioTrack = null; } } class DownloadFileAsync extends AsyncTask { private final String TAG = DownloadFileAsync.class.getCanonicalName(); @Override protected void onPreExecute() { super.onPreExecute(); showDialog(DIALOG_DOWNLOAD_PROGRESS); } @Override protected Long doInBackground(String... aurl) { int count; long totalBytes = 0; OutputStream output = null; InputStream input = null; try { // // Prepare the sdcard folders in order to download the resource file // String storageLocation = mSingleton.getSystemStorageLocation(); File resDirectory = new File(storageLocation); File userDirectory = new File(mSingleton.getUserStorageLocation()); if (!resDirectory.exists() && !resDirectory.mkdirs() || (!userDirectory.exists() && !userDirectory.mkdirs())) { throw new Exception("Failed to initialize system and user directories."); } URL url = new URL(aurl[0]); String filename = url.getPath().substring(url.getPath().lastIndexOf('/') + 1); URLConnection conexion = url.openConnection(); conexion.connect(); int lengthOfFile = conexion.getContentLength(); // Log.d(TAG, " Length of file: " + lengthOfFile); input = new BufferedInputStream(url.openStream()); // create a File object for the output file File outputFile = new File(resDirectory, filename); output = new FileOutputStream(outputFile); byte data[] = new byte[1024]; while ((count = input.read(data)) != -1) { totalBytes += count; publishProgress((int) ((totalBytes * 100) / lengthOfFile)); output.write(data, 0, count); } output.flush(); output.close(); input.close(); } catch (Exception e) { String errorMessage = "An error happened while downloading the resources. It could be that our server is temporarily down, that your device is not connected to a network, or that we cannot write to " + mSingleton.getSystemStorageLocation() + ". Please check your phone settings and try again. For more help please go to http://wagic.net"; //mSingleton.downloadError(errorMessage); //Log.e(TAG, errorMessage); //Log.e(TAG, e.getMessage()); } return Long.valueOf(totalBytes); } protected void onProgressUpdate(Integer... progress) { if (progress[0] != mProgressDialog.getProgress()) { // Log.d(TAG, "current progress : " + progress[0]); mProgressDialog.setProgress(progress[0]); } } @Override protected void onPostExecute(Long unused) { if (mErrorHappened) { dismissDialog(DIALOG_DOWNLOAD_PROGRESS); showDialog(DIALOG_DOWNLOAD_ERROR); return; } // rename the temporary file into the final filename String storageLocation = getSystemStorageLocation(); File preFile = new File(storageLocation + RES_FILENAME + ".tmp"); File postFile = new File(storageLocation + RES_FILENAME); if (preFile.exists()) preFile.renameTo(postFile); dismissDialog(DIALOG_DOWNLOAD_PROGRESS); // Start game; mSingleton.mainDisplay(); } } public boolean onKey(View v, int keyCode, KeyEvent event) { if ((keyCode == KeyEvent.KEYCODE_MENU) && (KeyEvent.ACTION_DOWN == event.getAction())) { super.onKeyDown(keyCode, event); return true; } else if ((keyCode == KeyEvent.KEYCODE_MENU) && (KeyEvent.ACTION_UP == event.getAction())) { super.onKeyUp(keyCode, event); return true; } return false; } private String getApplicationCode() { int v = 0; try { v = getPackageManager().getPackageInfo(getPackageName(), 0).versionCode; } catch (NameNotFoundException e) { // Huh? Really? v = 184; // shouldn't really happen but we need to default to something } return "0" + v; } // Empty onConfigurationChanged to stop the Activity from destroying/recreating on screen off @Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); } } /** * SDLSurface. This is what we draw on, so we need to know when it's created in order to do anything useful. * * Because of this, that's where we set up the SDL thread */ class SDLSurface extends SurfaceView implements SurfaceHolder.Callback, View.OnKeyListener, View.OnTouchListener, SensorEventListener { private static final String TAG = SDLSurface.class.getCanonicalName(); // This is what SDL runs in. It invokes SDL_main(), eventually private Thread mSDLThread; // EGL private objects private EGLContext mEGLContext; private EGLSurface mEGLSurface; private EGLDisplay mEGLDisplay; private EGLConfig mEGLConfig; // Sensors private static SensorManager mSensorManager; private static VelocityTracker mVelocityTracker; final private Object mSemSurface; private Boolean mSurfaceValid; void startSDLThread() { if (mSDLThread == null) { mSDLThread = new Thread(new SDLMain(), "SDLThread"); mSDLThread.start(); } } /** * Simple nativeInit() runnable */ class SDLMain implements Runnable { public void run() { // Runs SDL_main() SDLActivity.nativeInit(); SDLActivity.nativeQuit(); Log.d(TAG, "SDL thread terminated"); // On exit, tear everything down for a fresh restart next time. System.exit(0); } } // Startup public SDLSurface(Context context) { super(context); mSemSurface = new Object(); mSurfaceValid = false; getHolder().addCallback(this); setFocusable(true); setFocusableInTouchMode(true); requestFocus(); setOnKeyListener(this); setOnTouchListener(this); mSensorManager = (SensorManager) context.getSystemService("sensor"); } // Called when we have a valid drawing surface public void surfaceCreated(SurfaceHolder holder) { //Log.d(TAG, "surfaceCreated()"); enableSensor(Sensor.TYPE_ACCELEROMETER, true); } public void onDestroy() { // Send a quit message to the application // should that be in SDLActivity "onDestroy" instead ? SDLActivity.nativeQuit(); // Now wait for the SDL thread to quit if (mSDLThread != null) { try { mSDLThread.join(); } catch (Exception e) { Log.e(TAG, "Problem stopping thread: " + e); } mSDLThread = null; // Log.d(TAG, "Finished waiting for SDL thread"); } } // Called when we lose the surface public void surfaceDestroyed(SurfaceHolder holder) { Log.d(TAG, "surfaceDestroyed()"); synchronized (mSemSurface) { mSurfaceValid = false; mSemSurface.notifyAll(); } enableSensor(Sensor.TYPE_ACCELEROMETER, false); } // Called when the surface is resized public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { Log.d(TAG, "surfaceChanged()"); int sdlFormat = 0x85151002; // SDL_PIXELFORMAT_RGB565 by default switch (format) { case PixelFormat.A_8: Log.d("TAG", "pixel format A_8"); break; case PixelFormat.LA_88: Log.d("TAG", "pixel format LA_88"); break; case PixelFormat.L_8: Log.d("TAG", "pixel format L_8"); break; case PixelFormat.RGBA_4444: Log.d("TAG", "pixel format RGBA_4444"); sdlFormat = 0x85421002; // SDL_PIXELFORMAT_RGBA4444 break; case PixelFormat.RGBA_5551: Log.d(TAG, "pixel format RGBA_5551"); sdlFormat = 0x85441002; // SDL_PIXELFORMAT_RGBA5551 break; case PixelFormat.RGBA_8888: Log.d(TAG, "pixel format RGBA_8888"); sdlFormat = 0x86462004; // SDL_PIXELFORMAT_RGBA8888 break; case PixelFormat.RGBX_8888: Log.d(TAG, "pixel format RGBX_8888"); sdlFormat = 0x86262004; // SDL_PIXELFORMAT_RGBX8888 break; case PixelFormat.RGB_332: Log.d(TAG, "pixel format RGB_332"); sdlFormat = 0x84110801; // SDL_PIXELFORMAT_RGB332 break; case PixelFormat.RGB_565: Log.d(TAG, "pixel format RGB_565"); sdlFormat = 0x85151002; // SDL_PIXELFORMAT_RGB565 break; case PixelFormat.RGB_888: Log.d(TAG, "pixel format RGB_888"); // Not sure this is right, maybe SDL_PIXELFORMAT_RGB24 instead? sdlFormat = 0x86161804; // SDL_PIXELFORMAT_RGB888 break; default: Log.d(TAG, "pixel format unknown " + format); break; } SDLActivity.onNativeResize(width, height, sdlFormat); // Now start up the C app thread startSDLThread(); } // unused public void onDraw(Canvas canvas) { } // EGL functions public boolean initEGL(int majorVersion, int minorVersion) { Log.d(TAG, "Starting up OpenGL ES " + majorVersion + "." + minorVersion); try { EGL10 egl = (EGL10) EGLContext.getEGL(); EGLDisplay dpy = egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY); int[] version = new int[2]; egl.eglInitialize(dpy, version); int EGL_OPENGL_ES_BIT = 1; int EGL_OPENGL_ES2_BIT = 4; int renderableType = 0; if (majorVersion == 2) { renderableType = EGL_OPENGL_ES2_BIT; } else if (majorVersion == 1) { renderableType = EGL_OPENGL_ES_BIT; } int[] configSpec = { // EGL10.EGL_DEPTH_SIZE, 16, EGL10.EGL_RENDERABLE_TYPE, renderableType, EGL10.EGL_NONE }; EGLConfig[] configs = new EGLConfig[1]; int[] num_config = new int[1]; if (!egl.eglChooseConfig(dpy, configSpec, configs, 1, num_config) || num_config[0] == 0) { Log.e(TAG, "No EGL config available"); return false; } mEGLConfig = configs[0]; EGLContext ctx = egl.eglCreateContext(dpy, mEGLConfig, EGL10.EGL_NO_CONTEXT, null); if (ctx == EGL10.EGL_NO_CONTEXT) { Log.e(TAG, "Couldn't create context"); return false; } mEGLContext = ctx; mEGLDisplay = dpy; if (!createSurface(this.getHolder())) { return false; } } catch (Exception e) { Log.e(TAG, e + ""); for (StackTraceElement s : e.getStackTrace()) { Log.e(TAG, s.toString()); } } return true; } public Boolean createSurface(SurfaceHolder holder) { /* * The window size has changed, so we need to create a new surface. */ EGL10 egl = (EGL10) EGLContext.getEGL(); if (mEGLSurface != null) { /* * Unbind and destroy the old EGL surface, if there is one. */ egl.eglMakeCurrent(mEGLDisplay, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT); egl.eglDestroySurface(mEGLDisplay, mEGLSurface); } /* * Create an EGL surface we can render into. */ mEGLSurface = egl.eglCreateWindowSurface(mEGLDisplay, mEGLConfig, holder, null); if (mEGLSurface == EGL10.EGL_NO_SURFACE) { Log.e(TAG, "Couldn't create surface"); return false; } /* * Before we can issue GL commands, we need to make sure the context is current and bound to a surface. */ if (!egl.eglMakeCurrent(mEGLDisplay, mEGLSurface, mEGLSurface, mEGLContext)) { Log.e(TAG, "Couldn't make context current"); return false; } mSurfaceValid = true; return true; } // EGL buffer flip public void flipEGL() { if (!mSurfaceValid) { createSurface(this.getHolder()); } try { EGL10 egl = (EGL10) EGLContext.getEGL(); egl.eglWaitNative(EGL10.EGL_CORE_NATIVE_ENGINE, null); // drawing here egl.eglWaitGL(); egl.eglSwapBuffers(mEGLDisplay, mEGLSurface); } catch (Exception e) { Log.e(TAG, "flipEGL(): " + e); for (StackTraceElement s : e.getStackTrace()) { Log.e(TAG, s.toString()); } } } // Key events public boolean onKey(View v, int keyCode, KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_MENU) return false; if (keyCode == KeyEvent.KEYCODE_VOLUME_UP || keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) return false; if (event.getAction() == KeyEvent.ACTION_DOWN) { // Log.d(TAG, "key down: " + keyCode); SDLActivity.onNativeKeyDown(keyCode); return true; } else if (event.getAction() == KeyEvent.ACTION_UP) { // Log.d(TAG, "key up: " + keyCode); SDLActivity.onNativeKeyUp(keyCode); return true; } return false; } // Touch events public boolean onTouch(View v, MotionEvent event) { for (int index = 0; index < event.getPointerCount(); ++index) { int action = event.getActionMasked(); float x = event.getX(index); float y = event.getY(index); float p = event.getPressure(index); // TODO: Anything else we need to pass? SDLActivity.onNativeTouch(index, action, x, y, p); } // account for 'flick' type gestures by monitoring velocity if (event.getActionIndex() == 0) { if (event.getAction() == MotionEvent.ACTION_DOWN) { mVelocityTracker = VelocityTracker.obtain(); mVelocityTracker.clear(); mVelocityTracker.addMovement(event); } else if (event.getAction() == MotionEvent.ACTION_MOVE) { mVelocityTracker.addMovement(event); } else if (event.getAction() == MotionEvent.ACTION_UP) { mVelocityTracker.addMovement(event); // calc velocity mVelocityTracker.computeCurrentVelocity(1000); float xVelocity = mVelocityTracker.getXVelocity(0); float yVelocity = mVelocityTracker.getYVelocity(0); if (Math.abs(xVelocity) > 300 || Math.abs(yVelocity) > 300) SDLActivity.onNativeFlickGesture(xVelocity, yVelocity); mVelocityTracker.recycle(); } } return true; } // Sensor events public void enableSensor(int sensortype, boolean enabled) { // TODO: This uses getDefaultSensor - what if we have >1 accels? if (enabled) { mSensorManager.registerListener(this, mSensorManager.getDefaultSensor(sensortype), SensorManager.SENSOR_DELAY_GAME, null); } else { mSensorManager.unregisterListener(this, mSensorManager.getDefaultSensor(sensortype)); } } public void onAccuracyChanged(Sensor sensor, int accuracy) { // TODO } public void onSensorChanged(SensorEvent event) { if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) { SDLActivity.onNativeAccel(event.values[0], event.values[1], event.values[2]); } } }