Merge pull request #89999 from melquiadess/prevent-potential-NPEs-and-improve-nullability-handling

Android: Prevent potential NPEs and improve nullability handling
This commit is contained in:
Rémi Verschelde 2024-04-04 14:35:57 +02:00
commit 88f7012923
No known key found for this signature in database
GPG key ID: C3336907360768E1
3 changed files with 97 additions and 77 deletions

View file

@ -396,16 +396,19 @@ class Godot(private val context: Context) : SensorEventListener {
} }
if (host == primaryHost) { if (host == primaryHost) {
renderView!!.startRenderer() renderView?.startRenderer()
} }
val view: View = renderView!!.view
containerLayout?.addView( renderView?.let {
view, containerLayout?.addView(
it.view,
ViewGroup.LayoutParams( ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT ViewGroup.LayoutParams.MATCH_PARENT
) )
) )
}
editText.setView(renderView) editText.setView(renderView)
io?.setEdit(editText) io?.setEdit(editText)
@ -448,20 +451,23 @@ class Godot(private val context: Context) : SensorEventListener {
}) })
} else { } else {
// Infer the virtual keyboard height using visible area. // Infer the virtual keyboard height using visible area.
view.viewTreeObserver.addOnGlobalLayoutListener(object : OnGlobalLayoutListener { renderView?.view?.viewTreeObserver?.addOnGlobalLayoutListener(object : OnGlobalLayoutListener {
// Don't allocate a new Rect every time the callback is called. // Don't allocate a new Rect every time the callback is called.
val visibleSize = Rect() val visibleSize = Rect()
override fun onGlobalLayout() { override fun onGlobalLayout() {
val surfaceView = renderView!!.view renderView?.let {
surfaceView.getWindowVisibleDisplayFrame(visibleSize) val surfaceView = it.view
val keyboardHeight = surfaceView.height - visibleSize.bottom
GodotLib.setVirtualKeyboardHeight(keyboardHeight) surfaceView.getWindowVisibleDisplayFrame(visibleSize)
val keyboardHeight = surfaceView.height - visibleSize.bottom
GodotLib.setVirtualKeyboardHeight(keyboardHeight)
}
} }
}) })
} }
if (host == primaryHost) { if (host == primaryHost) {
renderView!!.queueOnRenderThread { renderView?.queueOnRenderThread {
for (plugin in pluginRegistry.allPlugins) { for (plugin in pluginRegistry.allPlugins) {
plugin.onRegisterPluginWithGodotNative() plugin.onRegisterPluginWithGodotNative()
} }
@ -495,7 +501,7 @@ class Godot(private val context: Context) : SensorEventListener {
return return
} }
renderView!!.onActivityStarted() renderView?.onActivityStarted()
} }
fun onResume(host: GodotHost) { fun onResume(host: GodotHost) {
@ -503,7 +509,7 @@ class Godot(private val context: Context) : SensorEventListener {
return return
} }
renderView!!.onActivityResumed() renderView?.onActivityResumed()
if (mAccelerometer != null) { if (mAccelerometer != null) {
mSensorManager.registerListener(this, mAccelerometer, SensorManager.SENSOR_DELAY_GAME) mSensorManager.registerListener(this, mAccelerometer, SensorManager.SENSOR_DELAY_GAME)
} }
@ -535,7 +541,7 @@ class Godot(private val context: Context) : SensorEventListener {
return return
} }
renderView!!.onActivityPaused() renderView?.onActivityPaused()
mSensorManager.unregisterListener(this) mSensorManager.unregisterListener(this)
for (plugin in pluginRegistry.allPlugins) { for (plugin in pluginRegistry.allPlugins) {
plugin.onMainPause() plugin.onMainPause()
@ -547,7 +553,7 @@ class Godot(private val context: Context) : SensorEventListener {
return return
} }
renderView!!.onActivityStopped() renderView?.onActivityStopped()
} }
fun onDestroy(primaryHost: GodotHost) { fun onDestroy(primaryHost: GodotHost) {
@ -569,7 +575,7 @@ class Godot(private val context: Context) : SensorEventListener {
* Configuration change callback * Configuration change callback
*/ */
fun onConfigurationChanged(newConfig: Configuration) { fun onConfigurationChanged(newConfig: Configuration) {
var newDarkMode = newConfig.uiMode?.and(Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES val newDarkMode = newConfig.uiMode.and(Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES
if (darkMode != newDarkMode) { if (darkMode != newDarkMode) {
darkMode = newDarkMode darkMode = newDarkMode
GodotLib.onNightModeChanged() GodotLib.onNightModeChanged()
@ -686,9 +692,7 @@ class Godot(private val context: Context) : SensorEventListener {
* This must be called after the render thread has started. * This must be called after the render thread has started.
*/ */
fun runOnRenderThread(action: Runnable) { fun runOnRenderThread(action: Runnable) {
if (renderView != null) { renderView?.queueOnRenderThread(action)
renderView!!.queueOnRenderThread(action)
}
} }
/** /**
@ -765,7 +769,7 @@ class Godot(private val context: Context) : SensorEventListener {
return mClipboard.hasPrimaryClip() return mClipboard.hasPrimaryClip()
} }
fun getClipboard(): String? { fun getClipboard(): String {
val clipData = mClipboard.primaryClip ?: return "" val clipData = mClipboard.primaryClip ?: return ""
val text = clipData.getItemAt(0).text ?: return "" val text = clipData.getItemAt(0).text ?: return ""
return text.toString() return text.toString()
@ -782,15 +786,14 @@ class Godot(private val context: Context) : SensorEventListener {
@Keep @Keep
private fun forceQuit(instanceId: Int): Boolean { private fun forceQuit(instanceId: Int): Boolean {
if (primaryHost == null) { primaryHost?.let {
return false if (instanceId == 0) {
} it.onGodotForceQuit(this)
return if (instanceId == 0) { return true
primaryHost!!.onGodotForceQuit(this) } else {
true return it.onGodotForceQuit(instanceId)
} else { }
primaryHost!!.onGodotForceQuit(instanceId) } ?: return false
}
} }
fun onBackPressed(host: GodotHost) { fun onBackPressed(host: GodotHost) {
@ -804,14 +807,14 @@ class Godot(private val context: Context) : SensorEventListener {
shouldQuit = false shouldQuit = false
} }
} }
if (shouldQuit && renderView != null) { if (shouldQuit) {
renderView!!.queueOnRenderThread { GodotLib.back() } renderView?.queueOnRenderThread { GodotLib.back() }
} }
} }
private fun getRotatedValues(values: FloatArray?): FloatArray? { private fun getRotatedValues(values: FloatArray?): FloatArray? {
if (values == null || values.size != 3) { if (values == null || values.size != 3) {
return values return null
} }
val display = val display =
(requireActivity().getSystemService(Context.WINDOW_SERVICE) as WindowManager).defaultDisplay (requireActivity().getSystemService(Context.WINDOW_SERVICE) as WindowManager).defaultDisplay
@ -848,35 +851,39 @@ class Godot(private val context: Context) : SensorEventListener {
} }
when (event.sensor.type) { when (event.sensor.type) {
Sensor.TYPE_ACCELEROMETER -> { Sensor.TYPE_ACCELEROMETER -> {
val rotatedValues = getRotatedValues(event.values) getRotatedValues(event.values)?.let { rotatedValues ->
renderView!!.queueOnRenderThread { renderView?.queueOnRenderThread {
GodotLib.accelerometer( GodotLib.accelerometer(
-rotatedValues!![0], -rotatedValues[1], -rotatedValues[2] -rotatedValues[0], -rotatedValues[1], -rotatedValues[2]
) )
}
} }
} }
Sensor.TYPE_GRAVITY -> { Sensor.TYPE_GRAVITY -> {
val rotatedValues = getRotatedValues(event.values) getRotatedValues(event.values)?.let { rotatedValues ->
renderView!!.queueOnRenderThread { renderView?.queueOnRenderThread {
GodotLib.gravity( GodotLib.gravity(
-rotatedValues!![0], -rotatedValues[1], -rotatedValues[2] -rotatedValues[0], -rotatedValues[1], -rotatedValues[2]
) )
}
} }
} }
Sensor.TYPE_MAGNETIC_FIELD -> { Sensor.TYPE_MAGNETIC_FIELD -> {
val rotatedValues = getRotatedValues(event.values) getRotatedValues(event.values)?.let { rotatedValues ->
renderView!!.queueOnRenderThread { renderView?.queueOnRenderThread {
GodotLib.magnetometer( GodotLib.magnetometer(
-rotatedValues!![0], -rotatedValues[1], -rotatedValues[2] -rotatedValues[0], -rotatedValues[1], -rotatedValues[2]
) )
}
} }
} }
Sensor.TYPE_GYROSCOPE -> { Sensor.TYPE_GYROSCOPE -> {
val rotatedValues = getRotatedValues(event.values) getRotatedValues(event.values)?.let { rotatedValues ->
renderView!!.queueOnRenderThread { renderView?.queueOnRenderThread {
GodotLib.gyroscope( GodotLib.gyroscope(
rotatedValues!![0], rotatedValues[1], rotatedValues[2] rotatedValues[0], rotatedValues[1], rotatedValues[2]
) )
}
} }
} }
} }
@ -1039,7 +1046,7 @@ class Godot(private val context: Context) : SensorEventListener {
@Keep @Keep
private fun initInputDevices() { private fun initInputDevices() {
renderView!!.initInputDevices() renderView?.initInputDevices()
} }
@Keep @Keep

View file

@ -83,8 +83,9 @@ abstract class GodotActivity : FragmentActivity(), GodotHost {
override fun onDestroy() { override fun onDestroy() {
Log.v(TAG, "Destroying Godot app...") Log.v(TAG, "Destroying Godot app...")
super.onDestroy() super.onDestroy()
if (godotFragment != null) {
terminateGodotInstance(godotFragment!!.godot) godotFragment?.let {
terminateGodotInstance(it.godot)
} }
} }
@ -93,22 +94,26 @@ abstract class GodotActivity : FragmentActivity(), GodotHost {
} }
private fun terminateGodotInstance(instance: Godot) { private fun terminateGodotInstance(instance: Godot) {
if (godotFragment != null && instance === godotFragment!!.godot) { godotFragment?.let {
Log.v(TAG, "Force quitting Godot instance") if (instance === it.godot) {
ProcessPhoenix.forceQuit(this) Log.v(TAG, "Force quitting Godot instance")
ProcessPhoenix.forceQuit(this)
}
} }
} }
override fun onGodotRestartRequested(instance: Godot) { override fun onGodotRestartRequested(instance: Godot) {
runOnUiThread { runOnUiThread {
if (godotFragment != null && instance === godotFragment!!.godot) { godotFragment?.let {
// It's very hard to properly de-initialize Godot on Android to restart the game if (instance === it.godot) {
// from scratch. Therefore, we need to kill the whole app process and relaunch it. // It's very hard to properly de-initialize Godot on Android to restart the game
// // from scratch. Therefore, we need to kill the whole app process and relaunch it.
// Restarting only the activity, wouldn't be enough unless it did proper cleanup (including //
// releasing and reloading native libs or resetting their state somehow and clearing static data). // Restarting only the activity, wouldn't be enough unless it did proper cleanup (including
Log.v(TAG, "Restarting Godot instance...") // releasing and reloading native libs or resetting their state somehow and clearing static data).
ProcessPhoenix.triggerRebirth(this) Log.v(TAG, "Restarting Godot instance...")
ProcessPhoenix.triggerRebirth(this)
}
} }
} }
} }

View file

@ -56,7 +56,9 @@ class FileAccessHandler(val context: Context) {
} }
return try { return try {
DataAccess.fileExists(storageScope, context, path!!) path?.let {
DataAccess.fileExists(storageScope, context, it)
} ?: false
} catch (e: SecurityException) { } catch (e: SecurityException) {
false false
} }
@ -69,20 +71,22 @@ class FileAccessHandler(val context: Context) {
} }
return try { return try {
DataAccess.removeFile(storageScope, context, path!!) path?.let {
DataAccess.removeFile(storageScope, context, it)
} ?: false
} catch (e: Exception) { } catch (e: Exception) {
false false
} }
} }
internal fun renameFile(context: Context, storageScopeIdentifier: StorageScope.Identifier, from: String?, to: String?): Boolean { internal fun renameFile(context: Context, storageScopeIdentifier: StorageScope.Identifier, from: String, to: String): Boolean {
val storageScope = storageScopeIdentifier.identifyStorageScope(from) val storageScope = storageScopeIdentifier.identifyStorageScope(from)
if (storageScope == StorageScope.UNKNOWN) { if (storageScope == StorageScope.UNKNOWN) {
return false return false
} }
return try { return try {
DataAccess.renameFile(storageScope, context, from!!, to!!) DataAccess.renameFile(storageScope, context, from, to)
} catch (e: Exception) { } catch (e: Exception) {
false false
} }
@ -106,16 +110,18 @@ class FileAccessHandler(val context: Context) {
return INVALID_FILE_ID return INVALID_FILE_ID
} }
try { return try {
val dataAccess = DataAccess.generateDataAccess(storageScope, context, path!!, accessFlag) ?: return INVALID_FILE_ID path?.let {
val dataAccess = DataAccess.generateDataAccess(storageScope, context, it, accessFlag) ?: return INVALID_FILE_ID
files.put(++lastFileId, dataAccess) files.put(++lastFileId, dataAccess)
return lastFileId lastFileId
} ?: INVALID_FILE_ID
} catch (e: FileNotFoundException) { } catch (e: FileNotFoundException) {
return FILE_NOT_FOUND_ERROR_ID FILE_NOT_FOUND_ERROR_ID
} catch (e: Exception) { } catch (e: Exception) {
Log.w(TAG, "Error while opening $path", e) Log.w(TAG, "Error while opening $path", e)
return INVALID_FILE_ID INVALID_FILE_ID
} }
} }
@ -176,7 +182,9 @@ class FileAccessHandler(val context: Context) {
} }
return try { return try {
DataAccess.fileLastModified(storageScope, context, filepath!!) filepath?.let {
DataAccess.fileLastModified(storageScope, context, it)
} ?: 0L
} catch (e: SecurityException) { } catch (e: SecurityException) {
0L 0L
} }