diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index cc5d8fdf..23372cbe 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -56,22 +56,16 @@
android:exported="false" />
+ android:exported="true" >
-
-
diff --git a/app/src/main/java/akio/apps/myrun/feature/main/MainActivity.kt b/app/src/main/java/akio/apps/myrun/feature/main/MainActivity.kt
index 8cd52511..b9a7cd84 100644
--- a/app/src/main/java/akio/apps/myrun/feature/main/MainActivity.kt
+++ b/app/src/main/java/akio/apps/myrun/feature/main/MainActivity.kt
@@ -2,8 +2,15 @@ package akio.apps.myrun.feature.main
import akio.apps.myrun.R
import akio.apps.myrun.data.activity.api.model.BaseActivityModel
+import akio.apps.myrun.data.authentication.api.model.SignInSuccessResult
import akio.apps.myrun.feature.activitydetail.ActivityExportService
+import akio.apps.myrun.feature.core.DialogDelegate
+import akio.apps.myrun.feature.core.ktx.getParcelableExtraExt
+import akio.apps.myrun.feature.core.ktx.lazyViewModelProvider
+import akio.apps.myrun.feature.core.navigation.OnBoardingNavigation
+import akio.apps.myrun.feature.main.di.DaggerMainActivityComponent
import akio.apps.myrun.feature.main.ui.MainNavHost
+import akio.apps.myrun.feature.registration.SignInActivity
import akio.apps.myrun.feature.route.RoutePlanningFacade
import akio.apps.myrun.feature.tracking.LocationPermissionChecker
import akio.apps.myrun.feature.tracking.RouteTrackingActivity
@@ -14,23 +21,85 @@ import android.widget.Toast
import androidx.activity.compose.setContent
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat
+import androidx.core.splashscreen.SplashScreen
+import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
import androidx.core.view.WindowCompat
import androidx.lifecycle.lifecycleScope
+import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
+import timber.log.Timber
class MainActivity : AppCompatActivity() {
private val locationPermissionChecker: LocationPermissionChecker =
LocationPermissionChecker(activity = this)
+ private val mainViewModel: MainViewModel by lazyViewModelProvider {
+ DaggerMainActivityComponent.factory().create(application).mainViewModel()
+ }
+
+ private val dialogDelegate by lazy { DialogDelegate(this) }
+
+ private lateinit var splashScreen: SplashScreen
+ private var splashStartTime: Long = 0
+
override fun onCreate(savedInstanceState: Bundle?) {
+ splashScreen = installSplashScreen().apply {
+ setKeepOnScreenCondition { true }
+ }
+ splashStartTime = System.currentTimeMillis()
super.onCreate(savedInstanceState)
WindowCompat.setDecorFitsSystemWindows(window, false)
+
+ lifecycleScope.launch {
+ mainViewModel.isUserSignedIn.collect(::onUserSignIn)
+ }
+ dialogDelegate.collectLaunchCatchingError(this, mainViewModel)
+ }
+
+ @Suppress("DEPRECATION")
+ override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
+ super.onActivityResult(requestCode, resultCode, data)
+ when (requestCode) {
+ RC_SIGN_IN -> verifySignInResult(resultCode, data)
+ }
+ }
+
+ private fun verifySignInResult(resultCode: Int, data: Intent?) {
+ if (resultCode == RESULT_CANCELED || data == null) {
+ finish()
+ return
+ }
+
+ data.getParcelableExtraExt(SignInActivity.RESULT_SIGN_RESULT_DATA)
+ ?: return
+
+ goHome()
+ }
+
+ private fun onUserSignIn(isSignedIn: Boolean) = lifecycleScope.launch {
+ Timber.d("onUserSignIn $isSignedIn")
+ val delayMore = SPLASH_MIN_SHOWTIME - (System.currentTimeMillis() - splashStartTime)
+ if (delayMore > 0) {
+ delay(delayMore)
+ }
+ if (isSignedIn) {
+ splashScreen.setKeepOnScreenCondition { false }
+ goHome()
+ } else {
+ val intent = OnBoardingNavigation.createSignInIntent(this@MainActivity)
+ ?: return@launch
+ @Suppress("DEPRECATION")
+ startActivityForResult(intent, RC_SIGN_IN)
+ }
+ }
+
+ private fun goHome() {
setContent {
MainNavHost(
onClickFloatingActionButton = ::openRouteTrackingOrCheckRequiredPermission,
onClickExportActivityFile = ::startActivityExportService
- ) { RoutePlanningFacade.startRoutePlanning(this) }
+ ) { RoutePlanningFacade.startRoutePlanning(this@MainActivity) }
}
}
@@ -73,5 +142,8 @@ class MainActivity : AppCompatActivity() {
companion object {
fun clearTaskIntent(context: Context): Intent = Intent(context, MainActivity::class.java)
.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK)
+
+ private const val SPLASH_MIN_SHOWTIME = 700
+ private const val RC_SIGN_IN = 1
}
}
diff --git a/app/src/main/java/akio/apps/myrun/feature/splash/SplashViewModel.kt b/app/src/main/java/akio/apps/myrun/feature/main/MainViewModel.kt
similarity index 89%
rename from app/src/main/java/akio/apps/myrun/feature/splash/SplashViewModel.kt
rename to app/src/main/java/akio/apps/myrun/feature/main/MainViewModel.kt
index d5c10a96..492f5bfb 100644
--- a/app/src/main/java/akio/apps/myrun/feature/splash/SplashViewModel.kt
+++ b/app/src/main/java/akio/apps/myrun/feature/main/MainViewModel.kt
@@ -1,4 +1,4 @@
-package akio.apps.myrun.feature.splash
+package akio.apps.myrun.feature.main
import akio.apps.myrun.data.authentication.api.UserAuthenticationState
import akio.apps.myrun.feature.core.launchcatching.LaunchCatchingDelegate
@@ -8,7 +8,7 @@ import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.flow
-class SplashViewModel @Inject constructor(
+class MainViewModel @Inject constructor(
private val userAuthenticationState: UserAuthenticationState,
private val launchCatchingDelegate: LaunchCatchingDelegate,
) : ViewModel(), LaunchCatchingDelegate by launchCatchingDelegate {
diff --git a/app/src/main/java/akio/apps/myrun/feature/main/di/HomeTabFeatureComponent.kt b/app/src/main/java/akio/apps/myrun/feature/main/di/HomeTabComponent.kt
similarity index 91%
rename from app/src/main/java/akio/apps/myrun/feature/main/di/HomeTabFeatureComponent.kt
rename to app/src/main/java/akio/apps/myrun/feature/main/di/HomeTabComponent.kt
index 4978e1f5..09832857 100644
--- a/app/src/main/java/akio/apps/myrun/feature/main/di/HomeTabFeatureComponent.kt
+++ b/app/src/main/java/akio/apps/myrun/feature/main/di/HomeTabComponent.kt
@@ -10,7 +10,7 @@ import dagger.Component
@FeatureScope
@Component(dependencies = [TrackingDataComponent::class])
-interface HomeTabFeatureComponent {
+interface HomeTabComponent {
fun homeTabViewModel(): HomeTabViewModel
@Component.Factory
@@ -19,6 +19,6 @@ interface HomeTabFeatureComponent {
@BindsInstance application: Application,
trackingDataComponent: TrackingDataComponent =
DaggerTrackingDataComponent.factory().create(application),
- ): HomeTabFeatureComponent
+ ): HomeTabComponent
}
}
diff --git a/app/src/main/java/akio/apps/myrun/feature/splash/di/SplashFeatureComponent.kt b/app/src/main/java/akio/apps/myrun/feature/main/di/MainActivityComponent.kt
similarity index 78%
rename from app/src/main/java/akio/apps/myrun/feature/splash/di/SplashFeatureComponent.kt
rename to app/src/main/java/akio/apps/myrun/feature/main/di/MainActivityComponent.kt
index e96e8582..6530ac36 100644
--- a/app/src/main/java/akio/apps/myrun/feature/splash/di/SplashFeatureComponent.kt
+++ b/app/src/main/java/akio/apps/myrun/feature/main/di/MainActivityComponent.kt
@@ -1,10 +1,10 @@
-package akio.apps.myrun.feature.splash.di
+package akio.apps.myrun.feature.main.di
import akio.apps.myrun.base.di.FeatureScope
import akio.apps.myrun.data.authentication.di.AuthenticationDataComponent
import akio.apps.myrun.data.authentication.di.DaggerAuthenticationDataComponent
import akio.apps.myrun.feature.core.launchcatching.LaunchCatchingModule
-import akio.apps.myrun.feature.splash.SplashViewModel
+import akio.apps.myrun.feature.main.MainViewModel
import android.app.Application
import dagger.BindsInstance
import dagger.Component
@@ -16,8 +16,8 @@ import dagger.Component
],
dependencies = [AuthenticationDataComponent::class]
)
-interface SplashFeatureComponent {
- fun splashViewModel(): SplashViewModel
+interface MainActivityComponent {
+ fun mainViewModel(): MainViewModel
@Component.Factory
interface Factory {
@@ -25,6 +25,6 @@ interface SplashFeatureComponent {
@BindsInstance application: Application,
authenticationDataComponent: AuthenticationDataComponent =
DaggerAuthenticationDataComponent.factory().create(application),
- ): SplashFeatureComponent
+ ): MainActivityComponent
}
}
diff --git a/app/src/main/java/akio/apps/myrun/feature/main/ui/HomeTabComposable.kt b/app/src/main/java/akio/apps/myrun/feature/main/ui/HomeTabComposable.kt
index c31de1cb..a46e1a2a 100644
--- a/app/src/main/java/akio/apps/myrun/feature/main/ui/HomeTabComposable.kt
+++ b/app/src/main/java/akio/apps/myrun/feature/main/ui/HomeTabComposable.kt
@@ -11,7 +11,7 @@ import akio.apps.myrun.feature.core.ui.AppTheme
import akio.apps.myrun.feature.core.ui.NavigationBarSpacer
import akio.apps.myrun.feature.feed.ui.ActivityFeedComposable
import akio.apps.myrun.feature.main.HomeTabViewModel
-import akio.apps.myrun.feature.main.di.DaggerHomeTabFeatureComponent
+import akio.apps.myrun.feature.main.di.DaggerHomeTabComponent
import akio.apps.myrun.feature.userstats.ui.CurrentUserStatsComposable
import android.app.Application
import androidx.annotation.StringRes
@@ -147,7 +147,7 @@ fun HomeTabComposable(
private fun rememberViewModel(): HomeTabViewModel {
val application = LocalContext.current.applicationContext as Application
return remember {
- DaggerHomeTabFeatureComponent.factory().create(application).homeTabViewModel()
+ DaggerHomeTabComponent.factory().create(application).homeTabViewModel()
}
}
diff --git a/app/src/main/java/akio/apps/myrun/feature/splash/SplashActivity.kt b/app/src/main/java/akio/apps/myrun/feature/splash/SplashActivity.kt
deleted file mode 100644
index b5d5293f..00000000
--- a/app/src/main/java/akio/apps/myrun/feature/splash/SplashActivity.kt
+++ /dev/null
@@ -1,90 +0,0 @@
-package akio.apps.myrun.feature.splash
-
-import akio.apps.myrun.data.authentication.api.model.SignInSuccessResult
-import akio.apps.myrun.feature.core.DialogDelegate
-import akio.apps.myrun.feature.core.ktx.collectRepeatOnStarted
-import akio.apps.myrun.feature.core.ktx.getParcelableExtraExt
-import akio.apps.myrun.feature.core.ktx.lazyViewModelProvider
-import akio.apps.myrun.feature.core.navigation.OnBoardingNavigation
-import akio.apps.myrun.feature.main.MainActivity
-import akio.apps.myrun.feature.registration.SignInActivity
-import akio.apps.myrun.feature.splash.di.DaggerSplashFeatureComponent
-import android.content.Intent
-import android.os.Bundle
-import androidx.appcompat.app.AppCompatActivity
-import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
-import androidx.lifecycle.lifecycleScope
-import kotlinx.coroutines.delay
-import kotlinx.coroutines.launch
-import timber.log.Timber
-
-class SplashActivity : AppCompatActivity() {
-
- private val splashViewModel: SplashViewModel by lazyViewModelProvider {
- DaggerSplashFeatureComponent.factory().create(application).splashViewModel()
- }
-
- private val dialogDelegate by lazy { DialogDelegate(this) }
-
- private val splashStartTime: Long = System.currentTimeMillis()
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
-
- installSplashScreen().setKeepOnScreenCondition { true }
- initObservers()
- }
-
- private fun initObservers() {
- dialogDelegate.collectLaunchCatchingError(this, splashViewModel)
- collectRepeatOnStarted(splashViewModel.isUserSignedIn, ::onUserSignIn)
- }
-
- private fun onUserSignIn(isSignedIn: Boolean) = lifecycleScope.launch {
- Timber.d("onUserSignIn $isSignedIn")
- val delayMore = SPLASH_MIN_SHOWTIME - (System.currentTimeMillis() - splashStartTime)
- if (delayMore > 0) {
- delay(delayMore)
- }
- if (isSignedIn) {
- goHome()
- } else {
- val intent = OnBoardingNavigation.createSignInIntent(this@SplashActivity)
- ?: return@launch
- @Suppress("DEPRECATION")
- startActivityForResult(intent, RC_SIGN_IN)
- }
- }
-
- @Suppress("DEPRECATION")
- override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
- super.onActivityResult(requestCode, resultCode, data)
- when (requestCode) {
- RC_SIGN_IN -> verifySignInResult(resultCode, data)
- RC_ON_BOARDING -> goHome()
- }
- }
-
- private fun goHome() {
- startActivity(MainActivity.clearTaskIntent(this))
- }
-
- private fun verifySignInResult(resultCode: Int, data: Intent?) {
- if (resultCode == RESULT_CANCELED || data == null) {
- finish()
- return
- }
-
- data.getParcelableExtraExt(SignInActivity.RESULT_SIGN_RESULT_DATA)
- ?: return
-
- goHome()
- }
-
- companion object {
- private const val RC_SIGN_IN = 1
- private const val RC_ON_BOARDING = 2
-
- private const val SPLASH_MIN_SHOWTIME = 700
- }
-}
diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml
index 15f29d62..3997a0ac 100644
--- a/app/src/main/res/values/themes.xml
+++ b/app/src/main/res/values/themes.xml
@@ -28,6 +28,6 @@
diff --git a/app/src/test/java/akio/apps/myrun/feature/core/navigation/OnBoardingNavigationTest.kt b/app/src/test/java/akio/apps/myrun/feature/core/navigation/OnBoardingNavigationTest.kt
index c7dfb921..15b4fd34 100644
--- a/app/src/test/java/akio/apps/myrun/feature/core/navigation/OnBoardingNavigationTest.kt
+++ b/app/src/test/java/akio/apps/myrun/feature/core/navigation/OnBoardingNavigationTest.kt
@@ -1,9 +1,7 @@
package akio.apps.myrun.feature.core.navigation
import akio.apps.myrun.feature.core.navigation.OnBoardingNavigation.SIGNIN_ACTIVITY_CLASS_NAME
-import akio.apps.myrun.feature.core.navigation.OnBoardingNavigation.SPLASH_ACTIVITY_CLASS_NAME
import akio.apps.myrun.feature.registration.SignInActivity
-import akio.apps.myrun.feature.splash.SplashActivity
import kotlin.test.assertEquals
import org.junit.Test
@@ -15,12 +13,4 @@ class OnBoardingNavigationTest {
SignInActivity::class.java.name
)
}
-
- @Test
- fun testSplashActivityClassNameChangedCorrectly() {
- assertEquals(
- SPLASH_ACTIVITY_CLASS_NAME,
- SplashActivity::class.java.name
- )
- }
}
diff --git a/app/src/test/java/akio/apps/myrun/feature/splash/SplashViewModelTest.kt b/app/src/test/java/akio/apps/myrun/feature/splash/MainViewModelTest.kt
similarity index 81%
rename from app/src/test/java/akio/apps/myrun/feature/splash/SplashViewModelTest.kt
rename to app/src/test/java/akio/apps/myrun/feature/splash/MainViewModelTest.kt
index a2d836c4..26832934 100644
--- a/app/src/test/java/akio/apps/myrun/feature/splash/SplashViewModelTest.kt
+++ b/app/src/test/java/akio/apps/myrun/feature/splash/MainViewModelTest.kt
@@ -2,6 +2,7 @@ package akio.apps.myrun.feature.splash
import akio.apps.myrun.data.authentication.api.UserAuthenticationState
import akio.apps.myrun.feature.core.launchcatching.LaunchCatchingDelegate
+import akio.apps.myrun.feature.main.MainViewModel
import app.cash.turbine.test
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runTest
@@ -12,9 +13,9 @@ import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever
@OptIn(ExperimentalCoroutinesApi::class)
-class SplashViewModelTest {
+class MainViewModelTest {
- private lateinit var splashViewModel: SplashViewModel
+ private lateinit var mainViewModel: MainViewModel
private lateinit var mockedUserAuthState: UserAuthenticationState
private lateinit var mockLaunchCatchingDelegate: LaunchCatchingDelegate
@@ -30,8 +31,8 @@ class SplashViewModelTest {
whenever(mockedUserAuthState.isSignedIn())
.then { throw userAuthStateError }
- splashViewModel = SplashViewModel(mockedUserAuthState, mockLaunchCatchingDelegate)
- splashViewModel.isUserSignedIn.test {
+ mainViewModel = MainViewModel(mockedUserAuthState, mockLaunchCatchingDelegate)
+ mainViewModel.isUserSignedIn.test {
awaitComplete()
verify(mockLaunchCatchingDelegate).setLaunchCatchingError(userAuthStateError)
}
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index f6f2324f..2d499206 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -24,7 +24,7 @@ workmanager = "2.8.0"
paging = "3.1.1"
room = "2.5.0"
lifecycle = "2.5.1"
-splashscreen = "1.0.0"
+splashscreen = "1.0.1"
androidx-annotation = "1.6.0"
multidex = "2.0.1"
androidx-coretesting = "2.2.0"