easypermissions icon indicating copy to clipboard operation
easypermissions copied to clipboard

Support new activity result API

Open mtwalli opened this issue 5 years ago • 9 comments

Current permission request in Android will be deprecated starting from androidx.activity:activity:1.2.0 and androidx.fragment:fragment:1.3.0.

The new Activity Result API will be used instead of current one. It would be nice to add support for it.

mtwalli avatar May 17 '20 15:05 mtwalli

@Mo-Jamal thanks for the heads up! I hadn't heard about this new API. You're right, EasyPermissions should use it.

samtstern avatar May 18 '20 18:05 samtstern

Is there any plan to support the new API? thanks

sachithd avatar Nov 03 '20 03:11 sachithd

I haven't had time to work on this, if anyone wants to submit a PR I am happy to review.

samtstern avatar Nov 03 '20 12:11 samtstern

@samtstern I have used this library for years due to its simplicity. with the new api "registerForActivityResult" I found a way to simplify it further. I'd love to have your opinion on this.

https://github.com/xanscale/LocalhostToolkit/blob/master/app/src/main/java/localhost/toolkit/app/appcompat/RequestPermissionLauncher.java

the usage are like:

private val permissionLauncher = RequestPermissionLauncher(this) // this must be class variable

permissionLauncher.launch("titleRational", "messageRational", Manifest.permission.XXX, Manifest.permission.YYY).observe(this) {
    if (it == RequestPermissionsManager.PermissionResult.GRANTED) {
       // TODO do what you want
    } else if (it == RequestPermissionsManager.PermissionResult.DENIED) {
       // TODO user refuse
    } else if (it == RequestPermissionsManager.PermissionResult.PERMANENTLY_DENIED) {
       // TODO user permanently refuse
    }  
}

xanscale avatar Feb 11 '21 22:02 xanscale

@xanscale I can't access that link you posted but this seems really interesting! If you're interested in sending a Pull Request for this I would be happy to review it. Or if you want to save yourself some work you could try outlining the complete approach in an issue so we could discuss it.

samtstern avatar Feb 12 '21 12:02 samtstern

@samtstern sorry, i renamed the file. just updated previous post

this system not use annotation to intercept callback, use livedata. i don't know, how to integrate with easypermission.

i linked to you because i used some your ideas in my code, and i loved easypermission, so i will love your comment to my library

xanscale avatar Feb 12 '21 13:02 xanscale

@samtstern any review?

xanscale avatar Feb 16 '21 20:02 xanscale

@xanscale sorry I haven't had time to take a look! I don't really work on EasyPermissions much anymore since it's been stable for so long, I'll try to find some time when I can.

samtstern avatar Feb 17 '21 14:02 samtstern

I used this https://github.com/xanscale/LocalhostToolkit/blob/master/app/src/main/java/localhost/toolkit/app/appcompat/RequestPermissionLauncher.java

With an adjustment that I have added the request code when calling launch() and also I'm passing the request code back.

import android.content.Context;
import android.content.pm.PackageManager;
import android.text.TextUtils;

import androidx.activity.result.ActivityResultCallback;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;

import java.util.Map;

public final class RequestPermissionContract implements ActivityResultCallback<Map<String, Boolean>> {
    private final RequestPermissionCallback callback;
    private int currentRequestCode;
    private final ActivityResultLauncher<String[]> permissionLauncher;
    private FragmentActivity activity;
    private Fragment fragment;

    public RequestPermissionContract(Fragment fragment, RequestPermissionCallback callback) {
        this.callback = callback;
        this.fragment = fragment;
        permissionLauncher = fragment.registerForActivityResult(new ActivityResultContracts.RequestMultiplePermissions(), this);
    }

    public RequestPermissionContract(FragmentActivity activity, RequestPermissionCallback callback) {
        this.callback = callback;
        this.activity = activity;
        permissionLauncher = activity.registerForActivityResult(new ActivityResultContracts.RequestMultiplePermissions(), this);
    }

