\ 'Java Android' 카테고리의 글 목록 :: Something New
728x90
반응형

웹뷰에서는 브라우저와 다르게 newtab으로 페이지를 열경우 oncreate외에 코드를 추가해 주어야한다.

webChromeClient를 이용해서 새로운 창을 여는 웹뷰설정과 그안에서의 다운로드 처리도 별도로 진행된다.

 

public class MyWebChromeClient extends WebChromeClient {
 //신규탭으로 열었을때
        @Override
        public boolean onCreateWindow(WebView view, boolean isDialog, boolean isUserGesture, Message resultMsg) {
            WebView newWebView = new WebView(view.getContext());
            WebSettings settings = newWebView.getSettings();
            //ウェブページのjavascript使用許可
            settings.setJavaScriptEnabled(true);
            //新しくタブを使用許可
            settings.setSupportMultipleWindows(true);
            //画面zoom許可
            settings.setSupportZoom(true);
            //画面拡大縮小許可
            settings.setBuiltInZoomControls(true);
            //拡大ショートカット許可しない
            settings.setDisplayZoomControls(false);
            //GPS許可
            settings.setGeolocationEnabled(true);
            //DBアクセス許可
            settings.setDatabaseEnabled(true);
            //フォームデータを保存しない
            settings.setSaveFormData(false);
            final Dialog dialog = new Dialog(view.getContext(), R.style.Theme_DialogPage);
            dialog.setContentView(newWebView);

            //다운로드처리
            newWebView.setDownloadListener(new DownloadListener() {

                @Override
                public void onDownloadStart(String url, String userAgent, String contentDisposition, String mimeType, long contentLength) {
                    try {
                        String mFileName;
                        DownloadManager dm = (DownloadManager) getSystemService(DOWNLOAD_SERVICE);
                        DownloadManager.Request request = new DownloadManager.Request(Uri.parse(url));

                        request.setMimeType(mimeType);
                        String cookies = CookieManager.getInstance().getCookie(url);

                        request.addRequestHeader("cookie", cookies);
                        request.addRequestHeader("User-Agent", userAgent);
                        request.addRequestHeader("Referer", getReferrer().getAuthority());
                        request.addRequestHeader("Content-Disposition", contentDisposition);
                        request.setDescription("Downloading file...");
                        String content = contentDisposition;
                        //アンドロイド側で読み込めるようにファイル名を編集
                        mFileName = content.replace("attachment; filename=", "").trim();
                        //WEBでエンコードされたファイル名をDecodeする
                        mFileName = URLDecoder.decode(mFileName, String.valueOf(UTF_8));
                        request.setTitle(mFileName);
                        //このダウンロードの実行中または完了時に、ダウンロード マネージャーによってシステム通知が投稿されるかどうかを制御します。
                        request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
                        //다운로드폴더설정 path=\내부스토리지\Android\data\어플이름\cache
                        request.setDestinationUri(Uri.fromFile(new File(getExternalCacheDir().getAbsolutePath(), mFileName)));
                        //このダウンロードを実行できるネットワークの種類を制限します。
                        request.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_MOBILE | DownloadManager.Request.NETWORK_WIFI);
                        //ダウンロード マネージャーがダウンロードを実行する準備ができ、接続が利用可能になると、ダウンロードは自動的に開始されます。
                        dm.enqueue(request);

                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            });
            newWebView.clearCache(true);
            dialog.show();
            //뒤로가기 키 설정
            dialog.setOnKeyListener(new DialogInterface.OnKeyListener() {
                @Override
                public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) {
                    if (keyCode == KeyEvent.KEYCODE_BACK) {
                        //다이얼로그 닫기
                        dialog.dismiss();
                    }
                    return true;
                }
            });

            newWebView.setWebViewClient(new MyWebViewClient());
            newWebView.setWebChromeClient(new MyWebChromeClient());

            //スレッド境界を越えて WebView を返すためのトランスポート オブジェクト。
            WebView.WebViewTransport transport = (WebView.WebViewTransport) resultMsg.obj;
            transport.setWebView(newWebView);
            resultMsg.sendToTarget();
            return true;
        }
        
  }
