package Ġsan cho . gn arly music player . adapters Ċ Ċ import Ġandroid . content . Context Ċ import Ġandroid . view . LayoutInflater Ċ import Ġandroid . view . View Ċ import Ġandroid . view . ViewGroup Ċ import Ġandroid . widget . TextView Ċ import Ġandroidx . recyclerview . widget . RecyclerView Ċ import Ġkotlinx . coroutines . Dispatch ers Ċ import Ġkotlinx . coroutines . Global Scope Ċ import Ġkotlinx . coroutines . Job Ċ import Ġkotlinx . coroutines . launch Ċ import Ġsan cho . gn arly music player .* Ċ import Ġsan cho . gn arly music player . comp ar ators . Explorer View Files And Dirs Comparator Ċ import Ġsan cho . gn arly music player . comp ar ators . Explorer View Files Comparator Ċ import Ġsan cho . gn arly music player . comp ar ators . Files Comparator Ċ import Ġsan cho . gn arly music player . models .* Ċ import Ġjava . io . File Ċ Ċ private Ġconst Ġval ĠEX PL OR ER _ NORMAL _ ITEM Ġ= Ġ 0 Ċ private Ġconst Ġval ĠEX PL OR ER _ GROUP _ ITEM Ġ= Ġ 1 Ċ Ċ class ĠExplorer Adapter ( Ċ ĉ private Ġval Ġ_ context : ĠContext , Ċ ĉ private Ġval Ġ_ dir List : ĠMutable List < Explorer View Item >, Ċ ĉ private Ġval Ġ_ queue Adapter : ĠQueue Adapter , Ċ ĉ private Ġval Ġ_ restore List Scroll Pos : Ġ( String ?) Ġ-> ĠUnit , Ċ ĉ private Ġval Ġ_ set Toolbar Text : Ġ( String ) Ġ-> ĠUnit , Ċ ĉ private Ġval Ġ_ set Dir List Loading : Ġ( Boolean ) Ġ-> ĠUnit , Ċ ĉ private Ġval Ġ_ setup Explorer Ctx Menu : Ġ( Bottom Sheet Dialog Ctx Menu , ĠString , ĠString ) Ġ-> ĠUnit ) Ġ: ĠRecyclerView . Adapter < Explorer Adapter . File Holder >() Ċ { Ċ ĉ private Ġval Ġ_ mounted Devices : ĠMutable List < Explorer View Item > Ġ= Ġmutable ListOf () Ċ ĉ var Ġcurrent Explorer Path : ĠFile ? Ġ= Ġnull Ċ ĉ var Ġsearch Results Open Ġ= Ġfalse ĊĊ ĉ private Ġvar Ġ_ file Loader Job : ĠJob ? Ġ= Ġnull ĊĊ ĉ init Ċ ĉ { Ċĉ ĉ val Ġexternal Storage Files Ġ= Ġ_ context . get External Files Dirs ( null ) Ċĉ ĉ for ( f Ġin Ġexternal Storage Files ) Ċĉ ĉ { Ċĉĉ ĉ val Ġdevice Ġ= Ġf ?. parent File ?. parent File ?. parent File ?. parent File Ġ// Ġthis Ġis Ġso Ġbad Ċĉĉ ĉ _ mounted Devices . add ( Explorer Item ( device ?. path Ġ?: Ġ"", Ġdevice ?. name Ġ?: Ġ"", Ġdevice ?. isDirectory Ġ?: Ġfalse )) Ċĉ ĉ } Ċ ĉ } ĊĊ ĉ override Ġfun Ġon Bind ViewHolder ( holder : ĠFile Holder , Ġposition : ĠInt ) Ċ ĉ { Ċĉ ĉ val Ġfile Ġ= Ġ_ dir List [ position ] ĊĊĉ ĉ if Ġ( file . is Header ) Ċĉ ĉ { Ċĉĉ ĉ holder . item View . findViewById < TextView >( R . id . explorer _ header _ text ). text Ġ= Ġfile . displayName Ċĉ ĉ } Ċĉ ĉ else Ċĉ ĉ { Ċĉĉ ĉ holder . item View . findViewById < TextView >( R . id . explorer _ text ). text Ġ= Ġfile . displayName ĊĊĉĉ ĉ val Ġicon Ġ= Ġwhen Ċĉĉ ĉ { Ċĉĉĉ ĉ file . isDirectory Ġ-> ĠR . drawable . folder Ċĉĉĉ ĉ file . isError Ġ-> ĠR . drawable . warning Ċĉĉĉ ĉ File Support Checker . isFile Supported And Playlist ( file . path ) Ġ-> ĠR . drawable . playlist Ċĉĉĉ ĉ else Ġ-> ĠR . drawable . note Ċĉĉ ĉ } ĊĊĉĉ ĉ holder . item View . findViewById < TextView >( R . id . explorer _ text ). set Compound Draw ables With Intr insic Bounds ( icon , Ġ 0 , Ġ 0 , Ġ 0 ) ĊĊĉĉ ĉ holder . item View . setOnClickListener Ġ{ Ġsingle Click ( file ) Ġ} Ċĉĉ ĉ holder . item View . setOn Long ClickListener Ġ{ Ċĉĉĉ ĉ // Ġdon 't Ġworry Ġabout Ġheader Ġitems , Ġthey Ġare Ġun click able Ġ( fam ous Ġlast Ġwords ) Ċĉĉĉ ĉ if Ġ( file . isDirectory Ġ|| ĠFile Support Checker . isFile Supported And Playlist ( file . path )) Ċĉĉĉ ĉ { Ċĉĉĉĉ ĉ long Click ( file ) Ġ// Ġdirectory Ġor Ġplaylist Ċĉĉĉ ĉ } Ċĉĉĉ ĉ else Ċĉĉĉ ĉ { Ċĉĉĉĉ ĉ val Ġdialog Ġ= ĠBottom Sheet Dialog Ctx Menu (_ context , ĠR . layout . bottom _ sheet _ explorer ) Ċĉĉĉĉ ĉ _ setup Explorer Ctx Menu ( dialog , Ġfile . displayName , Ġfile . path ) Ċĉĉĉĉ ĉ dialog . show () Ċĉĉĉ ĉ } ĊĊĉĉĉ ĉ true Ġ// Ġconsumed Ġlong Ġpress Ċĉĉ ĉ } Ċĉ ĉ } Ċ ĉ } ĊĊ ĉ override Ġfun ĠgetItem Count () Ġ= Ġ_ dir List . size ĊĊ ĉ override Ġfun ĠonCreate ViewHolder ( parent : ĠViewGroup , Ġview Type : ĠInt ): ĠFile Holder Ċ ĉ { Ċĉ ĉ return Ġwhen Ġ( view Type ) Ċĉ ĉ { Ċĉĉ ĉ EX PL OR ER _ NORMAL _ ITEM Ġ-> ĠFile Holder ( LayoutInflater . from (_ context ). inflate ( R . layout . explorer _ item , Ġparent , Ġfalse )) Ċĉĉ ĉ else Ġ-> ĠFile Holder ( LayoutInflater . from (_ context ). inflate ( R . layout . explorer _ group _ item , Ġparent , Ġfalse )) Ċĉ ĉ } Ċ ĉ } ĊĊ ĉ class ĠFile Holder ( view : ĠView ) Ġ: ĠRecyclerView . ViewHolder ( view ) ĊĊ ĉ override Ġfun ĠgetItem ViewType ( pos : ĠInt ): ĠInt Ċ ĉ { Ċĉ ĉ if Ġ(_ dir List [ pos ]. is Header ) Ċĉĉ ĉ return ĠEX PL OR ER _ GROUP _ ITEM ĊĊĉ ĉ return ĠEX PL OR ER _ NORMAL _ ITEM Ċ ĉ } ĊĊ ĉ private Ġfun ĠaddTo Queue ( queue Item : ĠQueue Item ) Ċ ĉ { Ċĉ ĉ Playback Queue . add ( queue Item ) Ċĉ ĉ _ queue Adapter . notify Item Inserted ( Playback Queue . last Idx ) Ċ ĉ } ĊĊ ĉ private Ġfun ĠaddTo Queue ( track List : ĠList < Queue Item >) Ċ ĉ { Ċĉ ĉ Playback Queue . add ( track List ) Ċĉ ĉ _ queue Adapter . notify Item Range Inserted ( Playback Queue . size Ġ- Ġtrack List . size , Ġtrack List . size ) Ċ ĉ } ĊĊ ĉ private Ġfun Ġsingle Click ( item : ĠExplorer View Item ) Ċ ĉ { Ċĉ ĉ val Ġfile Ġ= ĠFile ( item . path ) ĊĊĉ ĉ if Ġ(! file . exists ()) Ċĉ ĉ { Ċĉĉ ĉ To aster . show (_ context , Ġ_ context . getString ( R . string . file _ does nt _ exist )) Ċĉĉ ĉ return Ċĉ ĉ } ĊĊĉ ĉ if Ġ( file . isDirectory Ġ|| ĠFile Support Checker . isFile Supported And Playlist ( file . path )) Ċĉ ĉ { Ċĉĉ ĉ // Ġnavigate Ġto Ġdirectory Ġor Ġopen Ġplaylist Ċĉĉ ĉ update Directory View ( file ) Ċĉĉ ĉ _ restore List Scroll Pos ( null ) Ċĉĉ ĉ search Results Open Ġ= Ġfalse Ġ// Ġin Ġcase Ġthe Ġdir Ġwas Ġfrom Ġsearch Ġresults Ċĉ ĉ } Ċĉ ĉ else Ċĉ ĉ { Ċĉĉ ĉ // Ġaudio Ġfile Ċĉĉ ĉ addTo Queue ( Queue Item ( file . absolute Path , Ġfile . name Without Extension )) Ċĉ ĉ } Ċ ĉ } ĊĊ ĉ private Ġfun Ġlong Click ( item : ĠExplorer View Item ) Ċ ĉ { Ċĉ ĉ val Ġfile Ġ= ĠFile ( item . path ) ĊĊĉ ĉ if Ġ(! file . exists ()) Ċĉ ĉ { Ċĉĉ ĉ To aster . show (_ context , Ġ_ context . getString ( R . string . dir _ does nt _ exist )) Ċĉĉ ĉ return Ċĉ ĉ } ĊĊĉ ĉ if Ġ( file . isDirectory ) Ċĉ ĉ { Ċĉĉ ĉ // Ġadd Ġall Ġtracks Ġin Ġdir Ġ( not Ġrecursive ) Ċĉĉ ĉ val Ġfiles Ġ= Ġlist Dir ( file , Ġtrue ) ĊĊĉĉ ĉ if Ġ( files Ġ!= Ġnull ) Ċĉĉ ĉ { Ċĉĉĉ ĉ files . sort With ( Files Comparator ()) Ċĉĉĉ ĉ addTo Queue ( files . map Ġ{ ĠQueue Item ( it . absolute Path , Ġit . name Without Extension ) Ġ}) ĊĊĉĉĉ ĉ To aster . show (_ context , Ġ_ context . getString ( R . string . n _ tracks _ added _ to _ queue , Ġfiles . size )) Ċĉĉ ĉ } Ċĉĉ ĉ else Ċĉĉĉ ĉ To aster . show (_ context , Ġ_ context . getString ( R . string . file _ list _ error )) Ċĉ ĉ } Ċĉ ĉ else Ġif Ġ( File Support Checker . isFile Supported And Playlist ( file . path )) Ċĉ ĉ { Ċĉĉ ĉ // Ġadd Ġall Ġtracks Ġin Ġplaylist Ġ( not Ġrecursive ) Ċĉĉ ĉ val Ġfiles Ġ= Ġlist Playlist ( file ) ĊĊĉĉ ĉ files . removeAll Ġ{ Ġ! it . exists () Ġ} Ġ// Ġwe 're Ġnot Ġinterested Ġin Ġpaths Ġto Ġnon - existent Ġfiles ĊĊĉĉ ĉ addTo Queue ( files . map Ġ{ ĠQueue Item ( it . absolute Path , Ġit . name Without Extension ) Ġ}) ĊĊĉĉ ĉ To aster . show (_ context , Ġ_ context . getString ( R . string . n _ tracks _ added _ to _ queue , Ġfiles . size )) Ċĉ ĉ } Ċ ĉ } ĊĊ ĉ fun Ġsearch Open ( query : ĠString , Ġprev Dir List : ĠMutable List < Explorer View Item >) Ċ ĉ { Ċĉ ĉ _ set Dir List Loading ( true ) ĊĊĉ ĉ if Ġ(! search Results Open ) Ċĉ ĉ { Ċĉĉ ĉ prev Dir List . clear () Ċĉĉ ĉ prev Dir List . addAll (_ dir List ) Ċĉ ĉ } ĊĊĉ ĉ _ file Loader Job ?. cancel () ĊĊĉ ĉ _ file Loader Job Ġ= ĠGlobal Scope . launch ( Dispatch ers . IO ) Ċĉ ĉ { Ċĉĉ ĉ val Ġsearch Result List Ġ= Ġmutable ListOf < Explorer View Item >() ĊĊĉĉ ĉ val Ġquery But Lower Ġ= Ġquery . lowercase () ĊĊĉĉ ĉ // Ġadd Ġresults Ġfrom Ġcurrent Ġdir Ċĉĉ ĉ search Result List . addAll ( prev Dir List Ċĉĉĉ ĉ . filter Ġ{ Ġit . displayName . lowercase (). contains ( query But Lower ) Ġ} Ċĉĉĉ ĉ . sorted With ( Explorer View Files Comparator ()) Ċĉĉ ĉ ) ĊĊĉĉ ĉ // Ġadd Ġresults Ġfrom Ġfirst Ġlevel Ġdirs Ġ( grouped Ġby Ġsub dir Ġname ) Ċĉĉ ĉ for Ġ( elem Ġin Ġprev Dir List . filter Ġ{ Ġit . isDirectory Ġ}. sorted With ( Explorer View Files Comparator ())) Ċĉĉ ĉ { Ċĉĉĉ ĉ val Ġdir Ġ= ĠFile ( elem . path ) ĊĊĉĉĉ ĉ val Ġresults Ġ= Ġdir Ċĉĉĉĉ ĉ . list Files Ġ{ Ġfile Ġ-> Ċĉĉĉĉĉ ĉ ( file . isDirectory Ġ|| ĠFile Support Checker . isFile Supported ( file . name )) Ċĉĉĉĉĉĉĉ ĉ && Ġfile . name . lowercase (). contains ( query But Lower ) Ċĉĉĉĉ ĉ } Ċĉĉĉĉ ĉ ?. map Ġ{ ĠExplorer Item ( it . path , Ġit . name , Ġit . isDirectory ) Ġ} Ċĉĉĉĉ ĉ ?. sorted With ( Explorer View Files Comparator ()) ĊĊĉĉĉ ĉ if Ġ( results ?. isNotEmpty () Ġ== Ġtrue ) Ċĉĉĉ ĉ { Ċĉĉĉĉ ĉ // Ġadd Ġsub dir Ġheader Ċĉĉĉĉ ĉ search Result List . add ( Explorer Header ( elem . displayName )) ĊĊĉĉĉĉ ĉ // Ġadd Ġresults Ġin Ġthis Ġdir Ċĉĉĉĉ ĉ search Result List . addAll ( results ) Ċĉĉĉ ĉ } Ċĉĉ ĉ } ĊĊĉĉ ĉ Global Scope . launch ( Dispatch ers . Main ) Ċĉĉ ĉ { Ċĉĉĉ ĉ _ dir List . clear () Ċĉĉĉ ĉ _ dir List . addAll ( search Result List ) Ċĉĉĉ ĉ notify DataSet Changed () Ċĉĉĉ ĉ search Results Open Ġ= Ġtrue Ċĉĉĉ ĉ _ set Dir List Loading ( false ) Ċĉĉ ĉ } Ċĉ ĉ } Ċ ĉ } ĊĊ ĉ fun Ġsearch Close () Ċ ĉ { Ċĉ ĉ update Directory View ( current Explorer Path ) Ċĉ ĉ search Results Open Ġ= Ġfalse Ċ ĉ } ĊĊ ĉ fun Ġupdate Directory View ( new Path : ĠFile ?, Ġold Path : ĠString ? Ġ= Ġnull ) Ċ ĉ { Ċĉ ĉ current Explorer Path Ġ= Ġnew Path Ċĉ ĉ _ set Dir List Loading ( true ) Ċĉ ĉ _ set Toolbar Text ( if Ġ( new Path Ġ== Ġnull ) Ġ_ context . getString ( R . string . storage _ devices ) Ġelse Ġnew Path . absolute Path ) ĊĊĉ ĉ _ file Loader Job ?. cancel () ĊĊĉ ĉ _ file Loader Job Ġ= ĠGlobal Scope . launch ( Dispatch ers . IO ) Ċĉ ĉ { Ċĉĉ ĉ val Ġfuture List : ĠList < Explorer View Item >? Ġ= Ċĉĉĉ ĉ if Ġ( new Path Ġ== Ġnull Ġ|| Ġ! new Path . exists ()) Ċĉĉĉĉ ĉ update Directory View Show Storage () Ċĉĉĉ ĉ else Ġif Ġ( new Path . isDirectory ) Ċĉĉĉĉ ĉ update Directory View Dir ( new Path ) Ċĉĉĉ ĉ else Ċĉĉĉĉ ĉ update Directory View Playlist ( new Path ) ĊĊĉĉ ĉ Global Scope . launch ( Dispatch ers . Main ) Ċĉĉ ĉ { Ċĉĉĉ ĉ if Ġ( future List Ġ!= Ġnull ) Ċĉĉĉ ĉ { Ċĉĉĉĉ ĉ _ dir List . clear () Ġ// Ġmust Ġmodify Ġre cycler view Ġlist Ġon Ġmain Ġthread Ċĉĉĉĉ ĉ _ dir List . addAll ( future List ) Ċĉĉĉĉ ĉ notify DataSet Changed () Ċĉĉĉĉ ĉ _ restore List Scroll Pos ( old Path ) Ċĉĉĉĉ ĉ _ set Dir List Loading ( false ) Ċĉĉĉ ĉ } Ċĉĉĉ ĉ else Ċĉĉĉĉ ĉ To aster . show (_ context , Ġ_ context . getString ( R . string . file _ list _ error )) Ċĉĉ ĉ } Ċĉ ĉ } Ċ ĉ } ĊĊ ĉ private Ġfun Ġupdate Directory View Show Storage (): ĠMutable List < Explorer View Item > Ċ ĉ { Ċĉ ĉ return Ġ_ mounted Devices Ċ ĉ } ĊĊ ĉ private Ġfun Ġupdate Directory View Dir ( new Path : ĠFile ): ĠList < Explorer View Item >? Ċ ĉ { Ċĉ ĉ val Ġlist Ġ= Ġlist Dir ( new Path , Ġfalse ) ĊĊĉ ĉ val Ġview List Ġ= Ġlist ?. map { file Ġ-> ĠExplorer Item ( file . path , Ġfile . name , Ġfile . isDirectory ) Ġ} ?. to Mutable List () Ċĉ ĉ view List ?. sort With ( Explorer View Files And Dirs Comparator ()) ĊĊĉ ĉ return Ġview List Ċ ĉ } ĊĊ ĉ private Ġfun Ġupdate Directory View Playlist ( new Path : ĠFile ): ĠList < Explorer View Item > Ċ ĉ { Ċĉ ĉ val Ġlist Ġ= Ġmutable ListOf < Explorer View Item >() ĊĊĉ ĉ val Ġfiles Ġ= Ġlist Playlist ( new Path ) ĊĊĉ ĉ files . forEach Ġ{ Ċĉĉ ĉ if Ġ( it . exists ()) Ċĉĉĉ ĉ list . add ( Explorer Item ( it . path , Ġit . name , Ġit . isDirectory )) Ċĉĉ ĉ else Ċĉĉĉ ĉ list . add ( Explorer Error Item ( it . path , Ġit . name , Ġit . isDirectory )) Ċĉ ĉ } ĊĊĉ ĉ return Ġlist . to Mutable List () Ċ ĉ } ĊĊ ĉ private Ġfun Ġlist Dir ( path : ĠFile , Ġonly Audio : ĠBoolean ): ĠMutable List < File >? Ċ ĉ { Ċĉ ĉ return Ġpath . list Files Ġ{ Ġfile Ġ-> Ċĉĉ ĉ ( only Audio Ġ&& Ġ! file . isDirectory Ġ&& ĠFile Support Checker . isFile Supported And Audio ( file . name )) Ġ|| Ċĉĉĉĉ ĉ (! only Audio Ġ&& Ġ( file . isDirectory Ġ|| ĠFile Support Checker . isFile Supported ( file . name ))) Ċĉ ĉ } ?. to Mutable List () Ċ ĉ } ĊĊ ĉ private Ġfun Ġlist Playlist ( path : ĠFile ): ĠMutable List < File > Ċ ĉ { Ċĉ ĉ val Ġlist Ġ= Ġmutable ListOf < File >() ĊĊĉ ĉ path . read Lines (). forEach Ġ{ Ċĉĉ ĉ var Ġtrack Ġ= ĠFile ( path . parent , Ġit ) ĊĊĉĉ ĉ if Ġ(! track . exists ()) Ċĉĉĉ ĉ track Ġ= ĠFile ( it ) Ġ// Ġmaybe Ġit 's Ġabsolute Ġpath ĊĊĉĉ ĉ if Ġ(! track . isDirectory Ġ&& ĠFile Support Checker . isFile Supported And Audio ( it )) Ċĉĉĉ ĉ list . add ( track . canonical File ) Ġ// Ġresolve Ġpath Ġ(.. Ġand Ġstuff ) Ċĉ ĉ } ĊĊĉ ĉ return Ġlist Ċ ĉ } Ċ } Ċ