Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to properly pause app until permission request is finished?

I'm writing an app in which its only function is acting as a receiver for Bluetooth Low Energy. The app is useless if the user does not allow BLE. When the app is started for the first time, I want it to ask the user for Locations Permission (since Android requires it for BLE). I bring the permissions dialog up, but the rest of the app continues while the user is reading the dialog, starting BLEScanner and all that. I want the app to pause while the user is reading the dialog and deciding what to do. Here's my setup:

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_COARSE_LOCATION}, REQUEST_CODE);
        }

        //More UI preparation stuff

    @Override
    public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
        switch (requestCode) {
            case REQUEST_CODE: {
                if (permissions[0].equals(Manifest.permission.ACCESS_COARSE_LOCATION)) {
                    if (grantResults[0] != PackageManager.PERMISSION_GRANTED) {
                        PermissionDialog newDialog = new PermissionDialog();
                        newDialog.show(getSupportFragmentManager(), "Request Location Permission");
                }
            }
        }
    }

Where "PermissionDialog" is a different class that uses an DialogFragment to explain why the app needs the permission and then closes/restarts the app. In this case, the rest of the app continues, attempting to do Bluetooth stuff while the user is still reading the permission dialog that has popped up! Naively, I thought I could use a synchronize lock to do it, as below, but in this case, the callback is never called:

private final Object initLock = new Object();
public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_COARSE_LOCATION}, REQUEST_CODE);
        }

        Log.i("Lock", "At the lock!");

        try {
            synchronized (initLock) {
                initLock.wait();
            }
        }
        catch (InterruptedException e) {
            //TODO find out what to do here
        }

        Log.i("Lock", "Past the lock!");


    @Override
    public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
        switch (requestCode) {
            case REQUEST_CODE: {
                if (permissions[0].equals(Manifest.permission.ACCESS_COARSE_LOCATION)) {
                    if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                        synchronized (initLock){
                            initLock.notify();
                        }
                    }
                    else{
                        PermissionDialog newDialog = new PermissionDialog();
                        newDialog.show(getSupportFragmentManager(), "Request Location Permission");
                    }
                }
            }
        }
    }

What's the proper way to do this? I have a few ideas, but they all balloon in scope. Do I need to make another class that inherits AppCompatActivity to do the permissions and I call it as a thread? But then how do I know where the onRequestPermissionResult callback will go? I'm at a loss.

like image 323
MW2023 Avatar asked Sep 17 '25 16:09

MW2023


2 Answers

Put the code inside your //More UI preparation stuff to a new method. After the permissions, say 'init()'

private void init(){
    //More UI preparation stuff
}

Then inside onCreate, if permission is already granted, call init(); otherwise request permission.

@Override
protected void onCreate(Bundle savedInstanceState) {

    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
        ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_COARSE_LOCATION}, REQUEST_CODE);

    } else {
        init();
    }
}

Now handle the user's response inside onRequestPermissionsResult - If user has granted, initialize the UI, otherwise block the feature and/or notify the user about the issue.

@Override
public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
    switch (requestCode) {
        case REQUEST_CODE: {
            if (permissions[0].equals(Manifest.permission.ACCESS_COARSE_LOCATION)) {
                if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                    init();

                } else{
                    PermissionDialog newDialog = new PermissionDialog();
                    newDialog.show(getSupportFragmentManager(), "Request Location Permission");
                }
            }
        }
    }
}

PS: For this same purpose, I have prepared a library which makes this process a lot easier. Have a look at my library.

like image 164
Nabin Bhandari Avatar answered Sep 20 '25 06:09

Nabin Bhandari


You can use EasyPermissions and implement EasyPermissions.PermissionCallbacks to resolve it. And add this annotation:@AfterPermissionGranted before the method you want to run such as :

AfterPermissionGranted(PERMISSION_REQUESTCODE_BASE)
private void requestPerminssions(){
    if(EasyPermissions.hasPermissions(this,PERMISSIONS)){
        int hasCityData=config.getInt("isDataEmpty",0);
        if(hasCityData==0){
            new CountryInfoThread().execute();
        }else {
            new LocalPositionThread().execute();
        }
    }
}
like image 41
Seraph Gabriel Avatar answered Sep 20 '25 07:09

Seraph Gabriel