Skip to content

Commit c509a8b

Browse files
committed
Improve image load performance
1 parent 92a5fed commit c509a8b

File tree

159 files changed

+898
-625
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

159 files changed

+898
-625
lines changed

app/build.gradle.kts

+2-2
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,9 @@ android {
4242
else -> 0
4343
}
4444

45-
val vCode = 313
45+
val vCode = 316
4646
versionCode = vCode - singleAbiNum
47-
versionName = "1.3.0"
47+
versionName = "1.3.1"
4848

4949
ndk {
5050
//noinspection ChromeOsAbiSupport

lib/src/main/java/com/ismartcoding/lib/extensions/File.kt renamed to app/src/main/java/com/ismartcoding/plain/extensions/File.kt

+8-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package com.ismartcoding.lib.extensions
1+
package com.ismartcoding.plain.extensions
22

33
import android.content.Context
44
import android.graphics.Bitmap
@@ -15,6 +15,12 @@ import com.bumptech.glide.load.resource.bitmap.DownsampleStrategy
1515
import com.bumptech.glide.load.resource.bitmap.Downsampler
1616
import com.bumptech.glide.request.RequestOptions
1717
import com.caverock.androidsvg.SVG
18+
import com.ismartcoding.lib.extensions.compress
19+
import com.ismartcoding.lib.extensions.getMediaContentUri
20+
import com.ismartcoding.lib.extensions.isAudioFast
21+
import com.ismartcoding.lib.extensions.isPartialSupportVideo
22+
import com.ismartcoding.lib.extensions.isVideoFast
23+
import com.ismartcoding.lib.extensions.pathToMediaStoreUri
1824
import com.ismartcoding.lib.isQPlus
1925
import com.ismartcoding.lib.logcat.LogCat
2026
import java.io.ByteArrayOutputStream
@@ -147,4 +153,4 @@ fun File.getDuration(context: Context): Long {
147153
val time = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION)
148154
retriever.release()
149155
return (time?.toLong()?.div(1000)) ?: 0L
150-
}
156+
}

lib/src/main/java/com/ismartcoding/lib/extensions/ImageView.kt renamed to app/src/main/java/com/ismartcoding/plain/extensions/ImageView.kt

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
package com.ismartcoding.lib.extensions
1+
package com.ismartcoding.plain.extensions
22

33
import android.widget.ImageView
44
import com.bumptech.glide.Glide
5+
import com.ismartcoding.lib.extensions.isPartialSupportVideo
56
import com.ismartcoding.lib.helpers.CoroutinesHelper.coMain
67
import com.ismartcoding.lib.helpers.CoroutinesHelper.withIO
78
import java.io.File

lib/src/main/java/com/ismartcoding/lib/extensions/View.kt renamed to app/src/main/java/com/ismartcoding/plain/extensions/View.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package com.ismartcoding.lib.extensions
1+
package com.ismartcoding.plain.extensions
22

33
import android.util.TypedValue
44
import android.view.View

app/src/main/java/com/ismartcoding/plain/features/NoteHelper.kt

