Обновить src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt

- Add MEM overlay
- Add thermal overlay to show temperature in C/F with color indication
This commit is contained in:
pavelbarabanov 2025-03-28 19:11:22 +00:00 committed by Briar
parent 7c4fdaf528
commit 70c151fba0

View file

@ -59,12 +59,21 @@ import org.yuzu.yuzu_emu.overlay.model.OverlayLayout
import org.yuzu.yuzu_emu.utils.* import org.yuzu.yuzu_emu.utils.*
import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
import java.lang.NullPointerException import java.lang.NullPointerException
import android.content.BroadcastReceiver
import android.content.Intent
import android.content.IntentFilter
import android.os.BatteryManager
import android.util.TypedValue
import android.app.ActivityManager
import android.graphics.Color
import android.os.Debug
class EmulationFragment : Fragment(), SurfaceHolder.Callback { class EmulationFragment : Fragment(), SurfaceHolder.Callback {
private lateinit var emulationState: EmulationState private lateinit var emulationState: EmulationState
private var emulationActivity: EmulationActivity? = null private var emulationActivity: EmulationActivity? = null
private var perfStatsUpdater: (() -> Unit)? = null private var perfStatsUpdater: (() -> Unit)? = null
private var thermalStatsUpdater: (() -> Unit)? = null private var thermalStatsUpdater: (() -> Unit)? = null
private var batteryReceiverRegistered: Boolean = false
private var _binding: FragmentEmulationBinding? = null private var _binding: FragmentEmulationBinding? = null
private val binding get() = _binding!! private val binding get() = _binding!!
@ -372,7 +381,8 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
// Setup overlays // Setup overlays
updateShowFpsOverlay() updateShowFpsOverlay()
updateThermalOverlay() val temperature = getBatteryTemperature(requireContext())
updateThermalOverlay(temperature)
} }
} }
emulationViewModel.isEmulationStopping.collect(viewLifecycleOwner) { emulationViewModel.isEmulationStopping.collect(viewLifecycleOwner) {
@ -461,11 +471,23 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
override fun onPause() { override fun onPause() {
if (emulationState.isRunning && emulationActivity?.isInPictureInPictureMode != true) { if (emulationState.isRunning && emulationActivity?.isInPictureInPictureMode != true) {
emulationState.pause() emulationState.pause()
}
context?.let {
if (batteryReceiverRegistered) {
it.unregisterReceiver(batteryReceiver)
batteryReceiverRegistered = false
}
} }
super.onPause() super.onPause()
} }
override fun onDestroyView() { override fun onDestroyView() {
context?.let {
if (batteryReceiverRegistered) {
it.unregisterReceiver(batteryReceiver)
batteryReceiverRegistered = false
}
}
super.onDestroyView() super.onDestroyView()
_binding = null _binding = null
} }
@ -474,6 +496,14 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
NativeLibrary.clearEmulationActivity() NativeLibrary.clearEmulationActivity()
super.onDetach() super.onDetach()
} }
override fun onResume() {
super.onResume()
if (!batteryReceiverRegistered) {
val filter = IntentFilter(Intent.ACTION_BATTERY_CHANGED)
context?.registerReceiver(batteryReceiver, filter)
batteryReceiverRegistered = true
}
}
private fun resetInputOverlay() { private fun resetInputOverlay() {
IntSetting.OVERLAY_SCALE.reset() IntSetting.OVERLAY_SCALE.reset()
@ -482,7 +512,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
binding.surfaceInputOverlay.resetLayoutVisibilityAndPlacement() binding.surfaceInputOverlay.resetLayoutVisibilityAndPlacement()
} }
} }
@SuppressLint("DefaultLocale")
private fun updateShowFpsOverlay() { private fun updateShowFpsOverlay() {
val showOverlay = BooleanSetting.SHOW_PERFORMANCE_OVERLAY.getBoolean() val showOverlay = BooleanSetting.SHOW_PERFORMANCE_OVERLAY.getBoolean()
binding.showFpsText.setVisible(showOverlay) binding.showFpsText.setVisible(showOverlay)
@ -498,9 +528,21 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
val perfStats = NativeLibrary.getPerfStats() val perfStats = NativeLibrary.getPerfStats()
val cpuBackend = NativeLibrary.getCpuBackend() val cpuBackend = NativeLibrary.getCpuBackend()
val gpuDriver = NativeLibrary.getGpuDriver() val gpuDriver = NativeLibrary.getGpuDriver()
// Get memory info
val mi = ActivityManager.MemoryInfo()
val activityManager =
requireContext().getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
activityManager.getMemoryInfo(mi)
// Calculate used memory
val usedMegs = (mi.totalMem - mi.availMem) / 1048576L // Convert bytes to megabytes
if (_binding != null) { if (_binding != null) {
binding.showFpsText.text = binding.showFpsText.text = String.format(
String.format("FPS: %.1f\n%s/%s", perfStats[FPS], cpuBackend, gpuDriver) "FPS: %.1f\nMEM: %d MB\n%s/%s",
perfStats[FPS], usedMegs, cpuBackend, gpuDriver
)
} }
perfStatsUpdateHandler.postDelayed(perfStatsUpdater!!, 800) perfStatsUpdateHandler.postDelayed(perfStatsUpdater!!, 800)
} }
@ -513,36 +555,54 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
} }
} }
private fun updateThermalOverlay() { private val batteryReceiver = object : BroadcastReceiver() {
val showOverlay = BooleanSetting.SHOW_THERMAL_OVERLAY.getBoolean() override fun onReceive(context: Context?, intent: Intent?) {
binding.showThermalsText.setVisible(showOverlay) intent?.let {
if (showOverlay) { if (it.action == Intent.ACTION_BATTERY_CHANGED) {
thermalStatsUpdater = { val temperature = getBatteryTemperature(context!!)
if (emulationViewModel.emulationStarted.value && updateThermalOverlay(temperature)
}
}
}
}
private fun updateThermalOverlay(temperature: Float) {
if (BooleanSetting.SHOW_THERMAL_OVERLAY.getBoolean() &&
emulationViewModel.emulationStarted.value &&
!emulationViewModel.isEmulationStopping.value !emulationViewModel.isEmulationStopping.value
) { ) {
// Get thermal status for color
val thermalStatus = when (powerManager.currentThermalStatus) { val thermalStatus = when (powerManager.currentThermalStatus) {
PowerManager.THERMAL_STATUS_LIGHT -> "😥" PowerManager.THERMAL_STATUS_NONE -> 0f
PowerManager.THERMAL_STATUS_MODERATE -> "🥵" PowerManager.THERMAL_STATUS_LIGHT -> 0.25f
PowerManager.THERMAL_STATUS_SEVERE -> "🔥" PowerManager.THERMAL_STATUS_MODERATE -> 0.5f
PowerManager.THERMAL_STATUS_SEVERE -> 0.75f
PowerManager.THERMAL_STATUS_CRITICAL, PowerManager.THERMAL_STATUS_CRITICAL,
PowerManager.THERMAL_STATUS_EMERGENCY, PowerManager.THERMAL_STATUS_EMERGENCY,
PowerManager.THERMAL_STATUS_SHUTDOWN -> "☢️" PowerManager.THERMAL_STATUS_SHUTDOWN -> 1.0f
else -> 0f
}
else -> "🙂" // Convert to Fahrenheit
} val fahrenheit = (temperature * 9f / 5f) + 32f
if (_binding != null) {
binding.showThermalsText.text = thermalStatus // Color based on thermal status (green to red)
} val red = (thermalStatus * 255).toInt()
thermalStatsUpdateHandler.postDelayed(thermalStatsUpdater!!, 1000) val green = ((1f - thermalStatus) * 255).toInt()
} val color = android.graphics.Color.rgb(red, green, 0)
}
thermalStatsUpdateHandler.post(thermalStatsUpdater!!) binding.showThermalsText.setTextColor(color)
} else { binding.showThermalsText.text = String.format("%.1f°C • %.1f°F", temperature, fahrenheit)
if (thermalStatsUpdater != null) {
thermalStatsUpdateHandler.removeCallbacks(thermalStatsUpdater!!)
} }
} }
private fun getBatteryTemperature(context: Context): Float {
val intent: Intent? = context.registerReceiver(
null,
IntentFilter(Intent.ACTION_BATTERY_CHANGED)
)
val temperature = intent?.getIntExtra(BatteryManager.EXTRA_TEMPERATURE, 0) ?: 0
return temperature / 10.0f
} }
@SuppressLint("SourceLockedOrientationActivity") @SuppressLint("SourceLockedOrientationActivity")
@ -696,7 +756,21 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
R.id.thermal_indicator -> { R.id.thermal_indicator -> {
it.isChecked = !it.isChecked it.isChecked = !it.isChecked
BooleanSetting.SHOW_THERMAL_OVERLAY.setBoolean(it.isChecked) BooleanSetting.SHOW_THERMAL_OVERLAY.setBoolean(it.isChecked)
updateThermalOverlay() if (it.isChecked) {
val temperature = getBatteryTemperature(requireContext())
updateThermalOverlay(temperature)
if (!batteryReceiverRegistered) {
val filter = IntentFilter(Intent.ACTION_BATTERY_CHANGED)
context?.registerReceiver(batteryReceiver, filter)
batteryReceiverRegistered = true
}
} else {
if (batteryReceiverRegistered) {
context?.unregisterReceiver(batteryReceiver)
batteryReceiverRegistered = false
}
binding.showThermalsText.text = ""
}
true true
} }