Engineering note · Android

앱 선택기에서 어떤 앱을 선택했는지 확인하기

2026년 01월 29일이현수
태그androidintentchooserbroadcast-receiver

안드로이드 Intent.createChooser 사용 시 BroadcastReceiver와 IntentSender를 활용해 유저가 선택한 앱의 패키지명을 수신하는 방법과 URI 분석의 한계 정리.

안드로이드 웹뷰나 일반 앱에서 사진 및 파일을 업로드할 때 Intent.createChooser로 시스템 앱 선택기를 띄운다. 구글 포토, 내 파일, 기본 갤러리, 또는 커스텀 카메라 중 어떤 앱을 선택해서 사진을 가져왔는지 구분이 필요했다.

처음 시도했던 방식은 결과를 리턴받은 Uri의 Provider 스킴 패턴을 분석하는 것이었다.

text
// 구글 포토
content://com.google.android.apps.photos.contentprovider/.../ORIGINAL/NONE/image%2Fjpeg/160815479

// Samsung 내 파일
content://0@media/external/images/media/97

// 기본 Media Provider
content://com.android.providers.media.documents/document/image%3A97

// 커스텀 카메라 / FileProvider
content://com.example.myapp.fileprovider/files/Pictures/PHOTO_20260129.jpg

registerForActivityResult 결과로 들어온 content:// URI의 주소를 분석하면 대략 어떤 ContentProvider를 거쳐왔는지 짐작은 가능하다. 하지만 이 방식은 단말기 제조사, 안드 버전, 구글 포토 버전에 따라 URI 스킴 구조가 언제든 바뀔 수 있어 안정적이지 않다.

더 직관적인 해결책은 안드로이드 5.1(API 22) 이상에서 지원하는 Intent.createChooser(targetIntent, title, sender) API와 BroadcastReceiver를 조합하는 방식이다.

실제 환경에서는 원하는 대로 작동하지 않아 어떤 앱을 선택했는지 받지 못했다. 이런 방법이 있다는 정도로 정리한다.

URI 분석과 IntentSender 방식 비교

시스템 앱 선택기에서 유저가 특정 앱 아이콘을 터치하는 순간, OS는 오버로드된 IntentSender로 브로드캐스트를 전송한다. 이때 수신된 Intent에서 Intent.EXTRA_CHOSEN_COMPONENT 파라미터를 꺼내면 유저가 선택한 앱의 정확한 ComponentName 을 얻을 수 있다.

kotlin
class AppChooserReceiver : BroadcastReceiver() {
    companion object {
        private const val TAG = "AppChooserReceiver"
    }

    override fun onReceive(context: Context?, intent: Intent?) {
        // 유저가 Chooser에서 선택한 대상 컴포넌트 추출
        val targetComponent = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.33) {
            intent?.getParcelableExtra(Intent.EXTRA_CHOSEN_COMPONENT, ComponentName::class.java)
        } else {
            @Suppress("DEPRECATION")
            intent?.getParcelableExtra(Intent.EXTRA_CHOSEN_COMPONENT) as? ComponentName
        }

        val packageName = targetComponent?.packageName
        val className = targetComponent?.className

        Log.d(TAG, "선택된 앱 패키지: $packageName, 클래스: $className")
    }
}

우선 선택 이벤트를 수신할 BroadcastReceiver를 작성한다.

xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
    <application>
        <!-- Chooser 선택 이벤트를 수신하는 Receiver -->
        <receiver
            android:name=".AppChooserReceiver"
            android:exported="false" />
    </application>
</manifest>

AndroidManifest.xml에 브로드캐스트 리시버를 등록한다. android:exported="false" 옵션은 이 앱만 통신.

kotlin
private fun createChooserPendingIntent(): PendingIntent? {
    val intent = Intent(activity, AppChooserReceiver::class.java)

    val flags = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
        // OS가 Intent.EXTRA_CHOSEN_COMPONENT 데이터를 채워 넣을 수 있도록 MUTABLE 지정
        PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE
    } else {
        PendingIntent.FLAG_UPDATE_CURRENT
    }

    return PendingIntent.getBroadcast(
        activity,
        0,
        intent,
        flags
    )
}

// 웹뷰 onShowFileChooser 또는 일반 앱 공유 Intent 호출 시
override fun onShowFileChooser(
    webView: WebView,
    filePathCallback: ValueCallback<Array<Uri>>,
    fileChooserParams: FileChooserParams
): Boolean {
    val pendingIntent = createChooserPendingIntent()

    val pickIntent = Intent(Intent.ACTION_GET_CONTENT).apply {
        addCategory(Intent.CATEGORY_OPENABLE)
        type = "image/*"
    }

    // PendingIntent의 intentSender를 Intent.createChooser의 세 번째 인수로 전달
    val chooserIntent = Intent.createChooser(
        pickIntent,
        "사용할 앱을 선택해주세요.",
        pendingIntent?.intentSender
    )

    launcher.launch(chooserIntent)
    return true
}

createChooser 를 사용하면, 유저가 사진을 선택하기 위해 앱 선택기 UI에서 갤러리나 카메라를 누르는 즉시 AppChooserReceiver.onReceive()가 호출된다.

createChooser 를 구성할 때 intentSender를 세 번째 인수로 넘겨준다.

Android 12(API 31) 이상에서는 PendingIntent 생성 시 FLAG_MUTABLE 또는 FLAG_IMMUTABLE 플래그를 명시해야 한다. OS가 시스템 Chooser 내부에서 선택된 컴포넌트 정보(EXTRA_CHOSEN_COMPONENT)를 PendingIntent에 채워 넣어 브로드캐스트를 쏴야해서, 반드시 PendingIntent.FLAG_MUTABLE 플래그를 지정해야 정상 동작한다.

반환받은 URI 형태를 억지로 정규식 파싱하는 것보다 Intent.EXTRA_CHOSEN_COMPONENT에서 리턴해 주는 실제 패키지명을 확인하는 게 정확한데,, 이게 작동이 안된다.

좋아요와 댓글

댓글 남기기

댓글 0개

댓글을 불러오는 중입니다.