Merge pull request #11385 from JosJuice/android-input-overhaul

Android input overhaul
This commit is contained in:
JosJuice 2023-03-11 12:37:44 +01:00 committed by GitHub
commit 62ff2f1030
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
121 changed files with 5881 additions and 4990 deletions

View File

@ -1,104 +0,0 @@
[GCPad1]
Device = Android/0/Touchscreen
Buttons/A = `Button 0`
Buttons/B = `Button 1`
Buttons/Start = `Button 2`
Buttons/X = `Button 3`
Buttons/Y = `Button 4`
Buttons/Z = `Button 5`
D-Pad/Up = `Button 6`
D-Pad/Down = `Button 7`
D-Pad/Left = `Button 8`
D-Pad/Right = `Button 9`
Main Stick/Up = `Axis 11`
Main Stick/Down = `Axis 12`
Main Stick/Left = `Axis 13`
Main Stick/Right = `Axis 14`
C-Stick/Up = `Axis 16`
C-Stick/Down = `Axis 17`
C-Stick/Left = `Axis 18`
C-Stick/Right = `Axis 19`
Triggers/L = `Axis 20`
Triggers/R = `Axis 21`
Triggers/L-Analog = `Axis 20`
Triggers/R-Analog = `Axis 21`
Triggers/Threshold = 90,000000
Rumble/Motor = `Rumble 700`
[GCPad2]
Device = Android/1/Touchscreen
Buttons/A = `Button 0`
Buttons/B = `Button 1`
Buttons/Start = `Button 2`
Buttons/X = `Button 3`
Buttons/Y = `Button 4`
Buttons/Z = `Button 5`
D-Pad/Up = `Button 6`
D-Pad/Down = `Button 7`
D-Pad/Left = `Button 8`
D-Pad/Right = `Button 9`
Main Stick/Up = `Axis 11`
Main Stick/Down = `Axis 12`
Main Stick/Left = `Axis 13`
Main Stick/Right = `Axis 14`
C-Stick/Up = `Axis 16`
C-Stick/Down = `Axis 17`
C-Stick/Left = `Axis 18`
C-Stick/Right = `Axis 19`
Triggers/L = `Axis 20`
Triggers/R = `Axis 21`
Triggers/L-Analog = `Axis 20`
Triggers/R-Analog = `Axis 21`
Triggers/Threshold = 90,000000
Rumble/Motor = `Rumble 700`
[GCPad3]
Device = Android/2/Touchscreen
Buttons/A = `Button 0`
Buttons/B = `Button 1`
Buttons/Start = `Button 2`
Buttons/X = `Button 3`
Buttons/Y = `Button 4`
Buttons/Z = `Button 5`
D-Pad/Up = `Button 6`
D-Pad/Down = `Button 7`
D-Pad/Left = `Button 8`
D-Pad/Right = `Button 9`
Main Stick/Up = `Axis 11`
Main Stick/Down = `Axis 12`
Main Stick/Left = `Axis 13`
Main Stick/Right = `Axis 14`
C-Stick/Up = `Axis 16`
C-Stick/Down = `Axis 17`
C-Stick/Left = `Axis 18`
C-Stick/Right = `Axis 19`
Triggers/L = `Axis 20`
Triggers/R = `Axis 21`
Triggers/L-Analog = `Axis 20`
Triggers/R-Analog = `Axis 21`
Triggers/Threshold = 90,000000
Rumble/Motor = `Rumble 700`
[GCPad4]
Device = Android/3/Touchscreen
Buttons/A = `Button 0`
Buttons/B = `Button 1`
Buttons/Start = `Button 2`
Buttons/X = `Button 3`
Buttons/Y = `Button 4`
Buttons/Z = `Button 5`
D-Pad/Up = `Button 6`
D-Pad/Down = `Button 7`
D-Pad/Left = `Button 8`
D-Pad/Right = `Button 9`
Main Stick/Up = `Axis 11`
Main Stick/Down = `Axis 12`
Main Stick/Left = `Axis 13`
Main Stick/Right = `Axis 14`
C-Stick/Up = `Axis 16`
C-Stick/Down = `Axis 17`
C-Stick/Left = `Axis 18`
C-Stick/Right = `Axis 19`
Triggers/L = `Axis 20`
Triggers/R = `Axis 21`
Triggers/L-Analog = `Axis 20`
Triggers/R-Analog = `Axis 21`
Triggers/Threshold = 90,000000
Rumble/Motor = `Rumble 700`

View File

@ -1,580 +0,0 @@
[Wiimote1]
Device = Android/4/Touchscreen
Buttons/A = `Button 100`
Buttons/B = `Button 101`
Buttons/- = `Button 102`
Buttons/+ = `Button 103`
Buttons/Home = `Button 104`
Buttons/1 = `Button 105`
Buttons/2 = `Button 106`
D-Pad/Up = `Button 107`
D-Pad/Down = `Button 108`
D-Pad/Left = `Button 109`
D-Pad/Right = `Button 110`
IR/Up = `Axis 112`
IR/Down = `Axis 113`
IR/Left = `Axis 114`
IR/Right = `Axis 115`
IR/Forward = `Axis 116`
IR/Backward = `Axis 117`
IR/Hide = `Button 118`
IR/Total Pitch = 20
IR/Total Yaw = 25
IR/Vertical Offset = 10
Swing/Up = `Axis 120`
Swing/Down = `Axis 121`
Swing/Left = `Axis 122`
Swing/Right = `Axis 123`
Swing/Forward = `Axis 124`
Swing/Backward = `Axis 125`
Tilt/Forward = `Axis 127`
Tilt/Backward = `Axis 128`
Tilt/Left = `Axis 129`
Tilt/Right = `Axis 130`
Tilt/Modifier = `Button 131`
Tilt/Modifier/Range = 50,000000
Shake/X = `Button 132`
Shake/Y = `Button 133`
Shake/Z = `Button 134`
Extension = Nunchuk
Nunchuk/Buttons/C = `Button 200`
Nunchuk/Buttons/Z = `Button 201`
Nunchuk/Stick/Up = `Axis 203`
Nunchuk/Stick/Down = `Axis 204`
Nunchuk/Stick/Left = `Axis 205`
Nunchuk/Stick/Right = `Axis 206`
Nunchuk/Swing/Up = `Axis 208`
Nunchuk/Swing/Down = `Axis 209`
Nunchuk/Swing/Left = `Axis 210`
Nunchuk/Swing/Right = `Axis 211`
Nunchuk/Swing/Forward = `Axis 212`
Nunchuk/Swing/Backward = `Axis 213`
Nunchuk/Tilt/Forward = `Axis 215`
Nunchuk/Tilt/Backward = `Axis 216`
Nunchuk/Tilt/Left = `Axis 217`
Nunchuk/Tilt/Right = `Axis 218`
Nunchuk/Tilt/Modifier = `Button 219`
Nunchuk/Tilt/Modifier/Range = 50,000000
Nunchuk/Shake/X = `Button 220`
Nunchuk/Shake/Y = `Button 221`
Nunchuk/Shake/Z = `Button 222`
Classic/Buttons/A = `Button 300`
Classic/Buttons/B = `Button 301`
Classic/Buttons/X = `Button 302`
Classic/Buttons/Y = `Button 303`
Classic/Buttons/- = `Button 304`
Classic/Buttons/+ = `Button 305`
Classic/Buttons/Home = `Button 306`
Classic/Buttons/ZL = `Button 307`
Classic/Buttons/ZR = `Button 308`
Classic/D-Pad/Up = `Button 309`
Classic/D-Pad/Down = `Button 310`
Classic/D-Pad/Left = `Button 311`
Classic/D-Pad/Right = `Button 312`
Classic/Left Stick/Up = `Axis 314`
Classic/Left Stick/Down = `Axis 315`
Classic/Left Stick/Left = `Axis 316`
Classic/Left Stick/Right = `Axis 317`
Classic/Right Stick/Up = `Axis 319`
Classic/Right Stick/Down = `Axis 320`
Classic/Right Stick/Left = `Axis 321`
Classic/Right Stick/Right = `Axis 322`
Classic/Triggers/L = `Axis 323`
Classic/Triggers/R = `Axis 324`
Classic/Triggers/Threshold = 90,000000
Guitar/Buttons/- = `Button 400`
Guitar/Buttons/+ = `Button 401`
Guitar/Frets/Green = `Button 402`
Guitar/Frets/Red = `Button 403`
Guitar/Frets/Yellow = `Button 404`
Guitar/Frets/Blue = `Button 405`
Guitar/Frets/Orange = `Button 406`
Guitar/Strum/Up = `Button 407`
Guitar/Strum/Down = `Button 408`
Guitar/Stick/Up = `Axis 410`
Guitar/Stick/Down = `Axis 411`
Guitar/Stick/Left = `Axis 412`
Guitar/Stick/Right = `Axis 413`
Guitar/Whammy/Bar = `Axis 414`
Drums/Buttons/- = `Button 500`
Drums/Buttons/+ = `Button 501`
Drums/Pads/Red = `Button 502`
Drums/Pads/Yellow = `Button 503`
Drums/Pads/Blue = `Button 504`
Drums/Pads/Green = `Button 505`
Drums/Pads/Orange = `Button 506`
Drums/Pads/Bass = `Button 507`
Drums/Stick/Up = `Axis 509`
Drums/Stick/Down = `Axis 510`
Drums/Stick/Left = `Axis 511`
Drums/Stick/Right = `Axis 512`
Turntable/Buttons/Green Left = `Button 600`
Turntable/Buttons/Red Left = `Button 601`
Turntable/Buttons/Blue Left = `Button 602`
Turntable/Buttons/Green Right = `Button 603`
Turntable/Buttons/Red Right = `Button 604`
Turntable/Buttons/Blue Right = `Button 605`
Turntable/Buttons/- = `Button 606`
Turntable/Buttons/+ = `Button 607`
Turntable/Buttons/Home = `Button 608`
Turntable/Buttons/Euphoria = `Button 609`
Turntable/Table Left/Left = `Axis 611`
Turntable/Table Left/Right = `Axis 612`
Turntable/Table Right/Left = `Axis 614`
Turntable/Table Right/Right = `Axis 615`
Turntable/Stick/Up = `Axis 617`
Turntable/Stick/Down = `Axis 618`
Turntable/Stick/Left = `Axis 619`
Turntable/Stick/Right = `Axis 620`
Turntable/Effect/Dial = `Axis 621`
Turntable/Crossfade/Left = `Axis 623`
Turntable/Crossfade/Right = `Axis 624`
IMUAccelerometer/Left = `Axis 625`
IMUAccelerometer/Right = `Axis 626`
IMUAccelerometer/Forward = `Axis 627`
IMUAccelerometer/Backward = `Axis 628`
IMUAccelerometer/Up = `Axis 629`
IMUAccelerometer/Down = `Axis 630`
IMUGyroscope/Pitch Up = `Axis 631`
IMUGyroscope/Pitch Down = `Axis 632`
IMUGyroscope/Roll Left = `Axis 633`
IMUGyroscope/Roll Right = `Axis 634`
IMUGyroscope/Yaw Left = `Axis 635`
IMUGyroscope/Yaw Right = `Axis 636`
Source = 1
Rumble/Motor = `Rumble 700`
[Wiimote2]
Device = Android/5/Touchscreen
Buttons/A = `Button 100`
Buttons/B = `Button 101`
Buttons/- = `Button 102`
Buttons/+ = `Button 103`
Buttons/Home = `Button 104`
Buttons/1 = `Button 105`
Buttons/2 = `Button 106`
D-Pad/Up = `Button 107`
D-Pad/Down = `Button 108`
D-Pad/Left = `Button 109`
D-Pad/Right = `Button 110`
IR/Up = `Axis 112`
IR/Down = `Axis 113`
IR/Left = `Axis 114`
IR/Right = `Axis 115`
IR/Forward = `Axis 116`
IR/Backward = `Axis 117`
IR/Hide = `Button 118`
IR/Total Pitch = 15
IR/Total Yaw = 15
IR/Vertical Offset = 10
Swing/Up = `Axis 120`
Swing/Down = `Axis 121`
Swing/Left = `Axis 122`
Swing/Right = `Axis 123`
Swing/Forward = `Axis 124`
Swing/Backward = `Axis 125`
Tilt/Forward = `Axis 127`
Tilt/Backward = `Axis 128`
Tilt/Left = `Axis 129`
Tilt/Right = `Axis 130`
Tilt/Modifier = `Button 131`
Tilt/Modifier/Range = 50,000000
Shake/X = `Button 132`
Shake/Y = `Button 133`
Shake/Z = `Button 134`
Extension = None
Nunchuk/Buttons/C = `Button 200`
Nunchuk/Buttons/Z = `Button 201`
Nunchuk/Stick/Up = `Axis 203`
Nunchuk/Stick/Down = `Axis 204`
Nunchuk/Stick/Left = `Axis 205`
Nunchuk/Stick/Right = `Axis 206`
Nunchuk/Swing/Up = `Axis 208`
Nunchuk/Swing/Down = `Axis 209`
Nunchuk/Swing/Left = `Axis 210`
Nunchuk/Swing/Right = `Axis 211`
Nunchuk/Swing/Forward = `Axis 212`
Nunchuk/Swing/Backward = `Axis 213`
Nunchuk/Tilt/Forward = `Axis 215`
Nunchuk/Tilt/Backward = `Axis 216`
Nunchuk/Tilt/Left = `Axis 217`
Nunchuk/Tilt/Right = `Axis 218`
Nunchuk/Tilt/Modifier = `Button 219`
Nunchuk/Tilt/Modifier/Range = 50,000000
Nunchuk/Shake/X = `Button 220`
Nunchuk/Shake/Y = `Button 221`
Nunchuk/Shake/Z = `Button 222`
Classic/Buttons/A = `Button 300`
Classic/Buttons/B = `Button 301`
Classic/Buttons/X = `Button 302`
Classic/Buttons/Y = `Button 303`
Classic/Buttons/- = `Button 304`
Classic/Buttons/+ = `Button 305`
Classic/Buttons/Home = `Button 306`
Classic/Buttons/ZL = `Button 307`
Classic/Buttons/ZR = `Button 308`
Classic/D-Pad/Up = `Button 309`
Classic/D-Pad/Down = `Button 310`
Classic/D-Pad/Left = `Button 311`
Classic/D-Pad/Right = `Button 312`
Classic/Left Stick/Up = `Axis 314`
Classic/Left Stick/Down = `Axis 315`
Classic/Left Stick/Left = `Axis 316`
Classic/Left Stick/Right = `Axis 317`
Classic/Right Stick/Up = `Axis 319`
Classic/Right Stick/Down = `Axis 320`
Classic/Right Stick/Left = `Axis 321`
Classic/Right Stick/Right = `Axis 322`
Classic/Triggers/L = `Axis 323`
Classic/Triggers/R = `Axis 324`
Classic/Triggers/Threshold = 90,000000
Guitar/Buttons/- = `Button 400`
Guitar/Buttons/+ = `Button 401`
Guitar/Frets/Green = `Button 402`
Guitar/Frets/Red = `Button 403`
Guitar/Frets/Yellow = `Button 404`
Guitar/Frets/Blue = `Button 405`
Guitar/Frets/Orange = `Button 406`
Guitar/Strum/Up = `Button 407`
Guitar/Strum/Down = `Button 408`
Guitar/Stick/Up = `Axis 410`
Guitar/Stick/Down = `Axis 411`
Guitar/Stick/Left = `Axis 412`
Guitar/Stick/Right = `Axis 413`
Guitar/Whammy/Bar = `Axis 414`
Drums/Buttons/- = `Button 500`
Drums/Buttons/+ = `Button 501`
Drums/Pads/Red = `Button 502`
Drums/Pads/Yellow = `Button 503`
Drums/Pads/Blue = `Button 504`
Drums/Pads/Green = `Button 505`
Drums/Pads/Orange = `Button 506`
Drums/Pads/Bass = `Button 507`
Drums/Stick/Up = `Axis 509`
Drums/Stick/Down = `Axis 510`
Drums/Stick/Left = `Axis 511`
Drums/Stick/Right = `Axis 512`
Turntable/Buttons/Green Left = `Button 600`
Turntable/Buttons/Red Left = `Button 601`
Turntable/Buttons/Blue Left = `Button 602`
Turntable/Buttons/Green Right = `Button 603`
Turntable/Buttons/Red Right = `Button 604`
Turntable/Buttons/Blue Right = `Button 605`
Turntable/Buttons/- = `Button 606`
Turntable/Buttons/+ = `Button 607`
Turntable/Buttons/Home = `Button 608`
Turntable/Buttons/Euphoria = `Button 609`
Turntable/Table Left/Left = `Axis 611`
Turntable/Table Left/Right = `Axis 612`
Turntable/Table Right/Left = `Axis 614`
Turntable/Table Right/Right = `Axis 615`
Turntable/Stick/Up = `Axis 617`
Turntable/Stick/Down = `Axis 618`
Turntable/Stick/Left = `Axis 619`
Turntable/Stick/Right = `Axis 620`
Turntable/Effect/Dial = `Axis 621`
Turntable/Crossfade/Left = `Axis 623`
Turntable/Crossfade/Right = `Axis 624`
IMUAccelerometer/Left = `Axis 625`
IMUAccelerometer/Right = `Axis 626`
IMUAccelerometer/Forward = `Axis 627`
IMUAccelerometer/Backward = `Axis 628`
IMUAccelerometer/Up = `Axis 629`
IMUAccelerometer/Down = `Axis 630`
IMUGyroscope/Pitch Up = `Axis 631`
IMUGyroscope/Pitch Down = `Axis 632`
IMUGyroscope/Roll Left = `Axis 633`
IMUGyroscope/Roll Right = `Axis 634`
IMUGyroscope/Yaw Left = `Axis 635`
IMUGyroscope/Yaw Right = `Axis 636`
Source = 0
Rumble/Motor = `Rumble 700`
[Wiimote3]
Device = Android/6/Touchscreen
Buttons/A = `Button 100`
Buttons/B = `Button 101`
Buttons/- = `Button 102`
Buttons/+ = `Button 103`
Buttons/Home = `Button 104`
Buttons/1 = `Button 105`
Buttons/2 = `Button 106`
D-Pad/Up = `Button 107`
D-Pad/Down = `Button 108`
D-Pad/Left = `Button 109`
D-Pad/Right = `Button 110`
IR/Up = `Axis 112`
IR/Down = `Axis 113`
IR/Left = `Axis 114`
IR/Right = `Axis 115`
IR/Forward = `Axis 116`
IR/Backward = `Axis 117`
IR/Hide = `Button 118`
IR/Total Pitch = 15
IR/Total Yaw = 15
IR/Vertical Offset = 10
Swing/Up = `Axis 120`
Swing/Down = `Axis 121`
Swing/Left = `Axis 122`
Swing/Right = `Axis 123`
Swing/Forward = `Axis 124`
Swing/Backward = `Axis 125`
Tilt/Forward = `Axis 127`
Tilt/Backward = `Axis 128`
Tilt/Left = `Axis 129`
Tilt/Right = `Axis 130`
Tilt/Modifier = `Button 131`
Tilt/Modifier/Range = 50,000000
Shake/X = `Button 132`
Shake/Y = `Button 133`
Shake/Z = `Button 134`
Extension = None
Nunchuk/Buttons/C = `Button 200`
Nunchuk/Buttons/Z = `Button 201`
Nunchuk/Stick/Up = `Axis 203`
Nunchuk/Stick/Down = `Axis 204`
Nunchuk/Stick/Left = `Axis 205`
Nunchuk/Stick/Right = `Axis 206`
Nunchuk/Swing/Up = `Axis 208`
Nunchuk/Swing/Down = `Axis 209`
Nunchuk/Swing/Left = `Axis 210`
Nunchuk/Swing/Right = `Axis 211`
Nunchuk/Swing/Forward = `Axis 212`
Nunchuk/Swing/Backward = `Axis 213`
Nunchuk/Tilt/Forward = `Axis 215`
Nunchuk/Tilt/Backward = `Axis 216`
Nunchuk/Tilt/Left = `Axis 217`
Nunchuk/Tilt/Right = `Axis 218`
Nunchuk/Tilt/Modifier = `Button 219`
Nunchuk/Tilt/Modifier/Range = 50,000000
Nunchuk/Shake/X = `Button 220`
Nunchuk/Shake/Y = `Button 221`
Nunchuk/Shake/Z = `Button 222`
Classic/Buttons/A = `Button 300`
Classic/Buttons/B = `Button 301`
Classic/Buttons/X = `Button 302`
Classic/Buttons/Y = `Button 303`
Classic/Buttons/- = `Button 304`
Classic/Buttons/+ = `Button 305`
Classic/Buttons/Home = `Button 306`
Classic/Buttons/ZL = `Button 307`
Classic/Buttons/ZR = `Button 308`
Classic/D-Pad/Up = `Button 309`
Classic/D-Pad/Down = `Button 310`
Classic/D-Pad/Left = `Button 311`
Classic/D-Pad/Right = `Button 312`
Classic/Left Stick/Up = `Axis 314`
Classic/Left Stick/Down = `Axis 315`
Classic/Left Stick/Left = `Axis 316`
Classic/Left Stick/Right = `Axis 317`
Classic/Right Stick/Up = `Axis 319`
Classic/Right Stick/Down = `Axis 320`
Classic/Right Stick/Left = `Axis 321`
Classic/Right Stick/Right = `Axis 322`
Classic/Triggers/L = `Axis 323`
Classic/Triggers/R = `Axis 324`
Classic/Triggers/Threshold = 90,000000
Guitar/Buttons/- = `Button 400`
Guitar/Buttons/+ = `Button 401`
Guitar/Frets/Green = `Button 402`
Guitar/Frets/Red = `Button 403`
Guitar/Frets/Yellow = `Button 404`
Guitar/Frets/Blue = `Button 405`
Guitar/Frets/Orange = `Button 406`
Guitar/Strum/Up = `Button 407`
Guitar/Strum/Down = `Button 408`
Guitar/Stick/Up = `Axis 410`
Guitar/Stick/Down = `Axis 411`
Guitar/Stick/Left = `Axis 412`
Guitar/Stick/Right = `Axis 413`
Guitar/Whammy/Bar = `Axis 414`
Drums/Buttons/- = `Button 500`
Drums/Buttons/+ = `Button 501`
Drums/Pads/Red = `Button 502`
Drums/Pads/Yellow = `Button 503`
Drums/Pads/Blue = `Button 504`
Drums/Pads/Green = `Button 505`
Drums/Pads/Orange = `Button 506`
Drums/Pads/Bass = `Button 507`
Drums/Stick/Up = `Axis 509`
Drums/Stick/Down = `Axis 510`
Drums/Stick/Left = `Axis 511`
Drums/Stick/Right = `Axis 512`
Turntable/Buttons/Green Left = `Button 600`
Turntable/Buttons/Red Left = `Button 601`
Turntable/Buttons/Blue Left = `Button 602`
Turntable/Buttons/Green Right = `Button 603`
Turntable/Buttons/Red Right = `Button 604`
Turntable/Buttons/Blue Right = `Button 605`
Turntable/Buttons/- = `Button 606`
Turntable/Buttons/+ = `Button 607`
Turntable/Buttons/Home = `Button 608`
Turntable/Buttons/Euphoria = `Button 609`
Turntable/Table Left/Left = `Axis 611`
Turntable/Table Left/Right = `Axis 612`
Turntable/Table Right/Left = `Axis 614`
Turntable/Table Right/Right = `Axis 615`
Turntable/Stick/Up = `Axis 617`
Turntable/Stick/Down = `Axis 618`
Turntable/Stick/Left = `Axis 619`
Turntable/Stick/Right = `Axis 620`
Turntable/Effect/Dial = `Axis 621`
Turntable/Crossfade/Left = `Axis 623`
Turntable/Crossfade/Right = `Axis 624`
IMUAccelerometer/Left = `Axis 625`
IMUAccelerometer/Right = `Axis 626`
IMUAccelerometer/Forward = `Axis 627`
IMUAccelerometer/Backward = `Axis 628`
IMUAccelerometer/Up = `Axis 629`
IMUAccelerometer/Down = `Axis 630`
IMUGyroscope/Pitch Up = `Axis 631`
IMUGyroscope/Pitch Down = `Axis 632`
IMUGyroscope/Roll Left = `Axis 633`
IMUGyroscope/Roll Right = `Axis 634`
IMUGyroscope/Yaw Left = `Axis 635`
IMUGyroscope/Yaw Right = `Axis 636`
Source = 0
Rumble/Motor = `Rumble 700`
[Wiimote4]
Device = Android/7/Touchscreen
Buttons/A = `Button 100`
Buttons/B = `Button 101`
Buttons/- = `Button 102`
Buttons/+ = `Button 103`
Buttons/Home = `Button 104`
Buttons/1 = `Button 105`
Buttons/2 = `Button 106`
D-Pad/Up = `Button 107`
D-Pad/Down = `Button 108`
D-Pad/Left = `Button 109`
D-Pad/Right = `Button 110`
IR/Up = `Axis 112`
IR/Down = `Axis 113`
IR/Left = `Axis 114`
IR/Right = `Axis 115`
IR/Forward = `Axis 116`
IR/Backward = `Axis 117`
IR/Hide = `Button 118`
IR/Total Pitch = 15
IR/Total Yaw = 15
IR/Vertical Offset = 10
Swing/Up = `Axis 120`
Swing/Down = `Axis 121`
Swing/Left = `Axis 122`
Swing/Right = `Axis 123`
Swing/Forward = `Axis 124`
Swing/Backward = `Axis 125`
Tilt/Forward = `Axis 127`
Tilt/Backward = `Axis 128`
Tilt/Left = `Axis 129`
Tilt/Right = `Axis 130`
Tilt/Modifier = `Button 131`
Tilt/Modifier/Range = 50,000000
Shake/X = `Button 132`
Shake/Y = `Button 133`
Shake/Z = `Button 134`
Extension = None
Nunchuk/Buttons/C = `Button 200`
Nunchuk/Buttons/Z = `Button 201`
Nunchuk/Stick/Up = `Axis 203`
Nunchuk/Stick/Down = `Axis 204`
Nunchuk/Stick/Left = `Axis 205`
Nunchuk/Stick/Right = `Axis 206`
Nunchuk/Swing/Up = `Axis 208`
Nunchuk/Swing/Down = `Axis 209`
Nunchuk/Swing/Left = `Axis 210`
Nunchuk/Swing/Right = `Axis 211`
Nunchuk/Swing/Forward = `Axis 212`
Nunchuk/Swing/Backward = `Axis 213`
Nunchuk/Tilt/Forward = `Axis 215`
Nunchuk/Tilt/Backward = `Axis 216`
Nunchuk/Tilt/Left = `Axis 217`
Nunchuk/Tilt/Right = `Axis 218`
Nunchuk/Tilt/Modifier = `Button 219`
Nunchuk/Tilt/Modifier/Range = 50,000000
Nunchuk/Shake/X = `Button 220`
Nunchuk/Shake/Y = `Button 221`
Nunchuk/Shake/Z = `Button 222`
Classic/Buttons/A = `Button 300`
Classic/Buttons/B = `Button 301`
Classic/Buttons/X = `Button 302`
Classic/Buttons/Y = `Button 303`
Classic/Buttons/- = `Button 304`
Classic/Buttons/+ = `Button 305`
Classic/Buttons/Home = `Button 306`
Classic/Buttons/ZL = `Button 307`
Classic/Buttons/ZR = `Button 308`
Classic/D-Pad/Up = `Button 309`
Classic/D-Pad/Down = `Button 310`
Classic/D-Pad/Left = `Button 311`
Classic/D-Pad/Right = `Button 312`
Classic/Left Stick/Up = `Axis 314`
Classic/Left Stick/Down = `Axis 315`
Classic/Left Stick/Left = `Axis 316`
Classic/Left Stick/Right = `Axis 317`
Classic/Right Stick/Up = `Axis 319`
Classic/Right Stick/Down = `Axis 320`
Classic/Right Stick/Left = `Axis 321`
Classic/Right Stick/Right = `Axis 322`
Classic/Triggers/L = `Axis 323`
Classic/Triggers/R = `Axis 324`
Classic/Triggers/Threshold = 90,000000
Guitar/Buttons/- = `Button 400`
Guitar/Buttons/+ = `Button 401`
Guitar/Frets/Green = `Button 402`
Guitar/Frets/Red = `Button 403`
Guitar/Frets/Yellow = `Button 404`
Guitar/Frets/Blue = `Button 405`
Guitar/Frets/Orange = `Button 406`
Guitar/Strum/Up = `Button 407`
Guitar/Strum/Down = `Button 408`
Guitar/Stick/Up = `Axis 410`
Guitar/Stick/Down = `Axis 411`
Guitar/Stick/Left = `Axis 412`
Guitar/Stick/Right = `Axis 413`
Guitar/Whammy/Bar = `Axis 414`
Drums/Buttons/- = `Button 500`
Drums/Buttons/+ = `Button 501`
Drums/Pads/Red = `Button 502`
Drums/Pads/Yellow = `Button 503`
Drums/Pads/Blue = `Button 504`
Drums/Pads/Green = `Button 505`
Drums/Pads/Orange = `Button 506`
Drums/Pads/Bass = `Button 507`
Drums/Stick/Up = `Axis 509`
Drums/Stick/Down = `Axis 510`
Drums/Stick/Left = `Axis 511`
Drums/Stick/Right = `Axis 512`
Turntable/Buttons/Green Left = `Button 600`
Turntable/Buttons/Red Left = `Button 601`
Turntable/Buttons/Blue Left = `Button 602`
Turntable/Buttons/Green Right = `Button 603`
Turntable/Buttons/Red Right = `Button 604`
Turntable/Buttons/Blue Right = `Button 605`
Turntable/Buttons/- = `Button 606`
Turntable/Buttons/+ = `Button 607`
Turntable/Buttons/Home = `Button 608`
Turntable/Buttons/Euphoria = `Button 609`
Turntable/Table Left/Left = `Axis 611`
Turntable/Table Left/Right = `Axis 612`
Turntable/Table Right/Left = `Axis 614`
Turntable/Table Right/Right = `Axis 615`
Turntable/Stick/Up = `Axis 617`
Turntable/Stick/Down = `Axis 618`
Turntable/Stick/Left = `Axis 619`
Turntable/Stick/Right = `Axis 620`
Turntable/Effect/Dial = `Axis 621`
Turntable/Crossfade/Left = `Axis 623`
Turntable/Crossfade/Right = `Axis 624`
IMUAccelerometer/Left = `Axis 625`
IMUAccelerometer/Right = `Axis 626`
IMUAccelerometer/Forward = `Axis 627`
IMUAccelerometer/Backward = `Axis 628`
IMUAccelerometer/Up = `Axis 629`
IMUAccelerometer/Down = `Axis 630`
IMUGyroscope/Pitch Up = `Axis 631`
IMUGyroscope/Pitch Down = `Axis 632`
IMUGyroscope/Roll Left = `Axis 633`
IMUGyroscope/Roll Right = `Axis 634`
IMUGyroscope/Yaw Left = `Axis 635`
IMUGyroscope/Yaw Right = `Axis 636`
Source = 0
Rumble/Motor = `Rumble 700`

View File

@ -1,144 +0,0 @@
[Profile]
Device = Android/4/Touchscreen
Buttons/A = `Button 100`
Buttons/B = `Button 101`
Buttons/- = `Button 102`
Buttons/+ = `Button 103`
Buttons/Home = `Button 104`
Buttons/1 = `Button 105`
Buttons/2 = `Button 106`
D-Pad/Up = `Button 107`
D-Pad/Down = `Button 108`
D-Pad/Left = `Button 109`
D-Pad/Right = `Button 110`
IR/Up = `Axis 112`
IR/Down = `Axis 113`
IR/Left = `Axis 114`
IR/Right = `Axis 115`
IR/Forward = `Axis 116`
IR/Backward = `Axis 117`
IR/Hide = `Button 118`
IR/Total Pitch = 20
IR/Total Yaw = 25
IR/Vertical Offset = 10
Swing/Up = `Axis 120`
Swing/Down = `Axis 121`
Swing/Left = `Axis 122`
Swing/Right = `Axis 123`
Swing/Forward = `Axis 124`
Swing/Backward = `Axis 125`
Tilt/Forward = `Axis 127`
Tilt/Backward = `Axis 128`
Tilt/Left = `Axis 129`
Tilt/Right = `Axis 130`
Tilt/Modifier = `Button 131`
Tilt/Modifier/Range = 50,000000
Shake/X = `Button 132`
Shake/Y = `Button 133`
Shake/Z = `Button 134`
Extension = Nunchuk
Nunchuk/Buttons/C = `Button 200`
Nunchuk/Buttons/Z = `Button 201`
Nunchuk/Stick/Up = `Axis 203`
Nunchuk/Stick/Down = `Axis 204`
Nunchuk/Stick/Left = `Axis 205`
Nunchuk/Stick/Right = `Axis 206`
Nunchuk/Swing/Up = `Axis 208`
Nunchuk/Swing/Down = `Axis 209`
Nunchuk/Swing/Left = `Axis 210`
Nunchuk/Swing/Right = `Axis 211`
Nunchuk/Swing/Forward = `Axis 212`
Nunchuk/Swing/Backward = `Axis 213`
Nunchuk/Tilt/Forward = `Axis 215`
Nunchuk/Tilt/Backward = `Axis 216`
Nunchuk/Tilt/Left = `Axis 217`
Nunchuk/Tilt/Right = `Axis 218`
Nunchuk/Tilt/Modifier = `Button 219`
Nunchuk/Tilt/Modifier/Range = 50,000000
Nunchuk/Shake/X = `Button 220`
Nunchuk/Shake/Y = `Button 221`
Nunchuk/Shake/Z = `Button 222`
Classic/Buttons/A = `Button 300`
Classic/Buttons/B = `Button 301`
Classic/Buttons/X = `Button 302`
Classic/Buttons/Y = `Button 303`
Classic/Buttons/- = `Button 304`
Classic/Buttons/+ = `Button 305`
Classic/Buttons/Home = `Button 306`
Classic/Buttons/ZL = `Button 307`
Classic/Buttons/ZR = `Button 308`
Classic/D-Pad/Up = `Button 309`
Classic/D-Pad/Down = `Button 310`
Classic/D-Pad/Left = `Button 311`
Classic/D-Pad/Right = `Button 312`
Classic/Left Stick/Up = `Axis 314`
Classic/Left Stick/Down = `Axis 315`
Classic/Left Stick/Left = `Axis 316`
Classic/Left Stick/Right = `Axis 317`
Classic/Right Stick/Up = `Axis 319`
Classic/Right Stick/Down = `Axis 320`
Classic/Right Stick/Left = `Axis 321`
Classic/Right Stick/Right = `Axis 322`
Classic/Triggers/L = `Axis 323`
Classic/Triggers/R = `Axis 324`
Classic/Triggers/Threshold = 90,000000
Guitar/Buttons/- = `Button 400`
Guitar/Buttons/+ = `Button 401`
Guitar/Frets/Green = `Button 402`
Guitar/Frets/Red = `Button 403`
Guitar/Frets/Yellow = `Button 404`
Guitar/Frets/Blue = `Button 405`
Guitar/Frets/Orange = `Button 406`
Guitar/Strum/Up = `Button 407`
Guitar/Strum/Down = `Button 408`
Guitar/Stick/Up = `Axis 410`
Guitar/Stick/Down = `Axis 411`
Guitar/Stick/Left = `Axis 412`
Guitar/Stick/Right = `Axis 413`
Guitar/Whammy/Bar = `Axis 414`
Drums/Buttons/- = `Button 500`
Drums/Buttons/+ = `Button 501`
Drums/Pads/Red = `Button 502`
Drums/Pads/Yellow = `Button 503`
Drums/Pads/Blue = `Button 504`
Drums/Pads/Green = `Button 505`
Drums/Pads/Orange = `Button 506`
Drums/Pads/Bass = `Button 507`
Drums/Stick/Up = `Axis 509`
Drums/Stick/Down = `Axis 510`
Drums/Stick/Left = `Axis 511`
Drums/Stick/Right = `Axis 512`
Turntable/Buttons/Green Left = `Button 600`
Turntable/Buttons/Red Left = `Button 601`
Turntable/Buttons/Blue Left = `Button 602`
Turntable/Buttons/Green Right = `Button 603`
Turntable/Buttons/Red Right = `Button 604`
Turntable/Buttons/Blue Right = `Button 605`
Turntable/Buttons/- = `Button 606`
Turntable/Buttons/+ = `Button 607`
Turntable/Buttons/Home = `Button 608`
Turntable/Buttons/Euphoria = `Button 609`
Turntable/Table Left/Left = `Axis 611`
Turntable/Table Left/Right = `Axis 612`
Turntable/Table Right/Left = `Axis 614`
Turntable/Table Right/Right = `Axis 615`
Turntable/Stick/Up = `Axis 617`
Turntable/Stick/Down = `Axis 618`
Turntable/Stick/Left = `Axis 619`
Turntable/Stick/Right = `Axis 620`
Turntable/Effect/Dial = `Axis 621`
Turntable/Crossfade/Left = `Axis 623`
Turntable/Crossfade/Right = `Axis 624`
IMUAccelerometer/Left = `Axis 625`
IMUAccelerometer/Right = `Axis 626`
IMUAccelerometer/Forward = `Axis 627`
IMUAccelerometer/Backward = `Axis 628`
IMUAccelerometer/Up = `Axis 629`
IMUAccelerometer/Down = `Axis 630`
IMUGyroscope/Pitch Up = `Axis 631`
IMUGyroscope/Pitch Down = `Axis 632`
IMUGyroscope/Roll Left = `Axis 633`
IMUGyroscope/Roll Right = `Axis 634`
IMUGyroscope/Yaw Left = `Axis 635`
IMUGyroscope/Yaw Right = `Axis 636`
Rumble/Motor = `Rumble 700`

View File

@ -18,7 +18,6 @@ import org.dolphinemu.dolphinemu.activities.EmulationActivity;
import org.dolphinemu.dolphinemu.dialogs.AlertMessage;
import org.dolphinemu.dolphinemu.utils.CompressCallback;
import org.dolphinemu.dolphinemu.utils.Log;
import org.dolphinemu.dolphinemu.utils.Rumble;
import java.lang.ref.WeakReference;
import java.util.LinkedHashMap;
@ -44,7 +43,7 @@ public final class NativeLibrary
}
/**
* Button type for use in onTouchEvent
* Button type, for legacy use only
*/
public static final class ButtonType
{
@ -234,52 +233,6 @@ public final class NativeLibrary
// Disallows instantiation.
}
/**
* Default touchscreen device
*/
public static final String TouchScreenDevice = "Touchscreen";
/**
* Handles button press events for a gamepad.
*
* @param Device The input descriptor of the gamepad.
* @param Button Key code identifying which button was pressed.
* @param Action Mask identifying which action is happening (button pressed down, or button released).
* @return If we handled the button press.
*/
public static native boolean onGamePadEvent(String Device, int Button, int Action);
/**
* Handles gamepad movement events.
*
* @param Device The device ID of the gamepad.
* @param Axis The axis ID
* @param Value The value of the axis represented by the given ID.
*/
public static native void onGamePadMoveEvent(String Device, int Axis, float Value);
/**
* Rumble sent from native. Currently only supports phone rumble.
*
* @param padID Ignored for now. Future use would be to pass rumble to a connected controller
* @param state Ignored for now since phone rumble can't just be 'turned' on/off
*/
@Keep
public static void rumble(int padID, double state)
{
final EmulationActivity emulationActivity = sEmulationActivity.get();
if (emulationActivity == null)
{
Log.warning("[NativeLibrary] EmulationActivity is null");
return;
}
Rumble.checkRumble(padID, state);
}
public static native void SetMotionSensorsEnabled(boolean accelerometerEnabled,
boolean gyroscopeEnabled);
/**
* Gets the Dolphin version string.
*
@ -451,8 +404,6 @@ public final class NativeLibrary
*/
public static native void RefreshWiimotes();
public static native void ReloadWiimoteConfig();
public static native LinkedHashMap<String, String> GetLogTypeNames();
public static native void ReloadLoggerConfig();

View File

@ -2,7 +2,6 @@
package org.dolphinemu.dolphinemu.activities;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
@ -11,7 +10,6 @@ import android.os.Build;
import android.os.Bundle;
import android.util.Pair;
import android.util.SparseIntArray;
import android.view.InputDevice;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuItem;
@ -40,15 +38,15 @@ import org.dolphinemu.dolphinemu.NativeLibrary;
import org.dolphinemu.dolphinemu.R;
import org.dolphinemu.dolphinemu.databinding.ActivityEmulationBinding;
import org.dolphinemu.dolphinemu.databinding.DialogInputAdjustBinding;
import org.dolphinemu.dolphinemu.databinding.DialogIrSensitivityBinding;
import org.dolphinemu.dolphinemu.databinding.DialogSkylandersManagerBinding;
import org.dolphinemu.dolphinemu.features.input.model.ControllerInterface;
import org.dolphinemu.dolphinemu.features.input.model.DolphinSensorEventListener;
import org.dolphinemu.dolphinemu.features.settings.model.BooleanSetting;
import org.dolphinemu.dolphinemu.features.settings.model.IntSetting;
import org.dolphinemu.dolphinemu.features.settings.model.Settings;
import org.dolphinemu.dolphinemu.features.settings.model.StringSetting;
import org.dolphinemu.dolphinemu.features.settings.ui.MenuTag;
import org.dolphinemu.dolphinemu.features.settings.ui.SettingsActivity;
import org.dolphinemu.dolphinemu.features.settings.utils.SettingsFile;
import org.dolphinemu.dolphinemu.features.skylanders.SkylanderConfig;
import org.dolphinemu.dolphinemu.features.skylanders.model.Skylander;
import org.dolphinemu.dolphinemu.features.skylanders.ui.SkylanderSlot;
@ -61,14 +59,9 @@ import org.dolphinemu.dolphinemu.overlay.InputOverlayPointer;
import org.dolphinemu.dolphinemu.ui.main.MainPresenter;
import org.dolphinemu.dolphinemu.ui.main.ThemeProvider;
import org.dolphinemu.dolphinemu.utils.AfterDirectoryInitializationRunner;
import org.dolphinemu.dolphinemu.utils.ControllerMappingHelper;
import org.dolphinemu.dolphinemu.utils.FileBrowserHelper;
import org.dolphinemu.dolphinemu.utils.IniFile;
import org.dolphinemu.dolphinemu.utils.MotionListener;
import org.dolphinemu.dolphinemu.utils.Rumble;
import org.dolphinemu.dolphinemu.utils.ThemeHelper;
import java.io.File;
import java.lang.annotation.Retention;
import java.util.ArrayList;
import java.util.List;
@ -89,7 +82,6 @@ public final class EmulationActivity extends AppCompatActivity implements ThemeP
private EmulationFragment mEmulationFragment;
private SharedPreferences mPreferences;
private MotionListener mMotionListener;
private Settings mSettings;
@ -127,9 +119,9 @@ public final class EmulationActivity extends AppCompatActivity implements ThemeP
MENU_ACTION_LOAD_SLOT3, MENU_ACTION_LOAD_SLOT4, MENU_ACTION_LOAD_SLOT5,
MENU_ACTION_LOAD_SLOT6, MENU_ACTION_EXIT, MENU_ACTION_CHANGE_DISC,
MENU_ACTION_RESET_OVERLAY, MENU_SET_IR_RECENTER, MENU_SET_IR_MODE,
MENU_SET_IR_SENSITIVITY, MENU_ACTION_CHOOSE_DOUBLETAP, MENU_ACTION_MOTION_CONTROLS,
MENU_ACTION_PAUSE_EMULATION, MENU_ACTION_UNPAUSE_EMULATION, MENU_ACTION_OVERLAY_CONTROLS,
MENU_ACTION_SETTINGS, MENU_ACTION_SKYLANDERS})
MENU_ACTION_CHOOSE_DOUBLETAP, MENU_ACTION_PAUSE_EMULATION,
MENU_ACTION_UNPAUSE_EMULATION, MENU_ACTION_OVERLAY_CONTROLS, MENU_ACTION_SETTINGS,
MENU_ACTION_SKYLANDERS})
public @interface MenuAction
{
}
@ -159,13 +151,10 @@ public final class EmulationActivity extends AppCompatActivity implements ThemeP
public static final int MENU_ACTION_EXIT = 22;
public static final int MENU_ACTION_CHANGE_DISC = 23;
public static final int MENU_ACTION_JOYSTICK_REL_CENTER = 24;
public static final int MENU_ACTION_RUMBLE = 25;
public static final int MENU_ACTION_RESET_OVERLAY = 26;
public static final int MENU_SET_IR_RECENTER = 27;
public static final int MENU_SET_IR_MODE = 28;
public static final int MENU_SET_IR_SENSITIVITY = 29;
public static final int MENU_ACTION_CHOOSE_DOUBLETAP = 30;
public static final int MENU_ACTION_MOTION_CONTROLS = 31;
public static final int MENU_ACTION_PAUSE_EMULATION = 32;
public static final int MENU_ACTION_UNPAUSE_EMULATION = 33;
public static final int MENU_ACTION_OVERLAY_CONTROLS = 34;
@ -194,19 +183,14 @@ public final class EmulationActivity extends AppCompatActivity implements ThemeP
EmulationActivity.MENU_ACTION_CHOOSE_CONTROLLER);
buttonsActionsMap.append(R.id.menu_emulation_joystick_rel_center,
EmulationActivity.MENU_ACTION_JOYSTICK_REL_CENTER);
buttonsActionsMap.append(R.id.menu_emulation_rumble, EmulationActivity.MENU_ACTION_RUMBLE);
buttonsActionsMap
.append(R.id.menu_emulation_reset_overlay, EmulationActivity.MENU_ACTION_RESET_OVERLAY);
buttonsActionsMap.append(R.id.menu_emulation_ir_recenter,
EmulationActivity.MENU_SET_IR_RECENTER);
buttonsActionsMap.append(R.id.menu_emulation_set_ir_mode,
EmulationActivity.MENU_SET_IR_MODE);
buttonsActionsMap.append(R.id.menu_emulation_set_ir_sensitivity,
EmulationActivity.MENU_SET_IR_SENSITIVITY);
buttonsActionsMap.append(R.id.menu_emulation_choose_doubletap,
EmulationActivity.MENU_ACTION_CHOOSE_DOUBLETAP);
buttonsActionsMap.append(R.id.menu_emulation_motion_controls,
EmulationActivity.MENU_ACTION_MOTION_CONTROLS);
}
public static void launch(FragmentActivity activity, String filePath, boolean riivolution)
@ -302,30 +286,6 @@ public final class EmulationActivity extends AppCompatActivity implements ThemeP
sIgnoreLaunchRequests = false;
}
public static void updateWiimoteNewIniPreferences(Context context)
{
updateWiimoteNewController(InputOverlay.getConfiguredControllerType(context), context);
updateWiimoteNewImuIr(IntSetting.MAIN_MOTION_CONTROLS.getIntGlobal());
}
private static void updateWiimoteNewController(int value, Context context)
{
File wiimoteNewFile = SettingsFile.getSettingsFile(Settings.FILE_WIIMOTE);
IniFile wiimoteNewIni = new IniFile(wiimoteNewFile);
wiimoteNewIni.setString("Wiimote1", "Extension",
context.getResources().getStringArray(R.array.controllersValues)[value]);
wiimoteNewIni.setBoolean("Wiimote1", "Options/Sideways Wiimote", value == 2);
wiimoteNewIni.save(wiimoteNewFile);
}
private static void updateWiimoteNewImuIr(int value)
{
File wiimoteNewFile = SettingsFile.getSettingsFile(Settings.FILE_WIIMOTE);
IniFile wiimoteNewIni = new IniFile(wiimoteNewFile);
wiimoteNewIni.setBoolean("Wiimote1", "IMUIR/Enabled", value != 1);
wiimoteNewIni.save(wiimoteNewFile);
}
@Override
protected void onCreate(Bundle savedInstanceState)
{
@ -359,13 +319,9 @@ public final class EmulationActivity extends AppCompatActivity implements ThemeP
updateOrientation();
mMotionListener = new MotionListener(this);
// Set these options now so that the SurfaceView the game renders into is the right size.
enableFullscreenImmersive();
Rumble.initRumble(this);
ActivityEmulationBinding binding = ActivityEmulationBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
@ -453,15 +409,14 @@ public final class EmulationActivity extends AppCompatActivity implements ThemeP
updateOrientation();
if (NativeLibrary.IsGameMetadataValid())
updateMotionListener();
DolphinSensorEventListener.setDeviceRotation(
getWindowManager().getDefaultDisplay().getRotation());
}
@Override
protected void onPause()
{
super.onPause();
mMotionListener.disable();
}
@Override
@ -481,17 +436,8 @@ public final class EmulationActivity extends AppCompatActivity implements ThemeP
}
setTitle(NativeLibrary.GetCurrentTitleDescription());
updateMotionListener();
mEmulationFragment.refreshInputOverlay();
}
private void updateMotionListener()
{
if (NativeLibrary.IsEmulatingWii() && IntSetting.MAIN_MOTION_CONTROLS.getInt(mSettings) != 2)
mMotionListener.enable();
else
mMotionListener.disable();
mEmulationFragment.refreshInputOverlay(mSettings);
}
@Override
@ -644,8 +590,6 @@ public final class EmulationActivity extends AppCompatActivity implements ThemeP
// Populate the switch value for joystick center on touch
menu.findItem(R.id.menu_emulation_joystick_rel_center)
.setChecked(BooleanSetting.MAIN_JOYSTICK_REL_CENTER.getBoolean(mSettings));
menu.findItem(R.id.menu_emulation_rumble)
.setChecked(BooleanSetting.MAIN_PHONE_RUMBLE.getBoolean(mSettings));
if (wii)
{
menu.findItem(R.id.menu_emulation_ir_recenter)
@ -685,10 +629,6 @@ public final class EmulationActivity extends AppCompatActivity implements ThemeP
item.setChecked(!item.isChecked());
toggleJoystickRelCenter(item.isChecked());
break;
case MENU_ACTION_RUMBLE:
item.setChecked(!item.isChecked());
toggleRumble(item.isChecked());
break;
case MENU_SET_IR_RECENTER:
item.setChecked(!item.isChecked());
toggleRecenter(item.isChecked());
@ -721,7 +661,7 @@ public final class EmulationActivity extends AppCompatActivity implements ThemeP
adjustScale();
break;
// (Wii games only) Change the controller for the input overlay.
// Change the controller for the input overlay.
case MENU_ACTION_CHOOSE_CONTROLLER:
chooseController();
break;
@ -823,18 +763,10 @@ public final class EmulationActivity extends AppCompatActivity implements ThemeP
setIRMode();
break;
case MENU_SET_IR_SENSITIVITY:
setIRSensitivity();
break;
case MENU_ACTION_CHOOSE_DOUBLETAP:
chooseDoubleTapButton();
break;
case MENU_ACTION_MOTION_CONTROLS:
showMotionControlsOptions();
break;
case MENU_ACTION_SETTINGS:
SettingsActivity.launch(this, MenuTag.SETTINGS);
break;
@ -859,12 +791,6 @@ public final class EmulationActivity extends AppCompatActivity implements ThemeP
BooleanSetting.MAIN_JOYSTICK_REL_CENTER.setBoolean(mSettings, state);
}
private void toggleRumble(boolean state)
{
BooleanSetting.MAIN_PHONE_RUMBLE.setBoolean(mSettings, state);
Rumble.setPhoneVibrator(state, this);
}
private void toggleRecenter(boolean state)
{
BooleanSetting.MAIN_IR_ALWAYS_RECENTER.setBoolean(mSettings, state);
@ -889,26 +815,15 @@ public final class EmulationActivity extends AppCompatActivity implements ThemeP
@Override
public boolean dispatchKeyEvent(KeyEvent event)
{
if (mMenuVisible || event.getKeyCode() == KeyEvent.KEYCODE_BACK)
if (!mMenuVisible)
{
return super.dispatchKeyEvent(event);
if (ControllerInterface.dispatchKeyEvent(event))
{
return true;
}
}
int action;
switch (event.getAction())
{
case KeyEvent.ACTION_DOWN:
action = NativeLibrary.ButtonState.PRESSED;
break;
case KeyEvent.ACTION_UP:
action = NativeLibrary.ButtonState.RELEASED;
break;
default:
return false;
}
InputDevice input = event.getDevice();
return NativeLibrary.onGamePadEvent(input.getDescriptor(), event.getKeyCode(), action);
return super.dispatchKeyEvent(event);
}
private void toggleControls()
@ -916,9 +831,9 @@ public final class EmulationActivity extends AppCompatActivity implements ThemeP
MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(this)
.setTitle(R.string.emulation_toggle_controls);
int currentController = InputOverlay.getConfiguredControllerType(this);
int currentController = InputOverlay.getConfiguredControllerType(mSettings);
if (!NativeLibrary.IsEmulatingWii() || currentController == InputOverlay.OVERLAY_GAMECUBE)
if (currentController == InputOverlay.OVERLAY_GAMECUBE)
{
boolean[] gcEnabledButtons = new boolean[11];
String gcSettingBase = "MAIN_BUTTON_TOGGLE_GC_";
@ -972,7 +887,7 @@ public final class EmulationActivity extends AppCompatActivity implements ThemeP
builder.setNeutralButton(R.string.emulation_toggle_all,
(dialogInterface, i) -> mEmulationFragment.toggleInputOverlayVisibility(mSettings))
.setPositiveButton(R.string.ok, (dialogInterface, i) ->
mEmulationFragment.refreshInputOverlay())
mEmulationFragment.refreshInputOverlay(mSettings))
.show();
}
@ -980,7 +895,7 @@ public final class EmulationActivity extends AppCompatActivity implements ThemeP
{
int currentValue = IntSetting.MAIN_DOUBLE_TAP_BUTTON.getInt(mSettings);
int buttonList = InputOverlay.getConfiguredControllerType(this) ==
int buttonList = InputOverlay.getConfiguredControllerType(mSettings) ==
InputOverlay.OVERLAY_WIIMOTE_CLASSIC ? R.array.doubleTapWithClassic : R.array.doubleTap;
int checkedItem = -1;
@ -999,7 +914,7 @@ public final class EmulationActivity extends AppCompatActivity implements ThemeP
(DialogInterface dialog, int which) -> IntSetting.MAIN_DOUBLE_TAP_BUTTON.setInt(
mSettings, InputOverlayPointer.DOUBLE_TAP_OPTIONS.get(which)))
.setPositiveButton(R.string.ok,
(dialogInterface, i) -> mEmulationFragment.initInputPointer())
(dialogInterface, i) -> mEmulationFragment.initInputPointer(mSettings))
.show();
}
@ -1033,55 +948,75 @@ public final class EmulationActivity extends AppCompatActivity implements ThemeP
{
IntSetting.MAIN_CONTROL_SCALE.setInt(mSettings, (int) scaleSlider.getValue());
IntSetting.MAIN_CONTROL_OPACITY.setInt(mSettings, (int) sliderOpacity.getValue());
mEmulationFragment.refreshInputOverlay();
mEmulationFragment.refreshInputOverlay(mSettings);
})
.setNeutralButton(R.string.default_values, (dialog, which) ->
{
IntSetting.MAIN_CONTROL_SCALE.delete(mSettings);
IntSetting.MAIN_CONTROL_OPACITY.delete(mSettings);
mEmulationFragment.refreshInputOverlay();
mEmulationFragment.refreshInputOverlay(mSettings);
})
.show();
}
private void addControllerIfNotNone(List<CharSequence> entries, List<Integer> values,
IntSetting controller, int entry, int value)
{
if (controller.getInt(mSettings) != 0)
{
entries.add(getString(entry));
values.add(value);
}
}
private void chooseController()
{
ArrayList<CharSequence> entries = new ArrayList<>();
ArrayList<Integer> values = new ArrayList<>();
entries.add(getString(R.string.none));
values.add(-1);
addControllerIfNotNone(entries, values, IntSetting.MAIN_SI_DEVICE_0, R.string.controller_0, 0);
addControllerIfNotNone(entries, values, IntSetting.MAIN_SI_DEVICE_1, R.string.controller_1, 1);
addControllerIfNotNone(entries, values, IntSetting.MAIN_SI_DEVICE_2, R.string.controller_2, 2);
addControllerIfNotNone(entries, values, IntSetting.MAIN_SI_DEVICE_3, R.string.controller_3, 3);
if (NativeLibrary.IsEmulatingWii())
{
addControllerIfNotNone(entries, values, IntSetting.WIIMOTE_1_SOURCE, R.string.wiimote_0, 4);
addControllerIfNotNone(entries, values, IntSetting.WIIMOTE_2_SOURCE, R.string.wiimote_1, 5);
addControllerIfNotNone(entries, values, IntSetting.WIIMOTE_3_SOURCE, R.string.wiimote_2, 6);
addControllerIfNotNone(entries, values, IntSetting.WIIMOTE_4_SOURCE, R.string.wiimote_3, 7);
}
IntSetting controllerSetting = NativeLibrary.IsEmulatingWii() ?
IntSetting.MAIN_OVERLAY_WII_CONTROLLER : IntSetting.MAIN_OVERLAY_GC_CONTROLLER;
int currentValue = controllerSetting.getInt(mSettings);
int checkedItem = -1;
for (int i = 0; i < values.size(); i++)
{
if (values.get(i) == currentValue)
{
checkedItem = i;
break;
}
}
final SharedPreferences.Editor editor = mPreferences.edit();
new MaterialAlertDialogBuilder(this)
.setTitle(R.string.emulation_choose_controller)
.setSingleChoiceItems(R.array.controllersEntries,
InputOverlay.getConfiguredControllerType(this),
.setSingleChoiceItems(entries.toArray(new CharSequence[]{}), checkedItem,
(dialog, indexSelected) ->
{
editor.putInt("wiiController", indexSelected);
updateWiimoteNewController(indexSelected, this);
NativeLibrary.ReloadWiimoteConfig();
})
controllerSetting.setInt(mSettings, values.get(indexSelected)))
.setPositiveButton(R.string.ok, (dialogInterface, i) ->
{
editor.apply();
mEmulationFragment.refreshInputOverlay();
mEmulationFragment.refreshInputOverlay(mSettings);
})
.show();
}
private void showMotionControlsOptions()
{
new MaterialAlertDialogBuilder(this)
.setTitle(R.string.emulation_motion_controls)
.setSingleChoiceItems(R.array.motionControlsEntries,
IntSetting.MAIN_MOTION_CONTROLS.getInt(mSettings),
(dialog, indexSelected) ->
{
IntSetting.MAIN_MOTION_CONTROLS.setInt(mSettings, indexSelected);
updateMotionListener();
updateWiimoteNewImuIr(indexSelected);
NativeLibrary.ReloadWiimoteConfig();
})
.setPositiveButton(R.string.ok, (dialogInterface, i) -> dialogInterface.dismiss())
.setNeutralButton(R.string.emulation_more_controller_settings,
(dialogInterface, i) -> SettingsActivity.launch(this, MenuTag.SETTINGS))
.show();
}
@ -1098,88 +1033,6 @@ public final class EmulationActivity extends AppCompatActivity implements ThemeP
.show();
}
private void setIRSensitivity()
{
// IR settings always get saved per-game since WiimoteNew.ini is wiped upon reinstall.
File file = SettingsFile.getCustomGameSettingsFile(NativeLibrary.GetCurrentGameID());
IniFile ini = new IniFile(file);
int ir_pitch = ini.getInt(Settings.SECTION_CONTROLS, SettingsFile.KEY_WIIBIND_IR_PITCH, 20);
DialogIrSensitivityBinding dialogBinding =
DialogIrSensitivityBinding.inflate(getLayoutInflater());
TextView text_slider_value_pitch = dialogBinding.textIrPitch;
TextView units = dialogBinding.textIrPitchUnits;
Slider slider_pitch = dialogBinding.sliderPitch;
text_slider_value_pitch.setText(String.valueOf(ir_pitch));
units.setText(getString(R.string.pitch));
slider_pitch.setValueTo(100);
slider_pitch.setValue(ir_pitch);
slider_pitch.setStepSize(1);
slider_pitch.addOnChangeListener(
(slider, progress, fromUser) -> text_slider_value_pitch.setText(
String.valueOf((int) progress)));
int ir_yaw = ini.getInt(Settings.SECTION_CONTROLS, SettingsFile.KEY_WIIBIND_IR_YAW, 25);
TextView text_slider_value_yaw = dialogBinding.textIrYaw;
TextView units_yaw = dialogBinding.textIrYawUnits;
Slider seekbar_yaw = dialogBinding.sliderYaw;
text_slider_value_yaw.setText(String.valueOf(ir_yaw));
units_yaw.setText(getString(R.string.yaw));
seekbar_yaw.setValueTo(100);
seekbar_yaw.setValue(ir_yaw);
seekbar_yaw.setStepSize(1);
seekbar_yaw.addOnChangeListener((slider, progress, fromUser) -> text_slider_value_yaw.setText(
String.valueOf((int) progress)));
int ir_vertical_offset =
ini.getInt(Settings.SECTION_CONTROLS, SettingsFile.KEY_WIIBIND_IR_VERTICAL_OFFSET, 10);
TextView text_slider_value_vertical_offset = dialogBinding.textIrVerticalOffset;
TextView units_vertical_offset = dialogBinding.textIrVerticalOffsetUnits;
Slider seekbar_vertical_offset = dialogBinding.sliderVerticalOffset;
text_slider_value_vertical_offset.setText(String.valueOf(ir_vertical_offset));
units_vertical_offset.setText(getString(R.string.vertical_offset));
seekbar_vertical_offset.setValueTo(100);
seekbar_vertical_offset.setValue(ir_vertical_offset);
seekbar_vertical_offset.setStepSize(1);
seekbar_vertical_offset.addOnChangeListener(
(slider, progress, fromUser) -> text_slider_value_vertical_offset.setText(
String.valueOf((int) progress)));
new MaterialAlertDialogBuilder(this)
.setTitle(getString(R.string.emulation_ir_sensitivity))
.setView(dialogBinding.getRoot())
.setPositiveButton(R.string.ok, (dialogInterface, i) ->
{
ini.setString(Settings.SECTION_CONTROLS, SettingsFile.KEY_WIIBIND_IR_PITCH,
text_slider_value_pitch.getText().toString());
ini.setString(Settings.SECTION_CONTROLS, SettingsFile.KEY_WIIBIND_IR_YAW,
text_slider_value_yaw.getText().toString());
ini.setString(Settings.SECTION_CONTROLS, SettingsFile.KEY_WIIBIND_IR_VERTICAL_OFFSET,
text_slider_value_vertical_offset.getText().toString());
ini.save(file);
NativeLibrary.ReloadWiimoteConfig();
})
.setNegativeButton(R.string.cancel, null)
.setNeutralButton(R.string.default_values, (dialogInterface, i) ->
{
ini.deleteKey(Settings.SECTION_CONTROLS, SettingsFile.KEY_WIIBIND_IR_PITCH);
ini.deleteKey(Settings.SECTION_CONTROLS, SettingsFile.KEY_WIIBIND_IR_YAW);
ini.deleteKey(Settings.SECTION_CONTROLS, SettingsFile.KEY_WIIBIND_IR_VERTICAL_OFFSET);
ini.save(file);
NativeLibrary.ReloadWiimoteConfig();
})
.show();
}
private void showSkylanderPortalSettings()
{
mSkylandersBinding =
@ -1212,7 +1065,7 @@ public final class EmulationActivity extends AppCompatActivity implements ThemeP
new MaterialAlertDialogBuilder(this)
.setTitle(getString(R.string.emulation_touch_overlay_reset))
.setPositiveButton(R.string.yes,
(dialogInterface, i) -> mEmulationFragment.resetInputOverlay())
(dialogInterface, i) -> mEmulationFragment.resetInputOverlay(mSettings))
.setNegativeButton(R.string.cancel, null)
.show();
}
@ -1266,41 +1119,15 @@ public final class EmulationActivity extends AppCompatActivity implements ThemeP
@Override
public boolean dispatchGenericMotionEvent(MotionEvent event)
{
if (mMenuVisible)
if (!mMenuVisible)
{
return false;
}
if (((event.getSource() & InputDevice.SOURCE_CLASS_JOYSTICK) == 0))
{
return super.dispatchGenericMotionEvent(event);
}
// Don't attempt to do anything if we are disconnecting a device.
if (event.getActionMasked() == MotionEvent.ACTION_CANCEL)
return true;
InputDevice input = event.getDevice();
List<InputDevice.MotionRange> motions = input.getMotionRanges();
for (InputDevice.MotionRange range : motions)
{
int axis = range.getAxis();
float origValue = event.getAxisValue(axis);
float value = ControllerMappingHelper.scaleAxis(input, axis, origValue);
// If the input is still in the "flat" area, that means it's really zero.
// This is used to compensate for imprecision in joysticks.
if (Math.abs(value) > range.getFlat())
if (ControllerInterface.dispatchGenericMotionEvent(event))
{
NativeLibrary.onGamePadMoveEvent(input.getDescriptor(), axis, value);
}
else
{
NativeLibrary.onGamePadMoveEvent(input.getDescriptor(), axis, 0.0f);
return true;
}
}
return true;
return super.dispatchGenericMotionEvent(event);
}
private void showSubMenu(SaveLoadStateFragment.SaveOrLoad saveOrLoad)
@ -1333,7 +1160,7 @@ public final class EmulationActivity extends AppCompatActivity implements ThemeP
public void initInputPointer()
{
mEmulationFragment.initInputPointer();
mEmulationFragment.initInputPointer(mSettings);
}
@Override

View File

@ -1,171 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-or-later
package org.dolphinemu.dolphinemu.dialogs;
import android.content.Context;
import android.view.InputDevice;
import android.view.KeyEvent;
import android.view.MotionEvent;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import org.dolphinemu.dolphinemu.features.settings.model.view.InputBindingSetting;
import org.dolphinemu.dolphinemu.features.settings.ui.SettingsAdapter;
import org.dolphinemu.dolphinemu.utils.ControllerMappingHelper;
import org.dolphinemu.dolphinemu.utils.Log;
import java.util.ArrayList;
import java.util.List;
/**
* {@link AlertDialog} derivative that listens for
* motion events from controllers and joysticks.
*/
public final class MotionAlertDialog extends AlertDialog
{
// The selected input preference
private final InputBindingSetting setting;
private final ArrayList<Float> mPreviousValues = new ArrayList<>();
private int mPrevDeviceId = 0;
private boolean mWaitingForEvent = true;
private SettingsAdapter mAdapter;
/**
* Constructor
*
* @param context The current {@link Context}.
* @param setting The Preference to show this dialog for.
*/
public MotionAlertDialog(Context context, InputBindingSetting setting, SettingsAdapter adapter)
{
super(context);
this.setting = setting;
mAdapter = adapter;
}
public boolean onKeyEvent(int keyCode, KeyEvent event)
{
Log.debug("[MotionAlertDialog] Received key event: " + event.getAction());
if (event.getAction() == KeyEvent.ACTION_UP)
{
if (!ControllerMappingHelper.shouldKeyBeIgnored(event.getDevice(), keyCode))
{
setting.onKeyInput(mAdapter.getSettings(), event);
dismiss();
}
// Even if we ignore the key, we still consume it. Thus return true regardless.
return true;
}
return false;
}
@Override
public boolean onKeyLongPress(int keyCode, @NonNull KeyEvent event)
{
// Intended for devices with no touchscreen or mouse
if (keyCode == KeyEvent.KEYCODE_BACK)
{
setting.clearValue(mAdapter.getSettings());
dismiss();
return true;
}
return super.onKeyLongPress(keyCode, event);
}
@Override
public boolean dispatchKeyEvent(KeyEvent event)
{
// Handle this key if we care about it, otherwise pass it down the framework
return onKeyEvent(event.getKeyCode(), event) || super.dispatchKeyEvent(event);
}
@Override
public boolean dispatchGenericMotionEvent(@NonNull MotionEvent event)
{
// Handle this event if we care about it, otherwise pass it down the framework
return onMotionEvent(event) || super.dispatchGenericMotionEvent(event);
}
private boolean onMotionEvent(MotionEvent event)
{
if ((event.getSource() & InputDevice.SOURCE_CLASS_JOYSTICK) == 0)
return false;
if (event.getAction() != MotionEvent.ACTION_MOVE)
return false;
InputDevice input = event.getDevice();
List<InputDevice.MotionRange> motionRanges = input.getMotionRanges();
if (input.getId() != mPrevDeviceId)
{
mPreviousValues.clear();
}
mPrevDeviceId = input.getId();
boolean firstEvent = mPreviousValues.isEmpty();
int numMovedAxis = 0;
float axisMoveValue = 0.0f;
InputDevice.MotionRange lastMovedRange = null;
char lastMovedDir = '?';
if (mWaitingForEvent)
{
for (int i = 0; i < motionRanges.size(); i++)
{
InputDevice.MotionRange range = motionRanges.get(i);
int axis = range.getAxis();
float origValue = event.getAxisValue(axis);
float value = ControllerMappingHelper.scaleAxis(input, axis, origValue);
if (firstEvent)
{
mPreviousValues.add(value);
}
else
{
float previousValue = mPreviousValues.get(i);
// Only handle the axes that are not neutral (more than 0.5)
// but ignore any axis that has a constant value (e.g. always 1)
if (Math.abs(value) > 0.5f && value != previousValue)
{
// It is common to have multiple axes with the same physical input. For example,
// shoulder butters are provided as both AXIS_LTRIGGER and AXIS_BRAKE.
// To handle this, we ignore an axis motion that's the exact same as a motion
// we already saw. This way, we ignore axes with two names, but catch the case
// where a joystick is moved in two directions.
// ref: bottom of https://developer.android.com/training/game-controllers/controller-input.html
if (value != axisMoveValue)
{
axisMoveValue = value;
numMovedAxis++;
lastMovedRange = range;
lastMovedDir = value < 0.0f ? '-' : '+';
}
}
// Special case for d-pads (axis value jumps between 0 and 1 without any values
// in between). Without this, the user would need to press the d-pad twice
// due to the first press being caught by the "if (firstEvent)" case further up.
else if (Math.abs(value) < 0.25f && Math.abs(previousValue) > 0.75f)
{
numMovedAxis++;
lastMovedRange = range;
lastMovedDir = previousValue < 0.0f ? '-' : '+';
}
}
mPreviousValues.set(i, value);
}
// If only one axis moved, that's the winner.
if (numMovedAxis == 1)
{
mWaitingForEvent = false;
setting.onMotionInput(mAdapter.getSettings(), input, lastMovedRange, lastMovedDir);
dismiss();
}
}
return true;
}
}

View File

@ -0,0 +1,50 @@
// SPDX-License-Identifier: GPL-2.0-or-later
package org.dolphinemu.dolphinemu.features.input.model;
import org.dolphinemu.dolphinemu.features.input.model.controlleremu.ControlGroup;
import org.dolphinemu.dolphinemu.features.settings.model.AbstractBooleanSetting;
import org.dolphinemu.dolphinemu.features.settings.model.Settings;
public class ControlGroupEnabledSetting implements AbstractBooleanSetting
{
private final ControlGroup mControlGroup;
public ControlGroupEnabledSetting(ControlGroup controlGroup)
{
mControlGroup = controlGroup;
}
@Override
public boolean getBoolean(Settings settings)
{
return mControlGroup.getEnabled();
}
@Override
public void setBoolean(Settings settings, boolean newValue)
{
mControlGroup.setEnabled(newValue);
}
@Override
public boolean isOverridden(Settings settings)
{
return false;
}
@Override
public boolean isRuntimeEditable()
{
return true;
}
@Override
public boolean delete(Settings settings)
{
boolean newValue = mControlGroup.getDefaultEnabledValue() != ControlGroup.DEFAULT_ENABLED_NO;
mControlGroup.setEnabled(newValue);
return true;
}
}

View File

@ -0,0 +1,178 @@
// SPDX-License-Identifier: GPL-2.0-or-later
package org.dolphinemu.dolphinemu.features.input.model;
import android.content.Context;
import android.hardware.input.InputManager;
import android.os.Build;
import android.os.Handler;
import android.os.VibrationEffect;
import android.os.Vibrator;
import android.os.VibratorManager;
import android.view.InputDevice;
import android.view.KeyEvent;
import android.view.MotionEvent;
import androidx.annotation.Keep;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import org.dolphinemu.dolphinemu.DolphinApplication;
import org.dolphinemu.dolphinemu.utils.LooperThread;
/**
* This class interfaces with the native ControllerInterface,
* which is where the emulator core gets inputs from.
*/
public final class ControllerInterface
{
private static final class InputDeviceListener implements InputManager.InputDeviceListener
{
@Override
public void onInputDeviceAdded(int deviceId)
{
// Simple implementation for now. We could do something fancier if we wanted to.
refreshDevices();
}
@Override
public void onInputDeviceRemoved(int deviceId)
{
// Simple implementation for now. We could do something fancier if we wanted to.
refreshDevices();
}
@Override
public void onInputDeviceChanged(int deviceId)
{
// Simple implementation for now. We could do something fancier if we wanted to.
refreshDevices();
}
}
private static InputDeviceListener mInputDeviceListener;
private static LooperThread mLooperThread;
/**
* Activities which want to pass on inputs to native code
* should call this in their own dispatchKeyEvent method.
*
* @return true if the emulator core seems to be interested in this event.
* false if the event should be passed on to the default dispatchKeyEvent.
*/
public static native boolean dispatchKeyEvent(KeyEvent event);
/**
* Activities which want to pass on inputs to native code
* should call this in their own dispatchGenericMotionEvent method.
*
* @return true if the emulator core seems to be interested in this event.
* false if the event should be passed on to the default dispatchGenericMotionEvent.
*/
public static native boolean dispatchGenericMotionEvent(MotionEvent event);
/**
* {@link DolphinSensorEventListener} calls this for each axis of a received SensorEvent.
*
* @return true if the emulator core seems to be interested in this event.
* false if the sensor can be suspended to save battery.
*/
public static native boolean dispatchSensorEvent(String deviceQualifier, String axisName,
float value);
/**
* Called when a sensor is suspended or unsuspended.
*
* @param deviceQualifier A string used by native code for uniquely identifying devices.
* @param axisNames The name of all axes for the sensor.
* @param suspended Whether the sensor is now suspended.
*/
public static native void notifySensorSuspendedState(String deviceQualifier, String[] axisNames,
boolean suspended);
/**
* Rescans for input devices.
*/
public static native void refreshDevices();
public static native String[] getAllDeviceStrings();
@Nullable
public static native CoreDevice getDevice(String deviceString);
@Keep
private static void registerInputDeviceListener()
{
if (mLooperThread == null)
{
mLooperThread = new LooperThread("Hotplug thread");
mLooperThread.start();
}
if (mInputDeviceListener == null)
{
InputManager im = (InputManager)
DolphinApplication.getAppContext().getSystemService(Context.INPUT_SERVICE);
mInputDeviceListener = new InputDeviceListener();
im.registerInputDeviceListener(mInputDeviceListener, new Handler(mLooperThread.getLooper()));
}
}
@Keep
private static void unregisterInputDeviceListener()
{
if (mInputDeviceListener != null)
{
InputManager im = (InputManager)
DolphinApplication.getAppContext().getSystemService(Context.INPUT_SERVICE);
im.unregisterInputDeviceListener(mInputDeviceListener);
mInputDeviceListener = null;
}
}
@Keep @NonNull
private static DolphinVibratorManager getVibratorManager(InputDevice device)
{
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S)
{
return new DolphinVibratorManagerPassthrough(device.getVibratorManager());
}
else
{
return new DolphinVibratorManagerCompat(device.getVibrator());
}
}
@Keep @NonNull
private static DolphinVibratorManager getSystemVibratorManager()
{
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S)
{
VibratorManager vibratorManager = (VibratorManager)
DolphinApplication.getAppContext().getSystemService(Context.VIBRATOR_MANAGER_SERVICE);
if (vibratorManager != null)
return new DolphinVibratorManagerPassthrough(vibratorManager);
}
Vibrator vibrator = (Vibrator)
DolphinApplication.getAppContext().getSystemService(Context.VIBRATOR_SERVICE);
return new DolphinVibratorManagerCompat(vibrator);
}
@Keep
private static void vibrate(@NonNull Vibrator vibrator)
{
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
{
vibrator.vibrate(VibrationEffect.createOneShot(100, VibrationEffect.DEFAULT_AMPLITUDE));
}
else
{
vibrator.vibrate(100);
}
}
}

View File

@ -0,0 +1,48 @@
// SPDX-License-Identifier: GPL-2.0-or-later
package org.dolphinemu.dolphinemu.features.input.model;
import androidx.annotation.Keep;
/**
* Represents a C++ ciface::Core::Device.
*/
public final class CoreDevice
{
/**
* Represents a C++ ciface::Core::Device::Control.
*
* This class is non-static to ensure that the CoreDevice parent does not get garbage collected
* while a Control is still accessible. (CoreDevice's finalizer may delete the native controls.)
*/
@SuppressWarnings("InnerClassMayBeStatic")
public final class Control
{
@Keep
private final long mPointer;
@Keep
private Control(long pointer)
{
mPointer = pointer;
}
public native String getName();
}
@Keep
private final long mPointer;
@Keep
private CoreDevice(long pointer)
{
mPointer = pointer;
}
@Override
protected native void finalize();
public native Control[] getInputs();
public native Control[] getOutputs();
}

View File

@ -0,0 +1,440 @@
package org.dolphinemu.dolphinemu.features.input.model;
import android.content.Context;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.Build;
import android.view.InputDevice;
import android.view.Surface;
import androidx.annotation.Keep;
import org.dolphinemu.dolphinemu.DolphinApplication;
import org.dolphinemu.dolphinemu.utils.Log;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class DolphinSensorEventListener implements SensorEventListener
{
// Set of three axes. Creates a negative companion to each axis, and corrects for device rotation.
private static final int AXIS_SET_TYPE_DEVICE_COORDINATES = 0;
// Set of three axes. Creates a negative companion to each axis.
private static final int AXIS_SET_TYPE_OTHER_COORDINATES = 1;
private static class AxisSetDetails
{
public final int firstAxisOfSet;
public final int axisSetType;
public AxisSetDetails(int firstAxisOfSet, int axisSetType)
{
this.firstAxisOfSet = firstAxisOfSet;
this.axisSetType = axisSetType;
}
}
private static class SensorDetails
{
public final int sensorType;
public final String[] axisNames;
public final AxisSetDetails[] axisSetDetails;
public boolean isSuspended = true;
public SensorDetails(int sensorType, String[] axisNames, AxisSetDetails[] axisSetDetails)
{
this.sensorType = sensorType;
this.axisNames = axisNames;
this.axisSetDetails = axisSetDetails;
}
}
private static int sDeviceRotation = Surface.ROTATION_0;
private final SensorManager mSensorManager;
private final HashMap<Sensor, SensorDetails> mSensorDetails = new HashMap<>();
private final boolean mRotateCoordinatesForScreenOrientation;
private String mDeviceQualifier = "";
// The fastest sampling rate Android lets us use without declaring the HIGH_SAMPLING_RATE_SENSORS
// permission is 200 Hz. This is also the sampling rate of a Wii Remote, so it fits us perfectly.
private static final int SAMPLING_PERIOD_US = 1000000 / 200;
@Keep
public DolphinSensorEventListener()
{
mSensorManager = (SensorManager)
DolphinApplication.getAppContext().getSystemService(Context.SENSOR_SERVICE);
mRotateCoordinatesForScreenOrientation = true;
addSensors();
}
@Keep
public DolphinSensorEventListener(InputDevice inputDevice)
{
mRotateCoordinatesForScreenOrientation = false;
if (Build.VERSION.SDK_INT >= 31)
{
mSensorManager = inputDevice.getSensorManager();
// TODO: There is a bug where after suspending sensors, onSensorChanged can get called for
// a sensor that we never registered as a listener for. The way our code is currently written,
// this causes a NullPointerException, but if we checked for null we would instead have the
// problem of being spammed with onSensorChanged calls even though the sensor shouldn't be
// enabled. For now, let's comment out the ability to use InputDevice sensors.
//addSensors();
}
else
{
mSensorManager = null;
}
}
private void addSensors()
{
tryAddSensor(Sensor.TYPE_ACCELEROMETER, new String[]{"Accel Right", "Accel Left",
"Accel Forward", "Accel Backward", "Accel Up", "Accel Down"},
new AxisSetDetails[]{new AxisSetDetails(0, AXIS_SET_TYPE_DEVICE_COORDINATES)});
tryAddSensor(Sensor.TYPE_GYROSCOPE, new String[]{"Gyro Pitch Up", "Gyro Pitch Down",
"Gyro Roll Right", "Gyro Roll Left", "Gyro Yaw Left", "Gyro Yaw Right"},
new AxisSetDetails[]{new AxisSetDetails(0, AXIS_SET_TYPE_DEVICE_COORDINATES)});
tryAddSensor(Sensor.TYPE_LIGHT, "Light");
tryAddSensor(Sensor.TYPE_PRESSURE, "Pressure");
tryAddSensor(Sensor.TYPE_TEMPERATURE, "Device Temperature");
tryAddSensor(Sensor.TYPE_PROXIMITY, "Proximity");
tryAddSensor(Sensor.TYPE_GRAVITY, new String[]{"Gravity Right", "Gravity Left",
"Gravity Forward", "Gravity Backward", "Gravity Up", "Gravity Down"},
new AxisSetDetails[]{new AxisSetDetails(0, AXIS_SET_TYPE_DEVICE_COORDINATES)});
tryAddSensor(Sensor.TYPE_LINEAR_ACCELERATION,
new String[]{"Linear Acceleration Right", "Linear Acceleration Left",
"Linear Acceleration Forward", "Linear Acceleration Backward",
"Linear Acceleration Up", "Linear Acceleration Down"},
new AxisSetDetails[]{new AxisSetDetails(0, AXIS_SET_TYPE_DEVICE_COORDINATES)});
// The values provided by this sensor can be interpreted as an Euler vector or a quaternion.
// The directions of X and Y are flipped to match the Wii Remote coordinate system.
tryAddSensor(Sensor.TYPE_ROTATION_VECTOR,
new String[]{"Rotation Vector X-", "Rotation Vector X+", "Rotation Vector Y-",
"Rotation Vector Y+", "Rotation Vector Z+",
"Rotation Vector Z-", "Rotation Vector R", "Rotation Vector Heading Accuracy"},
new AxisSetDetails[]{new AxisSetDetails(0, AXIS_SET_TYPE_DEVICE_COORDINATES)});
tryAddSensor(Sensor.TYPE_RELATIVE_HUMIDITY, "Relative Humidity");
tryAddSensor(Sensor.TYPE_AMBIENT_TEMPERATURE, "Ambient Temperature");
// The values provided by this sensor can be interpreted as an Euler vector or a quaternion.
// The directions of X and Y are flipped to match the Wii Remote coordinate system.
tryAddSensor(Sensor.TYPE_GAME_ROTATION_VECTOR,
new String[]{"Game Rotation Vector X-", "Game Rotation Vector X+",
"Game Rotation Vector Y-", "Game Rotation Vector Y+", "Game Rotation Vector Z+",
"Game Rotation Vector Z-", "Game Rotation Vector R"},
new AxisSetDetails[]{new AxisSetDetails(0, AXIS_SET_TYPE_DEVICE_COORDINATES)});
tryAddSensor(Sensor.TYPE_GYROSCOPE_UNCALIBRATED,
new String[]{"Gyro Uncalibrated Pitch Up", "Gyro Uncalibrated Pitch Down",
"Gyro Uncalibrated Roll Right", "Gyro Uncalibrated Roll Left",
"Gyro Uncalibrated Yaw Left", "Gyro Uncalibrated Yaw Right",
"Gyro Drift Pitch Up", "Gyro Drift Pitch Down", "Gyro Drift Roll Right",
"Gyro Drift Roll Left", "Gyro Drift Yaw Left", "Gyro Drift Yaw Right"},
new AxisSetDetails[]{new AxisSetDetails(0, AXIS_SET_TYPE_DEVICE_COORDINATES),
new AxisSetDetails(3, AXIS_SET_TYPE_DEVICE_COORDINATES)});
tryAddSensor(Sensor.TYPE_HEART_RATE, "Heart Rate");
if (Build.VERSION.SDK_INT >= 24)
{
tryAddSensor(Sensor.TYPE_HEART_BEAT, "Heart Beat");
}
if (Build.VERSION.SDK_INT >= 26)
{
tryAddSensor(Sensor.TYPE_ACCELEROMETER_UNCALIBRATED,
new String[]{"Accel Uncalibrated Right", "Accel Uncalibrated Left",
"Accel Uncalibrated Forward", "Accel Uncalibrated Backward",
"Accel Uncalibrated Up", "Accel Uncalibrated Down",
"Accel Bias Right", "Accel Bias Left", "Accel Bias Forward",
"Accel Bias Backward", "Accel Bias Up", "Accel Bias Down"},
new AxisSetDetails[]{new AxisSetDetails(0, AXIS_SET_TYPE_DEVICE_COORDINATES),
new AxisSetDetails(3, AXIS_SET_TYPE_DEVICE_COORDINATES)});
}
if (Build.VERSION.SDK_INT >= 30)
{
tryAddSensor(Sensor.TYPE_HINGE_ANGLE, "Hinge Angle");
}
if (Build.VERSION.SDK_INT >= 33)
{
// The values provided by this sensor can be interpreted as an Euler vector.
// The directions of X and Y are flipped to match the Wii Remote coordinate system.
tryAddSensor(Sensor.TYPE_HEAD_TRACKER,
new String[]{"Head Rotation Vector X-", "Head Rotation Vector X+",
"Head Rotation Vector Y-", "Head Rotation Vector Y+",
"Head Rotation Vector Z+", "Head Rotation Vector Z-",
"Head Pitch Up", "Head Pitch Down", "Head Roll Right", "Head Roll Left",
"Head Yaw Left", "Head Yaw Right"},
new AxisSetDetails[]{new AxisSetDetails(0, AXIS_SET_TYPE_OTHER_COORDINATES),
new AxisSetDetails(3, AXIS_SET_TYPE_OTHER_COORDINATES)});
tryAddSensor(Sensor.TYPE_HEADING, new String[]{"Heading", "Heading Accuracy"},
new AxisSetDetails[]{});
}
}
private void tryAddSensor(int sensorType, String axisName)
{
tryAddSensor(sensorType, new String[]{axisName}, new AxisSetDetails[]{});
}
private void tryAddSensor(int sensorType, String[] axisNames, AxisSetDetails[] axisSetDetails)
{
Sensor sensor = mSensorManager.getDefaultSensor(sensorType);
if (sensor != null)
{
mSensorDetails.put(sensor, new SensorDetails(sensorType, axisNames, axisSetDetails));
}
}
@Override
public void onSensorChanged(SensorEvent sensorEvent)
{
final SensorDetails sensorDetails = mSensorDetails.get(sensorEvent.sensor);
final float[] values = sensorEvent.values;
final String[] axisNames = sensorDetails.axisNames;
final AxisSetDetails[] axisSetDetails = sensorDetails.axisSetDetails;
int eventAxisIndex = 0;
int detailsAxisIndex = 0;
int detailsAxisSetIndex = 0;
boolean keepSensorAlive = false;
while (eventAxisIndex < values.length && detailsAxisIndex < axisNames.length)
{
if (detailsAxisSetIndex < axisSetDetails.length &&
axisSetDetails[detailsAxisSetIndex].firstAxisOfSet == eventAxisIndex)
{
int rotation = Surface.ROTATION_0;
if (mRotateCoordinatesForScreenOrientation &&
axisSetDetails[detailsAxisSetIndex].axisSetType == AXIS_SET_TYPE_DEVICE_COORDINATES)
{
rotation = sDeviceRotation;
}
float x, y;
switch (rotation)
{
default:
case Surface.ROTATION_0:
x = values[eventAxisIndex];
y = values[eventAxisIndex + 1];
break;
case Surface.ROTATION_90:
x = -values[eventAxisIndex + 1];
y = values[eventAxisIndex];
break;
case Surface.ROTATION_180:
x = -values[eventAxisIndex];
y = -values[eventAxisIndex + 1];
break;
case Surface.ROTATION_270:
x = values[eventAxisIndex + 1];
y = -values[eventAxisIndex];
break;
}
float z = values[eventAxisIndex + 2];
keepSensorAlive |= ControllerInterface.dispatchSensorEvent(mDeviceQualifier,
axisNames[detailsAxisIndex], x);
keepSensorAlive |= ControllerInterface.dispatchSensorEvent(mDeviceQualifier,
axisNames[detailsAxisIndex + 1], x);
keepSensorAlive |= ControllerInterface.dispatchSensorEvent(mDeviceQualifier,
axisNames[detailsAxisIndex + 2], y);
keepSensorAlive |= ControllerInterface.dispatchSensorEvent(mDeviceQualifier,
axisNames[detailsAxisIndex + 3], y);
keepSensorAlive |= ControllerInterface.dispatchSensorEvent(mDeviceQualifier,
axisNames[detailsAxisIndex + 4], z);
keepSensorAlive |= ControllerInterface.dispatchSensorEvent(mDeviceQualifier,
axisNames[detailsAxisIndex + 5], z);
eventAxisIndex += 3;
detailsAxisIndex += 6;
detailsAxisSetIndex++;
}
else
{
keepSensorAlive |= ControllerInterface.dispatchSensorEvent(mDeviceQualifier,
axisNames[detailsAxisIndex], values[eventAxisIndex]);
eventAxisIndex++;
detailsAxisIndex++;
}
}
if (!keepSensorAlive)
{
setSensorSuspended(sensorEvent.sensor, sensorDetails, true);
}
}
@Override
public void onAccuracyChanged(Sensor sensor, int i)
{
// We don't care about this
}
/**
* The device qualifier set here will be passed on to native code,
* for the purpose of letting native code identify which device this object belongs to.
*/
@Keep
public void setDeviceQualifier(String deviceQualifier)
{
mDeviceQualifier = deviceQualifier;
}
/**
* If a sensor has been suspended to save battery, this unsuspends it.
* If the sensor isn't currently suspended, nothing happens.
*
* @param axisName The name of any of the sensor's axes.
*/
@Keep
public void requestUnsuspendSensor(String axisName)
{
for (Map.Entry<Sensor, SensorDetails> entry : mSensorDetails.entrySet())
{
if (Arrays.asList(entry.getValue().axisNames).contains(axisName))
{
setSensorSuspended(entry.getKey(), entry.getValue(), false);
}
}
}
private void setSensorSuspended(Sensor sensor, SensorDetails sensorDetails, boolean suspend)
{
boolean changeOccurred = false;
synchronized (sensorDetails)
{
if (sensorDetails.isSuspended != suspend)
{
ControllerInterface.notifySensorSuspendedState(mDeviceQualifier, sensorDetails.axisNames,
suspend);
if (suspend)
mSensorManager.unregisterListener(this, sensor);
else
mSensorManager.registerListener(this, sensor, SAMPLING_PERIOD_US);
sensorDetails.isSuspended = suspend;
changeOccurred = true;
}
}
if (changeOccurred)
{
Log.info((suspend ? "Suspended sensor " : "Unsuspended sensor ") + sensor.getName());
}
}
@Keep
public String[] getAxisNames()
{
ArrayList<String> axisNames = new ArrayList<>();
for (SensorDetails sensorDetails : getSensorDetailsSorted())
{
Collections.addAll(axisNames, sensorDetails.axisNames);
}
return axisNames.toArray(new String[]{});
}
@Keep
public boolean[] getNegativeAxes()
{
ArrayList<Boolean> negativeAxes = new ArrayList<>();
for (SensorDetails sensorDetails : getSensorDetailsSorted())
{
int eventAxisIndex = 0;
int detailsAxisIndex = 0;
int detailsAxisSetIndex = 0;
while (detailsAxisIndex < sensorDetails.axisNames.length)
{
if (detailsAxisSetIndex < sensorDetails.axisSetDetails.length &&
sensorDetails.axisSetDetails[detailsAxisSetIndex].firstAxisOfSet == eventAxisIndex)
{
negativeAxes.add(false);
negativeAxes.add(true);
negativeAxes.add(false);
negativeAxes.add(true);
negativeAxes.add(false);
negativeAxes.add(true);
eventAxisIndex += 3;
detailsAxisIndex += 6;
detailsAxisSetIndex++;
}
else
{
negativeAxes.add(false);
eventAxisIndex++;
detailsAxisIndex++;
}
}
}
boolean[] result = new boolean[negativeAxes.size()];
for (int i = 0; i < result.length; i++)
{
result[i] = negativeAxes.get(i);
}
return result;
}
private List<SensorDetails> getSensorDetailsSorted()
{
ArrayList<SensorDetails> sensorDetails = new ArrayList<>(mSensorDetails.values());
Collections.sort(sensorDetails, Comparator.comparingInt(s -> s.sensorType));
return sensorDetails;
}
/**
* Should be called when an activity or other component that uses sensor events is resumed.
*
* Sensor events that contain device coordinates will have the coordinates rotated by the value
* passed to this function.
*
* @param deviceRotation The current rotation of the device (i.e. rotation of the default display)
*/
public static void setDeviceRotation(int deviceRotation)
{
sDeviceRotation = deviceRotation;
}
}

View File

@ -0,0 +1,20 @@
// SPDX-License-Identifier: GPL-2.0-or-later
package org.dolphinemu.dolphinemu.features.input.model;
import android.os.Vibrator;
import androidx.annotation.Keep;
import androidx.annotation.NonNull;
/**
* A wrapper around {@link android.os.VibratorManager}, for backwards compatibility.
*/
public interface DolphinVibratorManager
{
@Keep @NonNull
Vibrator getVibrator(int vibratorId);
@Keep @NonNull
int[] getVibratorIds();
}

View File

@ -0,0 +1,35 @@
// SPDX-License-Identifier: GPL-2.0-or-later
package org.dolphinemu.dolphinemu.features.input.model;
import android.os.Vibrator;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
public final class DolphinVibratorManagerCompat implements DolphinVibratorManager
{
private final Vibrator mVibrator;
private final int[] mIds;
public DolphinVibratorManagerCompat(@Nullable Vibrator vibrator)
{
mVibrator = vibrator;
mIds = vibrator != null && vibrator.hasVibrator() ? new int[]{0} : new int[]{};
}
@Override @NonNull
public Vibrator getVibrator(int vibratorId)
{
if (vibratorId > mIds.length)
throw new IndexOutOfBoundsException();
return mVibrator;
}
@Override @NonNull
public int[] getVibratorIds()
{
return mIds;
}
}

View File

@ -0,0 +1,33 @@
// SPDX-License-Identifier: GPL-2.0-or-later
package org.dolphinemu.dolphinemu.features.input.model;
import android.os.Build;
import android.os.Vibrator;
import android.os.VibratorManager;
import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
@RequiresApi(api = Build.VERSION_CODES.S)
public final class DolphinVibratorManagerPassthrough implements DolphinVibratorManager
{
private final VibratorManager mVibratorManager;
public DolphinVibratorManagerPassthrough(@NonNull VibratorManager vibratorManager)
{
mVibratorManager = vibratorManager;
}
@Override @NonNull
public Vibrator getVibrator(int vibratorId)
{
return mVibratorManager.getVibrator(vibratorId);
}
@Override @NonNull
public int[] getVibratorIds()
{
return mVibratorManager.getVibratorIds();
}
}

View File

@ -0,0 +1,48 @@
// SPDX-License-Identifier: GPL-2.0-or-later
package org.dolphinemu.dolphinemu.features.input.model;
import org.dolphinemu.dolphinemu.features.input.model.controlleremu.NumericSetting;
import org.dolphinemu.dolphinemu.features.settings.model.AbstractBooleanSetting;
import org.dolphinemu.dolphinemu.features.settings.model.Settings;
public class InputMappingBooleanSetting implements AbstractBooleanSetting
{
private final NumericSetting mNumericSetting;
public InputMappingBooleanSetting(NumericSetting numericSetting)
{
mNumericSetting = numericSetting;
}
@Override
public boolean getBoolean(Settings settings)
{
return mNumericSetting.getBooleanValue();
}
@Override
public void setBoolean(Settings settings, boolean newValue)
{
mNumericSetting.setBooleanValue(newValue);
}
@Override
public boolean isOverridden(Settings settings)
{
return false;
}
@Override
public boolean isRuntimeEditable()
{
return true;
}
@Override
public boolean delete(Settings settings)
{
mNumericSetting.setBooleanValue(mNumericSetting.getBooleanDefaultValue());
return true;
}
}

View File

@ -0,0 +1,49 @@
// SPDX-License-Identifier: GPL-2.0-or-later
package org.dolphinemu.dolphinemu.features.input.model;
import org.dolphinemu.dolphinemu.features.input.model.controlleremu.NumericSetting;
import org.dolphinemu.dolphinemu.features.settings.model.AbstractFloatSetting;
import org.dolphinemu.dolphinemu.features.settings.model.Settings;
// Yes, floats are not the same thing as doubles... They're close enough, though
public class InputMappingDoubleSetting implements AbstractFloatSetting
{
private final NumericSetting mNumericSetting;
public InputMappingDoubleSetting(NumericSetting numericSetting)
{
mNumericSetting = numericSetting;
}
@Override
public float getFloat(Settings settings)
{
return (float) mNumericSetting.getDoubleValue();
}
@Override
public void setFloat(Settings settings, float newValue)
{
mNumericSetting.setDoubleValue(newValue);
}
@Override
public boolean isOverridden(Settings settings)
{
return false;
}
@Override
public boolean isRuntimeEditable()
{
return true;
}
@Override
public boolean delete(Settings settings)
{
mNumericSetting.setDoubleValue(mNumericSetting.getDoubleDefaultValue());
return true;
}
}

View File

@ -0,0 +1,48 @@
// SPDX-License-Identifier: GPL-2.0-or-later
package org.dolphinemu.dolphinemu.features.input.model;
import org.dolphinemu.dolphinemu.features.input.model.controlleremu.NumericSetting;
import org.dolphinemu.dolphinemu.features.settings.model.AbstractIntSetting;
import org.dolphinemu.dolphinemu.features.settings.model.Settings;
public class InputMappingIntSetting implements AbstractIntSetting
{
private final NumericSetting mNumericSetting;
public InputMappingIntSetting(NumericSetting numericSetting)
{
mNumericSetting = numericSetting;
}
@Override
public int getInt(Settings settings)
{
return mNumericSetting.getIntValue();
}
@Override
public void setInt(Settings settings, int newValue)
{
mNumericSetting.setIntValue(newValue);
}
@Override
public boolean isOverridden(Settings settings)
{
return false;
}
@Override
public boolean isRuntimeEditable()
{
return true;
}
@Override
public boolean delete(Settings settings)
{
mNumericSetting.setIntValue(mNumericSetting.getIntDefaultValue());
return true;
}
}

View File

@ -0,0 +1,34 @@
// SPDX-License-Identifier: GPL-2.0-or-later
package org.dolphinemu.dolphinemu.features.input.model;
import androidx.annotation.NonNull;
import org.dolphinemu.dolphinemu.features.input.model.controlleremu.EmulatedController;
public final class MappingCommon
{
private MappingCommon()
{
}
/**
* Waits until the user presses one or more inputs or until a timeout,
* then returns the pressed inputs.
*
* When this is being called, a separate thread must be calling ControllerInterface's
* dispatchKeyEvent and dispatchGenericMotionEvent, otherwise no inputs will be registered.
*
* @param controller The device to detect inputs from.
* @param allDevices Whether to also detect inputs from devices other than the specified one.
* @return The input(s) pressed by the user in the form of an InputCommon expression,
* or an empty string if there were no inputs.
*/
public static native String detectInput(@NonNull EmulatedController controller,
boolean allDevices);
public static native String getExpressionForControl(String control, String device,
String defaultDevice);
public static native void save();
}

View File

@ -0,0 +1,27 @@
// SPDX-License-Identifier: GPL-2.0-or-later
package org.dolphinemu.dolphinemu.features.input.model.controlleremu;
import androidx.annotation.Keep;
/**
* Represents a C++ ControllerEmu::Control.
*
* The lifetime of this class is managed by C++ code. Calling methods on it after it's destroyed
* in C++ is undefined behavior!
*/
public class Control
{
@Keep
private final long mPointer;
@Keep
private Control(long pointer)
{
mPointer = pointer;
}
public native String getUiName();
public native ControlReference getControlReference();
}

View File

@ -0,0 +1,66 @@
// SPDX-License-Identifier: GPL-2.0-or-later
package org.dolphinemu.dolphinemu.features.input.model.controlleremu;
import androidx.annotation.Keep;
/**
* Represents a C++ ControllerEmu::ControlGroup.
*
* The lifetime of this class is managed by C++ code. Calling methods on it after it's destroyed
* in C++ is undefined behavior!
*/
public class ControlGroup
{
public static final int TYPE_OTHER = 0;
public static final int TYPE_STICK = 1;
public static final int TYPE_MIXED_TRIGGERS = 2;
public static final int TYPE_BUTTONS = 3;
public static final int TYPE_FORCE = 4;
public static final int TYPE_ATTACHMENTS = 5;
public static final int TYPE_TILT = 6;
public static final int TYPE_CURSOR = 7;
public static final int TYPE_TRIGGERS = 8;
public static final int TYPE_SLIDER = 9;
public static final int TYPE_SHAKE = 10;
public static final int TYPE_IMU_ACCELEROMETER = 11;
public static final int TYPE_IMU_GYROSCOPE = 12;
public static final int TYPE_IMU_CURSOR = 13;
public static final int DEFAULT_ENABLED_ALWAYS = 0;
public static final int DEFAULT_ENABLED_YES = 1;
public static final int DEFAULT_ENABLED_NO = 2;
@Keep
private final long mPointer;
@Keep
private ControlGroup(long pointer)
{
mPointer = pointer;
}
public native String getUiName();
public native int getGroupType();
public native int getDefaultEnabledValue();
public native boolean getEnabled();
public native void setEnabled(boolean value);
public native int getControlCount();
public native Control getControl(int i);
public native int getNumericSettingCount();
public native NumericSetting getNumericSetting(int i);
/**
* If getGroupType returns TYPE_ATTACHMENTS, this returns the attachment selection setting.
* Otherwise, undefined behavior!
*/
public native NumericSetting getAttachmentSetting();
}

View File

@ -0,0 +1,39 @@
// SPDX-License-Identifier: GPL-2.0-or-later
package org.dolphinemu.dolphinemu.features.input.model.controlleremu;
import androidx.annotation.Keep;
import androidx.annotation.Nullable;
/**
* Represents a C++ ControlReference.
*
* The lifetime of this class is managed by C++ code. Calling methods on it after it's destroyed
* in C++ is undefined behavior!
*/
public class ControlReference
{
@Keep
private final long mPointer;
@Keep
private ControlReference(long pointer)
{
mPointer = pointer;
}
public native double getState();
public native String getExpression();
/**
* Sets the expression for this control reference.
*
* @param expr The new expression
* @return null on success, a human-readable error on failure
*/
@Nullable
public native String setExpression(String expr);
public native boolean isInput();
}

View File

@ -0,0 +1,52 @@
// SPDX-License-Identifier: GPL-2.0-or-later
package org.dolphinemu.dolphinemu.features.input.model.controlleremu;
import androidx.annotation.Keep;
/**
* Represents a C++ ControllerEmu::EmulatedController.
*
* The lifetime of this class is managed by C++ code. Calling methods on it after it's destroyed
* in C++ is undefined behavior!
*/
public class EmulatedController
{
@Keep
private final long mPointer;
@Keep
private EmulatedController(long pointer)
{
mPointer = pointer;
}
public native String getDefaultDevice();
public native void setDefaultDevice(String device);
public native int getGroupCount();
public native ControlGroup getGroup(int index);
public native void updateSingleControlReference(ControlReference controlReference);
public native void loadDefaultSettings();
public native void clearSettings();
public native void loadProfile(String path);
public native void saveProfile(String path);
public static native EmulatedController getGcPad(int controllerIndex);
public static native EmulatedController getWiimote(int controllerIndex);
public static native EmulatedController getWiimoteAttachment(int controllerIndex,
int attachmentIndex);
public static native int getSelectedWiimoteAttachment(int controllerIndex);
public static native NumericSetting getSidewaysWiimoteSetting(int controllerIndex);
}

View File

@ -0,0 +1,104 @@
// SPDX-License-Identifier: GPL-2.0-or-later
package org.dolphinemu.dolphinemu.features.input.model.controlleremu;
import androidx.annotation.Keep;
/**
* Represents a C++ ControllerEmu::NumericSetting.
*
* The lifetime of this class is managed by C++ code. Calling methods on it after it's destroyed
* in C++ is undefined behavior!
*/
public class NumericSetting
{
public static final int TYPE_INT = 0;
public static final int TYPE_DOUBLE = 1;
public static final int TYPE_BOOLEAN = 2;
@Keep
private final long mPointer;
@Keep
private NumericSetting(long pointer)
{
mPointer = pointer;
}
/**
* @return The name used in the UI.
*/
public native String getUiName();
/**
* @return A string applied to the number in the UI (unit of measure).
*/
public native String getUiSuffix();
/**
* @return Detailed description of the setting.
*/
public native String getUiDescription();
/**
* @return TYPE_INT, TYPE_DOUBLE or TYPE_BOOLEAN
*/
public native int getType();
public native ControlReference getControlReference();
/**
* If the type is TYPE_INT, gets the current value. Otherwise, undefined behavior!
*/
public native int getIntValue();
/**
* If the type is TYPE_INT, sets the current value. Otherwise, undefined behavior!
*/
public native void setIntValue(int value);
/**
* If the type is TYPE_INT, gets the default value. Otherwise, undefined behavior!
*/
public native int getIntDefaultValue();
/**
* If the type is TYPE_DOUBLE, gets the current value. Otherwise, undefined behavior!
*/
public native double getDoubleValue();
/**
* If the type is TYPE_DOUBLE, sets the current value. Otherwise, undefined behavior!
*/
public native void setDoubleValue(double value);
/**
* If the type is TYPE_DOUBLE, gets the default value. Otherwise, undefined behavior!
*/
public native double getDoubleDefaultValue();
/**
* If the type is TYPE_DOUBLE, returns the minimum valid value. Otherwise, undefined behavior!
*/
public native double getDoubleMin();
/**
* If the type is TYPE_DOUBLE, returns the maximum valid value. Otherwise, undefined behavior!
*/
public native double getDoubleMax();
/**
* If the type is TYPE_BOOLEAN, gets the current value. Otherwise, undefined behavior!
*/
public native boolean getBooleanValue();
/**
* If the type is TYPE_BOOLEAN, sets the current value. Otherwise, undefined behavior!
*/
public native void setBooleanValue(boolean value);
/**
* If the type is TYPE_BOOLEAN, gets the default value. Otherwise, undefined behavior!
*/
public native boolean getBooleanDefaultValue();
}

View File

@ -0,0 +1,70 @@
// SPDX-License-Identifier: GPL-2.0-or-later
package org.dolphinemu.dolphinemu.features.input.model.view;
import android.content.Context;
import org.dolphinemu.dolphinemu.features.input.model.ControllerInterface;
import org.dolphinemu.dolphinemu.features.input.model.controlleremu.EmulatedController;
import org.dolphinemu.dolphinemu.features.settings.model.Settings;
import org.dolphinemu.dolphinemu.features.settings.model.view.StringSingleChoiceSetting;
public class InputDeviceSetting extends StringSingleChoiceSetting
{
private final EmulatedController mController;
public InputDeviceSetting(Context context, int titleId, int descriptionId,
EmulatedController controller)
{
super(context, null, titleId, descriptionId, null, null, null);
mController = controller;
refreshChoicesAndValues();
}
@Override
public String getSelectedChoice(Settings settings)
{
return mController.getDefaultDevice();
}
@Override
public String getSelectedValue(Settings settings)
{
return mController.getDefaultDevice();
}
@Override
public void setSelectedValue(Settings settings, String newValue)
{
mController.setDefaultDevice(newValue);
}
@Override
public void refreshChoicesAndValues()
{
String[] devices = ControllerInterface.getAllDeviceStrings();
mChoices = devices;
mValues = devices;
}
@Override
public boolean isEditable()
{
return true;
}
@Override
public boolean canClear()
{
return true;
}
@Override
public void clear(Settings settings)
{
setSelectedValue(settings, "");
}
}

View File

@ -0,0 +1,71 @@
// SPDX-License-Identifier: GPL-2.0-or-later
package org.dolphinemu.dolphinemu.features.input.model.view;
import org.dolphinemu.dolphinemu.features.input.model.controlleremu.Control;
import org.dolphinemu.dolphinemu.features.input.model.controlleremu.ControlReference;
import org.dolphinemu.dolphinemu.features.input.model.controlleremu.EmulatedController;
import org.dolphinemu.dolphinemu.features.settings.model.AbstractSetting;
import org.dolphinemu.dolphinemu.features.settings.model.view.SettingsItem;
public final class InputMappingControlSetting extends SettingsItem
{
private final ControlReference mControlReference;
private final EmulatedController mController;
public InputMappingControlSetting(Control control, EmulatedController controller)
{
super(control.getUiName(), "");
mControlReference = control.getControlReference();
mController = controller;
}
public String getValue()
{
return mControlReference.getExpression();
}
public void setValue(String expr)
{
mControlReference.setExpression(expr);
mController.updateSingleControlReference(mControlReference);
}
public void clearValue()
{
setValue("");
}
@Override
public int getType()
{
return TYPE_INPUT_MAPPING_CONTROL;
}
@Override
public AbstractSetting getSetting()
{
return null;
}
@Override
public boolean isEditable()
{
return true;
}
public EmulatedController getController()
{
return mController;
}
public ControlReference getControlReference()
{
return mControlReference;
}
public boolean isInput()
{
return mControlReference.isInput();
}
}

View File

@ -0,0 +1,55 @@
// SPDX-License-Identifier: GPL-2.0-or-later
package org.dolphinemu.dolphinemu.features.input.ui;
import android.view.LayoutInflater;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import org.dolphinemu.dolphinemu.databinding.ListItemAdvancedMappingControlBinding;
import java.util.function.Consumer;
public final class AdvancedMappingControlAdapter
extends RecyclerView.Adapter<AdvancedMappingControlViewHolder>
{
private final Consumer<String> mOnClickCallback;
private String[] mControls = new String[0];
public AdvancedMappingControlAdapter(Consumer<String> onClickCallback)
{
mOnClickCallback = onClickCallback;
}
@NonNull @Override
public AdvancedMappingControlViewHolder onCreateViewHolder(@NonNull ViewGroup parent,
int viewType)
{
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
ListItemAdvancedMappingControlBinding binding =
ListItemAdvancedMappingControlBinding.inflate(inflater);
return new AdvancedMappingControlViewHolder(binding, mOnClickCallback);
}
@Override
public void onBindViewHolder(@NonNull AdvancedMappingControlViewHolder holder, int position)
{
holder.bind(mControls[position]);
}
@Override
public int getItemCount()
{
return mControls.length;
}
public void setControls(String[] controls)
{
mControls = controls;
notifyDataSetChanged();
}
}

View File

@ -0,0 +1,34 @@
// SPDX-License-Identifier: GPL-2.0-or-later
package org.dolphinemu.dolphinemu.features.input.ui;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import org.dolphinemu.dolphinemu.databinding.ListItemAdvancedMappingControlBinding;
import java.util.function.Consumer;
public class AdvancedMappingControlViewHolder extends RecyclerView.ViewHolder
{
private final ListItemAdvancedMappingControlBinding mBinding;
private String mName;
public AdvancedMappingControlViewHolder(@NonNull ListItemAdvancedMappingControlBinding binding,
Consumer<String> onClickCallback)
{
super(binding.getRoot());
mBinding = binding;
binding.getRoot().setOnClickListener(view -> onClickCallback.accept(mName));
}
public void bind(String name)
{
mName = name;
mBinding.textName.setText(name);
}
}

View File

@ -0,0 +1,151 @@
// SPDX-License-Identifier: GPL-2.0-or-later
package org.dolphinemu.dolphinemu.features.input.ui;
import android.content.Context;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import androidx.appcompat.app.AlertDialog;
import androidx.recyclerview.widget.LinearLayoutManager;
import com.google.android.material.divider.MaterialDividerItemDecoration;
import org.dolphinemu.dolphinemu.databinding.DialogAdvancedMappingBinding;
import org.dolphinemu.dolphinemu.features.input.model.ControllerInterface;
import org.dolphinemu.dolphinemu.features.input.model.CoreDevice;
import org.dolphinemu.dolphinemu.features.input.model.MappingCommon;
import org.dolphinemu.dolphinemu.features.input.model.controlleremu.ControlReference;
import org.dolphinemu.dolphinemu.features.input.model.controlleremu.EmulatedController;
import java.util.Arrays;
import java.util.Optional;
public final class AdvancedMappingDialog extends AlertDialog
implements AdapterView.OnItemClickListener
{
private final DialogAdvancedMappingBinding mBinding;
private final ControlReference mControlReference;
private final EmulatedController mController;
private final String[] mDevices;
private final AdvancedMappingControlAdapter mControlAdapter;
private String mSelectedDevice;
public AdvancedMappingDialog(Context context, DialogAdvancedMappingBinding binding,
ControlReference controlReference, EmulatedController controller)
{
super(context);
mBinding = binding;
mControlReference = controlReference;
mController = controller;
mDevices = ControllerInterface.getAllDeviceStrings();
// TODO: Remove workaround for text filtering issue in material components when fixed
// https://github.com/material-components/material-components-android/issues/1464
mBinding.dropdownDevice.setSaveEnabled(false);
binding.dropdownDevice.setOnItemClickListener(this);
ArrayAdapter<String> deviceAdapter = new ArrayAdapter<>(
context, android.R.layout.simple_spinner_dropdown_item, mDevices);
binding.dropdownDevice.setAdapter(deviceAdapter);
mControlAdapter = new AdvancedMappingControlAdapter(this::onControlClicked);
mBinding.listControl.setAdapter(mControlAdapter);
mBinding.listControl.setLayoutManager(new LinearLayoutManager(context));
MaterialDividerItemDecoration divider =
new MaterialDividerItemDecoration(context, LinearLayoutManager.VERTICAL);
divider.setLastItemDecorated(false);
mBinding.listControl.addItemDecoration(divider);
binding.editExpression.setText(controlReference.getExpression());
selectDefaultDevice();
}
public String getExpression()
{
return mBinding.editExpression.getText().toString();
}
@Override
public void onItemClick(AdapterView<?> adapterView, View view, int position, long id)
{
setSelectedDevice(mDevices[position]);
}
private void setSelectedDevice(String deviceString)
{
mSelectedDevice = deviceString;
CoreDevice device = ControllerInterface.getDevice(deviceString);
if (device == null)
setControls(new CoreDevice.Control[0]);
else if (mControlReference.isInput())
setControls(device.getInputs());
else
setControls(device.getOutputs());
}
private void setControls(CoreDevice.Control[] controls)
{
mControlAdapter.setControls(
Arrays.stream(controls)
.map(CoreDevice.Control::getName)
.toArray(String[]::new));
}
private void onControlClicked(String control)
{
String expression = MappingCommon.getExpressionForControl(control, mSelectedDevice,
mController.getDefaultDevice());
int start = Math.max(mBinding.editExpression.getSelectionStart(), 0);
int end = Math.max(mBinding.editExpression.getSelectionEnd(), 0);
mBinding.editExpression.getText().replace(
Math.min(start, end), Math.max(start, end), expression, 0, expression.length());
}
private void selectDefaultDevice()
{
String defaultDevice = mController.getDefaultDevice();
boolean isInput = mControlReference.isInput();
if (Arrays.asList(mDevices).contains(defaultDevice) &&
(isInput || deviceHasOutputs(defaultDevice)))
{
// The default device is available, and it's an appropriate choice. Pick it
setSelectedDevice(defaultDevice);
mBinding.dropdownDevice.setText(defaultDevice, false);
return;
}
else if (!isInput)
{
// Find the first device that has an output. (Most built-in devices don't have any)
Optional<String> deviceWithOutputs = Arrays.stream(mDevices)
.filter(AdvancedMappingDialog::deviceHasOutputs)
.findFirst();
if (deviceWithOutputs.isPresent())
{
setSelectedDevice(deviceWithOutputs.get());
mBinding.dropdownDevice.setText(deviceWithOutputs.get(), false);
return;
}
}
// Nothing found
setSelectedDevice("");
}
private static boolean deviceHasOutputs(String deviceString)
{
CoreDevice device = ControllerInterface.getDevice(deviceString);
return device != null && device.getOutputs().length > 0;
}
}

View File

@ -0,0 +1,99 @@
// SPDX-License-Identifier: GPL-2.0-or-later
package org.dolphinemu.dolphinemu.features.input.ui;
import android.app.Activity;
import android.view.InputDevice;
import android.view.KeyEvent;
import android.view.MotionEvent;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import org.dolphinemu.dolphinemu.features.input.model.ControllerInterface;
import org.dolphinemu.dolphinemu.features.input.model.MappingCommon;
import org.dolphinemu.dolphinemu.features.input.model.view.InputMappingControlSetting;
/**
* {@link AlertDialog} derivative that listens for
* motion events from controllers and joysticks.
*/
public final class MotionAlertDialog extends AlertDialog
{
private final Activity mActivity;
private final InputMappingControlSetting mSetting;
private final boolean mAllDevices;
private boolean mRunning = false;
/**
* Constructor
*
* @param activity The current {@link Activity}.
* @param setting The setting to show this dialog for.
* @param allDevices Whether to detect inputs from devices other than the configured one.
*/
public MotionAlertDialog(Activity activity, InputMappingControlSetting setting,
boolean allDevices)
{
super(activity);
mActivity = activity;
mSetting = setting;
mAllDevices = allDevices;
}
@Override
protected void onStart()
{
super.onStart();
mRunning = true;
new Thread(() ->
{
String result = MappingCommon.detectInput(mSetting.getController(), mAllDevices);
mActivity.runOnUiThread(() ->
{
if (mRunning)
{
mSetting.setValue(result);
dismiss();
}
});
}).start();
}
@Override
protected void onStop()
{
super.onStop();
mRunning = false;
}
@Override
public boolean dispatchKeyEvent(KeyEvent event)
{
ControllerInterface.dispatchKeyEvent(event);
if (event.getKeyCode() == KeyEvent.KEYCODE_BACK && event.isLongPress())
{
// Special case: Let the user cancel by long-pressing Back (intended for non-touch devices)
mSetting.clearValue();
dismiss();
}
return true;
}
@Override
public boolean dispatchGenericMotionEvent(@NonNull MotionEvent event)
{
if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0)
{
// Special case: Let the user cancel by touching an on-screen button
return super.dispatchGenericMotionEvent(event);
}
ControllerInterface.dispatchGenericMotionEvent(event);
return true;
}
}

View File

@ -0,0 +1,65 @@
// SPDX-License-Identifier: GPL-2.0-or-later
package org.dolphinemu.dolphinemu.features.input.ui;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import org.dolphinemu.dolphinemu.databinding.ListItemProfileBinding;
public final class ProfileAdapter extends RecyclerView.Adapter<ProfileViewHolder>
{
private final Context mContext;
private final ProfileDialogPresenter mPresenter;
private final String[] mStockProfileNames;
private final String[] mUserProfileNames;
public ProfileAdapter(Context context, ProfileDialogPresenter presenter)
{
mContext = context;
mPresenter = presenter;
mStockProfileNames = presenter.getProfileNames(true);
mUserProfileNames = presenter.getProfileNames(false);
}
@NonNull @Override
public ProfileViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType)
{
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
ListItemProfileBinding binding = ListItemProfileBinding.inflate(inflater, parent, false);
return new ProfileViewHolder(mPresenter, binding);
}
@Override
public void onBindViewHolder(@NonNull ProfileViewHolder holder, int position)
{
if (position < mStockProfileNames.length)
{
holder.bind(mStockProfileNames[position], true);
return;
}
position -= mStockProfileNames.length;
if (position < mUserProfileNames.length)
{
holder.bind(mUserProfileNames[position], false);
return;
}
holder.bindAsEmpty(mContext);
}
@Override
public int getItemCount()
{
return mStockProfileNames.length + mUserProfileNames.length + 1;
}
}

View File

@ -0,0 +1,76 @@
// SPDX-License-Identifier: GPL-2.0-or-later
package org.dolphinemu.dolphinemu.features.input.ui
import android.os.Build
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.LinearLayoutManager
import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
import com.google.android.material.divider.MaterialDividerItemDecoration
import org.dolphinemu.dolphinemu.R
import org.dolphinemu.dolphinemu.databinding.DialogInputProfilesBinding
import org.dolphinemu.dolphinemu.features.settings.ui.MenuTag
class ProfileDialog : BottomSheetDialogFragment() {
private var presenter: ProfileDialogPresenter? = null
private var _binding: DialogInputProfilesBinding? = null
private val binding get() = _binding!!
override fun onCreate(savedInstanceState: Bundle?) {
val menuTag: MenuTag = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
requireArguments().getSerializable(KEY_MENU_TAG, MenuTag::class.java) as MenuTag
} else {
requireArguments().getSerializable(KEY_MENU_TAG) as MenuTag
}
presenter = ProfileDialogPresenter(this, menuTag)
super.onCreate(savedInstanceState)
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = DialogInputProfilesBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
binding.profileList.adapter = ProfileAdapter(context, presenter)
binding.profileList.layoutManager = LinearLayoutManager(context)
val divider = MaterialDividerItemDecoration(requireActivity(), LinearLayoutManager.VERTICAL)
divider.isLastItemDecorated = false
binding.profileList.addItemDecoration(divider)
// You can't expand a bottom sheet with a controller/remote/other non-touch devices
val behavior: BottomSheetBehavior<View> = BottomSheetBehavior.from(view.parent as View)
if (!resources.getBoolean(R.bool.hasTouch)) {
behavior.state = BottomSheetBehavior.STATE_EXPANDED
}
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
companion object {
private const val KEY_MENU_TAG = "menu_tag"
@JvmStatic
fun create(menuTag: MenuTag): ProfileDialog {
val dialog = ProfileDialog()
val args = Bundle()
args.putSerializable(KEY_MENU_TAG, menuTag)
dialog.arguments = args
return dialog
}
}
}

View File

@ -0,0 +1,157 @@
// SPDX-License-Identifier: GPL-2.0-or-later
package org.dolphinemu.dolphinemu.features.input.ui;
import android.content.Context;
import android.view.LayoutInflater;
import androidx.annotation.NonNull;
import androidx.fragment.app.DialogFragment;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.google.android.material.textfield.TextInputEditText;
import org.dolphinemu.dolphinemu.R;
import org.dolphinemu.dolphinemu.databinding.DialogInputStringBinding;
import org.dolphinemu.dolphinemu.features.settings.ui.MenuTag;
import org.dolphinemu.dolphinemu.features.settings.ui.SettingsActivityView;
import org.dolphinemu.dolphinemu.utils.DirectoryInitialization;
import java.io.File;
import java.text.Collator;
import java.util.Arrays;
public final class ProfileDialogPresenter
{
private static final String EXTENSION = ".ini";
private final Context mContext;
private final DialogFragment mDialog;
private final MenuTag mMenuTag;
public ProfileDialogPresenter(MenuTag menuTag)
{
mContext = null;
mDialog = null;
mMenuTag = menuTag;
}
public ProfileDialogPresenter(DialogFragment dialog, MenuTag menuTag)
{
mContext = dialog.getContext();
mDialog = dialog;
mMenuTag = menuTag;
}
public String[] getProfileNames(boolean stock)
{
File[] profiles = new File(getProfileDirectoryPath(stock)).listFiles(
file -> !file.isDirectory() && file.getName().endsWith(EXTENSION));
if (profiles == null)
return new String[0];
return Arrays.stream(profiles)
.map(file -> file.getName().substring(0, file.getName().length() - EXTENSION.length()))
.sorted(Collator.getInstance())
.toArray(String[]::new);
}
public void loadProfile(@NonNull String profileName, boolean stock)
{
new MaterialAlertDialogBuilder(mContext)
.setMessage(mContext.getString(R.string.input_profile_confirm_load, profileName))
.setPositiveButton(R.string.yes, (dialogInterface, i) ->
{
mMenuTag.getCorrespondingEmulatedController()
.loadProfile(getProfilePath(profileName, stock));
((SettingsActivityView) mDialog.requireActivity()).onControllerSettingsChanged();
mDialog.dismiss();
})
.setNegativeButton(R.string.no, null)
.show();
}
public void saveProfile(@NonNull String profileName)
{
// If the user is saving over an existing profile, we should show an overwrite warning.
// If the user is creating a new profile, we normally shouldn't show a warning,
// but if they've entered the name of an existing profile, we should shown an overwrite warning.
String profilePath = getProfilePath(profileName, false);
if (!new File(profilePath).exists())
{
mMenuTag.getCorrespondingEmulatedController().saveProfile(profilePath);
mDialog.dismiss();
}
else
{
new MaterialAlertDialogBuilder(mContext)
.setMessage(mContext.getString(R.string.input_profile_confirm_save, profileName))
.setPositiveButton(R.string.yes, (dialogInterface, i) ->
{
mMenuTag.getCorrespondingEmulatedController().saveProfile(profilePath);
mDialog.dismiss();
})
.setNegativeButton(R.string.no, null)
.show();
}
}
public void saveProfileAndPromptForName()
{
LayoutInflater inflater = LayoutInflater.from(mContext);
DialogInputStringBinding binding = DialogInputStringBinding.inflate(inflater);
TextInputEditText input = binding.input;
new MaterialAlertDialogBuilder(mContext)
.setView(binding.getRoot())
.setPositiveButton(R.string.ok, (dialogInterface, i) ->
saveProfile(input.getText().toString()))
.setNegativeButton(R.string.cancel, null)
.show();
}
public void deleteProfile(@NonNull String profileName)
{
new MaterialAlertDialogBuilder(mContext)
.setMessage(mContext.getString(R.string.input_profile_confirm_delete, profileName))
.setPositiveButton(R.string.yes, (dialogInterface, i) ->
{
new File(getProfilePath(profileName, false)).delete();
mDialog.dismiss();
})
.setNegativeButton(R.string.no, null)
.show();
}
private String getProfileDirectoryName()
{
if (mMenuTag.isGCPadMenu())
return "GCPad";
else if (mMenuTag.isWiimoteMenu())
return "Wiimote";
else
throw new UnsupportedOperationException();
}
private String getProfileDirectoryPath(boolean stock)
{
if (stock)
{
return DirectoryInitialization.getSysDirectory() + "/Profiles/" + getProfileDirectoryName() +
'/';
}
else
{
return DirectoryInitialization.getUserDirectory() + "/Config/Profiles/" +
getProfileDirectoryName() + '/';
}
}
private String getProfilePath(String profileName, boolean stock)
{
return getProfileDirectoryPath(stock) + profileName + EXTENSION;
}
}

View File

@ -0,0 +1,76 @@
// SPDX-License-Identifier: GPL-2.0-or-later
package org.dolphinemu.dolphinemu.features.input.ui;
import android.content.Context;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import org.dolphinemu.dolphinemu.R;
import org.dolphinemu.dolphinemu.databinding.ListItemProfileBinding;
public class ProfileViewHolder extends RecyclerView.ViewHolder
{
private final ProfileDialogPresenter mPresenter;
private final ListItemProfileBinding mBinding;
private String mProfileName;
private boolean mStock;
public ProfileViewHolder(@NonNull ProfileDialogPresenter presenter,
@NonNull ListItemProfileBinding binding)
{
super(binding.getRoot());
mPresenter = presenter;
mBinding = binding;
binding.buttonLoad.setOnClickListener(view -> loadProfile());
binding.buttonSave.setOnClickListener(view -> saveProfile());
binding.buttonDelete.setOnClickListener(view -> deleteProfile());
}
public void bind(String profileName, boolean stock)
{
mProfileName = profileName;
mStock = stock;
mBinding.textName.setText(profileName);
mBinding.buttonLoad.setVisibility(View.VISIBLE);
mBinding.buttonSave.setVisibility(stock ? View.GONE : View.VISIBLE);
mBinding.buttonDelete.setVisibility(stock ? View.GONE : View.VISIBLE);
}
public void bindAsEmpty(Context context)
{
mProfileName = null;
mStock = false;
mBinding.textName.setText(context.getText(R.string.input_profile_new));
mBinding.buttonLoad.setVisibility(View.GONE);
mBinding.buttonSave.setVisibility(View.VISIBLE);
mBinding.buttonDelete.setVisibility(View.GONE);
}
private void loadProfile()
{
mPresenter.loadProfile(mProfileName, mStock);
}
private void saveProfile()
{
if (mProfileName == null)
mPresenter.saveProfileAndPromptForName();
else
mPresenter.saveProfile(mProfileName);
}
private void deleteProfile()
{
mPresenter.deleteProfile(mProfileName);
}
}

View File

@ -0,0 +1,77 @@
// SPDX-License-Identifier: GPL-2.0-or-later
package org.dolphinemu.dolphinemu.features.input.ui.viewholder;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import org.dolphinemu.dolphinemu.databinding.ListItemMappingBinding;
import org.dolphinemu.dolphinemu.features.input.model.view.InputMappingControlSetting;
import org.dolphinemu.dolphinemu.features.settings.model.view.SettingsItem;
import org.dolphinemu.dolphinemu.features.settings.ui.SettingsAdapter;
import org.dolphinemu.dolphinemu.features.settings.ui.viewholder.SettingViewHolder;
public final class InputMappingControlSettingViewHolder extends SettingViewHolder
{
private InputMappingControlSetting mItem;
private final ListItemMappingBinding mBinding;
public InputMappingControlSettingViewHolder(@NonNull ListItemMappingBinding binding,
SettingsAdapter adapter)
{
super(binding.getRoot(), adapter);
mBinding = binding;
}
@Override
public void bind(SettingsItem item)
{
mItem = (InputMappingControlSetting) item;
mBinding.textSettingName.setText(mItem.getName());
mBinding.textSettingDescription.setText(mItem.getValue());
mBinding.buttonAdvancedSettings.setOnClickListener(this::onLongClick);
setStyle(mBinding.textSettingName, mItem);
}
@Override
public void onClick(View clicked)
{
if (!mItem.isEditable())
{
showNotRuntimeEditableError();
return;
}
if (mItem.isInput())
getAdapter().onInputMappingClick(mItem, getBindingAdapterPosition());
else
getAdapter().onAdvancedInputMappingClick(mItem, getBindingAdapterPosition());
setStyle(mBinding.textSettingName, mItem);
}
@Override
public boolean onLongClick(View clicked)
{
if (!mItem.isEditable())
{
showNotRuntimeEditableError();
return true;
}
getAdapter().onAdvancedInputMappingClick(mItem, getBindingAdapterPosition());
return true;
}
@Nullable @Override
protected SettingsItem getItem()
{
return mItem;
}
}

View File

@ -100,8 +100,6 @@ public enum BooleanSetting implements AbstractBooleanSetting
"UseBlackBackgrounds", false),
MAIN_JOYSTICK_REL_CENTER(Settings.FILE_DOLPHIN, Settings.SECTION_INI_ANDROID,
"JoystickRelCenter", true),
MAIN_PHONE_RUMBLE(Settings.FILE_DOLPHIN, Settings.SECTION_INI_ANDROID,
"PhoneRumble", true),
MAIN_SHOW_INPUT_OVERLAY(Settings.FILE_DOLPHIN, Settings.SECTION_INI_ANDROID,
"ShowInputOverlay", true),
MAIN_IR_ALWAYS_RECENTER(Settings.FILE_DOLPHIN, Settings.SECTION_INI_ANDROID,

View File

@ -33,6 +33,10 @@ public enum IntSetting implements AbstractIntSetting
MAIN_AUDIO_VOLUME(Settings.FILE_DOLPHIN, Settings.SECTION_INI_DSP, "Volume", 100),
MAIN_OVERLAY_GC_CONTROLLER(Settings.FILE_DOLPHIN, Settings.SECTION_INI_ANDROID,
"OverlayGCController", 0), // Defaults to GameCube controller 1
MAIN_OVERLAY_WII_CONTROLLER(Settings.FILE_DOLPHIN, Settings.SECTION_INI_ANDROID,
"OverlayWiiController", 4), // Defaults to Wii Remote 1
MAIN_CONTROL_SCALE(Settings.FILE_DOLPHIN, Settings.SECTION_INI_ANDROID, "ControlScale", 50),
MAIN_CONTROL_OPACITY(Settings.FILE_DOLPHIN, Settings.SECTION_INI_ANDROID, "ControlOpacity", 65),
MAIN_EMULATION_ORIENTATION(Settings.FILE_DOLPHIN, Settings.SECTION_INI_ANDROID,
@ -41,7 +45,6 @@ public enum IntSetting implements AbstractIntSetting
MAIN_INTERFACE_THEME_MODE(Settings.FILE_DOLPHIN, Settings.SECTION_INI_ANDROID,
"InterfaceThemeMode", -1),
MAIN_LAST_PLATFORM_TAB(Settings.FILE_DOLPHIN, Settings.SECTION_INI_ANDROID, "LastPlatformTab", 0),
MAIN_MOTION_CONTROLS(Settings.FILE_DOLPHIN, Settings.SECTION_INI_ANDROID, "MotionControls", 1),
MAIN_IR_MODE(Settings.FILE_DOLPHIN, Settings.SECTION_INI_ANDROID, "IRMode",
InputOverlayPointer.MODE_FOLLOW),
@ -189,4 +192,16 @@ public enum IntSetting implements AbstractIntSetting
{
NativeConfig.setInt(layer, mFile, mSection, mKey, newValue);
}
public static IntSetting getSettingForSIDevice(int channel)
{
return new IntSetting[]{MAIN_SI_DEVICE_0, MAIN_SI_DEVICE_1, MAIN_SI_DEVICE_2, MAIN_SI_DEVICE_3}
[channel];
}
public static IntSetting getSettingForWiimoteSource(int index)
{
return new IntSetting[]{WIIMOTE_1_SOURCE, WIIMOTE_2_SOURCE, WIIMOTE_3_SOURCE, WIIMOTE_4_SOURCE,
WIIMOTE_BB_SOURCE}[index];
}
}

View File

@ -1,28 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-or-later
package org.dolphinemu.dolphinemu.features.settings.model;
import androidx.annotation.NonNull;
public class LegacyFloatSetting extends AbstractLegacySetting implements AbstractFloatSetting
{
private final float mDefaultValue;
public LegacyFloatSetting(String file, String section, String key, float defaultValue)
{
super(file, section, key);
mDefaultValue = defaultValue;
}
@Override
public float getFloat(@NonNull Settings settings)
{
return settings.getSection(mFile, mSection).getFloat(mKey, mDefaultValue);
}
@Override
public void setFloat(@NonNull Settings settings, float newValue)
{
settings.getSection(mFile, mSection).setFloat(mKey, newValue);
}
}

View File

@ -8,6 +8,7 @@ import android.widget.Toast;
import org.dolphinemu.dolphinemu.NativeLibrary;
import org.dolphinemu.dolphinemu.R;
import org.dolphinemu.dolphinemu.features.input.model.MappingCommon;
import org.dolphinemu.dolphinemu.features.settings.ui.SettingsActivityView;
import org.dolphinemu.dolphinemu.features.settings.utils.SettingsFile;
import org.dolphinemu.dolphinemu.services.GameFileCacheManager;
@ -24,7 +25,6 @@ public class Settings implements Closeable
public static final String FILE_SYSCONF = "SYSCONF";
public static final String FILE_GFX = "GFX";
public static final String FILE_LOGGER = "Logger";
public static final String FILE_GCPAD = "GCPadNew";
public static final String FILE_WIIMOTE = "WiimoteNew";
public static final String SECTION_INI_ANDROID = "Android";
@ -46,10 +46,7 @@ public class Settings implements Closeable
public static final String SECTION_STEREOSCOPY = "Stereoscopy";
public static final String SECTION_WIIMOTE = "Wiimote";
public static final String SECTION_BINDINGS = "Android";
public static final String SECTION_CONTROLS = "Controls";
public static final String SECTION_PROFILE = "Profile";
public static final String SECTION_ANALYTICS = "Analytics";
@ -64,7 +61,6 @@ public class Settings implements Closeable
FILE_WIIMOTE};
private Map<String, IniFile> mIniFiles = new HashMap<>();
private final Map<String, IniFile> mWiimoteProfileFiles = new HashMap<>();
private boolean mLoadedRecursiveIsoPathsValue = false;
@ -99,50 +95,6 @@ public class Settings implements Closeable
return mIsWii;
}
public IniFile getWiimoteProfile(String profile, int padID)
{
IniFile wiimoteProfileIni = mWiimoteProfileFiles.computeIfAbsent(profile, profileComputed ->
{
IniFile newIni = new IniFile();
newIni.load(SettingsFile.getWiiProfile(profileComputed), false);
return newIni;
});
if (!wiimoteProfileIni.exists(SECTION_PROFILE))
{
String defaultWiiProfilePath = DirectoryInitialization.getUserDirectory() +
"/Config/Profiles/Wiimote/WiimoteProfile.ini";
wiimoteProfileIni.load(defaultWiiProfilePath, false);
wiimoteProfileIni
.setString(SECTION_PROFILE, "Device", "Android/" + (padID + 4) + "/Touchscreen");
}
return wiimoteProfileIni;
}
public void enableWiimoteProfile(Settings settings, String profile, String profileKey)
{
getWiimoteControlsSection(settings).setString(profileKey, profile);
}
public boolean disableWiimoteProfile(Settings settings, String profileKey)
{
return getWiimoteControlsSection(settings).delete(profileKey);
}
public boolean isWiimoteProfileEnabled(Settings settings, String profile,
String profileKey)
{
return profile.equals(getWiimoteControlsSection(settings).getString(profileKey, ""));
}
private IniFile.Section getWiimoteControlsSection(Settings settings)
{
return settings.getSection(GAME_SETTINGS_PLACEHOLDER_FILE_NAME, SECTION_CONTROLS);
}
public int getWriteLayer()
{
return isGameSpecific() ? NativeConfig.LAYER_LOCAL_GAME : NativeConfig.LAYER_BASE_OR_CURRENT;
@ -218,13 +170,14 @@ public class Settings implements Closeable
SettingsFile.saveFile(entry.getKey(), entry.getValue(), view);
}
MappingCommon.save();
NativeConfig.save(NativeConfig.LAYER_BASE);
if (!NativeLibrary.IsRunning())
{
// Notify the native code of the changes to legacy settings
NativeLibrary.ReloadConfig();
NativeLibrary.ReloadWiimoteConfig();
}
// LogManager does use the new config system, but doesn't pick up on changes automatically
@ -251,11 +204,6 @@ public class Settings implements Closeable
NativeConfig.save(NativeConfig.LAYER_LOCAL_GAME);
}
for (Map.Entry<String, IniFile> entry : mWiimoteProfileFiles.entrySet())
{
entry.getValue().save(SettingsFile.getWiiProfile(entry.getKey()));
}
}
public void clearSettings()

View File

@ -1,70 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-or-later
package org.dolphinemu.dolphinemu.features.settings.model;
import androidx.annotation.NonNull;
import org.dolphinemu.dolphinemu.features.settings.utils.SettingsFile;
// This stuff is pretty ugly. It's a kind of workaround for certain controller settings
// not actually being available as game-specific settings.
public class WiimoteProfileBooleanSetting implements AbstractBooleanSetting
{
private final int mPadID;
private final String mSection;
private final String mKey;
private final boolean mDefaultValue;
private final String mProfileKey;
private final String mProfile;
public WiimoteProfileBooleanSetting(String gameID, int padID, String section, String key,
boolean defaultValue)
{
mPadID = padID;
mSection = section;
mKey = key;
mDefaultValue = defaultValue;
mProfileKey = SettingsFile.KEY_WIIMOTE_PROFILE + (padID + 1);
mProfile = gameID + "_Wii" + padID;
}
@Override
public boolean isOverridden(@NonNull Settings settings)
{
return settings.isWiimoteProfileEnabled(settings, mProfile, mProfileKey);
}
@Override
public boolean isRuntimeEditable()
{
return false;
}
@Override
public boolean delete(@NonNull Settings settings)
{
return settings.disableWiimoteProfile(settings, mProfileKey);
}
@Override
public boolean getBoolean(@NonNull Settings settings)
{
if (settings.isWiimoteProfileEnabled(settings, mProfile, mProfileKey))
return settings.getWiimoteProfile(mProfile, mPadID).getBoolean(mSection, mKey, mDefaultValue);
else
return mDefaultValue;
}
@Override
public void setBoolean(@NonNull Settings settings, boolean newValue)
{
settings.getWiimoteProfile(mProfile, mPadID).setBoolean(mSection, mKey, newValue);
settings.enableWiimoteProfile(settings, mProfile, mProfileKey);
}
}

View File

@ -1,70 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-or-later
package org.dolphinemu.dolphinemu.features.settings.model;
import androidx.annotation.NonNull;
import org.dolphinemu.dolphinemu.features.settings.utils.SettingsFile;
// This stuff is pretty ugly. It's a kind of workaround for certain controller settings
// not actually being available as game-specific settings.
public class WiimoteProfileStringSetting implements AbstractStringSetting
{
private final int mPadID;
private final String mSection;
private final String mKey;
private final String mDefaultValue;
private final String mProfileKey;
private final String mProfile;
public WiimoteProfileStringSetting(String gameID, int padID, String section, String key,
String defaultValue)
{
mPadID = padID;
mSection = section;
mKey = key;
mDefaultValue = defaultValue;
mProfileKey = SettingsFile.KEY_WIIMOTE_PROFILE + (padID + 1);
mProfile = gameID + "_Wii" + padID;
}
@Override
public boolean isOverridden(@NonNull Settings settings)
{
return settings.isWiimoteProfileEnabled(settings, mProfile, mProfileKey);
}
@Override
public boolean isRuntimeEditable()
{
return false;
}
@Override
public boolean delete(@NonNull Settings settings)
{
return settings.disableWiimoteProfile(settings, mProfileKey);
}
@NonNull @Override
public String getString(@NonNull Settings settings)
{
if (settings.isWiimoteProfileEnabled(settings, mProfile, mProfileKey))
return settings.getWiimoteProfile(mProfile, mPadID).getString(mSection, mKey, mDefaultValue);
else
return mDefaultValue;
}
@Override
public void setString(@NonNull Settings settings, @NonNull String newValue)
{
settings.getWiimoteProfile(mProfile, mPadID).setString(mSection, mKey, newValue);
settings.enableWiimoteProfile(settings, mProfile, mProfileKey);
}
}

View File

@ -19,6 +19,13 @@ public class FloatSliderSetting extends SliderSetting
mSetting = setting;
}
public FloatSliderSetting(AbstractFloatSetting setting, CharSequence name,
CharSequence description, int min, int max, String units)
{
super(name, description, min, max, units);
mSetting = setting;
}
public int getSelectedValue(Settings settings)
{
return Math.round(mSetting.getFloat(settings));

View File

@ -13,6 +13,11 @@ public class HeaderSetting extends SettingsItem
super(context, titleId, descriptionId);
}
public HeaderSetting(CharSequence title, CharSequence description)
{
super(title, description);
}
@Override
public int getType()
{

View File

@ -1,108 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-or-later
package org.dolphinemu.dolphinemu.features.settings.model.view;
import android.content.Context;
import android.content.SharedPreferences;
import android.view.InputDevice;
import android.view.KeyEvent;
import androidx.preference.PreferenceManager;
import org.dolphinemu.dolphinemu.DolphinApplication;
import org.dolphinemu.dolphinemu.features.settings.model.AbstractSetting;
import org.dolphinemu.dolphinemu.features.settings.model.Settings;
public class InputBindingSetting extends SettingsItem
{
private String mFile;
private String mSection;
private String mKey;
private String mGameId;
public InputBindingSetting(Context context, String file, String section, String key, int titleId,
String gameId)
{
super(context, titleId, 0);
mFile = file;
mSection = section;
mKey = key;
mGameId = gameId;
}
public String getKey()
{
return mKey;
}
public String getValue(Settings settings)
{
return settings.getSection(mFile, mSection).getString(mKey, "");
}
/**
* Saves the provided key input setting both to the INI file (so native code can use it) and as
* an Android preference (so it persists correctly and is human-readable.)
*
* @param keyEvent KeyEvent of this key press.
*/
public void onKeyInput(Settings settings, KeyEvent keyEvent)
{
InputDevice device = keyEvent.getDevice();
String bindStr = "Device '" + device.getDescriptor() + "'-Button " + keyEvent.getKeyCode();
String uiString = device.getName() + ": Button " + keyEvent.getKeyCode();
setValue(settings, bindStr, uiString);
}
/**
* Saves the provided motion input setting both to the INI file (so native code can use it) and as
* an Android preference (so it persists correctly and is human-readable.)
*
* @param device InputDevice from which the input event originated.
* @param motionRange MotionRange of the movement
* @param axisDir Either '-' or '+'
*/
public void onMotionInput(Settings settings, InputDevice device,
InputDevice.MotionRange motionRange, char axisDir)
{
String bindStr =
"Device '" + device.getDescriptor() + "'-Axis " + motionRange.getAxis() + axisDir;
String uiString = device.getName() + ": Axis " + motionRange.getAxis() + axisDir;
setValue(settings, bindStr, uiString);
}
public void setValue(Settings settings, String bind, String ui)
{
SharedPreferences
preferences =
PreferenceManager.getDefaultSharedPreferences(DolphinApplication.getAppContext());
SharedPreferences.Editor editor = preferences.edit();
editor.putString(mKey + mGameId, ui);
editor.apply();
settings.getSection(mFile, mSection).setString(mKey, bind);
}
public void clearValue(Settings settings)
{
setValue(settings, "", "");
}
@Override
public int getType()
{
return TYPE_INPUT_BINDING;
}
public String getGameId()
{
return mGameId;
}
@Override
public AbstractSetting getSetting()
{
return null;
}
}

View File

@ -1,69 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-or-later
package org.dolphinemu.dolphinemu.features.settings.model.view;
import android.content.Context;
import android.os.Vibrator;
import android.view.InputDevice;
import android.view.KeyEvent;
import org.dolphinemu.dolphinemu.DolphinApplication;
import org.dolphinemu.dolphinemu.R;
import org.dolphinemu.dolphinemu.features.settings.model.AbstractSetting;
import org.dolphinemu.dolphinemu.features.settings.model.Settings;
import org.dolphinemu.dolphinemu.utils.Rumble;
public class RumbleBindingSetting extends InputBindingSetting
{
public RumbleBindingSetting(Context context, String file, String section, String key, int titleId,
String gameId)
{
super(context, file, section, key, titleId, gameId);
}
/**
* Just need the device when saving rumble.
*/
@Override
public void onKeyInput(Settings settings, KeyEvent keyEvent)
{
saveRumble(settings, keyEvent.getDevice());
}
/**
* Just need the device when saving rumble.
*/
@Override
public void onMotionInput(Settings settings, InputDevice device,
InputDevice.MotionRange motionRange, char axisDir)
{
saveRumble(settings, device);
}
private void saveRumble(Settings settings, InputDevice device)
{
Vibrator vibrator = device.getVibrator();
if (vibrator != null && vibrator.hasVibrator())
{
setValue(settings, device.getDescriptor(), device.getName());
Rumble.doRumble(vibrator);
}
else
{
setValue(settings, "",
DolphinApplication.getAppContext().getString(R.string.rumble_not_found));
}
}
@Override
public int getType()
{
return TYPE_RUMBLE_BINDING;
}
@Override
public AbstractSetting getSetting()
{
return null;
}
}

View File

@ -21,9 +21,8 @@ public abstract class SettingsItem
public static final int TYPE_SINGLE_CHOICE = 2;
public static final int TYPE_SLIDER = 3;
public static final int TYPE_SUBMENU = 4;
public static final int TYPE_INPUT_BINDING = 5;
public static final int TYPE_INPUT_MAPPING_CONTROL = 5;
public static final int TYPE_STRING_SINGLE_CHOICE = 6;
public static final int TYPE_RUMBLE_BINDING = 7;
public static final int TYPE_SINGLE_CHOICE_DYNAMIC_DESCRIPTIONS = 8;
public static final int TYPE_FILE_PICKER = 9;
public static final int TYPE_RUN_RUNNABLE = 10;
@ -96,6 +95,11 @@ public abstract class SettingsItem
return getSetting() != null;
}
public boolean canClear()
{
return hasSetting();
}
public void clear(Settings settings)
{
getSetting().delete(settings);

View File

@ -23,6 +23,14 @@ public abstract class SliderSetting extends SettingsItem
mStepSize = stepSize;
}
public SliderSetting(CharSequence name, CharSequence description, int min, int max, String units)
{
super(name, description);
mMin = min;
mMax = max;
mUnits = units;
}
public abstract int getSelectedValue(Settings settings);
public int getMin()

View File

@ -12,11 +12,12 @@ import org.dolphinemu.dolphinemu.features.settings.ui.MenuTag;
public class StringSingleChoiceSetting extends SettingsItem
{
private AbstractStringSetting mSetting;
private final AbstractStringSetting mSetting;
private String[] mChoices;
private String[] mValues;
private MenuTag mMenuTag;
protected String[] mChoices;
protected String[] mValues;
private final MenuTag mMenuTag;
private int mNoChoicesAvailableString = 0;
public StringSingleChoiceSetting(Context context, AbstractStringSetting setting, int titleId,
int descriptionId, String[] choices, String[] values, MenuTag menuTag)
@ -34,6 +35,13 @@ public class StringSingleChoiceSetting extends SettingsItem
this(context, setting, titleId, descriptionId, choices, values, null);
}
public StringSingleChoiceSetting(Context context, AbstractStringSetting setting, int titleId,
int descriptionId, String[] choices, String[] values, int noChoicesAvailableString)
{
this(context, setting, titleId, descriptionId, choices, values, null);
mNoChoicesAvailableString = noChoicesAvailableString;
}
public StringSingleChoiceSetting(Context context, AbstractStringSetting setting, int titleId,
int descriptionId, int choicesId, int valuesId, MenuTag menuTag)
{
@ -60,6 +68,19 @@ public class StringSingleChoiceSetting extends SettingsItem
return mValues;
}
public String getChoiceAt(int index)
{
if (mChoices == null)
return null;
if (index >= 0 && index < mChoices.length)
{
return mChoices[index];
}
return "";
}
public String getValueAt(int index)
{
if (mValues == null)
@ -73,6 +94,11 @@ public class StringSingleChoiceSetting extends SettingsItem
return "";
}
public String getSelectedChoice(Settings settings)
{
return getChoiceAt(getSelectedValueIndex(settings));
}
public String getSelectedValue(Settings settings)
{
return mSetting.getString(settings);
@ -97,11 +123,20 @@ public class StringSingleChoiceSetting extends SettingsItem
return mMenuTag;
}
public int getNoChoicesAvailableString()
{
return mNoChoicesAvailableString;
}
public void setSelectedValue(Settings settings, String selection)
{
mSetting.setString(settings, selection);
}
public void refreshChoicesAndValues()
{
}
@Override
public int getType()
{

View File

@ -4,6 +4,8 @@ package org.dolphinemu.dolphinemu.features.settings.ui;
import androidx.annotation.NonNull;
import org.dolphinemu.dolphinemu.features.input.model.controlleremu.EmulatedController;
public enum MenuTag
{
SETTINGS("settings"),
@ -31,14 +33,26 @@ public enum MenuTag
GCPAD_2("gcpad", 1),
GCPAD_3("gcpad", 2),
GCPAD_4("gcpad", 3),
WIIMOTE_1("wiimote", 4),
WIIMOTE_2("wiimote", 5),
WIIMOTE_3("wiimote", 6),
WIIMOTE_4("wiimote", 7),
WIIMOTE_EXTENSION_1("wiimote_extension", 4),
WIIMOTE_EXTENSION_2("wiimote_extension", 5),
WIIMOTE_EXTENSION_3("wiimote_extension", 6),
WIIMOTE_EXTENSION_4("wiimote_extension", 7);
WIIMOTE_1("wiimote", 0),
WIIMOTE_2("wiimote", 1),
WIIMOTE_3("wiimote", 2),
WIIMOTE_4("wiimote", 3),
WIIMOTE_EXTENSION_1("wiimote_extension", 0),
WIIMOTE_EXTENSION_2("wiimote_extension", 1),
WIIMOTE_EXTENSION_3("wiimote_extension", 2),
WIIMOTE_EXTENSION_4("wiimote_extension", 3),
WIIMOTE_GENERAL_1("wiimote_general", 0),
WIIMOTE_GENERAL_2("wiimote_general", 1),
WIIMOTE_GENERAL_3("wiimote_general", 2),
WIIMOTE_GENERAL_4("wiimote_general", 3),
WIIMOTE_MOTION_SIMULATION_1("wiimote_motion_simulation", 0),
WIIMOTE_MOTION_SIMULATION_2("wiimote_motion_simulation", 1),
WIIMOTE_MOTION_SIMULATION_3("wiimote_motion_simulation", 2),
WIIMOTE_MOTION_SIMULATION_4("wiimote_motion_simulation", 3),
WIIMOTE_MOTION_INPUT_1("wiimote_motion_input", 0),
WIIMOTE_MOTION_INPUT_2("wiimote_motion_input", 1),
WIIMOTE_MOTION_INPUT_3("wiimote_motion_input", 2),
WIIMOTE_MOTION_INPUT_4("wiimote_motion_input", 3);
private String tag;
private int subType = -1;
@ -76,6 +90,16 @@ public enum MenuTag
return subType;
}
public EmulatedController getCorrespondingEmulatedController()
{
if (isGCPadMenu())
return EmulatedController.getGcPad(getSubType());
else if (isWiimoteMenu())
return EmulatedController.getWiimote(getSubType());
else
throw new UnsupportedOperationException();
}
public boolean isSerialPort1Menu()
{
return this == CONFIG_SERIALPORT1;
@ -112,11 +136,27 @@ public enum MenuTag
return getMenuTag("wiimote_extension", subtype);
}
public static MenuTag getWiimoteGeneralMenuTag(int subtype)
{
return getMenuTag("wiimote_general", subtype);
}
public static MenuTag getWiimoteMotionSimulationMenuTag(int subtype)
{
return getMenuTag("wiimote_motion_simulation", subtype);
}
public static MenuTag getWiimoteMotionInputMenuTag(int subtype)
{
return getMenuTag("wiimote_motion_input", subtype);
}
private static MenuTag getMenuTag(String tag, int subtype)
{
for (MenuTag menuTag : MenuTag.values())
{
if (menuTag.tag.equals(tag) && menuTag.subType == subtype) return menuTag;
if (menuTag.tag.equals(tag) && menuTag.subType == subtype)
return menuTag;
}
throw new IllegalArgumentException("You are asking for a menu that is not available or " +

View File

@ -9,6 +9,7 @@ import android.os.Bundle;
import android.provider.Settings;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.View;
import android.widget.Toast;
import androidx.annotation.NonNull;
@ -18,6 +19,7 @@ import androidx.core.graphics.Insets;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowCompat;
import androidx.core.view.WindowInsetsCompat;
import androidx.fragment.app.DialogFragment;
import androidx.fragment.app.FragmentTransaction;
import androidx.lifecycle.ViewModelProvider;
@ -41,15 +43,18 @@ public final class SettingsActivity extends AppCompatActivity implements Setting
private static final String ARG_GAME_ID = "game_id";
private static final String ARG_REVISION = "revision";
private static final String ARG_IS_WII = "is_wii";
private static final String KEY_MAPPING_ALL_DEVICES = "all_devices";
private static final String FRAGMENT_TAG = "settings";
private static final String FRAGMENT_DIALOG_TAG = "settings_dialog";
private SettingsActivityPresenter mPresenter;
private AlertDialog dialog;
private CollapsingToolbarLayout mToolbarLayout;
private ActivitySettingsBinding mBinding;
private boolean mMappingAllDevices = false;
public static void launch(Context context, MenuTag menuTag, String gameId, int revision,
boolean isWii)
{
@ -82,6 +87,10 @@ public final class SettingsActivity extends AppCompatActivity implements Setting
{
MainPresenter.skipRescanningLibrary();
}
else
{
mMappingAllDevices = savedInstanceState.getBoolean(KEY_MAPPING_ALL_DEVICES);
}
mBinding = ActivitySettingsBinding.inflate(getLayoutInflater());
setContentView(mBinding.getRoot());
@ -125,7 +134,10 @@ public final class SettingsActivity extends AppCompatActivity implements Setting
{
// Critical: If super method is not called, rotations will be busted.
super.onSaveInstanceState(outState);
mPresenter.saveState(outState);
outState.putBoolean(KEY_MAPPING_ALL_DEVICES, mMappingAllDevices);
}
@Override
@ -181,6 +193,12 @@ public final class SettingsActivity extends AppCompatActivity implements Setting
transaction.commit();
}
@Override
public void showDialogFragment(DialogFragment fragment)
{
fragment.show(getSupportFragmentManager(), FRAGMENT_DIALOG_TAG);
}
private boolean areSystemAnimationsEnabled()
{
float duration = Settings.Global.getFloat(
@ -304,6 +322,12 @@ public final class SettingsActivity extends AppCompatActivity implements Setting
mPresenter.onSettingChanged();
}
@Override
public void onControllerSettingsChanged()
{
getFragment().onControllerSettingsChanged();
}
@Override
public void onMenuTagAction(@NonNull MenuTag menuTag, int value)
{
@ -330,7 +354,27 @@ public final class SettingsActivity extends AppCompatActivity implements Setting
public void setToolbarTitle(String title)
{
mToolbarLayout.setTitle(title);
mBinding.toolbarSettingsLayout.setTitle(title);
}
@Override
public void setMappingAllDevices(boolean allDevices)
{
mMappingAllDevices = allDevices;
}
@Override
public boolean isMappingAllDevices()
{
return mMappingAllDevices;
}
@Override
public int setOldControllerSettingsWarningVisibility(boolean visible)
{
// We use INVISIBLE instead of GONE to avoid getting a stale height for the return value
mBinding.oldControllerSettingsWarning.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
return visible ? mBinding.oldControllerSettingsWarning.getHeight() : 0;
}
private void setInsets()
@ -343,6 +387,10 @@ public final class SettingsActivity extends AppCompatActivity implements Setting
mBinding.frameContentSettings.setPadding(insets.left, 0, insets.right, 0);
int textPadding = getResources().getDimensionPixelSize(R.dimen.spacing_large);
mBinding.oldControllerSettingsWarning.setPadding(textPadding + insets.left, textPadding,
textPadding + insets.right, textPadding + insets.bottom);
InsetsHelper.applyNavbarWorkaround(insets.bottom, mBinding.workaroundView);
ThemeHelper.setNavigationBarColor(this,
MaterialColors.getColor(mBinding.appbarSettings, R.attr.colorSurface));

View File

@ -5,6 +5,7 @@ package org.dolphinemu.dolphinemu.features.settings.ui;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.fragment.app.DialogFragment;
import org.dolphinemu.dolphinemu.features.settings.model.Settings;
@ -21,6 +22,13 @@ public interface SettingsActivityView
*/
void showSettingsFragment(MenuTag menuTag, Bundle extras, boolean addToStack, String gameId);
/**
* Shows a DialogFragment.
*
* Only one can be shown at a time.
*/
void showDialogFragment(DialogFragment fragment);
/**
* Called by a contained Fragment to get access to the Setting HashMap
* loaded from disk, so that each Fragment doesn't need to perform its own
@ -60,6 +68,14 @@ public interface SettingsActivityView
*/
void onSettingChanged();
/**
* Refetches the values of all controller settings.
*
* To be used when loading an input profile or performing some other action that changes all
* controller settings at once.
*/
void onControllerSettingsChanged();
/**
* Called by a containing Fragment to tell the containing Activity that the user wants to open the
* MenuTag associated with a setting.
@ -97,4 +113,25 @@ public interface SettingsActivityView
* Accesses the material toolbar layout and changes the title
*/
void setToolbarTitle(String title);
/**
* Sets whether the input mapping dialog should detect inputs from all devices,
* not just the device configured for the controller.
*/
void setMappingAllDevices(boolean allDevices);
/**
* Returns whether the input mapping dialog should detect inputs from all devices,
* not just the device configured for the controller.
*/
boolean isMappingAllDevices();
/**
* Shows or hides a warning telling the user that they're using incompatible controller settings.
* The warning is hidden by default.
*
* @param visible Whether the warning should be visible.
* @return The height of the warning view, or 0 if the view is now invisible.
*/
int setOldControllerSettingsWarningVisibility(boolean visible);
}

View File

@ -31,21 +31,24 @@ import com.google.android.material.timepicker.MaterialTimePicker;
import com.google.android.material.timepicker.TimeFormat;
import org.dolphinemu.dolphinemu.R;
import org.dolphinemu.dolphinemu.databinding.DialogAdvancedMappingBinding;
import org.dolphinemu.dolphinemu.databinding.DialogInputStringBinding;
import org.dolphinemu.dolphinemu.databinding.DialogSliderBinding;
import org.dolphinemu.dolphinemu.databinding.ListItemHeaderBinding;
import org.dolphinemu.dolphinemu.databinding.ListItemMappingBinding;
import org.dolphinemu.dolphinemu.databinding.ListItemSettingBinding;
import org.dolphinemu.dolphinemu.databinding.ListItemSettingSwitchBinding;
import org.dolphinemu.dolphinemu.databinding.ListItemSubmenuBinding;
import org.dolphinemu.dolphinemu.dialogs.MotionAlertDialog;
import org.dolphinemu.dolphinemu.features.input.ui.AdvancedMappingDialog;
import org.dolphinemu.dolphinemu.features.input.ui.MotionAlertDialog;
import org.dolphinemu.dolphinemu.features.input.model.view.InputMappingControlSetting;
import org.dolphinemu.dolphinemu.features.input.ui.viewholder.InputMappingControlSettingViewHolder;
import org.dolphinemu.dolphinemu.features.settings.model.Settings;
import org.dolphinemu.dolphinemu.features.settings.model.view.DateTimeChoiceSetting;
import org.dolphinemu.dolphinemu.features.settings.model.view.SwitchSetting;
import org.dolphinemu.dolphinemu.features.settings.model.view.FilePicker;
import org.dolphinemu.dolphinemu.features.settings.model.view.FloatSliderSetting;
import org.dolphinemu.dolphinemu.features.settings.model.view.InputBindingSetting;
import org.dolphinemu.dolphinemu.features.settings.model.view.IntSliderSetting;
import org.dolphinemu.dolphinemu.features.settings.model.view.RumbleBindingSetting;
import org.dolphinemu.dolphinemu.features.settings.model.view.SettingsItem;
import org.dolphinemu.dolphinemu.features.settings.model.view.SingleChoiceSetting;
import org.dolphinemu.dolphinemu.features.settings.model.view.SingleChoiceSettingDynamicDescriptions;
@ -57,9 +60,7 @@ import org.dolphinemu.dolphinemu.features.settings.ui.viewholder.DateTimeSetting
import org.dolphinemu.dolphinemu.features.settings.ui.viewholder.FilePickerViewHolder;
import org.dolphinemu.dolphinemu.features.settings.ui.viewholder.HeaderHyperLinkViewHolder;
import org.dolphinemu.dolphinemu.features.settings.ui.viewholder.HeaderViewHolder;
import org.dolphinemu.dolphinemu.features.settings.ui.viewholder.InputBindingSettingViewHolder;
import org.dolphinemu.dolphinemu.features.settings.ui.viewholder.InputStringSettingViewHolder;
import org.dolphinemu.dolphinemu.features.settings.ui.viewholder.RumbleBindingViewHolder;
import org.dolphinemu.dolphinemu.features.settings.ui.viewholder.RunRunnableViewHolder;
import org.dolphinemu.dolphinemu.features.settings.ui.viewholder.SettingViewHolder;
import org.dolphinemu.dolphinemu.features.settings.ui.viewholder.SingleChoiceViewHolder;
@ -124,13 +125,9 @@ public final class SettingsAdapter extends RecyclerView.Adapter<SettingViewHolde
case SettingsItem.TYPE_SUBMENU:
return new SubmenuViewHolder(ListItemSubmenuBinding.inflate(inflater), this);
case SettingsItem.TYPE_INPUT_BINDING:
return new InputBindingSettingViewHolder(ListItemSettingBinding.inflate(inflater), this,
mContext);
case SettingsItem.TYPE_RUMBLE_BINDING:
return new RumbleBindingViewHolder(ListItemSettingBinding.inflate(inflater), this,
mContext);
case SettingsItem.TYPE_INPUT_MAPPING_CONTROL:
return new InputMappingControlSettingViewHolder(ListItemMappingBinding.inflate(inflater),
this);
case SettingsItem.TYPE_FILE_PICKER:
return new FilePickerViewHolder(ListItemSettingBinding.inflate(inflater), this);
@ -259,11 +256,26 @@ public final class SettingsAdapter extends RecyclerView.Adapter<SettingViewHolde
mClickedItem = item;
mClickedPosition = position;
mDialog = new MaterialAlertDialogBuilder(mView.getActivity())
.setTitle(item.getName())
.setSingleChoiceItems(item.getChoices(), item.getSelectedValueIndex(getSettings()),
this)
.show();
item.refreshChoicesAndValues();
String[] choices = item.getChoices();
int noChoicesAvailableString = item.getNoChoicesAvailableString();
if (noChoicesAvailableString != 0 && choices.length == 0)
{
mDialog = new MaterialAlertDialogBuilder(mView.getActivity())
.setTitle(item.getName())
.setMessage(noChoicesAvailableString)
.setPositiveButton(R.string.ok, null)
.show();
}
else
{
mDialog = new MaterialAlertDialogBuilder(mView.getActivity())
.setTitle(item.getName())
.setSingleChoiceItems(item.getChoices(), item.getSelectedValueIndex(getSettings()),
this)
.show();
}
}
public void onSingleChoiceDynamicDescriptionsClick(SingleChoiceSettingDynamicDescriptions item,
@ -314,9 +326,19 @@ public final class SettingsAdapter extends RecyclerView.Adapter<SettingViewHolde
mView.loadSubMenu(item.getMenuKey());
}
public void onInputBindingClick(final InputBindingSetting item, final int position)
public void onInputMappingClick(final InputMappingControlSetting item, final int position)
{
final MotionAlertDialog dialog = new MotionAlertDialog(mContext, item, this);
if (item.getController().getDefaultDevice().isEmpty() && !mView.isMappingAllDevices())
{
new MaterialAlertDialogBuilder(mView.getActivity())
.setMessage(R.string.input_binding_no_device)
.setPositiveButton(R.string.ok, this)
.show();
return;
}
final MotionAlertDialog dialog = new MotionAlertDialog(mView.getActivity(), item,
mView.isMappingAllDevices());
Drawable background = ContextCompat.getDrawable(mContext, R.drawable.dialog_round);
@ColorInt int color = new ElevationOverlayProvider(dialog.getContext()).compositeOverlay(
@ -326,13 +348,11 @@ public final class SettingsAdapter extends RecyclerView.Adapter<SettingViewHolde
dialog.getWindow().setBackgroundDrawable(background);
dialog.setTitle(R.string.input_binding);
dialog.setMessage(String.format(mContext.getString(
item instanceof RumbleBindingSetting ?
R.string.input_rumble_description : R.string.input_binding_description),
dialog.setMessage(String.format(mContext.getString(R.string.input_binding_description),
item.getName()));
dialog.setButton(AlertDialog.BUTTON_NEGATIVE, mContext.getString(R.string.cancel), this);
dialog.setButton(AlertDialog.BUTTON_NEUTRAL, mContext.getString(R.string.clear),
(dialogInterface, i) -> item.clearValue(getSettings()));
(dialogInterface, i) -> item.clearValue());
dialog.setOnDismissListener(dialog1 ->
{
notifyItemChanged(position);
@ -342,6 +362,44 @@ public final class SettingsAdapter extends RecyclerView.Adapter<SettingViewHolde
dialog.show();
}
public void onAdvancedInputMappingClick(final InputMappingControlSetting item, final int position)
{
LayoutInflater inflater = LayoutInflater.from(mContext);
DialogAdvancedMappingBinding binding = DialogAdvancedMappingBinding.inflate(inflater);
final AdvancedMappingDialog dialog = new AdvancedMappingDialog(mContext, binding,
item.getControlReference(), item.getController());
Drawable background = ContextCompat.getDrawable(mContext, R.drawable.dialog_round);
@ColorInt int color = new ElevationOverlayProvider(dialog.getContext()).compositeOverlay(
MaterialColors.getColor(dialog.getWindow().getDecorView(), R.attr.colorSurface),
dialog.getWindow().getDecorView().getElevation());
background.setColorFilter(color, PorterDuff.Mode.SRC_ATOP);
dialog.getWindow().setBackgroundDrawable(background);
dialog.setTitle(item.isInput() ?
R.string.input_configure_input : R.string.input_configure_output);
dialog.setView(binding.getRoot());
dialog.setButton(AlertDialog.BUTTON_POSITIVE, mContext.getString(R.string.ok),
(dialogInterface, i) ->
{
item.setValue(dialog.getExpression());
notifyItemChanged(position);
mView.onSettingChanged();
});
dialog.setButton(AlertDialog.BUTTON_NEGATIVE, mContext.getString(R.string.cancel), this);
dialog.setButton(AlertDialog.BUTTON_NEUTRAL, mContext.getString(R.string.clear),
(dialogInterface, i) ->
{
item.clearValue();
notifyItemChanged(position);
mView.onSettingChanged();
});
dialog.setCanceledOnTouchOutside(false);
dialog.show();
}
public void onFilePickerDirectoryClick(SettingsItem item, int position)
{
mClickedItem = item;

View File

@ -13,6 +13,7 @@ import androidx.annotation.Nullable;
import androidx.core.graphics.Insets;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;
import androidx.fragment.app.DialogFragment;
import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
@ -36,6 +37,8 @@ public final class SettingsFragment extends Fragment implements SettingsFragment
private SettingsAdapter mAdapter;
private int mOldControllerSettingsWarningHeight = 0;
private static final Map<MenuTag, Integer> titles = new HashMap<>();
static
@ -65,14 +68,26 @@ public final class SettingsFragment extends Fragment implements SettingsFragment
titles.put(MenuTag.GCPAD_2, R.string.controller_1);
titles.put(MenuTag.GCPAD_3, R.string.controller_2);
titles.put(MenuTag.GCPAD_4, R.string.controller_3);
titles.put(MenuTag.WIIMOTE_1, R.string.wiimote_4);
titles.put(MenuTag.WIIMOTE_2, R.string.wiimote_5);
titles.put(MenuTag.WIIMOTE_3, R.string.wiimote_6);
titles.put(MenuTag.WIIMOTE_4, R.string.wiimote_7);
titles.put(MenuTag.WIIMOTE_EXTENSION_1, R.string.wiimote_extension_4);
titles.put(MenuTag.WIIMOTE_EXTENSION_2, R.string.wiimote_extension_5);
titles.put(MenuTag.WIIMOTE_EXTENSION_3, R.string.wiimote_extension_6);
titles.put(MenuTag.WIIMOTE_EXTENSION_4, R.string.wiimote_extension_7);
titles.put(MenuTag.WIIMOTE_1, R.string.wiimote_0);
titles.put(MenuTag.WIIMOTE_2, R.string.wiimote_1);
titles.put(MenuTag.WIIMOTE_3, R.string.wiimote_2);
titles.put(MenuTag.WIIMOTE_4, R.string.wiimote_3);
titles.put(MenuTag.WIIMOTE_EXTENSION_1, R.string.wiimote_extension_0);
titles.put(MenuTag.WIIMOTE_EXTENSION_2, R.string.wiimote_extension_1);
titles.put(MenuTag.WIIMOTE_EXTENSION_3, R.string.wiimote_extension_2);
titles.put(MenuTag.WIIMOTE_EXTENSION_4, R.string.wiimote_extension_3);
titles.put(MenuTag.WIIMOTE_GENERAL_1, R.string.wiimote_general);
titles.put(MenuTag.WIIMOTE_GENERAL_2, R.string.wiimote_general);
titles.put(MenuTag.WIIMOTE_GENERAL_3, R.string.wiimote_general);
titles.put(MenuTag.WIIMOTE_GENERAL_4, R.string.wiimote_general);
titles.put(MenuTag.WIIMOTE_MOTION_SIMULATION_1, R.string.wiimote_motion_simulation);
titles.put(MenuTag.WIIMOTE_MOTION_SIMULATION_2, R.string.wiimote_motion_simulation);
titles.put(MenuTag.WIIMOTE_MOTION_SIMULATION_3, R.string.wiimote_motion_simulation);
titles.put(MenuTag.WIIMOTE_MOTION_SIMULATION_4, R.string.wiimote_motion_simulation);
titles.put(MenuTag.WIIMOTE_MOTION_INPUT_1, R.string.wiimote_motion_input);
titles.put(MenuTag.WIIMOTE_MOTION_INPUT_2, R.string.wiimote_motion_input);
titles.put(MenuTag.WIIMOTE_MOTION_INPUT_3, R.string.wiimote_motion_input);
titles.put(MenuTag.WIIMOTE_MOTION_INPUT_4, R.string.wiimote_motion_input);
}
private FragmentSettingsBinding mBinding;
@ -203,6 +218,12 @@ public final class SettingsFragment extends Fragment implements SettingsFragment
mActivity.showSettingsFragment(menuKey, null, true, getArguments().getString(ARGUMENT_GAME_ID));
}
@Override
public void showDialogFragment(DialogFragment fragment)
{
mActivity.showDialogFragment(fragment);
}
@Override
public void showToastMessage(String message)
{
@ -221,6 +242,13 @@ public final class SettingsFragment extends Fragment implements SettingsFragment
mActivity.onSettingChanged();
}
@Override
public void onControllerSettingsChanged()
{
mAdapter.notifyAllSettingsChanged();
mPresenter.updateOldControllerSettingsWarningVisibility();
}
@Override
public void onMenuTagAction(@NonNull MenuTag menuTag, int value)
{
@ -232,13 +260,35 @@ public final class SettingsFragment extends Fragment implements SettingsFragment
return mActivity.hasMenuTagActionForValue(menuTag, value);
}
@Override
public void setMappingAllDevices(boolean allDevices)
{
mActivity.setMappingAllDevices(allDevices);
}
@Override
public boolean isMappingAllDevices()
{
return mActivity.isMappingAllDevices();
}
@Override
public void setOldControllerSettingsWarningVisibility(boolean visible)
{
mOldControllerSettingsWarningHeight =
mActivity.setOldControllerSettingsWarningVisibility(visible);
// Trigger the insets listener we've registered
mBinding.listSettings.requestApplyInsets();
}
private void setInsets()
{
ViewCompat.setOnApplyWindowInsetsListener(mBinding.listSettings, (v, windowInsets) ->
{
Insets insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars());
v.setPadding(0, 0, 0,
insets.bottom + getResources().getDimensionPixelSize(R.dimen.spacing_list));
int listSpacing = getResources().getDimensionPixelSize(R.dimen.spacing_list);
v.setPadding(0, 0, 0, insets.bottom + listSpacing + mOldControllerSettingsWarningHeight);
return windowInsets;
});
}

View File

@ -10,13 +10,24 @@ import android.text.TextUtils;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.collection.ArraySet;
import org.dolphinemu.dolphinemu.NativeLibrary;
import org.dolphinemu.dolphinemu.R;
import org.dolphinemu.dolphinemu.activities.UserDataActivity;
import org.dolphinemu.dolphinemu.features.input.model.InputMappingBooleanSetting;
import org.dolphinemu.dolphinemu.features.input.model.InputMappingDoubleSetting;
import org.dolphinemu.dolphinemu.features.input.model.InputMappingIntSetting;
import org.dolphinemu.dolphinemu.features.input.model.controlleremu.ControlGroup;
import org.dolphinemu.dolphinemu.features.input.model.ControlGroupEnabledSetting;
import org.dolphinemu.dolphinemu.features.input.model.controlleremu.EmulatedController;
import org.dolphinemu.dolphinemu.features.input.model.controlleremu.NumericSetting;
import org.dolphinemu.dolphinemu.features.input.model.view.InputDeviceSetting;
import org.dolphinemu.dolphinemu.features.input.model.view.InputMappingControlSetting;
import org.dolphinemu.dolphinemu.features.input.ui.ProfileDialog;
import org.dolphinemu.dolphinemu.features.input.ui.ProfileDialogPresenter;
import org.dolphinemu.dolphinemu.features.settings.model.AbstractBooleanSetting;
import org.dolphinemu.dolphinemu.features.settings.model.AbstractIntSetting;
import org.dolphinemu.dolphinemu.features.settings.model.AbstractStringSetting;
import org.dolphinemu.dolphinemu.features.settings.model.AdHocBooleanSetting;
import org.dolphinemu.dolphinemu.features.settings.model.BooleanSetting;
import org.dolphinemu.dolphinemu.features.settings.model.FloatSetting;
@ -26,26 +37,23 @@ import org.dolphinemu.dolphinemu.features.settings.model.PostProcessing;
import org.dolphinemu.dolphinemu.features.settings.model.ScaledIntSetting;
import org.dolphinemu.dolphinemu.features.settings.model.Settings;
import org.dolphinemu.dolphinemu.features.settings.model.StringSetting;
import org.dolphinemu.dolphinemu.features.settings.model.WiimoteProfileStringSetting;
import org.dolphinemu.dolphinemu.features.settings.model.view.DateTimeChoiceSetting;
import org.dolphinemu.dolphinemu.features.settings.model.view.SwitchSetting;
import org.dolphinemu.dolphinemu.features.settings.model.view.FilePicker;
import org.dolphinemu.dolphinemu.features.settings.model.view.FloatSliderSetting;
import org.dolphinemu.dolphinemu.features.settings.model.view.HeaderSetting;
import org.dolphinemu.dolphinemu.features.settings.model.view.HyperLinkHeaderSetting;
import org.dolphinemu.dolphinemu.features.settings.model.view.InputBindingSetting;
import org.dolphinemu.dolphinemu.features.settings.model.view.InputStringSetting;
import org.dolphinemu.dolphinemu.features.settings.model.view.IntSliderSetting;
import org.dolphinemu.dolphinemu.features.settings.model.view.InvertedSwitchSetting;
import org.dolphinemu.dolphinemu.features.settings.model.view.LogSwitchSetting;
import org.dolphinemu.dolphinemu.features.settings.model.view.PercentSliderSetting;
import org.dolphinemu.dolphinemu.features.settings.model.view.RumbleBindingSetting;
import org.dolphinemu.dolphinemu.features.settings.model.view.RunRunnable;
import org.dolphinemu.dolphinemu.features.settings.model.view.SettingsItem;
import org.dolphinemu.dolphinemu.features.settings.model.view.SingleChoiceSetting;
import org.dolphinemu.dolphinemu.features.settings.model.view.SingleChoiceSettingDynamicDescriptions;
import org.dolphinemu.dolphinemu.features.settings.model.view.StringSingleChoiceSetting;
import org.dolphinemu.dolphinemu.features.settings.model.view.SubmenuSetting;
import org.dolphinemu.dolphinemu.features.settings.utils.SettingsFile;
import org.dolphinemu.dolphinemu.ui.main.MainPresenter;
import org.dolphinemu.dolphinemu.utils.BooleanSupplier;
import org.dolphinemu.dolphinemu.utils.EGLHelper;
@ -54,8 +62,11 @@ import org.dolphinemu.dolphinemu.utils.ThreadUtil;
import org.dolphinemu.dolphinemu.utils.WiiUtils;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
public final class SettingsFragmentPresenter
{
@ -72,6 +83,7 @@ public final class SettingsFragmentPresenter
private Settings mSettings;
private ArrayList<SettingsItem> mSettingsList;
private boolean mHasOldControllerSettings = false;
private int mSerialPort1Type;
private int mControllerNumber;
@ -136,6 +148,7 @@ public final class SettingsFragmentPresenter
else
{
mView.showSettingsList(mSettingsList);
mView.setOldControllerSettingsWarningVisibility(mHasOldControllerSettings);
}
}
@ -246,6 +259,27 @@ public final class SettingsFragmentPresenter
addExtensionTypeSettings(sl, mControllerNumber, mControllerType);
break;
case WIIMOTE_GENERAL_1:
case WIIMOTE_GENERAL_2:
case WIIMOTE_GENERAL_3:
case WIIMOTE_GENERAL_4:
addWiimoteGeneralSubSettings(sl, mControllerNumber);
break;
case WIIMOTE_MOTION_SIMULATION_1:
case WIIMOTE_MOTION_SIMULATION_2:
case WIIMOTE_MOTION_SIMULATION_3:
case WIIMOTE_MOTION_SIMULATION_4:
addWiimoteMotionSimulationSubSettings(sl, mControllerNumber);
break;
case WIIMOTE_MOTION_INPUT_1:
case WIIMOTE_MOTION_INPUT_2:
case WIIMOTE_MOTION_INPUT_3:
case WIIMOTE_MOTION_INPUT_4:
addWiimoteMotionInputSubSettings(sl, mControllerNumber);
break;
default:
throw new UnsupportedOperationException("Unimplemented menu");
}
@ -259,13 +293,10 @@ public final class SettingsFragmentPresenter
sl.add(new SubmenuSetting(mContext, R.string.config, MenuTag.CONFIG));
sl.add(new SubmenuSetting(mContext, R.string.graphics_settings, MenuTag.GRAPHICS));
if (!NativeLibrary.IsRunning())
sl.add(new SubmenuSetting(mContext, R.string.gcpad_settings, MenuTag.GCPAD_TYPE));
if (mSettings.isWii())
{
sl.add(new SubmenuSetting(mContext, R.string.gcpad_settings, MenuTag.GCPAD_TYPE));
if (mSettings.isWii())
{
sl.add(new SubmenuSetting(mContext, R.string.wiimote_settings, MenuTag.WIIMOTE));
}
sl.add(new SubmenuSetting(mContext, R.string.wiimote_settings, MenuTag.WIIMOTE));
}
sl.add(new HeaderSetting(mContext, R.string.setting_clear_info, 0));
@ -801,14 +832,14 @@ public final class SettingsFragmentPresenter
private void addWiimoteSettings(ArrayList<SettingsItem> sl)
{
sl.add(new SingleChoiceSetting(mContext, IntSetting.WIIMOTE_1_SOURCE, R.string.wiimote_4, 0,
R.array.wiimoteTypeEntries, R.array.wiimoteTypeValues, MenuTag.getWiimoteMenuTag(4)));
sl.add(new SingleChoiceSetting(mContext, IntSetting.WIIMOTE_2_SOURCE, R.string.wiimote_5, 0,
R.array.wiimoteTypeEntries, R.array.wiimoteTypeValues, MenuTag.getWiimoteMenuTag(5)));
sl.add(new SingleChoiceSetting(mContext, IntSetting.WIIMOTE_3_SOURCE, R.string.wiimote_6, 0,
R.array.wiimoteTypeEntries, R.array.wiimoteTypeValues, MenuTag.getWiimoteMenuTag(6)));
sl.add(new SingleChoiceSetting(mContext, IntSetting.WIIMOTE_4_SOURCE, R.string.wiimote_7, 0,
R.array.wiimoteTypeEntries, R.array.wiimoteTypeValues, MenuTag.getWiimoteMenuTag(7)));
sl.add(new SingleChoiceSetting(mContext, IntSetting.WIIMOTE_1_SOURCE, R.string.wiimote_0, 0,
R.array.wiimoteTypeEntries, R.array.wiimoteTypeValues, MenuTag.getWiimoteMenuTag(0)));
sl.add(new SingleChoiceSetting(mContext, IntSetting.WIIMOTE_2_SOURCE, R.string.wiimote_1, 0,
R.array.wiimoteTypeEntries, R.array.wiimoteTypeValues, MenuTag.getWiimoteMenuTag(1)));
sl.add(new SingleChoiceSetting(mContext, IntSetting.WIIMOTE_3_SOURCE, R.string.wiimote_2, 0,
R.array.wiimoteTypeEntries, R.array.wiimoteTypeValues, MenuTag.getWiimoteMenuTag(2)));
sl.add(new SingleChoiceSetting(mContext, IntSetting.WIIMOTE_4_SOURCE, R.string.wiimote_3, 0,
R.array.wiimoteTypeEntries, R.array.wiimoteTypeValues, MenuTag.getWiimoteMenuTag(3)));
}
private void addGraphicsSettings(ArrayList<SettingsItem> sl)
@ -1079,62 +1110,17 @@ public final class SettingsFragmentPresenter
{
if (gcPadType == 6) // Emulated
{
sl.add(new HeaderSetting(mContext, R.string.generic_buttons, 0));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_GCBIND_A + gcPadNumber, R.string.button_a, mGameID));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_GCBIND_B + gcPadNumber, R.string.button_b, mGameID));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_GCBIND_X + gcPadNumber, R.string.button_x, mGameID));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_GCBIND_Y + gcPadNumber, R.string.button_y, mGameID));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_GCBIND_Z + gcPadNumber, R.string.button_z, mGameID));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_GCBIND_START + gcPadNumber, R.string.button_start, mGameID));
EmulatedController gcPad = EmulatedController.getGcPad(gcPadNumber);
sl.add(new HeaderSetting(mContext, R.string.controller_control, 0));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_GCBIND_CONTROL_UP + gcPadNumber, R.string.generic_up, mGameID));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_GCBIND_CONTROL_DOWN + gcPadNumber, R.string.generic_down, mGameID));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_GCBIND_CONTROL_LEFT + gcPadNumber, R.string.generic_left, mGameID));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_GCBIND_CONTROL_RIGHT + gcPadNumber, R.string.generic_right,
mGameID));
sl.add(new HeaderSetting(mContext, R.string.controller_c, 0));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_GCBIND_C_UP + gcPadNumber, R.string.generic_up, mGameID));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_GCBIND_C_DOWN + gcPadNumber, R.string.generic_down, mGameID));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_GCBIND_C_LEFT + gcPadNumber, R.string.generic_left, mGameID));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_GCBIND_C_RIGHT + gcPadNumber, R.string.generic_right, mGameID));
sl.add(new HeaderSetting(mContext, R.string.controller_trig, 0));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_GCBIND_TRIGGER_L + gcPadNumber, R.string.trigger_left, mGameID));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_GCBIND_TRIGGER_R + gcPadNumber, R.string.trigger_right, mGameID));
sl.add(new HeaderSetting(mContext, R.string.controller_dpad, 0));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_GCBIND_DPAD_UP + gcPadNumber, R.string.generic_up, mGameID));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_GCBIND_DPAD_DOWN + gcPadNumber, R.string.generic_down, mGameID));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_GCBIND_DPAD_LEFT + gcPadNumber, R.string.generic_left, mGameID));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_GCBIND_DPAD_RIGHT + gcPadNumber, R.string.generic_right, mGameID));
sl.add(new HeaderSetting(mContext, R.string.emulation_control_rumble, 0));
sl.add(new RumbleBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_EMU_RUMBLE + gcPadNumber, R.string.emulation_control_rumble,
mGameID));
if (!TextUtils.isEmpty(mGameID))
{
addControllerPerGameSettings(sl, "Pad", gcPadNumber);
}
else
{
addControllerMetaSettings(sl, gcPad);
addControllerMappingSettings(sl, gcPad, null);
}
}
else if (gcPadType == 12) // Adapter
{
@ -1147,440 +1133,227 @@ public final class SettingsFragmentPresenter
private void addWiimoteSubSettings(ArrayList<SettingsItem> sl, int wiimoteNumber)
{
// Bindings use controller numbers 4-7 (0-3 are GameCube), but the extension setting uses 1-4.
// But game specific extension settings are saved in their own profile. These profiles
// do not have any way to specify the controller that is loaded outside of knowing the filename
// of the profile that was loaded.
AbstractStringSetting extension;
final String defaultExtension = "None";
if (mGameID.isEmpty())
EmulatedController wiimote = EmulatedController.getWiimote(wiimoteNumber);
if (!TextUtils.isEmpty(mGameID))
{
extension = new LegacyStringSetting(Settings.FILE_WIIMOTE,
Settings.SECTION_WIIMOTE + (wiimoteNumber - 3), SettingsFile.KEY_WIIMOTE_EXTENSION,
defaultExtension);
addControllerPerGameSettings(sl, "Wiimote", wiimoteNumber);
}
else
{
extension = new WiimoteProfileStringSetting(mGameID, wiimoteNumber - 4,
Settings.SECTION_PROFILE, SettingsFile.KEY_WIIMOTE_EXTENSION, defaultExtension);
addControllerMetaSettings(sl, wiimote);
sl.add(new HeaderSetting(mContext, R.string.wiimote, 0));
sl.add(new SubmenuSetting(mContext, R.string.wiimote_general,
MenuTag.getWiimoteGeneralMenuTag(wiimoteNumber)));
sl.add(new SubmenuSetting(mContext, R.string.wiimote_motion_simulation,
MenuTag.getWiimoteMotionSimulationMenuTag(wiimoteNumber)));
sl.add(new SubmenuSetting(mContext, R.string.wiimote_motion_input,
MenuTag.getWiimoteMotionInputMenuTag(wiimoteNumber)));
// TYPE_OTHER is included here instead of in addWiimoteGeneralSubSettings so that touchscreen
// users won't have to dig into a submenu to find the Sideways Wii Remote setting
addControllerMappingSettings(sl, wiimote, new ArraySet<>(
Arrays.asList(ControlGroup.TYPE_ATTACHMENTS, ControlGroup.TYPE_OTHER)));
}
sl.add(new StringSingleChoiceSetting(mContext, extension, R.string.wiimote_extensions, 0,
R.array.wiimoteExtensionsEntries, R.array.wiimoteExtensionsValues,
MenuTag.getWiimoteExtensionMenuTag(wiimoteNumber)));
sl.add(new HeaderSetting(mContext, R.string.generic_buttons, 0));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_WIIBIND_A + wiimoteNumber, R.string.button_a, mGameID));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_WIIBIND_B + wiimoteNumber, R.string.button_b, mGameID));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_WIIBIND_1 + wiimoteNumber, R.string.button_one, mGameID));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_WIIBIND_2 + wiimoteNumber, R.string.button_two, mGameID));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_WIIBIND_MINUS + wiimoteNumber, R.string.button_minus, mGameID));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_WIIBIND_PLUS + wiimoteNumber, R.string.button_plus, mGameID));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_WIIBIND_HOME + wiimoteNumber, R.string.button_home, mGameID));
sl.add(new HeaderSetting(mContext, R.string.wiimote_ir, 0));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_WIIBIND_IR_UP + wiimoteNumber, R.string.generic_up, mGameID));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_WIIBIND_IR_DOWN + wiimoteNumber, R.string.generic_down, mGameID));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_WIIBIND_IR_LEFT + wiimoteNumber, R.string.generic_left, mGameID));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_WIIBIND_IR_RIGHT + wiimoteNumber, R.string.generic_right, mGameID));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_WIIBIND_IR_FORWARD + wiimoteNumber, R.string.generic_forward,
mGameID));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_WIIBIND_IR_BACKWARD + wiimoteNumber, R.string.generic_backward,
mGameID));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_WIIBIND_IR_HIDE + wiimoteNumber, R.string.ir_hide, mGameID));
sl.add(new HeaderSetting(mContext, R.string.wiimote_swing, 0));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_WIIBIND_SWING_UP + wiimoteNumber, R.string.generic_up, mGameID));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_WIIBIND_SWING_DOWN + wiimoteNumber, R.string.generic_down, mGameID));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_WIIBIND_SWING_LEFT + wiimoteNumber, R.string.generic_left, mGameID));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_WIIBIND_SWING_RIGHT + wiimoteNumber, R.string.generic_right, mGameID));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_WIIBIND_SWING_FORWARD + wiimoteNumber, R.string.generic_forward,
mGameID));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_WIIBIND_SWING_BACKWARD + wiimoteNumber, R.string.generic_backward,
mGameID));
sl.add(new HeaderSetting(mContext, R.string.wiimote_tilt, 0));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_WIIBIND_TILT_FORWARD + wiimoteNumber, R.string.generic_forward,
mGameID));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_WIIBIND_TILT_BACKWARD + wiimoteNumber, R.string.generic_backward,
mGameID));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_WIIBIND_TILT_LEFT + wiimoteNumber, R.string.generic_left, mGameID));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_WIIBIND_TILT_RIGHT + wiimoteNumber, R.string.generic_right, mGameID));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_WIIBIND_TILT_MODIFIER + wiimoteNumber, R.string.tilt_modifier,
mGameID));
sl.add(new HeaderSetting(mContext, R.string.wiimote_shake, 0));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_WIIBIND_SHAKE_X + wiimoteNumber, R.string.shake_x, mGameID));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_WIIBIND_SHAKE_Y + wiimoteNumber, R.string.shake_y, mGameID));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_WIIBIND_SHAKE_Z + wiimoteNumber, R.string.shake_z, mGameID));
sl.add(new HeaderSetting(mContext, R.string.controller_dpad, 0));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_WIIBIND_DPAD_UP + wiimoteNumber, R.string.generic_up, mGameID));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_WIIBIND_DPAD_DOWN + wiimoteNumber, R.string.generic_down, mGameID));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_WIIBIND_DPAD_LEFT + wiimoteNumber, R.string.generic_left, mGameID));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_WIIBIND_DPAD_RIGHT + wiimoteNumber, R.string.generic_right, mGameID));
sl.add(new HeaderSetting(mContext, R.string.emulation_control_rumble, 0));
sl.add(new RumbleBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_EMU_RUMBLE + wiimoteNumber, R.string.emulation_control_rumble,
mGameID));
}
private void addExtensionTypeSettings(ArrayList<SettingsItem> sl, int wiimoteNumber,
int extentionType)
int extensionType)
{
switch (extentionType)
addControllerMappingSettings(sl,
EmulatedController.getWiimoteAttachment(wiimoteNumber, extensionType), null);
}
private void addWiimoteGeneralSubSettings(ArrayList<SettingsItem> sl, int wiimoteNumber)
{
addControllerMappingSettings(sl, EmulatedController.getWiimote(wiimoteNumber),
Collections.singleton(ControlGroup.TYPE_BUTTONS));
}
private void addWiimoteMotionSimulationSubSettings(ArrayList<SettingsItem> sl, int wiimoteNumber)
{
addControllerMappingSettings(sl, EmulatedController.getWiimote(wiimoteNumber),
new ArraySet<>(Arrays.asList(ControlGroup.TYPE_FORCE, ControlGroup.TYPE_TILT,
ControlGroup.TYPE_CURSOR, ControlGroup.TYPE_SHAKE)));
}
private void addWiimoteMotionInputSubSettings(ArrayList<SettingsItem> sl, int wiimoteNumber)
{
addControllerMappingSettings(sl, EmulatedController.getWiimote(wiimoteNumber),
new ArraySet<>(Arrays.asList(ControlGroup.TYPE_IMU_ACCELEROMETER,
ControlGroup.TYPE_IMU_GYROSCOPE, ControlGroup.TYPE_IMU_CURSOR)));
}
/**
* Adds controller settings that can be set on a per-game basis.
*
* @param sl The list to place controller settings into.
* @param profileString The prefix used for the profile setting in game INI files.
* @param controllerNumber The index of the controller, 0-3.
*/
private void addControllerPerGameSettings(ArrayList<SettingsItem> sl, String profileString,
int controllerNumber)
{
String[] profiles = new ProfileDialogPresenter(mMenuTag).getProfileNames(false);
String profileKey = profileString + "Profile" + controllerNumber;
sl.add(new StringSingleChoiceSetting(mContext,
new LegacyStringSetting("", "Controls", profileKey, ""),
R.string.input_profile, 0, profiles, profiles, R.string.input_profiles_empty));
}
/**
* Adds settings and actions that apply to a controller as a whole.
* For instance, the device setting and the Clear action.
*
* @param sl The list to place controller settings into.
* @param controller The controller to add settings for.
*/
private void addControllerMetaSettings(ArrayList<SettingsItem> sl, EmulatedController controller)
{
sl.add(new InputDeviceSetting(mContext, R.string.input_device, 0, controller));
sl.add(new SwitchSetting(mContext, new AbstractBooleanSetting()
{
case 1: // Nunchuk
sl.add(new HeaderSetting(mContext, R.string.generic_buttons, 0));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_WIIBIND_NUNCHUK_C + wiimoteNumber, R.string.nunchuk_button_c,
mGameID));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_WIIBIND_NUNCHUK_Z + wiimoteNumber, R.string.button_z, mGameID));
@Override
public boolean isOverridden(Settings settings)
{
return false;
}
sl.add(new HeaderSetting(mContext, R.string.generic_stick, 0));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_WIIBIND_NUNCHUK_UP + wiimoteNumber, R.string.generic_up, mGameID));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_WIIBIND_NUNCHUK_DOWN + wiimoteNumber, R.string.generic_down,
mGameID));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_WIIBIND_NUNCHUK_LEFT + wiimoteNumber, R.string.generic_left,
mGameID));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_WIIBIND_NUNCHUK_RIGHT + wiimoteNumber, R.string.generic_right,
mGameID));
@Override
public boolean isRuntimeEditable()
{
return true;
}
sl.add(new HeaderSetting(mContext, R.string.wiimote_swing, 0));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_WIIBIND_NUNCHUK_SWING_UP + wiimoteNumber, R.string.generic_up,
mGameID));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_WIIBIND_NUNCHUK_SWING_DOWN + wiimoteNumber, R.string.generic_down,
mGameID));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_WIIBIND_NUNCHUK_SWING_LEFT + wiimoteNumber, R.string.generic_left,
mGameID));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_WIIBIND_NUNCHUK_SWING_RIGHT + wiimoteNumber,
R.string.generic_right, mGameID));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_WIIBIND_NUNCHUK_SWING_FORWARD + wiimoteNumber,
R.string.generic_forward, mGameID));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_WIIBIND_NUNCHUK_SWING_BACKWARD + wiimoteNumber,
R.string.generic_backward, mGameID));
@Override
public boolean delete(Settings settings)
{
mView.setMappingAllDevices(false);
return true;
}
sl.add(new HeaderSetting(mContext, R.string.wiimote_tilt, 0));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_WIIBIND_NUNCHUK_TILT_FORWARD + wiimoteNumber,
R.string.generic_forward, mGameID));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_WIIBIND_NUNCHUK_TILT_BACKWARD + wiimoteNumber,
R.string.generic_backward, mGameID));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_WIIBIND_NUNCHUK_TILT_LEFT + wiimoteNumber, R.string.generic_left,
mGameID));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_WIIBIND_NUNCHUK_TILT_RIGHT + wiimoteNumber, R.string.generic_right,
mGameID));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_WIIBIND_NUNCHUK_TILT_MODIFIER + wiimoteNumber,
R.string.tilt_modifier, mGameID));
@Override
public boolean getBoolean(Settings settings)
{
return mView.isMappingAllDevices();
}
sl.add(new HeaderSetting(mContext, R.string.wiimote_shake, 0));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_WIIBIND_NUNCHUK_SHAKE_X + wiimoteNumber, R.string.shake_x,
mGameID));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_WIIBIND_NUNCHUK_SHAKE_Y + wiimoteNumber, R.string.shake_y,
mGameID));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_WIIBIND_NUNCHUK_SHAKE_Z + wiimoteNumber, R.string.shake_z,
mGameID));
@Override
public void setBoolean(Settings settings, boolean newValue)
{
mView.setMappingAllDevices(newValue);
}
}, R.string.input_device_all_devices, R.string.input_device_all_devices_description));
sl.add(new RunRunnable(mContext, R.string.input_reset_to_default,
R.string.input_reset_to_default_description, R.string.input_reset_warning, 0, true,
() -> loadDefaultControllerSettings(controller)));
sl.add(new RunRunnable(mContext, R.string.input_clear, R.string.input_clear_description,
R.string.input_reset_warning, 0, true, () -> clearControllerSettings(controller)));
sl.add(new RunRunnable(mContext, R.string.input_profiles, 0, 0, 0, true,
() -> mView.showDialogFragment(ProfileDialog.create(mMenuTag))));
updateOldControllerSettingsWarningVisibility(controller);
}
/**
* Adds mapping settings and other control-specific settings.
*
* @param sl The list to place controller settings into.
* @param controller The controller to add settings for.
* @param groupTypeFilter If this is non-null, only groups whose types match this are considered.
*/
private void addControllerMappingSettings(ArrayList<SettingsItem> sl,
EmulatedController controller, Set<Integer> groupTypeFilter)
{
updateOldControllerSettingsWarningVisibility(controller);
int groupCount = controller.getGroupCount();
for (int i = 0; i < groupCount; i++)
{
ControlGroup group = controller.getGroup(i);
int groupType = group.getGroupType();
if (groupTypeFilter != null && !groupTypeFilter.contains(groupType))
continue;
sl.add(new HeaderSetting(group.getUiName(), ""));
if (group.getDefaultEnabledValue() != ControlGroup.DEFAULT_ENABLED_ALWAYS)
{
sl.add(new SwitchSetting(mContext, new ControlGroupEnabledSetting(group), R.string.enabled,
0));
}
int controlCount = group.getControlCount();
for (int j = 0; j < controlCount; j++)
{
sl.add(new InputMappingControlSetting(group.getControl(j), controller));
}
if (groupType == ControlGroup.TYPE_ATTACHMENTS)
{
NumericSetting attachmentSetting = group.getAttachmentSetting();
sl.add(new SingleChoiceSetting(mContext, new InputMappingIntSetting(attachmentSetting),
R.string.wiimote_extensions, 0, R.array.wiimoteExtensionsEntries,
R.array.wiimoteExtensionsValues,
MenuTag.getWiimoteExtensionMenuTag(mControllerNumber)));
}
int numericSettingCount = group.getNumericSettingCount();
for (int j = 0; j < numericSettingCount; j++)
{
addNumericSetting(sl, group.getNumericSetting(j));
}
}
}
private void addNumericSetting(ArrayList<SettingsItem> sl, NumericSetting setting)
{
switch (setting.getType())
{
case NumericSetting.TYPE_DOUBLE:
sl.add(new FloatSliderSetting(new InputMappingDoubleSetting(setting), setting.getUiName(),
"", (int) Math.ceil(setting.getDoubleMin()),
(int) Math.floor(setting.getDoubleMax()), setting.getUiSuffix()));
break;
case 2: // Classic
sl.add(new HeaderSetting(mContext, R.string.generic_buttons, 0));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_WIIBIND_CLASSIC_A + wiimoteNumber, R.string.button_a, mGameID));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_WIIBIND_CLASSIC_B + wiimoteNumber, R.string.button_b, mGameID));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_WIIBIND_CLASSIC_X + wiimoteNumber, R.string.button_x, mGameID));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_WIIBIND_CLASSIC_Y + wiimoteNumber, R.string.button_y, mGameID));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_WIIBIND_CLASSIC_ZL + wiimoteNumber, R.string.classic_button_zl,
mGameID));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_WIIBIND_CLASSIC_ZR + wiimoteNumber, R.string.classic_button_zr,
mGameID));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_WIIBIND_CLASSIC_MINUS + wiimoteNumber, R.string.button_minus,
mGameID));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_WIIBIND_CLASSIC_PLUS + wiimoteNumber, R.string.button_plus,
mGameID));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_WIIBIND_CLASSIC_HOME + wiimoteNumber, R.string.button_home,
mGameID));
sl.add(new HeaderSetting(mContext, R.string.classic_leftstick, 0));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_WIIBIND_CLASSIC_LEFT_UP + wiimoteNumber, R.string.generic_up,
mGameID));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_WIIBIND_CLASSIC_LEFT_DOWN + wiimoteNumber, R.string.generic_down,
mGameID));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_WIIBIND_CLASSIC_LEFT_LEFT + wiimoteNumber, R.string.generic_left,
mGameID));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_WIIBIND_CLASSIC_LEFT_RIGHT + wiimoteNumber, R.string.generic_right,
mGameID));
sl.add(new HeaderSetting(mContext, R.string.classic_rightstick, 0));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_WIIBIND_CLASSIC_RIGHT_UP + wiimoteNumber, R.string.generic_up,
mGameID));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_WIIBIND_CLASSIC_RIGHT_DOWN + wiimoteNumber, R.string.generic_down,
mGameID));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_WIIBIND_CLASSIC_RIGHT_LEFT + wiimoteNumber, R.string.generic_left,
mGameID));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_WIIBIND_CLASSIC_RIGHT_RIGHT + wiimoteNumber,
R.string.generic_right, mGameID));
sl.add(new HeaderSetting(mContext, R.string.controller_trig, 0));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_WIIBIND_CLASSIC_TRIGGER_L + wiimoteNumber, R.string.trigger_left,
mGameID));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_WIIBIND_CLASSIC_TRIGGER_R + wiimoteNumber, R.string.trigger_right,
mGameID));
sl.add(new HeaderSetting(mContext, R.string.controller_dpad, 0));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_WIIBIND_CLASSIC_DPAD_UP + wiimoteNumber, R.string.generic_up,
mGameID));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_WIIBIND_CLASSIC_DPAD_DOWN + wiimoteNumber, R.string.generic_down,
mGameID));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_WIIBIND_CLASSIC_DPAD_LEFT + wiimoteNumber, R.string.generic_left,
mGameID));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_WIIBIND_CLASSIC_DPAD_RIGHT + wiimoteNumber, R.string.generic_right,
mGameID));
break;
case 3: // Guitar
sl.add(new HeaderSetting(mContext, R.string.guitar_frets, 0));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_WIIBIND_GUITAR_FRET_GREEN + wiimoteNumber, R.string.generic_green,
mGameID));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_WIIBIND_GUITAR_FRET_RED + wiimoteNumber, R.string.generic_red,
mGameID));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_WIIBIND_GUITAR_FRET_YELLOW + wiimoteNumber,
R.string.generic_yellow, mGameID));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_WIIBIND_GUITAR_FRET_BLUE + wiimoteNumber, R.string.generic_blue,
mGameID));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_WIIBIND_GUITAR_FRET_ORANGE + wiimoteNumber,
R.string.generic_orange, mGameID));
sl.add(new HeaderSetting(mContext, R.string.guitar_strum, 0));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_WIIBIND_GUITAR_STRUM_UP + wiimoteNumber, R.string.generic_up,
mGameID));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_WIIBIND_GUITAR_STRUM_DOWN + wiimoteNumber, R.string.generic_down,
mGameID));
sl.add(new HeaderSetting(mContext, R.string.generic_buttons, 0));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_WIIBIND_GUITAR_MINUS + wiimoteNumber, R.string.button_minus,
mGameID));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_WIIBIND_GUITAR_PLUS + wiimoteNumber, R.string.button_plus,
mGameID));
sl.add(new HeaderSetting(mContext, R.string.generic_stick, 0));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_WIIBIND_GUITAR_STICK_UP + wiimoteNumber, R.string.generic_up,
mGameID));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_WIIBIND_GUITAR_STICK_DOWN + wiimoteNumber, R.string.generic_down,
mGameID));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_WIIBIND_GUITAR_STICK_LEFT + wiimoteNumber, R.string.generic_left,
mGameID));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_WIIBIND_GUITAR_STICK_RIGHT + wiimoteNumber, R.string.generic_right,
mGameID));
sl.add(new HeaderSetting(mContext, R.string.guitar_whammy, 0));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_WIIBIND_GUITAR_WHAMMY_BAR + wiimoteNumber, R.string.generic_right,
mGameID));
break;
case 4: // Drums
sl.add(new HeaderSetting(mContext, R.string.drums_pads, 0));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_WIIBIND_DRUMS_PAD_RED + wiimoteNumber, R.string.generic_red,
mGameID));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_WIIBIND_DRUMS_PAD_YELLOW + wiimoteNumber, R.string.generic_yellow,
mGameID));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_WIIBIND_DRUMS_PAD_BLUE + wiimoteNumber, R.string.generic_blue,
mGameID));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_WIIBIND_DRUMS_PAD_GREEN + wiimoteNumber, R.string.generic_green,
mGameID));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_WIIBIND_DRUMS_PAD_ORANGE + wiimoteNumber, R.string.generic_orange,
mGameID));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_WIIBIND_DRUMS_PAD_BASS + wiimoteNumber, R.string.drums_pad_bass,
mGameID));
sl.add(new HeaderSetting(mContext, R.string.generic_stick, 0));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_WIIBIND_DRUMS_STICK_UP + wiimoteNumber, R.string.generic_up,
mGameID));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_WIIBIND_DRUMS_STICK_DOWN + wiimoteNumber, R.string.generic_down,
mGameID));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_WIIBIND_DRUMS_STICK_LEFT + wiimoteNumber, R.string.generic_left,
mGameID));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_WIIBIND_DRUMS_STICK_RIGHT + wiimoteNumber, R.string.generic_right,
mGameID));
sl.add(new HeaderSetting(mContext, R.string.generic_buttons, 0));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_WIIBIND_DRUMS_MINUS + wiimoteNumber, R.string.button_minus,
mGameID));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_WIIBIND_DRUMS_PLUS + wiimoteNumber, R.string.button_plus,
mGameID));
break;
case 5: // Turntable
sl.add(new HeaderSetting(mContext, R.string.generic_buttons, 0));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_WIIBIND_TURNTABLE_GREEN_LEFT + wiimoteNumber,
R.string.turntable_button_green_left, mGameID));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_WIIBIND_TURNTABLE_RED_LEFT + wiimoteNumber,
R.string.turntable_button_red_left, mGameID));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_WIIBIND_TURNTABLE_BLUE_LEFT + wiimoteNumber,
R.string.turntable_button_blue_left, mGameID));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_WIIBIND_TURNTABLE_GREEN_RIGHT + wiimoteNumber,
R.string.turntable_button_green_right, mGameID));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_WIIBIND_TURNTABLE_RED_RIGHT + wiimoteNumber,
R.string.turntable_button_red_right, mGameID));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_WIIBIND_TURNTABLE_BLUE_RIGHT + wiimoteNumber,
R.string.turntable_button_blue_right, mGameID));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_WIIBIND_TURNTABLE_MINUS + wiimoteNumber,
R.string.button_minus, mGameID));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_WIIBIND_TURNTABLE_PLUS + wiimoteNumber,
R.string.button_plus, mGameID));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_WIIBIND_TURNTABLE_EUPHORIA + wiimoteNumber,
R.string.turntable_button_euphoria, mGameID));
sl.add(new HeaderSetting(mContext, R.string.turntable_table_left, 0));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_WIIBIND_TURNTABLE_LEFT_LEFT + wiimoteNumber, R.string.generic_left,
mGameID));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_WIIBIND_TURNTABLE_LEFT_RIGHT + wiimoteNumber,
R.string.generic_right, mGameID));
sl.add(new HeaderSetting(mContext, R.string.turntable_table_right, 0));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_WIIBIND_TURNTABLE_RIGHT_LEFT + wiimoteNumber,
R.string.generic_left, mGameID));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_WIIBIND_TURNTABLE_RIGHT_RIGHT + wiimoteNumber,
R.string.generic_right, mGameID));
sl.add(new HeaderSetting(mContext, R.string.generic_stick, 0));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_WIIBIND_TURNTABLE_STICK_UP + wiimoteNumber, R.string.generic_up,
mGameID));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_WIIBIND_TURNTABLE_STICK_DOWN + wiimoteNumber,
R.string.generic_down, mGameID));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_WIIBIND_TURNTABLE_STICK_LEFT + wiimoteNumber,
R.string.generic_left, mGameID));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_WIIBIND_TURNTABLE_STICK_RIGHT + wiimoteNumber,
R.string.generic_right, mGameID));
sl.add(new HeaderSetting(mContext, R.string.turntable_effect, 0));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_WIIBIND_TURNTABLE_EFFECT_DIAL + wiimoteNumber,
R.string.turntable_effect_dial, mGameID));
sl.add(new HeaderSetting(mContext, R.string.turntable_crossfade, 0));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_WIIBIND_TURNTABLE_CROSSFADE_LEFT + wiimoteNumber,
R.string.generic_left, mGameID));
sl.add(new InputBindingSetting(mContext, Settings.FILE_DOLPHIN, Settings.SECTION_BINDINGS,
SettingsFile.KEY_WIIBIND_TURNTABLE_CROSSFADE_RIGHT + wiimoteNumber,
R.string.generic_right, mGameID));
case NumericSetting.TYPE_BOOLEAN:
sl.add(new SwitchSetting(new InputMappingBooleanSetting(setting), setting.getUiName(),
setting.getUiDescription()));
break;
}
}
public void updateOldControllerSettingsWarningVisibility()
{
updateOldControllerSettingsWarningVisibility(mMenuTag.getCorrespondingEmulatedController());
}
private void updateOldControllerSettingsWarningVisibility(EmulatedController controller)
{
String defaultDevice = controller.getDefaultDevice();
mHasOldControllerSettings = defaultDevice.startsWith("Android/") &&
defaultDevice.endsWith("/Touchscreen");
mView.setOldControllerSettingsWarningVisibility(mHasOldControllerSettings);
}
private void loadDefaultControllerSettings(EmulatedController controller)
{
controller.loadDefaultSettings();
mView.onControllerSettingsChanged();
}
private void clearControllerSettings(EmulatedController controller)
{
controller.clearSettings();
mView.onControllerSettingsChanged();
}
private static int getLogVerbosityEntries()
{
// Value obtained from LogLevel in Common/Logging/Log.h

View File

@ -3,6 +3,7 @@
package org.dolphinemu.dolphinemu.features.settings.ui;
import androidx.annotation.NonNull;
import androidx.fragment.app.DialogFragment;
import androidx.fragment.app.FragmentActivity;
import org.dolphinemu.dolphinemu.features.settings.model.Settings;
@ -55,6 +56,8 @@ public interface SettingsFragmentView
*/
void loadSubMenu(MenuTag menuKey);
void showDialogFragment(DialogFragment fragment);
/**
* Tell the Fragment to tell the containing activity to display a toast message.
*
@ -72,6 +75,14 @@ public interface SettingsFragmentView
*/
void onSettingChanged();
/**
* Refetches the values of all controller settings.
*
* To be used when loading an input profile or performing some other action that changes all
* controller settings at once.
*/
void onControllerSettingsChanged();
/**
* Have the fragment tell the containing Activity that the user wants to open the MenuTag
* associated with a setting.
@ -89,4 +100,24 @@ public interface SettingsFragmentView
* @param value The current value of the setting.
*/
boolean hasMenuTagActionForValue(@NonNull MenuTag menuTag, int value);
/**
* Sets whether the input mapping dialog should detect inputs from all devices,
* not just the device configured for the controller.
*/
void setMappingAllDevices(boolean allDevices);
/**
* Returns whether the input mapping dialog should detect inputs from all devices,
* not just the device configured for the controller.
*/
boolean isMappingAllDevices();
/**
* Shows or hides a warning telling the user that they're using incompatible controller settings.
* The warning is hidden by default.
*
* @param visible Whether the warning should be visible.
*/
void setOldControllerSettingsWarningVisibility(boolean visible);
}

View File

@ -1,67 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-or-later
package org.dolphinemu.dolphinemu.features.settings.ui.viewholder;
import android.content.Context;
import android.content.SharedPreferences;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.preference.PreferenceManager;
import org.dolphinemu.dolphinemu.databinding.ListItemSettingBinding;
import org.dolphinemu.dolphinemu.features.settings.model.view.InputBindingSetting;
import org.dolphinemu.dolphinemu.features.settings.model.view.SettingsItem;
import org.dolphinemu.dolphinemu.features.settings.ui.SettingsAdapter;
public final class InputBindingSettingViewHolder extends SettingViewHolder
{
private InputBindingSetting mItem;
private final Context mContext;
private final ListItemSettingBinding mBinding;
public InputBindingSettingViewHolder(@NonNull ListItemSettingBinding binding,
SettingsAdapter adapter, Context context)
{
super(binding.getRoot(), adapter);
mBinding = binding;
mContext = context;
}
@Override
public void bind(SettingsItem item)
{
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(mContext);
mItem = (InputBindingSetting) item;
mBinding.textSettingName.setText(mItem.getName());
mBinding.textSettingDescription
.setText(sharedPreferences.getString(mItem.getKey() + mItem.getGameId(), ""));
setStyle(mBinding.textSettingName, mItem);
}
@Override
public void onClick(View clicked)
{
if (!mItem.isEditable())
{
showNotRuntimeEditableError();
return;
}
getAdapter().onInputBindingClick(mItem, getBindingAdapterPosition());
setStyle(mBinding.textSettingName, mItem);
}
@Nullable @Override
protected SettingsItem getItem()
{
return mItem;
}
}

View File

@ -1,67 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-or-later
package org.dolphinemu.dolphinemu.features.settings.ui.viewholder;
import android.content.Context;
import android.content.SharedPreferences;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.preference.PreferenceManager;
import org.dolphinemu.dolphinemu.databinding.ListItemSettingBinding;
import org.dolphinemu.dolphinemu.features.settings.model.view.RumbleBindingSetting;
import org.dolphinemu.dolphinemu.features.settings.model.view.SettingsItem;
import org.dolphinemu.dolphinemu.features.settings.ui.SettingsAdapter;
public class RumbleBindingViewHolder extends SettingViewHolder
{
private RumbleBindingSetting mItem;
private final Context mContext;
private final ListItemSettingBinding mBinding;
public RumbleBindingViewHolder(@NonNull ListItemSettingBinding binding, SettingsAdapter adapter,
Context context)
{
super(binding.getRoot(), adapter);
mBinding = binding;
mContext = context;
}
@Override
public void bind(SettingsItem item)
{
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(mContext);
mItem = (RumbleBindingSetting) item;
mBinding.textSettingName.setText(item.getName());
mBinding.textSettingDescription
.setText(sharedPreferences.getString(mItem.getKey() + mItem.getGameId(), ""));
setStyle(mBinding.textSettingName, mItem);
}
@Override
public void onClick(View clicked)
{
if (!mItem.isEditable())
{
showNotRuntimeEditableError();
return;
}
getAdapter().onInputBindingClick(mItem, getBindingAdapterPosition());
setStyle(mBinding.textSettingName, mItem);
}
@Nullable @Override
protected SettingsItem getItem()
{
return mItem;
}
}

View File

@ -83,7 +83,7 @@ public abstract class SettingViewHolder extends RecyclerView.ViewHolder
{
SettingsItem item = getItem();
if (item == null || !item.hasSetting())
if (item == null || !item.canClear())
return false;
if (!item.isEditable())

View File

@ -64,10 +64,8 @@ public final class SingleChoiceViewHolder extends SettingViewHolder
else if (item instanceof StringSingleChoiceSetting)
{
StringSingleChoiceSetting setting = (StringSingleChoiceSetting) item;
String[] choices = setting.getChoices();
int valueIndex = setting.getSelectedValueIndex(settings);
if (valueIndex != -1)
mBinding.textSettingDescription.setText(choices[valueIndex]);
String choice = setting.getSelectedChoice(settings);
mBinding.textSettingDescription.setText(choice);
}
else if (item instanceof SingleChoiceSettingDynamicDescriptions)
{

View File

@ -20,161 +20,6 @@ public final class SettingsFile
public static final String KEY_ISO_PATH_BASE = "ISOPath";
public static final String KEY_ISO_PATHS = "ISOPaths";
public static final String KEY_GCPAD_PLAYER_1 = "SIDevice0";
public static final String KEY_GCBIND_A = "InputA_";
public static final String KEY_GCBIND_B = "InputB_";
public static final String KEY_GCBIND_X = "InputX_";
public static final String KEY_GCBIND_Y = "InputY_";
public static final String KEY_GCBIND_Z = "InputZ_";
public static final String KEY_GCBIND_START = "InputStart_";
public static final String KEY_GCBIND_CONTROL_UP = "MainUp_";
public static final String KEY_GCBIND_CONTROL_DOWN = "MainDown_";
public static final String KEY_GCBIND_CONTROL_LEFT = "MainLeft_";
public static final String KEY_GCBIND_CONTROL_RIGHT = "MainRight_";
public static final String KEY_GCBIND_C_UP = "CStickUp_";
public static final String KEY_GCBIND_C_DOWN = "CStickDown_";
public static final String KEY_GCBIND_C_LEFT = "CStickLeft_";
public static final String KEY_GCBIND_C_RIGHT = "CStickRight_";
public static final String KEY_GCBIND_TRIGGER_L = "InputL_";
public static final String KEY_GCBIND_TRIGGER_R = "InputR_";
public static final String KEY_GCBIND_DPAD_UP = "DPadUp_";
public static final String KEY_GCBIND_DPAD_DOWN = "DPadDown_";
public static final String KEY_GCBIND_DPAD_LEFT = "DPadLeft_";
public static final String KEY_GCBIND_DPAD_RIGHT = "DPadRight_";
public static final String KEY_EMU_RUMBLE = "EmuRumble";
public static final String KEY_WIIMOTE_EXTENSION = "Extension";
// Controller keys for game specific settings
public static final String KEY_WIIMOTE_PROFILE = "WiimoteProfile";
public static final String KEY_WIIBIND_A = "WiimoteA_";
public static final String KEY_WIIBIND_B = "WiimoteB_";
public static final String KEY_WIIBIND_1 = "Wiimote1_";
public static final String KEY_WIIBIND_2 = "Wiimote2_";
public static final String KEY_WIIBIND_MINUS = "WiimoteMinus_";
public static final String KEY_WIIBIND_PLUS = "WiimotePlus_";
public static final String KEY_WIIBIND_HOME = "WiimoteHome_";
public static final String KEY_WIIBIND_IR_UP = "IRUp_";
public static final String KEY_WIIBIND_IR_DOWN = "IRDown_";
public static final String KEY_WIIBIND_IR_LEFT = "IRLeft_";
public static final String KEY_WIIBIND_IR_RIGHT = "IRRight_";
public static final String KEY_WIIBIND_IR_FORWARD = "IRForward_";
public static final String KEY_WIIBIND_IR_BACKWARD = "IRBackward_";
public static final String KEY_WIIBIND_IR_HIDE = "IRHide_";
public static final String KEY_WIIBIND_IR_PITCH = "IRTotalPitch";
public static final String KEY_WIIBIND_IR_YAW = "IRTotalYaw";
public static final String KEY_WIIBIND_IR_VERTICAL_OFFSET = "IRVerticalOffset";
public static final String KEY_WIIBIND_SWING_UP = "SwingUp_";
public static final String KEY_WIIBIND_SWING_DOWN = "SwingDown_";
public static final String KEY_WIIBIND_SWING_LEFT = "SwingLeft_";
public static final String KEY_WIIBIND_SWING_RIGHT = "SwingRight_";
public static final String KEY_WIIBIND_SWING_FORWARD = "SwingForward_";
public static final String KEY_WIIBIND_SWING_BACKWARD = "SwingBackward_";
public static final String KEY_WIIBIND_TILT_FORWARD = "TiltForward_";
public static final String KEY_WIIBIND_TILT_BACKWARD = "TiltBackward_";
public static final String KEY_WIIBIND_TILT_LEFT = "TiltLeft_";
public static final String KEY_WIIBIND_TILT_RIGHT = "TiltRight_";
public static final String KEY_WIIBIND_TILT_MODIFIER = "TiltModifier_";
public static final String KEY_WIIBIND_SHAKE_X = "ShakeX_";
public static final String KEY_WIIBIND_SHAKE_Y = "ShakeY_";
public static final String KEY_WIIBIND_SHAKE_Z = "ShakeZ_";
public static final String KEY_WIIBIND_DPAD_UP = "WiimoteUp_";
public static final String KEY_WIIBIND_DPAD_DOWN = "WiimoteDown_";
public static final String KEY_WIIBIND_DPAD_LEFT = "WiimoteLeft_";
public static final String KEY_WIIBIND_DPAD_RIGHT = "WiimoteRight_";
public static final String KEY_WIIBIND_NUNCHUK_C = "NunchukC_";
public static final String KEY_WIIBIND_NUNCHUK_Z = "NunchukZ_";
public static final String KEY_WIIBIND_NUNCHUK_UP = "NunchukUp_";
public static final String KEY_WIIBIND_NUNCHUK_DOWN = "NunchukDown_";
public static final String KEY_WIIBIND_NUNCHUK_LEFT = "NunchukLeft_";
public static final String KEY_WIIBIND_NUNCHUK_RIGHT = "NunchukRight_";
public static final String KEY_WIIBIND_NUNCHUK_SWING_UP = "NunchukSwingUp_";
public static final String KEY_WIIBIND_NUNCHUK_SWING_DOWN = "NunchukSwingDown_";
public static final String KEY_WIIBIND_NUNCHUK_SWING_LEFT = "NunchukSwingLeft_";
public static final String KEY_WIIBIND_NUNCHUK_SWING_RIGHT = "NunchukSwingRight_";
public static final String KEY_WIIBIND_NUNCHUK_SWING_FORWARD = "NunchukSwingForward_";
public static final String KEY_WIIBIND_NUNCHUK_SWING_BACKWARD = "NunchukSwingBackward_";
public static final String KEY_WIIBIND_NUNCHUK_TILT_FORWARD = "NunchukTiltForward_";
public static final String KEY_WIIBIND_NUNCHUK_TILT_BACKWARD = "NunchukTiltBackward_";
public static final String KEY_WIIBIND_NUNCHUK_TILT_LEFT = "NunchukTiltLeft_";
public static final String KEY_WIIBIND_NUNCHUK_TILT_RIGHT = "NunchukTiltRight_";
public static final String KEY_WIIBIND_NUNCHUK_TILT_MODIFIER = "NunchukTiltModifier_";
public static final String KEY_WIIBIND_NUNCHUK_SHAKE_X = "NunchukShakeX_";
public static final String KEY_WIIBIND_NUNCHUK_SHAKE_Y = "NunchukShakeY_";
public static final String KEY_WIIBIND_NUNCHUK_SHAKE_Z = "NunchukShakeZ_";
public static final String KEY_WIIBIND_CLASSIC_A = "ClassicA_";
public static final String KEY_WIIBIND_CLASSIC_B = "ClassicB_";
public static final String KEY_WIIBIND_CLASSIC_X = "ClassicX_";
public static final String KEY_WIIBIND_CLASSIC_Y = "ClassicY_";
public static final String KEY_WIIBIND_CLASSIC_ZL = "ClassicZL_";
public static final String KEY_WIIBIND_CLASSIC_ZR = "ClassicZR_";
public static final String KEY_WIIBIND_CLASSIC_MINUS = "ClassicMinus_";
public static final String KEY_WIIBIND_CLASSIC_PLUS = "ClassicPlus_";
public static final String KEY_WIIBIND_CLASSIC_HOME = "ClassicHome_";
public static final String KEY_WIIBIND_CLASSIC_LEFT_UP = "ClassicLeftStickUp_";
public static final String KEY_WIIBIND_CLASSIC_LEFT_DOWN = "ClassicLeftStickDown_";
public static final String KEY_WIIBIND_CLASSIC_LEFT_LEFT = "ClassicLeftStickLeft_";
public static final String KEY_WIIBIND_CLASSIC_LEFT_RIGHT = "ClassicLeftStickRight_";
public static final String KEY_WIIBIND_CLASSIC_RIGHT_UP = "ClassicRightStickUp_";
public static final String KEY_WIIBIND_CLASSIC_RIGHT_DOWN = "ClassicRightStickDown_";
public static final String KEY_WIIBIND_CLASSIC_RIGHT_LEFT = "ClassicRightStickLeft_";
public static final String KEY_WIIBIND_CLASSIC_RIGHT_RIGHT = "ClassicRightStickRight_";
public static final String KEY_WIIBIND_CLASSIC_TRIGGER_L = "ClassicTriggerL_";
public static final String KEY_WIIBIND_CLASSIC_TRIGGER_R = "ClassicTriggerR_";
public static final String KEY_WIIBIND_CLASSIC_DPAD_UP = "ClassicUp_";
public static final String KEY_WIIBIND_CLASSIC_DPAD_DOWN = "ClassicDown_";
public static final String KEY_WIIBIND_CLASSIC_DPAD_LEFT = "ClassicLeft_";
public static final String KEY_WIIBIND_CLASSIC_DPAD_RIGHT = "ClassicRight_";
public static final String KEY_WIIBIND_GUITAR_FRET_GREEN = "GuitarGreen_";
public static final String KEY_WIIBIND_GUITAR_FRET_RED = "GuitarRed_";
public static final String KEY_WIIBIND_GUITAR_FRET_YELLOW = "GuitarYellow_";
public static final String KEY_WIIBIND_GUITAR_FRET_BLUE = "GuitarBlue_";
public static final String KEY_WIIBIND_GUITAR_FRET_ORANGE = "GuitarOrange_";
public static final String KEY_WIIBIND_GUITAR_STRUM_UP = "GuitarStrumUp_";
public static final String KEY_WIIBIND_GUITAR_STRUM_DOWN = "GuitarStrumDown_";
public static final String KEY_WIIBIND_GUITAR_MINUS = "GuitarMinus_";
public static final String KEY_WIIBIND_GUITAR_PLUS = "GuitarPlus_";
public static final String KEY_WIIBIND_GUITAR_STICK_UP = "GuitarUp_";
public static final String KEY_WIIBIND_GUITAR_STICK_DOWN = "GuitarDown_";
public static final String KEY_WIIBIND_GUITAR_STICK_LEFT = "GuitarLeft_";
public static final String KEY_WIIBIND_GUITAR_STICK_RIGHT = "GuitarRight_";
public static final String KEY_WIIBIND_GUITAR_WHAMMY_BAR = "GuitarWhammy_";
public static final String KEY_WIIBIND_DRUMS_PAD_RED = "DrumsRed_";
public static final String KEY_WIIBIND_DRUMS_PAD_YELLOW = "DrumsYellow_";
public static final String KEY_WIIBIND_DRUMS_PAD_BLUE = "DrumsBlue_";
public static final String KEY_WIIBIND_DRUMS_PAD_GREEN = "DrumsGreen_";
public static final String KEY_WIIBIND_DRUMS_PAD_ORANGE = "DrumsOrange_";
public static final String KEY_WIIBIND_DRUMS_PAD_BASS = "DrumsBass_";
public static final String KEY_WIIBIND_DRUMS_MINUS = "DrumsMinus_";
public static final String KEY_WIIBIND_DRUMS_PLUS = "DrumsPlus_";
public static final String KEY_WIIBIND_DRUMS_STICK_UP = "DrumsUp_";
public static final String KEY_WIIBIND_DRUMS_STICK_DOWN = "DrumsDown_";
public static final String KEY_WIIBIND_DRUMS_STICK_LEFT = "DrumsLeft_";
public static final String KEY_WIIBIND_DRUMS_STICK_RIGHT = "DrumsRight_";
public static final String KEY_WIIBIND_TURNTABLE_GREEN_LEFT = "TurntableGreenLeft_";
public static final String KEY_WIIBIND_TURNTABLE_RED_LEFT = "TurntableRedLeft_";
public static final String KEY_WIIBIND_TURNTABLE_BLUE_LEFT = "TurntableBlueLeft_";
public static final String KEY_WIIBIND_TURNTABLE_GREEN_RIGHT = "TurntableGreenRight_";
public static final String KEY_WIIBIND_TURNTABLE_RED_RIGHT = "TurntableRedRight_";
public static final String KEY_WIIBIND_TURNTABLE_BLUE_RIGHT = "TurntableBlueRight_";
public static final String KEY_WIIBIND_TURNTABLE_MINUS = "TurntableMinus_";
public static final String KEY_WIIBIND_TURNTABLE_PLUS = "TurntablePlus_";
public static final String KEY_WIIBIND_TURNTABLE_EUPHORIA = "TurntableEuphoria_";
public static final String KEY_WIIBIND_TURNTABLE_LEFT_LEFT = "TurntableLeftLeft_";
public static final String KEY_WIIBIND_TURNTABLE_LEFT_RIGHT = "TurntableLeftRight_";
public static final String KEY_WIIBIND_TURNTABLE_RIGHT_LEFT = "TurntableRightLeft_";
public static final String KEY_WIIBIND_TURNTABLE_RIGHT_RIGHT = "TurntableRightRight_";
public static final String KEY_WIIBIND_TURNTABLE_STICK_UP = "TurntableUp_";
public static final String KEY_WIIBIND_TURNTABLE_STICK_DOWN = "TurntableDown_";
public static final String KEY_WIIBIND_TURNTABLE_STICK_LEFT = "TurntableLeft_";
public static final String KEY_WIIBIND_TURNTABLE_STICK_RIGHT = "TurntableRight_";
public static final String KEY_WIIBIND_TURNTABLE_EFFECT_DIAL = "TurntableEffDial_";
public static final String KEY_WIIBIND_TURNTABLE_CROSSFADE_LEFT = "TurntableCrossLeft_";
public static final String KEY_WIIBIND_TURNTABLE_CROSSFADE_RIGHT = "TurntableCrossRight_";
private static BiMap<String, String> sectionsMap = new BiMap<>();
static
@ -283,13 +128,4 @@ public final class SettingsFile
return new File(
DirectoryInitialization.getUserDirectory() + "/GameSettings/" + gameId + ".ini");
}
public static File getWiiProfile(String profile)
{
String wiiConfigPath =
DirectoryInitialization.getUserDirectory() + "/Config/Profiles/Wiimote/" +
profile + ".ini";
return new File(wiiConfigPath);
}
}

View File

@ -115,7 +115,7 @@ public final class EmulationFragment extends Fragment implements SurfaceHolder.C
{
int overlayX = mInputOverlay.getLeft();
int overlayY = mInputOverlay.getTop();
mInputOverlay.setSurfacePosition(new Rect(
mInputOverlay.setSurfacePosition(activity.getSettings(), new Rect(
surfaceView.getLeft() - overlayX, surfaceView.getTop() - overlayY,
surfaceView.getRight() - overlayX, surfaceView.getBottom() - overlayY));
});
@ -133,6 +133,10 @@ public final class EmulationFragment extends Fragment implements SurfaceHolder.C
public void onResume()
{
super.onResume();
if (mInputOverlay != null && NativeLibrary.IsGameMetadataValid())
mInputOverlay.refreshControls(activity.getSettings());
run(activity.isActivityRecreated());
}
@ -170,19 +174,19 @@ public final class EmulationFragment extends Fragment implements SurfaceHolder.C
.setBoolean(settings, !BooleanSetting.MAIN_SHOW_INPUT_OVERLAY.getBoolean(settings));
if (mInputOverlay != null)
mInputOverlay.refreshControls();
mInputOverlay.refreshControls(settings);
}
public void initInputPointer()
public void initInputPointer(Settings settings)
{
if (mInputOverlay != null)
mInputOverlay.initTouchPointer();
mInputOverlay.initTouchPointer(settings);
}
public void refreshInputOverlay()
public void refreshInputOverlay(Settings settings)
{
if (mInputOverlay != null)
mInputOverlay.refreshControls();
mInputOverlay.refreshControls(settings);
}
public void refreshOverlayPointer(Settings settings)
@ -191,10 +195,10 @@ public final class EmulationFragment extends Fragment implements SurfaceHolder.C
mInputOverlay.refreshOverlayPointer(settings);
}
public void resetInputOverlay()
public void resetInputOverlay(Settings settings)
{
if (mInputOverlay != null)
mInputOverlay.resetButtonPlacement();
mInputOverlay.resetButtonPlacement(settings);
}
@Override

View File

@ -29,15 +29,17 @@ import androidx.preference.PreferenceManager;
import org.dolphinemu.dolphinemu.NativeLibrary;
import org.dolphinemu.dolphinemu.NativeLibrary.ButtonType;
import org.dolphinemu.dolphinemu.R;
import org.dolphinemu.dolphinemu.features.input.model.InputMappingBooleanSetting;
import org.dolphinemu.dolphinemu.features.input.model.InputOverrider;
import org.dolphinemu.dolphinemu.features.input.model.InputOverrider.ControlId;
import org.dolphinemu.dolphinemu.features.input.model.controlleremu.EmulatedController;
import org.dolphinemu.dolphinemu.features.input.model.controlleremu.NumericSetting;
import org.dolphinemu.dolphinemu.features.settings.model.BooleanSetting;
import org.dolphinemu.dolphinemu.features.settings.model.IntSetting;
import org.dolphinemu.dolphinemu.features.settings.model.Settings;
import org.dolphinemu.dolphinemu.features.settings.utils.SettingsFile;
import org.dolphinemu.dolphinemu.utils.IniFile;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
@ -66,9 +68,11 @@ public final class InputOverlay extends SurfaceView implements OnTouchListener
private Rect mSurfacePosition = null;
private boolean mIsFirstRun = true;
private boolean mGameCubeRegistered = false;
private boolean mWiiRegistered = false;
private boolean[] mGcPadRegistered = new boolean[4];
private boolean[] mWiimoteRegistered = new boolean[4];
private boolean mIsInEditMode = false;
private int mControllerType = -1;
private int mControllerIndex = 0;
private InputOverlayDrawableButton mButtonBeingConfigured;
private InputOverlayDrawableDpad mDpadBeingConfigured;
private InputOverlayDrawableJoystick mJoystickBeingConfigured;
@ -131,10 +135,6 @@ public final class InputOverlay extends SurfaceView implements OnTouchListener
if (!mPreferences.getBoolean("OverlayInitV3", false))
defaultOverlay();
// Load the controls if we can. If not, EmulationActivity has to do it later.
if (NativeLibrary.IsGameMetadataValid())
refreshControls();
// Set the on touch listener.
setOnTouchListener(this);
@ -145,13 +145,13 @@ public final class InputOverlay extends SurfaceView implements OnTouchListener
requestFocus();
}
public void setSurfacePosition(Rect rect)
public void setSurfacePosition(Settings settings, Rect rect)
{
mSurfacePosition = rect;
initTouchPointer();
initTouchPointer(settings);
}
public void initTouchPointer()
public void initTouchPointer(Settings settings)
{
// Check if we have all the data we need yet
boolean aspectRatioAvailable = NativeLibrary.IsRunningAndStarted();
@ -164,7 +164,7 @@ public final class InputOverlay extends SurfaceView implements OnTouchListener
int doubleTapButton = IntSetting.MAIN_DOUBLE_TAP_BUTTON.getIntGlobal();
if (getConfiguredControllerType() != InputOverlay.OVERLAY_WIIMOTE_CLASSIC &&
if (getConfiguredControllerType(settings) != InputOverlay.OVERLAY_WIIMOTE_CLASSIC &&
doubleTapButton == ButtonType.CLASSIC_BUTTON_A)
{
doubleTapButton = ButtonType.WIIMOTE_BUTTON_A;
@ -186,7 +186,7 @@ public final class InputOverlay extends SurfaceView implements OnTouchListener
overlayPointer = new InputOverlayPointer(mSurfacePosition, doubleTapControl,
IntSetting.MAIN_IR_MODE.getIntGlobal(),
BooleanSetting.MAIN_IR_ALWAYS_RECENTER.getBooleanGlobal());
BooleanSetting.MAIN_IR_ALWAYS_RECENTER.getBooleanGlobal(), mControllerIndex);
}
@Override
@ -239,11 +239,11 @@ public final class InputOverlay extends SurfaceView implements OnTouchListener
button.setPressedState(true);
button.setTrackId(event.getPointerId(pointerIndex));
pressed = true;
InputOverrider.setControlState(0, button.getControl(), 1.0);
InputOverrider.setControlState(mControllerIndex, button.getControl(), 1.0);
int analogControl = getAnalogControlForTrigger(button.getControl());
if (analogControl >= 0)
InputOverrider.setControlState(0, analogControl, 1.0);
InputOverrider.setControlState(mControllerIndex, analogControl, 1.0);
}
break;
case MotionEvent.ACTION_UP:
@ -252,11 +252,11 @@ public final class InputOverlay extends SurfaceView implements OnTouchListener
if (button.getTrackId() == event.getPointerId(pointerIndex))
{
button.setPressedState(false);
InputOverrider.setControlState(0, button.getControl(), 0.0);
InputOverrider.setControlState(mControllerIndex, button.getControl(), 0.0);
int analogControl = getAnalogControlForTrigger(button.getControl());
if (analogControl >= 0)
InputOverrider.setControlState(0, analogControl, 0.0);
InputOverrider.setControlState(mControllerIndex, analogControl, 0.0);
button.setTrackId(-1);
}
@ -298,7 +298,7 @@ public final class InputOverlay extends SurfaceView implements OnTouchListener
{
if (!dpadPressed[i])
{
InputOverrider.setControlState(0, dpad.getControl(i), 0.0);
InputOverrider.setControlState(mControllerIndex, dpad.getControl(i), 0.0);
}
}
// Press buttons
@ -306,7 +306,7 @@ public final class InputOverlay extends SurfaceView implements OnTouchListener
{
if (dpadPressed[i])
{
InputOverrider.setControlState(0, dpad.getControl(i), 1.0);
InputOverrider.setControlState(mControllerIndex, dpad.getControl(i), 1.0);
}
}
setDpadState(dpad, dpadPressed[0], dpadPressed[1], dpadPressed[2], dpadPressed[3]);
@ -320,7 +320,7 @@ public final class InputOverlay extends SurfaceView implements OnTouchListener
for (int i = 0; i < 4; i++)
{
dpad.setState(InputOverlayDrawableDpad.STATE_DEFAULT);
InputOverrider.setControlState(0, dpad.getControl(i), 0.0);
InputOverrider.setControlState(mControllerIndex, dpad.getControl(i), 0.0);
}
dpad.setTrackId(-1);
}
@ -336,16 +336,18 @@ public final class InputOverlay extends SurfaceView implements OnTouchListener
pressed = true;
}
InputOverrider.setControlState(0, joystick.getXControl(), joystick.getX());
InputOverrider.setControlState(0, joystick.getYControl(), -joystick.getY());
InputOverrider.setControlState(mControllerIndex, joystick.getXControl(), joystick.getX());
InputOverrider.setControlState(mControllerIndex, joystick.getYControl(), -joystick.getY());
}
// No button/joystick pressed, safe to move pointer
if (!pressed && overlayPointer != null)
{
overlayPointer.onTouch(event);
InputOverrider.setControlState(0, ControlId.WIIMOTE_IR_X, overlayPointer.getX());
InputOverrider.setControlState(0, ControlId.WIIMOTE_IR_Y, -overlayPointer.getY());
InputOverrider.setControlState(mControllerIndex, ControlId.WIIMOTE_IR_X,
overlayPointer.getX());
InputOverrider.setControlState(mControllerIndex, ControlId.WIIMOTE_IR_Y,
-overlayPointer.getY());
}
invalidate();
@ -359,7 +361,6 @@ public final class InputOverlay extends SurfaceView implements OnTouchListener
int fingerPositionX = (int) event.getX(pointerIndex);
int fingerPositionY = (int) event.getY(pointerIndex);
int controller = getConfiguredControllerType();
String orientation =
getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT ?
"-Portrait" : "";
@ -398,7 +399,7 @@ public final class InputOverlay extends SurfaceView implements OnTouchListener
// Persist button position by saving new place.
saveControlPosition(mButtonBeingConfigured.getLegacyId(),
mButtonBeingConfigured.getBounds().left,
mButtonBeingConfigured.getBounds().top, controller, orientation);
mButtonBeingConfigured.getBounds().top, orientation);
mButtonBeingConfigured = null;
}
break;
@ -436,7 +437,7 @@ public final class InputOverlay extends SurfaceView implements OnTouchListener
// Persist button position by saving new place.
saveControlPosition(mDpadBeingConfigured.getLegacyId(),
mDpadBeingConfigured.getBounds().left, mDpadBeingConfigured.getBounds().top,
controller, orientation);
orientation);
mDpadBeingConfigured = null;
}
break;
@ -469,7 +470,7 @@ public final class InputOverlay extends SurfaceView implements OnTouchListener
{
saveControlPosition(mJoystickBeingConfigured.getLegacyId(),
mJoystickBeingConfigured.getBounds().left,
mJoystickBeingConfigured.getBounds().top, controller, orientation);
mJoystickBeingConfigured.getBounds().top, orientation);
mJoystickBeingConfigured = null;
}
break;
@ -486,14 +487,20 @@ public final class InputOverlay extends SurfaceView implements OnTouchListener
private void unregisterControllers()
{
if (mGameCubeRegistered)
InputOverrider.unregisterGameCube(0);
for (int i = 0; i < mGcPadRegistered.length; i++)
{
if (mGcPadRegistered[i])
InputOverrider.unregisterGameCube(i);
}
if (mWiiRegistered)
InputOverrider.unregisterWii(0);
for (int i = 0; i < mWiimoteRegistered.length; i++)
{
if (mWiimoteRegistered[i])
InputOverrider.unregisterWii(i);
}
mGameCubeRegistered = false;
mWiiRegistered = false;
Arrays.fill(mGcPadRegistered, false);
Arrays.fill(mWiimoteRegistered, false);
}
private int getAnalogControlForTrigger(int control)
@ -785,7 +792,7 @@ public final class InputOverlay extends SurfaceView implements OnTouchListener
}
}
public void refreshControls()
public void refreshControls(Settings settings)
{
unregisterControllers();
@ -798,67 +805,60 @@ public final class InputOverlay extends SurfaceView implements OnTouchListener
getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT ?
"-Portrait" : "";
mControllerType = getConfiguredControllerType(settings);
IntSetting controllerSetting = NativeLibrary.IsEmulatingWii() ?
IntSetting.MAIN_OVERLAY_WII_CONTROLLER : IntSetting.MAIN_OVERLAY_GC_CONTROLLER;
int controllerIndex = controllerSetting.getInt(settings);
if (BooleanSetting.MAIN_SHOW_INPUT_OVERLAY.getBooleanGlobal())
{
// Add all the enabled overlay items back to the HashSet.
if (!NativeLibrary.IsEmulatingWii())
switch (mControllerType)
{
IniFile dolphinIni = new IniFile(SettingsFile.getSettingsFile(Settings.FILE_DOLPHIN));
case OVERLAY_GAMECUBE:
if (IntSetting.getSettingForSIDevice(controllerIndex).getInt(settings) ==
DISABLED_GAMECUBE_CONTROLLER && mIsFirstRun)
{
Toast.makeText(getContext(), R.string.disabled_gc_overlay_notice, Toast.LENGTH_SHORT)
.show();
}
switch (dolphinIni.getInt(Settings.SECTION_INI_CORE, SettingsFile.KEY_GCPAD_PLAYER_1,
EMULATED_GAMECUBE_CONTROLLER))
{
case DISABLED_GAMECUBE_CONTROLLER:
if (mIsFirstRun)
{
Toast.makeText(getContext(), R.string.disabled_gc_overlay_notice, Toast.LENGTH_SHORT)
.show();
}
break;
mControllerIndex = controllerIndex;
InputOverrider.registerGameCube(mControllerIndex);
mGcPadRegistered[mControllerIndex] = true;
case EMULATED_GAMECUBE_CONTROLLER:
InputOverrider.registerGameCube(0);
mGameCubeRegistered = true;
addGameCubeOverlayControls(orientation);
break;
addGameCubeOverlayControls(orientation);
break;
case GAMECUBE_ADAPTER:
break;
}
}
else
{
switch (getConfiguredControllerType())
{
case OVERLAY_GAMECUBE:
InputOverrider.registerGameCube(0);
mGameCubeRegistered = true;
addGameCubeOverlayControls(orientation);
break;
case OVERLAY_WIIMOTE:
case OVERLAY_WIIMOTE_SIDEWAYS:
mControllerIndex = controllerIndex - 4;
InputOverrider.registerWii(mControllerIndex);
mWiimoteRegistered[mControllerIndex] = true;
case OVERLAY_WIIMOTE:
case OVERLAY_WIIMOTE_SIDEWAYS:
InputOverrider.registerWii(0);
mWiiRegistered = true;
addWiimoteOverlayControls(orientation);
break;
addWiimoteOverlayControls(orientation);
break;
case OVERLAY_WIIMOTE_NUNCHUK:
InputOverrider.registerWii(0);
mWiiRegistered = true;
addWiimoteOverlayControls(orientation);
addNunchukOverlayControls(orientation);
break;
case OVERLAY_WIIMOTE_NUNCHUK:
mControllerIndex = controllerIndex - 4;
InputOverrider.registerWii(mControllerIndex);
mWiimoteRegistered[mControllerIndex] = true;
case OVERLAY_WIIMOTE_CLASSIC:
InputOverrider.registerWii(0);
mWiiRegistered = true;
addClassicOverlayControls(orientation);
break;
addWiimoteOverlayControls(orientation);
addNunchukOverlayControls(orientation);
break;
case OVERLAY_NONE:
break;
}
case OVERLAY_WIIMOTE_CLASSIC:
mControllerIndex = controllerIndex - 4;
InputOverrider.registerWii(mControllerIndex);
mWiimoteRegistered[mControllerIndex] = true;
addClassicOverlayControls(orientation);
break;
case OVERLAY_NONE:
break;
}
}
@ -875,20 +875,20 @@ public final class InputOverlay extends SurfaceView implements OnTouchListener
}
}
public void resetButtonPlacement()
public void resetButtonPlacement(Settings settings)
{
boolean isLandscape =
getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE;
// Values for these come from R.array.controllersEntries
if (!NativeLibrary.IsEmulatingWii() || getConfiguredControllerType() == OVERLAY_GAMECUBE)
final int controller = getConfiguredControllerType(settings);
if (controller == OVERLAY_GAMECUBE)
{
if (isLandscape)
gcDefaultOverlay();
else
gcPortraitDefaultOverlay();
}
else if (getConfiguredControllerType() == OVERLAY_WIIMOTE_CLASSIC)
else if (controller == OVERLAY_WIIMOTE_CLASSIC)
{
if (isLandscape)
wiiClassicDefaultOverlay();
@ -908,27 +908,52 @@ public final class InputOverlay extends SurfaceView implements OnTouchListener
wiiOnlyPortraitDefaultOverlay();
}
}
refreshControls();
refreshControls(settings);
}
public static int getConfiguredControllerType(Context context)
public static int getConfiguredControllerType(Settings settings)
{
return PreferenceManager.getDefaultSharedPreferences(context)
.getInt("wiiController", OVERLAY_WIIMOTE_NUNCHUK);
IntSetting controllerSetting = NativeLibrary.IsEmulatingWii() ?
IntSetting.MAIN_OVERLAY_WII_CONTROLLER : IntSetting.MAIN_OVERLAY_GC_CONTROLLER;
int controllerIndex = controllerSetting.getInt(settings);
if (controllerIndex >= 0 && controllerIndex < 4)
{
// GameCube controller
if (IntSetting.getSettingForSIDevice(controllerIndex).getInt(settings) == 6)
return OVERLAY_GAMECUBE;
}
else if (controllerIndex >= 4 && controllerIndex < 8)
{
// Wii Remote
int wiimoteIndex = controllerIndex - 4;
if (IntSetting.getSettingForWiimoteSource(wiimoteIndex).getInt(settings) == 1)
{
int attachmentIndex = EmulatedController.getSelectedWiimoteAttachment(wiimoteIndex);
switch (attachmentIndex)
{
case 1:
return OVERLAY_WIIMOTE_NUNCHUK;
case 2:
return OVERLAY_WIIMOTE_CLASSIC;
}
NumericSetting sidewaysSetting = EmulatedController.getSidewaysWiimoteSetting(wiimoteIndex);
boolean sideways = new InputMappingBooleanSetting(sidewaysSetting).getBoolean(settings);
return sideways ? OVERLAY_WIIMOTE_SIDEWAYS : OVERLAY_WIIMOTE;
}
}
return OVERLAY_NONE;
}
private int getConfiguredControllerType()
{
return mPreferences.getInt("wiiController", OVERLAY_WIIMOTE_NUNCHUK);
}
private void saveControlPosition(int sharedPrefsId, int x, int y, int controller,
String orientation)
private void saveControlPosition(int sharedPrefsId, int x, int y, String orientation)
{
final SharedPreferences sPrefs = PreferenceManager.getDefaultSharedPreferences(getContext());
SharedPreferences.Editor sPrefsEditor = sPrefs.edit();
sPrefsEditor.putFloat(getXKey(sharedPrefsId, controller, orientation), x);
sPrefsEditor.putFloat(getYKey(sharedPrefsId, controller, orientation), y);
sPrefsEditor.putFloat(getXKey(sharedPrefsId, mControllerType, orientation), x);
sPrefsEditor.putFloat(getYKey(sharedPrefsId, mControllerType, orientation), y);
sPrefsEditor.apply();
}
@ -989,7 +1014,7 @@ public final class InputOverlay extends SurfaceView implements OnTouchListener
* @param control Control identifier for the button the InputOverlayDrawableButton represents.
* @return An {@link InputOverlayDrawableButton} with the correct drawing bounds set.
*/
private static InputOverlayDrawableButton initializeOverlayButton(Context context,
private InputOverlayDrawableButton initializeOverlayButton(Context context,
int defaultResId, int pressedResId, int legacyId, int control, String orientation)
{
// Resources handle for fetching the initial Drawable resource.
@ -997,7 +1022,6 @@ public final class InputOverlay extends SurfaceView implements OnTouchListener
// SharedPreference to retrieve the X and Y coordinates for the InputOverlayDrawableButton.
final SharedPreferences sPrefs = PreferenceManager.getDefaultSharedPreferences(context);
int controller = getConfiguredControllerType(context);
// Decide scale based on button ID and user preference
float scale;
@ -1023,7 +1047,7 @@ public final class InputOverlay extends SurfaceView implements OnTouchListener
break;
case ButtonType.WIIMOTE_BUTTON_1:
case ButtonType.WIIMOTE_BUTTON_2:
if (controller == 2)
if (mControllerType == OVERLAY_WIIMOTE_SIDEWAYS)
scale = 0.14f;
else
scale = 0.0875f;
@ -1061,8 +1085,8 @@ public final class InputOverlay extends SurfaceView implements OnTouchListener
// The X and Y coordinates of the InputOverlayDrawableButton on the InputOverlay.
// These were set in the input overlay configuration menu.
int drawableX = (int) sPrefs.getFloat(getXKey(legacyId, controller, orientation), 0f);
int drawableY = (int) sPrefs.getFloat(getYKey(legacyId, controller, orientation), 0f);
int drawableX = (int) sPrefs.getFloat(getXKey(legacyId, mControllerType, orientation), 0f);
int drawableY = (int) sPrefs.getFloat(getYKey(legacyId, mControllerType, orientation), 0f);
int width = overlayDrawable.getWidth();
int height = overlayDrawable.getHeight();
@ -1092,7 +1116,7 @@ public final class InputOverlay extends SurfaceView implements OnTouchListener
* @param rightControl Control identifier for the right button.
* @return the initialized {@link InputOverlayDrawableDpad}
*/
private static InputOverlayDrawableDpad initializeOverlayDpad(Context context,
private InputOverlayDrawableDpad initializeOverlayDpad(Context context,
int defaultResId,
int pressedOneDirectionResId,
int pressedTwoDirectionsResId,
@ -1108,7 +1132,6 @@ public final class InputOverlay extends SurfaceView implements OnTouchListener
// SharedPreference to retrieve the X and Y coordinates for the InputOverlayDrawableDpad.
final SharedPreferences sPrefs = PreferenceManager.getDefaultSharedPreferences(context);
int controller = getConfiguredControllerType(context);
// Decide scale based on button ID and user preference
float scale;
@ -1122,7 +1145,7 @@ public final class InputOverlay extends SurfaceView implements OnTouchListener
scale = 0.275f;
break;
default:
if (controller == 2 || controller == 1)
if (mControllerType == OVERLAY_WIIMOTE_SIDEWAYS || mControllerType == OVERLAY_WIIMOTE)
scale = 0.275f;
else
scale = 0.2125f;
@ -1148,8 +1171,8 @@ public final class InputOverlay extends SurfaceView implements OnTouchListener
// The X and Y coordinates of the InputOverlayDrawableDpad on the InputOverlay.
// These were set in the input overlay configuration menu.
int drawableX = (int) sPrefs.getFloat(getXKey(legacyId, controller, orientation), 0f);
int drawableY = (int) sPrefs.getFloat(getYKey(legacyId, controller, orientation), 0f);
int drawableX = (int) sPrefs.getFloat(getXKey(legacyId, mControllerType, orientation), 0f);
int drawableY = (int) sPrefs.getFloat(getYKey(legacyId, mControllerType, orientation), 0f);
int width = overlayDrawable.getWidth();
int height = overlayDrawable.getHeight();
@ -1177,16 +1200,15 @@ public final class InputOverlay extends SurfaceView implements OnTouchListener
* @param yControl Control identifier for the Y axis.
* @return the initialized {@link InputOverlayDrawableJoystick}.
*/
private static InputOverlayDrawableJoystick initializeOverlayJoystick(Context context,
int resOuter, int defaultResInner, int pressedResInner, int legacyId, int xControl,
int yControl, String orientation)
private InputOverlayDrawableJoystick initializeOverlayJoystick(Context context, int resOuter,
int defaultResInner, int pressedResInner, int legacyId, int xControl, int yControl,
String orientation)
{
// Resources handle for fetching the initial Drawable resource.
final Resources res = context.getResources();
// SharedPreference to retrieve the X and Y coordinates for the InputOverlayDrawableJoystick.
final SharedPreferences sPrefs = PreferenceManager.getDefaultSharedPreferences(context);
int controller = getConfiguredControllerType(context);
// Decide scale based on user preference
float scale = 0.275f;
@ -1201,8 +1223,8 @@ public final class InputOverlay extends SurfaceView implements OnTouchListener
// The X and Y coordinates of the InputOverlayDrawableButton on the InputOverlay.
// These were set in the input overlay configuration menu.
int drawableX = (int) sPrefs.getFloat(getXKey(legacyId, controller, orientation), 0f);
int drawableY = (int) sPrefs.getFloat(getYKey(legacyId, controller, orientation), 0f);
int drawableX = (int) sPrefs.getFloat(getXKey(legacyId, mControllerType, orientation), 0f);
int drawableY = (int) sPrefs.getFloat(getYKey(legacyId, mControllerType, orientation), 0f);
// Decide inner scale based on joystick ID
float innerScale;
@ -1225,7 +1247,8 @@ public final class InputOverlay extends SurfaceView implements OnTouchListener
// Send the drawableId to the joystick so it can be referenced when saving control position.
final InputOverlayDrawableJoystick overlayDrawable =
new InputOverlayDrawableJoystick(res, bitmapOuter, bitmapInnerDefault,
bitmapInnerPressed, outerRect, innerRect, legacyId, xControl, yControl);
bitmapInnerPressed, outerRect, innerRect, legacyId, xControl, yControl,
mControllerIndex);
// Need to set the image's position
overlayDrawable.setPosition(drawableX, drawableY);

View File

@ -31,6 +31,7 @@ public final class InputOverlayDrawableJoystick
private int mPreviousTouchX, mPreviousTouchY;
private final int mWidth;
private final int mHeight;
private final int mControllerIndex;
private Rect mVirtBounds;
private Rect mOrigBounds;
private int mOpacity;
@ -55,7 +56,7 @@ public final class InputOverlayDrawableJoystick
*/
public InputOverlayDrawableJoystick(Resources res, Bitmap bitmapOuter, Bitmap bitmapInnerDefault,
Bitmap bitmapInnerPressed, Rect rectOuter, Rect rectInner, int legacyId, int xControl,
int yControl)
int yControl, int controllerIndex)
{
mJoystickLegacyId = legacyId;
mJoystickXControl = xControl;
@ -68,6 +69,10 @@ public final class InputOverlayDrawableJoystick
mWidth = bitmapOuter.getWidth();
mHeight = bitmapOuter.getHeight();
if (controllerIndex < 0 || controllerIndex >= 4)
throw new IllegalArgumentException("controllerIndex must be 0-3");
mControllerIndex = controllerIndex;
setBounds(rectOuter);
mDefaultStateInnerBitmap.setBounds(rectInner);
mPressedStateInnerBitmap.setBounds(rectInner);
@ -221,7 +226,8 @@ public final class InputOverlayDrawableJoystick
double angle = Math.atan2(y, x) + Math.PI + Math.PI;
double radius = Math.hypot(y, x);
double maxRadius = InputOverrider.getGateRadiusAtAngle(0, mJoystickXControl, angle);
double maxRadius = InputOverrider.getGateRadiusAtAngle(mControllerIndex, mJoystickXControl,
angle);
if (radius > maxRadius)
{
y = maxRadius * Math.sin(angle);

View File

@ -32,6 +32,7 @@ public class InputOverlayPointer
private int mMode;
private boolean mRecenter;
private int mControllerIndex;
private boolean doubleTap = false;
private int mDoubleTapControl;
@ -47,11 +48,13 @@ public class InputOverlayPointer
DOUBLE_TAP_OPTIONS.add(NativeLibrary.ButtonType.CLASSIC_BUTTON_A);
}
public InputOverlayPointer(Rect surfacePosition, int doubleTapControl, int mode, boolean recenter)
public InputOverlayPointer(Rect surfacePosition, int doubleTapControl, int mode, boolean recenter,
int controllerIndex)
{
mDoubleTapControl = doubleTapControl;
mMode = mode;
mRecenter = recenter;
mControllerIndex = controllerIndex;
mGameCenterX = (surfacePosition.left + surfacePosition.right) / 2.0f;
mGameCenterY = (surfacePosition.top + surfacePosition.bottom) / 2.0f;
@ -128,8 +131,9 @@ public class InputOverlayPointer
{
if (doubleTap)
{
InputOverrider.setControlState(0, mDoubleTapControl, 1.0);
new Handler().postDelayed(() -> InputOverrider.setControlState(0, mDoubleTapControl, 0.0),
InputOverrider.setControlState(mControllerIndex, mDoubleTapControl, 1.0);
new Handler().postDelayed(() -> InputOverrider.setControlState(mControllerIndex,
mDoubleTapControl, 0.0),
50);
}
else

View File

@ -1,65 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-or-later
package org.dolphinemu.dolphinemu.utils;
import android.view.InputDevice;
import android.view.KeyEvent;
import android.view.MotionEvent;
/**
* Some controllers have incorrect mappings. This class has special-case fixes for them.
*/
public class ControllerMappingHelper
{
/**
* Some controllers report extra button presses that can be ignored.
*/
public static boolean shouldKeyBeIgnored(InputDevice inputDevice, int keyCode)
{
if (isDualShock4(inputDevice))
{
// The two analog triggers generate analog motion events as well as a keycode.
// We always prefer to use the analog values, so throw away the button press
return keyCode == KeyEvent.KEYCODE_BUTTON_L2 || keyCode == KeyEvent.KEYCODE_BUTTON_R2;
}
return false;
}
/**
* Scale an axis to be zero-centered with a proper range.
*/
public static float scaleAxis(InputDevice inputDevice, int axis, float value)
{
if (isDualShock4(inputDevice))
{
// Android doesn't have correct mappings for this controller's triggers. It reports them
// as RX & RY, centered at -1.0, and with a range of [-1.0, 1.0]
// Scale them to properly zero-centered with a range of [0.0, 1.0].
if (axis == MotionEvent.AXIS_RX || axis == MotionEvent.AXIS_RY)
{
return (value + 1) / 2.0f;
}
}
else if (isXboxOneWireless(inputDevice))
{
// Same as the DualShock 4, the mappings are missing.
if (axis == MotionEvent.AXIS_Z || axis == MotionEvent.AXIS_RZ)
{
return (value + 1) / 2.0f;
}
}
return value;
}
private static boolean isDualShock4(InputDevice inputDevice)
{
// Sony DualShock 4 controller
return inputDevice.getVendorId() == 0x54c && inputDevice.getProductId() == 0x9cc;
}
private static boolean isXboxOneWireless(InputDevice inputDevice)
{
// Microsoft Xbox One controller
return inputDevice.getVendorId() == 0x45e && inputDevice.getProductId() == 0x2e0;
}
}

View File

@ -31,17 +31,21 @@ import java.io.InputStream;
import java.io.OutputStream;
/**
* A service that spawns its own thread in order to copy several binary and shader files
* from the Dolphin APK to the external file system.
* A class that spawns its own thread in order perform initialization.
*
* The initialization steps include:
* - Extracting the Sys directory from the APK so it can be accessed using regular file APIs
* - Letting the native code know where on external storage it should place the User directory
* - Running the native code's init steps (which include things like populating the User directory)
*/
public final class DirectoryInitialization
{
public static final String EXTRA_STATE = "directoryState";
private static final int WiimoteNewVersion = 5; // Last changed in PR 8907
private static final MutableLiveData<DirectoryInitializationState> directoryState =
new MutableLiveData<>(DirectoryInitializationState.NOT_YET_INITIALIZED);
private static volatile boolean areDirectoriesAvailable = false;
private static String userPath;
private static String sysPath;
private static boolean isUsingLegacyUserDirectory = false;
public enum DirectoryInitializationState
@ -73,8 +77,7 @@ public final class DirectoryInitialization
System.exit(1);
}
initializeInternalStorage(context);
boolean wiimoteIniWritten = initializeExternalStorage(context);
extractSysDirectory(context);
NativeLibrary.Initialize();
NativeLibrary.ReportStartToAnalytics();
@ -82,13 +85,6 @@ public final class DirectoryInitialization
checkThemeSettings(context);
if (wiimoteIniWritten)
{
// This has to be done after calling NativeLibrary.Initialize(),
// as it relies on the config system
EmulationActivity.updateWiimoteNewIniPreferences(context);
}
directoryState.postValue(DirectoryInitializationState.DOLPHIN_DIRECTORIES_INITIALIZED);
}
@ -131,7 +127,7 @@ public final class DirectoryInitialization
return true;
}
private static void initializeInternalStorage(Context context)
private static void extractSysDirectory(Context context)
{
File sysDirectory = new File(context.getFilesDir(), "Sys");
@ -142,7 +138,7 @@ public final class DirectoryInitialization
// There is no extracted Sys directory, or there is a Sys directory from another
// version of Dolphin that might contain outdated files. Let's (re-)extract Sys.
deleteDirectoryRecursively(sysDirectory);
copyAssetFolder("Sys", sysDirectory, true, context);
copyAssetFolder("Sys", sysDirectory, context);
SharedPreferences.Editor editor = preferences.edit();
editor.putString("sysDirectoryVersion", revision);
@ -150,46 +146,8 @@ public final class DirectoryInitialization
}
// Let the native code know where the Sys directory is.
SetSysDirectory(sysDirectory.getPath());
}
// Returns whether the WiimoteNew.ini file was written to
private static boolean initializeExternalStorage(Context context)
{
// Create User directory structure and copy some NAND files from the extracted Sys directory.
CreateUserDirectories();
// GCPadNew.ini and WiimoteNew.ini must contain specific values in order for controller
// input to work as intended (they aren't user configurable), so we overwrite them just
// in case the user has tried to modify them manually.
//
// ...Except WiimoteNew.ini contains the user configurable settings for Wii Remote
// extensions in addition to all of its lines that aren't user configurable, so since we
// don't want to lose the selected extensions, we don't overwrite that file if it exists.
//
// TODO: Redo the Android controller system so that we don't have to extract these INIs.
String configDirectory = NativeLibrary.GetUserDirectory() + File.separator + "Config";
String profileDirectory =
NativeLibrary.GetUserDirectory() + File.separator + "Config/Profiles/Wiimote/";
createWiimoteProfileDirectory(profileDirectory);
copyAsset("GCPadNew.ini", new File(configDirectory, "GCPadNew.ini"), true, context);
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
boolean overwriteWiimoteIni = prefs.getInt("WiimoteNewVersion", 0) != WiimoteNewVersion;
boolean wiimoteIniWritten = copyAsset("WiimoteNew.ini",
new File(configDirectory, "WiimoteNew.ini"), overwriteWiimoteIni, context);
if (overwriteWiimoteIni)
{
SharedPreferences.Editor sPrefsEditor = prefs.edit();
sPrefsEditor.putInt("WiimoteNewVersion", WiimoteNewVersion);
sPrefsEditor.apply();
}
copyAsset("WiimoteProfile.ini", new File(profileDirectory, "WiimoteProfile.ini"), true,
context);
return wiimoteIniWritten;
sysPath = sysDirectory.getPath();
SetSysDirectory(sysPath);
}
private static void deleteDirectoryRecursively(@NonNull final File file)
@ -240,26 +198,33 @@ public final class DirectoryInitialization
return userPath;
}
public static String getSysDirectory()
{
if (!areDirectoriesAvailable)
{
throw new IllegalStateException(
"DirectoryInitialization must run before accessing the Sys directory!");
}
return sysPath;
}
public static File getGameListCache(Context context)
{
return new File(context.getExternalCacheDir(), "gamelist.cache");
}
private static boolean copyAsset(String asset, File output, Boolean overwrite, Context context)
private static boolean copyAsset(String asset, File output, Context context)
{
Log.verbose("[DirectoryInitialization] Copying File " + asset + " to " + output);
try
{
if (!output.exists() || overwrite)
try (InputStream in = context.getAssets().open(asset))
{
try (InputStream in = context.getAssets().open(asset))
try (OutputStream out = new FileOutputStream(output))
{
try (OutputStream out = new FileOutputStream(output))
{
copyFile(in, out);
return true;
}
copyFile(in, out);
return true;
}
}
}
@ -271,8 +236,7 @@ public final class DirectoryInitialization
return false;
}
private static void copyAssetFolder(String assetFolder, File outputFolder, Boolean overwrite,
Context context)
private static void copyAssetFolder(String assetFolder, File outputFolder, Context context)
{
Log.verbose("[DirectoryInitialization] Copying Folder " + assetFolder + " to " +
outputFolder);
@ -299,9 +263,8 @@ public final class DirectoryInitialization
createdFolder = true;
}
copyAssetFolder(assetFolder + File.separator + file, new File(outputFolder, file),
overwrite, context);
copyAsset(assetFolder + File.separator + file, new File(outputFolder, file), overwrite,
context);
copyAsset(assetFolder + File.separator + file, new File(outputFolder, file), context);
}
}
catch (IOException e)
@ -322,18 +285,6 @@ public final class DirectoryInitialization
}
}
private static void createWiimoteProfileDirectory(String directory)
{
File wiiPath = new File(directory);
if (!wiiPath.isDirectory())
{
if (!wiiPath.mkdirs())
{
Log.error("[DirectoryInitialization] Failed to create folder " + wiiPath.getAbsolutePath());
}
}
}
public static boolean preferOldFolderPicker(Context context)
{
// As of January 2021, ACTION_OPEN_DOCUMENT_TREE seems to be broken on the Nvidia Shield TV
@ -433,7 +384,5 @@ public final class DirectoryInitialization
}
}
private static native void CreateUserDirectories();
private static native void SetSysDirectory(String path);
}

View File

@ -0,0 +1,58 @@
// SPDX-License-Identifier: GPL-2.0-or-later
package org.dolphinemu.dolphinemu.utils;
import android.os.Looper;
public class LooperThread extends Thread
{
private Looper mLooper;
public LooperThread()
{
super();
}
public LooperThread(String name)
{
super(name);
}
@Override
public void run()
{
Looper.prepare();
synchronized (this)
{
mLooper = Looper.myLooper();
notifyAll();
}
Looper.loop();
}
public Looper getLooper()
{
if (!isAlive())
{
throw new IllegalStateException();
}
synchronized (this)
{
while (mLooper == null)
{
try
{
wait();
}
catch (InterruptedException ignored)
{
}
}
}
return mLooper;
}
}

View File

@ -1,128 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-or-later
package org.dolphinemu.dolphinemu.utils;
import android.app.Activity;
import android.content.Context;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.view.Surface;
import org.dolphinemu.dolphinemu.NativeLibrary;
import org.dolphinemu.dolphinemu.NativeLibrary.ButtonType;
public class MotionListener implements SensorEventListener
{
private final Activity mActivity;
private final SensorManager mSensorManager;
private final Sensor mAccelSensor;
private final Sensor mGyroSensor;
private boolean mEnabled = false;
// The same sampling period as for Wii Remotes
private static final int SAMPLING_PERIOD_US = 1000000 / 200;
public MotionListener(Activity activity)
{
mActivity = activity;
mSensorManager = (SensorManager) activity.getSystemService(Context.SENSOR_SERVICE);
mAccelSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
mGyroSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE);
}
@Override
public void onSensorChanged(SensorEvent sensorEvent)
{
float x, y;
float z = sensorEvent.values[2];
int orientation = mActivity.getWindowManager().getDefaultDisplay().getRotation();
switch (orientation)
{
default:
case Surface.ROTATION_0:
x = -sensorEvent.values[0];
y = -sensorEvent.values[1];
break;
case Surface.ROTATION_90:
x = sensorEvent.values[1];
y = -sensorEvent.values[0];
break;
case Surface.ROTATION_180:
x = sensorEvent.values[0];
y = sensorEvent.values[1];
break;
case Surface.ROTATION_270:
x = -sensorEvent.values[1];
y = sensorEvent.values[0];
break;
}
if (sensorEvent.sensor == mAccelSensor)
{
NativeLibrary.onGamePadMoveEvent(NativeLibrary.TouchScreenDevice,
ButtonType.WIIMOTE_ACCEL_LEFT, x);
NativeLibrary.onGamePadMoveEvent(NativeLibrary.TouchScreenDevice,
ButtonType.WIIMOTE_ACCEL_RIGHT, x);
NativeLibrary.onGamePadMoveEvent(NativeLibrary.TouchScreenDevice,
ButtonType.WIIMOTE_ACCEL_FORWARD, y);
NativeLibrary.onGamePadMoveEvent(NativeLibrary.TouchScreenDevice,
ButtonType.WIIMOTE_ACCEL_BACKWARD, y);
NativeLibrary.onGamePadMoveEvent(NativeLibrary.TouchScreenDevice,
ButtonType.WIIMOTE_ACCEL_UP, z);
NativeLibrary.onGamePadMoveEvent(NativeLibrary.TouchScreenDevice,
ButtonType.WIIMOTE_ACCEL_DOWN, z);
}
if (sensorEvent.sensor == mGyroSensor)
{
NativeLibrary.onGamePadMoveEvent(NativeLibrary.TouchScreenDevice,
ButtonType.WIIMOTE_GYRO_PITCH_UP, x);
NativeLibrary.onGamePadMoveEvent(NativeLibrary.TouchScreenDevice,
ButtonType.WIIMOTE_GYRO_PITCH_DOWN, x);
NativeLibrary.onGamePadMoveEvent(NativeLibrary.TouchScreenDevice,
ButtonType.WIIMOTE_GYRO_ROLL_LEFT, y);
NativeLibrary.onGamePadMoveEvent(NativeLibrary.TouchScreenDevice,
ButtonType.WIIMOTE_GYRO_ROLL_RIGHT, y);
NativeLibrary.onGamePadMoveEvent(NativeLibrary.TouchScreenDevice,
ButtonType.WIIMOTE_GYRO_YAW_LEFT, z);
NativeLibrary.onGamePadMoveEvent(NativeLibrary.TouchScreenDevice,
ButtonType.WIIMOTE_GYRO_YAW_RIGHT, z);
}
}
@Override
public void onAccuracyChanged(Sensor sensor, int i)
{
// We don't care about this
}
public void enable()
{
if (mEnabled)
return;
if (mAccelSensor != null)
mSensorManager.registerListener(this, mAccelSensor, SAMPLING_PERIOD_US);
if (mGyroSensor != null)
mSensorManager.registerListener(this, mGyroSensor, SAMPLING_PERIOD_US);
NativeLibrary.SetMotionSensorsEnabled(mAccelSensor != null, mGyroSensor != null);
mEnabled = true;
}
public void disable()
{
if (!mEnabled)
return;
mSensorManager.unregisterListener(this);
NativeLibrary.SetMotionSensorsEnabled(false, false);
mEnabled = false;
}
}

View File

@ -1,97 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-or-later
package org.dolphinemu.dolphinemu.utils;
import android.content.Context;
import android.os.Build;
import android.os.VibrationEffect;
import android.os.Vibrator;
import android.util.SparseArray;
import android.view.InputDevice;
import org.dolphinemu.dolphinemu.activities.EmulationActivity;
import org.dolphinemu.dolphinemu.features.settings.model.AdHocStringSetting;
import org.dolphinemu.dolphinemu.features.settings.model.BooleanSetting;
import org.dolphinemu.dolphinemu.features.settings.model.Settings;
import org.dolphinemu.dolphinemu.features.settings.utils.SettingsFile;
public class Rumble
{
private static Vibrator phoneVibrator;
private static final SparseArray<Vibrator> emuVibrators = new SparseArray<>();
public static void initRumble(EmulationActivity activity)
{
clear();
if (BooleanSetting.MAIN_PHONE_RUMBLE.getBooleanGlobal())
{
setPhoneVibrator(true, activity);
}
for (int i = 0; i < 8; i++)
{
String deviceName = AdHocStringSetting.getStringGlobal(Settings.FILE_DOLPHIN,
Settings.SECTION_BINDINGS, SettingsFile.KEY_EMU_RUMBLE + i, "");
if (!deviceName.isEmpty())
{
for (int id : InputDevice.getDeviceIds())
{
InputDevice device = InputDevice.getDevice(id);
if (deviceName.equals(device.getDescriptor()))
{
Vibrator vib = device.getVibrator();
if (vib != null && vib.hasVibrator())
emuVibrators.put(i, vib);
}
}
}
}
}
public static void setPhoneVibrator(boolean set, EmulationActivity activity)
{
if (set)
{
Vibrator vib = (Vibrator) activity.getSystemService(Context.VIBRATOR_SERVICE);
if (vib != null && vib.hasVibrator())
phoneVibrator = vib;
}
else
{
phoneVibrator = null;
}
}
private static void clear()
{
phoneVibrator = null;
emuVibrators.clear();
}
public static void checkRumble(int padId, double state)
{
if (phoneVibrator != null)
doRumble(phoneVibrator);
if (emuVibrators.get(padId) != null)
doRumble(emuVibrators.get(padId));
}
public static void doRumble(Vibrator vib)
{
// Check again that it exists and can vibrate
if (vib != null && vib.hasVibrator())
{
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
{
vib.vibrate(VibrationEffect.createOneShot(100, VibrationEffect.DEFAULT_AMPLITUDE));
}
else
{
vib.vibrate(100);
}
}
}
}

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="?attr/colorControlNormal"
android:pathData="M7,21Q6.175,21 5.588,20.413Q5,19.825 5,19V6H4V4H9V3H15V4H20V6H19V19Q19,19.825 18.413,20.413Q17.825,21 17,21ZM17,6H7V19Q7,19 7,19Q7,19 7,19H17Q17,19 17,19Q17,19 17,19ZM9,17H11V8H9ZM13,17H15V8H13ZM7,6V19Q7,19 7,19Q7,19 7,19Q7,19 7,19Q7,19 7,19Z"/>
</vector>

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportHeight="24"
android:viewportWidth="24">
<path
android:fillColor="?attr/colorControlNormal"
android:pathData="M12,8c1.1,0 2,-0.9 2,-2s-0.9,-2 -2,-2 -2,0.9 -2,2 0.9,2 2,2zM12,10c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2zM12,16c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2z" />
</vector>

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportHeight="24"
android:viewportWidth="24">
<path
android:fillColor="?attr/colorControlNormal"
android:pathData="M17,3L5,3c-1.11,0 -2,0.9 -2,2v14c0,1.1 0.89,2 2,2h14c1.1,0 2,-0.9 2,-2L21,7l-4,-4zM12,19c-1.66,0 -3,-1.34 -3,-3s1.34,-3 3,-3 3,1.34 3,3 -1.34,3 -3,3zM15,9L5,9L5,5h10v4z" />
</vector>

View File

@ -0,0 +1,56 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout android:id="@+id/root"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?android:attr/selectableItemBackground"
android:clickable="true"
android:focusable="true"
android:nextFocusLeft="@id/button_advanced_settings">
<TextView
android:id="@+id/text_setting_name"
style="@style/TextAppearance.MaterialComponents.Headline5"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
android:layout_marginEnd="@dimen/spacing_large"
android:layout_marginStart="@dimen/spacing_large"
android:layout_marginTop="@dimen/spacing_large"
android:textSize="16sp"
android:textAlignment="viewStart"
android:layout_toStartOf="@+id/button_more_settings"
tools:text="Setting Name" />
<TextView
android:id="@+id/text_setting_description"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_alignStart="@+id/text_setting_name"
android:layout_below="@+id/text_setting_name"
android:layout_marginBottom="@dimen/spacing_large"
android:layout_marginEnd="@dimen/spacing_large"
android:layout_marginStart="@dimen/spacing_large"
android:layout_marginTop="@dimen/spacing_small"
android:layout_toStartOf="@+id/button_advanced_settings"
android:textAlignment="viewStart"
tools:text="@string/overclock_enable_description" />
<Button
android:id="@+id/button_advanced_settings"
style="?attr/materialIconButtonStyle"
android:contentDescription="@string/advanced_settings"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_centerVertical="true"
android:layout_marginEnd="@dimen/spacing_small"
android:nextFocusRight="@id/root"
app:icon="@drawable/ic_more"
app:iconTint="?attr/colorOnPrimaryContainer" />
</RelativeLayout>

View File

@ -39,6 +39,18 @@
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior"/>
<TextView
android:id="@+id/old_controller_settings_warning"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:background="?attr/colorErrorContainer"
android:text="@string/old_controller_settings"
android:textColor="?attr/colorOnErrorContainer"
android:visibility="invisible"
android:clickable="true"
android:focusable="false" />
<View
android:id="@+id/workaround_view"
android:layout_width="match_parent"

View File

@ -0,0 +1,54 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingHorizontal="@dimen/spacing_large"
android:paddingTop="@dimen/spacing_medlarge">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/list_control"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_above="@+id/expression"
android:layout_alignParentEnd="true"
android:layout_alignParentStart="true"
android:layout_below="@+id/device" />
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/device"
style="@style/Widget.Material3.TextInputLayout.OutlinedBox.ExposedDropdownMenu"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
android:hint="@string/input_device">
<com.google.android.material.textfield.MaterialAutoCompleteTextView
android:id="@+id/dropdown_device"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="none" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/expression"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentEnd="true"
android:layout_alignParentStart="true"
android:hint="@string/input_expression">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/edit_expression"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="start"
android:importantForAutofill="no"
android:typeface="monospace" />
</com.google.android.material.textfield.TextInputLayout>
</RelativeLayout>

View File

@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.appcompat.widget.LinearLayoutCompat
android:id="@+id/profile_sheet"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<com.google.android.material.bottomsheet.BottomSheetDragHandleView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/profile_list"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</androidx.appcompat.widget.LinearLayoutCompat>
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View File

@ -1,132 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1">
<com.google.android.material.slider.Slider
android:id="@+id/slider_yaw"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintEnd_toStartOf="@id/text_ir_yaw"
app:layout_constraintStart_toEndOf="@id/text_ir_yaw_units"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/text_ir_yaw"
android:layout_width="26dp"
android:layout_height="wrap_content"
android:layout_marginEnd="24dp"
android:layout_marginStart="@dimen/spacing_medlarge"
android:gravity="end"
app:layout_constraintBottom_toBottomOf="@+id/slider_yaw"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/slider_yaw"
app:layout_constraintTop_toTopOf="@+id/slider_yaw"
tools:text="100" />
<TextView
android:id="@+id/text_ir_yaw_units"
android:layout_width="92dp"
android:layout_height="wrap_content"
android:layout_marginStart="24dp"
android:layout_marginEnd="@dimen/spacing_medlarge"
android:textAlignment="viewStart"
app:layout_constraintBottom_toBottomOf="@+id/slider_yaw"
app:layout_constraintEnd_toStartOf="@id/slider_yaw"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@+id/slider_yaw"
tools:text="Total Yaw" />
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1">
<com.google.android.material.slider.Slider
android:id="@+id/slider_pitch"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintEnd_toStartOf="@id/text_ir_pitch"
app:layout_constraintStart_toEndOf="@id/text_ir_pitch_units"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/text_ir_pitch"
android:layout_width="26dp"
android:layout_height="wrap_content"
android:layout_marginEnd="24dp"
android:layout_marginStart="@dimen/spacing_medlarge"
android:gravity="end"
app:layout_constraintBottom_toBottomOf="@+id/slider_pitch"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/slider_pitch"
app:layout_constraintTop_toTopOf="@+id/slider_pitch"
tools:text="100" />
<TextView
android:id="@+id/text_ir_pitch_units"
android:layout_width="92dp"
android:layout_height="wrap_content"
android:layout_marginStart="24dp"
android:layout_marginEnd="@dimen/spacing_medlarge"
android:textAlignment="viewStart"
app:layout_constraintBottom_toBottomOf="@+id/slider_pitch"
app:layout_constraintEnd_toStartOf="@id/slider_pitch"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@+id/slider_pitch"
tools:text="Total Pitch" />
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1">
<com.google.android.material.slider.Slider
android:id="@+id/slider_vertical_offset"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintEnd_toStartOf="@id/text_ir_vertical_offset"
app:layout_constraintStart_toEndOf="@id/text_ir_vertical_offset_units"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/text_ir_vertical_offset"
android:layout_width="26dp"
android:layout_height="wrap_content"
android:layout_marginEnd="24dp"
android:layout_marginStart="@dimen/spacing_medlarge"
android:gravity="end"
app:layout_constraintBottom_toBottomOf="@+id/slider_vertical_offset"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/slider_vertical_offset"
app:layout_constraintTop_toTopOf="@+id/slider_vertical_offset"
tools:text="100" />
<TextView
android:id="@+id/text_ir_vertical_offset_units"
android:layout_width="92dp"
android:layout_height="wrap_content"
android:layout_marginStart="24dp"
android:layout_marginEnd="@dimen/spacing_medlarge"
android:textAlignment="viewStart"
app:layout_constraintBottom_toBottomOf="@+id/slider_vertical_offset"
app:layout_constraintEnd_toStartOf="@id/slider_vertical_offset"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@+id/slider_vertical_offset"
tools:text="Vertical Offset" />
</androidx.constraintlayout.widget.ConstraintLayout>
</LinearLayout>

View File

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="54dp"
android:background="?android:attr/selectableItemBackground"
android:focusable="true"
android:clickable="true">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="@style/TextAppearance.MaterialComponents.Headline5"
tools:text="Button A"
android:layout_alignParentEnd="true"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
android:layout_marginStart="@dimen/spacing_large"
android:layout_marginEnd="@dimen/spacing_large"
android:layout_marginTop="@dimen/spacing_large"
android:id="@+id/text_name"
android:textAlignment="viewStart"
android:textSize="16sp" />
</RelativeLayout>

View File

@ -0,0 +1,56 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout android:id="@+id/root"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?android:attr/selectableItemBackground"
android:clickable="true"
android:focusable="true"
android:nextFocusRight="@id/button_advanced_settings">
<TextView
android:id="@+id/text_setting_name"
style="@style/TextAppearance.MaterialComponents.Headline5"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
android:layout_marginEnd="@dimen/spacing_large"
android:layout_marginStart="@dimen/spacing_large"
android:layout_marginTop="@dimen/spacing_large"
android:textSize="16sp"
android:textAlignment="viewStart"
android:layout_toStartOf="@+id/button_advanced_settings"
tools:text="Setting Name" />
<TextView
android:id="@+id/text_setting_description"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_alignStart="@+id/text_setting_name"
android:layout_below="@+id/text_setting_name"
android:layout_marginBottom="@dimen/spacing_large"
android:layout_marginEnd="@dimen/spacing_large"
android:layout_marginStart="@dimen/spacing_large"
android:layout_marginTop="@dimen/spacing_small"
android:layout_toStartOf="@+id/button_advanced_settings"
android:textAlignment="viewStart"
tools:text="@string/overclock_enable_description" />
<Button
android:id="@+id/button_advanced_settings"
style="?attr/materialIconButtonStyle"
android:contentDescription="@string/advanced_settings"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_centerVertical="true"
android:layout_marginEnd="@dimen/spacing_small"
android:nextFocusLeft="@id/root"
app:icon="@drawable/ic_more"
app:iconTint="?attr/colorOnPrimaryContainer" />
</RelativeLayout>

View File

@ -0,0 +1,69 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/root"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingHorizontal="@dimen/spacing_medlarge"
android:paddingVertical="@dimen/spacing_medlarge">
<com.google.android.material.textview.MaterialTextView
android:id="@+id/text_name"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_marginEnd="@dimen/spacing_large"
android:layout_marginStart="@dimen/spacing_small"
android:textColor="?attr/colorOnSurface"
android:textSize="16sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/button_delete"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="Wii Remote with Motion Plus Pointing" />
<Button
android:id="@+id/button_delete"
style="?attr/materialIconButtonFilledTonalStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/spacing_small"
android:contentDescription="@string/input_profile_delete"
android:tooltipText="@string/input_profile_delete"
app:icon="@drawable/ic_delete"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/button_load"
app:layout_constraintStart_toEndOf="@id/text_name"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="@+id/button_load"
style="?attr/materialIconButtonFilledTonalStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/spacing_small"
android:contentDescription="@string/input_profile_load"
android:tooltipText="@string/input_profile_load"
app:icon="@drawable/ic_load"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/button_save"
app:layout_constraintStart_toEndOf="@id/button_delete"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="@+id/button_save"
style="?attr/materialIconButtonFilledTonalStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/spacing_small"
android:contentDescription="@string/input_profile_save"
android:tooltipText="@string/input_profile_save"
app:icon="@drawable/ic_save"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/button_load"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -20,9 +20,8 @@
android:title="@string/emulation_control_joystick_rel_center"/>
<item
android:id="@+id/menu_emulation_rumble"
android:checkable="true"
android:title="@string/emulation_control_rumble"/>
android:id="@+id/menu_emulation_choose_controller"
android:title="@string/emulation_choose_controller"/>
<item
android:id="@+id/menu_emulation_reset_overlay"

View File

@ -19,21 +19,12 @@
android:id="@+id/menu_emulation_joystick_rel_center"
android:checkable="true"
android:title="@string/emulation_control_joystick_rel_center"/>
<item
android:id="@+id/menu_emulation_rumble"
android:checkable="true"
android:title="@string/emulation_control_rumble"/>
</group>
<item
android:id="@+id/menu_emulation_choose_controller"
android:title="@string/emulation_choose_controller"/>
<item
android:id="@+id/menu_emulation_motion_controls"
android:title="@string/emulation_motion_controls"/>
<item
android:id="@+id/menu_emulation_ir_group"
android:title="@string/emulation_ir_group">
@ -45,9 +36,6 @@
<item
android:id="@+id/menu_emulation_set_ir_mode"
android:title="@string/emulation_ir_mode"/>
<item
android:id="@+id/menu_emulation_set_ir_sensitivity"
android:title="@string/emulation_ir_sensitivity"/>
<item
android:id="@+id/menu_emulation_choose_doubletap"
android:title="@string/emulation_choose_doubletap"/>

View File

@ -210,14 +210,14 @@
<item>@string/extension_drums</item>
<item>@string/extension_turntable</item>
</string-array>
<string-array name="wiimoteExtensionsValues">
<item>None</item>
<item>Nunchuk</item>
<item>Classic</item>
<item>Guitar</item>
<item>Drums</item>
<item>Turntable</item>
</string-array>
<integer-array name="wiimoteExtensionsValues">
<item>0</item>
<item>1</item>
<item>2</item>
<item>3</item>
<item>4</item>
<item>5</item>
</integer-array>
<!-- Texture Cache Accuracy Preference -->
<string-array name="textureCacheAccuracyEntries">
@ -386,23 +386,6 @@
<item>2</item>
</integer-array>
<string-array name="controllersEntries">
<item>@string/controller_gc</item>
<item>@string/controller_wiimote</item>
<item>@string/controller_horizontal_wiimote</item>
<item>@string/controller_wiimote_plus_nunchuk</item>
<item>@string/controller_classic</item>
<item>@string/controller_none</item>
</string-array>
<string-array name="controllersValues">
<item>None</item>
<item>None</item>
<item>None</item>
<item>Nunchuk</item>
<item>Classic</item>
<item>None</item>
</string-array>
<string-array name="gcpadButtons">
<item>A</item>
<item>B</item>

View File

@ -6,109 +6,55 @@
<string name="host">app</string>
<string name="scheme">dolphinemu</string>
<string name="none">None</string>
<string name="controller_0">GameCube Controller 1</string>
<string name="controller_1">GameCube Controller 2</string>
<string name="controller_2">GameCube Controller 3</string>
<string name="controller_3">GameCube Controller 4</string>
<string name="controller_control">Control Stick</string>
<string name="controller_c">C Stick</string>
<string name="controller_trig">Triggers</string>
<string name="controller_dpad">D-Pad</string>
<string name="modifier_range">Modifier Range</string>
<string name="analog_radius">Analog Radius (High value = High sensitivity)</string>
<string name="analog_threshold">Analog Threshold (Low value = High sensitivity)</string>
<string name="wiimote_0">Wii Remote 1</string>
<string name="wiimote_1">Wii Remote 2</string>
<string name="wiimote_2">Wii Remote 3</string>
<string name="wiimote_3">Wii Remote 4</string>
<string name="wiimote_4">Wii Remote 1</string>
<string name="wiimote_5">Wii Remote 2</string>
<string name="wiimote_6">Wii Remote 3</string>
<string name="wiimote_7">Wii Remote 4</string>
<string name="wiimote_extension_4">Wii Remote Extension 1</string>
<string name="wiimote_extension_5">Wii Remote Extension 2</string>
<string name="wiimote_extension_6">Wii Remote Extension 3</string>
<string name="wiimote_extension_7">Wii Remote Extension 4</string>
<string name="wiimote_extension_0">Wii Remote Extension 1</string>
<string name="wiimote_extension_1">Wii Remote Extension 2</string>
<string name="wiimote_extension_2">Wii Remote Extension 3</string>
<string name="wiimote_extension_3">Wii Remote Extension 4</string>
<string name="wiimote">Wii Remote</string>
<string name="wiimote_general">Buttons</string>
<string name="wiimote_motion_simulation">Motion Simulation</string>
<string name="wiimote_motion_input">Motion Input</string>
<string name="wiimote_extensions">Extension</string>
<string name="wiimote_extensions_description">Choose and bind the Wii Remote extension.</string>
<string name="wiimote_ir">IR</string>
<string name="wiimote_swing">Swing</string>
<string name="wiimote_tilt">Tilt</string>
<string name="wiimote_shake">Shake</string>
<string name="input_device">Device</string>
<string name="input_device_all_devices">Create Mappings for Other Devices</string>
<string name="input_device_all_devices_description">Detects inputs from all devices, not just the selected device.</string>
<string name="input_profile">Profile</string>
<string name="input_profiles_empty">You haven\'t created any profiles yet.</string>
<string name="input_profiles">Profiles</string>
<string name="input_profile_new">(New Profile)</string>
<string name="input_profile_load">Load</string>
<string name="input_profile_save">Save</string>
<string name="input_profile_delete">Delete</string>
<string name="input_profile_confirm_load">Do you want to discard your current controller settings and load the profile \"%1$s\"?</string>
<string name="input_profile_confirm_save">Do you want to overwrite the profile \"%1$s\"?</string>
<string name="input_profile_confirm_delete">Do you want to delete the profile \"%1$s\"?</string>
<string name="input_reset_to_default">Default</string>
<string name="input_reset_to_default_description">Reset settings for this controller to the default.</string>
<string name="input_clear">Clear</string>
<string name="input_clear_description">Clear settings for this controller.</string>
<string name="input_reset_warning">Are you sure? Your current controller settings will be deleted.</string>
<string name="old_controller_settings">Your controller settings are from an old version of Dolphin and won\'t work in this version. Press \"Default\" to start over with new settings.</string>
<string name="input_binding">Input Binding</string>
<string name="input_binding_description">Press or move an input to bind it to %1$s.</string>
<string name="input_rumble_description">Press or move any input to set rumble.</string>
<!-- Generic buttons (Shared with lots of stuff) -->
<string name="generic_buttons">Buttons</string>
<string name="generic_up">Up</string>
<string name="generic_down">Down</string>
<string name="generic_left">Left</string>
<string name="generic_right">Right</string>
<string name="generic_forward">Forward</string>
<string name="generic_backward">Backward</string>
<string name="generic_stick">Stick</string>
<string name="generic_green">Green</string>
<string name="generic_red">Red</string>
<string name="generic_yellow">Yellow</string>
<string name="generic_blue">Blue</string>
<string name="generic_orange">Orange</string>
<!-- GameCube buttons (May be shared with other stuff too) -->
<string name="button_a">A</string>
<string name="button_b">B</string>
<string name="button_start">START</string>
<string name="button_x">X</string>
<string name="button_y">Y</string>
<string name="button_z">Z</string>
<string name="trigger_left">L</string>
<string name="trigger_right">R</string>
<!-- Wii Remote buttons (May be shared with other stuff too) -->
<string name="button_one">1</string>
<string name="button_two">2</string>
<string name="button_minus">-</string>
<string name="button_plus">+</string>
<string name="button_home">HOME</string>
<string name="ir_hide">Hide</string>
<string name="tilt_modifier">Modifier</string>
<string name="shake_x">X</string>
<string name="shake_y">Y</string>
<string name="shake_z">Z</string>
<!-- Nunchuk only buttons -->
<string name="nunchuk_button_c">C</string>
<!-- Classic only buttons -->
<string name="classic_button_zl">ZL</string>
<string name="classic_button_zr">ZR</string>
<string name="classic_leftstick">Left Stick</string>
<string name="classic_rightstick">Right Stick</string>
<!-- Guitar only buttons -->
<string name="guitar_frets">Frets</string>
<string name="guitar_strum">Strum</string>
<string name="guitar_whammy">Whammy</string>
<string name="guitar_whammy_bar">Bar</string>
<!-- Drums only buttons -->
<string name="drums_pads">Pads</string>
<string name="drums_pad_bass">Bass</string>
<!-- Turntable only buttons -->
<string name="turntable_button_green_left">Green Left</string>
<string name="turntable_button_red_left">Red Left</string>
<string name="turntable_button_blue_left">Blue Left</string>
<string name="turntable_button_green_right">Green Right</string>
<string name="turntable_button_red_right">Red Right</string>
<string name="turntable_button_blue_right">Blue Right</string>
<string name="turntable_button_euphoria">Euphoria</string>
<string name="turntable_table_left">Table Left</string>
<string name="turntable_table_right">Table Right</string>
<string name="turntable_effect">Effect</string>
<string name="turntable_effect_dial">Dial</string>
<string name="turntable_crossfade">Crossfade</string>
<string name="input_binding_no_device">You need to select a device first!</string>
<string name="input_configure_input">Configure Input</string>
<string name="input_configure_output">Configure Output</string>
<string name="input_expression">Expression</string>
<!-- Main Preference Fragment -->
<string name="settings">Settings</string>
@ -479,6 +425,7 @@
<string name="other">Other</string>
<string name="continue_anyway">Continue Anyway</string>
<string name="more_settings">More Settings</string>
<string name="advanced_settings">Advanced Settings</string>
<!-- Game Grid Screen-->
<string name="platform_gamecube">GameCube Games</string>
@ -528,7 +475,6 @@
<string name="preferences_save_exit">Save and Exit</string>
<string name="preferences_game_properties">Game Properties</string>
<string name="preferences_game_properties_with_game_id">Game Properties: %1$s</string>
<string name="preferences_extensions">Extension Bindings</string>
<string name="game_ini_junk_title">Junk Data Found</string>
<string name="game_ini_junk_question">The settings file for this game contains extraneous data added by an old version of Dolphin. This will likely prevent global settings from working as intended.\n\nWould you like to fix this by deleting the settings file for this game? All game-specific settings and cheats that you have added will be removed. This cannot be undone.</string>
@ -626,8 +572,8 @@ It can efficiently compress both junk data and encrypted Wii data.
<string name="emulation_control_opacity">Opacity</string>
<string name="emulation_control_adjustments">Adjust Controls</string>
<string name="emulation_control_joystick_rel_center">Relative Stick Center</string>
<string name="emulation_control_rumble">Rumble</string>
<string name="emulation_choose_controller">Choose Controller</string>
<string name="emulation_more_controller_settings">More Settings</string>
<string name="emulation_menu_help">Press Back to access the menu.\nLong press Back to exit emulation.</string>
<string name="emulation_touch_overlay_reset">Reset Overlay</string>
<string name="emulation_ir_group">Touch IR Pointer</string>
@ -635,7 +581,6 @@ It can efficiently compress both junk data and encrypted Wii data.
<string name="emulation_ir_mode">IR Mode</string>
<string name="emulation_ir_sensitivity">IR Sensitivity</string>
<string name="emulation_choose_doubletap">Double tap button</string>
<string name="emulation_motion_controls">Motion Controls</string>
<!-- GC Adapter Menu-->
<string name="gc_adapter_rumble">Enable Vibration</string>
@ -643,13 +588,6 @@ It can efficiently compress both junk data and encrypted Wii data.
<string name="gc_adapter_bongos">Bongo Controller</string>
<string name="gc_adapter_bongos_description">Enable this if you are using bongos on this port.</string>
<!-- Wii Remote Input Menu-->
<string name="header_wiimote_general">General</string>
<string name="header_controllers">Controllers</string>
<!-- Rumble -->
<string name="rumble_not_found">Device rumble not found</string>
<string name="path_not_changeable_scoped_storage">Due to the Scoped Storage policy in Android 11 and newer, you can\'t change this path.</string>
<string name="load_settings">Loading Settings…</string>
<string name="setting_not_runtime_editable">This setting can\'t be changed while a game is running.</string>
@ -674,14 +612,12 @@ It can efficiently compress both junk data and encrypted Wii data.
<string name="unavailable_paths">Dolphin does not have permission to access one or more configured paths. Would you like to fix this before starting?</string>
<!-- Misc -->
<string name="pitch">Total Pitch</string>
<string name="yaw">Total Yaw</string>
<string name="vertical_offset">Vertical Offset</string>
<string name="enabled">Enabled</string>
<string name="default_values">Default Values</string>
<string name="slider_setting_value">%1$d%2$s</string>
<string name="disc_number">Disc %1$d</string>
<string name="replug_gc_adapter">GameCube Adapter couldn\'t be opened. Please re-plug the device.</string>
<string name="disabled_gc_overlay_notice">GameCube Controller 1 is set to \"None\"</string>
<string name="disabled_gc_overlay_notice">The selected GameCube controller is set to \"None\"</string>
<string name="ignore_warning_alert_messages">Ignore for this session</string>
<!-- UI CPU Core selection -->
@ -825,14 +761,6 @@ It can efficiently compress both junk data and encrypted Wii data.
<string name="wiimote_emulated">Emulated</string>
<string name="wiimote_real">Real Wii Remote (DolphinBar required)</string>
<!-- Controller Types -->
<string name="controller_gc">GameCube Controller</string>
<string name="controller_wiimote">Wii Remote</string>
<string name="controller_horizontal_wiimote">Horizontal Wii Remote</string>
<string name="controller_wiimote_plus_nunchuk">Wii Remote + Nunchuk</string>
<string name="controller_classic">Classic Controller</string>
<string name="controller_none">None</string>
<!-- Gamepad Controls -->
<string name="gamepad_start">Start</string>
<string name="gamepad_d_pad">D-Pad</string>

View File

@ -13,7 +13,6 @@ static jclass s_string_class;
static jclass s_native_library_class;
static jmethodID s_display_alert_msg;
static jmethodID s_do_rumble;
static jmethodID s_update_touch_pointer;
static jmethodID s_on_title_changed;
static jmethodID s_finish_emulation_activity;
@ -84,6 +83,34 @@ static jfieldID s_riivolution_patches_pointer;
static jclass s_wii_update_cb_class;
static jmethodID s_wii_update_cb_run;
static jclass s_control_class;
static jfieldID s_control_pointer;
static jmethodID s_control_constructor;
static jclass s_numeric_setting_class;
static jfieldID s_numeric_setting_pointer;
static jmethodID s_numeric_setting_constructor;
static jclass s_control_group_class;
static jfieldID s_control_group_pointer;
static jmethodID s_control_group_constructor;
static jclass s_control_reference_class;
static jfieldID s_control_reference_pointer;
static jmethodID s_control_reference_constructor;
static jclass s_emulated_controller_class;
static jfieldID s_emulated_controller_pointer;
static jmethodID s_emulated_controller_constructor;
static jclass s_core_device_class;
static jfieldID s_core_device_pointer;
static jmethodID s_core_device_constructor;
static jclass s_core_device_control_class;
static jfieldID s_core_device_control_pointer;
static jmethodID s_core_device_control_constructor;
namespace IDCache
{
JNIEnv* GetEnvForThread()
@ -124,11 +151,6 @@ jmethodID GetDisplayAlertMsg()
return s_display_alert_msg;
}
jmethodID GetDoRumble()
{
return s_do_rumble;
}
jmethodID GetUpdateTouchPointer()
{
return s_update_touch_pointer;
@ -389,6 +411,111 @@ jmethodID GetWiiUpdateCallbackFunction()
return s_wii_update_cb_run;
}
jclass GetControlClass()
{
return s_control_class;
}
jfieldID GetControlPointer()
{
return s_control_pointer;
}
jmethodID GetControlConstructor()
{
return s_control_constructor;
}
jclass GetControlGroupClass()
{
return s_control_group_class;
}
jfieldID GetControlGroupPointer()
{
return s_control_group_pointer;
}
jmethodID GetControlGroupConstructor()
{
return s_control_group_constructor;
}
jclass GetControlReferenceClass()
{
return s_control_reference_class;
}
jfieldID GetControlReferencePointer()
{
return s_control_reference_pointer;
}
jmethodID GetControlReferenceConstructor()
{
return s_control_reference_constructor;
}
jclass GetEmulatedControllerClass()
{
return s_emulated_controller_class;
}
jfieldID GetEmulatedControllerPointer()
{
return s_emulated_controller_pointer;
}
jmethodID GetEmulatedControllerConstructor()
{
return s_emulated_controller_constructor;
}
jclass GetNumericSettingClass()
{
return s_numeric_setting_class;
}
jfieldID GetNumericSettingPointer()
{
return s_numeric_setting_pointer;
}
jmethodID GetNumericSettingConstructor()
{
return s_numeric_setting_constructor;
}
jclass GetCoreDeviceClass()
{
return s_core_device_class;
}
jfieldID GetCoreDevicePointer()
{
return s_core_device_pointer;
}
jmethodID GetCoreDeviceConstructor()
{
return s_core_device_constructor;
}
jclass GetCoreDeviceControlClass()
{
return s_core_device_control_class;
}
jfieldID GetCoreDeviceControlPointer()
{
return s_core_device_control_pointer;
}
jmethodID GetCoreDeviceControlConstructor()
{
return s_core_device_control_constructor;
}
} // namespace IDCache
extern "C" {
@ -408,7 +535,6 @@ JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved)
s_native_library_class = reinterpret_cast<jclass>(env->NewGlobalRef(native_library_class));
s_display_alert_msg = env->GetStaticMethodID(s_native_library_class, "displayAlertMsg",
"(Ljava/lang/String;Ljava/lang/String;ZZZ)Z");
s_do_rumble = env->GetStaticMethodID(s_native_library_class, "rumble", "(ID)V");
s_update_touch_pointer =
env->GetStaticMethodID(s_native_library_class, "updateTouchPointer", "()V");
s_on_title_changed = env->GetStaticMethodID(s_native_library_class, "onTitleChanged", "()V");
@ -548,6 +674,59 @@ JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved)
s_wii_update_cb_run = env->GetMethodID(s_wii_update_cb_class, "run", "(IIJ)Z");
env->DeleteLocalRef(wii_update_cb_class);
const jclass control_class =
env->FindClass("org/dolphinemu/dolphinemu/features/input/model/controlleremu/Control");
s_control_class = reinterpret_cast<jclass>(env->NewGlobalRef(control_class));
s_control_pointer = env->GetFieldID(control_class, "mPointer", "J");
s_control_constructor = env->GetMethodID(control_class, "<init>", "(J)V");
env->DeleteLocalRef(control_class);
const jclass control_group_class =
env->FindClass("org/dolphinemu/dolphinemu/features/input/model/controlleremu/ControlGroup");
s_control_group_class = reinterpret_cast<jclass>(env->NewGlobalRef(control_group_class));
s_control_group_pointer = env->GetFieldID(control_group_class, "mPointer", "J");
s_control_group_constructor = env->GetMethodID(control_group_class, "<init>", "(J)V");
env->DeleteLocalRef(control_group_class);
const jclass control_reference_class = env->FindClass(
"org/dolphinemu/dolphinemu/features/input/model/controlleremu/ControlReference");
s_control_reference_class = reinterpret_cast<jclass>(env->NewGlobalRef(control_reference_class));
s_control_reference_pointer = env->GetFieldID(control_reference_class, "mPointer", "J");
s_control_reference_constructor = env->GetMethodID(control_reference_class, "<init>", "(J)V");
env->DeleteLocalRef(control_reference_class);
const jclass emulated_controller_class = env->FindClass(
"org/dolphinemu/dolphinemu/features/input/model/controlleremu/EmulatedController");
s_emulated_controller_class =
reinterpret_cast<jclass>(env->NewGlobalRef(emulated_controller_class));
s_emulated_controller_pointer = env->GetFieldID(emulated_controller_class, "mPointer", "J");
s_emulated_controller_constructor = env->GetMethodID(emulated_controller_class, "<init>", "(J)V");
env->DeleteLocalRef(emulated_controller_class);
const jclass numeric_setting_class =
env->FindClass("org/dolphinemu/dolphinemu/features/input/model/controlleremu/NumericSetting");
s_numeric_setting_class = reinterpret_cast<jclass>(env->NewGlobalRef(numeric_setting_class));
s_numeric_setting_pointer = env->GetFieldID(numeric_setting_class, "mPointer", "J");
s_numeric_setting_constructor = env->GetMethodID(numeric_setting_class, "<init>", "(J)V");
env->DeleteLocalRef(numeric_setting_class);
const jclass core_device_class =
env->FindClass("org/dolphinemu/dolphinemu/features/input/model/CoreDevice");
s_core_device_class = reinterpret_cast<jclass>(env->NewGlobalRef(core_device_class));
s_core_device_pointer = env->GetFieldID(core_device_class, "mPointer", "J");
s_core_device_constructor = env->GetMethodID(core_device_class, "<init>", "(J)V");
env->DeleteLocalRef(core_device_class);
const jclass core_device_control_class =
env->FindClass("org/dolphinemu/dolphinemu/features/input/model/CoreDevice$Control");
s_core_device_control_class =
reinterpret_cast<jclass>(env->NewGlobalRef(core_device_control_class));
s_core_device_control_pointer = env->GetFieldID(core_device_control_class, "mPointer", "J");
s_core_device_control_constructor =
env->GetMethodID(core_device_control_class, "<init>",
"(Lorg/dolphinemu/dolphinemu/features/input/model/CoreDevice;J)V");
env->DeleteLocalRef(core_device_control_class);
return JNI_VERSION;
}
@ -575,5 +754,12 @@ JNIEXPORT void JNI_OnUnload(JavaVM* vm, void* reserved)
env->DeleteGlobalRef(s_graphics_mod_class);
env->DeleteGlobalRef(s_riivolution_patches_class);
env->DeleteGlobalRef(s_wii_update_cb_class);
env->DeleteGlobalRef(s_control_class);
env->DeleteGlobalRef(s_control_group_class);
env->DeleteGlobalRef(s_control_reference_class);
env->DeleteGlobalRef(s_emulated_controller_class);
env->DeleteGlobalRef(s_numeric_setting_class);
env->DeleteGlobalRef(s_core_device_class);
env->DeleteGlobalRef(s_core_device_control_class);
}
}

View File

@ -13,7 +13,6 @@ jclass GetStringClass();
jclass GetNativeLibraryClass();
jmethodID GetDisplayAlertMsg();
jmethodID GetDoRumble();
jmethodID GetUpdateTouchPointer();
jmethodID GetOnTitleChanged();
jmethodID GetFinishEmulationActivity();
@ -83,4 +82,32 @@ jfieldID GetRiivolutionPatchesPointer();
jclass GetWiiUpdateCallbackClass();
jmethodID GetWiiUpdateCallbackFunction();
jclass GetControlClass();
jfieldID GetControlPointer();
jmethodID GetControlConstructor();
jclass GetControlGroupClass();
jfieldID GetControlGroupPointer();
jmethodID GetControlGroupConstructor();
jclass GetControlReferenceClass();
jfieldID GetControlReferencePointer();
jmethodID GetControlReferenceConstructor();
jclass GetEmulatedControllerClass();
jfieldID GetEmulatedControllerPointer();
jmethodID GetEmulatedControllerConstructor();
jclass GetNumericSettingClass();
jfieldID GetNumericSettingPointer();
jmethodID GetNumericSettingConstructor();
jclass GetCoreDeviceClass();
jfieldID GetCoreDevicePointer();
jmethodID GetCoreDeviceConstructor();
jclass GetCoreDeviceControlClass();
jfieldID GetCoreDeviceControlPointer();
jmethodID GetCoreDeviceControlConstructor();
} // namespace IDCache

View File

@ -10,7 +10,20 @@ add_library(main SHARED
GameList/GameFile.cpp
GameList/GameFile.h
GameList/GameFileCache.cpp
Input/Control.cpp
Input/Control.h
Input/ControlGroup.cpp
Input/ControlGroup.h
Input/ControlReference.cpp
Input/ControlReference.h
Input/CoreDevice.cpp
Input/CoreDevice.h
Input/EmulatedController.cpp
Input/EmulatedController.h
Input/InputOverrider.cpp
Input/MappingCommon.cpp
Input/NumericSetting.cpp
Input/NumericSetting.h
IniFile.cpp
MainAndroid.cpp
RiivolutionPatches.cpp
@ -23,6 +36,7 @@ PRIVATE
androidcommon
common
core
inputcommon
uicommon
)

View File

@ -0,0 +1,51 @@
// Copyright 2022 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "jni/Input/Control.h"
#include <string>
#include <jni.h>
#include "Common/MsgHandler.h"
#include "InputCommon/ControlReference/ControlReference.h"
#include "InputCommon/ControllerEmu/Control/Control.h"
#include "jni/AndroidCommon/AndroidCommon.h"
#include "jni/AndroidCommon/IDCache.h"
#include "jni/Input/ControlReference.h"
static ControllerEmu::Control* GetPointer(JNIEnv* env, jobject obj)
{
return reinterpret_cast<ControllerEmu::Control*>(
env->GetLongField(obj, IDCache::GetControlPointer()));
}
jobject ControlToJava(JNIEnv* env, ControllerEmu::Control* control)
{
if (!control)
return nullptr;
return env->NewObject(IDCache::GetControlClass(), IDCache::GetControlConstructor(),
reinterpret_cast<jlong>(control));
}
extern "C" {
JNIEXPORT jstring JNICALL
Java_org_dolphinemu_dolphinemu_features_input_model_controlleremu_Control_getUiName(JNIEnv* env,
jobject obj)
{
ControllerEmu::Control* control = GetPointer(env, obj);
std::string ui_name = control->ui_name;
if (control->translate == ControllerEmu::Translatability::Translate)
ui_name = Common::GetStringT(ui_name.c_str());
return ToJString(env, ui_name);
}
JNIEXPORT jobject JNICALL
Java_org_dolphinemu_dolphinemu_features_input_model_controlleremu_Control_getControlReference(
JNIEnv* env, jobject obj)
{
return ControlReferenceToJava(env, GetPointer(env, obj)->control_ref.get());
}
}

View File

@ -0,0 +1,13 @@
// Copyright 2022 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <jni.h>
namespace ControllerEmu
{
class Control;
}
jobject ControlToJava(JNIEnv* env, ControllerEmu::Control* control);

View File

@ -0,0 +1,103 @@
// Copyright 2022 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "jni/Input/ControlGroup.h"
#include <jni.h>
#include "Common/MsgHandler.h"
#include "InputCommon/ControllerEmu/ControlGroup/Attachments.h"
#include "InputCommon/ControllerEmu/ControlGroup/ControlGroup.h"
#include "jni/AndroidCommon/AndroidCommon.h"
#include "jni/AndroidCommon/IDCache.h"
#include "jni/Input/Control.h"
#include "jni/Input/NumericSetting.h"
static ControllerEmu::ControlGroup* GetPointer(JNIEnv* env, jobject obj)
{
return reinterpret_cast<ControllerEmu::ControlGroup*>(
env->GetLongField(obj, IDCache::GetControlGroupPointer()));
}
jobject ControlGroupToJava(JNIEnv* env, ControllerEmu::ControlGroup* group)
{
if (!group)
return nullptr;
return env->NewObject(IDCache::GetControlGroupClass(), IDCache::GetControlGroupConstructor(),
reinterpret_cast<jlong>(group));
}
extern "C" {
JNIEXPORT jstring JNICALL
Java_org_dolphinemu_dolphinemu_features_input_model_controlleremu_ControlGroup_getUiName(
JNIEnv* env, jobject obj)
{
return ToJString(env, Common::GetStringT(GetPointer(env, obj)->ui_name.c_str()));
}
JNIEXPORT jint JNICALL
Java_org_dolphinemu_dolphinemu_features_input_model_controlleremu_ControlGroup_getGroupType(
JNIEnv* env, jobject obj)
{
return static_cast<jint>(GetPointer(env, obj)->type);
}
JNIEXPORT jint JNICALL
Java_org_dolphinemu_dolphinemu_features_input_model_controlleremu_ControlGroup_getDefaultEnabledValue(
JNIEnv* env, jobject obj)
{
return static_cast<jint>(GetPointer(env, obj)->default_value);
}
JNIEXPORT jboolean JNICALL
Java_org_dolphinemu_dolphinemu_features_input_model_controlleremu_ControlGroup_getEnabled(
JNIEnv* env, jobject obj)
{
return static_cast<jboolean>(GetPointer(env, obj)->enabled);
}
JNIEXPORT void JNICALL
Java_org_dolphinemu_dolphinemu_features_input_model_controlleremu_ControlGroup_setEnabled(
JNIEnv* env, jobject obj, jboolean value)
{
GetPointer(env, obj)->enabled = value;
}
JNIEXPORT jint JNICALL
Java_org_dolphinemu_dolphinemu_features_input_model_controlleremu_ControlGroup_getControlCount(
JNIEnv* env, jobject obj)
{
return static_cast<jint>(GetPointer(env, obj)->controls.size());
}
JNIEXPORT jobject JNICALL
Java_org_dolphinemu_dolphinemu_features_input_model_controlleremu_ControlGroup_getControl(
JNIEnv* env, jobject obj, jint i)
{
return ControlToJava(env, GetPointer(env, obj)->controls[i].get());
}
JNIEXPORT jint JNICALL
Java_org_dolphinemu_dolphinemu_features_input_model_controlleremu_ControlGroup_getNumericSettingCount(
JNIEnv* env, jobject obj)
{
return static_cast<jint>(GetPointer(env, obj)->numeric_settings.size());
}
JNIEXPORT jobject JNICALL
Java_org_dolphinemu_dolphinemu_features_input_model_controlleremu_ControlGroup_getNumericSetting(
JNIEnv* env, jobject obj, jint i)
{
return NumericSettingToJava(env, GetPointer(env, obj)->numeric_settings[i].get());
}
JNIEXPORT jobject JNICALL
Java_org_dolphinemu_dolphinemu_features_input_model_controlleremu_ControlGroup_getAttachmentSetting(
JNIEnv* env, jobject obj)
{
auto* group = reinterpret_cast<ControllerEmu::Attachments*>(GetPointer(env, obj));
return NumericSettingToJava(env, &group->GetSelectionSetting());
}
}

View File

@ -0,0 +1,13 @@
// Copyright 2022 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <jni.h>
namespace ControllerEmu
{
class ControlGroup;
}
jobject ControlGroupToJava(JNIEnv* env, ControllerEmu::ControlGroup* group);

View File

@ -0,0 +1,61 @@
// Copyright 2022 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "jni/Input/ControlReference.h"
#include <string>
#include <jni.h>
#include "InputCommon/ControlReference/ControlReference.h"
#include "jni/AndroidCommon/AndroidCommon.h"
#include "jni/AndroidCommon/IDCache.h"
ControlReference* ControlReferenceFromJava(JNIEnv* env, jobject control_reference)
{
return reinterpret_cast<ControlReference*>(
env->GetLongField(control_reference, IDCache::GetControlReferencePointer()));
}
jobject ControlReferenceToJava(JNIEnv* env, ControlReference* control_reference)
{
if (!control_reference)
return nullptr;
return env->NewObject(IDCache::GetControlReferenceClass(),
IDCache::GetControlReferenceConstructor(),
reinterpret_cast<jlong>(control_reference));
}
extern "C" {
JNIEXPORT jdouble JNICALL
Java_org_dolphinemu_dolphinemu_features_input_model_controlleremu_ControlReference_getState(
JNIEnv* env, jobject obj)
{
return ControlReferenceFromJava(env, obj)->GetState<double>();
}
JNIEXPORT jstring JNICALL
Java_org_dolphinemu_dolphinemu_features_input_model_controlleremu_ControlReference_getExpression(
JNIEnv* env, jobject obj)
{
return ToJString(env, ControlReferenceFromJava(env, obj)->GetExpression());
}
JNIEXPORT jstring JNICALL
Java_org_dolphinemu_dolphinemu_features_input_model_controlleremu_ControlReference_setExpression(
JNIEnv* env, jobject obj, jstring expr)
{
const std::optional<std::string> result =
ControlReferenceFromJava(env, obj)->SetExpression(GetJString(env, expr));
return result ? ToJString(env, *result) : nullptr;
}
JNIEXPORT jboolean JNICALL
Java_org_dolphinemu_dolphinemu_features_input_model_controlleremu_ControlReference_isInput(
JNIEnv* env, jobject obj)
{
return static_cast<jboolean>(ControlReferenceFromJava(env, obj)->IsInput());
}
}

View File

@ -0,0 +1,11 @@
// Copyright 2022 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <jni.h>
class ControlReference;
jobject ControlReferenceToJava(JNIEnv* env, ControlReference* control_reference);
ControlReference* ControlReferenceFromJava(JNIEnv* env, jobject control_reference);

View File

@ -0,0 +1,84 @@
// Copyright 2022 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "jni/Input/CoreDevice.h"
#include <memory>
#include <utility>
#include <vector>
#include <jni.h>
#include "InputCommon/ControllerInterface/CoreDevice.h"
#include "jni/AndroidCommon/AndroidCommon.h"
#include "jni/AndroidCommon/IDCache.h"
static ciface::Core::Device::Control* GetControlPointer(JNIEnv* env, jobject obj)
{
return reinterpret_cast<ciface::Core::Device::Control*>(
env->GetLongField(obj, IDCache::GetCoreDeviceControlPointer()));
}
static jobject CoreDeviceControlToJava(JNIEnv* env, jobject device,
ciface::Core::Device::Control* control)
{
if (!control)
return nullptr;
return env->NewObject(IDCache::GetCoreDeviceControlClass(),
IDCache::GetCoreDeviceControlConstructor(), device,
reinterpret_cast<jlong>(control));
}
template <typename T>
static jobjectArray CoreDeviceControlVectorToJava(JNIEnv* env, jobject device,
const std::vector<T*>& controls)
{
return VectorToJObjectArray(
env, controls, IDCache::GetCoreDeviceControlClass(),
[device](JNIEnv* env, T* control) { return CoreDeviceControlToJava(env, device, control); });
}
static std::shared_ptr<ciface::Core::Device>* GetDevicePointer(JNIEnv* env, jobject obj)
{
return reinterpret_cast<std::shared_ptr<ciface::Core::Device>*>(
env->GetLongField(obj, IDCache::GetCoreDevicePointer()));
}
jobject CoreDeviceToJava(JNIEnv* env, std::shared_ptr<ciface::Core::Device> device)
{
if (!device)
return nullptr;
return env->NewObject(
IDCache::GetCoreDeviceClass(), IDCache::GetCoreDeviceConstructor(),
reinterpret_cast<jlong>(new std::shared_ptr<ciface::Core::Device>(std::move(device))));
}
extern "C" {
JNIEXPORT jstring JNICALL
Java_org_dolphinemu_dolphinemu_features_input_model_CoreDevice_00024Control_getName(JNIEnv* env,
jobject obj)
{
return ToJString(env, GetControlPointer(env, obj)->GetName());
}
JNIEXPORT void JNICALL
Java_org_dolphinemu_dolphinemu_features_input_model_CoreDevice_finalize(JNIEnv* env, jobject obj)
{
delete GetDevicePointer(env, obj);
}
JNIEXPORT jobjectArray JNICALL
Java_org_dolphinemu_dolphinemu_features_input_model_CoreDevice_getInputs(JNIEnv* env, jobject obj)
{
return CoreDeviceControlVectorToJava(env, obj, (*GetDevicePointer(env, obj))->Inputs());
}
JNIEXPORT jobjectArray JNICALL
Java_org_dolphinemu_dolphinemu_features_input_model_CoreDevice_getOutputs(JNIEnv* env, jobject obj)
{
return CoreDeviceControlVectorToJava(env, obj, (*GetDevicePointer(env, obj))->Outputs());
}
}

View File

@ -0,0 +1,15 @@
// Copyright 2022 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <memory>
#include <jni.h>
namespace ciface::Core
{
class Device;
}
jobject CoreDeviceToJava(JNIEnv* env, std::shared_ptr<ciface::Core::Device> device);

View File

@ -0,0 +1,174 @@
// Copyright 2022 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <jni.h>
#include "Common/FileUtil.h"
#include "Common/IniFile.h"
#include "Core/HW/GCPad.h"
#include "Core/HW/Wiimote.h"
#include "Core/HW/WiimoteEmu/WiimoteEmu.h"
#include "InputCommon/ControllerEmu/ControlGroup/Attachments.h"
#include "InputCommon/ControllerEmu/ControllerEmu.h"
#include "InputCommon/ControllerInterface/ControllerInterface.h"
#include "InputCommon/InputConfig.h"
#include "jni/AndroidCommon/AndroidCommon.h"
#include "jni/AndroidCommon/IDCache.h"
#include "jni/Input/Control.h"
#include "jni/Input/ControlGroup.h"
#include "jni/Input/ControlReference.h"
#include "jni/Input/NumericSetting.h"
ControllerEmu::EmulatedController* EmulatedControllerFromJava(JNIEnv* env, jobject obj)
{
return reinterpret_cast<ControllerEmu::EmulatedController*>(
env->GetLongField(obj, IDCache::GetEmulatedControllerPointer()));
}
static jobject EmulatedControllerToJava(JNIEnv* env, ControllerEmu::EmulatedController* controller)
{
if (!controller)
return nullptr;
return env->NewObject(IDCache::GetEmulatedControllerClass(),
IDCache::GetEmulatedControllerConstructor(),
reinterpret_cast<jlong>(controller));
}
extern "C" {
JNIEXPORT jstring JNICALL
Java_org_dolphinemu_dolphinemu_features_input_model_controlleremu_EmulatedController_getDefaultDevice(
JNIEnv* env, jobject obj)
{
return ToJString(env, EmulatedControllerFromJava(env, obj)->GetDefaultDevice().ToString());
}
JNIEXPORT void JNICALL
Java_org_dolphinemu_dolphinemu_features_input_model_controlleremu_EmulatedController_setDefaultDevice(
JNIEnv* env, jobject obj, jstring j_device)
{
return EmulatedControllerFromJava(env, obj)->SetDefaultDevice(GetJString(env, j_device));
}
JNIEXPORT jint JNICALL
Java_org_dolphinemu_dolphinemu_features_input_model_controlleremu_EmulatedController_getGroupCount(
JNIEnv* env, jobject obj)
{
return static_cast<jint>(EmulatedControllerFromJava(env, obj)->groups.size());
}
JNIEXPORT jobject JNICALL
Java_org_dolphinemu_dolphinemu_features_input_model_controlleremu_EmulatedController_getGroup(
JNIEnv* env, jobject obj, jint controller_index)
{
return ControlGroupToJava(env,
EmulatedControllerFromJava(env, obj)->groups[controller_index].get());
}
JNIEXPORT void JNICALL
Java_org_dolphinemu_dolphinemu_features_input_model_controlleremu_EmulatedController_updateSingleControlReference(
JNIEnv* env, jobject obj, jobject control_reference)
{
return EmulatedControllerFromJava(env, obj)->UpdateSingleControlReference(
g_controller_interface, ControlReferenceFromJava(env, control_reference));
}
JNIEXPORT void JNICALL
Java_org_dolphinemu_dolphinemu_features_input_model_controlleremu_EmulatedController_loadDefaultSettings(
JNIEnv* env, jobject obj)
{
ControllerEmu::EmulatedController* controller = EmulatedControllerFromJava(env, obj);
controller->LoadDefaults(g_controller_interface);
controller->UpdateReferences(g_controller_interface);
}
JNIEXPORT void JNICALL
Java_org_dolphinemu_dolphinemu_features_input_model_controlleremu_EmulatedController_clearSettings(
JNIEnv* env, jobject obj)
{
ControllerEmu::EmulatedController* controller = EmulatedControllerFromJava(env, obj);
// Loading an empty IniFile section clears everything.
IniFile::Section section;
controller->LoadConfig(&section);
controller->UpdateReferences(g_controller_interface);
}
JNIEXPORT void JNICALL
Java_org_dolphinemu_dolphinemu_features_input_model_controlleremu_EmulatedController_loadProfile(
JNIEnv* env, jobject obj, jstring j_path)
{
ControllerEmu::EmulatedController* controller = EmulatedControllerFromJava(env, obj);
IniFile ini;
ini.Load(GetJString(env, j_path));
controller->LoadConfig(ini.GetOrCreateSection("Profile"));
controller->UpdateReferences(g_controller_interface);
}
JNIEXPORT void JNICALL
Java_org_dolphinemu_dolphinemu_features_input_model_controlleremu_EmulatedController_saveProfile(
JNIEnv* env, jobject obj, jstring j_path)
{
const std::string path = GetJString(env, j_path);
File::CreateFullPath(path);
IniFile ini;
EmulatedControllerFromJava(env, obj)->SaveConfig(ini.GetOrCreateSection("Profile"));
ini.Save(path);
}
JNIEXPORT jobject JNICALL
Java_org_dolphinemu_dolphinemu_features_input_model_controlleremu_EmulatedController_getGcPad(
JNIEnv* env, jclass, jint controller_index)
{
return EmulatedControllerToJava(env, Pad::GetConfig()->GetController(controller_index));
}
JNIEXPORT jobject JNICALL
Java_org_dolphinemu_dolphinemu_features_input_model_controlleremu_EmulatedController_getWiimote(
JNIEnv* env, jclass, jint controller_index)
{
return EmulatedControllerToJava(env, Wiimote::GetConfig()->GetController(controller_index));
}
JNIEXPORT jobject JNICALL
Java_org_dolphinemu_dolphinemu_features_input_model_controlleremu_EmulatedController_getWiimoteAttachment(
JNIEnv* env, jclass, jint controller_index, jint attachment_index)
{
auto* attachments = static_cast<ControllerEmu::Attachments*>(
Wiimote::GetWiimoteGroup(controller_index, WiimoteEmu::WiimoteGroup::Attachments));
return EmulatedControllerToJava(env, attachments->GetAttachmentList()[attachment_index].get());
}
JNIEXPORT jint JNICALL
Java_org_dolphinemu_dolphinemu_features_input_model_controlleremu_EmulatedController_getSelectedWiimoteAttachment(
JNIEnv* env, jclass, jint controller_index)
{
auto* attachments = static_cast<ControllerEmu::Attachments*>(
Wiimote::GetWiimoteGroup(controller_index, WiimoteEmu::WiimoteGroup::Attachments));
return static_cast<jint>(attachments->GetSelectedAttachment());
}
JNIEXPORT jobject JNICALL
Java_org_dolphinemu_dolphinemu_features_input_model_controlleremu_EmulatedController_getSidewaysWiimoteSetting(
JNIEnv* env, jclass, jint controller_index)
{
ControllerEmu::ControlGroup* options =
Wiimote::GetWiimoteGroup(controller_index, WiimoteEmu::WiimoteGroup::Options);
for (auto& setting : options->numeric_settings)
{
if (setting->GetININame() == WiimoteEmu::Wiimote::SIDEWAYS_OPTION)
return NumericSettingToJava(env, setting.get());
}
return nullptr;
}
}

View File

@ -0,0 +1,13 @@
// Copyright 2022 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <jni.h>
namespace ControllerEmu
{
class ControlReference;
}
ControllerEmu::EmulatedController* EmulatedControllerFromJava(JNIEnv* env, jobject obj);

View File

@ -0,0 +1,79 @@
// Copyright 2022 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#include <chrono>
#include <string>
#include <vector>
#include <jni.h>
#include "Core/FreeLookManager.h"
#include "Core/HW/GBAPad.h"
#include "Core/HW/GCKeyboard.h"
#include "Core/HW/GCPad.h"
#include "Core/HW/Wiimote.h"
#include "Core/HotkeyManager.h"
#include "InputCommon/ControllerInterface/ControllerInterface.h"
#include "InputCommon/ControllerInterface/MappingCommon.h"
#include "jni/AndroidCommon/AndroidCommon.h"
#include "jni/Input/EmulatedController.h"
namespace
{
constexpr auto INPUT_DETECT_INITIAL_TIME = std::chrono::seconds(3);
constexpr auto INPUT_DETECT_CONFIRMATION_TIME = std::chrono::milliseconds(0);
constexpr auto INPUT_DETECT_MAXIMUM_TIME = std::chrono::seconds(5);
} // namespace
extern "C" {
JNIEXPORT jstring JNICALL
Java_org_dolphinemu_dolphinemu_features_input_model_MappingCommon_detectInput(
JNIEnv* env, jclass, jobject j_emulated_controller, jboolean all_devices)
{
ControllerEmu::EmulatedController* emulated_controller =
EmulatedControllerFromJava(env, j_emulated_controller);
const ciface::Core::DeviceQualifier default_device = emulated_controller->GetDefaultDevice();
std::vector<std::string> device_strings;
if (all_devices)
device_strings = g_controller_interface.GetAllDeviceStrings();
else
device_strings = {default_device.ToString()};
auto detections =
g_controller_interface.DetectInput(device_strings, INPUT_DETECT_INITIAL_TIME,
INPUT_DETECT_CONFIRMATION_TIME, INPUT_DETECT_MAXIMUM_TIME);
ciface::MappingCommon::RemoveSpuriousTriggerCombinations(&detections);
return ToJString(env, ciface::MappingCommon::BuildExpression(detections, default_device,
ciface::MappingCommon::Quote::On));
}
JNIEXPORT jstring JNICALL
Java_org_dolphinemu_dolphinemu_features_input_model_MappingCommon_getExpressionForControl(
JNIEnv* env, jclass, jstring j_control, jstring j_device, jstring j_default_device)
{
ciface::Core::DeviceQualifier device_qualifier, default_device_qualifier;
device_qualifier.FromString(GetJString(env, j_device));
default_device_qualifier.FromString(GetJString(env, j_default_device));
return ToJString(env, ciface::MappingCommon::GetExpressionForControl(GetJString(env, j_control),
device_qualifier,
default_device_qualifier));
}
JNIEXPORT void JNICALL
Java_org_dolphinemu_dolphinemu_features_input_model_MappingCommon_save(JNIEnv* env, jclass)
{
Pad::GetConfig()->SaveConfig();
Pad::GetGBAConfig()->SaveConfig();
Keyboard::GetConfig()->SaveConfig();
Wiimote::GetConfig()->SaveConfig();
HotkeyManagerEmu::GetConfig()->SaveConfig();
FreeLook::GetInputConfig()->SaveConfig();
}
};

View File

@ -0,0 +1,160 @@
// Copyright 2022 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "jni/Input/NumericSetting.h"
#include <string>
#include <jni.h>
#include "Common/Assert.h"
#include "Common/MsgHandler.h"
#include "InputCommon/ControlReference/ControlReference.h"
#include "InputCommon/ControllerEmu/Setting/NumericSetting.h"
#include "jni/AndroidCommon/AndroidCommon.h"
#include "jni/AndroidCommon/IDCache.h"
#include "jni/Input/ControlReference.h"
static const char* NullStringToEmptyString(const char* str)
{
return str ? str : "";
}
static ControllerEmu::NumericSettingBase* GetPointer(JNIEnv* env, jobject obj)
{
return reinterpret_cast<ControllerEmu::NumericSettingBase*>(
env->GetLongField(obj, IDCache::GetNumericSettingPointer()));
}
template <typename T>
static ControllerEmu::NumericSetting<T>* GetPointer(JNIEnv* env, jobject obj)
{
return reinterpret_cast<ControllerEmu::NumericSetting<T>*>(
env->GetLongField(obj, IDCache::GetNumericSettingPointer()));
}
jobject NumericSettingToJava(JNIEnv* env, ControllerEmu::NumericSettingBase* numeric_setting)
{
if (!numeric_setting)
return nullptr;
return env->NewObject(IDCache::GetNumericSettingClass(), IDCache::GetNumericSettingConstructor(),
reinterpret_cast<jlong>(numeric_setting));
}
extern "C" {
JNIEXPORT jstring JNICALL
Java_org_dolphinemu_dolphinemu_features_input_model_controlleremu_NumericSetting_getUiName(
JNIEnv* env, jobject obj)
{
return ToJString(env, Common::GetStringT(GetPointer(env, obj)->GetUIName()));
}
JNIEXPORT jstring JNICALL
Java_org_dolphinemu_dolphinemu_features_input_model_controlleremu_NumericSetting_getUiSuffix(
JNIEnv* env, jobject obj)
{
const char* str = NullStringToEmptyString(GetPointer(env, obj)->GetUISuffix());
return ToJString(env, Common::GetStringT(str));
}
JNIEXPORT jstring JNICALL
Java_org_dolphinemu_dolphinemu_features_input_model_controlleremu_NumericSetting_getUiDescription(
JNIEnv* env, jobject obj)
{
const char* str = NullStringToEmptyString(GetPointer(env, obj)->GetUIDescription());
return ToJString(env, Common::GetStringT(str));
}
JNIEXPORT jint JNICALL
Java_org_dolphinemu_dolphinemu_features_input_model_controlleremu_NumericSetting_getType(
JNIEnv* env, jobject obj)
{
return static_cast<int>(GetPointer(env, obj)->GetType());
}
JNIEXPORT jobject JNICALL
Java_org_dolphinemu_dolphinemu_features_input_model_controlleremu_NumericSetting_getControlReference(
JNIEnv* env, jobject obj)
{
return ControlReferenceToJava(env, &GetPointer(env, obj)->GetInputReference());
}
JNIEXPORT jint JNICALL
Java_org_dolphinemu_dolphinemu_features_input_model_controlleremu_NumericSetting_getIntValue(
JNIEnv* env, jobject obj)
{
return GetPointer<int>(env, obj)->GetValue();
}
JNIEXPORT void JNICALL
Java_org_dolphinemu_dolphinemu_features_input_model_controlleremu_NumericSetting_setIntValue(
JNIEnv* env, jobject obj, jint value)
{
GetPointer<int>(env, obj)->SetValue(value);
}
JNIEXPORT jint JNICALL
Java_org_dolphinemu_dolphinemu_features_input_model_controlleremu_NumericSetting_getIntDefaultValue(
JNIEnv* env, jobject obj)
{
return GetPointer<int>(env, obj)->GetDefaultValue();
}
JNIEXPORT jdouble JNICALL
Java_org_dolphinemu_dolphinemu_features_input_model_controlleremu_NumericSetting_getDoubleValue(
JNIEnv* env, jobject obj)
{
return GetPointer<double>(env, obj)->GetValue();
}
JNIEXPORT void JNICALL
Java_org_dolphinemu_dolphinemu_features_input_model_controlleremu_NumericSetting_setDoubleValue(
JNIEnv* env, jobject obj, jdouble value)
{
GetPointer<double>(env, obj)->SetValue(value);
}
JNIEXPORT jdouble JNICALL
Java_org_dolphinemu_dolphinemu_features_input_model_controlleremu_NumericSetting_getDoubleDefaultValue(
JNIEnv* env, jobject obj)
{
return GetPointer<double>(env, obj)->GetDefaultValue();
}
JNIEXPORT jdouble JNICALL
Java_org_dolphinemu_dolphinemu_features_input_model_controlleremu_NumericSetting_getDoubleMin(
JNIEnv* env, jobject obj)
{
return GetPointer<double>(env, obj)->GetMinValue();
}
JNIEXPORT jdouble JNICALL
Java_org_dolphinemu_dolphinemu_features_input_model_controlleremu_NumericSetting_getDoubleMax(
JNIEnv* env, jobject obj)
{
return GetPointer<double>(env, obj)->GetMaxValue();
}
JNIEXPORT jboolean JNICALL
Java_org_dolphinemu_dolphinemu_features_input_model_controlleremu_NumericSetting_getBooleanValue(
JNIEnv* env, jobject obj)
{
return static_cast<jboolean>(GetPointer<bool>(env, obj)->GetValue());
}
JNIEXPORT void JNICALL
Java_org_dolphinemu_dolphinemu_features_input_model_controlleremu_NumericSetting_setBooleanValue(
JNIEnv* env, jobject obj, jboolean value)
{
GetPointer<bool>(env, obj)->SetValue(static_cast<bool>(value));
}
JNIEXPORT jboolean JNICALL
Java_org_dolphinemu_dolphinemu_features_input_model_controlleremu_NumericSetting_getBooleanDefaultValue(
JNIEnv* env, jobject obj)
{
return static_cast<jboolean>(GetPointer<bool>(env, obj)->GetDefaultValue());
}
}

View File

@ -0,0 +1,15 @@
// Copyright 2022 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <jni.h>
namespace ControllerEmu
{
class NumericSettingBase;
}
class ControlReference;
jobject NumericSettingToJava(JNIEnv* env, ControllerEmu::NumericSettingBase* control);

View File

@ -52,8 +52,6 @@
#include "DiscIO/ScrubbedBlob.h"
#include "DiscIO/Volume.h"
#include "InputCommon/ControllerInterface/Android/Android.h"
#include "InputCommon/ControllerInterface/Touch/ButtonManager.h"
#include "InputCommon/GCAdapter.h"
#include "UICommon/GameFile.h"
@ -291,24 +289,6 @@ Java_org_dolphinemu_dolphinemu_NativeLibrary_IsRunningAndUnpaused(JNIEnv*, jclas
return static_cast<jboolean>(Core::GetState() == Core::State::Running);
}
JNIEXPORT jboolean JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_onGamePadEvent(
JNIEnv* env, jclass, jstring jDevice, jint Button, jint Action)
{
return ButtonManager::GamepadEvent(GetJString(env, jDevice), Button, Action);
}
JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_onGamePadMoveEvent(
JNIEnv* env, jclass, jstring jDevice, jint Axis, jfloat Value)
{
ButtonManager::GamepadAxisEvent(GetJString(env, jDevice), Axis, Value);
}
JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_SetMotionSensorsEnabled(
JNIEnv*, jclass, jboolean accelerometer_enabled, jboolean gyroscope_enabled)
{
ciface::Android::SetMotionSensorsEnabled(accelerometer_enabled, gyroscope_enabled);
}
JNIEXPORT jstring JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_GetVersionString(JNIEnv* env,
jclass)
{
@ -376,12 +356,6 @@ JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_utils_DirectoryInitializat
File::SetSysDirectory(path);
}
JNIEXPORT void JNICALL
Java_org_dolphinemu_dolphinemu_utils_DirectoryInitialization_CreateUserDirectories(JNIEnv*, jclass)
{
UICommon::CreateDirectories();
}
JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_SetUserDirectory(
JNIEnv* env, jclass, jstring jDirectory)
{
@ -505,12 +479,6 @@ JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_RefreshWiimo
WiimoteReal::Refresh();
}
JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_ReloadWiimoteConfig(JNIEnv*,
jclass)
{
Wiimote::LoadConfig();
}
JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_ReloadConfig(JNIEnv*, jclass)
{
SConfig::GetInstance().LoadSettings();
@ -531,6 +499,7 @@ Java_org_dolphinemu_dolphinemu_NativeLibrary_UpdateGCAdapterScanThread(JNIEnv*,
JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_Initialize(JNIEnv*, jclass)
{
UICommon::CreateDirectories();
Common::RegisterMsgAlertHandler(&MsgAlert);
Common::AndroidSetReportHandler(&ReportSend);
DolphinAnalytics::AndroidSetGetValFunc(&GetAnalyticValue);
@ -584,8 +553,6 @@ static void Run(JNIEnv* env, std::unique_ptr<BootParameters>&& boot, bool riivol
if (BootManager::BootCore(std::move(boot), wsi))
{
ButtonManager::Init(SConfig::GetInstance().GetGameID());
static constexpr int WAIT_STEP = 25;
while (Core::GetState() == Core::State::Starting)
std::this_thread::sleep_for(std::chrono::milliseconds(WAIT_STEP));
@ -605,7 +572,6 @@ static void Run(JNIEnv* env, std::unique_ptr<BootParameters>&& boot, bool riivol
s_game_metadata_is_valid = false;
Core::Shutdown();
ButtonManager::Shutdown();
host_identity_guard.unlock();
env->CallStaticVoidMethod(IDCache::GetNativeLibraryClass(),

View File

@ -18,126 +18,13 @@ bool IsSettingSaveable(const Config::Location& config_location)
{
for (Config::System system :
{Config::System::SYSCONF, Config::System::GFX, Config::System::DualShockUDPClient,
Config::System::Logger, Config::System::FreeLook})
Config::System::Logger, Config::System::FreeLook, Config::System::Main})
{
if (config_location.system == system)
return true;
}
if (config_location.system == Config::System::Main)
{
for (const std::string_view section :
{"NetPlay", "General", "GBA", "Display", "Network", "Analytics", "AndroidOverlayButtons",
"DSP", "GameList", "FifoPlayer", "AutoUpdate", "Movie", "Input", "Debug",
"BluetoothPassthrough", "USBPassthrough", "Interface", "EmulatedUSBDevices"})
{
if (config_location.section == section)
return true;
}
// Android controller mappings are not saveable, other Android settings are.
// TODO: Kill the current Android controller mappings system
if (config_location.section == "Android")
{
static constexpr std::array<const char*, 14> android_setting_saveable = {
"ControlScale", "ControlOpacity", "EmulationOrientation",
"JoystickRelCenter", "LastPlatformTab", "MotionControls",
"PhoneRumble", "ShowInputOverlay", "IRMode",
"IRAlwaysRecenter", "ShowGameTitles", "InterfaceTheme",
"InterfaceThemeMode", "UseBlackBackgrounds"};
return std::any_of(
android_setting_saveable.cbegin(), android_setting_saveable.cend(),
[&config_location](const char* key) { return key == config_location.key; });
}
}
static const auto s_setting_saveable = {
// Main.Core
&Config::MAIN_DEFAULT_ISO.GetLocation(),
&Config::MAIN_ENABLE_CHEATS.GetLocation(),
&Config::MAIN_MEMCARD_A_PATH.GetLocation(),
&Config::MAIN_MEMCARD_B_PATH.GetLocation(),
&Config::MAIN_AUTO_DISC_CHANGE.GetLocation(),
&Config::MAIN_ALLOW_SD_WRITES.GetLocation(),
&Config::MAIN_DPL2_DECODER.GetLocation(),
&Config::MAIN_DPL2_QUALITY.GetLocation(),
&Config::MAIN_AUDIO_LATENCY.GetLocation(),
&Config::MAIN_AUDIO_STRETCH.GetLocation(),
&Config::MAIN_AUDIO_STRETCH_LATENCY.GetLocation(),
&Config::MAIN_OVERCLOCK.GetLocation(),
&Config::MAIN_OVERCLOCK_ENABLE.GetLocation(),
&Config::MAIN_RAM_OVERRIDE_ENABLE.GetLocation(),
&Config::MAIN_MEM1_SIZE.GetLocation(),
&Config::MAIN_MEM2_SIZE.GetLocation(),
&Config::MAIN_GFX_BACKEND.GetLocation(),
&Config::MAIN_ENABLE_SAVESTATES.GetLocation(),
&Config::MAIN_FALLBACK_REGION.GetLocation(),
&Config::MAIN_REAL_WII_REMOTE_REPEAT_REPORTS.GetLocation(),
&Config::MAIN_DSP_HLE.GetLocation(),
&Config::MAIN_CPU_CORE.GetLocation(),
&Config::MAIN_SKIP_IPL.GetLocation(),
&Config::MAIN_GC_LANGUAGE.GetLocation(),
&Config::MAIN_AGP_CART_A_PATH.GetLocation(),
&Config::MAIN_AGP_CART_B_PATH.GetLocation(),
&Config::MAIN_BBA_MAC.GetLocation(),
&Config::MAIN_BBA_XLINK_IP.GetLocation(),
&Config::MAIN_BBA_BUILTIN_DNS.GetLocation(),
&Config::MAIN_BBA_BUILTIN_IP.GetLocation(),
&Config::MAIN_BBA_XLINK_CHAT_OSD.GetLocation(),
&Config::MAIN_OVERRIDE_REGION_SETTINGS.GetLocation(),
&Config::MAIN_CUSTOM_RTC_ENABLE.GetLocation(),
&Config::MAIN_CUSTOM_RTC_VALUE.GetLocation(),
&Config::MAIN_JIT_FOLLOW_BRANCH.GetLocation(),
&Config::MAIN_FLOAT_EXCEPTIONS.GetLocation(),
&Config::MAIN_DIVIDE_BY_ZERO_EXCEPTIONS.GetLocation(),
&Config::MAIN_LOW_DCBZ_HACK.GetLocation(),
&Config::MAIN_FPRF.GetLocation(),
&Config::MAIN_ACCURATE_NANS.GetLocation(),
&Config::GetInfoForAdapterRumble(0).GetLocation(),
&Config::GetInfoForAdapterRumble(1).GetLocation(),
&Config::GetInfoForAdapterRumble(2).GetLocation(),
&Config::GetInfoForAdapterRumble(3).GetLocation(),
&Config::GetInfoForSimulateKonga(0).GetLocation(),
&Config::GetInfoForSimulateKonga(1).GetLocation(),
&Config::GetInfoForSimulateKonga(2).GetLocation(),
&Config::GetInfoForSimulateKonga(3).GetLocation(),
&Config::MAIN_EMULATION_SPEED.GetLocation(),
&Config::MAIN_PERF_MAP_DIR.GetLocation(),
&Config::MAIN_GPU_DETERMINISM_MODE.GetLocation(),
&Config::MAIN_DISABLE_ICACHE.GetLocation(),
&Config::MAIN_FAST_DISC_SPEED.GetLocation(),
&Config::MAIN_SYNC_ON_SKIP_IDLE.GetLocation(),
&Config::MAIN_FASTMEM.GetLocation(),
&Config::MAIN_TIMING_VARIANCE.GetLocation(),
&Config::MAIN_MAX_FALLBACK.GetLocation(),
&Config::MAIN_WII_SD_CARD.GetLocation(),
&Config::MAIN_WII_SD_CARD_ENABLE_FOLDER_SYNC.GetLocation(),
&Config::MAIN_WII_KEYBOARD.GetLocation(),
&Config::MAIN_WIIMOTE_CONTINUOUS_SCANNING.GetLocation(),
&Config::MAIN_WIIMOTE_ENABLE_SPEAKER.GetLocation(),
&Config::MAIN_CONNECT_WIIMOTES_FOR_CONTROLLER_INTERFACE.GetLocation(),
&Config::MAIN_SLOT_A.GetLocation(),
&Config::MAIN_SLOT_B.GetLocation(),
&Config::MAIN_SERIAL_PORT_1.GetLocation(),
&Config::GetInfoForSIDevice(0).GetLocation(),
&Config::GetInfoForSIDevice(1).GetLocation(),
&Config::GetInfoForSIDevice(2).GetLocation(),
&Config::GetInfoForSIDevice(3).GetLocation(),
&Config::MAIN_CPU_THREAD.GetLocation(),
&Config::MAIN_MMU.GetLocation(),
&Config::MAIN_PAUSE_ON_PANIC.GetLocation(),
&Config::MAIN_ACCURATE_CPU_CACHE.GetLocation(),
&Config::MAIN_BB_DUMP_PORT.GetLocation(),
&Config::MAIN_SYNC_GPU.GetLocation(),
&Config::MAIN_SYNC_GPU_MAX_DISTANCE.GetLocation(),
&Config::MAIN_SYNC_GPU_MIN_DISTANCE.GetLocation(),
&Config::MAIN_SYNC_GPU_OVERCLOCK.GetLocation(),
&Config::MAIN_OVERRIDE_BOOT_IOS.GetLocation(),
&Config::MAIN_GCI_FOLDER_A_PATH.GetLocation(),
&Config::MAIN_GCI_FOLDER_B_PATH.GetLocation(),
// UI.General
&Config::MAIN_USE_DISCORD_PRESENCE.GetLocation(),

Some files were not shown because too many files have changed in this diff Show More