Refactor Preferences and Loaders

This refactors all preferences and loaders in regards to This commit mainly refactors the adapters by adding spacing, comments and following other guidelines. In addition, preferences are now moved into their own sub-package and `LicensePreference` has a minor UI update.
This commit is contained in:
◱ PixelyIon 2020-04-12 21:42:46 +05:30 committed by ◱ PixelyIon
parent c9dcb070ad
commit ce4d295d81
13 changed files with 220 additions and 205 deletions

View File

@ -38,7 +38,12 @@
android:name="android.support.PARENT_ACTIVITY"
android:value="emu.skyline.MainActivity" />
</activity>
<activity android:name="emu.skyline.utility.FolderActivity">
<activity android:name="emu.skyline.preference.FolderActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="emu.skyline.SettingsActivity" />
</activity>
<activity android:name="emu.skyline.preference.LicenseDialog">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="emu.skyline.SettingsActivity" />
@ -47,7 +52,8 @@
android:name="emu.skyline.EmulationActivity"
android:configChanges="orientation|screenSize"
android:launchMode="singleInstance"
android:screenOrientation="landscape">
android:screenOrientation="landscape"
tools:ignore="LockedOrientationActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="emu.skyline.MainActivity" />

View File

@ -22,7 +22,9 @@ import java.util.*
* An enumeration of all supported ROM formats
*/
enum class RomFormat {
NRO, XCI, NSP
NRO,
XCI,
NSP,
}
/**
@ -80,7 +82,7 @@ class AppEntry : Serializable {
val nameIndex: Int = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME)
cursor.moveToFirst()
cursor.getString(nameIndex)
}!!
}!!.dropLast(format.name.length + 1)
this.format = format
this.uri = uri
}
@ -125,12 +127,12 @@ class AppEntry : Serializable {
*/
internal abstract class BaseLoader(val context: Context, val format: RomFormat) {
/**
* This returns an AppEntry object for the supplied document
* This is used to get the [AppEntry] for the specified [file] at the supplied [uri]
*/
abstract fun getAppEntry(file: RandomAccessDocument, uri: Uri): AppEntry
/**
* This returns if the supplied document is a valid ROM or not
* This returns if the supplied [file] is a valid ROM or not
*/
abstract fun verifyFile(file: RandomAccessDocument): Boolean
}

View File

