Skip to content

Commit

Permalink
Merge pull request #78 from AdultOfNineteen/refactor/issue-77
Browse files Browse the repository at this point in the history
[REFACTOR] NetworkMonitor 도입 및 인터페이스 주입 Binds 로 변경
  • Loading branch information
DongChyeon authored May 29, 2024
2 parents 0c3ebdd + 412ac42 commit 68248a1
Show file tree
Hide file tree
Showing 18 changed files with 222 additions and 97 deletions.
9 changes: 8 additions & 1 deletion app/src/main/java/com/teamwiney/winey/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,19 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.Surface
import androidx.compose.ui.Modifier
import androidx.core.view.WindowCompat
import com.teamwiney.core.common.NetworkMonitor
import com.teamwiney.core.common.rememberWineyAppState
import com.teamwiney.core.common.rememberWineyBottomSheetState
import com.teamwiney.ui.theme.WineyTheme
import dagger.hilt.android.AndroidEntryPoint
import javax.inject.Inject

@AndroidEntryPoint
class MainActivity : ComponentActivity() {

@Inject
lateinit var networkMonitor: NetworkMonitor

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// WindowInset 직접 조절하기 위해서
Expand All @@ -26,7 +31,9 @@ class MainActivity : ComponentActivity() {
color = WineyTheme.colors.background_1
) {
WineyNavHost(
appState = rememberWineyAppState(),
appState = rememberWineyAppState(
networkMonitor = networkMonitor
),
bottomSheetState = rememberWineyBottomSheetState()
)
}
Expand Down
15 changes: 15 additions & 0 deletions app/src/main/java/com/teamwiney/winey/WineyNavHost.kt
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,20 @@ import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.ModalBottomSheetLayout
import androidx.compose.material.Scaffold
import androidx.compose.material.Snackbar
import androidx.compose.material.SnackbarDuration
import androidx.compose.material.SnackbarHost
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.rememberUpdatedState
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.localbroadcastmanager.content.LocalBroadcastManager
import androidx.navigation.NavDestination
import androidx.navigation.NavType
Expand Down Expand Up @@ -85,6 +89,17 @@ fun WineyNavHost(
) {
val navController = appState.navController

val isOffline by appState.isOffline.collectAsStateWithLifecycle()

LaunchedEffect(isOffline) {
if (isOffline) {
appState.scaffoldState.snackbarHostState.showSnackbar(
message = "\uFE0F 인터넷에 연결되어 있지 않습니다.",
duration = SnackbarDuration.Indefinite
)
}
}

TokenExpiredBroadcastReceiver { intent ->
if (intent?.action == "com.teamwiney.winey.TOKEN_EXPIRED") {
appState.showSnackbar("토큰이 만료되었습니다. 다시 로그인해주세요.")
Expand Down
13 changes: 9 additions & 4 deletions core/common/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import java.util.Properties
plugins {
id(libs.plugins.android.library.get().pluginId)
id(libs.plugins.jetbrains.kotlin.android.get().pluginId)
id(libs.plugins.dagger.hilt.android.get().pluginId)
id(libs.plugins.kotlin.kapt.get().pluginId)
}

val properties = Properties().apply {
Expand All @@ -24,7 +26,6 @@ android {
)

testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles("consumer-rules.pro")
}
buildFeatures {
compose = true
Expand All @@ -42,11 +43,11 @@ android {
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
kotlinOptions {
jvmTarget = "1.8"
jvmTarget = "17"
}
}

Expand All @@ -61,5 +62,9 @@ dependencies {
implementation(libs.coroutines.android)
implementation(libs.lifecycle.runtime.viewmodel)
implementation(libs.converter.gson)

implementation(libs.datastore)
implementation(libs.dagger)
implementation(libs.hilt.android)
kapt(libs.hilt.android.compiler)
}
4 changes: 2 additions & 2 deletions core/common/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest>

<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
</manifest>
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package com.teamwiney.core.common

import android.content.Context
import android.net.ConnectivityManager
import android.net.ConnectivityManager.NetworkCallback
import android.net.Network
import android.net.NetworkCapabilities
import android.net.NetworkRequest
import android.os.Build
import android.util.Log
import androidx.core.content.getSystemService
import com.teamwiney.core.common.di.DispatcherModule
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.conflate
import kotlinx.coroutines.flow.flowOn
import javax.inject.Inject

interface NetworkMonitor {
val isOnline: Flow<Boolean>
}

class ConnectivityManagerNetworkMonitor @Inject constructor(
@ApplicationContext private val context: Context,
@DispatcherModule.IoDispatcher private val ioDispatcher: CoroutineDispatcher
): NetworkMonitor {
override val isOnline: Flow<Boolean> = callbackFlow {
val connectivityManager = context.getSystemService<ConnectivityManager>()
if (connectivityManager == null) {
trySend(false)
close()
return@callbackFlow
}

val callback = object : NetworkCallback() {

private val networks = mutableSetOf<Network>()

override fun onAvailable(network: Network) {
networks += network
trySend(true)

Log.d("NetworkMonitor", "Network available")
}

override fun onLost(network: Network) {
networks -= network
trySend(networks.isNotEmpty())

Log.d("NetworkMonitor", "Network lost")
}
}

val request = NetworkRequest.Builder()
.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
.build()
connectivityManager.registerNetworkCallback(request, callback)

trySend(connectivityManager.isCurrentlyConnected())

awaitClose {
connectivityManager.unregisterNetworkCallback(callback)
}
}
.flowOn(ioDispatcher)
.conflate()

private fun ConnectivityManager.isCurrentlyConnected(): Boolean {
return try {
activeNetwork
?.let(::getNetworkCapabilities)
?.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
?: false
} catch (e: Exception) {
e.printStackTrace()
false
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,14 @@ import com.naver.maps.map.compose.CameraPositionState
import com.naver.maps.map.compose.rememberCameraPositionState
import com.teamwiney.core.common.navigation.TopLevelDestination
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch

@Composable
fun rememberWineyAppState(
networkMonitor: NetworkMonitor,
isMapDetail: MutableState<Boolean> = mutableStateOf(false),
navController: NavHostController = rememberNavController(),
scaffoldState: ScaffoldState = rememberScaffoldState(),
Expand All @@ -33,6 +37,7 @@ fun rememberWineyAppState(
): WineyAppState {
return remember(Unit) {
WineyAppState(
networkMonitor,
isMapDetail,
navController,
scaffoldState,
Expand All @@ -44,6 +49,7 @@ fun rememberWineyAppState(

@Stable
class WineyAppState(
networkMonitor: NetworkMonitor,
val isMapDetail: MutableState<Boolean>,
val navController: NavHostController,
val scaffoldState: ScaffoldState,
Expand All @@ -56,6 +62,14 @@ class WineyAppState(

val topLevelDestination = TopLevelDestination.values().toList()

val isOffline = networkMonitor.isOnline
.map(Boolean::not)
.stateIn(
scope = scope,
started = SharingStarted.WhileSubscribed(5_000),
initialValue = false
)

val shouldShowBottomBar: Boolean
@Composable get() = !isMapDetail.value && currentDestination?.route ==
topLevelDestination.find { it.route == currentDestination?.route }?.route
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.teamwiney.data.di
package com.teamwiney.core.common.di

import dagger.Module
import dagger.Provides
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.teamwiney.core.common.di

import android.content.Context
import com.teamwiney.core.common.ConnectivityManagerNetworkMonitor
import com.teamwiney.core.common.NetworkMonitor
import dagger.Binds
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.components.SingletonComponent
import kotlinx.coroutines.CoroutineDispatcher

@Module
@InstallIn(SingletonComponent::class)
abstract class NetworkMonitorModule {

@Binds
abstract fun bindsNetworkMonitor(
networkMonitor: ConnectivityManagerNetworkMonitor
): NetworkMonitor

}
6 changes: 3 additions & 3 deletions core/design/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,11 @@ android {
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
kotlinOptions {
jvmTarget = "1.8"
jvmTarget = "17"
}
buildFeatures {
compose = true
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.teamwiney.data.datasource.auth

import com.teamwiney.core.common.model.SocialType
import com.teamwiney.data.di.DispatcherModule
import com.teamwiney.core.common.di.DispatcherModule
import com.teamwiney.data.network.model.request.FcmTokenRequest
import com.teamwiney.data.network.model.request.GoogleAccessTokenRequest
import com.teamwiney.data.network.model.request.PhoneNumberRequest
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.teamwiney.data.datasource.map

import com.teamwiney.core.common.base.CommonResponse
import com.teamwiney.data.di.DispatcherModule
import com.teamwiney.core.common.di.DispatcherModule
import com.teamwiney.data.network.adapter.ApiResult
import com.teamwiney.data.network.model.request.MapPosition
import com.teamwiney.data.network.model.response.BookmarkResult
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package com.teamwiney.data.datasource.tastingnote

import com.teamwiney.core.common.base.CommonResponse
import com.teamwiney.core.common.`typealias`.BaseResponse
import com.teamwiney.data.di.DispatcherModule
import com.teamwiney.core.common.di.DispatcherModule
import com.teamwiney.data.network.adapter.ApiResult
import com.teamwiney.data.network.model.response.PagingResponse
import com.teamwiney.data.network.model.response.TasteAnalysis
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.teamwiney.data.datasource.wine

import com.teamwiney.core.common.base.CommonResponse
import com.teamwiney.data.di.DispatcherModule
import com.teamwiney.core.common.di.DispatcherModule
import com.teamwiney.data.network.adapter.ApiResult
import com.teamwiney.data.network.model.response.PagingResponse
import com.teamwiney.data.network.model.response.SearchWine
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package com.teamwiney.data.datasource.winebadge

import com.teamwiney.data.di.DispatcherModule
import com.teamwiney.core.common.di.DispatcherModule
import com.teamwiney.data.network.service.WineBadgeService
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.flow.flow
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package com.teamwiney.data.datasource.winegrade

import com.teamwiney.data.di.DispatcherModule
import com.teamwiney.core.common.di.DispatcherModule
import com.teamwiney.data.network.service.WineGradeService
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.flow.flow
Expand Down
Loading

0 comments on commit 68248a1

Please sign in to comment.