33

I building an application that sampling GPS on starting. As you probably know, permissions are requested during run-time from Android M and above.

So, in my case I'm starting with checking if permissions are needed, like this:

private void permissionForAndroidM()
{
    if (Build.VERSION.SDK_INT > 22) {
        String[] allPermissionNeeded = {
                Manifest.permission.WRITE_EXTERNAL_STORAGE,
                Manifest.permission.ACCESS_FINE_LOCATION,
                Manifest.permission.ACCESS_COARSE_LOCATION,
                Manifest.permission.CAMERA,
                Manifest.permission.RECORD_AUDIO};

        List<String> permissionNeeded = new ArrayList<>();
        for (String permission : allPermissionNeeded)
            if (checkSelfPermission(permission) != PackageManager.PERMISSION_GRANTED)
                permissionNeeded.add(permission);
        if (permissionNeeded.size() > 0) {
            requestPermissions(permissionNeeded.toArray(new String[0]), 0);
        }
    }
}

but Android continuing running the code and asking for GPS data ( = crash, because the user didn't accept the permission request).

I find many solutions about waiting for user input (like using DialogInterface.OnClickListener, link, but it's cannot implemented in this case, because I'm not creating the dialog).

Bottom line, the question: How can I wait for user answer from the Android permission dialog ?

Community
  • 1
  • 1
AsfK
  • 3,328
  • 4
  • 36
  • 73

5 Answers5

26

You can handle user response overriding method

public void onRequestPermissionsResult(
    int requestCode,
    String[] permissions,
    int[] grantResults
)

from your activity.

xarlymg89
  • 2,552
  • 2
  • 27
  • 41
Valentin Yuryev
  • 764
  • 7
  • 18
  • 4
    I don't understand how adding this to the onRequest function would stop the thread until permissions received. Can you please clarify? – Rahul Feb 08 '21 at 21:01
22

Android continuing running the code and asking for GPS data ( = crash, because the user didn't accept the permission request).

Like many things in Android, requestPermissions() is asynchronous. The user has not even been prompted for the permissions by the time this method returns.

How can I wait for user answer from the Android permission dialog ?

You don't.

If you find that you already have the permission, you do your work. If you find that you have to request permission, you delay doing that work until you get the permission, in onRequestPermissionsResult().

CommonsWare
  • 986,068
  • 189
  • 2,389
  • 2,491
  • If I would not do my work, I will not be able to do it later without user command then. But I don't want to bother my user. – Dims Jul 23 '20 at 16:54
20

I worked it the below way:

1- Created a Boolean helper to check permissions granted:

public class UtilPermissions {
    public static boolean hasPermissions(Context context, String... allPermissionNeeded)
    {
        if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
                && context != null && allPermissionNeeded != null) 
            for (String permission : allPermissionNeeded) 
                if (ActivityCompat.checkSelfPermission(context, permission) != PackageManager.PERMISSION_GRANTED)
                     return false;
        return true;
    }
}

2- Created a Splash screen as:

public class Splash extends Activity {
    private static final int PERMISSION_ALL = 0;
    private Handler h;
    private Runnable r;
  /*  
    SharedPreferences mPrefs;
    final String settingScreenShownPref = "settingScreenShown";
    final String versionCheckedPref = "versionChecked";
  */
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        h = new Handler();
        r = new Runnable() {
            @Override
            public void run() {
                Toast.makeText(Splash.this, "Runnable started", Toast.LENGTH_SHORT).show();

  /*          // (OPTIONAL) these lines to check if the `First run` ativity is required
                int versionCode = BuildConfig.VERSION_CODE;
                String versionName = BuildConfig.VERSION_NAME;

                mPrefs = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
                SharedPreferences.Editor editor = mPrefs.edit();

                Boolean settingScreenShown = mPrefs.getBoolean(settingScreenShownPref, false);
                int savedVersionCode = mPrefs.getInt(versionCheckedPref, 1);

                if (!settingScreenShown || savedVersionCode != versionCode) {
                    startActivity(new Intent(Splash.this, FirstRun.class));
                    editor.putBoolean(settingScreenShownPref, true);
                    editor.putInt(versionCheckedPref, versionCode);
                    editor.commit();
                }
                else
  */
                startActivity(new Intent(Splash.this, MainActivity.class));
                finish();    
            }
        };

        String[] PERMISSIONS = {
                READ_PHONE_STATE,
                MODIFY_AUDIO_SETTINGS,
                ACCESS_FINE_LOCATION,
                READ_SMS
        };

        if(!UtilPermissions.hasPermissions(this, PERMISSIONS)){
            ActivityCompat.requestPermissions(this, PERMISSIONS, PERMISSION_ALL);
        }
        else
            h.postDelayed(r, 1500);
    }

  // Put the below OnRequestPermissionsResult code here
}

