Android: Use OpenDocument for NFC figures and fix crash (Fixes #13956)

- Switch from ActivityResultContracts.GetContent() to OpenDocument()
  which restores ACTION_OPEN_DOCUMENT behavior, granting write access
  to file descriptors opened in C++ via ContentHandler.openFd().
- Replace unsafe force-unwraps (!!) in EmulationActivity with null checks.
This commit is contained in:
Sergio Torres
2026-08-26 00:49:43 +02:00
parent 123d32248e
commit b1e360e1e4
4 changed files with 21 additions and 13 deletions
@@ -43,6 +43,7 @@ import org.dolphinemu.dolphinemu.features.infinitybase.ui.FigureSlotAdapter
import org.dolphinemu.dolphinemu.features.input.model.ControllerInterface import org.dolphinemu.dolphinemu.features.input.model.ControllerInterface
import org.dolphinemu.dolphinemu.features.input.model.DolphinSensorEventListener import org.dolphinemu.dolphinemu.features.input.model.DolphinSensorEventListener
import org.dolphinemu.dolphinemu.features.settings.model.BooleanSetting import org.dolphinemu.dolphinemu.features.settings.model.BooleanSetting
import org.dolphinemu.dolphinemu.utils.ContentHandler
import org.dolphinemu.dolphinemu.features.settings.model.IntSetting import org.dolphinemu.dolphinemu.features.settings.model.IntSetting
import org.dolphinemu.dolphinemu.features.settings.model.Settings import org.dolphinemu.dolphinemu.features.settings.model.Settings
import org.dolphinemu.dolphinemu.features.settings.model.StringSetting import org.dolphinemu.dolphinemu.features.settings.model.StringSetting
@@ -104,17 +105,21 @@ class EmulationActivity : AppCompatActivity(), ThemeProvider {
} }
val requestSkylanderFile = registerForActivityResult( val requestSkylanderFile = registerForActivityResult(
ActivityResultContracts.GetContent() ActivityResultContracts.OpenDocument()
) { uri: Uri? -> ) { uri: Uri? ->
if (uri != null) { if (uri != null) {
val slot = SkylanderConfig.loadSkylander( val slot = SkylanderConfig.loadSkylander(
skylanderSlots[skylanderSlot].portalSlot, skylanderSlots[skylanderSlot].portalSlot,
uri.toString() uri.toString()
)!! )
clearSkylander(skylanderSlot) if (slot != null && slot.first != null && slot.second != null) {
skylanderSlots[skylanderSlot].portalSlot = slot.first!! clearSkylander(skylanderSlot)
skylanderSlots[skylanderSlot].label = slot.second!! skylanderSlots[skylanderSlot].portalSlot = slot.first!!
skylandersBinding.figureManager.adapter!!.notifyItemChanged(skylanderSlot) skylanderSlots[skylanderSlot].label = slot.second!!
skylandersBinding.figureManager.adapter?.notifyItemChanged(skylanderSlot)
} else {
Toast.makeText(this, R.string.skylander_load_failed, Toast.LENGTH_SHORT).show()
}
skylanderSlot = -1 skylanderSlot = -1
skylanderData = Skylander.BLANK_SKYLANDER skylanderData = Skylander.BLANK_SKYLANDER
} }
@@ -131,10 +136,12 @@ class EmulationActivity : AppCompatActivity(), ThemeProvider {
uri.toString(), uri.toString(),
skylanderSlots[skylanderSlot].portalSlot skylanderSlots[skylanderSlot].portalSlot
) )
clearSkylander(skylanderSlot) if (slot.first != -1) {
skylanderSlots[skylanderSlot].portalSlot = slot.first clearSkylander(skylanderSlot)
skylanderSlots[skylanderSlot].label = slot.second skylanderSlots[skylanderSlot].portalSlot = slot.first
skylandersBinding.figureManager.adapter?.notifyItemChanged(skylanderSlot) skylanderSlots[skylanderSlot].label = slot.second
skylandersBinding.figureManager.adapter?.notifyItemChanged(skylanderSlot)
}
skylanderSlot = -1 skylanderSlot = -1
skylanderData = Skylander.BLANK_SKYLANDER skylanderData = Skylander.BLANK_SKYLANDER
} }
@@ -142,7 +149,7 @@ class EmulationActivity : AppCompatActivity(), ThemeProvider {
} }
val requestInfinityFigureFile = registerForActivityResult( val requestInfinityFigureFile = registerForActivityResult(
ActivityResultContracts.GetContent() ActivityResultContracts.OpenDocument()
) { uri: Uri? -> ) { uri: Uri? ->
if (uri != null) { if (uri != null) {
val label = InfinityConfig.loadFigure(infinityPosition, uri.toString()) val label = InfinityConfig.loadFigure(infinityPosition, uri.toString())
@@ -51,7 +51,7 @@ class FigureSlotAdapter(
holder.binding.buttonLoadFigure.setOnClickListener { holder.binding.buttonLoadFigure.setOnClickListener {
activity.setInfinityFigureData(0, "", figure.position, position) activity.setInfinityFigureData(0, "", figure.position, position)
activity.requestInfinityFigureFile.launch("*/*") activity.requestInfinityFigureFile.launch(arrayOf("*/*"))
} }
val inflater = LayoutInflater.from(activity) val inflater = LayoutInflater.from(activity)
@@ -47,7 +47,7 @@ class SkylanderSlotAdapter(
holder.binding.buttonLoadFigure.setOnClickListener { holder.binding.buttonLoadFigure.setOnClickListener {
activity.setSkylanderData(0, 0, "", position) activity.setSkylanderData(0, 0, "", position)
activity.requestSkylanderFile.launch("*/*") activity.requestSkylanderFile.launch(arrayOf("*/*"))
} }
val inflater = LayoutInflater.from(activity) val inflater = LayoutInflater.from(activity)
@@ -956,6 +956,7 @@ It can efficiently compress both junk data and encrypted Wii data.
<string name="skylander_id">ID</string> <string name="skylander_id">ID</string>
<string name="skylander_variant">Variant</string> <string name="skylander_variant">Variant</string>
<string name="invalid_skylander">Invalid Skylander Selection</string> <string name="invalid_skylander">Invalid Skylander Selection</string>
<string name="skylander_load_failed">Failed to load Skylander figure</string>
<string name="emulate_infinity_base">Infinity Base</string> <string name="emulate_infinity_base">Infinity Base</string>
<string name="infinity_manager">Infinity Manager</string> <string name="infinity_manager">Infinity Manager</string>