Skip to content

Commit f1b39eb

Browse files
authored
Merge pull request #174 from YAPP-Github/feat/#173-share-receive-photo
[feat] #173 외부 앱에서 이미지 공유 수신 기능 추가
2 parents dbc33b2 + 01aa339 commit f1b39eb

5 files changed

Lines changed: 64 additions & 1 deletion

File tree

app/src/main/AndroidManifest.xml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,16 @@
2929
<action android:name="android.intent.action.MAIN" />
3030
<category android:name="android.intent.category.LAUNCHER" />
3131
</intent-filter>
32+
<intent-filter>
33+
<action android:name="android.intent.action.SEND" />
34+
<category android:name="android.intent.category.DEFAULT" />
35+
<data android:mimeType="image/*" />
36+
</intent-filter>
37+
<intent-filter>
38+
<action android:name="android.intent.action.SEND_MULTIPLE" />
39+
<category android:name="android.intent.category.DEFAULT" />
40+
<data android:mimeType="image/*" />
41+
</intent-filter>
3242
</activity>
3343

3444
<activity

app/src/main/java/com/neki/android/app/MainActivity.kt

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.neki.android.app
22

3+
import android.content.Intent
34
import android.graphics.Color
45
import android.os.Bundle
56
import android.widget.Toast
@@ -8,7 +9,10 @@ import androidx.activity.SystemBarStyle
89
import androidx.activity.compose.setContent
910
import androidx.activity.enableEdgeToEdge
1011
import androidx.compose.runtime.CompositionLocalProvider
12+
import androidx.compose.runtime.getValue
13+
import androidx.compose.runtime.mutableStateOf
1114
import androidx.compose.runtime.remember
15+
import androidx.compose.runtime.setValue
1216
import androidx.lifecycle.lifecycleScope
1317
import androidx.navigation3.runtime.entryProvider
1418
import com.neki.android.app.main.MainRoute
@@ -29,10 +33,30 @@ import com.neki.android.feature.auth.api.AuthNavKey
2933
import com.neki.android.feature.auth.impl.navigation.authEntryProvider
3034
import com.neki.android.feature.photo_upload.api.navigateToQRScan
3135
import com.neki.android.feature.photo_upload.api.navigateToUploadAlbum
36+
import android.net.Uri
37+
import androidx.core.content.IntentCompat
3238
import dagger.hilt.android.AndroidEntryPoint
39+
import kotlinx.collections.immutable.ImmutableList
40+
import kotlinx.collections.immutable.persistentListOf
41+
import kotlinx.collections.immutable.toImmutableList
3342
import kotlinx.coroutines.launch
3443
import javax.inject.Inject
3544

