티스토리 뷰

728x90
반응형

지난 포스팅에서 아주 깔끔(?)하게 하이브리드앱에서 사용가능한 업로드 기능을 구현했는데


4.4.2 버전에서는 내장메소드가 제거되어있는터라


openfilechooser를 사용할수가 없는 비극의 현실이 있었습니다.



어떻게 하면될까요.


기획자와 잘 풀어서 여차저차 해결할수도 있겠지만


그래도 개발자인 이상 끈기를 가지고 도전해봐야겠죠.



4.4.2버전에서는 예외적으로 openfilechooser를 사용하지 않고 구현해야합니다.


이문제도 우회하여 풀어볼수 있는데요



바로 javascript interface를 사용하는것입니다.


android의 javascript interface를 사용하면 javascript 코드를 통하여 android 함수를 호출할수 있고,


반대로 android가 webview에 javascript 함수를 호출할수도 있습니다.



이점을 이용하여 사용자가 input type="file"을 터치하면 안드로이드의 함수를 실행하여 파일선택창을 강제로 띄워주고


파일을 선택한 다음에는 해당 파일을 decode한 string을 파라메터에 담아 webview가 쓸수 있도록 javascript함수를 호출해주는 원리죠.



마찬가지로 두개의 퍼미션이 필요합니다.


<uses-permission android:name="android.permission.INTERNET" />

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />



그리고 웹뷰를 생성하는데


addJavascriptInterface를 이용해서 자바스크립트인터페이스를 적용해줍니다.



        wv = (WebView) findViewById(R.id.webview);
        wv.setWebChromeClient(new WebChromeClient());
        wv.setWebViewClient(new WebViewClient());
        WebSettings set = wv.getSettings();
        set.setJavaScriptEnabled(true);
        wv.loadUrl("http://test.com/upload.php");
        wv.addJavascriptInterface(new JavaScriptInterface(this), "Android");



앞으로 webview의 웹페이지들은 Android 라는 이름의 객체에 접근할수 있게됩니다



    public class JavaScriptInterface {
        private Context context;

        public JavaScriptInterface(Context context) {
            this.context = context;
        }

        @JavascriptInterface
        public void selectImage() {
            Intent intent = new Intent(Intent.ACTION_PICK, android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
            intent.setType("image/*");
            startActivityForResult(intent, PICK_IMAGE_REQ_CODE);
        }
    }


selectImage()라는 함수를 만들었습니다.


webview의 페이지들은 


Android.selectImage();


이렇게 자바스크립트를 호출하면 바로 이 안드로이드 메소드가 실행됩니다.


사용자가 선택한 파일을 받아서 다시 웹뷰로 전해주는 코드는 다음과 같습니다.


    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (requestCode == PICK_IMAGE_REQ_CODE) {
            if (resultCode == Activity.RESULT_OK) {
                if (data != null) {
                    Uri uri = data.getData();

                    new AsyncTask<uri, void,="" string="">() {
                        @Override
                        protected String doInBackground(Uri... params) {
                            String mimeType = getMimeType(params[0]);

                            File file = uriToFile(params[0]);
                            String base64EncodedImage = fileToString(file);

                            return "javascript:updateImage('" + mimeType + "', '" + base64EncodedImage + "');";
                        }

                        @Override
                        protected void onPostExecute(String result) {
                            wv.loadUrl(result);
                        }
                    }.execute(uri);
                }
            }
        }
    }


updateImage라는 자바스크립트 함수를 호출하는 것이 보이나요?




그러면 아예 openfilechooser를 사용하지 않고 이런식으로 모든 버전을 구현해도 되지 않을까?


이렇게 생각하셨다면 짝짝짝 참 좋은 질문입니다.


이방법도 아쉬움이 존재하는데요


addJavascriptInterface 는 안드로이드 2.3 버전대에서 동작하지 않습니다...


2.3버전대를 무시하신다면 사용하셔도 좋긴합니다만


현재 제가 완성한 코드는 4.x 버전에서는 정상동작하나 5.0 이상버전에서 선택한 파일의 uri를 가져오는부분이(uriToFile 메소드) 완벽히 구현되지 않았습니다.


5.0이상에서도 동작가능한 코드로 수정하신분 계시면 github에 pull request 보내주시면 감사하겠습니다.



나머지 코드가 담긴 샘플 프로젝트는 AndroidJavascriptInterface 깃헙에 있습니다.

728x90
반응형
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/04   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30
글 보관함