From d9e9dc329ed9579191f35ba7d38f97bfeb06de45 Mon Sep 17 00:00:00 2001 From: Nora Ky Date: Thu, 25 Aug 2016 10:22:09 +0700 Subject: [PATCH] Add ability to read and write to app internal dir and cache dir in Android, getUserDataDirectory() and getUserHomeDirectory() are point to inernal storage. --- .../com/garagegames/torque2d/FileWalker.java | 29 ++++++ .../source/platformAndroid/AndroidFileio.cpp | 53 +++++++++-- engine/source/platformAndroid/T2DActivity.cpp | 94 +++++++++++++++++++ engine/source/platformAndroid/T2DActivity.h | 7 ++ 4 files changed, 175 insertions(+), 8 deletions(-) diff --git a/engine/compilers/android-studio/app/src/main/java/com/garagegames/torque2d/FileWalker.java b/engine/compilers/android-studio/app/src/main/java/com/garagegames/torque2d/FileWalker.java index 4edd53541..eec9647d8 100644 --- a/engine/compilers/android-studio/app/src/main/java/com/garagegames/torque2d/FileWalker.java +++ b/engine/compilers/android-studio/app/src/main/java/com/garagegames/torque2d/FileWalker.java @@ -1,6 +1,12 @@ package com.garagegames.torque2d; +import java.io.BufferedReader; +import java.io.DataInputStream; +import java.io.File; +import java.io.FileInputStream; import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; import java.util.Hashtable; import java.util.Vector; @@ -331,6 +337,29 @@ public static int GetFileSize(Context context, String file) return ret; } + public static String LoadInternalFile(Context context, String fileName) + { + try { + FileInputStream fis = new FileInputStream(new File(fileName)); + DataInputStream in = new DataInputStream(fis); + BufferedReader inputReader = new BufferedReader(new InputStreamReader(in)); + String inputString; + StringBuilder sb = new StringBuilder(); + while ((inputString = inputReader.readLine()) != null) { + sb.append(inputString); + } + fis.close(); + inputReader.close(); + in.close(); + + return sb.toString(); + + } catch (IOException e) { + e.printStackTrace(); + return ""; + } + } + public static void OpenURL(Context context, String url) { Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(url)); diff --git a/engine/source/platformAndroid/AndroidFileio.cpp b/engine/source/platformAndroid/AndroidFileio.cpp index 3a668450f..3f8743a10 100755 --- a/engine/source/platformAndroid/AndroidFileio.cpp +++ b/engine/source/platformAndroid/AndroidFileio.cpp @@ -46,6 +46,36 @@ //Cache handling functions //----------------------------------------------------------------------------- bool isCachePath(const char* path) +{ + if (!path || !*path) + return false; + + if (path[0] == '/') + { + if (strstr(path, Platform::osGetTemporaryDirectory()) != NULL) + { + return true; + } + else + { + return false; + } + } + else + { + const char* tmp = Platform::osGetTemporaryDirectory(); + if (strstr(path, tmp+1) != NULL) + { + return true; + } + else + { + return false; + } + } +} + +bool isUserDataPath(const char* path) { if (!path || !*path) return false; @@ -142,7 +172,7 @@ File::~File() File::Status File::open(const char *filename, const AccessMode openMode) { //If its a cache path then we need to open it using C methods not AssetManager - if (isCachePath(filename)) + if (isCachePath(filename) || isUserDataPath(filename)) { if (dStrlen(filename) > MAX_MAC_PATH_LONG) Con::warnf("File::open: Filename length is pretty long..."); @@ -155,7 +185,14 @@ File::Status File::open(const char *filename, const AccessMode openMode) switch (openMode) { case Read: - handle = (void *)fopen(filename, "rb"); // read only + capability = FileRead; + filePointer = 0; + buffer = (U8*)_AndroidLoadInternalFile(filename, &size); + if (buffer == NULL) + currentStatus = UnknownError; + else + currentStatus = Ok; + return currentStatus; break; case Write: handle = (void *)fopen(filename, "wb"); // write only @@ -794,7 +831,7 @@ bool Platform::isFile(const char *path) if (!path || !*path) return false; - if (isCachePath(path)) + if (isCachePath(path) || isUserDataPath(path)) { // make sure we can stat the file struct stat statData; @@ -824,7 +861,7 @@ bool Platform::isDirectory(const char *path) if (!path || !*path) return false; - if (isCachePath(path)) + if (isCachePath(path) || isUserDataPath(path)) { // make sure we can stat the file struct stat statData; @@ -847,7 +884,7 @@ S32 Platform::getFileSize(const char* pFilePath) if (!pFilePath || !*pFilePath) return 0; - if (isCachePath(pFilePath)) + if (isCachePath(pFilePath) || isUserDataPath(pFilePath)) { struct stat statData; if( stat(pFilePath, &statData) < 0 ) @@ -899,7 +936,7 @@ inline bool isGoodDirectoryCache(dirent* entry) //----------------------------------------------------------------------------- bool Platform::hasSubDirectory(const char *path) { - if (isCachePath(path)) + if (isCachePath(path) || isUserDataPath(path)) { DIR *dir; dirent *entry; @@ -1091,7 +1128,7 @@ bool recurseDumpDirectoriesCache(const char *basePath, const char *path, Vector< //----------------------------------------------------------------------------- bool Platform::dumpDirectories(const char *path, Vector &directoryVector, S32 depth, bool noBasePath) { - if (isCachePath(path)) + if (isCachePath(path) || isUserDataPath(path)) { PROFILE_START(dumpDirectories); @@ -1247,7 +1284,7 @@ static bool recurseDumpPathCache(const char* curPath, Vector //----------------------------------------------------------------------------- bool Platform::dumpPath(const char *path, Vector& fileVector, S32 depth) { - if (isCachePath(path)) + if (isCachePath(path) || isUserDataPath(path)) { PROFILE_START(dumpPath); const S32 len = dStrlen(path) + 1; diff --git a/engine/source/platformAndroid/T2DActivity.cpp b/engine/source/platformAndroid/T2DActivity.cpp index f681d5e31..87746dfd5 100755 --- a/engine/source/platformAndroid/T2DActivity.cpp +++ b/engine/source/platformAndroid/T2DActivity.cpp @@ -1225,6 +1225,7 @@ void android_main(struct android_app* state) { //store the cache dir activity.loadCacheDir(); + activity.loadInternalDir(); //enumerate fonts activity.enumerateFonts(); @@ -2622,4 +2623,97 @@ void adprintf(const char* fmt,...) { __android_log_print(ANDROID_LOG_INFO, "Torque2D", "%s", ss.str().c_str()); } +void T2DActivity::loadInternalDir(){ + + // Attaches the current thread to the JVM. + jint lResult; + jint lFlags = 0; + JavaVM* lJavaVM = platState.engine->app->activity->vm; + JNIEnv* lJNIEnv = platState.engine->app->activity->env; + + JavaVMAttachArgs lJavaVMAttachArgs; + lJavaVMAttachArgs.version = JNI_VERSION_1_6; + lJavaVMAttachArgs.name = "NativeThread"; + lJavaVMAttachArgs.group = NULL; + + lResult=lJavaVM->AttachCurrentThread(&lJNIEnv, &lJavaVMAttachArgs); + if (lResult == JNI_ERR) { + return; + } + + // Retrieves NativeActivity. + jobject lNativeActivity = platState.engine->app->activity->clazz; + jclass ClassNativeActivity = lJNIEnv->GetObjectClass(lNativeActivity); + + jmethodID getFilesDir = lJNIEnv->GetMethodID(ClassNativeActivity, "getFilesDir", "()Ljava/io/File;"); + jobject file = lJNIEnv->CallObjectMethod(platState.engine->app->activity->clazz, getFilesDir); + jclass fileClass = lJNIEnv->FindClass("java/io/File"); + jmethodID getAbsolutePath = lJNIEnv->GetMethodID(fileClass, "getAbsolutePath", "()Ljava/lang/String;"); + jstring jpath = (jstring)lJNIEnv->CallObjectMethod(file, getAbsolutePath); + const char* app_dir = lJNIEnv->GetStringUTFChars(jpath, NULL); + + strcpy(internalDir, app_dir); + internalDir[strlen(app_dir)] = '\0'; + + lJNIEnv->ReleaseStringUTFChars(jpath, app_dir); + + // Finished with the JVM. + lJavaVM->DetachCurrentThread(); +} + + +char* _AndroidLoadInternalFile(const char* fn, U32 *size) +{ + // Attaches the current thread to the JVM. + jint lResult; + jint lFlags = 0; + + JavaVM* lJavaVM = engine.app->activity->vm; + JNIEnv* lJNIEnv = engine.app->activity->env; + + JavaVMAttachArgs lJavaVMAttachArgs; + lJavaVMAttachArgs.version = JNI_VERSION_1_6; + lJavaVMAttachArgs.name = "NativeThread"; + lJavaVMAttachArgs.group = NULL; + + lResult=lJavaVM->AttachCurrentThread(&lJNIEnv, &lJavaVMAttachArgs); + if (lResult == JNI_ERR) { + return ""; + } + + // Retrieves NativeActivity. + jobject lNativeActivity = engine.app->activity->clazz; + jclass ClassNativeActivity = lJNIEnv->GetObjectClass(lNativeActivity); + + jmethodID getClassLoader = lJNIEnv->GetMethodID(ClassNativeActivity,"getClassLoader", "()Ljava/lang/ClassLoader;"); + jobject cls = lJNIEnv->CallObjectMethod(lNativeActivity, getClassLoader); + jclass classLoader = lJNIEnv->FindClass("java/lang/ClassLoader"); + jmethodID findClass = lJNIEnv->GetMethodID(classLoader, "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;"); + jstring strClassName = lJNIEnv->NewStringUTF("com.garagegames.torque2d.FileWalker"); + jclass FileWalkerClass = (jclass)lJNIEnv->CallObjectMethod(cls, findClass, strClassName); + jstring fileName = lJNIEnv->NewStringUTF(fn); + jmethodID MethodFileWalker = lJNIEnv->GetStaticMethodID(FileWalkerClass, "LoadInternalFile", "(Landroid/content/Context;Ljava/lang/String;)Ljava/lang/String;"); + jstring jcontent = (jstring)lJNIEnv->CallStaticObjectMethod(FileWalkerClass, MethodFileWalker, lNativeActivity, fileName); + + lJNIEnv->DeleteLocalRef(strClassName); + lJNIEnv->DeleteLocalRef(fileName); + *size = 0; + char *buffer = NULL; + if (jcontent != NULL) + { + const char* value = lJNIEnv->GetStringUTFChars(jcontent, NULL); + *size = strlen(value); + buffer = new char[*size + 1]; + strcpy(buffer, value); + buffer[*size] = '\0'; + + lJNIEnv->ReleaseStringUTFChars(jcontent, value); + lJavaVM->DetachCurrentThread(); + return buffer; + } + + lJavaVM->DetachCurrentThread(); + return buffer; +} + diff --git a/engine/source/platformAndroid/T2DActivity.h b/engine/source/platformAndroid/T2DActivity.h index 1dbef3f82..462adce0a 100755 --- a/engine/source/platformAndroid/T2DActivity.h +++ b/engine/source/platformAndroid/T2DActivity.h @@ -41,6 +41,7 @@ extern int _AndroidGetScreenWidth(); extern int _AndroidGetScreenHeight(); extern S32 _AndroidGameGetOrientation(); extern char* _AndroidLoadFile(const char* fileName, U32 *size); +extern char* _AndroidLoadInternalFile(const char* fileName, U32 *size); extern void android_InitDirList(const char* dir); extern void android_GetNextDir(const char* pdir, char *dir); extern void android_GetNextFile(const char* pdir, char *file); @@ -93,6 +94,7 @@ class T2DActivity { bool isLayedOut; char cacheDir[2048]; + char internalDir[2048]; bool accelerometerActive; @@ -113,6 +115,11 @@ class T2DActivity { char *getCacheDir() { return cacheDir; } + void loadInternalDir(); + char *getInternalDir() { + return internalDir; + } + void update(); void finishShutdown(); void finishGLSetup();