728x90
728x90
반응형

 //Web인코딩된 파일명을 디코드해줌
 mFileName = URLDecoder.decode(mFileName, String.valueOf(UTF_8));

서블릿에서 base64디코딩을 해줬어도 서블릿에서 안드로이드로 넘어올때 url인코딩이 되므로

파일명을 한번 더 디코딩해주어야한다.

 

728x90
728x90
반응형

안녕하세요

저는 웹뷰를 이용한 하이브리드 어플을 개발중이구요

오늘 하려는 어플 종료시 어플에서 다운로드한 파일 삭제하기는

제가 어플을 만드는 이유이기도 합니다.

웹은 이미 사용되는 중이었으나 고객이 원한건 웹에서는 cache를 지울수 없기 때문이었습니다.

저는 파일 업로드 및 다운로드 처리를 구현한 상태였구요

제가 다운로드한 path는 내장저장소의 개별어플 안 다운로드폴더 였습니다.

안드로이드의 업데이트로 내장저장소의 개별어플 내부의 디렉토리를 볼 수 없게 된 상태라는 것을 알고

살짝 멘붕에 빠지려던 찰나

캐시파일은 삭제가 가능하다는 document를 보고 다운로드path를 변경했습니다.

 

어플 종료시 작동하는 메소드로 onDestroy()부터 작성합니다.

    public void onDestroy() {
        super.onDestroy();
        deleteCache(getApplicationContext());
    }

이후에 deleteCache를 만들어 줍니다

여기서 File dir = context.getCacheDir(); 이 부분을 getFilesDir().getAbsolutePath();이렇게 바꿔보았으나

아예 디렉토리를 못찾더라구요

        public static void deleteCache(Context context) {
            try {
                File dir = context.getCacheDir();
                deleteDir(dir);
            } catch (Exception e) { 
            	e.printStackTrace();
            }
        }
        
        public static boolean deleteDir(File dir) {
            if (dir != null && dir.isDirectory()) {
                String[] children = dir.list();
                for (int i = 0; i < children.length; i++) {
                    boolean success = deleteDir(new File(dir, children[i]));
                    if (!success) {
                        return false;
                    }
                }
                return dir.delete();
            } else if(dir!= null && dir.isFile()) {
                return dir.delete();
            } else {
                return false;
            }
        }

 

저 같은 상황에 직면하신 분들이 또 계실지는 모르겠으니

다운로드 파일의 path를 캐시폴더로 바꿔줍니다

<웹뷰에서 파일 다운로드는 이 게시물을 참고해주세요>

https://ssomethingnew.tistory.com/37

 request.setDestinationInExternalFilesDir(getApplicationContext(),"/cache/", mFileName );

제가 찾은 답은 이렇게 작성해 주는 것이 었고요

원래는 "/cache/" 이 부분에 getCacheDir()을 넣었는데 getCacheDir()이거 자체에 컨텍스트 path까지 포함되어있어서

앞의 getApplicationContext()  부분의 path가 중복이 되더라고요 

디렉토리 내부에 어떤파일이든 다 삭제하는 것이 목표였기 때문에 저는 전체 삭제 해줬습니다만

혹시 특정 파일명만 삭제하시고 싶으시다면 for문안에 파일명만 지정해주는 것도 좋을 것 같습니다.

치치부에 축제(마쯔리)에 놀러가서 먹은 당고

 

728x90
728x90
반응형

저는 하이브리드 어플을 개발하고 있구요 webview에서 파일 다운로드하고 파일 열기까지 

제가 하는 환경에서 web에서 다운로드매니저로 다운받을때 url에 서버파일의 path가 담겨있지 않아서

웹.서블렛 수정까지 같이 하느라 시간이 많이 걸렸습니다만

여러분의 url에는 파일명과 파일path가 담겨있을거라 생각하고 

