Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feature] #76 암시적 인텐트를 통한 링크 저장 기능 구현 #77

Merged
merged 3 commits into from
Oct 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -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">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.SEND"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:mimeType="text/plain"/>
</intent-filter>
</activity>
</application>

Expand Down
40 changes: 32 additions & 8 deletions app/src/main/java/pokitmons/pokit/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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) }

Expand All @@ -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()
Expand All @@ -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 {
Expand All @@ -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
Expand Down
56 changes: 56 additions & 0 deletions app/src/main/java/pokitmons/pokit/MainViewModel.kt
Original file line number Diff line number Diff line change
@@ -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<String> = MutableStateFlow(Login.route)
val currentRoute: StateFlow<String> = _currentRoute.asStateFlow()

private val _navigationEvent: MutableEventFlow<NavigationEvent> = MutableEventFlow()
val navigationEvent: EventFlow<NavigationEvent> = _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()
}
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
}
Expand Down
3 changes: 3 additions & 0 deletions feature/home/src/main/java/pokitmons/pokit/home/HomeScreen.kt
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,9 @@ fun HomeScreen(
HomeSideEffect.NavigateToAddPokit -> {
onNavigateAddPokit()
}
is HomeSideEffect.NavigateToAddLink -> {
onNavigateAddLink(homeSideEffect.url)
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ package pokitmons.pokit.home.model

sealed class HomeSideEffect {
data object NavigateToAddPokit : HomeSideEffect()
data class NavigateToAddLink(val url: String) : HomeSideEffect()
}
Original file line number Diff line number Diff line change
@@ -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<String> = MutableEventFlow()
val sharedLinkUrl: EventFlow<String> = _sharedLinkUrl.asEventFlow()

fun setSharedLink(linkUrl: String) {
CoroutineScope(Dispatchers.IO).launch {
_sharedLinkUrl.emit(linkUrl)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 ->
Expand Down Expand Up @@ -239,6 +248,7 @@ class PokitViewModel @Inject constructor(
initPokitAddEventDetector()
initLinkRemoveEventDetector()
initClipboardLinkUrlDetector()
initPendingSharedLinkUrlDetector()

loadUnCategoryLinks()
loadPokits()
Expand Down
Loading