Android: Show time played in game details

Unlike in DolphinQt, there isn't much space to show playtimes directly in
the game list, so I've put it in the game details dialog instead.
This commit is contained in:
JosJuice 2025-04-12 14:27:09 +02:00
parent 3eee52cb6b
commit b8e70df413
7 changed files with 83 additions and 6 deletions

View File

@ -153,6 +153,8 @@ dependencies {
// For loading custom GPU drivers
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.3")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3")
implementation("com.nononsenseapps:filepicker:4.2.1")
}

View File

@ -19,6 +19,7 @@ import kotlinx.coroutines.launch
import org.dolphinemu.dolphinemu.NativeLibrary
import org.dolphinemu.dolphinemu.databinding.DialogGameDetailsBinding
import org.dolphinemu.dolphinemu.databinding.DialogGameDetailsTvBinding
import org.dolphinemu.dolphinemu.features.settings.model.BooleanSetting
import org.dolphinemu.dolphinemu.model.GameFile
class GameDetailsDialog : DialogFragment() {
@ -35,6 +36,21 @@ class GameDetailsDialog : DialogFragment() {
if (requireActivity() is AppCompatActivity) {
binding = DialogGameDetailsBinding.inflate(layoutInflater)
binding.apply {
if (BooleanSetting.MAIN_TIME_TRACKING.boolean) {
lifecycleScope.launch {
val totalMs = gameFile.getTimePlayedMs()
val totalMinutes = totalMs / 60000
val totalHours = totalMinutes / 60
textTimePlayed.text = resources.getString(
R.string.game_details_time_played,
totalHours,
totalMinutes % 60
)
}
} else {
textTimePlayed.visibility = View.GONE
}
textGameTitle.text = gameFile.getTitle()
textDescription.text = gameFile.getDescription()
if (gameFile.getDescription().isEmpty()) {
@ -87,6 +103,21 @@ class GameDetailsDialog : DialogFragment() {
} else {
tvBinding = DialogGameDetailsTvBinding.inflate(layoutInflater)
tvBinding.apply {
if (BooleanSetting.MAIN_TIME_TRACKING.boolean) {
lifecycleScope.launch {
val totalMs = gameFile.getTimePlayedMs()
val totalMinutes = totalMs / 60000
val totalHours = totalMinutes / 60
textTimePlayed.text = resources.getString(
R.string.game_details_time_played,
totalHours,
totalMinutes % 60
)
}
} else {
textTimePlayed.visibility = View.GONE
}
textGameTitle.text = gameFile.getTitle()
textDescription.text = gameFile.getDescription()
if (gameFile.getDescription().isEmpty()) {

View File

@ -3,6 +3,8 @@
package org.dolphinemu.dolphinemu.model
import androidx.annotation.Keep
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
@Keep
class GameFile private constructor(private val pointer: Long) {
@ -54,6 +56,15 @@ class GameFile private constructor(private val pointer: Long) {
external fun getBannerHeight(): Int
suspend fun getTimePlayedMs(): Long {
// getTimePlayedMsInternal reads from disk, so let's use coroutines.
return withContext(Dispatchers.IO) {
getTimePlayedMsInternal()
}
}
external private fun getTimePlayedMsInternal(): Long
val customCoverPath: String
get() = "${getPath().substring(0, getPath().lastIndexOf("."))}.cover.png"

View File

@ -42,12 +42,23 @@
android:id="@+id/banner"
android:layout_width="144dp"
android:layout_height="48dp"
android:layout_marginTop="20dp"
android:layout_marginBottom="16dp"
android:layout_marginTop="16dp"
tools:src="@drawable/no_banner"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/text_description" />
<TextView
android:id="@+id/text_time_played"
style="@android:style/TextAppearance.Material.Caption"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:textAlignment="viewStart"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/banner"
tools:text="Played for 1h 23m" />
<com.google.android.material.divider.MaterialDivider
android:id="@+id/divider_1"
android:layout_width="0dp"
@ -55,7 +66,7 @@
android:layout_marginTop="24dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/banner" />
app:layout_constraintTop_toBottomOf="@id/text_time_played" />
<TextView
android:id="@+id/label_country"

View File

@ -44,12 +44,24 @@
android:id="@+id/banner"
android:layout_width="144dp"
android:layout_height="48dp"
android:layout_marginTop="20dp"
android:layout_marginBottom="16dp"
android:layout_marginTop="16dp"
tools:src="@drawable/no_banner"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/text_description" />
<TextView
android:id="@+id/text_time_played"
style="@android:style/TextAppearance.Material.Caption"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:textColor="@color/dolphin_onSurface"
android:textAlignment="viewStart"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/banner"
tools:text="Played for 1h 23m" />
<View
android:id="@+id/divider_1"
android:layout_width="0dp"
@ -58,7 +70,7 @@
android:background="@color/dolphin_onSurfaceVariant"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/banner" />
app:layout_constraintTop_toBottomOf="@id/text_time_played" />
<TextView
android:id="@+id/label_country"

View File

@ -519,6 +519,7 @@
<string name="game_ini_junk_question">The settings file for this game contains extraneous data added by an old version of Dolphin. This will likely prevent global settings from working as intended.\n\nWould you like to fix this by deleting the settings file for this game? All game-specific settings and cheats that you have added will be removed. This cannot be undone.</string>
<!-- Game Details Screen -->
<string name="game_details_time_played">Played for %1$dh %2$dm</string>
<string name="game_details_country">Country</string>
<string name="game_details_company">Company</string>
<string name="game_details_game_id">Game ID</string>

View File

@ -3,12 +3,14 @@
#include "jni/GameList/GameFile.h"
#include <chrono>
#include <memory>
#include <utility>
#include <vector>
#include <jni.h>
#include "Core/TimePlayed.h"
#include "DiscIO/Blob.h"
#include "DiscIO/Enums.h"
#include "UICommon/GameFile.h"
@ -190,6 +192,13 @@ JNIEXPORT jint JNICALL Java_org_dolphinemu_dolphinemu_model_GameFile_getBannerHe
return static_cast<jint>(GetRef(env, obj)->GetBannerImage().height);
}
JNIEXPORT jlong JNICALL
Java_org_dolphinemu_dolphinemu_model_GameFile_getTimePlayedMsInternal(JNIEnv* env, jobject obj)
{
const std::chrono::milliseconds time = TimePlayed().GetTimePlayed(GetRef(env, obj)->GetGameID());
return time.count();
}
JNIEXPORT jobject JNICALL Java_org_dolphinemu_dolphinemu_model_GameFile_parse(JNIEnv* env, jclass,
jstring path)
{