package Ġtk . f rost bit . spaces hip connect Ċ Ċ import Ġandroid . app . Notification Ċ import Ġandroid . app . Notification Channel Ċ import Ġandroid . app . Notification Manager Ċ import Ġandroid . app . Service Ċ import Ġandroid . content .* Ċ import Ġandroid . content . pm . Service Info Ċ import Ġandroid . hardware . usb . Usb Device Ċ import Ġandroid . hardware . usb . Usb Manager Ċ import Ġandroid . media . Audio Attributes Ċ import Ġandroid . media . Media Metadata Ċ import Ġandroid . media . session . Media Controller Ċ import Ġandroid . media . session . Media Session Manager Ċ import Ġandroid . media . session . Playback State Ċ import Ġandroid . os . IB inder Ċ import Ġandroid . os . Locale List Ċ import Ġandroid . widget . Toast Ċ import Ġandroidx . core . content . getSystem Service Ċ import Ġkotlinx . coroutines .* Ċ import Ġkotlinx . coroutines . flow .* Ċ import Ġru . kont ur . k infra . logging . Logger Ċ import Ġtk . f rost bit . spaces hip connect . protocol . Connector Command Ċ import Ġtk . f rost bit . spaces hip connect . protocol . Connector Constants Ċ import Ġtk . f rost bit . spaces hip connect . protocol . Connector Settings Ċ import Ġjava . time . LocalDateTime Ċ import Ġjava . util .* Ċ import Ġkotlin . coroutines . Coroutine Context Ċ Ċ class ĠSp aces hip Connection Service Ġ: ĠService () Ġ{ ĊĊĠĠĠ Ġprivate Ġval Ġnotification Manager Ġby Ġlazy Ġ{ Ġget System Service < Notification Manager >() !! Ġ} ĊĠĠĠ Ġprivate Ġval Ġmedia Session Manager Ġby Ġlazy Ġ{ Ġget System Service < Media Session Manager >() !! Ġ} ĊĠĠĠ Ġprivate Ġval Ġnotification Channel Ġby Ġlazy Ġ{ Ġget Main Notification Channel () Ġ} ĊĠĠĠ Ġprivate Ġval Ġcoroutine Scope Ġ= ĠCor outine Scope ( ĊĠĠĠĠĠĠĠ ĠSuper visor Job () Ġ+ ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ ĠDispatch ers . Main Ġ+ ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ ĠCor outine ExceptionHandler ( this :: handle Exception ) ĊĠĠĠ Ġ) ĊĊĠĠĠ Ġprivate Ġval Ġdetach Receiver Ġ= ĠDet ach Receiver () ĊĠĠĠ Ġprivate Ġval Ġmedia Session Listener Ġ= ĠMedia Session Listener () ĊĊĠĠĠ Ġprivate Ġlateinit Ġvar Ġdevice : ĠU sb Device ĊĠĠĠ Ġprivate Ġvar Ġconnection : ĠSp aces hip Connection ? Ġ= Ġnull ĊĠĠĠ Ġprivate Ġvar Ġclosed Ġ= Ġfalse ĊĊĠĠĠ Ġoverride Ġfun ĠonStart Command ( intent : ĠIntent ?, Ġflags : ĠInt , Ġstart Id : ĠInt ): ĠInt Ġ{ ĊĠĠĠĠĠĠĠ Ġdevice Ġ= Ġintent ?. get Parcelable Extra ( Usb Manager . EXTRA _ DEVICE ) Ġ?: Ġrun Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġstop Self () ĊĠĠĠĠĠĠĠĠĠĠĠ Ġreturn ĠSTART _ NOT _ STICK Y ĊĠĠĠĠĠĠĠ Ġ} ĊĊĠĠĠĠĠĠĠ Ġstart Foreground ( ĊĠĠĠĠĠĠĠĠĠĠĠ ĠNOT IFICATION _ ID , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġbuild Notification (" Connecting "), ĊĠĠĠĠĠĠĠĠĠĠĠ ĠService Info . FO REG ROUND _ SERVICE _ TYPE _ CONNECTED _ DEVICE ĊĠĠĠĠĠĠĠ Ġ) ĊĊĠĠĠĠĠĠĠ Ġcoroutine Scope . launch Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġopen Connection ( device ) ĊĊĠĠĠĠĠĠĠĠĠĠĠ Ġconnection ?. let Ġ{ Ġconnection Ġ-> ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġlaunch Ġ{ Ġconnection . loop () Ġ} ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġconnection . validate () ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġafter Connect ( connection ) ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġlaunch Ġ{ Ġaudio Title Update Loop () Ġ} ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ} ĊĊĠĠĠĠĠĠĠĠĠĠĠ Ġnotification Manager . notify ( NOTIFICATION _ ID , Ġbuild Notification (" Connected ")) ĊĠĠĠĠĠĠĠĠĠĠĠ ĠToast . makeText ( this @ Spaces hip Connection Service , Ġ" Spaces hip Ġconnected ", ĠToast . LENGTH _ SHORT ). show () ĊĠĠĠĠĠĠĠ Ġ} ĊĊĠĠĠĠĠĠĠ Ġregister Receiver ( detach Receiver , ĠIntent Filter ( Usb Manager . ACTION _ USB _ DEVICE _ DET ACHED )) ĊĊĠĠĠĠĠĠĠ Ġval Ġnotification Listener Name Ġ= ĠComponent Name . create Relative ( this , Ġ". Notification Listener Service ") ĊĠĠĠĠĠĠĠ Ġmedia Session Manager . add On Active Sessions Changed Listener ( media Session Listener , Ġnotification Listener Name ) ĊĠĠĠĠĠĠĠ Ġmedia Session Listener . setActive Sessions ( media Session Manager . getActive Sessions ( notification Listener Name )) ĊĊĠĠĠĠĠĠĠ Ġreturn ĠSTART _ RE DE LI VER _ INT ENT ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġprivate Ġsuspend Ġfun Ġopen Connection ( device : ĠU sb Device ) Ġ{ ĊĠĠĠĠĠĠĠ Ġval Ġconnection Ġ= ĠSp aces hip Connection . open ( device , Ġthis ) ĊĠĠĠĠĠĠĠ Ġif Ġ( closed ) Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġconnection . close () ĊĠĠĠĠĠĠĠ Ġ} Ġelse Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġthis . connection Ġ= Ġconnection ĊĠĠĠĠĠĠĠ Ġ} ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġprivate Ġsuspend Ġfun Ġafter Connect ( connection : ĠSp aces hip Connection ) Ġ{ ĊĠĠĠĠĠĠĠ Ġconnection . exec Command ( Connector Command . Set DateTime ( LocalDateTime . now ())) ĊĊĠĠĠĠĠĠĠ Ġval Ġsettings Ġ= ĠConnector Settings ( ĊĠĠĠĠĠĠĠĠĠĠĠ Ġdate Locale Index Ġ= ĠLocale List . getDefault () ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ. to Language Tags (). split (",") ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ. indexOf First Ġ{ Ġpreferred Ġ-> ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ ĠConnector Settings . DATE _ LOCAL ES . any Ġ{ Ġpreferred . startsWith ( it ) Ġ} ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ} ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ. let Ġ{ Ġif Ġ( it Ġ== Ġ- 1 ) Ġ 1 Ġelse Ġit Ġ}, ĊĠĠĠĠĠĠĠĠĠĠĠ ĠdateFormat Index Ġ= Ġ 1 , ĊĠĠĠĠĠĠĠĠĠĠĠ Ġdate Caps Ġ= Ġtrue , ĊĠĠĠĠĠĠĠ Ġ) ĊĠĠĠĠĠĠĠ Ġlogger . info Ġ{ Ġ" Apply ing Ġsettings : Ġ$ settings " Ġ} ĊĠĠĠĠĠĠĠ Ġconnection . exec Command ( Connector Command . Set Settings ( settings )) ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġprivate Ġsuspend Ġfun Ġaudio Title Update Loop () Ġ{ ĊĠĠĠĠĠĠĠ Ġmedia Session Listener . current Title . collect Ġ{ Ġtitle Ġ-> ĊĠĠĠĠĠĠĠĠĠĠĠ Ġlogger . info Ġ{ Ġ" Updating Ġaudio Ġtitle : Ġ$ title " Ġ} ĊĠĠĠĠĠĠĠĠĠĠĠ Ġconnection ?. exec Command ( Connector Command . Set Audio Title ( title . toUpperCase ( Locale . ROOT ))) ĊĠĠĠĠĠĠĠ Ġ} ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġprivate Ġfun Ġhandle Exception ( context : ĠCor outine Context , Ġthrowable : ĠThrowable ) Ġ{ ĊĠĠĠĠĠĠĠ Ġlogger . error ( throwable ) Ġ{ Ġ" Connection Ġfailure " Ġ+ Ġcontext [ Coroutine Name ]?. let Ġ{ Ġ" Ġin Ġ$ it " Ġ}. or Empty () Ġ} ĊĠĠĠĠĠĠĠ ĠToast . makeText ( this @ Spaces hip Connection Service , Ġ" Spaces hip Ġdisconnected ", ĠToast . LENGTH _ LONG ). show () ĊĠĠĠĠĠĠĠ Ġclose (" failure ") ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġprivate Ġfun Ġbuild Notification ( title : ĠString , Ġtext : ĠString ? Ġ= Ġnull ): ĠNotification Ġ{ ĊĠĠĠĠĠĠĠ Ġreturn ĠNotification . Builder ( this , Ġnotification Channel . id ) ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ. set Small Icon ( R . drawable . ic _ auto ) ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ. setContent Title ( title ) ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ. setContent Text ( text ) ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ. setVisibility ( Notification . VIS IBILITY _ PUBLIC ) ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ. build () ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġprivate Ġfun Ġget Main Notification Channel (): ĠNotification Channel Ġ{ ĊĠĠĠĠĠĠĠ Ġreturn Ġnotification Manager . get Notification Channel ( NOTIFICATION _ CHANNEL _ ID ) Ġ?: Ġrun Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ ĠNotification Channel ( NOTIFICATION _ CHANNEL _ ID , Ġ" Connection ", ĠNotification Manager . IMPORT ANCE _ LOW ). also Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġnotification Manager . create Notification Channel ( it ) ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ} ĊĠĠĠĠĠĠĠ Ġ} ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġprivate Ġfun Ġclose ( message : ĠString ) Ġ{ ĊĠĠĠĠĠĠĠ Ġif Ġ( closed ) Ġreturn ĊĠĠĠĠĠĠĠ Ġclosed Ġ= Ġtrue ĊĊĠĠĠĠĠĠĠ Ġlogger . info Ġ{ Ġ" Connection Ġis Ġclosed Ġ($ message )" Ġ} ĊĠĠĠĠĠĠĠ Ġunregister Receiver ( detach Receiver ) ĊĠĠĠĠĠĠĠ Ġmedia Session Manager . remove On Active Sessions Changed Listener ( media Session Listener ) ĊĠĠĠĠĠĠĠ Ġcoroutine Scope . cancel ( message ) ĊĠĠĠĠĠĠĠ ĠGlobal Scope . launch Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġconnection ?. close () ĊĠĠĠĠĠĠĠ Ġ} ĊĠĠĠĠĠĠĠ Ġnotification Manager . notify ( NOTIFICATION _ ID , Ġbuild Notification (" Disconnected ")) ĊĠĠĠĠĠĠĠ Ġstop Self () ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġoverride Ġfun Ġon Destroy () Ġ{ ĊĠĠĠĠĠĠĠ Ġclose (" service Ġdestroyed ") ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġoverride Ġfun Ġon Bind ( intent : ĠIntent ): ĠI Binder ? Ġ= Ġnull ĊĊĠĠĠ Ġprivate Ġinner Ġclass ĠDet ach Receiver Ġ: ĠBroadcast Receiver () Ġ{ ĊĠĠĠĠĠĠĠ Ġoverride Ġfun Ġon Receive ( context : ĠContext , Ġintent : ĠIntent ) Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġval Ġdet ached Device Ġ= Ġintent . get Parcelable Extra < Usb Device >( Usb Manager . EXTRA _ DEVICE ) Ġ?: Ġreturn ĊĠĠĠĠĠĠĠĠĠĠĠ Ġif Ġ( det ached Device . deviceId Ġ== Ġdevice . deviceId ) Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġlogger . info Ġ{ Ġ" Det aching Ġfrom Ġdevice Ġ${ device . deviceId }" Ġ} ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġclose (" device Ġdet ached ") ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ} ĊĠĠĠĠĠĠĠ Ġ} ĊĠĠĠ Ġ} ĊĊĠĠĠ Ġprivate Ġclass ĠMedia Session Listener Ġ: ĠMedia Session Manager . On Active Sessions Changed Listener Ġ{ ĊĊĠĠĠĠĠĠĠ Ġprivate Ġval Ġcontroller Callback Ġ= ĠController Callback () ĊĠĠĠĠĠĠĠ Ġprivate Ġval Ġcontrollers Ġ= ĠMutable State Flow ( emptyList < Media Controller >()) ĊĠĠĠĠĠĠĠ Ġprivate Ġval Ġupdates Ġ= ĠMutable State Flow ( Update Cookie ()) ĊĠĠĠĠĠĠĠ Ġval Ġcurrent Title : ĠFlow < String > Ġ= Ġcontrollers . combine ( updates ) Ġ{ Ġcontrollers , Ġ_ Ġ-> Ġcontrollers Ġ} ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ. map Ġ{ Ġfind Playing Track ( it ). or Empty () Ġ} ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ. distinct Until Changed () ĊĊĠĠĠĠĠĠĠ Ġfun Ġset Active Sessions ( controllers : ĠList < Media Controller >) Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġfor Ġ( controller Ġin Ġcontrollers ) Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġcontroller . register Callback ( controller Callback ) ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ} ĊĠĠĠĠĠĠĠĠĠĠĠ Ġthis . controllers . value Ġ= Ġcontrollers ĊĠĠĠĠĠĠĠ Ġ} ĊĊĠĠĠĠĠĠĠ Ġoverride Ġfun Ġon Active Sessions Changed ( controllers : ĠList < Media Controller > ?) Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġlogger . debug Ġ{ Ġ" Media Ġsessions Ġupdated : Ġ$ controllers " Ġ} ĊĠĠĠĠĠĠĠĠĠĠĠ Ġset Active Sessions ( controllers . or Empty ()) ĊĠĠĠĠĠĠĠ Ġ} ĊĊĠĠĠĠĠĠĠ Ġprivate Ġfun Ġfind Playing Track ( controllers : ĠList < Media Controller >): ĠString ? Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġreturn Ġcontrollers ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ. filter Ġ{ Ġit . playback State ?. state Ġ== ĠPlay back State . STATE _ PLAY ING Ġ} ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ. filter Ġ{ Ġit . playback Info ?. audio Attributes ?. usage Ġ== ĠAudio Attributes . USAGE _ MEDIA Ġ} ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ. first OrNull () ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ? . metadata ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ? . let Ġ{ Ġget Track Title ( it ) Ġ} ĊĠĠĠĠĠĠĠ Ġ} ĊĊĠĠĠĠĠĠĠ Ġprivate Ġfun Ġget Track Title ( metadata : ĠMedia Metadata ): ĠString ? Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġval Ġtitle Ġ= Ġ( metadata . getString ( Media Metadata . METADATA _ KEY _ TITLE ) Ġ?: Ġreturn Ġnull ) ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ. substring Before (' (' ) ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ. substring Before ('[ ') ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ. substring Before (" Ġfeat .") ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ. substring Before (" Ġft .") ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ. trim () ĊĠĠĠĠĠĠĠĠĠĠĠ Ġval Ġartist Ġ= Ġmetadata . getString ( Media Metadata . METADATA _ KEY _ AR TI ST ) ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ? . take Unless Ġ{ Ġit Ġ== Ġ" Unknown Ġartist " Ġ} ĊĠĠĠĠĠĠĠĠĠĠĠ Ġval Ġsummary Ġ= Ġif Ġ( artist Ġ!= Ġnull ) Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ"$ title Ġ- Ġ$ artist " ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ} Ġelse Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġtitle ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ} ĊĠĠĠĠĠĠĠĠĠĠĠ Ġreturn Ġsummary ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ. replace (" Ġ- Ġ", Ġ"- ") ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġ. take ( Connector Constants . TEXT _ SECTION _ SIZE ) ĊĠĠĠĠĠĠĠ Ġ} ĊĊĠĠĠĠĠĠĠ Ġprivate Ġfun Ġupdate () Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġupdates . value Ġ= ĠUpdate Cookie () ĊĠĠĠĠĠĠĠ Ġ} ĊĊĠĠĠĠĠĠĠ Ġprivate Ġclass ĠUpdate Cookie ĊĊĠĠĠĠĠĠĠ Ġprivate Ġinner Ġclass ĠController Callback Ġ: ĠMedia Controller . Callback () Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠ Ġoverride Ġfun Ġon Playback StateChanged ( state : ĠPlay back State ?) Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġlogger . info Ġ{ Ġ" Playback Ġstate Ġchanged " Ġ} ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġupdate () ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ} ĊĊĠĠĠĠĠĠĠĠĠĠĠ Ġoverride Ġfun Ġon Metadata Changed ( metadata : ĠMedia Metadata ?) Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġlogger . info Ġ{ Ġ" Metadata Ġchanged " Ġ} ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġupdate () ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ} ĊĊĠĠĠĠĠĠĠĠĠĠĠ Ġoverride Ġfun Ġon Audio Info Changed ( info : ĠMedia Controller . Playback Info ?) Ġ{ ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġlogger . info Ġ{ Ġ" Audio Ġinfo Ġchanged " Ġ} ĊĠĠĠĠĠĠĠĠĠĠĠĠĠĠĠ Ġupdate () ĊĠĠĠĠĠĠĠĠĠĠĠ Ġ} ĊĠĠĠĠĠĠĠ Ġ} ĊĊĠĠĠ Ġ} ĊĊĠĠĠ Ġcompanion Ġobject Ġ{ ĊĊĠĠĠĠĠĠĠ Ġprivate Ġval Ġlogger Ġ= ĠLogger . current Class () ĊĠĠĠĠĠĠĠ Ġprivate Ġconst Ġval ĠNOT IFICATION _ ID Ġ= ĠNotification Ids . CONNECTION ĊĠĠĠĠĠĠĠ Ġprivate Ġconst Ġval ĠNOT IFICATION _ CHANNEL _ ID Ġ= Ġ" connection " ĊĊĠĠĠ Ġ} Ċ Ċ } Ċ