Pārlūkot izejas kodu

Merge pull request #50360 from m4gr3d/address_external_dir_access_master

Add support for Android scoped storage
Rémi Verschelde 4 gadi atpakaļ
vecāks
revīzija
be80d381d2

+ 3 - 8
core/core_bind.cpp

@@ -456,10 +456,6 @@ String _OS::get_user_data_dir() const {
 	return OS::get_singleton()->get_user_data_dir();
 }
 
-String _OS::get_external_data_dir() const {
-	return OS::get_singleton()->get_external_data_dir();
-}
-
 String _OS::get_config_dir() const {
 	// Exposed as `get_config_dir()` instead of `get_config_path()` for consistency with other exposed OS methods.
 	return OS::get_singleton()->get_config_path();
@@ -483,8 +479,8 @@ bool _OS::is_debug_build() const {
 #endif
 }
 
-String _OS::get_system_dir(SystemDir p_dir) const {
-	return OS::get_singleton()->get_system_dir(OS::SystemDir(p_dir));
+String _OS::get_system_dir(SystemDir p_dir, bool p_shared_storage) const {
+	return OS::get_singleton()->get_system_dir(OS::SystemDir(p_dir), p_shared_storage);
 }
 
 String _OS::get_keycode_string(uint32_t p_code) const {
@@ -567,8 +563,7 @@ void _OS::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("get_static_memory_peak_usage"), &_OS::get_static_memory_peak_usage);
 
 	ClassDB::bind_method(D_METHOD("get_user_data_dir"), &_OS::get_user_data_dir);
-	ClassDB::bind_method(D_METHOD("get_external_data_dir"), &_OS::get_external_data_dir);
-	ClassDB::bind_method(D_METHOD("get_system_dir", "dir"), &_OS::get_system_dir);
+	ClassDB::bind_method(D_METHOD("get_system_dir", "dir", "shared_storage"), &_OS::get_system_dir, DEFVAL(true));
 	ClassDB::bind_method(D_METHOD("get_config_dir"), &_OS::get_config_dir);
 	ClassDB::bind_method(D_METHOD("get_data_dir"), &_OS::get_data_dir);
 	ClassDB::bind_method(D_METHOD("get_cache_dir"), &_OS::get_cache_dir);

+ 1 - 2
core/core_bind.h

@@ -229,10 +229,9 @@ public:
 		SYSTEM_DIR_RINGTONES,
 	};
 
-	String get_system_dir(SystemDir p_dir) const;
+	String get_system_dir(SystemDir p_dir, bool p_shared_storage = true) const;
 
 	String get_user_data_dir() const;
-	String get_external_data_dir() const;
 	String get_config_dir() const;
 	String get_data_dir() const;
 	String get_cache_dir() const;

+ 1 - 6
core/os/os.cpp

@@ -277,18 +277,13 @@ String OS::get_user_data_dir() const {
 	return ".";
 }
 
-// Android OS path to app's external data storage
-String OS::get_external_data_dir() const {
-	return get_user_data_dir();
-};
-
 // Absolute path to res://
 String OS::get_resource_dir() const {
 	return ProjectSettings::get_singleton()->get_resource_path();
 }
 
 // Access system-specific dirs like Documents, Downloads, etc.
-String OS::get_system_dir(SystemDir p_dir) const {
+String OS::get_system_dir(SystemDir p_dir, bool p_shared_storage) const {
 	return ".";
 }
 

+ 1 - 2
core/os/os.h

@@ -250,7 +250,6 @@ public:
 	virtual String get_bundle_resource_dir() const;
 
 	virtual String get_user_data_dir() const;
-	virtual String get_external_data_dir() const;
 	virtual String get_resource_dir() const;
 
 	enum SystemDir {
@@ -264,7 +263,7 @@ public:
 		SYSTEM_DIR_RINGTONES,
 	};
 
-	virtual String get_system_dir(SystemDir p_dir) const;
+	virtual String get_system_dir(SystemDir p_dir, bool p_shared_storage = true) const;
 
 	virtual Error move_to_trash(const String &p_path) { return FAILED; }
 

+ 2 - 6
doc/classes/OS.xml

@@ -197,12 +197,6 @@
 				Returns the path to the current engine executable.
 			</description>
 		</method>
-		<method name="get_external_data_dir" qualifiers="const">
-			<return type="String" />
-			<description>
-				On Android, returns the absolute directory path where user data can be written to external storage if available. On all other platforms, this will return the same location as [method get_user_data_dir].
-			</description>
-		</method>
 		<method name="get_granted_permissions" qualifiers="const">
 			<return type="PackedStringArray" />
 			<description>
@@ -270,9 +264,11 @@
 		<method name="get_system_dir" qualifiers="const">
 			<return type="String" />
 			<argument index="0" name="dir" type="int" enum="OS.SystemDir" />
+			<argument index="1" name="shared_storage" type="bool" default="true" />
 			<description>
 				Returns the actual path to commonly used folders across different platforms. Available locations are specified in [enum SystemDir].
 				[b]Note:[/b] This method is implemented on Android, Linux, macOS and Windows.
+				[b]Note:[/b] Shared storage is implemented on Android and allows to differentiate between app specific and shared directories. Shared directories have additional restrictions on Android.
 			</description>
 		</method>
 		<method name="get_thread_caller_id" qualifiers="const">

+ 16 - 2
platform/android/export/export_plugin.cpp

@@ -717,6 +717,10 @@ Error EditorExportPlatformAndroid::copy_gradle_so(void *p_userdata, const Shared
 	return OK;
 }
 
+bool EditorExportPlatformAndroid::_has_storage_permission(const Vector<String> &p_permissions) {
+	return p_permissions.find("android.permission.READ_EXTERNAL_STORAGE") != -1 || p_permissions.find("android.permission.WRITE_EXTERNAL_STORAGE") != -1;
+}
+
 void EditorExportPlatformAndroid::_get_permissions(const Ref<EditorExportPreset> &p_preset, bool p_give_internet, Vector<String> &r_permissions) {
 	const char **aperms = android_perms;
 	while (*aperms) {
@@ -763,12 +767,17 @@ void EditorExportPlatformAndroid::_write_tmp_manifest(const Ref<EditorExportPres
 	Vector<String> perms;
 	_get_permissions(p_preset, p_give_internet, perms);
 	for (int i = 0; i < perms.size(); i++) {
-		manifest_text += vformat("    <uses-permission android:name=\"%s\" />\n", perms.get(i));
+		String permission = perms.get(i);
+		if (permission == "android.permission.WRITE_EXTERNAL_STORAGE" || permission == "android.permission.READ_EXTERNAL_STORAGE") {
+			manifest_text += vformat("    <uses-permission android:name=\"%s\" android:maxSdkVersion=\"29\" />\n", permission);
+		} else {
+			manifest_text += vformat("    <uses-permission android:name=\"%s\" />\n", permission);
+		}
 	}
 
 	manifest_text += _get_xr_features_tag(p_preset);
 	manifest_text += _get_instrumentation_tag(p_preset);
-	manifest_text += _get_application_tag(p_preset);
+	manifest_text += _get_application_tag(p_preset, _has_storage_permission(perms));
 	manifest_text += "</manifest>\n";
 	String manifest_path = vformat("res://android/build/src/%s/AndroidManifest.xml", (p_debug ? "debug" : "release"));
 
@@ -824,6 +833,7 @@ void EditorExportPlatformAndroid::_fix_manifest(const Ref<EditorExportPreset> &p
 	Vector<String> perms;
 	// Write permissions into the perms variable.
 	_get_permissions(p_preset, p_give_internet, perms);
+	bool has_storage_permission = _has_storage_permission(perms);
 
 	while (ofs < (uint32_t)p_manifest.size()) {
 		uint32_t chunk = decode_uint32(&p_manifest[ofs]);
@@ -913,6 +923,10 @@ void EditorExportPlatformAndroid::_fix_manifest(const Ref<EditorExportPreset> &p
 						}
 					}
 
+					if (tname == "application" && attrname == "requestLegacyExternalStorage") {
+						encode_uint32(has_storage_permission ? 0xFFFFFFFF : 0, &p_manifest.write[iofs + 16]);
+					}
+
 					if (tname == "application" && attrname == "allowBackup") {
 						encode_uint32(backup_allowed, &p_manifest.write[iofs + 16]);
 					}

+ 2 - 0
platform/android/export/export_plugin.h

@@ -134,6 +134,8 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
 
 	static Error copy_gradle_so(void *p_userdata, const SharedObject &p_so);
 
+	bool _has_storage_permission(const Vector<String> &p_permissions);
+
 	void _get_permissions(const Ref<EditorExportPreset> &p_preset, bool p_give_internet, Vector<String> &r_permissions);
 
 	void _write_tmp_manifest(const Ref<EditorExportPreset> &p_preset, bool p_give_internet, bool p_debug);

+ 5 - 3
platform/android/export/gradle_export_util.cpp

@@ -235,18 +235,20 @@ String _get_activity_tag(const Ref<EditorExportPreset> &p_preset) {
 	return manifest_activity_text;
 }
 
-String _get_application_tag(const Ref<EditorExportPreset> &p_preset) {
+String _get_application_tag(const Ref<EditorExportPreset> &p_preset, bool p_has_storage_permission) {
 	String manifest_application_text = vformat(
 			"    <application android:label=\"@string/godot_project_name_string\"\n"
 			"        android:allowBackup=\"%s\"\n"
 			"        android:icon=\"@mipmap/icon\"\n"
 			"        android:isGame=\"%s\"\n"
 			"        android:hasFragileUserData=\"%s\"\n"
-			"        tools:replace=\"android:allowBackup,android:isGame,android:hasFragileUserData\"\n"
+			"        android:requestLegacyExternalStorage=\"%s\"\n"
+			"        tools:replace=\"android:allowBackup,android:isGame,android:hasFragileUserData,android:requestLegacyExternalStorage\"\n"
 			"        tools:ignore=\"GoogleAppIndexingWarning\">\n\n",
 			bool_to_string(p_preset->get("user_data_backup/allow")),
 			bool_to_string(p_preset->get("package/classify_as_game")),
-			bool_to_string(p_preset->get("package/retain_data_on_uninstall")));
+			bool_to_string(p_preset->get("package/retain_data_on_uninstall")),
+			bool_to_string(p_has_storage_permission));
 
 	manifest_application_text += _get_activity_tag(p_preset);
 	manifest_application_text += "    </application>\n";

+ 1 - 1
platform/android/export/gradle_export_util.h

@@ -81,6 +81,6 @@ String _get_instrumentation_tag(const Ref<EditorExportPreset> &p_preset);
 
 String _get_activity_tag(const Ref<EditorExportPreset> &p_preset);
 
-String _get_application_tag(const Ref<EditorExportPreset> &p_preset);
+String _get_application_tag(const Ref<EditorExportPreset> &p_preset, bool p_has_storage_permission);
 
 #endif //GODOT_GRADLE_EXPORT_UTIL_H

+ 1 - 0
platform/android/java/app/AndroidManifest.xml

@@ -22,6 +22,7 @@
         android:icon="@mipmap/icon"
         android:isGame="true"
         android:hasFragileUserData="false"
+        android:requestLegacyExternalStorage="false"
         tools:ignore="GoogleAppIndexingWarning" >
 
         <!-- Records the version of the Godot editor used for building -->

+ 4 - 4
platform/android/java/app/config.gradle

@@ -1,8 +1,8 @@
 ext.versions = [
-    androidGradlePlugin: '4.2.1',
-    compileSdk         : 29,
-    minSdk             : 18,
-    targetSdk          : 29,
+    androidGradlePlugin: '4.2.2',
+    compileSdk         : 30,
+    minSdk             : 19,
+    targetSdk          : 30,
     buildTools         : '30.0.3',
     supportCoreUtils   : '1.0.0',
     kotlinVersion      : '1.5.10',

+ 0 - 5
platform/android/java/lib/src/org/godotengine/godot/Godot.java

@@ -49,7 +49,6 @@ import android.content.ClipData;
 import android.content.ClipboardManager;
 import android.content.ComponentName;
 import android.content.Context;
-import android.content.DialogInterface;
 import android.content.Intent;
 import android.content.SharedPreferences;
 import android.content.SharedPreferences.Editor;
@@ -68,15 +67,12 @@ import android.os.Environment;
 import android.os.Messenger;
 import android.os.VibrationEffect;
 import android.os.Vibrator;
-import android.provider.Settings.Secure;
 import android.view.Display;
-import android.view.KeyEvent;
 import android.view.LayoutInflater;
 import android.view.Surface;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.ViewGroup.LayoutParams;
-import android.view.ViewTreeObserver;
 import android.view.Window;
 import android.view.WindowManager;
 import android.widget.Button;
@@ -471,7 +467,6 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC
 
 		final Activity activity = getActivity();
 		io = new GodotIO(activity);
-		io.unique_id = Secure.getString(activity.getContentResolver(), Secure.ANDROID_ID);
 		GodotLib.io = io;
 		netUtils = new GodotNetUtils(activity);
 		mSensorManager = (SensorManager)activity.getSystemService(Context.SENSOR_SERVICE);

+ 51 - 196
platform/android/java/lib/src/org/godotengine/godot/GodotIO.java

@@ -30,15 +30,19 @@
 
 package org.godotengine.godot;
 
-import org.godotengine.godot.input.*;
+import org.godotengine.godot.input.GodotEditText;
 
 import android.app.Activity;
-import android.content.*;
+import android.content.ActivityNotFoundException;
+import android.content.Intent;
 import android.content.pm.ActivityInfo;
 import android.content.res.AssetManager;
 import android.graphics.Point;
 import android.net.Uri;
-import android.os.*;
+import android.os.Build;
+import android.os.Environment;
+import android.provider.Settings;
+import android.text.TextUtils;
 import android.util.DisplayMetrics;
 import android.util.Log;
 import android.util.SparseArray;
@@ -47,14 +51,16 @@ import android.view.DisplayCutout;
 import android.view.WindowInsets;
 
 import java.io.IOException;
-import java.io.InputStream;
 import java.util.Locale;
 
 // Wrapper for native library
 
 public class GodotIO {
-	AssetManager am;
-	final Activity activity;
+	private static final String TAG = GodotIO.class.getSimpleName();
+
+	private final AssetManager am;
+	private final Activity activity;
+	private final String uniqueId;
 	GodotEditText edit;
 
 	final int SCREEN_LANDSCAPE = 0;
@@ -65,167 +71,6 @@ public class GodotIO {
 	final int SCREEN_SENSOR_PORTRAIT = 5;
 	final int SCREEN_SENSOR = 6;
 
-	/////////////////////////
-	/// FILES
-	/////////////////////////
-
-	public int last_file_id = 1;
-
-	static class AssetData {
-		public boolean eof = false;
-		public String path;
-		public InputStream is;
-		public int len;
-		public int pos;
-	}
-
-	SparseArray<AssetData> streams;
-
-	public int file_open(String path, boolean write) {
-		//System.out.printf("file_open: Attempt to Open %s\n",path);
-
-		//Log.v("MyApp", "TRYING TO OPEN FILE: " + path);
-		if (write)
-			return -1;
-
-		AssetData ad = new AssetData();
-
-		try {
-			ad.is = am.open(path);
-
-		} catch (Exception e) {
-			//System.out.printf("Exception on file_open: %s\n",path);
-			return -1;
-		}
-
-		try {
-			ad.len = ad.is.available();
-		} catch (Exception e) {
-			System.out.printf("Exception availabling on file_open: %s\n", path);
-			return -1;
-		}
-
-		ad.path = path;
-		ad.pos = 0;
-		++last_file_id;
-		streams.put(last_file_id, ad);
-
-		return last_file_id;
-	}
-	public int file_get_size(int id) {
-		if (streams.get(id) == null) {
-			System.out.printf("file_get_size: Invalid file id: %d\n", id);
-			return -1;
-		}
-
-		return streams.get(id).len;
-	}
-	public void file_seek(int id, int bytes) {
-		if (streams.get(id) == null) {
-			System.out.printf("file_get_size: Invalid file id: %d\n", id);
-			return;
-		}
-		//seek sucks
-		AssetData ad = streams.get(id);
-		if (bytes > ad.len)
-			bytes = ad.len;
-		if (bytes < 0)
-			bytes = 0;
-
-		try {
-			if (bytes > (int)ad.pos) {
-				int todo = bytes - (int)ad.pos;
-				while (todo > 0) {
-					todo -= ad.is.skip(todo);
-				}
-				ad.pos = bytes;
-			} else if (bytes < (int)ad.pos) {
-				ad.is = am.open(ad.path);
-
-				ad.pos = bytes;
-				int todo = bytes;
-				while (todo > 0) {
-					todo -= ad.is.skip(todo);
-				}
-			}
-
-			ad.eof = false;
-		} catch (IOException e) {
-			System.out.printf("Exception on file_seek: %s\n", e);
-			return;
-		}
-	}
-
-	public int file_tell(int id) {
-		if (streams.get(id) == null) {
-			System.out.printf("file_read: Can't tell eof for invalid file id: %d\n", id);
-			return 0;
-		}
-
-		AssetData ad = streams.get(id);
-		return ad.pos;
-	}
-	public boolean file_eof(int id) {
-		if (streams.get(id) == null) {
-			System.out.printf("file_read: Can't check eof for invalid file id: %d\n", id);
-			return false;
-		}
-
-		AssetData ad = streams.get(id);
-		return ad.eof;
-	}
-
-	public byte[] file_read(int id, int bytes) {
-		if (streams.get(id) == null) {
-			System.out.printf("file_read: Can't read invalid file id: %d\n", id);
-			return new byte[0];
-		}
-
-		AssetData ad = streams.get(id);
-
-		if (ad.pos + bytes > ad.len) {
-			bytes = ad.len - ad.pos;
-			ad.eof = true;
-		}
-
-		if (bytes == 0) {
-			return new byte[0];
-		}
-
-		byte[] buf1 = new byte[bytes];
-		int r = 0;
-		try {
-			r = ad.is.read(buf1);
-		} catch (IOException e) {
-			System.out.printf("Exception on file_read: %s\n", e);
-			return new byte[bytes];
-		}
-
-		if (r == 0) {
-			return new byte[0];
-		}
-
-		ad.pos += r;
-
-		if (r < bytes) {
-			byte[] buf2 = new byte[r];
-			for (int i = 0; i < r; i++)
-				buf2[i] = buf1[i];
-			return buf2;
-		} else {
-			return buf1;
-		}
-	}
-
-	public void file_close(int id) {
-		if (streams.get(id) == null) {
-			System.out.printf("file_close: Can't close invalid file id: %d\n", id);
-			return;
-		}
-
-		streams.remove(id);
-	}
-
 	/////////////////////////
 	/// DIRECTORIES
 	/////////////////////////
@@ -236,9 +81,9 @@ public class GodotIO {
 		public String path;
 	}
 
-	public int last_dir_id = 1;
+	private int last_dir_id = 1;
 
-	SparseArray<AssetDir> dirs;
+	private final SparseArray<AssetDir> dirs;
 
 	public int dir_open(String path) {
 		AssetDir ad = new AssetDir();
@@ -257,7 +102,6 @@ public class GodotIO {
 			return -1;
 		}
 
-		//System.out.printf("Opened dir: %s\n",path);
 		++last_dir_id;
 		dirs.put(last_dir_id, ad);
 
@@ -320,9 +164,14 @@ public class GodotIO {
 	GodotIO(Activity p_activity) {
 		am = p_activity.getAssets();
 		activity = p_activity;
-		//streams = new HashMap<Integer, AssetData>();
-		streams = new SparseArray<>();
 		dirs = new SparseArray<>();
+		String androidId = Settings.Secure.getString(activity.getContentResolver(),
+				Settings.Secure.ANDROID_ID);
+		if (androidId == null) {
+			androidId = "";
+		}
+
+		uniqueId = androidId;
 	}
 
 	/////////////////////////
@@ -331,7 +180,6 @@ public class GodotIO {
 
 	public int openURI(String p_uri) {
 		try {
-			Log.v("MyApp", "TRYING TO OPEN URI: " + p_uri);
 			String path = p_uri;
 			String type = "";
 			if (path.startsWith("/")) {
@@ -357,12 +205,12 @@ public class GodotIO {
 		}
 	}
 
-	public String getDataDir() {
-		return activity.getFilesDir().getAbsolutePath();
+	public String getCacheDir() {
+		return activity.getCacheDir().getAbsolutePath();
 	}
 
-	public String getExternalDataDir() {
-		return activity.getExternalFilesDir(null).getAbsolutePath();
+	public String getDataDir() {
+		return activity.getFilesDir().getAbsolutePath();
 	}
 
 	public String getLocale() {
@@ -456,51 +304,58 @@ public class GodotIO {
 	public static final int SYSTEM_DIR_PICTURES = 6;
 	public static final int SYSTEM_DIR_RINGTONES = 7;
 
-	public String getSystemDir(int idx) {
-		String what = "";
+	public String getSystemDir(int idx, boolean shared_storage) {
+		String what;
 		switch (idx) {
-			case SYSTEM_DIR_DESKTOP: {
-				//what=Environment.DIRECTORY_DOCUMENTS;
-				what = Environment.DIRECTORY_DOWNLOADS;
+			case SYSTEM_DIR_DESKTOP:
+			default: {
+				what = null; // This leads to the app specific external root directory.
 			} break;
+
 			case SYSTEM_DIR_DCIM: {
 				what = Environment.DIRECTORY_DCIM;
-
 			} break;
+
 			case SYSTEM_DIR_DOCUMENTS: {
-				what = Environment.DIRECTORY_DOWNLOADS;
-				//what=Environment.DIRECTORY_DOCUMENTS;
+				what = Environment.DIRECTORY_DOCUMENTS;
 			} break;
+
 			case SYSTEM_DIR_DOWNLOADS: {
 				what = Environment.DIRECTORY_DOWNLOADS;
-
 			} break;
+
 			case SYSTEM_DIR_MOVIES: {
 				what = Environment.DIRECTORY_MOVIES;
-
 			} break;
+
 			case SYSTEM_DIR_MUSIC: {
 				what = Environment.DIRECTORY_MUSIC;
 			} break;
+
 			case SYSTEM_DIR_PICTURES: {
 				what = Environment.DIRECTORY_PICTURES;
 			} break;
+
 			case SYSTEM_DIR_RINGTONES: {
 				what = Environment.DIRECTORY_RINGTONES;
-
 			} break;
 		}
 
-		if (what.equals(""))
-			return "";
-		return Environment.getExternalStoragePublicDirectory(what).getAbsolutePath();
+		if (shared_storage) {
+			if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
+				Log.w(TAG, "Shared storage access is limited on Android 10 and higher.");
+			}
+			if (TextUtils.isEmpty(what)) {
+				return Environment.getExternalStorageDirectory().getAbsolutePath();
+			} else {
+				return Environment.getExternalStoragePublicDirectory(what).getAbsolutePath();
+			}
+		} else {
+			return activity.getExternalFilesDir(what).getAbsolutePath();
+		}
 	}
 
-	protected static final String PREFS_FILE = "device_id.xml";
-	protected static final String PREFS_DEVICE_ID = "device_id";
-
-	public static String unique_id = "";
 	public String getUniqueID() {
-		return unique_id;
+		return uniqueId;
 	}
 }

+ 10 - 10
platform/android/java_godot_io_wrapper.cpp

@@ -48,8 +48,8 @@ GodotIOJavaWrapper::GodotIOJavaWrapper(JNIEnv *p_env, jobject p_godot_io_instanc
 		}
 
 		_open_URI = p_env->GetMethodID(cls, "openURI", "(Ljava/lang/String;)I");
+		_get_cache_dir = p_env->GetMethodID(cls, "getCacheDir", "()Ljava/lang/String;");
 		_get_data_dir = p_env->GetMethodID(cls, "getDataDir", "()Ljava/lang/String;");
-		_get_external_data_dir = p_env->GetMethodID(cls, "getExternalDataDir", "()Ljava/lang/String;");
 		_get_locale = p_env->GetMethodID(cls, "getLocale", "()Ljava/lang/String;");
 		_get_model = p_env->GetMethodID(cls, "getModel", "()Ljava/lang/String;");
 		_get_screen_DPI = p_env->GetMethodID(cls, "getScreenDPI", "()I");
@@ -59,7 +59,7 @@ GodotIOJavaWrapper::GodotIOJavaWrapper(JNIEnv *p_env, jobject p_godot_io_instanc
 		_hide_keyboard = p_env->GetMethodID(cls, "hideKeyboard", "()V");
 		_set_screen_orientation = p_env->GetMethodID(cls, "setScreenOrientation", "(I)V");
 		_get_screen_orientation = p_env->GetMethodID(cls, "getScreenOrientation", "()I");
-		_get_system_dir = p_env->GetMethodID(cls, "getSystemDir", "(I)Ljava/lang/String;");
+		_get_system_dir = p_env->GetMethodID(cls, "getSystemDir", "(IZ)Ljava/lang/String;");
 	}
 }
 
@@ -82,22 +82,22 @@ Error GodotIOJavaWrapper::open_uri(const String &p_uri) {
 	}
 }
 
-String GodotIOJavaWrapper::get_user_data_dir() {
-	if (_get_data_dir) {
+String GodotIOJavaWrapper::get_cache_dir() {
+	if (_get_cache_dir) {
 		JNIEnv *env = get_jni_env();
 		ERR_FAIL_COND_V(env == nullptr, String());
-		jstring s = (jstring)env->CallObjectMethod(godot_io_instance, _get_data_dir);
+		jstring s = (jstring)env->CallObjectMethod(godot_io_instance, _get_cache_dir);
 		return jstring_to_string(s, env);
 	} else {
 		return String();
 	}
 }
 
-String GodotIOJavaWrapper::get_external_data_dir() {
-	if (_get_external_data_dir) {
+String GodotIOJavaWrapper::get_user_data_dir() {
+	if (_get_data_dir) {
 		JNIEnv *env = get_jni_env();
 		ERR_FAIL_COND_V(env == nullptr, String());
-		jstring s = (jstring)env->CallObjectMethod(godot_io_instance, _get_external_data_dir);
+		jstring s = (jstring)env->CallObjectMethod(godot_io_instance, _get_data_dir);
 		return jstring_to_string(s, env);
 	} else {
 		return String();
@@ -200,11 +200,11 @@ int GodotIOJavaWrapper::get_screen_orientation() {
 	}
 }
 
-String GodotIOJavaWrapper::get_system_dir(int p_dir) {
+String GodotIOJavaWrapper::get_system_dir(int p_dir, bool p_shared_storage) {
 	if (_get_system_dir) {
 		JNIEnv *env = get_jni_env();
 		ERR_FAIL_COND_V(env == nullptr, String("."));
-		jstring s = (jstring)env->CallObjectMethod(godot_io_instance, _get_system_dir, p_dir);
+		jstring s = (jstring)env->CallObjectMethod(godot_io_instance, _get_system_dir, p_dir, p_shared_storage);
 		return jstring_to_string(s, env);
 	} else {
 		return String(".");

+ 3 - 3
platform/android/java_godot_io_wrapper.h

@@ -46,8 +46,8 @@ private:
 	jclass cls;
 
 	jmethodID _open_URI = 0;
+	jmethodID _get_cache_dir = 0;
 	jmethodID _get_data_dir = 0;
-	jmethodID _get_external_data_dir = 0;
 	jmethodID _get_locale = 0;
 	jmethodID _get_model = 0;
 	jmethodID _get_screen_DPI = 0;
@@ -66,8 +66,8 @@ public:
 	jobject get_instance();
 
 	Error open_uri(const String &p_uri);
+	String get_cache_dir();
 	String get_user_data_dir();
-	String get_external_data_dir();
 	String get_locale();
 	String get_model();
 	int get_screen_dpi();
@@ -80,7 +80,7 @@ public:
 	void set_vk_height(int p_height);
 	void set_screen_orientation(int p_orient);
 	int get_screen_orientation();
-	String get_system_dir(int p_dir);
+	String get_system_dir(int p_dir, bool p_shared_storage);
 };
 
 #endif /* !JAVA_GODOT_IO_WRAPPER_H */

+ 11 - 7
platform/android/os_android.cpp

@@ -213,6 +213,10 @@ String OS_Android::get_model_name() const {
 	return OS_Unix::get_model_name();
 }
 
+String OS_Android::get_data_path() const {
+	return get_user_data_dir();
+}
+
 String OS_Android::get_user_data_dir() const {
 	if (data_dir_cache != String())
 		return data_dir_cache;
@@ -225,11 +229,11 @@ String OS_Android::get_user_data_dir() const {
 	return ".";
 }
 
-String OS_Android::get_external_data_dir() const {
-	String data_dir = godot_io_java->get_external_data_dir();
-	if (data_dir != "") {
-		data_dir = _remove_symlink(data_dir);
-		return data_dir;
+String OS_Android::get_cache_path() const {
+	String cache_dir = godot_io_java->get_cache_dir();
+	if (cache_dir != "") {
+		cache_dir = _remove_symlink(cache_dir);
+		return cache_dir;
 	}
 	return ".";
 }
@@ -242,8 +246,8 @@ String OS_Android::get_unique_id() const {
 	return OS::get_unique_id();
 }
 
-String OS_Android::get_system_dir(SystemDir p_dir) const {
-	return godot_io_java->get_system_dir(p_dir);
+String OS_Android::get_system_dir(SystemDir p_dir, bool p_shared_storage) const {
+	return godot_io_java->get_system_dir(p_dir, p_shared_storage);
 }
 
 void OS_Android::set_display_size(const Size2i &p_size) {

+ 3 - 2
platform/android/os_android.h

@@ -110,14 +110,15 @@ public:
 
 	virtual Error shell_open(String p_uri) override;
 	virtual String get_user_data_dir() const override;
-	virtual String get_external_data_dir() const override;
+	virtual String get_data_path() const override;
+	virtual String get_cache_path() const override;
 	virtual String get_resource_dir() const override;
 	virtual String get_locale() const override;
 	virtual String get_model_name() const override;
 
 	virtual String get_unique_id() const override;
 
-	virtual String get_system_dir(SystemDir p_dir) const override;
+	virtual String get_system_dir(SystemDir p_dir, bool p_shared_storage = true) const override;
 
 	void vibrate_handheld(int p_duration_ms) override;
 

+ 1 - 1
platform/linuxbsd/os_linuxbsd.cpp

@@ -273,7 +273,7 @@ String OS_LinuxBSD::get_cache_path() const {
 	}
 }
 
-String OS_LinuxBSD::get_system_dir(SystemDir p_dir) const {
+String OS_LinuxBSD::get_system_dir(SystemDir p_dir, bool p_shared_storage) const {
 	String xdgparam;
 
 	switch (p_dir) {

+ 1 - 1
platform/linuxbsd/os_linuxbsd.h

@@ -84,7 +84,7 @@ public:
 	virtual String get_data_path() const override;
 	virtual String get_cache_path() const override;
 
-	virtual String get_system_dir(SystemDir p_dir) const override;
+	virtual String get_system_dir(SystemDir p_dir, bool p_shared_storage = true) const override;
 
 	virtual Error shell_open(String p_uri) override;
 

+ 1 - 1
platform/osx/os_osx.h

@@ -84,7 +84,7 @@ public:
 	virtual String get_bundle_resource_dir() const override;
 	virtual String get_godot_dir_name() const override;
 
-	virtual String get_system_dir(SystemDir p_dir) const override;
+	virtual String get_system_dir(SystemDir p_dir, bool p_shared_storage = true) const override;
 
 	Error shell_open(String p_uri) override;
 

+ 1 - 1
platform/osx/os_osx.mm

@@ -395,7 +395,7 @@ String OS_OSX::get_godot_dir_name() const {
 	return String(VERSION_SHORT_NAME).capitalize();
 }
 
-String OS_OSX::get_system_dir(SystemDir p_dir) const {
+String OS_OSX::get_system_dir(SystemDir p_dir, bool p_shared_storage) const {
 	NSSearchPathDirectory id;
 	bool found = true;
 

+ 1 - 1
platform/windows/os_windows.cpp

@@ -681,7 +681,7 @@ String OS_Windows::get_godot_dir_name() const {
 	return String(VERSION_SHORT_NAME).capitalize();
 }
 
-String OS_Windows::get_system_dir(SystemDir p_dir) const {
+String OS_Windows::get_system_dir(SystemDir p_dir, bool p_shared_storage) const {
 	KNOWNFOLDERID id;
 
 	switch (p_dir) {

+ 1 - 1
platform/windows/os_windows.h

@@ -150,7 +150,7 @@ public:
 	virtual String get_cache_path() const override;
 	virtual String get_godot_dir_name() const override;
 
-	virtual String get_system_dir(SystemDir p_dir) const override;
+	virtual String get_system_dir(SystemDir p_dir, bool p_shared_storage = true) const override;
 	virtual String get_user_data_dir() const override;
 
 	virtual String get_unique_id() const override;