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:
commit
88f7012923
3 changed files with 97 additions and 77 deletions
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue