android: Migrate settings to navigation component
Consolidates all of the settings components to the fragment and activity with no interfaces and only the settings fragment presenter. This also includes new material animations and new viewmodel usage to prevent the fragment and activity directly interacting with one another.master
parent
f5e6b12c74
commit
95a939a49f
@ -1,81 +0,0 @@
|
|||||||
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
||||||
|
|
||||||
package org.yuzu.yuzu_emu.features.settings.ui
|
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import android.os.Bundle
|
|
||||||
import java.io.File
|
|
||||||
import org.yuzu.yuzu_emu.NativeLibrary
|
|
||||||
import org.yuzu.yuzu_emu.features.settings.model.Settings
|
|
||||||
import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile
|
|
||||||
import org.yuzu.yuzu_emu.utils.DirectoryInitialization
|
|
||||||
import org.yuzu.yuzu_emu.utils.Log
|
|
||||||
|
|
||||||
class SettingsActivityPresenter(private val activityView: SettingsActivityView) {
|
|
||||||
private var shouldSave = false
|
|
||||||
private lateinit var menuTag: String
|
|
||||||
private lateinit var gameId: String
|
|
||||||
|
|
||||||
fun onCreate(savedInstanceState: Bundle?, menuTag: String, gameId: String) {
|
|
||||||
this.menuTag = menuTag
|
|
||||||
this.gameId = gameId
|
|
||||||
if (savedInstanceState != null) {
|
|
||||||
shouldSave = savedInstanceState.getBoolean(KEY_SHOULD_SAVE)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun onStart() {
|
|
||||||
prepareDirectoriesIfNeeded()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun loadSettingsUI() {
|
|
||||||
// TODO: Load custom settings contextually
|
|
||||||
activityView.showSettingsFragment(menuTag, false, gameId)
|
|
||||||
activityView.onSettingsFileLoaded()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun prepareDirectoriesIfNeeded() {
|
|
||||||
val configFile =
|
|
||||||
File(
|
|
||||||
"${DirectoryInitialization.userDirectory}/config/" +
|
|
||||||
"${SettingsFile.FILE_NAME_CONFIG}.ini"
|
|
||||||
)
|
|
||||||
if (!configFile.exists()) {
|
|
||||||
Log.error(
|
|
||||||
"${DirectoryInitialization.userDirectory}/config/" +
|
|
||||||
"${SettingsFile.FILE_NAME_CONFIG}.ini"
|
|
||||||
)
|
|
||||||
Log.error("yuzu config file could not be found!")
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!DirectoryInitialization.areDirectoriesReady) {
|
|
||||||
DirectoryInitialization.start(activityView as Context)
|
|
||||||
}
|
|
||||||
loadSettingsUI()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun onStop(finishing: Boolean) {
|
|
||||||
if (finishing && shouldSave) {
|
|
||||||
Log.debug("[SettingsActivity] Settings activity stopping. Saving settings to INI...")
|
|
||||||
Settings.saveSettings()
|
|
||||||
NativeLibrary.reloadSettings()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun onSettingChanged() {
|
|
||||||
shouldSave = true
|
|
||||||
}
|
|
||||||
|
|
||||||
fun onSettingsReset() {
|
|
||||||
shouldSave = false
|
|
||||||
}
|
|
||||||
|
|
||||||
fun saveState(outState: Bundle) {
|
|
||||||
outState.putBoolean(KEY_SHOULD_SAVE, shouldSave)
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
private const val KEY_SHOULD_SAVE = "should_save"
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,38 +0,0 @@
|
|||||||
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
||||||
|
|
||||||
package org.yuzu.yuzu_emu.features.settings.ui
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Abstraction for the Activity that manages SettingsFragments.
|
|
||||||
*/
|
|
||||||
interface SettingsActivityView {
|
|
||||||
/**
|
|
||||||
* Show a new SettingsFragment.
|
|
||||||
*
|
|
||||||
* @param menuTag Identifier for the settings group that should be displayed.
|
|
||||||
* @param addToStack Whether or not this fragment should replace a previous one.
|
|
||||||
*/
|
|
||||||
fun showSettingsFragment(menuTag: String, addToStack: Boolean, gameId: String)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when a load operation completes.
|
|
||||||
*/
|
|
||||||
fun onSettingsFileLoaded()
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when a load operation fails.
|
|
||||||
*/
|
|
||||||
fun onSettingsFileNotFound()
|
|
||||||
|
|
||||||
/**
|
|
||||||
* End the activity.
|
|
||||||
*/
|
|
||||||
fun finish()
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called by a containing Fragment to tell the Activity that a setting was changed;
|
|
||||||
* unless this has been called, the Activity will not save to disk.
|
|
||||||
*/
|
|
||||||
fun onSettingChanged()
|
|
||||||
}
|
|
@ -1,42 +0,0 @@
|
|||||||
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
||||||
|
|
||||||
package org.yuzu.yuzu_emu.features.settings.ui
|
|
||||||
|
|
||||||
import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Abstraction for a screen showing a list of settings. Instances of
|
|
||||||
* this type of view will each display a layer of the setting hierarchy.
|
|
||||||
*/
|
|
||||||
interface SettingsFragmentView {
|
|
||||||
/**
|
|
||||||
* Pass an ArrayList to the View so that it can be displayed on screen.
|
|
||||||
*
|
|
||||||
* @param settingsList The result of converting the HashMap to an ArrayList
|
|
||||||
*/
|
|
||||||
fun showSettingsList(settingsList: ArrayList<SettingsItem>)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Instructs the Fragment to load the settings screen.
|
|
||||||
*/
|
|
||||||
fun loadSettingsList()
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return The Fragment's containing activity.
|
|
||||||
*/
|
|
||||||
val activityView: SettingsActivityView?
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tell the Fragment to tell the containing Activity to show a new
|
|
||||||
* Fragment containing a submenu of settings.
|
|
||||||
*
|
|
||||||
* @param menuKey Identifier for the settings group that should be shown.
|
|
||||||
*/
|
|
||||||
fun loadSubMenu(menuKey: String)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Have the fragment tell the containing Activity that a setting was modified.
|
|
||||||
*/
|
|
||||||
fun onSettingChanged()
|
|
||||||
}
|
|
@ -0,0 +1,47 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
package org.yuzu.yuzu_emu.model
|
||||||
|
|
||||||
|
import androidx.lifecycle.LiveData
|
||||||
|
import androidx.lifecycle.MutableLiveData
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
|
||||||
|
class SettingsViewModel : ViewModel() {
|
||||||
|
var game: Game? = null
|
||||||
|
|
||||||
|
var shouldSave = false
|
||||||
|
|
||||||
|
private val _toolbarTitle = MutableLiveData("")
|
||||||
|
val toolbarTitle: LiveData<String> get() = _toolbarTitle
|
||||||
|
|
||||||
|
private val _shouldRecreate = MutableLiveData(false)
|
||||||
|
val shouldRecreate: LiveData<Boolean> get() = _shouldRecreate
|
||||||
|
|
||||||
|
private val _shouldNavigateBack = MutableLiveData(false)
|
||||||
|
val shouldNavigateBack: LiveData<Boolean> get() = _shouldNavigateBack
|
||||||
|
|
||||||
|
private val _shouldShowResetSettingsDialog = MutableLiveData(false)
|
||||||
|
val shouldShowResetSettingsDialog: LiveData<Boolean> get() = _shouldShowResetSettingsDialog
|
||||||
|
|
||||||
|
fun setToolbarTitle(value: String) {
|
||||||
|
_toolbarTitle.value = value
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setShouldRecreate(value: Boolean) {
|
||||||
|
_shouldRecreate.value = value
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setShouldNavigateBack(value: Boolean) {
|
||||||
|
_shouldNavigateBack.value = value
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setShouldShowResetSettingsDialog(value: Boolean) {
|
||||||
|
_shouldShowResetSettingsDialog.value = value
|
||||||
|
}
|
||||||
|
|
||||||
|
fun clear() {
|
||||||
|
game = null
|
||||||
|
shouldSave = false
|
||||||
|
}
|
||||||
|
}
|
@ -1,16 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<set xmlns:android="http://schemas.android.com/apk/res/android">
|
|
||||||
|
|
||||||
<alpha
|
|
||||||
android:duration="125"
|
|
||||||
android:interpolator="@android:anim/decelerate_interpolator"
|
|
||||||
android:fromAlpha="1"
|
|
||||||
android:toAlpha="0" />
|
|
||||||
|
|
||||||
<translate
|
|
||||||
android:duration="125"
|
|
||||||
android:interpolator="@android:anim/decelerate_interpolator"
|
|
||||||
android:fromXDelta="0"
|
|
||||||
android:toXDelta="-75" />
|
|
||||||
|
|
||||||
</set>
|
|
@ -1,16 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<set xmlns:android="http://schemas.android.com/apk/res/android">
|
|
||||||
|
|
||||||
<alpha
|
|
||||||
android:duration="@android:integer/config_shortAnimTime"
|
|
||||||
android:interpolator="@android:anim/decelerate_interpolator"
|
|
||||||
android:fromAlpha="0"
|
|
||||||
android:toAlpha="1" />
|
|
||||||
|
|
||||||
<translate
|
|
||||||
android:duration="@android:integer/config_shortAnimTime"
|
|
||||||
android:interpolator="@android:anim/decelerate_interpolator"
|
|
||||||
android:fromXDelta="-200"
|
|
||||||
android:toXDelta="0" />
|
|
||||||
|
|
||||||
</set>
|
|
@ -1,16 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<set xmlns:android="http://schemas.android.com/apk/res/android">
|
|
||||||
|
|
||||||
<alpha
|
|
||||||
android:duration="125"
|
|
||||||
android:interpolator="@android:anim/decelerate_interpolator"
|
|
||||||
android:fromAlpha="1"
|
|
||||||
android:toAlpha="0" />
|
|
||||||
|
|
||||||
<translate
|
|
||||||
android:duration="125"
|
|
||||||
android:interpolator="@android:anim/decelerate_interpolator"
|
|
||||||
android:fromXDelta="0"
|
|
||||||
android:toXDelta="75" />
|
|
||||||
|
|
||||||
</set>
|
|
@ -1,16 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<set xmlns:android="http://schemas.android.com/apk/res/android">
|
|
||||||
|
|
||||||
<alpha
|
|
||||||
android:duration="@android:integer/config_shortAnimTime"
|
|
||||||
android:interpolator="@android:anim/decelerate_interpolator"
|
|
||||||
android:fromAlpha="0"
|
|
||||||
android:toAlpha="1" />
|
|
||||||
|
|
||||||
<translate
|
|
||||||
android:duration="@android:integer/config_shortAnimTime"
|
|
||||||
android:interpolator="@android:anim/decelerate_interpolator"
|
|
||||||
android:fromXDelta="200"
|
|
||||||
android:toXDelta="0" />
|
|
||||||
|
|
||||||
</set>
|
|
@ -1,10 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<set xmlns:android="http://schemas.android.com/apk/res/android">
|
|
||||||
|
|
||||||
<alpha
|
|
||||||
android:duration="@android:integer/config_shortAnimTime"
|
|
||||||
android:interpolator="@android:anim/decelerate_interpolator"
|
|
||||||
android:fromAlpha="1"
|
|
||||||
android:toAlpha="0" />
|
|
||||||
|
|
||||||
</set>
|
|
@ -1,20 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<set xmlns:android="http://schemas.android.com/apk/res/android">
|
|
||||||
|
|
||||||
<objectAnimator
|
|
||||||
android:propertyName="translationX"
|
|
||||||
android:valueType="floatType"
|
|
||||||
android:valueFrom="-1280dp"
|
|
||||||
android:valueTo="0"
|
|
||||||
android:interpolator="@android:interpolator/decelerate_quad"
|
|
||||||
android:duration="300"/>
|
|
||||||
|
|
||||||
<objectAnimator
|
|
||||||
android:propertyName="alpha"
|
|
||||||
android:valueType="floatType"
|
|
||||||
android:valueFrom="0"
|
|
||||||
android:valueTo="1"
|
|
||||||
android:interpolator="@android:interpolator/accelerate_quad"
|
|
||||||
android:duration="300"/>
|
|
||||||
|
|
||||||
</set>
|
|
@ -1,21 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<set xmlns:android="http://schemas.android.com/apk/res/android">
|
|
||||||
|
|
||||||
<!-- This animation is used ONLY when a submenu is replaced. -->
|
|
||||||
<objectAnimator
|
|
||||||
android:propertyName="translationX"
|
|
||||||
android:valueType="floatType"
|
|
||||||
android:valueFrom="0"
|
|
||||||
android:valueTo="-1280dp"
|
|
||||||
android:interpolator="@android:interpolator/decelerate_quad"
|
|
||||||
android:duration="200"/>
|
|
||||||
|
|
||||||
<objectAnimator
|
|
||||||
android:propertyName="alpha"
|
|
||||||
android:valueType="floatType"
|
|
||||||
android:valueFrom="1"
|
|
||||||
android:valueTo="0"
|
|
||||||
android:interpolator="@android:interpolator/decelerate_quad"
|
|
||||||
android:duration="200"/>
|
|
||||||
|
|
||||||
</set>
|
|
@ -1,14 +1,41 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<FrameLayout
|
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:id="@+id/coordinator_main"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent">
|
android:layout_height="match_parent"
|
||||||
|
android:background="?attr/colorSurface">
|
||||||
|
|
||||||
|
<com.google.android.material.appbar.AppBarLayout
|
||||||
|
android:id="@+id/appbar_settings"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:fitsSystemWindows="true"
|
||||||
|
app:elevation="0dp">
|
||||||
|
|
||||||
|
<com.google.android.material.appbar.CollapsingToolbarLayout
|
||||||
|
android:id="@+id/toolbar_settings_layout"
|
||||||
|
style="?attr/collapsingToolbarLayoutMediumStyle"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="?attr/collapsingToolbarLayoutMediumSize"
|
||||||
|
app:layout_scrollFlags="scroll|exitUntilCollapsed|snap">
|
||||||
|
|
||||||
|
<com.google.android.material.appbar.MaterialToolbar
|
||||||
|
android:id="@+id/toolbar_settings"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="?attr/actionBarSize"
|
||||||
|
app:layout_collapseMode="pin"
|
||||||
|
app:navigationIcon="@drawable/ic_back" />
|
||||||
|
|
||||||
|
</com.google.android.material.appbar.CollapsingToolbarLayout>
|
||||||
|
|
||||||
|
</com.google.android.material.appbar.AppBarLayout>
|
||||||
|
|
||||||
<androidx.recyclerview.widget.RecyclerView
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
android:id="@+id/list_settings"
|
android:id="@+id/list_settings"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:background="?attr/colorSurface"
|
android:clipToPadding="false"
|
||||||
android:clipToPadding="false" />
|
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
|
||||||
|
|
||||||
</FrameLayout>
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||||
|
@ -1,2 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<menu />
|
|
@ -0,0 +1,24 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:id="@+id/settings_navigation"
|
||||||
|
app:startDestination="@id/settingsFragment">
|
||||||
|
|
||||||
|
<fragment
|
||||||
|
android:id="@+id/settingsFragment"
|
||||||
|
android:name="org.yuzu.yuzu_emu.features.settings.ui.SettingsFragment"
|
||||||
|
android:label="SettingsFragment">
|
||||||
|
<argument
|
||||||
|
android:name="menuTag"
|
||||||
|
app:argType="string" />
|
||||||
|
<argument
|
||||||
|
android:name="game"
|
||||||
|
app:argType="org.yuzu.yuzu_emu.model.Game"
|
||||||
|
app:nullable="true" />
|
||||||
|
</fragment>
|
||||||
|
|
||||||
|
<action
|
||||||
|
android:id="@+id/action_global_settingsFragment"
|
||||||
|
app:destination="@id/settingsFragment" />
|
||||||
|
|
||||||
|
</navigation>
|
Loading…
Reference in New Issue