diff --git a/feature/webview/src/main/java/com/nexters/misik/webview/MainActivity.kt b/feature/webview/src/main/java/com/nexters/misik/webview/MainActivity.kt index 60cba9f..f8f4a9f 100644 --- a/feature/webview/src/main/java/com/nexters/misik/webview/MainActivity.kt +++ b/feature/webview/src/main/java/com/nexters/misik/webview/MainActivity.kt @@ -1,19 +1,13 @@ package com.nexters.misik.webview import android.os.Bundle -import android.webkit.WebChromeClient -import android.webkit.WebView -import android.webkit.WebViewClient import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.activity.enableEdgeToEdge import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding import androidx.compose.material3.Scaffold -import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier -import androidx.compose.ui.viewinterop.AndroidView -import com.nexters.misik.webview.MainActivity.Companion.WEB_URL import dagger.hilt.android.AndroidEntryPoint import javax.inject.Inject @@ -29,31 +23,8 @@ class MainActivity : ComponentActivity() { setContent { Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding -> - WebViewScreen(webAppInterface, modifier = Modifier.padding(innerPadding)) + WebViewScreen(webAppInterface = webAppInterface, modifier = Modifier.padding(innerPadding)) } } } - - companion object { - const val WEB_URL = "https://misik-web.vercel.app/" - } -} - -@Composable -fun WebViewScreen( - webAppInterface: WebInterface, - modifier: Modifier = Modifier, -) { - AndroidView( - modifier = modifier, - factory = { context -> - WebView(context).apply { - settings.javaScriptEnabled = true - webViewClient = WebViewClient() - webChromeClient = WebChromeClient() - addJavascriptInterface(webAppInterface, "AndroidBridge") - loadUrl(WEB_URL) - } - }, - ) } diff --git a/feature/webview/src/main/java/com/nexters/misik/webview/WebViewScreen.kt b/feature/webview/src/main/java/com/nexters/misik/webview/WebViewScreen.kt new file mode 100644 index 0000000..89e00db --- /dev/null +++ b/feature/webview/src/main/java/com/nexters/misik/webview/WebViewScreen.kt @@ -0,0 +1,92 @@ +package com.nexters.misik.webview + +import android.webkit.WebChromeClient +import android.webkit.WebResourceError +import android.webkit.WebResourceRequest +import android.webkit.WebView +import android.webkit.WebViewClient +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.material3.CircularProgressIndicator +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.viewinterop.AndroidView +import androidx.hilt.navigation.compose.hiltViewModel +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import timber.log.Timber + +@Composable +fun WebViewScreen( + webAppInterface: WebInterface, + modifier: Modifier = Modifier, + viewModel: WebViewViewModel = hiltViewModel(), +) { + val state by viewModel.state.collectAsStateWithLifecycle() + + // 페이지 로딩 이벤트 발생 (WebView 로드 시작 시) + LaunchedEffect(Unit) { + viewModel.onEvent(WebViewEvent.LoadPage) + } + + Box(modifier = modifier.fillMaxSize()) { + // 로딩 상태 UI + if (state.isLoading) { + Timber.d("WebViewScreen_UiState", "Loading") + CircularProgressIndicator( + modifier = Modifier.align(Alignment.Center), + ) + } + + // 에러 상태 UI + state.error?.let { + Timber.d("WebViewScreen_UiState", "Error: $it") + Text( + text = "Error: $it", + color = Color.Red, + modifier = Modifier.align(Alignment.Center), + ) + } + + // 콘텐츠가 있을 경우, WebView를 보여줌 + if (!state.isLoading) { + Timber.d("WebViewScreen_UiState", "Loaded") + AndroidView( + modifier = Modifier.fillMaxSize(), + factory = { context -> + WebView(context).apply { + settings.javaScriptEnabled = true + webViewClient = WebViewClient() + webChromeClient = WebChromeClient() + + addJavascriptInterface(webAppInterface, "AndroidBridge") + + loadUrl("https://misik-web.vercel.app/") + + // 페이지 로딩 완료 후 이벤트 처리 + webViewClient = object : WebViewClient() { + override fun onPageFinished(view: WebView?, url: String?) { + super.onPageFinished(view, url) + viewModel.onEvent(WebViewEvent.PageLoaded) + } + + override fun onReceivedError( + view: WebView?, + request: WebResourceRequest?, + error: WebResourceError?, + ) { + super.onReceivedError(view, request, error) + // 오류 발생 시 이벤트 호출 + viewModel.onEvent(WebViewEvent.JsError("Error loading page: ${error?.description}")) + } + } + } + }, + ) + } + } +} diff --git a/feature/webview/src/main/java/com/nexters/misik/webview/WebViewViewModel.kt b/feature/webview/src/main/java/com/nexters/misik/webview/WebViewViewModel.kt new file mode 100644 index 0000000..1cc4fb8 --- /dev/null +++ b/feature/webview/src/main/java/com/nexters/misik/webview/WebViewViewModel.kt @@ -0,0 +1,30 @@ +package com.nexters.misik.webview + +import androidx.lifecycle.ViewModel +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import javax.inject.Inject + +@HiltViewModel +class WebViewViewModel @Inject constructor() : ViewModel() { + private val _state = MutableStateFlow(WebViewState()) + val state: StateFlow get() = _state + + fun onEvent(event: WebViewEvent) { + when (event) { + WebViewEvent.LoadPage -> { + _state.value = _state.value.copy(isLoading = true, error = null) + } + WebViewEvent.PageLoaded -> { + _state.value = _state.value.copy(isLoading = false, error = null) + } + is WebViewEvent.JsResponse -> { + _state.value = _state.value.copy(isLoading = false, content = event.response, error = null) + } + is WebViewEvent.JsError -> { + _state.value = _state.value.copy(isLoading = false, error = event.error) + } + } + } +}