diff --git a/presentation/src/debug/AndroidManifest.xml b/presentation/src/debug/AndroidManifest.xml new file mode 100644 index 0000000..589b308 --- /dev/null +++ b/presentation/src/debug/AndroidManifest.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + diff --git a/presentation/src/debug/kotlin/com/ray/template/android/presentation/ui/main/DebugMainActivity.kt b/presentation/src/debug/kotlin/com/ray/template/android/presentation/ui/main/DebugMainActivity.kt new file mode 100644 index 0000000..2584afb --- /dev/null +++ b/presentation/src/debug/kotlin/com/ray/template/android/presentation/ui/main/DebugMainActivity.kt @@ -0,0 +1,16 @@ +package com.ray.template.android.presentation.ui.main + +import android.os.Bundle +import androidx.activity.compose.setContent +import androidx.appcompat.app.AppCompatActivity +import dagger.hilt.android.AndroidEntryPoint + +@AndroidEntryPoint +class DebugMainActivity : AppCompatActivity() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContent { + DebugMainScreen() + } + } +} diff --git a/presentation/src/debug/kotlin/com/ray/template/android/presentation/ui/main/DebugMainScreen.kt b/presentation/src/debug/kotlin/com/ray/template/android/presentation/ui/main/DebugMainScreen.kt new file mode 100644 index 0000000..dfd5586 --- /dev/null +++ b/presentation/src/debug/kotlin/com/ray/template/android/presentation/ui/main/DebugMainScreen.kt @@ -0,0 +1,81 @@ +package com.ray.template.android.presentation.ui.main + +import androidx.compose.foundation.gestures.detectDragGestures +import androidx.compose.foundation.layout.offset +import androidx.compose.foundation.layout.size +import androidx.compose.material3.Icon +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableFloatStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color.Companion.Blue +import androidx.compose.ui.input.pointer.pointerInput +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.unit.IntOffset +import androidx.hilt.navigation.compose.hiltViewModel +import androidx.navigation.NavController +import androidx.navigation.compose.NavHost +import androidx.navigation.compose.rememberNavController +import com.ray.template.android.presentation.R +import com.ray.template.android.presentation.common.theme.Space40 +import com.ray.template.android.presentation.common.theme.TemplateTheme +import com.ray.template.android.presentation.common.util.compose.ErrorObserver +import com.ray.template.android.presentation.common.util.compose.safeNavigate +import com.ray.template.android.presentation.common.view.RippleBox +import com.ray.template.android.presentation.ui.main.debug.DebugConstant +import com.ray.template.android.presentation.ui.main.debug.debugDestination +import com.ray.template.android.presentation.ui.main.splash.SplashConstant +import kotlin.math.roundToInt + +@Composable +fun DebugMainScreen( + viewModel: MainViewModel = hiltViewModel() +) { + TemplateTheme { + val navController = rememberNavController() + + NavHost( + navController = navController, + startDestination = SplashConstant.ROUTE + ) { + mainDestination(navController) + debugDestination(navController) + } + + ErrorObserver(viewModel) + MainScreenRefreshFailDialog(navController, viewModel.refreshFailEvent) + DebugPopup(navController) + } +} + +@Composable +private fun DebugPopup( + navController: NavController +) { + var offsetX by remember { mutableFloatStateOf(0f) } + var offsetY by remember { mutableFloatStateOf(0f) } + + RippleBox( + modifier = Modifier + .offset { IntOffset(offsetX.roundToInt(), offsetY.roundToInt()) } + .pointerInput(Unit) { + detectDragGestures { change, dragAmount -> + change.consume() + offsetX += dragAmount.x + offsetY += dragAmount.y + } + }, + onClick = { + navController.safeNavigate(DebugConstant.ROUTE) + } + ) { + Icon( + modifier = Modifier.size(Space40), + painter = painterResource(R.drawable.ic_more_vertical), + contentDescription = null, + tint = Blue + ) + } +} diff --git a/presentation/src/debug/kotlin/com/ray/template/android/presentation/ui/main/debug/DebugArgument.kt b/presentation/src/debug/kotlin/com/ray/template/android/presentation/ui/main/debug/DebugArgument.kt new file mode 100644 index 0000000..a3b7ea6 --- /dev/null +++ b/presentation/src/debug/kotlin/com/ray/template/android/presentation/ui/main/debug/DebugArgument.kt @@ -0,0 +1,23 @@ +package com.ray.template.android.presentation.ui.main.debug + +import androidx.compose.runtime.Immutable +import com.ray.template.android.common.util.coroutine.event.EventFlow +import kotlinx.coroutines.CoroutineExceptionHandler + +@Immutable +data class DebugArgument( + val state: DebugState, + val event: EventFlow, + val intent: (DebugIntent) -> Unit, + val logEvent: (eventName: String, params: Map) -> Unit, + val handler: CoroutineExceptionHandler +) + +sealed interface DebugState { + data object Init : DebugState +} + + +sealed interface DebugEvent + +sealed interface DebugIntent diff --git a/presentation/src/debug/kotlin/com/ray/template/android/presentation/ui/main/debug/DebugConstant.kt b/presentation/src/debug/kotlin/com/ray/template/android/presentation/ui/main/debug/DebugConstant.kt new file mode 100644 index 0000000..e2e55a8 --- /dev/null +++ b/presentation/src/debug/kotlin/com/ray/template/android/presentation/ui/main/debug/DebugConstant.kt @@ -0,0 +1,5 @@ +package com.ray.template.android.presentation.ui.main.debug + +object DebugConstant { + const val ROUTE = "debug" +} diff --git a/presentation/src/debug/kotlin/com/ray/template/android/presentation/ui/main/debug/DebugDestination.kt b/presentation/src/debug/kotlin/com/ray/template/android/presentation/ui/main/debug/DebugDestination.kt new file mode 100644 index 0000000..d04141b --- /dev/null +++ b/presentation/src/debug/kotlin/com/ray/template/android/presentation/ui/main/debug/DebugDestination.kt @@ -0,0 +1,37 @@ +package com.ray.template.android.presentation.ui.main.debug + +import androidx.compose.runtime.getValue +import androidx.hilt.navigation.compose.hiltViewModel +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import androidx.navigation.NavController +import androidx.navigation.NavGraphBuilder +import androidx.navigation.compose.composable +import com.ray.template.android.presentation.common.util.compose.ErrorObserver + +fun NavGraphBuilder.debugDestination( + navController: NavController +) { + composable( + route = DebugConstant.ROUTE + ) { + val viewModel: DebugViewModel = hiltViewModel() + + val argument: DebugArgument = let { + val state by viewModel.state.collectAsStateWithLifecycle() + + DebugArgument( + state = state, + event = viewModel.event, + intent = viewModel::onIntent, + logEvent = viewModel::logEvent, + handler = viewModel.handler + ) + } + + ErrorObserver(viewModel) + DebugScreen( + navController = navController, + argument = argument + ) + } +} diff --git a/presentation/src/debug/kotlin/com/ray/template/android/presentation/ui/main/debug/DebugScreen.kt b/presentation/src/debug/kotlin/com/ray/template/android/presentation/ui/main/debug/DebugScreen.kt new file mode 100644 index 0000000..22882ea --- /dev/null +++ b/presentation/src/debug/kotlin/com/ray/template/android/presentation/ui/main/debug/DebugScreen.kt @@ -0,0 +1,63 @@ +package com.ray.template.android.presentation.ui.main.debug + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.tooling.preview.Preview +import androidx.navigation.NavController +import androidx.navigation.compose.rememberNavController +import com.ray.template.android.common.util.coroutine.event.MutableEventFlow +import com.ray.template.android.common.util.coroutine.event.eventObserve +import com.ray.template.android.presentation.common.theme.Gray900 +import com.ray.template.android.presentation.common.theme.Headline0 +import com.ray.template.android.presentation.common.util.compose.LaunchedEffectWithLifecycle +import kotlinx.coroutines.CoroutineExceptionHandler +import kotlinx.coroutines.plus + +@Composable +fun DebugScreen( + navController: NavController, + argument: DebugArgument, +) { + val (state, event, intent, logEvent, handler) = argument + val scope = rememberCoroutineScope() + handler + val context = LocalContext.current + + Column( + modifier = Modifier.fillMaxSize(), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.Center + ) { + Text( + text = "Debug Page", + style = Headline0.merge(Gray900) + ) + } + + LaunchedEffectWithLifecycle(event, handler) { + event.eventObserve { event -> + + } + } +} + +@Preview +@Composable +private fun DebugScreenPreview() { + DebugScreen( + navController = rememberNavController(), + argument = DebugArgument( + state = DebugState.Init, + event = MutableEventFlow(), + intent = {}, + logEvent = { _, _ -> }, + handler = CoroutineExceptionHandler { _, _ -> } + ) + ) +} diff --git a/presentation/src/debug/kotlin/com/ray/template/android/presentation/ui/main/debug/DebugViewModel.kt b/presentation/src/debug/kotlin/com/ray/template/android/presentation/ui/main/debug/DebugViewModel.kt new file mode 100644 index 0000000..ce8ba6a --- /dev/null +++ b/presentation/src/debug/kotlin/com/ray/template/android/presentation/ui/main/debug/DebugViewModel.kt @@ -0,0 +1,34 @@ +package com.ray.template.android.presentation.ui.main.debug + +import androidx.lifecycle.SavedStateHandle +import com.ray.template.android.common.util.coroutine.event.EventFlow +import com.ray.template.android.common.util.coroutine.event.MutableEventFlow +import com.ray.template.android.common.util.coroutine.event.asEventFlow +import com.ray.template.android.presentation.common.base.BaseViewModel +import dagger.hilt.android.lifecycle.HiltViewModel +import javax.inject.Inject +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow + +@HiltViewModel +class DebugViewModel @Inject constructor( + private val savedStateHandle: SavedStateHandle +) : BaseViewModel() { + + private val _state: MutableStateFlow = MutableStateFlow(DebugState.Init) + val state: StateFlow = _state.asStateFlow() + + private val _event: MutableEventFlow = MutableEventFlow() + val event: EventFlow = _event.asEventFlow() + + init { + launch { + + } + } + + fun onIntent(intent: DebugIntent) { + + } +} diff --git a/presentation/src/main/kotlin/com/ray/template/android/presentation/ui/main/MainDestination.kt b/presentation/src/main/kotlin/com/ray/template/android/presentation/ui/main/MainDestination.kt new file mode 100644 index 0000000..ec1f0ea --- /dev/null +++ b/presentation/src/main/kotlin/com/ray/template/android/presentation/ui/main/MainDestination.kt @@ -0,0 +1,15 @@ +package com.ray.template.android.presentation.ui.main + +import androidx.navigation.NavController +import androidx.navigation.NavGraphBuilder +import com.ray.template.android.presentation.ui.main.home.homeDestination +import com.ray.template.android.presentation.ui.main.nonlogin.nonLoginNavGraphNavGraph +import com.ray.template.android.presentation.ui.main.splash.splashDestination + +fun NavGraphBuilder.mainDestination( + navController: NavController +) { + splashDestination(navController = navController) + nonLoginNavGraphNavGraph(navController = navController) + homeDestination(navController = navController) +} diff --git a/presentation/src/main/kotlin/com/ray/template/android/presentation/ui/main/MainScreen.kt b/presentation/src/main/kotlin/com/ray/template/android/presentation/ui/main/MainScreen.kt index cec1def..794864a 100644 --- a/presentation/src/main/kotlin/com/ray/template/android/presentation/ui/main/MainScreen.kt +++ b/presentation/src/main/kotlin/com/ray/template/android/presentation/ui/main/MainScreen.kt @@ -18,10 +18,7 @@ import com.ray.template.android.presentation.common.util.compose.ErrorObserver import com.ray.template.android.presentation.common.util.compose.LaunchedEffectWithLifecycle import com.ray.template.android.presentation.common.util.compose.safeNavigate import com.ray.template.android.presentation.common.view.DialogScreen -import com.ray.template.android.presentation.ui.main.home.homeDestination -import com.ray.template.android.presentation.ui.main.nonlogin.nonLoginNavGraphNavGraph import com.ray.template.android.presentation.ui.main.splash.SplashConstant -import com.ray.template.android.presentation.ui.main.splash.splashDestination @Composable fun MainScreen( @@ -30,17 +27,15 @@ fun MainScreen( TemplateTheme { val navController = rememberNavController() - ErrorObserver(viewModel) - MainScreenRefreshFailDialog(navController, viewModel.refreshFailEvent) - NavHost( navController = navController, startDestination = SplashConstant.ROUTE ) { - splashDestination(navController = navController) - nonLoginNavGraphNavGraph(navController = navController) - homeDestination(navController = navController) + mainDestination(navController) } + + ErrorObserver(viewModel) + MainScreenRefreshFailDialog(navController, viewModel.refreshFailEvent) } }