How to create Android PDF Viewer Application

When you visit Google Play Store, there are lots of applications you can download and use to open or view a pdf file in your device.

If you are wondering how this application is created, no worry because you will have the tools and knowledge you need at the end of this tutorial.

Below is a screenshot of what we will creates.

1. Create a new Android Project

  • Open Android Studio
  • Go to file menu
  • Select  new
  • Enter project name
  • Enter activity name
  • Keep other default settings
  • Click on finish button to create a new android project

2. List all PDF files in device

There are different ways we can use to access list of PDF files in our device: we can use File Manager App or File Picker or we can roll our own using Android API.

We will go with the last option. The code will fetch all PDF files in our device.

public class ListPDFUtils {

    private static final String TAG = ListPDFUtils.class.getSimpleName();

    private Context context;

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

    String[] projection = {
            MediaStore.Files.FileColumns._ID,
            MediaStore.Files.FileColumns.MIME_TYPE,
            MediaStore.Files.FileColumns.DATE_ADDED,
            MediaStore.Files.FileColumns.DATE_MODIFIED,
            MediaStore.Files.FileColumns.DISPLAY_NAME,
            MediaStore.Files.FileColumns.TITLE,
            MediaStore.Files.FileColumns.SIZE,
    };

    public List<Document> queryMediaStore(String fileMimeType){
        String whereClause = getFileType(fileMimeType);
        String orderBy = MediaStore.Files.FileColumns.SIZE + " DESC";
        Uri uri = MediaStore.Files.getContentUri("external");
        Cursor cursor = context.getContentResolver().query(uri, projection, whereClause, null, orderBy);

        int idCol = cursor.getColumnIndexOrThrow(MediaStore.Files.FileColumns._ID);
        int mimeCol = cursor.getColumnIndexOrThrow(MediaStore.Files.FileColumns.MIME_TYPE);
        int addedCol = cursor.getColumnIndexOrThrow(MediaStore.Files.FileColumns.DATE_ADDED);
        int modifiedCol = cursor.getColumnIndexOrThrow(MediaStore.Files.FileColumns.DATE_MODIFIED);
        int nameCol = cursor.getColumnIndexOrThrow(MediaStore.Files.FileColumns.DISPLAY_NAME);
        int titleCol = cursor.getColumnIndexOrThrow(MediaStore.Files.FileColumns.TITLE);
        int sizeCol = cursor.getColumnIndexOrThrow(MediaStore.Files.FileColumns.SIZE);

        List<Document> allDoc = new ArrayList<>();
        if (cursor.moveToFirst()) {
            do {
                Uri fileUri = Uri.withAppendedPath(uri, cursor.getString(idCol));
                String mimeType = cursor.getString(mimeCol);
                long dateAdded = cursor.getLong(addedCol);
                long dateModified = cursor.getLong(modifiedCol);
                String name = cursor.getString(nameCol);
                String title = cursor.getString(titleCol);
                long size = cursor.getLong(sizeCol);

                String path =getRealPathFromURI(context, fileUri);
                String extractName = StringUtils.capitalize(FilenameUtils.getName(path));
                Log.d(TAG, "File in PDF " + title);
                Document document = new Document(extractName, Utils.formatSize(size), Utils.formatDate(dateModified), path);
                int icon = getIcon(fileMimeType);
                document.setIcon(icon);
                allDoc.add(document);

            } while (cursor.moveToNext());
        }
        cursor.close();
        return allDoc;
    }

    public String getRealPathFromURI(Context context, Uri contentUri) {
        Cursor cursor = null;
        try {
            String[] proj = { MediaStore.Images.Media.DATA };
            cursor = context.getContentResolver().query(contentUri,  proj, null, null, null);
            int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
            cursor.moveToFirst();
            return cursor.getString(column_index);
        } finally {
            if (cursor != null) {
                cursor.close();
            }
        }
    }
}

3. Add Permission

Since accessing android storage API needs permission, we are going to add this code in our project manifest file.

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

4. Add Android Third-Party Libraries

We are going to add few android libraries that will help us solve some problems rather than reinventing the wheels.

Open your project build.gradle and add the libraries below.

implementation 'com.github.burakeregar:EasiestGenericRecyclerAdapter:v1.5'
implementation 'com.github.barteksc:android-pdf-viewer:3.2.0-beta.1'

We will use the EasiestGenericRecyclerAdapter to display all the PDF files we have fetched from our device.

We will use the PDF viewer to view the content of each PDF file.

5. List PDF File with EasiestGenericRecycler Adapter

private void populateRecyclerView() {
        Observable<List<Document>> pdfs = Observable.fromCallable(new Callable<List<Document>>() {
            @Override
            public List<Document> call() throws Exception {
                //return loadPdf();
                return listPDFUtils.queryMediaStore(ListPDFUtils.FileMimeTypes.PDF_MIME_TYPE);
            }
        });
        compositeDisposable.add(pdfs.subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Consumer<List<Document>>() {
                    @Override
                    public void accept(List<Document> documents) throws Exception {
                        progressBar.setVisibility(View.GONE);
                        recyclerView.setVisibility(View.VISIBLE);

                        setUpUIList(documents);
                    }
                })
        );
    }

    private void setUpUIList(List<Document> data){
        adapter = new GenericAdapterBuilder()
                .addModel(R.layout.list_pdf_item_layout, PdfViewHolder.class, Document.class)
                .setFilterEnabled()
                .execute();
        recyclerView.setAdapter(adapter);
        adapter.setList(data);
        adapter.setFilter(new SearchFilter(adapter));
    }

6. Create a new activity page

Now we will create a new activity page we will use to display a single PDF file.

Open the new activity layout page and paste the code below.

<com.github.barteksc.pdfviewer.PDFView
        android:id="@+id/pdf_view"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_marginStart="8dp"
        android:layout_marginLeft="8dp"
        android:layout_marginTop="8dp"
        android:layout_marginEnd="8dp"
        android:layout_marginRight="8dp"
        android:layout_marginBottom="8dp"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toTopOf="@id/edit_box"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"/>

We will get the PDFView reference in our activity page. When a single PDF file is clicked, we will get the File properties wrapped in a Document class.

Below is the code to view a PDF File.

 public PDFView.Configurator getPdfConfigurator(PDFView pdfView){
        PDFView.Configurator configurator = null;
        if (doc != null) {
            configurator =  pdfView.fromUri(getFileUri(context, doc.getDocumentPath()))
                    .enableDoubletap(true)
                    .enableSwipe(true)
                    .swipeHorizontal(true)
                    .enableAnnotationRendering(true)
                    .pageFitPolicy(FitPolicy.BOTH)
                    .fitEachPage(true)
                    .spacing(2);
        }
        return configurator;
    }
PDFView.Configurator configurator = getPdfConfigurator(pdfView);
            configurator.onPageChange(new OnPageChangeListener() {
                @Override
                public void onPageChanged(int page, int pageCount) {
                    currentIndexPage = page;
                    setPDFPaging(page, pageCount);
                }
            }).onTap(new OnTapListener() {
                @Override
                public boolean onTap(MotionEvent e) {
                    return false;
                }
            }).load();

With this, you are able to view any PDF file in your device.

If you have questions regarding this tutorial, kindly use the comment box below to drop your questions.

Add a Comment