+31-16
Original file line numberDiff line numberDiff line change
@@ -16,17 +16,27 @@ object NoteHelper {
1616
suspend fun count(query: String): Int {
1717
var sql = "SELECT COUNT(id) FROM notes"
1818
val where = ContentWhere()
19+
parseQuery(where, query)
20+
sql += " WHERE ${where.toSelection()}"
21+
22+
return noteDao.count(SimpleSQLiteQuery(sql, where.args.toTypedArray()))
23+
}
24+
25+
suspend fun getIdsAsync(query: String): Set<String> {
26+
var sql = "SELECT id FROM notes"
27+
val where = ContentWhere()
1928
if (query.isNotEmpty()) {
2029
parseQuery(where, query)
2130
sql += " WHERE ${where.toSelection()}"
2231
}
2332

24-
return noteDao.count(SimpleSQLiteQuery(sql, where.args.toTypedArray()))
33+
return noteDao.getIds(SimpleSQLiteQuery(sql, where.args.toTypedArray())).map { it.id }.toSet()
2534
}
2635

27-
suspend fun getIdsAsync(query: String): Set<String> {
36+
suspend fun getTrashedIdsAsync(query: String): Set<String> {
2837
var sql = "SELECT id FROM notes"
2938
val where = ContentWhere()
39+
where.trash = true
3040
if (query.isNotEmpty()) {
3141
parseQuery(where, query)
3242
sql += " WHERE ${where.toSelection()}"
@@ -42,10 +52,8 @@ object NoteHelper {
4252
): List<DNote> {
4353
var sql = "SELECT * FROM notes"
4454
val where = ContentWhere()
45-
if (query.isNotEmpty()) {
46-
parseQuery(where, query)
47-
sql += " WHERE ${where.toSelection()}"
48-
}
55+
parseQuery(where, query)
56+
sql += " WHERE ${where.toSelection()}"
4957

5058
sql += if (limit == Int.MAX_VALUE) {
5159
" ORDER BY updated_at DESC"
@@ -124,7 +132,7 @@ object NoteHelper {
124132
noteDao.trash(ids, now, now)
125133
}
126134

127-
fun untrashAsync(ids: Set<String>) {
135+
fun restoreAsync(ids: Set<String>) {
128136
noteDao.trash(ids, null, Clock.System.now())
129137
}
130138

@@ -137,17 +145,24 @@ object NoteHelper {
137145
query: String,
138146
) {
139147
QueryHelper.parseAsync(query).forEach {
140-
if (it.name == "text") {
141-
where.addLike("content", it.value)
142-
} else if (it.name == "ids") {
143-
where.addIn("id", it.value.split(","))
144-
} else if (it.name == "trash") {
145-
if (it.value == "true") {
146-
where.add("deleted_at IS NOT NULL")
147-
} else {
148-
where.add("deleted_at IS NULL")
148+
when (it.name) {
149+
"text" -> {
150+
where.addLike("content", it.value)
151+
}
152+
153+
"ids" -> {
154+
where.addIn("id", it.value.split(","))
155+
}
156+
157+
"trash" -> {
158+
where.trash = it.value.toBooleanStrictOrNull()
149159
}
150160
}
151161
}
162+
if (where.trash == true) {
163+
where.add("deleted_at IS NOT NULL")
164+
} else {
165+
where.add("deleted_at IS NULL")
166+
}
152167
}
153168
}

app/src/main/java/com/ismartcoding/plain/features/file/FileSystemHelper.kt

+33-4
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import android.os.StatFs
66
import android.os.storage.StorageManager
77
import android.provider.MediaStore
88
import android.text.TextUtils
9-
import com.ismartcoding.lib.extensions.getDirectChildrenCount
9+
import com.ismartcoding.plain.extensions.getDirectChildrenCount
1010
import com.ismartcoding.lib.isRPlus
1111
import com.ismartcoding.plain.R
1212
import com.ismartcoding.plain.extensions.sorted
@@ -17,6 +17,7 @@ import kotlinx.datetime.Instant
1717
import java.io.File
1818
import java.util.Collections
1919
import java.util.Locale
20+
import java.util.PriorityQueue
2021
import java.util.regex.Pattern
2122

2223
object FileSystemHelper {
@@ -121,8 +122,7 @@ object FileSystemHelper {
121122
var rootDirs: Array<File>? = null
122123
for (storageVolume in storageVolumes) {
123124
if (storageVolume.isRemovable) {
124-
var path = ""
125-
path =
125+
val path =
126126
if (isRPlus()) {
127127
storageVolume.directory.toString()
128128
} else {
@@ -261,7 +261,6 @@ object FileSystemHelper {
261261
}
262262

263263

264-
265264
fun getAllVolumeNames(context: Context): List<String> {
266265
val volumeNames = mutableListOf(MediaStore.VOLUME_EXTERNAL_PRIMARY)
267266
context.getExternalFilesDirs(null)
@@ -274,7 +273,37 @@ object FileSystemHelper {
274273
return volumeNames
275274
}
276275

276+
fun getRecentFiles(): List<File> {
277+
val externalStorageDir = Environment.getExternalStorageDirectory()
278+
279+
val recentFilesQueue = PriorityQueue<File>(100) { f1, f2 ->
280+
f1.lastModified().compareTo(f2.lastModified())
281+
}
282+
283+
gatherRecentFiles(externalStorageDir, recentFilesQueue)
277284

285+
return recentFilesQueue.sortedByDescending { it.lastModified() }
286+
}
287+
288+
private fun gatherRecentFiles(directory: File, recentFilesQueue: PriorityQueue<File>) {
289+
val files = directory.listFiles()
290+
if (files != null) {
291+
for (file in files) {
292+
if (file.isDirectory) {
293+
gatherRecentFiles(file, recentFilesQueue)
294+
} else {
295+
if (recentFilesQueue.size < 100) {
296+
recentFilesQueue.add(file)
297+
} else {
298+
if (file.lastModified() > (recentFilesQueue.peek()?.lastModified() ?: Long.MIN_VALUE)) {
299+
recentFilesQueue.poll()
300+
recentFilesQueue.add(file)
301+
}
302+
}
303+
}
304+
}
305+
}
306+
}
278307
}
279308

280309

app/src/main/java/com/ismartcoding/plain/features/media/AudioMediaStoreHelper.kt

+5-1
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,10 @@ object AudioMediaStoreHelper : BaseMediaContentHelper() {
5757
"artist" -> {
5858
where.addEqual(MediaStore.Audio.Media.ARTIST, it.value)
5959
}
60+
61+
"trash" -> {
62+
where.trash = it.value.toBooleanStrictOrNull()
63+
}
6064
}
6165
}
6266
return where
@@ -80,7 +84,7 @@ object AudioMediaStoreHelper : BaseMediaContentHelper() {
8084
val path = cursor.getStringValue(MediaStore.Audio.Media.DATA, cache)
8185
val bucketId = cursor.getStringValue(MediaStore.Audio.Media.BUCKET_ID, cache)
8286
val albumId = cursor.getStringValue(MediaStore.Audio.Media.ALBUM_ID, cache)
83-
DAudio(id, title, artist, path, duration, size, bucketId,albumId, createdAt, updatedAt)
87+
DAudio(id, title, artist, path, duration, size, bucketId, albumId, createdAt, updatedAt)
8488
} ?: emptyList()
8589
}
8690

app/src/main/java/com/ismartcoding/plain/features/media/BaseMediaContentHelper.kt

+63
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
11
package com.ismartcoding.plain.features.media
22

3+
import android.content.ContentValues
34
import android.content.Context
45
import android.database.Cursor
56
import android.net.Uri
7+
import android.os.Build
8+
import android.os.Environment
69
import android.provider.BaseColumns
710
import android.provider.MediaStore
11+
import androidx.annotation.RequiresApi
812
import com.ismartcoding.lib.content.ContentWhere
913
import com.ismartcoding.lib.data.SortBy
1014
import com.ismartcoding.lib.extensions.count
@@ -102,6 +106,17 @@ abstract class BaseMediaContentHelper {
102106
}?.toSet() ?: emptySet()
103107
}
104108

109+
suspend fun getTrashedIdsAsync(
110+
context: Context,
111+
query: String,
112+
): Set<String> {
113+
return context.contentResolver.getSearchCursor(
114+
uriExternal, arrayOf(BaseColumns._ID), buildWhere(query).apply { trash = true }
115+
)?.map { cursor, cache ->
116+
cursor.getStringValue(BaseColumns._ID, cache)
117+
}?.toSet() ?: emptySet()
118+
}
119+
105120
protected suspend fun getSearchCursorAsync(
106121
context: Context,
107122
query: String,
@@ -114,12 +129,14 @@ abstract class BaseMediaContentHelper {
114129
fun deleteRecordsAndFilesByIdsAsync(
115130
context: Context,
116131
ids: Set<String>,
132+
trash: Boolean? = null,
117133
): Set<String> {
118134
val paths = mutableSetOf<String>()
119135
val projection = arrayOf(BaseColumns._ID, MediaStore.MediaColumns.DATA)
120136
ids.chunked(500).forEach { chunk ->
121137
val where = ContentWhere()
122138
where.addIn(BaseColumns._ID, chunk)
139+
where.trash = trash
123140
context.contentResolver.getSearchCursor(uriExternal, projection, where)?.forEach { cursor, cache ->
124141
val id = cursor.getStringValue(BaseColumns._ID, cache)
125142
val path = cursor.getStringValue(MediaStore.MediaColumns.DATA, cache)
@@ -143,6 +160,52 @@ abstract class BaseMediaContentHelper {
143160
return paths
144161
}
145162

163+
fun getPathsByIdsAsync(
164+
context: Context,
165+
ids: Set<String>,
166+
): Set<String> {
167+
val paths = mutableSetOf<String>()
168+
val projection = arrayOf(BaseColumns._ID, MediaStore.MediaColumns.DATA)
169+
ids.chunked(500).forEach { chunk ->
170+
val where = ContentWhere()
171+
where.addIn(BaseColumns._ID, chunk)
172+
context.contentResolver.getSearchCursor(uriExternal, projection, where)?.forEach { cursor, cache ->
173+
val path = cursor.getStringValue(MediaStore.MediaColumns.DATA, cache)
174+
paths.add(path)
175+
}
176+
}
177+
178+
return paths
179+
}
180+
181+
@RequiresApi(Build.VERSION_CODES.R)
182+
fun trashByIdsAsync(
183+
context: Context,
184+
ids: Set<String>,
185+
) {
186+
val contentValues = ContentValues().apply {
187+
// put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_PICTURES + "/Trash")
188+
put(MediaStore.MediaColumns.IS_TRASHED, 1)
189+
}
190+
ids.forEach { id ->
191+
context.contentResolver.update(getItemUri(id), contentValues, null, null)
192+
}
193+
}
194+
195+
@RequiresApi(Build.VERSION_CODES.R)
196+
fun restoreByIdsAsync(
197+
context: Context,
198+
ids: Set<String>,
199+
) {
200+
val contentValues = ContentValues().apply {
201+
// put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_PICTURES + "/Trash")
202+
put(MediaStore.MediaColumns.IS_TRASHED, 0)
203+
}
204+
ids.forEach { id ->
205+
context.contentResolver.update(getItemUri(id), contentValues, null, null)
206+
}
207+
}
208+
146209
fun getBucketsAsync(context: Context): List<DMediaBucket> {
147210
val bucketMap = mutableMapOf<String, DMediaBucket>()
148211

app/src/main/java/com/ismartcoding/plain/features/media/FileMediaStoreHelper.kt

+5-3
Original file line numberDiff line numberDiff line change
@@ -207,13 +207,15 @@ object FileMediaStoreHelper : BaseContentHelper() {
207207
return items.sorted(sortBy)
208208
}
209209

210-
suspend fun getRecentFilesAsync(context: Context, query: String): List<DFile> {
210+
suspend fun getRecentFilesAsync(context: Context): List<DFile> {
211+
val where = ContentWhere()
212+
where.addNotEqual(MediaStore.Files.FileColumns.MIME_TYPE, "vnd.android.document/directory")
211213
return context.contentResolver.getPagingCursor(
212-
uriExternal, getProjection(), buildWhereAsync(query),
214+
uriExternal, getProjection(), where,
213215
100, 0, FileSortBy.DATE_DESC.toFileSortBy()
214216
)?.map { cursor, cache ->
215217
cursorToFile(cursor, cache)
216-
}?.filter { !it.isDir }?.take(50) ?: emptyList()
218+
} ?: emptyList()
217219
}
218220

219221
private fun cursorToFile(cursor: Cursor, cache: MutableMap<String, Int>): DFile {

app/src/main/java/com/ismartcoding/plain/features/media/ImageMediaStoreHelper.kt

+3-1
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,9 @@ object ImageMediaStoreHelper : BaseMediaContentHelper() {
4141
if (it.name == "text") {
4242
where.add("${MediaStore.Images.Media.TITLE} LIKE ?", "%${it.value}%")
4343
} else if (it.name == "bucket_id") {
44-
where.add("${MediaStore.Images.Media.BUCKET_ID} = ?", it.value)
44+
where.addEqual(MediaStore.Images.Media.BUCKET_ID, it.value)
45+
} else if (it.name == "trash") {
46+
where.trash = it.value.toBooleanStrictOrNull()
4547
}
4648
}
4749
return where

app/src/main/java/com/ismartcoding/plain/features/media/VideoMediaStoreHelper.kt

+3-1
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,9 @@ object VideoMediaStoreHelper : BaseMediaContentHelper() {
4444
if (it.name == "text") {
4545
where.add("${MediaStore.Video.Media.TITLE} LIKE ?", "%${it.value}%")
4646
} else if (it.name == "bucket_id") {
47-
where.add("${MediaStore.Video.Media.BUCKET_ID} = ?", it.value)
47+
where.addEqual(MediaStore.Video.Media.BUCKET_ID, it.value)
48+
} else if (it.name == "trash") {
49+
where.trash = it.value.toBooleanStrictOrNull()
4850
}
4951
}
5052
return where

lib/src/main/java/com/ismartcoding/lib/helpers/BitmapHelper.kt renamed to app/src/main/java/com/ismartcoding/plain/helpers/BitmapHelper.kt

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
1-
package com.ismartcoding.lib.helpers
1+
package com.ismartcoding.plain.helpers
22

33
import android.content.Context
44
import android.graphics.Bitmap
55
import android.graphics.BitmapFactory
6-
import com.ismartcoding.lib.extensions.getBitmapAsync
6+
import com.ismartcoding.plain.extensions.getBitmapAsync
77
import java.io.File
88

99
object BitmapHelper {
10-
fun decodeBitmapFromFileAsync(
10+
suspend fun decodeBitmapFromFileAsync(
1111
context: Context,
1212
path: String,
1313
reqWidth: Int,

0 commit comments

Comments
 (0)