Add joystick press and general clean up

This commit is contained in:
Willi Ye 2020-10-03 12:10:16 +02:00 committed by ◱ PixelyIon
parent 3057e4b29a
commit e023dbbf0a
10 changed files with 77 additions and 120 deletions

View File

@ -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
}

View File

@ -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)) }

View File

@ -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
}
}

View File

@ -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<ControllerItem?, BaseHeader, RecyclerView.ViewHolder>() {
/**
* 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
}
}
}

View File

@ -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
}
}
}

View File

@ -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)

View File

@ -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 {

View File

@ -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
}

View File

@ -6,7 +6,7 @@
android:layout_height="wrap_content">
<androidx.cardview.widget.CardView
android:id="@+id/app_item_grid"
android:id="@+id/card_app_item_grid"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"

View File

@ -6,7 +6,7 @@
android:layout_height="wrap_content">
<androidx.cardview.widget.CardView
android:id="@+id/app_item_grid"
android:id="@+id/card_app_item_grid"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"