Fixed network interfaces on Android

This commit is contained in:
Timotej Leginus 2023-06-10 22:52:50 +02:00
commit bfdc9e312f
14 changed files with 212 additions and 5 deletions

View file

@ -11,6 +11,7 @@ SPDX-License-Identifier: GPL-3.0-or-later
<uses-feature android:name="android.software.leanback" android:required="false" /> <uses-feature android:name="android.software.leanback" android:required="false" />
<uses-feature android:name="android.hardware.vulkan.version" android:version="0x401000" android:required="true" /> <uses-feature android:name="android.hardware.vulkan.version" android:version="0x401000" android:required="true" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.NFC" /> <uses-permission android:name="android.permission.NFC" />

View file

@ -10,6 +10,7 @@ import android.content.Context
import org.yuzu.yuzu_emu.utils.DirectoryInitialization import org.yuzu.yuzu_emu.utils.DirectoryInitialization
import org.yuzu.yuzu_emu.utils.DocumentsTree import org.yuzu.yuzu_emu.utils.DocumentsTree
import org.yuzu.yuzu_emu.utils.GpuDriverHelper import org.yuzu.yuzu_emu.utils.GpuDriverHelper
import org.yuzu.yuzu_emu.utils.NetworkHelper
import java.io.File import java.io.File
fun Context.getPublicFilesDir() : File = getExternalFilesDir(null) ?: filesDir fun Context.getPublicFilesDir() : File = getExternalFilesDir(null) ?: filesDir
@ -46,6 +47,7 @@ class YuzuApplication : Application() {
documentsTree = DocumentsTree() documentsTree = DocumentsTree()
DirectoryInitialization.start(applicationContext) DirectoryInitialization.start(applicationContext)
GpuDriverHelper.initializeDriverParameters(applicationContext) GpuDriverHelper.initializeDriverParameters(applicationContext)
NetworkHelper.setRoutes(applicationContext)
NativeLibrary.logDeviceInfo() NativeLibrary.logDeviceInfo()
createNotificationChannels(); createNotificationChannels();

View file

@ -107,6 +107,7 @@ class Settings {
const val SECTION_RENDERER = "Renderer" const val SECTION_RENDERER = "Renderer"
const val SECTION_AUDIO = "Audio" const val SECTION_AUDIO = "Audio"
const val SECTION_CPU = "Cpu" const val SECTION_CPU = "Cpu"
const val SECTION_NETWORK = "Network"
const val SECTION_THEME = "Theme" const val SECTION_THEME = "Theme"
const val SECTION_DEBUG = "Debug" const val SECTION_DEBUG = "Debug"
@ -151,6 +152,7 @@ class Settings {
SECTION_SYSTEM, SECTION_SYSTEM,
SECTION_RENDERER, SECTION_RENDERER,
SECTION_AUDIO, SECTION_AUDIO,
SECTION_NETWORK,
SECTION_CPU SECTION_CPU
) )
} }

View file

@ -8,7 +8,8 @@ enum class StringSetting(
override val section: String, override val section: String,
override val defaultValue: String override val defaultValue: String
) : AbstractStringSetting { ) : 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 override var string: String = defaultValue
@ -27,7 +28,8 @@ enum class StringSetting(
companion object { companion object {
private val NOT_RUNTIME_EDITABLE = listOf( private val NOT_RUNTIME_EDITABLE = listOf(
CUSTOM_RTC CUSTOM_RTC,
NETWORK_ROUTE
) )
fun from(key: String): StringSetting? = StringSetting.values().firstOrNull { it.key == key } fun from(key: String): StringSetting? = StringSetting.values().firstOrNull { it.key == key }

View file

@ -35,5 +35,6 @@ abstract class SettingsItem(
const val TYPE_STRING_SINGLE_CHOICE = 5 const val TYPE_STRING_SINGLE_CHOICE = 5
const val TYPE_DATETIME_SETTING = 6 const val TYPE_DATETIME_SETTING = 6
const val TYPE_RUNNABLE = 7 const val TYPE_RUNNABLE = 7
const val TYPE_TEXT_SETTING = 8
} }
} }

View file

@ -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
}
}

View file