@ -15,6 +15,9 @@ import java.io.IOException
* This loader is used to load in NRO (Nintendo Relocatable Object) files (https://switchbrew.org/wiki/NRO)
*/
internal class NroLoader(context: Context) : BaseLoader(context, RomFormat.NRO) {
/**
* This is used to get the [AppEntry] for the specified NRO
*/
override fun getAppEntry(file: RandomAccessDocument, uri: Uri): AppEntry {
return try {
file.seek(0x18) // Skip to NroHeader.size
@ -54,6 +57,9 @@ internal class NroLoader(context: Context) : BaseLoader(context, RomFormat.NRO)
}
}
/**
* This verifies if [file] is a valid NRO file
*/
override fun verifyFile(file: RandomAccessDocument): Boolean {
try {
file.seek(0x10) // Skip to NroHeader.magic

View File

@ -3,7 +3,7 @@
* Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
*/
package emu.skyline.utility
package emu.skyline.preference
import android.app.Activity
import android.content.Intent
@ -11,26 +11,39 @@ import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.preference.PreferenceManager
/**
* This activity is used to select a new search location and set preferences to reflect that
*/
class FolderActivity : AppCompatActivity() {
/**
* This launches the [Intent.ACTION_OPEN_DOCUMENT_TREE] intent on creation
*/
override fun onCreate(state: Bundle?) {
super.onCreate(state)
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE)
this.startActivityForResult(intent, 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) {
if (requestCode == 1) {
val uri = data!!.data!!
contentResolver.takePersistableUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION)
PreferenceManager.getDefaultSharedPreferences(this).edit()
.putString("search_location", uri.toString())
.putBoolean("refresh_required", true)
.apply()
finish()
}
} else
finish()
}
finish()
}
}

View File

@ -0,0 +1,59 @@
/*
* SPDX-License-Identifier: LGPL-3.0-or-later
* 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
import androidx.preference.R
/**
* This preference shows the decoded URI of it's preference and launches [FolderActivity]
*/
class FolderPreference : Preference {
/**
* The directory the preference is currently set to
*/
private var mDirectory: String? = null
constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {
summaryProvider = SimpleSummaryProvider()
}
constructor(context: Context?, attrs: AttributeSet?) : this(context, attrs, R.attr.preferenceStyle)
constructor(context: Context?) : this(context, null)
/**
* This launches [FolderActivity] on click to change the directory
*/
override fun onClick() {
val intent = Intent(context, FolderActivity::class.java)
(context as Activity).startActivityForResult(intent, 0)
}
/**
* This sets the initial value of [mDirectory]
*/
override fun onSetInitialValue(defaultValue: Any?) {
mDirectory = getPersistedString(defaultValue as String?)
}
/**
* This [Preference.SummaryProvider] is used to set the summary for URI values
*/
private class SimpleSummaryProvider : SummaryProvider<FolderPreference> {
/**
* This returns the decoded URI of the directory as the summary
*/
override fun provideSummary(preference: FolderPreference): CharSequence {
return Uri.decode(preference.mDirectory) ?: ""
}
}
}

View File

@ -0,0 +1,47 @@
/*
* SPDX-License-Identifier: LGPL-3.0-or-later
* Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
*/
package emu.skyline.preference
import android.graphics.Rect
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.Window
import androidx.fragment.app.DialogFragment
import emu.skyline.R
import kotlinx.android.synthetic.main.license_dialog.*
/**
* This dialog is used to display the contents of a license for a particular project
*/
class LicenseDialog : DialogFragment() {
/**
* This inflates the layout of the dialog and sets the minimum width/height to 90% of the screen size
*/
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val layout = layoutInflater.inflate(R.layout.license_dialog, container)
val displayRectangle = Rect()
val window: Window = activity!!.window
window.decorView.getWindowVisibleDisplayFrame(displayRectangle)
layout.minimumWidth = ((displayRectangle.width() * 0.9f).toInt())
layout.minimumHeight = ((displayRectangle.height() * 0.9f).toInt())
return layout
}
/**
* This sets the [license_url] and [license_content] based on arguments passed
*/
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
license_url.text = arguments?.getString("libraryUrl")!!
license_content.text = context?.getString(arguments?.getInt("libraryLicense")!!)!!
}
}

View File