파일 다운로드와 intent로 파일열기 검색한대로 했는데 왜 자꾸 커미션 넣으라고만 하냐구요ㅠㅠ

전 커미션 당연히 설정한 상태였습니다.

중요한건 파일명이 url이나 base64로 인코딩된 상태이면 디코딩이 필요할 수 있구요

저는 내부저장소에 개별어플파일안 \내장 저장공간\Android\data\어플이름\files\download에 저장했습니다.

\내장 저장공간\Download에 저장하고 싶으시다면 request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, mFileName); 

로 바꿔 써주시면 됩니다.

onCreate()안에 여러 웹뷰 세팅하시고 밑의 코드를 넣어주시면 됩니다.

    newWebView.setDownloadListener(new DownloadListener() {

                @Override
                public void onDownloadStart(String url, String userAgent, String contentDisposition, String mimeType, long contentLength) {
                    try {
                        DownloadManager dm = (DownloadManager) getSystemService(DOWNLOAD_SERVICE);
                        DownloadManager.Request request = new DownloadManager.Request(Uri.parse(url));
                        DownloadManager.Query query = new DownloadManager.Query();

                        request.setMimeType(mimeType);
                        String cookies = CookieManager.getInstance().getCookie(url);
                        request.addRequestHeader("cookie", cookies);
                        request.addRequestHeader("User-Agent", userAgent);
                        request.addRequestHeader("Referer", getReferrer().getAuthority());
                        request.addRequestHeader("Content-Disposition", contentDisposition);
                        request.setDescription("Downloading file...");
                        String content = contentDisposition;
                        String[] contentSplit = content.split(content);

                        mFileName = content.replace("attachment; filename=", "").trim();
                        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
                            mFileName = URLDecoder.decode( mFileName, UTF_8);
                        }

                        request.setTitle(mFileName);
                        request.allowScanningByMediaScanner();
                        request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
                        //다운받아서 넣을 파일의 디렉토리 지정해 주세요
                        //request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, mFileName); 
                        request.setDestinationInExternalFilesDir(getApplicationContext(),DIRECTORY_DOWNLOADS, mFileName);
                
                        request.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_MOBILE | DownloadManager.Request.NETWORK_WIFI);

                         dm.enqueue(request);
                       Toast.makeText(getApplicationContext(), "Downloading File", Toast.LENGTH_LONG).show();

                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            });

 

여기까지하시면 다운로드가 됩니다.

다운로드 후에 바로 다운로드한 파일을 열고 싶을 때 밑에 소스를 추가해 주세요

 private final BroadcastReceiver mCompleteReceiver = new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                String action = intent.getAction();
                if (ActivityCompat.checkSelfPermission(MainActivity.this,
                        Manifest.permission.WRITE_EXTERNAL_STORAGE) !=
                        PackageManager.PERMISSION_GRANTED) {

                    //권한 허가
                    ActivityCompat.requestPermissions(MainActivity.this,
                            new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},1000);
                }
                //파일 다운로드가 완료되었을 때
                if (DownloadManager.ACTION_DOWNLOAD_COMPLETE.equals(action)) {
                    Toast.makeText(context, "Complete.", Toast.LENGTH_SHORT).show();
                    long downloadId = intent.getLongExtra(
                            DownloadManager.EXTRA_DOWNLOAD_ID, 0);
                    openDownloadedAttachment(context, downloadId);
                }
            }
        };

