Add Controller Setup Guide

A setup guide for controllers that goes through every available button/stick sequentially and opens up a corresponding dialog to map them.
This commit is contained in:
PixelyIon 2022-03-11 20:38:11 +05:30
parent e2cae74425
commit 5dea15632c
7 changed files with 74 additions and 29 deletions

View File

@ -35,6 +35,8 @@ class ControllerGeneralViewItem(private val controllerId : Int, val type : Gener
}
GeneralType.RumbleDevice -> controller.rumbleDeviceName ?: context.getString(R.string.none)
GeneralType.SetupGuide -> context.getString(R.string.setup_guide_description)
}
super.bind(binding, position)

View File

@ -31,6 +31,7 @@ enum class ControllerType(val stringRes : Int, val firstController : Boolean, va
enum class GeneralType(val stringRes : Int, val compatibleControllers : Array<ControllerType>? = null) {
PartnerJoyCon(R.string.partner_joycon, arrayOf(ControllerType.JoyConLeft)),
RumbleDevice(R.string.rumble_device),
SetupGuide(R.string.setup_guide),
}
/**

View File

@ -9,14 +9,15 @@ import android.content.Intent
import android.graphics.Canvas
import android.os.Bundle
import android.view.KeyEvent
import android.view.View
import android.view.ViewTreeObserver
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.app.AppCompatDialogFragment
import androidx.coordinatorlayout.widget.CoordinatorLayout
import androidx.core.view.marginTop
import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import dagger.hilt.android.AndroidEntryPoint
import emu.skyline.R
@ -55,6 +56,10 @@ class ControllerActivity : AppCompatActivity() {
*/
val axisMap = mutableMapOf<AxisId, ControllerStickViewItem>()
val stickItems = mutableListOf<ControllerStickViewItem>()
val buttonItems = mutableListOf<ControllerButtonViewItem>()
@Inject
lateinit var settings : Settings
@ -108,17 +113,14 @@ class ControllerActivity : AppCompatActivity() {
}
}
wroteTitle = false
if (controller.type.sticks.isNotEmpty())
items.add(ControllerHeaderItem(getString(R.string.sticks)))
for (stick in controller.type.sticks) {
if (!wroteTitle) {
items.add(ControllerHeaderItem(getString(R.string.sticks)))
wroteTitle = true
}
val stickItem = ControllerStickViewItem(id, stick, onControllerStickClick)
items.add(stickItem)
stickItems.add(stickItem)
buttonMap[stick.button] = stickItem
axisMap[stick.xAxis] = stickItem
axisMap[stick.yAxis] = stickItem
@ -132,32 +134,26 @@ class ControllerActivity : AppCompatActivity() {
val buttonArrays = arrayOf(dpadButtons, faceButtons, shoulderTriggerButtons, shoulderRailButtons)
for (buttonArray in buttonArrays) {
wroteTitle = false
val filteredButtons = controller.type.buttons.filter { it in buttonArray.second }
for (button in controller.type.buttons.filter { it in buttonArray.second }) {
if (!wroteTitle) {
if (filteredButtons.isNotEmpty())
items.add(ControllerHeaderItem(getString(buttonArray.first)))
wroteTitle = true
}
for (button in filteredButtons) {
val buttonItem = ControllerButtonViewItem(id, button, onControllerButtonClick)
items.add(buttonItem)
buttonItems.add(buttonItem)
buttonMap[button] = buttonItem
}
}
wroteTitle = false
items.add(ControllerHeaderItem(getString(R.string.misc_buttons))) // The menu button will always exist
for (button in controller.type.buttons.filterNot { item -> buttonArrays.any { item in it.second } }.plus(ButtonId.Menu)) {
if (!wroteTitle) {
items.add(ControllerHeaderItem(getString(R.string.misc_buttons)))
wroteTitle = true
}
val buttonItem = ControllerButtonViewItem(id, button, onControllerButtonClick)
items.add(buttonItem)
buttonItems.add(buttonItem)
buttonMap[button] = buttonItem
}
} finally {
@ -309,6 +305,18 @@ class ControllerActivity : AppCompatActivity() {
GeneralType.RumbleDevice -> {
RumbleDialog(item).show(supportFragmentManager, null)
}
GeneralType.SetupGuide -> {
var dialogFragment : BottomSheetDialogFragment? = null
for (buttonItem in buttonItems.reversed())
dialogFragment = ButtonDialog(buttonItem, dialogFragment)
for (stickItem in stickItems.reversed())
dialogFragment = StickDialog(stickItem, dialogFragment)
dialogFragment?.show(supportFragmentManager, null)
}
}
Unit
}

View File

@ -11,6 +11,8 @@ import android.os.Handler
import android.os.Looper
import android.view.*
import android.view.animation.LinearInterpolator
import androidx.coordinatorlayout.widget.CoordinatorLayout
import androidx.fragment.app.commit
import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
import emu.skyline.R
@ -25,7 +27,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) : BottomSheetDialogFragment() {
class ButtonDialog @JvmOverloads constructor(private val item : ControllerButtonViewItem? = null, private val nextDialog : BottomSheetDialogFragment? = null) : BottomSheetDialogFragment() {
private var _binding : ButtonDialogBinding? = null
private val binding get() = _binding!!
@ -42,9 +44,23 @@ class ButtonDialog @JvmOverloads constructor(private val item : ControllerButton
override fun onStart() {
super.onStart()
val behavior = BottomSheetBehavior.from(requireView().parent as View)
val parentView = requireView().parent as View
if (parentView.layoutParams is CoordinatorLayout.LayoutParams) {
val behavior = BottomSheetBehavior.from(parentView)
behavior.state = BottomSheetBehavior.STATE_EXPANDED
}
}
private fun gotoNextOrDismiss() {
if (nextDialog != null) {
parentFragmentManager.commit {
remove(this@ButtonDialog)
add(nextDialog, null)
}
} else {
dismiss()
}
}
override fun onViewCreated(view : View, savedInstanceState : Bundle?) {
super.onViewCreated(view, savedInstanceState)
@ -69,7 +85,7 @@ class ButtonDialog @JvmOverloads constructor(private val item : ControllerButton
item.update()
dismiss()
gotoNextOrDismiss()
}
// Ensure that layout animations are proper
@ -131,7 +147,7 @@ class ButtonDialog @JvmOverloads constructor(private val item : ControllerButton
item.update()
dismiss()
gotoNextOrDismiss()
}
true
@ -209,7 +225,7 @@ class ButtonDialog @JvmOverloads constructor(private val item : ControllerButton
item.update()
dismiss()
gotoNextOrDismiss()
}
axisHandler.postDelayed(axisRunnable!!, 1000)

View File

@ -12,6 +12,8 @@ import android.os.Looper
import android.util.TypedValue
import android.view.*
import android.view.animation.LinearInterpolator
import androidx.coordinatorlayout.widget.CoordinatorLayout
import androidx.fragment.app.commit
import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
import emu.skyline.R
@ -29,7 +31,7 @@ import kotlin.math.max
*
* @param item This is used to hold the [ControllerStickViewItem] between instances
*/
class StickDialog @JvmOverloads constructor(val item : ControllerStickViewItem? = null) : BottomSheetDialogFragment() {
class StickDialog @JvmOverloads constructor(val item : ControllerStickViewItem? = null, private val nextDialog : BottomSheetDialogFragment? = null) : BottomSheetDialogFragment() {
/**
* This enumerates all of the stages this dialog can be in
*/
@ -82,6 +84,17 @@ class StickDialog @JvmOverloads constructor(val item : ControllerStickViewItem?
behavior.state = BottomSheetBehavior.STATE_EXPANDED
}
private fun gotoNextOrDismiss() {
if (nextDialog != null) {
parentFragmentManager.commit {
remove(this@StickDialog)
add(nextDialog, null)
}
} else {
dismiss()
}
}
/**
* This function converts [dip] (Density Independent Pixels) to normal pixels
*/
@ -218,6 +231,8 @@ class StickDialog @JvmOverloads constructor(val item : ControllerStickViewItem?
binding.stickNext.text = getString(if (ordinal + 1 == size) R.string.done else R.string.next)
updateAnimation()
} else if (ordinal == size) {
gotoNextOrDismiss()
} else {
dismiss()
}
@ -254,7 +269,7 @@ class StickDialog @JvmOverloads constructor(val item : ControllerStickViewItem?
item.update()
dismiss()
gotoNextOrDismiss()
}
// Ensure that layout animations are proper

View File

@ -68,7 +68,8 @@
android:max="100"
android:progress="25"
android:secondaryProgressTintMode="screen"
android:visibility="gone" />
android:visibility="gone"
tools:visibility="visible" />
<Button
android:id="@+id/button_reset"

View File

@ -161,4 +161,6 @@
<!-- Misc -->
<!--suppress AndroidLintUnusedResources -->
<string name="expand_button_title" tools:override="true">Expand</string>
<string name="setup_guide">Setup Guide</string>
<string name="setup_guide_description">Sequentially map every stick and button</string>
</resources>