googlesamples / easypermissions

Simplify Android M system permissions
https://firebaseopensource.com/projects/googlesamples/easypermissions/
Apache License 2.0
9.86k stars 1.46k forks source link

Support new activity result API #304

Closed mtwalli closed 1 year ago

mtwalli commented 4 years ago

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.

samtstern commented 4 years ago

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

sachithd commented 3 years ago

Is there any plan to support the new API? thanks

samtstern commented 3 years ago

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

xanscale commented 3 years ago

@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
    }  
}
samtstern commented 3 years ago

@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.

xanscale commented 3 years ago

@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 commented 3 years ago

@samtstern any review?

samtstern commented 3 years ago

@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.

deepak786 commented 3 years ago

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);