Search code examples
javaandroidandroid-emulator

READ_EXTERNAL_STORAGE Permission request not showing on emulator


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 :


Solution

  • 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