@ -3,30 +3,49 @@
* Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
*/
package emu.skyline.utility
package emu.skyline.preference
import android.content.Context
import android.os.Bundle
import android.util.AttributeSet
import android.util.Log
import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.DialogFragment.STYLE_NORMAL
import androidx.fragment.app.FragmentManager
import androidx.preference.Preference
import emu.skyline.R
import emu.skyline.SettingsActivity
/**
* This preference is used to show licenses and the source of a library
*/
class LicensePreference : Preference {
/**
* The [FragmentManager] is used to show the [LicenseDialog] fragment
*/
private val fragmentManager: FragmentManager
/**
* The tag used by this preference when launching a corresponding fragment
*/
private val mDialogFragmentTag = "LicensePreference"
/**
* The URL of the library
*/
private var libraryUrl: String? = null
/**
* The contents of the license of this library
*/
private var libraryLicense: Int? = null
/**
* The constructor assigns the [fragmentManager] from the activity and finds [libraryUrl] and [libraryLicense] in the attributes
*/
constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int, defStyleRes: Int) : super(context, attrs, defStyleAttr, defStyleRes) {
fragmentManager = (context as AppCompatActivity).supportFragmentManager
for (i in 0 until attrs!!.attributeCount) {
val attr = attrs.getAttributeName(i)
if (attr.equals("libraryUrl", ignoreCase = true))
libraryUrl = attrs.getAttributeValue(i)
else if (attr.equals("libraryLicense", ignoreCase = true))
@ -34,25 +53,26 @@ class LicensePreference : Preference {
}
}
constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : this(context, attrs, defStyleAttr, R.style.LicenseDialogTheme)
constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : this(context, attrs, defStyleAttr, 0)
constructor(context: Context?, attrs: AttributeSet?) : this(context, attrs, R.attr.dialogPreferenceStyle)
constructor(context: Context?) : this(context, null)
/**
* The [LicenseDialog] fragment is shown using [fragmentManager] on click with [libraryUrl] and [libraryLicense] passed as arguments
*/
override fun onClick() {
if (fragmentManager.findFragmentByTag(mDialogFragmentTag) != null)
return
val fragment = LicenseDialog()
val dialog = LicenseDialog()
val bundle = Bundle(2)
bundle.putString("libraryUrl", libraryUrl!!)
bundle.putInt("libraryLicense", libraryLicense!!)
fragment.arguments = bundle
dialog.arguments = bundle
fragment.setStyle(STYLE_NORMAL, R.style.LicenseDialogTheme)
fragment.show(fragmentManager, mDialogFragmentTag)
dialog.show(fragmentManager, mDialogFragmentTag)
}
}

View File

@ -3,13 +3,16 @@
* Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
*/
package emu.skyline.utility
package emu.skyline.preference
import android.content.Context
import android.util.AttributeSet
import androidx.appcompat.app.AppCompatDelegate
import androidx.preference.ListPreference
/**
* This preference is used to set the theme to Light/Dark mode
*/
class ThemePreference : ListPreference {
constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr)
@ -17,6 +20,9 @@ class ThemePreference : ListPreference {
constructor(context: Context?) : super(context)
/**
* This changes [AppCompatDelegate.sDefaultNightMode] based on what the user's selection is
*/
override fun callChangeListener(newValue: Any?): Boolean {
AppCompatDelegate.setDefaultNightMode(when ((newValue as String).toInt()) {
0 -> AppCompatDelegate.MODE_NIGHT_NO
@ -24,6 +30,7 @@ class ThemePreference : ListPreference {
2 -> AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM
else -> AppCompatDelegate.MODE_NIGHT_UNSPECIFIED
})
return super.callChangeListener(newValue)
}
}

View File

@ -1,115 +0,0 @@
/*
* SPDX-License-Identifier: LGPL-3.0-or-later
* Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
*/
package emu.skyline.utility
import android.app.Activity
import android.content.Context
import android.content.Intent
import android.content.res.TypedArray
import android.net.Uri
import android.os.Parcel
import android.os.Parcelable
import android.text.TextUtils
import android.util.AttributeSet
import androidx.preference.Preference
class FolderPreference : Preference {
private var mDirectory: String? = null
constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {
summaryProvider = SimpleSummaryProvider.instance
}
constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs) {
summaryProvider = SimpleSummaryProvider.instance
}
constructor(context: Context?) : super(context) {
summaryProvider = SimpleSummaryProvider.instance
}
override fun onClick() {
val intent = Intent(context, FolderActivity::class.java)
(context as Activity).startActivityForResult(intent, 0)
}
var directory: String?
get() = mDirectory
set(directory) {
val changed = !TextUtils.equals(mDirectory, directory)
if (changed) {
mDirectory = directory
persistString(directory)
if (changed) {
notifyChanged()
}
}
}
override fun onGetDefaultValue(a: TypedArray, index: Int): Any {
return a.getString(index)!!
}
override fun onSetInitialValue(defaultValue: Any?) {
directory = getPersistedString(defaultValue as String?)
}
override fun onSaveInstanceState(): Parcelable {
val superState = super.onSaveInstanceState()
if (isPersistent) {
return superState
}
val myState = SavedState(superState)
myState.mDirectory = directory
return myState
}
override fun onRestoreInstanceState(state: Parcelable?) {
if (state == null || state.javaClass != SavedState::class.java) {
super.onRestoreInstanceState(state)
return
}
val myState = state as SavedState
super.onRestoreInstanceState(myState.superState)
directory = myState.mDirectory
}
internal class SavedState : BaseSavedState {
var mDirectory: String? = null
constructor(source: Parcel) : super(source) {
mDirectory = source.readString()
}
constructor(superState: Parcelable?) : super(superState)
override fun writeToParcel(dest: Parcel, flags: Int) {
super.writeToParcel(dest, flags)
dest.writeString(mDirectory)
}
override fun describeContents(): Int {
return 0
}
}
class SimpleSummaryProvider private constructor() : SummaryProvider<FolderPreference> {
override fun provideSummary(preference: FolderPreference): CharSequence {
return Uri.decode(preference.directory!!)
}
companion object {
private var sSimpleSummaryProvider: SimpleSummaryProvider? = null
val instance: SimpleSummaryProvider?
get() {
if (sSimpleSummaryProvider == null) {
sSimpleSummaryProvider = SimpleSummaryProvider()
}
return sSimpleSummaryProvider
}
}
}
}

