diff --git a/frontend/app/src/main/java/com/example/speechbuddy/AuthActivity.kt b/frontend/app/src/main/java/com/example/speechbuddy/AuthActivity.kt index 45d36c79..3b9b2b21 100644 --- a/frontend/app/src/main/java/com/example/speechbuddy/AuthActivity.kt +++ b/frontend/app/src/main/java/com/example/speechbuddy/AuthActivity.kt @@ -1,24 +1,30 @@ package com.example.speechbuddy import android.content.Intent +import android.os.Build import android.os.Bundle import android.view.MotionEvent import android.view.inputmethod.InputMethodManager import androidx.activity.compose.setContent import androidx.activity.viewModels +import androidx.annotation.RequiresApi +import androidx.lifecycle.lifecycleScope import com.example.speechbuddy.compose.SpeechBuddyAuth import com.example.speechbuddy.ui.SpeechBuddyTheme -import com.example.speechbuddy.viewmodel.DisplaySettingsViewModel +import com.example.speechbuddy.utils.ResponseCode import com.example.speechbuddy.viewmodel.LoginViewModel import dagger.hilt.android.AndroidEntryPoint +import kotlinx.coroutines.launch +import java.time.LocalDate @AndroidEntryPoint class AuthActivity : BaseActivity() { private val loginViewModel: LoginViewModel by viewModels() - private val displaySettingsViewModel: DisplaySettingsViewModel by viewModels() + @RequiresApi(Build.VERSION_CODES.O) override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) subscribeObservers() @@ -29,17 +35,44 @@ class AuthActivity : BaseActivity() { settingsRepository = settingsRepository, initialDarkMode = getInitialDarkMode() ) { - SpeechBuddyAuth() + SpeechBuddyAuth(isBackup = false) } } } + @RequiresApi(Build.VERSION_CODES.O) private fun subscribeObservers() { sessionManager.isAuthorized.observe(this) { isAuthorized -> - if (isAuthorized) navHomeActivity() + if (isAuthorized && + sessionManager.userId.value != GUEST && + sessionManager.isLogin.value != true && + getAutoBackup() && + getLastBackupDate() != LocalDate.now().toString() + ) { + autoBackup() + } else if (isAuthorized){ + navHomeActivity() + } } } + companion object { + const val GUEST = -1 + } + + @RequiresApi(Build.VERSION_CODES.O) + private fun autoBackup() { + setContent { + SpeechBuddyTheme( + settingsRepository = settingsRepository, + initialDarkMode = getInitialDarkMode() + ) { + SpeechBuddyAuth(isBackup = true) + } + } + displayBackup() + } + private fun navHomeActivity() { val intent = Intent(this, HomeActivity::class.java) startActivity(intent) @@ -47,7 +80,33 @@ class AuthActivity : BaseActivity() { } private fun getInitialDarkMode(): Boolean { - return displaySettingsViewModel.getDarkMode() + var darkMode = false + lifecycleScope.launch { + settingsRepository.getDarkMode().collect { + darkMode = it.data?: false + } + } + return darkMode + } + + private fun getAutoBackup(): Boolean { + var autoBackup = true + lifecycleScope.launch { + settingsRepository.getAutoBackup().collect { + autoBackup = it.data?: true + } + } + return autoBackup + } + + private fun getLastBackupDate(): String { + var lastBackupDate = "" + lifecycleScope.launch { + settingsRepository.getLastBackupDate().collect { + lastBackupDate = it.data?: "" + } + } + return lastBackupDate } private fun checkPreviousAuthUser() { @@ -67,4 +126,73 @@ class AuthActivity : BaseActivity() { return super.dispatchTouchEvent(event) } + @RequiresApi(Build.VERSION_CODES.O) + private fun displayBackup() { + lifecycleScope.launch { + settingsRepository.displayBackup().collect { result -> + when (result.code()) { + ResponseCode.SUCCESS.value -> { symbolListBackup() } + + ResponseCode.NO_INTERNET_CONNECTION.value -> { + sessionManager.setIsLogin(false) + navHomeActivity() + } + } + } + } + } + + @RequiresApi(Build.VERSION_CODES.O) + private fun symbolListBackup() { + lifecycleScope.launch { + settingsRepository.symbolListBackup().collect { result -> + when (result.code()) { + ResponseCode.SUCCESS.value -> { favoriteSymbolBackup() } + + ResponseCode.NO_INTERNET_CONNECTION.value -> { + sessionManager.setIsLogin(false) + navHomeActivity() + } + } + + } + } + + } + + @RequiresApi(Build.VERSION_CODES.O) + private fun favoriteSymbolBackup() { + lifecycleScope.launch { + settingsRepository.favoriteSymbolBackup().collect { result -> + when (result.code()) { + ResponseCode.SUCCESS.value -> { weightTableBackup() } + + ResponseCode.NO_INTERNET_CONNECTION.value -> { + sessionManager.setIsLogin(false) + navHomeActivity() + } + } + } + } + } + + @RequiresApi(Build.VERSION_CODES.O) + private fun weightTableBackup() { + lifecycleScope.launch { + settingsRepository.weightTableBackup().collect { result -> + when (result.code()) { + ResponseCode.SUCCESS.value -> { + sessionManager.setIsLogin(false) + navHomeActivity() + } + + ResponseCode.NO_INTERNET_CONNECTION.value -> { + sessionManager.setIsLogin(false) + navHomeActivity() + } + } + } + } + } + } \ No newline at end of file diff --git a/frontend/app/src/main/java/com/example/speechbuddy/HomeActivity.kt b/frontend/app/src/main/java/com/example/speechbuddy/HomeActivity.kt index 6b613602..89b3ba2f 100644 --- a/frontend/app/src/main/java/com/example/speechbuddy/HomeActivity.kt +++ b/frontend/app/src/main/java/com/example/speechbuddy/HomeActivity.kt @@ -6,18 +6,16 @@ import android.os.Bundle import android.view.MotionEvent import android.view.inputmethod.InputMethodManager import androidx.activity.compose.setContent -import androidx.activity.viewModels import androidx.annotation.RequiresApi +import androidx.lifecycle.lifecycleScope import com.example.speechbuddy.compose.SpeechBuddyHome import com.example.speechbuddy.ui.SpeechBuddyTheme -import com.example.speechbuddy.viewmodel.DisplaySettingsViewModel import dagger.hilt.android.AndroidEntryPoint +import kotlinx.coroutines.launch @AndroidEntryPoint class HomeActivity : BaseActivity() { - private val displaySettingsViewModel: DisplaySettingsViewModel by viewModels() - @RequiresApi(Build.VERSION_CODES.O) override fun onCreate(savedInstanceState: Bundle?) { @@ -48,11 +46,23 @@ class HomeActivity : BaseActivity() { } private fun getInitialPage(): Boolean { - return displaySettingsViewModel.getInitialPage() + var initialPage = false + lifecycleScope.launch { + settingsRepository.getInitialPage().collect { + initialPage = it.data?: false + } + } + return initialPage } private fun getInitialDarkMode(): Boolean { - return displaySettingsViewModel.getDarkMode() + var darkMode = false + lifecycleScope.launch { + settingsRepository.getDarkMode().collect { + darkMode = it.data?: false + } + } + return darkMode } override fun dispatchTouchEvent(event: MotionEvent): Boolean { diff --git a/frontend/app/src/main/java/com/example/speechbuddy/compose/SpeechBuddyAuth.kt b/frontend/app/src/main/java/com/example/speechbuddy/compose/SpeechBuddyAuth.kt index b73d7693..18ef1a68 100644 --- a/frontend/app/src/main/java/com/example/speechbuddy/compose/SpeechBuddyAuth.kt +++ b/frontend/app/src/main/java/com/example/speechbuddy/compose/SpeechBuddyAuth.kt @@ -12,23 +12,28 @@ import com.example.speechbuddy.compose.resetpassword.ResetPasswordScreen import com.example.speechbuddy.compose.signup.SignupScreen @Composable -fun SpeechBuddyAuth() { +fun SpeechBuddyAuth( + isBackup: Boolean +) { val navController = rememberNavController() SpeechBuddyAuthNavHost( - navController = navController + navController = navController, + isBackup = isBackup ) } @Composable fun SpeechBuddyAuthNavHost( - navController: NavHostController + navController: NavHostController, + isBackup: Boolean ) { NavHost(navController = navController, startDestination = "landing") { composable("landing") { LandingScreen( onLoginClick = { navController.navigate("login") - } + }, + isBackup = isBackup ) } composable("login") { diff --git a/frontend/app/src/main/java/com/example/speechbuddy/compose/landing/LandingScreen.kt b/frontend/app/src/main/java/com/example/speechbuddy/compose/landing/LandingScreen.kt index 2ec6c9e3..19e3ce3a 100644 --- a/frontend/app/src/main/java/com/example/speechbuddy/compose/landing/LandingScreen.kt +++ b/frontend/app/src/main/java/com/example/speechbuddy/compose/landing/LandingScreen.kt @@ -1,33 +1,39 @@ package com.example.speechbuddy.compose.landing import androidx.compose.foundation.Image +import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.offset +import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.wrapContentSize +import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Surface +import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource -import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +import androidx.compose.ui.window.Dialog import androidx.hilt.navigation.compose.hiltViewModel import com.example.speechbuddy.R import com.example.speechbuddy.compose.utils.ButtonUi -import com.example.speechbuddy.ui.SpeechBuddyTheme import com.example.speechbuddy.viewmodel.LoginViewModel @Composable fun LandingScreen( modifier: Modifier = Modifier, onLoginClick: () -> Unit, - viewModel: LoginViewModel = hiltViewModel() + viewModel: LoginViewModel = hiltViewModel(), + isBackup: Boolean ) { Surface( modifier = modifier.fillMaxSize(), @@ -46,15 +52,55 @@ fun LandingScreen( modifier = Modifier.padding(horizontal = 24.dp, vertical = 64.dp), contentAlignment = Alignment.BottomCenter ) { - Column( - verticalArrangement = Arrangement.spacedBy(14.dp) + if (!isBackup) { + Column( + verticalArrangement = Arrangement.spacedBy(14.dp) + ) { + ButtonUi( + text = stringResource(id = R.string.start_guest_mode), + onClick = { viewModel.enterGuestMode() } + ) + ButtonUi(text = stringResource(id = R.string.do_login), onClick = onLoginClick) + } + } + } + } + + if (isBackup) { + Dialog( + onDismissRequest = {} + ) { + Box( + modifier = Modifier + .wrapContentSize() + .background(MaterialTheme.colorScheme.surface) + .padding(24.dp) ) { - ButtonUi( - text = stringResource(id = R.string.start_guest_mode), - onClick = { viewModel.enterGuestMode() } - ) - ButtonUi(text = stringResource(id = R.string.do_login), onClick = onLoginClick) + Column( + modifier = Modifier + .wrapContentSize() + ) { + Text( + text = stringResource(id = R.string.auto_backup_title), + style = MaterialTheme.typography.headlineSmall + ) + Spacer(modifier = Modifier.height(24.dp)) + Row( + modifier = Modifier + .wrapContentSize(), + verticalAlignment = Alignment.CenterVertically + ) { + CircularProgressIndicator( + color = MaterialTheme.colorScheme.primary + ) + Text( + text = stringResource(id = R.string.auto_backup_info), + modifier = Modifier.padding(start = 20.dp) + ) + } + } } } } + } \ No newline at end of file diff --git a/frontend/app/src/main/java/com/example/speechbuddy/domain/SessionManager.kt b/frontend/app/src/main/java/com/example/speechbuddy/domain/SessionManager.kt index cde2f16d..b9f62097 100644 --- a/frontend/app/src/main/java/com/example/speechbuddy/domain/SessionManager.kt +++ b/frontend/app/src/main/java/com/example/speechbuddy/domain/SessionManager.kt @@ -13,6 +13,7 @@ class SessionManager { private val _cachedToken = MutableLiveData(null) private val _userId = MutableLiveData(null) + private val _isLogin = MutableLiveData(false) val cachedToken: LiveData get() = _cachedToken @@ -20,6 +21,15 @@ class SessionManager { val userId: LiveData get() = _userId + val isLogin: LiveData + get() = _isLogin + + fun setIsLogin(value: Boolean) { + CoroutineScope(Dispatchers.Main).launch { + _isLogin.value = value + } + } + /** * Authorized only when _userId is set to GUEST or refreshToken is not null * (because accessToken is temporarily set in case of 'reset password') diff --git a/frontend/app/src/main/java/com/example/speechbuddy/repository/AuthRepository.kt b/frontend/app/src/main/java/com/example/speechbuddy/repository/AuthRepository.kt index 3c21f827..d5145a18 100644 --- a/frontend/app/src/main/java/com/example/speechbuddy/repository/AuthRepository.kt +++ b/frontend/app/src/main/java/com/example/speechbuddy/repository/AuthRepository.kt @@ -57,6 +57,7 @@ class AuthRepository @Inject constructor( response.body()?.let { authTokenDto -> authTokenDto.let { val authToken = authTokenDtoMapper.mapToDomainModel(authTokenDto) + sessionManager.setIsLogin(true) authTokenPrefsManager.saveAuthToken(authToken) Resource.success(authToken) } diff --git a/frontend/app/src/main/java/com/example/speechbuddy/viewmodel/BackupSettingsViewModel.kt b/frontend/app/src/main/java/com/example/speechbuddy/viewmodel/BackupSettingsViewModel.kt index c62e7c56..c51b37c6 100644 --- a/frontend/app/src/main/java/com/example/speechbuddy/viewmodel/BackupSettingsViewModel.kt +++ b/frontend/app/src/main/java/com/example/speechbuddy/viewmodel/BackupSettingsViewModel.kt @@ -41,7 +41,6 @@ class BackupSettingsViewModel @Inject internal constructor( viewModelScope.launch { repository.setAutoBackup(value) } - // TODO: Implement automated backup } private fun setLastBackupDate(value: String) { diff --git a/frontend/app/src/main/res/values/strings.xml b/frontend/app/src/main/res/values/strings.xml index 633e926a..7c2c10f8 100644 --- a/frontend/app/src/main/res/values/strings.xml +++ b/frontend/app/src/main/res/values/strings.xml @@ -8,6 +8,9 @@ 비밀번호 변경 이메일 인증 + + 자동 백업 중 + 로그인해 SpeechBuddy의 다양한 기능을 사용해보세요 SpeechBuddy에 오신 것을 환영합니다! @@ -15,6 +18,7 @@ 이메일 인증을 먼저 진행해주세요 비밀번호 변경을 위해 이메일 인증을 먼저 진행해주세요 회원가입을 위해 이메일 인증을 먼저 진행해주세요 + 자동 백업을 원하시지 않으시면 설정의 서버에 백업하기 메뉴에서 자동 백업을 꺼 주세요. 이메일