mirror of
https://git.eden-emu.dev/eden-emu/eden
synced 2026-04-29 02:33:48 +00:00
Android impl
Signed-off-by: crueter <crueter@eden-emu.dev>
This commit is contained in:
parent
02e63b1948
commit
57b7b08fd8
|
|
@ -268,11 +268,6 @@ if (NOT EXISTS ${PROJECT_BINARY_DIR}/${compat_json})
|
|||
file(WRITE ${PROJECT_BINARY_DIR}/${compat_json} "")
|
||||
endif()
|
||||
|
||||
if (YUZU_LEGACY)
|
||||
message(WARNING "Making legacy build. Performance may suffer.")
|
||||
add_compile_definitions(YUZU_LEGACY)
|
||||
endif()
|
||||
|
||||
if (ARCHITECTURE_arm64 AND (ANDROID OR PLATFORM_LINUX))
|
||||
set(HAS_NCE 1)
|
||||
add_compile_definitions(HAS_NCE=1)
|
||||
|
|
|
|||
|
|
@ -25,6 +25,16 @@ if (NIGHTLY_BUILD)
|
|||
add_compile_definitions(NIGHTLY_BUILD)
|
||||
endif()
|
||||
|
||||
if (YUZU_LEGACY)
|
||||
message(WARNING "Making legacy build. Performance may suffer.")
|
||||
add_compile_definitions(YUZU_LEGACY)
|
||||
endif()
|
||||
|
||||
if (GENSHIN_SPOOF)
|
||||
message(WARNING "Making Genshin spoof build")
|
||||
add_compile_definitions(GENSHIN_SPOOF)
|
||||
endif()
|
||||
|
||||
# Set compilation flags
|
||||
if (MSVC AND NOT CXX_CLANG)
|
||||
set(CMAKE_CONFIGURATION_TYPES Debug Release CACHE STRING "" FORCE)
|
||||
|
|
|
|||
|
|
@ -88,6 +88,7 @@ android {
|
|||
"-DBUILD_TESTING=OFF",
|
||||
"-DYUZU_TESTS=OFF",
|
||||
"-DDYNARMIC_TESTS=OFF",
|
||||
"-DENABLE_UPDATE_CHECKER=ON",
|
||||
*extraCMakeArgs.toTypedArray()
|
||||
)
|
||||
)
|
||||
|
|
@ -192,6 +193,12 @@ android {
|
|||
manifestPlaceholders += mapOf("appNameBase" to "Eden")
|
||||
resValue("string", "app_name_suffixed", "Eden")
|
||||
|
||||
externalNativeBuild {
|
||||
cmake {
|
||||
arguments.add("-DGENSHIN_SPOOF=ON")
|
||||
}
|
||||
}
|
||||
|
||||
ndk {
|
||||
abiFilters += listOf("arm64-v8a")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,6 +33,18 @@ import org.yuzu.yuzu_emu.applets.web.WebBrowser
|
|||
* with the native side of the Yuzu code.
|
||||
*/
|
||||
object NativeLibrary {
|
||||
data class UpdateResult(
|
||||
var tag: String = "",
|
||||
var title: String = "",
|
||||
var body: String = "",
|
||||
var url: String = "",
|
||||
var assets: MutableList<String> = mutableListOf()
|
||||
) {
|
||||
fun addAsset(asset: String) {
|
||||
assets.add(asset)
|
||||
}
|
||||
}
|
||||
|
||||
@JvmField
|
||||
var sEmulationActivity = WeakReference<EmulationActivity?>(null)
|
||||
|
||||
|
|
@ -240,17 +252,7 @@ object NativeLibrary {
|
|||
/**
|
||||
* Checks for available updates.
|
||||
*/
|
||||
external fun checkForUpdate(): Array<String>?
|
||||
|
||||
/**
|
||||
* Return the URL to the release page
|
||||
*/
|
||||
external fun getUpdateUrl(version: String): String
|
||||
|
||||
/**
|
||||
* Return the URL to download the APK for the given version
|
||||
*/
|
||||
external fun getUpdateApkUrl(tag: String, artifact: String, packageId: String): String
|
||||
external fun checkForUpdate(): UpdateResult?
|
||||
|
||||
/**
|
||||
* Returns whether the update checker is enabled through CMAKE options.
|
||||
|
|
|
|||
|
|
@ -175,25 +175,25 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
|
|||
val latestVersion = NativeLibrary.checkForUpdate()
|
||||
if (latestVersion != null) {
|
||||
runOnUiThread {
|
||||
val tag: String = latestVersion[0]
|
||||
val name: String = latestVersion[1]
|
||||
showUpdateDialog(tag, name)
|
||||
showUpdateDialog(latestVersion)
|
||||
}
|
||||
}
|
||||
}.start()
|
||||
}
|
||||
|
||||
private fun showUpdateDialog(tag: String, name: String) {
|
||||
// TODO(crueter): body, "View on Forgejo" button
|
||||
private fun showUpdateDialog(release: NativeLibrary.UpdateResult) {
|
||||
MaterialAlertDialogBuilder(this)
|
||||
.setTitle(R.string.update_available)
|
||||
.setMessage(getString(R.string.update_available_description, name))
|
||||
.setMessage(getString(R.string.update_available_description, release.title))
|
||||
.setPositiveButton(android.R.string.ok) { _, _ ->
|
||||
var artifact = tag
|
||||
// Nightly builds have a slightly different format
|
||||
if (NativeLibrary.isNightlyBuild()) {
|
||||
artifact = tag.substringAfter('.', tag)
|
||||
val assets = release.assets
|
||||
|
||||
if (assets.isEmpty()) {
|
||||
openLink(release.url)
|
||||
} else {
|
||||
downloadAndInstallUpdate(release)
|
||||
}
|
||||
downloadAndInstallUpdate(tag, artifact)
|
||||
}
|
||||
.setNeutralButton(R.string.cancel) { dialog, _ ->
|
||||
dialog.dismiss()
|
||||
|
|
@ -206,17 +206,25 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
|
|||
.show()
|
||||
}
|
||||
|
||||
private fun downloadAndInstallUpdate(version: String, artifact: String) {
|
||||
private fun openLink(link: String) {
|
||||
val intent = Intent(Intent.ACTION_VIEW, link.toUri())
|
||||
startActivity(intent)
|
||||
}
|
||||
|
||||
private fun downloadAndInstallUpdate(release: NativeLibrary.UpdateResult) {
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
val packageId = applicationContext.packageName
|
||||
val apkUrl = NativeLibrary.getUpdateApkUrl(version, artifact, packageId)
|
||||
val asset = release.assets[0]
|
||||
val artifact = asset.split("/").last()
|
||||
val apkFile = File(cacheDir, "update-$artifact.apk")
|
||||
|
||||
Log.info("Artifact: ${artifact}\nAsset: ${asset}")
|
||||
|
||||
withContext(Dispatchers.Main) {
|
||||
showDownloadProgressDialog()
|
||||
}
|
||||
|
||||
val downloader = APKDownloader(apkUrl, apkFile)
|
||||
val downloader = APKDownloader(asset, apkFile)
|
||||
downloader.download(
|
||||
onProgress = { progress ->
|
||||
runOnUiThread {
|
||||
|
|
@ -248,7 +256,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
|
|||
} else {
|
||||
Toast.makeText(
|
||||
this@MainActivity,
|
||||
getString(R.string.update_download_failed) + "\n\nURL: $apkUrl",
|
||||
getString(R.string.update_download_failed) + "\n\nURL: $asset",
|
||||
Toast.LENGTH_LONG
|
||||
).show()
|
||||
}
|
||||
|
|
@ -277,7 +285,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
|
|||
|
||||
private fun updateDownloadProgress(progress: Int) {
|
||||
progressBar?.progress = progress
|
||||
progressMessage?.text = "$progress%"
|
||||
progressMessage?.text = getString(R.string.percent, progress)
|
||||
}
|
||||
|
||||
private fun dismissDownloadProgressDialog() {
|
||||
|
|
|
|||
|
|
@ -1699,76 +1699,76 @@ JNIEXPORT jboolean JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_isNightlyBuild(
|
|||
#ifdef ENABLE_UPDATE_CHECKER
|
||||
|
||||
|
||||
JNIEXPORT jobjectArray JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_checkForUpdate(
|
||||
JNIEXPORT jobject JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_checkForUpdate(
|
||||
JNIEnv* env,
|
||||
jobject obj) {
|
||||
std::optional<UpdateChecker::Update> release = UpdateChecker::GetUpdate();
|
||||
std::optional<Common::Net::Release> release = UpdateChecker::GetUpdate();
|
||||
if (!release) return nullptr;
|
||||
|
||||
const std::string tag = release->tag;
|
||||
const std::string name = release->name;
|
||||
const std::string title = release->title;
|
||||
const std::string body = release->body;
|
||||
const std::string url = release->html_url;
|
||||
|
||||
jobjectArray result = env->NewObjectArray(2, env->FindClass("java/lang/String"), nullptr);
|
||||
// Android *should* only ever define a single asset.
|
||||
// If not, something has gone wrong, but the Kotlin side can handle it.
|
||||
const auto assets = release->GetPlatformAssets();
|
||||
|
||||
const jstring jtag = env->NewStringUTF(tag.c_str());
|
||||
const jstring jname = env->NewStringUTF(name.c_str());
|
||||
|
||||
env->SetObjectArrayElement(result, 0, jtag);
|
||||
env->SetObjectArrayElement(result, 1, jname);
|
||||
env->DeleteLocalRef(jtag);
|
||||
env->DeleteLocalRef(jname);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
JNIEXPORT jstring JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_getUpdateUrl(
|
||||
JNIEnv* env,
|
||||
jobject obj,
|
||||
jstring version) {
|
||||
const char* version_str = env->GetStringUTFChars(version, nullptr);
|
||||
const std::string url = fmt::format("{}/{}",
|
||||
std::string{Common::g_build_auto_update_api},
|
||||
version_str);
|
||||
env->ReleaseStringUTFChars(version, version_str);
|
||||
return env->NewStringUTF(url.c_str());
|
||||
}
|
||||
|
||||
JNIEXPORT jstring JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_getUpdateApkUrl(
|
||||
JNIEnv* env,
|
||||
jobject obj,
|
||||
jstring tag,
|
||||
jstring artifact,
|
||||
jstring packageId) {
|
||||
const char* version_str = env->GetStringUTFChars(tag, nullptr);
|
||||
const char* artifact_str = env->GetStringUTFChars(artifact, nullptr);
|
||||
const char* package_id_str = env->GetStringUTFChars(packageId, nullptr);
|
||||
|
||||
std::string variant;
|
||||
std::string package_id(package_id_str);
|
||||
|
||||
if (package_id.find("dev.legacy.eden_emulator") != std::string::npos) {
|
||||
variant = "legacy";
|
||||
} else if (package_id.find("com.miHoYo.Yuanshen") != std::string::npos) {
|
||||
variant = "optimized";
|
||||
} else {
|
||||
#ifdef ARCHITECTURE_arm64
|
||||
variant = "standard";
|
||||
#else
|
||||
variant = "chromeos";
|
||||
#endif
|
||||
jclass updateResultClass = env->FindClass("org/yuzu/yuzu_emu/NativeLibrary$UpdateResult");
|
||||
if (!updateResultClass) {
|
||||
LOG_ERROR(Frontend, "Could not find UpdateResult class");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const std::string apk_filename = fmt::format("Eden-Android-{}-{}.apk", artifact_str, variant);
|
||||
jmethodID updateResultCtor = env->GetMethodID(updateResultClass, "<init>", "()V");
|
||||
|
||||
const std::string url = fmt::format("https://{}/{}/{}",
|
||||
std::string{Common::g_build_auto_update_api},
|
||||
version_str, apk_filename);
|
||||
if (!updateResultCtor) {
|
||||
LOG_ERROR(Frontend, "Could not find UpdateResult ctor");
|
||||
env->DeleteLocalRef(updateResultClass);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
env->ReleaseStringUTFChars(tag, version_str);
|
||||
env->ReleaseStringUTFChars(artifact, artifact_str);
|
||||
env->ReleaseStringUTFChars(packageId, package_id_str);
|
||||
return env->NewStringUTF(url.c_str());
|
||||
jmethodID setTag = env->GetMethodID(updateResultClass, "setTag", "(Ljava/lang/String;)V");
|
||||
jmethodID setTitle = env->GetMethodID(updateResultClass, "setTitle", "(Ljava/lang/String;)V");
|
||||
jmethodID setBody = env->GetMethodID(updateResultClass, "setBody", "(Ljava/lang/String;)V");
|
||||
jmethodID setUrl = env->GetMethodID(updateResultClass, "setUrl", "(Ljava/lang/String;)V");
|
||||
jmethodID addAsset = env->GetMethodID(updateResultClass, "addAsset", "(Ljava/lang/String;)V");
|
||||
|
||||
jobject updateResult = env->NewObject(updateResultClass, updateResultCtor);
|
||||
|
||||
LOG_DEBUG(Frontend, "Tag: {}", tag);
|
||||
LOG_DEBUG(Frontend, "Title: {}", title);
|
||||
LOG_DEBUG(Frontend, "Body: {}", body);
|
||||
LOG_DEBUG(Frontend, "Url: {}", url);
|
||||
|
||||
const auto jtag = env->NewStringUTF(tag.c_str());
|
||||
const auto jtitle = env->NewStringUTF(title.c_str());
|
||||
const auto jbody = env->NewStringUTF(body.c_str());
|
||||
const auto jurl = env->NewStringUTF(url.c_str());
|
||||
|
||||
env->CallVoidMethod(updateResult, setTag, jtag);
|
||||
env->CallVoidMethod(updateResult, setTitle, jtitle);
|
||||
env->CallVoidMethod(updateResult, setBody, jbody);
|
||||
env->CallVoidMethod(updateResult, setUrl, jurl);
|
||||
|
||||
// TODO(crueter): Handling for multiple assets?
|
||||
// Maybe another data class x(
|
||||
for (const Common::Net::Asset &a : assets) {
|
||||
const auto jaurl = env->NewStringUTF(a.path.c_str());
|
||||
env->CallVoidMethod(updateResult, addAsset, jaurl);
|
||||
env->DeleteLocalRef(jaurl);
|
||||
}
|
||||
|
||||
env->DeleteLocalRef(jtag);
|
||||
env->DeleteLocalRef(jtitle);
|
||||
env->DeleteLocalRef(jbody);
|
||||
env->DeleteLocalRef(jurl);
|
||||
|
||||
env->DeleteLocalRef(updateResultClass);
|
||||
|
||||
return updateResult;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
JNIEXPORT jstring JNICALL Java_org_yuzu_yuzu_1emu_NativeLibrary_getBuildVersion(
|
||||
|
|
|
|||
|
|
@ -1783,5 +1783,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||
|
||||
<string name="external_content">External Content</string>
|
||||
<string name="add_folders">Add Folder</string>
|
||||
<string name="percent">%1$d%%</string>
|
||||
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -78,9 +78,10 @@ std::vector<Asset> Release::GetPlatformAssets() const {
|
|||
#elif defined(ARCHITECTURE_arm64)
|
||||
#ifdef YUZU_LEGACY
|
||||
find_asset("Standard", {"legacy.apk"});
|
||||
#elif defined(GENSHIN_SPOOF)
|
||||
find_asset("Standard", {"optimized.apk"});
|
||||
#else
|
||||
find_asset("Standard", {"standard.apk"});
|
||||
find_asset("Genshin Spoof", {"optimized.apk"});
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
|
@ -132,7 +133,7 @@ std::optional<Release> Release::FromJson(const nlohmann::json& json, const std::
|
|||
rel.host = host;
|
||||
|
||||
const auto release_base =
|
||||
fmt::format("{}/{}/releases", Common::g_build_auto_update_stable_api, repo);
|
||||
fmt::format("{}/{}/releases", host, repo);
|
||||
const auto fallback_html = fmt::format("{}/tag/{}", release_base, rel.tag);
|
||||
rel.html_url = json.value("html_url", fallback_html);
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue