3

I'm trying to set up a way to register permission result handlers after the activity is initialized, by creating a Map<Integer, Runnable> to hold the handlers, when I request permission I use a randomly generated code and save a runnable to the Map of codes/handlers.

Here's what I have so far in my activity class

private final HashMap<Integer, Runnable> onPermission = new HashMap<>();

//i call this from other classes to ask for a permission and register a handler
public void requestPermission(Runnable onGranted, String...permissions) {
    int code = Permissions.permissionRequestCode();
    onPermission.put(code, onGranted);
    requestPermissions(permissions, code);
}

//overriding this method to check if the Map has a handler for the granted request
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    Runnable handler = onPermission.get(requestCode);
    if (handler != null && isGranted(grantResults)) {
        handler.run();
        onPermission.remove(requestCode);
    }
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}

//utility method to check if all the requested permissions are granted
private boolean isGranted(int[] results) {
    for (int i : results) {
        if (i != PackageManager.PERMISSION_GRANTED) {
            return false;
        }
    }
    return true;
}

And here is an example of how this can be used, where owner is an my Activity instance :

private void readImages() {
    if (ContextCompat.checkSelfPermission(owner, Manifest.permission.READ_EXTERNAL_STORAGE)
            == PackageManager.PERMISSION_GRANTED) {
        loadImages();
    } else {
        owner.requestPermission(this::loadImages, Manifest.permission.READ_EXTERNAL_STORAGE);
    }
}

Now running this on an actual device (Android 12, API 31), works exactly like I would expect (the request shows, and the handler is executed when the permission is granted), however on the emulator (Android 13, API 33), nothing shows when the permission is requested, and it remains "not granted", the permission doesn't even show in the "App info" even though it's included in the manifest.

Real device :

screenshot from the real device

Emulator :

screenshot from the emulator

My manifest :

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

    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="Mesa"
        android:roundIcon="@mipmap/ic_launcher"
        android:supportsRtl="true"
        android:theme="@style/Theme.Mesa.NoActionBar"
        android:usesCleartextTraffic="true">
        <activity
            android:name=".app.Mesa"
            android:configChanges="uiMode|screenSize|colorMode"
            android:exported="true"
            android:theme="@style/Theme.Mesa.NoActionBar"
            android:windowSoftInputMode="adjustResize">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

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

</manifest>

My build.gradle :

android {
    compileSdk 33

    defaultConfig {
        applicationId "org.luke.mesa"
        minSdk 24
        targetSdk 33
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
        proguardFiles 'proguard-rules.pro'
    }

    buildTypes {
        release {
            minifyEnabled true
        }

        debug {
            minifyEnabled false
        }
    }
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_11
        targetCompatibility JavaVersion.VERSION_11
        allprojects {
            tasks.withType(JavaCompile){
                options.compilerArgs <<"-Xlint:deprecation"
            }
        }
    }
    buildFeatures {
        viewBinding true
        dataBinding true
    }
    packagingOptions {
        resources {
            merges += ['META-INF/DEPENDENCIES']
        }
    }
    namespace 'org.luke.mesa'
    dependenciesInfo {
        includeInApk true
        includeInBundle true
    }
    buildToolsVersion '33.0.0'
}

possible duplicates which didn't solve my problem :

SDIDSA
  • 894
  • 10
  • 19
  • 1
    Could it be this? https://developer.android.com/about/versions/13/behavior-changes-13#granular-media-permissions "Granular media permissions" – Ken Wolf Mar 03 '23 at 14:17
  • 1
    OH SHUCKS, I scanned through that page multiple times thinking it was "optional", just now I noticed the use of the word "must" : ** you must request one or more of the following granular media permissions instead of the READ_EXTERNAL_STORAGE permission** – SDIDSA Mar 03 '23 at 14:21
  • 1
    yes that was the issue, I just tried READ_MEDIA_IMAGES and the request shows properly and the handler is executed, thank you so much. consider writing an answer and I'll mark it accepted – SDIDSA Mar 03 '23 at 14:24

1 Answers1

3

As of Android 13 and above, apps that request READ_EXTERNAL_STORAGE must instead request one or more of the following more granular permissions:

  • READ_MEDIA_IMAGES
  • READ_MEDIA_VIDEO
  • READ_MEDIA_AUDIO

Reference: https://developer.android.com/about/versions/13/behavior-changes-13#granular-media-permissions

Ken Wolf
  • 23,133
  • 6
  • 63
  • 84