투케이2K

425. (kotlin/코틀린) [안드로이드 13] 웹뷰 (Webview) onShowFileChooser 사용해 input file 태그 이벤트 감지 및 갤러리 이미지 선택 전송 본문

Kotlin

425. (kotlin/코틀린) [안드로이드 13] 웹뷰 (Webview) onShowFileChooser 사용해 input file 태그 이벤트 감지 및 갤러리 이미지 선택 전송

투케이2K 2023. 10. 27. 09:47
반응형

[개발 환경 설정]

개발 툴 : AndroidStudio

개발 언어 : Kotlin

 

[소스 코드]

// --------------------------------------------------------------------------------------------------
[이벤트 감지 및 인텐트 전환 : 소스 코드]
// --------------------------------------------------------------------------------------------------

                // TODO [웹뷰 input file 태그 확인]
                override fun onShowFileChooser(webView: WebView?, filePathCallback: ValueCallback<Array<Uri>>?, fileChooserParams: FileChooserParams?): Boolean {
                    S_Log._F_(this@A_Webview, "웹뷰 input file 태그 이벤트 동작 감지", arrayOf(
                        "URL :: " + webView?.url.toString(),
                        "ValueCallback :: $filePathCallback",
                        "FileChooserParams :: " + Arrays.toString(fileChooserParams?.acceptTypes).toString()
                    ))


                    /**
                     * ------------------------------------
                     * TODO [네이티브 추가 설정 사항]
                     * ------------------------------------
                     * 1. 전역 변수 선언 실시 : ValueCallback mFilePathCallback;
                     * ------------------------------------
                     */


                    // ------------------------------------
                    // TODO [웹뷰 파일 콜백 지정 실시 : onActivity()]
                    // ------------------------------------
                    mFilePathCallback = filePathCallback
                    // ------------------------------------


                    // ------------------------------------
                    // TODO [이미지 파일만 지정 시 사용 : <input type="file" accept="image/*" multiple>]
                    // ------------------------------------
                    if (Arrays.toString(fileChooserParams?.acceptTypes).contains("image")) {
                        val intent = Intent(Intent.ACTION_PICK) // 갤러리 지정
                        intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true) // 사진 다중 선택 지정
                        intent.type = "image/*" // 이미지 파일 타입 지정
                        intent.data = MediaStore.Images.Media.EXTERNAL_CONTENT_URI // 미디어 스토어 사용 [이미지]
                        resultLauncher.launch(intent) // [startActivityForResult]
                    }
                    // ------------------------------------
                    // TODO [모든 형식 파일 지정 시 사용 : <input type="file" multiple>]
                    // ------------------------------------
                    else {
                        val intent = Intent(Intent.ACTION_GET_CONTENT) // 파일 지정 (image, txt, pdf ...)
                        intent.addCategory(Intent.CATEGORY_OPENABLE) // 카테고리 지정
                        intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true) // 파일 다중 선택 지정
                        intent.type = "*/*" // 모든 파일 지정
                        resultLauncher.launch(intent) // [startActivityForResult]
                    }
                    // ------------------------------------


                    // ----------------------------------
                    return true
                    // ----------------------------------
                }







// --------------------------------------------------------------------------------------------------
[사용자가 중도 취소 버튼 클릭 처리 : 소스 코드]
// --------------------------------------------------------------------------------------------------

    public override fun onResume() {
        super.onResume()
        // ===============================================================
        S_Log._F_(this@A_Webview, S_FinalMsg.LOG_Activity_onResume, null)
        // ===============================================================


        // ---------------------------------------------------------------
        // 외부 브라우저 복귀 시 화면 전환 애니메이션 없애기 위함
        // ---------------------------------------------------------------
        try { overridePendingTransition(0, 0) } catch (e: Exception) { S_Log._printStackTrace_(null, S_FinalMsg.LOG_BUG_STATE, null, e) }


        // ---------------------------------------------------------------
        // [웹뷰 파일 선택 취소 체크]
        // ---------------------------------------------------------------
        try {
            if (mFilePathCallback != null) {
                mFilePathCallback!!.onReceiveValue(null)
                mFilePathCallback = null
            }
        } catch (e: Exception) {
        }

    }








// --------------------------------------------------------------------------------------------------
[이미지 선택 완료 콜백 응답 처리 : 소스 코드]
// --------------------------------------------------------------------------------------------------

    val resultLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
        S_Log._F_(this@A_Webview, "ActivityResultLauncher :: 인텐트 응답 결과 확인", arrayOf(
            "RESULT :: $result"
        ))


        // -----------------------------------------
        // TODO [정상적으로 응답을 받은 경우]
        // -----------------------------------------
        if (result.resultCode == RESULT_OK) {

            // -----------------------------------------
            // [인텐트 데이터 얻어 온다]
            // -----------------------------------------
            val intent = result.data
            // -----------------------------------------


            // -----------------------------------------
            // TODO [선택한 파일 전송] : onResume 에서 [취소] 한 경우 null 처리 필요
            // -----------------------------------------
            // TODO [onResume 에서 null 전송 처리를 안해 주면 input file 이벤트 동작 안됨]
            // -----------------------------------------
            if (mFilePathCallback != null) {

                if (intent == null) {
                    S_Log._E_("[SEND] :: [파일 전송] :: intent :: null", null)

                    mFilePathCallback!!.onReceiveValue(null) // 웹으로 데이터 null 전송
                    mFilePathCallback = null // 초기화
                }
                else {
                    val clipData = intent.clipData // 선택한 파일 데이터 얻어온다
                    val fileCount = clipData!!.itemCount // 선택한 파일 개수 확인

                    if (fileCount > 0) { // 선택한 파일 있음
                        val filePathArray = arrayOfNulls<Uri>(fileCount) // 다중 파일을 담기 위한 배열 선언 실시
                        var stringData = ""

                        for (i in 0 until fileCount) { // for 문을 돌면서 배열에 uri 데이터 삽입
                            filePathArray[i] = intent.clipData!!.getItemAt(i).uri
                            stringData += "\n\n${intent.clipData!!.getItemAt(i).uri}"
                        }

                        S_Log._W_("[SEND] :: [파일 전송] :: mFilePathCallback :: getClipData", arrayOf(
                            "File Count :: $fileCount",
                            "File Path :: $stringData"
                        ))

                        mFilePathCallback!!.onReceiveValue(filePathArray as Array<Uri>) // 웹으로 데이터 전송 실시
                        mFilePathCallback = null // 초기화

                    }
                    else {
                        S_Log._E_("[SEND] :: [파일 전송] :: fileCount :: null", null)

                        mFilePathCallback!!.onReceiveValue(null) // 웹으로 데이터 null 전송
                        mFilePathCallback = null // 초기화
                    }
                }

            } else {
                S_Log._E_("[SEND] :: [파일 전송] :: mFilePathCallback :: null", null)
            }
            // -----------------------------------------
        }

    }
 

[결과 출력]

 

 

반응형
Comments