package Ġcom . d oring . android . permissions Ċ Ċ import Ġandroid . content . pm . PackageManager Ċ import Ġandroidx . activity . Component Activity Ċ import Ġandroidx . activity . result . Activity Result Launcher Ċ import Ġandroidx . activity . result . contract . Activity Result Contract Ċ import Ġandroidx . activity . result . contract . Activity Result Contracts Ċ import Ġandroidx . core . content . Context Compat Ċ import Ġandroidx . lifecycle . LiveData Ċ import Ġandroidx . lifecycle . as LiveData Ċ import Ġandroidx . lifecycle . lifecycle Scope Ċ import Ġkotlinx . coroutines . flow . Mutable Shared Flow Ċ import Ġkotlinx . coroutines . flow . Shared Flow Ċ import Ġkotlinx . coroutines . launch Ċ import Ġkotlinx . coroutines . sync . Mutex Ċ import Ġkotlinx . coroutines . sync . with Lock Ċ import Ġkotlin . coroutines . resume Ċ import Ġkotlin . coroutines . suspend Coroutine Ċ Ċ /** Ċ Ġ* ĠThis Ġ* must * Ġbe Ġcalled Ġun condition ally , Ġas Ġpart Ġof Ġinitialization Ġpath , Ċ Ġ* Ġtypically Ġas Ġa Ġfield Ġinitializer Ġof Ġan ĠActivity Ċ Ġ*/ Ċ class ĠPermissions ( ĊĠĠĠ Ġprivate Ġval Ġactivity : ĠComponent Activity , ĊĠĠĠ Ġprivate Ġval Ġmanifest Permissions : ĠArray < String >, ĊĠĠĠ Ġprivate Ġval Ġcustom Permissions : ĠArray < Custom Permission < *, Ġ* >>, Ċ ) Ġ{ ĊĠĠĠ Ġcompanion Ġobject Ġ{ ĊĠĠĠĠĠĠĠ Ġconst Ġval ĠTH RESH OLD _ REQUEST _ RESULT _ PERIOD _ TIME Ġ= Ġ 3 0 0 L ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġclass ĠCustom Permission < I , ĠO >( ĊĠĠĠĠĠĠĠ Ġval Ġname : ĠString , ĊĠĠĠĠĠĠĠ Ġ// ĠNeed Ġto Ġassign Ġa Ġguide Ġscreen Ġto Ġshow Ġbefore Ġrequesting Ġpermission . ĊĠĠĠĠĠĠĠ Ġ// ĠIt Ġis Ġnullable Ġbecause Ġthere Ġare Ġcases Ġwhere Ġyou Ġonly Ġwant Ġto Ġcheck Ġthe Ġgranted Ġpermission . ĊĠĠĠĠĠĠĠ Ġval Ġpermission Check Guide : Ġ((( I ?) Ġ-> ĠUnit ) Ġ-> ĠUnit )? Ġ= Ġnull , ĊĠĠĠĠĠĠĠ Ġ// ĠFunction Ġto Ġcheck Ġfor Ġpermission ĊĠĠĠĠĠĠĠ Ġval Ġpermission Check Result Callback : Ġ() Ġ-> ĠBoolean , ĊĠĠĠĠĠĠĠ Ġval Ġactivity Result Contract : ĠActivity Result Contract < I , ĠO >, ĊĠĠĠ Ġ) ĊĊĠĠĠ Ġ// ĠMutex Ġfor Ġrequest Ġcustom Ġpermission Ġse quent ially ĊĠĠĠ Ġprivate Ġval Ġmutex Ġ= ĠMutex () ĊĊĠĠĠ Ġprivate Ġvar Ġcurrent Request Custom Permission : ĠCustom Permission < *, Ġ* >? Ġ= Ġnull ĊĠĠĠ Ġprivate Ġvar Ġrequest StartTime Ġ= Ġ 0 L ĊĠĠĠ Ġprivate Ġvar Ġrequest And Result Time Diff Ġ= Ġ 0 L ĊĊĠĠĠ Ġ// ĠData Ġto Ġbe Ġput Ġin Ġthe Ġflow Ġafter Ġall Ġpermission Ġrequests Ġare Ġcompleted ĊĠĠĠ Ġprivate Ġval Ġmutable Result Map Ġ= Ġmutable Map Of < String , ĠBoolean >() ĊĊĠĠĠ Ġ// ĠFlow Ġthat Ġcan Ġget Ġthe Ġresult Ġof Ġrequesting Ġpermission ĊĠĠĠ Ġprivate Ġval Ġmutable Result Flow Ġ= ĠMutable Shared Flow < Map < String , ĠBoolean >>( replay Ġ= Ġ 1 ) ĊĠĠĠ Ġval Ġresult Flow : ĠShared Flow < Map < String , ĠBoolean >> Ġ= Ġmutable Result Flow ĊĊĠĠĠ Ġ// ĠActivity Ġshould Ġuse ĠLive Data Ġto Ġget Ġresults ĊĠĠĠ Ġ// ĠFlow Ġonly Ġused Ġfor Ġspecial Ġpurposes ĊĠĠĠ Ġval Ġresult LiveData : ĠLive Data < Map < String , ĠBoolean >> Ġ= Ġresult Flow . as LiveData () ĊĊĠĠĠ Ġprivate Ġval Ġrequest Manifest Permission Launcher Ġ= ĊĠĠĠĠĠĠĠ Ġactivity . register For Activity Result ( ĊĠĠĠĠĠĠĠĠĠĠĠ ĠActivity Result Contracts . Request Multiple Permissions () ĊĠĠĠĠĠĠĠ Ġ) Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġmutable Result Map Ġ+= Ġit ĊĠĠĠĠĠĠĠĠĠĠĠ Ġrequest And Result Time Diff Ġ= ĠSystem . currentTimeMillis () Ġ- Ġrequest StartTime ĊĠĠĠĠĠĠĠĠĠĠĠ Ġrequest Custom Permissions () ĊĠĠĠĠĠĠĠ Ġ} ĊĊĠĠĠ Ġprivate Ġval Ġcustom Permission Launcher Map Ġ= Ġcustom Permissions . associate Ġ{ ĊĠĠĠĠĠĠĠ Ġval Ġlauncher Ġ= Ġactivity . register For Activity Result ( it . activity Result Contract ) Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġcurrent Request Custom Permission ?. let Ġ{ Ġpermission Ġ-> ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġmutable Result Map [ permission . name ] Ġ= Ġpermission . permission Check Result Callback () ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ} ĊĠĠĠĠĠĠĠĠĠĠĠ Ġmutex . unlock () ĊĠĠĠĠĠĠĠ Ġ} ĊĠĠĠĠĠĠĠ Ġ@ Suppress (" UN CHECK ED _ CAST ") ĊĠĠĠĠĠĠĠ Ġit . name Ġto Ġ( launcher Ġas ? ĠActivity Result Launcher < Any ?> ) ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġprivate Ġfun Ġrequest Custom Permissions () Ġ{ ĊĠĠĠĠĠĠĠ Ġactivity . lifecycle Scope . launch Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġcustom Permissions . forEach Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġif Ġ(! it . permission Check Result Callback ()) Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġval Ġlaunch Input Ġ= Ġsuspend Coroutine < Any ?> Ġ{ Ġcont Ġ-> ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġval Ġcallback Ġ= Ġ{ Ġlaunch Requested : ĠAny ? Ġ-> ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġcont . resume ( launch Requested ) ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ} ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġit . permission Check Guide ?. invoke ( callback ) Ġ?: Ġcont . resume ( false ) ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ} ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġif Ġ( launch Input Ġ!= Ġnull ) Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġmutex . lock () ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġcurrent Request Custom Permission Ġ= Ġit ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġval Ġfailed If Null : ĠUnit ? Ġ= ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġcustom Permission Launcher Map [ it . name ]?. run Catch ing Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġlaunch ( launch Input ) ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ} ?. get OrNull () ĊĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ// ĠWhen Ġlauncher Ġis Ġnull Ġor Ġstart Ġactivity Ġfails Ġdue Ġto Ġhandling Ġintent ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġif Ġ( failed If Null Ġ== Ġnull ) Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġmutex . unlock () ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġmutable Result Map [ it . name ] Ġ= Ġit . permission Check Result Callback () ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ} ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ} Ġelse Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ// ĠUser Ġcloses Ġor Ġden ies Ġpermission Ġrequest Ġdialog . ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġmutable Result Map [ it . name ] Ġ= Ġfalse ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ} ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ} Ġelse Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ// ĠAlready Ġgranted Ġpermission . ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġmutable Result Map [ it . name ] Ġ= Ġtrue ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ} ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ} ĊĊĠĠĠĠĠĠĠĠĠĠĠ Ġ// Wait Ġuntil Ġall Ġthe Ġrequested Ġpermissions Ġare Ġterminated Ġand Ġthen Ġemit Ġthem . ĊĠĠĠĠĠĠĠĠĠĠĠ Ġmutex . with Lock Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ// Ġsus pending Ġon Ġbuffer Ġoverflow . Ġmutable Result Flow Ġhas Ġno Ġbuffer ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġmutable Result Flow . emit ( mutable Result Map ) ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ} ĊĠĠĠĠĠĠĠ Ġ} ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġfun Ġis Shown Any Manifest Permission (): ĠBoolean Ġ{ ĊĠĠĠĠĠĠĠ Ġ// ĠIf Ġthe Ġpermission Ġrequest Ġis Ġdenied Ġtwice , Ġthe Ġpop - up Ġno Ġlonger Ġappears ĊĠĠĠĠĠĠĠ Ġ// Ġit Ġtakes Ġ 5 0 ms Ġfor ĠPixel Ġ 4 Ġfrom Ġrequest Ġto Ġresponse . ĊĠĠĠĠĠĠĠ Ġ// ĠThe Ġpermission Ġrequest Ġpopup Ġis Ġreg arded Ġas Ġpop ping Ġup Ġonly Ġwhen Ġit Ġtakes Ġmore Ġthan Ġthe Ġlimit Ġtime ĊĠĠĠĠĠĠĠ Ġreturn Ġrequest And Result Time Diff Ġ> ĠTH RESH OLD _ REQUEST _ RESULT _ PERIOD _ TIME ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġfun Ġget Denied Permissions (): ĠList < String > Ġ{ ĊĠĠĠĠĠĠĠ Ġreturn Ġmanifest Permissions . filter Ġ{ ĠContext Compat . check Self Permission ( activity , Ġit ) Ġ== ĠPackage Manager . PERMISSION _ DEN IED Ġ} Ġ+ ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġcustom Permissions . filter Ġ{ Ġ! it . permission Check Result Callback () Ġ}. map Ġ{ Ġit . name Ġ} ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġfun Ġget Granted Permissions (): ĠList < String > Ġ{ ĊĠĠĠĠĠĠĠ Ġreturn Ġmanifest Permissions . filter Ġ{ ĠContext Compat . check Self Permission ( activity , Ġit ) Ġ== ĠPackage Manager . PERMISSION _ GRAN TED Ġ} Ġ+ ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġcustom Permissions . filter Ġ{ Ġit . permission Check Result Callback () Ġ}. map Ġ{ Ġit . name Ġ} ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġfun Ġrequest Permission () Ġ{ ĊĠĠĠĠĠĠĠ Ġmutable Result Map . clear () ĊĠĠĠĠĠĠĠ Ġcurrent Request Custom Permission Ġ= Ġnull ĊĊĠĠĠĠĠĠĠ Ġval Ġcount Denied Manifest Permissions Ġ= Ġmanifest Permissions . count Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ ĠContext Compat . check Self Permission ( activity , Ġit ) Ġ== ĠPackage Manager . PERMISSION _ DEN IED ĊĠĠĠĠĠĠĠ Ġ} ĊĊĠĠĠĠĠĠĠ Ġif Ġ( count Denied Manifest Permissions Ġ== Ġ 0 ) Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġmutable Result Map Ġ+= Ġmanifest Permissions . associate Ġ{ Ġit Ġto Ġtrue Ġ} ĊĠĠĠĠĠĠĠĠĠĠĠ Ġrequest Custom Permissions () ĊĠĠĠĠĠĠĠ Ġ} Ġelse Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġrequest And Result Time Diff Ġ= Ġ 0 L ĊĠĠĠĠĠĠĠĠĠĠĠ Ġrequest StartTime Ġ= ĠSystem . currentTimeMillis () ĊĠĠĠĠĠĠĠĠĠĠĠ Ġrequest Manifest Permission Launcher . launch ( manifest Permissions ) ĊĠĠĠĠĠĠĠ Ġ} ĊĠĠĠ Ġ} Ċ Ċ }