diff --git a/app/src/main/java/emu/skyline/AppDialog.kt b/app/src/main/java/emu/skyline/AppDialog.kt index 28f240a6..20828a46 100644 --- a/app/src/main/java/emu/skyline/AppDialog.kt +++ b/app/src/main/java/emu/skyline/AppDialog.kt @@ -96,7 +96,7 @@ class AppDialog : BottomSheetDialogFragment() { game_pin.setOnClickListener { val info = ShortcutInfo.Builder(context, item.title) - info.setShortLabel(item.meta.name) + info.setShortLabel(item.title) info.setActivity(ComponentName(requireContext(), EmulationActivity::class.java)) info.setIcon(Icon.createWithAdaptiveBitmap(item.icon ?: missingIcon)) diff --git a/app/src/main/java/emu/skyline/EmulationActivity.kt b/app/src/main/java/emu/skyline/EmulationActivity.kt index b91b5533..a0900b1a 100644 --- a/app/src/main/java/emu/skyline/EmulationActivity.kt +++ b/app/src/main/java/emu/skyline/EmulationActivity.kt @@ -164,7 +164,7 @@ class EmulationActivity : AppCompatActivity(), SurfaceHolder.Callback, View.OnTo } /** - * This executes the specified ROM, [preferenceFd] is assumed to be valid beforehand + * This executes the specified ROM * * @param rom The URI of the ROM to execute */ @@ -186,7 +186,7 @@ class EmulationActivity : AppCompatActivity(), SurfaceHolder.Callback, View.OnTo } /** - * This makes the window fullscreen then sets up [preferenceFd], sets up the performance statistics and finally calls [executeApplication] for executing the application + * This makes the window fullscreen, sets up the performance statistics and finally calls [executeApplication] for executing the application */ @SuppressLint("SetTextI18n") override fun onCreate(savedInstanceState : Bundle?) { @@ -415,7 +415,7 @@ class EmulationActivity : AppCompatActivity(), SurfaceHolder.Callback, View.OnTo else -> error("Invalid button id") } setAxisValue(0, stickId.xAxis.ordinal, (position.x * Short.MAX_VALUE).toInt()) - setAxisValue(0, stickId.yAxis.ordinal, (-position.y * Short.MAX_VALUE).toInt()) // Y is inverted + setAxisValue(0, stickId.yAxis.ordinal, (-position.y * Short.MAX_VALUE).toInt()) // Y is inverted, since drawing starts from top left } @SuppressLint("WrongConstant") diff --git a/app/src/main/java/emu/skyline/MainActivity.kt b/app/src/main/java/emu/skyline/MainActivity.kt index 5a7f168e..1ef1f260 100644 --- a/app/src/main/java/emu/skyline/MainActivity.kt +++ b/app/src/main/java/emu/skyline/MainActivity.kt @@ -31,9 +31,8 @@ import emu.skyline.adapter.GenericAdapter import emu.skyline.adapter.HeaderViewItem import emu.skyline.adapter.LayoutType import emu.skyline.data.AppItem -import emu.skyline.data.BaseElement -import emu.skyline.data.BaseHeader -import emu.skyline.data.ElementType +import emu.skyline.data.DataItem +import emu.skyline.data.HeaderItem import emu.skyline.loader.LoaderResult import emu.skyline.loader.RomFile import emu.skyline.loader.RomFormat @@ -71,7 +70,7 @@ class MainActivity : AppCompatActivity() { /** * This adds all files in [directory] with [extension] as an entry in [adapter] using [RomFile] to load metadata */ - private fun addEntries(extension : String, romFormat : RomFormat, directory : DocumentFile, romElements : ArrayList<BaseElement>, found : Boolean = false) : Boolean { + private fun addEntries(extension : String, romFormat : RomFormat, directory : DocumentFile, romElements : ArrayList<DataItem>, found : Boolean = false) : Boolean { var foundCurrent = found directory.listFiles().forEach { file -> @@ -83,7 +82,7 @@ class MainActivity : AppCompatActivity() { val finalFoundCurrent = foundCurrent runOnUiThread { if (!finalFoundCurrent) { - romElements.add(BaseHeader(romFormat.name)) + romElements.add(HeaderItem(romFormat.name)) adapter.addItem(HeaderViewItem(romFormat.name)) } @@ -111,8 +110,8 @@ class MainActivity : AppCompatActivity() { if (loadFromFile) { try { - loadSerializedList<BaseElement>(romsFile).forEach { - if (it.elementType == ElementType.Header && it is BaseHeader) + loadSerializedList<DataItem>(romsFile).forEach { + if (it is HeaderItem) adapter.addItem(HeaderViewItem(it.title)) else if (it is AppItem) adapter.addItem(it.toViewItem()) @@ -136,7 +135,7 @@ class MainActivity : AppCompatActivity() { val searchLocation = DocumentFile.fromTreeUri(this, Uri.parse(settings.searchLocation))!! - val romElements = ArrayList<BaseElement>() + val romElements = ArrayList<DataItem>() addEntries("nro", RomFormat.NRO, searchLocation, romElements) addEntries("nso", RomFormat.NSO, searchLocation, romElements) addEntries("nca", RomFormat.NCA, searchLocation, romElements) @@ -144,7 +143,7 @@ class MainActivity : AppCompatActivity() { runOnUiThread { if (romElements.isEmpty()) { - romElements.add(BaseHeader(getString(R.string.no_rom))) + romElements.add(HeaderItem(getString(R.string.no_rom))) adapter.addItem(HeaderViewItem(getString(R.string.no_rom))) } diff --git a/app/src/main/java/emu/skyline/SkylineApplication.kt b/app/src/main/java/emu/skyline/SkylineApplication.kt index f6768da9..5c062818 100644 --- a/app/src/main/java/emu/skyline/SkylineApplication.kt +++ b/app/src/main/java/emu/skyline/SkylineApplication.kt @@ -8,9 +8,12 @@ package emu.skyline import android.app.Application import emu.skyline.input.InputManager +/** + * Custom application class to initialize [InputManager] + */ class SkylineApplication : Application() { override fun onCreate() { super.onCreate() InputManager.init(applicationContext) } -} \ No newline at end of file +} diff --git a/app/src/main/java/emu/skyline/adapter/AppViewItem.kt b/app/src/main/java/emu/skyline/adapter/AppViewItem.kt index 6a70514e..5c37c206 100644 --- a/app/src/main/java/emu/skyline/adapter/AppViewItem.kt +++ b/app/src/main/java/emu/skyline/adapter/AppViewItem.kt @@ -69,5 +69,5 @@ class AppViewItem(var layoutType : LayoutType, private val item : AppItem, priva builder.show() } - override fun toString() = item.key() + override fun key() = item.key() } diff --git a/app/src/main/java/emu/skyline/adapter/GenericAdapter.kt b/app/src/main/java/emu/skyline/adapter/GenericAdapter.kt index 9299cf3e..6eb09fc0 100644 --- a/app/src/main/java/emu/skyline/adapter/GenericAdapter.kt +++ b/app/src/main/java/emu/skyline/adapter/GenericAdapter.kt @@ -13,6 +13,9 @@ import info.debatty.java.stringsimilarity.Cosine import info.debatty.java.stringsimilarity.JaroWinkler import java.util.* +/** + * Can handle any view types with [GenericViewHolderBinder] implemented, [GenericViewHolderBinder] are differentiated by the return value of [GenericViewHolderBinder.getLayoutFactory] + */ class GenericAdapter : RecyclerView.Adapter<GenericViewHolder>(), Filterable { var currentSearchTerm = "" @@ -70,7 +73,7 @@ class GenericAdapter : RecyclerView.Adapter<GenericViewHolder>(), Filterable { * This sorts the items in [allItems] in relation to how similar they are to [currentSearchTerm] */ fun extractSorted() = allItems.mapNotNull { item -> - item.toString().toLowerCase(Locale.getDefault()).let { + item.key().toLowerCase(Locale.getDefault()).let { val similarity = (jw.similarity(currentSearchTerm, it)) + cos.similarity(currentSearchTerm, it) / 2 if (similarity != 0.0) ScoredItem(similarity, item) else null } diff --git a/app/src/main/java/emu/skyline/adapter/GenericViewHolderBinder.kt b/app/src/main/java/emu/skyline/adapter/GenericViewHolderBinder.kt index 3bf2a2f0..66febe92 100644 --- a/app/src/main/java/emu/skyline/adapter/GenericViewHolderBinder.kt +++ b/app/src/main/java/emu/skyline/adapter/GenericViewHolderBinder.kt @@ -22,4 +22,9 @@ abstract class GenericViewHolderBinder { abstract fun getLayoutFactory() : GenericLayoutFactory abstract fun bind(holder : GenericViewHolder, position : Int) + + /** + * Used for filtering + */ + open fun key() : String = "" } diff --git a/app/src/main/java/emu/skyline/data/AppItem.kt b/app/src/main/java/emu/skyline/data/AppItem.kt index eead5b05..64c209f7 100644 --- a/app/src/main/java/emu/skyline/data/AppItem.kt +++ b/app/src/main/java/emu/skyline/data/AppItem.kt @@ -13,7 +13,7 @@ import emu.skyline.loader.LoaderResult /** * This class is a wrapper around [AppEntry], it is used for passing around game metadata */ -class AppItem(val meta : AppEntry) : BaseItem() { +class AppItem(private val meta : AppEntry) : DataItem { /** * The icon of the application */ @@ -56,5 +56,5 @@ class AppItem(val meta : AppEntry) : BaseItem() { /** * The name and author is used as the key */ - override fun key() = meta.name + if (meta.author != null) " ${meta.author}" else "" + fun key() = meta.name + if (meta.author != null) " ${meta.author}" else "" } diff --git a/app/src/main/java/emu/skyline/data/BaseItem.kt b/app/src/main/java/emu/skyline/data/BaseItem.kt deleted file mode 100644 index fed245df..00000000 --- a/app/src/main/java/emu/skyline/data/BaseItem.kt +++ /dev/null @@ -1,16 +0,0 @@ -/* - * SPDX-License-Identifier: MPL-2.0 - * Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/) - */ - -package emu.skyline.data - -/** - * This is an abstract class that all adapter item classes inherit from - */ -abstract class BaseItem : BaseElement(ElementType.Item) { - /** - * This function returns a string used for searching - */ - open fun key() : String = "" -} diff --git a/app/src/main/java/emu/skyline/data/BaseHeader.kt b/app/src/main/java/emu/skyline/data/DataItem.kt similarity index 68% rename from app/src/main/java/emu/skyline/data/BaseHeader.kt rename to app/src/main/java/emu/skyline/data/DataItem.kt index 9a8cf466..a7cce749 100644 --- a/app/src/main/java/emu/skyline/data/BaseHeader.kt +++ b/app/src/main/java/emu/skyline/data/DataItem.kt @@ -5,4 +5,6 @@ package emu.skyline.data -class BaseHeader(val title : String) : BaseElement(ElementType.Header) +import java.io.Serializable + +interface DataItem : Serializable diff --git a/app/src/main/java/emu/skyline/data/BaseElement.kt b/app/src/main/java/emu/skyline/data/HeaderItem.kt similarity index 50% rename from app/src/main/java/emu/skyline/data/BaseElement.kt rename to app/src/main/java/emu/skyline/data/HeaderItem.kt index f0f3a6db..015989b0 100644 --- a/app/src/main/java/emu/skyline/data/BaseElement.kt +++ b/app/src/main/java/emu/skyline/data/HeaderItem.kt @@ -5,11 +5,4 @@ package emu.skyline.data -import java.io.Serializable - -enum class ElementType { - Header, - Item -} - -abstract class BaseElement(val elementType : ElementType) : Serializable +class HeaderItem(val title : String) : DataItem diff --git a/app/src/main/java/emu/skyline/input/ControllerActivity.kt b/app/src/main/java/emu/skyline/input/ControllerActivity.kt index 2be07e54..f54ae287 100644 --- a/app/src/main/java/emu/skyline/input/ControllerActivity.kt +++ b/app/src/main/java/emu/skyline/input/ControllerActivity.kt @@ -38,7 +38,7 @@ class ControllerActivity : AppCompatActivity() { private val adapter = GenericAdapter() /** - * This is a map between a button and it's corresponding [ControllerItem] in [adapter] + * This is a map between a button and it's corresponding [ControllerViewItem] in [adapter] */ val buttonMap = mutableMapOf<ButtonId, ControllerViewItem>() diff --git a/app/src/main/java/emu/skyline/input/HostEvent.kt b/app/src/main/java/emu/skyline/input/HostEvent.kt index c0e85ac7..66287519 100644 --- a/app/src/main/java/emu/skyline/input/HostEvent.kt +++ b/app/src/main/java/emu/skyline/input/HostEvent.kt @@ -10,7 +10,7 @@ import android.view.MotionEvent import java.io.Serializable /** - * This an abstract class for all host events that is inherited by all other event classes + * This a sealed class for all host events that is inherited by all other event classes * * @param descriptor The device descriptor of the device this event originates from */ diff --git a/app/src/main/java/emu/skyline/input/onscreen/OnScreenButton.kt b/app/src/main/java/emu/skyline/input/onscreen/OnScreenButton.kt index 9573d939..e4dfe659 100644 --- a/app/src/main/java/emu/skyline/input/onscreen/OnScreenButton.kt +++ b/app/src/main/java/emu/skyline/input/onscreen/OnScreenButton.kt @@ -11,9 +11,11 @@ import android.graphics.Paint import android.graphics.Rect import androidx.core.content.ContextCompat import emu.skyline.input.ButtonId -import kotlin.math.absoluteValue import kotlin.math.roundToInt +/** + * Converts relative values, such as coordinates and boundaries, to their absolute counterparts, also handles layout modifications like scaling and custom positioning + */ abstract class OnScreenButton( onScreenControllerView : OnScreenControllerView, val buttonId : ButtonId, @@ -47,7 +49,11 @@ abstract class OnScreenButton( var height = 0 protected val adjustedHeight get() = width / CONFIGURED_ASPECT_RATIO - protected val heightDiff get() = (height - adjustedHeight).absoluteValue + + /** + * Buttons should be at bottom when device have large height than [adjustedHeight] + */ + protected val heightDiff get() = (height - adjustedHeight).coerceAtLeast(0f) protected val itemWidth get() = width * relativeWidth private val itemHeight get() = adjustedHeight * relativeHeight @@ -58,37 +64,23 @@ abstract class OnScreenButton( private val left get() = currentX - itemWidth / 2f private val top get() = currentY - itemHeight / 2f - protected val currentBounds - get() = Rect( - left.roundToInt(), - top.roundToInt(), - (left + itemWidth).roundToInt(), - (top + itemHeight).roundToInt() - ) + protected val currentBounds get() = Rect(left.roundToInt(), top.roundToInt(), (left + itemWidth).roundToInt(), (top + itemHeight).roundToInt()) + /** + * Keeps track of finger when there are multiple touches + */ var touchPointerId = -1 var isEditing = false private set - protected open fun renderCenteredText( - canvas : Canvas, - text : String, - size : Float, - x : Float, - y : Float - ) { + protected open fun renderCenteredText(canvas : Canvas, text : String, size : Float, x : Float, y : Float) { buttonSymbolPaint.apply { textSize = size textAlign = Paint.Align.LEFT getTextBounds(text, 0, text.length, textBoundsRect) } - canvas.drawText( - text, - x - textBoundsRect.width() / 2f - textBoundsRect.left, - y + textBoundsRect.height() / 2f - textBoundsRect.bottom, - buttonSymbolPaint - ) + canvas.drawText(text, x - textBoundsRect.width() / 2f - textBoundsRect.left, y + textBoundsRect.height() / 2f - textBoundsRect.bottom, buttonSymbolPaint) } open fun render(canvas : Canvas) { 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 18b49b24..94ec95b1 100644 --- a/app/src/main/java/emu/skyline/input/onscreen/OnScreenConfiguration.kt +++ b/app/src/main/java/emu/skyline/input/onscreen/OnScreenConfiguration.kt @@ -16,6 +16,9 @@ interface ControllerConfiguration { var relativeY : Float } +/** + * Dummy implementation so layout editor is able to render [OnScreenControllerView] when [android.view.View.isInEditMode] is true + */ class ControllerConfigurationDummy(defaultRelativeX : Float, defaultRelativeY : Float) : ControllerConfiguration { override var enabled = true override var globalScale = 1f 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 f9fe74dd..8dffbe01 100644 --- a/app/src/main/java/emu/skyline/input/onscreen/OnScreenControllerView.kt +++ b/app/src/main/java/emu/skyline/input/onscreen/OnScreenControllerView.kt @@ -25,12 +25,10 @@ import kotlin.math.roundToLong typealias OnButtonStateChangedListener = (buttonId : ButtonId, state : ButtonState) -> Unit typealias OnStickStateChangedListener = (buttonId : ButtonId, position : PointF) -> Unit -class OnScreenControllerView @JvmOverloads constructor( - context : Context, - attrs : AttributeSet? = null, - defStyleAttr : Int = 0, - defStyleRes : Int = 0 -) : View(context, attrs, defStyleAttr, defStyleRes) { +/** + * Renders On-Screen Controls as a single view, handles touch inputs and button toggling + */ +class OnScreenControllerView @JvmOverloads constructor(context : Context, attrs : AttributeSet? = null, defStyleAttr : Int = 0, defStyleRes : Int = 0) : View(context, attrs, defStyleAttr, defStyleRes) { private val controls = Controls(this) private var onButtonStateChangedListener : OnButtonStateChangedListener? = null private var onStickStateChangedListener : OnStickStateChangedListener? = null @@ -226,4 +224,4 @@ class OnScreenControllerView @JvmOverloads constructor( controls.allButtons.first { it.buttonId == buttonId }.config.enabled = enabled invalidate() } -} \ No newline at end of file +} 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 99eefb2c..ae3aac85 100644 --- a/app/src/main/java/emu/skyline/input/onscreen/OnScreenItemDefinitions.kt +++ b/app/src/main/java/emu/skyline/input/onscreen/OnScreenItemDefinitions.kt @@ -34,10 +34,13 @@ open class CircularButton( ) { val radius get() = itemWidth / 2f + /** + * Checks if [x] and [y] are within circle + */ override fun isTouched(x : Float, y : Float) : Boolean = PointF(currentX, currentY).minus(PointF(x, y)).length() <= radius override fun onFingerDown(x : Float, y : Float) { - drawable.alpha = (255 * 0.5f).roundToInt() + drawable.alpha = 125 } override fun onFingerUp(x : Float, y : Float) { @@ -81,19 +84,21 @@ class JoystickButton( override fun onFingerDown(x : Float, y : Float) { val relativeX = x / width val relativeY = (y - heightDiff) / adjustedHeight - if (!recenterSticks) { + if (recenterSticks) { this.relativeX = relativeX this.relativeY = relativeY } innerButton.relativeX = relativeX innerButton.relativeY = relativeY + // If first and second tap occur within 500 mills, then trigger stick press 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) { shortDoubleTapped = true + // Indicate stick being pressed with lower alpha value drawable.alpha = 50 } fingerDownTime = currentTime @@ -115,6 +120,7 @@ class JoystickButton( val outerToInner = finger.minus(position) val distance = outerToInner.length() if (distance > radius) { + // Limit distance to radius finger = position.add(outerToInner.multiply(1f / distance * radius)) } @@ -168,7 +174,7 @@ open class RectangularButton( override fun isTouched(x : Float, y : Float) = currentBounds.contains(x.roundToInt(), y.roundToInt()) override fun onFingerDown(x : Float, y : Float) { - drawable.alpha = (255 * 0.5f).roundToInt() + drawable.alpha = 125 } override fun onFingerUp(x : Float, y : Float) { diff --git a/app/src/main/java/emu/skyline/loader/RomFile.kt b/app/src/main/java/emu/skyline/loader/RomFile.kt index ff9fc790..74daf2e3 100644 --- a/app/src/main/java/emu/skyline/loader/RomFile.kt +++ b/app/src/main/java/emu/skyline/loader/RomFile.kt @@ -80,6 +80,7 @@ class AppEntry(var name : String, var author : String?, var icon : Bitmap?, var output.writeInt(loaderResult.value) output.writeBoolean(icon != null) icon?.let { + @Suppress("DEPRECATION") if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) it.compress(Bitmap.CompressFormat.WEBP_LOSSY, 100, output) else diff --git a/app/src/main/java/emu/skyline/utils/Settings.kt b/app/src/main/java/emu/skyline/utils/Settings.kt index 13e3ddb6..e940b391 100644 --- a/app/src/main/java/emu/skyline/utils/Settings.kt +++ b/app/src/main/java/emu/skyline/utils/Settings.kt @@ -24,7 +24,7 @@ class Settings(context : Context) { var onScreenControl by sharedPreferences(context, false) - var onScreenControlRecenterSticks by sharedPreferences(context, false) + var onScreenControlRecenterSticks by sharedPreferences(context, true) var logCompact by sharedPreferences(context, false) diff --git a/app/src/main/res/layout/button_dialog.xml b/app/src/main/res/layout/button_dialog.xml index 515eed10..2ecb1a9e 100644 --- a/app/src/main/res/layout/button_dialog.xml +++ b/app/src/main/res/layout/button_dialog.xml @@ -1,5 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/button_layout" android:layout_width="match_parent" android:layout_height="wrap_content" @@ -39,7 +40,7 @@ android:contentDescription="@string/buttons" android:outlineProvider="bounds" android:src="@drawable/ic_button" - android:tint="?android:attr/textColorPrimary" /> + app:tint="?android:attr/textColorPrimary" /> <TextView android:id="@+id/button_text" diff --git a/app/src/main/res/layout/stick_dialog.xml b/app/src/main/res/layout/stick_dialog.xml index 77cbdfd5..a8d8b1cb 100644 --- a/app/src/main/res/layout/stick_dialog.xml +++ b/app/src/main/res/layout/stick_dialog.xml @@ -1,5 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/stick_layout" android:layout_width="match_parent" android:layout_height="wrap_content" @@ -51,7 +52,7 @@ android:contentDescription="@string/buttons" android:outlineProvider="bounds" android:src="@drawable/ic_button" - android:tint="?android:attr/textColorPrimary" /> + app:tint="?android:attr/textColorPrimary" /> <RelativeLayout @@ -72,7 +73,7 @@ android:contentDescription="@string/buttons" android:outlineProvider="bounds" android:src="@drawable/ic_stick" - android:tint="?android:attr/textColorPrimary" /> + app:tint="?android:attr/textColorPrimary" /> <TextView android:id="@+id/stick_name"