Browse Source
Merge pull request #11405 from t895/emulation-loading
Merge pull request #11405 from t895/emulation-loading
android: Emulation loading UI and fixespull/15/merge
committed by
GitHub
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
28 changed files with 393 additions and 237 deletions
-
22src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt
-
15src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt
-
24src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt
-
52src/android/app/src/main/java/org/yuzu/yuzu_emu/disk_shader_cache/DiskShaderCacheProgress.kt
-
31src/android/app/src/main/java/org/yuzu/yuzu_emu/disk_shader_cache/ShaderProgressViewModel.kt
-
103src/android/app/src/main/java/org/yuzu/yuzu_emu/disk_shader_cache/ui/ShaderProgressDialogFragment.kt
-
110src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt
-
59src/android/app/src/main/java/org/yuzu/yuzu_emu/model/EmulationViewModel.kt
-
77src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameIconUtils.kt
-
14src/android/app/src/main/jni/id_cache.cpp
-
2src/android/app/src/main/jni/id_cache.h
-
22src/android/app/src/main/jni/native.cpp
-
83src/android/app/src/main/res/layout/fragment_emulation.xml
-
1src/android/app/src/main/res/values-de/strings.xml
-
1src/android/app/src/main/res/values-es/strings.xml
-
1src/android/app/src/main/res/values-fr/strings.xml
-
1src/android/app/src/main/res/values-it/strings.xml
-
1src/android/app/src/main/res/values-ja/strings.xml
-
1src/android/app/src/main/res/values-ko/strings.xml
-
1src/android/app/src/main/res/values-nb/strings.xml
-
1src/android/app/src/main/res/values-pl/strings.xml
-
1src/android/app/src/main/res/values-pt-rBR/strings.xml
-
1src/android/app/src/main/res/values-pt-rPT/strings.xml
-
1src/android/app/src/main/res/values-ru/strings.xml
-
1src/android/app/src/main/res/values-uk/strings.xml
-
1src/android/app/src/main/res/values-zh-rCN/strings.xml
-
1src/android/app/src/main/res/values-zh-rTW/strings.xml
-
2src/android/app/src/main/res/values/strings.xml
@ -1,31 +0,0 @@ |
|||||
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project |
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later |
|
||||
|
|
||||
package org.yuzu.yuzu_emu.disk_shader_cache |
|
||||
|
|
||||
import androidx.lifecycle.LiveData |
|
||||
import androidx.lifecycle.MutableLiveData |
|
||||
import androidx.lifecycle.ViewModel |
|
||||
|
|
||||
class ShaderProgressViewModel : ViewModel() { |
|
||||
private val _progress = MutableLiveData(0) |
|
||||
val progress: LiveData<Int> get() = _progress |
|
||||
|
|
||||
private val _max = MutableLiveData(0) |
|
||||
val max: LiveData<Int> get() = _max |
|
||||
|
|
||||
private val _message = MutableLiveData("") |
|
||||
val message: LiveData<String> get() = _message |
|
||||
|
|
||||
fun setProgress(progress: Int) { |
|
||||
_progress.postValue(progress) |
|
||||
} |
|
||||
|
|
||||
fun setMax(max: Int) { |
|
||||
_max.postValue(max) |
|
||||
} |
|
||||
|
|
||||
fun setMessage(msg: String) { |
|
||||
_message.postValue(msg) |
|
||||
} |
|
||||
} |
|
||||
@ -1,103 +0,0 @@ |
|||||
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project |
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later |
|
||||
|
|
||||
package org.yuzu.yuzu_emu.disk_shader_cache.ui |
|
||||
|
|
||||
import android.app.Dialog |
|
||||
import android.os.Bundle |
|
||||
import android.view.LayoutInflater |
|
||||
import android.view.View |
|
||||
import android.view.ViewGroup |
|
||||
import androidx.appcompat.app.AlertDialog |
|
||||
import androidx.fragment.app.DialogFragment |
|
||||
import androidx.lifecycle.ViewModelProvider |
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder |
|
||||
import org.yuzu.yuzu_emu.databinding.DialogProgressBarBinding |
|
||||
import org.yuzu.yuzu_emu.disk_shader_cache.DiskShaderCacheProgress |
|
||||
import org.yuzu.yuzu_emu.disk_shader_cache.ShaderProgressViewModel |
|
||||
|
|
||||
class ShaderProgressDialogFragment : DialogFragment() { |
|
||||
private var _binding: DialogProgressBarBinding? = null |
|
||||
private val binding get() = _binding!! |
|
||||
|
|
||||
private lateinit var alertDialog: AlertDialog |
|
||||
|
|
||||
private lateinit var shaderProgressViewModel: ShaderProgressViewModel |
|
||||
|
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { |
|
||||
_binding = DialogProgressBarBinding.inflate(layoutInflater) |
|
||||
shaderProgressViewModel = |
|
||||
ViewModelProvider(requireActivity())[ShaderProgressViewModel::class.java] |
|
||||
|
|
||||
val title = requireArguments().getString(TITLE) |
|
||||
val message = requireArguments().getString(MESSAGE) |
|
||||
|
|
||||
isCancelable = false |
|
||||
alertDialog = MaterialAlertDialogBuilder(requireActivity()) |
|
||||
.setView(binding.root) |
|
||||
.setTitle(title) |
|
||||
.setMessage(message) |
|
||||
.create() |
|
||||
return alertDialog |
|
||||
} |
|
||||
|
|
||||
override fun onCreateView( |
|
||||
inflater: LayoutInflater, |
|
||||
container: ViewGroup?, |
|
||||
savedInstanceState: Bundle? |
|
||||
): View { |
|
||||
return binding.root |
|
||||
} |
|
||||
|
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { |
|
||||
super.onViewCreated(view, savedInstanceState) |
|
||||
shaderProgressViewModel.progress.observe(viewLifecycleOwner) { progress -> |
|
||||
binding.progressBar.progress = progress |
|
||||
setUpdateText() |
|
||||
} |
|
||||
shaderProgressViewModel.max.observe(viewLifecycleOwner) { max -> |
|
||||
binding.progressBar.max = max |
|
||||
setUpdateText() |
|
||||
} |
|
||||
shaderProgressViewModel.message.observe(viewLifecycleOwner) { msg -> |
|
||||
alertDialog.setMessage(msg) |
|
||||
} |
|
||||
synchronized(DiskShaderCacheProgress.finishLock) { |
|
||||
DiskShaderCacheProgress.finishLock.notifyAll() |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
override fun onDestroyView() { |
|
||||
super.onDestroyView() |
|
||||
_binding = null |
|
||||
} |
|
||||
|
|
||||
fun onUpdateProgress(msg: String, progress: Int, max: Int) { |
|
||||
shaderProgressViewModel.setProgress(progress) |
|
||||
shaderProgressViewModel.setMax(max) |
|
||||
shaderProgressViewModel.setMessage(msg) |
|
||||
} |
|
||||
|
|
||||
private fun setUpdateText() { |
|
||||
binding.progressText.text = String.format( |
|
||||
"%d/%d", |
|
||||
shaderProgressViewModel.progress.value, |
|
||||
shaderProgressViewModel.max.value |
|
||||
) |
|
||||
} |
|
||||
|
|
||||
companion object { |
|
||||
const val TAG = "ProgressDialogFragment" |
|
||||
const val TITLE = "title" |
|
||||
const val MESSAGE = "message" |
|
||||
|
|
||||
fun newInstance(title: String, message: String): ShaderProgressDialogFragment { |
|
||||
val frag = ShaderProgressDialogFragment() |
|
||||
val args = Bundle() |
|
||||
args.putString(TITLE, title) |
|
||||
args.putString(MESSAGE, message) |
|
||||
frag.arguments = args |
|
||||
return frag |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -0,0 +1,59 @@ |
|||||
|
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project |
||||
|
// SPDX-License-Identifier: GPL-3.0-or-later |
||||
|
|
||||
|
package org.yuzu.yuzu_emu.model |
||||
|
|
||||
|
import androidx.lifecycle.LiveData |
||||
|
import androidx.lifecycle.MutableLiveData |
||||
|
import androidx.lifecycle.ViewModel |
||||
|
|
||||
|
class EmulationViewModel : ViewModel() { |
||||
|
private val _emulationStarted = MutableLiveData(false) |
||||
|
val emulationStarted: LiveData<Boolean> get() = _emulationStarted |
||||
|
|
||||
|
private val _isEmulationStopping = MutableLiveData(false) |
||||
|
val isEmulationStopping: LiveData<Boolean> get() = _isEmulationStopping |
||||
|
|
||||
|
private val _shaderProgress = MutableLiveData(0) |
||||
|
val shaderProgress: LiveData<Int> get() = _shaderProgress |
||||
|
|
||||
|
private val _totalShaders = MutableLiveData(0) |
||||
|
val totalShaders: LiveData<Int> get() = _totalShaders |
||||
|
|
||||
|
private val _shaderMessage = MutableLiveData("") |
||||
|
val shaderMessage: LiveData<String> get() = _shaderMessage |
||||
|
|
||||
|
fun setEmulationStarted(started: Boolean) { |
||||
|
_emulationStarted.postValue(started) |
||||
|
} |
||||
|
|
||||
|
fun setIsEmulationStopping(value: Boolean) { |
||||
|
_isEmulationStopping.value = value |
||||
|
} |
||||
|
|
||||
|
fun setShaderProgress(progress: Int) { |
||||
|
_shaderProgress.value = progress |
||||
|
} |
||||
|
|
||||
|
fun setTotalShaders(max: Int) { |
||||
|
_totalShaders.value = max |
||||
|
} |
||||
|
|
||||
|
fun setShaderMessage(msg: String) { |
||||
|
_shaderMessage.value = msg |
||||
|
} |
||||
|
|
||||
|
fun updateProgress(msg: String, progress: Int, max: Int) { |
||||
|
setShaderMessage(msg) |
||||
|
setShaderProgress(progress) |
||||
|
setTotalShaders(max) |
||||
|
} |
||||
|
|
||||
|
fun clear() { |
||||
|
_emulationStarted.value = false |
||||
|
_isEmulationStopping.value = false |
||||
|
_shaderProgress.value = 0 |
||||
|
_totalShaders.value = 0 |
||||
|
_shaderMessage.value = "" |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,77 @@ |
|||||
|
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project |
||||
|
// SPDX-License-Identifier: GPL-2.0-or-later |
||||
|
|
||||
|
package org.yuzu.yuzu_emu.utils |
||||
|
|
||||
|
import android.graphics.Bitmap |
||||
|
import android.graphics.BitmapFactory |
||||
|
import android.widget.ImageView |
||||
|
import androidx.core.graphics.drawable.toDrawable |
||||
|
import coil.ImageLoader |
||||
|
import coil.decode.DataSource |
||||
|
import coil.fetch.DrawableResult |
||||
|
import coil.fetch.FetchResult |
||||
|
import coil.fetch.Fetcher |
||||
|
import coil.key.Keyer |
||||
|
import coil.memory.MemoryCache |
||||
|
import coil.request.ImageRequest |
||||
|
import coil.request.Options |
||||
|
import org.yuzu.yuzu_emu.NativeLibrary |
||||
|
import org.yuzu.yuzu_emu.R |
||||
|
import org.yuzu.yuzu_emu.YuzuApplication |
||||
|
import org.yuzu.yuzu_emu.model.Game |
||||
|
|
||||
|
class GameIconFetcher( |
||||
|
private val game: Game, |
||||
|
private val options: Options |
||||
|
) : Fetcher { |
||||
|
override suspend fun fetch(): FetchResult { |
||||
|
return DrawableResult( |
||||
|
drawable = decodeGameIcon(game.path)!!.toDrawable(options.context.resources), |
||||
|
isSampled = false, |
||||
|
dataSource = DataSource.DISK |
||||
|
) |
||||
|
} |
||||
|
|
||||
|
private fun decodeGameIcon(uri: String): Bitmap? { |
||||
|
val data = NativeLibrary.getIcon(uri) |
||||
|
return BitmapFactory.decodeByteArray( |
||||
|
data, |
||||
|
0, |
||||
|
data.size, |
||||
|
BitmapFactory.Options() |
||||
|
) |
||||
|
} |
||||
|
|
||||
|
class Factory : Fetcher.Factory<Game> { |
||||
|
override fun create(data: Game, options: Options, imageLoader: ImageLoader): Fetcher = |
||||
|
GameIconFetcher(data, options) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
class GameIconKeyer : Keyer<Game> { |
||||
|
override fun key(data: Game, options: Options): String = data.path |
||||
|
} |
||||
|
|
||||
|
object GameIconUtils { |
||||
|
private val imageLoader = ImageLoader.Builder(YuzuApplication.appContext) |
||||
|
.components { |
||||
|
add(GameIconKeyer()) |
||||
|
add(GameIconFetcher.Factory()) |
||||
|
} |
||||
|
.memoryCache { |
||||
|
MemoryCache.Builder(YuzuApplication.appContext) |
||||
|
.maxSizePercent(0.25) |
||||
|
.build() |
||||
|
} |
||||
|
.build() |
||||
|
|
||||
|
fun loadGameIcon(game: Game, imageView: ImageView) { |
||||
|
val request = ImageRequest.Builder(YuzuApplication.appContext) |
||||
|
.data(game) |
||||
|
.target(imageView) |
||||
|
.error(R.drawable.default_icon) |
||||
|
.build() |
||||
|
imageLoader.enqueue(request) |
||||
|
} |
||||
|
} |
||||
Write
Preview
Loading…
Cancel
Save
Reference in new issue