From e7b0e004b38a9fa09d5f2f0a07df842a30f13fdf Mon Sep 17 00:00:00 2001 From: yunsehwan Date: Sun, 6 Oct 2024 11:38:59 +0900 Subject: [PATCH 1/3] =?UTF-8?q?[FEATURE]=20=EA=B3=B5=EC=9C=A0=ED=95=98?= =?UTF-8?q?=EA=B8=B0=20=EC=95=94=EC=8B=9C=EC=A0=81=20=EC=9D=B8=ED=85=90?= =?UTF-8?q?=ED=8A=B8=20=EC=88=98=EC=8B=A0=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/AndroidManifest.xml | 10 +++- .../main/java/pokitmons/pokit/MainActivity.kt | 39 ++++++++++--- .../java/pokitmons/pokit/MainViewModel.kt | 56 +++++++++++++++++++ .../pokit/navigation/RootDestination.kt | 2 + .../java/pokitmons/pokit/home/HomeScreen.kt | 3 + .../pokit/home/model/HomeSideEffect.kt | 1 + .../home/model/PendingSharedLinkManager.kt | 19 +++++++ .../pokit/home/pokit/PokitViewModel.kt | 10 ++++ 8 files changed, 130 insertions(+), 10 deletions(-) create mode 100644 app/src/main/java/pokitmons/pokit/MainViewModel.kt create mode 100644 feature/home/src/main/java/pokitmons/pokit/home/model/PendingSharedLinkManager.kt diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index fcab7b1f..e928a13c 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 3c78fe92..eaaf9f0a 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,11 @@ 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 +64,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 +93,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 00000000..41dad5cd --- /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 f2b1a432..d2214709 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/feature/home/src/main/java/pokitmons/pokit/home/HomeScreen.kt b/feature/home/src/main/java/pokitmons/pokit/home/HomeScreen.kt index bfa97b10..a48fea1a 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 3b0f6344..969372d7 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 00000000..068c488c --- /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 4da93d56..10de1192 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() From 65bc6f0a11362304e9440d5a0e94933327ac9483 Mon Sep 17 00:00:00 2001 From: yunsehwan Date: Sun, 6 Oct 2024 11:39:31 +0900 Subject: [PATCH 2/3] =?UTF-8?q?[FIX]=20=EC=84=A4=EC=A0=95=20=ED=99=94?= =?UTF-8?q?=EB=A9=B4=EC=97=90=EC=84=9C=20=EB=A1=9C=EA=B7=B8=EC=95=84?= =?UTF-8?q?=EC=9B=83=20=ED=9B=84=20=EB=A1=9C=EA=B7=B8=EC=9D=B8=20=ED=99=94?= =?UTF-8?q?=EB=A9=B4=EC=97=90=EC=84=9C=20=EB=92=A4=EB=A1=9C=EA=B0=80?= =?UTF-8?q?=EA=B8=B0=20=ED=81=B4=EB=A6=AD=EC=8B=9C=20=EB=A1=9C=EA=B7=B8?= =?UTF-8?q?=EC=9D=B8=20=ED=99=94=EB=A9=B4=EC=9D=B4=20=EC=A2=85=EB=A3=8C?= =?UTF-8?q?=EB=90=98=EC=A7=80=20=EC=95=8A=EB=8A=94=20=EB=AC=B8=EC=A0=9C=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/java/pokitmons/pokit/navigation/RootNavHost.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/pokitmons/pokit/navigation/RootNavHost.kt b/app/src/main/java/pokitmons/pokit/navigation/RootNavHost.kt index 3ea5bfed..439d88df 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 } } From e1f8623493479a6cc3c04ea10b8b36a1bddcf0fb Mon Sep 17 00:00:00 2001 From: yunsehwan Date: Sun, 6 Oct 2024 11:40:46 +0900 Subject: [PATCH 3/3] =?UTF-8?q?[CHORE]=20ktlint=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/java/pokitmons/pokit/MainActivity.kt | 3 ++- app/src/main/java/pokitmons/pokit/MainViewModel.kt | 6 +++--- .../main/java/pokitmons/pokit/home/model/HomeSideEffect.kt | 2 +- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/pokitmons/pokit/MainActivity.kt b/app/src/main/java/pokitmons/pokit/MainActivity.kt index eaaf9f0a..7fd8ede5 100644 --- a/app/src/main/java/pokitmons/pokit/MainActivity.kt +++ b/app/src/main/java/pokitmons/pokit/MainActivity.kt @@ -51,8 +51,9 @@ class MainActivity : ComponentActivity() { val currentDestination by remember(navBackStackEntry) { derivedStateOf { navBackStackEntry?.destination } } viewModel.navigationEvent.collectAsEffect { navigationEvent -> - if (navigationEvent is NavigationEvent.AddLink) + if (navigationEvent is NavigationEvent.AddLink) { navHostController.navigate("${AddLink.route}?${AddLink.linkUrl}=${navigationEvent.url}") + } } PokitTheme { diff --git a/app/src/main/java/pokitmons/pokit/MainViewModel.kt b/app/src/main/java/pokitmons/pokit/MainViewModel.kt index 41dad5cd..75711b8f 100644 --- a/app/src/main/java/pokitmons/pokit/MainViewModel.kt +++ b/app/src/main/java/pokitmons/pokit/MainViewModel.kt @@ -19,9 +19,9 @@ import pokitmons.pokit.navigation.ROUTE_WITHOUT_LOGIN import javax.inject.Inject @HiltViewModel -class MainViewModel @Inject constructor(): ViewModel() { +class MainViewModel @Inject constructor() : ViewModel() { private val _currentRoute: MutableStateFlow = MutableStateFlow(Login.route) - val currentRoute : StateFlow = _currentRoute.asStateFlow() + val currentRoute: StateFlow = _currentRoute.asStateFlow() private val _navigationEvent: MutableEventFlow = MutableEventFlow() val navigationEvent: EventFlow = _navigationEvent.asEventFlow() @@ -52,5 +52,5 @@ class MainViewModel @Inject constructor(): ViewModel() { } sealed class NavigationEvent { - data class AddLink(val url: String): NavigationEvent() + data class AddLink(val url: String) : NavigationEvent() } 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 969372d7..d44c77e6 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,5 +2,5 @@ package pokitmons.pokit.home.model sealed class HomeSideEffect { data object NavigateToAddPokit : HomeSideEffect() - data class NavigateToAddLink(val url: String): HomeSideEffect() + data class NavigateToAddLink(val url: String) : HomeSideEffect() }