mirror of
https://git.eden-emu.dev/eden-emu/eden.git
synced 2025-10-12 13:37:47 +00:00
[frontend, web] refactor: web service frontend rewrite (#221)
- Automatic verification based on regex - Token generation button - Removed unneeded links - public lobby creation [android] Signed-off-by: crueter <swurl@swurl.xyz> Co-authored-by: Aleksandr Popovich <alekpopo@pm.me> Co-authored-by: Aleksandr Popovich <alekpopo@proton.me> Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/221 Co-authored-by: crueter <swurl@swurl.xyz> Co-committed-by: crueter <swurl@swurl.xyz>
This commit is contained in:
parent
2fe728766e
commit
94c66f98bf
34 changed files with 1985 additions and 327 deletions
|
@ -1,7 +1,6 @@
|
|||
// SPDX-FileCopyrightText: 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
|
||||
package org.yuzu.yuzu_emu.dialogs
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
|
@ -19,6 +18,7 @@ import com.google.android.material.bottomsheet.BottomSheetDialog
|
|||
import org.yuzu.yuzu_emu.R
|
||||
import org.yuzu.yuzu_emu.databinding.DialogChatBinding
|
||||
import org.yuzu.yuzu_emu.databinding.ItemChatMessageBinding
|
||||
import org.yuzu.yuzu_emu.features.settings.model.StringSetting
|
||||
import org.yuzu.yuzu_emu.network.NetPlayManager
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
|
@ -82,7 +82,7 @@ class ChatDialog(context: Context) : BottomSheetDialog(context) {
|
|||
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
private fun sendMessage(message: String) {
|
||||
val username = NetPlayManager.getUsername(context)
|
||||
val username = StringSetting.WEB_USERNAME.getString()
|
||||
NetPlayManager.netPlaySendMessage(message)
|
||||
|
||||
val chatMessage = ChatMessage(
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// SPDX-FileCopyrightText: 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
package org.yuzu.yuzu_emu.dialogs
|
||||
|
@ -28,6 +28,7 @@ import info.debatty.java.stringsimilarity.JaroWinkler
|
|||
import org.yuzu.yuzu_emu.R
|
||||
import org.yuzu.yuzu_emu.databinding.DialogLobbyBrowserBinding
|
||||
import org.yuzu.yuzu_emu.databinding.ItemLobbyRoomBinding
|
||||
import org.yuzu.yuzu_emu.features.settings.model.StringSetting
|
||||
import org.yuzu.yuzu_emu.network.NetPlayManager
|
||||
import java.util.Locale
|
||||
|
||||
|
@ -144,7 +145,7 @@ class LobbyBrowser(context: Context) : BottomSheetDialog(context) {
|
|||
}
|
||||
|
||||
private fun joinRoom(room: NetPlayManager.RoomInfo, password: String) {
|
||||
val username = NetPlayManager.getUsername(context)
|
||||
val username = StringSetting.WEB_USERNAME.getString()
|
||||
|
||||
Thread {
|
||||
val result = NetPlayManager.netPlayJoinRoom(room.ip, room.port, username, password)
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// SPDX-FileCopyrightText: 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
package org.yuzu.yuzu_emu.dialogs
|
||||
|
@ -24,6 +24,7 @@ import androidx.recyclerview.widget.RecyclerView
|
|||
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialog
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.google.android.material.textfield.TextInputLayout
|
||||
import org.yuzu.yuzu_emu.R
|
||||
import org.yuzu.yuzu_emu.YuzuApplication
|
||||
import org.yuzu.yuzu_emu.databinding.DialogMultiplayerConnectBinding
|
||||
|
@ -32,6 +33,7 @@ import org.yuzu.yuzu_emu.databinding.DialogMultiplayerRoomBinding
|
|||
import org.yuzu.yuzu_emu.databinding.ItemBanListBinding
|
||||
import org.yuzu.yuzu_emu.databinding.ItemButtonNetplayBinding
|
||||
import org.yuzu.yuzu_emu.databinding.ItemTextNetplayBinding
|
||||
import org.yuzu.yuzu_emu.features.settings.model.StringSetting
|
||||
import org.yuzu.yuzu_emu.network.NetPlayManager
|
||||
import org.yuzu.yuzu_emu.utils.CompatUtils
|
||||
import org.yuzu.yuzu_emu.utils.GameHelper
|
||||
|
@ -180,9 +182,9 @@ class NetPlayDialog(context: Context) : BottomSheetDialog(context) {
|
|||
PopupMenu(view.context, view).apply {
|
||||
menuInflater.inflate(R.menu.menu_netplay_member, menu)
|
||||
menu.findItem(R.id.action_kick).isEnabled = isModerator &&
|
||||
netPlayItems.name != NetPlayManager.getUsername(context)
|
||||
netPlayItems.name != StringSetting.WEB_USERNAME.getString()
|
||||
menu.findItem(R.id.action_ban).isEnabled = isModerator &&
|
||||
netPlayItems.name != NetPlayManager.getUsername(context)
|
||||
netPlayItems.name != StringSetting.WEB_USERNAME.getString()
|
||||
setOnMenuItemClickListener { item ->
|
||||
if (item.itemId == R.id.action_kick) {
|
||||
NetPlayManager.netPlayKickUser(netPlayItems.name)
|
||||
|
@ -297,13 +299,13 @@ class NetPlayDialog(context: Context) : BottomSheetDialog(context) {
|
|||
|
||||
abstract class TextValidatorWatcher(
|
||||
private val btnConfirm: Button,
|
||||
private val view: EditText,
|
||||
private val layout: TextInputLayout,
|
||||
private val errorMessage: String
|
||||
) : TextWatcher {
|
||||
|
||||
companion object {
|
||||
val validStates: HashMap<EditText, Boolean> = hashMapOf()
|
||||
val validStates: HashMap<TextInputLayout, Boolean> = hashMapOf()
|
||||
}
|
||||
|
||||
abstract fun validate(s: String): Boolean
|
||||
|
||||
override fun beforeTextChanged(
|
||||
|
@ -325,20 +327,20 @@ class NetPlayDialog(context: Context) : BottomSheetDialog(context) {
|
|||
override fun afterTextChanged(s: Editable?) {
|
||||
val input = s.toString()
|
||||
val isValid = validate(input)
|
||||
view.error = if (isValid) null else errorMessage
|
||||
layout.isErrorEnabled = !isValid
|
||||
layout.error = if (isValid) null else errorMessage
|
||||
|
||||
validStates.put(view, isValid)
|
||||
validStates[layout] = isValid
|
||||
btnConfirm.isEnabled = !validStates.containsValue(false)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(alekpop, crueter): Properly handle getting banned (both during and in future connects)
|
||||
private fun showNetPlayInputDialog(isCreateRoom: Boolean) {
|
||||
TextValidatorWatcher.validStates.clear()
|
||||
val activity = CompatUtils.findActivity(context)
|
||||
val dialog = BottomSheetDialog(activity)
|
||||
|
||||
val validStates: HashMap<EditText, Boolean> = hashMapOf()
|
||||
|
||||
dialog.behavior.state = BottomSheetBehavior.STATE_EXPANDED
|
||||
dialog.behavior.state = BottomSheetBehavior.STATE_EXPANDED
|
||||
dialog.behavior.skipCollapsed =
|
||||
|
@ -347,6 +349,11 @@ class NetPlayDialog(context: Context) : BottomSheetDialog(context) {
|
|||
val binding = DialogMultiplayerRoomBinding.inflate(LayoutInflater.from(activity))
|
||||
dialog.setContentView(binding.root)
|
||||
|
||||
val visibilityList: List<String> = listOf(
|
||||
context.getString(R.string.multiplayer_public_visibility),
|
||||
context.getString(R.string.multiplayer_unlisted_visibility),
|
||||
)
|
||||
|
||||
binding.textTitle.text = activity.getString(
|
||||
if (isCreateRoom) R.string.multiplayer_create_room
|
||||
else R.string.multiplayer_join_room
|
||||
|
@ -355,7 +362,7 @@ class NetPlayDialog(context: Context) : BottomSheetDialog(context) {
|
|||
// setup listeners etc
|
||||
val roomNameWatcher = object : TextValidatorWatcher(
|
||||
binding.btnConfirm, // TODO(alekpop, crueter): Figure out a better way to deal with this?
|
||||
binding.roomName,
|
||||
binding.layoutRoomName,
|
||||
context.getString(
|
||||
R.string.multiplayer_room_name_error
|
||||
)
|
||||
|
@ -367,25 +374,32 @@ class NetPlayDialog(context: Context) : BottomSheetDialog(context) {
|
|||
|
||||
val preferredWatcher = object : TextValidatorWatcher(
|
||||
binding.btnConfirm,
|
||||
binding.dropdownPreferredGameName,
|
||||
binding.preferredGameName,
|
||||
context.getString(R.string.multiplayer_required)
|
||||
) {
|
||||
override fun validate(s: String): Boolean {
|
||||
return s.isNotEmpty()
|
||||
}
|
||||
}
|
||||
|
||||
override fun afterTextChanged(s: Editable?) {
|
||||
super.afterTextChanged(s)
|
||||
val visibilityWatcher = object : TextValidatorWatcher(
|
||||
binding.btnConfirm,
|
||||
binding.lobbyVisibility,
|
||||
context.getString(R.string.multiplayer_token_required)
|
||||
) {
|
||||
override fun validate(s: String): Boolean {
|
||||
if (s != context.getString(R.string.multiplayer_public_visibility)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// special case: remove dropdown arrow
|
||||
val input = s.toString()
|
||||
binding.preferredGameName.isEndIconVisible = validate(input)
|
||||
val token = StringSetting.WEB_TOKEN.getString()
|
||||
return token.matches(Regex("[a-z]{48}"))
|
||||
}
|
||||
}
|
||||
|
||||
val ipWatcher = object : TextValidatorWatcher(
|
||||
binding.btnConfirm,
|
||||
binding.ipAddress,
|
||||
binding.layoutIpAddress,
|
||||
context.getString(R.string.multiplayer_ip_error)
|
||||
) {
|
||||
override fun validate(s: String): Boolean {
|
||||
|
@ -400,17 +414,17 @@ class NetPlayDialog(context: Context) : BottomSheetDialog(context) {
|
|||
|
||||
val usernameWatcher = object : TextValidatorWatcher(
|
||||
binding.btnConfirm,
|
||||
binding.username,
|
||||
binding.layoutUsername,
|
||||
context.getString(R.string.multiplayer_username_error)
|
||||
) {
|
||||
override fun validate(s: String): Boolean {
|
||||
return s.length >= 5
|
||||
return s.length in 4..20
|
||||
}
|
||||
}
|
||||
|
||||
val portWatcher = object : TextValidatorWatcher(
|
||||
binding.btnConfirm,
|
||||
binding.ipPort,
|
||||
binding.layoutIpPort,
|
||||
context.getString(R.string.multiplayer_port_error)
|
||||
) {
|
||||
override fun validate(s: String): Boolean {
|
||||
|
@ -421,6 +435,7 @@ class NetPlayDialog(context: Context) : BottomSheetDialog(context) {
|
|||
if (isCreateRoom) {
|
||||
binding.roomName.addTextChangedListener(roomNameWatcher)
|
||||
binding.dropdownPreferredGameName.addTextChangedListener(preferredWatcher)
|
||||
binding.dropdownLobbyVisibility.addTextChangedListener(visibilityWatcher)
|
||||
|
||||
binding.dropdownPreferredGameName.apply {
|
||||
setAdapter(
|
||||
|
@ -431,19 +446,35 @@ class NetPlayDialog(context: Context) : BottomSheetDialog(context) {
|
|||
)
|
||||
)
|
||||
}
|
||||
|
||||
binding.dropdownLobbyVisibility.setText(context.getString(R.string.multiplayer_unlisted_visibility))
|
||||
|
||||
binding.dropdownLobbyVisibility.apply {
|
||||
setAdapter(
|
||||
ArrayAdapter(
|
||||
activity,
|
||||
R.layout.dropdown_item,
|
||||
visibilityList
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
binding.ipAddress.addTextChangedListener(ipWatcher)
|
||||
binding.ipPort.addTextChangedListener(portWatcher)
|
||||
binding.username.addTextChangedListener(usernameWatcher)
|
||||
|
||||
binding.ipAddress.setText(NetPlayManager.getRoomAddress(activity))
|
||||
binding.ipPort.setText(NetPlayManager.getRoomPort(activity))
|
||||
binding.username.setText(NetPlayManager.getUsername(activity))
|
||||
binding.username.setText(StringSetting.WEB_USERNAME.getString())
|
||||
|
||||
// manually trigger text listeners
|
||||
if (isCreateRoom) {
|
||||
roomNameWatcher.afterTextChanged(binding.roomName.text)
|
||||
preferredWatcher.afterTextChanged(binding.dropdownPreferredGameName.text)
|
||||
|
||||
// It's not needed here, the watcher is called by the initial set method
|
||||
// visibilityWatcher.afterTextChanged(binding.dropdownLobbyVisibility.text)
|
||||
}
|
||||
|
||||
ipWatcher.afterTextChanged(binding.ipAddress.text)
|
||||
|
@ -451,8 +482,10 @@ class NetPlayDialog(context: Context) : BottomSheetDialog(context) {
|
|||
usernameWatcher.afterTextChanged(binding.username.text)
|
||||
|
||||
binding.preferredGameName.visibility = if (isCreateRoom) View.VISIBLE else View.GONE
|
||||
binding.lobbyVisibility.visibility = if (isCreateRoom) View.VISIBLE else View.GONE
|
||||
binding.roomName.visibility = if (isCreateRoom) View.VISIBLE else View.GONE
|
||||
binding.maxPlayersContainer.visibility = if (isCreateRoom) View.VISIBLE else View.GONE
|
||||
|
||||
binding.maxPlayersLabel.text = context.getString(
|
||||
R.string.multiplayer_max_players_value,
|
||||
binding.maxPlayers.value.toInt()
|
||||
|
@ -464,7 +497,6 @@ class NetPlayDialog(context: Context) : BottomSheetDialog(context) {
|
|||
}
|
||||
|
||||
// TODO(alekpop, crueter): Room descriptions
|
||||
// TODO(alekpop, crueter): Public room creation
|
||||
// TODO(alekpop, crueter): Preview preferred games
|
||||
binding.btnConfirm.setOnClickListener {
|
||||
binding.btnConfirm.isEnabled = false
|
||||
|
@ -487,6 +519,9 @@ class NetPlayDialog(context: Context) : BottomSheetDialog(context) {
|
|||
val preferredIdx = gameNameList.indexOfFirst { it[0] == preferredGameName }
|
||||
val preferredGameId = if (preferredIdx == -1) 0 else gameIdList[preferredIdx][0]
|
||||
|
||||
val visibility = binding.dropdownLobbyVisibility.text.toString()
|
||||
val isPublic = visibility == context.getString(R.string.multiplayer_public_visibility)
|
||||
|
||||
Handler(Looper.getMainLooper()).post {
|
||||
val result = if (isCreateRoom) {
|
||||
NetPlayManager.netPlayCreateRoom(
|
||||
|
@ -497,23 +532,26 @@ class NetPlayDialog(context: Context) : BottomSheetDialog(context) {
|
|||
preferredGameId,
|
||||
password,
|
||||
roomName,
|
||||
maxPlayers
|
||||
maxPlayers,
|
||||
isPublic
|
||||
)
|
||||
} else {
|
||||
NetPlayManager.netPlayJoinRoom(ipAddress, port, username, password)
|
||||
}
|
||||
|
||||
if (result == 0) {
|
||||
// TODO(alekpop, crueter): These need to be moved as settings, editable in a tab
|
||||
NetPlayManager.setUsername(activity, username)
|
||||
StringSetting.WEB_USERNAME.setString(username)
|
||||
NetPlayManager.setRoomPort(activity, portStr)
|
||||
NetPlayManager.setRoomAddress(activity, ipAddress)
|
||||
if (!isCreateRoom) NetPlayManager.setRoomAddress(activity, ipAddress)
|
||||
|
||||
Toast.makeText(
|
||||
YuzuApplication.appContext,
|
||||
if (isCreateRoom) R.string.multiplayer_create_room_success
|
||||
else R.string.multiplayer_join_room_success,
|
||||
Toast.LENGTH_LONG
|
||||
).show()
|
||||
|
||||
dialog.dismiss()
|
||||
} else {
|
||||
Toast.makeText(
|
||||
|
@ -521,6 +559,7 @@ class NetPlayDialog(context: Context) : BottomSheetDialog(context) {
|
|||
R.string.multiplayer_could_not_connect,
|
||||
Toast.LENGTH_LONG
|
||||
).show()
|
||||
|
||||
binding.btnConfirm.isEnabled = true
|
||||
binding.btnConfirm.text = activity.getString(R.string.ok)
|
||||
}
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
|
@ -9,9 +12,8 @@ enum class StringSetting(override val key: String) : AbstractStringSetting {
|
|||
DRIVER_PATH("driver_path"),
|
||||
DEVICE_NAME("device_name"),
|
||||
|
||||
// TODO(crueter, alekpop): Netplay/settings needs to be properly worked into settings
|
||||
WEB_TOKEN("yuzu_token"),
|
||||
WEB_USERNAME("yuzu_username"),
|
||||
WEB_TOKEN("eden_token"),
|
||||
WEB_USERNAME("eden_username"),
|
||||
;
|
||||
|
||||
override fun getString(needsGlobal: Boolean): String = NativeConfig.getString(key, needsGlobal)
|
||||
|
|
|
@ -284,11 +284,32 @@ abstract class SettingsItem(
|
|||
descriptionId = R.string.use_custom_rtc_description
|
||||
)
|
||||
)
|
||||
|
||||
put(
|
||||
StringInputSetting(
|
||||
StringSetting.WEB_TOKEN,
|
||||
titleId = R.string.web_token,
|
||||
descriptionId = R.string.web_token_description
|
||||
descriptionId = R.string.web_token_description,
|
||||
onGenerate = {
|
||||
val chars = "abcdefghijklmnopqrstuvwxyz"
|
||||
(1..48).map { chars.random() }.joinToString("")
|
||||
},
|
||||
validator = { s ->
|
||||
s?.matches(Regex("[a-z]{48}")) == true
|
||||
},
|
||||
errorId = R.string.multiplayer_token_error
|
||||
)
|
||||
)
|
||||
|
||||
put(
|
||||
StringInputSetting(
|
||||
StringSetting.WEB_USERNAME,
|
||||
titleId = R.string.web_username,
|
||||
descriptionId = R.string.web_username_description,
|
||||
validator = { s ->
|
||||
s?.length in 4..20
|
||||
},
|
||||
errorId = R.string.multiplayer_username_error
|
||||
)
|
||||
)
|
||||
put(DateTimeSetting(LongSetting.CUSTOM_RTC, titleId = R.string.set_custom_rtc))
|
||||
|
|
|
@ -1,8 +1,12 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
package org.yuzu.yuzu_emu.features.settings.model.view
|
||||
|
||||
import android.text.Editable
|
||||
import androidx.annotation.StringRes
|
||||
import org.yuzu.yuzu_emu.features.settings.model.AbstractStringSetting
|
||||
|
||||
|
@ -11,7 +15,10 @@ class StringInputSetting(
|
|||
@StringRes titleId: Int = 0,
|
||||
titleString: String = "",
|
||||
@StringRes descriptionId: Int = 0,
|
||||
descriptionString: String = ""
|
||||
descriptionString: String = "",
|
||||
val onGenerate: (() -> String)? = null,
|
||||
val validator: ((s: String?) -> Boolean)? = null,
|
||||
@StringRes val errorId: Int = 0
|
||||
) : SettingsItem(setting, titleId, titleString, descriptionId, descriptionString) {
|
||||
override val type = TYPE_STRING_INPUT
|
||||
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
|
@ -6,9 +9,13 @@ package org.yuzu.yuzu_emu.features.settings.ui
|
|||
import android.app.Dialog
|
||||
import android.content.DialogInterface
|
||||
import android.os.Bundle
|
||||
import android.text.Editable
|
||||
import android.text.TextWatcher
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.EditText
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import androidx.fragment.app.activityViewModels
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
|
@ -138,6 +145,46 @@ class SettingsDialogFragment : DialogFragment(), DialogInterface.OnClickListener
|
|||
stringInputBinding = DialogEditTextBinding.inflate(layoutInflater)
|
||||
val item = settingsViewModel.clickedItem as StringInputSetting
|
||||
stringInputBinding.editText.setText(item.getSelectedValue())
|
||||
|
||||
val onGenerate = item.onGenerate
|
||||
stringInputBinding.generate.isVisible = onGenerate != null
|
||||
|
||||
if (onGenerate != null) {
|
||||
stringInputBinding.generate.setOnClickListener {
|
||||
stringInputBinding.editText.setText(onGenerate())
|
||||
}
|
||||
}
|
||||
|
||||
val validator = item.validator
|
||||
|
||||
if (validator != null) {
|
||||
val watcher = object : TextWatcher {
|
||||
override fun beforeTextChanged(
|
||||
s: CharSequence?,
|
||||
start: Int,
|
||||
count: Int,
|
||||
after: Int
|
||||
) {
|
||||
}
|
||||
|
||||
override fun onTextChanged(
|
||||
s: CharSequence?,
|
||||
start: Int,
|
||||
before: Int,
|
||||
count: Int
|
||||
) {
|
||||
}
|
||||
|
||||
override fun afterTextChanged(s: Editable?) {
|
||||
stringInputBinding.editText.error =
|
||||
if (validator(s.toString())) null else requireContext().getString(item.errorId)
|
||||
}
|
||||
}
|
||||
|
||||
stringInputBinding.editText.addTextChangedListener(watcher)
|
||||
watcher.afterTextChanged(stringInputBinding.editText.text)
|
||||
}
|
||||
|
||||
MaterialAlertDialogBuilder(requireContext())
|
||||
.setTitle(item.title)
|
||||
.setView(stringInputBinding.root)
|
||||
|
|
|
@ -212,30 +212,38 @@ class SettingsFragmentPresenter(
|
|||
add(BooleanSetting.USE_CUSTOM_RTC.key)
|
||||
add(LongSetting.CUSTOM_RTC.key)
|
||||
|
||||
// TODO(alekpop): Add functionality
|
||||
// add(HeaderSetting(R.string.network))
|
||||
// add(StringSetting.WEB_TOKEN.key)
|
||||
add(HeaderSetting(R.string.network))
|
||||
add(StringSetting.WEB_TOKEN.key)
|
||||
add(StringSetting.WEB_USERNAME.key)
|
||||
}
|
||||
}
|
||||
|
||||
private fun addGraphicsSettings(sl: ArrayList<SettingsItem>) {
|
||||
sl.apply {
|
||||
// TODO(crueter): reorganize this, this is awful
|
||||
add(HeaderSetting(R.string.backend))
|
||||
|
||||
add(IntSetting.RENDERER_ACCURACY.key)
|
||||
add(IntSetting.RENDERER_RESOLUTION.key)
|
||||
add(IntSetting.RENDERER_VSYNC.key)
|
||||
add(IntSetting.RENDERER_SCALING_FILTER.key)
|
||||
add(IntSetting.FSR_SHARPENING_SLIDER.key)
|
||||
add(IntSetting.RENDERER_ANTI_ALIASING.key)
|
||||
add(IntSetting.MAX_ANISOTROPY.key)
|
||||
add(IntSetting.RENDERER_SCREEN_LAYOUT.key)
|
||||
add(IntSetting.RENDERER_ASPECT_RATIO.key)
|
||||
add(IntSetting.VERTICAL_ALIGNMENT.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)
|
||||
add(BooleanSetting.RENDERER_REACTIVE_FLUSHING.key)
|
||||
|
||||
add(HeaderSetting(R.string.processing))
|
||||
|
||||
add(IntSetting.RENDERER_VSYNC.key)
|
||||
add(IntSetting.RENDERER_ANTI_ALIASING.key)
|
||||
add(IntSetting.MAX_ANISOTROPY.key)
|
||||
add(IntSetting.RENDERER_SCALING_FILTER.key)
|
||||
add(IntSetting.FSR_SHARPENING_SLIDER.key)
|
||||
|
||||
add(HeaderSetting(R.string.display))
|
||||
|
||||
add(IntSetting.RENDERER_SCREEN_LAYOUT.key)
|
||||
add(IntSetting.RENDERER_ASPECT_RATIO.key)
|
||||
add(IntSetting.VERTICAL_ALIGNMENT.key)
|
||||
add(BooleanSetting.PICTURE_IN_PICTURE.key)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// SPDX-FileCopyrightText: 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
package org.yuzu.yuzu_emu.network
|
||||
|
@ -16,6 +16,7 @@ import org.yuzu.yuzu_emu.R
|
|||
import org.yuzu.yuzu_emu.YuzuApplication
|
||||
import org.yuzu.yuzu_emu.dialogs.ChatMessage
|
||||
import java.net.Inet4Address
|
||||
import androidx.core.content.edit
|
||||
|
||||
object NetPlayManager {
|
||||
external fun netPlayCreateRoom(
|
||||
|
@ -26,7 +27,8 @@ object NetPlayManager {
|
|||
preferredGameId: Long,
|
||||
password: String,
|
||||
roomName: String,
|
||||
maxPlayers: Int
|
||||
maxPlayers: Int,
|
||||
isPublic: Boolean
|
||||
): Int
|
||||
|
||||
external fun netPlayJoinRoom(
|
||||
|
@ -125,17 +127,6 @@ object NetPlayManager {
|
|||
adapterRefreshListener = listener
|
||||
}
|
||||
|
||||
fun getUsername(activity: Context): String {
|
||||
val prefs = PreferenceManager.getDefaultSharedPreferences(activity)
|
||||
val name = "Eden${(Math.random() * 100).toInt()}"
|
||||
return prefs.getString("NetPlayUsername", name) ?: name
|
||||
}
|
||||
|
||||
fun setUsername(activity: Activity, name: String) {
|
||||
val prefs = PreferenceManager.getDefaultSharedPreferences(activity)
|
||||
prefs.edit().putString("NetPlayUsername", name).apply()
|
||||
}
|
||||
|
||||
fun getRoomAddress(activity: Activity): String {
|
||||
val prefs = PreferenceManager.getDefaultSharedPreferences(activity)
|
||||
val address = getIpAddressByWifi(activity)
|
||||
|
@ -144,7 +135,7 @@ object NetPlayManager {
|
|||
|
||||
fun setRoomAddress(activity: Activity, address: String) {
|
||||
val prefs = PreferenceManager.getDefaultSharedPreferences(activity)
|
||||
prefs.edit().putString("NetPlayRoomAddress", address).apply()
|
||||
prefs.edit { putString("NetPlayRoomAddress", address) }
|
||||
}
|
||||
|
||||
fun getRoomPort(activity: Activity): String {
|
||||
|
@ -154,7 +145,7 @@ object NetPlayManager {
|
|||
|
||||
fun setRoomPort(activity: Activity, port: String) {
|
||||
val prefs = PreferenceManager.getDefaultSharedPreferences(activity)
|
||||
prefs.edit().putString("NetPlayRoomPort", port).apply()
|
||||
prefs.edit { putString("NetPlayRoomPort", port) }
|
||||
}
|
||||
|
||||
private val chatMessages = mutableListOf<ChatMessage>()
|
||||
|
|
|
@ -975,12 +975,12 @@ Java_org_yuzu_yuzu_1emu_network_NetPlayManager_netPlayGetPublicRooms(
|
|||
JNIEXPORT jint JNICALL Java_org_yuzu_yuzu_1emu_network_NetPlayManager_netPlayCreateRoom(
|
||||
JNIEnv* env, [[maybe_unused]] jobject obj, jstring ipaddress, jint port,
|
||||
jstring username, jstring preferredGameName, jlong preferredGameId, jstring password,
|
||||
jstring room_name, jint max_players) {
|
||||
jstring room_name, jint max_players, jboolean isPublic) {
|
||||
return static_cast<jint>(
|
||||
multiplayer->NetPlayCreateRoom(Common::Android::GetJString(env, ipaddress), port,
|
||||
Common::Android::GetJString(env, username), Common::Android::GetJString(env, preferredGameName),
|
||||
preferredGameId,Common::Android::GetJString(env, password),
|
||||
Common::Android::GetJString(env, room_name), max_players));
|
||||
Common::Android::GetJString(env, room_name), max_players, isPublic));
|
||||
}
|
||||
|
||||
JNIEXPORT jint JNICALL Java_org_yuzu_yuzu_1emu_network_NetPlayManager_netPlayJoinRoom(
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
|
@ -20,4 +21,13 @@
|
|||
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<Button
|
||||
android:id="@+id/generate"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="15dp"
|
||||
android:text="@string/generate"
|
||||
app:layout_constraintEnd_toEndOf="@+id/edit_text_layout"
|
||||
app:layout_constraintTop_toBottomOf="@+id/edit_text_layout" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
android:textColor="?attr/colorOnSurface" />
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/layout_ip_address"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="@string/multiplayer_ip_address"
|
||||
|
@ -34,6 +35,7 @@
|
|||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/layout_ip_port"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="@string/multiplayer_ip_port"
|
||||
|
@ -47,6 +49,7 @@
|
|||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/layout_username"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="@string/multiplayer_username"
|
||||
|
@ -89,6 +92,7 @@
|
|||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/layout_room_name"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="@string/multiplayer_room_name"
|
||||
|
@ -125,6 +129,21 @@
|
|||
android:text="@string/multiplayer_max_players_value" />
|
||||
</LinearLayout>
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/lobby_visibility"
|
||||
style="@style/Widget.Material3.TextInputLayout.OutlinedBox.ExposedDropdownMenu"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:hint="@string/multiplayer_lobby_type"
|
||||
android:padding="8dp">
|
||||
|
||||
<com.google.android.material.textfield.MaterialAutoCompleteTextView
|
||||
android:id="@+id/dropdown_lobby_visibility"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:inputType="none" />
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btn_confirm"
|
||||
android:layout_width="wrap_content"
|
||||
|
|
1453
src/android/app/src/main/res/values-sr/strings.xml
Normal file
1453
src/android/app/src/main/res/values-sr/strings.xml
Normal file
File diff suppressed because it is too large
Load diff
|
@ -62,6 +62,7 @@
|
|||
<item>@string/language_spanish</item>
|
||||
<item>@string/language_taiwanese</item>
|
||||
<item>@string/language_traditional_chinese</item>
|
||||
<item>@string/language_serbian</item>
|
||||
</string-array>
|
||||
|
||||
<integer-array name="languageValues">
|
||||
|
@ -83,6 +84,7 @@
|
|||
<item>5</item>
|
||||
<item>11</item>
|
||||
<item>16</item>
|
||||
<item>18</item>
|
||||
</integer-array>
|
||||
|
||||
<string-array name="rendererApiNames">
|
||||
|
|
|
@ -204,18 +204,22 @@
|
|||
<string name="emulation_multiplayer">Multiplayer</string>
|
||||
<string name="multiplayer_game_name">Preferred Games</string>
|
||||
<string name="multiplayer_preferred_game_name">Preferred Game</string>
|
||||
<string name="multiplayer_lobby_type">Lobby Type</string>
|
||||
<string name="multiplayer_no_game">No Games Found</string>
|
||||
<string name="multiplayer_preferred_game_name_invalid">You must choose a Preferred Game to host a room.</string>
|
||||
<string name="multiplayer_room_name_error">Must be between 3 and 20 characters</string>
|
||||
<string name="multiplayer_required">Required</string>
|
||||
<string name="multiplayer_token_required">Web Token required, go to Advanced Settings -> System -> Network</string>
|
||||
<string name="multiplayer_ip_error">Invalid IP format</string>
|
||||
<string name="multiplayer_username_error">Must be at least 5 characters</string>
|
||||
<string name="multiplayer_username_error">Must be between 4–20 characters</string>
|
||||
<string name="multiplayer_token_error">Must be 48 characters, and lowercase a-z only</string>
|
||||
<string name="multiplayer_port_error">Must be between 1 and 65535</string>
|
||||
<string name="cancel">Cancel</string>
|
||||
<string name="ok">Ok</string>
|
||||
<string name="refresh">Refresh</string>
|
||||
<string name="room_list">Room List</string>
|
||||
|
||||
<string name="multiplayer_public_visibility">Public</string>
|
||||
<string name="multiplayer_unlisted_visibility">Unlisted</string>
|
||||
|
||||
<!-- Setup strings -->
|
||||
<string name="welcome">Welcome!</string>
|
||||
|
@ -443,12 +447,20 @@
|
|||
<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="generate">Generate</string>
|
||||
|
||||
<!-- Network settings strings -->
|
||||
<string name="web_token">Web Token</string>
|
||||
<string name="web_token_description">Web token used for creating public lobbies. It is a 48-character string containing only lowercase a-z.</string>
|
||||
<string name="web_username">Web Username</string>
|
||||
<string name="web_username_description">Username to be shown in multiplayer lobbies. It must be 4–20 characters.</string>
|
||||
<string name="network">Network</string>
|
||||
|
||||
<!-- Graphics settings strings -->
|
||||
<string name="backend">Backend</string>
|
||||
<string name="display">Display</string>
|
||||
<string name="processing">Post-Processing</string>
|
||||
|
||||
<string name="frame_skipping">WIP: Frameskip</string>
|
||||
<string name="frame_skipping_description">Toggle frame skipping to improve performance by reducing the number of rendered frames. This feature is still being worked on and will be enabled in future releases.</string>
|
||||
<string name="renderer_accuracy">Accuracy level</string>
|
||||
|
@ -820,6 +832,7 @@
|
|||
<string name="language_simplified_chinese" translatable="false">简体中文</string>
|
||||
<string name="language_traditional_chinese" translatable="false">正體中文</string>
|
||||
<string name="language_brazilian_portuguese" translatable="false">Português do Brasil</string>
|
||||
<string name="language_serbian" translatable="false">српски</string>
|
||||
|
||||
<!-- Memory Sizes -->
|
||||
<string name="memory_byte">Byte</string>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue