Compare commits
530 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a493ba76b4 | |||
| feb60de5c3 | |||
| 71f53b4218 | |||
| f131b0faeb | |||
| 6c64d5aff2 | |||
| de594995da | |||
| 4c16a1a26f | |||
| 862e66202c | |||
| 2136a46ab7 | |||
| b9c7e5c2c8 | |||
| d86e88a622 | |||
| 7eac28e410 | |||
| ea4c92f734 | |||
| c9cd938dfd | |||
| 4c5e3d5f7a | |||
| e2be180136 | |||
| 24548b1f5c | |||
| be0ecae108 | |||
| fcd54c6479 | |||
| 08296f151e | |||
| a134e924ff | |||
| 31ed6bae11 | |||
| 9a5ef835cc | |||
| df0d3698ae | |||
| 51fc608f68 | |||
| b30e19ba24 | |||
| ec6b67d862 | |||
| 4d0b7f8496 | |||
| e3b510a4b4 | |||
| 247d66a680 | |||
| 0047d8a01e | |||
| efc0187537 | |||
| b6fe8a0b3f | |||
| ecaa038b4d | |||
| 4aac971864 | |||
| 6c93cdffb1 | |||
| 470714e2d1 | |||
| 6b888b0fa8 | |||
| 1a1393dad7 | |||
| 55412962c0 | |||
| d920da2631 | |||
| ff72bf2cb2 | |||
| 4efb9763d9 | |||
| c600bc8652 | |||
| f1806d237f | |||
| ae57a99d7d | |||
| 767c4b5a99 | |||
| 904d03b01f | |||
| 9f1c9599a2 | |||
| 5f6666a7cd | |||
| 1906e2724f | |||
| 0c032d3f2f | |||
| a6735cba5f | |||
| 3b872b89d1 | |||
| 40d4e9543b | |||
| e588f341ed | |||
| 875246f5b2 | |||
| b16fefa106 | |||
| 2a255b2d61 | |||
| 9e331f9957 | |||
| 9169cbf728 | |||
| 2f9487cd38 | |||
| 1d03a0fa75 | |||
| 09f993899e | |||
| edce713fc9 | |||
| f75363177e | |||
| 4c6217f09b | |||
| c95f35ea85 | |||
| 40357098a2 | |||
| e7f4110791 | |||
| ca1dd1862b | |||
| 737d1cea62 | |||
| 2f8e237ab7 | |||
| 5191465b0a | |||
| 50c604f37f | |||
| dfbc22c291 | |||
| a5a3167eba | |||
| a423e0f9e0 | |||
| 511c1f0c8b | |||
| 8369fcd71a | |||
| 626916e9a4 | |||
| 507f360a81 | |||
| 5323d9f6b3 | |||
| 770d4b0b72 | |||
| e5fed31009 | |||
| f07484bc64 | |||
| 78b9956a04 | |||
| 90aa937593 | |||
| 940618a64d | |||
| 409fa5dda2 | |||
| 211b67668d | |||
| f0cd02b9bd | |||
| 34101d8c5e | |||
| bf8d7bc0da | |||
| 9543adf072 | |||
| 036d2686af | |||
| a80e0e7da5 | |||
| 9631dedea9 | |||
| 75de0cadcf | |||
| 4b321c003c | |||
| 0a83047368 | |||
| 9bb8ac7cb6 | |||
| d6e6ab11b1 | |||
| b3a1f793c3 | |||
| a294beb116 | |||
| eda403388a | |||
| 3032980478 | |||
| 7f96f4db3f | |||
| a0f9a3ab5b | |||
| b36fec486e | |||
| 57cf830862 | |||
| 41701052d3 | |||
| 57c8dcfd77 | |||
| 398e881428 | |||
| 92418e909f | |||
| 7b10ceda02 | |||
| 344162db75 | |||
| 48f913b6e7 | |||
| 0efda40b57 | |||
| 5872c7d420 | |||
| 2b6edd3efd | |||
| b0c6bf497a | |||
| bf16289d77 | |||
| 987b37798a | |||
| 135b645b3d | |||
| 1e468eac94 | |||
| 97b4ca1d01 | |||
| 1d7ff850d6 | |||
| 7e284809de | |||
| 324c93e4aa | |||
| 133788d0d4 | |||
| e8cb8b2668 | |||
| 361dbdddcc | |||
| 6a7123826a | |||
| ab3e3c11af | |||
| db5c24eb66 | |||
| f7755df2af | |||
| c60204e255 | |||
| 5e69769356 | |||
| 22cac3a5e3 | |||
| e867768316 | |||
| 07276cf62a | |||
| f04bc172ae | |||
| 585b6e9d46 | |||
| a9e29a3972 | |||
| 1e61c3e1e7 | |||
| 79d3cef8db | |||
| 3e0da4f698 | |||
| 789c16305d | |||
| 1836e62d33 | |||
| 0bbbe80f75 | |||
| 70be45c992 | |||
| 9b3c64f4a4 | |||
| eec3d356b6 | |||
| a872030a35 | |||
| 79e7d7f4ba | |||
| 2c1d850b46 | |||
| 2581590023 | |||
| adb0900906 | |||
| 2d608cd625 | |||
| 29955de767 | |||
| b0f62d8f24 | |||
| ed2d77ddbc | |||
| 6e883a26da | |||
| 8427b9d49d | |||
| 0bb1c7c804 | |||
| a5aa5876b4 | |||
| 911d2216be | |||
| 4da2105a32 | |||
| 1f9684eaf9 | |||
| 40c97c0549 | |||
| 6aee148b17 | |||
| b5b93e6741 | |||
| 18a4529851 | |||
| 9e4d606c4c | |||
| 6513a356f0 | |||
| 64f60f0acb | |||
| 65d4a16afd | |||
| 21c631b33b | |||
| f26dddf3b5 | |||
| 43be2bfe33 | |||
| 79ba5d9c26 | |||
| ca75c58f43 | |||
| 723df0f368 | |||
| 94b7ac50bb | |||
| 18450ebd78 | |||
| efdb2e8f3d | |||
| 7a84a1a974 | |||
| 789d9c8af9 | |||
| 4df063209b | |||
| 6256e3ca8e | |||
| 008d7e8c5f | |||
| 19e9bde9e0 | |||
| 6eb3a583cb | |||
| e0834ee50b | |||
| 79894152a8 | |||
| 9274eaecd0 | |||
| c733620024 | |||
| 897b411ae7 | |||
| 94836ba3b1 | |||
| b1909b0435 | |||
| 1cc764988f | |||
| da5c49f22d | |||
| 6b93b0b08c | |||
| 68f25217b8 | |||
| 0604b14263 | |||
| a065dcdcd9 | |||
| 3d4a064674 | |||
| e4dfd51337 | |||
| cfe73af6f2 | |||
| d6bd16b2c0 | |||
| a49b146ccc | |||
| fd9e157184 | |||
| 6cbd4020e8 | |||
| 3558b236cd | |||
| 48e82c4138 | |||
| 9eb70aea1d | |||
| 0460fbacc9 | |||
| c73297e840 | |||
| 633d869ff4 | |||
| e03f86cc54 | |||
| a0a3566977 | |||
| 77fb9d415b | |||
| bbdaa62175 | |||
| 31bffc7299 | |||
| 5f8f09d750 | |||
| dcfe674ed4 | |||
| bb195c2c2b | |||
| 8c59543ee3 | |||
| db37e583ff | |||
| d28e826e47 | |||
| 13beb85514 | |||
| 4b06bcc82c | |||
| 12ebc8d9d1 | |||
| 2b85e9e997 | |||
| 59b62c6507 | |||
| 2e760a9833 | |||
| bab4a13a41 | |||
| b56c7397ad | |||
| 689f346e97 | |||
| 249db0a59b | |||
| 9526ce95dd | |||
| 687158fe00 | |||
| d8507332c1 | |||
| 67e983a354 | |||
| f21058a6c0 | |||
| b456af31e6 | |||
| 0441853d0f | |||
| 60a1c6b95b | |||
| 794e6c7a96 | |||
| 22afa2c7a3 | |||
| 85a89ca3e3 | |||
| 26776c0e60 | |||
| e02ee8e59d | |||
| 134ecca9b0 | |||
| c5f1ec8040 | |||
| 765ea9b79d | |||
| c5bdc0054c | |||
| 0b7593d352 | |||
| bd05ace08d | |||
| fa56518f20 | |||
| b577d7a55f | |||
| d9dde7e6f3 | |||
| 2244b613cf | |||
| c73bb33ff1 | |||
| bcce184e60 | |||
| 326ebbb2fa | |||
| 07143ce15c | |||
| dbc73c6c6c | |||
| 8becf13e8b | |||
| 9e2ebb24df | |||
| 90c56f5dc1 | |||
| 4b0291172e | |||
| 12e4757cf3 | |||
| 144c0734f5 | |||
| f40f65f5d2 | |||
| 03c3f936cf | |||
| 66f41da365 | |||
| 7a986d731b | |||
| eae0570a1c | |||
| b57d98f847 | |||
| 762ac5aa9f | |||
| 1a4abd184f | |||
| 9524d7034c | |||
| 36d18e457b | |||
| db562bc08d | |||
| 18672e6a78 | |||
| 32ad99701d | |||
| 63c5340cc4 | |||
| a94371f67b | |||
| 22e4add562 | |||
| b1a7bbd458 | |||
| 27ab2a6e13 | |||
| 68ea0a2b72 | |||
| a8bd02acd8 | |||
| 3e4edbe007 | |||
| 4a9240599a | |||
| 053a16799e | |||
| 82c845dc2f | |||
| 3aa6d4d8ce | |||
| 9b961dddb4 | |||
| 224b6036a4 | |||
| 2c3281c66b | |||
| 1591923f91 | |||
| 56e5d99684 | |||
| ca75c9125d | |||
| 1a4874e178 | |||
| c00b63b9e1 | |||
| c8602e1b1f | |||
| faa6c35e78 | |||
| 8b64878258 | |||
| 519c12da15 | |||
| d74fd9e2fe | |||
| d011d3ff0e | |||
| a5fb9de6fa | |||
| c4ec76edba | |||
| 3f05b8facd | |||
| 65d3300875 | |||
| 7b2ac196d2 | |||
| d9456f0a11 | |||
| 2212c9653d | |||
| fe04a7523a | |||
| 45a76637f5 | |||
| bf7c45e560 | |||
| 48b67fc4a0 | |||
| 6c246f2ac5 | |||
| a34565727b | |||
| 6f4a080b98 | |||
| da6824d9fd | |||
| b36f45b239 | |||
| 07ae6659e7 | |||
| 880b004321 | |||
| 21ebe3e462 | |||
| 98cac9410c | |||
| 7b5d234558 | |||
| 84b0e29b56 | |||
| 5ecdcfa334 | |||
| 5f4857691e | |||
| b50ce645ac | |||
| b6d19329ac | |||
| 8c769b71a1 | |||
| 9512992fe2 | |||
| 6b10f04322 | |||
| c206a04747 | |||
| ec6ddaf766 | |||
| 36ea7565fa | |||
| 00b0938f10 | |||
| ed58445111 | |||
| b28b05e2aa | |||
| 8151a4d301 | |||
| 8ac8d703b9 | |||
| 1c1959eaeb | |||
| c0d152affa | |||
| 85d99f873f | |||
| 21bc2c14bc | |||
| d3997bad9b | |||
| 54fa1115a6 | |||
| 8347e5cdb9 | |||
| dac53b4ba0 | |||
| 0bb7990c49 | |||
| 9ef9ca0927 | |||
| bd42bba71c | |||
| a27f94830a | |||
| bd6f9f1d91 | |||
| bf15aa093c | |||
| 0e9b839b6f | |||
| 15a5bdd979 | |||
| fc4cde7513 | |||
| ff3859d482 | |||
| 10de8f2c60 | |||
| 51b89fddd0 | |||
| f585dec48d | |||
| ad1a9f3d3a | |||
| e797a917a9 | |||
| 71044f6def | |||
| a764f49910 | |||
| a17cde7b2c | |||
| a84c928827 | |||
| 9568d3bc60 | |||
| 0fe935a5de | |||
| c84c35ac74 | |||
| b32940d3ea | |||
| 7a0da729b4 | |||
| 0448eb6f0f | |||
| c334959440 | |||
| 2fa53ec1d9 | |||
| e37ad99f22 | |||
| ff57c66773 | |||
| c6d552f29b | |||
| 7f9b64519d | |||
| d89ef6280c | |||
| 8fb13372c2 | |||
| f9521f5bd4 | |||
| 53f904b740 | |||
| 99e2568304 | |||
| 38394f36d7 | |||
| 26f9d1f122 | |||
| 9cd5c44019 | |||
| 2687a83f6a | |||
| 35f25882e0 | |||
| f1f3d490ef | |||
| ae5c134ac6 | |||
| 03d4fffc70 | |||
| a0d56c855c | |||
| 9007d8c8d4 | |||
| 65bbf708a8 | |||
| 2f0db2708c | |||
| 184ee2d890 | |||
| d6b3e7f195 | |||
| 926e24c642 | |||
| 18a396b53f | |||
| c62e089260 | |||
| 1a246bf135 | |||
| 257a6aa2ba | |||
| 7bae22a3ca | |||
| f24d956ae2 | |||
| 4487c165c8 | |||
| e3f7e02555 | |||
| f782104125 | |||
| 7507a7f89f | |||
| 882859bc78 | |||
| 22284fc504 | |||
| d70f18b87b | |||
| ec388622ff | |||
| 6a425e95cb | |||
| 1fdfedc43e | |||
| 18b240c071 | |||
| 0aa99b8f47 | |||
| 481f91cc34 | |||
| feebdc9779 | |||
| a29e26200f | |||
| 75180bdc9d | |||
| cf44be1de6 | |||
| 95a31b8887 | |||
| c8673a16bb | |||
| 195d0a93b5 | |||
| 3491ba4a06 | |||
| 5326ea63e5 | |||
| e9e6296893 | |||
| 9335cf8857 | |||
| 00a612eaea | |||
| 4e855be38b | |||
| 69ba29e518 | |||
| 3d03e8b806 | |||
| ff9d8dd0b3 | |||
| 38b939b2e9 | |||
| a19f62e636 | |||
| b60013b277 | |||
| 5e4938ab1a | |||
| 854457a392 | |||
| 0d7d3d938c | |||
| 37a4a6751a | |||
| 93a1cd75fe | |||
| b356909212 | |||
| bb28f4a0c4 | |||
| 0993c71335 | |||
| 6e1b113c89 | |||
| c46f54b091 | |||
| 3983ce9b5c | |||
| d2cd08e3e1 | |||
| bb4ae5ee53 | |||
| 57d8cd6c40 | |||
| bf0d6b8806 | |||
| a07c88e686 | |||
| c8237d5c31 | |||
| 2fea1b8407 | |||
| 282ae8fa51 | |||
| aa6587d854 | |||
| 57401589c2 | |||
| f1a2e36711 | |||
| bdc01254a9 | |||
| ace91dd0c0 | |||
| 2921a24268 | |||
| 5269a46399 | |||
| 33e2dce715 | |||
| f3bc7354b1 | |||
| bd5ae33153 | |||
| 16f1592e50 | |||
| fda08cbbb0 | |||
| 1e24d02434 | |||
| a57ca3fb66 | |||
| c619199bb4 | |||
| 703bf7cfce | |||
| 4f69be8169 | |||
| b6ad7e263b | |||
| 9e9cb28471 | |||
| 2ffea42ec8 | |||
| 4a59dc2947 | |||
| c644c1a90a | |||
| 753bc3a448 | |||
| c708643972 | |||
| a85325f56a | |||
| bdb4fd208f | |||
| 1fae4a01a8 | |||
| 8992a62da4 | |||
| 1e740df9b8 | |||
| 02b897ce27 | |||
| df56ecc318 | |||
| 49cb89e324 | |||
| da8cbbf958 | |||
| 55087ab08a | |||
| d7e8926ff6 | |||
| 7dd3d1b8ad | |||
| fd09784231 | |||
| 3b612cff28 | |||
| 32d65fc8de | |||
| 3ff29de4a1 | |||
| dca36ebb87 | |||
| f93f31f4ae | |||
| 79f0202045 | |||
| 2fb71aecb0 | |||
| 9a878de33f | |||
| a50b50f8b4 | |||
| bd409c3416 | |||
| 2f22b53732 | |||
| 1e8616bd01 | |||
| e3c546a1ed | |||
| b99f94a7ff | |||
| 6a1ecab2dd | |||
| f70bafff1a | |||
| 3ad7eec9de | |||
| 7135bdc3bd | |||
| e69eebb14a | |||
| 0145c89879 | |||
| 667ec28697 | |||
| 5464423667 | |||
| cc4736fa58 | |||
| d31676935e | |||
| 26658c2e93 | |||
| b881949b6d |
@@ -8,8 +8,17 @@ ccache -s
|
||||
|
||||
BUILD_FLAVOR=mainline
|
||||
|
||||
if [ ! -z "${ANDROID_KEYSTORE_B64}" ]; then
|
||||
export ANDROID_KEYSTORE_FILE="${GITHUB_WORKSPACE}/ks.jks"
|
||||
base64 --decode <<< "${ANDROID_KEYSTORE_B64}" > "${ANDROID_KEYSTORE_FILE}"
|
||||
fi
|
||||
|
||||
cd src/android
|
||||
chmod +x ./gradlew
|
||||
./gradlew "assemble${BUILD_FLAVOR}Release" "bundle${BUILD_FLAVOR}Release"
|
||||
|
||||
ccache -s
|
||||
|
||||
if [ ! -z "${ANDROID_KEYSTORE_B64}" ]; then
|
||||
rm "${ANDROID_KEYSTORE_FILE}"
|
||||
fi
|
||||
|
||||
@@ -13,15 +13,3 @@ cp src/android/app/build/outputs/apk/"${BUILD_FLAVOR}/release/app-${BUILD_FLAVOR
|
||||
"artifacts/${REV_NAME}.apk"
|
||||
cp src/android/app/build/outputs/bundle/"${BUILD_FLAVOR}Release"/"app-${BUILD_FLAVOR}-release.aab" \
|
||||
"artifacts/${REV_NAME}.aab"
|
||||
|
||||
if [ -n "${ANDROID_KEYSTORE_B64}" ]
|
||||
then
|
||||
echo "Signing apk..."
|
||||
base64 --decode <<< "${ANDROID_KEYSTORE_B64}" > ks.jks
|
||||
|
||||
apksigner sign --ks ks.jks \
|
||||
--ks-key-alias "${ANDROID_KEY_ALIAS}" \
|
||||
--ks-pass env:ANDROID_KEYSTORE_PASS "artifacts/${REV_NAME}.apk"
|
||||
else
|
||||
echo "No keystore specified, not signing the APK files."
|
||||
fi
|
||||
|
||||
@@ -19,6 +19,7 @@ cmake .. \
|
||||
-DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON \
|
||||
-DENABLE_QT_TRANSLATION=ON \
|
||||
-DUSE_DISCORD_PRESENCE=ON \
|
||||
-DYUZU_CRASH_DUMPS=ON \
|
||||
-DYUZU_ENABLE_COMPATIBILITY_REPORTING=${ENABLE_COMPATIBILITY_REPORTING:-"OFF"} \
|
||||
-DYUZU_USE_BUNDLED_FFMPEG=ON \
|
||||
-GNinja
|
||||
|
||||
@@ -5,6 +5,6 @@
|
||||
|
||||
GITDATE="`git show -s --date=short --format='%ad' | sed 's/-//g'`"
|
||||
GITREV="`git show -s --format='%h'`"
|
||||
ARTIFACTS_DIR="artifacts"
|
||||
ARTIFACTS_DIR="$PWD/artifacts"
|
||||
|
||||
mkdir -p "${ARTIFACTS_DIR}/"
|
||||
|
||||
@@ -11,7 +11,7 @@ ccache -s
|
||||
mkdir build || true && cd build
|
||||
cmake .. \
|
||||
-DBoost_USE_STATIC_LIBS=ON \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
|
||||
-DCMAKE_CXX_FLAGS="-march=x86-64-v2" \
|
||||
-DCMAKE_CXX_COMPILER=/usr/lib/ccache/g++ \
|
||||
-DCMAKE_C_COMPILER=/usr/lib/ccache/gcc \
|
||||
@@ -23,6 +23,7 @@ cmake .. \
|
||||
-DYUZU_ENABLE_COMPATIBILITY_REPORTING=${ENABLE_COMPATIBILITY_REPORTING:-"OFF"} \
|
||||
-DYUZU_USE_BUNDLED_FFMPEG=ON \
|
||||
-DYUZU_ENABLE_LTO=ON \
|
||||
-DYUZU_CRASH_DUMPS=ON \
|
||||
-GNinja
|
||||
|
||||
ninja
|
||||
@@ -31,6 +32,19 @@ ccache -s
|
||||
|
||||
ctest -VV -C Release
|
||||
|
||||
# Separate debug symbols from specified executables
|
||||
for EXE in yuzu; do
|
||||
EXE_PATH="bin/$EXE"
|
||||
# Copy debug symbols out
|
||||
objcopy --only-keep-debug $EXE_PATH $EXE_PATH.debug
|
||||
# Add debug link and strip debug symbols
|
||||
objcopy -g --add-gnu-debuglink=$EXE_PATH.debug $EXE_PATH $EXE_PATH.out
|
||||
# Overwrite original with stripped copy
|
||||
mv $EXE_PATH.out $EXE_PATH
|
||||
done
|
||||
# Strip debug symbols from all executables
|
||||
find bin/ -type f -not -regex '.*.debug' -exec strip -g {} ';'
|
||||
|
||||
DESTDIR="$PWD/AppDir" ninja install
|
||||
rm -vf AppDir/usr/bin/yuzu-cmd AppDir/usr/bin/yuzu-tester
|
||||
|
||||
|
||||
@@ -59,4 +59,9 @@ if [ "${RELEASE_NAME}" = "mainline" ] || [ "${RELEASE_NAME}" = "early-access" ];
|
||||
cp "build/${APPIMAGE_NAME}" "${DIR_NAME}/yuzu-${RELEASE_NAME}.AppImage"
|
||||
fi
|
||||
|
||||
# Copy debug symbols to artifacts
|
||||
cd build/bin
|
||||
tar $COMPRESSION_FLAGS "${ARTIFACTS_DIR}/${REV_NAME}-debug.tar.xz" *.debug
|
||||
cd -
|
||||
|
||||
. .ci/scripts/common/post-upload.sh
|
||||
|
||||
@@ -17,7 +17,6 @@ cmake .. \
|
||||
-DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON \
|
||||
-DENABLE_QT_TRANSLATION=ON \
|
||||
-DUSE_CCACHE=ON \
|
||||
-DYUZU_CRASH_DUMPS=ON \
|
||||
-DYUZU_USE_BUNDLED_SDL2=OFF \
|
||||
-DYUZU_USE_EXTERNAL_SDL2=OFF \
|
||||
-DYUZU_TESTS=OFF \
|
||||
|
||||
+1
-1
@@ -3,4 +3,4 @@
|
||||
|
||||
[codespell]
|
||||
skip = ./.git,./build,./dist,./Doxyfile,./externals,./LICENSES,./src/android/app/src/main/res
|
||||
ignore-words-list = aci,allright,ba,canonicalizations,deques,froms,hda,inout,lod,masia,nam,nax,nd,optin,pullrequests,pullrequest,te,transfered,unstall,uscaled,zink
|
||||
ignore-words-list = aci,allright,ba,canonicalizations,deques,froms,hda,inout,lod,masia,nam,nax,nd,optin,pullrequests,pullrequest,te,transfered,unstall,uscaled,vas,zink
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
# SPDX-FileCopyrightText: 2023 yuzu Emulator Project
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
# CRLF -> LF
|
||||
90aa937593e53a5d5e070fb623b228578b0b225f
|
||||
@@ -40,11 +40,11 @@ jobs:
|
||||
sudo apt-get install -y ccache apksigner glslang-dev glslang-tools
|
||||
- name: Build
|
||||
run: ./.ci/scripts/android/build.sh
|
||||
- name: Copy and sign artifacts
|
||||
env:
|
||||
ANDROID_KEYSTORE_B64: ${{ secrets.ANDROID_KEYSTORE_B64 }}
|
||||
ANDROID_KEY_ALIAS: ${{ secrets.ANDROID_KEY_ALIAS }}
|
||||
ANDROID_KEYSTORE_PASS: ${{ secrets.ANDROID_KEYSTORE_PASS }}
|
||||
- name: Copy artifacts
|
||||
run: ./.ci/scripts/android/upload.sh
|
||||
- name: Upload
|
||||
uses: actions/upload-artifact@v3
|
||||
|
||||
+4
-1
@@ -32,7 +32,7 @@
|
||||
path = externals/xbyak
|
||||
url = https://github.com/herumi/xbyak.git
|
||||
[submodule "opus"]
|
||||
path = externals/opus/opus
|
||||
path = externals/opus
|
||||
url = https://github.com/xiph/opus.git
|
||||
[submodule "SDL"]
|
||||
path = externals/SDL
|
||||
@@ -58,3 +58,6 @@
|
||||
[submodule "VulkanMemoryAllocator"]
|
||||
path = externals/VulkanMemoryAllocator
|
||||
url = https://github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator.git
|
||||
[submodule "breakpad"]
|
||||
path = externals/breakpad
|
||||
url = https://github.com/yuzu-emu/breakpad.git
|
||||
|
||||
@@ -147,3 +147,7 @@ License: GPL-3.0-or-later
|
||||
Files: src/android/gradle/wrapper/*
|
||||
Copyright: 2023 yuzu Emulator Project
|
||||
License: GPL-3.0-or-later
|
||||
|
||||
Files: externals/stb/*
|
||||
Copyright: Sean Barrett
|
||||
License: MIT
|
||||
|
||||
+19
-52
@@ -11,7 +11,6 @@ list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/externals/cmake-modul
|
||||
include(DownloadExternals)
|
||||
include(CMakeDependentOption)
|
||||
include(CTest)
|
||||
include(FetchContent)
|
||||
|
||||
# Set bundled sdl2/qt as dependent options.
|
||||
# OFF by default, but if ENABLE_SDL2 and MSVC are true then ON
|
||||
@@ -53,7 +52,7 @@ option(YUZU_DOWNLOAD_ANDROID_VVL "Download validation layer binary for android"
|
||||
|
||||
CMAKE_DEPENDENT_OPTION(YUZU_ROOM "Compile LDN room server" ON "NOT ANDROID" OFF)
|
||||
|
||||
CMAKE_DEPENDENT_OPTION(YUZU_CRASH_DUMPS "Compile Windows crash dump (Minidump) support" OFF "WIN32" OFF)
|
||||
CMAKE_DEPENDENT_OPTION(YUZU_CRASH_DUMPS "Compile crash dump (Minidump) support" OFF "WIN32 OR LINUX" OFF)
|
||||
|
||||
option(YUZU_USE_BUNDLED_VCPKG "Use vcpkg for yuzu dependencies" "${MSVC}")
|
||||
|
||||
@@ -99,47 +98,8 @@ if (ANDROID AND YUZU_DOWNLOAD_ANDROID_VVL)
|
||||
DESTINATION "${vvl_lib_path}")
|
||||
endif()
|
||||
|
||||
# On Android, fetch and compile libcxx before doing anything else
|
||||
if (ANDROID)
|
||||
set(CMAKE_SKIP_INSTALL_RULES ON)
|
||||
set(LLVM_VERSION "15.0.6")
|
||||
|
||||
# Note: even though libcxx and libcxxabi have separate releases on the project page,
|
||||
# the separated releases cannot be compiled. Only in-tree builds work. Therefore we
|
||||
# must fetch the source release for the entire llvm tree.
|
||||
FetchContent_Declare(llvm
|
||||
URL "https://github.com/llvm/llvm-project/releases/download/llvmorg-${LLVM_VERSION}/llvm-project-${LLVM_VERSION}.src.tar.xz"
|
||||
URL_HASH SHA256=9d53ad04dc60cb7b30e810faf64c5ab8157dadef46c8766f67f286238256ff92
|
||||
TLS_VERIFY TRUE
|
||||
)
|
||||
FetchContent_MakeAvailable(llvm)
|
||||
|
||||
# libcxx has support for most of the range library, but it's gated behind a flag:
|
||||
add_compile_definitions(_LIBCPP_ENABLE_EXPERIMENTAL)
|
||||
|
||||
# Disable standard header inclusion
|
||||
set(ANDROID_STL "none")
|
||||
|
||||
# libcxxabi
|
||||
set(LIBCXXABI_INCLUDE_TESTS OFF)
|
||||
set(LIBCXXABI_ENABLE_SHARED FALSE)
|
||||
set(LIBCXXABI_ENABLE_STATIC TRUE)
|
||||
set(LIBCXXABI_LIBCXX_INCLUDES "${LIBCXX_TARGET_INCLUDE_DIRECTORY}" CACHE STRING "" FORCE)
|
||||
add_subdirectory("${llvm_SOURCE_DIR}/libcxxabi" "${llvm_BINARY_DIR}/libcxxabi")
|
||||
link_libraries(cxxabi_static)
|
||||
|
||||
# libcxx
|
||||
set(LIBCXX_ABI_NAMESPACE "__ndk1" CACHE STRING "" FORCE)
|
||||
set(LIBCXX_CXX_ABI "libcxxabi")
|
||||
set(LIBCXX_INCLUDE_TESTS OFF)
|
||||
set(LIBCXX_INCLUDE_BENCHMARKS OFF)
|
||||
set(LIBCXX_INCLUDE_DOCS OFF)
|
||||
set(LIBCXX_ENABLE_SHARED FALSE)
|
||||
set(LIBCXX_ENABLE_STATIC TRUE)
|
||||
set(LIBCXX_ENABLE_ASSERTIONS FALSE)
|
||||
add_subdirectory("${llvm_SOURCE_DIR}/libcxx" "${llvm_BINARY_DIR}/libcxx")
|
||||
set_target_properties(cxx-headers PROPERTIES INTERFACE_COMPILE_OPTIONS "-isystem${CMAKE_BINARY_DIR}/${LIBCXX_INSTALL_INCLUDE_DIR}")
|
||||
link_libraries(cxx_static cxx-headers)
|
||||
endif()
|
||||
|
||||
if (YUZU_USE_BUNDLED_VCPKG)
|
||||
@@ -179,9 +139,6 @@ if (YUZU_USE_BUNDLED_VCPKG)
|
||||
if (YUZU_TESTS)
|
||||
list(APPEND VCPKG_MANIFEST_FEATURES "yuzu-tests")
|
||||
endif()
|
||||
if (YUZU_CRASH_DUMPS)
|
||||
list(APPEND VCPKG_MANIFEST_FEATURES "dbghelp")
|
||||
endif()
|
||||
if (ENABLE_WEB_SERVICE)
|
||||
list(APPEND VCPKG_MANIFEST_FEATURES "web-service")
|
||||
endif()
|
||||
@@ -329,10 +286,12 @@ find_package(Boost 1.79.0 REQUIRED context)
|
||||
find_package(enet 1.3 MODULE)
|
||||
find_package(fmt 9 REQUIRED)
|
||||
find_package(inih 52 MODULE COMPONENTS INIReader)
|
||||
find_package(LLVM 17 MODULE COMPONENTS Demangle)
|
||||
find_package(LLVM 17.0.2 MODULE COMPONENTS Demangle)
|
||||
find_package(lz4 REQUIRED)
|
||||
find_package(nlohmann_json 3.8 REQUIRED)
|
||||
find_package(Opus 1.3 MODULE)
|
||||
find_package(RenderDoc MODULE)
|
||||
find_package(stb MODULE)
|
||||
find_package(VulkanMemoryAllocator CONFIG)
|
||||
find_package(ZLIB 1.2 REQUIRED)
|
||||
find_package(zstd 1.5 REQUIRED)
|
||||
@@ -399,6 +358,9 @@ function(set_yuzu_qt_components)
|
||||
if (ENABLE_QT_TRANSLATION)
|
||||
list(APPEND YUZU_QT_COMPONENTS2 LinguistTools)
|
||||
endif()
|
||||
if (USE_DISCORD_PRESENCE)
|
||||
list(APPEND YUZU_QT_COMPONENTS2 Network)
|
||||
endif()
|
||||
set(YUZU_QT_COMPONENTS ${YUZU_QT_COMPONENTS2} PARENT_SCOPE)
|
||||
endfunction(set_yuzu_qt_components)
|
||||
|
||||
@@ -586,6 +548,18 @@ if (NOT YUZU_USE_BUNDLED_FFMPEG)
|
||||
find_package(FFmpeg 4.3 REQUIRED QUIET COMPONENTS ${FFmpeg_COMPONENTS})
|
||||
endif()
|
||||
|
||||
if (WIN32 AND YUZU_CRASH_DUMPS)
|
||||
set(BREAKPAD_VER "breakpad-c89f9dd")
|
||||
download_bundled_external("breakpad/" ${BREAKPAD_VER} BREAKPAD_PREFIX)
|
||||
|
||||
set(BREAKPAD_CLIENT_INCLUDE_DIR "${BREAKPAD_PREFIX}/include")
|
||||
set(BREAKPAD_CLIENT_LIBRARY "${BREAKPAD_PREFIX}/lib/libbreakpad_client.lib")
|
||||
|
||||
add_library(libbreakpad_client INTERFACE IMPORTED)
|
||||
target_link_libraries(libbreakpad_client INTERFACE "${BREAKPAD_CLIENT_LIBRARY}")
|
||||
target_include_directories(libbreakpad_client INTERFACE "${BREAKPAD_CLIENT_INCLUDE_DIR}")
|
||||
endif()
|
||||
|
||||
# Prefer the -pthread flag on Linux.
|
||||
set(THREADS_PREFER_PTHREAD_FLAG ON)
|
||||
find_package(Threads REQUIRED)
|
||||
@@ -605,13 +579,6 @@ elseif (WIN32)
|
||||
# PSAPI is the Process Status API
|
||||
set(PLATFORM_LIBRARIES ${PLATFORM_LIBRARIES} psapi imm32 version)
|
||||
endif()
|
||||
|
||||
if (YUZU_CRASH_DUMPS)
|
||||
find_library(DBGHELP_LIBRARY dbghelp)
|
||||
if ("${DBGHELP_LIBRARY}" STREQUAL "DBGHELP_LIBRARY-NOTFOUND")
|
||||
message(FATAL_ERROR "YUZU_CRASH_DUMPS enabled but dbghelp library not found")
|
||||
endif()
|
||||
endif()
|
||||
elseif (CMAKE_SYSTEM_NAME MATCHES "^(Linux|kFreeBSD|GNU|SunOS)$")
|
||||
set(PLATFORM_LIBRARIES rt)
|
||||
endif()
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
# SPDX-FileCopyrightText: 2023 Alexandre Bouvier <contact@amb.tf>
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
find_path(RenderDoc_INCLUDE_DIR renderdoc_app.h)
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(RenderDoc
|
||||
REQUIRED_VARS RenderDoc_INCLUDE_DIR
|
||||
)
|
||||
|
||||
if (RenderDoc_FOUND AND NOT TARGET RenderDoc::API)
|
||||
add_library(RenderDoc::API INTERFACE IMPORTED)
|
||||
set_target_properties(RenderDoc::API PROPERTIES
|
||||
INTERFACE_INCLUDE_DIRECTORIES "${RenderDoc_INCLUDE_DIR}"
|
||||
)
|
||||
endif()
|
||||
|
||||
mark_as_advanced(RenderDoc_INCLUDE_DIR)
|
||||
@@ -0,0 +1,31 @@
|
||||
# SPDX-FileCopyrightText: 2023 Alexandre Bouvier <contact@amb.tf>
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
find_path(stb_image_INCLUDE_DIR stb_image.h PATH_SUFFIXES stb)
|
||||
find_path(stb_image_resize_INCLUDE_DIR stb_image_resize.h PATH_SUFFIXES stb)
|
||||
find_path(stb_image_write_INCLUDE_DIR stb_image_write.h PATH_SUFFIXES stb)
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(stb
|
||||
REQUIRED_VARS
|
||||
stb_image_INCLUDE_DIR
|
||||
stb_image_resize_INCLUDE_DIR
|
||||
stb_image_write_INCLUDE_DIR
|
||||
)
|
||||
|
||||
if (stb_FOUND AND NOT TARGET stb::headers)
|
||||
add_library(stb::headers INTERFACE IMPORTED)
|
||||
set_property(TARGET stb::headers PROPERTY
|
||||
INTERFACE_INCLUDE_DIRECTORIES
|
||||
"${stb_image_INCLUDE_DIR}"
|
||||
"${stb_image_resize_INCLUDE_DIR}"
|
||||
"${stb_image_write_INCLUDE_DIR}"
|
||||
)
|
||||
endif()
|
||||
|
||||
mark_as_advanced(
|
||||
stb_image_INCLUDE_DIR
|
||||
stb_image_resize_INCLUDE_DIR
|
||||
stb_image_write_INCLUDE_DIR
|
||||
)
|
||||
@@ -1,7 +1,7 @@
|
||||
# SPDX-FileCopyrightText: 2019 yuzu Emulator Project
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
# Gets a UTC timstamp and sets the provided variable to it
|
||||
# Gets a UTC timestamp and sets the provided variable to it
|
||||
function(get_timestamp _var)
|
||||
string(TIMESTAMP timestamp UTC)
|
||||
set(${_var} "${timestamp}" PARENT_SCOPE)
|
||||
|
||||
Vendored
+7273
File diff suppressed because it is too large
Load Diff
Vendored
+607
-441
File diff suppressed because it is too large
Load Diff
Vendored
+607
-441
File diff suppressed because it is too large
Load Diff
Vendored
+607
-441
File diff suppressed because it is too large
Load Diff
Vendored
+671
-494
File diff suppressed because it is too large
Load Diff
Vendored
+607
-441
File diff suppressed because it is too large
Load Diff
Vendored
+625
-457
File diff suppressed because it is too large
Load Diff
Vendored
+690
-522
File diff suppressed because it is too large
Load Diff
Vendored
+7288
File diff suppressed because it is too large
Load Diff
Vendored
+607
-441
File diff suppressed because it is too large
Load Diff
Vendored
+613
-445
File diff suppressed because it is too large
Load Diff
Vendored
+654
-486
File diff suppressed because it is too large
Load Diff
Vendored
+608
-442
File diff suppressed because it is too large
Load Diff
Vendored
+612
-444
File diff suppressed because it is too large
Load Diff
Vendored
+640
-471
File diff suppressed because it is too large
Load Diff
Vendored
+608
-442
File diff suppressed because it is too large
Load Diff
Vendored
+697
-525
File diff suppressed because it is too large
Load Diff
Vendored
+695
-524
File diff suppressed because it is too large
Load Diff
Vendored
+608
-442
File diff suppressed because it is too large
Load Diff
Vendored
+607
-441
File diff suppressed because it is too large
Load Diff
Vendored
+608
-442
File diff suppressed because it is too large
Load Diff
Vendored
+608
-442
File diff suppressed because it is too large
Load Diff
Vendored
+611
-443
File diff suppressed because it is too large
Load Diff
Vendored
+611
-443
File diff suppressed because it is too large
Load Diff
Vendored
+618
-450
File diff suppressed because it is too large
Load Diff
Vendored
+610
-442
File diff suppressed because it is too large
Load Diff
Vendored
+1
@@ -13,3 +13,4 @@ Exec=yuzu %f
|
||||
Categories=Game;Emulator;Qt;
|
||||
MimeType=application/x-nx-nro;application/x-nx-nso;application/x-nx-nsp;application/x-nx-xci;
|
||||
Keywords=Nintendo;Switch;
|
||||
StartupWMClass=yuzu
|
||||
|
||||
Vendored
+4
@@ -120,6 +120,10 @@ QWidget#connectedControllers {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
QWidget#closeButtons {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
QWidget#playersSupported,
|
||||
QWidget#controllersSupported,
|
||||
QWidget#controllerSupported1,
|
||||
|
||||
Vendored
+4
@@ -1380,6 +1380,10 @@ QWidget#connectedControllers {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
QWidget#closeButtons {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
QWidget#playersSupported,
|
||||
QWidget#controllersSupported,
|
||||
QWidget#controllerSupported1,
|
||||
|
||||
@@ -2305,6 +2305,10 @@ QWidget#connectedControllers {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
QWidget#closeButtons {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
QWidget#playersSupported,
|
||||
QWidget#controllersSupported,
|
||||
QWidget#controllerSupported1,
|
||||
|
||||
Vendored
+115
-2
@@ -134,6 +134,10 @@ endif()
|
||||
|
||||
# Opus
|
||||
if (NOT TARGET Opus::opus)
|
||||
set(OPUS_BUILD_TESTING OFF)
|
||||
set(OPUS_BUILD_PROGRAMS OFF)
|
||||
set(OPUS_INSTALL_PKG_CONFIG_MODULE OFF)
|
||||
set(OPUS_INSTALL_CMAKE_CONFIG_MODULE OFF)
|
||||
add_subdirectory(opus)
|
||||
endif()
|
||||
|
||||
@@ -171,14 +175,123 @@ endif()
|
||||
add_library(stb stb/stb_dxt.cpp)
|
||||
target_include_directories(stb PUBLIC ./stb)
|
||||
|
||||
if (NOT TARGET stb::headers)
|
||||
add_library(stb::headers ALIAS stb)
|
||||
endif()
|
||||
|
||||
add_library(bc_decoder bc_decoder/bc_decoder.cpp)
|
||||
target_include_directories(bc_decoder PUBLIC ./bc_decoder)
|
||||
|
||||
add_library(renderdoc INTERFACE)
|
||||
target_include_directories(renderdoc SYSTEM INTERFACE ./renderdoc)
|
||||
if (NOT TARGET RenderDoc::API)
|
||||
add_library(renderdoc INTERFACE)
|
||||
target_include_directories(renderdoc SYSTEM INTERFACE ./renderdoc)
|
||||
add_library(RenderDoc::API ALIAS renderdoc)
|
||||
endif()
|
||||
|
||||
if (ANDROID)
|
||||
if (ARCHITECTURE_arm64)
|
||||
add_subdirectory(libadrenotools)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Breakpad
|
||||
# https://github.com/microsoft/vcpkg/blob/master/ports/breakpad/CMakeLists.txt
|
||||
if (YUZU_CRASH_DUMPS AND NOT TARGET libbreakpad_client)
|
||||
set(BREAKPAD_WIN32_DEFINES
|
||||
NOMINMAX
|
||||
UNICODE
|
||||
WIN32_LEAN_AND_MEAN
|
||||
_CRT_SECURE_NO_WARNINGS
|
||||
_CRT_SECURE_NO_DEPRECATE
|
||||
_CRT_NONSTDC_NO_DEPRECATE
|
||||
)
|
||||
|
||||
# libbreakpad
|
||||
add_library(libbreakpad STATIC)
|
||||
file(GLOB_RECURSE LIBBREAKPAD_SOURCES breakpad/src/processor/*.cc)
|
||||
file(GLOB_RECURSE LIBDISASM_SOURCES breakpad/src/third_party/libdisasm/*.c)
|
||||
list(FILTER LIBBREAKPAD_SOURCES EXCLUDE REGEX "_unittest|_selftest|synth_minidump|/tests|/testdata|/solaris|microdump_stackwalk|minidump_dump|minidump_stackwalk")
|
||||
if (WIN32)
|
||||
list(FILTER LIBBREAKPAD_SOURCES EXCLUDE REGEX "/linux|/mac|/android")
|
||||
target_compile_definitions(libbreakpad PRIVATE ${BREAKPAD_WIN32_DEFINES})
|
||||
target_include_directories(libbreakpad PRIVATE "${CMAKE_GENERATOR_INSTANCE}/DIA SDK/include")
|
||||
elseif (APPLE)
|
||||
list(FILTER LIBBREAKPAD_SOURCES EXCLUDE REGEX "/linux|/windows|/android")
|
||||
else()
|
||||
list(FILTER LIBBREAKPAD_SOURCES EXCLUDE REGEX "/mac|/windows|/android")
|
||||
endif()
|
||||
target_sources(libbreakpad PRIVATE ${LIBBREAKPAD_SOURCES} ${LIBDISASM_SOURCES})
|
||||
target_include_directories(libbreakpad
|
||||
PUBLIC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/breakpad/src
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/breakpad/src/third_party/libdisasm
|
||||
)
|
||||
|
||||
# libbreakpad_client
|
||||
add_library(libbreakpad_client STATIC)
|
||||
file(GLOB LIBBREAKPAD_COMMON_SOURCES breakpad/src/common/*.cc breakpad/src/common/*.c breakpad/src/client/*.cc)
|
||||
|
||||
if (WIN32)
|
||||
file(GLOB_RECURSE LIBBREAKPAD_CLIENT_SOURCES breakpad/src/client/windows/*.cc breakpad/src/common/windows/*.cc)
|
||||
list(FILTER LIBBREAKPAD_COMMON_SOURCES EXCLUDE REGEX "language.cc|path_helper.cc|stabs_to_module.cc|stabs_reader.cc|minidump_file_writer.cc")
|
||||
target_include_directories(libbreakpad_client PRIVATE "${CMAKE_GENERATOR_INSTANCE}/DIA SDK/include")
|
||||
target_compile_definitions(libbreakpad_client PRIVATE ${BREAKPAD_WIN32_DEFINES})
|
||||
elseif (APPLE)
|
||||
target_compile_definitions(libbreakpad_client PRIVATE HAVE_MACH_O_NLIST_H)
|
||||
file(GLOB_RECURSE LIBBREAKPAD_CLIENT_SOURCES breakpad/src/client/mac/*.cc breakpad/src/common/mac/*.cc)
|
||||
list(APPEND LIBBREAKPAD_CLIENT_SOURCES breakpad/src/common/mac/MachIPC.mm)
|
||||
else()
|
||||
target_compile_definitions(libbreakpad_client PUBLIC -DHAVE_A_OUT_H)
|
||||
file(GLOB_RECURSE LIBBREAKPAD_CLIENT_SOURCES breakpad/src/client/linux/*.cc breakpad/src/common/linux/*.cc)
|
||||
endif()
|
||||
list(APPEND LIBBREAKPAD_CLIENT_SOURCES ${LIBBREAKPAD_COMMON_SOURCES})
|
||||
list(FILTER LIBBREAKPAD_CLIENT_SOURCES EXCLUDE REGEX "/sender|/tests|/unittests|/testcases|_unittest|_test")
|
||||
target_sources(libbreakpad_client PRIVATE ${LIBBREAKPAD_CLIENT_SOURCES})
|
||||
target_include_directories(libbreakpad_client PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/breakpad/src)
|
||||
|
||||
if (WIN32)
|
||||
target_link_libraries(libbreakpad_client PRIVATE wininet.lib)
|
||||
elseif (APPLE)
|
||||
find_library(CoreFoundation_FRAMEWORK CoreFoundation)
|
||||
target_link_libraries(libbreakpad_client PRIVATE ${CoreFoundation_FRAMEWORK})
|
||||
else()
|
||||
find_library(PTHREAD_LIBRARIES pthread)
|
||||
target_compile_definitions(libbreakpad_client PRIVATE HAVE_GETCONTEXT=1)
|
||||
if (PTHREAD_LIBRARIES)
|
||||
target_link_libraries(libbreakpad_client PRIVATE ${PTHREAD_LIBRARIES})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Host tools for symbol processing
|
||||
if (LINUX)
|
||||
find_package(ZLIB REQUIRED)
|
||||
|
||||
add_executable(minidump_stackwalk breakpad/src/processor/minidump_stackwalk.cc)
|
||||
target_link_libraries(minidump_stackwalk PRIVATE libbreakpad libbreakpad_client)
|
||||
|
||||
add_executable(dump_syms
|
||||
breakpad/src/common/dwarf_cfi_to_module.cc
|
||||
breakpad/src/common/dwarf_cu_to_module.cc
|
||||
breakpad/src/common/dwarf_line_to_module.cc
|
||||
breakpad/src/common/dwarf_range_list_handler.cc
|
||||
breakpad/src/common/language.cc
|
||||
breakpad/src/common/module.cc
|
||||
breakpad/src/common/path_helper.cc
|
||||
breakpad/src/common/stabs_reader.cc
|
||||
breakpad/src/common/stabs_to_module.cc
|
||||
breakpad/src/common/dwarf/bytereader.cc
|
||||
breakpad/src/common/dwarf/dwarf2diehandler.cc
|
||||
breakpad/src/common/dwarf/dwarf2reader.cc
|
||||
breakpad/src/common/dwarf/elf_reader.cc
|
||||
breakpad/src/common/linux/crc32.cc
|
||||
breakpad/src/common/linux/dump_symbols.cc
|
||||
breakpad/src/common/linux/elf_symbols_to_module.cc
|
||||
breakpad/src/common/linux/elfutils.cc
|
||||
breakpad/src/common/linux/file_id.cc
|
||||
breakpad/src/common/linux/linux_libc_support.cc
|
||||
breakpad/src/common/linux/memory_mapped_file.cc
|
||||
breakpad/src/common/linux/safe_readlink.cc
|
||||
breakpad/src/tools/linux/dump_syms/dump_syms.cc)
|
||||
target_link_libraries(dump_syms PRIVATE libbreakpad_client ZLIB::ZLIB)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
Vendored
+1
-1
Submodule externals/SDL updated: 031912c4b6...cc016b0046
Vendored
+1
-1
Submodule externals/Vulkan-Headers updated: ed857118e2...df60f03168
Vendored
+1
-1
Submodule externals/VulkanMemoryAllocator updated: 9b0fc3e7b0...2f382df218
+1
Submodule externals/breakpad added at c89f9dddc7
Vendored
+1
-1
Submodule externals/cpp-httplib updated: 6d963fbe8d...a609330e4c
Vendored
+1
-1
Submodule externals/cpp-jwt updated: e12ef06218...10ef5735d8
Vendored
+1
-1
Submodule externals/dynarmic updated: 7da378033a...0df09e2f6b
Vendored
+1
-1
Submodule externals/ffmpeg/ffmpeg updated: 6b6b9e593d...9c1294eadd
Vendored
+1
-1
Submodule externals/inih/inih updated: 1e80a47dff...9cecf0643d
Vendored
-7
@@ -49,11 +49,6 @@ if (MINGW OR (${CMAKE_SYSTEM_NAME} MATCHES "Linux") OR APPLE)
|
||||
|
||||
set(LIBUSB_INCLUDE_DIRS "${LIBUSB_SRC_DIR}/libusb" CACHE PATH "libusb headers path" FORCE)
|
||||
|
||||
# MINGW: causes "externals/libusb/libusb/libusb/os/windows_winusb.c:1427:2: error: conversion to non-scalar type requested", so cannot statically link it for now.
|
||||
if (NOT MINGW)
|
||||
set(LIBUSB_CFLAGS "-DGUID_DEVINTERFACE_USB_DEVICE=\\(GUID\\){0xA5DCBF10,0x6530,0x11D2,{0x90,0x1F,0x00,0xC0,0x4F,0xB9,0x51,0xED}}")
|
||||
endif()
|
||||
|
||||
make_directory("${LIBUSB_PREFIX}")
|
||||
|
||||
add_custom_command(
|
||||
@@ -146,8 +141,6 @@ else() # MINGW OR (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
|
||||
target_include_directories(usb BEFORE PRIVATE libusb/msvc)
|
||||
endif()
|
||||
|
||||
# Works around other libraries providing their own definition of USB GUIDs (e.g. SDL2)
|
||||
target_compile_definitions(usb PRIVATE "-DGUID_DEVINTERFACE_USB_DEVICE=(GUID){ 0xA5DCBF10, 0x6530, 0x11D2, {0x90, 0x1F, 0x00, 0xC0, 0x4F, 0xB9, 0x51, 0xED}}")
|
||||
else()
|
||||
target_include_directories(usb
|
||||
# turns out other projects also have "config.h", so make sure the
|
||||
|
||||
Vendored
+1
-1
Submodule externals/libusb/libusb updated: c6a35c5601...c060e9ce30
Vendored
+1
-1
@@ -27,7 +27,7 @@ if (CMAKE_SYSTEM_NAME STREQUAL "Windows" OR ANDROID)
|
||||
set(CAN_BUILD_NX_TZDB false)
|
||||
endif()
|
||||
|
||||
set(NX_TZDB_VERSION "220816")
|
||||
set(NX_TZDB_VERSION "221202")
|
||||
set(NX_TZDB_ARCHIVE "${CMAKE_CURRENT_BINARY_DIR}/${NX_TZDB_VERSION}.zip")
|
||||
|
||||
set(NX_TZDB_ROMFS_DIR "${CMAKE_CURRENT_BINARY_DIR}/nx_tzdb")
|
||||
|
||||
Vendored
+1
-1
Submodule externals/nx_tzdb/tzdb_to_nx updated: 212afa2394...0d17dd066d
+1
Submodule externals/opus added at 101a71e03b
Vendored
-259
@@ -1,259 +0,0 @@
|
||||
# SPDX-FileCopyrightText: 2019 yuzu Emulator Project
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
cmake_minimum_required(VERSION 3.8)
|
||||
|
||||
project(opus)
|
||||
|
||||
option(OPUS_STACK_PROTECTOR "Use stack protection" OFF)
|
||||
option(OPUS_USE_ALLOCA "Use alloca for stack arrays (on non-C99 compilers)" OFF)
|
||||
option(OPUS_CUSTOM_MODES "Enable non-Opus modes, e.g. 44.1 kHz & 2^n frames" OFF)
|
||||
option(OPUS_FIXED_POINT "Compile as fixed-point (for machines without a fast enough FPU)" OFF)
|
||||
option(OPUS_ENABLE_FLOAT_API "Compile with the floating point API (for machines with float library" ON)
|
||||
|
||||
include(opus/opus_functions.cmake)
|
||||
|
||||
if(OPUS_STACK_PROTECTOR)
|
||||
if(NOT MSVC) # GC on by default on MSVC
|
||||
check_and_set_flag(STACK_PROTECTION_STRONG -fstack-protector-strong)
|
||||
endif()
|
||||
else()
|
||||
if(MSVC)
|
||||
check_and_set_flag(BUFFER_SECURITY_CHECK /GS-)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
add_library(opus
|
||||
# CELT sources
|
||||
opus/celt/bands.c
|
||||
opus/celt/celt.c
|
||||
opus/celt/celt_decoder.c
|
||||
opus/celt/celt_encoder.c
|
||||
opus/celt/celt_lpc.c
|
||||
opus/celt/cwrs.c
|
||||
opus/celt/entcode.c
|
||||
opus/celt/entdec.c
|
||||
opus/celt/entenc.c
|
||||
opus/celt/kiss_fft.c
|
||||
opus/celt/laplace.c
|
||||
opus/celt/mathops.c
|
||||
opus/celt/mdct.c
|
||||
opus/celt/modes.c
|
||||
opus/celt/pitch.c
|
||||
opus/celt/quant_bands.c
|
||||
opus/celt/rate.c
|
||||
opus/celt/vq.c
|
||||
|
||||
# SILK sources
|
||||
opus/silk/A2NLSF.c
|
||||
opus/silk/CNG.c
|
||||
opus/silk/HP_variable_cutoff.c
|
||||
opus/silk/LPC_analysis_filter.c
|
||||
opus/silk/LPC_fit.c
|
||||
opus/silk/LPC_inv_pred_gain.c
|
||||
opus/silk/LP_variable_cutoff.c
|
||||
opus/silk/NLSF2A.c
|
||||
opus/silk/NLSF_VQ.c
|
||||
opus/silk/NLSF_VQ_weights_laroia.c
|
||||
opus/silk/NLSF_decode.c
|
||||
opus/silk/NLSF_del_dec_quant.c
|
||||
opus/silk/NLSF_encode.c
|
||||
opus/silk/NLSF_stabilize.c
|
||||
opus/silk/NLSF_unpack.c
|
||||
opus/silk/NSQ.c
|
||||
opus/silk/NSQ_del_dec.c
|
||||
opus/silk/PLC.c
|
||||
opus/silk/VAD.c
|
||||
opus/silk/VQ_WMat_EC.c
|
||||
opus/silk/ana_filt_bank_1.c
|
||||
opus/silk/biquad_alt.c
|
||||
opus/silk/bwexpander.c
|
||||
opus/silk/bwexpander_32.c
|
||||
opus/silk/check_control_input.c
|
||||
opus/silk/code_signs.c
|
||||
opus/silk/control_SNR.c
|
||||
opus/silk/control_audio_bandwidth.c
|
||||
opus/silk/control_codec.c
|
||||
opus/silk/dec_API.c
|
||||
opus/silk/decode_core.c
|
||||
opus/silk/decode_frame.c
|
||||
opus/silk/decode_indices.c
|
||||
opus/silk/decode_parameters.c
|
||||
opus/silk/decode_pitch.c
|
||||
opus/silk/decode_pulses.c
|
||||
opus/silk/decoder_set_fs.c
|
||||
opus/silk/enc_API.c
|
||||
opus/silk/encode_indices.c
|
||||
opus/silk/encode_pulses.c
|
||||
opus/silk/gain_quant.c
|
||||
opus/silk/init_decoder.c
|
||||
opus/silk/init_encoder.c
|
||||
opus/silk/inner_prod_aligned.c
|
||||
opus/silk/interpolate.c
|
||||
opus/silk/lin2log.c
|
||||
opus/silk/log2lin.c
|
||||
opus/silk/pitch_est_tables.c
|
||||
opus/silk/process_NLSFs.c
|
||||
opus/silk/quant_LTP_gains.c
|
||||
opus/silk/resampler.c
|
||||
opus/silk/resampler_down2.c
|
||||
opus/silk/resampler_down2_3.c
|
||||
opus/silk/resampler_private_AR2.c
|
||||
opus/silk/resampler_private_IIR_FIR.c
|
||||
opus/silk/resampler_private_down_FIR.c
|
||||
opus/silk/resampler_private_up2_HQ.c
|
||||
opus/silk/resampler_rom.c
|
||||
opus/silk/shell_coder.c
|
||||
opus/silk/sigm_Q15.c
|
||||
opus/silk/sort.c
|
||||
opus/silk/stereo_LR_to_MS.c
|
||||
opus/silk/stereo_MS_to_LR.c
|
||||
opus/silk/stereo_decode_pred.c
|
||||
opus/silk/stereo_encode_pred.c
|
||||
opus/silk/stereo_find_predictor.c
|
||||
opus/silk/stereo_quant_pred.c
|
||||
opus/silk/sum_sqr_shift.c
|
||||
opus/silk/table_LSF_cos.c
|
||||
opus/silk/tables_LTP.c
|
||||
opus/silk/tables_NLSF_CB_NB_MB.c
|
||||
opus/silk/tables_NLSF_CB_WB.c
|
||||
opus/silk/tables_gain.c
|
||||
opus/silk/tables_other.c
|
||||
opus/silk/tables_pitch_lag.c
|
||||
opus/silk/tables_pulses_per_block.c
|
||||
|
||||
# Opus sources
|
||||
opus/src/analysis.c
|
||||
opus/src/mapping_matrix.c
|
||||
opus/src/mlp.c
|
||||
opus/src/mlp_data.c
|
||||
opus/src/opus.c
|
||||
opus/src/opus_decoder.c
|
||||
opus/src/opus_encoder.c
|
||||
opus/src/opus_multistream.c
|
||||
opus/src/opus_multistream_decoder.c
|
||||
opus/src/opus_multistream_encoder.c
|
||||
opus/src/opus_projection_decoder.c
|
||||
opus/src/opus_projection_encoder.c
|
||||
opus/src/repacketizer.c
|
||||
)
|
||||
|
||||
if (DEBUG)
|
||||
target_sources(opus PRIVATE opus/silk/debug.c)
|
||||
endif()
|
||||
|
||||
if (OPUS_FIXED_POINT)
|
||||
target_sources(opus PRIVATE
|
||||
opus/silk/fixed/LTP_analysis_filter_FIX.c
|
||||
opus/silk/fixed/LTP_scale_ctrl_FIX.c
|
||||
opus/silk/fixed/apply_sine_window_FIX.c
|
||||
opus/silk/fixed/autocorr_FIX.c
|
||||
opus/silk/fixed/burg_modified_FIX.c
|
||||
opus/silk/fixed/corrMatrix_FIX.c
|
||||
opus/silk/fixed/encode_frame_FIX.c
|
||||
opus/silk/fixed/find_LPC_FIX.c
|
||||
opus/silk/fixed/find_LTP_FIX.c
|
||||
opus/silk/fixed/find_pitch_lags_FIX.c
|
||||
opus/silk/fixed/find_pred_coefs_FIX.c
|
||||
opus/silk/fixed/k2a_FIX.c
|
||||
opus/silk/fixed/k2a_Q16_FIX.c
|
||||
opus/silk/fixed/noise_shape_analysis_FIX.c
|
||||
opus/silk/fixed/pitch_analysis_core_FIX.c
|
||||
opus/silk/fixed/prefilter_FIX.c
|
||||
opus/silk/fixed/process_gains_FIX.c
|
||||
opus/silk/fixed/regularize_correlations_FIX.c
|
||||
opus/silk/fixed/residual_energy16_FIX.c
|
||||
opus/silk/fixed/residual_energy_FIX.c
|
||||
opus/silk/fixed/schur64_FIX.c
|
||||
opus/silk/fixed/schur_FIX.c
|
||||
opus/silk/fixed/solve_LS_FIX.c
|
||||
opus/silk/fixed/vector_ops_FIX.c
|
||||
opus/silk/fixed/warped_autocorrelation_FIX.c
|
||||
)
|
||||
else()
|
||||
target_sources(opus PRIVATE
|
||||
opus/silk/float/LPC_analysis_filter_FLP.c
|
||||
opus/silk/float/LPC_inv_pred_gain_FLP.c
|
||||
opus/silk/float/LTP_analysis_filter_FLP.c
|
||||
opus/silk/float/LTP_scale_ctrl_FLP.c
|
||||
opus/silk/float/apply_sine_window_FLP.c
|
||||
opus/silk/float/autocorrelation_FLP.c
|
||||
opus/silk/float/burg_modified_FLP.c
|
||||
opus/silk/float/bwexpander_FLP.c
|
||||
opus/silk/float/corrMatrix_FLP.c
|
||||
opus/silk/float/encode_frame_FLP.c
|
||||
opus/silk/float/energy_FLP.c
|
||||
opus/silk/float/find_LPC_FLP.c
|
||||
opus/silk/float/find_LTP_FLP.c
|
||||
opus/silk/float/find_pitch_lags_FLP.c
|
||||
opus/silk/float/find_pred_coefs_FLP.c
|
||||
opus/silk/float/inner_product_FLP.c
|
||||
opus/silk/float/k2a_FLP.c
|
||||
opus/silk/float/noise_shape_analysis_FLP.c
|
||||
opus/silk/float/pitch_analysis_core_FLP.c
|
||||
opus/silk/float/process_gains_FLP.c
|
||||
opus/silk/float/regularize_correlations_FLP.c
|
||||
opus/silk/float/residual_energy_FLP.c
|
||||
opus/silk/float/scale_copy_vector_FLP.c
|
||||
opus/silk/float/scale_vector_FLP.c
|
||||
opus/silk/float/schur_FLP.c
|
||||
opus/silk/float/sort_FLP.c
|
||||
opus/silk/float/warped_autocorrelation_FLP.c
|
||||
opus/silk/float/wrappers_FLP.c
|
||||
)
|
||||
endif()
|
||||
|
||||
target_compile_definitions(opus PRIVATE OPUS_BUILD ENABLE_HARDENING)
|
||||
|
||||
if(NOT MSVC)
|
||||
if(MINGW)
|
||||
target_compile_definitions(opus PRIVATE _FORTIFY_SOURCE=0)
|
||||
else()
|
||||
target_compile_definitions(opus PRIVATE _FORTIFY_SOURCE=2)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# It is strongly recommended to uncomment one of these VAR_ARRAYS: Use C99
|
||||
# variable-length arrays for stack allocation USE_ALLOCA: Use alloca() for stack
|
||||
# allocation If none is defined, then the fallback is a non-threadsafe global
|
||||
# array
|
||||
if(OPUS_USE_ALLOCA OR MSVC)
|
||||
target_compile_definitions(opus PRIVATE USE_ALLOCA)
|
||||
else()
|
||||
target_compile_definitions(opus PRIVATE VAR_ARRAYS)
|
||||
endif()
|
||||
|
||||
if(OPUS_CUSTOM_MODES)
|
||||
target_compile_definitions(opus PRIVATE CUSTOM_MODES)
|
||||
endif()
|
||||
|
||||
if(NOT OPUS_ENABLE_FLOAT_API)
|
||||
target_compile_definitions(opus PRIVATE DISABLE_FLOAT_API)
|
||||
endif()
|
||||
|
||||
target_compile_definitions(opus
|
||||
PUBLIC
|
||||
-DOPUS_VERSION="\\"1.3.1\\""
|
||||
|
||||
PRIVATE
|
||||
# Use C99 intrinsics to speed up float-to-int conversion
|
||||
HAVE_LRINTF
|
||||
)
|
||||
|
||||
if (FIXED_POINT)
|
||||
target_compile_definitions(opus PRIVATE -DFIXED_POINT=1 -DDISABLE_FLOAT_API)
|
||||
endif()
|
||||
|
||||
target_include_directories(opus
|
||||
PUBLIC
|
||||
opus/include
|
||||
|
||||
PRIVATE
|
||||
opus/celt
|
||||
opus/silk
|
||||
opus/silk/fixed
|
||||
opus/silk/float
|
||||
opus/src
|
||||
)
|
||||
|
||||
add_library(Opus::opus ALIAS opus)
|
||||
Vendored
-1
Submodule externals/opus/opus deleted from ad8fe90db7
Vendored
+7987
File diff suppressed because it is too large
Load Diff
Vendored
+2634
File diff suppressed because it is too large
Load Diff
Vendored
+1724
File diff suppressed because it is too large
Load Diff
Vendored
+1
-1
Submodule externals/vcpkg updated: cbf56573a9...ef2eef1734
+3
-3
@@ -21,7 +21,7 @@ if (MSVC)
|
||||
# Avoid windows.h from including some usually unused libs like winsocks.h, since this might cause some redefinition errors.
|
||||
add_definitions(-DWIN32_LEAN_AND_MEAN)
|
||||
|
||||
# Ensure that projects build with Unicode support.
|
||||
# Ensure that projects are built with Unicode support.
|
||||
add_definitions(-DUNICODE -D_UNICODE)
|
||||
|
||||
# /W4 - Level 4 warnings
|
||||
@@ -54,11 +54,11 @@ if (MSVC)
|
||||
/GT
|
||||
|
||||
# Modules
|
||||
/experimental:module- # Disable module support explicitly due to conflicts with precompiled headers
|
||||
/experimental:module- # Explicitly disable module support due to conflicts with precompiled headers.
|
||||
|
||||
# External headers diagnostics
|
||||
/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
|
||||
/external:W0 # Sets the default warning level to 0 for external headers, effectively disabling warnings for them.
|
||||
|
||||
# Warnings
|
||||
/W4
|
||||
|
||||
@@ -63,3 +63,6 @@ fastlane/Preview.html
|
||||
fastlane/screenshots
|
||||
fastlane/test_output
|
||||
fastlane/readme.md
|
||||
|
||||
# Autogenerated library for vulkan validation layers
|
||||
libVkLayer_khronos_validation.so
|
||||
|
||||
@@ -27,7 +27,7 @@ android {
|
||||
namespace = "org.yuzu.yuzu_emu"
|
||||
|
||||
compileSdkVersion = "android-34"
|
||||
ndkVersion = "25.2.9519653"
|
||||
ndkVersion = "26.1.10909125"
|
||||
|
||||
buildFeatures {
|
||||
viewBinding = true
|
||||
@@ -47,6 +47,10 @@ android {
|
||||
jniLibs.useLegacyPackaging = true
|
||||
}
|
||||
|
||||
androidResources {
|
||||
generateLocaleConfig = true
|
||||
}
|
||||
|
||||
defaultConfig {
|
||||
// TODO If this is ever modified, change application_id in strings.xml
|
||||
applicationId = "org.yuzu.yuzu_emu"
|
||||
@@ -203,23 +207,23 @@ ktlint {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation("androidx.core:core-ktx:1.10.1")
|
||||
implementation("androidx.core:core-ktx:1.12.0")
|
||||
implementation("androidx.appcompat:appcompat:1.6.1")
|
||||
implementation("androidx.recyclerview:recyclerview:1.3.0")
|
||||
implementation("androidx.recyclerview:recyclerview:1.3.1")
|
||||
implementation("androidx.constraintlayout:constraintlayout:2.1.4")
|
||||
implementation("androidx.fragment:fragment-ktx:1.6.0")
|
||||
implementation("androidx.fragment:fragment-ktx:1.6.1")
|
||||
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("androidx.preference:preference-ktx:1.2.1")
|
||||
implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.2")
|
||||
implementation("io.coil-kt:coil:2.2.2")
|
||||
implementation("androidx.core:core-splashscreen:1.0.1")
|
||||
implementation("androidx.window:window:1.1.0")
|
||||
implementation("androidx.window:window:1.2.0-beta03")
|
||||
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.6.0")
|
||||
implementation("androidx.navigation:navigation-ui-ktx:2.6.0")
|
||||
implementation("androidx.navigation:navigation-fragment-ktx:2.7.4")
|
||||
implementation("androidx.navigation:navigation-ui-ktx:2.7.4")
|
||||
implementation("info.debatty:java-string-similarity:2.0.0")
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.0")
|
||||
}
|
||||
|
||||
@@ -26,9 +26,7 @@ SPDX-License-Identifier: GPL-3.0-or-later
|
||||
android:supportsRtl="true"
|
||||
android:isGame="true"
|
||||
android:appCategory="game"
|
||||
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"
|
||||
android:enableOnBackInvokedCallback="true">
|
||||
|
||||
@@ -5,6 +5,7 @@ package org.yuzu.yuzu_emu
|
||||
|
||||
import android.app.Dialog
|
||||
import android.content.DialogInterface
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.text.Html
|
||||
import android.text.method.LinkMovementMethod
|
||||
@@ -15,13 +16,9 @@ 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.DocumentsTree
|
||||
import org.yuzu.yuzu_emu.utils.FileUtil
|
||||
import org.yuzu.yuzu_emu.utils.Log
|
||||
import org.yuzu.yuzu_emu.utils.SerializableHelper.serializable
|
||||
|
||||
@@ -72,43 +69,61 @@ object NativeLibrary {
|
||||
@Keep
|
||||
@JvmStatic
|
||||
fun openContentUri(path: String?, openmode: String?): Int {
|
||||
return if (isNativePath(path!!)) {
|
||||
return if (DocumentsTree.isNativePath(path!!)) {
|
||||
YuzuApplication.documentsTree!!.openContentUri(path, openmode)
|
||||
} else {
|
||||
openContentUri(appContext, path, openmode)
|
||||
FileUtil.openContentUri(path, openmode)
|
||||
}
|
||||
}
|
||||
|
||||
@Keep
|
||||
@JvmStatic
|
||||
fun getSize(path: String?): Long {
|
||||
return if (isNativePath(path!!)) {
|
||||
return if (DocumentsTree.isNativePath(path!!)) {
|
||||
YuzuApplication.documentsTree!!.getFileSize(path)
|
||||
} else {
|
||||
getFileSize(appContext, path)
|
||||
FileUtil.getFileSize(path)
|
||||
}
|
||||
}
|
||||
|
||||
@Keep
|
||||
@JvmStatic
|
||||
fun exists(path: String?): Boolean {
|
||||
return if (isNativePath(path!!)) {
|
||||
return if (DocumentsTree.isNativePath(path!!)) {
|
||||
YuzuApplication.documentsTree!!.exists(path)
|
||||
} else {
|
||||
exists(appContext, path)
|
||||
FileUtil.exists(path, suppressLog = true)
|
||||
}
|
||||
}
|
||||
|
||||
@Keep
|
||||
@JvmStatic
|
||||
fun isDirectory(path: String?): Boolean {
|
||||
return if (isNativePath(path!!)) {
|
||||
return if (DocumentsTree.isNativePath(path!!)) {
|
||||
YuzuApplication.documentsTree!!.isDirectory(path)
|
||||
} else {
|
||||
isDirectory(appContext, path)
|
||||
FileUtil.isDirectory(path)
|
||||
}
|
||||
}
|
||||
|
||||
@Keep
|
||||
@JvmStatic
|
||||
fun getParentDirectory(path: String): String =
|
||||
if (DocumentsTree.isNativePath(path)) {
|
||||
YuzuApplication.documentsTree!!.getParentDirectory(path)
|
||||
} else {
|
||||
path
|
||||
}
|
||||
|
||||
@Keep
|
||||
@JvmStatic
|
||||
fun getFilename(path: String): String =
|
||||
if (DocumentsTree.isNativePath(path)) {
|
||||
YuzuApplication.documentsTree!!.getFilename(path)
|
||||
} else {
|
||||
FileUtil.getFilename(Uri.parse(path))
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if pro controller isn't available and handheld is
|
||||
*/
|
||||
@@ -219,35 +234,14 @@ object NativeLibrary {
|
||||
|
||||
external fun initGameIni(gameID: String?)
|
||||
|
||||
/**
|
||||
* Gets the embedded icon within the given ROM.
|
||||
*
|
||||
* @param filename the file path to the ROM.
|
||||
* @return a byte array containing the JPEG data for the icon.
|
||||
*/
|
||||
external fun getIcon(filename: String): ByteArray
|
||||
|
||||
/**
|
||||
* Gets the embedded title of the given ISO/ROM.
|
||||
*
|
||||
* @param filename The file path to the ISO/ROM.
|
||||
* @return the embedded title of the ISO/ROM.
|
||||
*/
|
||||
external fun getTitle(filename: String): String
|
||||
|
||||
external fun getDescription(filename: String): String
|
||||
|
||||
external fun getGameId(filename: String): String
|
||||
|
||||
external fun getRegions(filename: String): String
|
||||
|
||||
external fun getCompany(filename: String): String
|
||||
|
||||
external fun isHomebrew(filename: String): Boolean
|
||||
|
||||
external fun setAppDirectory(directory: String)
|
||||
|
||||
external fun installFileToNand(filename: String): Int
|
||||
/**
|
||||
* Installs a nsp or xci file to nand
|
||||
* @param filename String representation of file uri
|
||||
* @param extension Lowercase string representation of file extension without "."
|
||||
*/
|
||||
external fun installFileToNand(filename: String, extension: String): Int
|
||||
|
||||
external fun initializeGpuDriver(
|
||||
hookLibDir: String?,
|
||||
@@ -258,7 +252,7 @@ object NativeLibrary {
|
||||
|
||||
external fun reloadKeys(): Boolean
|
||||
|
||||
external fun initializeEmulation()
|
||||
external fun initializeSystem(reload: Boolean)
|
||||
|
||||
external fun defaultCPUCore(): Int
|
||||
|
||||
@@ -292,11 +286,6 @@ object NativeLibrary {
|
||||
*/
|
||||
external fun stopEmulation()
|
||||
|
||||
/**
|
||||
* Resets the in-memory ROM metadata cache.
|
||||
*/
|
||||
external fun resetRomMetadata()
|
||||
|
||||
/**
|
||||
* Returns true if emulation is running (or is paused).
|
||||
*/
|
||||
@@ -473,12 +462,12 @@ object NativeLibrary {
|
||||
}
|
||||
|
||||
fun setEmulationActivity(emulationActivity: EmulationActivity?) {
|
||||
Log.verbose("[NativeLibrary] Registering EmulationActivity.")
|
||||
Log.debug("[NativeLibrary] Registering EmulationActivity.")
|
||||
sEmulationActivity = WeakReference(emulationActivity)
|
||||
}
|
||||
|
||||
fun clearEmulationActivity() {
|
||||
Log.verbose("[NativeLibrary] Unregistering EmulationActivity.")
|
||||
Log.debug("[NativeLibrary] Unregistering EmulationActivity.")
|
||||
sEmulationActivity.clear()
|
||||
}
|
||||
|
||||
@@ -511,6 +500,41 @@ object NativeLibrary {
|
||||
*/
|
||||
external fun submitInlineKeyboardInput(key_code: Int)
|
||||
|
||||
/**
|
||||
* Creates a generic user directory if it doesn't exist already
|
||||
*/
|
||||
external fun initializeEmptyUserDirectory()
|
||||
|
||||
/**
|
||||
* Gets the launch path for a given applet. It is the caller's responsibility to also
|
||||
* set the system's current applet ID before trying to launch the nca given by this function.
|
||||
*
|
||||
* @param id The applet entry ID
|
||||
* @return The applet's launch path
|
||||
*/
|
||||
external fun getAppletLaunchPath(id: Long): String
|
||||
|
||||
/**
|
||||
* Sets the system's current applet ID before launching.
|
||||
*
|
||||
* @param appletId One of the ids in the Service::AM::Applets::AppletId enum
|
||||
*/
|
||||
external fun setCurrentAppletId(appletId: Int)
|
||||
|
||||
/**
|
||||
* Sets the cabinet mode for launching the cabinet applet.
|
||||
*
|
||||
* @param cabinetMode One of the modes that corresponds to the enum in Service::NFP::CabinetMode
|
||||
*/
|
||||
external fun setCabinetMode(cabinetMode: Int)
|
||||
|
||||
/**
|
||||
* Checks whether NAND contents are available and valid.
|
||||
*
|
||||
* @return 'true' if firmware is available
|
||||
*/
|
||||
external fun isFirmwareAvailable(): Boolean
|
||||
|
||||
/**
|
||||
* Button type for use in onTouchEvent
|
||||
*/
|
||||
|
||||
@@ -11,6 +11,7 @@ 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 org.yuzu.yuzu_emu.utils.Log
|
||||
|
||||
fun Context.getPublicFilesDir(): File = getExternalFilesDir(null) ?: filesDir
|
||||
|
||||
@@ -47,8 +48,9 @@ class YuzuApplication : Application() {
|
||||
application = this
|
||||
documentsTree = DocumentsTree()
|
||||
DirectoryInitialization.start()
|
||||
GpuDriverHelper.initializeDriverParameters(applicationContext)
|
||||
GpuDriverHelper.initializeDriverParameters()
|
||||
NativeLibrary.logDeviceInfo()
|
||||
Log.logDeviceInfo()
|
||||
|
||||
createNotificationChannels()
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
package org.yuzu.yuzu_emu.activities
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Activity
|
||||
import android.app.PendingIntent
|
||||
import android.app.PictureInPictureParams
|
||||
@@ -44,9 +45,9 @@ import org.yuzu.yuzu_emu.features.settings.model.IntSetting
|
||||
import org.yuzu.yuzu_emu.features.settings.model.Settings
|
||||
import org.yuzu.yuzu_emu.model.EmulationViewModel
|
||||
import org.yuzu.yuzu_emu.model.Game
|
||||
import org.yuzu.yuzu_emu.utils.ControllerMappingHelper
|
||||
import org.yuzu.yuzu_emu.utils.ForegroundService
|
||||
import org.yuzu.yuzu_emu.utils.InputHandler
|
||||
import org.yuzu.yuzu_emu.utils.Log
|
||||
import org.yuzu.yuzu_emu.utils.MemoryUtil
|
||||
import org.yuzu.yuzu_emu.utils.NfcReader
|
||||
import org.yuzu.yuzu_emu.utils.ThemeHelper
|
||||
@@ -56,17 +57,16 @@ import kotlin.math.roundToInt
|
||||
class EmulationActivity : AppCompatActivity(), SensorEventListener {
|
||||
private lateinit var binding: ActivityEmulationBinding
|
||||
|
||||
private var controllerMappingHelper: ControllerMappingHelper? = null
|
||||
|
||||
var isActivityRecreated = false
|
||||
private lateinit var nfcReader: NfcReader
|
||||
private lateinit var inputHandler: InputHandler
|
||||
|
||||
private val gyro = FloatArray(3)
|
||||
private val accel = FloatArray(3)
|
||||
private var motionTimestamp: Long = 0
|
||||
private var flipMotionOrientation: Boolean = false
|
||||
|
||||
private var controllerIds = InputHandler.getGameControllerIds()
|
||||
|
||||
private val actionPause = "ACTION_EMULATOR_PAUSE"
|
||||
private val actionPlay = "ACTION_EMULATOR_PLAY"
|
||||
private val actionMute = "ACTION_EMULATOR_MUTE"
|
||||
@@ -81,6 +81,7 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
Log.gameLaunched = true
|
||||
ThemeHelper.setTheme(this)
|
||||
|
||||
super.onCreate(savedInstanceState)
|
||||
@@ -94,8 +95,6 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
|
||||
|
||||
isActivityRecreated = savedInstanceState != null
|
||||
|
||||
controllerMappingHelper = ControllerMappingHelper()
|
||||
|
||||
// Set these options now so that the SurfaceView the game renders into is the right size.
|
||||
enableFullscreenImmersive()
|
||||
|
||||
@@ -104,12 +103,11 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
|
||||
nfcReader = NfcReader(this)
|
||||
nfcReader.initialize()
|
||||
|
||||
inputHandler = InputHandler()
|
||||
inputHandler.initialize()
|
||||
InputHandler.initialize()
|
||||
|
||||
val preferences = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
|
||||
if (!preferences.getBoolean(Settings.PREF_MEMORY_WARNING_SHOWN, false)) {
|
||||
if (MemoryUtil.isLessThan(MemoryUtil.REQUIRED_MEMORY, MemoryUtil.Gb)) {
|
||||
if (MemoryUtil.isLessThan(MemoryUtil.REQUIRED_MEMORY, MemoryUtil.totalMemory)) {
|
||||
Toast.makeText(
|
||||
this,
|
||||
getString(
|
||||
@@ -161,6 +159,7 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
|
||||
super.onResume()
|
||||
nfcReader.startScanning()
|
||||
startMotionSensorListener()
|
||||
InputHandler.updateControllerIds()
|
||||
|
||||
buildPictureInPictureParams()
|
||||
}
|
||||
@@ -194,7 +193,7 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
|
||||
return super.dispatchKeyEvent(event)
|
||||
}
|
||||
|
||||
return inputHandler.dispatchKeyEvent(event)
|
||||
return InputHandler.dispatchKeyEvent(event)
|
||||
}
|
||||
|
||||
override fun dispatchGenericMotionEvent(event: MotionEvent): Boolean {
|
||||
@@ -209,7 +208,7 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
|
||||
return true
|
||||
}
|
||||
|
||||
return inputHandler.dispatchGenericMotionEvent(event)
|
||||
return InputHandler.dispatchGenericMotionEvent(event)
|
||||
}
|
||||
|
||||
override fun onSensorChanged(event: SensorEvent) {
|
||||
@@ -374,8 +373,10 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
|
||||
val pictureInPictureParamsBuilder = PictureInPictureParams.Builder()
|
||||
.getPictureInPictureActionsBuilder().getPictureInPictureAspectBuilder()
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||
val isEmulationActive = emulationViewModel.emulationStarted.value &&
|
||||
!emulationViewModel.isEmulationStopping.value
|
||||
pictureInPictureParamsBuilder.setAutoEnterEnabled(
|
||||
BooleanSetting.PICTURE_IN_PICTURE.boolean
|
||||
BooleanSetting.PICTURE_IN_PICTURE.boolean && isEmulationActive
|
||||
)
|
||||
}
|
||||
setPictureInPictureParams(pictureInPictureParamsBuilder.build())
|
||||
@@ -397,6 +398,7 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("UnspecifiedRegisterReceiverFlag")
|
||||
override fun onPictureInPictureModeChanged(
|
||||
isInPictureInPictureMode: Boolean,
|
||||
newConfig: Configuration
|
||||
@@ -409,7 +411,11 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
|
||||
addAction(actionMute)
|
||||
addAction(actionUnmute)
|
||||
}.also {
|
||||
registerReceiver(pictureInPictureReceiver, it)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
registerReceiver(pictureInPictureReceiver, it, RECEIVER_EXPORTED)
|
||||
} else {
|
||||
registerReceiver(pictureInPictureReceiver, it)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
// 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 android.widget.Toast
|
||||
import androidx.core.content.res.ResourcesCompat
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import androidx.navigation.findNavController
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
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.CardAppletOptionBinding
|
||||
import org.yuzu.yuzu_emu.model.Applet
|
||||
import org.yuzu.yuzu_emu.model.AppletInfo
|
||||
import org.yuzu.yuzu_emu.model.Game
|
||||
|
||||
class AppletAdapter(val activity: FragmentActivity, var applets: List<Applet>) :
|
||||
RecyclerView.Adapter<AppletAdapter.AppletViewHolder>(),
|
||||
View.OnClickListener {
|
||||
|
||||
override fun onCreateViewHolder(
|
||||
parent: ViewGroup,
|
||||
viewType: Int
|
||||
): AppletAdapter.AppletViewHolder {
|
||||
CardAppletOptionBinding.inflate(LayoutInflater.from(parent.context), parent, false)
|
||||
.apply { root.setOnClickListener(this@AppletAdapter) }
|
||||
.also { return AppletViewHolder(it) }
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: AppletViewHolder, position: Int) =
|
||||
holder.bind(applets[position])
|
||||
|
||||
override fun getItemCount(): Int = applets.size
|
||||
|
||||
override fun onClick(view: View) {
|
||||
val applet = (view.tag as AppletViewHolder).applet
|
||||
val appletPath = NativeLibrary.getAppletLaunchPath(applet.appletInfo.entryId)
|
||||
if (appletPath.isEmpty()) {
|
||||
Toast.makeText(
|
||||
YuzuApplication.appContext,
|
||||
R.string.applets_error_applet,
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
return
|
||||
}
|
||||
|
||||
if (applet.appletInfo == AppletInfo.Cabinet) {
|
||||
view.findNavController()
|
||||
.navigate(R.id.action_appletLauncherFragment_to_cabinetLauncherDialogFragment)
|
||||
return
|
||||
}
|
||||
|
||||
NativeLibrary.setCurrentAppletId(applet.appletInfo.appletId)
|
||||
val appletGame = Game(
|
||||
title = YuzuApplication.appContext.getString(applet.titleId),
|
||||
path = appletPath
|
||||
)
|
||||
val action = HomeNavigationDirections.actionGlobalEmulationActivity(appletGame)
|
||||
view.findNavController().navigate(action)
|
||||
}
|
||||
|
||||
inner class AppletViewHolder(val binding: CardAppletOptionBinding) :
|
||||
RecyclerView.ViewHolder(binding.root) {
|
||||
lateinit var applet: Applet
|
||||
|
||||
init {
|
||||
itemView.tag = this
|
||||
}
|
||||
|
||||
fun bind(applet: Applet) {
|
||||
this.applet = applet
|
||||
|
||||
binding.title.setText(applet.titleId)
|
||||
binding.description.setText(applet.descriptionId)
|
||||
binding.icon.setImageDrawable(
|
||||
ResourcesCompat.getDrawable(
|
||||
binding.icon.context.resources,
|
||||
applet.iconId,
|
||||
binding.icon.context.theme
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
// 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.core.content.res.ResourcesCompat
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
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.DialogListItemBinding
|
||||
import org.yuzu.yuzu_emu.model.CabinetMode
|
||||
import org.yuzu.yuzu_emu.adapters.CabinetLauncherDialogAdapter.CabinetModeViewHolder
|
||||
import org.yuzu.yuzu_emu.model.AppletInfo
|
||||
import org.yuzu.yuzu_emu.model.Game
|
||||
|
||||
class CabinetLauncherDialogAdapter(val fragment: Fragment) :
|
||||
RecyclerView.Adapter<CabinetModeViewHolder>(),
|
||||
View.OnClickListener {
|
||||
private val cabinetModes = CabinetMode.values().copyOfRange(1, CabinetMode.values().size)
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CabinetModeViewHolder {
|
||||
DialogListItemBinding.inflate(LayoutInflater.from(parent.context), parent, false)
|
||||
.apply { root.setOnClickListener(this@CabinetLauncherDialogAdapter) }
|
||||
.also { return CabinetModeViewHolder(it) }
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int = cabinetModes.size
|
||||
|
||||
override fun onBindViewHolder(holder: CabinetModeViewHolder, position: Int) =
|
||||
holder.bind(cabinetModes[position])
|
||||
|
||||
override fun onClick(view: View) {
|
||||
val mode = (view.tag as CabinetModeViewHolder).cabinetMode
|
||||
val appletPath = NativeLibrary.getAppletLaunchPath(AppletInfo.Cabinet.entryId)
|
||||
NativeLibrary.setCurrentAppletId(AppletInfo.Cabinet.appletId)
|
||||
NativeLibrary.setCabinetMode(mode.id)
|
||||
val appletGame = Game(
|
||||
title = YuzuApplication.appContext.getString(R.string.cabinet_applet),
|
||||
path = appletPath
|
||||
)
|
||||
val action = HomeNavigationDirections.actionGlobalEmulationActivity(appletGame)
|
||||
fragment.findNavController().navigate(action)
|
||||
}
|
||||
|
||||
inner class CabinetModeViewHolder(val binding: DialogListItemBinding) :
|
||||
RecyclerView.ViewHolder(binding.root) {
|
||||
lateinit var cabinetMode: CabinetMode
|
||||
|
||||
init {
|
||||
itemView.tag = this
|
||||
}
|
||||
|
||||
fun bind(cabinetMode: CabinetMode) {
|
||||
this.cabinetMode = cabinetMode
|
||||
binding.icon.setImageDrawable(
|
||||
ResourcesCompat.getDrawable(
|
||||
binding.icon.context.resources,
|
||||
cabinetMode.iconId,
|
||||
binding.icon.context.theme
|
||||
)
|
||||
)
|
||||
binding.title.setText(cabinetMode.titleId)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
package org.yuzu.yuzu_emu.adapters
|
||||
|
||||
import android.text.TextUtils
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.AsyncDifferConfig
|
||||
import androidx.recyclerview.widget.DiffUtil
|
||||
import androidx.recyclerview.widget.ListAdapter
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import org.yuzu.yuzu_emu.R
|
||||
import org.yuzu.yuzu_emu.databinding.CardDriverOptionBinding
|
||||
import org.yuzu.yuzu_emu.model.DriverViewModel
|
||||
import org.yuzu.yuzu_emu.utils.GpuDriverHelper
|
||||
import org.yuzu.yuzu_emu.utils.GpuDriverMetadata
|
||||
|
||||
class DriverAdapter(private val driverViewModel: DriverViewModel) :
|
||||
ListAdapter<Pair<String, GpuDriverMetadata>, DriverAdapter.DriverViewHolder>(
|
||||
AsyncDifferConfig.Builder(DiffCallback()).build()
|
||||
) {
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): DriverViewHolder {
|
||||
val binding =
|
||||
CardDriverOptionBinding.inflate(LayoutInflater.from(parent.context), parent, false)
|
||||
return DriverViewHolder(binding)
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int = currentList.size
|
||||
|
||||
override fun onBindViewHolder(holder: DriverViewHolder, position: Int) =
|
||||
holder.bind(currentList[position])
|
||||
|
||||
private fun onSelectDriver(position: Int) {
|
||||
driverViewModel.setSelectedDriverIndex(position)
|
||||
notifyItemChanged(driverViewModel.previouslySelectedDriver)
|
||||
notifyItemChanged(driverViewModel.selectedDriver)
|
||||
}
|
||||
|
||||
private fun onDeleteDriver(driverData: Pair<String, GpuDriverMetadata>, position: Int) {
|
||||
if (driverViewModel.selectedDriver > position) {
|
||||
driverViewModel.setSelectedDriverIndex(driverViewModel.selectedDriver - 1)
|
||||
}
|
||||
if (GpuDriverHelper.customDriverData == driverData.second) {
|
||||
driverViewModel.setSelectedDriverIndex(0)
|
||||
}
|
||||
driverViewModel.driversToDelete.add(driverData.first)
|
||||
driverViewModel.removeDriver(driverData)
|
||||
notifyItemRemoved(position)
|
||||
notifyItemChanged(driverViewModel.selectedDriver)
|
||||
}
|
||||
|
||||
inner class DriverViewHolder(val binding: CardDriverOptionBinding) :
|
||||
RecyclerView.ViewHolder(binding.root) {
|
||||
private lateinit var driverData: Pair<String, GpuDriverMetadata>
|
||||
|
||||
fun bind(driverData: Pair<String, GpuDriverMetadata>) {
|
||||
this.driverData = driverData
|
||||
val driver = driverData.second
|
||||
|
||||
binding.apply {
|
||||
radioButton.isChecked = driverViewModel.selectedDriver == bindingAdapterPosition
|
||||
root.setOnClickListener {
|
||||
onSelectDriver(bindingAdapterPosition)
|
||||
}
|
||||
buttonDelete.setOnClickListener {
|
||||
onDeleteDriver(driverData, bindingAdapterPosition)
|
||||
}
|
||||
|
||||
// Delay marquee by 3s
|
||||
title.postDelayed(
|
||||
{
|
||||
title.isSelected = true
|
||||
title.ellipsize = TextUtils.TruncateAt.MARQUEE
|
||||
version.isSelected = true
|
||||
version.ellipsize = TextUtils.TruncateAt.MARQUEE
|
||||
description.isSelected = true
|
||||
description.ellipsize = TextUtils.TruncateAt.MARQUEE
|
||||
},
|
||||
3000
|
||||
)
|
||||
if (driver.name == null) {
|
||||
title.setText(R.string.system_gpu_driver)
|
||||
description.text = ""
|
||||
version.text = ""
|
||||
version.visibility = View.GONE
|
||||
description.visibility = View.GONE
|
||||
buttonDelete.visibility = View.GONE
|
||||
} else {
|
||||
title.text = driver.name
|
||||
version.text = driver.version
|
||||
description.text = driver.description
|
||||
version.visibility = View.VISIBLE
|
||||
description.visibility = View.VISIBLE
|
||||
buttonDelete.visibility = View.VISIBLE
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class DiffCallback : DiffUtil.ItemCallback<Pair<String, GpuDriverMetadata>>() {
|
||||
override fun areItemsTheSame(
|
||||
oldItem: Pair<String, GpuDriverMetadata>,
|
||||
newItem: Pair<String, GpuDriverMetadata>
|
||||
): Boolean {
|
||||
return oldItem.first == newItem.first
|
||||
}
|
||||
|
||||
override fun areContentsTheSame(
|
||||
oldItem: Pair<String, GpuDriverMetadata>,
|
||||
newItem: Pair<String, GpuDriverMetadata>
|
||||
): Boolean {
|
||||
return oldItem.second == newItem.second
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -22,12 +22,16 @@ import androidx.core.graphics.drawable.toBitmap
|
||||
import androidx.core.graphics.drawable.toDrawable
|
||||
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
|
||||
import androidx.recyclerview.widget.ListAdapter
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.yuzu.yuzu_emu.HomeNavigationDirections
|
||||
import org.yuzu.yuzu_emu.R
|
||||
import org.yuzu.yuzu_emu.YuzuApplication
|
||||
@@ -92,28 +96,34 @@ class GameAdapter(private val activity: AppCompatActivity) :
|
||||
data = Uri.parse(holder.game.path)
|
||||
}
|
||||
|
||||
val layerDrawable = ResourcesCompat.getDrawable(
|
||||
YuzuApplication.appContext.resources,
|
||||
R.drawable.shortcut,
|
||||
null
|
||||
) as LayerDrawable
|
||||
layerDrawable.setDrawableByLayerId(
|
||||
R.id.shortcut_foreground,
|
||||
GameIconUtils.getGameIcon(holder.game).toDrawable(YuzuApplication.appContext.resources)
|
||||
)
|
||||
val inset = YuzuApplication.appContext.resources
|
||||
.getDimensionPixelSize(R.dimen.icon_inset)
|
||||
layerDrawable.setLayerInset(1, inset, inset, inset, inset)
|
||||
val shortcut = ShortcutInfoCompat.Builder(YuzuApplication.appContext, holder.game.path)
|
||||
.setShortLabel(holder.game.title)
|
||||
.setIcon(
|
||||
IconCompat.createWithAdaptiveBitmap(
|
||||
layerDrawable.toBitmap(config = Bitmap.Config.ARGB_8888)
|
||||
activity.lifecycleScope.launch {
|
||||
withContext(Dispatchers.IO) {
|
||||
val layerDrawable = ResourcesCompat.getDrawable(
|
||||
YuzuApplication.appContext.resources,
|
||||
R.drawable.shortcut,
|
||||
null
|
||||
) as LayerDrawable
|
||||
layerDrawable.setDrawableByLayerId(
|
||||
R.id.shortcut_foreground,
|
||||
GameIconUtils.getGameIcon(activity, holder.game)
|
||||
.toDrawable(YuzuApplication.appContext.resources)
|
||||
)
|
||||
)
|
||||
.setIntent(openIntent)
|
||||
.build()
|
||||
ShortcutManagerCompat.pushDynamicShortcut(YuzuApplication.appContext, shortcut)
|
||||
val inset = YuzuApplication.appContext.resources
|
||||
.getDimensionPixelSize(R.dimen.icon_inset)
|
||||
layerDrawable.setLayerInset(1, inset, inset, inset, inset)
|
||||
val shortcut =
|
||||
ShortcutInfoCompat.Builder(YuzuApplication.appContext, holder.game.path)
|
||||
.setShortLabel(holder.game.title)
|
||||
.setIcon(
|
||||
IconCompat.createWithAdaptiveBitmap(
|
||||
layerDrawable.toBitmap(config = Bitmap.Config.ARGB_8888)
|
||||
)
|
||||
)
|
||||
.setIntent(openIntent)
|
||||
.build()
|
||||
ShortcutManagerCompat.pushDynamicShortcut(YuzuApplication.appContext, shortcut)
|
||||
}
|
||||
}
|
||||
|
||||
val action = HomeNavigationDirections.actionGlobalEmulationActivity(holder.game)
|
||||
view.findNavController().navigate(action)
|
||||
@@ -147,7 +157,7 @@ class GameAdapter(private val activity: AppCompatActivity) :
|
||||
|
||||
private class DiffCallback : DiffUtil.ItemCallback<Game>() {
|
||||
override fun areItemsTheSame(oldItem: Game, newItem: Game): Boolean {
|
||||
return oldItem.gameId == newItem.gameId
|
||||
return oldItem.programId == newItem.programId
|
||||
}
|
||||
|
||||
override fun areContentsTheSame(oldItem: Game, newItem: Game): Boolean {
|
||||
|
||||
@@ -49,6 +49,7 @@ class HomeSettingAdapter(
|
||||
holder.option.onClick.invoke()
|
||||
} else {
|
||||
MessageDialogFragment.newInstance(
|
||||
activity,
|
||||
titleId = holder.option.disabledTitleId,
|
||||
descriptionId = holder.option.disabledMessageId
|
||||
).show(activity.supportFragmentManager, MessageDialogFragment.TAG)
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
// 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.recyclerview.widget.RecyclerView
|
||||
import org.yuzu.yuzu_emu.databinding.CardInstallableBinding
|
||||
import org.yuzu.yuzu_emu.model.Installable
|
||||
|
||||
class InstallableAdapter(private val installables: List<Installable>) :
|
||||
RecyclerView.Adapter<InstallableAdapter.InstallableViewHolder>() {
|
||||
override fun onCreateViewHolder(
|
||||
parent: ViewGroup,
|
||||
viewType: Int
|
||||
): InstallableAdapter.InstallableViewHolder {
|
||||
val binding =
|
||||
CardInstallableBinding.inflate(LayoutInflater.from(parent.context), parent, false)
|
||||
return InstallableViewHolder(binding)
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int = installables.size
|
||||
|
||||
override fun onBindViewHolder(holder: InstallableAdapter.InstallableViewHolder, position: Int) =
|
||||
holder.bind(installables[position])
|
||||
|
||||
inner class InstallableViewHolder(val binding: CardInstallableBinding) :
|
||||
RecyclerView.ViewHolder(binding.root) {
|
||||
lateinit var installable: Installable
|
||||
|
||||
fun bind(installable: Installable) {
|
||||
this.installable = installable
|
||||
|
||||
binding.title.setText(installable.titleId)
|
||||
binding.description.setText(installable.descriptionId)
|
||||
|
||||
if (installable.install != null) {
|
||||
binding.buttonInstall.visibility = View.VISIBLE
|
||||
binding.buttonInstall.setOnClickListener { installable.install.invoke() }
|
||||
}
|
||||
if (installable.export != null) {
|
||||
binding.buttonExport.visibility = View.VISIBLE
|
||||
binding.buttonExport.setOnClickListener { installable.export.invoke() }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -82,7 +82,6 @@ object Settings {
|
||||
|
||||
enum class MenuTag(val titleId: Int) {
|
||||
SECTION_ROOT(R.string.advanced_settings),
|
||||
SECTION_GENERAL(R.string.preferences_general),
|
||||
SECTION_SYSTEM(R.string.preferences_system),
|
||||
SECTION_RENDERER(R.string.preferences_graphics),
|
||||
SECTION_AUDIO(R.string.preferences_audio),
|
||||
|
||||
+3
@@ -3,10 +3,13 @@
|
||||
|
||||
package org.yuzu.yuzu_emu.features.settings.model.view
|
||||
|
||||
import androidx.annotation.DrawableRes
|
||||
|
||||
class RunnableSetting(
|
||||
titleId: Int,
|
||||
descriptionId: Int,
|
||||
val isRuntimeRunnable: Boolean,
|
||||
@DrawableRes val iconId: Int = 0,
|
||||
val runnable: () -> Unit
|
||||
) : SettingsItem(emptySetting, titleId, descriptionId) {
|
||||
override val type = TYPE_RUNNABLE
|
||||
|
||||
+5
-2
@@ -3,11 +3,14 @@
|
||||
|
||||
package org.yuzu.yuzu_emu.features.settings.model.view
|
||||
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.annotation.StringRes
|
||||
import org.yuzu.yuzu_emu.features.settings.model.Settings
|
||||
|
||||
class SubmenuSetting(
|
||||
titleId: Int,
|
||||
descriptionId: Int,
|
||||
@StringRes titleId: Int,
|
||||
@StringRes descriptionId: Int,
|
||||
@DrawableRes val iconId: Int,
|
||||
val menuKey: Settings.MenuTag
|
||||
) : SettingsItem(emptySetting, titleId, descriptionId) {
|
||||
override val type = TYPE_SUBMENU
|
||||
|
||||
+8
-5
@@ -21,6 +21,7 @@ import androidx.navigation.navArgs
|
||||
import com.google.android.material.color.MaterialColors
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.launch
|
||||
import org.yuzu.yuzu_emu.NativeLibrary
|
||||
import java.io.IOException
|
||||
import org.yuzu.yuzu_emu.R
|
||||
import org.yuzu.yuzu_emu.databinding.ActivitySettingsBinding
|
||||
@@ -168,7 +169,7 @@ class SettingsActivity : AppCompatActivity() {
|
||||
if (!settingsFile.delete()) {
|
||||
throw IOException("Failed to delete $settingsFile")
|
||||
}
|
||||
Settings.settingsList.forEach { it.reset() }
|
||||
NativeLibrary.reloadSettings()
|
||||
|
||||
Toast.makeText(
|
||||
applicationContext,
|
||||
@@ -181,12 +182,14 @@ class SettingsActivity : AppCompatActivity() {
|
||||
private fun setInsets() {
|
||||
ViewCompat.setOnApplyWindowInsetsListener(
|
||||
binding.navigationBarShade
|
||||
) { view: View, windowInsets: WindowInsetsCompat ->
|
||||
) { _: View, windowInsets: WindowInsetsCompat ->
|
||||
val barInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
|
||||
|
||||
val mlpShade = view.layoutParams as MarginLayoutParams
|
||||
mlpShade.height = barInsets.bottom
|
||||
view.layoutParams = mlpShade
|
||||
// The only situation where we care to have a nav bar shade is when it's at the bottom
|
||||
// of the screen where scrolling list elements can go behind it.
|
||||
val mlpNavShade = binding.navigationBarShade.layoutParams as MarginLayoutParams
|
||||
mlpNavShade.height = barInsets.bottom
|
||||
binding.navigationBarShade.layoutParams = mlpNavShade
|
||||
|
||||
windowInsets
|
||||
}
|
||||
|
||||
+2
-28
@@ -20,7 +20,6 @@ import androidx.lifecycle.repeatOnLifecycle
|
||||
import androidx.navigation.findNavController
|
||||
import androidx.navigation.fragment.navArgs
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.google.android.material.divider.MaterialDividerItemDecoration
|
||||
import com.google.android.material.transition.MaterialSharedAxis
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.launch
|
||||
@@ -68,15 +67,9 @@ class SettingsFragment : Fragment() {
|
||||
)
|
||||
|
||||
binding.toolbarSettingsLayout.title = getString(args.menuTag.titleId)
|
||||
val dividerDecoration = MaterialDividerItemDecoration(
|
||||
requireContext(),
|
||||
LinearLayoutManager.VERTICAL
|
||||
)
|
||||
dividerDecoration.isLastItemDecorated = false
|
||||
binding.listSettings.apply {
|
||||
adapter = settingsAdapter
|
||||
layoutManager = LinearLayoutManager(requireContext())
|
||||
addItemDecoration(dividerDecoration)
|
||||
}
|
||||
|
||||
binding.toolbarSettings.setNavigationOnClickListener {
|
||||
@@ -94,17 +87,6 @@ class SettingsFragment : Fragment() {
|
||||
}
|
||||
}
|
||||
}
|
||||
launch {
|
||||
settingsViewModel.isUsingSearch.collectLatest {
|
||||
if (it) {
|
||||
reenterTransition = MaterialSharedAxis(MaterialSharedAxis.Z, true)
|
||||
exitTransition = MaterialSharedAxis(MaterialSharedAxis.Z, false)
|
||||
} else {
|
||||
reenterTransition = MaterialSharedAxis(MaterialSharedAxis.X, false)
|
||||
exitTransition = MaterialSharedAxis(MaterialSharedAxis.X, true)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (args.menuTag == Settings.MenuTag.SECTION_ROOT) {
|
||||
@@ -112,8 +94,6 @@ class SettingsFragment : Fragment() {
|
||||
binding.toolbarSettings.setOnMenuItemClickListener {
|
||||
when (it.itemId) {
|
||||
R.id.action_search -> {
|
||||
reenterTransition = MaterialSharedAxis(MaterialSharedAxis.Z, true)
|
||||
exitTransition = MaterialSharedAxis(MaterialSharedAxis.Z, false)
|
||||
view.findNavController()
|
||||
.navigate(R.id.action_settingsFragment_to_settingsSearchFragment)
|
||||
true
|
||||
@@ -129,11 +109,6 @@ class SettingsFragment : Fragment() {
|
||||
setInsets()
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
settingsViewModel.setIsUsingSearch(false)
|
||||
}
|
||||
|
||||
private fun setInsets() {
|
||||
ViewCompat.setOnApplyWindowInsetsListener(
|
||||
binding.root
|
||||
@@ -144,10 +119,9 @@ class SettingsFragment : Fragment() {
|
||||
val leftInsets = barInsets.left + cutoutInsets.left
|
||||
val rightInsets = barInsets.right + cutoutInsets.right
|
||||
|
||||
val sideMargin = resources.getDimensionPixelSize(R.dimen.spacing_medlarge)
|
||||
val mlpSettingsList = binding.listSettings.layoutParams as MarginLayoutParams
|
||||
mlpSettingsList.leftMargin = sideMargin + leftInsets
|
||||
mlpSettingsList.rightMargin = sideMargin + rightInsets
|
||||
mlpSettingsList.leftMargin = leftInsets
|
||||
mlpSettingsList.rightMargin = rightInsets
|
||||
binding.listSettings.layoutParams = mlpSettingsList
|
||||
binding.listSettings.updatePadding(
|
||||
bottom = barInsets.bottom
|
||||
|
||||
+42
-21
@@ -3,7 +3,6 @@
|
||||
|
||||
package org.yuzu.yuzu_emu.features.settings.ui
|
||||
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import android.os.Build
|
||||
import android.widget.Toast
|
||||
@@ -32,8 +31,6 @@ class SettingsFragmentPresenter(
|
||||
private val preferences: SharedPreferences
|
||||
get() = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
|
||||
|
||||
private val context: Context get() = YuzuApplication.appContext
|
||||
|
||||
// Extension for populating settings list based on paired settings
|
||||
fun ArrayList<SettingsItem>.add(key: String) {
|
||||
val item = SettingsItem.settingsItems[key]!!
|
||||
@@ -53,7 +50,6 @@ class SettingsFragmentPresenter(
|
||||
val sl = ArrayList<SettingsItem>()
|
||||
when (menuTag) {
|
||||
Settings.MenuTag.SECTION_ROOT -> addConfigSettings(sl)
|
||||
Settings.MenuTag.SECTION_GENERAL -> addGeneralSettings(sl)
|
||||
Settings.MenuTag.SECTION_SYSTEM -> addSystemSettings(sl)
|
||||
Settings.MenuTag.SECTION_RENDERER -> addGraphicsSettings(sl)
|
||||
Settings.MenuTag.SECTION_AUDIO -> addAudioSettings(sl)
|
||||
@@ -75,30 +71,53 @@ class SettingsFragmentPresenter(
|
||||
|
||||
private fun addConfigSettings(sl: ArrayList<SettingsItem>) {
|
||||
sl.apply {
|
||||
add(SubmenuSetting(R.string.preferences_general, 0, Settings.MenuTag.SECTION_GENERAL))
|
||||
add(SubmenuSetting(R.string.preferences_system, 0, Settings.MenuTag.SECTION_SYSTEM))
|
||||
add(SubmenuSetting(R.string.preferences_graphics, 0, Settings.MenuTag.SECTION_RENDERER))
|
||||
add(SubmenuSetting(R.string.preferences_audio, 0, Settings.MenuTag.SECTION_AUDIO))
|
||||
add(SubmenuSetting(R.string.preferences_debug, 0, Settings.MenuTag.SECTION_DEBUG))
|
||||
add(
|
||||
RunnableSetting(R.string.reset_to_default, 0, false) {
|
||||
settingsViewModel.setShouldShowResetSettingsDialog(true)
|
||||
}
|
||||
SubmenuSetting(
|
||||
R.string.preferences_system,
|
||||
R.string.preferences_system_description,
|
||||
R.drawable.ic_system_settings,
|
||||
Settings.MenuTag.SECTION_SYSTEM
|
||||
)
|
||||
)
|
||||
add(
|
||||
SubmenuSetting(
|
||||
R.string.preferences_graphics,
|
||||
R.string.preferences_graphics_description,
|
||||
R.drawable.ic_graphics,
|
||||
Settings.MenuTag.SECTION_RENDERER
|
||||
)
|
||||
)
|
||||
add(
|
||||
SubmenuSetting(
|
||||
R.string.preferences_audio,
|
||||
R.string.preferences_audio_description,
|
||||
R.drawable.ic_audio,
|
||||
Settings.MenuTag.SECTION_AUDIO
|
||||
)
|
||||
)
|
||||
add(
|
||||
SubmenuSetting(
|
||||
R.string.preferences_debug,
|
||||
R.string.preferences_debug_description,
|
||||
R.drawable.ic_code,
|
||||
Settings.MenuTag.SECTION_DEBUG
|
||||
)
|
||||
)
|
||||
add(
|
||||
RunnableSetting(
|
||||
R.string.reset_to_default,
|
||||
R.string.reset_to_default_description,
|
||||
false,
|
||||
R.drawable.ic_restore
|
||||
) { settingsViewModel.setShouldShowResetSettingsDialog(true) }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun addGeneralSettings(sl: ArrayList<SettingsItem>) {
|
||||
sl.apply {
|
||||
add(BooleanSetting.RENDERER_USE_SPEED_LIMIT.key)
|
||||
add(ShortSetting.RENDERER_SPEED_LIMIT.key)
|
||||
add(IntSetting.CPU_ACCURACY.key)
|
||||
add(BooleanSetting.PICTURE_IN_PICTURE.key)
|
||||
}
|
||||
}
|
||||
|
||||
private fun addSystemSettings(sl: ArrayList<SettingsItem>) {
|
||||
sl.apply {
|
||||
add(BooleanSetting.RENDERER_USE_SPEED_LIMIT.key)
|
||||
add(ShortSetting.RENDERER_SPEED_LIMIT.key)
|
||||
add(BooleanSetting.USE_DOCKED_MODE.key)
|
||||
add(IntSetting.REGION_INDEX.key)
|
||||
add(IntSetting.LANGUAGE_INDEX.key)
|
||||
@@ -116,6 +135,7 @@ class SettingsFragmentPresenter(
|
||||
add(IntSetting.RENDERER_ANTI_ALIASING.key)
|
||||
add(IntSetting.RENDERER_SCREEN_LAYOUT.key)
|
||||
add(IntSetting.RENDERER_ASPECT_RATIO.key)
|
||||
add(BooleanSetting.PICTURE_IN_PICTURE.key)
|
||||
add(BooleanSetting.RENDERER_USE_DISK_SHADER_CACHE.key)
|
||||
add(BooleanSetting.RENDERER_FORCE_MAX_CLOCK.key)
|
||||
add(BooleanSetting.RENDERER_ASYNCHRONOUS_SHADERS.key)
|
||||
@@ -249,6 +269,7 @@ class SettingsFragmentPresenter(
|
||||
add(BooleanSetting.RENDERER_DEBUG.key)
|
||||
|
||||
add(HeaderSetting(R.string.cpu))
|
||||
add(IntSetting.CPU_ACCURACY.key)
|
||||
add(BooleanSetting.CPU_DEBUG_MODE.key)
|
||||
add(SettingsItem.FASTMEM_COMBINED)
|
||||
}
|
||||
|
||||
+14
@@ -4,6 +4,7 @@
|
||||
package org.yuzu.yuzu_emu.features.settings.ui.viewholder
|
||||
|
||||
import android.view.View
|
||||
import androidx.core.content.res.ResourcesCompat
|
||||
import org.yuzu.yuzu_emu.NativeLibrary
|
||||
import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding
|
||||
import org.yuzu.yuzu_emu.features.settings.model.view.RunnableSetting
|
||||
@@ -16,6 +17,19 @@ class RunnableViewHolder(val binding: ListItemSettingBinding, adapter: SettingsA
|
||||
|
||||
override fun bind(item: SettingsItem) {
|
||||
setting = item as RunnableSetting
|
||||
if (item.iconId != 0) {
|
||||
binding.icon.visibility = View.VISIBLE
|
||||
binding.icon.setImageDrawable(
|
||||
ResourcesCompat.getDrawable(
|
||||
binding.icon.resources,
|
||||
item.iconId,
|
||||
binding.icon.context.theme
|
||||
)
|
||||
)
|
||||
} else {
|
||||
binding.icon.visibility = View.GONE
|
||||
}
|
||||
|
||||
binding.textSettingName.setText(item.nameId)
|
||||
if (item.descriptionId != 0) {
|
||||
binding.textSettingDescription.setText(item.descriptionId)
|
||||
|
||||
+14
@@ -4,6 +4,7 @@
|
||||
package org.yuzu.yuzu_emu.features.settings.ui.viewholder
|
||||
|
||||
import android.view.View
|
||||
import androidx.core.content.res.ResourcesCompat
|
||||
import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding
|
||||
import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
|
||||
import org.yuzu.yuzu_emu.features.settings.model.view.SubmenuSetting
|
||||
@@ -15,6 +16,19 @@ class SubmenuViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAd
|
||||
|
||||
override fun bind(item: SettingsItem) {
|
||||
this.item = item as SubmenuSetting
|
||||
if (item.iconId != 0) {
|
||||
binding.icon.visibility = View.VISIBLE
|
||||
binding.icon.setImageDrawable(
|
||||
ResourcesCompat.getDrawable(
|
||||
binding.icon.resources,
|
||||
item.iconId,
|
||||
binding.icon.context.theme
|
||||
)
|
||||
)
|
||||
} else {
|
||||
binding.icon.visibility = View.GONE
|
||||
}
|
||||
|
||||
binding.textSettingName.setText(item.nameId)
|
||||
if (item.descriptionId != 0) {
|
||||
binding.textSettingDescription.setText(item.descriptionId)
|
||||
|
||||
@@ -114,10 +114,10 @@ class AboutFragment : Fragment() {
|
||||
val leftInsets = barInsets.left + cutoutInsets.left
|
||||
val rightInsets = barInsets.right + cutoutInsets.right
|
||||
|
||||
val mlpAppBar = binding.appbarAbout.layoutParams as MarginLayoutParams
|
||||
mlpAppBar.leftMargin = leftInsets
|
||||
mlpAppBar.rightMargin = rightInsets
|
||||
binding.appbarAbout.layoutParams = mlpAppBar
|
||||
val mlpToolbar = binding.toolbarAbout.layoutParams as MarginLayoutParams
|
||||
mlpToolbar.leftMargin = leftInsets
|
||||
mlpToolbar.rightMargin = rightInsets
|
||||
binding.toolbarAbout.layoutParams = mlpToolbar
|
||||
|
||||
val mlpScrollAbout = binding.scrollAbout.layoutParams as MarginLayoutParams
|
||||
mlpScrollAbout.leftMargin = leftInsets
|
||||
|
||||
@@ -0,0 +1,113 @@
|
||||
// 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 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.GridLayoutManager
|
||||
import com.google.android.material.transition.MaterialSharedAxis
|
||||
import org.yuzu.yuzu_emu.R
|
||||
import org.yuzu.yuzu_emu.adapters.AppletAdapter
|
||||
import org.yuzu.yuzu_emu.databinding.FragmentAppletLauncherBinding
|
||||
import org.yuzu.yuzu_emu.model.Applet
|
||||
import org.yuzu.yuzu_emu.model.AppletInfo
|
||||
import org.yuzu.yuzu_emu.model.HomeViewModel
|
||||
|
||||
class AppletLauncherFragment : Fragment() {
|
||||
private var _binding: FragmentAppletLauncherBinding? = 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)
|
||||
reenterTransition = MaterialSharedAxis(MaterialSharedAxis.X, false)
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View {
|
||||
_binding = FragmentAppletLauncherBinding.inflate(inflater)
|
||||
return binding.root
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
homeViewModel.setNavigationVisibility(visible = false, animated = true)
|
||||
homeViewModel.setStatusBarShadeVisibility(visible = false)
|
||||
|
||||
binding.toolbarApplets.setNavigationOnClickListener {
|
||||
binding.root.findNavController().popBackStack()
|
||||
}
|
||||
|
||||
val applets = listOf(
|
||||
Applet(
|
||||
R.string.album_applet,
|
||||
R.string.album_applet_description,
|
||||
R.drawable.ic_album,
|
||||
AppletInfo.PhotoViewer
|
||||
),
|
||||
Applet(
|
||||
R.string.cabinet_applet,
|
||||
R.string.cabinet_applet_description,
|
||||
R.drawable.ic_nfc,
|
||||
AppletInfo.Cabinet
|
||||
),
|
||||
Applet(
|
||||
R.string.mii_edit_applet,
|
||||
R.string.mii_edit_applet_description,
|
||||
R.drawable.ic_mii,
|
||||
AppletInfo.MiiEdit
|
||||
)
|
||||
)
|
||||
|
||||
binding.listApplets.apply {
|
||||
layoutManager = GridLayoutManager(
|
||||
requireContext(),
|
||||
resources.getInteger(R.integer.grid_columns)
|
||||
)
|
||||
adapter = AppletAdapter(requireActivity(), applets)
|
||||
}
|
||||
|
||||
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.toolbarApplets.layoutParams as ViewGroup.MarginLayoutParams
|
||||
mlpAppBar.leftMargin = leftInsets
|
||||
mlpAppBar.rightMargin = rightInsets
|
||||
binding.toolbarApplets.layoutParams = mlpAppBar
|
||||
|
||||
val mlpListApplets =
|
||||
binding.listApplets.layoutParams as ViewGroup.MarginLayoutParams
|
||||
mlpListApplets.leftMargin = leftInsets
|
||||
mlpListApplets.rightMargin = rightInsets
|
||||
binding.listApplets.layoutParams = mlpListApplets
|
||||
|
||||
binding.listApplets.updatePadding(bottom = barInsets.bottom)
|
||||
|
||||
windowInsets
|
||||
}
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
// 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.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import org.yuzu.yuzu_emu.R
|
||||
import org.yuzu.yuzu_emu.adapters.CabinetLauncherDialogAdapter
|
||||
import org.yuzu.yuzu_emu.databinding.DialogListBinding
|
||||
|
||||
class CabinetLauncherDialogFragment : DialogFragment() {
|
||||
private lateinit var binding: DialogListBinding
|
||||
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||
binding = DialogListBinding.inflate(layoutInflater)
|
||||
binding.dialogList.apply {
|
||||
layoutManager = LinearLayoutManager(requireContext())
|
||||
adapter = CabinetLauncherDialogAdapter(this@CabinetLauncherDialogFragment)
|
||||
}
|
||||
|
||||
return MaterialAlertDialogBuilder(requireContext())
|
||||
.setTitle(R.string.cabinet_launcher)
|
||||
.setView(binding.root)
|
||||
.create()
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View {
|
||||
return binding.root
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,186 @@
|
||||
// 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 androidx.activity.result.contract.ActivityResultContracts
|
||||
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.lifecycle.lifecycleScope
|
||||
import androidx.navigation.findNavController
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import com.google.android.material.transition.MaterialSharedAxis
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.launch
|
||||
import org.yuzu.yuzu_emu.R
|
||||
import org.yuzu.yuzu_emu.adapters.DriverAdapter
|
||||
import org.yuzu.yuzu_emu.databinding.FragmentDriverManagerBinding
|
||||
import org.yuzu.yuzu_emu.model.DriverViewModel
|
||||
import org.yuzu.yuzu_emu.model.HomeViewModel
|
||||
import org.yuzu.yuzu_emu.utils.FileUtil
|
||||
import org.yuzu.yuzu_emu.utils.GpuDriverHelper
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
|
||||
class DriverManagerFragment : Fragment() {
|
||||
private var _binding: FragmentDriverManagerBinding? = null
|
||||
private val binding get() = _binding!!
|
||||
|
||||
private val homeViewModel: HomeViewModel by activityViewModels()
|
||||
private val driverViewModel: DriverViewModel by activityViewModels()
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
enterTransition = MaterialSharedAxis(MaterialSharedAxis.X, true)
|
||||
returnTransition = MaterialSharedAxis(MaterialSharedAxis.X, false)
|
||||
reenterTransition = MaterialSharedAxis(MaterialSharedAxis.X, false)
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View {
|
||||
_binding = FragmentDriverManagerBinding.inflate(inflater)
|
||||
return binding.root
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
homeViewModel.setNavigationVisibility(visible = false, animated = true)
|
||||
homeViewModel.setStatusBarShadeVisibility(visible = false)
|
||||
|
||||
if (!driverViewModel.isInteractionAllowed) {
|
||||
DriversLoadingDialogFragment().show(
|
||||
childFragmentManager,
|
||||
DriversLoadingDialogFragment.TAG
|
||||
)
|
||||
}
|
||||
|
||||
binding.toolbarDrivers.setNavigationOnClickListener {
|
||||
binding.root.findNavController().popBackStack()
|
||||
}
|
||||
|
||||
binding.buttonInstall.setOnClickListener {
|
||||
getDriver.launch(arrayOf("application/zip"))
|
||||
}
|
||||
|
||||
binding.listDrivers.apply {
|
||||
layoutManager = GridLayoutManager(
|
||||
requireContext(),
|
||||
resources.getInteger(R.integer.grid_columns)
|
||||
)
|
||||
adapter = DriverAdapter(driverViewModel)
|
||||
}
|
||||
|
||||
viewLifecycleOwner.lifecycleScope.apply {
|
||||
launch {
|
||||
driverViewModel.driverList.collectLatest {
|
||||
(binding.listDrivers.adapter as DriverAdapter).submitList(it)
|
||||
}
|
||||
}
|
||||
launch {
|
||||
driverViewModel.newDriverInstalled.collect {
|
||||
if (_binding != null && it) {
|
||||
(binding.listDrivers.adapter as DriverAdapter).apply {
|
||||
notifyItemChanged(driverViewModel.previouslySelectedDriver)
|
||||
notifyItemChanged(driverViewModel.selectedDriver)
|
||||
driverViewModel.setNewDriverInstalled(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setInsets()
|
||||
}
|
||||
|
||||
// Start installing requested driver
|
||||
override fun onStop() {
|
||||
super.onStop()
|
||||
driverViewModel.onCloseDriverManager()
|
||||
}
|
||||
|
||||
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.toolbarDrivers.layoutParams as ViewGroup.MarginLayoutParams
|
||||
mlpAppBar.leftMargin = leftInsets
|
||||
mlpAppBar.rightMargin = rightInsets
|
||||
binding.toolbarDrivers.layoutParams = mlpAppBar
|
||||
|
||||
val mlplistDrivers = binding.listDrivers.layoutParams as ViewGroup.MarginLayoutParams
|
||||
mlplistDrivers.leftMargin = leftInsets
|
||||
mlplistDrivers.rightMargin = rightInsets
|
||||
binding.listDrivers.layoutParams = mlplistDrivers
|
||||
|
||||
val fabSpacing = resources.getDimensionPixelSize(R.dimen.spacing_fab)
|
||||
val mlpFab =
|
||||
binding.buttonInstall.layoutParams as ViewGroup.MarginLayoutParams
|
||||
mlpFab.leftMargin = leftInsets + fabSpacing
|
||||
mlpFab.rightMargin = rightInsets + fabSpacing
|
||||
mlpFab.bottomMargin = barInsets.bottom + fabSpacing
|
||||
binding.buttonInstall.layoutParams = mlpFab
|
||||
|
||||
binding.listDrivers.updatePadding(
|
||||
bottom = barInsets.bottom +
|
||||
resources.getDimensionPixelSize(R.dimen.spacing_bottom_list_fab)
|
||||
)
|
||||
|
||||
windowInsets
|
||||
}
|
||||
|
||||
private val getDriver =
|
||||
registerForActivityResult(ActivityResultContracts.OpenDocument()) { result ->
|
||||
if (result == null) {
|
||||
return@registerForActivityResult
|
||||
}
|
||||
|
||||
IndeterminateProgressDialogFragment.newInstance(
|
||||
requireActivity(),
|
||||
R.string.installing_driver,
|
||||
false
|
||||
) {
|
||||
val driverPath =
|
||||
"${GpuDriverHelper.driverStoragePath}/${FileUtil.getFilename(result)}"
|
||||
val driverFile = File(driverPath)
|
||||
|
||||
// Ignore file exceptions when a user selects an invalid zip
|
||||
try {
|
||||
if (!GpuDriverHelper.copyDriverToInternalStorage(result)) {
|
||||
throw IOException("Driver failed validation!")
|
||||
}
|
||||
} catch (_: IOException) {
|
||||
if (driverFile.exists()) {
|
||||
driverFile.delete()
|
||||
}
|
||||
return@newInstance getString(R.string.select_gpu_driver_error)
|
||||
}
|
||||
|
||||
val driverData = GpuDriverHelper.getMetadataFromZip(driverFile)
|
||||
val driverInList =
|
||||
driverViewModel.driverList.value.firstOrNull { it.second == driverData }
|
||||
if (driverInList != null) {
|
||||
return@newInstance getString(R.string.driver_already_installed)
|
||||
} else {
|
||||
driverViewModel.addDriver(Pair(driverPath, driverData))
|
||||
driverViewModel.setNewDriverInstalled(true)
|
||||
}
|
||||
return@newInstance Any()
|
||||
}.show(childFragmentManager, IndeterminateProgressDialogFragment.TAG)
|
||||
}
|
||||
}
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
// 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.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import androidx.fragment.app.activityViewModels
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.lifecycle.repeatOnLifecycle
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import kotlinx.coroutines.launch
|
||||
import org.yuzu.yuzu_emu.R
|
||||
import org.yuzu.yuzu_emu.databinding.DialogProgressBarBinding
|
||||
import org.yuzu.yuzu_emu.model.DriverViewModel
|
||||
|
||||
class DriversLoadingDialogFragment : DialogFragment() {
|
||||
private val driverViewModel: DriverViewModel by activityViewModels()
|
||||
|
||||
private lateinit var binding: DialogProgressBarBinding
|
||||
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||
binding = DialogProgressBarBinding.inflate(layoutInflater)
|
||||
binding.progressBar.isIndeterminate = true
|
||||
|
||||
isCancelable = false
|
||||
|
||||
return MaterialAlertDialogBuilder(requireContext())
|
||||
.setTitle(R.string.loading)
|
||||
.setView(binding.root)
|
||||
.create()
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View = binding.root
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
viewLifecycleOwner.lifecycleScope.apply {
|
||||
launch {
|
||||
repeatOnLifecycle(Lifecycle.State.RESUMED) {
|
||||
driverViewModel.areDriversLoading.collect { checkForDismiss() }
|
||||
}
|
||||
}
|
||||
launch {
|
||||
repeatOnLifecycle(Lifecycle.State.RESUMED) {
|
||||
driverViewModel.isDriverReady.collect { checkForDismiss() }
|
||||
}
|
||||
}
|
||||
launch {
|
||||
repeatOnLifecycle(Lifecycle.State.RESUMED) {
|
||||
driverViewModel.isDeletingDrivers.collect { checkForDismiss() }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkForDismiss() {
|
||||
if (driverViewModel.isInteractionAllowed) {
|
||||
dismiss()
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val TAG = "DriversLoadingDialogFragment"
|
||||
}
|
||||
}
|
||||
@@ -10,14 +10,14 @@ import android.content.DialogInterface
|
||||
import android.content.SharedPreferences
|
||||
import android.content.pm.ActivityInfo
|
||||
import android.content.res.Configuration
|
||||
import android.graphics.Color
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.util.Rational
|
||||
import android.os.SystemClock
|
||||
import android.view.*
|
||||
import android.widget.TextView
|
||||
import android.widget.Toast
|
||||
import androidx.activity.OnBackPressedCallback
|
||||
import androidx.appcompat.widget.PopupMenu
|
||||
import androidx.core.content.res.ResourcesCompat
|
||||
@@ -25,6 +25,7 @@ import androidx.core.graphics.Insets
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
import androidx.drawerlayout.widget.DrawerLayout
|
||||
import androidx.drawerlayout.widget.DrawerLayout.DrawerListener
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.activityViewModels
|
||||
import androidx.lifecycle.Lifecycle
|
||||
@@ -39,6 +40,7 @@ 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.flow.collect
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.launch
|
||||
import org.yuzu.yuzu_emu.HomeNavigationDirections
|
||||
@@ -50,10 +52,12 @@ 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.model.DriverViewModel
|
||||
import org.yuzu.yuzu_emu.model.Game
|
||||
import org.yuzu.yuzu_emu.model.EmulationViewModel
|
||||
import org.yuzu.yuzu_emu.overlay.InputOverlay
|
||||
import org.yuzu.yuzu_emu.utils.*
|
||||
import java.lang.NullPointerException
|
||||
|
||||
class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
||||
private lateinit var preferences: SharedPreferences
|
||||
@@ -69,6 +73,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
||||
private lateinit var game: Game
|
||||
|
||||
private val emulationViewModel: EmulationViewModel by activityViewModels()
|
||||
private val driverViewModel: DriverViewModel by activityViewModels()
|
||||
|
||||
private var isInFoldableLayout = false
|
||||
|
||||
@@ -105,10 +110,21 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
||||
null
|
||||
}
|
||||
}
|
||||
game = if (args.game != null) {
|
||||
args.game!!
|
||||
} else {
|
||||
intentGame ?: error("[EmulationFragment] No bootable game present!")
|
||||
|
||||
try {
|
||||
game = if (args.game != null) {
|
||||
args.game!!
|
||||
} else {
|
||||
intentGame!!
|
||||
}
|
||||
} catch (e: NullPointerException) {
|
||||
Toast.makeText(
|
||||
requireContext(),
|
||||
R.string.no_game_present,
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
requireActivity().finish()
|
||||
return
|
||||
}
|
||||
|
||||
// So this fragment doesn't restart on configuration changes; i.e. rotation.
|
||||
@@ -132,10 +148,40 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
||||
// This is using the correct scope, lint is just acting up
|
||||
@SuppressLint("UnsafeRepeatOnLifecycleDetector")
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
if (requireActivity().isFinishing) {
|
||||
return
|
||||
}
|
||||
|
||||
binding.surfaceEmulation.holder.addCallback(this)
|
||||
binding.showFpsText.setTextColor(Color.YELLOW)
|
||||
binding.doneControlConfig.setOnClickListener { stopConfiguringControls() }
|
||||
|
||||
binding.drawerLayout.addDrawerListener(object : DrawerListener {
|
||||
override fun onDrawerSlide(drawerView: View, slideOffset: Float) {
|
||||
binding.surfaceInputOverlay.dispatchTouchEvent(
|
||||
MotionEvent.obtain(
|
||||
SystemClock.uptimeMillis(),
|
||||
SystemClock.uptimeMillis() + 100,
|
||||
MotionEvent.ACTION_UP,
|
||||
0f,
|
||||
0f,
|
||||
0
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override fun onDrawerOpened(drawerView: View) {
|
||||
// No op
|
||||
}
|
||||
|
||||
override fun onDrawerClosed(drawerView: View) {
|
||||
// No op
|
||||
}
|
||||
|
||||
override fun onDrawerStateChanged(newState: Int) {
|
||||
// No op
|
||||
}
|
||||
})
|
||||
binding.drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED)
|
||||
binding.inGameMenu.getHeaderView(0).findViewById<TextView>(R.id.text_game_title).text =
|
||||
game.title
|
||||
@@ -264,6 +310,8 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
||||
ViewUtils.showView(binding.surfaceInputOverlay)
|
||||
ViewUtils.hideView(binding.loadingIndicator)
|
||||
|
||||
emulationState.updateSurface()
|
||||
|
||||
// Setup overlay
|
||||
updateShowFpsOverlay()
|
||||
}
|
||||
@@ -282,29 +330,43 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
||||
}
|
||||
}
|
||||
}
|
||||
launch {
|
||||
repeatOnLifecycle(Lifecycle.State.RESUMED) {
|
||||
driverViewModel.isDriverReady.collect {
|
||||
if (it && !emulationState.isRunning) {
|
||||
if (!DirectoryInitialization.areDirectoriesReady) {
|
||||
DirectoryInitialization.start()
|
||||
}
|
||||
|
||||
updateScreenLayout()
|
||||
|
||||
emulationState.run(emulationActivity!!.isActivityRecreated)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onConfigurationChanged(newConfig: Configuration) {
|
||||
super.onConfigurationChanged(newConfig)
|
||||
if (_binding == null) {
|
||||
return
|
||||
}
|
||||
|
||||
updateScreenLayout()
|
||||
if (emulationActivity?.isInPictureInPictureMode == true) {
|
||||
if (binding.drawerLayout.isOpen) {
|
||||
binding.drawerLayout.close()
|
||||
}
|
||||
if (EmulationMenuSettings.showOverlay) {
|
||||
binding.surfaceInputOverlay.post {
|
||||
binding.surfaceInputOverlay.visibility = View.VISIBLE
|
||||
}
|
||||
binding.surfaceInputOverlay.visibility = View.INVISIBLE
|
||||
}
|
||||
} else {
|
||||
if (EmulationMenuSettings.showOverlay && emulationViewModel.emulationStarted.value) {
|
||||
binding.surfaceInputOverlay.post {
|
||||
binding.surfaceInputOverlay.visibility = View.VISIBLE
|
||||
}
|
||||
binding.surfaceInputOverlay.visibility = View.VISIBLE
|
||||
} else {
|
||||
binding.surfaceInputOverlay.post {
|
||||
binding.surfaceInputOverlay.visibility = View.INVISIBLE
|
||||
}
|
||||
binding.surfaceInputOverlay.visibility = View.INVISIBLE
|
||||
}
|
||||
if (!isInFoldableLayout) {
|
||||
if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) {
|
||||
@@ -316,19 +378,8 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
if (!DirectoryInitialization.areDirectoriesReady) {
|
||||
DirectoryInitialization.start()
|
||||
}
|
||||
|
||||
updateScreenLayout()
|
||||
|
||||
emulationState.run(emulationActivity!!.isActivityRecreated)
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
if (emulationState.isRunning) {
|
||||
if (emulationState.isRunning && emulationActivity?.isInPictureInPictureMode != true) {
|
||||
emulationState.pause()
|
||||
}
|
||||
super.onPause()
|
||||
@@ -361,12 +412,12 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
||||
val FRAMETIME = 2
|
||||
val SPEED = 3
|
||||
perfStatsUpdater = {
|
||||
if (emulationViewModel.emulationStarted.value == true) {
|
||||
if (emulationViewModel.emulationStarted.value) {
|
||||
val perfStats = NativeLibrary.getPerfStats()
|
||||
if (perfStats[FPS] > 0 && _binding != null) {
|
||||
if (_binding != null) {
|
||||
binding.showFpsText.text = String.format("FPS: %.1f", perfStats[FPS])
|
||||
}
|
||||
perfStatsUpdateHandler.postDelayed(perfStatsUpdater!!, 100)
|
||||
perfStatsUpdateHandler.postDelayed(perfStatsUpdater!!, 800)
|
||||
}
|
||||
}
|
||||
perfStatsUpdateHandler.post(perfStatsUpdater!!)
|
||||
@@ -394,16 +445,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
)
|
||||
binding.surfaceEmulation.setAspectRatio(null)
|
||||
emulationActivity?.buildPictureInPictureParams()
|
||||
updateOrientation()
|
||||
}
|
||||
@@ -420,7 +462,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
||||
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
|
||||
@@ -434,7 +475,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
||||
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()
|
||||
@@ -442,7 +482,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
||||
}
|
||||
binding.emulationContainer.requestLayout()
|
||||
binding.inputContainer.requestLayout()
|
||||
binding.overlayContainer.requestLayout()
|
||||
binding.inGameMenu.requestLayout()
|
||||
}
|
||||
|
||||
@@ -668,24 +707,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
||||
}
|
||||
|
||||
v.setPadding(left, cutInsets.top, right, 0)
|
||||
|
||||
// Ensure FPS text doesn't get cut off by rounded display corners
|
||||
val sidePadding = resources.getDimensionPixelSize(R.dimen.spacing_xtralarge)
|
||||
if (cutInsets.left == 0) {
|
||||
binding.showFpsText.setPadding(
|
||||
sidePadding,
|
||||
cutInsets.top,
|
||||
cutInsets.right,
|
||||
cutInsets.bottom
|
||||
)
|
||||
} else {
|
||||
binding.showFpsText.setPadding(
|
||||
cutInsets.left,
|
||||
cutInsets.top,
|
||||
cutInsets.right,
|
||||
cutInsets.bottom
|
||||
)
|
||||
}
|
||||
windowInsets
|
||||
}
|
||||
}
|
||||
@@ -693,7 +714,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
||||
private class EmulationState(private val gamePath: String) {
|
||||
private var state: State
|
||||
private var surface: Surface? = null
|
||||
private var runWhenSurfaceIsValid = false
|
||||
|
||||
init {
|
||||
// Starting state is stopped.
|
||||
@@ -751,8 +771,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
||||
// If the surface is set, run now. Otherwise, wait for it to get set.
|
||||
if (surface != null) {
|
||||
runWithValidSurface()
|
||||
} else {
|
||||
runWhenSurfaceIsValid = true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -760,11 +778,18 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
||||
@Synchronized
|
||||
fun newSurface(surface: Surface?) {
|
||||
this.surface = surface
|
||||
if (runWhenSurfaceIsValid) {
|
||||
if (this.surface != null) {
|
||||
runWithValidSurface()
|
||||
}
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
fun updateSurface() {
|
||||
if (surface != null) {
|
||||
NativeLibrary.surfaceChanged(surface)
|
||||
}
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
fun clearSurface() {
|
||||
if (surface == null) {
|
||||
@@ -788,10 +813,9 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
||||
}
|
||||
|
||||
private fun runWithValidSurface() {
|
||||
runWhenSurfaceIsValid = false
|
||||
NativeLibrary.surfaceChanged(surface)
|
||||
when (state) {
|
||||
State.STOPPED -> {
|
||||
NativeLibrary.surfaceChanged(surface)
|
||||
val emulationThread = Thread({
|
||||
Log.debug("[EmulationFragment] Starting emulation thread.")
|
||||
NativeLibrary.run(gamePath)
|
||||
@@ -801,7 +825,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
||||
|
||||
State.PAUSED -> {
|
||||
Log.debug("[EmulationFragment] Resuming emulation.")
|
||||
NativeLibrary.surfaceChanged(surface)
|
||||
NativeLibrary.unpauseEmulation()
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@ package org.yuzu.yuzu_emu.fragments
|
||||
|
||||
import android.Manifest
|
||||
import android.content.ActivityNotFoundException
|
||||
import android.content.DialogInterface
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.os.Bundle
|
||||
@@ -27,21 +26,23 @@ import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.activityViewModels
|
||||
import androidx.navigation.findNavController
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import com.google.android.material.transition.MaterialSharedAxis
|
||||
import org.yuzu.yuzu_emu.BuildConfig
|
||||
import org.yuzu.yuzu_emu.HomeNavigationDirections
|
||||
import org.yuzu.yuzu_emu.NativeLibrary
|
||||
import org.yuzu.yuzu_emu.R
|
||||
import org.yuzu.yuzu_emu.adapters.HomeSettingAdapter
|
||||
import org.yuzu.yuzu_emu.databinding.FragmentHomeSettingsBinding
|
||||
import org.yuzu.yuzu_emu.features.DocumentProvider
|
||||
import org.yuzu.yuzu_emu.features.settings.model.Settings
|
||||
import org.yuzu.yuzu_emu.model.DriverViewModel
|
||||
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
|
||||
import org.yuzu.yuzu_emu.utils.Log
|
||||
|
||||
class HomeSettingsFragment : Fragment() {
|
||||
private var _binding: FragmentHomeSettingsBinding? = null
|
||||
@@ -50,6 +51,7 @@ class HomeSettingsFragment : Fragment() {
|
||||
private lateinit var mainActivity: MainActivity
|
||||
|
||||
private val homeViewModel: HomeViewModel by activityViewModels()
|
||||
private val driverViewModel: DriverViewModel by activityViewModels()
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
@@ -85,51 +87,42 @@ class HomeSettingsFragment : Fragment() {
|
||||
)
|
||||
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,
|
||||
{
|
||||
val action = HomeNavigationDirections.actionGlobalSettingsActivity(
|
||||
null,
|
||||
Settings.MenuTag.SECTION_THEME
|
||||
)
|
||||
binding.root.findNavController().navigate(action)
|
||||
}
|
||||
)
|
||||
)
|
||||
add(
|
||||
HomeSetting(
|
||||
R.string.install_gpu_driver,
|
||||
R.string.gpu_driver_manager,
|
||||
R.string.install_gpu_driver_description,
|
||||
R.drawable.ic_exit,
|
||||
{ driverInstaller() },
|
||||
R.drawable.ic_build,
|
||||
{
|
||||
binding.root.findNavController()
|
||||
.navigate(R.id.action_homeSettingsFragment_to_driverManagerFragment)
|
||||
},
|
||||
{ GpuDriverHelper.supportsCustomDriverLoading() },
|
||||
R.string.custom_driver_not_supported,
|
||||
R.string.custom_driver_not_supported_description
|
||||
R.string.custom_driver_not_supported_description,
|
||||
driverViewModel.selectedDriverMetadata
|
||||
)
|
||||
)
|
||||
add(
|
||||
HomeSetting(
|
||||
R.string.install_amiibo_keys,
|
||||
R.string.install_amiibo_keys_description,
|
||||
R.drawable.ic_nfc,
|
||||
{ mainActivity.getAmiiboKey.launch(arrayOf("*/*")) }
|
||||
R.string.applets,
|
||||
R.string.applets_description,
|
||||
R.drawable.ic_applet,
|
||||
{
|
||||
binding.root.findNavController()
|
||||
.navigate(R.id.action_homeSettingsFragment_to_appletLauncherFragment)
|
||||
},
|
||||
{ NativeLibrary.isFirmwareAvailable() },
|
||||
R.string.applets_error_firmware,
|
||||
R.string.applets_error_description
|
||||
)
|
||||
)
|
||||
add(
|
||||
HomeSetting(
|
||||
R.string.install_game_content,
|
||||
R.string.install_game_content_description,
|
||||
R.drawable.ic_system_update_alt,
|
||||
{ mainActivity.installGameUpdate.launch(arrayOf("*/*")) }
|
||||
R.string.manage_yuzu_data,
|
||||
R.string.manage_yuzu_data_description,
|
||||
R.drawable.ic_install,
|
||||
{
|
||||
binding.root.findNavController()
|
||||
.navigate(R.id.action_homeSettingsFragment_to_installableFragment)
|
||||
}
|
||||
)
|
||||
)
|
||||
add(
|
||||
@@ -148,35 +141,6 @@ class HomeSettingsFragment : Fragment() {
|
||||
homeViewModel.gamesDir
|
||||
)
|
||||
)
|
||||
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,
|
||||
@@ -185,6 +149,28 @@ class HomeSettingsFragment : Fragment() {
|
||||
{ shareLog() }
|
||||
)
|
||||
)
|
||||
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,
|
||||
{
|
||||
val action = HomeNavigationDirections.actionGlobalSettingsActivity(
|
||||
null,
|
||||
Settings.MenuTag.SECTION_THEME
|
||||
)
|
||||
binding.root.findNavController().navigate(action)
|
||||
}
|
||||
)
|
||||
)
|
||||
add(
|
||||
HomeSetting(
|
||||
R.string.about,
|
||||
@@ -216,7 +202,8 @@ class HomeSettingsFragment : Fragment() {
|
||||
}
|
||||
|
||||
binding.homeSettingsList.apply {
|
||||
layoutManager = LinearLayoutManager(requireContext())
|
||||
layoutManager =
|
||||
GridLayoutManager(requireContext(), resources.getInteger(R.integer.grid_columns))
|
||||
adapter = HomeSettingAdapter(
|
||||
requireActivity() as AppCompatActivity,
|
||||
viewLifecycleOwner,
|
||||
@@ -326,44 +313,32 @@ class HomeSettingsFragment : Fragment() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun driverInstaller() {
|
||||
// Get the driver name for the dialog message.
|
||||
var driverName = GpuDriverHelper.customDriverName
|
||||
if (driverName == null) {
|
||||
driverName = getString(R.string.system_gpu_driver)
|
||||
}
|
||||
|
||||
MaterialAlertDialogBuilder(requireContext())
|
||||
.setTitle(getString(R.string.select_gpu_driver_title))
|
||||
.setMessage(driverName)
|
||||
.setNegativeButton(android.R.string.cancel, null)
|
||||
.setNeutralButton(R.string.select_gpu_driver_default) { _: DialogInterface?, _: Int ->
|
||||
GpuDriverHelper.installDefaultDriver(requireContext())
|
||||
Toast.makeText(
|
||||
requireContext(),
|
||||
R.string.select_gpu_driver_use_default,
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
}
|
||||
.setPositiveButton(R.string.select_gpu_driver_install) { _: DialogInterface?, _: Int ->
|
||||
mainActivity.getDriver.launch(arrayOf("application/zip"))
|
||||
}
|
||||
.show()
|
||||
}
|
||||
|
||||
// Share the current log if we just returned from a game but share the old log
|
||||
// if we just started the app and the old log exists.
|
||||
private fun shareLog() {
|
||||
val file = DocumentFile.fromSingleUri(
|
||||
val currentLog = 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)
|
||||
val oldLog = DocumentFile.fromSingleUri(
|
||||
mainActivity,
|
||||
DocumentsContract.buildDocumentUri(
|
||||
DocumentProvider.AUTHORITY,
|
||||
"${DocumentProvider.ROOT_ID}/log/yuzu_log.txt.old.txt"
|
||||
)
|
||||
)!!
|
||||
|
||||
val intent = Intent(Intent.ACTION_SEND)
|
||||
.setDataAndType(currentLog.uri, FileUtil.TEXT_PLAIN)
|
||||
.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||
if (!Log.gameLaunched && oldLog.exists()) {
|
||||
intent.putExtra(Intent.EXTRA_STREAM, oldLog.uri)
|
||||
startActivity(Intent.createChooser(intent, getText(R.string.share_log)))
|
||||
} else if (currentLog.exists()) {
|
||||
intent.putExtra(Intent.EXTRA_STREAM, currentLog.uri)
|
||||
startActivity(Intent.createChooser(intent, getText(R.string.share_log)))
|
||||
} else {
|
||||
Toast.makeText(
|
||||
|
||||
-213
@@ -1,213 +0,0 @@
|
||||
// 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 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
|
||||
import kotlinx.coroutines.withContext
|
||||
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 org.yuzu.yuzu_emu.utils.FileUtil
|
||||
|
||||
class ImportExportSavesFragment : DialogFragment() {
|
||||
private val context = YuzuApplication.appContext
|
||||
private val savesFolder =
|
||||
"${context.getPublicFilesDir().canonicalPath}/nand/user/save/0000000000000000"
|
||||
|
||||
// Get first subfolder in saves folder (should be the user folder)
|
||||
private val savesFolderRoot = File(savesFolder).listFiles()?.firstOrNull()?.canonicalPath ?: ""
|
||||
private var lastZipCreated: File? = null
|
||||
|
||||
private lateinit var startForResultExportSave: ActivityResultLauncher<Intent>
|
||||
private lateinit var documentPicker: ActivityResultLauncher<Array<String>>
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
val activity = requireActivity() as AppCompatActivity
|
||||
|
||||
val activityResultRegistry = requireActivity().activityResultRegistry
|
||||
startForResultExportSave = activityResultRegistry.register(
|
||||
"startForResultExportSaveKey",
|
||||
ActivityResultContracts.StartActivityForResult()
|
||||
) {
|
||||
File(context.getPublicFilesDir().canonicalPath, "temp").deleteRecursively()
|
||||
}
|
||||
documentPicker = activityResultRegistry.register(
|
||||
"documentPickerKey",
|
||||
ActivityResultContracts.OpenDocument()
|
||||
) {
|
||||
it?.let { uri -> importSave(uri, activity) }
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||
return if (savesFolderRoot == "") {
|
||||
MaterialAlertDialogBuilder(requireContext())
|
||||
.setTitle(R.string.manage_save_data)
|
||||
.setMessage(R.string.import_export_saves_no_profile)
|
||||
.setPositiveButton(android.R.string.ok, null)
|
||||
.show()
|
||||
} else {
|
||||
MaterialAlertDialogBuilder(requireContext())
|
||||
.setTitle(R.string.manage_save_data)
|
||||
.setMessage(R.string.manage_save_data_description)
|
||||
.setNegativeButton(R.string.export_saves) { _, _ ->
|
||||
exportSave()
|
||||
}
|
||||
.setPositiveButton(R.string.import_saves) { _, _ ->
|
||||
documentPicker.launch(arrayOf("application/zip"))
|
||||
}
|
||||
.setNeutralButton(android.R.string.cancel, null)
|
||||
.show()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Zips the save files located in the given folder path and creates a new zip file with the current date and time.
|
||||
* @return true if the zip file is successfully created, false otherwise.
|
||||
*/
|
||||
private fun zipSave(): Boolean {
|
||||
try {
|
||||
val tempFolder = File(requireContext().getPublicFilesDir().canonicalPath, "temp")
|
||||
tempFolder.mkdirs()
|
||||
val saveFolder = File(savesFolderRoot)
|
||||
val outputZipFile = File(
|
||||
tempFolder,
|
||||
"yuzu saves - ${
|
||||
LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"))
|
||||
}.zip"
|
||||
)
|
||||
outputZipFile.createNewFile()
|
||||
ZipOutputStream(BufferedOutputStream(FileOutputStream(outputZipFile))).use { zos ->
|
||||
saveFolder.walkTopDown().forEach { file ->
|
||||
val zipFileName =
|
||||
file.absolutePath.removePrefix(savesFolderRoot).removePrefix("/")
|
||||
if (zipFileName == "") {
|
||||
return@forEach
|
||||
}
|
||||
val entry = ZipEntry("$zipFileName${(if (file.isDirectory) "/" else "")}")
|
||||
zos.putNextEntry(entry)
|
||||
if (file.isFile) {
|
||||
file.inputStream().use { fis -> fis.copyTo(zos) }
|
||||
}
|
||||
}
|
||||
}
|
||||
lastZipCreated = outputZipFile
|
||||
} catch (e: Exception) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* Exports the save file located in the given folder path by creating a zip file and sharing it via intent.
|
||||
*/
|
||||
private fun exportSave() {
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
val wasZipCreated = zipSave()
|
||||
val lastZipFile = lastZipCreated
|
||||
if (!wasZipCreated || lastZipFile == null) {
|
||||
withContext(Dispatchers.Main) {
|
||||
Toast.makeText(context, "Failed to export save", Toast.LENGTH_LONG).show()
|
||||
}
|
||||
return@launch
|
||||
}
|
||||
|
||||
withContext(Dispatchers.Main) {
|
||||
val file = DocumentFile.fromSingleUri(
|
||||
context,
|
||||
DocumentsContract.buildDocumentUri(
|
||||
DocumentProvider.AUTHORITY,
|
||||
"${DocumentProvider.ROOT_ID}/temp/${lastZipFile.name}"
|
||||
)
|
||||
)!!
|
||||
val intent = Intent(Intent.ACTION_SEND)
|
||||
.setDataAndType(file.uri, "application/zip")
|
||||
.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||
.putExtra(Intent.EXTRA_STREAM, file.uri)
|
||||
startForResultExportSave.launch(Intent.createChooser(intent, "Share save file"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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, 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
|
||||
val savesFolder = File(savesFolderRoot)
|
||||
val cacheSaveDir = File("${context.cacheDir.path}/saves/")
|
||||
cacheSaveDir.mkdir()
|
||||
|
||||
if (inputZip == null) {
|
||||
Toast.makeText(context, context.getString(R.string.fatal_error), Toast.LENGTH_LONG)
|
||||
.show()
|
||||
return
|
||||
}
|
||||
|
||||
val filterTitleId =
|
||||
FilenameFilter { _, dirName -> dirName.matches(Regex("^0100[\\dA-Fa-f]{12}$")) }
|
||||
|
||||
try {
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
FileUtil.unzip(inputZip, cacheSaveDir)
|
||||
cacheSaveDir.list(filterTitleId)?.forEach { savePath ->
|
||||
File(savesFolder, savePath).deleteRecursively()
|
||||
File(cacheSaveDir, savePath).copyRecursively(File(savesFolder, savePath), true)
|
||||
validZip = true
|
||||
}
|
||||
|
||||
withContext(Dispatchers.Main) {
|
||||
if (!validZip) {
|
||||
MessageDialogFragment.newInstance(
|
||||
titleId = R.string.save_file_invalid_zip_structure,
|
||||
descriptionId = R.string.save_file_invalid_zip_structure_description
|
||||
).show(activity.supportFragmentManager, MessageDialogFragment.TAG)
|
||||
return@withContext
|
||||
}
|
||||
Toast.makeText(
|
||||
context,
|
||||
context.getString(R.string.save_file_imported_success),
|
||||
Toast.LENGTH_LONG
|
||||
).show()
|
||||
}
|
||||
|
||||
cacheSaveDir.deleteRecursively()
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Toast.makeText(context, context.getString(R.string.fatal_error), Toast.LENGTH_LONG)
|
||||
.show()
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val TAG = "ImportExportSavesFragment"
|
||||
}
|
||||
}
|
||||
+59
-18
@@ -9,8 +9,9 @@ import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.Toast
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import androidx.fragment.app.activityViewModels
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
@@ -18,6 +19,7 @@ import androidx.lifecycle.lifecycleScope
|
||||
import androidx.lifecycle.repeatOnLifecycle
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import kotlinx.coroutines.launch
|
||||
import org.yuzu.yuzu_emu.R
|
||||
import org.yuzu.yuzu_emu.databinding.DialogProgressBarBinding
|
||||
import org.yuzu.yuzu_emu.model.TaskViewModel
|
||||
|
||||
@@ -28,19 +30,25 @@ class IndeterminateProgressDialogFragment : DialogFragment() {
|
||||
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||
val titleId = requireArguments().getInt(TITLE)
|
||||
val cancellable = requireArguments().getBoolean(CANCELLABLE)
|
||||
|
||||
binding = DialogProgressBarBinding.inflate(layoutInflater)
|
||||
binding.progressBar.isIndeterminate = true
|
||||
val dialog = MaterialAlertDialogBuilder(requireContext())
|
||||
.setTitle(titleId)
|
||||
.setView(binding.root)
|
||||
.create()
|
||||
dialog.setCanceledOnTouchOutside(false)
|
||||
|
||||
if (cancellable) {
|
||||
dialog.setNegativeButton(android.R.string.cancel, null)
|
||||
}
|
||||
|
||||
val alertDialog = dialog.create()
|
||||
alertDialog.setCanceledOnTouchOutside(false)
|
||||
|
||||
if (!taskViewModel.isRunning.value) {
|
||||
taskViewModel.runTask()
|
||||
}
|
||||
return dialog
|
||||
return alertDialog
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
@@ -53,24 +61,54 @@ class IndeterminateProgressDialogFragment : DialogFragment() {
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
viewLifecycleOwner.lifecycleScope.launch {
|
||||
repeatOnLifecycle(Lifecycle.State.CREATED) {
|
||||
taskViewModel.isComplete.collect {
|
||||
if (it) {
|
||||
dismiss()
|
||||
when (val result = taskViewModel.result.value) {
|
||||
is String -> Toast.makeText(requireContext(), result, Toast.LENGTH_LONG)
|
||||
.show()
|
||||
viewLifecycleOwner.lifecycleScope.apply {
|
||||
launch {
|
||||
repeatOnLifecycle(Lifecycle.State.CREATED) {
|
||||
taskViewModel.isComplete.collect {
|
||||
if (it) {
|
||||
dismiss()
|
||||
when (val result = taskViewModel.result.value) {
|
||||
is String -> Toast.makeText(
|
||||
requireContext(),
|
||||
result,
|
||||
Toast.LENGTH_LONG
|
||||
).show()
|
||||
|
||||
is MessageDialogFragment -> result.show(
|
||||
requireActivity().supportFragmentManager,
|
||||
MessageDialogFragment.TAG
|
||||
)
|
||||
is MessageDialogFragment -> result.show(
|
||||
requireActivity().supportFragmentManager,
|
||||
MessageDialogFragment.TAG
|
||||
)
|
||||
|
||||
else -> {
|
||||
// Do nothing
|
||||
}
|
||||
}
|
||||
taskViewModel.clear()
|
||||
}
|
||||
taskViewModel.clear()
|
||||
}
|
||||
}
|
||||
}
|
||||
launch {
|
||||
repeatOnLifecycle(Lifecycle.State.CREATED) {
|
||||
taskViewModel.cancelled.collect {
|
||||
if (it) {
|
||||
dialog?.setTitle(R.string.cancelling)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// By default, the ProgressDialog will immediately dismiss itself upon a button being pressed.
|
||||
// Setting the OnClickListener again after the dialog is shown overrides this behavior.
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
val alertDialog = dialog as AlertDialog
|
||||
val negativeButton = alertDialog.getButton(Dialog.BUTTON_NEGATIVE)
|
||||
negativeButton.setOnClickListener {
|
||||
alertDialog.setTitle(getString(R.string.cancelling))
|
||||
taskViewModel.setCancelled(true)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,16 +116,19 @@ class IndeterminateProgressDialogFragment : DialogFragment() {
|
||||
const val TAG = "IndeterminateProgressDialogFragment"
|
||||
|
||||
private const val TITLE = "Title"
|
||||
private const val CANCELLABLE = "Cancellable"
|
||||
|
||||
fun newInstance(
|
||||
activity: AppCompatActivity,
|
||||
activity: FragmentActivity,
|
||||
titleId: Int,
|
||||
cancellable: Boolean = false,
|
||||
task: () -> Any
|
||||
): IndeterminateProgressDialogFragment {
|
||||
val dialog = IndeterminateProgressDialogFragment()
|
||||
val args = Bundle()
|
||||
ViewModelProvider(activity)[TaskViewModel::class.java].task = task
|
||||
args.putInt(TITLE, titleId)
|
||||
args.putBoolean(CANCELLABLE, cancellable)
|
||||
dialog.arguments = args
|
||||
return dialog
|
||||
}
|
||||
|
||||
@@ -0,0 +1,138 @@
|
||||
// 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 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.GridLayoutManager
|
||||
import com.google.android.material.transition.MaterialSharedAxis
|
||||
import org.yuzu.yuzu_emu.R
|
||||
import org.yuzu.yuzu_emu.adapters.InstallableAdapter
|
||||
import org.yuzu.yuzu_emu.databinding.FragmentInstallablesBinding
|
||||
import org.yuzu.yuzu_emu.model.HomeViewModel
|
||||
import org.yuzu.yuzu_emu.model.Installable
|
||||
import org.yuzu.yuzu_emu.ui.main.MainActivity
|
||||
|
||||
class InstallableFragment : Fragment() {
|
||||
private var _binding: FragmentInstallablesBinding? = 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)
|
||||
reenterTransition = MaterialSharedAxis(MaterialSharedAxis.X, false)
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View {
|
||||
_binding = FragmentInstallablesBinding.inflate(layoutInflater)
|
||||
return binding.root
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
val mainActivity = requireActivity() as MainActivity
|
||||
|
||||
homeViewModel.setNavigationVisibility(visible = false, animated = true)
|
||||
homeViewModel.setStatusBarShadeVisibility(visible = false)
|
||||
|
||||
binding.toolbarInstallables.setNavigationOnClickListener {
|
||||
binding.root.findNavController().popBackStack()
|
||||
}
|
||||
|
||||
val installables = listOf(
|
||||
Installable(
|
||||
R.string.user_data,
|
||||
R.string.user_data_description,
|
||||
install = { mainActivity.importUserData.launch(arrayOf("application/zip")) },
|
||||
export = { mainActivity.exportUserData.launch("export.zip") }
|
||||
),
|
||||
Installable(
|
||||
R.string.install_game_content,
|
||||
R.string.install_game_content_description,
|
||||
install = { mainActivity.installGameUpdate.launch(arrayOf("*/*")) }
|
||||
),
|
||||
Installable(
|
||||
R.string.install_firmware,
|
||||
R.string.install_firmware_description,
|
||||
install = { mainActivity.getFirmware.launch(arrayOf("application/zip")) }
|
||||
),
|
||||
if (mainActivity.savesFolderRoot != "") {
|
||||
Installable(
|
||||
R.string.manage_save_data,
|
||||
R.string.import_export_saves_description,
|
||||
install = { mainActivity.importSaves.launch(arrayOf("application/zip")) },
|
||||
export = { mainActivity.exportSave() }
|
||||
)
|
||||
} else {
|
||||
Installable(
|
||||
R.string.manage_save_data,
|
||||
R.string.import_export_saves_description,
|
||||
install = { mainActivity.importSaves.launch(arrayOf("application/zip")) }
|
||||
)
|
||||
},
|
||||
Installable(
|
||||
R.string.install_prod_keys,
|
||||
R.string.install_prod_keys_description,
|
||||
install = { mainActivity.getProdKey.launch(arrayOf("*/*")) }
|
||||
),
|
||||
Installable(
|
||||
R.string.install_amiibo_keys,
|
||||
R.string.install_amiibo_keys_description,
|
||||
install = { mainActivity.getAmiiboKey.launch(arrayOf("*/*")) }
|
||||
)
|
||||
)
|
||||
|
||||
binding.listInstallables.apply {
|
||||
layoutManager = GridLayoutManager(
|
||||
requireContext(),
|
||||
resources.getInteger(R.integer.grid_columns)
|
||||
)
|
||||
adapter = InstallableAdapter(installables)
|
||||
}
|
||||
|
||||
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.toolbarInstallables.layoutParams as ViewGroup.MarginLayoutParams
|
||||
mlpAppBar.leftMargin = leftInsets
|
||||
mlpAppBar.rightMargin = rightInsets
|
||||
binding.toolbarInstallables.layoutParams = mlpAppBar
|
||||
|
||||
val mlpScrollAbout =
|
||||
binding.listInstallables.layoutParams as ViewGroup.MarginLayoutParams
|
||||
mlpScrollAbout.leftMargin = leftInsets
|
||||
mlpScrollAbout.rightMargin = rightInsets
|
||||
binding.listInstallables.layoutParams = mlpScrollAbout
|
||||
|
||||
binding.listInstallables.updatePadding(bottom = barInsets.bottom)
|
||||
|
||||
windowInsets
|
||||
}
|
||||
}
|
||||
@@ -4,14 +4,22 @@
|
||||
package org.yuzu.yuzu_emu.fragments
|
||||
|
||||
import android.app.Dialog
|
||||
import android.content.DialogInterface
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.text.Html
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import androidx.fragment.app.activityViewModels
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import org.yuzu.yuzu_emu.R
|
||||
import org.yuzu.yuzu_emu.model.MessageDialogViewModel
|
||||
|
||||
class MessageDialogFragment : DialogFragment() {
|
||||
private val messageDialogViewModel: MessageDialogViewModel by activityViewModels()
|
||||
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||
val titleId = requireArguments().getInt(TITLE_ID)
|
||||
val titleString = requireArguments().getString(TITLE_STRING)!!
|
||||
@@ -25,7 +33,9 @@ class MessageDialogFragment : DialogFragment() {
|
||||
if (titleId != 0) dialog.setTitle(titleId)
|
||||
if (titleString.isNotEmpty()) dialog.setTitle(titleString)
|
||||
|
||||
if (descriptionId != 0) dialog.setMessage(descriptionId)
|
||||
if (descriptionId != 0) {
|
||||
dialog.setMessage(Html.fromHtml(getString(descriptionId), Html.FROM_HTML_MODE_LEGACY))
|
||||
}
|
||||
if (descriptionString.isNotEmpty()) dialog.setMessage(descriptionString)
|
||||
|
||||
if (helpLinkId != 0) {
|
||||
@@ -37,6 +47,12 @@ class MessageDialogFragment : DialogFragment() {
|
||||
return dialog.show()
|
||||
}
|
||||
|
||||
override fun onDismiss(dialog: DialogInterface) {
|
||||
super.onDismiss(dialog)
|
||||
messageDialogViewModel.dismissAction.invoke()
|
||||
messageDialogViewModel.clear()
|
||||
}
|
||||
|
||||
private fun openLink(link: String) {
|
||||
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(link))
|
||||
startActivity(intent)
|
||||
@@ -52,11 +68,13 @@ class MessageDialogFragment : DialogFragment() {
|
||||
private const val HELP_LINK = "Link"
|
||||
|
||||
fun newInstance(
|
||||
activity: FragmentActivity,
|
||||
titleId: Int = 0,
|
||||
titleString: String = "",
|
||||
descriptionId: Int = 0,
|
||||
descriptionString: String = "",
|
||||
helpLinkId: Int = 0
|
||||
helpLinkId: Int = 0,
|
||||
dismissAction: () -> Unit = {}
|
||||
): MessageDialogFragment {
|
||||
val dialog = MessageDialogFragment()
|
||||
val bundle = Bundle()
|
||||
@@ -67,6 +85,8 @@ class MessageDialogFragment : DialogFragment() {
|
||||
putString(DESCRIPTION_STRING, descriptionString)
|
||||
putInt(HELP_LINK, helpLinkId)
|
||||
}
|
||||
ViewModelProvider(activity)[MessageDialogViewModel::class.java].dismissAction =
|
||||
dismissAction
|
||||
dialog.arguments = bundle
|
||||
return dialog
|
||||
}
|
||||
|
||||
@@ -40,8 +40,10 @@ class SettingsSearchFragment : Fragment() {
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
enterTransition = MaterialSharedAxis(MaterialSharedAxis.Z, false)
|
||||
returnTransition = MaterialSharedAxis(MaterialSharedAxis.Z, true)
|
||||
enterTransition = MaterialSharedAxis(MaterialSharedAxis.X, true)
|
||||
returnTransition = MaterialSharedAxis(MaterialSharedAxis.X, false)
|
||||
reenterTransition = MaterialSharedAxis(MaterialSharedAxis.X, false)
|
||||
exitTransition = MaterialSharedAxis(MaterialSharedAxis.X, true)
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
@@ -55,7 +57,6 @@ class SettingsSearchFragment : Fragment() {
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
settingsViewModel.setIsUsingSearch(true)
|
||||
|
||||
if (savedInstanceState != null) {
|
||||
binding.searchText.setText(savedInstanceState.getString(SEARCH_TEXT))
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user