From b3f82618d704d1d89ce36368c419a0427d0d640f Mon Sep 17 00:00:00 2001 From: PabloMK7 Date: Wed, 11 Mar 2026 18:34:12 +0100 Subject: [PATCH] android: Use StorageManager to get removable media path --- .../java/org/citra/citra_emu/NativeLibrary.kt | 2 +- .../citra_emu/utils/RemovableStorageHelper.kt | 49 ++++++++++++------- 2 files changed, 33 insertions(+), 18 deletions(-) diff --git a/src/android/app/src/main/java/org/citra/citra_emu/NativeLibrary.kt b/src/android/app/src/main/java/org/citra/citra_emu/NativeLibrary.kt index 49951cbbd..cf1659361 100644 --- a/src/android/app/src/main/java/org/citra/citra_emu/NativeLibrary.kt +++ b/src/android/app/src/main/java/org/citra/citra_emu/NativeLibrary.kt @@ -737,7 +737,7 @@ object NativeLibrary { return primaryStoragePath + dirSep + virtualPath } else { // User directory probably located on a removable storage device val storageIdString = pathSegment.substringBefore(":") - val removablePath = RemovableStorageHelper.getRemovableStoragePath(storageIdString) + val removablePath = RemovableStorageHelper.getRemovableStoragePath(CitraApplication.appContext, storageIdString) if (removablePath == null) { android.util.Log.e("NativeLibrary", diff --git a/src/android/app/src/main/java/org/citra/citra_emu/utils/RemovableStorageHelper.kt b/src/android/app/src/main/java/org/citra/citra_emu/utils/RemovableStorageHelper.kt index 82c4b9ffd..1d4958fed 100644 --- a/src/android/app/src/main/java/org/citra/citra_emu/utils/RemovableStorageHelper.kt +++ b/src/android/app/src/main/java/org/citra/citra_emu/utils/RemovableStorageHelper.kt @@ -4,28 +4,43 @@ package org.citra.citra_emu.utils -import org.citra.citra_emu.utils.BuildUtil -import java.io.File +import android.content.Context +import android.os.storage.StorageManager object RemovableStorageHelper { - // This really shouldn't be necessary, but the Android API seemingly - // doesn't have a way of doing this? - fun getRemovableStoragePath(idString: String): String? { - BuildUtil.assertNotGooglePlay() - // On certain Android flavours the external storage mount location can - // vary, so add extra cases here if we discover them. - val possibleMountPaths = listOf("/mnt/media_rw/$idString", "/storage/$idString") + private val pathCache = mutableMapOf() + private var scanned = false - for (mountPath in possibleMountPaths) { - val pathFile = File(mountPath); - if (pathFile.exists()) { - // TODO: Cache which mount location is being used for the remainder of the - // session, as it should never change. -OS - return pathFile.absolutePath - } + private fun scanVolumes(context: Context) { + if (scanned) { + return } - return null + val storageManager = context.getSystemService(Context.STORAGE_SERVICE) as StorageManager + + for (volume in storageManager.storageVolumes) { + if (!volume.isRemovable) { + continue + } + + val uuid = volume.uuid ?: continue + val dir = volume.directory ?: continue + + pathCache[uuid.uppercase()] = dir.absolutePath + } + + scanned = true + } + + fun getRemovableStoragePath(context: Context, idString: String): String? { + BuildUtil.assertNotGooglePlay() + val key = idString.uppercase() + + if (!scanned) { + scanVolumes(context) + } + + return pathCache[key] } }