@@ -133,10 +133,10 @@ class TvMainActivity : AppCompatActivity() {
133
133
binding.filesRowConfigurationHandler = object : ObservableKeyedRecyclerViewAdapter .RowConfigurationHandler <TvFileListItemBinding , KeyedFile > {
134
134
override fun onConfigureRow (binding : TvFileListItemBinding , item : KeyedFile , position : Int ) {
135
135
binding.root.setOnClickListener {
136
- if (item.isDirectory)
137
- navigateTo(item)
136
+ if (item.file. isDirectory)
137
+ navigateTo(item.file )
138
138
else {
139
- val uri = Uri .fromFile(item.canonicalFile )
139
+ val uri = Uri .fromFile(item.file )
140
140
files.clear()
141
141
filesRoot.set(" " )
142
142
lifecycleScope.launch {
@@ -153,13 +153,9 @@ class TvMainActivity : AppCompatActivity() {
153
153
}
154
154
155
155
binding.importButton.setOnClickListener {
156
- try {
157
- if (Build .VERSION .SDK_INT < Build .VERSION_CODES .Q )
158
- throw Exception ()
159
- tunnelFileImportResultLauncher.launch(" */*" )
160
- } catch (_: Throwable ) {
156
+ if (Build .VERSION .SDK_INT < Build .VERSION_CODES .Q ) {
161
157
if (filesRoot.get()?.isEmpty() != false ) {
162
- navigateTo(myComputerFile )
158
+ navigateTo(File ( " / " ) )
163
159
runOnUiThread {
164
160
binding.filesList.requestFocus()
165
161
}
@@ -170,6 +166,12 @@ class TvMainActivity : AppCompatActivity() {
170
166
binding.tunnelList.requestFocus()
171
167
}
172
168
}
169
+ } else {
170
+ try {
171
+ tunnelFileImportResultLauncher.launch(" */*" )
172
+ } catch (_: Throwable ) {
173
+ Toast .makeText(this @TvMainActivity, getString(R .string.tv_no_file_picker), Toast .LENGTH_LONG ).show()
174
+ }
173
175
}
174
176
}
175
177
@@ -198,50 +200,63 @@ class TvMainActivity : AppCompatActivity() {
198
200
pendingNavigation = null
199
201
}
200
202
203
+ private var cachedRoots: Collection <KeyedFile >? = null
204
+
201
205
private suspend fun makeStorageRoots (): Collection <KeyedFile > = withContext(Dispatchers .IO ) {
206
+ cachedRoots?.let { return @withContext it }
202
207
val list = HashSet <KeyedFile >()
203
208
if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .N ) {
204
209
val storageManager: StorageManager = getSystemService() ? : return @withContext list
205
210
list.addAll(storageManager.storageVolumes.mapNotNull { volume ->
206
211
if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .R ) {
207
- volume.directory?.let { KeyedFile (it.canonicalPath , volume.getDescription(this @TvMainActivity)) }
212
+ volume.directory?.let { KeyedFile (it, volume.getDescription(this @TvMainActivity)) }
208
213
} else {
209
- KeyedFile ((StorageVolume ::class .java.getMethod(" getPathFile" ).invoke(volume) as File ).canonicalPath , volume.getDescription(this @TvMainActivity))
214
+ KeyedFile ((StorageVolume ::class .java.getMethod(" getPathFile" ).invoke(volume) as File ), volume.getDescription(this @TvMainActivity))
210
215
}
211
216
})
212
217
} else {
213
218
@Suppress(" DEPRECATION" )
214
- list.add(KeyedFile (Environment .getExternalStorageDirectory().canonicalPath ))
219
+ list.add(KeyedFile (Environment .getExternalStorageDirectory()))
215
220
try {
216
221
File (" /storage" ).listFiles()?.forEach {
217
222
if (! it.isDirectory) return @forEach
218
223
try {
219
224
if (Environment .isExternalStorageRemovable(it)) {
220
- list.add(KeyedFile (it.canonicalPath ))
225
+ list.add(KeyedFile (it))
221
226
}
222
227
} catch (_: Throwable ) {
223
228
}
224
229
}
225
230
} catch (_: Throwable ) {
226
231
}
227
232
}
233
+ cachedRoots = list
228
234
list
229
235
}
230
236
231
- private val myComputerFile = File (" " )
237
+ private fun isBelowCachedRoots (maybeChild : File ): Boolean {
238
+ val cachedRoots = cachedRoots ? : return true
239
+ for (root in cachedRoots) {
240
+ if (maybeChild.canonicalPath.startsWith(root.file.canonicalPath))
241
+ return false
242
+ }
243
+ return true
244
+ }
232
245
233
246
private fun navigateTo (directory : File ) {
247
+ require(Build .VERSION .SDK_INT < Build .VERSION_CODES .Q )
248
+
234
249
if (ContextCompat .checkSelfPermission(this , Manifest .permission.READ_EXTERNAL_STORAGE ) != PackageManager .PERMISSION_GRANTED ) {
235
250
pendingNavigation = directory
236
251
permissionRequestPermissionLauncher.launch(Manifest .permission.READ_EXTERNAL_STORAGE )
237
252
return
238
253
}
239
254
240
255
lifecycleScope.launch {
241
- if (directory == myComputerFile ) {
256
+ if (isBelowCachedRoots( directory) ) {
242
257
val roots = makeStorageRoots()
243
258
if (roots.count() == 1 ) {
244
- navigateTo(roots.first())
259
+ navigateTo(roots.first().file )
245
260
return @launch
246
261
}
247
262
files.clear()
@@ -253,18 +268,18 @@ class TvMainActivity : AppCompatActivity() {
253
268
val newFiles = withContext(Dispatchers .IO ) {
254
269
val newFiles = ArrayList <KeyedFile >()
255
270
try {
256
- val parent = KeyedFile ( directory.canonicalPath + " /.. " )
257
- if (directory.canonicalPath != " / " && parent.list() != null )
258
- newFiles.add(parent)
271
+ directory.parentFile?. let {
272
+ newFiles.add( KeyedFile (it, " ../ " ) )
273
+ }
259
274
val listing = directory.listFiles() ? : return @withContext null
260
275
listing.forEach {
261
276
if (it.extension == " conf" || it.extension == " zip" || it.isDirectory)
262
- newFiles.add(KeyedFile (it.canonicalPath ))
277
+ newFiles.add(KeyedFile (it))
263
278
}
264
279
newFiles.sortWith { a, b ->
265
- if (a.isDirectory && ! b.isDirectory) - 1
266
- else if (! a.isDirectory && b.isDirectory) 1
267
- else a.compareTo(b)
280
+ if (a.file. isDirectory && ! b.file .isDirectory) - 1
281
+ else if (! a.file. isDirectory && b.file .isDirectory) 1
282
+ else a.file. compareTo(b.file )
268
283
}
269
284
} catch (e: Throwable ) {
270
285
Log .e(TAG , Log .getStackTraceString(e))
@@ -319,9 +334,9 @@ class TvMainActivity : AppCompatActivity() {
319
334
}
320
335
}
321
336
322
- class KeyedFile (pathname : String , private val forcedKey : String? = null ) : File(pathname), Keyed<String> {
337
+ class KeyedFile (val file : File , private val forcedKey : String? = null ) : Keyed<String> {
323
338
override val key: String
324
- get() = forcedKey ? : if (isDirectory) " $name /" else name
339
+ get() = forcedKey ? : if (file. isDirectory) " ${file. name} /" else file. name
325
340
}
326
341
327
342
companion object {
0 commit comments