@ -7,13 +7,14 @@ import android.content.Context
import android.content.DialogInterface import android.content.DialogInterface
import android.icu.util.Calendar import android.icu.util.Calendar
import android.icu.util.TimeZone import android.icu.util.TimeZone
import android.text.TextWatcher
import android.text.format.DateFormat import android.text.format.DateFormat
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.TextView import android.widget.TextView
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.setFragmentResultListener import androidx.core.widget.doAfterTextChanged
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.datepicker.MaterialDatePicker import com.google.android.material.datepicker.MaterialDatePicker
import com.google.android.material.dialog.MaterialAlertDialogBuilder 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.MaterialTimePicker
import com.google.android.material.timepicker.TimeFormat import com.google.android.material.timepicker.TimeFormat
import org.yuzu.yuzu_emu.R 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.DialogSliderBinding
import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding
import org.yuzu.yuzu_emu.databinding.ListItemSettingSwitchBinding import org.yuzu.yuzu_emu.databinding.ListItemSettingSwitchBinding
@ -83,6 +85,10 @@ class SettingsAdapter(
RunnableViewHolder(ListItemSettingBinding.inflate(inflater), this) RunnableViewHolder(ListItemSettingBinding.inflate(inflater), this)
} }
SettingsItem.TYPE_TEXT_SETTING -> {
TextSettingViewHolder(ListItemSettingBinding.inflate(inflater), this)
}
else -> { else -> {
// TODO: Create an error view since we can't return null now // TODO: Create an error view since we can't return null now
HeaderViewHolder(ListItemSettingsHeaderBinding.inflate(inflater), this) HeaderViewHolder(ListItemSettingsHeaderBinding.inflate(inflater), this)
@ -167,6 +173,7 @@ class SettingsAdapter(
.setSelection(storedTime) .setSelection(storedTime)
.setTitleText(R.string.select_rtc_date) .setTitleText(R.string.select_rtc_date)
.build() .build()
val timePicker: MaterialTimePicker = MaterialTimePicker.Builder() val timePicker: MaterialTimePicker = MaterialTimePicker.Builder()
.setTimeFormat(timeFormat) .setTimeFormat(timeFormat)
.setHour(calendar.get(Calendar.HOUR_OF_DAY)) .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) { fun onSliderClick(item: SliderSetting, position: Int) {
clickedItem = item clickedItem = item
clickedPosition = position clickedPosition = position

View file

@ -7,7 +7,6 @@ import android.content.SharedPreferences
import android.os.Build import android.os.Build
import android.text.TextUtils import android.text.TextUtils
import androidx.preference.PreferenceManager import androidx.preference.PreferenceManager
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import org.yuzu.yuzu_emu.R import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.YuzuApplication import org.yuzu.yuzu_emu.YuzuApplication
import org.yuzu.yuzu_emu.features.settings.model.AbstractBooleanSetting 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_SYSTEM -> addSystemSettings(sl)
Settings.SECTION_RENDERER -> addGraphicsSettings(sl) Settings.SECTION_RENDERER -> addGraphicsSettings(sl)
Settings.SECTION_AUDIO -> addAudioSettings(sl) Settings.SECTION_AUDIO -> addAudioSettings(sl)
Settings.SECTION_NETWORK -> addNetworkSettings(sl)
Settings.SECTION_THEME -> addThemeSettings(sl) Settings.SECTION_THEME -> addThemeSettings(sl)
Settings.SECTION_DEBUG -> addDebugSettings(sl) Settings.SECTION_DEBUG -> addDebugSettings(sl)
else -> { else -> {
@ -102,6 +102,13 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
Settings.SECTION_RENDERER Settings.SECTION_RENDERER
) )
) )
add(
SubmenuSetting(
R.string.preferences_network,
0,
Settings.SECTION_NETWORK
)
)
add( add(
SubmenuSetting( SubmenuSetting(
R.string.preferences_audio, R.string.preferences_audio,
@ -437,6 +444,22 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
} }
} }
private fun addNetworkSettings(sl: ArrayList<SettingsItem>) {
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<SettingsItem>) { private fun addDebugSettings(sl: ArrayList<SettingsItem>) {
settingsActivity.setToolbarTitle(settingsActivity.getString(R.string.preferences_debug)) settingsActivity.setToolbarTitle(settingsActivity.getString(R.string.preferences_debug))
sl.apply { sl.apply {

View file

@ -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
}
}

View file

@ -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
}
}

View file

@ -244,6 +244,11 @@ void Config::ReadValues() {
ReadSetting("Audio", Settings::values.audio_output_device_id); ReadSetting("Audio", Settings::values.audio_output_device_id);
ReadSetting("Audio", Settings::values.volume); ReadSetting("Audio", Settings::values.volume);
// Network
Settings::values.network_route =
config->GetString("Network", "network_route", "");
// Miscellaneous // Miscellaneous
// log_filter has a different default here than from common // log_filter has a different default here than from common
Settings::values.log_filter = "*:Info"; Settings::values.log_filter = "*:Info";

View file

@ -151,6 +151,10 @@
<string name="use_custom_rtc_description">Allows you to set a custom real-time clock separate from your current system time.</string> <string name="use_custom_rtc_description">Allows you to set a custom real-time clock separate from your current system time.</string>
<string name="set_custom_rtc">Set custom RTC</string> <string name="set_custom_rtc">Set custom RTC</string>
<!-- Network settings strings -->
<string name="network_route_desc">Sets the default network route</string>
<string name="set_network_route">Set network route</string>
<!-- Graphics settings strings --> <!-- Graphics settings strings -->
<string name="renderer_api">API</string> <string name="renderer_api">API</string>
<string name="renderer_accuracy">Accuracy level</string> <string name="renderer_accuracy">Accuracy level</string>
@ -203,6 +207,7 @@
<string name="preferences_settings">Settings</string> <string name="preferences_settings">Settings</string>
<string name="preferences_general">General</string> <string name="preferences_general">General</string>
<string name="preferences_system">System</string> <string name="preferences_system">System</string>
<string name="preferences_network">Network</string>
<string name="preferences_graphics">Graphics</string> <string name="preferences_graphics">Graphics</string>
<string name="preferences_audio">Audio</string> <string name="preferences_audio">Audio</string>
<string name="preferences_theme">Theme and color</string> <string name="preferences_theme">Theme and color</string>

View file

@ -581,6 +581,7 @@ struct Values {
// Network // Network
Setting<std::string> network_interface{std::string(), "network_interface"}; Setting<std::string> network_interface{std::string(), "network_interface"};
Setting<std::string> network_route{std::string(), "network_route"};
// WebService // WebService
Setting<bool> enable_telemetry{true, "enable_telemetry"}; Setting<bool> enable_telemetry{true, "enable_telemetry"};

View file

@ -6,6 +6,8 @@
#include <sstream> #include <sstream>
#include <vector> #include <vector>
#include <boost/algorithm/string.hpp>
#include "common/bit_cast.h" #include "common/bit_cast.h"
#include "common/common_types.h" #include "common/common_types.h"
#include "common/logging/log.h" #include "common/logging/log.h"
@ -20,6 +22,8 @@
#include <cerrno> #include <cerrno>
#include <ifaddrs.h> #include <ifaddrs.h>
#include <net/if.h> #include <net/if.h>
#include <arpa/inet.h>
#endif #endif
namespace Network { namespace Network {
@ -91,8 +95,31 @@ std::vector<NetworkInterface> GetAvailableNetworkInterfaces() {
return result; return result;
} }
#else #elif defined(__ANDROID__)
std::vector<NetworkInterface> GetAvailableNetworkInterfaces() {
std::vector<NetworkInterface> result;
std::vector<std::string> 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<NetworkInterface> GetAvailableNetworkInterfaces() { std::vector<NetworkInterface> GetAvailableNetworkInterfaces() {
struct ifaddrs* ifaddr = nullptr; struct ifaddrs* ifaddr = nullptr;
@ -187,6 +214,10 @@ std::vector<NetworkInterface> GetAvailableNetworkInterfaces() {
#endif #endif
std::optional<NetworkInterface> GetSelectedNetworkInterface() { std::optional<NetworkInterface> GetSelectedNetworkInterface() {
#ifdef __ANDROID__
Network::SelectFirstNetworkInterface(); // TODO ANDROID
#endif
const auto& selected_network_interface = Settings::values.network_interface.GetValue(); const auto& selected_network_interface = Settings::values.network_interface.GetValue();
const auto network_interfaces = Network::GetAvailableNetworkInterfaces(); const auto network_interfaces = Network::GetAvailableNetworkInterfaces();
if (network_interfaces.empty()) { if (network_interfaces.empty()) {