diff --git a/app/src/main/java/emu/skyline/EmulationActivity.kt b/app/src/main/java/emu/skyline/EmulationActivity.kt index 8149d429..b91b5533 100644 --- a/app/src/main/java/emu/skyline/EmulationActivity.kt +++ b/app/src/main/java/emu/skyline/EmulationActivity.kt @@ -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() - /** - * 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) diff --git a/app/src/main/java/emu/skyline/LogActivity.kt b/app/src/main/java/emu/skyline/LogActivity.kt index 2e768ee7..3cdda6f8 100644 --- a/app/src/main/java/emu/skyline/LogActivity.kt +++ b/app/src/main/java/emu/skyline/LogActivity.kt @@ -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 diff --git a/app/src/main/java/emu/skyline/MainActivity.kt b/app/src/main/java/emu/skyline/MainActivity.kt index 16a3fc71..5a7f168e 100644 --- a/app/src/main/java/emu/skyline/MainActivity.kt +++ b/app/src/main/java/emu/skyline/MainActivity.kt @@ -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() 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) } } } diff --git a/app/src/main/java/emu/skyline/adapter/controller/ControllerCheckBoxViewItem.kt b/app/src/main/java/emu/skyline/adapter/controller/ControllerCheckBoxViewItem.kt index 3569e97d..9b7fc89b 100644 --- a/app/src/main/java/emu/skyline/adapter/controller/ControllerCheckBoxViewItem.kt +++ b/app/src/main/java/emu/skyline/adapter/controller/ControllerCheckBoxViewItem.kt @@ -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 { diff --git a/app/src/main/java/emu/skyline/input/ControllerActivity.kt b/app/src/main/java/emu/skyline/input/ControllerActivity.kt index 17c13408..2be07e54 100644 --- a/app/src/main/java/emu/skyline/input/ControllerActivity.kt +++ b/app/src/main/java/emu/skyline/input/ControllerActivity.kt @@ -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() - 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) }) diff --git a/app/src/main/java/emu/skyline/input/dialog/ButtonDialog.kt b/app/src/main/java/emu/skyline/input/dialog/ButtonDialog.kt index 54b0a15c..34799f61 100644 --- a/app/src/main/java/emu/skyline/input/dialog/ButtonDialog.kt +++ b/app/src/main/java/emu/skyline/input/dialog/ButtonDialog.kt @@ -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 */ diff --git a/app/src/main/java/emu/skyline/input/onscreen/OnScreenConfiguration.kt b/app/src/main/java/emu/skyline/input/onscreen/OnScreenConfiguration.kt index 39588d4a..18b49b24 100644 --- a/app/src/main/java/emu/skyline/input/onscreen/OnScreenConfiguration.kt +++ b/app/src/main/java/emu/skyline/input/onscreen/OnScreenConfiguration.kt @@ -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 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 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(context : Context, private val prefix : String, private val clazz : Class, private val default : T) : ReadWriteProperty { - 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 -} diff --git a/app/src/main/java/emu/skyline/input/onscreen/OnScreenControllerView.kt b/app/src/main/java/emu/skyline/input/onscreen/OnScreenControllerView.kt index 1df3d25c..f9fe74dd 100644 --- a/app/src/main/java/emu/skyline/input/onscreen/OnScreenControllerView.kt +++ b/app/src/main/java/emu/skyline/input/onscreen/OnScreenControllerView.kt @@ -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() + 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() } diff --git a/app/src/main/java/emu/skyline/input/onscreen/OnScreenEditActivity.kt b/app/src/main/java/emu/skyline/input/onscreen/OnScreenEditActivity.kt index ada47a5f..b1fe6274 100644 --- a/app/src/main/java/emu/skyline/input/onscreen/OnScreenEditActivity.kt +++ b/app/src/main/java/emu/skyline/input/onscreen/OnScreenEditActivity.kt @@ -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 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 { diff --git a/app/src/main/java/emu/skyline/input/onscreen/OnScreenItemDefinitions.kt b/app/src/main/java/emu/skyline/input/onscreen/OnScreenItemDefinitions.kt index dba3def3..99eefb2c 100644 --- a/app/src/main/java/emu/skyline/input/onscreen/OnScreenItemDefinitions.kt +++ b/app/src/main/java/emu/skyline/input/onscreen/OnScreenItemDefinitions.kt @@ -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 diff --git a/app/src/main/java/emu/skyline/input/onscreen/PointExtensions.kt b/app/src/main/java/emu/skyline/utils/PointExtensions.kt similarity index 91% rename from app/src/main/java/emu/skyline/input/onscreen/PointExtensions.kt rename to app/src/main/java/emu/skyline/utils/PointExtensions.kt index 282353b5..fad793c2 100644 --- a/app/src/main/java/emu/skyline/input/onscreen/PointExtensions.kt +++ b/app/src/main/java/emu/skyline/utils/PointExtensions.kt @@ -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 diff --git a/app/src/main/java/emu/skyline/SerializationHelper.kt b/app/src/main/java/emu/skyline/utils/SerializationHelper.kt similarity index 95% rename from app/src/main/java/emu/skyline/SerializationHelper.kt rename to app/src/main/java/emu/skyline/utils/SerializationHelper.kt index 5dcbd958..12d01a2b 100644 --- a/app/src/main/java/emu/skyline/SerializationHelper.kt +++ b/app/src/main/java/emu/skyline/utils/SerializationHelper.kt @@ -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 diff --git a/app/src/main/java/emu/skyline/utils/Settings.kt b/app/src/main/java/emu/skyline/utils/Settings.kt new file mode 100644 index 00000000..13e3ddb6 --- /dev/null +++ b/app/src/main/java/emu/skyline/utils/Settings.kt @@ -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") +} diff --git a/app/src/main/java/emu/skyline/utils/SharedPreferencesDelegate.kt b/app/src/main/java/emu/skyline/utils/SharedPreferencesDelegate.kt new file mode 100644 index 00000000..2cf9e87b --- /dev/null +++ b/app/src/main/java/emu/skyline/utils/SharedPreferencesDelegate.kt @@ -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 sharedPreferences(context : Context, default : T, prefix : String = "", prefName : String? = null) = SharedPreferencesDelegate(context, T::class.java, default, prefix, prefName) + +@Suppress("UNCHECKED_CAST") +class SharedPreferencesDelegate(context : Context, private val clazz : Class, private val default : T, private val prefix : String, prefName : String?) : ReadWriteProperty { + 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() + } +} diff --git a/app/src/main/res/drawable/ic_button.xml b/app/src/main/res/drawable/ic_button.xml index 0e745b12..09a1ed01 100644 --- a/app/src/main/res/drawable/ic_button.xml +++ b/app/src/main/res/drawable/ic_button.xml @@ -1,8 +1,8 @@ - + + android:color="#25FFFFFF" /> diff --git a/app/src/main/res/drawable/ic_rectangular_button.xml b/app/src/main/res/drawable/ic_rectangular_button.xml index 6fc96728..5b095f8d 100644 --- a/app/src/main/res/drawable/ic_rectangular_button.xml +++ b/app/src/main/res/drawable/ic_rectangular_button.xml @@ -2,10 +2,10 @@ - + + android:color="#25FFFFFF" /> diff --git a/app/src/main/res/drawable/ic_restore.xml b/app/src/main/res/drawable/ic_restore.xml new file mode 100644 index 00000000..f6345074 --- /dev/null +++ b/app/src/main/res/drawable/ic_restore.xml @@ -0,0 +1,10 @@ + + + + diff --git a/app/src/main/res/drawable/ic_stick.xml b/app/src/main/res/drawable/ic_stick.xml index 43cd3feb..1ff56a9a 100644 --- a/app/src/main/res/drawable/ic_stick.xml +++ b/app/src/main/res/drawable/ic_stick.xml @@ -2,10 +2,10 @@ - + + android:color="#25FFFFFF" /> @@ -18,7 +18,7 @@ - + diff --git a/app/src/main/res/drawable/ic_stick_circle.xml b/app/src/main/res/drawable/ic_stick_circle.xml deleted file mode 100644 index 0e745b12..00000000 --- a/app/src/main/res/drawable/ic_stick_circle.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - diff --git a/app/src/main/res/drawable/ic_trigger_button_left.xml b/app/src/main/res/drawable/ic_trigger_button_left.xml index 91431b17..26478d0b 100644 --- a/app/src/main/res/drawable/ic_trigger_button_left.xml +++ b/app/src/main/res/drawable/ic_trigger_button_left.xml @@ -2,10 +2,10 @@ - + + android:color="#25FFFFFF" /> diff --git a/app/src/main/res/drawable/ic_trigger_button_right.xml b/app/src/main/res/drawable/ic_trigger_button_right.xml index 748cbb73..22e31f2b 100644 --- a/app/src/main/res/drawable/ic_trigger_button_right.xml +++ b/app/src/main/res/drawable/ic_trigger_button_right.xml @@ -2,10 +2,10 @@ - + + android:color="#25FFFFFF" /> diff --git a/app/src/main/res/layout/button_dialog.xml b/app/src/main/res/layout/button_dialog.xml index b62ac3dc..515eed10 100644 --- a/app/src/main/res/layout/button_dialog.xml +++ b/app/src/main/res/layout/button_dialog.xml @@ -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" /> - + android:focusable="true" + android:orientation="horizontal"> - - - + android:layout_weight="1" + android:orientation="vertical"> + + + + + - + android:layout_gravity="center_vertical" + android:clickable="false" /> + diff --git a/app/src/main/res/layout/stick_dialog.xml b/app/src/main/res/layout/stick_dialog.xml index 0a4bcea2..77cbdfd5 100644 --- a/app/src/main/res/layout/stick_dialog.xml +++ b/app/src/main/res/layout/stick_dialog.xml @@ -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" /> + android:src="@drawable/ic_stick" + android:tint="?android:attr/textColorPrimary" /> Joystick Confirm Cancel + Recenter sticks on touch Controller Configure Controller Controller Type