diff --git a/app/src/main/java/emu/skyline/EmulationActivity.kt b/app/src/main/java/emu/skyline/EmulationActivity.kt index 265ffffb..1b113012 100644 --- a/app/src/main/java/emu/skyline/EmulationActivity.kt +++ b/app/src/main/java/emu/skyline/EmulationActivity.kt @@ -411,9 +411,7 @@ class EmulationActivity : AppCompatActivity(), SurfaceHolder.Callback, View.OnTo return true } - private fun onButtonStateChanged(buttonId : ButtonId, state : ButtonState) { - setButtonState(0, buttonId.value(), state.state) - } + private fun onButtonStateChanged(buttonId : ButtonId, state : ButtonState) = setButtonState(0, buttonId.value(), state.state) private fun onStickStateChanged(buttonId : ButtonId, position : PointF) { val stickId = when (buttonId) { @@ -423,7 +421,6 @@ class EmulationActivity : AppCompatActivity(), SurfaceHolder.Callback, View.OnTo else -> error("Invalid button id") } - Log.i("blaa", "$position") 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 } diff --git a/app/src/main/java/emu/skyline/MainActivity.kt b/app/src/main/java/emu/skyline/MainActivity.kt index 2d3fa610..47a467da 100644 --- a/app/src/main/java/emu/skyline/MainActivity.kt +++ b/app/src/main/java/emu/skyline/MainActivity.kt @@ -49,9 +49,7 @@ class MainActivity : AppCompatActivity() { /** * The adapter used for adding elements to [app_list] */ - private val adapter by lazy { - AppAdapter(layoutType = layoutType, onClick = ::selectStartGame, onLongClick = ::selectShowGameDialog) - } + private lateinit var adapter : AppAdapter private var reloading = AtomicBoolean() @@ -223,6 +221,7 @@ class MainActivity : AppCompatActivity() { val metrics = resources.displayMetrics val gridSpan = ceil((metrics.widthPixels / metrics.density) / itemWidth).toInt() + adapter = AppAdapter(layoutType = layoutType, onClick = ::selectStartGame, onLongClick = ::selectShowGameDialog) app_list.adapter = adapter app_list.layoutManager = when (adapter.layoutType) { LayoutType.List -> LinearLayoutManager(this).also { app_list.addItemDecoration(DividerItemDecoration(this, RecyclerView.VERTICAL)) } diff --git a/app/src/main/java/emu/skyline/adapter/AppAdapter.kt b/app/src/main/java/emu/skyline/adapter/AppAdapter.kt index f5bbec35..689bc65b 100644 --- a/app/src/main/java/emu/skyline/adapter/AppAdapter.kt +++ b/app/src/main/java/emu/skyline/adapter/AppAdapter.kt @@ -15,12 +15,13 @@ import android.view.ViewGroup import android.view.Window import android.widget.ImageView import android.widget.RelativeLayout -import android.widget.TextView import androidx.core.content.ContextCompat import androidx.core.graphics.drawable.toBitmap import androidx.recyclerview.widget.RecyclerView import emu.skyline.R import emu.skyline.data.AppItem +import kotlinx.android.extensions.LayoutContainer +import kotlinx.android.synthetic.main.app_item_grid_compact.* /** * This enumerates the type of layouts the menu can be in @@ -47,23 +48,9 @@ internal class AppAdapter(val layoutType : LayoutType, private val onClick : Int super.addHeader(BaseHeader(string)) } - /** - * The ViewHolder used by items is used to hold the views associated with an item - * - * @param parent The parent view that contains all the others - * @param icon The ImageView associated with the icon - * @param title The TextView associated with the title - * @param subtitle The TextView associated with the subtitle - */ - private class ItemViewHolder(val parent : View, var icon : ImageView, var title : TextView, var subtitle : TextView, var card : View? = null) : RecyclerView.ViewHolder(parent) + private class ItemViewHolder(override val containerView : View) : RecyclerView.ViewHolder(containerView), LayoutContainer - /** - * The ViewHolder used by headers is used to hold the views associated with an headers - * - * @param parent The parent view that contains all the others - * @param header The TextView associated with the header - */ - private class HeaderViewHolder(val parent : View, var header : TextView? = null) : RecyclerView.ViewHolder(parent) + private class HeaderViewHolder(override val containerView : View) : RecyclerView.ViewHolder(containerView), LayoutContainer /** * This function creates the view-holder of type [viewType] with the layout parent as [parent] @@ -79,20 +66,9 @@ internal class AppAdapter(val layoutType : LayoutType, private val onClick : Int } return when (ElementType.values()[viewType]) { - ElementType.Item -> { - ItemViewHolder(view, view.findViewById(R.id.icon), view.findViewById(R.id.text_title), view.findViewById(R.id.text_subtitle)).apply { - if (layoutType == LayoutType.Grid || layoutType == LayoutType.GridCompact) { - card = view.findViewById(R.id.app_item_grid) - title.isSelected = true - } - } - } + ElementType.Item -> ItemViewHolder(view) - ElementType.Header -> { - HeaderViewHolder(view).apply { - header = view.findViewById(R.id.text_title) - } - } + ElementType.Header -> HeaderViewHolder(view) } } @@ -103,8 +79,8 @@ internal class AppAdapter(val layoutType : LayoutType, private val onClick : Int val item = getItem(position) if (item is AppItem && holder is ItemViewHolder) { - holder.title.text = item.title - holder.subtitle.text = item.subTitle ?: item.loaderResultString(holder.subtitle.context) + holder.text_title.text = item.title + holder.text_subtitle.text = item.subTitle ?: item.loaderResultString(holder.text_subtitle.context) holder.icon.setImageBitmap(item.icon ?: missingIcon) @@ -114,13 +90,13 @@ internal class AppAdapter(val layoutType : LayoutType, private val onClick : Int when (layoutType) { LayoutType.List -> holder.itemView - LayoutType.Grid, LayoutType.GridCompact -> holder.card!! + LayoutType.Grid, LayoutType.GridCompact -> holder.card_app_item_grid }.apply { setOnClickListener { onClick.invoke(item) } setOnLongClickListener { true.also { onLongClick.invoke(item) } } } } else if (item is BaseHeader && holder is HeaderViewHolder) { - holder.header!!.text = item.title + holder.text_title.text = item.title } } diff --git a/app/src/main/java/emu/skyline/adapter/ControllerAdapter.kt b/app/src/main/java/emu/skyline/adapter/ControllerAdapter.kt index 504be4f1..fe23b3ca 100644 --- a/app/src/main/java/emu/skyline/adapter/ControllerAdapter.kt +++ b/app/src/main/java/emu/skyline/adapter/ControllerAdapter.kt @@ -9,11 +9,13 @@ import android.content.Context import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import android.widget.TextView import androidx.recyclerview.widget.RecyclerView import emu.skyline.R import emu.skyline.data.BaseItem import emu.skyline.input.* +import kotlinx.android.extensions.LayoutContainer +import kotlinx.android.synthetic.main.controller_item.* +import kotlinx.android.synthetic.main.section_item.text_title /** * This is a class that holds everything relevant to a single item in the controller configuration list @@ -22,14 +24,8 @@ import emu.skyline.input.* * @param subContent The secondary line of text to show data more specific data about the item */ abstract class ControllerItem(var content : String, var subContent : String) : BaseItem() { - /** - * The underlying adapter this item is contained within - */ - var adapter : ControllerAdapter? = null + lateinit var adapter : ControllerAdapter - /** - * The position of this item in the adapter - */ var position : Int? = null /** @@ -42,7 +38,7 @@ abstract class ControllerItem(var content : String, var subContent : String) : B if (subContent != null) this.subContent = subContent - position?.let { adapter?.notifyItemChanged(it) } + position?.let { adapter.notifyItemChanged(it) } } /** @@ -149,71 +145,45 @@ class ControllerStickItem(val context : ControllerActivity, val stick : StickId) override fun update() = update(null, getSummary(context, stick)) } +class ControllerCheckBox() + /** * This adapter is used to create a list which handles having a simple view */ class ControllerAdapter(private val onItemClickCallback : (item : ControllerItem) -> Unit) : HeaderAdapter() { - /** - * This adds a header to the view with the contents of [string] - */ fun addHeader(string : String) { super.addHeader(BaseHeader(string)) } - /** - * This functions sets [ControllerItem.adapter] and delegates the call to [HeaderAdapter.addItem] - */ fun addItem(item : ControllerItem) { item.adapter = this super.addItem(item) } - /** - * The ViewHolder used by items is used to hold the views associated with an item - * - * @param parent The parent view that contains all the others - * @param title The TextView associated with the title - * @param subtitle The TextView associated with the subtitle - * @param item The View containing the two other views - */ - class ItemViewHolder(val parent : View, var title : TextView, var subtitle : TextView, var item : View) : RecyclerView.ViewHolder(parent) + private class ItemViewHolder(override val containerView : View) : RecyclerView.ViewHolder(containerView), LayoutContainer - /** - * The ViewHolder used by headers is used to hold the views associated with an headers - * - * @param parent The parent view that contains all the others - * @param header The TextView associated with the header - */ - private class HeaderViewHolder(val parent : View, var header : TextView? = null) : RecyclerView.ViewHolder(parent) + private class HeaderViewHolder(override val containerView : View) : RecyclerView.ViewHolder(containerView), LayoutContainer - /** - * This function creates the view-holder of type [viewType] with the layout parent as [parent] - */ - override fun onCreateViewHolder(parent : ViewGroup, viewType : Int) = when (ElementType.values()[viewType]) { - ElementType.Header -> LayoutInflater.from(parent.context).inflate(R.layout.section_item, parent, false).let { view -> - HeaderViewHolder(view).apply { header = view.findViewById(R.id.text_title) } - } + override fun onCreateViewHolder(parent : ViewGroup, viewType : Int) : RecyclerView.ViewHolder = LayoutInflater.from(parent.context).let { layoutInflater -> + when (ElementType.values()[viewType]) { + ElementType.Header -> HeaderViewHolder(layoutInflater.inflate(R.layout.section_item, parent, false)) - ElementType.Item -> LayoutInflater.from(parent.context).inflate(R.layout.controller_item, parent, false).let { view -> - ItemViewHolder(view, view.findViewById(R.id.text_title), view.findViewById(R.id.text_subtitle), view.findViewById(R.id.controller_item)) + ElementType.Item -> ItemViewHolder(layoutInflater.inflate(R.layout.controller_item, parent, false)) } } - /** - * This function binds the item at [position] to the supplied [holder] - */ override fun onBindViewHolder(holder : RecyclerView.ViewHolder, position : Int) { val item = getItem(position) if (item is ControllerItem && holder is ItemViewHolder) { item.position = position - holder.title.text = item.content - holder.subtitle.text = item.subContent + holder.text_title.text = item.content + holder.text_subtitle.text = item.subContent - holder.parent.setOnClickListener { onItemClickCallback.invoke(item) } + holder.itemView.setOnClickListener { onItemClickCallback.invoke(item) } } else if (item is BaseHeader && holder is HeaderViewHolder) { - holder.header?.text = item.title + holder.text_title.text = item.title } } } diff --git a/app/src/main/java/emu/skyline/adapter/LogAdapter.kt b/app/src/main/java/emu/skyline/adapter/LogAdapter.kt index 841c0cdc..e845b5cf 100644 --- a/app/src/main/java/emu/skyline/adapter/LogAdapter.kt +++ b/app/src/main/java/emu/skyline/adapter/LogAdapter.kt @@ -11,11 +11,12 @@ import android.content.Context import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import android.widget.TextView import android.widget.Toast import androidx.recyclerview.widget.RecyclerView import emu.skyline.R import emu.skyline.data.BaseItem +import kotlinx.android.extensions.LayoutContainer +import kotlinx.android.synthetic.main.log_item.* /** * This class is used to hold all data about a log entry @@ -55,22 +56,9 @@ internal class LogAdapter internal constructor(val context : Context, val compac } } - /** - * The ViewHolder used by items is used to hold the views associated with an item - * - * @param parent The parent view that contains all the others - * @param title The TextView associated with the title - * @param subtitle The TextView associated with the subtitle - */ - private class ItemViewHolder(val parent : View, var title : TextView, var subtitle : TextView? = null) : RecyclerView.ViewHolder(parent) + private class ItemViewHolder(override val containerView : View) : RecyclerView.ViewHolder(containerView), LayoutContainer - /** - * The ViewHolder used by headers is used to hold the views associated with an headers - * - * @param parent The parent view that contains all the others - * @param header The TextView associated with the header - */ - private class HeaderViewHolder(val parent : View, var header : TextView) : RecyclerView.ViewHolder(parent) + private class HeaderViewHolder(override val containerView : View) : RecyclerView.ViewHolder(containerView), LayoutContainer /** * This function creates the view-holder of type [viewType] with the layout parent as [parent] @@ -87,14 +75,14 @@ internal class LogAdapter internal constructor(val context : Context, val compac return when (ElementType.values()[viewType]) { ElementType.Item -> { if (compact) { - ItemViewHolder(view, view.findViewById(R.id.text_title)) + ItemViewHolder(view) } else { - ItemViewHolder(view, view.findViewById(R.id.text_title), view.findViewById(R.id.text_subtitle)) + ItemViewHolder(view) } } ElementType.Header -> { - HeaderViewHolder(view, view.findViewById(R.id.text_title)) + HeaderViewHolder(view) } } } @@ -106,15 +94,15 @@ internal class LogAdapter internal constructor(val context : Context, val compac val item = getItem(position) if (item is LogItem && holder is ItemViewHolder) { - holder.title.text = item.message - holder.subtitle?.text = item.level + holder.text_title.text = item.message + holder.text_subtitle?.text = item.level - holder.parent.setOnClickListener { + holder.itemView.setOnClickListener { clipboard.setPrimaryClip(ClipData.newPlainText("Log Message", item.message + " (" + item.level + ")")) Toast.makeText(holder.itemView.context, "Copied to clipboard", Toast.LENGTH_LONG).show() } } else if (item is BaseHeader && holder is HeaderViewHolder) { - holder.header.text = item.title + holder.text_title.text = item.title } } } diff --git a/app/src/main/java/emu/skyline/input/ControllerActivity.kt b/app/src/main/java/emu/skyline/input/ControllerActivity.kt index 12809643..5eb7bcf8 100644 --- a/app/src/main/java/emu/skyline/input/ControllerActivity.kt +++ b/app/src/main/java/emu/skyline/input/ControllerActivity.kt @@ -25,7 +25,7 @@ class ControllerActivity : AppCompatActivity() { /** * The index of the controller this activity manages */ - var id : Int = -1 + val id by lazy { intent.getIntExtra("index", 0) } /** * The adapter used by [controller_list] to hold all the items @@ -55,6 +55,8 @@ class ControllerActivity : AppCompatActivity() { if (controller.type == ControllerType.None) return + + var wroteTitle = false for (item in GeneralType.values()) { @@ -128,8 +130,6 @@ class ControllerActivity : AppCompatActivity() { override fun onCreate(state : Bundle?) { super.onCreate(state) - id = intent.getIntExtra("index", 0) - if (id < 0 || id > 7) throw IllegalArgumentException() @@ -159,7 +159,7 @@ class ControllerActivity : AppCompatActivity() { is ControllerTypeItem -> { val controller = InputManager.controllers[id]!! - val types = ControllerType.values().filter { !it.firstController || id == 0 } + val types = ControllerType.values().apply { if (id != 0) filter { !it.firstController } } val typeNames = types.map { getString(it.stringRes) }.toTypedArray() MaterialAlertDialogBuilder(this) 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 f12d4d54..e60b477d 100644 --- a/app/src/main/java/emu/skyline/input/onscreen/OnScreenControllerView.kt +++ b/app/src/main/java/emu/skyline/input/onscreen/OnScreenControllerView.kt @@ -87,7 +87,7 @@ class OnScreenControllerView @JvmOverloads constructor( val outerToInner = joystick.outerToInner() val outerToInnerLength = outerToInner.length() val direction = outerToInner.normalize() - val duration = (150f * outerToInnerLength / radius).roundToLong() + val duration = (50f * outerToInnerLength / radius).roundToLong() joystickAnimators[joystick] = ValueAnimator.ofFloat(outerToInnerLength, 0f).apply { addUpdateListener { animation -> val value = animation.animatedValue as Float @@ -106,6 +106,8 @@ class OnScreenControllerView @JvmOverloads constructor( override fun onAnimationEnd(animation : Animator?) { super.onAnimationEnd(animation) + if (joystick.shortDoubleTapped) + onButtonStateChangedListener?.invoke(joystick.buttonId, ButtonState.Released) joystick.onFingerUp(event.x, event.y) invalidate() } @@ -123,6 +125,8 @@ class OnScreenControllerView @JvmOverloads constructor( joystickAnimators[joystick] = null joystick.touchPointerId = pointerId joystick.onFingerDown(x, y) + if (joystick.shortDoubleTapped) + onButtonStateChangedListener?.invoke(joystick.buttonId, ButtonState.Pressed) performClick() handled = true } @@ -139,7 +143,7 @@ class OnScreenControllerView @JvmOverloads constructor( } } - handled.also { if (it) invalidate() } + handled.also { if (it) invalidate() else super.onTouchEvent(event) } } private val editingTouchHandler = OnTouchListener { _, event -> @@ -169,7 +173,7 @@ class OnScreenControllerView @JvmOverloads constructor( } } false - }.also { handled -> if (handled) invalidate() } + }.also { handled -> if (handled) invalidate() else super.onTouchEvent(event) } } init { 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 2ed0c01b..1a51b8f8 100644 --- a/app/src/main/java/emu/skyline/input/onscreen/OnScreenItemDefinitions.kt +++ b/app/src/main/java/emu/skyline/input/onscreen/OnScreenItemDefinitions.kt @@ -7,6 +7,7 @@ package emu.skyline.input.onscreen import android.graphics.Canvas import android.graphics.PointF +import android.os.SystemClock import androidx.core.graphics.minus import emu.skyline.R import emu.skyline.input.ButtonId @@ -59,6 +60,11 @@ class JoystickButton( private val innerButton = CircularButton(onScreenControllerView, buttonId, config.relativeX, config.relativeY, defaultRelativeRadiusToX * 0.75f, R.drawable.ic_stick) + private var fingerDownTime = 0L + private var fingerUpTime = 0L + var shortDoubleTapped = false + private set + override fun renderCenteredText(canvas : Canvas, text : String, size : Float, x : Float, y : Float) = Unit override fun render(canvas : Canvas) { @@ -74,12 +80,23 @@ class JoystickButton( relativeY = (y - heightDiff) / adjustedHeight innerButton.relativeX = relativeX innerButton.relativeY = relativeY + + val currentTime = SystemClock.elapsedRealtime() + val firstTapDiff = fingerUpTime - fingerDownTime + val secondTapDiff = currentTime - fingerUpTime + if (firstTapDiff in 0..500 && secondTapDiff in 0..500) { + shortDoubleTapped = true + } + fingerDownTime = currentTime } override fun onFingerUp(x : Float, y : Float) { loadConfigValues() innerButton.relativeX = relativeX innerButton.relativeY = relativeY + + fingerUpTime = SystemClock.elapsedRealtime() + shortDoubleTapped = false } fun onFingerMoved(x : Float, y : Float) : PointF { @@ -91,6 +108,11 @@ class JoystickButton( finger = position.add(outerToInner.multiply(1f / distance * radius)) } + if (distance > radius * 0.075f) { + fingerDownTime = 0 + fingerUpTime = 0 + } + innerButton.relativeX = finger.x / width innerButton.relativeY = (finger.y - heightDiff) / adjustedHeight return finger.minus(position).multiply(1f / radius) @@ -100,6 +122,7 @@ class JoystickButton( override fun edit(x : Float, y : Float) { super.edit(x, y) + innerButton.relativeX = relativeX innerButton.relativeY = relativeY } diff --git a/app/src/main/res/layout/app_item_grid.xml b/app/src/main/res/layout/app_item_grid.xml index a38fa7c3..007bdbb4 100644 --- a/app/src/main/res/layout/app_item_grid.xml +++ b/app/src/main/res/layout/app_item_grid.xml @@ -6,7 +6,7 @@ android:layout_height="wrap_content">