GardenMobile & interfaces

Working with Android runtime permissions

A historical permission-controller example, with explicit requests, result callbacks, and the limits of an all-permissions approach.

Declaring a permission and receiving a runtime grant are separate steps. This older example used a small permission-controller library to connect an Activity’s request flow to its result callbacks.

Historical Android example. The Kotlin and manifest snippets are preserved from the original note. They have not been rebuilt against a current Android SDK, and the storage permissions and callback APIs need a version-specific review before reuse.

The library and manifest

The original installation reference is nc-android-permission on JitPack. The source note did not pin a library release, so this revision does not invent a dependency version or a Gradle declaration.

Its manifest collected the permissions and camera features used by the example:

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

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

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

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

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

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

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

<uses-feature android:name="android.hardware.camera" />

<uses-feature android:name="android.hardware.camera.autofocus" />

This is the historical manifest, not a list every application should request. Decide which capabilities a feature actually needs, then check the Android behavior for the application’s target SDK.

Request a specific capability

The first approach implements ExplicitPermissionResult, creates an NExplicitPermission controller, requests the camera permission, and forwards onRequestPermissionsResult to that controller:

import android.os.Bundle
import android.util.Log
import androidx.appcompat.app.AppCompatActivity
import com.nhancv.npermission.ExplicitPermissionResult
import com.nhancv.npermission.NExplicitPermission

class MainActivity : AppCompatActivity(), ExplicitPermissionResult {
 
    private var nCameraPermission: NExplicitPermission? = null
 
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
 
        // @nhancv 2019-08-25: Create permission controller
        nCameraPermission = NExplicitPermission(this,
                explicitPermissionResult = this)
    }
 
    override fun onResume() {
        super.onResume()
        nCameraPermission?.requestPermission(android.Manifest.permission.CAMERA, false)
    }
 
    override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults)
        nCameraPermission!!.onRequestPermissionsResult(requestCode, permissions, grantResults)
    }
 
    override fun onExplicitPermissionResult(permission: String, isGranted: Boolean) {
        Log.e(TAG, "onExplicitPermissionResult: $permission isGranted: $isGranted")
    }
 
    companion object {
        private val TAG = MainActivity::class.java.simpleName
    }
}

Notice the lifecycle placement: this sample asks during onResume. Current Android permission guidance recommends requesting a permission in the context of the feature that needs it, handling denial, and allowing the rest of the application to remain useful. The historical snippet is retained rather than presented as an updated interaction pattern.

The older all-permissions alternative

The second approach delegates to NAllPermission and waits for all declared permissions:

import android.os.Bundle
import android.util.Log
import androidx.appcompat.app.AppCompatActivity
import com.nhancv.npermission.AllPermissionResult
import com.nhancv.npermission.NAllPermission
 
 
class MainActivity : AppCompatActivity(), AllPermissionResult {
 
    private var nAllPermission: NAllPermission? = null
 
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
 
        // @nhancv 2019-08-25: Create permission controller
        nAllPermission = NAllPermission(this,
                allPermissionResult = this)
    }
 
    override fun onResume() {
        super.onResume()
        nAllPermission?.forceAllPermissionGranted()
    }
 
    override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults)
        nAllPermission!!.onRequestPermissionsResult(requestCode, permissions, grantResults)
    }
 
    override fun onAllPermissionGranted() {
        Log.e(TAG, "onAllPermissionGranted: isAllGranted")
    }
 
    companion object {
        private val TAG = MainActivity::class.java.simpleName
    }
}

This option explains the library’s older API, but an all-permissions gate is a different user experience from a contextual request. Do not interpret the method name as a reason to block unrelated features when a permission is declined.

What to check before reuse

  • Which permissions are necessary for the specific action?
  • Which rules apply to the target Android version and target SDK?
  • What happens after denial or revocation?
  • Does the library release fit the application’s current callback and lifecycle APIs?

For a fresh implementation, start with the current runtime permission workflow, then choose how to integrate it. The snippets above document the earlier experiment without claiming a current build result.

← Back to the garden