diff --git a/app/build.gradle b/app/build.gradle index 3838ce1..968bb80 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -11,9 +11,10 @@ android { testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" externalNativeBuild { cmake { - cppFlags "" + //cppFlags "" //abiFilters 'arm64-v8a', 'armeabi-v7a', 'x86', 'x86_64' //abiFilters 'x86_64' + version "3.10.2" } } } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index c8df19f..e818743 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,12 +1,7 @@ - - - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + GetObjectClass(jniEnv, mainActivity); jmethodID midStr = (*jniEnv)->GetMethodID(jniEnv, mainActivityClass, "openFileFromContentResolver", "(Ljava/lang/String;I)I"); - jstring utfUrl = (*jniEnv)->NewStringUTF(jniEnv, url); - int result = (*jniEnv)->CallIntMethod(jniEnv, mainActivity, midStr, utfUrl, writeAccess); + jstring utfFileURL = (*jniEnv)->NewStringUTF(jniEnv, fileURL); + int result = (*jniEnv)->CallIntMethod(jniEnv, mainActivity, midStr, utfFileURL, writeAccess); + return result; +} +int openFileInFolderFromContentResolver(const TCHAR * filename, const TCHAR * folderURL, int writeAccess) { + JNIEnv *jniEnv = getJNIEnvironment(); + jclass mainActivityClass = (*jniEnv)->GetObjectClass(jniEnv, mainActivity); + jmethodID midStr = (*jniEnv)->GetMethodID(jniEnv, mainActivityClass, "openFileInFolderFromContentResolver", "(Ljava/lang/String;Ljava/lang/String;I)I"); + jstring utfFilename = (*jniEnv)->NewStringUTF(jniEnv, filename); + jstring utfFolderURL = (*jniEnv)->NewStringUTF(jniEnv, folderURL); + int result = (*jniEnv)->CallIntMethod(jniEnv, mainActivity, midStr, utfFilename, utfFolderURL, writeAccess); return result; } int closeFileFromContentResolver(int fd) { @@ -368,12 +377,11 @@ JNIEXPORT jint JNICALL Java_org_emulator_forty_eight_NativeLib_onFileNew(JNIEnv // *filename = _T('\0'); // } // _tcscpy(szRomDirectory, szEmuDirectory); - TCHAR * fileScheme = _T("raw:"); - TCHAR * urlContentSchemeFound = _tcsstr(szChosenCurrentKml, fileScheme); - if(urlContentSchemeFound) { - _tcscpy(szChosenCurrentKml, szChosenCurrentKml + 4 * sizeof(TCHAR)); - _tcscpy(szEmuDirectory, szChosenCurrentKml); - TCHAR * filename = _tcsrchr(szEmuDirectory, _T('/')); + TCHAR * fileScheme = _T("document:"); + TCHAR * urlSchemeFound = _tcsstr(szChosenCurrentKml, fileScheme); + if(urlSchemeFound) { + _tcscpy(szEmuDirectory, szChosenCurrentKml + _tcslen(fileScheme) * sizeof(TCHAR)); + TCHAR * filename = _tcschr(szEmuDirectory, _T('|')); if(filename) { *filename = _T('\0'); } @@ -408,22 +416,6 @@ JNIEXPORT jint JNICALL Java_org_emulator_forty_eight_NativeLib_onFileOpen(JNIEnv _tcscpy(szBufferFilename, stateFilenameUTF8); chooseCurrentKmlMode = ChooseKmlMode_FILE_OPEN; - //TODO -// TCHAR * fileScheme = _T("raw:"); -// TCHAR * urlContentSchemeFound = _tcsstr(szChosenCurrentKml, fileScheme); -// if(urlContentSchemeFound) { -// _tcscpy(szChosenCurrentKml, szChosenCurrentKml + 4 * sizeof(TCHAR)); -// _tcscpy(szEmuDirectory, szChosenCurrentKml); -// TCHAR * filename = _tcsrchr(szEmuDirectory, _T('/')); -// if(filename) { -// *filename = _T('\0'); -// } -// _tcscpy(szRomDirectory, szEmuDirectory); -// } else { -// _tcscpy(szEmuDirectory, "assets/calculators/"); -// _tcscpy(szRomDirectory, "assets/calculators/"); -// } - BOOL result = OpenDocument(szBufferFilename); if (result) MruAdd(szBufferFilename); diff --git a/app/src/main/cpp/win32-layer.c b/app/src/main/cpp/win32-layer.c index 33bd968..8d046ae 100644 --- a/app/src/main/cpp/win32-layer.c +++ b/app/src/main/cpp/win32-layer.c @@ -16,10 +16,14 @@ extern AndroidBitmapInfo androidBitmapInfo; HANDLE hWnd; LPTSTR szTitle; -LPTSTR szCurrentDirectorySet = NULL; -const TCHAR * assetsPrefix = _T("assets/"), - assetsPrefixLength = 7; +LPTSTR szCurrentAssetDirectory = NULL; +LPTSTR szCurrentContentDirectory = NULL; AAssetManager * assetManager; +const TCHAR * assetsPrefix = _T("assets/"); +size_t assetsPrefixLength; +const TCHAR * contentScheme = _T("content://"); +size_t contentSchemeLength; +const TCHAR * documentScheme = _T("document:"); //static HDC mainPaintDC = NULL; struct timerEvent { BOOL valid; @@ -43,6 +47,9 @@ void win32Init() { for (int i = 0; i < MAX_FILE_MAPPING_HANDLE; ++i) { fileMappingHandles[i] = NULL; } + + assetsPrefixLength = _tcslen(assetsPrefix); + contentSchemeLength = _tcslen(contentScheme); } VOID OutputDebugString(LPCSTR lpOutputString) { @@ -58,15 +65,21 @@ DWORD GetCurrentDirectory(DWORD nBufferLength, LPTSTR lpBuffer) { BOOL SetCurrentDirectory(LPCTSTR path) { + szCurrentContentDirectory = NULL; + szCurrentAssetDirectory = NULL; + if(path == NULL) return FALSE; - if(_tcsncmp(path, assetsPrefix, assetsPrefixLength / sizeof(TCHAR)) == 0) - szCurrentDirectorySet = (LPTSTR) (path + assetsPrefixLength); - else - szCurrentDirectorySet = NULL; - - return (BOOL) chdir(path); + if(_tcsncmp(path, contentScheme, contentSchemeLength) == 0) { + szCurrentContentDirectory = (LPTSTR) path; + return TRUE; + } else if(_tcsncmp(path, assetsPrefix, assetsPrefixLength) == 0) { + szCurrentAssetDirectory = (LPTSTR) (path + assetsPrefixLength * sizeof(TCHAR)); + return TRUE; + } else { + return (BOOL) chdir(path); + } } extern BOOL settingsPort2en; @@ -81,31 +94,33 @@ HANDLE CreateFile(LPCTSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode, if(!settingsPort2wr && (dwDesiredAccess & GENERIC_WRITE)) return (HANDLE) INVALID_HANDLE_VALUE; } - if(chooseCurrentKmlMode == ChooseKmlMode_FILE_OPEN && lpFileName[0] == '/') { - TCHAR * fileExtension = _tcsrchr(lpFileName, _T('.')); - if(fileExtension && ((fileExtension[1] == 'K' && fileExtension[2] == 'M' && fileExtension[3] == 'L') || - (fileExtension[1] == 'k' && fileExtension[2] == 'm' && fileExtension[3] == 'l') - )) { - _tcscpy(szEmuDirectory, lpFileName); - TCHAR * filename = _tcsrchr(szEmuDirectory, _T('/')); - if(filename) { - *filename = _T('\0'); - } - _tcscpy(szRomDirectory, szEmuDirectory); - SetCurrentDirectory(szEmuDirectory); - } - } +// if(chooseCurrentKmlMode == ChooseKmlMode_FILE_OPEN && lpFileName[0] == '/') { +// TCHAR * fileExtension = _tcsrchr(lpFileName, _T('.')); +// if(fileExtension && ((fileExtension[1] == 'K' && fileExtension[2] == 'M' && fileExtension[3] == 'L') || +// (fileExtension[1] == 'k' && fileExtension[2] == 'm' && fileExtension[3] == 'l') +// )) { +// _tcscpy(szEmuDirectory, lpFileName); +// TCHAR * filename = _tcsrchr(szEmuDirectory, _T('/')); +// if(filename) { +// *filename = _T('\0'); +// } +// _tcscpy(szRomDirectory, szEmuDirectory); +// SetCurrentDirectory(szEmuDirectory); +// } +// } + TCHAR * foundDocumentScheme = _tcsstr(lpFileName, documentScheme); - if(!forceNormalFile && (szCurrentDirectorySet || _tcsncmp(lpFileName, assetsPrefix, assetsPrefixLength / sizeof(TCHAR)) == 0)) { + if(!forceNormalFile && (szCurrentAssetDirectory || _tcsncmp(lpFileName, assetsPrefix, assetsPrefixLength) == 0) && foundDocumentScheme == NULL) { + // Asset file TCHAR szFileName[MAX_PATH]; AAsset * asset = NULL; szFileName[0] = _T('\0'); - if(szCurrentDirectorySet) { - _tcscpy(szFileName, szCurrentDirectorySet); + if(szCurrentAssetDirectory) { + _tcscpy(szFileName, szCurrentAssetDirectory); _tcscat(szFileName, lpFileName); asset = AAssetManager_open(assetManager, szFileName, AASSET_MODE_STREAMING); } else { - asset = AAssetManager_open(assetManager, lpFileName + assetsPrefixLength, AASSET_MODE_STREAMING); + asset = AAssetManager_open(assetManager, lpFileName + assetsPrefixLength * sizeof(TCHAR), AASSET_MODE_STREAMING); } if(asset) { HANDLE handle = malloc(sizeof(struct _HANDLE)); @@ -115,7 +130,7 @@ HANDLE CreateFile(LPCTSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode, return handle; } } else { - + // Normal file BOOL useOpenFileFromContentResolver = FALSE; int flags = O_RDWR; int fd = -1; @@ -134,13 +149,27 @@ HANDLE CreateFile(LPCTSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode, flags |= O_CREAT; } - TCHAR * urlContentSchemeFound = _tcsstr(lpFileName, _T("content://")); + if(foundDocumentScheme) { + TCHAR * filename = _tcsrchr(lpFileName, _T('|')); + if(filename) + lpFileName = filename + 1; + } + + TCHAR * urlContentSchemeFound = _tcsstr(lpFileName, contentScheme); if(urlContentSchemeFound) { + // Case of an absolute file with the scheme content:// fd = openFileFromContentResolver(lpFileName, dwDesiredAccess); useOpenFileFromContentResolver = TRUE; if(fd == -1) { LOGD("openFileFromContentResolver() %d", errno); } + } else if(szCurrentContentDirectory) { + // Case of a relative file to a folder with the scheme content:// + fd = openFileInFolderFromContentResolver(lpFileName, szCurrentContentDirectory, dwDesiredAccess); + useOpenFileFromContentResolver = TRUE; + if(fd == -1) { + LOGD("openFileFromContentResolver() %d", errno); + } } else { TCHAR * urlFileSchemeFound = _tcsstr(lpFileName, _T("file://")); if(urlFileSchemeFound) diff --git a/app/src/main/cpp/win32-layer.h b/app/src/main/cpp/win32-layer.h index 776f558..3073509 100644 --- a/app/src/main/cpp/win32-layer.h +++ b/app/src/main/cpp/win32-layer.h @@ -1118,7 +1118,8 @@ extern int lstrcmpi(LPCSTR lpString1, LPCSTR lpString2); extern void mainViewUpdateCallback(); extern void mainViewResizeCallback(int x, int y); -extern int openFileFromContentResolver(const TCHAR * url, int writeAccess); +extern int openFileFromContentResolver(const TCHAR * fileURL, int writeAccess); +extern int openFileInFolderFromContentResolver(const TCHAR * filename, const TCHAR * folderURL, int writeAccess); extern int closeFileFromContentResolver(int fd); extern int showAlert(const TCHAR * messageText, int flags); extern void sendMenuItemCommand(int menuItem); diff --git a/app/src/main/java/org/emulator/forty/eight/MainActivity.java b/app/src/main/java/org/emulator/forty/eight/MainActivity.java index 6fcacdc..be50dce 100644 --- a/app/src/main/java/org/emulator/forty/eight/MainActivity.java +++ b/app/src/main/java/org/emulator/forty/eight/MainActivity.java @@ -108,15 +108,6 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On final Toolbar toolbar = findViewById(R.id.toolbar); setSupportActionBar(toolbar); -// FloatingActionButton fab = findViewById(R.id.fab); -// fab.setOnClickListener(new View.OnClickListener() { -// @Override -// public void onClick(View view) { -// Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG) -// .setAction("Action", null).show(); -// } -// }); - drawer = findViewById(R.id.drawer_layout); ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(this, drawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close); drawer.addDrawerListener(toggle); @@ -175,46 +166,45 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On String documentToOpenUrl = sharedPreferences.getString("lastDocument", ""); - Uri documentToOpenUri = null; - boolean isFileAndNeedPermission = false; - Intent intent = getIntent(); - if(intent != null) { - String action = intent.getAction(); - if(action != null) { - if (action.equals(Intent.ACTION_VIEW)) { - documentToOpenUri = intent.getData(); - if (documentToOpenUri != null) { - String scheme = documentToOpenUri.getScheme(); - if(scheme != null && scheme.compareTo("file") == 0) { - documentToOpenUrl = documentToOpenUri.getPath(); - isFileAndNeedPermission = true; - } else - documentToOpenUrl = documentToOpenUri.toString(); - } - } else if (action.equals(Intent.ACTION_SEND)) { - documentToOpenUri = intent.getParcelableExtra(Intent.EXTRA_STREAM); - if (documentToOpenUri != null) { - documentToOpenUrl = documentToOpenUri.toString(); - } - } - } - } +// Uri documentToOpenUri = null; +// boolean isFileAndNeedPermission = false; +// Intent intent = getIntent(); +// if(intent != null) { +// String action = intent.getAction(); +// if(action != null) { +// if (action.equals(Intent.ACTION_VIEW)) { +// documentToOpenUri = intent.getData(); +// if (documentToOpenUri != null) { +// String scheme = documentToOpenUri.getScheme(); +// if(scheme != null && scheme.compareTo("file") == 0) { +// documentToOpenUrl = documentToOpenUri.getPath(); +// isFileAndNeedPermission = true; +// } else +// documentToOpenUrl = documentToOpenUri.toString(); +// } +// } else if (action.equals(Intent.ACTION_SEND)) { +// documentToOpenUri = intent.getParcelableExtra(Intent.EXTRA_STREAM); +// if (documentToOpenUri != null) { +// documentToOpenUrl = documentToOpenUri.toString(); +// } +// } +// } +// } //https://developer.android.com/guide/topics/providers/document-provider#permissions if(documentToOpenUrl != null && documentToOpenUrl.length() > 0) try { - if(isFileAndNeedPermission - && ActivityCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { - ActivityCompat.requestPermissions(this, new String[]{ Manifest.permission.WRITE_EXTERNAL_STORAGE }, PERMISSIONS_REQUEST_WRITE_EXTERNAL_STORAGE); - //return; - } else { +// if(isFileAndNeedPermission +// && ActivityCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { +// ActivityCompat.requestPermissions(this, new String[]{ Manifest.permission.WRITE_EXTERNAL_STORAGE }, PERMISSIONS_REQUEST_WRITE_EXTERNAL_STORAGE); +// //return; +// } else { if(onFileOpen(documentToOpenUrl) != 0) { saveLastDocument(documentToOpenUrl); - if(intent != null && documentToOpenUri != null && !isFileAndNeedPermission) - makeUriPersistable(intent, documentToOpenUri); +// if(intent != null && documentToOpenUri != null && !isFileAndNeedPermission) +// makeUriPersistable(intent, documentToOpenUri); } - - } +// } } catch (Exception e) { Log.e(TAG, e.getMessage()); } @@ -447,16 +437,15 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On } catch (IOException e) { e.printStackTrace(); } - String cKmlType = null; //"S"; kmlScripts.clear(); Pattern patternGlobalTitle = Pattern.compile("\\s*Title\\s+\"(.*)\""); Pattern patternGlobalModel = Pattern.compile("\\s*Model\\s+\"(.*)\""); Matcher m; - for (String calculatorsAssetFilename : calculatorsAssetFilenames) { - if (calculatorsAssetFilename.toLowerCase().lastIndexOf(".kml") != -1) { + for (String calculatorFilename : calculatorsAssetFilenames) { + if (calculatorFilename.toLowerCase().lastIndexOf(".kml") != -1) { BufferedReader reader = null; try { - reader = new BufferedReader(new InputStreamReader(assetManager.open("calculators/" + calculatorsAssetFilename), "UTF-8")); + reader = new BufferedReader(new InputStreamReader(assetManager.open("calculators/" + calculatorFilename), "UTF-8")); // do reading, usually loop until end of file reading String mLine; boolean inGlobal = false; @@ -473,12 +462,10 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On if (inGlobal) { if (mLine.indexOf("End") == 0) { KMLScriptItem newKMLScriptItem = new KMLScriptItem(); - newKMLScriptItem.filename = calculatorsAssetFilename; + newKMLScriptItem.filename = calculatorFilename; newKMLScriptItem.title = title; newKMLScriptItem.model = model; kmlScripts.add(newKMLScriptItem); - title = null; - model = null; break; } @@ -535,19 +522,18 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On calculatorsAssetFilenames.add(url); } } - String cKmlType = null; //"S"; kmlScripts.clear(); Pattern patternGlobalTitle = Pattern.compile("\\s*Title\\s+\"(.*)\""); Pattern patternGlobalModel = Pattern.compile("\\s*Model\\s+\"(.*)\""); Matcher m; - for (String calculatorsAssetFilename : calculatorsAssetFilenames) { - if (calculatorsAssetFilename.toLowerCase().lastIndexOf(".kml") != -1) { + for (String calculatorFilename : calculatorsAssetFilenames) { + if (calculatorFilename.toLowerCase().lastIndexOf(".kml") != -1) { BufferedReader reader = null; try { - Uri calculatorsAssetFilenameUri = Uri.parse(calculatorsAssetFilename); + Uri calculatorsAssetFilenameUri = Uri.parse(calculatorFilename); DocumentFile documentFile = DocumentFile.fromSingleUri(this, calculatorsAssetFilenameUri); - Uri documentFileUri = documentFile.getUri(); - InputStream inputStream = getContentResolver().openInputStream(documentFile.getUri()); + Uri fileUri = documentFile.getUri(); + InputStream inputStream = getContentResolver().openInputStream(fileUri); reader = new BufferedReader(new InputStreamReader(inputStream, "UTF-8")); // do reading, usually loop until end of file reading String mLine; @@ -565,12 +551,10 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On if (inGlobal) { if (mLine.indexOf("End") == 0) { KMLScriptItem newKMLScriptItem = new KMLScriptItem(); - newKMLScriptItem.filename = kmlFolderUseDefault ? calculatorsAssetFilename : kmlFolderURL + "|" + calculatorsAssetFilename; + newKMLScriptItem.filename = kmlFolderUseDefault ? calculatorFilename : "document:" + kmlFolderURL + "|" + calculatorFilename; newKMLScriptItem.title = title; newKMLScriptItem.model = model; kmlScripts.add(newKMLScriptItem); - title = null; - model = null; break; } @@ -1050,9 +1034,9 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On final int GENERIC_READ = 1; final int GENERIC_WRITE = 2; Map parcelFileDescriptorPerFd = null; - int openFileFromContentResolver(String url, int writeAccess) { + int openFileFromContentResolver(String fileURL, int writeAccess) { //https://stackoverflow.com/a/31677287 - Uri uri = Uri.parse(url); + Uri uri = Uri.parse(fileURL); ParcelFileDescriptor filePfd; try { String mode = ""; @@ -1072,6 +1056,19 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On parcelFileDescriptorPerFd.put(fd, filePfd); return fd; } + int openFileInFolderFromContentResolver(String filename, String folderURL, int writeAccess) { + Uri folderURI = Uri.parse(folderURL); + DocumentFile folderDocumentFile = DocumentFile.fromTreeUri(this, folderURI); + for (DocumentFile file : folderDocumentFile.listFiles()) { + final String url = file.getUri().toString(); + final String name = file.getName(); + //Log.d(TAG, "url: " + url + ", name: " + name); + if(filename.equals(name)) { + return openFileFromContentResolver(url, writeAccess); + } + } + return -1; + } int closeFileFromContentResolver(int fd) { if(parcelFileDescriptorPerFd != null) { ParcelFileDescriptor filePfd = parcelFileDescriptorPerFd.get(fd); diff --git a/app/src/main/res/drawable/ic_open_in_new_black_24dp.xml b/app/src/main/res/drawable/ic_open_in_new_black_24dp.xml deleted file mode 100644 index 60b75a5..0000000 --- a/app/src/main/res/drawable/ic_open_in_new_black_24dp.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_tv_black_24dp.xml b/app/src/main/res/drawable/ic_tv_black_24dp.xml deleted file mode 100644 index 36269ad..0000000 --- a/app/src/main/res/drawable/ic_tv_black_24dp.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml index d1da4b2..339e141 100644 --- a/app/src/main/res/values/dimens.xml +++ b/app/src/main/res/values/dimens.xml @@ -1,8 +1,8 @@ - 16dp - 16dp + + 8dp - 176dp + 16dp diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index b4ecc71..4ff1e13 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -19,9 +19,9 @@ Settings Open navigation drawer Close navigation drawer - Emu48 for Android - android.studio@android.com - Navigation header + + + New... Open... Save diff --git a/build.gradle b/build.gradle index e59eddb..f540efe 100644 --- a/build.gradle +++ b/build.gradle @@ -7,7 +7,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:3.3.0' + classpath 'com.android.tools.build:gradle:3.3.1' // NOTE: Do not place your application dependencies here; they belong