├── .gitignore ├── .idea ├── .gitignore ├── compiler.xml ├── gradle.xml ├── kotlinc.xml ├── misc.xml └── vcs.xml ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── farimarwat │ │ └── permissionmateexample │ │ └── ExampleInstrumentedTest.kt │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── farimarwat │ │ │ └── permissionmateexample │ │ │ └── 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-anydpi-v33 │ │ └── ic_launcher.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 │ │ └── xml │ │ ├── backup_rules.xml │ │ └── data_extraction_rules.xml │ └── test │ └── java │ └── com │ └── farimarwat │ └── permissionmateexample │ └── 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/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 20 | 21 | -------------------------------------------------------------------------------- /.idea/kotlinc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 9 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PermissionMate 2 | PermissionMat is an Android library that simplifies the management of permissions in just two simple steps. With its user-friendly interface and streamlined process, PermissionMat makes it easier than ever to handle permissions in your Android applications. Plus, it's even easier to use than the popular PermissionX library! 3 | 4 | In addition to simplifying the management of permissions, PermissionMat also provides the ability to manage permission rationales. This means that if a user denies a permission request, the library will automatically show a rationale explaining why the permission is necessary and ask the user to grant it again. With this feature, you can ensure that your app has all the permissions it needs to function properly, while still providing a seamless user experience 5 | 6 | #### Simple Example 7 | Create the instance of PermissionMate in create method 8 | ``` 9 | val pm = PermissionMate.Builder(this) 10 | .setPermissions(listOf(PMate( 11 | Manifest.permission.CALL_PHONE, 12 | false,"Phone call permission is required to work app correctly"))) 13 | .build() 14 | ``` 15 | Next call start() on any click listener button 16 | ``` 17 | pm.start() 18 | ``` 19 | **Note:Don't call the start() in the same function for example creating and calling it in a single button click. If you do that then this will raise an exception** 20 | 21 | ### Implementation 22 | ``` 23 | implementation "io.github.farimarwat:permissionmate:1.2" 24 | ``` 25 | 26 | ### Step 1 (Place permissions in manifest file) 27 | ``` 28 | 30 | 31 | 32 | 33 | ``` 34 | 35 | ### Step 2 (Create instance of PermissionMate inside create) 36 | ``` 37 | lateinit var pm:PermissionMate 38 | override fun onCreate(savedInstanceState: Bundle?) { 39 | super.onCreate(savedInstanceState) 40 | setContentView(binding.root) 41 | mContext = this 42 | pm = PermissionMate.Builder(this) 43 | .setPermissions( 44 | mutableListOf( 45 | PMate( 46 | Manifest.permission.CALL_PHONE, 47 | false,"Phone call permission is required to work app correctly"), 48 | PMate( 49 | Manifest.permission.CAMERA, 50 | true,"Camera permission is required to work app correctly"), 51 | PMate( 52 | Manifest.permission.READ_CONTACTS, 53 | false,"Read Contacts permission is required to work app correctly") 54 | ).also { 55 | if(Build.VERSION.SDK_INT <= Build.VERSION_CODES.Q){ 56 | it.add(PMate( 57 | Manifest.permission.WRITE_EXTERNAL_STORAGE, 58 | true,"Storage permission is required to work app correctly")) 59 | } 60 | } 61 | ) 62 | .setListener(object : PermissionMateListener { 63 | override fun onPermission(permission: String, granted: Boolean) { 64 | super.onPermission(permission, granted) 65 | Log.e(TAG,"Permission: $permission Granted: $granted") 66 | } 67 | 68 | override fun onError(error: String) { 69 | super.onError(error) 70 | Log.e(TAG,error) 71 | } 72 | 73 | override fun onComplete(permissionsgranted: List) { 74 | super.onComplete(permissionsgranted) 75 | Log.e(TAG,"Permission Granted: ${permissionsgranted.size}") 76 | } 77 | }) 78 | .build() 79 | 80 | //Your more code goes here 81 | ... 82 | } 83 | ``` 84 | Finaly call start(): 85 | ``` 86 | binding.button.setOnClickListener { 87 | 88 | pm.start() 89 | } 90 | ``` 91 | ### To avoid memory leaks: 92 | Kindly call pm.dispose() on onDestroy() method of the activity. So it will prevent from memory leaks. 93 | ``` 94 | override fun onDestroy() { 95 | super.onDestroy() 96 | pm.dispose() 97 | } 98 | ``` 99 | ### Documentation 100 | 101 | #### PMate Data Class 102 | It takes three arguments 103 | - Permission 104 | - Required (If it is required and set to true then it will do not go further until it get this permission) 105 | - Msg (show a rationale explaining message why the permission is necessary) 106 | 107 | #### PermissionMateListener 108 | This is an optional callback listener to get results: 109 | ``` 110 | PermissionMateListener { 111 | override fun onPermission(permission: String, granted: Boolean) { 112 | super.onPermission(permission, granted) 113 | Log.e(TAG,"Permission: $permission Granted: $granted") 114 | } 115 | 116 | override fun onError(error: String) { 117 | super.onError(error) 118 | Log.e(TAG,error) 119 | } 120 | 121 | override fun onComplete(permissionsgranted: List) { 122 | super.onComplete(permissionsgranted) 123 | Log.e(TAG,"Permission Granted: ${permissionsgranted.size}") 124 | } 125 | } 126 | ``` 127 | 128 | #### Change Log 129 | **Version 1.2** 130 | 131 | A Crash fixed. Reported by (Wajahat Khan) 132 | 133 | Lifecycleevent added to detect permission on resume, getting back from settings. 134 | 135 | **Version 1.1** 136 | 137 | Minor bugs fixed 138 | 139 | **Version 1.0** 140 | 141 | Initial release 142 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.application' 3 | id 'org.jetbrains.kotlin.android' 4 | } 5 | 6 | android { 7 | namespace 'com.farimarwat.permissionmateexample' 8 | compileSdk 33 9 | 10 | defaultConfig { 11 | applicationId "com.farimarwat.permissionmateexample" 12 | minSdk 21 13 | targetSdk 33 14 | versionCode 1 15 | versionName "1.0" 16 | 17 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 18 | } 19 | 20 | buildTypes { 21 | release { 22 | minifyEnabled false 23 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 24 | } 25 | } 26 | compileOptions { 27 | sourceCompatibility JavaVersion.VERSION_1_8 28 | targetCompatibility JavaVersion.VERSION_1_8 29 | } 30 | kotlinOptions { 31 | jvmTarget = '1.8' 32 | } 33 | buildFeatures{ 34 | viewBinding true 35 | } 36 | } 37 | 38 | dependencies { 39 | 40 | implementation 'androidx.core:core-ktx:1.7.0' 41 | implementation 'androidx.appcompat:appcompat:1.6.1' 42 | implementation 'com.google.android.material:material:1.8.0' 43 | implementation 'androidx.constraintlayout:constraintlayout:2.1.4' 44 | testImplementation 'junit:junit:4.13.2' 45 | androidTestImplementation 'androidx.test.ext:junit:1.1.5' 46 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1' 47 | implementation "io.github.farimarwat:permissionmate:1.2" 48 | } -------------------------------------------------------------------------------- /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/com/farimarwat/permissionmateexample/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package com.farimarwat.permissionmateexample 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("com.farimarwat.permissionmateexample", appContext.packageName) 23 | } 24 | } -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 7 | 8 | 9 | 10 | 19 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /app/src/main/java/com/farimarwat/permissionmateexample/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.farimarwat.permissionmateexample 2 | 3 | import android.content.Context 4 | import androidx.appcompat.app.AppCompatActivity 5 | import android.os.Bundle 6 | import com.farimarwat.permissionmate.PMate 7 | import com.farimarwat.permissionmate.PermissionMate 8 | import com.farimarwat.permissionmateexample.databinding.ActivityMainBinding 9 | import android.Manifest 10 | import android.os.Build 11 | import android.util.Log 12 | import com.farimarwat.permissionmate.PermissionMateListener 13 | import com.farimarwat.permissionmate.TAG 14 | 15 | class MainActivity : AppCompatActivity() { 16 | val binding by lazy { 17 | ActivityMainBinding.inflate(layoutInflater) 18 | } 19 | lateinit var mContext: Context 20 | lateinit var pm:PermissionMate 21 | override fun onCreate(savedInstanceState: Bundle?) { 22 | super.onCreate(savedInstanceState) 23 | setContentView(binding.root) 24 | mContext = this 25 | 26 | pm = PermissionMate.Builder(this) 27 | .setPermissions( 28 | mutableListOf( 29 | PMate( 30 | Manifest.permission.CALL_PHONE, 31 | false,"Phone call permission is required to work app correctly"), 32 | PMate( 33 | Manifest.permission.CAMERA, 34 | true,"Camera permission is required to work app correctly"), 35 | PMate( 36 | Manifest.permission.READ_CONTACTS, 37 | false,"Read Contacts permission is required to work app correctly") 38 | ).also { 39 | if(Build.VERSION.SDK_INT <= Build.VERSION_CODES.Q){ 40 | it.add(PMate( 41 | Manifest.permission.WRITE_EXTERNAL_STORAGE, 42 | true,"Storage permission is required to work app correctly")) 43 | } 44 | } 45 | ) 46 | .setListener(object : PermissionMateListener { 47 | override fun onPermission(permission: String, granted: Boolean) { 48 | super.onPermission(permission, granted) 49 | Log.e(TAG,"Permission: $permission Granted: $granted") 50 | } 51 | 52 | override fun onError(error: String) { 53 | super.onError(error) 54 | Log.e(TAG,error) 55 | } 56 | 57 | override fun onComplete(permissionsgranted: List) { 58 | super.onComplete(permissionsgranted) 59 | Log.e(TAG,"Permission Granted: ${permissionsgranted.size}") 60 | } 61 | }) 62 | .build() 63 | binding.button.setOnClickListener { 64 | 65 | pm.start() 66 | } 67 | } 68 | 69 | override fun onDestroy() { 70 | super.onDestroy() 71 | pm.dispose() 72 | } 73 | } -------------------------------------------------------------------------------- /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 |