[Scummvm-git-logs] scummvm master -> c85d146f58618bc81ec5173f6087c992e21dc105

sev- sev at scummvm.org
Fri Sep 27 00:23:19 CEST 2019


This automated email contains information about 10 new commits which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .

Summary:
bc1c4b3ec1 ANDROID: Initial code for external storage enumerator
4e5f26b44e ANDROID: Turn map into spliced list for easier marshalling
2b72cc0b5c ANDROID: Hook external storage class into ScummVMHelper
580142c9ea ANDROID: Hook getAllStorageLocations() into JNI
23211392c0 ANDROID: Use external storage enumerator for the root directory
f12d7160f8 ANDROID: Added list of external storages to the list of accessible directories
0481669b1f ANDROID: Fix exception
3c92722db6 ANDROID: Request permissions to external storage
27fbde1443 ANDROID: Added more logic for scraping the storage paths
c85d146f58 NEWS: Mention new Android functionality


Commit: bc1c4b3ec1893a25cb5f9e52185a19c9b330366e
    https://github.com/scummvm/scummvm/commit/bc1c4b3ec1893a25cb5f9e52185a19c9b330366e
Author: Eugene Sandulenko (sev at scummvm.org)
Date: 2019-09-27T00:22:20+02:00

Commit Message:
ANDROID: Initial code for external storage enumerator

Changed paths:
  A backends/platform/android/org/scummvm/scummvm/ExternalStorage.java


diff --git a/backends/platform/android/org/scummvm/scummvm/ExternalStorage.java b/backends/platform/android/org/scummvm/scummvm/ExternalStorage.java
new file mode 100644
index 0000000..42ec185
--- /dev/null
+++ b/backends/platform/android/org/scummvm/scummvm/ExternalStorage.java
@@ -0,0 +1,142 @@
+package org.scummvm.scummvm;
+
+import android.os.Environment;
+import java.io.File;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Scanner;
+
+/**
+ * Contains helper methods to get list of available media
+ */
+public class ExternalStorage {
+	public static final String SD_CARD = "sdCard";
+	public static final String EXTERNAL_SD_CARD = "externalSdCard";
+	public static final String DATA_DIRECTORY = "ScummVM data directory";
+
+	/**
+	 * @return True if the external storage is available. False otherwise.
+	 */
+	public static boolean isAvailable() {
+		String state = Environment.getExternalStorageState();
+		if (Environment.MEDIA_MOUNTED.equals(state) || Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
+			return true;
+		}
+		return false;
+	}
+
+	public static String getSdCardPath() {
+		return Environment.getExternalStorageDirectory().getPath() + "/";
+	}
+
+	/**
+	 * @return True if the external storage is writable. False otherwise.
+	 */
+	public static boolean isWritable() {
+		String state = Environment.getExternalStorageState();
+		if (Environment.MEDIA_MOUNTED.equals(state)) {
+			return true;
+		}
+		return false;
+
+	}
+
+	/**
+	 * @return A map of all storage locations available
+	 */
+	public static Map<String, String> getAllStorageLocations() {
+		Map<String, String> map = new HashMap<String, String>(10);
+
+		List<String> mMounts = new ArrayList<String>(10);
+		List<String> mVold = new ArrayList<String>(10);
+		mMounts.add("/mnt/sdcard");
+		mVold.add("/mnt/sdcard");
+
+		try {
+			File mountFile = new File("/proc/mounts");
+			if (mountFile.exists()){
+				Scanner scanner = new Scanner(mountFile);
+				while (scanner.hasNext()) {
+					String line = scanner.nextLine();
+					if (line.startsWith("/dev/block/vold/")) {
+						String[] lineElements = line.split(" ");
+						String element = lineElements[1];
+
+						// don't add the default mount path
+						// it's already in the list.
+						if (!element.equals("/mnt/sdcard"))
+							mMounts.add(element);
+					}
+				}
+			}
+		} catch (Exception e) {
+			e.printStackTrace();
+		}
+
+		try {
+			File voldFile = new File("/system/etc/vold.fstab");
+			if (voldFile.exists()){
+				Scanner scanner = new Scanner(voldFile);
+				while (scanner.hasNext()) {
+					String line = scanner.nextLine();
+					if (line.startsWith("dev_mount")) {
+						String[] lineElements = line.split(" ");
+						String element = lineElements[2];
+
+						if (element.contains(":"))
+							element = element.substring(0, element.indexOf(":"));
+						if (!element.equals("/mnt/sdcard"))
+							mVold.add(element);
+					}
+				}
+			}
+		} catch (Exception e) {
+			e.printStackTrace();
+		}
+
+
+		for (int i = 0; i < mMounts.size(); i++) {
+			String mount = mMounts.get(i);
+			if (!mVold.contains(mount))
+				mMounts.remove(i--);
+		}
+		mVold.clear();
+
+		List<String> mountHash = new ArrayList<String>(10);
+
+		for (String mount : mMounts){
+			File root = new File(mount);
+			if (root.exists() && root.isDirectory() && root.canRead()) {
+				File[] list = root.listFiles();
+				String hash = "[";
+				if (list != null) {
+					for (File f : list) {
+						hash += f.getName().hashCode() + ":" + f.length() + ", ";
+					}
+				}
+				hash += "]";
+				if (!mountHash.contains(hash)){
+					String key = SD_CARD + "_" + map.size();
+					if (map.size() == 0) {
+						key = SD_CARD;
+					} else if (map.size() == 1) {
+						key = EXTERNAL_SD_CARD;
+					}
+					mountHash.add(hash);
+					map.put(key, root.getAbsolutePath());
+				}
+			}
+		}
+
+		mMounts.clear();
+
+		map.put(SD_CARD, Environment.getDataDirectory().getAbsolutePath());
+
+		if (map.isEmpty()) {
+			map.put(DATA_DIRECTORY, Environment.getExternalStorageDirectory().getAbsolutePath());
+		}
+		return map;
+	}
+}


