Skip to content

Commit

Permalink
Use activity contracts for callbacks
Browse files Browse the repository at this point in the history
  • Loading branch information
Grarak authored and PixelyIon committed Jun 17, 2021
1 parent 8dd4858 commit 8f3390f
Show file tree
Hide file tree
Showing 20 changed files with 156 additions and 336 deletions.
29 changes: 17 additions & 12 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,6 @@ android {
sourceCompatibility = javaVersion
targetCompatibility = javaVersion
}
kotlinOptions {
jvmTarget = javaVersion.toString()
}

/* Build Options */
buildTypes {
release {
debuggable true
Expand All @@ -53,9 +48,6 @@ android {
shrinkResources false
}
}
buildFeatures {
viewBinding true
}

/* Linting */
lintOptions {
Expand All @@ -75,9 +67,18 @@ android {
aaptOptions {
ignoreAssetsPattern "*.md"
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8

kotlinOptions {
jvmTarget = javaVersion.toString()
useIR = true
}
buildFeatures {
viewBinding true
prefab true
compose true
}
composeOptions {
kotlinCompilerExtensionVersion compose_version
}
}

Expand All @@ -97,9 +98,13 @@ dependencies {
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"
implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version"
implementation 'androidx.fragment:fragment-ktx:1.2.5'
implementation 'androidx.fragment:fragment-ktx:1.3.0'
implementation "com.google.dagger:hilt-android:$hilt_version"
kapt "com.google.dagger:hilt-android-compiler:$hilt_version"
implementation "androidx.compose.ui:ui:$compose_version"
implementation "androidx.compose.material:material:$compose_version"
implementation "androidx.compose.ui:ui-tooling:$compose_version"
implementation 'androidx.activity:activity-compose:1.3.0-alpha03'
implementation 'com.google.android:flexbox:2.0.1'

/* Kotlin */
Expand Down
7 changes: 2 additions & 5 deletions app/src/main/java/emu/skyline/AppDialog.kt
Original file line number Diff line number Diff line change
Expand Up @@ -68,11 +68,8 @@ class AppDialog : BottomSheetDialogFragment() {
}
}

/**
* This fills all the dialog with the information from [item] if it is valid and setup all user interaction
*/
override fun onActivityCreated(savedInstanceState : Bundle?) {
super.onActivityCreated(savedInstanceState)
override fun onViewCreated(view : View, savedInstanceState : Bundle?) {
super.onViewCreated(view, savedInstanceState)

val missingIcon = ContextCompat.getDrawable(requireActivity(), R.drawable.default_icon)!!.toBitmap(256, 256)

Expand Down
60 changes: 17 additions & 43 deletions app/src/main/java/emu/skyline/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import android.graphics.Rect
import android.net.Uri
import android.os.Bundle
import android.view.View
import androidx.activity.result.contract.ActivityResultContracts
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.app.AppCompatDelegate
Expand All @@ -20,7 +21,6 @@ import androidx.core.graphics.drawable.toBitmap
import androidx.core.view.isInvisible
import androidx.core.view.isVisible
import androidx.core.view.size
import androidx.lifecycle.observe
import androidx.preference.PreferenceManager
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView
Expand Down Expand Up @@ -75,6 +75,19 @@ class MainActivity : AppCompatActivity() {
}
}

private val documentPicker = registerForActivityResult(ActivityResultContracts.OpenDocumentTree()) {
it?.let { uri ->
contentResolver.takePersistableUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION)
settings.searchLocation = uri.toString()

loadRoms(false)
}
}

private val settingsCallback = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
if (settings.refreshRequired) loadRoms(false)
}

private fun AppItem.toViewItem() = AppViewItem(layoutType, this, missingIcon, ::selectStartGame, ::selectShowGameDialog)

override fun onCreate(savedInstanceState : Bundle?) {
Expand Down Expand Up @@ -117,12 +130,12 @@ class MainActivity : AppCompatActivity() {
}
binding.chipGroup.check(binding.chipGroup.getChildAt(settings.filter).id)

viewModel.stateData.observe(owner = this, onChanged = ::handleState)
viewModel.stateData.observe(this, ::handleState)
loadRoms(!settings.refreshRequired)

binding.searchBar.apply {
binding.logIcon.setOnClickListener { startActivity(Intent(context, LogActivity::class.java)) }
binding.settingsIcon.setOnClickListener { startActivityForResult(Intent(context, SettingsActivity::class.java), 3) }
binding.settingsIcon.setOnClickListener { settingsCallback.launch(Intent(context, SettingsActivity::class.java)) }
binding.refreshIcon.setOnClickListener { loadRoms(false) }
addTextChangedListener(afterTextChanged = { editable ->
editable?.let { text -> adapter.filter.filter(text.toString()) }
Expand Down Expand Up @@ -222,11 +235,7 @@ class MainActivity : AppCompatActivity() {
binding.appList.layoutManager = CustomLayoutManager(gridSpan)
setAppListDecoration()

if (settings.searchLocation.isEmpty()) startActivityForResult(Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).apply {
flags = Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION or
Intent.FLAG_GRANT_PREFIX_URI_PERMISSION or
Intent.FLAG_GRANT_READ_URI_PERMISSION
}, 1)
if (settings.searchLocation.isEmpty()) documentPicker.launch(null)
}

private fun getDataItems() = mutableListOf<DataItem>().apply {
Expand Down Expand Up @@ -288,41 +297,6 @@ class MainActivity : AppCompatActivity() {
if (items.isEmpty()) adapter.setItems(listOf(HeaderViewItem(getString(R.string.no_rom))))
}

/**
* This handles receiving activity result from [Intent.ACTION_OPEN_DOCUMENT_TREE], [Intent.ACTION_OPEN_DOCUMENT] and [SettingsActivity]
*/
override fun onActivityResult(requestCode : Int, resultCode : Int, intent : Intent?) {
super.onActivityResult(requestCode, resultCode, intent)

if (resultCode == RESULT_OK) {
when (requestCode) {
1 -> {
val uri = intent!!.data!!
contentResolver.takePersistableUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION)
settings.searchLocation = uri.toString()

loadRoms(!settings.refreshRequired)
}

2 -> {
try {
val intentGame = Intent(this, EmulationActivity::class.java)
intentGame.data = intent!!.data!!

if (resultCode != 0)
startActivityForResult(intentGame, resultCode)
else
startActivity(intentGame)
} catch (e : Exception) {
Snackbar.make(findViewById(android.R.id.content), getString(R.string.error) + ": ${e.localizedMessage}", Snackbar.LENGTH_SHORT).show()
}
}

3 -> if (settings.refreshRequired) loadRoms(false)
}
}
}

override fun onResume() {
super.onResume()

Expand Down
46 changes: 3 additions & 43 deletions app/src/main/java/emu/skyline/SettingsActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,11 @@

package emu.skyline

import android.content.Intent
import android.os.Bundle
import android.view.KeyEvent
import androidx.appcompat.app.AppCompatActivity
import androidx.preference.PreferenceFragmentCompat
import androidx.preference.PreferenceGroup
import emu.skyline.databinding.SettingsActivityBinding
import emu.skyline.preference.ActivityResultPreference
import emu.skyline.preference.DocumentActivity

class SettingsActivity : AppCompatActivity() {
val binding by lazy { SettingsActivityBinding.inflate(layoutInflater) }
Expand All @@ -35,57 +31,21 @@ class SettingsActivity : AppCompatActivity() {
supportActionBar?.setDisplayHomeAsUpEnabled(true)

supportFragmentManager
.beginTransaction()
.replace(R.id.settings, preferenceFragment)
.commit()
}

/**
* This is used to refresh the preferences after [DocumentActivity] or [emu.skyline.input.ControllerActivity] has returned
*/
public override fun onActivityResult(requestCode : Int, resultCode : Int, data : Intent?) {
super.onActivityResult(requestCode, resultCode, data)

preferenceFragment.delegateActivityResult(requestCode, resultCode, data)
.beginTransaction()
.replace(R.id.settings, preferenceFragment)
.commit()
}

/**
* This fragment is used to display all of the preferences
*/
class PreferenceFragment : PreferenceFragmentCompat() {
private var requestCodeCounter = 0

/**
* Delegates activity result to all preferences which implement [ActivityResultPreference]
*/
fun delegateActivityResult(requestCode : Int, resultCode : Int, data : Intent?) {
preferenceScreen.delegateActivityResult(requestCode, resultCode, data)
}

/**
* This constructs the preferences from [R.xml.preferences]
*/
override fun onCreatePreferences(savedInstanceState : Bundle?, rootKey : String?) {
setPreferencesFromResource(R.xml.preferences, rootKey)
preferenceScreen.assignActivityRequestCode()
}

private fun PreferenceGroup.assignActivityRequestCode() {
for (i in 0 until preferenceCount) {
when (val pref = getPreference(i)) {
is PreferenceGroup -> pref.assignActivityRequestCode()
is ActivityResultPreference -> pref.requestCode = requestCodeCounter++
}
}
}

private fun PreferenceGroup.delegateActivityResult(requestCode : Int, resultCode : Int, data : Intent?) {
for (i in 0 until preferenceCount) {
when (val pref = getPreference(i)) {
is PreferenceGroup -> pref.delegateActivityResult(requestCode, resultCode, data)
is ActivityResultPreference -> pref.onActivityResult(requestCode, resultCode, data)
}
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import dagger.hilt.InstallIn
import dagger.hilt.android.EntryPointAccessors
import dagger.hilt.components.SingletonComponent
import emu.skyline.input.InputManager
import emu.skyline.utils.Settings

@EntryPoint
@InstallIn(SingletonComponent::class)
Expand All @@ -14,3 +15,11 @@ interface InputManagerProviderEntryPoint {
}

fun Context.getInputManager() = EntryPointAccessors.fromApplication(this, InputManagerProviderEntryPoint::class.java).inputManager()

@EntryPoint
@InstallIn(SingletonComponent::class)
interface SettingsProviderEntryPoint {
fun settings() : Settings
}

fun Context.getSettings() = EntryPointAccessors.fromApplication(this, SettingsProviderEntryPoint::class.java).settings()
13 changes: 5 additions & 8 deletions app/src/main/java/emu/skyline/input/dialog/ButtonDialog.kt
Original file line number Diff line number Diff line change
Expand Up @@ -45,19 +45,16 @@ class ButtonDialog @JvmOverloads constructor(private val item : ControllerButton
behavior.state = BottomSheetBehavior.STATE_EXPANDED
}

/**
* This sets up all user interaction with this dialog
*/
override fun onActivityCreated(savedInstanceState : Bundle?) {
super.onActivityCreated(savedInstanceState)
override fun onViewCreated(view : View, savedInstanceState : Bundle?) {
super.onViewCreated(view, savedInstanceState)

if (item != null && context is ControllerActivity) {
val context = requireContext() as ControllerActivity
val controller = inputManager.controllers[context.id]!!

// View focus handling so all input is always directed to this view
view?.requestFocus()
view?.onFocusChangeListener = View.OnFocusChangeListener { v, hasFocus -> if (!hasFocus) v.requestFocus() }
view.requestFocus()
view.onFocusChangeListener = View.OnFocusChangeListener { v, hasFocus -> if (!hasFocus) v.requestFocus() }

// Write the text for the button's icon
binding.buttonText.text = item.button.short ?: item.button.toString()
Expand Down Expand Up @@ -145,7 +142,7 @@ class ButtonDialog @JvmOverloads constructor(private val item : ControllerButton

val axesHistory = arrayOfNulls<Float>(axes.size) // The last recorded value of an axis, this is used to eliminate any stagnant axes

view?.setOnGenericMotionListener { _, event ->
view.setOnGenericMotionListener { _, event ->
// We retrieve the value of the HAT axes so that we can check for change and ignore any input from them so it'll be passed onto the [KeyEvent] handler
val dpadX = event.getAxisValue(MotionEvent.AXIS_HAT_X)
val dpadY = event.getAxisValue(MotionEvent.AXIS_HAT_Y)
Expand Down
7 changes: 2 additions & 5 deletions app/src/main/java/emu/skyline/input/dialog/RumbleDialog.kt
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,8 @@ class RumbleDialog @JvmOverloads constructor(val item : ControllerGeneralViewIte
behavior.state = BottomSheetBehavior.STATE_EXPANDED
}

/**
* This sets up all user interaction with this dialog
*/
override fun onActivityCreated(savedInstanceState : Bundle?) {
super.onActivityCreated(savedInstanceState)
override fun onViewCreated(view : View, savedInstanceState : Bundle?) {
super.onViewCreated(view, savedInstanceState)

if (item != null && context is ControllerActivity) {
val context = requireContext() as ControllerActivity
Expand Down
17 changes: 6 additions & 11 deletions app/src/main/java/emu/skyline/input/dialog/StickDialog.kt
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,13 @@ import android.view.*
import android.view.animation.LinearInterpolator
import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
import dagger.hilt.android.AndroidEntryPoint
import emu.skyline.R
import emu.skyline.adapter.controller.ControllerStickViewItem
import emu.skyline.databinding.StickDialogBinding
import emu.skyline.di.getInputManager
import emu.skyline.input.*
import emu.skyline.input.MotionHostEvent.Companion.axes
import java.util.*
import javax.inject.Inject
import kotlin.math.abs
import kotlin.math.max

Expand Down Expand Up @@ -224,19 +222,16 @@ class StickDialog @JvmOverloads constructor(val item : ControllerStickViewItem?
}
}

/**
* This sets up all user interaction with this dialog
*/
override fun onActivityCreated(savedInstanceState : Bundle?) {
super.onActivityCreated(savedInstanceState)
override fun onViewCreated(view : View, savedInstanceState : Bundle?) {
super.onViewCreated(view, savedInstanceState)

if (item != null && context is ControllerActivity) {
val context = requireContext() as ControllerActivity
val controller = inputManager.controllers[context.id]!!

// View focus handling so all input is always directed to this view
view?.requestFocus()
view?.onFocusChangeListener = View.OnFocusChangeListener { v, hasFocus -> if (!hasFocus) v.requestFocus() }
view.requestFocus()
view.onFocusChangeListener = View.OnFocusChangeListener { v, hasFocus -> if (!hasFocus) v.requestFocus() }

// Write the text for the stick's icon
binding.stickName.text = item.stick.button.short ?: item.stick.button.toString()
Expand Down Expand Up @@ -286,7 +281,7 @@ class StickDialog @JvmOverloads constructor(val item : ControllerStickViewItem?
axisRunnable?.let { handler.removeCallbacks(it) }
}

view?.setOnKeyListener { _, _, event ->
view.setOnKeyListener { _, _, event ->
when {
// We want all input events from Joysticks and Buttons except for [KeyEvent.KEYCODE_BACK] as that will should be processed elsewhere
((event.isFromSource(InputDevice.SOURCE_CLASS_BUTTON) && event.keyCode != KeyEvent.KEYCODE_BACK) || event.isFromSource(InputDevice.SOURCE_CLASS_JOYSTICK)) && event.repeatCount == 0 -> {
Expand Down Expand Up @@ -415,7 +410,7 @@ class StickDialog @JvmOverloads constructor(val item : ControllerStickViewItem?

var oldHat = Pair(0.0f, 0.0f) // The last values of the HAT axes so that they can be ignored in [View.OnGenericMotionListener] so they are passed onto [DialogInterface.OnKeyListener] as [KeyEvent]s

view?.setOnGenericMotionListener { _, event ->
view.setOnGenericMotionListener { _, event ->
// We retrieve the value of the HAT axes so that we can check for change and ignore any input from them so it'll be passed onto the [KeyEvent] handler
val hat = Pair(event.getAxisValue(MotionEvent.AXIS_HAT_X), event.getAxisValue(MotionEvent.AXIS_HAT_Y))

Expand Down
Loading

0 comments on commit 8f3390f

Please sign in to comment.