diff --git a/src/android/app/src/main/AndroidManifest.xml b/src/android/app/src/main/AndroidManifest.xml
index eef5660425..ddf52ce961 100644
--- a/src/android/app/src/main/AndroidManifest.xml
+++ b/src/android/app/src/main/AndroidManifest.xml
@@ -11,6 +11,7 @@ SPDX-License-Identifier: GPL-3.0-or-later
+
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/YuzuApplication.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/YuzuApplication.kt
index 4c947b7869..9fd9add6de 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/YuzuApplication.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/YuzuApplication.kt
@@ -10,6 +10,7 @@ import android.content.Context
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.NetworkHelper
import java.io.File
fun Context.getPublicFilesDir() : File = getExternalFilesDir(null) ?: filesDir
@@ -46,6 +47,7 @@ class YuzuApplication : Application() {
documentsTree = DocumentsTree()
DirectoryInitialization.start(applicationContext)
GpuDriverHelper.initializeDriverParameters(applicationContext)
+ NetworkHelper.setRoutes(applicationContext)
NativeLibrary.logDeviceInfo()
createNotificationChannels();
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/Settings.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/Settings.kt
index 8df20b928f..4eefa0bcce 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/Settings.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/Settings.kt
@@ -107,6 +107,7 @@ class Settings {
const val SECTION_RENDERER = "Renderer"
const val SECTION_AUDIO = "Audio"
const val SECTION_CPU = "Cpu"
+ const val SECTION_NETWORK = "Network"
const val SECTION_THEME = "Theme"
const val SECTION_DEBUG = "Debug"
@@ -151,6 +152,7 @@ class Settings {
SECTION_SYSTEM,
SECTION_RENDERER,
SECTION_AUDIO,
+ SECTION_NETWORK,
SECTION_CPU
)
}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/StringSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/StringSetting.kt
index 63f95690c7..999f080cb0 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/StringSetting.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/StringSetting.kt
@@ -8,7 +8,8 @@ enum class StringSetting(
override val section: String,
override val defaultValue: String
) : AbstractStringSetting {
- CUSTOM_RTC("custom_rtc", Settings.SECTION_SYSTEM, "0");
+ CUSTOM_RTC("custom_rtc", Settings.SECTION_SYSTEM, "0"),
+ NETWORK_ROUTE("network_route", Settings.SECTION_NETWORK, ";;;;");
override var string: String = defaultValue
@@ -27,7 +28,8 @@ enum class StringSetting(
companion object {
private val NOT_RUNTIME_EDITABLE = listOf(
- CUSTOM_RTC
+ CUSTOM_RTC,
+ NETWORK_ROUTE
)
fun from(key: String): StringSetting? = StringSetting.values().firstOrNull { it.key == key }
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt
index 07520849ec..d12b9331ee 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt
@@ -35,5 +35,6 @@ abstract class SettingsItem(
const val TYPE_STRING_SINGLE_CHOICE = 5
const val TYPE_DATETIME_SETTING = 6
const val TYPE_RUNNABLE = 7
+ const val TYPE_TEXT_SETTING = 8
}
}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/TextSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/TextSetting.kt
new file mode 100644
index 0000000000..c1fc51c16f
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/TextSetting.kt
@@ -0,0 +1,31 @@
+// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package org.yuzu.yuzu_emu.features.settings.model.view
+
+import org.yuzu.yuzu_emu.features.settings.model.AbstractSetting
+import org.yuzu.yuzu_emu.features.settings.model.AbstractStringSetting
+
+class TextSetting(
+ setting: AbstractSetting?,
+ titleId: Int,
+ descriptionId: Int,
+ val key: String? = null,
+ private val defaultValue: String? = null
+) : SettingsItem(setting, titleId, descriptionId) {
+ override val type = TYPE_TEXT_SETTING
+
+ val value: String
+ get() = if (setting != null) {
+ val setting = setting as AbstractStringSetting
+ setting.string
+ } else {
+ defaultValue!!
+ }
+
+ fun setSelectedValue(string: String): AbstractStringSetting {
+ val stringSetting = setting as AbstractStringSetting
+ stringSetting.string = string
+ return stringSetting
+ }
+}
\ No newline at end of file
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsAdapter.kt
index 1eb4899fcb..2dc205a7f5 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsAdapter.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsAdapter.kt
@@ -7,13 +7,14 @@ import android.content.Context
import android.content.DialogInterface
import android.icu.util.Calendar
import android.icu.util.TimeZone
+import android.text.TextWatcher
import android.text.format.DateFormat
import android.view.LayoutInflater
import android.view.ViewGroup
import android.widget.TextView
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
-import androidx.fragment.app.setFragmentResultListener
+import androidx.core.widget.doAfterTextChanged
import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.datepicker.MaterialDatePicker
import com.google.android.material.dialog.MaterialAlertDialogBuilder
@@ -21,6 +22,7 @@ import com.google.android.material.slider.Slider
import com.google.android.material.timepicker.MaterialTimePicker
import com.google.android.material.timepicker.TimeFormat
import org.yuzu.yuzu_emu.R
+import org.yuzu.yuzu_emu.databinding.DialogEditTextBinding
import org.yuzu.yuzu_emu.databinding.DialogSliderBinding
import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding
import org.yuzu.yuzu_emu.databinding.ListItemSettingSwitchBinding
@@ -83,6 +85,10 @@ class SettingsAdapter(
RunnableViewHolder(ListItemSettingBinding.inflate(inflater), this)
}
+ SettingsItem.TYPE_TEXT_SETTING -> {
+ TextSettingViewHolder(ListItemSettingBinding.inflate(inflater), this)
+ }
+
else -> {
// TODO: Create an error view since we can't return null now
HeaderViewHolder(ListItemSettingsHeaderBinding.inflate(inflater), this)
@@ -167,6 +173,7 @@ class SettingsAdapter(
.setSelection(storedTime)
.setTitleText(R.string.select_rtc_date)
.build()
+
val timePicker: MaterialTimePicker = MaterialTimePicker.Builder()
.setTimeFormat(timeFormat)
.setHour(calendar.get(Calendar.HOUR_OF_DAY))
@@ -199,6 +206,38 @@ class SettingsAdapter(
)
}
+ fun onTextSettingClick(item: TextSetting, position: Int) {
+ clickedItem = item
+ clickedPosition = position
+ var value = item.value
+
+ val inflater = LayoutInflater.from(context)
+ val editTextBinding = DialogEditTextBinding.inflate(inflater)
+
+ editTextBinding.editText.setText(value)
+
+ editTextBinding.editText.doAfterTextChanged {
+ value = it.toString()
+ }
+
+ dialog = MaterialAlertDialogBuilder(context)
+ .setTitle(item.nameId)
+ .setView(editTextBinding.root)
+ .setPositiveButton(android.R.string.ok) { _, _ ->
+ if (item.value != value) {
+ fragmentView.onSettingChanged()
+ }
+ notifyItemChanged(clickedPosition)
+
+ val setting = item.setSelectedValue(value)
+ fragmentView.putSetting(setting)
+ clickedItem = null
+ }
+ .setNegativeButton(android.R.string.cancel, defaultCancelListener)
+ .show()
+
+ }
+
fun onSliderClick(item: SliderSetting, position: Int) {
clickedItem = item
clickedPosition = position
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt
index 061046b2e7..e64f3760b9 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt
@@ -7,7 +7,6 @@ import android.content.SharedPreferences
import android.os.Build
import android.text.TextUtils
import androidx.preference.PreferenceManager
-import com.google.android.material.dialog.MaterialAlertDialogBuilder
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.YuzuApplication
import org.yuzu.yuzu_emu.features.settings.model.AbstractBooleanSetting
@@ -67,6 +66,7 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
Settings.SECTION_SYSTEM -> addSystemSettings(sl)
Settings.SECTION_RENDERER -> addGraphicsSettings(sl)
Settings.SECTION_AUDIO -> addAudioSettings(sl)
+ Settings.SECTION_NETWORK -> addNetworkSettings(sl)
Settings.SECTION_THEME -> addThemeSettings(sl)
Settings.SECTION_DEBUG -> addDebugSettings(sl)
else -> {
@@ -102,6 +102,13 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
Settings.SECTION_RENDERER
)
)
+ add(
+ SubmenuSetting(
+ R.string.preferences_network,
+ 0,
+ Settings.SECTION_NETWORK
+ )
+ )
add(
SubmenuSetting(
R.string.preferences_audio,
@@ -437,6 +444,22 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
}
}
+ private fun addNetworkSettings(sl: ArrayList) {
+ settingsActivity.setToolbarTitle(settingsActivity.getString(R.string.preferences_network))
+
+ sl.apply {
+ add(
+ TextSetting(
+ StringSetting.NETWORK_ROUTE,
+ R.string.set_network_route,
+ R.string.network_route_desc,
+ StringSetting.NETWORK_ROUTE.key,
+ StringSetting.NETWORK_ROUTE.defaultValue
+ )
+ )
+ }
+ }
+
private fun addDebugSettings(sl: ArrayList) {
settingsActivity.setToolbarTitle(settingsActivity.getString(R.string.preferences_debug))
sl.apply {
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/TextSettingViewHolder.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/TextSettingViewHolder.kt
new file mode 100644
index 0000000000..9557b07fca
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/TextSettingViewHolder.kt
@@ -0,0 +1,44 @@
+// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package org.yuzu.yuzu_emu.features.settings.ui.viewholder
+
+import android.view.View
+import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding
+import org.yuzu.yuzu_emu.features.settings.model.view.DateTimeSetting
+import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
+import org.yuzu.yuzu_emu.features.settings.model.view.TextSetting
+import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter
+import java.time.Instant
+import java.time.ZoneId
+import java.time.ZonedDateTime
+import java.time.format.DateTimeFormatter
+import java.time.format.FormatStyle
+
+class TextSettingViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAdapter) :
+ SettingViewHolder(binding.root, adapter) {
+ private lateinit var setting: TextSetting
+
+ override fun bind(item: SettingsItem) {
+ setting = item as TextSetting
+
+ binding.textSettingName.setText(item.nameId)
+ if (item.descriptionId != 0) {
+ binding.textSettingDescription.setText(item.descriptionId)
+ binding.textSettingDescription.visibility = View.VISIBLE
+ }
+ }
+
+ override fun onClick(clicked: View) {
+ if (setting.isEditable) {
+ adapter.onTextSettingClick(setting, bindingAdapterPosition)
+ }
+ }
+
+ override fun onLongClick(clicked: View): Boolean {
+ if (setting.isEditable) {
+ return adapter.onLongClick(setting.setting!!, bindingAdapterPosition)
+ }
+ return false
+ }
+}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/NetworkHelper.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/NetworkHelper.kt
new file mode 100644
index 0000000000..b5b32b5aad
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/NetworkHelper.kt
@@ -0,0 +1,20 @@
+package org.yuzu.yuzu_emu.utils
+
+import android.content.Context
+import android.net.ConnectivityManager
+
+
+
+
+object NetworkHelper {
+ fun setRoutes(context: Context) {
+ val connectivity =
+ context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
+
+ val lp = connectivity.getLinkProperties(connectivity.activeNetwork)
+
+ // lp.routes
+
+
+ }
+}
\ No newline at end of file
diff --git a/src/android/app/src/main/jni/config.cpp b/src/android/app/src/main/jni/config.cpp
index 2d622a048c..b96dd1f7ee 100644
--- a/src/android/app/src/main/jni/config.cpp
+++ b/src/android/app/src/main/jni/config.cpp
@@ -244,6 +244,11 @@ void Config::ReadValues() {
ReadSetting("Audio", Settings::values.audio_output_device_id);
ReadSetting("Audio", Settings::values.volume);
+ // Network
+
+ Settings::values.network_route =
+ config->GetString("Network", "network_route", "");
+
// Miscellaneous
// log_filter has a different default here than from common
Settings::values.log_filter = "*:Info";
diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml
index 0ae69afb47..a2358a93ab 100644
--- a/src/android/app/src/main/res/values/strings.xml
+++ b/src/android/app/src/main/res/values/strings.xml
@@ -151,6 +151,10 @@
Allows you to set a custom real-time clock separate from your current system time.
Set custom RTC
+
+ Sets the default network route
+ Set network route
+
API
Accuracy level
@@ -203,6 +207,7 @@
Settings
General
System
+ Network
Graphics
Audio
Theme and color
diff --git a/src/common/settings.h b/src/common/settings.h
index 9682281b08..0613d6f596 100644
--- a/src/common/settings.h
+++ b/src/common/settings.h
@@ -581,6 +581,7 @@ struct Values {
// Network
Setting network_interface{std::string(), "network_interface"};
+ Setting network_route{std::string(), "network_route"};
// WebService
Setting enable_telemetry{true, "enable_telemetry"};
diff --git a/src/core/internal_network/network_interface.cpp b/src/core/internal_network/network_interface.cpp
index 4c909a6d3c..0c84db932c 100644
--- a/src/core/internal_network/network_interface.cpp
+++ b/src/core/internal_network/network_interface.cpp
@@ -6,6 +6,8 @@
#include
#include
+#include
+
#include "common/bit_cast.h"
#include "common/common_types.h"
#include "common/logging/log.h"
@@ -20,6 +22,8 @@
#include
#include
#include
+#include
+
#endif
namespace Network {
@@ -91,8 +95,31 @@ std::vector GetAvailableNetworkInterfaces() {
return result;
}
-#else
+#elif defined(__ANDROID__)
+std::vector GetAvailableNetworkInterfaces() {
+ std::vector result;
+ std::vector route_parts;
+
+ boost::split(route_parts, Settings::values.network_route.GetValue(), boost::is_any_of(";"));
+
+ struct in_addr ip{}, sm{}, gw{};
+
+ inet_pton(AF_INET, route_parts[1].c_str(), &ip);
+ inet_pton(AF_INET, route_parts[2].c_str(), &sm);
+ inet_pton(AF_INET, route_parts[3].c_str(), &gw);
+
+ result.emplace_back(NetworkInterface{
+ .name{route_parts[0]},
+ .ip_address{ip},
+ .subnet_mask{sm},
+ .gateway{gw}
+ });
+
+ return result;
+}
+
+#else
std::vector GetAvailableNetworkInterfaces() {
struct ifaddrs* ifaddr = nullptr;
@@ -187,6 +214,10 @@ std::vector GetAvailableNetworkInterfaces() {
#endif
std::optional GetSelectedNetworkInterface() {
+#ifdef __ANDROID__
+ Network::SelectFirstNetworkInterface(); // TODO ANDROID
+#endif
+
const auto& selected_network_interface = Settings::values.network_interface.GetValue();
const auto network_interfaces = Network::GetAvailableNetworkInterfaces();
if (network_interfaces.empty()) {