From 479209886bf4c7aab0cf513b35779045ef4e4826 Mon Sep 17 00:00:00 2001 From: Willi Ye Date: Sun, 7 Feb 2021 20:42:03 +0100 Subject: [PATCH] Rework search * Call search once for all formats --- app/build.gradle | 1 - .../java/emu/skyline/EmulationActivity.kt | 5 +- app/src/main/java/emu/skyline/LogActivity.kt | 8 ++- app/src/main/java/emu/skyline/MainActivity.kt | 35 +++++++--- .../main/java/emu/skyline/MainViewModel.kt | 67 +++++-------------- app/src/main/java/emu/skyline/RomProvider.kt | 36 ++++++++++ .../emu/skyline/input/ControllerActivity.kt | 3 +- .../input/onscreen/OnScreenEditActivity.kt | 8 ++- .../skyline/preference/DocumentActivity.kt | 8 ++- .../emu/skyline/utils/SerializationHelper.kt | 10 +-- .../main/java/emu/skyline/utils/Settings.kt | 6 +- 11 files changed, 108 insertions(+), 79 deletions(-) create mode 100644 app/src/main/java/emu/skyline/RomProvider.kt diff --git a/app/build.gradle b/app/build.gradle index 02b05eba..a38f82f7 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -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" diff --git a/app/src/main/java/emu/skyline/EmulationActivity.kt b/app/src/main/java/emu/skyline/EmulationActivity.kt index 66eefce4..4898b1de 100644 --- a/app/src/main/java/emu/skyline/EmulationActivity.kt +++ b/app/src/main/java/emu/skyline/EmulationActivity.kt @@ -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() @@ -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 diff --git a/app/src/main/java/emu/skyline/LogActivity.kt b/app/src/main/java/emu/skyline/LogActivity.kt index a9770334..ee37305f 100644 --- a/app/src/main/java/emu/skyline/LogActivity.kt +++ b/app/src/main/java/emu/skyline/LogActivity.kt @@ -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) diff --git a/app/src/main/java/emu/skyline/MainActivity.kt b/app/src/main/java/emu/skyline/MainActivity.kt index 38a84f31..d8d91302 100644 --- a/app/src/main/java/emu/skyline/MainActivity.kt +++ b/app/src/main/java/emu/skyline/MainActivity.kt @@ -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() + 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() } diff --git a/app/src/main/java/emu/skyline/MainViewModel.kt b/app/src/main/java/emu/skyline/MainViewModel.kt index d48b448f..54447185 100644 --- a/app/src/main/java/emu/skyline/MainViewModel.kt +++ b/app/src/main/java/emu/skyline/MainViewModel.kt @@ -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) : MainState() + class Loaded(val items : HashMap>) : 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, 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() - 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) } } } diff --git a/app/src/main/java/emu/skyline/RomProvider.kt b/app/src/main/java/emu/skyline/RomProvider.kt new file mode 100644 index 00000000..113092ef --- /dev/null +++ b/app/src/main/java/emu/skyline/RomProvider.kt @@ -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, directory : DocumentFile, entries : HashMap>) { + 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>() + addEntries(mapOf("nro" to NRO, "nso" to NSO, "nca" to NCA, "nsp" to NSP, "xci" to XCI), documentFile, entries) + entries + } +} diff --git a/app/src/main/java/emu/skyline/input/ControllerActivity.kt b/app/src/main/java/emu/skyline/input/ControllerActivity.kt index 02ec58ea..1f74d8dc 100644 --- a/app/src/main/java/emu/skyline/input/ControllerActivity.kt +++ b/app/src/main/java/emu/skyline/input/ControllerActivity.kt @@ -53,7 +53,8 @@ class ControllerActivity : AppCompatActivity() { */ val axisMap = mutableMapOf() - private val settings by lazy { Settings(this) } + @Inject + lateinit var settings : Settings @Inject lateinit var inputManager : InputManager diff --git a/app/src/main/java/emu/skyline/input/onscreen/OnScreenEditActivity.kt b/app/src/main/java/emu/skyline/input/onscreen/OnScreenEditActivity.kt index 508f850f..a3d387a6 100644 --- a/app/src/main/java/emu/skyline/input/onscreen/OnScreenEditActivity.kt +++ b/app/src/main/java/emu/skyline/input/onscreen/OnScreenEditActivity.kt @@ -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 { diff --git a/app/src/main/java/emu/skyline/preference/DocumentActivity.kt b/app/src/main/java/emu/skyline/preference/DocumentActivity.kt index cd8cb21f..538f015a 100644 --- a/app/src/main/java/emu/skyline/preference/DocumentActivity.kt +++ b/app/src/main/java/emu/skyline/preference/DocumentActivity.kt @@ -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() diff --git a/app/src/main/java/emu/skyline/utils/SerializationHelper.kt b/app/src/main/java/emu/skyline/utils/SerializationHelper.kt index 12d01a2b..a8af4d50 100644 --- a/app/src/main/java/emu/skyline/utils/SerializationHelper.kt +++ b/app/src/main/java/emu/skyline/utils/SerializationHelper.kt @@ -10,13 +10,9 @@ import java.io.ObjectInputStream import java.io.ObjectOutputStream import java.io.Serializable -fun ArrayList.serialize(file : File) { - ObjectOutputStream(file.outputStream()).use { - it.writeObject(this) - } +fun T.toFile(file : File) { + ObjectOutputStream(file.outputStream()).use { it.writeObject(this) } } @Suppress("UNCHECKED_CAST") -fun loadSerializedList(file : File) = ObjectInputStream(file.inputStream()).use { - it.readObject() -} as ArrayList +fun fromFile(file : File) = ObjectInputStream(file.inputStream()).use { it.readObject() } as T diff --git a/app/src/main/java/emu/skyline/utils/Settings.kt b/app/src/main/java/emu/skyline/utils/Settings.kt index f829f4ed..7cd0e68d 100644 --- a/app/src/main/java/emu/skyline/utils/Settings.kt +++ b/app/src/main/java/emu/skyline/utils/Settings.kt @@ -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, "")