Skip to content

Commit

Permalink
Add splash screen
Browse files Browse the repository at this point in the history
  • Loading branch information
Rawa committed Jul 1, 2024
1 parent 4ac1cae commit 227144b
Show file tree
Hide file tree
Showing 12 changed files with 250 additions and 15 deletions.
4 changes: 2 additions & 2 deletions android/app/src/debug/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@
tools:ignore="ScopedStorage" />
<application android:icon="@mipmap/ic_launcher"
android:roundIcon="@mipmap/ic_launcher"
android:theme="@style/AppTheme"
android:theme="@style/Theme.App.Starting"
android:extractNativeLibs="true"
android:allowBackup="false"
android:banner="@drawable/banner"
android:name=".MullvadApplication"
tools:ignore="DataExtractionRules,GoogleAppIndexingWarning"></application>
tools:ignore="DataExtractionRules,GoogleAppIndexingWarning"/>
</manifest>
2 changes: 1 addition & 1 deletion android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher"
android:theme="@style/AppTheme"
android:theme="@style/Theme.App.Starting"
tools:ignore="GoogleAppIndexingWarning">
<!--
MainActivity
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ import com.ramcosta.composedestinations.generated.destinations.PrivacyDisclaimer
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
import net.mullvad.mullvadvpn.R
import net.mullvad.mullvadvpn.compose.component.ScaffoldWithTopBar
import net.mullvad.mullvadvpn.compose.transitions.DefaultTransition
import net.mullvad.mullvadvpn.compose.util.CollectSideEffectWithLifecycle
import net.mullvad.mullvadvpn.lib.theme.AppTheme
import net.mullvad.mullvadvpn.lib.theme.Dimens
Expand All @@ -45,7 +44,7 @@ private fun PreviewLoadingScreen() {
}

// Set this as the start destination of the default nav graph
@Destination<RootGraph>(start = true, style = DefaultTransition::class)
@Destination<RootGraph>(start = true)
@Composable
fun Splash(navigator: DestinationsNavigator) {
val viewModel: SplashViewModel = koinViewModel()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import net.mullvad.mullvadvpn.repository.RelayListFilterRepository
import net.mullvad.mullvadvpn.repository.RelayListRepository
import net.mullvad.mullvadvpn.repository.RelayOverridesRepository
import net.mullvad.mullvadvpn.repository.SettingsRepository
import net.mullvad.mullvadvpn.repository.SplashCompleteRepository
import net.mullvad.mullvadvpn.repository.SplitTunnelingRepository
import net.mullvad.mullvadvpn.ui.serviceconnection.AppVersionInfoRepository
import net.mullvad.mullvadvpn.ui.serviceconnection.ServiceConnectionManager
Expand Down Expand Up @@ -124,6 +125,7 @@ val uiModule = module {
single { SplitTunnelingRepository(get()) }
single { ApiAccessRepository(get()) }
single { NewDeviceRepository() }
single { SplashCompleteRepository() }

single { AccountExpiryNotificationUseCase(get()) }
single { TunnelStateNotificationUseCase(get()) }
Expand Down Expand Up @@ -188,7 +190,7 @@ val uiModule = module {
viewModel { PrivacyDisclaimerViewModel(get(), IS_PLAY_BUILD) }
viewModel { SelectLocationViewModel(get(), get(), get(), get(), get(), get()) }
viewModel { SettingsViewModel(get(), get(), IS_PLAY_BUILD) }
viewModel { SplashViewModel(get(), get(), get()) }
viewModel { SplashViewModel(get(), get(), get(), get()) }
viewModel { VoucherDialogViewModel(get()) }
viewModel { VpnSettingsViewModel(get(), get(), get()) }
viewModel { WelcomeViewModel(get(), get(), get(), get(), isPlayBuild = IS_PLAY_BUILD) }
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package net.mullvad.mullvadvpn.repository

class SplashCompleteRepository {
private var splashComplete = false

fun isSplashComplete() = splashComplete

fun onSplashCompleted() {
splashComplete = true
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.result.contract.ActivityResultContracts
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
import androidx.core.view.WindowCompat
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
Expand All @@ -17,6 +18,7 @@ import net.mullvad.mullvadvpn.lib.common.util.SdkUtils.requestNotificationPermis
import net.mullvad.mullvadvpn.lib.intent.IntentProvider
import net.mullvad.mullvadvpn.lib.theme.AppTheme
import net.mullvad.mullvadvpn.repository.PrivacyDisclaimerRepository
import net.mullvad.mullvadvpn.repository.SplashCompleteRepository
import net.mullvad.mullvadvpn.ui.serviceconnection.ServiceConnectionManager
import net.mullvad.mullvadvpn.viewmodel.NoDaemonViewModel
import org.koin.android.ext.android.getKoin
Expand All @@ -31,6 +33,8 @@ class MainActivity : ComponentActivity() {

private lateinit var privacyDisclaimerRepository: PrivacyDisclaimerRepository
private lateinit var serviceConnectionManager: ServiceConnectionManager
private lateinit var splashCompleteRepository: SplashCompleteRepository
private var isReadyNextDraw: Boolean = false
private lateinit var noDaemonViewModel: NoDaemonViewModel
private lateinit var intentProvider: IntentProvider

Expand All @@ -45,16 +49,21 @@ class MainActivity : ComponentActivity() {
serviceConnectionManager = get()
noDaemonViewModel = get()
intentProvider = get()
splashCompleteRepository = get()
}
lifecycle.addObserver(noDaemonViewModel)

installSplashScreen().setKeepOnScreenCondition {
val isReady = isReadyNextDraw
isReadyNextDraw = splashCompleteRepository.isSplashComplete()
!isReady
}
super.onCreate(savedInstanceState)

// Needs to be before set content since we want to access the intent in compose
if (savedInstanceState == null) {
intentProvider.setStartIntent(intent)
}

setContent { AppTheme { MullvadApp() } }

// This is to protect against tapjacking attacks
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package net.mullvad.mullvadvpn.viewmodel
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.async
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.flow
Expand All @@ -14,14 +16,24 @@ import net.mullvad.mullvadvpn.lib.model.DeviceState
import net.mullvad.mullvadvpn.lib.shared.AccountRepository
import net.mullvad.mullvadvpn.lib.shared.DeviceRepository
import net.mullvad.mullvadvpn.repository.PrivacyDisclaimerRepository
import net.mullvad.mullvadvpn.repository.SplashCompleteRepository

data class SplashScreenState(val splashComplete: Boolean = false)

class SplashViewModel(
private val privacyDisclaimerRepository: PrivacyDisclaimerRepository,
private val accountRepository: AccountRepository,
private val deviceRepository: DeviceRepository,
private val splashCompleteRepository: SplashCompleteRepository
) : ViewModel() {

val uiSideEffect = flow { emit(getStartDestination()) }
val uiSideEffect = flow {
emit(getStartDestination())
splashCompleteRepository.onSplashCompleted()
}

private val _uiState = MutableStateFlow<SplashScreenState>(SplashScreenState(false))
val uiState: StateFlow<SplashScreenState> = _uiState

private suspend fun getStartDestination(): SplashUiSideEffect {
if (!privacyDisclaimerRepository.hasAcceptedPrivacyDisclosure()) {
Expand Down
5 changes: 4 additions & 1 deletion android/lib/resource/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,7 @@ android {
}
}

dependencies { implementation(Dependencies.AndroidX.appcompat) }
dependencies {
implementation(Dependencies.AndroidX.appcompat)
implementation(Dependencies.AndroidX.coreSplashscreen)
}
Loading

0 comments on commit 227144b

Please sign in to comment.