Today's

길을 나서지 않으면 그 길에서 만날 수 있는 사람을 만날 수 없다

모바일 앱(안드로이드)

안드로이드 앱 만들기 : webView 을 pdf 파일로 만들어서 공유해 보기

Billcorea 2022. 3. 1. 17:30
반응형

오늘은 내 앱의 화면에 올라온 내용중에 webView 에 들어 있는 것을 이미지 파일로 만들고 pdf 파일에 담아서 공유하는 기능을 구현해 보고자 한다. 

 

먼저할 것은 인터넷 접속을 위한 권한 부여

 

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

굳이 사용자에게 허가를 받지 않아도 된다.  그 다음에 layout 을 구성해 보았다. 

 

변환할 webView 에시

보는 것 처럼 화면에는 버튼 한개와 webView 만 한개 만들어 두었다.

 

layout의 코드는 다음과 같이.

 

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <Button
        android:id="@+id/pdfShare"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="40dp"
        android:layout_marginTop="16dp"
        android:text="Button"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <WebView
        android:id="@+id/webView"
        android:layout_width="0dp"
        android:layout_height="645dp"
        android:layout_marginTop="4dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.0"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/pdfShare" />

</androidx.constraintlayout.widget.ConstraintLayout>

 

그 다음은 이미지를 임시로 저장하고 그걸 pdf 로 전환하는 방법을 구현할 예정이기 때문에 임시저장소를 선택할 수 있도록 file_provider 의 값을 등록해 준다.  res/xml 폴더에 file_provider.xml 로 아래 내용을 저장해 주었다. 

cache 을 사용하는 것은  파일 저장을 하기 위해서 사용자에게 권한을 허가 받지 않아도 되기 떄문이다. 

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <cache-path name="cache" path="."/>
</paths>

 

다음은 activity을 구현해 보아야겠다.

먼저 webView 에 나의 블로그를 load할 수 있도록 하고, load가 다 되었다면 그 때 이미지 파일에 임시 저장을 한 다음, 

버튼을 클릭하면 그 이미지 파일을 열어서 공유하는 intent 을 호출 하도록 구현할 예정이다. 

 


import androidx.appcompat.app.AppCompatActivity;
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.core.app.ActivityCompat;
import androidx.core.app.ShareCompat;
import androidx.core.content.FileProvider;

import android.Manifest;
import android.annotation.SuppressLint;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Typeface;
import android.graphics.drawable.Drawable;
import android.graphics.pdf.PdfDocument;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Environment;
import android.util.Log;
import android.view.View;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.Toast;

import com.billcoreatech.pdftest.databinding.ActivityMainBinding;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.List;

public class MainActivity extends AppCompatActivity {

    private static final String TAG = "MainActivity";
    Bitmap bitmap, scalebmp ;
    int pageWidth = 1200 ;
    ActivityMainBinding bind ;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        bind = ActivityMainBinding.inflate(getLayoutInflater());
        setContentView(bind.getRoot());

        bind.webView.loadUrl("https://billcorea.tistory.com");
        bind.webView.setWebViewClient(new WebViewClient() {
            @Override
            public void onPageFinished(WebView view, String url) {
                super.onPageFinished(view, url);
                new Background().execute();
            }
        });

        bind.pdfShare.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if (createPDF ()) {
                    File pdfFile = new File(getCacheDir(), "/pizza.pdf");
                    Uri contentUri = FileProvider.getUriForFile(getApplicationContext(), getApplicationContext().getPackageName()+".fileProvider", pdfFile);

                    Intent shareIntent = new Intent(Intent.ACTION_SEND);
                    shareIntent.setType("application/pdf");
                    shareIntent.putExtra(Intent.EXTRA_STREAM, contentUri);
                    shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);