    /**
     * launch the dialog to request the permissions.
     * You can pass as many permissions as required.
     *
     * @param rationale is required of you want to show the dialog before requesting the permission and permission was already denied previously.
     */
    public void launch(int requestCode, @Nullable String rationale, String... permissions) {
        if (checkSelfPermission(permissions)) {
            // permissions are already granted, so pass the callback
            callback.onActivityResult(requestCode, PermissionResult.GRANTED);
            return;
        }
        if (shouldShowRequestPermissionRationale(permissions)) {
            if (!TextUtils.isEmpty(rationale)) {
                // user want to show the rationale dialog
                AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
                if (rationale != null)
                    builder.setMessage(rationale);
                builder.setPositiveButton(android.R.string.ok, (dialog, which) -> launchPermissionLauncher(requestCode, permissions));
                builder.setNegativeButton(android.R.string.cancel, (dialog, which) -> callback.onActivityResult(requestCode, PermissionResult.DENIED));
                builder.create().show();
                return;
            }
        }

        // launch the permissions
        launchPermissionLauncher(requestCode, permissions);
    }

    private void launchPermissionLauncher(int requestCode, String... permissions) {
        this.currentRequestCode = requestCode;
        // request the permissions
        permissionLauncher.launch(permissions);
    }

    @Override
    public void onActivityResult(Map<String, Boolean> results) {
        if (results.isEmpty()) {
            callback.onActivityResult(currentRequestCode, PermissionResult.DENIED);
            return;
        }
        String[] permissions = results.keySet().toArray(new String[0]);
        if (checkSelfPermission(permissions)) {
            // all permissions are granted
            callback.onActivityResult(currentRequestCode, PermissionResult.GRANTED);
        } else if (shouldShowRequestPermissionRationale(permissions)) {
            callback.onActivityResult(currentRequestCode, PermissionResult.DENIED);
        } else {
            // permissions are permanently disabled
            callback.onActivityResult(currentRequestCode, PermissionResult.PERMANENTLY_DENIED);
        }
    }

    private FragmentActivity getActivity() {
        if (activity != null)
            return activity;
        else
            return fragment.requireActivity();
    }

    private boolean shouldShowRequestPermissionRationale(String... permissions) {
        for (String perm : permissions)
            if (ActivityCompat.shouldShowRequestPermissionRationale(getActivity(), perm))
                return true;
        return false;
    }

    /**
     * check if all the permissions are granted
     */
    private boolean checkSelfPermission(String... permissions) {
        return checkSelfPermission(getActivity(), permissions);
    }

    public static boolean checkSelfPermission(Context context, String... permissions) {
        for (String perm : permissions)
            if (ContextCompat.checkSelfPermission(context, perm) != PackageManager.PERMISSION_GRANTED)
                return false;
        return true;
    }

    public interface RequestPermissionCallback {
        /**
         * function to pass the result with request code.
         * Will return the aggregated result of all permissions i.e. if all the permissions are granted then it will be true.
         */
        void onActivityResult(int requestCode, PermissionResult result);
    }

    public enum PermissionResult {GRANTED, DENIED, PERMANENTLY_DENIED}
}

Usage:

private final RequestPermissionContract permissionLauncher = new RequestPermissionContract(this, (requestCode, permissionResult) -> {
        if (requestCode == PERMISSION_REQUEST_CODE) {
            if (permissionResult == RequestPermissionContract.PermissionResult.GRANTED) {
                // Permission is granted
            } else if (permissionResult == RequestPermissionContract.PermissionResult.DENIED) {
                // permission denied
            } else if (permissionResult == RequestPermissionContract.PermissionResult.PERMANENTLY_DENIED) {
                // permanently denied
            }
        }
    });

// call the below code on click of button for e.g:
permissionLauncher.launch(PERMISSION_REQUEST_CODE, "Allow this permission to use the functionality", Manifest.permission.CALL_PHONE);

deepak786 avatar Jul 06 '21 17:07 deepak786