skip to Main Content

I am trying to make my app allow users to read pdf files on my app. I have added the uses permission in my manifest file and my code works perfectly fine yet when the runtime permission pops up and I allow for the app to access my device storage no pdf files show up on my app for me to select and to read. Why is this happening and what should I change?

This is the current code in my MainActivity.java class:

    public class MainActivity extends AppCompatActivity {

    private PdfAdapter pdfAdapter;
    private List<File> pdfList;
    private RecyclerView recyclerView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        runtimePermission();
    }

    private void runtimePermission() {
        Dexter.withContext(MainActivity.this).withPermission(Manifest.permission.READ_EXTERNAL_STORAGE)
                .withListener(new PermissionListener() {
                    @Override
                    public void onPermissionGranted(PermissionGrantedResponse permissionGrantedResponse) {
                        displayPdf();
                    }

                    @Override
                    public void onPermissionDenied(PermissionDeniedResponse permissionDeniedResponse) {
                        Toast.makeText(MainActivity.this, "Permission is Required", Toast.LENGTH_SHORT).show();
                    }

                    @Override
                    public void onPermissionRationaleShouldBeShown(PermissionRequest permissionRequest, PermissionToken permissionToken) {
                        permissionToken.continuePermissionRequest();
                    }
                }).check();

    }

    public ArrayList<File> findPdf (File file)
    {
        ArrayList<File> arrayList = new ArrayList<>();
        File[] files = file.listFiles();

        for (File singleFile: files)
        {
            if (singleFile.isDirectory() && singleFile.isHidden()) {
                arrayList.addAll(findPdf(singleFile));
            }
            else {
                if (singleFile.getName().endsWith(".pdf")){
                    arrayList.add(singleFile);
                }
            }
        }
        return arrayList;
    }

    private void displayPdf() {
        recyclerView = findViewById(R.id.recycler_view);
        recyclerView.setHasFixedSize(true);
        recyclerView.setLayoutManager(new GridLayoutManager(this, 3));
        pdfList = new ArrayList<>();
        pdfList.addAll(findPdf(Environment.getExternalStorageDirectory()));
        pdfAdapter = new PdfAdapter(this, pdfList);
        recyclerView.setAdapter(pdfAdapter);
    }
}

I have also set up my Pdfadapter.java class and pdfviewholder.java class but the pdfs don’t show up in my app. I am trying to make my app usable for sdks between 21 up to the latest version. My compilesdk is 32, min sdk 32 and targetsdk 32 in my gradle file.

2

Answers


  1. First of all, never use the file API for fetching the media from device storage except you have a special requirement. It’s slow as hell when you have so many files stored on the device and memory consumption is a thing to consider when making an app.

    Here, you can read all the media files using Mediastore API which is memory efficient and provides many customizations while loading media files.

    So, all you have to do is get the storage permission and then load PDFs using mediastore like this,

    Load all PDFs from device storage including SD card :

    public ArrayList<MediaModel> getAllPDFFromStorage(Context ctx) {
            ArrayList<MediaModel> pdfList = new ArrayList<>();
    
            String[] projection = {
                    MediaStore.Files.FileColumns._ID,
                    MediaStore.Files.FileColumns.DISPLAY_NAME,
                    MediaStore.Files.FileColumns.DATA,
                    MediaStore.Files.FileColumns.DISPLAY_NAME,
                    MediaStore.Files.FileColumns.DATE_MODIFIED,
                    MediaStore.Files.FileColumns.MIME_TYPE,
                    MediaStore.Files.FileColumns.SIZE
            };
    
            String sortOrder = MediaStore.Files.FileColumns.DATE_MODIFIED + " DESC";
    
            String selection = MediaStore.Files.FileColumns.MIME_TYPE + " = ?";
    
            String mimeTypePdf = MimeTypeMap.getSingleton().getMimeTypeFromExtension("pdf");
            String[] selectionArgs = {mimeTypePdf};
    
            Uri collection;
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
                collection = MediaStore.Files.getContentUri(MediaStore.VOLUME_EXTERNAL);
            } else {
                collection = MediaStore.Files.getContentUri("external");
            }
    
            Cursor cursor = ctx.getContentResolver().query(collection, projection, selection, selectionArgs, sortOrder);
            if (cursor != null && cursor.moveToFirst()) {
    
                do {
                    long id = cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.Files.FileColumns._ID));
                    String path = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Files.FileColumns.DATA));
                    String dirName = "";
                    String parent = new File(path).getParent();
                    if (!parent.isEmpty()) {
                        dirName = new File(parent.toString()).getName();
                    }
                    String name = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Files.FileColumns.DISPLAY_NAME));
                    long date = cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.Files.FileColumns.DATE_MODIFIED));
                    String size = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Files.FileColumns.SIZE));
                    Uri contentUri;
                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
                        contentUri = ContentUris.withAppendedId(
                                MediaStore.Files.getContentUri(MediaStore.VOLUME_EXTERNAL), id
                        );
                    } else {
                        contentUri = ContentUris.withAppendedId(
                                MediaStore.Files.getContentUri("external"), id
                        );
                    }
    
                    if (size != null && new File(path).exists()) {
                        MediaModel mediaModel = new MediaModel();
                        mediaModel.setFileName(name);
                        mediaModel.setFilePath(path);
                        mediaModel.setUri(contentUri);
                        mediaModel.setDateAdded(date);
                        mediaModel.setFileSize(Long.parseLong(size));
                        mediaModel.setDirName(dirName);
                        pdfList.add(mediaModel);
                    }
                } while (cursor.moveToNext());
    
                cursor.close();
            }
    
            return pdfList;
        }
    

    Here is the MediaModel model class for better access of file details:

    class MediaModel {
        String fileName;
        String filePath;
        Uri uri;
        long dateAdded;
        long fileSize;
        String dirName;
    
        public String getFileName() {
            return fileName;
        }
    
        public void setFileName(String fileName) {
            this.fileName = fileName;
        }
    
        public String getFilePath() {
            return filePath;
        }
    
        public void setFilePath(String filePath) {
            this.filePath = filePath;
        }
    
        public Uri getUri() {
            return uri;
        }
    
        public void setUri(Uri uri) {
            this.uri = uri;
        }
    
        public long getDateAdded() {
            return dateAdded;
        }
    
        public void setDateAdded(long dateAdded) {
            this.dateAdded = dateAdded;
        }
    
        public long getFileSize() {
            return fileSize;
        }
    
        public void setFileSize(long fileSize) {
            this.fileSize = fileSize;
        }
    
        public String getDirName() {
            return dirName;
        }
    
        public void setDirName(String dirName) {
            this.dirName = dirName;
        }
    }
    

    Check your permission code and try this method and you’ll get it.

    And yes, make sure you load the PDFs on background thread for better performance and populate the recyclerview on main thread.

    Login or Signup to reply.
  2. If the files are not created by your app you will not be able to list them on Android 11+ devices.

    Not with file.listFiles().

    Not with MediaStore queries.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search