Commit: 4e5f26b44e9ef369641dd0825f17287b8cce8b08
    https://github.com/scummvm/scummvm/commit/4e5f26b44e9ef369641dd0825f17287b8cce8b08
Author: Eugene Sandulenko (sev at scummvm.org)
Date: 2019-09-27T00:22:20+02:00

Commit Message:
ANDROID: Turn map into spliced list for easier marshalling

Changed paths:
    backends/platform/android/org/scummvm/scummvm/ExternalStorage.java


diff --git a/backends/platform/android/org/scummvm/scummvm/ExternalStorage.java b/backends/platform/android/org/scummvm/scummvm/ExternalStorage.java
index 42ec185..234fba9 100644
--- a/backends/platform/android/org/scummvm/scummvm/ExternalStorage.java
+++ b/backends/platform/android/org/scummvm/scummvm/ExternalStorage.java
@@ -3,9 +3,7 @@ package org.scummvm.scummvm;
 import android.os.Environment;
 import java.io.File;
 import java.util.ArrayList;
-import java.util.HashMap;
 import java.util.List;
-import java.util.Map;
 import java.util.Scanner;
 
 /**
@@ -44,10 +42,10 @@ public class ExternalStorage {
 	}
 
 	/**
-	 * @return A map of all storage locations available
+	 * @return list of locations available. Odd elements are names, even are paths
 	 */
-	public static Map<String, String> getAllStorageLocations() {
-		Map<String, String> map = new HashMap<String, String>(10);
+	public static List<String> getAllStorageLocations() {
+		List<String> map = new ArrayList<String>(20);
 
 		List<String> mMounts = new ArrayList<String>(10);
 		List<String> mVold = new ArrayList<String>(10);
@@ -118,24 +116,27 @@ public class ExternalStorage {
 				}
 				hash += "]";
 				if (!mountHash.contains(hash)){
-					String key = SD_CARD + "_" + map.size();
+					String key = SD_CARD + "_" + (map.size() / 2);
 					if (map.size() == 0) {
 						key = SD_CARD;
-					} else if (map.size() == 1) {
+					} else if (map.size() == 2) {
 						key = EXTERNAL_SD_CARD;
 					}
 					mountHash.add(hash);
-					map.put(key, root.getAbsolutePath());
+					map.add(key);
+					map.add(root.getAbsolutePath());
 				}
 			}
 		}
 
 		mMounts.clear();
 
-		map.put(SD_CARD, Environment.getDataDirectory().getAbsolutePath());
+		map.add(DATA_DIRECTORY);
+		map.add(Environment.getDataDirectory().getAbsolutePath());
 
 		if (map.isEmpty()) {
-			map.put(DATA_DIRECTORY, Environment.getExternalStorageDirectory().getAbsolutePath());
+			map.add(SD_CARD);
+			map.add(Environment.getExternalStorageDirectory().getAbsolutePath());
 		}
 		return map;
 	}


Commit: 2b72cc0b5ce716a8163ef211887df5c9742983a5
    https://github.com/scummvm/scummvm/commit/2b72cc0b5ce716a8163ef211887df5c9742983a5
Author: Eugene Sandulenko (sev at scummvm.org)
Date: 2019-09-27T00:22:20+02:00

Commit Message:
ANDROID: Hook external storage class into ScummVMHelper

Changed paths:
    backends/platform/android/org/scummvm/scummvm/ScummVM.java
    backends/platform/android/org/scummvm/scummvm/ScummVMActivity.java


diff --git a/backends/platform/android/org/scummvm/scummvm/ScummVM.java b/backends/platform/android/org/scummvm/scummvm/ScummVM.java
index 8dd974b..ea7bf52 100644
--- a/backends/platform/android/org/scummvm/scummvm/ScummVM.java
+++ b/backends/platform/android/org/scummvm/scummvm/ScummVM.java
@@ -16,6 +16,7 @@ import javax.microedition.khronos.egl.EGLSurface;
 
 import java.io.File;
 import java.util.LinkedHashMap;
