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:
parent
3eee52cb6b
commit
b8e70df413
|
@ -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")
|
||||
}
|
||||
|
||||
|
|
|
@ -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()) {
|
||||
|
|
|
@ -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"
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue