diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index fcab7b1..e928a13 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -18,14 +18,20 @@ android:screenOrientation="portrait" android:name=".MainActivity" android:exported="true" - android:label="@string/app_name" android:windowSoftInputMode="adjustResize" - android:theme="@style/Theme.Pokit"> + android:theme="@style/Theme.Pokit" + android:launchMode="singleInstance" + tools:ignore="DiscouragedApi,LockedOrientationActivity"> + + + + + diff --git a/app/src/main/java/pokitmons/pokit/MainActivity.kt b/app/src/main/java/pokitmons/pokit/MainActivity.kt index 3c78fe9..7fd8ede 100644 --- a/app/src/main/java/pokitmons/pokit/MainActivity.kt +++ b/app/src/main/java/pokitmons/pokit/MainActivity.kt @@ -2,10 +2,12 @@ package pokitmons.pokit import android.content.ClipData import android.content.ClipboardManager +import android.content.Intent import android.os.Build import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent +import androidx.activity.viewModels import androidx.compose.foundation.Image import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box @@ -24,14 +26,18 @@ import androidx.navigation.compose.currentBackStackEntryAsState import androidx.navigation.compose.rememberNavController import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.delay +import pokitmons.pokit.core.feature.flow.collectAsEffect import pokitmons.pokit.core.ui.theme.PokitTheme -import pokitmons.pokit.home.model.ClipboardLinkManager +import pokitmons.pokit.navigation.AddLink import pokitmons.pokit.navigation.RootNavHost @AndroidEntryPoint class MainActivity : ComponentActivity() { + private val viewModel: MainViewModel by viewModels() + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) + handleSharedLinkIntent(intent) setContent { var showSplash by remember { mutableStateOf(true) } @@ -44,6 +50,12 @@ class MainActivity : ComponentActivity() { val navBackStackEntry by navHostController.currentBackStackEntryAsState() val currentDestination by remember(navBackStackEntry) { derivedStateOf { navBackStackEntry?.destination } } + viewModel.navigationEvent.collectAsEffect { navigationEvent -> + if (navigationEvent is NavigationEvent.AddLink) { + navHostController.navigate("${AddLink.route}?${AddLink.linkUrl}=${navigationEvent.url}") + } + } + PokitTheme { if (showSplash) { SplashScreen() @@ -53,25 +65,26 @@ class MainActivity : ComponentActivity() { LaunchedEffect(currentDestination) { currentDestination?.route?.let { route -> - // 믹스패널/파베 애널리틱스 화면 이동 로깅용 + viewModel.setCurrentRoute(route) } } } } } + override fun onNewIntent(intent: Intent) { + super.onNewIntent(intent) + handleSharedLinkIntent(intent) + } + override fun onWindowFocusChanged(hasFocus: Boolean) { super.onWindowFocusChanged(hasFocus) if (hasFocus) { val clipboardManager = getSystemService(CLIPBOARD_SERVICE) as ClipboardManager clipboardManager.primaryClip?.let { clipData -> - if (clipData.itemCount == 0) return@let - val clipboardTextData = clipData.getItemAt(0).text.toString() - - if (!ClipboardLinkManager.checkUrlIsValid(clipboardTextData)) return@let - - ClipboardLinkManager.setClipboardLink(clipboardTextData) + val setClipDataSuccess = viewModel.setClipData(clipData) + if (!setClipDataSuccess) return@let if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { clipboardManager.clearPrimaryClip() } else { @@ -81,6 +94,17 @@ class MainActivity : ComponentActivity() { } } } + + private fun handleSharedLinkIntent(intent: Intent) { + val action = intent.action ?: return + + val isSharedLinkData = (action == Intent.ACTION_SEND && intent.type == "text/plain") + if (isSharedLinkData) { + intent.getStringExtra(Intent.EXTRA_TEXT)?.let { url -> + viewModel.setSharedLinkUrl(url) + } + } + } } @Composable diff --git a/app/src/main/java/pokitmons/pokit/MainViewModel.kt b/app/src/main/java/pokitmons/pokit/MainViewModel.kt new file mode 100644 index 0000000..75711b8 --- /dev/null +++ b/app/src/main/java/pokitmons/pokit/MainViewModel.kt @@ -0,0 +1,56 @@ +package pokitmons.pokit + +import android.content.ClipData +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.update +import kotlinx.coroutines.launch +import pokitmons.pokit.core.feature.flow.EventFlow +import pokitmons.pokit.core.feature.flow.MutableEventFlow +import pokitmons.pokit.core.feature.flow.asEventFlow +import pokitmons.pokit.home.model.ClipboardLinkManager +import pokitmons.pokit.home.model.PendingSharedLinkManager +import pokitmons.pokit.navigation.Login +import pokitmons.pokit.navigation.ROUTE_WITHOUT_LOGIN +import javax.inject.Inject + +@HiltViewModel +class MainViewModel @Inject constructor() : ViewModel() { + private val _currentRoute: MutableStateFlow = MutableStateFlow(Login.route) + val currentRoute: StateFlow = _currentRoute.asStateFlow() + + private val _navigationEvent: MutableEventFlow = MutableEventFlow() + val navigationEvent: EventFlow = _navigationEvent.asEventFlow() + + fun setCurrentRoute(route: String) { + _currentRoute.update { route } + } + + fun setClipData(clipData: ClipData): Boolean { + if (clipData.itemCount == 0) return false + val clipboardTextData = clipData.getItemAt(0).text.toString() + + if (!ClipboardLinkManager.checkUrlIsValid(clipboardTextData)) return false + + ClipboardLinkManager.setClipboardLink(clipboardTextData) + return true + } + + fun setSharedLinkUrl(url: String) { + if (currentRoute.value in ROUTE_WITHOUT_LOGIN) { + PendingSharedLinkManager.setSharedLink(url) + } else { + viewModelScope.launch { + _navigationEvent.emit(NavigationEvent.AddLink(url)) + } + } + } +} + +sealed class NavigationEvent { + data class AddLink(val url: String) : NavigationEvent() +} diff --git a/app/src/main/java/pokitmons/pokit/navigation/RootDestination.kt b/app/src/main/java/pokitmons/pokit/navigation/RootDestination.kt index f2b1a43..d221470 100644 --- a/app/src/main/java/pokitmons/pokit/navigation/RootDestination.kt +++ b/app/src/main/java/pokitmons/pokit/navigation/RootDestination.kt @@ -3,6 +3,8 @@ package pokitmons.pokit.navigation import androidx.navigation.NavType import androidx.navigation.navArgument +val ROUTE_WITHOUT_LOGIN = listOf(Login.route, TermOfService.route, InputNickname.route, SelectKeyword.route, SignUpSuccess.route) + object Login { val route: String = "login" } diff --git a/app/src/main/java/pokitmons/pokit/navigation/RootNavHost.kt b/app/src/main/java/pokitmons/pokit/navigation/RootNavHost.kt index 3ea5bfe..439d88d 100644 --- a/app/src/main/java/pokitmons/pokit/navigation/RootNavHost.kt +++ b/app/src/main/java/pokitmons/pokit/navigation/RootNavHost.kt @@ -154,7 +154,7 @@ fun RootNavHost( onNavigateToEditNickname = { navHostController.navigate(EditNickname.route) }, onNavigateToLogin = { navHostController.navigate(Login.route) { - popUpTo(navHostController.graph.startDestinationId) { + popUpTo(navHostController.graph.id) { inclusive = true } } diff --git a/feature/home/src/main/java/pokitmons/pokit/home/HomeScreen.kt b/feature/home/src/main/java/pokitmons/pokit/home/HomeScreen.kt index bfa97b1..a48fea1 100644 --- a/feature/home/src/main/java/pokitmons/pokit/home/HomeScreen.kt +++ b/feature/home/src/main/java/pokitmons/pokit/home/HomeScreen.kt @@ -70,6 +70,9 @@ fun HomeScreen( HomeSideEffect.NavigateToAddPokit -> { onNavigateAddPokit() } + is HomeSideEffect.NavigateToAddLink -> { + onNavigateAddLink(homeSideEffect.url) + } } } diff --git a/feature/home/src/main/java/pokitmons/pokit/home/model/HomeSideEffect.kt b/feature/home/src/main/java/pokitmons/pokit/home/model/HomeSideEffect.kt index 3b0f634..d44c77e 100644 --- a/feature/home/src/main/java/pokitmons/pokit/home/model/HomeSideEffect.kt +++ b/feature/home/src/main/java/pokitmons/pokit/home/model/HomeSideEffect.kt @@ -2,4 +2,5 @@ package pokitmons.pokit.home.model sealed class HomeSideEffect { data object NavigateToAddPokit : HomeSideEffect() + data class NavigateToAddLink(val url: String) : HomeSideEffect() } diff --git a/feature/home/src/main/java/pokitmons/pokit/home/model/PendingSharedLinkManager.kt b/feature/home/src/main/java/pokitmons/pokit/home/model/PendingSharedLinkManager.kt new file mode 100644 index 0000000..068c488 --- /dev/null +++ b/feature/home/src/main/java/pokitmons/pokit/home/model/PendingSharedLinkManager.kt @@ -0,0 +1,19 @@ +package pokitmons.pokit.home.model + +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import pokitmons.pokit.core.feature.flow.EventFlow +import pokitmons.pokit.core.feature.flow.MutableEventFlow +import pokitmons.pokit.core.feature.flow.asEventFlow + +object PendingSharedLinkManager { + private val _sharedLinkUrl: MutableEventFlow = MutableEventFlow() + val sharedLinkUrl: EventFlow = _sharedLinkUrl.asEventFlow() + + fun setSharedLink(linkUrl: String) { + CoroutineScope(Dispatchers.IO).launch { + _sharedLinkUrl.emit(linkUrl) + } + } +} diff --git a/feature/home/src/main/java/pokitmons/pokit/home/pokit/PokitViewModel.kt b/feature/home/src/main/java/pokitmons/pokit/home/pokit/PokitViewModel.kt index 4da93d5..10de119 100644 --- a/feature/home/src/main/java/pokitmons/pokit/home/pokit/PokitViewModel.kt +++ b/feature/home/src/main/java/pokitmons/pokit/home/pokit/PokitViewModel.kt @@ -34,6 +34,7 @@ import pokitmons.pokit.home.model.ClipboardLinkManager import pokitmons.pokit.home.model.HomeSideEffect import pokitmons.pokit.home.model.HomeToastMessage import pokitmons.pokit.home.model.LinkAddToastMessage +import pokitmons.pokit.home.model.PendingSharedLinkManager import javax.inject.Inject import kotlin.math.max import com.strayalpaca.pokitdetail.model.Link as DetailLink @@ -107,6 +108,14 @@ class PokitViewModel @Inject constructor( } } + private fun initPendingSharedLinkUrlDetector() { + viewModelScope.launch { + PendingSharedLinkManager.sharedLinkUrl.collectLatest { linkUrl -> + _sideEffect.emit(HomeSideEffect.NavigateToAddLink(linkUrl)) + } + } + } + private fun initPokitUpdateEventDetector() { viewModelScope.launch { PokitUpdateEvent.updatedPokit.collectLatest { updatedPokit -> @@ -239,6 +248,7 @@ class PokitViewModel @Inject constructor( initPokitAddEventDetector() initLinkRemoveEventDetector() initClipboardLinkUrlDetector() + initPendingSharedLinkUrlDetector() loadUnCategoryLinks() loadPokits()