View File

@ -1,34 +0,0 @@
/*
* SPDX-License-Identifier: LGPL-3.0-or-later
* Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
*/
package emu.skyline.utility
import android.os.Bundle
import android.text.method.ScrollingMovementMethod
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.DialogFragment
import emu.skyline.R
import kotlinx.android.synthetic.main.license_dialog.*
class LicenseDialog : DialogFragment() {
private var libraryUrl: String = ""
private var libraryLicense: String = ""
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
libraryUrl = arguments?.getString("libraryUrl")!!
libraryLicense = context?.getString(arguments?.getInt("libraryLicense")!!)!!
return requireActivity().layoutInflater.inflate(R.layout.license_dialog, container)
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
license_url.text = libraryUrl
license_content.text = libraryLicense
}
}

View File

@ -1,35 +1,32 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="10dp">
android:layout_height="wrap_content">
<TextView
android:id="@+id/license_url"
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:autoLink="web"
android:textAlignment="center"
android:textAppearance="@style/TextAppearance.AppCompat.Body2"
android:textSize="15sp" />
android:orientation="vertical"
android:paddingTop="10dp">
<ScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/license_url"
android:layout_alignStart="@id/license_url"
android:layout_marginTop="10dp"
android:background="@color/cardview_dark_background">
<TextView
android:id="@+id/license_url"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:autoLink="web"
android:textAlignment="center"
android:textAppearance="@style/TextAppearance.AppCompat.Body2"
android:textSize="15sp" />
<TextView
android:id="@+id/license_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fontFamily="monospace"
android:padding="10dp"
android:textAppearance="@style/TextAppearance.AppCompat.Body1"
android:textColor="@android:color/tertiary_text_light" />
android:textAlignment="center"
android:textAppearance="@style/TextAppearance.MaterialComponents.Subtitle1" />
</ScrollView>
</RelativeLayout>
</LinearLayout>
</ScrollView>

View File

@ -39,6 +39,7 @@
<string name="audren_buffer_desc">The size of the buffer used to store audio samples for ROMs using audren. A lower value will result in less latency but potentially increased audio stutter depending on the performance of the device</string>
<!-- Licenses -->
<string name="licenses">Licenses</string>
<string name="skyline_license_description">The license of Skyline (LGPLv3 or later)</string>
<string name="fmtlib">{fmt}</string>
<string name="fmtlib_description">We use libfmt for formatting strings (Custom License)</string>
<string name="oboe">oboe</string>
@ -55,5 +56,6 @@
<string name="amat_description">We use Android Material Components to have a consistent material design UI (Apache License 2.0)</string>
<string name="ktstd">Kotlin Standard Library</string>
<string name="ktstd_description">We use Kotlin Standard Library for accessing convenience functions in Kotlin (Apache License 2.0)</string>
<string name="skyline_description">The license of Skyline (LGPLv3 or later)</string>
<string name="mtico">Material Design Icons</string>
<string name="mtico_description">We use Material Design Icons to have consistent iconography throughout the application</string>
</resources>