45+
private fun Intent.extractShareUriStrings(): ImmutableList<String> {
46+
val uris: List<Uri> = when (action) {
47+
Intent.ACTION_SEND -> {
48+
IntentCompat.getParcelableExtra(this, Intent.EXTRA_STREAM, Uri::class.java)
49+
?.let { listOf(it) } ?: emptyList()
50+
}
51+
Intent.ACTION_SEND_MULTIPLE -> {
52+
IntentCompat.getParcelableArrayListExtra(this, Intent.EXTRA_STREAM, Uri::class.java)
53+
?: emptyList()
54+
}
55+
else -> emptyList()
56+
}
57+
return uris.map { it.toString() }.toImmutableList()
58+
}
59+
3660
@AndroidEntryPoint
3761
class MainActivity : ComponentActivity() {
3862

@@ -54,9 +78,13 @@ class MainActivity : ComponentActivity() {
5478
@Inject
5579
lateinit var authEventManager: AuthEventManager
5680

81+
private var pendingShareUriStrings by mutableStateOf<ImmutableList<String>>(persistentListOf())
82+
5783
override fun onCreate(savedInstanceState: Bundle?) {
5884
super.onCreate(savedInstanceState)
5985

86+
pendingShareUriStrings = intent.extractShareUriStrings()
87+
6088
enableEdgeToEdge(
6189
navigationBarStyle = SystemBarStyle.auto(
6290
lightScrim = Color.TRANSPARENT,
@@ -97,6 +125,8 @@ class MainActivity : ComponentActivity() {
97125
navigateToQRScan = mainNavigator::navigateToQRScan,
98126
navigateToUploadAlbumWithGallery = mainNavigator::navigateToUploadAlbum,
99127
navigateToUploadAlbumWithQRScan = mainNavigator::navigateToUploadAlbum,
128+
pendingShareUriStrings = pendingShareUriStrings,
129+
onShareUrisConsumed = { pendingShareUriStrings = persistentListOf() },
100130
)
101131
}
102132
}
@@ -107,6 +137,14 @@ class MainActivity : ComponentActivity() {
107137
observeAuthEvents()
108138
}
109139

140+
override fun onNewIntent(intent: Intent) {
141+
super.onNewIntent(intent)
142+
val uriStrings = intent.extractShareUriStrings()
143+
if (uriStrings.isNotEmpty()) {
144+
pendingShareUriStrings = uriStrings
145+
}
146+
}
147+
110148
private fun observeAuthEvents() {
111149
lifecycleScope.launch {
112150
authEventManager.authEvent.collect { event ->

app/src/main/java/com/neki/android/app/main/MainContract.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ sealed interface MainIntent {
1818
data object ClickQRScan : MainIntent
1919
data object ClickGalleryUpload : MainIntent
2020
data class SelectGalleryImage(val uris: List<Uri>) : MainIntent
21+
data class ShareImageReceived(val uris: List<Uri>) : MainIntent
2122
data class QRCodeScanned(val imageUrl: String) : MainIntent
2223
data object DismissSelectWithAlbumDialog : MainIntent
2324
data object ClickUploadWithAlbum : MainIntent

app/src/main/java/com/neki/android/app/main/MainScreen.kt

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import androidx.compose.foundation.layout.PaddingValues
1010
import androidx.compose.foundation.layout.fillMaxSize
1111
import androidx.compose.foundation.layout.navigationBarsPadding
1212
import androidx.compose.foundation.layout.padding
13+
import androidx.compose.runtime.LaunchedEffect
1314
import androidx.compose.material3.Scaffold
1415
import androidx.compose.runtime.Composable
1516
import androidx.compose.runtime.getValue
@@ -32,6 +33,9 @@ import com.neki.android.core.navigation.result.ResultEffect
3233
import com.neki.android.core.ui.component.LoadingDialog
3334
import com.neki.android.core.ui.compose.collectWithLifecycle
3435
import com.neki.android.core.ui.toast.NekiToast
36+
import androidx.core.net.toUri
37+
import kotlinx.collections.immutable.ImmutableList
38+
import kotlinx.collections.immutable.persistentListOf
3539
import com.neki.android.feature.archive.api.ArchiveNavKey
3640
import com.neki.android.feature.archive.api.ArchiveResult
3741
import com.neki.android.feature.map.api.MapNavKey
@@ -52,6 +56,8 @@ fun MainRoute(
5256
navigateToQRScan: () -> Unit,
5357
navigateToUploadAlbumWithGallery: (List<String>) -> Unit,
5458
navigateToUploadAlbumWithQRScan: (String) -> Unit,
59+
pendingShareUriStrings: ImmutableList<String> = persistentListOf(),
60+
onShareUrisConsumed: () -> Unit = {},
5561
viewModel: MainViewModel = hiltViewModel(),
5662
) {
5763
val uiState by viewModel.store.uiState.collectAsStateWithLifecycle()
@@ -69,6 +75,13 @@ fun MainRoute(
6975
}
7076
}
7177

78+
LaunchedEffect(pendingShareUriStrings) {
79+
if (pendingShareUriStrings.isNotEmpty()) {
80+
viewModel.store.onIntent(MainIntent.ShareImageReceived(pendingShareUriStrings.map { it.toUri() }))
81+
onShareUrisConsumed()
82+
}
83+
}
84+
7285
ResultEffect<QRScanResult>(resultBus) { result ->
7386
when (result) {
7487
is QRScanResult.QRCodeScanned -> viewModel.store.onIntent(MainIntent.QRCodeScanned(result.imageUrl))
@@ -153,7 +166,7 @@ fun MainScreen(
153166
)
154167
}
155168

156-
if (uiState.isLoading) {
169+
if (uiState.isLoading && !uiState.isShowSelectWithAlbumDialog) {
157170
LoadingDialog()
158171
}
159172

app/src/main/java/com/neki/android/app/main/MainViewModel.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ class MainViewModel @Inject constructor(
4444
postSideEffect(MainSideEffect.OpenGallery)
4545
}
4646
is MainIntent.SelectGalleryImage -> reduce { copy(isShowSelectWithAlbumDialog = true, selectedUris = intent.uris.toImmutableList()) }
47+
is MainIntent.ShareImageReceived -> reduce { copy(isShowSelectWithAlbumDialog = true, selectedUris = intent.uris.toImmutableList()) }
4748
is MainIntent.QRCodeScanned -> reduce { copy(scannedImageUrl = intent.imageUrl, isShowSelectWithAlbumDialog = true) }
4849
MainIntent.DismissSelectWithAlbumDialog -> reduce {
4950
copy(

0 commit comments

Comments
 (0)