+import java.util.List;
 
 public abstract class ScummVM implements SurfaceHolder.Callback, Runnable {
 	final protected static String LOG_TAG = "ScummVM";
@@ -62,6 +63,7 @@ public abstract class ScummVM implements SurfaceHolder.Callback, Runnable {
 	abstract protected void showVirtualKeyboard(boolean enable);
 	abstract protected void showKeyboardControl(boolean enable);
 	abstract protected String[] getSysArchives();
+	abstract protected List<String> getAllStorageLocations();
 
 	public ScummVM(AssetManager asset_manager, SurfaceHolder holder) {
 		_asset_manager = asset_manager;
diff --git a/backends/platform/android/org/scummvm/scummvm/ScummVMActivity.java b/backends/platform/android/org/scummvm/scummvm/ScummVMActivity.java
index f353cea..a955f02 100644
--- a/backends/platform/android/org/scummvm/scummvm/ScummVMActivity.java
+++ b/backends/platform/android/org/scummvm/scummvm/ScummVMActivity.java
@@ -24,6 +24,7 @@ import android.widget.ImageView;
 import android.widget.Toast;
 
 import java.io.File;
+import java.util.List;
 
 public class ScummVMActivity extends Activity {
 
@@ -155,12 +156,18 @@ public class ScummVMActivity extends Activity {
 			return new String[0];
 		}
 
+		@Override
+		protected List<String> getAllStorageLocations() {
+			return _externalStorage.getAllStorageLocations();
+		}
+
 	}
 
 	private MyScummVM _scummvm;
 	private ScummVMEvents _events;
 	private MouseHelper _mouseHelper;
 	private Thread _scummvm_thread;
+	private ExternalStorage _externalStorage;
 
 	@Override
 	public void onCreate(Bundle savedInstanceState) {


Commit: 580142c9eaf7fafd4f15827545aaa7f4a1d03322
    https://github.com/scummvm/scummvm/commit/580142c9eaf7fafd4f15827545aaa7f4a1d03322
Author: Eugene Sandulenko (sev at scummvm.org)
Date: 2019-09-27T00:22:20+02:00

Commit Message:
ANDROID: Hook getAllStorageLocations() into JNI

Changed paths:
    backends/platform/android/jni.cpp
    backends/platform/android/jni.h
    backends/platform/android/org/scummvm/scummvm/ExternalStorage.java
    backends/platform/android/org/scummvm/scummvm/ScummVM.java
    backends/platform/android/org/scummvm/scummvm/ScummVMActivity.java


diff --git a/backends/platform/android/jni.cpp b/backends/platform/android/jni.cpp
index 2c27838..8c330c0 100644
--- a/backends/platform/android/jni.cpp
+++ b/backends/platform/android/jni.cpp
@@ -86,6 +86,7 @@ jmethodID JNI::_MID_setWindowCaption = 0;
 jmethodID JNI::_MID_showVirtualKeyboard = 0;
 jmethodID JNI::_MID_showKeyboardControl = 0;
 jmethodID JNI::_MID_getSysArchives = 0;
+jmethodID JNI::_MID_getAllStorageLocations = 0;
 jmethodID JNI::_MID_initSurface = 0;
 jmethodID JNI::_MID_deinitSurface = 0;
 
@@ -531,6 +532,7 @@ void JNI::create(JNIEnv *env, jobject self, jobject asset_manager,
 	FIND_METHOD(, showVirtualKeyboard, "(Z)V");
 	FIND_METHOD(, showKeyboardControl, "(Z)V");
 	FIND_METHOD(, getSysArchives, "()[Ljava/lang/String;");
+	FIND_METHOD(, getAllStorageLocations, "()[Ljava/lang/String;");
 	FIND_METHOD(, initSurface, "()Ljavax/microedition/khronos/egl/EGLSurface;");
 	FIND_METHOD(, deinitSurface, "()V");
 
@@ -697,4 +699,38 @@ jstring JNI::getCurrentCharset(JNIEnv *env, jobject self) {
 	return env->NewStringUTF("ISO-8859-1");
 }
 
+Common::Array<Common::String> JNI::getAllStorageLocations() {
+	Common::Array<Common::String> *res = new Common::Array<Common::String>();
+
+	JNIEnv *env = JNI::getEnv();
+
+	jobjectArray array =
+		(jobjectArray)env->CallObjectMethod(_jobj, _MID_getAllStorageLocations);
+
+	if (env->ExceptionCheck()) {
+		LOGE("Error finding system archive path");
+
+		env->ExceptionDescribe();
+		env->ExceptionClear();
+
+		return *res;
+	}
+
+	jsize size = env->GetArrayLength(array);
+	for (jsize i = 0; i < size; ++i) {
+		jstring path_obj = (jstring)env->GetObjectArrayElement(array, i);
+		const char *path = env->GetStringUTFChars(path_obj, 0);
+
+		if (path != 0) {
+			res->push_back(path);
+			env->ReleaseStringUTFChars(path_obj, path);
+		}
+
+		env->DeleteLocalRef(path_obj);
+	}
+
+	return *res;
+}
+
+
 #endif
diff --git a/backends/platform/android/jni.h b/backends/platform/android/jni.h
index b6e5df8..fe4b948 100644
--- a/backends/platform/android/jni.h
+++ b/backends/platform/android/jni.h
@@ -30,6 +30,7 @@
 
 #include "common/fs.h"
 #include "common/archive.h"
+#include "common/array.h"
 
 class OSystem_Android;
 
@@ -79,6 +80,8 @@ public:
 	static inline int writeAudio(JNIEnv *env, jbyteArray &data, int offset,
 									int size);
 
+	static Common::Array<Common::String> getAllStorageLocations();
+
 private:
 	static JavaVM *_vm;
 	// back pointer to (java) peer instance
@@ -104,6 +107,7 @@ private:
 	static jmethodID _MID_showVirtualKeyboard;
 	static jmethodID _MID_showKeyboardControl;
 	static jmethodID _MID_getSysArchives;
+	static jmethodID _MID_getAllStorageLocations;
 	static jmethodID _MID_initSurface;
 	static jmethodID _MID_deinitSurface;
 
diff --git a/backends/platform/android/org/scummvm/scummvm/ExternalStorage.java b/backends/platform/android/org/scummvm/scummvm/ExternalStorage.java
index 234fba9..d8bab31 100644
--- a/backends/platform/android/org/scummvm/scummvm/ExternalStorage.java
+++ b/backends/platform/android/org/scummvm/scummvm/ExternalStorage.java
@@ -104,7 +104,7 @@ public class ExternalStorage {
 
 		List<String> mountHash = new ArrayList<String>(10);
 
-		for (String mount : mMounts){
+		for (String mount : mMounts) {
 			File root = new File(mount);
 			if (root.exists() && root.isDirectory() && root.canRead()) {
 				File[] list = root.listFiles();
diff --git a/backends/platform/android/org/scummvm/scummvm/ScummVM.java b/backends/platform/android/org/scummvm/scummvm/ScummVM.java
index ea7bf52..34349d4 100644
--- a/backends/platform/android/org/scummvm/scummvm/ScummVM.java
+++ b/backends/platform/android/org/scummvm/scummvm/ScummVM.java
@@ -63,7 +63,7 @@ public abstract class ScummVM implements SurfaceHolder.Callback, Runnable {
 	abstract protected void showVirtualKeyboard(boolean enable);
 	abstract protected void showKeyboardControl(boolean enable);
 	abstract protected String[] getSysArchives();
-	abstract protected List<String> getAllStorageLocations();
+	abstract protected String[] getAllStorageLocations();
 
 	public ScummVM(AssetManager asset_manager, SurfaceHolder holder) {
 		_asset_manager = asset_manager;
diff --git a/backends/platform/android/org/scummvm/scummvm/ScummVMActivity.java b/backends/platform/android/org/scummvm/scummvm/ScummVMActivity.java
index a955f02..6978f6e 100644
--- a/backends/platform/android/org/scummvm/scummvm/ScummVMActivity.java
+++ b/backends/platform/android/org/scummvm/scummvm/ScummVMActivity.java
@@ -157,8 +157,8 @@ public class ScummVMActivity extends Activity {
 		}
 
 		@Override
-		protected List<String> getAllStorageLocations() {
-			return _externalStorage.getAllStorageLocations();
+		protected String[] getAllStorageLocations() {
+			return _externalStorage.getAllStorageLocations().toArray(new String[0]);
 		}
 
 	}


Commit: 23211392c0310179bf27612d7a7b58eec106b8ce
    https://github.com/scummvm/scummvm/commit/23211392c0310179bf27612d7a7b58eec106b8ce
Author: Eugene Sandulenko (sev at scummvm.org)
Date: 2019-09-27T00:22:20+02:00

Commit Message:
ANDROID: Use external storage enumerator for the root directory

Changed paths:
    backends/fs/posix/posix-fs.cpp


diff --git a/backends/fs/posix/posix-fs.cpp b/backends/fs/posix/posix-fs.cpp
index 507f075..fe72cfe 100644
--- a/backends/fs/posix/posix-fs.cpp
+++ b/backends/fs/posix/posix-fs.cpp
@@ -57,6 +57,9 @@
 #include <os2.h>
 #endif
 
+#if defined(__ANDROID__) && !defined(ANDROIDSDL)
+#include "backends/platform/android/jni.h"
+#endif
 
 bool POSIXFilesystemNode::exists() const {
 	return access(_path.c_str(), F_OK) == 0;
@@ -190,6 +193,23 @@ bool POSIXFilesystemNode::getChildren(AbstractFSList &myList, ListMode mode, boo
 	}
 #endif
 
+#if defined(__ANDROID__) && !defined(ANDROIDSDL)
+	if (_path == "/") {
+		Common::Array<Common::String> list = JNI::getAllStorageLocations();
+		for (Common::Array<Common::String>::const_iterator it = list.begin(), end = list.end(); it != end; ++it) {
+			POSIXFilesystemNode *entry = new POSIXFilesystemNode();
+
+			entry->_isDirectory = true;
+			entry->_isValid = true;
+			entry->_displayName = *it;
+			++it;
+			entry->_path = *it;
+			myList.push_back(entry);
+		}
+		return true;
+	}
+#endif
+
 	DIR *dirp = opendir(_path.c_str());
 	struct dirent *dp;
 


Commit: f12d7160f840eaa2607002cf0be2e3e8c1b17941
    https://github.com/scummvm/scummvm/commit/f12d7160f840eaa2607002cf0be2e3e8c1b17941
Author: Eugene Sandulenko (sev at scummvm.org)
Date: 2019-09-27T00:22:20+02:00

Commit Message:
ANDROID: Added list of external storages to the list of accessible directories

Changed paths:
    backends/platform/android/org/scummvm/scummvm/ExternalStorage.java


diff --git a/backends/platform/android/org/scummvm/scummvm/ExternalStorage.java b/backends/platform/android/org/scummvm/scummvm/ExternalStorage.java
index d8bab31..acfd239 100644
--- a/backends/platform/android/org/scummvm/scummvm/ExternalStorage.java
+++ b/backends/platform/android/org/scummvm/scummvm/ExternalStorage.java
@@ -115,7 +115,7 @@ public class ExternalStorage {
 					}
 				}
 				hash += "]";
-				if (!mountHash.contains(hash)){
+				if (!mountHash.contains(hash)) {
 					String key = SD_CARD + "_" + (map.size() / 2);
 					if (map.size() == 0) {
 						key = SD_CARD;
@@ -134,10 +134,38 @@ public class ExternalStorage {
 		map.add(DATA_DIRECTORY);
 		map.add(Environment.getDataDirectory().getAbsolutePath());
 
-		if (map.isEmpty()) {
-			map.add(SD_CARD);
-			map.add(Environment.getExternalStorageDirectory().getAbsolutePath());
+		// Now go through the external storage
+		String state = Environment.getExternalStorageState();
+
+		if (Environment.MEDIA_MOUNTED.equals(state) || Environment.MEDIA_MOUNTED_READ_ONLY.equals(state) ) {  // we can read the External Storage...
+			// Retrieve the primary External Storage:
+			File primaryExternalStorage = Environment.getExternalStorageDirectory();
+
+			//Retrieve the External Storages root directory:
+			String externalStorageRootDir;
+			int count = 0;
+			if ((externalStorageRootDir = primaryExternalStorage.getParent()) == null) {  // no parent...
+				String key = primaryExternalStorage.getAbsolutePath();
+				if (!map.contains(key)) {
+					map.add(key); // Make name as directory
+					map.add(key);
+				}
+			} else {
+				File externalStorageRoot = new File(externalStorageRootDir);
+				File[] files = externalStorageRoot.listFiles();
+
+				for (final File file : files) {
+					if (file.isDirectory() && file.canRead() && (file.listFiles().length > 0)) {  // it is a real directory (not a USB drive)...
+						String key = file.getAbsolutePath();
+						if (!map.contains(key)) {
+							map.add(key); // Make name as directory
+							map.add(key);
+						}
+					}
+				}
+			}
 		}
+
 		return map;
 	}
 }


Commit: 0481669b1f89f026786fb689169be9ff1f7b1466
    https://github.com/scummvm/scummvm/commit/0481669b1f89f026786fb689169be9ff1f7b1466
Author: Eugene Sandulenko (sev at scummvm.org)
Date: 2019-09-27T00:22:20+02:00

Commit Message:
ANDROID: Fix exception

Changed paths:
    backends/platform/android/org/scummvm/scummvm/ExternalStorage.java


diff --git a/backends/platform/android/org/scummvm/scummvm/ExternalStorage.java b/backends/platform/android/org/scummvm/scummvm/ExternalStorage.java
index acfd239..b9e7bf0 100644
--- a/backends/platform/android/org/scummvm/scummvm/ExternalStorage.java
+++ b/backends/platform/android/org/scummvm/scummvm/ExternalStorage.java
@@ -54,7 +54,7 @@ public class ExternalStorage {
 
 		try {
 			File mountFile = new File("/proc/mounts");
-			if (mountFile.exists()){
+			if (mountFile.exists()) {
 				Scanner scanner = new Scanner(mountFile);
 				while (scanner.hasNext()) {
 					String line = scanner.nextLine();
@@ -154,12 +154,14 @@ public class ExternalStorage {
 				File externalStorageRoot = new File(externalStorageRootDir);
 				File[] files = externalStorageRoot.listFiles();
 
-				for (final File file : files) {
-					if (file.isDirectory() && file.canRead() && (file.listFiles().length > 0)) {  // it is a real directory (not a USB drive)...
-						String key = file.getAbsolutePath();
-						if (!map.contains(key)) {
-							map.add(key); // Make name as directory
-							map.add(key);
+				if (files.length > 0) {
+					for (final File file : files) {
+						if (file.isDirectory() && file.canRead() && (file.listFiles().length > 0)) {  // it is a real directory (not a USB drive)...
+							String key = file.getAbsolutePath();
+							if (!map.contains(key)) {
+								map.add(key); // Make name as directory
+								map.add(key);
+							}
 						}
 					}
 				}


Commit: 3c92722db64e0e9af47b44e77a27371940f290f2
    https://github.com/scummvm/scummvm/commit/3c92722db64e0e9af47b44e77a27371940f290f2
Author: Eugene Sandulenko (sev at scummvm.org)
Date: 2019-09-27T00:22:20+02:00

Commit Message:
ANDROID: Request permissions to external storage

Changed paths:
    backends/platform/android/org/scummvm/scummvm/ScummVMActivity.java
    dists/android/AndroidManifest.xml


diff --git a/backends/platform/android/org/scummvm/scummvm/ScummVMActivity.java b/backends/platform/android/org/scummvm/scummvm/ScummVMActivity.java
index 6978f6e..2d48dfa 100644
--- a/backends/platform/android/org/scummvm/scummvm/ScummVMActivity.java
+++ b/backends/platform/android/org/scummvm/scummvm/ScummVMActivity.java
@@ -1,5 +1,7 @@
 package org.scummvm.scummvm;
 
+import android.Manifest;
+import android.content.pm.PackageManager;
 import android.app.Activity;
 import android.app.AlertDialog;
 import android.content.Context;
@@ -33,6 +35,11 @@ public class ScummVMActivity extends Activity {
 
 	private ClipboardManager _clipboard;
 
+	/**
+	* Id to identify an external storage read request.
+	*/
+	private static final int MY_PERMISSIONS_REQUEST_READ_EXT_STORAGE = 100; // is an app-defined int constant. The callback method gets the result of the request.
+
 	static {
 		try {
 			MouseHelper.checkHoverAvailable(); // this throws exception if we're on too old version
@@ -53,6 +60,7 @@ public class ScummVMActivity extends Activity {
 		}
 	};
 
+
 	private class MyScummVM extends ScummVM {
 		public MyScummVM(SurfaceHolder holder) {
 			super(ScummVMActivity.this.getAssets(), holder);
@@ -158,7 +166,14 @@ public class ScummVMActivity extends Activity {
 
 		@Override
 		protected String[] getAllStorageLocations() {
-			return _externalStorage.getAllStorageLocations().toArray(new String[0]);
+			if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
+			    && checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED
+			) {
+				requestPermissions(new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, MY_PERMISSIONS_REQUEST_READ_EXT_STORAGE);
+			} else {
+				return _externalStorage.getAllStorageLocations().toArray(new String[0]);
+			}
+			return new String[0]; // an array of zero length
 		}
 
 	}
@@ -306,6 +321,28 @@ public class ScummVMActivity extends Activity {
 		}
 	}
 
+
+	@Override
+	public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
+		switch (requestCode) {
+		case MY_PERMISSIONS_REQUEST_READ_EXT_STORAGE:
+			// If request is cancelled, the result arrays are empty.
+			if (grantResults.length > 0
+			   && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
+				// permission was granted
+				Log.i(ScummVM.LOG_TAG, "Read External Storage permission was granted at Runtime");
+			} else {
+				// permission denied! We won't be able to make use of functionality depending on this permission.
+				Toast.makeText(this, "Until permission is granted, some folders might not be listed!", Toast.LENGTH_SHORT)
+				              .show();
+			}
+			break;
+		default:
+			break;
+		}
+	}
+
+
 	@Override
 	public boolean onTrackballEvent(MotionEvent e) {
 		if (_events != null)
diff --git a/dists/android/AndroidManifest.xml b/dists/android/AndroidManifest.xml
index 923da5f..0d32c4b 100644
--- a/dists/android/AndroidManifest.xml
+++ b/dists/android/AndroidManifest.xml
@@ -14,6 +14,9 @@
 			android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
 
 	<uses-permission
+			android:name="android.permission.READ_EXTERNAL_STORAGE"/>
+
+	<uses-permission
 			android:name="android.permission.ACCESS_WIFI_STATE"/>
 
 	<uses-feature


Commit: 27fbde1443b8a53d39a43c4ff6c93b638c0944ab
    https://github.com/scummvm/scummvm/commit/27fbde1443b8a53d39a43c4ff6c93b638c0944ab
Author: Eugene Sandulenko (sev at scummvm.org)
Date: 2019-09-27T00:22:20+02:00

Commit Message:
ANDROID: Added more logic for scraping the storage paths

Changed paths:
    backends/platform/android/org/scummvm/scummvm/ExternalStorage.java


diff --git a/backends/platform/android/org/scummvm/scummvm/ExternalStorage.java b/backends/platform/android/org/scummvm/scummvm/ExternalStorage.java
index b9e7bf0..01482a3 100644
--- a/backends/platform/android/org/scummvm/scummvm/ExternalStorage.java
+++ b/backends/platform/android/org/scummvm/scummvm/ExternalStorage.java
@@ -6,6 +6,19 @@ import java.util.ArrayList;
 import java.util.List;
 import java.util.Scanner;
 
+import android.text.TextUtils;
+import java.io.BufferedReader;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.LinkedHashSet;
+import java.util.Locale;
+import java.util.regex.Pattern;
+import android.util.Log;
+import android.os.Build;
+
+
 /**
  * Contains helper methods to get list of available media
  */
@@ -14,6 +27,308 @@ public class ExternalStorage {
 	public static final String EXTERNAL_SD_CARD = "externalSdCard";
 	public static final String DATA_DIRECTORY = "ScummVM data directory";
 
+
+	// Find candidate removable sd card paths
+	// Code reference: https://stackoverflow.com/a/54411385
+	private static final String ANDROID_DIR = File.separator + "Android";
+
+	private static String ancestor(File dir) {
+		// getExternalFilesDir() and getExternalStorageDirectory()
+		// may return something app-specific like:
+		//   /storage/sdcard1/Android/data/com.mybackuparchives.android/files
+		// so we want the great-great-grandparent folder.
+		if (dir == null) {
+			return null;
+		} else {
+			String path = dir.getAbsolutePath();
+			int i = path.indexOf(ANDROID_DIR);
+			if (i == -1) {
+				return path;
+			} else {
+				return path.substring(0, i);
+			}
+		}
+	}
+
+	private static Pattern
+		/** Pattern that SD card device should match */
+		devicePattern = Pattern.compile("/dev/(block/.*vold.*|fuse)|/mnt/.*"),
+		/** Pattern that SD card mount path should match */
+		pathPattern = Pattern.compile("/(mnt|storage|external_sd|extsd|_ExternalSD|Removable|.*MicroSD).*", Pattern.CASE_INSENSITIVE),
+		/** Pattern that the mount path should not match.
+		 * 'emulated' indicates an internal storage location, so skip it.
+		 * 'asec' is an encrypted package file, decrypted and mounted as a directory. */
+		pathAntiPattern = Pattern.compile(".*(/secure|/asec|/emulated).*"),
+		/** These are expected fs types, including vfat. tmpfs is not OK.
+		 * fuse can be removable SD card (as on Moto E or Asus ZenPad), or can be internal (Huawei G610). */
+		fsTypePattern = Pattern.compile(".*(fat|msdos|ntfs|ext[34]|fuse|sdcard|esdfs).*");
+
+	/** Common paths for microSD card. **/
+	private static String[] commonPaths = {
+		// Some of these taken from
+		// https://stackoverflow.com/questions/13976982/removable-storage-external-sdcard-path-by-manufacturers
+		// These are roughly in order such that the earlier ones, if they exist, are more sure
+		// to be removable storage than the later ones.
+		"/mnt/Removable/MicroSD",
+		"/storage/removable/sdcard1", // !< Sony Xperia Z1
+		"/Removable/MicroSD", // Asus ZenPad C
+		"/removable/microsd",
+		"/external_sd", // Samsung
+		"/_ExternalSD", // some LGs
+		"/storage/extSdCard", // later Samsung
+		"/storage/extsdcard", // Main filesystem is case-sensitive; FAT isn't.
+		"/mnt/extsd", // some Chinese tablets, e.g. Zeki
+		"/storage/sdcard1", // If this exists it's more likely than sdcard0 to be removable.
+		"/mnt/extSdCard",
+		"/mnt/sdcard/external_sd",
+		"/mnt/external_sd",
+		"/storage/external_SD",
+		"/storage/ext_sd", // HTC One Max
+		"/mnt/sdcard/_ExternalSD",
+		"/mnt/sdcard-ext",
+
+		"/sdcard2", // HTC One M8s
+		"/sdcard1", // Sony Xperia Z
+		"/mnt/media_rw/sdcard1",   // 4.4.2 on CyanogenMod S3
+		"/mnt/sdcard", // This can be built-in storage (non-removable).
+		"/sdcard",
+		"/storage/sdcard0",
+		"/emmc",
+		"/mnt/emmc",
+		"/sdcard/sd",
+		"/mnt/sdcard/bpemmctest",
+		"/mnt/external1",
+		"/data/sdext4",
+		"/data/sdext3",
+		"/data/sdext2",
+		"/data/sdext",
+		"/storage/microsd" //ASUS ZenFone 2
+
+		// If we ever decide to support USB OTG storage, the following paths could be helpful:
+		// An LG Nexus 5 apparently uses usb://1002/UsbStorage/ as a URI to access an SD
+		// card over OTG cable. Other models, like Galaxy S5, use /storage/UsbDriveA
+		//        "/mnt/usb_storage",
+		//        "/mnt/UsbDriveA",
+		//        "/mnt/UsbDriveB",
+	};
+
+	/** Find path to removable SD card. */
+	public static LinkedHashSet<File> findSdCardPath() {
+		String[] mountFields;
+		BufferedReader bufferedReader = null;
+		String lineRead = null;
+
+		/** Possible SD card paths */
+		LinkedHashSet<File> candidatePaths = new LinkedHashSet<File>();
+
+		/** Build a list of candidate paths, roughly in order of preference. That way if
+		 * we can't definitively detect removable storage, we at least can pick a more likely
+		 * candidate. */
+
+		// Could do: use getExternalStorageState(File path), with and without an argument, when
+		// available. With an argument is available since API level 21.
+		// This may not be necessary, since we also check whether a directory exists and has contents,
+		// which would fail if the external storage state is neither MOUNTED nor MOUNTED_READ_ONLY.
+
+		// I moved hard-coded paths toward the end, but we need to make sure we put the ones in
+		// backwards order that are returned by the OS. And make sure the iterators respect
+		// the order!
+		// This is because when multiple "external" storage paths are returned, it's always (in
+		// experience, but not guaranteed by documentation) with internal/emulated storage
+		// first, removable storage second.
+
+		// Add value of environment variables as candidates, if set:
+		// EXTERNAL_STORAGE, SECONDARY_STORAGE, EXTERNAL_SDCARD_STORAGE
+		// But note they are *not* necessarily *removable* storage! Especially EXTERNAL_STORAGE.
+		// And they are not documented (API) features. Typically useful only for old versions of Android.
+
+		String val = System.getenv("SECONDARY_STORAGE");
+		if (!TextUtils.isEmpty(val)) {
+			addPath(val, candidatePaths);
+		}
+
+		val = System.getenv("EXTERNAL_SDCARD_STORAGE");
+		if (!TextUtils.isEmpty(val)) {
+			addPath(val, candidatePaths);
+		}
+
+		// Get listing of mounted devices with their properties.
+		ArrayList<File> mountedPaths = new ArrayList<File>();
+		try {
+			// Note: Despite restricting some access to /proc (http://stackoverflow.com/a/38728738/423105),
+			// Android 7.0 does *not* block access to /proc/mounts, according to our test on George's Alcatel A30 GSM.
+			bufferedReader = new BufferedReader(new FileReader("/proc/mounts"));
+
+			// Iterate over each line of the mounts listing.
+			while ((lineRead = bufferedReader.readLine()) != null) {
+//				Log.d(ScummVM.LOG_TAG, "\nMounts line: " + lineRead);
+				mountFields = lineRead.split(" ");
+
+				// columns: device, mountpoint, fs type, options... Example:
+				// /dev/block/vold/179:97 /storage/sdcard1 vfat rw,dirsync,nosuid,nodev,noexec,relatime,uid=1000,gid=1015,fmask=0002,dmask=0002,allow_utime=0020,codepage=cp437,iocharset=iso8859-1,shortname=mixed,utf8,errors=remount-ro 0 0
+				String device = mountFields[0], path = mountFields[1], fsType = mountFields[2];
+
+				// The device, path, and fs type must conform to expected patterns.
+				// mtdblock is internal, I'm told.
+				// Check for disqualifying patterns in the path.
+				// If this mounts line fails our tests, skip it.
+				if (!(devicePattern.matcher(device).matches()
+						&& pathPattern.matcher(path).matches()
+						&& fsTypePattern.matcher(fsType).matches())
+					|| device.contains("mtdblock")
+					|| pathAntiPattern.matcher(path).matches()
+				) {
+					continue;
+				}
+
+				// TODO maybe: check options to make sure it's mounted RW?
+				// The answer at http://stackoverflow.com/a/13648873/423105 does.
+				// But it hasn't seemed to be necessary so far in my testing.
+
+				// This line met the criteria so far, so add it to candidate list.
+				addPath(path, mountedPaths);
+			}
+		} catch (IOException ignored) { }
+		finally {
+			if (bufferedReader != null) {
+				try {
+					bufferedReader.close();
+				} catch (IOException ignored) { }
+			}
+		}
+
+		// Append the paths from mount table to candidate list, in reverse order.
+		if (!mountedPaths.isEmpty()) {
+			// See https://stackoverflow.com/a/5374346/423105 on why the following is necessary.
+			// Basically, .toArray() needs its parameter to know what type of array to return.
+			File[] mountedPathsArray = mountedPaths.toArray(new File[mountedPaths.size()]);
+			addAncestors(mountedPathsArray, candidatePaths);
+		}
+
+		// Add hard-coded known common paths to candidate list:
+		addStrings(commonPaths, candidatePaths);
+
+		// If the above doesn't work we could try the following other options, but in my experience they
+		// haven't added anything helpful yet.
+
+		// getExternalFilesDir() and getExternalStorageDirectory() typically something app-specific like
+		//   /storage/sdcard1/Android/data/com.mybackuparchives.android/files
+		// so we want the great-great-grandparent folder.
+
+		// This may be non-removable.
+		Log.d(ScummVM.LOG_TAG, "Environment.getExternalStorageDirectory():");
+		addPath(ancestor(Environment.getExternalStorageDirectory()), candidatePaths);
+
+		// TODO maybe: use getExternalStorageState(File path), with and without an argument, when
+		// available. With an argument is available since API level 21.
+		// This may not be necessary, since we also check whether a directory exists,
+		// which would fail if the external storage state is neither MOUNTED nor MOUNTED_READ_ONLY.
+
+		// A "public" external storage directory. But in my experience it doesn't add anything helpful.
+		// Note that you can't pass null, or you'll get an NPE.
+		final File publicDirectory = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MUSIC);
+		// Take the parent, because we tend to get a path like /pathTo/sdCard/Music.
+		addPath(publicDirectory.getParentFile().getAbsolutePath(), candidatePaths);
+		// EXTERNAL_STORAGE: may not be removable.
+		val = System.getenv("EXTERNAL_STORAGE");
+		if (!TextUtils.isEmpty(val)) {
+			addPath(val, candidatePaths);
+		}
+
+		if (candidatePaths.isEmpty()) {
+			Log.w(ScummVM.LOG_TAG, "No removable microSD card found.");
+			return candidatePaths;
+		} else {
+			Log.i(ScummVM.LOG_TAG, "\nFound potential removable storage locations: " + candidatePaths);
+		}
+
+		// Accept or eliminate candidate paths if we can determine whether they're removable storage.
+		// In Lollipop and later, we can check isExternalStorageRemovable() status on each candidate.
+		if (Build.VERSION.SDK_INT >= 21) {
+			Iterator<File> itf = candidatePaths.iterator();
+			while (itf.hasNext()) {
+				File dir = itf.next();
+				// handle illegalArgumentException if the path is not a valid storage device.
+				try {
+					if (Environment.isExternalStorageRemovable(dir)) {
+						Log.i(ScummVM.LOG_TAG, dir.getPath() + " is removable external storage");
+						addPath(dir.getAbsolutePath(), candidatePaths);
+					} else if (Environment.isExternalStorageEmulated(dir)) {
+						Log.d(ScummVM.LOG_TAG, "Removing emulated external storage dir " + dir);
+						itf.remove();
+					}
+				} catch (IllegalArgumentException e) {
+					Log.d(ScummVM.LOG_TAG, "isRemovable(" + dir.getPath() + "): not a valid storage device.", e);
+				}
+			}
+		}
+
+		// Continue trying to accept or eliminate candidate paths based on whether they're removable storage.
+		// On pre-Lollipop, we only have singular externalStorage. Check whether it's removable.
+		if (Build.VERSION.SDK_INT >= 9) {
+			File externalStorage = Environment.getExternalStorageDirectory();
+			Log.d(ScummVM.LOG_TAG, String.format(Locale.ROOT, "findSDCardPath: getExternalStorageDirectory = %s", externalStorage.getPath()));
+			if (Environment.isExternalStorageRemovable()) {
+				// Make sure this is a candidate.
+				// TODO: Does this contains() work? Should we be canonicalizing paths before comparing?
+				if (candidatePaths.contains(externalStorage)) {
+					Log.d(ScummVM.LOG_TAG, "Using externalStorage dir " + externalStorage);
+					// return externalStorage;
+					addPath(externalStorage.getAbsolutePath(), candidatePaths);
+				}
+			} else if (Build.VERSION.SDK_INT >= 11 && Environment.isExternalStorageEmulated()) {
+				Log.d(ScummVM.LOG_TAG, "Removing emulated external storage dir " + externalStorage);
+				candidatePaths.remove(externalStorage);
+			}
+		}
+
+		return candidatePaths;
+	}
+
+
+	/** Add each path to the collection. */
+	private static void addStrings(String[] newPaths, LinkedHashSet<File> candidatePaths) {
+		for (String path : newPaths) {
+			addPath(path, candidatePaths);
+		}
+	}
+
+	/** Add ancestor of each File to the collection. */
+	private static void addAncestors(File[] files, LinkedHashSet<File> candidatePaths) {
+		for (int i = files.length - 1; i >= 0; i--) {
+			addPath(ancestor(files[i]), candidatePaths);
+		}
+	}
+
+	/**
+	 * Add a new candidate directory path to our list, if it's not obviously wrong.
+	 * Supply path as either String or File object.
+	 * @param strNew path of directory to add
+	 */
+	private static void addPath(String strNew, Collection<File> paths) {
+		// If one of the arguments is null, fill it in from the other.
+		if (strNew != null && !strNew.isEmpty()) {
+			File fileNew = new File(strNew);
+
+			if (!paths.contains(fileNew) &&
+				// Check for paths known not to be removable SD card.
+				// The antipattern check can be redundant, depending on where this is called from.
+				!pathAntiPattern.matcher(strNew).matches()) {
+
+				// Eliminate candidate if not a directory or not fully accessible.
+				if (fileNew.exists() && fileNew.isDirectory() && fileNew.canExecute()) {
+					Log.d(ScummVM.LOG_TAG, "  Adding candidate path " + strNew);
+					paths.add(fileNew);
+				} else {
+					Log.d(ScummVM.LOG_TAG, String.format(Locale.ROOT, "  Invalid path %s: exists: %b isDir: %b canExec: %b canRead: %b",
+					      strNew, fileNew.exists(), fileNew.isDirectory(), fileNew.canExecute(), fileNew.canRead()));
+				}
+			}
+		}
+	}
+
+
+
 	/**
 	 * @return True if the external storage is available. False otherwise.
 	 */
@@ -135,9 +450,7 @@ public class ExternalStorage {
 		map.add(Environment.getDataDirectory().getAbsolutePath());
 
 		// Now go through the external storage
-		String state = Environment.getExternalStorageState();
-
-		if (Environment.MEDIA_MOUNTED.equals(state) || Environment.MEDIA_MOUNTED_READ_ONLY.equals(state) ) {  // we can read the External Storage...
+		if (isAvailable()) {  // we can read the External Storage...
 			// Retrieve the primary External Storage:
 			File primaryExternalStorage = Environment.getExternalStorageDirectory();
 
@@ -154,7 +467,7 @@ public class ExternalStorage {
 				File externalStorageRoot = new File(externalStorageRootDir);
 				File[] files = externalStorageRoot.listFiles();
 
-				if (files.length > 0) {
+				if (files != null) {
 					for (final File file : files) {
 						if (file.isDirectory() && file.canRead() && (file.listFiles().length > 0)) {  // it is a real directory (not a USB drive)...
 							String key = file.getAbsolutePath();
@@ -168,6 +481,16 @@ public class ExternalStorage {
 			}
 		}
 
+		// Get candidates for removable external storage
+		LinkedHashSet<File> candidateRemovableSdCardPaths = findSdCardPath();
+		for (final File file : candidateRemovableSdCardPaths) {
+			String key = file.getAbsolutePath();
+			if (!map.contains(key)) {
+				map.add(key); // Make name as directory
+				map.add(key);
+			}
+		}
+
 		return map;
 	}
 }


Commit: c85d146f58618bc81ec5173f6087c992e21dc105
    https://github.com/scummvm/scummvm/commit/c85d146f58618bc81ec5173f6087c992e21dc105
Author: Eugene Sandulenko (sev at scummvm.org)
Date: 2019-09-27T00:22:20+02:00

Commit Message:
NEWS: Mention new Android functionality

Changed paths:
    NEWS.md


diff --git a/NEWS.md b/NEWS.md
index bf6a571..5a77eb7 100644
--- a/NEWS.md
+++ b/NEWS.md
@@ -134,6 +134,7 @@ For a more comprehensive changelog of the latest experimental code, see:
    - Added a button to show the virtual keyboard.
    - Implemented clipboard support.
    - Use the dedicated GUI option for enabling the touchpad mode
+   - Added code for searching accessible external media.
 
  iOS port:
    - Added support for Smart Keyboard.





More information about the Scummvm-git-logs mailing list