From abed363f3801903b5f5eadb4179a88b4cc5b9325 Mon Sep 17 00:00:00 2001 From: zhangmengxiong Date: Wed, 1 Mar 2023 18:00:27 +0800 Subject: [PATCH 01/11] 666 --- .../java/com/mx/imgpicker/MXImagePicker.kt | 4 +- .../com/mx/imgpicker/adapts/FolderAdapt.kt | 3 +- .../app/picker/MXImgPickerActivity.kt | 7 +- .../com/mx/imgpicker/app/picker/MXPickerVM.kt | 67 +++---------------- .../app/picker/fragment/MXPickerFragment.kt | 12 +++- .../java/com/mx/imgpicker/db/MXDBSource.kt | 4 +- .../java/com/mx/imgpicker/models/beans.kt | 5 ++ .../java/com/mx/imgpicker/utils/MXScanBiz.kt | 51 ++++++++++++++ .../utils/source_loader/IMXSource.kt | 5 +- .../utils/source_loader/MXDirSource.kt | 16 +++-- .../utils/source_loader/MXImageSource.kt | 17 +++-- .../utils/source_loader/MXVideoSource.kt | 15 +++-- app/build.gradle | 9 ++- .../com/mx/imagepicker_sample/MainActivity.kt | 8 ++- 14 files changed, 137 insertions(+), 86 deletions(-) create mode 100644 ImagePickerLib/src/main/java/com/mx/imgpicker/utils/MXScanBiz.kt diff --git a/ImagePickerLib/src/main/java/com/mx/imgpicker/MXImagePicker.kt b/ImagePickerLib/src/main/java/com/mx/imgpicker/MXImagePicker.kt index 0d42b06..4e97aab 100644 --- a/ImagePickerLib/src/main/java/com/mx/imgpicker/MXImagePicker.kt +++ b/ImagePickerLib/src/main/java/com/mx/imgpicker/MXImagePicker.kt @@ -14,11 +14,11 @@ import java.util.concurrent.atomic.AtomicBoolean object MXImagePicker { private val hasInit = AtomicBoolean(false) private var application: Application? = null - internal fun init(application: Application) { + fun init(application: Application) { if (hasInit.get()) return + this.application = application application.registerActivityLifecycleCallbacks(activityLifecycleCall) hasInit.set(true) - this.application = application } internal fun getContext() = application!! diff --git a/ImagePickerLib/src/main/java/com/mx/imgpicker/adapts/FolderAdapt.kt b/ImagePickerLib/src/main/java/com/mx/imgpicker/adapts/FolderAdapt.kt index 548ec8e..fa240f0 100644 --- a/ImagePickerLib/src/main/java/com/mx/imgpicker/adapts/FolderAdapt.kt +++ b/ImagePickerLib/src/main/java/com/mx/imgpicker/adapts/FolderAdapt.kt @@ -10,6 +10,7 @@ import androidx.recyclerview.widget.RecyclerView import com.mx.imgpicker.MXImagePicker import com.mx.imgpicker.R import com.mx.imgpicker.app.picker.MXPickerVM +import com.mx.imgpicker.db.MXDBSource import com.mx.imgpicker.models.MXDirItem import kotlinx.coroutines.launch @@ -39,7 +40,7 @@ internal class FolderAdapt( if (item.lastItem == null) { lifecycleScope.launch { - item.lastItem = vm.sourceDB.queryLastItem(item.path, vm.pickerType) + item.lastItem = MXDBSource.instance.queryLastItem(item.path, vm.pickerType) this@FolderAdapt.notifyItemChanged(position) } } diff --git a/ImagePickerLib/src/main/java/com/mx/imgpicker/app/picker/MXImgPickerActivity.kt b/ImagePickerLib/src/main/java/com/mx/imgpicker/app/picker/MXImgPickerActivity.kt index 01a564d..39b5028 100644 --- a/ImagePickerLib/src/main/java/com/mx/imgpicker/app/picker/MXImgPickerActivity.kt +++ b/ImagePickerLib/src/main/java/com/mx/imgpicker/app/picker/MXImgPickerActivity.kt @@ -21,6 +21,7 @@ import com.mx.imgpicker.models.MXItem import com.mx.imgpicker.models.MXPickerType import com.mx.imgpicker.observer.MXSysImageObserver import com.mx.imgpicker.observer.MXSysVideoObserver +import com.mx.imgpicker.utils.MXScanBiz import com.mx.imgpicker.utils.MXUtils import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch @@ -113,7 +114,7 @@ class MXImgPickerActivity : AppCompatActivity() { lifecycleScope.launch { vm.reloadMediaList() } } - vm.startScan() + MXScanBiz.scanAll(this, lifecycleScope) } fun showLargeView(show: Boolean, target: MXItem? = null) { @@ -138,13 +139,13 @@ class MXImgPickerActivity : AppCompatActivity() { private val imageChangeObserver = MXSysImageObserver { if (isDestroyed) return@MXSysImageObserver if (vm.pickerType in arrayOf(MXPickerType.Image, MXPickerType.ImageAndVideo)) { - vm.startScan() + MXScanBiz.scanRecent(this, lifecycleScope) } } private val videoChangeObserver = MXSysVideoObserver { if (isDestroyed) return@MXSysVideoObserver if (vm.pickerType in arrayOf(MXPickerType.Video, MXPickerType.ImageAndVideo)) { - vm.startScan() + MXScanBiz.scanRecent(this, lifecycleScope) } } diff --git a/ImagePickerLib/src/main/java/com/mx/imgpicker/app/picker/MXPickerVM.kt b/ImagePickerLib/src/main/java/com/mx/imgpicker/app/picker/MXPickerVM.kt index e38fdea..a09dc6e 100644 --- a/ImagePickerLib/src/main/java/com/mx/imgpicker/app/picker/MXPickerVM.kt +++ b/ImagePickerLib/src/main/java/com/mx/imgpicker/app/picker/MXPickerVM.kt @@ -8,27 +8,14 @@ import com.mx.imgpicker.R import com.mx.imgpicker.db.MXDBSource import com.mx.imgpicker.models.* import com.mx.imgpicker.utils.MXUtils -import com.mx.imgpicker.utils.source_loader.MXDirSource -import com.mx.imgpicker.utils.source_loader.MXImageSource -import com.mx.imgpicker.utils.source_loader.MXVideoSource import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.isActive import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import java.io.File -import java.util.concurrent.atomic.AtomicBoolean -import kotlin.math.abs internal class MXPickerVM : ViewModel() { - companion object { - private const val PAGE_SIZE = 40 - } - private val allResStr by lazy { MXImagePicker.getContext().resources.getString(R.string.mx_picker_string_all) } private val allDir by lazy { MXDirItem(allResStr, "", 0) } - val sourceDB by lazy(LazyThreadSafetyMode.SYNCHRONIZED) { - MXDBSource(MXImagePicker.getContext()) - } var pickerType: MXPickerType = MXPickerType.Image // 类型 private set @@ -72,59 +59,21 @@ internal class MXPickerVM : ViewModel() { val needCompress = MutableLiveData(true) // 是否需要压缩 val fullScreenSelectIndex = MutableLiveData(0) // 是否需要压缩 - private val scanLock = AtomicBoolean(false) - private var lastReloadTime = 0L - fun startScan() { - viewModelScope.launch { - if (scanLock.get()) return@launch - scanLock.set(true) - MXUtils.log("开始扫描--> <--") - val context = MXImagePicker.getContext() - val scanResult: ((List) -> Boolean) = { list -> - viewModelScope.launch { - if (list.isEmpty()) return@launch - val hasSave = withContext(Dispatchers.IO) { - mediaList.containsAll(list) - } - if (hasSave) return@launch - sourceDB.addSysSource(list) - if (abs(System.currentTimeMillis() - lastReloadTime) > 3000) { - reloadMediaList() - } - } - this.isActive - } - if (pickerType != MXPickerType.Video) { - MXImageSource.scan(context, PAGE_SIZE, scanResult) - } - if (pickerType != MXPickerType.Image) { - MXVideoSource.scan(context, PAGE_SIZE, scanResult) - } - - val dirs = sourceDB.getAllDirList(pickerType) - MXDirSource(dirs).scan(context, PAGE_SIZE, scanResult) - - reloadMediaList() - MXUtils.log("结束扫描--> <--") - scanLock.set(false) - } - } - fun addPrivateSource(file: File, type: MXPickerType) { - viewModelScope.launch { sourceDB.addPrivateSource(file, type) } + viewModelScope.launch { MXDBSource.instance.addPrivateSource(file, type) } } suspend fun reloadMediaList() = withContext(Dispatchers.IO) { val start = System.currentTimeMillis() val selectDir = selectDirLive.value ?: allDir - val mediaList = sourceDB.getAllSource(pickerType, selectDir.path, maxListSize) + val mediaList = MXDBSource.instance.getAllSource(pickerType, selectDir.path, maxListSize) if (!MXUtils.compareList(_mediaList, mediaList)) { MXUtils.log("刷新->图片列表 ${_mediaList?.size}->${mediaList.size}") _mediaList = mediaList mediaListLive.postValue(Any()) } - val dirs = sourceDB.getAllDirList(pickerType) + val dirs = MXDBSource.instance.getAllDirList(pickerType) allDir.childSize = dirs.sumOf { it.childSize } val allDirs = listOf(allDir) + dirs @@ -134,9 +83,15 @@ internal class MXPickerVM : ViewModel() { dirListLive.postValue(Any()) } MXUtils.log("刷新->加载时长:${(System.currentTimeMillis() - start) / 1000f} 秒") + } - if (mediaList.isNotEmpty()) { - lastReloadTime = System.currentTimeMillis() + suspend fun onMediaInsert(file: File) = withContext(Dispatchers.IO) { + val ext = file.extension?.lowercase() + val type = if (ext in MXUtils.IMAGE_EXT) MXPickerType.Image else MXPickerType.Video + val item = MXItem(file.absolutePath, file.lastModified(), type) + _mediaList = ArrayList(_mediaList ?: emptyList()).apply { + add(0, item) } + mediaListLive.postValue(Any()) } } \ No newline at end of file diff --git a/ImagePickerLib/src/main/java/com/mx/imgpicker/app/picker/fragment/MXPickerFragment.kt b/ImagePickerLib/src/main/java/com/mx/imgpicker/app/picker/fragment/MXPickerFragment.kt index eb5dd51..62897e5 100644 --- a/ImagePickerLib/src/main/java/com/mx/imgpicker/app/picker/fragment/MXPickerFragment.kt +++ b/ImagePickerLib/src/main/java/com/mx/imgpicker/app/picker/fragment/MXPickerFragment.kt @@ -28,6 +28,7 @@ import com.mx.imgpicker.models.MXPickerType import com.mx.imgpicker.utils.MXUtils import kotlinx.coroutines.delay import kotlinx.coroutines.launch +import java.io.File internal class MXPickerFragment : Fragment() { private val vm by lazy { ViewModelProvider(requireActivity()).get(MXPickerVM::class.java) } @@ -48,6 +49,8 @@ internal class MXPickerFragment : Fragment() { private var willResizeLay: View? = null private var willResizeImg: ImageView? = null + private var targetFile: File? = null + override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, @@ -132,9 +135,10 @@ internal class MXPickerFragment : Fragment() { val intent = captureBuilder.createIntent(requireContext()) val file = captureBuilder.getCaptureFile() vm.addPrivateSource(file, type) + targetFile = file + MXUtils.log("PATH = ${file.absolutePath}") startActivityForResult(intent, 0x12) - MXUtils.log("PATH = ${file.absolutePath}") } if (vm.pickerType == MXPickerType.ImageAndVideo) { @@ -246,9 +250,11 @@ internal class MXPickerFragment : Fragment() { } override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + val file = targetFile ?: return + if (!file.exists()) return lifecycleScope.launch { - delay(1000) - vm.reloadMediaList() + vm.onMediaInsert(file) } + targetFile = null } } \ No newline at end of file diff --git a/ImagePickerLib/src/main/java/com/mx/imgpicker/db/MXDBSource.kt b/ImagePickerLib/src/main/java/com/mx/imgpicker/db/MXDBSource.kt index 4842fc1..08f4757 100644 --- a/ImagePickerLib/src/main/java/com/mx/imgpicker/db/MXDBSource.kt +++ b/ImagePickerLib/src/main/java/com/mx/imgpicker/db/MXDBSource.kt @@ -4,6 +4,7 @@ import android.content.ContentValues import android.content.Context import android.database.Cursor import android.database.sqlite.SQLiteDatabase +import com.mx.imgpicker.MXImagePicker import com.mx.imgpicker.models.MXDirItem import com.mx.imgpicker.models.MXItem import com.mx.imgpicker.models.MXPickerType @@ -13,9 +14,10 @@ import kotlinx.coroutines.withContext import java.io.File import kotlin.math.abs -internal class MXDBSource(val context: Context) { +internal class MXDBSource private constructor(val context: Context) { companion object { private val lock = Object() + val instance by lazy(LazyThreadSafetyMode.SYNCHRONIZED) { MXDBSource(MXImagePicker.getContext()) } } private val dbHelp by lazy { MXSQLite(context.applicationContext) } diff --git a/ImagePickerLib/src/main/java/com/mx/imgpicker/models/beans.kt b/ImagePickerLib/src/main/java/com/mx/imgpicker/models/beans.kt index a845621..fbe506b 100644 --- a/ImagePickerLib/src/main/java/com/mx/imgpicker/models/beans.kt +++ b/ImagePickerLib/src/main/java/com/mx/imgpicker/models/beans.kt @@ -115,3 +115,8 @@ internal data class MXDirItem( return "MXDirItem(name='$name', path='$path', childSize=$childSize)" } } + + +interface IScanCallback { + fun onResult(list: List) +} \ No newline at end of file diff --git a/ImagePickerLib/src/main/java/com/mx/imgpicker/utils/MXScanBiz.kt b/ImagePickerLib/src/main/java/com/mx/imgpicker/utils/MXScanBiz.kt new file mode 100644 index 0000000..aa488f3 --- /dev/null +++ b/ImagePickerLib/src/main/java/com/mx/imgpicker/utils/MXScanBiz.kt @@ -0,0 +1,51 @@ +package com.mx.imgpicker.utils + +import android.content.Context +import com.mx.imgpicker.db.MXDBSource +import com.mx.imgpicker.models.IScanCallback +import com.mx.imgpicker.models.MXItem +import com.mx.imgpicker.models.MXPickerType +import com.mx.imgpicker.utils.source_loader.MXDirSource +import com.mx.imgpicker.utils.source_loader.MXImageSource +import com.mx.imgpicker.utils.source_loader.MXVideoSource +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.launch +import java.util.concurrent.atomic.AtomicBoolean + +object MXScanBiz { + private const val PAGE_SIZE = 10 + private const val SCAN_SIZE = 50 + private val isInSearch = AtomicBoolean(false) + fun scanAll(context: Context, scope: CoroutineScope) { + startScan(context, 0, scope) + } + + fun scanRecent(context: Context, scope: CoroutineScope) { + startScan(context, SCAN_SIZE, scope) + } + + private fun startScan(context: Context, scanSize: Int, scope: CoroutineScope) { + if (isInSearch.get()) return + + val scanResult = object : IScanCallback { + override fun onResult(list: List) { + if (list.isEmpty()) return + MXUtils.log("MXScanBiz -- 扫描到数据:${list.joinToString("\n") { it.path }}") + scope.launch { + MXDBSource.instance.addSysSource(list) + } + } + } + + scope.launch { + isInSearch.set(true) + MXUtils.log("MXScanBiz -- 开始扫描") + MXImageSource.scan(context, PAGE_SIZE, scanSize, scanResult) + MXVideoSource.scan(context, PAGE_SIZE, scanSize, scanResult) + val dirs = MXDBSource.instance.getAllDirList(MXPickerType.ImageAndVideo) + MXDirSource(dirs).scan(context, PAGE_SIZE, scanSize, scanResult) + MXUtils.log("MXScanBiz -- 结束扫描") + isInSearch.set(false) + } + } +} \ No newline at end of file diff --git a/ImagePickerLib/src/main/java/com/mx/imgpicker/utils/source_loader/IMXSource.kt b/ImagePickerLib/src/main/java/com/mx/imgpicker/utils/source_loader/IMXSource.kt index 57ca437..4052809 100644 --- a/ImagePickerLib/src/main/java/com/mx/imgpicker/utils/source_loader/IMXSource.kt +++ b/ImagePickerLib/src/main/java/com/mx/imgpicker/utils/source_loader/IMXSource.kt @@ -1,14 +1,15 @@ package com.mx.imgpicker.utils.source_loader import android.content.Context -import com.mx.imgpicker.models.MXItem +import com.mx.imgpicker.models.IScanCallback import java.io.File internal interface IMXSource { suspend fun scan( context: Context, pageSize: Int, - onScanCall: ((List) -> Boolean) + maxScanSize: Int, + onScanCall: IScanCallback ) fun save(context: Context, file: File): Boolean diff --git a/ImagePickerLib/src/main/java/com/mx/imgpicker/utils/source_loader/MXDirSource.kt b/ImagePickerLib/src/main/java/com/mx/imgpicker/utils/source_loader/MXDirSource.kt index d65e0f2..7baf615 100644 --- a/ImagePickerLib/src/main/java/com/mx/imgpicker/utils/source_loader/MXDirSource.kt +++ b/ImagePickerLib/src/main/java/com/mx/imgpicker/utils/source_loader/MXDirSource.kt @@ -1,11 +1,13 @@ package com.mx.imgpicker.utils.source_loader import android.content.Context +import com.mx.imgpicker.models.IScanCallback import com.mx.imgpicker.models.MXDirItem import com.mx.imgpicker.models.MXItem import com.mx.imgpicker.models.MXPickerType import com.mx.imgpicker.utils.MXUtils import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.isActive import kotlinx.coroutines.withContext import java.io.File @@ -13,10 +15,12 @@ internal class MXDirSource(private val dirs: List) : IMXSource { override suspend fun scan( context: Context, pageSize: Int, - onScanCall: (List) -> Boolean + maxScanSize: Int, + onScanCall: IScanCallback ) = withContext(Dispatchers.IO) { if (dirs.isEmpty()) return@withContext val list = ArrayList() + var size = 0 for (dir in dirs) { val files = File(dir.path).listFiles() if (files == null || files.isEmpty()) continue @@ -30,6 +34,7 @@ internal class MXDirSource(private val dirs: List) : IMXSource { MXPickerType.Image ) ) + size++ } else if (ext in MXUtils.VIDEO_EXT) { list.add( MXItem( @@ -38,18 +43,21 @@ internal class MXDirSource(private val dirs: List) : IMXSource { MXPickerType.Video ) ) + size++ } + + if (maxScanSize in 1 until size) return@withContext if (list.size >= pageSize) { - val continueScan = onScanCall.invoke(list.toList()) + onScanCall.onResult(list.toList()) list.clear() - if (!continueScan) { + if (!isActive) { return@withContext } } } } if (list.isNotEmpty()) { - onScanCall.invoke(list.toList()) + onScanCall.onResult(list.toList()) } } diff --git a/ImagePickerLib/src/main/java/com/mx/imgpicker/utils/source_loader/MXImageSource.kt b/ImagePickerLib/src/main/java/com/mx/imgpicker/utils/source_loader/MXImageSource.kt index 25a1f46..d688a13 100644 --- a/ImagePickerLib/src/main/java/com/mx/imgpicker/utils/source_loader/MXImageSource.kt +++ b/ImagePickerLib/src/main/java/com/mx/imgpicker/utils/source_loader/MXImageSource.kt @@ -9,9 +9,11 @@ import android.graphics.BitmapFactory import android.net.Uri import android.os.Build import android.provider.MediaStore +import com.mx.imgpicker.models.IScanCallback import com.mx.imgpicker.models.MXItem import com.mx.imgpicker.models.MXPickerType import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.isActive import kotlinx.coroutines.withContext import java.io.File @@ -23,7 +25,8 @@ internal object MXImageSource : IMXSource { override suspend fun scan( context: Context, pageSize: Int, - onScanCall: ((List) -> Boolean) + maxScanSize: Int, + onScanCall: IScanCallback ) = withContext(Dispatchers.IO) { //扫描图片 val resolver = context.contentResolver ?: return@withContext @@ -41,6 +44,7 @@ internal object MXImageSource : IMXSource { val images = ArrayList() var mCursor: Cursor? = null + var size = 0 try { mCursor = MXContentProvide.createCursor( resolver, SOURCE_URI, columns.toTypedArray(), @@ -58,18 +62,19 @@ internal object MXImageSource : IMXSource { images.add(item) } if (images.size >= pageSize) { - val scanContinue = onScanCall.invoke(images.toList()) + onScanCall.onResult(images.toList()) images.clear() - if (!scanContinue) break } - } while (mCursor.moveToNext()) - onScanCall.invoke(images.toList()) + size++ + if (maxScanSize in 1 until size) break + } while (mCursor.moveToNext() && isActive) + onScanCall.onResult(images.toList()) } catch (e: Exception) { e.printStackTrace() } finally { try { mCursor?.close() - } catch (e: Exception) { + } catch (_: Exception) { } } } diff --git a/ImagePickerLib/src/main/java/com/mx/imgpicker/utils/source_loader/MXVideoSource.kt b/ImagePickerLib/src/main/java/com/mx/imgpicker/utils/source_loader/MXVideoSource.kt index 421cd23..4859c3e 100644 --- a/ImagePickerLib/src/main/java/com/mx/imgpicker/utils/source_loader/MXVideoSource.kt +++ b/ImagePickerLib/src/main/java/com/mx/imgpicker/utils/source_loader/MXVideoSource.kt @@ -9,9 +9,11 @@ import android.media.MediaMetadataRetriever import android.net.Uri import android.os.Build import android.provider.MediaStore +import com.mx.imgpicker.models.IScanCallback import com.mx.imgpicker.models.MXItem import com.mx.imgpicker.models.MXPickerType import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.isActive import kotlinx.coroutines.withContext import java.io.File @@ -21,7 +23,8 @@ internal object MXVideoSource : IMXSource { override suspend fun scan( context: Context, pageSize: Int, - onScanCall: ((List) -> Boolean) + maxScanSize: Int, + onScanCall: IScanCallback ) = withContext(Dispatchers.IO) { //扫描图片 val resolver = context.contentResolver ?: return@withContext @@ -40,6 +43,7 @@ internal object MXVideoSource : IMXSource { val images = ArrayList() var mCursor: Cursor? = null + var size = 0 try { mCursor = MXContentProvide.createCursor( resolver, SOURCE_URI, columns.toTypedArray(), @@ -56,12 +60,13 @@ internal object MXVideoSource : IMXSource { images.add(item) } if (images.size >= pageSize) { - val scanContinue = onScanCall.invoke(images.toList()) + onScanCall.onResult(images.toList()) images.clear() - if (!scanContinue) break } - } while (mCursor.moveToNext()) - onScanCall.invoke(images.toList()) + size++ + if (maxScanSize in 1 until size) break + } while (mCursor.moveToNext() && isActive) + onScanCall.onResult(images.toList()) } catch (e: Exception) { e.printStackTrace() } finally { diff --git a/app/build.gradle b/app/build.gradle index e2856d9..b247651 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -31,11 +31,18 @@ android { dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" - implementation 'androidx.appcompat:appcompat:1.4.1' + implementation 'androidx.appcompat:appcompat:1.5.1' + implementation 'androidx.core:core-ktx:1.8.0' implementation "androidx.recyclerview:recyclerview:1.2.1" implementation 'com.gitee.zhangmengxiong:MXStarter:v1.0.0' implementation 'com.github.bumptech.glide:glide:4.13.2' implementation 'androidx.constraintlayout:constraintlayout:2.0.4' + implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.1' + implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4' + implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.3.0' + + implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.1" + implementation "androidx.lifecycle:lifecycle-extensions:2.2.0" // rxpermission implementation 'io.reactivex.rxjava3:rxjava:3.0.4' diff --git a/app/src/main/java/com/mx/imagepicker_sample/MainActivity.kt b/app/src/main/java/com/mx/imagepicker_sample/MainActivity.kt index 17eb195..f95c41a 100644 --- a/app/src/main/java/com/mx/imagepicker_sample/MainActivity.kt +++ b/app/src/main/java/com/mx/imagepicker_sample/MainActivity.kt @@ -4,7 +4,8 @@ import android.Manifest import android.net.Uri import android.os.Bundle import android.view.View -import androidx.appcompat.app.AppCompatActivity +import androidx.fragment.app.FragmentActivity +import androidx.lifecycle.lifecycleScope import com.bumptech.glide.Glide import com.gyf.immersionbar.ImmersionBar import com.mx.imgpicker.MXImagePicker @@ -13,10 +14,11 @@ import com.mx.imgpicker.builder.MXCaptureBuilder import com.mx.imgpicker.builder.MXPickerBuilder import com.mx.imgpicker.models.MXPickerType import com.mx.imgpicker.compress.MXImageCompress +import com.mx.imgpicker.utils.MXScanBiz import com.mx.starter.MXStarter import java.io.File -class MainActivity : AppCompatActivity() { +class MainActivity : FragmentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) @@ -150,5 +152,7 @@ class MainActivity : AppCompatActivity() { ), "图片详情" ) } + MXImagePicker.init(application) + MXScanBiz.scanAll(this, lifecycleScope) } } \ No newline at end of file -- Gitee From 5c89bf8049c4bf6b626cd968d8ea0d0aa29f211b Mon Sep 17 00:00:00 2001 From: zhangmengxiong Date: Thu, 2 Mar 2023 09:51:48 +0800 Subject: [PATCH 02/11] Update README.md --- README.md | 109 +++++++++++++++++++++++++++++++++++------------------- 1 file changed, 70 insertions(+), 39 deletions(-) diff --git a/README.md b/README.md index 06c0068..e6df1c3 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,21 @@ # ImagePicker + ## 介绍 + 基于Kotlin,AndroidX的仿微信图片选择器 [![](https://jitpack.io/v/com.gitee.zhangmengxiong/MXImagePicker.svg)](https://jitpack.io/#com.gitee.zhangmengxiong/MXImagePicker) --- -引用方法1 - Gradle引用 +引用方法1 - Gradle引用 + ```gradle implementation 'com.gitee.zhangmengxiong:MXImagePicker:1.5.7' ``` + --- -引用方法2 - AAR包引用 +引用方法2 - AAR包引用 + - [下载aar - 1.5.7版本](https://gitee.com/zhangmengxiong/MXImagePicker/releases/download/1.5.7/ImagePickerLib-1.5.7.aar) + --- ![Image text](https://gitee.com/zhangmengxiong/MXImagePicker/raw/master/imgs/screenshot1.png) @@ -18,6 +24,7 @@ ## 使用方法 #### 第一步:项目增加Androidx库和Glide图片加载库、图片缩放库 + ```gradle implementation "androidx.appcompat:appcompat:x.x.x" implementation "androidx.recyclerview:recyclerview:x.x.x" @@ -27,83 +34,103 @@ ``` #### 第二步:使用前需要修改‘AndroidManifest.xml’配置:添加相册、存储权限 + ```kotlin Manifest.permission.CAMERA - Manifest.permission.READ_EXTERNAL_STORAGE +Manifest.permission.READ_EXTERNAL_STORAGE - // targetSdkVersion >= 29 的应用需要在application节点添加以下属性 - android:requestLegacyExternalStorage="true" +// targetSdkVersion >= 29 的应用需要在application节点添加以下属性 +android:requestLegacyExternalStorage = "true" ``` + 注意:`没有权限进入选择页面会报错!` #### 第三步:启动选择页面 + ```kotlin val intent = MXPickerBuilder().setMaxSize(3).createIntent(this) -startActivityForResult(intent,0x22) +startActivityForResult(intent, 0x22) ``` + ##### MXPickerBuilder参数说明 + 1. `setMaxSize(size: Int)` 设置最大选择文件个数 -2. `setType(type: PickerType)` 设置类型 +2. `setType(type: PickerType)` 设置类型 * PickerType.Image = 图片 * PickerType.Video = 视频 - * PickerType.ImageAndVideo = 图片 + 视频 混合选择 + * PickerType.ImageAndVideo = 图片 + 视频 混合选择 3. `setCameraEnable(enable: Boolean)` 设置是否启动拍摄功能,默认=true -4. `setMaxVideoLength(length: Int)` 当类型=Video时,可以选择视频最大时长限制,单位:秒 默认=-1 无限制 -5. `setMaxListSize(size: Int)` 最长列表加载长度,防止图片过多时产生OOM -1=不限制 默认限制长度=1000条 +4. `setMaxVideoLength(length: Int)` 当类型=Video时,可以选择视频最大时长限制,单位:秒 默认=-1 无限制 +5. `setMaxListSize(size: Int)` 最长列表加载长度,防止图片过多时产生OOM -1=不限制 默认限制长度=1000条 + +##### MXImagePicker预加载说明 + +预加载可以提前搜索本机图片/视频资源,减少首次进入选择页面时空白时间 + +```kotlin +MXImagePicker.init(application) +MXScanBiz.scanAll(this, lifecycleScope) +``` ```kotlin // 在图片选择器Activity创建时会回调这个方法,一般会通过这个来改变导航栏、状态栏的Theme,demo中搭配`ImmersionBar`来实现沉浸式效果 MXImagePicker.registerActivityCallback { activity -> ImmersionBar.with(activity) - .autoDarkModeEnable(true) - .statusBarColorInt(activity.resources.getColor(R.color.picker_color_background)) - .fitsSystemWindows(true) - .navigationBarColor(R.color.picker_color_background) - .init() + .autoDarkModeEnable(true) + .statusBarColorInt(activity.resources.getColor(R.color.picker_color_background)) + .fitsSystemWindows(true) + .navigationBarColor(R.color.picker_color_background) + .init() } ``` ##### 页面颜色设置 + 将下面颜色值放如主项目的资源xml中,可以修改页面对应的颜色显示 + ```xml - #333333 - - - #F1F1F1 +#333333 + + +#F1F1F1 - - #03CE65 + +#03CE65 ``` ##### 多语言设置 + 将下面字符串定义放入对应的语言目录中,可以修改页面对应的文字提示 + ```xml - 选择 - 全部 - 您最多只能选择 %s 张图片! - 您最多只能选择 %s 个视频! - 只能选择 %s 秒以内的视频 - 需要写入存储、相机权限 - 需要读取存储权限 - 打开失败! - 预览 - 原图 - 拍摄图片 - 拍摄视频 - 图片查看 + +选择全部 +您最多只能选择 %s 张图片!您最多只能选择 %s 个视频! +只能选择 %s 秒以内的视频需要写入存储、相机权限 +需要读取存储权限打开失败! +预览原图 +拍摄图片拍摄视频 +图片查看 ``` dimens.xml 资源 + ```xml - - 50dp + +50dp ``` ##### 自定义图片加载器(默认使用Glide) 通过继承实现接口`IImageLoader` ,并注册到服务`MXImagePicker`即可 + ```kotlin // 数据对象 data class MXItem(val path: String, val time: Long, val type: MXPickerType, val duration: Int = 0) @@ -124,6 +151,7 @@ MXImagePicker.registerImageLoader { activity, item, imageView -> ``` #### 第四步:获取返回结果 + ```kotlin override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) @@ -134,9 +162,8 @@ override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) } ``` - - ### 调取摄像头单独拍摄照片 + ```kotlin val builder = MXCaptureBuilder().setType(MXPickerType.Image) @@ -145,7 +172,9 @@ startActivityForResult(builder.createIntent(this), 0x11) // 在onActivityResult获取结果 val file = builder.getCaptureFile() ``` + ### 调取摄像头单独拍摄视频 + ```kotlin val builder = MXCaptureBuilder().setType(MXPickerType.Video).setMaxVideoLength(10) startActivityForResult(builder.createIntent(this), 0x11) @@ -154,9 +183,10 @@ startActivityForResult(builder.createIntent(this), 0x11) val file = builder.getCaptureFile() ``` - ### 图片查看器 + ![Image text](https://gitee.com/zhangmengxiong/MXImagePicker/raw/master/imgs/screenshot3.png) + ```kotlin MXImgShowActivity.open( this, arrayListOf( @@ -167,6 +197,7 @@ MXImgShowActivity.open( ``` ### 单张图片压缩 + ```kotlin val file = File(".../xx.png") val scaleImg = MXImageCompress.from(context) -- Gitee From 6df747c290cbe6b0f49a082eb1b1338a8df192c2 Mon Sep 17 00:00:00 2001 From: zhangmengxiong Date: Thu, 2 Mar 2023 09:52:49 +0800 Subject: [PATCH 03/11] Update README.md --- README.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index e6df1c3..77c877d 100644 --- a/README.md +++ b/README.md @@ -52,6 +52,15 @@ val intent = MXPickerBuilder().setMaxSize(3).createIntent(this) startActivityForResult(intent, 0x22) ``` +##### 预加载说明 + +预加载可以提前搜索本机图片/视频资源,减少首次进入选择页面时空白时间 + +```kotlin +MXImagePicker.init(application) +MXScanBiz.scanAll(this, lifecycleScope) +``` + ##### MXPickerBuilder参数说明 1. `setMaxSize(size: Int)` 设置最大选择文件个数 @@ -63,15 +72,6 @@ startActivityForResult(intent, 0x22) 4. `setMaxVideoLength(length: Int)` 当类型=Video时,可以选择视频最大时长限制,单位:秒 默认=-1 无限制 5. `setMaxListSize(size: Int)` 最长列表加载长度,防止图片过多时产生OOM -1=不限制 默认限制长度=1000条 -##### MXImagePicker预加载说明 - -预加载可以提前搜索本机图片/视频资源,减少首次进入选择页面时空白时间 - -```kotlin -MXImagePicker.init(application) -MXScanBiz.scanAll(this, lifecycleScope) -``` - ```kotlin // 在图片选择器Activity创建时会回调这个方法,一般会通过这个来改变导航栏、状态栏的Theme,demo中搭配`ImmersionBar`来实现沉浸式效果 MXImagePicker.registerActivityCallback { activity -> -- Gitee From 48dd404aaa4c9ffc22d88191342a33129ce63bf2 Mon Sep 17 00:00:00 2001 From: zhangmengxiong Date: Thu, 2 Mar 2023 10:15:39 +0800 Subject: [PATCH 04/11] 666 --- .../app/picker/MXImgPickerActivity.kt | 10 ++++-- .../java/com/mx/imgpicker/utils/MXScanBiz.kt | 36 ++++++++++++++----- .../com/mx/imagepicker_sample/MainActivity.kt | 2 +- 3 files changed, 36 insertions(+), 12 deletions(-) diff --git a/ImagePickerLib/src/main/java/com/mx/imgpicker/app/picker/MXImgPickerActivity.kt b/ImagePickerLib/src/main/java/com/mx/imgpicker/app/picker/MXImgPickerActivity.kt index 39b5028..33bd014 100644 --- a/ImagePickerLib/src/main/java/com/mx/imgpicker/app/picker/MXImgPickerActivity.kt +++ b/ImagePickerLib/src/main/java/com/mx/imgpicker/app/picker/MXImgPickerActivity.kt @@ -113,7 +113,9 @@ class MXImgPickerActivity : AppCompatActivity() { vm.selectDirLive.observe(this) { lifecycleScope.launch { vm.reloadMediaList() } } - + MXScanBiz.setOnUpdateListener { + lifecycleScope.launch { vm.reloadMediaList() } + } MXScanBiz.scanAll(this, lifecycleScope) } @@ -180,9 +182,13 @@ class MXImgPickerActivity : AppCompatActivity() { override fun onDestroy() { try { contentResolver.unregisterContentObserver(imageChangeObserver) + } catch (_: Exception) { + } + try { contentResolver.unregisterContentObserver(videoChangeObserver) - } catch (e: Exception) { + } catch (_: Exception) { } + MXScanBiz.setOnUpdateListener(null) super.onDestroy() } diff --git a/ImagePickerLib/src/main/java/com/mx/imgpicker/utils/MXScanBiz.kt b/ImagePickerLib/src/main/java/com/mx/imgpicker/utils/MXScanBiz.kt index aa488f3..3e56574 100644 --- a/ImagePickerLib/src/main/java/com/mx/imgpicker/utils/MXScanBiz.kt +++ b/ImagePickerLib/src/main/java/com/mx/imgpicker/utils/MXScanBiz.kt @@ -9,43 +9,61 @@ import com.mx.imgpicker.utils.source_loader.MXDirSource import com.mx.imgpicker.utils.source_loader.MXImageSource import com.mx.imgpicker.utils.source_loader.MXVideoSource import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Job +import kotlinx.coroutines.cancelAndJoin import kotlinx.coroutines.launch import java.util.concurrent.atomic.AtomicBoolean object MXScanBiz { - private const val PAGE_SIZE = 10 - private const val SCAN_SIZE = 50 - private val isInSearch = AtomicBoolean(false) + private const val PAGE_SIZE = 30 + private const val SCAN_SIZE = 10 + private var scanAllFinish = false + private var listener: (() -> Unit)? = null + fun scanAll(context: Context, scope: CoroutineScope) { + MXUtils.log("MXScanBiz -- scanAll") startScan(context, 0, scope) } fun scanRecent(context: Context, scope: CoroutineScope) { + MXUtils.log("MXScanBiz -- scanRecent") startScan(context, SCAN_SIZE, scope) } - private fun startScan(context: Context, scanSize: Int, scope: CoroutineScope) { - if (isInSearch.get()) return + internal fun setOnUpdateListener(call: (() -> Unit)?) { + listener = call + } + private var scanJob: Job? = null + private fun startScan( + context: Context, + scanSize: Int, + scope: CoroutineScope + ) { val scanResult = object : IScanCallback { override fun onResult(list: List) { if (list.isEmpty()) return - MXUtils.log("MXScanBiz -- 扫描到数据:${list.joinToString("\n") { it.path }}") +// MXUtils.log("MXScanBiz -- 扫描到数据:${list.joinToString("\n") { it.path }}") scope.launch { MXDBSource.instance.addSysSource(list) + listener?.invoke() } } } - scope.launch { - isInSearch.set(true) + val oldJob = scanJob + val job = scope.launch { + oldJob?.cancelAndJoin() MXUtils.log("MXScanBiz -- 开始扫描") MXImageSource.scan(context, PAGE_SIZE, scanSize, scanResult) MXVideoSource.scan(context, PAGE_SIZE, scanSize, scanResult) val dirs = MXDBSource.instance.getAllDirList(MXPickerType.ImageAndVideo) MXDirSource(dirs).scan(context, PAGE_SIZE, scanSize, scanResult) + } + job.invokeOnCompletion { MXUtils.log("MXScanBiz -- 结束扫描") - isInSearch.set(false) + listener?.invoke() } + scanJob = job } } \ No newline at end of file diff --git a/app/src/main/java/com/mx/imagepicker_sample/MainActivity.kt b/app/src/main/java/com/mx/imagepicker_sample/MainActivity.kt index f95c41a..0fe7aef 100644 --- a/app/src/main/java/com/mx/imagepicker_sample/MainActivity.kt +++ b/app/src/main/java/com/mx/imagepicker_sample/MainActivity.kt @@ -153,6 +153,6 @@ class MainActivity : FragmentActivity() { ) } MXImagePicker.init(application) - MXScanBiz.scanAll(this, lifecycleScope) +// MXScanBiz.scanAll(this, lifecycleScope) } } \ No newline at end of file -- Gitee From 7f300e264036958bd46f4f675932c4b293a13057 Mon Sep 17 00:00:00 2001 From: zhangmengxiong Date: Thu, 2 Mar 2023 10:19:08 +0800 Subject: [PATCH 05/11] 666 --- .../src/main/java/com/mx/imgpicker/utils/MXScanBiz.kt | 7 +++---- .../java/com/mx/imgpicker/utils/source_loader/IMXSource.kt | 1 + .../com/mx/imgpicker/utils/source_loader/MXDirSource.kt | 1 + .../com/mx/imgpicker/utils/source_loader/MXImageSource.kt | 4 ++++ .../com/mx/imgpicker/utils/source_loader/MXVideoSource.kt | 4 ++++ 5 files changed, 13 insertions(+), 4 deletions(-) diff --git a/ImagePickerLib/src/main/java/com/mx/imgpicker/utils/MXScanBiz.kt b/ImagePickerLib/src/main/java/com/mx/imgpicker/utils/MXScanBiz.kt index 3e56574..ab8a58d 100644 --- a/ImagePickerLib/src/main/java/com/mx/imgpicker/utils/MXScanBiz.kt +++ b/ImagePickerLib/src/main/java/com/mx/imgpicker/utils/MXScanBiz.kt @@ -17,7 +17,6 @@ import java.util.concurrent.atomic.AtomicBoolean object MXScanBiz { private const val PAGE_SIZE = 30 private const val SCAN_SIZE = 10 - private var scanAllFinish = false private var listener: (() -> Unit)? = null fun scanAll(context: Context, scope: CoroutineScope) { @@ -55,10 +54,10 @@ object MXScanBiz { val job = scope.launch { oldJob?.cancelAndJoin() MXUtils.log("MXScanBiz -- 开始扫描") - MXImageSource.scan(context, PAGE_SIZE, scanSize, scanResult) - MXVideoSource.scan(context, PAGE_SIZE, scanSize, scanResult) + MXImageSource.scan(context, PAGE_SIZE, 0, scanSize, scanResult) + MXVideoSource.scan(context, PAGE_SIZE, 0, scanSize, scanResult) val dirs = MXDBSource.instance.getAllDirList(MXPickerType.ImageAndVideo) - MXDirSource(dirs).scan(context, PAGE_SIZE, scanSize, scanResult) + MXDirSource(dirs).scan(context, PAGE_SIZE, 0, scanSize, scanResult) } job.invokeOnCompletion { MXUtils.log("MXScanBiz -- 结束扫描") diff --git a/ImagePickerLib/src/main/java/com/mx/imgpicker/utils/source_loader/IMXSource.kt b/ImagePickerLib/src/main/java/com/mx/imgpicker/utils/source_loader/IMXSource.kt index 4052809..15e8c83 100644 --- a/ImagePickerLib/src/main/java/com/mx/imgpicker/utils/source_loader/IMXSource.kt +++ b/ImagePickerLib/src/main/java/com/mx/imgpicker/utils/source_loader/IMXSource.kt @@ -8,6 +8,7 @@ internal interface IMXSource { suspend fun scan( context: Context, pageSize: Int, + offset: Int, maxScanSize: Int, onScanCall: IScanCallback ) diff --git a/ImagePickerLib/src/main/java/com/mx/imgpicker/utils/source_loader/MXDirSource.kt b/ImagePickerLib/src/main/java/com/mx/imgpicker/utils/source_loader/MXDirSource.kt index 7baf615..2bb5f6d 100644 --- a/ImagePickerLib/src/main/java/com/mx/imgpicker/utils/source_loader/MXDirSource.kt +++ b/ImagePickerLib/src/main/java/com/mx/imgpicker/utils/source_loader/MXDirSource.kt @@ -15,6 +15,7 @@ internal class MXDirSource(private val dirs: List) : IMXSource { override suspend fun scan( context: Context, pageSize: Int, + offset: Int, maxScanSize: Int, onScanCall: IScanCallback ) = withContext(Dispatchers.IO) { diff --git a/ImagePickerLib/src/main/java/com/mx/imgpicker/utils/source_loader/MXImageSource.kt b/ImagePickerLib/src/main/java/com/mx/imgpicker/utils/source_loader/MXImageSource.kt index d688a13..e02ec6b 100644 --- a/ImagePickerLib/src/main/java/com/mx/imgpicker/utils/source_loader/MXImageSource.kt +++ b/ImagePickerLib/src/main/java/com/mx/imgpicker/utils/source_loader/MXImageSource.kt @@ -25,6 +25,7 @@ internal object MXImageSource : IMXSource { override suspend fun scan( context: Context, pageSize: Int, + offset: Int, maxScanSize: Int, onScanCall: IScanCallback ) = withContext(Dispatchers.IO) { @@ -56,6 +57,9 @@ internal object MXImageSource : IMXSource { if (mCursor == null || !mCursor.moveToFirst()) { return@withContext } + if (offset in 1 until mCursor.count) { + mCursor.move(offset) + } do { val item = cursorToImageItem(resolver, mCursor) if (item != null) { diff --git a/ImagePickerLib/src/main/java/com/mx/imgpicker/utils/source_loader/MXVideoSource.kt b/ImagePickerLib/src/main/java/com/mx/imgpicker/utils/source_loader/MXVideoSource.kt index 4859c3e..e0c8c19 100644 --- a/ImagePickerLib/src/main/java/com/mx/imgpicker/utils/source_loader/MXVideoSource.kt +++ b/ImagePickerLib/src/main/java/com/mx/imgpicker/utils/source_loader/MXVideoSource.kt @@ -23,6 +23,7 @@ internal object MXVideoSource : IMXSource { override suspend fun scan( context: Context, pageSize: Int, + offset: Int, maxScanSize: Int, onScanCall: IScanCallback ) = withContext(Dispatchers.IO) { @@ -54,6 +55,9 @@ internal object MXVideoSource : IMXSource { if (mCursor == null || !mCursor.moveToFirst()) { return@withContext } + if (offset in 1 until mCursor.count) { + mCursor.move(offset) + } do { val item = cursorToImageItem(resolver, mCursor) if (item != null) { -- Gitee From aa7ddaadcd273d04d78d571b9ce516c4c1fa5ba1 Mon Sep 17 00:00:00 2001 From: zhangmengxiong Date: Thu, 2 Mar 2023 10:55:16 +0800 Subject: [PATCH 06/11] 666 --- .../com/mx/imgpicker/app/picker/MXPickerVM.kt | 2 +- .../java/com/mx/imgpicker/models/beans.kt | 5 -- .../java/com/mx/imgpicker/utils/MXScanBiz.kt | 86 ++++++++++++------- .../utils/source_loader/IMXSource.kt | 10 +-- .../utils/source_loader/MXDirSource.kt | 30 ++----- .../utils/source_loader/MXImageSource.kt | 35 +++----- .../utils/source_loader/MXVideoSource.kt | 37 ++++---- 7 files changed, 95 insertions(+), 110 deletions(-) diff --git a/ImagePickerLib/src/main/java/com/mx/imgpicker/app/picker/MXPickerVM.kt b/ImagePickerLib/src/main/java/com/mx/imgpicker/app/picker/MXPickerVM.kt index a09dc6e..c2fdf1c 100644 --- a/ImagePickerLib/src/main/java/com/mx/imgpicker/app/picker/MXPickerVM.kt +++ b/ImagePickerLib/src/main/java/com/mx/imgpicker/app/picker/MXPickerVM.kt @@ -90,7 +90,7 @@ internal class MXPickerVM : ViewModel() { val type = if (ext in MXUtils.IMAGE_EXT) MXPickerType.Image else MXPickerType.Video val item = MXItem(file.absolutePath, file.lastModified(), type) _mediaList = ArrayList(_mediaList ?: emptyList()).apply { - add(0, item) + if (!this.contains(item)) add(0, item) } mediaListLive.postValue(Any()) } diff --git a/ImagePickerLib/src/main/java/com/mx/imgpicker/models/beans.kt b/ImagePickerLib/src/main/java/com/mx/imgpicker/models/beans.kt index fbe506b..d70f3db 100644 --- a/ImagePickerLib/src/main/java/com/mx/imgpicker/models/beans.kt +++ b/ImagePickerLib/src/main/java/com/mx/imgpicker/models/beans.kt @@ -114,9 +114,4 @@ internal data class MXDirItem( override fun toString(): String { return "MXDirItem(name='$name', path='$path', childSize=$childSize)" } -} - - -interface IScanCallback { - fun onResult(list: List) } \ No newline at end of file diff --git a/ImagePickerLib/src/main/java/com/mx/imgpicker/utils/MXScanBiz.kt b/ImagePickerLib/src/main/java/com/mx/imgpicker/utils/MXScanBiz.kt index ab8a58d..a600b87 100644 --- a/ImagePickerLib/src/main/java/com/mx/imgpicker/utils/MXScanBiz.kt +++ b/ImagePickerLib/src/main/java/com/mx/imgpicker/utils/MXScanBiz.kt @@ -2,26 +2,26 @@ package com.mx.imgpicker.utils import android.content.Context import com.mx.imgpicker.db.MXDBSource -import com.mx.imgpicker.models.IScanCallback -import com.mx.imgpicker.models.MXItem import com.mx.imgpicker.models.MXPickerType import com.mx.imgpicker.utils.source_loader.MXDirSource import com.mx.imgpicker.utils.source_loader.MXImageSource import com.mx.imgpicker.utils.source_loader.MXVideoSource -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Job -import kotlinx.coroutines.cancelAndJoin -import kotlinx.coroutines.launch -import java.util.concurrent.atomic.AtomicBoolean +import kotlinx.coroutines.* +import kotlin.math.min object MXScanBiz { - private const val PAGE_SIZE = 30 - private const val SCAN_SIZE = 10 + private val lock = Object() + private const val PAGE_SIZE = 10 + private const val SCAN_SIZE = 5 + + private var hasScanAllImage = false + private var hasScanAllVideo = false + private var hasScanAllDirs = false private var listener: (() -> Unit)? = null fun scanAll(context: Context, scope: CoroutineScope) { MXUtils.log("MXScanBiz -- scanAll") - startScan(context, 0, scope) + startScan(context, Int.MAX_VALUE, scope) } fun scanRecent(context: Context, scope: CoroutineScope) { @@ -33,36 +33,64 @@ object MXScanBiz { listener = call } - private var scanJob: Job? = null private fun startScan( context: Context, scanSize: Int, scope: CoroutineScope ) { - val scanResult = object : IScanCallback { - override fun onResult(list: List) { - if (list.isEmpty()) return -// MXUtils.log("MXScanBiz -- 扫描到数据:${list.joinToString("\n") { it.path }}") - scope.launch { - MXDBSource.instance.addSysSource(list) - listener?.invoke() - } - } - } - - val oldJob = scanJob val job = scope.launch { - oldJob?.cancelAndJoin() MXUtils.log("MXScanBiz -- 开始扫描") - MXImageSource.scan(context, PAGE_SIZE, 0, scanSize, scanResult) - MXVideoSource.scan(context, PAGE_SIZE, 0, scanSize, scanResult) - val dirs = MXDBSource.instance.getAllDirList(MXPickerType.ImageAndVideo) - MXDirSource(dirs).scan(context, PAGE_SIZE, 0, scanSize, scanResult) + scanImage(context, scanSize) + scanVideo(context, scanSize) + scanDirs(context, scanSize) } job.invokeOnCompletion { MXUtils.log("MXScanBiz -- 结束扫描") listener?.invoke() } - scanJob = job } + + private suspend fun scanImage(context: Context, scanSize: Int) = withContext(Dispatchers.IO) { + var sumSize = 0 + var currentScanSize = 0 + val scanPageSize = min(PAGE_SIZE, scanSize) + do { + val list = synchronized(lock) { + MXImageSource.scan(context, scanPageSize, sumSize) + } ?: return@withContext + currentScanSize = list.size + sumSize += currentScanSize + MXDBSource.instance.addSysSource(list) + listener?.invoke() + } while (currentScanSize > 0 && sumSize < scanSize) + + MXUtils.log("MXScanBiz -- scanImage 结束扫描 -->${sumSize}") + } + + private suspend fun scanVideo(context: Context, scanSize: Int) = withContext(Dispatchers.IO) { + var sumSize = 0 + var currentScanSize = 0 + val scanPageSize = min(PAGE_SIZE, scanSize) + do { + val list = synchronized(lock) { + MXVideoSource.scan(context, scanPageSize, sumSize) + } ?: return@withContext + currentScanSize = list.size + sumSize += currentScanSize + MXDBSource.instance.addSysSource(list) + listener?.invoke() + } while (currentScanSize > 0 && sumSize < scanSize) + MXUtils.log("MXScanBiz -- scanVideo 结束扫描 -->${sumSize}") + } + + private suspend fun scanDirs(context: Context, scanSize: Int) = withContext(Dispatchers.IO) { + val dirs = MXDBSource.instance.getAllDirList(MXPickerType.ImageAndVideo) + val list = synchronized(lock) { + MXDirSource(dirs).scan(context, Int.MAX_VALUE, scanSize) + } ?: return@withContext + MXDBSource.instance.addSysSource(list) + listener?.invoke() + MXUtils.log("MXScanBiz -- scanDirs 结束扫描 -->${list.size}") + } + } \ No newline at end of file diff --git a/ImagePickerLib/src/main/java/com/mx/imgpicker/utils/source_loader/IMXSource.kt b/ImagePickerLib/src/main/java/com/mx/imgpicker/utils/source_loader/IMXSource.kt index 15e8c83..4e4f2b0 100644 --- a/ImagePickerLib/src/main/java/com/mx/imgpicker/utils/source_loader/IMXSource.kt +++ b/ImagePickerLib/src/main/java/com/mx/imgpicker/utils/source_loader/IMXSource.kt @@ -1,17 +1,11 @@ package com.mx.imgpicker.utils.source_loader import android.content.Context -import com.mx.imgpicker.models.IScanCallback +import com.mx.imgpicker.models.MXItem import java.io.File internal interface IMXSource { - suspend fun scan( - context: Context, - pageSize: Int, - offset: Int, - maxScanSize: Int, - onScanCall: IScanCallback - ) + fun scan(context: Context, size: Int, offset: Int): List? fun save(context: Context, file: File): Boolean } \ No newline at end of file diff --git a/ImagePickerLib/src/main/java/com/mx/imgpicker/utils/source_loader/MXDirSource.kt b/ImagePickerLib/src/main/java/com/mx/imgpicker/utils/source_loader/MXDirSource.kt index 2bb5f6d..c0198e4 100644 --- a/ImagePickerLib/src/main/java/com/mx/imgpicker/utils/source_loader/MXDirSource.kt +++ b/ImagePickerLib/src/main/java/com/mx/imgpicker/utils/source_loader/MXDirSource.kt @@ -1,7 +1,6 @@ package com.mx.imgpicker.utils.source_loader import android.content.Context -import com.mx.imgpicker.models.IScanCallback import com.mx.imgpicker.models.MXDirItem import com.mx.imgpicker.models.MXItem import com.mx.imgpicker.models.MXPickerType @@ -12,21 +11,14 @@ import kotlinx.coroutines.withContext import java.io.File internal class MXDirSource(private val dirs: List) : IMXSource { - override suspend fun scan( - context: Context, - pageSize: Int, - offset: Int, - maxScanSize: Int, - onScanCall: IScanCallback - ) = withContext(Dispatchers.IO) { - if (dirs.isEmpty()) return@withContext + override fun scan(context: Context, size: Int, offset: Int): List? { + if (dirs.isEmpty()) return null val list = ArrayList() - var size = 0 for (dir in dirs) { val files = File(dir.path).listFiles() if (files == null || files.isEmpty()) continue for (file in files) { - val ext = file.extension?.lowercase() + val ext = file.extension.lowercase() if (ext in MXUtils.IMAGE_EXT) { list.add( MXItem( @@ -35,7 +27,6 @@ internal class MXDirSource(private val dirs: List) : IMXSource { MXPickerType.Image ) ) - size++ } else if (ext in MXUtils.VIDEO_EXT) { list.add( MXItem( @@ -44,22 +35,13 @@ internal class MXDirSource(private val dirs: List) : IMXSource { MXPickerType.Video ) ) - size++ } - - if (maxScanSize in 1 until size) return@withContext - if (list.size >= pageSize) { - onScanCall.onResult(list.toList()) - list.clear() - if (!isActive) { - return@withContext - } + if (list.size >= size) { + return list } } } - if (list.isNotEmpty()) { - onScanCall.onResult(list.toList()) - } + return list } override fun save(context: Context, file: File): Boolean { diff --git a/ImagePickerLib/src/main/java/com/mx/imgpicker/utils/source_loader/MXImageSource.kt b/ImagePickerLib/src/main/java/com/mx/imgpicker/utils/source_loader/MXImageSource.kt index e02ec6b..b6c0969 100644 --- a/ImagePickerLib/src/main/java/com/mx/imgpicker/utils/source_loader/MXImageSource.kt +++ b/ImagePickerLib/src/main/java/com/mx/imgpicker/utils/source_loader/MXImageSource.kt @@ -9,7 +9,6 @@ import android.graphics.BitmapFactory import android.net.Uri import android.os.Build import android.provider.MediaStore -import com.mx.imgpicker.models.IScanCallback import com.mx.imgpicker.models.MXItem import com.mx.imgpicker.models.MXPickerType import kotlinx.coroutines.Dispatchers @@ -22,15 +21,10 @@ internal object MXImageSource : IMXSource { const val MIME_TYPE = "image/*" private val SOURCE_URI = MediaStore.Images.Media.EXTERNAL_CONTENT_URI - override suspend fun scan( - context: Context, - pageSize: Int, - offset: Int, - maxScanSize: Int, - onScanCall: IScanCallback - ) = withContext(Dispatchers.IO) { + override fun scan(context: Context, size: Int, offset: Int): List? { + val images = ArrayList() //扫描图片 - val resolver = context.contentResolver ?: return@withContext + val resolver = context.contentResolver ?: return null val columns = arrayListOf( MediaStore.Images.Media.DATA, MediaStore.Images.Media._ID, @@ -43,9 +37,7 @@ internal object MXImageSource : IMXSource { val where = MediaStore.Images.Media.SIZE + " > ? " val whereArgs = arrayListOf("0") - val images = ArrayList() var mCursor: Cursor? = null - var size = 0 try { mCursor = MXContentProvide.createCursor( resolver, SOURCE_URI, columns.toTypedArray(), @@ -55,24 +47,24 @@ internal object MXImageSource : IMXSource { ) if (mCursor == null || !mCursor.moveToFirst()) { - return@withContext + return null } - if (offset in 1 until mCursor.count) { - mCursor.move(offset) + if (offset > 0) { + if (offset < mCursor.count) { + mCursor.move(offset) + } else { + return null + } } do { val item = cursorToImageItem(resolver, mCursor) if (item != null) { images.add(item) } - if (images.size >= pageSize) { - onScanCall.onResult(images.toList()) - images.clear() + if (images.size >= size) { + break } - size++ - if (maxScanSize in 1 until size) break - } while (mCursor.moveToNext() && isActive) - onScanCall.onResult(images.toList()) + } while (mCursor.moveToNext()) } catch (e: Exception) { e.printStackTrace() } finally { @@ -81,6 +73,7 @@ internal object MXImageSource : IMXSource { } catch (_: Exception) { } } + return images } private fun cursorToImageItem(contentResolver: ContentResolver, mCursor: Cursor): MXItem? { diff --git a/ImagePickerLib/src/main/java/com/mx/imgpicker/utils/source_loader/MXVideoSource.kt b/ImagePickerLib/src/main/java/com/mx/imgpicker/utils/source_loader/MXVideoSource.kt index e0c8c19..f578b7e 100644 --- a/ImagePickerLib/src/main/java/com/mx/imgpicker/utils/source_loader/MXVideoSource.kt +++ b/ImagePickerLib/src/main/java/com/mx/imgpicker/utils/source_loader/MXVideoSource.kt @@ -9,7 +9,6 @@ import android.media.MediaMetadataRetriever import android.net.Uri import android.os.Build import android.provider.MediaStore -import com.mx.imgpicker.models.IScanCallback import com.mx.imgpicker.models.MXItem import com.mx.imgpicker.models.MXPickerType import kotlinx.coroutines.Dispatchers @@ -20,15 +19,10 @@ import java.io.File internal object MXVideoSource : IMXSource { const val MIME_TYPE = "video/*" private val SOURCE_URI = MediaStore.Video.Media.EXTERNAL_CONTENT_URI - override suspend fun scan( - context: Context, - pageSize: Int, - offset: Int, - maxScanSize: Int, - onScanCall: IScanCallback - ) = withContext(Dispatchers.IO) { + override fun scan(context: Context, size: Int, offset: Int): List? { + val images = ArrayList() //扫描图片 - val resolver = context.contentResolver ?: return@withContext + val resolver = context.contentResolver ?: return null val columns = arrayListOf( MediaStore.Video.Media.DATA, MediaStore.Video.Media._ID, @@ -42,9 +36,7 @@ internal object MXVideoSource : IMXSource { val where = MediaStore.Video.Media.SIZE + ">?" val whereArgs = arrayListOf("0") - val images = ArrayList() var mCursor: Cursor? = null - var size = 0 try { mCursor = MXContentProvide.createCursor( resolver, SOURCE_URI, columns.toTypedArray(), @@ -53,32 +45,33 @@ internal object MXVideoSource : IMXSource { false ) if (mCursor == null || !mCursor.moveToFirst()) { - return@withContext + return null } - if (offset in 1 until mCursor.count) { - mCursor.move(offset) + if (offset > 0) { + if (offset < mCursor.count) { + mCursor.move(offset) + } else { + return null + } } do { val item = cursorToImageItem(resolver, mCursor) if (item != null) { images.add(item) } - if (images.size >= pageSize) { - onScanCall.onResult(images.toList()) - images.clear() + if (images.size >= size) { + break } - size++ - if (maxScanSize in 1 until size) break - } while (mCursor.moveToNext() && isActive) - onScanCall.onResult(images.toList()) + } while (mCursor.moveToNext()) } catch (e: Exception) { e.printStackTrace() } finally { try { mCursor?.close() - } catch (e: Exception) { + } catch (_: Exception) { } } + return images } private fun cursorToImageItem(contentResolver: ContentResolver, mCursor: Cursor): MXItem? { -- Gitee From 463d1278866a77a54b0fdffe550c113968314088 Mon Sep 17 00:00:00 2001 From: zhangmengxiong Date: Thu, 2 Mar 2023 11:05:13 +0800 Subject: [PATCH 07/11] 666 --- .../app/picker/fragment/MXPickerFragment.kt | 1 - .../java/com/mx/imgpicker/db/MXDBSource.kt | 63 +++++++++--------- .../java/com/mx/imgpicker/utils/MXScanBiz.kt | 65 ++++++++++++------- .../utils/source_loader/MXDirSource.kt | 3 - .../utils/source_loader/MXImageSource.kt | 3 - .../utils/source_loader/MXVideoSource.kt | 3 - 6 files changed, 74 insertions(+), 64 deletions(-) diff --git a/ImagePickerLib/src/main/java/com/mx/imgpicker/app/picker/fragment/MXPickerFragment.kt b/ImagePickerLib/src/main/java/com/mx/imgpicker/app/picker/fragment/MXPickerFragment.kt index 62897e5..3172fa7 100644 --- a/ImagePickerLib/src/main/java/com/mx/imgpicker/app/picker/fragment/MXPickerFragment.kt +++ b/ImagePickerLib/src/main/java/com/mx/imgpicker/app/picker/fragment/MXPickerFragment.kt @@ -26,7 +26,6 @@ import com.mx.imgpicker.builder.MXCaptureBuilder import com.mx.imgpicker.models.MXCompressType import com.mx.imgpicker.models.MXPickerType import com.mx.imgpicker.utils.MXUtils -import kotlinx.coroutines.delay import kotlinx.coroutines.launch import java.io.File diff --git a/ImagePickerLib/src/main/java/com/mx/imgpicker/db/MXDBSource.kt b/ImagePickerLib/src/main/java/com/mx/imgpicker/db/MXDBSource.kt index 08f4757..005a04c 100644 --- a/ImagePickerLib/src/main/java/com/mx/imgpicker/db/MXDBSource.kt +++ b/ImagePickerLib/src/main/java/com/mx/imgpicker/db/MXDBSource.kt @@ -55,41 +55,40 @@ internal class MXDBSource private constructor(val context: Context) { /** * 批量添加/替换系统视频、图片数据 */ - suspend fun addSysSource(list: List): Boolean = - withContext(Dispatchers.IO) { - synchronized(lock) { - val database = dbHelp.writableDatabase - val insertSql = - "replace into ${MXSQLite.DB_NAME}(" + - "${MXSQLite.DB_PATH}, " + - "${MXSQLite.DB_DIR}, " + - "${MXSQLite.DB_TYPE}, " + - "${MXSQLite.DB_PRIVATE}, " + - "${MXSQLite.DB_TIME}, " + - "${MXSQLite.DB_VIDEO_LENGTH}) " + - "values(?,?,?,?,?,?)" - val stat = database.compileStatement(insertSql) - database.beginTransaction() - try { - for (item in list) { - stat.bindString(1, item.path) - stat.bindString(2, File(item.path).parentFile?.absolutePath) - stat.bindString(3, item.type.value) - stat.bindString(4, MXSQLite.VALUE_PRIVATE_SYS) - stat.bindLong(5, item.timeInMs) - stat.bindLong(6, item.duration.toLong()) - stat.executeInsert() - } - database.setTransactionSuccessful() - } catch (e: Exception) { - e.printStackTrace() - } finally { - database.endTransaction() - database.close() + fun addSysSource(list: List): Boolean { + synchronized(lock) { + val database = dbHelp.writableDatabase + val insertSql = + "replace into ${MXSQLite.DB_NAME}(" + + "${MXSQLite.DB_PATH}, " + + "${MXSQLite.DB_DIR}, " + + "${MXSQLite.DB_TYPE}, " + + "${MXSQLite.DB_PRIVATE}, " + + "${MXSQLite.DB_TIME}, " + + "${MXSQLite.DB_VIDEO_LENGTH}) " + + "values(?,?,?,?,?,?)" + val stat = database.compileStatement(insertSql) + database.beginTransaction() + try { + for (item in list) { + stat.bindString(1, item.path) + stat.bindString(2, File(item.path).parentFile?.absolutePath) + stat.bindString(3, item.type.value) + stat.bindString(4, MXSQLite.VALUE_PRIVATE_SYS) + stat.bindLong(5, item.timeInMs) + stat.bindLong(6, item.duration.toLong()) + stat.executeInsert() } + database.setTransactionSuccessful() + } catch (e: Exception) { + e.printStackTrace() + } finally { + database.endTransaction() + database.close() } - return@withContext false } + return false + } /** * 获取对应类型的所有数据 diff --git a/ImagePickerLib/src/main/java/com/mx/imgpicker/utils/MXScanBiz.kt b/ImagePickerLib/src/main/java/com/mx/imgpicker/utils/MXScanBiz.kt index a600b87..ff59f34 100644 --- a/ImagePickerLib/src/main/java/com/mx/imgpicker/utils/MXScanBiz.kt +++ b/ImagePickerLib/src/main/java/com/mx/imgpicker/utils/MXScanBiz.kt @@ -6,13 +6,16 @@ import com.mx.imgpicker.models.MXPickerType import com.mx.imgpicker.utils.source_loader.MXDirSource import com.mx.imgpicker.utils.source_loader.MXImageSource import com.mx.imgpicker.utils.source_loader.MXVideoSource -import kotlinx.coroutines.* +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext import kotlin.math.min object MXScanBiz { private val lock = Object() - private const val PAGE_SIZE = 10 - private const val SCAN_SIZE = 5 + private const val PAGE_SIZE = 20 + private const val SCAN_SIZE = PAGE_SIZE * 2 private var hasScanAllImage = false private var hasScanAllVideo = false @@ -21,28 +24,39 @@ object MXScanBiz { fun scanAll(context: Context, scope: CoroutineScope) { MXUtils.log("MXScanBiz -- scanAll") - startScan(context, Int.MAX_VALUE, scope) + val job = scope.launch { + MXUtils.log("MXScanBiz -- scanAll 开始扫描") + if (!hasScanAllImage) { + val size = scanImage(context, Int.MAX_VALUE) + if (size > 0) { + hasScanAllImage = true + } + } + if (!hasScanAllVideo) { + val size = scanVideo(context, Int.MAX_VALUE) + if (size > 0) { + hasScanAllVideo = true + } + } + if (!hasScanAllDirs) { + val size = scanDirs(context, Int.MAX_VALUE) + if (size > 0) { + hasScanAllDirs = true + } + } + } + job.invokeOnCompletion { + MXUtils.log("MXScanBiz -- scanAll 结束扫描") + listener?.invoke() + } } fun scanRecent(context: Context, scope: CoroutineScope) { MXUtils.log("MXScanBiz -- scanRecent") - startScan(context, SCAN_SIZE, scope) - } - - internal fun setOnUpdateListener(call: (() -> Unit)?) { - listener = call - } - - private fun startScan( - context: Context, - scanSize: Int, - scope: CoroutineScope - ) { val job = scope.launch { MXUtils.log("MXScanBiz -- 开始扫描") - scanImage(context, scanSize) - scanVideo(context, scanSize) - scanDirs(context, scanSize) + scanImage(context, SCAN_SIZE) + scanVideo(context, SCAN_SIZE) } job.invokeOnCompletion { MXUtils.log("MXScanBiz -- 结束扫描") @@ -50,6 +64,10 @@ object MXScanBiz { } } + internal fun setOnUpdateListener(call: (() -> Unit)?) { + listener = call + } + private suspend fun scanImage(context: Context, scanSize: Int) = withContext(Dispatchers.IO) { var sumSize = 0 var currentScanSize = 0 @@ -57,7 +75,7 @@ object MXScanBiz { do { val list = synchronized(lock) { MXImageSource.scan(context, scanPageSize, sumSize) - } ?: return@withContext + } ?: return@withContext 0 currentScanSize = list.size sumSize += currentScanSize MXDBSource.instance.addSysSource(list) @@ -65,6 +83,7 @@ object MXScanBiz { } while (currentScanSize > 0 && sumSize < scanSize) MXUtils.log("MXScanBiz -- scanImage 结束扫描 -->${sumSize}") + return@withContext sumSize } private suspend fun scanVideo(context: Context, scanSize: Int) = withContext(Dispatchers.IO) { @@ -74,23 +93,25 @@ object MXScanBiz { do { val list = synchronized(lock) { MXVideoSource.scan(context, scanPageSize, sumSize) - } ?: return@withContext + } ?: return@withContext 0 currentScanSize = list.size sumSize += currentScanSize MXDBSource.instance.addSysSource(list) listener?.invoke() } while (currentScanSize > 0 && sumSize < scanSize) MXUtils.log("MXScanBiz -- scanVideo 结束扫描 -->${sumSize}") + return@withContext sumSize } private suspend fun scanDirs(context: Context, scanSize: Int) = withContext(Dispatchers.IO) { val dirs = MXDBSource.instance.getAllDirList(MXPickerType.ImageAndVideo) val list = synchronized(lock) { MXDirSource(dirs).scan(context, Int.MAX_VALUE, scanSize) - } ?: return@withContext + } ?: return@withContext 0 MXDBSource.instance.addSysSource(list) listener?.invoke() MXUtils.log("MXScanBiz -- scanDirs 结束扫描 -->${list.size}") + return@withContext list.size } } \ No newline at end of file diff --git a/ImagePickerLib/src/main/java/com/mx/imgpicker/utils/source_loader/MXDirSource.kt b/ImagePickerLib/src/main/java/com/mx/imgpicker/utils/source_loader/MXDirSource.kt index c0198e4..38e404d 100644 --- a/ImagePickerLib/src/main/java/com/mx/imgpicker/utils/source_loader/MXDirSource.kt +++ b/ImagePickerLib/src/main/java/com/mx/imgpicker/utils/source_loader/MXDirSource.kt @@ -5,9 +5,6 @@ import com.mx.imgpicker.models.MXDirItem import com.mx.imgpicker.models.MXItem import com.mx.imgpicker.models.MXPickerType import com.mx.imgpicker.utils.MXUtils -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.isActive -import kotlinx.coroutines.withContext import java.io.File internal class MXDirSource(private val dirs: List) : IMXSource { diff --git a/ImagePickerLib/src/main/java/com/mx/imgpicker/utils/source_loader/MXImageSource.kt b/ImagePickerLib/src/main/java/com/mx/imgpicker/utils/source_loader/MXImageSource.kt index b6c0969..1e3bf50 100644 --- a/ImagePickerLib/src/main/java/com/mx/imgpicker/utils/source_loader/MXImageSource.kt +++ b/ImagePickerLib/src/main/java/com/mx/imgpicker/utils/source_loader/MXImageSource.kt @@ -11,9 +11,6 @@ import android.os.Build import android.provider.MediaStore import com.mx.imgpicker.models.MXItem import com.mx.imgpicker.models.MXPickerType -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.isActive -import kotlinx.coroutines.withContext import java.io.File diff --git a/ImagePickerLib/src/main/java/com/mx/imgpicker/utils/source_loader/MXVideoSource.kt b/ImagePickerLib/src/main/java/com/mx/imgpicker/utils/source_loader/MXVideoSource.kt index f578b7e..0847c89 100644 --- a/ImagePickerLib/src/main/java/com/mx/imgpicker/utils/source_loader/MXVideoSource.kt +++ b/ImagePickerLib/src/main/java/com/mx/imgpicker/utils/source_loader/MXVideoSource.kt @@ -11,9 +11,6 @@ import android.os.Build import android.provider.MediaStore import com.mx.imgpicker.models.MXItem import com.mx.imgpicker.models.MXPickerType -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.isActive -import kotlinx.coroutines.withContext import java.io.File internal object MXVideoSource : IMXSource { -- Gitee From d26df9e53c08e0e716a7a73ca86c5c6bafc2a948 Mon Sep 17 00:00:00 2001 From: zhangmengxiong Date: Thu, 2 Mar 2023 13:30:42 +0800 Subject: [PATCH 08/11] Update README.md --- README.md | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 77c877d..367872c 100644 --- a/README.md +++ b/README.md @@ -105,19 +105,19 @@ MXImagePicker.registerActivityCallback { activity -> 将下面字符串定义放入对应的语言目录中,可以修改页面对应的文字提示 ```xml - -选择全部 -您最多只能选择 %s 张图片!您最多只能选择 %s 个视频! -只能选择 %s 秒以内的视频需要写入存储、相机权限 -需要读取存储权限打开失败! -预览原图 -拍摄图片拍摄视频 -图片查看 + 选择 + 全部 + 您最多只能选择 %s 张图片! + 您最多只能选择 %s 个视频! + 只能选择 %s 秒以内的视频 + 需要写入存储、相机权限 + 需要读取存储权限 + 打开失败! + 预览 + 原图 + 拍摄图片 + 拍摄视频 + 图片查看 ``` dimens.xml 资源 -- Gitee From ea151e2c5c6b1b51b2c5078826e7bbf887cb8f13 Mon Sep 17 00:00:00 2001 From: zhangmengxiong Date: Thu, 2 Mar 2023 13:34:27 +0800 Subject: [PATCH 09/11] 1.5.8 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 优化图片/视频预览加载速度 --- README.md | 12 +++--------- build.gradle | 2 +- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 367872c..3d055ce 100644 --- a/README.md +++ b/README.md @@ -5,21 +5,15 @@ 基于Kotlin,AndroidX的仿微信图片选择器 [![](https://jitpack.io/v/com.gitee.zhangmengxiong/MXImagePicker.svg)](https://jitpack.io/#com.gitee.zhangmengxiong/MXImagePicker) --- -引用方法1 - Gradle引用 +Gradle引用 ```gradle - implementation 'com.gitee.zhangmengxiong:MXImagePicker:1.5.7' + implementation 'com.gitee.zhangmengxiong:MXImagePicker:1.5.8' ``` ---- -引用方法2 - AAR包引用 - -- [下载aar - 1.5.7版本](https://gitee.com/zhangmengxiong/MXImagePicker/releases/download/1.5.7/ImagePickerLib-1.5.7.aar) - ---- - ![Image text](https://gitee.com/zhangmengxiong/MXImagePicker/raw/master/imgs/screenshot1.png) ![Image text](https://gitee.com/zhangmengxiong/MXImagePicker/raw/master/imgs/screenshot2.png) +![Image text](https://gitee.com/zhangmengxiong/MXImagePicker/raw/master/imgs/screenshot3.png) ## 使用方法 diff --git a/build.gradle b/build.gradle index 4ef87e5..b484a12 100644 --- a/build.gradle +++ b/build.gradle @@ -37,5 +37,5 @@ ext { minSdkVersion = 19 versionCode = 1 - versionName = "1.5.7" + versionName = "1.5.8" } \ No newline at end of file -- Gitee From 686082a18627e38f93c8a838c0a733be691db077 Mon Sep 17 00:00:00 2001 From: zhangmengxiong Date: Thu, 2 Mar 2023 13:54:08 +0800 Subject: [PATCH 10/11] 666 --- .../java/com/mx/imgpicker/utils/MXScanBiz.kt | 45 ++++++++++++++----- .../utils/source_loader/MXDirSource.kt | 38 ++++++++-------- 2 files changed, 51 insertions(+), 32 deletions(-) diff --git a/ImagePickerLib/src/main/java/com/mx/imgpicker/utils/MXScanBiz.kt b/ImagePickerLib/src/main/java/com/mx/imgpicker/utils/MXScanBiz.kt index ff59f34..695549f 100644 --- a/ImagePickerLib/src/main/java/com/mx/imgpicker/utils/MXScanBiz.kt +++ b/ImagePickerLib/src/main/java/com/mx/imgpicker/utils/MXScanBiz.kt @@ -71,15 +71,20 @@ object MXScanBiz { private suspend fun scanImage(context: Context, scanSize: Int) = withContext(Dispatchers.IO) { var sumSize = 0 var currentScanSize = 0 + var currentScanPage = 0 val scanPageSize = min(PAGE_SIZE, scanSize) do { val list = synchronized(lock) { MXImageSource.scan(context, scanPageSize, sumSize) - } ?: return@withContext 0 + } ?: break currentScanSize = list.size sumSize += currentScanSize MXDBSource.instance.addSysSource(list) - listener?.invoke() + + currentScanPage++ + if (currentScanPage < 3 || currentScanPage % 10 == 0) { + listener?.invoke() + } } while (currentScanSize > 0 && sumSize < scanSize) MXUtils.log("MXScanBiz -- scanImage 结束扫描 -->${sumSize}") @@ -89,15 +94,20 @@ object MXScanBiz { private suspend fun scanVideo(context: Context, scanSize: Int) = withContext(Dispatchers.IO) { var sumSize = 0 var currentScanSize = 0 + var currentScanPage = 0 val scanPageSize = min(PAGE_SIZE, scanSize) do { val list = synchronized(lock) { MXVideoSource.scan(context, scanPageSize, sumSize) - } ?: return@withContext 0 + } ?: break currentScanSize = list.size sumSize += currentScanSize MXDBSource.instance.addSysSource(list) - listener?.invoke() + + currentScanPage++ + if (currentScanPage < 3 || currentScanPage % 10 == 0) { + listener?.invoke() + } } while (currentScanSize > 0 && sumSize < scanSize) MXUtils.log("MXScanBiz -- scanVideo 结束扫描 -->${sumSize}") return@withContext sumSize @@ -105,13 +115,24 @@ object MXScanBiz { private suspend fun scanDirs(context: Context, scanSize: Int) = withContext(Dispatchers.IO) { val dirs = MXDBSource.instance.getAllDirList(MXPickerType.ImageAndVideo) - val list = synchronized(lock) { - MXDirSource(dirs).scan(context, Int.MAX_VALUE, scanSize) - } ?: return@withContext 0 - MXDBSource.instance.addSysSource(list) - listener?.invoke() - MXUtils.log("MXScanBiz -- scanDirs 结束扫描 -->${list.size}") - return@withContext list.size - } + var sumSize = 0 + var currentScanSize = 0 + var currentScanPage = 0 + val scanPageSize = min(PAGE_SIZE, scanSize) + do { + val list = synchronized(lock) { + MXDirSource(dirs).scan(context, scanPageSize, sumSize) + } ?: break + currentScanSize = list.size + sumSize += currentScanSize + MXDBSource.instance.addSysSource(list) + currentScanPage++ + if (currentScanPage < 3 || currentScanPage % 10 == 0) { + listener?.invoke() + } + } while (currentScanSize > 0 && sumSize < scanSize) + MXUtils.log("MXScanBiz -- scanDirs 结束扫描 -->${sumSize}") + return@withContext sumSize + } } \ No newline at end of file diff --git a/ImagePickerLib/src/main/java/com/mx/imgpicker/utils/source_loader/MXDirSource.kt b/ImagePickerLib/src/main/java/com/mx/imgpicker/utils/source_loader/MXDirSource.kt index 38e404d..21610b1 100644 --- a/ImagePickerLib/src/main/java/com/mx/imgpicker/utils/source_loader/MXDirSource.kt +++ b/ImagePickerLib/src/main/java/com/mx/imgpicker/utils/source_loader/MXDirSource.kt @@ -11,30 +11,28 @@ internal class MXDirSource(private val dirs: List) : IMXSource { override fun scan(context: Context, size: Int, offset: Int): List? { if (dirs.isEmpty()) return null val list = ArrayList() + var index = 0 for (dir in dirs) { - val files = File(dir.path).listFiles() + val files = File(dir.path).listFiles()?.sortedBy { it.name } if (files == null || files.isEmpty()) continue for (file in files) { - val ext = file.extension.lowercase() - if (ext in MXUtils.IMAGE_EXT) { - list.add( - MXItem( - file.absolutePath, - file.lastModified(), - MXPickerType.Image - ) - ) - } else if (ext in MXUtils.VIDEO_EXT) { - list.add( - MXItem( - file.absolutePath, - file.lastModified(), - MXPickerType.Video - ) - ) + val item = when (file.extension.lowercase()) { + in MXUtils.IMAGE_EXT -> { + MXItem(file.absolutePath, file.lastModified(), MXPickerType.Image) + } + in MXUtils.VIDEO_EXT -> { + MXItem(file.absolutePath, file.lastModified(), MXPickerType.Video) + } + else -> null } - if (list.size >= size) { - return list + if (item != null) { + index++ + if (index > offset) { + list.add(item) + } + if (list.size >= size) { + return list + } } } } -- Gitee From a279bf8f81ca1969b8ff908eeab66c46a628d60f Mon Sep 17 00:00:00 2001 From: zhangmengxiong Date: Thu, 2 Mar 2023 13:57:29 +0800 Subject: [PATCH 11/11] Update MXImgPickerActivity.kt --- .../java/com/mx/imgpicker/app/picker/MXImgPickerActivity.kt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ImagePickerLib/src/main/java/com/mx/imgpicker/app/picker/MXImgPickerActivity.kt b/ImagePickerLib/src/main/java/com/mx/imgpicker/app/picker/MXImgPickerActivity.kt index 33bd014..b573f42 100644 --- a/ImagePickerLib/src/main/java/com/mx/imgpicker/app/picker/MXImgPickerActivity.kt +++ b/ImagePickerLib/src/main/java/com/mx/imgpicker/app/picker/MXImgPickerActivity.kt @@ -82,6 +82,11 @@ class MXImgPickerActivity : AppCompatActivity() { } } + override fun onStart() { + super.onStart() + MXScanBiz.scanRecent(this, lifecycleScope) + } + override fun onRequestPermissionsResult( requestCode: Int, permissions: Array, -- Gitee