Skip to content

Commit

Permalink
Add option to view achievement list while running a ROM
Browse files Browse the repository at this point in the history
  • Loading branch information
rafaelvcaetano committed Aug 20, 2023
1 parent d45e1a7 commit a24a99f
Show file tree
Hide file tree
Showing 13 changed files with 335 additions and 43 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import me.magnum.melonds.impl.camera.DSiCameraSourceMultiplexer
import me.magnum.melonds.impl.camera.PhysicalDSiCameraSource
import me.magnum.melonds.impl.camera.StaticImageDSiCameraSource
import me.magnum.melonds.impl.emulator.AndroidEmulatorManager
import me.magnum.melonds.impl.emulator.EmulatorSession
import me.magnum.melonds.impl.emulator.LifecycleOwnerProvider
import me.magnum.melonds.impl.emulator.SramProvider
import me.magnum.melonds.impl.image.BitmapFactoryBitmapLoader
Expand Down Expand Up @@ -127,4 +128,10 @@ object EmulatorRuntimeModule {
cameraManagerMultiplexer,
)
}

@Provides
@ActivityRetainedScoped
fun provideEmulatorSession(): EmulatorSession {
return EmulatorSession()
}
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,31 @@
package me.magnum.melonds.impl.emulator

class EmulatorSession(
private var areRetroAchievementsEnabled: Boolean,
enabledRetroAchievementsHardcoreMode: Boolean,
) {
import me.magnum.melonds.domain.model.ConsoleType
import me.magnum.melonds.domain.model.Rom
import me.magnum.melonds.domain.model.retroachievements.GameAchievementData

var isRetroAchievementsHardcoreModeEnabled: Boolean = enabledRetroAchievementsHardcoreMode
class EmulatorSession {

var isRetroAchievementsHardcoreModeEnabled = false
private set

private var areRetroAchievementsEnabled = false
private var isRetroAchievementsIntegrationEnabled = false
private var sessionType: SessionType? = null

fun startSession(areRetroAchievementsEnabled: Boolean, isRetroAchievementsHardcoreModeEnabled: Boolean, sessionType: SessionType) {
this.areRetroAchievementsEnabled = areRetroAchievementsEnabled
this.isRetroAchievementsHardcoreModeEnabled = isRetroAchievementsHardcoreModeEnabled
this.sessionType = sessionType
}

fun reset() {
areRetroAchievementsEnabled = false
isRetroAchievementsHardcoreModeEnabled = false
isRetroAchievementsIntegrationEnabled = false
sessionType = null
}

fun updateRetroAchievementsSettings(areRetroAchievementsEnabled: Boolean, isHardcoreModeEnabled: Boolean): Boolean {
this.areRetroAchievementsEnabled = areRetroAchievementsEnabled

Expand All @@ -20,6 +38,14 @@ class EmulatorSession(
return true
}

fun updateRetroAchievementsIntegrationStatus(integrationStatus: GameAchievementData.IntegrationStatus) {
isRetroAchievementsIntegrationEnabled = integrationStatus == GameAchievementData.IntegrationStatus.ENABLED
}

fun areRetroAchievementsEnabled(): Boolean {
return areRetroAchievementsEnabled && isRetroAchievementsIntegrationEnabled
}

fun areSaveStatesAllowed(): Boolean {
// Cannot use save-states when RA hardcore is enabled
return !isRetroAchievementsHardcoreModeEnabled || !areRetroAchievementsEnabled
Expand All @@ -29,4 +55,13 @@ class EmulatorSession(
// Cannot use cheats when RA hardcore is enabled
return !isRetroAchievementsHardcoreModeEnabled || !areRetroAchievementsEnabled
}

fun currentSessionType(): SessionType? {
return sessionType
}

sealed class SessionType {
data class RomSession(val rom: Rom) : SessionType()
data class FirmwareSession(val consoleType: ConsoleType) : SessionType()
}
}
Original file line number Diff line number Diff line change
@@ -1,35 +1,28 @@
package me.magnum.melonds.ui.romdetails
package me.magnum.melonds.ui.common.viewmodel

import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Job
import kotlinx.coroutines.channels.BufferOverflow
import kotlinx.coroutines.ensureActive
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.launch
import me.magnum.melonds.domain.model.Rom
import me.magnum.melonds.domain.model.retroachievements.RAUserAchievement
import me.magnum.melonds.domain.repositories.RetroAchievementsRepository
import me.magnum.melonds.domain.repositories.SettingsRepository
import me.magnum.melonds.parcelables.RomParcelable
import me.magnum.melonds.ui.romdetails.model.RomAchievementsSummary
import me.magnum.melonds.ui.romdetails.model.RomRetroAchievementsUiState
import me.magnum.rcheevosapi.model.RAAchievement
import javax.inject.Inject

@HiltViewModel
class RomRetroAchievementsViewModel @Inject constructor(
abstract class RetroAchievementsViewModel (
private val retroAchievementsRepository: RetroAchievementsRepository,
private val settingsRepository: SettingsRepository,
private val savedStateHandle: SavedStateHandle,
) : ViewModel() {

private val rom by lazy {
savedStateHandle.get<RomParcelable>(RomDetailsActivity.KEY_ROM)!!.rom
}

private val _uiState = MutableStateFlow<RomRetroAchievementsUiState>(RomRetroAchievementsUiState.Loading)
val uiState by lazy {
loadAchievements()
Expand All @@ -39,19 +32,27 @@ class RomRetroAchievementsViewModel @Inject constructor(
private val _viewAchievementEvent = MutableSharedFlow<String>(extraBufferCapacity = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST)
val viewAchievementEvent = _viewAchievementEvent.asSharedFlow()

private var achievementLoadJob: Job? = null

protected abstract fun getRom(): Rom

private fun loadAchievements() {
viewModelScope.launch {
achievementLoadJob?.cancel()
achievementLoadJob = viewModelScope.launch {
if (retroAchievementsRepository.isUserAuthenticated()) {
val forHardcoreMode = settingsRepository.isRetroAchievementsHardcoreEnabled()
retroAchievementsRepository.getGameUserAchievements(rom.retroAchievementsHash, forHardcoreMode).fold(
retroAchievementsRepository.getGameUserAchievements(getRom().retroAchievementsHash, forHardcoreMode).fold(
onSuccess = { achievements ->
val sortedAchievements = achievements.sortedBy {
// Display unlocked achievements first
if (it.isUnlocked) 0 else 1
}
_uiState.value = RomRetroAchievementsUiState.Ready(sortedAchievements, buildAchievementsSummary(forHardcoreMode, sortedAchievements))
},
onFailure = { _uiState.value = RomRetroAchievementsUiState.AchievementLoadError },
onFailure = {
ensureActive()
_uiState.value = RomRetroAchievementsUiState.AchievementLoadError
},
)
} else {
_uiState.value = RomRetroAchievementsUiState.LoggedOut
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package me.magnum.melonds.ui.emulator
import android.content.Context
import android.content.Intent
import android.content.res.Configuration
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.view.KeyEvent
Expand All @@ -20,17 +21,26 @@ import androidx.compose.animation.core.LinearEasing
import androidx.compose.animation.core.animate
import androidx.compose.animation.core.tween
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.offset
import androidx.compose.material.LocalContentColor
import androidx.compose.material.MaterialTheme
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.onSizeChanged
import androidx.compose.ui.platform.LocalView
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Dialog
import androidx.compose.ui.window.DialogProperties
import androidx.compose.ui.window.DialogWindowProvider
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.net.toUri
import androidx.core.os.ConfigurationCompat
Expand Down Expand Up @@ -65,10 +75,10 @@ import me.magnum.melonds.domain.repositories.SettingsRepository
import me.magnum.melonds.extensions.insetsControllerCompat
import me.magnum.melonds.extensions.parcelable
import me.magnum.melonds.extensions.setLayoutOrientation
import me.magnum.melonds.impl.emulator.LifecycleOwnerProvider
import me.magnum.melonds.parcelables.RomInfoParcelable
import me.magnum.melonds.parcelables.RomParcelable
import me.magnum.melonds.ui.cheats.CheatsActivity
import me.magnum.melonds.impl.emulator.LifecycleOwnerProvider
import me.magnum.melonds.ui.emulator.input.FrontendInputHandler
import me.magnum.melonds.ui.emulator.input.INativeInputListener
import me.magnum.melonds.ui.emulator.input.InputProcessor
Expand All @@ -83,6 +93,7 @@ import me.magnum.melonds.ui.emulator.rewind.EdgeSpacingDecorator
import me.magnum.melonds.ui.emulator.rewind.RewindSaveStateAdapter
import me.magnum.melonds.ui.emulator.rewind.model.RewindWindow
import me.magnum.melonds.ui.emulator.rom.SaveStateListAdapter
import me.magnum.melonds.ui.emulator.ui.AchievementListUi
import me.magnum.melonds.ui.emulator.ui.AchievementPopupUi
import me.magnum.melonds.ui.emulator.ui.RAIntegrationEventUi
import me.magnum.melonds.ui.settings.SettingsActivity
Expand Down Expand Up @@ -198,6 +209,7 @@ class EmulatorActivity : AppCompatActivity() {
viewModel.rewindToState(it)
closeRewindWindow()
}
private val showAchievementList = mutableStateOf(false)
private var resumeEmulatorOnActivityResume = true
private var emulatorReady = false

Expand Down Expand Up @@ -314,6 +326,43 @@ class EmulatorActivity : AppCompatActivity() {
}
}
}

if (showAchievementList.value) {
Dialog(
properties = DialogProperties(usePlatformDefaultWidth = false),
onDismissRequest = {
viewModel.resumeEmulator()
showAchievementList.value = false
}
) {
(LocalView.current.parent as DialogWindowProvider).window.setDimAmount(0.8f)

val achievementsViewModel by viewModels<EmulatorRetroAchievementsViewModel>()
val achievementListState by achievementsViewModel.uiState.collectAsState()

LaunchedEffect(Unit) {
// Perform a load immediately so that the last achievement data is discarded. This is to ensure that the latest up-to-date data is displayed and
// that if the user has loaded a new ROM, then the achievements of the new ROM are loaded
achievementsViewModel.retryLoadAchievements()
achievementsViewModel.viewAchievementEvent.collectLatest {
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(it))
startActivity(intent)
}
}

// Force dark colors here because the background will be dark
MelonTheme(isDarkTheme = true) {
CompositionLocalProvider(LocalContentColor provides MaterialTheme.colors.onSurface) {
AchievementListUi(
modifier = Modifier.fillMaxSize(),
state = achievementListState,
onViewAchievement = achievementsViewModel::viewAchievement,
onRetry = achievementsViewModel::retryLoadAchievements,
)
}
}
}
}
}
}

Expand Down Expand Up @@ -404,6 +453,7 @@ class EmulatorActivity : AppCompatActivity() {
}
}
}
EmulatorUiEvent.ShowAchievementList -> showAchievementList.value = true
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package me.magnum.melonds.ui.emulator

import dagger.hilt.android.lifecycle.HiltViewModel
import me.magnum.melonds.domain.model.Rom
import me.magnum.melonds.domain.repositories.RetroAchievementsRepository
import me.magnum.melonds.domain.repositories.SettingsRepository
import me.magnum.melonds.impl.emulator.EmulatorSession
import me.magnum.melonds.ui.common.viewmodel.RetroAchievementsViewModel
import javax.inject.Inject

@HiltViewModel
class EmulatorRetroAchievementsViewModel @Inject constructor(
retroAchievementsRepository: RetroAchievementsRepository,
settingsRepository: SettingsRepository,
private val emulatorSession: EmulatorSession,
) : RetroAchievementsViewModel(retroAchievementsRepository, settingsRepository) {

override fun getRom(): Rom {
val romSession = emulatorSession.currentSessionType() as? EmulatorSession.SessionType.RomSession
if (romSession == null) {
error("Emulator must be running a ROM session")
}

return romSession.rom
}
}
Loading

0 comments on commit a24a99f

Please sign in to comment.