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.RumbleDevice -> controller.rumbleDeviceName ?: context.getString(R.string.none)
GeneralType.SetupGuide -> context.getString(R.string.setup_guide_description)
} }
super.bind(binding, position) 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) { enum class GeneralType(val stringRes : Int, val compatibleControllers : Array<ControllerType>? = null) {
PartnerJoyCon(R.string.partner_joycon, arrayOf(ControllerType.JoyConLeft)), PartnerJoyCon(R.string.partner_joycon, arrayOf(ControllerType.JoyConLeft)),
RumbleDevice(R.string.rumble_device), 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.graphics.Canvas
import android.os.Bundle import android.os.Bundle
import android.view.KeyEvent import android.view.KeyEvent
import android.view.View
import android.view.ViewTreeObserver import android.view.ViewTreeObserver
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.app.AppCompatDialogFragment
import androidx.coordinatorlayout.widget.CoordinatorLayout import androidx.coordinatorlayout.widget.CoordinatorLayout
import androidx.core.view.marginTop import androidx.core.view.marginTop
import androidx.recyclerview.widget.DividerItemDecoration import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import emu.skyline.R import emu.skyline.R
@ -55,6 +56,10 @@ class ControllerActivity : AppCompatActivity() {
*/ */
val axisMap = mutableMapOf<AxisId, ControllerStickViewItem>() val axisMap = mutableMapOf<AxisId, ControllerStickViewItem>()
val stickItems = mutableListOf<ControllerStickViewItem>()
val buttonItems = mutableListOf<ControllerButtonViewItem>()
@Inject @Inject
lateinit var settings : Settings 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) { for (stick in controller.type.sticks) {
if (!wroteTitle) {
items.add(ControllerHeaderItem(getString(R.string.sticks)))
wroteTitle = true
}
val stickItem = ControllerStickViewItem(id, stick, onControllerStickClick) val stickItem = ControllerStickViewItem(id, stick, onControllerStickClick)
items.add(stickItem) items.add(stickItem)
stickItems.add(stickItem)
buttonMap[stick.button] = stickItem buttonMap[stick.button] = stickItem
axisMap[stick.xAxis] = stickItem axisMap[stick.xAxis] = stickItem
axisMap[stick.yAxis] = stickItem axisMap[stick.yAxis] = stickItem
@ -132,32 +134,26 @@ class ControllerActivity : AppCompatActivity() {
val buttonArrays = arrayOf(dpadButtons, faceButtons, shoulderTriggerButtons, shoulderRailButtons) val buttonArrays = arrayOf(dpadButtons, faceButtons, shoulderTriggerButtons, shoulderRailButtons)
for (buttonArray in buttonArrays) { 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 (filteredButtons.isNotEmpty())
if (!wroteTitle) { items.add(ControllerHeaderItem(getString(buttonArray.first)))
items.add(ControllerHeaderItem(getString(buttonArray.first)))
wroteTitle = true
}
for (button in filteredButtons) {
val buttonItem = ControllerButtonViewItem(id, button, onControllerButtonClick) val buttonItem = ControllerButtonViewItem(id, button, onControllerButtonClick)
items.add(buttonItem) items.add(buttonItem)
buttonItems.add(buttonItem)
buttonMap[button] = 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)) { 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) val buttonItem = ControllerButtonViewItem(id, button, onControllerButtonClick)
items.add(buttonItem) items.add(buttonItem)
buttonItems.add(buttonItem)
buttonMap[button] = buttonItem buttonMap[button] = buttonItem
} }
} finally { } finally {
@ -309,6 +305,18 @@ class ControllerActivity : AppCompatActivity() {
GeneralType.RumbleDevice -> { GeneralType.RumbleDevice -> {
RumbleDialog(item).show(supportFragmentManager, null) 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 Unit
} }

View File

@ -11,6 +11,8 @@ import android.os.Handler
import android.os.Looper import android.os.Looper
import android.view.* import android.view.*
import android.view.animation.LinearInterpolator 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.BottomSheetBehavior
import com.google.android.material.bottomsheet.BottomSheetDialogFragment import com.google.android.material.bottomsheet.BottomSheetDialogFragment
import emu.skyline.R import emu.skyline.R
@ -25,7 +27,7 @@ import kotlin.math.abs
* *
* @param item This is used to hold the [ControllerButtonViewItem] between instances * @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 var _binding : ButtonDialogBinding? = null
private val binding get() = _binding!! private val binding get() = _binding!!
@ -42,8 +44,22 @@ class ButtonDialog @JvmOverloads constructor(private val item : ControllerButton
override fun onStart() { override fun onStart() {
super.onStart() super.onStart()
val behavior = BottomSheetBehavior.from(requireView().parent as View) val parentView = requireView().parent as View
behavior.state = BottomSheetBehavior.STATE_EXPANDED 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?) { override fun onViewCreated(view : View, savedInstanceState : Bundle?) {
@ -69,7 +85,7 @@ class ButtonDialog @JvmOverloads constructor(private val item : ControllerButton
item.update() item.update()
dismiss() gotoNextOrDismiss()
} }
// Ensure that layout animations are proper // Ensure that layout animations are proper
@ -131,7 +147,7 @@ class ButtonDialog @JvmOverloads constructor(private val item : ControllerButton
item.update() item.update()
dismiss() gotoNextOrDismiss()
} }
true true
@ -209,7 +225,7 @@ class ButtonDialog @JvmOverloads constructor(private val item : ControllerButton
item.update() item.update()
dismiss() gotoNextOrDismiss()
} }
axisHandler.postDelayed(axisRunnable!!, 1000) axisHandler.postDelayed(axisRunnable!!, 1000)

View File

@ -12,6 +12,8 @@ import android.os.Looper
import android.util.TypedValue import android.util.TypedValue
import android.view.* import android.view.*
import android.view.animation.LinearInterpolator 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.BottomSheetBehavior
import com.google.android.material.bottomsheet.BottomSheetDialogFragment import com.google.android.material.bottomsheet.BottomSheetDialogFragment
import emu.skyline.R import emu.skyline.R
@ -29,7 +31,7 @@ import kotlin.math.max
* *
* @param item This is used to hold the [ControllerStickViewItem] between instances * @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 * 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 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 * 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) binding.stickNext.text = getString(if (ordinal + 1 == size) R.string.done else R.string.next)
updateAnimation() updateAnimation()
} else if (ordinal == size) {
gotoNextOrDismiss()
} else { } else {
dismiss() dismiss()
} }
@ -254,7 +269,7 @@ class StickDialog @JvmOverloads constructor(val item : ControllerStickViewItem?
item.update() item.update()
dismiss() gotoNextOrDismiss()
} }
// Ensure that layout animations are proper // Ensure that layout animations are proper

View File

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

View File

@ -161,4 +161,6 @@
<!-- Misc --> <!-- Misc -->
<!--suppress AndroidLintUnusedResources --> <!--suppress AndroidLintUnusedResources -->
<string name="expand_button_title" tools:override="true">Expand</string> <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> </resources>