                    List<ResolveInfo> resInfoList = getPackageManager().queryIntentActivities(shareIntent, PackageManager.MATCH_DEFAULT_ONLY);
                    for (ResolveInfo resolveInfo : resInfoList) {
                        String packageName = resolveInfo.activityInfo.packageName;
                        grantUriPermission(packageName, contentUri,
                                Intent.FLAG_GRANT_READ_URI_PERMISSION);
                    }
                    startActivity(Intent.createChooser(shareIntent, "PDF 공유"));
                };
            }
        });

    }

    private boolean createPDF() {

        File file = new File(getCacheDir(), "/aaaa.png");
        if (!file.isFile()) {
            Toast.makeText(getApplicationContext(), "파일 생성이 되지 않았습니다.", Toast.LENGTH_SHORT).show();
            return false ;
        }

        bitmap = BitmapFactory.decodeFile(getCacheDir().toString() + "/aaaa.png");

        PdfDocument pdfDocument = new PdfDocument();
        Paint paint = new Paint();

        PdfDocument.PageInfo pageInfo = new PdfDocument.PageInfo.Builder(bitmap.getWidth(), bitmap.getHeight(), 1).create();
        PdfDocument.Page page = pdfDocument.startPage(pageInfo);
        Canvas canvas = page.getCanvas();
        canvas.drawBitmap(bitmap, 0, 0, paint);

        pdfDocument.finishPage(page);

        File file1 = new File(getCacheDir(), "/pizza.pdf");
        try {
            pdfDocument.writeTo(new FileOutputStream(file1));
        } catch (IOException e) {
            return false ;
        }
        pdfDocument.close();

        return true ;
    }

    class Background extends AsyncTask<Void, Void, Bitmap>
    {
        @Override
        protected Bitmap doInBackground(Void... params)
        {
            try
            {
                Thread.sleep(2000);
                Bitmap bitmap = Bitmap.createBitmap(bind.webView.getWidth(), bind.webView.getHeight(), Bitmap.Config.ARGB_8888);
                Canvas canvas = new Canvas(bitmap);
                bind.webView.draw(canvas);
                return bitmap;
            }
            catch (InterruptedException e){}
            catch (Exception e){}
            return null;
        }
        @SuppressLint("WrongThread")
        @Override
        protected void onPostExecute(Bitmap bm)
        {
            try {
                OutputStream fOut = null;
                File file = new File(getCacheDir(), "/aaaa.png");
                fOut = new FileOutputStream(file);

                bm.compress(Bitmap.CompressFormat.PNG, 50, fOut);
                fOut.flush();
                fOut.close();
                bm.recycle();

                Log.e(TAG, "fileName=" + getCacheDir() + "/aaaa.png");

                Toast.makeText(getApplicationContext(), "공유할 파일이 생성되었습니다.", Toast.LENGTH_SHORT).show();
            } catch (Exception e) {
                e.printStackTrace();

                Log.e(TAG, "" + e.toString());
            }
        }
    }
}

 

webView 에 load는 시간이 소요 되기 떄문에 Background 로 load 된 이미지를 png 로 변환하도록 구현이 되었고, 

버튼을 클릭하면 그 때 png 파일이 있는 지 체크 해서 없으면 알림을 보여주고, 있으면 해당 이미지 파일을 pdf 로 전환해서 하고, pdf 가 생성되었다면, intent 을 호출해서 공유하도록 구현하는 방식이다. 

 

다만, API 32 기준으로 AVD에서 테스트를 해 보면서 오류 로그가 보이기는 했으나, 실행은 되었다. provider 에게 uri read 권한을 주어야 할 것 같은데, 아직 구글링을 했을 때 정확한 해소 방안은 찾을 수 없었다.

 

어떻게 하지 ?

 

 

실행 예시

 

알게될 날이 올까 ?

 

오류 해소를 위해서 다음 링크 까지는 해 보았으나... 아직 ... ㅠㅠ;;

 

https://stackoverflow.com/questions/57689792/permission-denial-while-sharing-file-with-fileprovider/59439316#59439316

 

Permission Denial while sharing file with FileProvider

I am trying to share file with FileProvider. I checked that file is shared properly with apps like Gmail, Google Drive etc. Even though following exception is thrown: 2019-08-28 11:43:03.169 12573-...

stackoverflow.com

 

반응형