mirror of
https://git.eden-emu.dev/archive/citron
synced 2026-03-22 17:46:08 -04:00
feat(android): implement Project Thor dual-screen dashboard
- Add ThorPresentation.kt for secondary display performance dashboard - Create thor_presentation.xml with real-time stats layout - Modify EmulationActivity to auto-detect secondary displays - Display FPS, speed, frame time, resolution, and shader status - Color-coded FPS indicator (green/yellow/orange/red) - Add 12 new string resources for Thor dashboard UI - Keep main screen immersive while bottom screen shows stats Signed-off-by: Zephyron <zephyron@citron-emu.org>
This commit is contained in:
@@ -53,6 +53,7 @@ import org.citron.citron_emu.utils.NativeConfig
|
||||
import org.citron.citron_emu.utils.NfcReader
|
||||
import org.citron.citron_emu.utils.ParamPackage
|
||||
import org.citron.citron_emu.utils.ThemeHelper
|
||||
import org.citron.citron_emu.features.thor.ThorPresentation
|
||||
import java.text.NumberFormat
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
@@ -72,6 +73,9 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
|
||||
private val actionMute = "ACTION_EMULATOR_MUTE"
|
||||
private val actionUnmute = "ACTION_EMULATOR_UNMUTE"
|
||||
|
||||
// PROJECT THOR: Dual Screen Dashboard for AYN Thor
|
||||
private var thorPresentation: ThorPresentation? = null
|
||||
|
||||
private val emulationViewModel: EmulationViewModel by viewModels()
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
@@ -177,12 +181,67 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
|
||||
InputHandler.updateControllerData()
|
||||
|
||||
buildPictureInPictureParams()
|
||||
|
||||
// PROJECT THOR: Detect secondary display and show performance dashboard
|
||||
// This keeps the main screen immersive while showing stats on the bottom display
|
||||
initThorPresentation()
|
||||
}
|
||||
|
||||
/**
|
||||
* PROJECT THOR: Initialize the dual-screen performance dashboard
|
||||
* Automatically detects secondary displays (like AYN Thor's bottom screen)
|
||||
* and shows a live performance dashboard with FPS, speed, and settings info.
|
||||
*/
|
||||
private fun initThorPresentation() {
|
||||
if (thorPresentation == null && ThorPresentation.hasSecondaryDisplay(this)) {
|
||||
try {
|
||||
thorPresentation = ThorPresentation.createIfAvailable(this)
|
||||
thorPresentation?.let { presentation ->
|
||||
presentation.show()
|
||||
presentation.startUpdates()
|
||||
Toast.makeText(
|
||||
this,
|
||||
getString(R.string.thor_secondary_display_detected),
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
Log.debug("[Thor] Secondary display detected - Dashboard enabled")
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.error("[Thor] Failed to initialize presentation: ${e.message}")
|
||||
thorPresentation = null
|
||||
}
|
||||
} else {
|
||||
// Resume updates if presentation already exists
|
||||
thorPresentation?.startUpdates()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* PROJECT THOR: Clean up the dual-screen presentation
|
||||
*/
|
||||
private fun cleanupThorPresentation() {
|
||||
thorPresentation?.let { presentation ->
|
||||
presentation.stopUpdates()
|
||||
if (presentation.isShowing) {
|
||||
presentation.dismiss()
|
||||
}
|
||||
}
|
||||
thorPresentation = null
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
nfcReader.stopScanning()
|
||||
stopMotionSensorListener()
|
||||
|
||||
// PROJECT THOR: Stop dashboard updates when paused
|
||||
thorPresentation?.stopUpdates()
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
// PROJECT THOR: Clean up dual-screen presentation on destroy
|
||||
cleanupThorPresentation()
|
||||
super.onDestroy()
|
||||
}
|
||||
|
||||
override fun onUserLeaveHint() {
|
||||
|
||||
@@ -0,0 +1,225 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 citron Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
package org.citron.citron_emu.features.thor
|
||||
|
||||
import android.app.Presentation
|
||||
import android.content.Context
|
||||
import android.hardware.display.DisplayManager
|
||||
import android.os.Bundle
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.view.Display
|
||||
import android.view.View
|
||||
import android.widget.TextView
|
||||
import org.citron.citron_emu.NativeLibrary
|
||||
import org.citron.citron_emu.R
|
||||
import org.citron.citron_emu.features.settings.model.BooleanSetting
|
||||
import org.citron.citron_emu.features.settings.model.IntSetting
|
||||
|
||||
/**
|
||||
* PROJECT THOR: Dual Screen Implementation for AYN Thor
|
||||
*
|
||||
* This Presentation class renders a Live Performance Dashboard on the Thor's
|
||||
* secondary bottom display, keeping the main 1080p screen completely immersive.
|
||||
*
|
||||
* Features:
|
||||
* - Real-time FPS, Emulation Speed %, and Frame Time
|
||||
* - Live settings display (Resolution, Docked State, CPU Accuracy)
|
||||
* - Shader compilation indicator
|
||||
*/
|
||||
class ThorPresentation(
|
||||
context: Context,
|
||||
display: Display
|
||||
) : Presentation(context, display) {
|
||||
|
||||
private lateinit var fpsText: TextView
|
||||
private lateinit var speedText: TextView
|
||||
private lateinit var frameTimeText: TextView
|
||||
private lateinit var resolutionText: TextView
|
||||
private lateinit var dockedText: TextView
|
||||
private lateinit var cpuAccuracyText: TextView
|
||||
private lateinit var shaderText: TextView
|
||||
private lateinit var gpuDriverText: TextView
|
||||
private lateinit var astcModeText: TextView
|
||||
|
||||
private val handler = Handler(Looper.getMainLooper())
|
||||
private var isRunning = false
|
||||
|
||||
// Poll interval for performance stats (500ms as specified in report)
|
||||
private val pollInterval = 500L
|
||||
|
||||
private val updateRunnable = object : Runnable {
|
||||
override fun run() {
|
||||
if (isRunning) {
|
||||
updatePerformanceStats()
|
||||
handler.postDelayed(this, pollInterval)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.thor_presentation)
|
||||
|
||||
initializeViews()
|
||||
updateStaticSettings()
|
||||
}
|
||||
|
||||
private fun initializeViews() {
|
||||
fpsText = findViewById(R.id.thor_fps)
|
||||
speedText = findViewById(R.id.thor_speed)
|
||||
frameTimeText = findViewById(R.id.thor_frame_time)
|
||||
resolutionText = findViewById(R.id.thor_resolution)
|
||||
dockedText = findViewById(R.id.thor_docked)
|
||||
cpuAccuracyText = findViewById(R.id.thor_cpu_accuracy)
|
||||
shaderText = findViewById(R.id.thor_shader_status)
|
||||
gpuDriverText = findViewById(R.id.thor_gpu_driver)
|
||||
astcModeText = findViewById(R.id.thor_astc_mode)
|
||||
}
|
||||
|
||||
/**
|
||||
* Update static settings that don't change during gameplay
|
||||
*/
|
||||
private fun updateStaticSettings() {
|
||||
// Resolution scaling - based on arrays.xml rendererResolutionValues
|
||||
val resolutionIndex = IntSetting.RENDERER_RESOLUTION.getInt()
|
||||
val resolutionString = when (resolutionIndex) {
|
||||
-1 -> "0.25X (180p)"
|
||||
0 -> "0.5X (360p)"
|
||||
1 -> "0.75X (540p)"
|
||||
2 -> "1X (720p)"
|
||||
11 -> "1.25X (900p)"
|
||||
3 -> "1.5X (1080p)" // PROJECT THOR: Optimal for AYN Thor
|
||||
12 -> "1.75X (1260p)"
|
||||
4 -> "2X (1440p)"
|
||||
5 -> "3X (2160p)"
|
||||
6 -> "4X (2880p)"
|
||||
7 -> "5X"
|
||||
8 -> "6X"
|
||||
9 -> "7X"
|
||||
10 -> "8X"
|
||||
else -> "${resolutionIndex}X"
|
||||
}
|
||||
resolutionText.text = resolutionString
|
||||
|
||||
// Docked mode
|
||||
val isDocked = BooleanSetting.DOCKED_MODE.getBoolean()
|
||||
dockedText.text = if (isDocked) "Docked" else "Handheld"
|
||||
|
||||
// CPU Accuracy
|
||||
val cpuAccuracy = IntSetting.CPU_ACCURACY.getInt()
|
||||
val cpuAccuracyString = when (cpuAccuracy) {
|
||||
0 -> "Auto"
|
||||
1 -> "Accurate"
|
||||
2 -> "Unsafe"
|
||||
3 -> "Paranoid"
|
||||
else -> "Unknown"
|
||||
}
|
||||
cpuAccuracyText.text = cpuAccuracyString
|
||||
|
||||
// GPU Driver (if custom driver is loaded)
|
||||
val gpuDriver = NativeLibrary.getGpuDriver()
|
||||
gpuDriverText.text = if (gpuDriver.isNotEmpty()) gpuDriver else "System Driver"
|
||||
|
||||
// ASTC Decode Mode
|
||||
val astcMode = IntSetting.ANDROID_ASTC_MODE.getInt()
|
||||
val astcModeString = when (astcMode) {
|
||||
0 -> "Auto"
|
||||
1 -> "Native"
|
||||
2 -> "Decompress"
|
||||
else -> "Auto"
|
||||
}
|
||||
astcModeText.text = astcModeString
|
||||
}
|
||||
|
||||
/**
|
||||
* Update real-time performance statistics
|
||||
* Called every 500ms while emulation is running
|
||||
*/
|
||||
private fun updatePerformanceStats() {
|
||||
try {
|
||||
val perfStats = NativeLibrary.getPerfStats()
|
||||
if (perfStats != null && perfStats.size >= 4) {
|
||||
// perfStats format: [fps, emulationSpeed, frameTime, fifoPercentage]
|
||||
val fps = perfStats[0]
|
||||
val speed = perfStats[1]
|
||||
val frameTime = perfStats[2]
|
||||
|
||||
fpsText.text = String.format("%.1f FPS", fps)
|
||||
speedText.text = String.format("%.0f%%", speed * 100)
|
||||
frameTimeText.text = String.format("%.2f ms", frameTime * 1000)
|
||||
|
||||
// Color code FPS based on performance
|
||||
val fpsColor = when {
|
||||
fps >= 58 -> 0xFF00FF00.toInt() // Green - excellent
|
||||
fps >= 45 -> 0xFFFFFF00.toInt() // Yellow - acceptable
|
||||
fps >= 30 -> 0xFFFF8800.toInt() // Orange - playable
|
||||
else -> 0xFFFF0000.toInt() // Red - poor
|
||||
}
|
||||
fpsText.setTextColor(fpsColor)
|
||||
}
|
||||
|
||||
// Shader compilation status
|
||||
val shadersBuilding = NativeLibrary.getShadersBuilding()
|
||||
if (shadersBuilding > 0) {
|
||||
shaderText.text = context.getString(R.string.thor_shaders_building) + " ($shadersBuilding)"
|
||||
shaderText.visibility = View.VISIBLE
|
||||
shaderText.setTextColor(0xFFFFAA00.toInt()) // Orange
|
||||
} else {
|
||||
shaderText.visibility = View.GONE
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
// Handle any native library exceptions gracefully
|
||||
fpsText.text = "-- FPS"
|
||||
speedText.text = "--%"
|
||||
frameTimeText.text = "-- ms"
|
||||
}
|
||||
}
|
||||
|
||||
fun startUpdates() {
|
||||
isRunning = true
|
||||
handler.post(updateRunnable)
|
||||
}
|
||||
|
||||
fun stopUpdates() {
|
||||
isRunning = false
|
||||
handler.removeCallbacks(updateRunnable)
|
||||
}
|
||||
|
||||
override fun onStop() {
|
||||
super.onStop()
|
||||
stopUpdates()
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val TAG = "ThorPresentation"
|
||||
|
||||
/**
|
||||
* Check if the device has a secondary display (like AYN Thor)
|
||||
*/
|
||||
fun hasSecondaryDisplay(context: Context): Boolean {
|
||||
val displayManager = context.getSystemService(Context.DISPLAY_SERVICE) as DisplayManager
|
||||
val displays = displayManager.displays
|
||||
return displays.size > 1
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the secondary display for Thor presentation
|
||||
*/
|
||||
fun getSecondaryDisplay(context: Context): Display? {
|
||||
val displayManager = context.getSystemService(Context.DISPLAY_SERVICE) as DisplayManager
|
||||
val displays = displayManager.displays
|
||||
// Find the first non-default display
|
||||
return displays.firstOrNull { it.displayId != Display.DEFAULT_DISPLAY }
|
||||
}
|
||||
|
||||
/**
|
||||
* Create and show the Thor presentation on the secondary display
|
||||
*/
|
||||
fun createIfAvailable(context: Context): ThorPresentation? {
|
||||
val secondaryDisplay = getSecondaryDisplay(context) ?: return null
|
||||
return ThorPresentation(context, secondaryDisplay)
|
||||
}
|
||||
}
|
||||
}
|
||||
301
src/android/app/src/main/res/layout/thor_presentation.xml
Normal file
301
src/android/app/src/main/res/layout/thor_presentation.xml
Normal file
@@ -0,0 +1,301 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- PROJECT THOR: Dual Screen Dashboard Layout for AYN Thor -->
|
||||
<!-- This layout is displayed on the secondary bottom screen during emulation -->
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="#CC000000"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical"
|
||||
android:padding="16dp">
|
||||
|
||||
<!-- Title Bar -->
|
||||
<TextView
|
||||
android:id="@+id/thor_title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/thor_dashboard_title"
|
||||
android:textColor="#FFFFFF"
|
||||
android:textSize="14sp"
|
||||
android:textStyle="bold"
|
||||
android:layout_marginBottom="12dp" />
|
||||
|
||||
<!-- Main Stats Row -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center"
|
||||
android:layout_marginBottom="8dp">
|
||||
|
||||
<!-- FPS -->
|
||||
<LinearLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:orientation="vertical"
|
||||
android:gravity="center">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/thor_fps"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="60.0 FPS"
|
||||
android:textColor="#00FF00"
|
||||
android:textSize="24sp"
|
||||
android:textStyle="bold"
|
||||
tools:text="60.0 FPS" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/thor_fps_label"
|
||||
android:textColor="#888888"
|
||||
android:textSize="10sp" />
|
||||
</LinearLayout>
|
||||
|
||||
<!-- Divider -->
|
||||
<View
|
||||
android:layout_width="1dp"
|
||||
android:layout_height="40dp"
|
||||
android:background="#444444" />
|
||||
|
||||
<!-- Speed -->
|
||||
<LinearLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:orientation="vertical"
|
||||
android:gravity="center">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/thor_speed"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="100%"
|
||||
android:textColor="#FFFFFF"
|
||||
android:textSize="24sp"
|
||||
android:textStyle="bold"
|
||||
tools:text="100%" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/thor_speed_label"
|
||||
android:textColor="#888888"
|
||||
android:textSize="10sp" />
|
||||
</LinearLayout>
|
||||
|
||||
<!-- Divider -->
|
||||
<View
|
||||
android:layout_width="1dp"
|
||||
android:layout_height="40dp"
|
||||
android:background="#444444" />
|
||||
|
||||
<!-- Frame Time -->
|
||||
<LinearLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:orientation="vertical"
|
||||
android:gravity="center">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/thor_frame_time"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="16.67 ms"
|
||||
android:textColor="#FFFFFF"
|
||||
android:textSize="24sp"
|
||||
android:textStyle="bold"
|
||||
tools:text="16.67 ms" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/thor_frame_time_label"
|
||||
android:textColor="#888888"
|
||||
android:textSize="10sp" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
<!-- Shader Compilation Status (shown only when compiling) -->
|
||||
<TextView
|
||||
android:id="@+id/thor_shader_status"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/thor_shaders_building"
|
||||
android:textColor="#FFAA00"
|
||||
android:textSize="12sp"
|
||||
android:textStyle="italic"
|
||||
android:visibility="gone"
|
||||
android:layout_marginBottom="8dp"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<!-- Divider Line -->
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:background="#444444"
|
||||
android:layout_marginVertical="8dp" />
|
||||
|
||||
<!-- Settings Row -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center">
|
||||
|
||||
<!-- Resolution -->
|
||||
<LinearLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:orientation="vertical"
|
||||
android:gravity="center">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/thor_resolution"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="1.5X"
|
||||
android:textColor="#00AAFF"
|
||||
android:textSize="16sp"
|
||||
android:textStyle="bold"
|
||||
tools:text="1.5X (1080p)" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/thor_resolution_label"
|
||||
android:textColor="#888888"
|
||||
android:textSize="10sp" />
|
||||
</LinearLayout>
|
||||
|
||||
<!-- Docked State -->
|
||||
<LinearLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:orientation="vertical"
|
||||
android:gravity="center">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/thor_docked"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Docked"
|
||||
android:textColor="#00AAFF"
|
||||
android:textSize="16sp"
|
||||
android:textStyle="bold"
|
||||
tools:text="Docked" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/thor_mode_label"
|
||||
android:textColor="#888888"
|
||||
android:textSize="10sp" />
|
||||
</LinearLayout>
|
||||
|
||||
<!-- CPU Accuracy -->
|
||||
<LinearLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:orientation="vertical"
|
||||
android:gravity="center">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/thor_cpu_accuracy"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Auto"
|
||||
android:textColor="#00AAFF"
|
||||
android:textSize="16sp"
|
||||
android:textStyle="bold"
|
||||
tools:text="Auto" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/thor_cpu_label"
|
||||
android:textColor="#888888"
|
||||
android:textSize="10sp" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
<!-- Second Settings Row -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center"
|
||||
android:layout_marginTop="8dp">
|
||||
|
||||
<!-- GPU Driver -->
|
||||
<LinearLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:orientation="vertical"
|
||||
android:gravity="center">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/thor_gpu_driver"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Turnip"
|
||||
android:textColor="#AA88FF"
|
||||
android:textSize="14sp"
|
||||
android:textStyle="bold"
|
||||
android:maxLines="1"
|
||||
android:ellipsize="end"
|
||||
tools:text="Turnip v24.1" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/thor_gpu_driver_label"
|
||||
android:textColor="#888888"
|
||||
android:textSize="10sp" />
|
||||
</LinearLayout>
|
||||
|
||||
<!-- ASTC Mode -->
|
||||
<LinearLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:orientation="vertical"
|
||||
android:gravity="center">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/thor_astc_mode"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Native"
|
||||
android:textColor="#AA88FF"
|
||||
android:textSize="14sp"
|
||||
android:textStyle="bold"
|
||||
tools:text="Native" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/thor_astc_label"
|
||||
android:textColor="#888888"
|
||||
android:textSize="10sp" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
<!-- Citron Branding -->
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/thor_branding"
|
||||
android:textColor="#444444"
|
||||
android:textSize="10sp"
|
||||
android:layout_marginTop="12dp" />
|
||||
|
||||
</LinearLayout>
|
||||
@@ -1325,4 +1325,18 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
<string name="crt_mask_aperture">Aperture Grille</string>
|
||||
<string name="crt_mask_shadow">Shadow Mask</string>
|
||||
|
||||
<!-- PROJECT THOR: Dual Screen Dashboard Strings -->
|
||||
<string name="thor_dashboard_title">CITRON PERFORMANCE DASHBOARD</string>
|
||||
<string name="thor_fps_label">Frame Rate</string>
|
||||
<string name="thor_speed_label">Speed</string>
|
||||
<string name="thor_frame_time_label">Frame Time</string>
|
||||
<string name="thor_resolution_label">Resolution</string>
|
||||
<string name="thor_mode_label">Mode</string>
|
||||
<string name="thor_cpu_label">CPU</string>
|
||||
<string name="thor_gpu_driver_label">GPU Driver</string>
|
||||
<string name="thor_astc_label">ASTC</string>
|
||||
<string name="thor_shaders_building">Compiling Shaders…</string>
|
||||
<string name="thor_branding">Powered by Citron</string>
|
||||
<string name="thor_secondary_display_detected">Secondary display detected - Dashboard enabled</string>
|
||||
|
||||
</resources>
|
||||
|
||||
Reference in New Issue
Block a user