mirror of
https://github.com/skyline-emu/skyline.git
synced 2024-12-23 10:01:50 +01:00
Use property delegate to handle preferences
* Add option to disable joystick recentering
This commit is contained in:
parent
5c4aa95da6
commit
7526a985fb
@ -15,9 +15,9 @@ import android.util.Log
|
||||
import android.view.*
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.view.isInvisible
|
||||
import androidx.preference.PreferenceManager
|
||||
import emu.skyline.input.*
|
||||
import emu.skyline.loader.getRomFormat
|
||||
import emu.skyline.utils.Settings
|
||||
import kotlinx.android.synthetic.main.emu_activity.*
|
||||
import java.io.File
|
||||
import kotlin.math.abs
|
||||
@ -36,11 +36,6 @@ class EmulationActivity : AppCompatActivity(), SurfaceHolder.Callback, View.OnTo
|
||||
*/
|
||||
private var vibrators = HashMap<Int, Vibrator>()
|
||||
|
||||
/**
|
||||
* A boolean flag denoting the current operation mode of the emulator (Docked = true/Handheld = false)
|
||||
*/
|
||||
private var operationMode = true
|
||||
|
||||
/**
|
||||
* The surface object used for displaying frames
|
||||
*/
|
||||
@ -63,6 +58,8 @@ class EmulationActivity : AppCompatActivity(), SurfaceHolder.Callback, View.OnTo
|
||||
*/
|
||||
private lateinit var emulationThread : Thread
|
||||
|
||||
private val settings by lazy { Settings(this) }
|
||||
|
||||
/**
|
||||
* This is the entry point into the emulation code for libskyline
|
||||
*
|
||||
@ -149,7 +146,7 @@ class EmulationActivity : AppCompatActivity(), SurfaceHolder.Callback, View.OnTo
|
||||
if (controller.type != ControllerType.None) {
|
||||
val type = when (controller.type) {
|
||||
ControllerType.None -> throw IllegalArgumentException()
|
||||
ControllerType.HandheldProController -> if (operationMode) ControllerType.ProController.id else ControllerType.HandheldProController.id
|
||||
ControllerType.HandheldProController -> if (settings.operationMode) ControllerType.ProController.id else ControllerType.HandheldProController.id
|
||||
ControllerType.ProController, ControllerType.JoyConLeft, ControllerType.JoyConRight -> controller.type.id
|
||||
}
|
||||
|
||||
@ -212,9 +209,7 @@ class EmulationActivity : AppCompatActivity(), SurfaceHolder.Callback, View.OnTo
|
||||
|
||||
game_view.holder.addCallback(this)
|
||||
|
||||
val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this)
|
||||
|
||||
if (sharedPreferences.getBoolean("perf_stats", false)) {
|
||||
if (settings.perfStats) {
|
||||
perf_stats.postDelayed(object : Runnable {
|
||||
override fun run() {
|
||||
perf_stats.text = "${getFps()} FPS\n${getFrametime()}ms"
|
||||
@ -223,15 +218,13 @@ class EmulationActivity : AppCompatActivity(), SurfaceHolder.Callback, View.OnTo
|
||||
}, 250)
|
||||
}
|
||||
|
||||
operationMode = sharedPreferences.getBoolean("operation_mode", operationMode)
|
||||
|
||||
@Suppress("DEPRECATION") val display = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) display!! else windowManager.defaultDisplay
|
||||
display?.supportedModes?.maxBy { it.refreshRate + (it.physicalHeight * it.physicalWidth) }?.let { window.attributes.preferredDisplayModeId = it.modeId }
|
||||
|
||||
game_view.setOnTouchListener(this)
|
||||
|
||||
// Hide on screen controls when first controller is not set
|
||||
on_screen_controller_view.isInvisible = !InputManager.controllers[0]!!.type.firstController || !sharedPreferences.getBoolean("on_screen_control", false)
|
||||
on_screen_controller_view.isInvisible = !InputManager.controllers[0]!!.type.firstController || !settings.onScreenControl
|
||||
on_screen_controller_view.setOnButtonStateChangedListener(::onButtonStateChanged)
|
||||
on_screen_controller_view.setOnStickStateChangedListener(::onStickStateChanged)
|
||||
|
||||
|
@ -14,13 +14,13 @@ import android.view.MenuItem
|
||||
import android.widget.Toast
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.appcompat.widget.SearchView
|
||||
import androidx.preference.PreferenceManager
|
||||
import androidx.recyclerview.widget.DividerItemDecoration
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import emu.skyline.adapter.GenericAdapter
|
||||
import emu.skyline.adapter.HeaderViewItem
|
||||
import emu.skyline.adapter.LogViewItem
|
||||
import emu.skyline.utils.Settings
|
||||
import kotlinx.android.synthetic.main.log_activity.*
|
||||
import kotlinx.android.synthetic.main.titlebar.*
|
||||
import org.json.JSONObject
|
||||
@ -52,9 +52,10 @@ class LogActivity : AppCompatActivity() {
|
||||
setSupportActionBar(toolbar)
|
||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||
|
||||
val prefs = PreferenceManager.getDefaultSharedPreferences(this)
|
||||
val compact = prefs.getBoolean("log_compact", false)
|
||||
val logLevel = prefs.getString("log_level", "3")!!.toInt()
|
||||
val settings = Settings(this)
|
||||
|
||||
val compact = settings.logCompact
|
||||
val logLevel = settings.logLevel.toInt()
|
||||
val logLevels = resources.getStringArray(R.array.log_level)
|
||||
|
||||
log_list.adapter = adapter
|
||||
|
@ -37,6 +37,9 @@ import emu.skyline.data.ElementType
|
||||
import emu.skyline.loader.LoaderResult
|
||||
import emu.skyline.loader.RomFile
|
||||
import emu.skyline.loader.RomFormat
|
||||
import emu.skyline.utils.Settings
|
||||
import emu.skyline.utils.loadSerializedList
|
||||
import emu.skyline.utils.serialize
|
||||
import kotlinx.android.synthetic.main.main_activity.*
|
||||
import kotlinx.android.synthetic.main.titlebar.*
|
||||
import java.io.File
|
||||
@ -50,10 +53,7 @@ class MainActivity : AppCompatActivity() {
|
||||
private val TAG = MainActivity::class.java.simpleName
|
||||
}
|
||||
|
||||
/**
|
||||
* This is used to get/set shared preferences
|
||||
*/
|
||||
private val sharedPreferences by lazy { PreferenceManager.getDefaultSharedPreferences(this) }
|
||||
private val settings by lazy { Settings(this) }
|
||||
|
||||
/**
|
||||
* The adapter used for adding elements to [app_list]
|
||||
@ -62,7 +62,7 @@ class MainActivity : AppCompatActivity() {
|
||||
|
||||
private var reloading = AtomicBoolean()
|
||||
|
||||
private val layoutType get() = LayoutType.values()[sharedPreferences.getString("layout_type", "1")!!.toInt()]
|
||||
private val layoutType get() = LayoutType.values()[settings.layoutType.toInt()]
|
||||
|
||||
private val missingIcon by lazy { ContextCompat.getDrawable(this, R.drawable.default_icon)!!.toBitmap(256, 256) }
|
||||
|
||||
@ -134,7 +134,7 @@ class MainActivity : AppCompatActivity() {
|
||||
try {
|
||||
runOnUiThread { adapter.removeAllItems() }
|
||||
|
||||
val searchLocation = DocumentFile.fromTreeUri(this, Uri.parse(sharedPreferences.getString("search_location", "")))!!
|
||||
val searchLocation = DocumentFile.fromTreeUri(this, Uri.parse(settings.searchLocation))!!
|
||||
|
||||
val romElements = ArrayList<BaseElement>()
|
||||
addEntries("nro", RomFormat.NRO, searchLocation, romElements)
|
||||
@ -155,10 +155,10 @@ class MainActivity : AppCompatActivity() {
|
||||
}
|
||||
}
|
||||
|
||||
sharedPreferences.edit().putBoolean("refresh_required", false).apply()
|
||||
settings.refreshRequired = false
|
||||
} catch (e : IllegalArgumentException) {
|
||||
runOnUiThread {
|
||||
sharedPreferences.edit().remove("search_location").apply()
|
||||
settings.searchLocation = ""
|
||||
|
||||
val intent = intent
|
||||
finish()
|
||||
@ -191,7 +191,7 @@ class MainActivity : AppCompatActivity() {
|
||||
|
||||
PreferenceManager.setDefaultValues(this, R.xml.preferences, false)
|
||||
|
||||
AppCompatDelegate.setDefaultNightMode(when ((sharedPreferences.getString("app_theme", "2")?.toInt())) {
|
||||
AppCompatDelegate.setDefaultNightMode(when ((settings.appTheme.toInt())) {
|
||||
0 -> AppCompatDelegate.MODE_NIGHT_NO
|
||||
1 -> AppCompatDelegate.MODE_NIGHT_YES
|
||||
2 -> AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM
|
||||
@ -267,13 +267,13 @@ class MainActivity : AppCompatActivity() {
|
||||
}
|
||||
setAppListDecoration()
|
||||
|
||||
if (sharedPreferences.getString("search_location", "") == "") {
|
||||
if (settings.searchLocation.isEmpty()) {
|
||||
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE)
|
||||
intent.flags = Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION or Intent.FLAG_GRANT_PREFIX_URI_PERMISSION or Intent.FLAG_GRANT_READ_URI_PERMISSION
|
||||
|
||||
startActivityForResult(intent, 1)
|
||||
} else {
|
||||
refreshAdapter(!sharedPreferences.getBoolean("refresh_required", false))
|
||||
refreshAdapter(!settings.refreshRequired)
|
||||
}
|
||||
}
|
||||
|
||||
@ -301,7 +301,7 @@ class MainActivity : AppCompatActivity() {
|
||||
}
|
||||
|
||||
private fun selectStartGame(appItem : AppItem) {
|
||||
if (sharedPreferences.getBoolean("select_action", false))
|
||||
if (settings.selectAction)
|
||||
AppDialog.newInstance(appItem).show(supportFragmentManager, "game")
|
||||
else if (appItem.loaderResult == LoaderResult.Success)
|
||||
startActivity(Intent(this, EmulationActivity::class.java).apply { data = appItem.uri })
|
||||
@ -341,9 +341,9 @@ class MainActivity : AppCompatActivity() {
|
||||
1 -> {
|
||||
val uri = intent!!.data!!
|
||||
contentResolver.takePersistableUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||
sharedPreferences.edit().putString("search_location", uri.toString()).apply()
|
||||
settings.searchLocation = uri.toString()
|
||||
|
||||
refreshAdapter(!sharedPreferences.getBoolean("refresh_required", false))
|
||||
refreshAdapter(!settings.refreshRequired)
|
||||
}
|
||||
|
||||
2 -> {
|
||||
@ -361,8 +361,7 @@ class MainActivity : AppCompatActivity() {
|
||||
}
|
||||
|
||||
3 -> {
|
||||
if (sharedPreferences.getBoolean("refresh_required", false))
|
||||
refreshAdapter(false)
|
||||
if (settings.refreshRequired) refreshAdapter(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ package emu.skyline.adapter.controller
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.view.isGone
|
||||
import emu.skyline.R
|
||||
import emu.skyline.adapter.GenericLayoutFactory
|
||||
import emu.skyline.adapter.GenericViewHolder
|
||||
@ -22,7 +23,9 @@ class ControllerCheckBoxViewItem(var title : String, var summary : String, var c
|
||||
override fun getLayoutFactory() : GenericLayoutFactory = ControllerCheckBoxLayoutFactory
|
||||
|
||||
override fun bind(holder : GenericViewHolder, position : Int) {
|
||||
holder.text_title.isGone = title.isEmpty()
|
||||
holder.text_title.text = title
|
||||
holder.text_subtitle.isGone = summary.isEmpty()
|
||||
holder.text_subtitle.text = summary
|
||||
holder.checkbox.isChecked = checked
|
||||
holder.itemView.setOnClickListener {
|
||||
|
@ -9,7 +9,6 @@ import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.view.KeyEvent
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.preference.PreferenceManager
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import emu.skyline.R
|
||||
@ -20,6 +19,7 @@ import emu.skyline.input.dialog.ButtonDialog
|
||||
import emu.skyline.input.dialog.RumbleDialog
|
||||
import emu.skyline.input.dialog.StickDialog
|
||||
import emu.skyline.input.onscreen.OnScreenEditActivity
|
||||
import emu.skyline.utils.Settings
|
||||
import kotlinx.android.synthetic.main.controller_activity.*
|
||||
import kotlinx.android.synthetic.main.titlebar.*
|
||||
|
||||
@ -47,7 +47,7 @@ class ControllerActivity : AppCompatActivity() {
|
||||
*/
|
||||
val axisMap = mutableMapOf<AxisId, ControllerStickViewItem>()
|
||||
|
||||
private val sharedPrefs by lazy { PreferenceManager.getDefaultSharedPreferences(this) }
|
||||
private val settings by lazy { Settings(this) }
|
||||
|
||||
/**
|
||||
* This function updates the [adapter] based on information from [InputManager]
|
||||
@ -65,9 +65,15 @@ class ControllerActivity : AppCompatActivity() {
|
||||
if (id == 0 && controller.type.firstController) {
|
||||
adapter.addItem(HeaderViewItem(getString(R.string.osc)))
|
||||
|
||||
adapter.addItem(ControllerCheckBoxViewItem(getString(R.string.osc_enable), getString(R.string.osc_not_shown), sharedPrefs.getBoolean("on_screen_control", false)) { item, position ->
|
||||
item.summary = getString(if (item.checked) R.string.osc_shown else R.string.osc_not_shown)
|
||||
sharedPrefs.edit().putBoolean("on_screen_control", item.checked).apply()
|
||||
val oscSummary = { checked : Boolean -> getString(if (checked) R.string.osc_shown else R.string.osc_not_shown) }
|
||||
adapter.addItem(ControllerCheckBoxViewItem(getString(R.string.osc_enable), oscSummary.invoke(settings.onScreenControl), settings.onScreenControl) { item, position ->
|
||||
item.summary = oscSummary.invoke(item.checked)
|
||||
settings.onScreenControl = item.checked
|
||||
adapter.notifyItemChanged(position)
|
||||
})
|
||||
|
||||
adapter.addItem(ControllerCheckBoxViewItem(getString(R.string.osc_recenter_sticks), "", settings.onScreenControlRecenterSticks) { item, position ->
|
||||
settings.onScreenControlRecenterSticks = item.checked
|
||||
adapter.notifyItemChanged(position)
|
||||
})
|
||||
|
||||
|
@ -24,7 +24,7 @@ import kotlin.math.abs
|
||||
*
|
||||
* @param item This is used to hold the [ControllerButtonViewItem] between instances
|
||||
*/
|
||||
class ButtonDialog @JvmOverloads constructor(private val item : ControllerButtonViewItem? = null, private val position : Int? = null) : BottomSheetDialogFragment() {
|
||||
class ButtonDialog @JvmOverloads constructor(private val item : ControllerButtonViewItem? = null) : BottomSheetDialogFragment() {
|
||||
/**
|
||||
* This inflates the layout of the dialog after initial view creation
|
||||
*/
|
||||
|
@ -7,8 +7,7 @@ package emu.skyline.input.onscreen
|
||||
|
||||
import android.content.Context
|
||||
import emu.skyline.input.ButtonId
|
||||
import kotlin.properties.ReadWriteProperty
|
||||
import kotlin.reflect.KProperty
|
||||
import emu.skyline.utils.sharedPreferences
|
||||
|
||||
interface ControllerConfiguration {
|
||||
var enabled : Boolean
|
||||
@ -17,55 +16,18 @@ interface ControllerConfiguration {
|
||||
var relativeY : Float
|
||||
}
|
||||
|
||||
class ControllerConfigurationDummy(
|
||||
defaultRelativeX : Float,
|
||||
defaultRelativeY : Float
|
||||
) : ControllerConfiguration {
|
||||
class ControllerConfigurationDummy(defaultRelativeX : Float, defaultRelativeY : Float) : ControllerConfiguration {
|
||||
override var enabled = true
|
||||
override var globalScale = 1f
|
||||
override var relativeX = defaultRelativeX
|
||||
override var relativeY = defaultRelativeY
|
||||
}
|
||||
|
||||
class ControllerConfigurationImpl(
|
||||
private val context : Context,
|
||||
private val buttonId : ButtonId,
|
||||
defaultRelativeX : Float,
|
||||
defaultRelativeY : Float
|
||||
) : ControllerConfiguration {
|
||||
private inline fun <reified T> config(default : T) = ControllerPrefs(context, "${buttonId.name}_", T::class.java, default)
|
||||
class ControllerConfigurationImpl(private val context : Context, private val buttonId : ButtonId, defaultRelativeX : Float, defaultRelativeY : Float) : ControllerConfiguration {
|
||||
private inline fun <reified T> config(default : T, prefix : String = "${buttonId.name}_") = sharedPreferences(context, default, prefix, "controller_config")
|
||||
|
||||
override var enabled by config(true)
|
||||
override var globalScale by ControllerPrefs(context, "on_screen_controller_", Float::class.java, 1f)
|
||||
override var globalScale by config(1f, "")
|
||||
override var relativeX by config(defaultRelativeX)
|
||||
override var relativeY by config(defaultRelativeY)
|
||||
}
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
private class ControllerPrefs<T>(context : Context, private val prefix : String, private val clazz : Class<T>, private val default : T) : ReadWriteProperty<Any, T> {
|
||||
companion object {
|
||||
const val CONTROLLER_CONFIG = "controller_config"
|
||||
}
|
||||
|
||||
private val prefs = context.getSharedPreferences(CONTROLLER_CONFIG, Context.MODE_PRIVATE)
|
||||
|
||||
override fun setValue(thisRef : Any, property : KProperty<*>, value : T) {
|
||||
prefs.edit().apply {
|
||||
when (clazz) {
|
||||
Float::class.java, java.lang.Float::class.java -> putFloat(prefix + property.name, value as Float)
|
||||
Boolean::class.java, java.lang.Boolean::class.java -> putBoolean(prefix + property.name, value as Boolean)
|
||||
else -> error("Unsupported type $clazz ${Float::class.java}")
|
||||
}
|
||||
}.apply()
|
||||
}
|
||||
|
||||
override fun getValue(thisRef : Any, property : KProperty<*>) : T =
|
||||
prefs.let {
|
||||
@Suppress("IMPLICIT_CAST_TO_ANY")
|
||||
when (clazz) {
|
||||
Float::class.java, java.lang.Float::class.java -> it.getFloat(prefix + property.name, default as Float)
|
||||
Boolean::class.java, java.lang.Boolean::class.java -> it.getBoolean(prefix + property.name, default as Boolean)
|
||||
else -> error("Unsupported type $clazz")
|
||||
}
|
||||
} as T
|
||||
}
|
||||
|
@ -17,6 +17,9 @@ import android.view.View
|
||||
import android.view.View.OnTouchListener
|
||||
import emu.skyline.input.ButtonId
|
||||
import emu.skyline.input.ButtonState
|
||||
import emu.skyline.utils.add
|
||||
import emu.skyline.utils.multiply
|
||||
import emu.skyline.utils.normalize
|
||||
import kotlin.math.roundToLong
|
||||
|
||||
typealias OnButtonStateChangedListener = (buttonId : ButtonId, state : ButtonState) -> Unit
|
||||
@ -32,6 +35,11 @@ class OnScreenControllerView @JvmOverloads constructor(
|
||||
private var onButtonStateChangedListener : OnButtonStateChangedListener? = null
|
||||
private var onStickStateChangedListener : OnStickStateChangedListener? = null
|
||||
private val joystickAnimators = mutableMapOf<JoystickButton, Animator?>()
|
||||
var recenterSticks = false
|
||||
set(value) {
|
||||
field = value
|
||||
controls.joysticks.forEach { it.recenterSticks = recenterSticks }
|
||||
}
|
||||
|
||||
override fun onDraw(canvas : Canvas) {
|
||||
super.onDraw(canvas)
|
||||
@ -95,7 +103,7 @@ class OnScreenControllerView @JvmOverloads constructor(
|
||||
val value = animation.animatedValue as Float
|
||||
val vector = direction.multiply(value)
|
||||
val newPosition = position.add(vector)
|
||||
joystick.onFingerMoved(newPosition.x, newPosition.y)
|
||||
joystick.onFingerMoved(newPosition.x, newPosition.y, false)
|
||||
onStickStateChangedListener?.invoke(joystick.buttonId, vector.multiply(1f / radius))
|
||||
invalidate()
|
||||
}
|
||||
@ -129,6 +137,8 @@ class OnScreenControllerView @JvmOverloads constructor(
|
||||
joystick.onFingerDown(x, y)
|
||||
if (joystick.shortDoubleTapped)
|
||||
onButtonStateChangedListener?.invoke(joystick.buttonId, ButtonState.Pressed)
|
||||
if (recenterSticks)
|
||||
onStickStateChangedListener?.invoke(joystick.buttonId, joystick.outerToInnerRelative())
|
||||
performClick()
|
||||
handled = true
|
||||
}
|
||||
@ -144,8 +154,7 @@ class OnScreenControllerView @JvmOverloads constructor(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
handled.also { if (it) invalidate() else super.onTouchEvent(event) }
|
||||
handled.also { if (it) invalidate() }
|
||||
}
|
||||
|
||||
private val editingTouchHandler = OnTouchListener { _, event ->
|
||||
@ -175,7 +184,7 @@ class OnScreenControllerView @JvmOverloads constructor(
|
||||
}
|
||||
}
|
||||
false
|
||||
}.also { handled -> if (handled) invalidate() else super.onTouchEvent(event) }
|
||||
}.also { handled -> if (handled) invalidate() }
|
||||
}
|
||||
|
||||
init {
|
||||
@ -194,12 +203,12 @@ class OnScreenControllerView @JvmOverloads constructor(
|
||||
}
|
||||
|
||||
fun increaseScale() {
|
||||
controls.globalScale *= 1.1f
|
||||
controls.globalScale += 0.05f
|
||||
invalidate()
|
||||
}
|
||||
|
||||
fun decreaseScale() {
|
||||
controls.globalScale *= 0.9f
|
||||
controls.globalScale -= 0.05f
|
||||
invalidate()
|
||||
}
|
||||
|
||||
|
@ -16,6 +16,7 @@ import androidx.core.content.ContextCompat
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
import emu.skyline.R
|
||||
import emu.skyline.utils.Settings
|
||||
import kotlinx.android.synthetic.main.main_activity.fab_parent
|
||||
import kotlinx.android.synthetic.main.on_screen_edit_activity.*
|
||||
|
||||
@ -31,7 +32,7 @@ class OnScreenEditActivity : AppCompatActivity() {
|
||||
} else {
|
||||
fullEditVisible = !fullEditVisible
|
||||
toggleFabVisibility(fullEditVisible)
|
||||
fabMapping[R.drawable.ic_close]!!.animate().rotationBy(if (fullEditVisible) -45f else 45f)
|
||||
fabMapping[R.drawable.ic_close]!!.animate().rotation(if (fullEditVisible) 0f else 45f)
|
||||
}
|
||||
}
|
||||
|
||||
@ -70,7 +71,7 @@ class OnScreenEditActivity : AppCompatActivity() {
|
||||
}
|
||||
|
||||
private val actions : List<Pair<Int, () -> Unit>> = listOf(
|
||||
Pair(R.drawable.ic_refresh, { on_screen_controller_view.resetControls() }),
|
||||
Pair(R.drawable.ic_restore, { on_screen_controller_view.resetControls() }),
|
||||
Pair(R.drawable.ic_toggle, toggleAction),
|
||||
Pair(R.drawable.ic_edit, editAction),
|
||||
Pair(R.drawable.ic_zoom_out, { on_screen_controller_view.decreaseScale() }),
|
||||
@ -83,6 +84,7 @@ class OnScreenEditActivity : AppCompatActivity() {
|
||||
override fun onCreate(savedInstanceState : Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.on_screen_edit_activity)
|
||||
on_screen_controller_view.recenterSticks = Settings(this).onScreenControlRecenterSticks
|
||||
|
||||
actions.forEach { pair ->
|
||||
fab_parent.addView(FloatingActionButton(this).apply {
|
||||
|
@ -12,6 +12,8 @@ import androidx.core.graphics.minus
|
||||
import emu.skyline.R
|
||||
import emu.skyline.input.ButtonId
|
||||
import emu.skyline.input.ButtonId.*
|
||||
import emu.skyline.utils.add
|
||||
import emu.skyline.utils.multiply
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
open class CircularButton(
|
||||
@ -55,11 +57,12 @@ class JoystickButton(
|
||||
defaultRelativeX,
|
||||
defaultRelativeY,
|
||||
defaultRelativeRadiusToX,
|
||||
R.drawable.ic_stick_circle
|
||||
R.drawable.ic_button
|
||||
) {
|
||||
|
||||
private val innerButton = CircularButton(onScreenControllerView, buttonId, config.relativeX, config.relativeY, defaultRelativeRadiusToX * 0.75f, R.drawable.ic_stick)
|
||||
|
||||
var recenterSticks = false
|
||||
private lateinit var initialTapPosition : PointF
|
||||
private var fingerDownTime = 0L
|
||||
private var fingerUpTime = 0L
|
||||
var shortDoubleTapped = false
|
||||
@ -76,12 +79,17 @@ class JoystickButton(
|
||||
}
|
||||
|
||||
override fun onFingerDown(x : Float, y : Float) {
|
||||
relativeX = x / width
|
||||
relativeY = (y - heightDiff) / adjustedHeight
|
||||
val relativeX = x / width
|
||||
val relativeY = (y - heightDiff) / adjustedHeight
|
||||
if (!recenterSticks) {
|
||||
this.relativeX = relativeX
|
||||
this.relativeY = relativeY
|
||||
}
|
||||
innerButton.relativeX = relativeX
|
||||
innerButton.relativeY = relativeY
|
||||
|
||||
val currentTime = SystemClock.elapsedRealtime()
|
||||
initialTapPosition = PointF(x, y)
|
||||
val firstTapDiff = fingerUpTime - fingerDownTime
|
||||
val secondTapDiff = currentTime - fingerUpTime
|
||||
if (firstTapDiff in 0..500 && secondTapDiff in 0..500) {
|
||||
@ -101,16 +109,17 @@ class JoystickButton(
|
||||
drawable.alpha = 255
|
||||
}
|
||||
|
||||
fun onFingerMoved(x : Float, y : Float) : PointF {
|
||||
fun onFingerMoved(x : Float, y : Float, manualMove : Boolean = true) : PointF {
|
||||
val position = PointF(currentX, currentY)
|
||||
var finger = PointF(x, y)
|
||||
val outerToInner = finger.minus(position)
|
||||
val distance = outerToInner.length()
|
||||
if (distance >= radius) {
|
||||
if (distance > radius) {
|
||||
finger = position.add(outerToInner.multiply(1f / distance * radius))
|
||||
}
|
||||
|
||||
if (distance > radius * 0.075f) {
|
||||
// If finger get moved to much, then don't trigger as joystick being pressed
|
||||
if (manualMove && initialTapPosition.minus(finger).length() > radius * 0.075f) {
|
||||
fingerDownTime = 0
|
||||
fingerUpTime = 0
|
||||
}
|
||||
@ -122,6 +131,8 @@ class JoystickButton(
|
||||
|
||||
fun outerToInner() = PointF(innerButton.currentX, innerButton.currentY).minus(PointF(currentX, currentY))
|
||||
|
||||
fun outerToInnerRelative() = outerToInner().multiply(1f / radius)
|
||||
|
||||
override fun edit(x : Float, y : Float) {
|
||||
super.edit(x, y)
|
||||
|
||||
@ -209,13 +220,13 @@ class Controls(onScreenControllerView : OnScreenControllerView) {
|
||||
)
|
||||
|
||||
val rectangularButtons = listOf(
|
||||
RectangularButton(onScreenControllerView, L, 0.1f, 0.25f, 0.075f, 0.08f),
|
||||
RectangularButton(onScreenControllerView, R, 0.9f, 0.25f, 0.075f, 0.08f)
|
||||
RectangularButton(onScreenControllerView, L, 0.1f, 0.25f, 0.09f, 0.1f),
|
||||
RectangularButton(onScreenControllerView, R, 0.9f, 0.25f, 0.09f, 0.1f)
|
||||
)
|
||||
|
||||
val triggerButtons = listOf(
|
||||
TriggerButton(onScreenControllerView, ZL, 0.1f, 0.1f, 0.075f, 0.08f),
|
||||
TriggerButton(onScreenControllerView, ZR, 0.9f, 0.1f, 0.075f, 0.08f)
|
||||
TriggerButton(onScreenControllerView, ZL, 0.1f, 0.1f, 0.09f, 0.1f),
|
||||
TriggerButton(onScreenControllerView, ZR, 0.9f, 0.1f, 0.09f, 0.1f)
|
||||
)
|
||||
|
||||
val allButtons = circularButtons + joysticks + rectangularButtons + triggerButtons
|
||||
|
@ -3,7 +3,7 @@
|
||||
* Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
|
||||
*/
|
||||
|
||||
package emu.skyline.input.onscreen
|
||||
package emu.skyline.utils
|
||||
|
||||
import android.graphics.PointF
|
||||
|
@ -3,7 +3,7 @@
|
||||
* Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
|
||||
*/
|
||||
|
||||
package emu.skyline
|
||||
package emu.skyline.utils
|
||||
|
||||
import java.io.File
|
||||
import java.io.ObjectInputStream
|
32
app/src/main/java/emu/skyline/utils/Settings.kt
Normal file
32
app/src/main/java/emu/skyline/utils/Settings.kt
Normal file
@ -0,0 +1,32 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: MPL-2.0
|
||||
* Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
|
||||
*/
|
||||
|
||||
package emu.skyline.utils
|
||||
|
||||
import android.content.Context
|
||||
|
||||
class Settings(context : Context) {
|
||||
var layoutType by sharedPreferences(context, "1")
|
||||
|
||||
var searchLocation by sharedPreferences(context, "")
|
||||
|
||||
var refreshRequired by sharedPreferences(context, false)
|
||||
|
||||
var appTheme by sharedPreferences(context, "2")
|
||||
|
||||
var selectAction by sharedPreferences(context, false)
|
||||
|
||||
var perfStats by sharedPreferences(context, false)
|
||||
|
||||
var operationMode by sharedPreferences(context, true)
|
||||
|
||||
var onScreenControl by sharedPreferences(context, false)
|
||||
|
||||
var onScreenControlRecenterSticks by sharedPreferences(context, false)
|
||||
|
||||
var logCompact by sharedPreferences(context, false)
|
||||
|
||||
var logLevel by sharedPreferences(context, "3")
|
||||
}
|
@ -0,0 +1,48 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: MPL-2.0
|
||||
* Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
|
||||
*/
|
||||
|
||||
package emu.skyline.utils
|
||||
|
||||
import android.content.Context
|
||||
import androidx.preference.PreferenceManager
|
||||
import kotlin.properties.ReadWriteProperty
|
||||
import kotlin.reflect.KProperty
|
||||
|
||||
inline fun <reified T> sharedPreferences(context : Context, default : T, prefix : String = "", prefName : String? = null) = SharedPreferencesDelegate(context, T::class.java, default, prefix, prefName)
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
class SharedPreferencesDelegate<T>(context : Context, private val clazz : Class<T>, private val default : T, private val prefix : String, prefName : String?) : ReadWriteProperty<Any, T> {
|
||||
private val prefs = prefName?.let { context.getSharedPreferences(prefName, Context.MODE_PRIVATE) } ?: PreferenceManager.getDefaultSharedPreferences(context)
|
||||
|
||||
override fun setValue(thisRef : Any, property : KProperty<*>, value : T) = (prefix + pascalToSnakeCase(property.name)).let { keyName ->
|
||||
prefs.edit().apply {
|
||||
when (clazz) {
|
||||
Float::class.java, java.lang.Float::class.java -> putFloat(keyName, value as Float)
|
||||
Boolean::class.java, java.lang.Boolean::class.java -> putBoolean(keyName, value as Boolean)
|
||||
String::class.java, java.lang.String::class.java -> putString(keyName, value as String)
|
||||
else -> error("Unsupported type $clazz ${Float::class.java}")
|
||||
}
|
||||
}.apply()
|
||||
}
|
||||
|
||||
override fun getValue(thisRef : Any, property : KProperty<*>) : T = (prefix + pascalToSnakeCase(property.name)).let { keyName ->
|
||||
prefs.let {
|
||||
@Suppress("IMPLICIT_CAST_TO_ANY")
|
||||
when (clazz) {
|
||||
Float::class.java, java.lang.Float::class.java -> it.getFloat(keyName, default as Float)
|
||||
Boolean::class.java, java.lang.Boolean::class.java -> it.getBoolean(keyName, default as Boolean)
|
||||
String::class.java, java.lang.String::class.java -> it.getString(keyName, default as String)
|
||||
else -> error("Unsupported type $clazz")
|
||||
}
|
||||
} as T
|
||||
}
|
||||
|
||||
private fun pascalToSnakeCase(text : String) = StringBuilder().apply {
|
||||
text.forEachIndexed { index, c ->
|
||||
if (index != 0 && c.isUpperCase()) append('_')
|
||||
append(c.toLowerCase())
|
||||
}.toString()
|
||||
}
|
||||
}
|
@ -1,8 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="oval">
|
||||
<solid android:color="#50FFFFFF" />
|
||||
<solid android:color="#20FFFFFF" />
|
||||
<stroke
|
||||
android:width="2.5dp"
|
||||
android:color="#A0FFFFFF" />
|
||||
android:color="#25FFFFFF" />
|
||||
</shape>
|
||||
|
@ -2,10 +2,10 @@
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item>
|
||||
<shape android:shape="rectangle">
|
||||
<solid android:color="#50FFFFFF" />
|
||||
<solid android:color="#20FFFFFF" />
|
||||
<stroke
|
||||
android:width="2dp"
|
||||
android:color="#A0FFFFFF" />
|
||||
android:color="#25FFFFFF" />
|
||||
<size
|
||||
android:width="25dp"
|
||||
android:height="25dp" />
|
||||
|
10
app/src/main/res/drawable/ic_restore.xml
Normal file
10
app/src/main/res/drawable/ic_restore.xml
Normal file
@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#000"
|
||||
android:pathData="M12,3A9,9 0 0,0 3,12H0L4,16L8,12H5A7,7 0 0,1 12,5A7,7 0 0,1 19,12A7,7 0 0,1 12,19C10.5,19 9.09,18.5 7.94,17.7L6.5,19.14C8.04,20.3 9.94,21 12,21A9,9 0 0,0 21,12A9,9 0 0,0 12,3M14,12A2,2 0 0,0 12,10A2,2 0 0,0 10,12A2,2 0 0,0 12,14A2,2 0 0,0 14,12Z" />
|
||||
</vector>
|
@ -2,10 +2,10 @@
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item>
|
||||
<shape android:shape="oval">
|
||||
<solid android:color="#50FFFFFF" />
|
||||
<solid android:color="#10FFFFFF" />
|
||||
<stroke
|
||||
android:width="2dp"
|
||||
android:color="#A0FFFFFF" />
|
||||
android:color="#25FFFFFF" />
|
||||
<size
|
||||
android:width="25dp"
|
||||
android:height="25dp" />
|
||||
@ -18,7 +18,7 @@
|
||||
</item>
|
||||
<item>
|
||||
<shape android:shape="oval">
|
||||
<solid android:color="#50000000" />
|
||||
<solid android:color="#30000000" />
|
||||
<size
|
||||
android:width="30dp"
|
||||
android:height="30dp" />
|
||||
|
@ -1,8 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="oval">
|
||||
<solid android:color="#50FFFFFF" />
|
||||
<stroke
|
||||
android:width="2.5dp"
|
||||
android:color="#A0FFFFFF" />
|
||||
</shape>
|
@ -2,10 +2,10 @@
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item>
|
||||
<shape android:shape="rectangle">
|
||||
<solid android:color="#50FFFFFF" />
|
||||
<solid android:color="#20FFFFFF" />
|
||||
<stroke
|
||||
android:width="2dp"
|
||||
android:color="#A0FFFFFF" />
|
||||
android:color="#25FFFFFF" />
|
||||
<size
|
||||
android:width="25dp"
|
||||
android:height="25dp" />
|
||||
|
@ -2,10 +2,10 @@
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item>
|
||||
<shape android:shape="rectangle">
|
||||
<solid android:color="#50FFFFFF" />
|
||||
<solid android:color="#20FFFFFF" />
|
||||
<stroke
|
||||
android:width="2dp"
|
||||
android:color="#A0FFFFFF" />
|
||||
android:color="#25FFFFFF" />
|
||||
<size
|
||||
android:width="25dp"
|
||||
android:height="25dp" />
|
||||
|
@ -36,10 +36,10 @@
|
||||
android:id="@+id/button_icon"
|
||||
android:layout_width="50dp"
|
||||
android:layout_height="50dp"
|
||||
android:alpha="0.15"
|
||||
android:contentDescription="@string/buttons"
|
||||
android:outlineProvider="bounds"
|
||||
android:src="@drawable/ic_button" />
|
||||
android:src="@drawable/ic_button"
|
||||
android:tint="?android:attr/textColorPrimary" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/button_text"
|
||||
|
@ -1,52 +1,42 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/controller_item"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?attr/selectableItemBackground"
|
||||
android:clickable="true"
|
||||
android:focusable="true">
|
||||
android:focusable="true"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/text_title"
|
||||
android:layout_width="wrap_content"
|
||||
<LinearLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:textAppearance="?android:attr/textAppearanceListItem"
|
||||
app:layout_constrainedWidth="true"
|
||||
app:layout_constraintBottom_toTopOf="@id/text_subtitle"
|
||||
app:layout_constraintEnd_toStartOf="@id/checkbox"
|
||||
app:layout_constraintHorizontal_bias="0"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:text="Title" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/text_subtitle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:textAppearance="?android:attr/textAppearanceListItemSecondary"
|
||||
android:textColor="@android:color/tertiary_text_light"
|
||||
app:layout_constrainedWidth="true"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@id/checkbox"
|
||||
app:layout_constraintHorizontal_bias="0"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/text_title"
|
||||
tools:text="Summary" />
|
||||
android:layout_weight="1"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/text_title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="?android:attr/textAppearanceListItem"
|
||||
tools:text="Title" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/text_subtitle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="?android:attr/textAppearanceListItemSecondary"
|
||||
android:textColor="@android:color/tertiary_text_light"
|
||||
tools:text="Summary" />
|
||||
</LinearLayout>
|
||||
|
||||
<com.google.android.material.checkbox.MaterialCheckBox
|
||||
android:id="@+id/checkbox"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:clickable="false"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="1"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
android:layout_gravity="center_vertical"
|
||||
android:clickable="false" />
|
||||
</LinearLayout>
|
||||
|
@ -50,7 +50,8 @@
|
||||
android:alpha="0.4"
|
||||
android:contentDescription="@string/buttons"
|
||||
android:outlineProvider="bounds"
|
||||
android:src="@drawable/ic_stick_circle" />
|
||||
android:src="@drawable/ic_button"
|
||||
android:tint="?android:attr/textColorPrimary" />
|
||||
|
||||
|
||||
<RelativeLayout
|
||||
@ -70,7 +71,8 @@
|
||||
android:alpha="0.4"
|
||||
android:contentDescription="@string/buttons"
|
||||
android:outlineProvider="bounds"
|
||||
android:src="@drawable/ic_stick" />
|
||||
android:src="@drawable/ic_stick"
|
||||
android:tint="?android:attr/textColorPrimary" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/stick_name"
|
||||
|
@ -60,6 +60,7 @@
|
||||
<string name="joystick">Joystick</string>
|
||||
<string name="confirm">Confirm</string>
|
||||
<string name="cancel">Cancel</string>
|
||||
<string name="osc_recenter_sticks">Recenter sticks on touch</string>
|
||||
<string name="controller">Controller</string>
|
||||
<string name="config_controller">Configure Controller</string>
|
||||
<string name="controller_type">Controller Type</string>
|
||||
|
Loading…
Reference in New Issue
Block a user