-
Notifications
You must be signed in to change notification settings - Fork 375
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Folder picker performance improvements (#1874)
* Improve the performance of the folder picker by querying all the required data at once to reduce the IPC calls. * Clean up the document file logic. * Only propagate the scanner as active while searching for books, not for covers. * Add timing diagnostics. * Make use of the Caching DocumentFile in the book scanning process to greatly improve the overall performance. * Simplify the time measurement. * Prioritize the scanning based on the amount of audio files. * Abstract the CachedDocumentFile and create a file based implementation that is used in unit tests.
- Loading branch information
1 parent
d706e0b
commit 5a8ca7e
Showing
29 changed files
with
400 additions
and
154 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
plugins { | ||
id("voice.library") | ||
alias(libs.plugins.anvil) | ||
|
||
} | ||
|
||
anvil { | ||
generateDaggerFactories.set(true) | ||
} | ||
|
||
dependencies { | ||
implementation(projects.common) | ||
implementation(libs.dagger.core) | ||
implementation(libs.androidxCore) | ||
} |
28 changes: 28 additions & 0 deletions
28
documentfile/src/main/kotlin/voice/documentfile/CachedDocumentFile.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
package voice.documentfile | ||
|
||
import android.net.Uri | ||
|
||
interface CachedDocumentFile { | ||
val children: List<CachedDocumentFile> | ||
val name: String? | ||
val isDirectory: Boolean | ||
val isFile: Boolean | ||
val length: Long | ||
val lastModified: Long | ||
val uri: Uri | ||
} | ||
|
||
fun CachedDocumentFile.nameWithoutExtension(): String { | ||
val name = name | ||
return if (name == null) { | ||
uri.pathSegments.lastOrNull() | ||
?.dropWhile { it != ':' } | ||
?.removePrefix(":") | ||
?.takeUnless { it.isBlank() } | ||
?: uri.toString() | ||
} else { | ||
name.substringBeforeLast(".") | ||
.takeUnless { it.isEmpty() } | ||
?: name | ||
} | ||
} |
7 changes: 7 additions & 0 deletions
7
documentfile/src/main/kotlin/voice/documentfile/CachedDocumentFileFactory.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
package voice.documentfile | ||
|
||
import android.net.Uri | ||
|
||
interface CachedDocumentFileFactory { | ||
fun create(uri: Uri): CachedDocumentFile | ||
} |
25 changes: 25 additions & 0 deletions
25
documentfile/src/main/kotlin/voice/documentfile/FileBasedDocumentFile.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
package voice.documentfile | ||
|
||
import android.net.Uri | ||
import androidx.core.net.toFile | ||
import androidx.core.net.toUri | ||
import java.io.File | ||
|
||
data class FileBasedDocumentFile( | ||
private val file: File, | ||
) : CachedDocumentFile { | ||
|
||
override val children: List<CachedDocumentFile> get() = file.listFiles()?.map { FileBasedDocumentFile(it) } ?: emptyList() | ||
override val name: String? get() = file.name | ||
override val isDirectory: Boolean get() = file.isDirectory | ||
override val isFile: Boolean get() = file.isFile | ||
override val length: Long get() = file.length() | ||
override val lastModified: Long get() = file.lastModified() | ||
override val uri: Uri get() = file.toUri() | ||
} | ||
|
||
object FileBasedDocumentFactory : CachedDocumentFileFactory { | ||
override fun create(uri: Uri): CachedDocumentFile { | ||
return FileBasedDocumentFile(uri.toFile()) | ||
} | ||
} |
39 changes: 39 additions & 0 deletions
39
documentfile/src/main/kotlin/voice/documentfile/FileContents.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
package voice.documentfile | ||
|
||
import android.database.Cursor | ||
import android.provider.DocumentsContract | ||
import androidx.core.database.getLongOrNull | ||
import androidx.core.database.getStringOrNull | ||
|
||
internal data class FileContents( | ||
val name: String?, | ||
val isFile: Boolean, | ||
val isDirectory: Boolean, | ||
val length: Long, | ||
val lastModified: Long, | ||
) { | ||
|
||
companion object { | ||
val columns = arrayOf( | ||
DocumentsContract.Document.COLUMN_DOCUMENT_ID, | ||
DocumentsContract.Document.COLUMN_MIME_TYPE, | ||
DocumentsContract.Document.COLUMN_DISPLAY_NAME, | ||
DocumentsContract.Document.COLUMN_SIZE, | ||
DocumentsContract.Document.COLUMN_LAST_MODIFIED, | ||
) | ||
|
||
fun readFrom(cursor: Cursor): FileContents { | ||
val mimeType = cursor.getStringOrNull(DocumentsContract.Document.COLUMN_MIME_TYPE) | ||
return FileContents( | ||
name = cursor.getStringOrNull(DocumentsContract.Document.COLUMN_DISPLAY_NAME), | ||
isFile = mimeType != null && mimeType != DocumentsContract.Document.MIME_TYPE_DIR, | ||
isDirectory = mimeType == DocumentsContract.Document.MIME_TYPE_DIR, | ||
length = cursor.getLongOrNull(DocumentsContract.Document.COLUMN_SIZE) ?: 0L, | ||
lastModified = cursor.getLongOrNull(DocumentsContract.Document.COLUMN_LAST_MODIFIED) ?: 0L, | ||
) | ||
} | ||
} | ||
} | ||
|
||
private fun Cursor.getStringOrNull(columnName: String): String? = getStringOrNull(getColumnIndexOrThrow(columnName)) | ||
private fun Cursor.getLongOrNull(columnName: String): Long? = getLongOrNull(getColumnIndexOrThrow(columnName)) |
26 changes: 26 additions & 0 deletions
26
documentfile/src/main/kotlin/voice/documentfile/ParseContents.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
package voice.documentfile | ||
|
||
import android.content.Context | ||
import android.net.Uri | ||
import android.provider.DocumentsContract | ||
import androidx.core.database.getStringOrNull | ||
|
||
internal fun parseContents(uri: Uri, context: Context): List<CachedDocumentFile> { | ||
val childrenUri = DocumentsContract.buildChildDocumentsUriUsingTree( | ||
uri, | ||
DocumentsContract.getDocumentId(uri), | ||
) | ||
return context.contentResolver.query( | ||
childrenUri, | ||
FileContents.columns, null, null, null, | ||
)?.use { cursor -> | ||
val files = mutableListOf<CachedDocumentFile>() | ||
while (cursor.moveToNext()) { | ||
val documentId = cursor.getStringOrNull(cursor.getColumnIndexOrThrow(DocumentsContract.Document.COLUMN_DOCUMENT_ID)) | ||
val documentUri = DocumentsContract.buildDocumentUriUsingTree(uri, documentId) | ||
val contents = FileContents.readFrom(cursor) | ||
files += RealCachedDocumentFile(context, documentUri, contents) | ||
} | ||
files | ||
} ?: emptyList() | ||
} |
38 changes: 38 additions & 0 deletions
38
documentfile/src/main/kotlin/voice/documentfile/RealCachedDocumentFile.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
package voice.documentfile | ||
|
||
import android.content.Context | ||
import android.net.Uri | ||
|
||
internal data class RealCachedDocumentFile( | ||
val context: Context, | ||
override val uri: Uri, | ||
private val preFilledContent: FileContents?, | ||
) : CachedDocumentFile { | ||
|
||
override val children: List<CachedDocumentFile> by lazy { | ||
if (isDirectory) { | ||
parseContents(uri, context) | ||
} else { | ||
emptyList() | ||
} | ||
} | ||
|
||
private val content: FileContents? by lazy { | ||
preFilledContent ?: context.contentResolver.query( | ||
uri, | ||
FileContents.columns, null, null, null, | ||
)?.use { cursor -> | ||
if (cursor.moveToFirst()) { | ||
FileContents.readFrom(cursor) | ||
} else { | ||
null | ||
} | ||
} | ||
} | ||
|
||
override val name: String? by lazy { content?.name } | ||
override val isDirectory: Boolean by lazy { content?.isDirectory ?: false } | ||
override val isFile: Boolean by lazy { content?.isFile ?: false } | ||
override val length: Long by lazy { content?.length ?: 0L } | ||
override val lastModified: Long by lazy { content?.lastModified ?: 0L } | ||
} |
17 changes: 17 additions & 0 deletions
17
documentfile/src/main/kotlin/voice/documentfile/RealCachedDocumentFileFactory.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
package voice.documentfile | ||
|
||
import android.content.Context | ||
import android.net.Uri | ||
import com.squareup.anvil.annotations.ContributesBinding | ||
import voice.common.AppScope | ||
import javax.inject.Inject | ||
|
||
@ContributesBinding(AppScope::class) | ||
class RealCachedDocumentFileFactory | ||
@Inject constructor( | ||
private val context: Context, | ||
) : CachedDocumentFileFactory { | ||
override fun create(uri: Uri): CachedDocumentFile { | ||
return RealCachedDocumentFile(context = context, uri = uri, preFilledContent = null) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
package voice.documentfile | ||
|
||
fun CachedDocumentFile.walk(): Sequence<CachedDocumentFile> = sequence { | ||
suspend fun SequenceScope<CachedDocumentFile>.walk(file: CachedDocumentFile) { | ||
yield(file) | ||
if (file.isDirectory) { | ||
file.children.forEach { walk(it) } | ||
} | ||
} | ||
walk(this@walk) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.