Search code examples
androiddownloadandroid-contentresolverscoped-storage

Does passing the Activity Context to an singleton affect the getContentResolver()?


**Problem:**I'm updating an app to Android 11 (API 30) and I cannot get the getContentResolver() to work in the following instance though it works elsewhere.

With help from this forum and reading the Scoped Storage docs, elsewhere in the app, the following code works within the Actiities they reside; which includes inputting and outputting from the shared download folder using the getContentResolver(). The file types are a variety.

What I'm trying to do: This app also allows a user (with approved permissions) to attach a variety of files (e.g., docs, audio, video, etc.) to notes in a database. In this case, a PDF. However, the file information gets passed to a Singleton class created to avoid redundancy in the same code within other activities, so I just pass the Context if needed.

As I mentioned, this code works fine when contained within the Activity and the only difference I see is the context is passed to a singleton class. I was hoping someone would see something I'm not.

I do realize the URI is not passed (see the resultLauncher code, but I don't believe that really matters, I can take the file path and change it to an URI, which I have successfully multiple times. Aside from passing to a singleton class, this is the only difference (I can see) compared to my other code.

I could be way off here, the first app and all. A user needs to have the option to attach multiple file types.

**INTENT**
 btnAddFile.setOnClickListener(v -> {
     Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
     intent.addCategory(Intent.CATEGORY_OPENABLE);
     intent.setType("*/*");
     resultLauncher.launch(intent);

 });

RESULT LAUNCHER

 resultLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), result -> {
     if (result.getResultCode() == Activity.RESULT_OK && result.getData() != null){
  Uri uri = result.getData().getData();
  String util = UriUtils.getPathFromUri(this, uri);
  tableLayoutFiles.addView(BuildTableLayout.setupFilesTableRow(EditNote.this,tableLayoutFiles, util,false));
     }
 });

CODE WITHIN SINGLETON CLASS METHOD (a TableLayout of files and file paths TextViews are passed. So the following code resides in a loop iterating the TextView (i.e., tv) file paths.

File f = new File(tv.getText().toString());
String n = f.getName();
try {
     InputStream fis = context.getContentResolver().openInputStream(Uri.fromFile(new File(f.getPath())));
     ByteArrayOutputStream bos = new ByteArrayOutputStream();
     byte[] buf = new byte[1024];
     for (int read; (read = fis.read(buf)) != -1; ) {
  bos.write(buf, 0, read);
     }
     fis.close();

     noteFiles.add(new Files(0, n, bos.toByteArray()));
 } catch (Exception e) {
     Log.e("getContentResolver", e.toString());
     e.printStackTrace();
 }

Solution

  • Well I solved my own problem and as I suspected, I had to pass the Uri directly to the getContentResolver().openInputStream() and could not convert a path using Uri.fromFile(). Another learners experience.

    Adding a new variable HashMap<String, Uri>() called fileURIs to my method, I passed the file Uri to my method. I used a file path as the key and the file's Uri as the key value. I updated the getContentResolver().openInputStream() within the for loop to retrieve the correct file Uri.

    Update:

    InputStream fis = context.getContentResolver().openInputStream(fileURIs.get(f.getPath()));
    

     File f = new File(tv.getText().toString());
     String n = f.getName();
     try {
         InputStream fis = context.getContentResolver().openInputStream(fileURIs.get(f.getPath()));
         ByteArrayOutputStream bos = new ByteArrayOutputStream();
         byte[] buf = new byte[1024];
         for (int read; (read = fis.read(buf)) != -1; ) {
             bos.write(buf, 0, read);
         }
         fis.close();
    
         noteFiles.add(new Files(0, n, bos.toByteArray()));
     } catch (Exception e) {
         Log.e("getContentResolver", e.toString());
         e.printStackTrace();
     }
    

    And I should clarify, the use of Uri.fromFile() does work on file/directories that are not "access denied", so passing a cache file path can be passed to the ContextResolver() without issue in this fashion. In my case, when files are imported from an xml, they get moved to the cache directory which eliminates the issue and why Uri.fromFile() works. That was part of my confusion and not being fully eductated on scoped storage.