From 6124280f24bd7ab807e384fcf4e2f793eaa5381b Mon Sep 17 00:00:00 2001 From: "techdragon.nguyen@gmail.com" Date: Thu, 1 Mar 2012 03:24:34 +0000 Subject: [PATCH] added a prefernces screen to handle media card selection on Android devices. Contains actual fix for 4297. Added a place marker to allow volume change during app. Right now it's either loud or off. There isn't a way to allow the volume to gradually go up and odwn based on the values set in settings. --- .../SDL/src/main/android/SDL_android_main.cpp | 3 + JGE/include/JGE.h | 6 + JGE/include/JSoundSystem.h | 5 +- JGE/src/JFileSystem.cpp | 10 +- JGE/src/JGE.cpp | 61 ++- JGE/src/android/JSfx.cpp | 21 +- projects/mtg/Android/AndroidManifest.xml | 5 +- projects/mtg/Android/default.properties | 2 +- projects/mtg/Android/res/values/strings.xml | 1 + .../src/net/wagic/utils/StorageOptions.java | 137 ++++++ .../src/org/libsdl/app/SDLActivity.java | 424 +++++++++++++++--- projects/mtg/include/GameStateDeckViewer.h | 2 +- projects/mtg/src/GameStateShop.cpp | 24 +- 13 files changed, 604 insertions(+), 97 deletions(-) create mode 100644 projects/mtg/Android/src/net/wagic/utils/StorageOptions.java diff --git a/JGE/Dependencies/SDL/src/main/android/SDL_android_main.cpp b/JGE/Dependencies/SDL/src/main/android/SDL_android_main.cpp index 2b9749bff..442363e1b 100644 --- a/JGE/Dependencies/SDL/src/main/android/SDL_android_main.cpp +++ b/JGE/Dependencies/SDL/src/main/android/SDL_android_main.cpp @@ -1,6 +1,7 @@ /* Include the SDL main definition header */ #include "SDL_main.h" +#include "JGE.h" /******************************************************************************* Functions called by JNI @@ -13,6 +14,8 @@ extern "C" void SDL_Android_Init(JNIEnv* env, jclass cls); // Library init extern "C" jint JNI_OnLoad(JavaVM* vm, void* reserved) { + JGE *mEngine = JGE::GetInstance(); + mEngine->setJVM(vm); return JNI_VERSION_1_4; } diff --git a/JGE/include/JGE.h b/JGE/include/JGE.h index d723d350e..8168821f3 100644 --- a/JGE/include/JGE.h +++ b/JGE/include/JGE.h @@ -126,6 +126,7 @@ class JGE #endif #if defined (ANDROID) + JavaVM * mJavaVM; JNIEnv * mJNIEnv; jclass mJNIClass; jmethodID midSendCommand; @@ -164,6 +165,10 @@ class JGE static JGE* GetInstance(); static void Destroy(); +#ifdef ANDROID + JNIEnv * getJNIEnv(); + void setJVM (JavaVM * vm); +#endif void Init(); void End(); @@ -377,6 +382,7 @@ class JGE /// Access to JNI Environment void SetJNIEnv(JNIEnv * env, jclass cls); void sendJNICommand(std::string command); + std::string getFileSystemLocation(); #endif protected: diff --git a/JGE/include/JSoundSystem.h b/JGE/include/JSoundSystem.h index 56bb335c2..a1a88e271 100644 --- a/JGE/include/JSoundSystem.h +++ b/JGE/include/JSoundSystem.h @@ -82,6 +82,7 @@ public: SLObjectItf playerObject; SLPlayItf playInterface; SLSeekItf seekInterface; + SLVolumeItf musicVolumeInterface; #else void* mTrack; #endif //WITH_FMOD @@ -115,6 +116,7 @@ class JSample #elif defined ANDROID SLObjectItf playerObject; SLPlayItf playInterface; + SLVolumeItf sampleVolumeInterface; void* mSample; #else void* mSample; @@ -242,9 +244,8 @@ protected: private: -#ifdef WIN32 JMusic *mCurrentMusic; -#endif + JSample *mCurrentSample; int mVolume; int mMusicVolume; diff --git a/JGE/src/JFileSystem.cpp b/JGE/src/JFileSystem.cpp index a42336a33..7f4699582 100644 --- a/JGE/src/JFileSystem.cpp +++ b/JGE/src/JFileSystem.cpp @@ -16,6 +16,9 @@ User folder is the only one that is really needed to guarantee both read and wri The content that users should not be touching. */ +#if defined(ANDROID) +#include "../../include/PrecompiledHeader.h" +#endif #ifdef WIN32 #pragma warning(disable : 4786) @@ -113,8 +116,10 @@ JFileSystem::JFileSystem(const string & _userPath, const string & _systemPath) systemPath = [[documentsDirectory stringByAppendingString: @"/Res/"] cStringUsingEncoding:1]; #elif defined (ANDROID) - userPath = "/sdcard/Wagic/Res/"; + userPath = JGE::GetInstance()->getFileSystemLocation(); systemPath = ""; + + DebugTrace("User path " << userPath); #elif defined (QT_CONFIG) QDir dir(QDir::homePath()); dir.cd(USERDIR); @@ -165,7 +170,7 @@ JFileSystem::JFileSystem(const string & _userPath, const string & _systemPath) systemPath += '/'; } - mUserFSPath = userPath; + mUserFSPath = userPath; MAKEDIR(userPath.c_str()); mSystemFSPath = systemPath; @@ -178,6 +183,7 @@ JFileSystem::JFileSystem(const string & _userPath, const string & _systemPath) mPassword = NULL; mFileSize = 0; mCurrentFileInZip = NULL; + }; void JFileSystem::Destroy() diff --git a/JGE/src/JGE.cpp b/JGE/src/JGE.cpp index de5af017d..349ee200d 100644 --- a/JGE/src/JGE.cpp +++ b/JGE/src/JGE.cpp @@ -619,48 +619,68 @@ void JGE::SendCommand(std::string command, float& x, float& y, float& width, flo #if defined (ANDROID) -int getJNIEnv(JNIEnv *env) +JNIEnv * JGE::getJNIEnv() { + JNIEnv *env; int status = mJavaVM->GetEnv((void **)&env, JNI_VERSION_1_4); - if(status < 0) + if(status == JNI_ERR) { DebugTrace("Failed to get JNI environment, assuming native thread"); status = mJavaVM->AttachCurrentThread(&env, NULL); - if(status < 0) + if(status == JNI_ERR) { - LogNativeToAndroidExt("callback_handler: failed to attach current thread"); - return JNI_ERR; + DebugTrace("callback_handler: failed to attach current thread"); + return NULL; } } - - return JNI_VERSION_1_4; + else if (status == JNI_EDETACHED) + { + DebugTrace("env is detached. trying to attach it to the current thread"); + jint attachSuccess = mJavaVM->AttachCurrentThread(&env,NULL); + if(attachSuccess == 0) + { + return env; + } + else + { + return NULL; + }//end if/els + } + else if (status == JNI_OK) + { + return env; + } + + return NULL; } string JGE::getFileSystemLocation() { char result[512]; - JNIEnv * env; - if (getJNIEnv(env) == JNI_ERR) + JNIEnv * env = getJNIEnv(); + if (env == NULL) { DebugTrace("An Error Occurred in getting the JNI Environment whie trying to get the system folder location. Defaulting to /mnt/sdcard/net.wagic.app/Wagic"); return "/mnt/sdcard/net.wagic.app/Wagic"; }; - jmethodID methodId = env->GetStaticMethodID(mJNIClass, "getSystemFolderPath", "()Ljava/lang/String;"); + + jclass jniClass = env->FindClass("org/libsdl/app/SDLActivity"); + jmethodID methodId = env->GetStaticMethodID( jniClass, "getSystemFolderPath", "()Ljava/lang/String;"); + if (methodId == 0) { DebugTrace("An Error Occurred in getting the JNI methodID for getSystemFolderPath. Defaulting to /mnt/sdcard/net.wagic.app/Wagic"); return "/mnt/sdcard/net.wagic.app/Wagic"; }; - - jstring systemPath = (jstring) env->CallStaticObjectMethod(mJNIClass, methodId); - string retVal = "/mnt/sdcard/net.wagic.app/Wagic/"; - // Now convert the Java String to C++ char array + + jstring systemPath = (jstring) env->CallStaticObjectMethod(jniClass, methodId); + + // Now convert the Java String to C++ char array const char* cstr = env->GetStringUTFChars(systemPath, 0); - string val (cstr); - retVal = val; + string retVal (cstr); env->ReleaseStringUTFChars(systemPath, cstr); - env->DeleteLocalRef(systemPath); - + env->DeleteLocalRef(systemPath); + return retVal; } @@ -672,6 +692,11 @@ void JGE::SetJNIEnv(JNIEnv * env, jclass cls) midSendCommand = mJNIEnv->GetStaticMethodID(mJNIClass,"jgeSendCommand","(Ljava/lang/String;)V"); } +void JGE::setJVM( JavaVM *vm) +{ + mJavaVM = vm; +} + void JGE::sendJNICommand(string command) { if (midSendCommand) { diff --git a/JGE/src/android/JSfx.cpp b/JGE/src/android/JSfx.cpp index f75a391ea..dec8039b6 100644 --- a/JGE/src/android/JSfx.cpp +++ b/JGE/src/android/JSfx.cpp @@ -22,7 +22,7 @@ static SLObjectItf outputMixObject = NULL; ////////////////////////////////////////////////////////////////////////// JMusic::JMusic() - : playerObject(0), playInterface(0), seekInterface(0) + : playerObject(0), playInterface(0), seekInterface(0), musicVolumeInterface(0) { } @@ -42,6 +42,7 @@ JMusic::~JMusic() playerObject = NULL; playInterface = NULL; seekInterface = NULL; + musicVolumeInterface = NULL; } } @@ -194,9 +195,13 @@ JMusic *JSoundSystem::LoadMusic(const char *fileName) DebugTrace("result " << result); // get the volume interface - //result = (*music->playerObject)->GetInterface(music->playerObject, SL_IID_VOLUME, &musicVolumeInterface); + //result = (*music->playerObject)->GetInterface(music->playerObject, SL_IID_VOLUME, (void *)&music->musicVolumeInterface); + DebugTrace("result " << result); } + + mCurrentMusic = music; + return music; } @@ -207,6 +212,8 @@ void JSoundSystem::PlayMusic(JMusic *music, bool looping) { SLresult result; + //(*music->musicVolumeInterface)->SetVolumeLevel(music->musicVolumeInterface, -1 * mVolume); + // enable whole file looping result = (*music->seekInterface)->SetLoop(music->seekInterface, looping?SL_BOOLEAN_TRUE:SL_BOOLEAN_FALSE, 0, SL_TIME_UNKNOWN); @@ -250,10 +257,13 @@ void JSoundSystem::SetVolume(int volume) void JSoundSystem::SetMusicVolume(int volume) { mVolume = volume; + //(*mCurrentMusic->musicVolumeInterface)->SetVolumeLevel(mCurrentMusic->musicVolumeInterface, -1 * mVolume); } void JSoundSystem::SetSfxVolume(int volume){ mSampleVolume = volume; + + //(*mCurrentSample->sampleVolumeInterface)->SetVolumeLevel(mCurrentSample->sampleVolumeInterface, -1 * mSampleVolume); SetMusicVolume(mVolume); } @@ -296,8 +306,11 @@ JSample *JSoundSystem::LoadSample(const char *fileName) DebugTrace("result " << result); // get the volume interface - //result = (*sample->playerObject)->GetInterface(sample->playerObject, SL_IID_VOLUME, &sampleVolumeInterface); + //result = (*sample->playerObject)->GetInterface(sample->playerObject, SL_IID_VOLUME, &sample->sampleVolumeInterface); } + + mCurrentSample = sample; + return sample; } @@ -307,7 +320,7 @@ void JSoundSystem::PlaySample(JSample *sample) if(sample && sample->playerObject && sample->playInterface) { SLresult result; - + //(*sample->sampleVolumeInterface)->SetVolumeLevel(sample->sampleVolumeInterface, mSampleVolume); result = (*sample->playInterface)->SetPlayState(sample->playInterface, SL_PLAYSTATE_PLAYING); } diff --git a/projects/mtg/Android/AndroidManifest.xml b/projects/mtg/Android/AndroidManifest.xml index 60cb3cf32..05d083ba8 100644 --- a/projects/mtg/Android/AndroidManifest.xml +++ b/projects/mtg/Android/AndroidManifest.xml @@ -17,9 +17,8 @@ - + - + diff --git a/projects/mtg/Android/default.properties b/projects/mtg/Android/default.properties index 9a2c9f6c8..bdc22130b 100644 --- a/projects/mtg/Android/default.properties +++ b/projects/mtg/Android/default.properties @@ -8,4 +8,4 @@ # project structure. # Project target. -target=android-9 +target=Google Inc.:Google APIs:11 diff --git a/projects/mtg/Android/res/values/strings.xml b/projects/mtg/Android/res/values/strings.xml index 15ce3c190..6caf40600 100644 --- a/projects/mtg/Android/res/values/strings.xml +++ b/projects/mtg/Android/res/values/strings.xml @@ -1,4 +1,5 @@ Wagic + Wagic v0.180\\nAll Rights Reserved. diff --git a/projects/mtg/Android/src/net/wagic/utils/StorageOptions.java b/projects/mtg/Android/src/net/wagic/utils/StorageOptions.java new file mode 100644 index 000000000..6802b2062 --- /dev/null +++ b/projects/mtg/Android/src/net/wagic/utils/StorageOptions.java @@ -0,0 +1,137 @@ +package net.wagic.utils; +import java.io.File; +import java.util.ArrayList; +import java.util.Scanner; + +public class StorageOptions { + private static ArrayList mMounts = new ArrayList(); + private static ArrayList mVold = new ArrayList(); + + public static String[] labels; + public static String[] paths; + public static int count = 0; + + public static void determineStorageOptions() { + readMountsFile(); + readVoldFile(); + compareMountsWithVold(); + testAndCleanMountsList(); + setProperties(); + } + private static void readMountsFile() { + /* + * Scan the /proc/mounts file and look for lines like this: + * /dev/block/vold/179:1 /mnt/sdcard vfat rw,dirsync,nosuid,nodev,noexec,relatime,uid=1000,gid=1015,fmask=0602,dmask=0602,allow_utime=0020,codepage=cp437,iocharset=iso8859-1,shortname=mixed,utf8,errors=remount-ro 0 0 + * + * When one is found, split it into its elements + * and then pull out the path to the that mount point + * and add it to the arraylist + */ + + try { + Scanner scanner = new Scanner(new File("/proc/mounts")); + while (scanner.hasNext()) { + String line = scanner.nextLine(); + if (line.startsWith("/dev/block/vold/")) { + String[] lineElements = line.split(" "); + lineElements[1].replaceAll(":.*$", ""); + mMounts.add(lineElements[1]); + } + } + } catch (Exception e) { + // Auto-generated catch block + e.printStackTrace(); + } + } + + private static void readVoldFile() { + /* + * Scan the /system/etc/vold.fstab file and look for lines like this: + * dev_mount sdcard /mnt/sdcard 1 /devices/platform/s3c-sdhci.0/mmc_host/mmc0 + * + * When one is found, split it into its elements + * and then pull out the path to the that mount point + * and add it to the arraylist + */ + + try { + Scanner scanner = new Scanner(new File("/system/etc/vold.fstab")); + while (scanner.hasNext()) { + String line = scanner.nextLine(); + if (line.startsWith("dev_mount")) { + String[] lineElements = line.split(" "); + lineElements[2] = lineElements[2].replaceAll(":.*$", ""); + mVold.add(lineElements[2]); + } + } + } catch (Exception e) { + // Auto-generated catch block + e.printStackTrace(); + } + } + + private static void compareMountsWithVold() { + /* + * Sometimes the two lists of mount points will be different. + * We only want those mount points that are in both list. + * + * Compare the two lists together and remove items that are not in both lists. + */ + + for (int i = 0; i < mMounts.size(); i++) { + String mount = mMounts.get(i); + if (!mVold.contains(mount)) + mMounts.remove(i--); + } + + // donŐt need this anymore, clear the vold list to reduce memory + // use and to prepare it for the next time itŐs needed. + mVold.clear(); + } + + private static void testAndCleanMountsList() { + /* + * Now that we have a cleaned list of mount paths + * Test each one to make sure itŐs a valid and + * available path. If it is not, remove it from + * the list. + */ + + for (int i = 0; i < mMounts.size(); i++) { + String mount = mMounts.get(i); + File root = new File(mount); + if (!root.exists() || !root.isDirectory() || !root.canWrite()) + mMounts.remove(i--); + } + } + + private static void setProperties() { + /* + * At this point all the paths in the list should be + * valid. Build the public properties. + */ + + ArrayList mLabels = new ArrayList(); + + int i = 1; + for (String path: mMounts) + { + if ("/mnt/sdcard".equalsIgnoreCase(path)) + mLabels.add("Built-in Storage"); + else + mLabels.add("External SD Card " + i++); + } + + labels = new String[mLabels.size()]; + mLabels.toArray(labels); + + paths = new String[mMounts.size()]; + mMounts.toArray(paths); + + count = Math.min(labels.length, paths.length); + + // donŐt need this anymore, clear the mounts list to reduce memory + // use and to prepare it for the next time itŐs needed. + mMounts.clear(); + } +} \ No newline at end of file diff --git a/projects/mtg/Android/src/org/libsdl/app/SDLActivity.java b/projects/mtg/Android/src/org/libsdl/app/SDLActivity.java index 6f306c1f4..e3d9efd9c 100644 --- a/projects/mtg/Android/src/org/libsdl/app/SDLActivity.java +++ b/projects/mtg/Android/src/org/libsdl/app/SDLActivity.java @@ -1,17 +1,28 @@ package org.libsdl.app; +import java.io.BufferedInputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.URL; +import java.net.URLConnection; + 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 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.graphics.Canvas; import android.graphics.PixelFormat; import android.hardware.Sensor; @@ -29,31 +40,30 @@ 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; -import com.google.ads.*; - - -import java.io.BufferedInputStream; -import java.io.File; -import java.io.FileOutputStream; -import java.io.InputStream; -import java.io.OutputStream; -import java.net.URL; -import java.net.URLConnection; +import com.google.ads.AdRequest; +import com.google.ads.AdSize; +import com.google.ads.AdView; /** SDL Activity */ -public class SDLActivity extends Activity { +public class SDLActivity extends Activity implements OnKeyListener{ + // TAG used for debugging in DDMS + public static String TAG = Activity.class.getCanonicalName(); // Main components private static AdView mAdView; @@ -74,9 +84,220 @@ public class SDLActivity extends Activity { public final static String RES_FOLDER = "/sdcard/Wagic/Res/"; public static final String RES_FILENAME = "core_0180.zip"; public static final String RES_URL = "http://wagic.googlecode.com/files/"; - + + public String systemFolder = "/sdcard/Wagic/Res/"; + private String userFolder; + + // 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 ? sdcardPath : internalPath) + "/Res/"; + userFolder = (usesInternalSdCard ? sdcardPath : internalPath) + "/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 useSdCard = (!settings.getBoolean(kStoreDataOnRemovableSdCardPreference, false)) && Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()); + 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; + } + + return false; + } + + 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 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(); + File externalFilesDir = Environment.getExternalStorageDirectory(); + if ( externalFilesDir != null) { + internalPath = externalFilesDir.getAbsolutePath() + "/" + packageName + "/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 + "/" + packageName + "/Wagic"; + } + } + + updateStorageLocations(); + } + catch (Exception ioex) + { + Log.e( "SDL", "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 = RES_URL + RES_FILENAME; + 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); } @@ -85,6 +306,49 @@ public class SDLActivity extends Activity { 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]); + } + } + + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + SubMenu settingsMenu = menu.addSubMenu(Menu.NONE, 1, 1, "Settings"); + menu.add(Menu.NONE, 2, 2, "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) + { + //display some info about the app + AlertDialog.Builder infoDialog = new AlertDialog.Builder(this); + infoDialog.setTitle("Wagic Info"); + infoDialog.setMessage("Version 0.180"); + infoDialog.show(); + + } + else + return super.onOptionsItemSelected(item); + return true; + } + @Override protected Dialog onCreateDialog(int id) { switch (id) { @@ -169,8 +433,18 @@ public class SDLActivity extends Activity { // So we can call stuff from static callbacks mSingleton = this; - - File file = new File(RES_FOLDER + RES_FILENAME); + mContext = this.getApplicationContext(); + + StorageOptions.determineStorageOptions(); + checkStorageLocationPreference(); + } + + public void initializeGame() + { + String coreFileLocation = getSystemStorageLocation() + RES_FILENAME; + + File file = new File(coreFileLocation); + if (file.exists()) { mainDisplay(); } else { @@ -179,9 +453,9 @@ public class SDLActivity extends Activity { new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT)); startDownload(); } - } - + + // Events @Override protected void onPause() { @@ -232,7 +506,7 @@ public class SDLActivity extends Activity { if (msg.arg1 == COMMAND_CHANGE_TITLE) { setTitle((String)msg.obj); } - if (msg.arg1 == COMMAND_JGE_MSG) { + else if (msg.arg1 == COMMAND_JGE_MSG) { processJGEMsg((String)msg.obj); } } @@ -260,9 +534,19 @@ public class SDLActivity extends Activity { 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); } @@ -378,7 +662,9 @@ public class SDLActivity extends Activity { } - class DownloadFileAsync extends AsyncTask { + class DownloadFileAsync extends AsyncTask { + final String TAG1 = DownloadFileAsync.class.getCanonicalName(); + @Override protected void onPreExecute() { super.onPreExecute(); @@ -386,43 +672,47 @@ public class SDLActivity extends Activity { } @Override - protected String doInBackground(String... aurl) { + 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 state = Environment.getExternalStorageState(); - if (! Environment.MEDIA_MOUNTED.equals(state)) { - mSingleton.downloadError("cannot write to SD Card, please check your sd card"); - return null; + + 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."); } - File resDirectory = new File(RES_FOLDER); //TODO use getExternalStorageDirectory() and update the C++ code - resDirectory.mkdirs(); URL url = new URL(aurl[0]); + String filename = url.getPath().substring( url.getPath().lastIndexOf( '/') + 1); URLConnection conexion = url.openConnection(); conexion.connect(); - int lenghtOfFile = conexion.getContentLength(); - Log.d("ANDRO_ASYNC", "Length of file: " + lenghtOfFile); + int lengthOfFile = conexion.getContentLength(); + Log.d("Wagic - " + TAG1, " Length of file: " + lengthOfFile); - InputStream input = new BufferedInputStream(url.openStream()); + input = new BufferedInputStream(url.openStream()); // create a File object for the output file - File outputFile = new File(resDirectory, RES_FILENAME + ".tmp"); + File outputFile = new File(resDirectory, filename + ".tmp"); - OutputStream output = new FileOutputStream(outputFile); + output = new FileOutputStream(outputFile); byte data[] = new byte[1024]; - - long total = 0; - while ((count = input.read(data)) != -1) { - total += count; - publishProgress(""+(int)((total*100)/lenghtOfFile)); + totalBytes += count; + publishProgress((int)((totalBytes*100)/lengthOfFile)); output.write(data, 0, count); } @@ -430,33 +720,61 @@ public class SDLActivity extends Activity { output.close(); input.close(); } catch (Exception e) { - mSingleton.downloadError("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 " + RES_FOLDER + ". Please check your phone settings and try again. For more help please go to http://wagic.net"); + 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(TAG1, errorMessage); + Log.e(TAG1, e.getMessage()); } - return null; + + return new Long(totalBytes); } - protected void onProgressUpdate(String... progress) { - Log.d("ANDRO_ASYNC",progress[0]); - mProgressDialog.setProgress(Integer.parseInt(progress[0])); + protected void onProgressUpdate(Integer... progress) { + if (progress[0] != mProgressDialog.getProgress()) + { + Log.d("Wagic - " + TAG1, "current progress : " + progress[0]); + mProgressDialog.setProgress(progress[0]); + } } @Override - protected void onPostExecute(String unused) { + protected void onPostExecute(Long unused) { if (mErrorHappened) { dismissDialog(DIALOG_DOWNLOAD_PROGRESS); showDialog(DIALOG_DOWNLOAD_ERROR); return; } //rename the temporary file into the final filename - File preFile = new File(RES_FOLDER + RES_FILENAME + ".tmp"); - File postFile = new File(RES_FOLDER + RES_FILENAME); - preFile.renameTo(postFile); + 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(); } } + + + @Override + 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; + } @@ -753,15 +1071,17 @@ class SDLSurface extends SurfaceView implements SurfaceHolder.Callback, // Key events public boolean onKey(View v, int keyCode, KeyEvent event) { + if (keyCode == KeyEvent.KEYCODE_MENU) + return false; if (event.getAction() == KeyEvent.ACTION_DOWN) { - //Log.v("SDL", "key down: " + keyCode); - SDLActivity.onNativeKeyDown(keyCode); + //Log.d("SDL", "key down: " + keyCode); + SDLActivity.onNativeKeyDown(keyCode); return true; } else if (event.getAction() == KeyEvent.ACTION_UP) { - //Log.v("SDL", "key up: " + keyCode); - SDLActivity.onNativeKeyUp(keyCode); + //Log.d("SDL", "key up: " + keyCode); + SDLActivity.onNativeKeyUp(keyCode); return true; } @@ -838,7 +1158,3 @@ class SDLSurface extends SurfaceView implements SurfaceHolder.Callback, } } } - - - - diff --git a/projects/mtg/include/GameStateDeckViewer.h b/projects/mtg/include/GameStateDeckViewer.h index f12ba69e1..63d15592a 100644 --- a/projects/mtg/include/GameStateDeckViewer.h +++ b/projects/mtg/include/GameStateDeckViewer.h @@ -77,7 +77,7 @@ enum DECK_VIEWER_MENU_ITEMS #define LOW_SPEED 1.5 #define MAX_SAVED_FILTERS Constants::NB_Colors + 1 -#define CARDS_DISPLAYED 10 +#define CARDS_DISPLAYED 5 class GameStateDeckViewer: public GameState, public JGuiListener { diff --git a/projects/mtg/src/GameStateShop.cpp b/projects/mtg/src/GameStateShop.cpp index ad5d87c31..9647d83a6 100644 --- a/projects/mtg/src/GameStateShop.cpp +++ b/projects/mtg/src/GameStateShop.cpp @@ -44,7 +44,7 @@ BoosterDisplay::BoosterDisplay(int id, GameObserver* game, int x, int y, JGuiLis bool BoosterDisplay::CheckUserInput(JButton key) { - if (JGE_BTN_UP == key || JGE_BTN_DOWN == key) + if (JGE_BTN_UP == key || JGE_BTN_DOWN == key || JGE_BTN_PRI == key) return false; return CardDisplay::CheckUserInput(key); @@ -608,17 +608,6 @@ void GameStateShop::Update(float dt) taskList = NEW TaskList(); taskList->Start(); } - else if (btn == JGE_BTN_PRI) - { - srcCards->Shuffle(); - load(); - disablePurchase = false; - clearInput = true; - return; - - } - else if (btn == JGE_BTN_CANCEL) - options[Options::DISABLECARDS].number = !options[Options::DISABLECARDS].number; else if (boosterDisplay) { if (btn == JGE_BTN_SEC) @@ -630,6 +619,17 @@ void GameStateShop::Update(float dt) } return; } + else if (btn == JGE_BTN_PRI) // so we don't shuffle while we view our newly purchased booster display. + { + srcCards->Shuffle(); + load(); + disablePurchase = false; + clearInput = true; + return; + + } + else if (btn == JGE_BTN_CANCEL) + options[Options::DISABLECARDS].number = !options[Options::DISABLECARDS].number; else if (btn == JGE_BTN_SEC) { bListCards = !bListCards;