mirror of
https://github.com/zarzet/SpotiFLAC-Mobile.git
synced 2026-10-22 07:18:38 +02:00
refactor(android): move SAF batch exists and modtime queries out of MainActivitySafScan.kt
safExistsBatch and getSafFileModTimes answer per-URI document queries for library and history checks and do not take part in the scan, so they move verbatim into SafBatchQueries.kt. The file header of MainActivitySafScan.kt now describes what is left there.
This commit is contained in:
1 parent
671b5a2502
commit
eeda955363
2 files changed
+82
-72
No files matched your search
@@ -1,7 +1,5 @@
|
||||
package com.zarz.spotiflac
|
||||
|
||||
import android.net.Uri
|
||||
import android.provider.DocumentsContract
|
||||
import androidx.documentfile.provider.DocumentFile
|
||||
import org.json.JSONArray
|
||||
import org.json.JSONObject
|
||||
@@ -11,8 +9,10 @@ import java.io.IOException
|
||||
import java.security.MessageDigest
|
||||
import java.util.Locale
|
||||
|
||||
// SAF library-scan subsystem: tree walking, incremental diff, CUE resolution,
|
||||
// and scan-progress state shared with the progress stream in MainActivity.
|
||||
// SAF library scans: the full and incremental tree scans, the pause and
|
||||
// cancel check, and scan-progress state shared with the progress stream in
|
||||
// MainActivity. Listing, checkpoint, CUE, history inspection and batch query
|
||||
// helpers live in the Saf*.kt files beside this one.
|
||||
|
||||
internal fun MainActivity.buildStableLibraryId(filePath: String): String {
|
||||
val digest = MessageDigest.getInstance("SHA-1")
|
||||
@@ -928,71 +928,3 @@ internal fun MainActivity.scanSafTreeIncremental(
|
||||
spill.raw(",\"skippedCount\":$skippedCount,\"totalFiles\":$totalFiles}")
|
||||
return spill.result()
|
||||
}
|
||||
|
||||
// A failed DocumentsProvider query must never be mistaken for a missing file.
|
||||
// Unlike DocumentFile.exists(), this keeps null cursors/exceptions inconclusive.
|
||||
internal fun MainActivity.safExistsBatch(urisJson: String): String {
|
||||
val result = JSONObject()
|
||||
val uris = JSONArray(urisJson)
|
||||
val accessibleTrees = mutableMapOf<String, Boolean>()
|
||||
val permissions = contentResolver.persistedUriPermissions
|
||||
val projection = arrayOf(DocumentsContract.Document.COLUMN_DOCUMENT_ID)
|
||||
for (index in 0 until uris.length()) {
|
||||
val path = uris.optString(index)
|
||||
if (path.isBlank()) continue
|
||||
var status = "unknown"
|
||||
try {
|
||||
val uri = Uri.parse(path)
|
||||
val treeAccessible = if (DocumentsContract.isTreeUri(uri)) {
|
||||
val treeId = DocumentsContract.getTreeDocumentId(uri)
|
||||
val tree = DocumentsContract.buildTreeDocumentUri(uri.authority, treeId)
|
||||
accessibleTrees.getOrPut(tree.toString()) {
|
||||
val granted = permissions.any { it.uri == tree && it.isReadPermission }
|
||||
if (!granted) false else {
|
||||
val root = DocumentsContract.buildDocumentUriUsingTree(tree, treeId)
|
||||
contentResolver.query(root, projection, null, null, null)?.use {
|
||||
it.moveToFirst()
|
||||
} == true
|
||||
}
|
||||
}
|
||||
} else false
|
||||
contentResolver.query(uri, projection, null, null, null)?.use { cursor ->
|
||||
status = if (cursor.moveToFirst()) "found"
|
||||
else if (treeAccessible &&
|
||||
!cursor.extras.getBoolean(DocumentsContract.EXTRA_LOADING, false))
|
||||
"missing" else "unknown"
|
||||
}
|
||||
} catch (_: Exception) {
|
||||
// Revoked permission, offline storage and provider errors: retain row.
|
||||
}
|
||||
result.put(path, status)
|
||||
}
|
||||
return result.toString()
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve SAF file last-modified values for a list of content URIs.
|
||||
* Returns JSON object mapping uri -> lastModified (unix millis).
|
||||
*/
|
||||
internal fun MainActivity.getSafFileModTimes(urisJson: String): String {
|
||||
val result = JSONObject()
|
||||
val uris = try {
|
||||
JSONArray(urisJson)
|
||||
} catch (_: Exception) {
|
||||
JSONArray()
|
||||
}
|
||||
|
||||
for (i in 0 until uris.length()) {
|
||||
val uriStr = uris.optString(i, "")
|
||||
if (uriStr.isBlank()) continue
|
||||
try {
|
||||
val uri = Uri.parse(uriStr)
|
||||
val doc = DocumentFile.fromSingleUri(this, uri)
|
||||
if (doc != null && doc.exists()) {
|
||||
result.put(uriStr, doc.lastModified())
|
||||
}
|
||||
} catch (_: Exception) {}
|
||||
}
|
||||
|
||||
return result.toString()
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
package com.zarz.spotiflac
|
||||
|
||||
import android.net.Uri
|
||||
import android.provider.DocumentsContract
|
||||
import androidx.documentfile.provider.DocumentFile
|
||||
import org.json.JSONArray
|
||||
import org.json.JSONObject
|
||||
|
||||
// Batch SAF document queries for library and history checks: existence
|
||||
// status per URI and last-modified values per URI.
|
||||
|
||||
// A failed DocumentsProvider query must never be mistaken for a missing file.
|
||||
// Unlike DocumentFile.exists(), this keeps null cursors/exceptions inconclusive.
|
||||
internal fun MainActivity.safExistsBatch(urisJson: String): String {
|
||||
val result = JSONObject()
|
||||
val uris = JSONArray(urisJson)
|
||||
val accessibleTrees = mutableMapOf<String, Boolean>()
|
||||
val permissions = contentResolver.persistedUriPermissions
|
||||
val projection = arrayOf(DocumentsContract.Document.COLUMN_DOCUMENT_ID)
|
||||
for (index in 0 until uris.length()) {
|
||||
val path = uris.optString(index)
|
||||
if (path.isBlank()) continue
|
||||
var status = "unknown"
|
||||
try {
|
||||
val uri = Uri.parse(path)
|
||||
val treeAccessible = if (DocumentsContract.isTreeUri(uri)) {
|
||||
val treeId = DocumentsContract.getTreeDocumentId(uri)
|
||||
val tree = DocumentsContract.buildTreeDocumentUri(uri.authority, treeId)
|
||||
accessibleTrees.getOrPut(tree.toString()) {
|
||||
val granted = permissions.any { it.uri == tree && it.isReadPermission }
|
||||
if (!granted) false else {
|
||||
val root = DocumentsContract.buildDocumentUriUsingTree(tree, treeId)
|
||||
contentResolver.query(root, projection, null, null, null)?.use {
|
||||
it.moveToFirst()
|
||||
} == true
|
||||
}
|
||||
}
|
||||
} else false
|
||||
contentResolver.query(uri, projection, null, null, null)?.use { cursor ->
|
||||
status = if (cursor.moveToFirst()) "found"
|
||||
else if (treeAccessible &&
|
||||
!cursor.extras.getBoolean(DocumentsContract.EXTRA_LOADING, false))
|
||||
"missing" else "unknown"
|
||||
}
|
||||
} catch (_: Exception) {
|
||||
// Revoked permission, offline storage and provider errors: retain row.
|
||||
}
|
||||
result.put(path, status)
|
||||
}
|
||||
return result.toString()
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve SAF file last-modified values for a list of content URIs.
|
||||
* Returns JSON object mapping uri -> lastModified (unix millis).
|
||||
*/
|
||||
internal fun MainActivity.getSafFileModTimes(urisJson: String): String {
|
||||
val result = JSONObject()
|
||||
val uris = try {
|
||||
JSONArray(urisJson)
|
||||
} catch (_: Exception) {
|
||||
JSONArray()
|
||||
}
|
||||
|
||||
for (i in 0 until uris.length()) {
|
||||
val uriStr = uris.optString(i, "")
|
||||
if (uriStr.isBlank()) continue
|
||||
try {
|
||||
val uri = Uri.parse(uriStr)
|
||||
val doc = DocumentFile.fromSingleUri(this, uri)
|
||||
if (doc != null && doc.exists()) {
|
||||
result.put(uriStr, doc.lastModified())
|
||||
}
|
||||
} catch (_: Exception) {}
|
||||
}
|
||||
|
||||
return result.toString()
|
||||
}
|
||||
Reference in new issue
Block a user