3- Created the OnRequestPermissionsResult as below:

@Override
public void onRequestPermissionsResult(int requestCode,
                                       String permissions[], int[] grantResults) {

    int index = 0;
    Map<String, Integer> PermissionsMap = new HashMap<String, Integer>();
    for (String permission : permissions){
        PermissionsMap.put(permission, grantResults[index]);
        index++;
    }

    if((PermissionsMap.get(ACCESS_FINE_LOCATION) != 0)
            || PermissionsMap.get(READ_SMS) != 0){
        Toast.makeText(this, "Location and SMS permissions are a must", Toast.LENGTH_SHORT).show();
        finish();
    }
    else
    {
        h.postDelayed(r, 1500);
    }
}

4- Defined the Splash screen as Launcher in the AndroidManifest.xml as:

<activity
    android:name=".MainActivity"
    android:label="@string/app_name"
    android:theme="@style/AppTheme.NoActionBar">
</activity>

<activity
    android:name=".Splash"
    android:configChanges="orientation|keyboardHidden|screenSize"
    android:theme="@style/SplashTheme">

    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>

5- In the styles.xml defined the SplashTheme as:

<style name="SplashTheme" parent="Theme.AppCompat.NoActionBar">
    <item name="android:windowBackground">@drawable/Splash</item>
</style>

6- The @drawable/Splash.xmls is:

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">

    <item
        android:drawable="@color/lightgray"/>

    <item>
        <bitmap
            android:gravity="center"
            android:src="@mipmap/ic_launcher"/>
    </item>

</layer-list>

7- In the values/colors.xmls the lightgray color had been defined as below:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <color name="lightgray">#D3D3D3</color>
</resources>
Hasan A Yousef
  • 22,789
  • 24
  • 132
  • 203
2

After asking a permission, with a while loop, you can check if the permission is granted continuously , if granted you can change the value and exit while loop.

Boolean isAnswered = true;

    while(isAnswered){

        if (ActivityCompat.checkSelfPermission(getApplication(), Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(getApplication(), Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
            Log.d("isanswered", String.valueOf(isAnswered));
        }
        else{
            isAnswered = false;
            Log.d("isanswered", String.valueOf(isAnswered));
        }
    }
Caner Kaya
  • 52
  • 1
  • 6
  • 1
    NO! This might work as a dirty hot fix. If you'll have 20 objects and all of them want one or more permission then you will keep alive 20 threads only for some permissions??? + there is no delay so each thread will be in full load.. – Stoica Mircea Oct 27 '20 at 12:38
2

I think this is late but maybe it will help someone. So, let's say you want to start a position listener in your app but you don't have the permissions yet to register the listener, but you want to be notified as soon as somewhere your permission is granted. Just listen for that event.

object PermissionsHelper{
     private val listeners = ArrayList<PermissionListener>()

     fun addListener(listener: PermissionListener){ ... }

     fun notifyPermissionsGranted(list : ArrayList<String>) { ... }
     /** OR */
     fun notifyPermissionsStatusChanged() { ... } // and all of them will check again.
}

so where you want to listen for position you will check that your permission is granted or not, if not you call PermissionsHelper.getInstance().addListener(...) and in UI when onRequestPermissionsResult has a granted permission you will call ass follows PermissionsHelper.getInstance().notifyPermissionsGranted( ... ) then you resume your registering to system for location.

Stoica Mircea
  • 782
  • 10
  • 22