Compare commits

..

734 Commits

Author SHA1 Message Date
Matías Locatti
ed93cbd462 Blacklist EDS3 blending from new AMD drivers 2023-06-28 20:10:27 -03:00
liamwhite
b60b70e86d Merge pull request #10837 from liamwhite/mali-support
android: Mali support
2023-06-28 12:53:17 -04:00
GPUCode
ddcc958336 renderer_vulkan: Prevent crashes when blitting depth stencil 2023-06-27 18:00:09 -07:00
GPUCode
eac46ad7ce video_core: Add BCn decoding support 2023-06-27 18:00:09 -07:00
GPUCode
b8c96cee5f renderer_vulkan: Add more feature checking 2023-06-27 18:00:09 -07:00
GPUCode
220a42896d renderer_vulkan: Don't assume debug tool with debug renderer
* Causes crashes because mali drivers don't support debug utils
2023-06-27 18:00:09 -07:00
GPUCode
1522b95658 renderer_vulkan: Bump minimum SPIRV version
* 1.3 is guaranteed on all 1.1 drivers
2023-06-27 18:00:09 -07:00
GPUCode
c339af37a7 renderer_vulkan: Respect viewport limit 2023-06-27 18:00:09 -07:00
GPUCode
a9b44d37e1 renderer_vulkan: Don't add transform feedback flag if unsupported 2023-06-27 18:00:09 -07:00
GPUCode
72e7f5b4dd renderer_vulkan: Add suport for debug report callback 2023-06-27 18:00:09 -07:00
liamwhite
0fe44071f8 Merge pull request #10933 from merryhime/dunno
arm_dynarmic_32: Remove disabling of block linking on arm64
2023-06-27 20:50:24 -04:00
liamwhite
e0d406042c Merge pull request #10932 from abouvier/git-indent
gitmodules: normalize indentation and url
2023-06-27 20:50:16 -04:00
liamwhite
0b2798be27 Merge pull request #10930 from lat9nq/msvc-inconsistent-time-zones
settings: Catch runtime_error, fallback time zone
2023-06-27 20:50:06 -04:00
lat9nq
21675c9b68 settings: Clean up includes
Adds <version> since we are looking at C++ implementation version
details. Also moves exception header includes into the if preprocessor
command since we only use it there.
2023-06-27 19:13:54 -04:00
Merry
e3c548d081 arm_dynarmic_32: Remove disabling of block linking on arm64 2023-06-27 23:51:49 +01:00
lat9nq
32475efbc4 settings: Catch runtime_error, fallback time zone
Windows will let you select time zones that  will fail in their
own C++ implementation library. Evidently from the stack trace, we get a
runtime error to work with, so catch it and use the fallback.
2023-06-27 18:12:26 -04:00
Morph
bd4fefe5e7 Merge pull request #10931 from german77/clang
yuzu: Fix clang format
2023-06-27 18:11:22 -04:00
german77
30544fbfa5 yuzu: Fix clang format 2023-06-27 15:55:23 -06:00
Alexandre Bouvier
33cd3a0db0 gitmodules: normalize indentation and url 2023-06-27 23:39:29 +02:00
Narr the Reg
112b660456 Merge pull request #9663 from EBADBEEF/disable-controller-applet
qt: add option to disable controller applet
2023-06-27 10:32:51 -06:00
liamwhite
2b3bfafb9e Merge pull request #10867 from Kelebek1/dma_safe
Use safe reads in DMA engine
2023-06-27 11:21:47 -04:00
liamwhite
c6959449d1 Merge pull request #10473 from GPUCode/vma
Use vulkan memory allocator
2023-06-27 11:21:36 -04:00
liamwhite
20111c86b6 Merge pull request #10495 from bm01/master
input_common: Redesign mouse panning
2023-06-27 11:21:28 -04:00
liamwhite
f254ce2c60 Merge pull request #10679 from zeltermann/wakelock-reason
Only use SDL wakelock on Linux
2023-06-27 11:21:20 -04:00
liamwhite
dafbc86366 Merge pull request #10916 from ameerj/lolmem
OpenGL: Add Local Memory warmup shader for Nvidia
2023-06-27 11:21:10 -04:00
liamwhite
02882d9251 Merge pull request #10925 from t895/fs-agony
android: Fix size check for content uris
2023-06-26 23:33:51 -04:00
Charles Lombardo
9074a70b01 android: Fix size check for content uris
Fix for checking file size for android content uris
2023-06-26 22:24:18 -04:00
liamwhite
2ed8b3cbb9 Merge pull request #10908 from kiri11/clarify-ring-ui
Clarify Ring-Con configuration message in UI
2023-06-26 14:31:30 -04:00
liamwhite
b82c649b0f Merge pull request #10903 from german77/nfc_state
input_common: Improve nfc state handling and 3rd party support
2023-06-26 14:31:23 -04:00
liamwhite
ce990adae5 Merge pull request #10901 from german77/sdl_fix
input_common: Make use of new SDL features
2023-06-26 14:31:14 -04:00
liamwhite
c280a2fe1c Merge pull request #10894 from lat9nq/bsd-tzdb
nx_tzdb: Update tzdb_to_nx
2023-06-26 14:31:07 -04:00
liamwhite
28df6ac0aa Merge pull request #10888 from 8bitDream/native
android: (native) Parameter types from Android Studio
2023-06-26 14:31:00 -04:00
liamwhite
0030fa9721 Merge pull request #10865 from t895/extension-meme
android: Clean up file extension checks
2023-06-26 14:30:49 -04:00
Charles Lombardo
0f31039831 android: Clean up file extension checks 2023-06-26 13:25:56 -04:00
GPUCode
b6c6dcc576 externals: Use cmake subdirectory 2023-06-26 18:59:24 +03:00
Kelebek1
ffbaf574ca Use safe reads in DMA engine 2023-06-26 11:34:02 +01:00
ameerj
4f160633d3 OpenGL: Limit lmem warmup to NVIDIA
🐸
2023-06-25 19:06:51 -04:00
ameerj
405eae3734 shaders: Track local memory usage 2023-06-25 18:59:33 -04:00
ameerj
b198339580 emit_glasm: Fix lmem size computation 2023-06-25 18:43:52 -04:00
ameerj
82107b33a2 OpenGL: Add Local Memory warmup shader 2023-06-25 18:43:23 -04:00
lat9nq
f5569bfed9 nx_tzdb: Update tzdb_to_nx to 212afa2
Moves build data to a separate directory so the build happens out of the source
tree.
2023-06-25 17:20:18 -04:00
Kirill Ignatev
b6e89bdf2c Hyphenate Joy-Con and clarify further 2023-06-25 12:51:16 -04:00
Kirill Ignatev
b6025cf62f Clarify Ring-Con configuration message in UI
Not obvious how left controller should be set up
Mention that it should be left physical dual emulated
2023-06-25 11:52:15 -04:00
german77
bf641e2964 core: hid: Allow to read bin files while switch controller is available 2023-06-24 18:59:55 -06:00
german77
5aa208e264 input_common: Dont try to read/write data from 3rd party controllers 2023-06-24 18:59:55 -06:00
german77
ec9a71b12a externals: Include player led fix on SDL 2023-06-24 17:43:08 -06:00
german77
474fa13a1a input_common: Make use of new SDL features 2023-06-24 17:42:56 -06:00
zeltermann
482fbded9b Only use SDL wakelock on Linux
SDL has internally fixed shenanigans related to wakelocking through DBus
from inside sandboxes from around August 2022, so we can now remove the
workaround we used since 2021.
2023-06-24 14:51:41 +07:00
lat9nq
e5769e9467 nx_tzdb: Update tzdb_to_nx
Includes fixes for other BSD's, and axes shell scripts for pure CMake.
2023-06-23 19:07:26 -04:00
Morph
3a991f3aef Merge pull request #10891 from german77/sdl28v2
externals: Include post release SDL fixes
2023-06-23 17:41:54 -04:00
Narr the Reg
142c1b72f9 externals: Include post release SDL fixes 2023-06-23 12:25:47 -06:00
Abandoned Cart
b53945a99f android: define [[maybe_unused]] (const) auto 2023-06-23 14:05:14 -04:00
Abandoned Cart
a58a1403ba android: Parameter types from Android Studio
Android Studio marked these parameters as errors because it is an instance, not a class, that is being passed from Java.
2023-06-23 10:36:30 -04:00
liamwhite
5ab4aa1edb Merge pull request #10811 from 8bitDream/pip_mute
android: Add a PiP interface to mute / unmute
2023-06-23 09:27:28 -04:00
liamwhite
a674022434 Merge pull request #10859 from liamwhite/no-more-atomic-wait
general: remove atomic signal and wait
2023-06-23 09:27:14 -04:00
liamwhite
87b9b5d10f Merge pull request #10842 from german77/native_mifare
input_common: Implement native mifare/skylander support for joycons/pro controller
2023-06-23 09:27:00 -04:00
liamwhite
575d467d95 Merge pull request #10884 from liamwhite/spaghetti-vfs
vfs_real: lock concurrent accesses
2023-06-23 09:26:48 -04:00
Liam
1dd166f766 vfs_real: lock concurrent accesses 2023-06-23 00:57:24 -04:00
bunnei
2fc5dedf69 Merge pull request #10457 from Kelebek1/optimise
Remove memory allocations in some hot paths
2023-06-22 21:53:07 -07:00
bunnei
3f3e4efb30 Merge pull request #10806 from liamwhite/worst-fs-implementation-ever
vfs_real: misc optimizations
2023-06-22 21:46:50 -07:00
bunnei
5558fc4aa5 Merge pull request #10794 from 8bitDream/multiples
android: Add support for multiple installs
2023-06-22 14:26:24 -07:00
Morph
7f12c6159f Merge pull request #10878 from GPUCode/log-droid
android: Log settings
2023-06-22 16:16:20 -04:00
bunnei
8e9c6429b0 Merge pull request #10873 from german77/sdl
externals: Update sdl to 2.28.0
2023-06-22 10:27:01 -07:00
bunnei
dfa0e9479d Merge pull request #10869 from 8bitDream/memory
android: Add a notice when RAM inadequate
2023-06-22 10:26:32 -07:00
GPUCode
c133509368 android: Log settings 2023-06-22 20:17:52 +03:00
GPUCode
75fb29e08e vulkan_common: Remove required flags
* Allows VMA to fallback to system RAM instead of crashing
2023-06-22 20:03:12 +03:00
Liam
1586f1c0b1 general: remove atomic signal and wait 2023-06-22 09:25:23 -04:00
Kelebek1
5da70f7197 Remove memory allocations in some hot paths 2023-06-22 08:05:10 +01:00
bunnei
e3122c5b46 Merge pull request #10086 from Morph1984/coretiming-ng-1
core_timing: Use CNTPCT as the guest CPU tick
2023-06-21 21:12:46 -07:00
bunnei
7eb7d56b1b Merge pull request #10777 from liamwhite/no-barrier
video_core: optionally skip barriers on feedback loops
2023-06-21 21:10:08 -07:00
bunnei
8cb6b33809 Merge pull request #10841 from liamwhite/math-is-hard
vfs_concat: fix offset calculation when not aligned to file boundary
2023-06-21 21:07:08 -07:00
bunnei
8ad64bc253 Merge pull request #10863 from lat9nq/tz-end-of-string
time_zone_manager: Stop on comma
2023-06-21 21:05:03 -07:00
Narr the Reg
84d43489c5 input_common: Implement native mifare support 2023-06-21 17:54:58 -06:00
Narr the Reg
106b61b1e0 externals: Update sdl to 2.28.0 2023-06-21 17:11:14 -06:00
Abandoned Cart
1a85d8804a android: Generalize string message dialog 2023-06-21 18:25:15 -04:00
Abandoned Cart
6c7e284f64 android: Add support for concurrent installs 2023-06-21 18:25:15 -04:00
Abandoned Cart
8b841aa7ba android: Convert memory sizes to resource 2023-06-21 18:24:49 -04:00
Abandoned Cart
699e78c666 android: Add a notice when RAM inadequate 2023-06-21 18:24:49 -04:00
Abandoned Cart
cfc6ef42d9 android: Refactor native and corresponding variables 2023-06-21 18:23:13 -04:00
Abandoned Cart
e35371e50c Fix JNI and expose mute settings to Android 2023-06-21 18:23:13 -04:00
Abandoned Cart
e31152ee34 android: Add a PiP interface to mute / unmute 2023-06-21 17:21:36 -04:00
liamwhite
eea2145698 Merge pull request #10864 from t895/disable-mali-driver
android: Don't show custom driver button on mali and x86
2023-06-21 16:50:30 -04:00
Charles Lombardo
e684515578 android: Don't show custom driver button on mali and x86 2023-06-20 20:06:36 -04:00
lat9nq
ae1a8a7dc7 time_zone_manager: Add null terminator
We aren't null-terminating this string after the copy, and we need to.
2023-06-20 15:54:28 -04:00
lat9nq
fd5d7947f6 time_zone_manager: Stop on comma
This is a deviation from the reference time zone implementation. The
actual code will set a pointer to the time zone name here, but for us we
have a limited number of characters to work with, and the name of the
time zone here could be larger than 8 characters.

We can make the assumption that time zone names greater than five
characters in length include a comma that denotes more data. Nintendo
just truncates that data for the name, so we can do the same.

time_zone_manager: Check for length of array

Just to be double sure that we never break past the array length,
directly compare against it.
2023-06-20 15:54:05 -04:00
bunnei
a67bdeb2c2 Merge pull request #10853 from lat9nq/update_tzdb_to_nx
externals: Update tzdb_to_nx
2023-06-20 10:42:54 -07:00
liamwhite
f1e12e3b08 Merge pull request #10818 from vonchenplus/render_target_samples
video_core: add samples check when find render target
2023-06-20 09:55:23 -04:00
liamwhite
93061d1ea1 Merge pull request #10835 from lat9nq/intel-restrict-compute-disable
vulkan_device: Restrict compute disable only to affected Intel drivers
2023-06-20 09:55:14 -04:00
liamwhite
6d12e7320b Merge pull request #10840 from Kelebek1/unbug_blinks_brain
Use current GPU address when unmapping GPU pages, not the base
2023-06-20 09:55:01 -04:00
toast2903
78ff2862f6 vulkan_device: Remove brace initializer
Co-authored-by: Tobias <thm.frey@gmail.com>
2023-06-19 17:35:12 -04:00
lat9nq
197e13d93d video_core: Check broken compute earlier
Checks it as the system is determining what settings to enable. Reduces
the need to check settings while the system is running.
2023-06-19 17:33:30 -04:00
lat9nq
bedb5135c0 nx_tzdb: Rename GNU_DATE variable
The repository can handle either GNU date or Apple date now.
2023-06-19 15:30:11 -04:00
lat9nq
256c7ec0a7 externals: Update tzdb_to_nx
Includes a fix for the Apple date utility.
2023-06-19 15:27:54 -04:00
Liam
e5f1b22e16 vfs_concat: verify short read 2023-06-19 09:47:05 -04:00
Liam
b0beca52a3 vfs_concat: fix offset calculation when not aligned to file boundary 2023-06-18 22:21:29 -04:00
Kelebek1
711190bb67 Use current GPU address when unmapping GPU pages, not the base 2023-06-19 00:19:50 +01:00
lat9nq
b9a86b040b vk_device_info: Check only affected Intel drivers
Renames is_intel_proprietary to has_broken_compute for accuracy.

vk_device_info: Use vulkan::device to check compute
2023-06-18 16:15:51 -04:00
lat9nq
346c253cd2 video_core: Formalize HasBrokenCompute
Also limits it to only affected Intel proprietrary driver versions.

vulkan_device: Move broken compute determination

vk_device: Remove errant back quote
2023-06-18 16:15:47 -04:00
liamwhite
ce191ba32b Merge pull request #10825 from 8bitDream/vcpkg-zlib
externals: Update vcpkg to 2023.06.17
2023-06-18 09:43:12 -04:00
liamwhite
23371fa187 Merge pull request #10829 from lat9nq/remove-external-mem
vulkan_device: Remove external memory extension
2023-06-18 09:43:03 -04:00
liamwhite
af7f3f078c Merge pull request #10486 from lat9nq/vk-device-find-once
yuzu-qt: Load Vulkan device info at startup
2023-06-18 09:42:55 -04:00
liamwhite
66b8042b59 Merge pull request #10798 from vonchenplus/draw_texture_scale
video_core: drawtexture support upscale
2023-06-18 09:42:41 -04:00
liamwhite
8acf728d5d Merge pull request #10809 from Kelebek1/reduce_vertex_bindings
Synchronize vertex buffer even when it doesn't require binding
2023-06-18 09:42:32 -04:00
GPUCode
ee0d68300e renderer_vulkan: Add missing initializers 2023-06-18 14:14:03 +03:00
GPUCode
7b2f680468 renderer_vulkan: Use VMA for buffers 2023-06-18 12:45:18 +03:00
GPUCode
48e39756f1 renderer_vulkan: Use VMA for images 2023-06-18 12:45:18 +03:00
GPUCode
c60eed36b7 memory_allocator: Remove OpenGL interop
* Appears to be unused atm
2023-06-18 12:45:18 +03:00
lat9nq
6448eade2e externals: Add vma and initialize it
video_core: Move vma implementation to library
2023-06-18 12:45:12 +03:00
bunnei
6e293be20b Merge pull request #10797 from lat9nq/tzdb-patch
time: Various time zone fixes
2023-06-17 23:47:16 -07:00
bunnei
20db91f0fc Merge pull request #10828 from liamwhite/somehow-still-using-llvm-14
renderer_vulkan: add missing include
2023-06-17 23:45:44 -07:00
lat9nq
8a526b2c26 vulkan_device: Remove external memory extension
Unused in yuzu. Enables yuzu to boot games in Wine using Vulkan.
2023-06-18 01:20:08 -04:00
Liam
565a1226d7 renderer_vulkan: add missing include 2023-06-17 23:57:47 -04:00
Abandoned Cart
fd0ef5411c externals: Update vcpkg to 2023.06.17
Fixes for zlib and qt5
2023-06-17 21:46:09 -04:00
lat9nq
b99c4dd568 time_zone_service: Always write time zone rule data
Switch firmware will initialize this data even if the given parameters
are invalid. We should do the same.
2023-06-17 20:53:39 -04:00
Morph
c0fd793ef6 Merge pull request #10813 from lat9nq/no-atomic-bool
k_thread: Use a mutex and cond_var to sync bool
2023-06-17 20:29:57 -04:00
Fernando S
27a36cd51b Merge pull request #10744 from Wollnashorn/af-for-all
video_core: Improved anisotropic filtering heuristics
2023-06-18 00:02:05 +02:00
Kelebek1
e681f5678c Synchronize vertex buffer even when it doesn't require binding 2023-06-17 17:47:00 -04:00
lat9nq
e34e1b1c95 k_thread: Use a mutex and cond_var to sync bool
std::atomic<bool> is broken on MinGW and causes deadlocks there.
Use a normal cond var in its stead.
2023-06-17 15:25:36 -04:00
FengChen
76a676883a video_core: add samples check when find render target 2023-06-17 23:48:51 +08:00
Wollnashorn
3e47ebe2e9 video_core: Only apply AF to 2D (array) image types 2023-06-17 14:20:44 +02:00
Wollnashorn
c309a1c69b video_core: Removed AF for all mip modes option as it's default now 2023-06-17 11:19:39 +02:00
lat9nq
4cbdce17b6 nx_tzdb: Directly reference variables in if statements
Addresses review feedback.
2023-06-17 01:48:46 -04:00
bunnei
ec423c6919 Merge pull request #10783 from liamwhite/memory
video_core: preallocate fewer IR blocks
2023-06-16 16:53:25 -07:00
bunnei
24e1e4dcee Merge pull request #10808 from t895/settings-stuffs
android: Expose settings
2023-06-16 16:52:54 -07:00
bunnei
975122f4bb Merge pull request #10807 from t895/ktlint-fixes
android: Ktlint fixes
2023-06-16 16:47:14 -07:00
liamwhite
a1adcc31d3 Merge pull request #10731 from german77/misc_fixes
service: nfc: Accuracy fixes
2023-06-16 18:18:24 -04:00
Liam
94e7cb05da vfs_real: ensure size cache is reset on write 2023-06-16 16:43:14 -04:00
Charles Lombardo
9f92104f3e android: Expose audio output engine setting 2023-06-16 16:42:56 -04:00
Charles Lombardo
330358cd16 android: Bump ktlint version to 0.47.1 2023-06-16 16:32:08 -04:00
Charles Lombardo
fc6a2fe779 android: Disable import-ordering ktlint check 2023-06-16 16:31:49 -04:00
Liam
bf47f777b1 patch_manager: remove unnecessary GetSize calls 2023-06-16 16:29:10 -04:00
Liam
734242c5bc vfs_real: misc optimizations 2023-06-16 16:29:06 -04:00
Charles Lombardo
13a4de647d android: Expose CPU debugging option 2023-06-16 16:25:06 -04:00
Charles Lombardo
3ac2c74e85 android: Expose fastmem option 2023-06-16 16:24:40 -04:00
Charles Lombardo
5aca03d0ff android: Support changing multiple settings at once 2023-06-16 15:49:49 -04:00
bunnei
4112031c81 Merge pull request #10801 from 8bitDream/fix_aspect
android: Fix aspect ratio when rotating screen
2023-06-16 11:45:32 -07:00
Abandoned Cart
c89be0dfab android: Fix aspect ratio when rotating screen 2023-06-16 10:11:18 -04:00
Feng Chen
b77a247e8c video_core: drawtexture support upscale 2023-06-16 20:51:15 +08:00
Wollnashorn
2dc0ff79ec video_core: Use sampler IDs instead pointers in the pipeline config
The previous approach of storing pointers returned by `GetGraphicsSampler`/`GetComputeSampler` caused UB, as these functions can cause reallocation of the sampler slot vector and therefore invalidate the pointers
2023-06-16 13:45:14 +02:00
lat9nq
1fa16bc594 cmake: Add warn about cross compiling, disable android 2023-06-16 05:38:33 -04:00
lat9nq
d9e2824c4e cmake: Check for target is Windows
MinGW has issues building tzdb2nx due to the headers being Windows
specific. Download for this toolchain as well.
2023-06-16 05:32:11 -04:00
lat9nq
d35c989902 cmake: Use non-conflicting variable names 2023-06-16 05:17:06 -04:00
lat9nq
7ffb96f474 cmake: Extra time zone data download checks
Extra sanitization for Windows hosts, and fail loudly when the
download fails.

cmake: Fix status code reading
2023-06-16 05:17:03 -04:00
lat9nq
e9701a3cda cmake: Add option to always download time zone data 2023-06-16 04:32:31 -04:00
lat9nq
b23c358e3d externals: submodule tzdb_to_nx
Fix for Flatpak being unable to download during CMake configure.
2023-06-16 04:15:19 -04:00
lat9nq
cdc73498e3 nx_tzdb: Support submoduling tzdb_to_nx
Fix for flatpak having no internet access during CMake configure.
2023-06-16 04:00:19 -04:00
liamwhite
c7fc5b9348 Merge pull request #10739 from zeltermann/sdl-cpuinfo
Re-enable SDL's `CPUinfo` subsystem
2023-06-16 00:08:53 -04:00
liamwhite
41295ff8fe Merge pull request #10795 from german77/foomiibo
input_common: Add foomiibo support
2023-06-16 00:08:30 -04:00
Charles Lombardo
9dd52019a9 Merge pull request #10767 from t895/lint
android: Linting
2023-06-16 00:06:55 -04:00
lat9nq
8d8f850bd6 time_zone_manager: Compare to the correct boolean
Reference implementation does not compare the booleans as we had them.
Use the correct ones as in the reference.

Also adds an assert. I have been made aware of a crash here and am
not able to reproduce currently.
2023-06-15 23:05:41 -04:00
lat9nq
03e8d9aca7 nx_tzdb: Correct Antarctica spelling 2023-06-15 23:03:54 -04:00
Charles Lombardo
d0be850f25 android: Apply ktlint codestyle 2023-06-15 22:36:54 -04:00
Charles Lombardo
d85129aa17 Android: Use ktlint for Kotlin code style 2023-06-15 22:22:49 -04:00
Charles Lombardo
a29fa119e0 android: Enable android linting 2023-06-15 22:19:58 -04:00
bunnei
9a04793ae8 Merge pull request #10796 from bunnei/fix-saf
android: fs: Fix Exists / IsFile for SAF.
2023-06-15 18:39:14 -07:00
bunnei
0114abad9a Merge pull request #10790 from liamwhite/arm-driver-moment
vulkan_device: disable extended_dynamic_state2 on ARM drivers
2023-06-15 18:34:31 -07:00
bunnei
ca1cb9fd19 Merge pull request #10775 from liamwhite/cb2
renderer_vulkan: propagate conditional barrier support
2023-06-15 17:37:03 -07:00
Narr the Reg
0c90a0926f input_common: Add amiibo with originality signature support 2023-06-15 18:22:13 -06:00
bunnei
5384fa4998 android: fs: Fix Exists / IsFile for SAF. 2023-06-15 17:20:56 -07:00
bunnei
3c217a5574 Merge pull request #10639 from 8bitDream/pictureinpicture
android: Support for Picture in Picture / Portrait
2023-06-15 16:40:13 -07:00
Wollnashorn
a3b7b5b22a video_core: Fallback to default anisotropy instead to 1x anisotropy 2023-06-15 23:16:26 +02:00
Wollnashorn
745d16132b video_core: Disable AF for non-color image formats 2023-06-15 20:59:33 +02:00
Wollnashorn
3e8cd91d54 video_core: Fixed compilation errors because of name shadowing 2023-06-15 18:46:40 +02:00
Liam
3304d58edb vulkan_device: disable extended_dynamic_state2 on ARM drivers 2023-06-15 12:29:54 -04:00
Wollnashorn
42c944b250 video_core: Add per-image anisotropy heuristics (format & mip count) 2023-06-15 18:19:32 +02:00
Liam
2c01669046 video_core: preallocate fewer IR blocks 2023-06-14 21:37:57 -04:00
bunnei
ce0510913a Merge pull request #10729 from liamwhite/windows-is-a-meme
vfs_real: add file LRU cache for open file limits
2023-06-14 18:32:25 -07:00
Narr the Reg
61b4588517 service: nfc: Read tag protocol only for nfc backend 2023-06-14 18:16:23 -06:00
Narr the Reg
b1b13ddc6b service: nfc: Accuracy fixes 2023-06-14 18:08:35 -06:00
Morph
fb97aec26b Merge pull request #10781 from 8bitDream/vcpkg
externals: Fix update vcpkg to 2023.06.14
2023-06-14 18:07:05 -04:00
Morph
702a2ac631 Merge pull request #10749 from Morph1984/strong-typing
buffer_cache_base: Specify buffer type in HostBindings
2023-06-14 18:03:11 -04:00
Abandoned Cart
0869099da4 externals: Fix update vcpkg to 2023.06.14
Forgot to update the manifest to reflect the submodule in the previous commit.
2023-06-14 18:01:03 -04:00
Abandoned Cart
0e957c2e35 android: Move overlays to their own layout 2023-06-14 16:43:24 -04:00
Abandoned Cart
e20c4fbbd4 android: Initialize defaults for each orientations 2023-06-14 16:35:58 -04:00
Abandoned Cart
f34535f362 android: Display FPS with emulation on hinge 2023-06-14 16:35:57 -04:00
Abandoned Cart
724823c193 android: Remove PiP reliance on fragment 2023-06-14 16:35:56 -04:00
Abandoned Cart
0b442b6dd2 android: Set layout by fragment, not view 2023-06-14 16:35:54 -04:00
Abandoned Cart
2b5dde162a android: Add a separate foldable layout set 2023-06-14 16:34:23 -04:00
Abandoned Cart
fb28f9fd96 android: Set portrait default control params 2023-06-14 16:34:22 -04:00
Abandoned Cart
eb4026e3db android: Actually implement portrait controls 2023-06-14 16:34:19 -04:00
Abandoned Cart
0ef93541b4 android: Enable automated portrait controls 2023-06-14 16:34:16 -04:00
Abandoned Cart
de9100ea81 android: Add Picture in Picture / Orientation 2023-06-14 16:34:14 -04:00
bunnei
a10a091928 Merge pull request #10726 from t895/emulation-nav-component
android: Adapt EmulationActivity to navigation component
2023-06-14 12:47:14 -07:00
Charles Lombardo
b79c993328 android: Adapt EmulationActivity to navigation component 2023-06-14 14:55:25 -04:00
Liam
8d6aefdcc4 video_core: optionally skip barriers on feedback loops 2023-06-14 14:11:46 -04:00
bunnei
278336af63 Merge pull request #10773 from 8bitDream/vcpkg
externals: update vcpkg to 2023.06.14
2023-06-14 09:32:14 -07:00
Liam
cc4334870b renderer_vulkan: propagate conditional barrier support 2023-06-14 10:49:40 -04:00
Abandoned Cart
1cae01f5d5 externals: update vcpkg to 2023.06.14
Since vcpkg doesn't set version numbers between releases, one was assigned in the proper format
2023-06-14 08:34:33 -04:00
Wollnashorn
0de6b9e3f5 video_core: Apply AF only to samplers with normal LOD range [0, 1+x] 2023-06-14 13:27:27 +02:00
Wollnashorn
a9e4dddad5 video_core: Fix default anisotropic heuristic 2023-06-14 11:21:22 +02:00
Wollnashorn
44f616edb9 video_core: Never apply AF to None mipmap mode
Should fix some artifacts with the "apply anisotropic filtering for all mipmap modes" option
2023-06-14 03:57:39 +02:00
Liam
ed7c4af915 vfs_real: require file existence on open 2023-06-13 17:22:47 -04:00
Liam
dbbe237668 vfs_real: add simplified open file cache 2023-06-13 17:16:14 -04:00
bunnei
698a3eda50 Merge pull request #10603 from lat9nq/tz-more-complete
core,common: Implement missing time zone data/computations
2023-06-13 13:28:45 -07:00
Charles Lombardo
190eed8199 Merge pull request #10760 from FearlessTobi/translations
android: Declare languages in locales_config.xml
2023-06-13 15:48:45 -04:00
Wollnashorn
b9bba3ac89 video_core: Disable anisotropic filtering for samplers with depth compare 2023-06-13 21:32:32 +02:00
FearlessTobi
00fe302e60 android: Declare languages in locales_config.xml
This is required to make per-app language swithcing possible on Android 13.
2023-06-13 20:26:45 +02:00
Charles Lombardo
0644c9d6cb Merge pull request #10751 from german77/touch
android: Fix touch input
2023-06-13 14:01:02 -04:00
bunnei
14d25e2c75 Merge pull request #10747 from liamwhite/arm-interface-decouple
core: decouple ARM interface from Dynarmic
2023-06-13 09:45:09 -07:00
Liam
0e7eaaba5a vfs_real: lazily open files 2023-06-13 10:37:34 -04:00
Liam
f25236a4d6 vfs_real: add file LRU cache for open file limits 2023-06-13 10:37:34 -04:00
german77
322ac1c20c android: Fix touch input 2023-06-13 00:17:10 -06:00
Morph
925586f97b buffer_cache_base: Specify buffer type in HostBindings
Avoid reinterpret-casting from void pointer since the type is already known at compile time.
2023-06-13 00:59:42 -04:00
bunnei
e2f6199225 Merge pull request #10746 from bunnei/update-android-settings
android: Update settings, remove unused translations
2023-06-12 21:15:26 -07:00
Liam
8506915208 core: decouple ARM interface from Dynarmic 2023-06-12 22:11:51 -04:00
Wollnashorn
0eacf547c0 video_core: Option to apply anisotropic filtering for all mipmap modes 2023-06-13 03:21:01 +02:00
liamwhite
e0de6dd63f Merge pull request #10675 from liamwhite/scaler
image_info: adjust rescale thresholds and refactor constant use
2023-06-12 21:16:36 -04:00
bunnei
bcdd35e8be android: settings: Disable force_max_clock by default. 2023-06-12 17:57:48 -07:00
bunnei
f8a33f85ef android: settings: Add reactive flushing as a default-disabled setting. 2023-06-12 17:56:44 -07:00
bunnei
306ad012c8 android: res: Remove translated strings that no longer exist. 2023-06-12 17:45:02 -07:00
bunnei
f9197f4dae Merge pull request #10743 from FearlessTobi/translations
android: Add translation files manually
2023-06-12 17:20:27 -07:00
FearlessTobi
a550fe7a7d codespell: Exclude android resources directory 2023-06-13 02:15:21 +02:00
bunnei
9bee930045 Merge pull request #10705 from german77/updates
android: Add update and DLC support
2023-06-12 17:03:44 -07:00
FearlessTobi
26cdd7d980 android: Add translation files manually 2023-06-13 01:00:23 +02:00
bunnei
5144ca8bb6 Merge pull request #10728 from t895/game-hash
android: Use autogenerated hash code function for Game class
2023-06-12 14:45:18 -07:00
bunnei
5a2e0d5b76 Merge pull request #10724 from t895/auto-version-property
android: Use autoVersion when gradle property is set
2023-06-12 13:12:32 -07:00
Matías Locatti
42b2bc204f Merge pull request #10699 from liamwhite/conditional-barrier
shader_recompiler: remove barriers in conditional control flow when device lacks support
2023-06-12 16:50:59 -03:00
bunnei
ad8f122ab1 Merge pull request #10693 from liamwhite/f64-to-f32
shader_recompiler: translate f64 to f32 when unsupported on host
2023-06-12 12:46:54 -07:00
zeltermann
0c04e27df3 Re-enable SDL's CPUinfo subsystem
See https://github.com/libsdl-org/SDL/issues/7809.
Disabling CPUinfo triggers a bug in SDL's audio subsystem, which breaks
SDL's JACK output on Linux. We're lucky it hasn't broken anything else.
2023-06-12 21:36:07 +07:00
Narr the Reg
a338de7850 android: Add update support 2023-06-11 23:33:50 -06:00
Morph
333f792e10 Merge pull request #10718 from liamwhite/buffered-io
qt: use larger buffer for update install
2023-06-12 00:58:34 -04:00
Charles Lombardo
eb7ccf5249 android: Use autogenerated hash code function for Game class 2023-06-11 21:15:13 -04:00
Charles Lombardo
5751822e31 android: Use autoVersion when gradle property is set 2023-06-11 20:04:08 -04:00
Baptiste Marie
8e3d4e3396 input_common: Redesign mouse panning 2023-06-12 00:47:52 +02:00
bunnei
569f8d3b44 Merge pull request #10668 from Kelebek1/reduce_vertex_bindings
Combine vertex/transform feedback buffer binding into a single call
2023-06-11 11:33:48 -07:00
bunnei
f4c383392d Merge pull request #10713 from t895/gradle-updates
android: Gradle updates
2023-06-11 11:31:03 -07:00
bunnei
5fcc05a4b2 Merge pull request #10711 from t895/better-extension-check
android: Use ContentResolver to get file extension
2023-06-11 11:29:08 -07:00
Liam
b1081329b9 qt: use larger buffer for update install 2023-06-11 11:43:04 -04:00
Charles Lombardo
92d49ad652 android: Update dependencies 2023-06-11 02:17:29 -04:00
Charles Lombardo
f23a2b514b android: Differentiate build types with new names
Change the applicationIdSuffix and app launcher title based on build type
2023-06-11 02:16:45 -04:00
Charles Lombardo
37e135d74d Android: Remove unused relWithVersionCode build type 2023-06-11 02:15:28 -04:00
Charles Lombardo
16fe64ad0c android: Use ContentResolver to get file extension
Fixes an issue where we try to resolve file extension from URIs. Sometimes the URI will not contain the file name at all and instead a string of numbers. Here we query the content resolver and guarantee that we get a file name every time.
2023-06-11 01:41:58 -04:00
bunnei
b6f2490288 Merge pull request #10703 from bunnei/fix-android-layout
android: Fix screen orientation & blurriness.
2023-06-10 16:24:16 -07:00
bunnei
ea716eb5cc android: Fix screen orientation & blurriness. 2023-06-10 15:13:06 -07:00
bunnei
6b898c6d69 Merge pull request #10670 from liamwhite/fxaa2
vk_blit_screen: use higher bit depth for fxaa
2023-06-10 14:35:23 -07:00
Liam
2f1e87dd83 shader_recompiler: translate f64 to f32 when unsupported on host 2023-06-10 12:38:49 -04:00
Liam
2bb7ea436d shader_recompiler: remove barriers in conditional control flow when device lacks support 2023-06-10 12:30:39 -04:00
Morph
fa5dfcb712 Merge pull request #10685 from liamwhite/serialization-is-hard
qt: persist framerate sync option
2023-06-10 12:28:00 -04:00
Morph
cb1fd1bad8 Merge pull request #10697 from 8bitDream/codespell
Add a codespell exception for kotlin OptIn
2023-06-10 12:27:19 -04:00
Abandoned Cart
d8609eef89 Add a codespell exception for kotlin OptIn 2023-06-10 05:35:57 -04:00
bunnei
f759ff3a5c Merge pull request #10691 from t895/nro-check
android: Add proper homebrew check
2023-06-09 23:59:51 -07:00
Charles Lombardo
72d9dc9a3f android: Add proper homebrew check 2023-06-09 20:17:51 -04:00
bunnei
55b543f466 Merge pull request #10686 from t895/version-check
android: Fix input overlay version check
2023-06-09 15:27:21 -07:00
Charles Lombardo
3cce51d25b android: Fix input overlay version check 2023-06-09 15:15:57 -04:00
liamwhite
4d395b3b72 Merge pull request #10614 from xcfrg/shader-backend-status-bar
yuzu: add opengl shader backend info in status bar
2023-06-09 09:46:11 -04:00
Liam
5a0d4e1d38 qt: persist framerate sync option 2023-06-09 09:40:34 -04:00
liamwhite
b3e2c9f9f1 Merge pull request #10623 from german77/backup
service: nfc: Add backup support
2023-06-08 21:54:12 -04:00
liamwhite
2a1acbfb4d Merge pull request #10666 from liamwhite/my-framerate-is-fine
nvnflinger: allow locking framerate during video playback
2023-06-08 21:53:57 -04:00
liamwhite
a57150afbd Merge pull request #10676 from bunnei/fix-mi-5-android
android: EmulationActivity: Fix orientation on Mi Pad 5.
2023-06-08 21:53:51 -04:00
liamwhite
60cc611f38 Merge pull request #10677 from tokarevart/fix-warning
Fix a potentially uninitialized local variable warning causing a build error
2023-06-08 21:53:40 -04:00
bunnei
064bad6ddf android: EmulationActivity: Fix orientation on Mi Pad 5. 2023-06-08 17:20:13 -07:00
Tokarev Artem
007c3fa7df Fix potentially uninitialized local variable warning 2023-06-09 05:12:22 +05:00
Liam
05b66877d1 image_info: adjust rescale thresholds and refactor constant use 2023-06-08 17:46:40 -04:00
Liam
74671186bf vk_blit_screen: use higher bit depth for fxaa 2023-06-08 11:27:57 -04:00
Kelebek1
ace6c2318b Combine vertex/transform feedback buffer binding into a single call 2023-06-08 12:13:27 +01:00
Liam
6c34adb1de nvnflinger: allow locking framerate during video playback 2023-06-08 01:15:51 -04:00
Morph
3e6d81a008 nvdisp: Fix SingleCore frametime reporting 2023-06-07 22:04:02 -04:00
Morph
2e1e725443 core_timing: Fix SingleCore cycle timer 2023-06-07 21:44:42 -04:00
Morph
907507886d (wall, native)_clock: Add GetGPUTick
Allows us to directly calculate the GPU tick without double conversion to and from the host clock tick.
2023-06-07 21:44:42 -04:00
Morph
9dcc7bde8b time: Use compile time division for TimeSpanType conversion 2023-06-07 21:44:42 -04:00
Morph
8e56a84566 core_timing: Use CNTPCT as the guest CPU tick
Previously, we were mixing the raw CPU frequency and CNTFRQ.
The raw CPU frequency (1020 MHz) should've never been used as CNTPCT (whose frequency is CNTFRQ) is the only counter available.
2023-06-07 21:44:42 -04:00
Morph
bbd502f67a nvnflinger: Acquire lock prior to signaling the vsync variable 2023-06-07 21:44:42 -04:00
Morph
1492a65454 (wall, native)_clock: Rework NativeClock 2023-06-07 21:44:42 -04:00
Morph
dd12dd4c67 x64: Deduplicate RDTSC usage 2023-06-07 21:44:42 -04:00
bunnei
9c6fc44a59 Merge pull request #10650 from qurious-pixel/android_tv
Android TV banner
2023-06-07 16:32:25 -07:00
qurious-pixel
45f80f2b06 remove version code declaration 2023-06-07 13:27:51 -07:00
liamwhite
86cbd867d2 Merge pull request #10655 from Morph1984/msvc-cxx20
CMakeLists: Force C++20 on MSVC due to conflicts with C++23 modules
2023-06-07 14:04:25 -04:00
liamwhite
5c79a07a36 Merge pull request #10635 from mrcmunir/l4t-tx1-nvidia
Make VK_EXT_robustness2 optional
2023-06-07 14:04:14 -04:00
liamwhite
cfb76d8f3e Merge pull request #10476 from ameerj/gl-memory-maps
OpenGL: Make use of persistent buffer maps in buffer cache
2023-06-07 14:03:57 -04:00
liamwhite
6907d30258 Merge pull request #10583 from ameerj/ill-logic
AccelerateDMA: Fix incorrect check in Buffer<->Texture copies
2023-06-07 14:03:40 -04:00
liamwhite
219bd90152 Merge pull request #10591 from keve1227/localized-game-icons
Localize game icons
2023-06-07 14:03:28 -04:00
Morph
f62f43c0da CMakeLists: Force C++20 on MSVC due to conflicts with C++23 modules
The latest version of MSVC STL brings C++23 standard library modules, which conflict with precompiled headers.
Disabling with /experimental:module- has no effect, so force C++20 in the meantime while we wait for module support in other compilers.
2023-06-06 20:20:09 -04:00
german77
107aa52cdb service: nfc: Add backup support 2023-06-06 17:06:21 -06:00
Morph
238e46ec93 Merge pull request #10651 from Morph1984/a
github/gitmodules: Misc fixes
2023-06-06 17:15:41 -04:00
Morph
08ba16e105 gitmodules: Fix libadrenotools submodule 2023-06-06 15:12:12 -04:00
Morph
099f3f639e github: Remove release workflow 2023-06-06 15:12:12 -04:00
Morph
4c9769d7ea Merge pull request #10649 from german77/version
android: Set version code
2023-06-06 15:11:54 -04:00
Live session user
9611a9e220 Android TV banner 2023-06-06 11:32:25 -07:00
Narr the Reg
4d61319307 android: Set version code 2023-06-06 12:14:38 -06:00
Carlos Estrague / Mrc_munir
b854981917 Updated to lexicographical order suggestions 2023-06-06 19:33:52 +02:00
Narr the Reg
d967ab8e79 Merge pull request #10643 from 8bitDream/gradle-config
android: Improve Gradle build configuration
2023-06-06 11:19:20 -06:00
Narr the Reg
7d5cc33feb Merge pull request #10641 from 8bitDream/ci-android
android: Fix ci builds with Java 17
2023-06-06 11:17:46 -06:00
Abandoned Cart
0968e315f4 android: Improve Gradle build configuration 2023-06-06 12:46:21 -04:00
Abandoned Cart
5ff28ffc6d android: Fix ci builds with Java 17 2023-06-06 07:06:34 -04:00
lat9nq
013c34cb32 vk_device_info: Clean up includes [IWYU] 2023-06-06 01:54:44 -04:00
lat9nq
f9fc996083 vk_device_info: Add SPDX data 2023-06-06 01:54:44 -04:00
lat9nq
fc0c4db20d yuzu-qt: Load Vulkan device info at startup
Loading it when the configuration opens now incurs a noticeable delay.
We also don't need to rediscover the same data repeatedly each time the
configuration opens.

Moves vulkan device info discovery to yuzu's startup as opposed to the
configure_graphics constructor.
2023-06-06 01:54:44 -04:00
bunnei
069d7e6be4 android: audio_core: sink_stream: Remove unnecessary check. 2023-06-05 21:47:36 -07:00
bunnei
cb95d7fe1b Merge pull request #10508 from yuzu-emu/lime
Project Lime - yuzu Android Port
2023-06-05 21:43:43 -07:00
Carlos Estrague / Mrc_munir
19d05bd4d7 Make VK_EXT_robustness2 optional
For some reason nvidia implemented Vulkan 1.2 supported without support for VK_EXT_robustness2 in tegra X1/X2 .

Fix vulkan work in TX1/TX2  L4T drivers .
2023-06-06 06:32:47 +02:00
bunnei
036996429e Merge pull request #10633 from t895/variable-surface-ratio
android: Use a custom view for changing emulation aspect ratio
2023-06-05 20:27:58 -07:00
bunnei
dc2a0b2e50 Merge pull request #10578 from PabloG02/lime-firmware&logs
Add UI to import firmware and share logs
2023-06-05 17:41:19 -07:00
bunnei
e1078ec0f4 android: HomeSettingsFragment: Use string resource for "Share log". 2023-06-05 17:40:43 -07:00
Charles Lombardo
c8b91b3a89 android: Use a custom view for changing emulation aspect ratio
Credit to the Skyline team for the FixedRatioSurfaceView.
2023-06-05 20:24:36 -04:00
bunnei
db7b106f1d Merge pull request #10611 from liamwhite/audio-deadlock
audio_renderer: resolve adsp thread deadlock shutdown
2023-06-05 17:15:19 -07:00
bunnei
71766f3269 Merge pull request #10618 from t895/licenses
android: Add licenses page
2023-06-05 17:14:15 -07:00
PabloG02
409ff26f02 Address feedback 2023-06-06 00:07:54 +02:00
lat9nq
8f9afbcd91 tz_manager: Fix comparison to wrong integer 2023-06-05 15:15:23 -04:00
lat9nq
3218313c22 tz_manager: Implement missing transition times
time_zone_manager: Use s64 storage
2023-06-05 15:15:23 -04:00
lat9nq
78a47f1ee8 tz_manager: Warn on unimplemented code 2023-06-05 15:15:23 -04:00
lat9nq
dea61f5d00 tz_manager: Fix character offset not advancing 2023-06-05 15:15:23 -04:00
lat9nq
63c51abe42 tz_manager: Fix off-by-one error 2023-06-05 15:15:23 -04:00
lat9nq
de1fe66d81 time_zone: Handle offset time zones
time_zone: Remove maybe_unused

time_zone: Use s64 storages

time_zone: Catch by reference
2023-06-05 15:15:23 -04:00
lat9nq
84642bdd3f time_zone_binary: Add zoneinfo data
Adds the basic time zone data for the system archive.

time_zone_binary: Implement full system archive

time_zone_binary: Remove unneeded template

tz_binary: Make GenerateFiles static
2023-06-05 15:15:23 -04:00
lat9nq
73036c83a3 nx_tzdb: Create headers from downloaded system archive data
Use lat9nq/tzdb_to_nx release data to generate header files.

nx_tzdb: Use an interface library

nx_tzdb: Gate download if achive not exists

nx_tzdb: Fix header generator brace closing

nx_tzdb: Add base directory files

nx_tzdb: Add SPDX info
2023-06-05 15:15:22 -04:00
Narr the Reg
a40e0fdf9e time: Implement missing services
Implements GetTotalLocationNameCount LoadLocationNameList and
GetTimeZoneRuleVersion.

tz-manager: Fix sign issue
2023-06-05 15:15:11 -04:00
lat9nq
8d52dc163a time_zone_manager: Implement go_ahead/go_back 2023-06-05 15:15:11 -04:00
lat9nq
5d9dd88387 tz_content_manager: Try the system time zone first
If we can't find the normal time zone string, try searching for the
closest one.
2023-06-05 15:15:11 -04:00
lat9nq
3979c7daa4 common: Move system time zone string detection
Moves it from Settings to Common::TimeZone, since this algorithm doesn't
depend on the setting. It also lets us use it in other libraries.

common: Various fixes

time_zone: Don't double up the std::abs

Too many absolute values were causing mirrored time zones to resolve
as the same.
2023-06-05 15:15:11 -04:00
lat9nq
011438fa95 configure_system: Remove external offset on custom rtc 2023-06-05 15:15:11 -04:00
lat9nq
a39b9134db time: Remove auto timezone consideration
GetTimeZoneString no longer reports a setting unique to yuzu, so we
can assume a valid timezone string in core.
2023-06-05 15:15:11 -04:00
lat9nq
3e68a284ae settings: Always report a valid time zone
Prevents needing to deduce the non-Switch setting in core. Instead, we
deduce the meaning of this setting where the heresy is committed, in
common.

settings: Remove strftime usage

GetTimeZoneString: Use standard features

Also forces GMT on MinGW due to broken strftime.
2023-06-05 15:15:11 -04:00
lat9nq
9e2164be74 time_manager: Don't offset RTC by system time zone
This causes the emulated system's universal time to be on the user's clock, and the user time to
be off if they set a time zone.

time_manager: Remove GetExternalRtcTime
2023-06-05 15:15:11 -04:00
lat9nq
c378cbbc2d tz_content_manager: Detect system time zone
Uses C++20 tzdb to determine the system timezone. The switch uses the
597 posix time zones, so this needs tests if the system time zone isn't
posix-compliant.
2023-06-05 15:15:11 -04:00
Charles Lombardo
cba5865afe android: Create licenses page 2023-06-05 14:34:23 -04:00
bunnei
2f7658bd75 Merge pull request #10613 from t895/settings-changes
android: String and settings organization changes
2023-06-04 19:17:42 -07:00
bunnei
78319435e6 Merge pull request #10622 from t895/load-settings
android: Load settings at the start of each activity
2023-06-04 19:14:38 -07:00
Charles Lombardo
5e58af0616 android: Move settings to debug submenu 2023-06-04 19:53:27 -04:00
Charles Lombardo
0078f97227 android: Load settings at the start of each activity 2023-06-04 19:37:10 -04:00
xcfrg
a64ad8315f yuzu: add opengl shader backend info in status bar 2023-06-04 17:24:30 -04:00
bunnei
e6fce1cbbd Merge pull request #10594 from liamwhite/double-patch
fsp-srv: avoid patching romfs multiple times
2023-06-04 13:24:47 -07:00
PabloG02
3733187c14 Attempt to move the unzip coroutine to a ViewModel 2023-06-04 20:52:12 +02:00
PabloG02
72597b8ffe android: update strings 2023-06-04 20:52:12 +02:00
PabloG02
8713c442e9 android: add option to share log 2023-06-04 20:52:12 +02:00
PabloG02
5435f0be5e android: add option to install firmware 2023-06-04 20:52:12 +02:00
PabloG02
19674ec78d android: move unzip function to FileUtil and use SecurityException 2023-06-04 20:50:00 +02:00
Charles Lombardo
5de8c5b5c7 android: Several string changes 2023-06-04 13:30:56 -04:00
Liam
e96a3a1713 audio_renderer: resolve adsp thread deadlock shutdown 2023-06-04 13:00:10 -04:00
bunnei
125a0e5a07 Merge pull request #10588 from liamwhite/vfs-cached
vfs: add vfs_cached for romfs build
2023-06-03 23:23:45 -07:00
Charles Lombardo
e804f24519 Merge pull request #10605 from 8bitDream/kotlin
android: Resolve a couple Gradle warnings
2023-06-04 02:22:34 -04:00
bunnei
c5782150f0 Merge pull request #10550 from kkoniuszy/linux-map-buildup-fix
host_memory: merge adjacent placeholder mappings on Linux
2023-06-03 23:22:24 -07:00
Abandoned Cart
00a391ce10 android: Resolve a couple Gradle warnings 2023-06-04 02:06:38 -04:00
bunnei
42074dc6c7 Merge pull request #10595 from 8bitDream/deprecated
android: Replace deprecated and Java code
2023-06-03 22:08:30 -07:00
Abandoned Cart
cfa8bec5b9 android: Add support for split foldable view 2023-06-03 22:57:28 -04:00
Kevin Sundqvist Norlén
a2cfe3749a Fix typo
Co-authored-by: liamwhite <liamwhite@users.noreply.github.com>
2023-06-03 21:31:44 +02:00
Abandoned Cart
b394a6b937 android: Replace deprecated and Java code 2023-06-03 15:16:25 -04:00
Liam
a75bc759fe fsp-srv: avoid patching romfs multiple times 2023-06-03 14:27:08 -04:00
Keve1227
a0f235f4fd Update Chinese NX language names
... as per the TLoZ: TotK icon files. Would this conflict with older games?
2023-06-03 17:23:14 +02:00
Keve1227
27313fe576 Issue a reload if the system language changed 2023-06-03 17:17:03 +02:00
Keve1227
d0aa63069f Pick game icon based on the configured system language 2023-06-03 17:13:24 +02:00
Minionguyjpro
58c54aecb6 Update README.md (Add Android at builds description) (#10586) 2023-06-03 16:29:26 +02:00
Liam
6e23c84669 romfs: use vfs_cached for romfs output 2023-06-03 08:56:59 -04:00
Liam
790f91fcc5 vfs: add vfs_cached for romfs build 2023-06-03 08:50:54 -04:00
bunnei
296ccb698d android: cmake: Use cmake_dependent_option as appropriate. 2023-06-03 00:14:33 -07:00
Charles Lombardo
a789046127 android: Fix crash on importing invalid save 2023-06-03 00:06:08 -07:00
bunnei
db6737f2ba android: vk_presentation_manager: Fix unusued needs_recreation. 2023-06-03 00:06:08 -07:00
Charles Lombardo
f94eb320ff android: Rename "Input Overlay" to "Overlay Options" 2023-06-03 00:06:08 -07:00
Charles Lombardo
c927a30d09 android: Adjust import/export saves dialog 2023-06-03 00:06:08 -07:00
Charles Lombardo
20abd49a21 android: Warning dialogs for key errors 2023-06-03 00:06:08 -07:00
bunnei
df70fdc95b android: vk_turbo_mode: Remove unnecessary device recreation.
- Fixes a rare crash.
2023-06-03 00:06:08 -07:00
bunnei
4ac9778652 android: EmulationFragment: Remove unnecessary surface destroy on pause. 2023-06-03 00:06:08 -07:00
bunnei
098e2c4077 android: renderer_vulkan: Fix crash with surface recreation. 2023-06-03 00:06:07 -07:00
bunnei
057117f009 android: Fix presentation layout on foldable and tablet devices. 2023-06-03 00:06:07 -07:00
Charles Lombardo
ca4b07a2d7 android: Enable overlay scale/opacity dialog 2023-06-03 00:06:07 -07:00
PabloG02
1957b7e6cc Add image to card_game.xml to preview in the Layout Editor 2023-06-03 00:06:07 -07:00
PabloG02
a7e0a0d5b1 Save the position of buttons as a percentage 2023-06-03 00:06:07 -07:00
Charles Lombardo
8e8627a258 android: Don't crash the app when selecting a zip that causes a SecurityException 2023-06-03 00:06:07 -07:00
bunnei
eb4ab9bc58 input_common: Fix virtual amiibos 2023-06-03 00:06:06 -07:00
bunnei
40e938376b android: audio_core: Avoid shutdown hang. 2023-06-03 00:06:06 -07:00
bunnei
9ca8687b5f android: ForegroundService: Handle null intent. 2023-06-03 00:06:06 -07:00
bunnei
17ae85e724 android: ImportExportSavesFragment: Cleanup strings. 2023-06-03 00:06:06 -07:00
bunnei
b325ad16bc Update src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/ImportExportSavesFragment.kt 2023-06-03 00:06:06 -07:00
PabloG02
4010764279 Remove ?. 2023-06-03 00:06:06 -07:00
PabloG02
9650c1d02d Check if folder exists before letting the user import/export saves 2023-06-03 00:06:06 -07:00
PabloG02
33d36ded28 Add save import/export in UI 2023-06-03 00:06:06 -07:00
bunnei
b2570d2546 android: Use ext-android-bin for external binaries. 2023-06-03 00:06:06 -07:00
Charles Lombardo
ad9c2356a8 android: Fix FPS text getting cut off by rounded display corners 2023-06-03 00:06:05 -07:00
Charles Lombardo
fdd200d33f android: Prevent deleting the settings file while a game is running 2023-06-03 00:06:05 -07:00
Charles Lombardo
4e87a01db6 android: Fix link text color for base theme dialog 2023-06-03 00:06:05 -07:00
bunnei
ee8caac82f android: Various fixes for CI. 2023-06-03 00:06:05 -07:00
bunnei
17b5ed9baf android: externals: Update libadrenotools, use useLegacyPackaging. 2023-06-03 00:06:05 -07:00
Charles Lombardo
0f9c5b8d6a android: Re-enable service notification 2023-06-03 00:06:05 -07:00
Charles Lombardo
09747ca2d3 android: Ensure keys are loaded before populating games list 2023-06-03 00:06:05 -07:00
Charles Lombardo
897b748895 android: Use dialog fragment for the reset settings dialog 2023-06-03 00:06:05 -07:00
Charles Lombardo
9c1d42342d android: Upgrade AGP to 8.0.2 2023-06-03 00:06:04 -07:00
Charles Lombardo
7de86266fd android: Show notification permission page during setup 2023-06-03 00:06:04 -07:00
Charles Lombardo
419daf770d android: DIsable FPS counter by default 2023-06-03 00:06:04 -07:00
Charles Lombardo
bfb4e3bcaa android: Improve searches with one character
The Jaccard algorithm is great for searches with 2 or more characters but nothing is returned for searches with one character. To get around this, just search with JaroWinkler for single character searches.
2023-06-03 00:06:04 -07:00
Charles Lombardo
0cbae33790 android: Stop building x86 packages in APKs
This was really only meant for building the app to run in an emulator. If this is necessary, just add manually.
2023-06-03 00:06:04 -07:00
Charles Lombardo
d49eb7faad android: Add FPS toggle 2023-06-03 00:06:04 -07:00
liushuyu
be6159842a CI: use the verify pipeline to do releases 2023-06-03 00:06:04 -07:00
Charles Lombardo
8426e97f45 android: Clean up app build.gradle
Removes the conflicting declaration of "version" and changes to versionCode that did nothing.
2023-06-03 00:06:04 -07:00
bunnei
cf9f4f67dd video_core: vk_rasterizer: Decrease draw dispatch count for Android. 2023-06-03 00:06:04 -07:00
bunnei
b4725332a2 android: config: Expose VSync as a configurable setting. 2023-06-03 00:06:04 -07:00
bunnei
f0ed20c8a2 android: GPU: Enable async presentation, increase frames in flight. 2023-06-03 00:06:03 -07:00
Charles Lombardo
116e2b5f02 android: Enable onBackInvokedCallback
For now this enables the ability to see the new Android 13 back gesture animations but later we can create custom animations that follow the back gesture.
2023-06-03 00:06:03 -07:00
Charles Lombardo
7812de4ade android: Remove deprecated use of onBackPressed() 2023-06-03 00:06:03 -07:00
Charles Lombardo
ffba83d568 android: Add option for touch overlay haptics
Disabled by default
2023-06-03 00:06:03 -07:00
Charles Lombardo
5dbf842a46 android: Improve missing game handling
Previously the app would crash if you selected a game that no longer existed. Now we show an error message and reload the games list to remove any invalid games from the list.
2023-06-03 00:06:03 -07:00
Charles Lombardo
c11c5b2eb7 android: Clean up dependencies
Additionally updates material and androidx core libraries
2023-06-03 00:06:03 -07:00
Charles Lombardo
a10b1c8ef5 android: Delete java code style file 2023-06-03 00:06:03 -07:00
Charles Lombardo
e42c966110 android: Settings UI tweaks
New spacing and fonts for list items
2023-06-03 00:06:03 -07:00
Charles Lombardo
412c95e0b0 android: Simplify setup in search and games fragments 2023-06-03 00:06:03 -07:00
Charles Lombardo
aa8a48e94c android: Use collapsing toolbar layout in settings 2023-06-03 00:06:03 -07:00
Charles Lombardo
b31ab11842 android: Remove unnecessary JvmStatic/JvmField annotations 2023-06-03 00:06:02 -07:00
Charles Lombardo
8d3288b6ff android: Fix navigation rail animation in rtl layout 2023-06-03 00:06:02 -07:00
Charles Lombardo
c930b2bad2 android: Use cutout insets on setup fragment 2023-06-03 00:06:02 -07:00
Charles Lombardo
ee57aa83a4 android: Button to reset all settings 2023-06-03 00:06:02 -07:00
Charles Lombardo
0f06e73a7c android: Use proguard file in relWithDebInfo 2023-06-03 00:06:02 -07:00
Charles Lombardo
a0a0703f30 android: Fix background color within inset areas 2023-06-03 00:06:02 -07:00
Charles Lombardo
070d250858 android: Shortcut to settings activity on reselection 2023-06-03 00:06:01 -07:00
Charles Lombardo
c31a37c828 android: Expose custom RTC setting 2023-06-03 00:06:01 -07:00
Charles Lombardo
f7934bdaf7 android: Reset setting on long press 2023-06-03 00:06:01 -07:00
Charles Lombardo
2289f7ad15 android: Fix issues with ea/main icons and version codes
Now all yuzu icon variants are taken care of and now we have a build variant that uses the versioning we need for the play store.
2023-06-03 00:06:01 -07:00
Charles Lombardo
5213701e18 android: Move theme options out of advanced settings 2023-06-03 00:06:01 -07:00
Charles Lombardo
bafd4d344f android: Check if cached games are valid
Fixes bug when you close yuzu, delete games, and reopen to an instant crash.
2023-06-03 00:06:01 -07:00
german77
aa957df0dc android: Invert rotation to match phone orientation 2023-06-03 00:06:01 -07:00
bunnei
5d43594a70 android: vulkan_device: Skip BGR565 emulation on S8gen2. 2023-06-03 00:06:01 -07:00
bunnei
1a424ea6c6 android: config: Use default anisotropic filtering. 2023-06-03 00:06:01 -07:00
Charles Lombardo
41c20f8460 android: Remove top padding from in game menu items 2023-06-03 00:06:00 -07:00
Charles Lombardo
ec81c6cf44 android: Use different icons for mainline/ea 2023-06-03 00:06:00 -07:00
Charles Lombardo
f69bc78dc5 android: Add early access upgrade fragment
We now have a second build flavor that will determine whether the "Get Early Access" button appears.
2023-06-03 00:06:00 -07:00
bunnei
8b8123b733 android: vulkan_device: Only compile OverrideBcnFormats when used. 2023-06-03 00:06:00 -07:00
Liam
6b2e89a865 android: remove spurious warnings about BCn formats when patched with adrenotools 2023-06-03 00:06:00 -07:00
bunnei
befd477279 android: video_core: Disable some problematic things on GPU Normal. 2023-06-03 00:06:00 -07:00
bunnei
6b093224c1 android: settings: Use mailbox vsync by default. 2023-06-03 00:06:00 -07:00
bunnei
8dc7fe0c96 android: video_core: Disable problematic compute shaders.
- Fixes #104.
2023-06-03 00:06:00 -07:00
Charles Lombardo
1c9dae7cac android: Update progard to fix settings crash
R8 full mode was removing important classes from Wini that would cause a crash on saving settings. This keeps the relevant classes and suppresses warnings about irrelevant ones.
2023-06-03 00:05:59 -07:00
bunnei
117bc2ae6c android: vulkan: Recreate surface after suspension & adapt to async. presentation. 2023-06-03 00:05:59 -07:00
Charles Lombardo
65dc35a1a5 android: Game data cache 2023-06-03 00:05:59 -07:00
Charles Lombardo
b0bef6173a android: Update to Kotlin 1.8.21 2023-06-03 00:05:59 -07:00
Charles Lombardo
c16e663f70 android: Disable jetifier
We no longer depend on any legacy libraries that required this flag
2023-06-03 00:05:59 -07:00
Charles Lombardo
d8bacdfc14 android: Update dependencies 2023-06-03 00:05:59 -07:00
Charles Lombardo
56d3711e34 android: Migrate to AGP 8.0.1 2023-06-03 00:05:59 -07:00
Charles Lombardo
48065c7a0e android: Enable non-transitive R classes
New default going forward for new android projects. Best to follow the new standard.
2023-06-03 00:05:59 -07:00
bunnei
570c4a2c1b android: config: Enable asynchronous presentation by default on Android. 2023-06-03 00:05:59 -07:00
bunnei
ca4bf3844e video_core: Enable support_descriptor_aliasing on Turnip, disable storage atomic otherwise. 2023-06-03 00:05:58 -07:00
german77
e5bdb7011b android: fix deadzone calculation 2023-06-03 00:05:58 -07:00
Charles Lombardo
13b29c3f49 android: Fix background color when starting emulation 2023-06-03 00:05:58 -07:00
Charles Lombardo
34b7d58157 android: Persistent scrollbars on home settings fragment 2023-06-03 00:05:58 -07:00
Charles Lombardo
f461465a92 android: Use short build hash 2023-06-03 00:05:58 -07:00
Charles Lombardo
86e395595a android: Use navigation bar shade view 2023-06-03 00:05:58 -07:00
Charles Lombardo
21e8a8277a android: About fragment 2023-06-03 00:05:58 -07:00
Charles Lombardo
09e7b14d0e android: Use x-axis animation for navigation rail 2023-06-03 00:05:58 -07:00
Charles Lombardo
03541703fa android: Sort games alphabetically by default 2023-06-03 00:05:58 -07:00
Charles Lombardo
9f433e281a android: New icons for navigation bar 2023-06-03 00:05:57 -07:00
Charles Lombardo
efc054e47f android: New icons for home settings fragment 2023-06-03 00:05:57 -07:00
Charles Lombardo
9f6f21946c android: Add navigation rail 2023-06-03 00:05:57 -07:00
Charles Lombardo
6df030998a android: Search Fragment 2023-06-03 00:05:57 -07:00
Charles Lombardo
3281dc597e android: Fix potential zip traversal exploit 2023-06-03 00:05:57 -07:00
german77
d1fb7ea58b android: Add dedicated show overlay checkbox 2023-06-03 00:05:57 -07:00
Charles Lombardo
912bf6a0c6 android: Add user directory shortcut 2023-06-03 00:05:57 -07:00
german77
265b9139e0 android: Fix inline keyboard input 2023-06-03 00:05:57 -07:00
Charles Lombardo
7131432037 android: Fix grammatical mistake in video core error message 2023-06-03 00:05:56 -07:00
Charles Lombardo
4bb82e4860 android: Adjust wording on GPU driver install button 2023-06-03 00:05:56 -07:00
Narr the Reg
c46a1da5dc android: Add deadzone to stick input 2023-06-03 00:05:56 -07:00
german77
166bff88b8 android: Move motion listener to emulation activity 2023-06-03 00:05:56 -07:00
Narr the Reg
5c1310dc5d core: hid: Finish linking motion from virtual controllers 2023-06-03 00:05:56 -07:00
Charles Lombardo
d015b0db93 android: Change wording for "Add Games" button (#100)
Co-authored-by: bunnei <bunneidev@gmail.com>
2023-06-03 00:05:56 -07:00
Charles Lombardo
0d16805445 android: Scroll shortcut for games list
If you reselect the "Games" menu item in the bottom navigation menu, the list smoothly scrolls to the top.
2023-06-03 00:05:56 -07:00
Charles Lombardo
274b2be24f android: Setup screen hotfix
Added help button link for add games warning and a check for whether a task was completed on a given screen.
2023-06-03 00:05:56 -07:00
Charles Lombardo
792ce5cb2f android: Swap Default and Install buttons for GPU driver installation dialog 2023-06-03 00:05:56 -07:00
Charles Lombardo
72247a2324 android: Add warnings to setup screens 2023-06-03 00:05:56 -07:00
Charles Lombardo
71667e4d6d android: Allow search bar to scroll offscreen 2023-06-03 00:05:55 -07:00
Charles Lombardo
3b9791d887 android: Update app icon
Small icon updates from Flam
2023-06-03 00:05:55 -07:00
Charles Lombardo
c070a588b9 android: Change organization of the settings tab in the home screen 2023-06-03 00:05:55 -07:00
Charles Lombardo
c29f14fa0f android: Properly pop setup fragment from the back stack 2023-06-03 00:05:55 -07:00
Charles Lombardo
f41939b66e android: Vertically scalable setup pages
Previously the setup pages would remain at a fixed height but now the icon and two text boxes will give up space as a device gets shorter. This eliminates the need for a scrolling view further problems with padding.
2023-06-03 00:05:55 -07:00
Charles Lombardo
48c506682d android: Fix setup rotation bug
If you rotated the device at the "Add Games" screen the buttons would disappear until you trigged them from the beginning page swap. Now button state is saved across recreation.
2023-06-03 00:05:55 -07:00
Charles Lombardo
61e0042633 android: Temporarily switch for a fixed version code for testing 2023-06-03 00:05:55 -07:00
Charles Lombardo
986f858e6f android: Fix alignment of SwipeRefreshLayout 2023-06-03 00:05:55 -07:00
Charles Lombardo
388dc0757f android: Shape/spacing adjustments to game card
Ripple effect now reaches into rounded corners, icon size changed, company text removed, title font adjusted, and spacing around the card was adjusted as well. Text also doesn't get cut off anymore and instead scrolls indefinitely on one line.
2023-06-03 00:05:55 -07:00
Charles Lombardo
f984775f12 android: Manual tweaks for dialog colors
Small fix for Flam
2023-06-03 00:05:54 -07:00
Charles Lombardo
f2cadc4ce1 android: Fix black backgrounds bug
Start using a specific night mode check because black backgrounds could apply incorrectly when using the light app mode, dark system mode, and black backgrounds. Launching the settings activity will show light mode colors/navigation bars but with black backgrounds.
2023-06-03 00:05:54 -07:00
Charles Lombardo
033adb9723 android: Use navigation bar shade view for settings activity 2023-06-03 00:05:54 -07:00
Charles Lombardo
cfa3d73f2f android: Disable editing themes during emulation 2023-06-03 00:05:54 -07:00
Charles Lombardo
62c47011a0 android: Prevent situation where binding is called on a null view 2023-06-03 00:05:54 -07:00
Charles Lombardo
d7178ed16e android: Add black backgrounds toggle 2023-06-03 00:05:54 -07:00
Charles Lombardo
d9684a2010 android: Add theme mode picker 2023-06-03 00:05:54 -07:00
Charles Lombardo
f0ba58f5aa android: Add theme picker 2023-06-03 00:05:54 -07:00
Charles Lombardo
5b26ac9293 android: Prevent potential abstract settings crash 2023-06-03 00:05:54 -07:00
Charles Lombardo
b6f57da70a android: Fix cast for abstract settings 2023-06-03 00:05:54 -07:00
Charles Lombardo
7531641ecc android: Create xml for Material You theme 2023-06-03 00:05:54 -07:00
Charles Lombardo
89593d70bf android: Remove check for API 29 in themes 2023-06-03 00:05:53 -07:00
Charles Lombardo
8cef21a0b0 android: Adjustments to home option card
Several spacing/color adjustments provided by Flam
2023-06-03 00:05:53 -07:00
Charles Lombardo
a82f8fe1c1 android: Use different colors for logo in options menu
Reverting to the official logo colors
2023-06-03 00:05:53 -07:00
Charles Lombardo
9673974f73 android: New default theme colors 2023-06-03 00:05:53 -07:00
Charles Lombardo
6549348656 android: Use libnx default icon
Credit to jaames for the original icon
2023-06-03 00:05:53 -07:00
Liam
8b4c5a9939 android: enable LTO 2023-06-03 00:05:53 -07:00
Charles Lombardo
83dae1739c android: Show error if invalid keys file is selected
There aren't MIME types specific enough for filtering out files that aren't amiibo or production keys. So here we just check for the extensions "bin" or "keys" where appropriate and stop the process if incorrect. Previously you could select any document and it could cause the app to hang.
2023-06-03 00:05:53 -07:00
Charles Lombardo
d8c102444c android: Fix first time setup scrolling bug
If you quickly scrolled from the second page to the first and then back, the next/back buttons would disappear.
2023-06-03 00:05:53 -07:00
Charles Lombardo
45d81dc6bd android: Fix A button preference key 2023-06-03 00:05:53 -07:00
Charles Lombardo
59525ddbeb android: First time setup screen 2023-06-03 00:05:52 -07:00
Charles Lombardo
766655fa41 android: Prevent editing unsafe settings at runtime
There currently isn't a visual "disabled" cue for any of the view holders that aren't the switch setting. This will be improved in the future.
2023-06-03 00:05:52 -07:00
Charles Lombardo
c609847e49 android: Abstract settings
Previously we could only add settings that would change our ini file. Now we can create abstract settings in our presenter to alter things like shared preferences for theme support!
2023-06-03 00:05:52 -07:00
german77
6dfe4240ac android: Implement gamepad input 2023-06-03 00:05:52 -07:00
Charles Lombardo
f5c48f92f2 android: Bump minimum version to Android 11 2023-06-03 00:05:52 -07:00
Charles Lombardo
9c9d9131b5 android: Decouple status bar shade from navigation bar visibility 2023-06-03 00:05:52 -07:00
Charles Lombardo
d57ae50f17 android: Enable code minification 2023-06-03 00:05:52 -07:00
Charles Lombardo
921e6dddcc android: Switch from a colored status bar to a custom view
Allows for smoother transitions with the search bar
2023-06-03 00:05:52 -07:00
Charles Lombardo
3f2b832371 android: Adjustments to card_game
Removed a currently unused text view and moved to material text views.
2023-06-03 00:05:51 -07:00
Charles Lombardo
233ae9ab69 android: MainActivity overhaul
This moves several parts of the main activity into fragments that manage themselves to react to changes. UI changes like the appearance of a new search view or when the games list changes now gets updated via multiple view models. This also starts a conversion to the androidx navigation component which furthers the goals mentioned previously with more fragment responsibility. This will eventually allow us to use one activity with interchanging fragments and multiple view models that are stored within that central activity.

fdas
2023-06-03 00:05:51 -07:00
Charles Lombardo
859c40f00e android: Enforce Vulkan 1.1 support as minimum 2023-06-03 00:05:51 -07:00
Charles Lombardo
e5487243d3 android: Update gradle version to 8.1 2023-06-03 00:05:51 -07:00
Charles Lombardo
b1ebc1c395 android: Update app dependencies 2023-06-03 00:05:51 -07:00
Charles Lombardo
ab87f74998 android: Convert gradle scripts to Kotlin DSL 2023-06-03 00:05:51 -07:00
bunnei
12c9e18b55 android: vulkan: Disable vertex_input_dynamic_state on Qualcomm. 2023-06-03 00:05:51 -07:00
bunnei
d2e55558df android: settings: Add scaling filter & anti-aliasing options. (#66) 2023-06-03 00:05:50 -07:00
bunnei
4006468f73 android: video_core: Add support for disk shader cache. (#64) 2023-06-03 00:05:50 -07:00
bunnei
6d2e7de2e0 android: vulkan_debug_callback: Ignore many innocuous errors. 2023-06-03 00:05:50 -07:00
bunnei
30bf5d5b07 android: config: Change docked mode and GPU accuracy to favor performance on Android. 2023-06-03 00:05:50 -07:00
german77
dc52152a81 service: account: Save user profile folder on first user creation 2023-06-03 00:05:50 -07:00
german77
f9974f7ef3 android: Initialize account manager 2023-06-03 00:05:50 -07:00
german77
5aec62930c android: Remove unsafe null check 2023-06-03 00:05:50 -07:00
Charles Lombardo
78f02ab9a6 android: Scale input overlay independently of system display scale 2023-06-03 00:05:50 -07:00
Charles Lombardo
aeaddf407b android: Use apply instead of commit for shared preferences
Previously we were operating on the assumption that apply'd settings wouldn't be visible immediately. This isn't true and settings will be accessible via memory before being stored to disk. This reduces any potential stutters caused by saving to shared preferences.
2023-06-03 00:05:50 -07:00
Charles Lombardo
fcbf08ca98 android: Add DPad slide toggle 2023-06-03 00:05:50 -07:00
Charles Lombardo
bebc822334 android: Add relative stick center toggle 2023-06-03 00:05:49 -07:00
Charles Lombardo
63819af214 android: Make hash and branch accessible from BuildConfig 2023-06-03 00:05:49 -07:00
Charles Lombardo
f5055ca930 android: Backup shared preferences where applicable 2023-06-03 00:05:49 -07:00
Charles Lombardo
940dbdcff2 android: Enable retaining app data after uninstall 2023-06-03 00:05:49 -07:00
Charles Lombardo
1cc8011355 android: Remove unused doFrame function 2023-06-03 00:05:49 -07:00
Charles Lombardo
4d9011a8f0 android: Convert NativeLibrary to Kotlin 2023-06-03 00:05:49 -07:00
Charles Lombardo
a827486391 android: Remove LocalBroadcastManager
This causes a couple of minor changes to directory initialization. We don't have a lengthy initialization step so we could spend less time creating state receivers and just run initialization on the main thread. We also don't have a situation where external storage will be a concern so checks are removed in favor of a binary check to see if initialization is ready.

This additionally removes the unused DoFrame callback.
2023-06-03 00:05:49 -07:00
Charles Lombardo
9d7a60346f android: Remove game database
The content provider + database solution was excessive and is now replaced with the simple file checks from before but turned into an array list held within a viewmodel.
2023-06-03 00:05:49 -07:00
Charles Lombardo
4816d5158e android: Adjust game icon loading 2023-06-03 00:05:49 -07:00
Charles Lombardo
730c63df7e android: Remove unused dimensions files 2023-06-03 00:05:48 -07:00
Charles Lombardo
83b1459af5 android: Slightly reduce game card size 2023-06-03 00:05:48 -07:00
Charles Lombardo
ac417a4ffa android: Only show company text view if it has content 2023-06-03 00:05:48 -07:00
Charles Lombardo
c0aa5392b9 android: Fix check for ok text in software keyboard 2023-06-03 00:05:48 -07:00
Narr the Reg
ca4be4283d android: Implement amiibo reading from nfc tag 2023-06-03 00:05:48 -07:00
bunnei
b2aeb50229 android: vulkan_device: Disable VK_EXT_custom_border_color on Adreno.
- Causes crashes on sampler creation with Super Mario Odyssey.
2023-06-03 00:05:48 -07:00
Charles Lombardo
1ea84854bd android: Add toggle controls option to input overlay 2023-06-03 00:05:48 -07:00
Charles Lombardo
8b99a1e49b android: Do not update FPS text on null view 2023-06-03 00:05:48 -07:00
Charles Lombardo
d30103b69f android: Convert keyboard applet to kotlin and refactor 2023-06-03 00:05:48 -07:00
bunnei
d5ebfc8e21 android: Implement basic software keyboard applet. 2023-06-03 00:05:47 -07:00
bunnei
58ede89c60 android: config: Disable shader cache by default on Android. 2023-06-03 00:05:47 -07:00
german77
a862c33fc4 android: Fix fps counter not showing up 2023-06-03 00:05:47 -07:00
Charles Lombardo
ec048361af android: Prevent showing games on an invalid view 2023-06-03 00:05:47 -07:00
Charles Lombardo
b0a434b99f android: Re-implement overlay editing 2023-06-03 00:05:47 -07:00
Charles Lombardo
5807cf1b4d android: Fix popup menu going out of bounds 2023-06-03 00:05:47 -07:00
Charles Lombardo
3878c6ced1 android: Use autofit grid for games fragment 2023-06-03 00:05:47 -07:00
Charles Lombardo
9cb7e7072d android: Prevent updating empty game list text on invalid view 2023-06-03 00:05:47 -07:00
Charles Lombardo
295ffd4d47 android: Persist settings across configuration changes
Mostly things get refactored here to remove previous assumptions made about how the activity/fragment lifecycles would operate. The important change for persistence is removing the assumption that the user will be at the first settings fragment on recreation when deciding whether or not to reload settings. Now we check a flag in Settings to know if we loaded the settings within this lifecycle.
2023-06-03 00:05:47 -07:00
Charles Lombardo
aaefe8a0e0 android: Store settings object in viewmodel 2023-06-03 00:05:47 -07:00
Charles Lombardo
06e58cf088 android: Remove configChanges exceptions 2023-06-03 00:05:46 -07:00
Charles Lombardo
35e9a99452 Android: Enable resizeable activities 2023-06-03 00:05:46 -07:00
Charles Lombardo
89b9285627 android: Fix emulation fragment comments 2023-06-03 00:05:46 -07:00
Charles Lombardo
273e81bb94 android: Use modal navigation drawer as in game menu 2023-06-03 00:05:46 -07:00
Charles Lombardo
1f3b41366c android: Make Game class parcelable 2023-06-03 00:05:46 -07:00
Charles Lombardo
c53e927368 android: Add kotlin parcelize plugin 2023-06-03 00:05:46 -07:00
Charles Lombardo
e2a7143a3d android: Remove deprecated use of onActivityResult 2023-06-03 00:05:46 -07:00
Charles Lombardo
d9e7e71a8e android: Fix RTL layouts 2023-06-03 00:05:46 -07:00
Charles Lombardo
3c9aa8d230 android: Use ellipsis character 2023-06-03 00:05:46 -07:00
Charles Lombardo
16c7afbd46 android: Move all array strings to main strings file 2023-06-03 00:05:46 -07:00
Charles Lombardo
03d9247527 android: Remove unused strings 2023-06-03 00:05:45 -07:00
Charles Lombardo
bf584d85ad android: Remove unused colors 2023-06-03 00:05:45 -07:00
Charles Lombardo
9a842deba5 android: Remove citra date time picker 2023-06-03 00:05:45 -07:00
Charles Lombardo
87211c8aec android: Remove unused premium header layout 2023-06-03 00:05:45 -07:00
Charles Lombardo
1c93ac8e03 android: Remove unused fragment animations 2023-06-03 00:05:45 -07:00
Charles Lombardo
27ec749bdf android: Remove unused string arrays 2023-06-03 00:05:45 -07:00
Charles Lombardo
dafa6dff07 android: Remove unused integer xmls 2023-06-03 00:05:45 -07:00
Charles Lombardo
74653f1e80 android: Refactor ic_launcher.xml to drawables 2023-06-03 00:05:45 -07:00
Charles Lombardo
8a34e58ad6 android: Suppress lint in InsetsHelper 2023-06-03 00:05:45 -07:00
Charles Lombardo
1b40a3df19 android: Add data extraction rules 2023-06-03 00:05:44 -07:00
Charles Lombardo
93d6a1fc9c android: Remove requestLegacyExternalStorage attribute 2023-06-03 00:05:44 -07:00
Charles Lombardo
c803d9e5c7 android: Remove unused permissions 2023-06-03 00:05:44 -07:00
Charles Lombardo
d3c3b69755 android: Inset input overlay based on system cutouts 2023-06-03 00:05:44 -07:00
Narr the Reg
3f35b34515 Use yuzu as category instead of citra 2023-06-03 00:05:44 -07:00
Charles Lombardo
1634391bff android: Stop updating fps counter when emulation stops 2023-06-03 00:05:44 -07:00
Charles Lombardo
a49a24b079 android: Move driver installation off of main thread
Additionally creates an indeterminate loading dialog during installation
2023-06-03 00:05:44 -07:00
Charles Lombardo
72bef4fa95 android: Fix crash when decodeGameIcon creates a null Bitmap 2023-06-03 00:05:44 -07:00
Charles Lombardo
72679c7bae android: Use view binding 2023-06-03 00:05:44 -07:00
Charles Lombardo
e49e6cac7e android: Enable view binding 2023-06-03 00:05:44 -07:00
Charles Lombardo
4de3abdd5a android: Refactor CheckBoxSetting to SwitchSetting 2023-06-03 00:05:44 -07:00
bunnei
b5b4e50c32 android: EmulationActivity: Fix variable shadowing in fragment creation. 2023-06-03 00:05:43 -07:00
bunnei
f45a0b94d6 android: res: fragment_emulation: Ensure FPS counter is shown. 2023-06-03 00:05:43 -07:00
Liam
b19754c73f common: link libandroid on android 2023-06-03 00:05:43 -07:00
Liam
ee10cdad35 cmake: download architecture-specific ffmpeg for android 2023-06-03 00:05:43 -07:00
Liam
616cf70a80 build: only enable adrenotools on arm64 2023-06-03 00:05:43 -07:00
Charles Lombardo
515f3deea1 android: Use Skyline's document provider 2023-06-03 00:05:43 -07:00
Charles Lombardo
55e4c2d87b android: Use androidx splash screen 2023-06-03 00:05:43 -07:00
Charles Lombardo
3fcc6b1104 android: Replace Picasso with Coil 2023-06-03 00:05:43 -07:00
Charles Lombardo
37cc94526b android: New swipe to refresh color scheme 2023-06-03 00:05:43 -07:00
Charles Lombardo
352559b83d android: New settings fragment animations 2023-06-03 00:05:43 -07:00
Charles Lombardo
f40059e4ba android: Use edge to edge 2023-06-03 00:05:43 -07:00
Charles Lombardo
5840d60724 android: Use Material 3 components 2023-06-03 00:05:42 -07:00
Charles Lombardo
18f4ef436d android: Modernize theme system 2023-06-03 00:05:42 -07:00
Charles Lombardo
09780c76aa android: Use vector icons 2023-06-03 00:05:42 -07:00
Charles Lombardo
527229c8b3 android: Use adaptive icon 2023-06-03 00:05:42 -07:00
bunnei
c385b2b07b android: settings: Dynamically evaluate valueAsString
Co-Authored-By: bunnei <bunneidev@gmail.com>
2023-06-03 00:05:42 -07:00
Charles Lombardo
2d934720f5 android: Add license identifier 2023-06-03 00:05:42 -07:00
Charles Lombardo
95af1b2a23 android: Convert YuzuApplication to Kotlin 2023-06-03 00:05:42 -07:00
Charles Lombardo
1a4de8d213 android: Convert Action1 to Kotlin 2023-06-03 00:05:42 -07:00
Charles Lombardo
7a0d7bb3f3 android: Convert GameViewHolder to Kotlin 2023-06-03 00:05:41 -07:00
Charles Lombardo
8a4eb062e8 android: Remove ThemeUtil 2023-06-03 00:05:41 -07:00
Charles Lombardo
b8eb8bd2b5 android: Convert StartupHandler to Kotlin 2023-06-03 00:05:41 -07:00
Charles Lombardo
de1dff557d android: Convert Log to Kotlin 2023-06-03 00:05:41 -07:00
Charles Lombardo
a05c6deb66 android: Convert GpuDriverMetadata to Kotlin 2023-06-03 00:05:41 -07:00
Charles Lombardo
d2f9cf133d android: Convert GpuDriverHelper to Kotlin 2023-06-03 00:05:41 -07:00
Charles Lombardo
bf0c383024 android: Convert GameIconRequestHandler to Kotlin 2023-06-03 00:05:41 -07:00
Charles Lombardo
5d5233ec32 android: Convert ForegroundService to Kotlin 2023-06-03 00:05:41 -07:00
Charles Lombardo
7b54c2b2e2 android: Convert FileUtil to Kotlin 2023-06-03 00:05:41 -07:00
Charles Lombardo
7fb7f3e83f android: Convert FileBrowserHelper to Kotlin 2023-06-03 00:05:41 -07:00
Charles Lombardo
c9d2d74f1f android: Convert EmulationMenuSettings to Kotlin 2023-06-03 00:05:40 -07:00
Charles Lombardo
2444df2bf4 android: Convert DocumentsTree to Kotlin 2023-06-03 00:05:40 -07:00
Charles Lombardo
711aedeaae android: Convert DirectoryStateReceiver to Kotlin 2023-06-03 00:05:40 -07:00
Charles Lombardo
a8994a57d6 android: Convert DirectoryInitialization to Kotlin 2023-06-03 00:05:40 -07:00
Charles Lombardo
b9f1f70688 android: Convert ControllerMappingHelper to Kotlin 2023-06-03 00:05:40 -07:00
Charles Lombardo
c02a27ebd2 android: Convert BiMap to Kotlin 2023-06-03 00:05:40 -07:00
Charles Lombardo
8710c6e14c android: Convert AddDirectoryHelper to Kotlin 2023-06-03 00:05:40 -07:00
Charles Lombardo
e83de8eefb android: Convert PlatformGamesView to Kotlin 2023-06-03 00:05:40 -07:00
Charles Lombardo
fcce7b898f android: Convert PlatformGamesPresenter to Kotlin 2023-06-03 00:05:40 -07:00
Charles Lombardo
0b2350ad5b android: Convert PlatformGamesFragment to Kotlin 2023-06-03 00:05:40 -07:00
Charles Lombardo
67c2e89d2c android: Convert MainView to Kotlin 2023-06-03 00:05:39 -07:00
Charles Lombardo
0f742b3464 android: Convert MainPresenter to Kotlin 2023-06-03 00:05:39 -07:00
Charles Lombardo
42b3e72e96 android: Convert InputOverlayDrawableJoystick to Kotlin 2023-06-03 00:05:39 -07:00
Charles Lombardo
5c8372a566 android: Convert MainActivity to Kotlin 2023-06-03 00:05:39 -07:00
Charles Lombardo
f4508b255f android: Remove ExampleInstrumentedTest 2023-06-03 00:05:39 -07:00
Charles Lombardo
482d3e0b5f android: Remove TwoPaneOnBackPressedCallback
Leftover UI code for dolphin's cheat system. Removing for now.
2023-06-03 00:05:39 -07:00
Charles Lombardo
d85678a80f android: Convert InputOverlayDrawableDpad to Kotlin 2023-06-03 00:05:39 -07:00
Charles Lombardo
0177e908e9 android: Convert InputOverlayDrawableButton to Kotlin 2023-06-03 00:05:39 -07:00
Charles Lombardo
a1c57de466 android: Convert InputOverlay to Kotlin 2023-06-03 00:05:39 -07:00
Charles Lombardo
096cdc57bb android: Remove DividerItemDecoration
Removed in favor of material components version
2023-06-03 00:05:39 -07:00
Charles Lombardo
24ade95a13 android: Inherit from Material 3 themes
Partially breaks the UI for now but is necessary to use new material components.
2023-06-03 00:05:38 -07:00
Charles Lombardo
b5819594ba android: Convert MinimalDocumentFile to Kotlin 2023-06-03 00:05:38 -07:00
Charles Lombardo
9e8ab499dc android: Convert GameProvider to Kotlin 2023-06-03 00:05:38 -07:00
Charles Lombardo
4ce86a526c android: Convert GameDatabase to Kotlin 2023-06-03 00:05:38 -07:00
Charles Lombardo
bbe5dee9f8 android: Convert Game to Kotlin 2023-06-03 00:05:38 -07:00
Charles Lombardo
66079923ae android: Convert EmulationFragment to Kotlin 2023-06-03 00:05:38 -07:00
Charles Lombardo
0e4256651a android: Convert SettingsFile to Kotlin 2023-06-03 00:05:38 -07:00
Charles Lombardo
a29c615f8d android: Convert SettingsFrameLayout to Kotlin 2023-06-03 00:05:38 -07:00
Charles Lombardo
c39bf17f83 android: Convert SettingsFragmentView to Kotlin 2023-06-03 00:05:38 -07:00
Charles Lombardo
14d156701f android: Convert SettingsFragmentPresenter to Kotlin 2023-06-03 00:05:38 -07:00
Charles Lombardo
1fc66f1b30 android: Convert SettingsFragment to Kotlin 2023-06-03 00:05:37 -07:00
Charles Lombardo
469f0ec019 android: Convert SettingsActivityView to Kotlin 2023-06-03 00:05:37 -07:00
Charles Lombardo
34ce4877bd android: Convert SettingsActivityPresenter to Kotlin 2023-06-03 00:05:37 -07:00
Charles Lombardo
b10e13c090 android: Convert SettingsActivity to Kotlin 2023-06-03 00:05:37 -07:00
Charles Lombardo
ed83650ee4 android: Convert SubmenuViewHolder to Kotlin 2023-06-03 00:05:37 -07:00
Charles Lombardo
6044d924f7 android: Convert SliderViewHolder to Kotlin 2023-06-03 00:05:37 -07:00
Charles Lombardo
fd1801aec4 android: Convert SingleChoiceViewHolder to Kotlin 2023-06-03 00:05:37 -07:00
Charles Lombardo
c42eb92557 android: Convert SettingViewHolder to Kotlin 2023-06-03 00:05:37 -07:00
Charles Lombardo
d472f41580 android: Convert HeaderViewHolder to Kotlin 2023-06-03 00:05:37 -07:00
Charles Lombardo
e02e33826b android: Convert DateTimeViewHolder to Kotlin 2023-06-03 00:05:37 -07:00
Charles Lombardo
3a5b9ecba2 android: Convert CheckBoxSettingViewHolder to Kotlin 2023-06-03 00:05:36 -07:00
Charles Lombardo
f0a9fcf100 android: Convert StringSetting to Kotlin 2023-06-03 00:05:36 -07:00
Charles Lombardo
412ec72d26 android: Convert SettingSection to Kotlin 2023-06-03 00:05:36 -07:00
Charles Lombardo
6f80f9d5b0 android: Convert Setting to Kotlin 2023-06-03 00:05:36 -07:00
Charles Lombardo
6e7fdcb484 android: Convert IntSetting to Kotlin 2023-06-03 00:05:36 -07:00
Charles Lombardo
2439fc8374 android: Convert FloatSetting to Kotlin 2023-06-03 00:05:36 -07:00
Charles Lombardo
88b9d484e8 android: Convert BooleanSetting to Kotlin 2023-06-03 00:05:36 -07:00
Charles Lombardo
b98aaf1635 android: Convert SubmenuSetting to Kotlin 2023-06-03 00:05:36 -07:00
Charles Lombardo
91884976a1 android: Convert StringSingleChoiceSetting to Kotlin 2023-06-03 00:05:36 -07:00
Charles Lombardo
89eed93ce0 android: Convert SliderSetting to Kotlin 2023-06-03 00:05:36 -07:00
Charles Lombardo
a0e91e3a93 android: Convert SingleChoiceSetting to Kotlin 2023-06-03 00:05:35 -07:00
Charles Lombardo
22b44be0b2 android: Convert SettingsItem to Kotlin 2023-06-03 00:05:35 -07:00
Charles Lombardo
537c16d4cf android: Convert HeaderSetting to Kotlin 2023-06-03 00:05:35 -07:00
Charles Lombardo
21841b6520 android: Convert DateTimeSetting to Kotlin 2023-06-03 00:05:35 -07:00
Charles Lombardo
4d9cfc6798 android: Convert CheckBoxSetting to Kotlin 2023-06-03 00:05:35 -07:00
Charles Lombardo
0d044e9f2f android: Convert GameAdapter to Kotlin 2023-06-03 00:05:35 -07:00
Charles Lombardo
87f4c3f105 android: Convert SettingsAdapter to Kotlin
Update SettingsAdapter.kt
2023-06-03 00:05:35 -07:00
Charles Lombardo
39a65f8446 android: Convert EmulationActivity to Kotlin 2023-06-03 00:05:35 -07:00
Charles Lombardo
7cd72a7c6d android: Use material slider in settings dialog 2023-06-03 00:05:35 -07:00
Charles Lombardo
fa38c7be4f android: Convert Settings to Kotlin 2023-06-03 00:05:35 -07:00
Charles Lombardo
753a0c6b5d android: Use androidx preferences 2023-06-03 00:05:34 -07:00
bunnei
2dfbfadf82 android: frontend: Add unique error strings for Vulkan initialization errors. 2023-06-03 00:05:34 -07:00
german77
bde568c3c5 android: Use the center of the object and reduce draw calls 2023-06-03 00:05:34 -07:00
german77
7dd02363a3 android: Replace old buttons with vectors 2023-06-03 00:05:34 -07:00
Charles Lombardo
834d53fbbf android: Enable Kotlin support 2023-06-03 00:05:34 -07:00
Charles Lombardo
96ea063b45 android: Upgrade java version to 11 2023-06-03 00:05:33 -07:00
Charles Lombardo
9191ae23ec android: Upgrade dependencies 2023-06-03 00:05:33 -07:00
Charles Lombardo
82dca7e586 android: Upgrade to AGP 7.4.2 2023-06-03 00:05:33 -07:00
Charles Lombardo
fc785972f9 android: Replace lintOptions with lint 2023-06-03 00:05:33 -07:00
Charles Lombardo
9507e99165 android: Move namespace to app module build.gradle 2023-06-03 00:05:33 -07:00
Charles Lombardo
fc0c5fa86f android: bump compile/target sdk to 33 2023-06-03 00:05:33 -07:00
Charles Lombardo
9762646112 android: Upgrade gradle to 8.0.1 2023-06-03 00:05:33 -07:00
liushuyu
e26bd1421e video_core: fix clang-format errors 2023-06-03 00:05:33 -07:00
liushuyu
19eec22b38 CMake: fix pkg-config behavior when building for Android 2023-06-03 00:05:33 -07:00
liushuyu
1d0329a065 CI: add Android build systems 2023-06-03 00:05:33 -07:00
bunnei
26ee6844c2 android: build.gradle: Cleanup build types. 2023-06-03 00:05:32 -07:00
bunnei
4769d716fc android: frontend: settings: Add graphics debugging. 2023-06-03 00:05:32 -07:00
bunnei
0276197744 android: jni: Ensure system is only initialized once.
- Fixes likelihood that fastmem allocation succeeds.
2023-06-03 00:05:32 -07:00
bunnei
ff2f370946 video_core: vulkan_device: Correct error message for unsuitable driver. 2023-06-03 00:05:32 -07:00
bunnei
dcbf0c43c0 android: frontend: Cleanup framerate counter. 2023-06-03 00:05:32 -07:00
bunnei
8248d69093 android: vulkan: Implement adrenotools turbo mode. 2023-06-03 00:05:32 -07:00
bunnei
21320d80d9 android: vulkan_device: Disable VK_EXT_extended_dynamic_state2 on Qualcomm.
- Newer drivers report this as supported, but it is broken.
2023-06-03 00:05:32 -07:00
bunnei
b0f8aef057 android: frontend: Add support for GPU driver selection. 2023-06-03 00:05:32 -07:00
bunnei
4c38220a64 android: native: Add support for custom Vulkan driver loading. 2023-06-03 00:05:31 -07:00
bunnei
ae099d583c core: frontend: Refactor GraphicsContext to its own module. 2023-06-03 00:05:31 -07:00
bunnei
32cf6beee3 common: dynamic_library: Add ctor for existing handle. 2023-06-03 00:05:31 -07:00
bunnei
e9f35d3260 android: EmulationFragment: Always reset overlay.
- Ensures correct placement until we have better overlay configuration.
2023-06-03 00:05:31 -07:00
Billy Laws
b4a12b889e Avoid using VectorExtractDynamic for subgroup mask on Adreno GPUs
This crashes their shader compiler for some reason.
2023-06-03 00:05:31 -07:00
Billy Laws
158a1896ec Implement scaled vertex buffer format emulation
These formats are unsupported by mobile GPUs so they need to be emulated in shaders instead.
2023-06-03 00:05:31 -07:00
Billy Laws
206f1304d6 Disable push descriptors on adreno drivers
Regular descriptors are around 1.5x faster to update.
2023-06-03 00:05:31 -07:00
Billy Laws
26bdecbf45 Disable VK_EXT_extended_dynamic_state on mali 2023-06-03 00:05:31 -07:00
Billy Laws
a3c261d940 Disable multithreaded pipeline compilation on Qualcomm drivers
This causes crashes during compilation on several 6xx and 5xx driver versions.
2023-06-03 00:05:31 -07:00
Narr the Reg
f1bb2f3685 android: Add motion sensor 2023-06-03 00:05:30 -07:00
Narr the Reg
92fb7cc4e4 android: Hook jni input properly 2023-06-03 00:05:30 -07:00
Narr the Reg
5b80dee181 android: cleanup touch update loop 2023-06-03 00:05:30 -07:00
Narr the Reg
3be891ea6f android: Clean joystick overlay 2023-06-03 00:05:30 -07:00
Narr the Reg
639a1f885c android: Clean dpad overlay 2023-06-03 00:05:30 -07:00
Narr the Reg
1ab269775d android: Clean button overlay 2023-06-03 00:05:30 -07:00
Narr the Reg
43e43021a3 android: Add all buttons to screen controller 2023-06-03 00:05:30 -07:00
Narr the Reg
58531ecf4f android: Apply clang format 2023-06-03 00:05:30 -07:00
bunnei
0e52d11ede android: frontend: Implement game grid view. (#9) 2023-06-03 00:05:30 -07:00
german77
5ed8d46340 android: Replace notification icon with yuzu 2023-06-03 00:05:30 -07:00
bunnei
7a89c2fe3a android: strings: Refresh key dumping URL. 2023-06-03 00:05:29 -07:00
bunnei
ddf10cdb18 android: frontend: Modify ROM load messaging for invalid keys. 2023-06-03 00:05:29 -07:00
bunnei
93cf8c3090 android: frontend: Integrate key installation for SAF. 2023-06-03 00:05:29 -07:00
bunnei
63a98e3e1c android: jni: Add function to reload keys. 2023-06-03 00:05:29 -07:00
bunnei
93bad47edb core: crypto: key_manager: Add methods to reload & validate keys. 2023-06-03 00:05:29 -07:00
bunnei
f33776af67 android: EmulationActivity: Temporarily disable running notification. 2023-06-03 00:05:29 -07:00
bunnei
ef605f7d8f android: Implement SAF support & migrate to SDK 31. (#4) 2023-06-03 00:05:29 -07:00
bunnei
39ab81a098 android: Harden emulation shutdown when loader fails. 2023-06-03 00:05:29 -07:00
bunnei
e12e1efa40 android: SettingsFragmentPresenter: Fix default renderer backend. 2023-06-03 00:05:29 -07:00
bunnei
d6a41b3290 android: jni: native: Add lock around HaltEmulation, tighten run loop. 2023-06-03 00:05:29 -07:00
bunnei
d553fd4c3a android: jni: native: Refactor locking for is_running. 2023-06-03 00:05:28 -07:00
bunnei
104ff475d2 android: jni: native: Remove unnecessary atomic for is_running. 2023-06-03 00:05:28 -07:00
bunnei
9ba67eab4f android: jni: native: Tighten up emulation start/stop signaling. 2023-06-03 00:05:28 -07:00
bunnei
1e94d16dad android: jni: native: Consolidate emulation state into EmulationSession singleton.
- Fixes state management issues across multiple boots.
- Fixes crashes related to unsafe access of perf stats.
2023-06-03 00:05:28 -07:00
bunnei
6cc21a56d9 android: Frontend: Fix rendering aspect ratio & add a setting for it. 2023-06-03 00:05:28 -07:00
bunnei
4f903d8d35 android: Integrate settings frontend with yuzu & remove unused code. 2023-06-03 00:05:28 -07:00
Liam
f7a3f1ddf4 externals: add adrenotools for bcenabler 2023-06-03 00:05:28 -07:00
Liam
7cdeaa90af device_memory: Use smaller virtual reservation size for compatibility with 39-bit paging 2023-06-03 00:05:28 -07:00
bunnei
2972a3ccc7 video_core: vulkan_device: Device initialization for Adreno. 2023-06-03 00:05:28 -07:00
bunnei
91350524c2 video_core: vk_pipeline_cache: Disable support_descriptor_aliasing on Android. 2023-06-03 00:05:28 -07:00
bunnei
6ed62a9f10 video_core: vk_swapchain: Fix image format for Android. 2023-06-03 00:05:28 -07:00
bunnei
5e198d1421 android: Minimize frontend & convert to yuzu. 2023-06-03 00:05:27 -07:00
bunnei
18527a8c42 video_core: vk_blit_screen: Rotate viewport for Android landscape. 2023-06-03 00:05:27 -07:00
bunnei
cdbab60bbb common: error: Fix for Android. 2023-06-03 00:05:27 -07:00
bunnei
f8b87e6fab common: fs: Implement for Android. 2023-06-03 00:05:27 -07:00
bunnei
99296a1510 common: logging: Implement Android logcat backend. 2023-06-03 00:05:26 -07:00
bunnei
afdee9abea common: host_memory: Implement for Android. 2023-06-03 00:05:26 -07:00
bunnei
e6d5dbb58e android: Minimal JNI for yuzu. 2023-06-03 00:05:26 -07:00
bunnei
bb2cbbfba3 android: Add Citra frontend. 2023-06-03 00:05:26 -07:00
bunnei
851b1008a8 cmake: Integrate bundled FFmpeg for Android. 2023-06-03 00:05:26 -07:00
bunnei
5de8ee7bba cmake: Integrate submoduled LLVM & fixes for Android. 2023-06-03 00:05:26 -07:00
bunnei
e931bb8c44 Merge pull request #10575 from bm01/issue-template
issue_template: Add link to website, Getting Log Files
2023-06-02 23:58:04 -07:00
ameerj
1fc47361a1 texture_cache: Fix incorrect logic for AccelerateDMA 2023-06-02 18:07:52 -04:00
Baptiste Marie
206fe987e1 issue_template: Add link to website, Getting Log Files 2023-06-02 17:21:46 +02:00
kkoniuszy
584e8b5c52 host_memory: merge adjacent placeholder mappings on Linux
Track the private anonymous placeholder mappings created by Unmap() and
wherever possible, replace existing placeholders with larger ones
instead of creating many small ones.

This helps with the buildup of mappings in /proc/YUZU_PID/maps after a
longer gaming session, improving stability without having to increase
vm.max_map_count to a ridiculous value. The amount of placeholder
mappings will no longer outgrow the amount of actual memfd mappings in
cases of high memory fragmentation.
2023-06-01 22:57:27 +02:00
ameerj
cb0a410907 gl_staging_buffers: Optimization to reduce fence waiting 2023-05-28 00:38:47 -04:00
ameerj
642c14f0c7 OpenGL: Make use of persistent buffer maps in buffer cache downloads
Persistent buffer maps were already used by the texture cache, this extends their usage for the buffer cache.

In my testing, using the memory maps for uploads was slower than the existing "ImmediateUpload" path, so the memory map usage is limited to downloads for the time being.
2023-05-28 00:38:46 -04:00
EBADBEEF
a84ad180e8 qt: add option to disable controller applet
- add checkbox to disable the controller applet UI
- when controller applet is disabled, use the yuzu-cmd fallback
  controller applet that applies controller config based on rules
- See https://github.com/yuzu-emu/yuzu/issues/8552 for some discussion
2023-01-22 23:36:40 -08:00
445 changed files with 16373 additions and 4752 deletions

View File

@@ -2,5 +2,5 @@
; SPDX-License-Identifier: GPL-2.0-or-later
[codespell]
skip = ./.git,./build,./dist,./Doxyfile,./externals,./LICENSES
ignore-words-list = aci,allright,ba,deques,froms,hda,inout,lod,masia,nam,nax,nd,pullrequests,pullrequest,te,transfered,unstall,uscaled,zink
skip = ./.git,./build,./dist,./Doxyfile,./externals,./LICENSES,./src/android/app/src/main/res
ignore-words-list = aci,allright,ba,deques,froms,hda,inout,lod,masia,nam,nax,nd,optin,pullrequests,pullrequest,te,transfered,unstall,uscaled,zink

View File

@@ -43,7 +43,7 @@ body:
id: log
attributes:
label: Log File
description: A log file will help our developers to better diagnose and fix the issue.
description: A log file will help our developers to better diagnose and fix the issue. Instructions can be found [here](https://yuzu-emu.org/help/reference/log-files).
validations:
required: true
- type: textarea

View File

@@ -129,6 +129,11 @@ jobs:
- uses: actions/checkout@v3
with:
submodules: recursive
- name: set up JDK 17
uses: actions/setup-java@v3
with:
java-version: '17'
distribution: 'adopt'
- name: Set up cache
uses: actions/cache@v3
with:
@@ -160,24 +165,3 @@ jobs:
with:
name: android
path: artifacts/
release:
runs-on: ubuntu-latest
needs: [ android ]
if: ${{ startsWith(github.ref, 'refs/tags/') }}
steps:
- uses: actions/download-artifact@v3
- name: Create release
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: ${{ github.ref_name }}
release_name: ${{ github.ref_name }}
draft: false
prerelease: false
- name: Upload artifacts
uses: alexellis/upload-assets@0.4.0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
asset_paths: '["./**/*.tar.*","./**/*.AppImage","./**/*.7z","./**/*.zip","./**/*.apk","./**/*.aab"]'

2
.gitignore vendored
View File

@@ -26,6 +26,8 @@ CMakeSettings.json
# OSX global filetypes
# Created by Finder or Spotlight in directories for various OS functionality (indexing, etc)
.DS_Store
.DS_Store?
._*
.AppleDouble
.LSOverride
.Spotlight-V100

48
.gitmodules vendored
View File

@@ -2,35 +2,35 @@
# SPDX-License-Identifier: GPL-2.0-or-later
[submodule "enet"]
path = externals/enet
url = https://github.com/lsalzman/enet.git
path = externals/enet
url = https://github.com/lsalzman/enet.git
[submodule "inih"]
path = externals/inih/inih
url = https://github.com/benhoyt/inih.git
path = externals/inih/inih
url = https://github.com/benhoyt/inih.git
[submodule "cubeb"]
path = externals/cubeb
url = https://github.com/mozilla/cubeb.git
path = externals/cubeb
url = https://github.com/mozilla/cubeb.git
[submodule "dynarmic"]
path = externals/dynarmic
url = https://github.com/MerryMage/dynarmic.git
path = externals/dynarmic
url = https://github.com/merryhime/dynarmic.git
[submodule "libusb"]
path = externals/libusb/libusb
url = https://github.com/libusb/libusb.git
[submodule "discord-rpc"]
path = externals/discord-rpc
url = https://github.com/yuzu-emu/discord-rpc.git
path = externals/discord-rpc
url = https://github.com/yuzu-emu/discord-rpc.git
[submodule "Vulkan-Headers"]
path = externals/Vulkan-Headers
url = https://github.com/KhronosGroup/Vulkan-Headers.git
path = externals/Vulkan-Headers
url = https://github.com/KhronosGroup/Vulkan-Headers.git
[submodule "sirit"]
path = externals/sirit
url = https://github.com/yuzu-emu/sirit
path = externals/sirit
url = https://github.com/yuzu-emu/sirit.git
[submodule "mbedtls"]
path = externals/mbedtls
url = https://github.com/yuzu-emu/mbedtls
path = externals/mbedtls
url = https://github.com/yuzu-emu/mbedtls.git
[submodule "xbyak"]
path = externals/xbyak
url = https://github.com/herumi/xbyak.git
path = externals/xbyak
url = https://github.com/herumi/xbyak.git
[submodule "opus"]
path = externals/opus/opus
url = https://github.com/xiph/opus.git
@@ -45,10 +45,16 @@
url = https://github.com/FFmpeg/FFmpeg.git
[submodule "vcpkg"]
path = externals/vcpkg
url = https://github.com/Microsoft/vcpkg.git
url = https://github.com/microsoft/vcpkg.git
[submodule "cpp-jwt"]
path = externals/cpp-jwt
url = https://github.com/arun11299/cpp-jwt.git
[submodule "externals/libadrenotools"]
[submodule "libadrenotools"]
path = externals/libadrenotools
url = https://github.com/bylaws/libadrenotools
url = https://github.com/bylaws/libadrenotools.git
[submodule "tzdb_to_nx"]
path = externals/nx_tzdb/tzdb_to_nx
url = https://github.com/lat9nq/tzdb_to_nx.git
[submodule "VulkanMemoryAllocator"]
path = externals/vma/VulkanMemoryAllocator
url = https://github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator.git

View File

@@ -59,6 +59,8 @@ option(YUZU_CHECK_SUBMODULES "Check if submodules are present" ON)
option(YUZU_ENABLE_LTO "Enable link-time optimization" OFF)
option(YUZU_DOWNLOAD_TIME_ZONE_DATA "Always download time zone binaries" OFF)
CMAKE_DEPENDENT_OPTION(YUZU_USE_FASTER_LD "Check if a faster linker is available" ON "NOT WIN32" OFF)
# On Android, fetch and compile libcxx before doing anything else
@@ -255,7 +257,7 @@ endif()
# boost asio's concept usage doesn't play nicely with some compilers yet.
add_definitions(-DBOOST_ASIO_DISABLE_CONCEPTS)
if (MSVC)
add_compile_options($<$<COMPILE_LANGUAGE:CXX>:/std:c++latest>)
add_compile_options($<$<COMPILE_LANGUAGE:CXX>:/std:c++20>)
# boost still makes use of deprecated result_of.
add_definitions(-D_HAS_DEPRECATED_RESULT_OF)
@@ -487,7 +489,7 @@ if (ENABLE_SDL2)
if (YUZU_USE_BUNDLED_SDL2)
# Detect toolchain and platform
if ((MSVC_VERSION GREATER_EQUAL 1920 AND MSVC_VERSION LESS 1940) AND ARCHITECTURE_x86_64)
set(SDL2_VER "SDL2-2.0.18")
set(SDL2_VER "SDL2-2.28.0")
else()
message(FATAL_ERROR "No bundled SDL2 binaries for your toolchain. Disable YUZU_USE_BUNDLED_SDL2 and provide your own.")
endif()
@@ -507,7 +509,7 @@ if (ENABLE_SDL2)
elseif (YUZU_USE_EXTERNAL_SDL2)
message(STATUS "Using SDL2 from externals.")
else()
find_package(SDL2 2.0.18 REQUIRED)
find_package(SDL2 2.26.4 REQUIRED)
endif()
endif()

View File

@@ -13,7 +13,7 @@ SPDX-License-Identifier: GPL-2.0-or-later
<h4 align="center"><b>yuzu</b> is the world's most popular, open-source, Nintendo Switch emulator — started by the creators of <a href="https://citra-emu.org" target="_blank">Citra</a>.
<br>
It is written in C++ with portability in mind, and we actively maintain builds for Windows and Linux.
It is written in C++ with portability in mind, and we actively maintain builds for Windows, Linux and Android.
</h4>
<p align="center">

View File

@@ -63,8 +63,9 @@ if (YUZU_USE_EXTERNAL_SDL2)
# Yuzu itself needs: Atomic Audio Events Joystick Haptic Sensor Threads Timers
# Since 2.0.18 Atomic+Threads required for HIDAPI/libusb (see https://github.com/libsdl-org/SDL/issues/5095)
# Yuzu-cmd also needs: Video (depends on Loadso/Dlopen)
# CPUinfo also required for SDL Audio, at least until 2.28.0 (see https://github.com/libsdl-org/SDL/issues/7809)
set(SDL_UNUSED_SUBSYSTEMS
CPUinfo File Filesystem
File Filesystem
Locale Power Render)
foreach(_SUB ${SDL_UNUSED_SUBSYSTEMS})
string(TOUPPER ${_SUB} _OPT)
@@ -139,6 +140,14 @@ if (YUZU_USE_EXTERNAL_VULKAN_HEADERS)
add_subdirectory(Vulkan-Headers)
endif()
# TZDB (Time Zone Database)
add_subdirectory(nx_tzdb)
# VMA
add_library(vma vma/vma.cpp)
target_include_directories(vma PUBLIC ./vma/VulkanMemoryAllocator/include)
target_link_libraries(vma PRIVATE Vulkan::Headers)
if (NOT TARGET LLVM::Demangle)
add_library(demangle demangle/ItaniumDemangle.cpp)
target_include_directories(demangle PUBLIC ./demangle)
@@ -148,6 +157,9 @@ endif()
add_library(stb stb/stb_dxt.cpp)
target_include_directories(stb PUBLIC ./stb)
add_library(bc_decoder bc_decoder/bc_decoder.cpp)
target_include_directories(bc_decoder PUBLIC ./bc_decoder)
if (ANDROID)
if (ARCHITECTURE_arm64)
add_subdirectory(libadrenotools)

2
externals/SDL vendored

1522
externals/bc_decoder/bc_decoder.cpp vendored Normal file

File diff suppressed because it is too large Load Diff

43
externals/bc_decoder/bc_decoder.h vendored Normal file
View File

@@ -0,0 +1,43 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright © 2022 Skyline Team and Contributors (https://github.com/skyline-emu/)
#pragma once
#include <cstdint>
namespace bcn {
/**
* @brief Decodes a BC1 encoded image to R8G8B8A8
*/
void DecodeBc1(const uint8_t *src, uint8_t *dst, size_t x, size_t y, size_t width, size_t height);
/**
* @brief Decodes a BC2 encoded image to R8G8B8A8
*/
void DecodeBc2(const uint8_t *src, uint8_t *dst, size_t x, size_t y, size_t width, size_t height);
/**
* @brief Decodes a BC3 encoded image to R8G8B8A8
*/
void DecodeBc3(const uint8_t *src, uint8_t *dst, size_t x, size_t y, size_t width, size_t height);
/**
* @brief Decodes a BC4 encoded image to R8
*/
void DecodeBc4(const uint8_t *src, uint8_t *dst, size_t x, size_t y, size_t width, size_t height, bool isSigned);
/**
* @brief Decodes a BC5 encoded image to R8G8
*/
void DecodeBc5(const uint8_t *src, uint8_t *dst, size_t x, size_t y, size_t width, size_t height, bool isSigned);
/**
* @brief Decodes a BC6 encoded image to R16G16B16A16
*/
void DecodeBc6(const uint8_t *src, uint8_t *dst, size_t x, size_t y, size_t width, size_t height, bool isSigned);
/**
* @brief Decodes a BC7 encoded image to R8G8B8A8
*/
void DecodeBc7(const uint8_t *src, uint8_t *dst, size_t x, size_t y, size_t width, size_t height);
}

101
externals/nx_tzdb/CMakeLists.txt vendored Normal file
View File

@@ -0,0 +1,101 @@
# SPDX-FileCopyrightText: 2023 yuzu Emulator Project
# SPDX-License-Identifier: GPL-2.0-or-later
set(NX_TZDB_INCLUDE_DIR "${CMAKE_CURRENT_BINARY_DIR}/include")
add_library(nx_tzdb INTERFACE)
find_program(GIT git)
find_program(GNU_MAKE make)
find_program(DATE_PROG date)
set(CAN_BUILD_NX_TZDB true)
if (NOT GIT)
set(CAN_BUILD_NX_TZDB false)
endif()
if (NOT GNU_MAKE)
set(CAN_BUILD_NX_TZDB false)
endif()
if (NOT DATE_PROG)
set(CAN_BUILD_NX_TZDB false)
endif()
if (CMAKE_SYSTEM_NAME STREQUAL "Windows" OR ANDROID)
# tzdb_to_nx currently requires a posix-compliant host
# MinGW and Android are handled here due to the executable format being different from the host system
# TODO (lat9nq): cross-compiling support
set(CAN_BUILD_NX_TZDB false)
endif()
set(NX_TZDB_VERSION "220816")
set(NX_TZDB_ARCHIVE "${CMAKE_CURRENT_BINARY_DIR}/${NX_TZDB_VERSION}.zip")
set(NX_TZDB_ROMFS_DIR "${CMAKE_CURRENT_BINARY_DIR}/nx_tzdb")
if ((NOT CAN_BUILD_NX_TZDB OR YUZU_DOWNLOAD_TIME_ZONE_DATA) AND NOT EXISTS ${NX_TZDB_ARCHIVE})
set(NX_TZDB_DOWNLOAD_URL "https://github.com/lat9nq/tzdb_to_nx/releases/download/${NX_TZDB_VERSION}/${NX_TZDB_VERSION}.zip")
message(STATUS "Downloading time zone data from ${NX_TZDB_DOWNLOAD_URL}...")
file(DOWNLOAD ${NX_TZDB_DOWNLOAD_URL} ${NX_TZDB_ARCHIVE}
STATUS NX_TZDB_DOWNLOAD_STATUS)
list(GET NX_TZDB_DOWNLOAD_STATUS 0 NX_TZDB_DOWNLOAD_STATUS_CODE)
if (NOT NX_TZDB_DOWNLOAD_STATUS_CODE EQUAL 0)
message(FATAL_ERROR "Time zone data download failed (status code ${NX_TZDB_DOWNLOAD_STATUS_CODE})")
endif()
file(ARCHIVE_EXTRACT
INPUT
${NX_TZDB_ARCHIVE}
DESTINATION
${NX_TZDB_ROMFS_DIR})
elseif (CAN_BUILD_NX_TZDB AND NOT YUZU_DOWNLOAD_TIME_ZONE_DATA)
add_subdirectory(tzdb_to_nx)
add_dependencies(nx_tzdb x80e)
set(NX_TZDB_ROMFS_DIR "${NX_TZDB_DIR}")
endif()
target_include_directories(nx_tzdb
INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/include
INTERFACE ${NX_TZDB_INCLUDE_DIR})
function(CreateHeader ZONE_PATH HEADER_NAME)
set(HEADER_PATH "${NX_TZDB_INCLUDE_DIR}/nx_tzdb/${HEADER_NAME}.h")
add_custom_command(
OUTPUT
${NX_TZDB_INCLUDE_DIR}/nx_tzdb/${HEADER_NAME}.h
COMMAND
${CMAKE_COMMAND} -P ${CMAKE_CURRENT_SOURCE_DIR}/NxTzdbCreateHeader.cmake
${ZONE_PATH}
${HEADER_NAME}
${NX_TZDB_INCLUDE_DIR}
${CMAKE_CURRENT_SOURCE_DIR}
DEPENDS
tzdb_template.h.in
NxTzdbCreateHeader.cmake)
target_sources(nx_tzdb PRIVATE ${HEADER_PATH})
endfunction()
CreateHeader(${NX_TZDB_ROMFS_DIR} base)
CreateHeader(${NX_TZDB_ROMFS_DIR}/zoneinfo zoneinfo)
CreateHeader(${NX_TZDB_ROMFS_DIR}/zoneinfo/Africa africa)
CreateHeader(${NX_TZDB_ROMFS_DIR}/zoneinfo/America america)
CreateHeader(${NX_TZDB_ROMFS_DIR}/zoneinfo/America/Argentina america_argentina)
CreateHeader(${NX_TZDB_ROMFS_DIR}/zoneinfo/America/Indiana america_indiana)
CreateHeader(${NX_TZDB_ROMFS_DIR}/zoneinfo/America/Kentucky america_kentucky)
CreateHeader(${NX_TZDB_ROMFS_DIR}/zoneinfo/America/North_Dakota america_north_dakota)
CreateHeader(${NX_TZDB_ROMFS_DIR}/zoneinfo/Antarctica antarctica)
CreateHeader(${NX_TZDB_ROMFS_DIR}/zoneinfo/Arctic arctic)
CreateHeader(${NX_TZDB_ROMFS_DIR}/zoneinfo/Asia asia)
CreateHeader(${NX_TZDB_ROMFS_DIR}/zoneinfo/Atlantic atlantic)
CreateHeader(${NX_TZDB_ROMFS_DIR}/zoneinfo/Australia australia)
CreateHeader(${NX_TZDB_ROMFS_DIR}/zoneinfo/Brazil brazil)
CreateHeader(${NX_TZDB_ROMFS_DIR}/zoneinfo/Canada canada)
CreateHeader(${NX_TZDB_ROMFS_DIR}/zoneinfo/Chile chile)
CreateHeader(${NX_TZDB_ROMFS_DIR}/zoneinfo/Etc etc)
CreateHeader(${NX_TZDB_ROMFS_DIR}/zoneinfo/Europe europe)
CreateHeader(${NX_TZDB_ROMFS_DIR}/zoneinfo/Indian indian)
CreateHeader(${NX_TZDB_ROMFS_DIR}/zoneinfo/Mexico mexico)
CreateHeader(${NX_TZDB_ROMFS_DIR}/zoneinfo/Pacific pacific)
CreateHeader(${NX_TZDB_ROMFS_DIR}/zoneinfo/US us)

View File

@@ -0,0 +1,8 @@
# SPDX-FileCopyrightText: 2023 yuzu Emulator Project
# SPDX-License-Identifier: GPL-2.0-or-later
# CMake does not have a way to list the files in a specific directory,
# so we need this script to do that for us in a platform-agnostic fashion
file(GLOB FILE_LIST LIST_DIRECTORIES false RELATIVE ${CMAKE_SOURCE_DIR} "*")
execute_process(COMMAND ${CMAKE_COMMAND} -E echo "${FILE_LIST};")

View File

@@ -0,0 +1,46 @@
# SPDX-FileCopyrightText: 2023 yuzu Emulator Project
# SPDX-License-Identifier: GPL-2.0-or-later
set(ZONE_PATH ${CMAKE_ARGV3})
set(HEADER_NAME ${CMAKE_ARGV4})
set(NX_TZDB_INCLUDE_DIR ${CMAKE_ARGV5})
set(NX_TZDB_SOURCE_DIR ${CMAKE_ARGV6})
execute_process(
COMMAND ${CMAKE_COMMAND} -P ${NX_TZDB_SOURCE_DIR}/ListFilesInDirectory.cmake
WORKING_DIRECTORY ${ZONE_PATH}
OUTPUT_VARIABLE FILE_LIST)
set(DIRECTORY_NAME ${HEADER_NAME})
set(FILE_DATA "")
foreach(ZONE_FILE ${FILE_LIST})
if (ZONE_FILE STREQUAL "\n")
continue()
endif()
string(APPEND FILE_DATA "{\"${ZONE_FILE}\",\n{")
file(READ ${ZONE_PATH}/${ZONE_FILE} ZONE_DATA HEX)
string(LENGTH "${ZONE_DATA}" ZONE_DATA_LEN)
foreach(I RANGE 0 ${ZONE_DATA_LEN} 2)
math(EXPR BREAK_LINE "(${I} + 2) % 38")
string(SUBSTRING "${ZONE_DATA}" "${I}" 2 HEX_DATA)
if (NOT HEX_DATA)
break()
endif()
string(APPEND FILE_DATA "0x${HEX_DATA},")
if (BREAK_LINE EQUAL 0)
string(APPEND FILE_DATA "\n")
else()
string(APPEND FILE_DATA " ")
endif()
endforeach()
string(APPEND FILE_DATA "}},\n")
endforeach()
file(READ ${NX_TZDB_SOURCE_DIR}/tzdb_template.h.in NX_TZDB_TEMPLATE_H_IN)
file(CONFIGURE OUTPUT ${NX_TZDB_INCLUDE_DIR}/nx_tzdb/${HEADER_NAME}.h CONTENT "${NX_TZDB_TEMPLATE_H_IN}")

27
externals/nx_tzdb/include/nx_tzdb.h vendored Normal file
View File

@@ -0,0 +1,27 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "nx_tzdb/africa.h"
#include "nx_tzdb/america.h"
#include "nx_tzdb/america_argentina.h"
#include "nx_tzdb/america_indiana.h"
#include "nx_tzdb/america_kentucky.h"
#include "nx_tzdb/america_north_dakota.h"
#include "nx_tzdb/antarctica.h"
#include "nx_tzdb/arctic.h"
#include "nx_tzdb/asia.h"
#include "nx_tzdb/atlantic.h"
#include "nx_tzdb/australia.h"
#include "nx_tzdb/base.h"
#include "nx_tzdb/brazil.h"
#include "nx_tzdb/canada.h"
#include "nx_tzdb/chile.h"
#include "nx_tzdb/etc.h"
#include "nx_tzdb/europe.h"
#include "nx_tzdb/indian.h"
#include "nx_tzdb/mexico.h"
#include "nx_tzdb/pacific.h"
#include "nx_tzdb/us.h"
#include "nx_tzdb/zoneinfo.h"

18
externals/nx_tzdb/tzdb_template.h.in vendored Normal file
View File

@@ -0,0 +1,18 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <cstdint>
#include <map>
#include <vector>
namespace NxTzdb {
// clang-format off
const static std::map<const char*, const std::vector<uint8_t>> @DIRECTORY_NAME@ =
{
@FILE_DATA@};
// clang-format on
} // namespace NxTzdb

1
externals/nx_tzdb/tzdb_to_nx vendored Submodule

8
externals/vma/vma.cpp vendored Normal file
View File

@@ -0,0 +1,8 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#define VMA_IMPLEMENTATION
#define VMA_STATIC_VULKAN_FUNCTIONS 0
#define VMA_DYNAMIC_VULKAN_FUNCTIONS 1
#include <vk_mem_alloc.h>

View File

@@ -43,7 +43,7 @@ if (MSVC)
/Zo
/permissive-
/EHsc
/std:c++latest
/std:c++20
/utf-8
/volatile:iso
/Zc:externConstexpr
@@ -51,8 +51,10 @@ if (MSVC)
/Zc:throwingNew
/GT
# Modules
/experimental:module- # Disable module support explicitly due to conflicts with precompiled headers
# External headers diagnostics
/experimental:external # Enables the external headers options. This option isn't required in Visual Studio 2019 version 16.10 and later
/external:anglebrackets # Treats all headers included by #include <header>, where the header file is enclosed in angle brackets (< >), as external headers
/external:W0 # Sets the default warning level to 0 for external headers, effectively turning off warnings for external headers

View File

@@ -1,11 +1,18 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
import android.annotation.SuppressLint
import kotlin.collections.setOf
import org.jetbrains.kotlin.konan.properties.Properties
import org.jlleitschuh.gradle.ktlint.reporter.ReporterType
plugins {
id("com.android.application")
id("org.jetbrains.kotlin.android")
id("kotlin-parcelize")
kotlin("plugin.serialization") version "1.8.21"
id("androidx.navigation.safeargs.kotlin")
id("org.jlleitschuh.gradle.ktlint") version "11.4.0"
}
/**
@@ -35,21 +42,11 @@ android {
jvmTarget = "17"
}
packagingOptions {
packaging {
// This is necessary for libadrenotools custom driver loading
jniLibs.useLegacyPackaging = true
}
lint {
// This is important as it will run lint but not abort on error
// Lint has some overly obnoxious "errors" that should really be warnings
abortOnError = false
//Uncomment disable lines for test builds...
//disable 'MissingTranslation'bin
//disable 'ExtraTranslation'
}
defaultConfig {
// TODO If this is ever modified, change application_id in strings.xml
applicationId = "org.yuzu.yuzu_emu"
@@ -57,7 +54,22 @@ android {
targetSdk = 33
versionName = getGitVersion()
// If you want to use autoVersion for the versionCode, create a property in local.properties
// named "autoVersioned" and set it to "true"
val properties = Properties()
val versionProperty = try {
properties.load(project.rootProject.file("local.properties").inputStream())
properties.getProperty("autoVersioned") ?: ""
} catch (e: Exception) { "" }
versionCode = if (versionProperty == "true") {
autoVersion
} else {
1
}
ndk {
@SuppressLint("ChromeOsAbiSupport")
abiFilters += listOf("arm64-v8a")
}
@@ -70,16 +82,7 @@ android {
// Signed by release key, allowing for upload to Play Store.
release {
signingConfig = signingConfigs.getByName("debug")
isMinifyEnabled = true
isDebuggable = false
proguardFiles(
getDefaultProguardFile("proguard-android.txt"),
"proguard-rules.pro"
)
}
register("relWithVersionCode") {
resValue("string", "app_name_suffixed", "yuzu")
signingConfig = signingConfigs.getByName("debug")
isMinifyEnabled = true
isDebuggable = false
@@ -92,6 +95,7 @@ android {
// builds a release build that doesn't need signing
// Attaches 'debug' suffix to version and package name, allowing installation alongside the release build.
register("relWithDebInfo") {
resValue("string", "app_name_suffixed", "yuzu Debug Release")
signingConfig = signingConfigs.getByName("debug")
isMinifyEnabled = true
isDebuggable = true
@@ -99,16 +103,19 @@ android {
getDefaultProguardFile("proguard-android.txt"),
"proguard-rules.pro"
)
versionNameSuffix = "-debug"
versionNameSuffix = "-relWithDebInfo"
applicationIdSuffix = ".relWithDebInfo"
isJniDebuggable = true
}
// Signed by debug key disallowing distribution on Play Store.
// Attaches 'debug' suffix to version and package name, allowing installation alongside the release build.
debug {
resValue("string", "app_name_suffixed", "yuzu Debug")
isDebuggable = true
isJniDebuggable = true
versionNameSuffix = "-debug"
applicationIdSuffix = ".debug"
}
}
@@ -153,24 +160,42 @@ android {
}
}
tasks.getByPath("preBuild").dependsOn("ktlintCheck")
ktlint {
version.set("0.47.1")
android.set(true)
ignoreFailures.set(false)
disabledRules.set(
setOf(
"no-wildcard-imports",
"package-name",
"import-ordering"
)
)
reporters {
reporter(ReporterType.CHECKSTYLE)
}
}
dependencies {
implementation("androidx.core:core-ktx:1.10.1")
implementation("androidx.appcompat:appcompat:1.6.1")
implementation("androidx.recyclerview:recyclerview:1.3.0")
implementation("androidx.constraintlayout:constraintlayout:2.1.4")
implementation("androidx.fragment:fragment-ktx:1.5.7")
implementation("androidx.fragment:fragment-ktx:1.6.0")
implementation("androidx.documentfile:documentfile:1.0.1")
implementation("com.google.android.material:material:1.9.0")
implementation("androidx.preference:preference:1.2.0")
implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.1")
implementation("io.coil-kt:coil:2.2.2")
implementation("androidx.core:core-splashscreen:1.0.1")
implementation("androidx.window:window:1.0.0")
implementation("androidx.window:window:1.1.0")
implementation("org.ini4j:ini4j:0.5.4")
implementation("androidx.constraintlayout:constraintlayout:2.1.4")
implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0")
implementation("androidx.navigation:navigation-fragment-ktx:2.5.3")
implementation("androidx.navigation:navigation-ui-ktx:2.5.3")
implementation("androidx.navigation:navigation-fragment-ktx:2.6.0")
implementation("androidx.navigation:navigation-ui-ktx:2.6.0")
implementation("info.debatty:java-string-similarity:2.0.0")
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.0")
}

View File

@@ -6,17 +6,10 @@ SPDX-License-Identifier: GPL-3.0-or-later
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-feature
android:name="android.hardware.touchscreen"
android:required="false"/>
<uses-feature
android:name="android.hardware.gamepad"
android:required="false"/>
<uses-feature
android:name="android.hardware.vulkan.version"
android:version="0x401000"
android:required="true" />
<uses-feature android:name="android.hardware.touchscreen" android:required="false" />
<uses-feature android:name="android.hardware.gamepad" android:required="false" />
<uses-feature android:name="android.software.leanback" android:required="false" />
<uses-feature android:name="android.hardware.vulkan.version" android:version="0x401000" android:required="true" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
@@ -25,13 +18,14 @@ SPDX-License-Identifier: GPL-3.0-or-later
<application
android:name="org.yuzu.yuzu_emu.YuzuApplication"
android:label="@string/app_name"
android:label="@string/app_name_suffixed"
android:icon="@drawable/ic_launcher"
android:allowBackup="true"
android:hasFragileUserData="true"
android:supportsRtl="true"
android:isGame="true"
android:banner="@drawable/ic_launcher"
android:localeConfig="@xml/locales_config"
android:banner="@drawable/tv_banner"
android:extractNativeLibs="true"
android:fullBackupContent="@xml/data_extraction_rules"
android:dataExtractionRules="@xml/data_extraction_rules_api_31"
@@ -44,9 +38,10 @@ SPDX-License-Identifier: GPL-3.0-or-later
<!-- This intentfilter marks this Activity as the one that gets launched from Home screen. -->
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER"/>
<category android:name="android.intent.category.LAUNCHER" />
<category android:name="android.intent.category.LEANBACK_LAUNCHER" />
</intent-filter>
</activity>
@@ -58,8 +53,9 @@ SPDX-License-Identifier: GPL-3.0-or-later
<activity
android:name="org.yuzu.yuzu_emu.activities.EmulationActivity"
android:theme="@style/Theme.Yuzu.Main"
android:launchMode="singleTop"
android:screenOrientation="userLandscape"
android:supportsPictureInPicture="true"
android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|uiMode"
android:exported="true">
<intent-filter>

View File

@@ -14,16 +14,18 @@ import android.widget.TextView
import androidx.annotation.Keep
import androidx.fragment.app.DialogFragment
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import java.lang.ref.WeakReference
import org.yuzu.yuzu_emu.YuzuApplication.Companion.appContext
import org.yuzu.yuzu_emu.activities.EmulationActivity
import org.yuzu.yuzu_emu.utils.DocumentsTree.Companion.isNativePath
import org.yuzu.yuzu_emu.utils.FileUtil.exists
import org.yuzu.yuzu_emu.utils.FileUtil.getFileSize
import org.yuzu.yuzu_emu.utils.FileUtil.isDirectory
import org.yuzu.yuzu_emu.utils.FileUtil.openContentUri
import org.yuzu.yuzu_emu.utils.Log.error
import org.yuzu.yuzu_emu.utils.Log.verbose
import org.yuzu.yuzu_emu.utils.Log.warning
import org.yuzu.yuzu_emu.utils.SerializableHelper.serializable
import java.lang.ref.WeakReference
/**
* Class which contains methods that interact
@@ -74,7 +76,9 @@ object NativeLibrary {
fun openContentUri(path: String?, openmode: String?): Int {
return if (isNativePath(path!!)) {
YuzuApplication.documentsTree!!.openContentUri(path, openmode)
} else openContentUri(appContext, path, openmode)
} else {
openContentUri(appContext, path, openmode)
}
}
@Keep
@@ -82,7 +86,29 @@ object NativeLibrary {
fun getSize(path: String?): Long {
return if (isNativePath(path!!)) {
YuzuApplication.documentsTree!!.getFileSize(path)
} else getFileSize(appContext, path)
} else {
getFileSize(appContext, path)
}
}
@Keep
@JvmStatic
fun exists(path: String?): Boolean {
return if (isNativePath(path!!)) {
YuzuApplication.documentsTree!!.exists(path)
} else {
exists(appContext, path)
}
}
@Keep
@JvmStatic
fun isDirectory(path: String?): Boolean {
return if (isNativePath(path!!)) {
YuzuApplication.documentsTree!!.isDirectory(path)
} else {
isDirectory(appContext, path)
}
}
/**
@@ -223,8 +249,12 @@ object NativeLibrary {
external fun getCompany(filename: String): String
external fun isHomebrew(filename: String): Boolean
external fun setAppDirectory(directory: String)
external fun installFileToNand(filename: String): Int
external fun initializeGpuDriver(
hookLibDir: String?,
customDriverDir: String?,
@@ -256,7 +286,7 @@ object NativeLibrary {
/**
* Unpauses emulation from a paused state.
*/
external fun unPauseEmulation()
external fun unpauseEmulation()
/**
* Pauses emulation.
@@ -278,6 +308,26 @@ object NativeLibrary {
*/
external fun isRunning(): Boolean
/**
* Returns true if emulation is paused.
*/
external fun isPaused(): Boolean
/**
* Mutes emulation sound
*/
external fun muteAudio(): Boolean
/**
* Unmutes emulation sound
*/
external fun unmuteAudio(): Boolean
/**
* Returns true if emulation audio is muted.
*/
external fun isMuted(): Boolean
/**
* Returns the performance stats for the current game
*/
@@ -427,7 +477,9 @@ object NativeLibrary {
Html.FROM_HTML_MODE_LEGACY
)
)
.setPositiveButton(android.R.string.ok) { _: DialogInterface?, _: Int -> emulationActivity.finish() }
.setPositiveButton(android.R.string.ok) { _: DialogInterface?, _: Int ->
emulationActivity.finish()
}
.setOnDismissListener { emulationActivity.finish() }
emulationActivity.runOnUiThread {
val alert = builder.create()
@@ -505,4 +557,15 @@ object NativeLibrary {
const val RELEASED = 0
const val PRESSED = 1
}
/**
* Result from installFileToNand
*/
object InstallFileToNandResult {
const val Success = 0
const val SuccessFileOverwritten = 1
const val Error = 2
const val ErrorBaseGame = 3
const val ErrorFilenameExtension = 4
}
}

View File

@@ -7,12 +7,12 @@ import android.app.Application
import android.app.NotificationChannel
import android.app.NotificationManager
import android.content.Context
import java.io.File
import org.yuzu.yuzu_emu.utils.DirectoryInitialization
import org.yuzu.yuzu_emu.utils.DocumentsTree
import org.yuzu.yuzu_emu.utils.GpuDriverHelper
import java.io.File
fun Context.getPublicFilesDir() : File = getExternalFilesDir(null) ?: filesDir
fun Context.getPublicFilesDir(): File = getExternalFilesDir(null) ?: filesDir
class YuzuApplication : Application() {
private fun createNotificationChannels() {
@@ -21,7 +21,9 @@ class YuzuApplication : Application() {
getString(R.string.emulation_notification_channel_name),
NotificationManager.IMPORTANCE_LOW
)
emulationChannel.description = getString(R.string.emulation_notification_channel_description)
emulationChannel.description = getString(
R.string.emulation_notification_channel_description
)
emulationChannel.setSound(null, null)
emulationChannel.vibrationPattern = null
@@ -48,7 +50,7 @@ class YuzuApplication : Application() {
GpuDriverHelper.initializeDriverParameters(applicationContext)
NativeLibrary.logDeviceInfo()
createNotificationChannels();
createNotificationChannels()
}
companion object {

View File

@@ -4,48 +4,57 @@
package org.yuzu.yuzu_emu.activities
import android.app.Activity
import android.app.PendingIntent
import android.app.PictureInPictureParams
import android.app.RemoteAction
import android.content.BroadcastReceiver
import android.content.Context
import android.content.DialogInterface
import android.content.Intent
import android.content.IntentFilter
import android.content.res.Configuration
import android.graphics.Rect
import android.graphics.drawable.Icon
import android.hardware.Sensor
import android.hardware.SensorEvent
import android.hardware.SensorEventListener
import android.hardware.SensorManager
import android.os.Build
import android.os.Bundle
import android.util.Rational
import android.view.InputDevice
import android.view.KeyEvent
import android.view.MotionEvent
import android.view.Surface
import android.view.View
import android.view.WindowManager
import android.view.inputmethod.InputMethodManager
import android.widget.Toast
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import androidx.preference.PreferenceManager
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.slider.Slider.OnChangeListener
import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.WindowInsetsControllerCompat
import androidx.navigation.fragment.NavHostFragment
import org.yuzu.yuzu_emu.NativeLibrary
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.databinding.DialogSliderBinding
import org.yuzu.yuzu_emu.features.settings.model.Settings
import org.yuzu.yuzu_emu.fragments.EmulationFragment
import org.yuzu.yuzu_emu.databinding.ActivityEmulationBinding
import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting
import org.yuzu.yuzu_emu.features.settings.model.IntSetting
import org.yuzu.yuzu_emu.features.settings.model.SettingsViewModel
import org.yuzu.yuzu_emu.model.Game
import org.yuzu.yuzu_emu.utils.ControllerMappingHelper
import org.yuzu.yuzu_emu.utils.EmulationMenuSettings
import org.yuzu.yuzu_emu.utils.ForegroundService
import org.yuzu.yuzu_emu.utils.InputHandler
import org.yuzu.yuzu_emu.utils.MemoryUtil
import org.yuzu.yuzu_emu.utils.NfcReader
import org.yuzu.yuzu_emu.utils.SerializableHelper.parcelable
import org.yuzu.yuzu_emu.utils.ThemeHelper
import kotlin.math.roundToInt
class EmulationActivity : AppCompatActivity(), SensorEventListener {
private lateinit var binding: ActivityEmulationBinding
private var controllerMappingHelper: ControllerMappingHelper? = null
var isActivityRecreated = false
private var menuVisible = false
private var emulationFragment: EmulationFragment? = null
private lateinit var nfcReader: NfcReader
private lateinit var inputHandler: InputHandler
@@ -54,7 +63,12 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
private var motionTimestamp: Long = 0
private var flipMotionOrientation: Boolean = false
private lateinit var game: Game
private val actionPause = "ACTION_EMULATOR_PAUSE"
private val actionPlay = "ACTION_EMULATOR_PLAY"
private val actionMute = "ACTION_EMULATOR_MUTE"
private val actionUnmute = "ACTION_EMULATOR_UNMUTE"
private val settingsViewModel: SettingsViewModel by viewModels()
override fun onDestroy() {
stopForegroundService(this)
@@ -64,40 +78,47 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
override fun onCreate(savedInstanceState: Bundle?) {
ThemeHelper.setTheme(this)
settingsViewModel.settings.loadSettings()
super.onCreate(savedInstanceState)
if (savedInstanceState == null) {
// Get params we were passed
game = intent.parcelable(EXTRA_SELECTED_GAME)!!
isActivityRecreated = false
} else {
isActivityRecreated = true
restoreState(savedInstanceState)
}
binding = ActivityEmulationBinding.inflate(layoutInflater)
setContentView(binding.root)
val navHostFragment =
supportFragmentManager.findFragmentById(R.id.fragment_container) as NavHostFragment
val navController = navHostFragment.navController
navController
.setGraph(R.navigation.emulation_navigation, intent.extras)
isActivityRecreated = savedInstanceState != null
controllerMappingHelper = ControllerMappingHelper()
// Set these options now so that the SurfaceView the game renders into is the right size.
enableFullscreenImmersive()
setContentView(R.layout.activity_emulation)
window.decorView.setBackgroundColor(getColor(android.R.color.black))
// Find or create the EmulationFragment
emulationFragment =
supportFragmentManager.findFragmentById(R.id.frame_emulation_fragment) as EmulationFragment?
if (emulationFragment == null) {
emulationFragment = EmulationFragment.newInstance(game)
supportFragmentManager.beginTransaction()
.add(R.id.frame_emulation_fragment, emulationFragment!!)
.commit()
}
title = game.title
nfcReader = NfcReader(this)
nfcReader.initialize()
inputHandler = InputHandler()
inputHandler.initialize()
val memoryUtil = MemoryUtil(this)
if (memoryUtil.isLessThan(8, MemoryUtil.Gb)) {
Toast.makeText(
this,
getString(
R.string.device_memory_inadequate,
memoryUtil.getDeviceRAM(),
"8 ${getString(R.string.memory_gigabyte)}"
),
Toast.LENGTH_LONG
).show()
}
// Start a foreground service to prevent the app from getting killed in the background
val startIntent = Intent(this, ForegroundService::class.java)
startForegroundService(startIntent)
@@ -131,10 +152,7 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
nfcReader.startScanning()
startMotionSensorListener()
NativeLibrary.notifyOrientationChange(
EmulationMenuSettings.landscapeScreenLayout,
getAdjustedRotation()
)
buildPictureInPictureParams()
}
override fun onPause() {
@@ -143,17 +161,22 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
stopMotionSensorListener()
}
override fun onUserLeaveHint() {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) {
if (BooleanSetting.PICTURE_IN_PICTURE.boolean && !isInPictureInPictureMode) {
val pictureInPictureParamsBuilder = PictureInPictureParams.Builder()
.getPictureInPictureActionsBuilder().getPictureInPictureAspectBuilder()
enterPictureInPictureMode(pictureInPictureParamsBuilder.build())
}
}
}
override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent)
setIntent(intent)
nfcReader.onNewIntent(intent)
}
override fun onSaveInstanceState(outState: Bundle) {
outState.putParcelable(EXTRA_SELECTED_GAME, game)
super.onSaveInstanceState(outState)
}
override fun dispatchKeyEvent(event: KeyEvent): Boolean {
if (event.source and InputDevice.SOURCE_JOYSTICK != InputDevice.SOURCE_JOYSTICK &&
event.source and InputDevice.SOURCE_GAMEPAD != InputDevice.SOURCE_GAMEPAD
@@ -240,40 +263,152 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
override fun onAccuracyChanged(sensor: Sensor, i: Int) {}
private fun getAdjustedRotation():Int {
val rotation = windowManager.defaultDisplay.rotation;
val config: Configuration = resources.configuration
if ((config.screenLayout and Configuration.SCREENLAYOUT_LONG_YES) != 0 ||
(config.screenLayout and Configuration.SCREENLAYOUT_LONG_NO) == 0) {
return rotation;
}
when (rotation) {
Surface.ROTATION_0 -> return Surface.ROTATION_90;
Surface.ROTATION_90 -> return Surface.ROTATION_0;
Surface.ROTATION_180 -> return Surface.ROTATION_270;
Surface.ROTATION_270 -> return Surface.ROTATION_180;
}
return rotation;
}
private fun restoreState(savedInstanceState: Bundle) {
game = savedInstanceState.parcelable(EXTRA_SELECTED_GAME)!!
}
private fun enableFullscreenImmersive() {
window.attributes.layoutInDisplayCutoutMode =
WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES
WindowCompat.setDecorFitsSystemWindows(window, false)
window.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN)
WindowInsetsControllerCompat(window, window.decorView).let { controller ->
controller.hide(WindowInsetsCompat.Type.systemBars())
controller.systemBarsBehavior =
WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
}
}
// It would be nice to use IMMERSIVE_STICKY, but that doesn't show the toolbar.
window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_STABLE or
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or
View.SYSTEM_UI_FLAG_HIDE_NAVIGATION or
View.SYSTEM_UI_FLAG_FULLSCREEN or
View.SYSTEM_UI_FLAG_IMMERSIVE
private fun PictureInPictureParams.Builder.getPictureInPictureAspectBuilder():
PictureInPictureParams.Builder {
val aspectRatio = when (IntSetting.RENDERER_ASPECT_RATIO.int) {
0 -> Rational(16, 9)
1 -> Rational(4, 3)
2 -> Rational(21, 9)
3 -> Rational(16, 10)
else -> null // Best fit
}
return this.apply { aspectRatio?.let { setAspectRatio(it) } }
}
private fun PictureInPictureParams.Builder.getPictureInPictureActionsBuilder():
PictureInPictureParams.Builder {
val pictureInPictureActions: MutableList<RemoteAction> = mutableListOf()
val pendingFlags = PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
if (NativeLibrary.isPaused()) {
val playIcon = Icon.createWithResource(this@EmulationActivity, R.drawable.ic_pip_play)
val playPendingIntent = PendingIntent.getBroadcast(
this@EmulationActivity,
R.drawable.ic_pip_play,
Intent(actionPlay),
pendingFlags
)
val playRemoteAction = RemoteAction(
playIcon,
getString(R.string.play),
getString(R.string.play),
playPendingIntent
)
pictureInPictureActions.add(playRemoteAction)
} else {
val pauseIcon = Icon.createWithResource(this@EmulationActivity, R.drawable.ic_pip_pause)
val pausePendingIntent = PendingIntent.getBroadcast(
this@EmulationActivity,
R.drawable.ic_pip_pause,
Intent(actionPause),
pendingFlags
)
val pauseRemoteAction = RemoteAction(
pauseIcon,
getString(R.string.pause),
getString(R.string.pause),
pausePendingIntent
)
pictureInPictureActions.add(pauseRemoteAction)
}
if (NativeLibrary.isMuted()) {
val unmuteIcon = Icon.createWithResource(
this@EmulationActivity,
R.drawable.ic_pip_unmute
)
val unmutePendingIntent = PendingIntent.getBroadcast(
this@EmulationActivity,
R.drawable.ic_pip_unmute,
Intent(actionUnmute),
pendingFlags
)
val unmuteRemoteAction = RemoteAction(
unmuteIcon,
getString(R.string.unmute),
getString(R.string.unmute),
unmutePendingIntent
)
pictureInPictureActions.add(unmuteRemoteAction)
} else {
val muteIcon = Icon.createWithResource(this@EmulationActivity, R.drawable.ic_pip_mute)
val mutePendingIntent = PendingIntent.getBroadcast(
this@EmulationActivity,
R.drawable.ic_pip_mute,
Intent(actionMute),
pendingFlags
)
val muteRemoteAction = RemoteAction(
muteIcon,
getString(R.string.mute),
getString(R.string.mute),
mutePendingIntent
)
pictureInPictureActions.add(muteRemoteAction)
}
return this.apply { setActions(pictureInPictureActions) }
}
fun buildPictureInPictureParams() {
val pictureInPictureParamsBuilder = PictureInPictureParams.Builder()
.getPictureInPictureActionsBuilder().getPictureInPictureAspectBuilder()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
pictureInPictureParamsBuilder.setAutoEnterEnabled(
BooleanSetting.PICTURE_IN_PICTURE.boolean
)
}
setPictureInPictureParams(pictureInPictureParamsBuilder.build())
}
private var pictureInPictureReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent) {
if (intent.action == actionPlay) {
if (NativeLibrary.isPaused()) NativeLibrary.unpauseEmulation()
} else if (intent.action == actionPause) {
if (!NativeLibrary.isPaused()) NativeLibrary.pauseEmulation()
}
if (intent.action == actionUnmute) {
if (NativeLibrary.isMuted()) NativeLibrary.unmuteAudio()
} else if (intent.action == actionMute) {
if (!NativeLibrary.isMuted()) NativeLibrary.muteAudio()
}
buildPictureInPictureParams()
}
}
override fun onPictureInPictureModeChanged(
isInPictureInPictureMode: Boolean,
newConfig: Configuration
) {
super.onPictureInPictureModeChanged(isInPictureInPictureMode, newConfig)
if (isInPictureInPictureMode) {
IntentFilter().apply {
addAction(actionPause)
addAction(actionPlay)
addAction(actionMute)
addAction(actionUnmute)
}.also {
registerReceiver(pictureInPictureReceiver, it)
}
} else {
try {
unregisterReceiver(pictureInPictureReceiver)
} catch (ignored: Exception) {
}
// Always resume audio, since there is no UI button
if (NativeLibrary.isMuted()) NativeLibrary.unmuteAudio()
}
}
private fun startMotionSensorListener() {

View File

@@ -16,6 +16,7 @@ import androidx.appcompat.app.AppCompatActivity
import androidx.documentfile.provider.DocumentFile
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope
import androidx.navigation.findNavController
import androidx.preference.PreferenceManager
import androidx.recyclerview.widget.AsyncDifferConfig
import androidx.recyclerview.widget.DiffUtil
@@ -23,13 +24,13 @@ import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import coil.load
import kotlinx.coroutines.launch
import org.yuzu.yuzu_emu.HomeNavigationDirections
import org.yuzu.yuzu_emu.NativeLibrary
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.YuzuApplication
import org.yuzu.yuzu_emu.databinding.CardGameBinding
import org.yuzu.yuzu_emu.activities.EmulationActivity
import org.yuzu.yuzu_emu.model.Game
import org.yuzu.yuzu_emu.adapters.GameAdapter.GameViewHolder
import org.yuzu.yuzu_emu.databinding.CardGameBinding
import org.yuzu.yuzu_emu.model.Game
import org.yuzu.yuzu_emu.model.GamesViewModel
class GameAdapter(private val activity: AppCompatActivity) :
@@ -58,7 +59,10 @@ class GameAdapter(private val activity: AppCompatActivity) :
override fun onClick(view: View) {
val holder = view.tag as GameViewHolder
val gameExists = DocumentFile.fromSingleUri(YuzuApplication.appContext, Uri.parse(holder.game.path))?.exists() == true
val gameExists = DocumentFile.fromSingleUri(
YuzuApplication.appContext,
Uri.parse(holder.game.path)
)?.exists() == true
if (!gameExists) {
Toast.makeText(
YuzuApplication.appContext,
@@ -78,7 +82,8 @@ class GameAdapter(private val activity: AppCompatActivity) :
)
.apply()
EmulationActivity.launch(activity, holder.game)
val action = HomeNavigationDirections.actionGlobalEmulationActivity(holder.game)
view.findNavController().navigate(action)
}
inner class GameViewHolder(val binding: CardGameBinding) :

View File

@@ -58,11 +58,12 @@ class HomeSettingAdapter(private val activity: AppCompatActivity, var options: L
)
when (option.titleId) {
R.string.get_early_access -> binding.optionLayout.background =
ContextCompat.getDrawable(
binding.optionCard.context,
R.drawable.premium_background
)
R.string.get_early_access ->
binding.optionLayout.background =
ContextCompat.getDrawable(
binding.optionCard.context,
R.drawable.premium_background
)
}
}
}

View File

@@ -0,0 +1,54 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu.adapters
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.app.AppCompatActivity
import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.RecyclerView.ViewHolder
import org.yuzu.yuzu_emu.YuzuApplication
import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding
import org.yuzu.yuzu_emu.fragments.LicenseBottomSheetDialogFragment
import org.yuzu.yuzu_emu.model.License
class LicenseAdapter(private val activity: AppCompatActivity, var licenses: List<License>) :
RecyclerView.Adapter<LicenseAdapter.LicenseViewHolder>(),
View.OnClickListener {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): LicenseViewHolder {
val binding =
ListItemSettingBinding.inflate(LayoutInflater.from(parent.context), parent, false)
binding.root.setOnClickListener(this)
return LicenseViewHolder(binding)
}
override fun getItemCount(): Int = licenses.size
override fun onBindViewHolder(holder: LicenseViewHolder, position: Int) {
holder.bind(licenses[position])
}
override fun onClick(view: View) {
val license = (view.tag as LicenseViewHolder).license
LicenseBottomSheetDialogFragment.newInstance(license)
.show(activity.supportFragmentManager, LicenseBottomSheetDialogFragment.TAG)
}
inner class LicenseViewHolder(val binding: ListItemSettingBinding) : ViewHolder(binding.root) {
lateinit var license: License
init {
itemView.tag = this
}
fun bind(license: License) {
this.license = license
val context = YuzuApplication.appContext
binding.textSettingName.text = context.getString(license.titleId)
binding.textSettingDescription.text = context.getString(license.descriptionId)
}
}
}

View File

@@ -12,10 +12,10 @@ import android.view.WindowInsets
import android.view.inputmethod.InputMethodManager
import androidx.annotation.Keep
import androidx.core.view.ViewCompat
import java.io.Serializable
import org.yuzu.yuzu_emu.NativeLibrary
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.applets.keyboard.ui.KeyboardDialogFragment
import java.io.Serializable
@Keep
object SoftwareKeyboard {
@@ -40,19 +40,22 @@ object SoftwareKeyboard {
// There isn't a good way to know that the IMM is dismissed, so poll every 500ms to submit inline keyboard result.
val handler = Handler(Looper.myLooper()!!)
val delayMs = 500
handler.postDelayed(object : Runnable {
override fun run() {
val insets = ViewCompat.getRootWindowInsets(overlayView)
val isKeyboardVisible = insets!!.isVisible(WindowInsets.Type.ime())
if (isKeyboardVisible) {
handler.postDelayed(this, delayMs.toLong())
return
}
handler.postDelayed(
object : Runnable {
override fun run() {
val insets = ViewCompat.getRootWindowInsets(overlayView)
val isKeyboardVisible = insets!!.isVisible(WindowInsets.Type.ime())
if (isKeyboardVisible) {
handler.postDelayed(this, delayMs.toLong())
return
}
// No longer visible, submit the result.
NativeLibrary.submitInlineKeyboardInput(KeyEvent.KEYCODE_ENTER)
}
}, delayMs.toLong())
// No longer visible, submit the result.
NativeLibrary.submitInlineKeyboardInput(KeyEvent.KEYCODE_ENTER)
}
},
delayMs.toLong()
)
}
@JvmStatic

View File

@@ -63,7 +63,7 @@ class KeyboardDialogFragment : DialogFragment() {
val headerText =
config.header_text!!.ifEmpty { resources.getString(R.string.software_keyboard) }
val okText =
config.ok_text!!.ifEmpty { resources.getString(android.R.string.ok) }
config.ok_text!!.ifEmpty { resources.getString(R.string.submit) }
return MaterialAlertDialogBuilder(requireContext())
.setTitle(headerText)

View File

@@ -20,7 +20,10 @@ object DiskShaderCacheProgress {
emulationActivity.getString(R.string.loading),
emulationActivity.getString(R.string.preparing_shaders)
)
fragment.show(emulationActivity.supportFragmentManager, ShaderProgressDialogFragment.TAG)
fragment.show(
emulationActivity.supportFragmentManager,
ShaderProgressDialogFragment.TAG
)
}
synchronized(finishLock) { finishLock.wait() }
}

View File

@@ -62,7 +62,9 @@ class ShaderProgressDialogFragment : DialogFragment() {
shaderProgressViewModel.message.observe(viewLifecycleOwner) { msg ->
alertDialog.setMessage(msg)
}
synchronized(DiskShaderCacheProgress.finishLock) { DiskShaderCacheProgress.finishLock.notifyAll() }
synchronized(DiskShaderCacheProgress.finishLock) {
DiskShaderCacheProgress.finishLock.notifyAll()
}
}
override fun onDestroyView() {

View File

@@ -13,11 +13,11 @@ import android.os.ParcelFileDescriptor
import android.provider.DocumentsContract
import android.provider.DocumentsProvider
import android.webkit.MimeTypeMap
import java.io.*
import org.yuzu.yuzu_emu.BuildConfig
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.YuzuApplication
import org.yuzu.yuzu_emu.getPublicFilesDir
import java.io.*
class DocumentProvider : DocumentsProvider() {
private val baseDirectory: File
@@ -44,7 +44,7 @@ class DocumentProvider : DocumentsProvider() {
DocumentsContract.Document.COLUMN_SIZE
)
const val AUTHORITY : String = BuildConfig.APPLICATION_ID + ".user"
const val AUTHORITY: String = BuildConfig.APPLICATION_ID + ".user"
const val ROOT_ID: String = "root"
}
@@ -58,7 +58,11 @@ class DocumentProvider : DocumentsProvider() {
private fun getFile(documentId: String): File {
if (documentId.startsWith(ROOT_ID)) {
val file = baseDirectory.resolve(documentId.drop(ROOT_ID.length + 1))
if (!file.exists()) throw FileNotFoundException("${file.absolutePath} ($documentId) not found")
if (!file.exists()) {
throw FileNotFoundException(
"${file.absolutePath} ($documentId) not found"
)
}
return file
} else {
throw FileNotFoundException("'$documentId' is not in any known root")
@@ -80,7 +84,8 @@ class DocumentProvider : DocumentsProvider() {
add(DocumentsContract.Root.COLUMN_SUMMARY, null)
add(
DocumentsContract.Root.COLUMN_FLAGS,
DocumentsContract.Root.FLAG_SUPPORTS_CREATE or DocumentsContract.Root.FLAG_SUPPORTS_IS_CHILD
DocumentsContract.Root.FLAG_SUPPORTS_CREATE or
DocumentsContract.Root.FLAG_SUPPORTS_IS_CHILD
)
add(DocumentsContract.Root.COLUMN_TITLE, context!!.getString(R.string.app_name))
add(DocumentsContract.Root.COLUMN_DOCUMENT_ID, getDocumentId(baseDirectory))
@@ -127,11 +132,13 @@ class DocumentProvider : DocumentsProvider() {
try {
if (DocumentsContract.Document.MIME_TYPE_DIR == mimeType) {
if (!newFile.mkdir())
if (!newFile.mkdir()) {
throw IOException("Failed to create directory")
}
} else {
if (!newFile.createNewFile())
if (!newFile.createNewFile()) {
throw IOException("Failed to create file")
}
}
} catch (e: IOException) {
throw FileNotFoundException("Couldn't create document '${newFile.path}': ${e.message}")
@@ -142,8 +149,9 @@ class DocumentProvider : DocumentsProvider() {
override fun deleteDocument(documentId: String?) {
val file = getFile(documentId!!)
if (!file.delete())
if (!file.delete()) {
throw FileNotFoundException("Couldn't delete document with ID '$documentId'")
}
}
override fun removeDocument(documentId: String, parentDocumentId: String?) {
@@ -151,38 +159,55 @@ class DocumentProvider : DocumentsProvider() {
val file = getFile(documentId)
if (parent == file || file.parentFile == null || file.parentFile!! == parent) {
if (!file.delete())
if (!file.delete()) {
throw FileNotFoundException("Couldn't delete document with ID '$documentId'")
}
} else {
throw FileNotFoundException("Couldn't delete document with ID '$documentId'")
}
}
override fun renameDocument(documentId: String?, displayName: String?): String {
if (displayName == null)
throw FileNotFoundException("Couldn't rename document '$documentId' as the new name is null")
if (displayName == null) {
throw FileNotFoundException(
"Couldn't rename document '$documentId' as the new name is null"
)
}
val sourceFile = getFile(documentId!!)
val sourceParentFile = sourceFile.parentFile
?: throw FileNotFoundException("Couldn't rename document '$documentId' as it has no parent")
?: throw FileNotFoundException(
"Couldn't rename document '$documentId' as it has no parent"
)
val destFile = sourceParentFile.resolve(displayName)
try {
if (!sourceFile.renameTo(destFile))
throw FileNotFoundException("Couldn't rename document from '${sourceFile.name}' to '${destFile.name}'")
if (!sourceFile.renameTo(destFile)) {
throw FileNotFoundException(
"Couldn't rename document from '${sourceFile.name}' to '${destFile.name}'"
)
}
} catch (e: Exception) {
throw FileNotFoundException("Couldn't rename document from '${sourceFile.name}' to '${destFile.name}': ${e.message}")
throw FileNotFoundException(
"Couldn't rename document from '${sourceFile.name}' to '${destFile.name}': " +
"${e.message}"
)
}
return getDocumentId(destFile)
}
private fun copyDocument(
sourceDocumentId: String, sourceParentDocumentId: String,
sourceDocumentId: String,
sourceParentDocumentId: String,
targetParentDocumentId: String?
): String {
if (!isChildDocument(sourceParentDocumentId, sourceDocumentId))
throw FileNotFoundException("Couldn't copy document '$sourceDocumentId' as its parent is not '$sourceParentDocumentId'")
if (!isChildDocument(sourceParentDocumentId, sourceDocumentId)) {
throw FileNotFoundException(
"Couldn't copy document '$sourceDocumentId' as its parent is not " +
"'$sourceParentDocumentId'"
)
}
return copyDocument(sourceDocumentId, targetParentDocumentId)
}
@@ -193,8 +218,13 @@ class DocumentProvider : DocumentsProvider() {
val newFile = parent.resolveWithoutConflict(oldFile.name)
try {
if (!(newFile.createNewFile() && newFile.setWritable(true) && newFile.setReadable(true)))
if (!(
newFile.createNewFile() && newFile.setWritable(true) &&
newFile.setReadable(true)
)
) {
throw IOException("Couldn't create new file")
}
FileInputStream(oldFile).use { inStream ->
FileOutputStream(newFile).use { outStream ->
@@ -209,12 +239,14 @@ class DocumentProvider : DocumentsProvider() {
}
override fun moveDocument(
sourceDocumentId: String, sourceParentDocumentId: String?,
sourceDocumentId: String,
sourceParentDocumentId: String?,
targetParentDocumentId: String?
): String {
try {
val newDocumentId = copyDocument(
sourceDocumentId, sourceParentDocumentId!!,
sourceDocumentId,
sourceParentDocumentId!!,
targetParentDocumentId
)
removeDocument(sourceDocumentId, sourceParentDocumentId)
@@ -245,24 +277,30 @@ class DocumentProvider : DocumentsProvider() {
add(DocumentsContract.Document.COLUMN_DOCUMENT_ID, localDocumentId)
add(
DocumentsContract.Document.COLUMN_DISPLAY_NAME,
if (localFile == baseDirectory) context!!.getString(R.string.app_name) else localFile.name
if (localFile == baseDirectory) {
context!!.getString(R.string.app_name)
} else {
localFile.name
}
)
add(DocumentsContract.Document.COLUMN_SIZE, localFile.length())
add(DocumentsContract.Document.COLUMN_MIME_TYPE, getTypeForFile(localFile))
add(DocumentsContract.Document.COLUMN_LAST_MODIFIED, localFile.lastModified())
add(DocumentsContract.Document.COLUMN_FLAGS, flags)
if (localFile == baseDirectory)
if (localFile == baseDirectory) {
add(DocumentsContract.Root.COLUMN_ICON, R.drawable.ic_yuzu)
}
}
return cursor
}
private fun getTypeForFile(file: File): Any {
return if (file.isDirectory)
return if (file.isDirectory) {
DocumentsContract.Document.MIME_TYPE_DIR
else
} else {
getTypeForName(file.name)
}
}
private fun getTypeForName(name: String): Any {
@@ -270,8 +308,9 @@ class DocumentProvider : DocumentsProvider() {
if (lastDot >= 0) {
val extension = name.substring(lastDot + 1)
val mime = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension)
if (mime != null)
if (mime != null) {
return mime
}
}
return "application/octect-stream"
}

View File

@@ -8,6 +8,10 @@ enum class BooleanSetting(
override val section: String,
override val defaultValue: Boolean
) : AbstractBooleanSetting {
CPU_DEBUG_MODE("cpu_debug_mode", Settings.SECTION_CPU, false),
FASTMEM("cpuopt_fastmem", Settings.SECTION_CPU, true),
FASTMEM_EXCLUSIVES("cpuopt_fastmem_exclusives", Settings.SECTION_CPU, true),
PICTURE_IN_PICTURE("picture_in_picture", Settings.SECTION_GENERAL, true),
USE_CUSTOM_RTC("custom_rtc_enabled", Settings.SECTION_SYSTEM, false);
override var boolean: Boolean = defaultValue
@@ -27,6 +31,7 @@ enum class BooleanSetting(
companion object {
private val NOT_RUNTIME_EDITABLE = listOf(
PICTURE_IN_PICTURE,
USE_CUSTOM_RTC
)

View File

@@ -26,13 +26,18 @@ enum class IntSetting(
RENDERER_FORCE_MAX_CLOCK(
"force_max_clock",
Settings.SECTION_RENDERER,
1
0
),
RENDERER_ASYNCHRONOUS_SHADERS(
"use_asynchronous_shaders",
Settings.SECTION_RENDERER,
0
),
RENDERER_REACTIVE_FLUSHING(
"use_reactive_flushing",
Settings.SECTION_RENDERER,
0
),
RENDERER_DEBUG(
"debug",
Settings.SECTION_RENDERER,
@@ -88,6 +93,11 @@ enum class IntSetting(
Settings.SECTION_RENDERER,
0
),
RENDERER_SCREEN_LAYOUT(
"screen_layout",
Settings.SECTION_RENDERER,
Settings.LayoutOption_MobileLandscape
),
RENDERER_ASPECT_RATIO(
"aspect_ratio",
Settings.SECTION_RENDERER,

View File

@@ -4,11 +4,11 @@
package org.yuzu.yuzu_emu.features.settings.model
import android.text.TextUtils
import java.util.*
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.YuzuApplication
import org.yuzu.yuzu_emu.features.settings.ui.SettingsActivityView
import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile
import java.util.*
class Settings {
private var gameId: String? = null
@@ -39,7 +39,7 @@ class Settings {
val isEmpty: Boolean
get() = sections.isEmpty()
fun loadSettings(view: SettingsActivityView) {
fun loadSettings(view: SettingsActivityView? = null) {
sections = SettingsSectionMap()
loadYuzuSettings(view)
if (!TextUtils.isEmpty(gameId)) {
@@ -48,13 +48,13 @@ class Settings {
isLoaded = true
}
private fun loadYuzuSettings(view: SettingsActivityView) {
private fun loadYuzuSettings(view: SettingsActivityView?) {
for ((fileName) in configFileSectionsMap) {
sections.putAll(SettingsFile.readFile(fileName, view))
}
}
private fun loadCustomGameSettings(gameId: String, view: SettingsActivityView) {
private fun loadCustomGameSettings(gameId: String, view: SettingsActivityView?) {
// Custom game settings
mergeSections(SettingsFile.readCustomGameSettings(gameId, view))
}
@@ -108,6 +108,7 @@ class Settings {
const val SECTION_AUDIO = "Audio"
const val SECTION_CPU = "Cpu"
const val SECTION_THEME = "Theme"
const val SECTION_DEBUG = "Debug"
const val PREF_OVERLAY_INIT = "OverlayInit"
const val PREF_CONTROL_SCALE = "controlScale"
@@ -132,7 +133,6 @@ class Settings {
const val PREF_MENU_SETTINGS_JOYSTICK_REL_CENTER = "EmulationMenuSettings_JoystickRelCenter"
const val PREF_MENU_SETTINGS_DPAD_SLIDE = "EmulationMenuSettings_DpadSlideEnable"
const val PREF_MENU_SETTINGS_HAPTICS = "EmulationMenuSettings_Haptics"
const val PREF_MENU_SETTINGS_LANDSCAPE = "EmulationMenuSettings_LandscapeScreenLayout"
const val PREF_MENU_SETTINGS_SHOW_FPS = "EmulationMenuSettings_ShowFps"
const val PREF_MENU_SETTINGS_SHOW_OVERLAY = "EmulationMenuSettings_ShowOverlay"
@@ -143,6 +143,10 @@ class Settings {
private val configFileSectionsMap: MutableMap<String, List<String>> = HashMap()
const val LayoutOption_Unspecified = 0
const val LayoutOption_MobilePortrait = 4
const val LayoutOption_MobileLandscape = 5
init {
configFileSectionsMap[SettingsFile.FILE_NAME_CONFIG] =
listOf(

View File

@@ -8,6 +8,7 @@ enum class StringSetting(
override val section: String,
override val defaultValue: String
) : AbstractStringSetting {
AUDIO_OUTPUT_ENGINE("output_engine", Settings.SECTION_AUDIO, "auto"),
CUSTOM_RTC("custom_rtc", Settings.SECTION_SYSTEM, "0");
override var string: String = defaultValue

View File

@@ -3,12 +3,8 @@
package org.yuzu.yuzu_emu.features.settings.model.view
import org.yuzu.yuzu_emu.features.settings.model.AbstractSetting
class HeaderSetting(
setting: AbstractSetting?,
titleId: Int,
descriptionId: Int
) : SettingsItem(setting, titleId, descriptionId) {
titleId: Int
) : SettingsItem(null, titleId, 0) {
override val type = TYPE_HEADER
}

View File

@@ -4,7 +4,6 @@
package org.yuzu.yuzu_emu.features.settings.model.view
import org.yuzu.yuzu_emu.features.settings.model.AbstractIntSetting
import org.yuzu.yuzu_emu.features.settings.model.IntSetting
class SingleChoiceSetting(
setting: AbstractIntSetting?,

View File

@@ -3,13 +3,11 @@
package org.yuzu.yuzu_emu.features.settings.model.view
import kotlin.math.roundToInt
import org.yuzu.yuzu_emu.features.settings.model.AbstractFloatSetting
import org.yuzu.yuzu_emu.features.settings.model.AbstractIntSetting
import org.yuzu.yuzu_emu.features.settings.model.AbstractSetting
import org.yuzu.yuzu_emu.features.settings.model.FloatSetting
import org.yuzu.yuzu_emu.features.settings.model.IntSetting
import org.yuzu.yuzu_emu.utils.Log
import kotlin.math.roundToInt
class SliderSetting(
setting: AbstractSetting?,
@@ -19,7 +17,7 @@ class SliderSetting(
val max: Int,
val units: String,
val key: String? = null,
val defaultValue: Int? = null,
val defaultValue: Int? = null
) : SettingsItem(setting, titleId, descriptionId) {
override val type = TYPE_SLIDER

View File

@@ -5,24 +5,25 @@ package org.yuzu.yuzu_emu.features.settings.model.view
import org.yuzu.yuzu_emu.features.settings.model.AbstractSetting
import org.yuzu.yuzu_emu.features.settings.model.AbstractStringSetting
import org.yuzu.yuzu_emu.features.settings.model.StringSetting
class StringSingleChoiceSetting(
val key: String? = null,
setting: AbstractSetting?,
titleId: Int,
descriptionId: Int,
val choicesId: Array<String>,
private val valuesId: Array<String>?,
val choices: Array<String>,
val values: Array<String>?,
val key: String? = null,
private val defaultValue: String? = null
) : SettingsItem(setting, titleId, descriptionId) {
override val type = TYPE_STRING_SINGLE_CHOICE
fun getValueAt(index: Int): String? {
if (valuesId == null) return null
return if (index >= 0 && index < valuesId.size) {
valuesId[index]
} else ""
if (values == null) return null
return if (index >= 0 && index < values.size) {
values[index]
} else {
""
}
}
val selectedValue: String
@@ -35,8 +36,8 @@ class StringSingleChoiceSetting(
val selectValueIndex: Int
get() {
val selectedValue = selectedValue
for (i in valuesId!!.indices) {
if (valuesId[i] == selectedValue) {
for (i in values!!.indices) {
if (values[i] == selectedValue) {
return i
}
}

View File

@@ -3,13 +3,10 @@
package org.yuzu.yuzu_emu.features.settings.model.view
import org.yuzu.yuzu_emu.features.settings.model.AbstractSetting
class SubmenuSetting(
setting: AbstractSetting?,
titleId: Int,
descriptionId: Int,
val menuKey: String
) : SettingsItem(setting, titleId, descriptionId) {
) : SettingsItem(null, titleId, descriptionId) {
override val type = TYPE_SUBMENU
}

View File

@@ -8,17 +8,18 @@ import android.content.Intent
import android.os.Bundle
import android.view.Menu
import android.view.View
import android.view.ViewGroup.MarginLayoutParams
import android.widget.Toast
import androidx.activity.OnBackPressedCallback
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.ViewCompat
import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsCompat
import android.view.ViewGroup.MarginLayoutParams
import androidx.activity.OnBackPressedCallback
import androidx.core.view.updatePadding
import com.google.android.material.color.MaterialColors
import org.yuzu.yuzu_emu.NativeLibrary
import java.io.IOException
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.databinding.ActivitySettingsBinding
import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting
@@ -29,7 +30,6 @@ import org.yuzu.yuzu_emu.features.settings.model.SettingsViewModel
import org.yuzu.yuzu_emu.features.settings.model.StringSetting
import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile
import org.yuzu.yuzu_emu.utils.*
import java.io.IOException
class SettingsActivity : AppCompatActivity(), SettingsActivityView {
private val presenter = SettingsActivityPresenter(this)
@@ -59,7 +59,9 @@ class SettingsActivity : AppCompatActivity(), SettingsActivityView {
setSupportActionBar(binding.toolbarSettings)
supportActionBar!!.setDisplayHomeAsUpEnabled(true)
if (InsetsHelper.getSystemGestureType(applicationContext) != InsetsHelper.GESTURE_NAVIGATION) {
if (InsetsHelper.getSystemGestureType(applicationContext) !=
InsetsHelper.GESTURE_NAVIGATION
) {
binding.navigationBarShade.setBackgroundColor(
ThemeHelper.getColorWithOpacity(
MaterialColors.getColor(
@@ -75,7 +77,8 @@ class SettingsActivity : AppCompatActivity(), SettingsActivityView {
this,
object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() = navigateBack()
})
}
)
setInsets()
}
@@ -148,11 +151,13 @@ class SettingsActivity : AppCompatActivity(), SettingsActivityView {
private fun areSystemAnimationsEnabled(): Boolean {
val duration = android.provider.Settings.Global.getFloat(
contentResolver,
android.provider.Settings.Global.ANIMATOR_DURATION_SCALE, 1f
android.provider.Settings.Global.ANIMATOR_DURATION_SCALE,
1f
)
val transition = android.provider.Settings.Global.getFloat(
contentResolver,
android.provider.Settings.Global.TRANSITION_ANIMATION_SCALE, 1f
android.provider.Settings.Global.TRANSITION_ANIMATION_SCALE,
1f
)
return duration != 0f && transition != 0f
}
@@ -207,7 +212,9 @@ class SettingsActivity : AppCompatActivity(), SettingsActivityView {
get() = supportFragmentManager.findFragmentByTag(FRAGMENT_TAG) as SettingsFragment?
private fun setInsets() {
ViewCompat.setOnApplyWindowInsetsListener(binding.frameContent) { view: View, windowInsets: WindowInsetsCompat ->
ViewCompat.setOnApplyWindowInsetsListener(
binding.frameContent
) { view: View, windowInsets: WindowInsetsCompat ->
val barInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
val cutoutInsets = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout())
view.updatePadding(
@@ -239,5 +246,17 @@ class SettingsActivity : AppCompatActivity(), SettingsActivityView {
settings.putExtra(ARG_GAME_ID, gameId)
context.startActivity(settings)
}
fun launch(
context: Context,
launcher: ActivityResultLauncher<Intent>,
menuTag: String?,
gameId: String?
) {
val settings = Intent(context, SettingsActivity::class.java)
settings.putExtra(ARG_MENU_TAG, menuTag)
settings.putExtra(ARG_GAME_ID, gameId)
launcher.launch(settings)
}
}
}

View File

@@ -6,12 +6,12 @@ package org.yuzu.yuzu_emu.features.settings.ui
import android.content.Context
import android.os.Bundle
import android.text.TextUtils
import java.io.File
import org.yuzu.yuzu_emu.NativeLibrary
import org.yuzu.yuzu_emu.features.settings.model.Settings
import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile
import org.yuzu.yuzu_emu.utils.DirectoryInitialization
import org.yuzu.yuzu_emu.utils.Log
import java.io.File
class SettingsActivityPresenter(private val activityView: SettingsActivityView) {
val settings: Settings get() = activityView.settings
@@ -46,9 +46,15 @@ class SettingsActivityPresenter(private val activityView: SettingsActivityView)
private fun prepareDirectoriesIfNeeded() {
val configFile =
File(DirectoryInitialization.userDirectory + "/config/" + SettingsFile.FILE_NAME_CONFIG + ".ini")
File(
"${DirectoryInitialization.userDirectory}/config/" +
"${SettingsFile.FILE_NAME_CONFIG}.ini"
)
if (!configFile.exists()) {
Log.error(DirectoryInitialization.userDirectory + "/config/" + SettingsFile.FILE_NAME_CONFIG + ".ini")
Log.error(
"${DirectoryInitialization.userDirectory}/config/" +
"${SettingsFile.FILE_NAME_CONFIG}.ini"
)
Log.error("yuzu config file could not be found!")
}

View File

@@ -13,7 +13,6 @@ import android.view.ViewGroup
import android.widget.TextView
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.setFragmentResultListener
import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.datepicker.MaterialDatePicker
import com.google.android.material.dialog.MaterialAlertDialogBuilder
@@ -139,7 +138,7 @@ class SettingsAdapter(
clickedItem = item
dialog = MaterialAlertDialogBuilder(context)
.setTitle(item.nameId)
.setSingleChoiceItems(item.choicesId, item.selectValueIndex, this)
.setSingleChoiceItems(item.choices, item.selectValueIndex, this)
.show()
}

View File

@@ -50,7 +50,10 @@ class SettingsFragment : Fragment(), SettingsFragmentView {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
settingsAdapter = SettingsAdapter(this, requireActivity())
val dividerDecoration = MaterialDividerItemDecoration(requireContext(), LinearLayoutManager.VERTICAL)
val dividerDecoration = MaterialDividerItemDecoration(
requireContext(),
LinearLayoutManager.VERTICAL
)
dividerDecoration.isLastItemDecorated = false
binding.listSettings.apply {
adapter = settingsAdapter
@@ -99,7 +102,9 @@ class SettingsFragment : Fragment(), SettingsFragmentView {
}
private fun setInsets() {
ViewCompat.setOnApplyWindowInsetsListener(binding.listSettings) { view: View, windowInsets: WindowInsetsCompat ->
ViewCompat.setOnApplyWindowInsetsListener(
binding.listSettings
) { view: View, windowInsets: WindowInsetsCompat ->
val insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
view.updatePadding(bottom = insets.bottom)
windowInsets

View File

@@ -7,7 +7,6 @@ import android.content.SharedPreferences
import android.os.Build
import android.text.TextUtils
import androidx.preference.PreferenceManager
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.YuzuApplication
import org.yuzu.yuzu_emu.features.settings.model.AbstractBooleanSetting
@@ -43,7 +42,7 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
}
fun putSetting(setting: AbstractSetting) {
if (setting.section == null) {
if (setting.section == null || setting.key == null) {
return
}
@@ -68,6 +67,7 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
Settings.SECTION_RENDERER -> addGraphicsSettings(sl)
Settings.SECTION_AUDIO -> addAudioSettings(sl)
Settings.SECTION_THEME -> addThemeSettings(sl)
Settings.SECTION_DEBUG -> addDebugSettings(sl)
else -> {
fragmentView.showToastMessage("Unimplemented menu", false)
return
@@ -78,11 +78,10 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
}
private fun addConfigSettings(sl: ArrayList<SettingsItem>) {
settingsActivity.setToolbarTitle(settingsActivity.getString(R.string.preferences_advanced_settings))
settingsActivity.setToolbarTitle(settingsActivity.getString(R.string.advanced_settings))
sl.apply {
add(
SubmenuSetting(
null,
R.string.preferences_general,
0,
Settings.SECTION_GENERAL
@@ -90,7 +89,6 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
)
add(
SubmenuSetting(
null,
R.string.preferences_system,
0,
Settings.SECTION_SYSTEM
@@ -98,7 +96,6 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
)
add(
SubmenuSetting(
null,
R.string.preferences_graphics,
0,
Settings.SECTION_RENDERER
@@ -106,12 +103,18 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
)
add(
SubmenuSetting(
null,
R.string.preferences_audio,
0,
Settings.SECTION_AUDIO
)
)
add(
SubmenuSetting(
R.string.preferences_debug,
0,
Settings.SECTION_DEBUG
)
)
add(
RunnableSetting(
R.string.reset_to_default,
@@ -162,6 +165,15 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
IntSetting.CPU_ACCURACY.defaultValue
)
)
add(
SwitchSetting(
BooleanSetting.PICTURE_IN_PICTURE,
R.string.picture_in_picture,
R.string.picture_in_picture_description,
BooleanSetting.PICTURE_IN_PICTURE.key,
BooleanSetting.PICTURE_IN_PICTURE.defaultValue
)
)
}
}
@@ -223,17 +235,6 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
private fun addGraphicsSettings(sl: ArrayList<SettingsItem>) {
settingsActivity.setToolbarTitle(settingsActivity.getString(R.string.preferences_graphics))
sl.apply {
add(
SingleChoiceSetting(
IntSetting.RENDERER_BACKEND,
R.string.renderer_api,
0,
R.array.rendererApiNames,
R.array.rendererApiValues,
IntSetting.RENDERER_BACKEND.key,
IntSetting.RENDERER_BACKEND.defaultValue
)
)
add(
SingleChoiceSetting(
IntSetting.RENDERER_ACCURACY,
@@ -289,6 +290,17 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
IntSetting.RENDERER_ANTI_ALIASING.defaultValue
)
)
add(
SingleChoiceSetting(
IntSetting.RENDERER_SCREEN_LAYOUT,
R.string.renderer_screen_layout,
0,
R.array.rendererScreenLayoutNames,
R.array.rendererScreenLayoutValues,
IntSetting.RENDERER_SCREEN_LAYOUT.key,
IntSetting.RENDERER_SCREEN_LAYOUT.defaultValue
)
)
add(
SingleChoiceSetting(
IntSetting.RENDERER_ASPECT_RATIO,
@@ -329,11 +341,11 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
)
add(
SwitchSetting(
IntSetting.RENDERER_DEBUG,
R.string.renderer_debug,
R.string.renderer_debug_description,
IntSetting.RENDERER_DEBUG.key,
IntSetting.RENDERER_DEBUG.defaultValue
IntSetting.RENDERER_REACTIVE_FLUSHING,
R.string.renderer_reactive_flushing,
R.string.renderer_reactive_flushing_description,
IntSetting.RENDERER_REACTIVE_FLUSHING.key,
IntSetting.RENDERER_REACTIVE_FLUSHING.defaultValue
)
)
}
@@ -341,18 +353,31 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
private fun addAudioSettings(sl: ArrayList<SettingsItem>) {
settingsActivity.setToolbarTitle(settingsActivity.getString(R.string.preferences_audio))
sl.add(
SliderSetting(
IntSetting.AUDIO_VOLUME,
R.string.audio_volume,
R.string.audio_volume_description,
0,
100,
"%",
IntSetting.AUDIO_VOLUME.key,
IntSetting.AUDIO_VOLUME.defaultValue
sl.apply {
add(
StringSingleChoiceSetting(
StringSetting.AUDIO_OUTPUT_ENGINE,
R.string.audio_output_engine,
0,
settingsActivity.resources.getStringArray(R.array.outputEngineEntries),
settingsActivity.resources.getStringArray(R.array.outputEngineValues),
StringSetting.AUDIO_OUTPUT_ENGINE.key,
StringSetting.AUDIO_OUTPUT_ENGINE.defaultValue
)
)
)
add(
SliderSetting(
IntSetting.AUDIO_VOLUME,
R.string.audio_volume,
R.string.audio_volume_description,
0,
100,
"%",
IntSetting.AUDIO_VOLUME.key,
IntSetting.AUDIO_VOLUME.defaultValue
)
)
}
}
private fun addThemeSettings(sl: ArrayList<SettingsItem>) {
@@ -451,4 +476,64 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
)
}
}
private fun addDebugSettings(sl: ArrayList<SettingsItem>) {
settingsActivity.setToolbarTitle(settingsActivity.getString(R.string.preferences_debug))
sl.apply {
add(HeaderSetting(R.string.gpu))
add(
SingleChoiceSetting(
IntSetting.RENDERER_BACKEND,
R.string.renderer_api,
0,
R.array.rendererApiNames,
R.array.rendererApiValues,
IntSetting.RENDERER_BACKEND.key,
IntSetting.RENDERER_BACKEND.defaultValue
)
)
add(
SwitchSetting(
IntSetting.RENDERER_DEBUG,
R.string.renderer_debug,
R.string.renderer_debug_description,
IntSetting.RENDERER_DEBUG.key,
IntSetting.RENDERER_DEBUG.defaultValue
)
)
add(HeaderSetting(R.string.cpu))
add(
SwitchSetting(
BooleanSetting.CPU_DEBUG_MODE,
R.string.cpu_debug_mode,
R.string.cpu_debug_mode_description,
BooleanSetting.CPU_DEBUG_MODE.key,
BooleanSetting.CPU_DEBUG_MODE.defaultValue
)
)
val fastmem = object : AbstractBooleanSetting {
override var boolean: Boolean
get() =
BooleanSetting.FASTMEM.boolean && BooleanSetting.FASTMEM_EXCLUSIVES.boolean
set(value) {
BooleanSetting.FASTMEM.boolean = value
BooleanSetting.FASTMEM_EXCLUSIVES.boolean = value
}
override val key: String? = null
override val section: String = Settings.SECTION_CPU
override val isRuntimeEditable: Boolean = false
override val valueAsString: String = ""
override val defaultValue: Any = true
}
add(
SwitchSetting(
fastmem,
R.string.fastmem,
0
)
)
}
}
}

View File

@@ -4,15 +4,15 @@
package org.yuzu.yuzu_emu.features.settings.ui.viewholder
import android.view.View
import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding
import org.yuzu.yuzu_emu.features.settings.model.view.DateTimeSetting
import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter
import java.time.Instant
import java.time.ZoneId
import java.time.ZonedDateTime
import java.time.format.DateTimeFormatter
import java.time.format.FormatStyle
import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding
import org.yuzu.yuzu_emu.features.settings.model.view.DateTimeSetting
import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter
class DateTimeViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAdapter) :
SettingViewHolder(binding.root, adapter) {

View File

@@ -26,6 +26,14 @@ class SingleChoiceViewHolder(val binding: ListItemSettingBinding, adapter: Setti
for (i in values.indices) {
if (values[i] == item.selectedValue) {
binding.textSettingDescription.text = resMgr.getStringArray(item.choicesId)[i]
return
}
}
} else if (item is StringSingleChoiceSetting) {
for (i in item.values!!.indices) {
if (item.values[i] == item.selectedValue) {
binding.textSettingDescription.text = item.choices[i]
return
}
}
} else {

View File

@@ -6,8 +6,8 @@ package org.yuzu.yuzu_emu.features.settings.ui.viewholder
import android.view.View
import android.widget.CompoundButton
import org.yuzu.yuzu_emu.databinding.ListItemSettingSwitchBinding
import org.yuzu.yuzu_emu.features.settings.model.view.SwitchSetting
import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
import org.yuzu.yuzu_emu.features.settings.model.view.SwitchSetting
import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter
class SwitchSettingViewHolder(val binding: ListItemSettingSwitchBinding, adapter: SettingsAdapter) :

View File

@@ -3,6 +3,8 @@
package org.yuzu.yuzu_emu.features.settings.utils
import java.io.*
import java.util.*
import org.ini4j.Wini
import org.yuzu.yuzu_emu.NativeLibrary
import org.yuzu.yuzu_emu.R
@@ -13,8 +15,6 @@ import org.yuzu.yuzu_emu.features.settings.ui.SettingsActivityView
import org.yuzu.yuzu_emu.utils.BiMap
import org.yuzu.yuzu_emu.utils.DirectoryInitialization
import org.yuzu.yuzu_emu.utils.Log
import java.io.*
import java.util.*
/**
* Contains static methods for interacting with .ini files in which settings are stored.
@@ -37,7 +37,7 @@ object SettingsFile {
private fun readFile(
ini: File?,
isCustomGame: Boolean,
view: SettingsActivityView?
view: SettingsActivityView? = null
): HashMap<String, SettingSection?> {
val sections: HashMap<String, SettingSection?> = SettingsSectionMap()
var reader: BufferedReader? = null
@@ -74,10 +74,13 @@ object SettingsFile {
return sections
}
fun readFile(fileName: String, view: SettingsActivityView): HashMap<String, SettingSection?> {
fun readFile(fileName: String, view: SettingsActivityView?): HashMap<String, SettingSection?> {
return readFile(getSettingsFile(fileName), false, view)
}
fun readFile(fileName: String): HashMap<String, SettingSection?> =
readFile(getSettingsFile(fileName), false)
/**
* Reads a given .ini file from disk and returns it as a HashMap of SettingSections, themselves
* effectively a HashMap of key/value settings. If unsuccessful, outputs an error telling why it
@@ -88,7 +91,7 @@ object SettingsFile {
*/
fun readCustomGameSettings(
gameId: String,
view: SettingsActivityView
view: SettingsActivityView?
): HashMap<String, SettingSection?> {
return readFile(getCustomGameSettingsFile(gameId), true, view)
}
@@ -134,9 +137,12 @@ object SettingsFile {
for (settingKey in sortedKeySet) {
val setting = settings[settingKey]
NativeLibrary.setUserSetting(
gameId, mapSectionNameFromIni(
gameId,
mapSectionNameFromIni(
section.name
), setting!!.key, setting.valueAsString
),
setting!!.key,
setting.valueAsString
)
}
}
@@ -145,13 +151,17 @@ object SettingsFile {
private fun mapSectionNameFromIni(generalSectionName: String): String? {
return if (sectionsMap.getForward(generalSectionName) != null) {
sectionsMap.getForward(generalSectionName)
} else generalSectionName
} else {
generalSectionName
}
}
private fun mapSectionNameToIni(generalSectionName: String): String {
return if (sectionsMap.getBackward(generalSectionName) != null) {
sectionsMap.getBackward(generalSectionName).toString()
} else generalSectionName
} else {
generalSectionName
}
}
fun getSettingsFile(fileName: String): File {
@@ -234,5 +244,21 @@ object SettingsFile {
val setting = settings[key]
parser.put(header, setting!!.key, setting.valueAsString)
}
BooleanSetting.values().forEach {
if (!keySet.contains(it.key)) {
parser.put(header, it.key, it.valueAsString)
}
}
IntSetting.values().forEach {
if (!keySet.contains(it.key)) {
parser.put(header, it.key, it.valueAsString)
}
}
StringSetting.values().forEach {
if (!keySet.contains(it.key)) {
parser.put(header, it.key, it.valueAsString)
}
}
}
}

View File

@@ -15,13 +15,12 @@ import android.view.View
import android.view.ViewGroup
import android.view.ViewGroup.MarginLayoutParams
import android.widget.Toast
import androidx.core.content.ContextCompat
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.updatePadding
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.navigation.fragment.findNavController
import androidx.navigation.findNavController
import com.google.android.material.transition.MaterialSharedAxis
import org.yuzu.yuzu_emu.BuildConfig
import org.yuzu.yuzu_emu.R
@@ -38,6 +37,7 @@ class AboutFragment : Fragment() {
super.onCreate(savedInstanceState)
enterTransition = MaterialSharedAxis(MaterialSharedAxis.X, true)
returnTransition = MaterialSharedAxis(MaterialSharedAxis.X, false)
reenterTransition = MaterialSharedAxis(MaterialSharedAxis.X, false)
}
override fun onCreateView(
@@ -54,7 +54,7 @@ class AboutFragment : Fragment() {
homeViewModel.setStatusBarShadeVisibility(visible = false)
binding.toolbarAbout.setNavigationOnClickListener {
parentFragmentManager.primaryNavigationFragment?.findNavController()?.popBackStack()
binding.root.findNavController().popBackStack()
}
binding.imageLogo.setOnLongClickListener {
@@ -66,7 +66,15 @@ class AboutFragment : Fragment() {
true
}
binding.buttonContributors.setOnClickListener { openLink(getString(R.string.contributors_link)) }
binding.buttonContributors.setOnClickListener {
openLink(
getString(R.string.contributors_link)
)
}
binding.buttonLicenses.setOnClickListener {
exitTransition = MaterialSharedAxis(MaterialSharedAxis.X, true)
binding.root.findNavController().navigate(R.id.action_aboutFragment_to_licensesFragment)
}
binding.textBuildHash.text = BuildConfig.GIT_HASH
binding.buttonBuildHash.setOnClickListener {
@@ -97,7 +105,9 @@ class AboutFragment : Fragment() {
}
private fun setInsets() =
ViewCompat.setOnApplyWindowInsetsListener(binding.root) { _: View, windowInsets: WindowInsetsCompat ->
ViewCompat.setOnApplyWindowInsetsListener(
binding.root
) { _: View, windowInsets: WindowInsetsCompat ->
val barInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
val cutoutInsets = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout())

View File

@@ -49,7 +49,11 @@ class EarlyAccessFragment : Fragment() {
parentFragmentManager.primaryNavigationFragment?.findNavController()?.popBackStack()
}
binding.getEarlyAccessButton.setOnClickListener { openLink(getString(R.string.play_store_link)) }
binding.getEarlyAccessButton.setOnClickListener {
openLink(
getString(R.string.play_store_link)
)
}
setInsets()
}
@@ -60,7 +64,9 @@ class EarlyAccessFragment : Fragment() {
}
private fun setInsets() =
ViewCompat.setOnApplyWindowInsetsListener(binding.root) { _: View, windowInsets: WindowInsetsCompat ->
ViewCompat.setOnApplyWindowInsetsListener(
binding.root
) { _: View, windowInsets: WindowInsetsCompat ->
val barInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
val cutoutInsets = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout())

View File

@@ -7,35 +7,51 @@ import android.annotation.SuppressLint
import android.app.AlertDialog
import android.content.Context
import android.content.DialogInterface
import android.content.Intent
import android.content.SharedPreferences
import android.content.pm.ActivityInfo
import android.content.res.Configuration
import android.graphics.Color
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.util.Rational
import android.view.*
import android.widget.TextView
import androidx.activity.OnBackPressedCallback
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.widget.PopupMenu
import androidx.core.content.res.ResourcesCompat
import androidx.core.graphics.Insets
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import androidx.navigation.fragment.navArgs
import androidx.preference.PreferenceManager
import androidx.window.layout.FoldingFeature
import androidx.window.layout.WindowInfoTracker
import androidx.window.layout.WindowLayoutInfo
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.slider.Slider
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import org.yuzu.yuzu_emu.NativeLibrary
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.YuzuApplication
import org.yuzu.yuzu_emu.activities.EmulationActivity
import org.yuzu.yuzu_emu.databinding.DialogOverlayAdjustBinding
import org.yuzu.yuzu_emu.databinding.FragmentEmulationBinding
import org.yuzu.yuzu_emu.features.settings.model.IntSetting
import org.yuzu.yuzu_emu.features.settings.model.Settings
import org.yuzu.yuzu_emu.features.settings.ui.SettingsActivity
import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile
import org.yuzu.yuzu_emu.model.Game
import org.yuzu.yuzu_emu.overlay.InputOverlay
import org.yuzu.yuzu_emu.utils.*
import org.yuzu.yuzu_emu.utils.SerializableHelper.parcelable
class EmulationFragment : Fragment(), SurfaceHolder.Callback {
private lateinit var preferences: SharedPreferences
@@ -46,13 +62,30 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
private var _binding: FragmentEmulationBinding? = null
private val binding get() = _binding!!
private lateinit var game: Game
val args by navArgs<EmulationFragmentArgs>()
private var isInFoldableLayout = false
private lateinit var onReturnFromSettings: ActivityResultLauncher<Intent>
override fun onAttach(context: Context) {
super.onAttach(context)
if (context is EmulationActivity) {
emulationActivity = context
NativeLibrary.setEmulationActivity(context)
lifecycleScope.launch(Dispatchers.Main) {
lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
WindowInfoTracker.getOrCreate(context)
.windowLayoutInfo(context)
.collect { updateFoldableLayout(context, it) }
}
}
onReturnFromSettings = context.activityResultRegistry.register(
"SettingsResult",
ActivityResultContracts.StartActivityForResult()
) { updateScreenLayout() }
} else {
throw IllegalStateException("EmulationFragment must have EmulationActivity parent")
}
@@ -67,8 +100,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
// So this fragment doesn't restart on configuration changes; i.e. rotation.
retainInstance = true
preferences = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
game = requireArguments().parcelable(EmulationActivity.EXTRA_SELECTED_GAME)!!
emulationState = EmulationState(game.path)
emulationState = EmulationState(args.game.path)
}
/**
@@ -92,7 +124,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
updateShowFpsOverlay()
binding.inGameMenu.getHeaderView(0).findViewById<TextView>(R.id.text_game_title).text =
game.title
args.game.title
binding.inGameMenu.setNavigationItemSelectedListener {
when (it.itemId) {
R.id.menu_pause_emulation -> {
@@ -117,7 +149,12 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
}
R.id.menu_settings -> {
SettingsActivity.launch(requireContext(), SettingsFile.FILE_NAME_CONFIG, "")
SettingsActivity.launch(
requireContext(),
onReturnFromSettings,
SettingsFile.FILE_NAME_CONFIG,
""
)
true
}
@@ -142,9 +179,48 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
requireActivity(),
object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
if (binding.drawerLayout.isOpen) binding.drawerLayout.close() else binding.drawerLayout.open()
if (binding.drawerLayout.isOpen) {
binding.drawerLayout.close()
} else {
binding.drawerLayout.open()
}
}
})
}
)
viewLifecycleOwner.lifecycleScope.launch(Dispatchers.Main) {
lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
WindowInfoTracker.getOrCreate(requireContext())
.windowLayoutInfo(requireActivity())
.collect { updateFoldableLayout(requireActivity() as EmulationActivity, it) }
}
}
}
override fun onConfigurationChanged(newConfig: Configuration) {
super.onConfigurationChanged(newConfig)
if (emulationActivity?.isInPictureInPictureMode == true) {
if (binding.drawerLayout.isOpen) {
binding.drawerLayout.close()
}
if (EmulationMenuSettings.showOverlay) {
binding.surfaceInputOverlay.post { binding.surfaceInputOverlay.isVisible = false }
}
} else {
if (EmulationMenuSettings.showOverlay) {
binding.surfaceInputOverlay.post { binding.surfaceInputOverlay.isVisible = true }
}
if (!isInFoldableLayout) {
if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) {
binding.surfaceInputOverlay.orientation = InputOverlay.PORTRAIT
} else {
binding.surfaceInputOverlay.orientation = InputOverlay.LANDSCAPE
}
}
if (!binding.surfaceInputOverlay.isInEditMode) {
refreshInputOverlay()
}
}
}
override fun onResume() {
@@ -152,6 +228,9 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
if (!DirectoryInitialization.areDirectoriesReady) {
DirectoryInitialization.start(requireContext())
}
updateScreenLayout()
emulationState.run(emulationActivity!!.isActivityRecreated)
}
@@ -211,6 +290,74 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
}
}
@SuppressLint("SourceLockedOrientationActivity")
private fun updateOrientation() {
emulationActivity?.let {
it.requestedOrientation = when (IntSetting.RENDERER_SCREEN_LAYOUT.int) {
Settings.LayoutOption_MobileLandscape ->
ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE
Settings.LayoutOption_MobilePortrait ->
ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT
Settings.LayoutOption_Unspecified -> ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
else -> ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE
}
}
}
private fun updateScreenLayout() {
binding.surfaceEmulation.setAspectRatio(
when (IntSetting.RENDERER_ASPECT_RATIO.int) {
0 -> Rational(16, 9)
1 -> Rational(4, 3)
2 -> Rational(21, 9)
3 -> Rational(16, 10)
4 -> null // Stretch
else -> Rational(16, 9)
}
)
emulationActivity?.buildPictureInPictureParams()
updateOrientation()
}
private fun updateFoldableLayout(
emulationActivity: EmulationActivity,
newLayoutInfo: WindowLayoutInfo
) {
val isFolding =
(newLayoutInfo.displayFeatures.find { it is FoldingFeature } as? FoldingFeature)?.let {
if (it.isSeparating) {
emulationActivity.requestedOrientation =
ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
if (it.orientation == FoldingFeature.Orientation.HORIZONTAL) {
// Restrict emulation and overlays to the top of the screen
binding.emulationContainer.layoutParams.height = it.bounds.top
binding.overlayContainer.layoutParams.height = it.bounds.top
// Restrict input and menu drawer to the bottom of the screen
binding.inputContainer.layoutParams.height = it.bounds.bottom
binding.inGameMenu.layoutParams.height = it.bounds.bottom
isInFoldableLayout = true
binding.surfaceInputOverlay.orientation = InputOverlay.FOLDABLE
refreshInputOverlay()
}
}
it.isSeparating
} ?: false
if (!isFolding) {
binding.emulationContainer.layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT
binding.inputContainer.layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT
binding.overlayContainer.layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT
binding.inGameMenu.layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT
isInFoldableLayout = false
updateOrientation()
onConfigurationChanged(resources.configuration)
}
binding.emulationContainer.requestLayout()
binding.inputContainer.requestLayout()
binding.overlayContainer.requestLayout()
binding.inGameMenu.requestLayout()
}
override fun surfaceCreated(holder: SurfaceHolder) {
// We purposely don't do anything here.
// All work is done in surfaceChanged, which we are guaranteed to get even for surface creation.
@@ -281,6 +428,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
.setPositiveButton(android.R.string.ok) { _, _ ->
refreshInputOverlay()
}
.setNegativeButton(android.R.string.cancel, null)
.setNeutralButton(R.string.emulation_toggle_all) { _, _ -> }
.show()
@@ -337,7 +485,19 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
popup.show()
}
@SuppressLint("SourceLockedOrientationActivity")
private fun startConfiguringControls() {
// Lock the current orientation to prevent editing inconsistencies
if (IntSetting.RENDERER_SCREEN_LAYOUT.int == Settings.LayoutOption_Unspecified) {
emulationActivity?.let {
it.requestedOrientation =
if (resources.configuration.orientation == Configuration.ORIENTATION_PORTRAIT) {
ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT
} else {
ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE
}
}
}
binding.doneControlConfig.visibility = View.VISIBLE
binding.surfaceInputOverlay.setIsInEditMode(true)
}
@@ -345,6 +505,12 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
private fun stopConfiguringControls() {
binding.doneControlConfig.visibility = View.GONE
binding.surfaceInputOverlay.setIsInEditMode(false)
// Unlock the orientation if it was locked for editing
if (IntSetting.RENDERER_SCREEN_LAYOUT.int == Settings.LayoutOption_Unspecified) {
emulationActivity?.let {
it.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
}
}
}
@SuppressLint("SetTextI18n")
@@ -354,18 +520,22 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
inputScaleSlider.apply {
valueTo = 150F
value = preferences.getInt(Settings.PREF_CONTROL_SCALE, 50).toFloat()
addOnChangeListener(Slider.OnChangeListener { _, value, _ ->
inputScaleValue.text = "${value.toInt()}%"
setControlScale(value.toInt())
})
addOnChangeListener(
Slider.OnChangeListener { _, value, _ ->
inputScaleValue.text = "${value.toInt()}%"
setControlScale(value.toInt())
}
)
}
inputOpacitySlider.apply {
valueTo = 100F
value = preferences.getInt(Settings.PREF_CONTROL_OPACITY, 100).toFloat()
addOnChangeListener(Slider.OnChangeListener { _, value, _ ->
inputOpacityValue.text = "${value.toInt()}%"
setControlOpacity(value.toInt())
})
addOnChangeListener(
Slider.OnChangeListener { _, value, _ ->
inputOpacityValue.text = "${value.toInt()}%"
setControlOpacity(value.toInt())
}
)
}
inputScaleValue.text = "${inputScaleSlider.value.toInt()}%"
inputOpacityValue.text = "${inputOpacitySlider.value.toInt()}%"
@@ -397,7 +567,9 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
}
private fun setInsets() {
ViewCompat.setOnApplyWindowInsetsListener(binding.inGameMenu) { v: View, windowInsets: WindowInsetsCompat ->
ViewCompat.setOnApplyWindowInsetsListener(
binding.inGameMenu
) { v: View, windowInsets: WindowInsetsCompat ->
val cutInsets: Insets = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout())
var left = 0
var right = 0
@@ -517,8 +689,12 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
state = State.PAUSED
}
State.PAUSED -> Log.warning("[EmulationFragment] Surface cleared while emulation paused.")
else -> Log.warning("[EmulationFragment] Surface cleared while emulation stopped.")
State.PAUSED -> Log.warning(
"[EmulationFragment] Surface cleared while emulation paused."
)
else -> Log.warning(
"[EmulationFragment] Surface cleared while emulation stopped."
)
}
}
}
@@ -538,7 +714,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
State.PAUSED -> {
Log.debug("[EmulationFragment] Resuming emulation.")
NativeLibrary.surfaceChanged(surface)
NativeLibrary.unPauseEmulation()
NativeLibrary.unpauseEmulation()
}
else -> Log.debug("[EmulationFragment] Bug, run called while already running.")
@@ -553,13 +729,5 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
companion object {
private val perfStatsUpdateHandler = Handler(Looper.myLooper()!!)
fun newInstance(game: Game): EmulationFragment {
val args = Bundle()
args.putParcelable(EmulationActivity.EXTRA_SELECTED_GAME, game)
val fragment = EmulationFragment()
fragment.arguments = args
return fragment
}
}
}

View File

@@ -19,10 +19,10 @@ import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
import androidx.core.content.ContextCompat
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.updatePadding
import androidx.documentfile.provider.DocumentFile
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.navigation.fragment.findNavController
@@ -40,6 +40,7 @@ import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile
import org.yuzu.yuzu_emu.model.HomeSetting
import org.yuzu.yuzu_emu.model.HomeViewModel
import org.yuzu.yuzu_emu.ui.main.MainActivity
import org.yuzu.yuzu_emu.utils.FileUtil
import org.yuzu.yuzu_emu.utils.GpuDriverHelper
class HomeSettingsFragment : Fragment() {
@@ -67,57 +68,109 @@ class HomeSettingsFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
mainActivity = requireActivity() as MainActivity
val optionsList: MutableList<HomeSetting> = mutableListOf(
HomeSetting(
R.string.advanced_settings,
R.string.settings_description,
R.drawable.ic_settings
) { SettingsActivity.launch(requireContext(), SettingsFile.FILE_NAME_CONFIG, "") },
HomeSetting(
R.string.open_user_folder,
R.string.open_user_folder_description,
R.drawable.ic_folder_open
) { openFileManager() },
HomeSetting(
R.string.preferences_theme,
R.string.theme_and_color_description,
R.drawable.ic_palette
) { SettingsActivity.launch(requireContext(), Settings.SECTION_THEME, "") },
HomeSetting(
R.string.install_gpu_driver,
R.string.install_gpu_driver_description,
R.drawable.ic_exit
) { driverInstaller() },
HomeSetting(
R.string.install_amiibo_keys,
R.string.install_amiibo_keys_description,
R.drawable.ic_nfc
) { mainActivity.getAmiiboKey.launch(arrayOf("*/*")) },
HomeSetting(
R.string.select_games_folder,
R.string.select_games_folder_description,
R.drawable.ic_add
) { mainActivity.getGamesDirectory.launch(Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).data) },
HomeSetting(
R.string.manage_save_data,
R.string.import_export_saves_description,
R.drawable.ic_save
) { ImportExportSavesFragment().show(parentFragmentManager, ImportExportSavesFragment.TAG) },
HomeSetting(
R.string.install_prod_keys,
R.string.install_prod_keys_description,
R.drawable.ic_unlock
) { mainActivity.getProdKey.launch(arrayOf("*/*")) },
HomeSetting(
R.string.about,
R.string.about_description,
R.drawable.ic_info_outline
) {
exitTransition = MaterialSharedAxis(MaterialSharedAxis.X, true)
parentFragmentManager.primaryNavigationFragment?.findNavController()
?.navigate(R.id.action_homeSettingsFragment_to_aboutFragment)
val optionsList: MutableList<HomeSetting> = mutableListOf<HomeSetting>().apply {
add(
HomeSetting(
R.string.advanced_settings,
R.string.settings_description,
R.drawable.ic_settings
) { SettingsActivity.launch(requireContext(), SettingsFile.FILE_NAME_CONFIG, "") }
)
add(
HomeSetting(
R.string.open_user_folder,
R.string.open_user_folder_description,
R.drawable.ic_folder_open
) { openFileManager() }
)
add(
HomeSetting(
R.string.preferences_theme,
R.string.theme_and_color_description,
R.drawable.ic_palette
) { SettingsActivity.launch(requireContext(), Settings.SECTION_THEME, "") }
)
if (GpuDriverHelper.supportsCustomDriverLoading()) {
add(
HomeSetting(
R.string.install_gpu_driver,
R.string.install_gpu_driver_description,
R.drawable.ic_exit
) { driverInstaller() }
)
}
)
add(
HomeSetting(
R.string.install_amiibo_keys,
R.string.install_amiibo_keys_description,
R.drawable.ic_nfc
) { mainActivity.getAmiiboKey.launch(arrayOf("*/*")) }
)
add(
HomeSetting(
R.string.install_game_content,
R.string.install_game_content_description,
R.drawable.ic_system_update_alt
) { mainActivity.installGameUpdate.launch(arrayOf("*/*")) }
)
add(
HomeSetting(
R.string.select_games_folder,
R.string.select_games_folder_description,
R.drawable.ic_add
) {
mainActivity.getGamesDirectory.launch(
Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).data
)
}
)
add(
HomeSetting(
R.string.manage_save_data,
R.string.import_export_saves_description,
R.drawable.ic_save
) {
ImportExportSavesFragment().show(
parentFragmentManager,
ImportExportSavesFragment.TAG
)
}
)
add(
HomeSetting(
R.string.install_prod_keys,
R.string.install_prod_keys_description,
R.drawable.ic_unlock
) { mainActivity.getProdKey.launch(arrayOf("*/*")) }
)
add(
HomeSetting(
R.string.install_firmware,
R.string.install_firmware_description,
R.drawable.ic_firmware
) { mainActivity.getFirmware.launch(arrayOf("application/zip")) }
)
add(
HomeSetting(
R.string.share_log,
R.string.share_log_description,
R.drawable.ic_log
) { shareLog() }
)
add(
HomeSetting(
R.string.about,
R.string.about_description,
R.drawable.ic_info_outline
) {
exitTransition = MaterialSharedAxis(MaterialSharedAxis.X, true)
parentFragmentManager.primaryNavigationFragment?.findNavController()
?.navigate(R.id.action_homeSettingsFragment_to_aboutFragment)
}
)
}
if (!BuildConfig.PREMIUM) {
optionsList.add(
@@ -204,7 +257,11 @@ class HomeSettingsFragment : Fragment() {
val intent = Intent(action)
intent.addCategory(Intent.CATEGORY_DEFAULT)
intent.data = DocumentsContract.buildRootUri(authority, DocumentProvider.ROOT_ID)
intent.addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION or Intent.FLAG_GRANT_PREFIX_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
intent.addFlags(
Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION or
Intent.FLAG_GRANT_PREFIX_URI_PERMISSION or
Intent.FLAG_GRANT_WRITE_URI_PERMISSION
)
return intent
}
@@ -262,8 +319,33 @@ class HomeSettingsFragment : Fragment() {
.show()
}
private fun shareLog() {
val file = DocumentFile.fromSingleUri(
mainActivity,
DocumentsContract.buildDocumentUri(
DocumentProvider.AUTHORITY,
"${DocumentProvider.ROOT_ID}/log/yuzu_log.txt"
)
)!!
if (file.exists()) {
val intent = Intent(Intent.ACTION_SEND)
.setDataAndType(file.uri, FileUtil.TEXT_PLAIN)
.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
.putExtra(Intent.EXTRA_STREAM, file.uri)
startActivity(Intent.createChooser(intent, getText(R.string.share_log)))
} else {
Toast.makeText(
requireContext(),
getText(R.string.share_log_missing),
Toast.LENGTH_SHORT
).show()
}
}
private fun setInsets() =
ViewCompat.setOnApplyWindowInsetsListener(binding.root) { view: View, windowInsets: WindowInsetsCompat ->
ViewCompat.setOnApplyWindowInsetsListener(
binding.root
) { view: View, windowInsets: WindowInsetsCompat ->
val barInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
val cutoutInsets = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout())
val spacingNavigation = resources.getDimensionPixelSize(R.dimen.spacing_navigation)

View File

@@ -11,9 +11,18 @@ import android.provider.DocumentsContract
import android.widget.Toast
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity
import androidx.documentfile.provider.DocumentFile
import androidx.fragment.app.DialogFragment
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import java.io.BufferedOutputStream
import java.io.File
import java.io.FileOutputStream
import java.io.FilenameFilter
import java.time.LocalDateTime
import java.time.format.DateTimeFormatter
import java.util.zip.ZipEntry
import java.util.zip.ZipOutputStream
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
@@ -22,18 +31,7 @@ import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.YuzuApplication
import org.yuzu.yuzu_emu.features.DocumentProvider
import org.yuzu.yuzu_emu.getPublicFilesDir
import java.io.BufferedInputStream
import java.io.BufferedOutputStream
import java.io.File
import java.io.FileOutputStream
import java.io.FilenameFilter
import java.io.IOException
import java.io.InputStream
import java.time.LocalDateTime
import java.time.format.DateTimeFormatter
import java.util.zip.ZipEntry
import java.util.zip.ZipInputStream
import java.util.zip.ZipOutputStream
import org.yuzu.yuzu_emu.utils.FileUtil
class ImportExportSavesFragment : DialogFragment() {
private val context = YuzuApplication.appContext
@@ -49,6 +47,7 @@ class ImportExportSavesFragment : DialogFragment() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val activity = requireActivity() as AppCompatActivity
val activityResultRegistry = requireActivity().activityResultRegistry
startForResultExportSave = activityResultRegistry.register(
@@ -61,7 +60,7 @@ class ImportExportSavesFragment : DialogFragment() {
"documentPickerKey",
ActivityResultContracts.OpenDocument()
) {
it?.let { uri -> importSave(uri) }
it?.let { uri -> importSave(uri, activity) }
}
}
@@ -99,7 +98,7 @@ class ImportExportSavesFragment : DialogFragment() {
val outputZipFile = File(
tempFolder,
"yuzu saves - ${
LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"))
LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"))
}.zip"
)
outputZipFile.createNewFile()
@@ -107,12 +106,14 @@ class ImportExportSavesFragment : DialogFragment() {
saveFolder.walkTopDown().forEach { file ->
val zipFileName =
file.absolutePath.removePrefix(savesFolderRoot).removePrefix("/")
if (zipFileName == "")
if (zipFileName == "") {
return@forEach
}
val entry = ZipEntry("$zipFileName${(if (file.isDirectory) "/" else "")}")
zos.putNextEntry(entry)
if (file.isFile)
if (file.isFile) {
file.inputStream().use { fis -> fis.copyTo(zos) }
}
}
}
lastZipCreated = outputZipFile
@@ -122,33 +123,6 @@ class ImportExportSavesFragment : DialogFragment() {
return true
}
/**
* Extracts the save files located in the given zip file and copies them to the saves folder.
* @exception IOException if the file was being created outside of the target directory
*/
private fun unzip(zipStream: InputStream, destDir: File): Boolean {
val zis = ZipInputStream(BufferedInputStream(zipStream))
var entry: ZipEntry? = zis.nextEntry
while (entry != null) {
val entryName = entry.name
val entryFile = File(destDir, entryName)
if (!entryFile.canonicalPath.startsWith(destDir.canonicalPath + File.separator)) {
zis.close()
throw IOException("Entry is outside of the target dir: " + entryFile.name)
}
if (entry.isDirectory) {
entryFile.mkdirs()
} else {
entryFile.parentFile?.mkdirs()
entryFile.createNewFile()
entryFile.outputStream().use { fos -> zis.copyTo(fos) }
}
entry = zis.nextEntry
}
zis.close()
return true
}
/**
* Exports the save file located in the given folder path by creating a zip file and sharing it via intent.
*/
@@ -165,7 +139,8 @@ class ImportExportSavesFragment : DialogFragment() {
withContext(Dispatchers.Main) {
val file = DocumentFile.fromSingleUri(
context, DocumentsContract.buildDocumentUri(
context,
DocumentsContract.buildDocumentUri(
DocumentProvider.AUTHORITY,
"${DocumentProvider.ROOT_ID}/temp/${lastZipFile.name}"
)
@@ -183,7 +158,7 @@ class ImportExportSavesFragment : DialogFragment() {
* Imports the save files contained in the zip file, and replaces any existing ones with the new save file.
* @param zipUri The Uri of the zip file containing the save file(s) to import.
*/
private fun importSave(zipUri: Uri) {
private fun importSave(zipUri: Uri, activity: AppCompatActivity) {
val inputZip = context.contentResolver.openInputStream(zipUri)
// A zip needs to have at least one subfolder named after a TitleId in order to be considered valid.
var validZip = false
@@ -202,7 +177,7 @@ class ImportExportSavesFragment : DialogFragment() {
try {
CoroutineScope(Dispatchers.IO).launch {
unzip(inputZip, cacheSaveDir)
FileUtil.unzip(inputZip, cacheSaveDir)
cacheSaveDir.list(filterTitleId)?.forEach { savePath ->
File(savesFolder, savePath).deleteRecursively()
File(cacheSaveDir, savePath).copyRecursively(File(savesFolder, savePath), true)
@@ -214,7 +189,7 @@ class ImportExportSavesFragment : DialogFragment() {
MessageDialogFragment.newInstance(
R.string.save_file_invalid_zip_structure,
R.string.save_file_invalid_zip_structure_description
).show(childFragmentManager, MessageDialogFragment.TAG)
).show(activity.supportFragmentManager, MessageDialogFragment.TAG)
return@withContext
}
Toast.makeText(

View File

@@ -0,0 +1,69 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu.fragments
import android.app.Dialog
import android.os.Bundle
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.DialogFragment
import androidx.fragment.app.activityViewModels
import androidx.lifecycle.ViewModelProvider
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import org.yuzu.yuzu_emu.databinding.DialogProgressBarBinding
import org.yuzu.yuzu_emu.model.TaskViewModel
class IndeterminateProgressDialogFragment : DialogFragment() {
private val taskViewModel: TaskViewModel by activityViewModels()
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val titleId = requireArguments().getInt(TITLE)
val progressBinding = DialogProgressBarBinding.inflate(layoutInflater)
progressBinding.progressBar.isIndeterminate = true
val dialog = MaterialAlertDialogBuilder(requireContext())
.setTitle(titleId)
.setView(progressBinding.root)
.create()
dialog.setCanceledOnTouchOutside(false)
taskViewModel.isComplete.observe(this) { complete ->
if (complete) {
dialog.dismiss()
when (val result = taskViewModel.result.value) {
is String -> Toast.makeText(requireContext(), result, Toast.LENGTH_LONG).show()
is MessageDialogFragment -> result.show(
parentFragmentManager,
MessageDialogFragment.TAG
)
}
taskViewModel.clear()
}
}
if (taskViewModel.isRunning.value == false) {
taskViewModel.runTask()
}
return dialog
}
companion object {
const val TAG = "IndeterminateProgressDialogFragment"
private const val TITLE = "Title"
fun newInstance(
activity: AppCompatActivity,
titleId: Int,
task: () -> Any
): IndeterminateProgressDialogFragment {
val dialog = IndeterminateProgressDialogFragment()
val args = Bundle()
ViewModelProvider(activity)[TaskViewModel::class.java].task = task
args.putInt(TITLE, titleId)
dialog.arguments = args
return dialog
}
}
}

View File

@@ -0,0 +1,59 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu.fragments
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
import org.yuzu.yuzu_emu.databinding.DialogLicenseBinding
import org.yuzu.yuzu_emu.model.License
import org.yuzu.yuzu_emu.utils.SerializableHelper.parcelable
class LicenseBottomSheetDialogFragment : BottomSheetDialogFragment() {
private var _binding: DialogLicenseBinding? = null
private val binding get() = _binding!!
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = DialogLicenseBinding.inflate(layoutInflater)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
BottomSheetBehavior.from<View>(view.parent as View).state =
BottomSheetBehavior.STATE_HALF_EXPANDED
val license = requireArguments().parcelable<License>(LICENSE)!!
binding.apply {
textTitle.setText(license.titleId)
textLink.setText(license.linkId)
textCopyright.setText(license.copyrightId)
textLicense.setText(license.licenseId)
}
}
companion object {
const val TAG = "LicenseBottomSheetDialogFragment"
const val LICENSE = "License"
fun newInstance(
license: License
): LicenseBottomSheetDialogFragment {
val dialog = LicenseBottomSheetDialogFragment()
val bundle = Bundle()
bundle.putParcelable(LICENSE, license)
dialog.arguments = bundle
return dialog
}
}
}

View File

@@ -0,0 +1,139 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu.fragments
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.ViewGroup.MarginLayoutParams
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.updatePadding
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.navigation.findNavController
import androidx.recyclerview.widget.LinearLayoutManager
import com.google.android.material.transition.MaterialSharedAxis
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.adapters.LicenseAdapter
import org.yuzu.yuzu_emu.databinding.FragmentLicensesBinding
import org.yuzu.yuzu_emu.model.HomeViewModel
import org.yuzu.yuzu_emu.model.License
class LicensesFragment : Fragment() {
private var _binding: FragmentLicensesBinding? = null
private val binding get() = _binding!!
private val homeViewModel: HomeViewModel by activityViewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enterTransition = MaterialSharedAxis(MaterialSharedAxis.X, true)
returnTransition = MaterialSharedAxis(MaterialSharedAxis.X, false)
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = FragmentLicensesBinding.inflate(layoutInflater)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
homeViewModel.setNavigationVisibility(visible = false, animated = true)
homeViewModel.setStatusBarShadeVisibility(visible = false)
binding.toolbarLicenses.setNavigationOnClickListener {
binding.root.findNavController().popBackStack()
}
val licenses = listOf(
License(
R.string.license_fidelityfx_fsr,
R.string.license_fidelityfx_fsr_description,
R.string.license_fidelityfx_fsr_link,
R.string.license_fidelityfx_fsr_copyright,
R.string.license_fidelityfx_fsr_text
),
License(
R.string.license_cubeb,
R.string.license_cubeb_description,
R.string.license_cubeb_link,
R.string.license_cubeb_copyright,
R.string.license_cubeb_text
),
License(
R.string.license_dynarmic,
R.string.license_dynarmic_description,
R.string.license_dynarmic_link,
R.string.license_dynarmic_copyright,
R.string.license_dynarmic_text
),
License(
R.string.license_ffmpeg,
R.string.license_ffmpeg_description,
R.string.license_ffmpeg_link,
R.string.license_ffmpeg_copyright,
R.string.license_ffmpeg_text
),
License(
R.string.license_opus,
R.string.license_opus_description,
R.string.license_opus_link,
R.string.license_opus_copyright,
R.string.license_opus_text
),
License(
R.string.license_sirit,
R.string.license_sirit_description,
R.string.license_sirit_link,
R.string.license_sirit_copyright,
R.string.license_sirit_text
),
License(
R.string.license_adreno_tools,
R.string.license_adreno_tools_description,
R.string.license_adreno_tools_link,
R.string.license_adreno_tools_copyright,
R.string.license_adreno_tools_text
)
)
binding.listLicenses.apply {
layoutManager = LinearLayoutManager(requireContext())
adapter = LicenseAdapter(requireActivity() as AppCompatActivity, licenses)
}
setInsets()
}
private fun setInsets() =
ViewCompat.setOnApplyWindowInsetsListener(
binding.root
) { _: View, windowInsets: WindowInsetsCompat ->
val barInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
val cutoutInsets = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout())
val leftInsets = barInsets.left + cutoutInsets.left
val rightInsets = barInsets.right + cutoutInsets.right
val mlpAppBar = binding.appbarLicenses.layoutParams as MarginLayoutParams
mlpAppBar.leftMargin = leftInsets
mlpAppBar.rightMargin = rightInsets
binding.appbarLicenses.layoutParams = mlpAppBar
val mlpScrollAbout = binding.listLicenses.layoutParams as MarginLayoutParams
mlpScrollAbout.leftMargin = leftInsets
mlpScrollAbout.rightMargin = rightInsets
binding.listLicenses.layoutParams = mlpScrollAbout
binding.listLicenses.updatePadding(bottom = barInsets.bottom)
windowInsets
}
}

View File

@@ -0,0 +1,62 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu.fragments
import android.app.Dialog
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import androidx.fragment.app.DialogFragment
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import org.yuzu.yuzu_emu.R
class LongMessageDialogFragment : DialogFragment() {
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val titleId = requireArguments().getInt(TITLE)
val description = requireArguments().getString(DESCRIPTION)
val helpLinkId = requireArguments().getInt(HELP_LINK)
val dialog = MaterialAlertDialogBuilder(requireContext())
.setPositiveButton(R.string.close, null)
.setTitle(titleId)
.setMessage(description)
if (helpLinkId != 0) {
dialog.setNeutralButton(R.string.learn_more) { _, _ ->
openLink(getString(helpLinkId))
}
}
return dialog.show()
}
private fun openLink(link: String) {
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(link))
startActivity(intent)
}
companion object {
const val TAG = "LongMessageDialogFragment"
private const val TITLE = "Title"
private const val DESCRIPTION = "Description"
private const val HELP_LINK = "Link"
fun newInstance(
titleId: Int,
description: String,
helpLinkId: Int = 0
): LongMessageDialogFragment {
val dialog = LongMessageDialogFragment()
val bundle = Bundle()
bundle.apply {
putInt(TITLE, titleId)
putString(DESCRIPTION, description)
putInt(HELP_LINK, helpLinkId)
}
dialog.arguments = bundle
return dialog
}
}
}

View File

@@ -20,6 +20,7 @@ import androidx.fragment.app.activityViewModels
import androidx.preference.PreferenceManager
import info.debatty.java.stringsimilarity.Jaccard
import info.debatty.java.stringsimilarity.JaroWinkler
import java.util.Locale
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.YuzuApplication
import org.yuzu.yuzu_emu.adapters.GameAdapter
@@ -28,9 +29,6 @@ import org.yuzu.yuzu_emu.layout.AutofitGridLayoutManager
import org.yuzu.yuzu_emu.model.Game
import org.yuzu.yuzu_emu.model.GamesViewModel
import org.yuzu.yuzu_emu.model.HomeViewModel
import org.yuzu.yuzu_emu.utils.FileUtil
import org.yuzu.yuzu_emu.utils.Log
import java.util.Locale
class SearchFragment : Fragment() {
private var _binding: FragmentSearchBinding? = null
@@ -127,24 +125,15 @@ class SearchFragment : Fragment() {
}
}
R.id.chip_homebrew -> {
baseList.filter {
Log.error("Guh - ${it.path}")
FileUtil.hasExtension(it.path, "nro")
|| FileUtil.hasExtension(it.path, "nso")
}
}
R.id.chip_homebrew -> baseList.filter { it.isHomebrew }
R.id.chip_retail -> baseList.filter {
FileUtil.hasExtension(it.path, "xci")
|| FileUtil.hasExtension(it.path, "nsp")
}
R.id.chip_retail -> baseList.filter { !it.isHomebrew }
else -> baseList
}
if (binding.searchText.text.toString().isEmpty()
&& binding.chipGroup.checkedChipId != View.NO_ID
if (binding.searchText.text.toString().isEmpty() &&
binding.chipGroup.checkedChipId != View.NO_ID
) {
gamesViewModel.setSearchedGames(filteredList)
return
@@ -179,14 +168,16 @@ class SearchFragment : Fragment() {
private fun focusSearch() {
if (_binding != null) {
binding.searchText.requestFocus()
val imm =
requireActivity().getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager?
val imm = requireActivity()
.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager?
imm?.showSoftInput(binding.searchText, InputMethodManager.SHOW_IMPLICIT)
}
}
private fun setInsets() =
ViewCompat.setOnApplyWindowInsetsListener(binding.root) { view: View, windowInsets: WindowInsetsCompat ->
ViewCompat.setOnApplyWindowInsetsListener(
binding.root
) { view: View, windowInsets: WindowInsetsCompat ->
val barInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
val cutoutInsets = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout())
val extraListSpacing = resources.getDimensionPixelSize(R.dimen.spacing_med)

View File

@@ -25,6 +25,7 @@ import androidx.navigation.findNavController
import androidx.preference.PreferenceManager
import androidx.viewpager2.widget.ViewPager2.OnPageChangeCallback
import com.google.android.material.transition.MaterialFadeThrough
import java.io.File
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.YuzuApplication
import org.yuzu.yuzu_emu.adapters.SetupAdapter
@@ -35,7 +36,6 @@ import org.yuzu.yuzu_emu.model.SetupPage
import org.yuzu.yuzu_emu.ui.main.MainActivity
import org.yuzu.yuzu_emu.utils.DirectoryInitialization
import org.yuzu.yuzu_emu.utils.GameHelper
import java.io.File
class SetupFragment : Fragment() {
private var _binding: FragmentSetupBinding? = null
@@ -82,7 +82,8 @@ class SetupFragment : Fragment() {
requireActivity().finish()
}
}
})
}
)
requireActivity().window.navigationBarColor =
ContextCompat.getColor(requireContext(), android.R.color.transparent)
@@ -148,14 +149,20 @@ class SetupFragment : Fragment() {
R.drawable.ic_add,
true,
R.string.add_games,
{ mainActivity.getGamesDirectory.launch(Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).data) },
{
mainActivity.getGamesDirectory.launch(
Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).data
)
},
true,
R.string.add_games_warning,
R.string.add_games_warning_description,
R.string.add_games_warning_help,
{
val preferences =
PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
PreferenceManager.getDefaultSharedPreferences(
YuzuApplication.appContext
)
preferences.getString(GameHelper.KEY_GAME_PATH, "")!!.isNotEmpty()
}
)
@@ -260,7 +267,9 @@ class SetupFragment : Fragment() {
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
private val permissionLauncher =
registerForActivityResult(ActivityResultContracts.RequestPermission()) {
if (!it && !shouldShowRequestPermissionRationale(Manifest.permission.POST_NOTIFICATIONS)) {
if (!it &&
!shouldShowRequestPermissionRationale(Manifest.permission.POST_NOTIFICATIONS)
) {
PermissionDeniedDialogFragment().show(
childFragmentManager,
PermissionDeniedDialogFragment.TAG
@@ -315,7 +324,9 @@ class SetupFragment : Fragment() {
}
private fun setInsets() =
ViewCompat.setOnApplyWindowInsetsListener(binding.root) { view: View, windowInsets: WindowInsetsCompat ->
ViewCompat.setOnApplyWindowInsetsListener(
binding.root
) { view: View, windowInsets: WindowInsetsCompat ->
val barInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
val cutoutInsets = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout())
view.setPadding(

View File

@@ -44,7 +44,9 @@ class AutofitGridLayoutManager(
override fun onLayoutChildren(recycler: Recycler, state: RecyclerView.State) {
val width = width
val height = height
if (columnWidth > 0 && width > 0 && height > 0 && (isColumnWidthChanged || lastWidth != width || lastHeight != height)) {
if (columnWidth > 0 && width > 0 && height > 0 &&
(isColumnWidthChanged || lastWidth != width || lastHeight != height)
) {
val totalSpace: Int = if (orientation == VERTICAL) {
width - paddingRight - paddingLeft
} else {

View File

@@ -4,9 +4,9 @@
package org.yuzu.yuzu_emu.model
import android.os.Parcelable
import java.util.HashSet
import kotlinx.parcelize.Parcelize
import kotlinx.serialization.Serializable
import java.util.HashSet
@Parcelize
@Serializable
@@ -16,26 +16,34 @@ class Game(
val regions: String,
val path: String,
val gameId: String,
val company: String
val company: String,
val isHomebrew: Boolean
) : Parcelable {
val keyAddedToLibraryTime get() = "${gameId}_AddedToLibraryTime"
val keyLastPlayedTime get() = "${gameId}_LastPlayed"
override fun equals(other: Any?): Boolean {
if (other !is Game)
if (other !is Game) {
return false
}
return title == other.title
&& description == other.description
&& regions == other.regions
&& path == other.path
&& gameId == other.gameId
&& company == other.company
return hashCode() == other.hashCode()
}
override fun hashCode(): Int {
var result = title.hashCode()
result = 31 * result + description.hashCode()
result = 31 * result + regions.hashCode()
result = 31 * result + path.hashCode()
result = 31 * result + gameId.hashCode()
result = 31 * result + company.hashCode()
result = 31 * result + isHomebrew.hashCode()
return result
}
companion object {
val extensions: Set<String> = HashSet(
listOf(".xci", ".nsp", ".nca", ".nro")
listOf("xci", "nsp", "nca", "nro")
)
}
}

View File

@@ -10,16 +10,19 @@ import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import androidx.preference.PreferenceManager
import java.util.Locale
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.MissingFieldException
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json
import org.yuzu.yuzu_emu.NativeLibrary
import org.yuzu.yuzu_emu.YuzuApplication
import org.yuzu.yuzu_emu.utils.GameHelper
import java.util.Locale
@OptIn(ExperimentalSerializationApi::class)
class GamesViewModel : ViewModel() {
private val _games = MutableLiveData<List<Game>>(emptyList())
val games: LiveData<List<Game>> get() = _games
@@ -49,7 +52,13 @@ class GamesViewModel : ViewModel() {
if (storedGames!!.isNotEmpty()) {
val deserializedGames = mutableSetOf<Game>()
storedGames.forEach {
val game: Game = Json.decodeFromString(it)
val game: Game
try {
game = Json.decodeFromString(it)
} catch (e: MissingFieldException) {
return@forEach
}
val gameExists =
DocumentFile.fromSingleUri(YuzuApplication.appContext, Uri.parse(game.path))
?.exists()
@@ -90,8 +99,9 @@ class GamesViewModel : ViewModel() {
}
fun reloadGames(directoryChanged: Boolean) {
if (isReloading.value == true)
if (isReloading.value == true) {
return
}
_isReloading.postValue(true)
viewModelScope.launch {

View File

@@ -0,0 +1,16 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu.model
import android.os.Parcelable
import kotlinx.parcelize.Parcelize
@Parcelize
data class License(
val titleId: Int,
val descriptionId: Int,
val linkId: Int,
val copyrightId: Int,
val licenseId: Int
) : Parcelable

View File

@@ -0,0 +1,47 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu.model
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
class TaskViewModel : ViewModel() {
private val _result = MutableLiveData<Any>()
val result: LiveData<Any> = _result
private val _isComplete = MutableLiveData<Boolean>()
val isComplete: LiveData<Boolean> = _isComplete
private val _isRunning = MutableLiveData<Boolean>()
val isRunning: LiveData<Boolean> = _isRunning
lateinit var task: () -> Any
init {
clear()
}
fun clear() {
_result.value = Any()
_isComplete.value = false
_isRunning.value = false
}
fun runTask() {
if (_isRunning.value == true) {
return
}
_isRunning.value = true
viewModelScope.launch(Dispatchers.IO) {
val res = task()
_result.postValue(res)
_isComplete.postValue(true)
}
}
}

View File

@@ -6,7 +6,6 @@ package org.yuzu.yuzu_emu.overlay
import android.app.Activity
import android.content.Context
import android.content.SharedPreferences
import android.content.res.Configuration
import android.graphics.Bitmap
import android.graphics.Canvas
import android.graphics.Point
@@ -24,6 +23,8 @@ import android.view.WindowInsets
import androidx.core.content.ContextCompat
import androidx.preference.PreferenceManager
import androidx.window.layout.WindowMetricsCalculator
import kotlin.math.max
import kotlin.math.min
import org.yuzu.yuzu_emu.NativeLibrary
import org.yuzu.yuzu_emu.NativeLibrary.ButtonType
import org.yuzu.yuzu_emu.NativeLibrary.StickType
@@ -31,14 +32,13 @@ import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.YuzuApplication
import org.yuzu.yuzu_emu.features.settings.model.Settings
import org.yuzu.yuzu_emu.utils.EmulationMenuSettings
import kotlin.math.max
import kotlin.math.min
/**
* Draws the interactive input overlay on top of the
* [SurfaceView] that is rendering emulation.
*/
class InputOverlay(context: Context, attrs: AttributeSet?) : SurfaceView(context, attrs),
class InputOverlay(context: Context, attrs: AttributeSet?) :
SurfaceView(context, attrs),
OnTouchListener {
private val overlayButtons: MutableSet<InputOverlayDrawableButton> = HashSet()
private val overlayDpads: MutableSet<InputOverlayDrawableDpad> = HashSet()
@@ -51,12 +51,14 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : SurfaceView(context
private lateinit var windowInsets: WindowInsets
var orientation = LANDSCAPE
override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
super.onLayout(changed, left, top, right, bottom)
windowInsets = rootWindowInsets
if (!preferences.getBoolean(Settings.PREF_OVERLAY_INIT, false)) {
if (!preferences.getBoolean("${Settings.PREF_OVERLAY_INIT}$orientation", false)) {
defaultOverlay()
}
@@ -93,7 +95,11 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : SurfaceView(context
var shouldUpdateView = false
val playerIndex =
if (NativeLibrary.isHandheldOnly()) NativeLibrary.ConsoleDevice else NativeLibrary.Player1Device
if (NativeLibrary.isHandheldOnly()) {
NativeLibrary.ConsoleDevice
} else {
NativeLibrary.Player1Device
}
for (button in overlayButtons) {
if (!button.updateStatus(event)) {
@@ -156,8 +162,9 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : SurfaceView(context
shouldUpdateView = true
}
if (shouldUpdateView)
if (shouldUpdateView) {
invalidate()
}
if (!preferences.getBoolean(Settings.PREF_TOUCH_ENABLED, true)) {
return true
@@ -233,10 +240,6 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : SurfaceView(context
val fingerPositionX = event.getX(pointerIndex).toInt()
val fingerPositionY = event.getY(pointerIndex).toInt()
// TODO: Provide support for portrait layout
//val orientation =
// if (resources.configuration.orientation == Configuration.ORIENTATION_PORTRAIT) "-Portrait" else ""
for (button in overlayButtons) {
// Determine the button state to apply based on the MotionEvent action flag.
when (event.action and MotionEvent.ACTION_MASK) {
@@ -245,9 +248,9 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : SurfaceView(context
// If no button is being moved now, remember the currently touched button to move.
if (buttonBeingConfigured == null &&
button.bounds.contains(
fingerPositionX,
fingerPositionY
)
fingerPositionX,
fingerPositionY
)
) {
buttonBeingConfigured = button
buttonBeingConfigured!!.onConfigureTouch(event)
@@ -266,7 +269,7 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : SurfaceView(context
buttonBeingConfigured!!.buttonId,
buttonBeingConfigured!!.bounds.centerX(),
buttonBeingConfigured!!.bounds.centerY(),
""
orientation
)
buttonBeingConfigured = null
}
@@ -299,7 +302,7 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : SurfaceView(context
dpadBeingConfigured!!.upId,
dpadBeingConfigured!!.bounds.centerX(),
dpadBeingConfigured!!.bounds.centerY(),
""
orientation
)
dpadBeingConfigured = null
}
@@ -311,9 +314,9 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : SurfaceView(context
MotionEvent.ACTION_DOWN,
MotionEvent.ACTION_POINTER_DOWN -> if (joystickBeingConfigured == null &&
joystick.bounds.contains(
fingerPositionX,
fingerPositionY
)
fingerPositionX,
fingerPositionY
)
) {
joystickBeingConfigured = joystick
joystickBeingConfigured!!.onConfigureTouch(event)
@@ -330,7 +333,7 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : SurfaceView(context
joystickBeingConfigured!!.buttonId,
joystickBeingConfigured!!.bounds.centerX(),
joystickBeingConfigured!!.bounds.centerY(),
""
orientation
)
joystickBeingConfigured = null
}
@@ -533,8 +536,6 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : SurfaceView(context
overlayButtons.clear()
overlayDpads.clear()
overlayJoysticks.clear()
val orientation =
if (resources.configuration.orientation == Configuration.ORIENTATION_PORTRAIT) "-Portrait" else ""
// Add all the enabled overlay items back to the HashSet.
if (EmulationMenuSettings.showOverlay) {
@@ -548,8 +549,8 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : SurfaceView(context
val min = windowSize.first
val max = windowSize.second
PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext).edit()
.putFloat("$sharedPrefsId$orientation-X", (x - min.x).toFloat() / max.x)
.putFloat("$sharedPrefsId$orientation-Y", (y - min.y).toFloat() / max.y)
.putFloat("$sharedPrefsId-X$orientation", (x - min.x).toFloat() / max.x)
.putFloat("$sharedPrefsId-Y$orientation", (y - min.y).toFloat() / max.y)
.apply()
}
@@ -558,145 +559,250 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : SurfaceView(context
}
private fun defaultOverlay() {
if (!preferences.getBoolean(Settings.PREF_OVERLAY_INIT, false)) {
defaultOverlayLandscape()
if (!preferences.getBoolean("${Settings.PREF_OVERLAY_INIT}$orientation", false)) {
defaultOverlayByLayout(orientation)
}
resetButtonPlacement()
preferences.edit()
.putBoolean(Settings.PREF_OVERLAY_INIT, true)
.putBoolean("${Settings.PREF_OVERLAY_INIT}$orientation", true)
.apply()
}
fun resetButtonPlacement() {
defaultOverlayLandscape()
defaultOverlayByLayout(orientation)
refreshControls()
}
private fun defaultOverlayLandscape() {
private val landscapeResources = arrayOf(
R.integer.SWITCH_BUTTON_A_X,
R.integer.SWITCH_BUTTON_A_Y,
R.integer.SWITCH_BUTTON_B_X,
R.integer.SWITCH_BUTTON_B_Y,
R.integer.SWITCH_BUTTON_X_X,
R.integer.SWITCH_BUTTON_X_Y,
R.integer.SWITCH_BUTTON_Y_X,
R.integer.SWITCH_BUTTON_Y_Y,
R.integer.SWITCH_TRIGGER_ZL_X,
R.integer.SWITCH_TRIGGER_ZL_Y,
R.integer.SWITCH_TRIGGER_ZR_X,
R.integer.SWITCH_TRIGGER_ZR_Y,
R.integer.SWITCH_BUTTON_DPAD_X,
R.integer.SWITCH_BUTTON_DPAD_Y,
R.integer.SWITCH_TRIGGER_L_X,
R.integer.SWITCH_TRIGGER_L_Y,
R.integer.SWITCH_TRIGGER_R_X,
R.integer.SWITCH_TRIGGER_R_Y,
R.integer.SWITCH_BUTTON_PLUS_X,
R.integer.SWITCH_BUTTON_PLUS_Y,
R.integer.SWITCH_BUTTON_MINUS_X,
R.integer.SWITCH_BUTTON_MINUS_Y,
R.integer.SWITCH_BUTTON_HOME_X,
R.integer.SWITCH_BUTTON_HOME_Y,
R.integer.SWITCH_BUTTON_CAPTURE_X,
R.integer.SWITCH_BUTTON_CAPTURE_Y,
R.integer.SWITCH_STICK_R_X,
R.integer.SWITCH_STICK_R_Y,
R.integer.SWITCH_STICK_L_X,
R.integer.SWITCH_STICK_L_Y
)
private val portraitResources = arrayOf(
R.integer.SWITCH_BUTTON_A_X_PORTRAIT,
R.integer.SWITCH_BUTTON_A_Y_PORTRAIT,
R.integer.SWITCH_BUTTON_B_X_PORTRAIT,
R.integer.SWITCH_BUTTON_B_Y_PORTRAIT,
R.integer.SWITCH_BUTTON_X_X_PORTRAIT,
R.integer.SWITCH_BUTTON_X_Y_PORTRAIT,
R.integer.SWITCH_BUTTON_Y_X_PORTRAIT,
R.integer.SWITCH_BUTTON_Y_Y_PORTRAIT,
R.integer.SWITCH_TRIGGER_ZL_X_PORTRAIT,
R.integer.SWITCH_TRIGGER_ZL_Y_PORTRAIT,
R.integer.SWITCH_TRIGGER_ZR_X_PORTRAIT,
R.integer.SWITCH_TRIGGER_ZR_Y_PORTRAIT,
R.integer.SWITCH_BUTTON_DPAD_X_PORTRAIT,
R.integer.SWITCH_BUTTON_DPAD_Y_PORTRAIT,
R.integer.SWITCH_TRIGGER_L_X_PORTRAIT,
R.integer.SWITCH_TRIGGER_L_Y_PORTRAIT,
R.integer.SWITCH_TRIGGER_R_X_PORTRAIT,
R.integer.SWITCH_TRIGGER_R_Y_PORTRAIT,
R.integer.SWITCH_BUTTON_PLUS_X_PORTRAIT,
R.integer.SWITCH_BUTTON_PLUS_Y_PORTRAIT,
R.integer.SWITCH_BUTTON_MINUS_X_PORTRAIT,
R.integer.SWITCH_BUTTON_MINUS_Y_PORTRAIT,
R.integer.SWITCH_BUTTON_HOME_X_PORTRAIT,
R.integer.SWITCH_BUTTON_HOME_Y_PORTRAIT,
R.integer.SWITCH_BUTTON_CAPTURE_X_PORTRAIT,
R.integer.SWITCH_BUTTON_CAPTURE_Y_PORTRAIT,
R.integer.SWITCH_STICK_R_X_PORTRAIT,
R.integer.SWITCH_STICK_R_Y_PORTRAIT,
R.integer.SWITCH_STICK_L_X_PORTRAIT,
R.integer.SWITCH_STICK_L_Y_PORTRAIT
)
private val foldableResources = arrayOf(
R.integer.SWITCH_BUTTON_A_X_FOLDABLE,
R.integer.SWITCH_BUTTON_A_Y_FOLDABLE,
R.integer.SWITCH_BUTTON_B_X_FOLDABLE,
R.integer.SWITCH_BUTTON_B_Y_FOLDABLE,
R.integer.SWITCH_BUTTON_X_X_FOLDABLE,
R.integer.SWITCH_BUTTON_X_Y_FOLDABLE,
R.integer.SWITCH_BUTTON_Y_X_FOLDABLE,
R.integer.SWITCH_BUTTON_Y_Y_FOLDABLE,
R.integer.SWITCH_TRIGGER_ZL_X_FOLDABLE,
R.integer.SWITCH_TRIGGER_ZL_Y_FOLDABLE,
R.integer.SWITCH_TRIGGER_ZR_X_FOLDABLE,
R.integer.SWITCH_TRIGGER_ZR_Y_FOLDABLE,
R.integer.SWITCH_BUTTON_DPAD_X_FOLDABLE,
R.integer.SWITCH_BUTTON_DPAD_Y_FOLDABLE,
R.integer.SWITCH_TRIGGER_L_X_FOLDABLE,
R.integer.SWITCH_TRIGGER_L_Y_FOLDABLE,
R.integer.SWITCH_TRIGGER_R_X_FOLDABLE,
R.integer.SWITCH_TRIGGER_R_Y_FOLDABLE,
R.integer.SWITCH_BUTTON_PLUS_X_FOLDABLE,
R.integer.SWITCH_BUTTON_PLUS_Y_FOLDABLE,
R.integer.SWITCH_BUTTON_MINUS_X_FOLDABLE,
R.integer.SWITCH_BUTTON_MINUS_Y_FOLDABLE,
R.integer.SWITCH_BUTTON_HOME_X_FOLDABLE,
R.integer.SWITCH_BUTTON_HOME_Y_FOLDABLE,
R.integer.SWITCH_BUTTON_CAPTURE_X_FOLDABLE,
R.integer.SWITCH_BUTTON_CAPTURE_Y_FOLDABLE,
R.integer.SWITCH_STICK_R_X_FOLDABLE,
R.integer.SWITCH_STICK_R_Y_FOLDABLE,
R.integer.SWITCH_STICK_L_X_FOLDABLE,
R.integer.SWITCH_STICK_L_Y_FOLDABLE
)
private fun getResourceValue(orientation: String, position: Int): Float {
return when (orientation) {
PORTRAIT -> resources.getInteger(portraitResources[position]).toFloat() / 1000
FOLDABLE -> resources.getInteger(foldableResources[position]).toFloat() / 1000
else -> resources.getInteger(landscapeResources[position]).toFloat() / 1000
}
}
private fun defaultOverlayByLayout(orientation: String) {
// Each value represents the position of the button in relation to the screen size without insets.
preferences.edit()
.putFloat(
ButtonType.BUTTON_A.toString() + "-X",
resources.getInteger(R.integer.SWITCH_BUTTON_A_X).toFloat() / 1000
ButtonType.BUTTON_A.toString() + "-X$orientation",
getResourceValue(orientation, 0)
)
.putFloat(
ButtonType.BUTTON_A.toString() + "-Y",
resources.getInteger(R.integer.SWITCH_BUTTON_A_Y).toFloat() / 1000
ButtonType.BUTTON_A.toString() + "-Y$orientation",
getResourceValue(orientation, 1)
)
.putFloat(
ButtonType.BUTTON_B.toString() + "-X",
resources.getInteger(R.integer.SWITCH_BUTTON_B_X).toFloat() / 1000
ButtonType.BUTTON_B.toString() + "-X$orientation",
getResourceValue(orientation, 2)
)
.putFloat(
ButtonType.BUTTON_B.toString() + "-Y",
resources.getInteger(R.integer.SWITCH_BUTTON_B_Y).toFloat() / 1000
ButtonType.BUTTON_B.toString() + "-Y$orientation",
getResourceValue(orientation, 3)
)
.putFloat(
ButtonType.BUTTON_X.toString() + "-X",
resources.getInteger(R.integer.SWITCH_BUTTON_X_X).toFloat() / 1000
ButtonType.BUTTON_X.toString() + "-X$orientation",
getResourceValue(orientation, 4)
)
.putFloat(
ButtonType.BUTTON_X.toString() + "-Y",
resources.getInteger(R.integer.SWITCH_BUTTON_X_Y).toFloat() / 1000
ButtonType.BUTTON_X.toString() + "-Y$orientation",
getResourceValue(orientation, 5)
)
.putFloat(
ButtonType.BUTTON_Y.toString() + "-X",
resources.getInteger(R.integer.SWITCH_BUTTON_Y_X).toFloat() / 1000
ButtonType.BUTTON_Y.toString() + "-X$orientation",
getResourceValue(orientation, 6)
)
.putFloat(
ButtonType.BUTTON_Y.toString() + "-Y",
resources.getInteger(R.integer.SWITCH_BUTTON_Y_Y).toFloat() / 1000
ButtonType.BUTTON_Y.toString() + "-Y$orientation",
getResourceValue(orientation, 7)
)
.putFloat(
ButtonType.TRIGGER_ZL.toString() + "-X",
resources.getInteger(R.integer.SWITCH_TRIGGER_ZL_X).toFloat() / 1000
ButtonType.TRIGGER_ZL.toString() + "-X$orientation",
getResourceValue(orientation, 8)
)
.putFloat(
ButtonType.TRIGGER_ZL.toString() + "-Y",
resources.getInteger(R.integer.SWITCH_TRIGGER_ZL_Y).toFloat() / 1000
ButtonType.TRIGGER_ZL.toString() + "-Y$orientation",
getResourceValue(orientation, 9)
)
.putFloat(
ButtonType.TRIGGER_ZR.toString() + "-X",
resources.getInteger(R.integer.SWITCH_TRIGGER_ZR_X).toFloat() / 1000
ButtonType.TRIGGER_ZR.toString() + "-X$orientation",
getResourceValue(orientation, 10)
)
.putFloat(
ButtonType.TRIGGER_ZR.toString() + "-Y",
resources.getInteger(R.integer.SWITCH_TRIGGER_ZR_Y).toFloat() / 1000
ButtonType.TRIGGER_ZR.toString() + "-Y$orientation",
getResourceValue(orientation, 11)
)
.putFloat(
ButtonType.DPAD_UP.toString() + "-X",
resources.getInteger(R.integer.SWITCH_BUTTON_DPAD_X).toFloat() / 1000
ButtonType.DPAD_UP.toString() + "-X$orientation",
getResourceValue(orientation, 12)
)
.putFloat(
ButtonType.DPAD_UP.toString() + "-Y",
resources.getInteger(R.integer.SWITCH_BUTTON_DPAD_Y).toFloat() / 1000
ButtonType.DPAD_UP.toString() + "-Y$orientation",
getResourceValue(orientation, 13)
)
.putFloat(
ButtonType.TRIGGER_L.toString() + "-X",
resources.getInteger(R.integer.SWITCH_TRIGGER_L_X).toFloat() / 1000
ButtonType.TRIGGER_L.toString() + "-X$orientation",
getResourceValue(orientation, 14)
)
.putFloat(
ButtonType.TRIGGER_L.toString() + "-Y",
resources.getInteger(R.integer.SWITCH_TRIGGER_L_Y).toFloat() / 1000
ButtonType.TRIGGER_L.toString() + "-Y$orientation",
getResourceValue(orientation, 15)
)
.putFloat(
ButtonType.TRIGGER_R.toString() + "-X",
resources.getInteger(R.integer.SWITCH_TRIGGER_R_X).toFloat() / 1000
ButtonType.TRIGGER_R.toString() + "-X$orientation",
getResourceValue(orientation, 16)
)
.putFloat(
ButtonType.TRIGGER_R.toString() + "-Y",
resources.getInteger(R.integer.SWITCH_TRIGGER_R_Y).toFloat() / 1000
ButtonType.TRIGGER_R.toString() + "-Y$orientation",
getResourceValue(orientation, 17)
)
.putFloat(
ButtonType.BUTTON_PLUS.toString() + "-X",
resources.getInteger(R.integer.SWITCH_BUTTON_PLUS_X).toFloat() / 1000
ButtonType.BUTTON_PLUS.toString() + "-X$orientation",
getResourceValue(orientation, 18)
)
.putFloat(
ButtonType.BUTTON_PLUS.toString() + "-Y",
resources.getInteger(R.integer.SWITCH_BUTTON_PLUS_Y).toFloat() / 1000
ButtonType.BUTTON_PLUS.toString() + "-Y$orientation",
getResourceValue(orientation, 19)
)
.putFloat(
ButtonType.BUTTON_MINUS.toString() + "-X",
resources.getInteger(R.integer.SWITCH_BUTTON_MINUS_X).toFloat() / 1000
ButtonType.BUTTON_MINUS.toString() + "-X$orientation",
getResourceValue(orientation, 20)
)
.putFloat(
ButtonType.BUTTON_MINUS.toString() + "-Y",
resources.getInteger(R.integer.SWITCH_BUTTON_MINUS_Y).toFloat() / 1000
ButtonType.BUTTON_MINUS.toString() + "-Y$orientation",
getResourceValue(orientation, 21)
)
.putFloat(
ButtonType.BUTTON_HOME.toString() + "-X",
resources.getInteger(R.integer.SWITCH_BUTTON_HOME_X).toFloat() / 1000
ButtonType.BUTTON_HOME.toString() + "-X$orientation",
getResourceValue(orientation, 22)
)
.putFloat(
ButtonType.BUTTON_HOME.toString() + "-Y",
resources.getInteger(R.integer.SWITCH_BUTTON_HOME_Y).toFloat() / 1000
ButtonType.BUTTON_HOME.toString() + "-Y$orientation",
getResourceValue(orientation, 23)
)
.putFloat(
ButtonType.BUTTON_CAPTURE.toString() + "-X",
resources.getInteger(R.integer.SWITCH_BUTTON_CAPTURE_X)
.toFloat() / 1000
ButtonType.BUTTON_CAPTURE.toString() + "-X$orientation",
getResourceValue(orientation, 24)
)
.putFloat(
ButtonType.BUTTON_CAPTURE.toString() + "-Y",
resources.getInteger(R.integer.SWITCH_BUTTON_CAPTURE_Y)
.toFloat() / 1000
ButtonType.BUTTON_CAPTURE.toString() + "-Y$orientation",
getResourceValue(orientation, 25)
)
.putFloat(
ButtonType.STICK_R.toString() + "-X",
resources.getInteger(R.integer.SWITCH_STICK_R_X).toFloat() / 1000
ButtonType.STICK_R.toString() + "-X$orientation",
getResourceValue(orientation, 26)
)
.putFloat(
ButtonType.STICK_R.toString() + "-Y",
resources.getInteger(R.integer.SWITCH_STICK_R_Y).toFloat() / 1000
ButtonType.STICK_R.toString() + "-Y$orientation",
getResourceValue(orientation, 27)
)
.putFloat(
ButtonType.STICK_L.toString() + "-X",
resources.getInteger(R.integer.SWITCH_STICK_L_X).toFloat() / 1000
ButtonType.STICK_L.toString() + "-X$orientation",
getResourceValue(orientation, 28)
)
.putFloat(
ButtonType.STICK_L.toString() + "-Y",
resources.getInteger(R.integer.SWITCH_STICK_L_Y).toFloat() / 1000
ButtonType.STICK_L.toString() + "-Y$orientation",
getResourceValue(orientation, 29)
)
.apply()
}
@@ -709,13 +815,17 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : SurfaceView(context
private val preferences: SharedPreferences =
PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
const val LANDSCAPE = ""
const val PORTRAIT = "_Portrait"
const val FOLDABLE = "_Foldable"
/**
* Resizes a [Bitmap] by a given scale factor
*
* @param context Context for getting the vector drawable
* @param drawableId The ID of the drawable to scale.
* @param scale The scale factor for the bitmap.
* @return The scaled [Bitmap]
* @return The scaled [Bitmap]
*/
private fun getBitmap(context: Context, drawableId: Int, scale: Float): Bitmap {
val vectorDrawable = ContextCompat.getDrawable(context, drawableId) as VectorDrawable
@@ -749,14 +859,13 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : SurfaceView(context
* Gets the safe screen size for drawing the overlay
*
* @param context Context for getting the window metrics
* @return A pair of points, the first being the top left corner of the safe area,
* @return A pair of points, the first being the top left corner of the safe area,
* the second being the bottom right corner of the safe area
*/
private fun getSafeScreenSize(context: Context): Pair<Point, Point> {
// Get screen size
val windowMetrics =
WindowMetricsCalculator.getOrCreate()
.computeCurrentWindowMetrics(context as Activity)
val windowMetrics = WindowMetricsCalculator.getOrCreate()
.computeCurrentWindowMetrics(context as Activity)
var maxY = windowMetrics.bounds.height().toFloat()
var maxX = windowMetrics.bounds.width().toFloat()
var minY = 0
@@ -765,18 +874,26 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : SurfaceView(context
// If we have API access, calculate the safe area to draw the overlay
var cutoutLeft = 0
var cutoutBottom = 0
val insets = context.windowManager.currentWindowMetrics.windowInsets.displayCutout
if (insets != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
if (insets.boundingRectTop.bottom != 0 && insets.boundingRectTop.bottom > maxY / 2)
insets.boundingRectTop.bottom.toFloat() else maxY
if (insets.boundingRectRight.left != 0 && insets.boundingRectRight.left > maxX / 2)
insets.boundingRectRight.left.toFloat() else maxX
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
val insets = context.windowManager.currentWindowMetrics.windowInsets.displayCutout
if (insets != null) {
if (insets.boundingRectTop.bottom != 0 &&
insets.boundingRectTop.bottom > maxY / 2
) {
maxY = insets.boundingRectTop.bottom.toFloat()
}
if (insets.boundingRectRight.left != 0 &&
insets.boundingRectRight.left > maxX / 2
) {
maxX = insets.boundingRectRight.left.toFloat()
}
minX = insets.boundingRectLeft.right - insets.boundingRectLeft.left
minY = insets.boundingRectBottom.top - insets.boundingRectBottom.bottom
minX = insets.boundingRectLeft.right - insets.boundingRectLeft.left
minY = insets.boundingRectBottom.top - insets.boundingRectBottom.bottom
cutoutLeft = insets.boundingRectRight.right - insets.boundingRectRight.left
cutoutBottom = insets.boundingRectTop.top - insets.boundingRectTop.bottom
cutoutLeft = insets.boundingRectRight.right - insets.boundingRectRight.left
cutoutBottom = insets.boundingRectTop.top - insets.boundingRectTop.bottom
}
}
// This makes sure that if we have an inset on one side of the screen, we mirror it on
@@ -876,8 +993,8 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : SurfaceView(context
// The X and Y coordinates of the InputOverlayDrawableButton on the InputOverlay.
// These were set in the input overlay configuration menu.
val xKey = "$buttonId$orientation-X"
val yKey = "$buttonId$orientation-Y"
val xKey = "$buttonId-X$orientation"
val yKey = "$buttonId-Y$orientation"
val drawableXPercent = sPrefs.getFloat(xKey, 0f)
val drawableYPercent = sPrefs.getFloat(yKey, 0f)
val drawableX = (drawableXPercent * max.x + min.x).toInt()
@@ -957,8 +1074,8 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : SurfaceView(context
// The X and Y coordinates of the InputOverlayDrawableDpad on the InputOverlay.
// These were set in the input overlay configuration menu.
val drawableXPercent = sPrefs.getFloat("${ButtonType.DPAD_UP}$orientation-X", 0f)
val drawableYPercent = sPrefs.getFloat("${ButtonType.DPAD_UP}$orientation-Y", 0f)
val drawableXPercent = sPrefs.getFloat("${ButtonType.DPAD_UP}-X$orientation", 0f)
val drawableYPercent = sPrefs.getFloat("${ButtonType.DPAD_UP}-Y$orientation", 0f)
val drawableX = (drawableXPercent * max.x + min.x).toInt()
val drawableY = (drawableYPercent * max.y + min.y).toInt()
val width = overlayDrawable.width
@@ -1024,8 +1141,8 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : SurfaceView(context
// The X and Y coordinates of the InputOverlayDrawableButton on the InputOverlay.
// These were set in the input overlay configuration menu.
val drawableXPercent = sPrefs.getFloat("$button$orientation-X", 0f)
val drawableYPercent = sPrefs.getFloat("$button$orientation-Y", 0f)
val drawableXPercent = sPrefs.getFloat("$button-X$orientation", 0f)
val drawableYPercent = sPrefs.getFloat("$button-Y$orientation", 0f)
val drawableX = (drawableXPercent * max.x + min.x).toInt()
val drawableY = (drawableYPercent * max.y + min.y).toInt()
val outerScale = 1.66f

View File

@@ -133,7 +133,10 @@ class InputOverlayDrawableDpad(
downButtonState = axisY > VIRT_AXIS_DEADZONE
leftButtonState = axisX < -VIRT_AXIS_DEADZONE
rightButtonState = axisX > VIRT_AXIS_DEADZONE
return oldUpState != upButtonState || oldDownState != downButtonState || oldLeftState != leftButtonState || oldRightState != rightButtonState
return oldUpState != upButtonState ||
oldDownState != downButtonState ||
oldLeftState != leftButtonState ||
oldRightState != rightButtonState
}
return false
}

View File

@@ -9,12 +9,12 @@ import android.graphics.Canvas
import android.graphics.Rect
import android.graphics.drawable.BitmapDrawable
import android.view.MotionEvent
import org.yuzu.yuzu_emu.NativeLibrary
import org.yuzu.yuzu_emu.utils.EmulationMenuSettings
import kotlin.math.atan2
import kotlin.math.cos
import kotlin.math.sin
import kotlin.math.sqrt
import org.yuzu.yuzu_emu.NativeLibrary
import org.yuzu.yuzu_emu.utils.EmulationMenuSettings
/**
* Custom [BitmapDrawable] that is capable
@@ -241,14 +241,22 @@ class InputOverlayDrawableJoystick(
private fun setInnerBounds() {
var x = virtBounds.centerX() + (xAxis * (virtBounds.width() / 2)).toInt()
var y = virtBounds.centerY() + (yAxis * (virtBounds.height() / 2)).toInt()
if (x > virtBounds.centerX() + virtBounds.width() / 2) x =
virtBounds.centerX() + virtBounds.width() / 2
if (x < virtBounds.centerX() - virtBounds.width() / 2) x =
virtBounds.centerX() - virtBounds.width() / 2
if (y > virtBounds.centerY() + virtBounds.height() / 2) y =
virtBounds.centerY() + virtBounds.height() / 2
if (y < virtBounds.centerY() - virtBounds.height() / 2) y =
virtBounds.centerY() - virtBounds.height() / 2
if (x > virtBounds.centerX() + virtBounds.width() / 2) {
x =
virtBounds.centerX() + virtBounds.width() / 2
}
if (x < virtBounds.centerX() - virtBounds.width() / 2) {
x =
virtBounds.centerX() - virtBounds.width() / 2
}
if (y > virtBounds.centerY() + virtBounds.height() / 2) {
y =
virtBounds.centerY() + virtBounds.height() / 2
}
if (y < virtBounds.centerY() - virtBounds.height() / 2) {
y =
virtBounds.centerY() - virtBounds.height() / 2
}
val width = pressedStateInnerBitmap.bounds.width() / 2
val height = pressedStateInnerBitmap.bounds.height() / 2
defaultStateInnerBitmap.setBounds(

View File

@@ -99,7 +99,9 @@ class GamesFragment : Fragment() {
}
shouldSwapData.observe(viewLifecycleOwner) { shouldSwapData ->
if (shouldSwapData) {
(binding.gridGames.adapter as GameAdapter).submitList(gamesViewModel.games.value!!)
(binding.gridGames.adapter as GameAdapter).submitList(
gamesViewModel.games.value!!
)
gamesViewModel.setShouldSwapData(false)
}
}
@@ -128,7 +130,9 @@ class GamesFragment : Fragment() {
}
private fun setInsets() =
ViewCompat.setOnApplyWindowInsetsListener(binding.root) { view: View, windowInsets: WindowInsetsCompat ->
ViewCompat.setOnApplyWindowInsetsListener(
binding.root
) { view: View, windowInsets: WindowInsetsCompat ->
val barInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
val cutoutInsets = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout())
val extraListSpacing = resources.getDimensionPixelSize(R.dimen.spacing_large)

View File

@@ -4,6 +4,7 @@
package org.yuzu.yuzu_emu.ui.main
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import android.view.View
import android.view.ViewGroup.MarginLayoutParams
@@ -26,6 +27,9 @@ import androidx.preference.PreferenceManager
import com.google.android.material.color.MaterialColors
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.navigation.NavigationBarView
import java.io.File
import java.io.FilenameFilter
import java.io.IOException
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
@@ -35,19 +39,22 @@ import org.yuzu.yuzu_emu.activities.EmulationActivity
import org.yuzu.yuzu_emu.databinding.ActivityMainBinding
import org.yuzu.yuzu_emu.databinding.DialogProgressBarBinding
import org.yuzu.yuzu_emu.features.settings.model.Settings
import org.yuzu.yuzu_emu.features.settings.model.SettingsViewModel
import org.yuzu.yuzu_emu.features.settings.ui.SettingsActivity
import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile
import org.yuzu.yuzu_emu.fragments.IndeterminateProgressDialogFragment
import org.yuzu.yuzu_emu.fragments.LongMessageDialogFragment
import org.yuzu.yuzu_emu.fragments.MessageDialogFragment
import org.yuzu.yuzu_emu.model.GamesViewModel
import org.yuzu.yuzu_emu.model.HomeViewModel
import org.yuzu.yuzu_emu.utils.*
import java.io.IOException
class MainActivity : AppCompatActivity(), ThemeProvider {
private lateinit var binding: ActivityMainBinding
private val homeViewModel: HomeViewModel by viewModels()
private val gamesViewModel: GamesViewModel by viewModels()
private val settingsViewModel: SettingsViewModel by viewModels()
override var themeId: Int = 0
@@ -55,6 +62,8 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
val splashScreen = installSplashScreen()
splashScreen.setKeepOnScreenCondition { !DirectoryInitialization.areDirectoriesReady }
settingsViewModel.settings.loadSettings()
ThemeHelper.setTheme(this)
super.onCreate(savedInstanceState)
@@ -79,7 +88,9 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
ThemeHelper.SYSTEM_BAR_ALPHA
)
)
if (InsetsHelper.getSystemGestureType(applicationContext) != InsetsHelper.GESTURE_NAVIGATION) {
if (InsetsHelper.getSystemGestureType(applicationContext) !=
InsetsHelper.GESTURE_NAVIGATION
) {
binding.navigationBarShade.setBackgroundColor(
ThemeHelper.getColorWithOpacity(
MaterialColors.getColor(
@@ -165,7 +176,9 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
binding.navigationView.height.toFloat() * 2
translationY(0f)
} else {
if (ViewCompat.getLayoutDirection(binding.navigationView) == ViewCompat.LAYOUT_DIRECTION_LTR) {
if (ViewCompat.getLayoutDirection(binding.navigationView) ==
ViewCompat.LAYOUT_DIRECTION_LTR
) {
binding.navigationView.translationX =
binding.navigationView.width.toFloat() * -2
translationX(0f)
@@ -182,7 +195,9 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
if (smallLayout) {
translationY(binding.navigationView.height.toFloat() * 2)
} else {
if (ViewCompat.getLayoutDirection(binding.navigationView) == ViewCompat.LAYOUT_DIRECTION_LTR) {
if (ViewCompat.getLayoutDirection(binding.navigationView) ==
ViewCompat.LAYOUT_DIRECTION_LTR
) {
translationX(binding.navigationView.width.toFloat() * -2)
} else {
translationX(binding.navigationView.width.toFloat() * 2)
@@ -227,7 +242,9 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
}
private fun setInsets() =
ViewCompat.setOnApplyWindowInsetsListener(binding.root) { _: View, windowInsets: WindowInsetsCompat ->
ViewCompat.setOnApplyWindowInsetsListener(
binding.root
) { _: View, windowInsets: WindowInsetsCompat ->
val insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
val mlpStatusShade = binding.statusBarShade.layoutParams as MarginLayoutParams
mlpStatusShade.height = insets.top
@@ -249,8 +266,9 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
val getGamesDirectory =
registerForActivityResult(ActivityResultContracts.OpenDocumentTree()) { result ->
if (result == null)
if (result == null) {
return@registerForActivityResult
}
contentResolver.takePersistableUriPermission(
result,
@@ -274,13 +292,14 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
val getProdKey =
registerForActivityResult(ActivityResultContracts.OpenDocument()) { result ->
if (result == null)
if (result == null) {
return@registerForActivityResult
}
if (!FileUtil.hasExtension(result.toString(), "keys")) {
if (FileUtil.getExtension(result) != "keys") {
MessageDialogFragment.newInstance(
R.string.reading_keys_failure,
R.string.install_keys_failure_extension_description
R.string.install_prod_keys_failure_extension_description
).show(supportFragmentManager, MessageDialogFragment.TAG)
return@registerForActivityResult
}
@@ -315,15 +334,69 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
}
}
val getFirmware =
registerForActivityResult(ActivityResultContracts.OpenDocument()) { result ->
if (result == null) {
return@registerForActivityResult
}
val inputZip = contentResolver.openInputStream(result)
if (inputZip == null) {
Toast.makeText(
applicationContext,
getString(R.string.fatal_error),
Toast.LENGTH_LONG
).show()
return@registerForActivityResult
}
val filterNCA = FilenameFilter { _, dirName -> dirName.endsWith(".nca") }
val firmwarePath =
File(DirectoryInitialization.userDirectory + "/nand/system/Contents/registered/")
val cacheFirmwareDir = File("${cacheDir.path}/registered/")
val task: () -> Any = {
var messageToShow: Any
try {
FileUtil.unzip(inputZip, cacheFirmwareDir)
val unfilteredNumOfFiles = cacheFirmwareDir.list()?.size ?: -1
val filteredNumOfFiles = cacheFirmwareDir.list(filterNCA)?.size ?: -2
messageToShow = if (unfilteredNumOfFiles != filteredNumOfFiles) {
MessageDialogFragment.newInstance(
R.string.firmware_installed_failure,
R.string.firmware_installed_failure_description
)
} else {
firmwarePath.deleteRecursively()
cacheFirmwareDir.copyRecursively(firmwarePath, true)
getString(R.string.save_file_imported_success)
}
} catch (e: Exception) {
messageToShow = getString(R.string.fatal_error)
} finally {
cacheFirmwareDir.deleteRecursively()
}
messageToShow
}
IndeterminateProgressDialogFragment.newInstance(
this,
R.string.firmware_installing,
task
).show(supportFragmentManager, IndeterminateProgressDialogFragment.TAG)
}
val getAmiiboKey =
registerForActivityResult(ActivityResultContracts.OpenDocument()) { result ->
if (result == null)
if (result == null) {
return@registerForActivityResult
}
if (!FileUtil.hasExtension(result.toString(), "bin")) {
if (FileUtil.getExtension(result) != "bin") {
MessageDialogFragment.newInstance(
R.string.reading_keys_failure,
R.string.install_keys_failure_extension_description
R.string.install_amiibo_keys_failure_extension_description
).show(supportFragmentManager, MessageDialogFragment.TAG)
return@registerForActivityResult
}
@@ -359,8 +432,9 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
val getDriver =
registerForActivityResult(ActivityResultContracts.OpenDocument()) { result ->
if (result == null)
if (result == null) {
return@registerForActivityResult
}
val takeFlags =
Intent.FLAG_GRANT_WRITE_URI_PERMISSION or Intent.FLAG_GRANT_READ_URI_PERMISSION
@@ -408,4 +482,111 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
}
}
}
val installGameUpdate = registerForActivityResult(
ActivityResultContracts.OpenMultipleDocuments()
) { documents: List<Uri> ->
if (documents.isNotEmpty()) {
IndeterminateProgressDialogFragment.newInstance(
this@MainActivity,
R.string.install_game_content
) {
var installSuccess = 0
var installOverwrite = 0
var errorBaseGame = 0
var errorExtension = 0
var errorOther = 0
var errorTotal = 0
lifecycleScope.launch {
documents.forEach {
when (NativeLibrary.installFileToNand(it.toString())) {
NativeLibrary.InstallFileToNandResult.Success -> {
installSuccess += 1
}
NativeLibrary.InstallFileToNandResult.SuccessFileOverwritten -> {
installOverwrite += 1
}
NativeLibrary.InstallFileToNandResult.ErrorBaseGame -> {
errorBaseGame += 1
}
NativeLibrary.InstallFileToNandResult.ErrorFilenameExtension -> {
errorExtension += 1
}
else -> {
errorOther += 1
}
}
}
withContext(Dispatchers.Main) {
val separator = System.getProperty("line.separator") ?: "\n"
val installResult = StringBuilder()
if (installSuccess > 0) {
installResult.append(
getString(
R.string.install_game_content_success_install,
installSuccess
)
)
installResult.append(separator)
}
if (installOverwrite > 0) {
installResult.append(
getString(
R.string.install_game_content_success_overwrite,
installOverwrite
)
)
installResult.append(separator)
}
errorTotal = errorBaseGame + errorExtension + errorOther
if (errorTotal > 0) {
installResult.append(separator)
installResult.append(
getString(
R.string.install_game_content_failed_count,
errorTotal
)
)
installResult.append(separator)
if (errorBaseGame > 0) {
installResult.append(separator)
installResult.append(
getString(R.string.install_game_content_failure_base)
)
installResult.append(separator)
}
if (errorExtension > 0) {
installResult.append(separator)
installResult.append(
getString(R.string.install_game_content_failure_file_extension)
)
installResult.append(separator)
}
if (errorOther > 0) {
installResult.append(
getString(R.string.install_game_content_failure_description)
)
installResult.append(separator)
}
LongMessageDialogFragment.newInstance(
R.string.install_game_content_failure,
installResult.toString().trim(),
R.string.install_game_content_help_link
).show(supportFragmentManager, LongMessageDialogFragment.TAG)
} else {
LongMessageDialogFragment.newInstance(
R.string.install_game_content_success,
installResult.toString().trim()
).show(supportFragmentManager, LongMessageDialogFragment.TAG)
}
}
}
return@newInstance installSuccess + installOverwrite + errorTotal
}.show(supportFragmentManager, IndeterminateProgressDialogFragment.TAG)
}
}
}

View File

@@ -19,7 +19,9 @@ class ControllerMappingHelper {
// 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
keyCode == KeyEvent.KEYCODE_BUTTON_L2 || keyCode == KeyEvent.KEYCODE_BUTTON_R2
} else false
} else {
false
}
}
/**

View File

@@ -4,8 +4,8 @@
package org.yuzu.yuzu_emu.utils
import android.content.Context
import org.yuzu.yuzu_emu.NativeLibrary
import java.io.IOException
import org.yuzu.yuzu_emu.NativeLibrary
object DirectoryInitialization {
private var userPath: String? = null

View File

@@ -5,10 +5,10 @@ package org.yuzu.yuzu_emu.utils
import android.net.Uri
import androidx.documentfile.provider.DocumentFile
import org.yuzu.yuzu_emu.YuzuApplication
import org.yuzu.yuzu_emu.model.MinimalDocumentFile
import java.io.File
import java.util.*
import org.yuzu.yuzu_emu.YuzuApplication
import org.yuzu.yuzu_emu.model.MinimalDocumentFile
class DocumentsTree {
private var root: DocumentsNode? = null
@@ -29,13 +29,20 @@ class DocumentsTree {
val node = resolvePath(filepath)
return if (node == null || node.isDirectory) {
0
} else FileUtil.getFileSize(YuzuApplication.appContext, node.uri.toString())
} else {
FileUtil.getFileSize(YuzuApplication.appContext, node.uri.toString())
}
}
fun exists(filepath: String): Boolean {
return resolvePath(filepath) != null
}
fun isDirectory(filepath: String): Boolean {
val node = resolvePath(filepath)
return node != null && node.isDirectory
}
private fun resolvePath(filepath: String): DocumentsNode? {
val tokens = StringTokenizer(filepath, File.separator, false)
var iterator = root
@@ -106,7 +113,9 @@ class DocumentsTree {
fun isNativePath(path: String): Boolean {
return if (path.isNotEmpty()) {
path[0] == '/'
} else false
} else {
false
}
}
}
}

View File

@@ -11,14 +11,6 @@ object EmulationMenuSettings {
private val preferences =
PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
// These must match what is defined in src/core/settings.h
const val LayoutOption_Default = 0
const val LayoutOption_SingleScreen = 1
const val LayoutOption_LargeScreen = 2
const val LayoutOption_SideScreen = 3
const val LayoutOption_MobilePortrait = 4
const val LayoutOption_MobileLandscape = 5
var joystickRelCenter: Boolean
get() = preferences.getBoolean(Settings.PREF_MENU_SETTINGS_JOYSTICK_REL_CENTER, true)
set(value) {
@@ -41,16 +33,6 @@ object EmulationMenuSettings {
.apply()
}
var landscapeScreenLayout: Int
get() = preferences.getInt(
Settings.PREF_MENU_SETTINGS_LANDSCAPE,
LayoutOption_MobileLandscape
)
set(value) {
preferences.edit()
.putInt(Settings.PREF_MENU_SETTINGS_LANDSCAPE, value)
.apply()
}
var showFps: Boolean
get() = preferences.getBoolean(Settings.PREF_MENU_SETTINGS_SHOW_FPS, false)
set(value) {

View File

@@ -8,11 +8,16 @@ import android.database.Cursor
import android.net.Uri
import android.provider.DocumentsContract
import androidx.documentfile.provider.DocumentFile
import org.yuzu.yuzu_emu.model.MinimalDocumentFile
import java.io.BufferedInputStream
import java.io.File
import java.io.FileOutputStream
import java.io.IOException
import java.io.InputStream
import java.net.URLDecoder
import java.util.zip.ZipEntry
import java.util.zip.ZipInputStream
import org.yuzu.yuzu_emu.YuzuApplication
import org.yuzu.yuzu_emu.model.MinimalDocumentFile
object FileUtil {
const val PATH_TREE = "tree"
@@ -179,19 +184,18 @@ object FileUtil {
/**
* Get file display name from given path
* @param path content uri path
* @param uri content uri
* @return String display name
*/
fun getFilename(context: Context, path: String): String {
val resolver = context.contentResolver
fun getFilename(uri: Uri): String {
val resolver = YuzuApplication.appContext.contentResolver
val columns = arrayOf(
DocumentsContract.Document.COLUMN_DISPLAY_NAME
)
var filename = ""
var c: Cursor? = null
try {
val mUri = Uri.parse(path)
c = resolver.query(mUri, columns, null, null, null)
c = resolver.query(uri, columns, null, null, null)
c!!.moveToNext()
filename = c.getString(0)
} catch (e: Exception) {
@@ -276,6 +280,34 @@ object FileUtil {
return false
}
/**
* Extracts the given zip file into the given directory.
* @exception IOException if the file was being created outside of the target directory
*/
@Throws(SecurityException::class)
fun unzip(zipStream: InputStream, destDir: File): Boolean {
ZipInputStream(BufferedInputStream(zipStream)).use { zis ->
var entry: ZipEntry? = zis.nextEntry
while (entry != null) {
val entryName = entry.name
val entryFile = File(destDir, entryName)
if (!entryFile.canonicalPath.startsWith(destDir.canonicalPath + File.separator)) {
throw SecurityException("Entry is outside of the target dir: " + entryFile.name)
}
if (entry.isDirectory) {
entryFile.mkdirs()
} else {
entryFile.parentFile?.mkdirs()
entryFile.createNewFile()
entryFile.outputStream().use { fos -> zis.copyTo(fos) }
}
entry = zis.nextEntry
}
}
return true
}
fun isRootTreeUri(uri: Uri): Boolean {
val paths = uri.pathSegments
return paths.size == 2 && PATH_TREE == paths[0]
@@ -292,7 +324,9 @@ object FileUtil {
}
}
fun hasExtension(path: String, extension: String): Boolean {
return path.substring(path.lastIndexOf(".") + 1).contains(extension)
fun getExtension(uri: Uri): String {
val fileName = getFilename(uri)
return fileName.substring(fileName.lastIndexOf(".") + 1)
.lowercase()
}
}

View File

@@ -54,7 +54,7 @@ class ForegroundService : Service() {
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
if (intent == null) {
return START_NOT_STICKY;
return START_NOT_STICKY
}
if (intent.action == ACTION_STOP) {
NotificationManagerCompat.from(this).cancel(EMULATION_RUNNING_NOTIFICATION)

View File

@@ -6,13 +6,11 @@ package org.yuzu.yuzu_emu.utils
import android.content.SharedPreferences
import android.net.Uri
import androidx.preference.PreferenceManager
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import org.yuzu.yuzu_emu.NativeLibrary
import org.yuzu.yuzu_emu.YuzuApplication
import org.yuzu.yuzu_emu.model.Game
import java.util.*
object GameHelper {
const val KEY_GAME_PATH = "game_path"
@@ -34,15 +32,9 @@ object GameHelper {
val children = FileUtil.listFiles(context, gamesUri)
for (file in children) {
if (!file.isDirectory) {
val filename = file.uri.toString()
val extensionStart = filename.lastIndexOf('.')
if (extensionStart > 0) {
val fileExtension = filename.substring(extensionStart)
// Check that the file has an extension we care about before trying to read out of it.
if (Game.extensions.contains(fileExtension.lowercase(Locale.getDefault()))) {
games.add(getGame(filename))
}
// Check that the file has an extension we care about before trying to read out of it.
if (Game.extensions.contains(FileUtil.getExtension(file.uri))) {
games.add(getGame(file.uri))
}
}
}
@@ -60,21 +52,19 @@ object GameHelper {
return games.toList()
}
private fun getGame(filePath: String): Game {
private fun getGame(uri: Uri): Game {
val filePath = uri.toString()
var name = NativeLibrary.getTitle(filePath)
// If the game's title field is empty, use the filename.
if (name.isEmpty()) {
name = filePath.substring(filePath.lastIndexOf("/") + 1)
name = FileUtil.getFilename(uri)
}
var gameId = NativeLibrary.getGameId(filePath)
// If the game's ID field is empty, use the filename without extension.
if (gameId.isEmpty()) {
gameId = filePath.substring(
filePath.lastIndexOf("/") + 1,
filePath.lastIndexOf(".")
)
gameId = name.substring(0, name.lastIndexOf("."))
}
val newGame = Game(
@@ -83,7 +73,8 @@ object GameHelper {
NativeLibrary.getRegions(filePath),
filePath,
gameId,
NativeLibrary.getCompany(filePath)
NativeLibrary.getCompany(filePath),
NativeLibrary.isHomebrew(filePath)
)
val addedTime = preferences.getLong(newGame.keyAddedToLibraryTime, 0L)

View File

@@ -5,14 +5,14 @@ package org.yuzu.yuzu_emu.utils
import android.content.Context
import android.net.Uri
import org.yuzu.yuzu_emu.NativeLibrary
import org.yuzu.yuzu_emu.utils.FileUtil.copyUriToInternalStorage
import java.io.BufferedInputStream
import java.io.File
import java.io.FileInputStream
import java.io.FileOutputStream
import java.io.IOException
import java.util.zip.ZipInputStream
import org.yuzu.yuzu_emu.NativeLibrary
import org.yuzu.yuzu_emu.utils.FileUtil.copyUriToInternalStorage
object GpuDriverHelper {
private const val META_JSON_FILENAME = "meta.json"
@@ -113,6 +113,8 @@ object GpuDriverHelper {
initializeDriverParameters(context)
}
external fun supportsCustomDriverLoading(): Boolean
// Parse the custom driver metadata to retrieve the name.
val customDriverName: String?
get() {

View File

@@ -3,12 +3,12 @@
package org.yuzu.yuzu_emu.utils
import org.json.JSONException
import org.json.JSONObject
import java.io.IOException
import java.nio.charset.StandardCharsets
import java.nio.file.Files
import java.nio.file.Paths
import org.json.JSONException
import org.json.JSONObject
class GpuDriverMetadata(metadataFilePath: String) {
var name: String? = null

View File

@@ -5,8 +5,8 @@ package org.yuzu.yuzu_emu.utils
import android.view.KeyEvent
import android.view.MotionEvent
import org.yuzu.yuzu_emu.NativeLibrary
import kotlin.math.sqrt
import org.yuzu.yuzu_emu.NativeLibrary
class InputHandler {
fun initialize() {
@@ -68,7 +68,11 @@ class InputHandler {
6 -> NativeLibrary.Player6Device
7 -> NativeLibrary.Player7Device
8 -> NativeLibrary.Player8Device
else -> if (NativeLibrary.isHandheldOnly()) NativeLibrary.ConsoleDevice else NativeLibrary.Player1Device
else -> if (NativeLibrary.isHandheldOnly()) {
NativeLibrary.ConsoleDevice
} else {
NativeLibrary.Player1Device
}
}
}
@@ -107,7 +111,11 @@ class InputHandler {
}
private fun getAxisToButton(axis: Float): Int {
return if (axis > 0.5f) NativeLibrary.ButtonState.PRESSED else NativeLibrary.ButtonState.RELEASED
return if (axis > 0.5f) {
NativeLibrary.ButtonState.PRESSED
} else {
NativeLibrary.ButtonState.RELEASED
}
}
private fun setAxisDpadState(playerNumber: Int, xAxis: Float, yAxis: Float) {
@@ -287,7 +295,6 @@ class InputHandler {
}
}
private fun setJoyconAxisInput(event: MotionEvent, axis: Int) {
// Joycon support is half dead. Right joystick doesn't work
val playerNumber = getPlayerNumber(event.device.controllerNumber)
@@ -355,6 +362,4 @@ class InputHandler {
)
}
}
}
}

View File

@@ -4,9 +4,7 @@
package org.yuzu.yuzu_emu.utils
import android.annotation.SuppressLint
import android.app.Activity
import android.content.Context
import android.graphics.Rect
object InsetsHelper {
const val THREE_BUTTON_NAVIGATION = 0
@@ -20,12 +18,8 @@ object InsetsHelper {
resources.getIdentifier("config_navBarInteractionMode", "integer", "android")
return if (resourceId != 0) {
resources.getInteger(resourceId)
} else 0
}
fun getBottomPaddingRequired(activity: Activity): Int {
val visibleFrame = Rect()
activity.window.decorView.getWindowVisibleDisplayFrame(visibleFrame)
return visibleFrame.bottom - visibleFrame.top - activity.resources.displayMetrics.heightPixels
} else {
0
}
}
}

View File

@@ -0,0 +1,59 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu.utils
import android.app.ActivityManager
import android.content.Context
import org.yuzu.yuzu_emu.R
import java.util.Locale
class MemoryUtil(val context: Context) {
private val Long.floatForm: String
get() = String.format(Locale.ROOT, "%.2f", this.toDouble())
private fun bytesToSizeUnit(size: Long): String {
return when {
size < Kb -> "${size.floatForm} ${context.getString(R.string.memory_byte)}"
size < Mb -> "${(size / Kb).floatForm} ${context.getString(R.string.memory_kilobyte)}"
size < Gb -> "${(size / Mb).floatForm} ${context.getString(R.string.memory_megabyte)}"
size < Tb -> "${(size / Gb).floatForm} ${context.getString(R.string.memory_gigabyte)}"
size < Pb -> "${(size / Tb).floatForm} ${context.getString(R.string.memory_terabyte)}"
size < Eb -> "${(size / Pb).floatForm} ${context.getString(R.string.memory_petabyte)}"
else -> "${(size / Eb).floatForm} ${context.getString(R.string.memory_exabyte)}"
}
}
private val totalMemory =
with(context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager) {
val memInfo = ActivityManager.MemoryInfo()
getMemoryInfo(memInfo)
memInfo.totalMem
}
fun isLessThan(minimum: Int, size: Long): Boolean {
return when (size) {
Kb -> totalMemory < Mb && totalMemory < minimum
Mb -> totalMemory < Gb && (totalMemory / Mb) < minimum
Gb -> totalMemory < Tb && (totalMemory / Gb) < minimum
Tb -> totalMemory < Pb && (totalMemory / Tb) < minimum
Pb -> totalMemory < Eb && (totalMemory / Pb) < minimum
Eb -> totalMemory / Eb < minimum
else -> totalMemory < Kb && totalMemory < minimum
}
}
fun getDeviceRAM(): String {
return bytesToSizeUnit(totalMemory)
}
companion object {
const val Kb: Long = 1024
const val Mb = Kb * 1024
const val Gb = Mb * 1024
const val Tb = Gb * 1024
const val Pb = Tb * 1024
const val Eb = Pb * 1024
}
}

View File

@@ -13,8 +13,8 @@ import android.nfc.tech.NfcA
import android.os.Build
import android.os.Handler
import android.os.Looper
import org.yuzu.yuzu_emu.NativeLibrary
import java.io.IOException
import org.yuzu.yuzu_emu.NativeLibrary
class NfcReader(private val activity: Activity) {
private var nfcAdapter: NfcAdapter? = null
@@ -25,10 +25,13 @@ class NfcReader(private val activity: Activity) {
pendingIntent = PendingIntent.getActivity(
activity,
0, Intent(activity, activity.javaClass),
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S)
0,
Intent(activity, activity.javaClass),
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE
else PendingIntent.FLAG_UPDATE_CURRENT
} else {
PendingIntent.FLAG_UPDATE_CURRENT
}
)
val tagDetected = IntentFilter(NfcAdapter.ACTION_TAG_DISCOVERED)
@@ -45,9 +48,9 @@ class NfcReader(private val activity: Activity) {
fun onNewIntent(intent: Intent) {
val action = intent.action
if (NfcAdapter.ACTION_TAG_DISCOVERED != action
&& NfcAdapter.ACTION_TECH_DISCOVERED != action
&& NfcAdapter.ACTION_NDEF_DISCOVERED != action
if (NfcAdapter.ACTION_TAG_DISCOVERED != action &&
NfcAdapter.ACTION_TECH_DISCOVERED != action &&
NfcAdapter.ACTION_NDEF_DISCOVERED != action
) {
return
}
@@ -84,7 +87,7 @@ class NfcReader(private val activity: Activity) {
}
private fun ntag215ReadAll(amiibo: NfcA): ByteArray? {
val bufferSize = amiibo.maxTransceiveLength;
val bufferSize = amiibo.maxTransceiveLength
val tagSize = 0x21C
val pageSize = 4
val lastPage = tagSize / pageSize - 1
@@ -103,7 +106,7 @@ class NfcReader(private val activity: Activity) {
val data = ntag215FastRead(amiibo, dataStart, dataEnd - 1)
System.arraycopy(data, 0, tagData, i, (dataEnd - dataStart) * pageSize)
} catch (e: IOException) {
return null;
return null
}
}
return tagData

View File

@@ -11,30 +11,34 @@ import java.io.Serializable
object SerializableHelper {
inline fun <reified T : Serializable> Bundle.serializable(key: String): T? {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU)
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
getSerializable(key, T::class.java)
else
} else {
getSerializable(key) as? T
}
}
inline fun <reified T : Serializable> Intent.serializable(key: String): T? {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU)
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
getSerializableExtra(key, T::class.java)
else
} else {
getSerializableExtra(key) as? T
}
}
inline fun <reified T : Parcelable> Bundle.parcelable(key: String): T? {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU)
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
getParcelable(key, T::class.java)
else
} else {
getParcelable(key) as? T
}
}
inline fun <reified T : Parcelable> Intent.parcelable(key: String): T? {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU)
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
getParcelableExtra(key, T::class.java)
else
} else {
getParcelableExtra(key) as? T
}
}
}

View File

@@ -3,21 +3,19 @@
package org.yuzu.yuzu_emu.utils
import android.app.Activity
import android.content.res.Configuration
import android.graphics.Color
import androidx.annotation.ColorInt
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.app.AppCompatDelegate
import androidx.core.content.ContextCompat
import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsControllerCompat
import androidx.preference.PreferenceManager
import kotlin.math.roundToInt
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.YuzuApplication
import org.yuzu.yuzu_emu.features.settings.model.Settings
import org.yuzu.yuzu_emu.ui.main.ThemeProvider
import kotlin.math.roundToInt
object ThemeHelper {
const val SYSTEM_BAR_ALPHA = 0.9f
@@ -36,8 +34,8 @@ object ThemeHelper {
// Using a specific night mode check because this could apply incorrectly when using the
// light app mode, dark system mode, and black backgrounds. Launching the settings activity
// will then show light mode colors/navigation bars but with black backgrounds.
if (preferences.getBoolean(Settings.PREF_BLACK_BACKGROUNDS, false)
&& isNightMode(activity)
if (preferences.getBoolean(Settings.PREF_BLACK_BACKGROUNDS, false) &&
isNightMode(activity)
) {
activity.setTheme(R.style.ThemeOverlay_Yuzu_Dark)
}
@@ -46,8 +44,10 @@ object ThemeHelper {
@ColorInt
fun getColorWithOpacity(@ColorInt color: Int, alphaFactor: Float): Int {
return Color.argb(
(alphaFactor * Color.alpha(color)).roundToInt(), Color.red(color),
Color.green(color), Color.blue(color)
(alphaFactor * Color.alpha(color)).roundToInt(),
Color.red(color),
Color.green(color),
Color.blue(color)
)
}

View File

@@ -0,0 +1,48 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu.views
import android.content.Context
import android.util.AttributeSet
import android.util.Rational
import android.view.SurfaceView
import kotlin.math.roundToInt
class FixedRatioSurfaceView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : SurfaceView(context, attrs, defStyleAttr) {
private var aspectRatio: Float = 0f // (width / height), 0f is a special value for stretch
/**
* Sets the desired aspect ratio for this view
* @param ratio the ratio to force the view to, or null to stretch to fit
*/
fun setAspectRatio(ratio: Rational?) {
aspectRatio = ratio?.toFloat() ?: 0f
}
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
val width = MeasureSpec.getSize(widthMeasureSpec)
val height = MeasureSpec.getSize(heightMeasureSpec)
if (aspectRatio != 0f) {
val newWidth: Int
val newHeight: Int
if (height * aspectRatio < width) {
newWidth = (height * aspectRatio).roundToInt()
newHeight = height
} else {
newWidth = width
newHeight = (width / aspectRatio).roundToInt()
}
val left = (width - newWidth) / 2
val top = (height - newHeight) / 2
setLeftTopRightBottom(left, top, left + newWidth, top + newHeight)
} else {
setLeftTopRightBottom(0, 0, width, height)
}
}
}

View File

@@ -14,7 +14,6 @@ add_library(yuzu-android SHARED
id_cache.cpp
id_cache.h
native.cpp
native.h
)
set_property(TARGET yuzu-android PROPERTY IMPORTED_LOCATION ${FFmpeg_LIBRARY_DIR})

View File

@@ -235,9 +235,13 @@ void Config::ReadValues() {
Settings::values.async_presentation =
config->GetBoolean("Renderer", "async_presentation", true);
// Enable force_max_clock by default on Android
// Disable force_max_clock by default on Android
Settings::values.renderer_force_max_clock =
config->GetBoolean("Renderer", "force_max_clock", true);
config->GetBoolean("Renderer", "force_max_clock", false);
// Disable use_reactive_flushing by default on Android
Settings::values.use_reactive_flushing =
config->GetBoolean("Renderer", "use_reactive_flushing", false);
// Audio
ReadSetting("Audio", Settings::values.sink_id);

View File

@@ -251,7 +251,7 @@ backend =
# 0: Off, 1 (default): On
async_presentation =
# Enable graphics API debugging mode.
# Forces the GPU to run at the maximum possible clocks (thermal constraints will still be applied).
# 0 (default): Disabled, 1: Enabled
force_max_clock =
@@ -328,6 +328,10 @@ shader_backend =
# 0 (default): Off, 1: On
use_asynchronous_shaders =
# Uses reactive flushing instead of predictive flushing. Allowing a more accurate syncing of memory.
# 0 (default): Off, 1: On
use_reactive_flushing =
# NVDEC emulation.
# 0: Disabled, 1: CPU Decoding, 2 (default): GPU Decoding
nvdec_emulation =

View File

@@ -13,6 +13,8 @@
#include <android/api-level.h>
#include <android/native_window_jni.h>
#include <core/loader/nro.h>
#include <jni.h>
#include "common/detached_tasks.h"
#include "common/dynamic_library.h"
@@ -27,7 +29,10 @@
#include "core/core.h"
#include "core/cpu_manager.h"
#include "core/crypto/key_manager.h"
#include "core/file_sys/card_image.h"
#include "core/file_sys/registered_cache.h"
#include "core/file_sys/submission_package.h"
#include "core/file_sys/vfs.h"
#include "core/file_sys/vfs_real.h"
#include "core/frontend/applets/cabinet.h"
#include "core/frontend/applets/controller.h"
@@ -55,6 +60,9 @@
#include "video_core/rasterizer_interface.h"
#include "video_core/renderer_base.h"
#define jconst [[maybe_unused]] const auto
#define jauto [[maybe_unused]] auto
namespace {
class EmulationSession final {
@@ -93,12 +101,72 @@ public:
m_native_window = native_window;
}
u32 ScreenRotation() const {
return m_screen_rotation;
}
int InstallFileToNand(std::string filename) {
jconst copy_func = [](const FileSys::VirtualFile& src, const FileSys::VirtualFile& dest,
std::size_t block_size) {
if (src == nullptr || dest == nullptr) {
return false;
}
if (!dest->Resize(src->GetSize())) {
return false;
}
void SetScreenRotation(u32 screen_rotation) {
m_screen_rotation = screen_rotation;
using namespace Common::Literals;
[[maybe_unused]] std::vector<u8> buffer(1_MiB);
for (std::size_t i = 0; i < src->GetSize(); i += buffer.size()) {
jconst read = src->Read(buffer.data(), buffer.size(), i);
dest->Write(buffer.data(), read, i);
}
return true;
};
enum InstallResult {
Success = 0,
SuccessFileOverwritten = 1,
InstallError = 2,
ErrorBaseGame = 3,
ErrorFilenameExtension = 4,
};
m_system.SetContentProvider(std::make_unique<FileSys::ContentProviderUnion>());
m_system.GetFileSystemController().CreateFactories(*m_vfs);
[[maybe_unused]] std::shared_ptr<FileSys::NSP> nsp;
if (filename.ends_with("nsp")) {
nsp = std::make_shared<FileSys::NSP>(m_vfs->OpenFile(filename, FileSys::Mode::Read));
if (nsp->IsExtractedType()) {
return InstallError;
}
} else if (filename.ends_with("xci")) {
jconst xci =
std::make_shared<FileSys::XCI>(m_vfs->OpenFile(filename, FileSys::Mode::Read));
nsp = xci->GetSecurePartitionNSP();
} else {
return ErrorFilenameExtension;
}
if (!nsp) {
return InstallError;
}
if (nsp->GetStatus() != Loader::ResultStatus::Success) {
return InstallError;
}
jconst res = m_system.GetFileSystemController().GetUserNANDContents()->InstallEntry(
*nsp, true, copy_func);
switch (res) {
case FileSys::InstallResult::Success:
return Success;
case FileSys::InstallResult::OverwriteExisting:
return SuccessFileOverwritten;
case FileSys::InstallResult::ErrorBaseInstall:
return ErrorBaseGame;
default:
return InstallError;
}
}
void InitializeGpuDriver(const std::string& hook_lib_dir, const std::string& custom_driver_dir,
@@ -138,6 +206,11 @@ public:
return m_is_running;
}
bool IsPaused() const {
std::scoped_lock lock(m_mutex);
return m_is_running && m_is_paused;
}
const Core::PerfStatsResults& PerfStats() const {
std::scoped_lock m_perf_stats_lock(m_perf_stats_mutex);
return m_perf_stats;
@@ -161,14 +234,15 @@ public:
m_window = std::make_unique<EmuWindow_Android>(&m_input_subsystem, m_native_window,
m_vulkan_library);
m_system.SetFilesystem(m_vfs);
// Initialize system.
auto android_keyboard = std::make_unique<SoftwareKeyboard::AndroidKeyboard>();
jauto android_keyboard = std::make_unique<SoftwareKeyboard::AndroidKeyboard>();
m_software_keyboard = android_keyboard.get();
m_system.SetShuttingDown(false);
m_system.ApplySettings();
Settings::LogSettings();
m_system.HIDCore().ReloadInputDevices();
m_system.SetContentProvider(std::make_unique<FileSys::ContentProviderUnion>());
m_system.SetFilesystem(std::make_shared<FileSys::RealVfsFilesystem>());
m_system.SetAppletFrontendSet({
nullptr, // Amiibo Settings
nullptr, // Controller Selector
@@ -180,7 +254,8 @@ public:
std::move(android_keyboard), // Software Keyboard
nullptr, // Web Browser
});
m_system.GetFileSystemController().CreateFactories(*m_system.GetFilesystem());
m_system.SetContentProvider(std::make_unique<FileSys::ContentProviderUnion>());
m_system.GetFileSystemController().CreateFactories(*m_vfs);
// Initialize account manager
m_profile_manager = std::make_unique<Service::Account::ProfileManager>();
@@ -222,11 +297,13 @@ public:
void PauseEmulation() {
std::scoped_lock lock(m_mutex);
m_system.Pause();
m_is_paused = true;
}
void UnPauseEmulation() {
std::scoped_lock lock(m_mutex);
m_system.Run();
m_is_paused = false;
}
void HaltEmulation() {
@@ -258,7 +335,7 @@ public:
while (true) {
{
std::unique_lock lock(m_mutex);
[[maybe_unused]] std::unique_lock lock(m_mutex);
if (m_cv.wait_for(lock, std::chrono::milliseconds(800),
[&]() { return !m_is_running; })) {
// Emulation halted.
@@ -281,12 +358,16 @@ public:
return GetRomMetadata(path).icon;
}
bool GetIsHomebrew(const std::string& path) {
return GetRomMetadata(path).isHomebrew;
}
void ResetRomMetadata() {
m_rom_metadata_cache.clear();
}
bool IsHandheldOnly() {
const auto npad_style_set = m_system.HIDCore().GetSupportedStyleTag();
jconst npad_style_set = m_system.HIDCore().GetSupportedStyleTag();
if (npad_style_set.fullkey == 1) {
return false;
@@ -299,17 +380,17 @@ public:
return !Settings::values.use_docked_mode.GetValue();
}
void SetDeviceType(int index, int type) {
auto controller = m_system.HIDCore().GetEmulatedControllerByIndex(index);
void SetDeviceType([[maybe_unused]] int index, int type) {
jauto controller = m_system.HIDCore().GetEmulatedControllerByIndex(index);
controller->SetNpadStyleIndex(static_cast<Core::HID::NpadStyleIndex>(type));
}
void OnGamepadConnectEvent(int index) {
auto controller = m_system.HIDCore().GetEmulatedControllerByIndex(index);
void OnGamepadConnectEvent([[maybe_unused]] int index) {
jauto controller = m_system.HIDCore().GetEmulatedControllerByIndex(index);
// Ensure that player1 is configured correctly and handheld disconnected
if (controller->GetNpadIdType() == Core::HID::NpadIdType::Player1) {
auto handheld =
jauto handheld =
m_system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Handheld);
if (controller->GetNpadStyleIndex() == Core::HID::NpadStyleIndex::Handheld) {
@@ -321,7 +402,8 @@ public:
// Ensure that handheld is configured correctly and player 1 disconnected
if (controller->GetNpadIdType() == Core::HID::NpadIdType::Handheld) {
auto player1 = m_system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1);
jauto player1 =
m_system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1);
if (controller->GetNpadStyleIndex() != Core::HID::NpadStyleIndex::Handheld) {
player1->SetNpadStyleIndex(Core::HID::NpadStyleIndex::Handheld);
@@ -335,8 +417,8 @@ public:
}
}
void OnGamepadDisconnectEvent(int index) {
auto controller = m_system.HIDCore().GetEmulatedControllerByIndex(index);
void OnGamepadDisconnectEvent([[maybe_unused]] int index) {
jauto controller = m_system.HIDCore().GetEmulatedControllerByIndex(index);
controller->Disconnect();
}
@@ -348,10 +430,11 @@ private:
struct RomMetadata {
std::string title;
std::vector<u8> icon;
bool isHomebrew;
};
RomMetadata GetRomMetadata(const std::string& path) {
if (auto search = m_rom_metadata_cache.find(path); search != m_rom_metadata_cache.end()) {
if (jauto search = m_rom_metadata_cache.find(path); search != m_rom_metadata_cache.end()) {
return search->second;
}
@@ -359,12 +442,18 @@ private:
}
RomMetadata CacheRomMetadata(const std::string& path) {
const auto file = Core::GetGameFileFromPath(m_vfs, path);
const auto loader = Loader::GetLoader(EmulationSession::GetInstance().System(), file, 0, 0);
jconst file = Core::GetGameFileFromPath(m_vfs, path);
jauto loader = Loader::GetLoader(EmulationSession::GetInstance().System(), file, 0, 0);
RomMetadata entry;
loader->ReadTitle(entry.title);
loader->ReadIcon(entry.icon);
if (loader->GetFileType() == Loader::FileType::NRO) {
jauto loader_nro = dynamic_cast<Loader::AppLoader_NRO*>(loader.get());
entry.isHomebrew = loader_nro->IsHomebrew();
} else {
entry.isHomebrew = false;
}
m_rom_metadata_cache[path] = entry;
@@ -388,16 +477,16 @@ private:
// Window management
std::unique_ptr<EmuWindow_Android> m_window;
ANativeWindow* m_native_window{};
u32 m_screen_rotation{};
// Core emulation
Core::System m_system;
InputCommon::InputSubsystem m_input_subsystem;
Common::DetachedTasks m_detached_tasks;
Core::PerfStatsResults m_perf_stats{};
std::shared_ptr<FileSys::RealVfsFilesystem> m_vfs;
std::shared_ptr<FileSys::VfsFilesystem> m_vfs;
Core::SystemResultStatus m_load_result{Core::SystemResultStatus::ErrorNotInitialized};
bool m_is_running{};
bool m_is_paused{};
SoftwareKeyboard::AndroidKeyboard* m_software_keyboard{};
std::unique_ptr<Service::Account::ProfileManager> m_profile_manager;
@@ -414,10 +503,6 @@ private:
} // Anonymous namespace
u32 GetAndroidScreenRotation() {
return EmulationSession::GetInstance().ScreenRotation();
}
static Core::SystemResultStatus RunEmulation(const std::string& filepath) {
Common::Log::Initialize();
Common::Log::SetColorConsoleBackendEnabled(true);
@@ -435,7 +520,7 @@ static Core::SystemResultStatus RunEmulation(const std::string& filepath) {
SCOPE_EXIT({ EmulationSession::GetInstance().ShutdownEmulation(); });
const auto result = EmulationSession::GetInstance().InitializeEmulation(filepath);
jconst result = EmulationSession::GetInstance().InitializeEmulation(filepath);
if (result != Core::SystemResultStatus::Success) {
return result;
}
@@ -447,79 +532,104 @@ static Core::SystemResultStatus RunEmulation(const std::string& filepath) {
extern "C" {
void Java_org_yuzu_yuzu_1emu_NativeLibrary_surfaceChanged(JNIEnv* env,
[[maybe_unused]] jclass clazz,
jobject surf) {
void Java_org_yuzu_yuzu_1emu_NativeLibrary_surfaceChanged(JNIEnv* env, jobject instance,
[[maybe_unused]] jobject surf) {
EmulationSession::GetInstance().SetNativeWindow(ANativeWindow_fromSurface(env, surf));
EmulationSession::GetInstance().SurfaceChanged();
}
void Java_org_yuzu_yuzu_1emu_NativeLibrary_surfaceDestroyed(JNIEnv* env,
[[maybe_unused]] jclass clazz) {
void Java_org_yuzu_yuzu_1emu_NativeLibrary_surfaceDestroyed(JNIEnv* env, jobject instance) {
ANativeWindow_release(EmulationSession::GetInstance().NativeWindow());
EmulationSession::GetInstance().SetNativeWindow(nullptr);
EmulationSession::GetInstance().SurfaceChanged();
}
void Java_org_yuzu_yuzu_1emu_NativeLibrary_notifyOrientationChange(JNIEnv* env,
[[maybe_unused]] jclass clazz,
jint layout_option,
jint rotation) {
return EmulationSession::GetInstance().SetScreenRotation(static_cast<u32>(rotation));
}
void Java_org_yuzu_yuzu_1emu_NativeLibrary_setAppDirectory(JNIEnv* env,
[[maybe_unused]] jclass clazz,
jstring j_directory) {
void Java_org_yuzu_yuzu_1emu_NativeLibrary_setAppDirectory(JNIEnv* env, jobject instance,
[[maybe_unused]] jstring j_directory) {
Common::FS::SetAppDirectory(GetJString(env, j_directory));
}
void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_initializeGpuDriver(
JNIEnv* env, [[maybe_unused]] jclass clazz, jstring hook_lib_dir, jstring custom_driver_dir,
jstring custom_driver_name, jstring file_redirect_dir) {
int Java_org_yuzu_yuzu_1emu_NativeLibrary_installFileToNand(JNIEnv* env, jobject instance,
[[maybe_unused]] jstring j_file) {
return EmulationSession::GetInstance().InstallFileToNand(GetJString(env, j_file));
}
void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_initializeGpuDriver(JNIEnv* env, jclass clazz,
jstring hook_lib_dir,
jstring custom_driver_dir,
jstring custom_driver_name,
jstring file_redirect_dir) {
EmulationSession::GetInstance().InitializeGpuDriver(
GetJString(env, hook_lib_dir), GetJString(env, custom_driver_dir),
GetJString(env, custom_driver_name), GetJString(env, file_redirect_dir));
}
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_reloadKeys(JNIEnv* env,
[[maybe_unused]] jclass clazz) {
[[maybe_unused]] static bool CheckKgslPresent() {
constexpr auto KgslPath{"/dev/kgsl-3d0"};
return access(KgslPath, F_OK) == 0;
}
[[maybe_unused]] bool SupportsCustomDriver() {
return android_get_device_api_level() >= 28 && CheckKgslPresent();
}
jboolean JNICALL Java_org_yuzu_yuzu_1emu_utils_GpuDriverHelper_supportsCustomDriverLoading(
JNIEnv* env, jobject instance) {
#ifdef ARCHITECTURE_arm64
// If the KGSL device exists custom drivers can be loaded using adrenotools
return SupportsCustomDriver();
#else
return false;
#endif
}
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_reloadKeys(JNIEnv* env, jclass clazz) {
Core::Crypto::KeyManager::Instance().ReloadKeys();
return static_cast<jboolean>(Core::Crypto::KeyManager::Instance().AreKeysLoaded());
}
void Java_org_yuzu_yuzu_1emu_NativeLibrary_unPauseEmulation([[maybe_unused]] JNIEnv* env,
[[maybe_unused]] jclass clazz) {
void Java_org_yuzu_yuzu_1emu_NativeLibrary_unpauseEmulation(JNIEnv* env, jclass clazz) {
EmulationSession::GetInstance().UnPauseEmulation();
}
void Java_org_yuzu_yuzu_1emu_NativeLibrary_pauseEmulation([[maybe_unused]] JNIEnv* env,
[[maybe_unused]] jclass clazz) {
void Java_org_yuzu_yuzu_1emu_NativeLibrary_pauseEmulation(JNIEnv* env, jclass clazz) {
EmulationSession::GetInstance().PauseEmulation();
}
void Java_org_yuzu_yuzu_1emu_NativeLibrary_stopEmulation([[maybe_unused]] JNIEnv* env,
[[maybe_unused]] jclass clazz) {
void Java_org_yuzu_yuzu_1emu_NativeLibrary_stopEmulation(JNIEnv* env, jclass clazz) {
EmulationSession::GetInstance().HaltEmulation();
}
void Java_org_yuzu_yuzu_1emu_NativeLibrary_resetRomMetadata([[maybe_unused]] JNIEnv* env,
[[maybe_unused]] jclass clazz) {
void Java_org_yuzu_yuzu_1emu_NativeLibrary_resetRomMetadata(JNIEnv* env, jclass clazz) {
EmulationSession::GetInstance().ResetRomMetadata();
}
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isRunning([[maybe_unused]] JNIEnv* env,
[[maybe_unused]] jclass clazz) {
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isRunning(JNIEnv* env, jclass clazz) {
return static_cast<jboolean>(EmulationSession::GetInstance().IsRunning());
}
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isHandheldOnly([[maybe_unused]] JNIEnv* env,
[[maybe_unused]] jclass clazz) {
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isPaused(JNIEnv* env, jclass clazz) {
return static_cast<jboolean>(EmulationSession::GetInstance().IsPaused());
}
void Java_org_yuzu_yuzu_1emu_NativeLibrary_muteAduio(JNIEnv* env, jclass clazz) {
Settings::values.audio_muted = true;
}
void Java_org_yuzu_yuzu_1emu_NativeLibrary_unmuteAudio(JNIEnv* env, jclass clazz) {
Settings::values.audio_muted = false;
}
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isMuted(JNIEnv* env, jclass clazz) {
return static_cast<jboolean>(Settings::values.audio_muted.GetValue());
}
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isHandheldOnly(JNIEnv* env, jclass clazz) {
return EmulationSession::GetInstance().IsHandheldOnly();
}
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_setDeviceType([[maybe_unused]] JNIEnv* env,
[[maybe_unused]] jclass clazz,
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_setDeviceType(JNIEnv* env, jclass clazz,
jint j_device, jint j_type) {
if (EmulationSession::GetInstance().IsRunning()) {
EmulationSession::GetInstance().SetDeviceType(j_device, j_type);
@@ -527,8 +637,7 @@ jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_setDeviceType([[maybe_unused]] JN
return static_cast<jboolean>(true);
}
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadConnectEvent([[maybe_unused]] JNIEnv* env,
[[maybe_unused]] jclass clazz,
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadConnectEvent(JNIEnv* env, jclass clazz,
jint j_device) {
if (EmulationSession::GetInstance().IsRunning()) {
EmulationSession::GetInstance().OnGamepadConnectEvent(j_device);
@@ -536,17 +645,16 @@ jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadConnectEvent([[maybe_unu
return static_cast<jboolean>(true);
}
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadDisconnectEvent(
[[maybe_unused]] JNIEnv* env, [[maybe_unused]] jclass clazz, jint j_device) {
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadDisconnectEvent(JNIEnv* env, jclass clazz,
jint j_device) {
if (EmulationSession::GetInstance().IsRunning()) {
EmulationSession::GetInstance().OnGamepadDisconnectEvent(j_device);
}
return static_cast<jboolean>(true);
}
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadButtonEvent([[maybe_unused]] JNIEnv* env,
[[maybe_unused]] jclass clazz,
[[maybe_unused]] jint j_device,
jint j_button, jint action) {
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadButtonEvent(JNIEnv* env, jclass clazz,
jint j_device, jint j_button,
jint action) {
if (EmulationSession::GetInstance().IsRunning()) {
// Ensure gamepad is connected
EmulationSession::GetInstance().OnGamepadConnectEvent(j_device);
@@ -556,8 +664,7 @@ jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadButtonEvent([[maybe_unus
return static_cast<jboolean>(true);
}
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadJoystickEvent([[maybe_unused]] JNIEnv* env,
[[maybe_unused]] jclass clazz,
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadJoystickEvent(JNIEnv* env, jclass clazz,
jint j_device, jint stick_id,
jfloat x, jfloat y) {
if (EmulationSession::GetInstance().IsRunning()) {
@@ -567,9 +674,8 @@ jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadJoystickEvent([[maybe_un
}
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadMotionEvent(
[[maybe_unused]] JNIEnv* env, [[maybe_unused]] jclass clazz, jint j_device,
jlong delta_timestamp, jfloat gyro_x, jfloat gyro_y, jfloat gyro_z, jfloat accel_x,
jfloat accel_y, jfloat accel_z) {
JNIEnv* env, jclass clazz, jint j_device, jlong delta_timestamp, jfloat gyro_x, jfloat gyro_y,
jfloat gyro_z, jfloat accel_x, jfloat accel_y, jfloat accel_z) {
if (EmulationSession::GetInstance().IsRunning()) {
EmulationSession::GetInstance().Window().OnGamepadMotionEvent(
j_device, delta_timestamp, gyro_x, gyro_y, gyro_z, accel_x, accel_y, accel_z);
@@ -577,8 +683,7 @@ jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadMotionEvent(
return static_cast<jboolean>(true);
}
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onReadNfcTag([[maybe_unused]] JNIEnv* env,
[[maybe_unused]] jclass clazz,
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onReadNfcTag(JNIEnv* env, jclass clazz,
jbyteArray j_data) {
jboolean isCopy{false};
std::span<u8> data(reinterpret_cast<u8*>(env->GetByteArrayElements(j_data, &isCopy)),
@@ -590,102 +695,92 @@ jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onReadNfcTag([[maybe_unused]] JNI
return static_cast<jboolean>(true);
}
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onRemoveNfcTag([[maybe_unused]] JNIEnv* env,
[[maybe_unused]] jclass clazz) {
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_onRemoveNfcTag(JNIEnv* env, jclass clazz) {
if (EmulationSession::GetInstance().IsRunning()) {
EmulationSession::GetInstance().Window().OnRemoveNfcTag();
}
return static_cast<jboolean>(true);
}
void Java_org_yuzu_yuzu_1emu_NativeLibrary_onTouchPressed([[maybe_unused]] JNIEnv* env,
[[maybe_unused]] jclass clazz, jint id,
void Java_org_yuzu_yuzu_1emu_NativeLibrary_onTouchPressed(JNIEnv* env, jclass clazz, jint id,
jfloat x, jfloat y) {
if (EmulationSession::GetInstance().IsRunning()) {
EmulationSession::GetInstance().Window().OnTouchPressed(id, x, y);
}
}
void Java_org_yuzu_yuzu_1emu_NativeLibrary_onTouchMoved([[maybe_unused]] JNIEnv* env,
[[maybe_unused]] jclass clazz, jint id,
void Java_org_yuzu_yuzu_1emu_NativeLibrary_onTouchMoved(JNIEnv* env, jclass clazz, jint id,
jfloat x, jfloat y) {
if (EmulationSession::GetInstance().IsRunning()) {
EmulationSession::GetInstance().Window().OnTouchMoved(id, x, y);
}
}
void Java_org_yuzu_yuzu_1emu_NativeLibrary_onTouchReleased([[maybe_unused]] JNIEnv* env,
[[maybe_unused]] jclass clazz, jint id) {
void Java_org_yuzu_yuzu_1emu_NativeLibrary_onTouchReleased(JNIEnv* env, jclass clazz, jint id) {
if (EmulationSession::GetInstance().IsRunning()) {
EmulationSession::GetInstance().Window().OnTouchReleased(id);
}
}
jbyteArray Java_org_yuzu_yuzu_1emu_NativeLibrary_getIcon([[maybe_unused]] JNIEnv* env,
[[maybe_unused]] jclass clazz,
[[maybe_unused]] jstring j_filename) {
auto icon_data = EmulationSession::GetInstance().GetRomIcon(GetJString(env, j_filename));
jbyteArray Java_org_yuzu_yuzu_1emu_NativeLibrary_getIcon(JNIEnv* env, jclass clazz,
jstring j_filename) {
jauto icon_data = EmulationSession::GetInstance().GetRomIcon(GetJString(env, j_filename));
jbyteArray icon = env->NewByteArray(static_cast<jsize>(icon_data.size()));
env->SetByteArrayRegion(icon, 0, env->GetArrayLength(icon),
reinterpret_cast<jbyte*>(icon_data.data()));
return icon;
}
jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getTitle([[maybe_unused]] JNIEnv* env,
[[maybe_unused]] jclass clazz,
[[maybe_unused]] jstring j_filename) {
auto title = EmulationSession::GetInstance().GetRomTitle(GetJString(env, j_filename));
jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getTitle(JNIEnv* env, jclass clazz,
jstring j_filename) {
jauto title = EmulationSession::GetInstance().GetRomTitle(GetJString(env, j_filename));
return env->NewStringUTF(title.c_str());
}
jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getDescription([[maybe_unused]] JNIEnv* env,
[[maybe_unused]] jclass clazz,
jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getDescription(JNIEnv* env, jclass clazz,
jstring j_filename) {
return j_filename;
}
jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getGameId([[maybe_unused]] JNIEnv* env,
[[maybe_unused]] jclass clazz,
jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getGameId(JNIEnv* env, jclass clazz,
jstring j_filename) {
return j_filename;
}
jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getRegions([[maybe_unused]] JNIEnv* env,
[[maybe_unused]] jclass clazz,
[[maybe_unused]] jstring j_filename) {
jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getRegions(JNIEnv* env, jclass clazz,
jstring j_filename) {
return env->NewStringUTF("");
}
jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getCompany([[maybe_unused]] JNIEnv* env,
[[maybe_unused]] jclass clazz,
[[maybe_unused]] jstring j_filename) {
jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getCompany(JNIEnv* env, jclass clazz,
jstring j_filename) {
return env->NewStringUTF("");
}
void Java_org_yuzu_yuzu_1emu_NativeLibrary_initializeEmulation
[[maybe_unused]] (JNIEnv* env, [[maybe_unused]] jclass clazz) {
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_isHomebrew(JNIEnv* env, jclass clazz,
jstring j_filename) {
return EmulationSession::GetInstance().GetIsHomebrew(GetJString(env, j_filename));
}
void Java_org_yuzu_yuzu_1emu_NativeLibrary_initializeEmulation(JNIEnv* env, jclass clazz) {
// Create the default config.ini.
Config{};
// Initialize the emulated system.
EmulationSession::GetInstance().System().Initialize();
}
jint Java_org_yuzu_yuzu_1emu_NativeLibrary_defaultCPUCore([[maybe_unused]] JNIEnv* env,
[[maybe_unused]] jclass clazz) {
jint Java_org_yuzu_yuzu_1emu_NativeLibrary_defaultCPUCore(JNIEnv* env, jclass clazz) {
return {};
}
void Java_org_yuzu_yuzu_1emu_NativeLibrary_run__Ljava_lang_String_2Ljava_lang_String_2Z(
[[maybe_unused]] JNIEnv* env, [[maybe_unused]] jclass clazz, [[maybe_unused]] jstring j_file,
[[maybe_unused]] jstring j_savestate, [[maybe_unused]] jboolean j_delete_savestate) {}
JNIEnv* env, jclass clazz, jstring j_file, jstring j_savestate, jboolean j_delete_savestate) {}
void Java_org_yuzu_yuzu_1emu_NativeLibrary_reloadSettings([[maybe_unused]] JNIEnv* env,
[[maybe_unused]] jclass clazz) {
void Java_org_yuzu_yuzu_1emu_NativeLibrary_reloadSettings(JNIEnv* env, jclass clazz) {
Config{};
}
jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getUserSetting([[maybe_unused]] JNIEnv* env,
[[maybe_unused]] jclass clazz,
jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getUserSetting(JNIEnv* env, jclass clazz,
jstring j_game_id, jstring j_section,
jstring j_key) {
std::string_view game_id = env->GetStringUTFChars(j_game_id, 0);
@@ -699,8 +794,7 @@ jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getUserSetting([[maybe_unused]] JN
return env->NewStringUTF("");
}
void Java_org_yuzu_yuzu_1emu_NativeLibrary_setUserSetting([[maybe_unused]] JNIEnv* env,
[[maybe_unused]] jclass clazz,
void Java_org_yuzu_yuzu_1emu_NativeLibrary_setUserSetting(JNIEnv* env, jclass clazz,
jstring j_game_id, jstring j_section,
jstring j_key, jstring j_value) {
std::string_view game_id = env->GetStringUTFChars(j_game_id, 0);
@@ -714,20 +808,18 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_setUserSetting([[maybe_unused]] JNIEn
env->ReleaseStringUTFChars(j_value, value.data());
}
void Java_org_yuzu_yuzu_1emu_NativeLibrary_initGameIni([[maybe_unused]] JNIEnv* env,
[[maybe_unused]] jclass clazz,
void Java_org_yuzu_yuzu_1emu_NativeLibrary_initGameIni(JNIEnv* env, jclass clazz,
jstring j_game_id) {
std::string_view game_id = env->GetStringUTFChars(j_game_id, 0);
env->ReleaseStringUTFChars(j_game_id, game_id.data());
}
jdoubleArray Java_org_yuzu_yuzu_1emu_NativeLibrary_getPerfStats([[maybe_unused]] JNIEnv* env,
[[maybe_unused]] jclass clazz) {
jdoubleArray Java_org_yuzu_yuzu_1emu_NativeLibrary_getPerfStats(JNIEnv* env, jclass clazz) {
jdoubleArray j_stats = env->NewDoubleArray(4);
if (EmulationSession::GetInstance().IsRunning()) {
const auto results = EmulationSession::GetInstance().PerfStats();
jconst results = EmulationSession::GetInstance().PerfStats();
// Converting the structure into an array makes it easier to pass it to the frontend
double stats[4] = {results.system_fps, results.average_game_fps, results.frametime,
@@ -739,11 +831,11 @@ jdoubleArray Java_org_yuzu_yuzu_1emu_NativeLibrary_getPerfStats([[maybe_unused]]
return j_stats;
}
void Java_org_yuzu_yuzu_1emu_utils_DirectoryInitialization_setSysDirectory(
[[maybe_unused]] JNIEnv* env, [[maybe_unused]] jclass clazz, jstring j_path) {}
void Java_org_yuzu_yuzu_1emu_utils_DirectoryInitialization_setSysDirectory(JNIEnv* env,
jclass clazz,
jstring j_path) {}
void Java_org_yuzu_yuzu_1emu_NativeLibrary_run__Ljava_lang_String_2([[maybe_unused]] JNIEnv* env,
[[maybe_unused]] jclass clazz,
void Java_org_yuzu_yuzu_1emu_NativeLibrary_run__Ljava_lang_String_2(JNIEnv* env, jclass clazz,
jstring j_path) {
const std::string path = GetJString(env, j_path);
@@ -754,8 +846,7 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_run__Ljava_lang_String_2([[maybe_unus
}
}
void Java_org_yuzu_yuzu_1emu_NativeLibrary_logDeviceInfo([[maybe_unused]] JNIEnv* env,
[[maybe_unused]] jclass clazz) {
void Java_org_yuzu_yuzu_1emu_NativeLibrary_logDeviceInfo(JNIEnv* env, jclass clazz) {
LOG_INFO(Frontend, "yuzu Version: {}-{}", Common::g_scm_branch, Common::g_scm_desc);
LOG_INFO(Frontend, "Host OS: Android API level {}", android_get_device_api_level());
}

View File

@@ -1,165 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <jni.h>
// Function calls from the Java side
#ifdef __cplusplus
extern "C" {
#endif
JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_UnPauseEmulation(JNIEnv* env,
jclass clazz);
JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_PauseEmulation(JNIEnv* env,
jclass clazz);
JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_StopEmulation(JNIEnv* env,
jclass clazz);
JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_ResetRomMetadata(JNIEnv* env,
jclass clazz);
JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_IsRunning(JNIEnv* env,
jclass clazz);
JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_isHandheldOnly(JNIEnv* env,
jclass clazz);
JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_setDeviceType(JNIEnv* env,
jclass clazz,
jstring j_device,
jstring j_type);
JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadConnectEvent(
JNIEnv* env, jclass clazz, jstring j_device);
JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadDisconnectEvent(
JNIEnv* env, jclass clazz, jstring j_device);
JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadEvent(
JNIEnv* env, jclass clazz, jstring j_device, jint j_button, jint action);
JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadMoveEvent(
JNIEnv* env, jclass clazz, jstring j_device, jint axis, jfloat x, jfloat y);
JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_onGamePadAxisEvent(
JNIEnv* env, jclass clazz, jstring j_device, jint axis_id, jfloat axis_val);
JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_onReadNfcTag(JNIEnv* env,
jclass clazz,
jbyteArray j_data);
JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_onRemoveNfcTag(JNIEnv* env,
jclass clazz);
JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_onTouchEvent(JNIEnv* env,
jclass clazz,
jfloat x, jfloat y,
jboolean pressed);
JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_onTouchMoved(JNIEnv* env, jclass clazz,
jfloat x, jfloat y);
JNIEXPORT jbyteArray JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_GetIcon(JNIEnv* env,
jclass clazz,
jstring j_file);
JNIEXPORT jstring JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_GetTitle(JNIEnv* env, jclass clazz,
jstring j_filename);
JNIEXPORT jstring JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_GetDescription(JNIEnv* env,
jclass clazz,
jstring j_filename);
JNIEXPORT jstring JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_GetGameId(JNIEnv* env, jclass clazz,
jstring j_filename);
JNIEXPORT jstring JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_GetRegions(JNIEnv* env,
jclass clazz,
jstring j_filename);
JNIEXPORT jstring JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_GetCompany(JNIEnv* env,
jclass clazz,
jstring j_filename);
JNIEXPORT jstring JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_GetGitRevision(JNIEnv* env,
jclass clazz);
JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_SetAppDirectory(JNIEnv* env,
jclass clazz,
jstring j_directory);
JNIEXPORT void JNICALL
Java_org_yuzu_yuzu_1emu_NativeLibrary_Java_org_yuzu_yuzu_1emu_NativeLibrary_InitializeGpuDriver(
JNIEnv* env, jclass clazz, jstring hook_lib_dir, jstring custom_driver_dir,
jstring custom_driver_name, jstring file_redirect_dir);
JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_ReloadKeys(JNIEnv* env,
jclass clazz);
JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_utils_DirectoryInitialization_SetSysDirectory(
JNIEnv* env, jclass clazz, jstring path_);
JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_SetSysDirectory(JNIEnv* env,
jclass clazz,
jstring path);
JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_InitializeEmulation(JNIEnv* env,
jclass clazz);
JNIEXPORT jint JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_DefaultCPUCore(JNIEnv* env,
jclass clazz);
JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_SetProfiling(JNIEnv* env, jclass clazz,
jboolean enable);
JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_WriteProfileResults(JNIEnv* env,
jclass clazz);
JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_NotifyOrientationChange(
JNIEnv* env, jclass clazz, jint layout_option, jint rotation);
JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_Run__Ljava_lang_String_2(
JNIEnv* env, jclass clazz, jstring j_path);
JNIEXPORT void JNICALL
Java_org_yuzu_yuzu_1emu_NativeLibrary_Run__Ljava_lang_String_2Ljava_lang_String_2Z(
JNIEnv* env, jclass clazz, jstring j_file, jstring j_savestate, jboolean j_delete_savestate);
JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_SurfaceChanged(JNIEnv* env,
jclass clazz,
jobject surf);
JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_SurfaceDestroyed(JNIEnv* env,
jclass clazz);
JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_InitGameIni(JNIEnv* env, jclass clazz,
jstring j_game_id);
JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_ReloadSettings(JNIEnv* env,
jclass clazz);
JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_SetUserSetting(
JNIEnv* env, jclass clazz, jstring j_game_id, jstring j_section, jstring j_key,
jstring j_value);
JNIEXPORT jstring JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_GetUserSetting(
JNIEnv* env, jclass clazz, jstring game_id, jstring section, jstring key);
JNIEXPORT jdoubleArray JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_GetPerfStats(JNIEnv* env,
jclass clazz);
JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_LogDeviceInfo(JNIEnv* env,
jclass clazz);
JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_SubmitInlineKeyboardText(
JNIEnv* env, jclass clazz, jstring j_text);
JNIEXPORT void JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_SubmitInlineKeyboardInput(
JNIEnv* env, jclass clazz, jint j_key_code);
#ifdef __cplusplus
}
#endif

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

View File

@@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M160,840Q127,840 103.5,816.5Q80,793 80,760L80,200Q80,167 103.5,143.5Q127,120 160,120L720,120Q753,120 776.5,143.5Q800,167 800,200L800,280L840,280Q857,280 868.5,291.5Q880,303 880,320Q880,337 868.5,348.5Q857,360 840,360L800,360L800,440L840,440Q857,440 868.5,451.5Q880,463 880,480Q880,497 868.5,508.5Q857,520 840,520L800,520L800,600L840,600Q857,600 868.5,611.5Q880,623 880,640Q880,657 868.5,668.5Q857,680 840,680L800,680L800,760Q800,793 776.5,816.5Q753,840 720,840L160,840ZM160,760L720,760Q720,760 720,760Q720,760 720,760L720,200Q720,200 720,200Q720,200 720,200L160,200Q160,200 160,200Q160,200 160,200L160,760Q160,760 160,760Q160,760 160,760ZM280,680L400,680Q417,680 428.5,668.5Q440,657 440,640L440,560Q440,543 428.5,531.5Q417,520 400,520L280,520Q263,520 251.5,531.5Q240,543 240,560L240,640Q240,657 251.5,668.5Q263,680 280,680ZM520,400L600,400Q617,400 628.5,388.5Q640,377 640,360L640,320Q640,303 628.5,291.5Q617,280 600,280L520,280Q503,280 491.5,291.5Q480,303 480,320L480,360Q480,377 491.5,388.5Q503,400 520,400ZM280,480L400,480Q417,480 428.5,468.5Q440,457 440,440L440,320Q440,303 428.5,291.5Q417,280 400,280L280,280Q263,280 251.5,291.5Q240,303 240,320L240,440Q240,457 251.5,468.5Q263,480 280,480ZM520,680L600,680Q617,680 628.5,668.5Q640,657 640,640L640,480Q640,463 628.5,451.5Q617,440 600,440L520,440Q503,440 491.5,451.5Q480,463 480,480L480,640Q480,657 491.5,668.5Q503,680 520,680ZM160,200L160,200Q160,200 160,200Q160,200 160,200L160,760Q160,760 160,760Q160,760 160,760L160,760Q160,760 160,760Q160,760 160,760L160,200Q160,200 160,200Q160,200 160,200Z"/>
</vector>

View File

@@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M360,720L600,720Q617,720 628.5,708.5Q640,697 640,680Q640,663 628.5,651.5Q617,640 600,640L360,640Q343,640 331.5,651.5Q320,663 320,680Q320,697 331.5,708.5Q343,720 360,720ZM360,560L600,560Q617,560 628.5,548.5Q640,537 640,520Q640,503 628.5,491.5Q617,480 600,480L360,480Q343,480 331.5,491.5Q320,503 320,520Q320,537 331.5,548.5Q343,560 360,560ZM240,880Q207,880 183.5,856.5Q160,833 160,800L160,160Q160,127 183.5,103.5Q207,80 240,80L527,80Q543,80 557.5,86Q572,92 583,103L777,297Q788,308 794,322.5Q800,337 800,353L800,800Q800,833 776.5,856.5Q753,880 720,880L240,880ZM520,320L520,160L240,160Q240,160 240,160Q240,160 240,160L240,800Q240,800 240,800Q240,800 240,800L720,800Q720,800 720,800Q720,800 720,800L720,360L560,360Q543,360 531.5,348.5Q520,337 520,320ZM240,160L240,160L240,320Q240,337 240,348.5Q240,360 240,360L240,360L240,160L240,320Q240,337 240,348.5Q240,360 240,360L240,360L240,800Q240,800 240,800Q240,800 240,800L240,800Q240,800 240,800Q240,800 240,800L240,160Q240,160 240,160Q240,160 240,160Z"/>
</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="@android:color/white"
android:pathData="M7,9v6h4l5,5V4l-5,5H7z" />
</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="@android:color/white"
android:pathData="M6,19h4L10,5L6,5v14zM14,5v14h4L18,5h-4z" />
</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="@android:color/white"
android:pathData="M8,5v14l11,-7z" />
</vector>

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