14

I'm developing a custom compound View that needs to access external storage. How can I implement the permission handling without involving outside parties, i.e. Activity or Fragment?

I get that I can request the permissions using the View's context, but how can I handle onRequestPermissionsResult() inside the View? Is it even possible?

If it's not possible, what would be the most elegant solution to handle something like this?

Magnus
  • 17,157
  • 19
  • 104
  • 189
  • I know your post is old and you may have moved on from this issue. There are a few libraries out there that will let you do this. – J_Strauton Feb 02 '19 at 19:29

4 Answers4

9

I'm developing a custom compound View that needs to access external storage

IMHO, that's an architecture bug. A View is for displaying stuff to the user, and sometimes for collecting low-level input events and turning them into higher-order constructs (e.g., clicks, swipes). A View should not have any connection to files, databases, etc. See the MVC, MVP, MVVM, and similar GUI architecture patterns.

WebView, which does not abide by this, causes problems (e.g., doing disk I/O on the main application thread) as a result.

How can I implement the permission handling without involving outside parties, i.e. Activity or Fragment?

You can't. It is the responsibility of the activity or fragment to request the permission, presumably before your view needs this data.

what would be the most elegant solution to handle something like this?

Extract the data-access portion of this View into something else that is managed by the activity or fragment, where the threading, permissions, and other work associated with that data access can be managed.

CommonsWare
  • 986,068
  • 189
  • 2,389
  • 2,491
2

You can't work with permissions without the instance of the activity, but you can do your code prettier. If you want to send a request and handle it in one place, then you can use the example below.

Just create something looks like BaseActivity and put there such code

public class PermActivity extends Activity {

    interface OnPermissionCallback{
        void requestResult(String[] permissions, int[] grantResults);
    }

    private SparseArray<OnPermissionCallback> permissionCallback = new SparseArray<>();

    @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        permissionCallback.get(requestCode).requestResult(permissions, grantResults);
    }

    public void addPermissionCallback(int requestCode, OnPermissionCallback  callback){
        permissionCallback.put(requestCode, callback);
    }
}

And now in our client code, we can do something like that

class SomeClasThatWorksWithPerms{

    private PermActivity activity;

    public SomeClasWorksWithPerms(PermActivity activity) {
        this.activity = activity;
    }

    void foo(){
        if (ContextCompat.checkSelfPermission(activity, WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED){
            // do something
        }else {
            activity.addPermissionCallback(0, (perms, grantResults) -> {
                if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                    foo(); // try one more
                }
            });
            activity.requestPermissions(new String[]{WRITE_EXTERNAL_STORAGE}, 0);
        }
    }
}

I have used spareArray and indexation by the request code but you can use another way of storing callbacks.

It's very simple example, you can see something more serious there https://github.com/mrizyver/Fl_Launcher/blob/master/app/src/main/java/com/izyver/fllauncher/presentation/activity/FlActivity.kt - as you can see, it is activity https://github.com/mrizyver/Fl_Launcher/blob/master/app/src/main/java/com/izyver/fllauncher/presentation/loaders/WallpaperLoader.kt - our client code that works with permissions

Izyver
  • 41
  • 4
0

let us assume you need to call the requestPermissionLauncher from a dialog fragment when a user clicks on "OK" or some other button. here is the requestPermissionLauncher found in MainActivity or you can put it in any other activity where the dialog fragment is called from.

public ActivityResultLauncher<String> requestPermissionLauncher =
        registerForActivityResult(new ActivityResultContracts.RequestPermission(), isGranted -> {
            if (isGranted) {
                // Permission is granted. Continue the action or workflow in your
                // app.
               
            } else {
                // Explain to the user that the feature is unavailable because the
                // features requires a permission that the user has denied. At the
                // same time, respect the user's decision. Don't link to system
                // settings in an effort to convince the user to change their
                // decision.
               
            }
        });

here is the code source if you want to refer https://developer.android.com/training/permissions/requesting

Then in your dialog fragment use the following code to call to the instance requestPermissionLauncher

((MainActivity)getContext()).requestPermissionLauncher.launch(Manifest.permission.[*your permission goes here*]);
Abey Bruck
  • 532
  • 5
  • 7
-2

It's only possible in Activities and Fragments.

What you can do is copy public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) in your View and call that method in the corresponding one in the Activity or Fragment where the Context is.

sylv.mf
  • 47
  • 4