View File

@ -19,11 +19,11 @@
<PreferenceCategory
android:key="category_emulator"
android:title="@string/emulator">
<emu.skyline.utility.FolderPreference
<emu.skyline.preference.FolderPreference
app:key="search_location"
app:title="@string/search_location"
app:useSimpleSummaryProvider="true" />
<emu.skyline.utility.ThemePreference
<emu.skyline.preference.ThemePreference
android:defaultValue="2"
android:entries="@array/app_theme"
android:entryValues="@array/app_theme_val"
@ -77,50 +77,55 @@
android:key="category_licenses"
android:title="@string/licenses"
app:initialExpandedChildrenCount="3">
<emu.skyline.utility.LicensePreference
<emu.skyline.preference.LicensePreference
libraryLicense="@string/lgpl3_license"
libraryUrl="https://github.com/skyline-emu/skyline"
app:summary="@string/skyline_description"
app:summary="@string/skyline_license_description"
app:title="@string/app_name" />
<emu.skyline.utility.LicensePreference
<emu.skyline.preference.LicensePreference
libraryLicense="@string/fmtlib_license"
libraryUrl="https://github.com/fmtlib/fmt"
app:summary="@string/fmtlib_description"
app:title="@string/fmtlib" />
<emu.skyline.utility.LicensePreference
<emu.skyline.preference.LicensePreference
libraryLicense="@string/apache2_license"
libraryUrl="https://github.com/google/oboe"
app:summary="@string/oboe_description"
app:title="@string/oboe" />
<emu.skyline.utility.LicensePreference
<emu.skyline.preference.LicensePreference
libraryLicense="@string/apache2_license"
libraryUrl="https://github.com/KhronosGroup/Vulkan-Hpp"
app:summary="@string/vkhpp_description"
app:title="@string/vkhpp" />
<emu.skyline.utility.LicensePreference
<emu.skyline.preference.LicensePreference
libraryLicense="@string/zlib_license"
libraryUrl="https://github.com/leethomason/tinyxml2"
app:summary="@string/txml2_description"
app:title="@string/txml2" />
<emu.skyline.utility.LicensePreference
<emu.skyline.preference.LicensePreference
libraryLicense="@string/apache2_license"
libraryUrl="https://github.com/tdebatty/java-string-similarity"
app:summary="@string/jssim_description"
app:title="@string/jssim" />
<emu.skyline.utility.LicensePreference
<emu.skyline.preference.LicensePreference
libraryLicense="@string/apache2_license"
libraryUrl="https://developer.android.com/jetpack/androidx"
app:summary="@string/andx_description"
app:title="@string/andx" />
<emu.skyline.utility.LicensePreference
<emu.skyline.preference.LicensePreference
libraryLicense="@string/apache2_license"
libraryUrl="https://github.com/material-components/material-components-android"
app:summary="@string/amat_description"
app:title="@string/amat" />
<emu.skyline.utility.LicensePreference
<emu.skyline.preference.LicensePreference
libraryLicense="@string/apache2_license"
libraryUrl="https://kotlinlang.org/api/latest/jvm/stdlib"
app:summary="@string/ktstd_description"
app:title="@string/ktstd" />
<emu.skyline.preference.LicensePreference
libraryLicense="@string/apache2_license"
libraryUrl="https://material.io/resources/icons"
app:summary="@string/mtico_description"
app:title="@string/mtico" />
</PreferenceCategory>
</androidx.preference.PreferenceScreen>