android: Remove LocalBroadcastManager

This causes a couple of minor changes to directory initialization. We don't have a lengthy initialization step so we could spend less time creating state receivers and just run initialization on the main thread. We also don't have a situation where external storage will be a concern so checks are removed in favor of a binary check to see if initialization is ready.

This additionally removes the unused DoFrame callback.
This commit is contained in:
Charles Lombardo 2023-04-04 04:03:55 -04:00 committed by bunnei
parent 9d7a60346f
commit a827486391
11 changed files with 17 additions and 225 deletions

View File

@ -142,7 +142,6 @@ dependencies {
implementation 'androidx.window:window:1.0.0' implementation 'androidx.window:window:1.0.0'
implementation 'org.ini4j:ini4j:0.5.4' implementation 'org.ini4j:ini4j:0.5.4'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4' implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
implementation 'androidx.localbroadcastmanager:localbroadcastmanager:1.1.0'
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0' implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'
} }

View File

@ -218,8 +218,6 @@ public final class NativeLibrary {
public static native void SurfaceDestroyed(); public static native void SurfaceDestroyed();
public static native void DoFrame();
/** /**
* Unpauses emulation from a paused state. * Unpauses emulation from a paused state.
*/ */

View File

@ -5,31 +5,25 @@ package org.yuzu.yuzu_emu.features.settings.ui
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.IntentFilter
import android.os.Bundle import android.os.Bundle
import android.view.Menu import android.view.Menu
import android.view.View import android.view.View
import android.widget.Toast import android.widget.Toast
import androidx.activity.viewModels import androidx.activity.viewModels
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.ViewCompat import androidx.core.view.ViewCompat
import androidx.core.view.WindowCompat import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsCompat import androidx.core.view.WindowInsetsCompat
import androidx.core.view.updatePadding import androidx.core.view.updatePadding
import androidx.localbroadcastmanager.content.LocalBroadcastManager
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import org.yuzu.yuzu_emu.NativeLibrary import org.yuzu.yuzu_emu.NativeLibrary
import org.yuzu.yuzu_emu.R import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.databinding.ActivitySettingsBinding import org.yuzu.yuzu_emu.databinding.ActivitySettingsBinding
import org.yuzu.yuzu_emu.databinding.DialogProgressBarBinding
import org.yuzu.yuzu_emu.features.settings.model.Settings import org.yuzu.yuzu_emu.features.settings.model.Settings
import org.yuzu.yuzu_emu.features.settings.model.SettingsViewModel import org.yuzu.yuzu_emu.features.settings.model.SettingsViewModel
import org.yuzu.yuzu_emu.utils.* import org.yuzu.yuzu_emu.utils.*
class SettingsActivity : AppCompatActivity(), SettingsActivityView { class SettingsActivity : AppCompatActivity(), SettingsActivityView {
private val presenter = SettingsActivityPresenter(this) private val presenter = SettingsActivityPresenter(this)
private var dialog: AlertDialog? = null
private lateinit var binding: ActivitySettingsBinding private lateinit var binding: ActivitySettingsBinding
@ -134,47 +128,6 @@ class SettingsActivity : AppCompatActivity(), SettingsActivityView {
return duration != 0f && transition != 0f return duration != 0f && transition != 0f
} }
override fun startDirectoryInitializationService(
receiver: DirectoryStateReceiver?,
filter: IntentFilter
) {
LocalBroadcastManager.getInstance(this).registerReceiver(
receiver!!,
filter
)
DirectoryInitialization.start(this)
}
override fun stopListeningToDirectoryInitializationService(receiver: DirectoryStateReceiver) {
LocalBroadcastManager.getInstance(this).unregisterReceiver(receiver)
}
override fun showLoading() {
if (dialog == null) {
val loadingBinding = DialogProgressBarBinding.inflate(layoutInflater)
loadingBinding.progressBar.isIndeterminate = true
dialog = MaterialAlertDialogBuilder(this)
.setTitle(R.string.load_settings)
.setView(loadingBinding.root)
.setCancelable(false)
.create()
}
dialog!!.show()
}
override fun hideLoading() {
dialog!!.dismiss()
}
override fun showExternalStorageNotMountedHint() {
Toast.makeText(
this,
R.string.external_storage_not_mounted,
Toast.LENGTH_SHORT
).show()
}
override fun onSettingsFileLoaded() { override fun onSettingsFileLoaded() {
val fragment: SettingsFragmentView? = settingsFragment val fragment: SettingsFragmentView? = settingsFragment
fragment?.loadSettingsList() fragment?.loadSettingsList()

View File

@ -3,15 +3,13 @@
package org.yuzu.yuzu_emu.features.settings.ui package org.yuzu.yuzu_emu.features.settings.ui
import android.content.IntentFilter import android.content.Context
import android.os.Bundle import android.os.Bundle
import android.text.TextUtils import android.text.TextUtils
import org.yuzu.yuzu_emu.NativeLibrary import org.yuzu.yuzu_emu.NativeLibrary
import org.yuzu.yuzu_emu.features.settings.model.Settings import org.yuzu.yuzu_emu.features.settings.model.Settings
import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile
import org.yuzu.yuzu_emu.utils.DirectoryInitialization import org.yuzu.yuzu_emu.utils.DirectoryInitialization
import org.yuzu.yuzu_emu.utils.DirectoryInitialization.DirectoryInitializationState
import org.yuzu.yuzu_emu.utils.DirectoryStateReceiver
import org.yuzu.yuzu_emu.utils.Log import org.yuzu.yuzu_emu.utils.Log
import java.io.File import java.io.File
@ -19,7 +17,6 @@ class SettingsActivityPresenter(private val activityView: SettingsActivityView)
val settings: Settings get() = activityView.settings val settings: Settings get() = activityView.settings
private var shouldSave = false private var shouldSave = false
private var directoryStateReceiver: DirectoryStateReceiver? = null
private lateinit var menuTag: String private lateinit var menuTag: String
private lateinit var gameId: String private lateinit var gameId: String
@ -54,33 +51,14 @@ class SettingsActivityPresenter(private val activityView: SettingsActivityView)
Log.error(DirectoryInitialization.userDirectory + "/config/" + SettingsFile.FILE_NAME_CONFIG + ".ini") Log.error(DirectoryInitialization.userDirectory + "/config/" + SettingsFile.FILE_NAME_CONFIG + ".ini")
Log.error("yuzu config file could not be found!") Log.error("yuzu config file could not be found!")
} }
if (DirectoryInitialization.areDirectoriesReady()) {
loadSettingsUI() if (!DirectoryInitialization.areDirectoriesReady) {
} else { DirectoryInitialization.start(activityView as Context)
activityView.showLoading()
val statusIntentFilter = IntentFilter(DirectoryInitialization.BROADCAST_ACTION)
directoryStateReceiver =
DirectoryStateReceiver { directoryInitializationState: DirectoryInitializationState ->
if (directoryInitializationState == DirectoryInitializationState.YUZU_DIRECTORIES_INITIALIZED) {
activityView.hideLoading()
loadSettingsUI()
} else if (directoryInitializationState == DirectoryInitializationState.CANT_FIND_EXTERNAL_STORAGE) {
activityView.showExternalStorageNotMountedHint()
activityView.hideLoading()
}
}
activityView.startDirectoryInitializationService(
directoryStateReceiver,
statusIntentFilter
)
} }
loadSettingsUI()
} }
fun onStop(finishing: Boolean) { fun onStop(finishing: Boolean) {
if (directoryStateReceiver != null) {
activityView.stopListeningToDirectoryInitializationService(directoryStateReceiver!!)
directoryStateReceiver = null
}
if (finishing && shouldSave) { if (finishing && shouldSave) {
Log.debug("[SettingsActivity] Settings activity stopping. Saving settings to INI...") Log.debug("[SettingsActivity] Settings activity stopping. Saving settings to INI...")
settings.saveSettings(activityView) settings.saveSettings(activityView)

View File

@ -3,9 +3,7 @@
package org.yuzu.yuzu_emu.features.settings.ui package org.yuzu.yuzu_emu.features.settings.ui
import android.content.IntentFilter
import org.yuzu.yuzu_emu.features.settings.model.Settings import org.yuzu.yuzu_emu.features.settings.model.Settings
import org.yuzu.yuzu_emu.utils.DirectoryStateReceiver
/** /**
* Abstraction for the Activity that manages SettingsFragments. * Abstraction for the Activity that manages SettingsFragments.
@ -56,37 +54,4 @@ interface SettingsActivityView {
* unless this has been called, the Activity will not save to disk. * unless this has been called, the Activity will not save to disk.
*/ */
fun onSettingChanged() fun onSettingChanged()
/**
* Show loading dialog while loading the settings
*/
fun showLoading()
/**
* Hide the loading the dialog
*/
fun hideLoading()
/**
* Show a hint to the user that the app needs the external storage to be mounted
*/
fun showExternalStorageNotMountedHint()
/**
* Start the DirectoryInitialization and listen for the result.
*
* @param receiver the broadcast receiver for the DirectoryInitialization
* @param filter the Intent broadcasts to be received.
*/
fun startDirectoryInitializationService(
receiver: DirectoryStateReceiver?,
filter: IntentFilter
)
/**
* Stop listening to the DirectoryInitialization.
*
* @param receiver The broadcast receiver to unregister.
*/
fun stopListeningToDirectoryInitializationService(receiver: DirectoryStateReceiver)
} }

View File

@ -4,14 +4,13 @@
package org.yuzu.yuzu_emu.fragments package org.yuzu.yuzu_emu.fragments
import android.content.Context import android.content.Context
import android.content.IntentFilter
import android.content.SharedPreferences import android.content.SharedPreferences
import android.graphics.Color import android.graphics.Color
import android.os.Bundle import android.os.Bundle
import android.os.Handler import android.os.Handler
import android.os.Looper
import android.view.* import android.view.*
import android.widget.TextView import android.widget.TextView
import android.widget.Toast
import androidx.activity.OnBackPressedCallback import androidx.activity.OnBackPressedCallback
import androidx.appcompat.widget.PopupMenu import androidx.appcompat.widget.PopupMenu
import androidx.core.content.res.ResourcesCompat import androidx.core.content.res.ResourcesCompat
@ -19,7 +18,6 @@ import androidx.core.graphics.Insets
import androidx.core.view.ViewCompat import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat import androidx.core.view.WindowInsetsCompat
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.localbroadcastmanager.content.LocalBroadcastManager
import androidx.preference.PreferenceManager import androidx.preference.PreferenceManager
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import org.yuzu.yuzu_emu.NativeLibrary import org.yuzu.yuzu_emu.NativeLibrary
@ -32,13 +30,11 @@ import org.yuzu.yuzu_emu.features.settings.ui.SettingsActivity
import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile
import org.yuzu.yuzu_emu.model.Game import org.yuzu.yuzu_emu.model.Game
import org.yuzu.yuzu_emu.utils.* import org.yuzu.yuzu_emu.utils.*
import org.yuzu.yuzu_emu.utils.DirectoryInitialization.DirectoryInitializationState
import org.yuzu.yuzu_emu.utils.SerializableHelper.parcelable import org.yuzu.yuzu_emu.utils.SerializableHelper.parcelable
class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.FrameCallback { class EmulationFragment : Fragment(), SurfaceHolder.Callback {
private lateinit var preferences: SharedPreferences private lateinit var preferences: SharedPreferences
private lateinit var emulationState: EmulationState private lateinit var emulationState: EmulationState
private var directoryStateReceiver: DirectoryStateReceiver? = null
private var emulationActivity: EmulationActivity? = null private var emulationActivity: EmulationActivity? = null
private var perfStatsUpdater: (() -> Unit)? = null private var perfStatsUpdater: (() -> Unit)? = null
@ -144,25 +140,16 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
Choreographer.getInstance().postFrameCallback(this) if (!DirectoryInitialization.areDirectoriesReady) {
if (DirectoryInitialization.areDirectoriesReady()) { DirectoryInitialization.start(requireContext())
emulationState.run(emulationActivity!!.isActivityRecreated)
} else {
setupDirectoriesThenStartEmulation()
} }
emulationState.run(emulationActivity!!.isActivityRecreated)
} }
override fun onPause() { override fun onPause() {
if (directoryStateReceiver != null) {
LocalBroadcastManager.getInstance(requireActivity()).unregisterReceiver(
directoryStateReceiver!!
)
directoryStateReceiver = null
}
if (emulationState.isRunning) { if (emulationState.isRunning) {
emulationState.pause() emulationState.pause()
} }
Choreographer.getInstance().removeFrameCallback(this)
super.onPause() super.onPause()
} }
@ -176,36 +163,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram
super.onDetach() super.onDetach()
} }
private fun setupDirectoriesThenStartEmulation() {
val statusIntentFilter = IntentFilter(
DirectoryInitialization.BROADCAST_ACTION
)
directoryStateReceiver =
DirectoryStateReceiver { directoryInitializationState: DirectoryInitializationState ->
if (directoryInitializationState ==
DirectoryInitializationState.YUZU_DIRECTORIES_INITIALIZED
) {
emulationState.run(emulationActivity!!.isActivityRecreated)
} else if (directoryInitializationState ==
DirectoryInitializationState.CANT_FIND_EXTERNAL_STORAGE
) {
Toast.makeText(
context,
R.string.external_storage_not_mounted,
Toast.LENGTH_SHORT
)
.show()
}
}
// Registers the DirectoryStateReceiver and its intent filters
LocalBroadcastManager.getInstance(requireActivity()).registerReceiver(
directoryStateReceiver!!,
statusIntentFilter
)
DirectoryInitialization.start(requireContext())
}
fun refreshInputOverlay() { fun refreshInputOverlay() {
binding.surfaceInputOverlay.refreshControls() binding.surfaceInputOverlay.refreshControls()
} }
@ -259,11 +216,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram
emulationState.clearSurface() emulationState.clearSurface()
} }
override fun doFrame(frameTimeNanos: Long) {
Choreographer.getInstance().postFrameCallback(this)
NativeLibrary.DoFrame()
}
private fun showOverlayOptions() { private fun showOverlayOptions() {
val anchor = binding.inGameMenu.findViewById<View>(R.id.menu_overlay_controls) val anchor = binding.inGameMenu.findViewById<View>(R.id.menu_overlay_controls)
val popup = PopupMenu(requireContext(), anchor) val popup = PopupMenu(requireContext(), anchor)
@ -474,7 +426,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram
} }
companion object { companion object {
private val perfStatsUpdateHandler = Handler() private val perfStatsUpdateHandler = Handler(Looper.myLooper()!!)
fun newInstance(game: Game): EmulationFragment { fun newInstance(game: Game): EmulationFragment {
val args = Bundle() val args = Bundle()

View File

@ -41,7 +41,7 @@ class MainActivity : AppCompatActivity(), MainView {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
val splashScreen = installSplashScreen() val splashScreen = installSplashScreen()
splashScreen.setKeepOnScreenCondition { !DirectoryInitialization.areDirectoriesReady() } splashScreen.setKeepOnScreenCondition { !DirectoryInitialization.areDirectoriesReady }
ThemeHelper.setTheme(this) ThemeHelper.setTheme(this)

View File

@ -4,46 +4,26 @@
package org.yuzu.yuzu_emu.utils package org.yuzu.yuzu_emu.utils
import android.content.Context import android.content.Context
import android.content.Intent
import androidx.localbroadcastmanager.content.LocalBroadcastManager
import org.yuzu.yuzu_emu.NativeLibrary import org.yuzu.yuzu_emu.NativeLibrary
import java.io.IOException import java.io.IOException
import java.util.concurrent.atomic.AtomicBoolean
object DirectoryInitialization { object DirectoryInitialization {
const val BROADCAST_ACTION = "org.yuzu.yuzu_emu.BROADCAST"
const val EXTRA_STATE = "directoryState"
@Volatile
private var directoryState: DirectoryInitializationState? = null
private var userPath: String? = null private var userPath: String? = null
private val isDirectoryInitializationRunning = AtomicBoolean(false)
var areDirectoriesReady: Boolean = false
@JvmStatic @JvmStatic
fun start(context: Context) { fun start(context: Context) {
// Can take a few seconds to run, so don't block UI thread. if (!areDirectoriesReady) {
Runnable { init(context) }.run()
}
private fun init(context: Context) {
if (!isDirectoryInitializationRunning.compareAndSet(false, true)) return
if (directoryState != DirectoryInitializationState.YUZU_DIRECTORIES_INITIALIZED) {
initializeInternalStorage(context) initializeInternalStorage(context)
NativeLibrary.InitializeEmulation() NativeLibrary.InitializeEmulation()
directoryState = DirectoryInitializationState.YUZU_DIRECTORIES_INITIALIZED areDirectoriesReady = true
} }
isDirectoryInitializationRunning.set(false)
sendBroadcastState(directoryState, context)
}
fun areDirectoriesReady(): Boolean {
return directoryState == DirectoryInitializationState.YUZU_DIRECTORIES_INITIALIZED
} }
val userDirectory: String? val userDirectory: String?
get() { get() {
checkNotNull(directoryState) { "DirectoryInitialization has to run at least once!" } check(areDirectoriesReady) { "Directory initialization is not ready!" }
check(!isDirectoryInitializationRunning.get()) { "DirectoryInitialization has to finish running first!" }
return userPath return userPath
} }
@ -55,15 +35,4 @@ object DirectoryInitialization {
e.printStackTrace() e.printStackTrace()
} }
} }
private fun sendBroadcastState(state: DirectoryInitializationState?, context: Context) {
val localIntent = Intent(BROADCAST_ACTION)
.putExtra(EXTRA_STATE, state)
LocalBroadcastManager.getInstance(context).sendBroadcast(localIntent)
}
enum class DirectoryInitializationState {
YUZU_DIRECTORIES_INITIALIZED,
CANT_FIND_EXTERNAL_STORAGE
}
} }

View File

@ -1,18 +0,0 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu.utils
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import org.yuzu.yuzu_emu.utils.DirectoryInitialization.DirectoryInitializationState
class DirectoryStateReceiver(var callback: (DirectoryInitializationState) -> Unit) :
BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
val state = intent
.getSerializableExtra(DirectoryInitialization.EXTRA_STATE) as DirectoryInitializationState
callback.invoke(state)
}
}

View File

@ -367,8 +367,6 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_SurfaceDestroyed(JNIEnv* env,
EmulationSession::GetInstance().SurfaceChanged(); EmulationSession::GetInstance().SurfaceChanged();
} }
void Java_org_yuzu_yuzu_1emu_NativeLibrary_DoFrame(JNIEnv* env, [[maybe_unused]] jclass clazz) {}
void Java_org_yuzu_yuzu_1emu_NativeLibrary_NotifyOrientationChange(JNIEnv* env, void Java_org_yuzu_yuzu_1emu_NativeLibrary_NotifyOrientationChange(JNIEnv* env,
[[maybe_unused]] jclass clazz, [[maybe_unused]] jclass clazz,
jint layout_option, jint layout_option,

View File

@ -98,8 +98,6 @@
<string name="load_settings">Loading Settings…</string> <string name="load_settings">Loading Settings…</string>
<string name="external_storage_not_mounted">The external storage needs to be available in order to use yuzu</string>
<string name="empty_gamelist">No files were found or no game directory has been selected yet.</string> <string name="empty_gamelist">No files were found or no game directory has been selected yet.</string>
<!-- Software keyboard --> <!-- Software keyboard -->