@ -18,6 +18,7 @@ import android.content.IntentFilter
import android.content.res.Configuration
import android.content.res.Configuration
import android.graphics.Rect
import android.graphics.Rect
import android.graphics.drawable.Icon
import android.graphics.drawable.Icon
import android.hardware.input.InputManager
import android.hardware.Sensor
import android.hardware.Sensor
import android.hardware.SensorEvent
import android.hardware.SensorEvent
import android.hardware.SensorEventListener
import android.hardware.SensorEventListener
@ -63,11 +64,12 @@ import kotlin.math.roundToInt
import org.yuzu.yuzu_emu.utils.ForegroundService
import org.yuzu.yuzu_emu.utils.ForegroundService
import androidx.core.os.BundleCompat
import androidx.core.os.BundleCompat
class EmulationActivity : AppCompatActivity ( ) , SensorEventListener {
class EmulationActivity : AppCompatActivity ( ) , SensorEventListener , InputManager . InputDeviceListener {
private lateinit var binding : ActivityEmulationBinding
private lateinit var binding : ActivityEmulationBinding
var isActivityRecreated = false
var isActivityRecreated = false
private lateinit var nfcReader : NfcReader
private lateinit var nfcReader : NfcReader
private lateinit var inputManager : InputManager
private var touchDownTime : Long = 0
private var touchDownTime : Long = 0
private val maxTapDuration = 500L
private val maxTapDuration = 500L
@ -140,6 +142,9 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
nfcReader = NfcReader ( this )
nfcReader = NfcReader ( this )
nfcReader . initialize ( )
nfcReader . initialize ( )
inputManager = getSystemService ( INPUT_SERVICE ) as InputManager
inputManager . registerInputDeviceListener ( this , null )
foregroundService = Intent ( this , ForegroundService :: class . java )
foregroundService = Intent ( this , ForegroundService :: class . java )
startForegroundService ( foregroundService )
startForegroundService ( foregroundService )
@ -206,9 +211,9 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
override fun onDestroy ( ) {
override fun onDestroy ( ) {
super . onDestroy ( )
super . onDestroy ( )
inputManager . unregisterInputDeviceListener ( this )
stopForegroundService ( this )
stopForegroundService ( this )
NativeLibrary . playTimeManagerStop ( )
NativeLibrary . playTimeManagerStop ( )
}
}
override fun onUserLeaveHint ( ) {
override fun onUserLeaveHint ( ) {
@ -244,8 +249,10 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
val isPhysicalKeyboard = event . source and InputDevice . SOURCE_KEYBOARD == InputDevice . SOURCE_KEYBOARD &&
val isPhysicalKeyboard = event . source and InputDevice . SOURCE_KEYBOARD == InputDevice . SOURCE_KEYBOARD &&
event . device ?. isVirtual == false
event . device ?. isVirtual == false
if ( event . source and InputDevice . SOURCE_JOYSTICK != InputDevice . SOURCE_JOYSTICK &&
event . source and InputDevice . SOURCE_GAMEPAD != InputDevice . SOURCE_GAMEPAD &&
val isControllerInput = event . source and InputDevice . SOURCE_JOYSTICK == InputDevice . SOURCE_JOYSTICK ||
event . source and InputDevice . SOURCE_GAMEPAD == InputDevice . SOURCE_GAMEPAD
if ( !is ControllerInput &&
event . source and InputDevice . SOURCE_MOUSE != InputDevice . SOURCE_MOUSE &&
event . source and InputDevice . SOURCE_MOUSE != InputDevice . SOURCE_MOUSE &&
!is PhysicalKeyboard
!is PhysicalKeyboard
) {
) {
@ -256,12 +263,18 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
return super . dispatchKeyEvent ( event )
return super . dispatchKeyEvent ( event )
}
}
if ( isControllerInput && event . action == KeyEvent . ACTION_DOWN ) {
notifyControllerInput ( )
}
return InputHandler . dispatchKeyEvent ( event )
return InputHandler . dispatchKeyEvent ( event )
}
}
override fun dispatchGenericMotionEvent ( event : MotionEvent ) : Boolean {
override fun dispatchGenericMotionEvent ( event : MotionEvent ) : Boolean {
if ( event . source and InputDevice . SOURCE_JOYSTICK != InputDevice . SOURCE_JOYSTICK &&
event . source and InputDevice . SOURCE_GAMEPAD != InputDevice . SOURCE_GAMEPAD &&
val isControllerInput = event . source and InputDevice . SOURCE_JOYSTICK == InputDevice . SOURCE_JOYSTICK ||
event . source and InputDevice . SOURCE_GAMEPAD == InputDevice . SOURCE_GAMEPAD
if ( !is ControllerInput &&
event . source and InputDevice . SOURCE_KEYBOARD != InputDevice . SOURCE_KEYBOARD &&
event . source and InputDevice . SOURCE_KEYBOARD != InputDevice . SOURCE_KEYBOARD &&
event . source and InputDevice . SOURCE_MOUSE != InputDevice . SOURCE_MOUSE
event . source and InputDevice . SOURCE_MOUSE != InputDevice . SOURCE_MOUSE
) {
) {
@ -277,9 +290,54 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
return true
return true
}
}
if ( isControllerInput ) {
notifyControllerInput ( )
}
return InputHandler . dispatchGenericMotionEvent ( event )
return InputHandler . dispatchGenericMotionEvent ( event )
}
}
private fun notifyControllerInput ( ) {
val navHostFragment =
supportFragmentManager . findFragmentById ( R . id . fragment_container ) as ? NavHostFragment
val emulationFragment =
navHostFragment ?. childFragmentManager ?. fragments ?. firstOrNull ( ) as ? org . yuzu . yuzu_emu . fragments . EmulationFragment
emulationFragment ?. onControllerInputDetected ( )
}
private fun isGameController ( deviceId : Int ) : Boolean {
val device = InputDevice . getDevice ( deviceId ) ?: return false
val sources = device . sources
return sources and InputDevice . SOURCE_GAMEPAD == InputDevice . SOURCE_GAMEPAD ||
sources and InputDevice . SOURCE_JOYSTICK == InputDevice . SOURCE_JOYSTICK
}
override fun onInputDeviceAdded ( deviceId : Int ) {
if ( isGameController ( deviceId ) ) {
InputHandler . updateControllerData ( )
val navHostFragment =
supportFragmentManager . findFragmentById ( R . id . fragment_container ) as ? NavHostFragment
val emulationFragment =
navHostFragment ?. childFragmentManager ?. fragments ?. firstOrNull ( ) as ? org . yuzu . yuzu_emu . fragments . EmulationFragment
emulationFragment ?. onControllerConnected ( )
}
}
override fun onInputDeviceRemoved ( deviceId : Int ) {
InputHandler . updateControllerData ( )
val navHostFragment =
supportFragmentManager . findFragmentById ( R . id . fragment_container ) as ? NavHostFragment
val emulationFragment =
navHostFragment ?. childFragmentManager ?. fragments ?. firstOrNull ( ) as ? org . yuzu . yuzu_emu . fragments . EmulationFragment
emulationFragment ?. onControllerDisconnected ( )
}
override fun onInputDeviceChanged ( deviceId : Int ) {
if ( isGameController ( deviceId ) ) {
InputHandler . updateControllerData ( )
}
}
override fun onSensorChanged ( event : SensorEvent ) {
override fun onSensorChanged ( event : SensorEvent ) {
val rotation = this . display ?. rotation
val rotation = this . display ?. rotation
if ( rotation == Surface . ROTATION_90 ) {
if ( rotation == Surface . ROTATION_90 ) {
@ -519,8 +577,10 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
when ( event . action ) {
when ( event . action ) {
MotionEvent . ACTION_DOWN -> {
MotionEvent . ACTION_DOWN -> {
touchDownTime = System . currentTimeMillis ( )
touchDownTime = System . currentTimeMillis ( )
// show overlay immediately on touch and cancel timer
if ( ! emulationViewModel . drawerOpen . value ) {
// show overlay immediately on touch and cancel timer when only auto-hide is enabled
if ( ! emulationViewModel . drawerOpen . value &&
BooleanSetting . ENABLE_INPUT_OVERLAY_AUTO_HIDE . getBoolean ( ) &&
! BooleanSetting . HIDE_OVERLAY_ON_CONTROLLER_INPUT . getBoolean ( ) ) {
fragment . handler . removeCallbacksAndMessages ( null )
fragment . handler . removeCallbacksAndMessages ( null )
fragment . showOverlay ( )
fragment . showOverlay ( )
}
}