diff --git a/AOS/app/src/main/java/boostcamp/and07/mindsync/data/di/ConnectivityManagerModule.kt b/AOS/app/src/main/java/boostcamp/and07/mindsync/data/di/ConnectivityManagerModule.kt new file mode 100644 index 00000000..a4780d50 --- /dev/null +++ b/AOS/app/src/main/java/boostcamp/and07/mindsync/data/di/ConnectivityManagerModule.kt @@ -0,0 +1,22 @@ +package boostcamp.and07.mindsync.data.di + +import android.content.Context +import android.net.ConnectivityManager +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.android.qualifiers.ApplicationContext +import dagger.hilt.components.SingletonComponent +import javax.inject.Singleton + +@Module +@InstallIn(SingletonComponent::class) +object ConnectivityManagerModule { + @Singleton + @Provides + fun provideConnectivityManager( + @ApplicationContext context: Context, + ): ConnectivityManager { + return context.getSystemService(ConnectivityManager::class.java) + } +} diff --git a/AOS/app/src/main/java/boostcamp/and07/mindsync/data/network/NetworkManager.kt b/AOS/app/src/main/java/boostcamp/and07/mindsync/data/network/NetworkManager.kt new file mode 100644 index 00000000..9df3a032 --- /dev/null +++ b/AOS/app/src/main/java/boostcamp/and07/mindsync/data/network/NetworkManager.kt @@ -0,0 +1,42 @@ +package boostcamp.and07.mindsync.data.network + +import android.net.ConnectivityManager +import android.net.Network +import android.net.NetworkCapabilities +import android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET +import android.net.NetworkRequest +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import javax.inject.Inject + +class NetworkManager + @Inject + constructor(private val connectivityManager: ConnectivityManager) { + private val _isConnected = MutableStateFlow(false) + val isConnected: StateFlow = _isConnected + + private val request: NetworkRequest = + NetworkRequest.Builder() + .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED) + .addCapability(NET_CAPABILITY_INTERNET) + .build() + + private val networkCallback = + object : ConnectivityManager.NetworkCallback() { + override fun onAvailable(network: Network) { + _isConnected.value = true + } + + override fun onLost(network: Network) { + _isConnected.value = false + } + } + + fun registerNetworkCallback() { + connectivityManager.registerNetworkCallback(request, networkCallback) + } + + fun unRegisterNetworkCallback() { + connectivityManager.unregisterNetworkCallback(networkCallback) + } + } diff --git a/AOS/app/src/main/java/boostcamp/and07/mindsync/ui/base/BaseActivityViewModel.kt b/AOS/app/src/main/java/boostcamp/and07/mindsync/ui/base/BaseActivityViewModel.kt index f924d410..280773f2 100644 --- a/AOS/app/src/main/java/boostcamp/and07/mindsync/ui/base/BaseActivityViewModel.kt +++ b/AOS/app/src/main/java/boostcamp/and07/mindsync/ui/base/BaseActivityViewModel.kt @@ -2,11 +2,16 @@ package boostcamp.and07.mindsync.ui.base import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import boostcamp.and07.mindsync.data.network.NetworkManager import boostcamp.and07.mindsync.data.repository.login.LogoutEventRepository import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharedFlow +import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asSharedFlow +import kotlinx.coroutines.flow.collectLatest +import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import javax.inject.Inject @@ -15,9 +20,17 @@ open class BaseActivityViewModel @Inject constructor( private val logoutEventRepository: LogoutEventRepository, + private val networkManager: NetworkManager, ) : ViewModel() { private val _events = MutableSharedFlow() val events: SharedFlow = _events.asSharedFlow() + private val _isConnected = MutableStateFlow(false) + val isConnected: StateFlow = _isConnected + + init { + networkManager.registerNetworkCallback() + observerNetworkConnection() + } private fun sendLogoutEvent() { viewModelScope.launch { @@ -29,9 +42,22 @@ open class BaseActivityViewModel } } + private fun observerNetworkConnection() { + viewModelScope.launch { + networkManager.isConnected.collectLatest { isConnected -> + _isConnected.update { isConnected } + } + } + } + fun logout() { viewModelScope.launch { logoutEventRepository.logout() } } + + override fun onCleared() { + super.onCleared() + networkManager.unRegisterNetworkCallback() + } } diff --git a/AOS/app/src/main/java/boostcamp/and07/mindsync/ui/boardlist/BoardListScreen.kt b/AOS/app/src/main/java/boostcamp/and07/mindsync/ui/boardlist/BoardListScreen.kt index f9ac859f..955635da 100644 --- a/AOS/app/src/main/java/boostcamp/and07/mindsync/ui/boardlist/BoardListScreen.kt +++ b/AOS/app/src/main/java/boostcamp/and07/mindsync/ui/boardlist/BoardListScreen.kt @@ -39,6 +39,7 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle import boostcamp.and07.mindsync.R import boostcamp.and07.mindsync.data.model.Board import boostcamp.and07.mindsync.ui.dialog.AddBoardDialogScreen +import boostcamp.and07.mindsync.ui.dialog.DisConnectedNetworkDialogScreen import boostcamp.and07.mindsync.ui.theme.MindSyncTheme import coil.compose.AsyncImage import java.io.File @@ -58,6 +59,7 @@ fun BoardListScreen( updateBoardName: (CharSequence) -> Unit, ) { val uiState by boardListViewModel.boardUiState.collectAsStateWithLifecycle() + val isConnected by boardListViewModel.isConnected.collectAsStateWithLifecycle() Scaffold(bottomBar = { BoardListBottomBar( uiState = uiState, @@ -86,6 +88,9 @@ fun BoardListScreen( closeDialog = { showDialog(false) }, ) } + if (isConnected.not()) { + DisConnectedNetworkDialogScreen() + } } } } diff --git a/AOS/app/src/main/java/boostcamp/and07/mindsync/ui/boardlist/BoardListViewModel.kt b/AOS/app/src/main/java/boostcamp/and07/mindsync/ui/boardlist/BoardListViewModel.kt index 1b1a21ad..0ad71518 100644 --- a/AOS/app/src/main/java/boostcamp/and07/mindsync/ui/boardlist/BoardListViewModel.kt +++ b/AOS/app/src/main/java/boostcamp/and07/mindsync/ui/boardlist/BoardListViewModel.kt @@ -1,9 +1,11 @@ package boostcamp.and07.mindsync.ui.boardlist import androidx.lifecycle.SavedStateHandle -import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import boostcamp.and07.mindsync.data.network.NetworkManager import boostcamp.and07.mindsync.data.repository.boardlist.BoardListRepository +import boostcamp.and07.mindsync.data.repository.login.LogoutEventRepository +import boostcamp.and07.mindsync.ui.base.BaseActivityViewModel import boostcamp.and07.mindsync.ui.util.fileToMultiPart import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.CoroutineExceptionHandler @@ -23,7 +25,9 @@ class BoardListViewModel constructor( private val savedStateHandle: SavedStateHandle, private val boardListRepository: BoardListRepository, - ) : ViewModel() { + logoutEventRepository: LogoutEventRepository, + networkManager: NetworkManager, + ) : BaseActivityViewModel(logoutEventRepository, networkManager) { private val _boardUiState = MutableStateFlow(BoardListUiState()) val boardUiState: StateFlow = _boardUiState private val _boardUiEvent = MutableSharedFlow() diff --git a/AOS/app/src/main/java/boostcamp/and07/mindsync/ui/dialog/DisConnectedNetworkDialog.kt b/AOS/app/src/main/java/boostcamp/and07/mindsync/ui/dialog/DisConnectedNetworkDialog.kt new file mode 100644 index 00000000..fed5f9cc --- /dev/null +++ b/AOS/app/src/main/java/boostcamp/and07/mindsync/ui/dialog/DisConnectedNetworkDialog.kt @@ -0,0 +1,79 @@ +package boostcamp.and07.mindsync.ui.dialog + +import android.app.Dialog +import android.content.DialogInterface +import android.graphics.Color +import android.graphics.drawable.ColorDrawable +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.view.WindowManager +import androidx.fragment.app.DialogFragment +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.lifecycleScope +import androidx.lifecycle.repeatOnLifecycle +import boostcamp.and07.mindsync.R +import boostcamp.and07.mindsync.databinding.DialogDisconnectNetworkBinding +import dagger.hilt.android.AndroidEntryPoint +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.collectLatest +import kotlinx.coroutines.launch + +@AndroidEntryPoint +class DisConnectedNetworkDialog(private val isConnected: StateFlow) : + DialogFragment() { + private var _binding: DialogDisconnectNetworkBinding? = null + private val binding get() = _binding!! + + override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { + val dialog = super.onCreateDialog(savedInstanceState) + dialog.window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT)) + dialog.window?.attributes?.windowAnimations = R.style.AnimationDialogStyle + isCancelable = false + return dialog + } + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle?, + ): View? { + _binding = DialogDisconnectNetworkBinding.inflate(inflater, container, false) + observerNetworkState() + return binding.root + } + + override fun onStart() { + super.onStart() + resizeDialog() + } + + private fun observerNetworkState() { + lifecycleScope.launch { + repeatOnLifecycle(Lifecycle.State.STARTED) { + isConnected.collectLatest { isConnected -> + if (isConnected) { + dismiss() + } + } + } + } + } + + private fun resizeDialog() { + val params: ViewGroup.LayoutParams? = dialog?.window?.attributes + + val displayMetrics = requireActivity().resources.displayMetrics + val deviceWidth = displayMetrics.widthPixels + + params?.width = (deviceWidth * 0.8).toInt() + params?.height = WindowManager.LayoutParams.WRAP_CONTENT + dialog?.window?.attributes = params as WindowManager.LayoutParams + } + + override fun onDismiss(dialog: DialogInterface) { + _binding = null + super.onDismiss(dialog) + } +} diff --git a/AOS/app/src/main/java/boostcamp/and07/mindsync/ui/dialog/DisConntedNetworkDialog.kt b/AOS/app/src/main/java/boostcamp/and07/mindsync/ui/dialog/DisConntedNetworkDialog.kt new file mode 100644 index 00000000..c35ff533 --- /dev/null +++ b/AOS/app/src/main/java/boostcamp/and07/mindsync/ui/dialog/DisConntedNetworkDialog.kt @@ -0,0 +1,68 @@ +package boostcamp.and07.mindsync.ui.dialog + +import androidx.compose.foundation.Image +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.width +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalConfiguration +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 boostcamp.and07.mindsync.R +import boostcamp.and07.mindsync.ui.theme.MindSyncTheme + +@Composable +fun DisConnectedNetworkDialogScreen() { + val screenWidth = LocalConfiguration.current.screenWidthDp.dp + val screenHeight = LocalConfiguration.current.screenHeightDp.dp + val dialogWidth = screenWidth * 0.8f + + Dialog(onDismissRequest = {}) { + Column( + modifier = + Modifier + .width(dialogWidth) + .height(screenHeight), + verticalArrangement = Arrangement.Center, + ) { + Row( + modifier = + Modifier + .align(Alignment.CenterHorizontally), + verticalAlignment = Alignment.CenterVertically, + ) { + Image( + painter = painterResource(id = R.drawable.ic_network_off), + contentDescription = null, + ) + } + Spacer(modifier = Modifier.height(30.dp)) + Row( + modifier = Modifier.align(Alignment.CenterHorizontally), + ) { + Text( + text = stringResource(id = R.string.disconnected_network_message), + style = MaterialTheme.typography.displaySmall, + ) + } + } + } +} + +@Preview(showBackground = true) +@Composable +private fun LoadingDialogPreview() { + MindSyncTheme { + DisConnectedNetworkDialogScreen() + } +} diff --git a/AOS/app/src/main/java/boostcamp/and07/mindsync/ui/login/LoginViewModel.kt b/AOS/app/src/main/java/boostcamp/and07/mindsync/ui/login/LoginViewModel.kt index 018c055f..f0f781f3 100644 --- a/AOS/app/src/main/java/boostcamp/and07/mindsync/ui/login/LoginViewModel.kt +++ b/AOS/app/src/main/java/boostcamp/and07/mindsync/ui/login/LoginViewModel.kt @@ -2,6 +2,7 @@ package boostcamp.and07.mindsync.ui.login import android.util.Log import androidx.lifecycle.viewModelScope +import boostcamp.and07.mindsync.data.network.NetworkManager import boostcamp.and07.mindsync.data.repository.login.LoginRepository import boostcamp.and07.mindsync.data.repository.login.LogoutEventRepository import boostcamp.and07.mindsync.data.repository.login.TokenRepository @@ -24,9 +25,10 @@ class LoginViewModel constructor( private val loginRepository: LoginRepository, private val tokenRepository: TokenRepository, - private val logoutEventRepository: LogoutEventRepository, + logoutEventRepository: LogoutEventRepository, + networkManager: NetworkManager, ) : - BaseActivityViewModel(logoutEventRepository) { + BaseActivityViewModel(logoutEventRepository, networkManager) { private val _loginEvent = MutableSharedFlow() val loginEvent = _loginEvent.asSharedFlow() diff --git a/AOS/app/src/main/java/boostcamp/and07/mindsync/ui/main/MainActivity.kt b/AOS/app/src/main/java/boostcamp/and07/mindsync/ui/main/MainActivity.kt index d8553ba7..e16ac8ee 100644 --- a/AOS/app/src/main/java/boostcamp/and07/mindsync/ui/main/MainActivity.kt +++ b/AOS/app/src/main/java/boostcamp/and07/mindsync/ui/main/MainActivity.kt @@ -18,6 +18,7 @@ import boostcamp.and07.mindsync.databinding.ActivityMainBinding import boostcamp.and07.mindsync.ui.base.BaseActivity import boostcamp.and07.mindsync.ui.base.BaseActivityViewModel import boostcamp.and07.mindsync.ui.boardlist.UsersAdapter +import boostcamp.and07.mindsync.ui.dialog.DisConnectedNetworkDialog import boostcamp.and07.mindsync.ui.profile.ProfileActivity import boostcamp.and07.mindsync.ui.space.list.SpaceListFragmentDirections import boostcamp.and07.mindsync.ui.util.ThrottleDuration @@ -54,6 +55,7 @@ class MainActivity : setSideBarNavigation() setBinding() observeEvent() + showDisconnectedNetworkDialog() } override fun getViewModel(): BaseActivityViewModel { @@ -83,6 +85,22 @@ class MainActivity : } } + private fun showDisconnectedNetworkDialog() { + val dialog = DisConnectedNetworkDialog(mainViewModel.isConnected) + lifecycleScope.launch { + repeatOnLifecycle(Lifecycle.State.STARTED) { + mainViewModel.isConnected.collectLatest { isConnected -> + if (isConnected.not()) { + dialog.show( + this@MainActivity.supportFragmentManager, + "DisConnectedNetworkDialog", + ) + } + } + } + } + } + private fun setNavController() { val navHostFragment = (supportFragmentManager.findFragmentById(R.id.fcv_main_nav_host) as NavHostFragment) @@ -213,7 +231,11 @@ class MainActivity : spaceAdapter.setSideBarClickListener( object : SpaceClickListener { override fun onClickSpace(space: Space) { - navController.navigate(SpaceListFragmentDirections.actionToBoardListFragment(spaceId = space.id)) + navController.navigate( + SpaceListFragmentDirections.actionToBoardListFragment( + spaceId = space.id, + ), + ) mainViewModel.updateCurrentSpace(space) } }, diff --git a/AOS/app/src/main/java/boostcamp/and07/mindsync/ui/main/MainViewModel.kt b/AOS/app/src/main/java/boostcamp/and07/mindsync/ui/main/MainViewModel.kt index fd02c5c3..88b61dbb 100644 --- a/AOS/app/src/main/java/boostcamp/and07/mindsync/ui/main/MainViewModel.kt +++ b/AOS/app/src/main/java/boostcamp/and07/mindsync/ui/main/MainViewModel.kt @@ -2,6 +2,7 @@ package boostcamp.and07.mindsync.ui.main import androidx.lifecycle.viewModelScope import boostcamp.and07.mindsync.data.model.Space +import boostcamp.and07.mindsync.data.network.NetworkManager import boostcamp.and07.mindsync.data.repository.login.LogoutEventRepository import boostcamp.and07.mindsync.data.repository.profile.ProfileRepository import boostcamp.and07.mindsync.data.repository.profilespace.ProfileSpaceRepository @@ -23,8 +24,9 @@ class MainViewModel constructor( private val profileRepository: ProfileRepository, private val profileSpaceRepository: ProfileSpaceRepository, - private val logoutEventRepository: LogoutEventRepository, - ) : BaseActivityViewModel(logoutEventRepository) { + logoutEventRepository: LogoutEventRepository, + networkManager: NetworkManager, + ) : BaseActivityViewModel(logoutEventRepository, networkManager) { private val _uiState = MutableStateFlow(MainUiState()) val uiState: StateFlow = _uiState private val _event = MutableSharedFlow() diff --git a/AOS/app/src/main/java/boostcamp/and07/mindsync/ui/profile/ProfileFragment.kt b/AOS/app/src/main/java/boostcamp/and07/mindsync/ui/profile/ProfileFragment.kt index e104409d..4ab1fe6f 100644 --- a/AOS/app/src/main/java/boostcamp/and07/mindsync/ui/profile/ProfileFragment.kt +++ b/AOS/app/src/main/java/boostcamp/and07/mindsync/ui/profile/ProfileFragment.kt @@ -5,9 +5,9 @@ import android.net.Uri import android.os.Bundle import android.view.View import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue import androidx.navigation.navGraphViewModels import boostcamp.and07.mindsync.R +import boostcamp.and07.mindsync.data.network.NetworkManager import boostcamp.and07.mindsync.data.repository.login.LogoutEventRepository import boostcamp.and07.mindsync.data.repository.profile.ProfileRepository import boostcamp.and07.mindsync.ui.base.BaseComposeFragment @@ -26,8 +26,11 @@ class ProfileFragment : BaseComposeFragment() { @Inject lateinit var loginEveRepository: LogoutEventRepository + @Inject + lateinit var networkManager: NetworkManager + private val profileViewModel: ProfileViewModel by navGraphViewModels(R.id.nav_profile) { - ProfileViewModelFactory(profileRepository, loginEveRepository) + ProfileViewModelFactory(profileRepository, loginEveRepository, networkManager) } private lateinit var imagePickerHandler: ImagePickerHandler diff --git a/AOS/app/src/main/java/boostcamp/and07/mindsync/ui/profile/ProfileScreen.kt b/AOS/app/src/main/java/boostcamp/and07/mindsync/ui/profile/ProfileScreen.kt index e9300492..c742c6f2 100644 --- a/AOS/app/src/main/java/boostcamp/and07/mindsync/ui/profile/ProfileScreen.kt +++ b/AOS/app/src/main/java/boostcamp/and07/mindsync/ui/profile/ProfileScreen.kt @@ -45,6 +45,7 @@ import boostcamp.and07.mindsync.R import boostcamp.and07.mindsync.ui.components.BackIconButton import boostcamp.and07.mindsync.ui.components.EditIconButton import boostcamp.and07.mindsync.ui.components.MSButton +import boostcamp.and07.mindsync.ui.dialog.DisConnectedNetworkDialogScreen import boostcamp.and07.mindsync.ui.dialog.NickNameDialog import boostcamp.and07.mindsync.ui.theme.Blue1 import boostcamp.and07.mindsync.ui.theme.Gray4 @@ -68,6 +69,7 @@ fun ProfileScreen( var nicknameColor by remember { mutableStateOf(Gray4) } val snackBarHostState = remember { SnackbarHostState() } val coroutineScope = rememberCoroutineScope() + val isConnected by profileViewModel.isConnected.collectAsStateWithLifecycle() HandleProfileEvents( profileViewModel = profileViewModel, onBack = onBack, @@ -92,6 +94,7 @@ fun ProfileScreen( updateProfile = updateProfile, updateNickname = updateNickname, editNickname = editNickname, + isConneceted = isConnected, ) } @@ -126,6 +129,7 @@ private fun ProfileContent( updateNickname: (CharSequence) -> Unit = { }, editNickname: (CharSequence) -> Unit = { }, snackBarHostState: SnackbarHostState = SnackbarHostState(), + isConneceted: Boolean = false, ) { Scaffold( topBar = { ProfileTopAppBar(onBack) }, @@ -200,6 +204,9 @@ private fun ProfileContent( updateNickname = { updateNickname(it) }, ) } + if (isConneceted.not()) { + DisConnectedNetworkDialogScreen() + } } @Composable diff --git a/AOS/app/src/main/java/boostcamp/and07/mindsync/ui/profile/ProfileViewModel.kt b/AOS/app/src/main/java/boostcamp/and07/mindsync/ui/profile/ProfileViewModel.kt index 3e010cc8..4b465e33 100644 --- a/AOS/app/src/main/java/boostcamp/and07/mindsync/ui/profile/ProfileViewModel.kt +++ b/AOS/app/src/main/java/boostcamp/and07/mindsync/ui/profile/ProfileViewModel.kt @@ -2,6 +2,7 @@ package boostcamp.and07.mindsync.ui.profile import android.net.Uri import androidx.lifecycle.viewModelScope +import boostcamp.and07.mindsync.data.network.NetworkManager import boostcamp.and07.mindsync.data.repository.login.LogoutEventRepository import boostcamp.and07.mindsync.data.repository.profile.ProfileRepository import boostcamp.and07.mindsync.ui.base.BaseActivityViewModel @@ -25,8 +26,9 @@ class ProfileViewModel constructor( private val profileRepository: ProfileRepository, logoutEventRepository: LogoutEventRepository, + networkManager: NetworkManager, ) : - BaseActivityViewModel(logoutEventRepository) { + BaseActivityViewModel(logoutEventRepository, networkManager) { private val _uiState = MutableStateFlow(ProfileUiState()) val uiState: StateFlow = _uiState private val _event = MutableSharedFlow() diff --git a/AOS/app/src/main/java/boostcamp/and07/mindsync/ui/profile/ProfileViewModelFactory.kt b/AOS/app/src/main/java/boostcamp/and07/mindsync/ui/profile/ProfileViewModelFactory.kt index d30cbb1c..985c50c5 100644 --- a/AOS/app/src/main/java/boostcamp/and07/mindsync/ui/profile/ProfileViewModelFactory.kt +++ b/AOS/app/src/main/java/boostcamp/and07/mindsync/ui/profile/ProfileViewModelFactory.kt @@ -2,18 +2,20 @@ package boostcamp.and07.mindsync.ui.profile import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider +import boostcamp.and07.mindsync.data.network.NetworkManager import boostcamp.and07.mindsync.data.repository.login.LogoutEventRepository import boostcamp.and07.mindsync.data.repository.profile.ProfileRepository class ProfileViewModelFactory( private val profileRepository: ProfileRepository, private val logoutEventRepository: LogoutEventRepository, + private val networkManager: NetworkManager, ) : ViewModelProvider.Factory { override fun create(modelClass: Class): T { if (modelClass.isAssignableFrom(ProfileViewModel::class.java)) { @Suppress("UNCHECKED_CAST") - return ProfileViewModel(profileRepository, logoutEventRepository) as T + return ProfileViewModel(profileRepository, logoutEventRepository, networkManager) as T } throw IllegalArgumentException("Unknown ViewModel class") } diff --git a/AOS/app/src/main/java/boostcamp/and07/mindsync/ui/recyclebin/RecycleBinViewModel.kt b/AOS/app/src/main/java/boostcamp/and07/mindsync/ui/recyclebin/RecycleBinViewModel.kt index e6c7deee..80b5ad38 100644 --- a/AOS/app/src/main/java/boostcamp/and07/mindsync/ui/recyclebin/RecycleBinViewModel.kt +++ b/AOS/app/src/main/java/boostcamp/and07/mindsync/ui/recyclebin/RecycleBinViewModel.kt @@ -1,9 +1,11 @@ package boostcamp.and07.mindsync.ui.recyclebin -import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import boostcamp.and07.mindsync.data.model.Board +import boostcamp.and07.mindsync.data.network.NetworkManager import boostcamp.and07.mindsync.data.repository.boardlist.BoardListRepository +import boostcamp.and07.mindsync.data.repository.login.LogoutEventRepository +import boostcamp.and07.mindsync.ui.base.BaseActivityViewModel import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.CoroutineExceptionHandler import kotlinx.coroutines.flow.MutableSharedFlow @@ -20,7 +22,9 @@ class RecycleBinViewModel @Inject constructor( private val boardListRepository: BoardListRepository, - ) : ViewModel() { + logoutEventRepository: LogoutEventRepository, + networkManager: NetworkManager, + ) : BaseActivityViewModel(logoutEventRepository, networkManager) { private val _uiState = MutableStateFlow(RecycleBinUiState()) val uiState: StateFlow = _uiState private val _uiEvent = MutableSharedFlow() diff --git a/AOS/app/src/main/java/boostcamp/and07/mindsync/ui/space/generate/AddSpaceScreen.kt b/AOS/app/src/main/java/boostcamp/and07/mindsync/ui/space/generate/AddSpaceScreen.kt index 30de5b3b..8d239622 100644 --- a/AOS/app/src/main/java/boostcamp/and07/mindsync/ui/space/generate/AddSpaceScreen.kt +++ b/AOS/app/src/main/java/boostcamp/and07/mindsync/ui/space/generate/AddSpaceScreen.kt @@ -45,6 +45,7 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle import boostcamp.and07.mindsync.R +import boostcamp.and07.mindsync.ui.dialog.DisConnectedNetworkDialogScreen import boostcamp.and07.mindsync.ui.space.SpaceUiEvent import boostcamp.and07.mindsync.ui.space.SpaceUiState import boostcamp.and07.mindsync.ui.theme.Blue1 @@ -63,6 +64,7 @@ fun AddSpaceScreen( ) { val uiState by addSpaceViewModel.uiState.collectAsStateWithLifecycle() val snackBarHostState = remember { SnackbarHostState() } + val isConnected by addSpaceViewModel.isConnected.collectAsStateWithLifecycle() HandleAddSpaceEvents( addSpaceViewModel = addSpaceViewModel, onBack = onBackClicked, @@ -77,6 +79,9 @@ fun AddSpaceScreen( .padding(innerPadding) .fillMaxWidth(), ) { + if (isConnected.not()) { + DisConnectedNetworkDialogScreen() + } AddSpaceContent( uiState = uiState, onBackClicked = onBackClicked, diff --git a/AOS/app/src/main/java/boostcamp/and07/mindsync/ui/space/generate/AddSpaceViewModel.kt b/AOS/app/src/main/java/boostcamp/and07/mindsync/ui/space/generate/AddSpaceViewModel.kt index 8ff6359b..e19ef708 100644 --- a/AOS/app/src/main/java/boostcamp/and07/mindsync/ui/space/generate/AddSpaceViewModel.kt +++ b/AOS/app/src/main/java/boostcamp/and07/mindsync/ui/space/generate/AddSpaceViewModel.kt @@ -1,6 +1,7 @@ package boostcamp.and07.mindsync.ui.space.generate import androidx.lifecycle.viewModelScope +import boostcamp.and07.mindsync.data.network.NetworkManager import boostcamp.and07.mindsync.data.repository.login.LogoutEventRepository import boostcamp.and07.mindsync.data.repository.space.SpaceRepository import boostcamp.and07.mindsync.ui.base.BaseActivityViewModel @@ -26,7 +27,8 @@ class AddSpaceViewModel constructor( private val spaceRepository: SpaceRepository, logoutEventRepository: LogoutEventRepository, - ) : BaseActivityViewModel(logoutEventRepository) { + networkManager: NetworkManager, + ) : BaseActivityViewModel(logoutEventRepository, networkManager) { private val _uiState = MutableStateFlow(SpaceUiState()) val uiState: StateFlow = _uiState private val _event = MutableSharedFlow() diff --git a/AOS/app/src/main/java/boostcamp/and07/mindsync/ui/space/join/AddInviteSpaceViewModel.kt b/AOS/app/src/main/java/boostcamp/and07/mindsync/ui/space/join/AddInviteSpaceViewModel.kt index 774c525e..b00abcd3 100644 --- a/AOS/app/src/main/java/boostcamp/and07/mindsync/ui/space/join/AddInviteSpaceViewModel.kt +++ b/AOS/app/src/main/java/boostcamp/and07/mindsync/ui/space/join/AddInviteSpaceViewModel.kt @@ -1,7 +1,11 @@ package boostcamp.and07.mindsync.ui.space.join +import boostcamp.and07.mindsync.data.network.NetworkManager import boostcamp.and07.mindsync.data.repository.login.LogoutEventRepository import boostcamp.and07.mindsync.ui.base.BaseActivityViewModel -class AddInviteSpaceViewModel(logoutEventRepository: LogoutEventRepository) : - BaseActivityViewModel(logoutEventRepository) +class AddInviteSpaceViewModel( + logoutEventRepository: LogoutEventRepository, + networkManager: NetworkManager, +) : + BaseActivityViewModel(logoutEventRepository, networkManager) diff --git a/AOS/app/src/main/java/boostcamp/and07/mindsync/ui/space/join/ConfirmInviteSpaceViewModel.kt b/AOS/app/src/main/java/boostcamp/and07/mindsync/ui/space/join/ConfirmInviteSpaceViewModel.kt index 0b72ec97..a22a7c19 100644 --- a/AOS/app/src/main/java/boostcamp/and07/mindsync/ui/space/join/ConfirmInviteSpaceViewModel.kt +++ b/AOS/app/src/main/java/boostcamp/and07/mindsync/ui/space/join/ConfirmInviteSpaceViewModel.kt @@ -1,9 +1,11 @@ package boostcamp.and07.mindsync.ui.space.join -import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import boostcamp.and07.mindsync.data.model.Space +import boostcamp.and07.mindsync.data.network.NetworkManager +import boostcamp.and07.mindsync.data.repository.login.LogoutEventRepository import boostcamp.and07.mindsync.data.repository.profilespace.ProfileSpaceRepository +import boostcamp.and07.mindsync.ui.base.BaseActivityViewModel import boostcamp.and07.mindsync.ui.space.SpaceUiEvent import boostcamp.and07.mindsync.ui.space.SpaceUiState import dagger.hilt.android.lifecycle.HiltViewModel @@ -22,7 +24,9 @@ class ConfirmInviteSpaceViewModel @Inject constructor( private val profileSpaceRepository: ProfileSpaceRepository, - ) : ViewModel() { + logoutEventRepository: LogoutEventRepository, + networkManager: NetworkManager, + ) : BaseActivityViewModel(logoutEventRepository, networkManager) { private val _uiState = MutableStateFlow(SpaceUiState()) val uiState: StateFlow = _uiState private val _event = MutableSharedFlow() diff --git a/AOS/app/src/main/java/boostcamp/and07/mindsync/ui/space/join/InputSpaceCodeViewModel.kt b/AOS/app/src/main/java/boostcamp/and07/mindsync/ui/space/join/InputSpaceCodeViewModel.kt index fcb4ea4a..3c60b33a 100644 --- a/AOS/app/src/main/java/boostcamp/and07/mindsync/ui/space/join/InputSpaceCodeViewModel.kt +++ b/AOS/app/src/main/java/boostcamp/and07/mindsync/ui/space/join/InputSpaceCodeViewModel.kt @@ -1,8 +1,10 @@ package boostcamp.and07.mindsync.ui.space.join -import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import boostcamp.and07.mindsync.data.network.NetworkManager +import boostcamp.and07.mindsync.data.repository.login.LogoutEventRepository import boostcamp.and07.mindsync.data.repository.space.SpaceRepository +import boostcamp.and07.mindsync.ui.base.BaseActivityViewModel import boostcamp.and07.mindsync.ui.space.SpaceUiEvent import boostcamp.and07.mindsync.ui.space.SpaceUiState import boostcamp.and07.mindsync.ui.util.SpaceExceptionMessage @@ -22,7 +24,9 @@ class InputSpaceCodeViewModel @Inject constructor( private val spaceRepository: SpaceRepository, - ) : ViewModel() { + logoutEventRepository: LogoutEventRepository, + networkManager: NetworkManager, + ) : BaseActivityViewModel(logoutEventRepository, networkManager) { private val _uiState = MutableStateFlow(SpaceUiState()) val uiState: StateFlow = _uiState private val _event = MutableSharedFlow() diff --git a/AOS/app/src/main/java/boostcamp/and07/mindsync/ui/space/list/SpaceListViewModel.kt b/AOS/app/src/main/java/boostcamp/and07/mindsync/ui/space/list/SpaceListViewModel.kt index 2df4afdd..5415088e 100644 --- a/AOS/app/src/main/java/boostcamp/and07/mindsync/ui/space/list/SpaceListViewModel.kt +++ b/AOS/app/src/main/java/boostcamp/and07/mindsync/ui/space/list/SpaceListViewModel.kt @@ -1,9 +1,11 @@ package boostcamp.and07.mindsync.ui.space.list -import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import boostcamp.and07.mindsync.data.network.NetworkManager +import boostcamp.and07.mindsync.data.repository.login.LogoutEventRepository import boostcamp.and07.mindsync.data.repository.profilespace.ProfileSpaceRepository import boostcamp.and07.mindsync.data.repository.space.SpaceRepository +import boostcamp.and07.mindsync.ui.base.BaseActivityViewModel import boostcamp.and07.mindsync.ui.space.SpaceUiEvent import boostcamp.and07.mindsync.ui.space.SpaceUiState import dagger.hilt.android.lifecycle.HiltViewModel @@ -23,7 +25,9 @@ class SpaceListViewModel constructor( private val spaceRepository: SpaceRepository, private val profileSpaceRepository: ProfileSpaceRepository, - ) : ViewModel() { + logoutEventRepository: LogoutEventRepository, + networkManager: NetworkManager, + ) : BaseActivityViewModel(logoutEventRepository, networkManager) { private val _uiState = MutableStateFlow(SpaceUiState()) val uiState: StateFlow = _uiState private val _uiEvent = MutableSharedFlow() diff --git a/AOS/app/src/main/res/drawable/ic_network_off.xml b/AOS/app/src/main/res/drawable/ic_network_off.xml new file mode 100644 index 00000000..a44c7b90 --- /dev/null +++ b/AOS/app/src/main/res/drawable/ic_network_off.xml @@ -0,0 +1,9 @@ + + + diff --git a/AOS/app/src/main/res/layout/dialog_disconnect_network.xml b/AOS/app/src/main/res/layout/dialog_disconnect_network.xml new file mode 100644 index 00000000..08db3262 --- /dev/null +++ b/AOS/app/src/main/res/layout/dialog_disconnect_network.xml @@ -0,0 +1,29 @@ + + + + + + + + \ No newline at end of file diff --git a/AOS/app/src/main/res/values/strings.xml b/AOS/app/src/main/res/values/strings.xml index 8356817f..7c9b08e0 100644 --- a/AOS/app/src/main/res/values/strings.xml +++ b/AOS/app/src/main/res/values/strings.xml @@ -7,4 +7,5 @@ 스페이스를 추가해서\n다른사람들과 함께\n마인드맵을 그려보세요! 스페이스 추가하기 MindSync 시작하기 + 네트워크 연결이 끊겼습니다.