여기까지는 흔하게 파일 완료 액션을 잡아서 어찌저찌 했습니다만 중요한건 여기부터 입니다

  private void openDownloadedAttachment(final Context context, final long downloadId) {
            DownloadManager downloadManager = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);
            DownloadManager.Query query = new DownloadManager.Query();
            query.setFilterById(downloadId);
            Cursor cursor = downloadManager.query(query);
            //다운로드한 정보를 꺼내서
            if (cursor.moveToFirst()) {
                @SuppressLint("Range") int downloadStatus = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_STATUS));
                @SuppressLint("Range") String downloadLocalUri = cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI));
                @SuppressLint("Range") String downloadMimeType = cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_MEDIA_TYPE));
                if ((downloadStatus == DownloadManager.STATUS_SUCCESSFUL) && downloadLocalUri != null) {
                    openDownloadedAttachment(context, Uri.parse(downloadLocalUri), downloadMimeType);
                    mUri = String.valueOf(Uri.parse(downloadLocalUri));
                }
            }
            cursor.close();
        }
        

		//fileprovider로 열기
        private void openDownloadedAttachment(final Context context, Uri attachmentUri, final String attachmentMimeType) {
            if(attachmentUri!=null) {
                // Get Content Uri.
                if (ContentResolver.SCHEME_FILE.equals(attachmentUri.getScheme())) {
                    // FileUri - Convert it to contentUri.
                    File file = new File(attachmentUri.getPath());
                    attachmentUri = FileProvider.getUriForFile(getApplicationContext(), getApplicationContext()
                            .getPackageName()+".provider", file);
                }

                Intent openAttachmentIntent = new Intent(Intent.ACTION_VIEW);
                openAttachmentIntent.setDataAndType(attachmentUri, attachmentMimeType);
                openAttachmentIntent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
                try {
                    context.startActivity(openAttachmentIntent);
                } catch (ActivityNotFoundException e) {
                    Toast.makeText(context, "error", Toast.LENGTH_LONG).show();
                }
            }
        }

 

예전에는  file:/// 형태의 uri 로 가능했다고 하는데

이젠 content://로 바뀌어야해서 fileprovider를 사용해줍니다. 

저는 이 방법을 통해 파일 다운로드 후 pdf파일 열기까지 성공했습니다

hakusyu위스키 공장에서 시음

 

728x90
728x90
반응형

안드로이드는 업데이트가 잦아서 무슨 기능을 검색해서 쓰려고하면

버전이 안맞아서 검색의 무한루프에 빠져버린다

제가 하고 있는 건 웹은 작동하고 있는 환경에서

추가로 어플을 만드는 하이브리드 어플을 만들고 있는 상황입니다.

최신 코드라고 해도 startactivityforresult deprecated되어서 가져다 쓸 수 없었으므로,,

일단 manefest에서 커미션은 허용했다는 전제하에

public class MyWebChromeClient extends WebChromeClient  안에 넣어 주면 됩니다.

  @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
        @Override
        public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback, FileChooserParams fileChooserParams) {

            //파일 업로드 
            if (mFilePathCallback != null) {
                //파일을 한번 열면 초기화 시킬것 안하면 두번째는 반응이 없음
                mFilePathCallback.onReceiveValue(null);
            }
            mFilePathCallback = filePathCallback;

			//WEBVIEW에서 파일을 선택하는 INTENT 작동
            Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
            intent.addCategory(Intent.CATEGORY_OPENABLE);
            intent.setType("*/*");  
            
            //이부분이 startactivityforresult을 쓴 사람들이 많았음 deprecated된 부분
            startActivityIntent.launch(intent);
            
            return true;
        }

startactivityforresult deprecated 대체를 검색해서 맞춰서 만든 코드

        ActivityResultLauncher<Intent> startActivityIntent = registerForActivityResult(
                new ActivityResultContracts.StartActivityForResult(),
                new ActivityResultCallback<ActivityResult>() {
                    @Override
                    public void onActivityResult(ActivityResult result) {
                        if(result.getResultCode() == RESULT_OK) {
                            //파일 선택완료
                            if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                                mFilePathCallback.onReceiveValue(WebChromeClient.FileChooserParams.parseResult(result.getResultCode(), result.getData()));
                            }else{
                                mFilePathCallback.onReceiveValue(new Uri[]{result.getData().getData()});
                            }
                            mFilePathCallback = null;
                        } else {
                            //취소했을 경우
                            if(mFilePathCallback != null) {
                                mFilePathCallback.onReceiveValue(null);
                                mFilePathCallback = null;
                            }
                        }
                    }
                });

포켓몬 카페 갔을 때 찍은 피카츄

 

 

728x90

+ Recent posts