mirror of
https://github.com/skyline-emu/skyline.git
synced 2024-11-26 12:04:15 +01:00
Rework search
* Call search once for all formats
This commit is contained in:
parent
571e189ecd
commit
479209886b
@ -97,7 +97,6 @@ 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.lifecycle:lifecycle-common-java8:$lifecycle_version"
|
||||
implementation 'androidx.fragment:fragment-ktx:1.2.5'
|
||||
implementation "com.google.dagger:hilt-android:$hilt_version"
|
||||
kapt "com.google.dagger:hilt-android-compiler:$hilt_version"
|
||||
|
@ -39,7 +39,7 @@ class EmulationActivity : AppCompatActivity(), SurfaceHolder.Callback, View.OnTo
|
||||
private val binding by lazy { EmuActivityBinding.inflate(layoutInflater) }
|
||||
|
||||
/**
|
||||
* A map of [Vibrator]s that correspond to [inputManager.controllers]
|
||||
* A map of [Vibrator]s that correspond to [InputManager.controllers]
|
||||
*/
|
||||
private var vibrators = HashMap<Int, Vibrator>()
|
||||
|
||||
@ -54,7 +54,8 @@ class EmulationActivity : AppCompatActivity(), SurfaceHolder.Callback, View.OnTo
|
||||
*/
|
||||
private lateinit var emulationThread : Thread
|
||||
|
||||
private val settings by lazy { Settings(this) }
|
||||
@Inject
|
||||
lateinit var settings : Settings
|
||||
|
||||
@Inject
|
||||
lateinit var inputManager : InputManager
|
||||
|
@ -17,6 +17,7 @@ import androidx.appcompat.widget.SearchView
|
||||
import androidx.recyclerview.widget.DividerItemDecoration
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import emu.skyline.adapter.GenericAdapter
|
||||
import emu.skyline.adapter.HeaderViewItem
|
||||
import emu.skyline.adapter.LogViewItem
|
||||
@ -27,8 +28,10 @@ import java.io.File
|
||||
import java.io.FileNotFoundException
|
||||
import java.io.IOException
|
||||
import java.net.URL
|
||||
import javax.inject.Inject
|
||||
import javax.net.ssl.HttpsURLConnection
|
||||
|
||||
@AndroidEntryPoint
|
||||
class LogActivity : AppCompatActivity() {
|
||||
private val binding by lazy { LogActivityBinding.inflate(layoutInflater) }
|
||||
|
||||
@ -39,6 +42,9 @@ class LogActivity : AppCompatActivity() {
|
||||
|
||||
private val adapter = GenericAdapter()
|
||||
|
||||
@Inject
|
||||
lateinit var settings : Settings
|
||||
|
||||
override fun onCreate(savedInstanceState : Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
@ -47,8 +53,6 @@ class LogActivity : AppCompatActivity() {
|
||||
setSupportActionBar(binding.titlebar.toolbar)
|
||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||
|
||||
val settings = Settings(this)
|
||||
|
||||
val compact = settings.logCompact
|
||||
val logLevel = settings.logLevel.toInt()
|
||||
val logLevels = resources.getStringArray(R.array.log_level)
|
||||
|
@ -24,6 +24,7 @@ import androidx.recyclerview.widget.DividerItemDecoration
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import emu.skyline.adapter.AppViewItem
|
||||
import emu.skyline.adapter.GenericAdapter
|
||||
import emu.skyline.adapter.HeaderViewItem
|
||||
@ -33,13 +34,17 @@ import emu.skyline.data.DataItem
|
||||
import emu.skyline.data.HeaderItem
|
||||
import emu.skyline.databinding.MainActivityBinding
|
||||
import emu.skyline.loader.LoaderResult
|
||||
import emu.skyline.loader.RomFormat
|
||||
import emu.skyline.utils.Settings
|
||||
import javax.inject.Inject
|
||||
import kotlin.math.ceil
|
||||
|
||||
@AndroidEntryPoint
|
||||
class MainActivity : AppCompatActivity() {
|
||||
private val binding by lazy { MainActivityBinding.inflate(layoutInflater) }
|
||||
|
||||
private val settings by lazy { Settings(this) }
|
||||
@Inject
|
||||
lateinit var settings : Settings
|
||||
|
||||
private val adapter = GenericAdapter()
|
||||
|
||||
@ -52,16 +57,15 @@ class MainActivity : AppCompatActivity() {
|
||||
private fun AppItem.toViewItem() = AppViewItem(layoutType, this, missingIcon, ::selectStartGame, ::selectShowGameDialog)
|
||||
|
||||
override fun onCreate(savedInstanceState : Bundle?) {
|
||||
AppCompatDelegate.setDefaultNightMode(
|
||||
when ((settings.appTheme.toInt())) {
|
||||
0 -> AppCompatDelegate.MODE_NIGHT_NO
|
||||
1 -> AppCompatDelegate.MODE_NIGHT_YES
|
||||
2 -> AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM
|
||||
else -> AppCompatDelegate.MODE_NIGHT_UNSPECIFIED
|
||||
}
|
||||
)
|
||||
|
||||
// Need to create new instance of settings, dependency injection happens
|
||||
AppCompatDelegate.setDefaultNightMode(when ((Settings(this).appTheme.toInt())) {
|
||||
0 -> AppCompatDelegate.MODE_NIGHT_NO
|
||||
1 -> AppCompatDelegate.MODE_NIGHT_YES
|
||||
2 -> AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM
|
||||
else -> AppCompatDelegate.MODE_NIGHT_UNSPECIFIED
|
||||
})
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
setContentView(binding.root)
|
||||
|
||||
PreferenceManager.setDefaultValues(this, R.xml.preferences, false)
|
||||
@ -195,7 +199,16 @@ class MainActivity : AppCompatActivity() {
|
||||
}
|
||||
is MainState.Loaded -> {
|
||||
binding.swipeRefreshLayout.isRefreshing = false
|
||||
populateAdapter(state.items)
|
||||
|
||||
val formatOrder = arrayOf(RomFormat.NSP, RomFormat.NRO, RomFormat.NSO, RomFormat.NCA)
|
||||
val items = mutableListOf<DataItem>()
|
||||
for (format in formatOrder) {
|
||||
state.items[format]?.let {
|
||||
items.add(HeaderItem(format.name))
|
||||
it.forEach { entry -> items.add(AppItem(entry)) }
|
||||
}
|
||||
}
|
||||
populateAdapter(items)
|
||||
}
|
||||
is MainState.Error -> Snackbar.make(findViewById(android.R.id.content), getString(R.string.error) + ": ${state.ex.localizedMessage}", Snackbar.LENGTH_SHORT).show()
|
||||
}
|
||||
|
@ -3,30 +3,29 @@ package emu.skyline
|
||||
import android.content.Context
|
||||
import android.net.Uri
|
||||
import android.util.Log
|
||||
import androidx.documentfile.provider.DocumentFile
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import emu.skyline.data.AppItem
|
||||
import emu.skyline.data.DataItem
|
||||
import emu.skyline.data.HeaderItem
|
||||
import emu.skyline.loader.RomFile
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import emu.skyline.loader.AppEntry
|
||||
import emu.skyline.loader.RomFormat
|
||||
import emu.skyline.utils.loadSerializedList
|
||||
import emu.skyline.utils.serialize
|
||||
import emu.skyline.utils.fromFile
|
||||
import emu.skyline.utils.toFile
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
import javax.inject.Inject
|
||||
|
||||
sealed class MainState {
|
||||
object Loading : MainState()
|
||||
class Loaded(val items : List<DataItem>) : MainState()
|
||||
class Loaded(val items : HashMap<RomFormat, ArrayList<AppEntry>>) : MainState()
|
||||
class Error(val ex : Exception) : MainState()
|
||||
}
|
||||
|
||||
class MainViewModel : ViewModel() {
|
||||
@HiltViewModel
|
||||
class MainViewModel @Inject constructor(private val romProvider : RomProvider) : ViewModel() {
|
||||
companion object {
|
||||
private val TAG = MainViewModel::class.java.simpleName
|
||||
}
|
||||
@ -39,30 +38,6 @@ class MainViewModel : ViewModel() {
|
||||
|
||||
var searchBarAnimated = false
|
||||
|
||||
/**
|
||||
* This adds all files in [directory] with [extension] as an entry using [RomFile] to load metadata
|
||||
*/
|
||||
private fun addEntries(context : Context, extension : String, romFormat : RomFormat, directory : DocumentFile, romElements : ArrayList<DataItem>, found : Boolean = false) : Boolean {
|
||||
var foundCurrent = found
|
||||
|
||||
directory.listFiles().forEach { file ->
|
||||
if (file.isDirectory) {
|
||||
foundCurrent = addEntries(context, extension, romFormat, file, romElements, foundCurrent)
|
||||
} else {
|
||||
if (extension.equals(file.name?.substringAfterLast("."), ignoreCase = true)) {
|
||||
RomFile(context, romFormat, file.uri).let { romFile ->
|
||||
if (!foundCurrent) romElements.add(HeaderItem(romFormat.name))
|
||||
romElements.add(AppItem(romFile.appEntry))
|
||||
|
||||
foundCurrent = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return foundCurrent
|
||||
}
|
||||
|
||||
/**
|
||||
* This refreshes the contents of the adapter by either trying to load cached adapter data or searches for them to recreate a list
|
||||
*
|
||||
@ -77,33 +52,21 @@ class MainViewModel : ViewModel() {
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
if (loadFromFile) {
|
||||
try {
|
||||
state = MainState.Loaded(loadSerializedList(romsFile))
|
||||
state = MainState.Loaded(fromFile(romsFile))
|
||||
return@launch
|
||||
} catch (e : Exception) {
|
||||
Log.w(TAG, "Ran into exception while loading: ${e.message}")
|
||||
}
|
||||
}
|
||||
|
||||
val romElements = romProvider.loadRoms(searchLocation)
|
||||
try {
|
||||
val searchDocument = DocumentFile.fromTreeUri(context, searchLocation)!!
|
||||
|
||||
val romElements = ArrayList<DataItem>()
|
||||
addEntries(context, "nsp", RomFormat.NSP, searchDocument, romElements)
|
||||
addEntries(context, "xci", RomFormat.XCI, searchDocument, romElements)
|
||||
addEntries(context, "nro", RomFormat.NRO, searchDocument, romElements)
|
||||
addEntries(context, "nso", RomFormat.NSO, searchDocument, romElements)
|
||||
addEntries(context, "nca", RomFormat.NCA, searchDocument, romElements)
|
||||
|
||||
try {
|
||||
romElements.serialize(romsFile)
|
||||
} catch (e : IOException) {
|
||||
Log.w(TAG, "Ran into exception while saving: ${e.message}")
|
||||
}
|
||||
|
||||
state = MainState.Loaded(romElements)
|
||||
} catch (e : Exception) {
|
||||
state = MainState.Error(e)
|
||||
romElements.toFile(romsFile)
|
||||
} catch (e : IOException) {
|
||||
Log.w(TAG, "Ran into exception while saving: ${e.message}")
|
||||
}
|
||||
|
||||
state = MainState.Loaded(romElements)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
36
app/src/main/java/emu/skyline/RomProvider.kt
Normal file
36
app/src/main/java/emu/skyline/RomProvider.kt
Normal file
@ -0,0 +1,36 @@
|
||||
package emu.skyline
|
||||
|
||||
import android.content.Context
|
||||
import android.net.Uri
|
||||
import androidx.documentfile.provider.DocumentFile
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import emu.skyline.loader.AppEntry
|
||||
import emu.skyline.loader.RomFile
|
||||
import emu.skyline.loader.RomFormat
|
||||
import emu.skyline.loader.RomFormat.*
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
class RomProvider @Inject constructor(@ApplicationContext private val context : Context) {
|
||||
/**
|
||||
* This adds all files in [directory] with [extension] as an entry using [RomFile] to load metadata
|
||||
*/
|
||||
private fun addEntries(fileFormats : Map<String, RomFormat>, directory : DocumentFile, entries : HashMap<RomFormat, ArrayList<AppEntry>>) {
|
||||
directory.listFiles().forEach { file ->
|
||||
if (file.isDirectory) {
|
||||
addEntries(fileFormats, file, entries)
|
||||
} else {
|
||||
fileFormats[file.name?.substringAfterLast(".")]?.let { romFormat ->
|
||||
entries.getOrPut(romFormat, { arrayListOf() }).add(RomFile(context, romFormat, file.uri).appEntry)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun loadRoms(searchLocation : Uri) = DocumentFile.fromTreeUri(context, searchLocation)!!.let { documentFile ->
|
||||
val entries = hashMapOf<RomFormat, ArrayList<AppEntry>>()
|
||||
addEntries(mapOf("nro" to NRO, "nso" to NSO, "nca" to NCA, "nsp" to NSP, "xci" to XCI), documentFile, entries)
|
||||
entries
|
||||
}
|
||||
}
|
@ -53,7 +53,8 @@ class ControllerActivity : AppCompatActivity() {
|
||||
*/
|
||||
val axisMap = mutableMapOf<AxisId, ControllerStickViewItem>()
|
||||
|
||||
private val settings by lazy { Settings(this) }
|
||||
@Inject
|
||||
lateinit var settings : Settings
|
||||
|
||||
@Inject
|
||||
lateinit var inputManager : InputManager
|
||||
|
@ -15,16 +15,22 @@ import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.content.ContextCompat
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import emu.skyline.R
|
||||
import emu.skyline.databinding.OnScreenEditActivityBinding
|
||||
import emu.skyline.utils.Settings
|
||||
import javax.inject.Inject
|
||||
|
||||
@AndroidEntryPoint
|
||||
class OnScreenEditActivity : AppCompatActivity() {
|
||||
private val binding by lazy { OnScreenEditActivityBinding.inflate(layoutInflater) }
|
||||
|
||||
private var fullEditVisible = true
|
||||
private var editMode = false
|
||||
|
||||
@Inject
|
||||
lateinit var settings : Settings
|
||||
|
||||
private val closeAction : () -> Unit = {
|
||||
if (editMode) {
|
||||
toggleFabVisibility(true)
|
||||
@ -85,7 +91,7 @@ class OnScreenEditActivity : AppCompatActivity() {
|
||||
override fun onCreate(savedInstanceState : Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(binding.root)
|
||||
binding.onScreenControllerView.recenterSticks = Settings(this).onScreenControlRecenterSticks
|
||||
binding.onScreenControllerView.recenterSticks = settings.onScreenControlRecenterSticks
|
||||
|
||||
actions.forEach { pair ->
|
||||
binding.fabParent.addView(LayoutInflater.from(this).inflate(R.layout.on_screen_edit_mini_fab, binding.fabParent, false).apply {
|
||||
|
@ -10,11 +10,14 @@ 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"
|
||||
@ -24,6 +27,9 @@ abstract class DocumentActivity : AppCompatActivity() {
|
||||
|
||||
protected abstract val actionIntent : Intent
|
||||
|
||||
@Inject
|
||||
lateinit var settings : Settings
|
||||
|
||||
/**
|
||||
* This launches the [Intent.ACTION_OPEN_DOCUMENT_TREE] intent on creation
|
||||
*/
|
||||
@ -46,7 +52,7 @@ abstract class DocumentActivity : AppCompatActivity() {
|
||||
|
||||
contentResolver.takePersistableUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||
|
||||
Settings(this).refreshRequired = true
|
||||
settings.refreshRequired = true
|
||||
PreferenceManager.getDefaultSharedPreferences(this).edit()
|
||||
.putString(keyName, uri.toString())
|
||||
.apply()
|
||||
|
@ -10,13 +10,9 @@ import java.io.ObjectInputStream
|
||||
import java.io.ObjectOutputStream
|
||||
import java.io.Serializable
|
||||
|
||||
fun <T : Serializable> ArrayList<T>.serialize(file : File) {
|
||||
ObjectOutputStream(file.outputStream()).use {
|
||||
it.writeObject(this)
|
||||
}
|
||||
fun <T : Serializable> T.toFile(file : File) {
|
||||
ObjectOutputStream(file.outputStream()).use { it.writeObject(this) }
|
||||
}
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
fun <T : Serializable> loadSerializedList(file : File) = ObjectInputStream(file.inputStream()).use {
|
||||
it.readObject()
|
||||
} as ArrayList<T>
|
||||
fun <T : Serializable> fromFile(file : File) = ObjectInputStream(file.inputStream()).use { it.readObject() } as T
|
||||
|
@ -6,8 +6,12 @@
|
||||
package emu.skyline.utils
|
||||
|
||||
import android.content.Context
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
class Settings(context : Context) {
|
||||
@Singleton
|
||||
class Settings @Inject constructor(@ApplicationContext private val context : Context) {
|
||||
var layoutType by sharedPreferences(context, "1")
|
||||
|
||||
var searchLocation by sharedPreferences(context, "")
|
||||
|
Loading…
Reference in New Issue
Block a user