From ae8e398e8714ee556a0aca5cd02e6299f9acf502 Mon Sep 17 00:00:00 2001 From: Sehwan Yun <39579912+l5x5l@users.noreply.github.com> Date: Tue, 8 Oct 2024 19:12:45 +0900 Subject: [PATCH] =?UTF-8?q?[Feature]=20#76=20=EC=95=94=EC=8B=9C=EC=A0=81?= =?UTF-8?q?=20=EC=9D=B8=ED=85=90=ED=8A=B8=EB=A5=BC=20=ED=86=B5=ED=95=9C=20?= =?UTF-8?q?=EB=A7=81=ED=81=AC=20=EC=A0=80=EC=9E=A5=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84=20(#77)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [FEATURE] 공유하기 암시적 인텐트 수신 기능 구현 * [FIX] 설정 화면에서 로그아웃 후 로그인 화면에서 뒤로가기 클릭시 로그인 화면이 종료되지 않는 문제 수정 * [CHORE] ktlint 적용 --- app/src/main/AndroidManifest.xml | 10 +++- .../main/java/pokitmons/pokit/MainActivity.kt | 40 ++++++++++--- .../java/pokitmons/pokit/MainViewModel.kt | 56 +++++++++++++++++++ .../pokit/navigation/RootDestination.kt | 2 + .../pokitmons/pokit/navigation/RootNavHost.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 ++++ 9 files changed, 132 insertions(+), 11 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..7fd8ede5 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 00000000..75711b8f --- /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/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 } } 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..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,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()