Skip to content

Commit 7a8f708

Browse files
committed
tv: handle going up directories better
Signed-off-by: Jason A. Donenfeld <[email protected]>
1 parent e729c5d commit 7a8f708

File tree

2 files changed

+41
-25
lines changed

2 files changed

+41
-25
lines changed

ui/src/main/java/com/wireguard/android/activity/TvMainActivity.kt

Lines changed: 40 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -133,10 +133,10 @@ class TvMainActivity : AppCompatActivity() {
133133
binding.filesRowConfigurationHandler = object : ObservableKeyedRecyclerViewAdapter.RowConfigurationHandler<TvFileListItemBinding, KeyedFile> {
134134
override fun onConfigureRow(binding: TvFileListItemBinding, item: KeyedFile, position: Int) {
135135
binding.root.setOnClickListener {
136-
if (item.isDirectory)
137-
navigateTo(item)
136+
if (item.file.isDirectory)
137+
navigateTo(item.file)
138138
else {
139-
val uri = Uri.fromFile(item.canonicalFile)
139+
val uri = Uri.fromFile(item.file)
140140
files.clear()
141141
filesRoot.set("")
142142
lifecycleScope.launch {
@@ -153,13 +153,9 @@ class TvMainActivity : AppCompatActivity() {
153153
}
154154

155155
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) {
161157
if (filesRoot.get()?.isEmpty() != false) {
162-
navigateTo(myComputerFile)
158+
navigateTo(File("/"))
163159
runOnUiThread {
164160
binding.filesList.requestFocus()
165161
}
@@ -170,6 +166,12 @@ class TvMainActivity : AppCompatActivity() {
170166
binding.tunnelList.requestFocus()
171167
}
172168
}
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+
}
173175
}
174176
}
175177

@@ -198,50 +200,63 @@ class TvMainActivity : AppCompatActivity() {
198200
pendingNavigation = null
199201
}
200202

203+
private var cachedRoots: Collection<KeyedFile>? = null
204+
201205
private suspend fun makeStorageRoots(): Collection<KeyedFile> = withContext(Dispatchers.IO) {
206+
cachedRoots?.let { return@withContext it }
202207
val list = HashSet<KeyedFile>()
203208
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
204209
val storageManager: StorageManager = getSystemService() ?: return@withContext list
205210
list.addAll(storageManager.storageVolumes.mapNotNull { volume ->
206211
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)) }
208213
} 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))
210215
}
211216
})
212217
} else {
213218
@Suppress("DEPRECATION")
214-
list.add(KeyedFile(Environment.getExternalStorageDirectory().canonicalPath))
219+
list.add(KeyedFile(Environment.getExternalStorageDirectory()))
215220
try {
216221
File("/storage").listFiles()?.forEach {
217222
if (!it.isDirectory) return@forEach
218223
try {
219224
if (Environment.isExternalStorageRemovable(it)) {
220-
list.add(KeyedFile(it.canonicalPath))
225+
list.add(KeyedFile(it))
221226
}
222227
} catch (_: Throwable) {
223228
}
224229
}
225230
} catch (_: Throwable) {
226231
}
227232
}
233+
cachedRoots = list
228234
list
229235
}
230236

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+
}
232245

233246
private fun navigateTo(directory: File) {
247+
require(Build.VERSION.SDK_INT < Build.VERSION_CODES.Q)
248+
234249
if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
235250
pendingNavigation = directory
236251
permissionRequestPermissionLauncher.launch(Manifest.permission.READ_EXTERNAL_STORAGE)
237252
return
238253
}
239254

240255
lifecycleScope.launch {
241-
if (directory == myComputerFile) {
256+
if (isBelowCachedRoots(directory)) {
242257
val roots = makeStorageRoots()
243258
if (roots.count() == 1) {
244-
navigateTo(roots.first())
259+
navigateTo(roots.first().file)
245260
return@launch
246261
}
247262
files.clear()
@@ -253,18 +268,18 @@ class TvMainActivity : AppCompatActivity() {
253268
val newFiles = withContext(Dispatchers.IO) {
254269
val newFiles = ArrayList<KeyedFile>()
255270
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+
}
259274
val listing = directory.listFiles() ?: return@withContext null
260275
listing.forEach {
261276
if (it.extension == "conf" || it.extension == "zip" || it.isDirectory)
262-
newFiles.add(KeyedFile(it.canonicalPath))
277+
newFiles.add(KeyedFile(it))
263278
}
264279
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)
268283
}
269284
} catch (e: Throwable) {
270285
Log.e(TAG, Log.getStackTraceString(e))
@@ -319,9 +334,9 @@ class TvMainActivity : AppCompatActivity() {
319334
}
320335
}
321336

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> {
323338
override val key: String
324-
get() = forcedKey ?: if (isDirectory) "$name/" else name
339+
get() = forcedKey ?: if (file.isDirectory) "${file.name}/" else file.name
325340
}
326341

327342
companion object {

ui/src/main/res/values/strings.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@
103103
<string name="delete">Delete</string>
104104
<string name="tv_delete">Select tunnel to delete</string>
105105
<string name="tv_select_a_storage_drive">Select a storage drive</string>
106+
<string name="tv_no_file_picker">Please install a file management utility to browse files</string>
106107
<string name="tv_add_tunnel_get_started">Add a tunnel to get started</string>
107108
<string name="disable_config_export_title">Disable config exporting</string>
108109
<string name="disable_config_export_description">Disabling config exporting makes private keys less accessible</string>

0 commit comments

Comments
 (0)