package Ġme . h uf man . android aut oid rive . phone ui Ċ Ċ import Ġandroid . app . Activity Ċ import Ġandroid . app . Notification Manager Ċ import Ġandroid . content . Intent Ċ import Ġandroid . content . pm . PackageManager Ċ import Ġandroid . net . Uri Ċ import Ġandroid . os .* Ċ import Ġandroidx . browser . custom tabs . Custom Tabs Intent Ċ import Ġandroid . util . Log Ċ import Ġandroid . widget . Toast Ċ import Ġcom . adam rat z man . spotify . Spot ify Scope Ċ import Ġcom . adam rat z man . spotify . get Spot ify Pk ce Code Challenge Ċ import Ġkotlinx . coroutines . run Blocking Ċ import Ġme . h uf man . android aut oid rive . AppSettings Ċ import Ġme . h uf man . android aut oid rive . Mutable AppSettings Receiver Ċ import Ġme . h uf man . android aut oid rive . R Ċ import Ġme . h uf man . android aut oid rive . music . spotify . Spot ify WebApi Ċ import Ġme . h uf man . android aut oid rive . music . controllers . Spot ify App Controller Ċ import Ġme . h uf man . android aut oid rive . music . spotify . Spot ify Auth StateManager Ċ import Ġnet . openid . app auth .* Ċ import Ġnet . openid . app auth . connectivity . Default Connection Builder Ċ import Ġjava . util . concurrent . Count DownLatch Ċ import Ġjava . util . concurrent . atomic . Atomic Reference Ċ import Ġkotlin . random . Random Ċ Ċ /** Ċ Ġ* ĠActivity Ġthat Ġis Ġused Ġto Ġperform Ġthe ĠSpotify Ġauthorization Ġprocess Ġand Ġupdate Ġthe Ġ[ Auth State ] Ġwith Ċ Ġ* Ġthe Ġresulting Ġauthorization Ġcode . ĠThe Ġactivity Ġwill Ġbe Ġclosed Ġonce Ġthe Ġprocess Ġhas Ġeither Ġbeen Ċ Ġ* Ġcanceled , Ġfailed , Ġor Ġsucceeds . Ċ Ġ*/ Ċ class ĠSpotify Authorization Activity : ĠActivity () Ġ{ Ċ ĉ comp anion Ġobject Ġ{ Ċĉ ĉ const Ġval ĠTAG Ġ= Ġ" Authorization Activity " ĊĊĉ ĉ const Ġval ĠREQUEST _ CODE _ SP OT IFY _ LOGIN Ġ= Ġ 2 3 5 6 ĊĊĉ ĉ const Ġval ĠEX TRA _ AUTHORIZATION _ RESULT Ġ= Ġ" authorization Result " Ċĉ ĉ const Ġval ĠAUTH ORIZ ATION _ C ANCE LED Ġ= Ġ 1 Ċĉ ĉ const Ġval ĠAUTH ORIZ ATION _ SUCCESS Ġ= Ġ 0 Ċĉ ĉ const Ġval ĠAUTH ORIZ ATION _ FAILED Ġ= Ġ- 1 ĊĊĉ ĉ val ĠCODE _ VER IFIER Ġ= Ġgenerate Code Verifier String () ĊĊĉ ĉ private Ġconst Ġval ĠAUTH ORIZ ATION _ URI Ġ= Ġ" https :// accounts . spotify . com / authorize " Ċĉ ĉ private Ġconst Ġval ĠACCESS _ TOKEN _ URI Ġ= Ġ" https :// accounts . spotify . com / api / token " ĊĊĉ ĉ /** Ċĉĉ Ġ* ĠGenerates Ġa Ġ 4 5 Ġcharacter Ġrandom Ġalpha Ġnumeric Ġstring Ġto Ġbe Ġused Ġfor Ġthe Ġcode Ġverifier Ġin Ċĉĉ Ġ* Ġthe ĠPK CE Ġauthentication Ġflow . Ċĉĉ Ġ*/ Ċĉ ĉ private Ġfun Ġgenerate Code Verifier String (): ĠString Ġ{ Ċĉĉ ĉ val Ġchar Pool Ġ= Ġ(' a ' .. ' z ') Ġ+ Ġ(' A ' .. ' Z ') Ġ+ Ġ(' 0 ' .. ' 9 ') Ċĉĉ ĉ return Ġ( 1 .. 4 5 ). map Ġ{ Ġchar Pool [ Random . nextInt ( 0 , Ġchar Pool . size )] Ġ}. join ToString ("") Ċĉ ĉ } Ċ ĉ } ĊĊ ĉ private Ġval Ġauth Request Ġ= ĠAtomic Reference < Authorization Request >() Ċ ĉ private Ġval Ġauth Intent Ġ= ĠAtomic Reference < Custom Tabs Intent >() Ċ ĉ private Ġvar Ġauth Intent Latch Ġ= ĠCount DownLatch ( 1 ) Ċ ĉ private Ġval Ġscopes Ġ= ĠlistOf ( Ċĉĉ ĉ Spot ify Scope . USER _ MO DIFY _ PLAY BACK _ STATE . uri , Ċĉĉ ĉ Spot ify Scope . USER _ LIBRARY _ READ . uri , Ċĉĉ ĉ Spot ify Scope . PLAY LIST _ MO DIFY _ PRIVATE . uri , Ċĉĉ ĉ Spot ify Scope . PLAY LIST _ READ _ PRIVATE . uri , Ċĉĉ ĉ Spot ify Scope . U GC _ IMAGE _ UPLOAD . uri ) Ċ ĉ private Ġval Ġlazy Auth Service Ġ= Ġlazy Ġ{ Ċĉ ĉ create Authorization Service () Ċ ĉ } Ċ ĉ private Ġval Ġauth Service : ĠAuthorization Service Ġby Ġlazy Auth Service Ċ ĉ private Ġval Ġapp Settings Receiver : ĠMutable AppSettings Receiver Ġby Ġlazy Ġ{ Ċĉ ĉ Mutable AppSettings Receiver ( application Context ) Ċ ĉ } Ċ ĉ private Ġval Ġauth StateManager : ĠSpotify Auth StateManager Ġby Ġlazy Ġ{ Ċĉ ĉ Spot ify Auth StateManager . getInstance ( app Settings Receiver ) Ċ ĉ } ĊĊ ĉ override Ġfun ĠonCreate ( savedInstanceState : ĠBundle ?) Ġ{ Ċĉ ĉ super . onCreate ( savedInstanceState ) Ċĉ ĉ initialize App Auth () Ċĉ ĉ do Auth () Ċ ĉ } ĊĊ ĉ override Ġfun Ġon Activity Result ( request Code : ĠInt , ĠresultCode : ĠInt , Ġdata : ĠIntent ?) Ġ{ Ċĉ ĉ super . on Activity Result ( request Code , ĠresultCode , Ġdata ) Ċĉ ĉ process Authorization Code ( request Code , ĠresultCode , Ġdata ) Ċ ĉ } ĊĊ ĉ override Ġfun Ġon Destroy () Ġ{ Ċĉ ĉ super . on Destroy () Ċĉ ĉ if Ġ( lazy Auth Service . isInitialized ()) Ġ{ Ċĉĉ ĉ auth Service . dispose () Ċĉ ĉ } Ċ ĉ } ĊĊ ĉ /** Ċĉ Ġ* ĠPerforms Ġthe Ġauthorization Ġrequest Ċĉ Ġ*/ Ċ ĉ private Ġfun Ġdo Auth () Ġ{ Ċĉ ĉ Log . d ( TAG , Ġ" Authorization Ġstarted ") Ċĉ ĉ try Ġ{ Ċĉĉ ĉ auth Intent Latch . await () Ċĉ ĉ } Ġcatch Ġ( ex : ĠInterruptedException ) Ġ{ Ċĉĉ ĉ Log . w ( TAG , Ġ" Inter rupted Ġwhile Ġwaiting Ġfor Ġauth Ġintent ") Ċĉ ĉ } ĊĊĉ ĉ val Ġintent Ġ= Ġauth Service . get Authorization Request Intent ( Ċĉĉĉ ĉ auth Request . get (), Ċĉĉĉ ĉ auth Intent . get ()) ĊĊĉ ĉ startActivity For Result ( intent , ĠREQUEST _ CODE _ SP OT IFY _ LOGIN ) Ċ ĉ } ĊĊ ĉ private Ġfun Ġwarm Up Browser () Ġ{ Ċĉ ĉ auth Intent Latch Ġ= ĠCount DownLatch ( 1 ) Ċĉ ĉ Log . d ( TAG , Ġ" W arm ing Ġup Ġbrowser Ġinstance Ġfor Ġauth Ġrequest ") Ċĉ ĉ val Ġintent Builder Ġ= Ġauth Service . create Custom Tabs Intent Builder ( auth Request . get (). to Uri ()) Ċĉ ĉ auth Intent . set ( intent Builder . build ()) Ċĉ ĉ auth Intent Latch . count Down () Ċ ĉ } ĊĊ ĉ private Ġfun Ġcreate Auth Request () Ġ{ Ċĉ ĉ Log . d ( TAG , Ġ" Creating Ġauth Ġrequest ") Ċĉ ĉ val Ġcode Challenge Ġ= Ġget Spot ify Pk ce Code Challenge ( CODE _ VER IFIER ) Ċĉ ĉ val Ġcode Challenge Method Ġ= Ġ" S 2 5 6 " Ċĉ ĉ val ĠclientId Ġ= Ġpackage Manager . getApplication Info ( packageName , ĠPackage Manager . GET _ META _ DATA ) Ċĉĉĉ ĉ . metaData . getString (" com . spotify . music . API _ KEY ", Ġ" un available ") Ċĉ ĉ val Ġauth RequestBuilder Ġ= ĠAuthorization Request . Builder ( Ċĉĉĉ ĉ auth StateManager . get Authorization Service Configuration () !! , Ċĉĉĉ ĉ clientId , Ċĉĉĉ ĉ ResponseType Values . CODE , Ċĉĉĉ ĉ Uri . parse ( Spot ify App Controller . RE DIRECT _ URI )) Ċĉĉĉ ĉ . set Scopes ( scopes ) Ċĉĉĉ ĉ . set Code Verifier ( CODE _ VER IFIER , Ġcode Challenge , Ġcode Challenge Method ) Ċĉ ĉ auth Request . set ( auth RequestBuilder . build ()) Ċ ĉ } ĊĊ ĉ private Ġfun Ġinitialize App Auth () Ġ{ Ċĉ ĉ Log . d ( TAG , Ġ" Initializing ĠApp Auth ") Ċĉ ĉ re create Authorization Service () ĊĊĉ ĉ if Ġ( auth StateManager . get Authorization Service Configuration () Ġ== Ġnull ) Ġ{ Ċĉĉ ĉ val Ġauth Endpoint Uri Ġ= ĠUri . parse ( AUTHORIZATION _ URI ) Ċĉĉ ĉ val Ġtoken Endpoint Uri Ġ= ĠUri . parse ( ACCESS _ TOKEN _ URI ) Ċĉĉ ĉ val Ġconfig Ġ= ĠAuthorization Service Configuration ( auth Endpoint Uri , Ġtoken Endpoint Uri ) Ċĉĉ ĉ auth StateManager . replace Auth State ( Auth State ( config )) Ċĉ ĉ } ĊĊĉ ĉ run Blocking Ġ{ Ċĉĉ ĉ create Auth Request () Ċĉĉ ĉ warm Up Browser () Ċĉ ĉ } Ċ ĉ } ĊĊ ĉ private Ġfun Ġrecreate Authorization Service () Ġ{ Ċĉ ĉ if Ġ( lazy Auth Service . isInitialized ()) Ġ{ Ċĉĉ ĉ Log . d ( TAG , Ġ" Discard ing Ġexisting ĠAuth Service Ġinstance ") Ċĉĉ ĉ auth Service . dispose () Ċĉ ĉ } Ċĉ ĉ auth Request . set ( null ) Ċĉ ĉ auth Intent . set ( null ) Ċ ĉ } ĊĊ ĉ private Ġfun Ġcreate Authorization Service (): ĠAuthorization Service Ġ{ Ċĉ ĉ Log . d ( TAG , Ġ" Creating Ġauthorization Ġservice ") Ċĉ ĉ val Ġbuilder Ġ= ĠApp Auth Configuration . Builder () Ċĉ ĉ builder . setConnection Builder ( Default Connection Builder . INSTANCE ) Ċĉ ĉ return ĠAuthorization Service ( this , Ġbuilder . build ()) Ċ ĉ } ĊĊ ĉ private Ġfun Ġprocess Authorization Code ( request Code : ĠInt , ĠresultCode : ĠInt , Ġdata : ĠIntent ?) Ġ{ Ċĉ ĉ if Ġ( request Code Ġ== ĠREQUEST _ CODE _ SP OT IFY _ LOGIN ) Ġ{ Ċĉĉ ĉ if Ġ( result Code Ġ== ĠRESULT _ C ANCE LED ) Ġ{ Ċĉĉĉ ĉ on Authorization Canceled () Ċĉĉĉ ĉ return Ċĉĉ ĉ } ĊĊĉĉ ĉ data ?. let Ġ{ Ġintent Ġ-> Ċĉĉĉ ĉ val Ġresponse Ġ= ĠAuthorization Response . from Intent ( intent ) Ċĉĉĉ ĉ val Ġex Ġ= ĠAuthorization Exception . from Intent ( intent ) Ċĉĉĉ ĉ when Ġ{ Ċĉĉĉĉ ĉ response ?. authorization Code Ġ!= Ġnull Ġ-> Ġ{ Ċĉĉĉĉĉ ĉ auth StateManager . update Authorization Response ( response , Ġex ) Ċĉĉĉĉĉ ĉ on Authorization Suc ceed () Ċĉĉĉĉ ĉ } Ċĉĉĉĉ ĉ ex Ġ!= Ġnull Ġ-> Ġ{ Ċĉĉĉĉĉ ĉ on Authorization Failed (" Authorization Ġflow Ġfailed : Ġ" Ġ+ Ġex . message ) Ċĉĉĉĉ ĉ } Ċĉĉĉĉ ĉ else Ġ-> Ġ{ Ċĉĉĉĉĉ ĉ on Authorization Failed (" No Ġauthorization Ġstate Ġretained Ġ- Ġre authorization Ġrequired ") Ċĉĉĉĉ ĉ } Ċĉĉĉ ĉ } Ċĉĉ ĉ } Ċĉ ĉ } Ċ ĉ } ĊĊ ĉ /** Ċĉ Ġ* ĠAuthor ized Ġcanceled Ġworkflow . ĠThis Ġis Ġcalled Ġif Ġthe Ġauthorization Ġprocess Ġactivity Ġwas Ġcanceled . Ċĉ Ġ*/ Ċ ĉ private Ġfun Ġon Authorization Canceled () Ġ{ Ċĉ ĉ Log . d ( TAG , Ġ" Authorization Ġcanceled ") Ċĉ ĉ Toast . makeText ( this , ĠgetString ( R . string . txt _ spotify _ api _ authorization _ c anceled ), ĠToast . LENGTH _ SHORT ). show () Ċĉ ĉ finish Activity With Result ( AUTHORIZATION _ C ANCE LED ) Ċ ĉ } ĊĊ ĉ /** Ċĉ Ġ* ĠAuthor ized Ġfailed Ġworkflow . ĠThis Ġis Ġcalled Ġif Ġthe Ġauthorization Ġprocess Ġfailed . Ċĉ Ġ*/ Ċ ĉ private Ġfun Ġon Authorization Failed ( error : ĠString ?) Ġ{ Ċĉ ĉ Log . d ( TAG , Ġ" Authorization Ġfailed Ġwith Ġthe Ġerror : Ġ$ error ") Ċĉ ĉ Toast . makeText ( this , ĠgetString ( R . string . txt _ spotify _ api _ authorization _ failed ), ĠToast . LENGTH _ SHORT ). show () Ċĉ ĉ finish Activity With Result ( AUTHORIZATION _ FAILED ) Ċ ĉ } ĊĊ ĉ /** Ċĉ Ġ* ĠAuthorization Ġsuccess Ġworkflow . ĠThe Ġ[ Spot ify WebApi ] Ġweb ĠAPI Ġclient Ġinstance Ġwill Ġbe Ġinitialized Ċĉ Ġ* Ġwith Ġthe Ġauthorized Ġcode Ġobtained . Ċĉ Ġ*/ Ċ ĉ private Ġfun Ġon Authorization Suc ceed () Ġ{ Ċĉ ĉ Log . d ( TAG , Ġ" Authorization Ġprocess Ġcompleted Ġsuccessfully . ĠAuth State Ġupdated ") Ċĉ ĉ clear Not Authorized Notification () Ċĉ ĉ app Settings Receiver [ AppSettings . KEYS . SP OT IFY _ SHOW _ UN AUTHENT IC ATED _ NOTIFICATION ] Ġ= Ġ" true " Ċĉ ĉ Spot ify WebApi . getInstance ( application Context , Ġapp Settings Receiver ). initialize WebApi () Ċĉ ĉ Toast . makeText ( this , ĠgetString ( R . string . txt _ spotify _ api _ authorization _ success ), ĠToast . LENGTH _ SHORT ). show () Ċĉ ĉ finish Activity With Result ( AUTHORIZATION _ SUCCESS ) Ċ ĉ } ĊĊ ĉ /** Ċĉ Ġ* ĠFin ishes Ġthe Ġactivity Ġwith Ġthe Ġspecified Ġresult Ġconstant . Ċĉ Ġ*/ Ċ ĉ private Ġfun Ġfinish Activity With Result ( authorization Result : ĠInt ) Ġ{ Ċĉ ĉ val Ġreturn Intent Ġ= ĠIntent () Ċĉ ĉ return Intent . putExtra ( EXTRA _ AUTHORIZATION _ RESULT , Ġauthorization Result ) Ċĉ ĉ set Result ( RESULT _ OK , Ġreturn Intent ) Ċĉ ĉ finish () Ċ ĉ } ĊĊ ĉ /** Ċĉ Ġ* ĠClear s Ġthe Ġnotification Ġthat Ġis Ġdisplayed Ġwhen Ġthe Ġuser Ġis Ġnot Ġauthenticated . Ċĉ Ġ*/ Ċ ĉ private Ġfun Ġclear Not Authorized Notification () Ġ{ Ċĉ ĉ val Ġnotification Manager Ġ= Ġget System Service ( NOTIFICATION _ SERVICE ) Ġas ĠNotification Manager Ċĉ ĉ notification Manager . cancel ( Spot ify WebApi . NOTIFICATION _ REQ _ ID ) Ċ ĉ } Ċ }