diff --git a/app/build.gradle b/app/build.gradle index b20c8221..7fd52f67 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -28,11 +28,6 @@ android { sourceCompatibility = javaVersion targetCompatibility = javaVersion } - kotlinOptions { - jvmTarget = javaVersion.toString() - } - - /* Build Options */ buildTypes { release { debuggable true @@ -53,9 +48,6 @@ android { shrinkResources false } } - buildFeatures { - viewBinding true - } /* Linting */ lintOptions { @@ -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 } } @@ -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 */ diff --git a/app/src/main/java/emu/skyline/AppDialog.kt b/app/src/main/java/emu/skyline/AppDialog.kt index 3c6385dc..869e7e9b 100644 --- a/app/src/main/java/emu/skyline/AppDialog.kt +++ b/app/src/main/java/emu/skyline/AppDialog.kt @@ -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) diff --git a/app/src/main/java/emu/skyline/MainActivity.kt b/app/src/main/java/emu/skyline/MainActivity.kt index e5f7c26c..8f0e4846 100644 --- a/app/src/main/java/emu/skyline/MainActivity.kt +++ b/app/src/main/java/emu/skyline/MainActivity.kt @@ -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 @@ -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 @@ -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?) { @@ -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()) } @@ -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().apply { @@ -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() diff --git a/app/src/main/java/emu/skyline/SettingsActivity.kt b/app/src/main/java/emu/skyline/SettingsActivity.kt index a497533b..e6dde61d 100644 --- a/app/src/main/java/emu/skyline/SettingsActivity.kt +++ b/app/src/main/java/emu/skyline/SettingsActivity.kt @@ -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) } @@ -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) - } - } } } diff --git a/app/src/main/java/emu/skyline/di/InputManagerProviderEntryPoint.kt b/app/src/main/java/emu/skyline/di/EntryPoints.kt similarity index 64% rename from app/src/main/java/emu/skyline/di/InputManagerProviderEntryPoint.kt rename to app/src/main/java/emu/skyline/di/EntryPoints.kt index 2b320346..9a016d91 100644 --- a/app/src/main/java/emu/skyline/di/InputManagerProviderEntryPoint.kt +++ b/app/src/main/java/emu/skyline/di/EntryPoints.kt @@ -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) @@ -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() diff --git a/app/src/main/java/emu/skyline/input/dialog/ButtonDialog.kt b/app/src/main/java/emu/skyline/input/dialog/ButtonDialog.kt index e2bece55..f0590ee3 100644 --- a/app/src/main/java/emu/skyline/input/dialog/ButtonDialog.kt +++ b/app/src/main/java/emu/skyline/input/dialog/ButtonDialog.kt @@ -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() @@ -145,7 +142,7 @@ class ButtonDialog @JvmOverloads constructor(private val item : ControllerButton val axesHistory = arrayOfNulls(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) diff --git a/app/src/main/java/emu/skyline/input/dialog/RumbleDialog.kt b/app/src/main/java/emu/skyline/input/dialog/RumbleDialog.kt index e68dc611..9439a900 100644 --- a/app/src/main/java/emu/skyline/input/dialog/RumbleDialog.kt +++ b/app/src/main/java/emu/skyline/input/dialog/RumbleDialog.kt @@ -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 diff --git a/app/src/main/java/emu/skyline/input/dialog/StickDialog.kt b/app/src/main/java/emu/skyline/input/dialog/StickDialog.kt index 055c97a8..b8713047 100644 --- a/app/src/main/java/emu/skyline/input/dialog/StickDialog.kt +++ b/app/src/main/java/emu/skyline/input/dialog/StickDialog.kt @@ -14,7 +14,6 @@ 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 @@ -22,7 +21,6 @@ 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 @@ -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() @@ -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 -> { @@ -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)) diff --git a/app/src/main/java/emu/skyline/preference/ActivityResultPreference.kt b/app/src/main/java/emu/skyline/preference/ActivityResultPreference.kt deleted file mode 100644 index d1a8cc29..00000000 --- a/app/src/main/java/emu/skyline/preference/ActivityResultPreference.kt +++ /dev/null @@ -1,20 +0,0 @@ -/* - * SPDX-License-Identifier: MPL-2.0 - * Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/) - */ - -package emu.skyline.preference - -import android.content.Context -import android.content.Intent -import android.util.AttributeSet -import androidx.preference.Preference - -/** - * Some preferences need results from activities, this delegates the results to them - */ -abstract class ActivityResultPreference @JvmOverloads constructor(context : Context?, attrs : AttributeSet? = null, defStyleAttr : Int = 0) : Preference(context, attrs, defStyleAttr) { - var requestCode = 0 - - abstract fun onActivityResult(requestCode : Int, resultCode : Int, data : Intent?) -} diff --git a/app/src/main/java/emu/skyline/preference/ControllerPreference.kt b/app/src/main/java/emu/skyline/preference/ControllerPreference.kt index abba6367..710761aa 100644 --- a/app/src/main/java/emu/skyline/preference/ControllerPreference.kt +++ b/app/src/main/java/emu/skyline/preference/ControllerPreference.kt @@ -5,10 +5,12 @@ package emu.skyline.preference -import android.app.Activity import android.content.Context import android.content.Intent import android.util.AttributeSet +import androidx.activity.ComponentActivity +import androidx.activity.result.contract.ActivityResultContracts +import androidx.preference.Preference import androidx.preference.Preference.SummaryProvider import emu.skyline.R import emu.skyline.di.getInputManager @@ -17,7 +19,12 @@ import emu.skyline.input.ControllerActivity /** * This preference is used to launch [ControllerActivity] using a preference */ -class ControllerPreference @JvmOverloads constructor(context : Context, attrs : AttributeSet? = null, defStyleAttr : Int = R.attr.preferenceStyle) : ActivityResultPreference(context, attrs, defStyleAttr) { +class ControllerPreference @JvmOverloads constructor(context : Context, attrs : AttributeSet? = null, defStyleAttr : Int = R.attr.preferenceStyle) : Preference(context, attrs, defStyleAttr) { + private val controllerCallback = (context as ComponentActivity).registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { + inputManager.syncObjects() + notifyChanged() + } + /** * The index of the controller this preference manages */ @@ -48,14 +55,5 @@ class ControllerPreference @JvmOverloads constructor(context : Context, attrs : /** * This launches [ControllerActivity] on click to configure the controller */ - override fun onClick() { - (context as Activity).startActivityForResult(Intent(context, ControllerActivity::class.java).apply { putExtra("index", index) }, requestCode) - } - - override fun onActivityResult(requestCode : Int, resultCode : Int, data : Intent?) { - if (this.requestCode == requestCode) { - inputManager.syncObjects() - notifyChanged() - } - } + override fun onClick() = controllerCallback.launch(Intent(context, ControllerActivity::class.java)) } diff --git a/app/src/main/java/emu/skyline/preference/DocumentActivity.kt b/app/src/main/java/emu/skyline/preference/DocumentActivity.kt deleted file mode 100644 index 538f015a..00000000 --- a/app/src/main/java/emu/skyline/preference/DocumentActivity.kt +++ /dev/null @@ -1,63 +0,0 @@ -/* - * SPDX-License-Identifier: MPL-2.0 - * Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/) - */ - -package emu.skyline.preference - -import android.app.Activity -import android.content.Intent -import android.os.Bundle -import androidx.appcompat.app.AppCompatActivity -import androidx.preference.PreferenceManager -import dagger.hilt.android.AndroidEntryPoint -import emu.skyline.utils.Settings -import javax.inject.Inject - -/** - * This activity is used to launch a document picker and saves the result to preferences - */ -@AndroidEntryPoint -abstract class DocumentActivity : AppCompatActivity() { - companion object { - const val KEY_NAME = "key_name" - } - - private lateinit var keyName : String - - protected abstract val actionIntent : Intent - - @Inject - lateinit var settings : Settings - - /** - * This launches the [Intent.ACTION_OPEN_DOCUMENT_TREE] intent on creation - */ - override fun onCreate(state : Bundle?) { - super.onCreate(state) - - keyName = intent.getStringExtra(KEY_NAME)!! - - this.startActivityForResult(actionIntent, 1) - } - - /** - * This changes the search location preference if the [Intent.ACTION_OPEN_DOCUMENT_TREE] has returned and [finish]es the activity - */ - public override fun onActivityResult(requestCode : Int, resultCode : Int, data : Intent?) { - super.onActivityResult(requestCode, resultCode, data) - - if (resultCode == Activity.RESULT_OK && requestCode == 1) { - val uri = data!!.data!! - - contentResolver.takePersistableUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION) - - settings.refreshRequired = true - PreferenceManager.getDefaultSharedPreferences(this).edit() - .putString(keyName, uri.toString()) - .apply() - } - setResult(resultCode) - finish() - } -} diff --git a/app/src/main/java/emu/skyline/preference/FileActivity.kt b/app/src/main/java/emu/skyline/preference/FileActivity.kt deleted file mode 100644 index 388c1e6b..00000000 --- a/app/src/main/java/emu/skyline/preference/FileActivity.kt +++ /dev/null @@ -1,18 +0,0 @@ -/* - * SPDX-License-Identifier: MPL-2.0 - * Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/) - */ - -package emu.skyline.preference - -import android.content.Intent - -/** - * Launches document picker to select one file - */ -class FileActivity : DocumentActivity() { - override val actionIntent = Intent(Intent.ACTION_OPEN_DOCUMENT).apply { - addCategory(Intent.CATEGORY_OPENABLE) - type = "*/*" - } -} diff --git a/app/src/main/java/emu/skyline/preference/FilePreference.kt b/app/src/main/java/emu/skyline/preference/FilePreference.kt deleted file mode 100644 index 8d8176cf..00000000 --- a/app/src/main/java/emu/skyline/preference/FilePreference.kt +++ /dev/null @@ -1,35 +0,0 @@ -/* - * SPDX-License-Identifier: MPL-2.0 - * Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/) - */ - -package emu.skyline.preference - -import android.app.Activity -import android.content.Context -import android.content.Intent -import android.net.Uri -import android.util.AttributeSet -import androidx.preference.PreferenceManager -import com.google.android.material.snackbar.Snackbar -import emu.skyline.KeyReader -import emu.skyline.R -import emu.skyline.SettingsActivity - -/** - * Launches [FileActivity] and process the selected file for key import - */ -class FilePreference @JvmOverloads constructor(context : Context?, attrs : AttributeSet? = null, defStyleAttr : Int = androidx.preference.R.attr.preferenceStyle) : ActivityResultPreference(context, attrs, defStyleAttr) { - override fun onClick() = (context as Activity).startActivityForResult(Intent(context, FileActivity::class.java).apply { putExtra(DocumentActivity.KEY_NAME, key) }, requestCode) - - override fun onActivityResult(requestCode : Int, resultCode : Int, data : Intent?) { - if (this.requestCode == requestCode && resultCode == Activity.RESULT_OK && (key == KeyReader.KeyType.Prod.keyName || key == KeyReader.KeyType.Title.keyName)) { - val success = KeyReader.import( - context, - Uri.parse(PreferenceManager.getDefaultSharedPreferences(context).getString(key, "")), - KeyReader.KeyType.parse(key) - ) - Snackbar.make((context as SettingsActivity).binding.root, if (success) R.string.import_keys_success else R.string.import_keys_failed, Snackbar.LENGTH_LONG).show() - } - } -} diff --git a/app/src/main/java/emu/skyline/preference/FolderActivity.kt b/app/src/main/java/emu/skyline/preference/FolderActivity.kt deleted file mode 100644 index 362b7695..00000000 --- a/app/src/main/java/emu/skyline/preference/FolderActivity.kt +++ /dev/null @@ -1,15 +0,0 @@ -/* - * SPDX-License-Identifier: MPL-2.0 - * Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/) - */ - -package emu.skyline.preference - -import android.content.Intent - -/** - * Launches document picker to select a folder - */ -class FolderActivity : DocumentActivity() { - override val actionIntent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE) -} diff --git a/app/src/main/java/emu/skyline/preference/FolderPickerPreference.kt b/app/src/main/java/emu/skyline/preference/FolderPickerPreference.kt new file mode 100644 index 00000000..1dde6451 --- /dev/null +++ b/app/src/main/java/emu/skyline/preference/FolderPickerPreference.kt @@ -0,0 +1,38 @@ +/* + * SPDX-License-Identifier: MPL-2.0 + * Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/) + */ + +package emu.skyline.preference + +import android.content.Context +import android.content.Intent +import android.net.Uri +import android.util.AttributeSet +import androidx.activity.ComponentActivity +import androidx.activity.result.contract.ActivityResultContracts +import androidx.preference.Preference +import androidx.preference.Preference.SummaryProvider +import androidx.preference.PreferenceManager +import androidx.preference.R +import emu.skyline.di.getSettings + +class FolderPickerPreference @JvmOverloads constructor(context : Context, attrs : AttributeSet? = null, defStyleAttr : Int = R.attr.preferenceStyle) : Preference(context, attrs, defStyleAttr) { + private val documentPicker = (context as ComponentActivity).registerForActivityResult(ActivityResultContracts.OpenDocumentTree()) { + it?.let { uri -> + context.contentResolver.takePersistableUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION) + + context.getSettings().refreshRequired = true + PreferenceManager.getDefaultSharedPreferences(context).edit().putString(key, uri.toString()).apply() + notifyChanged() + } + } + + init { + summaryProvider = SummaryProvider { preference -> + Uri.decode(preference.getPersistedString("")) + } + } + + override fun onClick() = documentPicker.launch(null) +} diff --git a/app/src/main/java/emu/skyline/preference/FolderPreference.kt b/app/src/main/java/emu/skyline/preference/FolderPreference.kt deleted file mode 100644 index 1af46377..00000000 --- a/app/src/main/java/emu/skyline/preference/FolderPreference.kt +++ /dev/null @@ -1,36 +0,0 @@ -/* - * SPDX-License-Identifier: MPL-2.0 - * Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/) - */ - -package emu.skyline.preference - -import android.app.Activity -import android.content.Context -import android.content.Intent -import android.net.Uri -import android.util.AttributeSet -import androidx.preference.Preference.SummaryProvider -import androidx.preference.R - -/** - * This preference shows the decoded URI of it's preference and launches [DocumentActivity] - */ -class FolderPreference @JvmOverloads constructor(context : Context?, attrs : AttributeSet? = null, defStyleAttr : Int = R.attr.preferenceStyle) : ActivityResultPreference(context, attrs, defStyleAttr) { - init { - summaryProvider = SummaryProvider { preference -> - Uri.decode(preference.getPersistedString("")) - } - } - - /** - * This launches [DocumentActivity] on click to change the directory - */ - override fun onClick() { - (context as Activity).startActivityForResult(Intent(context, FolderActivity::class.java).apply { putExtra(DocumentActivity.KEY_NAME, key) }, requestCode) - } - - override fun onActivityResult(requestCode : Int, resultCode : Int, data : Intent?) { - if (requestCode == requestCode) notifyChanged() - } -} diff --git a/app/src/main/java/emu/skyline/preference/KeyPickerPreference.kt b/app/src/main/java/emu/skyline/preference/KeyPickerPreference.kt new file mode 100644 index 00000000..ef731b55 --- /dev/null +++ b/app/src/main/java/emu/skyline/preference/KeyPickerPreference.kt @@ -0,0 +1,33 @@ +/* + * SPDX-License-Identifier: MPL-2.0 + * Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/) + */ + +package emu.skyline.preference + +import android.content.Context +import android.content.Intent +import android.util.AttributeSet +import androidx.activity.ComponentActivity +import androidx.activity.result.contract.ActivityResultContracts +import androidx.preference.Preference +import com.google.android.material.snackbar.Snackbar +import emu.skyline.KeyReader +import emu.skyline.R +import emu.skyline.SettingsActivity +import emu.skyline.di.getSettings + +class KeyPickerPreference @JvmOverloads constructor(context : Context, attrs : AttributeSet? = null, defStyleAttr : Int = androidx.preference.R.attr.preferenceStyle) : Preference(context, attrs, defStyleAttr) { + private val documentPicker = (context as ComponentActivity).registerForActivityResult(ActivityResultContracts.OpenDocument()) { + it?.let { uri -> + context.contentResolver.takePersistableUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION) + + context.getSettings().refreshRequired = true + + val success = KeyReader.import(context, uri, KeyReader.KeyType.parse(key)) + Snackbar.make((context as SettingsActivity).binding.root, if (success) R.string.import_keys_success else R.string.import_keys_failed, Snackbar.LENGTH_LONG).show() + } + } + + override fun onClick() = documentPicker.launch(null) +} diff --git a/app/src/main/java/emu/skyline/preference/LicenseDialog.kt b/app/src/main/java/emu/skyline/preference/LicenseDialog.kt index b0a2e2ca..e20a2aa5 100644 --- a/app/src/main/java/emu/skyline/preference/LicenseDialog.kt +++ b/app/src/main/java/emu/skyline/preference/LicenseDialog.kt @@ -32,8 +32,8 @@ class LicenseDialog : DialogFragment() { }.root } - override fun onActivityCreated(savedInstanceState : Bundle?) { - super.onActivityCreated(savedInstanceState) + override fun onViewCreated(view : View, savedInstanceState : Bundle?) { + super.onViewCreated(view, savedInstanceState) binding.licenseUrl.text = requireArguments().getString("libraryUrl") binding.licenseContent.text = getString(requireArguments().getInt("libraryLicense")) diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml index 381c7b81..afe79085 100644 --- a/app/src/main/res/xml/preferences.xml +++ b/app/src/main/res/xml/preferences.xml @@ -3,7 +3,7 @@ - - - diff --git a/build.gradle b/build.gradle index f117a346..4291938e 100644 --- a/build.gradle +++ b/build.gradle @@ -1,17 +1,19 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { - ext.kotlin_version = '1.4.30' - ext.lifecycle_version = '2.2.0' - ext.hilt_version = '2.31.2-alpha' + ext { + kotlin_version = '1.4.31' + lifecycle_version = '2.3.0' + hilt_version = '2.32-alpha' + compose_version = '1.0.0-beta01' + } repositories { google() - jcenter() mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:4.1.2' + classpath 'com.android.tools.build:gradle:7.0.0-alpha08' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath "com.google.dagger:hilt-android-gradle-plugin:$hilt_version" @@ -23,6 +25,8 @@ buildscript { allprojects { repositories { google() + mavenCentral() + maven { url "https://google.bintray.com/flexbox-layout" } jcenter() } }