diff options
Diffstat (limited to 'src/android')
8 files changed, 111 insertions, 57 deletions
diff --git a/src/android/app/build.gradle.kts b/src/android/app/build.gradle.kts index a8db70511..fe79a701c 100644 --- a/src/android/app/build.gradle.kts +++ b/src/android/app/build.gradle.kts @@ -95,6 +95,7 @@ android { // builds a release build that doesn't need signing // Attaches 'debug' suffix to version and package name, allowing installation alongside the release build. register("relWithDebInfo") { + isDefault = true resValue("string", "app_name_suffixed", "yuzu Debug Release") signingConfig = signingConfigs.getByName("debug") isMinifyEnabled = true @@ -122,6 +123,7 @@ android { flavorDimensions.add("version") productFlavors { create("mainline") { + isDefault = true dimension = "version" buildConfigField("Boolean", "PREMIUM", "false") } diff --git a/src/android/app/src/main/AndroidManifest.xml b/src/android/app/src/main/AndroidManifest.xml index 6184f3eb6..933244140 100644 --- a/src/android/app/src/main/AndroidManifest.xml +++ b/src/android/app/src/main/AndroidManifest.xml @@ -25,6 +25,7 @@ SPDX-License-Identifier: GPL-3.0-or-later android:hasFragileUserData="false" android:supportsRtl="true" android:isGame="true" + android:appCategory="game" android:localeConfig="@xml/locales_config" android:banner="@drawable/tv_banner" android:extractNativeLibs="true" @@ -55,7 +56,6 @@ SPDX-License-Identifier: GPL-3.0-or-later android:name="org.yuzu.yuzu_emu.activities.EmulationActivity" android:theme="@style/Theme.Yuzu.Main" android:launchMode="singleTop" - android:screenOrientation="userLandscape" android:supportsPictureInPicture="true" android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|uiMode" android:exported="true"> diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivity.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivity.kt index a5af5a7ae..e6fffc832 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivity.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivity.kt @@ -11,7 +11,6 @@ import android.view.View import android.view.ViewGroup.MarginLayoutParams import android.widget.Toast import androidx.activity.OnBackPressedCallback -import androidx.activity.result.ActivityResultLauncher import androidx.activity.viewModels import androidx.appcompat.app.AppCompatActivity import androidx.core.view.ViewCompat @@ -246,17 +245,5 @@ class SettingsActivity : AppCompatActivity(), SettingsActivityView { settings.putExtra(ARG_GAME_ID, gameId) context.startActivity(settings) } - - fun launch( - context: Context, - launcher: ActivityResultLauncher<Intent>, - menuTag: String?, - gameId: String? - ) { - val settings = Intent(context, SettingsActivity::class.java) - settings.putExtra(ARG_MENU_TAG, menuTag) - settings.putExtra(ARG_GAME_ID, gameId) - launcher.launch(settings) - } } } diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt index 25b9d4018..09e93a017 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt @@ -7,7 +7,6 @@ import android.annotation.SuppressLint import android.app.AlertDialog import android.content.Context import android.content.DialogInterface -import android.content.Intent import android.content.SharedPreferences import android.content.pm.ActivityInfo import android.content.res.Configuration @@ -19,8 +18,6 @@ import android.util.Rational import android.view.* import android.widget.TextView import androidx.activity.OnBackPressedCallback -import androidx.activity.result.ActivityResultLauncher -import androidx.activity.result.contract.ActivityResultContracts import androidx.appcompat.widget.PopupMenu import androidx.core.content.res.ResourcesCompat import androidx.core.graphics.Insets @@ -66,8 +63,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { private var isInFoldableLayout = false - private lateinit var onReturnFromSettings: ActivityResultLauncher<Intent> - override fun onAttach(context: Context) { super.onAttach(context) if (context is EmulationActivity) { @@ -81,11 +76,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { .collect { updateFoldableLayout(context, it) } } } - - onReturnFromSettings = context.activityResultRegistry.register( - "SettingsResult", - ActivityResultContracts.StartActivityForResult() - ) { updateScreenLayout() } } else { throw IllegalStateException("EmulationFragment must have EmulationActivity parent") } @@ -149,12 +139,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { } R.id.menu_settings -> { - SettingsActivity.launch( - requireContext(), - onReturnFromSettings, - SettingsFile.FILE_NAME_CONFIG, - "" - ) + SettingsActivity.launch(requireContext(), SettingsFile.FILE_NAME_CONFIG, "") true } diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameHelper.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameHelper.kt index f8e7eeca7..f71d0a098 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameHelper.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameHelper.kt @@ -11,6 +11,7 @@ import kotlinx.serialization.json.Json import org.yuzu.yuzu_emu.NativeLibrary import org.yuzu.yuzu_emu.YuzuApplication import org.yuzu.yuzu_emu.model.Game +import org.yuzu.yuzu_emu.model.MinimalDocumentFile object GameHelper { const val KEY_GAME_PATH = "game_path" @@ -29,15 +30,7 @@ object GameHelper { // Ensure keys are loaded so that ROM metadata can be decrypted. NativeLibrary.reloadKeys() - val children = FileUtil.listFiles(context, gamesUri) - for (file in children) { - if (!file.isDirectory) { - // Check that the file has an extension we care about before trying to read out of it. - if (Game.extensions.contains(FileUtil.getExtension(file.uri))) { - games.add(getGame(file.uri)) - } - } - } + addGamesRecursive(games, FileUtil.listFiles(context, gamesUri), 3) // Cache list of games found on disk val serializedGames = mutableSetOf<String>() @@ -52,6 +45,30 @@ object GameHelper { return games.toList() } + private fun addGamesRecursive( + games: MutableList<Game>, + files: Array<MinimalDocumentFile>, + depth: Int + ) { + if (depth <= 0) { + return + } + + files.forEach { + if (it.isDirectory) { + addGamesRecursive( + games, + FileUtil.listFiles(YuzuApplication.appContext, it.uri), + depth - 1 + ) + } else { + if (Game.extensions.contains(FileUtil.getExtension(it.uri))) { + games.add(getGame(it.uri)) + } + } + } + } + private fun getGame(uri: Uri): Game { val filePath = uri.toString() var name = NativeLibrary.getTitle(filePath) diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/views/FixedRatioSurfaceView.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/views/FixedRatioSurfaceView.kt index 685ccaa76..2f0868c63 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/views/FixedRatioSurfaceView.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/views/FixedRatioSurfaceView.kt @@ -7,7 +7,6 @@ import android.content.Context import android.util.AttributeSet import android.util.Rational import android.view.SurfaceView -import kotlin.math.roundToInt class FixedRatioSurfaceView @JvmOverloads constructor( context: Context, @@ -22,27 +21,44 @@ class FixedRatioSurfaceView @JvmOverloads constructor( */ fun setAspectRatio(ratio: Rational?) { aspectRatio = ratio?.toFloat() ?: 0f + requestLayout() } override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { - super.onMeasure(widthMeasureSpec, heightMeasureSpec) - val width = MeasureSpec.getSize(widthMeasureSpec) - val height = MeasureSpec.getSize(heightMeasureSpec) + val displayWidth: Float = MeasureSpec.getSize(widthMeasureSpec).toFloat() + val displayHeight: Float = MeasureSpec.getSize(heightMeasureSpec).toFloat() if (aspectRatio != 0f) { - val newWidth: Int - val newHeight: Int - if (height * aspectRatio < width) { - newWidth = (height * aspectRatio).roundToInt() - newHeight = height + val displayAspect = displayWidth / displayHeight + if (displayAspect < aspectRatio) { + // Max out width + val halfHeight = displayHeight / 2 + val surfaceHeight = displayWidth / aspectRatio + val newTop: Float = halfHeight - (surfaceHeight / 2) + val newBottom: Float = halfHeight + (surfaceHeight / 2) + super.onMeasure( + widthMeasureSpec, + MeasureSpec.makeMeasureSpec( + newBottom.toInt() - newTop.toInt(), + MeasureSpec.EXACTLY + ) + ) + return } else { - newWidth = width - newHeight = (width / aspectRatio).roundToInt() + // Max out height + val halfWidth = displayWidth / 2 + val surfaceWidth = displayHeight * aspectRatio + val newLeft: Float = halfWidth - (surfaceWidth / 2) + val newRight: Float = halfWidth + (surfaceWidth / 2) + super.onMeasure( + MeasureSpec.makeMeasureSpec( + newRight.toInt() - newLeft.toInt(), + MeasureSpec.EXACTLY + ), + heightMeasureSpec + ) + return } - val left = (width - newWidth) / 2 - val top = (height - newHeight) / 2 - setLeftTopRightBottom(left, top, left + newWidth, top + newHeight) - } else { - setLeftTopRightBottom(0, 0, width, height) } + super.onMeasure(widthMeasureSpec, heightMeasureSpec) } } diff --git a/src/android/app/src/main/jni/config.cpp b/src/android/app/src/main/jni/config.cpp index 5e1f10f99..9de9bd93e 100644 --- a/src/android/app/src/main/jni/config.cpp +++ b/src/android/app/src/main/jni/config.cpp @@ -11,6 +11,7 @@ #include "common/fs/path_util.h" #include "common/logging/log.h" #include "common/settings.h" +#include "common/settings_enums.h" #include "core/hle/service/acc/profile_manager.h" #include "input_common/main.h" #include "jni/config.h" @@ -144,7 +145,9 @@ void Config::ReadValues() { Service::Account::MAX_USERS - 1); // Disable docked mode by default on Android - Settings::values.use_docked_mode = config->GetBoolean("System", "use_docked_mode", false); + Settings::values.use_docked_mode.SetValue(config->GetBoolean("System", "use_docked_mode", false) + ? Settings::ConsoleMode::Docked + : Settings::ConsoleMode::Handheld); const auto rng_seed_enabled = config->GetBoolean("System", "rng_seed_enabled", false); if (rng_seed_enabled) { diff --git a/src/android/app/src/main/jni/native.cpp b/src/android/app/src/main/jni/native.cpp index c23b2f19e..7e17833a0 100644 --- a/src/android/app/src/main/jni/native.cpp +++ b/src/android/app/src/main/jni/native.cpp @@ -30,6 +30,7 @@ #include "core/cpu_manager.h" #include "core/crypto/key_manager.h" #include "core/file_sys/card_image.h" +#include "core/file_sys/content_archive.h" #include "core/file_sys/registered_cache.h" #include "core/file_sys/submission_package.h" #include "core/file_sys/vfs.h" @@ -224,6 +225,42 @@ public: m_system.Renderer().NotifySurfaceChanged(); } + void ConfigureFilesystemProvider(const std::string& filepath) { + const auto file = m_system.GetFilesystem()->OpenFile(filepath, FileSys::Mode::Read); + if (!file) { + return; + } + + auto loader = Loader::GetLoader(m_system, file); + if (!loader) { + return; + } + + const auto file_type = loader->GetFileType(); + if (file_type == Loader::FileType::Unknown || file_type == Loader::FileType::Error) { + return; + } + + u64 program_id = 0; + const auto res2 = loader->ReadProgramId(program_id); + if (res2 == Loader::ResultStatus::Success && file_type == Loader::FileType::NCA) { + m_manual_provider->AddEntry(FileSys::TitleType::Application, + FileSys::GetCRTypeFromNCAType(FileSys::NCA{file}.GetType()), + program_id, file); + } else if (res2 == Loader::ResultStatus::Success && + (file_type == Loader::FileType::XCI || file_type == Loader::FileType::NSP)) { + const auto nsp = file_type == Loader::FileType::NSP + ? std::make_shared<FileSys::NSP>(file) + : FileSys::XCI{file}.GetSecurePartitionNSP(); + for (const auto& title : nsp->GetNCAs()) { + for (const auto& entry : title.second) { + m_manual_provider->AddEntry(entry.first.first, entry.first.second, title.first, + entry.second->GetBaseFile()); + } + } + } + } + Core::SystemResultStatus InitializeEmulation(const std::string& filepath) { std::scoped_lock lock(m_mutex); @@ -254,8 +291,14 @@ public: std::move(android_keyboard), // Software Keyboard nullptr, // Web Browser }); + + // Initialize filesystem. + m_manual_provider = std::make_unique<FileSys::ManualContentProvider>(); m_system.SetContentProvider(std::make_unique<FileSys::ContentProviderUnion>()); + m_system.RegisterContentProvider(FileSys::ContentProviderUnionSlot::FrontendManual, + m_manual_provider.get()); m_system.GetFileSystemController().CreateFactories(*m_vfs); + ConfigureFilesystemProvider(filepath); // Initialize account manager m_profile_manager = std::make_unique<Service::Account::ProfileManager>(); @@ -377,7 +420,7 @@ public: return false; } - return !Settings::values.use_docked_mode.GetValue(); + return !Settings::IsDockedMode(); } void SetDeviceType([[maybe_unused]] int index, int type) { @@ -489,6 +532,7 @@ private: bool m_is_paused{}; SoftwareKeyboard::AndroidKeyboard* m_software_keyboard{}; std::unique_ptr<Service::Account::ProfileManager> m_profile_manager; + std::unique_ptr<FileSys::ManualContentProvider> m_manual_provider; // GPU driver parameters std::shared_ptr<Common::DynamicLibrary> m_vulkan_library; |