├── .gitignore ├── .idea ├── .gitignore ├── .name ├── compiler.xml ├── deploymentTargetDropDown.xml ├── gradle.xml ├── misc.xml └── vcs.xml ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── eu │ │ └── tutorials │ │ └── permissiondemo │ │ └── ExampleInstrumentedTest.kt │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── eu │ │ │ └── tutorials │ │ │ └── permissiondemo │ │ │ └── MainActivity.kt │ └── res │ │ ├── drawable-v24 │ │ └── ic_launcher_foreground.xml │ │ ├── drawable │ │ └── ic_launcher_background.xml │ │ ├── layout │ │ └── activity_main.xml │ │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── values-night │ │ └── themes.xml │ │ └── values │ │ ├── colors.xml │ │ ├── strings.xml │ │ └── themes.xml │ └── test │ └── java │ └── eu │ └── tutorials │ └── permissiondemo │ └── ExampleUnitTest.kt ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/caches 5 | /.idea/libraries 6 | /.idea/modules.xml 7 | /.idea/workspace.xml 8 | /.idea/navEditor.xml 9 | /.idea/assetWizardSettings.xml 10 | .DS_Store 11 | /build 12 | /captures 13 | .externalNativeBuild 14 | .cxx 15 | local.properties 16 | -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | -------------------------------------------------------------------------------- /.idea/.name: -------------------------------------------------------------------------------- 1 | Permission demo -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/deploymentTargetDropDown.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 20 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 10 | 11 | 12 | 13 | 14 | 16 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.application' 3 | id 'kotlin-android' 4 | } 5 | 6 | android { 7 | compileSdk 31 8 | 9 | defaultConfig { 10 | applicationId "eu.tutorials.permissiondemo" 11 | minSdk 21 12 | targetSdk 31 13 | versionCode 1 14 | versionName "1.0" 15 | 16 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 17 | } 18 | 19 | buildTypes { 20 | release { 21 | minifyEnabled false 22 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 23 | } 24 | } 25 | compileOptions { 26 | sourceCompatibility JavaVersion.VERSION_1_8 27 | targetCompatibility JavaVersion.VERSION_1_8 28 | } 29 | kotlinOptions { 30 | jvmTarget = '1.8' 31 | } 32 | } 33 | 34 | dependencies { 35 | implementation 'androidx.core:core-ktx:1.6.0' 36 | implementation 'androidx.appcompat:appcompat:1.3.1' 37 | implementation 'com.google.android.material:material:1.4.0' 38 | implementation 'androidx.constraintlayout:constraintlayout:2.1.0' 39 | testImplementation 'junit:junit:4.+' 40 | androidTestImplementation 'androidx.test.ext:junit:1.1.3' 41 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' 42 | } -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /app/src/androidTest/java/eu/tutorials/permissiondemo/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package eu.tutorials.permissiondemo 2 | 3 | import androidx.test.platform.app.InstrumentationRegistry 4 | import androidx.test.ext.junit.runners.AndroidJUnit4 5 | 6 | import org.junit.Test 7 | import org.junit.runner.RunWith 8 | 9 | import org.junit.Assert.* 10 | 11 | /** 12 | * Instrumented test, which will execute on an Android device. 13 | * 14 | * See [testing documentation](http://d.android.com/tools/testing). 15 | */ 16 | @RunWith(AndroidJUnit4::class) 17 | class ExampleInstrumentedTest { 18 | @Test 19 | fun useAppContext() { 20 | // Context of the app under test. 21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext 22 | assertEquals("eu.tutorials.permissiondemo", appContext.packageName) 23 | } 24 | } -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 14 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /app/src/main/java/eu/tutorials/permissiondemo/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package eu.tutorials.permissiondemo 2 | 3 | import android.Manifest 4 | import android.content.pm.PackageManager 5 | import android.os.Build 6 | import androidx.appcompat.app.AppCompatActivity 7 | import android.os.Bundle 8 | import android.util.Log 9 | import android.widget.Button 10 | import android.widget.Toast 11 | import androidx.activity.result.ActivityResultLauncher 12 | import androidx.activity.result.contract.ActivityResultContracts 13 | import androidx.appcompat.app.AlertDialog 14 | import androidx.core.content.ContextCompat 15 | 16 | class MainActivity : AppCompatActivity() { 17 | 18 | 19 | //Todo 1: This time we creat the Activity result launcher of type Array 20 | private val cameraAndLocationResultLauncher:ActivityResultLauncher> = 21 | registerForActivityResult( 22 | ActivityResultContracts.RequestMultiplePermissions() 23 | ){permissions-> 24 | /** 25 | Here it returns a Map of permission name as key with boolean as value 26 | Todo 2: We loop through the map to get the value we need which is the boolean 27 | value 28 | */ 29 | Log.d("MainActivity","Permissions $permissions") 30 | permissions.entries.forEach { 31 | val permissionName = it.key 32 | //Todo 3: if it is granted then we show its granted 33 | val isGranted = it.value 34 | if (isGranted) { 35 | //check the permission name and perform the specific operation 36 | if ( permissionName == Manifest.permission.ACCESS_FINE_LOCATION) { 37 | Toast.makeText( 38 | this, 39 | "Permission granted for location", 40 | Toast.LENGTH_LONG 41 | ) 42 | .show() 43 | }else{ 44 | //check the permission name and perform the specific operation 45 | Toast.makeText( 46 | this, 47 | "Permission granted for Camera", 48 | Toast.LENGTH_LONG 49 | ) 50 | .show() 51 | } 52 | } else { 53 | if ( permissionName == Manifest.permission.ACCESS_FINE_LOCATION) { 54 | Toast.makeText( 55 | this, 56 | "Permission denied for location", 57 | Toast.LENGTH_LONG 58 | ) 59 | .show() 60 | }else{ 61 | Toast.makeText( 62 | this, 63 | "Permission denied for Camera", 64 | Toast.LENGTH_LONG 65 | ) 66 | .show() 67 | } 68 | } 69 | } 70 | } 71 | 72 | override fun onCreate(savedInstanceState: Bundle?) { 73 | super.onCreate(savedInstanceState) 74 | setContentView(R.layout.activity_main) 75 | 76 | val btnCameraPermission: Button = findViewById(R.id.btnCameraPermission) 77 | 78 | btnCameraPermission.setOnClickListener { 79 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && shouldShowRequestPermissionRationale( 80 | Manifest.permission.CAMERA 81 | ) 82 | ) { 83 | showRationaleDialog(" Permission Demo requires camera access", 84 | "Camera cannot be used because Camera access is denied") 85 | } else { 86 | // You can directly ask for the permission. 87 | // The registered ActivityResultCallback gets the result of this request. 88 | cameraAndLocationResultLauncher.launch( 89 | arrayOf(Manifest.permission.CAMERA,Manifest.permission.ACCESS_FINE_LOCATION) 90 | ) 91 | 92 | } 93 | } 94 | 95 | } 96 | 97 | /** 98 | * Shows rationale dialog for displaying why the app needs permission 99 | * Only shown if the user has denied the permission request previously 100 | */ 101 | private fun showRationaleDialog( 102 | title: String, 103 | message: String, 104 | ) { 105 | val builder: AlertDialog.Builder = AlertDialog.Builder(this) 106 | builder.setTitle(title) 107 | .setMessage(message) 108 | .setPositiveButton("Cancel") { dialog, _ -> 109 | dialog.dismiss() 110 | } 111 | builder.create().show() 112 | } 113 | 114 | } -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 15 | 18 | 21 | 22 | 23 | 24 | 30 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 |