Browse Source

Use coroutines to load files/folders, part of #1664.

Aidan Follestad 6 years ago
parent
commit
7b4212f958

+ 3 - 2
core/src/main/java/com/afollestad/materialdialogs/internal/main/DialogContentLayout.kt

@@ -125,11 +125,12 @@ internal class DialogContentLayout(
     top: Int = -1,
     bottom: Int = -1
   ) {
+    val targetView = if (scrollView != null) scrollView else recyclerView
     if (top != -1) {
-      scrollView.updatePadding(top = top)
+      targetView.updatePadding(top = top)
     }
     if (bottom != -1) {
-      scrollView.updatePadding(bottom = bottom)
+      targetView.updatePadding(bottom = bottom)
     }
   }
 

+ 1 - 1
dependencies.gradle

@@ -13,6 +13,6 @@ ext.versions = [
     kotlin            : '1.3.10',
     androidx          : '1.0.0',
     assent            : '2.2.0',
-
+    coroutines        : '1.0.1',
     dotsIndicator     : '1.0.0'
 ]

+ 1 - 0
files/build.gradle

@@ -31,6 +31,7 @@ android {
 dependencies {
   implementation 'androidx.recyclerview:recyclerview:' + versions.androidx
   implementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk7:' + versions.kotlin
+  implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:' + versions.coroutines
 
   implementation project(':core')
   implementation project(':input')

+ 37 - 28
files/src/main/java/com/afollestad/materialdialogs/files/FileChooserAdapter.kt

@@ -27,6 +27,11 @@ import com.afollestad.materialdialogs.files.utilext.setVisible
 import com.afollestad.materialdialogs.utils.MDUtil.isColorDark
 import com.afollestad.materialdialogs.utils.MDUtil.resolveColor
 import com.afollestad.materialdialogs.utils.MDUtil.resolveDrawable
+import kotlinx.coroutines.Dispatchers.Main
+import kotlinx.coroutines.GlobalScope
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.async
+import kotlinx.coroutines.launch
 import java.io.File
 
 internal class FileChooserViewHolder(
@@ -60,21 +65,21 @@ internal class FileChooserAdapter(
   var selectedFile: File? = null
 
   private var currentFolder = initialFolder
-  private var listingJob: Job<List<File>>? = null
+  private var listingJob: Job? = null
   private var contents: List<File>? = null
 
   private val isLightTheme =
     resolveColor(dialog.windowContext, attr = android.R.attr.textColorPrimary).isColorDark()
 
   init {
-    dialog.onDismiss { listingJob?.abort() }
-    loadContents(initialFolder)
+    dialog.onDismiss { listingJob?.cancel() }
+    switchDirectory(initialFolder)
   }
 
   fun itemClicked(index: Int) {
     if (currentFolder.hasParent() && index == goUpIndex()) {
       // go up
-      loadContents(currentFolder.betterParent()!!)
+      switchDirectory(currentFolder.betterParent()!!)
       return
     } else if (currentFolder.canWrite() && allowFolderCreation && index == newFolderIndex()) {
       // New folder
@@ -83,7 +88,7 @@ internal class FileChooserAdapter(
           folderCreationLabel = folderCreationLabel
       ) {
         // Refresh view
-        loadContents(currentFolder)
+        switchDirectory(currentFolder)
       }
       return
     }
@@ -92,7 +97,7 @@ internal class FileChooserAdapter(
     val selected = contents!![actualIndex].jumpOverEmulated()
 
     if (selected.isDirectory) {
-      loadContents(selected)
+      switchDirectory(selected)
     } else {
       val previousSelectedIndex = getSelectedIndex()
       this.selectedFile = selected
@@ -109,30 +114,34 @@ internal class FileChooserAdapter(
     }
   }
 
-  private fun loadContents(directory: File) {
-    if (onlyFolders) {
-      this.selectedFile = directory
-      dialog.setActionButtonEnabled(POSITIVE, true)
-    }
-
-    this.currentFolder = directory
-    dialog.title(text = directory.friendlyName())
-
-    listingJob?.abort()
-    listingJob = job<List<File>> { _ ->
-      val rawContents = directory.listFiles() ?: emptyArray()
+  private fun switchDirectory(directory: File) {
+    listingJob?.cancel()
+    listingJob = GlobalScope.launch(Main) {
       if (onlyFolders) {
-        rawContents
-            .filter { it.isDirectory && filter?.invoke(it) ?: true }
-            .sortedBy { it.name.toLowerCase() }
-      } else {
-        rawContents
-            .filter { filter?.invoke(it) ?: true }
-            .sortedWith(compareBy({ !it.isDirectory }, { it.nameWithoutExtension.toLowerCase() }))
+        selectedFile = directory
+        dialog.setActionButtonEnabled(POSITIVE, true)
+      }
+
+      currentFolder = directory
+      dialog.title(text = directory.friendlyName())
+
+      val result = async {
+        val rawContents = directory.listFiles() ?: emptyArray()
+        if (onlyFolders) {
+          rawContents
+              .filter { it.isDirectory && filter?.invoke(it) ?: true }
+              .sortedBy { it.name.toLowerCase() }
+        } else {
+          rawContents
+              .filter { filter?.invoke(it) ?: true }
+              .sortedWith(compareBy({ !it.isDirectory }, { it.nameWithoutExtension.toLowerCase() }))
+        }
       }
-    }.after {
-      this.contents = it
-      this.emptyView.setVisible(it.isEmpty())
+
+      contents = result.await()
+          .apply {
+            emptyView.setVisible(isEmpty())
+          }
       notifyDataSetChanged()
     }
   }

+ 0 - 46
files/src/main/java/com/afollestad/materialdialogs/files/Job.kt

@@ -1,46 +0,0 @@
-/*
- * Licensed under Apache-2.0
- *
- * Designed and developed by Aidan Follestad (@afollestad)
- */
-package com.afollestad.materialdialogs.files
-
-import android.os.Handler
-
-internal typealias Execution<T> = (Job<T>) -> T
-internal typealias PostExecution<T> = (T) -> Unit
-
-// Can probably be replaced with coroutines
-internal class Job<T>(private val execution: Execution<T>) {
-
-  private var thread: Thread? = null
-  private var after: ((T) -> Unit)? = null
-  private var handler = Handler()
-
-  var isAborted: Boolean = false
-    private set
-
-  fun after(after: PostExecution<T>): Job<T> {
-    this.after = after
-    return execute()
-  }
-
-  fun abort() {
-    thread?.interrupt()
-    thread = null
-  }
-
-  private fun execute(): Job<T> {
-    thread = Thread(Runnable {
-      val result = execution(this@Job)
-      if (isAborted) return@Runnable
-      handler.post { after?.invoke(result) }
-    })
-    thread?.start()
-    return this
-  }
-}
-
-internal fun <T> job(execution: Execution<T>): Job<T> {
-  return Job(execution)
-}